From 700bcc73e1b829cb15b1dcc56027b8da364c2468 Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Sun, 14 Jan 2018 14:16:58 -0500 Subject: XEEN: Fixes for encryption and resource offsets in save files --- engines/xeen/files.cpp | 29 +++++++++++++++++------------ engines/xeen/files.h | 27 ++++++++++++++++++++++++++- engines/xeen/saves.cpp | 2 +- 3 files changed, 44 insertions(+), 14 deletions(-) (limited to 'engines') diff --git a/engines/xeen/files.cpp b/engines/xeen/files.cpp index 164313ca4c..0c6ff1d0cf 100644 --- a/engines/xeen/files.cpp +++ b/engines/xeen/files.cpp @@ -88,12 +88,13 @@ void BaseCCArchive::loadIndex(Common::SeekableReadStream &stream) { void BaseCCArchive::saveIndex(Common::WriteStream &stream) { // Fill up the data for the index entries into a raw data block byte *rawIndex = new byte[_index.size() * 8]; + byte b; byte *entryP = rawIndex; for (uint i = 0; i < _index.size(); ++i, entryP += 8) { CCEntry &entry = _index[i]; WRITE_LE_UINT16(&entryP[0], entry._id); - WRITE_LE_UINT32(&entryP[2], entry._offset); + WRITE_LE_UINT32(&entryP[2], entry._writeOffset); WRITE_LE_UINT16(&entryP[5], entry._size); entryP[7] = 0; } @@ -101,8 +102,11 @@ void BaseCCArchive::saveIndex(Common::WriteStream &stream) { // Encrypt the index int seed = 0xac; for (uint i = 0; i < _index.size() * 8; ++i, seed += 0x67) { - byte b = (rawIndex[i] - seed) & 0xff; - rawIndex[i] = (byte)((b >> 2) | (b << 6)); + b = (rawIndex[i] - seed) & 0xff; + b = (byte)((b >> 2) | (b << 6)); + + assert(rawIndex[i] == (byte)((((b << 2) | (b >> 6)) + seed) & 0xff)); + rawIndex[i] = b; } // Write out the number of entries and the encrypted index data @@ -489,19 +493,19 @@ void SaveArchive::save(Common::WriteStream &s) { _party->synchronize(sPty); pty.finalize(); - // First caclculate file offsets for each resource, since replaced resources - // will shift file offsets for even the succeeding unchanged resources - for (uint idx = 1, pos = _index[0]._offset + _index[0]._size; idx < _index.size(); ++idx) { - _index[idx]._offset = pos; - pos += _index[idx]._size; + // First caclculate new offsets and total filesize + _dataSize = _index.size() * 8 + 2; + for (uint idx = 0; idx < _index.size(); ++idx) { + _index[idx]._writeOffset = (idx == 0) ? _dataSize : + _index[idx - 1]._writeOffset + _index[idx - 1]._size; + _dataSize += _index[idx]._size; } - // Write out the size of the save archive - _dataSize = _index.back()._offset + _index.back()._size; s.writeUint32LE(_dataSize); // Save out the index - saveIndex(s); + SubWriteStream dataStream(&s); + saveIndex(dataStream); // Save out each resource in turn for (uint idx = 0; idx < _index.size(); ++idx) { @@ -511,7 +515,8 @@ void SaveArchive::save(Common::WriteStream &s) { entry->read(data, entry->size()); // Write it out to the savegame - s.write(data, entry->size()); + assert(dataStream.pos() == _index[idx]._writeOffset); + dataStream.write(data, entry->size()); delete[] data; delete entry; } diff --git a/engines/xeen/files.h b/engines/xeen/files.h index ff10db5228..37b2422e2d 100644 --- a/engines/xeen/files.h +++ b/engines/xeen/files.h @@ -64,8 +64,9 @@ struct CCEntry { uint16 _id; uint32 _offset; uint16 _size; + uint32 _writeOffset; - CCEntry() : _id(0), _offset(0), _size(0) {} + CCEntry() : _id(0), _offset(0), _size(0), _writeOffset(0) {} CCEntry(uint16 id, uint32 offset, uint32 size) : _id(id), _offset(offset), _size(size) { } @@ -185,6 +186,30 @@ public: static bool exists(const Common::String &filename, int ccMode); }; +/** + * SubWriteStream provides a way of compartmentalizing writing to a subsection of + * a file. This is primarily useful for the pos() function which can, for example, + * be used in asserts to ensure writing is being done at the correct offset within + * the bounds of the structure being written. +*/ +class SubWriteStream : virtual public Common::WriteStream { +protected: + Common::WriteStream *_parentStream; + uint32 _begin; + DisposeAfterUse::Flag _disposeAfterUse; +public: + SubWriteStream(Common::WriteStream *parentStream) : + _parentStream(parentStream), _begin(parentStream->pos()) { + } + + virtual uint32 write(const void *dataPtr, uint32 dataSize) { + return _parentStream->write(dataPtr, dataSize); + } + virtual bool flush() { return _parentStream->flush(); } + virtual void finalize() {} + virtual int32 pos() const { return _parentStream->pos() - _begin; } +}; + class StringArray : public Common::StringArray { public: StringArray() {} diff --git a/engines/xeen/saves.cpp b/engines/xeen/saves.cpp index f84176c935..166cf6fdec 100644 --- a/engines/xeen/saves.cpp +++ b/engines/xeen/saves.cpp @@ -143,7 +143,7 @@ Common::Error SavesManager::saveGameState(int slot, const Common::String &desc) Map &map = *g_vm->_map; map.saveMaze(); - + // Write the savegame header XeenSavegameHeader header; header._saveName = desc; writeSavegameHeader(out, header); -- cgit v1.2.3