diff options
Diffstat (limited to 'engines/fullpipe/stateloader.cpp')
-rw-r--r-- | engines/fullpipe/stateloader.cpp | 291 |
1 files changed, 272 insertions, 19 deletions
diff --git a/engines/fullpipe/stateloader.cpp b/engines/fullpipe/stateloader.cpp index 141196c7d7..adc4fe3731 100644 --- a/engines/fullpipe/stateloader.cpp +++ b/engines/fullpipe/stateloader.cpp @@ -25,6 +25,9 @@ #include "common/file.h" #include "common/array.h" #include "common/list.h" +#include "common/memstream.h" + +#include "graphics/thumbnail.h" #include "fullpipe/objects.h" #include "fullpipe/gameloader.h" @@ -37,6 +40,241 @@ namespace Fullpipe { +bool GameLoader::readSavegame(const char *fname) { + SaveHeader header; + Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(fname); + + if (!saveFile) { + warning("Cannot open save %s for loading", fname); + return false; + } + + header.version = saveFile->readUint32LE(); + saveFile->read(header.magic, 32); + header.updateCounter = saveFile->readUint32LE(); + header.unkField = saveFile->readUint32LE(); + header.encSize = saveFile->readUint32LE(); + + debugC(3, kDebugLoading, "version: %d magic: %s updateCounter: %d unkField: %d encSize: %d, pos: %d", + header.version, header.magic, header.updateCounter, header.unkField, header.encSize, saveFile->pos()); + + if (header.version != 48) + return false; + + _updateCounter = header.updateCounter; + + byte *data = (byte *)malloc(header.encSize); + saveFile->read(data, header.encSize); + + byte *map = (byte *)malloc(800); + saveFile->read(map, 800); + + MfcArchive temp(new Common::MemoryReadStream(map, 800)); + + if (_savegameCallback) + _savegameCallback(&temp, false); + + delete saveFile; + + // Deobfuscate the data + for (int i = 0; i < header.encSize; i++) + data[i] -= i & 0x7f; + + MfcArchive *archive = new MfcArchive(new Common::MemoryReadStream(data, header.encSize)); + + GameVar *var = (GameVar *)archive->readClass(); + + GameVar *v = _gameVar->getSubVarByName("OBJSTATES"); + + if (!v) { + v = _gameVar->addSubVarAsInt("OBJSTATES", 0); + + if (!v) { + warning("No state to save"); + delete archive; + return false; + } + } + + addVar(var, v); + + getGameLoaderInventory()->loadPartial(*archive); + + uint32 arrSize = archive->readUint32LE(); + + debugC(3, kDebugLoading, "Reading %d infos", arrSize); + + for (uint i = 0; i < arrSize; i++) { + _sc2array[i]._picAniInfosCount = archive->readUint32LE(); + + if (_sc2array[i]._picAniInfosCount) + debugC(3, kDebugLoading, "Count %d: %d", i, _sc2array[i]._picAniInfosCount); + + free(_sc2array[i]._picAniInfos); + _sc2array[i]._picAniInfos = (PicAniInfo **)malloc(sizeof(PicAniInfo *) * _sc2array[i]._picAniInfosCount); + + for (int j = 0; j < _sc2array[i]._picAniInfosCount; j++) { + _sc2array[i]._picAniInfos[j] = new PicAniInfo(); + _sc2array[i]._picAniInfos[j]->load(*archive); + } + + _sc2array[i]._isLoaded = 0; + } + + delete archive; + + getGameLoaderInventory()->rebuildItemRects(); + + PreloadItem preloadItem; + + v = _gameVar->getSubVarByName("OBJSTATES")->getSubVarByName("SAVEGAME"); + + if (v) { + if (g_fp->_currentScene) + preloadItem.preloadId1 = g_fp->_currentScene->_sceneId & 0xffff; + else + preloadItem.preloadId1 = 0; + + preloadItem.param = v->getSubVarAsInt("Entrance"); + preloadItem.preloadId2 = 0; + preloadItem.sceneId = v->getSubVarAsInt("Scene"); + + if (_preloadCallback) { + if (!_preloadCallback(preloadItem, 0)) + return false; + } + + clearGlobalMessageQueueList1(); + + if (g_fp->_currentScene) + unloadScene(g_fp->_currentScene->_sceneId); + + g_fp->_currentScene = 0; + + if (_preloadCallback) + _preloadCallback(preloadItem, 50); + + loadScene(preloadItem.sceneId); + + ExCommand *ex = new ExCommand(preloadItem.sceneId, 17, 62, 0, 0, 0, 1, 0, 0, 0); + ex->_excFlags = 2; + ex->_param = preloadItem.param; + + if (_preloadCallback) + _preloadCallback(preloadItem, 100); + + ex->postMessage(); + } + + return true; +} + +void parseSavegameHeader(Fullpipe::FullpipeSavegameHeader &header, SaveStateDescriptor &desc) { + int day = (header.date >> 24) & 0xFF; + int month = (header.date >> 16) & 0xFF; + int year = header.date & 0xFFFF; + desc.setSaveDate(year, month, day); + int hour = (header.time >> 8) & 0xFF; + int minutes = header.time & 0xFF; + desc.setSaveTime(hour, minutes); + desc.setPlayTime(header.playtime * 1000); + + desc.setDescription(header.saveName); +} + +void fillDummyHeader(Fullpipe::FullpipeSavegameHeader &header) { + // This is wrong header, perhaps it is original savegame. Thus fill out dummy values + header.date = (20 << 24) | (9 << 16) | 2016; + header.time = (9 << 8) | 56; + header.playtime = 1000; +} + +bool readSavegameHeader(Common::InSaveFile *in, FullpipeSavegameHeader &header) { + header.thumbnail = NULL; + + uint oldPos = in->pos(); + + in->seek(-4, SEEK_END); + + int headerOffset = in->readUint32LE(); + + // Sanity check + if (headerOffset >= in->pos() || headerOffset == 0) { + in->seek(oldPos, SEEK_SET); // Rewind the file + fillDummyHeader(header); + return false; + } + + in->seek(headerOffset, SEEK_SET); + + in->read(header.id, 6); + + // Validate the header Id + if (strcmp(header.id, "SVMCR")) { + in->seek(oldPos, SEEK_SET); // Rewind the file + fillDummyHeader(header); + return false; + } + + header.version = in->readByte(); + if (header.version != FULLPIPE_SAVEGAME_VERSION) { + in->seek(oldPos, SEEK_SET); // Rewind the file + fillDummyHeader(header); + return false; + } + + header.date = in->readUint32LE(); + header.time = in->readUint16LE(); + header.playtime = in->readUint32LE(); + + // Generate savename + SaveStateDescriptor desc; + + parseSavegameHeader(header, desc); + header.saveName = Common::String::format("%s %s", desc.getSaveDate().c_str(), desc.getSaveTime().c_str()); + + // Get the thumbnail + header.thumbnail = Graphics::loadThumbnail(*in); + + in->seek(oldPos, SEEK_SET); // Rewind the file + + if (!header.thumbnail) + return false; + + return true; +} + +void GameLoader::addVar(GameVar *var, GameVar *subvar) { + if (var && subvar) { + int type = var->_varType; + if (type == subvar->_varType && (!type || type == 1)) + subvar->_value.intValue = var->_value.intValue; + + for (GameVar *v = var->_subVars; v; v = v->_nextVarObj) { + GameVar *nv = subvar->getSubVarByName(v->_varName); + if (!nv) { + nv = new GameVar; + nv->_varName = (char *)calloc(strlen(v->_varName) + 1, 1); + strcpy(nv->_varName, v->_varName); + nv->_varType = v->_varType; + + subvar->addSubVar(nv); + } + + addVar(v, nv); + } + } +} + +void gameLoaderSavegameCallback(MfcArchive *archive, bool mode) { + if (mode) + for (int i = 0; i < 200; i++) + archive->writeUint32LE(g_fp->_mapTable[i]); + else + for (int i = 0; i < 200; i++) + g_fp->_mapTable[i] = archive->readUint32LE(); +} + bool FullpipeEngine::loadGam(const char *fname, int scene) { _gameLoader = new GameLoader(); @@ -50,8 +288,13 @@ bool FullpipeEngine::loadGam(const char *fname, int scene) { addMessageHandlerByIndex(global_messageHandler1, 0, 4); _inventory = getGameLoaderInventory(); - _inventory->setItemFlags(ANI_INV_MAP, 0x10003); - _inventory->addItem(ANI_INV_MAP, 1); + + if (isDemo() && getLanguage() == Common::RU_RUS) { + _inventory->addItem(ANI_INV_HAMMER, 1); + } else { + _inventory->setItemFlags(ANI_INV_MAP, 0x10003); + _inventory->addItem(ANI_INV_MAP, 1); + } _inventory->rebuildItemRects(); @@ -60,7 +303,7 @@ bool FullpipeEngine::loadGam(const char *fname, int scene) { // _sceneSwitcher = sceneSwitcher; // substituted with direct call _gameLoader->_preloadCallback = preloadCallback; - // _readSavegameCallback = gameLoaderReadSavegameCallback; // TODO + _gameLoader->_savegameCallback = gameLoaderSavegameCallback; _aniMan = accessScene(SC_COMMON)->getAniMan(); _scene2 = 0; @@ -81,6 +324,9 @@ bool FullpipeEngine::loadGam(const char *fname, int scene) { setMusicAllowed(_gameLoader->_gameVar->getSubVarAsInt("MUSIC_ALLOWED")); + if (scene == -1) + return true; + if (scene) { _gameLoader->loadScene(726); _gameLoader->gotoScene(726, TrubaLeft); @@ -92,8 +338,13 @@ bool FullpipeEngine::loadGam(const char *fname, int scene) { _gameLoader->loadScene(SC_INTRO1); _gameLoader->gotoScene(SC_INTRO1, TrubaUp); } else { - _gameLoader->loadScene(SC_1); - _gameLoader->gotoScene(SC_1, TrubaLeft); + if (g_fp->isDemo() && g_fp->getLanguage() == Common::RU_RUS) { + _gameLoader->loadScene(SC_9); + _gameLoader->gotoScene(SC_9, TrubaDown); + } else { + _gameLoader->loadScene(SC_1); + _gameLoader->gotoScene(SC_1, TrubaLeft); + } } } @@ -112,7 +363,7 @@ GameProject::GameProject() { } bool GameProject::load(MfcArchive &file) { - debug(5, "GameProject::load()"); + debugC(5, kDebugLoading, "GameProject::load()"); _field_4 = 0; _headerFilename = 0; @@ -124,10 +375,10 @@ bool GameProject::load(MfcArchive &file) { _headerFilename = file.readPascalString(); - debug(1, "_gameProjectVersion = %d", g_fp->_gameProjectVersion); - debug(1, "_pictureScale = %d", g_fp->_pictureScale); - debug(1, "_scrollSpeed = %d", g_fp->_scrollSpeed); - debug(1, "_headerFilename = %s", _headerFilename); + debugC(1, kDebugLoading, "_gameProjectVersion = %d", g_fp->_gameProjectVersion); + debugC(1, kDebugLoading, "_pictureScale = %d", g_fp->_pictureScale); + debugC(1, kDebugLoading, "_scrollSpeed = %d", g_fp->_scrollSpeed); + debugC(1, kDebugLoading, "_headerFilename = %s", _headerFilename); _sceneTagList = new SceneTagList(); @@ -159,6 +410,8 @@ GameVar::GameVar() { _varType = 0; _value.floatValue = 0; _varName = 0; + + _objtype = kObjTypeGameVar; } GameVar::~GameVar() { @@ -205,24 +458,24 @@ bool GameVar::load(MfcArchive &file) { _varName = file.readPascalString(); _varType = file.readUint32LE(); - debugN(6, "[%03d] ", file.getLevel()); + debugCN(6, kDebugLoading, "[%03d] ", file.getLevel()); for (int i = 0; i < file.getLevel(); i++) - debugN(6, " "); + debugCN(6, kDebugLoading, " "); - debugN(6, "<%s>: ", transCyrillic((byte *)_varName)); + debugCN(6, kDebugLoading, "<%s>: ", transCyrillic((byte *)_varName)); switch (_varType) { case 0: _value.intValue = file.readUint32LE(); - debug(6, "d --> %d", _value.intValue); + debugC(6, kDebugLoading, "d --> %d", _value.intValue); break; case 1: _value.intValue = file.readUint32LE(); // FIXME - debug(6, "f --> %f", _value.floatValue); + debugC(6, kDebugLoading, "f --> %f", _value.floatValue); break; case 2: _value.stringValue = file.readPascalString(); - debug(6, "s --> %s", _value.stringValue); + debugC(6, kDebugLoading, "s --> %s", _value.stringValue); break; default: error("Unknown var type: %d (0x%x)", _varType, _varType); @@ -342,7 +595,7 @@ GameVar *GameVar::getSubVarByIndex(int idx) { } bool PicAniInfo::load(MfcArchive &file) { - debug(5, "PicAniInfo::load()"); + debugC(5, kDebugLoading, "PicAniInfo::load()"); type = file.readUint32LE(); objectId = file.readUint16LE(); @@ -350,8 +603,8 @@ bool PicAniInfo::load(MfcArchive &file) { field_8 = file.readUint32LE(); sceneId = file.readUint16LE(); field_E = file.readUint16LE(); - ox = file.readUint32LE(); - oy = file.readUint32LE(); + ox = file.readSint32LE(); + oy = file.readSint32LE(); priority = file.readUint32LE(); staticsId = file.readUint16LE(); movementId = file.readUint16LE(); |