scummvm/engines/saga/saveload.cpp
Filippos Karapetis 2a6a79b167 SAGA: Remove the buggy actor swapping fixup code for IHNM
This was a hack that was implemented while IHNM was being developed.
That code should no longer be needed. If this issue does occur again,
the actual cause should be investigated, instead of hiding it with
workarounds. The code was buggy anyway, as _currentProtag was not
initialized properly
2014-10-27 12:28:18 +02:00

387 lines
10 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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "common/savefile.h"
#include "common/system.h"
#include "graphics/thumbnail.h"
#include "saga/saga.h"
#include "saga/actor.h"
#include "saga/events.h"
#include "saga/interface.h"
#include "saga/isomap.h"
#include "saga/music.h"
#include "saga/render.h"
#include "saga/scene.h"
#include "saga/script.h"
#define CURRENT_SAGA_VER 8
namespace Saga {
static SaveFileData emptySlot = {
"", 0
};
char* SagaEngine::calcSaveFileName(uint slotNumber) {
static char name[MAX_FILE_NAME];
sprintf(name, "%s.s%02u", _targetName.c_str(), slotNumber);
return name;
}
SaveFileData *SagaEngine::getSaveFile(uint idx) {
if (idx >= MAX_SAVES) {
error("getSaveFileName wrong idx");
}
if (isSaveListFull()) {
return &_saveFiles[_saveFilesCount - idx - 1];
} else {
if (!emptySlot.name[0])
strcpy(emptySlot.name, getTextString(kTextNewSave));
return (idx == 0) ? &emptySlot : &_saveFiles[_saveFilesCount - idx];
}
}
bool SagaEngine::locateSaveFile(char *saveName, uint &titleNumber) {
uint i;
for (i = 0; i < _saveFilesCount; i++) {
if (strcmp(saveName, _saveFiles[i].name) == 0) {
if (isSaveListFull()) {
titleNumber = _saveFilesCount - i - 1;
} else {
titleNumber = _saveFilesCount - i;
}
return true;
}
}
return false;
}
uint SagaEngine::getNewSaveSlotNumber() const {
uint i, j;
bool found;
for (i = 0; i < MAX_SAVES; i++) {
found = false;
for (j = 0; j < _saveFilesCount; j++) {
if (_saveFiles[j].slotNumber == i) {
found = true;
break;
}
}
if (!found) {
return i;
}
}
error("getNewSaveSlotNumber save list is full");
}
static int compareSaveFileData(const void *a, const void *b) {
const SaveFileData *s1 = (const SaveFileData *)a;
const SaveFileData *s2 = (const SaveFileData *)b;
if (s1->slotNumber < s2->slotNumber) {
return -1;
} else if (s1->slotNumber > s2->slotNumber) {
return 1;
} else {
return 0;
}
}
void SagaEngine::fillSaveList() {
int i;
Common::InSaveFile *in;
Common::StringArray filenames;
char slot[3];
int slotNumber;
char *name;
name = calcSaveFileName(MAX_SAVES);
name[strlen(name) - 2] = '*';
name[strlen(name) - 1] = 0;
filenames = _saveFileMan->listSavefiles(name);
for (i = 0; i < MAX_SAVES; i++) {
_saveFiles[i].name[0] = 0;
_saveFiles[i].slotNumber = (uint)-1;
}
_saveFilesCount = 0;
for (Common::StringArray::iterator file = filenames.begin(); file != filenames.end(); ++file){
//Obtain the last 2 digits of the filename, since they correspond to the save slot
slot[0] = file->c_str()[file->size()-2];
slot[1] = file->c_str()[file->size()-1];
slot[2] = 0;
slotNumber = atoi(slot);
if (slotNumber >= 0 && slotNumber < MAX_SAVES) {
name = calcSaveFileName(slotNumber);
if ((in = _saveFileMan->openForLoading(name)) != NULL) {
_saveHeader.type = in->readUint32BE();
_saveHeader.size = in->readUint32LE();
_saveHeader.version = in->readUint32LE();
in->read(_saveHeader.name, sizeof(_saveHeader.name));
if (_saveHeader.type != MKTAG('S','A','G','A')) {
warning("SagaEngine::load wrong save %s format", name);
i++;
continue;
}
strcpy(_saveFiles[_saveFilesCount].name, _saveHeader.name);
_saveFiles[_saveFilesCount].slotNumber = slotNumber;
delete in;
_saveFilesCount++;
}
}
}
qsort(_saveFiles, _saveFilesCount, sizeof(_saveFiles[0]), compareSaveFileData);
}
void SagaEngine::save(const char *fileName, const char *saveName) {
Common::OutSaveFile *out;
char title[TITLESIZE];
if (!(out = _saveFileMan->openForSaving(fileName))) {
return;
}
_saveHeader.type = MKTAG('S','A','G','A');
_saveHeader.size = 0;
_saveHeader.version = CURRENT_SAGA_VER;
// Note that IHNM has a smaller save title size than ITE
// We allocate the ITE save title size here, to preserve
// savegame backwards compatibility
Common::strlcpy(_saveHeader.name, saveName, SAVE_TITLE_SIZE);
out->writeUint32BE(_saveHeader.type);
out->writeUint32LE(_saveHeader.size);
out->writeUint32LE(_saveHeader.version);
out->write(_saveHeader.name, sizeof(_saveHeader.name));
// Original game title
memset(title, 0, TITLESIZE);
strncpy(title, _gameTitle.c_str(), TITLESIZE);
out->write(title, TITLESIZE);
// Thumbnail
// First draw scene without save dialog
int oldMode = _interface->getMode();
_interface->setMode(kPanelMain);
_render->drawScene();
Graphics::saveThumbnail(*out);
_interface->setMode(oldMode);
// Date / time
TimeDate curTime;
_system->getTimeAndDate(curTime);
uint32 saveDate = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
uint16 saveTime = ((curTime.tm_hour & 0xFF) << 8) | ((curTime.tm_min) & 0xFF);
uint32 playTime = g_engine->getTotalPlayTime() / 1000;
out->writeUint32BE(saveDate);
out->writeUint16BE(saveTime);
out->writeUint32BE(playTime);
// Surrounding scene
out->writeSint32LE(_scene->getOutsetSceneNumber());
#ifdef ENABLE_IHNM
if (getGameId() == GID_IHNM) {
out->writeSint32LE(_scene->currentChapterNumber());
out->writeSint32LE(0); // obsolete, was used for the protagonist
out->writeSint32LE(_scene->getCurrentMusicTrack());
out->writeSint32LE(_scene->getCurrentMusicRepeat());
}
#endif
// Inset scene
out->writeSint32LE(_scene->currentSceneNumber());
#ifdef ENABLE_IHNM
if (getGameId() == GID_IHNM) {
out->writeUint32LE(_globalFlags);
for (int i = 0; i < ARRAYSIZE(_ethicsPoints); i++)
out->writeSint16LE(_ethicsPoints[i]);
}
#endif
_interface->saveState(out);
_actor->saveState(out);
out->writeSint16LE(_script->_commonBuffer.size());
out->write(_script->_commonBuffer.getBuffer(), _script->_commonBuffer.size());
// ISO map x, y coordinates for ITE
if (getGameId() == GID_ITE) {
out->writeSint16LE(_isoMap->getMapPosition().x);
out->writeSint16LE(_isoMap->getMapPosition().y);
}
out->finalize();
if (out->err())
warning("Can't write file '%s'. (Disk full?)", fileName);
delete out;
_interface->resetSaveReminder();
}
void SagaEngine::load(const char *fileName) {
Common::InSaveFile *in;
int commonBufferSize;
int sceneNumber, insetSceneNumber;
int mapx, mapy;
char title[TITLESIZE];
if (!(in = _saveFileMan->openForLoading(fileName))) {
return;
}
_saveHeader.type = in->readUint32BE();
_saveHeader.size = in->readUint32LE();
_saveHeader.version = in->readUint32LE();
in->read(_saveHeader.name, sizeof(_saveHeader.name));
// Some older saves were not written in an endian safe fashion.
// We try to detect this here by checking for extremly high version values.
// If found, we retry with the data swapped.
if (_saveHeader.version > 0xFFFFFF) {
warning("This savegame is not endian safe, retrying with the data swapped");
_saveHeader.version = SWAP_BYTES_32(_saveHeader.version);
}
debug(2, "Save version: 0x%X", _saveHeader.version);
if (_saveHeader.version < 4)
warning("This savegame is not endian-safe. There may be problems");
if (_saveHeader.type != MKTAG('S','A','G','A')) {
error("SagaEngine::load wrong save game format");
}
if (_saveHeader.version > 4) {
in->read(title, TITLESIZE);
debug(0, "Save is for: %s", title);
}
if (_saveHeader.version >= 6) {
// We don't need the thumbnail here, so just read it and discard it
Graphics::skipThumbnail(*in);
in->readUint32BE(); // save date
in->readUint16BE(); // save time
if (_saveHeader.version >= 8) {
uint32 playTime = in->readUint32BE();
g_engine->setTotalPlayTime(playTime * 1000);
}
}
// Clear pending events here, and don't process queued music events
_events->clearList(false);
// Surrounding scene
sceneNumber = in->readSint32LE();
#ifdef ENABLE_IHNM
if (getGameId() == GID_IHNM) {
int currentChapter = _scene->currentChapterNumber();
_scene->setChapterNumber(in->readSint32LE());
in->skip(4); // obsolete, was used for setting the protagonist
if (_scene->currentChapterNumber() != currentChapter)
_scene->changeScene(-2, 0, kTransitionFade, _scene->currentChapterNumber());
_scene->setCurrentMusicTrack(in->readSint32LE());
_scene->setCurrentMusicRepeat(in->readSint32LE());
_music->stop();
if (_scene->currentChapterNumber() == 8)
_interface->setMode(kPanelChapterSelection);
if (!isIHNMDemo()) {
_music->play(_music->_songTable[_scene->getCurrentMusicTrack()], _scene->getCurrentMusicRepeat() ? MUSIC_LOOP : MUSIC_NORMAL);
} else {
_music->play(3, MUSIC_LOOP);
}
}
#endif
// Inset scene
insetSceneNumber = in->readSint32LE();
#ifdef ENABLE_IHNM
if (getGameId() == GID_IHNM) {
_globalFlags = in->readUint32LE();
for (int i = 0; i < ARRAYSIZE(_ethicsPoints); i++)
_ethicsPoints[i] = in->readSint16LE();
}
#endif
_interface->loadState(in);
_actor->loadState(in);
commonBufferSize = in->readSint16LE();
_script->_commonBuffer.resize(commonBufferSize);
in->read(_script->_commonBuffer.getBuffer(), commonBufferSize);
if (getGameId() == GID_ITE) {
mapx = in->readSint16LE();
mapy = in->readSint16LE();
_isoMap->setMapPosition(mapx, mapy);
}
// Note: the mapx, mapy ISO map positions were incorrectly saved
// for IHNM too, which has no ISO map scenes, up to save version 6.
// Since they're at the end of the savegame, we just ignore them
delete in;
// Mute volume to prevent outScene music play
int volume = _music->getVolume();
_music->setVolume(0);
_scene->clearSceneQueue();
_scene->changeScene(sceneNumber, ACTOR_NO_ENTRANCE, kTransitionNoFade);
_events->handleEvents(0); //dissolve backgrounds
if (insetSceneNumber != sceneNumber) {
_render->setFlag(RF_DISABLE_ACTORS);
_scene->draw();
_render->drawScene();
_render->clearFlag(RF_DISABLE_ACTORS);
_scene->changeScene(insetSceneNumber, ACTOR_NO_ENTRANCE, kTransitionNoFade);
}
_music->setVolume(volume);
_interface->draw();
}
} // End of namespace Saga