From 71c4329d86566aa6281a4790b08fe8e77d057f10 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Tue, 28 Jun 2011 11:58:52 -0400 Subject: MOHAWK: Rework archive handling A new base class has been introduced (aptly named "Archive"), which is much cleaner than inheriting from MohawkArchive. In addition, the underlying resource retrieving code has been merged to reduce dupliplication. --- engines/mohawk/cstime.cpp | 2 +- engines/mohawk/cursors.cpp | 2 +- engines/mohawk/livingbooks.cpp | 21 +- engines/mohawk/livingbooks.h | 10 +- engines/mohawk/mohawk.h | 4 +- engines/mohawk/myst.cpp | 4 +- engines/mohawk/resource.cpp | 592 +++++++++++++++++--------------------- engines/mohawk/resource.h | 143 +++------ engines/mohawk/riven.cpp | 4 +- engines/mohawk/riven_saveload.cpp | 2 +- 10 files changed, 322 insertions(+), 462 deletions(-) (limited to 'engines/mohawk') diff --git a/engines/mohawk/cstime.cpp b/engines/mohawk/cstime.cpp index 59bc5ad661..3f9827581b 100644 --- a/engines/mohawk/cstime.cpp +++ b/engines/mohawk/cstime.cpp @@ -217,7 +217,7 @@ void MohawkEngine_CSTime::nextScene() { void MohawkEngine_CSTime::loadResourceFile(Common::String name) { MohawkArchive *archive = new MohawkArchive(); - if (!archive->open(name + ".mhk")) + if (!archive->openFile(name + ".mhk")) error("failed to open %s.mhk", name.c_str()); _mhk.push_back(archive); } diff --git a/engines/mohawk/cursors.cpp b/engines/mohawk/cursors.cpp index a797e4e127..3284a3228f 100644 --- a/engines/mohawk/cursors.cpp +++ b/engines/mohawk/cursors.cpp @@ -234,7 +234,7 @@ LivingBooksCursorManager_v2::LivingBooksCursorManager_v2() { // Try to open the system archive if we have it _sysArchive = new MohawkArchive(); - if (!_sysArchive->open("system.mhk")) { + if (!_sysArchive->openFile("system.mhk")) { delete _sysArchive; _sysArchive = 0; } diff --git a/engines/mohawk/livingbooks.cpp b/engines/mohawk/livingbooks.cpp index 248a9e607a..8df5db720c 100644 --- a/engines/mohawk/livingbooks.cpp +++ b/engines/mohawk/livingbooks.cpp @@ -82,7 +82,7 @@ LBPage::LBPage(MohawkEngine_LivingBooks *vm) : _vm(vm) { _cascade = false; } -void LBPage::open(MohawkArchive *mhk, uint16 baseId) { +void LBPage::open(Archive *mhk, uint16 baseId) { _mhk = mhk; _baseId = baseId; @@ -385,8 +385,8 @@ bool MohawkEngine_LivingBooks::loadPage(LBMode mode, uint page, uint subpage) { warning("ignoring 'killgag' for filename '%s'", filename.c_str()); } - MohawkArchive *pageArchive = createMohawkArchive(); - if (!filename.empty() && pageArchive->open(filename)) { + Archive *pageArchive = createArchive(); + if (!filename.empty() && pageArchive->openFile(filename)) { _page = new LBPage(this); _page->open(pageArchive, 1000); } else { @@ -590,11 +590,11 @@ void MohawkEngine_LivingBooks::updatePage() { } } -void MohawkEngine_LivingBooks::addArchive(MohawkArchive *archive) { +void MohawkEngine_LivingBooks::addArchive(Archive *archive) { _mhk.push_back(archive); } -void MohawkEngine_LivingBooks::removeArchive(MohawkArchive *archive) { +void MohawkEngine_LivingBooks::removeArchive(Archive *archive) { for (uint i = 0; i < _mhk.size(); i++) { if (archive != _mhk[i]) continue; @@ -863,8 +863,11 @@ Common::String MohawkEngine_LivingBooks::convertWinFileName(const Common::String return filename; } -MohawkArchive *MohawkEngine_LivingBooks::createMohawkArchive() const { - return isPreMohawk() ? new LivingBooksArchive_v1() : new MohawkArchive(); +Archive *MohawkEngine_LivingBooks::createArchive() const { + if (isPreMohawk()) + return new LivingBooksArchive_v1(); + + return new MohawkArchive(); } bool MohawkEngine_LivingBooks::isPreMohawk() const { @@ -3855,8 +3858,8 @@ void LBProxyItem::init() { } debug(1, "LBProxyItem loading archive '%s' with id %d", filename.c_str(), baseId); - MohawkArchive *pageArchive = _vm->createMohawkArchive(); - if (!pageArchive->open(filename)) + Archive *pageArchive = _vm->createArchive(); + if (!pageArchive->openFile(filename)) error("failed to open archive '%s' (for proxy '%s')", filename.c_str(), _desc.c_str()); _page = new LBPage(_vm); _page->open(pageArchive, baseId); diff --git a/engines/mohawk/livingbooks.h b/engines/mohawk/livingbooks.h index 02cf6c3f64..ed198a60c1 100644 --- a/engines/mohawk/livingbooks.h +++ b/engines/mohawk/livingbooks.h @@ -628,7 +628,7 @@ public: LBPage(MohawkEngine_LivingBooks *vm); ~LBPage(); - void open(MohawkArchive *mhk, uint16 baseId); + void open(Archive *mhk, uint16 baseId); uint16 getResourceVersion(); void itemDestroyed(LBItem *item); @@ -638,7 +638,7 @@ public: protected: MohawkEngine_LivingBooks *_vm; - MohawkArchive *_mhk; + Archive *_mhk; Common::Array _items; uint16 _baseId; @@ -667,8 +667,8 @@ public: Common::Rect readRect(Common::ReadStreamEndian *stream); GUI::Debugger *getDebugger() { return _console; } - void addArchive(MohawkArchive *archive); - void removeArchive(MohawkArchive *Archive); + void addArchive(Archive *archive); + void removeArchive(Archive *Archive); void addItem(LBItem *item); void removeItems(const Common::Array &items); @@ -697,7 +697,7 @@ public: // helper functions, also used by LBProxyItem Common::String getFileNameFromConfig(const Common::String §ion, const Common::String &key, Common::String &leftover); - MohawkArchive *createMohawkArchive() const; + Archive *createArchive() const; private: LivingBooksConsole *_console; diff --git a/engines/mohawk/mohawk.h b/engines/mohawk/mohawk.h index f0618f7374..2f0e570d56 100644 --- a/engines/mohawk/mohawk.h +++ b/engines/mohawk/mohawk.h @@ -77,7 +77,7 @@ enum MohawkGameFeatures { struct MohawkGameDescription; class Sound; class PauseDialog; -class MohawkArchive; +class Archive; class CursorManager; class MohawkEngine : public ::Engine { @@ -123,7 +123,7 @@ private: protected: // An array holding the main Mohawk archives require by the games - Common::Array _mhk; + Common::Array _mhk; }; } // End of namespace Mohawk diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp index b60f8bd1ee..6bdf163a91 100644 --- a/engines/mohawk/myst.cpp +++ b/engines/mohawk/myst.cpp @@ -281,7 +281,7 @@ Common::Error MohawkEngine_Myst::run() { // Load Help System (Masterpiece Edition Only) if (getFeatures() & GF_ME) { MohawkArchive *mhk = new MohawkArchive(); - if (!mhk->open("help.dat")) + if (!mhk->openFile("help.dat")) error("Could not load help.dat"); _mhk.push_back(mhk); } @@ -488,7 +488,7 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS _mhk[0] = new MohawkArchive(); } - if (!_mhk[0]->open(mystFiles[_curStack])) + if (!_mhk[0]->openFile(mystFiles[_curStack])) error("Could not open %s", mystFiles[_curStack]); if (getPlatform() == Common::kPlatformMacintosh) diff --git a/engines/mohawk/resource.cpp b/engines/mohawk/resource.cpp index 37fc73de67..2dc6d2252a 100644 --- a/engines/mohawk/resource.cpp +++ b/engines/mohawk/resource.cpp @@ -29,23 +29,25 @@ namespace Mohawk { -MohawkArchive::MohawkArchive() { - _mhk = NULL; - _types = NULL; - _fileTable = NULL; +// Base Archive code + +Archive::Archive() { + _stream = 0; +} + +Archive::~Archive() { + close(); } -bool MohawkArchive::open(const Common::String &filename) { +bool Archive::openFile(const Common::String &fileName) { Common::File *file = new Common::File(); - if (!file->open(filename)) { + if (!file->open(fileName)) { delete file; return false; } - _curFile = filename; - - if (!open(file)) { + if (!openStream(file)) { close(); return false; } @@ -53,431 +55,349 @@ bool MohawkArchive::open(const Common::String &filename) { return true; } -void MohawkArchive::close() { - delete _mhk; _mhk = NULL; - delete[] _types; _types = NULL; - delete[] _fileTable; _fileTable = NULL; - - _curFile.clear(); +void Archive::close() { + _types.clear(); + delete _stream; _stream = 0; } -bool MohawkArchive::open(Common::SeekableReadStream *stream) { - // Make sure no other file is open... - close(); - _mhk = stream; - - if (_mhk->readUint32BE() != ID_MHWK) { - warning("Could not find tag 'MHWK'"); - return false; - } - - /* uint32 fileSize = */ _mhk->readUint32BE(); - - if (_mhk->readUint32BE() != ID_RSRC) { - warning("Could not find tag \'RSRC\'"); +bool Archive::hasResource(uint32 tag, uint16 id) const { + if (!_types.contains(tag)) return false; - } - _rsrc.version = _mhk->readUint16BE(); + return _types[tag].contains(id); +} - if (_rsrc.version != 0x100) { - warning("Unsupported Mohawk resource version %d.%d", (_rsrc.version >> 8) & 0xff, _rsrc.version & 0xff); +bool Archive::hasResource(uint32 tag, const Common::String &resName) const { + if (!_types.contains(tag) || resName.empty()) return false; - } - _rsrc.compaction = _mhk->readUint16BE(); // Only used in creation, not in reading - _rsrc.filesize = _mhk->readUint32BE(); - _rsrc.abs_offset = _mhk->readUint32BE(); - _rsrc.file_table_offset = _mhk->readUint16BE(); - _rsrc.file_table_size = _mhk->readUint16BE(); + const ResourceMap &resMap = _types[tag]; - debug (3, "Absolute Offset = %08x", _rsrc.abs_offset); + for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++) + if (it->_value.name.equalsIgnoreCase(resName)) + return true; - ///////////////////////////////// - //Resource Dir - ///////////////////////////////// + return false; +} - // Type Table - _mhk->seek(_rsrc.abs_offset); - _typeTable.name_offset = _mhk->readUint16BE(); - _typeTable.resource_types = _mhk->readUint16BE(); +Common::SeekableReadStream *Archive::getResource(uint32 tag, uint16 id) { + if (!_types.contains(tag)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); - debug (0, "Name List Offset = %04x Number of Resource Types = %04x", _typeTable.name_offset, _typeTable.resource_types); + const ResourceMap &resMap = _types[tag]; - _types = new Type[_typeTable.resource_types]; + if (!resMap.contains(id)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); - for (uint16 i = 0; i < _typeTable.resource_types; i++) { - _types[i].tag = _mhk->readUint32BE(); - _types[i].resource_table_offset = _mhk->readUint16BE(); - _types[i].name_table_offset = _mhk->readUint16BE(); + const Resource &res = resMap[id]; - // HACK: Zoombini's SND resource starts will a NULL. - if (_types[i].tag == ID_SND) - debug (3, "Type[%02d]: Tag = \'SND\' ResTable Offset = %04x NameTable Offset = %04x", i, _types[i].resource_table_offset, _types[i].name_table_offset); - else - debug (3, "Type[%02d]: Tag = \'%s\' ResTable Offset = %04x NameTable Offset = %04x", i, tag2str(_types[i].tag), _types[i].resource_table_offset, _types[i].name_table_offset); + return new Common::SeekableSubReadStream(_stream, res.offset, res.offset + res.size); +} - // Resource Table - _mhk->seek(_rsrc.abs_offset + _types[i].resource_table_offset); - _types[i].resTable.resources = _mhk->readUint16BE(); +uint32 Archive::getOffset(uint32 tag, uint16 id) const { + if (!_types.contains(tag)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); - debug (3, "Resources = %04x", _types[i].resTable.resources); + const ResourceMap &resMap = _types[tag]; - _types[i].resTable.entries = new Type::ResourceTable::Entries[_types[i].resTable.resources]; + if (!resMap.contains(id)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); - for (uint16 j = 0; j < _types[i].resTable.resources; j++) { - _types[i].resTable.entries[j].id = _mhk->readUint16BE(); - _types[i].resTable.entries[j].index = _mhk->readUint16BE(); + return resMap[id].offset; +} - debug (4, "Entry[%02x]: ID = %04x (%d) Index = %04x", j, _types[i].resTable.entries[j].id, _types[i].resTable.entries[j].id, _types[i].resTable.entries[j].index); - } +uint16 Archive::findResourceID(uint32 tag, const Common::String &resName) const { + if (!_types.contains(tag) || resName.empty()) + return 0xFFFF; - // Name Table - _mhk->seek(_rsrc.abs_offset + _types[i].name_table_offset); - _types[i].nameTable.num = _mhk->readUint16BE(); + const ResourceMap &resMap = _types[tag]; - debug (3, "Names = %04x", _types[i].nameTable.num); + for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++) + if (it->_value.name.equalsIgnoreCase(resName)) + return it->_key; - _types[i].nameTable.entries = new Type::NameTable::Entries[_types[i].nameTable.num]; + return 0xFFFF; +} - for (uint16 j = 0; j < _types[i].nameTable.num; j++) { - _types[i].nameTable.entries[j].offset = _mhk->readUint16BE(); - _types[i].nameTable.entries[j].index = _mhk->readUint16BE(); +Common::String Archive::getName(uint32 tag, uint16 id) const { + if (!_types.contains(tag)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); - debug (4, "Entry[%02x]: Name List Offset = %04x Index = %04x", j, _types[i].nameTable.entries[j].offset, _types[i].nameTable.entries[j].index); + const ResourceMap &resMap = _types[tag]; - // Name List - uint32 pos = _mhk->pos(); - _mhk->seek(_rsrc.abs_offset + _typeTable.name_offset + _types[i].nameTable.entries[j].offset); - char c = (char)_mhk->readByte(); - while (c != 0) { - _types[i].nameTable.entries[j].name += c; - c = (char)_mhk->readByte(); - } + if (!resMap.contains(id)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); - debug (3, "Name = \'%s\'", _types[i].nameTable.entries[j].name.c_str()); - - // Get back to next entry - _mhk->seek(pos); - } + return resMap[id].name; +} - // Return to next TypeTable entry - _mhk->seek(_rsrc.abs_offset + (i + 1) * 8 + 4); - debug (3, "\n"); - } +// Mohawk Archive code - _mhk->seek(_rsrc.abs_offset + _rsrc.file_table_offset); - _fileTableAmount = _mhk->readUint32BE(); - _fileTable = new FileTable[_fileTableAmount]; +struct FileTableEntry { + uint32 offset; + uint32 size; + byte flags; + uint16 unknown; +}; - for (uint32 i = 0; i < _fileTableAmount; i++) { - _fileTable[i].offset = _mhk->readUint32BE(); - _fileTable[i].dataSize = _mhk->readUint16BE(); - _fileTable[i].dataSize += _mhk->readByte() << 16; // Get bits 15-24 of dataSize too - _fileTable[i].flags = _mhk->readByte(); - _fileTable[i].unk = _mhk->readUint16BE(); +struct NameTableEntry { + uint16 index; + Common::String name; +}; - // Add in another 3 bits for file size from the flags. - // The flags are useless to us except for doing this ;) - _fileTable[i].dataSize += (_fileTable[i].flags & 7) << 24; +bool MohawkArchive::openStream(Common::SeekableReadStream *stream) { + // Make sure no other file is open... + close(); - debug (4, "File[%02x]: Offset = %08x DataSize = %07x Flags = %02x Unk = %04x", i, _fileTable[i].offset, _fileTable[i].dataSize, _fileTable[i].flags, _fileTable[i].unk); + if (stream->readUint32BE() != ID_MHWK) { + warning("Could not find tag 'MHWK'"); + return false; } - return true; -} - -int MohawkArchive::getTypeIndex(uint32 tag) { - for (uint16 i = 0; i < _typeTable.resource_types; i++) - if (_types[i].tag == tag) - return i; - return -1; // not found -} - -int MohawkArchive::getIDIndex(int typeIndex, uint16 id) { - for (uint16 i = 0; i < _types[typeIndex].resTable.resources; i++) - if (_types[typeIndex].resTable.entries[i].id == id) - return i; - return -1; // not found -} - -int MohawkArchive::getIDIndex(int typeIndex, const Common::String &resName) { - int index = -1; + /* uint32 fileSize = */ stream->readUint32BE(); - for (uint16 i = 0; i < _types[typeIndex].nameTable.num; i++) - if (_types[typeIndex].nameTable.entries[i].name.matchString(resName)) { - index = _types[typeIndex].nameTable.entries[i].index; - break; - } - - if (index < 0) - return -1; // Not found + if (stream->readUint32BE() != ID_RSRC) { + warning("Could not find tag \'RSRC\'"); + return false; + } - for (uint16 i = 0; i < _types[typeIndex].resTable.resources; i++) - if (_types[typeIndex].resTable.entries[i].index == index) - return i; + uint16 version = stream->readUint16BE(); - return -1; // Not found -} + if (version != 0x100) { + warning("Unsupported Mohawk resource version %d.%d", (version >> 8) & 0xff, version & 0xff); + return false; + } -uint16 MohawkArchive::findResourceID(uint32 type, const Common::String &resName) { - int typeIndex = getTypeIndex(type); + /* uint16 compaction = */ stream->readUint16BE(); // Only used in creation, not in reading + /* uint32 rsrcSize = */ stream->readUint32BE(); + uint32 absOffset = stream->readUint32BE(); + uint16 fileTableOffset = stream->readUint16BE(); + /* uint16 fileTableSize = */ stream->readUint16BE(); - if (typeIndex < 0) - return 0xFFFF; + // First, read in the file table + stream->seek(absOffset + fileTableOffset); + Common::Array fileTable; + fileTable.resize(stream->readUint32BE()); - int idIndex = getIDIndex(typeIndex, resName); + debug(4, "Reading file table with %d entries", fileTable.size()); - if (idIndex < 0) - return 0xFFFF; + for (uint32 i = 0; i < fileTable.size(); i++) { + fileTable[i].offset = stream->readUint32BE(); + fileTable[i].size = stream->readUint16BE(); + fileTable[i].size += stream->readByte() << 16; // Get bits 15-24 of size too + fileTable[i].flags = stream->readByte(); + fileTable[i].unknown = stream->readUint16BE(); - return _types[typeIndex].resTable.entries[idIndex].id; -} - -bool MohawkArchive::hasResource(uint32 tag, uint16 id) { - if (!_mhk) - return false; + // Add in another 3 bits for file size from the flags. + // The flags are useless to us except for doing this ;) + fileTable[i].size += (fileTable[i].flags & 7) << 24; - int16 typeIndex = getTypeIndex(tag); + debug(4, "File[%02x]: Offset = %08x Size = %07x Flags = %02x Unknown = %04x", i, fileTable[i].offset, fileTable[i].size, fileTable[i].flags, fileTable[i].unknown); + } - if (typeIndex < 0) - return false; + // Now go in an read in each of the types + stream->seek(absOffset); + uint16 stringTableOffset = stream->readUint16BE(); + uint16 typeCount = stream->readUint16BE(); - return getIDIndex(typeIndex, id) >= 0; -} + debug(0, "Name List Offset = %04x Number of Resource Types = %04x", stringTableOffset, typeCount); -bool MohawkArchive::hasResource(uint32 tag, const Common::String &resName) { - if (!_mhk) - return false; + for (uint16 i = 0; i < typeCount; i++) { + uint32 tag = stream->readUint32BE(); + uint16 resourceTableOffset = stream->readUint16BE(); + uint16 nameTableOffset = stream->readUint16BE(); - int16 typeIndex = getTypeIndex(tag); + // HACK: Zoombini's SND resource starts will a NULL. + if (tag == ID_SND) + debug(3, "Type[%02d]: Tag = \'SND\' ResTable Offset = %04x NameTable Offset = %04x", i, resourceTableOffset, nameTableOffset); + else + debug(3, "Type[%02d]: Tag = \'%s\' ResTable Offset = %04x NameTable Offset = %04x", i, tag2str(tag), resourceTableOffset, nameTableOffset); - if (typeIndex < 0) - return false; + // Name Table + stream->seek(absOffset + nameTableOffset); + Common::Array nameTable; + nameTable.resize(stream->readUint16BE()); - return getIDIndex(typeIndex, resName) >= 0; -} + debug(3, "Names = %04x", nameTable.size()); -Common::String MohawkArchive::getName(uint32 tag, uint16 id) { - if (!_mhk) - return 0; + for (uint16 j = 0; j < nameTable.size(); j++) { + uint16 offset = stream->readUint16BE(); + nameTable[j].index = stream->readUint16BE(); - int16 typeIndex = getTypeIndex(tag); + debug(4, "Entry[%02x]: Name List Offset = %04x Index = %04x", j, offset, nameTable[j].index); - if (typeIndex < 0) - return 0; + // Name List + uint32 pos = stream->pos(); + stream->seek(absOffset + stringTableOffset + offset); + char c = (char)stream->readByte(); + while (c != 0) { + nameTable[j].name += c; + c = (char)stream->readByte(); + } - int16 idIndex = -1; + debug(3, "Name = \'%s\'", nameTable[j].name.c_str()); - for (uint16 i = 0; i < _types[typeIndex].resTable.resources; i++) - if (_types[typeIndex].resTable.entries[i].id == id) { - idIndex = _types[typeIndex].resTable.entries[i].index; - break; + // Get back to next entry + stream->seek(pos); } - assert(idIndex >= 0); - - for (uint16 i = 0; i < _types[typeIndex].nameTable.num; i++) - if (_types[typeIndex].nameTable.entries[i].index == idIndex) - return _types[typeIndex].nameTable.entries[i].name; - - return 0; // not found -} - -uint32 MohawkArchive::getOffset(uint32 tag, uint16 id) { - assert(_mhk); + // Resource Table + stream->seek(absOffset + resourceTableOffset); + uint16 resourceCount = stream->readUint16BE(); - int16 typeIndex = getTypeIndex(tag); - assert(typeIndex >= 0); + debug(3, "Resource count = %04x", resourceCount); - int16 idIndex = getIDIndex(typeIndex, id); - assert(idIndex >= 0); + ResourceMap &resMap = _types[tag]; - return _fileTable[_types[typeIndex].resTable.entries[idIndex].index - 1].offset; -} + for (uint16 j = 0; j < resourceCount; j++) { + uint16 id = stream->readUint16BE(); + uint16 index = stream->readUint16BE(); -Common::SeekableReadStream *MohawkArchive::getResource(uint32 tag, uint16 id) { - if (!_mhk) - error("MohawkArchive::getResource(): No File in Use"); + Resource &res = resMap[id]; - int16 typeIndex = getTypeIndex(tag); - - if (typeIndex < 0) - error("Could not find a tag of '%s' in file '%s'", tag2str(tag), _curFile.c_str()); + // Pull out the name from the name table + for (uint32 k = 0; k < nameTable.size(); k++) { + if (nameTable[k].index == index) { + res.name = nameTable[k].name; + break; + } + } - int16 idIndex = getIDIndex(typeIndex, id); + // Pull out our offset/size too + res.offset = fileTable[index - 1].offset; + + // WORKAROUND: tMOV resources pretty much ignore the size part of the file table, + // as the original just passed the full Mohawk file to QuickTime and the offset. + // We need to do this because of the way Mohawk is set up (this is much more "proper" + // than passing _stream at the right offset). We may want to do that in the future, though. + if (tag == ID_TMOV) { + if (index == fileTable.size() - 1) + res.size = stream->size() - fileTable[index - 1].offset; + else + res.size = fileTable[index].offset - fileTable[index - 1].offset; + } else + res.size = fileTable[index - 1].size; + + debug(4, "Entry[%02x]: ID = %04x (%d) Index = %04x", j, id, id, index); + } - if (idIndex < 0) - error("Could not find '%s' %04x in file '%s'", tag2str(tag), id, _curFile.c_str()); - // Note: the fileTableIndex is based off 1, not 0. So, subtract 1 - uint16 fileTableIndex = _types[typeIndex].resTable.entries[idIndex].index - 1; + // Return to next TypeTable entry + stream->seek(absOffset + (i + 1) * 8 + 4); - // WORKAROUND: tMOV resources pretty much ignore the size part of the file table, - // as the original just passed the full Mohawk file to QuickTime and the offset. - // We need to do this because of the way Mohawk is set up (this is much more "proper" - // than passing _mhk at the right offset). We may want to do that in the future, though. - if (_types[typeIndex].tag == ID_TMOV) { - if (fileTableIndex == _fileTableAmount - 1) - return new Common::SeekableSubReadStream(_mhk, _fileTable[fileTableIndex].offset, _mhk->size()); - else - return new Common::SeekableSubReadStream(_mhk, _fileTable[fileTableIndex].offset, _fileTable[fileTableIndex + 1].offset); + debug(3, "\n"); } - return new Common::SeekableSubReadStream(_mhk, _fileTable[fileTableIndex].offset, _fileTable[fileTableIndex].offset + _fileTable[fileTableIndex].dataSize); + _stream = stream; + return true; } -bool LivingBooksArchive_v1::open(Common::SeekableReadStream *stream) { +// Living Books Archive code + +bool LivingBooksArchive_v1::openStream(Common::SeekableReadStream *stream) { close(); - _mhk = stream; // This is for the "old" Mohawk resource format used in some older // Living Books. It is very similar, just missing the MHWK tag and // some other minor differences, especially with the file table // being merged into the resource table. - uint32 headerSize = _mhk->readUint32BE(); + uint32 headerSize = stream->readUint32BE(); // NOTE: There are differences besides endianness! (Subtle changes, // but different). if (headerSize == 6) { // We're in Big Endian mode (Macintosh) - _mhk->readUint16BE(); // Resource Table Size - _typeTable.resource_types = _mhk->readUint16BE(); - _types = new OldType[_typeTable.resource_types]; + stream->readUint16BE(); // Resource Table Size + uint16 typeCount = stream->readUint16BE(); - debug (0, "Old Mohawk File (Macintosh): Number of Resource Types = %04x", _typeTable.resource_types); + debug(0, "Old Mohawk File (Macintosh): Number of Resource Types = %04x", typeCount); - for (uint16 i = 0; i < _typeTable.resource_types; i++) { - _types[i].tag = _mhk->readUint32BE(); - _types[i].resource_table_offset = (uint16)_mhk->readUint32BE() + 6; - _mhk->readUint32BE(); // Unknown (always 0?) + for (uint16 i = 0; i < typeCount; i++) { + uint32 tag = stream->readUint32BE(); + uint32 resourceTableOffset = stream->readUint32BE() + 6; + stream->readUint32BE(); // Unknown (always 0?) - debug (3, "Type[%02d]: Tag = \'%s\' ResTable Offset = %04x", i, tag2str(_types[i].tag), _types[i].resource_table_offset); + debug(3, "Type[%02d]: Tag = \'%s\' ResTable Offset = %04x", i, tag2str(tag), resourceTableOffset); - uint32 oldPos = _mhk->pos(); + uint32 oldPos = stream->pos(); // Resource Table/File Table - _mhk->seek(_types[i].resource_table_offset); - _types[i].resTable.resources = _mhk->readUint16BE(); - _types[i].resTable.entries = new OldType::ResourceTable::Entries[_types[i].resTable.resources]; - - for (uint16 j = 0; j < _types[i].resTable.resources; j++) { - _types[i].resTable.entries[j].id = _mhk->readUint16BE(); - _types[i].resTable.entries[j].offset = _mhk->readUint32BE(); - _types[i].resTable.entries[j].size = _mhk->readByte() << 16; - _types[i].resTable.entries[j].size += _mhk->readUint16BE(); - _mhk->skip(5); // Unknown (always 0?) - - debug (4, "Entry[%02x]: ID = %04x (%d)\tOffset = %08x, Size = %08x", j, _types[i].resTable.entries[j].id, _types[i].resTable.entries[j].id, _types[i].resTable.entries[j].offset, _types[i].resTable.entries[j].size); - } - - _mhk->seek(oldPos); - debug (3, "\n"); - } - } else if (SWAP_BYTES_32(headerSize) == 6) { // We're in Little Endian mode (Windows) - _mhk->readUint16LE(); // Resource Table Size - _typeTable.resource_types = _mhk->readUint16LE(); - _types = new OldType[_typeTable.resource_types]; + stream->seek(resourceTableOffset); + uint16 resourceCount = stream->readUint16BE(); - debug (0, "Old Mohawk File (Windows): Number of Resource Types = %04x", _typeTable.resource_types); + ResourceMap &resMap = _types[tag]; - for (uint16 i = 0; i < _typeTable.resource_types; i++) { - _types[i].tag = _mhk->readUint32LE(); - _types[i].resource_table_offset = _mhk->readUint16LE() + 6; - _mhk->readUint16LE(); // Unknown (always 0?) + for (uint16 j = 0; j < resourceCount; j++) { + uint16 id = stream->readUint16BE(); - debug (3, "Type[%02d]: Tag = \'%s\' ResTable Offset = %04x", i, tag2str(_types[i].tag), _types[i].resource_table_offset); + Resource &res = resMap[id]; - uint32 oldPos = _mhk->pos(); + res.offset = stream->readUint32BE(); + res.size = stream->readByte() << 16; + res.size |= stream->readUint16BE(); + stream->skip(5); // Unknown (always 0?) - // Resource Table/File Table - _mhk->seek(_types[i].resource_table_offset); - _types[i].resTable.resources = _mhk->readUint16LE(); - _types[i].resTable.entries = new OldType::ResourceTable::Entries[_types[i].resTable.resources]; - - for (uint16 j = 0; j < _types[i].resTable.resources; j++) { - _types[i].resTable.entries[j].id = _mhk->readUint16LE(); - _types[i].resTable.entries[j].offset = _mhk->readUint32LE(); - _types[i].resTable.entries[j].size = _mhk->readUint32LE(); - _mhk->readUint16LE(); // Unknown (always 0?) - - debug (4, "Entry[%02x]: ID = %04x (%d)\tOffset = %08x, Size = %08x", j, _types[i].resTable.entries[j].id, _types[i].resTable.entries[j].id, _types[i].resTable.entries[j].offset, _types[i].resTable.entries[j].size); + debug(4, "Entry[%02x]: ID = %04x (%d)\tOffset = %08x, Size = %08x", j, id, id, res.offset, res.size); } - _mhk->seek(oldPos); - debug (3, "\n"); + stream->seek(oldPos); + debug(3, "\n"); } - } else { - warning("Could not determine type of Old Mohawk resource"); - return false; - } - - return true; -} - -uint32 LivingBooksArchive_v1::getOffset(uint32 tag, uint16 id) { - assert(_mhk); - - int16 typeIndex = getTypeIndex(tag); - assert(typeIndex >= 0); + } else if (SWAP_BYTES_32(headerSize) == 6) { // We're in Little Endian mode (Windows) + stream->readUint16LE(); // Resource Table Size + uint16 typeCount = stream->readUint16LE(); - int16 idIndex = getIDIndex(typeIndex, id); - assert(idIndex >= 0); + debug(0, "Old Mohawk File (Windows): Number of Resource Types = %04x", typeCount); - return _types[typeIndex].resTable.entries[idIndex].offset; -} + for (uint16 i = 0; i < typeCount; i++) { + uint32 tag = stream->readUint32LE(); + uint16 resourceTableOffset = stream->readUint16LE() + 6; + stream->readUint16LE(); // Unknown (always 0?) -Common::SeekableReadStream *LivingBooksArchive_v1::getResource(uint32 tag, uint16 id) { - if (!_mhk) - error("LivingBooksArchive_v1::getResource(): No File in Use"); + debug(3, "Type[%02d]: Tag = \'%s\' ResTable Offset = %04x", i, tag2str(tag), resourceTableOffset); - int16 typeIndex = getTypeIndex(tag); + uint32 oldPos = stream->pos(); - if (typeIndex < 0) - error("Could not find a tag of \'%s\' in file \'%s\'", tag2str(tag), _curFile.c_str()); + // Resource Table/File Table + stream->seek(resourceTableOffset); + uint16 resourceCount = stream->readUint16LE(); - int16 idIndex = getIDIndex(typeIndex, id); + ResourceMap &resMap = _types[tag]; - if (idIndex < 0) - error("Could not find \'%s\' %04x in file \'%s\'", tag2str(tag), id, _curFile.c_str()); + for (uint16 j = 0; j < resourceCount; j++) { + uint16 id = stream->readUint16LE(); - return new Common::SeekableSubReadStream(_mhk, _types[typeIndex].resTable.entries[idIndex].offset, _types[typeIndex].resTable.entries[idIndex].offset + _types[typeIndex].resTable.entries[idIndex].size); -} + Resource &res = resMap[id]; -bool LivingBooksArchive_v1::hasResource(uint32 tag, uint16 id) { - if (!_mhk) - return false; + res.offset = stream->readUint32LE(); + res.size = stream->readUint32LE(); + stream->readUint16LE(); // Unknown (always 0?) - int16 typeIndex = getTypeIndex(tag); + debug(4, "Entry[%02x]: ID = %04x (%d)\tOffset = %08x, Size = %08x", j, id, id, res.offset, res.size); + } - if (typeIndex < 0) + stream->seek(oldPos); + debug(3, "\n"); + } + } else { + // Not a valid Living Books Archive return false; + } - return getIDIndex(typeIndex, id) >= 0; + _stream = stream; + return true; } -int LivingBooksArchive_v1::getTypeIndex(uint32 tag) { - for (uint16 i = 0; i < _typeTable.resource_types; i++) - if (_types[i].tag == tag) - return i; - return -1; // not found -} - -int LivingBooksArchive_v1::getIDIndex(int typeIndex, uint16 id) { - for (uint16 i = 0; i < _types[typeIndex].resTable.resources; i++) - if (_types[typeIndex].resTable.entries[i].id == id) - return i; - return -1; // not found -} +// DOS Archive (v2) code // Partially based on the Prince of Persia Format Specifications // See http://sdfg.com.ar/git/?p=fp-git.git;a=blob;f=FP/doc/FormatSpecifications // However, I'm keeping with the terminology we've been using with the // later archive formats. -bool DOSArchive_v2::open(Common::SeekableReadStream *stream) { +bool DOSArchive_v2::openStream(Common::SeekableReadStream *stream) { close(); uint32 typeTableOffset = stream->readUint32LE(); @@ -488,36 +408,38 @@ bool DOSArchive_v2::open(Common::SeekableReadStream *stream) { stream->seek(typeTableOffset); - _typeTable.resource_types = stream->readUint16LE(); - _types = new OldType[_typeTable.resource_types]; + uint16 typeCount = stream->readUint16LE(); - for (uint16 i = 0; i < _typeTable.resource_types; i++) { - _types[i].tag = stream->readUint32LE(); - _types[i].resource_table_offset = stream->readUint16LE(); + for (uint16 i = 0; i < typeCount; i++) { + uint32 tag = stream->readUint32LE(); + uint16 resourceTableOffset = stream->readUint16LE(); - debug(3, "Type[%02d]: Tag = \'%s\' ResTable Offset = %04x", i, tag2str(_types[i].tag), _types[i].resource_table_offset); + debug(3, "Type[%02d]: Tag = \'%s\' ResTable Offset = %04x", i, tag2str(tag), resourceTableOffset); uint32 oldPos = stream->pos(); // Resource Table/File Table - stream->seek(_types[i].resource_table_offset + typeTableOffset); - _types[i].resTable.resources = stream->readUint16LE(); - _types[i].resTable.entries = new OldType::ResourceTable::Entries[_types[i].resTable.resources]; + stream->seek(resourceTableOffset + typeTableOffset); + uint16 resourceCount = stream->readUint16LE(); + + ResourceMap &resMap = _types[tag]; + + for (uint16 j = 0; j < resourceCount; j++) { + uint16 id = stream->readUint16LE(); - for (uint16 j = 0; j < _types[i].resTable.resources; j++) { - _types[i].resTable.entries[j].id = stream->readUint16LE(); - _types[i].resTable.entries[j].offset = stream->readUint32LE() + 1; // Need to add one to the offset to skip the checksum byte - _types[i].resTable.entries[j].size = stream->readUint16LE(); - stream->skip(3); // Skip the useless flags + Resource &res = resMap[id]; + res.offset = stream->readUint32LE() + 1; // Need to add one to the offset to skip the checksum byte + res.size = stream->readUint16LE(); + stream->skip(3); // Skip (useless) flags - debug (4, "Entry[%02x]: ID = %04x (%d)\tOffset = %08x, Size = %08x", j, _types[i].resTable.entries[j].id, _types[i].resTable.entries[j].id, _types[i].resTable.entries[j].offset, _types[i].resTable.entries[j].size); + debug(4, "Entry[%02x]: ID = %04x (%d)\tOffset = %08x, Size = %08x", j, id, id, res.offset, res.size); } stream->seek(oldPos); - debug (3, "\n"); + debug(3, "\n"); } - _mhk = stream; + _stream = stream; return true; } diff --git a/engines/mohawk/resource.h b/engines/mohawk/resource.h index cabce04e38..d4e91e0ec4 100644 --- a/engines/mohawk/resource.h +++ b/engines/mohawk/resource.h @@ -22,6 +22,7 @@ #include "common/scummsys.h" #include "common/endian.h" +#include "common/hashmap.h" #include "common/file.h" #include "common/str.h" @@ -128,127 +129,61 @@ namespace Mohawk { #define ID_BBOX MKTAG('B','B','O','X') // Boxes? (CSWorld, CSAmtrak) #define ID_SYSX MKTAG('S','Y','S','X') // MIDI Sysex -struct FileTable { - uint32 offset; - uint32 dataSize; // Really 27 bits - byte flags; // Mostly useless except for the bottom 3 bits which are part of the size - uint16 unk; // Always 0 -}; - -struct Type { - Type() { resTable.entries = NULL; nameTable.entries = NULL; } - ~Type() { delete[] resTable.entries; delete[] nameTable.entries; } - - //Type Table - uint32 tag; - uint16 resource_table_offset; - uint16 name_table_offset; - - struct ResourceTable { - uint16 resources; - struct Entries { - uint16 id; - uint16 index; - } *entries; - } resTable; - - struct NameTable { - uint16 num; - struct Entries { - uint16 offset; - uint16 index; - // Name List - Common::String name; - } *entries; - } nameTable; -}; - -struct TypeTable { - uint16 name_offset; - uint16 resource_types; -}; -struct RSRC_Header { - uint16 version; - uint16 compaction; - uint32 filesize; - uint32 abs_offset; - uint16 file_table_offset; - uint16 file_table_size; -}; - -class MohawkArchive { +class Archive { public: - MohawkArchive(); - virtual ~MohawkArchive() { close(); } + Archive(); + virtual ~Archive(); - bool open(const Common::String &filename); - virtual bool open(Common::SeekableReadStream *stream); + bool openFile(const Common::String &fileName); + virtual bool openStream(Common::SeekableReadStream *stream) = 0; void close(); - virtual bool hasResource(uint32 tag, uint16 id); - virtual bool hasResource(uint32 tag, const Common::String &resName); - virtual Common::SeekableReadStream *getResource(uint32 tag, uint16 id); - virtual uint32 getOffset(uint32 tag, uint16 id); - virtual uint16 findResourceID(uint32 type, const Common::String &resName); - Common::String getName(uint32 tag, uint16 id); + bool isOpen() const { return _stream != 0; } + + bool hasResource(uint32 tag, uint16 id) const; + bool hasResource(uint32 tag, const Common::String &resName) const; + Common::SeekableReadStream *getResource(uint32 tag, uint16 id); + uint32 getOffset(uint32 tag, uint16 id) const; + uint16 findResourceID(uint32 tag, const Common::String &resName) const; + Common::String getName(uint32 tag, uint16 id) const; protected: - Common::SeekableReadStream *_mhk; - TypeTable _typeTable; - Common::String _curFile; - -private: - RSRC_Header _rsrc; - Type *_types; - FileTable *_fileTable; - uint16 _nameTableAmount; - uint16 _resourceTableAmount; - uint16 _fileTableAmount; - - int getTypeIndex(uint32 tag); - int getIDIndex(int typeIndex, uint16 id); - int getIDIndex(int typeIndex, const Common::String &resName); + Common::SeekableReadStream *_stream; + + struct Resource { + uint32 offset; + uint32 size; + Common::String name; + }; + + typedef Common::HashMap ResourceMap; + typedef Common::HashMap TypeMap; + TypeMap _types; }; -class LivingBooksArchive_v1 : public MohawkArchive { +class MohawkArchive : public Archive { public: - LivingBooksArchive_v1() : MohawkArchive() {} - ~LivingBooksArchive_v1() {} + MohawkArchive() : Archive() {} + ~MohawkArchive() {} - bool hasResource(uint32 tag, uint16 id); - bool hasResource(uint32 tag, const Common::String &resName) { return false; } - virtual bool open(Common::SeekableReadStream *stream); - Common::SeekableReadStream *getResource(uint32 tag, uint16 id); - Common::SeekableReadStream *getResource(uint32 tag, const Common::String &resName) { return 0; } - uint32 getOffset(uint32 tag, uint16 id); - uint16 findResourceID(uint32 type, const Common::String &resName) { return 0xFFFF; } + bool openStream(Common::SeekableReadStream *stream); +}; -protected: - struct OldType { - uint32 tag; - uint16 resource_table_offset; - struct ResourceTable { - uint16 resources; - struct Entries { - uint16 id; - uint32 offset; - uint32 size; - } *entries; - } resTable; - } *_types; - -private: - int getTypeIndex(uint32 tag); - int getIDIndex(int typeIndex, uint16 id); +class LivingBooksArchive_v1 : public Archive { +public: + LivingBooksArchive_v1() : Archive() {} + ~LivingBooksArchive_v1() {} + + bool openStream(Common::SeekableReadStream *stream); }; -class DOSArchive_v2 : public LivingBooksArchive_v1 { +class DOSArchive_v2 : public Archive { public: - DOSArchive_v2() : LivingBooksArchive_v1() {} + DOSArchive_v2() : Archive() {} ~DOSArchive_v2() {} - virtual bool open(Common::SeekableReadStream *stream); + bool openStream(Common::SeekableReadStream *stream); }; } // End of namespace Mohawk diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp index 8f8bc15990..612b8b3685 100644 --- a/engines/mohawk/riven.cpp +++ b/engines/mohawk/riven.cpp @@ -143,7 +143,7 @@ Common::Error MohawkEngine_Riven::run() { _extrasFile = new MohawkArchive(); // We need extras.mhk for inventory images, marble images, and credits images - if (!_extrasFile->open("extras.mhk")) { + if (!_extrasFile->openFile("extras.mhk")) { Common::String message = "You're missing 'extras.mhk'. Using the 'arcriven.z' installer file also works."; GUIErrorMessage(message); warning("%s", message.c_str()); @@ -317,7 +317,7 @@ void MohawkEngine_Riven::changeToStack(uint16 n) { Common::String filename = Common::String(prefix) + endings[i]; MohawkArchive *mhk = new MohawkArchive(); - if (mhk->open(filename)) + if (mhk->openFile(filename)) _mhk.push_back(mhk); else delete mhk; diff --git a/engines/mohawk/riven_saveload.cpp b/engines/mohawk/riven_saveload.cpp index 35d82d4aa0..f1ce2447a5 100644 --- a/engines/mohawk/riven_saveload.cpp +++ b/engines/mohawk/riven_saveload.cpp @@ -100,7 +100,7 @@ bool RivenSaveLoad::loadGame(Common::String filename) { MohawkArchive *mhk = new MohawkArchive(); - if (!mhk->open(loadFile)) { + if (!mhk->openStream(loadFile)) { warning("Save file is not a Mohawk archive"); delete mhk; return false; -- cgit v1.2.3