LoadgameResult DMEngine::loadgame(int16 slot) { if (slot == -1 && _newGameFl == k0_modeLoadSavedGame) return kDMLoadgameFailure; bool fadePalette = true; Common::String fileName; Common::SaveFileManager *saveFileManager = nullptr; Common::InSaveFile *file = nullptr; struct { SaveTarget _saveTarget; int32 _saveVersion; OriginalSaveFormat _saveFormat; OriginalSavePlatform _savePlatform; uint16 _dungeonId; } dmSaveHeader; if (_newGameFl) { //L1366_B_FadePalette = !F0428_DIALOG_RequireGameDiskInDrive_NoDialogDrawn(C0_DO_NOT_FORCE_DIALOG_DM_CSB, true); _restartGameAllowed = false; _championMan->_partyChampionCount = 0; _championMan->_leaderHandObject = Thing::_none; } else { fileName = getSavefileName(slot); saveFileManager = _system->getSavefileManager(); file = saveFileManager->openForLoading(fileName); SaveGameHeader header; readSaveGameHeader(file, &header); warning("MISSING CODE: missing check for matching format and platform in save in f435_loadgame"); dmSaveHeader._saveTarget = (SaveTarget)file->readSint32BE(); dmSaveHeader._saveVersion = file->readSint32BE(); dmSaveHeader._saveFormat = (OriginalSaveFormat)file->readSint32BE(); dmSaveHeader._savePlatform = (OriginalSavePlatform)file->readSint32BE(); // Skip _gameId, which was useless file->readSint32BE(); dmSaveHeader._dungeonId = file->readUint16BE(); _gameTime = file->readSint32BE(); // G0349_ul_LastRandomNumber = L1371_s_GlobalData.LastRandomNumber; _championMan->_partyChampionCount = file->readUint16BE(); _dungeonMan->_partyMapX = file->readSint16BE(); _dungeonMan->_partyMapY = file->readSint16BE(); _dungeonMan->_partyDir = (Direction)file->readUint16BE(); _dungeonMan->_partyMapIndex = file->readByte(); _championMan->_leaderIndex = (ChampionIndex)file->readSint16BE(); _championMan->_magicCasterChampionIndex = (ChampionIndex)file->readSint16BE(); _timeline->_eventCount = file->readUint16BE(); _timeline->_firstUnusedEventIndex = file->readUint16BE(); _timeline->_eventMaxCount = file->readUint16BE(); _groupMan->_currActiveGroupCount = file->readUint16BE(); _projexpl->_lastCreatureAttackTime = file->readSint32BE(); _projexpl->_lastPartyMovementTime = file->readSint32BE(); _disabledMovementTicks = file->readSint16BE(); _projectileDisableMovementTicks = file->readSint16BE(); _lastProjectileDisabledMovementDirection = file->readSint16BE(); _championMan->_leaderHandObject = Thing(file->readUint16BE()); _groupMan->_maxActiveGroupCount = file->readUint16BE(); if (!_restartGameRequest) { _timeline->initTimeline(); _groupMan->initActiveGroups(); } _groupMan->loadActiveGroupPart(file); _championMan->loadPartyPart2(file); _timeline->loadEventsPart(file); _timeline->loadTimelinePart(file); // read sentinel uint32 sentinel = file->readUint32BE(); assert(sentinel == 0x6f85e3d3); _dungeonId = dmSaveHeader._dungeonId; } _dungeonMan->loadDungeonFile(file); delete file; if (_newGameFl) { _timeline->initTimeline(); _groupMan->initActiveGroups(); if (fadePalette) { _displayMan->startEndFadeToPalette(_displayMan->_blankBuffer); delay(1); _displayMan->fillScreen(kDMColorBlack); _displayMan->startEndFadeToPalette(_displayMan->_paletteTopAndBottomScreen); } } else { _restartGameAllowed = true; switch (getGameLanguage()) { // localized case Common::DE_DEU: _dialog->dialogDraw(nullptr, "SPIEL WIRD GELADEN . . .", nullptr, nullptr, nullptr, nullptr, true, true, true); break; case Common::FR_FRA: _dialog->dialogDraw(nullptr, "CHARGEMENT DU JEU . . .", nullptr, nullptr, nullptr, nullptr, true, true, true); break; default: _dialog->dialogDraw(nullptr, "LOADING GAME . . .", nullptr, nullptr, nullptr, nullptr, true, true, true); break; } } _championMan->_partyDead = false; return kDMLoadgameSuccess; }
/** * Restore game from supplied slot number */ bool FileManager::restoreGame(const int16 slot) { debugC(1, kDebugFile, "restoreGame(%d)", slot); int16 savegameId; if (slot == -1) { GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Restore game:", "Restore", false); savegameId = dialog->runModalWithCurrentTarget(); delete dialog; } else { savegameId = slot; } if (savegameId < 0) // dialog aborted return false; Common::String savegameFile = _vm->getSavegameFilename(savegameId); Common::SaveFileManager *saveMan = g_system->getSavefileManager(); Common::InSaveFile *in = saveMan->openForLoading(savegameFile); if (!in) return false; // Initialize new-game status _vm->initStatus(); // Check version, can't restore from different versions int saveVersion = in->readByte(); if (saveVersion != kSavegameVersion) { warning("Savegame of incompatible version"); delete in; return false; } // Skip over description int32 saveGameNameSize = in->readSint16BE(); in->skip(saveGameNameSize); Graphics::skipThumbnail(*in); in->skip(6); // Skip date & time // If hero image is currently swapped, swap it back before restore if (_vm->_heroImage != kHeroIndex) _vm->_object->swapImages(kHeroIndex, _vm->_heroImage); _vm->_object->restoreObjects(in); _vm->_heroImage = in->readByte(); // If hero swapped in saved game, swap it byte heroImg = _vm->_heroImage; if (heroImg != kHeroIndex) _vm->_object->swapImages(kHeroIndex, _vm->_heroImage); _vm->_heroImage = heroImg; Status &gameStatus = _vm->getGameStatus(); int score = in->readSint16BE(); _vm->setScore(score); gameStatus._storyModeFl = (in->readByte() == 1); _vm->_mouse->setJumpExitFl(in->readByte() == 1); gameStatus._gameOverFl = (in->readByte() == 1); for (int i = 0; i < _vm->_numStates; i++) _vm->_screenStates[i] = in->readByte(); _vm->_scheduler->restoreSchedulerData(in); // Restore palette and change it if necessary _vm->_screen->restorePal(in); // Restore maze status _vm->_maze._enabledFl = (in->readByte() == 1); _vm->_maze._size = in->readByte(); _vm->_maze._x1 = in->readSint16BE(); _vm->_maze._y1 = in->readSint16BE(); _vm->_maze._x2 = in->readSint16BE(); _vm->_maze._y2 = in->readSint16BE(); _vm->_maze._x3 = in->readSint16BE(); _vm->_maze._x4 = in->readSint16BE(); _vm->_maze._firstScreenIndex = in->readByte(); _vm->_scheduler->restoreScreen(*_vm->_screenPtr); if ((_vm->getGameStatus()._viewState = (Vstate) in->readByte()) != kViewPlay) _vm->_screen->hideCursor(); delete in; return true; }
int AgiEngine::loadGame(const Common::String &fileName, bool checkId) { char description[31], saveVersion, loadId[8]; int i, vtEntries = MAX_VIEWTABLE; uint8 t; int16 parm[7]; Common::InSaveFile *in; debugC(3, kDebugLevelMain | kDebugLevelSavegame, "AgiEngine::loadGame(%s)", fileName.c_str()); if (!(in = _saveFileMan->openForLoading(fileName))) { warning("Can't open file '%s', game not loaded", fileName.c_str()); return errBadFileOpen; } else { debugC(3, kDebugLevelMain | kDebugLevelSavegame, "Successfully opened %s for reading", fileName.c_str()); } uint32 typea = in->readUint32BE(); if (typea == AGIflag) { debugC(6, kDebugLevelMain | kDebugLevelSavegame, "Has AGI flag, good start"); } else { warning("This doesn't appear to be an AGI savegame, game not restored"); delete in; return errOK; } in->read(description, 31); 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); 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 >= 4) { // We don't need the thumbnail here, so just read it and discard it Graphics::skipThumbnail(*in); in->readUint32BE(); // save date in->readUint16BE(); // save time // TODO: played time } _game.state = (State)in->readByte(); in->read(loadId, 8); if (strcmp(loadId, _game.id) && checkId) { delete in; warning("This save seems to be from a different AGI game (save from %s, running %s), not loaded", loadId, _game.id); return errBadFileOpen; } strncpy(_game.id, loadId, 8); if (saveVersion >= 5) { char md5[32 + 1]; for (i = 0; i < 32; i++) { md5[i] = in->readByte(); } md5[i] = 0; // terminate // As noted above in AgiEngine::saveGame the MD5 sum field may be all zero // when the save was made via a fallback matched game. In this case we will // replace the MD5 sum with a nicer string, so that the user can easily see // this fact in the debug output. The string saved in "md5" will never match // any valid MD5 sum, thus it is safe to do that here. if (md5[0] == 0) strcpy(md5, "fallback matched"); debug(0, "Saved game MD5: \"%s\"", md5); if (!getGameMD5()) { warning("Since your game was only detected via the fallback detector, there is no possibility to assure the save is compatible with your game version"); debug(0, "The game used for saving is \"%s\".", md5); } else if (strcmp(md5, getGameMD5())) { warning("Game was saved with different gamedata - you may encounter problems"); debug(0, "Your game is \"%s\" and save is \"%s\".", getGameMD5(), md5); } } 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) _game.horizon = in->readSint16BE(); _game.lineStatus = in->readSint16BE(); _game.lineUserInput = in->readSint16BE(); _game.lineMinPrint = in->readSint16BE(); // These are never saved _game.cursorPos = 0; _game.inputBuffer[0] = 0; _game.echoBuffer[0] = 0; _game.keypress = 0; _game.inputMode = (InputMode)in->readSint16BE(); _game.lognum = in->readSint16BE(); _game.playerControl = in->readSint16BE(); if (in->readSint16BE()) quitGame(); _game.statusLine = in->readSint16BE(); _game.clockEnabled = in->readSint16BE(); _game.exitAllLogics = in->readSint16BE(); _game.pictureShown = in->readSint16BE(); _game.hasPrompt = in->readSint16BE(); _game.gameFlags = in->readSint16BE(); _game.inputEnabled = in->readSint16BE(); for (i = 0; i < _HEIGHT; i++) _game.priTable[i] = in->readByte(); if (_game.hasWindow) 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(); // game.hires - rebuilt from image stack // game.sbuf - rebuilt from image stack // game.ego_words - fixed by clean_input // game.num_ego_words - fixed by clean_input _game.numObjects = in->readSint16BE(); for (i = 0; i < (int16)_game.numObjects; i++) objectSetLocation(i, in->readSint16BE()); // Those are not serialized for (i = 0; i < MAX_DIRS; i++) { _game.controllerOccured[i] = false; } for (i = 0; i < MAX_STRINGS; i++) in->read(_game.strings[i], MAX_STRINGLEN); for (i = 0; i < MAX_DIRS; i++) { if (in->readByte() & RES_LOADED) agiLoadResource(rLOGIC, i); else agiUnloadResource(rLOGIC, i); _game.logics[i].sIP = in->readSint16BE(); _game.logics[i].cIP = in->readSint16BE(); } for (i = 0; i < MAX_DIRS; i++) { if (in->readByte() & RES_LOADED) agiLoadResource(rPICTURE, i); else agiUnloadResource(rPICTURE, i); } for (i = 0; i < MAX_DIRS; i++) { if (in->readByte() & RES_LOADED) agiLoadResource(rVIEW, i); else agiUnloadResource(rVIEW, i); } for (i = 0; i < MAX_DIRS; i++) { if (in->readByte() & RES_LOADED) agiLoadResource(rSOUND, i); else agiUnloadResource(rSOUND, i); } // game.pictures - loaded above // game.logics - loaded above // game.views - loaded above // 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(); } for (i = vtEntries; i < MAX_VIEWTABLE; i++) { memset(&_game.viewTable[i], 0, sizeof(VtEntry)); } // Fix some pointers in viewtable for (i = 0; i < MAX_VIEWTABLE; i++) { VtEntry *v = &_game.viewTable[i]; if (_game.dirView[v->currentView].offset == _EMPTY) continue; if (!(_game.dirView[v->currentView].flags & RES_LOADED)) agiLoadResource(rVIEW, v->currentView); 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... } _sprites->eraseBoth(); // Clear input line _gfx->clearScreen(0); writeStatus(); // Recreate background from saved image stack clearImageStack(); while ((t = in->readByte()) != 0) { for (i = 0; i < 7; i++) parm[i] = in->readSint16BE(); replayImageStackCall(t, parm[0], parm[1], parm[2], parm[3], parm[4], parm[5], parm[6]); } // Load AGIPAL Data if (saveVersion >= 3) _gfx->setAGIPal(in->readSint16BE()); delete in; debugC(3, kDebugLevelMain | kDebugLevelSavegame, "Closed %s", fileName.c_str()); setflag(fRestoreJustRan, true); _game.hasPrompt = 0; // force input line repaint if necessary cleanInput(); _sprites->eraseBoth(); _sprites->blitBoth(); _sprites->commitBoth(); _picture->showPic(); _gfx->doUpdate(); return errOK; }