diff options
author | Eugene Sandulenko | 2016-10-26 10:08:17 +0200 |
---|---|---|
committer | Eugene Sandulenko | 2016-10-26 10:48:37 +0200 |
commit | 814db5fd38ae87a23674f07461ca35358bd77eb5 (patch) | |
tree | 254d522031dc8055274ddab3a2a540c4e074ca46 /engines/director/archive.cpp | |
parent | df2bd1a94c17b4cf4e6981b85d80d23172d31ba0 (diff) | |
download | scummvm-rg350-814db5fd38ae87a23674f07461ca35358bd77eb5.tar.gz scummvm-rg350-814db5fd38ae87a23674f07461ca35358bd77eb5.tar.bz2 scummvm-rg350-814db5fd38ae87a23674f07461ca35358bd77eb5.zip |
DIRECTOR: Renamed resource/archive files to better reflect reality
Diffstat (limited to 'engines/director/archive.cpp')
-rw-r--r-- | engines/director/archive.cpp | 527 |
1 files changed, 362 insertions, 165 deletions
diff --git a/engines/director/archive.cpp b/engines/director/archive.cpp index 9976c253f7..829764513f 100644 --- a/engines/director/archive.cpp +++ b/engines/director/archive.cpp @@ -20,233 +20,430 @@ * */ -#include "common/macresman.h" +#include "director/archive.h" -#include "director/director.h" -#include "director/resource.h" -#include "director/lingo/lingo.h" +#include "common/debug.h" +#include "common/macresman.h" namespace Director { -Archive *DirectorEngine::createArchive() { - if (getPlatform() == Common::kPlatformMacintosh) { - if (getVersion() < 4) - return new MacArchive(); - else - return new RIFXArchive(); - } else { - return new RIFFArchive(); - } +// Base Archive code + +Archive::Archive() { + _stream = 0; + _isBigEndian = true; } -void DirectorEngine::loadMainArchive() { - if (getPlatform() == Common::kPlatformWindows) - loadEXE(); - else - loadMac(); -} - -void DirectorEngine::cleanupMainArchive() { - delete _mainArchive; - delete _macBinary; -} - -void DirectorEngine::loadEXE() { - Common::SeekableReadStream *exeStream = SearchMan.createReadStreamForMember(getEXEName()); - if (!exeStream) - error("Failed to open EXE '%s'", getEXEName().c_str()); - - _lingo->processEvent(kEventStart, 0); - - exeStream->seek(-4, SEEK_END); - exeStream->seek(exeStream->readUint32LE()); - - switch (getVersion()) { - case 3: - loadEXEv3(exeStream); - break; - case 4: - loadEXEv4(exeStream); - break; - case 5: - loadEXEv5(exeStream); - break; - case 7: - loadEXEv7(exeStream); - break; - default: - error("Unhandled Windows EXE version %d", getVersion()); +Archive::~Archive() { + close(); +} + +bool Archive::openFile(const Common::String &fileName) { + Common::File *file = new Common::File(); + + if (!file->open(fileName)) { + delete file; + return false; + } + + if (!openStream(file)) { + close(); + return false; } + + _fileName = fileName; + + return true; } -void DirectorEngine::loadEXEv3(Common::SeekableReadStream *stream) { - uint16 entryCount = stream->readUint16LE(); - if (entryCount != 1) - error("Unhandled multiple entry v3 EXE"); +void Archive::close() { + _types.clear(); - stream->skip(5); // unknown + if (_stream) + delete _stream; - stream->readUint32LE(); // Main MMM size - Common::String mmmFileName = readPascalString(*stream); - Common::String directoryName = readPascalString(*stream); + _stream = 0; +} - debugC(1, kDebugLoading, "Main MMM: '%s'", mmmFileName.c_str()); - debugC(1, kDebugLoading, "Directory Name: '%s'", directoryName.c_str()); +bool Archive::hasResource(uint32 tag, uint16 id) const { + if (!_types.contains(tag)) + return false; - _mainArchive = new RIFFArchive(); + return _types[tag].contains(id); +} + +bool Archive::hasResource(uint32 tag, const Common::String &resName) const { + if (!_types.contains(tag) || resName.empty()) + return false; + + const ResourceMap &resMap = _types[tag]; + + for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++) + if (it->_value.name.matchString(resName)) + return true; + + return false; +} - if (!_mainArchive->openFile(mmmFileName)) - error("Could not open '%s'", mmmFileName.c_str()); +Common::SeekableSubReadStreamEndian *Archive::getResource(uint32 tag, uint16 id) { + if (!_types.contains(tag)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); - delete stream; + const ResourceMap &resMap = _types[tag]; + + if (!resMap.contains(id)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); + + const Resource &res = resMap[id]; + + return new Common::SeekableSubReadStreamEndian(_stream, res.offset, res.offset + res.size, _isBigEndian, DisposeAfterUse::NO); } -void DirectorEngine::loadEXEv4(Common::SeekableReadStream *stream) { - if (stream->readUint32BE() != MKTAG('P', 'J', '9', '3')) - error("Invalid projector tag found in v4 EXE"); +uint32 Archive::getOffset(uint32 tag, uint16 id) const { + if (!_types.contains(tag)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); - uint32 rifxOffset = stream->readUint32LE(); - /* uint32 fontMapOffset = */ stream->readUint32LE(); - /* uint32 resourceForkOffset1 = */ stream->readUint32LE(); - /* uint32 resourceForkOffset2 = */ stream->readUint32LE(); - stream->readUint32LE(); // graphics DLL offset - stream->readUint32LE(); // sound DLL offset - /* uint32 rifxOffsetAlt = */ stream->readUint32LE(); // equivalent to rifxOffset + const ResourceMap &resMap = _types[tag]; - loadEXERIFX(stream, rifxOffset); + if (!resMap.contains(id)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); + + return resMap[id].offset; } -void DirectorEngine::loadEXEv5(Common::SeekableReadStream *stream) { - if (stream->readUint32LE() != MKTAG('P', 'J', '9', '5')) - error("Invalid projector tag found in v5 EXE"); +uint16 Archive::findResourceID(uint32 tag, const Common::String &resName) const { + if (!_types.contains(tag) || resName.empty()) + return 0xFFFF; + + const ResourceMap &resMap = _types[tag]; + + for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++) + if (it->_value.name.matchString(resName)) + return it->_key; - uint32 rifxOffset = stream->readUint32LE(); - stream->readUint32LE(); // unknown - stream->readUint32LE(); // unknown - stream->readUint32LE(); // unknown - /* uint16 screenWidth = */ stream->readUint16LE(); - /* uint16 screenHeight = */ stream->readUint16LE(); - stream->readUint32LE(); // unknown - stream->readUint32LE(); // unknown - /* uint32 fontMapOffset = */ stream->readUint32LE(); + return 0xFFFF; +} + +Common::String Archive::getName(uint32 tag, uint16 id) const { + if (!_types.contains(tag)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); + + const ResourceMap &resMap = _types[tag]; + + if (!resMap.contains(id)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); + + return resMap[id].name; +} + +Common::Array<uint32> Archive::getResourceTypeList() const { + Common::Array<uint32> typeList; + + for (TypeMap::const_iterator it = _types.begin(); it != _types.end(); it++) + typeList.push_back(it->_key); + + return typeList; +} + +Common::Array<uint16> Archive::getResourceIDList(uint32 type) const { + Common::Array<uint16> idList; + + if (!_types.contains(type)) + return idList; + + const ResourceMap &resMap = _types[type]; + + for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++) + idList.push_back(it->_key); + + return idList; +} + +uint32 Archive::convertTagToUppercase(uint32 tag) { + uint32 newTag = toupper(tag >> 24) << 24; + newTag |= toupper((tag >> 16) & 0xFF) << 16; + newTag |= toupper((tag >> 8) & 0xFF) << 8; + + return newTag | toupper(tag & 0xFF); +} + +// Mac Archive code + +MacArchive::MacArchive() : Archive(), _resFork(0) { +} + +MacArchive::~MacArchive() { + delete _resFork; +} - loadEXERIFX(stream, rifxOffset); +void MacArchive::close() { + Archive::close(); + delete _resFork; + _resFork = 0; } -void DirectorEngine::loadEXEv7(Common::SeekableReadStream *stream) { - if (stream->readUint32LE() != MKTAG('P', 'J', '0', '0')) - error("Invalid projector tag found in v7 EXE"); +bool MacArchive::openFile(const Common::String &fileName) { + close(); - uint32 rifxOffset = stream->readUint32LE(); - stream->readUint32LE(); // unknown - stream->readUint32LE(); // unknown - stream->readUint32LE(); // unknown - stream->readUint32LE(); // unknown - stream->readUint32LE(); // some DLL offset + _resFork = new Common::MacResManager(); - loadEXERIFX(stream, rifxOffset); + if (!_resFork->open(fileName) || !_resFork->hasResFork()) { + close(); + return false; + } + + _fileName = _resFork->getBaseFileName(); + if (_fileName.hasSuffix(".bin")) { + for (int i = 0; i < 4; i++) + _fileName.deleteLastChar(); + } + + Common::MacResTagArray tagArray = _resFork->getResTagArray(); + + for (uint32 i = 0; i < tagArray.size(); i++) { + ResourceMap &resMap = _types[tagArray[i]]; + Common::MacResIDArray idArray = _resFork->getResIDArray(tagArray[i]); + + for (uint32 j = 0; j < idArray.size(); j++) { + Resource &res = resMap[idArray[j]]; + + res.offset = res.size = 0; // unused + res.name = _resFork->getResName(tagArray[i], idArray[j]); + debug(3, "Found MacArchive resource '%s' %d: %s", tag2str(tagArray[i]), idArray[j], res.name.c_str()); + } + } + + return true; } -void DirectorEngine::loadEXERIFX(Common::SeekableReadStream *stream, uint32 offset) { - _mainArchive = new RIFXArchive(); +bool MacArchive::openStream(Common::SeekableReadStream *stream, uint32 startOffset) { + // TODO: Add support for this (v4 Windows games) + return false; +} - if (!_mainArchive->openStream(stream, offset)) - error("Failed to load RIFX from EXE"); +Common::SeekableSubReadStreamEndian *MacArchive::getResource(uint32 tag, uint16 id) { + assert(_resFork); + Common::SeekableReadStream *stream = _resFork->getResource(tag, id); + return new Common::SeekableSubReadStreamEndian(stream, 0, stream->size(), true, DisposeAfterUse::NO); } -void DirectorEngine::loadMac() { - if (getVersion() < 4) { - // The data is part of the resource fork of the executable - _mainArchive = new MacArchive(); +// RIFF Archive code + +bool RIFFArchive::openStream(Common::SeekableReadStream *stream, uint32 startOffset) { + close(); + + stream->seek(startOffset); - if (!_mainArchive->openFile(getEXEName())) - error("Failed to open Mac binary '%s'", getEXEName().c_str()); - } else { - // The RIFX is located in the data fork of the executable - _macBinary = new Common::MacResManager(); + if (convertTagToUppercase(stream->readUint32BE()) != MKTAG('R', 'I', 'F', 'F')) + return false; - if (!_macBinary->open(getEXEName()) || !_macBinary->hasDataFork()) - error("Failed to open Mac binary '%s'", getEXEName().c_str()); + stream->readUint32LE(); // size - Common::SeekableReadStream *dataFork = _macBinary->getDataFork(); - _mainArchive = new RIFXArchive(); + if (convertTagToUppercase(stream->readUint32BE()) != MKTAG('R', 'M', 'M', 'P')) + return false; - // First we need to detect PPC vs. 68k + if (convertTagToUppercase(stream->readUint32BE()) != MKTAG('C', 'F', 'T', 'C')) + return false; - uint32 tag = dataFork->readUint32BE(); - uint32 startOffset; + uint32 cftcSize = stream->readUint32LE(); + uint32 startPos = stream->pos(); + stream->readUint32LE(); // unknown (always 0?) - 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 - startOffset = dataFork->readUint32BE(); - } else { - // 68k: The RIFX is the only thing in the data fork - startOffset = 0; + while ((uint32)stream->pos() < startPos + cftcSize) { + uint32 tag = convertTagToUppercase(stream->readUint32BE()); + + uint32 size = stream->readUint32LE(); + uint32 id = stream->readUint32LE(); + uint32 offset = stream->readUint32LE(); + + if (tag == 0) + break; + + uint16 startResPos = stream->pos(); + stream->seek(offset + 12); + + Common::String name = ""; + byte nameSize = stream->readByte(); + + if (nameSize) { + for (uint8 i = 0; i < nameSize; i++) { + name += stream->readByte(); + } } - if (!_mainArchive->openStream(dataFork, startOffset)) - error("Failed to load RIFX from Mac binary"); + stream->seek(startResPos); + + debug(3, "Found RIFF resource '%s' %d: %d @ 0x%08x", tag2str(tag), id, size, offset); + + ResourceMap &resMap = _types[tag]; + Resource &res = resMap[id]; + res.offset = offset; + res.size = size; + res.name = name; } + + _stream = stream; + return true; } -void DirectorEngine::loadSharedCastsFrom(Common::String filename) { - Archive *shardcst = createArchive(); +Common::SeekableSubReadStreamEndian *RIFFArchive::getResource(uint32 tag, uint16 id) { + if (!_types.contains(tag)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); + + const ResourceMap &resMap = _types[tag]; - debugC(1, kDebugLoading, "Loading Shared cast '%s'", filename.c_str()); + if (!resMap.contains(id)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); - if (!shardcst->openFile(filename)) { - warning("No shared cast %s", filename.c_str()); + const Resource &res = resMap[id]; - return; + // Adjust to skip the resource header + uint32 offset = res.offset + 12; + uint32 size = res.size - 4; + // Skip the Pascal string + _stream->seek(offset); + byte stringSize = _stream->readByte(); // 1 for this byte + + offset += stringSize + 1; + size -= stringSize + 1; + + // Align to nearest word boundary + if (offset & 1) { + offset++; + size--; } - _sharedDIB = new Common::HashMap<int, Common::SeekableSubReadStreamEndian *>; - _sharedSTXT = new Common::HashMap<int, Common::SeekableSubReadStreamEndian *>; - _sharedSound = new Common::HashMap<int, Common::SeekableSubReadStreamEndian *>; - _sharedBMP = new Common::HashMap<int, Common::SeekableSubReadStreamEndian *>; + return new Common::SeekableSubReadStreamEndian(_stream, offset, offset + size, true, DisposeAfterUse::NO); +} - Score *castScore = new Score(this, shardcst); +// RIFX Archive code - castScore->loadConfig(*shardcst->getResource(MKTAG('V','W','C','F'), 1024)); - castScore->loadCastData(*shardcst->getResource(MKTAG('V','W','C','R'), 1024)); +bool RIFXArchive::openStream(Common::SeekableReadStream *stream, uint32 startOffset) { + close(); - _sharedCasts = &castScore->_casts; + stream->seek(startOffset); - Common::Array<uint16> dib = shardcst->getResourceIDList(MKTAG('D','I','B',' ')); - if (dib.size() != 0) { - debugC(3, kDebugLoading, "Loading %d DIBs", dib.size()); + uint32 headerTag = stream->readUint32BE(); - for (Common::Array<uint16>::iterator iterator = dib.begin(); iterator != dib.end(); ++iterator) { - debugC(3, kDebugLoading, "Shared DIB %d", *iterator); - _sharedDIB->setVal(*iterator, shardcst->getResource(MKTAG('D','I','B',' '), *iterator)); - } + 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, startOffset + 4, stream->size(), _isBigEndian, DisposeAfterUse::NO); + + subStream.readUint32(); // size + + 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() - startOffset - 4; + + subStream.seek(mmapOffset); + + if (subStream.readUint32() != MKTAG('m', 'm', 'a', 'p')) + return false; + + subStream.readUint32(); // mmap length + subStream.readUint16(); // unknown + subStream.readUint16(); // unknown + subStream.readUint32(); // resCount + empty entries + uint32 resCount = subStream.readUint32(); + 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++) { + uint32 tag = subStream.readUint32(); + uint32 size = subStream.readUint32(); + uint32 offset = subStream.readUint32(); + /*uint16 flags = */ subStream.readUint16(); + /*uint16 unk1 = */ subStream.readUint16(); + /*uint32 unk2 = */ subStream.readUint32(); + + debug(3, "Found RIFX resource index %d: '%s', %d @ 0x%08x", i, tag2str(tag), size, offset); + + 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]; } - Common::Array<uint16> stxt = shardcst->getResourceIDList(MKTAG('S','T','X','T')); - if (stxt.size() != 0) { - debugC(3, kDebugLoading, "Loading %d STXTs", stxt.size()); + // 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; + } - for (Common::Array<uint16>::iterator iterator = stxt.begin(); iterator != stxt.end(); ++iterator) { - debugC(3, kDebugLoading, "Shared STXT %d", *iterator); - _sharedSTXT->setVal(*iterator, shardcst->getResource(MKTAG('S','T','X','T'), *iterator)); - } + // A KEY* must be present + if (!keyRes) { + warning("No 'KEY*' resource present"); + return false; } - Common::Array<uint16> bmp = shardcst->getResourceIDList(MKTAG('B','I','T','D')); - if (bmp.size() != 0) { - debugC(3, kDebugLoading, "Loading %d BITDs", bmp.size()); - for (Common::Array<uint16>::iterator iterator = bmp.begin(); iterator != bmp.end(); ++iterator) { - _sharedBMP->setVal(*iterator, shardcst->getResource(MKTAG('B','I','T','D'), *iterator)); - } + // 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(); } - Common::Array<uint16> sound = shardcst->getResourceIDList(MKTAG('S','N','D',' ')); - if (stxt.size() != 0) { - debugC(3, kDebugLoading, "Loading %d SNDs", sound.size()); - for (Common::Array<uint16>::iterator iterator = sound.begin(); iterator != sound.end(); ++iterator) { - _sharedSound->setVal(*iterator, shardcst->getResource(MKTAG('S','N','D',' '), *iterator)); + // 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(3, "Found RIFX resource: '%s' 0x%04x, %d @ 0x%08x", tag2str(resTag), id, res.size, res.offset); + _types[resTag][id] = res; } + + _stream = stream; + return true; } } // End of namespace Director |