#include "common/savefile.h" #include "common/system.h" #include "igor/igor.h" namespace Igor { class TypeSerializer { public: TypeSerializer(Common::ReadStream *s) : _readStr(s), _writeStr(0), _saving(false) {} TypeSerializer(Common::WriteStream *s) : _readStr(0), _writeStr(s), _saving(true) {} #define SAVEORLOAD_BASIC_TYPE(T, N, S) \ void saveOrLoad ## N (T &t) { \ if (_saving) { \ _writeStr->write ## S (t); \ } else { \ t = _readStr->read ## S (); \ } \ } SAVEORLOAD_BASIC_TYPE(int8, Byte, Byte) SAVEORLOAD_BASIC_TYPE(uint8, Byte, Byte) SAVEORLOAD_BASIC_TYPE(int16, Int16, Uint16LE) SAVEORLOAD_BASIC_TYPE(uint16, Int16, Uint16LE) void saveOrLoadBool(bool &b) { if (_saving) { _writeStr->writeByte(b ? 1 : 0); } else { b = _readStr->readByte() != 0; } } void saveOrLoadPad(int sz) { if (_saving) { while (sz--) { _writeStr->writeByte(0); } } else { while (sz--) { _readStr->readByte(); } } } void saveOrLoadPascalString(char *str) { if (_saving) { int len = strlen(str); assert(len < 100); _writeStr->writeByte(len); _writeStr->write(str, len); for (; len < 100; ++len) { _writeStr->writeByte(' '); } } else { int len = _readStr->readByte(); assert(len < 100); _readStr->read(str, len); str[len] = 0; for (; len < 100; ++len) { _readStr->readByte(); } } } void saveOrLoadWalkData(WalkData &wd) { saveOrLoadPad(2); saveOrLoadInt16(wd.x); saveOrLoadInt16(wd.y); saveOrLoadByte(wd.posNum); saveOrLoadByte(wd.frameNum); saveOrLoadByte(wd.clipSkipX); saveOrLoadInt16(wd.clipWidth); saveOrLoadInt16(wd.scaleWidth); saveOrLoadByte(wd.xPosChanged); saveOrLoadInt16(wd.dxPos); saveOrLoadByte(wd.yPosChanged); saveOrLoadInt16(wd.dyPos); saveOrLoadByte(wd.scaleHeight); } void saveOrLoadAction(Action &a) { saveOrLoadByte(a.verb); saveOrLoadByte(a.object1Num); saveOrLoadByte(a.object1Type); saveOrLoadByte(a.verbType); saveOrLoadByte(a.object2Num); saveOrLoadByte(a.object2Type); } void saveOrLoadGameState(GameStateData &gs) { saveOrLoadByte(gs.enableLight); saveOrLoadByte(gs.colorLum); for (int i = 0; i < 5; ++i) { saveOrLoadInt16(gs.counter[i]); } saveOrLoadBool(gs.igorMoving); saveOrLoadBool(gs.dialogueTextRunning); saveOrLoadBool(gs.updateLight); saveOrLoadBool(gs.unkF); saveOrLoadByte(gs.unk10); saveOrLoadByte(gs.unk11); saveOrLoadBool(gs.dialogueStarted); saveOrLoadPad(1); for (int i = 0; i < 500; ++i) { saveOrLoadByte(gs.dialogueData[i]); } saveOrLoadByte(gs.dialogueChoiceStart); saveOrLoadByte(gs.dialogueChoiceCount); saveOrLoadPad(2); saveOrLoadByte(gs.nextMusicCounter); saveOrLoadBool(gs.jumpToNextMusic); saveOrLoadByte(gs.configSoundEnabled); saveOrLoadByte(gs.talkSpeed); saveOrLoadByte(gs.talkMode); saveOrLoadPad(3); saveOrLoadByte(gs.musicNum); saveOrLoadByte(gs.musicSequenceIndex); } private: bool _saving; Common::ReadStream *_readStr; Common::WriteStream *_writeStr; }; void IgorEngine::saveOrLoadGameState(TypeSerializer &typeSerializer) { for (int i = 0; i < 100; ++i) { typeSerializer.saveOrLoadWalkData(_walkData[i]); } typeSerializer.saveOrLoadPad(20); typeSerializer.saveOrLoadByte(_walkDataCurrentIndex); typeSerializer.saveOrLoadByte(_walkDataLastIndex); typeSerializer.saveOrLoadByte(_walkCurrentFrame); typeSerializer.saveOrLoadByte(_walkCurrentPos); typeSerializer.saveOrLoadPad(23); typeSerializer.saveOrLoadAction(_currentAction); typeSerializer.saveOrLoadPad(10); typeSerializer.saveOrLoadInt16(_currentPart); typeSerializer.saveOrLoadPad(8); typeSerializer.saveOrLoadByte(_actionCode); typeSerializer.saveOrLoadByte(_actionWalkPoint); typeSerializer.saveOrLoadPad(2); typeSerializer.saveOrLoadInt16(_inputVars[kInputCursorXPos]); typeSerializer.saveOrLoadInt16(_inputVars[kInputCursorYPos]); typeSerializer.saveOrLoadGameState(_gameState); for (int i = 0; i < 112; ++i) { typeSerializer.saveOrLoadByte(_objectsState[i]); } for (int i = 0; i < 74; ++i) { typeSerializer.saveOrLoadByte(_inventoryInfo[i]); } } Common::Error IgorEngine::loadGameState(int slot) { char name[64]; generateGameStateFileName(slot, name, 63); Common::InSaveFile *isf = _saveFileMan->openForLoading(name); if (isf) { TypeSerializer ts(isf); ts.saveOrLoadPascalString(_saveStateDescriptions[slot]); saveOrLoadGameState(ts); delete isf; memcpy(_igorPalette, (_currentPart == 760) ? PAL_IGOR_2 : PAL_IGOR_1, 48); UPDATE_OBJECT_STATE(255); playMusic(_gameState.musicNum); _system->warpMouse(_inputVars[kInputCursorXPos], _inputVars[kInputCursorYPos]); if (_currentPart < 900) { showCursor(); } debug(0, "Loaded state, current part %d", _currentPart); } return Common::kNoError; // TODO: return success/failure } Common::Error IgorEngine::saveGameState(int slot) { char name[64]; generateGameStateFileName(slot, name, 63); Common::OutSaveFile *osf = _saveFileMan->openForSaving(name); if (osf) { TypeSerializer ts(osf); ts.saveOrLoadPascalString(_saveStateDescriptions[slot]); saveOrLoadGameState(ts); delete osf; } return Common::kNoError; // TODO: return success/failure } void IgorEngine::generateGameStateFileName(int num, char *dst, int len) const { snprintf(dst, len, "%s.%d", _targetName.c_str(), num); dst[len] = 0; } } // namespace Igor