Skip to content

Commit

Permalink
DIRECTOR: Fix RIFX support to actually be useful
Browse files Browse the repository at this point in the history
  • Loading branch information
Matthew Hoops authored and sev- committed Aug 3, 2016
1 parent a984931 commit de61313
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 44 deletions.
11 changes: 5 additions & 6 deletions engines/director/director.cpp
Expand Up @@ -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");
}

Expand All @@ -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");
}
}
Expand Down
107 changes: 87 additions & 20 deletions engines/director/resource.cpp
Expand Up @@ -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;
}
Expand All @@ -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;

Expand All @@ -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();
Expand Down Expand Up @@ -288,35 +290,34 @@ 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'))
return false;

subStream.readUint32(); // imap length
subStream.readUint32(); // unknown
uint32 mmapOffset = subStream.readUint32() - startPos - 4;
uint32 mmapOffset = subStream.readUint32() - startOffset - 4;

subStream.seek(mmapOffset);

Expand All @@ -331,18 +332,84 @@ bool RIFXArchive::openStream(Common::SeekableReadStream *stream) {
subStream.skip(8); // all 0xFF
subStream.readUint32(); // unknown

Common::Array<Resource> 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<uint32> 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;
Expand Down
27 changes: 9 additions & 18 deletions engines/director/resource.h
Expand Up @@ -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; }
Expand All @@ -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<uint32, uint> { // Insert Twitter joke
uint operator()(uint32 val) const { return (uint)Archive::convertTagToUppercase(val); }
};

struct EqualsTag : public Common::BinaryFunction<uint32, uint32, uint> {
bool operator()(const uint32 &val1, const uint32 &val2) const {
return Archive::convertTagToUppercase(val1) == Archive::convertTagToUppercase(val2);
}
};

typedef Common::HashMap<uint16, Resource> ResourceMap;
typedef Common::HashMap<uint32, ResourceMap, HashTag, EqualsTag> TypeMap;
typedef Common::HashMap<uint32, ResourceMap> TypeMap;
TypeMap _types;
};

Expand All @@ -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:
Expand All @@ -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
Expand Down

0 comments on commit de61313

Please sign in to comment.