aboutsummaryrefslogtreecommitdiff
path: root/engines/cine
diff options
context:
space:
mode:
Diffstat (limited to 'engines/cine')
-rw-r--r--engines/cine/anim.cpp74
-rw-r--r--engines/cine/anim.h57
-rw-r--r--engines/cine/bg.cpp2
-rw-r--r--engines/cine/bg.h3
-rw-r--r--engines/cine/bg_list.cpp4
-rw-r--r--engines/cine/bg_list.h2
-rw-r--r--engines/cine/cine.cpp5
-rw-r--r--engines/cine/cine.h8
-rw-r--r--engines/cine/gfx.cpp81
-rw-r--r--engines/cine/gfx.h12
-rw-r--r--engines/cine/main_loop.cpp39
-rw-r--r--engines/cine/object.cpp75
-rw-r--r--engines/cine/object.h10
-rw-r--r--engines/cine/pal.h2
-rw-r--r--engines/cine/part.cpp4
-rw-r--r--engines/cine/prc.cpp9
-rw-r--r--engines/cine/prc.h2
-rw-r--r--engines/cine/script.h30
-rw-r--r--engines/cine/script_fw.cpp88
-rw-r--r--engines/cine/script_os.cpp87
-rw-r--r--engines/cine/sound.cpp1
-rw-r--r--engines/cine/texte.cpp10
-rw-r--r--engines/cine/texte.h5
-rw-r--r--engines/cine/unpack.cpp2
-rw-r--r--engines/cine/various.cpp1385
-rw-r--r--engines/cine/various.h8
26 files changed, 1424 insertions, 581 deletions
diff --git a/engines/cine/anim.cpp b/engines/cine/anim.cpp
index 055eb733c3..8dbccebedf 100644
--- a/engines/cine/anim.cpp
+++ b/engines/cine/anim.cpp
@@ -769,19 +769,18 @@ void loadAbs(const char *resourceName, uint16 idx) {
/*! \brief Load animDataTable from save
* \param fHandle Savefile open for reading
- * \param broken Broken/correct file format switch
+ * \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::InSaveFile &fHandle, bool broken) {
+void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGameFormat saveGameFormat) {
int16 currentAnim, foundFileIdx;
int8 isMask = 0, isSpl = 0;
byte *dataPtr, *ptr;
char *animName, part[256];
byte transparentColor = 0;
- AnimData *currentPtr;
AnimHeaderStruct animHeader;
uint16 width, height, bpp, var1;
@@ -791,30 +790,46 @@ void loadResourcesFromSave(Common::InSaveFile &fHandle, bool broken) {
strcpy(part, currentPartName);
- for (currentAnim = 0; currentAnim < NUM_MAX_ANIMDATA; currentAnim++) {
- currentPtr = &animDataTable[currentAnim];
+ // 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();
+ for (currentAnim = 0; currentAnim < NUM_MAX_ANIMDATA; currentAnim += animHeader.numFrames) {
+ // Initialize the number of frames variable to a sane number.
+ // This is needed when using continue later in this function.
+ animHeader.numFrames = 1;
+
+ // 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();
- if (!broken) {
- if (!fHandle.readUint32BE()) {
- fHandle.skip(18);
- continue;
- }
- fHandle.readUint32BE();
+ 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);
- if (foundFileIdx < 0 || (broken && !fHandle.readByte())) {
+ // 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) {
continue;
}
+ // Alright, the animation entry looks to be valid so let's start handling it...
if (strcmp(currentPartName, name)) {
closePart();
loadPart(name);
@@ -823,13 +838,14 @@ void loadResourcesFromSave(Common::InSaveFile &fHandle, bool broken) {
animName = partBuffer[foundFileIdx].partName;
ptr = dataPtr = readBundleFile(foundFileIdx);
+ // isSpl and isMask are mutually exclusive cases
isSpl = (strstr(animName, ".SPL")) ? 1 : 0;
isMask = (strstr(animName, ".MSK")) ? 1 : 0;
if (isSpl) {
width = (uint16) partBuffer[foundFileIdx].unpackedSize;
height = 1;
- frame = 0;
+ animHeader.numFrames = 1;
type = ANIM_RAW;
} else {
Common::MemoryReadStream readS(ptr, 0x16);
@@ -843,25 +859,35 @@ void loadResourcesFromSave(Common::InSaveFile &fHandle, bool broken) {
type = ANIM_MASK;
} else {
type = ANIM_MASKSPRITE;
+ }
+ }
- loadRelatedPalette(animName);
- transparentColor = getAnimTransparentColor(animName);
-
- // special case transparency handling
- if (!strcmp(animName, "L2202.ANI")) {
- transparentColor = (frame < 2) ? 0 : 7;
- } else if (!strcmp(animName, "L4601.ANI")) {
- transparentColor = (frame < 1) ? 0xE : 0;
- }
+ loadRelatedPalette(animName);
+ transparentColor = getAnimTransparentColor(animName);
+ // Make sure we load at least one frame and also that we
+ // don't overflow the animDataTable by writing beyond its end.
+ animHeader.numFrames = CLIP<uint16>(animHeader.numFrames, 1, NUM_MAX_ANIMDATA - currentAnim);
+
+ // Load the frames
+ for (frame = 0; frame < animHeader.numFrames; frame++) {
+ // special case transparency handling
+ if (!strcmp(animName, "L2202.ANI")) {
+ transparentColor = (frame < 2) ? 0 : 7;
+ } else if (!strcmp(animName, "L4601.ANI")) {
+ transparentColor = (frame < 1) ? 0xE : 0;
}
+
+ // Load a single frame
+ animDataTable[currentAnim + frame].load(ptr + frame * width * height, type, width, height, foundFileIdx, frame, name, transparentColor);
}
- ptr += frame * width * height;
- currentPtr->load(ptr, type, width, height, foundFileIdx, frame, name, transparentColor);
free(dataPtr);
}
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 d63033e670..b0ce55f7ee 100644
--- a/engines/cine/anim.h
+++ b/engines/cine/anim.h
@@ -26,8 +26,63 @@
#ifndef CINE_ANIM_H
#define CINE_ANIM_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
+};
+
struct AnimHeaderStruct {
byte field_0;
byte field_1;
@@ -101,7 +156,7 @@ void freeAnimDataTable(void);
void freeAnimDataRange(byte startIdx, byte numIdx);
void loadResource(const char *resourceName);
void loadAbs(const char *resourceName, uint16 idx);
-void loadResourcesFromSave(Common::InSaveFile &fHandle, bool broken);
+void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGameFormat saveGameFormat);
void generateMask(const byte *sprite, byte *mask, uint16 size, byte transparency);
} // End of namespace Cine
diff --git a/engines/cine/bg.cpp b/engines/cine/bg.cpp
index c5b7fb4e3d..2a4e7f0ab1 100644
--- a/engines/cine/bg.cpp
+++ b/engines/cine/bg.cpp
@@ -35,7 +35,7 @@ namespace Cine {
uint16 bgVar0;
byte *additionalBgTable[9];
-byte currentAdditionalBgIdx = 0, currentAdditionalBgIdx2 = 0;
+int16 currentAdditionalBgIdx = 0, currentAdditionalBgIdx2 = 0;
byte loadCtFW(const char *ctName) {
uint16 header[32];
diff --git a/engines/cine/bg.h b/engines/cine/bg.h
index 5fa8209131..9f97bc467d 100644
--- a/engines/cine/bg.h
+++ b/engines/cine/bg.h
@@ -35,6 +35,9 @@ void addBackground(const char *bgName, uint16 bgIdx);
extern uint16 bgVar0;
+extern int16 currentAdditionalBgIdx;
+extern int16 currentAdditionalBgIdx2;
+
} // End of namespace Cine
#endif
diff --git a/engines/cine/bg_list.cpp b/engines/cine/bg_list.cpp
index cf25f1d355..fddca078e5 100644
--- a/engines/cine/bg_list.cpp
+++ b/engines/cine/bg_list.cpp
@@ -63,6 +63,7 @@ void addSpriteFilledToBGList(int16 objIdx) {
void createBgIncrustListElement(int16 objIdx, int16 param) {
BGIncrust tmp;
+ tmp.unkPtr = 0;
tmp.objIdx = objIdx;
tmp.param = param;
tmp.x = objectTable[objIdx].x;
@@ -82,7 +83,7 @@ void resetBgIncrustList(void) {
/*! \brief Restore incrust list from savefile
* \param fHandle Savefile open for reading
*/
-void loadBgIncrustFromSave(Common::InSaveFile &fHandle) {
+void loadBgIncrustFromSave(Common::SeekableReadStream &fHandle) {
BGIncrust tmp;
int size = fHandle.readSint16BE();
@@ -90,6 +91,7 @@ void loadBgIncrustFromSave(Common::InSaveFile &fHandle) {
fHandle.readUint32BE();
fHandle.readUint32BE();
+ tmp.unkPtr = 0;
tmp.objIdx = fHandle.readUint16BE();
tmp.param = fHandle.readUint16BE();
tmp.x = fHandle.readUint16BE();
diff --git a/engines/cine/bg_list.h b/engines/cine/bg_list.h
index 1849d6ec3d..9a402baee8 100644
--- a/engines/cine/bg_list.h
+++ b/engines/cine/bg_list.h
@@ -51,7 +51,7 @@ void addSpriteFilledToBGList(int16 idx);
void createBgIncrustListElement(int16 objIdx, int16 param);
void resetBgIncrustList(void);
-void loadBgIncrustFromSave(Common::InSaveFile &fHandle);
+void loadBgIncrustFromSave(Common::SeekableReadStream &fHandle);
} // End of namespace Cine
diff --git a/engines/cine/cine.cpp b/engines/cine/cine.cpp
index efc33fadaf..f6778b6457 100644
--- a/engines/cine/cine.cpp
+++ b/engines/cine/cine.cpp
@@ -42,7 +42,6 @@
#include "cine/sound.h"
#include "cine/various.h"
-
namespace Cine {
Sound *g_sound;
@@ -70,6 +69,10 @@ CineEngine::~CineEngine() {
freeErrmessDat();
}
Common::clearAllSpecialDebugLevels();
+
+ free(palPtr);
+ free(partBuffer);
+ free(textDataPtr);
}
int CineEngine::init() {
diff --git a/engines/cine/cine.h b/engines/cine/cine.h
index 710840c17e..eaae555812 100644
--- a/engines/cine/cine.h
+++ b/engines/cine/cine.h
@@ -94,10 +94,17 @@ public:
Common::StringList _volumeResourceFiles;
StringPtrHashMap _volumeEntriesMap;
+ TextHandler _textHandler;
private:
void initialize(void);
+ void resetEngine();
+ bool loadPlainSaveFW(Common::SeekableReadStream &in, CineSaveGameFormat saveGameFormat);
+ bool loadTempSaveOS(Common::SeekableReadStream &in);
bool makeLoad(char *saveName);
+ void makeSaveFW(Common::OutSaveFile &out);
+ void makeSaveOS(Common::OutSaveFile &out);
+ void makeSave(char *saveFileName);
void mainLoop(int bootScriptIdx);
void readVolCnf();
@@ -107,6 +114,7 @@ private:
extern CineEngine *g_cine;
#define BOOT_PRC_NAME "AUTO00.PRC"
+#define COPY_PROT_FAIL_PRC_NAME "L201.ANI"
enum {
VAR_MOUSE_X_MODE = 253,
diff --git a/engines/cine/gfx.cpp b/engines/cine/gfx.cpp
index 47446f2410..cbddf0fc59 100644
--- a/engines/cine/gfx.cpp
+++ b/engines/cine/gfx.cpp
@@ -337,7 +337,7 @@ int FWRenderer::drawChar(char character, int x, int y) {
x += 5;
} else if ((width = fontParamTable[(unsigned char)character].characterWidth)) {
idx = fontParamTable[(unsigned char)character].characterIdx;
- drawSpriteRaw(textTable[idx][0], textTable[idx][1], 16, 8, _backBuffer, x, y);
+ drawSpriteRaw(g_cine->_textHandler.textTable[idx][0], g_cine->_textHandler.textTable[idx][1], 16, 8, _backBuffer, x, y);
x += width + 1;
}
@@ -601,20 +601,26 @@ void FWRenderer::setScroll(unsigned int shift) {
error("Future Wars renderer doesn't support multiple backgrounds");
}
+/*! \brief Future Wars has no scrolling backgrounds so scroll value is always zero.
+ */
+uint FWRenderer::getScroll() const {
+ return 0;
+}
+
/*! \brief Placeholder for Operation Stealth implementation
*/
void FWRenderer::removeBg(unsigned int idx) {
error("Future Wars renderer doesn't support multiple backgrounds");
}
-void FWRenderer::saveBg(Common::OutSaveFile &fHandle) {
+void FWRenderer::saveBgNames(Common::OutSaveFile &fHandle) {
fHandle.write(_bgName, 13);
}
/*! \brief Restore active and backup palette from save
* \param fHandle Savefile open for reading
*/
-void FWRenderer::restorePalette(Common::InSaveFile &fHandle) {
+void FWRenderer::restorePalette(Common::SeekableReadStream &fHandle) {
int i;
if (!_palette) {
@@ -655,6 +661,49 @@ void FWRenderer::savePalette(Common::OutSaveFile &fHandle) {
}
}
+/*! \brief Write active and backup palette to save
+ * \param fHandle Savefile open for writing
+ */
+void OSRenderer::savePalette(Common::OutSaveFile &fHandle) {
+ int i;
+
+ assert(_activeHiPal);
+
+ // Write the active 256 color palette.
+ for (i = 0; i < _hiPalSize; i++) {
+ fHandle.writeByte(_activeHiPal[i]);
+ }
+
+ // Write the active 256 color palette a second time.
+ // FIXME: The backup 256 color palette should be saved here instead of the active one.
+ for (i = 0; i < _hiPalSize; i++) {
+ fHandle.writeByte(_activeHiPal[i]);
+ }
+}
+
+/*! \brief Restore active and backup palette from save
+ * \param fHandle Savefile open for reading
+ */
+void OSRenderer::restorePalette(Common::SeekableReadStream &fHandle) {
+ int i;
+
+ if (!_activeHiPal) {
+ _activeHiPal = new byte[_hiPalSize];
+ }
+
+ assert(_activeHiPal);
+
+ for (i = 0; i < _hiPalSize; i++) {
+ _activeHiPal[i] = fHandle.readByte();
+ }
+
+ // Jump over the backup 256 color palette.
+ // FIXME: Load the backup 256 color palette and use it properly.
+ fHandle.seek(_hiPalSize, SEEK_CUR);
+
+ _changePal = 1;
+}
+
/*! \brief Rotate active palette
* \param a First color to rotate
* \param b Last color to rotate
@@ -938,7 +987,7 @@ int OSRenderer::drawChar(char character, int x, int y) {
x += 5;
} else if ((width = fontParamTable[(unsigned char)character].characterWidth)) {
idx = fontParamTable[(unsigned char)character].characterIdx;
- drawSpriteRaw2(textTable[idx][0], 0, 16, 8, _backBuffer, x, y);
+ drawSpriteRaw2(g_cine->_textHandler.textTable[idx][0], 0, 16, 8, _backBuffer, x, y);
x += width + 1;
}
@@ -969,6 +1018,7 @@ void OSRenderer::drawBackground() {
/*! \brief Draw one overlay
* \param it Overlay info
+ * \todo Add handling of type 22 overlays
*/
void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
int len;
@@ -979,6 +1029,9 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
switch (it->type) {
// color sprite
case 0:
+ if (objectTable[it->objIdx].frame < 0) {
+ break;
+ }
sprite = animDataTable + objectTable[it->objIdx].frame;
len = sprite->_realWidth * sprite->_height;
mask = new byte[len];
@@ -988,6 +1041,13 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
delete[] mask;
break;
+ // bitmap
+ case 4:
+ if (objectTable[it->objIdx].frame >= 0) {
+ FWRenderer::renderOverlay(it);
+ }
+ break;
+
// masked background
case 20:
assert(it->objIdx < NUM_MAX_OBJECT);
@@ -1236,6 +1296,13 @@ void OSRenderer::setScroll(unsigned int shift) {
_bgShift = shift;
}
+/*! \brief Get background scroll
+ * \return Background scroll in pixels
+ */
+uint OSRenderer::getScroll() const {
+ return _bgShift;
+}
+
/*! \brief Unload background from renderer
* \param idx Background to unload
*/
@@ -1259,6 +1326,12 @@ void OSRenderer::removeBg(unsigned int idx) {
memset(_bgTable[idx].name, 0, sizeof (_bgTable[idx].name));
}
+void OSRenderer::saveBgNames(Common::OutSaveFile &fHandle) {
+ for (int i = 0; i < 8; i++) {
+ fHandle.write(_bgTable[i].name, 13);
+ }
+}
+
/*! \brief Fade to black
* \bug Operation Stealth sometimes seems to fade to black using
* transformPalette resulting in double fadeout
diff --git a/engines/cine/gfx.h b/engines/cine/gfx.h
index c63c79ac82..6a3aa1ef89 100644
--- a/engines/cine/gfx.h
+++ b/engines/cine/gfx.h
@@ -108,13 +108,14 @@ public:
virtual void selectBg(unsigned int idx);
virtual void selectScrollBg(unsigned int idx);
virtual void setScroll(unsigned int shift);
+ virtual uint getScroll() const;
virtual void removeBg(unsigned int idx);
- void saveBg(Common::OutSaveFile &fHandle);
+ virtual void saveBgNames(Common::OutSaveFile &fHandle);
virtual void refreshPalette();
virtual void reloadPalette();
- void restorePalette(Common::InSaveFile &fHandle);
- void savePalette(Common::OutSaveFile &fHandle);
+ virtual void restorePalette(Common::SeekableReadStream &fHandle);
+ virtual void savePalette(Common::OutSaveFile &fHandle);
virtual void rotatePalette(int a, int b, int c);
virtual void transformPalette(int first, int last, int r, int g, int b);
@@ -128,6 +129,7 @@ public:
*/
class OSRenderer : public FWRenderer {
private:
+ // FIXME: Background table's size is probably 8 instead of 9. Check to make sure and correct if necessary.
palBg _bgTable[9]; ///< Table of backgrounds loaded into renderer
byte *_activeHiPal; ///< Active 256 color palette
unsigned int _currentBg; ///< Current background
@@ -163,10 +165,14 @@ public:
void selectBg(unsigned int idx);
void selectScrollBg(unsigned int idx);
void setScroll(unsigned int shift);
+ uint getScroll() const;
void removeBg(unsigned int idx);
+ void saveBgNames(Common::OutSaveFile &fHandle);
void refreshPalette();
void reloadPalette();
+ void restorePalette(Common::SeekableReadStream &fHandle);
+ void savePalette(Common::OutSaveFile &fHandle);
void rotatePalette(int a, int b, int c);
void transformPalette(int first, int last, int r, int g, int b);
diff --git a/engines/cine/main_loop.cpp b/engines/cine/main_loop.cpp
index cfb828cf3c..e5e670c973 100644
--- a/engines/cine/main_loop.cpp
+++ b/engines/cine/main_loop.cpp
@@ -179,6 +179,20 @@ int getKeyData() {
return k;
}
+/** Removes elements from seqList that have their member variable var4 set to value -1. */
+void purgeSeqList() {
+ Common::List<SeqListElement>::iterator it = seqList.begin();
+ while (it != seqList.end()) {
+ if (it->var4 == -1) {
+ // Erase the element and jump to the next element
+ it = seqList.erase(it);
+ } else {
+ // Let the element be and jump to the next element
+ it++;
+ }
+ }
+}
+
void CineEngine::mainLoop(int bootScriptIdx) {
bool playerAction;
uint16 quitFlag;
@@ -186,6 +200,7 @@ void CineEngine::mainLoop(int bootScriptIdx) {
uint16 mouseButton;
quitFlag = 0;
+ exitEngine = 0;
if (_preLoad == false) {
resetBgIncrustList();
@@ -194,7 +209,7 @@ void CineEngine::mainLoop(int bootScriptIdx) {
errorVar = 0;
- addScriptToList0(bootScriptIdx);
+ addScriptToGlobalScripts(bootScriptIdx);
menuVar = 0;
@@ -234,13 +249,25 @@ void CineEngine::mainLoop(int bootScriptIdx) {
do {
stopMusicAfterFadeOut();
di = executePlayerInput();
+
+ // Clear the zoneQuery table (Operation Stealth specific)
+ if (g_cine->getGameType() == Cine::GType_OS) {
+ for (uint i = 0; i < NUM_MAX_ZONE; i++) {
+ zoneQuery[i] = 0;
+ }
+ }
- processSeqList();
- executeList1();
- executeList0();
+ if (g_cine->getGameType() == Cine::GType_OS) {
+ processSeqList();
+ }
+ executeObjectScripts();
+ executeGlobalScripts();
- purgeList1();
- purgeList0();
+ purgeObjectScripts();
+ purgeGlobalScripts();
+ if (g_cine->getGameType() == Cine::GType_OS) {
+ purgeSeqList();
+ }
if (playerCommand == -1) {
setMouseCursor(MOUSE_CURSOR_NORMAL);
diff --git a/engines/cine/object.cpp b/engines/cine/object.cpp
index 7666f05352..c02e01c8ce 100644
--- a/engines/cine/object.cpp
+++ b/engines/cine/object.cpp
@@ -99,21 +99,36 @@ int removeOverlay(uint16 objIdx, uint16 param) {
/*! \brief Add new overlay sprite to the list
* \param objIdx Associate the overlay with this object
- * \param param Type of new overlay
+ * \param type Type of new overlay
* \todo Why are x, y, width and color left uninitialized?
*/
-void addOverlay(uint16 objIdx, uint16 param) {
+void addOverlay(uint16 objIdx, uint16 type) {
Common::List<overlay>::iterator it;
overlay tmp;
for (it = overlayList.begin(); it != overlayList.end(); ++it) {
+ // This is done for both Future Wars and Operation Stealth
if (objectTable[it->objIdx].mask >= objectTable[objIdx].mask) {
break;
}
+
+ // There are additional checks in Operation Stealth's implementation
+ if (g_cine->getGameType() == Cine::GType_OS && (it->type == 2 || it->type == 3)) {
+ break;
+ }
+ }
+
+ // In Operation Stealth's implementation we might bail out early
+ if (g_cine->getGameType() == Cine::GType_OS && it != overlayList.end() && it->objIdx == objIdx && it->type == type) {
+ return;
}
tmp.objIdx = objIdx;
- tmp.type = param;
+ tmp.type = type;
+ tmp.x = 0;
+ tmp.y = 0;
+ tmp.width = 0;
+ tmp.color = 0;
overlayList.insert(it, tmp);
}
@@ -122,24 +137,22 @@ void addOverlay(uint16 objIdx, uint16 param) {
* \param objIdx Associate the overlay with this object
* \param param source background index
*/
-void addGfxElementA0(int16 objIdx, int16 param) {
+void addGfxElement(int16 objIdx, int16 param, int16 type) {
Common::List<overlay>::iterator it;
overlay tmp;
for (it = overlayList.begin(); it != overlayList.end(); ++it) {
- // wtf?!
- if (objectTable[it->objIdx].mask == objectTable[objIdx].mask &&
- (it->type == 2 || it->type == 3)) {
+ if (objectTable[it->objIdx].mask >= objectTable[objIdx].mask || it->type == 2 || it->type == 3) {
break;
}
}
- if (it != overlayList.end() && it->objIdx == objIdx && it->type == 20 && it->x == param) {
+ if (it != overlayList.end() && it->objIdx == objIdx && it->type == type && it->x == param) {
return;
}
tmp.objIdx = objIdx;
- tmp.type = 20;
+ tmp.type = type;
tmp.x = param;
tmp.y = 0;
tmp.width = 0;
@@ -153,11 +166,11 @@ void addGfxElementA0(int16 objIdx, int16 param) {
* \param param Remove overlay using this background
* \todo Check that it works
*/
-void removeGfxElementA0(int16 objIdx, int16 param) {
+void removeGfxElement(int16 objIdx, int16 param, int16 type) {
Common::List<overlay>::iterator it;
for (it = overlayList.begin(); it != overlayList.end(); ++it) {
- if (it->objIdx == objIdx && it->type == 20 && it->x == param) {
+ if (it->objIdx == objIdx && it->type == type && it->x == param) {
overlayList.erase(it);
return;
}
@@ -170,8 +183,12 @@ void setupObject(byte objIdx, uint16 param1, uint16 param2, uint16 param3, uint1
objectTable[objIdx].mask = param3;
objectTable[objIdx].frame = param4;
- if (removeOverlay(objIdx, 0)) {
- addOverlay(objIdx, 0);
+ if (g_cine->getGameType() == Cine::GType_OS) {
+ resetGfxEntityEntry(objIdx);
+ } else { // Future Wars
+ if (removeOverlay(objIdx, 0)) {
+ addOverlay(objIdx, 0);
+ }
}
}
@@ -199,9 +216,12 @@ void modifyObjectParam(byte objIdx, byte paramIdx, int16 newValue) {
case 3:
objectTable[objIdx].mask = newValue;
- // TODO: Check this part against disassembly
- if (removeOverlay(objIdx, 0)) {
- addOverlay(objIdx, 0);
+ if (g_cine->getGameType() == Cine::GType_OS) { // Operation Stealth specific
+ resetGfxEntityEntry(objIdx);
+ } else { // Future Wars specific
+ if (removeOverlay(objIdx, 0)) {
+ addOverlay(objIdx, 0);
+ }
}
break;
case 4:
@@ -221,6 +241,29 @@ void modifyObjectParam(byte objIdx, byte paramIdx, int16 newValue) {
}
}
+/**
+ * Check if at least one of the range B's endpoints is inside range A,
+ * not counting the starting and ending points of range A.
+ * Used at least by Operation Stealth's opcode 0x8D i.e. 141.
+ */
+bool compareRanges(uint16 aStart, uint16 aEnd, uint16 bStart, uint16 bEnd) {
+ return (bStart > aStart && bStart < aEnd) || (bEnd > aStart && bEnd < aEnd);
+}
+
+uint16 compareObjectParamRanges(uint16 objIdx1, uint16 xAdd1, uint16 yAdd1, uint16 maskAdd1, uint16 objIdx2, uint16 xAdd2, uint16 yAdd2, uint16 maskAdd2) {
+ assert(objIdx1 < NUM_MAX_OBJECT && objIdx2 < NUM_MAX_OBJECT);
+ const objectStruct &obj1 = objectTable[objIdx1];
+ const objectStruct &obj2 = objectTable[objIdx2];
+
+ if (compareRanges(obj1.x, obj1.x + xAdd1, obj2.x, obj2.x + xAdd2) &&
+ compareRanges(obj1.y, obj1.y + yAdd1, obj2.y, obj2.y + yAdd2) &&
+ compareRanges(obj1.mask, obj1.mask + maskAdd1, obj2.mask, obj2.mask + maskAdd2)) {
+ return kCmpEQ;
+ } else {
+ return 0;
+ }
+}
+
uint16 compareObjectParam(byte objIdx, byte type, int16 value) {
uint16 compareResult = 0;
int16 objectParam = getObjectParam(objIdx, type);
diff --git a/engines/cine/object.h b/engines/cine/object.h
index e7de39649d..7ad65eb75f 100644
--- a/engines/cine/object.h
+++ b/engines/cine/object.h
@@ -50,7 +50,7 @@ struct overlay {
};
#define NUM_MAX_OBJECT 255
-#define NUM_MAX_VAR 256
+#define NUM_MAX_VAR 255
extern objectStruct objectTable[NUM_MAX_OBJECT];
@@ -60,15 +60,17 @@ void loadObject(char *pObjectName);
void setupObject(byte objIdx, uint16 param1, uint16 param2, uint16 param3, uint16 param4);
void modifyObjectParam(byte objIdx, byte paramIdx, int16 newValue);
-void addOverlay(uint16 objIdx, uint16 param);
+void addOverlay(uint16 objIdx, uint16 type);
int removeOverlay(uint16 objIdx, uint16 param);
-void addGfxElementA0(int16 objIdx, int16 param);
-void removeGfxElementA0(int16 objIdx, int16 param);
+void addGfxElement(int16 objIdx, int16 param, int16 type);
+void removeGfxElement(int16 objIdx, int16 param, int16 type);
int16 getObjectParam(uint16 objIdx, uint16 paramIdx);
void addObjectParam(byte objIdx, byte paramIdx, int16 newValue);
void subObjectParam(byte objIdx, byte paramIdx, int16 newValue);
+bool compareRanges(uint16 aStart, uint16 aEnd, uint16 bStart, uint16 bEnd);
+uint16 compareObjectParamRanges(uint16 objIdx1, uint16 xAdd1, uint16 yAdd1, uint16 maskAdd1, uint16 objIdx2, uint16 xAdd2, uint16 yAdd2, uint16 maskAdd2);
uint16 compareObjectParam(byte objIdx, byte param1, int16 param2);
} // End of namespace Cine
diff --git a/engines/cine/pal.h b/engines/cine/pal.h
index 70fcc0d98a..768cf0d27d 100644
--- a/engines/cine/pal.h
+++ b/engines/cine/pal.h
@@ -34,6 +34,8 @@ struct PalEntry {
byte pal2[16];
};
+extern PalEntry *palPtr;
+
void loadPal(const char *fileName);
void loadRelatedPalette(const char *fileName);
diff --git a/engines/cine/part.cpp b/engines/cine/part.cpp
index b39f1eff7d..88f2dcef52 100644
--- a/engines/cine/part.cpp
+++ b/engines/cine/part.cpp
@@ -289,8 +289,8 @@ void dumpBundle(const char *fileName) {
debug(0, "%s", partBuffer[i].partName);
- Common::File out;
- if (out.open(Common::String("dumps/") + partBuffer[i].partName, Common::File::kFileWriteMode)) {
+ Common::DumpFile out;
+ if (out.open(Common::String("dumps/") + partBuffer[i].partName)) {
out.write(data, partBuffer[i].unpackedSize);
out.close();
}
diff --git a/engines/cine/prc.cpp b/engines/cine/prc.cpp
index 402c97b1a6..27b1044620 100644
--- a/engines/cine/prc.cpp
+++ b/engines/cine/prc.cpp
@@ -40,8 +40,9 @@ ScriptList objectScripts;
/*! \todo Is script size of 0 valid?
* \todo Fix script dump code
+ * @return Was the loading successful?
*/
-void loadPrc(const char *pPrcName) {
+bool loadPrc(const char *pPrcName) {
byte i;
uint16 numScripts;
byte *scriptPtr, *dataPtr;
@@ -52,9 +53,9 @@ void loadPrc(const char *pPrcName) {
scriptTable.clear();
// This is copy protection. Used to hang the machine
- if (!scumm_stricmp(pPrcName, "L201.ANI")) {
+ if (!scumm_stricmp(pPrcName, COPY_PROT_FAIL_PRC_NAME)) {
exitEngine = 1;
- return;
+ return false;
}
checkDataDisk(-1);
@@ -107,6 +108,8 @@ void loadPrc(const char *pPrcName) {
}
}
#endif
+
+ return true;
}
} // End of namespace Cine
diff --git a/engines/cine/prc.h b/engines/cine/prc.h
index f5129d28b1..05bb240372 100644
--- a/engines/cine/prc.h
+++ b/engines/cine/prc.h
@@ -31,7 +31,7 @@ namespace Cine {
extern ScriptList globalScripts;
extern ScriptList objectScripts;
-void loadPrc(const char *pPrcName);
+bool loadPrc(const char *pPrcName);
} // End of namespace Cine
diff --git a/engines/cine/script.h b/engines/cine/script.h
index eeac0e8809..19576e4c1a 100644
--- a/engines/cine/script.h
+++ b/engines/cine/script.h
@@ -61,7 +61,7 @@ private:
public:
// Explicit to prevent var=0 instead of var[i]=0 typos.
explicit ScriptVars(unsigned int len = 50);
- ScriptVars(Common::InSaveFile &fHandle, unsigned int len = 50);
+ ScriptVars(Common::SeekableReadStream &fHandle, unsigned int len = 50);
ScriptVars(const ScriptVars &src);
~ScriptVars(void);
@@ -71,8 +71,8 @@ public:
void save(Common::OutSaveFile &fHandle) const;
void save(Common::OutSaveFile &fHandle, unsigned int len) const;
- void load(Common::InSaveFile &fHandle);
- void load(Common::InSaveFile &fHandle, unsigned int len);
+ void load(Common::SeekableReadStream &fHandle);
+ void load(Common::SeekableReadStream &fHandle, unsigned int len);
void reset(void);
};
@@ -198,7 +198,7 @@ protected:
int o1_blitAndFade();
int o1_fadeToBlack();
int o1_transformPaletteRange();
- int o1_setDefaultMenuColor2();
+ int o1_setDefaultMenuBgColor();
int o1_palRotate();
int o1_break();
int o1_endScript();
@@ -213,7 +213,7 @@ protected:
int o1_initializeZoneData();
int o1_setZoneDataEntry();
int o1_getZoneDataEntry();
- int o1_setDefaultMenuColor();
+ int o1_setPlayerCommandPosY();
int o1_allowPlayerInput();
int o1_disallowPlayerInput();
int o1_changeDataDisk();
@@ -237,7 +237,7 @@ protected:
int o2_playSample();
int o2_playSampleAlt();
int o2_op81();
- int o2_op82();
+ int o2_modifySeqListElement();
int o2_isSeqRunning();
int o2_gotoIfSupNearest();
int o2_gotoIfSupEquNearest();
@@ -258,10 +258,10 @@ protected:
int o2_useBgScroll();
int o2_setAdditionalBgVScroll();
int o2_op9F();
- int o2_addGfxElementA0();
- int o2_removeGfxElementA0();
- int o2_opA2();
- int o2_opA3();
+ int o2_addGfxElementType20();
+ int o2_removeGfxElementType20();
+ int o2_addGfxElementType21();
+ int o2_removeGfxElementType21();
int o2_loadMask22();
int o2_unloadMask22();
@@ -371,16 +371,16 @@ void dumpScript(char *dumpName);
#define OP_requestCheckPendingDataLoad 0x42
#define OP_endScript 0x50
-void addScriptToList0(uint16 idx);
+void addScriptToGlobalScripts(uint16 idx);
int16 checkCollision(int16 objIdx, int16 x, int16 y, int16 numZones, int16 zoneIdx);
void runObjectScript(int16 entryIdx);
-void executeList1(void);
-void executeList0(void);
+void executeObjectScripts(void);
+void executeGlobalScripts(void);
-void purgeList1(void);
-void purgeList0(void);
+void purgeObjectScripts(void);
+void purgeGlobalScripts(void);
} // End of namespace Cine
diff --git a/engines/cine/script_fw.cpp b/engines/cine/script_fw.cpp
index 845120c99e..e761a0c8e4 100644
--- a/engines/cine/script_fw.cpp
+++ b/engines/cine/script_fw.cpp
@@ -38,7 +38,13 @@
namespace Cine {
-ScriptVars globalVars(NUM_MAX_VAR);
+/**
+ * Global variables.
+ * 255 of these are saved, but there's one more that's used for bypassing the copy protection.
+ * In CineEngine::mainLoop(int bootScriptIdx) there's this code: globalVars[VAR_BYPASS_PROTECTION] = 0;
+ * And as VAR_BYPASS_PROTECTION is 255 that's why we're allocating one more than we otherwise would.
+ */
+ScriptVars globalVars(NUM_MAX_VAR + 1);
uint16 compareVars(int16 a, int16 b);
@@ -135,7 +141,7 @@ const Opcode FWScript::_opcodeTable[] = {
{ &FWScript::o1_transformPaletteRange, "bbwww" },
/* 48 */
{ 0, 0 },
- { &FWScript::o1_setDefaultMenuColor2, "b" },
+ { &FWScript::o1_setDefaultMenuBgColor, "b" },
{ &FWScript::o1_palRotate, "bbb" },
{ 0, 0 },
/* 4C */
@@ -174,7 +180,7 @@ const Opcode FWScript::_opcodeTable[] = {
{ &FWScript::o1_setZoneDataEntry, "bw" },
{ &FWScript::o1_getZoneDataEntry, "bb" },
/* 68 */
- { &FWScript::o1_setDefaultMenuColor, "b" },
+ { &FWScript::o1_setPlayerCommandPosY, "b" },
{ &FWScript::o1_allowPlayerInput, "" },
{ &FWScript::o1_disallowPlayerInput, "" },
{ &FWScript::o1_changeDataDisk, "b" },
@@ -230,7 +236,7 @@ ScriptVars::ScriptVars(unsigned int len) : _size(len), _vars(new int16[len]) {
* \param fHandle Savefile open for reading
* \param len Size of array
*/
-ScriptVars::ScriptVars(Common::InSaveFile &fHandle, unsigned int len)
+ScriptVars::ScriptVars(Common::SeekableReadStream &fHandle, unsigned int len)
: _size(len), _vars(new int16[len]) {
assert(_vars);
@@ -306,7 +312,7 @@ void ScriptVars::save(Common::OutSaveFile &fHandle, unsigned int len) const {
/*! \brief Restore array from savefile
* \param fHandle Savefile open for reading
*/
-void ScriptVars::load(Common::InSaveFile &fHandle) {
+void ScriptVars::load(Common::SeekableReadStream &fHandle) {
load(fHandle, _size);
}
@@ -314,7 +320,7 @@ void ScriptVars::load(Common::InSaveFile &fHandle) {
* \param fHandle Savefile open for reading
* \param len Length of data to be read
*/
-void ScriptVars::load(Common::InSaveFile &fHandle, unsigned int len) {
+void ScriptVars::load(Common::SeekableReadStream &fHandle, unsigned int len) {
debug(6, "assert(%d <= %d)", len, _size);
assert(len <= _size);
for (unsigned int i = 0; i < len; i++) {
@@ -1019,6 +1025,20 @@ int FWScript::o1_divVar() {
}
int FWScript::o1_compareVar() {
+ // WORKAROUND: A workaround for a script bug in script file CODE2.PRC
+ // in at least some of the Amiga and Atari ST versions of Future Wars.
+ // Fixes bug #2016647 (FW: crash with italian amiga version). A local
+ // variable 251 is compared against value 0 although it's quite apparent
+ // from the context in the script that instead global variable 251 should
+ // be compared against value 0. So looks like someone made a typo when
+ // making the scripts. Therefore we change that particular comparison
+ // from using the local variable 251 to using the global variable 251.
+ if (g_cine->getGameType() == Cine::GType_FW && scumm_stricmp(currentPrcName, "CODE2.PRC") == 0 &&
+ (g_cine->getPlatform() == Common::kPlatformAmiga || g_cine->getPlatform() == Common::kPlatformAtariST) &&
+ _script.getByte(_pos) == 251 && _script.getByte(_pos + 1) == 0 && _script.getWord(_pos + 2) == 0) {
+ return o1_compareGlobalVar();
+ }
+
byte varIdx = getNextByte();
byte varType = getNextByte();
@@ -1259,7 +1279,7 @@ int FWScript::o1_startGlobalScript() {
assert(param < NUM_MAX_SCRIPT);
debugC(5, kCineDebugScript, "Line: %d: startScript(%d)", _line, param);
- addScriptToList0(param);
+ addScriptToGlobalScripts(param);
return 0;
}
@@ -1385,10 +1405,11 @@ int FWScript::o1_transformPaletteRange() {
return 0;
}
-int FWScript::o1_setDefaultMenuColor2() {
+/** Set the default background color used for message boxes. */
+int FWScript::o1_setDefaultMenuBgColor() {
byte param = getNextByte();
- debugC(5, kCineDebugScript, "Line: %d: setDefaultMenuColor2(%d)", _line, param);
+ debugC(5, kCineDebugScript, "Line: %d: setDefaultMenuBgColor(%d)", _line, param);
renderer->_messageBg = param;
return 0;
@@ -1554,10 +1575,11 @@ int FWScript::o1_getZoneDataEntry() {
return 0;
}
-int FWScript::o1_setDefaultMenuColor() {
+/** Set the player command string's vertical position on-screen. */
+int FWScript::o1_setPlayerCommandPosY() {
byte param = getNextByte();
- debugC(5, kCineDebugScript, "Line: %d: setDefaultMenuColor(%d)", _line, param);
+ debugC(5, kCineDebugScript, "Line: %d: setPlayerCommandPosY(%d)", _line, param);
renderer->_cmdY = param;
return 0;
@@ -1732,7 +1754,7 @@ int FWScript::o1_unloadMask5() {
//-----------------------------------------------------------------------
-void addScriptToList0(uint16 idx) {
+void addScriptToGlobalScripts(uint16 idx) {
ScriptPtr tmp(scriptInfo->create(*scriptTable[idx], idx));
assert(tmp);
globalScripts.push_back(tmp);
@@ -1764,18 +1786,32 @@ int16 checkCollision(int16 objIdx, int16 x, int16 y, int16 numZones, int16 zoneI
int16 lx = objectTable[objIdx].x + x;
int16 ly = objectTable[objIdx].y + y;
int16 idx;
+ int16 result = 0;
for (int16 i = 0; i < numZones; i++) {
idx = getZoneFromPositionRaw(page3Raw, lx + i, ly, 320);
- assert(idx >= 0 && idx <= NUM_MAX_ZONE);
+ assert(idx >= 0 && idx < NUM_MAX_ZONE);
+
+ // The zoneQuery table is updated here only in Operation Stealth
+ if (g_cine->getGameType() == Cine::GType_OS) {
+ if (zoneData[idx] < NUM_MAX_ZONE) {
+ zoneQuery[zoneData[idx]]++;
+ }
+ }
if (zoneData[idx] == zoneIdx) {
- return 1;
+ result = 1;
+ // Future Wars breaks out early on the first match, but
+ // Operation Stealth doesn't because it needs to update
+ // the zoneQuery table for the whole loop's period.
+ if (g_cine->getGameType() == Cine::GType_FW) {
+ break;
+ }
}
}
- return 0;
+ return result;
}
uint16 compareVars(int16 a, int16 b) {
@@ -1792,7 +1828,7 @@ uint16 compareVars(int16 a, int16 b) {
return flag;
}
-void executeList1(void) {
+void executeObjectScripts(void) {
ScriptList::iterator it = objectScripts.begin();
for (; it != objectScripts.end();) {
if ((*it)->_index < 0 || (*it)->execute() < 0) {
@@ -1803,7 +1839,7 @@ void executeList1(void) {
}
}
-void executeList0(void) {
+void executeGlobalScripts(void) {
ScriptList::iterator it = globalScripts.begin();
for (; it != globalScripts.end();) {
if ((*it)->_index < 0 || (*it)->execute() < 0) {
@@ -1814,12 +1850,16 @@ void executeList0(void) {
}
}
-/*! \todo objectScripts.clear()?
+/*! \todo Remove object scripts with script index of -1 (Not script position, but script index!).
+ * This would seem to be valid for both Future Wars and Operation Stealth.
*/
-void purgeList1(void) {
+void purgeObjectScripts(void) {
}
-void purgeList0(void) {
+/*! \todo Remove global scripts with script index of -1 (Not script position, but script index!).
+ * This would seem to be valid for both Future Wars and Operation Stealth.
+ */
+void purgeGlobalScripts(void) {
}
////////////////////////////////////
@@ -2352,7 +2392,7 @@ void decompileScript(const byte *scriptPtr, uint16 scriptSize, uint16 scriptIdx)
param = *(localScriptPtr + position);
position++;
- sprintf(lineBuffer, "setDefaultMenuColor2(%d)\n", param);
+ sprintf(lineBuffer, "setDefaultMenuBgColor(%d)\n", param);
break;
}
@@ -2502,7 +2542,7 @@ void decompileScript(const byte *scriptPtr, uint16 scriptSize, uint16 scriptIdx)
param = *(localScriptPtr + position);
position++;
- sprintf(lineBuffer, "setDefaultMenuBoxColor(%d)\n", param);
+ sprintf(lineBuffer, "setPlayerCommandPosY(%d)\n", param);
break;
}
@@ -2917,10 +2957,10 @@ void decompileScript(const byte *scriptPtr, uint16 scriptSize, uint16 scriptIdx)
}
void dumpScript(char *dumpName) {
- Common::File fHandle;
+ Common::DumpFile fHandle;
uint16 i;
- fHandle.open(dumpName, Common::File::kFileWriteMode);
+ fHandle.open(dumpName);
for (i = 0; i < decompileBufferPosition; i++) {
fHandle.writeString(Common::String(decompileBuffer[i]));
diff --git a/engines/cine/script_os.cpp b/engines/cine/script_os.cpp
index 319fca5d3c..a764281758 100644
--- a/engines/cine/script_os.cpp
+++ b/engines/cine/script_os.cpp
@@ -131,7 +131,7 @@ const Opcode OSScript::_opcodeTable[] = {
{ &FWScript::o1_transformPaletteRange, "bbwww" },
/* 48 */
{ 0, 0 },
- { &FWScript::o1_setDefaultMenuColor2, "b" },
+ { &FWScript::o1_setDefaultMenuBgColor, "b" },
{ &FWScript::o1_palRotate, "bbb" },
{ 0, 0 },
/* 4C */
@@ -170,7 +170,7 @@ const Opcode OSScript::_opcodeTable[] = {
{ &FWScript::o1_setZoneDataEntry, "bw" },
{ &FWScript::o1_getZoneDataEntry, "bb" },
/* 68 */
- { &FWScript::o1_setDefaultMenuColor, "b" },
+ { &FWScript::o1_setPlayerCommandPosY, "b" },
{ &FWScript::o1_allowPlayerInput, "" },
{ &FWScript::o1_disallowPlayerInput, "" },
{ &FWScript::o1_changeDataDisk, "b" }, /* Same as opcodes 0x95 and 0xA9. */
@@ -202,7 +202,7 @@ const Opcode OSScript::_opcodeTable[] = {
/* 80 */
{ &FWScript::o2_removeSeq, "bb" },
{ &FWScript::o2_op81, "" }, /* TODO: Name this opcode properly. */
- { &FWScript::o2_op82, "bbwwb" }, /* TODO: Name this opcode properly. */
+ { &FWScript::o2_modifySeqListElement, "bbwwb" },
{ &FWScript::o2_isSeqRunning, "bb" },
/* 84 */
{ &FWScript::o2_gotoIfSupNearest, "b" },
@@ -240,10 +240,10 @@ const Opcode OSScript::_opcodeTable[] = {
{ &FWScript::o2_setAdditionalBgVScroll, "c" },
{ &FWScript::o2_op9F, "ww" }, /* TODO: Name this opcode properly. */
/* A0 */
- { &FWScript::o2_addGfxElementA0, "ww" }, /* TODO: Name this opcode properly. */
- { &FWScript::o2_removeGfxElementA0, "ww" }, /* TODO: Name this opcode properly. */
- { &FWScript::o2_opA2, "ww" }, /* TODO: Name this opcode properly. */
- { &FWScript::o2_opA3, "ww" }, /* TODO: Name this opcode properly. */
+ { &FWScript::o2_addGfxElementType20, "ww" }, /* TODO: Name this opcode properly. */
+ { &FWScript::o2_removeGfxElementType20, "ww" }, /* TODO: Name this opcode properly. */
+ { &FWScript::o2_addGfxElementType21, "ww" }, /* TODO: Name this opcode properly. */
+ { &FWScript::o2_removeGfxElementType21, "ww" }, /* TODO: Name this opcode properly. */
/* A4 */
{ &FWScript::o2_loadMask22, "b" }, /* TODO: Name this opcode properly. */
{ &FWScript::o2_unloadMask22, "b" }, /* TODO: Name this opcode properly. */
@@ -442,6 +442,7 @@ int FWScript::o2_removeSeq() {
}
/*! \todo Implement this instruction
+ * \note According to the scripts' opcode usage comparison this opcode isn't used at all.
*/
int FWScript::o2_op81() {
warning("STUB: o2_op81()");
@@ -449,23 +450,25 @@ int FWScript::o2_op81() {
return 0;
}
-/*! \todo Implement this instruction
- */
-int FWScript::o2_op82() {
+int FWScript::o2_modifySeqListElement() {
byte a = getNextByte();
byte b = getNextByte();
uint16 c = getNextWord();
uint16 d = getNextWord();
byte e = getNextByte();
- warning("STUB: o2_op82(%x, %x, %x, %x, %x)", a, b, c, d, e);
+ debugC(5, kCineDebugScript, "Line: %d: o2_modifySeqListElement(%d,%d,%d,%d,%d)", _line, a, b, c, d, e);
+
+ modifySeqListElement(a, 0, b, c, d, e);
return 0;
}
+/*! \todo Check whether this opcode's name is backwards (i.e. should it be o2_isSeqNotRunning?)
+ */
int FWScript::o2_isSeqRunning() {
byte a = getNextByte();
byte b = getNextByte();
- debugC(5, kCineDebugScript, "Line: %d: OP83(%d,%d) -> TODO", _line, a, b);
+ debugC(5, kCineDebugScript, "Line: %d: o2_isSeqRunning(%d,%d)", _line, a, b);
if (isSeqRunning(a, 0, b)) {
_compare = 1;
@@ -593,19 +596,18 @@ int FWScript::o2_stopObjectScript() {
return 0;
}
-/*! \todo Implement this instruction
- */
int FWScript::o2_op8D() {
- uint16 a = getNextWord();
- uint16 b = getNextWord();
- uint16 c = getNextWord();
- uint16 d = getNextWord();
- uint16 e = getNextWord();
- uint16 f = getNextWord();
- uint16 g = getNextWord();
- uint16 h = getNextWord();
- warning("STUB: o2_op8D(%x, %x, %x, %x, %x, %x, %x, %x)", a, b, c, d, e, f, g, h);
- // _currentScriptElement->compareResult = ...
+ uint16 objIdx1 = getNextWord();
+ uint16 xAdd1 = getNextWord();
+ uint16 yAdd1 = getNextWord();
+ uint16 maskAdd1 = getNextWord();
+ uint16 objIdx2 = getNextWord();
+ uint16 xAdd2 = getNextWord();
+ uint16 yAdd2 = getNextWord();
+ uint16 maskAdd2 = getNextWord();
+ debugC(5, kCineDebugScript, "Line: %d: o2_op8D(%d, %d, %d, %d, %d, %d, %d, %d)", _line, objIdx1, xAdd1, yAdd1, maskAdd1, objIdx2, xAdd2, yAdd2, maskAdd2);
+
+ _compare = compareObjectParamRanges(objIdx1, xAdd1, yAdd1, maskAdd1, objIdx2, xAdd2, yAdd2, maskAdd2);
return 0;
}
@@ -649,16 +651,15 @@ int FWScript::o2_loadBg() {
return 0;
}
-/*! \todo Check the current implementation for correctness
- */
int FWScript::o2_wasZoneChecked() {
byte param = getNextByte();
- _compare = (param < 16 && zoneData[param]);
+ _compare = (param < NUM_MAX_ZONE && zoneQuery[param]) ? 1 : 0;
debugC(5, kCineDebugScript, "Line: %d: o2_wasZoneChecked(%d)", _line, param);
return 0;
}
/*! \todo Implement this instruction
+ * \note According to the scripts' opcode usage comparison this opcode isn't used at all.
*/
int FWScript::o2_op9B() {
uint16 a = getNextWord();
@@ -674,6 +675,7 @@ int FWScript::o2_op9B() {
}
/*! \todo Implement this instruction
+ * \note According to the scripts' opcode usage comparison this opcode isn't used at all.
*/
int FWScript::o2_op9C() {
uint16 a = getNextWord();
@@ -713,6 +715,7 @@ int FWScript::o2_setAdditionalBgVScroll() {
}
/*! \todo Implement this instruction
+ * \note According to the scripts' opcode usage comparison this opcode isn't used at all.
*/
int FWScript::o2_op9F() {
warning("o2_op9F()");
@@ -721,42 +724,36 @@ int FWScript::o2_op9F() {
return 0;
}
-int FWScript::o2_addGfxElementA0() {
+int FWScript::o2_addGfxElementType20() {
uint16 param1 = getNextWord();
uint16 param2 = getNextWord();
- debugC(5, kCineDebugScript, "Line: %d: addGfxElementA0(%d,%d)", _line, param1, param2);
- addGfxElementA0(param1, param2);
+ debugC(5, kCineDebugScript, "Line: %d: o2_addGfxElementType20(%d,%d)", _line, param1, param2);
+ addGfxElement(param1, param2, 20);
return 0;
}
-/*! \todo Implement this instruction
- */
-int FWScript::o2_removeGfxElementA0() {
+int FWScript::o2_removeGfxElementType20() {
uint16 idx = getNextWord();
uint16 param = getNextWord();
- warning("STUB? o2_removeGfxElementA0(%x, %x)", idx, param);
- removeGfxElementA0(idx, param);
+ debugC(5, kCineDebugScript, "Line: %d: o2_removeGfxElementType20(%d,%d)", _line, idx, param);
+ removeGfxElement(idx, param, 20);
return 0;
}
-/*! \todo Implement this instruction
- */
-int FWScript::o2_opA2() {
+int FWScript::o2_addGfxElementType21() {
uint16 a = getNextWord();
uint16 b = getNextWord();
- warning("STUB: o2_opA2(%x, %x)", a, b);
- // addGfxElementA2();
+ debugC(5, kCineDebugScript, "Line: %d: o2_addGfxElementType21(%d,%d)", _line, a, b);
+ addGfxElement(a, b, 21);
return 0;
}
-/*! \todo Implement this instruction
- */
-int FWScript::o2_opA3() {
+int FWScript::o2_removeGfxElementType21() {
uint16 a = getNextWord();
uint16 b = getNextWord();
- warning("STUB: o2_opA3(%x, %x)", a, b);
- // removeGfxElementA2();
+ debugC(5, kCineDebugScript, "Line: %d: o2_removeGfxElementType21(%d,%d)", _line, a, b);
+ removeGfxElement(a, b, 21);
return 0;
}
diff --git a/engines/cine/sound.cpp b/engines/cine/sound.cpp
index e808de6922..f26032fe98 100644
--- a/engines/cine/sound.cpp
+++ b/engines/cine/sound.cpp
@@ -249,6 +249,7 @@ AdlibSoundDriver::AdlibSoundDriver(Audio::Mixer *mixer)
AdlibSoundDriver::~AdlibSoundDriver() {
_mixer->stopHandle(_soundHandle);
+ OPLDestroy(_opl);
}
void AdlibSoundDriver::setupChannel(int channel, const byte *data, int instrument, int volume) {
diff --git a/engines/cine/texte.cpp b/engines/cine/texte.cpp
index 9b4b83f420..e4fd334926 100644
--- a/engines/cine/texte.cpp
+++ b/engines/cine/texte.cpp
@@ -31,8 +31,6 @@ namespace Cine {
byte *textDataPtr;
-byte textTable[256][2][16 * 8];
-
const char **failureMessages;
const CommandeType *defaultActionCommand;
const CommandeType *systemMenu;
@@ -77,14 +75,14 @@ void loadTextData(const char *pFileName, byte *pDestinationBuffer) {
loadRelatedPalette(pFileName);
for (i = 0; i < numCharacters; i++) {
- gfxConvertSpriteToRaw(textTable[i][0], tempBuffer, 16, 8);
- generateMask(textTable[i][0], textTable[i][1], 16 * 8, 0);
+ gfxConvertSpriteToRaw(g_cine->_textHandler.textTable[i][0], tempBuffer, 16, 8);
+ generateMask(g_cine->_textHandler.textTable[i][0], g_cine->_textHandler.textTable[i][1], 16 * 8, 0);
tempBuffer += dataSize;
}
} else {
for (i = 0; i < 90; i++) {
- gfxConvertSpriteToRaw(textTable[i][0], tempBuffer, 8, 8);
- generateMask(textTable[i][0], textTable[i][1], 8 * 8, 0);
+ gfxConvertSpriteToRaw(g_cine->_textHandler.textTable[i][0], tempBuffer, 8, 8);
+ generateMask(g_cine->_textHandler.textTable[i][0], g_cine->_textHandler.textTable[i][1], 8 * 8, 0);
tempBuffer += 0x40;
}
}
diff --git a/engines/cine/texte.h b/engines/cine/texte.h
index ae82832aea..f471c3c49e 100644
--- a/engines/cine/texte.h
+++ b/engines/cine/texte.h
@@ -34,7 +34,10 @@ namespace Cine {
typedef char CommandeType[20];
extern byte *textDataPtr;
-extern byte textTable[256][2][16 * 8];
+
+struct TextHandler {
+ byte textTable[256][2][16 * 8];
+};
extern const char **failureMessages;
extern const CommandeType *defaultActionCommand;
diff --git a/engines/cine/unpack.cpp b/engines/cine/unpack.cpp
index dcd3181242..5d85ff6cab 100644
--- a/engines/cine/unpack.cpp
+++ b/engines/cine/unpack.cpp
@@ -111,7 +111,7 @@ bool CineUnpacker::unpack(const byte *src, uint srcLen, byte *dst, uint dstLen)
while (_dst >= _dstBegin && !_error) {
/*
Bits => Action:
- 0 0 => unpackRawBytes(3 bits + 1) i.e. unpackRawBytes(1..9)
+ 0 0 => unpackRawBytes(3 bits + 1) i.e. unpackRawBytes(1..8)
1 1 1 => unpackRawBytes(8 bits + 9) i.e. unpackRawBytes(9..264)
0 1 => copyRelocatedBytes(8 bits, 2) i.e. copyRelocatedBytes(0..255, 2)
1 0 0 => copyRelocatedBytes(9 bits, 3) i.e. copyRelocatedBytes(0..511, 3)
diff --git a/engines/cine/various.cpp b/engines/cine/various.cpp
index 9b98ddb253..2fcb015fcd 100644
--- a/engines/cine/various.cpp
+++ b/engines/cine/various.cpp
@@ -95,6 +95,9 @@ int16 saveVar2;
byte isInPause = 0;
+// TODO: Implement inputVar0's changes in the program
+// Currently inputVar0 isn't updated anywhere even though it's used at least in processSeqListElement.
+uint16 inputVar0 = 0;
byte inputVar1 = 0;
uint16 inputVar2 = 0, inputVar3 = 0;
@@ -112,6 +115,7 @@ int16 objListTab[20];
uint16 exitEngine;
uint16 zoneData[NUM_MAX_ZONE];
+uint16 zoneQuery[NUM_MAX_ZONE]; //!< Only exists in Operation Stealth
void stopMusicAfterFadeOut(void) {
@@ -132,6 +136,7 @@ void runObjectScript(int16 entryIdx) {
*/
void addPlayerCommandMessage(int16 cmd) {
overlay tmp;
+ memset(&tmp, 0, sizeof(tmp));
tmp.objIdx = cmd;
tmp.type = 3;
@@ -224,6 +229,143 @@ 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]);
+ }
+}
+
+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];
@@ -241,21 +383,143 @@ bool CineEngine::loadSaveDirectory(void) {
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
+ uint32 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 handlem open for reading
+ * \param fHandle Savefile handle open for reading
* \param isGlobal Restore object or global script?
*/
-void loadScriptFromSave(Common::InSaveFile *fHandle, bool isGlobal) {
+void loadScriptFromSave(Common::SeekableReadStream &fHandle, bool isGlobal) {
ScriptVars localVars, labels;
uint16 compare, pos;
int16 idx;
- labels.load(*fHandle);
- localVars.load(*fHandle);
+ labels.load(fHandle);
+ localVars.load(fHandle);
- compare = fHandle->readUint16BE();
- pos = fHandle->readUint16BE();
- idx = fHandle->readUint16BE();
+ compare = fHandle.readUint16BE();
+ pos = fHandle.readUint16BE();
+ idx = fHandle.readUint16BE();
// no way to reinitialize these
if (idx < 0) {
@@ -278,7 +542,7 @@ void loadScriptFromSave(Common::InSaveFile *fHandle, bool isGlobal) {
/*! \brief Restore overlay sprites from savefile
* \param fHandle Savefile open for reading
*/
-void loadOverlayFromSave(Common::InSaveFile &fHandle) {
+void loadOverlayFromSave(Common::SeekableReadStream &fHandle) {
overlay tmp;
fHandle.readUint32BE();
@@ -294,127 +558,10 @@ void loadOverlayFromSave(Common::InSaveFile &fHandle) {
overlayList.push_back(tmp);
}
-/*! \brief Savefile format tester
- * \param fHandle Savefile to check
- *
- * This function seeks through savefile and tries to guess if it's the original
- * savegame format or broken format from ScummVM 0.10/0.11
- * The test is incomplete but this should cover 99.99% of cases.
- * If anyone makes a savefile which could confuse this test, assert will
- * report it
- */
-bool brokenSave(Common::InSaveFile &fHandle) {
- // Backward seeking not supported in compressed savefiles
- // if you really want it, finish it yourself
- return false;
-
- // fixed size part: 14093 bytes (12308 bytes in broken save)
- // animDataTable begins at byte 6431
-
- int filesize = fHandle.size();
- int startpos = fHandle.pos();
- int pos, tmp;
- bool correct = false, broken = false;
-
- // check for correct format
- while (filesize > 14093) {
- pos = 14093;
-
- fHandle.seek(pos);
- tmp = fHandle.readUint16BE();
- pos += 2 + tmp * 206;
- if (pos >= filesize) break;
-
- fHandle.seek(pos);
- tmp = fHandle.readUint16BE();
- pos += 2 + tmp * 206;
- if (pos >= filesize) break;
-
- fHandle.seek(pos);
- tmp = fHandle.readUint16BE();
- pos += 2 + tmp * 20;
- if (pos >= filesize) break;
-
- fHandle.seek(pos);
- tmp = fHandle.readUint16BE();
- pos += 2 + tmp * 20;
-
- if (pos == filesize) correct = true;
- break;
- }
- debug(5, "brokenSave: correct format check %s: size=%d, pos=%d",
- correct ? "passed" : "failed", filesize, pos);
-
- // check for broken format
- while (filesize > 12308) {
- pos = 12308;
-
- fHandle.seek(pos);
- tmp = fHandle.readUint16BE();
- pos += 2 + tmp * 206;
- if (pos >= filesize) break;
-
- fHandle.seek(pos);
- tmp = fHandle.readUint16BE();
- pos += 2 + tmp * 206;
- if (pos >= filesize) break;
-
- fHandle.seek(pos);
- tmp = fHandle.readUint16BE();
- pos += 2 + tmp * 20;
- if (pos >= filesize) break;
-
- fHandle.seek(pos);
- tmp = fHandle.readUint16BE();
- pos += 2 + tmp * 20;
-
- if (pos == filesize) broken = true;
- break;
- }
- debug(5, "brokenSave: broken format check %s: size=%d, pos=%d",
- broken ? "passed" : "failed", filesize, pos);
-
- // there's a very small chance that both cases will match
- // if anyone runs into it, you'll have to walk through
- // the animDataTable and try to open part file for each entry
- if (!correct && !broken) {
- error("brokenSave: file format check failed");
- } else if (correct && broken) {
- error("brokenSave: both file formats seem to apply");
- }
-
- fHandle.seek(startpos);
- debug(5, "brokenSave: detected %s file format",
- correct ? "correct" : "broken");
-
- return broken;
-}
-
-/*! \todo Implement Operation Stealth loading, this is obviously Future Wars only
- */
-bool CineEngine::makeLoad(char *saveName) {
- int16 i;
- int16 size;
- bool broken;
- Common::InSaveFile *fHandle;
- char bgName[13];
-
- fHandle = g_saveFileMan->openForLoading(saveName);
-
- if (!fHandle) {
- drawString(otherMessages[0], 0);
- waitPlayerInput();
- // restoreScreen();
- checkDataDisk(-1);
- return false;
- }
-
+void CineEngine::resetEngine() {
g_sound->stopMusic();
freeAnimDataTable();
overlayList.clear();
- // if (g_cine->getGameType() == Cine::GType_OS) {
- // freeUnkList();
- // }
bgIncrustList.clear();
closePart();
@@ -424,7 +571,9 @@ bool CineEngine::makeLoad(char *saveName) {
scriptTable.clear();
messageTable.clear();
- for (i = 0; i < NUM_MAX_OBJECT; i++) {
+ for (int i = 0; i < NUM_MAX_OBJECT; i++) {
+ objectTable[i].x = 0;
+ objectTable[i].y = 0;
objectTable[i].part = 0;
objectTable[i].name[0] = 0;
objectTable[i].frame = 0;
@@ -458,124 +607,381 @@ bool CineEngine::makeLoad(char *saveName) {
checkForPendingDataLoadSwitch = 0;
- broken = brokenSave(*fHandle);
+ if (g_cine->getGameType() == Cine::GType_OS) {
+ seqList.clear();
+ currentAdditionalBgIdx = 0;
+ currentAdditionalBgIdx2 = 0;
+ // TODO: Add resetting of the following variables
+ // adBgVar1 = 0;
+ // adBgVar0 = 0;
+ // gfxFadeOutCompleted = 0;
+ }
+}
- currentDisk = fHandle->readUint16BE();
+bool loadObjectTable(Common::SeekableReadStream &in) {
+ in.readUint16BE(); // Entry count
+ in.readUint16BE(); // Entry size
- fHandle->read(currentPartName, 13);
- fHandle->read(currentDatName, 13);
+ 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();
+}
- saveVar2 = fHandle->readSint16BE();
+bool loadZoneData(Common::SeekableReadStream &in) {
+ for (int i = 0; i < 16; i++) {
+ zoneData[i] = in.readUint16BE();
+ }
+ return !in.ioFailed();
+}
- fHandle->read(currentPrcName, 13);
- fHandle->read(currentRelName, 13);
- fHandle->read(currentMsgName, 13);
- fHandle->read(bgName, 13);
- fHandle->read(currentCtName, 13);
+bool loadCommandVariables(Common::SeekableReadStream &in) {
+ for (int i = 0; i < 4; i++) {
+ commandVar3[i] = in.readUint16BE();
+ }
+ return !in.ioFailed();
+}
- checkDataDisk(currentDisk);
+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();
+}
- if (strlen(currentPartName)) {
- loadPart(currentPartName);
+bool loadGlobalScripts(Common::SeekableReadStream &in) {
+ int size = in.readSint16BE();
+ for (int i = 0; i < size; i++) {
+ loadScriptFromSave(in, true);
}
+ return !in.ioFailed();
+}
- if (strlen(currentPrcName)) {
- loadPrc(currentPrcName);
+bool loadObjectScripts(Common::SeekableReadStream &in) {
+ int size = in.readSint16BE();
+ for (int i = 0; i < size; i++) {
+ loadScriptFromSave(in, false);
}
+ return !in.ioFailed();
+}
- if (strlen(currentRelName)) {
- loadRel(currentRelName);
+bool loadOverlayList(Common::SeekableReadStream &in) {
+ int size = in.readSint16BE();
+ for (int i = 0; i < size; i++) {
+ loadOverlayFromSave(in);
}
+ return !in.ioFailed();
+}
- if (strlen(bgName)) {
- loadBg(bgName);
- }
+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();
+}
- if (strlen(currentCtName)) {
- loadCtFW(currentCtName);
+bool loadZoneQuery(Common::SeekableReadStream &in) {
+ for (int i = 0; i < 16; i++) {
+ zoneQuery[i] = in.readUint16BE();
}
+ return !in.ioFailed();
+}
- fHandle->readUint16BE();
- fHandle->readUint16BE();
+bool CineEngine::loadTempSaveOS(Common::SeekableReadStream &in) {
+ char musicName[13];
+ char bgNames[8][13];
- for (i = 0; i < 255; i++) {
- objectTable[i].x = fHandle->readSint16BE();
- objectTable[i].y = fHandle->readSint16BE();
- objectTable[i].mask = fHandle->readUint16BE();
- objectTable[i].frame = fHandle->readSint16BE();
- objectTable[i].costume = fHandle->readSint16BE();
- fHandle->read(objectTable[i].name, 20);
- objectTable[i].part = fHandle->readUint16BE();
+ // 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;
}
- renderer->restorePalette(*fHandle);
+ // 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]);
+ }
- globalVars.load(*fHandle, NUM_MAX_VAR - 1);
+ // Add backgrounds 1-7 (Uses addBackground)
+ for (int i = 1; i < 8; i++) {
+ if (strlen(bgNames[i])) {
+ addBackground(bgNames[i], i);
+ }
+ }
- for (i = 0; i < 16; i++) {
- zoneData[i] = fHandle->readUint16BE();
+ if (strlen(currentCtName)) {
+ loadCtOS(currentCtName);
+ }
}
- for (i = 0; i < 4; i++) {
- commandVar3[i] = fHandle->readUint16BE();
+ loadObjectTable(in);
+ renderer->restorePalette(in);
+ globalVars.load(in, NUM_MAX_VAR);
+ loadZoneData(in);
+ loadCommandVariables(in);
+ in.read(commandBuffer, 0x50);
+ 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);
}
- fHandle->read(commandBuffer, 0x50);
- renderer->setCommand(commandBuffer);
+ // TODO: Add current music loading and playing here
+ // TODO: Palette handling?
- renderer->_cmdY = fHandle->readUint16BE();
+ 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");
+ }
- bgVar0 = fHandle->readUint16BE();
- allowPlayerInput = fHandle->readUint16BE();
- playerCommand = fHandle->readSint16BE();
- commandVar1 = fHandle->readSint16BE();
- isDrawCommandEnabled = fHandle->readUint16BE();
- var5 = fHandle->readUint16BE();
- var4 = fHandle->readUint16BE();
- var3 = fHandle->readUint16BE();
- var2 = fHandle->readUint16BE();
- commandVar2 = fHandle->readSint16BE();
+ return !in.ioFailed();
+}
- renderer->_messageBg = fHandle->readUint16BE();
+bool CineEngine::loadPlainSaveFW(Common::SeekableReadStream &in, CineSaveGameFormat saveGameFormat) {
+ char bgName[13];
- fHandle->readUint16BE();
- fHandle->readUint16BE();
+ // At savefile position 0x0000:
+ currentDisk = in.readUint16BE();
- loadResourcesFromSave(*fHandle, broken);
+ // At 0x0002:
+ in.read(currentPartName, 13);
+ // At 0x000F:
+ in.read(currentDatName, 13);
- // TODO: handle screen params (really required ?)
- fHandle->readUint16BE();
- fHandle->readUint16BE();
- fHandle->readUint16BE();
- fHandle->readUint16BE();
- fHandle->readUint16BE();
- fHandle->readUint16BE();
+ // At 0x001C:
+ saveVar2 = in.readSint16BE();
- size = fHandle->readSint16BE();
- for (i = 0; i < size; i++) {
- loadScriptFromSave(fHandle, true);
+ // 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);
}
- size = fHandle->readSint16BE();
- for (i = 0; i < size; i++) {
- loadScriptFromSave(fHandle, false);
+ if (strlen(currentPrcName)) {
+ loadPrc(currentPrcName);
}
- size = fHandle->readSint16BE();
- for (i = 0; i < size; i++) {
- loadOverlayFromSave(*fHandle);
+ if (strlen(currentRelName)) {
+ loadRel(currentRelName);
}
- loadBgIncrustFromSave(*fHandle);
+ if (strlen(bgName)) {
+ loadBg(bgName);
+ }
- delete fHandle;
+ 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):
+ in.read(commandBuffer, 0x50);
+ 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);
}
- setMouseCursor(MOUSE_CURSOR_NORMAL);
-
if (strlen(currentDatName)) {
/* i = saveVar2;
saveVar2 = 0;
@@ -585,135 +991,139 @@ bool CineEngine::makeLoad(char *saveName) {
}*/
}
- return true;
+ return !in.ioFailed();
}
-void makeSave(char *saveFileName) {
- int16 i;
- Common::OutSaveFile *fHandle;
-
- fHandle = g_saveFileMan->openForSaving(saveFileName);
+bool CineEngine::makeLoad(char *saveName) {
+ Common::SharedPtr<Common::InSaveFile> saveFile(g_saveFileMan->openForLoading(saveName));
- if (!fHandle) {
- drawString(otherMessages[1], 0);
+ if (!saveFile) {
+ drawString(otherMessages[0], 0);
waitPlayerInput();
// restoreScreen();
checkDataDisk(-1);
- return;
- }
-
- fHandle->writeUint16BE(currentDisk);
- fHandle->write(currentPartName, 13);
- fHandle->write(currentDatName, 13);
- fHandle->writeUint16BE(saveVar2);
- fHandle->write(currentPrcName, 13);
- fHandle->write(currentRelName, 13);
- fHandle->write(currentMsgName, 13);
- renderer->saveBg(*fHandle);
- fHandle->write(currentCtName, 13);
-
- fHandle->writeUint16BE(0xFF);
- fHandle->writeUint16BE(0x20);
-
- for (i = 0; i < 255; i++) {
- fHandle->writeUint16BE(objectTable[i].x);
- fHandle->writeUint16BE(objectTable[i].y);
- fHandle->writeUint16BE(objectTable[i].mask);
- fHandle->writeUint16BE(objectTable[i].frame);
- fHandle->writeUint16BE(objectTable[i].costume);
- fHandle->write(objectTable[i].name, 20);
- fHandle->writeUint16BE(objectTable[i].part);
- }
-
- renderer->savePalette(*fHandle);
-
- globalVars.save(*fHandle, NUM_MAX_VAR - 1);
-
- for (i = 0; i < 16; i++) {
- fHandle->writeUint16BE(zoneData[i]);
+ return false;
}
- for (i = 0; i < 4; i++) {
- fHandle->writeUint16BE(commandVar3[i]);
+ 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 be 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);
+ }
}
- fHandle->write(commandBuffer, 0x50);
-
- fHandle->writeUint16BE(renderer->_cmdY);
-
- fHandle->writeUint16BE(bgVar0);
- fHandle->writeUint16BE(allowPlayerInput);
- fHandle->writeUint16BE(playerCommand);
- fHandle->writeUint16BE(commandVar1);
- fHandle->writeUint16BE(isDrawCommandEnabled);
- fHandle->writeUint16BE(var5);
- fHandle->writeUint16BE(var4);
- fHandle->writeUint16BE(var3);
- fHandle->writeUint16BE(var2);
- fHandle->writeUint16BE(commandVar2);
-
- fHandle->writeUint16BE(renderer->_messageBg);
-
- fHandle->writeUint16BE(0xFF);
- fHandle->writeUint16BE(0x1E);
+ setMouseCursor(MOUSE_CURSOR_NORMAL);
- for (i = 0; i < NUM_MAX_ANIMDATA; i++) {
- animDataTable[i].save(*fHandle);
- }
+ return result;
+}
- fHandle->writeUint16BE(0); // Screen params, unhandled
- fHandle->writeUint16BE(0);
- fHandle->writeUint16BE(0);
- fHandle->writeUint16BE(0);
- fHandle->writeUint16BE(0);
- fHandle->writeUint16BE(0);
+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);
+ out.write(commandBuffer, 0x50);
+
+ 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);
+}
- {
- ScriptList::iterator it;
- fHandle->writeUint16BE(globalScripts.size());
- for (it = globalScripts.begin(); it != globalScripts.end(); ++it) {
- (*it)->save(*fHandle);
- }
+void CineEngine::makeSave(char *saveFileName) {
+ Common::SharedPtr<Common::OutSaveFile> fHandle(g_saveFileMan->openForSaving(saveFileName));
- fHandle->writeUint16BE(objectScripts.size());
- for (it = objectScripts.begin(); it != objectScripts.end(); ++it) {
- (*it)->save(*fHandle);
- }
- }
+ setMouseCursor(MOUSE_CURSOR_DISK);
- {
- Common::List<overlay>::iterator it;
-
- fHandle->writeUint16BE(overlayList.size());
-
- for (it = overlayList.begin(); it != overlayList.end(); ++it) {
- fHandle->writeUint32BE(0);
- fHandle->writeUint32BE(0);
- fHandle->writeUint16BE(it->objIdx);
- fHandle->writeUint16BE(it->type);
- fHandle->writeSint16BE(it->x);
- fHandle->writeSint16BE(it->y);
- fHandle->writeSint16BE(it->width);
- fHandle->writeSint16BE(it->color);
+ if (!fHandle) {
+ drawString(otherMessages[1], 0);
+ waitPlayerInput();
+ // restoreScreen();
+ checkDataDisk(-1);
+ } else {
+ if (g_cine->getGameType() == GType_FW) {
+ makeSaveFW(*fHandle);
+ } else {
+ makeSaveOS(*fHandle);
}
}
- Common::List<BGIncrust>::iterator it;
- fHandle->writeUint16BE(bgIncrustList.size());
-
- for (it = bgIncrustList.begin(); it != bgIncrustList.end(); ++it) {
- fHandle->writeUint32BE(0); // next
- fHandle->writeUint32BE(0); // unkPtr
- fHandle->writeUint16BE(it->objIdx);
- fHandle->writeUint16BE(it->param);
- fHandle->writeUint16BE(it->x);
- fHandle->writeUint16BE(it->y);
- fHandle->writeUint16BE(it->frame);
- fHandle->writeUint16BE(it->part);
- }
-
- delete fHandle;
-
setMouseCursor(MOUSE_CURSOR_NORMAL);
}
@@ -854,6 +1264,89 @@ 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);
+ out.write(commandBuffer, 0x50);
+ 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
@@ -1510,12 +2003,22 @@ void mainLoopSub6(void) {
void checkForPendingDataLoad(void) {
if (newPrcName[0] != 0) {
- loadPrc(newPrcName);
+ bool loadPrcOk = loadPrc(newPrcName);
strcpy(currentPrcName, newPrcName);
strcpy(newPrcName, "");
- addScriptToList0(1);
+ // Check that the loading of the script file was successful before
+ // trying to add script 1 from it to the global scripts list. This
+ // fixes a crash when failing copy protection in Amiga or Atari ST
+ // versions of Future Wars.
+ if (loadPrcOk) {
+ addScriptToGlobalScripts(1);
+ } else if (scumm_stricmp(currentPrcName, COPY_PROT_FAIL_PRC_NAME)) {
+ // We only show an error here for other files than the file that
+ // is loaded if copy protection fails (i.e. L201.ANI).
+ warning("checkForPendingDataLoad: loadPrc(%s) failed", currentPrcName);
+ }
}
if (newRelName[0] != 0) {
@@ -1582,16 +2085,19 @@ void removeSeq(uint16 param1, uint16 param2, uint16 param3) {
}
}
-uint16 isSeqRunning(uint16 param1, uint16 param2, uint16 param3) {
+bool isSeqRunning(uint16 param1, uint16 param2, uint16 param3) {
Common::List<SeqListElement>::iterator it;
for (it = seqList.begin(); it != seqList.end(); ++it) {
if (it->objIdx == param1 && it->var4 == param2 && it->varE == param3) {
- return 1;
+ // Just to be on the safe side there's a restriction of the
+ // addition's result to 16-bit arithmetic here like in the
+ // original. It's possible that it's not strictly needed.
+ return ((it->var14 + it->var16) & 0xFFFF) == 0;
}
}
- return 0;
+ return true;
}
void addSeqListElement(uint16 objIdx, int16 param1, int16 param2, int16 frame, int16 param4, int16 param5, int16 param6, int16 param7, int16 param8) {
@@ -1618,6 +2124,19 @@ void addSeqListElement(uint16 objIdx, int16 param1, int16 param2, int16 frame, i
seqList.insert(it, tmp);
}
+void modifySeqListElement(uint16 objIdx, int16 var4Test, int16 param1, int16 param2, int16 param3, int16 param4) {
+ // Find a suitable list element and modify it
+ for (Common::List<SeqListElement>::iterator it = seqList.begin(); it != seqList.end(); ++it) {
+ if (it->objIdx == objIdx && it->var4 == var4Test) {
+ it->varC = param1;
+ it->var18 = param2;
+ it->var1A = param3;
+ it->var10 = it->var12 = param4;
+ break;
+ }
+ }
+}
+
void computeMove1(SeqListElement &element, int16 x, int16 y, int16 param1,
int16 param2, int16 x2, int16 y2) {
element.var16 = 0;
@@ -1662,105 +2181,51 @@ uint16 computeMove2(SeqListElement &element) {
return returnVar;
}
-// sort all the gfx stuff...
-
-void resetGfxEntityEntry(uint16 objIdx) {
-#if 0
- overlayHeadElement* tempHead = &overlayHead;
- byte* var_16 = NULL;
- uint16 var_10 = 0;
- uint16 var_12 = 0;
- overlayHeadElement* currentHead = tempHead->next;
- byte* var_1A = NULL;
- overlayHeadElement* var1E = &overlayHead;
-
- while (currentHead) {
- tempHead2 = currentHead->next;
-
- if (currentHead->objIdx == objIdx && currentHead->type!=2 && currentHead->type!=3 && currentHead->type!=0x14) {
- tempHead->next = tempHead2;
-
- if (tempHead2) {
- tempHead2->previous = currentHead->previous;
- } else {
- seqVar0 = currentHead->previous;
- }
-
- var_22 = var_16;
-
- if (!var_22) {
- // todo: goto?
- }
-
- var_22->previous = currentHead;
- } else {
- }
-
- if (currentHead->type == 0x14) {
- } else {
- }
-
- if (currentHead->type == 0x2 || currentHead->type == 0x3) {
- si = 10000;
- } else {
- si = objectTable[currentHead->objIdx];
- }
-
- if (objectTable[objIdx]>si) {
- var1E = currentHead;
- }
-
- tempHead = tempHead->next;
-
- }
-
- if (var_1A) {
- currentHead = var_16;
- var_22 = var_1E->next;
- var_1E->next = currentHead;
- var_1A->next = var_22;
-
- if (var_1E != &gfxEntityHead) {
- currentHead->previous = var_1E;
- }
-
- if (!var_22) {
- seqVar0 = var_1A;
- } else {
- var_22->previous = var_1A;
- }
-
- }
-#endif
-}
-
-uint16 addAni(uint16 param1, uint16 objIdx, const byte *ptr, SeqListElement &element, uint16 param3, int16 *param4) {
- const byte *currentPtr = ptr;
- const byte *ptrData;
- const byte *ptr2;
+uint16 addAni(uint16 param1, uint16 objIdx, const int8 *ptr, SeqListElement &element, uint16 param3, int16 *param4) {
+ const int8 *ptrData;
+ const int8 *ptr2;
int16 di;
- assert(ptr);
- assert(param4);
+ debug(5, "addAni: param1 = %d, objIdx = %d, ptr = %p, element.var8 = %d, element.var14 = %d param3 = %d",
+ param1, objIdx, ptr, element.var8, element.var14, param3);
- dummyU16 = READ_BE_UINT16((currentPtr + param1 * 2) + 8);
+ // In the original an error string is set and 0 is returned if the following doesn't hold
+ assert(ptr);
+ // We probably could just use a local variable here instead of the dummyU16 but
+ // haven't checked if this has any side-effects so keeping it this way still.
+ dummyU16 = READ_BE_UINT16(ptr + param1 * 2 + 8);
ptrData = ptr + dummyU16;
+ // In the original an error string is set and 0 is returned if the following doesn't hold
assert(*ptrData);
di = (objectTable[objIdx].costume + 1) % (*ptrData);
- ptr2 = (ptrData + (di * 8)) + 1;
-
+ ++ptrData; // Jump over the just read byte
+ // Here ptr2 seems to be indexing a table of structs (8 bytes per struct):
+ // struct {
+ // int8 x; // 0 (Used with checkCollision)
+ // int8 y; // 1 (Used with checkCollision)
+ // int8 numZones; // 2 (Used with checkCollision)
+ // int8 var3; // 3 (Not used in this function)
+ // int8 xAdd; // 4 (Used with an object)
+ // int8 yAdd; // 5 (Used with an object)
+ // int8 maskAdd; // 6 (Used with an object)
+ // int8 frameAdd; // 7 (Used with an object)
+ // };
+ ptr2 = ptrData + di * 8;
+
+ // We might probably safely discard the AND by 1 here because
+ // at least in the original checkCollision returns always 0 or 1.
if ((checkCollision(objIdx, ptr2[0], ptr2[1], ptr2[2], ptr[0]) & 1)) {
return 0;
}
- objectTable[objIdx].x += (int8)ptr2[4];
- objectTable[objIdx].y += (int8)ptr2[5];
- objectTable[objIdx].mask += (int8)ptr2[6];
+ objectTable[objIdx].x += ptr2[4];
+ objectTable[objIdx].y += ptr2[5];
+ objectTable[objIdx].mask += ptr2[6];
- if (objectTable[objIdx].frame) {
+ if (ptr2[6]) {
resetGfxEntityEntry(objIdx);
}
@@ -1769,19 +2234,79 @@ uint16 addAni(uint16 param1, uint16 objIdx, const byte *ptr, SeqListElement &ele
if (param3 || !element.var14) {
objectTable[objIdx].costume = di;
} else {
+ assert(param4);
*param4 = di;
}
return 1;
}
+/*!
+ * Permutates the overlay list into a different order according to some logic.
+ * \todo Check this function for correctness (Wasn't very easy to reverse engineer so there may be errors)
+ */
+void resetGfxEntityEntry(uint16 objIdx) {
+ Common::List<overlay>::iterator it, bObjsCutPoint;
+ Common::List<overlay> aReverseObjs, bObjs;
+ bool foundCutPoint = false;
+
+ // Go through the overlay list and partition the whole list into two categories (Type A and type B objects)
+ for (it = overlayList.begin(); it != overlayList.end(); ++it) {
+ if (it->objIdx == objIdx && it->type != 2 && it->type != 3) { // Type A object
+ aReverseObjs.push_front(*it);
+ } else { // Type B object
+ bObjs.push_back(*it);
+ uint16 objectMask;
+ if (it->type == 2 || it->type == 3) {
+ objectMask = 10000;
+ } else {
+ objectMask = objectTable[it->objIdx].mask;
+ }
+
+ if (objectTable[objIdx].mask > objectMask) { // Check for B objects' cut point
+ bObjsCutPoint = bObjs.reverse_begin();
+ foundCutPoint = true;
+ }
+ }
+ }
+
+ // Recreate the overlay list in a different order.
+ overlayList.clear();
+ if (foundCutPoint) {
+ // If a cut point was found the order is:
+ // B objects before the cut point, the cut point, A objects in reverse order, B objects after cut point.
+ ++bObjsCutPoint; // Include the cut point in the first list insertion
+ overlayList.insert(overlayList.end(), bObjs.begin(), bObjsCutPoint);
+ overlayList.insert(overlayList.end(), aReverseObjs.begin(), aReverseObjs.end());
+ overlayList.insert(overlayList.end(), bObjsCutPoint, bObjs.end());
+ } else {
+ // If no cut point was found the order is:
+ // A objects in reverse order, B objects.
+ overlayList.insert(overlayList.end(), aReverseObjs.begin(), aReverseObjs.end());
+ overlayList.insert(overlayList.end(), bObjs.begin(), bObjs.end());
+ }
+}
+
void processSeqListElement(SeqListElement &element) {
int16 x = objectTable[element.objIdx].x;
int16 y = objectTable[element.objIdx].y;
- const byte *ptr1 = animDataTable[element.frame].data();
+ const int8 *ptr1 = (const int8 *) animDataTable[element.frame].data();
int16 var_10;
int16 var_4;
int16 var_2;
+
+ // Initial interpretations for variables addressed through ptr1 (8-bit addressing):
+ // These may be inaccurate!
+ // 0: ?
+ // 1: xRadius
+ // 2: yRadius
+ // 3: ?
+ // 4: xAdd
+ // 5: yAdd
+ // 6: ?
+ // 7: ?
+ // After this come (At least at positions 0, 1 and 3 in 16-bit addressing)
+ // 16-bit big-endian values used for addressing through ptr1.
if (element.var12 < element.var10) {
element.var12++;
@@ -1791,22 +2316,44 @@ void processSeqListElement(SeqListElement &element) {
element.var12 = 0;
if (ptr1) {
- uint16 param1 = ptr1[1];
- uint16 param2 = ptr1[2];
+ int16 param1 = ptr1[1];
+ int16 param2 = ptr1[2];
if (element.varC != 255) {
- // FIXME: Why is this here? Fingolfin gets lots of these
- // in his copy of Operation Stealth (value 0 or 236) under
- // Mac OS X. Maybe it's a endian issue? At least the graphics
- // in the copy protection screen are partially messed up.
- warning("processSeqListElement: varC = %d", element.varC);
- }
-
- if (globalVars[VAR_MOUSE_X_POS] || globalVars[VAR_MOUSE_Y_POS]) {
- computeMove1(element, ptr1[4] + x, ptr1[5] + y, param1, param2, globalVars[VAR_MOUSE_X_POS], globalVars[VAR_MOUSE_Y_POS]);
+ int16 x2 = element.var18;
+ int16 y2 = element.var1A;
+ if (element.varC) {
+ x2 += objectTable[element.varC].x;
+ y2 += objectTable[element.varC].y;
+ }
+ computeMove1(element, ptr1[4] + x, ptr1[5] + y, param1, param2, x2, y2);
} else {
- element.var16 = 0;
- element.var14 = 0;
+ if (inputVar0 && allowPlayerInput) {
+ int16 adder = param1 + 1;
+ if (inputVar0 != 1) {
+ adder = -adder;
+ }
+ // FIXME: In Operation Stealth's disassembly global variable 251 is used here
+ // but it's named as VAR_MOUSE_Y_MODE in ScummVM. Is it correct or a
+ // left over from Future Wars's reverse engineering?
+ globalVars[VAR_MOUSE_X_POS] = globalVars[251] = ptr1[4] + x + adder;
+ }
+
+ if (inputVar1 && allowPlayerInput) {
+ int16 adder = param2 + 1;
+ if (inputVar1 != 1) {
+ adder = -adder;
+ }
+ // TODO: Name currently unnamed global variable 252
+ globalVars[VAR_MOUSE_Y_POS] = globalVars[252] = ptr1[5] + y + adder;
+ }
+
+ if (globalVars[VAR_MOUSE_X_POS] || globalVars[VAR_MOUSE_Y_POS]) {
+ computeMove1(element, ptr1[4] + x, ptr1[5] + y, param1, param2, globalVars[VAR_MOUSE_X_POS], globalVars[VAR_MOUSE_Y_POS]);
+ } else {
+ element.var16 = 0;
+ element.var14 = 0;
+ }
}
var_10 = computeMove2(element);
@@ -1847,14 +2394,14 @@ void processSeqListElement(SeqListElement &element) {
}
}
- if (element.var16 + element.var14) {
+ if (element.var16 + element.var14 == 0) {
if (element.var1C) {
if (element.var1E) {
objectTable[element.objIdx].costume = 0;
element.var1E = 0;
}
- addAni(element.var1C + 3, element.objIdx, ptr1, element, 1, (int16 *) & var2);
+ addAni(element.var1C + 3, element.objIdx, ptr1, element, 1, &var_2);
}
}
diff --git a/engines/cine/various.h b/engines/cine/various.h
index 91662c16ff..d87679ca08 100644
--- a/engines/cine/various.h
+++ b/engines/cine/various.h
@@ -44,7 +44,7 @@ extern bool inMenu;
struct SeqListElement {
int16 var4;
- uint16 objIdx;
+ uint16 objIdx; ///< Is this really unsigned?
int16 var8;
int16 frame;
int16 varC;
@@ -130,16 +130,20 @@ struct SelectedObjStruct {
#define NUM_MAX_ZONE 16
extern uint16 zoneData[NUM_MAX_ZONE];
+extern uint16 zoneQuery[NUM_MAX_ZONE];
void addMessage(byte param1, int16 param2, int16 param3, int16 param4, int16 param5);
void removeMessages();
void removeSeq(uint16 param1, uint16 param2, uint16 param3);
-uint16 isSeqRunning(uint16 param1, uint16 param2, uint16 param3);
+bool isSeqRunning(uint16 param1, uint16 param2, uint16 param3);
void addSeqListElement(uint16 objIdx, int16 param1, int16 param2, int16 frame, int16 param4, int16 param5, int16 param6, int16 param7, int16 param8);
+void modifySeqListElement(uint16 objIdx, int16 var4Test, int16 param1, int16 param2, int16 param3, int16 param4);
void processSeqList(void);
+void resetGfxEntityEntry(uint16 objIdx);
+
bool makeTextEntryMenu(const char *caption, char *string, int strLen, int y);
} // End of namespace Cine