From de61313e949ad2fe8eb076493fd4990f54a69555 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Sun, 8 Mar 2015 15:34:48 -0400 Subject: [PATCH] DIRECTOR: Fix RIFX support to actually be useful --- engines/director/director.cpp | 11 ++-- engines/director/resource.cpp | 107 +++++++++++++++++++++++++++------- engines/director/resource.h | 27 +++------ 3 files changed, 101 insertions(+), 44 deletions(-) diff --git a/engines/director/director.cpp b/engines/director/director.cpp index 26644979848a..1ae6f5471356 100644 --- a/engines/director/director.cpp +++ b/engines/director/director.cpp @@ -163,11 +163,9 @@ void DirectorEngine::loadEXEv7(Common::SeekableReadStream *stream) { } void DirectorEngine::loadEXERIFX(Common::SeekableReadStream *stream, uint32 offset) { - stream->seek(offset); - _mainArchive = new RIFXArchive(); - if (!_mainArchive->openStream(stream)) + if (!_mainArchive->openStream(stream, offset)) error("Failed to load RIFX from EXE"); } @@ -191,16 +189,17 @@ void DirectorEngine::loadMac() { // First we need to detect PPC vs. 68k uint32 tag = dataFork->readUint32BE(); + uint32 startOffset; if (SWAP_BYTES_32(tag) == MKTAG('P', 'J', '9', '3') || tag == MKTAG('P', 'J', '9', '5') || tag == MKTAG('P', 'J', '0', '0')) { // PPC: The RIFX shares the data fork with the binary - dataFork->seek(dataFork->readUint32BE()); + startOffset = dataFork->readUint32BE(); } else { // 68k: The RIFX is the only thing in the data fork - dataFork->seek(0); + startOffset = 0; } - if (!_mainArchive->openStream(dataFork)) + if (!_mainArchive->openStream(dataFork, startOffset)) error("Failed to load RIFX from Mac binary"); } } diff --git a/engines/director/resource.cpp b/engines/director/resource.cpp index 2c0b47805395..39a92829589e 100644 --- a/engines/director/resource.cpp +++ b/engines/director/resource.cpp @@ -204,7 +204,7 @@ bool MacArchive::openFile(const Common::String &fileName) { return true; } -bool MacArchive::openStream(Common::SeekableReadStream *stream) { +bool MacArchive::openStream(Common::SeekableReadStream *stream, uint32 startOffset) { // TODO: Add support for this (v4 Windows games) return false; } @@ -216,9 +216,11 @@ Common::SeekableReadStream *MacArchive::getResource(uint32 tag, uint16 id) { // RIFF Archive code -bool RIFFArchive::openStream(Common::SeekableReadStream *stream) { +bool RIFFArchive::openStream(Common::SeekableReadStream *stream, uint32 startOffset) { close(); + stream->seek(startOffset); + if (convertTagToUppercase(stream->readUint32BE()) != MKTAG('R', 'I', 'F', 'F')) return false; @@ -235,7 +237,7 @@ bool RIFFArchive::openStream(Common::SeekableReadStream *stream) { stream->readUint32LE(); // unknown (always 0?) while ((uint32)stream->pos() < startPos + cftcSize) { - uint32 tag = stream->readUint32BE(); + uint32 tag = convertTagToUppercase(stream->readUint32BE()); uint32 size = stream->readUint32LE(); uint32 id = stream->readUint32LE(); uint32 offset = stream->readUint32LE(); @@ -288,27 +290,26 @@ Common::SeekableReadStream *RIFFArchive::getResource(uint32 tag, uint16 id) { // RIFX Archive code -bool RIFXArchive::openStream(Common::SeekableReadStream *stream) { +bool RIFXArchive::openStream(Common::SeekableReadStream *stream, uint32 startOffset) { close(); - uint32 startPos = stream->pos(); + stream->seek(startOffset); - uint32 tag = stream->readUint32BE(); - bool isBigEndian; + uint32 headerTag = stream->readUint32BE(); - if (tag == MKTAG('R', 'I', 'F', 'X')) - isBigEndian = true; - else if (SWAP_BYTES_32(tag) == MKTAG('R', 'I', 'F', 'X')) - isBigEndian = false; + if (headerTag == MKTAG('R', 'I', 'F', 'X')) + _isBigEndian = true; + else if (SWAP_BYTES_32(headerTag) == MKTAG('R', 'I', 'F', 'X')) + _isBigEndian = false; else return false; - Common::SeekableSubReadStreamEndian subStream(stream, startPos + 4, stream->size(), isBigEndian, DisposeAfterUse::NO); + Common::SeekableSubReadStreamEndian subStream(stream, startOffset + 4, stream->size(), _isBigEndian, DisposeAfterUse::NO); subStream.readUint32(); // size - uint32 type = subStream.readUint32(); - if (type != MKTAG('M', 'V', '9', '3') && type != MKTAG('A', 'P', 'P', 'L')) + uint32 rifxType = subStream.readUint32(); + if (rifxType != MKTAG('M', 'V', '9', '3') && rifxType != MKTAG('A', 'P', 'P', 'L')) return false; if (subStream.readUint32() != MKTAG('i', 'm', 'a', 'p')) @@ -316,7 +317,7 @@ bool RIFXArchive::openStream(Common::SeekableReadStream *stream) { subStream.readUint32(); // imap length subStream.readUint32(); // unknown - uint32 mmapOffset = subStream.readUint32() - startPos - 4; + uint32 mmapOffset = subStream.readUint32() - startOffset - 4; subStream.seek(mmapOffset); @@ -331,18 +332,84 @@ bool RIFXArchive::openStream(Common::SeekableReadStream *stream) { subStream.skip(8); // all 0xFF subStream.readUint32(); // unknown + Common::Array resources; + + // Need to look for these two resources + const Resource *keyRes = 0; + const Resource *casRes = 0; + for (uint32 i = 0; i < resCount; i++) { - tag = subStream.readUint32(); + uint32 tag = subStream.readUint32(); uint32 size = subStream.readUint32(); uint32 offset = subStream.readUint32(); - subStream.skip(8); // always 0? + /*uint16 flags = */ subStream.readUint16(); + /*uint16 unk1 = */ subStream.readUint16(); + /*uint32 unk2 = */ subStream.readUint32(); - debug(0, "Found RIFX resource %d: '%s', %d @ 0x%08x", i, tag2str(tag), size, offset); + debug(0, "Found RIFX resource index %d: '%s', %d @ 0x%08x", i, tag2str(tag), size, offset); - ResourceMap &resMap = _types[tag]; - Resource &res = resMap[i]; // FIXME: What is the ID? Is this setup even viable? + Resource res; res.offset = offset; res.size = size; + resources.push_back(res); + + // APPL is a special case; it has an embedded "normal" archive + if (rifxType == MKTAG('A', 'P', 'P', 'L') && tag == MKTAG('F', 'i', 'l', 'e')) + return openStream(stream, offset); + + // Looking for two types here + if (tag == MKTAG('K', 'E', 'Y', '*')) + keyRes = &resources[resources.size() - 1]; + else if (tag == MKTAG('C', 'A', 'S', '*')) + casRes = &resources[resources.size() - 1]; + } + + // We need to have found the 'File' resource already + if (rifxType == MKTAG('A', 'P', 'P', 'L')) { + warning("No 'File' resource present in APPL archive"); + return false; + } + + // A KEY* must be present + if (!keyRes) { + warning("No 'KEY*' resource present"); + return false; + } + + // Parse the CAS*, if present + Common::Array casEntries; + if (casRes) { + Common::SeekableSubReadStreamEndian casStream(stream, casRes->offset + 8, casRes->offset + 8 + casRes->size, _isBigEndian, DisposeAfterUse::NO); + casEntries.resize(casRes->size / 4); + for (uint32 i = 0; i < casEntries.size(); i++) + casEntries[i] = casStream.readUint32(); + } + + // Parse the KEY* + Common::SeekableSubReadStreamEndian keyStream(stream, keyRes->offset + 8, keyRes->offset + 8 + keyRes->size, _isBigEndian, DisposeAfterUse::NO); + /*uint16 unk1 = */ keyStream.readUint16(); + /*uint16 unk2 = */ keyStream.readUint16(); + /*uint32 unk3 = */ keyStream.readUint32(); + uint32 keyCount = keyStream.readUint32(); + + for (uint32 i = 0; i < keyCount; i++) { + uint32 index = keyStream.readUint32(); + uint32 id = keyStream.readUint32(); + uint32 resTag = keyStream.readUint32(); + + // Handle CAS*/CASt nonsense + if (resTag == MKTAG('C', 'A', 'S', 't')) { + for (uint32 j = 0; j < casEntries.size(); j++) { + if (casEntries[j] == index) { + id += j + 1; + break; + } + } + } + + const Resource &res = resources[index]; + debug(0, "Found RIFX resource: '%s' 0x%04x, %d @ 0x%08x", tag2str(resTag), id, res.size, res.offset); + _types[resTag][id] = res; } _stream = stream; diff --git a/engines/director/resource.h b/engines/director/resource.h index 0068912a2398..93b1bd9d1d30 100644 --- a/engines/director/resource.h +++ b/engines/director/resource.h @@ -44,7 +44,7 @@ class Archive { virtual ~Archive(); virtual bool openFile(const Common::String &fileName); - virtual bool openStream(Common::SeekableReadStream *stream) = 0; + virtual bool openStream(Common::SeekableReadStream *stream, uint32 offset = 0) = 0; virtual void close(); bool isOpen() const { return _stream != 0; } @@ -70,20 +70,8 @@ class Archive { Common::String name; }; - // Have separate hash/equals functions for tags to make them - // case-insensitive. - struct HashTag : public Common::UnaryFunction { // Insert Twitter joke - uint operator()(uint32 val) const { return (uint)Archive::convertTagToUppercase(val); } - }; - - struct EqualsTag : public Common::BinaryFunction { - bool operator()(const uint32 &val1, const uint32 &val2) const { - return Archive::convertTagToUppercase(val1) == Archive::convertTagToUppercase(val2); - } - }; - typedef Common::HashMap ResourceMap; - typedef Common::HashMap TypeMap; + typedef Common::HashMap TypeMap; TypeMap _types; }; @@ -94,7 +82,7 @@ class MacArchive : public Archive { void close(); bool openFile(const Common::String &fileName); - bool openStream(Common::SeekableReadStream *stream); + bool openStream(Common::SeekableReadStream *stream, uint32 startOffset = 0); Common::SeekableReadStream *getResource(uint32 tag, uint16 id); private: @@ -106,16 +94,19 @@ class RIFFArchive : public Archive { RIFFArchive() : Archive() {} ~RIFFArchive() {} - bool openStream(Common::SeekableReadStream *stream); + bool openStream(Common::SeekableReadStream *stream, uint32 startOffset = 0); Common::SeekableReadStream *getResource(uint32 tag, uint16 id); }; class RIFXArchive : public Archive { public: - RIFXArchive() : Archive() {} + RIFXArchive() : Archive(), _isBigEndian(true) {} ~RIFXArchive() {} - bool openStream(Common::SeekableReadStream *stream); + bool openStream(Common::SeekableReadStream *stream, uint32 startOffset = 0); + +private: + bool _isBigEndian; }; } // End of namespace Director