From 8a595e7771aa89d06876e13d7ab6751e26da8982 Mon Sep 17 00:00:00 2001 From: Martin Kiewitz Date: Fri, 29 Jan 2016 13:13:40 +0100 Subject: AGI: graphics rewrite + cleanup - graphics code fully rewritten - Apple IIgs font support - Amiga Topaz support - Word parser rewritten - menu code rewritten - removed forced 2 second delay on all room changes replaced with heuristic to detect situations, where it's required - lots of naming cleanup - new console commands show_map, screenobj, vmvars and vmflags - all sorts of hacks/workarounds removed - added SCI wait mouse cursor - added Apple IIgs mouse cursor - added Atari ST mouse cursor - added Amiga/Apple IIgs transition - added Atari ST transition - user can select another render mode and use Apple IIgs palette + transition for PC versions - inventory screen rewritten - SetSimple command now properly implemented - PreAGI Mickey: Sierra logo now shown - saved games: now saving controller key mapping also saving automatic save data (SetSimple command) - fixed invalid memory access when saving games (31 bytes were saved using Common::String c_ptr() Special Thanks to: - fuzzie for helping out with the Apple IIgs font + valgrind - eriktorbjorn for helping out with valgrind - LordHoto for figuring out the code, that caused invalid memory access in the original code, when saving a game - sev for help out with reversing the Amiga transition currently missing: - mouse support for menu - mouse support for system dialogs - predictive dialog support --- engines/agi/saveload.cpp | 984 ++++++++++++++++++++++------------------------- 1 file changed, 466 insertions(+), 518 deletions(-) (limited to 'engines/agi/saveload.cpp') diff --git a/engines/agi/saveload.cpp b/engines/agi/saveload.cpp index 41a7a943ff..b11927c542 100644 --- a/engines/agi/saveload.cpp +++ b/engines/agi/saveload.cpp @@ -38,11 +38,14 @@ #include "agi/agi.h" #include "agi/graphics.h" +#include "agi/text.h" #include "agi/sprite.h" #include "agi/keyboard.h" #include "agi/menu.h" +#include "agi/systemui.h" +#include "agi/words.h" -#define SAVEGAME_VERSION 6 +#define SAVEGAME_CURRENT_VERSION 7 // // Version 0 (Sarien): view table has 64 entries @@ -52,19 +55,23 @@ // Version 4 (ScummVM): added thumbnails and save creation date/time // Version 5 (ScummVM): Added game md5 // Version 6 (ScummVM): Added game played time +// Version 7 (ScummVM): Added controller key mappings +// required for some games for quick-loading from ScummVM main menu +// for games, that do not set all key mappings right at the start +// Added automatic save data (for command SetSimple) // namespace Agi { static const uint32 AGIflag = MKTAG('A','G','I',':'); -int AgiEngine::saveGame(const Common::String &fileName, const Common::String &description) { +int AgiEngine::saveGame(const Common::String &fileName, const Common::String &descriptionString) { char gameIDstring[8] = "gameIDX"; int i; Common::OutSaveFile *out; int result = errOK; - debugC(3, kDebugLevelMain | kDebugLevelSavegame, "AgiEngine::saveGame(%s, %s)", fileName.c_str(), description.c_str()); + debugC(3, kDebugLevelMain | kDebugLevelSavegame, "AgiEngine::saveGame(%s, %s)", fileName.c_str(), descriptionString.c_str()); if (!(out = _saveFileMan->openForSaving(fileName))) { warning("Can't create file '%s', game not saved", fileName.c_str()); return errBadFileOpen; @@ -73,10 +80,17 @@ int AgiEngine::saveGame(const Common::String &fileName, const Common::String &de } out->writeUint32BE(AGIflag); - out->write(description.c_str(), 31); - out->writeByte(SAVEGAME_VERSION); - debugC(5, kDebugLevelMain | kDebugLevelSavegame, "Writing save game version (%d)", SAVEGAME_VERSION); + // Write description of saved game, limited to SAVEDGAME_DESCRIPTION_LEN characters + terminating NUL + char description[SAVEDGAME_DESCRIPTION_LEN + 1]; + + memset(description, 0, sizeof(description)); + strncpy(description, descriptionString.c_str(), SAVEDGAME_DESCRIPTION_LEN); + assert(SAVEDGAME_DESCRIPTION_LEN + 1 == 31); // safety + out->write(description, 31); + + out->writeByte(SAVEGAME_CURRENT_VERSION); + debugC(5, kDebugLevelMain | kDebugLevelSavegame, "Writing save game version (%d)", SAVEGAME_CURRENT_VERSION); // Thumbnail Graphics::saveThumbnail(*out); @@ -116,37 +130,50 @@ int AgiEngine::saveGame(const Common::String &fileName, const Common::String &de out->writeByte(tmp[i]); } + // Version 7+: Save automatic saving state (set.simple opcode) + out->writeByte(_game.automaticSave); + out->write(_game.automaticSaveDescription, 31); + for (i = 0; i < MAX_FLAGS; i++) out->writeByte(_game.flags[i]); for (i = 0; i < MAX_VARS; i++) out->writeByte(_game.vars[i]); out->writeSint16BE((int8)_game.horizon); - out->writeSint16BE((int16)_game.lineStatus); - out->writeSint16BE((int16)_game.lineUserInput); - out->writeSint16BE((int16)_game.lineMinPrint); + out->writeSint16BE((int16)_text->statusRow_Get()); + out->writeSint16BE((int16)_text->promptRow_Get()); + out->writeSint16BE((int16)_text->getWindowRowMin()); out->writeSint16BE((int16)_game.inputMode); out->writeSint16BE((int16)_game.lognum); out->writeSint16BE((int16)_game.playerControl); out->writeSint16BE((int16)shouldQuit()); - out->writeSint16BE((int16)_game.statusLine); + if (_text->statusEnabled()) { + out->writeSint16BE(0x7FFF); + } else { + out->writeSint16BE(0); + } out->writeSint16BE((int16)_game.clockEnabled); out->writeSint16BE((int16)_game.exitAllLogics); out->writeSint16BE((int16)_game.pictureShown); out->writeSint16BE((int16)_game.hasPrompt); out->writeSint16BE((int16)_game.gameFlags); - out->writeSint16BE(_game.inputEnabled); + if (_text->promptIsEnabled()) { + out->writeSint16BE(0x7FFF); + } else { + out->writeSint16BE(0); + } - for (i = 0; i < _HEIGHT; i++) - out->writeByte(_game.priTable[i]); + // TODO: save if priority table was modified + for (i = 0; i < SCRIPT_HEIGHT; i++) + out->writeByte(_gfx->priorityFromY(i)); out->writeSint16BE((int16)_game.gfxMode); - out->writeByte(_game.cursorChar); - out->writeSint16BE((int16)_game.colorFg); - out->writeSint16BE((int16)_game.colorBg); + out->writeByte(_text->inputGetCursorChar()); + out->writeSint16BE((int16)_text->charAttrib_GetForeground()); + out->writeSint16BE((int16)_text->charAttrib_GetBackground()); // game.hires // game.sbuf @@ -157,21 +184,29 @@ int AgiEngine::saveGame(const Common::String &fileName, const Common::String &de for (i = 0; i < (int16)_game.numObjects; i++) out->writeSint16BE((int16)objectGetLocation(i)); + // Version 7+: save controller key mappings + // required for games, that do not set all key mappings right at the start + // when quick restoring is used from ScummVM menu, only 1 cycle is executed + for (i = 0; i < MAX_CONTROLLER_KEYMAPPINGS; i++) { + out->writeUint16BE(_game.controllerKeyMapping[i].keycode); + out->writeByte(_game.controllerKeyMapping[i].controllerSlot); + } + // game.ev_keyp for (i = 0; i < MAX_STRINGS; i++) out->write(_game.strings[i], MAX_STRINGLEN); // record info about loaded resources - for (i = 0; i < MAX_DIRS; i++) { + for (i = 0; i < MAX_DIRECTORY_ENTRIES; i++) { out->writeByte(_game.dirLogic[i].flags); out->writeSint16BE((int16)_game.logics[i].sIP); out->writeSint16BE((int16)_game.logics[i].cIP); } - for (i = 0; i < MAX_DIRS; i++) + for (i = 0; i < MAX_DIRECTORY_ENTRIES; i++) out->writeByte(_game.dirPic[i].flags); - for (i = 0; i < MAX_DIRS; i++) + for (i = 0; i < MAX_DIRECTORY_ENTRIES; i++) out->writeByte(_game.dirView[i].flags); - for (i = 0; i < MAX_DIRS; i++) + for (i = 0; i < MAX_DIRECTORY_ENTRIES; i++) out->writeByte(_game.dirSound[i].flags); // game.pictures @@ -179,51 +214,77 @@ int AgiEngine::saveGame(const Common::String &fileName, const Common::String &de // game.views // game.sounds - for (i = 0; i < MAX_VIEWTABLE; i++) { - VtEntry *v = &_game.viewTable[i]; + for (i = 0; i < SCREENOBJECTS_MAX; i++) { + ScreenObjEntry *screenObj = &_game.screenObjTable[i]; - out->writeByte(v->stepTime); - out->writeByte(v->stepTimeCount); - out->writeByte(v->entry); - out->writeSint16BE(v->xPos); - out->writeSint16BE(v->yPos); - out->writeByte(v->currentView); + out->writeByte(screenObj->stepTime); + out->writeByte(screenObj->stepTimeCount); + out->writeByte(screenObj->objectNr); + out->writeSint16BE(screenObj->xPos); + out->writeSint16BE(screenObj->yPos); + out->writeByte(screenObj->currentViewNr); // v->view_data - out->writeByte(v->currentLoop); - out->writeByte(v->numLoops); + out->writeByte(screenObj->currentLoopNr); + out->writeByte(screenObj->loopCount); // v->loop_data - out->writeByte(v->currentCel); - out->writeByte(v->numCels); + out->writeByte(screenObj->currentCelNr); + out->writeByte(screenObj->celCount); // v->cel_data // v->cel_data_2 - out->writeSint16BE(v->xPos2); - out->writeSint16BE(v->yPos2); + out->writeSint16BE(screenObj->xPos_prev); + out->writeSint16BE(screenObj->yPos_prev); // v->s - out->writeSint16BE(v->xSize); - out->writeSint16BE(v->ySize); - out->writeByte(v->stepSize); - out->writeByte(v->cycleTime); - out->writeByte(v->cycleTimeCount); - out->writeByte(v->direction); + out->writeSint16BE(screenObj->xSize); + out->writeSint16BE(screenObj->ySize); + out->writeByte(screenObj->stepSize); + out->writeByte(screenObj->cycleTime); + out->writeByte(screenObj->cycleTimeCount); + out->writeByte(screenObj->direction); - out->writeByte(v->motion); - out->writeByte(v->cycle); - out->writeByte(v->priority); + out->writeByte(screenObj->motionType); + out->writeByte(screenObj->cycle); + out->writeByte(screenObj->priority); - out->writeUint16BE(v->flags); + out->writeUint16BE(screenObj->flags); - out->writeByte(v->parm1); - out->writeByte(v->parm2); - out->writeByte(v->parm3); - out->writeByte(v->parm4); + // this was done so that saved games compatibility isn't broken + switch (screenObj->motionType) { + case kMotionNormal: + out->writeByte(0); + out->writeByte(0); + out->writeByte(0); + out->writeByte(0); + break; + case kMotionWander: + out->writeByte(screenObj->wander_count); + out->writeByte(0); + out->writeByte(0); + out->writeByte(0); + break; + case kMotionFollowEgo: + out->writeByte(screenObj->follow_stepSize); + out->writeByte(screenObj->follow_flag); + out->writeByte(screenObj->follow_count); + out->writeByte(0); + break; + case kMotionEgo: + case kMotionMoveObj: + out->writeByte((byte)screenObj->move_x); // problematic! int16 -> byte + out->writeByte((byte)screenObj->move_y); + out->writeByte(screenObj->move_stepSize); + out->writeByte(screenObj->move_flag); + break; + default: + error("unknown motion-type"); + } } // Save image stack @@ -249,7 +310,7 @@ int AgiEngine::saveGame(const Common::String &fileName, const Common::String &de warning("Can't write file '%s'. (Disk full?)", fileName.c_str()); result = errIOError; } else - debugC(1, kDebugLevelMain | kDebugLevelSavegame, "Saved game %s in file %s", description.c_str(), fileName.c_str()); + debugC(1, kDebugLevelMain | kDebugLevelSavegame, "Saved game %s in file %s", descriptionString.c_str(), fileName.c_str()); delete out; debugC(3, kDebugLevelMain | kDebugLevelSavegame, "Closed %s", fileName.c_str()); @@ -260,8 +321,10 @@ int AgiEngine::saveGame(const Common::String &fileName, const Common::String &de } int AgiEngine::loadGame(const Common::String &fileName, bool checkId) { - char description[31], saveVersion, loadId[8]; - int i, vtEntries = MAX_VIEWTABLE; + char description[SAVEDGAME_DESCRIPTION_LEN + 1]; + byte saveVersion = 0; + char loadId[8]; + int i, vtEntries = SCREENOBJECTS_MAX; uint8 t; int16 parm[7]; Common::InSaveFile *in; @@ -284,17 +347,28 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) { return errOK; } - in->read(description, 31); + assert(SAVEDGAME_DESCRIPTION_LEN + 1 == 31); // safety + in->read(description, 31); // skip description + // check, if there is a terminating NUL inside description + uint16 descriptionPos = 0; + while (description[descriptionPos]) { + descriptionPos++; + if (descriptionPos >= sizeof(description)) + error("saved game description is corrupt"); + } debugC(6, kDebugLevelMain | kDebugLevelSavegame, "Description is: %s", description); saveVersion = in->readByte(); if (saveVersion < 2) // is the save game pre-ScummVM? - warning("Old save game version (%d, current version is %d). Will try and read anyway, but don't be surprised if bad things happen", saveVersion, SAVEGAME_VERSION); + warning("Old save game version (%d, current version is %d). Will try and read anyway, but don't be surprised if bad things happen", saveVersion, SAVEGAME_CURRENT_VERSION); if (saveVersion < 3) warning("This save game contains no AGIPAL data, if the game is using the AGIPAL hack, it won't work correctly"); + if (saveVersion > SAVEGAME_CURRENT_VERSION) + error("Saved game was created with a newer version of ScummVM. Unable to load."); + if (saveVersion >= 4) { // We don't need the thumbnail here, so just read it and discard it Graphics::skipThumbnail(*in); @@ -348,53 +422,74 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) { } } + if (saveVersion >= 7) { + // Restore automatic saving state (set.simple opcode) + _game.automaticSave = in->readByte(); + in->read(_game.automaticSaveDescription, 31); + } else { + _game.automaticSave = false; + _game.automaticSaveDescription[0] = 0; + } + for (i = 0; i < MAX_FLAGS; i++) _game.flags[i] = in->readByte(); for (i = 0; i < MAX_VARS; i++) _game.vars[i] = in->readByte(); - setvar(vFreePages, 180); // Set amount of free memory to realistic value (Overwriting the just loaded value) + setVar(VM_VAR_FREE_PAGES, 180); // Set amount of free memory to realistic value (Overwriting the just loaded value) _game.horizon = in->readSint16BE(); - _game.lineStatus = in->readSint16BE(); - _game.lineUserInput = in->readSint16BE(); - _game.lineMinPrint = in->readSint16BE(); + _text->statusRow_Set(in->readSint16BE()); + _text->promptRow_Set(in->readSint16BE()); + _text->configureScreen(in->readSint16BE()); // These are never saved - _game.cursorPos = 0; - _game.inputBuffer[0] = 0; - _game.echoBuffer[0] = 0; + _text->promptReset(); + _game.keypress = 0; _game.inputMode = (InputMode)in->readSint16BE(); + if ((_game.inputMode != INPUTMODE_NORMAL) && (_game.inputMode != INPUTMODE_NONE)) { + // other input modes were removed + _game.inputMode = INPUTMODE_NORMAL; + } + _game.lognum = in->readSint16BE(); _game.playerControl = in->readSint16BE(); if (in->readSint16BE()) quitGame(); - _game.statusLine = in->readSint16BE(); + if (in->readSint16BE()) { + _text->statusEnable(); + } else { + _text->statusDisable(); + } _game.clockEnabled = in->readSint16BE(); _game.exitAllLogics = in->readSint16BE(); - _game.pictureShown = in->readSint16BE(); + in->readSint16BE(); // was _game.pictureShown + //_game.pictureShown = in->readSint16BE(); _game.hasPrompt = in->readSint16BE(); _game.gameFlags = in->readSint16BE(); - _game.inputEnabled = in->readSint16BE(); + if (in->readSint16BE()) { + _text->promptEnable(); + } else { + _text->promptDisable(); + } - for (i = 0; i < _HEIGHT; i++) - _game.priTable[i] = in->readByte(); + for (i = 0; i < SCRIPT_HEIGHT; i++) + _gfx->setPriority(i, in->readByte()); - if (_game.hasWindow) - closeWindow(); + _text->closeWindow(); _game.msgBoxTicks = 0; _game.block.active = false; - // game.window - fixed by close_window() - // game.has_window - fixed by close_window() _game.gfxMode = in->readSint16BE(); - _game.cursorChar = in->readByte(); - _game.colorFg = in->readSint16BE(); - _game.colorBg = in->readSint16BE(); + _text->inputSetCursorChar(in->readByte()); + + int16 textForeground = in->readSint16BE(); + int16 textBackground = in->readSint16BE(); + _text->charAttrib_Set(textForeground, textBackground); // game.hires - rebuilt from image stack // game.sbuf - rebuilt from image stack @@ -407,41 +502,49 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) { objectSetLocation(i, in->readSint16BE()); // Those are not serialized - for (i = 0; i < MAX_DIRS; i++) { + for (i = 0; i < MAX_CONTROLLERS; i++) { _game.controllerOccured[i] = false; } + if (saveVersion >= 7) { + // For old saves, we just keep the current controllers + for (i = 0; i < MAX_CONTROLLER_KEYMAPPINGS; i++) { + _game.controllerKeyMapping[i].keycode = in->readUint16BE(); + _game.controllerKeyMapping[i].controllerSlot = in->readByte(); + } + } + for (i = 0; i < MAX_STRINGS; i++) in->read(_game.strings[i], MAX_STRINGLEN); - for (i = 0; i < MAX_DIRS; i++) { + for (i = 0; i < MAX_DIRECTORY_ENTRIES; i++) { if (in->readByte() & RES_LOADED) - agiLoadResource(rLOGIC, i); + agiLoadResource(RESOURCETYPE_LOGIC, i); else - agiUnloadResource(rLOGIC, i); + agiUnloadResource(RESOURCETYPE_LOGIC, i); _game.logics[i].sIP = in->readSint16BE(); _game.logics[i].cIP = in->readSint16BE(); } - for (i = 0; i < MAX_DIRS; i++) { + for (i = 0; i < MAX_DIRECTORY_ENTRIES; i++) { if (in->readByte() & RES_LOADED) - agiLoadResource(rPICTURE, i); + agiLoadResource(RESOURCETYPE_PICTURE, i); else - agiUnloadResource(rPICTURE, i); + agiUnloadResource(RESOURCETYPE_PICTURE, i); } - for (i = 0; i < MAX_DIRS; i++) { + for (i = 0; i < MAX_DIRECTORY_ENTRIES; i++) { if (in->readByte() & RES_LOADED) - agiLoadResource(rVIEW, i); + agiLoadResource(RESOURCETYPE_VIEW, i); else - agiUnloadResource(rVIEW, i); + agiUnloadResource(RESOURCETYPE_VIEW, i); } - for (i = 0; i < MAX_DIRS; i++) { + for (i = 0; i < MAX_DIRECTORY_ENTRIES; i++) { if (in->readByte() & RES_LOADED) - agiLoadResource(rSOUND, i); + agiLoadResource(RESOURCETYPE_SOUND, i); else - agiUnloadResource(rSOUND, i); + agiUnloadResource(RESOURCETYPE_SOUND, i); } // game.pictures - loaded above @@ -450,78 +553,102 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) { // game.sounds - loaded above for (i = 0; i < vtEntries; i++) { - VtEntry *v = &_game.viewTable[i]; - - v->stepTime = in->readByte(); - v->stepTimeCount = in->readByte(); - v->entry = in->readByte(); - v->xPos = in->readSint16BE(); - v->yPos = in->readSint16BE(); - v->currentView = in->readByte(); - - // v->view_data - fixed below - - v->currentLoop = in->readByte(); - v->numLoops = in->readByte(); - - // v->loop_data - fixed below - - v->currentCel = in->readByte(); - v->numCels = in->readByte(); - - // v->cel_data - fixed below - // v->cel_data_2 - fixed below - - v->xPos2 = in->readSint16BE(); - v->yPos2 = in->readSint16BE(); - - // v->s - fixed below - - v->xSize = in->readSint16BE(); - v->ySize = in->readSint16BE(); - v->stepSize = in->readByte(); - v->cycleTime = in->readByte(); - v->cycleTimeCount = in->readByte(); - v->direction = in->readByte(); - - v->motion = (MotionType)in->readByte(); - v->cycle = (CycleType)in->readByte(); - v->priority = in->readByte(); - - v->flags = in->readUint16BE(); - - v->parm1 = in->readByte(); - v->parm2 = in->readByte(); - v->parm3 = in->readByte(); - v->parm4 = in->readByte(); + ScreenObjEntry *screenObj = &_game.screenObjTable[i]; + + screenObj->stepTime = in->readByte(); + screenObj->stepTimeCount = in->readByte(); + screenObj->objectNr = in->readByte(); + screenObj->xPos = in->readSint16BE(); + screenObj->yPos = in->readSint16BE(); + screenObj->currentViewNr = in->readByte(); + + // screenObj->view_data - fixed below + + screenObj->currentLoopNr = in->readByte(); + screenObj->loopCount = in->readByte(); + + // screenObj->loop_data - fixed below + + screenObj->currentCelNr = in->readByte(); + screenObj->celCount = in->readByte(); + + // screenObj->cel_data - fixed below + // screenObj->cel_data_2 - fixed below + + screenObj->xPos_prev = in->readSint16BE(); + screenObj->yPos_prev = in->readSint16BE(); + + // screenObj->s - fixed below + + screenObj->xSize = in->readSint16BE(); + screenObj->ySize = in->readSint16BE(); + screenObj->stepSize = in->readByte(); + screenObj->cycleTime = in->readByte(); + screenObj->cycleTimeCount = in->readByte(); + screenObj->direction = in->readByte(); + + screenObj->motionType = (MotionType)in->readByte(); + screenObj->cycle = (CycleType)in->readByte(); + screenObj->priority = in->readByte(); + + screenObj->flags = in->readUint16BE(); + + // this was done so that saved games compatibility isn't broken + switch (screenObj->motionType) { + case kMotionNormal: + in->readByte(); + in->readByte(); + in->readByte(); + in->readByte(); + break; + case kMotionWander: + screenObj->wander_count = in->readByte(); + in->readByte(); + in->readByte(); + in->readByte(); + break; + case kMotionFollowEgo: + screenObj->follow_stepSize = in->readByte(); + screenObj->follow_flag = in->readByte(); + screenObj->follow_count = in->readByte(); + in->readByte(); + break; + case kMotionEgo: + case kMotionMoveObj: + screenObj->move_x = in->readByte(); // problematic! int16 -> byte + screenObj->move_y = in->readByte(); + screenObj->move_stepSize = in->readByte(); + screenObj->move_flag = in->readByte(); + break; + default: + error("unknown motion-type"); + } } - for (i = vtEntries; i < MAX_VIEWTABLE; i++) { - memset(&_game.viewTable[i], 0, sizeof(VtEntry)); + for (i = vtEntries; i < SCREENOBJECTS_MAX; i++) { + memset(&_game.screenObjTable[i], 0, sizeof(ScreenObjEntry)); } - // Fix some pointers in viewtable + // Fix some pointers in screenObjTable - for (i = 0; i < MAX_VIEWTABLE; i++) { - VtEntry *v = &_game.viewTable[i]; + for (i = 0; i < SCREENOBJECTS_MAX; i++) { + ScreenObjEntry *screenObj = &_game.screenObjTable[i]; - if (_game.dirView[v->currentView].offset == _EMPTY) + if (_game.dirView[screenObj->currentViewNr].offset == _EMPTY) continue; - if (!(_game.dirView[v->currentView].flags & RES_LOADED)) - agiLoadResource(rVIEW, v->currentView); + if (!(_game.dirView[screenObj->currentViewNr].flags & RES_LOADED)) + agiLoadResource(RESOURCETYPE_VIEW, screenObj->currentViewNr); - setView(v, v->currentView); // Fix v->view_data - setLoop(v, v->currentLoop); // Fix v->loop_data - setCel(v, v->currentCel); // Fix v->cel_data - v->celData2 = v->celData; - v->s = NULL; // not sure if it is used... + setView(screenObj, screenObj->currentViewNr); // Fix v->view_data + setLoop(screenObj, screenObj->currentLoopNr); // Fix v->loop_data + setCel(screenObj, screenObj->currentCelNr); // Fix v->cel_data } - _sprites->eraseBoth(); + _sprites->eraseSprites(); + + _game.pictureShown = false; - // Clear input line - _gfx->clearScreen(0); - writeStatus(); + _gfx->clearDisplay(0, false); // clear display screen, but not copy it to actual screen for now b/c transition // Recreate background from saved image stack clearImageStack(); @@ -539,259 +666,26 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) { delete in; debugC(3, kDebugLevelMain | kDebugLevelSavegame, "Closed %s", fileName.c_str()); - setflag(fRestoreJustRan, true); + setflag(VM_FLAG_RESTORE_JUST_RAN, true); _game.hasPrompt = 0; // force input line repaint if necessary - cleanInput(); - - _sprites->eraseBoth(); - _sprites->blitBoth(); - _sprites->commitBoth(); - _picture->showPic(); - _gfx->doUpdate(); - - return errOK; -} - -#define NUM_SLOTS 100 -#define NUM_VISIBLE_SLOTS 12 - -Common::String AgiEngine::getSavegameFilename(int num) const { - Common::String saveLoadSlot = _targetName; - saveLoadSlot += Common::String::format(".%.3d", num); - return saveLoadSlot; -} - -void AgiEngine::getSavegameDescription(int num, char *buf, bool showEmpty) { - Common::InSaveFile *in; - Common::String fileName = getSavegameFilename(num); - - debugC(4, kDebugLevelMain | kDebugLevelSavegame, "Current game id is %s", _targetName.c_str()); - - if (!(in = _saveFileMan->openForLoading(fileName))) { - debugC(4, kDebugLevelMain | kDebugLevelSavegame, "File %s does not exist", fileName.c_str()); - - if (showEmpty) - strcpy(buf, " (empty slot)"); - else - *buf = 0; - } else { - debugC(4, kDebugLevelMain | kDebugLevelSavegame, "Successfully opened %s for reading", fileName.c_str()); - - uint32 type = in->readUint32BE(); - - if (type == AGIflag) { - debugC(6, kDebugLevelMain | kDebugLevelSavegame, "Has AGI flag, good start"); - in->read(buf, 31); - } else { - warning("This doesn't appear to be an AGI savegame"); - strcpy(buf, "(corrupt file)"); - } - - delete in; - } -} - -int AgiEngine::selectSlot() { - int i, key, active = 0; - int rc = -1; - int hm = 1, vm = 3; // box margins - int xmin, xmax, slotClicked; - char desc[NUM_VISIBLE_SLOTS][40]; - int textCenter, buttonLength, buttonX[2], buttonY; - const char *buttonText[] = { " OK ", "Cancel", NULL }; - - _noSaveLoadAllowed = true; - - for (i = 0; i < NUM_VISIBLE_SLOTS; i++) { - getSavegameDescription(_firstSlot + i, desc[i]); - } - - textCenter = GFX_WIDTH / CHAR_LINES / 2; - buttonLength = 6; - buttonX[0] = (textCenter - 3 * buttonLength / 2) * CHAR_COLS; - buttonX[1] = (textCenter + buttonLength / 2) * CHAR_COLS; - buttonY = (vm + 17) * CHAR_LINES; - - for (i = 0; i < 2; i++) - _gfx->drawCurrentStyleButton(buttonX[i], buttonY, buttonText[i], false, false, i == 0); - - AllowSyntheticEvents on(this); - int oldFirstSlot = _firstSlot + 1; - int oldActive = active + 1; - bool exitSelectSlot = false; - while (!exitSelectSlot && !(shouldQuit() || _restartGame)) { - int sbPos = 0; - - // Use the extreme scrollbar positions only if the extreme - // slots are in sight. (We have to calculate this even if we - // don't redraw the save slots, because it's also used for - // clicking in the scrollbar. - - if (_firstSlot == 0) - sbPos = 1; - else if (_firstSlot == NUM_SLOTS - NUM_VISIBLE_SLOTS) - sbPos = NUM_VISIBLE_SLOTS - 2; - else { - sbPos = 2 + (_firstSlot * (NUM_VISIBLE_SLOTS - 4)) / (NUM_SLOTS - NUM_VISIBLE_SLOTS - 1); - if (sbPos >= NUM_VISIBLE_SLOTS - 3) - sbPos = NUM_VISIBLE_SLOTS - 3; - } - - if (oldFirstSlot != _firstSlot || oldActive != active) { - char dstr[64]; - for (i = 0; i < NUM_VISIBLE_SLOTS; i++) { - sprintf(dstr, "[%2d. %-28.28s]", i + _firstSlot, desc[i]); - printText(dstr, 0, hm + 1, vm + 4 + i, - (40 - 2 * hm) - 1, i == active ? MSG_BOX_COLOR : MSG_BOX_TEXT, - i == active ? MSG_BOX_TEXT : MSG_BOX_COLOR); - } - - char upArrow[] = "^"; - char downArrow[] = "v"; - char scrollBar[] = " "; - - for (i = 1; i < NUM_VISIBLE_SLOTS - 1; i++) - printText(scrollBar, 35, hm + 1, vm + 4 + i, 1, MSG_BOX_COLOR, 7, true); - - printText(upArrow, 35, hm + 1, vm + 4, 1, 8, 7); - printText(downArrow, 35, hm + 1, vm + 4 + NUM_VISIBLE_SLOTS - 1, 1, 8, 7); - printText(scrollBar, 35, hm + 1, vm + 4 + sbPos, 1, MSG_BOX_COLOR, MSG_BOX_TEXT); - - oldActive = active; - oldFirstSlot = _firstSlot; - } - - pollTimer(); - key = doPollKeyboard(); - - // It may happen that somebody will open GMM while - // this dialog is open, and load a game - // We are processing it here, effectively jumping - // out of the dead loop - if (getflag(fRestoreJustRan)) { - rc = -2; - exitSelectSlot = true; - } + _words->clearEgoWords(); - if (!exitSelectSlot) { - switch (key) { - case KEY_ENTER: - rc = active; - Common::strlcpy(_game.strings[MAX_STRINGS], desc[i], MAX_STRINGLEN); - debugC(8, kDebugLevelMain | kDebugLevelInput, "Button pressed: %d", rc); - exitSelectSlot = true; - break; - case KEY_ESCAPE: - rc = -1; - exitSelectSlot = true; - break; - case BUTTON_LEFT: - if (_gfx->testButton(buttonX[0], buttonY, buttonText[0])) { - rc = active; - strncpy(_game.strings[MAX_STRINGS], desc[i], MAX_STRINGLEN); - debugC(8, kDebugLevelMain | kDebugLevelInput, "Button pressed: %d", rc); - exitSelectSlot = true; - } else if (_gfx->testButton(buttonX[1], buttonY, buttonText[1])) { - rc = -1; - exitSelectSlot = true; - } else { - slotClicked = ((int)_mouse.y - 1) / CHAR_COLS - (vm + 4); - xmin = (hm + 1) * CHAR_COLS; - xmax = xmin + CHAR_COLS * 34; - if ((int)_mouse.x >= xmin && (int)_mouse.x <= xmax) { - if (slotClicked >= 0 && slotClicked < NUM_VISIBLE_SLOTS) - active = slotClicked; - } - xmin = (hm + 36) * CHAR_COLS; - xmax = xmin + CHAR_COLS; - if ((int)_mouse.x >= xmin && (int)_mouse.x <= xmax) { - if (slotClicked >= 0 && slotClicked < NUM_VISIBLE_SLOTS) { - if (slotClicked == 0) - keyEnqueue(KEY_UP); - else if (slotClicked == NUM_VISIBLE_SLOTS - 1) - keyEnqueue(KEY_DOWN); - else if (slotClicked < sbPos) - keyEnqueue(KEY_UP_RIGHT); - else if (slotClicked > sbPos) - keyEnqueue(KEY_DOWN_RIGHT); - } - } - } - break; + // don't delay anything right after restoring a game + nonBlockingText_Forget(); - case KEY_DOWN: - active++; - if (active >= NUM_VISIBLE_SLOTS) { - if (_firstSlot + NUM_VISIBLE_SLOTS < NUM_SLOTS) { - _firstSlot++; - for (i = 1; i < NUM_VISIBLE_SLOTS; i++) - memcpy(desc[i - 1], desc[i], sizeof(desc[0])); - getSavegameDescription(_firstSlot + NUM_VISIBLE_SLOTS - 1, desc[NUM_VISIBLE_SLOTS - 1]); - } - active = NUM_VISIBLE_SLOTS - 1; - } - break; - case KEY_UP: - active--; - if (active < 0) { - active = 0; - if (_firstSlot > 0) { - _firstSlot--; - for (i = NUM_VISIBLE_SLOTS - 1; i > 0; i--) - memcpy(desc[i], desc[i - 1], sizeof(desc[0])); - getSavegameDescription(_firstSlot, desc[0]); - } - } - break; + _sprites->eraseSprites(); + _sprites->buildAllSpriteLists(); + _sprites->drawAllSpriteLists(); + _picture->showPicWithTransition(); + _game.pictureShown = true; + _text->statusDraw(); + _text->promptRedraw(); - // Page Up/Down and mouse wheel scrolling all leave 'active' - // unchanged so that a visible slot will remain selected. + // copy everything over (we should probably only copy over the remaining parts of the screen w/o play screen + _gfx->copyDisplayRectToScreen(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT); - case WHEEL_DOWN: - if (_firstSlot < NUM_SLOTS - NUM_VISIBLE_SLOTS) { - _firstSlot++; - for (i = 1; i < NUM_VISIBLE_SLOTS; i++) - memcpy(desc[i - 1], desc[i], sizeof(desc[0])); - getSavegameDescription(_firstSlot + NUM_VISIBLE_SLOTS - 1, desc[NUM_VISIBLE_SLOTS - 1]); - } - break; - case WHEEL_UP: - if (_firstSlot > 0) { - _firstSlot--; - for (i = NUM_VISIBLE_SLOTS - 1; i > 0; i--) - memcpy(desc[i], desc[i - 1], sizeof(desc[0])); - getSavegameDescription(_firstSlot, desc[0]); - } - break; - case KEY_DOWN_RIGHT: - // This is probably triggered by Page Down. - _firstSlot += NUM_VISIBLE_SLOTS; - if (_firstSlot > NUM_SLOTS - NUM_VISIBLE_SLOTS) { - _firstSlot = NUM_SLOTS - NUM_VISIBLE_SLOTS; - } - for (i = 0; i < NUM_VISIBLE_SLOTS; i++) - getSavegameDescription(_firstSlot + i, desc[i]); - break; - case KEY_UP_RIGHT: - // This is probably triggered by Page Up. - _firstSlot -= NUM_VISIBLE_SLOTS; - if (_firstSlot < 0) { - _firstSlot = 0; - } - for (i = 0; i < NUM_VISIBLE_SLOTS; i++) - getSavegameDescription(_firstSlot + i, desc[i]); - break; - } - } - _gfx->doUpdate(); - } - - closeWindow(); - - _noSaveLoadAllowed = false; - - return rc; + return errOK; } int AgiEngine::scummVMSaveLoadDialog(bool isSave) { @@ -834,7 +728,8 @@ int AgiEngine::doSave(int slot, const Common::String &desc) { // Make sure all graphics was blitted to screen. This fixes bug // #2960567: "AGI: Ego partly erased in Load/Save thumbnails" - _gfx->doUpdate(); + _gfx->updateScreen(); +// _gfx->doUpdate(); return saveGame(fileName, desc); } @@ -843,160 +738,213 @@ int AgiEngine::doLoad(int slot, bool showMessages) { Common::String fileName = getSavegameFilename(slot); debugC(8, kDebugLevelMain | kDebugLevelResources, "file is [%s]", fileName.c_str()); - _sprites->eraseBoth(); + _sprites->eraseSprites(); _sound->stopSound(); - closeWindow(); + _text->closeWindow(); int result = loadGame(fileName); if (result == errOK) { - if (showMessages) - messageBox("Game restored."); _game.exitAllLogics = 1; - _menu->enableAll(); + _menu->itemEnableAll(); } else { if (showMessages) - messageBox("Error restoring game."); + _text->messageBox("Error restoring game."); } return result; } -int AgiEngine::saveGameDialog() { - if (!ConfMan.getBool("originalsaveload")) - return scummVMSaveLoadDialog(true); +SavedGameSlotIdArray AgiEngine::getSavegameSlotIds() { + Common::StringArray filenames; + int16 numberPos = _targetName.size() + 1; + int16 slotId = 0; + SavedGameSlotIdArray slotIdArray; - char *desc; - const char *buttons[] = { "Do as I say!", "I regret", NULL }; - char dstr[200]; - int rc, slot = 0; - int hm, vm, hp, vp; - int w; - - hm = 1; - vm = 3; - hp = hm * CHAR_COLS; - vp = vm * CHAR_LINES; - w = (40 - 2 * hm) - 1; - - do { - drawWindow(hp, vp, GFX_WIDTH - hp, GFX_HEIGHT - vp); - printText("Select a slot in which you wish to\nsave the game:", - 0, hm + 1, vm + 1, w, MSG_BOX_TEXT, MSG_BOX_COLOR); - slot = selectSlot(); - if (slot + _firstSlot == 0) - messageBox("That slot is for Autosave only."); - else if (slot < 0) - return errOK; - } while (slot + _firstSlot == 0); - - drawWindow(hp, vp + 5 * CHAR_LINES, GFX_WIDTH - hp, - GFX_HEIGHT - vp - 9 * CHAR_LINES); - printText("Enter a description for this game:", - 0, hm + 1, vm + 6, w, MSG_BOX_TEXT, MSG_BOX_COLOR); - _gfx->drawRectangle(3 * CHAR_COLS, 11 * CHAR_LINES - 1, - 37 * CHAR_COLS, 12 * CHAR_LINES, MSG_BOX_TEXT); - _gfx->flushBlock(3 * CHAR_COLS, 11 * CHAR_LINES - 1, - 37 * CHAR_COLS, 12 * CHAR_LINES); - - // The description field of the save/restore dialog holds 32 characters - // but we use four of them for the slot number. The input field is a - // bit wider than that, so we don't have to worry about leaving space - // for the cursor. - - getString(2, 11, 28, MAX_STRINGS); - - // If we're saving over an old slot, show the old description. We can't - // access that buffer directly, so we have to feed the characters to - // the input handler one at a time. - - char name[40]; - int numChars; - - getSavegameDescription(_firstSlot + slot, name, false); - - for (numChars = 0; numChars < 28 && name[numChars]; numChars++) - handleGetstring(name[numChars]); - - _gfx->printCharacter(numChars + 3, 11, _game.cursorChar, MSG_BOX_COLOR, MSG_BOX_TEXT); - do { - mainCycle(); - } while (_game.inputMode == INPUT_GETSTRING); - closeWindow(); - - desc = _game.strings[MAX_STRINGS]; - sprintf(dstr, "Are you sure you want to save the game " - "described as:\n\n%s\n\nin slot %d?\n\n\n", desc, _firstSlot + slot); - - rc = selectionBox(dstr, buttons); - - if (rc != 0) { - messageBox("Game NOT saved."); - return errOK; + // search for saved game filenames... + filenames = _saveFileMan->listSavefiles(_targetName + ".###"); + + Common::StringArray::iterator it; + Common::StringArray::iterator end = filenames.end();; + + // convert to lower-case, just to be sure + for (it = filenames.begin(); it != end; it++) { + it->toLowercase(); } + // sort + Common::sort(filenames.begin(), filenames.end()); - int result = doSave(_firstSlot + slot, desc); + // now extract slot-Ids + for (it = filenames.begin(); it != end; it++) { + slotId = atoi(it->c_str() + numberPos); - if (result == errOK) - messageBox("Game saved."); - else - messageBox("Error saving game."); + slotIdArray.push_back(slotId); + } + return slotIdArray; +} - return result; +Common::String AgiEngine::getSavegameFilename(int16 slotId) const { + Common::String saveLoadSlot = _targetName; + saveLoadSlot += Common::String::format(".%.3d", slotId); + return saveLoadSlot; } -int AgiEngine::saveGameSimple() { - if (!ConfMan.getBool("originalsaveload")) - return scummVMSaveLoadDialog(true); +bool AgiEngine::getSavegameInformation(int16 slotId, Common::String &saveDescription, uint32 &saveDate, uint16 &saveTime, bool &saveIsValid) { + Common::InSaveFile *in; + Common::String fileName = getSavegameFilename(slotId); + char saveGameDescription[31]; + int16 curPos = 0; + byte saveVersion = 0; - Common::String fileName = getSavegameFilename(0); + saveDescription.clear(); + saveDate = 0; + saveTime = 0; + saveIsValid = false; - int result = saveGame(fileName, "Default savegame"); - if (result != errOK) - messageBox("Error saving game."); - return result; -} + debugC(4, kDebugLevelMain | kDebugLevelSavegame, "Current game id is %s", _targetName.c_str()); -int AgiEngine::loadGameDialog() { - if (!ConfMan.getBool("originalsaveload")) - return scummVMSaveLoadDialog(false); + if (!(in = _saveFileMan->openForLoading(fileName))) { + debugC(4, kDebugLevelMain | kDebugLevelSavegame, "File %s does not exist", fileName.c_str()); + return false; + + } else { + debugC(4, kDebugLevelMain | kDebugLevelSavegame, "Successfully opened %s for reading", fileName.c_str()); - int slot = 0; - int hm, vm, hp, vp; // box margins - int w; + uint32 type = in->readUint32BE(); - hm = 1; - vm = 3; - hp = hm * CHAR_COLS; - vp = vm * CHAR_LINES; - w = (40 - 2 * hm) - 1; + if (type != AGIflag) { + warning("This doesn't appear to be an AGI savegame"); + saveDescription += "[ScummVM: not an AGI save]"; + delete in; + return true; + } - _sprites->eraseBoth(); - _sound->stopSound(); + debugC(6, kDebugLevelMain | kDebugLevelSavegame, "Has AGI flag, good start"); + if (in->read(saveGameDescription, 31) != 31) { + warning("unexpected EOF"); + delete in; + saveDescription += "[ScummVM: invalid save]"; + return true; + } - drawWindow(hp, vp, GFX_WIDTH - hp, GFX_HEIGHT - vp); - printText("Select a game which you wish to\nrestore:", - 0, hm + 1, vm + 1, w, MSG_BOX_TEXT, MSG_BOX_COLOR); + for (curPos = 0; curPos < 31; curPos++) { + if (!saveGameDescription[curPos]) + break; + } + if (curPos >= 31) { + warning("corrupted description"); + delete in; + saveDescription += "[ScummVM: invalid save]"; + return true; + } - slot = selectSlot(); + saveVersion = in->readByte(); + if (saveVersion > SAVEGAME_CURRENT_VERSION) { + warning("save from a future ScummVM, not supported"); + delete in; + saveDescription += "[ScummVM: not supported]"; + return true; + } - if (slot < 0) { - if (slot == -1) // slot = -2 when GMM was launched - messageBox("Game NOT restored."); + if (saveVersion >= 4) { + // We don't need the thumbnail here, so just read it and discard it + Graphics::skipThumbnail(*in); - return errOK; + saveDate = in->readUint32BE(); + saveTime = in->readUint16BE(); + + // save date is DDMMYYYY, we need a proper format + byte saveDateDay = saveDate >> 24; + byte saveDateMonth = (saveDate >> 16) & 0xFF; + uint16 saveDateYear = saveDate & 0xFFFF; + + saveDate = (saveDateYear << 16) | (saveDateMonth << 8) | saveDateDay; + + } else { + saveDate = 0; + saveTime = 0; + } + + saveDescription += saveGameDescription; + saveIsValid = true; + + delete in; + return true; } +} - return doLoad(_firstSlot + slot, true); +bool AgiEngine::loadGameAutomatic() { + int16 automaticRestoreGameSlotId = 0; + + automaticRestoreGameSlotId = _systemUI->figureOutAutomaticRestoreGameSlot(_game.automaticSaveDescription); + if (automaticRestoreGameSlotId >= 0) { + if (doLoad(automaticRestoreGameSlotId, true) == errOK) { + return true; + } + } + return false; } -int AgiEngine::loadGameSimple() { +bool AgiEngine::loadGameDialog() { + int16 restoreGameSlotId = 0; + if (!ConfMan.getBool("originalsaveload")) return scummVMSaveLoadDialog(false); - else - return doLoad(0, true); + + restoreGameSlotId = _systemUI->askForRestoreGameSlot(); + if (restoreGameSlotId >= 0) { + if (doLoad(restoreGameSlotId, true) == errOK) { + return true; + } + } + return errOK; +} + +// Try to figure out either the slot, that is currently using the automatic saved game description +// or get a new slot. +// If we fail, return false, so that the regular saved game dialog is called +// Original AGI was limited to 12 saves, we are effectively limited to 100 saves at the moment. +// +// btw. this also means that entering an existant name in Mixed Up Mother Goose will effectively overwrite +// that saved game. This is also what original AGI did. +bool AgiEngine::saveGameAutomatic() { + int16 automaticSaveGameSlotId = 0; + + automaticSaveGameSlotId = _systemUI->figureOutAutomaticSaveGameSlot(_game.automaticSaveDescription); + if (automaticSaveGameSlotId >= 0) { + Common::String slotDescription(_game.automaticSaveDescription); + + // WORKAROUND: Remove window in case one is currently shown, otherwise it would get saved in the thumbnail + // Happens for Mixed Up Mother Goose. The scripts close the window after saving. + // Original interpreter obviously did not do this, but original interpreter also did not save thumbnails. + _text->closeWindow(); + + if (doSave(automaticSaveGameSlotId, slotDescription) == errOK) { + return true; + } + } + return false; } +bool AgiEngine::saveGameDialog() { + int16 saveGameSlotId = 0; + Common::String slotDescription; + + if (!ConfMan.getBool("originalsaveload")) + return scummVMSaveLoadDialog(true); + + saveGameSlotId = _systemUI->askForSaveGameSlot(); + if (saveGameSlotId >= 0) { + if (_systemUI->askForSaveGameDescription(saveGameSlotId, slotDescription)) { + if (doSave(saveGameSlotId, slotDescription) == errOK) { + return true; + } + } + } + return false; +} + + void AgiEngine::recordImageStackCall(uint8 type, int16 p1, int16 p2, int16 p3, int16 p4, int16 p5, int16 p6, int16 p7) { ImageStackElement pnew; @@ -1019,11 +967,11 @@ void AgiEngine::replayImageStackCall(uint8 type, int16 p1, int16 p2, int16 p3, switch (type) { case ADD_PIC: debugC(8, kDebugLevelMain, "--- decoding picture %d ---", p1); - agiLoadResource(rPICTURE, p1); + agiLoadResource(RESOURCETYPE_PICTURE, p1); _picture->decodePicture(p1, p2, p3 != 0); break; case ADD_VIEW: - agiLoadResource(rVIEW, p1); + agiLoadResource(RESOURCETYPE_VIEW, p1); _sprites->addToPic(p1, p2, p3, p4, p5, p6, p7); break; } @@ -1041,12 +989,12 @@ void AgiEngine::checkQuickLoad() { if (ConfMan.hasKey("save_slot")) { Common::String saveNameBuffer = getSavegameFilename(ConfMan.getInt("save_slot")); - _sprites->eraseBoth(); + _sprites->eraseSprites(); _sound->stopSound(); if (loadGame(saveNameBuffer, false) == errOK) { // Do not check game id _game.exitAllLogics = 1; - _menu->enableAll(); + _menu->itemEnableAll(); } } } @@ -1054,21 +1002,21 @@ void AgiEngine::checkQuickLoad() { Common::Error AgiEngine::loadGameState(int slot) { Common::String saveLoadSlot = getSavegameFilename(slot); - _sprites->eraseBoth(); + _sprites->eraseSprites(); _sound->stopSound(); if (loadGame(saveLoadSlot) == errOK) { _game.exitAllLogics = 1; - _menu->enableAll(); + _menu->itemEnableAll(); return Common::kNoError; } else { return Common::kUnknownError; } } -Common::Error AgiEngine::saveGameState(int slot, const Common::String &desc) { +Common::Error AgiEngine::saveGameState(int slot, const Common::String &description) { Common::String saveLoadSlot = getSavegameFilename(slot); - if (saveGame(saveLoadSlot, desc) == errOK) + if (saveGame(saveLoadSlot, description) == errOK) return Common::kNoError; else return Common::kUnknownError; -- cgit v1.2.3 From 4bc01ab7d585f6be9d25a96ce9544b95f459d7e6 Mon Sep 17 00:00:00 2001 From: Martin Kiewitz Date: Sun, 31 Jan 2016 17:56:53 +0100 Subject: AGI: getflag/setflag/etc. cleanup renamed getflag() to getFlag() renamed setflag() to setFlag() renamed flipflag() to flipFlag() preagi: renamed setFlag for this engine to setWinnieFlag --- engines/agi/saveload.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'engines/agi/saveload.cpp') diff --git a/engines/agi/saveload.cpp b/engines/agi/saveload.cpp index b11927c542..b6974ee651 100644 --- a/engines/agi/saveload.cpp +++ b/engines/agi/saveload.cpp @@ -134,6 +134,9 @@ int AgiEngine::saveGame(const Common::String &fileName, const Common::String &de out->writeByte(_game.automaticSave); out->write(_game.automaticSaveDescription, 31); + // touch VM_VAR_SECONDS, so that it gets updated + getVar(VM_VAR_SECONDS); + for (i = 0; i < MAX_FLAGS; i++) out->writeByte(_game.flags[i]); for (i = 0; i < MAX_VARS; i++) @@ -666,7 +669,7 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) { delete in; debugC(3, kDebugLevelMain | kDebugLevelSavegame, "Closed %s", fileName.c_str()); - setflag(VM_FLAG_RESTORE_JUST_RAN, true); + setFlag(VM_FLAG_RESTORE_JUST_RAN, true); _game.hasPrompt = 0; // force input line repaint if necessary _words->clearEgoWords(); -- cgit v1.2.3 From fd9c46831df3bcd09bc6f85d5e41c2beb3f7c024 Mon Sep 17 00:00:00 2001 From: Martin Kiewitz Date: Sun, 31 Jan 2016 20:53:36 +0100 Subject: AGI: remove timer hack, implement in game timer in game timer is now updated, when scripts read in game timer VM variables and during main loop. ScummVM total play time feature is used for it. Game cycle syncing is done at the same time. --- engines/agi/saveload.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'engines/agi/saveload.cpp') diff --git a/engines/agi/saveload.cpp b/engines/agi/saveload.cpp index b6974ee651..b47a0cf43f 100644 --- a/engines/agi/saveload.cpp +++ b/engines/agi/saveload.cpp @@ -331,6 +331,7 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) { uint8 t; int16 parm[7]; Common::InSaveFile *in; + bool totalPlayTimeWasSet = false; debugC(3, kDebugLevelMain | kDebugLevelSavegame, "AgiEngine::loadGame(%s)", fileName.c_str()); @@ -380,7 +381,8 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) { in->readUint16BE(); // save time if (saveVersion >= 6) { uint32 playTime = in->readUint32BE(); - g_engine->setTotalPlayTime(playTime * 1000); + inGameTimerReset(playTime * 1000); + totalPlayTimeWasSet = true; } } @@ -439,6 +441,19 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) { for (i = 0; i < MAX_VARS; i++) _game.vars[i] = in->readByte(); + if (!totalPlayTimeWasSet) { + // If we haven't gotten total play time by now, try to calculate it by using VM Variables + // This will happen for at least saves before version 6 + // Direct access because otherwise we would trigger an update to these variables according to ScummVM total play time + byte playTimeSeconds = _game.vars[VM_VAR_SECONDS]; + byte playTimeMinutes = _game.vars[VM_VAR_MINUTES]; + byte playTimeHours = _game.vars[VM_VAR_HOURS]; + byte playTimeDays = _game.vars[VM_VAR_DAYS]; + uint32 playTime = (playTimeSeconds + (playTimeMinutes * 60) + (playTimeHours * 3600) + (playTimeDays * 86400)) * 1000; + + inGameTimerReset(playTime); + } + setVar(VM_VAR_FREE_PAGES, 180); // Set amount of free memory to realistic value (Overwriting the just loaded value) _game.horizon = in->readSint16BE(); -- cgit v1.2.3 From a9b25b53d7a7084941553166abd20a602e83e184 Mon Sep 17 00:00:00 2001 From: Martin Kiewitz Date: Sun, 31 Jan 2016 22:14:35 +0100 Subject: AGI: properly implement volume control + sync Original code did assume that AGI volume level is 0-15 (0 for silence, 15 for maximum volume). It actually is the other way. 0 is maximum, 15 is silence. Fixed that. Also implemented sync with ScummVM settings dialog. In case "mute" is enabled by the user, any volume changes done by scripts are ignored. Fixes Manhunter 1 Apple IIgs not getting sound anymore since the VM Var cleanup (the script volume change by the scripts didn't reach us before) --- engines/agi/saveload.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'engines/agi/saveload.cpp') diff --git a/engines/agi/saveload.cpp b/engines/agi/saveload.cpp index b47a0cf43f..6b1b5495f7 100644 --- a/engines/agi/saveload.cpp +++ b/engines/agi/saveload.cpp @@ -703,6 +703,9 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) { // copy everything over (we should probably only copy over the remaining parts of the screen w/o play screen _gfx->copyDisplayRectToScreen(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT); + // Sync volume settings from ScummVM system settings, so that VM volume variable is overwritten + setVolumeViaSystemSetting(); + return errOK; } -- cgit v1.2.3