aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
Diffstat (limited to 'engines')
-rw-r--r--engines/adl/adl.cpp1038
-rw-r--r--engines/adl/adl.h247
-rw-r--r--engines/adl/configure.engine3
-rw-r--r--engines/adl/detection.cpp247
-rw-r--r--engines/adl/detection.h45
-rw-r--r--engines/adl/display.cpp520
-rw-r--r--engines/adl/display.h102
-rw-r--r--engines/adl/hires1.cpp396
-rw-r--r--engines/adl/hires1.h113
-rw-r--r--engines/adl/module.mk18
-rw-r--r--engines/agi/text.cpp6
-rw-r--r--engines/composer/detection.cpp47
-rw-r--r--engines/dialogs.cpp10
-rw-r--r--engines/drascula/actors.cpp4
-rw-r--r--engines/drascula/animation.cpp16
-rw-r--r--engines/drascula/drascula.cpp4
-rw-r--r--engines/drascula/drascula.h2
-rw-r--r--engines/drascula/graphics.cpp99
-rw-r--r--engines/drascula/objects.cpp2
-rw-r--r--engines/drascula/rooms.cpp12
-rw-r--r--engines/drascula/saveload.cpp28
-rw-r--r--engines/drascula/talk.cpp2
-rw-r--r--engines/dreamweb/detection_tables.h20
-rw-r--r--engines/gob/detection/tables_fascin.h14
-rw-r--r--engines/gob/detection/tables_playtoons.h18
-rw-r--r--engines/kyra/lol.h1
-rw-r--r--engines/kyra/sound_lol.cpp10
-rw-r--r--engines/kyra/staticres_lol.cpp2
-rw-r--r--engines/mohawk/POTFILES1
-rw-r--r--engines/mohawk/detection.cpp33
-rw-r--r--engines/mohawk/detection_tables.h84
-rw-r--r--engines/mohawk/myst.cpp17
-rw-r--r--engines/mohawk/myst_scripts.cpp9
-rw-r--r--engines/mohawk/myst_stacks/myst.cpp2
-rw-r--r--engines/mohawk/myst_state.cpp99
-rw-r--r--engines/mohawk/myst_state.h22
-rw-r--r--engines/queen/detection.cpp59
-rw-r--r--engines/saga/detection_tables.h34
-rw-r--r--engines/saga/displayinfo.h18
-rw-r--r--engines/saga/interface.cpp26
-rw-r--r--engines/saga/saga.cpp2
-rw-r--r--engines/saga/saga.h3
-rw-r--r--engines/sci/console.cpp40
-rw-r--r--engines/sci/engine/savegame.cpp230
-rw-r--r--engines/sci/engine/scriptdebug.cpp6
-rw-r--r--engines/sci/engine/seg_manager.cpp62
-rw-r--r--engines/sci/engine/segment.cpp20
-rw-r--r--engines/sci/engine/segment.h21
-rw-r--r--engines/scumm/POTFILES1
-rw-r--r--engines/scumm/detection.cpp17
-rw-r--r--engines/scumm/dialogs.cpp2
-rw-r--r--engines/scumm/scumm-md5.h3
-rw-r--r--engines/sherlock/scalpel/scalpel_journal.cpp5
-rw-r--r--engines/sherlock/talk.cpp10
-rw-r--r--engines/sherlock/tattoo/tattoo_journal.cpp10
-rw-r--r--engines/sherlock/tattoo/widget_inventory.cpp2
-rw-r--r--engines/wage/detection.cpp1
-rw-r--r--engines/wage/detection_tables.h196
-rw-r--r--engines/wage/gui.cpp136
-rw-r--r--engines/wage/gui.h5
-rw-r--r--engines/wage/script.cpp2
-rw-r--r--engines/wage/util.cpp13
-rw-r--r--engines/wage/wage.cpp20
-rw-r--r--engines/wage/wage.h4
-rw-r--r--engines/wage/world.cpp14
-rw-r--r--engines/wage/world.h2
-rw-r--r--engines/wintermute/base/base_persistence_manager.cpp2
67 files changed, 3709 insertions, 550 deletions
diff --git a/engines/adl/adl.cpp b/engines/adl/adl.cpp
new file mode 100644
index 0000000000..1ab74c3cf6
--- /dev/null
+++ b/engines/adl/adl.cpp
@@ -0,0 +1,1038 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "common/config-manager.h"
+#include "common/debug.h"
+#include "common/error.h"
+#include "common/file.h"
+#include "common/system.h"
+#include "common/events.h"
+#include "common/stream.h"
+#include "common/savefile.h"
+
+#include "engines/util.h"
+
+#include "graphics/palette.h"
+#include "graphics/thumbnail.h"
+
+#include "adl/adl.h"
+#include "adl/display.h"
+#include "adl/detection.h"
+
+namespace Adl {
+
+AdlEngine::~AdlEngine() {
+ delete _display;
+}
+
+AdlEngine::AdlEngine(OSystem *syst, const AdlGameDescription *gd) :
+ Engine(syst),
+ _display(nullptr),
+ _gameDescription(gd),
+ _isRestarting(false),
+ _isRestoring(false),
+ _saveVerb(0),
+ _saveNoun(0),
+ _restoreVerb(0),
+ _restoreNoun(0),
+ _canSaveNow(false),
+ _canRestoreNow(false) {
+}
+
+Common::String AdlEngine::readString(Common::ReadStream &stream, byte until) const {
+ Common::String str;
+
+ while (1) {
+ byte b = stream.readByte();
+
+ if (stream.eos() || stream.err())
+ error("Error reading string");
+
+ if (b == until)
+ break;
+
+ str += b;
+ };
+
+ return str;
+}
+
+Common::String AdlEngine::readStringAt(Common::SeekableReadStream &stream, uint offset, byte until) const {
+ stream.seek(offset);
+ return readString(stream, until);
+}
+
+void AdlEngine::printMessage(uint idx, bool wait) const {
+ Common::String msg = _messages[idx - 1];
+ wordWrap(msg);
+ _display->printString(msg);
+
+ if (wait)
+ delay(14 * 166018 / 1000);
+}
+
+void AdlEngine::delay(uint32 ms) const {
+ Common::EventManager *ev = g_system->getEventManager();
+
+ uint32 start = g_system->getMillis();
+
+ while (!g_engine->shouldQuit() && g_system->getMillis() - start < ms) {
+ Common::Event event;
+ if (ev->pollEvent(event)) {
+ if (event.type == Common::EVENT_KEYDOWN && (event.kbd.flags & Common::KBD_CTRL)) {
+ switch(event.kbd.keycode) {
+ case Common::KEYCODE_q:
+ g_engine->quitGame();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ g_system->delayMillis(16);
+ }
+}
+
+Common::String AdlEngine::inputString(byte prompt) const {
+ Common::String s;
+
+ if (prompt > 0)
+ _display->printString(Common::String(prompt));
+
+ while (1) {
+ byte b = inputKey();
+
+ if (g_engine->shouldQuit() || _isRestoring)
+ return 0;
+
+ if (b == 0)
+ continue;
+
+ if (b == ('\r' | 0x80)) {
+ s += b;
+ _display->printString(Common::String(b));
+ return s;
+ }
+
+ if (b < 0xa0) {
+ switch (b) {
+ case Common::KEYCODE_BACKSPACE | 0x80:
+ if (!s.empty()) {
+ _display->moveCursorBackward();
+ _display->setCharAtCursor(APPLECHAR(' '));
+ s.deleteLastChar();
+ }
+ break;
+ };
+ } else {
+ if (s.size() < 255) {
+ s += b;
+ _display->printString(Common::String(b));
+ }
+ }
+ }
+}
+
+byte AdlEngine::inputKey() const {
+ Common::EventManager *ev = g_system->getEventManager();
+
+ byte key = 0;
+
+ _display->showCursor(true);
+
+ while (!g_engine->shouldQuit() && !_isRestoring && key == 0) {
+ Common::Event event;
+ if (ev->pollEvent(event)) {
+ if (event.type != Common::EVENT_KEYDOWN)
+ continue;
+
+ if (event.kbd.flags & Common::KBD_CTRL) {
+ if (event.kbd.keycode == Common::KEYCODE_q)
+ g_engine->quitGame();
+ continue;
+ }
+
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_BACKSPACE:
+ case Common::KEYCODE_RETURN:
+ key = convertKey(event.kbd.keycode);
+ break;
+ default:
+ if (event.kbd.ascii >= 0x20 && event.kbd.ascii < 0x80)
+ key = convertKey(event.kbd.ascii);
+ };
+ }
+
+ _display->updateTextScreen();
+ g_system->delayMillis(16);
+ }
+
+ _display->showCursor(false);
+
+ return key;
+}
+
+void AdlEngine::loadWords(Common::ReadStream &stream, WordMap &map) const {
+ uint index = 0;
+
+ while (1) {
+ ++index;
+
+ byte buf[IDI_WORD_SIZE];
+
+ if (stream.read(buf, IDI_WORD_SIZE) < IDI_WORD_SIZE)
+ error("Error reading word list");
+
+ Common::String word((char *)buf, IDI_WORD_SIZE);
+
+ if (!map.contains(word))
+ map[word] = index;
+
+ byte synonyms = stream.readByte();
+
+ if (stream.err() || stream.eos())
+ error("Error reading word list");
+
+ if (synonyms == 0xff)
+ break;
+
+ for (uint i = 0; i < synonyms; ++i) {
+ if (stream.read((char *)buf, IDI_WORD_SIZE) < IDI_WORD_SIZE)
+ error("Error reading word list");
+
+ word = Common::String((char *)buf, IDI_WORD_SIZE);
+
+ if (!map.contains(word))
+ map[word] = index;
+ }
+ }
+}
+
+void AdlEngine::readCommands(Common::ReadStream &stream, Commands &commands) {
+ while (1) {
+ Command command;
+ command.room = stream.readByte();
+
+ if (command.room == 0xff)
+ return;
+
+ command.verb = stream.readByte();
+ command.noun = stream.readByte();
+
+ byte scriptSize = stream.readByte() - 6;
+
+ command.numCond = stream.readByte();
+ command.numAct = stream.readByte();
+
+ for (uint i = 0; i < scriptSize; ++i)
+ command.script.push_back(stream.readByte());
+
+ if (stream.eos() || stream.err())
+ error("Failed to read commands");
+
+ if (command.numCond == 0 && command.script[0] == IDO_ACT_SAVE) {
+ _saveVerb = command.verb;
+ _saveNoun = command.noun;
+ }
+
+ if (command.numCond == 0 && command.script[0] == IDO_ACT_LOAD) {
+ _restoreVerb = command.verb;
+ _restoreNoun = command.noun;
+ }
+
+ commands.push_back(command);
+ }
+}
+
+Common::Error AdlEngine::run() {
+ _display = new Display();
+
+ loadData();
+
+ int saveSlot = ConfMan.getInt("save_slot");
+ if (saveSlot >= 0) {
+ if (loadGameState(saveSlot).getCode() != Common::kNoError)
+ error("Failed to load save game from slot %i", saveSlot);
+ _display->moveCursorTo(Common::Point(0, 23));
+ _isRestoring = true;
+ } else {
+ runIntro();
+ initState();
+ }
+
+ _display->setMode(DISPLAY_MODE_MIXED);
+ _display->printAsciiString("\r\r\r\r\r");
+
+ while (1) {
+ uint verb = 0, noun = 0;
+
+ // When restoring from the launcher, we don't read
+ // input on the first iteration. This is needed to
+ // ensure that restoring from the launcher and
+ // restoring in-game brings us to the same game state.
+ // (Also see comment below.)
+ if (!_isRestoring) {
+ clearScreen();
+ showRoom();
+
+ _canSaveNow = _canRestoreNow = true;
+ getInput(verb, noun);
+ _canSaveNow = _canRestoreNow = false;
+
+ if (shouldQuit())
+ break;
+
+ // If we just restored from the GMM, we skip this command
+ // set, as no command has been input by the user
+ if (!_isRestoring)
+ if (!doOneCommand(_roomCommands, verb, noun))
+ printMessage(_messageIds.dontUnderstand);
+ }
+
+ if (_isRestoring) {
+ // We restored from the GMM or launcher. As restoring
+ // with "RESTORE GAME" does not end command processing,
+ // we don't break it off here either. This essentially
+ // means that restoring a game will always run through
+ // the global commands and increase the move counter
+ // before the first user input.
+ _display->printAsciiString("\r");
+ _isRestoring = false;
+ verb = _restoreVerb;
+ noun = _restoreNoun;
+ }
+
+ // Restarting does end command processing
+ if (_isRestarting) {
+ _isRestarting = false;
+ continue;
+ }
+
+ doAllCommands(_globalCommands, verb, noun);
+ _state.moves++;
+ }
+
+ return Common::kNoError;
+}
+
+bool AdlEngine::hasFeature(EngineFeature f) const {
+ switch (f) {
+ case kSupportsLoadingDuringRuntime:
+ case kSupportsSavingDuringRuntime:
+ case kSupportsRTL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+Common::Error AdlEngine::loadGameState(int slot) {
+ Common::String fileName = Common::String::format("%s.s%02d", _targetName.c_str(), slot);
+ Common::InSaveFile *inFile = getSaveFileManager()->openForLoading(fileName);
+
+ if (!inFile) {
+ warning("Failed to open file '%s'", fileName.c_str());
+ return Common::kUnknownError;
+ }
+
+ if (inFile->readUint32BE() != MKTAG('A', 'D', 'L', ':')) {
+ warning("No header found in '%s'", fileName.c_str());
+ delete inFile;
+ return Common::kUnknownError;
+ }
+
+ byte saveVersion = inFile->readByte();
+ if (saveVersion != SAVEGAME_VERSION) {
+ warning("Save game version %i not supported", saveVersion);
+ delete inFile;
+ return Common::kUnknownError;
+ }
+
+ // Skip description
+ inFile->seek(SAVEGAME_NAME_LEN, SEEK_CUR);
+ // Skip save time
+ inFile->seek(6, SEEK_CUR);
+
+ uint32 playTime = inFile->readUint32BE();
+
+ Graphics::skipThumbnail(*inFile);
+
+ initState();
+
+ _state.room = inFile->readByte();
+ _state.moves = inFile->readByte();
+ _state.isDark = inFile->readByte();
+
+ uint32 size = inFile->readUint32BE();
+ if (size != _state.rooms.size())
+ error("Room count mismatch (expected %i; found %i)", _state.rooms.size(), size);
+
+ for (uint i = 0; i < size; ++i) {
+ _state.rooms[i].picture = inFile->readByte();
+ _state.rooms[i].curPicture = inFile->readByte();
+ }
+
+ size = inFile->readUint32BE();
+ if (size != _state.items.size())
+ error("Item count mismatch (expected %i; found %i)", _state.items.size(), size);
+
+ for (uint i = 0; i < size; ++i) {
+ _state.items[i].room = inFile->readByte();
+ _state.items[i].picture = inFile->readByte();
+ _state.items[i].position.x = inFile->readByte();
+ _state.items[i].position.y = inFile->readByte();
+ _state.items[i].state = inFile->readByte();
+ }
+
+ size = inFile->readUint32BE();
+ if (size != _state.vars.size())
+ error("Variable count mismatch (expected %i; found %i)", _state.vars.size(), size);
+
+ for (uint i = 0; i < size; ++i)
+ _state.vars[i] = inFile->readByte();
+
+ if (inFile->err() || inFile->eos())
+ error("Failed to load game '%s'", fileName.c_str());
+
+ delete inFile;
+
+ setTotalPlayTime(playTime);
+
+ _isRestoring = true;
+ return Common::kNoError;
+}
+
+bool AdlEngine::canLoadGameStateCurrently() {
+ return _canRestoreNow;
+}
+
+Common::Error AdlEngine::saveGameState(int slot, const Common::String &desc) {
+ Common::String fileName = Common::String::format("%s.s%02d", _targetName.c_str(), slot);
+ Common::OutSaveFile *outFile = getSaveFileManager()->openForSaving(fileName);
+
+ if (!outFile) {
+ warning("Failed to open file '%s'", fileName.c_str());
+ return Common::kUnknownError;
+ }
+
+ outFile->writeUint32BE(MKTAG('A', 'D', 'L', ':'));
+ outFile->writeByte(SAVEGAME_VERSION);
+
+ char name[SAVEGAME_NAME_LEN] = { };
+
+ if (!desc.empty())
+ strncpy(name, desc.c_str(), sizeof(name) - 1);
+ else {
+ Common::String defaultName("Save ");
+ defaultName += 'A' + slot;
+ strncpy(name, defaultName.c_str(), sizeof(name) - 1);
+ }
+
+ outFile->write(name, sizeof(name));
+
+ TimeDate t;
+ g_system->getTimeAndDate(t);
+
+ outFile->writeUint16BE(t.tm_year);
+ outFile->writeByte(t.tm_mon);
+ outFile->writeByte(t.tm_mday);
+ outFile->writeByte(t.tm_hour);
+ outFile->writeByte(t.tm_min);
+
+ uint32 playTime = getTotalPlayTime();
+ outFile->writeUint32BE(playTime);
+
+ _display->saveThumbnail(*outFile);
+
+ outFile->writeByte(_state.room);
+ outFile->writeByte(_state.moves);
+ outFile->writeByte(_state.isDark);
+
+ outFile->writeUint32BE(_state.rooms.size());
+ for (uint i = 0; i < _state.rooms.size(); ++i) {
+ outFile->writeByte(_state.rooms[i].picture);
+ outFile->writeByte(_state.rooms[i].curPicture);
+ }
+
+ outFile->writeUint32BE(_state.items.size());
+ for (uint i = 0; i < _state.items.size(); ++i) {
+ outFile->writeByte(_state.items[i].room);
+ outFile->writeByte(_state.items[i].picture);
+ outFile->writeByte(_state.items[i].position.x);
+ outFile->writeByte(_state.items[i].position.y);
+ outFile->writeByte(_state.items[i].state);
+ }
+
+ outFile->writeUint32BE(_state.vars.size());
+ for (uint i = 0; i < _state.vars.size(); ++i)
+ outFile->writeByte(_state.vars[i]);
+
+ outFile->finalize();
+
+ if (outFile->err()) {
+ delete outFile;
+ warning("Failed to save game '%s'", fileName.c_str());
+ return Common::kUnknownError;
+ }
+
+ delete outFile;
+ return Common::kNoError;
+}
+
+bool AdlEngine::canSaveGameStateCurrently() {
+ if (!_canSaveNow)
+ return false;
+
+ Commands::const_iterator cmd;
+
+ // Here we check whether or not the game currently accepts the command
+ // "SAVE GAME". This prevents saving via the GMM in situations where
+ // it wouldn't otherwise be possible to do so.
+ for (cmd = _roomCommands.begin(); cmd != _roomCommands.end(); ++cmd) {
+ uint offset;
+ if (matchCommand(*cmd, _saveVerb, _saveNoun, &offset))
+ return cmd->script[offset] == IDO_ACT_SAVE;
+ }
+
+ return false;
+}
+
+void AdlEngine::wordWrap(Common::String &str) const {
+ uint end = 39;
+
+ while (1) {
+ if (str.size() <= end)
+ return;
+
+ while (str[end] != APPLECHAR(' '))
+ --end;
+
+ str.setChar(APPLECHAR('\r'), end);
+ end += 40;
+ }
+}
+
+byte AdlEngine::convertKey(uint16 ascii) const {
+ ascii = toupper(ascii);
+
+ if (ascii >= 0x80)
+ return 0;
+
+ ascii |= 0x80;
+
+ if (ascii >= 0x80 && ascii <= 0xe0)
+ return ascii;
+
+ return 0;
+}
+
+Common::String AdlEngine::getLine() const {
+ // Original engine uses a global here, which isn't reset between
+ // calls and may not match actual mode
+ bool textMode = false;
+
+ while (1) {
+ Common::String line = inputString(APPLECHAR('?'));
+
+ if (shouldQuit() || _isRestoring)
+ return "";
+
+ if ((byte)line[0] == ('\r' | 0x80)) {
+ textMode = !textMode;
+ _display->setMode(textMode ? DISPLAY_MODE_TEXT : DISPLAY_MODE_MIXED);
+ continue;
+ }
+
+ // Remove the return
+ line.deleteLastChar();
+ return line;
+ }
+}
+
+Common::String AdlEngine::getWord(const Common::String &line, uint &index) const {
+ Common::String str;
+
+ for (uint i = 0; i < 8; ++i)
+ str += APPLECHAR(' ');
+
+ int copied = 0;
+
+ // Skip initial whitespace
+ while (1) {
+ if (index == line.size())
+ return str;
+ if (line[index] != APPLECHAR(' '))
+ break;
+ ++index;
+ }
+
+ // Copy up to 8 characters
+ while (1) {
+ if (copied < 8)
+ str.setChar(line[index], copied++);
+
+ index++;
+
+ if (index == line.size() || line[index] == APPLECHAR(' '))
+ return str;
+ }
+}
+
+void AdlEngine::getInput(uint &verb, uint &noun) {
+ while (1) {
+ _display->printString(_strings.enterCommand);
+ Common::String line = getLine();
+
+ if (shouldQuit() || _isRestoring)
+ return;
+
+ uint index = 0;
+ Common::String verbStr = getWord(line, index);
+
+ if (!_verbs.contains(verbStr)) {
+ Common::String err = _strings.verbError;
+ for (uint i = 0; i < verbStr.size(); ++i)
+ err.setChar(verbStr[i], i + 19);
+ _display->printString(err);
+ continue;
+ }
+
+ verb = _verbs[verbStr];
+
+ Common::String nounStr = getWord(line, index);
+
+ if (!_nouns.contains(nounStr)) {
+ Common::String err = _strings.nounError;
+ for (uint i = 0; i < verbStr.size(); ++i)
+ err.setChar(verbStr[i], i + 19);
+ for (uint i = 0; i < nounStr.size(); ++i)
+ err.setChar(nounStr[i], i + 30);
+ _display->printString(err);
+ continue;
+ }
+
+ noun = _nouns[nounStr];
+ return;
+ }
+}
+
+void AdlEngine::showRoom() const {
+ if (!_state.isDark) {
+ drawPic(getCurRoom().curPicture);
+ drawItems();
+ }
+
+ _display->updateHiResScreen();
+ printMessage(getCurRoom().description, false);
+}
+
+void AdlEngine::clearScreen() const {
+ _display->setMode(DISPLAY_MODE_MIXED);
+ _display->clear(0x00);
+}
+
+void AdlEngine::drawItems() const {
+ Common::Array<Item>::const_iterator item;
+
+ uint dropped = 0;
+
+ for (item = _state.items.begin(); item != _state.items.end(); ++item) {
+ if (item->room != _state.room)
+ continue;
+
+ if (item->state == IDI_ITEM_MOVED) {
+ if (getCurRoom().picture == getCurRoom().curPicture) {
+ const Common::Point &p = _itemOffsets[dropped];
+ if (item->isLineArt)
+ drawLineArt(_lineArt[item->picture - 1], p);
+ else
+ drawPic(item->picture, p);
+ ++dropped;
+ }
+ continue;
+ }
+
+ Common::Array<byte>::const_iterator pic;
+
+ for (pic = item->roomPictures.begin(); pic != item->roomPictures.end(); ++pic) {
+ if (*pic == getCurRoom().curPicture) {
+ if (item->isLineArt)
+ drawLineArt(_lineArt[item->picture - 1], item->position);
+ else
+ drawPic(item->picture, item->position);
+ continue;
+ }
+ }
+ }
+}
+
+void AdlEngine::drawNextPixel(Common::Point &p, byte color, byte bits, byte quadrant) const {
+ if (bits & 4)
+ _display->putPixel(p, color);
+
+ bits += quadrant;
+
+ if (bits & 1)
+ p.x += (bits & 2 ? -1 : 1);
+ else
+ p.y += (bits & 2 ? 1 : -1);
+}
+
+void AdlEngine::drawLineArt(const Common::Array<byte> &lineArt, const Common::Point &pos, byte rotation, byte scaling, byte color) const {
+ const byte stepping[] = {
+ 0xff, 0xfe, 0xfa, 0xf4, 0xec, 0xe1, 0xd4, 0xc5,
+ 0xb4, 0xa1, 0x8d, 0x78, 0x61, 0x49, 0x31, 0x18,
+ 0xff
+ };
+
+ byte quadrant = rotation >> 4;
+ rotation &= 0xf;
+ byte xStep = stepping[rotation];
+ byte yStep = stepping[(rotation ^ 0xf) + 1] + 1;
+
+ Common::Point p(pos);
+
+ for (uint i = 0; i < lineArt.size(); ++i) {
+ byte b = lineArt[i];
+
+ do {
+ byte xFrac = 0x80;
+ byte yFrac = 0x80;
+ for (uint j = 0; j < scaling; ++j) {
+ if (xFrac + xStep + 1 > 255)
+ drawNextPixel(p, color, b, quadrant);
+ xFrac += xStep + 1;
+ if (yFrac + yStep > 255)
+ drawNextPixel(p, color, b, quadrant + 1);
+ yFrac += yStep;
+ }
+ b >>= 3;
+ } while (b != 0);
+ }
+}
+
+const Room &AdlEngine::getRoom(uint i) const {
+ if (i < 1 || i > _state.rooms.size())
+ error("Room %i out of range [1, %i]", i, _state.rooms.size());
+
+ return _state.rooms[i - 1];
+}
+
+Room &AdlEngine::getRoom(uint i) {
+ if (i < 1 || i > _state.rooms.size())
+ error("Room %i out of range [1, %i]", i, _state.rooms.size());
+
+ return _state.rooms[i - 1];
+}
+
+const Room &AdlEngine::getCurRoom() const {
+ return getRoom(_state.room);
+}
+
+Room &AdlEngine::getCurRoom() {
+ return getRoom(_state.room);
+}
+
+const Item &AdlEngine::getItem(uint i) const {
+ if (i < 1 || i > _state.items.size())
+ error("Item %i out of range [1, %i]", i, _state.items.size());
+
+ return _state.items[i - 1];
+}
+
+Item &AdlEngine::getItem(uint i) {
+ if (i < 1 || i > _state.items.size())
+ error("Item %i out of range [1, %i]", i, _state.items.size());
+
+ return _state.items[i - 1];
+}
+
+byte AdlEngine::getVar(uint i) const {
+ if (i >= _state.vars.size())
+ error("Variable %i out of range [0, %i]", i, _state.vars.size() - 1);
+
+ return _state.vars[i];
+}
+
+void AdlEngine::setVar(uint i, byte value) {
+ if (i >= _state.vars.size())
+ error("Variable %i out of range [0, %i]", i, _state.vars.size() - 1);
+
+ _state.vars[i] = value;
+}
+
+void AdlEngine::takeItem(byte noun) {
+ Common::Array<Item>::iterator item;
+
+ for (item = _state.items.begin(); item != _state.items.end(); ++item) {
+ if (item->noun != noun || item->room != _state.room)
+ continue;
+
+ if (item->state == IDI_ITEM_DOESNT_MOVE) {
+ printMessage(_messageIds.itemDoesntMove);
+ return;
+ }
+
+ if (item->state == IDI_ITEM_MOVED) {
+ item->room = IDI_NONE;
+ return;
+ }
+
+ Common::Array<byte>::const_iterator pic;
+ for (pic = item->roomPictures.begin(); pic != item->roomPictures.end(); ++pic) {
+ if (*pic == getCurRoom().curPicture) {
+ item->room = IDI_NONE;
+ item->state = IDI_ITEM_MOVED;
+ return;
+ }
+ }
+ }
+
+ printMessage(_messageIds.itemNotHere);
+}
+
+void AdlEngine::dropItem(byte noun) {
+ Common::Array<Item>::iterator item;
+
+ for (item = _state.items.begin(); item != _state.items.end(); ++item) {
+ if (item->noun != noun || item->room != IDI_NONE)
+ continue;
+
+ item->room = _state.room;
+ item->state = IDI_ITEM_MOVED;
+ return;
+ }
+
+ printMessage(_messageIds.dontUnderstand);
+}
+
+#define ARG(N) (command.script[offset + (N)])
+
+bool AdlEngine::matchCommand(const Command &command, byte verb, byte noun, uint *actions) const {
+ if (command.room != IDI_NONE && command.room != _state.room)
+ return false;
+
+ if (command.verb != IDI_NONE && command.verb != verb)
+ return false;
+
+ if (command.noun != IDI_NONE && command.noun != noun)
+ return false;
+
+ uint offset = 0;
+ for (uint i = 0; i < command.numCond; ++i) {
+ switch (ARG(0)) {
+ case IDO_CND_ITEM_IN_ROOM:
+ if (getItem(ARG(1)).room != ARG(2))
+ return false;
+ offset += 3;
+ break;
+ case IDO_CND_MOVES_GE:
+ if (ARG(1) > _state.moves)
+ return false;
+ offset += 2;
+ break;
+ case IDO_CND_VAR_EQ:
+ if (getVar(ARG(1)) != ARG(2))
+ return false;
+ offset += 3;
+ break;
+ case IDO_CND_CUR_PIC_EQ:
+ if (getCurRoom().curPicture != ARG(1))
+ return false;
+ offset += 2;
+ break;
+ case IDO_CND_ITEM_PIC_EQ:
+ if (getItem(ARG(1)).picture != ARG(2))
+ return false;
+ offset += 3;
+ break;
+ default:
+ error("Invalid condition opcode %02x", command.script[offset]);
+ }
+ }
+
+ if (actions)
+ *actions = offset;
+
+ return true;
+}
+
+void AdlEngine::doActions(const Command &command, byte noun, byte offset) {
+ for (uint i = 0; i < command.numAct; ++i) {
+ switch (ARG(0)) {
+ case IDO_ACT_VAR_ADD:
+ setVar(ARG(2), getVar(ARG(2) + ARG(1)));
+ offset += 3;
+ break;
+ case IDO_ACT_VAR_SUB:
+ setVar(ARG(2), getVar(ARG(2)) - ARG(1));
+ offset += 3;
+ break;
+ case IDO_ACT_VAR_SET:
+ setVar(ARG(1), ARG(2));
+ offset += 3;
+ break;
+ case IDO_ACT_LIST_ITEMS: {
+ Common::Array<Item>::const_iterator item;
+
+ for (item = _state.items.begin(); item != _state.items.end(); ++item)
+ if (item->room == IDI_NONE)
+ printMessage(item->description);
+
+ ++offset;
+ break;
+ }
+ case IDO_ACT_MOVE_ITEM:
+ getItem(ARG(1)).room = ARG(2);
+ offset += 3;
+ break;
+ case IDO_ACT_SET_ROOM:
+ getCurRoom().curPicture = getCurRoom().picture;
+ _state.room = ARG(1);
+ offset += 2;
+ break;
+ case IDO_ACT_SET_CUR_PIC:
+ getCurRoom().curPicture = ARG(1);
+ offset += 2;
+ break;
+ case IDO_ACT_SET_PIC:
+ getCurRoom().picture = getCurRoom().curPicture = ARG(1);
+ offset += 2;
+ break;
+ case IDO_ACT_PRINT_MSG:
+ printMessage(ARG(1));
+ offset += 2;
+ break;
+ case IDO_ACT_SET_LIGHT:
+ _state.isDark = false;
+ ++offset;
+ break;
+ case IDO_ACT_SET_DARK:
+ _state.isDark = true;
+ ++offset;
+ break;
+ case IDO_ACT_SAVE:
+ saveGameState(0, "");
+ ++offset;
+ break;
+ case IDO_ACT_LOAD:
+ loadGameState(0);
+ ++offset;
+ // Original engine does not jump out of the loop,
+ // so we don't either.
+ // We reset the restore flag, as the restore game
+ // process is complete
+ _isRestoring = false;
+ break;
+ case IDO_ACT_RESTART: {
+ _display->printString(_strings.playAgain);
+ Common::String input = inputString();
+ if (input.size() == 0 || input[0] != APPLECHAR('N')) {
+ _isRestarting = true;
+ _display->clear(0x00);
+ _display->updateHiResScreen();
+ restartGame();
+ return;
+ }
+ // Fall-through
+ }
+ case IDO_ACT_QUIT:
+ printMessage(_messageIds.thanksForPlaying);
+ quitGame();
+ return;
+ case IDO_ACT_PLACE_ITEM:
+ getItem(ARG(1)).room = ARG(2);
+ getItem(ARG(1)).position.x = ARG(3);
+ getItem(ARG(1)).position.y = ARG(4);
+ offset += 5;
+ break;
+ case IDO_ACT_SET_ITEM_PIC:
+ getItem(ARG(2)).picture = ARG(1);
+ offset += 3;
+ break;
+ case IDO_ACT_RESET_PIC:
+ getCurRoom().curPicture = getCurRoom().picture;
+ ++offset;
+ break;
+ case IDO_ACT_GO_NORTH:
+ case IDO_ACT_GO_SOUTH:
+ case IDO_ACT_GO_EAST:
+ case IDO_ACT_GO_WEST:
+ case IDO_ACT_GO_UP:
+ case IDO_ACT_GO_DOWN: {
+ byte room = getCurRoom().connections[ARG(0) - IDO_ACT_GO_NORTH];
+
+ if (room == 0) {
+ printMessage(_messageIds.cantGoThere);
+ return;
+ }
+
+ getCurRoom().curPicture = getCurRoom().picture;
+ _state.room = room;
+ return;
+ }
+ case IDO_ACT_TAKE_ITEM:
+ takeItem(noun);
+ ++offset;
+ break;
+ case IDO_ACT_DROP_ITEM:
+ dropItem(noun);
+ ++offset;
+ break;
+ case IDO_ACT_SET_ROOM_PIC:
+ getRoom(ARG(1)).picture = getRoom(ARG(1)).curPicture = ARG(2);
+ offset += 3;
+ break;
+ default:
+ error("Invalid action opcode %02x", ARG(0));
+ }
+ }
+}
+
+#undef ARG
+
+bool AdlEngine::doOneCommand(const Commands &commands, byte verb, byte noun) {
+ Commands::const_iterator cmd;
+
+ for (cmd = commands.begin(); cmd != commands.end(); ++cmd) {
+ uint offset = 0;
+ if (matchCommand(*cmd, verb, noun, &offset)) {
+ doActions(*cmd, noun, offset);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void AdlEngine::doAllCommands(const Commands &commands, byte verb, byte noun) {
+ Commands::const_iterator cmd;
+
+ for (cmd = commands.begin(); cmd != commands.end(); ++cmd) {
+ uint offset = 0;
+ if (matchCommand(*cmd, verb, noun, &offset))
+ doActions(*cmd, noun, offset);
+ }
+}
+
+} // End of namespace Adl
diff --git a/engines/adl/adl.h b/engines/adl/adl.h
new file mode 100644
index 0000000000..4ea7566669
--- /dev/null
+++ b/engines/adl/adl.h
@@ -0,0 +1,247 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ADL_ADL_H
+#define ADL_ADL_H
+
+#include "common/array.h"
+#include "common/rect.h"
+#include "common/str.h"
+
+#include "engines/engine.h"
+
+namespace Common {
+class ReadStream;
+class SeekableReadStream;
+}
+
+namespace Adl {
+
+class Display;
+struct AdlGameDescription;
+
+// Conditional opcodes
+#define IDO_CND_ITEM_IN_ROOM 0x03
+#define IDO_CND_MOVES_GE 0x05
+#define IDO_CND_VAR_EQ 0x06
+#define IDO_CND_CUR_PIC_EQ 0x09
+#define IDO_CND_ITEM_PIC_EQ 0x0a
+
+// Action opcodes
+#define IDO_ACT_VAR_ADD 0x01
+#define IDO_ACT_VAR_SUB 0x02
+#define IDO_ACT_VAR_SET 0x03
+#define IDO_ACT_LIST_ITEMS 0x04
+#define IDO_ACT_MOVE_ITEM 0x05
+#define IDO_ACT_SET_ROOM 0x06
+#define IDO_ACT_SET_CUR_PIC 0x07
+#define IDO_ACT_SET_PIC 0x08
+#define IDO_ACT_PRINT_MSG 0x09
+#define IDO_ACT_SET_LIGHT 0x0a
+#define IDO_ACT_SET_DARK 0x0b
+#define IDO_ACT_QUIT 0x0d
+#define IDO_ACT_SAVE 0x0f
+#define IDO_ACT_LOAD 0x10
+#define IDO_ACT_RESTART 0x11
+#define IDO_ACT_PLACE_ITEM 0x12
+#define IDO_ACT_SET_ITEM_PIC 0x13
+#define IDO_ACT_RESET_PIC 0x14
+#define IDO_ACT_GO_NORTH 0x15
+#define IDO_ACT_GO_SOUTH 0x16
+#define IDO_ACT_GO_EAST 0x17
+#define IDO_ACT_GO_WEST 0x18
+#define IDO_ACT_GO_UP 0x19
+#define IDO_ACT_GO_DOWN 0x1a
+#define IDO_ACT_TAKE_ITEM 0x1b
+#define IDO_ACT_DROP_ITEM 0x1c
+#define IDO_ACT_SET_ROOM_PIC 0x1d
+
+#define IDI_WORD_SIZE 8
+
+struct Room {
+ byte description;
+ byte connections[6];
+ byte picture;
+ byte curPicture;
+};
+
+struct Picture {
+ byte block;
+ uint16 offset;
+};
+
+struct Command {
+ byte room;
+ byte verb, noun;
+ byte numCond, numAct;
+ Common::Array<byte> script;
+};
+
+enum {
+ IDI_ITEM_NOT_MOVED,
+ IDI_ITEM_MOVED,
+ IDI_ITEM_DOESNT_MOVE
+};
+
+#define IDI_NONE 0xfe
+
+struct Item {
+ byte noun;
+ byte room;
+ byte picture;
+ bool isLineArt;
+ Common::Point position;
+ int state;
+ byte description;
+ Common::Array<byte> roomPictures;
+};
+
+struct State {
+ Common::Array<Room> rooms;
+ Common::Array<Item> items;
+ Common::Array<byte> vars;
+
+ byte room;
+ uint16 moves;
+ bool isDark;
+
+ State() : room(1), moves(0), isDark(false) { }
+};
+
+typedef Common::List<Command> Commands;
+typedef Common::HashMap<Common::String, uint> WordMap;
+
+class AdlEngine : public Engine {
+public:
+ virtual ~AdlEngine();
+
+protected:
+ AdlEngine(OSystem *syst, const AdlGameDescription *gd);
+
+ Common::String readString(Common::ReadStream &stream, byte until = 0) const;
+ Common::String readStringAt(Common::SeekableReadStream &stream, uint offset, byte until = 0) const;
+
+ virtual void printMessage(uint idx, bool wait = true) const;
+ void delay(uint32 ms) const;
+
+ Common::String inputString(byte prompt = 0) const;
+ byte inputKey() const;
+
+ void loadWords(Common::ReadStream &stream, WordMap &map) const;
+ void readCommands(Common::ReadStream &stream, Commands &commands);
+
+ Display *_display;
+
+ // Message strings in data file
+ Common::Array<Common::String> _messages;
+ // Picture data
+ Common::Array<Picture> _pictures;
+ // Dropped item screen offsets
+ Common::Array<Common::Point> _itemOffsets;
+ // Drawings consisting of horizontal and vertical lines only, but
+ // supporting scaling and rotation
+ Common::Array<Common::Array<byte> > _lineArt;
+ // <room, verb, noun, script> lists
+ Commands _roomCommands;
+ Commands _globalCommands;
+
+ WordMap _verbs;
+ WordMap _nouns;
+
+ struct {
+ Common::String enterCommand;
+ Common::String dontHaveIt;
+ Common::String gettingDark;
+ Common::String verbError;
+ Common::String nounError;
+ Common::String playAgain;
+ } _strings;
+
+ struct {
+ uint cantGoThere;
+ uint dontUnderstand;
+ uint itemDoesntMove;
+ uint itemNotHere;
+ uint thanksForPlaying;
+ } _messageIds;
+
+ // Game state
+ State _state;
+
+private:
+ virtual void runIntro() const { }
+ virtual void loadData() = 0;
+ virtual void initState() = 0;
+ virtual void restartGame() = 0;
+ virtual void drawPic(byte pic, Common::Point pos = Common::Point()) const = 0;
+
+ // Engine
+ Common::Error run();
+ bool hasFeature(EngineFeature f) const;
+ Common::Error loadGameState(int slot);
+ bool canLoadGameStateCurrently();
+ Common::Error saveGameState(int slot, const Common::String &desc);
+ bool canSaveGameStateCurrently();
+
+ // Text output
+ void wordWrap(Common::String &str) const;
+
+ // Text input
+ byte convertKey(uint16 ascii) const;
+ Common::String getLine() const;
+ Common::String getWord(const Common::String &line, uint &index) const;
+ void getInput(uint &verb, uint &noun);
+
+ // Graphics
+ void showRoom() const;
+ void clearScreen() const;
+ void drawItems() const;
+ void drawNextPixel(Common::Point &p, byte color, byte bits, byte quadrant) const;
+ void drawLineArt(const Common::Array<byte> &lineArt, const Common::Point &pos, byte rotation = 0, byte scaling = 1, byte color = 0x7f) const;
+
+ // Game state functions
+ const Room &getRoom(uint i) const;
+ Room &getRoom(uint i);
+ const Room &getCurRoom() const;
+ Room &getCurRoom();
+ const Item &getItem(uint i) const;
+ Item &getItem(uint i);
+ byte getVar(uint i) const;
+ void setVar(uint i, byte value);
+ void takeItem(byte noun);
+ void dropItem(byte noun);
+ bool matchCommand(const Command &command, byte verb, byte noun, uint *actions = nullptr) const;
+ void doActions(const Command &command, byte noun, byte offset);
+ bool doOneCommand(const Commands &commands, byte verb, byte noun);
+ void doAllCommands(const Commands &commands, byte verb, byte noun);
+
+ const AdlGameDescription *_gameDescription;
+ bool _isRestarting, _isRestoring;
+ byte _saveVerb, _saveNoun, _restoreVerb, _restoreNoun;
+ bool _canSaveNow, _canRestoreNow;
+};
+
+AdlEngine *HiRes1Engine__create(OSystem *syst, const AdlGameDescription *gd);
+
+} // End of namespace Adl
+
+#endif
diff --git a/engines/adl/configure.engine b/engines/adl/configure.engine
new file mode 100644
index 0000000000..844e2b8e6a
--- /dev/null
+++ b/engines/adl/configure.engine
@@ -0,0 +1,3 @@
+# This file is included from the main "configure" script
+# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
+add_engine adl "ADL" no
diff --git a/engines/adl/detection.cpp b/engines/adl/detection.cpp
new file mode 100644
index 0000000000..1a8c5025e8
--- /dev/null
+++ b/engines/adl/detection.cpp
@@ -0,0 +1,247 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/system.h"
+#include "common/savefile.h"
+#include "common/translation.h"
+
+#include "graphics/thumbnail.h"
+
+#include "engines/advancedDetector.h"
+
+#include "adl/detection.h"
+
+namespace Adl {
+
+#define GAMEOPTION_COLOR GUIO_GAMEOPTIONS1
+#define GAMEOPTION_SCANLINES GUIO_GAMEOPTIONS2
+
+static const ADExtraGuiOptionsMap optionsList[] = {
+ {
+ GAMEOPTION_COLOR,
+ {
+ _s("Color mode"),
+ _s("Use color graphics"),
+ "color",
+ false
+ }
+ },
+
+ {
+ GAMEOPTION_SCANLINES,
+ {
+ _s("Scanlines"),
+ _s("Show scanlines"),
+ "scanlines",
+ false
+ }
+ },
+
+ AD_EXTRA_GUI_OPTIONS_TERMINATOR
+};
+
+static const PlainGameDescriptor adlGames[] = {
+ {"hires1", "Hi-Res Adventure #1: Mystery House"},
+ {0, 0}
+};
+
+static const AdlGameDescription gameDescriptions[] = {
+ { // Hi-Res Adventure #1: Mystery House - Apple II - 1987 PD release
+ {
+ "hires1", 0,
+ {
+ {"ADVENTURE", 0, "22d9e63a11d69fa033ba1738715ad09a", 29952},
+ {"AUTO LOAD OBJ", 0, "23bfccfe9fcff9b22cf6c41bde9078ac", 12291},
+ {"MYSTERY.HELLO", 0, "2289b7fea300b506e902a4c597968369", 836},
+ AD_LISTEND
+ },
+ Common::EN_ANY,
+ Common::kPlatformApple2GS, // FIXME
+ ADGF_NO_FLAGS,
+ GUIO2(GAMEOPTION_COLOR, GAMEOPTION_SCANLINES)
+ },
+ GAME_TYPE_HIRES1
+ },
+ { AD_TABLE_END_MARKER, GAME_TYPE_NONE }
+};
+
+class AdlMetaEngine : public AdvancedMetaEngine {
+public:
+ AdlMetaEngine() : AdvancedMetaEngine(gameDescriptions, sizeof(AdlGameDescription), adlGames, optionsList) { }
+
+ const char *getName() const {
+ return "ADL";
+ }
+
+ const char *getOriginalCopyright() const {
+ return "Copyright (C) Sierra On-Line";
+ }
+
+ bool hasFeature(MetaEngineFeature f) const;
+ SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
+ int getMaximumSaveSlot() const { return 'O' - 'A'; }
+ SaveStateList listSaves(const char *target) const;
+ void removeSaveState(const char *target, int slot) const;
+
+ bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const;
+};
+
+bool AdlMetaEngine::hasFeature(MetaEngineFeature f) const {
+ switch(f) {
+ case kSupportsListSaves:
+ case kSupportsLoadingDuringStartup:
+ case kSupportsDeleteSave:
+ case kSavesSupportMetaInfo:
+ case kSavesSupportThumbnail:
+ case kSavesSupportCreationDate:
+ case kSavesSupportPlayTime:
+ return true;
+ default:
+ return false;
+ }
+}
+
+SaveStateDescriptor AdlMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+ Common::String fileName = Common::String::format("%s.s%02d", target, slot);
+ Common::InSaveFile *inFile = g_system->getSavefileManager()->openForLoading(fileName);
+
+ if (!inFile)
+ return SaveStateDescriptor();
+
+ if (inFile->readUint32BE() != MKTAG('A', 'D', 'L', ':')) {
+ delete inFile;
+ return SaveStateDescriptor();
+ }
+
+ byte saveVersion = inFile->readByte();
+ if (saveVersion != SAVEGAME_VERSION) {
+ delete inFile;
+ return SaveStateDescriptor();
+ }
+
+ char name[SAVEGAME_NAME_LEN] = { };
+ inFile->read(name, sizeof(name) - 1);
+ inFile->readByte();
+
+ if (inFile->eos() || inFile->err()) {
+ delete inFile;
+ return SaveStateDescriptor();
+ }
+
+ SaveStateDescriptor sd(slot, name);
+
+ int year = inFile->readUint16BE();
+ int month = inFile->readByte();
+ int day = inFile->readByte();
+ sd.setSaveDate(year + 1900, month + 1, day);
+
+ int hour = inFile->readByte();
+ int minutes = inFile->readByte();
+ sd.setSaveTime(hour, minutes);
+
+ uint32 playTime = inFile->readUint32BE();
+ sd.setPlayTime(playTime);
+
+ if (inFile->eos() || inFile->err()) {
+ delete inFile;
+ return SaveStateDescriptor();
+ }
+
+ Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*inFile);
+ sd.setThumbnail(thumbnail);
+
+ delete inFile;
+ return sd;
+}
+
+SaveStateList AdlMetaEngine::listSaves(const char *target) const {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Common::StringArray files = saveFileMan->listSavefiles(Common::String(target) + ".s##");
+
+ SaveStateList saveList;
+
+ for (uint i = 0; i < files.size(); ++i) {
+ const Common::String &fileName = files[i];
+ Common::InSaveFile *inFile = saveFileMan->openForLoading(fileName);
+ if (!inFile) {
+ warning("Cannot open save file '%s'", fileName.c_str());
+ continue;
+ }
+
+ if (inFile->readUint32BE() != MKTAG('A', 'D', 'L', ':')) {
+ warning("No header found in '%s'", fileName.c_str());
+ delete inFile;
+ continue;
+ }
+
+ byte saveVersion = inFile->readByte();
+ if (saveVersion != SAVEGAME_VERSION) {
+ warning("Unsupported save game version %i found in '%s'", saveVersion, fileName.c_str());
+ delete inFile;
+ continue;
+ }
+
+ char name[SAVEGAME_NAME_LEN] = { };
+ inFile->read(name, sizeof(name) - 1);
+ delete inFile;
+
+ int slotNum = atoi(fileName.c_str() + fileName.size() - 2);
+ SaveStateDescriptor sd(slotNum, name);
+ saveList.push_back(sd);
+ }
+
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
+ return saveList;
+}
+
+void AdlMetaEngine::removeSaveState(const char *target, int slot) const {
+ Common::String fileName = Common::String::format("%s.s%02d", target, slot);
+ g_system->getSavefileManager()->removeSavefile(fileName);
+}
+
+Engine *HiRes1Engine_create(OSystem *syst, const AdlGameDescription *gd);
+
+bool AdlMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const {
+ if (!gd)
+ return false;
+
+ const AdlGameDescription *adlGd = (const AdlGameDescription *)gd;
+
+ switch (adlGd->gameType) {
+ case GAME_TYPE_HIRES1:
+ *engine = HiRes1Engine_create(syst, adlGd);
+ break;
+ default:
+ error("Unknown GameType");
+ }
+
+ return true;
+}
+
+} // End of namespace Adl
+
+#if PLUGIN_ENABLED_DYNAMIC(ADL)
+ REGISTER_PLUGIN_DYNAMIC(ADL, PLUGIN_TYPE_ENGINE, Adl::AdlMetaEngine);
+#else
+ REGISTER_PLUGIN_STATIC(ADL, PLUGIN_TYPE_ENGINE, Adl::AdlMetaEngine);
+#endif
diff --git a/engines/adl/detection.h b/engines/adl/detection.h
new file mode 100644
index 0000000000..c646aeb5b9
--- /dev/null
+++ b/engines/adl/detection.h
@@ -0,0 +1,45 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ADL_DETECTION_H
+#define ADL_DETECTION_H
+
+#include "engines/advancedDetector.h"
+
+namespace Adl {
+
+#define SAVEGAME_VERSION 0
+#define SAVEGAME_NAME_LEN 32
+
+enum GameType {
+ GAME_TYPE_NONE,
+ GAME_TYPE_HIRES1
+};
+
+struct AdlGameDescription {
+ ADGameDescription desc;
+ GameType gameType;
+};
+
+} // End of namespace Adl
+
+#endif
diff --git a/engines/adl/display.cpp b/engines/adl/display.cpp
new file mode 100644
index 0000000000..6342504bc3
--- /dev/null
+++ b/engines/adl/display.cpp
@@ -0,0 +1,520 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/stream.h"
+#include "common/rect.h"
+#include "common/system.h"
+#include "common/str.h"
+#include "common/config-manager.h"
+
+#include "graphics/surface.h"
+#include "graphics/palette.h"
+#include "graphics/thumbnail.h"
+
+#include "engines/util.h"
+
+#include "adl/display.h"
+
+namespace Adl {
+
+// This implements the Apple II "Hi-Res" display mode
+
+#define DISPLAY_PITCH (DISPLAY_WIDTH / 7)
+#define DISPLAY_SIZE (DISPLAY_PITCH * DISPLAY_HEIGHT)
+
+#define TEXT_WIDTH 40
+#define TEXT_HEIGHT 24
+#define TEXT_BUF_SIZE (TEXT_WIDTH * TEXT_HEIGHT)
+
+#define COLOR_PALETTE_ENTRIES 8
+static const byte colorPalette[COLOR_PALETTE_ENTRIES * 3] = {
+ 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff,
+ 0xc7, 0x34, 0xff,
+ 0x38, 0xcb, 0x00,
+ 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff,
+ 0x0d, 0xa1, 0xff,
+ 0xf2, 0x5e, 0x00
+};
+
+// Corresponding color in second palette
+#define PAL2(X) ((X) | 0x04)
+
+// Alternate color for odd pixel rows (for scanlines)
+#define ALTCOL(X) ((X) | 0x08)
+
+// Green monochrome palette
+#define MONO_PALETTE_ENTRIES 2
+static const byte monoPalette[MONO_PALETTE_ENTRIES * 3] = {
+ 0x00, 0x00, 0x00,
+ 0x00, 0xc0, 0x01
+};
+
+// Uppercase-only Apple II font (manually created).
+static const byte font[64][5] = {
+ { 0x7c, 0x82, 0xba, 0xb2, 0x9c }, { 0xf8, 0x24, 0x22, 0x24, 0xf8 }, // @A
+ { 0xfe, 0x92, 0x92, 0x92, 0x6c }, { 0x7c, 0x82, 0x82, 0x82, 0x44 }, // BC
+ { 0xfe, 0x82, 0x82, 0x82, 0x7c }, { 0xfe, 0x92, 0x92, 0x92, 0x82 }, // DE
+ { 0xfe, 0x12, 0x12, 0x12, 0x02 }, { 0x7c, 0x82, 0x82, 0xa2, 0xe2 }, // FG
+ { 0xfe, 0x10, 0x10, 0x10, 0xfe }, { 0x00, 0x82, 0xfe, 0x82, 0x00 }, // HI
+ { 0x40, 0x80, 0x80, 0x80, 0x7e }, { 0xfe, 0x10, 0x28, 0x44, 0x82 }, // JK
+ { 0xfe, 0x80, 0x80, 0x80, 0x80 }, { 0xfe, 0x04, 0x18, 0x04, 0xfe }, // LM
+ { 0xfe, 0x08, 0x10, 0x20, 0xfe }, { 0x7c, 0x82, 0x82, 0x82, 0x7c }, // NO
+ { 0xfe, 0x12, 0x12, 0x12, 0x0c }, { 0x7c, 0x82, 0xa2, 0x42, 0xbc }, // PQ
+ { 0xfe, 0x12, 0x32, 0x52, 0x8c }, { 0x4c, 0x92, 0x92, 0x92, 0x64 }, // RS
+ { 0x02, 0x02, 0xfe, 0x02, 0x02 }, { 0x7e, 0x80, 0x80, 0x80, 0x7e }, // TU
+ { 0x3e, 0x40, 0x80, 0x40, 0x3e }, { 0xfe, 0x40, 0x30, 0x40, 0xfe }, // VW
+ { 0xc6, 0x28, 0x10, 0x28, 0xc6 }, { 0x06, 0x08, 0xf0, 0x08, 0x06 }, // XY
+ { 0xc2, 0xa2, 0x92, 0x8a, 0x86 }, { 0xfe, 0xfe, 0x82, 0x82, 0x82 }, // Z[
+ { 0x04, 0x08, 0x10, 0x20, 0x40 }, { 0x82, 0x82, 0x82, 0xfe, 0xfe }, // \]
+ { 0x20, 0x10, 0x08, 0x10, 0x20 }, { 0x80, 0x80, 0x80, 0x80, 0x80 }, // ^_
+ { 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0xbe, 0x00, 0x00 }, // !
+ { 0x00, 0x0e, 0x00, 0x0e, 0x00 }, { 0x28, 0xfe, 0x28, 0xfe, 0x28 }, // "#
+ { 0x48, 0x54, 0xfe, 0x54, 0x24 }, { 0x46, 0x26, 0x10, 0xc8, 0xc4 }, // $%
+ { 0x6c, 0x92, 0xac, 0x40, 0xa0 }, { 0x00, 0x00, 0x0e, 0x00, 0x00 }, // &'
+ { 0x38, 0x44, 0x82, 0x00, 0x00 }, { 0x00, 0x00, 0x82, 0x44, 0x38 }, // ()
+ { 0x44, 0x28, 0xfe, 0x28, 0x44 }, { 0x10, 0x10, 0x7c, 0x10, 0x10 }, // *+
+ { 0x00, 0x80, 0x60, 0x00, 0x00 }, { 0x10, 0x10, 0x10, 0x10, 0x10 }, // ,-
+ { 0x00, 0x00, 0x80, 0x00, 0x00 }, { 0x40, 0x20, 0x10, 0x08, 0x04 }, // ./
+ { 0x7c, 0xa2, 0x92, 0x8a, 0x7c }, { 0x00, 0x84, 0xfe, 0x80, 0x00 }, // 01
+ { 0xc4, 0xa2, 0x92, 0x92, 0x8c }, { 0x42, 0x82, 0x92, 0x9a, 0x66 }, // 23
+ { 0x30, 0x28, 0x24, 0xfe, 0x20 }, { 0x4e, 0x8a, 0x8a, 0x8a, 0x72 }, // 45
+ { 0x78, 0x94, 0x92, 0x92, 0x62 }, { 0x02, 0xe2, 0x12, 0x0a, 0x06 }, // 67
+ { 0x6c, 0x92, 0x92, 0x92, 0x6c }, { 0x8c, 0x92, 0x92, 0x52, 0x3c }, // 89
+ { 0x00, 0x00, 0x28, 0x00, 0x00 }, { 0x00, 0x80, 0x68, 0x00, 0x00 }, // :;
+ { 0x10, 0x28, 0x44, 0x82, 0x00 }, { 0x28, 0x28, 0x28, 0x28, 0x28 }, // <=
+ { 0x00, 0x82, 0x44, 0x28, 0x10 }, { 0x04, 0x02, 0xb2, 0x0a, 0x04 } // >?
+};
+
+Display::Display() :
+ _mode(DISPLAY_MODE_TEXT),
+ _cursorPos(0),
+ _showCursor(false) {
+
+ initGraphics(DISPLAY_WIDTH * 2, DISPLAY_HEIGHT * 2, true);
+
+ _monochrome = !ConfMan.getBool("color");
+ _scanlines = ConfMan.getBool("scanlines");
+
+ if (_monochrome)
+ g_system->getPaletteManager()->setPalette(monoPalette, 0, MONO_PALETTE_ENTRIES);
+ else
+ g_system->getPaletteManager()->setPalette(colorPalette, 0, COLOR_PALETTE_ENTRIES);
+
+ showScanlines(_scanlines);
+
+ _frameBuf = new byte[DISPLAY_SIZE];
+ memset(_frameBuf, 0, DISPLAY_SIZE);
+ _frameBufSurface = new Graphics::Surface;
+ // We need 2x scaling to properly render the half-pixel shift
+ // of the second palette
+ _frameBufSurface->create(DISPLAY_WIDTH * 2, DISPLAY_HEIGHT * 2, Graphics::PixelFormat::createFormatCLUT8());
+
+ _textBuf = new byte[TEXT_BUF_SIZE];
+ memset(_textBuf, APPLECHAR(' '), TEXT_BUF_SIZE);
+ _textBufSurface = new Graphics::Surface;
+ // For ease of copying, also use 2x scaling here
+ _textBufSurface->create(DISPLAY_WIDTH * 2, DISPLAY_HEIGHT * 2, Graphics::PixelFormat::createFormatCLUT8());
+
+ createFont();
+}
+
+Display::~Display() {
+ delete[] _frameBuf;
+ _frameBufSurface->free();
+ delete _frameBufSurface;
+
+ delete[] _textBuf;
+ _textBufSurface->free();
+ delete _textBufSurface;
+
+ _font->free();
+ delete _font;
+}
+
+void Display::setMode(DisplayMode mode) {
+ _mode = mode;
+
+ if (_mode == DISPLAY_MODE_TEXT || _mode == DISPLAY_MODE_MIXED)
+ updateTextScreen();
+ if (_mode == DISPLAY_MODE_HIRES || _mode == DISPLAY_MODE_MIXED)
+ updateHiResScreen();
+}
+
+void Display::updateTextScreen() {
+ updateTextSurface();
+
+ if (_mode == DISPLAY_MODE_TEXT)
+ g_system->copyRectToScreen(_textBufSurface->getPixels(), _textBufSurface->pitch, 0, 0, _textBufSurface->w, _textBufSurface->h);
+ else if (_mode == DISPLAY_MODE_MIXED)
+ g_system->copyRectToScreen(_textBufSurface->getBasePtr(0, _textBufSurface->h - 4 * 8 * 2), _textBufSurface->pitch, 0, _textBufSurface->h - 4 * 8 * 2, _textBufSurface->w, 4 * 8 * 2);
+
+ g_system->updateScreen();
+}
+
+void Display::updateHiResScreen() {
+ updateHiResSurface();
+
+ if (_mode == DISPLAY_MODE_HIRES)
+ g_system->copyRectToScreen(_frameBufSurface->getPixels(), _frameBufSurface->pitch, 0, 0, _frameBufSurface->w, _frameBufSurface->h);
+ else if (_mode == DISPLAY_MODE_MIXED)
+ g_system->copyRectToScreen(_frameBufSurface->getPixels(), _frameBufSurface->pitch, 0, 0, _frameBufSurface->w, _frameBufSurface->h - 4 * 8 * 2);
+
+ g_system->updateScreen();
+}
+
+bool Display::saveThumbnail(Common::WriteStream &out) {
+ if (_scanlines) {
+ showScanlines(false);
+ g_system->updateScreen();
+ }
+
+ bool retval = Graphics::saveThumbnail(out);
+
+ if (_scanlines) {
+ showScanlines(true);
+ g_system->updateScreen();
+ }
+
+ return retval;
+}
+
+void Display::loadFrameBuffer(Common::ReadStream &stream) {
+ byte *dst = _frameBuf;
+ for (uint j = 0; j < 8; ++j) {
+ for (uint i = 0; i < 8; ++i) {
+ stream.read(dst, DISPLAY_PITCH);
+ dst += DISPLAY_PITCH * 64;
+ stream.read(dst, DISPLAY_PITCH);
+ dst += DISPLAY_PITCH * 64;
+ stream.read(dst, DISPLAY_PITCH);
+ stream.readUint32LE();
+ stream.readUint32LE();
+ dst -= DISPLAY_PITCH * 120;
+ }
+ dst -= DISPLAY_PITCH * 63;
+ }
+
+ if (stream.eos() || stream.err())
+ error("Failed to read frame buffer");
+}
+
+void Display::putPixel(const Common::Point &p, byte color) {
+ byte offset = p.x / 7;
+
+ if (offset & 1) {
+ byte c = color << 1;
+ if (c >= 0x40 && c < 0xc0)
+ color ^= 0x7f;
+ }
+
+ byte *b = _frameBuf + p.y * DISPLAY_PITCH + offset;
+ color ^= *b;
+ color &= 1 << (p.x % 7);
+ *b ^= color;
+}
+
+void Display::clear(byte color) {
+ byte val = 0;
+
+ byte c = color << 1;
+ if (c >= 0x40 && c < 0xc0)
+ val = 0x7f;
+
+ for (uint i = 0; i < DISPLAY_SIZE; ++i) {
+ _frameBuf[i] = color;
+ color ^= val;
+ }
+}
+
+void Display::home() {
+ memset(_textBuf, APPLECHAR(' '), TEXT_BUF_SIZE);
+ _cursorPos = 0;
+}
+
+void Display::moveCursorForward() {
+ ++_cursorPos;
+
+ if (_cursorPos >= TEXT_BUF_SIZE)
+ scrollUp();
+}
+
+void Display::moveCursorBackward() {
+ if (_cursorPos > 0)
+ --_cursorPos;
+}
+
+void Display::moveCursorTo(const Common::Point &pos) {
+ _cursorPos = pos.y * TEXT_WIDTH + pos.x;
+
+ if (_cursorPos >= TEXT_BUF_SIZE)
+ error("Cursor position (%i, %i) out of bounds", pos.x, pos.y);
+}
+
+void Display::printString(const Common::String &str) {
+ Common::String::const_iterator c;
+ for (c = str.begin(); c != str.end(); ++c) {
+ byte b = *c;
+
+ if (*c == APPLECHAR('\r'))
+ _cursorPos = (_cursorPos / TEXT_WIDTH + 1) * TEXT_WIDTH;
+ else if (b < 0x80 || b >= 0xa0) {
+ setCharAtCursor(b);
+ ++_cursorPos;
+ }
+
+ if (_cursorPos == TEXT_BUF_SIZE)
+ scrollUp();
+ }
+
+ updateTextScreen();
+}
+
+void Display::printAsciiString(const Common::String &str) {
+ Common::String aStr;
+
+ Common::String::const_iterator it;
+ for (it = str.begin(); it != str.end(); ++it)
+ aStr += APPLECHAR(*it);
+
+ printString(aStr);
+}
+
+void Display::setCharAtCursor(byte c) {
+ _textBuf[_cursorPos] = c;
+}
+
+void Display::showCursor(bool enable) {
+ _showCursor = enable;
+}
+
+void Display::showScanlines(bool enable) {
+ byte pal[COLOR_PALETTE_ENTRIES * 3] = { };
+
+ if (enable)
+ g_system->getPaletteManager()->setPalette(pal, COLOR_PALETTE_ENTRIES, COLOR_PALETTE_ENTRIES);
+ else {
+ g_system->getPaletteManager()->grabPalette(pal, 0, COLOR_PALETTE_ENTRIES);
+ g_system->getPaletteManager()->setPalette(pal, COLOR_PALETTE_ENTRIES, COLOR_PALETTE_ENTRIES);
+ }
+}
+
+static byte processColorBits(uint16 &bits, bool &odd, bool secondPal) {
+ byte color = 0;
+
+ switch (bits & 0x7) {
+ case 0x3: // 011 (white)
+ case 0x6: // 110
+ case 0x7: // 111
+ color = 1;
+ break;
+ case 0x2: // 010 (color)
+ color = 2 + odd;
+ break;
+ case 0x5: // 101 (color)
+ color = 2 + !odd;
+ }
+
+ if (secondPal)
+ color = PAL2(color);
+
+ odd = !odd;
+ bits >>= 1;
+
+ return color;
+}
+
+static void renderPixelRowColor(byte *dst, byte *src) {
+ uint16 bits = (src[0] & 0x7f) << 1;
+ byte pal = src[0] >> 7;
+
+ if (pal != 0)
+ *dst++ = 0;
+
+ bool odd = false;
+
+ for (uint i = 0; i < DISPLAY_PITCH; ++i) {
+ if (i != DISPLAY_PITCH - 1) {
+ bits |= (src[i + 1] & 0x7f) << 8;
+ pal |= (src[i + 1] >> 7) << 1;
+ }
+
+ // For the first 6 bits in the block we draw two pixels
+ for (uint j = 0; j < 6; ++j) {
+ byte color = processColorBits(bits, odd, pal & 1);
+ *dst++ = color;
+ *dst++ = color;
+ }
+
+ // Last bit of the block, draw one, two or three pixels
+ byte color = processColorBits(bits, odd, pal & 1);
+
+ // Draw the first pixel
+ *dst++ = color;
+
+ switch (pal) {
+ case 0x0:
+ case 0x3:
+ // If palette stays the same, draw a second pixel
+ *dst++ = color;
+ break;
+ case 0x2:
+ // If we're moving from first to second palette,
+ // draw a second pixel, and a third in the second
+ // palette.
+ *dst++ = color;
+ *dst++ = PAL2(color);
+ }
+
+ pal >>= 1;
+ }
+}
+
+static void renderPixelRowMono(byte *dst, byte *src) {
+ byte pal = src[0] >> 7;
+
+ if (pal != 0)
+ *dst++ = 0;
+
+ for (uint i = 0; i < DISPLAY_PITCH; ++i) {
+ if (i != DISPLAY_PITCH - 1)
+ pal |= (src[i + 1] >> 7) << 1;
+
+ for (uint j = 0; j < 6; ++j) {
+ bool color = src[i] & (1 << j);
+ *dst++ = color;
+ *dst++ = color;
+ }
+
+ bool color = src[i] & (1 << 6);
+
+ *dst++ = color;
+
+ switch (pal) {
+ case 0x0:
+ case 0x3:
+ *dst++ = color;
+ break;
+ case 0x2:
+ *dst++ = color;
+ *dst++ = color;
+ }
+
+ pal >>= 1;
+ }
+}
+
+static void copyEvenSurfaceRows(Graphics::Surface &surf) {
+ byte *src = (byte *)surf.getPixels();
+
+ for (uint y = 0; y < surf.h / 2; ++y) {
+ byte *dst = src + surf.pitch;
+ for (uint x = 0; x < surf.w; ++x)
+ dst[x] = ALTCOL(src[x]);
+ src += surf.pitch * 2;
+ }
+}
+
+void Display::updateHiResSurface() {
+ byte *src = _frameBuf;
+ byte *dst = (byte *)_frameBufSurface->getPixels();
+
+ for (uint i = 0; i < DISPLAY_HEIGHT; ++i) {
+ if (_monochrome)
+ renderPixelRowMono(dst, src);
+ else
+ renderPixelRowColor(dst, src);
+ src += DISPLAY_PITCH;
+ dst += _frameBufSurface->pitch * 2;
+ }
+
+ copyEvenSurfaceRows(*_frameBufSurface);
+}
+
+void Display::updateTextSurface() {
+ for (uint row = 0; row < 24; ++row)
+ for (uint col = 0; col < TEXT_WIDTH; ++col) {
+ uint charPos = row * TEXT_WIDTH + col;
+ char c = _textBuf[row * TEXT_WIDTH + col];
+
+ if (charPos == _cursorPos && _showCursor)
+ c = (c & 0x3f) | 0x40;
+
+ Common::Rect r(7 * 2, 8 * 2);
+ r.translate(((c & 0x3f) % 16) * 7 * 2, (c & 0x3f) / 16 * 8 * 2);
+
+ if (!(c & 0x80)) {
+ if (!(c & 0x40) || ((g_system->getMillis() / 270) & 1))
+ r.translate(0, 4 * 8 * 2);
+ }
+
+ _textBufSurface->copyRectToSurface(*_font, col * 7 * 2, row * 8 * 2, r);
+ }
+}
+
+void Display::drawChar(byte c, int x, int y) {
+ byte *buf = (byte *)_font->getPixels() + y * _font->pitch + x;
+
+ for (uint row = 0; row < 8; ++row) {
+ for (uint col = 1; col < 6; ++col) {
+ if (font[c][col - 1] & (1 << row)) {
+ buf[col * 2] = 1;
+ buf[col * 2 + 1] = 1;
+ }
+ }
+
+ buf += 2 * _font->pitch;
+ }
+}
+
+void Display::createFont() {
+ _font = new Graphics::Surface;
+ _font->create(16 * 7 * 2, 4 * 8 * 2 * 2, Graphics::PixelFormat::createFormatCLUT8());
+
+ for (uint i = 0; i < 4; ++i)
+ for (uint j = 0; j < 16; ++j)
+ drawChar(i * 16 + j, j * 7 * 2, i * 8 * 2);
+
+ // Create inverted font
+ byte *buf = (byte *)_font->getPixels();
+ byte *bufInv = buf + (_font->h / 2) * _font->pitch;
+
+ for (uint row = 0; row < _font->h / 2; row += 2) {
+ for (uint col = 0; col < _font->w; ++col)
+ bufInv[col] = (buf[col] ? 0 : 1);
+
+ buf += _font->pitch * 2;
+ bufInv += _font->pitch * 2;
+ }
+
+ copyEvenSurfaceRows(*_font);
+}
+
+void Display::scrollUp() {
+ memmove(_textBuf, _textBuf + TEXT_WIDTH, TEXT_BUF_SIZE - TEXT_WIDTH);
+ memset(_textBuf + TEXT_BUF_SIZE - TEXT_WIDTH, APPLECHAR(' '), TEXT_WIDTH);
+ if (_cursorPos >= TEXT_WIDTH)
+ _cursorPos -= TEXT_WIDTH;
+}
+
+} // End of namespace Adl
diff --git a/engines/adl/display.h b/engines/adl/display.h
new file mode 100644
index 0000000000..ff01e3faf2
--- /dev/null
+++ b/engines/adl/display.h
@@ -0,0 +1,102 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ADL_DISPLAY_H
+#define ADL_DISPLAY_H
+
+#include <common/types.h>
+
+namespace Common {
+class ReadStream;
+class WriteStream;
+class String;
+struct Point;
+}
+
+namespace Graphics {
+struct Surface;
+}
+
+namespace Adl {
+
+#define DISPLAY_WIDTH 280
+#define DISPLAY_HEIGHT 192
+
+enum DisplayMode {
+ DISPLAY_MODE_HIRES,
+ DISPLAY_MODE_TEXT,
+ DISPLAY_MODE_MIXED
+};
+
+#define APPLECHAR(C) ((char)((C) | 0x80))
+
+class Display {
+public:
+ Display();
+ ~Display();
+
+ void setMode(DisplayMode mode);
+ void updateTextScreen();
+ void updateHiResScreen();
+ bool saveThumbnail(Common::WriteStream &out);
+
+ // Graphics
+ void loadFrameBuffer(Common::ReadStream &stream);
+ void putPixel(const Common::Point &p, byte color);
+ void clear(byte color);
+
+ // Text
+ void home();
+ void moveCursorTo(const Common::Point &pos);
+ void moveCursorForward();
+ void moveCursorBackward();
+ void printString(const Common::String &str);
+ void printAsciiString(const Common::String &str);
+ void setCharAtCursor(byte c);
+ void showCursor(bool enable);
+
+private:
+ void updateHiResSurface();
+ void showScanlines(bool enable);
+
+ void updateTextSurface();
+ void drawChar(byte c, int x, int y);
+ void createFont();
+ void scrollUp();
+
+ DisplayMode _mode;
+
+ byte *_frameBuf;
+ Graphics::Surface *_frameBufSurface;
+ bool _scanlines;
+ bool _monochrome;
+
+ byte *_textBuf;
+ Graphics::Surface *_textBufSurface;
+ Graphics::Surface *_font;
+ uint _cursorPos;
+ bool _showCursor;
+};
+
+} // End of namespace Adl
+
+#endif
diff --git a/engines/adl/hires1.cpp b/engines/adl/hires1.cpp
new file mode 100644
index 0000000000..6e1e31df9f
--- /dev/null
+++ b/engines/adl/hires1.cpp
@@ -0,0 +1,396 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/system.h"
+#include "common/debug.h"
+#include "common/error.h"
+#include "common/file.h"
+#include "common/stream.h"
+
+#include "adl/hires1.h"
+#include "adl/display.h"
+
+namespace Adl {
+
+void HiRes1Engine::runIntro() const {
+ Common::File file;
+
+ if (!file.open(IDS_HR1_EXE_0))
+ error("Failed to open file '" IDS_HR1_EXE_0 "'");
+
+ file.seek(IDI_HR1_OFS_LOGO_0);
+ _display->setMode(DISPLAY_MODE_HIRES);
+ _display->loadFrameBuffer(file);
+ _display->updateHiResScreen();
+ delay(4000);
+
+ if (shouldQuit())
+ return;
+
+ _display->setMode(DISPLAY_MODE_TEXT);
+
+ Common::File basic;
+ if (!basic.open(IDS_HR1_LOADER))
+ error("Failed to open file '" IDS_HR1_LOADER "'");
+
+ Common::String str;
+
+ str = readStringAt(basic, IDI_HR1_OFS_PD_TEXT_0, '"');
+ _display->printAsciiString(str + '\r');
+
+ str = readStringAt(basic, IDI_HR1_OFS_PD_TEXT_1, '"');
+ _display->printAsciiString(str + "\r\r");
+
+ str = readStringAt(basic, IDI_HR1_OFS_PD_TEXT_2, '"');
+ _display->printAsciiString(str + "\r\r");
+
+ str = readStringAt(basic, IDI_HR1_OFS_PD_TEXT_3, '"');
+ _display->printAsciiString(str + '\r');
+
+ inputKey();
+ if (g_engine->shouldQuit())
+ return;
+
+ _display->setMode(DISPLAY_MODE_MIXED);
+
+ str = readStringAt(file, IDI_HR1_OFS_GAME_OR_HELP);
+
+ bool instructions = false;
+
+ while (1) {
+ _display->printString(str);
+ Common::String s = inputString();
+
+ if (g_engine->shouldQuit())
+ break;
+
+ if (s.empty())
+ continue;
+
+ if (s[0] == APPLECHAR('I')) {
+ instructions = true;
+ break;
+ } else if (s[0] == APPLECHAR('G')) {
+ break;
+ }
+ };
+
+ if (instructions) {
+ _display->setMode(DISPLAY_MODE_TEXT);
+ file.seek(IDI_HR1_OFS_INTRO_TEXT);
+
+ const uint pages[] = { 6, 6, 4, 5, 8, 7, 0 };
+
+ uint page = 0;
+ while (pages[page] != 0) {
+ _display->home();
+
+ uint count = pages[page++];
+ for (uint i = 0; i < count; ++i) {
+ str = readString(file);
+ _display->printString(str);
+ file.seek(3, SEEK_CUR);
+ }
+
+ inputString();
+
+ if (g_engine->shouldQuit())
+ return;
+
+ file.seek(6, SEEK_CUR);
+ }
+ }
+
+ _display->printAsciiString("\r");
+
+ file.close();
+
+ _display->setMode(DISPLAY_MODE_MIXED);
+
+ if (!file.open(IDS_HR1_EXE_1))
+ error("Failed to open file '" IDS_HR1_EXE_1 "'");
+
+ // Title screen shown during loading
+ file.seek(IDI_HR1_OFS_LOGO_1);
+ _display->loadFrameBuffer(file);
+ _display->updateHiResScreen();
+ delay(2000);
+}
+
+void HiRes1Engine::loadData() {
+ Common::File f;
+
+ if (!f.open(IDS_HR1_MESSAGES))
+ error("Failed to open file '" IDS_HR1_MESSAGES "'");
+
+ for (uint i = 0; i < IDI_HR1_NUM_MESSAGES; ++i)
+ _messages.push_back(readString(f, APPLECHAR('\r')) + APPLECHAR('\r'));
+
+ f.close();
+
+ if (!f.open(IDS_HR1_EXE_1))
+ error("Failed to open file '" IDS_HR1_EXE_1 "'");
+
+ // Some messages have overrides inside the executable
+ _messages[IDI_HR1_MSG_CANT_GO_THERE - 1] = readStringAt(f, IDI_HR1_OFS_STR_CANT_GO_THERE);
+ _messages[IDI_HR1_MSG_DONT_HAVE_IT - 1] = readStringAt(f, IDI_HR1_OFS_STR_DONT_HAVE_IT);
+ _messages[IDI_HR1_MSG_DONT_UNDERSTAND - 1] = readStringAt(f, IDI_HR1_OFS_STR_DONT_UNDERSTAND);
+ _messages[IDI_HR1_MSG_GETTING_DARK - 1] = readStringAt(f, IDI_HR1_OFS_STR_GETTING_DARK);
+
+ // Load other strings from executable
+ _strings.enterCommand = readStringAt(f, IDI_HR1_OFS_STR_ENTER_COMMAND);
+ _strings.dontHaveIt = readStringAt(f, IDI_HR1_OFS_STR_DONT_HAVE_IT);
+ _strings.gettingDark = readStringAt(f, IDI_HR1_OFS_STR_GETTING_DARK);
+ _strings.verbError = readStringAt(f, IDI_HR1_OFS_STR_VERB_ERROR);
+ _strings.nounError = readStringAt(f, IDI_HR1_OFS_STR_NOUN_ERROR);
+ _strings.playAgain = readStringAt(f, IDI_HR1_OFS_STR_PLAY_AGAIN);
+ _gameStrings.pressReturn = readStringAt(f, IDI_HR1_OFS_STR_PRESS_RETURN);
+
+ // Set message IDs
+ _messageIds.cantGoThere = IDI_HR1_MSG_CANT_GO_THERE;
+ _messageIds.dontUnderstand = IDI_HR1_MSG_DONT_UNDERSTAND;
+ _messageIds.itemDoesntMove = IDI_HR1_MSG_ITEM_DOESNT_MOVE;
+ _messageIds.itemNotHere = IDI_HR1_MSG_ITEM_NOT_HERE;
+ _messageIds.thanksForPlaying = IDI_HR1_MSG_THANKS_FOR_PLAYING;
+
+ // Load picture data from executable
+ f.seek(IDI_HR1_OFS_PICS);
+ for (uint i = 0; i < IDI_HR1_NUM_PICS; ++i) {
+ struct Picture pic;
+ pic.block = f.readByte();
+ pic.offset = f.readUint16LE();
+ _pictures.push_back(pic);
+ }
+
+ // Load commands from executable
+ f.seek(IDI_HR1_OFS_CMDS_1);
+ readCommands(f, _roomCommands);
+
+ f.seek(IDI_HR1_OFS_CMDS_0);
+ readCommands(f, _globalCommands);
+
+ // Load dropped item offsets
+ f.seek(IDI_HR1_OFS_ITEM_OFFSETS);
+ for (uint i = 0; i < IDI_HR1_NUM_ITEM_OFFSETS; ++i) {
+ Common::Point p;
+ p.x = f.readByte();
+ p.y = f.readByte();
+ _itemOffsets.push_back(p);
+ }
+
+ // Load right-angle line art
+ f.seek(IDI_HR1_OFS_LINE_ART);
+ uint16 lineArtTotal = f.readUint16LE();
+ for (uint i = 0; i < lineArtTotal; ++i) {
+ f.seek(IDI_HR1_OFS_LINE_ART + 2 + i * 2);
+ uint16 offset = f.readUint16LE();
+ f.seek(IDI_HR1_OFS_LINE_ART + offset);
+
+ Common::Array<byte> lineArt;
+ byte b = f.readByte();
+ while (b != 0) {
+ lineArt.push_back(b);
+ b = f.readByte();
+ }
+ _lineArt.push_back(lineArt);
+ }
+
+ if (f.eos() || f.err())
+ error("Failed to read game data from '" IDS_HR1_EXE_1 "'");
+
+ f.seek(IDI_HR1_OFS_VERBS);
+ loadWords(f, _verbs);
+
+ f.seek(IDI_HR1_OFS_NOUNS);
+ loadWords(f, _nouns);
+}
+
+void HiRes1Engine::initState() {
+ Common::File f;
+
+ _state.room = 1;
+ _state.moves = 0;
+ _state.isDark = false;
+
+ _state.vars.clear();
+ _state.vars.resize(IDI_HR1_NUM_VARS);
+
+ if (!f.open(IDS_HR1_EXE_1))
+ error("Failed to open file '" IDS_HR1_EXE_1 "'");
+
+ // Load room data from executable
+ _state.rooms.clear();
+ f.seek(IDI_HR1_OFS_ROOMS);
+ for (uint i = 0; i < IDI_HR1_NUM_ROOMS; ++i) {
+ Room room;
+ f.readByte();
+ room.description = f.readByte();
+ for (uint j = 0; j < 6; ++j)
+ room.connections[j] = f.readByte();
+ room.picture = f.readByte();
+ room.curPicture = f.readByte();
+ _state.rooms.push_back(room);
+ }
+
+ // Load item data from executable
+ _state.items.clear();
+ f.seek(IDI_HR1_OFS_ITEMS);
+ while (f.readByte() != 0xff) {
+ Item item;
+ item.noun = f.readByte();
+ item.room = f.readByte();
+ item.picture = f.readByte();
+ item.isLineArt = f.readByte();
+ item.position.x = f.readByte();
+ item.position.y = f.readByte();
+ item.state = f.readByte();
+ item.description = f.readByte();
+
+ f.readByte();
+
+ byte size = f.readByte();
+
+ for (uint i = 0; i < size; ++i)
+ item.roomPictures.push_back(f.readByte());
+
+ _state.items.push_back(item);
+ }
+}
+
+void HiRes1Engine::restartGame() {
+ initState();
+ _display->printString(_gameStrings.pressReturn);
+ inputString(); // Missing in the original
+ _display->printAsciiString("\r\r\r\r\r");
+}
+
+void HiRes1Engine::drawPic(byte pic, Common::Point pos) const {
+ Common::File f;
+ Common::String name = Common::String::format("BLOCK%i", _pictures[pic].block);
+
+ if (!f.open(name))
+ error("Failed to open file '%s'", name.c_str());
+
+ f.seek(_pictures[pic].offset);
+ drawPic(f, pos);
+}
+
+void HiRes1Engine::printMessage(uint idx, bool wait) const {
+ // Messages with hardcoded overrides don't delay after printing.
+ // It's unclear if this is a bug or not. In some cases the result
+ // is that these strings will scroll past the four-line text window
+ // before the user gets a chance to read them.
+ // NOTE: later games seem to wait for a key when the text window
+ // overflows and don't use delays. It might be better to use
+ // that system for this game as well.
+ switch (idx) {
+ case IDI_HR1_MSG_CANT_GO_THERE:
+ case IDI_HR1_MSG_DONT_HAVE_IT:
+ case IDI_HR1_MSG_DONT_UNDERSTAND:
+ case IDI_HR1_MSG_GETTING_DARK:
+ wait = false;
+ }
+
+ AdlEngine::printMessage(idx, wait);
+}
+
+void HiRes1Engine::drawLine(const Common::Point &p1, const Common::Point &p2, byte color) const {
+ // This draws a four-connected line
+
+ int16 deltaX = p2.x - p1.x;
+ int8 xStep = 1;
+
+ if (deltaX < 0) {
+ deltaX = -deltaX;
+ xStep = -1;
+ }
+
+ int16 deltaY = p2.y - p1.y;
+ int8 yStep = -1;
+
+ if (deltaY > 0) {
+ deltaY = -deltaY;
+ yStep = 1;
+ }
+
+ Common::Point p(p1);
+ int16 steps = deltaX - deltaY + 1;
+ int16 err = deltaX + deltaY;
+
+ while (1) {
+ _display->putPixel(p, color);
+
+ if (--steps == 0)
+ return;
+
+ if (err < 0) {
+ p.y += yStep;
+ err += deltaX;
+ } else {
+ p.x += xStep;
+ err += deltaY;
+ }
+ }
+}
+
+void HiRes1Engine::drawPic(Common::ReadStream &stream, const Common::Point &pos) const {
+ byte x, y;
+ bool bNewLine = false;
+ byte oldX = 0, oldY = 0;
+ while (1) {
+ x = stream.readByte();
+ y = stream.readByte();
+
+ if (stream.err() || stream.eos())
+ error("Failed to read picture");
+
+ if (x == 0xff && y == 0xff)
+ return;
+
+ if (x == 0 && y == 0) {
+ bNewLine = true;
+ continue;
+ }
+
+ x += pos.x;
+ y += pos.y;
+
+ if (y > 160)
+ y = 160;
+
+ if (bNewLine) {
+ _display->putPixel(Common::Point(x, y), 0x7f);
+ bNewLine = false;
+ } else {
+ drawLine(Common::Point(oldX, oldY), Common::Point(x, y), 0x7f);
+ }
+
+ oldX = x;
+ oldY = y;
+ }
+}
+
+Engine *HiRes1Engine_create(OSystem *syst, const AdlGameDescription *gd) {
+ return new HiRes1Engine(syst, gd);
+}
+
+} // End of namespace Adl
diff --git a/engines/adl/hires1.h b/engines/adl/hires1.h
new file mode 100644
index 0000000000..25f4744d26
--- /dev/null
+++ b/engines/adl/hires1.h
@@ -0,0 +1,113 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ADL_HIRES1_H
+#define ADL_HIRES1_H
+
+#include "common/str.h"
+
+#include "adl/adl.h"
+
+namespace Common {
+class ReadStream;
+struct Point;
+}
+
+namespace Adl {
+
+#define IDS_HR1_EXE_0 "AUTO LOAD OBJ"
+#define IDS_HR1_EXE_1 "ADVENTURE"
+#define IDS_HR1_LOADER "MYSTERY.HELLO"
+#define IDS_HR1_MESSAGES "MESSAGES"
+
+#define IDI_HR1_NUM_ROOMS 41
+#define IDI_HR1_NUM_PICS 98
+#define IDI_HR1_NUM_VARS 20
+#define IDI_HR1_NUM_ITEM_OFFSETS 21
+#define IDI_HR1_NUM_MESSAGES 167
+
+// Messages used outside of scripts
+#define IDI_HR1_MSG_CANT_GO_THERE 137
+#define IDI_HR1_MSG_DONT_UNDERSTAND 37
+#define IDI_HR1_MSG_ITEM_DOESNT_MOVE 151
+#define IDI_HR1_MSG_ITEM_NOT_HERE 152
+#define IDI_HR1_MSG_THANKS_FOR_PLAYING 140
+#define IDI_HR1_MSG_DONT_HAVE_IT 127
+#define IDI_HR1_MSG_GETTING_DARK 7
+
+#define IDI_HR1_OFS_STR_ENTER_COMMAND 0x5bbc
+#define IDI_HR1_OFS_STR_VERB_ERROR 0x5b4f
+#define IDI_HR1_OFS_STR_NOUN_ERROR 0x5b8e
+#define IDI_HR1_OFS_STR_PLAY_AGAIN 0x5f1e
+#define IDI_HR1_OFS_STR_CANT_GO_THERE 0x6c0a
+#define IDI_HR1_OFS_STR_DONT_HAVE_IT 0x6c31
+#define IDI_HR1_OFS_STR_DONT_UNDERSTAND 0x6c51
+#define IDI_HR1_OFS_STR_GETTING_DARK 0x6c7c
+#define IDI_HR1_OFS_STR_PRESS_RETURN 0x5f68
+
+#define IDI_HR1_OFS_PD_TEXT_0 0x005d
+#define IDI_HR1_OFS_PD_TEXT_1 0x012b
+#define IDI_HR1_OFS_PD_TEXT_2 0x016d
+#define IDI_HR1_OFS_PD_TEXT_3 0x0259
+
+#define IDI_HR1_OFS_INTRO_TEXT 0x0066
+#define IDI_HR1_OFS_GAME_OR_HELP 0x000f
+
+#define IDI_HR1_OFS_LOGO_0 0x1003
+#define IDI_HR1_OFS_LOGO_1 0x1800
+
+#define IDI_HR1_OFS_ITEMS 0x0100
+#define IDI_HR1_OFS_ROOMS 0x050a
+#define IDI_HR1_OFS_PICS 0x4b00
+#define IDI_HR1_OFS_CMDS_0 0x3c00
+#define IDI_HR1_OFS_CMDS_1 0x3d00
+
+#define IDI_HR1_OFS_ITEM_OFFSETS 0x68ff
+#define IDI_HR1_OFS_LINE_ART 0x4f00
+
+#define IDI_HR1_OFS_VERBS 0x3800
+#define IDI_HR1_OFS_NOUNS 0x0f00
+
+class HiRes1Engine : public AdlEngine {
+public:
+ HiRes1Engine(OSystem *syst, const AdlGameDescription *gd) : AdlEngine(syst, gd) { }
+
+private:
+ // AdlEngine
+ void runIntro() const;
+ void loadData();
+ void initState();
+ void restartGame();
+ void drawPic(byte pic, Common::Point pos) const;
+ void printMessage(uint idx, bool wait = true) const;
+
+ void drawLine(const Common::Point &p1, const Common::Point &p2, byte color) const;
+ void drawPic(Common::ReadStream &stream, const Common::Point &pos) const;
+
+ struct {
+ Common::String pressReturn;
+ } _gameStrings;
+};
+
+} // End of namespace Adl
+
+#endif
diff --git a/engines/adl/module.mk b/engines/adl/module.mk
new file mode 100644
index 0000000000..6acd06f6de
--- /dev/null
+++ b/engines/adl/module.mk
@@ -0,0 +1,18 @@
+MODULE := engines/adl
+
+MODULE_OBJS := \
+ adl.o \
+ detection.o \
+ display.o \
+ hires1.o
+
+MODULE_DIRS += \
+ engines/adl
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_ADL), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/agi/text.cpp b/engines/agi/text.cpp
index 0cacce2421..110ba10632 100644
--- a/engines/agi/text.cpp
+++ b/engines/agi/text.cpp
@@ -885,6 +885,12 @@ void TextMgr::stringEdit(int16 stringMaxLen) {
_inputStringRow = _textPos.row;
_inputStringColumn = _textPos.column;
+ if (_inputCursorChar) {
+ // Cursor character is shown, which means we are one beyond the start of the input
+ // Adjust the column for predictive input dialog
+ _inputStringColumn--;
+ }
+
// Caller can set the input string
_inputStringCursorPos = 0;
while (_inputStringCursorPos < inputStringLen) {
diff --git a/engines/composer/detection.cpp b/engines/composer/detection.cpp
index a3ab18ae54..689a72a743 100644
--- a/engines/composer/detection.cpp
+++ b/engines/composer/detection.cpp
@@ -253,6 +253,36 @@ static const ComposerGameDescription gameDescriptions[] = {
GType_ComposerV2
},
+ { // Provided by WindlePoons, "100% Kids Darby & Gregor" Pack. Bugreport #6825
+ {
+ "darby",
+ 0,
+ {
+ {"book.ini", 0, "285308372f7dddff2ca5a25c9192cf5c", 2545},
+ {"page99.rsc", 0, "40b4879e9ba6a34d6aa2a9d2e30c5ef7", 1286480},
+ AD_LISTEND
+ },
+ Common::DE_DEU,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GType_ComposerV2
+ },
+
+ { // Provided by Niv Baehr, Bugreport #6878
+ {
+ "darby",
+ 0,
+ AD_ENTRY1("page99.rsc", "183463d18c050563dcdec2d9f9670515"),
+ Common::HE_ISR,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GType_ComposerV2
+ },
+
{
{
"gregory",
@@ -296,6 +326,23 @@ static const ComposerGameDescription gameDescriptions[] = {
GType_ComposerV2
},
+ { // Provided by WindlePoons, "100% Kids Darby & Gregor" Pack. Bugreport #6825
+ {
+ "gregory",
+ 0,
+ {
+ {"book.ini", 0, "e54fc5c00de5f94e908a969e445af5d0", 2234},
+ {"page99.rsc", 0, "1ae6610de621a9901bf87b874fbf331f", 388644},
+ AD_LISTEND
+ },
+ Common::DE_DEU,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GType_ComposerV2
+ },
+
{ // Provided by sev
{
"princess",
diff --git a/engines/dialogs.cpp b/engines/dialogs.cpp
index 8498e50b8d..a21a4a8126 100644
--- a/engines/dialogs.cpp
+++ b/engines/dialogs.cpp
@@ -43,13 +43,13 @@
#include "engines/engine.h"
#include "engines/metaengine.h"
-#ifdef SMALL_SCREEN_DEVICE
+#ifdef GUI_ENABLE_KEYSDIALOG
#include "gui/KeysDialog.h"
#endif
class ConfigDialog : public GUI::OptionsDialog {
protected:
-#ifdef SMALL_SCREEN_DEVICE
+#ifdef GUI_ENABLE_KEYSDIALOG
GUI::Dialog *_keysDialog;
#endif
@@ -307,14 +307,14 @@ ConfigDialog::ConfigDialog(bool subtitleControls)
new GUI::ButtonWidget(this, "GlobalConfig.Ok", _("~O~K"), 0, GUI::kOKCmd);
new GUI::ButtonWidget(this, "GlobalConfig.Cancel", _("~C~ancel"), 0, GUI::kCloseCmd);
-#ifdef SMALL_SCREEN_DEVICE
+#ifdef GUI_ENABLE_KEYSDIALOG
new GUI::ButtonWidget(this, "GlobalConfig.Keys", _("~K~eys"), 0, kKeysCmd);
_keysDialog = NULL;
#endif
}
ConfigDialog::~ConfigDialog() {
-#ifdef SMALL_SCREEN_DEVICE
+#ifdef GUI_ENABLE_KEYSDIALOG
delete _keysDialog;
#endif
}
@@ -323,7 +323,7 @@ void ConfigDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32
switch (cmd) {
case kKeysCmd:
-#ifdef SMALL_SCREEN_DEVICE
+#ifdef GUI_ENABLE_KEYSDIALOG
//
// Create the sub dialog(s)
//
diff --git a/engines/drascula/actors.cpp b/engines/drascula/actors.cpp
index 849e2ccd24..b459c4539b 100644
--- a/engines/drascula/actors.cpp
+++ b/engines/drascula/actors.cpp
@@ -123,6 +123,8 @@ void DrasculaEngine::startWalking() {
walkUp();
else if (roomY > curY + curHeight)
walkDown();
+ else
+ characterMoved = 0;
} else {
if ((roomX < curX + curWidth / 2 ) && (roomY <= (curY + curHeight)))
quadrant_1();
@@ -189,7 +191,7 @@ void DrasculaEngine::moveCharacters() {
}
if (currentChapter != 2 && currentChapter != 3) {
- if (hare_se_ve == 0) {
+ if (characterVisible == 0) {
increaseFrameNum();
return;
}
diff --git a/engines/drascula/animation.cpp b/engines/drascula/animation.cpp
index 5009a62e84..439253e5d6 100644
--- a/engines/drascula/animation.cpp
+++ b/engines/drascula/animation.cpp
@@ -360,7 +360,7 @@ void DrasculaEngine::animation_2_1() {
int l;
gotoObject(231, 91);
- hare_se_ve = 0;
+ characterVisible = 0;
term_int = 0;
@@ -433,7 +433,7 @@ void DrasculaEngine::animation_2_1() {
curX = 91;
curY = 95;
trackProtagonist = 1;
- hare_se_ve = 1;
+ characterVisible = 1;
loadPic("97g.alg", extraSurface);
if (animate("lev.bin", 15))
@@ -1434,7 +1434,7 @@ void DrasculaEngine::animation_12_5() {
doBreak = 1;
previousMusic = roomMusic;
- hare_se_ve = 1;
+ characterVisible = 1;
clearRoom();
trackProtagonist = 1;
characterMoved = 0;
@@ -1543,7 +1543,7 @@ void DrasculaEngine::animation_1_6() {
updateEvents();
clearRoom();
black();
- hare_se_ve = 0;
+ characterVisible = 0;
flags[0] = 0;
updateRoom();
updateScreen();
@@ -1618,7 +1618,7 @@ void DrasculaEngine::animation_6_6() {
curX = -1;
selectVerb(kVerbNone);
enterRoom(58);
- hare_se_ve = 1;
+ characterVisible = 1;
trackProtagonist = 1;
animate("hbp.bin", 14);
@@ -2138,7 +2138,7 @@ void DrasculaEngine::animation_5_4(){
loadPic("anh_dr.alg", backSurface);
gotoObject(99, 160);
gotoObject(38, 177);
- hare_se_ve = 0;
+ characterVisible = 0;
updateRoom();
updateScreen();
delay(800);
@@ -2156,7 +2156,7 @@ void DrasculaEngine::animation_5_4(){
talk_igor(30, kIgorFront);
loadPic(96, frontSurface);
loadPic(99, backSurface);
- hare_se_ve = 1;
+ characterVisible = 1;
fadeToBlack(0);
exitRoom(0);
}
@@ -2211,7 +2211,7 @@ void DrasculaEngine::activatePendulum() {
debug(4, "activatePendulum()");
flags[1] = 2;
- hare_se_ve = 0;
+ characterVisible = 0;
_roomNumber = 102;
loadPic(102, bgSurface, HALF_PAL);
loadPic("an_p1.alg", drawSurface3);
diff --git a/engines/drascula/drascula.cpp b/engines/drascula/drascula.cpp
index 9ac9031fb7..b821e7dbbe 100644
--- a/engines/drascula/drascula.cpp
+++ b/engines/drascula/drascula.cpp
@@ -144,7 +144,7 @@ DrasculaEngine::DrasculaEngine(OSystem *syst, const DrasculaGameDescription *gam
curDirection = 0;
trackProtagonist = 0;
_characterFrame = 0;
- hare_se_ve = 0;
+ characterVisible = 0;
roomX = 0;
roomY = 0;
checkFlags = 0;
@@ -299,7 +299,7 @@ Common::Error DrasculaEngine::run() {
characterMoved = 0;
trackProtagonist = 3;
_characterFrame = 0;
- hare_se_ve = 1;
+ characterVisible = 1;
checkFlags = 1;
doBreak = 0;
walkToObject = 0;
diff --git a/engines/drascula/drascula.h b/engines/drascula/drascula.h
index 762add50a5..acca2e5915 100644
--- a/engines/drascula/drascula.h
+++ b/engines/drascula/drascula.h
@@ -429,7 +429,7 @@ public:
int frame_y;
int curX, curY, characterMoved, curDirection, trackProtagonist, _characterFrame;
- int hare_se_ve; // TODO: what is this for?
+ int characterVisible;
int roomX, roomY, checkFlags;
int doBreak;
int stepX, stepY;
diff --git a/engines/drascula/graphics.cpp b/engines/drascula/graphics.cpp
index 077047a6eb..4ed949cc99 100644
--- a/engines/drascula/graphics.cpp
+++ b/engines/drascula/graphics.cpp
@@ -319,28 +319,42 @@ int DrasculaEngine::print_abc_opc(const char *said, int screenY, int game) {
}
bool DrasculaEngine::textFitsCentered(char *text, int x) {
- int len = strlen(text);
- int tmp = CLIP<int>(x - len * CHAR_WIDTH / 2, 60, 255);
- return (tmp + len * CHAR_WIDTH) <= 320;
+ int halfLen = (strlen(text) / 2) * CHAR_WIDTH;
+
+ // See comment in centerText()
+ if (x > 160)
+ x = 315 - x;
+ return (halfLen <= x);
}
void DrasculaEngine::centerText(const char *message, int textX, int textY) {
char msg[200];
- char messageLine[200];
- char tmpMessageLine[200];
- *messageLine = 0;
- *tmpMessageLine = 0;
- char *curWord;
- int curLine = 0;
- int x = 0;
- // original starts printing 4 lines above textY
- int y = CLIP<int>(textY - (4 * CHAR_HEIGHT), 0, 320);
-
Common::strlcpy(msg, message, 200);
+
+ // We make sure to have a width of at least 120 pixels by clipping the center.
+ // In theory since the screen width is 320 I would expect something like this:
+ // x = CLIP<int>(x, 60, 260);
+ // return (x - halfLen >= 0 && x + halfLen <= 319);
+
+ // The engines does things differently though. It tries to clips text at 315 instead of 319.
+ // And instead of testing the upper bound if x is greater than 160 it takes the complement to 315
+ // and test only the lower bounds. However since 160 is not the middle of 315, we end up having
+ // text that can go beyond 315 (up to 320) if x is in [159, 160].
+ // Also note that if the numbers of characters is odd, there is one more character to the right
+ // than to the left as it computes the half length with an integer division by two BEFORE multiplying
+ // by CHAR_WIDTH. Thus in theory we may end up with one character out of the screen!
+ // Be faithfull to the original and do the same though.
+
+ textX = CLIP<int>(textX, 60, 255);
// If the message fits on screen as-is, just print it here
if (textFitsCentered(msg, textX)) {
- x = CLIP<int>(textX - strlen(msg) * CHAR_WIDTH / 2, 60, 255);
+ int x = textX - (strlen(msg) / 2) * CHAR_WIDTH - 1;
+ // The original starts to draw (nbLines + 2) lines above textY, except if there is a single line
+ // in which case it starts drawing at (nbLines + 3) above textY.
+ // Also clip to the screen height although the original does not do it.
+ int y = textY - 4 * CHAR_HEIGHT;
+ y = CLIP<int>(y, 0, 200 - CHAR_HEIGHT);
print_abc(msg, x, y);
return;
}
@@ -351,42 +365,61 @@ void DrasculaEngine::centerText(const char *message, int textX, int textY) {
// with the German translation.
if (!strchr(msg, ' ')) {
int len = strlen(msg);
- x = CLIP<int>(textX - len * CHAR_WIDTH / 2, 0, 319 - len * CHAR_WIDTH);
+ int x = CLIP<int>(textX - (len / 2) * CHAR_WIDTH - 1, 0, 319 - len * CHAR_WIDTH);
+ int y = textY - 4 * CHAR_HEIGHT;
+ y = CLIP<int>(y, 0, 200 - CHAR_HEIGHT);
print_abc(msg, x, y);
return;
}
// Message doesn't fit on screen, split it
-
+ char messageLines[15][41]; // screenWidth/charWidth = 320/8 = 40. Thus lines can have up to 41 characters with the null terminator (despite the original allocating only 40 characters here).
+ int curLine = 0;
+ char messageCurLine[50];
+ char tmpMessageCurLine[50];
+ *messageCurLine = 0;
+ *tmpMessageCurLine = 0;
// Get a word from the message
- curWord = strtok(msg, " ");
+ char* curWord = strtok(msg, " ");
while (curWord != NULL) {
// Check if the word and the current line fit on screen
- if (tmpMessageLine[0] != '\0')
- Common::strlcat(tmpMessageLine, " ", 200);
- Common::strlcat(tmpMessageLine, curWord, 200);
- if (textFitsCentered(tmpMessageLine, textX)) {
+ if (tmpMessageCurLine[0] != '\0')
+ Common::strlcat(tmpMessageCurLine, " ", 50);
+ Common::strlcat(tmpMessageCurLine, curWord, 50);
+ if (textFitsCentered(tmpMessageCurLine, textX)) {
// Line fits, so add the word to the current message line
- strcpy(messageLine, tmpMessageLine);
+ strcpy(messageCurLine, tmpMessageCurLine);
} else {
- // Line doesn't fit, so show the current line on screen and
- // create a new one
- // If it goes off screen, print_abc will adjust it
- x = CLIP<int>(textX - strlen(messageLine) * CHAR_WIDTH / 2, 60, 255);
- print_abc(messageLine, x, y + curLine * CHAR_HEIGHT);
- Common::strlcpy(messageLine, curWord, 200);
- Common::strlcpy(tmpMessageLine, curWord, 200);
- curLine++;
+ // Line does't fit. Store the current line and start a new line.
+ Common::strlcpy(messageLines[curLine++], messageCurLine, 41);
+ Common::strlcpy(messageCurLine, curWord, 50);
+ Common::strlcpy(tmpMessageCurLine, curWord, 50);
}
// Get next word
curWord = strtok(NULL, " ");
-
if (curWord == NULL) {
- x = CLIP<int>(textX - strlen(messageLine) * CHAR_WIDTH / 2, 60, 255);
- print_abc(messageLine, x, y + curLine * CHAR_HEIGHT);
+ // The original has an interesting bug that if we split the text on several lines
+ // a space is added at the end (which impacts the alignment, and may even cause the line
+ // to become too long).
+ Common::strlcat(messageCurLine, " ", 50);
+ if (!textFitsCentered(messageCurLine, textX)) {
+ messageCurLine[strlen(messageCurLine) - 1] = '\0';
+ Common::strlcpy(messageLines[curLine++], messageCurLine, 41);
+ strcpy(messageLines[curLine++], " ");
+ } else
+ Common::strlcpy(messageLines[curLine++], messageCurLine, 41);
}
}
+
+ // The original starts to draw (nbLines + 2) lines above textY.
+ // Also clip to the screen height although the original does not do it.
+ int y = textY - (curLine + 2) * CHAR_HEIGHT;
+ y = CLIP<int>(y, 0, 200 - curLine * (CHAR_HEIGHT + 2) + 2);
+ for (int line = 0 ; line < curLine ; ++line, y += CHAR_HEIGHT + 2) {
+ int textHalfLen = (strlen(messageLines[line]) / 2) * CHAR_WIDTH;
+ print_abc(messageLines[line], textX - textHalfLen - 1, y);
+ }
}
void DrasculaEngine::screenSaver() {
diff --git a/engines/drascula/objects.cpp b/engines/drascula/objects.cpp
index cd7d502194..823c073d43 100644
--- a/engines/drascula/objects.cpp
+++ b/engines/drascula/objects.cpp
@@ -62,7 +62,7 @@ void DrasculaEngine::gotoObject(int pointX, int pointY) {
hideCursor();
if (currentChapter == 5 || currentChapter == 6) {
- if (hare_se_ve == 0) {
+ if (characterVisible == 0) {
curX = roomX;
curY = roomY;
updateRoom();
diff --git a/engines/drascula/rooms.cpp b/engines/drascula/rooms.cpp
index 8691bd2cb4..57d4517295 100644
--- a/engines/drascula/rooms.cpp
+++ b/engines/drascula/rooms.cpp
@@ -980,12 +980,12 @@ bool DrasculaEngine::room_59(int fl) {
playSound(12);
pause(19);
stopSound();
- hare_se_ve = 0;
+ characterVisible = 0;
updateRoom();
copyRect(101, 34, curX - 4, curY - 1, 37, 70, drawSurface3, screenSurface);
copyBackground(0, 0, 0, 0, 320, 200, screenSurface, bgSurface);
updateScreen();
- hare_se_ve = 1;
+ characterVisible = 1;
clearRoom();
loadPic("tlef0.alg", bgSurface, COMPLETE_PAL);
loadPic("tlef1.alg", drawSurface3);
@@ -1399,7 +1399,7 @@ void DrasculaEngine::update_58_pre() {
}
void DrasculaEngine::update_58() {
- if (hare_se_ve == 1)
+ if (characterVisible == 1)
copyRect(67, 139, 140, 147, 12, 16, drawSurface3, screenSurface);
}
@@ -1845,7 +1845,7 @@ void DrasculaEngine::enterRoom(int roomIndex) {
}
if (currentChapter == 5)
- hare_se_ve = 1;
+ characterVisible = 1;
updateVisible();
@@ -1885,7 +1885,7 @@ void DrasculaEngine::enterRoom(int roomIndex) {
if (currentChapter == 5) {
if (_roomNumber == 45)
- hare_se_ve = 0;
+ characterVisible = 0;
if (_roomNumber == 49 && flags[7] == 0) {
playTalkSequence(4); // sequence 4, chapter 5
}
@@ -1961,7 +1961,7 @@ bool DrasculaEngine::exitRoom(int doorNumber) {
}
if (currentChapter == 5)
- hare_se_ve = 1;
+ characterVisible = 1;
clearRoom();
if (!sscanf(_targetSurface[doorNumber], "%d", &roomNum)) {
diff --git a/engines/drascula/saveload.cpp b/engines/drascula/saveload.cpp
index d0f16aa941..eb72a999d4 100644
--- a/engines/drascula/saveload.cpp
+++ b/engines/drascula/saveload.cpp
@@ -255,6 +255,19 @@ bool DrasculaEngine::loadGame(int slot) {
if (!(in = _saveFileMan->openForLoading(saveFileName))) {
error("missing savegame file %s", saveFileName.c_str());
}
+
+ // If we currently are in room 102 while being attached below the pendulum
+ // the character is invisible and some surface are temporarily used for other
+ // things. Reset those before loading the savegame otherwise we may have some
+ // issues such as the protagonist being invisible after reloading a savegame.
+ if (_roomNumber == 102 && flags[1] == 2) {
+ characterVisible = 1;
+ loadPic(96, frontSurface);
+ loadPic(97, frontSurface);
+ loadPic(97, extraSurface);
+ loadPic(99, backSurface);
+ }
+
loadMetaData(in, slot, true);
Graphics::skipThumbnail(*in);
@@ -287,8 +300,23 @@ bool DrasculaEngine::loadGame(int slot) {
if (!sscanf(currentData, "%d.ald", &roomNum)) {
error("Bad save format");
}
+
+ // When loading room 102 while being attached below the pendulum Some variables
+ // are not correctly set and can cause random crashes when calling enterRoom below.
+ // The crash occurs in moveCharacters() when accessing factor_red[curY + curHeight].
+ if (roomNum == 102 && flags[1] == 2) {
+ curX = 103;
+ curY = 108;
+ curWidth = curHeight = 0;
+ }
+
enterRoom(roomNum);
selectVerb(kVerbNone);
+
+ // When loading room 102 while being attached below the pendulum we
+ // need to call activatePendulum() to properly initialized the scene.
+ if (_roomNumber == 102 && flags[1] == 2)
+ activatePendulum();
return true;
}
diff --git a/engines/drascula/talk.cpp b/engines/drascula/talk.cpp
index e9fec868f8..cc329b206b 100644
--- a/engines/drascula/talk.cpp
+++ b/engines/drascula/talk.cpp
@@ -232,7 +232,7 @@ void DrasculaEngine::talk_solo(const char *said, const char *filename) {
if (currentChapter == 1)
color_abc(color_solo);
- else if (currentChapter == 4)
+ else if (currentChapter == 5)
color_abc(kColorRed);
talkInit(filename);
diff --git a/engines/dreamweb/detection_tables.h b/engines/dreamweb/detection_tables.h
index cb9bebb304..0a59543c51 100644
--- a/engines/dreamweb/detection_tables.h
+++ b/engines/dreamweb/detection_tables.h
@@ -244,6 +244,26 @@ static const DreamWebGameDescription gameDescriptions[] = {
},
},
+ // Czech fan-made translation
+ // From bug #7078
+ {
+ {
+ "dreamweb",
+ "CD",
+ {
+ {"dreamweb.r00", 0, "3b5c87717fc40cc5a5ae19c155662ee3", 152918},
+ {"dreamweb.r02", 0, "28458718167a040d7e988cf7d2298eae", 210466},
+ {"dreamweb.exe", 0, "40cc15bdc8fa3a785b5fd1ecd6194119", 65440},
+ AD_LISTEND
+ },
+ Common::CZ_CZE,
+ Common::kPlatformDOS,
+ ADGF_CD,
+ GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE)
+ },
+ },
+
+
{ AD_TABLE_END_MARKER }
};
diff --git a/engines/gob/detection/tables_fascin.h b/engines/gob/detection/tables_fascin.h
index 7c7c9a7a2f..92272e9852 100644
--- a/engines/gob/detection/tables_fascin.h
+++ b/engines/gob/detection/tables_fascin.h
@@ -187,6 +187,20 @@
kFeaturesCD,
"intro.stk", 0, 0
},
+{ // From bug #7069
+ {
+ "fascination",
+ "",
+ AD_ENTRY1s("disk0.stk", "fbf73d7919e1a6752d924eccc14838d7", 190498),
+ ES_ESP,
+ kPlatformDOS,
+ ADGF_NO_FLAGS,
+ GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH)
+ },
+ kGameTypeFascination,
+ kFeaturesNone,
+ "disk0.stk", 0, 0
+},
// -- Amiga --
diff --git a/engines/gob/detection/tables_playtoons.h b/engines/gob/detection/tables_playtoons.h
index f249e3ffa6..e495db9e25 100644
--- a/engines/gob/detection/tables_playtoons.h
+++ b/engines/gob/detection/tables_playtoons.h
@@ -222,6 +222,24 @@
kFeatures640x480,
"intro2.stk", 0, 0
},
+{ // Version 1.002. Bug #7052
+ {
+ "playtoons2",
+ "",
+ {
+ {"playtoon.stk", 0, "8c98e9a11be9bb203a55e8c6e68e519b", 25574338},
+ {"spirou.stk", 0, "91080dc148de1bbd6a97321c1a1facf3", 9817086},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformDOS,
+ ADGF_NO_FLAGS,
+ GUIO3(GUIO_NOSUBTITLES, GUIO_NOSPEECH, GUIO_NOASPECT)
+ },
+ kGameTypePlaytoons,
+ kFeatures640x480,
+ "intro2.stk", 0, 0
+},
{
{
"playtoons2",
diff --git a/engines/kyra/lol.h b/engines/kyra/lol.h
index e060b307af..af58397200 100644
--- a/engines/kyra/lol.h
+++ b/engines/kyra/lol.h
@@ -463,6 +463,7 @@ private:
const uint8 *_musicTrackMap;
const uint16 *_ingameSoundIndex;
+ int _ingameSoundIndexSize;
const uint8 *_ingameGMSoundIndex;
int _ingameGMSoundIndexSize;
const uint8 *_ingameMT32SoundIndex;
diff --git a/engines/kyra/sound_lol.cpp b/engines/kyra/sound_lol.cpp
index 8be0cb6ab9..6e7551ed0e 100644
--- a/engines/kyra/sound_lol.cpp
+++ b/engines/kyra/sound_lol.cpp
@@ -161,7 +161,7 @@ void LoLEngine::snd_playSoundEffect(int track, int volume) {
return;
_lastSfxTrack = track;
- if (track == -1 || track >= _ingameSoundListSize)
+ if (track == -1 || track >= _ingameSoundIndexSize)
return;
volume &= 0xFF;
@@ -216,10 +216,10 @@ bool LoLEngine::snd_processEnvironmentalSoundEffect(int soundId, int block) {
for (int i = 3; i > 0; i--) {
int dir = calcMonsterDirection(cbl & 0x1F, cbl >> 5, block & 0x1F, block >> 5);
cbl = (cbl + blockShiftTable[dir]) & 0x3FF;
- if (cbl != block) {
- if (testWallFlag(cbl, 0, 1))
- _environmentSfxVol >>= 1;
- }
+ if (cbl == block)
+ break;
+ if (testWallFlag(cbl, 0, 1))
+ _environmentSfxVol >>= 1;
}
}
diff --git a/engines/kyra/staticres_lol.cpp b/engines/kyra/staticres_lol.cpp
index 9a4fc281d5..c40b4a0c7d 100644
--- a/engines/kyra/staticres_lol.cpp
+++ b/engines/kyra/staticres_lol.cpp
@@ -255,7 +255,7 @@ void LoLEngine::initStaticResource() {
int tempSize;
_pakFileList = _staticres->loadStrings(kLoLIngamePakFiles, _pakFileListSize);
_charDefaults = _staticres->loadCharData(kLoLCharacterDefs, _charDefaultsSize);
- _ingameSoundIndex = (const uint16 *)_staticres->loadRawData(kLoLIngameSfxIndex, tempSize);
+ _ingameSoundIndex = (const uint16 *)_staticres->loadRawData(kLoLIngameSfxIndex, _ingameSoundIndexSize);
_musicTrackMap = _staticres->loadRawData(kLoLMusicTrackMap, tempSize);
_ingameGMSoundIndex = _staticres->loadRawData(kLoLIngameGMSfxIndex, _ingameGMSoundIndexSize);
_ingameMT32SoundIndex = _staticres->loadRawData(kLoLIngameMT32SfxIndex, _ingameMT32SoundIndexSize);
diff --git a/engines/mohawk/POTFILES b/engines/mohawk/POTFILES
index 54d9dcaa3a..036059da6a 100644
--- a/engines/mohawk/POTFILES
+++ b/engines/mohawk/POTFILES
@@ -1,3 +1,4 @@
+engines/mohawk/detection.cpp
engines/mohawk/dialogs.cpp
engines/mohawk/myst.cpp
engines/mohawk/riven.cpp
diff --git a/engines/mohawk/detection.cpp b/engines/mohawk/detection.cpp
index a64d7ff7df..7c202998eb 100644
--- a/engines/mohawk/detection.cpp
+++ b/engines/mohawk/detection.cpp
@@ -221,10 +221,25 @@ SaveStateList MohawkMetaEngine::listSaves(const char *target) const {
// Loading games is only supported in Myst/Riven currently.
#ifdef ENABLE_MYST
if (strstr(target, "myst")) {
- filenames = Mohawk::MystGameState::generateSaveGameList();
+ filenames = g_system->getSavefileManager()->listSavefiles("myst-###.mys");
+ size_t prefixLen = sizeof("myst") - 1;
+
+ for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) {
+ // Extract the slot number from the filename
+ char slot[4];
+ slot[0] = (*filename)[prefixLen + 1];
+ slot[1] = (*filename)[prefixLen + 2];
+ slot[2] = (*filename)[prefixLen + 3];
+ slot[3] = '\0';
+
+ int slotNum = atoi(slot);
+
+ // Read the description from the save
+ Common::String description = Mohawk::MystGameState::querySaveDescription(slotNum);
+ saveList.push_back(SaveStateDescriptor(slotNum, description));
+ }
- for (uint32 i = 0; i < filenames.size(); i++)
- saveList.push_back(SaveStateDescriptor(i, filenames[i]));
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
} else
#endif
if (strstr(target, "riven")) {
@@ -238,11 +253,11 @@ SaveStateList MohawkMetaEngine::listSaves(const char *target) const {
}
void MohawkMetaEngine::removeSaveState(const char *target, int slot) const {
+
// Removing saved games is only supported in Myst/Riven currently.
#ifdef ENABLE_MYST
if (strstr(target, "myst")) {
- Common::StringArray filenames = Mohawk::MystGameState::generateSaveGameList();
- Mohawk::MystGameState::deleteSave(filenames[slot]);
+ Mohawk::MystGameState::deleteSave(slot);
} else
#endif
if (strstr(target, "riven")) {
@@ -254,13 +269,7 @@ void MohawkMetaEngine::removeSaveState(const char *target, int slot) const {
SaveStateDescriptor MohawkMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
#ifdef ENABLE_MYST
if (strstr(target, "myst")) {
- Common::StringArray filenames = Mohawk::MystGameState::generateSaveGameList();
-
- if (slot >= (int) filenames.size()) {
- return SaveStateDescriptor();
- }
-
- return Mohawk::MystGameState::querySaveMetaInfos(filenames[slot]);
+ return Mohawk::MystGameState::querySaveMetaInfos(slot);
} else
#endif
{
diff --git a/engines/mohawk/detection_tables.h b/engines/mohawk/detection_tables.h
index 7941a0d51a..e3eab89a34 100644
--- a/engines/mohawk/detection_tables.h
+++ b/engines/mohawk/detection_tables.h
@@ -40,7 +40,7 @@ static const MohawkGameDescription gameDescriptions[] = {
AD_ENTRY1("MYST.DAT", "ae3258c9c90128d274aa6a790b3ad181"),
Common::EN_ANY,
Common::kPlatformWindows,
- ADGF_UNSTABLE,
+ ADGF_TESTING,
GUI_OPTIONS_MYST
},
GType_MYST,
@@ -58,7 +58,7 @@ static const MohawkGameDescription gameDescriptions[] = {
AD_ENTRY1("DEMO.DAT", "c39303dd53fb5c4e7f3c23231c606cd0"),
Common::EN_ANY,
Common::kPlatformWindows,
- ADGF_DEMO | ADGF_UNSTABLE,
+ ADGF_DEMO | ADGF_TESTING,
GUI_OPTIONS_MYST_DEMO
},
GType_MYST,
@@ -76,7 +76,7 @@ static const MohawkGameDescription gameDescriptions[] = {
AD_ENTRY1("MYST.DAT", "4beb3366ed3f3b9bfb6e81a14a43bdcc"),
Common::DE_DEU,
Common::kPlatformWindows,
- ADGF_UNSTABLE,
+ ADGF_TESTING,
GUI_OPTIONS_MYST
},
GType_MYST,
@@ -94,7 +94,7 @@ static const MohawkGameDescription gameDescriptions[] = {
AD_ENTRY1("MYST.DAT", "e0937cca1ab125e48e30dc3cd5046ddf"),
Common::DE_DEU,
Common::kPlatformWindows,
- ADGF_UNSTABLE,
+ ADGF_TESTING,
GUI_OPTIONS_MYST
},
GType_MYST,
@@ -112,7 +112,7 @@ static const MohawkGameDescription gameDescriptions[] = {
AD_ENTRY1("MYST.DAT", "f7e7d7ca69934f1351b5acd4fe4d44c2"),
Common::ES_ESP,
Common::kPlatformWindows,
- ADGF_UNSTABLE,
+ ADGF_TESTING,
GUI_OPTIONS_MYST
},
GType_MYST,
@@ -130,7 +130,7 @@ static const MohawkGameDescription gameDescriptions[] = {
AD_ENTRY1("MYST.DAT", "a5795ce1751fc42525e4f9a1859181d5"),
Common::IT_ITA,
Common::kPlatformWindows,
- ADGF_UNSTABLE,
+ ADGF_TESTING,
GUI_OPTIONS_MYST
},
GType_MYST,
@@ -148,7 +148,7 @@ static const MohawkGameDescription gameDescriptions[] = {
AD_ENTRY1("MYST.DAT", "032c88e3b7e8db4ca475e7b7db9a66bb"),
Common::JA_JPN,
Common::kPlatformWindows,
- ADGF_UNSTABLE,
+ ADGF_TESTING,
GUI_OPTIONS_MYST
},
GType_MYST,
@@ -166,7 +166,7 @@ static const MohawkGameDescription gameDescriptions[] = {
AD_ENTRY1("MYST.DAT", "d631d42567a941c67c78f2e491f4ea58"),
Common::FR_FRA,
Common::kPlatformWindows,
- ADGF_UNSTABLE,
+ ADGF_TESTING,
GUI_OPTIONS_MYST
},
GType_MYST,
@@ -184,7 +184,7 @@ static const MohawkGameDescription gameDescriptions[] = {
AD_ENTRY1("MAKING.DAT", "f6387e8f0f7b8a3e42c95294315d6a0e"),
Common::EN_ANY,
Common::kPlatformWindows,
- ADGF_UNSTABLE,
+ ADGF_TESTING,
GUI_OPTIONS_MYST_MAKING_OF
},
GType_MAKINGOF,
@@ -202,7 +202,7 @@ static const MohawkGameDescription gameDescriptions[] = {
AD_ENTRY1("MAKING.DAT", "03ff62607e64419ab2b6ebf7b7bcdf63"),
Common::JA_JPN,
Common::kPlatformWindows,
- ADGF_UNSTABLE,
+ ADGF_TESTING,
GUI_OPTIONS_MYST_MAKING_OF
},
GType_MAKINGOF,
@@ -220,7 +220,7 @@ static const MohawkGameDescription gameDescriptions[] = {
AD_ENTRY1("MYST.DAT", "c4cae9f143b5947262e6cb2397e1617e"),
Common::EN_ANY,
Common::kPlatformWindows,
- ADGF_UNSTABLE,
+ ADGF_TESTING,
GUI_OPTIONS_MYST_ME
},
GType_MYST,
@@ -238,7 +238,7 @@ static const MohawkGameDescription gameDescriptions[] = {
AD_ENTRY1("MYST.DAT", "f88e0ace66dbca78eebdaaa1d3314ceb"),
Common::DE_DEU,
Common::kPlatformWindows,
- ADGF_UNSTABLE,
+ ADGF_TESTING,
GUI_OPTIONS_MYST_ME
},
GType_MYST,
@@ -256,7 +256,7 @@ static const MohawkGameDescription gameDescriptions[] = {
AD_ENTRY1("MYST.DAT", "aea81633b2d2ae498f09072fb87263b6"),
Common::FR_FRA,
Common::kPlatformWindows,
- ADGF_UNSTABLE,
+ ADGF_TESTING,
GUI_OPTIONS_MYST_ME
},
GType_MYST,
@@ -274,7 +274,7 @@ static const MohawkGameDescription gameDescriptions[] = {
AD_ENTRY1("MYST.DAT", "4a05771b60f4a69869838d01e85c9e80"),
Common::PL_POL,
Common::kPlatformWindows,
- ADGF_UNSTABLE,
+ ADGF_TESTING,
GUI_OPTIONS_MYST_ME
},
GType_MYST,
@@ -355,6 +355,23 @@ static const MohawkGameDescription gameDescriptions[] = {
},
// Riven: The Sequel to Myst
+ // Version 1.0.0 (5CD) - Russian, Fargus
+ {
+ {
+ "riven",
+ "",
+ AD_ENTRY1s("a_Data.MHK", "2a840ed74fe5dc3a388bced674d379d5", 12024358),
+ Common::RU_RUS,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GType_RIVEN,
+ 0,
+ 0,
+ },
+
+ // Riven: The Sequel to Myst
// Version 1.1 (5CD) - Russian, Fargus
{
{
@@ -1878,6 +1895,23 @@ static const MohawkGameDescription gameDescriptions[] = {
"Living Books Player"
},
+ // From Matthew Winder in bug#6557
+ // v1.0E, English, Windows
+ {
+ {
+ "arthurbday",
+ "",
+ AD_ENTRY1s("AB16B.LB", "c169be346de7b0bbfcd18761fc0a3e49", 3093),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GType_LIVINGBOOKSV2,
+ 0,
+ 0,
+ },
+
// From Torsten in bug#3422652
{
{
@@ -2100,6 +2134,22 @@ static const MohawkGameDescription gameDescriptions[] = {
0
},
+ // From Matthew Winder in bug#6557
+ {
+ {
+ "lilmonster",
+ "",
+ AD_ENTRY1s("lmasf.lb", "fcb665df1713d0411a41515efb20bebc", 4136),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GType_LIVINGBOOKSV2,
+ 0,
+ 0
+ },
+
// From afholman in bug#3309308
{
{
@@ -2704,7 +2754,7 @@ static const MohawkGameDescription fallbackDescs[] = {
AD_ENTRY1(0, 0),
Common::UNK_LANG,
Common::kPlatformWindows,
- ADGF_UNSTABLE,
+ ADGF_TESTING,
GUI_OPTIONS_MYST
},
GType_MYST,
@@ -2719,7 +2769,7 @@ static const MohawkGameDescription fallbackDescs[] = {
AD_ENTRY1(0, 0),
Common::UNK_LANG,
Common::kPlatformWindows,
- ADGF_UNSTABLE,
+ ADGF_TESTING,
GUI_OPTIONS_MYST_MAKING_OF
},
GType_MAKINGOF,
@@ -2734,7 +2784,7 @@ static const MohawkGameDescription fallbackDescs[] = {
AD_ENTRY1(0, 0),
Common::UNK_LANG,
Common::kPlatformWindows,
- ADGF_UNSTABLE,
+ ADGF_TESTING,
GUI_OPTIONS_MYST_ME
},
GType_MYST,
diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp
index c16fab9131..e2bc88ebf6 100644
--- a/engines/mohawk/myst.cpp
+++ b/engines/mohawk/myst.cpp
@@ -231,11 +231,9 @@ Common::Error MohawkEngine_Myst::run() {
// Load game from launcher/command line if requested
if (ConfMan.hasKey("save_slot") && hasGameSaveSupport()) {
- uint32 gameToLoad = ConfMan.getInt("save_slot");
- Common::StringArray savedGamesList = MystGameState::generateSaveGameList();
- if (gameToLoad > savedGamesList.size())
- error ("Could not find saved game");
- _gameState->load(savedGamesList[gameToLoad]);
+ int saveSlot = ConfMan.getInt("save_slot");
+ if (!_gameState->load(saveSlot))
+ error("Failed to load save game from slot %i", saveSlot);
} else {
// Start us on the first stack.
if (getGameType() == GType_MAKINGOF)
@@ -1083,19 +1081,14 @@ void MohawkEngine_Myst::loadResources() {
}
Common::Error MohawkEngine_Myst::loadGameState(int slot) {
- if (_gameState->load(MystGameState::generateSaveGameList()[slot]))
+ if (_gameState->load(slot))
return Common::kNoError;
return Common::kUnknownError;
}
Common::Error MohawkEngine_Myst::saveGameState(int slot, const Common::String &desc) {
- Common::StringArray saveList = MystGameState::generateSaveGameList();
-
- if ((uint)slot < saveList.size())
- MystGameState::deleteSave(saveList[slot]);
-
- return _gameState->save(desc) ? Common::kNoError : Common::kUnknownError;
+ return _gameState->save(slot, desc) ? Common::kNoError : Common::kUnknownError;
}
bool MohawkEngine_Myst::hasGameSaveSupport() const {
diff --git a/engines/mohawk/myst_scripts.cpp b/engines/mohawk/myst_scripts.cpp
index 04e7c5a9b7..6ad7dd088b 100644
--- a/engines/mohawk/myst_scripts.cpp
+++ b/engines/mohawk/myst_scripts.cpp
@@ -685,9 +685,14 @@ void MystScriptParser::o_changeBackgroundSound(uint16 op, uint16 var, uint16 arg
// Used on Channelwood Card 3225 with argc = 8 i.e. Conditional Sound List
debugC(kDebugScript, "Opcode %d: Process Sound Block", op);
- Common::MemoryReadStream stream = Common::MemoryReadStream((const byte *) argv, argc * sizeof(uint16));
+ Common::MemoryWriteStreamDynamic writeStream = Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES);
+ for (uint i = 0; i < argc; i++) {
+ writeStream.writeUint16LE(argv[i]);
+ }
+
+ Common::MemoryReadStream readStream = Common::MemoryReadStream(writeStream.getData(), writeStream.size());
- MystSoundBlock soundBlock = _vm->readSoundBlock(&stream);
+ MystSoundBlock soundBlock = _vm->readSoundBlock(&readStream);
_vm->applySoundBlock(soundBlock);
}
diff --git a/engines/mohawk/myst_stacks/myst.cpp b/engines/mohawk/myst_stacks/myst.cpp
index 9d23d2fb10..4dc392a7e9 100644
--- a/engines/mohawk/myst_stacks/myst.cpp
+++ b/engines/mohawk/myst_stacks/myst.cpp
@@ -3089,6 +3089,8 @@ void Myst::clockReset() {
}
void Myst::clockResetWeight() {
+ _vm->_sound->replaceSoundMyst(9113);
+
_clockWeightVideo = _vm->_video->playMovie(_vm->wrapMovieFilename("cl1wlfch", kMystStack));
if (!_clockWeightVideo)
error("Failed to open cl1wlfch movie");
diff --git a/engines/mohawk/myst_state.cpp b/engines/mohawk/myst_state.cpp
index 06cd69b23c..4324d6bde5 100644
--- a/engines/mohawk/myst_state.cpp
+++ b/engines/mohawk/myst_state.cpp
@@ -106,16 +106,12 @@ MystGameState::MystGameState(MohawkEngine_Myst *vm, Common::SaveFileManager *sav
MystGameState::~MystGameState() {
}
-Common::StringArray MystGameState::generateSaveGameList() {
- return g_system->getSavefileManager()->listSavefiles("*.mys");
-}
-
-bool MystGameState::load(const Common::String &filename) {
- if (!loadState(filename)) {
+bool MystGameState::load(int slot) {
+ if (!loadState(slot)) {
return false;
}
- loadMetadata(filename);
+ loadMetadata(slot);
// Set Channelwood elevator state to down, because we start on the lower level
_channelwood.elevatorState = 0;
@@ -136,7 +132,8 @@ bool MystGameState::load(const Common::String &filename) {
return true;
}
-bool MystGameState::loadState(const Common::String &filename) {
+bool MystGameState::loadState(int slot) {
+ Common::String filename = buildSaveFilename(slot);
Common::InSaveFile *loadFile = _saveFileMan->openForLoading(filename);
if (!loadFile) {
return false;
@@ -160,9 +157,10 @@ bool MystGameState::loadState(const Common::String &filename) {
return true;
}
-void MystGameState::loadMetadata(const Common::String &filename) {
+void MystGameState::loadMetadata(int slot) {
// Open the metadata file
- Common::InSaveFile *metadataFile = openMetadataFile(filename);
+ Common::String filename = buildMetadataFilename(slot);
+ Common::InSaveFile *metadataFile = _vm->getSaveFileManager()->openForLoading(filename);
if (!metadataFile) {
return;
}
@@ -179,25 +177,19 @@ void MystGameState::loadMetadata(const Common::String &filename) {
delete metadataFile;
}
-bool MystGameState::save(const Common::String &filename) {
- // Make sure the description does not have an extension
- Common::String desc = filename;
- if (filename.hasSuffix(".mys") || filename.hasSuffix(".MYS")) {
- desc = removeExtension(filename);
- }
-
- if (!saveState(desc)) {
+bool MystGameState::save(int slot, const Common::String &desc) {
+ if (!saveState(slot)) {
return false;
}
updateMetadateForSaving(desc);
- return saveMetadata(desc);
+ return saveMetadata(slot);
}
-bool MystGameState::saveState(const Common::String &desc) {
+bool MystGameState::saveState(int slot) {
// Make sure we have the right extension
- Common::String filename = desc + ".mys";
+ Common::String filename = buildSaveFilename(slot);
Common::OutSaveFile *saveFile = _saveFileMan->openForSaving(filename);
if (!saveFile) {
return false;
@@ -213,6 +205,14 @@ bool MystGameState::saveState(const Common::String &desc) {
return true;
}
+Common::String MystGameState::buildSaveFilename(int slot) {
+ return Common::String::format("myst-%03d.mys", slot);
+}
+
+Common::String MystGameState::buildMetadataFilename(int slot) {
+ return Common::String::format("myst-%03d.mym", slot);
+}
+
void MystGameState::updateMetadateForSaving(const Common::String &desc) {
// Update save creation info
TimeDate t;
@@ -226,10 +226,10 @@ void MystGameState::updateMetadateForSaving(const Common::String &desc) {
_metadata.totalPlayTime = _vm->getTotalPlayTime();
}
-bool MystGameState::saveMetadata(const Common::String &desc) {
+bool MystGameState::saveMetadata(int slot) {
// Write the metadata to a separate file so that the save files
// are still compatible with the original engine
- Common::String metadataFilename = desc + ".mym";
+ Common::String metadataFilename = buildMetadataFilename(slot);
Common::OutSaveFile *metadataFile = _saveFileMan->openForSaving(metadataFilename);
if (!metadataFile) {
return false;
@@ -248,14 +248,12 @@ bool MystGameState::saveMetadata(const Common::String &desc) {
return true;
}
-SaveStateDescriptor MystGameState::querySaveMetaInfos(const Common::String filename) {
- SaveStateDescriptor desc;
- desc.setDescription(filename);
-
+SaveStateDescriptor MystGameState::querySaveMetaInfos(int slot) {
// Open the metadata file
- Common::InSaveFile *metadataFile = openMetadataFile(filename);
+ Common::String filename = buildMetadataFilename(slot);
+ Common::InSaveFile *metadataFile = g_system->getSavefileManager()->openForLoading(filename);
if (!metadataFile) {
- return desc;
+ return SaveStateDescriptor();
}
Common::Serializer m(metadataFile, nullptr);
@@ -264,10 +262,11 @@ SaveStateDescriptor MystGameState::querySaveMetaInfos(const Common::String filen
Mohawk::MystSaveMetadata metadata;
if (!metadata.sync(m)) {
delete metadataFile;
- return desc;
+ return SaveStateDescriptor();
}
// Set the save description
+ SaveStateDescriptor desc;
desc.setDescription(metadata.saveDescription);
desc.setSaveDate(metadata.saveYear, metadata.saveMonth, metadata.saveDay);
desc.setSaveTime(metadata.saveHour, metadata.saveMinute);
@@ -279,20 +278,26 @@ SaveStateDescriptor MystGameState::querySaveMetaInfos(const Common::String filen
return desc;
}
-Common::InSaveFile *MystGameState::openMetadataFile(const Common::String &filename) {
- // Remove the extension
- Common::String baseName = removeExtension(filename);
-
+Common::String MystGameState::querySaveDescription(int slot) {
// Open the metadata file
- return g_system->getSavefileManager()->openForLoading(baseName + ".mym");
-}
+ Common::String filename = buildMetadataFilename(slot);
+ Common::InSaveFile *metadataFile = g_system->getSavefileManager()->openForLoading(filename);
+ if (!metadataFile) {
+ return "";
+ }
+
+ Common::Serializer m(metadataFile, nullptr);
-Common::String MystGameState::removeExtension(const Common::String &filename) {
- Common::String baseName = filename;
- for (uint i = 0; i < 4; i++) {
- baseName.deleteLastChar();
+ // Read the metadata file
+ Mohawk::MystSaveMetadata metadata;
+ if (!metadata.sync(m)) {
+ delete metadataFile;
+ return "";
}
- return baseName;
+
+ delete metadataFile;
+
+ return metadata.saveDescription;
}
void MystGameState::syncGameState(Common::Serializer &s, bool isME) {
@@ -471,12 +476,14 @@ void MystGameState::syncGameState(Common::Serializer &s, bool isME) {
warning("Unexpected File Position 0x%03X At End of Save/Load", s.bytesSynced());
}
-void MystGameState::deleteSave(const Common::String &saveName) {
- debugC(kDebugSaveLoad, "Deleting save file \'%s\'", saveName.c_str());
- Common::String basename = removeExtension(saveName);
+void MystGameState::deleteSave(int slot) {
+ Common::String filename = buildSaveFilename(slot);
+ Common::String metadataFilename = buildMetadataFilename(slot);
+
+ debugC(kDebugSaveLoad, "Deleting save file \'%s\'", filename.c_str());
- g_system->getSavefileManager()->removeSavefile(saveName);
- g_system->getSavefileManager()->removeSavefile(basename + ".mym");
+ g_system->getSavefileManager()->removeSavefile(filename);
+ g_system->getSavefileManager()->removeSavefile(metadataFilename);
}
void MystGameState::addZipDest(uint16 stack, uint16 view) {
diff --git a/engines/mohawk/myst_state.h b/engines/mohawk/myst_state.h
index 50359a5b52..7d5f3f7102 100644
--- a/engines/mohawk/myst_state.h
+++ b/engines/mohawk/myst_state.h
@@ -58,12 +58,12 @@ public:
MystGameState(MohawkEngine_Myst*, Common::SaveFileManager*);
~MystGameState();
- static Common::StringArray generateSaveGameList();
- static SaveStateDescriptor querySaveMetaInfos(const Common::String filename);
+ static SaveStateDescriptor querySaveMetaInfos(int slot);
+ static Common::String querySaveDescription(int slot);
- bool load(const Common::String &filename);
- bool save(const Common::String &filename);
- static void deleteSave(const Common::String &saveName);
+ bool load(int slot);
+ bool save(int slot, const Common::String &desc);
+ static void deleteSave(int slot);
void addZipDest(uint16 stack, uint16 view);
bool isReachableZipDest(uint16 stack, uint16 view);
@@ -292,13 +292,13 @@ public:
private:
void syncGameState(Common::Serializer &s, bool isME);
- static Common::InSaveFile *openMetadataFile(const Common::String &filename);
- bool loadState(const Common::String &filename);
- void loadMetadata(const Common::String &filename);
- bool saveState(const Common::String &desc);
+ static Common::String buildSaveFilename(int slot);
+ static Common::String buildMetadataFilename(int slot);
+ bool loadState(int slot);
+ void loadMetadata(int slot);
+ bool saveState(int slot);
void updateMetadateForSaving(const Common::String &desc);
- bool saveMetadata(const Common::String &desc);
- static Common::String removeExtension(const Common::String &filename);
+ bool saveMetadata(int slot);
// The values in these regions are lists of VIEW resources
// which correspond to visited zip destinations
diff --git a/engines/queen/detection.cpp b/engines/queen/detection.cpp
index 81e0767836..aed8b7dcb1 100644
--- a/engines/queen/detection.cpp
+++ b/engines/queen/detection.cpp
@@ -105,6 +105,19 @@ static const QueenGameDescription gameDescriptions[] = {
},
},
+ // DOS Demo - English (from Bugreport #6946)
+ {
+ {
+ "queen",
+ "Demo Alt",
+ AD_ENTRY1s("queen.1", "2871fc6f8090f37fa1a0c556a1c97460", 3735447),
+ Common::EN_ANY,
+ Common::kPlatformDOS,
+ ADGF_DEMO,
+ GUIO1(GUIO_NOSPEECH)
+ },
+ },
+
// DOS Interview Demo - English
{
{
@@ -131,20 +144,18 @@ static const QueenGameDescription gameDescriptions[] = {
},
},
-#if 0
// Amiga Floppy - English
{
{
"queen",
"Floppy",
- AD_ENTRY1s("queen.1", NULL, 351775), // TODO: Fill in correct MD5
+ AD_ENTRY1s("queen.1", "9c209c2cbc1730e3138663c4fd29c2e8", 351775),
Common::EN_ANY,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOSPEECH)
},
},
-#endif
// DOS Floppy - English
{
@@ -198,6 +209,19 @@ static const QueenGameDescription gameDescriptions[] = {
},
},
+ // DOS Floppy - Russian (From Bugreport #6946)
+ {
+ {
+ "queen",
+ "Floppy",
+ AD_ENTRY1s("queen.1", "f5e827645d3c887be3bdf4729d847756", 22677657),
+ Common::RU_RUS,
+ Common::kPlatformDOS,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOSPEECH)
+ },
+ },
+
// DOS CD - French
{
{
@@ -211,35 +235,31 @@ static const QueenGameDescription gameDescriptions[] = {
},
},
-#if 0
// DOS Floppy - German
{
{
"queen",
"Floppy",
- AD_ENTRY1s("queen.1", NULL, 22240013), // TODO: Fill in correct MD5
+ AD_ENTRY1s("queen.1", "f5e827645d3c887be3bdf4729d847756", 22240013),
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOSPEECH)
},
},
-#endif
-#if 0
// DOS CD - German
{
{
"queen",
"Talkie",
- AD_ENTRY1s("queen.1", NULL, 217648975), // TODO: Fill in correct MD5
+ AD_ENTRY1s("queen.1", "551d595be8af890fc4cb8533c9c5f5f1", 217648975),
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ALT_INTRO)
},
},
-#endif
#if 0
// DOS CD - Hebrew
@@ -256,20 +276,18 @@ static const QueenGameDescription gameDescriptions[] = {
},
#endif
-#if 0
// DOS Floppy - Italian
{
{
"queen",
"Floppy",
- AD_ENTRY1s("queen.1", NULL, 22461366), // TODO: Fill in correct MD5
+ AD_ENTRY1s("queen.1", "f5e827645d3c887be3bdf4729d847756", 22461366),
Common::IT_ITA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOSPEECH)
},
},
-#endif
// DOS CD - Italian
{
@@ -284,20 +302,18 @@ static const QueenGameDescription gameDescriptions[] = {
},
},
-#if 0
// DOS CD - Spanish
{
{
"queen",
"Talkie",
- AD_ENTRY1s("queen.1", NULL, 190730602), // TODO: Fill in correct MD5
+ AD_ENTRY1s("queen.1", "b6302bccf70463de3d5faf0f0628f742", 190730602),
Common::ES_ESP,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ALT_INTRO)
},
},
-#endif
// DOS CD - English (Compressed Freeware Release v1.0)
{
@@ -377,6 +393,19 @@ static const QueenGameDescription gameDescriptions[] = {
},
},
+ // DOS CD - Hungarian (Compressed Freeware Release v1.02)
+ {
+ {
+ "queen",
+ "Talkie",
+ AD_ENTRY1s("queen.1c", "21fd690b372f8a6289f6f33bc986276c", 51329031),
+ Common::HU_HUN,
+ Common::kPlatformDOS,
+ ADGF_NO_FLAGS,
+ GUIO1(GAMEOPTION_ALT_INTRO)
+ },
+ },
+
// TODO: Freeware Release for Spanish DOS CD is missing.
#if 0
// DOS CD - Spanish (Compressed Freeware Release v1.0)
diff --git a/engines/saga/detection_tables.h b/engines/saga/detection_tables.h
index 8b3a0e5207..71225ceb2f 100644
--- a/engines/saga/detection_tables.h
+++ b/engines/saga/detection_tables.h
@@ -733,6 +733,36 @@ static const SAGAGameDescription gameDescriptions[] = {
GUIO1(GUIO_NOASPECT)
},
GID_IHNM,
+ GF_IHNM_COLOR_FIX,
+ IHNM_DEFAULT_SCENE,
+ &IHNM_Resources,
+ ARRAYSIZE(IHNMCD_GameFonts),
+ IHNMCD_GameFonts,
+ NULL,
+ },
+
+ // I Have No Mouth And I Must Scream - German fan CD translation
+ // English CD version with German text patch (with Nimdok)
+ // (English speech - German text)
+ {
+ {
+ "ihnm",
+ "fan-made",
+ {
+ {"musicfm.res", GAME_MUSICFILE_FM, "0439083e3dfdc51b486071d45872ae52", 302676},
+ {"musicgm.res", GAME_MUSICFILE_GM, "80f875a1fb384160d1f4b27166eef583", 314020},
+ {"scream.res", GAME_RESOURCEFILE, "46bbdc65d164ba7e89836a0935eec8e6", 79219797},
+ {"scripts.res", GAME_SCRIPTFILE, "be38bbc5a26be809dbf39f13befebd01", 523800},
+ {"patch.re_", GAME_PATCHFILE | GAME_RESOURCEFILE, "58b79e61594779513c7f2d35509fa89e", 5038599},
+ {"sfx.res", GAME_SOUNDFILE, "1c610d543f32ec8b525e3f652536f269", 22561056},
+ { NULL, 0, NULL, 0}
+ },
+ Common::DE_DEU,
+ Common::kPlatformDOS,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_IHNM,
0,
IHNM_DEFAULT_SCENE,
&IHNM_Resources,
@@ -761,7 +791,7 @@ static const SAGAGameDescription gameDescriptions[] = {
GUIO1(GUIO_NOASPECT)
},
GID_IHNM,
- 0,
+ GF_IHNM_COLOR_FIX,
IHNM_DEFAULT_SCENE,
&IHNM_Resources,
ARRAYSIZE(IHNMCD_GameFonts),
@@ -790,7 +820,7 @@ static const SAGAGameDescription gameDescriptions[] = {
GUIO1(GUIO_NOASPECT)
},
GID_IHNM,
- 0,
+ GF_IHNM_COLOR_FIX,
IHNM_DEFAULT_SCENE,
&IHNM_Resources,
ARRAYSIZE(IHNMCD_GameFonts),
diff --git a/engines/saga/displayinfo.h b/engines/saga/displayinfo.h
index a0cdf5733b..67448936ce 100644
--- a/engines/saga/displayinfo.h
+++ b/engines/saga/displayinfo.h
@@ -177,11 +177,11 @@ static PanelButton ITE_OptionPanelButtons[] = {
{kPanelButtonOption, 241,98, 57,17, kTextSave,'s',0, 0,0,0}, //save
{kPanelButtonOptionSaveFiles, 166,20, 112,74, 0,'-',0, 0,0,0}, //savefiles
- {kPanelButtonOptionText,106,4, 0,0, kTextGameOptions,'-',0, 0,0,0}, // text: game options
- {kPanelButtonOptionText,11,22, 0,0, kTextReadingSpeed,'-',0, 0,0,0}, // text: read speed
- {kPanelButtonOptionText,28,22, 0,0, kTextShowDialog,'-',0, 0,0,0}, // text: read speed
- {kPanelButtonOptionText,73,41, 0,0, kTextMusic,'-',0, 0,0,0}, // text: music
- {kPanelButtonOptionText,69,60, 0,0, kTextSound,'-',0, 0,0,0}, // text: noise
+ {kPanelButtonOptionText,-1,4, 0,0, kTextGameOptions,'-',0, 0,0,0}, // text: game options
+ {kPanelButtonOptionText,5,18, 109,17, kTextReadingSpeed,'-',0, 0,0,0}, // text: read speed
+ {kPanelButtonOptionText,5,18, 109,17, kTextShowDialog,'-',0, 0,0,0}, // text: read speed
+ {kPanelButtonOptionText,5,37, 109,17, kTextMusic,'-',0, 0,0,0}, // text: music
+ {kPanelButtonOptionText,5,56, 109,17, kTextSound,'-',0, 0,0,0}, // text: noise
};
static PanelButton ITE_QuitPanelButtons[] = {
@@ -326,10 +326,10 @@ static PanelButton IHNM_ConversePanelButtons[] = {
static PanelButton IHNM_OptionPanelButtons[] = {
{kPanelButtonOptionSlider, 421,16, 16,138, 0,'-',0, 0,0,0}, //slider-scroller
- {kPanelButtonOptionText,28,36, 0,0, kTextReadingSpeed,'-',0, 0,0,0}, // text: read speed
- {kPanelButtonOptionText,60,61, 0,0, kTextMusic,'-',0, 0,0,0}, // text: music
- {kPanelButtonOptionText,60,86, 0,0, kTextSound,'-',0, 0,0,0}, // text: noise
- {kPanelButtonOptionText,56,111, 0,0, kTextVoices,'-',0, 0,0,0}, // text: voices
+ {kPanelButtonOptionText,11,30, 139,21, kTextReadingSpeed,'-',0, 0,0,0}, // text: read speed
+ {kPanelButtonOptionText,11,55, 139,21, kTextMusic,'-',0, 0,0,0}, // text: music
+ {kPanelButtonOptionText,11,80, 139,21, kTextSound,'-',0, 0,0,0}, // text: noise
+ {kPanelButtonOptionText,11,105, 139,21, kTextVoices,'-',0, 0,0,0}, // text: voices
{kPanelButtonOption, 154,30, 79,23, kTextReadingSpeed,'r',0, 0,0,0}, //read speed
{kPanelButtonOption, 154,55, 79,23, kTextMusic,'m',0, 0,0,0}, //music
{kPanelButtonOption, 154,80, 79,23, kTextSound,'n',0, 0,0,0}, //sound-noise
diff --git a/engines/saga/interface.cpp b/engines/saga/interface.cpp
index ad940aaf8b..cb09d53762 100644
--- a/engines/saga/interface.cpp
+++ b/engines/saga/interface.cpp
@@ -867,7 +867,7 @@ void Interface::calcOptionSaveSlider() {
void Interface::drawPanelText(InterfacePanel *panel, PanelButton *panelButton) {
const char *text;
- int textWidth;
+ int textWidth, textHeight;
Rect rect;
Point textPoint;
KnownColor textShadowKnownColor = kKnownColorVerbTextShadow;
@@ -900,12 +900,26 @@ void Interface::drawPanelText(InterfacePanel *panel, PanelButton *panelButton) {
}
panel->calcPanelButtonRect(panelButton, rect);
+ if (_vm->getGameId() == GID_ITE) {
+ textWidth = _vm->_font->getStringWidth(kKnownFontMedium, text, 0, kFontNormal);
+ textHeight = _vm->_font->getHeight(kKnownFontMedium);
+ } else {
+ textWidth = _vm->_font->getStringWidth(kKnownFontVerb, text, 0, kFontNormal);
+ textHeight = _vm->_font->getHeight(kKnownFontVerb);
+ }
if (panelButton->xOffset < 0) {
- if (_vm->getGameId() == GID_ITE)
- textWidth = _vm->_font->getStringWidth(kKnownFontMedium, text, 0, kFontNormal);
- else
- textWidth = _vm->_font->getStringWidth(kKnownFontVerb, text, 0, kFontNormal);
+ // Special case: Centered to dialog. This is used for things like the
+ // title of a dialog.
rect.left += 2 + (panel->imageWidth - 1 - textWidth) / 2;
+ } else {
+ // The standard case is used for the things that look a bit like buttons
+ // but are not clickable, e.g. texts like "Music", "Sound", etc.
+ if (_vm->getGameId() == GID_ITE) {
+ rect.left = rect.right - textWidth - 3;
+ } else {
+ rect.left = (rect.right + rect.left - textWidth) / 2;
+ }
+ rect.top = (rect.top + rect.bottom - textHeight) / 2;
}
textPoint.x = rect.left;
@@ -1865,7 +1879,7 @@ void Interface::drawStatusBar() {
// Fixes bug #1848016 - "IHNM: Wrong Subtitles Color (Spanish)". This
// also applies to the German and French versions (bug #7064 - "IHNM:
// text mistake in german version").
- int offset = (_vm->getLanguage() == Common::ES_ESP || _vm->getLanguage() == Common::DE_DEU || _vm->getLanguage() == Common::FR_FRA) ? 1 : 0;
+ int offset = (_vm->getFeatures() & GF_IHNM_COLOR_FIX) ? 1 : 0;
// Disable the status text in IHNM when the chapter is 8
if (_vm->getGameId() == GID_IHNM && _vm->_scene->currentChapterNumber() == 8)
diff --git a/engines/saga/saga.cpp b/engines/saga/saga.cpp
index 77a21e7f93..649888e7ea 100644
--- a/engines/saga/saga.cpp
+++ b/engines/saga/saga.cpp
@@ -582,7 +582,7 @@ ColorId SagaEngine::KnownColor2ColorId(KnownColor knownColor) {
// Fixes bug #1848016 - "IHNM: Wrong Subtitles Color (Spanish)". This
// also applies to the German and French versions (bug #7064 - "IHNM:
// text mistake in german version").
- int offset = (getLanguage() == Common::ES_ESP || getLanguage() == Common::DE_DEU || getLanguage() == Common::FR_FRA) ? 1 : 0;
+ int offset = (getFeatures() & GF_IHNM_COLOR_FIX) ? 1 : 0;
switch (knownColor) {
case(kKnownColorTransparent):
diff --git a/engines/saga/saga.h b/engines/saga/saga.h
index 9c7b2f5295..06cb411e5a 100644
--- a/engines/saga/saga.h
+++ b/engines/saga/saga.h
@@ -139,7 +139,8 @@ enum GameFeatures {
GF_ITE_FLOPPY = 1 << 0,
GF_ITE_DOS_DEMO = 1 << 1,
GF_EXTRA_ITE_CREDITS = 1 << 2,
- GF_8BIT_UNSIGNED_PCM = 1 << 3
+ GF_8BIT_UNSIGNED_PCM = 1 << 3,
+ GF_IHNM_COLOR_FIX = 1 << 4
};
enum VerbTypeIds {
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp
index a092e0676d..1661f92cfe 100644
--- a/engines/sci/console.cpp
+++ b/engines/sci/console.cpp
@@ -1861,7 +1861,7 @@ bool Console::cmdSavedBits(int argc, const char **argv) {
for (uint i = 0; i < entries.size(); ++i) {
uint16 offset = entries[i].getOffset();
- const Hunk& h = hunks->_table[offset];
+ const Hunk& h = hunks->at(offset);
if (strcmp(h.type, "SaveBits()") == 0) {
byte* memoryPtr = (byte *)h.mem;
@@ -1928,7 +1928,7 @@ bool Console::cmdShowSavedBits(int argc, const char **argv) {
return true;
}
- const Hunk& h = hunks->_table[memoryHandle.getOffset()];
+ const Hunk& h = hunks->at(memoryHandle.getOffset());
if (strcmp(h.type, "SaveBits()") != 0) {
debugPrintf("Invalid address.\n");
@@ -2144,32 +2144,32 @@ bool Console::segmentInfo(int nr) {
break;
case SEG_TYPE_CLONES: {
- CloneTable *ct = (CloneTable *)mobj;
+ CloneTable &ct = *(CloneTable *)mobj;
debugPrintf("clones\n");
- for (uint i = 0; i < ct->_table.size(); i++)
- if (ct->isValidEntry(i)) {
+ for (uint i = 0; i < ct.size(); i++)
+ if (ct.isValidEntry(i)) {
reg_t objpos = make_reg(nr, i);
debugPrintf(" [%04x] %s; copy of ", i, _engine->_gamestate->_segMan->getObjectName(objpos));
// Object header
- const Object *obj = _engine->_gamestate->_segMan->getObject(ct->_table[i].getPos());
+ const Object *obj = _engine->_gamestate->_segMan->getObject(ct[i].getPos());
if (obj)
- debugPrintf("[%04x:%04x] %s : %3d vars, %3d methods\n", PRINT_REG(ct->_table[i].getPos()),
- _engine->_gamestate->_segMan->getObjectName(ct->_table[i].getPos()),
+ debugPrintf("[%04x:%04x] %s : %3d vars, %3d methods\n", PRINT_REG(ct[i].getPos()),
+ _engine->_gamestate->_segMan->getObjectName(ct[i].getPos()),
obj->getVarCount(), obj->getMethodCount());
}
}
break;
case SEG_TYPE_LISTS: {
- ListTable *lt = (ListTable *)mobj;
+ ListTable &lt = *(ListTable *)mobj;
debugPrintf("lists\n");
- for (uint i = 0; i < lt->_table.size(); i++)
- if (lt->isValidEntry(i)) {
+ for (uint i = 0; i < lt.size(); i++)
+ if (lt.isValidEntry(i)) {
debugPrintf(" [%04x]: ", i);
- printList(&(lt->_table[i]));
+ printList(&lt[i]);
}
}
break;
@@ -2180,13 +2180,13 @@ bool Console::segmentInfo(int nr) {
}
case SEG_TYPE_HUNK: {
- HunkTable *ht = (HunkTable *)mobj;
+ HunkTable &ht = *(HunkTable *)mobj;
- debugPrintf("hunk (total %d)\n", ht->entries_used);
- for (uint i = 0; i < ht->_table.size(); i++)
- if (ht->isValidEntry(i)) {
+ debugPrintf("hunk (total %d)\n", ht.entries_used);
+ for (uint i = 0; i < ht.size(); i++)
+ if (ht.isValidEntry(i)) {
debugPrintf(" [%04x] %d bytes at %p, type=%s\n",
- i, ht->_table[i].size, ht->_table[i].mem, ht->_table[i].type);
+ i, ht[i].size, ht[i].mem, ht[i].type);
}
}
break;
@@ -4362,7 +4362,7 @@ void Console::printList(List *list) {
return;
}
- node = &(nt->_table[pos.getOffset()]);
+ node = &nt->at(pos.getOffset());
debugPrintf("\t%04x:%04x : %04x:%04x -> %04x:%04x\n", PRINT_REG(pos), PRINT_REG(node->key), PRINT_REG(node->value));
@@ -4392,7 +4392,7 @@ int Console::printNode(reg_t addr) {
return 1;
}
- list = &(lt->_table[addr.getOffset()]);
+ list = &lt->at(addr.getOffset());
debugPrintf("%04x:%04x : first x last = (%04x:%04x, %04x:%04x)\n", PRINT_REG(addr), PRINT_REG(list->first), PRINT_REG(list->last));
} else {
@@ -4411,7 +4411,7 @@ int Console::printNode(reg_t addr) {
debugPrintf("Address does not contain a node\n");
return 1;
}
- node = &(nt->_table[addr.getOffset()]);
+ node = &nt->at(addr.getOffset());
debugPrintf("%04x:%04x : prev x next = (%04x:%04x, %04x:%04x); maps %04x:%04x -> %04x:%04x\n",
PRINT_REG(addr), PRINT_REG(node->pred), PRINT_REG(node->succ), PRINT_REG(node->key), PRINT_REG(node->value));
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index fcb65157d8..0cc1e752e1 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -60,24 +60,125 @@ namespace Sci {
#pragma mark -
-// Experimental hack: Use syncWithSerializer to sync. By default, this assume
-// the object to be synced is a subclass of Serializable and thus tries to invoke
-// the saveLoadWithSerializer() method. But it is possible to specialize this
-// template function to handle stuff that is not implementing that interface.
-template<typename T>
-void syncWithSerializer(Common::Serializer &s, T &obj) {
+// These are serialization functions for various objects.
+
+void syncWithSerializer(Common::Serializer &s, Common::Serializable &obj) {
+ obj.saveLoadWithSerializer(s);
+}
+
+// FIXME: Object could implement Serializable to make use of the function
+// above.
+void syncWithSerializer(Common::Serializer &s, Object &obj) {
obj.saveLoadWithSerializer(s);
}
+void syncWithSerializer(Common::Serializer &s, reg_t &obj) {
+ // Segment and offset are accessed directly here
+ s.syncAsUint16LE(obj._segment);
+ s.syncAsUint16LE(obj._offset);
+}
+
+void syncWithSerializer(Common::Serializer &s, synonym_t &obj) {
+ s.syncAsUint16LE(obj.replaceant);
+ s.syncAsUint16LE(obj.replacement);
+}
+
+void syncWithSerializer(Common::Serializer &s, Class &obj) {
+ s.syncAsSint32LE(obj.script);
+ syncWithSerializer(s, obj.reg);
+}
+
+void syncWithSerializer(Common::Serializer &s, List &obj) {
+ syncWithSerializer(s, obj.first);
+ syncWithSerializer(s, obj.last);
+}
+
+void syncWithSerializer(Common::Serializer &s, Node &obj) {
+ syncWithSerializer(s, obj.pred);
+ syncWithSerializer(s, obj.succ);
+ syncWithSerializer(s, obj.key);
+ syncWithSerializer(s, obj.value);
+}
+
+#ifdef ENABLE_SCI32
+void syncWithSerializer(Common::Serializer &s, SciArray<reg_t> &obj) {
+ byte type = 0;
+ uint32 size = 0;
+
+ if (s.isSaving()) {
+ type = (byte)obj.getType();
+ size = obj.getSize();
+ }
+ s.syncAsByte(type);
+ s.syncAsUint32LE(size);
+ if (s.isLoading()) {
+ obj.setType((int8)type);
+
+ // HACK: Skip arrays that have a negative type
+ if ((int8)type < 0)
+ return;
+
+ obj.setSize(size);
+ }
+
+ for (uint32 i = 0; i < size; i++) {
+ reg_t value;
+
+ if (s.isSaving())
+ value = obj.getValue(i);
+
+ syncWithSerializer(s, value);
+
+ if (s.isLoading())
+ obj.setValue(i, value);
+ }
+}
+
+void syncWithSerializer(Common::Serializer &s, SciString &obj) {
+ uint32 size = 0;
+
+ if (s.isSaving()) {
+ size = obj.getSize();
+ s.syncAsUint32LE(size);
+ } else {
+ s.syncAsUint32LE(size);
+ obj.setSize(size);
+ }
+
+ for (uint32 i = 0; i < size; i++) {
+ char value = 0;
+
+ if (s.isSaving())
+ value = obj.getValue(i);
+
+ s.syncAsByte(value);
+
+ if (s.isLoading())
+ obj.setValue(i, value);
+ }
+}
+#endif
+
+#pragma mark -
+
// By default, sync using syncWithSerializer, which in turn can easily be overloaded.
template<typename T>
struct DefaultSyncer : Common::BinaryFunction<Common::Serializer, T, void> {
void operator()(Common::Serializer &s, T &obj) const {
- //obj.saveLoadWithSerializer(s);
syncWithSerializer(s, obj);
}
};
+// Syncer for entries in a segment obj table
+template<typename T>
+struct SegmentObjTableEntrySyncer : Common::BinaryFunction<Common::Serializer, typename T::Entry &, void> {
+ void operator()(Common::Serializer &s, typename T::Entry &entry) const {
+ s.syncAsSint32LE(entry.next_free);
+
+ syncWithSerializer(s, entry.data);
+ }
+};
+
/**
* Sync a Common::Array using a Common::Serializer.
* When saving, this writes the length of the array, then syncs (writes) all entries.
@@ -116,18 +217,10 @@ void syncArray(Common::Serializer &s, Common::Array<T> &arr) {
sync(s, arr);
}
-
-template<>
-void syncWithSerializer(Common::Serializer &s, reg_t &obj) {
- // Segment and offset are accessed directly here
- s.syncAsUint16LE(obj._segment);
- s.syncAsUint16LE(obj._offset);
-}
-
-template<>
-void syncWithSerializer(Common::Serializer &s, synonym_t &obj) {
- s.syncAsUint16LE(obj.replaceant);
- s.syncAsUint16LE(obj.replacement);
+template<typename T, class Syncer>
+void syncArray(Common::Serializer &s, Common::Array<T> &arr) {
+ ArraySyncer<T, Syncer> sync;
+ sync(s, arr);
}
void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
@@ -247,12 +340,6 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
}
-template<>
-void syncWithSerializer(Common::Serializer &s, Class &obj) {
- s.syncAsSint32LE(obj.script);
- syncWithSerializer(s, obj.reg);
-}
-
static void sync_SavegameMetadata(Common::Serializer &s, SavegameMetadata &obj) {
s.syncString(obj.name);
s.syncVersion(CURRENT_SAVEGAME_VERSION);
@@ -331,102 +418,13 @@ void Object::saveLoadWithSerializer(Common::Serializer &s) {
syncArray<reg_t>(s, _variables);
}
-template<>
-void syncWithSerializer(Common::Serializer &s, SegmentObjTable<Clone>::Entry &obj) {
- s.syncAsSint32LE(obj.next_free);
-
- syncWithSerializer<Object>(s, obj);
-}
-
-template<>
-void syncWithSerializer(Common::Serializer &s, SegmentObjTable<List>::Entry &obj) {
- s.syncAsSint32LE(obj.next_free);
-
- syncWithSerializer(s, obj.first);
- syncWithSerializer(s, obj.last);
-}
-
-template<>
-void syncWithSerializer(Common::Serializer &s, SegmentObjTable<Node>::Entry &obj) {
- s.syncAsSint32LE(obj.next_free);
-
- syncWithSerializer(s, obj.pred);
- syncWithSerializer(s, obj.succ);
- syncWithSerializer(s, obj.key);
- syncWithSerializer(s, obj.value);
-}
-
-#ifdef ENABLE_SCI32
-template<>
-void syncWithSerializer(Common::Serializer &s, SegmentObjTable<SciArray<reg_t> >::Entry &obj) {
- s.syncAsSint32LE(obj.next_free);
-
- byte type = 0;
- uint32 size = 0;
-
- if (s.isSaving()) {
- type = (byte)obj.getType();
- size = obj.getSize();
- }
- s.syncAsByte(type);
- s.syncAsUint32LE(size);
- if (s.isLoading()) {
- obj.setType((int8)type);
-
- // HACK: Skip arrays that have a negative type
- if ((int8)type < 0)
- return;
-
- obj.setSize(size);
- }
-
- for (uint32 i = 0; i < size; i++) {
- reg_t value;
-
- if (s.isSaving())
- value = obj.getValue(i);
-
- syncWithSerializer(s, value);
-
- if (s.isLoading())
- obj.setValue(i, value);
- }
-}
-
-template<>
-void syncWithSerializer(Common::Serializer &s, SegmentObjTable<SciString>::Entry &obj) {
- s.syncAsSint32LE(obj.next_free);
-
- uint32 size = 0;
-
- if (s.isSaving()) {
- size = obj.getSize();
- s.syncAsUint32LE(size);
- } else {
- s.syncAsUint32LE(size);
- obj.setSize(size);
- }
-
- for (uint32 i = 0; i < size; i++) {
- char value = 0;
-
- if (s.isSaving())
- value = obj.getValue(i);
-
- s.syncAsByte(value);
-
- if (s.isLoading())
- obj.setValue(i, value);
- }
-}
-#endif
template<typename T>
void sync_Table(Common::Serializer &s, T &obj) {
s.syncAsSint32LE(obj.first_free);
s.syncAsSint32LE(obj.entries_used);
- syncArray<typename T::Entry>(s, obj._table);
+ syncArray<typename T::Entry, SegmentObjTableEntrySyncer<T> >(s, obj._table);
}
void CloneTable::saveLoadWithSerializer(Common::Serializer &s) {
@@ -903,7 +901,7 @@ void SegManager::reconstructClones() {
if (!isUsed)
continue;
- CloneTable::Entry &seeker = ct->_table[j];
+ CloneTable::value_type &seeker = ct->at(j);
const Object *baseObj = getObject(seeker.getSpeciesSelector());
seeker.cloneFromObject(baseObj);
if (!baseObj) {
diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp
index 7d70f30d55..b017e62df7 100644
--- a/engines/sci/engine/scriptdebug.cpp
+++ b/engines/sci/engine/scriptdebug.cpp
@@ -741,13 +741,13 @@ void logKernelCall(const KernelFunction *kernelCall, const KernelSubFunction *ke
switch (mobj->getType()) {
case SEG_TYPE_HUNK:
{
- HunkTable *ht = (HunkTable *)mobj;
+ HunkTable &ht = *(HunkTable *)mobj;
int index = argv[parmNr].getOffset();
- if (ht->isValidEntry(index)) {
+ if (ht.isValidEntry(index)) {
// NOTE: This ", deleted" isn't as useful as it could
// be because it prints the status _after_ the kernel
// call.
- debugN(" ('%s' hunk%s)", ht->_table[index].type, ht->_table[index].mem ? "" : ", deleted");
+ debugN(" ('%s' hunk%s)", ht[index].type, ht[index].mem ? "" : ", deleted");
} else
debugN(" (INVALID hunk ref)");
break;
diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp
index 8090b1861d..95e3cd15f9 100644
--- a/engines/sci/engine/seg_manager.cpp
+++ b/engines/sci/engine/seg_manager.cpp
@@ -247,9 +247,9 @@ Object *SegManager::getObject(reg_t pos) const {
if (mobj != NULL) {
if (mobj->getType() == SEG_TYPE_CLONES) {
- CloneTable *ct = (CloneTable *)mobj;
- if (ct->isValidEntry(pos.getOffset()))
- obj = &(ct->_table[pos.getOffset()]);
+ CloneTable &ct = *(CloneTable *)mobj;
+ if (ct.isValidEntry(pos.getOffset()))
+ obj = &(ct[pos.getOffset()]);
else
warning("getObject(): Trying to get an invalid object");
} else if (mobj->getType() == SEG_TYPE_SCRIPT) {
@@ -313,7 +313,7 @@ reg_t SegManager::findObjectByName(const Common::String &name, int index) {
} else if (mobj->getType() == SEG_TYPE_CLONES) {
// It's clone table, scan all objects in it
const CloneTable *ct = (const CloneTable *)mobj;
- for (uint idx = 0; idx < ct->_table.size(); ++idx) {
+ for (uint idx = 0; idx < ct->size(); ++idx) {
if (!ct->isValidEntry(idx))
continue;
@@ -404,7 +404,7 @@ reg_t SegManager::allocateHunkEntry(const char *hunk_type, int size) {
offset = table->allocEntry();
reg_t addr = make_reg(_hunksSegId, offset);
- Hunk *h = &(table->_table[offset]);
+ Hunk *h = &table->at(offset);
if (!h)
return NULL_REG;
@@ -424,7 +424,7 @@ byte *SegManager::getHunkPointer(reg_t addr) {
return NULL;
}
- return (byte *)ht->_table[addr.getOffset()].mem;
+ return (byte *)ht->at(addr.getOffset()).mem;
}
Clone *SegManager::allocateClone(reg_t *addr) {
@@ -439,7 +439,7 @@ Clone *SegManager::allocateClone(reg_t *addr) {
offset = table->allocEntry();
*addr = make_reg(_clonesSegId, offset);
- return &(table->_table[offset]);
+ return &table->at(offset);
}
List *SegManager::allocateList(reg_t *addr) {
@@ -453,7 +453,7 @@ List *SegManager::allocateList(reg_t *addr) {
offset = table->allocEntry();
*addr = make_reg(_listsSegId, offset);
- return &(table->_table[offset]);
+ return &table->at(offset);
}
Node *SegManager::allocateNode(reg_t *addr) {
@@ -467,7 +467,7 @@ Node *SegManager::allocateNode(reg_t *addr) {
offset = table->allocEntry();
*addr = make_reg(_nodesSegId, offset);
- return &(table->_table[offset]);
+ return &table->at(offset);
}
reg_t SegManager::newNode(reg_t value, reg_t key) {
@@ -486,14 +486,14 @@ List *SegManager::lookupList(reg_t addr) {
return NULL;
}
- ListTable *lt = (ListTable *)_heap[addr.getSegment()];
+ ListTable &lt = *(ListTable *)_heap[addr.getSegment()];
- if (!lt->isValidEntry(addr.getOffset())) {
+ if (!lt.isValidEntry(addr.getOffset())) {
error("Attempt to use non-list %04x:%04x as list", PRINT_REG(addr));
return NULL;
}
- return &(lt->_table[addr.getOffset()]);
+ return &(lt[addr.getOffset()]);
}
Node *SegManager::lookupNode(reg_t addr, bool stopOnDiscarded) {
@@ -507,9 +507,9 @@ Node *SegManager::lookupNode(reg_t addr, bool stopOnDiscarded) {
return NULL;
}
- NodeTable *nt = (NodeTable *)_heap[addr.getSegment()];
+ NodeTable &nt = *(NodeTable *)_heap[addr.getSegment()];
- if (!nt->isValidEntry(addr.getOffset())) {
+ if (!nt.isValidEntry(addr.getOffset())) {
if (!stopOnDiscarded)
return NULL;
@@ -517,7 +517,7 @@ Node *SegManager::lookupNode(reg_t addr, bool stopOnDiscarded) {
return NULL;
}
- return &(nt->_table[addr.getOffset()]);
+ return &(nt[addr.getOffset()]);
}
SegmentRef SegManager::dereference(reg_t pointer) {
@@ -873,32 +873,32 @@ SciArray<reg_t> *SegManager::allocateArray(reg_t *addr) {
offset = table->allocEntry();
*addr = make_reg(_arraysSegId, offset);
- return &(table->_table[offset]);
+ return &table->at(offset);
}
SciArray<reg_t> *SegManager::lookupArray(reg_t addr) {
if (_heap[addr.getSegment()]->getType() != SEG_TYPE_ARRAY)
error("Attempt to use non-array %04x:%04x as array", PRINT_REG(addr));
- ArrayTable *arrayTable = (ArrayTable *)_heap[addr.getSegment()];
+ ArrayTable &arrayTable = *(ArrayTable *)_heap[addr.getSegment()];
- if (!arrayTable->isValidEntry(addr.getOffset()))
+ if (!arrayTable.isValidEntry(addr.getOffset()))
error("Attempt to use non-array %04x:%04x as array", PRINT_REG(addr));
- return &(arrayTable->_table[addr.getOffset()]);
+ return &(arrayTable[addr.getOffset()]);
}
void SegManager::freeArray(reg_t addr) {
if (_heap[addr.getSegment()]->getType() != SEG_TYPE_ARRAY)
error("Attempt to use non-array %04x:%04x as array", PRINT_REG(addr));
- ArrayTable *arrayTable = (ArrayTable *)_heap[addr.getSegment()];
+ ArrayTable &arrayTable = *(ArrayTable *)_heap[addr.getSegment()];
- if (!arrayTable->isValidEntry(addr.getOffset()))
+ if (!arrayTable.isValidEntry(addr.getOffset()))
error("Attempt to use non-array %04x:%04x as array", PRINT_REG(addr));
- arrayTable->_table[addr.getOffset()].destroy();
- arrayTable->freeEntry(addr.getOffset());
+ arrayTable[addr.getOffset()].destroy();
+ arrayTable.freeEntry(addr.getOffset());
}
SciString *SegManager::allocateString(reg_t *addr) {
@@ -913,32 +913,32 @@ SciString *SegManager::allocateString(reg_t *addr) {
offset = table->allocEntry();
*addr = make_reg(_stringSegId, offset);
- return &(table->_table[offset]);
+ return &table->at(offset);
}
SciString *SegManager::lookupString(reg_t addr) {
if (_heap[addr.getSegment()]->getType() != SEG_TYPE_STRING)
error("lookupString: Attempt to use non-string %04x:%04x as string", PRINT_REG(addr));
- StringTable *stringTable = (StringTable *)_heap[addr.getSegment()];
+ StringTable &stringTable = *(StringTable *)_heap[addr.getSegment()];
- if (!stringTable->isValidEntry(addr.getOffset()))
+ if (!stringTable.isValidEntry(addr.getOffset()))
error("lookupString: Attempt to use non-string %04x:%04x as string", PRINT_REG(addr));
- return &(stringTable->_table[addr.getOffset()]);
+ return &(stringTable[addr.getOffset()]);
}
void SegManager::freeString(reg_t addr) {
if (_heap[addr.getSegment()]->getType() != SEG_TYPE_STRING)
error("freeString: Attempt to use non-string %04x:%04x as string", PRINT_REG(addr));
- StringTable *stringTable = (StringTable *)_heap[addr.getSegment()];
+ StringTable &stringTable = *(StringTable *)_heap[addr.getSegment()];
- if (!stringTable->isValidEntry(addr.getOffset()))
+ if (!stringTable.isValidEntry(addr.getOffset()))
error("freeString: Attempt to use non-string %04x:%04x as string", PRINT_REG(addr));
- stringTable->_table[addr.getOffset()].destroy();
- stringTable->freeEntry(addr.getOffset());
+ stringTable[addr.getOffset()].destroy();
+ stringTable.freeEntry(addr.getOffset());
}
#endif
diff --git a/engines/sci/engine/segment.cpp b/engines/sci/engine/segment.cpp
index bb90698e6a..2cff799f4b 100644
--- a/engines/sci/engine/segment.cpp
+++ b/engines/sci/engine/segment.cpp
@@ -97,7 +97,7 @@ Common::Array<reg_t> CloneTable::listAllOutgoingReferences(reg_t addr) const {
error("Unexpected request for outgoing references from clone at %04x:%04x", PRINT_REG(addr));
}
- const Clone *clone = &(_table[addr.getOffset()]);
+ const Clone *clone = &at(addr.getOffset());
// Emit all member variables (including references to the 'super' delegate)
for (uint i = 0; i < clone->getVarCount(); i++)
@@ -112,7 +112,7 @@ Common::Array<reg_t> CloneTable::listAllOutgoingReferences(reg_t addr) const {
void CloneTable::freeAtAddress(SegManager *segMan, reg_t addr) {
#ifdef GC_DEBUG
- Object *victim_obj = &(_table[addr.getOffset()]);
+ Object *victim_obj = &at(addr.getOffset());
if (!(victim_obj->_flags & OBJECT_FLAG_FREED))
warning("[GC] Clone %04x:%04x not reachable and not freed (freeing now)", PRINT_REG(addr));
@@ -208,7 +208,7 @@ Common::Array<reg_t> ListTable::listAllOutgoingReferences(reg_t addr) const {
error("Invalid list referenced for outgoing references: %04x:%04x", PRINT_REG(addr));
}
- const List *list = &(_table[addr.getOffset()]);
+ const List *list = &at(addr.getOffset());
tmp.push_back(list->first);
tmp.push_back(list->last);
@@ -225,7 +225,7 @@ Common::Array<reg_t> NodeTable::listAllOutgoingReferences(reg_t addr) const {
if (!isValidEntry(addr.getOffset())) {
error("Invalid node referenced for outgoing references: %04x:%04x", PRINT_REG(addr));
}
- const Node *node = &(_table[addr.getOffset()]);
+ const Node *node = &at(addr.getOffset());
// We need all four here. Can't just stick with 'pred' OR 'succ' because node operations allow us
// to walk around from any given node
@@ -252,13 +252,13 @@ SegmentRef DynMem::dereference(reg_t pointer) {
SegmentRef ArrayTable::dereference(reg_t pointer) {
SegmentRef ret;
ret.isRaw = false;
- ret.maxSize = _table[pointer.getOffset()].getSize() * 2;
- ret.reg = _table[pointer.getOffset()].getRawData();
+ ret.maxSize = at(pointer.getOffset()).getSize() * 2;
+ ret.reg = at(pointer.getOffset()).getRawData();
return ret;
}
void ArrayTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) {
- _table[sub_addr.getOffset()].destroy();
+ at(sub_addr.getOffset()).destroy();
freeEntry(sub_addr.getOffset());
}
@@ -268,7 +268,7 @@ Common::Array<reg_t> ArrayTable::listAllOutgoingReferences(reg_t addr) const {
error("Invalid array referenced for outgoing references: %04x:%04x", PRINT_REG(addr));
}
- const SciArray<reg_t> *array = &(_table[addr.getOffset()]);
+ const SciArray<reg_t> *array = &at(addr.getOffset());
for (uint32 i = 0; i < array->getSize(); i++) {
reg_t value = array->getValue(i);
@@ -305,8 +305,8 @@ void SciString::fromString(const Common::String &string) {
SegmentRef StringTable::dereference(reg_t pointer) {
SegmentRef ret;
ret.isRaw = true;
- ret.maxSize = _table[pointer.getOffset()].getSize();
- ret.raw = (byte *)_table[pointer.getOffset()].getRawData();
+ ret.maxSize = at(pointer.getOffset()).getSize();
+ ret.raw = (byte *)at(pointer.getOffset()).getRawData();
return ret;
}
diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h
index 2699bc2e5b..50c77d0538 100644
--- a/engines/sci/engine/segment.h
+++ b/engines/sci/engine/segment.h
@@ -210,16 +210,17 @@ struct Hunk {
template<typename T>
struct SegmentObjTable : public SegmentObj {
typedef T value_type;
- struct Entry : public T {
+ struct Entry {
+ T data;
int next_free; /* Only used for free entries */
};
enum { HEAPENTRY_INVALID = -1 };
-
int first_free; /**< Beginning of a singly linked list for entries */
int entries_used; /**< Statistical information */
- Common::Array<Entry> _table;
+ typedef Common::Array<Entry> ArrayType;
+ ArrayType _table;
public:
SegmentObjTable(SegmentType type) : SegmentObj(type) {
@@ -272,6 +273,14 @@ public:
tmp.push_back(make_reg(segId, i));
return tmp;
}
+
+ uint size() const { return _table.size(); }
+
+ T &at(uint index) { return _table[index].data; }
+ const T &at(uint index) const { return _table[index].data; }
+
+ T &operator[](uint index) { return at(index); }
+ const T &operator[](uint index) const { return at(index); }
};
@@ -323,8 +332,8 @@ struct HunkTable : public SegmentObjTable<Hunk> {
}
void freeEntryContents(int idx) {
- free(_table[idx].mem);
- _table[idx].mem = 0;
+ free(at(idx).mem);
+ at(idx).mem = 0;
}
virtual void freeEntry(int idx) {
@@ -502,7 +511,7 @@ struct StringTable : public SegmentObjTable<SciString> {
StringTable() : SegmentObjTable<SciString>(SEG_TYPE_STRING) {}
virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr) {
- _table[sub_addr.getOffset()].destroy();
+ at(sub_addr.getOffset()).destroy();
freeEntry(sub_addr.getOffset());
}
diff --git a/engines/scumm/POTFILES b/engines/scumm/POTFILES
index 246f14d3f0..039aa16755 100644
--- a/engines/scumm/POTFILES
+++ b/engines/scumm/POTFILES
@@ -1,3 +1,4 @@
+engines/scumm/detection.cpp
engines/scumm/dialogs.cpp
engines/scumm/help.cpp
engines/scumm/input.cpp
diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp
index 9264a6443b..0867b20fc3 100644
--- a/engines/scumm/detection.cpp
+++ b/engines/scumm/detection.cpp
@@ -29,6 +29,7 @@
#include "common/md5.h"
#include "common/savefile.h"
#include "common/system.h"
+#include "common/translation.h"
#include "audio/mididrv.h"
@@ -957,6 +958,7 @@ public:
virtual int getMaximumSaveSlot() const;
virtual void removeSaveState(const char *target, int slot) const;
virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
+ virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
};
bool ScummMetaEngine::hasFeature(MetaEngineFeature f) const {
@@ -1329,6 +1331,21 @@ SaveStateDescriptor ScummMetaEngine::querySaveMetaInfos(const char *target, int
return desc;
}
+static const ExtraGuiOption comiObjectLabelsOption = {
+ _s("Show Object Line"),
+ _s("Show the names of objects at the bottom of the screen"),
+ "object_labels",
+ true
+};
+
+const ExtraGuiOptions ScummMetaEngine::getExtraGuiOptions(const Common::String &target) const {
+ ExtraGuiOptions options;
+ if (target.empty() || ConfMan.get("gameid", target) == "comi") {
+ options.push_back(comiObjectLabelsOption);
+ }
+ return options;
+}
+
#if PLUGIN_ENABLED_DYNAMIC(SCUMM)
REGISTER_PLUGIN_DYNAMIC(SCUMM, PLUGIN_TYPE_ENGINE, ScummMetaEngine);
#else
diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp
index 21c7428621..ba9cb2a277 100644
--- a/engines/scumm/dialogs.cpp
+++ b/engines/scumm/dialogs.cpp
@@ -52,7 +52,7 @@
#include "scumm/help.h"
#endif
-#ifdef SMALL_SCREEN_DEVICE
+#ifdef GUI_ENABLE_KEYSDIALOG
#include "gui/KeysDialog.h"
#endif
diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h
index 41c59cb521..e986ae4b47 100644
--- a/engines/scumm/scumm-md5.h
+++ b/engines/scumm/scumm-md5.h
@@ -1,5 +1,5 @@
/*
- This file was generated by the md5table tool on Tue Jan 12 23:47:54 2016
+ This file was generated by the md5table tool on Mon Mar 28 09:52:54 2016
DO NOT EDIT MANUALLY!
*/
@@ -189,6 +189,7 @@ static const MD5Table md5table[] = {
{ "3b832f4a90740bf22e9b8ed42ca0128c", "freddi4", "HE 99", "", -1, Common::EN_GRB, Common::kPlatformUnknown },
{ "3c4c471342bd95505a42334367d8f127", "puttmoon", "HE 70", "", 12161, Common::RU_RUS, Common::kPlatformWindows },
{ "3cce1913a3bc586b51a75c3892ff18dd", "indy3", "VGA", "VGA", -1, Common::RU_RUS, Common::kPlatformDOS },
+ { "3cf4b6ff78f735b671d8ccc2bc110b15", "maniac", "V2", "V2", -1, Common::ES_ESP, Common::kPlatformAmiga },
{ "3d219e7546039543307b55a91282bf18", "funpack", "", "", -1, Common::EN_ANY, Common::kPlatformDOS },
{ "3de99ef0523f8ca7958faa3afccd035a", "spyfox", "HE 100", "Updated", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "3df6ead57930488bc61e6e41901d0e97", "fbear", "HE 62", "", -1, Common::EN_ANY, Common::kPlatformMacintosh },
diff --git a/engines/sherlock/scalpel/scalpel_journal.cpp b/engines/sherlock/scalpel/scalpel_journal.cpp
index d6f8021e5b..151d986d81 100644
--- a/engines/sherlock/scalpel/scalpel_journal.cpp
+++ b/engines/sherlock/scalpel/scalpel_journal.cpp
@@ -485,11 +485,6 @@ int ScalpelJournal::getSearchString(bool printError) {
screen.makeField(Common::Rect(12, 185, 307, 196));
- screen.fillRect(Common::Rect(12, 185, 307, 186), BUTTON_BOTTOM);
- screen.vLine(12, 185, 195, BUTTON_BOTTOM);
- screen.hLine(13, 195, 306, BUTTON_TOP);
- screen.hLine(306, 186, 195, BUTTON_TOP);
-
if (printError) {
screen.gPrint(Common::Point((SHERLOCK_SCREEN_WIDTH - screen.stringWidth(_fixedTextSearchNotFound)) / 2, 185),
INV_FOREGROUND, "%s", _fixedTextSearchNotFound.c_str());
diff --git a/engines/sherlock/talk.cpp b/engines/sherlock/talk.cpp
index b543472513..3c6bf44837 100644
--- a/engines/sherlock/talk.cpp
+++ b/engines/sherlock/talk.cpp
@@ -28,9 +28,11 @@
#include "sherlock/scalpel/scalpel_talk.h"
#include "sherlock/scalpel/scalpel_user_interface.h"
#include "sherlock/tattoo/tattoo.h"
+#include "sherlock/tattoo/tattoo_fixed_text.h"
#include "sherlock/tattoo/tattoo_people.h"
#include "sherlock/tattoo/tattoo_scene.h"
#include "sherlock/tattoo/tattoo_talk.h"
+#include "sherlock/tattoo/tattoo_user_interface.h"
namespace Sherlock {
@@ -306,8 +308,14 @@ void Talk::talkTo(const Common::String filename) {
if (_scriptMoreFlag && _scriptSelect != 100)
select = _scriptSelect;
- if (select == -1)
+ if (select == -1) {
+ if (IS_ROSE_TATTOO) {
+ static_cast<Tattoo::TattooUserInterface *>(&ui)->putMessage(
+ "%s", _vm->_fixedText->getText(Tattoo::kFixedText_NoEffect));
+ return;
+ }
error("Couldn't find statement to display");
+ }
// Add the statement into the journal and talk history
if (_talkTo != -1 && !_talkHistory[_converseNum][select])
diff --git a/engines/sherlock/tattoo/tattoo_journal.cpp b/engines/sherlock/tattoo/tattoo_journal.cpp
index 918887f320..8e1a61d36e 100644
--- a/engines/sherlock/tattoo/tattoo_journal.cpp
+++ b/engines/sherlock/tattoo/tattoo_journal.cpp
@@ -523,15 +523,15 @@ void TattooJournal::drawControls(int mode) {
if (mode != 2) {
// Draw the Bars separating the Journal Commands
- int xp = r.right / 3;
+ int xp = r.left + r.width() / 3;
for (int idx = 0; idx < 2; ++idx) {
screen._backBuffer1.SHtransBlitFrom(images[6], Common::Point(xp - 2, r.top + 1));
screen._backBuffer1.SHtransBlitFrom(images[7], Common::Point(xp - 2, yp - 1));
- screen._backBuffer1.hLine(xp - 1, r.top + 4, yp - 2, INFO_TOP);
- screen._backBuffer1.hLine(xp, r.top + 4, yp - 2, INFO_MIDDLE);
- screen._backBuffer1.hLine(xp + 1, r.top + 4, yp - 2, INFO_BOTTOM);
- xp = r.right / 3 * 2;
+ screen._backBuffer1.vLine(xp - 1, r.top + 4, yp - 2, INFO_TOP);
+ screen._backBuffer1.vLine(xp, r.top + 4, yp - 2, INFO_MIDDLE);
+ screen._backBuffer1.vLine(xp + 1, r.top + 4, yp - 2, INFO_BOTTOM);
+ xp += r.width() / 3;
}
}
diff --git a/engines/sherlock/tattoo/widget_inventory.cpp b/engines/sherlock/tattoo/widget_inventory.cpp
index 34331f0eae..9f126cf7a7 100644
--- a/engines/sherlock/tattoo/widget_inventory.cpp
+++ b/engines/sherlock/tattoo/widget_inventory.cpp
@@ -546,7 +546,7 @@ void WidgetInventory::drawBars() {
_surface.SHtransBlitFrom(images[7], Common::Point(x - 1, INVENTORY_YSIZE + 2));
}
- _surface.hLine(x + 2, INVENTORY_YSIZE + 2, INVENTORY_YSIZE + 8, INFO_BOTTOM);
+ _surface.vLine(x + 2, INVENTORY_YSIZE + 2, INVENTORY_YSIZE + 8, INFO_BOTTOM);
}
void WidgetInventory::drawInventory() {
diff --git a/engines/wage/detection.cpp b/engines/wage/detection.cpp
index 512d432e54..1b418b5aa8 100644
--- a/engines/wage/detection.cpp
+++ b/engines/wage/detection.cpp
@@ -54,6 +54,7 @@ static const PlainGameDescriptor wageGames[] = {
class WageMetaEngine : public AdvancedMetaEngine {
public:
WageMetaEngine() : AdvancedMetaEngine(Wage::gameDescriptions, sizeof(ADGameDescription), wageGames) {
+ _md5Bytes = 50000;
_singleId = "wage";
_guiOptions = GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI);
}
diff --git a/engines/wage/detection_tables.h b/engines/wage/detection_tables.h
index 1a177c2bb0..096338659b 100644
--- a/engines/wage/detection_tables.h
+++ b/engines/wage/detection_tables.h
@@ -32,108 +32,148 @@ namespace Wage {
#define BIGGAME(t,v,f,m,s) { t,v,AD_ENTRY1s(f,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_DEFAULT,GUIO0()}
static const ADGameDescription gameDescriptions[] = {
- FANGAME("3rd Floor", "913812a1ac7a6b0e48dadd1afa1c7763", 281409),
- BIGGAME("afm", "v1.8", "Another Fine Mess 1.8", "94a9c4f8b3dabd1846d76215a49bd221", 1420723),
- BIGGAME("amot", "v1.8", "A Mess O' Trouble 1.8", "26207bdf0bb539464f136f0669af885f", 1843104),
+ FANGAME("3rd Floor", "3ed49d2163e46d2c9b33fd80927d9e22", 281409),
+ FANGAME("3rd Floor", "3ed49d2163e46d2c9b33fd80927d9e22", 281423), // alt version
+ BIGGAME("afm", "v1.8", "Another Fine Mess 1.8", "abc7188469a9a7083fd4caec55a4f76e", 1420723),
+ BIGGAME("amot", "v1.8", "A Mess O' Trouble 1.8", "6b59e5bb9a4b74ecdd9f66d4e36a59cf", 1843104),
// No Next on the first screen?
- FANGAME("Brownie's Dream", "94a9c4f8b3dabd1846d76215a49bd221", 440704),
- FANGAMEN("Brownie's Time Travels", "Brownie's Time Travels v1.2", "94a9c4f8b3dabd1846d76215a49bd221", 471589),
- FANGAME("Bug Hunt", "595117cbed33e8de1ab3714b33880205", 195699),
- BIGGAME("cantitoe", "", "Camp Cantitoe", "913812a1ac7a6b0e48dadd1afa1c7763", 616985),
+ FANGAME("Brownie's Dream", "6fdcce532bcd50b7e4f3f6bab50a0ee6", 440704),
+ FANGAMEN("Brownie's Time Travels", "Brownie's Time Travels v1.2", "55842a100b56e236c5ad69563e01fc24", 471589),
+ FANGAME("Bug Hunt", "738e2e8a1020be48c5ef42da571674ae", 195699),
+ FANGAME("Bug Hunt", "118a41121143488719d28daa9af8cd39", 195779), // alt version
+ BIGGAME("cantitoe", "", "Camp Cantitoe", "1780c41d14b876461a19dbeceebf2a37", 616985),
// Problems with letter rendering
- FANGAME("Canal District", "a56aa3cd4a6e070e15ce1d5815c7be0a", 641470),
- FANGAME("Carbon Copy", "913812a1ac7a6b0e48dadd1afa1c7763", 519445),
+ FANGAME("Canal District", "34e7a8e84b33ba8ea38b4ffd76ef074f", 641470),
+ FANGAME("Carbon Copy", "9e781acd63290ae390d515cffc742011", 519445),
// Invalid rect in scene "FINALE"
- FANGAME("Castle of Ert", "327610eb2298a9427a566288312df040", 198955),
- FANGAME("Deep Angst", "b130b3c811cd89024dd5fdd2b71f70b8", 329550),
- FANGAME("Deep Ennui", "913812a1ac7a6b0e48dadd1afa1c7763", 86075),
+ FANGAME("Castle of Ert", "a45b439bb3a9c8a4a14b996024222068", 198955),
+ FANGAMEN("Castle of Ert", "Castle of Ert.1", "a45b439bb3a9c8a4a14b996024222068", 198983), // alt version
+ FANGAMEND("Death Mall", "Death Mall Demo", "1c78fc15fb037b242a0bc6bac7d4d889", 254874),
+ FANGAME("Deep Angst", "7f8821f7b279269a91f9aadfed98eec0", 329550), // Original gile name "Deep Angst™"
+ FANGAME("Deep Ennui", "7fa4368834a22a9d4b7246a6297b455f", 86075),
// Polygons with ignored byte 1
- FANGAME("Double Trouble", "1652e36857a04c01dc560234c4818619", 542371),
- BIGGAME("drakmythcastle", "disk I", "Drakmyth Castle disk I of II", "94a9c4f8b3dabd1846d76215a49bd221", 793784),
- BIGGAME("drakmythcastle", "disk II", "Drakmyth Castle II", "cc978cc9a5256724702463cb5aaaffa0", 1685659),
+ FANGAME("Double Trouble", "3f0c032377d87704267283380800633a", 542371),
+ BIGGAME("drakmythcastle", "disk I", "Drakmyth Castle disk I of II", "5b1fd760fbc081c608acebfe1d07a58a", 793784),
+ BIGGAME("drakmythcastle", "disk II", "Drakmyth Castle II", "1116f9c2c781f79e1f9c868b51ae7fa5", 1685659),
// Crash at start in GUI rendering
- FANGAME("Dune Eternity", "94a9c4f8b3dabd1846d76215a49bd221", 290201), // Original file name is "***DUNE ETERNITY*** "
- FANGAMEN("Dungeon World II", "DungeonWorld2", "0154ea11d3cbb536c13b4ae9e6902d48", 230199),
- FANGAME("Edg's World", "913812a1ac7a6b0e48dadd1afa1c7763", 106769),
- FANGAME("Eidisi I", "595117cbed33e8de1ab3714b33880205", 172552),
+ FANGAME("Dune Eternity", "6b29f82e235815ffc4c9f30dc09968dd", 290201), // Original file name is "***DUNE ETERNITY*** "
+ FANGAMEN("Dungeon World II", "DungeonWorld2", "753df07166ca48e303d782cc72dd4053", 230199),
+ // Made for bigger resolution
+ FANGAME("Dynasty of Dar", "b2e9a5cca28acb85617b1477a5fca3e2", 275693),
+ FANGAME("Edg's World", "0a3a3aaa36088c514b668f1f62120006", 106769),
+ FANGAME("Eidisi I", "3d778c0fe7addf5f29e7593ba0fd3953", 172552),
+ FANGAME("Eidisi I", "8c2fb325a49344568c5536bba36a2556", 172566), // alt version
// Problems(?) with text on the first screen
- FANGAMEN("Enchanted Pencils", "Enchanted Pencils 0.99 (PG)", "595117cbed33e8de1ab3714b33880205", 408913),
- FANGAME("Escape from School!", "913812a1ac7a6b0e48dadd1afa1c7763", 50105),
- FANGAME("Everyman 1", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 335705),
- FANGAME("Exploration Zeta!", "c477921aeee6ed0f8997ba44447eb2d0", 366599),
+ FANGAMEN("Enchanted Pencils", "Enchanted Pencils 0.99 (PG)", "9a9777a83e58bebfa6f1662d5e236384", 408913),
+ FANGAME("Escape!", "3ada261c2d1d9ce6b9da068237472689", 65075), // Original file name "Escape!†"
+ FANGAME("Escape from School!", "2055747bb874052333190eb993246a7f", 50105),
+ FANGAME("Escape from School!", "fcc581e52d1fc8ea4603d7c953fa935a", 50119), // Original file name "Escape from School!†"
+ FANGAME("Everyman 1", "e20cebf0091a1b1bf023aac6f28c9011", 335705),
+ FANGAME("Exploration Zeta!", "6127d9c04ad68f0cbb5f6aa1d95b48a2", 366599),
+ // Cannot proceed past the first scene
+ FANGAMEND("Explorer", "Explorer DEMO", "a9ebdecf6c8de95a03e593d877dacc13", 461228),
// Crash in console rendering on the first scene
- FANGAME("Fantasy Quest", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 762754),
- FANGAME("Find the Heart", "595117cbed33e8de1ab3714b33880205", 106235), // From Joshua's Worlds 1.0
- // Problems with window overlay
- FANGAMEN("Jumble", "LSJUMBLE", "e12ec4d76d48bdc86567c5e63750547e", 647339), // Original file name is "LSJUMBLE† "
- FANGAME("Karth of the Jungle", "595117cbed33e8de1ab3714b33880205", 96711),
- FANGAME("Karth of the Jungle", "595117cbed33e8de1ab3714b33880205", 96960), // Alternative version
- FANGAME("Karth of the Jungle II", "c106835ab4436de054e03aec3ce904ce", 201053),
- FANGAMEN("Little Pythagoras", "Little Pythagoras 1.1.1", "94a9c4f8b3dabd1846d76215a49bd221", 628821),
- FANGAME("Lost Crystal", "8174c81ea1858d0079ae040dae2cefd3", 771072),
+ FANGAME("Fantasy Quest", "b42b0e86e2c84464283640c74b25e015", 762754),
+ FANGAME("Find the Heart", "aa244c15f2ba8cef468714be34223acd", 106235), // From Joshua's Worlds 1.0
+ FANGAME("Find the Heart", "a6834cb230cea1953f5bf1f8f7aacabd", 105885), // From Joshua's Worlds. Alt version
+ FANGAME("Find the Heart", "a6834cb230cea1953f5bf1f8f7aacabd", 105871), // Standalone
+ FANGAMEN("Fortune Teller", "Fortune Teller 1.1", "7d2628eeea67b33379e01c0aef8dd196", 73931),
+ FANGAMEN("Haunted House", "Haunted House 1.5", "5db2f95c7abaa9d060b94271a5bc57f8", 177500),
+ // Cropped graphics on first scene
+ FANGAME("Intro to Gothic", "6f732eaad6e3b85795f8ee6c6a40d837", 208067),
+ // No Next button in intro
+ FANGAME("Jamie the Demon Slayer", "fa0ca9618c18425b6d9bf913f762d91b", 232789),
+ FANGAMEN("Journey", "The Journey 1.6.2 US", "e66f37472e1414a088eb5d5acc4df794", 820572),
+ FANGAMEN("Jumble", "LSJUMBLE", "7c46851d2f90c7da9efe40b1688869c2", 647339), // Original file name is "LSJUMBLE† "
+ FANGAME("Karth of the Jungle", "5f2346834821dc3c4008e139cd37b3cb", 96711),
+ FANGAME("Karth of the Jungle", "444f9426f342135fbcc32180e5ba5b1c", 96960), // Alternative version
+ FANGAME("Karth of the Jungle II", "32161b27de894fd9e3f054afc4013f34", 201053),
+ FANGAMEN("Little Pythagoras", "Little Pythagoras 1.1.1", "75906fa955de695ac3e8164e7d88ac7b", 628821),
+ FANGAME("Lost Crystal", "d5e27a83f2884a24c6ec26c6cb776fe9", 771072),
// Crash in design drawing on startup
- FANGAMEN("Lost In Kookyville", "Lost In Kookyville 1.2.4", "e6cea2234cee9d0dba7be10bc1ad6055", 721569),
- FANGAME("Magic Rings", "913812a1ac7a6b0e48dadd1afa1c7763", 109044),
+ FANGAMEN("Lost In Kookyville", "Lost In Kookyville 1.2.4", "5ab6259706b33230dbfba05618c2c5c9", 721569),
+ FANGAME("Magic Rings", "450e986694b96f3b9e6cc64e57b753dc", 109044),
// No way to click on the house
- FANGAME("Messy House", "913812a1ac7a6b0e48dadd1afa1c7763", 177120),
- FANGAME("Midnight Snack", "913812a1ac7a6b0e48dadd1afa1c7763", 67952),
- FANGAME("Midnight Snack", "913812a1ac7a6b0e48dadd1afa1c7763", 67966), // Alt version
- FANGAME("Minitorian", "913812a1ac7a6b0e48dadd1afa1c7763", 586464),
- FANGAME("M'Lord's Warrior", "7d30b6e68ecf197b2d15492630bdeb89", 465639), // Original file name is "M'Lord's Warrior †"
+ FANGAME("Messy House", "705df61da9e7d742b7ad678e59eb7bfb", 177120),
+ FANGAME("Midnight Snack", "76986389f9a08dd95450c8b9cf408653", 67952),
+ FANGAME("Midnight Snack", "76986389f9a08dd95450c8b9cf408653", 67966), // Alt version
+ FANGAME("Mike's House", "3d23c2b88cefd958bcbc4d4c711003d8", 87357),
+ FANGAME("Minitorian", "15fbb2bd75d83155ed21edbc5dc9558f", 586464),
+ FANGAME("M'Lord's Warrior", "0bebb2c62529c89590f6c5be6e1e9838", 465639), // Original file name is "M'Lord's Warrior †"
// Unhandled comparison case
- FANGAME("Mountain of Mayhem", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 750003), // Original file name "Mountain of Mayhem †"
+ FANGAME("Mountain of Mayhem", "4088fc534042081b7ab7b49675ab4a6e", 750003), // Original file name "Mountain of Mayhem †"
// No way to pass through the first screen
- FANGAME("Nightcrawler Ned", "94a9c4f8b3dabd1846d76215a49bd221", 366542),
+ FANGAME("Nightcrawler Ned", "0cf27bf82de299c69405f7910146bf00", 366542),
// Crash on startup
- FANGAMEN("Parrot Talk", "PARROT TALK V1", "d81f2d03a1e863f04fb1e3a5495b720e", 118936),
+ FANGAMEN("Parrot Talk", "PARROT TALK V1", "b1570b0779891d5d50a3cf0146d28202", 118936),
// Crash on startup
- FANGAMEN("Parrot Talk", "PARROT TALKV2", "d81f2d03a1e863f04fb1e3a5495b720e", 118884),
- FANGAME("Pavilion", "4d991d7d1534d48d90598d86ea6d5d97", 231687),
- FANGAMEN("Pencils", "Pencils.99", "913812a1ac7a6b0e48dadd1afa1c7763", 408551),
+ FANGAMEN("Parrot Talk", "PARROT TALKV2", "0c1e920ed3ff74b8f22eaaf0d3496d5a", 118884),
+ FANGAME("Pavilion", "3a33149569325a44d98544452323c819", 231687),
+ FANGAMEN("Pencils", "Pencils.99", "9c200938488565080e12989e784586e2", 408551),
// Polygons with byte 1
- FANGAME("Periapt", "913812a1ac7a6b0e48dadd1afa1c7763", 406006),
- FANGAME("Puzzle Piece Search", "595117cbed33e8de1ab3714b33880205", 247693), // From Joshua's Worlds 1.0
+ FANGAME("Periapt", "fb4052819126b88d7e03ebc00c669a9d", 406006),
+ FANGAME("Psychotic!", "6b4ae6261b405e2feac58c5a2ddb67c5", 247693),
+ FANGAME("Puzzle Piece Search", "6b4ae6261b405e2feac58c5a2ddb67c5", 247693), // From Joshua's Worlds 1.0
+ FANGAME("The Puzzle Piece Search", "fb99797c429c18ec68418fdd12af17a1", 247338), // From Joshua's Worlds
+ FANGAME("The Puzzle Piece Search", "fb99797c429c18ec68418fdd12af17a1", 247324), // Stnadalone
// Empty(?) first scene
- FANGAME("Pyramid of No Return", "77a55a45f794b4d4a56703d3acce871e", 385145),
- FANGAME("Queen Quest", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 57026),
- FANGAME("Quest for T-Rex", "913812a1ac7a6b0e48dadd1afa1c7763", 592584),
+ FANGAME("Pyramid of No Return", "38383ac85cc16703f13f8d82f1398123", 385145),
+ // Cropped graphics at the first scene
+ FANGAME("Psychotic!", "29b30e6aae9cc6db5eccb09f695ff25e", 367309),
+ FANGAME("P-W Adventure", "9bf86fb946683500d23887ef185726ab", 219216),
+ FANGAMEN("Pyramid of Ert", "Pyramid of Ert V1.2", "fb931cd35440a66864a434c773b496da", 315783),
+ FANGAME("Queen Quest", "8273e29afe64a984eb0ce7b43fdf3a59", 57039), // alt version
+ FANGAME("Quest for T-Rex", "f16f2cd525c9aeb4733295d8d842b902", 592584),
// Crash in console rendering on the initial scene
- FANGAME("Quest for the Dark Sword", "b35dd0c078da9f35fc25a455f56bb129", 572576),
- FANGAME("Radical Castle", "677bfee4afeca2f7152eb8b76c85ca8d", 355601),
- FANGAME("Radical Castle 1.0", "677bfee4afeca2f7152eb8b76c85ca8d", 347278),
- BIGGAME("raysmaze", "v1.5", "Ray's Maze1.5", "064b16d8c20724f8debbbdc3aafde538", 1408516),
- BIGGAME("raysmaze", "v1.5/alt", "Ray's Maze1.5", "92cca777800c3d31a77b5ed7f6ee49ad", 1408516),
+ FANGAME("Quest for the Dark Sword", "4815d9a770904b26c463b7e4fcd121c7", 572576),
+ FANGAME("Radical Castle", "09b70763c7a48a76240bd0e42737caaa", 355601),
+ FANGAME("Radical Castle 1.0", "8ae2e29ffeca52a5c7fae66dec4764a3", 347278),
+ BIGGAME("raysmaze", "v1.5", "Ray's Maze1.5", "521583e59bdc1d611f963cef1dc25869", 1408516),
+ BIGGAME("raysmaze", "v1.5/alt", "Ray's Maze1.5", "120e65bec953b981b2e0aed45ad45d70", 1408516),
+ // Next button is not visible
+ FANGAME("Ray's World Builder Demo", "d252ee8e38c9abc50455d071a367d031", 116056),
// Unhandled comparison case
- FANGAME("Sands of Time", "913812a1ac7a6b0e48dadd1afa1c7763", 122672), // Original file name "Sands of Time†"
- BIGGAME("scepters", "", "Scepters", "3311deef8bf82f0b4b1cfa15a3b3289d", 346595),
+ FANGAME("Sands of Time", "b00ea866cb04cd87124e5720bc2c84c7", 122672), // Original file name "Sands of Time†"
+ BIGGAME("scepters", "", "Scepters", "f8db17cd96be056cf8a8bb9cfe46cf3a", 346595),
+ BIGGAME("scepters", "", "Scepters", "1fd7ca93ef16f4752fb46ee9cfa0949a", 347540), // alt version
+ FANGAME("Schmoozer", "e0f416bae626e2c638055b7f495d8c78", 221500),
// ??? problems with dog bitmap?
- FANGAMEN("Space Adventure", "SpaceAdventure", "f9f3f1c419f56955f7966355b34ea5c8", 155356),
- FANGAMEN("Spear of Destiny", "SpearOfDestiny", "913812a1ac7a6b0e48dadd1afa1c7763", 333665), // Original file name "SpearOfDestiny†"
- FANGAME("Star Trek", "44aaef4806578700429de5aaf95c266e", 53320),
- FANGAME("Strange Disappearance", "d81f2d03a1e863f04fb1e3a5495b720e", 772282),
+ FANGAMEN("Space Adventure", "SpaceAdventure", "7b6c883b3510e21cfabf4c8caaeb1f16", 155356),
+ FANGAMEN("Space Adventure", "SpaceAdventure", "3bd6fc9327f35db5390a9bf86afcd872", 155356), // alt version
+ FANGAMEN("Spear of Destiny", "SpearOfDestiny", "f1252ff34dd279f4ec1844bb403a578c", 333665), // Original file name "SpearOfDestiny†"
+ FANGAME("Star Trek", "fe20d06bc50c7fcebda0db533e141d4a", 53320),
+ FANGAME("Strange Disappearance", "782fae517f7374cd7f43f428331ce445", 772282),
// Code 0x03 in text
- FANGAME("Swamp Witch", "913812a1ac7a6b0e48dadd1afa1c7763", 739781), // Original file name "Swamp Witch†"
- FANGAME("Sweetspace Now!", "e12ec4d76d48bdc86567c5e63750547e", 123813), // Comes with Jumble
+ FANGAME("Swamp Witch", "4f146c0a5c59e7d4717a0423271fa89d", 739781), // Original file name "Swamp Witch†"
+ FANGAME("Sweetspace Now!", "1d419bc0b04c51468ddc40a90125bf00", 123813), // Comes with Jumble
// Wrong scrolling in the first console text
- FANGAMEN("Sword of Siegfried", "Sword of Siegfried 1.0", "913812a1ac7a6b0e48dadd1afa1c7763", 234763),
- FANGAME("Time Bomb", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 64564),
- FANGAME("Time Bomb", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 64578), // Alt version
- FANGAMEND("The Ashland Revolution", "The Ashland Revolution Demo", "913812a1ac7a6b0e48dadd1afa1c7763", 145023), // Original file name "The Ashland Revolution Demo†"
- FANGAME("The Axe-orcist", "94a9c4f8b3dabd1846d76215a49bd221", 308764),
- FANGAMEN("The Hotel Caper", "The Hotel Caper V1.0", "595117cbed33e8de1ab3714b33880205", 231969),
+ FANGAMEN("Sword of Siegfried", "Sword of Siegfried 1.0", "1ee92830690f89ea142ac0847176a0c3", 234763),
+ FANGAME("Terrorist", "68208fa5e426312fb12402894add5e4a", 524469), // Original file name "Terrorist†"
+ FANGAME("Time Bomb", "b7a369d57d43ec8d9fd53832fd38d7db", 64564),
+ FANGAME("Time Bomb", "b7a369d57d43ec8d9fd53832fd38d7db", 64578), // Alt version
+ FANGAMEND("The Ashland Revolution", "The Ashland Revolution Demo", "3c7a1bdeab48a077a4f54fe69da61a9f", 145023), // Original file name "The Ashland Revolution Demo†"
+ FANGAME("The Axe-orcist", "bfdf6a4ce87e6b368977af3b683466db", 308764),
+ FANGAMEN("The Hotel Caper", "The Hotel Caper V1.0", "0d11a6ca1357e27ffff5231fe89cc429", 231969),
+ FANGAMEN("The Hotel Caper", "The Hotel Caper V1.0", "6c80fa6a36d16aa0edef86d8800c90db", 231969), // alt version
// Invalid rect in scene "Access Tube 1"
- FANGAMEN("The Phoenix v1.2", "The Phoenix", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 431640),
- FANGAME("The Sultan's Palace", "358799d446ee4fc12f793febd6c94b95", 456855),
+ FANGAMEN("The Phoenix v1.2", "The Phoenix", "0a4a01b83c993408ae824cc4f63957ea", 431640),
+ FANGAME("The Phoenix", "0a4a01b83c993408ae824cc4f63957ea", 431643),
+ FANGAME("The Sultan's Palace", "98c845323489344d7e2c9d1c3e53d1fc", 456855),
// Admission for on 3rd screen is messed up
- FANGAME("The Tower", "435f420b9dff895ae1ddf1338040c51d", 556539),
+ FANGAME("The Tower", "135fe861928d15b5acd8b355460c54bf", 556539),
// Polygons with ignored byte 1 and 2 on second scene
- FANGAME("The Village", "913812a1ac7a6b0e48dadd1afa1c7763", 314828),
+ FANGAME("The Village", "b9b5cfbfc7f482eae7587b55edc135ed", 314828),
+ FANGAME("The Wizard's Apprentice", "7eff3cb7d1a3f59639c62cf196039745", 782824),
+ // Messed up first scene
+ FANGAMEND("Tombworld", "Demo TombWorld", "f7c86166e29fb8b57f7a1400d4963a4e", 664252), // Original file name "Demo TombWorld©"
// Doesn't go past first scene
- BIGGAME("twisted", "", "Twisted! 1.6", "26207bdf0bb539464f136f0669af885f", 960954),
- FANGAME("Wishing Well", "913812a1ac7a6b0e48dadd1afa1c7763", 103688),
- FANGAME("Wizard's Warehouse", "913812a1ac7a6b0e48dadd1afa1c7763", 159748),
- FANGAME("ZikTuria", "418e74ca71029a1e9db80d0eb30c0843", 52972),
- FANGAME("Zoony", "539a64151426edc92da5eedadf39f23c", 154990), // original filename "Zoony™"
+ BIGGAME("twisted", "", "Twisted! 1.6", "97ab265eddf0cfed6d43d062c853cbc0", 960954),
+ FANGAME("Volcano II", "4dbb7ec6111c0f872da8ed8ba14763c9", 82991), // Original file name "Volcano II†"
+ FANGAME("Wishing Well", "ece06c419cbb2d32941e6b5c7d9d7c1a", 103688),
+ FANGAME("Wizard's Warehouse", "ee1b86841583e2b58ac39bf97017dc7b", 159748),
+ FANGAMEN("Wizard's Warehouse 2", "WizWarehouse 2.0", "6502bd974fe149fe76d6d5ae9d1e6878", 230870),
+ FANGAME("ZikTuria", "1b934fca68d633d231dccd2047d2d274", 52972),
+ FANGAME("Zoony", "7bb293b81117cbd974ce54fafa06f258", 154990), // original filename "Zoony™"
AD_TABLE_END_MARKER
};
diff --git a/engines/wage/gui.cpp b/engines/wage/gui.cpp
index 9dd1a24b3c..93c799e73a 100644
--- a/engines/wage/gui.cpp
+++ b/engines/wage/gui.cpp
@@ -50,6 +50,7 @@
#include "graphics/cursorman.h"
#include "graphics/fonts/bdf.h"
#include "graphics/palette.h"
+#include "graphics/primitives.h"
#include "wage/wage.h"
#include "wage/design.h"
@@ -237,39 +238,20 @@ void Gui::draw() {
return;
}
- if (_scene != _engine->_world->_player->_currentScene || _sceneDirty) {
- _scene = _engine->_world->_player->_currentScene;
-
- drawDesktop();
-
+ if (_scene != _engine->_world->_player->_currentScene)
_sceneDirty = true;
- _consoleDirty = true;
- _menuDirty = true;
- _consoleFullRedraw = true;
-
- _scene->paint(&_screen, _scene->_designBounds->left, _scene->_designBounds->top);
- _sceneArea.left = _scene->_designBounds->left + kBorderWidth - 2;
- _sceneArea.top = _scene->_designBounds->top + kBorderWidth - 2;
- _sceneArea.setWidth(_scene->_designBounds->width() - 2 * kBorderWidth);
- _sceneArea.setHeight(_scene->_designBounds->height() - 2 * kBorderWidth);
+ if (_sceneDirty || _bordersDirty)
+ drawDesktop();
- _consoleTextArea.left = _scene->_textBounds->left + kBorderWidth - 2;
- _consoleTextArea.top = _scene->_textBounds->top + kBorderWidth - 2;
- _consoleTextArea.setWidth(_scene->_textBounds->width() - 2 * kBorderWidth);
- _consoleTextArea.setHeight(_scene->_textBounds->height() - 2 * kBorderWidth);
+ if (_sceneIsActive) {
+ drawConsole();
+ drawScene();
+ } else {
+ drawScene();
+ drawConsole();
}
- if (_scene && (_bordersDirty || _sceneDirty))
- paintBorder(&_screen, _sceneArea, kWindowScene);
-
- // Render console
- if (_consoleDirty || _consoleFullRedraw)
- renderConsole(&_screen, _consoleTextArea);
-
- if (_bordersDirty || _consoleDirty || _consoleFullRedraw)
- paintBorder(&_screen, _consoleTextArea, kWindowConsole);
-
if (_menuDirty)
_menu->render();
@@ -287,6 +269,41 @@ void Gui::draw() {
_consoleFullRedraw = false;
}
+void Gui::drawScene() {
+ if (!_sceneDirty && !_bordersDirty)
+ return;
+
+ _scene = _engine->_world->_player->_currentScene;
+
+ _sceneDirty = true;
+ _consoleDirty = true;
+ _menuDirty = true;
+ _consoleFullRedraw = true;
+
+ _scene->paint(&_screen, _scene->_designBounds->left, _scene->_designBounds->top);
+
+ _sceneArea.left = _scene->_designBounds->left + kBorderWidth - 2;
+ _sceneArea.top = _scene->_designBounds->top + kBorderWidth - 2;
+ _sceneArea.setWidth(_scene->_designBounds->width() - 2 * kBorderWidth);
+ _sceneArea.setHeight(_scene->_designBounds->height() - 2 * kBorderWidth);
+
+ _consoleTextArea.left = _scene->_textBounds->left + kBorderWidth - 2;
+ _consoleTextArea.top = _scene->_textBounds->top + kBorderWidth - 2;
+ _consoleTextArea.setWidth(_scene->_textBounds->width() - 2 * kBorderWidth);
+ _consoleTextArea.setHeight(_scene->_textBounds->height() - 2 * kBorderWidth);
+
+ paintBorder(&_screen, _sceneArea, kWindowScene);
+}
+
+// Render console
+void Gui::drawConsole() {
+ if (!_consoleDirty && !_consoleFullRedraw && !_bordersDirty)
+ return;
+
+ renderConsole(&_screen, _consoleTextArea);
+ paintBorder(&_screen, _consoleTextArea, kWindowConsole);
+}
+
void Gui::drawBox(Graphics::Surface *g, int x, int y, int w, int h) {
Common::Rect r(x, y, x + w + 1, y + h + 1);
@@ -310,7 +327,17 @@ const int arrowPixels[ARROW_H][ARROW_W] = {
{0,1,1,1,1,1,1,1,1,1,1,0},
{1,1,1,1,1,1,1,1,1,1,1,1}};
-void Gui::paintBorder(Graphics::Surface *g, Common::Rect &r, WindowType windowType, int highlightedPart) {
+static void drawPixelInverted(int x, int y, int color, void *data) {
+ Graphics::Surface *surface = (Graphics::Surface *)data;
+
+ if (x >= 0 && x < surface->w && y >= 0 && y < surface->h) {
+ byte *p = (byte *)surface->getBasePtr(x, y);
+
+ *p = *p == kColorWhite ? kColorBlack : kColorWhite;
+ }
+}
+
+void Gui::paintBorder(Graphics::Surface *g, Common::Rect &r, WindowType windowType, int highlightedPart, float scrollPos, float scrollSize) {
bool active = false, scrollable = false, closeable = false, drawTitle = false;
const int size = kBorderWidth;
int x = r.left - size;
@@ -351,39 +378,28 @@ void Gui::paintBorder(Graphics::Surface *g, Common::Rect &r, WindowType windowTy
} else {
int x1 = x + width - 15;
int y1 = y + size + 1;
- int color1 = kColorBlack;
- int color2 = kColorWhite;
- if (highlightedPart == kBorderScrollUp) {
- SWAP(color1, color2);
- fillRect(g, x + width - kBorderWidth + 2, y + size, size - 4, r.height() / 2);
- }
+
for (int yy = 0; yy < ARROW_H; yy++) {
- for (int xx = 0; xx < ARROW_W; xx++) {
- if (arrowPixels[yy][xx] != 0) {
- g->hLine(x1 + xx, y1 + yy, x1 + xx, color1);
- } else {
- g->hLine(x1 + xx, y1 + yy, x1 + xx, color2);
- }
- }
+ for (int xx = 0; xx < ARROW_W; xx++)
+ g->hLine(x1 + xx, y1 + yy, x1 + xx, (arrowPixels[yy][xx] != 0 ? kColorBlack : kColorWhite));
}
- fillRect(g, x + width - 13, y + size + ARROW_H, 8, r.height() / 2 - ARROW_H, color1);
- color1 = kColorBlack;
- color2 = kColorWhite;
- if (highlightedPart == kBorderScrollDown) {
- SWAP(color1, color2);
- fillRect(g, x + width - kBorderWidth + 2, y + size + r.height() / 2, size - 4, r.height() / 2);
- }
- fillRect(g, x + width - 13, y + size + r.height() / 2, 8, r.height() / 2 - ARROW_H, color1);
+ fillRect(g, x + width - 13, y + size + ARROW_H, 8, height - 2 * size - 1 - ARROW_H * 2);
+
y1 += height - 2 * size - ARROW_H - 2;
for (int yy = 0; yy < ARROW_H; yy++) {
- for (int xx = 0; xx < ARROW_W; xx++) {
- if (arrowPixels[ARROW_H - yy - 1][xx] != 0) {
- g->hLine(x1 + xx, y1 + yy, x1 + xx, color1);
- } else {
- g->hLine(x1 + xx, y1 + yy, x1 + xx, color2);
- }
- }
+ for (int xx = 0; xx < ARROW_W; xx++)
+ g->hLine(x1 + xx, y1 + yy, x1 + xx, (arrowPixels[ARROW_H - yy - 1][xx] != 0 ? kColorBlack : kColorWhite));
+ }
+
+ if (highlightedPart == kBorderScrollUp || highlightedPart == kBorderScrollDown) {
+ int rx1 = x + width - kBorderWidth + 2;
+ int ry1 = y + size + r.height() * scrollPos;
+ int rx2 = rx1 + size - 4;
+ int ry2 = ry1 + r.height() * scrollSize;
+ Common::Rect rr(rx1, ry1, rx2, ry2);
+
+ Graphics::drawFilledRect(rr, kColorBlack, drawPixelInverted, g);
}
}
if (closeable) {
@@ -619,7 +635,11 @@ void Gui::mouseDown(int x, int y) {
} else if (_consoleTextArea.contains(x, y)) {
startMarking(x, y);
} else if ((borderClick = isInBorder(_consoleTextArea, x, y)) != kBorderNone) {
- paintBorder(&_screen, _consoleTextArea, kWindowConsole, borderClick);
+ int textFullSize = _lines.size() * _consoleLineHeight + _consoleTextArea.height();
+ float scrollPos = (float)_scrollPos / textFullSize;
+ float scrollSize = (float)_consoleTextArea.height() / textFullSize;
+
+ paintBorder(&_screen, _consoleTextArea, kWindowConsole, borderClick, scrollPos, scrollSize);
}
}
diff --git a/engines/wage/gui.h b/engines/wage/gui.h
index 73814d39b4..48ec41c30a 100644
--- a/engines/wage/gui.h
+++ b/engines/wage/gui.h
@@ -121,9 +121,12 @@ public:
void enableNewGameMenus();
private:
+ void drawScene();
+ void drawConsole();
void undrawCursor();
void drawDesktop();
- void paintBorder(Graphics::Surface *g, Common::Rect &r, WindowType windowType, int highlightedPart = kBorderNone);
+ void paintBorder(Graphics::Surface *g, Common::Rect &r, WindowType windowType, int highlightedPart = kBorderNone,
+ float scrollPos = 0.0, float scrollSize = 0.0);
void renderConsole(Graphics::Surface *g, Common::Rect &r);
void drawBox(Graphics::Surface *g, int x, int y, int w, int h);
void fillRect(Graphics::Surface *g, int x, int y, int w, int h, int color = kColorBlack);
diff --git a/engines/wage/script.cpp b/engines/wage/script.cpp
index 294c08ed82..61336dce88 100644
--- a/engines/wage/script.cpp
+++ b/engines/wage/script.cpp
@@ -1099,7 +1099,7 @@ struct Mapping {
{ "\?\?\?(0xf5)", OPCODE },
{ "\?\?\?(0xf6)", OPCODE },
{ "\?\?\?(0xf7)", OPCODE },
- { "\?\?\?(0xf8)", OPCODE }, // 0xa8
+ { "\?\?\?(0xf8)", OPCODE }, // 0xf8
{ "\?\?\?(0xf9)", OPCODE },
{ "\?\?\?(0xfa)", OPCODE },
{ "\?\?\?(0xfb)", OPCODE },
diff --git a/engines/wage/util.cpp b/engines/wage/util.cpp
index f31a83ca04..8c8af6652e 100644
--- a/engines/wage/util.cpp
+++ b/engines/wage/util.cpp
@@ -122,4 +122,17 @@ const char *getGenderSpecificPronoun(int gender, bool capitalize) {
return capitalize ? "It" : "it";
}
+bool isStorageScene(const Common::String &name) {
+ if (name.equalsIgnoreCase(STORAGESCENE))
+ return true;
+
+ if (name.equalsIgnoreCase("STROAGE@")) // Jumble
+ return true;
+
+ if (name.equalsIgnoreCase("STORAGE@@")) // Jumble
+ return true;
+
+ return false;
+}
+
} // End of namespace Wage
diff --git a/engines/wage/wage.cpp b/engines/wage/wage.cpp
index e0299c8da2..aa480b63fe 100644
--- a/engines/wage/wage.cpp
+++ b/engines/wage/wage.cpp
@@ -281,15 +281,16 @@ void WageEngine::performInitialSetup() {
debug(5, "Resetting Owners: %d", _world->_orderedObjs.size());
for (uint i = 0; i < _world->_orderedObjs.size(); i++) {
Obj *obj = _world->_orderedObjs[i];
- if (!obj->_sceneOrOwner.equalsIgnoreCase(STORAGESCENE)) {
+ if (!isStorageScene(obj->_sceneOrOwner)) {
Common::String location = obj->_sceneOrOwner;
location.toLowercase();
- if (_world->_scenes.contains(location)) {
- _world->move(obj, _world->_scenes[location]);
+ Scene *scene = getSceneByName(location);
+ if (scene != NULL) {
+ _world->move(obj, scene);
} else {
if (!_world->_chrs.contains(location)) {
// Note: PLAYER@ is not a valid target here.
- warning("Couldn't move %s to %s", obj->_name.c_str(), obj->_sceneOrOwner.c_str());
+ warning("Couldn't move %s to \"%s\"", obj->_name.c_str(), obj->_sceneOrOwner.c_str());
} else {
// TODO: Add check for max items.
_world->move(obj, _world->_chrs[location]);
@@ -301,7 +302,7 @@ void WageEngine::performInitialSetup() {
bool playerPlaced = false;
for (uint i = 0; i < _world->_orderedChrs.size(); i++) {
Chr *chr = _world->_orderedChrs[i];
- if (!chr->_initialScene.equalsIgnoreCase(STORAGESCENE)) {
+ if (!isStorageScene(chr->_initialScene)) {
Common::String key = chr->_initialScene;
key.toLowercase();
if (_world->_scenes.contains(key) && _world->_scenes[key] != NULL) {
@@ -328,13 +329,14 @@ void WageEngine::doClose() {
}
Scene *WageEngine::getSceneByName(Common::String &location) {
- Scene *scene;
if (location.equals("random@")) {
- scene = _world->getRandomScene();
+ return _world->getRandomScene();
} else {
- scene = _world->_scenes[location];
+ if (_world->_scenes.contains(location))
+ return _world->_scenes[location];
+ else
+ return NULL;
}
- return scene;
}
void WageEngine::onMove(Designed *what, Designed *from, Designed *to) {
diff --git a/engines/wage/wage.h b/engines/wage/wage.h
index 8ca306aea3..a0be7a70a9 100644
--- a/engines/wage/wage.h
+++ b/engines/wage/wage.h
@@ -75,6 +75,8 @@ typedef Common::Array<Chr *> ChrArray;
typedef Common::List<Obj *> ObjList;
typedef Common::List<Chr *> ChrList;
+#define STORAGESCENE "STORAGE@"
+
enum OperandType {
OBJ = 0,
CHR = 1,
@@ -113,7 +115,7 @@ Common::Rect *readRect(Common::SeekableReadStream *in);
const char *getIndefiniteArticle(const Common::String &word);
const char *prependGenderSpecificPronoun(int gender);
const char *getGenderSpecificPronoun(int gender, bool capitalize);
-
+bool isStorageScene(const Common::String &name);
typedef Common::Array<byte *> Patterns;
diff --git a/engines/wage/world.cpp b/engines/wage/world.cpp
index 40b1555e35..7e7bc33712 100644
--- a/engines/wage/world.cpp
+++ b/engines/wage/world.cpp
@@ -105,6 +105,18 @@ bool World::loadWorld(Common::MacResManager *resMan) {
Common::SeekableReadStream *res;
Common::MacResIDArray::const_iterator iter;
+ // Dumping interpreter code
+#if 1
+ res = resMan->getResource(MKTAG('C','O','D','E'), 1);
+ warning("code size: %d", res->size());
+ byte *buf = (byte *)malloc(res->size());
+ res->read(buf, res->size());
+ Common::DumpFile out;
+ out.open("code.bin");
+ out.write(buf, res->size());
+ out.close();
+#endif
+
if ((resArray = resMan->getResIDArray(MKTAG('G','C','O','D'))).size() == 0)
return false;
@@ -421,7 +433,7 @@ static bool objComparator(const Obj *o1, const Obj *o2) {
if (o1Immobile == o2Immobile) {
return o1->_index - o2->_index;
}
- return o1Immobile;
+ return o1Immobile ? -1 : 1;
}
void World::move(Obj *obj, Scene *scene, bool skipSort) {
diff --git a/engines/wage/world.h b/engines/wage/world.h
index e9041139df..355d660c8d 100644
--- a/engines/wage/world.h
+++ b/engines/wage/world.h
@@ -50,8 +50,6 @@
namespace Wage {
-#define STORAGESCENE "STORAGE@"
-
class Sound;
class World {
diff --git a/engines/wintermute/base/base_persistence_manager.cpp b/engines/wintermute/base/base_persistence_manager.cpp
index 39462f7a15..5a694e7ce2 100644
--- a/engines/wintermute/base/base_persistence_manager.cpp
+++ b/engines/wintermute/base/base_persistence_manager.cpp
@@ -183,7 +183,7 @@ void BasePersistenceManager::getSaveStateDesc(int slot, SaveStateDescriptor &des
}
}
- desc.setSaveDate(_savedTimestamp.tm_year, _savedTimestamp.tm_mon, _savedTimestamp.tm_mday);
+ desc.setSaveDate(_savedTimestamp.tm_year + 1900, _savedTimestamp.tm_mon + 1, _savedTimestamp.tm_mday);
desc.setSaveTime(_savedTimestamp.tm_hour, _savedTimestamp.tm_min);
desc.setPlayTime(0);
}