aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFilippos Karapetis2008-11-13 08:59:17 +0000
committerFilippos Karapetis2008-11-13 08:59:17 +0000
commit162634cc95e5a2a86ae1611d69024075dfdad450 (patch)
tree15265f4cbaab2fc5bbeaad2fcf25bdb2a35ff6fe
parent0ba17539acad48797323f1481a0533082778b61d (diff)
downloadscummvm-rg350-162634cc95e5a2a86ae1611d69024075dfdad450.tar.gz
scummvm-rg350-162634cc95e5a2a86ae1611d69024075dfdad450.tar.bz2
scummvm-rg350-162634cc95e5a2a86ae1611d69024075dfdad450.zip
Started moving Save/Load code to saveload.cpp/.h
svn-id: r35033
-rw-r--r--engines/cine/anim.cpp71
-rw-r--r--engines/cine/anim.h53
-rw-r--r--engines/cine/bg_list.h1
-rw-r--r--engines/cine/module.mk1
-rw-r--r--engines/cine/saveload.cpp1048
-rw-r--r--engines/cine/saveload.h89
-rw-r--r--engines/cine/various.cpp942
-rw-r--r--engines/cine/various.h8
8 files changed, 1146 insertions, 1067 deletions
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<uint> 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<uint32>(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<overlay>::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<BGIncrust>::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<SeqListElement>::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<Common::InSaveFile> 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<Common::MemoryReadStream> 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<Common::OutSaveFile> 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<uint32>(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<overlay>::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<BGIncrust>::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<SeqListElement>::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<uint> 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<Common::InSaveFile> 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<Common::MemoryReadStream> 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<Common::OutSaveFile> 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);