diff options
Diffstat (limited to 'engines/mads/resources.cpp')
-rw-r--r-- | engines/mads/resources.cpp | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/engines/mads/resources.cpp b/engines/mads/resources.cpp new file mode 100644 index 0000000000..1fb75e6ba2 --- /dev/null +++ b/engines/mads/resources.cpp @@ -0,0 +1,417 @@ +/* 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. + * + */ + +#include "common/scummsys.h" +#include "common/archive.h" +#include "common/textconsole.h" +#include "mads/mads.h" +#include "mads/resources.h" + +namespace MADS { + +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(MADSEngine *vm); + + /** + * 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(MADSEngine *vm); + 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; +}; + +const char *const MADSCONCAT_STRING = "MADSCONCAT"; + +HagArchive::HagArchive(MADSEngine *vm) { + loadIndex(vm); +} + +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 new stream for the specific resource + f.seek(hagEntry._offset); + return f.readStream(hagEntry._size); + } + + return nullptr; +} + +void HagArchive::loadIndex(MADSEngine *vm) { + Common::File hagFile; + + for (int sectionIndex = -1; sectionIndex < 11; ++sectionIndex) { + if (sectionIndex == 0) + continue; + + // Dragonsphere does not have some sections - skip them + if (vm->getGameID() == GType_Dragonsphere) { + if (sectionIndex == 7 || sectionIndex == 8) + continue; + } + + // Phantom does not have some sections - skip them + if (vm->getGameID() == GType_Phantom) { + if (sectionIndex == 6 || sectionIndex == 7 || sectionIndex == 8) + continue; + } + + Common::String filename = (sectionIndex == -1) ? "GLOBAL.HAG" : + Common::String::format("SECTION%d.HAG", sectionIndex); + if (sectionIndex == 10) { + // Speech + if (!Common::File::exists("SPEECH.HAG")) + break; + else + filename = "SPEECH.HAG"; + } + 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; + hagIndex._filename = filename; + + 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.compareToIgnoreCase(resName) == 0) + 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; +} + +/*------------------------------------------------------------------------*/ + +void Resources::init(MADSEngine *vm) { + SearchMan.add("HAG", new HagArchive(vm)); +} + +Common::String Resources::formatName(RESPREFIX resType, int id, const Common::String &ext) { + Common::String result = "*"; + + if (resType == 3 && !id) { + id = id / 100; + } + + if (!ext.empty()) { + switch (resType) { + case RESPREFIX_GL: + result += "GL000"; + break; + case RESPREFIX_SC: + result += Common::String::format("SC%.3d", id); + break; + case RESPREFIX_RM: + result += Common::String::format("RM%.3d", id); + break; + default: + break; + } + + result += ext; + } + + return result; +} + +Common::String Resources::formatName(int prefix, char asciiCh, int id, EXTTYPE extType, + const Common::String &suffix) { + Common::String result; + if (prefix <= 0) { + result = "*"; + } else { + result = Common::String::format("%s%.3d", + (prefix < 100) ? "*SC" : "*RM", prefix); + } + + result += Common::String::format("%c", asciiCh); + if (id >= 0) + result += Common::String::format("%d", id); + if (!suffix.empty()) + result += suffix; + + switch (extType) { + case EXT_SS: + result += ".SS"; + break; + case EXT_AA: + result += ".AA"; + break; + case EXT_DAT: + result += ".DAT"; + break; + case EXT_HH: + result += ".HH"; + break; + case EXT_ART: + result += ".ART"; + break; + case EXT_INT: + result += ".INT"; + break; + default: + break; + } + + return result; +} + +Common::String Resources::formatResource(const Common::String &resName, + const Common::String &hagFilename) { +// int v1 = 0, v2 = 0; + + if (resName.hasPrefix("*")) { + // Resource file specified + error("TODO: formatResource"); + } else { + // File outside of hag file + return resName; + } +} + +Common::String Resources::formatAAName(int idx) { + return formatName(0, 'I', idx, EXT_AA, ""); +} + +/*------------------------------------------------------------------------*/ + +void File::openFile(const Common::String &filename) { + if (!Common::File::open(filename)) + error("Could not open file - %s", filename.c_str()); +} + +/*------------------------------------------------------------------------*/ + +void SynchronizedList::synchronize(Common::Serializer &s) { + int v = 0; + int count = size(); + s.syncAsUint16LE(count); + + if (s.isSaving()) { + for (int idx = 0; idx < count; ++idx) { + v = (*this)[idx]; + s.syncAsSint32LE(v); + } + } else { + clear(); + reserve(count); + for (int idx = 0; idx < count; ++idx) { + s.syncAsSint32LE(v); + push_back(v); + } + } +} + +} // End of namespace MADS |