scummvm/engines/bagel/baglib/save_game_file.cpp
Le Philousophe 5201b0ae39 BAGEL: Don't allocate the save state on the stack
It's big and can be allocated on heap.
2025-01-24 22:57:30 +02:00

314 lines
7.4 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/algorithm.h"
#include "common/memstream.h"
#include "common/serializer.h"
#include "bagel/baglib/save_game_file.h"
#include "bagel/boflib/misc.h"
#include "bagel/bagel.h"
namespace Bagel {
#define WORLD_DIR "$SBARDIR\\WLD\\%s"
void StSavegameHeader::synchronize(Common::Serializer &s) {
s.syncBytes((byte *)_szTitle, MAX_SAVE_TITLE);
s.syncBytes((byte *)_szUserName, MAX_USER_NAME);
s.syncAsUint32LE(_bUsed);
}
void StVar::synchronize(Common::Serializer &s) {
s.syncBytes((byte *)_szName, MAX_VAR_NAME);
s.syncBytes((byte *)_szValue, MAX_VAR_VALUE);
s.syncAsUint16LE(_nType);
s.syncAsByte(_bGlobal);
s.syncAsByte(_bConstant);
s.syncAsByte(_bReference);
s.syncAsByte(_bTimer);
s.syncAsByte(_bRandom);
s.syncAsByte(_bNumeric);
s.syncAsByte(_bAttached);
s.syncAsByte(_bUsed);
}
void StVar::clear() {
Common::fill(_szName, _szName + MAX_VAR_NAME, '\0');
Common::fill(_szValue, _szValue + MAX_VAR_VALUE, '\0');
_nType = 0;
_bGlobal = 0;
_bConstant = 0;
_bReference = 0;
_bTimer = 0;
_bRandom = 0;
_bNumeric = 0;
_bAttached = 0;
_bUsed = 0;
}
void StObj::synchronize(Common::Serializer &s) {
s.syncBytes((byte *)_szName, MAX_OBJ_NAME);
s.syncBytes((byte *)_szSDev, MAX_SDEV_NAME);
s.syncAsUint32LE(_lState);
s.syncAsUint32LE(_lProperties);
s.syncAsUint32LE(_lType);
s.syncAsUint32LE(_lLoop);
s.syncAsUint32LE(_lSpeed);
s.syncAsByte(_bAttached);
s.syncAsByte(_bUsed);
s.syncAsUint16LE(_nFlags);
}
void StObj::clear() {
Common::fill(_szName, _szName + MAX_OBJ_NAME, '\0');
Common::fill(_szSDev, _szSDev + MAX_SDEV_NAME, '\0');
_lState = 0;
_lProperties = 0;
_lType = 0;
_lLoop = 0;
_lSpeed = 0;
_bAttached = 0;
_bUsed = 0;
_nFlags = 0;
}
void StBagelSave::synchronize(Common::Serializer &s) {
for (int i = 0; i < MAX_VARS; ++i)
_stVarList[i].synchronize(s);
for (int i = 0; i < MAX_OBJS; ++i)
_stObjList[i].synchronize(s);
for (int i = 0; i < MAX_OBJS; ++i)
_stObjListEx[i].synchronize(s);
s.syncBytes((byte *)_szScript, MAX_FNAME);
s.syncAsUint32LE(_nLocType);
for (int i = 0; i < MAX_CLOSEUP_DEPTH; ++i)
s.syncBytes((byte *)_szLocStack[i], MAX_SDEV_NAME);
s.syncAsUint16LE(_nLocX);
s.syncAsUint16LE(_nLocY);
s.syncAsUint16LE(_bUseEx);
s.syncAsUint16LE(_nFiller);
}
void StBagelSave::clear() {
for (int i = 0; i < MAX_VARS; ++i)
_stVarList[i].clear();
for (int i = 0; i < MAX_OBJS; ++i) {
_stObjList[i].clear();
_stObjListEx[i].clear();
}
Common::fill(_szScript, _szScript + MAX_FNAME, '\0');
_nLocType = 0;
for (int i = 0; i < MAX_CLOSEUP_DEPTH; ++i)
Common::fill(_szLocStack[i], _szLocStack[i] + MAX_SDEV_NAME, '\0');
_nLocX = 0;
_nLocY = 0;
_bUseEx = 0;
_nFiller = 0;
}
CBagSaveGameFile::CBagSaveGameFile(bool isSaving) {
setFile("spacebar.sav",
isSaving ?
(CDF_MEMORY | CDF_ENCRYPT | CDF_KEEPOPEN | CDF_CREATE | CDF_SAVEFILE) :
(CDF_MEMORY | CDF_ENCRYPT | CDF_KEEPOPEN | CDF_SAVEFILE)
);
}
ErrorCode CBagSaveGameFile::writeSavedGame() {
assert(isValidObject(this));
// Populate the save data
StBagelSave *saveData = new StBagelSave();
g_engine->_masterWin->fillSaveBuffer(saveData);
Common::String str = "./" + Common::String(saveData->_szScript);
str.replace('/', '\\');
Common::strcpy_s(saveData->_szScript, str.c_str());
// Set up header fields
StSavegameHeader header;
Common::strcpy_s(header._szTitle, "ScummVM Save");
Common::strcpy_s(header._szUserName, "ScummVM User");
header._bUsed = 1;
// Create the data buffer
Common::MemoryWriteStreamDynamic stream(DisposeAfterUse::YES);
Common::Serializer s(nullptr, &stream);
header.synchronize(s);
stream.writeUint32LE(StBagelSave::size());
saveData->synchronize(s);
// Add the record
addRecord(stream.getData(), stream.size(), true, 0);
delete saveData;
return _errCode;
}
ErrorCode CBagSaveGameFile::readSavedGame(int32 slotNum) {
assert(isValidObject(this));
int32 lRecNum = findRecord(slotNum);
if (lRecNum != -1) {
int32 lSize = getRecSize(lRecNum);
if (lSize == StSavegameHeader::size()) {
_errCode = ERR_FREAD;
} else {
byte *pBuf = (byte *)bofAlloc(lSize);
readRecord(lRecNum, pBuf);
// Load in the savegame
Common::MemoryReadStream stream(pBuf, lSize);
Common::Serializer s(&stream, nullptr);
StSavegameHeader header;
header.synchronize(s);
s.skip(4); // Skip save data structure size
StBagelSave *saveData = new StBagelSave();
saveData->synchronize(s);
bofFree(pBuf);
CBofString str(saveData->_szScript);
fixPathName(str);
const char *path = str.getBuffer();
assert(!strncmp(path, "./", 2));
Common::strcpy_s(saveData->_szScript, path + 2);
// Restore the game
g_engine->_masterWin->doRestore(saveData);
delete saveData;
}
} else {
_errCode = ERR_FREAD;
}
return _errCode;
}
ErrorCode CBagSaveGameFile::readTitle(int32 lSlot, StSavegameHeader *pSavedGame) {
assert(isValidObject(this));
// validate input
assert(lSlot >= 0 && lSlot < MAX_SAVED_GAMES);
assert(pSavedGame != nullptr);
int32 lRecNum = findRecord(lSlot);
if (lRecNum != -1) {
int32 lSize = getRecSize(lRecNum);
byte *pBuf = (byte *)bofAlloc(lSize);
readRecord(lRecNum, pBuf);
// Fill StSavegameHeader structure with this game's saved info
memcpy(pSavedGame, pBuf, sizeof(StSavegameHeader));
bofFree(pBuf);
} else {
reportError(ERR_UNKNOWN, "Unable to find saved game #%ld in %s", lSlot, _szFileName);
}
return(_errCode);
}
ErrorCode CBagSaveGameFile::readTitleOnly(int32 lSlot, char *pGameTitle) {
assert(isValidObject(this));
// Validate input
assert(lSlot >= 0 && lSlot < MAX_SAVED_GAMES);
assert(pGameTitle != nullptr);
byte pBuf[MAX_SAVE_TITLE + 1];
int32 lRecNum = findRecord(lSlot);
if (lRecNum != -1) {
int32 lSize = MAX_SAVE_TITLE;
readFromFile(lRecNum, pBuf, lSize);
// Fill with current game title
memcpy(pGameTitle, pBuf, lSize);
} else {
reportError(ERR_UNKNOWN, "Unable to find saved game #%ld in %s", lSlot, _szFileName);
}
return _errCode;
}
int32 CBagSaveGameFile::getActualNumSaves() {
assert(isValidObject(this));
int32 lNumSaves = 0;
int32 lNumRecs = getNumSavedGames();
for (int32 i = 0; i < lNumRecs; i++) {
StSavegameHeader stGameInfo;
if (readTitle(i, &stGameInfo) == ERR_NONE) {
if (stGameInfo._bUsed) {
lNumSaves++;
}
} else {
break;
}
}
return(lNumSaves);
}
bool CBagSaveGameFile::anySavedGames() {
assert(isValidObject(this));
int32 lNumRecs = getNumSavedGames();
for (int32 i = 0; i < lNumRecs; i++) {
StSavegameHeader stGameInfo;
if (readTitle(i, &stGameInfo) == ERR_NONE) {
if (stGameInfo._bUsed) {
return true;
}
} else {
break;
}
}
return false;
}
} // namespace Bagel