From 162634cc95e5a2a86ae1611d69024075dfdad450 Mon Sep 17 00:00:00 2001 From: Filippos Karapetis Date: Thu, 13 Nov 2008 08:59:17 +0000 Subject: Started moving Save/Load code to saveload.cpp/.h svn-id: r35033 --- engines/cine/anim.cpp | 71 --- engines/cine/anim.h | 53 --- engines/cine/bg_list.h | 1 - engines/cine/module.mk | 1 + engines/cine/saveload.cpp | 1048 +++++++++++++++++++++++++++++++++++++++++++++ engines/cine/saveload.h | 89 ++++ engines/cine/various.cpp | 942 ---------------------------------------- engines/cine/various.h | 8 + 8 files changed, 1146 insertions(+), 1067 deletions(-) create mode 100644 engines/cine/saveload.cpp create mode 100644 engines/cine/saveload.h diff --git a/engines/cine/anim.cpp b/engines/cine/anim.cpp index f5cde579e6..a0f998e02f 100644 --- a/engines/cine/anim.cpp +++ b/engines/cine/anim.cpp @@ -776,75 +776,4 @@ int loadResource(const char *resourceName, int16 idx) { return result; } -/*! \brief Load animDataTable from save - * \param fHandle Savefile open for reading - * \param saveGameFormat The used savegame format - * \todo Add Operation Stealth savefile support - * - * Unlike the old code, this one actually rebuilds the table one frame - * at a time. - */ -void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGameFormat saveGameFormat) { - int16 currentAnim, foundFileIdx, frame; - char *animName, part[256], name[10]; - uint16 width, height, bpp, var1; - - strcpy(part, currentPartName); - - // We only support these variations of the savegame format at the moment. - assert(saveGameFormat == ANIMSIZE_23 || saveGameFormat == ANIMSIZE_30_PTRS_INTACT); - - const int entrySize = ((saveGameFormat == ANIMSIZE_23) ? 23 : 30); - const int fileStartPos = fHandle.pos(); - currentAnim = 0; - while (currentAnim < NUM_MAX_ANIMDATA) { - // Seek to the start of the current animation's entry - fHandle.seek(fileStartPos + currentAnim * entrySize); - // Read in the current animation entry - width = fHandle.readUint16BE(); - var1 = fHandle.readUint16BE(); - bpp = fHandle.readUint16BE(); - height = fHandle.readUint16BE(); - - bool validPtr = false; - // Handle variables only present in animation entries of size 30 - if (entrySize == 30) { - validPtr = (fHandle.readUint32BE() != 0); // Read data pointer - fHandle.readUint32BE(); // Discard mask pointer - } - - foundFileIdx = fHandle.readSint16BE(); - frame = fHandle.readSint16BE(); - fHandle.read(name, 10); - - // Handle variables only present in animation entries of size 23 - if (entrySize == 23) { - validPtr = (fHandle.readByte() != 0); - } - - // Don't try to load invalid entries. - if (foundFileIdx < 0 || !validPtr) { - currentAnim++; // Jump over the invalid entry - continue; - } - - // Alright, the animation entry looks to be valid so let's start handling it... - if (strcmp(currentPartName, name)) { - closePart(); - loadPart(name); - } - - animName = partBuffer[foundFileIdx].partName; - loadRelatedPalette(animName); // Is this for Future Wars only? - const int16 prevAnim = currentAnim; - currentAnim = loadResource(animName, currentAnim); - assert(currentAnim > prevAnim); // Make sure we advance forward - } - - loadPart(part); - - // Make sure we jump over all the animation entries - fHandle.seek(fileStartPos + NUM_MAX_ANIMDATA * entrySize); -} - } // End of namespace Cine diff --git a/engines/cine/anim.h b/engines/cine/anim.h index 8b1541eb3f..f0d3b67aac 100644 --- a/engines/cine/anim.h +++ b/engines/cine/anim.h @@ -30,59 +30,6 @@ namespace Cine { -/** - * Cine engine's save game formats. - * Enumeration entries (Excluding the one used as an error) - * are sorted according to age (i.e. top one is oldest, last one newest etc). - * - * ANIMSIZE_UNKNOWN: - * - Animation data entry size is unknown (Used as an error). - * - * ANIMSIZE_23: - * - Animation data entry size is 23 bytes. - * - Used at least by 0.11.0 and 0.11.1 releases of ScummVM. - * - Introduced in revision 21772, stopped using in revision 31444. - * - * ANIMSIZE_30_PTRS_BROKEN: - * - Animation data entry size is 30 bytes. - * - Data and mask pointers in the saved structs are always NULL. - * - Introduced in revision 31453, stopped using in revision 32073. - * - * ANIMSIZE_30_PTRS_INTACT: - * - Animation data entry size is 30 bytes. - * - Data and mask pointers in the saved structs are intact, - * so you can test them for equality or inequality with NULL - * but don't try using them for anything else, it won't work. - * - Introduced in revision 31444, got broken in revision 31453, - * got fixed in revision 32073 and used after that. - * - * TEMP_OS_FORMAT: - * - Temporary Operation Stealth savegame format. - * - NOT backward compatible and NOT to be supported in the future. - * This format should ONLY be used during development and abandoned - * later in favor of a better format! - */ -enum CineSaveGameFormat { - ANIMSIZE_UNKNOWN, - ANIMSIZE_23, - ANIMSIZE_30_PTRS_BROKEN, - ANIMSIZE_30_PTRS_INTACT, - TEMP_OS_FORMAT -}; - -/** Identifier for the temporary Operation Stealth savegame format. */ -static const uint32 TEMP_OS_FORMAT_ID = MKID_BE('TEMP'); - -/** The current version number of Operation Stealth's savegame format. */ -static const uint32 CURRENT_OS_SAVE_VER = 0; - -/** Chunk header used by the temporary Operation Stealth savegame format. */ -struct ChunkHeader { - uint32 id; ///< Identifier (e.g. MKID_BE('TEMP')) - uint32 version; ///< Version number - uint32 size; ///< Size of the chunk after this header in bytes -}; - struct AnimHeaderStruct { byte field_0; byte field_1; diff --git a/engines/cine/bg_list.h b/engines/cine/bg_list.h index 9a402baee8..f6c73ea9a3 100644 --- a/engines/cine/bg_list.h +++ b/engines/cine/bg_list.h @@ -28,7 +28,6 @@ #include "common/scummsys.h" -#include "common/savefile.h" #include "common/list.h" namespace Cine { diff --git a/engines/cine/module.mk b/engines/cine/module.mk index 131709e2ab..8382dc69df 100644 --- a/engines/cine/module.mk +++ b/engines/cine/module.mk @@ -14,6 +14,7 @@ MODULE_OBJS = \ part.o \ prc.o \ rel.o \ + saveload.o \ script_fw.o \ script_os.o \ sound.o \ diff --git a/engines/cine/saveload.cpp b/engines/cine/saveload.cpp new file mode 100644 index 0000000000..75581565e2 --- /dev/null +++ b/engines/cine/saveload.cpp @@ -0,0 +1,1048 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/savefile.h" + +#include "cine/cine.h" +#include "cine/bg_list.h" +#include "cine/saveload.h" +#include "cine/various.h" + +namespace Cine { + +int16 currentDisk; +int16 saveVar2; + + +bool writeChunkHeader(Common::OutSaveFile &out, const ChunkHeader &header) { + out.writeUint32BE(header.id); + out.writeUint32BE(header.version); + out.writeUint32BE(header.size); + return !out.ioFailed(); +} + +bool loadChunkHeader(Common::SeekableReadStream &in, ChunkHeader &header) { + header.id = in.readUint32BE(); + header.version = in.readUint32BE(); + header.size = in.readUint32BE(); + return !in.ioFailed(); +} + +/*! \brief Savegame format detector + * \param fHandle Savefile to check + * \return Savegame format on success, ANIMSIZE_UNKNOWN on failure + * + * This function seeks through the savefile and tries to determine the + * savegame format it uses. There's a miniscule chance that the detection + * algorithm could get confused and think that the file uses both the older + * and the newer format but that is such a remote possibility that I wouldn't + * worry about it at all. + * + * Also detects the temporary Operation Stealth savegame format now. + */ +enum CineSaveGameFormat detectSaveGameFormat(Common::SeekableReadStream &fHandle) { + const uint32 prevStreamPos = fHandle.pos(); + + // First check for the temporary Operation Stealth savegame format. + fHandle.seek(0); + ChunkHeader hdr; + loadChunkHeader(fHandle, hdr); + fHandle.seek(prevStreamPos); + if (hdr.id == TEMP_OS_FORMAT_ID) { + return TEMP_OS_FORMAT; + } + + // Ok, so the savegame isn't using the temporary Operation Stealth savegame format. + // Let's check for the plain Future Wars savegame format and its different versions then. + // The animDataTable begins at savefile position 0x2315. + // Each animDataTable entry takes 23 bytes in older saves (Revisions 21772-31443) + // and 30 bytes in the save format after that (Revision 31444 and onwards). + // There are 255 entries in the animDataTable in both of the savefile formats. + static const uint animDataTableStart = 0x2315; + static const uint animEntriesCount = 255; + static const uint oldAnimEntrySize = 23; + static const uint newAnimEntrySize = 30; + static const uint animEntrySizeChoices[] = {oldAnimEntrySize, newAnimEntrySize}; + Common::Array animEntrySizeMatches; + + // Try to walk through the savefile using different animDataTable entry sizes + // and make a list of all the successful entry sizes. + for (uint i = 0; i < ARRAYSIZE(animEntrySizeChoices); i++) { + // 206 = 2 * 50 * 2 + 2 * 3 (Size of global and object script entries) + // 20 = 4 * 2 + 2 * 6 (Size of overlay and background incrust entries) + static const uint sizeofScreenParams = 2 * 6; + static const uint globalScriptEntrySize = 206; + static const uint objectScriptEntrySize = 206; + static const uint overlayEntrySize = 20; + static const uint bgIncrustEntrySize = 20; + static const uint chainEntrySizes[] = { + globalScriptEntrySize, + objectScriptEntrySize, + overlayEntrySize, + bgIncrustEntrySize + }; + + uint animEntrySize = animEntrySizeChoices[i]; + // Jump over the animDataTable entries and the screen parameters + int32 newPos = animDataTableStart + animEntrySize * animEntriesCount + sizeofScreenParams; + // Check that there's data left after the point we're going to jump to + if (newPos >= fHandle.size()) { + continue; + } + fHandle.seek(newPos); + + // Jump over the remaining items in the savegame file + // (i.e. the global scripts, object scripts, overlays and background incrusts). + bool chainWalkSuccess = true; + for (uint chainIndex = 0; chainIndex < ARRAYSIZE(chainEntrySizes); chainIndex++) { + // Read entry count and jump over the entries + int entryCount = fHandle.readSint16BE(); + newPos = fHandle.pos() + chainEntrySizes[chainIndex] * entryCount; + // Check that we didn't go past the end of file. + // Note that getting exactly to the end of file is acceptable. + if (newPos > fHandle.size()) { + chainWalkSuccess = false; + break; + } + fHandle.seek(newPos); + } + + // If we could walk the chain successfully and + // got exactly to the end of file then we've got a match. + if (chainWalkSuccess && fHandle.pos() == fHandle.size()) { + // We found a match, let's save it + animEntrySizeMatches.push_back(animEntrySize); + } + } + + // Check that we got only one entry size match. + // If we didn't, then return an error. + enum CineSaveGameFormat result = ANIMSIZE_UNKNOWN; + if (animEntrySizeMatches.size() == 1) { + const uint animEntrySize = animEntrySizeMatches[0]; + assert(animEntrySize == oldAnimEntrySize || animEntrySize == newAnimEntrySize); + if (animEntrySize == oldAnimEntrySize) { + result = ANIMSIZE_23; + } else { // animEntrySize == newAnimEntrySize + // Check data and mask pointers in all of the animDataTable entries + // to see whether we've got the version with the broken data and mask pointers or not. + // In the broken format all data and mask pointers were always zero. + static const uint relativeDataPos = 2 * 4; + bool pointersIntact = false; + for (uint i = 0; i < animEntriesCount; i++) { + fHandle.seek(animDataTableStart + i * animEntrySize + relativeDataPos); + uint32 data = fHandle.readUint32BE(); + uint32 mask = fHandle.readUint32BE(); + if ((data != 0) || (mask != 0)) { + pointersIntact = true; + break; + } + } + result = (pointersIntact ? ANIMSIZE_30_PTRS_INTACT : ANIMSIZE_30_PTRS_BROKEN); + } + } else if (animEntrySizeMatches.size() > 1) { + warning("Savegame format detector got confused by input data. Detecting savegame to be using an unknown format"); + } else { // animEtrySizeMatches.size() == 0 + debug(3, "Savegame format detector was unable to detect savegame's format"); + } + + fHandle.seek(prevStreamPos); + return result; +} + +/*! \brief Restore script list item from savefile + * \param fHandle Savefile handle open for reading + * \param isGlobal Restore object or global script? + */ +void loadScriptFromSave(Common::SeekableReadStream &fHandle, bool isGlobal) { + ScriptVars localVars, labels; + uint16 compare, pos; + int16 idx; + + labels.load(fHandle); + localVars.load(fHandle); + + compare = fHandle.readUint16BE(); + pos = fHandle.readUint16BE(); + idx = fHandle.readUint16BE(); + + // no way to reinitialize these + if (idx < 0) { + return; + } + + // original code loaded everything into globalScripts, this should be + // the correct behavior + if (isGlobal) { + ScriptPtr tmp(scriptInfo->create(*scriptTable[idx], idx, labels, localVars, compare, pos)); + assert(tmp); + globalScripts.push_back(tmp); + } else { + ScriptPtr tmp(scriptInfo->create(*relTable[idx], idx, labels, localVars, compare, pos)); + assert(tmp); + objectScripts.push_back(tmp); + } +} + +/*! \brief Restore overlay sprites from savefile + * \param fHandle Savefile open for reading + */ +void loadOverlayFromSave(Common::SeekableReadStream &fHandle) { + overlay tmp; + + fHandle.readUint32BE(); + fHandle.readUint32BE(); + + tmp.objIdx = fHandle.readUint16BE(); + tmp.type = fHandle.readUint16BE(); + tmp.x = fHandle.readSint16BE(); + tmp.y = fHandle.readSint16BE(); + tmp.width = fHandle.readSint16BE(); + tmp.color = fHandle.readSint16BE(); + + overlayList.push_back(tmp); +} + +bool loadObjectTable(Common::SeekableReadStream &in) { + in.readUint16BE(); // Entry count + in.readUint16BE(); // Entry size + + for (int i = 0; i < NUM_MAX_OBJECT; i++) { + objectTable[i].x = in.readSint16BE(); + objectTable[i].y = in.readSint16BE(); + objectTable[i].mask = in.readUint16BE(); + objectTable[i].frame = in.readSint16BE(); + objectTable[i].costume = in.readSint16BE(); + in.read(objectTable[i].name, 20); + objectTable[i].part = in.readUint16BE(); + } + return !in.ioFailed(); +} + +bool loadZoneData(Common::SeekableReadStream &in) { + for (int i = 0; i < 16; i++) { + zoneData[i] = in.readUint16BE(); + } + return !in.ioFailed(); +} + +bool loadCommandVariables(Common::SeekableReadStream &in) { + for (int i = 0; i < 4; i++) { + commandVar3[i] = in.readUint16BE(); + } + return !in.ioFailed(); +} + +bool loadScreenParams(Common::SeekableReadStream &in) { + // TODO: handle screen params (really required ?) + in.readUint16BE(); + in.readUint16BE(); + in.readUint16BE(); + in.readUint16BE(); + in.readUint16BE(); + in.readUint16BE(); + return !in.ioFailed(); +} + +bool loadGlobalScripts(Common::SeekableReadStream &in) { + int size = in.readSint16BE(); + for (int i = 0; i < size; i++) { + loadScriptFromSave(in, true); + } + return !in.ioFailed(); +} + +bool loadObjectScripts(Common::SeekableReadStream &in) { + int size = in.readSint16BE(); + for (int i = 0; i < size; i++) { + loadScriptFromSave(in, false); + } + return !in.ioFailed(); +} + +bool loadOverlayList(Common::SeekableReadStream &in) { + int size = in.readSint16BE(); + for (int i = 0; i < size; i++) { + loadOverlayFromSave(in); + } + return !in.ioFailed(); +} + +bool loadSeqList(Common::SeekableReadStream &in) { + uint size = in.readUint16BE(); + SeqListElement tmp; + for (uint i = 0; i < size; i++) { + tmp.var4 = in.readSint16BE(); + tmp.objIdx = in.readUint16BE(); + tmp.var8 = in.readSint16BE(); + tmp.frame = in.readSint16BE(); + tmp.varC = in.readSint16BE(); + tmp.varE = in.readSint16BE(); + tmp.var10 = in.readSint16BE(); + tmp.var12 = in.readSint16BE(); + tmp.var14 = in.readSint16BE(); + tmp.var16 = in.readSint16BE(); + tmp.var18 = in.readSint16BE(); + tmp.var1A = in.readSint16BE(); + tmp.var1C = in.readSint16BE(); + tmp.var1E = in.readSint16BE(); + seqList.push_back(tmp); + } + return !in.ioFailed(); +} + +bool loadZoneQuery(Common::SeekableReadStream &in) { + for (int i = 0; i < 16; i++) { + zoneQuery[i] = in.readUint16BE(); + } + return !in.ioFailed(); +} + +void saveObjectTable(Common::OutSaveFile &out) { + out.writeUint16BE(NUM_MAX_OBJECT); // Entry count + out.writeUint16BE(0x20); // Entry size + + for (int i = 0; i < NUM_MAX_OBJECT; i++) { + out.writeUint16BE(objectTable[i].x); + out.writeUint16BE(objectTable[i].y); + out.writeUint16BE(objectTable[i].mask); + out.writeUint16BE(objectTable[i].frame); + out.writeUint16BE(objectTable[i].costume); + out.write(objectTable[i].name, 20); + out.writeUint16BE(objectTable[i].part); + } +} + +void saveZoneData(Common::OutSaveFile &out) { + for (int i = 0; i < 16; i++) { + out.writeUint16BE(zoneData[i]); + } +} + +void saveCommandVariables(Common::OutSaveFile &out) { + for (int i = 0; i < 4; i++) { + out.writeUint16BE(commandVar3[i]); + } +} + +/*! \brief Save the 80 bytes long command buffer padded to that length with zeroes. */ +void saveCommandBuffer(Common::OutSaveFile &out) { + // Let's make sure there's space for the trailing zero + // (That's why we subtract one from the maximum command buffer size here). + uint32 size = MIN(commandBuffer.size(), kMaxCommandBufferSize - 1); + out.write(commandBuffer.c_str(), size); + // Write the rest as zeroes (Here we also write the string's trailing zero) + for (uint i = 0; i < kMaxCommandBufferSize - size; i++) { + out.writeByte(0); + } +} + +void saveAnimDataTable(Common::OutSaveFile &out) { + out.writeUint16BE(NUM_MAX_ANIMDATA); // Entry count + out.writeUint16BE(0x1E); // Entry size + + for (int i = 0; i < NUM_MAX_ANIMDATA; i++) { + animDataTable[i].save(out); + } +} + +void saveScreenParams(Common::OutSaveFile &out) { + // Screen parameters, unhandled + out.writeUint16BE(0); + out.writeUint16BE(0); + out.writeUint16BE(0); + out.writeUint16BE(0); + out.writeUint16BE(0); + out.writeUint16BE(0); +} + +void saveGlobalScripts(Common::OutSaveFile &out) { + ScriptList::const_iterator it; + out.writeUint16BE(globalScripts.size()); + for (it = globalScripts.begin(); it != globalScripts.end(); ++it) { + (*it)->save(out); + } +} + +void saveObjectScripts(Common::OutSaveFile &out) { + ScriptList::const_iterator it; + out.writeUint16BE(objectScripts.size()); + for (it = objectScripts.begin(); it != objectScripts.end(); ++it) { + (*it)->save(out); + } +} + +void saveOverlayList(Common::OutSaveFile &out) { + Common::List::const_iterator it; + + out.writeUint16BE(overlayList.size()); + + for (it = overlayList.begin(); it != overlayList.end(); ++it) { + out.writeUint32BE(0); // next + out.writeUint32BE(0); // previous? + out.writeUint16BE(it->objIdx); + out.writeUint16BE(it->type); + out.writeSint16BE(it->x); + out.writeSint16BE(it->y); + out.writeSint16BE(it->width); + out.writeSint16BE(it->color); + } +} + +void saveBgIncrustList(Common::OutSaveFile &out) { + Common::List::const_iterator it; + out.writeUint16BE(bgIncrustList.size()); + + for (it = bgIncrustList.begin(); it != bgIncrustList.end(); ++it) { + out.writeUint32BE(0); // next + out.writeUint32BE(0); // previous? + out.writeUint16BE(it->objIdx); + out.writeUint16BE(it->param); + out.writeUint16BE(it->x); + out.writeUint16BE(it->y); + out.writeUint16BE(it->frame); + out.writeUint16BE(it->part); + } +} + +void saveZoneQuery(Common::OutSaveFile &out) { + for (int i = 0; i < 16; i++) { + out.writeUint16BE(zoneQuery[i]); + } +} + +void saveSeqList(Common::OutSaveFile &out) { + Common::List::const_iterator it; + out.writeUint16BE(seqList.size()); + + for (it = seqList.begin(); it != seqList.end(); ++it) { + out.writeSint16BE(it->var4); + out.writeUint16BE(it->objIdx); + out.writeSint16BE(it->var8); + out.writeSint16BE(it->frame); + out.writeSint16BE(it->varC); + out.writeSint16BE(it->varE); + out.writeSint16BE(it->var10); + out.writeSint16BE(it->var12); + out.writeSint16BE(it->var14); + out.writeSint16BE(it->var16); + out.writeSint16BE(it->var18); + out.writeSint16BE(it->var1A); + out.writeSint16BE(it->var1C); + out.writeSint16BE(it->var1E); + } +} + +bool CineEngine::loadSaveDirectory(void) { + Common::InSaveFile *fHandle; + char tmp[80]; + + snprintf(tmp, 80, "%s.dir", _targetName.c_str()); + fHandle = g_saveFileMan->openForLoading(tmp); + + if (!fHandle) { + return false; + } + + fHandle->read(currentSaveName, 10 * 20); + delete fHandle; + + return true; +} + +bool CineEngine::loadTempSaveOS(Common::SeekableReadStream &in) { + char musicName[13]; + char bgNames[8][13]; + + // First check the temporary Operation Stealth savegame format header. + ChunkHeader hdr; + loadChunkHeader(in, hdr); + if (hdr.id != TEMP_OS_FORMAT_ID) { + warning("loadTempSaveOS: File has incorrect identifier. Not loading savegame"); + return false; + } else if (hdr.version > CURRENT_OS_SAVE_VER) { + warning("loadTempSaveOS: Detected newer format version. Not loading savegame"); + return false; + } else if ((int)hdr.version < (int)CURRENT_OS_SAVE_VER) { + warning("loadTempSaveOS: Detected older format version. Trying to load nonetheless. Things may break"); + } else { // hdr.id == TEMP_OS_FORMAT_ID && hdr.version == CURRENT_OS_SAVE_VER + debug(3, "loadTempSaveOS: Found correct header (Both the identifier and version number match)."); + } + + // There shouldn't be any data in the header's chunk currently so it's an error if there is. + if (hdr.size > 0) { + warning("loadTempSaveOS: Format header's chunk seems to contain data so format is incorrect. Not loading savegame"); + return false; + } + + // Ok, so we've got a correct header for a temporary Operation Stealth savegame. + // Let's start loading the plain savegame data then. + currentDisk = in.readUint16BE(); + in.read(currentPartName, 13); + in.read(currentPrcName, 13); + in.read(currentRelName, 13); + in.read(currentMsgName, 13); + + // Load the 8 background names. + for (uint i = 0; i < 8; i++) { + in.read(bgNames[i], 13); + } + + in.read(currentCtName, 13); + + // Moved the loading of current procedure, relation, + // backgrounds and Ct here because if they were at the + // end of this function then the global scripts loading + // made an array out of bounds access. In the original + // game's disassembly these aren't here but at the end. + // The difference is probably in how we handle loading + // the global scripts and some other things (i.e. the + // loading routines aren't exactly the same and subtle + // semantic differences result in having to do things + // in a different order). + { + // Not sure if this is needed with Operation Stealth... + checkDataDisk(currentDisk); + + if (strlen(currentPrcName)) { + loadPrc(currentPrcName); + } + + if (strlen(currentRelName)) { + loadRel(currentRelName); + } + + // Load first background (Uses loadBg) + if (strlen(bgNames[0])) { + loadBg(bgNames[0]); + } + + // Add backgrounds 1-7 (Uses addBackground) + for (int i = 1; i < 8; i++) { + if (strlen(bgNames[i])) { + addBackground(bgNames[i], i); + } + } + + if (strlen(currentCtName)) { + loadCtOS(currentCtName); + } + } + + loadObjectTable(in); + renderer->restorePalette(in); + globalVars.load(in, NUM_MAX_VAR); + loadZoneData(in); + loadCommandVariables(in); + char tempCommandBuffer[kMaxCommandBufferSize]; + in.read(tempCommandBuffer, kMaxCommandBufferSize); + commandBuffer = tempCommandBuffer; + renderer->setCommand(commandBuffer); + loadZoneQuery(in); + + // TODO: Use the loaded string (Current music name (String, 13 bytes)). + in.read(musicName, 13); + + // TODO: Use the loaded value (Is music loaded? (Uint16BE, Boolean)). + in.readUint16BE(); + + // TODO: Use the loaded value (Is music playing? (Uint16BE, Boolean)). + in.readUint16BE(); + + renderer->_cmdY = in.readUint16BE(); + in.readUint16BE(); // Some unknown variable that seems to always be zero + allowPlayerInput = in.readUint16BE(); + playerCommand = in.readUint16BE(); + commandVar1 = in.readUint16BE(); + isDrawCommandEnabled = in.readUint16BE(); + var5 = in.readUint16BE(); + var4 = in.readUint16BE(); + var3 = in.readUint16BE(); + var2 = in.readUint16BE(); + commandVar2 = in.readUint16BE(); + renderer->_messageBg = in.readUint16BE(); + + // TODO: Use the loaded value (adBgVar1 (Uint16BE)). + in.readUint16BE(); + + currentAdditionalBgIdx = in.readSint16BE(); + currentAdditionalBgIdx2 = in.readSint16BE(); + + // TODO: Check whether the scroll value really gets used correctly after this. + // Note that the backgrounds are loaded only later than this value is set. + renderer->setScroll(in.readUint16BE()); + + // TODO: Use the loaded value (adBgVar0 (Uint16BE). Maybe this means bgVar0?). + in.readUint16BE(); + + disableSystemMenu = in.readUint16BE(); + + // TODO: adBgVar1 = 1 here + + // Load the animDataTable entries + in.readUint16BE(); // Entry count (255 in the PC version of Operation Stealth). + in.readUint16BE(); // Entry size (36 in the PC version of Operation Stealth). + loadResourcesFromSave(in, ANIMSIZE_30_PTRS_INTACT); + + loadScreenParams(in); + loadGlobalScripts(in); + loadObjectScripts(in); + loadSeqList(in); + loadOverlayList(in); + loadBgIncrustFromSave(in); + + // Left this here instead of moving it earlier in this function with + // the other current value loadings (e.g. loading of current procedure, + // current backgrounds etc). Mostly emulating the way we've handled + // Future Wars savegames and hoping that things work out. + if (strlen(currentMsgName)) { + loadMsg(currentMsgName); + } + + // TODO: Add current music loading and playing here + // TODO: Palette handling? + + if (in.pos() == in.size()) { + debug(3, "loadTempSaveOS: Loaded the whole savefile."); + } else { + warning("loadTempSaveOS: Loaded the savefile but didn't exhaust it completely. Something was left over"); + } + + return !in.ioFailed(); +} + +bool CineEngine::loadPlainSaveFW(Common::SeekableReadStream &in, CineSaveGameFormat saveGameFormat) { + char bgName[13]; + + // At savefile position 0x0000: + currentDisk = in.readUint16BE(); + + // At 0x0002: + in.read(currentPartName, 13); + // At 0x000F: + in.read(currentDatName, 13); + + // At 0x001C: + saveVar2 = in.readSint16BE(); + + // At 0x001E: + in.read(currentPrcName, 13); + // At 0x002B: + in.read(currentRelName, 13); + // At 0x0038: + in.read(currentMsgName, 13); + // At 0x0045: + in.read(bgName, 13); + // At 0x0052: + in.read(currentCtName, 13); + + checkDataDisk(currentDisk); + + if (strlen(currentPartName)) { + loadPart(currentPartName); + } + + if (strlen(currentPrcName)) { + loadPrc(currentPrcName); + } + + if (strlen(currentRelName)) { + loadRel(currentRelName); + } + + if (strlen(bgName)) { + loadBg(bgName); + } + + if (strlen(currentCtName)) { + loadCtFW(currentCtName); + } + + // At 0x005F: + loadObjectTable(in); + + // At 0x2043 (i.e. 0x005F + 2 * 2 + 255 * 32): + renderer->restorePalette(in); + + // At 0x2083 (i.e. 0x2043 + 16 * 2 * 2): + globalVars.load(in, NUM_MAX_VAR); + + // At 0x2281 (i.e. 0x2083 + 255 * 2): + loadZoneData(in); + + // At 0x22A1 (i.e. 0x2281 + 16 * 2): + loadCommandVariables(in); + + // At 0x22A9 (i.e. 0x22A1 + 4 * 2): + char tempCommandBuffer[kMaxCommandBufferSize]; + in.read(tempCommandBuffer, kMaxCommandBufferSize); + commandBuffer = tempCommandBuffer; + renderer->setCommand(commandBuffer); + + // At 0x22F9 (i.e. 0x22A9 + 0x50): + renderer->_cmdY = in.readUint16BE(); + + // At 0x22FB: + bgVar0 = in.readUint16BE(); + // At 0x22FD: + allowPlayerInput = in.readUint16BE(); + // At 0x22FF: + playerCommand = in.readSint16BE(); + // At 0x2301: + commandVar1 = in.readSint16BE(); + // At 0x2303: + isDrawCommandEnabled = in.readUint16BE(); + // At 0x2305: + var5 = in.readUint16BE(); + // At 0x2307: + var4 = in.readUint16BE(); + // At 0x2309: + var3 = in.readUint16BE(); + // At 0x230B: + var2 = in.readUint16BE(); + // At 0x230D: + commandVar2 = in.readSint16BE(); + + // At 0x230F: + renderer->_messageBg = in.readUint16BE(); + + // At 0x2311: + in.readUint16BE(); + // At 0x2313: + in.readUint16BE(); + + // At 0x2315: + loadResourcesFromSave(in, saveGameFormat); + + loadScreenParams(in); + loadGlobalScripts(in); + loadObjectScripts(in); + loadOverlayList(in); + loadBgIncrustFromSave(in); + + if (strlen(currentMsgName)) { + loadMsg(currentMsgName); + } + + if (strlen(currentDatName)) { +/* i = saveVar2; + saveVar2 = 0; + loadMusic(); + if (i) { + playMusic(); + }*/ + } + + return !in.ioFailed(); +} + +bool CineEngine::makeLoad(char *saveName) { + Common::SharedPtr saveFile(g_saveFileMan->openForLoading(saveName)); + + if (!saveFile) { + drawString(otherMessages[0], 0); + waitPlayerInput(); + // restoreScreen(); + checkDataDisk(-1); + return false; + } + + setMouseCursor(MOUSE_CURSOR_DISK); + + uint32 saveSize = saveFile->size(); + // TODO: Evaluate the maximum savegame size for the temporary Operation Stealth savegame format. + if (saveSize == 0) { // Savefile's compressed using zlib format can't tell their unpacked size, test for it + // Can't get information about the savefile's size so let's try + // reading as much as we can from the file up to a predefined upper limit. + // + // Some estimates for maximum savefile sizes (All with 255 animDataTable entries of 30 bytes each): + // With 256 global scripts, object scripts, overlays and background incrusts: + // 0x2315 + (255 * 30) + (2 * 6) + (206 + 206 + 20 + 20) * 256 = ~129kB + // With 512 global scripts, object scripts, overlays and background incrusts: + // 0x2315 + (255 * 30) + (2 * 6) + (206 + 206 + 20 + 20) * 512 = ~242kB + // + // I think it extremely unlikely that there would be over 512 global scripts, object scripts, + // overlays and background incrusts so 256kB seems like quite a safe upper limit. + // NOTE: If the savegame format is changed then this value might have to be re-evaluated! + // Hopefully devices with more limited memory can also cope with this memory allocation. + saveSize = 256 * 1024; + } + Common::SharedPtr in(saveFile->readStream(saveSize)); + + // Try to detect the used savegame format + enum CineSaveGameFormat saveGameFormat = detectSaveGameFormat(*in); + + // Handle problematic savegame formats + bool load = true; // Should we try to load the savegame? + bool result = false; + if (saveGameFormat == ANIMSIZE_30_PTRS_BROKEN) { + // One might be able to load the ANIMSIZE_30_PTRS_BROKEN format but + // that's not implemented here because it was never used in a stable + // release of ScummVM but only during development (From revision 31453, + // which introduced the problem, until revision 32073, which fixed it). + // Therefore we bail out if we detect this particular savegame format. + warning("Detected a known broken savegame format, not loading savegame"); + load = false; // Don't load the savegame + } else if (saveGameFormat == ANIMSIZE_UNKNOWN) { + // If we can't detect the savegame format + // then let's try the default format and hope for the best. + warning("Couldn't detect the used savegame format, trying default savegame format. Things may break"); + saveGameFormat = ANIMSIZE_30_PTRS_INTACT; + } + + if (load) { + // Reset the engine's state + resetEngine(); + + if (saveGameFormat == TEMP_OS_FORMAT) { + // Load the temporary Operation Stealth savegame format + result = loadTempSaveOS(*in); + } else { + // Load the plain Future Wars savegame format + result = loadPlainSaveFW(*in, saveGameFormat); + } + } + + setMouseCursor(MOUSE_CURSOR_NORMAL); + + return result; +} + +void CineEngine::makeSaveFW(Common::OutSaveFile &out) { + out.writeUint16BE(currentDisk); + out.write(currentPartName, 13); + out.write(currentDatName, 13); + out.writeUint16BE(saveVar2); + out.write(currentPrcName, 13); + out.write(currentRelName, 13); + out.write(currentMsgName, 13); + renderer->saveBgNames(out); + out.write(currentCtName, 13); + + saveObjectTable(out); + renderer->savePalette(out); + globalVars.save(out, NUM_MAX_VAR); + saveZoneData(out); + saveCommandVariables(out); + saveCommandBuffer(out); + + out.writeUint16BE(renderer->_cmdY); + out.writeUint16BE(bgVar0); + out.writeUint16BE(allowPlayerInput); + out.writeUint16BE(playerCommand); + out.writeUint16BE(commandVar1); + out.writeUint16BE(isDrawCommandEnabled); + out.writeUint16BE(var5); + out.writeUint16BE(var4); + out.writeUint16BE(var3); + out.writeUint16BE(var2); + out.writeUint16BE(commandVar2); + out.writeUint16BE(renderer->_messageBg); + + saveAnimDataTable(out); + saveScreenParams(out); + + saveGlobalScripts(out); + saveObjectScripts(out); + saveOverlayList(out); + saveBgIncrustList(out); +} + +/** + * Save an Operation Stealth type savegame. WIP! + * + * NOTE: This is going to be very much a work in progress so the Operation Stealth's + * savegame formats that are going to be tried are extremely probably not going + * to be supported at all after Operation Stealth becomes officially supported. + * This means that the savegame format will hopefully change to something nicer + * when official support for Operation Stealth begins. + */ +void CineEngine::makeSaveOS(Common::OutSaveFile &out) { + int i; + + // Make a temporary Operation Stealth savegame format chunk header and save it. + ChunkHeader header; + header.id = TEMP_OS_FORMAT_ID; + header.version = CURRENT_OS_SAVE_VER; + header.size = 0; // No data is currently put inside the chunk, all the plain data comes right after it. + writeChunkHeader(out, header); + + // Start outputting the plain savegame data right after the chunk header. + out.writeUint16BE(currentDisk); + out.write(currentPartName, 13); + out.write(currentPrcName, 13); + out.write(currentRelName, 13); + out.write(currentMsgName, 13); + renderer->saveBgNames(out); + out.write(currentCtName, 13); + + saveObjectTable(out); + renderer->savePalette(out); + globalVars.save(out, NUM_MAX_VAR); + saveZoneData(out); + saveCommandVariables(out); + saveCommandBuffer(out); + saveZoneQuery(out); + + // FIXME: Save a proper name here, saving an empty string currently. + // 0x2925: Current music name (String, 13 bytes). + for (i = 0; i < 13; i++) { + out.writeByte(0); + } + // FIXME: Save proper value for this variable, currently writing zero + // 0x2932: Is music loaded? (Uint16BE, Boolean). + out.writeUint16BE(0); + // FIXME: Save proper value for this variable, currently writing zero + // 0x2934: Is music playing? (Uint16BE, Boolean). + out.writeUint16BE(0); + + out.writeUint16BE(renderer->_cmdY); + out.writeUint16BE(0); // Some unknown variable that seems to always be zero + out.writeUint16BE(allowPlayerInput); + out.writeUint16BE(playerCommand); + out.writeUint16BE(commandVar1); + out.writeUint16BE(isDrawCommandEnabled); + out.writeUint16BE(var5); + out.writeUint16BE(var4); + out.writeUint16BE(var3); + out.writeUint16BE(var2); + out.writeUint16BE(commandVar2); + out.writeUint16BE(renderer->_messageBg); + + // FIXME: Save proper value for this variable, currently writing zero. + // An unknown variable at 0x295E: adBgVar1 (Uint16BE). + out.writeUint16BE(0); + out.writeSint16BE(currentAdditionalBgIdx); + out.writeSint16BE(currentAdditionalBgIdx2); + // FIXME: Save proper value for this variable, currently writing zero. + // 0x2954: additionalBgVScroll (Uint16BE). This probably means renderer->_bgShift. + out.writeUint16BE(0); + // FIXME: Save proper value for this variable, currently writing zero. + // An unknown variable at 0x2956: adBgVar0 (Uint16BE). Maybe this means bgVar0? + out.writeUint16BE(0); + out.writeUint16BE(disableSystemMenu); + + saveAnimDataTable(out); + saveScreenParams(out); + saveGlobalScripts(out); + saveObjectScripts(out); + saveSeqList(out); + saveOverlayList(out); + saveBgIncrustList(out); +} + +void CineEngine::makeSave(char *saveFileName) { + Common::SharedPtr fHandle(g_saveFileMan->openForSaving(saveFileName)); + + setMouseCursor(MOUSE_CURSOR_DISK); + + if (!fHandle) { + drawString(otherMessages[1], 0); + waitPlayerInput(); + // restoreScreen(); + checkDataDisk(-1); + } else { + if (g_cine->getGameType() == GType_FW) { + makeSaveFW(*fHandle); + } else { + makeSaveOS(*fHandle); + } + } + + setMouseCursor(MOUSE_CURSOR_NORMAL); +} + +/*! \brief Load animDataTable from save + * \param fHandle Savefile open for reading + * \param saveGameFormat The used savegame format + * \todo Add Operation Stealth savefile support + * + * Unlike the old code, this one actually rebuilds the table one frame + * at a time. + */ +void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGameFormat saveGameFormat) { + int16 currentAnim, foundFileIdx, frame; + char *animName, part[256], name[10]; + uint16 width, height, bpp, var1; + + strcpy(part, currentPartName); + + // We only support these variations of the savegame format at the moment. + assert(saveGameFormat == ANIMSIZE_23 || saveGameFormat == ANIMSIZE_30_PTRS_INTACT); + + const int entrySize = ((saveGameFormat == ANIMSIZE_23) ? 23 : 30); + const int fileStartPos = fHandle.pos(); + currentAnim = 0; + while (currentAnim < NUM_MAX_ANIMDATA) { + // Seek to the start of the current animation's entry + fHandle.seek(fileStartPos + currentAnim * entrySize); + // Read in the current animation entry + width = fHandle.readUint16BE(); + var1 = fHandle.readUint16BE(); + bpp = fHandle.readUint16BE(); + height = fHandle.readUint16BE(); + + bool validPtr = false; + // Handle variables only present in animation entries of size 30 + if (entrySize == 30) { + validPtr = (fHandle.readUint32BE() != 0); // Read data pointer + fHandle.readUint32BE(); // Discard mask pointer + } + + foundFileIdx = fHandle.readSint16BE(); + frame = fHandle.readSint16BE(); + fHandle.read(name, 10); + + // Handle variables only present in animation entries of size 23 + if (entrySize == 23) { + validPtr = (fHandle.readByte() != 0); + } + + // Don't try to load invalid entries. + if (foundFileIdx < 0 || !validPtr) { + currentAnim++; // Jump over the invalid entry + continue; + } + + // Alright, the animation entry looks to be valid so let's start handling it... + if (strcmp(currentPartName, name)) { + closePart(); + loadPart(name); + } + + animName = partBuffer[foundFileIdx].partName; + loadRelatedPalette(animName); // Is this for Future Wars only? + const int16 prevAnim = currentAnim; + currentAnim = loadResource(animName, currentAnim); + assert(currentAnim > prevAnim); // Make sure we advance forward + } + + loadPart(part); + + // Make sure we jump over all the animation entries + fHandle.seek(fileStartPos + NUM_MAX_ANIMDATA * entrySize); +} + +} // End of namespace Cine diff --git a/engines/cine/saveload.h b/engines/cine/saveload.h new file mode 100644 index 0000000000..65f24f838d --- /dev/null +++ b/engines/cine/saveload.h @@ -0,0 +1,89 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef CINE_SAVELOAD_H +#define CINE_SAVELOAD_H + +#include "common/endian.h" + +namespace Cine { + +/** + * Cine engine's save game formats. + * Enumeration entries (Excluding the one used as an error) + * are sorted according to age (i.e. top one is oldest, last one newest etc). + * + * ANIMSIZE_UNKNOWN: + * - Animation data entry size is unknown (Used as an error). + * + * ANIMSIZE_23: + * - Animation data entry size is 23 bytes. + * - Used at least by 0.11.0 and 0.11.1 releases of ScummVM. + * - Introduced in revision 21772, stopped using in revision 31444. + * + * ANIMSIZE_30_PTRS_BROKEN: + * - Animation data entry size is 30 bytes. + * - Data and mask pointers in the saved structs are always NULL. + * - Introduced in revision 31453, stopped using in revision 32073. + * + * ANIMSIZE_30_PTRS_INTACT: + * - Animation data entry size is 30 bytes. + * - Data and mask pointers in the saved structs are intact, + * so you can test them for equality or inequality with NULL + * but don't try using them for anything else, it won't work. + * - Introduced in revision 31444, got broken in revision 31453, + * got fixed in revision 32073 and used after that. + * + * TEMP_OS_FORMAT: + * - Temporary Operation Stealth savegame format. + * - NOT backward compatible and NOT to be supported in the future. + * This format should ONLY be used during development and abandoned + * later in favor of a better format! + */ +enum CineSaveGameFormat { + ANIMSIZE_UNKNOWN, + ANIMSIZE_23, + ANIMSIZE_30_PTRS_BROKEN, + ANIMSIZE_30_PTRS_INTACT, + TEMP_OS_FORMAT +}; + +/** Identifier for the temporary Operation Stealth savegame format. */ +static const uint32 TEMP_OS_FORMAT_ID = MKID_BE('TEMP'); + +/** The current version number of Operation Stealth's savegame format. */ +static const uint32 CURRENT_OS_SAVE_VER = 0; + +/** Chunk header used by the temporary Operation Stealth savegame format. */ +struct ChunkHeader { + uint32 id; ///< Identifier (e.g. MKID_BE('TEMP')) + uint32 version; ///< Version number + uint32 size; ///< Size of the chunk after this header in bytes +}; + + +} // End of namespace Cine + +#endif diff --git a/engines/cine/various.cpp b/engines/cine/various.cpp index 4d928456d4..3a826fab97 100644 --- a/engines/cine/various.cpp +++ b/engines/cine/various.cpp @@ -26,7 +26,6 @@ #include "common/endian.h" #include "common/events.h" -#include "common/savefile.h" #include "cine/cine.h" #include "cine/main_loop.h" @@ -91,8 +90,6 @@ char currentCtName[15]; char currentPartName[15]; char currentDatName[30]; -int16 saveVar2; - byte isInPause = 0; /*! \brief Values used by the xMoveKeyb variable */ @@ -116,7 +113,6 @@ uint16 yMoveKeyb = kKeybMoveCenterY; SelectedObjStruct currentSelectedObject; CommandeType currentSaveName[10]; -int16 currentDisk; static const int16 choiceResultTable[] = { 1, 1, 1, 2, 1, 1, 1 }; static const int16 subObjectUseTable[] = { 3, 3, 3, 3, 3, 0, 0 }; @@ -276,347 +272,6 @@ int16 getObjectUnderCursor(uint16 x, uint16 y) { return -1; } -bool writeChunkHeader(Common::OutSaveFile &out, const ChunkHeader &header) { - out.writeUint32BE(header.id); - out.writeUint32BE(header.version); - out.writeUint32BE(header.size); - return !out.ioFailed(); -} - -bool loadChunkHeader(Common::SeekableReadStream &in, ChunkHeader &header) { - header.id = in.readUint32BE(); - header.version = in.readUint32BE(); - header.size = in.readUint32BE(); - return !in.ioFailed(); -} - -void saveObjectTable(Common::OutSaveFile &out) { - out.writeUint16BE(NUM_MAX_OBJECT); // Entry count - out.writeUint16BE(0x20); // Entry size - - for (int i = 0; i < NUM_MAX_OBJECT; i++) { - out.writeUint16BE(objectTable[i].x); - out.writeUint16BE(objectTable[i].y); - out.writeUint16BE(objectTable[i].mask); - out.writeUint16BE(objectTable[i].frame); - out.writeUint16BE(objectTable[i].costume); - out.write(objectTable[i].name, 20); - out.writeUint16BE(objectTable[i].part); - } -} - -void saveZoneData(Common::OutSaveFile &out) { - for (int i = 0; i < 16; i++) { - out.writeUint16BE(zoneData[i]); - } -} - -void saveCommandVariables(Common::OutSaveFile &out) { - for (int i = 0; i < 4; i++) { - out.writeUint16BE(commandVar3[i]); - } -} - -/*! \brief Save the 80 bytes long command buffer padded to that length with zeroes. */ -void saveCommandBuffer(Common::OutSaveFile &out) { - // Let's make sure there's space for the trailing zero - // (That's why we subtract one from the maximum command buffer size here). - uint32 size = MIN(commandBuffer.size(), kMaxCommandBufferSize - 1); - out.write(commandBuffer.c_str(), size); - // Write the rest as zeroes (Here we also write the string's trailing zero) - for (uint i = 0; i < kMaxCommandBufferSize - size; i++) { - out.writeByte(0); - } -} - -void saveAnimDataTable(Common::OutSaveFile &out) { - out.writeUint16BE(NUM_MAX_ANIMDATA); // Entry count - out.writeUint16BE(0x1E); // Entry size - - for (int i = 0; i < NUM_MAX_ANIMDATA; i++) { - animDataTable[i].save(out); - } -} - -void saveScreenParams(Common::OutSaveFile &out) { - // Screen parameters, unhandled - out.writeUint16BE(0); - out.writeUint16BE(0); - out.writeUint16BE(0); - out.writeUint16BE(0); - out.writeUint16BE(0); - out.writeUint16BE(0); -} - -void saveGlobalScripts(Common::OutSaveFile &out) { - ScriptList::const_iterator it; - out.writeUint16BE(globalScripts.size()); - for (it = globalScripts.begin(); it != globalScripts.end(); ++it) { - (*it)->save(out); - } -} - -void saveObjectScripts(Common::OutSaveFile &out) { - ScriptList::const_iterator it; - out.writeUint16BE(objectScripts.size()); - for (it = objectScripts.begin(); it != objectScripts.end(); ++it) { - (*it)->save(out); - } -} - -void saveOverlayList(Common::OutSaveFile &out) { - Common::List::const_iterator it; - - out.writeUint16BE(overlayList.size()); - - for (it = overlayList.begin(); it != overlayList.end(); ++it) { - out.writeUint32BE(0); // next - out.writeUint32BE(0); // previous? - out.writeUint16BE(it->objIdx); - out.writeUint16BE(it->type); - out.writeSint16BE(it->x); - out.writeSint16BE(it->y); - out.writeSint16BE(it->width); - out.writeSint16BE(it->color); - } -} - -void saveBgIncrustList(Common::OutSaveFile &out) { - Common::List::const_iterator it; - out.writeUint16BE(bgIncrustList.size()); - - for (it = bgIncrustList.begin(); it != bgIncrustList.end(); ++it) { - out.writeUint32BE(0); // next - out.writeUint32BE(0); // previous? - out.writeUint16BE(it->objIdx); - out.writeUint16BE(it->param); - out.writeUint16BE(it->x); - out.writeUint16BE(it->y); - out.writeUint16BE(it->frame); - out.writeUint16BE(it->part); - } -} - -void saveZoneQuery(Common::OutSaveFile &out) { - for (int i = 0; i < 16; i++) { - out.writeUint16BE(zoneQuery[i]); - } -} - -void saveSeqList(Common::OutSaveFile &out) { - Common::List::const_iterator it; - out.writeUint16BE(seqList.size()); - - for (it = seqList.begin(); it != seqList.end(); ++it) { - out.writeSint16BE(it->var4); - out.writeUint16BE(it->objIdx); - out.writeSint16BE(it->var8); - out.writeSint16BE(it->frame); - out.writeSint16BE(it->varC); - out.writeSint16BE(it->varE); - out.writeSint16BE(it->var10); - out.writeSint16BE(it->var12); - out.writeSint16BE(it->var14); - out.writeSint16BE(it->var16); - out.writeSint16BE(it->var18); - out.writeSint16BE(it->var1A); - out.writeSint16BE(it->var1C); - out.writeSint16BE(it->var1E); - } -} - -bool CineEngine::loadSaveDirectory(void) { - Common::InSaveFile *fHandle; - char tmp[80]; - - snprintf(tmp, 80, "%s.dir", _targetName.c_str()); - fHandle = g_saveFileMan->openForLoading(tmp); - - if (!fHandle) { - return false; - } - - fHandle->read(currentSaveName, 10 * 20); - delete fHandle; - - return true; -} - -/*! \brief Savegame format detector - * \param fHandle Savefile to check - * \return Savegame format on success, ANIMSIZE_UNKNOWN on failure - * - * This function seeks through the savefile and tries to determine the - * savegame format it uses. There's a miniscule chance that the detection - * algorithm could get confused and think that the file uses both the older - * and the newer format but that is such a remote possibility that I wouldn't - * worry about it at all. - * - * Also detects the temporary Operation Stealth savegame format now. - */ -enum CineSaveGameFormat detectSaveGameFormat(Common::SeekableReadStream &fHandle) { - const uint32 prevStreamPos = fHandle.pos(); - - // First check for the temporary Operation Stealth savegame format. - fHandle.seek(0); - ChunkHeader hdr; - loadChunkHeader(fHandle, hdr); - fHandle.seek(prevStreamPos); - if (hdr.id == TEMP_OS_FORMAT_ID) { - return TEMP_OS_FORMAT; - } - - // Ok, so the savegame isn't using the temporary Operation Stealth savegame format. - // Let's check for the plain Future Wars savegame format and its different versions then. - // The animDataTable begins at savefile position 0x2315. - // Each animDataTable entry takes 23 bytes in older saves (Revisions 21772-31443) - // and 30 bytes in the save format after that (Revision 31444 and onwards). - // There are 255 entries in the animDataTable in both of the savefile formats. - static const uint animDataTableStart = 0x2315; - static const uint animEntriesCount = 255; - static const uint oldAnimEntrySize = 23; - static const uint newAnimEntrySize = 30; - static const uint animEntrySizeChoices[] = {oldAnimEntrySize, newAnimEntrySize}; - Common::Array animEntrySizeMatches; - - // Try to walk through the savefile using different animDataTable entry sizes - // and make a list of all the successful entry sizes. - for (uint i = 0; i < ARRAYSIZE(animEntrySizeChoices); i++) { - // 206 = 2 * 50 * 2 + 2 * 3 (Size of global and object script entries) - // 20 = 4 * 2 + 2 * 6 (Size of overlay and background incrust entries) - static const uint sizeofScreenParams = 2 * 6; - static const uint globalScriptEntrySize = 206; - static const uint objectScriptEntrySize = 206; - static const uint overlayEntrySize = 20; - static const uint bgIncrustEntrySize = 20; - static const uint chainEntrySizes[] = { - globalScriptEntrySize, - objectScriptEntrySize, - overlayEntrySize, - bgIncrustEntrySize - }; - - uint animEntrySize = animEntrySizeChoices[i]; - // Jump over the animDataTable entries and the screen parameters - int32 newPos = animDataTableStart + animEntrySize * animEntriesCount + sizeofScreenParams; - // Check that there's data left after the point we're going to jump to - if (newPos >= fHandle.size()) { - continue; - } - fHandle.seek(newPos); - - // Jump over the remaining items in the savegame file - // (i.e. the global scripts, object scripts, overlays and background incrusts). - bool chainWalkSuccess = true; - for (uint chainIndex = 0; chainIndex < ARRAYSIZE(chainEntrySizes); chainIndex++) { - // Read entry count and jump over the entries - int entryCount = fHandle.readSint16BE(); - newPos = fHandle.pos() + chainEntrySizes[chainIndex] * entryCount; - // Check that we didn't go past the end of file. - // Note that getting exactly to the end of file is acceptable. - if (newPos > fHandle.size()) { - chainWalkSuccess = false; - break; - } - fHandle.seek(newPos); - } - - // If we could walk the chain successfully and - // got exactly to the end of file then we've got a match. - if (chainWalkSuccess && fHandle.pos() == fHandle.size()) { - // We found a match, let's save it - animEntrySizeMatches.push_back(animEntrySize); - } - } - - // Check that we got only one entry size match. - // If we didn't, then return an error. - enum CineSaveGameFormat result = ANIMSIZE_UNKNOWN; - if (animEntrySizeMatches.size() == 1) { - const uint animEntrySize = animEntrySizeMatches[0]; - assert(animEntrySize == oldAnimEntrySize || animEntrySize == newAnimEntrySize); - if (animEntrySize == oldAnimEntrySize) { - result = ANIMSIZE_23; - } else { // animEntrySize == newAnimEntrySize - // Check data and mask pointers in all of the animDataTable entries - // to see whether we've got the version with the broken data and mask pointers or not. - // In the broken format all data and mask pointers were always zero. - static const uint relativeDataPos = 2 * 4; - bool pointersIntact = false; - for (uint i = 0; i < animEntriesCount; i++) { - fHandle.seek(animDataTableStart + i * animEntrySize + relativeDataPos); - uint32 data = fHandle.readUint32BE(); - uint32 mask = fHandle.readUint32BE(); - if ((data != 0) || (mask != 0)) { - pointersIntact = true; - break; - } - } - result = (pointersIntact ? ANIMSIZE_30_PTRS_INTACT : ANIMSIZE_30_PTRS_BROKEN); - } - } else if (animEntrySizeMatches.size() > 1) { - warning("Savegame format detector got confused by input data. Detecting savegame to be using an unknown format"); - } else { // animEtrySizeMatches.size() == 0 - debug(3, "Savegame format detector was unable to detect savegame's format"); - } - - fHandle.seek(prevStreamPos); - return result; -} - -/*! \brief Restore script list item from savefile - * \param fHandle Savefile handle open for reading - * \param isGlobal Restore object or global script? - */ -void loadScriptFromSave(Common::SeekableReadStream &fHandle, bool isGlobal) { - ScriptVars localVars, labels; - uint16 compare, pos; - int16 idx; - - labels.load(fHandle); - localVars.load(fHandle); - - compare = fHandle.readUint16BE(); - pos = fHandle.readUint16BE(); - idx = fHandle.readUint16BE(); - - // no way to reinitialize these - if (idx < 0) { - return; - } - - // original code loaded everything into globalScripts, this should be - // the correct behavior - if (isGlobal) { - ScriptPtr tmp(scriptInfo->create(*scriptTable[idx], idx, labels, localVars, compare, pos)); - assert(tmp); - globalScripts.push_back(tmp); - } else { - ScriptPtr tmp(scriptInfo->create(*relTable[idx], idx, labels, localVars, compare, pos)); - assert(tmp); - objectScripts.push_back(tmp); - } -} - -/*! \brief Restore overlay sprites from savefile - * \param fHandle Savefile open for reading - */ -void loadOverlayFromSave(Common::SeekableReadStream &fHandle) { - overlay tmp; - - fHandle.readUint32BE(); - fHandle.readUint32BE(); - - tmp.objIdx = fHandle.readUint16BE(); - tmp.type = fHandle.readUint16BE(); - tmp.x = fHandle.readSint16BE(); - tmp.y = fHandle.readSint16BE(); - tmp.width = fHandle.readSint16BE(); - tmp.color = fHandle.readSint16BE(); - - overlayList.push_back(tmp); -} - void CineEngine::resetEngine() { g_sound->stopMusic(); freeAnimDataTable(); @@ -668,520 +323,6 @@ void CineEngine::resetEngine() { } } -bool loadObjectTable(Common::SeekableReadStream &in) { - in.readUint16BE(); // Entry count - in.readUint16BE(); // Entry size - - for (int i = 0; i < NUM_MAX_OBJECT; i++) { - objectTable[i].x = in.readSint16BE(); - objectTable[i].y = in.readSint16BE(); - objectTable[i].mask = in.readUint16BE(); - objectTable[i].frame = in.readSint16BE(); - objectTable[i].costume = in.readSint16BE(); - in.read(objectTable[i].name, 20); - objectTable[i].part = in.readUint16BE(); - } - return !in.ioFailed(); -} - -bool loadZoneData(Common::SeekableReadStream &in) { - for (int i = 0; i < 16; i++) { - zoneData[i] = in.readUint16BE(); - } - return !in.ioFailed(); -} - -bool loadCommandVariables(Common::SeekableReadStream &in) { - for (int i = 0; i < 4; i++) { - commandVar3[i] = in.readUint16BE(); - } - return !in.ioFailed(); -} - -bool loadScreenParams(Common::SeekableReadStream &in) { - // TODO: handle screen params (really required ?) - in.readUint16BE(); - in.readUint16BE(); - in.readUint16BE(); - in.readUint16BE(); - in.readUint16BE(); - in.readUint16BE(); - return !in.ioFailed(); -} - -bool loadGlobalScripts(Common::SeekableReadStream &in) { - int size = in.readSint16BE(); - for (int i = 0; i < size; i++) { - loadScriptFromSave(in, true); - } - return !in.ioFailed(); -} - -bool loadObjectScripts(Common::SeekableReadStream &in) { - int size = in.readSint16BE(); - for (int i = 0; i < size; i++) { - loadScriptFromSave(in, false); - } - return !in.ioFailed(); -} - -bool loadOverlayList(Common::SeekableReadStream &in) { - int size = in.readSint16BE(); - for (int i = 0; i < size; i++) { - loadOverlayFromSave(in); - } - return !in.ioFailed(); -} - -bool loadSeqList(Common::SeekableReadStream &in) { - uint size = in.readUint16BE(); - SeqListElement tmp; - for (uint i = 0; i < size; i++) { - tmp.var4 = in.readSint16BE(); - tmp.objIdx = in.readUint16BE(); - tmp.var8 = in.readSint16BE(); - tmp.frame = in.readSint16BE(); - tmp.varC = in.readSint16BE(); - tmp.varE = in.readSint16BE(); - tmp.var10 = in.readSint16BE(); - tmp.var12 = in.readSint16BE(); - tmp.var14 = in.readSint16BE(); - tmp.var16 = in.readSint16BE(); - tmp.var18 = in.readSint16BE(); - tmp.var1A = in.readSint16BE(); - tmp.var1C = in.readSint16BE(); - tmp.var1E = in.readSint16BE(); - seqList.push_back(tmp); - } - return !in.ioFailed(); -} - -bool loadZoneQuery(Common::SeekableReadStream &in) { - for (int i = 0; i < 16; i++) { - zoneQuery[i] = in.readUint16BE(); - } - return !in.ioFailed(); -} - -bool CineEngine::loadTempSaveOS(Common::SeekableReadStream &in) { - char musicName[13]; - char bgNames[8][13]; - - // First check the temporary Operation Stealth savegame format header. - ChunkHeader hdr; - loadChunkHeader(in, hdr); - if (hdr.id != TEMP_OS_FORMAT_ID) { - warning("loadTempSaveOS: File has incorrect identifier. Not loading savegame"); - return false; - } else if (hdr.version > CURRENT_OS_SAVE_VER) { - warning("loadTempSaveOS: Detected newer format version. Not loading savegame"); - return false; - } else if ((int)hdr.version < (int)CURRENT_OS_SAVE_VER) { - warning("loadTempSaveOS: Detected older format version. Trying to load nonetheless. Things may break"); - } else { // hdr.id == TEMP_OS_FORMAT_ID && hdr.version == CURRENT_OS_SAVE_VER - debug(3, "loadTempSaveOS: Found correct header (Both the identifier and version number match)."); - } - - // There shouldn't be any data in the header's chunk currently so it's an error if there is. - if (hdr.size > 0) { - warning("loadTempSaveOS: Format header's chunk seems to contain data so format is incorrect. Not loading savegame"); - return false; - } - - // Ok, so we've got a correct header for a temporary Operation Stealth savegame. - // Let's start loading the plain savegame data then. - currentDisk = in.readUint16BE(); - in.read(currentPartName, 13); - in.read(currentPrcName, 13); - in.read(currentRelName, 13); - in.read(currentMsgName, 13); - - // Load the 8 background names. - for (uint i = 0; i < 8; i++) { - in.read(bgNames[i], 13); - } - - in.read(currentCtName, 13); - - // Moved the loading of current procedure, relation, - // backgrounds and Ct here because if they were at the - // end of this function then the global scripts loading - // made an array out of bounds access. In the original - // game's disassembly these aren't here but at the end. - // The difference is probably in how we handle loading - // the global scripts and some other things (i.e. the - // loading routines aren't exactly the same and subtle - // semantic differences result in having to do things - // in a different order). - { - // Not sure if this is needed with Operation Stealth... - checkDataDisk(currentDisk); - - if (strlen(currentPrcName)) { - loadPrc(currentPrcName); - } - - if (strlen(currentRelName)) { - loadRel(currentRelName); - } - - // Load first background (Uses loadBg) - if (strlen(bgNames[0])) { - loadBg(bgNames[0]); - } - - // Add backgrounds 1-7 (Uses addBackground) - for (int i = 1; i < 8; i++) { - if (strlen(bgNames[i])) { - addBackground(bgNames[i], i); - } - } - - if (strlen(currentCtName)) { - loadCtOS(currentCtName); - } - } - - loadObjectTable(in); - renderer->restorePalette(in); - globalVars.load(in, NUM_MAX_VAR); - loadZoneData(in); - loadCommandVariables(in); - char tempCommandBuffer[kMaxCommandBufferSize]; - in.read(tempCommandBuffer, kMaxCommandBufferSize); - commandBuffer = tempCommandBuffer; - renderer->setCommand(commandBuffer); - loadZoneQuery(in); - - // TODO: Use the loaded string (Current music name (String, 13 bytes)). - in.read(musicName, 13); - - // TODO: Use the loaded value (Is music loaded? (Uint16BE, Boolean)). - in.readUint16BE(); - - // TODO: Use the loaded value (Is music playing? (Uint16BE, Boolean)). - in.readUint16BE(); - - renderer->_cmdY = in.readUint16BE(); - in.readUint16BE(); // Some unknown variable that seems to always be zero - allowPlayerInput = in.readUint16BE(); - playerCommand = in.readUint16BE(); - commandVar1 = in.readUint16BE(); - isDrawCommandEnabled = in.readUint16BE(); - var5 = in.readUint16BE(); - var4 = in.readUint16BE(); - var3 = in.readUint16BE(); - var2 = in.readUint16BE(); - commandVar2 = in.readUint16BE(); - renderer->_messageBg = in.readUint16BE(); - - // TODO: Use the loaded value (adBgVar1 (Uint16BE)). - in.readUint16BE(); - - currentAdditionalBgIdx = in.readSint16BE(); - currentAdditionalBgIdx2 = in.readSint16BE(); - - // TODO: Check whether the scroll value really gets used correctly after this. - // Note that the backgrounds are loaded only later than this value is set. - renderer->setScroll(in.readUint16BE()); - - // TODO: Use the loaded value (adBgVar0 (Uint16BE). Maybe this means bgVar0?). - in.readUint16BE(); - - disableSystemMenu = in.readUint16BE(); - - // TODO: adBgVar1 = 1 here - - // Load the animDataTable entries - in.readUint16BE(); // Entry count (255 in the PC version of Operation Stealth). - in.readUint16BE(); // Entry size (36 in the PC version of Operation Stealth). - loadResourcesFromSave(in, ANIMSIZE_30_PTRS_INTACT); - - loadScreenParams(in); - loadGlobalScripts(in); - loadObjectScripts(in); - loadSeqList(in); - loadOverlayList(in); - loadBgIncrustFromSave(in); - - // Left this here instead of moving it earlier in this function with - // the other current value loadings (e.g. loading of current procedure, - // current backgrounds etc). Mostly emulating the way we've handled - // Future Wars savegames and hoping that things work out. - if (strlen(currentMsgName)) { - loadMsg(currentMsgName); - } - - // TODO: Add current music loading and playing here - // TODO: Palette handling? - - if (in.pos() == in.size()) { - debug(3, "loadTempSaveOS: Loaded the whole savefile."); - } else { - warning("loadTempSaveOS: Loaded the savefile but didn't exhaust it completely. Something was left over"); - } - - return !in.ioFailed(); -} - -bool CineEngine::loadPlainSaveFW(Common::SeekableReadStream &in, CineSaveGameFormat saveGameFormat) { - char bgName[13]; - - // At savefile position 0x0000: - currentDisk = in.readUint16BE(); - - // At 0x0002: - in.read(currentPartName, 13); - // At 0x000F: - in.read(currentDatName, 13); - - // At 0x001C: - saveVar2 = in.readSint16BE(); - - // At 0x001E: - in.read(currentPrcName, 13); - // At 0x002B: - in.read(currentRelName, 13); - // At 0x0038: - in.read(currentMsgName, 13); - // At 0x0045: - in.read(bgName, 13); - // At 0x0052: - in.read(currentCtName, 13); - - checkDataDisk(currentDisk); - - if (strlen(currentPartName)) { - loadPart(currentPartName); - } - - if (strlen(currentPrcName)) { - loadPrc(currentPrcName); - } - - if (strlen(currentRelName)) { - loadRel(currentRelName); - } - - if (strlen(bgName)) { - loadBg(bgName); - } - - if (strlen(currentCtName)) { - loadCtFW(currentCtName); - } - - // At 0x005F: - loadObjectTable(in); - - // At 0x2043 (i.e. 0x005F + 2 * 2 + 255 * 32): - renderer->restorePalette(in); - - // At 0x2083 (i.e. 0x2043 + 16 * 2 * 2): - globalVars.load(in, NUM_MAX_VAR); - - // At 0x2281 (i.e. 0x2083 + 255 * 2): - loadZoneData(in); - - // At 0x22A1 (i.e. 0x2281 + 16 * 2): - loadCommandVariables(in); - - // At 0x22A9 (i.e. 0x22A1 + 4 * 2): - char tempCommandBuffer[kMaxCommandBufferSize]; - in.read(tempCommandBuffer, kMaxCommandBufferSize); - commandBuffer = tempCommandBuffer; - renderer->setCommand(commandBuffer); - - // At 0x22F9 (i.e. 0x22A9 + 0x50): - renderer->_cmdY = in.readUint16BE(); - - // At 0x22FB: - bgVar0 = in.readUint16BE(); - // At 0x22FD: - allowPlayerInput = in.readUint16BE(); - // At 0x22FF: - playerCommand = in.readSint16BE(); - // At 0x2301: - commandVar1 = in.readSint16BE(); - // At 0x2303: - isDrawCommandEnabled = in.readUint16BE(); - // At 0x2305: - var5 = in.readUint16BE(); - // At 0x2307: - var4 = in.readUint16BE(); - // At 0x2309: - var3 = in.readUint16BE(); - // At 0x230B: - var2 = in.readUint16BE(); - // At 0x230D: - commandVar2 = in.readSint16BE(); - - // At 0x230F: - renderer->_messageBg = in.readUint16BE(); - - // At 0x2311: - in.readUint16BE(); - // At 0x2313: - in.readUint16BE(); - - // At 0x2315: - loadResourcesFromSave(in, saveGameFormat); - - loadScreenParams(in); - loadGlobalScripts(in); - loadObjectScripts(in); - loadOverlayList(in); - loadBgIncrustFromSave(in); - - if (strlen(currentMsgName)) { - loadMsg(currentMsgName); - } - - if (strlen(currentDatName)) { -/* i = saveVar2; - saveVar2 = 0; - loadMusic(); - if (i) { - playMusic(); - }*/ - } - - return !in.ioFailed(); -} - -bool CineEngine::makeLoad(char *saveName) { - Common::SharedPtr saveFile(g_saveFileMan->openForLoading(saveName)); - - if (!saveFile) { - drawString(otherMessages[0], 0); - waitPlayerInput(); - // restoreScreen(); - checkDataDisk(-1); - return false; - } - - setMouseCursor(MOUSE_CURSOR_DISK); - - uint32 saveSize = saveFile->size(); - // TODO: Evaluate the maximum savegame size for the temporary Operation Stealth savegame format. - if (saveSize == 0) { // Savefile's compressed using zlib format can't tell their unpacked size, test for it - // Can't get information about the savefile's size so let's try - // reading as much as we can from the file up to a predefined upper limit. - // - // Some estimates for maximum savefile sizes (All with 255 animDataTable entries of 30 bytes each): - // With 256 global scripts, object scripts, overlays and background incrusts: - // 0x2315 + (255 * 30) + (2 * 6) + (206 + 206 + 20 + 20) * 256 = ~129kB - // With 512 global scripts, object scripts, overlays and background incrusts: - // 0x2315 + (255 * 30) + (2 * 6) + (206 + 206 + 20 + 20) * 512 = ~242kB - // - // I think it extremely unlikely that there would be over 512 global scripts, object scripts, - // overlays and background incrusts so 256kB seems like quite a safe upper limit. - // NOTE: If the savegame format is changed then this value might have to be re-evaluated! - // Hopefully devices with more limited memory can also cope with this memory allocation. - saveSize = 256 * 1024; - } - Common::SharedPtr in(saveFile->readStream(saveSize)); - - // Try to detect the used savegame format - enum CineSaveGameFormat saveGameFormat = detectSaveGameFormat(*in); - - // Handle problematic savegame formats - bool load = true; // Should we try to load the savegame? - bool result = false; - if (saveGameFormat == ANIMSIZE_30_PTRS_BROKEN) { - // One might be able to load the ANIMSIZE_30_PTRS_BROKEN format but - // that's not implemented here because it was never used in a stable - // release of ScummVM but only during development (From revision 31453, - // which introduced the problem, until revision 32073, which fixed it). - // Therefore we bail out if we detect this particular savegame format. - warning("Detected a known broken savegame format, not loading savegame"); - load = false; // Don't load the savegame - } else if (saveGameFormat == ANIMSIZE_UNKNOWN) { - // If we can't detect the savegame format - // then let's try the default format and hope for the best. - warning("Couldn't detect the used savegame format, trying default savegame format. Things may break"); - saveGameFormat = ANIMSIZE_30_PTRS_INTACT; - } - - if (load) { - // Reset the engine's state - resetEngine(); - - if (saveGameFormat == TEMP_OS_FORMAT) { - // Load the temporary Operation Stealth savegame format - result = loadTempSaveOS(*in); - } else { - // Load the plain Future Wars savegame format - result = loadPlainSaveFW(*in, saveGameFormat); - } - } - - setMouseCursor(MOUSE_CURSOR_NORMAL); - - return result; -} - -void CineEngine::makeSaveFW(Common::OutSaveFile &out) { - out.writeUint16BE(currentDisk); - out.write(currentPartName, 13); - out.write(currentDatName, 13); - out.writeUint16BE(saveVar2); - out.write(currentPrcName, 13); - out.write(currentRelName, 13); - out.write(currentMsgName, 13); - renderer->saveBgNames(out); - out.write(currentCtName, 13); - - saveObjectTable(out); - renderer->savePalette(out); - globalVars.save(out, NUM_MAX_VAR); - saveZoneData(out); - saveCommandVariables(out); - saveCommandBuffer(out); - - out.writeUint16BE(renderer->_cmdY); - out.writeUint16BE(bgVar0); - out.writeUint16BE(allowPlayerInput); - out.writeUint16BE(playerCommand); - out.writeUint16BE(commandVar1); - out.writeUint16BE(isDrawCommandEnabled); - out.writeUint16BE(var5); - out.writeUint16BE(var4); - out.writeUint16BE(var3); - out.writeUint16BE(var2); - out.writeUint16BE(commandVar2); - out.writeUint16BE(renderer->_messageBg); - - saveAnimDataTable(out); - saveScreenParams(out); - - saveGlobalScripts(out); - saveObjectScripts(out); - saveOverlayList(out); - saveBgIncrustList(out); -} - -void CineEngine::makeSave(char *saveFileName) { - Common::SharedPtr fHandle(g_saveFileMan->openForSaving(saveFileName)); - - setMouseCursor(MOUSE_CURSOR_DISK); - - if (!fHandle) { - drawString(otherMessages[1], 0); - waitPlayerInput(); - // restoreScreen(); - checkDataDisk(-1); - } else { - if (g_cine->getGameType() == GType_FW) { - makeSaveFW(*fHandle); - } else { - makeSaveOS(*fHandle); - } - } - - setMouseCursor(MOUSE_CURSOR_NORMAL); -} - void CineEngine::makeSystemMenu(void) { int16 numEntry, systemCommand; int16 mouseX, mouseY, mouseButton; @@ -1319,89 +460,6 @@ void CineEngine::makeSystemMenu(void) { } } -/** - * Save an Operation Stealth type savegame. WIP! - * - * NOTE: This is going to be very much a work in progress so the Operation Stealth's - * savegame formats that are going to be tried are extremely probably not going - * to be supported at all after Operation Stealth becomes officially supported. - * This means that the savegame format will hopefully change to something nicer - * when official support for Operation Stealth begins. - */ -void CineEngine::makeSaveOS(Common::OutSaveFile &out) { - int i; - - // Make a temporary Operation Stealth savegame format chunk header and save it. - ChunkHeader header; - header.id = TEMP_OS_FORMAT_ID; - header.version = CURRENT_OS_SAVE_VER; - header.size = 0; // No data is currently put inside the chunk, all the plain data comes right after it. - writeChunkHeader(out, header); - - // Start outputting the plain savegame data right after the chunk header. - out.writeUint16BE(currentDisk); - out.write(currentPartName, 13); - out.write(currentPrcName, 13); - out.write(currentRelName, 13); - out.write(currentMsgName, 13); - renderer->saveBgNames(out); - out.write(currentCtName, 13); - - saveObjectTable(out); - renderer->savePalette(out); - globalVars.save(out, NUM_MAX_VAR); - saveZoneData(out); - saveCommandVariables(out); - saveCommandBuffer(out); - saveZoneQuery(out); - - // FIXME: Save a proper name here, saving an empty string currently. - // 0x2925: Current music name (String, 13 bytes). - for (i = 0; i < 13; i++) { - out.writeByte(0); - } - // FIXME: Save proper value for this variable, currently writing zero - // 0x2932: Is music loaded? (Uint16BE, Boolean). - out.writeUint16BE(0); - // FIXME: Save proper value for this variable, currently writing zero - // 0x2934: Is music playing? (Uint16BE, Boolean). - out.writeUint16BE(0); - - out.writeUint16BE(renderer->_cmdY); - out.writeUint16BE(0); // Some unknown variable that seems to always be zero - out.writeUint16BE(allowPlayerInput); - out.writeUint16BE(playerCommand); - out.writeUint16BE(commandVar1); - out.writeUint16BE(isDrawCommandEnabled); - out.writeUint16BE(var5); - out.writeUint16BE(var4); - out.writeUint16BE(var3); - out.writeUint16BE(var2); - out.writeUint16BE(commandVar2); - out.writeUint16BE(renderer->_messageBg); - - // FIXME: Save proper value for this variable, currently writing zero. - // An unknown variable at 0x295E: adBgVar1 (Uint16BE). - out.writeUint16BE(0); - out.writeSint16BE(currentAdditionalBgIdx); - out.writeSint16BE(currentAdditionalBgIdx2); - // FIXME: Save proper value for this variable, currently writing zero. - // 0x2954: additionalBgVScroll (Uint16BE). This probably means renderer->_bgShift. - out.writeUint16BE(0); - // FIXME: Save proper value for this variable, currently writing zero. - // An unknown variable at 0x2956: adBgVar0 (Uint16BE). Maybe this means bgVar0? - out.writeUint16BE(0); - out.writeUint16BE(disableSystemMenu); - - saveAnimDataTable(out); - saveScreenParams(out); - saveGlobalScripts(out); - saveObjectScripts(out); - saveSeqList(out); - saveOverlayList(out); - saveBgIncrustList(out); -} - void drawMessageBox(int16 x, int16 y, int16 width, int16 currentY, int16 offset, int16 color, byte* page) { gfxDrawLine(x + offset, y + offset, x + width - offset, y + offset, color, page); // top gfxDrawLine(x + offset, currentY + 4 - offset, x + width - offset, currentY + 4 - offset, color, page); // bottom diff --git a/engines/cine/various.h b/engines/cine/various.h index 81a1ab1115..34c56cd5db 100644 --- a/engines/cine/various.h +++ b/engines/cine/various.h @@ -41,6 +41,9 @@ void initLanguage(Common::Language lang); int16 makeMenuChoice(const CommandeType commandList[], uint16 height, uint16 X, uint16 Y, uint16 width, bool recheckValue = false); void makeCommandLine(void); void makeActionMenu(void); +void drawString(const char *string, byte param); +void waitPlayerInput(void); +void setTextWindow(uint16 param1, uint16 param2, uint16 param3, uint16 param4); extern bool disableSystemMenu; extern bool inMenu; @@ -70,6 +73,11 @@ extern uint16 var2; extern uint16 var3; extern uint16 var4; extern uint16 var5; +extern int16 commandVar1; +extern int16 commandVar2; +extern int16 commandVar3[4]; + +extern char currentDatName[30]; void setTextWindow(uint16 param1, uint16 param2, uint16 param3, uint16 param4); -- cgit v1.2.3