Initial commit
This commit is contained in:
2
engines/made/POTFILES
Normal file
2
engines/made/POTFILES
Normal file
@@ -0,0 +1,2 @@
|
||||
engines/made/detection_tables.h
|
||||
engines/made/metaengine.cpp
|
||||
3
engines/made/configure.engine
Normal file
3
engines/made/configure.engine
Normal file
@@ -0,0 +1,3 @@
|
||||
# This file is included from the main "configure" script
|
||||
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
|
||||
add_engine made "MADE" yes "" "" "" "midi"
|
||||
34
engines/made/console.cpp
Normal file
34
engines/made/console.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "made/console.h"
|
||||
#include "made/made.h"
|
||||
|
||||
namespace Made {
|
||||
|
||||
MadeConsole::MadeConsole(MadeEngine *vm) : GUI::Debugger(), _vm(vm) {
|
||||
assert(_vm);
|
||||
}
|
||||
|
||||
MadeConsole::~MadeConsole() {
|
||||
}
|
||||
|
||||
} // End of namespace Made
|
||||
42
engines/made/console.h
Normal file
42
engines/made/console.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MADE_CONSOLE_H
|
||||
#define MADE_CONSOLE_H
|
||||
|
||||
#include "gui/debugger.h"
|
||||
|
||||
namespace Made {
|
||||
|
||||
class MadeEngine;
|
||||
|
||||
class MadeConsole : public GUI::Debugger {
|
||||
public:
|
||||
MadeConsole(MadeEngine *vm);
|
||||
~MadeConsole(void) override;
|
||||
|
||||
private:
|
||||
MadeEngine *_vm;
|
||||
};
|
||||
|
||||
} // End of namespace Made
|
||||
|
||||
#endif
|
||||
4
engines/made/credits.pl
Normal file
4
engines/made/credits.pl
Normal file
@@ -0,0 +1,4 @@
|
||||
begin_section("MADE");
|
||||
add_person("Benjamin Haisch", "john_doe", "");
|
||||
add_person("Filippos Karapetis", "bluegr", "");
|
||||
end_section();
|
||||
840
engines/made/database.cpp
Normal file
840
engines/made/database.cpp
Normal file
@@ -0,0 +1,840 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "made/database.h"
|
||||
#include "made/redreader.h"
|
||||
|
||||
#include "common/endian.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/file.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/system.h"
|
||||
|
||||
namespace Made {
|
||||
|
||||
/*
|
||||
Class types:
|
||||
0x7FFF byte array
|
||||
0x7FFE word array
|
||||
< 0x7FFE object
|
||||
*/
|
||||
|
||||
Object::Object() : _objData(nullptr), _freeData(false) {
|
||||
_objSize = 0;
|
||||
}
|
||||
|
||||
Object::~Object() {
|
||||
if (_freeData && _objData)
|
||||
delete[] _objData;
|
||||
}
|
||||
|
||||
const char *Object::getString() {
|
||||
if (getClass() == 0x7FFF)
|
||||
return (const char*)getData();
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Object::setString(const char *str) {
|
||||
if (getClass() == 0x7FFF) {
|
||||
char *objStr = (char *)getData();
|
||||
if (str)
|
||||
strncpy(objStr, str, getSize());
|
||||
else
|
||||
objStr[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
bool Object::isObject() {
|
||||
return getClass() < 0x7FFE;
|
||||
}
|
||||
|
||||
bool Object::isVector() {
|
||||
return getClass() == 0x7FFF;
|
||||
}
|
||||
|
||||
int16 Object::getVectorSize() {
|
||||
if (getClass() == 0x7FFF || getClass() == 0x7FFE) {
|
||||
return getSize();
|
||||
} else if (getClass() < 0x7FFE) {
|
||||
return getCount1() + getCount2();
|
||||
} else {
|
||||
// should never reach here
|
||||
error("Unknown object class");
|
||||
return 0; // for compilers that don't support NORETURN
|
||||
}
|
||||
}
|
||||
|
||||
int16 Object::getVectorItem(int16 index) {
|
||||
if (getClass() == 0x7FFF) {
|
||||
byte *vector = (byte *)getData();
|
||||
return vector[index];
|
||||
} else if (getClass() <= 0x7FFE) {
|
||||
int16 *vector = (int16 *)getData();
|
||||
return READ_LE_UINT16(&vector[index]);
|
||||
} else {
|
||||
// should never reach here
|
||||
error("Unknown object class");
|
||||
return 0; // for compilers that don't support NORETURN
|
||||
}
|
||||
}
|
||||
|
||||
void Object::setVectorItem(int16 index, int16 value) {
|
||||
if (getClass() == 0x7FFF) {
|
||||
byte *vector = (byte *)getData();
|
||||
vector[index] = value;
|
||||
} else if (getClass() <= 0x7FFE) {
|
||||
int16 *vector = (int16 *)getData();
|
||||
WRITE_LE_UINT16(&vector[index], value);
|
||||
}
|
||||
}
|
||||
|
||||
void Object::dump(const Common::String &filename) {
|
||||
/*
|
||||
FILE *o = fopen(filename, "wb");
|
||||
fwrite(_objData, _objSize, 1, o);
|
||||
fclose(o);
|
||||
*/
|
||||
}
|
||||
|
||||
int ObjectV2::load(Common::SeekableReadStream &source) {
|
||||
|
||||
if (_freeData && _objData)
|
||||
delete[] _objData;
|
||||
|
||||
_freeData = true;
|
||||
|
||||
byte header[4];
|
||||
source.read(header, 4);
|
||||
|
||||
uint16 type = READ_LE_UINT16(header);
|
||||
if (type == 0x7FFF) {
|
||||
_objSize = READ_LE_UINT16(header + 2);
|
||||
} else if (type == 0x7FFE) {
|
||||
_objSize = READ_LE_UINT16(header + 2) * 2;
|
||||
} else if (type < 0x7FFE) {
|
||||
byte count1 = header[2];
|
||||
byte count2 = header[3];
|
||||
_objSize = (count1 + count2) * 2;
|
||||
}
|
||||
_objSize += 4;
|
||||
_objData = new byte[_objSize];
|
||||
memcpy(_objData, header, 4);
|
||||
source.read(_objData + 4, _objSize - 4);
|
||||
|
||||
return _objSize;
|
||||
|
||||
}
|
||||
|
||||
int ObjectV2::load(byte *source) {
|
||||
// Not implemented/used for version 2 objects
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ObjectV2::save(Common::WriteStream &dest) {
|
||||
dest.write(_objData, _objSize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16 ObjectV2::getFlags() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint16 ObjectV2::getClass() {
|
||||
return READ_LE_UINT16(_objData);
|
||||
}
|
||||
|
||||
uint16 ObjectV2::getSize() {
|
||||
return READ_LE_UINT16(_objData + 2);
|
||||
}
|
||||
|
||||
byte ObjectV2::getCount1() {
|
||||
return _objData[2];
|
||||
}
|
||||
|
||||
byte ObjectV2::getCount2() {
|
||||
return _objData[3];
|
||||
}
|
||||
|
||||
byte *ObjectV2::getData() {
|
||||
return _objData + 4;
|
||||
}
|
||||
|
||||
int ObjectV1::load(Common::SeekableReadStream &source) {
|
||||
ObjectV2::load(source);
|
||||
// Manhole EGA has the two property counts reversed
|
||||
SWAP(_objData[2], _objData[3]);
|
||||
return _objSize;
|
||||
}
|
||||
|
||||
int ObjectV3::load(Common::SeekableReadStream &source) {
|
||||
|
||||
_freeData = true;
|
||||
source.readUint16LE(); // skip flags
|
||||
uint16 type = source.readUint16LE();
|
||||
if (type == 0x7FFF) {
|
||||
_objSize = source.readUint16LE();
|
||||
} else if (type == 0x7FFE) {
|
||||
_objSize = source.readUint16LE() * 2;
|
||||
} else if (type < 0x7FFE) {
|
||||
byte count1 = source.readByte();
|
||||
byte count2 = source.readByte();
|
||||
_objSize = (count1 + count2) * 2;
|
||||
}
|
||||
source.seek(-6, SEEK_CUR);
|
||||
_objSize += 6;
|
||||
_objData = new byte[_objSize];
|
||||
source.read(_objData, _objSize);
|
||||
return _objSize;
|
||||
|
||||
}
|
||||
|
||||
int ObjectV3::load(byte *source) {
|
||||
_objData = source;
|
||||
_freeData = false;
|
||||
if (getClass() < 0x7FFE) {
|
||||
_objSize = (getCount1() + getCount2()) * 2;
|
||||
} else {
|
||||
_objSize = getSize();
|
||||
}
|
||||
_objSize += 6;
|
||||
return _objSize;
|
||||
}
|
||||
|
||||
int ObjectV3::save(Common::WriteStream &dest) {
|
||||
// Not implemented/used for version 3 objects
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16 ObjectV3::getFlags() {
|
||||
return READ_LE_UINT16(_objData);
|
||||
}
|
||||
|
||||
uint16 ObjectV3::getClass() {
|
||||
return READ_LE_UINT16(_objData + 2);
|
||||
}
|
||||
|
||||
uint16 ObjectV3::getSize() {
|
||||
return READ_LE_UINT16(_objData + 4);
|
||||
}
|
||||
|
||||
byte ObjectV3::getCount1() {
|
||||
return _objData[4];
|
||||
}
|
||||
|
||||
byte ObjectV3::getCount2() {
|
||||
return _objData[5];
|
||||
}
|
||||
|
||||
byte *ObjectV3::getData() {
|
||||
return _objData + 6;
|
||||
}
|
||||
|
||||
|
||||
|
||||
GameDatabase::GameDatabase(MadeEngine *vm) : _vm(vm) {
|
||||
_gameState = nullptr;
|
||||
_gameStateSize = 0;
|
||||
_mainCodeObjectIndex = 0;
|
||||
_isRedSource = false;
|
||||
}
|
||||
|
||||
GameDatabase::~GameDatabase() {
|
||||
delete[] _gameState;
|
||||
}
|
||||
|
||||
void GameDatabase::open(const char *filename) {
|
||||
debug(1, "GameDatabase::open() Loading from %s", filename);
|
||||
_isRedSource = false;
|
||||
_filename = filename;
|
||||
_redFilename = "";
|
||||
Common::File fd;
|
||||
if (!fd.open(filename))
|
||||
error("GameDatabase::open() Could not open %s", filename);
|
||||
load(fd);
|
||||
fd.close();
|
||||
}
|
||||
|
||||
void GameDatabase::openFromRed(const char *redFilename, const char *filename) {
|
||||
debug(1, "GameDatabase::openFromRed() Loading from %s->%s", redFilename, filename);
|
||||
_isRedSource = true;
|
||||
_filename = filename;
|
||||
_redFilename = redFilename;
|
||||
Common::SeekableReadStream *fileS = RedReader::loadFromRed(redFilename, filename);
|
||||
if (!fileS)
|
||||
error("GameDatabase::openFromRed() Could not load %s from %s", filename, redFilename);
|
||||
load(*fileS);
|
||||
delete fileS;
|
||||
}
|
||||
|
||||
void GameDatabase::reload() {
|
||||
if (!_isRedSource) {
|
||||
Common::File fd;
|
||||
if (!fd.open(_filename.c_str()))
|
||||
error("GameDatabase::reload() Could not open %s", _filename.c_str());
|
||||
reloadFromStream(fd);
|
||||
} else {
|
||||
Common::SeekableReadStream *fileS = RedReader::loadFromRed(_redFilename.c_str(), _filename.c_str());
|
||||
if (!fileS)
|
||||
error("GameDatabase::openFromRed() Could not load %s from %s", _filename.c_str(), _redFilename.c_str());
|
||||
reloadFromStream(*fileS);
|
||||
delete fileS;
|
||||
}
|
||||
}
|
||||
|
||||
int16 GameDatabase::getVar(int16 index) {
|
||||
return (int16)READ_LE_UINT16(_gameState + index * 2);
|
||||
}
|
||||
|
||||
void GameDatabase::setVar(int16 index, int16 value) {
|
||||
WRITE_LE_UINT16(_gameState + index * 2, value);
|
||||
}
|
||||
|
||||
const char *GameDatabase::getObjectString(int16 index) {
|
||||
Object *obj = getObject(index);
|
||||
if (obj)
|
||||
return obj->getString();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
void GameDatabase::setObjectString(int16 index, const char *str) {
|
||||
Object *obj = getObject(index);
|
||||
if (obj)
|
||||
obj->setString(str);
|
||||
}
|
||||
|
||||
int16 *GameDatabase::findObjectPropertyCached(int16 objectIndex, int16 propertyId, int16 &propertyFlag) {
|
||||
uint32 id = (objectIndex << 16) | propertyId;
|
||||
ObjectPropertyCacheMap::iterator iter = _objectPropertyCache.find(id);
|
||||
int16 *propertyPtr = nullptr;
|
||||
if (iter != _objectPropertyCache.end()) {
|
||||
propertyPtr = (*iter)._value;
|
||||
} else {
|
||||
propertyPtr = findObjectProperty(objectIndex, propertyId, propertyFlag);
|
||||
_objectPropertyCache[id] = propertyPtr;
|
||||
}
|
||||
propertyFlag = 1;
|
||||
return propertyPtr;
|
||||
}
|
||||
|
||||
int16 GameDatabase::getObjectProperty(int16 objectIndex, int16 propertyId) {
|
||||
|
||||
if (objectIndex == 0)
|
||||
return 0;
|
||||
|
||||
int16 propertyFlag;
|
||||
//int16 *property = findObjectProperty(objectIndex, propertyId, propertyFlag);
|
||||
int16 *property = findObjectPropertyCached(objectIndex, propertyId, propertyFlag);
|
||||
|
||||
if (property) {
|
||||
return (int16)READ_LE_UINT16(property);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int16 GameDatabase::setObjectProperty(int16 objectIndex, int16 propertyId, int16 value) {
|
||||
|
||||
if (objectIndex == 0)
|
||||
return 0;
|
||||
|
||||
int16 propertyFlag;
|
||||
//int16 *property = findObjectProperty(objectIndex, propertyId, propertyFlag);
|
||||
int16 *property = findObjectPropertyCached(objectIndex, propertyId, propertyFlag);
|
||||
|
||||
if (property) {
|
||||
if (propertyFlag == 1) {
|
||||
WRITE_LE_UINT16(property, value);
|
||||
} else {
|
||||
warning("GameDatabase::setObjectProperty(%04X, %04X, %04X) Trying to set constant property",
|
||||
objectIndex, propertyId, value);
|
||||
}
|
||||
return value;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GameDatabase::dumpObject(int16 index) {
|
||||
Object *obj = getObject(index);
|
||||
obj->dump(Common::String::format("obj%04X.0", index));
|
||||
}
|
||||
|
||||
|
||||
/* GameDatabaseV2 */
|
||||
|
||||
GameDatabaseV2::GameDatabaseV2(MadeEngine *vm) : GameDatabase(vm), _gameText(nullptr) {
|
||||
}
|
||||
|
||||
GameDatabaseV2::~GameDatabaseV2() {
|
||||
delete[] _gameText;
|
||||
}
|
||||
|
||||
void GameDatabaseV2::load(Common::SeekableReadStream &sourceS) {
|
||||
int16 version = sourceS.readUint16LE();
|
||||
|
||||
// Manhole:NE, Rodney's Funscreen and LGOP2 are version 54
|
||||
// The earlier EGA version of Manhole is version 40
|
||||
if (version != 54 && version != 40)
|
||||
warning("Unknown database version, known versions are 54 and 40");
|
||||
|
||||
char header[6];
|
||||
sourceS.read(header, 6);
|
||||
if (strncmp(header, "ADVSYS", 6))
|
||||
warning ("Unexpected database header, expected ADVSYS");
|
||||
|
||||
uint32 textOffs = 0, objectsOffs = 0, objectsSize = 0, textSize;
|
||||
uint16 objectCount = 0, varObjectCount = 0;
|
||||
|
||||
sourceS.readUint16LE(); // skip sub-version
|
||||
sourceS.skip(18); // skip program name
|
||||
|
||||
if (version == 40) {
|
||||
sourceS.readUint16LE(); // skip unused
|
||||
objectCount = sourceS.readUint16LE();
|
||||
_gameStateSize = sourceS.readUint16LE() * 2;
|
||||
objectsOffs = sourceS.readUint16LE() * 512;
|
||||
textOffs = sourceS.readUint16LE() * 512;
|
||||
_mainCodeObjectIndex = sourceS.readUint16LE();
|
||||
varObjectCount = 0; // unused in V1
|
||||
objectsSize = 0; // unused in V1
|
||||
} else if (version == 54) {
|
||||
textOffs = sourceS.readUint16LE() * 512;
|
||||
objectCount = sourceS.readUint16LE();
|
||||
varObjectCount = sourceS.readUint16LE();
|
||||
_gameStateSize = sourceS.readUint16LE() * 2;
|
||||
sourceS.readUint16LE(); // unknown
|
||||
objectsOffs = sourceS.readUint16LE() * 512;
|
||||
sourceS.readUint16LE(); // unknown
|
||||
_mainCodeObjectIndex = sourceS.readUint16LE();
|
||||
sourceS.readUint16LE(); // unknown
|
||||
objectsSize = sourceS.readUint32LE() * 2;
|
||||
}
|
||||
|
||||
textSize = objectsOffs - textOffs;
|
||||
|
||||
debug(0, "textOffs = %08X; textSize = %08X; objectCount = %d; varObjectCount = %d; gameStateSize = %d; objectsOffs = %08X; objectsSize = %d; _mainCodeObjectIndex = %04X\n", textOffs, textSize, objectCount, varObjectCount, _gameStateSize, objectsOffs, objectsSize, _mainCodeObjectIndex);
|
||||
|
||||
_gameState = new byte[_gameStateSize + 2]();
|
||||
setVar(1, objectCount);
|
||||
|
||||
sourceS.seek(textOffs);
|
||||
_gameText = new char[textSize];
|
||||
sourceS.read(_gameText, textSize);
|
||||
// "Decrypt" the text data
|
||||
for (uint32 i = 0; i < textSize; i++)
|
||||
_gameText[i] += 0x1E;
|
||||
|
||||
sourceS.seek(objectsOffs);
|
||||
|
||||
if (version == 40) {
|
||||
// Initialize the object array
|
||||
for (uint32 i = 0; i < objectCount; i++)
|
||||
_objects.push_back(NULL);
|
||||
// Read two "sections" of objects
|
||||
// It seems the first section is data while the second one is code.
|
||||
// The interpreter however doesn't care which section the objects come from.
|
||||
for (int section = 0; section < 2; section++) {
|
||||
while (!sourceS.eos()) {
|
||||
int16 objIndex = sourceS.readUint16LE();
|
||||
debug(1, "objIndex = %04X; section = %d", objIndex, section);
|
||||
if (objIndex == 0)
|
||||
break;
|
||||
Object *obj = new ObjectV1();
|
||||
obj->load(sourceS);
|
||||
_objects[objIndex - 1] = obj;
|
||||
}
|
||||
}
|
||||
} else if (version == 54) {
|
||||
for (uint32 i = 0; i < objectCount; i++) {
|
||||
Object *obj = new ObjectV2();
|
||||
int objSize = obj->load(sourceS);
|
||||
// Objects are aligned on 2-byte-boundaries, skip unused bytes
|
||||
sourceS.skip(objSize % 2);
|
||||
_objects.push_back(obj);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GameDatabaseV2::reloadFromStream(Common::SeekableReadStream &sourceS) {
|
||||
// Not used in version 2 games
|
||||
}
|
||||
|
||||
bool GameDatabaseV2::getSavegameDescription(const char *filename, Common::String &description, int16 version) {
|
||||
// Not used in version 2 games
|
||||
return false;
|
||||
}
|
||||
|
||||
int16 GameDatabaseV2::savegame(const char *filename, const char *description, int16 version) {
|
||||
Common::OutSaveFile *out;
|
||||
int16 result = 0;
|
||||
if (!(out = g_system->getSavefileManager()->openForSaving(filename))) {
|
||||
warning("Can't create file '%s', game not saved", filename);
|
||||
return 6;
|
||||
}
|
||||
// Variable 0 is not saved
|
||||
out->write(_gameState + 2, _gameStateSize - 2);
|
||||
for (uint i = 0; i < _objects.size(); i++)
|
||||
_objects[i]->save(*out);
|
||||
out->finalize();
|
||||
delete out;
|
||||
return result;
|
||||
}
|
||||
|
||||
int16 GameDatabaseV2::loadgame(const char *filename, int16 version) {
|
||||
Common::InSaveFile *in;
|
||||
int16 result = 0;
|
||||
if (!(in = g_system->getSavefileManager()->openForLoading(filename))) {
|
||||
warning("Can't open file '%s', game not loaded", filename);
|
||||
return 1;
|
||||
}
|
||||
// Variable 0 is not loaded
|
||||
in->read(_gameState + 2, _gameStateSize - 2);
|
||||
for (uint i = 0; i < _objects.size(); i++) {
|
||||
_objects[i]->load(*in);
|
||||
}
|
||||
delete in;
|
||||
|
||||
_objectPropertyCache.clear(); // make sure to clear cache
|
||||
return result;
|
||||
}
|
||||
|
||||
int16 *GameDatabaseV2::findObjectProperty(int16 objectIndex, int16 propertyId, int16 &propertyFlag) {
|
||||
|
||||
Object *obj = getObject(objectIndex);
|
||||
if (obj->getClass() >= 0x7FFE) {
|
||||
error("GameDatabaseV2::findObjectProperty(%04X, %04X) Not an object", objectIndex, propertyId);
|
||||
}
|
||||
|
||||
int16 *prop = (int16 *)obj->getData();
|
||||
byte count1 = obj->getCount1();
|
||||
byte count2 = obj->getCount2();
|
||||
|
||||
int16 *propPtr1 = prop + count1;
|
||||
int16 *propPtr2 = prop + count2;
|
||||
|
||||
// First see if the property exists in the given object
|
||||
while (count2--) {
|
||||
if ((READ_LE_UINT16(prop) & 0x7FFF) == propertyId) {
|
||||
propertyFlag = obj->getFlags() & 1;
|
||||
return propPtr1;
|
||||
}
|
||||
prop++;
|
||||
propPtr1++;
|
||||
}
|
||||
|
||||
// Now check in the object hierarchy of the given object
|
||||
int16 parentObjectIndex = obj->getClass();
|
||||
if (parentObjectIndex == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
while (parentObjectIndex != 0) {
|
||||
|
||||
obj = getObject(parentObjectIndex);
|
||||
|
||||
prop = (int16 *)obj->getData();
|
||||
count1 = obj->getCount1();
|
||||
count2 = obj->getCount2();
|
||||
|
||||
propPtr1 = propPtr2 + count1 - count2;
|
||||
int16 *propertyPtr = prop + count1;
|
||||
|
||||
while (count2--) {
|
||||
if ((READ_LE_UINT16(prop) & 0x8000) == 0) {
|
||||
if ((READ_LE_UINT16(prop) & 0x7FFF) == propertyId) {
|
||||
propertyFlag = obj->getFlags() & 1;
|
||||
return propPtr1;
|
||||
} else {
|
||||
propPtr1++;
|
||||
}
|
||||
} else {
|
||||
if ((READ_LE_UINT16(prop) & 0x7FFF) == propertyId) {
|
||||
propertyFlag = obj->getFlags() & 1;
|
||||
return propertyPtr;
|
||||
}
|
||||
}
|
||||
prop++;
|
||||
propertyPtr++;
|
||||
}
|
||||
|
||||
parentObjectIndex = obj->getClass();
|
||||
|
||||
}
|
||||
|
||||
debug(1, "findObjectProperty(%04X, %04X) Property not found", objectIndex, propertyId);
|
||||
return nullptr;
|
||||
|
||||
}
|
||||
|
||||
const char *GameDatabaseV2::getString(uint16 offset) {
|
||||
return (const char*)&_gameText[offset * 4];
|
||||
}
|
||||
|
||||
|
||||
/* GameDatabaseV3 */
|
||||
|
||||
GameDatabaseV3::GameDatabaseV3(MadeEngine *vm) : GameDatabase(vm) {
|
||||
_gameText = nullptr;
|
||||
_gameStateOffs = 0;
|
||||
}
|
||||
|
||||
void GameDatabaseV3::load(Common::SeekableReadStream &sourceS) {
|
||||
char header[6];
|
||||
sourceS.read(header, 6);
|
||||
if (strncmp(header, "ADVSYS", 6))
|
||||
warning ("Unexpected database header, expected ADVSYS");
|
||||
|
||||
/*uint32 unk = */sourceS.readUint32LE();
|
||||
|
||||
sourceS.skip(20);
|
||||
|
||||
uint32 objectIndexOffs = sourceS.readUint32LE();
|
||||
uint16 objectCount = sourceS.readUint16LE();
|
||||
_gameStateOffs = sourceS.readUint32LE();
|
||||
_gameStateSize = sourceS.readUint32LE();
|
||||
uint32 objectsOffs = sourceS.readUint32LE();
|
||||
uint32 objectsSize = sourceS.readUint32LE();
|
||||
_mainCodeObjectIndex = sourceS.readUint16LE();
|
||||
|
||||
debug(2, "objectIndexOffs = %08X; objectCount = %d; gameStateOffs = %08X; gameStateSize = %d; objectsOffs = %08X; objectsSize = %d\n", objectIndexOffs, objectCount, _gameStateOffs, _gameStateSize, objectsOffs, objectsSize);
|
||||
|
||||
_gameState = new byte[_gameStateSize];
|
||||
sourceS.seek(_gameStateOffs);
|
||||
sourceS.read(_gameState, _gameStateSize);
|
||||
|
||||
Common::Array<uint32> objectOffsets;
|
||||
sourceS.seek(objectIndexOffs);
|
||||
for (uint32 i = 0; i < objectCount; i++)
|
||||
objectOffsets.push_back(sourceS.readUint32LE());
|
||||
|
||||
for (uint32 i = 0; i < objectCount; i++) {
|
||||
Object *obj = new ObjectV3();
|
||||
// The LSB indicates if it's a constant or variable object.
|
||||
// Constant objects are loaded from disk, while variable objects exist
|
||||
// in the _gameState buffer.
|
||||
if (objectOffsets[i] & 1) {
|
||||
sourceS.seek(objectsOffs + objectOffsets[i] - 1);
|
||||
obj->load(sourceS);
|
||||
} else {
|
||||
obj->load(_gameState + objectOffsets[i]);
|
||||
}
|
||||
_objects.push_back(obj);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GameDatabaseV3::reloadFromStream(Common::SeekableReadStream &sourceS) {
|
||||
sourceS.seek(_gameStateOffs);
|
||||
sourceS.read(_gameState, _gameStateSize);
|
||||
|
||||
_objectPropertyCache.clear(); // make sure to clear cache
|
||||
}
|
||||
|
||||
bool GameDatabaseV3::getSavegameDescription(const char *filename, Common::String &description, int16 version) {
|
||||
Common::InSaveFile *in;
|
||||
char desc[64];
|
||||
|
||||
if (!(in = g_system->getSavefileManager()->openForLoading(filename))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 header = in->readUint32BE();
|
||||
if (header != MKTAG('S','G','A','M')) {
|
||||
warning("Save game header missing");
|
||||
delete in;
|
||||
return false;
|
||||
}
|
||||
|
||||
int32 size = in->readUint32LE();
|
||||
int16 saveVersion = in->readUint16LE();
|
||||
|
||||
if (saveVersion != version) {
|
||||
warning("Save game %s was saved with a different version of the game. Game version is %d, save version is %d", filename, version, saveVersion);
|
||||
delete in;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (size != in->size() - 64) {
|
||||
warning("Unexpected save game size. Expected %d, size is %d (file size - 64)", size, (int)in->size() - 64);
|
||||
delete in;
|
||||
return false;
|
||||
}
|
||||
|
||||
in->read(desc, 64);
|
||||
description = desc;
|
||||
|
||||
delete in;
|
||||
return true;
|
||||
}
|
||||
|
||||
int16 GameDatabaseV3::savegame(const char *filename, const char *description, int16 version) {
|
||||
Common::OutSaveFile *out;
|
||||
char desc[64];
|
||||
int16 result = 0;
|
||||
uint32 size = 4 + 4 + 2 + _gameStateSize;
|
||||
if (!(out = g_system->getSavefileManager()->openForSaving(filename))) {
|
||||
warning("Can't create file '%s', game not saved", filename);
|
||||
return 6;
|
||||
}
|
||||
Common::strlcpy(desc, description, 64);
|
||||
out->writeUint32BE(MKTAG('S','G','A','M'));
|
||||
out->writeUint32LE(size);
|
||||
out->writeUint16LE(version);
|
||||
out->write(desc, 64);
|
||||
out->write(_gameState, _gameStateSize);
|
||||
out->finalize();
|
||||
delete out;
|
||||
return result;
|
||||
}
|
||||
|
||||
int16 GameDatabaseV3::loadgame(const char *filename, int16 version) {
|
||||
Common::InSaveFile *in;
|
||||
uint32 expectedSize = 4 + 4 + 2 + _gameStateSize;
|
||||
|
||||
if (!(in = g_system->getSavefileManager()->openForLoading(filename))) {
|
||||
warning("Can't open file '%s', game not loaded", filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32 header = in->readUint32BE();
|
||||
if (header != MKTAG('S','G','A','M')) {
|
||||
warning("Save game header missing");
|
||||
delete in;
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32 size = in->readUint32LE();
|
||||
int16 saveVersion = in->readUint16LE();
|
||||
|
||||
if (saveVersion != version) {
|
||||
warning("Save game %s was saved with a different version of the game. Game version is %d, save version is %d", filename, version, saveVersion);
|
||||
delete in;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (size != expectedSize) {
|
||||
warning("Unexpected save game size. Expected %d, size is %d", expectedSize, size);
|
||||
delete in;
|
||||
return 1;
|
||||
}
|
||||
|
||||
in->skip(64); // skip savegame description
|
||||
in->read(_gameState, _gameStateSize);
|
||||
delete in;
|
||||
|
||||
_objectPropertyCache.clear(); // make sure to clear cache
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int16 *GameDatabaseV3::findObjectProperty(int16 objectIndex, int16 propertyId, int16 &propertyFlag) {
|
||||
Object *obj = getObject(objectIndex);
|
||||
if (obj->getClass() >= 0x7FFE) {
|
||||
error("GameDatabaseV3::findObjectProperty(%04X, %04X) Not an object", objectIndex, propertyId);
|
||||
}
|
||||
|
||||
int16 *prop = (int16 *)obj->getData();
|
||||
byte count1 = obj->getCount1();
|
||||
byte count2 = obj->getCount2();
|
||||
|
||||
int16 *propPtr1 = prop + count1;
|
||||
int16 *propPtr2 = prop + count2;
|
||||
|
||||
// First see if the property exists in the given object
|
||||
while (count2-- > 0) {
|
||||
if ((READ_LE_UINT16(prop) & 0x3FFF) == propertyId) {
|
||||
if (READ_LE_UINT16(prop) & 0x4000) {
|
||||
propertyFlag = 1;
|
||||
return (int16 *)_gameState + READ_LE_UINT16(propPtr1);
|
||||
} else {
|
||||
propertyFlag = obj->getFlags() & 1;
|
||||
return propPtr1;
|
||||
}
|
||||
}
|
||||
prop++;
|
||||
propPtr1++;
|
||||
}
|
||||
|
||||
// Now check in the object hierarchy of the given object
|
||||
int16 parentObjectIndex = obj->getClass();
|
||||
if (parentObjectIndex == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
while (parentObjectIndex != 0) {
|
||||
|
||||
obj = getObject(parentObjectIndex);
|
||||
|
||||
prop = (int16 *)obj->getData();
|
||||
count1 = obj->getCount1();
|
||||
count2 = obj->getCount2();
|
||||
|
||||
propPtr1 = propPtr2 + count1 - count2;
|
||||
int16 *propertyPtr = prop + count1;
|
||||
|
||||
while (count2-- > 0) {
|
||||
if (!(READ_LE_UINT16(prop) & 0x8000)) {
|
||||
if ((READ_LE_UINT16(prop) & 0x3FFF) == propertyId) {
|
||||
if (READ_LE_UINT16(prop) & 0x4000) {
|
||||
propertyFlag = 1;
|
||||
return (int16 *)_gameState + READ_LE_UINT16(propPtr1);
|
||||
} else {
|
||||
propertyFlag = obj->getFlags() & 1;
|
||||
return propPtr1;
|
||||
}
|
||||
} else {
|
||||
propPtr1++;
|
||||
}
|
||||
} else {
|
||||
if ((READ_LE_UINT16(prop) & 0x3FFF) == propertyId) {
|
||||
if (READ_LE_UINT16(prop) & 0x4000) {
|
||||
propertyFlag = 1;
|
||||
return (int16 *)_gameState + READ_LE_UINT16(propertyPtr);
|
||||
} else {
|
||||
propertyFlag = obj->getFlags() & 1;
|
||||
return propertyPtr;
|
||||
}
|
||||
}
|
||||
}
|
||||
prop++;
|
||||
propertyPtr++;
|
||||
}
|
||||
|
||||
parentObjectIndex = obj->getClass();
|
||||
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
||||
}
|
||||
|
||||
const char *GameDatabaseV3::getString(uint16 offset) {
|
||||
// Not used in version 3 games
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // End of namespace Made
|
||||
198
engines/made/database.h
Normal file
198
engines/made/database.h
Normal file
@@ -0,0 +1,198 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MADE_DATABASE_H
|
||||
#define MADE_DATABASE_H
|
||||
|
||||
#include "common/hashmap.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
class WriteStream;
|
||||
class String;
|
||||
}
|
||||
|
||||
namespace Made {
|
||||
|
||||
class MadeEngine;
|
||||
|
||||
class Object {
|
||||
public:
|
||||
|
||||
Object();
|
||||
virtual ~Object();
|
||||
|
||||
virtual int load(Common::SeekableReadStream &source) = 0;
|
||||
virtual int load(byte *source) = 0;
|
||||
virtual int save(Common::WriteStream &dest) = 0;
|
||||
virtual uint16 getFlags() = 0;
|
||||
virtual uint16 getClass() = 0;
|
||||
virtual uint16 getSize() = 0;
|
||||
virtual byte getCount1() = 0;
|
||||
virtual byte getCount2() = 0;
|
||||
virtual byte *getData() = 0;
|
||||
virtual bool isConstant() = 0;
|
||||
|
||||
const char *getString();
|
||||
void setString(const char *str);
|
||||
|
||||
bool isObject();
|
||||
bool isVector();
|
||||
|
||||
int16 getVectorSize();
|
||||
int16 getVectorItem(int16 index);
|
||||
void setVectorItem(int16 index, int16 value);
|
||||
|
||||
void dump(const Common::String &filename);
|
||||
|
||||
protected:
|
||||
bool _freeData;
|
||||
uint16 _objSize;
|
||||
byte *_objData;
|
||||
};
|
||||
|
||||
class ObjectV2 : public Object {
|
||||
public:
|
||||
int load(Common::SeekableReadStream &source) override;
|
||||
int load(byte *source) override;
|
||||
int save(Common::WriteStream &dest) override;
|
||||
uint16 getFlags() override;
|
||||
uint16 getClass() override;
|
||||
uint16 getSize() override;
|
||||
byte getCount1() override;
|
||||
byte getCount2() override;
|
||||
byte *getData() override;
|
||||
|
||||
bool isConstant() override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class ObjectV1 : public ObjectV2 {
|
||||
public:
|
||||
int load(Common::SeekableReadStream &source) override;
|
||||
};
|
||||
|
||||
class ObjectV3 : public Object {
|
||||
public:
|
||||
int load(Common::SeekableReadStream &source) override;
|
||||
int load(byte *source) override;
|
||||
int save(Common::WriteStream &dest) override;
|
||||
uint16 getFlags() override;
|
||||
uint16 getClass() override;
|
||||
uint16 getSize() override;
|
||||
byte getCount1() override;
|
||||
byte getCount2() override;
|
||||
byte *getData() override;
|
||||
|
||||
bool isConstant() override {
|
||||
return !(getFlags() & 1);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class GameDatabase {
|
||||
public:
|
||||
|
||||
GameDatabase(MadeEngine *vm);
|
||||
virtual ~GameDatabase();
|
||||
|
||||
void open(const char *filename);
|
||||
void openFromRed(const char *redFilename, const char *filename);
|
||||
|
||||
void reload();
|
||||
|
||||
Object *getObject(int16 index) const {
|
||||
if (index >= 1)
|
||||
return _objects[index - 1];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint getObjectCount() const { return _objects.size(); }
|
||||
|
||||
int16 getMainCodeObjectIndex() const { return _mainCodeObjectIndex; }
|
||||
|
||||
int16 getVar(int16 index);
|
||||
void setVar(int16 index, int16 value);
|
||||
|
||||
const char *getObjectString(int16 index);
|
||||
void setObjectString(int16 index, const char *str);
|
||||
|
||||
virtual int16 *findObjectProperty(int16 objectIndex, int16 propertyId, int16 &propertyFlag) = 0;
|
||||
int16 *findObjectPropertyCached(int16 objectIndex, int16 propertyId, int16 &propertyFlag);
|
||||
virtual const char *getString(uint16 offset) = 0;
|
||||
virtual bool getSavegameDescription(const char *filename, Common::String &description, int16 version) = 0;
|
||||
virtual int16 savegame(const char *filename, const char *description, int16 version) = 0;
|
||||
virtual int16 loadgame(const char *filename, int16 version) = 0;
|
||||
|
||||
int16 getObjectProperty(int16 objectIndex, int16 propertyId);
|
||||
int16 setObjectProperty(int16 objectIndex, int16 propertyId, int16 value);
|
||||
|
||||
void dumpObject(int16 index);
|
||||
|
||||
protected:
|
||||
typedef Common::HashMap<uint32, int16 *> ObjectPropertyCacheMap;
|
||||
MadeEngine *_vm;
|
||||
Common::Array<Object *> _objects;
|
||||
ObjectPropertyCacheMap _objectPropertyCache;
|
||||
byte *_gameState;
|
||||
uint32 _gameStateSize;
|
||||
int16 _mainCodeObjectIndex;
|
||||
bool _isRedSource;
|
||||
Common::String _filename, _redFilename;
|
||||
virtual void load(Common::SeekableReadStream &sourceS) = 0;
|
||||
virtual void reloadFromStream(Common::SeekableReadStream &sourceS) = 0;
|
||||
};
|
||||
|
||||
class GameDatabaseV2 : public GameDatabase {
|
||||
public:
|
||||
GameDatabaseV2(MadeEngine *vm);
|
||||
~GameDatabaseV2() override;
|
||||
int16 *findObjectProperty(int16 objectIndex, int16 propertyId, int16 &propertyFlag) override;
|
||||
const char *getString(uint16 offset) override;
|
||||
bool getSavegameDescription(const char *filename, Common::String &description, int16 version) override;
|
||||
int16 savegame(const char *filename, const char *description, int16 version) override;
|
||||
int16 loadgame(const char *filename, int16 version) override;
|
||||
protected:
|
||||
char *_gameText;
|
||||
void load(Common::SeekableReadStream &sourceS) override;
|
||||
void reloadFromStream(Common::SeekableReadStream &sourceS) override;
|
||||
};
|
||||
|
||||
class GameDatabaseV3 : public GameDatabase {
|
||||
public:
|
||||
GameDatabaseV3(MadeEngine *vm);
|
||||
int16 *findObjectProperty(int16 objectIndex, int16 propertyId, int16 &propertyFlag) override;
|
||||
const char *getString(uint16 offset) override;
|
||||
bool getSavegameDescription(const char *filename, Common::String &description, int16 version) override;
|
||||
int16 savegame(const char *filename, const char *description, int16 version) override;
|
||||
int16 loadgame(const char *filename, int16 version) override;
|
||||
protected:
|
||||
char *_gameText;
|
||||
uint32 _gameStateOffs;
|
||||
void load(Common::SeekableReadStream &sourceS) override;
|
||||
void reloadFromStream(Common::SeekableReadStream &sourceS) override;
|
||||
};
|
||||
|
||||
} // End of namespace Made
|
||||
|
||||
#endif /* MADE_H */
|
||||
75
engines/made/detection.cpp
Normal file
75
engines/made/detection.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "base/plugins.h"
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
#include "made/detection.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
|
||||
static const PlainGameDescriptor madeGames[] = {
|
||||
{"manhole", "The Manhole"},
|
||||
{"rtz", "Return to Zork"},
|
||||
{"lgop2", "Leather Goddesses of Phobos 2"},
|
||||
{"rodney", "Rodney's Funscreen"},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
#include "made/detection_tables.h"
|
||||
|
||||
class MadeMetaEngineDetection : public AdvancedMetaEngineDetection<Made::MadeGameDescription> {
|
||||
public:
|
||||
MadeMetaEngineDetection() : AdvancedMetaEngineDetection(Made::gameDescriptions, madeGames) {
|
||||
_guiOptions = GUIO1(GAMEOPTION_TTS);
|
||||
}
|
||||
|
||||
const char *getName() const override {
|
||||
return "made";
|
||||
}
|
||||
|
||||
const char *getEngineName() const override {
|
||||
return "MADE";
|
||||
}
|
||||
|
||||
const char *getOriginalCopyright() const override {
|
||||
return "MADE Engine (C) Activision";
|
||||
}
|
||||
|
||||
ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist, ADDetectedGameExtraInfo **extra) const override;
|
||||
};
|
||||
|
||||
ADDetectedGame MadeMetaEngineDetection::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist, ADDetectedGameExtraInfo **extra) const {
|
||||
// Set the default values for the fallback descriptor's ADGameDescription part.
|
||||
Made::g_fallbackDesc.desc.language = Common::UNK_LANG;
|
||||
Made::g_fallbackDesc.desc.platform = Common::kPlatformDOS;
|
||||
Made::g_fallbackDesc.desc.flags = ADGF_NO_FLAGS;
|
||||
|
||||
// Set default values for the fallback descriptor's MadeGameDescription part.
|
||||
Made::g_fallbackDesc.gameID = 0;
|
||||
Made::g_fallbackDesc.features = 0;
|
||||
Made::g_fallbackDesc.version = 3;
|
||||
|
||||
//return (const ADGameDescription *)&Made::g_fallbackDesc;
|
||||
return ADDetectedGame();
|
||||
}
|
||||
|
||||
REGISTER_PLUGIN_STATIC(MADE_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, MadeMetaEngineDetection);
|
||||
60
engines/made/detection.h
Normal file
60
engines/made/detection.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MADE_DETECTION_H
|
||||
#define MADE_DETECTION_H
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
namespace Made {
|
||||
|
||||
enum MadeGameID {
|
||||
GID_RTZ = 0,
|
||||
GID_MANHOLE = 1,
|
||||
GID_LGOP2 = 2,
|
||||
GID_RODNEY = 3
|
||||
};
|
||||
|
||||
enum MadeGameFeatures {
|
||||
GF_DEMO = 1 << 0,
|
||||
GF_CD = 1 << 1,
|
||||
GF_CD_COMPRESSED = 1 << 2,
|
||||
GF_FLOPPY = 1 << 3
|
||||
};
|
||||
|
||||
struct MadeGameDescription {
|
||||
AD_GAME_DESCRIPTION_HELPERS(desc);
|
||||
|
||||
ADGameDescription desc;
|
||||
|
||||
int gameID;
|
||||
int gameType;
|
||||
uint32 features;
|
||||
uint16 version;
|
||||
};
|
||||
|
||||
#define GAMEOPTION_INTRO_MUSIC_DIGITAL GUIO_GAMEOPTIONS1
|
||||
#define GAMEOPTION_TTS GUIO_GAMEOPTIONS2
|
||||
#define GAMEOPTION_WINDOWS_CURSORS GUIO_GAMEOPTIONS3
|
||||
|
||||
} // End of namespace Made
|
||||
|
||||
#endif // MADE_DETECTION_H
|
||||
660
engines/made/detection_tables.h
Normal file
660
engines/made/detection_tables.h
Normal file
@@ -0,0 +1,660 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MADE_DETECTION_TABLES_H
|
||||
#define MADE_DETECTION_TABLES_H
|
||||
|
||||
#include "made/detection.h"
|
||||
|
||||
#include "common/translation.h"
|
||||
|
||||
namespace Made {
|
||||
|
||||
static const MadeGameDescription gameDescriptions[] = {
|
||||
|
||||
{
|
||||
// NOTE: Return to Zork entries with *.dat are used to detect the game via rtzcd.dat,
|
||||
// which is packed inside rtzcd.red. Entries with *.red refer to the packed file
|
||||
// directly, which is the "official" way.
|
||||
|
||||
// Return to Zork - English CD version 1.0 9/15/93 (installed)
|
||||
// Ticket #8858 submitted by spookypeanut
|
||||
{
|
||||
"rtz",
|
||||
"V1.0, 9/15/93, installed, CD",
|
||||
AD_ENTRY1("rtzcd.dat", "e95c38ded389e39cfbf87a8cb250b12e"),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GAMEOPTION_INTRO_MUSIC_DIGITAL)
|
||||
},
|
||||
GID_RTZ,
|
||||
0,
|
||||
GF_CD,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// Return to Zork - English CD version 1.0 9/15/93
|
||||
// Ticket #8858 submitted by spookypeanut
|
||||
{
|
||||
"rtz",
|
||||
"V1.0, 9/15/93, CD",
|
||||
AD_ENTRY1("rtzcd.red", "cd8b62ece4677c438688c1de3f5379b9"),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO1(GAMEOPTION_INTRO_MUSIC_DIGITAL)
|
||||
},
|
||||
GID_RTZ,
|
||||
0,
|
||||
GF_CD_COMPRESSED,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// Return to Zork - English CD version 1.1 12/7/93 (installed)
|
||||
{
|
||||
"rtz",
|
||||
"V1.1, 12/7/93, installed, CD",
|
||||
AD_ENTRY1s("rtzcd.dat", "a1db8c97a78dae10f91d356f16ad07b8", 536064),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO1(GAMEOPTION_INTRO_MUSIC_DIGITAL)
|
||||
},
|
||||
GID_RTZ,
|
||||
0,
|
||||
GF_CD,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// Return to Zork - English OEM CD version 1.1 12/7/93
|
||||
{
|
||||
"rtz",
|
||||
"V1.1, 12/7/93, CD",
|
||||
AD_ENTRY1s("rtzcd.red", "c4e2430e6b6c6ff1562a80fb4a9df24c", 276177),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO1(GAMEOPTION_INTRO_MUSIC_DIGITAL)
|
||||
},
|
||||
GID_RTZ,
|
||||
0,
|
||||
GF_CD_COMPRESSED,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// Return to Zork - English Retail CD version 1.1 12/7/93
|
||||
{
|
||||
"rtz",
|
||||
"V1.1, 12/7/93, CD",
|
||||
AD_ENTRY1s("rtzcd.red", "c4e2430e6b6c6ff1562a80fb4a9df24c", 276466),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO1(GAMEOPTION_INTRO_MUSIC_DIGITAL)
|
||||
},
|
||||
GID_RTZ,
|
||||
0,
|
||||
GF_CD_COMPRESSED,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// Return to Zork - English CD version 1.2 9/29/94 (installed)
|
||||
// Supplied by Dark-Star in the ScummVM forums
|
||||
{
|
||||
"rtz",
|
||||
"V1.2, 9/29/94, installed, CD",
|
||||
AD_ENTRY1("rtzcd.dat", "9d740378da2d16e83d0d0efff01bf83a"),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO1(GAMEOPTION_INTRO_MUSIC_DIGITAL)
|
||||
},
|
||||
GID_RTZ,
|
||||
0,
|
||||
GF_CD,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// Return to Zork - English CD version 1.2 9/29/94
|
||||
{
|
||||
"rtz",
|
||||
"V1.2, 9/29/94, CD",
|
||||
{
|
||||
{ "rtzcd.red", 0, "946997d8b0aa6cb4e848bad02a1fc3d2", 276584 },
|
||||
{ "rtzcd.prj", 0, "974d74410c3c29d50e857863e8bf40e2", 43016792 },
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO1(GAMEOPTION_INTRO_MUSIC_DIGITAL)
|
||||
},
|
||||
GID_RTZ,
|
||||
0,
|
||||
GF_CD_COMPRESSED,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// Return to Zork - German CD version 1.2 9/29/94 (installed)
|
||||
// Supplied by Dark-Star in the ScummVM forums
|
||||
{
|
||||
"rtz",
|
||||
"V1.2, 9/29/94, installed, CD",
|
||||
AD_ENTRY1s("rtzcd.dat", "9d740378da2d16e83d0d0efff01bf83a", 525824),
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO1(GAMEOPTION_INTRO_MUSIC_DIGITAL)
|
||||
},
|
||||
GID_RTZ,
|
||||
0,
|
||||
GF_CD,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// Return to Zork - German CD version 1.2 4/18/95
|
||||
// Supplied by Dark-Star in the ScummVM forums
|
||||
{
|
||||
"rtz",
|
||||
"V1.2, 4/18/95, CD",
|
||||
AD_ENTRY1s("rtzcd.red", "946997d8b0aa6cb4e848bad02a1fc3d2", 355442),
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO1(GAMEOPTION_INTRO_MUSIC_DIGITAL)
|
||||
},
|
||||
GID_RTZ,
|
||||
0,
|
||||
GF_CD_COMPRESSED,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// Return to Zork - Italian CD version 1.2 3/31/95 (installed)
|
||||
// Patch #4225 submitted by goodoldgeorg
|
||||
{
|
||||
"rtz",
|
||||
"V1.2, 3/31/95, installed, CD",
|
||||
AD_ENTRY1s("rtzcd.dat", "5b86035aed0277f96e3d173542b5364a", 523776),
|
||||
Common::IT_ITA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO1(GAMEOPTION_INTRO_MUSIC_DIGITAL)
|
||||
},
|
||||
GID_RTZ,
|
||||
0,
|
||||
GF_CD,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// Return to Zork - Italian CD version 1.2 3/31/95
|
||||
// Patch #4225 submitted by goodoldgeorg
|
||||
{
|
||||
"rtz",
|
||||
"V1.2, 3/31/95, CD",
|
||||
AD_ENTRY1s("rtzcd.red", "946997d8b0aa6cb4e848bad02a1fc3d2", 354971),
|
||||
Common::IT_ITA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO1(GAMEOPTION_INTRO_MUSIC_DIGITAL)
|
||||
},
|
||||
GID_RTZ,
|
||||
0,
|
||||
GF_CD_COMPRESSED,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// Return to Zork - French CD version 1.2 5/13/95 (installed)
|
||||
// Patch #4225 submitted by goodoldgeorg
|
||||
{
|
||||
"rtz",
|
||||
"V1.2, 5/13/95, installed, CD",
|
||||
AD_ENTRY1s("rtzcd.dat", "bde8251a8e34e87c54e3f93147d56c9e", 523776),
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO1(GAMEOPTION_INTRO_MUSIC_DIGITAL)
|
||||
},
|
||||
GID_RTZ,
|
||||
0,
|
||||
GF_CD,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// Return to Zork - French CD version 1.2 5/13/95
|
||||
// Patch #4225 submitted by goodoldgeorg
|
||||
{
|
||||
"rtz",
|
||||
"V1.2, 3/31/95, CD",
|
||||
AD_ENTRY1s("rtzcd.red", "946997d8b0aa6cb4e848bad02a1fc3d2", 354614),
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO1(GAMEOPTION_INTRO_MUSIC_DIGITAL)
|
||||
},
|
||||
GID_RTZ,
|
||||
0,
|
||||
GF_CD_COMPRESSED,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// Return to Zork - Korean CD version 1.2 9/29/94
|
||||
// Dub only. No text was translated, even in menus, so there are no font issues.
|
||||
// submitted by trembyle
|
||||
{
|
||||
"rtz",
|
||||
"V1.2, 9/29/94, CD",
|
||||
{
|
||||
{ "rtzcd.red", 0, "946997d8b0aa6cb4e848bad02a1fc3d2", 276584 },
|
||||
{ "rtzcd.prj", 0, "3c8644f7ce77b74968637c035c3532d8", 48083511 },
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::KO_KOR,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO1(GAMEOPTION_INTRO_MUSIC_DIGITAL)
|
||||
},
|
||||
GID_RTZ,
|
||||
0,
|
||||
GF_CD_COMPRESSED,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// Return to Zork - English floppy version
|
||||
{
|
||||
"rtz",
|
||||
"Floppy",
|
||||
AD_ENTRY1("rtz.prj", "764d02f52ce1c219f2c0066677fba4ce"),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NOSPEECH)
|
||||
},
|
||||
GID_RTZ,
|
||||
0,
|
||||
GF_FLOPPY,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// Return to Zork - Demo
|
||||
{
|
||||
"rtz",
|
||||
"Demo",
|
||||
{
|
||||
{ "demo.dat", 0, "2a6a1354bd5346fad4aee08e5b56caaa", 34304 },
|
||||
{ "demo.prj", 0, "46891bd6e5180228fe4b3253d500997b", 1675348 },
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_DEMO,
|
||||
GUIO0()
|
||||
},
|
||||
GID_RTZ,
|
||||
0,
|
||||
GF_DEMO,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// Return to Zork - Standalone CD Demo v1.1
|
||||
{
|
||||
"rtz",
|
||||
"V1.1, 12/6/93, Demo CD",
|
||||
AD_ENTRY1s("rtzcd.red", "827cfb323eae37b385985a2359fae3e9", 133784),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_DEMO | ADGF_CD,
|
||||
GUIO1(GAMEOPTION_INTRO_MUSIC_DIGITAL)
|
||||
},
|
||||
GID_RTZ,
|
||||
0,
|
||||
GF_CD_COMPRESSED,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// Return to Zork - Demo from Zork Anthology CD
|
||||
// Bugreport #11202
|
||||
{
|
||||
"rtz",
|
||||
"V1.2, 9/8/94, Demo CD",
|
||||
AD_ENTRY1s("rtzcd.red", "946997d8b0aa6cb4e848bad02a1fc3d2", 130683),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_DEMO | ADGF_CD,
|
||||
GUIO1(GAMEOPTION_INTRO_MUSIC_DIGITAL)
|
||||
},
|
||||
GID_RTZ,
|
||||
0,
|
||||
GF_CD_COMPRESSED,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// Return to Zork - Mac Demo from Zork Anthology CD
|
||||
// Same disc as DOS version (on ISO-9660)
|
||||
// The only resource fork is in the executable
|
||||
{
|
||||
"rtz",
|
||||
"V1.2, 5/4/94, Demo CD",
|
||||
AD_ENTRY1s("Return To Zork", "0c1377afd4b6fc4ee900e1882ac13895", 1714064),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_DEMO | ADGF_CD | ADGF_MACRESFORK,
|
||||
GUIO1(GAMEOPTION_INTRO_MUSIC_DIGITAL)
|
||||
},
|
||||
GID_RTZ,
|
||||
0,
|
||||
GF_CD_COMPRESSED,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// Return to Zork - Japanese DOS
|
||||
// This is the RTZCD.DAT in the base directory of the FM-Towns CD
|
||||
{
|
||||
"rtz",
|
||||
"",
|
||||
AD_ENTRY1("rtzcd.dat", "c4fccf67ad247f09b94c3c808b138576"),
|
||||
Common::JA_JPN,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO0()
|
||||
},
|
||||
GID_RTZ,
|
||||
0,
|
||||
GF_CD,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// Return to Zork - Japanese FM-Towns
|
||||
// This is in the RTZFM folder of the FM-Towns CD
|
||||
{
|
||||
"rtz",
|
||||
"",
|
||||
AD_ENTRY1("rtzcd.dat", "e949a6a42d82daabfa7d4dc0a87a9843"),
|
||||
Common::JA_JPN,
|
||||
Common::kPlatformFMTowns,
|
||||
ADGF_CD,
|
||||
GUIO1(GUIO_NOASPECT)
|
||||
},
|
||||
GID_RTZ,
|
||||
0,
|
||||
GF_CD,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// Return to Zork - Japanese PC-98
|
||||
// This is in the RTZ9821 folder of the FM-Towns CD
|
||||
{
|
||||
"rtz",
|
||||
"",
|
||||
AD_ENTRY1("rtzcd.dat", "0c0117e98530c736a141c2aad6834dc5"),
|
||||
Common::JA_JPN,
|
||||
Common::kPlatformPC98,
|
||||
ADGF_CD,
|
||||
GUIO0()
|
||||
},
|
||||
GID_RTZ,
|
||||
0,
|
||||
GF_CD,
|
||||
3,
|
||||
},
|
||||
|
||||
// The Manhole: Masterpiece Edition is not a MADE engine and cannot be
|
||||
// supported by MADE. It is a HyperCard-like engine
|
||||
{
|
||||
// The Manhole: Masterpiece Edition (GOG/CD)
|
||||
{
|
||||
"manhole",
|
||||
_s("The game is using unsupported engine"),
|
||||
AD_ENTRY1("manhole.dat", "e8cec9bf21e4c50a7ebc193a4e0b48f5"),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_UNSUPPORTED,
|
||||
GUIO1(GUIO_NOSPEECH)
|
||||
},
|
||||
GID_MANHOLE,
|
||||
0,
|
||||
GF_CD,
|
||||
2,
|
||||
},
|
||||
|
||||
// Bugreport #5855
|
||||
{
|
||||
{
|
||||
"manhole",
|
||||
_s("The game is using unsupported engine"),
|
||||
AD_ENTRY1s("manhole.dat", "df77ad5232757d7149342fb6471de4ed", 99317),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_UNSUPPORTED,
|
||||
GUIO1(GUIO_NOSPEECH)
|
||||
},
|
||||
GID_MANHOLE,
|
||||
0,
|
||||
GF_CD,
|
||||
2,
|
||||
},
|
||||
|
||||
{
|
||||
// The Manhole: New and Enhanced
|
||||
{
|
||||
"manhole",
|
||||
"",
|
||||
AD_ENTRY1("manhole.dat", "cb21e31ed35c963208343bc995225b73"),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_CD,
|
||||
GUIO1(GUIO_NOSPEECH)
|
||||
},
|
||||
GID_MANHOLE,
|
||||
0,
|
||||
GF_CD,
|
||||
2,
|
||||
},
|
||||
|
||||
{
|
||||
// The Manhole (EGA, 5.25")
|
||||
{
|
||||
"manhole",
|
||||
"EGA",
|
||||
AD_ENTRY1("manhole.dat", "2b1658292599a861c4cd3cf6cdb3c581"),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NOSPEECH)
|
||||
},
|
||||
GID_MANHOLE,
|
||||
0,
|
||||
GF_FLOPPY,
|
||||
1,
|
||||
},
|
||||
|
||||
{
|
||||
// The Manhole
|
||||
{
|
||||
"manhole",
|
||||
"",
|
||||
AD_ENTRY1s("manhole.dat", "2f14b5d87a862aad25701514dc282475", 119667),
|
||||
Common::JA_JPN,
|
||||
Common::kPlatformFMTowns,
|
||||
ADGF_CD | ADGF_UNSTABLE,
|
||||
GUIO1(GUIO_NOSPEECH)
|
||||
},
|
||||
GID_MANHOLE,
|
||||
0,
|
||||
GF_CD,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// The Manhole DOS/V
|
||||
// Platform: IBM PS-55, Sega TeraDrive or DOS/V-compatibles
|
||||
// MADE v2.00a JAPAN PC - Copyright (c) 1990, MEDIAGENIC
|
||||
{
|
||||
"manhole",
|
||||
"DOS-V",
|
||||
AD_ENTRY1s("manhole.dat", "14522ee9139ca0823ac0cc15805e1fcc", 112303),
|
||||
Common::JA_JPN,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_UNSTABLE,
|
||||
GUIO1(GUIO_NOSPEECH)
|
||||
},
|
||||
GID_MANHOLE,
|
||||
0,
|
||||
GF_FLOPPY,
|
||||
3,
|
||||
},
|
||||
|
||||
{
|
||||
// Leather Goddesses of Phobos 2 (English)
|
||||
{
|
||||
"lgop2",
|
||||
"",
|
||||
AD_ENTRY1("lgop2.dat", "8137996db200ff67e8f172ff106f2e48"),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NOSPEECH)
|
||||
},
|
||||
GID_LGOP2,
|
||||
0,
|
||||
GF_FLOPPY,
|
||||
2,
|
||||
},
|
||||
|
||||
{
|
||||
// Leather Goddesses of Phobos 2 (German)
|
||||
// Supplied by windlepoons (bug tracker #4218)
|
||||
{
|
||||
"lgop2",
|
||||
"",
|
||||
AD_ENTRY1s("lgop2.dat", "a0ffea6a3b7e39bd861edd00c397641c", 299466),
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NOSPEECH)
|
||||
},
|
||||
GID_LGOP2,
|
||||
0,
|
||||
GF_FLOPPY,
|
||||
2,
|
||||
},
|
||||
|
||||
{
|
||||
// Leather Goddesses of Phobos 2 (French)
|
||||
// Supplied by goodoldgeorg (bug tracker #4219)
|
||||
{
|
||||
"lgop2",
|
||||
"",
|
||||
AD_ENTRY1s("lgop2.dat", "f9e974087af7cf4b7ec2d8dc45d01e0c", 295366),
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NOSPEECH)
|
||||
},
|
||||
GID_LGOP2,
|
||||
0,
|
||||
GF_FLOPPY,
|
||||
2,
|
||||
},
|
||||
|
||||
{
|
||||
// Leather Goddesses of Phobos 2 (Spanish)
|
||||
// Supplied by goodoldgeorg (bug tracker #4219)
|
||||
{
|
||||
"lgop2",
|
||||
"",
|
||||
AD_ENTRY1s("lgop2.dat", "96eb95b4d75b9a3da0b0d67e3b4a787d", 288984),
|
||||
Common::ES_ESP,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NOSPEECH)
|
||||
},
|
||||
GID_LGOP2,
|
||||
0,
|
||||
GF_FLOPPY,
|
||||
2,
|
||||
},
|
||||
|
||||
{
|
||||
// Rodney's Funscreen
|
||||
// MS-DOS, Win16 and Tandy VIS all share the same resource but a different player.
|
||||
{
|
||||
"rodney",
|
||||
"",
|
||||
AD_ENTRY1s("rodneys.dat", "a79887dbaa47689facd7c6f09258ba5a", 92990),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOSPEECH, GAMEOPTION_WINDOWS_CURSORS)
|
||||
},
|
||||
GID_RODNEY,
|
||||
0,
|
||||
GF_FLOPPY,
|
||||
2,
|
||||
},
|
||||
|
||||
{ AD_TABLE_END_MARKER, 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
/**
|
||||
* The fallback game descriptor used by the Made engine's fallbackDetector.
|
||||
* Contents of this struct are to be overwritten by the fallbackDetector.
|
||||
*/
|
||||
static MadeGameDescription g_fallbackDesc = {
|
||||
{
|
||||
"",
|
||||
"",
|
||||
AD_ENTRY1(0, 0), // This should always be AD_ENTRY1(0, 0) in the fallback descriptor
|
||||
Common::UNK_LANG,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO0()
|
||||
},
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
};
|
||||
|
||||
} // End of namespace Made
|
||||
|
||||
#endif
|
||||
302
engines/made/graphics.cpp
Normal file
302
engines/made/graphics.cpp
Normal file
@@ -0,0 +1,302 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "made/graphics.h"
|
||||
|
||||
#include "common/endian.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Made {
|
||||
|
||||
byte ValueReader::readPixel() {
|
||||
byte value;
|
||||
if (_nibbleMode) {
|
||||
if (_nibbleSwitch) {
|
||||
value = (_buffer[0] >> 4) & 0x0F;
|
||||
_buffer++;
|
||||
} else {
|
||||
value = _buffer[0] & 0x0F;
|
||||
}
|
||||
_nibbleSwitch = !_nibbleSwitch;
|
||||
} else {
|
||||
value = _buffer[0];
|
||||
_buffer++;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint16 ValueReader::readUint16() {
|
||||
uint16 value = READ_LE_UINT16(_buffer);
|
||||
_buffer += 2;
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32 ValueReader::readUint32() {
|
||||
uint32 value = READ_LE_UINT32(_buffer);
|
||||
_buffer += 4;
|
||||
return value;
|
||||
}
|
||||
|
||||
void ValueReader::resetNibbleSwitch() {
|
||||
_nibbleSwitch = false;
|
||||
}
|
||||
|
||||
void decompressImage(byte *source, Graphics::Surface &surface, uint16 cmdOffs, uint16 pixelOffs, uint16 maskOffs, uint16 lineSize, byte cmdFlags, byte pixelFlags, byte maskFlags, bool deltaFrame) {
|
||||
|
||||
const int offsets[] = {
|
||||
0, 1, 2, 3,
|
||||
320, 321, 322, 323,
|
||||
640, 641, 642, 643,
|
||||
960, 961, 962, 963
|
||||
};
|
||||
|
||||
uint16 pitch = surface.pitch;
|
||||
uint16 width = surface.w;
|
||||
uint16 height = surface.h;
|
||||
|
||||
byte *cmdBuffer = source + cmdOffs;
|
||||
ValueReader maskReader(source + maskOffs, (maskFlags & 2) != 0);
|
||||
ValueReader pixelReader(source + pixelOffs, (pixelFlags & 2) != 0);
|
||||
|
||||
if ((maskFlags != 0) && (maskFlags != 2) && (pixelFlags != 0) && (pixelFlags != 2) && (cmdFlags != 0))
|
||||
error("decompressImage() Unsupported flags: cmdFlags = %02X; maskFlags = %02X, pixelFlags = %02X", cmdFlags, maskFlags, pixelFlags);
|
||||
|
||||
byte *destPtr = (byte *)surface.getPixels();
|
||||
|
||||
byte lineBuf[640 * 4];
|
||||
byte bitBuf[40];
|
||||
|
||||
int bitBufLastOfs = (((lineSize + 1) >> 1) << 1) - 2;
|
||||
int bitBufLastCount = ((width + 3) >> 2) & 7;
|
||||
if (bitBufLastCount == 0)
|
||||
bitBufLastCount = 8;
|
||||
|
||||
while (height > 0) {
|
||||
|
||||
int drawDestOfs = 0;
|
||||
|
||||
memset(lineBuf, 0, sizeof(lineBuf));
|
||||
|
||||
memcpy(bitBuf, cmdBuffer, lineSize);
|
||||
cmdBuffer += lineSize;
|
||||
|
||||
for (uint16 bitBufOfs = 0; bitBufOfs < lineSize; bitBufOfs += 2) {
|
||||
|
||||
uint16 bits = READ_LE_UINT16(&bitBuf[bitBufOfs]);
|
||||
|
||||
int bitCount;
|
||||
if (bitBufOfs == bitBufLastOfs)
|
||||
bitCount = bitBufLastCount;
|
||||
else
|
||||
bitCount = 8;
|
||||
|
||||
for (int curCmd = 0; curCmd < bitCount; curCmd++) {
|
||||
int cmd = bits & 3;
|
||||
bits >>= 2;
|
||||
|
||||
byte pixels[4];
|
||||
uint32 mask;
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
case 0:
|
||||
pixels[0] = pixelReader.readPixel();
|
||||
for (int i = 0; i < 16; i++)
|
||||
lineBuf[drawDestOfs + offsets[i]] = pixels[0];
|
||||
break;
|
||||
|
||||
case 1:
|
||||
pixels[0] = pixelReader.readPixel();
|
||||
pixels[1] = pixelReader.readPixel();
|
||||
mask = maskReader.readUint16();
|
||||
for (int i = 0; i < 16; i++) {
|
||||
lineBuf[drawDestOfs + offsets[i]] = pixels[mask & 1];
|
||||
mask >>= 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
pixels[0] = pixelReader.readPixel();
|
||||
pixels[1] = pixelReader.readPixel();
|
||||
pixels[2] = pixelReader.readPixel();
|
||||
pixels[3] = pixelReader.readPixel();
|
||||
mask = maskReader.readUint32();
|
||||
for (int i = 0; i < 16; i++) {
|
||||
lineBuf[drawDestOfs + offsets[i]] = pixels[mask & 3];
|
||||
mask >>= 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (!deltaFrame) {
|
||||
// For EGA pictures: Pixels are read starting from a new byte
|
||||
maskReader.resetNibbleSwitch();
|
||||
// Yes, it reads from maskReader here
|
||||
for (int i = 0; i < 16; i++)
|
||||
lineBuf[drawDestOfs + offsets[i]] = maskReader.readPixel();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
drawDestOfs += 4;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (deltaFrame) {
|
||||
for (int y = 0; y < 4 && height > 0; y++, height--) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
if (lineBuf[x + y * 320] != 0)
|
||||
*destPtr = lineBuf[x + y * 320];
|
||||
destPtr++;
|
||||
}
|
||||
destPtr += pitch - width;
|
||||
}
|
||||
} else {
|
||||
for (int y = 0; y < 4 && height > 0; y++, height--) {
|
||||
memcpy(destPtr, &lineBuf[y * 320], width);
|
||||
destPtr += pitch;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void decompressMovieImage(byte *source, Graphics::Surface &surface, uint16 cmdOffs, uint16 pixelOffs, uint16 maskOffs, uint16 lineSize) {
|
||||
|
||||
uint16 width = surface.w;
|
||||
uint16 height = surface.h;
|
||||
uint16 bx = 0, by = 0, bw = ((width + 3) / 4) * 4;
|
||||
|
||||
byte *cmdBuffer = source + cmdOffs;
|
||||
byte *maskBuffer = source + maskOffs;
|
||||
byte *pixelBuffer = source + pixelOffs;
|
||||
|
||||
byte *destPtr = (byte *)surface.getPixels();
|
||||
|
||||
byte bitBuf[40];
|
||||
|
||||
int bitBufLastOfs = (((lineSize + 1) >> 1) << 1) - 2;
|
||||
int bitBufLastCount = ((width + 3) >> 2) & 7;
|
||||
if (bitBufLastCount == 0)
|
||||
bitBufLastCount = 8;
|
||||
|
||||
debug(1, "width = %d; bw = %d", width, bw);
|
||||
|
||||
while (height > 0) {
|
||||
|
||||
memcpy(bitBuf, cmdBuffer, lineSize);
|
||||
cmdBuffer += lineSize;
|
||||
|
||||
for (uint16 bitBufOfs = 0; bitBufOfs < lineSize; bitBufOfs += 2) {
|
||||
|
||||
uint16 bits = READ_LE_UINT16(&bitBuf[bitBufOfs]);
|
||||
|
||||
int bitCount;
|
||||
if (bitBufOfs == bitBufLastOfs)
|
||||
bitCount = bitBufLastCount;
|
||||
else
|
||||
bitCount = 8;
|
||||
|
||||
for (int curCmd = 0; curCmd < bitCount; curCmd++) {
|
||||
uint cmd = bits & 3;
|
||||
bits >>= 2;
|
||||
|
||||
byte pixels[4], block[16];
|
||||
uint32 mask;
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
case 0:
|
||||
pixels[0] = *pixelBuffer++;
|
||||
for (int i = 0; i < 16; i++)
|
||||
block[i] = pixels[0];
|
||||
break;
|
||||
|
||||
case 1:
|
||||
pixels[0] = *pixelBuffer++;
|
||||
pixels[1] = *pixelBuffer++;
|
||||
mask = READ_LE_UINT16(maskBuffer);
|
||||
maskBuffer += 2;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
block[i] = pixels[mask & 1];
|
||||
mask >>= 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
pixels[0] = *pixelBuffer++;
|
||||
pixels[1] = *pixelBuffer++;
|
||||
pixels[2] = *pixelBuffer++;
|
||||
pixels[3] = *pixelBuffer++;
|
||||
mask = READ_LE_UINT32(maskBuffer);
|
||||
maskBuffer += 4;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
block[i] = pixels[mask & 3];
|
||||
mask >>= 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmd != 3) {
|
||||
uint16 blockPos = 0;
|
||||
uint32 maxW = MIN(4, surface.w - bx);
|
||||
uint32 maxH = (MIN(4, surface.h - by) + by) * width;
|
||||
for (uint32 yc = by * width; yc < maxH; yc += width) {
|
||||
for (uint32 xc = 0; xc < maxW; xc++) {
|
||||
destPtr[(bx + xc) + yc] = block[xc + blockPos];
|
||||
}
|
||||
blockPos += 4;
|
||||
}
|
||||
}
|
||||
|
||||
bx += 4;
|
||||
if (bx >= bw) {
|
||||
bx = 0;
|
||||
by += 4;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
height -= 4;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // End of namespace Made
|
||||
51
engines/made/graphics.h
Normal file
51
engines/made/graphics.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MADE_GRAPHICS_H
|
||||
#define MADE_GRAPHICS_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace Made {
|
||||
|
||||
class ValueReader {
|
||||
public:
|
||||
ValueReader(byte *source, bool nibbleMode) : _buffer(source), _nibbleBuf(0), _nibbleMode(nibbleMode), _nibbleSwitch(false) {}
|
||||
byte readPixel();
|
||||
uint16 readUint16();
|
||||
uint32 readUint32();
|
||||
void resetNibbleSwitch();
|
||||
protected:
|
||||
byte _nibbleBuf;
|
||||
bool _nibbleMode, _nibbleSwitch;
|
||||
byte *_buffer;
|
||||
};
|
||||
|
||||
void decompressImage(byte *source, Graphics::Surface &surface, uint16 cmdOffs, uint16 pixelOffs, uint16 maskOffs, uint16 lineSize, byte cmdFlags, byte pixelFlags, byte maskFlags, bool deltaFrame = false);
|
||||
void decompressMovieImage(byte *source, Graphics::Surface &surface, uint16 cmdOffs, uint16 pixelOffs, uint16 maskOffs, uint16 lineSize);
|
||||
|
||||
} // End of namespace Made
|
||||
|
||||
#endif /* MADE_H */
|
||||
509
engines/made/made.cpp
Normal file
509
engines/made/made.cpp
Normal file
@@ -0,0 +1,509 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "made/made.h"
|
||||
#include "made/console.h"
|
||||
#include "made/pmvplayer.h"
|
||||
#include "made/resource.h"
|
||||
#include "made/screen.h"
|
||||
#include "made/database.h"
|
||||
#include "made/script.h"
|
||||
#include "made/music.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/events.h"
|
||||
#include "common/formats/winexe_ne.h"
|
||||
#include "common/system.h"
|
||||
#include "common/error.h"
|
||||
|
||||
#include "engines/util.h"
|
||||
|
||||
#include "graphics/wincursor.h"
|
||||
|
||||
#include "backends/audiocd/audiocd.h"
|
||||
|
||||
namespace Made {
|
||||
|
||||
MadeEngine::MadeEngine(OSystem *syst, const MadeGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) {
|
||||
|
||||
_eventNum = 0;
|
||||
_eventMouseX = _eventMouseY = 0;
|
||||
_eventKey = 0;
|
||||
|
||||
_useWinCursors = false;
|
||||
|
||||
_autoStopSound = false;
|
||||
_soundEnergyIndex = 0;
|
||||
_soundEnergyArray = nullptr;
|
||||
_musicBeatStart = 0;
|
||||
_cdTimeStart = 0;
|
||||
_introMusicDigital = true;
|
||||
if (ConfMan.hasKey("intro_music_digital"))
|
||||
_introMusicDigital = ConfMan.getBool("intro_music_digital");
|
||||
|
||||
_rnd = new Common::RandomSource("made");
|
||||
|
||||
setDebugger(new MadeConsole(this));
|
||||
|
||||
_system->getAudioCDManager()->open();
|
||||
|
||||
_pmvPlayer = new PmvPlayer(this, _mixer);
|
||||
_res = new ResourceReader();
|
||||
_screen = new Screen(this);
|
||||
|
||||
if (getGameID() == GID_LGOP2 || getGameID() == GID_MANHOLE || getGameID() == GID_RODNEY) {
|
||||
_dat = new GameDatabaseV2(this);
|
||||
} else if (getGameID() == GID_RTZ) {
|
||||
_dat = new GameDatabaseV3(this);
|
||||
} else {
|
||||
error("Unknown GameID");
|
||||
}
|
||||
|
||||
_script = new ScriptInterpreter(this);
|
||||
|
||||
_music = nullptr;
|
||||
|
||||
_soundRate = 0;
|
||||
|
||||
_saveLoadScreenOpen = false;
|
||||
_openingCreditsOpen = true;
|
||||
_tapeRecorderOpen = false;
|
||||
_previousRect = -1;
|
||||
_previousTextBox = -1;
|
||||
_voiceText = true;
|
||||
_forceVoiceText = false;
|
||||
_forceQueueText = false;
|
||||
|
||||
#ifdef USE_TTS
|
||||
_rtzSaveLoadIndex = ARRAYSIZE(_rtzSaveLoadButtonText);
|
||||
_rtzFirstSaveSlot = 0;
|
||||
_tapeRecorderIndex = 0;
|
||||
_playOMaticButtonIndex = ARRAYSIZE(_playOMaticButtonText);
|
||||
#endif
|
||||
|
||||
// Set default sound frequency
|
||||
switch (getGameID()) {
|
||||
case GID_RODNEY:
|
||||
_soundRate = 11025;
|
||||
break;
|
||||
case GID_MANHOLE:
|
||||
_soundRate = 11025;
|
||||
break;
|
||||
case GID_LGOP2:
|
||||
_soundRate = 8000;
|
||||
break;
|
||||
case GID_RTZ:
|
||||
// Return to Zork sets it itself via a script function
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MadeEngine::~MadeEngine() {
|
||||
_system->getAudioCDManager()->stop();
|
||||
|
||||
delete _rnd;
|
||||
delete _pmvPlayer;
|
||||
delete _res;
|
||||
delete _screen;
|
||||
delete _dat;
|
||||
delete _script;
|
||||
delete _music;
|
||||
}
|
||||
|
||||
void MadeEngine::syncSoundSettings() {
|
||||
Engine::syncSoundSettings();
|
||||
|
||||
if (_music)
|
||||
_music->syncSoundSettings();
|
||||
}
|
||||
|
||||
int16 MadeEngine::getTicks() {
|
||||
return g_system->getMillis() * 30 / 1000;
|
||||
}
|
||||
|
||||
int16 MadeEngine::getTimer(int16 timerNum) {
|
||||
if (timerNum > 0 && timerNum <= ARRAYSIZE(_timers) && _timers[timerNum - 1] != -1) {
|
||||
Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
|
||||
if (getGameID() == GID_LGOP2 && ttsMan && ttsMan->isSpeaking()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return (getTicks() - _timers[timerNum - 1]);
|
||||
} else
|
||||
return 32000;
|
||||
}
|
||||
|
||||
void MadeEngine::setTimer(int16 timerNum, int16 value) {
|
||||
if (timerNum > 0 && timerNum <= ARRAYSIZE(_timers))
|
||||
_timers[timerNum - 1] = value;
|
||||
}
|
||||
|
||||
void MadeEngine::resetTimer(int16 timerNum) {
|
||||
if (timerNum > 0 && timerNum <= ARRAYSIZE(_timers))
|
||||
_timers[timerNum - 1] = getTicks();
|
||||
}
|
||||
|
||||
int16 MadeEngine::allocTimer() {
|
||||
for (int i = 0; i < ARRAYSIZE(_timers); i++) {
|
||||
if (_timers[i] == -1) {
|
||||
_timers[i] = getTicks();
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MadeEngine::freeTimer(int16 timerNum) {
|
||||
if (timerNum > 0 && timerNum <= ARRAYSIZE(_timers))
|
||||
_timers[timerNum - 1] = -1;
|
||||
}
|
||||
|
||||
void MadeEngine::resetAllTimers() {
|
||||
for (int i = 0; i < ARRAYSIZE(_timers); i++)
|
||||
_timers[i] = -1;
|
||||
}
|
||||
|
||||
void MadeEngine::sayText(const Common::String &text, Common::TextToSpeechManager::Action action) const {
|
||||
if (text.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
|
||||
if (ttsMan != nullptr && ConfMan.getBool("tts_enabled")) {
|
||||
ttsMan->say(text, action, _ttsTextEncoding);
|
||||
}
|
||||
}
|
||||
|
||||
void MadeEngine::stopTextToSpeech() const {
|
||||
Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
|
||||
if (ttsMan != nullptr && ConfMan.getBool("tts_enabled") && ttsMan->isSpeaking()) {
|
||||
ttsMan->stop();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_TTS
|
||||
|
||||
void MadeEngine::checkHoveringSaveLoadScreen() {
|
||||
static const Common::Rect rtzSaveLoadScreenButtons[] = {
|
||||
Common::Rect(184, 174, 241, 189), // Cancel button
|
||||
Common::Rect(109, 174, 166, 189), // Save/load button
|
||||
Common::Rect(25, 20, 297, 158) // Text entry box
|
||||
};
|
||||
|
||||
static const uint8 kRtzSaveLoadButtonCount = ARRAYSIZE(rtzSaveLoadScreenButtons);
|
||||
static const uint8 kRtzSaveBoxHeight = 14;
|
||||
|
||||
enum RtzSaveLoadScreenIndex {
|
||||
kCancel = 0,
|
||||
kSaveOrLoad = 1,
|
||||
kTextBox = 2
|
||||
};
|
||||
|
||||
if (_saveLoadScreenOpen && getGameID() == GID_RTZ) {
|
||||
bool hoveringOverButton = false;
|
||||
for (uint8 i = 0; i < kRtzSaveLoadButtonCount; ++i) {
|
||||
if (rtzSaveLoadScreenButtons[i].contains(_eventMouseX, _eventMouseY)) {
|
||||
if (_previousRect != i) {
|
||||
if (i == kTextBox) {
|
||||
int index = MIN((_eventMouseY - 20) / kRtzSaveBoxHeight, 9);
|
||||
|
||||
if (index != _previousTextBox) {
|
||||
sayText(Common::String::format("%d", _rtzFirstSaveSlot + index));
|
||||
_previousTextBox = index;
|
||||
}
|
||||
} else {
|
||||
sayText(_rtzSaveLoadButtonText[i]);
|
||||
_previousRect = i;
|
||||
}
|
||||
}
|
||||
|
||||
hoveringOverButton = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hoveringOverButton) {
|
||||
_previousRect = -1;
|
||||
_previousTextBox = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MadeEngine::checkHoveringPlayOMatic(int16 spriteY) {
|
||||
static const Common::Rect lgop2PlayOMaticButtons[] = {
|
||||
Common::Rect(105, 102, 225, 122),
|
||||
Common::Rect(105, 127, 225, 147),
|
||||
Common::Rect(105, 152, 225, 172),
|
||||
Common::Rect(105, 177, 225, 197)
|
||||
};
|
||||
|
||||
static const uint8 kLgop2PlayOMaticButtonCount = ARRAYSIZE(lgop2PlayOMaticButtons);
|
||||
|
||||
if (_saveLoadScreenOpen && getGameID() == GID_LGOP2) {
|
||||
bool hoveringOverButton = false;
|
||||
for (uint8 i = 0; i < kLgop2PlayOMaticButtonCount; ++i) {
|
||||
if (lgop2PlayOMaticButtons[i].contains(_eventMouseX, _eventMouseY) || spriteY == lgop2PlayOMaticButtons[i].top) {
|
||||
if (_previousRect != i || spriteY != -1) {
|
||||
sayText(_playOMaticButtonText[i], Common::TextToSpeechManager::INTERRUPT);
|
||||
_previousRect = i;
|
||||
}
|
||||
|
||||
hoveringOverButton = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hoveringOverButton) {
|
||||
_previousRect = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Common::String MadeEngine::getSavegameFilename(int16 saveNum) {
|
||||
return Common::String::format("%s.%03d", getTargetName().c_str(), saveNum);
|
||||
}
|
||||
|
||||
void MadeEngine::handleEvents() {
|
||||
|
||||
Common::Event event;
|
||||
Common::EventManager *eventMan = _system->getEventManager();
|
||||
|
||||
// NOTE: Don't reset _eventNum to 0 here or no events will get through to the scripts.
|
||||
|
||||
while (eventMan->pollEvent(event)) {
|
||||
switch (event.type) {
|
||||
|
||||
case Common::EVENT_MOUSEMOVE:
|
||||
_eventMouseX = event.mouse.x;
|
||||
_eventMouseY = event.mouse.y;
|
||||
|
||||
#ifdef USE_TTS
|
||||
checkHoveringSaveLoadScreen();
|
||||
checkHoveringPlayOMatic();
|
||||
#endif
|
||||
|
||||
break;
|
||||
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
_eventNum = 2;
|
||||
|
||||
if (_openingCreditsOpen) {
|
||||
_openingCreditsOpen = false;
|
||||
stopTextToSpeech();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Common::EVENT_LBUTTONUP:
|
||||
_eventNum = 1;
|
||||
break;
|
||||
|
||||
case Common::EVENT_RBUTTONDOWN:
|
||||
_eventNum = 4;
|
||||
break;
|
||||
|
||||
case Common::EVENT_RBUTTONUP:
|
||||
_eventNum = 3;
|
||||
break;
|
||||
case Common::EVENT_KEYDOWN:
|
||||
// Handle any special keys here
|
||||
// Supported keys taken from https://web.archive.org/web/20141114142447/http://www.allgame.com/game.php?id=13542&tab=controls
|
||||
if (event.kbd.keycode == Common::KEYCODE_BACKSPACE) {
|
||||
_eventNum = 5;
|
||||
_eventKey = 9;
|
||||
} else {
|
||||
_eventNum = 5;
|
||||
_eventKey = event.kbd.ascii;
|
||||
}
|
||||
break;
|
||||
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
|
||||
switch (event.customType) {
|
||||
case kActionCursorUp:
|
||||
_eventMouseY = MAX<int16>(0, _eventMouseY - 1);
|
||||
g_system->warpMouse(_eventMouseX, _eventMouseY);
|
||||
break;
|
||||
case kActionCursorDown:
|
||||
_eventMouseY = MIN<int16>(199, _eventMouseY + 1);
|
||||
g_system->warpMouse(_eventMouseX, _eventMouseY);
|
||||
break;
|
||||
case kActionCursorLeft:
|
||||
_eventMouseX = MAX<int16>(0, _eventMouseX - 1);
|
||||
g_system->warpMouse(_eventMouseX, _eventMouseY);
|
||||
break;
|
||||
case kActionCursorRight:
|
||||
_eventMouseX = MIN<int16>(319, _eventMouseX + 1);
|
||||
g_system->warpMouse(_eventMouseX, _eventMouseY);
|
||||
break;
|
||||
case kActionMenu:
|
||||
_eventNum = 5;
|
||||
_eventKey = 21; //KEYCODE F1
|
||||
break;
|
||||
case kActionSaveGame:
|
||||
_eventNum = 5;
|
||||
_eventKey = 22; //KEYCODE F2
|
||||
break;
|
||||
case kActionLoadGame:
|
||||
_eventNum = 5;
|
||||
_eventKey = 23; //KEYCODE F3
|
||||
break;
|
||||
case kActionRepeatMessage:
|
||||
_eventNum = 5;
|
||||
_eventKey = 24; //KEYCODE F4
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
_system->getAudioCDManager()->update();
|
||||
|
||||
}
|
||||
|
||||
Common::Error MadeEngine::run() {
|
||||
if (getPlatform() == Common::kPlatformMacintosh)
|
||||
_music = nullptr; // TODO: Macintosh music player
|
||||
else
|
||||
_music = new DOSMusicPlayer(this, getGameID() == GID_RTZ);
|
||||
syncSoundSettings();
|
||||
|
||||
// Initialize backend
|
||||
initGraphics(320, 200);
|
||||
|
||||
resetAllTimers();
|
||||
|
||||
Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
|
||||
if (ttsMan != nullptr) {
|
||||
ttsMan->enable(ConfMan.getBool("tts_enabled"));
|
||||
|
||||
if (getLanguage() == Common::KO_KOR) { // Korean version doesn't translate any text
|
||||
ttsMan->setLanguage("en");
|
||||
} else {
|
||||
ttsMan->setLanguage(ConfMan.get("language"));
|
||||
}
|
||||
|
||||
if (getLanguage() == Common::JA_JPN) {
|
||||
_ttsTextEncoding = Common::CodePage::kWindows932;
|
||||
} else {
|
||||
_ttsTextEncoding = Common::CodePage::kDos850;
|
||||
}
|
||||
}
|
||||
|
||||
if (getGameID() == GID_RTZ) {
|
||||
if (getFeatures() & GF_DEMO) {
|
||||
_dat->open("demo.dat");
|
||||
_res->open("demo.prj");
|
||||
} else if (getFeatures() & GF_CD) {
|
||||
_dat->open("rtzcd.dat");
|
||||
_res->open("rtzcd.prj");
|
||||
} else if (getFeatures() & GF_CD_COMPRESSED) {
|
||||
_dat->openFromRed("rtzcd.red", "rtzcd.dat");
|
||||
_res->open("rtzcd.prj");
|
||||
} else if (getFeatures() & GF_FLOPPY) {
|
||||
_dat->open("rtz.dat");
|
||||
_res->open("rtz.prj");
|
||||
} else {
|
||||
error("Unknown RTZ game features");
|
||||
}
|
||||
} else if (getGameID() == GID_MANHOLE) {
|
||||
_dat->open("manhole.dat");
|
||||
|
||||
if (getVersion() == 2) {
|
||||
_res->open("manhole.prj");
|
||||
} else {
|
||||
_res->openResourceBlocks();
|
||||
}
|
||||
} else if (getGameID() == GID_LGOP2) {
|
||||
_dat->open("lgop2.dat");
|
||||
_res->open("lgop2.prj");
|
||||
} else if (getGameID() == GID_RODNEY) {
|
||||
_dat->open("rodneys.dat");
|
||||
_res->open("rodneys.prj");
|
||||
|
||||
if (ConfMan.hasKey("windows_cursors") && ConfMan.getBool("windows_cursors")) {
|
||||
// Try to open the EXE and get the hand cursor out
|
||||
Common::WinResources *exe = Common::WinResources::createFromEXE("rodneysw.exe"); // Win16 executable
|
||||
if (!exe)
|
||||
exe = Common::WinResources::createFromEXE("rodneysv.exe"); // Tandy VIS executable
|
||||
|
||||
if (exe) {
|
||||
Graphics::WinCursorGroup *_winCursor = Graphics::WinCursorGroup::createCursorGroup(exe, Common::WinResourceID("HANDCURSOR"));
|
||||
if (_winCursor) {
|
||||
if (_winCursor->cursors.size() > 0) {
|
||||
_screen->setMouseCursor(_winCursor->cursors[0].cursor);
|
||||
_useWinCursors = true;
|
||||
}
|
||||
delete _winCursor;
|
||||
}
|
||||
|
||||
delete exe;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error ("Unknown MADE game");
|
||||
}
|
||||
|
||||
if ((getFeatures() & GF_CD) || (getFeatures() & GF_CD_COMPRESSED)) {
|
||||
if (!existExtractedCDAudioFiles()
|
||||
&& !isDataAndCDAudioReadFromSameCD()) {
|
||||
warnMissingExtractedCDAudio();
|
||||
}
|
||||
}
|
||||
|
||||
_autoStopSound = false;
|
||||
_eventNum = _eventKey = _eventMouseX = _eventMouseY = 0;
|
||||
|
||||
#ifdef DUMP_SCRIPTS
|
||||
_script->dumpAllScripts();
|
||||
#else
|
||||
if (! _useWinCursors)
|
||||
_screen->setDefaultMouseCursor();
|
||||
|
||||
_script->runScript(_dat->getMainCodeObjectIndex());
|
||||
#endif
|
||||
|
||||
if (_music)
|
||||
_music->close();
|
||||
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
void MadeEngine::pauseEngineIntern(bool pause) {
|
||||
Engine::pauseEngineIntern(pause);
|
||||
|
||||
if (pause) {
|
||||
if (_music)
|
||||
_music->pause();
|
||||
} else {
|
||||
if (_music)
|
||||
_music->resume();
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Made
|
||||
163
engines/made/made.h
Normal file
163
engines/made/made.h
Normal file
@@ -0,0 +1,163 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MADE_MADE_H
|
||||
#define MADE_MADE_H
|
||||
|
||||
#include "made/sound.h"
|
||||
#include "made/detection.h"
|
||||
|
||||
#include "engines/engine.h"
|
||||
|
||||
#include "common/random.h"
|
||||
#include "common/text-to-speech.h"
|
||||
|
||||
/**
|
||||
* This is the namespace of the Made engine.
|
||||
*
|
||||
* Status of this engine: ???
|
||||
*
|
||||
* Games using this engine:
|
||||
* - Return to Zork
|
||||
* - Leather Goddesses of Phobos 2
|
||||
* - The Manhole
|
||||
* - Rodney's Funscreen
|
||||
*/
|
||||
namespace Made {
|
||||
|
||||
const uint32 kTimerResolution = 40;
|
||||
|
||||
class ResourceReader;
|
||||
class PmvPlayer;
|
||||
class Screen;
|
||||
class ScriptInterpreter;
|
||||
class GameDatabase;
|
||||
class MusicPlayer;
|
||||
class MadeConsole;
|
||||
|
||||
enum MADEAction {
|
||||
kActionNone,
|
||||
kActionCursorUp,
|
||||
kActionCursorDown,
|
||||
kActionCursorLeft,
|
||||
kActionCursorRight,
|
||||
kActionMenu,
|
||||
kActionSaveGame,
|
||||
kActionLoadGame,
|
||||
kActionRepeatMessage
|
||||
};
|
||||
|
||||
|
||||
class MadeEngine : public ::Engine {
|
||||
protected:
|
||||
|
||||
// Engine APIs
|
||||
Common::Error run() override;
|
||||
|
||||
public:
|
||||
MadeEngine(OSystem *syst, const MadeGameDescription *gameDesc);
|
||||
~MadeEngine() override;
|
||||
|
||||
bool hasFeature(EngineFeature f) const override;
|
||||
void syncSoundSettings() override;
|
||||
|
||||
Common::RandomSource *_rnd;
|
||||
const MadeGameDescription *_gameDescription;
|
||||
uint32 getGameID() const;
|
||||
uint32 getFeatures() const;
|
||||
uint16 getVersion() const;
|
||||
Common::Platform getPlatform() const;
|
||||
Common::Language getLanguage() const;
|
||||
|
||||
public:
|
||||
PmvPlayer *_pmvPlayer;
|
||||
ResourceReader *_res;
|
||||
Screen *_screen;
|
||||
GameDatabase *_dat;
|
||||
ScriptInterpreter *_script;
|
||||
MusicPlayer *_music;
|
||||
|
||||
bool _useWinCursors;
|
||||
|
||||
uint16 _eventNum;
|
||||
int _eventMouseX, _eventMouseY;
|
||||
uint16 _eventKey;
|
||||
|
||||
int _soundRate;
|
||||
bool _autoStopSound;
|
||||
uint _soundEnergyIndex;
|
||||
SoundEnergyArray *_soundEnergyArray;
|
||||
|
||||
uint32 _musicBeatStart;
|
||||
uint32 _cdTimeStart;
|
||||
bool _introMusicDigital;
|
||||
|
||||
Common::CodePage _ttsTextEncoding;
|
||||
int _previousRect;
|
||||
int _previousTextBox;
|
||||
bool _saveLoadScreenOpen;
|
||||
bool _openingCreditsOpen;
|
||||
bool _tapeRecorderOpen;
|
||||
|
||||
bool _voiceText;
|
||||
bool _forceVoiceText;
|
||||
bool _forceQueueText;
|
||||
|
||||
#ifdef USE_TTS
|
||||
Common::String _rtzSaveLoadButtonText[2];
|
||||
uint8 _rtzFirstSaveSlot;
|
||||
uint8 _rtzSaveLoadIndex;
|
||||
|
||||
Common::String _tapeRecorderText[4];
|
||||
uint8 _tapeRecorderIndex;
|
||||
|
||||
Common::String _playOMaticButtonText[4];
|
||||
uint8 _playOMaticButtonIndex;
|
||||
#endif
|
||||
|
||||
int32 _timers[50];
|
||||
int16 getTicks();
|
||||
int16 getTimer(int16 timerNum);
|
||||
void setTimer(int16 timerNum, int16 value);
|
||||
void resetTimer(int16 timerNum);
|
||||
int16 allocTimer();
|
||||
void freeTimer(int16 timerNum);
|
||||
void resetAllTimers();
|
||||
|
||||
void sayText(const Common::String &text, Common::TextToSpeechManager::Action action = Common::TextToSpeechManager::INTERRUPT) const;
|
||||
void stopTextToSpeech() const;
|
||||
#ifdef USE_TTS
|
||||
void checkHoveringSaveLoadScreen();
|
||||
void checkHoveringPlayOMatic(int16 spriteY = -1);
|
||||
#endif
|
||||
|
||||
const Common::String getTargetName() { return _targetName; }
|
||||
Common::String getSavegameFilename(int16 saveNum);
|
||||
|
||||
void handleEvents();
|
||||
|
||||
protected:
|
||||
void pauseEngineIntern(bool pause) override;
|
||||
};
|
||||
|
||||
} // End of namespace Made
|
||||
|
||||
#endif /* MADE_H */
|
||||
221
engines/made/metaengine.cpp
Normal file
221
engines/made/metaengine.cpp
Normal file
@@ -0,0 +1,221 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
#include "common/translation.h"
|
||||
#include "made/made.h"
|
||||
#include "made/detection.h"
|
||||
|
||||
#include "backends/keymapper/action.h"
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
#include "backends/keymapper/standard-actions.h"
|
||||
|
||||
|
||||
namespace Made {
|
||||
|
||||
static const ADExtraGuiOptionsMap optionsList[] = {
|
||||
{
|
||||
GAMEOPTION_INTRO_MUSIC_DIGITAL,
|
||||
{
|
||||
_s("Play a digital soundtrack during the opening movie"),
|
||||
_s("If selected, the game will use a digital soundtrack during the introduction. Otherwise, it will play MIDI music."),
|
||||
"intro_music_digital",
|
||||
true,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
|
||||
#ifdef USE_TTS
|
||||
{
|
||||
GAMEOPTION_TTS,
|
||||
{
|
||||
_s("Enable Text to Speech"),
|
||||
_s("Use TTS to read text in the game (if TTS is available)"),
|
||||
"tts_enabled",
|
||||
false,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
#endif
|
||||
{
|
||||
GAMEOPTION_WINDOWS_CURSORS,
|
||||
{
|
||||
_s("Use Windows cursors"),
|
||||
_s("If selected, the game will use Windows mouse cursors bundled in the original .exe file. Otherwise, it will use lower resolution cursors from the data files."),
|
||||
"windows_cursors",
|
||||
true,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
|
||||
AD_EXTRA_GUI_OPTIONS_TERMINATOR
|
||||
};
|
||||
|
||||
uint32 MadeEngine::getGameID() const {
|
||||
return _gameDescription->gameID;
|
||||
}
|
||||
|
||||
uint32 MadeEngine::getFeatures() const {
|
||||
return _gameDescription->features;
|
||||
}
|
||||
|
||||
Common::Platform MadeEngine::getPlatform() const {
|
||||
return _gameDescription->desc.platform;
|
||||
}
|
||||
|
||||
uint16 MadeEngine::getVersion() const {
|
||||
return _gameDescription->version;
|
||||
}
|
||||
|
||||
Common::Language MadeEngine::getLanguage() const {
|
||||
return _gameDescription->desc.language;
|
||||
}
|
||||
|
||||
} // End of namespace Made
|
||||
|
||||
class MadeMetaEngine : public AdvancedMetaEngine<Made::MadeGameDescription> {
|
||||
public:
|
||||
const char *getName() const override {
|
||||
return "made";
|
||||
}
|
||||
|
||||
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
|
||||
return Made::optionsList;
|
||||
}
|
||||
|
||||
bool hasFeature(MetaEngineFeature f) const override;
|
||||
Common::Error createInstance(OSystem *syst, Engine **engine, const Made::MadeGameDescription *desc) const override;
|
||||
|
||||
Common::KeymapArray initKeymaps(const char *target) const override;
|
||||
};
|
||||
|
||||
bool MadeMetaEngine::hasFeature(MetaEngineFeature f) const {
|
||||
return
|
||||
false;
|
||||
}
|
||||
|
||||
bool Made::MadeEngine::hasFeature(EngineFeature f) const {
|
||||
return
|
||||
(f == kSupportsReturnToLauncher);
|
||||
}
|
||||
|
||||
Common::Error MadeMetaEngine::createInstance(OSystem *syst, Engine **engine, const Made::MadeGameDescription *desc) const {
|
||||
*engine = new Made::MadeEngine(syst,desc);
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
Common::KeymapArray MadeMetaEngine::initKeymaps(const char *target) const {
|
||||
using namespace Common;
|
||||
using namespace Made;
|
||||
|
||||
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "made-default", _("Default keymappings"));
|
||||
Keymap *gameKeyMap = new Keymap(Keymap::kKeymapTypeGame, "game-shortcuts", _("Game keymappings"));
|
||||
|
||||
Action *act;
|
||||
|
||||
act = new Action(kStandardActionLeftClick, _("Left click"));
|
||||
act->setLeftClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_LEFT");
|
||||
act->addDefaultInputMapping("JOY_A");
|
||||
act->addDefaultInputMapping("KP_PLUS");
|
||||
engineKeyMap->addAction(act);
|
||||
|
||||
act = new Action(kStandardActionRightClick, _("Right click"));
|
||||
act->setRightClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_RIGHT");
|
||||
act->addDefaultInputMapping("JOY_B");
|
||||
act->addDefaultInputMapping("KP_MINUS");
|
||||
engineKeyMap->addAction(act);
|
||||
|
||||
act = new Action(kStandardActionSkip, _("Skip"));
|
||||
act->setKeyEvent(KeyState(KEYCODE_ESCAPE, ASCII_ESCAPE));
|
||||
act->addDefaultInputMapping("ESCAPE");
|
||||
act->addDefaultInputMapping("JOY_Y");
|
||||
act->allowKbdRepeats();
|
||||
engineKeyMap->addAction(act);
|
||||
|
||||
act = new Action("CRSORUP", _("Cursor up"));
|
||||
act->setCustomEngineActionEvent(kActionCursorUp);
|
||||
act->addDefaultInputMapping("UP");
|
||||
act->addDefaultInputMapping("KP8");
|
||||
act->allowKbdRepeats();
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("CRSORDOWN", _("Cursor down"));
|
||||
act->setCustomEngineActionEvent(kActionCursorDown);
|
||||
act->addDefaultInputMapping("DOWN");
|
||||
act->addDefaultInputMapping("KP2");
|
||||
act->allowKbdRepeats();
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("CRSORLEFT", _("Cursor left"));
|
||||
act->setCustomEngineActionEvent(kActionCursorLeft);
|
||||
act->addDefaultInputMapping("LEFT");
|
||||
act->addDefaultInputMapping("KP4");
|
||||
act->allowKbdRepeats();
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("CRSORRIGHT", _("Cursor right"));
|
||||
act->setCustomEngineActionEvent(kActionCursorRight);
|
||||
act->addDefaultInputMapping("RIGHT");
|
||||
act->addDefaultInputMapping("KP6");
|
||||
act->allowKbdRepeats();
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("MENU", _("Menu"));
|
||||
act->setCustomEngineActionEvent(kActionMenu);
|
||||
act->addDefaultInputMapping("F1");
|
||||
act->addDefaultInputMapping("JOY_GUIDE");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("SAVEGAME", _("Save game"));
|
||||
act->setCustomEngineActionEvent(kActionSaveGame);
|
||||
act->addDefaultInputMapping("F2");
|
||||
act->addDefaultInputMapping("JOY_LEFT_SHOULDER");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("LOADGAME", _("Load game"));
|
||||
act->setCustomEngineActionEvent(kActionLoadGame);
|
||||
act->addDefaultInputMapping("F3");
|
||||
act->addDefaultInputMapping("JOY_RIGHT_SHOULDER");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("RPTMSG", _("Repeat last message"));
|
||||
act->setCustomEngineActionEvent(kActionRepeatMessage);
|
||||
act->addDefaultInputMapping("F4");
|
||||
act->addDefaultInputMapping("JOY_X");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
KeymapArray keymaps(2);
|
||||
keymaps[0] = engineKeyMap;
|
||||
keymaps[1] = gameKeyMap;
|
||||
|
||||
return keymaps;
|
||||
}
|
||||
|
||||
#if PLUGIN_ENABLED_DYNAMIC(MADE)
|
||||
REGISTER_PLUGIN_DYNAMIC(MADE, PLUGIN_TYPE_ENGINE, MadeMetaEngine);
|
||||
#else
|
||||
REGISTER_PLUGIN_STATIC(MADE, PLUGIN_TYPE_ENGINE, MadeMetaEngine);
|
||||
#endif
|
||||
29
engines/made/module.mk
Normal file
29
engines/made/module.mk
Normal file
@@ -0,0 +1,29 @@
|
||||
MODULE := engines/made
|
||||
|
||||
MODULE_OBJS := \
|
||||
console.o \
|
||||
database.o \
|
||||
graphics.o \
|
||||
made.o \
|
||||
metaengine.o \
|
||||
music.o \
|
||||
pmvplayer.o \
|
||||
redreader.o \
|
||||
resource.o \
|
||||
screen.o \
|
||||
screenfx.o \
|
||||
script.o \
|
||||
scriptfuncs.o \
|
||||
sound.o
|
||||
|
||||
|
||||
# This module can be built as a plugin
|
||||
ifeq ($(ENABLE_MADE), DYNAMIC_PLUGIN)
|
||||
PLUGIN := 1
|
||||
endif
|
||||
|
||||
# Include common rules
|
||||
include $(srcdir)/rules.mk
|
||||
|
||||
# Detection objects
|
||||
DETECT_OBJS += $(MODULE)/detection.o
|
||||
218
engines/made/music.cpp
Normal file
218
engines/made/music.cpp
Normal file
@@ -0,0 +1,218 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// MIDI music class
|
||||
|
||||
#include "made/music.h"
|
||||
#include "made/redreader.h"
|
||||
#include "made/resource.h"
|
||||
|
||||
#include "audio/adlib_ms.h"
|
||||
#include "audio/midiparser.h"
|
||||
#include "audio/miles.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/file.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Made {
|
||||
|
||||
const uint8 DOSMusicPlayer::MT32_GOODBYE_MSG[] = { 0x52, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x54, 0x6F, 0x20, 0x5A, 0x6F, 0x72, 0x6B, 0x20, 0x53, 0x6F, 0x6F, 0x6E, 0x21 };
|
||||
|
||||
DOSMusicPlayer::DOSMusicPlayer(MadeEngine *vm, bool milesAudio) : _vm(vm), _parser(nullptr) {
|
||||
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MT32);
|
||||
_driverType = MidiDriver::getMusicType(dev);
|
||||
if (_driverType == MT_GM && ConfMan.getBool("native_mt32"))
|
||||
_driverType = MT_MT32;
|
||||
|
||||
Common::SeekableReadStream *adLibInstrumentStream = nullptr;
|
||||
switch (_driverType) {
|
||||
case MT_ADLIB:
|
||||
if (milesAudio) {
|
||||
if (Common::File::exists("rtzcd.red")) {
|
||||
// Installing Return to Zork produces both a SAMPLE.AD and
|
||||
// a SAMPLE.OPL file, but they are identical. The resource
|
||||
// file appears to only contain SAMPLE.AD.
|
||||
adLibInstrumentStream = RedReader::loadFromRed("rtzcd.red", "SAMPLE.AD");
|
||||
}
|
||||
_driver = Audio::MidiDriver_Miles_AdLib_create("SAMPLE.AD", "SAMPLE.OPL", adLibInstrumentStream);
|
||||
delete adLibInstrumentStream;
|
||||
} else {
|
||||
_driver = new MidiDriver_ADLIB_MADE(OPL::Config::kOpl2);
|
||||
}
|
||||
break;
|
||||
case MT_GM:
|
||||
case MT_MT32:
|
||||
if (milesAudio) {
|
||||
_driver = Audio::MidiDriver_Miles_MIDI_create(MT_MT32, "");
|
||||
} else {
|
||||
_driver = new MidiDriver_MT32GM(MT_MT32);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
_driver = new MidiDriver_NULL_Multisource();
|
||||
break;
|
||||
}
|
||||
|
||||
if (_driver) {
|
||||
_driver->property(MidiDriver::PROP_USER_VOLUME_SCALING, true);
|
||||
if (_driver->open() != 0)
|
||||
error("Failed to open MIDI driver.");
|
||||
|
||||
_driver->setTimerCallback(this, &timerCallback);
|
||||
}
|
||||
|
||||
syncSoundSettings();
|
||||
}
|
||||
|
||||
DOSMusicPlayer::~DOSMusicPlayer() {
|
||||
if (_parser) {
|
||||
_parser->stopPlaying();
|
||||
delete _parser;
|
||||
}
|
||||
if (_driver) {
|
||||
_driver->setTimerCallback(nullptr, nullptr);
|
||||
_driver->close();
|
||||
delete _driver;
|
||||
}
|
||||
}
|
||||
|
||||
void DOSMusicPlayer::close() {
|
||||
if (_parser)
|
||||
_parser->stopPlaying();
|
||||
|
||||
if (_vm->getGameID() == GID_RTZ && _vm->getPlatform() == Common::kPlatformDOS && _driver) {
|
||||
MidiDriver_MT32GM *mt32Driver = dynamic_cast<MidiDriver_MT32GM *>(_driver);
|
||||
if (mt32Driver)
|
||||
mt32Driver->sysExMT32(MT32_GOODBYE_MSG, MidiDriver_MT32GM::MT32_DISPLAY_NUM_CHARS,
|
||||
MidiDriver_MT32GM::MT32_DISPLAY_MEMORY_ADDRESS, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
bool DOSMusicPlayer::load(int16 musicNum) {
|
||||
GenericResource *xmidi = _vm->_res->getXmidi(musicNum);
|
||||
if (xmidi) {
|
||||
_vm->_res->freeResource(xmidi);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DOSMusicPlayer::play(int16 musicNum) {
|
||||
if (_vm->getGameID() == GID_RTZ) {
|
||||
if (musicNum > 0) {
|
||||
_musicRes = _vm->_res->getXmidi(musicNum);
|
||||
if (_musicRes)
|
||||
playXMIDI(_musicRes);
|
||||
}
|
||||
} else {
|
||||
// HACK: music number 2 in LGOP2 is file MT32SET.TON, which
|
||||
// is used to set the MT32 instruments. This is not loaded
|
||||
// correctly and the game freezes, and since we don't support
|
||||
// MT32 music yet, we ignore it here
|
||||
// FIXME: Remove this hack and handle this file properly
|
||||
if (_vm->getGameID() == GID_LGOP2 && musicNum == 2)
|
||||
return;
|
||||
if (musicNum > 0) {
|
||||
_musicRes = _vm->_res->getMidi(musicNum);
|
||||
if (_musicRes)
|
||||
playSMF(_musicRes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DOSMusicPlayer::playXMIDI(GenericResource *midiResource) {
|
||||
if (_parser) {
|
||||
_parser->unloadMusic();
|
||||
} else {
|
||||
_parser = MidiParser::createParser_XMIDI(nullptr, nullptr, 0);
|
||||
|
||||
_parser->setMidiDriver(_driver);
|
||||
_parser->setTimerRate(_driver->getBaseTempo());
|
||||
_parser->property(MidiParser::mpSendSustainOffOnNotesOff, 1);
|
||||
}
|
||||
|
||||
// Load XMID resource data
|
||||
|
||||
_parser->loadMusic(midiResource->getData(), midiResource->getSize());
|
||||
}
|
||||
|
||||
void DOSMusicPlayer::playSMF(GenericResource *midiResource) {
|
||||
if (_parser) {
|
||||
_parser->unloadMusic();
|
||||
} else {
|
||||
_parser = MidiParser::createParser_SMF(0);
|
||||
|
||||
_parser->setMidiDriver(_driver);
|
||||
_parser->setTimerRate(_driver->getBaseTempo());
|
||||
_parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
|
||||
}
|
||||
|
||||
// Load MIDI resource data
|
||||
|
||||
_parser->loadMusic(midiResource->getData(), midiResource->getSize());
|
||||
}
|
||||
|
||||
void DOSMusicPlayer::pause() {
|
||||
if (_parser)
|
||||
_parser->pausePlaying();
|
||||
}
|
||||
|
||||
void DOSMusicPlayer::resume() {
|
||||
if (_parser)
|
||||
_parser->resumePlaying();
|
||||
}
|
||||
|
||||
void DOSMusicPlayer::stop() {
|
||||
if (_parser)
|
||||
_parser->stopPlaying();
|
||||
|
||||
if (_musicRes) {
|
||||
_vm->_res->freeResource(_musicRes);
|
||||
_musicRes = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool DOSMusicPlayer::isPlaying() {
|
||||
return _parser ? _parser->isPlaying() : false;
|
||||
}
|
||||
|
||||
void DOSMusicPlayer::syncSoundSettings() {
|
||||
if (_driver)
|
||||
_driver->syncSoundSettings();
|
||||
}
|
||||
|
||||
void DOSMusicPlayer::onTimer() {
|
||||
if (_parser)
|
||||
_parser->onTimer();
|
||||
}
|
||||
|
||||
void DOSMusicPlayer::timerCallback(void *data) {
|
||||
((DOSMusicPlayer *)data)->onTimer();
|
||||
}
|
||||
|
||||
MidiDriver_ADLIB_MADE::MidiDriver_ADLIB_MADE(OPL::Config::OplType oplType) : MidiDriver_ADLIB_Multisource(oplType) {
|
||||
_modulationDepth = MODULATION_DEPTH_LOW;
|
||||
_vibratoDepth = VIBRATO_DEPTH_LOW;
|
||||
_defaultChannelVolume = 0x7F;
|
||||
}
|
||||
|
||||
} // End of namespace Made
|
||||
99
engines/made/music.h
Normal file
99
engines/made/music.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Music class
|
||||
|
||||
#ifndef MADE_MUSIC_H
|
||||
#define MADE_MUSIC_H
|
||||
|
||||
#include "made.h"
|
||||
|
||||
#include "audio/adlib_ms.h"
|
||||
#include "audio/mididrv.h"
|
||||
#include "audio/mididrv_ms.h"
|
||||
#include "audio/mt32gm.h"
|
||||
#include "audio/midiparser.h"
|
||||
|
||||
namespace Made {
|
||||
|
||||
class GenericResource;
|
||||
|
||||
class MusicPlayer {
|
||||
public:
|
||||
virtual ~MusicPlayer() {}
|
||||
|
||||
virtual void close() = 0;
|
||||
|
||||
virtual bool load(int16 musicNum) = 0;
|
||||
virtual void play(int16 musicNum) = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual void pause() = 0;
|
||||
virtual void resume() = 0;
|
||||
|
||||
virtual bool isPlaying() = 0;
|
||||
virtual void syncSoundSettings() = 0;
|
||||
};
|
||||
|
||||
class DOSMusicPlayer : public MusicPlayer {
|
||||
private:
|
||||
static const uint8 MT32_GOODBYE_MSG[MidiDriver_MT32GM::MT32_DISPLAY_NUM_CHARS];
|
||||
|
||||
public:
|
||||
DOSMusicPlayer(MadeEngine *vm, bool milesAudio);
|
||||
~DOSMusicPlayer();
|
||||
|
||||
void close() override;
|
||||
|
||||
bool load(int16 musicNum) override;
|
||||
void play(int16 musicNum) override;
|
||||
void stop() override;
|
||||
void pause() override;
|
||||
void resume() override;
|
||||
|
||||
bool isPlaying() override;
|
||||
void syncSoundSettings() override;
|
||||
|
||||
private:
|
||||
MadeEngine *_vm;
|
||||
MidiParser *_parser;
|
||||
MidiDriver_Multisource *_driver;
|
||||
|
||||
MusicType _driverType;
|
||||
|
||||
GenericResource *_musicRes;
|
||||
|
||||
void playXMIDI(GenericResource *midiResource);
|
||||
void playSMF(GenericResource *midiResource);
|
||||
|
||||
static void timerCallback(void *refCon);
|
||||
void onTimer();
|
||||
};
|
||||
|
||||
class MidiDriver_ADLIB_MADE : public MidiDriver_ADLIB_Multisource {
|
||||
public:
|
||||
MidiDriver_ADLIB_MADE(OPL::Config::OplType oplType);
|
||||
|
||||
// TODO Implement AdLib driver logic for Manhole / LGoP2
|
||||
};
|
||||
|
||||
} // End of namespace Made
|
||||
|
||||
#endif
|
||||
425
engines/made/pmvplayer.cpp
Normal file
425
engines/made/pmvplayer.cpp
Normal file
@@ -0,0 +1,425 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "made/pmvplayer.h"
|
||||
#include "made/made.h"
|
||||
#include "made/screen.h"
|
||||
#include "made/graphics.h"
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/system.h"
|
||||
#include "common/events.h"
|
||||
|
||||
#include "audio/decoders/raw.h"
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Made {
|
||||
|
||||
#ifdef USE_TTS
|
||||
|
||||
// English seems to be the only language that doesn't have voice clips for these lines
|
||||
static const char *introOpeningLines[] = {
|
||||
"You are standing by a white house",
|
||||
"Behind House\nYou are standing behind the white house. In one corner is a small window which is slightly ajar.",
|
||||
"Go southwest then go northwest",
|
||||
"West of House\nYou are standing in a field west of a white house with a boarded front door. There is a small mailbox here.",
|
||||
"Open mailbox"
|
||||
};
|
||||
|
||||
static const char *openingCreditsEnglish[] = {
|
||||
"Design: Doug Barnett",
|
||||
"Art Direction: Joe Asperin",
|
||||
"Technical Direction: William Volk",
|
||||
"Screenplay: Michele Em",
|
||||
"Music: Nathan Wang and Teri Mason",
|
||||
"Producer: Eddie Dombrower"
|
||||
};
|
||||
|
||||
static const char *openingCreditsGerman[] = {
|
||||
"Entwurf: Doug Barnett",
|
||||
"K\201nstlerischer Leitung: Joe Asperin",
|
||||
"Technische Leitung: William Volk",
|
||||
"Drehbuch: Michele Em",
|
||||
"Musik: Nathan Wang und Teri Mason",
|
||||
"Produzent: Eddie Dombrower"
|
||||
};
|
||||
|
||||
static const char *openingCreditsItalian[] = {
|
||||
"Disegno: Doug Barnett",
|
||||
"Direzione Artistica: Joe Asperin",
|
||||
"Direzione Tecnica: William Volk",
|
||||
"Sceneggiatura: Michele Em",
|
||||
"Musica: Nathan Wang e Teri Mason",
|
||||
"Produttore: Eddie Dombrower"
|
||||
};
|
||||
|
||||
static const char *openingCreditsFrench[] = {
|
||||
"Conception: Doug Barnett",
|
||||
"Direction Artistique: Joe Asperin",
|
||||
"Direction Technique: William Volk",
|
||||
"Sc\202nario: Michele Em",
|
||||
"Musique: Nathan Wang et Teri Mason",
|
||||
"Producteur: Eddie Dombrower"
|
||||
};
|
||||
|
||||
static const char *openingCreditsJapanese[] = {
|
||||
"\x83\x66\x83\x55\x83\x43\x83\x93\x81\x45\x83\x5f\x83\x4f\x81\x45\x83\x6f\x81\x5b\x83\x6c\x83\x62\x83\x67", // デザイン・ダグ・バーネット
|
||||
"\x83\x41\x81\x5b\x83\x67\x83\x66\x83\x42\x83\x8c\x83\x4e\x83\x56\x83\x87\x83\x93:"
|
||||
"\x83\x57\x83\x87\x81\x5b\x81\x45\x83\x41\x83\x58\x83\x79\x83\x8a\x83\x93", // アートディレクション:ジョー・アスペリン
|
||||
"\x83\x65\x83\x4e\x83\x6a\x83\x4a\x83\x8b\x83\x66\x83\x42\x83\x8c\x83\x4e\x83\x56\x83\x87\x83\x93:"
|
||||
"\x83\x45\x83\x42\x83\x8a\x83\x41\x83\x80\x81\x45\x83\x94\x83\x48\x83\x8b\x83\x4e", // テクニカルディレクション:ウィリアム・ヴォルク
|
||||
"\x8b\x72\x96\x7b:\x83\x7e\x83\x56\x83\x46\x83\x8b\x81\x45\x83\x47\x83\x80", // 脚本:ミシェル・エム
|
||||
"\x89\xb9\x8a\x79:\x83\x6c\x83\x43\x83\x54\x83\x93\x81\x45\x83\x8f\x83\x93\x83\x67\x83\x65\x83\x8a\x81\x5b"
|
||||
"\x81\x45\x83\x81\x83\x43\x83\x5c\x83\x93", // 音楽:ネイサン・ワンとテリー・メイソン
|
||||
"\x83\x76\x83\x8d\x83\x66\x83\x85\x81\x5b\x83\x54\x81\x5b:\x83\x47\x83\x66\x83\x42\x81\x45\x83\x68\x83\x93"
|
||||
"\x83\x75\x83\x8d\x83\x8f\x81\x5b" // プロデューサー: エディ・ドンブロワー
|
||||
};
|
||||
|
||||
enum IntroTextFrame {
|
||||
kStandingByHouse = 20,
|
||||
kBehindHouse = 53,
|
||||
kGoSouthwest = 170,
|
||||
kWestOfHouse = 312,
|
||||
kOpenMailbox = 430,
|
||||
kDesign = 716,
|
||||
kArtDirection = 773,
|
||||
kTechnicalDirection = 833,
|
||||
kScreenplay = 892,
|
||||
kMusic = 948,
|
||||
kProducer = 1004
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
PmvPlayer::PmvPlayer(MadeEngine *vm, Audio::Mixer *mixer) : _fd(nullptr), _vm(vm), _mixer(mixer) {
|
||||
_audioStream = nullptr;
|
||||
_surface = nullptr;
|
||||
_aborted = false;
|
||||
}
|
||||
|
||||
PmvPlayer::~PmvPlayer() {
|
||||
}
|
||||
|
||||
bool PmvPlayer::play(const char *filename) {
|
||||
_aborted = false;
|
||||
_surface = nullptr;
|
||||
|
||||
_fd = new Common::File();
|
||||
if (!_fd->open(filename)) {
|
||||
delete _fd;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 chunkType, chunkSize, prevChunkSize = 0;
|
||||
|
||||
readChunk(chunkType, chunkSize); // "MOVE"
|
||||
if (chunkType != MKTAG('M','O','V','E')) {
|
||||
warning("Unexpected PMV video header, expected 'MOVE'");
|
||||
delete _fd;
|
||||
return false;
|
||||
}
|
||||
|
||||
readChunk(chunkType, chunkSize); // "MHED"
|
||||
if (chunkType != MKTAG('M','H','E','D')) {
|
||||
warning("Unexpected PMV video header, expected 'MHED'");
|
||||
delete _fd;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint frameDelay = _fd->readUint16LE();
|
||||
_fd->skip(4); // always 0?
|
||||
uint frameCount = _fd->readUint16LE();
|
||||
_fd->skip(4); // always 0?
|
||||
|
||||
uint soundFreq = _fd->readUint16LE();
|
||||
// Note: There seem to be weird sound frequencies in PMV videos.
|
||||
// Not sure why, but leaving those original frequencies intact
|
||||
// results to sound being choppy. Therefore, we set them to more
|
||||
// "common" values here (11025 instead of 11127 and 22050 instead
|
||||
// of 22254)
|
||||
if (soundFreq == 11127)
|
||||
soundFreq = 11025;
|
||||
|
||||
if (soundFreq == 22254)
|
||||
soundFreq = 22050;
|
||||
|
||||
for (int i = 0; i < 22; i++) {
|
||||
int unk = _fd->readUint16LE();
|
||||
debug(2, "%i ", unk);
|
||||
}
|
||||
|
||||
_mixer->stopAll();
|
||||
|
||||
// Read palette
|
||||
_fd->read(_paletteRGB, 768);
|
||||
_vm->_screen->setRGBPalette(_paletteRGB);
|
||||
|
||||
uint32 frameNumber = 0;
|
||||
uint16 chunkCount = 0;
|
||||
uint32 soundSize = 0;
|
||||
uint32 soundChunkOfs = 0, palChunkOfs = 0;
|
||||
uint32 palSize = 0;
|
||||
byte *frameData = nullptr, *audioData, *soundData, *palData, *imageData;
|
||||
bool firstTime = true;
|
||||
|
||||
uint32 skipFrames = 0;
|
||||
|
||||
uint32 bytesRead;
|
||||
uint16 width, height, cmdOffs, pixelOffs, maskOffs, lineSize;
|
||||
|
||||
// TODO: Sound can still be a little choppy. A bug in the decoder or -
|
||||
// perhaps more likely - do we have to implement double buffering to
|
||||
// get it to work well?
|
||||
_audioStream = Audio::makeQueuingAudioStream(soundFreq, false);
|
||||
|
||||
SoundDecoderData *soundDecoderData = new SoundDecoderData();
|
||||
|
||||
// First cutscene after the opening credits finish
|
||||
if (strcmp(filename, "FWIZ01X1.PMV") == 0) {
|
||||
_vm->_openingCreditsOpen = false;
|
||||
}
|
||||
|
||||
while (!_vm->shouldQuit() && !_aborted && !_fd->eos() && frameNumber < frameCount) {
|
||||
|
||||
int32 frameTime = _vm->getTotalPlayTime();
|
||||
|
||||
readChunk(chunkType, chunkSize);
|
||||
if (chunkType != MKTAG('M','F','R','M')) {
|
||||
warning("Unknown chunk type");
|
||||
}
|
||||
|
||||
// Only reallocate the frame data buffer if its size has changed
|
||||
if (prevChunkSize != chunkSize || !frameData) {
|
||||
delete[] frameData;
|
||||
frameData = new byte[chunkSize];
|
||||
}
|
||||
|
||||
prevChunkSize = chunkSize;
|
||||
|
||||
bytesRead = _fd->read(frameData, chunkSize);
|
||||
|
||||
if (bytesRead < chunkSize || _fd->eos())
|
||||
break;
|
||||
|
||||
soundChunkOfs = READ_LE_UINT32(frameData + 8);
|
||||
palChunkOfs = READ_LE_UINT32(frameData + 16);
|
||||
|
||||
// Handle audio
|
||||
if (soundChunkOfs) {
|
||||
audioData = frameData + soundChunkOfs - 8;
|
||||
chunkSize = READ_LE_UINT16(audioData + 4);
|
||||
chunkCount = READ_LE_UINT16(audioData + 6);
|
||||
|
||||
debug(1, "chunkCount = %d; chunkSize = %d; total = %d\n", chunkCount, chunkSize, chunkCount * chunkSize);
|
||||
|
||||
soundSize = chunkCount * chunkSize;
|
||||
soundData = (byte *)malloc(soundSize);
|
||||
decompressSound(audioData + 8, soundData, chunkSize, chunkCount, nullptr, soundDecoderData);
|
||||
_audioStream->queueBuffer(soundData, soundSize, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED);
|
||||
}
|
||||
|
||||
// Handle palette
|
||||
if (palChunkOfs) {
|
||||
palData = frameData + palChunkOfs - 8;
|
||||
palSize = READ_LE_UINT32(palData + 4);
|
||||
decompressPalette(palData + 8, _paletteRGB, palSize);
|
||||
_vm->_screen->setRGBPalette(_paletteRGB);
|
||||
}
|
||||
|
||||
// Handle video
|
||||
imageData = frameData + READ_LE_UINT32(frameData + 12) - 8;
|
||||
|
||||
// frameNum @0
|
||||
width = READ_LE_UINT16(imageData + 8);
|
||||
height = READ_LE_UINT16(imageData + 10);
|
||||
cmdOffs = READ_LE_UINT16(imageData + 12);
|
||||
pixelOffs = READ_LE_UINT16(imageData + 16);
|
||||
maskOffs = READ_LE_UINT16(imageData + 20);
|
||||
lineSize = READ_LE_UINT16(imageData + 24);
|
||||
|
||||
debug(2, "width = %d; height = %d; cmdOffs = %04X; pixelOffs = %04X; maskOffs = %04X; lineSize = %d\n",
|
||||
width, height, cmdOffs, pixelOffs, maskOffs, lineSize);
|
||||
|
||||
if (!_surface) {
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
}
|
||||
|
||||
decompressMovieImage(imageData, *_surface, cmdOffs, pixelOffs, maskOffs, lineSize);
|
||||
|
||||
if (firstTime) {
|
||||
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_audioStreamHandle, _audioStream);
|
||||
skipFrames = 0;
|
||||
firstTime = false;
|
||||
}
|
||||
|
||||
handleEvents();
|
||||
updateScreen();
|
||||
|
||||
if (skipFrames == 0) {
|
||||
uint32 soundElapsedTime = _vm->_mixer->getElapsedTime(_audioStreamHandle).msecs();
|
||||
int32 waitTime = (frameNumber * frameDelay) -
|
||||
soundElapsedTime - (_vm->getTotalPlayTime() - frameTime);
|
||||
|
||||
if (waitTime < 0) {
|
||||
skipFrames = -waitTime / frameDelay;
|
||||
warning("Video A/V sync broken, skipping %d frame(s)", skipFrames + 1);
|
||||
} else if (waitTime > 0)
|
||||
g_system->delayMillis(waitTime);
|
||||
|
||||
} else
|
||||
skipFrames--;
|
||||
|
||||
#ifdef USE_TTS
|
||||
if (strcmp(filename, "fintro00.pmv") == 0 || strcmp(filename, "fintro01.pmv") == 0) {
|
||||
const char **texts;
|
||||
|
||||
switch (_vm->getLanguage()) {
|
||||
case Common::EN_ANY:
|
||||
if (frameNumber < kDesign) {
|
||||
texts = introOpeningLines;
|
||||
} else {
|
||||
texts = openingCreditsEnglish;
|
||||
}
|
||||
break;
|
||||
case Common::DE_DEU:
|
||||
texts = openingCreditsGerman;
|
||||
break;
|
||||
case Common::IT_ITA:
|
||||
texts = openingCreditsItalian;
|
||||
break;
|
||||
case Common::FR_FRA:
|
||||
texts = openingCreditsFrench;
|
||||
break;
|
||||
case Common::JA_JPN:
|
||||
texts = openingCreditsJapanese;
|
||||
break;
|
||||
case Common::KO_KOR:
|
||||
texts = openingCreditsEnglish;
|
||||
break;
|
||||
default:
|
||||
texts = openingCreditsEnglish;
|
||||
}
|
||||
|
||||
int index = -1;
|
||||
|
||||
switch (frameNumber) {
|
||||
case kStandingByHouse:
|
||||
case kDesign:
|
||||
index = 0;
|
||||
break;
|
||||
case kBehindHouse:
|
||||
case kArtDirection:
|
||||
index = 1;
|
||||
break;
|
||||
case kGoSouthwest:
|
||||
case kTechnicalDirection:
|
||||
index = 2;
|
||||
break;
|
||||
case kWestOfHouse:
|
||||
case kScreenplay:
|
||||
index = 3;
|
||||
break;
|
||||
case kOpenMailbox:
|
||||
case kMusic:
|
||||
index = 4;
|
||||
break;
|
||||
case kProducer:
|
||||
index = 5;
|
||||
}
|
||||
|
||||
if (index != -1 && (_vm->getLanguage() == Common::EN_ANY || frameNumber >= kDesign)) {
|
||||
_vm->sayText(texts[index], Common::TextToSpeechManager::QUEUE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
frameNumber++;
|
||||
|
||||
}
|
||||
|
||||
delete soundDecoderData;
|
||||
delete[] frameData;
|
||||
|
||||
_audioStream->finish();
|
||||
_mixer->stopHandle(_audioStreamHandle);
|
||||
|
||||
//delete _audioStream;
|
||||
delete _fd;
|
||||
|
||||
if(_surface)
|
||||
_surface->free();
|
||||
|
||||
delete _surface;
|
||||
|
||||
return !_aborted;
|
||||
|
||||
}
|
||||
|
||||
void PmvPlayer::readChunk(uint32 &chunkType, uint32 &chunkSize) {
|
||||
chunkType = _fd->readUint32BE();
|
||||
chunkSize = _fd->readUint32LE();
|
||||
|
||||
debug(2, "ofs = %08X; chunkType = %c%c%c%c; chunkSize = %d\n",
|
||||
(int)_fd->pos(),
|
||||
(chunkType >> 24) & 0xFF, (chunkType >> 16) & 0xFF, (chunkType >> 8) & 0xFF, chunkType & 0xFF,
|
||||
chunkSize);
|
||||
|
||||
}
|
||||
|
||||
void PmvPlayer::handleEvents() {
|
||||
Common::Event event;
|
||||
while (_vm->_system->getEventManager()->pollEvent(event)) {
|
||||
switch (event.type) {
|
||||
case Common::EVENT_KEYDOWN:
|
||||
if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
|
||||
_aborted = true;
|
||||
_vm->stopTextToSpeech();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PmvPlayer::updateScreen() {
|
||||
_vm->_system->copyRectToScreen(_surface->getPixels(), _surface->pitch,
|
||||
(320 - _surface->w) / 2, (200 - _surface->h) / 2, _surface->w, _surface->h);
|
||||
_vm->_system->updateScreen();
|
||||
}
|
||||
|
||||
void PmvPlayer::decompressPalette(byte *palData, byte *outPal, uint32 palDataSize) {
|
||||
byte *palDataEnd = palData + palDataSize;
|
||||
while (palData < palDataEnd) {
|
||||
byte count = *palData++;
|
||||
byte entry = *palData++;
|
||||
if (count == 255 && entry == 255)
|
||||
break;
|
||||
memcpy(&outPal[entry * 3], palData, (count + 1) * 3);
|
||||
palData += (count + 1) * 3;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
66
engines/made/pmvplayer.h
Normal file
66
engines/made/pmvplayer.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MADE_PMVPLAYER_H
|
||||
#define MADE_PMVPLAYER_H
|
||||
|
||||
#include "audio/mixer.h"
|
||||
|
||||
namespace Common {
|
||||
class File;
|
||||
}
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
class QueuingAudioStream;
|
||||
}
|
||||
|
||||
namespace Made {
|
||||
|
||||
class MadeEngine;
|
||||
|
||||
class PmvPlayer {
|
||||
public:
|
||||
PmvPlayer(MadeEngine *vm, Audio::Mixer *mixer);
|
||||
~PmvPlayer();
|
||||
// Returns true if the movie was played till the end
|
||||
bool play(const char *filename);
|
||||
protected:
|
||||
MadeEngine *_vm;
|
||||
Audio::Mixer *_mixer;
|
||||
Common::File *_fd;
|
||||
Audio::QueuingAudioStream *_audioStream;
|
||||
Audio::SoundHandle _audioStreamHandle;
|
||||
byte _paletteRGB[768];
|
||||
Graphics::Surface *_surface;
|
||||
bool _aborted;
|
||||
void readChunk(uint32 &chunkType, uint32 &chunkSize);
|
||||
void handleEvents();
|
||||
void updateScreen();
|
||||
void decompressPalette(byte *palData, byte *outPal, uint32 palDataSize);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
447
engines/made/redreader.cpp
Normal file
447
engines/made/redreader.cpp
Normal file
@@ -0,0 +1,447 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "made/redreader.h"
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/memstream.h"
|
||||
|
||||
namespace Made {
|
||||
|
||||
Common::SeekableReadStream *RedReader::load(const char *redFilename, const char *filename) {
|
||||
|
||||
Common::File fd;
|
||||
FileEntry fileEntry;
|
||||
|
||||
if (!fd.open(redFilename))
|
||||
error("RedReader::RedReader() Could not open %s", redFilename);
|
||||
|
||||
if (!seekFile(fd, fileEntry, filename))
|
||||
error("RedReader::RedReader() Could not find %s in archive %s", filename, redFilename);
|
||||
|
||||
byte *fileBuf = (byte *)malloc(fileEntry.origSize);
|
||||
|
||||
LzhDecompressor* lzhDec = new LzhDecompressor();
|
||||
lzhDec->decompress(fd, fileBuf, fileEntry.compSize, fileEntry.origSize);
|
||||
delete lzhDec;
|
||||
|
||||
return new Common::MemoryReadStream(fileBuf, fileEntry.origSize, DisposeAfterUse::YES);
|
||||
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *RedReader::loadFromRed(const char *redFilename, const char *filename) {
|
||||
RedReader* red = new RedReader();
|
||||
Common::SeekableReadStream *stream = red->load(redFilename, filename);
|
||||
delete red;
|
||||
return stream;
|
||||
}
|
||||
|
||||
bool RedReader::seekFile(Common::File &fd, FileEntry &fileEntry, const char *filename) {
|
||||
char arcFilename[13];
|
||||
while (true) {
|
||||
fd.skip(8); // skip unknown
|
||||
fileEntry.compSize = fd.readUint32LE();
|
||||
if (fd.eos()) break;
|
||||
|
||||
fileEntry.origSize = fd.readUint32LE();
|
||||
fd.skip(10); // skip unknown
|
||||
fd.read(arcFilename, 13);
|
||||
fd.skip(2); // skip unknown
|
||||
// Check if we have found the file
|
||||
if (!scumm_stricmp(arcFilename, filename))
|
||||
return true;
|
||||
// Skip compressed data
|
||||
fd.skip(fileEntry.compSize);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
LzhDecompressor::LzhDecompressor() {
|
||||
freq = nullptr;
|
||||
len_table = nullptr;
|
||||
sortptr = nullptr;
|
||||
_source = nullptr;
|
||||
|
||||
_compSize = 0;
|
||||
_blockPos = 0;
|
||||
_bitbuf = 0;
|
||||
_subbitbuf = 0;
|
||||
_bitcount = 0;
|
||||
_blocksize = 0;
|
||||
tree_n = 0;
|
||||
heapsize = 0;
|
||||
decode_i = 0;
|
||||
decode_j = 0;
|
||||
count_len_depth = 0;
|
||||
}
|
||||
|
||||
LzhDecompressor::~LzhDecompressor() {
|
||||
}
|
||||
|
||||
int LzhDecompressor::decompress(Common::SeekableReadStream &source, byte *dest, uint32 sourceLen, uint32 destLen) {
|
||||
|
||||
int bufsize;
|
||||
byte* buffer;
|
||||
|
||||
buffer = (byte *)calloc(DICSIZ, 1);
|
||||
|
||||
_source = &source;
|
||||
_compSize = sourceLen;
|
||||
|
||||
count_len_depth = 0;
|
||||
|
||||
_blockPos = 0;
|
||||
|
||||
decode_start();
|
||||
while (destLen > 0) {
|
||||
bufsize = ((destLen > DICSIZ) ? DICSIZ : destLen);
|
||||
decode(bufsize, buffer);
|
||||
memcpy(dest, buffer, bufsize);
|
||||
dest += bufsize;
|
||||
destLen -= bufsize;
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
byte LzhDecompressor::readByte() {
|
||||
if (_blockPos == 0xFFE) {
|
||||
_blockPos = 0;
|
||||
_source->skip(2); // skip unknown value
|
||||
}
|
||||
byte temp = _source->readByte();
|
||||
_blockPos++;
|
||||
return temp;
|
||||
}
|
||||
|
||||
void LzhDecompressor::fillbuf(int count) {
|
||||
_bitbuf <<= count;
|
||||
while (count > _bitcount) {
|
||||
_bitbuf |= _subbitbuf << (count -= _bitcount);
|
||||
if (_compSize != 0) {
|
||||
_compSize--;
|
||||
_subbitbuf = readByte();
|
||||
} else _subbitbuf = 0;
|
||||
_bitcount = 8;
|
||||
}
|
||||
_bitbuf |= _subbitbuf >> (_bitcount -= count);
|
||||
}
|
||||
|
||||
uint LzhDecompressor::getbits(int count) {
|
||||
uint x;
|
||||
x = _bitbuf >> (BITBUFSIZ - count);
|
||||
fillbuf(count);
|
||||
return x;
|
||||
}
|
||||
|
||||
void LzhDecompressor::init_getbits() {
|
||||
_bitbuf = 0;
|
||||
_subbitbuf = 0;
|
||||
_bitcount = 0;
|
||||
fillbuf(BITBUFSIZ);
|
||||
}
|
||||
|
||||
void LzhDecompressor::decode_start() {
|
||||
huf_decode_start();
|
||||
decode_j = 0;
|
||||
}
|
||||
|
||||
void LzhDecompressor::decode(uint count, byte buffer[]) {
|
||||
uint r, c;
|
||||
r = 0;
|
||||
while (--decode_j >= 0) {
|
||||
buffer[r] = buffer[decode_i];
|
||||
decode_i = (decode_i + 1) & (DICSIZ - 1);
|
||||
if (++r == count) return;
|
||||
}
|
||||
for ( ; ; ) {
|
||||
c = decode_c();
|
||||
if (c <= 255) {
|
||||
buffer[r] = c;
|
||||
if (++r == count) return;
|
||||
} else {
|
||||
decode_j = c - (255 + 1 - THRESHOLD);
|
||||
decode_i = (r - decode_p() - 1) & (DICSIZ - 1);
|
||||
while (--decode_j >= 0) {
|
||||
buffer[r] = buffer[decode_i];
|
||||
decode_i = (decode_i + 1) & (DICSIZ - 1);
|
||||
if (++r == count) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LzhDecompressor::read_pt_len(int nn, int nbit, int i_special) {
|
||||
int i, c, v;
|
||||
unsigned int mask;
|
||||
v = getbits(nbit);
|
||||
if (v == 0) {
|
||||
c = getbits(nbit);
|
||||
for (i = 0; i < nn; i++) _pt_len[i] = 0;
|
||||
for (i = 0; i < 256; i++) _pt_table[i] = c;
|
||||
} else {
|
||||
i = 0;
|
||||
while (i < v) {
|
||||
c = _bitbuf >> (BITBUFSIZ - 3);
|
||||
if (c == 7) {
|
||||
mask = 1U << (BITBUFSIZ - 1 - 3);
|
||||
while (mask & _bitbuf) { mask >>= 1; c++; }
|
||||
}
|
||||
fillbuf((c < 7) ? 3 : c - 3);
|
||||
_pt_len[i++] = c;
|
||||
if (i == i_special) {
|
||||
c = getbits(2);
|
||||
while (--c >= 0) _pt_len[i++] = 0;
|
||||
}
|
||||
}
|
||||
while (i < nn) _pt_len[i++] = 0;
|
||||
make_table(nn, _pt_len, 8, _pt_table);
|
||||
}
|
||||
}
|
||||
|
||||
void LzhDecompressor::read_c_len() {
|
||||
uint i, v;
|
||||
int c;
|
||||
unsigned int mask;
|
||||
v = getbits(CBIT);
|
||||
if (v == 0) {
|
||||
c = getbits(CBIT);
|
||||
for (i = 0; i < NC; i++) _c_len[i] = 0;
|
||||
for (i = 0; i < 4096; i++) _c_table[i] = c;
|
||||
} else {
|
||||
i = 0;
|
||||
while (i < v) {
|
||||
c = _pt_table[_bitbuf >> (BITBUFSIZ - 8)];
|
||||
if (c >= NT) {
|
||||
mask = 1U << (BITBUFSIZ - 1 - 8);
|
||||
do {
|
||||
if (_bitbuf & mask) c = _right[c];
|
||||
else c = _left [c];
|
||||
mask >>= 1;
|
||||
} while (c >= NT);
|
||||
}
|
||||
fillbuf(_pt_len[c]);
|
||||
if (c <= 2) {
|
||||
if (c == 0) c = 1;
|
||||
else if (c == 1) c = getbits(4) + 3;
|
||||
else c = getbits(CBIT) + 20;
|
||||
while (--c >= 0) _c_len[i++] = 0;
|
||||
} else _c_len[i++] = c - 2;
|
||||
}
|
||||
while (i < NC) _c_len[i++] = 0;
|
||||
make_table(NC, _c_len, 12, _c_table);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int LzhDecompressor::decode_c() {
|
||||
uint j, mask;
|
||||
if (_blocksize == 0) {
|
||||
_blocksize = getbits(16);
|
||||
read_pt_len(NT, TBIT, 3);
|
||||
read_c_len();
|
||||
read_pt_len(NP, PBIT, -1);
|
||||
}
|
||||
_blocksize--;
|
||||
j = _c_table[_bitbuf >> (BITBUFSIZ - 12)];
|
||||
if (j >= NC) {
|
||||
mask = 1U << (BITBUFSIZ - 1 - 12);
|
||||
do {
|
||||
if (_bitbuf & mask) j = _right[j];
|
||||
else j = _left [j];
|
||||
mask >>= 1;
|
||||
} while (j >= NC);
|
||||
}
|
||||
fillbuf(_c_len[j]);
|
||||
return j;
|
||||
}
|
||||
|
||||
unsigned int LzhDecompressor::decode_p() {
|
||||
unsigned int j, mask;
|
||||
j = _pt_table[_bitbuf >> (BITBUFSIZ - 8)];
|
||||
if (j >= NP) {
|
||||
mask = 1U << (BITBUFSIZ - 1 - 8);
|
||||
do {
|
||||
if (_bitbuf & mask) j = _right[j];
|
||||
else j = _left [j];
|
||||
mask >>= 1;
|
||||
} while (j >= NP);
|
||||
}
|
||||
fillbuf(_pt_len[j]);
|
||||
if (j != 0) j = (1U << (j - 1)) + getbits(j - 1);
|
||||
return j;
|
||||
}
|
||||
|
||||
void LzhDecompressor::huf_decode_start() {
|
||||
init_getbits();
|
||||
_blocksize = 0;
|
||||
}
|
||||
|
||||
void LzhDecompressor::make_table(uint nchar, byte bitlen[], uint tablebits, uint16 table[]) {
|
||||
uint16 count[17], weight[17], start[18], *p;
|
||||
uint i, k, len, ch, jutbits, avail, nextcode, mask;
|
||||
for (i = 1; i <= 16; i++) count[i] = 0;
|
||||
for (i = 0; i < nchar; i++) count[bitlen[i]]++;
|
||||
start[1] = 0;
|
||||
for (i = 1; i <= 16; i++)
|
||||
start[i + 1] = start[i] + (count[i] << (16 - i));
|
||||
if (start[17] != (uint16)(1U << 16))
|
||||
error("LzhDecompressor::make_table() Bad table");
|
||||
jutbits = 16 - tablebits;
|
||||
for (i = 1; i <= tablebits; i++) {
|
||||
start[i] >>= jutbits;
|
||||
weight[i] = 1U << (tablebits - i);
|
||||
}
|
||||
for (; i <= 16; i++) {
|
||||
weight[i] = 1U << (16 - i);
|
||||
}
|
||||
i = start[tablebits + 1] >> jutbits;
|
||||
if (i != (uint16)(1U << 16)) {
|
||||
k = 1U << tablebits;
|
||||
while (i != k) table[i++] = 0;
|
||||
}
|
||||
avail = nchar;
|
||||
mask = 1U << (15 - tablebits);
|
||||
for (ch = 0; ch < nchar; ch++) {
|
||||
if ((len = bitlen[ch]) == 0) continue;
|
||||
nextcode = start[len] + weight[len];
|
||||
if (len <= tablebits) {
|
||||
for (i = start[len]; i < nextcode; i++) table[i] = ch;
|
||||
} else {
|
||||
k = start[len];
|
||||
p = &table[k >> jutbits];
|
||||
i = len - tablebits;
|
||||
while (i != 0) {
|
||||
if (*p == 0) {
|
||||
_right[avail] = _left[avail] = 0;
|
||||
*p = avail++;
|
||||
}
|
||||
if (k & mask) p = &_right[*p];
|
||||
else p = &_left[*p];
|
||||
k <<= 1; i--;
|
||||
}
|
||||
*p = ch;
|
||||
}
|
||||
start[len] = nextcode;
|
||||
}
|
||||
}
|
||||
|
||||
/* call with i = root */
|
||||
void LzhDecompressor::count_len(int i) {
|
||||
if (i < tree_n)
|
||||
len_cnt[(count_len_depth < 16) ? count_len_depth : 16]++;
|
||||
else {
|
||||
count_len_depth++;
|
||||
count_len(_left [i]);
|
||||
count_len(_right[i]);
|
||||
count_len_depth--;
|
||||
}
|
||||
}
|
||||
|
||||
void LzhDecompressor::make_len(int root) {
|
||||
int i, k;
|
||||
uint cum;
|
||||
for (i = 0; i <= 16; i++) len_cnt[i] = 0;
|
||||
count_len(root);
|
||||
cum = 0;
|
||||
for (i = 16; i > 0; i--)
|
||||
cum += len_cnt[i] << (16 - i);
|
||||
while (cum != (1U << 16)) {
|
||||
len_cnt[16]--;
|
||||
for (i = 15; i > 0; i--) {
|
||||
if (len_cnt[i] != 0) {
|
||||
len_cnt[i]--;
|
||||
len_cnt[i+1] += 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
cum--;
|
||||
}
|
||||
for (i = 16; i > 0; i--) {
|
||||
k = len_cnt[i];
|
||||
while (--k >= 0) len_table[*sortptr++] = i;
|
||||
}
|
||||
}
|
||||
|
||||
void LzhDecompressor::downheap(int i) {
|
||||
int j, k;
|
||||
k = heap[i];
|
||||
while ((j = 2 * i) <= heapsize) {
|
||||
if (j < heapsize && freq[heap[j]] > freq[heap[j + 1]])
|
||||
j++;
|
||||
if (freq[k] <= freq[heap[j]]) break;
|
||||
heap[i] = heap[j]; i = j;
|
||||
}
|
||||
heap[i] = k;
|
||||
}
|
||||
|
||||
void LzhDecompressor::make_code(int n, byte len[], uint16 code[]) {
|
||||
int i;
|
||||
uint16 start[18];
|
||||
start[1] = 0;
|
||||
for (i = 1; i <= 16; i++)
|
||||
start[i + 1] = (start[i] + len_cnt[i]) << 1;
|
||||
for (i = 0; i < n; i++) code[i] = start[len[i]]++;
|
||||
}
|
||||
|
||||
/* make tree, calculate len[], return root */
|
||||
int LzhDecompressor::make_tree(int nparm, uint16 freqparm[], byte lenparm[], uint16 codeparm[]) {
|
||||
int i, j, k, avail;
|
||||
|
||||
tree_n = nparm;
|
||||
freq = freqparm;
|
||||
len_table = lenparm;
|
||||
avail = tree_n;
|
||||
heapsize = 0;
|
||||
heap[1] = 0;
|
||||
for (i = 0; i < tree_n; i++) {
|
||||
len_table[i] = 0;
|
||||
if (freq[i]) heap[++heapsize] = i;
|
||||
}
|
||||
if (heapsize < 2) {
|
||||
codeparm[heap[1]] = 0;
|
||||
return heap[1];
|
||||
}
|
||||
for (i = heapsize / 2; i >= 1; i--)
|
||||
downheap(i); /* make priority queue */
|
||||
sortptr = codeparm;
|
||||
do { /* while queue has at least two entries */
|
||||
i = heap[1]; /* take out least-freq entry */
|
||||
if (i < tree_n) *sortptr++ = i;
|
||||
heap[1] = heap[heapsize--];
|
||||
downheap(1);
|
||||
j = heap[1]; /* next least-freq entry */
|
||||
if (j < tree_n) *sortptr++ = j;
|
||||
k = avail++; /* generate new node */
|
||||
freq[k] = freq[i] + freq[j];
|
||||
heap[1] = k;
|
||||
downheap(1); /* put into queue */
|
||||
_left[k] = i;
|
||||
_right[k] = j;
|
||||
} while (heapsize > 1);
|
||||
sortptr = codeparm;
|
||||
make_len(k);
|
||||
make_code(nparm, lenparm, codeparm);
|
||||
return k; /* return root */
|
||||
}
|
||||
|
||||
}
|
||||
107
engines/made/redreader.h
Normal file
107
engines/made/redreader.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MADE_REDREADER_H
|
||||
#define MADE_REDREADER_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
class File;
|
||||
}
|
||||
|
||||
namespace Made {
|
||||
|
||||
class RedReader {
|
||||
public:
|
||||
Common::SeekableReadStream *load(const char *redFilename, const char *filename);
|
||||
static Common::SeekableReadStream *loadFromRed(const char *redFilename, const char *filename);
|
||||
private:
|
||||
struct FileEntry {
|
||||
uint32 compSize, origSize;
|
||||
};
|
||||
bool seekFile(Common::File &fd, FileEntry &fileEntry, const char *filename);
|
||||
};
|
||||
|
||||
const uint BITBUFSIZ = 16;
|
||||
const uint DICBIT = 13;
|
||||
const uint DICSIZ = 1 << DICBIT;
|
||||
const uint MATCHBIT = 8;
|
||||
const uint MAXMATCH = 256;
|
||||
const uint THRESHOLD = 3;
|
||||
const uint NC = 255 + MAXMATCH + 2 - THRESHOLD;
|
||||
const uint CBIT = 9;
|
||||
const uint CODE_BIT = 16;
|
||||
const uint NP = DICBIT + 1;
|
||||
const int NT = CODE_BIT + 3;
|
||||
const uint PBIT = 4;
|
||||
const uint TBIT = 5;
|
||||
const uint NPT = NT;
|
||||
|
||||
class LzhDecompressor {
|
||||
public:
|
||||
LzhDecompressor();
|
||||
~LzhDecompressor();
|
||||
int decompress(Common::SeekableReadStream &source, byte *dest, uint32 compSize, uint32 origSize);
|
||||
private:
|
||||
Common::SeekableReadStream *_source;
|
||||
uint32 _compSize, _blockPos;
|
||||
|
||||
uint16 _bitbuf;
|
||||
uint _subbitbuf;
|
||||
int _bitcount;
|
||||
uint16 _left[2 * NC - 1], _right[2 * NC - 1];
|
||||
byte _c_len[NC], _pt_len[NPT];
|
||||
uint _blocksize;
|
||||
uint16 _c_table[4096], _pt_table[256];
|
||||
int tree_n, heapsize;
|
||||
short heap[NC + 1];
|
||||
uint16 *freq, *sortptr, len_cnt[17];
|
||||
byte *len_table;
|
||||
|
||||
int decode_i, decode_j;
|
||||
int count_len_depth;
|
||||
|
||||
byte readByte();
|
||||
|
||||
void fillbuf(int count);
|
||||
uint getbits(int count);
|
||||
void init_getbits();
|
||||
void decode_start();
|
||||
void decode(uint count, byte text[]);
|
||||
void huf_decode_start();
|
||||
unsigned int decode_c();
|
||||
unsigned int decode_p();
|
||||
void read_pt_len(int nn, int nbit, int i_special);
|
||||
void read_c_len();
|
||||
void count_len(int i);
|
||||
void make_len(int root);
|
||||
void downheap(int i);
|
||||
void make_code(int n, byte len[], uint16 code[]);
|
||||
void make_table(uint nchar, byte bitlen[], uint tablebits, uint16 table[]);
|
||||
int make_tree(int nparm, uint16 freqparm[], byte lenparm[], uint16 codeparm[]);
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Made
|
||||
|
||||
#endif /* MADE_H */
|
||||
593
engines/made/resource.cpp
Normal file
593
engines/made/resource.cpp
Normal file
@@ -0,0 +1,593 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "made/resource.h"
|
||||
#include "made/graphics.h"
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#include "audio/decoders/raw.h"
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
namespace Made {
|
||||
|
||||
/* Resource */
|
||||
|
||||
Resource::~Resource() {
|
||||
}
|
||||
|
||||
/* PictureResource */
|
||||
|
||||
PictureResource::PictureResource() : _picture(nullptr), _picturePalette(nullptr) {
|
||||
_hasPalette = false;
|
||||
_paletteColorCount = 0;
|
||||
}
|
||||
|
||||
PictureResource::~PictureResource() {
|
||||
if (_picture) {
|
||||
_picture->free();
|
||||
delete _picture;
|
||||
_picture = nullptr;
|
||||
}
|
||||
|
||||
delete[] _picturePalette;
|
||||
_picturePalette = nullptr;
|
||||
}
|
||||
|
||||
void PictureResource::load(byte *source, int size) {
|
||||
if (READ_BE_UINT32(source) == MKTAG('F','l','e','x')) {
|
||||
loadChunked(source, size);
|
||||
} else {
|
||||
loadRaw(source, size);
|
||||
}
|
||||
}
|
||||
|
||||
void PictureResource::loadRaw(byte *source, int size) {
|
||||
// Loads a "raw" picture as used in RtZ, LGoP2, Manhole:N&E and Rodney's Funscreen
|
||||
|
||||
Common::MemoryReadStream *sourceS = new Common::MemoryReadStream(source, size);
|
||||
|
||||
_hasPalette = (sourceS->readByte() != 0);
|
||||
byte cmdFlags = sourceS->readByte();
|
||||
byte pixelFlags = sourceS->readByte();
|
||||
byte maskFlags = sourceS->readByte();
|
||||
uint16 cmdOffs = sourceS->readUint16LE();
|
||||
uint16 pixelOffs = sourceS->readUint16LE();
|
||||
uint16 maskOffs = sourceS->readUint16LE();
|
||||
uint16 lineSize = sourceS->readUint16LE();
|
||||
/*uint16 u = */sourceS->readUint16LE();
|
||||
uint16 width = sourceS->readUint16LE();
|
||||
uint16 height = sourceS->readUint16LE();
|
||||
|
||||
if (cmdFlags || pixelFlags || maskFlags) {
|
||||
warning("PictureResource::loadRaw() Graphic has flags set (%d, %d, %d)", cmdFlags, pixelFlags, maskFlags);
|
||||
}
|
||||
|
||||
_paletteColorCount = (cmdOffs - 18) / 3; // 18 = sizeof header
|
||||
|
||||
debug(2, "width = %d; height = %d\n", width, height);
|
||||
|
||||
if (_hasPalette) {
|
||||
_picturePalette = new byte[_paletteColorCount * 3];
|
||||
sourceS->read(_picturePalette, _paletteColorCount * 3);
|
||||
}
|
||||
|
||||
_picture = new Graphics::Surface();
|
||||
_picture->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
|
||||
decompressImage(source, *_picture, cmdOffs, pixelOffs, maskOffs, lineSize, cmdFlags, pixelFlags, maskFlags);
|
||||
|
||||
delete sourceS;
|
||||
|
||||
}
|
||||
|
||||
void PictureResource::loadChunked(byte *source, int size) {
|
||||
// Loads a "chunked" picture as used in Manhole EGA
|
||||
|
||||
Common::MemoryReadStream *sourceS = new Common::MemoryReadStream(source, size);
|
||||
|
||||
byte cmdFlags = 0, pixelFlags = 0, maskFlags = 0;
|
||||
uint16 cmdOffs = 0, pixelOffs = 0, maskOffs = 0;
|
||||
uint16 lineSize = 0, width = 0, height = 0;
|
||||
|
||||
sourceS->skip(36); // skip the "Flex" header
|
||||
|
||||
_hasPalette = false;
|
||||
|
||||
while (!sourceS->eos()) {
|
||||
|
||||
uint32 chunkType = sourceS->readUint32BE();
|
||||
uint32 chunkSize = sourceS->readUint32BE();
|
||||
|
||||
if (sourceS->eos())
|
||||
break;
|
||||
|
||||
debug(0, "chunkType = %08X; chunkSize = %d", chunkType, chunkSize);
|
||||
|
||||
if (chunkType == MKTAG('R','e','c','t')) {
|
||||
debug(0, "Rect");
|
||||
sourceS->skip(4);
|
||||
height = sourceS->readUint16BE();
|
||||
width = sourceS->readUint16BE();
|
||||
debug(0, "width = %d; height = %d", width, height);
|
||||
} else if (chunkType == MKTAG('f','M','a','p')) {
|
||||
debug(0, "fMap");
|
||||
lineSize = sourceS->readUint16BE();
|
||||
sourceS->skip(11);
|
||||
cmdFlags = sourceS->readByte();
|
||||
cmdOffs = sourceS->pos();
|
||||
sourceS->skip(chunkSize - 14);
|
||||
debug(0, "lineSize = %d; cmdFlags = %d; cmdOffs = %04X", lineSize, cmdFlags, cmdOffs);
|
||||
} else if (chunkType == MKTAG('f','L','C','o')) {
|
||||
debug(0, "fLCo");
|
||||
sourceS->skip(9);
|
||||
pixelFlags = sourceS->readByte();
|
||||
pixelOffs = sourceS->pos();
|
||||
sourceS->skip(chunkSize - 10);
|
||||
debug(0, "pixelFlags = %d; pixelOffs = %04X", pixelFlags, pixelOffs);
|
||||
} else if (chunkType == MKTAG('f','P','i','x')) {
|
||||
debug(0, "fPix");
|
||||
sourceS->skip(9);
|
||||
maskFlags = sourceS->readByte();
|
||||
maskOffs = sourceS->pos();
|
||||
sourceS->skip(chunkSize - 10);
|
||||
debug(0, "maskFlags = %d; maskOffs = %04X", maskFlags, maskOffs);
|
||||
} else if (chunkType == MKTAG('f','G','C','o')) {
|
||||
debug(0, "fGCo");
|
||||
_hasPalette = true;
|
||||
_paletteColorCount = chunkSize / 3;
|
||||
_picturePalette = new byte[_paletteColorCount * 3];
|
||||
sourceS->read(_picturePalette, _paletteColorCount * 3);
|
||||
} else {
|
||||
error("PictureResource::loadChunked() Invalid chunk %08X at %08X", chunkType, (int)sourceS->pos());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!cmdOffs || !pixelOffs /*|| !maskOffs*/ || !lineSize || !width || !height) {
|
||||
error("PictureResource::loadChunked() Error parsing the picture data, one or more chunks/parameters are missing");
|
||||
}
|
||||
|
||||
_picture = new Graphics::Surface();
|
||||
_picture->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
|
||||
decompressImage(source, *_picture, cmdOffs, pixelOffs, maskOffs, lineSize, cmdFlags, pixelFlags, maskFlags);
|
||||
|
||||
delete sourceS;
|
||||
|
||||
}
|
||||
|
||||
/* AnimationResource */
|
||||
|
||||
AnimationResource::AnimationResource() {
|
||||
_flags = 0;
|
||||
_width = 0;
|
||||
_height = 0;
|
||||
}
|
||||
|
||||
AnimationResource::~AnimationResource() {
|
||||
for (uint i = 0; i < _frames.size(); i++) {
|
||||
_frames[i]->free();
|
||||
delete _frames[i];
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationResource::load(byte *source, int size) {
|
||||
Common::MemoryReadStream *sourceS = new Common::MemoryReadStream(source, size);
|
||||
|
||||
sourceS->readUint32LE();
|
||||
sourceS->readUint32LE();
|
||||
sourceS->readUint16LE();
|
||||
|
||||
_flags = sourceS->readUint16LE();
|
||||
_width = sourceS->readUint16LE();
|
||||
_height = sourceS->readUint16LE();
|
||||
sourceS->readUint32LE();
|
||||
uint16 frameCount = sourceS->readUint16LE();
|
||||
sourceS->readUint16LE();
|
||||
sourceS->readUint16LE();
|
||||
|
||||
for (uint16 i = 0; i < frameCount; i++) {
|
||||
|
||||
sourceS->seek(26 + i * 4);
|
||||
|
||||
uint32 frameOffs = sourceS->readUint32LE();
|
||||
|
||||
sourceS->seek(frameOffs);
|
||||
sourceS->readUint32LE();
|
||||
sourceS->readUint32LE();
|
||||
|
||||
uint16 frameWidth = sourceS->readUint16LE();
|
||||
uint16 frameHeight = sourceS->readUint16LE();
|
||||
uint16 cmdOffs = sourceS->readUint16LE();
|
||||
sourceS->readUint16LE();
|
||||
uint16 pixelOffs = sourceS->readUint16LE();
|
||||
sourceS->readUint16LE();
|
||||
uint16 maskOffs = sourceS->readUint16LE();
|
||||
sourceS->readUint16LE();
|
||||
uint16 lineSize = sourceS->readUint16LE();
|
||||
|
||||
Graphics::Surface *frame = new Graphics::Surface();
|
||||
frame->create(frameWidth, frameHeight, Graphics::PixelFormat::createFormatCLUT8());
|
||||
|
||||
decompressImage(source + frameOffs, *frame, cmdOffs, pixelOffs, maskOffs, lineSize, 0, 0, 0, _flags & 1);
|
||||
|
||||
_frames.push_back(frame);
|
||||
|
||||
}
|
||||
|
||||
delete sourceS;
|
||||
}
|
||||
|
||||
/* SoundResource */
|
||||
|
||||
SoundResource::SoundResource() : _soundSize(0), _soundData(nullptr) {
|
||||
_soundEnergyArray = nullptr;
|
||||
}
|
||||
|
||||
SoundResource::~SoundResource() {
|
||||
delete[] _soundData;
|
||||
delete _soundEnergyArray;
|
||||
}
|
||||
|
||||
void SoundResource::load(byte *source, int size) {
|
||||
uint16 chunkCount = READ_LE_UINT16(source + 8);
|
||||
uint16 chunkSize = READ_LE_UINT16(source + 12);
|
||||
|
||||
_soundSize = chunkCount * chunkSize;
|
||||
_soundData = new byte[_soundSize];
|
||||
|
||||
_soundEnergyArray = new SoundEnergyArray;
|
||||
|
||||
decompressSound(source + 14, _soundData, chunkSize, chunkCount, _soundEnergyArray);
|
||||
}
|
||||
|
||||
Audio::AudioStream *SoundResource::getAudioStream(int soundRate, bool loop) {
|
||||
Audio::RewindableAudioStream *stream =
|
||||
Audio::makeRawStream(_soundData, _soundSize, soundRate, Audio::FLAG_UNSIGNED, DisposeAfterUse::NO);
|
||||
|
||||
if (loop)
|
||||
return Audio::makeLoopingAudioStream(stream, 0);
|
||||
else
|
||||
return stream;
|
||||
}
|
||||
|
||||
void SoundResourceV1::load(byte *source, int size) {
|
||||
_soundSize = size * 4;
|
||||
_soundData = new byte[_soundSize];
|
||||
ManholeEgaSoundDecompressor dec;
|
||||
dec.decompress(source, _soundData, size);
|
||||
}
|
||||
|
||||
/* MenuResource */
|
||||
|
||||
MenuResource::MenuResource() {
|
||||
}
|
||||
|
||||
MenuResource::~MenuResource() {
|
||||
}
|
||||
|
||||
void MenuResource::load(byte *source, int size) {
|
||||
_strings.clear();
|
||||
Common::MemoryReadStream *sourceS = new Common::MemoryReadStream(source, size);
|
||||
sourceS->skip(4); // skip "MENU"
|
||||
uint16 count = sourceS->readUint16LE();
|
||||
for (uint16 i = 0; i < count; i++) {
|
||||
uint16 offs = sourceS->readUint16LE();
|
||||
const char *string = (const char*)(source + offs);
|
||||
_strings.push_back(string);
|
||||
debug(2, "%02d: %s\n", i, string);
|
||||
}
|
||||
delete sourceS;
|
||||
}
|
||||
|
||||
const char *MenuResource::getString(uint index) const {
|
||||
if (index < _strings.size())
|
||||
return _strings[index].c_str();
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* FontResource */
|
||||
|
||||
FontResource::FontResource() : _data(nullptr), _size(0) {
|
||||
}
|
||||
|
||||
FontResource::~FontResource() {
|
||||
delete[] _data;
|
||||
}
|
||||
|
||||
void FontResource::load(byte *source, int size) {
|
||||
_data = new byte[size];
|
||||
_size = size;
|
||||
memcpy(_data, source, size);
|
||||
}
|
||||
|
||||
int FontResource::getHeight() const {
|
||||
return _data[0];
|
||||
}
|
||||
|
||||
int FontResource::getCharWidth(uint c) const {
|
||||
byte *charData = getCharData(c);
|
||||
if (charData)
|
||||
return charData[0];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
byte *FontResource::getChar(uint c) const {
|
||||
byte *charData = getCharData(c);
|
||||
if (charData)
|
||||
return charData + 1;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int FontResource::getTextWidth(const char *text) {
|
||||
int width = 0;
|
||||
if (text) {
|
||||
int len = strlen(text);
|
||||
for (int pos = 0; pos < len; pos++)
|
||||
width += getCharWidth(text[pos]);
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
byte *FontResource::getCharData(uint c) const {
|
||||
if (c < 28 || c > 255)
|
||||
return nullptr;
|
||||
return _data + 1 + (c - 28) * (getHeight() + 1);
|
||||
}
|
||||
|
||||
/* GenericResource */
|
||||
|
||||
GenericResource::GenericResource() : _data(nullptr), _size(0) {
|
||||
}
|
||||
|
||||
GenericResource::~GenericResource() {
|
||||
delete[] _data;
|
||||
}
|
||||
|
||||
void GenericResource::load(byte *source, int size) {
|
||||
_data = new byte[size];
|
||||
_size = size;
|
||||
memcpy(_data, source, size);
|
||||
}
|
||||
|
||||
/* ResourceReader */
|
||||
|
||||
ResourceReader::ResourceReader() {
|
||||
_isV1 = false;
|
||||
_cacheDataSize = 0;
|
||||
|
||||
_fd = _fdMusic = _fdPics = _fdSounds = nullptr;
|
||||
_cacheCount = 0;
|
||||
}
|
||||
|
||||
ResourceReader::~ResourceReader() {
|
||||
if (!_isV1) {
|
||||
delete _fd;
|
||||
} else {
|
||||
delete _fdPics;
|
||||
delete _fdSounds;
|
||||
delete _fdMusic;
|
||||
}
|
||||
}
|
||||
|
||||
// V2
|
||||
void ResourceReader::open(const char *filename) {
|
||||
_fd = new Common::File();
|
||||
if (!_fd->open(filename))
|
||||
error("ResourceReader::open() Could not open '%s'", filename);
|
||||
|
||||
_fd->skip(0x18); // skip header for now
|
||||
|
||||
uint16 indexCount = _fd->readUint16LE();
|
||||
|
||||
for (uint16 i = 0; i < indexCount; i++) {
|
||||
|
||||
uint32 resType = _fd->readUint32BE();
|
||||
uint32 indexOffs = _fd->readUint32LE();
|
||||
_fd->readUint32LE();
|
||||
_fd->readUint32LE();
|
||||
_fd->readUint32LE();
|
||||
_fd->readUint16LE();
|
||||
_fd->readUint16LE();
|
||||
|
||||
// We don't need ARCH, FREE and OMNI resources
|
||||
if (resType == kResARCH || resType == kResFREE || resType == kResOMNI)
|
||||
continue;
|
||||
|
||||
//debug(2, "resType = %08X; indexOffs = %d\n", resType, indexOffs);
|
||||
|
||||
uint32 oldOffs = _fd->pos();
|
||||
|
||||
ResourceSlots *resSlots = new ResourceSlots();
|
||||
_fd->seek(indexOffs);
|
||||
loadIndex(resSlots);
|
||||
_resSlots[resType] = resSlots;
|
||||
|
||||
_fd->seek(oldOffs);
|
||||
|
||||
}
|
||||
|
||||
_cacheCount = 0;
|
||||
}
|
||||
|
||||
// V1
|
||||
void ResourceReader::openResourceBlocks() {
|
||||
_isV1 = true;
|
||||
_fdPics = new Common::File();
|
||||
_fdSounds = new Common::File();
|
||||
_fdMusic = new Common::File();
|
||||
|
||||
openResourceBlock("pics.blk", _fdPics, kResFLEX);
|
||||
openResourceBlock("snds.blk", _fdSounds, kResSNDS);
|
||||
openResourceBlock("music.blk", _fdMusic, kResMIDI);
|
||||
}
|
||||
|
||||
void ResourceReader::openResourceBlock(const char *filename, Common::File *blockFile, uint32 resType) {
|
||||
if (!blockFile->open(filename))
|
||||
error("Failed to open '%s'", filename);
|
||||
|
||||
blockFile->readUint16LE(); // Skip unused
|
||||
uint16 count = blockFile->readUint16LE();
|
||||
blockFile->readUint16LE(); // Skip unused
|
||||
uint32 type = blockFile->readUint32BE();
|
||||
if (type != kResFLEX)
|
||||
warning("openResourceBlocks: resource header is not 'FLEX'");
|
||||
|
||||
_resSlots[resType] = new ResourceSlots();
|
||||
|
||||
// Add dummy entry since the resources are 1-based
|
||||
_resSlots[resType]->push_back(ResourceSlot(0, 0));
|
||||
|
||||
for (uint16 i = 0; i < count; i++) {
|
||||
uint32 offset = blockFile->readUint32LE();
|
||||
blockFile->readUint32LE();
|
||||
uint32 size = blockFile->readUint32LE();
|
||||
_resSlots[resType]->push_back(ResourceSlot(offset, size));
|
||||
}
|
||||
}
|
||||
|
||||
PictureResource *ResourceReader::getPicture(int index) {
|
||||
return createResource<PictureResource>(kResFLEX, index);
|
||||
}
|
||||
|
||||
AnimationResource *ResourceReader::getAnimation(int index) {
|
||||
return createResource<AnimationResource>(kResANIM, index);
|
||||
}
|
||||
|
||||
SoundResource *ResourceReader::getSound(int index) {
|
||||
if (!_isV1)
|
||||
return createResource<SoundResource>(kResSNDS, index);
|
||||
else
|
||||
return createResource<SoundResourceV1>(kResSNDS, index);
|
||||
}
|
||||
|
||||
MenuResource *ResourceReader::getMenu(int index) {
|
||||
return createResource<MenuResource>(kResMENU, index);
|
||||
}
|
||||
|
||||
FontResource *ResourceReader::getFont(int index) {
|
||||
return createResource<FontResource>(kResFONT, index);
|
||||
}
|
||||
|
||||
GenericResource *ResourceReader::getXmidi(int index) {
|
||||
return createResource<GenericResource>(kResXMID, index);
|
||||
}
|
||||
|
||||
GenericResource *ResourceReader::getMidi(int index) {
|
||||
return createResource<GenericResource>(kResMIDI, index);
|
||||
}
|
||||
|
||||
void ResourceReader::loadIndex(ResourceSlots *slots) {
|
||||
_fd->readUint32LE(); // skip INDX
|
||||
_fd->readUint32LE(); // skip index size
|
||||
_fd->readUint32LE(); // skip unknown
|
||||
_fd->readUint32LE(); // skip res type
|
||||
uint16 count1 = _fd->readUint16LE();
|
||||
uint16 count2 = _fd->readUint16LE();
|
||||
uint16 count = MAX(count1, count2);
|
||||
_fd->readUint16LE(); // skip unknown count
|
||||
for (uint16 i = 0; i < count; i++) {
|
||||
uint32 offs = _fd->readUint32LE();
|
||||
uint32 size = _fd->readUint32LE();
|
||||
slots->push_back(ResourceSlot(offs, size));
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceReader::freeResource(Resource *resource) {
|
||||
tossResourceFromCache(resource->_slot);
|
||||
}
|
||||
|
||||
bool ResourceReader::loadResource(ResourceSlot *slot, byte *&buffer, uint32 &size) {
|
||||
int offset = !_isV1 ? 62 : 0;
|
||||
if (slot && slot->size > 0) {
|
||||
size = slot->size - offset;
|
||||
buffer = new byte[size];
|
||||
debug(2, "ResourceReader::loadResource() %08X", slot->offs + offset);
|
||||
_fd->seek(slot->offs + offset);
|
||||
_fd->read(buffer, size);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ResourceSlot *ResourceReader::getResourceSlot(uint32 resType, uint index) {
|
||||
ResourceSlots *slots = _resSlots[resType];
|
||||
|
||||
if (!slots)
|
||||
return nullptr;
|
||||
|
||||
if (index >= 1 && index < slots->size()) {
|
||||
return &(*slots)[index];
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Resource *ResourceReader::getResourceFromCache(ResourceSlot *slot) {
|
||||
if (slot->res)
|
||||
slot->refCount++;
|
||||
return slot->res;
|
||||
}
|
||||
|
||||
void ResourceReader::addResourceToCache(ResourceSlot *slot, Resource *res) {
|
||||
_cacheDataSize += slot->size;
|
||||
|
||||
if (_cacheDataSize >= kMaxResourceCacheSize) {
|
||||
purgeCache();
|
||||
}
|
||||
|
||||
slot->res = res;
|
||||
slot->refCount = 1;
|
||||
_cacheCount++;
|
||||
}
|
||||
|
||||
void ResourceReader::tossResourceFromCache(ResourceSlot *slot) {
|
||||
if (slot->res)
|
||||
slot->refCount--;
|
||||
}
|
||||
|
||||
void ResourceReader::purgeCache() {
|
||||
debug(2, "ResourceReader::purgeCache()");
|
||||
for (const auto &res : _resSlots) {
|
||||
ResourceSlots *slots = res._value;
|
||||
for (ResourceSlots::iterator slotIter = slots->begin(); slotIter != slots->end(); ++slotIter) {
|
||||
ResourceSlot *slot = &(*slotIter);
|
||||
if (slot->refCount <= 0 && slot->res) {
|
||||
_cacheDataSize -= slot->size;
|
||||
delete slot->res;
|
||||
slot->res = nullptr;
|
||||
slot->refCount = 0;
|
||||
_cacheCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Made
|
||||
269
engines/made/resource.h
Normal file
269
engines/made/resource.h
Normal file
@@ -0,0 +1,269 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MADE_RESOURCE_H
|
||||
#define MADE_RESOURCE_H
|
||||
|
||||
#include "made/sound.h"
|
||||
|
||||
#include "common/endian.h"
|
||||
#include "common/array.h"
|
||||
#include "common/hashmap.h"
|
||||
|
||||
namespace Common {
|
||||
class File;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
class AudioStream;
|
||||
}
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace Made {
|
||||
|
||||
/// This value specifies the size of the resource cache
|
||||
/// which stores recently used resources. On the DS,
|
||||
/// 400Kb is all we can spare, while 1Mb seems like a
|
||||
/// good value for larger systems.
|
||||
#ifndef __DS__
|
||||
const int kMaxResourceCacheSize = 1000 * 1024;
|
||||
#else
|
||||
const int kMaxResourceCacheSize = 400 * 1024;
|
||||
#endif
|
||||
|
||||
|
||||
enum ResourceType {
|
||||
kResARCH = MKTAG('A','R','C','H'),
|
||||
kResFREE = MKTAG('F','R','E','E'),
|
||||
kResOMNI = MKTAG('O','M','N','I'),
|
||||
kResFLEX = MKTAG('F','L','E','X'),
|
||||
kResSNDS = MKTAG('S','N','D','S'),
|
||||
kResANIM = MKTAG('A','N','I','M'),
|
||||
kResMENU = MKTAG('M','E','N','U'),
|
||||
kResFONT = MKTAG('F','O','N','T'),
|
||||
kResXMID = MKTAG('X','M','I','D'),
|
||||
kResMIDI = MKTAG('M','I','D','I')
|
||||
};
|
||||
|
||||
struct ResourceSlot;
|
||||
|
||||
class Resource {
|
||||
public:
|
||||
ResourceSlot *_slot;
|
||||
virtual ~Resource();
|
||||
};
|
||||
|
||||
class PictureResource : public Resource {
|
||||
public:
|
||||
PictureResource();
|
||||
~PictureResource() override;
|
||||
void load(byte *source, int size);
|
||||
Graphics::Surface *getPicture() const { return _picture; }
|
||||
byte *getPalette() const { return _picturePalette; }
|
||||
bool hasPalette() const { return _hasPalette; }
|
||||
int getPaletteColorCount() const { return _paletteColorCount; }
|
||||
protected:
|
||||
Graphics::Surface *_picture;
|
||||
byte *_picturePalette;
|
||||
int _paletteColorCount;
|
||||
bool _hasPalette;
|
||||
void loadRaw(byte *source, int size);
|
||||
void loadChunked(byte *source, int size);
|
||||
};
|
||||
|
||||
class AnimationResource : public Resource {
|
||||
public:
|
||||
AnimationResource();
|
||||
~AnimationResource() override;
|
||||
void load(byte *source, int size);
|
||||
int getCount() const { return _frames.size(); }
|
||||
Graphics::Surface *getFrame(int index) const {
|
||||
if ((uint)index < _frames.size()) {
|
||||
return _frames[index];
|
||||
} else {
|
||||
warning("getFrame: Tried to obtain invalid frame %i, array has %i frames", index, _frames.size());
|
||||
return _frames[_frames.size() - 1];
|
||||
}
|
||||
}
|
||||
uint16 getFlags() const { return _flags; }
|
||||
int16 getWidth() const { return _width; }
|
||||
int16 getHeight() const { return _height; }
|
||||
protected:
|
||||
Common::Array<Graphics::Surface *> _frames;
|
||||
uint16 _flags;
|
||||
int16 _width, _height;
|
||||
};
|
||||
|
||||
class SoundResource : public Resource {
|
||||
public:
|
||||
SoundResource();
|
||||
~SoundResource() override;
|
||||
virtual void load(byte *source, int size);
|
||||
Audio::AudioStream *getAudioStream(int soundRate, bool loop = false);
|
||||
SoundEnergyArray *getSoundEnergyArray() const { return _soundEnergyArray; }
|
||||
int getSoundSize() const { return _soundSize; }
|
||||
protected:
|
||||
byte *_soundData;
|
||||
int _soundSize;
|
||||
SoundEnergyArray *_soundEnergyArray;
|
||||
};
|
||||
|
||||
class SoundResourceV1 : public SoundResource {
|
||||
public:
|
||||
SoundResourceV1() {}
|
||||
~SoundResourceV1() override {}
|
||||
void load(byte *source, int size) override;
|
||||
};
|
||||
|
||||
class MenuResource : public Resource {
|
||||
public:
|
||||
MenuResource();
|
||||
~MenuResource() override;
|
||||
void load(byte *source, int size);
|
||||
int getCount() const { return _strings.size(); }
|
||||
const char *getString(uint index) const;
|
||||
protected:
|
||||
Common::Array<Common::String> _strings;
|
||||
};
|
||||
|
||||
class FontResource : public Resource {
|
||||
public:
|
||||
FontResource();
|
||||
~FontResource() override;
|
||||
void load(byte *source, int size);
|
||||
int getHeight() const;
|
||||
int getCharWidth(uint c) const;
|
||||
int getTextWidth(const char *text);
|
||||
byte *getChar(uint c) const;
|
||||
protected:
|
||||
byte *_data;
|
||||
int _size;
|
||||
byte *getCharData(uint c) const;
|
||||
};
|
||||
|
||||
class GenericResource : public Resource {
|
||||
public:
|
||||
GenericResource();
|
||||
~GenericResource() override;
|
||||
void load(byte *source, int size);
|
||||
byte *getData() const { return _data; }
|
||||
int getSize() const { return _size; }
|
||||
protected:
|
||||
byte *_data;
|
||||
int _size;
|
||||
};
|
||||
|
||||
struct ResourceSlot {
|
||||
uint32 offs;
|
||||
uint32 size;
|
||||
Resource *res;
|
||||
int refCount;
|
||||
ResourceSlot() : offs(0), size(0), res(NULL), refCount(0) {
|
||||
}
|
||||
ResourceSlot(uint32 roffs, uint32 rsize) : offs(roffs), size(rsize), res(NULL), refCount(0) {
|
||||
}
|
||||
};
|
||||
|
||||
class ResourceReader {
|
||||
public:
|
||||
ResourceReader();
|
||||
~ResourceReader();
|
||||
|
||||
void open(const char *filename);
|
||||
void openResourceBlocks();
|
||||
|
||||
PictureResource *getPicture(int index);
|
||||
AnimationResource *getAnimation(int index);
|
||||
SoundResource *getSound(int index);
|
||||
MenuResource *getMenu(int index);
|
||||
FontResource *getFont(int index);
|
||||
GenericResource *getXmidi(int index);
|
||||
GenericResource *getMidi(int index);
|
||||
|
||||
void freeResource(Resource *resource);
|
||||
|
||||
protected:
|
||||
|
||||
Common::File *_fd;
|
||||
Common::File *_fdPics, *_fdSounds, *_fdMusic; // V1
|
||||
bool _isV1;
|
||||
|
||||
typedef Common::Array<ResourceSlot> ResourceSlots;
|
||||
typedef Common::HashMap<uint32, ResourceSlots *> ResMap;
|
||||
void openResourceBlock(const char *filename, Common::File *blockFile, uint32 resType);
|
||||
|
||||
ResMap _resSlots;
|
||||
int _cacheCount;
|
||||
int _cacheDataSize;
|
||||
|
||||
void loadIndex(ResourceSlots *slots);
|
||||
|
||||
template<class T>
|
||||
T *createResource(uint32 resType, int index) {
|
||||
ResourceSlot *slot = getResourceSlot(resType, index);
|
||||
if (!slot)
|
||||
return NULL;
|
||||
T *res = (T *)getResourceFromCache(slot);
|
||||
if (!res) {
|
||||
byte *buffer;
|
||||
uint32 size;
|
||||
|
||||
// Read from the correct file for V1 games
|
||||
if (_isV1) {
|
||||
switch (resType) {
|
||||
case kResSNDS:
|
||||
_fd = _fdSounds;
|
||||
break;
|
||||
case kResMIDI:
|
||||
_fd = _fdMusic;
|
||||
break;
|
||||
default:
|
||||
_fd = _fdPics;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (loadResource(slot, buffer, size)) {
|
||||
res = new T();
|
||||
res->_slot = slot;
|
||||
res->load(buffer, size);
|
||||
addResourceToCache(slot, res);
|
||||
delete[] buffer;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool loadResource(ResourceSlot *slot, byte *&buffer, uint32 &size);
|
||||
ResourceSlot *getResourceSlot(uint32 resType, uint index);
|
||||
Resource *getResourceFromCache(ResourceSlot *slot);
|
||||
void addResourceToCache(ResourceSlot *slot, Resource *res);
|
||||
void tossResourceFromCache(ResourceSlot *slot);
|
||||
void purgeCache();
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Made
|
||||
|
||||
#endif /* MADE_H */
|
||||
990
engines/made/screen.cpp
Normal file
990
engines/made/screen.cpp
Normal file
@@ -0,0 +1,990 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "made/screen.h"
|
||||
#include "made/made.h"
|
||||
#include "made/screenfx.h"
|
||||
#include "made/database.h"
|
||||
|
||||
#include "common/system.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
#include "graphics/paletteman.h"
|
||||
#include "graphics/cursorman.h"
|
||||
|
||||
namespace Made {
|
||||
|
||||
enum TextChannelIndex {
|
||||
kTapeRecorderName = 84,
|
||||
kTapeRecorderTrack = 85,
|
||||
kTapeRecorderMaxTrack = 86,
|
||||
kTapeRecorderTime = 87,
|
||||
kTapeRecorderScan = 88,
|
||||
kHoverOver = 89,
|
||||
kClickMessage = 97
|
||||
};
|
||||
|
||||
enum TapeRecorderIndex {
|
||||
kTime = 0,
|
||||
kMaxTrack = 1,
|
||||
kTrack = 2,
|
||||
kName = 3
|
||||
};
|
||||
|
||||
Screen::Screen(MadeEngine *vm) : _vm(vm) {
|
||||
|
||||
_palette = new byte[768];
|
||||
_newPalette = new byte[768];
|
||||
|
||||
_backgroundScreen = new Graphics::Surface();
|
||||
_backgroundScreen->create(320, 200, Graphics::PixelFormat::createFormatCLUT8());
|
||||
|
||||
_workScreen = new Graphics::Surface();
|
||||
_workScreen->create(320, 200, Graphics::PixelFormat::createFormatCLUT8());
|
||||
|
||||
_backgroundScreenDrawCtx.clipRect = Common::Rect(320, 200);
|
||||
_workScreenDrawCtx.clipRect = Common::Rect(320, 200);
|
||||
|
||||
_backgroundScreenDrawCtx.destSurface = _backgroundScreen;
|
||||
_workScreenDrawCtx.destSurface = _workScreen;
|
||||
_clipArea.destSurface = _workScreen;
|
||||
|
||||
// Screen mask is only needed in v2 games
|
||||
if (_vm->getGameID() != GID_RTZ) {
|
||||
_screenMask = new Graphics::Surface();
|
||||
_screenMask->create(320, 200, Graphics::PixelFormat::createFormatCLUT8());
|
||||
_maskDrawCtx.clipRect = Common::Rect(320, 200);
|
||||
_maskDrawCtx.destSurface = _screenMask;
|
||||
}
|
||||
|
||||
for (int i = 0; i <= 3; i++)
|
||||
_excludeClipAreaEnabled[i] = false;
|
||||
|
||||
_screenLock = false;
|
||||
_paletteLock = false;
|
||||
|
||||
_paletteInitialized = false;
|
||||
_needPalette = false;
|
||||
_oldPaletteColorCount = 256;
|
||||
_paletteColorCount = 256;
|
||||
memset(_newPalette, 0, 768);
|
||||
memset(_palette, 0, 768);
|
||||
|
||||
_ground = 1;
|
||||
_clip = 0;
|
||||
_exclude = 0;
|
||||
_mask = 0;
|
||||
|
||||
_visualEffectNum = 0;
|
||||
_fx = new ScreenEffects(this);
|
||||
|
||||
_textX = 0;
|
||||
_textY = 0;
|
||||
_textColor = 0;
|
||||
_textRect.left = 0;
|
||||
_textRect.top = 0;
|
||||
_textRect.right = 320;
|
||||
_textRect.bottom = 200;
|
||||
_font = nullptr;
|
||||
_currentFontNum = 0;
|
||||
_fontDrawCtx.clipRect = Common::Rect(320, 200);
|
||||
_fontDrawCtx.destSurface = _backgroundScreen;
|
||||
_outlineColor = 0;
|
||||
_dropShadowColor = 0;
|
||||
|
||||
_queueNextText = false;
|
||||
_voiceTimeText = false;
|
||||
|
||||
clearChannels();
|
||||
}
|
||||
|
||||
Screen::~Screen() {
|
||||
|
||||
delete[] _palette;
|
||||
delete[] _newPalette;
|
||||
|
||||
delete _backgroundScreen;
|
||||
delete _workScreen;
|
||||
if (_vm->getGameID() != GID_RTZ)
|
||||
delete _screenMask;
|
||||
delete _fx;
|
||||
}
|
||||
|
||||
void Screen::clearScreen() {
|
||||
_backgroundScreen->fillRect(Common::Rect(0, 0, 320, 200), 0);
|
||||
_workScreen->fillRect(Common::Rect(0, 0, 320, 200), 0);
|
||||
if (_vm->getGameID() != GID_RTZ)
|
||||
_screenMask->fillRect(Common::Rect(0, 0, 320, 200), 0);
|
||||
_mask = 0;
|
||||
_needPalette = true;
|
||||
}
|
||||
|
||||
void Screen::setExcludeArea(uint16 x1, uint16 y1, uint16 x2, uint16 y2) {
|
||||
|
||||
_excludeClipAreaEnabled[0] = false;
|
||||
_excludeClipAreaEnabled[1] = false;
|
||||
_excludeClipAreaEnabled[2] = false;
|
||||
_excludeClipAreaEnabled[3] = false;
|
||||
|
||||
if (x1 == 0 && y1 == 0 && x2 == 0 && y2 == 0) {
|
||||
_excludeClipArea[0].clipRect = Common::Rect(320, 200);
|
||||
_excludeClipAreaEnabled[0] = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (y1 > 0 && y2 > 0) {
|
||||
_excludeClipArea[0].clipRect = Common::Rect(320, y1);
|
||||
_excludeClipAreaEnabled[0] = true;
|
||||
}
|
||||
|
||||
if (y1 < 200 && y2 < 200) {
|
||||
_excludeClipArea[1].clipRect = Common::Rect(0, y2, 320, 200);
|
||||
_excludeClipAreaEnabled[1] = true;
|
||||
}
|
||||
|
||||
if (x1 > 0 && x2 > 0) {
|
||||
_excludeClipArea[2].clipRect = Common::Rect(0, y1, x1, y2);
|
||||
_excludeClipAreaEnabled[2] = true;
|
||||
}
|
||||
|
||||
if (x1 < 320 && x2 < 320) {
|
||||
_excludeClipArea[3].clipRect = Common::Rect(x2, y1, 320, y2);
|
||||
_excludeClipAreaEnabled[3] = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Screen::drawSurface(Graphics::Surface *sourceSurface, int x, int y, int16 flipX, int16 flipY, int16 mask, const ClipInfo &clipInfo) {
|
||||
|
||||
byte *source, *dest, *maskp = nullptr;
|
||||
int startX = 0;
|
||||
int startY = 0;
|
||||
int clipWidth = sourceSurface->w;
|
||||
int clipHeight = sourceSurface->h;
|
||||
|
||||
if (x < clipInfo.clipRect.left) {
|
||||
startX = clipInfo.clipRect.left - x;
|
||||
clipWidth -= startX;
|
||||
x = clipInfo.clipRect.left;
|
||||
}
|
||||
|
||||
if (y < clipInfo.clipRect.top) {
|
||||
startY = clipInfo.clipRect.top - y;
|
||||
clipHeight -= startY;
|
||||
y = clipInfo.clipRect.top;
|
||||
}
|
||||
|
||||
if (x + clipWidth > clipInfo.clipRect.right) {
|
||||
clipWidth = clipInfo.clipRect.right - x;
|
||||
}
|
||||
|
||||
if (y + clipHeight > clipInfo.clipRect.bottom) {
|
||||
clipHeight = clipInfo.clipRect.bottom - y;
|
||||
}
|
||||
|
||||
source = (byte *)sourceSurface->getBasePtr(0, startY);
|
||||
dest = (byte *)clipInfo.destSurface->getBasePtr(x, y);
|
||||
if (_vm->getGameID() != GID_RTZ)
|
||||
maskp = (byte *)_maskDrawCtx.destSurface->getBasePtr(x, y);
|
||||
|
||||
int32 sourcePitch, linePtrAdd, sourceAdd;
|
||||
byte *linePtr;
|
||||
|
||||
if (flipX) {
|
||||
linePtrAdd = -1;
|
||||
sourceAdd = sourceSurface->w - startX - 1;
|
||||
} else {
|
||||
linePtrAdd = 1;
|
||||
sourceAdd = startX;
|
||||
}
|
||||
|
||||
if (flipY) {
|
||||
sourcePitch = -sourceSurface->pitch;
|
||||
source += (clipHeight - 1) * sourceSurface->pitch;
|
||||
} else {
|
||||
sourcePitch = sourceSurface->pitch;
|
||||
}
|
||||
|
||||
for (int16 yc = 0; yc < clipHeight; yc++) {
|
||||
linePtr = source + sourceAdd;
|
||||
for (int16 xc = 0; xc < clipWidth; xc++) {
|
||||
if (*linePtr && (_vm->getGameID() == GID_RTZ || (mask == 0 || (maskp && maskp[xc] == 0)))) {
|
||||
if (*linePtr)
|
||||
dest[xc] = *linePtr;
|
||||
}
|
||||
linePtr += linePtrAdd;
|
||||
}
|
||||
|
||||
source += sourcePitch;
|
||||
dest += clipInfo.destSurface->pitch;
|
||||
if (_vm->getGameID() != GID_RTZ)
|
||||
maskp += _maskDrawCtx.destSurface->pitch;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Screen::setRGBPalette(byte *palRGB, int start, int count) {
|
||||
_vm->_system->getPaletteManager()->setPalette(palRGB, start, count);
|
||||
}
|
||||
|
||||
uint16 Screen::updateChannel(uint16 channelIndex) {
|
||||
return channelIndex;
|
||||
}
|
||||
|
||||
void Screen::deleteChannel(uint16 channelIndex) {
|
||||
if (channelIndex < 1 || channelIndex >= 100)
|
||||
return;
|
||||
|
||||
if (_channels[channelIndex - 1].type == 2) {
|
||||
_channels[channelIndex - 1].previousText.clear();
|
||||
}
|
||||
|
||||
_channels[channelIndex - 1].type = 0;
|
||||
_channels[channelIndex - 1].state = 0;
|
||||
_channels[channelIndex - 1].index = 0;
|
||||
}
|
||||
|
||||
int16 Screen::getChannelType(uint16 channelIndex) {
|
||||
if (channelIndex < 1 || channelIndex >= 100)
|
||||
return -1;
|
||||
return _channels[channelIndex - 1].type;
|
||||
}
|
||||
|
||||
int16 Screen::getChannelState(uint16 channelIndex) {
|
||||
if (channelIndex < 1 || channelIndex >= 100)
|
||||
return -1;
|
||||
return _channels[channelIndex - 1].state;
|
||||
}
|
||||
|
||||
void Screen::setChannelState(uint16 channelIndex, int16 state) {
|
||||
if (channelIndex < 1 || channelIndex >= 100 || _channels[channelIndex - 1].type == 0)
|
||||
return;
|
||||
|
||||
if (state != _channels[channelIndex - 1].state && _channels[channelIndex - 1].type == 2) {
|
||||
_channels[channelIndex - 1].previousText.clear();
|
||||
_queueNextText = true;
|
||||
}
|
||||
|
||||
_channels[channelIndex - 1].state = state;
|
||||
}
|
||||
|
||||
uint16 Screen::setChannelLocation(uint16 channelIndex, int16 x, int16 y) {
|
||||
if (channelIndex < 1 || channelIndex >= 100 || _channels[channelIndex - 1].type == 0)
|
||||
return 0;
|
||||
_channels[channelIndex - 1].x = x;
|
||||
_channels[channelIndex - 1].y = y;
|
||||
return updateChannel(channelIndex - 1) + 1;
|
||||
}
|
||||
|
||||
uint16 Screen::setChannelContent(uint16 channelIndex, uint16 index) {
|
||||
if (channelIndex < 1 || channelIndex >= 100 || _channels[channelIndex - 1].type == 0)
|
||||
return 0;
|
||||
_channels[channelIndex - 1].index = index;
|
||||
return updateChannel(channelIndex - 1) + 1;
|
||||
}
|
||||
|
||||
void Screen::setChannelUseMask(uint16 channelIndex) {
|
||||
if (channelIndex < 1 || channelIndex >= 100)
|
||||
return;
|
||||
_channels[channelIndex - 1].mask = _mask;
|
||||
}
|
||||
|
||||
void Screen::drawSpriteChannels(const ClipInfo &clipInfo, int16 includeStateMask, int16 excludeStateMask) {
|
||||
|
||||
for (int i = 0; i <= 3; i++)
|
||||
_excludeClipArea[i].destSurface = clipInfo.destSurface;
|
||||
|
||||
_clipArea.destSurface = clipInfo.destSurface;
|
||||
|
||||
for (uint16 i = 0; i < _channelsUsedCount; i++) {
|
||||
|
||||
debug(2, "drawSpriteChannels() i = %d\n", i);
|
||||
|
||||
if (((_channels[i].state & includeStateMask) == includeStateMask) && (_channels[i].state & excludeStateMask) == 0) {
|
||||
int16 flipX = _channels[i].state & 0x10;
|
||||
int16 flipY = _channels[i].state & 0x20;
|
||||
|
||||
debug(2, "drawSpriteChannels() type = %d; index = %04X\n", _channels[i].type, _channels[i].index);
|
||||
|
||||
switch (_channels[i].type) {
|
||||
|
||||
case 1: // drawFlex
|
||||
if (_channels[i].state & 4) {
|
||||
drawFlex(_channels[i].index, _channels[i].x, _channels[i].y, flipX, flipY, _channels[i].mask, _clipArea);
|
||||
} else if (_channels[i].state & 8) {
|
||||
for (int excludeIndex = 0; excludeIndex < 4; excludeIndex++) {
|
||||
if (_excludeClipAreaEnabled[excludeIndex]) {
|
||||
drawFlex(_channels[i].index, _channels[i].x, _channels[i].y, flipX, flipY, _channels[i].mask, _excludeClipArea[excludeIndex]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
drawFlex(_channels[i].index, _channels[i].x, _channels[i].y, flipX, flipY, _channels[i].mask, clipInfo);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: // drawObjectText
|
||||
printObjectText(_channels[i].index, _channels[i].x, _channels[i].y, _channels[i].fontNum, _channels[i].textColor, _channels[i].outlineColor, clipInfo);
|
||||
break;
|
||||
|
||||
case 3: // drawAnimFrame
|
||||
if (_channels[i].state & 4) {
|
||||
drawAnimFrame(_channels[i].index, _channels[i].x, _channels[i].y, _channels[i].frameNum, flipX, flipY, _clipArea);
|
||||
} else if (_channels[i].state & 8) {
|
||||
for (int excludeIndex = 0; excludeIndex < 4; excludeIndex++) {
|
||||
if (_excludeClipAreaEnabled[excludeIndex]) {
|
||||
drawAnimFrame(_channels[i].index, _channels[i].x, _channels[i].y, _channels[i].frameNum, flipX, flipY, _excludeClipArea[excludeIndex]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
drawAnimFrame(_channels[i].index, _channels[i].x, _channels[i].y, _channels[i].frameNum, flipX, flipY, clipInfo);
|
||||
}
|
||||
break;
|
||||
|
||||
case 4: // drawMenuText
|
||||
// Never used in any game
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Screen::updateSprites() {
|
||||
// TODO: This needs some more work, dirty rectangles are currently not used
|
||||
|
||||
memcpy(_workScreen->getPixels(), _backgroundScreen->getPixels(), 64000);
|
||||
|
||||
drawSpriteChannels(_backgroundScreenDrawCtx, 3, 0);
|
||||
drawSpriteChannels(_workScreenDrawCtx, 1, 2);
|
||||
|
||||
_vm->_system->copyRectToScreen(_workScreen->getPixels(), _workScreen->pitch, 0, 0, _workScreen->w, _workScreen->h);
|
||||
_vm->_screen->updateScreenAndWait(10);
|
||||
}
|
||||
|
||||
void Screen::clearChannels() {
|
||||
for (uint16 i = 0; i < ARRAYSIZE(_channels); i++) {
|
||||
_channels[i].type = 0;
|
||||
_channels[i].index = 0;
|
||||
_channels[i].mask = 0;
|
||||
}
|
||||
_channelsUsedCount = 0;
|
||||
}
|
||||
|
||||
uint16 Screen::drawFlex(uint16 flexIndex, int16 x, int16 y, int16 flipX, int16 flipY, int16 mask, const ClipInfo &clipInfo) {
|
||||
|
||||
if (flexIndex == 0)
|
||||
return 0;
|
||||
|
||||
PictureResource *flex = _vm->_res->getPicture(flexIndex);
|
||||
if (!flex)
|
||||
error("Failed to find picture %d", flexIndex);
|
||||
|
||||
Graphics::Surface *sourceSurface = flex->getPicture();
|
||||
|
||||
drawSurface(sourceSurface, x, y, flipX, flipY, mask, clipInfo);
|
||||
|
||||
// Palette is set in showPage
|
||||
if (flex->hasPalette() && !_paletteLock && _needPalette) {
|
||||
byte *flexPalette = flex->getPalette();
|
||||
_oldPaletteColorCount = _paletteColorCount;
|
||||
_paletteColorCount = flex->getPaletteColorCount();
|
||||
memcpy(_newPalette, _palette, _oldPaletteColorCount * 3);
|
||||
memcpy(_palette, flexPalette, _paletteColorCount * 3);
|
||||
_needPalette = false;
|
||||
}
|
||||
|
||||
_vm->_res->freeResource(flex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Screen::drawAnimFrame(uint16 animIndex, int16 x, int16 y, int16 frameNum, int16 flipX, int16 flipY, const ClipInfo &clipInfo) {
|
||||
|
||||
if (frameNum < 0)
|
||||
return;
|
||||
|
||||
AnimationResource *anim = _vm->_res->getAnimation(animIndex);
|
||||
Graphics::Surface *sourceSurface = anim->getFrame(frameNum);
|
||||
|
||||
drawSurface(sourceSurface, x, y, flipX, flipY, 0, clipInfo);
|
||||
|
||||
_vm->_res->freeResource(anim);
|
||||
}
|
||||
|
||||
uint16 Screen::drawPic(uint16 index, int16 x, int16 y, int16 flipX, int16 flipY) {
|
||||
drawFlex(index, x, y, flipX, flipY, 0, _backgroundScreenDrawCtx);
|
||||
|
||||
#ifdef USE_TTS
|
||||
if (_vm->getGameID() == GID_RTZ && index > 0) {
|
||||
if (index == 843) { // Save/load screen
|
||||
_vm->_saveLoadScreenOpen = true;
|
||||
_vm->_rtzSaveLoadIndex = 0;
|
||||
_vm->_rtzFirstSaveSlot = 0;
|
||||
} else {
|
||||
_vm->_saveLoadScreenOpen = false;
|
||||
}
|
||||
|
||||
if (index == 1501) { // Tape recorder
|
||||
_vm->_tapeRecorderOpen = true;
|
||||
} else {
|
||||
_vm->_tapeRecorderOpen = false;
|
||||
}
|
||||
} else if (_vm->getGameID() == GID_LGOP2) {
|
||||
if (index == 465) { // Save/load screen (Play-O-Matic)
|
||||
_vm->_playOMaticButtonIndex = 0;
|
||||
_vm->_saveLoadScreenOpen = true;
|
||||
} else if (index == 196) { // Play-O-Matic button highlights
|
||||
_vm->checkHoveringPlayOMatic(y);
|
||||
} else if (index != 463 && index != 0) {
|
||||
// 1216 is drawn before "best interactive fiction" line, 757 before "choose your character", and 761 before the
|
||||
// second copyright message, all of which need voicing
|
||||
if (index == 1216 || index == 757 || index == 761) {
|
||||
_vm->_forceVoiceText = true;
|
||||
}
|
||||
_vm->_saveLoadScreenOpen = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16 Screen::drawMask(uint16 index, int16 x, int16 y) {
|
||||
drawFlex(index, x, y, 0, 0, 0, _maskDrawCtx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16 Screen::drawAnimPic(uint16 animIndex, int16 x, int16 y, int16 frameNum, int16 flipX, int16 flipY) {
|
||||
drawAnimFrame(animIndex, x, y, frameNum, flipX, flipY, _backgroundScreenDrawCtx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Screen::addSprite(uint16 spriteIndex) {
|
||||
bool oldScreenLock = _screenLock;
|
||||
drawFlex(spriteIndex, 0, 0, 0, 0, 0, _backgroundScreenDrawCtx);
|
||||
_screenLock = oldScreenLock;
|
||||
}
|
||||
|
||||
uint16 Screen::drawSprite(uint16 flexIndex, int16 x, int16 y) {
|
||||
return placeSprite(_channelsUsedCount + 1, flexIndex, x, y);
|
||||
}
|
||||
|
||||
uint16 Screen::placeSprite(uint16 channelIndex, uint16 flexIndex, int16 x, int16 y) {
|
||||
|
||||
debug(2, "placeSprite(%d, %04X, %d, %d)\n", channelIndex, flexIndex, x, y);
|
||||
|
||||
if (channelIndex < 1 || channelIndex >= 100)
|
||||
return 0;
|
||||
|
||||
channelIndex--;
|
||||
|
||||
PictureResource *flex = _vm->_res->getPicture(flexIndex);
|
||||
|
||||
if (flex) {
|
||||
//Graphics::Surface *surf = flex->getPicture();
|
||||
|
||||
int16 state = 1;
|
||||
/*int16 x1, y1, x2, y2;
|
||||
|
||||
x1 = x;
|
||||
y1 = y;
|
||||
x2 = x + surf->w + 1;
|
||||
y2 = y + surf->h + 1;*/
|
||||
|
||||
if (_ground == 0)
|
||||
state |= 2;
|
||||
if (_clip != 0)
|
||||
state |= 4;
|
||||
if (_exclude != 0)
|
||||
state |= 8;
|
||||
|
||||
_channels[channelIndex].state = state;
|
||||
_channels[channelIndex].type = 1;
|
||||
_channels[channelIndex].index = flexIndex;
|
||||
_channels[channelIndex].x = x;
|
||||
_channels[channelIndex].y = y;
|
||||
|
||||
if (_channelsUsedCount <= channelIndex)
|
||||
_channelsUsedCount = channelIndex + 1;
|
||||
|
||||
_vm->_res->freeResource(flex);
|
||||
} else {
|
||||
_channels[channelIndex].type = 0;
|
||||
_channels[channelIndex].state = 0;
|
||||
}
|
||||
|
||||
return channelIndex + 1;
|
||||
|
||||
}
|
||||
|
||||
uint16 Screen::placeAnim(uint16 channelIndex, uint16 animIndex, int16 x, int16 y, int16 frameNum) {
|
||||
|
||||
if (channelIndex < 1 || channelIndex >= 100)
|
||||
return 0;
|
||||
|
||||
channelIndex--;
|
||||
|
||||
AnimationResource *anim = _vm->_res->getAnimation(animIndex);
|
||||
|
||||
if (anim) {
|
||||
|
||||
int16 state = 1;
|
||||
/*int16 x1, y1, x2, y2;
|
||||
|
||||
x1 = x;
|
||||
y1 = y;
|
||||
x2 = x + anim->getWidth();
|
||||
y2 = y + anim->getHeight();*/
|
||||
|
||||
if (anim->getFlags() == 1 || _ground == 0)
|
||||
state |= 2;
|
||||
if (_clip != 0)
|
||||
state |= 4;
|
||||
if (_exclude != 0)
|
||||
state |= 8;
|
||||
|
||||
_channels[channelIndex].state = state;
|
||||
_channels[channelIndex].type = 3;
|
||||
_channels[channelIndex].index = animIndex;
|
||||
_channels[channelIndex].frameNum = frameNum;
|
||||
_channels[channelIndex].x = x;
|
||||
_channels[channelIndex].y = y;
|
||||
|
||||
if (_channelsUsedCount <= channelIndex)
|
||||
_channelsUsedCount = channelIndex + 1;
|
||||
|
||||
_vm->_res->freeResource(anim);
|
||||
} else {
|
||||
_channels[channelIndex].type = 0;
|
||||
_channels[channelIndex].state = 0;
|
||||
}
|
||||
|
||||
return channelIndex + 1;
|
||||
|
||||
}
|
||||
|
||||
int16 Screen::setAnimFrame(uint16 channelIndex, int16 frameNum) {
|
||||
if (channelIndex < 1 || channelIndex >= 100 || _channels[channelIndex - 1].type == 0)
|
||||
return 0;
|
||||
channelIndex--;
|
||||
_channels[channelIndex].frameNum = frameNum;
|
||||
return updateChannel(channelIndex) + 1;
|
||||
}
|
||||
|
||||
int16 Screen::getAnimFrame(uint16 channelIndex) {
|
||||
if (channelIndex < 1 || channelIndex >= 100 || _channels[channelIndex - 1].type == 0)
|
||||
return -1;
|
||||
return _channels[channelIndex - 1].frameNum;
|
||||
}
|
||||
|
||||
uint16 Screen::placeText(uint16 channelIndex, uint16 textObjectIndex, int16 x, int16 y, uint16 fontNum, int16 textColor, int16 outlineColor) {
|
||||
|
||||
if (channelIndex < 1 || channelIndex >= 100 || textObjectIndex == 0 || fontNum == 0)
|
||||
return 0;
|
||||
|
||||
channelIndex--;
|
||||
|
||||
Object *obj = _vm->_dat->getObject(textObjectIndex);
|
||||
const char *text = obj->getString();
|
||||
|
||||
//int16 x1, y1, x2, y2;
|
||||
|
||||
setFont(fontNum);
|
||||
|
||||
int textWidth = _font->getTextWidth(text);
|
||||
//int textHeight = _font->getHeight();
|
||||
|
||||
if (outlineColor != -1) {
|
||||
textWidth += 2;
|
||||
//textHeight += 2;
|
||||
x--;
|
||||
y--;
|
||||
}
|
||||
|
||||
/*x1 = x;
|
||||
y1 = y;
|
||||
x2 = x + textWidth;
|
||||
y2 = y + textHeight;*/
|
||||
|
||||
if (textWidth > 0 && outlineColor != -1) {
|
||||
x++;
|
||||
y++;
|
||||
}
|
||||
|
||||
int16 state = 1;
|
||||
|
||||
if (_ground == 0)
|
||||
state |= 2;
|
||||
|
||||
// The channel for this message isn't deleted until the text on screen disappears, but it gets refreshed
|
||||
// if the player clicks again, so the previous text needs to be manually reset here to allow the message to be voiced
|
||||
// whenever the player clicks
|
||||
if (channelIndex == kClickMessage && (_channels[channelIndex].x != x || _channels[channelIndex].y != y)) {
|
||||
_channels[channelIndex].previousText.clear();
|
||||
_queueNextText = true;
|
||||
}
|
||||
|
||||
_channels[channelIndex].state = state;
|
||||
_channels[channelIndex].type = 2;
|
||||
_channels[channelIndex].index = textObjectIndex;
|
||||
_channels[channelIndex].x = x;
|
||||
_channels[channelIndex].y = y;
|
||||
_channels[channelIndex].textColor = textColor;
|
||||
_channels[channelIndex].fontNum = fontNum;
|
||||
_channels[channelIndex].outlineColor = outlineColor;
|
||||
|
||||
#ifdef USE_TTS
|
||||
voiceChannelText(text, channelIndex);
|
||||
#endif
|
||||
|
||||
if (_channelsUsedCount <= channelIndex)
|
||||
_channelsUsedCount = channelIndex + 1;
|
||||
|
||||
return channelIndex + 1;
|
||||
}
|
||||
|
||||
#ifdef USE_TTS
|
||||
|
||||
void Screen::voiceChannelText(const char *text, uint16 channelIndex) {
|
||||
if ((channelIndex != kTapeRecorderTime && strcmp(_channels[channelIndex].previousText.c_str(), text)) ||
|
||||
(channelIndex == kTapeRecorderTime && _voiceTimeText)) {
|
||||
size_t len = strlen(text);
|
||||
_channels[channelIndex].previousText = text;
|
||||
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (channelIndex == kHoverOver && _queueNextText) {
|
||||
_vm->sayText(text, Common::TextToSpeechManager::QUEUE);
|
||||
_queueNextText = false;
|
||||
} else {
|
||||
bool voiceText = true;
|
||||
|
||||
Object *object = nullptr;
|
||||
const char *message = nullptr;
|
||||
switch (channelIndex) {
|
||||
case kTapeRecorderName:
|
||||
// Voice name, track, and max track all at once, so that they're all properly voiced
|
||||
// when the player switches between entries on the tape recorder (the track and max track numbers
|
||||
// aren't necessarily unique, and may not be voiced otherwise)
|
||||
_vm->sayText(Common::String::format("%s: %s", _vm->_tapeRecorderText[kName].c_str(), text));
|
||||
|
||||
// Track
|
||||
object = _vm->_dat->getObject(_channels[kTapeRecorderTrack].index);
|
||||
if (object) {
|
||||
message = object->getString();
|
||||
}
|
||||
_channels[kTapeRecorderTrack].previousText = message;
|
||||
_vm->sayText(Common::String::format("%s: %s", _vm->_tapeRecorderText[kTrack].c_str(),
|
||||
_channels[kTapeRecorderTrack].previousText.c_str()),
|
||||
Common::TextToSpeechManager::QUEUE);
|
||||
|
||||
// Max track
|
||||
object = _vm->_dat->getObject(_channels[kTapeRecorderMaxTrack].index);
|
||||
if (object) {
|
||||
message = object->getString();
|
||||
}
|
||||
_vm->sayText(Common::String::format("%s: %s", _vm->_tapeRecorderText[kMaxTrack].c_str(), message),
|
||||
Common::TextToSpeechManager::QUEUE);
|
||||
|
||||
voiceText = false;
|
||||
break;
|
||||
case kTapeRecorderTrack:
|
||||
if (!_channels[kTapeRecorderName].previousText.empty()) {
|
||||
// Voice here in case the track is changed while the tape recorder is open
|
||||
_vm->sayText(Common::String::format("%s: %s", _vm->_tapeRecorderText[kTrack].c_str(), text),
|
||||
Common::TextToSpeechManager::QUEUE);
|
||||
}
|
||||
// fall through
|
||||
case kTapeRecorderMaxTrack:
|
||||
// Max track shouldn't change unless the player changes entries, in which case it'll be
|
||||
// voiced under the kTapeRecorderName condition, so no need to voice it here
|
||||
voiceText = false;
|
||||
break;
|
||||
case kTapeRecorderTime:
|
||||
_voiceTimeText = false;
|
||||
_vm->sayText(Common::String::format("%s: %s", _vm->_tapeRecorderText[kTime].c_str(), text), Common::TextToSpeechManager::QUEUE);
|
||||
voiceText = false;
|
||||
}
|
||||
|
||||
if (voiceText) {
|
||||
if (_vm->_saveLoadScreenOpen) {
|
||||
Common::String ttsText(text);
|
||||
ttsText.replace('_', ' ');
|
||||
_vm->sayText(ttsText, Common::TextToSpeechManager::QUEUE);
|
||||
} else {
|
||||
_vm->sayText(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void Screen::show() {
|
||||
|
||||
if (_screenLock)
|
||||
return;
|
||||
|
||||
drawSpriteChannels(_backgroundScreenDrawCtx, 3, 0);
|
||||
memcpy(_workScreen->getPixels(), _backgroundScreen->getPixels(), 64000);
|
||||
drawSpriteChannels(_workScreenDrawCtx, 1, 2);
|
||||
|
||||
_fx->run(_visualEffectNum, _workScreen, _palette, _newPalette, _paletteColorCount);
|
||||
_visualEffectNum = 0;
|
||||
|
||||
if (!_paletteInitialized) {
|
||||
memcpy(_newPalette, _palette, _paletteColorCount * 3);
|
||||
_oldPaletteColorCount = _paletteColorCount;
|
||||
_paletteInitialized = true;
|
||||
}
|
||||
|
||||
updateScreenAndWait(10);
|
||||
}
|
||||
|
||||
void Screen::flash(int flashCount) {
|
||||
_fx->flash(flashCount, _palette, _paletteColorCount);
|
||||
}
|
||||
|
||||
void Screen::setFont(int16 fontNum) {
|
||||
if (fontNum == _currentFontNum)
|
||||
return;
|
||||
if (_font)
|
||||
_vm->_res->freeResource(_font);
|
||||
_font = _vm->_res->getFont(fontNum);
|
||||
_currentFontNum = fontNum;
|
||||
}
|
||||
|
||||
void Screen::printChar(uint c, int16 x, int16 y, byte color) {
|
||||
|
||||
if (!_font)
|
||||
return;
|
||||
|
||||
uint width = 8, height = _font->getHeight();
|
||||
byte *charData = _font->getChar(c);
|
||||
|
||||
if (!charData)
|
||||
return;
|
||||
|
||||
byte p;
|
||||
byte *dest = (byte *)_fontDrawCtx.destSurface->getBasePtr(x, y);
|
||||
|
||||
for (uint yc = 0; yc < height; yc++) {
|
||||
p = charData[yc];
|
||||
for (uint xc = 0; xc < width; xc++) {
|
||||
if (p & 0x80)
|
||||
dest[xc] = color;
|
||||
p <<= 1;
|
||||
}
|
||||
dest += _fontDrawCtx.destSurface->pitch;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Screen::printText(const char *text) {
|
||||
|
||||
const int tabWidth = 5;
|
||||
|
||||
if (!_font)
|
||||
return;
|
||||
|
||||
int textLen = strlen(text);
|
||||
int textHeight = _font->getHeight();
|
||||
int linePos = 1;
|
||||
int16 x = _textX;
|
||||
int16 y = _textY;
|
||||
|
||||
for (int textPos = 0; textPos < textLen; textPos++) {
|
||||
|
||||
uint c = ((const byte*)text)[textPos];
|
||||
int charWidth = _font->getCharWidth(c);
|
||||
|
||||
if (c == 9) {
|
||||
linePos = ((linePos / tabWidth) + 1) * tabWidth;
|
||||
x = _textRect.left + linePos * _font->getCharWidth(32);
|
||||
} else if (c == 10) {
|
||||
linePos = 1;
|
||||
x = _textRect.left;
|
||||
y += textHeight;
|
||||
} else if (c == 13) {
|
||||
linePos = 1;
|
||||
x = _textRect.left;
|
||||
} else if (c == 32) {
|
||||
int wrapPos = textPos + 1;
|
||||
int wrapX = x + charWidth;
|
||||
while (wrapPos < textLen && text[wrapPos] != 0 && text[wrapPos] != 32 && text[wrapPos] >= 28) {
|
||||
wrapX += _font->getCharWidth(text[wrapPos]);
|
||||
wrapPos++;
|
||||
}
|
||||
if (wrapX >= _textRect.right) {
|
||||
linePos = 1;
|
||||
x = _textRect.left;
|
||||
y += textHeight;
|
||||
charWidth = 0;
|
||||
// TODO: text[textPos] = '\x01';
|
||||
}
|
||||
}
|
||||
|
||||
if (x + charWidth > _textRect.right) {
|
||||
linePos = 1;
|
||||
x = _textRect.left;
|
||||
y += textHeight;
|
||||
}
|
||||
|
||||
if (y + textHeight > _textRect.bottom) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
if (c >= 28 && c <= 255) {
|
||||
if (_dropShadowColor != -1) {
|
||||
printChar(c, x + 1, y + 1, _dropShadowColor);
|
||||
}
|
||||
if (_outlineColor != -1) {
|
||||
printChar(c, x, y - 1, _outlineColor);
|
||||
printChar(c, x, y + 1, _outlineColor);
|
||||
printChar(c, x - 1, y, _outlineColor);
|
||||
printChar(c, x + 1, y, _outlineColor);
|
||||
printChar(c, x - 1, y - 1, _outlineColor);
|
||||
printChar(c, x - 1, y + 1, _outlineColor);
|
||||
printChar(c, x + 1, y - 1, _outlineColor);
|
||||
printChar(c, x + 1, y + 1, _outlineColor);
|
||||
}
|
||||
printChar(c, x, y, _textColor);
|
||||
x += charWidth;
|
||||
linePos++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_textX = x;
|
||||
_textY = y;
|
||||
|
||||
}
|
||||
|
||||
void Screen::printTextEx(const char *text, int16 x, int16 y, int16 fontNum, int16 textColor, int16 outlineColor, const ClipInfo &clipInfo) {
|
||||
if (*text == 0 || x < 0 || y < 0)
|
||||
return;
|
||||
|
||||
int16 oldFontNum = _currentFontNum;
|
||||
Common::Rect oldTextRect;
|
||||
ClipInfo oldFontDrawCtx = _fontDrawCtx;
|
||||
|
||||
_fontDrawCtx = clipInfo;
|
||||
|
||||
getTextRect(oldTextRect);
|
||||
setFont(fontNum);
|
||||
setTextColor(textColor);
|
||||
setOutlineColor(outlineColor);
|
||||
setTextXY(x, y);
|
||||
printText(text);
|
||||
setTextRect(oldTextRect);
|
||||
setFont(oldFontNum);
|
||||
_fontDrawCtx = oldFontDrawCtx;
|
||||
|
||||
if (_vm->getGameID() != GID_RTZ && _vm->getGameID() != GID_LGOP2) {
|
||||
_vm->sayText(text);
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::printObjectText(int16 objectIndex, int16 x, int16 y, int16 fontNum, int16 textColor, int16 outlineColor, const ClipInfo &clipInfo) {
|
||||
|
||||
if (objectIndex == 0)
|
||||
return;
|
||||
|
||||
Object *obj = _vm->_dat->getObject(objectIndex);
|
||||
const char *text = obj->getString();
|
||||
|
||||
printTextEx(text, x, y, fontNum, textColor, outlineColor, clipInfo);
|
||||
|
||||
}
|
||||
|
||||
int16 Screen::getTextWidth(int16 fontNum, const char *text) {
|
||||
setFont(fontNum);
|
||||
return _font->getTextWidth(text);
|
||||
}
|
||||
|
||||
Graphics::Surface *Screen::lockScreen() {
|
||||
return _vm->_system->lockScreen();
|
||||
}
|
||||
|
||||
void Screen::unlockScreen() {
|
||||
_vm->_system->unlockScreen();
|
||||
}
|
||||
|
||||
void Screen::showWorkScreen() {
|
||||
_vm->_system->copyRectToScreen(_workScreen->getPixels(), _workScreen->pitch, 0, 0, _workScreen->w, _workScreen->h);
|
||||
}
|
||||
|
||||
void Screen::copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) {
|
||||
_vm->_system->copyRectToScreen(buf, pitch, x, y, w, h);
|
||||
}
|
||||
|
||||
void Screen::updateScreenAndWait(int delay) {
|
||||
_vm->_system->updateScreen();
|
||||
uint32 startTime = _vm->_system->getMillis();
|
||||
while (_vm->_system->getMillis() < startTime + delay) {
|
||||
_vm->handleEvents();
|
||||
_vm->_system->delayMillis(5);
|
||||
}
|
||||
}
|
||||
|
||||
int16 Screen::addToSpriteList(int16 index, int16 xofs, int16 yofs) {
|
||||
SpriteListItem item;
|
||||
item.index = index;
|
||||
item.xofs = xofs;
|
||||
item.yofs = yofs;
|
||||
_spriteList.push_back(item);
|
||||
return _spriteList.size();
|
||||
}
|
||||
|
||||
SpriteListItem Screen::getFromSpriteList(int16 index) {
|
||||
if (((uint) index) > _spriteList.size()) {
|
||||
SpriteListItem emptyItem;
|
||||
emptyItem.index = 0;
|
||||
emptyItem.xofs = 0;
|
||||
emptyItem.yofs = 0;
|
||||
return emptyItem;
|
||||
} else {
|
||||
return _spriteList[index - 1];
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::clearSpriteList() {
|
||||
_spriteList.clear();
|
||||
}
|
||||
|
||||
void Screen::setMouseCursor(const Graphics::Cursor *cursor) {
|
||||
CursorMan.replaceCursor(cursor, true);
|
||||
}
|
||||
|
||||
void Screen::setDefaultMouseCursor() {
|
||||
CursorMan.replaceCursor(defaultMouseCursor, 16, 16, 9, 2, 0);
|
||||
}
|
||||
|
||||
} // End of namespace Made
|
||||
238
engines/made/screen.h
Normal file
238
engines/made/screen.h
Normal file
@@ -0,0 +1,238 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MADE_SCREEN_H
|
||||
#define MADE_SCREEN_H
|
||||
|
||||
#include "made/resource.h"
|
||||
|
||||
#include "common/rect.h"
|
||||
|
||||
#include "graphics/cursor.h"
|
||||
|
||||
namespace Made {
|
||||
|
||||
struct SpriteChannel {
|
||||
int16 type;
|
||||
int16 state;
|
||||
uint16 index;
|
||||
int16 x, y;
|
||||
uint16 fontNum;
|
||||
int16 textColor, outlineColor;
|
||||
int16 frameNum;
|
||||
int16 mask;
|
||||
Common::String previousText;
|
||||
};
|
||||
|
||||
struct ClipInfo {
|
||||
Common::Rect clipRect;
|
||||
Graphics::Surface *destSurface;
|
||||
};
|
||||
|
||||
struct SpriteListItem {
|
||||
int16 index, xofs, yofs;
|
||||
};
|
||||
|
||||
class MadeEngine;
|
||||
class ScreenEffects;
|
||||
|
||||
static const byte defaultMouseCursor[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 15, 15, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 15, 15, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 15, 15, 1, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 15, 15, 1, 0, 0, 0, 0, 0,
|
||||
1, 1, 15, 1, 15, 1, 15, 1, 15, 15, 1, 0, 0, 0, 0, 0,
|
||||
1, 15, 15, 1, 15, 1, 15, 1, 15, 15, 1, 0, 0, 0, 0, 0,
|
||||
1, 15, 15, 15, 15, 15, 15, 15, 15, 15, 1, 0, 1, 1, 1, 0,
|
||||
1, 15, 15, 15, 15, 15, 15, 15, 15, 15, 1, 1, 15, 15, 15, 1,
|
||||
1, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 1, 1, 1,
|
||||
1, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 1, 1, 0, 0,
|
||||
1, 1, 15, 15, 15, 15, 15, 15, 15, 15, 15, 1, 1, 0, 0, 0,
|
||||
0, 1, 1, 15, 15, 15, 15, 15, 15, 15, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
class Screen {
|
||||
public:
|
||||
Screen(MadeEngine *vm);
|
||||
~Screen();
|
||||
|
||||
void clearScreen();
|
||||
|
||||
void drawSurface(Graphics::Surface *sourceSurface, int x, int y, int16 flipX, int16 flipY, int16 mask, const ClipInfo &clipInfo);
|
||||
|
||||
void setRGBPalette(byte *palRGB, int start = 0, int count = 256);
|
||||
bool isPaletteLocked() { return _paletteLock; }
|
||||
void setPaletteLock(bool lock) { _paletteLock = lock; }
|
||||
bool isScreenLocked() { return _screenLock; }
|
||||
void setScreenLock(bool lock) { _screenLock = lock; }
|
||||
void setVisualEffectNum(int visualEffectNum) { _visualEffectNum = visualEffectNum; }
|
||||
|
||||
void setClipArea(uint16 x1, uint16 y1, uint16 x2, uint16 y2) {
|
||||
_clipArea.clipRect = Common::Rect(x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
void setExcludeArea(uint16 x1, uint16 y1, uint16 x2, uint16 y2);
|
||||
|
||||
void setClip(int16 clip) { _clip = clip; }
|
||||
void setExclude(int16 exclude) { _exclude = exclude; }
|
||||
void setGround(int16 ground) { _ground = ground; }
|
||||
void setMask(int16 mask) { _mask = mask; }
|
||||
|
||||
void setTextColor(int16 color) { _textColor = color; }
|
||||
|
||||
void setTextRect(const Common::Rect &textRect) {
|
||||
_textRect = textRect;
|
||||
_textX = _textRect.left;
|
||||
_textY = _textRect.top;
|
||||
}
|
||||
|
||||
void getTextRect(Common::Rect &textRect) {
|
||||
textRect = _textRect;
|
||||
}
|
||||
|
||||
void setOutlineColor(int16 color) {
|
||||
_outlineColor = color;
|
||||
_dropShadowColor = -1;
|
||||
}
|
||||
|
||||
void setDropShadowColor(int16 color) {
|
||||
_outlineColor = -1;
|
||||
_dropShadowColor = color;
|
||||
}
|
||||
|
||||
void setTextXY(int16 x, int16 y) {
|
||||
_textX = x;
|
||||
_textY = y;
|
||||
}
|
||||
|
||||
void homeText() {
|
||||
_textX = _textRect.left;
|
||||
_textY = _textRect.top;
|
||||
}
|
||||
|
||||
void setQueueNextText(bool value) { _queueNextText = value; }
|
||||
void setVoiceTimeText(bool value) { _voiceTimeText = value; }
|
||||
|
||||
uint16 updateChannel(uint16 channelIndex);
|
||||
void deleteChannel(uint16 channelIndex);
|
||||
int16 getChannelType(uint16 channelIndex);
|
||||
int16 getChannelState(uint16 channelIndex);
|
||||
void setChannelState(uint16 channelIndex, int16 state);
|
||||
uint16 setChannelLocation(uint16 channelIndex, int16 x, int16 y);
|
||||
uint16 setChannelContent(uint16 channelIndex, uint16 index);
|
||||
void setChannelUseMask(uint16 channelIndex);
|
||||
void drawSpriteChannels(const ClipInfo &clipInfo, int16 includeStateMask, int16 excludeStateMask);
|
||||
void updateSprites();
|
||||
void clearChannels();
|
||||
|
||||
uint16 drawFlex(uint16 flexIndex, int16 x, int16 y, int16 flipX, int16 flipY, int16 mask, const ClipInfo &clipInfo);
|
||||
|
||||
void drawAnimFrame(uint16 animIndex, int16 x, int16 y, int16 frameNum, int16 flipX, int16 flipY, const ClipInfo &clipInfo);
|
||||
|
||||
uint16 drawPic(uint16 index, int16 x, int16 y, int16 flipX, int16 flipY);
|
||||
uint16 drawMask(uint16 index, int16 x, int16 y);
|
||||
|
||||
uint16 drawAnimPic(uint16 animIndex, int16 x, int16 y, int16 frameNum, int16 flipX, int16 flipY);
|
||||
|
||||
void addSprite(uint16 spriteIndex);
|
||||
|
||||
uint16 drawSprite(uint16 flexIndex, int16 x, int16 y);
|
||||
uint16 placeSprite(uint16 channelIndex, uint16 flexIndex, int16 x, int16 y);
|
||||
|
||||
uint16 placeAnim(uint16 channelIndex, uint16 animIndex, int16 x, int16 y, int16 frameNum);
|
||||
int16 setAnimFrame(uint16 channelIndex, int16 frameNum);
|
||||
int16 getAnimFrame(uint16 channelIndex);
|
||||
|
||||
uint16 placeText(uint16 channelIndex, uint16 textObjectIndex, int16 x, int16 y, uint16 fontNum, int16 textColor, int16 outlineColor);
|
||||
#ifdef USE_TTS
|
||||
void voiceChannelText(const char *text, uint16 channelIndex);
|
||||
#endif
|
||||
|
||||
void show();
|
||||
void flash(int count);
|
||||
|
||||
void setFont(int16 fontNum);
|
||||
void printChar(uint c, int16 x, int16 y, byte color);
|
||||
void printText(const char *text);
|
||||
void printTextEx(const char *text, int16 x, int16 y, int16 fontNum, int16 textColor, int16 outlineColor, const ClipInfo &clipInfo);
|
||||
void printObjectText(int16 objectIndex, int16 x, int16 y, int16 fontNum, int16 textColor, int16 outlineColor, const ClipInfo &clipInfo);
|
||||
int16 getTextWidth(int16 fontNum, const char *text);
|
||||
|
||||
// Interface functions for the screen effects class
|
||||
Graphics::Surface *lockScreen();
|
||||
void unlockScreen();
|
||||
void showWorkScreen();
|
||||
void copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h);
|
||||
void updateScreenAndWait(int delay);
|
||||
|
||||
int16 addToSpriteList(int16 index, int16 xofs, int16 yofs);
|
||||
SpriteListItem getFromSpriteList(int16 index);
|
||||
void clearSpriteList();
|
||||
|
||||
void setMouseCursor(const Graphics::Cursor *cursor);
|
||||
void setDefaultMouseCursor();
|
||||
|
||||
protected:
|
||||
MadeEngine *_vm;
|
||||
ScreenEffects *_fx;
|
||||
|
||||
bool _screenLock;
|
||||
bool _paletteLock;
|
||||
|
||||
byte *_palette, *_newPalette;
|
||||
int _paletteColorCount, _oldPaletteColorCount;
|
||||
bool _paletteInitialized, _needPalette;
|
||||
int16 _textColor;
|
||||
int16 _outlineColor;
|
||||
int16 _dropShadowColor;
|
||||
|
||||
int16 _textX, _textY;
|
||||
Common::Rect _textRect;
|
||||
int16 _currentFontNum;
|
||||
FontResource *_font;
|
||||
ClipInfo _fontDrawCtx;
|
||||
|
||||
int16 _clip, _exclude, _ground, _mask;
|
||||
int _visualEffectNum;
|
||||
|
||||
Graphics::Surface *_backgroundScreen, *_workScreen, *_screenMask;
|
||||
ClipInfo _clipArea, _backgroundScreenDrawCtx, _workScreenDrawCtx, _maskDrawCtx;
|
||||
|
||||
ClipInfo _excludeClipArea[4];
|
||||
bool _excludeClipAreaEnabled[4];
|
||||
|
||||
uint16 _channelsUsedCount;
|
||||
SpriteChannel _channels[100];
|
||||
|
||||
bool _queueNextText;
|
||||
bool _voiceTimeText;
|
||||
|
||||
Common::Array<SpriteListItem> _spriteList;
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Made
|
||||
|
||||
#endif /* MADE_H */
|
||||
543
engines/made/screenfx.cpp
Normal file
543
engines/made/screenfx.cpp
Normal file
@@ -0,0 +1,543 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "made/screenfx.h"
|
||||
#include "made/screen.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Made {
|
||||
|
||||
const byte ScreenEffects::vfxOffsTable[64] = {
|
||||
5, 2, 6, 1, 4, 7, 3, 0,
|
||||
7, 4, 0, 3, 6, 1, 5, 2,
|
||||
2, 5, 1, 6, 3, 0, 4, 7,
|
||||
0, 3, 7, 4, 1, 6, 2, 5,
|
||||
4, 0, 2, 5, 7, 3, 1, 6,
|
||||
1, 6, 4, 0, 2, 5, 7, 3,
|
||||
6, 1, 3, 7, 5, 2, 0, 4,
|
||||
3, 7, 5, 2, 0, 4, 6, 1
|
||||
};
|
||||
|
||||
const byte ScreenEffects::vfxOffsIndexTable[8] = {
|
||||
6, 7, 2, 3, 4, 5, 0, 1
|
||||
};
|
||||
|
||||
|
||||
ScreenEffects::ScreenEffects(Screen *screen) : _screen(screen) {
|
||||
vfxOffsTablePtr = &vfxOffsTable[6 * 8];
|
||||
vfxX1 = 0;
|
||||
vfxY1 = 0;
|
||||
vfxWidth = 0;
|
||||
vfxHeight = 0;
|
||||
|
||||
_fxPalette = new byte[768];
|
||||
|
||||
_blendedPaletteStatus._active = false;
|
||||
_blendedPaletteStatus._palette = _blendedPaletteStatus._newPalette = nullptr;
|
||||
_blendedPaletteStatus._colorCount = 0;
|
||||
_blendedPaletteStatus._value = _blendedPaletteStatus._maxValue = 0;
|
||||
_blendedPaletteStatus._incr = 0;
|
||||
}
|
||||
|
||||
ScreenEffects::~ScreenEffects() {
|
||||
delete[] _fxPalette;
|
||||
}
|
||||
|
||||
void ScreenEffects::run(int16 effectNum, Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount) {
|
||||
// TODO: Put effect functions into an array
|
||||
|
||||
switch (effectNum) {
|
||||
|
||||
case 0: // No effect
|
||||
vfx00(surface, palette, newPalette, colorCount);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
vfx01(surface, palette, newPalette, colorCount);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
vfx02(surface, palette, newPalette, colorCount);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
vfx03(surface, palette, newPalette, colorCount);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
vfx04(surface, palette, newPalette, colorCount);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
vfx05(surface, palette, newPalette, colorCount);
|
||||
break;
|
||||
|
||||
case 6: // "Curtain open" effect
|
||||
vfx06(surface, palette, newPalette, colorCount);
|
||||
break;
|
||||
|
||||
case 7: // "Curtain close" effect
|
||||
vfx07(surface, palette, newPalette, colorCount);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
vfx08(surface, palette, newPalette, colorCount);
|
||||
break;
|
||||
|
||||
case 9: // "Checkerboard" effect
|
||||
vfx09(surface, palette, newPalette, colorCount);
|
||||
break;
|
||||
|
||||
case 10: // "Screen wipe in", left to right
|
||||
vfx10(surface, palette, newPalette, colorCount);
|
||||
break;
|
||||
|
||||
case 11: // "Screen wipe in", right to left
|
||||
vfx11(surface, palette, newPalette, colorCount);
|
||||
break;
|
||||
|
||||
case 12: // "Screen wipe in", top to bottom
|
||||
vfx12(surface, palette, newPalette, colorCount);
|
||||
break;
|
||||
|
||||
case 13: // "Screen wipe in", bottom to top
|
||||
vfx13(surface, palette, newPalette, colorCount);
|
||||
break;
|
||||
|
||||
case 14: // "Screen open" effect
|
||||
vfx14(surface, palette, newPalette, colorCount);
|
||||
break;
|
||||
|
||||
case 15:
|
||||
vfx15(surface, palette, newPalette, colorCount);
|
||||
break;
|
||||
|
||||
case 16:
|
||||
vfx16(surface, palette, newPalette, colorCount);
|
||||
break;
|
||||
|
||||
case 17: // Palette fadeout/fadein
|
||||
vfx17(surface, palette, newPalette, colorCount);
|
||||
break;
|
||||
|
||||
case 18:
|
||||
vfx18(surface, palette, newPalette, colorCount);
|
||||
break;
|
||||
|
||||
case 19:
|
||||
vfx19(surface, palette, newPalette, colorCount);
|
||||
break;
|
||||
|
||||
case 20:
|
||||
vfx20(surface, palette, newPalette, colorCount);
|
||||
break;
|
||||
|
||||
default:
|
||||
vfx00(surface, palette, newPalette, colorCount);
|
||||
warning("Unimplemented visual effect %d", effectNum);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ScreenEffects::flash(int flashCount, byte *palette, int colorCount) {
|
||||
int palSize = colorCount * 3;
|
||||
if (flashCount < 1)
|
||||
flashCount = 1;
|
||||
for (int i = 0; i < palSize; i++)
|
||||
_fxPalette[i] = CLIP<byte>(255 - palette[i], 0, 255);
|
||||
while (flashCount--) {
|
||||
_screen->setRGBPalette(_fxPalette, 0, colorCount);
|
||||
_screen->updateScreenAndWait(20);
|
||||
_screen->setRGBPalette(palette, 0, colorCount);
|
||||
_screen->updateScreenAndWait(20);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenEffects::setPalette(byte *palette) {
|
||||
if (!_screen->isPaletteLocked()) {
|
||||
_screen->setRGBPalette(palette, 0, 256);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenEffects::setBlendedPalette(byte *palette, byte *newPalette, int colorCount, int16 value, int16 maxValue) {
|
||||
if (!_screen->isPaletteLocked()) {
|
||||
int32 mulValue = (value * 64) / maxValue;
|
||||
for (int i = 0; i < colorCount * 3; i++)
|
||||
_fxPalette[i] = CLIP<int32>(newPalette[i] - (newPalette[i] - palette[i]) * mulValue / 64, 0, 255);
|
||||
_screen->setRGBPalette(_fxPalette, 0, 256);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenEffects::startBlendedPalette(byte *palette, byte *newPalette, int colorCount, int16 maxValue) {
|
||||
_blendedPaletteStatus._palette = palette;
|
||||
_blendedPaletteStatus._newPalette = newPalette;
|
||||
_blendedPaletteStatus._colorCount = colorCount;
|
||||
_blendedPaletteStatus._maxValue = maxValue;
|
||||
_blendedPaletteStatus._incr = maxValue / 10; // ~10 palette updates
|
||||
_blendedPaletteStatus._value = 0;
|
||||
// Don't do anything if the two palettes are identical
|
||||
_blendedPaletteStatus._active = memcmp(palette, newPalette, colorCount * 3) != 0;
|
||||
}
|
||||
|
||||
void ScreenEffects::stepBlendedPalette() {
|
||||
if (_blendedPaletteStatus._active && _blendedPaletteStatus._value <= _blendedPaletteStatus._maxValue) {
|
||||
setBlendedPalette(_blendedPaletteStatus._palette, _blendedPaletteStatus._newPalette,
|
||||
_blendedPaletteStatus._colorCount, _blendedPaletteStatus._value, _blendedPaletteStatus._maxValue);
|
||||
if (_blendedPaletteStatus._value == _blendedPaletteStatus._maxValue)
|
||||
_blendedPaletteStatus._value++;
|
||||
else
|
||||
_blendedPaletteStatus._value = MIN<int16>(_blendedPaletteStatus._value + _blendedPaletteStatus._incr, _blendedPaletteStatus._maxValue);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenEffects::copyFxRect(Graphics::Surface *surface, int16 x1, int16 y1, int16 x2, int16 y2) {
|
||||
|
||||
// TODO: Clean up
|
||||
|
||||
byte *src, *dst;
|
||||
|
||||
x1 = CLIP<int16>(x1, 0, 320);
|
||||
y1 = CLIP<int16>(y1, 0, 200);
|
||||
x2 = CLIP<int16>(x2, 0, 320);
|
||||
y2 = CLIP<int16>(y2, 0, 200);
|
||||
|
||||
x2 -= x1;
|
||||
y2 -= y1;
|
||||
vfxX1 = x1 & 0x0E;
|
||||
x1 += 16;
|
||||
x1 = x1 & 0xFFF0;
|
||||
x2 += vfxX1;
|
||||
x2 -= 15;
|
||||
if (x2 < 0)
|
||||
x2 = 0;
|
||||
vfxWidth = x2 & 0x0E;
|
||||
x2 = x2 & 0xFFF0;
|
||||
|
||||
vfxY1 = y1 & 7;
|
||||
|
||||
byte *source = (byte *)surface->getBasePtr(x1, y1);
|
||||
|
||||
Graphics::Surface *vgaScreen = _screen->lockScreen();
|
||||
byte *dest = (byte *)vgaScreen->getBasePtr(x1, y1);
|
||||
|
||||
int16 addX = x2 / 16;
|
||||
|
||||
while (y2-- > 0) {
|
||||
|
||||
int16 addVal = vfxOffsTablePtr[vfxY1] * 2;
|
||||
int16 w = 0;
|
||||
vfxY1 = (vfxY1 + 1) & 7;
|
||||
|
||||
src = source + addVal;
|
||||
dst = dest + addVal;
|
||||
|
||||
if (addVal < vfxX1) {
|
||||
if (addVal < vfxWidth)
|
||||
w = 1;
|
||||
else
|
||||
w = 0;
|
||||
} else {
|
||||
src -= 16;
|
||||
dst -= 16;
|
||||
if (addVal < vfxWidth)
|
||||
w = 2;
|
||||
else
|
||||
w = 1;
|
||||
}
|
||||
|
||||
w += addX;
|
||||
|
||||
while (w-- > 0) {
|
||||
*dst++ = *src++;
|
||||
*dst++ = *src++;
|
||||
src += 14;
|
||||
dst += 14;
|
||||
}
|
||||
|
||||
source += 320;
|
||||
dest += 320;
|
||||
|
||||
}
|
||||
|
||||
vfxHeight = (vfxHeight + 1) & 7;
|
||||
vfxOffsTablePtr = &vfxOffsTable[vfxOffsIndexTable[vfxHeight] * 8];
|
||||
|
||||
_screen->unlockScreen();
|
||||
|
||||
}
|
||||
|
||||
// No effect
|
||||
void ScreenEffects::vfx00(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount) {
|
||||
setPalette(palette);
|
||||
_screen->showWorkScreen();
|
||||
// Workaround for The Manhole, else animations will be shown too fast
|
||||
_screen->updateScreenAndWait(100);
|
||||
}
|
||||
|
||||
void ScreenEffects::vfx01(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount) {
|
||||
startBlendedPalette(palette, newPalette, colorCount, 312);
|
||||
for (int x = 0; x < 320; x += 8) {
|
||||
_screen->copyRectToScreen(surface->getBasePtr(x, 0), surface->pitch, x, 0, 8, 200);
|
||||
stepBlendedPalette();
|
||||
_screen->updateScreenAndWait(25);
|
||||
}
|
||||
setPalette(palette);
|
||||
}
|
||||
|
||||
void ScreenEffects::vfx02(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount) {
|
||||
startBlendedPalette(palette, newPalette, colorCount, 312);
|
||||
for (int x = 312; x >= 0; x -= 8) {
|
||||
_screen->copyRectToScreen(surface->getBasePtr(x, 0), surface->pitch, x, 0, 8, 200);
|
||||
stepBlendedPalette();
|
||||
_screen->updateScreenAndWait(25);
|
||||
}
|
||||
setPalette(palette);
|
||||
}
|
||||
|
||||
void ScreenEffects::vfx03(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount) {
|
||||
startBlendedPalette(palette, newPalette, colorCount, 190);
|
||||
for (int y = 0; y < 200; y += 10) {
|
||||
_screen->copyRectToScreen(surface->getBasePtr(0, y), surface->pitch, 0, y, 320, 10);
|
||||
stepBlendedPalette();
|
||||
_screen->updateScreenAndWait(25);
|
||||
}
|
||||
setPalette(palette);
|
||||
}
|
||||
|
||||
void ScreenEffects::vfx04(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount) {
|
||||
startBlendedPalette(palette, newPalette, colorCount, 190);
|
||||
for (int y = 190; y >= 0; y -= 10) {
|
||||
_screen->copyRectToScreen(surface->getBasePtr(0, y), surface->pitch, 0, y, 320, 10);
|
||||
stepBlendedPalette();
|
||||
_screen->updateScreenAndWait(25);
|
||||
}
|
||||
setPalette(palette);
|
||||
}
|
||||
|
||||
void ScreenEffects::vfx05(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount) {
|
||||
startBlendedPalette(palette, newPalette, colorCount, 90);
|
||||
for (int y = 0; y < 100; y += 10) {
|
||||
_screen->copyRectToScreen(surface->getBasePtr(0, y + 100), surface->pitch, 0, y + 100, 320, 10);
|
||||
_screen->copyRectToScreen(surface->getBasePtr(0, 90 - y), surface->pitch, 0, 90 - y, 320, 10);
|
||||
stepBlendedPalette();
|
||||
_screen->updateScreenAndWait(25);
|
||||
}
|
||||
setPalette(palette);
|
||||
}
|
||||
|
||||
// "Curtain open" effect
|
||||
void ScreenEffects::vfx06(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount) {
|
||||
startBlendedPalette(palette, newPalette, colorCount, 152);
|
||||
for (int x = 0; x < 160; x += 8) {
|
||||
_screen->copyRectToScreen(surface->getBasePtr(x + 160, 0), surface->pitch, x + 160, 0, 8, 200);
|
||||
_screen->copyRectToScreen(surface->getBasePtr(152 - x, 0), surface->pitch, 152 - x, 0, 8, 200);
|
||||
stepBlendedPalette();
|
||||
_screen->updateScreenAndWait(25);
|
||||
}
|
||||
setPalette(palette);
|
||||
}
|
||||
|
||||
// "Curtain close" effect
|
||||
void ScreenEffects::vfx07(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount) {
|
||||
startBlendedPalette(palette, newPalette, colorCount, 152);
|
||||
for (int x = 152; x >= 0; x -= 8) {
|
||||
_screen->copyRectToScreen(surface->getBasePtr(x + 160, 0), surface->pitch, x + 160, 0, 8, 200);
|
||||
_screen->copyRectToScreen(surface->getBasePtr(152 - x, 0), surface->pitch, 152 - x, 0, 8, 200);
|
||||
stepBlendedPalette();
|
||||
_screen->updateScreenAndWait(25);
|
||||
}
|
||||
setPalette(palette);
|
||||
}
|
||||
|
||||
// "Screen slide in" right to left
|
||||
void ScreenEffects::vfx08(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount) {
|
||||
for (int x = 8; x <= 320; x += 8) {
|
||||
_screen->copyRectToScreen(surface->getPixels(), surface->pitch, 320 - x, 0, x, 200);
|
||||
_screen->updateScreenAndWait(25);
|
||||
}
|
||||
setPalette(palette);
|
||||
}
|
||||
|
||||
// "Checkerboard" effect
|
||||
void ScreenEffects::vfx09(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
copyFxRect(surface, 0, 0, 320, 200);
|
||||
// We set the final palette here, once
|
||||
setBlendedPalette(palette, newPalette, colorCount, i * 4 + 3, 32);
|
||||
// The original behavior follows - the end result is the same, though
|
||||
//for (int j = 0; j < 4; j++)
|
||||
// setBlendedPalette(palette, newPalette, colorCount, i * 4 + j, 32);
|
||||
_screen->updateScreenAndWait(25);
|
||||
}
|
||||
setPalette(palette);
|
||||
}
|
||||
|
||||
// "Screen wipe in", left to right
|
||||
void ScreenEffects::vfx10(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount) {
|
||||
startBlendedPalette(palette, newPalette, colorCount, 368);
|
||||
for (int x = -56; x < 312; x += 8) {
|
||||
copyFxRect(surface, x, 0, x + 64, 200);
|
||||
stepBlendedPalette();
|
||||
_screen->updateScreenAndWait(25);
|
||||
}
|
||||
setPalette(palette);
|
||||
}
|
||||
|
||||
// "Screen wipe in", right to left
|
||||
void ScreenEffects::vfx11(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount) {
|
||||
startBlendedPalette(palette, newPalette, colorCount, 368);
|
||||
for (int x = 312; x > -56; x -= 8) {
|
||||
copyFxRect(surface, x, 0, x + 64, 200);
|
||||
stepBlendedPalette();
|
||||
_screen->updateScreenAndWait(25);
|
||||
}
|
||||
setPalette(palette);
|
||||
}
|
||||
|
||||
// "Screen wipe in", top to bottom
|
||||
void ScreenEffects::vfx12(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount) {
|
||||
startBlendedPalette(palette, newPalette, colorCount, 260);
|
||||
for (int y = -70; y < 312; y += 10) {
|
||||
copyFxRect(surface, 0, y, 320, y + 80);
|
||||
stepBlendedPalette();
|
||||
_screen->updateScreenAndWait(25);
|
||||
}
|
||||
setPalette(palette);
|
||||
}
|
||||
|
||||
// "Screen wipe in", bottom to top
|
||||
void ScreenEffects::vfx13(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount) {
|
||||
startBlendedPalette(palette, newPalette, colorCount, 260);
|
||||
for (int y = 312; y > -70; y -= 10) {
|
||||
copyFxRect(surface, 0, y, 320, y + 80);
|
||||
stepBlendedPalette();
|
||||
_screen->updateScreenAndWait(25);
|
||||
}
|
||||
setPalette(palette);
|
||||
}
|
||||
|
||||
// "Screen open" effect
|
||||
void ScreenEffects::vfx14(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount) {
|
||||
int16 x = 8, y = 5;
|
||||
startBlendedPalette(palette, newPalette, colorCount, 27);
|
||||
for (int i = 0; i < 27; i++) {
|
||||
copyFxRect(surface, 160 - x, 100 - y, 160 + x, 100 + y);
|
||||
x += 8;
|
||||
y += 5;
|
||||
stepBlendedPalette();
|
||||
_screen->updateScreenAndWait(25);
|
||||
}
|
||||
setPalette(palette);
|
||||
}
|
||||
|
||||
void ScreenEffects::vfx15(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount) {
|
||||
int16 x = 8;
|
||||
startBlendedPalette(palette, newPalette, colorCount, 27);
|
||||
for (int i = 0; i < 27; i++) {
|
||||
copyFxRect(surface, 160 - x, 0, 160 + x, 200);
|
||||
x += 8;
|
||||
stepBlendedPalette();
|
||||
_screen->updateScreenAndWait(25);
|
||||
}
|
||||
setPalette(palette);
|
||||
}
|
||||
|
||||
void ScreenEffects::vfx16(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount) {
|
||||
int16 y = 8;
|
||||
startBlendedPalette(palette, newPalette, colorCount, 27);
|
||||
for (int i = 0; i < 27; i++) {
|
||||
copyFxRect(surface, 0, 100 - y, 320, 100 + y);
|
||||
y += 5;
|
||||
stepBlendedPalette();
|
||||
_screen->updateScreenAndWait(25);
|
||||
}
|
||||
setPalette(palette);
|
||||
}
|
||||
|
||||
// Palette fadeout/fadein
|
||||
void ScreenEffects::vfx17(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount) {
|
||||
|
||||
byte tempPalette[768];
|
||||
|
||||
bool savedPaletteLock = _screen->isPaletteLocked();
|
||||
_screen->setPaletteLock(false);
|
||||
|
||||
memcpy(tempPalette, palette, 768);
|
||||
|
||||
// Fade out to black
|
||||
memset(palette, 0, 768);
|
||||
startBlendedPalette(palette, newPalette, colorCount, 50);
|
||||
for (int i = 0; i < 50; i++) {
|
||||
stepBlendedPalette();
|
||||
_screen->updateScreenAndWait(25);
|
||||
}
|
||||
_screen->setRGBPalette(palette, 0, colorCount);
|
||||
|
||||
memcpy(palette, tempPalette, 768);
|
||||
|
||||
_screen->showWorkScreen();
|
||||
|
||||
// Fade from black to palette
|
||||
memset(newPalette, 0, 768);
|
||||
startBlendedPalette(palette, newPalette, colorCount, 50);
|
||||
for (int i = 0; i < 50; i++) {
|
||||
stepBlendedPalette();
|
||||
_screen->updateScreenAndWait(25);
|
||||
}
|
||||
_screen->setRGBPalette(palette, 0, colorCount);
|
||||
|
||||
_screen->setPaletteLock(savedPaletteLock);
|
||||
|
||||
}
|
||||
|
||||
// "Screen slide in" left to right
|
||||
void ScreenEffects::vfx18(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount) {
|
||||
for (int x = 8; x <= 320; x += 8) {
|
||||
_screen->copyRectToScreen(surface->getBasePtr(320 - x, 0), surface->pitch, 0, 0, x, 200);
|
||||
_screen->updateScreenAndWait(25);
|
||||
}
|
||||
|
||||
setPalette(palette);
|
||||
}
|
||||
|
||||
// "Screen slide in" top to bottom
|
||||
void ScreenEffects::vfx19(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount) {
|
||||
for (int y = 4; y <= 200; y += 4) {
|
||||
_screen->copyRectToScreen(surface->getBasePtr(0, 200 - y), surface->pitch, 0, 0, 320, y);
|
||||
_screen->updateScreenAndWait(25);
|
||||
}
|
||||
|
||||
setPalette(palette);
|
||||
}
|
||||
|
||||
// "Screen slide in" bottom to top
|
||||
void ScreenEffects::vfx20(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount) {
|
||||
for (int y = 4; y <= 200; y += 4) {
|
||||
_screen->copyRectToScreen(surface->getPixels(), surface->pitch, 0, 200 - y, 320, y);
|
||||
_screen->updateScreenAndWait(25);
|
||||
}
|
||||
|
||||
setPalette(palette);
|
||||
}
|
||||
|
||||
} // End of namespace Made
|
||||
88
engines/made/screenfx.h
Normal file
88
engines/made/screenfx.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MADE_SCREENFX_H
|
||||
#define MADE_SCREENFX_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace Made {
|
||||
|
||||
class Screen;
|
||||
|
||||
struct BlendedPaletteStatus {
|
||||
bool _active;
|
||||
byte *_palette, *_newPalette;
|
||||
int _colorCount;
|
||||
int16 _value, _maxValue, _incr;
|
||||
};
|
||||
|
||||
class ScreenEffects {
|
||||
public:
|
||||
ScreenEffects(Screen *screen);
|
||||
~ScreenEffects();
|
||||
void run(int16 effectNum, Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount);
|
||||
void flash(int count, byte *palette, int colorCount);
|
||||
private:
|
||||
Screen *_screen;
|
||||
byte *_fxPalette;
|
||||
static const byte vfxOffsTable[64];
|
||||
static const byte vfxOffsIndexTable[8];
|
||||
const byte *vfxOffsTablePtr;
|
||||
int16 vfxX1, vfxY1, vfxWidth, vfxHeight;
|
||||
BlendedPaletteStatus _blendedPaletteStatus;
|
||||
|
||||
void setPalette(byte *palette);
|
||||
void setBlendedPalette(byte *palette, byte *newPalette, int colorCount, int16 value, int16 maxValue);
|
||||
void startBlendedPalette(byte *palette, byte *newPalette, int colorCount, int16 maxValue);
|
||||
void stepBlendedPalette();
|
||||
void copyFxRect(Graphics::Surface *surface, int16 x1, int16 y1, int16 x2, int16 y2);
|
||||
|
||||
void vfx00(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount);
|
||||
void vfx01(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount);
|
||||
void vfx02(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount);
|
||||
void vfx03(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount);
|
||||
void vfx04(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount);
|
||||
void vfx05(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount);
|
||||
void vfx06(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount);
|
||||
void vfx07(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount);
|
||||
void vfx08(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount);
|
||||
void vfx09(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount);
|
||||
void vfx10(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount);
|
||||
void vfx11(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount);
|
||||
void vfx12(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount);
|
||||
void vfx13(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount);
|
||||
void vfx14(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount);
|
||||
void vfx15(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount);
|
||||
void vfx16(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount);
|
||||
void vfx17(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount);
|
||||
void vfx18(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount);
|
||||
void vfx19(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount);
|
||||
void vfx20(Graphics::Surface *surface, byte *palette, byte *newPalette, int colorCount);
|
||||
};
|
||||
|
||||
} // End of namespace Made
|
||||
|
||||
#endif /* MADE_H */
|
||||
749
engines/made/script.cpp
Normal file
749
engines/made/script.cpp
Normal file
@@ -0,0 +1,749 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "made/script.h"
|
||||
#include "made/scriptfuncs.h"
|
||||
#include "made/made.h"
|
||||
#include "made/database.h"
|
||||
#include "made/screen.h"
|
||||
|
||||
#include "common/util.h"
|
||||
|
||||
namespace Made {
|
||||
|
||||
/* ScriptInterpreter */
|
||||
|
||||
|
||||
ScriptInterpreter::ScriptInterpreter(MadeEngine *vm) : _vm(vm) {
|
||||
#ifdef DUMP_SCRIPTS
|
||||
#define COMMAND(x, sig) { &ScriptInterpreter::x, #x, sig }
|
||||
#else
|
||||
#define COMMAND(x, sig) { &ScriptInterpreter::x, #x}
|
||||
#endif
|
||||
static CommandEntry commandProcs[] = {
|
||||
/* 01 */
|
||||
COMMAND(cmd_branchTrue, "W"),
|
||||
COMMAND(cmd_branchFalse, "W"),
|
||||
COMMAND(cmd_branch, "W"),
|
||||
COMMAND(cmd_true, ""),
|
||||
/* 05 */
|
||||
COMMAND(cmd_false, ""),
|
||||
COMMAND(cmd_push, ""),
|
||||
COMMAND(cmd_not, ""),
|
||||
COMMAND(cmd_add, ""),
|
||||
/* 09 */
|
||||
COMMAND(cmd_sub, ""),
|
||||
COMMAND(cmd_mul, ""),
|
||||
COMMAND(cmd_div, ""),
|
||||
COMMAND(cmd_mod, ""),
|
||||
/* 13 */
|
||||
COMMAND(cmd_band, ""),
|
||||
COMMAND(cmd_bor, ""),
|
||||
COMMAND(cmd_bnot, ""),
|
||||
COMMAND(cmd_lt, ""),
|
||||
/* 17 */
|
||||
COMMAND(cmd_eq, ""),
|
||||
COMMAND(cmd_gt, ""),
|
||||
COMMAND(cmd_loadConstant, "w"),
|
||||
COMMAND(cmd_loadVariable, "w"),
|
||||
/* 21 */
|
||||
COMMAND(cmd_getObjectProperty, ""),
|
||||
COMMAND(cmd_setObjectProperty, ""),
|
||||
COMMAND(cmd_set, "w"),
|
||||
COMMAND(cmd_print, ""),
|
||||
/* 25 */
|
||||
COMMAND(cmd_terpri, ""),
|
||||
COMMAND(cmd_printNumber, ""),
|
||||
COMMAND(cmd_vref, ""),
|
||||
COMMAND(cmd_vset, ""),
|
||||
/* 29 */
|
||||
COMMAND(cmd_vsize, ""),
|
||||
COMMAND(cmd_exit, ""),
|
||||
COMMAND(cmd_return, ""),
|
||||
COMMAND(cmd_call, "b"),
|
||||
/* 33 */
|
||||
COMMAND(cmd_svar, ""),
|
||||
COMMAND(cmd_sset, ""),
|
||||
COMMAND(cmd_split, ""),
|
||||
COMMAND(cmd_snlit, ""),
|
||||
/* 37 */
|
||||
COMMAND(cmd_yorn, ""),
|
||||
COMMAND(cmd_save, ""),
|
||||
COMMAND(cmd_restore, ""),
|
||||
COMMAND(cmd_arg, "b"),
|
||||
/* 41 */
|
||||
COMMAND(cmd_aset, "b"),
|
||||
COMMAND(cmd_tmp, "b"),
|
||||
COMMAND(cmd_tset, "b"),
|
||||
COMMAND(cmd_tspace, "b"),
|
||||
/* 45 */
|
||||
COMMAND(cmd_class, ""),
|
||||
COMMAND(cmd_objectp, ""),
|
||||
COMMAND(cmd_vectorp, ""),
|
||||
COMMAND(cmd_restart, ""),
|
||||
/* 49 */
|
||||
COMMAND(cmd_rand, ""),
|
||||
COMMAND(cmd_randomize, ""),
|
||||
COMMAND(cmd_send, "b"),
|
||||
COMMAND(cmd_extend, "Eb"),
|
||||
/* 53 */
|
||||
COMMAND(cmd_catch, ""),
|
||||
COMMAND(cmd_cdone, ""),
|
||||
COMMAND(cmd_throw, ""),
|
||||
COMMAND(cmd_functionp, ""),
|
||||
/* 57 */
|
||||
COMMAND(cmd_le, ""),
|
||||
COMMAND(cmd_ge, ""),
|
||||
COMMAND(cmd_varx, ""),
|
||||
COMMAND(cmd_setx, "")
|
||||
};
|
||||
_commands = commandProcs;
|
||||
_commandsMax = ARRAYSIZE(commandProcs) + 1;
|
||||
|
||||
_functions = new ScriptFunctions(_vm);
|
||||
_functions->setupExternalsTable();
|
||||
|
||||
_localStackPos = 0;
|
||||
_runningScriptObjectIndex = 0;
|
||||
_codeBase = nullptr;
|
||||
_codeIp = nullptr;
|
||||
|
||||
#undef COMMAND
|
||||
}
|
||||
|
||||
ScriptInterpreter::~ScriptInterpreter() {
|
||||
delete _functions;
|
||||
}
|
||||
|
||||
void ScriptInterpreter::runScript(int16 scriptObjectIndex) {
|
||||
|
||||
uint32 opcodeSleepCounter = 0;
|
||||
|
||||
_runningScriptObjectIndex = scriptObjectIndex;
|
||||
|
||||
_localStackPos = _stack.getStackPos();
|
||||
|
||||
_codeBase = _vm->_dat->getObject(_runningScriptObjectIndex)->getData();
|
||||
_codeIp = _codeBase;
|
||||
|
||||
while (!_vm->shouldQuit()) {
|
||||
byte opcode = readByte();
|
||||
|
||||
if (opcode >= 1 && opcode <= _commandsMax) {
|
||||
debug(4, "[%04X:%04X] %s", _runningScriptObjectIndex, (uint) (_codeIp - _codeBase), _commands[opcode - 1].desc);
|
||||
(this->*_commands[opcode - 1].proc)();
|
||||
} else {
|
||||
warning("ScriptInterpreter::runScript(%d) Unknown opcode %02X", _runningScriptObjectIndex, opcode);
|
||||
}
|
||||
|
||||
/* We sleep a little after 500 opcodes to reduce the CPU load.
|
||||
*/
|
||||
if (++opcodeSleepCounter > 500) {
|
||||
_vm->_screen->updateScreenAndWait(5);
|
||||
opcodeSleepCounter = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
byte ScriptInterpreter::readByte() {
|
||||
return *_codeIp++;
|
||||
}
|
||||
|
||||
int16 ScriptInterpreter::readInt16() {
|
||||
int16 temp = (int16)READ_LE_UINT16(_codeIp);
|
||||
_codeIp += 2;
|
||||
debug(4, "readInt16() value = %04X", temp);
|
||||
return temp;
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_branchTrue() {
|
||||
int16 ofs = readInt16();
|
||||
if (_stack.top() != 0)
|
||||
_codeIp = _codeBase + ofs;
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_branchFalse() {
|
||||
int16 ofs = readInt16();
|
||||
if (_stack.top() == 0)
|
||||
_codeIp = _codeBase + ofs;
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_branch() {
|
||||
int16 ofs = readInt16();
|
||||
_codeIp = _codeBase + ofs;
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_true() {
|
||||
_stack.setTop(-1);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_false() {
|
||||
_stack.setTop(0);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_push() {
|
||||
_stack.push();
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_not() {
|
||||
if (_stack.top() == 0)
|
||||
_stack.setTop(-1);
|
||||
else
|
||||
_stack.setTop(0);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_add() {
|
||||
int16 value = _stack.pop();
|
||||
_stack.setTop(_stack.top() + value);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_sub() {
|
||||
int16 value = _stack.pop();
|
||||
_stack.setTop(_stack.top() - value);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_mul() {
|
||||
int16 value = _stack.pop();
|
||||
_stack.setTop(_stack.top() * value);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_div() {
|
||||
int16 value = _stack.pop();
|
||||
if (value == 0)
|
||||
_stack.setTop(0);
|
||||
else
|
||||
_stack.setTop(_stack.top() / value);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_mod() {
|
||||
int16 value = _stack.pop();
|
||||
if (value == 0)
|
||||
_stack.setTop(0);
|
||||
else
|
||||
_stack.setTop(_stack.top() % value);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_band() {
|
||||
int16 value = _stack.pop();
|
||||
_stack.setTop(_stack.top() & value);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_bor() {
|
||||
int16 value = _stack.pop();
|
||||
_stack.setTop(_stack.top() | value);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_bnot() {
|
||||
_stack.setTop(~_stack.top());
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_lt() {
|
||||
int16 value = _stack.pop();
|
||||
if (_stack.top() < value)
|
||||
_stack.setTop(-1);
|
||||
else
|
||||
_stack.setTop(0);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_eq() {
|
||||
int16 value = _stack.pop();
|
||||
if (_stack.top() == value)
|
||||
_stack.setTop(-1);
|
||||
else
|
||||
_stack.setTop(0);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_gt() {
|
||||
int16 value = _stack.pop();
|
||||
if (_stack.top() > value)
|
||||
_stack.setTop(-1);
|
||||
else
|
||||
_stack.setTop(0);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_loadConstant() {
|
||||
int16 value = readInt16();
|
||||
debug(4, "value = %04X (%d)", value, value);
|
||||
_stack.setTop(value);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_loadVariable() {
|
||||
int16 variable = readInt16();
|
||||
int16 value = _vm->_dat->getVar(variable);
|
||||
debug(4, "variable = %d; value = %d (%04X)", variable, value, value);
|
||||
_stack.setTop(value);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_getObjectProperty() {
|
||||
int16 propertyId = _stack.pop();
|
||||
int16 objectIndex = _stack.top();
|
||||
int16 value = _vm->_dat->getObjectProperty(objectIndex, propertyId);
|
||||
debug(4, "value = %04X(%d)", value, value);
|
||||
_stack.setTop(value);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_setObjectProperty() {
|
||||
int16 value = _stack.pop();
|
||||
int16 propertyId = _stack.pop();
|
||||
int16 objectIndex = _stack.top();
|
||||
value = _vm->_dat->setObjectProperty(objectIndex, propertyId, value);
|
||||
_stack.setTop(value);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_set() {
|
||||
int16 variable = readInt16();
|
||||
debug(4, "var(%d) = %04d (%d)", variable, _stack.top(), _stack.top());
|
||||
_vm->_dat->setVar(variable, _stack.top());
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_print() {
|
||||
// TODO: This opcode was used for printing debug messages
|
||||
const char *text = _vm->_dat->getObjectString(_stack.top());
|
||||
debug(4, "%s", text);
|
||||
_stack.setTop(0);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_terpri() {
|
||||
// TODO: This opcode was used for printing debug messages
|
||||
debug(4, "\n");
|
||||
_stack.setTop(0);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_printNumber() {
|
||||
// TODO: This opcode was used for printing debug messages
|
||||
debug(4, "%d", _stack.top());
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_vref() {
|
||||
int16 value = 0;
|
||||
int16 index = _stack.pop();
|
||||
int16 objectIndex = _stack.top();
|
||||
debug(4, "index = %d; objectIndex = %d", index, objectIndex);
|
||||
if (objectIndex > 0) {
|
||||
Object *obj = _vm->_dat->getObject(objectIndex);
|
||||
value = obj->getVectorItem(index);
|
||||
}
|
||||
_stack.setTop(value);
|
||||
debug(4, "--> value = %d", value);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_vset() {
|
||||
int16 value = _stack.pop();
|
||||
int16 index = _stack.pop();
|
||||
int16 objectIndex = _stack.top();
|
||||
debug(4, "index = %d; objectIndex = %d; value = %d", index, objectIndex, value);
|
||||
if (objectIndex > 0) {
|
||||
Object *obj = _vm->_dat->getObject(objectIndex);
|
||||
obj->setVectorItem(index, value);
|
||||
}
|
||||
_stack.setTop(value);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_vsize() {
|
||||
int16 objectIndex = _stack.top();
|
||||
int16 size = 0;
|
||||
if (objectIndex > 0) {
|
||||
Object *obj = _vm->_dat->getObject(objectIndex);
|
||||
size = obj->getVectorSize();
|
||||
}
|
||||
_stack.setTop(size);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_exit() {
|
||||
_vm->quitGame();
|
||||
// Make sure the "quit" event is handled immediately
|
||||
_vm->handleEvents();
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_return() {
|
||||
|
||||
// Check if returning from main function
|
||||
if (_localStackPos == kScriptStackSize) {
|
||||
_vm->quitGame();
|
||||
// Make sure the "quit" event is handled immediately
|
||||
_vm->handleEvents();
|
||||
return;
|
||||
}
|
||||
|
||||
int16 funcResult = _stack.top();
|
||||
_stack.setStackPos(_localStackPos);
|
||||
_localStackPos = kScriptStackLimit - _stack.pop();
|
||||
_runningScriptObjectIndex = _stack.pop();
|
||||
_codeBase = _vm->_dat->getObject(_runningScriptObjectIndex)->getData();
|
||||
_codeIp = _codeBase + _stack.pop();
|
||||
byte argc = _stack.pop();
|
||||
_stack.free(argc);
|
||||
_stack.setTop(funcResult);
|
||||
debug(4, "LEAVE: stackPtr = %d; _localStackPos = %d\n", _stack.getStackPos(), _localStackPos);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_call() {
|
||||
debug(4, "\nENTER: stackPtr = %d; _localStackPos = %d", _stack.getStackPos(), _localStackPos);
|
||||
byte argc = readByte();
|
||||
|
||||
_stack.push(argc);
|
||||
_stack.push(_codeIp - _codeBase);
|
||||
_stack.push(_runningScriptObjectIndex);
|
||||
_stack.push(kScriptStackLimit - _localStackPos);
|
||||
_localStackPos = _stack.getStackPos();
|
||||
_runningScriptObjectIndex = _stack.peek(_localStackPos + argc + 4);
|
||||
debug(4, "argc = %d; _runningScriptObjectIndex = %04X", argc, _runningScriptObjectIndex);
|
||||
_codeBase = _vm->_dat->getObject(_runningScriptObjectIndex)->getData();
|
||||
_codeIp = _codeBase;
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_svar() {
|
||||
// Never used in LGOP2, RTZ, Manhole:NE, Rodney
|
||||
warning("Unimplemented command: cmd_svar");
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_sset() {
|
||||
// Never used in LGOP2, RTZ, Manhole:NE, Rodney
|
||||
warning("Unimplemented command: cmd_sset");
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_split() {
|
||||
// Never used in LGOP2, RTZ, Manhole:NE, Rodney
|
||||
warning("Unimplemented command: cmd_split");
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_snlit() {
|
||||
// Never used in LGOP2, RTZ, Manhole:NE, Rodney
|
||||
warning("Unimplemented command: cmd_snlit");
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_yorn() {
|
||||
// Never used in LGOP2, RTZ, Manhole:NE, Rodney
|
||||
warning("Unimplemented command: cmd_yorn");
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_save() {
|
||||
int16 result = 0;
|
||||
int16 stringOfs = _stack.top();
|
||||
const char *filename = _vm->_dat->getString(stringOfs);
|
||||
result = _vm->_dat->savegame(filename, "", 0);
|
||||
_stack.setTop(result);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_restore() {
|
||||
int16 result = 0;
|
||||
int16 stringOfs = _stack.top();
|
||||
const char *filename = _vm->_dat->getString(stringOfs);
|
||||
result = _vm->_dat->loadgame(filename, 0);
|
||||
_stack.setTop(result);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_arg() {
|
||||
int16 argIndex = readByte();
|
||||
debug(4, "argIndex = %d; value = %04X (%d)", argIndex, _stack.peek(_localStackPos + 4 + argIndex), _stack.peek(_localStackPos + 4 + argIndex));
|
||||
_stack.setTop(_stack.peek(_localStackPos + 4 + argIndex));
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_aset() {
|
||||
int16 argIndex = readByte();
|
||||
debug(4, "argIndex = %d; value = %d", argIndex, _stack.peek(_localStackPos + 4 + argIndex));
|
||||
_stack.poke(_localStackPos + 4 + argIndex, _stack.top());
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_tmp() {
|
||||
int16 tempIndex = readByte();
|
||||
debug(4, "tempIndex = %d; value = %d", tempIndex, _stack.peek(_localStackPos - tempIndex - 1));
|
||||
_stack.setTop(_stack.peek(_localStackPos - tempIndex - 1));
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_tset() {
|
||||
int16 tempIndex = readByte();
|
||||
debug(4, "tempIndex = %d; value = %d", tempIndex, _stack.top());
|
||||
_stack.poke(_localStackPos - tempIndex - 1, _stack.top());
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_tspace() {
|
||||
int16 tempCount = readByte();
|
||||
debug(4, "tempCount = %d", tempCount);
|
||||
_stack.alloc(tempCount);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_class() {
|
||||
// Never used in LGOP2, RTZ, Manhole:NE, Rodney
|
||||
warning("Unimplemented command: cmd_class");
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_objectp() {
|
||||
Object *obj = _vm->_dat->getObject(_stack.top());
|
||||
if (obj->isObject())
|
||||
_stack.setTop(-1);
|
||||
else
|
||||
_stack.setTop(0);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_vectorp() {
|
||||
// Never used in LGOP2, RTZ, Manhole:NE, Rodney
|
||||
warning("Unimplemented command: cmd_vectorp");
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_restart() {
|
||||
_vm->_dat->reload();
|
||||
_vm->_screen->clearChannels();
|
||||
_vm->resetAllTimers();
|
||||
_stack.setTop(0);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_rand() {
|
||||
_stack.setTop(_vm->_rnd->getRandomNumber(_stack.top() - 1));
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_randomize() {
|
||||
_vm->_rnd->setSeed(Common::RandomSource::generateNewSeed());
|
||||
_stack.setTop(0);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_send() {
|
||||
|
||||
debug(4, "\nENTER: stackPtr = %d; _localStackPos = %d", _stack.getStackPos(), _localStackPos);
|
||||
|
||||
byte argc = readByte();
|
||||
|
||||
debug(4, "argc = %d", argc);
|
||||
|
||||
_stack.push(argc);
|
||||
_stack.push(_codeIp - _codeBase);
|
||||
_stack.push(_runningScriptObjectIndex);
|
||||
_stack.push(kScriptStackLimit - _localStackPos);
|
||||
_localStackPos = _stack.getStackPos();
|
||||
|
||||
int16 propertyId = _stack.peek(_localStackPos + argc + 2);
|
||||
int16 objectIndex = _stack.peek(_localStackPos + argc + 4);
|
||||
|
||||
debug(4, "objectIndex = %d (%04X); propertyId = %d(%04X)", objectIndex, objectIndex, propertyId, propertyId);
|
||||
|
||||
if (objectIndex != 0) {
|
||||
objectIndex = _vm->_dat->getObject(objectIndex)->getClass();
|
||||
} else {
|
||||
objectIndex = _stack.peek(_localStackPos + argc + 3);
|
||||
}
|
||||
|
||||
debug(4, "--> objectIndex = %d(%04X)", objectIndex, objectIndex);
|
||||
|
||||
if (objectIndex != 0) {
|
||||
_runningScriptObjectIndex = _vm->_dat->getObjectProperty(objectIndex, propertyId);
|
||||
if (_runningScriptObjectIndex != 0) {
|
||||
_codeBase = _vm->_dat->getObject(_runningScriptObjectIndex)->getData();
|
||||
_codeIp = _codeBase;
|
||||
} else {
|
||||
_stack.push(0);
|
||||
cmd_return();
|
||||
}
|
||||
} else {
|
||||
_stack.push(0);
|
||||
cmd_return();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_extend() {
|
||||
|
||||
byte func = readByte();
|
||||
|
||||
byte argc = readByte();
|
||||
int16 *argv = _stack.getStackPtr();
|
||||
|
||||
debug(4, "func = %d (%s); argc = %d", func, _functions->getFuncName(func), argc);
|
||||
for (int i = 0; i < argc; i++)
|
||||
debug(2, "argv[%02d] = %04X (%d)", i, argv[i], argv[i]);
|
||||
|
||||
int16 result = _functions->callFunction(func, argc, argv);
|
||||
debug(2, "result = %04X (%d)", result, result);
|
||||
|
||||
_stack.free(argc);
|
||||
|
||||
_stack.setTop(result);
|
||||
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_catch() {
|
||||
// Never used in LGOP2, RTZ, Manhole:NE, Rodney
|
||||
warning("Unimplemented command: cmd_catch");
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_cdone() {
|
||||
// Never used in LGOP2, RTZ, Manhole:NE, Rodney
|
||||
warning("Unimplemented command: cmd_cdone");
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_throw() {
|
||||
// Never used in LGOP2, RTZ, Manhole:NE, Rodney
|
||||
warning("Unimplemented command: cmd_throw");
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_functionp() {
|
||||
// Never used in LGOP2, RTZ, Manhole:NE, Rodney
|
||||
warning("Unimplemented command: cmd_functionp");
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_le() {
|
||||
int16 value = _stack.pop();
|
||||
if (_stack.top() <= value)
|
||||
_stack.setTop(-1);
|
||||
else
|
||||
_stack.setTop(0);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_ge() {
|
||||
int16 value = _stack.pop();
|
||||
if (_stack.top() >= value)
|
||||
_stack.setTop(-1);
|
||||
else
|
||||
_stack.setTop(0);
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_varx() {
|
||||
// Never used in LGOP2, RTZ, Manhole:NE, Rodney
|
||||
warning("Unimplemented command: cmd_varx");
|
||||
}
|
||||
|
||||
void ScriptInterpreter::cmd_setx() {
|
||||
// Never used in LGOP2, RTZ, Manhole:NE, Rodney
|
||||
warning("Unimplemented command: cmd_setx");
|
||||
}
|
||||
|
||||
#ifdef DUMP_SCRIPTS
|
||||
void ScriptInterpreter::dumpScript(int16 objectIndex, int *opcodeStats, int *externStats) {
|
||||
|
||||
debug(1, "Dumping code for object %04X", objectIndex);
|
||||
|
||||
Object *obj = _vm->_dat->getObject(objectIndex);
|
||||
byte *code = obj->getData(), *codeStart = code, *codeEnd = code + obj->getSize();
|
||||
|
||||
while (code < codeEnd) {
|
||||
byte opcode = *code++;
|
||||
if (opcode >= 1 && opcode <= _commandsMax) {
|
||||
Common::String codeLine;
|
||||
const char *desc = _commands[opcode - 1].desc;
|
||||
const char *sig = _commands[opcode - 1].sig;
|
||||
int valueType; /* 0: dec; 1: hex; 2: extended function */
|
||||
int16 value;
|
||||
opcodeStats[opcode - 1]++;
|
||||
|
||||
codeLine += Common::String::format("[%04X] ", (uint16)(code - codeStart - 1));
|
||||
codeLine += desc;
|
||||
for (; *sig != '\0'; sig++) {
|
||||
codeLine += " ";
|
||||
switch (*sig) {
|
||||
case 'b':
|
||||
valueType = 0;
|
||||
value = *code++;
|
||||
break;
|
||||
case 'B':
|
||||
valueType = 1;
|
||||
value = *code++;
|
||||
break;
|
||||
case 'w':
|
||||
valueType = 0;
|
||||
value = READ_LE_UINT16(code);
|
||||
code += 2;
|
||||
break;
|
||||
case 'W':
|
||||
valueType = 1;
|
||||
value = READ_LE_UINT16(code);
|
||||
code += 2;
|
||||
break;
|
||||
case 'E':
|
||||
valueType = 2;
|
||||
value = *code++;
|
||||
break;
|
||||
}
|
||||
|
||||
Common::String tempStr;
|
||||
switch (valueType) {
|
||||
case 0:
|
||||
tempStr = Common::String::format("%d", value);
|
||||
break;
|
||||
case 1:
|
||||
tempStr = Common::String::format("0x%X", value);
|
||||
break;
|
||||
case 2:
|
||||
if (value < _functions->getCount()) {
|
||||
tempStr = Common::String::format("%s", _functions->getFuncName(value));
|
||||
externStats[value]++;
|
||||
} else {
|
||||
tempStr = Common::String::format("invalid: %d", value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
codeLine += tempStr;
|
||||
}
|
||||
debug(1, "%s", codeLine.c_str());
|
||||
} else {
|
||||
error("ScriptInterpreter::dumpScript(%d) Unknown opcode %02X", objectIndex, opcode);
|
||||
}
|
||||
}
|
||||
debug(1, "-------------------------------------------");
|
||||
}
|
||||
|
||||
void ScriptInterpreter::dumpAllScripts() {
|
||||
int *opcodeStats = new int[_commandsMax - 1];
|
||||
int *externStats = new int[_functions->getCount()];
|
||||
|
||||
for (int i = 0; i < _commandsMax; i++)
|
||||
opcodeStats[i] = 0;
|
||||
for (int i = 0; i < _functions->getCount(); i++)
|
||||
externStats[i] = 0;
|
||||
|
||||
for (uint objectIndex = 1; objectIndex <= _vm->_dat->getObjectCount(); objectIndex++) {
|
||||
Object *obj = _vm->_dat->getObject(objectIndex);
|
||||
// Check if it's a byte array which might contain code
|
||||
if (obj->getClass() != 0x7FFF)
|
||||
continue;
|
||||
// Code objects aren't excplicitly marked as such, we need to check if
|
||||
// the last byte is a cmd_return opcode.
|
||||
byte *retByte = obj->getData() + obj->getSize() - 1;
|
||||
if (*retByte == 0x1F) {
|
||||
dumpScript(objectIndex, opcodeStats, externStats);
|
||||
}
|
||||
}
|
||||
|
||||
debug(1, "OPCODE statistics:");
|
||||
for (int i = 0; i < _commandsMax - 1; i++)
|
||||
if (opcodeStats[i] > 0)
|
||||
debug(1, "%-30s: %d", _commands[i].desc, opcodeStats[i]);
|
||||
debug(1, "UNUSED OPCODE statistics:");
|
||||
for (int i = 0; i < _commandsMax - 1; i++)
|
||||
if (opcodeStats[i] == 0)
|
||||
debug(1, "%-30s: %d", _commands[i].desc, opcodeStats[i]);
|
||||
debug(1, ".");
|
||||
|
||||
debug(1, "EXTERN statistics (%d):", _functions->getCount());
|
||||
for (int i = 0; i < _functions->getCount(); i++)
|
||||
if (externStats[i] > 0)
|
||||
debug(1, "%-30s: %d", _functions->getFuncName(i), externStats[i]);
|
||||
debug(1, "UNUSED EXTERN statistics (%d):", _functions->getCount());
|
||||
for (int i = 0; i < _functions->getCount(); i++)
|
||||
if (externStats[i] == 0)
|
||||
debug(1, "%-30s: %d", _functions->getFuncName(i), externStats[i]);
|
||||
debug(1, ".");
|
||||
|
||||
delete[] opcodeStats;
|
||||
delete[] externStats;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // End of namespace Made
|
||||
169
engines/made/script.h
Normal file
169
engines/made/script.h
Normal file
@@ -0,0 +1,169 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MADE_SCRIPT_H
|
||||
#define MADE_SCRIPT_H
|
||||
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Made {
|
||||
|
||||
// Define this to dump all game scripts and a usage statistic of all
|
||||
// opcodes/extended functions instead of running the actual game.
|
||||
// Then run ScummVM with debuglevel 1.
|
||||
//#define DUMP_SCRIPTS
|
||||
|
||||
class MadeEngine;
|
||||
class ScriptFunctions;
|
||||
|
||||
const int kScriptStackSize = 1000;
|
||||
const int kScriptStackLimit = kScriptStackSize + 1;
|
||||
|
||||
class ScriptStack {
|
||||
public:
|
||||
ScriptStack() {
|
||||
for (int16 i = 0; i < kScriptStackSize; i++)
|
||||
_stack[i] = 0;
|
||||
_stackPos = kScriptStackSize;
|
||||
}
|
||||
~ScriptStack() {}
|
||||
inline int16 top() { return _stack[_stackPos]; }
|
||||
inline int16 pop() {
|
||||
if (_stackPos == kScriptStackSize)
|
||||
error("ScriptStack::pop() Stack underflow");
|
||||
return _stack[_stackPos++];
|
||||
}
|
||||
inline void push(int16 value = 0) {
|
||||
if (_stackPos == 0)
|
||||
error("ScriptStack::push() Stack overflow");
|
||||
_stack[--_stackPos] = value;
|
||||
}
|
||||
inline void setTop(int16 value) { _stack[_stackPos] = value; }
|
||||
inline int16 peek(int16 index) { return _stack[index]; }
|
||||
inline void poke(int16 index, int16 value) { _stack[index] = value; }
|
||||
inline void alloc(int16 count) { _stackPos -= count; }
|
||||
inline void free(int16 count) { _stackPos += count; }
|
||||
inline int16 getStackPos() const { return _stackPos; }
|
||||
inline void setStackPos(int16 stackPtr) { _stackPos = stackPtr; }
|
||||
inline int16 *getStackPtr() { return &_stack[_stackPos]; }
|
||||
protected:
|
||||
int16 _stack[kScriptStackSize];
|
||||
int16 _stackPos;
|
||||
};
|
||||
|
||||
class ScriptInterpreter {
|
||||
public:
|
||||
ScriptInterpreter(MadeEngine *vm);
|
||||
~ScriptInterpreter();
|
||||
void runScript(int16 scriptObjectIndex);
|
||||
void dumpScript(int16 objectIndex, int *opcodeStats, int *externStats);
|
||||
void dumpAllScripts();
|
||||
protected:
|
||||
MadeEngine *_vm;
|
||||
|
||||
ScriptStack _stack;
|
||||
int16 _localStackPos;
|
||||
int16 _runningScriptObjectIndex;
|
||||
byte *_codeBase, *_codeIp;
|
||||
|
||||
ScriptFunctions *_functions;
|
||||
|
||||
byte readByte();
|
||||
int16 readInt16();
|
||||
|
||||
typedef void (ScriptInterpreter::*CommandProc)();
|
||||
struct CommandEntry {
|
||||
CommandProc proc;
|
||||
const char *desc;
|
||||
#ifdef DUMP_SCRIPTS
|
||||
const char *sig;
|
||||
#endif
|
||||
};
|
||||
|
||||
const CommandEntry *_commands;
|
||||
int16 _commandsMax;
|
||||
|
||||
void cmd_branchTrue();
|
||||
void cmd_branchFalse();
|
||||
void cmd_branch();
|
||||
void cmd_true();
|
||||
void cmd_false();
|
||||
void cmd_push();
|
||||
void cmd_not();
|
||||
void cmd_add();
|
||||
void cmd_sub();
|
||||
void cmd_mul();
|
||||
void cmd_div();
|
||||
void cmd_mod();
|
||||
void cmd_band();
|
||||
void cmd_bor();
|
||||
void cmd_bnot();
|
||||
void cmd_lt();
|
||||
void cmd_eq();
|
||||
void cmd_gt();
|
||||
void cmd_loadConstant();
|
||||
void cmd_loadVariable();
|
||||
void cmd_getObjectProperty();
|
||||
void cmd_setObjectProperty();
|
||||
void cmd_set();
|
||||
void cmd_print();
|
||||
void cmd_terpri();
|
||||
void cmd_printNumber();
|
||||
void cmd_vref();
|
||||
void cmd_vset();
|
||||
void cmd_vsize();
|
||||
void cmd_exit();
|
||||
void cmd_return();
|
||||
void cmd_call();
|
||||
void cmd_svar();
|
||||
void cmd_sset();
|
||||
void cmd_split();
|
||||
void cmd_snlit();
|
||||
void cmd_yorn();
|
||||
void cmd_save();
|
||||
void cmd_restore();
|
||||
void cmd_arg();
|
||||
void cmd_aset();
|
||||
void cmd_tmp();
|
||||
void cmd_tset();
|
||||
void cmd_tspace();
|
||||
void cmd_class();
|
||||
void cmd_objectp();
|
||||
void cmd_vectorp();
|
||||
void cmd_restart();
|
||||
void cmd_rand();
|
||||
void cmd_randomize();
|
||||
void cmd_send();
|
||||
void cmd_extend();
|
||||
void cmd_catch();
|
||||
void cmd_cdone();
|
||||
void cmd_throw();
|
||||
void cmd_functionp();
|
||||
void cmd_le();
|
||||
void cmd_ge();
|
||||
void cmd_varx();
|
||||
void cmd_setx();
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Made
|
||||
|
||||
#endif /* MADE_H */
|
||||
1151
engines/made/scriptfuncs.cpp
Normal file
1151
engines/made/scriptfuncs.cpp
Normal file
File diff suppressed because it is too large
Load Diff
185
engines/made/scriptfuncs.h
Normal file
185
engines/made/scriptfuncs.h
Normal file
@@ -0,0 +1,185 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MADE_SCRIPTFUNCS_H
|
||||
#define MADE_SCRIPTFUNCS_H
|
||||
|
||||
#include "made/resource.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/system.h"
|
||||
|
||||
namespace Audio {
|
||||
class PCSpeaker;
|
||||
}
|
||||
|
||||
namespace Made {
|
||||
|
||||
class MadeEngine;
|
||||
|
||||
typedef Common::Functor2<int16, int16*, int16> ExternalFunc;
|
||||
|
||||
class ScriptFunctions {
|
||||
public:
|
||||
ScriptFunctions(MadeEngine *vm);
|
||||
virtual ~ScriptFunctions();
|
||||
|
||||
int16 callFunction(uint16 index, int16 argc, int16 *argv) {
|
||||
if (index >= _externalFuncs.size())
|
||||
error("ScriptFunctions::callFunction() Invalid function index %d", index);
|
||||
debug(4, "%s", _externalFuncNames[index]);
|
||||
return (*_externalFuncs[index])(argc, argv);
|
||||
}
|
||||
|
||||
void setupExternalsTable();
|
||||
const char* getFuncName(int index) { return _externalFuncNames[index]; }
|
||||
int getCount() const { return _externalFuncs.size(); }
|
||||
void stopSound();
|
||||
|
||||
protected:
|
||||
MadeEngine *_vm;
|
||||
Audio::SoundHandle _audioStreamHandle;
|
||||
Audio::SoundHandle _voiceStreamHandle;
|
||||
SoundResource* _soundResource;
|
||||
bool _soundStarted;
|
||||
bool _soundWasPlaying;
|
||||
// The sound length in milliseconds for purpose of checking if the sound is
|
||||
// still playing.
|
||||
int _soundCheckLength;
|
||||
// The audio volume set by the game scripts.
|
||||
uint8 _gameAudioVolume;
|
||||
|
||||
// PlayNote/StopNote and PlayTele/StopTele wave generators
|
||||
Audio::PCSpeaker *_pcSpeaker1, *_pcSpeaker2;
|
||||
|
||||
Common::Array<const ExternalFunc *> _externalFuncs;
|
||||
Common::Array<const char *> _externalFuncNames;
|
||||
|
||||
int16 sfSystemCall(int16 argc, int16 *argv);
|
||||
int16 sfInitGraf(int16 argc, int16 *argv);
|
||||
int16 sfRestoreGraf(int16 argc, int16 *argv);
|
||||
int16 sfDrawPicture(int16 argc, int16 *argv);
|
||||
int16 sfClearScreen(int16 argc, int16 *argv);
|
||||
int16 sfShowPage(int16 argc, int16 *argv);
|
||||
int16 sfPollEvent(int16 argc, int16 *argv);
|
||||
int16 sfGetMouseX(int16 argc, int16 *argv);
|
||||
int16 sfGetMouseY(int16 argc, int16 *argv);
|
||||
int16 sfGetKey(int16 argc, int16 *argv);
|
||||
int16 sfSetVisualEffect(int16 argc, int16 *argv);
|
||||
int16 sfPlaySound(int16 argc, int16 *argv);
|
||||
int16 sfPlayMusic(int16 argc, int16 *argv);
|
||||
int16 sfStopMusic(int16 argc, int16 *argv);
|
||||
int16 sfIsMusicPlaying(int16 argc, int16 *argv);
|
||||
int16 sfSetTextPos(int16 argc, int16 *argv);
|
||||
int16 sfFlashScreen(int16 argc, int16 *argv);
|
||||
int16 sfPlayNote(int16 argc, int16 *argv);
|
||||
int16 sfStopNote(int16 argc, int16 *argv);
|
||||
int16 sfPlayTele(int16 argc, int16 *argv);
|
||||
int16 sfStopTele(int16 argc, int16 *argv);
|
||||
int16 sfHideMouseCursor(int16 argc, int16 *argv);
|
||||
int16 sfShowMouseCursor(int16 argc, int16 *argv);
|
||||
int16 sfGetMusicBeat(int16 argc, int16 *argv);
|
||||
int16 sfSetScreenLock(int16 argc, int16 *argv);
|
||||
int16 sfAddSprite(int16 argc, int16 *argv);
|
||||
int16 sfFreeAnim(int16 argc, int16 *argv);
|
||||
int16 sfDrawSprite(int16 argc, int16 *argv);
|
||||
int16 sfEraseSprites(int16 argc, int16 *argv);
|
||||
int16 sfUpdateSprites(int16 argc, int16 *argv);
|
||||
int16 sfGetTimer(int16 argc, int16 *argv);
|
||||
int16 sfSetTimer(int16 argc, int16 *argv);
|
||||
int16 sfResetTimer(int16 argc, int16 *argv);
|
||||
int16 sfAllocTimer(int16 argc, int16 *argv);
|
||||
int16 sfFreeTimer(int16 argc, int16 *argv);
|
||||
int16 sfSetPaletteLock(int16 argc, int16 *argv);
|
||||
int16 sfSetFont(int16 argc, int16 *argv);
|
||||
int16 sfDrawText(int16 argc, int16 *argv);
|
||||
int16 sfHomeText(int16 argc, int16 *argv);
|
||||
int16 sfSetTextRect(int16 argc, int16 *argv);
|
||||
int16 sfSetTextXY(int16 argc, int16 *argv);
|
||||
int16 sfSetFontDropShadow(int16 argc, int16 *argv);
|
||||
int16 sfSetFontColor(int16 argc, int16 *argv);
|
||||
int16 sfSetFontOutline(int16 argc, int16 *argv);
|
||||
int16 sfLoadMouseCursor(int16 argc, int16 *argv);
|
||||
int16 sfSetSpriteGround(int16 argc, int16 *argv);
|
||||
int16 sfLoadResText(int16 argc, int16 *argv);
|
||||
int16 sfSetClipArea(int16 argc, int16 *argv);
|
||||
int16 sfSetSpriteClip(int16 argc, int16 *argv);
|
||||
int16 sfAddScreenMask(int16 argc, int16 *argv);
|
||||
int16 sfSetSpriteMask(int16 argc, int16 *argv);
|
||||
int16 sfSoundPlaying(int16 argc, int16 *argv);
|
||||
int16 sfStopSound(int16 argc, int16 *argv);
|
||||
int16 sfPlayVoice(int16 argc, int16 *argv);
|
||||
int16 sfPlayCd(int16 argc, int16 *argv);
|
||||
int16 sfStopCd(int16 argc, int16 *argv);
|
||||
int16 sfGetCdStatus(int16 argc, int16 *argv);
|
||||
int16 sfGetCdTime(int16 argc, int16 *argv);
|
||||
int16 sfPlayCdSegment(int16 argc, int16 *argv);
|
||||
int16 sfPrintf(int16 argc, int16 *argv);
|
||||
int16 sfClearMono(int16 argc, int16 *argv);
|
||||
int16 sfGetSoundEnergy(int16 argc, int16 *argv);
|
||||
int16 sfClearText(int16 argc, int16 *argv);
|
||||
int16 sfAnimText(int16 argc, int16 *argv);
|
||||
int16 sfGetTextWidth(int16 argc, int16 *argv);
|
||||
int16 sfPlayMovie(int16 argc, int16 *argv);
|
||||
int16 sfLoadSound(int16 argc, int16 *argv);
|
||||
int16 sfLoadMusic(int16 argc, int16 *argv);
|
||||
int16 sfLoadPicture(int16 argc, int16 *argv);
|
||||
int16 sfSetMusicVolume(int16 argc, int16 *argv);
|
||||
int16 sfRestartEvents(int16 argc, int16 *argv);
|
||||
int16 sfPlaceSprite(int16 argc, int16 *argv);
|
||||
int16 sfPlaceText(int16 argc, int16 *argv);
|
||||
int16 sfDeleteChannel(int16 argc, int16 *argv);
|
||||
int16 sfGetChannelType(int16 argc, int16 *argv);
|
||||
int16 sfSetChannelState(int16 argc, int16 *argv);
|
||||
int16 sfSetChannelLocation(int16 argc, int16 *argv);
|
||||
int16 sfSetChannelContent(int16 argc, int16 *argv);
|
||||
int16 sfSetExcludeArea(int16 argc, int16 *argv);
|
||||
int16 sfSetSpriteExclude(int16 argc, int16 *argv);
|
||||
int16 sfGetChannelState(int16 argc, int16 *argv);
|
||||
int16 sfPlaceAnim(int16 argc, int16 *argv);
|
||||
int16 sfSetAnimFrame(int16 argc, int16 *argv);
|
||||
int16 sfGetAnimFrame(int16 argc, int16 *argv);
|
||||
int16 sfGetAnimFrameCount(int16 argc, int16 *argv);
|
||||
int16 sfGetPictureWidth(int16 argc, int16 *argv);
|
||||
int16 sfGetPictureHeight(int16 argc, int16 *argv);
|
||||
int16 sfSetSoundRate(int16 argc, int16 *argv);
|
||||
int16 sfDrawAnimPic(int16 argc, int16 *argv);
|
||||
int16 sfLoadAnim(int16 argc, int16 *argv);
|
||||
int16 sfReadText(int16 argc, int16 *argv);
|
||||
int16 sfReadMenu(int16 argc, int16 *argv);
|
||||
int16 sfDrawMenu(int16 argc, int16 *argv);
|
||||
int16 sfGetMenuCount(int16 argc, int16 *argv);
|
||||
int16 sfSaveGame(int16 argc, int16 *argv);
|
||||
int16 sfLoadGame(int16 argc, int16 *argv);
|
||||
int16 sfGetGameDescription(int16 argc, int16 *argv);
|
||||
int16 sfShakeScreen(int16 argc, int16 *argv);
|
||||
int16 sfPlaceMenu(int16 argc, int16 *argv);
|
||||
int16 sfSetSoundVolume(int16 argc, int16 *argv);
|
||||
int16 sfGetSynthType(int16 argc, int16 *argv);
|
||||
int16 sfIsSlowSystem(int16 argc, int16 *argv);
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Made
|
||||
|
||||
#endif /* MADE_H */
|
||||
282
engines/made/sound.cpp
Normal file
282
engines/made/sound.cpp
Normal file
@@ -0,0 +1,282 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "made/sound.h"
|
||||
|
||||
#include "common/endian.h"
|
||||
|
||||
namespace Made {
|
||||
|
||||
void ManholeEgaSoundDecompressor::decompress(byte *source, byte *dest, uint32 size) {
|
||||
/* Some kind of ADPCM compression. I hope this works on BE machines. */
|
||||
int newmode;
|
||||
_source = source;
|
||||
_dest = dest;
|
||||
_size = size;
|
||||
_bitBuffer = 0;
|
||||
_bitsLeft = 0;
|
||||
_writeFlag = false;
|
||||
_eof = false;
|
||||
_sample1 = 0x80000;
|
||||
_sample2 = 0x800000;
|
||||
_sample3 = 0x800000;
|
||||
_sample4 = 0x800000;
|
||||
_mode = getBit();
|
||||
while (!_eof) {
|
||||
update1();
|
||||
update3();
|
||||
update0();
|
||||
newmode = getBit();
|
||||
if (_eof)
|
||||
break;
|
||||
if (newmode == _mode) {
|
||||
update1();
|
||||
update3();
|
||||
do {
|
||||
update0();
|
||||
newmode = getBit();
|
||||
if (_eof || newmode != _mode)
|
||||
break;
|
||||
update2();
|
||||
update3();
|
||||
} while (1);
|
||||
}
|
||||
_mode = newmode;
|
||||
}
|
||||
}
|
||||
|
||||
int ManholeEgaSoundDecompressor::getBit() {
|
||||
if (_bitsLeft == 0) {
|
||||
if (_size == 0) {
|
||||
_eof = true;
|
||||
return 0;
|
||||
}
|
||||
_bitBuffer = READ_BE_UINT16(_source);
|
||||
_source += 2;
|
||||
_bitsLeft = 16;
|
||||
_size -= 2;
|
||||
}
|
||||
int temp = _bitBuffer & 0x8000;
|
||||
_bitBuffer <<= 1;
|
||||
_bitsLeft--;
|
||||
return temp;
|
||||
}
|
||||
|
||||
void ManholeEgaSoundDecompressor::update0() {
|
||||
SWAP(_sample1, _sample3);
|
||||
if (_sample2 & 0x80000000) {
|
||||
_sample2 -= (_sample2 >> 8) | 0xFF000000;
|
||||
} else {
|
||||
_sample2 -= _sample2 >> 8;
|
||||
}
|
||||
_sample2 += 0x8000;
|
||||
if (_sample2 & 0x80000000) {
|
||||
_sample2 = 0;
|
||||
} else if ((_sample2 & 0xFFFF0000) > 0x00FF0000) {
|
||||
_sample2 = 0xFF0000;
|
||||
}
|
||||
_sample1 += _sample2;
|
||||
_sample1 >>= 1;
|
||||
_sample1 -= _sample4;
|
||||
_sample1 >>= 2;
|
||||
_sample4 += _sample1;
|
||||
if (_writeFlag) {
|
||||
*_dest++ = (_sample4 & 0xFF0000) >> 16;
|
||||
}
|
||||
_writeFlag = !_writeFlag;
|
||||
_sample1 = _sample2;
|
||||
SWAP(_sample1, _sample3);
|
||||
}
|
||||
|
||||
void ManholeEgaSoundDecompressor::update1() {
|
||||
if (_sample1 & 0x80000000) {
|
||||
_sample1 -= (_sample1 >> 8) | 0xFF000000;
|
||||
} else {
|
||||
_sample1 -= _sample1 >> 8;
|
||||
}
|
||||
_sample1 += 500;
|
||||
}
|
||||
|
||||
void ManholeEgaSoundDecompressor::update2() {
|
||||
uint32 temp = (_sample1 >> 6) | ((_sample1 & 0xFF) << 27) | ((_sample1 & 0xC0) >> 5);
|
||||
if (_sample1 & 0x80000000) {
|
||||
_sample1 += temp | 0xFC000000;
|
||||
} else {
|
||||
_sample1 += temp & 0x03FFFFFF;
|
||||
}
|
||||
_sample1 += 500;
|
||||
}
|
||||
|
||||
void ManholeEgaSoundDecompressor::update3() {
|
||||
if (_mode)
|
||||
_sample2 -= _sample1;
|
||||
else
|
||||
_sample2 += _sample1;
|
||||
}
|
||||
|
||||
void decompressSound(byte *source, byte *dest, uint16 chunkSize, uint16 chunkCount, SoundEnergyArray *soundEnergyArray, SoundDecoderData *soundDecoderData) {
|
||||
|
||||
int16 prevSample, workSample;
|
||||
byte* soundBuffer;
|
||||
byte deltaSoundBuffer[1024];
|
||||
int16 soundBuffer2[16];
|
||||
byte deltaType, type;
|
||||
uint16 workChunkSize, byteCount, bitCount;
|
||||
byte bitMask, bitShift;
|
||||
uint16 ofs = 0;
|
||||
uint16 i = 0, l = 0;
|
||||
byte val;
|
||||
|
||||
SoundEnergyItem soundEnergyItem;
|
||||
|
||||
const int modeValues[3][4] = {
|
||||
{ 2, 8, 0x01, 1},
|
||||
{ 4, 4, 0x03, 2},
|
||||
{16, 2, 0x0F, 4}
|
||||
};
|
||||
|
||||
soundEnergyItem.position = 0;
|
||||
memset(deltaSoundBuffer, 0, 1024);
|
||||
|
||||
if (soundEnergyArray)
|
||||
soundEnergyArray->clear();
|
||||
|
||||
if (soundDecoderData) {
|
||||
soundBuffer = soundDecoderData->_soundBuffer;
|
||||
prevSample = soundDecoderData->_prevSample;
|
||||
} else {
|
||||
soundBuffer = new byte[1025];
|
||||
memset(soundBuffer, 0x80, 1025);
|
||||
prevSample = 0;
|
||||
}
|
||||
|
||||
while (chunkCount--) {
|
||||
deltaType = (*source) >> 6;
|
||||
workChunkSize = chunkSize;
|
||||
|
||||
if (deltaType == 1)
|
||||
workChunkSize /= 2;
|
||||
else if (deltaType == 2)
|
||||
workChunkSize /= 4;
|
||||
|
||||
type = (*source++) & 0x0F;
|
||||
|
||||
workSample = prevSample;
|
||||
|
||||
soundEnergyItem.position += chunkSize;
|
||||
|
||||
switch (type) {
|
||||
|
||||
case 0:
|
||||
memset(soundBuffer, 0x80, workChunkSize);
|
||||
workSample = 0;
|
||||
|
||||
soundEnergyItem.energy = 0;
|
||||
if (soundEnergyArray)
|
||||
soundEnergyArray->push_back(soundEnergyItem);
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
byteCount = modeValues[type - 2][0];
|
||||
bitCount = modeValues[type - 2][1];
|
||||
bitMask = modeValues[type - 2][2];
|
||||
bitShift = modeValues[type - 2][3];
|
||||
ofs = 0;
|
||||
|
||||
for (i = 0; i < byteCount; i++)
|
||||
soundBuffer2[i] = (*source++) * 2 - 128;
|
||||
|
||||
while (ofs < workChunkSize) {
|
||||
val = *source++;
|
||||
for (i = 0; i < bitCount; i++) {
|
||||
workSample = CLIP<int16>(workSample + soundBuffer2[val & bitMask], -127, 127);
|
||||
val >>= bitShift;
|
||||
soundBuffer[ofs++] = workSample + 128;
|
||||
}
|
||||
}
|
||||
|
||||
soundEnergyItem.energy = type - 1;
|
||||
if (soundEnergyArray)
|
||||
soundEnergyArray->push_back(soundEnergyItem);
|
||||
|
||||
break;
|
||||
|
||||
case 5:
|
||||
for (i = 0; i < workChunkSize; i++)
|
||||
soundBuffer[i] = *source++;
|
||||
workSample = soundBuffer[workChunkSize - 1] - 128;
|
||||
|
||||
soundEnergyItem.energy = 4;
|
||||
if (soundEnergyArray)
|
||||
soundEnergyArray->push_back(soundEnergyItem);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
delete[] soundBuffer;
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (deltaType > 0) {
|
||||
// NB: The original did not add this extra value at the end (as far
|
||||
// as I can tell), and so technically read past the filled part of
|
||||
// soundBuffer.
|
||||
soundBuffer[workChunkSize] = soundBuffer[workChunkSize - 1];
|
||||
|
||||
if (deltaType == 1) {
|
||||
for (i = 0; i < chunkSize - 1; i += 2) {
|
||||
l = i / 2;
|
||||
deltaSoundBuffer[i] = soundBuffer[l];
|
||||
deltaSoundBuffer[i + 1] = (soundBuffer[l] + soundBuffer[l + 1]) / 2;
|
||||
}
|
||||
} else if (deltaType == 2) {
|
||||
for (i = 0; i < chunkSize - 1; i += 4) {
|
||||
l = i / 4;
|
||||
deltaSoundBuffer[i] = soundBuffer[l];
|
||||
deltaSoundBuffer[i + 2] = (soundBuffer[l] + soundBuffer[l + 1]) / 2;
|
||||
deltaSoundBuffer[i + 1] = (deltaSoundBuffer[i + 2] + soundBuffer[l]) / 2;
|
||||
deltaSoundBuffer[i + 3] = (deltaSoundBuffer[i + 2] + soundBuffer[l + 1]) / 2;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < chunkSize; i++)
|
||||
soundBuffer[i] = deltaSoundBuffer[i];
|
||||
}
|
||||
|
||||
prevSample = workSample;
|
||||
memcpy(dest, soundBuffer, chunkSize);
|
||||
dest += chunkSize;
|
||||
}
|
||||
|
||||
if (soundDecoderData) {
|
||||
soundDecoderData->_prevSample = prevSample;
|
||||
} else {
|
||||
delete[] soundBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Made
|
||||
74
engines/made/sound.h
Normal file
74
engines/made/sound.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MADE_SOUND_H
|
||||
#define MADE_SOUND_H
|
||||
|
||||
#include "common/array.h"
|
||||
|
||||
namespace Made {
|
||||
|
||||
class ManholeEgaSoundDecompressor {
|
||||
public:
|
||||
void decompress(byte *source, byte *dest, uint32 size);
|
||||
protected:
|
||||
byte *_source, *_dest;
|
||||
uint32 _size;
|
||||
uint16 _bitBuffer;
|
||||
int _bitsLeft;
|
||||
int32 _sample1, _sample2, _sample3, _sample4;
|
||||
bool _writeFlag;
|
||||
bool _eof;
|
||||
int _mode;
|
||||
int getBit();
|
||||
void update0();
|
||||
void update1();
|
||||
void update2();
|
||||
void update3();
|
||||
};
|
||||
|
||||
struct SoundEnergyItem {
|
||||
uint32 position;
|
||||
byte energy;
|
||||
};
|
||||
|
||||
typedef Common::Array<SoundEnergyItem> SoundEnergyArray;
|
||||
|
||||
|
||||
// Persistent data for decompressSound(). When calling decompressSound()
|
||||
// repeatedly (for the same stream), pass the same SoundDecoderData object to
|
||||
// ensure decoding properly resumes.
|
||||
class SoundDecoderData {
|
||||
public:
|
||||
SoundDecoderData() {
|
||||
memset(_soundBuffer, 0x80, sizeof(_soundBuffer));
|
||||
_prevSample = 0;
|
||||
}
|
||||
|
||||
byte _soundBuffer[1025];
|
||||
int16 _prevSample;
|
||||
};
|
||||
|
||||
void decompressSound(byte *source, byte *dest, uint16 chunkSize, uint16 chunkCount, SoundEnergyArray *soundEnergyArray = NULL, SoundDecoderData *decoderData = NULL);
|
||||
|
||||
} // End of namespace Made
|
||||
|
||||
#endif /* MADE_H */
|
||||
Reference in New Issue
Block a user