diff options
Diffstat (limited to 'engines/mads/resources.cpp')
-rw-r--r-- | engines/mads/resources.cpp | 259 |
1 files changed, 257 insertions, 2 deletions
diff --git a/engines/mads/resources.cpp b/engines/mads/resources.cpp index 27b94a9e55..0630230bec 100644 --- a/engines/mads/resources.cpp +++ b/engines/mads/resources.cpp @@ -21,12 +21,267 @@ */ #include "common/scummsys.h" +#include "common/archive.h" +#include "common/substream.h" +#include "common/textconsole.h" +#include "mads/mads.h" #include "mads/resources.h" namespace MADS { -ResourcesManager::ResourcesManager(MADSEngine *vm) { - _vm = vm; +enum ResourceType {RESTYPE_ROOM, RESTYPE_SC, RESTYPE_TEXT, RESTYPE_QUO, RESTYPE_I, + RESTYPE_OB, RESTYPE_FONT, RESTYPE_SOUND, RESTYPE_SPEECH, RESTYPE_HAS_EXT, RESTYPE_NO_EXT}; + +/** + * HAG Archives implementation + */ +class HagArchive : public Common::Archive { +private: + /** + * Details of a single entry in a HAG file index + */ + struct HagEntry { + Common::String _resourceName; + uint32 _offset; + uint32 _size; + + HagEntry(): _offset(0), _size(0) {} + HagEntry(Common::String resourceName, uint32 offset, uint32 size): + _resourceName(resourceName), _offset(offset), _size(size) {} + }; + + class HagIndex { + public: + Common::List<HagEntry> _entries; + Common::String _filename; + }; + + Common::Array<HagIndex> _index; + + /** + * Load the index of all the game's HAG files + */ + void loadIndex(); + + /** + * Given a resource name, opens up the correct HAG file and returns whether + * an entry with the given name exists. + */ + bool getHeaderEntry(const Common::String &resourceName, HagIndex &hagIndex, HagEntry &hagEntry) const; + + /** + * Returns the HAG resource filename that will contain a given resource + */ + Common::String getResourceFilename(const Common::String &resourceName) const; + + /** + * Return a resource type given a resource name + */ + ResourceType getResourceType(const Common::String &resourceName) const; +public: + HagArchive(); + virtual ~HagArchive(); + + // Archive implementation + virtual bool hasFile(const Common::String &name) const; + virtual int listMembers(Common::ArchiveMemberList &list) const; + virtual const Common::ArchiveMemberPtr getMember(const Common::String &name) const; + virtual Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const; +}; + +void ResourcesManager::init(MADSEngine *vm) { + if (vm->getGameFeatures() & GF_MADS) + SearchMan.add("HAG", new HagArchive()); + else + error("Unsupported game engine"); +} + +/*------------------------------------------------------------------------*/ + +void File::openFile(const Common::String &filename) { + if (!Common::File::open(filename)) + error("Could not open file - %s", filename.c_str()); +} + +/*------------------------------------------------------------------------*/ + +const char *const MADSCONCAT_STRING = "MADSCONCAT"; + +HagArchive::HagArchive() { + loadIndex(); +} + +HagArchive::~HagArchive() { +} + +// Archive implementation +bool HagArchive::hasFile(const Common::String &name) const { + HagIndex hagIndex; + HagEntry hagEntry; + return getHeaderEntry(name, hagIndex, hagEntry); +} + +int HagArchive::listMembers(Common::ArchiveMemberList &list) const { + int members = 0; + + for (uint hagCtr = 0; hagCtr < _index.size(); ++hagCtr) { + HagIndex hagIndex = _index[hagCtr]; + Common::List<HagEntry>::iterator i; + + for (i = hagIndex._entries.begin(); i != hagIndex._entries.end(); ++i) { + list.push_back(Common::ArchiveMemberList::value_type( + new Common::GenericArchiveMember((*i)._resourceName, this))); + ++members; + } + } + + return members; +} + +const Common::ArchiveMemberPtr HagArchive::getMember(const Common::String &name) const { + if (!hasFile(name)) + return Common::ArchiveMemberPtr(); + + return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this)); +} + +Common::SeekableReadStream *HagArchive::createReadStreamForMember(const Common::String &name) const { + HagIndex hagIndex; + HagEntry hagEntry; + + if (getHeaderEntry(name, hagIndex, hagEntry)) { + // Entry found. If the correct file is not already open, open it + Common::File f; + if (!f.open(hagIndex._filename)) + error("Could not open HAG file"); + + // Return a substream for the specific resource + return new Common::SeekableSubReadStream(&f, + hagEntry._offset, hagEntry._size, DisposeAfterUse::YES); + } + + return nullptr; +} + +void HagArchive::loadIndex() { + Common::File hagFile; + + for (int sectionIndex = -1; sectionIndex < 10; ++sectionIndex) { + Common::String filename = (sectionIndex == -1) ? "GLOBAL.HAG" : + Common::String::format("SECTION%d.HAG", sectionIndex); + if (!hagFile.open(filename)) + error("Could not locate HAG file - %s", filename.c_str()); + + // Check for header + char headerBuffer[16]; + if ((hagFile.read(headerBuffer, 16) != 16) || + (strncmp(headerBuffer, MADSCONCAT_STRING, 10) != 0)) + error("Invalid HAG file opened"); + + // Scan through the HAG index + int numEntries = hagFile.readUint16LE(); + + HagIndex hagIndex; + for (int idx = 0; idx < numEntries; ++idx) { + // Read in the details of the next resource + char resourceBuffer[14]; + uint32 offset = hagFile.readUint32LE(); + uint32 size = hagFile.readUint32LE(); + hagFile.read(resourceBuffer, 14); + + hagIndex._entries.push_back(HagEntry(resourceBuffer, offset, size)); + } + + hagFile.close(); + _index.push_back(hagIndex); + } +} + +bool HagArchive::getHeaderEntry(const Common::String &resourceName, + HagIndex &hagIndex, HagEntry &hagEntry) const { + Common::String resName = resourceName; + resName.toUppercase(); + if (resName[0] == '*') + resName.deleteChar(0); + + Common::String hagFilename = getResourceFilename(resName); + + // Find the index for the given file + for (uint hagCtr = 0; hagCtr < _index.size(); ++hagCtr) { + hagIndex = _index[hagCtr]; + + if (hagIndex._filename == hagFilename) { + Common::List<HagEntry>::iterator ei; + for (ei = hagIndex._entries.begin(); ei != hagIndex._entries.end(); ++ei) { + hagEntry = *ei; + if (hagEntry._resourceName == resName) + return true; + } + } + } + + return false; +} + +Common::String HagArchive::getResourceFilename(const Common::String &resourceName) const { + ResourceType resType = getResourceType(resourceName); + Common::String outputFilename = "GLOBAL.HAG"; + + if ((resType == RESTYPE_ROOM) || (resType == RESTYPE_SC)) { + int value = atoi(resourceName.c_str() + 2); + int hagFileNum = (resType == RESTYPE_ROOM) ? value / 100 : value; + + if (hagFileNum > 0) + outputFilename = Common::String::format("SECTION%d.HAG", hagFileNum); + } + + if (resType == RESTYPE_SPEECH) + outputFilename = "SPEECH.HAG"; + + return outputFilename; +} + +ResourceType HagArchive::getResourceType(const Common::String &resourceName) const { + if (resourceName.hasPrefix("RM")) { + // Room resource + return RESTYPE_ROOM; + } else if (resourceName.hasPrefix("SC")) { + // SC resource + return RESTYPE_SC; + } else if (resourceName.hasSuffix(".TXT")) { + // Text resource + return RESTYPE_TEXT; + } else if (resourceName.hasSuffix(".QUO")) { + // QUO resource + return RESTYPE_QUO; + } else if (resourceName.hasPrefix("I")) { + // I resource + return RESTYPE_I; + } else if (resourceName.hasPrefix("OB")) { + // OB resource + return RESTYPE_OB; + } else if (resourceName.hasPrefix("FONT")) { + // FONT resource + return RESTYPE_FONT; + } else if (resourceName.hasPrefix("SOUND")) { + // SOUND resource + return RESTYPE_SOUND; + } else if (resourceName.hasPrefix("SPCHC")) { + // SPEECH resource + return RESTYPE_SPEECH; + } + + // Check for a known extension + const char *extPos = strchr(resourceName.c_str(), '.'); + if (extPos) { + ++extPos; + if (!strcmp(extPos, "FL") || !strcmp(extPos, "LBM") || !strcmp(extPos, "ANM") || + !strcmp(extPos, "AA") || !strcmp(extPos, "SS")) { + return RESTYPE_HAS_EXT; + } + } + + return RESTYPE_NO_EXT; } } // End of namespace MADS |