diff options
author | Johannes Schickel | 2008-02-07 23:13:13 +0000 |
---|---|---|
committer | Johannes Schickel | 2008-02-07 23:13:13 +0000 |
commit | ae0b605ad4c2b69f1e47e0d98c5cf3a881267318 (patch) | |
tree | be84bc96837b8b2c8cd58be918ca25a13564df4f /engines/kyra/resource.cpp | |
parent | 356ae3b9ce8b28b50e1f3df8c45bddeb861cd45a (diff) | |
download | scummvm-rg350-ae0b605ad4c2b69f1e47e0d98c5cf3a881267318.tar.gz scummvm-rg350-ae0b605ad4c2b69f1e47e0d98c5cf3a881267318.tar.bz2 scummvm-rg350-ae0b605ad4c2b69f1e47e0d98c5cf3a881267318.zip |
Reworked Kyrandia resource loading code.
svn-id: r30820
Diffstat (limited to 'engines/kyra/resource.cpp')
-rw-r--r-- | engines/kyra/resource.cpp | 566 |
1 files changed, 277 insertions, 289 deletions
diff --git a/engines/kyra/resource.cpp b/engines/kyra/resource.cpp index b6d3bfe57a..9077a1291a 100644 --- a/engines/kyra/resource.cpp +++ b/engines/kyra/resource.cpp @@ -28,31 +28,17 @@ #include "common/endian.h" #include "common/file.h" #include "common/fs.h" -#include "common/hash-str.h" #include "common/func.h" #include "common/algorithm.h" #include "gui/message.h" #include "kyra/resource.h" -#include "kyra/script.h" -#include "kyra/wsamovie.h" -#include "kyra/screen.h" namespace Kyra { -namespace { -struct ResFilenameEqual : public Common::UnaryFunction<ResourceFile*, bool> { - uint _filename; - ResFilenameEqual(uint file) : _filename(file) {} - - bool operator()(const ResourceFile *f) { - return f->filename() == _filename; - } -}; -} // end of anonymous namespace - -Resource::Resource(KyraEngine *vm) : _vm(vm) { +Resource::Resource(KyraEngine *vm) : _loaders(), _map(), _vm(vm) { + initializeLoaders(); } Resource::~Resource() { @@ -95,12 +81,7 @@ bool Resource::reset() { return true; } else if (_vm->game() == GI_KYRA3) { - // load the installation package file for kyra3 - INSFile *insFile = new INSFile("WESTWOOD.001"); - assert(insFile); - if (!insFile->isValid()) - error("'WESTWOOD.001' file not found or corrupt"); - _pakfiles.push_back(insFile); + loadPakFile("WESTWOOD.001"); } FSList fslist; @@ -115,7 +96,6 @@ bool Resource::reset() { }; Common::for_each(list, list + ARRAYSIZE(list), Common::bind1st(Common::mem_fun(&Resource::loadPakFile), this)); - Common::for_each(_pakfiles.begin(), _pakfiles.end(), Common::bind2nd(Common::mem_fun(&ResourceFile::protect), true)); } else { for (FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { Common::String filename = file->getName(); @@ -125,55 +105,48 @@ bool Resource::reset() { if (filename == "TWMUSIC.PAK") continue; - if (filename.hasSuffix("PAK") || filename.hasSuffix("APK")) { + if (filename == ((_vm->gameFlags().lang == Common::EN_ANY) ? "JMC.PAK" : "EMC.PAK")) + continue; + + if (filename.hasSuffix(".PAK") || filename.hasSuffix(".APK")) { if (!loadPakFile(file->getName())) error("couldn't open pakfile '%s'", file->getName().c_str()); } } - - if (_vm->gameFlags().platform == Common::kPlatformFMTowns || _vm->gameFlags().platform == Common::kPlatformPC98) { - uint unloadHash = (_vm->gameFlags().lang == Common::EN_ANY) ? Common::hashit_lower("JMC.PAK") : Common::hashit_lower("EMC.PAK"); - - ResIterator file = Common::find_if(_pakfiles.begin(), _pakfiles.end(), ResFilenameEqual(unloadHash)); - if (file != _pakfiles.end()) { - delete *file; - _pakfiles.erase(file); - } - } } return true; } bool Resource::loadPakFile(const Common::String &filename) { - ResIterator listFile = Common::find_if(_pakfiles.begin(), _pakfiles.end(), ResFilenameEqual(Common::hashit_lower(filename))); - if (listFile != _pakfiles.end()) { - (*listFile)->open(); - return true; - } + ResFileMap::iterator iter = _map.find(filename); + if (iter == _map.end()) + return false; - const bool isKyraDat = filename.equalsIgnoreCase(StaticResource::staticDataFilename()); - uint32 size = 0; + iter->_value.loadable = true; - Common::File handle; - if (!getFileHandle(filename.c_str(), &size, handle)) { - (!isKyraDat ? error : warning)("couldn't load file: '%s'", filename.c_str()); + if (!isAccessable(filename)) return false; - } - PAKFile *file = new PAKFile(filename.c_str(), handle.name(), handle, (_vm->gameFlags().platform == Common::kPlatformAmiga) && !isKyraDat); - handle.close(); + if (iter->_value.preload) + return true; - if (!file) - return false; + Common::SeekableReadStream *stream = getFileStream(filename); + assert(stream); + + const ResArchiveLoader *loader = getLoader(iter->_value.type); + assert(loader); + + loader->loadFile(filename, *stream, _map); + delete stream; + stream = 0; - if (!file->isValid()) { - error("'%s' is no valid pak file", filename.c_str()); - delete file; + iter = _map.find(filename); + if (iter == _map.end()) return false; - } + iter->_value.preload = true; + detectFileTypes(); - _pakfiles.push_back(file); return true; } @@ -221,205 +194,252 @@ bool Resource::loadFileList(const char * const *filelist, uint32 numFiles) { } void Resource::unloadPakFile(const Common::String &filename) { - ResIterator pak = Common::find_if(_pakfiles.begin(), _pakfiles.end(), ResFilenameEqual(Common::hashit_lower(filename))); - if (pak != _pakfiles.end()) - (*pak)->close(); + ResFileMap::iterator iter = _map.find(filename); + if (iter != _map.end()) { + if (!iter->_value.prot) + iter->_value.loadable = false; + } } bool Resource::isInPakList(const Common::String &filename) const { - return (Common::find_if(_pakfiles.begin(), _pakfiles.end(), ResFilenameEqual(Common::hashit_lower(filename))) != _pakfiles.end()); + return isAccessable(filename); } void Resource::unloadAllPakFiles() { - for (ResIterator start = _pakfiles.begin(); start != _pakfiles.end(); ++start) { - delete *start; - *start = 0; + FilesystemNode dir(ConfMan.get("path")); + + if (!dir.exists() || !dir.isDirectory()) + error("invalid game path '%s'", dir.getPath().c_str()); + + FSList fslist; + if (!dir.getChildren(fslist, FilesystemNode::kListFilesOnly)) + error("can't list files inside game path '%s'", dir.getPath().c_str()); + + // remove all entries + _map.clear(); + + Common::File temp; + if (temp.open("kyra.dat")) { + ResFileEntry entry; + entry.parent = ""; + entry.size = temp.size(); + entry.loadable = true; + entry.preload = false; + entry.prot = false; + entry.type = ResFileEntry::kPak; + entry.offset = 0; + _map["kyra.dat"] = entry; + temp.close(); + } + + for (FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { + ResFileEntry entry; + entry.parent = ""; + if (!temp.open(file->getPath())) + error("couldn't open file '%s'", file->getName().c_str()); + entry.size = temp.size(); + entry.offset = 0; + entry.loadable = true; + entry.preload = false; + entry.prot = false; + entry.type = ResFileEntry::kAutoDetect; + _map[file->getName()] = entry; + temp.close(); } - _pakfiles.clear(); + + detectFileTypes(); } uint8 *Resource::fileData(const char *file, uint32 *size) const { - Common::File fileHandle; + Common::SeekableReadStream *stream = getFileStream(file); + if (!stream) + return 0; + uint32 bufferSize = stream->size(); + uint8 *buffer = new uint8[bufferSize]; + assert(buffer); if (size) - *size = 0; - - // test to open it in the main dir - if (fileHandle.open(file)) { - uint32 fileSize = fileHandle.size(); - uint8 *buffer = new uint8[fileSize]; - assert(buffer); - - fileHandle.read(buffer, fileSize); - - if (size) - *size = fileSize; + *size = bufferSize; + stream->read(buffer, bufferSize); + delete stream; + return buffer; +} - return buffer; - } else { - // opens the file inside a PAK File - uint fileHash = Common::hashit_lower(file); - for (ConstResIterator cur = _pakfiles.begin(); cur != _pakfiles.end(); ++cur) { - if (!(*cur)->isOpen()) - continue; +uint32 Resource::getFileSize(const char *file) const { + if (!isAccessable(file)) + return 0; + ResFileMap::const_iterator iter = _map.find(file); + if (iter != _map.end()) + return iter->_value.size; + return 0; +} - uint8* result = (*cur)->getFile(fileHash); +bool Resource::loadFileToBuf(const char *file, void *buf, uint32 maxSize) { + Common::SeekableReadStream *stream = getFileStream(file); + if (!stream) + return false; - if (result) { - uint32 fileSize = (*cur)->getFileSize(fileHash); + if (maxSize < stream->size()) { + delete stream; + return false; + } + memset(buf, 0, maxSize); + stream->read(buf, stream->size()); + delete stream; + return true; +} - if (!fileSize) - continue; +Common::SeekableReadStream *Resource::getFileStream(const Common::String &file) const { + if (!isAccessable(file)) + return 0; - if (size) - *size = fileSize; + ResFileMap::const_iterator iter = _map.find(file); + if (iter == _map.end()) + return 0; - return result; - } + if (!iter->_value.parent.empty()) { + Common::SeekableReadStream *parent = getFileStream(iter->_value.parent); + assert(parent); + ResFileMap::const_iterator parentIter = _map.find(iter->_value.parent); + const ResArchiveLoader *loader = getLoader(parentIter->_value.type); + assert(loader); + return loader->loadFileFromArchive(file, parent, _map); + } else { + Common::File *stream = new Common::File(); + if (!stream->open(file.c_str())) { + warning("Couldn't open file '%s'", file.c_str()); + return 0; } + return stream; } return 0; } -bool Resource::getFileHandle(const char *file, uint32 *size, Common::File &filehandle) { - filehandle.close(); - - if (filehandle.open(file)) { - if (size) - *size = filehandle.size(); - return true; - } - - uint fileHash = Common::hashit_lower(file); - for (ResIterator start = _pakfiles.begin() ;start != _pakfiles.end(); ++start) { - if (!(*start)->isOpen()) - continue; - - if ((*start)->getFileHandle(fileHash, filehandle)) { - uint32 tSize = (*start)->getFileSize(fileHash); - - if (!tSize) - continue; +bool Resource::isAccessable(const Common::String &file) const { + ResFileMap::const_iterator iter = _map.find(file); + while (true) { + if (iter == _map.end()) + break; - if (size) - *size = tSize; + if (!iter->_value.loadable) + return false; - return true; - } + if (!iter->_value.parent.empty()) + iter = _map.find(iter->_value.parent); + else + return iter->_value.loadable; } - return false; } -uint32 Resource::getFileSize(const char *file) const { - Common::File temp; - if (temp.open(file)) - return temp.size(); - - uint fileHash = Common::hashit_lower(file); - for (ConstResIterator start = _pakfiles.begin() ;start != _pakfiles.end(); ++start) { - if (!(*start)->isOpen()) +void Resource::detectFileTypes() { + for (ResFileMap::iterator i = _map.begin(); i != _map.end(); ++i) { + if (!isAccessable(i->_key)) continue; - uint32 size = (*start)->getFileSize(fileHash); + if (i->_value.type == ResFileEntry::kAutoDetect) { + for (LoaderIterator l = _loaders.begin(); l != _loaders.end(); ++l) { + Common::SeekableReadStream *stream = getFileStream(i->_key); + if ((*l)->isLoadable(i->_key, *stream)) { + i->_value.type = (*l)->getType(); + i->_value.loadable = false; + i->_value.preload = false; + break; + } + delete stream; + stream = 0; + } - if (size) - return size; + if (i->_value.type == ResFileEntry::kAutoDetect) { + i->_value.type = ResFileEntry::kRaw; + i->_value.loadable = true; + } + } } - - return 0; } -bool Resource::loadFileToBuf(const char *file, void *buf, uint32 maxSize) { - Common::File tempHandle; - uint32 size = 0; - - if (!getFileHandle(file, &size, tempHandle)) - return false; +#pragma mark - +#pragma mark - ResFileLodaer +#pragma mark - - if (size > maxSize) - return false; +class ResLoaderPak : public ResArchiveLoader { +public: + bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const; + bool loadFile(const Common::String &filename, Common::SeekableReadStream &stream, ResFileMap &map) const; + Common::SeekableReadStream *loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileMap &map) const; - memset(buf, 0, maxSize); - tempHandle.read(buf, size); + ResFileEntry::kType getType() const { + return ResFileEntry::kPak; + } +}; - return true; +bool ResLoaderPak::isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const { + // TODO improve check: + Common::String file = filename; + file.toUppercase(); + return ((file.hasSuffix(".PAK") && file != "TWMUSIC.PAK") || file.hasSuffix(".APK") || file.hasSuffix(".VRM") || file.hasSuffix(".TLK")); } -/////////////////////////////////////////// -// Pak file manager -PAKFile::PAKFile(const char *file, const char *physfile, Common::File &pakfile, bool isAmiga) : ResourceFile() { - _open = false; - - if (!pakfile.isOpen()) { - debug(3, "couldn't open pakfile '%s'\n", file); - return; - } - - uint32 off = pakfile.pos(); - uint32 filesize = pakfile.size(); +bool ResLoaderPak::loadFile(const Common::String &filename, Common::SeekableReadStream &stream, ResFileMap &map) const { + uint32 filesize = stream.size(); + + Common::List<Common::String> filenames; + Common::List<ResFileEntry> entries; uint32 pos = 0, startoffset = 0, endoffset = 0; + bool switchEndian = false; - if (isAmiga) - startoffset = pakfile.readUint32BE(); - else - startoffset = pakfile.readUint32LE(); - + startoffset = stream.readUint32LE(); pos += 4; if (startoffset > filesize) { - warning("PAK file '%s' is corrupted", file); - return; + switchEndian = true; + startoffset = SWAP_BYTES_32(startoffset); } - pos += 4; - while (pos < filesize) { - PakChunk chunk; uint8 buffer[64]; uint32 nameLength; // Move to the position of the next file entry - pakfile.seek(pos); + stream.seek(pos); // Read in the header - if (pakfile.read(&buffer, 64) < 5) { - warning("PAK file '%s' is corrupted", file); - return; + if (stream.read(&buffer, 64) < 5) { + warning("PAK file '%s' is corrupted", filename.c_str()); + return false; } // Quit now if we encounter an empty string if (!(*((const char*)buffer))) break; - chunk._name = Common::hashit_lower((const char*)buffer); nameLength = strlen((const char*)buffer) + 1; if (nameLength > 60) { - warning("PAK file '%s' is corrupted", file); - return; + warning("PAK file '%s' is corrupted", filename.c_str()); + return false; } - if (isAmiga) - endoffset = READ_BE_UINT32(buffer + nameLength); - else - endoffset = READ_LE_UINT32(buffer + nameLength); + endoffset = (switchEndian ? READ_BE_UINT32 : READ_LE_UINT32)(buffer + nameLength); - if (!endoffset) { + if (!endoffset) endoffset = filesize; - } else if (endoffset > filesize || startoffset > endoffset) { - warning("PAK file '%s' is corrupted", file); - return; - } if (startoffset != endoffset) { - chunk._start = startoffset; - chunk._size = endoffset - startoffset; - - _files.push_back(chunk); + ResFileEntry entry; + entry.size = endoffset - startoffset; + entry.offset = startoffset; + entry.parent = filename; + entry.type = ResFileEntry::kAutoDetect; + entry.loadable = true; + entry.prot = false; + entry.preload = false; + + filenames.push_back(Common::String((const char*)buffer)); + entries.push_back(entry); } if (endoffset == filesize) @@ -429,161 +449,129 @@ PAKFile::PAKFile(const char *file, const char *physfile, Common::File &pakfile, pos += nameLength + 4; } - _open = true; - _filename = Common::hashit_lower(file); - _physfile = physfile; - _physOffset = off; -} + assert(filenames.size() == entries.size()); + Common::List<ResFileEntry>::iterator entry = entries.begin(); + Common::List<Common::String>::iterator file = filenames.begin(); -PAKFile::~PAKFile() { - _physfile.clear(); - _open = false; + for (; entry != entries.end(); ++entry, ++file) { + map.erase(*file); + map[*file] = *entry; + } - _files.clear(); + return true; } -uint8 *PAKFile::getFile(uint hash) const { - ConstPakIterator file = Common::find_if(_files.begin(), _files.end(), Common::bind2nd(Common::EqualTo<uint>(), hash)); - if (file == _files.end()) - return 0; +Common::SeekableReadStream *ResLoaderPak::loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileMap &map) const { + assert(archive); - Common::File pakfile; - if (!openFile(pakfile)) - return false; + ResFileMap::const_iterator entry = map.find(file); + if (entry == map.end()) + return 0; - pakfile.seek(file->_start, SEEK_CUR); - uint8 *buffer = new uint8[file->_size]; - assert(buffer); - pakfile.read(buffer, file->_size); - return buffer; + archive->seek(entry->_value.offset, SEEK_SET); + Common::SeekableSubReadStream *stream = new Common::SeekableSubReadStream(archive, entry->_value.offset, entry->_value.offset + entry->_value.size, true); + assert(stream); + return stream; } -bool PAKFile::getFileHandle(uint hash, Common::File &filehandle) const { - filehandle.close(); +class ResLoaderIns : public ResArchiveLoader { +public: + bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const; + bool loadFile(const Common::String &filename, Common::SeekableReadStream &stream, ResFileMap &map) const; + Common::SeekableReadStream *loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileMap &map) const; - ConstPakIterator file = Common::find_if(_files.begin(), _files.end(), Common::bind2nd(Common::EqualTo<uint>(), hash)); - if (file == _files.end()) - return false; - - if (!openFile(filehandle)) - return false; - - filehandle.seek(file->_start, SEEK_CUR); - return true; -} - -uint32 PAKFile::getFileSize(uint hash) const { - ConstPakIterator file = Common::find_if(_files.begin(), _files.end(), Common::bind2nd(Common::EqualTo<uint>(), hash)); - return (file != _files.end()) ? file->_size : 0; -} + ResFileEntry::kType getType() const { + return ResFileEntry::kIns; + } +}; -bool PAKFile::openFile(Common::File &filehandle) const { - filehandle.close(); +bool ResLoaderIns::isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const { + stream.seek(3); + uint32 size = stream.readUint32LE(); - if (!filehandle.open(_physfile)) + if (size > stream.size()) return false; - filehandle.seek(_physOffset, SEEK_CUR); - return true; -} + stream.seek(size+1, SEEK_SET); + uint8 buffer[2]; + stream.read(&buffer, 2); -/////////////////////////////////////////// -// Ins file manager -INSFile::INSFile(const char *file) : ResourceFile(), _files() { - Common::File pakfile; - _open = false; + return (buffer[0] == 0x0D && buffer[1] == 0x0A); +} - if (!pakfile.open(file)) { - debug(3, "couldn't open insfile '%s'\n", file); - return; - } +bool ResLoaderIns::loadFile(const Common::String &filename, Common::SeekableReadStream &stream, ResFileMap &map) const { + Common::List<Common::String> filenames; // thanks to eriktorbjorn for this code (a bit modified though) - - // skip first three bytes - pakfile.seek(3); + stream.seek(3, SEEK_SET); // first file is the index table - uint32 filesize = pakfile.readUint32LE(); - + uint32 size = stream.readUint32LE(); Common::String temp = ""; - for (uint i = 0; i < filesize; ++i) { - byte c = pakfile.readByte(); + for (uint32 i = 0; i < size; ++i) { + byte c = stream.readByte(); if (c == '\\') { temp = ""; } else if (c == 0x0D) { // line endings are CRLF - c = pakfile.readByte(); + c = stream.readByte(); assert(c == 0x0A); ++i; - FileEntry newEntry; - newEntry._name = Common::hashit_lower(temp.c_str()); - newEntry._start = 0; - newEntry._size = 0; - _files.push_back(newEntry); - - temp = ""; + filenames.push_back(temp); } else { temp += (char)c; } } - pakfile.seek(3); + stream.seek(3, SEEK_SET); - for (FileIterator start = _files.begin(); start != _files.end(); ++start) { - filesize = pakfile.readUint32LE(); - start->_size = filesize; - start->_start = pakfile.pos(); - pakfile.seek(filesize, SEEK_CUR); - } + for (Common::List<Common::String>::iterator file = filenames.begin(); file != filenames.end(); ++file) { + map.erase(*file); - _filename = Common::hashit_lower(file); - _physfile = file; - _open = true; -} + ResFileEntry entry; + entry.parent = filename; + entry.type = ResFileEntry::kAutoDetect; + entry.loadable = true; + entry.preload = false; + entry.prot = false; + entry.size = stream.readUint32LE(); + entry.offset = stream.pos(); + stream.seek(entry.size, SEEK_CUR); -INSFile::~INSFile() { - _open = false; + map[*file] = entry; + } - _files.clear(); + return true; } -uint8 *INSFile::getFile(uint hash) const { - ConstFileIterator file = Common::find_if(_files.begin(), _files.end(), Common::bind2nd(Common::EqualTo<uint>(), hash)); - if (file == _files.end()) - return 0; +Common::SeekableReadStream *ResLoaderIns::loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileMap &map) const { + assert(archive); - Common::File pakfile; - if (!pakfile.open(_physfile)) - return false; + ResFileMap::const_iterator entry = map.find(file); + if (entry == map.end()) + return 0; - pakfile.seek(file->_start); - uint8 *buffer = new uint8[file->_size]; - assert(buffer); - pakfile.read(buffer, file->_size); - return buffer; + archive->seek(entry->_value.offset, SEEK_SET); + Common::SeekableSubReadStream *stream = new Common::SeekableSubReadStream(archive, entry->_value.offset, entry->_value.offset + entry->_value.size, true); + assert(stream); + return stream; } -bool INSFile::getFileHandle(uint hash, Common::File &filehandle) const { - ConstFileIterator file = Common::find_if(_files.begin(), _files.end(), Common::bind2nd(Common::EqualTo<uint>(), hash)); - - if (file == _files.end()) - return false; - - if (!filehandle.open(_physfile)) - return false; - - filehandle.seek(file->_start, SEEK_CUR); - return true; +void Resource::initializeLoaders() { + _loaders.push_back(new ResLoaderPak()); + _loaders.push_back(new ResLoaderIns()); } -uint32 INSFile::getFileSize(uint hash) const { - ConstFileIterator file = Common::find_if(_files.begin(), _files.end(), Common::bind2nd(Common::EqualTo<uint>(), hash)); - return (file != _files.end()) ? file->_size : 0; +const ResArchiveLoader *Resource::getLoader(ResFileEntry::kType type) const { + for (CLoaderIterator i = _loaders.begin(); i != _loaders.end(); ++i) { + if ((*i)->getType() == type) + return *i; + } + return 0; } } // end of namespace Kyra |