From 814db5fd38ae87a23674f07461ca35358bd77eb5 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Wed, 26 Oct 2016 10:08:17 +0200 Subject: DIRECTOR: Renamed resource/archive files to better reflect reality --- engines/director/archive.cpp | 527 +++++++++++++++++++++++++++++------------- engines/director/archive.h | 109 +++++++++ engines/director/director.cpp | 2 +- engines/director/frame.cpp | 2 +- engines/director/resource.cpp | 527 +++++++++++++----------------------------- engines/director/resource.h | 109 --------- engines/director/score.cpp | 2 +- 7 files changed, 639 insertions(+), 639 deletions(-) create mode 100644 engines/director/archive.h delete mode 100644 engines/director/resource.h 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 Archive::getResourceTypeList() const { + Common::Array typeList; + + for (TypeMap::const_iterator it = _types.begin(); it != _types.end(); it++) + typeList.push_back(it->_key); + + return typeList; +} + +Common::Array Archive::getResourceIDList(uint32 type) const { + Common::Array 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; - _sharedSTXT = new Common::HashMap; - _sharedSound = new Common::HashMap; - _sharedBMP = new Common::HashMap; + 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 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::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 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 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::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 bmp = shardcst->getResourceIDList(MKTAG('B','I','T','D')); - if (bmp.size() != 0) { - debugC(3, kDebugLoading, "Loading %d BITDs", bmp.size()); - for (Common::Array::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 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 sound = shardcst->getResourceIDList(MKTAG('S','N','D',' ')); - if (stxt.size() != 0) { - debugC(3, kDebugLoading, "Loading %d SNDs", sound.size()); - for (Common::Array::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 diff --git a/engines/director/archive.h b/engines/director/archive.h new file mode 100644 index 0000000000..54d7086326 --- /dev/null +++ b/engines/director/archive.h @@ -0,0 +1,109 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef DIRECTOR_ARCHIVE_H +#define DIRECTOR_ARCHIVE_H + +#include "common/file.h" +#include "common/substream.h" + +namespace Common { +class MacResManager; +} + +namespace Director { + +// Completely ripped off of Mohawk's Archive code + +class Archive { +public: + Archive(); + virtual ~Archive(); + + virtual bool openFile(const Common::String &fileName); + virtual bool openStream(Common::SeekableReadStream *stream, uint32 offset = 0) = 0; + virtual void close(); + + Common::String getFileName() const { return _fileName; } + + bool isOpen() const { return _stream != 0; } + + bool hasResource(uint32 tag, uint16 id) const; + bool hasResource(uint32 tag, const Common::String &resName) const; + virtual Common::SeekableSubReadStreamEndian *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; + + Common::Array getResourceTypeList() const; + Common::Array getResourceIDList(uint32 type) const; + bool _isBigEndian; + static uint32 convertTagToUppercase(uint32 tag); + +protected: + Common::SeekableReadStream *_stream; + struct Resource { + uint32 offset; + uint32 size; + Common::String name; + }; + typedef Common::HashMap ResourceMap; + typedef Common::HashMap TypeMap; + TypeMap _types; + + Common::String _fileName; +}; + +class MacArchive : public Archive { +public: + MacArchive(); + ~MacArchive(); + + void close(); + bool openFile(const Common::String &fileName); + bool openStream(Common::SeekableReadStream *stream, uint32 startOffset = 0); + Common::SeekableSubReadStreamEndian *getResource(uint32 tag, uint16 id); + +private: + Common::MacResManager *_resFork; +}; + +class RIFFArchive : public Archive { +public: + RIFFArchive() : Archive() {} + ~RIFFArchive() {} + + bool openStream(Common::SeekableReadStream *stream, uint32 startOffset = 0); + Common::SeekableSubReadStreamEndian *getResource(uint32 tag, uint16 id); +}; + +class RIFXArchive : public Archive { +public: + RIFXArchive() : Archive(){ _isBigEndian = true; } + ~RIFXArchive() {} + + bool openStream(Common::SeekableReadStream *stream, uint32 startOffset = 0); +}; + +} // End of namespace Director + +#endif diff --git a/engines/director/director.cpp b/engines/director/director.cpp index 034c7c4afb..32358e4b6e 100644 --- a/engines/director/director.cpp +++ b/engines/director/director.cpp @@ -27,7 +27,7 @@ #include "graphics/macgui/macwindowmanager.h" #include "director/director.h" -#include "director/resource.h" +#include "director/archive.h" #include "director/sound.h" #include "director/lingo/lingo.h" diff --git a/engines/director/frame.cpp b/engines/director/frame.cpp index 0ce7f2be9b..c00d8bd2b4 100644 --- a/engines/director/frame.cpp +++ b/engines/director/frame.cpp @@ -29,7 +29,7 @@ #include "director/director.h" #include "director/frame.h" #include "director/images.h" -#include "director/resource.h" +#include "director/archive.h" #include "director/score.h" #include "director/sprite.h" diff --git a/engines/director/resource.cpp b/engines/director/resource.cpp index 8efec141d1..a704918860 100644 --- a/engines/director/resource.cpp +++ b/engines/director/resource.cpp @@ -20,430 +20,233 @@ * */ -#include "director/resource.h" - -#include "common/debug.h" #include "common/macresman.h" -namespace Director { - -// Base Archive code - -Archive::Archive() { - _stream = 0; - _isBigEndian = true; -} - -Archive::~Archive() { - close(); -} +#include "director/director.h" +#include "director/archive.h" +#include "director/lingo/lingo.h" -bool Archive::openFile(const Common::String &fileName) { - Common::File *file = new Common::File(); - - if (!file->open(fileName)) { - delete file; - return false; - } +namespace Director { - if (!openStream(file)) { - close(); - return false; +Archive *DirectorEngine::createArchive() { + if (getPlatform() == Common::kPlatformMacintosh) { + if (getVersion() < 4) + return new MacArchive(); + else + return new RIFXArchive(); + } else { + return new RIFFArchive(); } - - _fileName = fileName; - - return true; } -void Archive::close() { - _types.clear(); - - if (_stream) - delete _stream; - - _stream = 0; -} - -bool Archive::hasResource(uint32 tag, uint16 id) const { - if (!_types.contains(tag)) - return false; - - 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; -} - -Common::SeekableSubReadStreamEndian *Archive::getResource(uint32 tag, uint16 id) { - 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); - - const Resource &res = resMap[id]; - - return new Common::SeekableSubReadStreamEndian(_stream, res.offset, res.offset + res.size, _isBigEndian, DisposeAfterUse::NO); -} - -uint32 Archive::getOffset(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].offset; -} - -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; - - return 0xFFFF; +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()); + } } -Common::String Archive::getName(uint32 tag, uint16 id) const { - if (!_types.contains(tag)) - error("Archive does not contain '%s' %04x", tag2str(tag), id); +void DirectorEngine::loadEXEv3(Common::SeekableReadStream *stream) { + uint16 entryCount = stream->readUint16LE(); + if (entryCount != 1) + error("Unhandled multiple entry v3 EXE"); - const ResourceMap &resMap = _types[tag]; + stream->skip(5); // unknown - if (!resMap.contains(id)) - error("Archive does not contain '%s' %04x", tag2str(tag), id); + stream->readUint32LE(); // Main MMM size + Common::String mmmFileName = readPascalString(*stream); + Common::String directoryName = readPascalString(*stream); - return resMap[id].name; -} + debugC(1, kDebugLoading, "Main MMM: '%s'", mmmFileName.c_str()); + debugC(1, kDebugLoading, "Directory Name: '%s'", directoryName.c_str()); -Common::Array Archive::getResourceTypeList() const { - Common::Array typeList; + _mainArchive = new RIFFArchive(); - for (TypeMap::const_iterator it = _types.begin(); it != _types.end(); it++) - typeList.push_back(it->_key); + if (!_mainArchive->openFile(mmmFileName)) + error("Could not open '%s'", mmmFileName.c_str()); - return typeList; + delete stream; } -Common::Array Archive::getResourceIDList(uint32 type) const { - Common::Array idList; - - if (!_types.contains(type)) - return idList; - - const ResourceMap &resMap = _types[type]; +void DirectorEngine::loadEXEv4(Common::SeekableReadStream *stream) { + if (stream->readUint32BE() != MKTAG('P', 'J', '9', '3')) + error("Invalid projector tag found in v4 EXE"); - for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++) - idList.push_back(it->_key); + 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 - return idList; + loadEXERIFX(stream, rifxOffset); } -uint32 Archive::convertTagToUppercase(uint32 tag) { - uint32 newTag = toupper(tag >> 24) << 24; - newTag |= toupper((tag >> 16) & 0xFF) << 16; - newTag |= toupper((tag >> 8) & 0xFF) << 8; +void DirectorEngine::loadEXEv5(Common::SeekableReadStream *stream) { + if (stream->readUint32LE() != MKTAG('P', 'J', '9', '5')) + error("Invalid projector tag found in v5 EXE"); - return newTag | toupper(tag & 0xFF); -} - -// Mac Archive code - -MacArchive::MacArchive() : Archive(), _resFork(0) { -} - -MacArchive::~MacArchive() { - delete _resFork; -} + 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(); -void MacArchive::close() { - Archive::close(); - delete _resFork; - _resFork = 0; + loadEXERIFX(stream, rifxOffset); } -bool MacArchive::openFile(const Common::String &fileName) { - close(); +void DirectorEngine::loadEXEv7(Common::SeekableReadStream *stream) { + if (stream->readUint32LE() != MKTAG('P', 'J', '0', '0')) + error("Invalid projector tag found in v7 EXE"); - _resFork = new Common::MacResManager(); + uint32 rifxOffset = stream->readUint32LE(); + stream->readUint32LE(); // unknown + stream->readUint32LE(); // unknown + stream->readUint32LE(); // unknown + stream->readUint32LE(); // unknown + stream->readUint32LE(); // some DLL offset - 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; + loadEXERIFX(stream, rifxOffset); } -bool MacArchive::openStream(Common::SeekableReadStream *stream, uint32 startOffset) { - // TODO: Add support for this (v4 Windows games) - return false; -} +void DirectorEngine::loadEXERIFX(Common::SeekableReadStream *stream, uint32 offset) { + _mainArchive = new RIFXArchive(); -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); + if (!_mainArchive->openStream(stream, offset)) + error("Failed to load RIFX from EXE"); } -// RIFF Archive code - -bool RIFFArchive::openStream(Common::SeekableReadStream *stream, uint32 startOffset) { - close(); - - stream->seek(startOffset); +void DirectorEngine::loadMac() { + if (getVersion() < 4) { + // The data is part of the resource fork of the executable + _mainArchive = new MacArchive(); - if (convertTagToUppercase(stream->readUint32BE()) != MKTAG('R', 'I', 'F', 'F')) - return false; + 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(); - stream->readUint32LE(); // size + if (!_macBinary->open(getEXEName()) || !_macBinary->hasDataFork()) + error("Failed to open Mac binary '%s'", getEXEName().c_str()); - if (convertTagToUppercase(stream->readUint32BE()) != MKTAG('R', 'M', 'M', 'P')) - return false; + Common::SeekableReadStream *dataFork = _macBinary->getDataFork(); + _mainArchive = new RIFXArchive(); - if (convertTagToUppercase(stream->readUint32BE()) != MKTAG('C', 'F', 'T', 'C')) - return false; + // First we need to detect PPC vs. 68k - uint32 cftcSize = stream->readUint32LE(); - uint32 startPos = stream->pos(); - stream->readUint32LE(); // unknown (always 0?) + uint32 tag = dataFork->readUint32BE(); + uint32 startOffset; - 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 (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; } - 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; + if (!_mainArchive->openStream(dataFork, startOffset)) + error("Failed to load RIFX from Mac binary"); } - - _stream = stream; - return true; } -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]; +void DirectorEngine::loadSharedCastsFrom(Common::String filename) { + Archive *shardcst = createArchive(); - if (!resMap.contains(id)) - error("Archive does not contain '%s' %04x", tag2str(tag), id); + debugC(1, kDebugLoading, "Loading Shared cast '%s'", filename.c_str()); - const Resource &res = resMap[id]; + if (!shardcst->openFile(filename)) { + warning("No shared cast %s", filename.c_str()); - // 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--; + return; } - return new Common::SeekableSubReadStreamEndian(_stream, offset, offset + size, true, DisposeAfterUse::NO); -} + _sharedDIB = new Common::HashMap; + _sharedSTXT = new Common::HashMap; + _sharedSound = new Common::HashMap; + _sharedBMP = new Common::HashMap; -// RIFX Archive code + Score *castScore = new Score(this, shardcst); -bool RIFXArchive::openStream(Common::SeekableReadStream *stream, uint32 startOffset) { - close(); + castScore->loadConfig(*shardcst->getResource(MKTAG('V','W','C','F'), 1024)); + castScore->loadCastData(*shardcst->getResource(MKTAG('V','W','C','R'), 1024)); - stream->seek(startOffset); + _sharedCasts = &castScore->_casts; - uint32 headerTag = stream->readUint32BE(); + Common::Array dib = shardcst->getResourceIDList(MKTAG('D','I','B',' ')); + if (dib.size() != 0) { + debugC(3, kDebugLoading, "Loading %d DIBs", dib.size()); - 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 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]; + for (Common::Array::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)); + } } - // 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; - } + Common::Array stxt = shardcst->getResourceIDList(MKTAG('S','T','X','T')); + if (stxt.size() != 0) { + debugC(3, kDebugLoading, "Loading %d STXTs", stxt.size()); - // A KEY* must be present - if (!keyRes) { - warning("No 'KEY*' resource present"); - return false; + for (Common::Array::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)); + } } - // 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(); + Common::Array bmp = shardcst->getResourceIDList(MKTAG('B','I','T','D')); + if (bmp.size() != 0) { + debugC(3, kDebugLoading, "Loading %d BITDs", bmp.size()); + for (Common::Array::iterator iterator = bmp.begin(); iterator != bmp.end(); ++iterator) { + _sharedBMP->setVal(*iterator, shardcst->getResource(MKTAG('B','I','T','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; - } - } + Common::Array sound = shardcst->getResourceIDList(MKTAG('S','N','D',' ')); + if (stxt.size() != 0) { + debugC(3, kDebugLoading, "Loading %d SNDs", sound.size()); + for (Common::Array::iterator iterator = sound.begin(); iterator != sound.end(); ++iterator) { + _sharedSound->setVal(*iterator, shardcst->getResource(MKTAG('S','N','D',' '), *iterator)); } - - 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 diff --git a/engines/director/resource.h b/engines/director/resource.h deleted file mode 100644 index 1265908cba..0000000000 --- a/engines/director/resource.h +++ /dev/null @@ -1,109 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef DIRECTOR_RESOURCE_H -#define DIRECTOR_RESOURCE_H - -#include "common/file.h" -#include "common/substream.h" - -namespace Common { -class MacResManager; -} - -namespace Director { - -// Completely ripped off of Mohawk's Archive code - -class Archive { -public: - Archive(); - virtual ~Archive(); - - virtual bool openFile(const Common::String &fileName); - virtual bool openStream(Common::SeekableReadStream *stream, uint32 offset = 0) = 0; - virtual void close(); - - Common::String getFileName() const { return _fileName; } - - bool isOpen() const { return _stream != 0; } - - bool hasResource(uint32 tag, uint16 id) const; - bool hasResource(uint32 tag, const Common::String &resName) const; - virtual Common::SeekableSubReadStreamEndian *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; - - Common::Array getResourceTypeList() const; - Common::Array getResourceIDList(uint32 type) const; - bool _isBigEndian; - static uint32 convertTagToUppercase(uint32 tag); - -protected: - Common::SeekableReadStream *_stream; - struct Resource { - uint32 offset; - uint32 size; - Common::String name; - }; - typedef Common::HashMap ResourceMap; - typedef Common::HashMap TypeMap; - TypeMap _types; - - Common::String _fileName; -}; - -class MacArchive : public Archive { -public: - MacArchive(); - ~MacArchive(); - - void close(); - bool openFile(const Common::String &fileName); - bool openStream(Common::SeekableReadStream *stream, uint32 startOffset = 0); - Common::SeekableSubReadStreamEndian *getResource(uint32 tag, uint16 id); - -private: - Common::MacResManager *_resFork; -}; - -class RIFFArchive : public Archive { -public: - RIFFArchive() : Archive() {} - ~RIFFArchive() {} - - bool openStream(Common::SeekableReadStream *stream, uint32 startOffset = 0); - Common::SeekableSubReadStreamEndian *getResource(uint32 tag, uint16 id); -}; - -class RIFXArchive : public Archive { -public: - RIFXArchive() : Archive(){ _isBigEndian = true; } - ~RIFXArchive() {} - - bool openStream(Common::SeekableReadStream *stream, uint32 startOffset = 0); -}; - -} // End of namespace Director - -#endif diff --git a/engines/director/score.cpp b/engines/director/score.cpp index 5c594c20fc..7b03ca1a7d 100644 --- a/engines/director/score.cpp +++ b/engines/director/score.cpp @@ -32,7 +32,7 @@ #include "director/score.h" #include "director/frame.h" -#include "director/resource.h" +#include "director/archive.h" #include "director/sound.h" #include "director/sprite.h" #include "director/lingo/lingo.h" -- cgit v1.2.3