mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-23 02:11:38 +00:00
3ce737eb2a
svn-id: r28748
360 lines
9.8 KiB
C++
360 lines
9.8 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.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
#include "common/stdafx.h"
|
|
|
|
#include "common/config-manager.h"
|
|
#include "common/savefile.h"
|
|
#include "common/system.h"
|
|
#include "common/file.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/sagaresnames.h"
|
|
#include "saga/scene.h"
|
|
#include "saga/script.h"
|
|
|
|
#define CURRENT_SAGA_VER 5
|
|
|
|
namespace Saga {
|
|
|
|
static SaveFileData emptySlot = {
|
|
"", 0
|
|
};
|
|
|
|
//TODO:
|
|
// - delete savegame
|
|
|
|
char* SagaEngine::calcSaveFileName(uint slotNumber) {
|
|
static char name[MAX_FILE_NAME];
|
|
sprintf(name, "%s.s%02d", _targetName.c_str(), slotNumber);
|
|
return name;
|
|
}
|
|
|
|
SaveFileData *SagaEngine::getSaveFile(uint idx) {
|
|
if (idx >= _saveFilesMaxCount) {
|
|
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() {
|
|
uint i, j;
|
|
bool found;
|
|
if (isSaveListFull()) {
|
|
error("getNewSaveSlotNumber save list is full");
|
|
}
|
|
for (i = 0; i < MAX_SAVES; i++) {
|
|
if (_saveMarks[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");
|
|
}
|
|
|
|
void SagaEngine::fillSaveList() {
|
|
int i;
|
|
Common::InSaveFile *in;
|
|
char *name;
|
|
|
|
name = calcSaveFileName(MAX_SAVES);
|
|
name[strlen(name) - 2] = 0;
|
|
_saveFileMan->listSavefiles(name, _saveMarks, MAX_SAVES);
|
|
|
|
_saveFilesMaxCount = 0;
|
|
for (i = 0; i < MAX_SAVES; i++) {
|
|
if (_saveMarks[i]) {
|
|
_saveFilesMaxCount++;
|
|
}
|
|
_saveFiles[i].name[0] = 0;
|
|
_saveFiles[i].slotNumber = (uint)-1;
|
|
}
|
|
|
|
_saveFilesCount = 0;
|
|
|
|
i = 0;
|
|
while (i < MAX_SAVES) {
|
|
if (_saveMarks[i]) {
|
|
name = calcSaveFileName(i);
|
|
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 != MKID_BE('SAGA')) {
|
|
warning("SagaEngine::load wrong save %s format", name);
|
|
i++;
|
|
continue;
|
|
}
|
|
strcpy(_saveFiles[_saveFilesCount].name, _saveHeader.name);
|
|
_saveFiles[_saveFilesCount].slotNumber = i;
|
|
delete in;
|
|
_saveFilesCount++;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
|
|
#define TITLESIZE 80
|
|
void SagaEngine::save(const char *fileName, const char *saveName) {
|
|
Common::OutSaveFile *out;
|
|
char title[TITLESIZE];
|
|
|
|
if (!(out = _saveFileMan->openForSaving(fileName))) {
|
|
return;
|
|
}
|
|
|
|
_saveHeader.type = MKID_BE('SAGA');
|
|
_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
|
|
strncpy(_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);
|
|
|
|
// Surrounding scene
|
|
out->writeSint32LE(_scene->getOutsetSceneNumber());
|
|
if (getGameType() != GType_ITE) {
|
|
out->writeSint32LE(_scene->currentChapterNumber());
|
|
// Protagonist
|
|
out->writeSint32LE(_scene->currentProtag());
|
|
out->writeSint32LE(_scene->getCurrentMusicTrack());
|
|
out->writeSint32LE(_scene->getCurrentMusicRepeat());
|
|
}
|
|
|
|
// Inset scene
|
|
out->writeSint32LE(_scene->currentSceneNumber());
|
|
|
|
if (getGameType() != GType_ITE) {
|
|
out->writeUint32LE(_globalFlags);
|
|
for (int i = 0; i < ARRAYSIZE(_ethicsPoints); i++)
|
|
out->writeSint16LE(_ethicsPoints[i]);
|
|
}
|
|
|
|
_interface->saveState(out);
|
|
|
|
_actor->saveState(out);
|
|
|
|
out->writeSint16LE(_script->_commonBufferSize);
|
|
|
|
out->write(_script->_commonBuffer, _script->_commonBufferSize);
|
|
|
|
out->writeSint16LE(_isoMap->getMapPosition().x);
|
|
out->writeSint16LE(_isoMap->getMapPosition().y);
|
|
|
|
out->finalize();
|
|
|
|
// TODO: Check out->ioFailed()
|
|
|
|
delete out;
|
|
}
|
|
|
|
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: %x", _saveHeader.version);
|
|
|
|
if (_saveHeader.version < 4)
|
|
warning("This savegame is not endian-safe. There may be problems");
|
|
|
|
if (_saveHeader.type != MKID_BE('SAGA')) {
|
|
error("SagaEngine::load wrong format");
|
|
}
|
|
|
|
if (_saveHeader.version > 4) {
|
|
in->read(title, TITLESIZE);
|
|
debug(0, "Save is for: %s", title);
|
|
}
|
|
|
|
// Surrounding scene
|
|
sceneNumber = in->readSint32LE();
|
|
if (getGameType() != GType_ITE) {
|
|
int currentChapter = _scene->currentChapterNumber();
|
|
_scene->setChapterNumber(in->readSint32LE());
|
|
_scene->setProtag(in->readSint32LE());
|
|
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 (getGameId() != GID_IHNM_DEMO) {
|
|
// HACK for chapter 6 (last chapter) in IHNM. For some reason, the songtable loaded is
|
|
// incorrect, and the game crashes here when trying to load a music track there. For now,
|
|
// just don't change the music track for chapter 6
|
|
// FIXME: Figure out what's wrong with the loaded music track and remove this hack
|
|
// Note that when this hack is removed, remove it from Script::sfPlayMusic and
|
|
// Script::sfQueueMusic as well
|
|
if (getGameType() == GType_IHNM && _scene->currentChapterNumber() == 6) {
|
|
// do nothing
|
|
} else {
|
|
_music->play(_music->_songTable[_scene->getCurrentMusicTrack()], _scene->getCurrentMusicRepeat() ? MUSIC_LOOP : MUSIC_NORMAL);
|
|
}
|
|
//_music->play(_music->_songTable[_scene->getCurrentMusicTrack()], _scene->getCurrentMusicRepeat() ? MUSIC_LOOP : MUSIC_NORMAL);
|
|
} else {
|
|
_music->play(3, MUSIC_LOOP);
|
|
}
|
|
}
|
|
|
|
// Inset scene
|
|
insetSceneNumber = in->readSint32LE();
|
|
|
|
if (getGameType() != GType_ITE) {
|
|
_globalFlags = in->readUint32LE();
|
|
for (int i = 0; i < ARRAYSIZE(_ethicsPoints); i++)
|
|
_ethicsPoints[i] = in->readSint16LE();
|
|
}
|
|
|
|
_interface->loadState(in);
|
|
|
|
_actor->loadState(in);
|
|
|
|
commonBufferSize = in->readSint16LE();
|
|
in->read(_script->_commonBuffer, commonBufferSize);
|
|
|
|
mapx = in->readSint16LE();
|
|
mapy = in->readSint16LE();
|
|
|
|
delete in;
|
|
|
|
// Mute volume to prevent outScene music play
|
|
int volume = _music->getVolume();
|
|
_music->setVolume(0);
|
|
|
|
_isoMap->setMapPosition(mapx, mapy);
|
|
|
|
// Protagonist swapping
|
|
if (getGameType() != GType_ITE) {
|
|
if (_scene->currentProtag() != 0 && _scene->currentChapterNumber() != 6) {
|
|
ActorData *actor1 = _actor->getFirstActor();
|
|
ActorData *actor2;
|
|
// The original gets actor2 from the current protagonist ID, but this is sometimes wrong
|
|
// If the current protagonist ID is not correct, use the stored protagonist
|
|
if (!_actor->validActorId(_scene->currentProtag())) {
|
|
actor2 = _actor->_protagonist;
|
|
} else {
|
|
actor2 = _actor->getActor(_scene->currentProtag());
|
|
}
|
|
|
|
SWAP(actor1->_location, actor2->_location);
|
|
|
|
actor2->_flags &= ~kProtagonist;
|
|
actor1->_flags |= kProtagonist;
|
|
_actor->_protagonist = _actor->_centerActor = actor1;
|
|
_scene->setProtag(actor1->_id);
|
|
}
|
|
}
|
|
|
|
_scene->clearSceneQueue();
|
|
_scene->changeScene(sceneNumber, ACTOR_NO_ENTRANCE, kTransitionNoFade);
|
|
|
|
_events->handleEvents(0); //dissolve backgrounds
|
|
|
|
if (insetSceneNumber != sceneNumber) {
|
|
_render->setFlag(RF_DISABLE_ACTORS);
|
|
_render->drawScene();
|
|
_render->clearFlag(RF_DISABLE_ACTORS);
|
|
_scene->changeScene(insetSceneNumber, ACTOR_NO_ENTRANCE, kTransitionNoFade);
|
|
}
|
|
|
|
_music->setVolume(volume);
|
|
|
|
_interface->draw();
|
|
}
|
|
|
|
} // End of namespace Saga
|