aboutsummaryrefslogtreecommitdiff
path: root/sword2/save_rest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sword2/save_rest.cpp')
-rw-r--r--sword2/save_rest.cpp308
1 files changed, 100 insertions, 208 deletions
diff --git a/sword2/save_rest.cpp b/sword2/save_rest.cpp
index 0332fa2f6a..cf948b7c14 100644
--- a/sword2/save_rest.cpp
+++ b/sword2/save_rest.cpp
@@ -43,63 +43,58 @@ namespace Sword2 {
// Max length of a savegame filename
#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) {
- // SaveGameHeader
- SWAP32(header.checksum);
- SWAP32(header.varLength);
- SWAP32(header.screenId);
- SWAP32(header.runListId);
- SWAP32(header.feet_x);
- SWAP32(header.feet_y);
- SWAP32(header.music_id);
-
- // ObjectHub
- SWAP32(header.player_hub.type);
- SWAP32(header.player_hub.logic_level);
- 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]);
- }
+/**
+ * Calculate size of required savegame buffer
+ */
- // ObjectLogic
- SWAP32(header.logic.looping);
- SWAP32(header.logic.pause);
-
- // ObjectGraphic
- SWAP32(header.graphic.type);
- SWAP32(header.graphic.anim_resource);
- SWAP32(header.graphic.anim_pc);
-
- // ObjectMega
- SWAP32(header.mega.currently_walking);
- SWAP32(header.mega.walk_pc);
- SWAP32(header.mega.scale_a);
- SWAP32(header.mega.scale_b);
- SWAP32(header.mega.feet_x);
- SWAP32(header.mega.feet_y);
- SWAP32(header.mega.current_dir);
- SWAP32(header.mega.megaset_res);
+uint32 Sword2Engine::findBufferSize() {
+ // Size of savegame header + size of global variables
+ return 212 + _resman->fetchLen(1);
}
-#endif
/**
* Save the game.
*/
uint32 Sword2Engine::saveGame(uint16 slotNo, byte *desc) {
+ char description[SAVE_DESCRIPTION_LEN];
uint32 bufferSize = findBufferSize();
- byte *saveBufferMem = (byte *)malloc(bufferSize);
+ byte *saveBuffer = (byte *)malloc(bufferSize);
+ ScreenInfo *screenInfo = _screen->getScreenInfo();
+
+ memset(description, 0, sizeof(description));
+ strncpy(description, (char *)desc, SAVE_DESCRIPTION_LEN - 1);
+
+ Common::MemoryWriteStream writeS(saveBuffer, bufferSize);
+
+ byte *globalVars = _resman->openResource(1);
+ byte *objectHub = _resman->openResource(CUR_PLAYER_ID) + ResHeader::size();
- fillSaveBuffer(saveBufferMem, bufferSize, desc);
+ // Script no. 7 - 'george_savedata_request' calls fnPassPlayerSaveData
+ _logic->runResScript(CUR_PLAYER_ID, 7);
+
+ writeS.writeUint32LE(0); // Checksum
+ writeS.write(description, SAVE_DESCRIPTION_LEN);
+ writeS.writeUint32LE(_resman->fetchLen(1));
+ writeS.writeUint32LE(screenInfo->background_layer_id);
+ writeS.writeUint32LE(_logic->getRunList());
+ writeS.writeUint32LE(screenInfo->feet_x);
+ writeS.writeUint32LE(screenInfo->feet_y);
+ writeS.writeUint32LE(_sound->getLoopingMusicId());
+ writeS.write(objectHub, ObjectHub::size());
+ writeS.write(_logic->_saveLogic, ObjectLogic::size());
+ writeS.write(_logic->_saveGraphic, ObjectGraphic::size());
+ writeS.write(_logic->_saveMega, ObjectMega::size());
+ writeS.write(globalVars, _resman->fetchLen(1));
+
+ WRITE_LE_UINT32(saveBuffer, calcChecksum(saveBuffer + 4, bufferSize - 4));
+
+ _resman->closeResource(CUR_PLAYER_ID);
+ _resman->closeResource(1);
- uint32 errorCode = saveData(slotNo, saveBufferMem, bufferSize);
+ uint32 errorCode = saveData(slotNo, saveBuffer, bufferSize);
- free(saveBufferMem);
+ free(saveBuffer);
if (errorCode != SR_OK) {
uint32 textId;
@@ -119,65 +114,6 @@ uint32 Sword2Engine::saveGame(uint16 slotNo, byte *desc) {
return errorCode;
}
-/**
- * Calculate size of required savegame buffer
- */
-
-uint32 Sword2Engine::findBufferSize() {
- // Size of savegame header + size of global variables
- return sizeof(_saveGameHeader) + _resman->fetchLen(1);
-}
-
-void Sword2Engine::fillSaveBuffer(byte *buffer, uint32 size, byte *desc) {
- // Set up the _saveGameHeader. Checksum gets filled in last of all.
- ScreenInfo *screenInfo = _screen->getScreenInfo();
-
- strcpy(_saveGameHeader.description, (char *)desc);
- _saveGameHeader.varLength = _resman->fetchLen(1);
- _saveGameHeader.screenId = screenInfo->background_layer_id;
- _saveGameHeader.runListId = _logic->getRunList();
- _saveGameHeader.feet_x = screenInfo->feet_x;
- _saveGameHeader.feet_y = screenInfo->feet_y;
- _saveGameHeader.music_id = _sound->getLoopingMusicId();
-
- memcpy(&_saveGameHeader.player_hub, _resman->openResource(CUR_PLAYER_ID) + sizeof(StandardHeader), sizeof(ObjectHub));
-
- _resman->closeResource(CUR_PLAYER_ID);
-
- // Get the logic, graphic and mega structures
- getPlayerStructures();
-
-#ifdef SCUMM_BIG_ENDIAN
- convertHeaderEndian(_saveGameHeader);
-#endif
-
- // Copy the header to the buffer, even though it isn't quite complete
- memcpy(buffer, &_saveGameHeader, sizeof(_saveGameHeader));
-
- // Get the global variables
-
- byte *varsRes = _resman->openResource(1);
- memcpy(buffer + sizeof(_saveGameHeader), varsRes, FROM_LE_32(_saveGameHeader.varLength));
-
-#ifdef SCUMM_BIG_ENDIAN
- uint32 *globalVars = (uint32 *)(buffer + sizeof(_saveGameHeader) + sizeof(StandardHeader));
- const uint numVars = (FROM_LE_32(_saveGameHeader.varLength) - sizeof(StandardHeader)) / 4;
-
- for (uint i = 0; i < numVars; i++)
- globalVars[i] = SWAP_BYTES_32(globalVars[i]);
-#endif
-
- _resman->closeResource(1);
-
- // Finally, get the checksum
-
- _saveGameHeader.checksum = TO_LE_32(calcChecksum(buffer + sizeof(_saveGameHeader.checksum), size - sizeof(_saveGameHeader.checksum)));
-
- // All done
-
- memcpy(buffer, &_saveGameHeader.checksum, sizeof(_saveGameHeader.checksum));
-}
-
uint32 Sword2Engine::saveData(uint16 slotNo, byte *buffer, uint32 bufferSize) {
char saveFileName[MAX_FILENAME_LEN];
@@ -284,20 +220,17 @@ uint32 Sword2Engine::restoreData(uint16 slotNo, byte *buffer, uint32 bufferSize)
}
uint32 Sword2Engine::restoreFromBuffer(byte *buffer, uint32 size) {
- // Get a copy of the header from the savegame buffer
- memcpy(&_saveGameHeader, buffer, sizeof(_saveGameHeader));
-
-#ifdef SCUMM_BIG_ENDIAN
- convertHeaderEndian(_saveGameHeader);
-#endif
+ Common::MemoryReadStream readS(buffer, size);
// Calc checksum & check that aginst the value stored in the header
- if (_saveGameHeader.checksum != calcChecksum(buffer + sizeof(_saveGameHeader.checksum), size - sizeof(_saveGameHeader.checksum))) {
+ if (readS.readUint32LE() != calcChecksum(buffer + 4, size - 4)) {
free(buffer);
return SR_ERR_INCOMPATIBLE;
}
+ readS.seek(SAVE_DESCRIPTION_LEN, SEEK_CUR);
+
// 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.
@@ -305,49 +238,75 @@ uint32 Sword2Engine::restoreFromBuffer(byte *buffer, uint32 size) {
// Historical note: During development, earlier savegames would often
// be shorter than the current expected length.
- if (_saveGameHeader.varLength != _resman->fetchLen(1)) {
+ if (readS.readUint32LE() != _resman->fetchLen(1)) {
free(buffer);
return SR_ERR_INCOMPATIBLE;
}
- // Clean out system
+ byte *globalVars = _resman->openResource(1);
+ byte *objectHub = _resman->openResource(CUR_PLAYER_ID) + ResHeader::size();
+
+ uint32 screenId = readS.readUint32LE();
+ uint32 runListId = readS.readUint32LE();
+ uint32 feetX = readS.readUint32LE();
+ uint32 feetY = readS.readUint32LE();
+ uint32 musicId = readS.readUint32LE();
// Trash all resources from memory except player object & global vars
_resman->killAll(false);
-
- // Clean out the system kill list (no more objects to kill)
_logic->resetKillList();
- // Object hub is just after the standard header
- memcpy(_resman->openResource(CUR_PLAYER_ID) + sizeof(StandardHeader), &_saveGameHeader.player_hub, sizeof(ObjectHub));
+ readS.read(objectHub, ObjectHub::size());
+ readS.read(_logic->_saveLogic, ObjectLogic::size());
+ readS.read(_logic->_saveGraphic, ObjectGraphic::size());
+ readS.read(_logic->_saveMega, ObjectMega::size());
- _resman->closeResource(CUR_PLAYER_ID);
+ // 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.
- // Fill in the player object structures from the header
- putPlayerStructures();
+ // Script no. 8 - 'george_savedata_return' calls fnGetPlayerSaveData
+ _logic->runResScript(CUR_PLAYER_ID, 8);
- // Copy variables from savegame buffer to memory
- byte *varsRes = _resman->openResource(1);
+ // Script no. 14 - 'set_up_nico_anim_tables'
+ _logic->runResScript(CUR_PLAYER_ID, 14);
- memcpy(varsRes, buffer + sizeof(_saveGameHeader), _saveGameHeader.varLength);
+ // Which megaset was the player at the time of saving?
+ ObjectMega obMega(_logic->_saveMega);
-#ifdef SCUMM_BIG_ENDIAN
- uint32 *globalVars = (uint32 *)(varsRes + sizeof(StandardHeader));
- const uint numVars = (_saveGameHeader.varLength - sizeof(StandardHeader)) / 4;
+ uint32 scriptNo = 0;
- for (uint i = 0; i < numVars; i++)
- globalVars[i] = SWAP_BYTES_32(globalVars[i]);
-#endif
+ switch (obMega.getMegasetRes()) {
+ case 36: // GeoMega:
+ scriptNo = 9; // script no.9 - 'player_is_george'
+ break;
+ case 2003: // GeoMegaB:
+ scriptNo = 13; // script no.13 - 'player_is_georgeB'
+ break;
+ case 1366: // NicMegaA:
+ scriptNo = 11; // script no.11 - 'player_is_nicoA'
+ break;
+ case 1437: // NicMegaB:
+ scriptNo = 12; // script no.12 - 'player_is_nicoB'
+ break;
+ case 1575: // NicMegaC:
+ scriptNo = 10; // script no.10 - 'player_is_nicoC'
+ break;
+ }
+
+ _logic->runResScript(CUR_PLAYER_ID, scriptNo);
+
+ // Copy variables from savegame buffer to memory
+ readS.read(globalVars, _resman->fetchLen(1));
+ _resman->closeResource(CUR_PLAYER_ID);
_resman->closeResource(1);
- // 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[0] = screenId;
pars[1] = 1;
_logic->fnInitBackground(pars);
@@ -361,19 +320,19 @@ uint32 Sword2Engine::restoreFromBuffer(byte *buffer, uint32 size) {
// Remember that these can change through the game, so need saving &
// restoring too.
- screenInfo->feet_x = _saveGameHeader.feet_x;
- screenInfo->feet_y = _saveGameHeader.feet_y;
+ screenInfo->feet_x = feetX;
+ screenInfo->feet_y = feetY;
// Start the new run list
- _logic->expressChangeSession(_saveGameHeader.runListId);
+ _logic->expressChangeSession(runListId);
// Force in the new scroll position, so unsightly scroll-catch-up does
// not occur when screen first draws after returning from restore panel
// Set the screen record of player position - ready for setScrolling()
- screenInfo->player_feet_x = _saveGameHeader.mega.feet_x;
- screenInfo->player_feet_y = _saveGameHeader.mega.feet_y;
+ screenInfo->player_feet_x = obMega.getFeetX();
+ screenInfo->player_feet_y = obMega.getFeetY();
// if this screen is wide, recompute the scroll offsets now
if (screenInfo->scroll_flag)
@@ -386,8 +345,8 @@ uint32 Sword2Engine::restoreFromBuffer(byte *buffer, uint32 size) {
// in systemMenuMouse(), but with ScummVM we have other ways of
// restoring savegames so it's easier to put it here as well.
- if (_saveGameHeader.music_id) {
- pars[0] = _saveGameHeader.music_id;
+ if (musicId) {
+ pars[0] = musicId;
pars[1] = FX_LOOP;
_logic->fnPlayMusic(pars);
} else
@@ -411,12 +370,10 @@ uint32 Sword2Engine::getSaveDescription(uint16 slotNo, byte *description) {
return SR_ERR_FILEOPEN;
}
- SaveGameHeader dummy;
+ in->readUint32LE();
+ in->read(description, SAVE_DESCRIPTION_LEN);
- in->read(&dummy, sizeof(dummy));
delete in;
-
- strcpy((char *)description, dummy.description);
return SR_OK;
}
@@ -442,71 +399,6 @@ bool Sword2Engine::saveExists(uint16 slotNo) {
return true;
}
-/**
- * Request the player object structures which need saving.
- */
-
-void Sword2Engine::getPlayerStructures() {
- StandardHeader *head = (StandardHeader *)_resman->openResource(CUR_PLAYER_ID);
-
- assert(head->fileType == GAME_OBJECT);
-
- char *raw_script_ad = (char *)head;
-
- // Script no. 7 - 'george_savedata_request' calls fnPassPlayerSaveData
- uint32 null_pc = 7;
-
- _logic->runScript(raw_script_ad, raw_script_ad, &null_pc);
- _resman->closeResource(CUR_PLAYER_ID);
-}
-
-/**
- * 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.
- */
-
-void Sword2Engine::putPlayerStructures() {
- StandardHeader *head = (StandardHeader *)_resman->openResource(CUR_PLAYER_ID);
-
- assert(head->fileType == GAME_OBJECT);
-
- char *raw_script_ad = (char *)head;
-
- // Script no. 8 - 'george_savedata_return' calls fnGetPlayerSaveData
-
- uint32 null_pc = 8;
- _logic->runScript(raw_script_ad, raw_script_ad, &null_pc);
-
- // 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?
-
- switch (_saveGameHeader.mega.megaset_res) {
- case 36: // GeoMega:
- null_pc = 9; // script no.9 - 'player_is_george'
- break;
- case 2003: // GeoMegaB:
- null_pc = 13; // script no.13 - 'player_is_georgeB'
- break;
- case 1366: // NicMegaA:
- null_pc = 11; // script no.11 - 'player_is_nicoA'
- break;
- case 1437: // NicMegaB:
- null_pc = 12; // script no.12 - 'player_is_nicoB'
- break;
- case 1575: // NicMegaC:
- null_pc = 10; // script no.10 - 'player_is_nicoC'
- break;
- }
-
- _logic->runScript(raw_script_ad, raw_script_ad, &null_pc);
- _resman->closeResource(CUR_PLAYER_ID);
-}
-
uint32 Sword2Engine::calcChecksum(byte *buffer, uint32 size) {
uint32 total = 0;