mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
The previous assumption that all files were identical is wrong. Use full paths to make it work properly.
337 lines
9.7 KiB
C++
337 lines
9.7 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/archive.h"
|
|
#include "common/config-manager.h"
|
|
#include "common/file.h"
|
|
#include "common/savefile.h"
|
|
#include "common/system.h"
|
|
|
|
#include "cryomni3d/versailles/engine.h"
|
|
|
|
namespace CryOmni3D {
|
|
namespace Versailles {
|
|
|
|
Common::Error CryOmni3DEngine_Versailles::loadGameState(int slot) {
|
|
_loadedSave = slot + 1;
|
|
_abortCommand = kAbortLoadGame;
|
|
return Common::kNoError;
|
|
}
|
|
|
|
Common::Error CryOmni3DEngine_Versailles::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
|
|
saveGame(_isVisiting, slot + 1, desc);
|
|
return Common::kNoError;
|
|
}
|
|
|
|
Common::String CryOmni3DEngine_Versailles::getSaveFileName(bool visit, uint saveNum) const {
|
|
return Common::String::format("%s%s.%04u", _targetName.c_str(), visit ? "_visit" : "", saveNum);
|
|
}
|
|
|
|
Common::String CryOmni3DEngine_Versailles::getSaveStateName(int slot) const {
|
|
return Common::String::format("%s.%04u", _targetName.c_str(), slot);
|
|
}
|
|
|
|
bool CryOmni3DEngine_Versailles::canVisit() const {
|
|
return Common::File::exists(getFilePath(kFileTypeSaveGameVisit, "game0001.sav"));
|
|
}
|
|
|
|
void CryOmni3DEngine_Versailles::getSavesList(bool visit, Common::StringArray &saveNames,
|
|
int &nextSaveNum) {
|
|
nextSaveNum = 1;
|
|
bool supportsAutoName = (_messages.size() >= 148);
|
|
|
|
char saveName[kSaveDescriptionLen + 1];
|
|
// Terminate saveName here forever (we don't overrun kSaveDescriptionLen)
|
|
saveName[kSaveDescriptionLen] = '\0';
|
|
Common::String pattern = Common::String::format("%s%s.####", _targetName.c_str(),
|
|
visit ? "_visit" : "");
|
|
Common::StringArray filenames = _saveFileMan->listSavefiles(pattern);
|
|
sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
|
|
|
|
saveNames.clear();
|
|
saveNames.reserve(100);
|
|
|
|
int num = 1;
|
|
int slotNum;
|
|
|
|
if (visit) {
|
|
// Add bootstrap visit
|
|
Common::Path visitPath(getFilePath(kFileTypeSaveGameVisit, "game0001.sav"));
|
|
Common::File visitFile;
|
|
if (visitFile.open(visitPath)) {
|
|
visitFile.read(saveName, kSaveDescriptionLen);
|
|
saveNames.push_back(saveName);
|
|
} else {
|
|
warning("visiting mode but no bootstrap");
|
|
// No bootstrap visit, too bad
|
|
saveNames.push_back(_messages[55]); //Fill with free slot
|
|
}
|
|
num++;
|
|
}
|
|
|
|
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end();
|
|
++file) {
|
|
// Obtain the last 4 digits of the filename, since they correspond to the save slot
|
|
slotNum = atoi(file->c_str() + file->size() - 4);
|
|
|
|
if (slotNum >= 1 && slotNum <= 99) {
|
|
while (num < slotNum) {
|
|
saveNames.push_back(_messages[55]); //Fill with free slot
|
|
num++;
|
|
}
|
|
|
|
num++;
|
|
Common::InSaveFile *in = _saveFileMan->openForLoading(*file);
|
|
if (in) {
|
|
if (in->read(saveName, kSaveDescriptionLen) != kSaveDescriptionLen) {
|
|
warning("getSavesList(): Corrupted save %s", saveName);
|
|
delete in;
|
|
|
|
continue;
|
|
}
|
|
|
|
Common::String saveNameStr = saveName;
|
|
if (supportsAutoName && saveNameStr.hasPrefix("AUTO")) {
|
|
int saveNum = atoi(saveName + 4);
|
|
if (saveNum >= 1 && saveNum <= 9999) {
|
|
in->seek(436); // Go to current level
|
|
uint32 level = in->readUint32BE();
|
|
|
|
if (level < 8) {
|
|
saveNameStr = Common::String::format(_messages[146].c_str(), level);
|
|
} else {
|
|
saveNameStr = _messages[147];
|
|
}
|
|
saveNameStr += Common::String::format(" - %d", saveNum);
|
|
if (saveNum >= nextSaveNum) {
|
|
if (saveNum >= 9999) {
|
|
nextSaveNum = 9999;
|
|
} else {
|
|
nextSaveNum = saveNum + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
saveNames.push_back(saveNameStr);
|
|
delete in;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (uint i = saveNames.size(); i < 100; i++) {
|
|
saveNames.push_back(_messages[55]);
|
|
}
|
|
}
|
|
|
|
void CryOmni3DEngine_Versailles::saveGame(bool visit, uint saveNum,
|
|
const Common::String &saveName) {
|
|
if (visit && saveNum == 1) {
|
|
error("Can't erase bootstrap visit");
|
|
}
|
|
|
|
Common::String saveFileName = getSaveFileName(visit, saveNum);
|
|
|
|
Common::OutSaveFile *out;
|
|
|
|
if (!(out = _saveFileMan->openForSaving(saveFileName))) {
|
|
return;
|
|
}
|
|
|
|
// Sync countdown to game variable before saving it to file
|
|
syncCountdown();
|
|
|
|
// Write save name
|
|
|
|
// Allocate one more byte to silence GCC warning
|
|
// The save name doesn't have to be null terminated in the save file
|
|
char saveNameC[kSaveDescriptionLen + 1];
|
|
memset(saveNameC, 0, sizeof(saveNameC));
|
|
strncpy(saveNameC, saveName.c_str(), kSaveDescriptionLen);
|
|
out->write(saveNameC, kSaveDescriptionLen);
|
|
|
|
// dummy values
|
|
out->writeUint32LE(0);
|
|
out->writeUint32BE(0);
|
|
out->writeUint32BE(0);
|
|
|
|
// Dialog variables
|
|
assert(_dialogsMan.size() < 200);
|
|
for (uint i = 0; i < _dialogsMan.size(); i++) {
|
|
out->writeByte(_dialogsMan[i]);
|
|
}
|
|
for (uint i = _dialogsMan.size(); i < 200; i++) {
|
|
out->writeByte(0);
|
|
}
|
|
|
|
// Inventory
|
|
assert(_inventory.size() == 50);
|
|
for (Inventory::const_iterator it = _inventory.begin(); it != _inventory.end(); it++) {
|
|
uint objId = uint(-1);
|
|
if (*it != nullptr) {
|
|
// Inventory contains pointers to objects stored in _objects
|
|
objId = *it - _objects.begin();
|
|
}
|
|
out->writeUint32BE(objId);
|
|
}
|
|
// Offset of inventory in toolbar
|
|
out->writeUint32BE(_toolbar.inventoryOffset());
|
|
|
|
// Level, place, warp position
|
|
out->writeUint32BE(_currentLevel);
|
|
out->writeUint32BE(_currentPlaceId);
|
|
out->writeDoubleBE(_omni3dMan.getAlpha());
|
|
out->writeDoubleBE(_omni3dMan.getBeta());
|
|
|
|
// Places states
|
|
assert(_placeStates.size() <= 100);
|
|
Common::Array<PlaceState>::const_iterator placeIt = _placeStates.begin();
|
|
for (uint i = 0; placeIt != _placeStates.end(); placeIt++, i++) {
|
|
out->writeUint32BE(placeIt->state);
|
|
}
|
|
for (uint i = _placeStates.size(); i < 100; i++) {
|
|
out->writeUint32BE(0);
|
|
}
|
|
|
|
// Game variables
|
|
assert(_gameVariables.size() < 100);
|
|
for (Common::Array<uint>::const_iterator it = _gameVariables.begin();
|
|
it != _gameVariables.end(); it++) {
|
|
out->writeUint32BE(*it);
|
|
}
|
|
for (uint i = _gameVariables.size(); i < 100; i++) {
|
|
out->writeUint32BE(0);
|
|
}
|
|
|
|
out->finalize();
|
|
|
|
delete out;
|
|
}
|
|
|
|
bool CryOmni3DEngine_Versailles::loadGame(bool visit, uint saveNum) {
|
|
Common::SeekableReadStream *in;
|
|
|
|
if (visit && saveNum == 1) {
|
|
// Load bootstrap visit
|
|
Common::File *visitFile = new Common::File();
|
|
if (!visitFile->open(getFilePath(kFileTypeSaveGameVisit, "game0001.sav"))) {
|
|
delete visitFile;
|
|
error("Can't load visit file");
|
|
}
|
|
in = visitFile;
|
|
} else {
|
|
Common::String saveFileName = getSaveFileName(visit, saveNum);
|
|
in = _saveFileMan->openForLoading(saveFileName);
|
|
}
|
|
|
|
if (!in || in->size() != 1260) {
|
|
return false;
|
|
}
|
|
|
|
musicStop();
|
|
|
|
// Load save name but don't use it
|
|
char saveNameC[kSaveDescriptionLen];
|
|
in->read(saveNameC, sizeof(saveNameC));
|
|
|
|
// dummy values
|
|
(void) in->readUint32LE();
|
|
(void) in->readUint32BE();
|
|
(void) in->readUint32BE();
|
|
|
|
// Dialog variables
|
|
assert(_dialogsMan.size() < 200);
|
|
for (uint i = 0; i < _dialogsMan.size(); i++) {
|
|
_dialogsMan[i] = in->readByte();
|
|
}
|
|
for (uint i = _dialogsMan.size(); i < 200; i++) {
|
|
// Read the remaining bytes but don't use them
|
|
(void) in->readByte();
|
|
}
|
|
|
|
// Inventory
|
|
assert(_inventory.size() == 50);
|
|
for (Inventory::iterator it = _inventory.begin(); it != _inventory.end(); it++) {
|
|
uint objId = in->readUint32BE();
|
|
if (objId >= _objects.size()) {
|
|
objId = uint(-1);
|
|
}
|
|
if (objId != uint(-1)) {
|
|
*it = _objects.begin() + objId;
|
|
} else {
|
|
*it = nullptr;
|
|
}
|
|
}
|
|
// Offset of inventory in toolbar
|
|
_toolbar.setInventoryOffset(in->readUint32BE());
|
|
|
|
// Level, place, warp position
|
|
_currentLevel = in->readUint32BE();
|
|
// Use nextPlace to force place move
|
|
_nextPlaceId = in->readUint32BE();
|
|
|
|
// Store alpha and beta for later use
|
|
double alpha = in->readDoubleBE();
|
|
double beta = in->readDoubleBE();
|
|
|
|
// Places states
|
|
// Store them and use them once we called initNewLevel, we can't call it before because it needs _gameVariables (and especially kCurrentTime) to be correctly set
|
|
uint32 placesStates[100];
|
|
for (uint i = 0; i < 100; i++) {
|
|
placesStates[i] = in->readUint32BE();
|
|
}
|
|
|
|
// Game variables
|
|
assert(_gameVariables.size() < 100);
|
|
for (Common::Array<uint>::iterator it = _gameVariables.begin(); it != _gameVariables.end();
|
|
it++) {
|
|
*it = in->readUint32BE();
|
|
}
|
|
for (uint i = _gameVariables.size(); i < 100; i++) {
|
|
// Read the remaining variables but don't use them
|
|
(void) in->readUint32BE();
|
|
}
|
|
|
|
delete in;
|
|
|
|
if (_gameVariables[GameVariables::kCurrentTime] == 0) {
|
|
_gameVariables[GameVariables::kCurrentTime] = 1;
|
|
}
|
|
initCountdown();
|
|
|
|
// Everything has been loaded, setup new level
|
|
// We will set places states and warp coordinates just after that to avoid them from being reset
|
|
initNewLevel(_currentLevel);
|
|
|
|
_omni3dMan.setAlpha(alpha);
|
|
_omni3dMan.setBeta(beta);
|
|
|
|
// _placeStates has just been resized in initNewLevel
|
|
uint i = 0;
|
|
for (Common::Array<PlaceState>::iterator placeIt = _placeStates.begin();
|
|
placeIt != _placeStates.end() && i < ARRAYSIZE(placesStates); placeIt++, i++) {
|
|
placeIt->state = placesStates[i];
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
} // End of namespace Versailles
|
|
} // End of namespace CryOmni3D
|