aboutsummaryrefslogtreecommitdiff
path: root/engines/director/archive.cpp
diff options
context:
space:
mode:
authorEugene Sandulenko2016-10-26 10:08:17 +0200
committerEugene Sandulenko2016-10-26 10:48:37 +0200
commit814db5fd38ae87a23674f07461ca35358bd77eb5 (patch)
tree254d522031dc8055274ddab3a2a540c4e074ca46 /engines/director/archive.cpp
parentdf2bd1a94c17b4cf4e6981b85d80d23172d31ba0 (diff)
downloadscummvm-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.cpp527
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