mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-21 09:21:08 +00:00
f1fc68773d
svn-id: r8493
958 lines
28 KiB
C++
958 lines
28 KiB
C++
/* ScummVM - Scumm Interpreter
|
|
* Copyright (C) 2001 Ludvig Strigeus
|
|
* Copyright (C) 2001-2003 The ScummVM project
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* $Header$
|
|
*
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
#include "actor.h"
|
|
#include "charset.h"
|
|
#include "imuse.h"
|
|
#include "imuse_digi.h"
|
|
#include "resource.h"
|
|
#include "saveload.h"
|
|
#include "scumm.h"
|
|
#include "sound.h"
|
|
#include "verbs.h"
|
|
#include "common/config-file.h"
|
|
#include "sound/mixer.h"
|
|
|
|
struct SaveGameHeader {
|
|
uint32 type;
|
|
uint32 size;
|
|
uint32 ver;
|
|
char name[32];
|
|
};
|
|
|
|
|
|
void Scumm::requestSave(int slot, const char *name) {
|
|
_saveLoadSlot = slot;
|
|
_saveLoadCompatible = false;
|
|
_saveLoadFlag = 1; // 1 for save
|
|
strcpy(_saveLoadName, name);
|
|
}
|
|
|
|
void Scumm::requestLoad(int slot) {
|
|
_saveLoadSlot = slot;
|
|
_saveLoadCompatible = false;
|
|
_saveLoadFlag = 2; // 2 for load
|
|
}
|
|
|
|
bool Scumm::saveState(int slot, bool compat, SaveFileManager *mgr) {
|
|
char filename[256];
|
|
SaveFile *out;
|
|
SaveGameHeader hdr;
|
|
|
|
makeSavegameName(filename, slot, compat);
|
|
|
|
if (!(out = mgr->open_savefile(filename, true)))
|
|
return false;
|
|
|
|
memcpy(hdr.name, _saveLoadName, sizeof(hdr.name));
|
|
|
|
hdr.type = MKID('SCVM');
|
|
hdr.size = 0;
|
|
hdr.ver = TO_LE_32(CURRENT_VER);
|
|
|
|
out->fwrite(&hdr, sizeof(hdr), 1);
|
|
|
|
Serializer ser(out, true, CURRENT_VER);
|
|
saveOrLoad(&ser, CURRENT_VER);
|
|
delete out;
|
|
debug(1, "State saved as '%s'", filename);
|
|
return true;
|
|
}
|
|
|
|
bool Scumm::loadState(int slot, bool compat, SaveFileManager *mgr) {
|
|
char filename[256];
|
|
SaveFile *out;
|
|
int i, j;
|
|
SaveGameHeader hdr;
|
|
int sb, sh;
|
|
|
|
makeSavegameName(filename, slot, compat);
|
|
if (!(out = mgr->open_savefile(filename, false)))
|
|
return false;
|
|
|
|
out->fread(&hdr, sizeof(hdr), 1);
|
|
if (hdr.type != MKID('SCVM')) {
|
|
warning("Invalid savegame '%s'", filename);
|
|
delete out;
|
|
return false;
|
|
}
|
|
|
|
// In older versions of ScummVM, the header version was not endian safe.
|
|
// We account for that by retrying once with swapped byte order.
|
|
if (hdr.ver > CURRENT_VER)
|
|
hdr.ver = SWAP_BYTES_32(hdr.ver);
|
|
if (hdr.ver < VER_V7 || hdr.ver > CURRENT_VER)
|
|
{
|
|
warning("Invalid version of '%s'", filename);
|
|
delete out;
|
|
return false;
|
|
}
|
|
|
|
// Due to a bug in scummvm up to and including 0.3.0, save games could be saved
|
|
// in the V8/V9 format but were tagged with a V7 mark. Ouch. So we just pretend V7 == V8 here
|
|
if (hdr.ver == VER_V7)
|
|
hdr.ver = VER_V8;
|
|
|
|
memcpy(_saveLoadName, hdr.name, sizeof(hdr.name));
|
|
|
|
if (_imuseDigital) {
|
|
_imuseDigital->stopAll();
|
|
}
|
|
|
|
_sound->stopBundleMusic();
|
|
_sound->stopCD();
|
|
_sound->pauseSounds(true);
|
|
|
|
CHECK_HEAP
|
|
closeRoom();
|
|
memset(_inventory, 0, sizeof(_inventory[0]) * _numInventory);
|
|
|
|
/* Nuke all resources */
|
|
for (i = rtFirst; i <= rtLast; i++)
|
|
if (i != rtTemp && i != rtBuffer && (i != rtSound || _saveSound))
|
|
for (j = 0; j < res.num[i]; j++) {
|
|
nukeResource(i, j);
|
|
res.flags[i][j] = 0;
|
|
}
|
|
|
|
initScummVars();
|
|
|
|
if (_features & GF_OLD_BUNDLE)
|
|
loadCharset(0); // FIXME - HACK ?
|
|
|
|
Serializer ser(out, false, hdr.ver);
|
|
saveOrLoad(&ser, hdr.ver);
|
|
delete out;
|
|
|
|
sb = _screenB;
|
|
sh = _screenH;
|
|
|
|
gdi._mask.top = gdi._mask.left = 32767;
|
|
gdi._mask.right = gdi._mask.bottom = 0;
|
|
_charset->_hasMask = false;
|
|
|
|
initScreens(0, 0, _screenWidth, _screenHeight);
|
|
|
|
// Force a fade to black
|
|
int old_screenEffectFlag = _screenEffectFlag;
|
|
_screenEffectFlag = true;
|
|
fadeOut(129);
|
|
_screenEffectFlag = old_screenEffectFlag ? true : false;
|
|
|
|
initScreens(0, sb, _screenWidth, sh);
|
|
|
|
_completeScreenRedraw = true;
|
|
|
|
if (_version == 1) {
|
|
setupC64Palette();
|
|
setDirtyColors(0, 15);
|
|
} else if (_features & GF_16COLOR) {
|
|
// HACK: There was a time when ScummVM didn't store the
|
|
// palette in _currentPalette for 16-color games. To avoid
|
|
// breaking savegame compatibility, always set up the default
|
|
// palette after loading a game.
|
|
setupEGAPalette();
|
|
setDirtyColors(0, 15);
|
|
} else
|
|
setDirtyColors(0, 255);
|
|
|
|
_lastCodePtr = NULL;
|
|
|
|
_drawObjectQueNr = 0;
|
|
_verbMouseOver = 0;
|
|
|
|
if (_features & GF_NEW_CAMERA)
|
|
cameraMoved();
|
|
|
|
initBGBuffers(_roomHeight);
|
|
|
|
if (_version == 2) {
|
|
// Regenerate strip table when loading
|
|
_roomStrips = gdi.generateStripTable(getResourceAddress(rtRoom, _roomResource) + _IM00_offs,
|
|
_roomWidth, _roomHeight, _roomStrips);
|
|
}
|
|
|
|
if ((_features & GF_AUDIOTRACKS) && VAR(VAR_MUSIC_TIMER) > 0)
|
|
_sound->startCDTimer();
|
|
|
|
CHECK_HEAP debug(1, "State loaded from '%s'", filename);
|
|
|
|
_sound->pauseSounds(false);
|
|
|
|
return true;
|
|
}
|
|
|
|
void Scumm::makeSavegameName(char *out, int slot, bool compatible) {
|
|
const char *dir = getSavePath();
|
|
|
|
#ifndef __PALM_OS__
|
|
sprintf(out, "%s%s.%c%.2d", dir, _game_name, compatible ? 'c' : 's', slot);
|
|
#else
|
|
sprintf(out, "%s%s.%s%.2d", dir, _game_name, compatible ? "c" : "s", slot);
|
|
#endif
|
|
}
|
|
|
|
void Scumm::listSavegames(bool *marks, int num, SaveFileManager *mgr) {
|
|
char prefix[256];
|
|
makeSavegameName(prefix, 99, false);
|
|
prefix[strlen(prefix)-2] = 0;
|
|
mgr->list_savefiles(prefix, marks, num);
|
|
}
|
|
|
|
bool Scumm::getSavegameName(int slot, char *desc, SaveFileManager *mgr) {
|
|
char filename[256];
|
|
SaveFile *out;
|
|
SaveGameHeader hdr;
|
|
int len;
|
|
|
|
makeSavegameName(filename, slot, false);
|
|
if (!(out = mgr->open_savefile(filename, false))) {
|
|
strcpy(desc, "");
|
|
return false;
|
|
}
|
|
len = out->fread(&hdr, sizeof(hdr), 1);
|
|
delete out;
|
|
|
|
if (len != 1 || hdr.type != MKID('SCVM')) {
|
|
strcpy(desc, "Invalid savegame");
|
|
return false;
|
|
}
|
|
|
|
if (hdr.ver > CURRENT_VER)
|
|
hdr.ver = TO_LE_32(hdr.ver);
|
|
if (hdr.ver < VER_V7 || hdr.ver > CURRENT_VER) {
|
|
strcpy(desc, "Invalid version");
|
|
return false;
|
|
}
|
|
|
|
memcpy(desc, hdr.name, sizeof(hdr.name));
|
|
desc[sizeof(hdr.name) - 1] = 0;
|
|
return true;
|
|
}
|
|
|
|
void Scumm::saveOrLoad(Serializer *s, uint32 savegameVersion) {
|
|
const SaveLoadEntry objectEntries[] = {
|
|
MKLINE(ObjectData, OBIMoffset, sleUint32, VER_V8),
|
|
MKLINE(ObjectData, OBCDoffset, sleUint32, VER_V8),
|
|
MKLINE(ObjectData, walk_x, sleUint16, VER_V8),
|
|
MKLINE(ObjectData, walk_y, sleUint16, VER_V8),
|
|
MKLINE(ObjectData, obj_nr, sleUint16, VER_V8),
|
|
MKLINE(ObjectData, x_pos, sleInt16, VER_V8),
|
|
MKLINE(ObjectData, y_pos, sleInt16, VER_V8),
|
|
MKLINE(ObjectData, width, sleUint16, VER_V8),
|
|
MKLINE(ObjectData, height, sleUint16, VER_V8),
|
|
MKLINE(ObjectData, actordir, sleByte, VER_V8),
|
|
MKLINE(ObjectData, parentstate, sleByte, VER_V8),
|
|
MKLINE(ObjectData, parent, sleByte, VER_V8),
|
|
MKLINE(ObjectData, state, sleByte, VER_V8),
|
|
MKLINE(ObjectData, fl_object_index, sleByte, VER_V8),
|
|
MKEND()
|
|
};
|
|
|
|
const SaveLoadEntry *actorEntries = Actor::getSaveLoadEntries();
|
|
|
|
const SaveLoadEntry verbEntries[] = {
|
|
MKLINE(VerbSlot, x, sleInt16, VER_V8),
|
|
MKLINE(VerbSlot, y, sleInt16, VER_V8),
|
|
MKLINE(VerbSlot, right, sleInt16, VER_V8),
|
|
MKLINE(VerbSlot, bottom, sleInt16, VER_V8),
|
|
MKLINE(VerbSlot, old.left, sleInt16, VER_V8),
|
|
MKLINE(VerbSlot, old.top, sleInt16, VER_V8),
|
|
MKLINE(VerbSlot, old.right, sleInt16, VER_V8),
|
|
MKLINE(VerbSlot, old.bottom, sleInt16, VER_V8),
|
|
|
|
MKLINE_OLD(VerbSlot, verbid, sleByte, VER_V8, VER_V11),
|
|
MKLINE(VerbSlot, verbid, sleInt16, VER_V12),
|
|
|
|
MKLINE(VerbSlot, color, sleByte, VER_V8),
|
|
MKLINE(VerbSlot, hicolor, sleByte, VER_V8),
|
|
MKLINE(VerbSlot, dimcolor, sleByte, VER_V8),
|
|
MKLINE(VerbSlot, bkcolor, sleByte, VER_V8),
|
|
MKLINE(VerbSlot, type, sleByte, VER_V8),
|
|
MKLINE(VerbSlot, charset_nr, sleByte, VER_V8),
|
|
MKLINE(VerbSlot, curmode, sleByte, VER_V8),
|
|
MKLINE(VerbSlot, saveid, sleByte, VER_V8),
|
|
MKLINE(VerbSlot, key, sleByte, VER_V8),
|
|
MKLINE(VerbSlot, center, sleByte, VER_V8),
|
|
MKLINE(VerbSlot, field_1B, sleByte, VER_V8),
|
|
MKLINE(VerbSlot, imgindex, sleUint16, VER_V8),
|
|
MKEND()
|
|
};
|
|
|
|
const SaveLoadEntry mainEntries[] = {
|
|
MKLINE(Scumm, _roomWidth, sleUint16, VER_V8),
|
|
MKLINE(Scumm, _roomHeight, sleUint16, VER_V8),
|
|
MKLINE(Scumm, _ENCD_offs, sleUint32, VER_V8),
|
|
MKLINE(Scumm, _EXCD_offs, sleUint32, VER_V8),
|
|
MKLINE(Scumm, _IM00_offs, sleUint32, VER_V8),
|
|
MKLINE(Scumm, _CLUT_offs, sleUint32, VER_V8),
|
|
MK_OBSOLETE(Scumm, _EPAL_offs, sleUint32, VER_V8, VER_V9),
|
|
MKLINE(Scumm, _PALS_offs, sleUint32, VER_V8),
|
|
MKLINE(Scumm, _curPalIndex, sleByte, VER_V8),
|
|
MKLINE(Scumm, _currentRoom, sleByte, VER_V8),
|
|
MKLINE(Scumm, _roomResource, sleByte, VER_V8),
|
|
MKLINE(Scumm, _numObjectsInRoom, sleByte, VER_V8),
|
|
MKLINE(Scumm, _currentScript, sleByte, VER_V8),
|
|
MKARRAY(Scumm, _localScriptList[0], sleUint32, NUM_LOCALSCRIPT, VER_V8),
|
|
|
|
|
|
// vm.localvar grew from 25 to 40 script entries and then from
|
|
// 16 to 32 bit variables (but that wasn't reflect here)... and
|
|
// THEN from 16 to 25 variables.
|
|
MKARRAY2_OLD(Scumm, vm.localvar[0][0], sleUint16, 17, 25, (byte*)vm.localvar[1] - (byte*)vm.localvar[0], VER_V8, VER_V8),
|
|
MKARRAY2_OLD(Scumm, vm.localvar[0][0], sleUint16, 17, 40, (byte*)vm.localvar[1] - (byte*)vm.localvar[0], VER_V9, VER_V14),
|
|
|
|
// We used to save 25 * 40 = 1000 blocks; but actually, each 'row consisted of 26 entry,
|
|
// i.e. 26 * 40 = 1040. Thus the last 40 blocks of localvar where not saved at all. To be
|
|
// able to load this screwed format, we use a trick: We load 26 * 38 = 988 blocks.
|
|
// Then, we mark the followin 12 blocks (24 bytes) as obsolete.
|
|
MKARRAY2_OLD(Scumm, vm.localvar[0][0], sleUint16, 26, 38, (byte*)vm.localvar[1] - (byte*)vm.localvar[0], VER_V15, VER_V17),
|
|
MK_OBSOLETE_ARRAY(Scumm, vm.localvar[39][0], sleUint16, 12, VER_V15, VER_V17),
|
|
|
|
// This was the first proper multi dimensional version of the localvars, with 32 bit values
|
|
MKARRAY2_OLD(Scumm, vm.localvar[0][0], sleUint32, 26, 40, (byte*)vm.localvar[1] - (byte*)vm.localvar[0], VER_V18, VER_V19),
|
|
|
|
// Then we doubled the script slots again, from 40 to 80
|
|
MKARRAY2(Scumm, vm.localvar[0][0], sleUint32, 26, NUM_SCRIPT_SLOT, (byte*)vm.localvar[1] - (byte*)vm.localvar[0], VER_V20),
|
|
|
|
|
|
MKARRAY(Scumm, _resourceMapper[0], sleByte, 128, VER_V8),
|
|
MKARRAY(Scumm, _charsetColorMap[0], sleByte, 16, VER_V8),
|
|
|
|
// _charsetData grew from 10*16 to 15*16 bytes
|
|
MKARRAY_OLD(Scumm, _charsetData[0][0], sleByte, 10 * 16, VER_V8, VER_V9),
|
|
MKARRAY(Scumm, _charsetData[0][0], sleByte, 15 * 16, VER_V10),
|
|
|
|
MKLINE(Scumm, _curExecScript, sleUint16, VER_V8),
|
|
|
|
MKLINE(Scumm, camera._dest.x, sleInt16, VER_V8),
|
|
MKLINE(Scumm, camera._dest.y, sleInt16, VER_V8),
|
|
MKLINE(Scumm, camera._cur.x, sleInt16, VER_V8),
|
|
MKLINE(Scumm, camera._cur.y, sleInt16, VER_V8),
|
|
MKLINE(Scumm, camera._last.x, sleInt16, VER_V8),
|
|
MKLINE(Scumm, camera._last.y, sleInt16, VER_V8),
|
|
MKLINE(Scumm, camera._accel.x, sleInt16, VER_V8),
|
|
MKLINE(Scumm, camera._accel.y, sleInt16, VER_V8),
|
|
MKLINE(Scumm, _screenStartStrip, sleInt16, VER_V8),
|
|
MKLINE(Scumm, _screenEndStrip, sleInt16, VER_V8),
|
|
MKLINE(Scumm, camera._mode, sleByte, VER_V8),
|
|
MKLINE(Scumm, camera._follows, sleByte, VER_V8),
|
|
MKLINE(Scumm, camera._leftTrigger, sleInt16, VER_V8),
|
|
MKLINE(Scumm, camera._rightTrigger, sleInt16, VER_V8),
|
|
MKLINE(Scumm, camera._movingToActor, sleUint16, VER_V8),
|
|
|
|
MKLINE(Scumm, _actorToPrintStrFor, sleByte, VER_V8),
|
|
MKLINE(Scumm, _charsetColor, sleByte, VER_V8),
|
|
|
|
// _charsetBufPos was changed from byte to int
|
|
MKLINE_OLD(Scumm, _charsetBufPos, sleByte, VER_V8, VER_V9),
|
|
MKLINE(Scumm, _charsetBufPos, sleInt16, VER_V10),
|
|
|
|
MKLINE(Scumm, _haveMsg, sleByte, VER_V8),
|
|
MKLINE(Scumm, _useTalkAnims, sleByte, VER_V8),
|
|
|
|
MKLINE(Scumm, _talkDelay, sleInt16, VER_V8),
|
|
MKLINE(Scumm, _defaultTalkDelay, sleInt16, VER_V8),
|
|
MKLINE(Scumm, _numInMsgStack, sleInt16, VER_V8),
|
|
MKLINE(Scumm, _sentenceNum, sleByte, VER_V8),
|
|
|
|
MKLINE(Scumm, vm.cutSceneStackPointer, sleByte, VER_V8),
|
|
MKARRAY(Scumm, vm.cutScenePtr[0], sleUint32, 5, VER_V8),
|
|
MKARRAY(Scumm, vm.cutSceneScript[0], sleByte, 5, VER_V8),
|
|
MKARRAY(Scumm, vm.cutSceneData[0], sleInt16, 5, VER_V8),
|
|
MKLINE(Scumm, vm.cutSceneScriptIndex, sleInt16, VER_V8),
|
|
|
|
MKLINE(Scumm, _numNestedScripts, sleByte, VER_V8),
|
|
MKLINE(Scumm, _userPut, sleByte, VER_V8),
|
|
MKLINE(Scumm, _userState, sleUint16, VER_V17),
|
|
MKLINE(Scumm, _cursor.state, sleByte, VER_V8),
|
|
MK_OBSOLETE(Scumm, gdi._cursorActive, sleByte, VER_V8, VER_V20),
|
|
MKLINE(Scumm, _currentCursor, sleByte, VER_V8),
|
|
MKARRAY(Scumm, _grabbedCursor[0], sleByte, 8192, VER_V20),
|
|
MKLINE(Scumm, _cursor.width, sleInt16, VER_V20),
|
|
MKLINE(Scumm, _cursor.height, sleInt16, VER_V20),
|
|
MKLINE(Scumm, _cursor.hotspotX, sleInt16, VER_V20),
|
|
MKLINE(Scumm, _cursor.hotspotY, sleInt16, VER_V20),
|
|
MKLINE(Scumm, _cursor.animate, sleByte, VER_V20),
|
|
MKLINE(Scumm, _cursor.animateIndex, sleByte, VER_V20),
|
|
MKLINE(Scumm, _mouse.x, sleInt16, VER_V20),
|
|
MKLINE(Scumm, _mouse.y, sleInt16, VER_V20),
|
|
|
|
MKLINE(Scumm, _doEffect, sleByte, VER_V8),
|
|
MKLINE(Scumm, _switchRoomEffect, sleByte, VER_V8),
|
|
MKLINE(Scumm, _newEffect, sleByte, VER_V8),
|
|
MKLINE(Scumm, _switchRoomEffect2, sleByte, VER_V8),
|
|
MKLINE(Scumm, _BgNeedsRedraw, sleByte, VER_V8),
|
|
|
|
// The state of palManipulate is stored only since V10
|
|
MKLINE(Scumm, _palManipStart, sleByte, VER_V10),
|
|
MKLINE(Scumm, _palManipEnd, sleByte, VER_V10),
|
|
MKLINE(Scumm, _palManipCounter, sleUint16, VER_V10),
|
|
|
|
// gfxUsageBits grew from 200 to 410 entries. Then 3 * 410 entries:
|
|
MKARRAY_OLD(Scumm, gfxUsageBits[0], sleUint32, 200, VER_V8, VER_V9),
|
|
MKARRAY_OLD(Scumm, gfxUsageBits[0], sleUint32, 410, VER_V10, VER_V13),
|
|
MKARRAY(Scumm, gfxUsageBits[0], sleUint32, 3 * 410, VER_V14),
|
|
|
|
MKLINE(Scumm, gdi._transparentColor, sleByte, VER_V8),
|
|
MKARRAY(Scumm, _currentPalette[0], sleByte, 768, VER_V8),
|
|
|
|
MKARRAY(Scumm, _proc_special_palette[0], sleByte, 256, VER_V8),
|
|
|
|
MKARRAY(Scumm, _charsetBuffer[0], sleByte, 256, VER_V8),
|
|
|
|
MKLINE(Scumm, _egoPositioned, sleByte, VER_V8),
|
|
|
|
// gdi._imgBufOffs grew from 4 to 5 entries :
|
|
MKARRAY_OLD(Scumm, gdi._imgBufOffs[0], sleUint16, 4, VER_V8, VER_V9),
|
|
MKARRAY(Scumm, gdi._imgBufOffs[0], sleUint16, 5, VER_V10),
|
|
|
|
MKLINE(Scumm, gdi._numZBuffer, sleByte, VER_V8),
|
|
|
|
MKLINE(Scumm, _screenEffectFlag, sleByte, VER_V8),
|
|
|
|
MK_OBSOLETE(Scumm, _randSeed1, sleUint32, VER_V8, VER_V9),
|
|
MK_OBSOLETE(Scumm, _randSeed2, sleUint32, VER_V8, VER_V9),
|
|
|
|
// Converted _shakeEnabled to boolean and added a _shakeFrame field.
|
|
MKLINE_OLD(Scumm, _shakeEnabled, sleInt16, VER_V8, VER_V9),
|
|
MKLINE(Scumm, _shakeEnabled, sleByte, VER_V10),
|
|
MKLINE(Scumm, _shakeFrame, sleUint32, VER_V10),
|
|
|
|
MKLINE(Scumm, _keepText, sleByte, VER_V8),
|
|
|
|
MKLINE(Scumm, _screenB, sleUint16, VER_V8),
|
|
MKLINE(Scumm, _screenH, sleUint16, VER_V8),
|
|
|
|
MK_OBSOLETE(Scumm, _cd_track, sleInt16, VER_V9, VER_V9),
|
|
MK_OBSOLETE(Scumm, _cd_loops, sleInt16, VER_V9, VER_V9),
|
|
MK_OBSOLETE(Scumm, _cd_frame, sleInt16, VER_V9, VER_V9),
|
|
MK_OBSOLETE(Scumm, _cd_end, sleInt16, VER_V9, VER_V9),
|
|
|
|
MKEND()
|
|
};
|
|
|
|
const SaveLoadEntry scriptSlotEntries[] = {
|
|
MKLINE(ScriptSlot, offs, sleUint32, VER_V8),
|
|
MKLINE(ScriptSlot, delay, sleInt32, VER_V8),
|
|
MKLINE(ScriptSlot, number, sleUint16, VER_V8),
|
|
MKLINE(ScriptSlot, delayFrameCount, sleUint16, VER_V8),
|
|
MKLINE(ScriptSlot, status, sleByte, VER_V8),
|
|
MKLINE(ScriptSlot, where, sleByte, VER_V8),
|
|
MKLINE(ScriptSlot, freezeResistant, sleByte, VER_V8),
|
|
MKLINE(ScriptSlot, recursive, sleByte, VER_V8),
|
|
MKLINE(ScriptSlot, freezeCount, sleByte, VER_V8),
|
|
MKLINE(ScriptSlot, didexec, sleByte, VER_V8),
|
|
MKLINE(ScriptSlot, cutsceneOverride, sleByte, VER_V8),
|
|
MK_OBSOLETE(ScriptSlot, unk5, sleByte, VER_V8, VER_V10),
|
|
MKEND()
|
|
};
|
|
|
|
const SaveLoadEntry nestedScriptEntries[] = {
|
|
MKLINE(NestedScript, number, sleUint16, VER_V8),
|
|
MKLINE(NestedScript, where, sleByte, VER_V8),
|
|
MKLINE(NestedScript, slot, sleByte, VER_V8),
|
|
MKEND()
|
|
};
|
|
|
|
const SaveLoadEntry sentenceTabEntries[] = {
|
|
MKLINE(SentenceTab, verb, sleUint8, VER_V8),
|
|
MKLINE(SentenceTab, preposition, sleUint8, VER_V8),
|
|
MKLINE(SentenceTab, objectA, sleUint16, VER_V8),
|
|
MKLINE(SentenceTab, objectB, sleUint16, VER_V8),
|
|
MKLINE(SentenceTab, freezeCount, sleUint8, VER_V8),
|
|
MKEND()
|
|
};
|
|
|
|
const SaveLoadEntry stringTabEntries[] = {
|
|
// TODO - It makes no sense to have all these t_* fields in StringTab
|
|
// Rather let's dump them all when the save game format changes, and
|
|
// keep two StringTab objects where we have one now: a "normal" one,
|
|
// and a temporar y"t_" one.
|
|
// Then backup/restore of a StringTab entry becomes a one liner.
|
|
MKLINE(StringTab, xpos, sleInt16, VER_V8),
|
|
MKLINE(StringTab, t_xpos, sleInt16, VER_V8),
|
|
MKLINE(StringTab, ypos, sleInt16, VER_V8),
|
|
MKLINE(StringTab, t_ypos, sleInt16, VER_V8),
|
|
MKLINE(StringTab, right, sleInt16, VER_V8),
|
|
MKLINE(StringTab, t_right, sleInt16, VER_V8),
|
|
MKLINE(StringTab, color, sleInt8, VER_V8),
|
|
MKLINE(StringTab, t_color, sleInt8, VER_V8),
|
|
MKLINE(StringTab, charset, sleInt8, VER_V8),
|
|
MKLINE(StringTab, t_charset, sleInt8, VER_V8),
|
|
MKLINE(StringTab, center, sleByte, VER_V8),
|
|
MKLINE(StringTab, t_center, sleByte, VER_V8),
|
|
MKLINE(StringTab, overhead, sleByte, VER_V8),
|
|
MKLINE(StringTab, t_overhead, sleByte, VER_V8),
|
|
MKLINE(StringTab, no_talk_anim, sleByte, VER_V8),
|
|
MKLINE(StringTab, t_no_talk_anim, sleByte, VER_V8),
|
|
MKEND()
|
|
};
|
|
|
|
const SaveLoadEntry colorCycleEntries[] = {
|
|
MKLINE(ColorCycle, delay, sleUint16, VER_V8),
|
|
MKLINE(ColorCycle, counter, sleUint16, VER_V8),
|
|
MKLINE(ColorCycle, flags, sleUint16, VER_V8),
|
|
MKLINE(ColorCycle, start, sleByte, VER_V8),
|
|
MKLINE(ColorCycle, end, sleByte, VER_V8),
|
|
MKEND()
|
|
};
|
|
|
|
const SaveLoadEntry scaleSlotsEntries[] = {
|
|
MKLINE(ScaleSlot, x1, sleUint16, VER_V13),
|
|
MKLINE(ScaleSlot, y1, sleUint16, VER_V13),
|
|
MKLINE(ScaleSlot, scale1, sleUint16, VER_V13),
|
|
MKLINE(ScaleSlot, x2, sleUint16, VER_V13),
|
|
MKLINE(ScaleSlot, y2, sleUint16, VER_V13),
|
|
MKLINE(ScaleSlot, scale2, sleUint16, VER_V13),
|
|
MKEND()
|
|
};
|
|
|
|
int i, j;
|
|
int var120Backup;
|
|
int var98Backup;
|
|
|
|
if (!s->isSaving() && _saveSound) {
|
|
_sound->stopAllSounds();
|
|
if (_mixer) {
|
|
if (_imuseDigital) {
|
|
_imuseDigital->stopAll();
|
|
} else {
|
|
_mixer->stopAll();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Because old savegames won't fill the entire gfxUsageBits[] array,
|
|
// clear it here just to be sure it won't hold any unforseen garbage.
|
|
if (!s->isSaving())
|
|
memset(gfxUsageBits, 0, sizeof(gfxUsageBits));
|
|
|
|
s->saveLoadEntries(this, mainEntries);
|
|
|
|
if (!s->isSaving() && savegameVersion < VER_V14)
|
|
upgradeGfxUsageBits();
|
|
|
|
if (!s->isSaving() && savegameVersion >= VER_V20) {
|
|
updateCursor();
|
|
_system->warp_mouse(_mouse.x, _mouse.y);
|
|
}
|
|
|
|
s->saveLoadArrayOf(_actors, _numActors, sizeof(_actors[0]), actorEntries);
|
|
|
|
if (savegameVersion < VER_V9)
|
|
s->saveLoadArrayOf(vm.slot, 25, sizeof(vm.slot[0]), scriptSlotEntries);
|
|
else if (savegameVersion < VER_V20)
|
|
s->saveLoadArrayOf(vm.slot, 40, sizeof(vm.slot[0]), scriptSlotEntries);
|
|
else
|
|
s->saveLoadArrayOf(vm.slot, NUM_SCRIPT_SLOT, sizeof(vm.slot[0]), scriptSlotEntries);
|
|
|
|
s->saveLoadArrayOf(_objs, _numLocalObjects, sizeof(_objs[0]), objectEntries);
|
|
if (!s->isSaving() && savegameVersion < VER_V13) {
|
|
// Since roughly v13 of the save games, the objs storage has changed a bit
|
|
for (i = _numObjectsInRoom; i < _numLocalObjects; i++) {
|
|
_objs[i].obj_nr = 0;
|
|
}
|
|
|
|
}
|
|
s->saveLoadArrayOf(_verbs, _numVerbs, sizeof(_verbs[0]), verbEntries);
|
|
s->saveLoadArrayOf(vm.nest, 16, sizeof(vm.nest[0]), nestedScriptEntries);
|
|
s->saveLoadArrayOf(_sentence, 6, sizeof(_sentence[0]), sentenceTabEntries);
|
|
s->saveLoadArrayOf(_string, 6, sizeof(_string[0]), stringTabEntries);
|
|
s->saveLoadArrayOf(_colorCycle, 16, sizeof(_colorCycle[0]), colorCycleEntries);
|
|
|
|
if (savegameVersion >= VER_V13)
|
|
s->saveLoadArrayOf(_scaleSlots, 20, sizeof(_scaleSlots[0]), scaleSlotsEntries);
|
|
|
|
// Save all resource. Fingolfin doesn't like this part of the save/load code a bit.
|
|
// It is very fragile: e.g. if we change the num limit for one resource type, this
|
|
// code will break down. Worse, there is no way such a problem could easily be detected.
|
|
// We should at least store for each save resource it's type and ID. Then at least
|
|
// we can perform some integrety checks when loading.
|
|
for (i = rtFirst; i <= rtLast; i++)
|
|
if (res.mode[i] == 0)
|
|
for (j = 1; j < res.num[i]; j++)
|
|
saveLoadResource(s, i, j);
|
|
|
|
s->saveLoadArrayOf(_objectOwnerTable, _numGlobalObjects, sizeof(_objectOwnerTable[0]), sleByte);
|
|
s->saveLoadArrayOf(_objectStateTable, _numGlobalObjects, sizeof(_objectStateTable[0]), sleByte);
|
|
if (_objectRoomTable)
|
|
s->saveLoadArrayOf(_objectRoomTable, _numGlobalObjects, sizeof(_objectRoomTable[0]), sleByte);
|
|
|
|
if (_shadowPaletteSize)
|
|
s->saveLoadArrayOf(_shadowPalette, _shadowPaletteSize, 1, sleByte);
|
|
|
|
// PalManip data was not saved before V10 save games
|
|
if (savegameVersion < VER_V10)
|
|
_palManipCounter = 0;
|
|
if (_palManipCounter) {
|
|
if (!_palManipPalette)
|
|
_palManipPalette = (byte *)calloc(0x300, 1);
|
|
if (!_palManipIntermediatePal)
|
|
_palManipPalette = (byte *)calloc(0x300, 1);
|
|
s->saveLoadArrayOf(_palManipPalette, 0x300, 1, sleByte);
|
|
s->saveLoadArrayOf(_palManipIntermediatePal, 0x600, 1, sleByte);
|
|
}
|
|
|
|
s->saveLoadArrayOf(_classData, _numGlobalObjects, sizeof(_classData[0]), sleUint32);
|
|
|
|
var120Backup = _scummVars[120];
|
|
var98Backup = _scummVars[98];
|
|
|
|
// The variables grew from 16 to 32 bit.
|
|
if (savegameVersion < VER_V15)
|
|
s->saveLoadArrayOf(_scummVars, _numVariables, sizeof(_scummVars[0]), sleInt16);
|
|
else
|
|
s->saveLoadArrayOf(_scummVars, _numVariables, sizeof(_scummVars[0]), sleInt32);
|
|
|
|
if (_gameId == GID_TENTACLE) // Maybe misplaced, but that's the main idea
|
|
_scummVars[120] = var120Backup;
|
|
if (_gameId == GID_INDY4)
|
|
_scummVars[98] = var98Backup;;
|
|
|
|
s->saveLoadArrayOf(_bitVars, _numBitVariables >> 3, 1, sleByte);
|
|
|
|
/* Save or load a list of the locked objects */
|
|
if (s->isSaving()) {
|
|
for (i = rtFirst; i <= rtLast; i++)
|
|
for (j = 1; j < res.num[i]; j++) {
|
|
if (res.flags[i][j] & RF_LOCK) {
|
|
s->saveByte(i);
|
|
s->saveWord(j);
|
|
}
|
|
}
|
|
s->saveByte(0xFF);
|
|
} else {
|
|
int r;
|
|
while ((r = s->loadByte()) != 0xFF) {
|
|
res.flags[r][s->loadWord()] |= RF_LOCK;
|
|
}
|
|
}
|
|
|
|
if (_imuse && _saveSound) {
|
|
_imuse->save_or_load(s, this);
|
|
_imuse->set_master_volume (_sound->_sound_volume_master);
|
|
_imuse->set_music_volume (_sound->_sound_volume_music);
|
|
}
|
|
}
|
|
|
|
void Scumm::saveLoadResource(Serializer *ser, int type, int idx) {
|
|
byte *ptr;
|
|
uint32 size;
|
|
|
|
/* don't save/load these resource types */
|
|
if (type == rtTemp || type == rtBuffer || res.mode[type])
|
|
return;
|
|
|
|
if (ser->isSaving()) {
|
|
ptr = res.address[type][idx];
|
|
if (ptr == NULL) {
|
|
ser->saveUint32(0);
|
|
return;
|
|
}
|
|
|
|
size = ((MemBlkHeader *)ptr)->size;
|
|
|
|
ser->saveUint32(size);
|
|
ser->saveBytes(ptr + sizeof(MemBlkHeader), size);
|
|
|
|
if (type == rtInventory) {
|
|
ser->saveWord(_inventory[idx]);
|
|
}
|
|
} else {
|
|
size = ser->loadUint32();
|
|
if (size) {
|
|
createResource(type, idx, size);
|
|
ser->loadBytes(getResourceAddress(type, idx), size);
|
|
if (type == rtInventory) {
|
|
_inventory[idx] = ser->loadWord();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Serializer::saveBytes(void *b, int len) {
|
|
_saveLoadStream->fwrite(b, 1, len);
|
|
}
|
|
|
|
void Serializer::loadBytes(void *b, int len) {
|
|
_saveLoadStream->fread(b, 1, len);
|
|
}
|
|
|
|
#ifdef _WIN32_WCE
|
|
|
|
// Perhaps not necessary anymore with latest checks
|
|
|
|
bool Serializer::checkEOFLoadStream() {
|
|
if (!_saveLoadStream->fseek(1, SEEK_CUR))
|
|
return true;
|
|
if (_saveLoadStream->feof())
|
|
return true;
|
|
_saveLoadStream->fseek(-1, SEEK_CUR);
|
|
return false;
|
|
}
|
|
#elif defined(__PALM_OS__)
|
|
bool Serializer::checkEOFLoadStream() {
|
|
|
|
if (_saveLoadStream->feof())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
|
|
void Serializer::saveUint32(uint32 d) {
|
|
uint32 e = FROM_LE_32(d);
|
|
saveBytes(&e, 4);
|
|
}
|
|
|
|
void Serializer::saveWord(uint16 d) {
|
|
uint16 e = FROM_LE_16(d);
|
|
saveBytes(&e, 2);
|
|
}
|
|
|
|
void Serializer::saveByte(byte b) {
|
|
saveBytes(&b, 1);
|
|
}
|
|
|
|
uint32 Serializer::loadUint32() {
|
|
uint32 e;
|
|
loadBytes(&e, 4);
|
|
return FROM_LE_32(e);
|
|
}
|
|
|
|
uint16 Serializer::loadWord() {
|
|
uint16 e;
|
|
loadBytes(&e, 2);
|
|
return FROM_LE_16(e);
|
|
}
|
|
|
|
byte Serializer::loadByte() {
|
|
byte e;
|
|
loadBytes(&e, 1);
|
|
return e;
|
|
}
|
|
|
|
void Serializer::saveArrayOf(void *b, int len, int datasize, byte filetype) {
|
|
byte *at = (byte *)b;
|
|
uint32 data;
|
|
|
|
// speed up byte arrays
|
|
if (datasize == 1 && filetype == sleByte) {
|
|
saveBytes(b, len);
|
|
return;
|
|
}
|
|
|
|
while (--len >= 0) {
|
|
if (datasize == 0) {
|
|
// Do nothing for obsolete data
|
|
data = 0;
|
|
} else if (datasize == 1) {
|
|
data = *(byte *)at;
|
|
at += 1;
|
|
} else if (datasize == 2) {
|
|
data = *(uint16 *)at;
|
|
at += 2;
|
|
} else if (datasize == 4) {
|
|
data = *(uint32 *)at;
|
|
at += 4;
|
|
} else {
|
|
error("saveLoadArrayOf: invalid size %d", datasize);
|
|
}
|
|
switch (filetype) {
|
|
case sleByte:
|
|
saveByte((byte)data);
|
|
break;
|
|
case sleUint16:
|
|
case sleInt16:
|
|
saveWord((int16)data);
|
|
break;
|
|
case sleInt32:
|
|
case sleUint32:
|
|
saveUint32(data);
|
|
break;
|
|
default:
|
|
error("saveLoadArrayOf: invalid filetype %d", filetype);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Serializer::loadArrayOf(void *b, int len, int datasize, byte filetype) {
|
|
byte *at = (byte *)b;
|
|
uint32 data;
|
|
|
|
// speed up byte arrays
|
|
if (datasize == 1 && filetype == sleByte) {
|
|
loadBytes(b, len);
|
|
return;
|
|
}
|
|
|
|
while (--len >= 0) {
|
|
switch (filetype) {
|
|
case sleByte:
|
|
data = loadByte();
|
|
break;
|
|
case sleUint16:
|
|
data = loadWord();
|
|
break;
|
|
case sleInt16:
|
|
data = (int16)loadWord();
|
|
break;
|
|
case sleUint32:
|
|
data = loadUint32();
|
|
break;
|
|
case sleInt32:
|
|
data = (int32)loadUint32();
|
|
break;
|
|
default:
|
|
error("saveLoadArrayOf: invalid filetype %d", filetype);
|
|
}
|
|
if (datasize == 0) {
|
|
// Do nothing for obsolete data
|
|
} else if (datasize == 1) {
|
|
*(byte *)at = (byte)data;
|
|
at += 1;
|
|
} else if (datasize == 2) {
|
|
*(uint16 *)at = (uint16)data;
|
|
at += 2;
|
|
} else if (datasize == 4) {
|
|
*(uint32 *)at = data;
|
|
at += 4;
|
|
} else {
|
|
error("saveLoadArrayOf: invalid size %d", datasize);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Serializer::saveLoadArrayOf(void *b, int num, int datasize, const SaveLoadEntry *sle) {
|
|
byte *data = (byte *)b;
|
|
|
|
if (isSaving()) {
|
|
while (--num >= 0) {
|
|
saveEntries(data, sle);
|
|
data += datasize;
|
|
}
|
|
} else {
|
|
while (--num >= 0) {
|
|
loadEntries(data, sle);
|
|
data += datasize;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Serializer::saveLoadArrayOf(void *b, int len, int datasize, byte filetype) {
|
|
if (isSaving())
|
|
saveArrayOf(b, len, datasize, filetype);
|
|
else
|
|
loadArrayOf(b, len, datasize, filetype);
|
|
}
|
|
|
|
void Serializer::saveLoadEntries(void *d, const SaveLoadEntry *sle) {
|
|
if (isSaving())
|
|
saveEntries(d, sle);
|
|
else
|
|
loadEntries(d, sle);
|
|
}
|
|
|
|
void Serializer::saveEntries(void *d, const SaveLoadEntry *sle) {
|
|
byte type;
|
|
byte *at;
|
|
int size;
|
|
|
|
while (sle->offs != 0xFFFF) {
|
|
at = (byte *)d + sle->offs;
|
|
size = sle->size;
|
|
type = (byte) sle->type;
|
|
|
|
if (sle->maxVersion != CURRENT_VER) {
|
|
// Skip obsolete entries
|
|
if (type & 128)
|
|
sle++;
|
|
} else if (size == 0xFF) {
|
|
// save reference
|
|
void *ptr = *((void **)at);
|
|
saveWord(ptr ? ((*_save_ref) (_ref_me, type, ptr) + 1) : 0);
|
|
} else {
|
|
// save entry
|
|
int columns = 1;
|
|
int rows = 1;
|
|
int rowlen = 0;
|
|
if (type & 128) {
|
|
sle++;
|
|
columns = sle->offs;
|
|
rows = sle->type;
|
|
rowlen = sle->size;
|
|
type &= ~128;
|
|
}
|
|
while (rows--) {
|
|
saveArrayOf(at, columns, size, type);
|
|
at += rowlen;
|
|
}
|
|
}
|
|
sle++;
|
|
}
|
|
}
|
|
|
|
void Serializer::loadEntries(void *d, const SaveLoadEntry *sle) {
|
|
byte type;
|
|
byte *at;
|
|
int size;
|
|
|
|
while (sle->offs != 0xFFFF) {
|
|
at = (byte *)d + sle->offs;
|
|
size = sle->size;
|
|
type = (byte) sle->type;
|
|
|
|
if (_savegameVersion < sle->minVersion || _savegameVersion > sle->maxVersion) {
|
|
// Skip entries which are not present in this save game version
|
|
if (type & 128)
|
|
sle++;
|
|
} else if (size == 0xFF) {
|
|
// load reference...
|
|
int num = loadWord();
|
|
// ...but only use it if it's still there in CURRENT_VER
|
|
if (sle->maxVersion == CURRENT_VER)
|
|
*((void **)at) = num ? (*_load_ref) (_ref_me, type, num - 1) : NULL;
|
|
} else {
|
|
// load entry
|
|
int columns = 1;
|
|
int rows = 1;
|
|
int rowlen = 0;
|
|
|
|
if (type & 128) {
|
|
sle++;
|
|
columns = sle->offs;
|
|
rows = sle->type;
|
|
rowlen = sle->size;
|
|
type &= ~128;
|
|
}
|
|
while (rows--) {
|
|
loadArrayOf(at, columns, size, type);
|
|
at += rowlen;
|
|
}
|
|
}
|
|
sle++;
|
|
}
|
|
}
|