mirror of
https://github.com/libretro/scummvm.git
synced 2025-04-03 15:21:40 +00:00
LASTEXPRESS: Savegame support update
- Implement Menu::startGame() properly - Add stubs functions for game restart - Made savegame headers serializable and moved validity checks inside struct definition - Implement create/init savegame functions - Add SavegameStream to be able to read/write to the same memory stream - Add stubs for setup, writeEntry & loadEntry functions svn-id: r53841
This commit is contained in:
parent
8217efc74a
commit
ae2c4b7cd2
@ -402,7 +402,23 @@ void Logic::eventTick(const Common::Event &) {
|
||||
// Game over, Chapters & credits
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Handle game over
|
||||
/**
|
||||
* Resets the game state.
|
||||
*/
|
||||
void Logic::resetState() {
|
||||
getState()->scene = kSceneDefault;
|
||||
|
||||
warning("Logic::resetState: not implemented! You need to restart the engine until this is implemented.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle game over
|
||||
*
|
||||
* @param type The savegame type.
|
||||
* @param value The value (event, time, index, ...)
|
||||
* @param sceneIndex Index of the scene to show.
|
||||
* @param showScene true to show a scene, false to return to menu directly
|
||||
*/
|
||||
void Logic::gameOver(SavegameType type, uint32 value, SceneIndex sceneIndex, bool showScene) const {
|
||||
|
||||
getSound()->processEntries();
|
||||
|
@ -54,6 +54,7 @@ public:
|
||||
void eventMouse(const Common::Event &ev);
|
||||
void eventTick(const Common::Event &ev);
|
||||
|
||||
void resetState();
|
||||
void gameOver(SavegameType type, uint32 value, SceneIndex sceneIndex, bool showScene) const;
|
||||
void playFinalSequence() const;
|
||||
void updateCursor(bool redraw = true) const;
|
||||
|
@ -360,7 +360,7 @@ Menu::Menu(LastExpressEngine *engine) : _engine(engine),
|
||||
_gameId(kGameBlue), _hasShownStartScreen(false), _hasShownIntro(false),
|
||||
_isShowingCredits(false), _isGameStarted(false), _isShowingMenu(false),
|
||||
_creditsSequenceIndex(0), _checkHotspotsTicks(15), _mouseFlags(Common::EVENT_INVALID), _lastHotspot(NULL),
|
||||
_currentIndex(0), _currentTime(0), _lowerTime(0), _index(0), _index2(0), _time(0), _delta(0), _handleTimeDelta(false) {
|
||||
_currentIndex(0), _currentTime(0), _lowerTime(0), _index(0), _savegameIndex(0), _time(0), _delta(0), _handleTimeDelta(false) {
|
||||
|
||||
_clock = new Clock(_engine);
|
||||
_trainLine = new TrainLine(_engine);
|
||||
@ -661,8 +661,8 @@ bool Menu::handleEvent(StartMenuAction action, Common::EventType type) {
|
||||
if (_isGameStarted) {
|
||||
showFrame(kOverlayEggButtons, kButtonContinue, true);
|
||||
|
||||
if (_index2 == _index) {
|
||||
showFrame(kOverlayTooltip, isGameFinished() ? kTooltipViewGameEnding : kTooltipContinueGame, true);
|
||||
if (_savegameIndex == _index) {
|
||||
showFrame(kOverlayTooltip, getSaveLoad()->isGameFinished(_index, _savegameIndex) ? kTooltipViewGameEnding : kTooltipContinueGame, true);
|
||||
} else {
|
||||
showFrame(kOverlayTooltip, kTooltipContinueRewoundGame, true);
|
||||
}
|
||||
@ -828,7 +828,7 @@ bool Menu::handleEvent(StartMenuAction action, Common::EventType type) {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
case kMenuForwardGame:
|
||||
if (_index2 <= _index || _currentTime > _time) {
|
||||
if (_savegameIndex <= _index || _currentTime > _time) {
|
||||
hideOverlays();
|
||||
break;
|
||||
}
|
||||
@ -1075,7 +1075,7 @@ void Menu::init(bool doSavegame, SavegameType type, uint32 value) {
|
||||
|
||||
// Create a new savegame if needed
|
||||
if (!SaveLoad::isSavegamePresent(_gameId))
|
||||
SaveLoad::writeMainHeader(_gameId);
|
||||
getSaveLoad()->create(_gameId);
|
||||
|
||||
if (doSavegame)
|
||||
getSaveLoad()->saveGame(kSavegameTypeEvent2, kEntityPlayer, kEventNone);
|
||||
@ -1084,18 +1084,12 @@ void Menu::init(bool doSavegame, SavegameType type, uint32 value) {
|
||||
// TODO: remove existing savegame temp file
|
||||
}
|
||||
|
||||
// Init savegame and get the header data
|
||||
getSaveLoad()->initSavegame(_gameId, true);
|
||||
SaveLoad::SavegameMainHeader header;
|
||||
if (!SaveLoad::loadMainHeader(_gameId, &header))
|
||||
error("Menu::init: Corrupted savegame - Recovery path not implemented!");
|
||||
|
||||
// Init Menu values
|
||||
_index2 = header.index;
|
||||
_lowerTime = getSaveLoad()->getEntry(_index2)->time;
|
||||
// Init savegame & menu values
|
||||
_savegameIndex = getSaveLoad()->init(_gameId, true);
|
||||
_lowerTime = getSaveLoad()->getTime(_savegameIndex);
|
||||
|
||||
if (useSameIndex)
|
||||
_index = _index2;
|
||||
_index = _savegameIndex;
|
||||
|
||||
//if (!getGlobalTimer())
|
||||
// _index3 = 0;
|
||||
@ -1103,8 +1097,8 @@ void Menu::init(bool doSavegame, SavegameType type, uint32 value) {
|
||||
if (!getProgress().chapter)
|
||||
getProgress().chapter = kChapter1;
|
||||
|
||||
getState()->time = getSaveLoad()->getEntry(_index)->time;
|
||||
getProgress().chapter = getSaveLoad()->getEntry(_index)->chapter;
|
||||
getState()->time = getSaveLoad()->getTime(_index);
|
||||
getProgress().chapter = getSaveLoad()->getChapter(_index);
|
||||
|
||||
if (_lowerTime >= kTimeStartGame) {
|
||||
_currentTime = getState()->time;
|
||||
@ -1117,11 +1111,26 @@ void Menu::init(bool doSavegame, SavegameType type, uint32 value) {
|
||||
}
|
||||
|
||||
void Menu::startGame() {
|
||||
// TODO: we need to reset the current scene
|
||||
getState()->scene = kSceneDefault;
|
||||
// TODO: Clear train sequences & savegame headers
|
||||
|
||||
getEntities()->setup(true, kEntityPlayer);
|
||||
warning("Menu::startGame: not implemented!");
|
||||
if (0 /* test for temp filename */ ) {
|
||||
if (_savegameIndex == _index)
|
||||
getSaveLoad()->loadGame(_gameId);
|
||||
else
|
||||
getSaveLoad()->loadGame2(_gameId);
|
||||
} else {
|
||||
if (_savegameIndex == _index) {
|
||||
setGlobalTimer(0);
|
||||
if (_index) {
|
||||
getSaveLoad()->loadGame(_gameId);
|
||||
} else {
|
||||
getLogic()->resetState();
|
||||
getEntities()->setup(true, kEntityPlayer);
|
||||
}
|
||||
} else {
|
||||
getSaveLoad()->loadGame2(_gameId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Switch to the next savegame
|
||||
@ -1132,7 +1141,7 @@ void Menu::switchGame() {
|
||||
|
||||
// Initialize savegame if needed
|
||||
if (!SaveLoad::isSavegamePresent(_gameId))
|
||||
SaveLoad::writeMainHeader(_gameId);
|
||||
getSaveLoad()->create(_gameId);
|
||||
|
||||
getState()->time = 0;
|
||||
|
||||
@ -1141,53 +1150,11 @@ void Menu::switchGame() {
|
||||
_trainLine->clear();
|
||||
|
||||
// Clear loaded savegame data
|
||||
getSaveLoad()->clearEntries();
|
||||
getSaveLoad()->clear();
|
||||
|
||||
init(false, kSavegameTypeIndex, 0);
|
||||
}
|
||||
|
||||
bool Menu::isGameFinished() const {
|
||||
SaveLoad::SavegameEntryHeader *data = getSaveLoad()->getEntry(_index);
|
||||
|
||||
if (_index2 != _index)
|
||||
return false;
|
||||
|
||||
if (data->type != SaveLoad::kHeaderType2)
|
||||
return false;
|
||||
|
||||
return (data->event == kEventAnnaKilled
|
||||
|| data->event == kEventKronosHostageAnnaNoFirebird
|
||||
|| data->event == kEventKahinaPunchBaggageCarEntrance
|
||||
|| data->event == kEventKahinaPunchBlue
|
||||
|| data->event == kEventKahinaPunchYellow
|
||||
|| data->event == kEventKahinaPunchSalon
|
||||
|| data->event == kEventKahinaPunchKitchen
|
||||
|| data->event == kEventKahinaPunchBaggageCar
|
||||
|| data->event == kEventKahinaPunchCar
|
||||
|| data->event == kEventKahinaPunchSuite4
|
||||
|| data->event == kEventKahinaPunchRestaurant
|
||||
|| data->event == kEventKahinaPunch
|
||||
|| data->event == kEventKronosGiveFirebird
|
||||
|| data->event == kEventAugustFindCorpse
|
||||
|| data->event == kEventMertensBloodJacket
|
||||
|| data->event == kEventMertensCorpseFloor
|
||||
|| data->event == kEventMertensCorpseBed
|
||||
|| data->event == kEventCoudertBloodJacket
|
||||
|| data->event == kEventGendarmesArrestation
|
||||
|| data->event == kEventAbbotDrinkGiveDetonator
|
||||
|| data->event == kEventMilosCorpseFloor
|
||||
|| data->event == kEventLocomotiveAnnaStopsTrain
|
||||
|| data->event == kEventTrainStopped
|
||||
|| data->event == kEventCathVesnaRestaurantKilled
|
||||
|| data->event == kEventCathVesnaTrainTopKilled
|
||||
|| data->event == kEventLocomotiveConductorsDiscovered
|
||||
|| data->event == kEventViennaAugustUnloadGuns
|
||||
|| data->event == kEventViennaKronosFirebird
|
||||
|| data->event == kEventVergesAnnaDead
|
||||
|| data->event == kEventTrainExplosionBridge
|
||||
|| data->event == kEventKronosBringNothing);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Overlays & elements
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@ -1307,7 +1274,7 @@ void Menu::initTime(SavegameType type, uint32 value) {
|
||||
|
||||
// Iterate through existing entries
|
||||
do {
|
||||
if (getSaveLoad()->getEntry(entryIndex)->time <= value)
|
||||
if (getSaveLoad()->getTime(entryIndex) <= value)
|
||||
break;
|
||||
|
||||
entryIndex--;
|
||||
@ -1320,7 +1287,7 @@ void Menu::initTime(SavegameType type, uint32 value) {
|
||||
break;
|
||||
|
||||
do {
|
||||
if (getSaveLoad()->getEntry(entryIndex)->event == (EventIndex)value)
|
||||
if (getSaveLoad()->getValue(entryIndex) == value)
|
||||
break;
|
||||
|
||||
entryIndex--;
|
||||
@ -1332,7 +1299,7 @@ void Menu::initTime(SavegameType type, uint32 value) {
|
||||
if (_index > 1) {
|
||||
uint32 index = _index;
|
||||
do {
|
||||
if (getSaveLoad()->getEntry(index)->event == (EventIndex)value)
|
||||
if (getSaveLoad()->getValue(index) == value)
|
||||
break;
|
||||
|
||||
index--;
|
||||
@ -1347,7 +1314,7 @@ void Menu::initTime(SavegameType type, uint32 value) {
|
||||
|
||||
if (entryIndex) {
|
||||
_currentIndex = entryIndex;
|
||||
updateTime(getSaveLoad()->getEntry(entryIndex)->time);
|
||||
updateTime(getSaveLoad()->getTime(entryIndex));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1381,7 +1348,7 @@ void Menu::adjustIndex(uint32 time1, uint32 time2, bool searchEntry) {
|
||||
if ((int32)_index >= 0) {
|
||||
do {
|
||||
// Calculate new delta
|
||||
int32 newDelta = time1 - getSaveLoad()->getEntry(currentIndex)->time;
|
||||
int32 newDelta = time1 - getSaveLoad()->getTime(currentIndex);
|
||||
|
||||
if (newDelta >= 0 && timeDelta >= newDelta) {
|
||||
timeDelta = newDelta;
|
||||
@ -1398,10 +1365,10 @@ void Menu::adjustIndex(uint32 time1, uint32 time2, bool searchEntry) {
|
||||
if (searchEntry) {
|
||||
uint32 currentIndex = _index;
|
||||
|
||||
if (_index2 >= _index) {
|
||||
if (_savegameIndex >= _index) {
|
||||
do {
|
||||
// Calculate new delta
|
||||
int32 newDelta = getSaveLoad()->getEntry(currentIndex)->time - time1;
|
||||
int32 newDelta = getSaveLoad()->getTime(currentIndex) - time1;
|
||||
|
||||
if (newDelta >= 0 && timeDelta > newDelta) {
|
||||
timeDelta = newDelta;
|
||||
@ -1409,7 +1376,7 @@ void Menu::adjustIndex(uint32 time1, uint32 time2, bool searchEntry) {
|
||||
}
|
||||
|
||||
++currentIndex;
|
||||
} while (currentIndex >= _index2);
|
||||
} while (currentIndex >= _savegameIndex);
|
||||
}
|
||||
} else {
|
||||
index = _index + 1;
|
||||
@ -1421,45 +1388,45 @@ void Menu::adjustIndex(uint32 time1, uint32 time2, bool searchEntry) {
|
||||
}
|
||||
|
||||
if (_index == _currentIndex) {
|
||||
if (getProgress().chapter != getSaveLoad()->getEntry(index)->chapter)
|
||||
getProgress().chapter = getSaveLoad()->getEntry(_index)->chapter;
|
||||
if (getProgress().chapter != getSaveLoad()->getChapter(index))
|
||||
getProgress().chapter = getSaveLoad()->getChapter(_index);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::goToTime(uint32 time) {
|
||||
|
||||
uint32 entryIndex = 0;
|
||||
uint32 deltaTime = (uint32)ABS((int32)(getSaveLoad()->getEntry(0)->time - time));
|
||||
uint32 deltaTime = (uint32)ABS((int32)(getSaveLoad()->getTime(0) - time));
|
||||
uint32 index = 0;
|
||||
|
||||
do {
|
||||
uint32 deltaTime2 = (uint32)ABS((int32)(getSaveLoad()->getEntry(index)->time - time));
|
||||
uint32 deltaTime2 = (uint32)ABS((int32)(getSaveLoad()->getTime(index) - time));
|
||||
if (deltaTime2 < deltaTime) {
|
||||
deltaTime = deltaTime2;
|
||||
entryIndex = index;
|
||||
}
|
||||
|
||||
++index;
|
||||
} while (_index2 >= index);
|
||||
} while (_savegameIndex >= index);
|
||||
|
||||
_currentIndex = entryIndex;
|
||||
updateTime(getSaveLoad()->getEntry(entryIndex)->time);
|
||||
updateTime(getSaveLoad()->getTime(entryIndex));
|
||||
}
|
||||
|
||||
void Menu::setTime() {
|
||||
_currentIndex = _index;
|
||||
_currentTime = getSaveLoad()->getEntry(_currentIndex)->time;
|
||||
_currentTime = getSaveLoad()->getTime(_currentIndex);
|
||||
|
||||
if (_time == _currentTime)
|
||||
adjustTime();
|
||||
}
|
||||
|
||||
void Menu::forwardTime() {
|
||||
if (_index2 <= _index)
|
||||
if (_savegameIndex <= _index)
|
||||
return;
|
||||
|
||||
_currentIndex = _index2;
|
||||
updateTime(getSaveLoad()->getEntry(_currentIndex)->time);
|
||||
_currentIndex = _savegameIndex;
|
||||
updateTime(getSaveLoad()->getTime(_currentIndex));
|
||||
}
|
||||
|
||||
void Menu::rewindTime() {
|
||||
@ -1467,7 +1434,7 @@ void Menu::rewindTime() {
|
||||
return;
|
||||
|
||||
_currentIndex = 0;
|
||||
updateTime(getSaveLoad()->getEntry(_currentIndex)->time);
|
||||
updateTime(getSaveLoad()->getTime(_currentIndex));
|
||||
}
|
||||
|
||||
void Menu::adjustTime() {
|
||||
|
@ -145,7 +145,6 @@ private:
|
||||
// Game-related
|
||||
void startGame();
|
||||
void switchGame();
|
||||
bool isGameFinished() const;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Overlays & elements
|
||||
@ -183,7 +182,7 @@ private:
|
||||
uint32 _lowerTime; // lower time value
|
||||
|
||||
uint32 _index;
|
||||
uint32 _index2;
|
||||
uint32 _savegameIndex;
|
||||
uint32 _time;
|
||||
uint32 _delta;
|
||||
bool _handleTimeDelta;
|
||||
|
@ -23,8 +23,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lastexpress/game/logic.h"
|
||||
#include "lastexpress/game/savegame.h"
|
||||
|
||||
#include "lastexpress/game/logic.h"
|
||||
#include "lastexpress/game/menu.h"
|
||||
#include "lastexpress/game/state.h"
|
||||
|
||||
#include "lastexpress/debug.h"
|
||||
@ -36,10 +38,6 @@
|
||||
|
||||
namespace LastExpress {
|
||||
|
||||
// Savegame signatures
|
||||
#define SAVEGAME_SIGNATURE 0x12001200
|
||||
#define SAVEGAME_HEADER 0xE660E660
|
||||
|
||||
// Names of savegames
|
||||
static const struct {
|
||||
const char *saveFile;
|
||||
@ -56,15 +54,91 @@ static const struct {
|
||||
// Constructors
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SaveLoad::SaveLoad(LastExpressEngine *engine) : _engine(engine) {
|
||||
_gameTicksLastSavegame = 0;
|
||||
SaveLoad::SaveLoad(LastExpressEngine *engine) : _engine(engine), _savegame(NULL), _gameTicksLastSavegame(0) {
|
||||
}
|
||||
|
||||
SaveLoad::~SaveLoad() {
|
||||
clear();
|
||||
|
||||
SAFE_DELETE(_savegame);
|
||||
|
||||
//Zero passed pointers
|
||||
_engine = NULL;
|
||||
}
|
||||
|
||||
clearEntries();
|
||||
void SaveLoad::initStream() {
|
||||
SAFE_DELETE(_savegame);
|
||||
|
||||
_savegame = new SavegameStream();
|
||||
}
|
||||
|
||||
void SaveLoad::flushStream(GameId id) {
|
||||
Common::OutSaveFile *save = openForSaving(id);
|
||||
if (!save)
|
||||
error("SaveLoad::initSave: Cannot init savegame (%s)!", getFilename(id).c_str());
|
||||
|
||||
save->write(_savegame->getData(), _savegame->size());
|
||||
|
||||
delete save;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Init
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void SaveLoad::create(GameId id) {
|
||||
initStream();
|
||||
|
||||
Common::Serializer ser(NULL, _savegame);
|
||||
SavegameMainHeader header;
|
||||
header.saveLoadWithSerializer(ser);
|
||||
|
||||
flushStream(id);
|
||||
}
|
||||
|
||||
uint32 SaveLoad::init(GameId id, bool resetHeaders) {
|
||||
initStream();
|
||||
|
||||
// Open savegame
|
||||
Common::InSaveFile *save = openForLoading(id);
|
||||
if (save->size() < 32)
|
||||
error("SaveLoad::init - Savegame seems to be corrupted (not enough data: %i bytes)", save->size());
|
||||
|
||||
// Load all savegame data
|
||||
while (!save->eos() && !save->err())
|
||||
_savegame->writeByte(save->readByte());
|
||||
_savegame->seek(0);
|
||||
|
||||
delete save;
|
||||
|
||||
// Load the main header
|
||||
Common::Serializer ser(_savegame, NULL);
|
||||
SavegameMainHeader header;
|
||||
header.saveLoadWithSerializer(ser);
|
||||
if (!header.isValid())
|
||||
error("SaveLoad::init - Savegame seems to be corrupted (invalid header)");
|
||||
|
||||
// Reset cached entry headers if needed
|
||||
if (resetHeaders) {
|
||||
clear();
|
||||
|
||||
SavegameEntryHeader *header = new SavegameEntryHeader();
|
||||
header->time = kTimeCityParis;
|
||||
header->chapter = kChapter1;
|
||||
|
||||
_gameHeaders.push_back(header);
|
||||
}
|
||||
|
||||
warning("SaveLoad::initSavegame: not implemented!");
|
||||
|
||||
// return the index to the current save game entry (we store count + 1 entries, so we're good)
|
||||
return 0; //header.count;
|
||||
}
|
||||
|
||||
void SaveLoad::clear() {
|
||||
for (uint i = 0; i < _gameHeaders.size(); i++)
|
||||
SAFE_DELETE(_gameHeaders[i]);
|
||||
|
||||
_gameHeaders.clear();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@ -80,21 +154,80 @@ bool SaveLoad::loadGame(GameId id) {
|
||||
//Common::InSaveFile *save = SaveLoad::openForLoading(id);
|
||||
// Validate header
|
||||
|
||||
|
||||
|
||||
|
||||
error("SaveLoad::loadgame: not implemented!");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SaveLoad::loadGame2(GameId id) {
|
||||
error("SaveLoad::loadgame2: not implemented!");
|
||||
}
|
||||
|
||||
// Save game
|
||||
void SaveLoad::saveGame(SavegameType type, EntityIndex entity, uint32 value) {
|
||||
if (getState()->scene <= kSceneIntro)
|
||||
return;
|
||||
|
||||
// Save ticks
|
||||
_gameTicksLastSavegame = getState()->timeTicks;
|
||||
// Validate main header
|
||||
SavegameMainHeader header;
|
||||
if (!loadMainHeader(_savegame, &header)) {
|
||||
debugC(2, kLastExpressDebugSavegame, "SaveLoad::saveGame - Cannot load main header: %s", getFilename(getMenu()->getGameId()).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
warning("SaveLoad::savegame: not implemented!");
|
||||
// Validate the current entry if it exists
|
||||
if (header.count > 0) {
|
||||
_savegame->seek(header.offsetEntry);
|
||||
|
||||
// Load entry header
|
||||
SavegameEntryHeader entry;
|
||||
Common::Serializer ser(_savegame, NULL);
|
||||
entry.saveLoadWithSerializer(ser);
|
||||
|
||||
if (!entry.isValid() || getState()->time < entry.time || (type == kSavegameTypeTickInterval && getState()->time == entry.time))
|
||||
return;
|
||||
|
||||
if ((type == kSavegameTypeTime || type == kSavegameTypeEvent)
|
||||
&& (entry.type == kSavegameTypeTickInterval && (getState()->time - entry.time) < 450)) {
|
||||
_savegame->seek(header.offsetEntry);
|
||||
--header.count;
|
||||
} else {
|
||||
_savegame->seek(header.offset);
|
||||
}
|
||||
} else {
|
||||
// Seek to the next savegame entry
|
||||
_savegame->seek(header.offset);
|
||||
}
|
||||
|
||||
if (type != kSavegameTypeEvent2 && type != kSavegameTypeAuto)
|
||||
header.offsetEntry = _savegame->pos();
|
||||
|
||||
// Write the savegame entry
|
||||
writeEntry(type, entity, value);
|
||||
|
||||
if (!header.keepIndex)
|
||||
++header.count;
|
||||
|
||||
if (type == kSavegameTypeEvent2 || type == kSavegameTypeAuto) {
|
||||
header.keepIndex = 1;
|
||||
} else {
|
||||
header.keepIndex = 0;
|
||||
header.offset = _savegame->pos();
|
||||
|
||||
// Save ticks
|
||||
_gameTicksLastSavegame = getState()->timeTicks;
|
||||
}
|
||||
|
||||
// Validate the main header
|
||||
if (!header.isValid())
|
||||
error("SaveLoad::saveGame: main game header is invalid!");
|
||||
|
||||
// Write the main header
|
||||
_savegame->seek(0);
|
||||
Common::Serializer ser(NULL, _savegame);
|
||||
header.saveLoadWithSerializer(ser);
|
||||
|
||||
flushStream(getMenu()->getGameId());
|
||||
}
|
||||
|
||||
void SaveLoad::saveVolumeBrightness() {
|
||||
@ -107,7 +240,7 @@ void SaveLoad::saveVolumeBrightness() {
|
||||
|
||||
// Check if a specific savegame exists
|
||||
bool SaveLoad::isSavegamePresent(GameId id) {
|
||||
if (g_system->getSavefileManager()->listSavefiles(getSavegameName(id)).size() == 0)
|
||||
if (g_system->getSavefileManager()->listSavefiles(getFilename(id)).size() == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -116,114 +249,57 @@ bool SaveLoad::isSavegamePresent(GameId id) {
|
||||
// Check if the game has been started in the specific savegame
|
||||
bool SaveLoad::isSavegameValid(GameId id) {
|
||||
if (!isSavegamePresent(id)) {
|
||||
debugC(2, kLastExpressDebugSavegame, "SaveLoad::isSavegameValid - Savegame does not exist: %s", getSavegameName(id).c_str());
|
||||
debugC(2, kLastExpressDebugSavegame, "SaveLoad::isSavegameValid - Savegame does not exist: %s", getFilename(id).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
SavegameMainHeader header;
|
||||
if (!loadMainHeader(id, &header))
|
||||
return false;
|
||||
|
||||
return validateMainHeader(header);
|
||||
Common::InSaveFile *save = openForLoading(id);
|
||||
return loadMainHeader(save, &header);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Headers
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool SaveLoad::loadMainHeader(GameId id, SavegameMainHeader *header) {
|
||||
// Read first 32 bytes of savegame
|
||||
Common::InSaveFile *save = openForLoading(id);
|
||||
if (!save) {
|
||||
debugC(2, kLastExpressDebugSavegame, "SaveLoad::loadMainHeader - Cannot open savegame for reading: %s", getSavegameName(id).c_str());
|
||||
bool SaveLoad::loadMainHeader(Common::InSaveFile *stream, SavegameMainHeader *header) {
|
||||
if (!stream)
|
||||
return false;
|
||||
|
||||
// Check there is enough data (32 bytes)
|
||||
if (stream->size() < 32) {
|
||||
debugC(2, kLastExpressDebugSavegame, "SaveLoad::loadMainHeader - Savegame seems to be corrupted (not enough data: %i bytes)!", stream->size());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check there is enough data
|
||||
if (save->size() < 32) {
|
||||
debugC(2, kLastExpressDebugSavegame, "SaveLoad::loadMainHeader - Savegame seems to be corrupted (not enough data: %i bytes): %s", save->size(), getSavegameName(id).c_str());
|
||||
delete save;
|
||||
return false;
|
||||
}
|
||||
// Rewind stream
|
||||
stream->seek(0);
|
||||
|
||||
header->signature = save->readUint32LE();
|
||||
header->index = save->readUint32LE();
|
||||
header->time = save->readUint32LE();
|
||||
header->field_C = save->readUint32LE();
|
||||
header->field_10 = save->readUint32LE();
|
||||
header->brightness = save->readSint32LE();
|
||||
header->volume = save->readSint32LE();
|
||||
header->field_1C = save->readUint32LE();
|
||||
Common::Serializer ser(stream, NULL);
|
||||
header->saveLoadWithSerializer(ser);
|
||||
|
||||
delete save;
|
||||
|
||||
// Valide the header
|
||||
if (!validateMainHeader(*header)) {
|
||||
debugC(2, kLastExpressDebugSavegame, "SaveLoad::loadMainHeader - Cannot validate main header for savegame %s.", getSavegameName(id).c_str());
|
||||
// Validate the header
|
||||
if (!header->isValid()) {
|
||||
debugC(2, kLastExpressDebugSavegame, "SaveLoad::loadMainHeader - Cannot validate main header!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SaveLoad::loadEntryHeader(Common::InSaveFile *save, SavegameEntryHeader *header) {
|
||||
header->signature = save->readUint32LE();
|
||||
header->type = (HeaderType)save->readUint32LE();
|
||||
header->time = save->readUint32LE();
|
||||
header->field_C = save->readUint32LE();
|
||||
header->chapter = (ChapterIndex)save->readUint32LE();
|
||||
header->event = (EventIndex)save->readUint32LE();
|
||||
header->field_18 = save->readUint32LE();
|
||||
header->field_1C = save->readUint32LE();
|
||||
void SaveLoad::loadEntryHeader(SavegameEntryHeader *header) {
|
||||
error("SaveLoad::loadEntryHeader: not implemented!");
|
||||
}
|
||||
|
||||
bool SaveLoad::validateMainHeader(const SavegameMainHeader &header) {
|
||||
if (header.signature != SAVEGAME_SIGNATURE)
|
||||
return false;
|
||||
|
||||
/* Check not needed as it can never be < 0
|
||||
if (header.chapter < 0)
|
||||
return false;*/
|
||||
|
||||
if (header.time < 32)
|
||||
return false;
|
||||
|
||||
if (header.field_C < 32)
|
||||
return false;
|
||||
|
||||
if (header.field_10 != 1 && header.field_10)
|
||||
return false;
|
||||
|
||||
if (header.brightness < 0 || header.brightness > 6)
|
||||
return false;
|
||||
|
||||
if (header.volume < 0 || header.volume > 7)
|
||||
return false;
|
||||
|
||||
if (header.field_1C != 9)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Entries
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) {
|
||||
warning("SaveLoad::writeEntry: not implemented!");
|
||||
}
|
||||
|
||||
bool SaveLoad::validateEntryHeader(const SavegameEntryHeader &header) {
|
||||
if (header.signature != SAVEGAME_HEADER)
|
||||
return false;
|
||||
|
||||
if (header.type < kHeaderType1 || header.type > kHeaderType5)
|
||||
return false;
|
||||
|
||||
if (header.time < kTimeStartGame || header.time > kTimeCityConstantinople)
|
||||
return false;
|
||||
|
||||
if (header.field_C <= 0 || header.field_C >= 15)
|
||||
return false;
|
||||
|
||||
/* No check for < 0, as it cannot happen normaly */
|
||||
if (header.chapter == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
void SaveLoad::readEntry(SavegameType type, EntityIndex entity, uint32 value) {
|
||||
warning("SaveLoad::readEntry: not implemented!");
|
||||
}
|
||||
|
||||
SaveLoad::SavegameEntryHeader *SaveLoad::getEntry(uint32 index) {
|
||||
@ -233,78 +309,80 @@ SaveLoad::SavegameEntryHeader *SaveLoad::getEntry(uint32 index) {
|
||||
return _gameHeaders[index];
|
||||
}
|
||||
|
||||
void SaveLoad::clearEntries() {
|
||||
for (uint i = 0; i < _gameHeaders.size(); i++)
|
||||
SAFE_DELETE(_gameHeaders[i]);
|
||||
|
||||
_gameHeaders.clear();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Init
|
||||
// Checks
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void SaveLoad::writeMainHeader(GameId id) {
|
||||
Common::OutSaveFile *save = openForSaving(id);
|
||||
if (!save) {
|
||||
debugC(2, kLastExpressDebugSavegame, "SaveLoad::initSavegame - Cannot open savegame for writing: %s", getSavegameName(id).c_str());
|
||||
return;
|
||||
}
|
||||
bool SaveLoad::isGameFinished(uint32 menuIndex, uint32 savegameIndex) {
|
||||
SavegameEntryHeader *data = getEntry(menuIndex);
|
||||
|
||||
// Write default values to savegame
|
||||
save->writeUint32LE(SAVEGAME_SIGNATURE);
|
||||
save->writeUint32LE(0);
|
||||
save->writeUint32LE(32);
|
||||
save->writeUint32LE(32);
|
||||
save->writeUint32LE(0);
|
||||
save->writeUint32LE(3);
|
||||
save->writeUint32LE(7);
|
||||
save->writeUint32LE(9);
|
||||
if (savegameIndex != menuIndex)
|
||||
return false;
|
||||
|
||||
delete save;
|
||||
if (data->type != kSavegameTypeEvent)
|
||||
return false;
|
||||
|
||||
return (data->value == kEventAnnaKilled
|
||||
|| data->value == kEventKronosHostageAnnaNoFirebird
|
||||
|| data->value == kEventKahinaPunchBaggageCarEntrance
|
||||
|| data->value == kEventKahinaPunchBlue
|
||||
|| data->value == kEventKahinaPunchYellow
|
||||
|| data->value == kEventKahinaPunchSalon
|
||||
|| data->value == kEventKahinaPunchKitchen
|
||||
|| data->value == kEventKahinaPunchBaggageCar
|
||||
|| data->value == kEventKahinaPunchCar
|
||||
|| data->value == kEventKahinaPunchSuite4
|
||||
|| data->value == kEventKahinaPunchRestaurant
|
||||
|| data->value == kEventKahinaPunch
|
||||
|| data->value == kEventKronosGiveFirebird
|
||||
|| data->value == kEventAugustFindCorpse
|
||||
|| data->value == kEventMertensBloodJacket
|
||||
|| data->value == kEventMertensCorpseFloor
|
||||
|| data->value == kEventMertensCorpseBed
|
||||
|| data->value == kEventCoudertBloodJacket
|
||||
|| data->value == kEventGendarmesArrestation
|
||||
|| data->value == kEventAbbotDrinkGiveDetonator
|
||||
|| data->value == kEventMilosCorpseFloor
|
||||
|| data->value == kEventLocomotiveAnnaStopsTrain
|
||||
|| data->value == kEventTrainStopped
|
||||
|| data->value == kEventCathVesnaRestaurantKilled
|
||||
|| data->value == kEventCathVesnaTrainTopKilled
|
||||
|| data->value == kEventLocomotiveConductorsDiscovered
|
||||
|| data->value == kEventViennaAugustUnloadGuns
|
||||
|| data->value == kEventViennaKronosFirebird
|
||||
|| data->value == kEventVergesAnnaDead
|
||||
|| data->value == kEventTrainExplosionBridge
|
||||
|| data->value == kEventKronosBringNothing);
|
||||
}
|
||||
|
||||
void SaveLoad::initSavegame(GameId id, bool resetHeaders) {
|
||||
//Common::OutSaveFile *save = openForSaving(id);
|
||||
//if (!save) {
|
||||
// debugC(2, kLastExpressDebugSavegame, "SaveLoad::initSavegame - Cannot open savegame for writing: %s", getSavegameName(id).c_str());
|
||||
// return;
|
||||
//}
|
||||
|
||||
if (resetHeaders) {
|
||||
clearEntries();
|
||||
|
||||
SavegameEntryHeader *header = new SavegameEntryHeader();
|
||||
header->time = kTimeCityParis;
|
||||
header->chapter = kChapter1;
|
||||
|
||||
_gameHeaders.push_back(header);
|
||||
}
|
||||
|
||||
// Open the savegame and read all game headers
|
||||
|
||||
warning("SaveLoad::initSavegame: not implemented!");
|
||||
|
||||
//delete save;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Private methods
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Get the file name from the savegame ID
|
||||
Common::String SaveLoad::getSavegameName(GameId id) {
|
||||
Common::String SaveLoad::getFilename(GameId id) {
|
||||
if (id >= 6)
|
||||
error("SaveLoad::getSavegameName - attempting to use an invalid game id. Valid values: 0 - 5, was %d", id);
|
||||
error("SaveLoad::getName - attempting to use an invalid game id. Valid values: 0 - 5, was %d", id);
|
||||
|
||||
return gameInfo[id].saveFile;
|
||||
}
|
||||
|
||||
Common::InSaveFile *SaveLoad::openForLoading(GameId id) {
|
||||
return g_system->getSavefileManager()->openForLoading(getSavegameName(id));
|
||||
Common::InSaveFile *load = g_system->getSavefileManager()->openForLoading(getFilename(id));
|
||||
|
||||
if (!load)
|
||||
debugC(2, kLastExpressDebugSavegame, "SaveLoad::openForLoading - Cannot open savegame for loading: %s", getFilename(id).c_str());
|
||||
|
||||
return load;
|
||||
}
|
||||
|
||||
Common::OutSaveFile *SaveLoad::openForSaving(GameId id) {
|
||||
return g_system->getSavefileManager()->openForSaving(getSavegameName(id));
|
||||
Common::OutSaveFile *save = g_system->getSavefileManager()->openForSaving(getFilename(id));
|
||||
|
||||
if (!save)
|
||||
debugC(2, kLastExpressDebugSavegame, "SaveLoad::openForSaving - Cannot open savegame for writing: %s", getFilename(id).c_str());
|
||||
|
||||
return save;
|
||||
}
|
||||
|
||||
} // End of namespace LastExpress
|
||||
|
@ -77,95 +77,208 @@
|
||||
#include "lastexpress/shared.h"
|
||||
|
||||
#include "common/savefile.h"
|
||||
#include "common/serializer.h"
|
||||
|
||||
namespace LastExpress {
|
||||
|
||||
// Savegame signatures
|
||||
#define SAVEGAME_SIGNATURE 0x12001200
|
||||
#define SAVEGAME_HEADER 0xE660E660
|
||||
|
||||
class LastExpressEngine;
|
||||
|
||||
class SaveLoad {
|
||||
public:
|
||||
enum HeaderType {
|
||||
kHeaderTypeNone = 0,
|
||||
kHeaderType1 = 1,
|
||||
kHeaderType2 = 2,
|
||||
kHeaderType3 = 3,
|
||||
kHeaderType4 = 4,
|
||||
kHeaderType5 = 5
|
||||
};
|
||||
|
||||
struct SavegameMainHeader {
|
||||
uint32 signature;
|
||||
uint32 index;
|
||||
uint32 time;
|
||||
uint32 field_C;
|
||||
uint32 field_10;
|
||||
int32 brightness;
|
||||
int32 volume;
|
||||
uint32 field_1C;
|
||||
};
|
||||
|
||||
struct SavegameEntryHeader {
|
||||
uint32 signature;
|
||||
HeaderType type;
|
||||
uint32 time;
|
||||
int field_C;
|
||||
ChapterIndex chapter;
|
||||
EventIndex event;
|
||||
int field_18;
|
||||
int field_1C;
|
||||
|
||||
SavegameEntryHeader() {
|
||||
signature = 0;
|
||||
type = kHeaderTypeNone;
|
||||
time = 0;
|
||||
field_C = 0;
|
||||
chapter = kChapterAll;
|
||||
event = kEventNone;
|
||||
field_18 = 0;
|
||||
field_1C = 0;
|
||||
}
|
||||
};
|
||||
|
||||
SaveLoad(LastExpressEngine *engine);
|
||||
~SaveLoad();
|
||||
|
||||
// Init
|
||||
void create(GameId id);
|
||||
void clear();
|
||||
uint32 init(GameId id, bool resetHeaders);
|
||||
|
||||
// Save & Load
|
||||
bool loadGame(GameId id);
|
||||
bool loadGame2(GameId id);
|
||||
void saveGame(SavegameType type, EntityIndex entity, uint32 value);
|
||||
|
||||
void saveVolumeBrightness();
|
||||
|
||||
// Init
|
||||
void initSavegame(GameId id, bool resetHeaders);
|
||||
static void writeMainHeader(GameId id);
|
||||
|
||||
// Getting information
|
||||
static bool isSavegamePresent(GameId id);
|
||||
static bool isSavegameValid(GameId id);
|
||||
|
||||
// Opening save files
|
||||
static Common::InSaveFile *openForLoading(GameId id);
|
||||
static Common::OutSaveFile *openForSaving(GameId id);
|
||||
bool isGameFinished(uint32 menuIndex, uint32 savegameIndex);
|
||||
|
||||
// Headers
|
||||
static bool loadMainHeader(GameId id, SavegameMainHeader *header);
|
||||
SavegameEntryHeader *getEntry(uint32 index);
|
||||
void clearEntries();
|
||||
|
||||
uint32 getLastSavegameTicks() const { return _gameTicksLastSavegame; }
|
||||
// Accessors
|
||||
TimeValue getTime(uint32 index) { return getEntry(index)->time; }
|
||||
ChapterIndex getChapter(uint32 index) { return getEntry(index)->chapter; }
|
||||
uint32 getValue(uint32 index) { return getEntry(index)->value; }
|
||||
uint32 getLastSavegameTicks() const { return _gameTicksLastSavegame; }
|
||||
|
||||
private:
|
||||
class SavegameStream : public Common::MemoryWriteStreamDynamic, public Common::SeekableReadStream {
|
||||
public:
|
||||
SavegameStream() : MemoryWriteStreamDynamic(DisposeAfterUse::YES),
|
||||
_eos(false) {}
|
||||
|
||||
int32 pos() const { return MemoryWriteStreamDynamic::pos(); }
|
||||
int32 size() const { return MemoryWriteStreamDynamic::size(); }
|
||||
bool seek(int32 offset, int whence = SEEK_SET) { return MemoryWriteStreamDynamic::seek(offset, whence); }
|
||||
bool eos() const { return _eos; }
|
||||
uint32 read(void *dataPtr, uint32 dataSize) {
|
||||
if ((int32)dataSize > size() - pos()) {
|
||||
dataSize = size() - pos();
|
||||
_eos = true;
|
||||
}
|
||||
memcpy(dataPtr, getData() + pos(), dataSize);
|
||||
|
||||
seek(dataSize, SEEK_CUR);
|
||||
|
||||
return dataSize;
|
||||
}
|
||||
private:
|
||||
bool _eos;
|
||||
};
|
||||
|
||||
LastExpressEngine *_engine;
|
||||
|
||||
uint32 _gameTicksLastSavegame;
|
||||
struct SavegameMainHeader : Common::Serializable {
|
||||
uint32 signature;
|
||||
uint32 count;
|
||||
uint32 offset;
|
||||
uint32 offsetEntry;
|
||||
uint32 keepIndex;
|
||||
int32 brightness;
|
||||
int32 volume;
|
||||
uint32 field_1C;
|
||||
|
||||
SavegameMainHeader() {
|
||||
signature = SAVEGAME_SIGNATURE;
|
||||
count = 0;
|
||||
offset = 32;
|
||||
offsetEntry = 32;
|
||||
keepIndex = 0;
|
||||
brightness = 3;
|
||||
volume = 7;
|
||||
field_1C = 9;
|
||||
}
|
||||
|
||||
void saveLoadWithSerializer(Common::Serializer &s) {
|
||||
s.syncAsUint32LE(signature);
|
||||
s.syncAsUint32LE(count);
|
||||
s.syncAsUint32LE(offset);
|
||||
s.syncAsUint32LE(offsetEntry);
|
||||
s.syncAsUint32LE(keepIndex);
|
||||
s.syncAsUint32LE(brightness);
|
||||
s.syncAsUint32LE(volume);
|
||||
s.syncAsUint32LE(field_1C);
|
||||
}
|
||||
|
||||
bool isValid() {
|
||||
if (signature != SAVEGAME_SIGNATURE)
|
||||
return false;
|
||||
|
||||
/* Check not needed as it can never be < 0
|
||||
if (header.chapter < 0)
|
||||
return false;*/
|
||||
|
||||
if (offset < 32)
|
||||
return false;
|
||||
|
||||
if (offsetEntry < 32)
|
||||
return false;
|
||||
|
||||
if (keepIndex != 1 && keepIndex != 0)
|
||||
return false;
|
||||
|
||||
if (brightness < 0 || brightness > 6)
|
||||
return false;
|
||||
|
||||
if (volume < 0 || volume > 7)
|
||||
return false;
|
||||
|
||||
if (field_1C != 9)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct SavegameEntryHeader : Common::Serializable {
|
||||
uint32 signature;
|
||||
SavegameType type;
|
||||
TimeValue time;
|
||||
int field_C;
|
||||
ChapterIndex chapter;
|
||||
uint32 value;
|
||||
int field_18;
|
||||
int field_1C;
|
||||
|
||||
SavegameEntryHeader() {
|
||||
signature = 0;
|
||||
type = kSavegameTypeIndex;
|
||||
time = kTimeNone;
|
||||
field_C = 0;
|
||||
chapter = kChapterAll;
|
||||
value = 0;
|
||||
field_18 = 0;
|
||||
field_1C = 0;
|
||||
}
|
||||
|
||||
void saveLoadWithSerializer(Common::Serializer &s) {
|
||||
s.syncAsUint32LE(signature);
|
||||
s.syncAsUint32LE(type);
|
||||
s.syncAsUint32LE(time);
|
||||
s.syncAsUint32LE(field_C);
|
||||
s.syncAsUint32LE(chapter);
|
||||
s.syncAsUint32LE(value);
|
||||
s.syncAsUint32LE(field_18);
|
||||
s.syncAsUint32LE(field_1C);
|
||||
}
|
||||
|
||||
bool isValid() {
|
||||
if (signature != SAVEGAME_HEADER)
|
||||
return false;
|
||||
|
||||
if (type < kSavegameTypeTime || type > kSavegameTypeTickInterval)
|
||||
return false;
|
||||
|
||||
if (time < kTimeStartGame || time > kTimeCityConstantinople)
|
||||
return false;
|
||||
|
||||
if (field_C <= 0 || field_C >= 15)
|
||||
return false;
|
||||
|
||||
/* No check for < 0, as it cannot happen normaly */
|
||||
if (chapter == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
SavegameStream *_savegame;
|
||||
Common::Array<SavegameEntryHeader *> _gameHeaders;
|
||||
uint32 _gameTicksLastSavegame;
|
||||
|
||||
static Common::String getSavegameName(GameId id);
|
||||
// Headers
|
||||
static bool loadMainHeader(Common::InSaveFile *stream, SavegameMainHeader *header);
|
||||
void loadEntryHeader(SavegameEntryHeader *header);
|
||||
|
||||
static void loadEntryHeader(Common::InSaveFile *save, SavegameEntryHeader *header);
|
||||
// Entries
|
||||
void writeEntry(SavegameType type, EntityIndex entity, uint32 value);
|
||||
void readEntry(SavegameType type, EntityIndex entity, uint32 value);
|
||||
SavegameEntryHeader *getEntry(uint32 index);
|
||||
|
||||
static bool validateMainHeader(const SavegameMainHeader &header);
|
||||
static bool validateEntryHeader(const SavegameEntryHeader &header);
|
||||
// Opening save files
|
||||
static Common::String getFilename(GameId id);
|
||||
static Common::InSaveFile *openForLoading(GameId id);
|
||||
static Common::OutSaveFile *openForSaving(GameId id);
|
||||
|
||||
// Savegame stream
|
||||
void initStream();
|
||||
void flushStream(GameId id);
|
||||
};
|
||||
|
||||
} // End of namespace LastExpress
|
||||
|
@ -342,6 +342,8 @@ enum SceneIndex {
|
||||
kSceneNone = 0,
|
||||
kSceneMenu = 1,
|
||||
|
||||
kSceneIntro = 30,
|
||||
|
||||
// Inventory
|
||||
kSceneMatchbox = 31,
|
||||
kSceneTelegram = 32,
|
||||
|
Loading…
x
Reference in New Issue
Block a user