aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sword2/save_rest.cpp355
-rw-r--r--sword2/sword2.h16
2 files changed, 147 insertions, 224 deletions
diff --git a/sword2/save_rest.cpp b/sword2/save_rest.cpp
index c65825db2c..02453cc1a9 100644
--- a/sword2/save_rest.cpp
+++ b/sword2/save_rest.cpp
@@ -35,18 +35,16 @@
namespace Sword2 {
-// max length of a savegame filename, including full path
-#define MAX_FILENAME_LEN 128
+// A savegame consists of a header and the global variables
-// savegame consists of header & global variables resource
+// Max length of a savegame filename, including full path
+#define MAX_FILENAME_LEN 128
#ifdef SCUMM_BIG_ENDIAN
// Quick macro to make swapping in-place easier to write
#define SWAP32(x) x = SWAP_BYTES_32(x)
static void convertHeaderEndian(Sword2Engine::SaveGameHeader &header) {
- int i;
-
// SaveGameHeader
SWAP32(header.checksum);
SWAP32(header.varLength);
@@ -59,7 +57,7 @@ static void convertHeaderEndian(Sword2Engine::SaveGameHeader &header) {
// ObjectHub
SWAP32(header.player_hub.type);
SWAP32(header.player_hub.logic_level);
- for (i = 0; i < TREE_SIZE; i++) {
+ for (int i = 0; i < TREE_SIZE; i++) {
SWAP32(header.player_hub.logic[i]);
SWAP32(header.player_hub.script_id[i]);
SWAP32(header.player_hub.script_pc[i]);
@@ -86,88 +84,56 @@ static void convertHeaderEndian(Sword2Engine::SaveGameHeader &header) {
}
#endif
-// SAVE GAME
+/**
+ * Save the game.
+ */
uint32 Sword2Engine::saveGame(uint16 slotNo, byte *desc) {
- byte *saveBufferMem;
- uint32 bufferSize;
- uint32 errorCode;
-
- // allocate the savegame buffer
-
- bufferSize = findBufferSize();
- saveBufferMem = (byte *) malloc(bufferSize);
+ uint32 bufferSize = findBufferSize();
+ byte *saveBufferMem = (byte *) malloc(bufferSize);
fillSaveBuffer(saveBufferMem, bufferSize, desc);
- // save it (hopefully no longer platform-specific)
-
- // save the buffer
- errorCode = saveData(slotNo, saveBufferMem, bufferSize);
-
- // free the buffer
+ uint32 errorCode = saveData(slotNo, saveBufferMem, bufferSize);
free(saveBufferMem);
-
return errorCode;
}
-// calculate size of required savegame buffer
+/**
+ * Calculate size of required savegame buffer
+ */
uint32 Sword2Engine::findBufferSize(void) {
- // size of savegame header + size of global variables
+ // Size of savegame header + size of global variables
return sizeof(_saveGameHeader) + _resman->fetchLen(1);
}
void Sword2Engine::fillSaveBuffer(byte *buffer, uint32 size, byte *desc) {
- byte *varsRes;
+ // Set up the _saveGameHeader. Checksum gets filled in last of all.
- // set up the _saveGameHeader
-
- // 'checksum' gets filled in last of all
-
- // player's description of savegame
strcpy(_saveGameHeader.description, (char *) desc);
-
- // length of global variables resource
_saveGameHeader.varLength = _resman->fetchLen(1);
-
- // resource id of current screen file
_saveGameHeader.screenId = _thisScreen.background_layer_id;
-
- // resource id of current run-list
_saveGameHeader.runListId = _logic->getRunList();
-
- // those scroll position control things
_saveGameHeader.feet_x = _thisScreen.feet_x;
_saveGameHeader.feet_y = _thisScreen.feet_y;
-
- // id of currently looping music (or zero)
_saveGameHeader.music_id = _loopingMusicId;
- // object hub
memcpy(&_saveGameHeader.player_hub, _resman->openResource(CUR_PLAYER_ID) + sizeof(StandardHeader), sizeof(ObjectHub));
+
_resman->closeResource(CUR_PLAYER_ID);
- // logic, graphic & mega structures
- // copy the 4 essential player object structures into the header
+ // Get the logic, graphic and mega structures
getPlayerStructures();
- // copy the header to the buffer
-
#ifdef SCUMM_BIG_ENDIAN
convertHeaderEndian(_saveGameHeader);
#endif
- // copy the header to the savegame buffer
- memcpy(buffer, &_saveGameHeader, sizeof(_saveGameHeader));
-
- // copy the global variables to the buffer
-
- // open variables resource
- varsRes = _resman->openResource(1);
+ // Get the global variables
- // copy that to the buffer, following the header
+ byte *varsRes = _resman->openResource(1);
memcpy(buffer + sizeof(_saveGameHeader), varsRes, FROM_LE_32(_saveGameHeader.varLength));
#ifdef SCUMM_BIG_ENDIAN
@@ -178,74 +144,61 @@ void Sword2Engine::fillSaveBuffer(byte *buffer, uint32 size, byte *desc) {
globalVars[i] = SWAP_BYTES_32(globalVars[i]);
#endif
- // close variables resource
_resman->closeResource(1);
- // set the checksum & copy that to the buffer
+ // Finally, get the checksum
_saveGameHeader.checksum = TO_LE_32(calcChecksum(buffer + sizeof(_saveGameHeader.checksum), size - sizeof(_saveGameHeader.checksum)));
- memcpy(buffer, &_saveGameHeader.checksum, sizeof(_saveGameHeader.checksum));
+
+
+ // All done
+
+ memcpy(buffer, &_saveGameHeader, sizeof(_saveGameHeader));
}
uint32 Sword2Engine::saveData(uint16 slotNo, byte *buffer, uint32 bufferSize) {
char saveFileName[MAX_FILENAME_LEN];
- uint32 itemsWritten;
- SaveFile *out;
- SaveFileManager *mgr = _system->get_savefile_manager();
- // construct filename
sprintf(saveFileName, "%s.%.3d", _targetName, slotNo);
-
+
+ SaveFileManager *mgr = _system->get_savefile_manager();
+ SaveFile *out;
+
if (!(out = mgr->open_savefile(saveFileName, getSavePath(), true))) {
- // error: couldn't open file
delete mgr;
return SR_ERR_FILEOPEN;
}
- // write the buffer
- itemsWritten = out->write(buffer, bufferSize);
+ uint32 itemsWritten = out->write(buffer, bufferSize);
delete out;
delete mgr;
- // if we successfully wrote it all, then everything's ok
if (itemsWritten == bufferSize)
return SR_OK;
- // write failed for some reason (could be hard drive full)
return SR_ERR_WRITEFAIL;
}
-// RESTORE GAME
+/**
+ * Restore the game.
+ */
uint32 Sword2Engine::restoreGame(uint16 slotNo) {
- byte *saveBufferMem;
- uint32 bufferSize;
- uint32 errorCode;
-
- // allocate the savegame buffer
-
- bufferSize = findBufferSize();
- saveBufferMem = (byte *) malloc(bufferSize);
-
- // read the savegame file into our buffer
+ uint32 bufferSize = findBufferSize();
+ byte *saveBufferMem = (byte *) malloc(bufferSize);
- // load savegame into buffer
- errorCode = restoreData(slotNo, saveBufferMem, bufferSize);
+ uint32 errorCode = restoreData(slotNo, saveBufferMem, bufferSize);
- // if it was read in successfully, then restore the game from the
- // buffer & free the buffer
+ // If it was read in successfully, then restore the game from the
+ // buffer & free the buffer. Note that restoreFromBuffer() frees the
+ // buffer in order to clear it from memory before loading in the new
+ // screen and runlist, so we only need to free it in case of failure.
- if (errorCode == SR_OK) {
+ if (errorCode == SR_OK)
errorCode = restoreFromBuffer(saveBufferMem, bufferSize);
-
- // Note that the buffer has been freed inside
- // restoreFromBuffer, in order to clear it from memory before
- // loading in the new screen & runlist
- } else {
- // because restoreFromBuffer would have freed it
+ else
free(saveBufferMem);
- }
// Force the game engine to pick a cursor. This appears to be needed
// when using the -x command-line option to restore a game.
@@ -255,51 +208,38 @@ uint32 Sword2Engine::restoreGame(uint16 slotNo) {
uint32 Sword2Engine::restoreData(uint16 slotNo, byte *buffer, uint32 bufferSize) {
char saveFileName[MAX_FILENAME_LEN];
- SaveFile *in;
- SaveFileManager *mgr = _system->get_savefile_manager();
- uint32 itemsRead;
- // construct filename
sprintf(saveFileName, "%s.%.3d", _targetName, slotNo);
+ SaveFileManager *mgr = _system->get_savefile_manager();
+ SaveFile *in;
+
if (!(in = mgr->open_savefile(saveFileName, getSavePath(), false))) {
// error: couldn't open file
delete mgr;
return SR_ERR_FILEOPEN;
}
- // read savegame into the buffer
- itemsRead = in->read(buffer, bufferSize);
+ // Read savegame into the buffer
+ uint32 itemsRead = in->read(buffer, bufferSize);
- if (itemsRead == bufferSize) {
- // We successfully read it all
- delete in;
- delete mgr;
- return SR_OK;
- } else {
- // We didn't get all of it.
-
-/*
- // There is no ioFailed() function in SaveFile yet, I think.
- if (in->ioFailed()) {
- delete in;
- delete mgr;
- return SR_ERR_READFAIL;
- }
-*/
+ delete in;
+ delete mgr;
+
+ if (itemsRead != bufferSize) {
+ // We didn't get all of it. At the moment we have no way of
+ // knowing why, so assume that it's an incompatible savegame.
delete in;
delete mgr;
- // error: incompatible save-data - can't use!
return SR_ERR_INCOMPATIBLE;
}
+
+ return SR_OK;
}
uint32 Sword2Engine::restoreFromBuffer(byte *buffer, uint32 size) {
- byte *varsRes;
- int32 pars[2];
-
- // get a copy of the header from the savegame buffer
+ // Get a copy of the header from the savegame buffer
memcpy(&_saveGameHeader, buffer, sizeof(_saveGameHeader));
#ifdef SCUMM_BIG_ENDIAN
@@ -310,51 +250,40 @@ uint32 Sword2Engine::restoreFromBuffer(byte *buffer, uint32 size) {
if (_saveGameHeader.checksum != calcChecksum(buffer + sizeof(_saveGameHeader.checksum), size - sizeof(_saveGameHeader.checksum))) {
free(buffer);
-
- // error: incompatible save-data - can't use!
return SR_ERR_INCOMPATIBLE;
}
- // check savegame against length of current global variables resource
+ // Check savegame against length of current global variables resource
// This would most probably be trapped by the checksum test anyway,
- // but it doesn't do any harm to check this as well
+ // but it doesn't do any harm to check this as well.
- // Note that during development, earlier savegames will often be
- // shorter than the current expected length
+ // Historical note: During development, earlier savegames would often
+ // be shorter than the current expected length.
- // if header contradicts actual current size of global variables
if (_saveGameHeader.varLength != _resman->fetchLen(1)) {
free(buffer);
-
- // error: incompatible save-data - can't use!
return SR_ERR_INCOMPATIBLE;
}
- // clean out system
+ // Clean out system
- // trash all resources from memory except player object & global
- // variables
+ // Trash all resources from memory except player object & global vars
_resman->killAll(false);
- // clean out the system kill list (no more objects to kill)
+ // Clean out the system kill list (no more objects to kill)
_logic->resetKillList();
- // get player character data from savegame buffer
-
- // object hub is just after the standard header
+ // Object hub is just after the standard header
memcpy(_resman->openResource(CUR_PLAYER_ID) + sizeof(StandardHeader), &_saveGameHeader.player_hub, sizeof(ObjectHub));
_resman->closeResource(CUR_PLAYER_ID);
- // fill in the 4 essential player object structures from the header
+ // Fill in the player object structures from the header
putPlayerStructures();
- // get variables resource from the savegame buffer
-
- // open variables resource
- varsRes = _resman->openResource(1);
+ // Copy variables from savegame buffer to memory
+ byte *varsRes = _resman->openResource(1);
- // copy that to the buffer, following the header
memcpy(varsRes, buffer + sizeof(_saveGameHeader), _saveGameHeader.varLength);
#ifdef SCUMM_BIG_ENDIAN
@@ -365,13 +294,14 @@ uint32 Sword2Engine::restoreFromBuffer(byte *buffer, uint32 size) {
globalVars[i] = SWAP_BYTES_32(globalVars[i]);
#endif
- // close variables resource
_resman->closeResource(1);
- // free it now, rather than in RestoreGame, to unblock memory before
+ // Free it now, rather than in RestoreGame, to unblock memory before
// new screen & runlist loaded
free(buffer);
+ int32 pars[2];
+
pars[0] = _saveGameHeader.screenId;
pars[1] = 1;
_logic->fnInitBackground(pars);
@@ -380,13 +310,14 @@ uint32 Sword2Engine::restoreFromBuffer(byte *buffer, uint32 size) {
// fade up instead!
_thisScreen.new_palette = 99;
- // these need setting after the defaults get set in fnInitBackground
- // remember that these can change through the game, so need saving &
- // restoring too
+ // These need setting after the defaults get set in fnInitBackground.
+ // Remember that these can change through the game, so need saving &
+ // restoring too.
+
_thisScreen.feet_x = _saveGameHeader.feet_x;
_thisScreen.feet_y = _saveGameHeader.feet_y;
- // start the new run list
+ // Start the new run list
_logic->expressChangeSession(_saveGameHeader.runListId);
// Force in the new scroll position, so unsightly scroll-catch-up does
@@ -418,32 +349,28 @@ uint32 Sword2Engine::restoreFromBuffer(byte *buffer, uint32 size) {
} else
_logic->fnStopMusic(NULL);
- // Write to walkthrough file (zebug0.txt)
-
- debug(5, "RESTORED GAME \"%s\"", _saveGameHeader.description);
-
- // game restored ok
return SR_OK;
}
-// GetSaveDescription - PC version...
+/**
+ * Get the description of a savegame
+ */
uint32 Sword2Engine::getSaveDescription(uint16 slotNo, byte *description) {
char saveFileName[MAX_FILENAME_LEN];
- SaveGameHeader dummy;
- SaveFile *in;
- SaveFileManager *mgr = _system->get_savefile_manager();
- // construct filename
sprintf(saveFileName, "%s.%.3d", _targetName, slotNo);
+ SaveFileManager *mgr = _system->get_savefile_manager();
+ SaveFile *in;
+
if (!(in = mgr->open_savefile(saveFileName, getSavePath(), false))) {
- // error: couldn't open file
delete mgr;
return SR_ERR_FILEOPEN;
}
- // read header
+ SaveGameHeader dummy;
+
in->read(&dummy, sizeof(dummy));
delete in;
delete mgr;
@@ -461,12 +388,12 @@ bool Sword2Engine::saveExists(void) {
bool Sword2Engine::saveExists(uint16 slotNo) {
char saveFileName[MAX_FILENAME_LEN];
- SaveFileManager *mgr = _system->get_savefile_manager();
- SaveFile *in;
- // construct filename
sprintf(saveFileName, "%s.%.3d", _targetName, slotNo);
+ SaveFileManager *mgr = _system->get_savefile_manager();
+ SaveFile *in;
+
if (!(in = mgr->open_savefile(saveFileName, getSavePath(), false))) {
delete mgr;
return false;
@@ -474,56 +401,51 @@ bool Sword2Engine::saveExists(uint16 slotNo) {
delete in;
delete mgr;
-
return true;
}
-void Sword2Engine::getPlayerStructures(void) {
- // request the player object structures which need saving
+/**
+ * Request the player object structures which need saving.
+ */
- // script no. 7 - 'george_savedata_request' calls fnPassPlayerSaveData
+void Sword2Engine::getPlayerStructures(void) {
+ StandardHeader *head = (StandardHeader *) _resman->openResource(CUR_PLAYER_ID);
- uint32 null_pc = 7;
- char *raw_script_ad;
- StandardHeader *head;
+ assert(head->fileType == GAME_OBJECT);
- head = (StandardHeader *) _resman->openResource(CUR_PLAYER_ID);
+ char *raw_script_ad = (char *) head;
- if (head->fileType != GAME_OBJECT)
- error("incorrect CUR_PLAYER_ID=%d", CUR_PLAYER_ID);
+ // Script no. 7 - 'george_savedata_request' calls fnPassPlayerSaveData
+ uint32 null_pc = 7;
- raw_script_ad = (char *) head;
_logic->runScript(raw_script_ad, raw_script_ad, &null_pc);
_resman->closeResource(CUR_PLAYER_ID);
}
-void Sword2Engine::putPlayerStructures(void) {
- // fill out the player object structures from the savegame structures
- // also run the appropriate scripts to set up george's anim tables &
- // walkdata, and nico's anim tables
-
- uint32 null_pc;
- char *raw_script_ad;
- StandardHeader *head;
+/**
+ * Fill out the player object structures from the savegame structures also run
+ * the appropriate scripts to set up george's anim tables and walkdata, and
+ * Nico's anim tables.
+ */
- head = (StandardHeader *) _resman->openResource(CUR_PLAYER_ID);
+void Sword2Engine::putPlayerStructures(void) {
+ StandardHeader *head = (StandardHeader *) _resman->openResource(CUR_PLAYER_ID);
- if (head->fileType != GAME_OBJECT)
- error("incorrect CUR_PLAYER_ID=%d", CUR_PLAYER_ID);
+ assert(head->fileType == GAME_OBJECT);
- raw_script_ad = (char *) head;
+ char *raw_script_ad = (char *) head;
- // script no. 8 - 'george_savedata_return' calls fnGetPlayerSaveData
+ // Script no. 8 - 'george_savedata_return' calls fnGetPlayerSaveData
- null_pc = 8;
+ uint32 null_pc = 8;
_logic->runScript(raw_script_ad, raw_script_ad, &null_pc);
- // script no. 14 - 'set_up_nico_anim_tables'
+ // Script no. 14 - 'set_up_nico_anim_tables'
null_pc = 14;
_logic->runScript(raw_script_ad, raw_script_ad, &null_pc);
- // which megaset was the player at the time of saving?
+ // Which megaset was the player at the time of saving?
switch (_saveGameHeader.mega.megaset_res) {
case 36: // GeoMega:
@@ -556,72 +478,69 @@ uint32 Sword2Engine::calcChecksum(byte *buffer, uint32 size) {
return total;
}
-int32 Logic::fnPassPlayerSaveData(int32 *params) {
- // copies the 4 essential player structures into the savegame header
- // - run script 7 of player object to request this
-
- // remember, we cannot simply read a compact any longer but instead
- // must request it from the object itself
+/**
+ * Copies the 4 essential player structures into the savegame header - run
+ * script 7 of player object to request this.
+ *
+ * Remember, we cannot simply read a compact any longer but instead must
+ * request it from the object itself.
+ */
+int32 Logic::fnPassPlayerSaveData(int32 *params) {
// params: 0 pointer to object's logic structure
// 1 pointer to object's graphic structure
// 2 pointer to object's mega structure
- // copy from player object to savegame header
+ // Copy from player object to savegame header
memcpy(&_vm->_saveGameHeader.logic, _vm->_memory->decodePtr(params[0]), sizeof(ObjectLogic));
memcpy(&_vm->_saveGameHeader.graphic, _vm->_memory->decodePtr(params[1]), sizeof(ObjectGraphic));
memcpy(&_vm->_saveGameHeader.mega, _vm->_memory->decodePtr(params[2]), sizeof(ObjectMega));
- // makes no odds
return IR_CONT;
}
-int32 Logic::fnGetPlayerSaveData(int32 *params) {
- // reverse of fnPassPlayerSaveData
- // - run script 8 of player object
+/**
+ * Reverse of fnPassPlayerSaveData() - run script 8 of player object.
+ */
+int32 Logic::fnGetPlayerSaveData(int32 *params) {
// params: 0 pointer to object's logic structure
// 1 pointer to object's graphic structure
// 2 pointer to object's mega structure
- ObjectLogic *ob_logic = (ObjectLogic *) _vm->_memory->decodePtr(params[0]);
- ObjectGraphic *ob_graphic = (ObjectGraphic *) _vm->_memory->decodePtr(params[1]);
- ObjectMega *ob_mega = (ObjectMega *) _vm->_memory->decodePtr(params[2]);
+ byte *logic_ptr = _vm->_memory->decodePtr(params[0]);
+ byte *graphic_ptr = _vm->_memory->decodePtr(params[1]);
+ byte *mega_ptr = _vm->_memory->decodePtr(params[2]);
- int32 pars[3];
+ // Copy from savegame header to player object
- // copy from savegame header to player object
+ memcpy(logic_ptr, &_vm->_saveGameHeader.logic, sizeof(ObjectLogic));
+ memcpy(graphic_ptr, &_vm->_saveGameHeader.graphic, sizeof(ObjectGraphic));
+ memcpy(mega_ptr, &_vm->_saveGameHeader.mega, sizeof(ObjectMega));
- memcpy((byte *) ob_logic, &_vm->_saveGameHeader.logic, sizeof(ObjectLogic));
- memcpy((byte *) ob_graphic, &_vm->_saveGameHeader.graphic, sizeof(ObjectGraphic));
- memcpy((byte *) ob_mega, &_vm->_saveGameHeader.mega, sizeof(ObjectMega));
+ // Any walk-data must be cleared - the player will be set to stand if
+ // he was walking when saved.
- // any walk-data must be cleared - the player will be set to stand if
- // he was walking when saved
+ ObjectMega *ob_mega = (ObjectMega *) mega_ptr;
- // if the player was walking when game was saved
if (ob_mega->currently_walking) {
- // clear the flag
ob_mega->currently_walking = 0;
- // pointer to object's graphic structure
- pars[0] = _vm->_memory->encodePtr((byte *) ob_graphic);
-
- // pointer to object's mega structure
- pars[1] = _vm->_memory->encodePtr((byte *) ob_mega);
+ int32 pars[3];
- // target direction
+ pars[0] = params[1]; // ob_graphic;
+ pars[1] = params[2]; // ob_mega
pars[2] = ob_mega->current_dir;
- // set player to stand
fnStand(pars);
- // reset looping flag (which would have been '1' during fnWalk)
+ // Reset looping flag (which would have been 1 during fnWalk)
+ ObjectLogic *ob_logic = (ObjectLogic *) logic_ptr;
+
ob_logic->looping = 0;
}
- // makes no odds
return IR_CONT;
}
diff --git a/sword2/sword2.h b/sword2/sword2.h
index b256557f01..d75ad15ffe 100644
--- a/sword2/sword2.h
+++ b/sword2/sword2.h
@@ -279,6 +279,10 @@ public:
// savegame file header
+#if !defined(__GNUC__)
+ #pragma START_PACK_STRUCTS
+#endif
+
struct SaveGameHeader {
// sum of all bytes in file, excluding this uint32
uint32 checksum;
@@ -294,13 +298,13 @@ public:
uint32 music_id; // copy of 'looping_music_id'
ObjectHub player_hub; // copy of player object's object_hub structure
ObjectLogic logic; // copy of player character logic structure
+ ObjectGraphic graphic; // copy of player character graphic structure
+ ObjectMega mega; // copy of player character mega structure
+ } GCC_PACK;
- // copy of player character graphic structure
- ObjectGraphic graphic;
-
- // copy of player character mega structure
- ObjectMega mega;
- };
+#if !defined(__GNUC__)
+ #pragma END_PACK_STRUCTS
+#endif
SaveGameHeader _saveGameHeader;