aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Schickel2008-09-07 21:46:37 +0000
committerJohannes Schickel2008-09-07 21:46:37 +0000
commit7aaf6139e25b55d4eca335553fe6da654ba1b341 (patch)
treeff4526b996ca66a07547d282f416722c1bc0786f
parentd48a57723a47d127d6e9c02f44743a89423ce6f8 (diff)
downloadscummvm-rg350-7aaf6139e25b55d4eca335553fe6da654ba1b341.tar.gz
scummvm-rg350-7aaf6139e25b55d4eca335553fe6da654ba1b341.tar.bz2
scummvm-rg350-7aaf6139e25b55d4eca335553fe6da654ba1b341.zip
Moved Kyra resource code to a SearchSet/Archive based implementation, this removes dependencies on Common::File.
svn-id: r34428
-rw-r--r--engines/kyra/module.mk1
-rw-r--r--engines/kyra/resource.cpp1418
-rw-r--r--engines/kyra/resource.h101
-rw-r--r--engines/kyra/resource_intern.cpp1072
-rw-r--r--engines/kyra/resource_intern.h131
5 files changed, 1291 insertions, 1432 deletions
diff --git a/engines/kyra/module.mk b/engines/kyra/module.mk
index e059a8ce4b..b38661ada5 100644
--- a/engines/kyra/module.mk
+++ b/engines/kyra/module.mk
@@ -23,6 +23,7 @@ MODULE_OBJS := \
kyra_mr.o \
lol.o \
resource.o \
+ resource_intern.o \
saveload.o \
saveload_lok.o \
saveload_hof.o \
diff --git a/engines/kyra/resource.cpp b/engines/kyra/resource.cpp
index 340d9e0d1c..362c6a205d 100644
--- a/engines/kyra/resource.cpp
+++ b/engines/kyra/resource.cpp
@@ -23,31 +23,37 @@
*
*/
+#include "kyra/resource.h"
+#include "kyra/resource_intern.h"
#include "common/config-manager.h"
#include "common/endian.h"
#include "common/file.h"
#include "common/fs.h"
#include "common/func.h"
-
-#include "kyra/resource.h"
+#include "common/system.h"
namespace Kyra {
-Resource::Resource(KyraEngine_v1 *vm) : _loaders(), _map(), _vm(vm) {
+Resource::Resource(KyraEngine_v1 *vm) : _archiveCache(), _files(), _archiveFiles(new Common::SearchSet()), _protectedFiles(new Common::SearchSet()), _loaders(), _vm(vm) {
initializeLoaders();
+
+ Common::SharedPtr<Common::Archive> path(new Common::FSDirectory(ConfMan.get("path")));
+ Common::SharedPtr<Common::Archive> extrapath(new Common::FSDirectory(ConfMan.get("extrapath")));
+ _vm->_system->addSysArchivesToSearchSet(_files);
+ _files.add("path", path, 3);
+ _files.add("extrapath", extrapath, 3);
+ // compressed installer archives are added at level '2',
+ // but that's done in Resource::reset not here
+ _files.add("protected", _protectedFiles, 1);
+ _files.add("archives", _archiveFiles, 0);
}
Resource::~Resource() {
- _map.clear();
_loaders.clear();
-
- clearCompFileList();
- _compLoaders.clear();
}
bool Resource::reset() {
- clearCompFileList();
unloadAllPakFiles();
Common::FilesystemNode dir(ConfMan.get("path"));
@@ -71,18 +77,12 @@ bool Resource::reset() {
loadPakFile("CHAPTER1.VRM");
} else if (_vm->game() == GI_KYRA2) {
if (_vm->gameFlags().useInstallerPackage)
- tryLoadCompFiles();
+ _files.add("installer", loadInstallerArchive("WESTWOOD", "%03d", 6), 2);
// mouse pointer, fonts, etc. required for initializing
if (_vm->gameFlags().isDemo && !_vm->gameFlags().isTalkie) {
loadPakFile("GENERAL.PAK");
} else {
- if (_vm->gameFlags().isTalkie) {
- // Add default file directories
- Common::File::addDefaultDirectory(ConfMan.get("path") + "/hof_cd");
- Common::File::addDefaultDirectory(ConfMan.get("path") + "/HOF_CD");
- }
-
loadPakFile("INTROGEN.PAK");
loadPakFile("OTHER.PAK");
}
@@ -94,17 +94,13 @@ bool Resource::reset() {
error("couldn't load file: 'WESTWOOD.001'");
}
- // Add default file directories
- Common::File::addDefaultDirectory(ConfMan.get("path") + "/malcolm");
- Common::File::addDefaultDirectory(ConfMan.get("path") + "/MALCOLM");
-
if (!loadFileList("FILEDATA.FDT"))
error("couldn't load file: 'FILEDATA.FDT'");
return true;
} else if (_vm->game() == GI_LOL) {
if (_vm->gameFlags().useInstallerPackage)
- tryLoadCompFiles();
+ _files.add("installer", loadInstallerArchive("WESTWOOD", "%d", 0), 2);
return true;
}
@@ -120,12 +116,9 @@ bool Resource::reset() {
"CAVE.APK", "DRAGON1.APK", "DRAGON2.APK", "LAGOON.APK"
};
- Common::for_each(list, list + ARRAYSIZE(list), Common::bind1st(Common::mem_fun(&Resource::loadPakFile), this));
-
- for (int i = 0; i < ARRAYSIZE(list); ++i) {
- ResFileMap::iterator iterator = _map.find(list[i]);
- if (iterator != _map.end())
- iterator->_value.prot = true;
+ for (uint i = 0; i < ARRAYSIZE(list); ++i) {
+ Common::ArchivePtr archive = loadArchive(list[i]);
+ _protectedFiles->add(list[i], archive, 0);
}
} else {
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
@@ -149,78 +142,18 @@ bool Resource::reset() {
return true;
}
-bool Resource::loadPakFile(const Common::String &filename) {
- if (!isAccessible(filename))
- return false;
-
- ResFileMap::iterator iter = _map.find(filename);
- if (iter == _map.end())
- return false;
+bool Resource::loadPakFile(Common::String filename) {
+ filename.toUppercase();
- if (iter->_value.preload) {
- iter->_value.mounted = true;
+ if (_archiveFiles->hasArchive(filename) || _protectedFiles->hasArchive(filename))
return true;
- }
- const ResArchiveLoader *loader = getLoader(iter->_value.type);
- if (!loader) {
- error("no archive loader for file '%s' found which is of type %d", filename.c_str(), iter->_value.type);
+ Common::ArchivePtr archive = loadArchive(filename);
+ if (!archive)
return false;
- }
- Common::SeekableReadStream *stream = getFileStream(filename);
- if (!stream) {
- error("archive file '%s' not found", filename.c_str());
- return false;
- }
+ _archiveFiles->add(filename, archive, 0);
- iter->_value.mounted = true;
- iter->_value.preload = true;
- ResArchiveLoader::FileList files;
- loader->loadFile(filename, *stream, files);
- delete stream;
- stream = 0;
-
- for (ResArchiveLoader::FileList::iterator i = files.begin(); i != files.end(); ++i) {
- iter = _map.find(i->filename);
- if (iter == _map.end()) {
- // We do an internal check for a file in gamepath with same filename to
- // allow overwriting files inside archives with plain files inside the
- // game directory
- checkFile(i->filename);
-
- // A new file entry, so we just insert it into the file map.
- if (_map.find(i->filename) == _map.end())
- _map[i->filename] = i->entry;
- } else if (!iter->_value.parent.empty()) {
- if (!iter->_value.parent.equalsIgnoreCase(filename)) {
- ResFileMap::iterator oldParent = _map.find(iter->_value.parent);
- if (oldParent != _map.end()) {
- // Protected files and their embedded file entries do not get overwritten.
- if (!oldParent->_value.prot) {
- // If the old parent is not protected we mark it as not preload anymore,
- // since now no longer all of its embedded files are in the filemap.
- oldParent->_value.preload = false;
- iter->_value = i->entry;
- }
- } else {
- // Old parent not found? That's strange... But we just overwrite the old
- // entry.
- iter->_value = i->entry;
- }
- } else {
- // The old parent has the same filenames as the new archive, we are sure and overwrite the
- // old file entry, could be afterall that the preload flag of the new archive was
- // just unflagged.
- iter->_value = i->entry;
- }
- }
- // 'else' case would mean here overwriting an existing file entry in the map without parent.
- // We don't support that though, so one can overwrite files from archives by putting
- // them in the gamepath.
- }
-
- detectFileTypes();
return true;
}
@@ -240,11 +173,11 @@ bool Resource::loadFileList(const Common::String &filedata) {
buffer[12] = 0;
f.seek(offset + 16, SEEK_SET);
- Common::String filename = Common::String((char*)buffer);
+ Common::String filename = Common::String((char *)buffer);
filename.toUppercase();
if (filename.hasSuffix(".PAK")) {
- if (!isAccessible(filename) && _vm->gameFlags().isDemo) {
+ if (!exists(filename.c_str()) && _vm->gameFlags().isDemo) {
// the demo version supplied with Kyra3 does not
// contain all pak files listed in filedata.fdt
// so we don't do anything here if they are non
@@ -273,33 +206,20 @@ bool Resource::loadFileList(const char * const *filelist, uint32 numFiles) {
return true;
}
-void Resource::unloadPakFile(const Common::String &filename) {
- ResFileMap::iterator iter = _map.find(filename);
- if (iter != _map.end()) {
- if (!iter->_value.prot)
- iter->_value.mounted = false;
- }
-}
-
-void Resource::clearCompFileList() {
- for (CompFileMap::iterator i = _compFiles.begin(); i != _compFiles.end(); ++i)
- delete[] i->_value.data;
-
- _compFiles.clear();
+void Resource::unloadPakFile(Common::String filename) {
+ filename.toUppercase();
+ _archiveFiles->remove(filename);
+ _protectedFiles->remove(filename);
}
-bool Resource::isInPakList(const Common::String &filename) {
- if (!isAccessible(filename))
- return false;
- ResFileMap::iterator iter = _map.find(filename);
- if (iter == _map.end())
- return false;
- return (iter->_value.type != ResFileEntry::kRaw);
+bool Resource::isInPakList(Common::String filename) {
+ filename.toUppercase();
+ return (_archiveFiles->hasArchive(filename) || _protectedFiles->hasArchive(filename));
}
void Resource::unloadAllPakFiles() {
- // remove all entries
- _map.clear();
+ _archiveFiles->clear();
+ _protectedFiles->clear();
}
uint8 *Resource::fileData(const char *file, uint32 *size) {
@@ -318,9 +238,7 @@ uint8 *Resource::fileData(const char *file, uint32 *size) {
}
bool Resource::exists(const char *file, bool errorOutOnFail) {
- if (Common::File::exists(file))
- return true;
- else if (isAccessible(file))
+ if (_files.hasFile(file))
return true;
else if (errorOutOnFail)
error("File '%s' can't be found", file);
@@ -328,21 +246,13 @@ bool Resource::exists(const char *file, bool errorOutOnFail) {
}
uint32 Resource::getFileSize(const char *file) {
- CompFileMap::iterator compEntry;
-
- if (Common::File::exists(file)) {
- Common::File f;
- if (f.open(file))
- return f.size();
- } else {
- if (!isAccessible(file))
- return 0;
+ Common::SeekableReadStream *stream = getFileStream(file);
+ if (!stream)
+ return 0;
- ResFileMap::const_iterator iter = _map.find(file);
- if (iter != _map.end())
- return iter->_value.size;
- }
- return 0;
+ uint32 size = stream->size();
+ delete stream;
+ return size;
}
bool Resource::loadFileToBuf(const char *file, void *buf, uint32 maxSize) {
@@ -357,1222 +267,51 @@ bool Resource::loadFileToBuf(const char *file, void *buf, uint32 maxSize) {
}
Common::SeekableReadStream *Resource::getFileStream(const Common::String &file) {
- CompFileMap::iterator compEntry;
-
- if ((compEntry = _compFiles.find(file)) != _compFiles.end())
- return new Common::MemoryReadStream(compEntry->_value.data, compEntry->_value.size, false);
-
- if (!isAccessible(file))
- return 0;
-
- ResFileMap::const_iterator iter = _map.find(file);
- if (iter == _map.end())
- return 0;
-
- if (iter->_value.parent.empty()) {
- Common::File *stream = new Common::File();
- if (!stream->open(file)) {
- delete stream;
- stream = 0;
- error("Couldn't open file '%s'", file.c_str());
- }
- return stream;
- } else {
- Common::SeekableReadStream *parent = getFileStream(iter->_value.parent);
- assert(parent);
-
- ResFileEntry* parentEntry = getParentEntry(&iter->_value);
- const ResArchiveLoader *loader = getLoader(parentEntry->type);
- assert(loader);
-
- return loader->loadFileFromArchive(file, parent, iter->_value);
- }
-
- return 0;
-}
-
-bool Resource::isAccessible(const Common::String &file) {
- checkFile(file);
-
- ResFileMap::const_iterator iter = _map.find(file);
- if (iter == _map.end())
- return false;
-
- return isAccessible(&iter->_value);
-}
-
-bool Resource::isAccessible(const ResFileEntry *fileEntry) {
- assert(fileEntry);
-
- const ResFileEntry* currentEntry = fileEntry;
- while (!currentEntry->parent.empty()) {
- if (currentEntry->parentEntry) {
- currentEntry = currentEntry->parentEntry;
- } else {
- ResFileMap::iterator it = _map.find(currentEntry->parent);
- if (it == _map.end())
- return false;
- else
- currentEntry->parentEntry = &it->_value;
- }
- // parent can never be a non archive file
- if (currentEntry->type == ResFileEntry::kRaw)
- return false;
- // not mounted parent means not accessable
- else if (!currentEntry->mounted)
- return false;
- }
-
- return true;
-}
-
-ResFileEntry *Resource::getParentEntry(const ResFileEntry *entry) const {
- assert(entry);
- if (entry->parent.empty()) {
- return 0;
- } else if (entry->parentEntry) {
- assert(_map.find(entry->parent) != _map.end()); // If some day the hash map implementations changes and moves nodes around,
- // this assumption would fail and the whole system would need a refactoring
- assert(entry->parentEntry == &_map.find(entry->parent)->_value);
- return entry->parentEntry;
- } else {
- ResFileMap::iterator it = _map.find(entry->parent);
- if (it == _map.end())
- return 0; // If it happens often, the structure maybe deserves a flag to avoid rechecking the map
- else {
- entry->parentEntry = &it->_value;
- return entry->parentEntry;
- }
- }
-}
-
-ResFileEntry *Resource::getParentEntry(const Common::String &filename) const {
- ResFileMap::iterator it = _map.find(filename);
- assert(it != _map.end());
- return getParentEntry(&it->_value);
-}
-
-void Resource::checkFile(const Common::String &file) {
- if (_map.find(file) == _map.end()) {
- CompFileMap::const_iterator iter;
-
- if ((iter = _compFiles.find(file)) != _compFiles.end()) {
- ResFileEntry& entry = _map[file];
- entry.parent = "";
- entry.parentEntry = 0;
- entry.size = iter->_value.size;
- entry.mounted = false;
- entry.preload = false;
- entry.prot = false;
- entry.type = ResFileEntry::kAutoDetect;
- entry.offset = 0;
-
- detectFileType(file, &entry);
- } else if (Common::File::exists(file)) {
- Common::File temp;
- if (temp.open(file)) {
- ResFileEntry& entry = _map[file];
- entry.parent = "";
- entry.parentEntry = 0;
- entry.size = temp.size();
- entry.mounted = file.compareToIgnoreCase(StaticResource::staticDataFilename()) != 0;
- entry.preload = false;
- entry.prot = false;
- entry.type = ResFileEntry::kAutoDetect;
- entry.offset = 0;
- temp.close();
-
- detectFileType(file, &entry);
- }
- }
- }
-}
-
-void Resource::detectFileType(const Common::String &filename, ResFileEntry *fileEntry) {
- assert(fileEntry);
-
- if (!isAccessible(fileEntry))
- return;
-
- if (fileEntry->type == ResFileEntry::kAutoDetect) {
- Common::SeekableReadStream *stream = 0;
- for (LoaderIterator l = _loaders.begin(); l != _loaders.end(); ++l) {
- if (!(*l)->checkFilename(filename))
- continue;
-
- if (!stream)
- stream = getFileStream(filename);
-
- if ((*l)->isLoadable(filename, *stream)) {
- fileEntry->type = (*l)->getType();
- fileEntry->mounted = false;
- fileEntry->preload = false;
- break;
- }
- }
- delete stream;
- stream = 0;
-
- if (fileEntry->type == ResFileEntry::kAutoDetect)
- fileEntry->type = ResFileEntry::kRaw;
- }
+ return _files.openFile(file);
}
-void Resource::detectFileTypes() {
- for (ResFileMap::iterator i = _map.begin(); i != _map.end(); ++i)
- detectFileType(i->_key, &i->_value);
-}
-
-void Resource::tryLoadCompFiles() {
- for (CCompLoaderIterator i = _compLoaders.begin(); i != _compLoaders.end(); ++i) {
- if ((*i)->checkForFiles())
- (*i)->loadFile(_compFiles);
- }
-}
-
-#pragma mark -
-#pragma mark - ResFileLodaer
-#pragma mark -
-
-class ResLoaderPak : public ResArchiveLoader {
-public:
- bool checkFilename(Common::String filename) const;
- bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const;
- bool loadFile(const Common::String &filename, Common::SeekableReadStream &stream, FileList &files) const;
- Common::SeekableReadStream *loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileEntry entry) const;
-
- ResFileEntry::kType getType() const {
- return ResFileEntry::kPak;
- }
-};
-
-bool ResLoaderPak::checkFilename(Common::String filename) const {
- filename.toUppercase();
- return (filename.hasSuffix(".PAK") || filename.hasSuffix(".APK") || filename.hasSuffix(".VRM") || filename.hasSuffix(".TLK") || filename.equalsIgnoreCase(StaticResource::staticDataFilename()));
-}
-
-bool ResLoaderPak::isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const {
- uint32 filesize = stream.size();
- uint32 offset = 0;
- bool switchEndian = false;
- bool firstFile = true;
-
- offset = stream.readUint32LE();
- if (offset > filesize) {
- switchEndian = true;
- offset = SWAP_BYTES_32(offset);
- }
+Common::ArchivePtr Resource::loadArchive(const Common::String &file) {
+ ArchiveMap::iterator cachedArchive = _archiveCache.find(file);
+ if (cachedArchive != _archiveCache.end())
+ return cachedArchive->_value;
- Common::String file = "";
- while (!stream.eos()) {
- // The start offset of a file should never be in the filelist
- if (offset < stream.pos() || offset > filesize)
- return false;
-
- byte c = 0;
-
- file = "";
-
- while (!stream.eos() && (c = stream.readByte()) != 0)
- file += c;
-
- if (stream.eos())
- return false;
-
- // Quit now if we encounter an empty string
- if (file.empty()) {
- if (firstFile)
- return false;
- else
+ Common::SeekableReadStream *stream = getFileStream(file);
+ if (!stream)
+ return Common::ArchivePtr();
+
+ Common::ArchivePtr archive;
+ for (LoaderList::const_iterator i = _loaders.begin(); i != _loaders.end(); ++i) {
+ if ((*i)->checkFilename(file)) {
+ if ((*i)->isLoadable(file, *stream)) {
+ stream->seek(0, SEEK_SET);
+ archive = Common::ArchivePtr((*i)->load(this, file, *stream));
break;
- }
-
- firstFile = false;
- offset = switchEndian ? stream.readUint32BE() : stream.readUint32LE();
-
- if (!offset || offset == filesize)
- break;
- }
-
- return true;
-}
-
-namespace {
-
-Common::String readString(Common::SeekableReadStream &stream) {
- Common::String result;
- char c = 0;
-
- while ((c = stream.readByte()) != 0)
- result += c;
-
- return result;
-}
-
-} // end of anonymous namespace
-
-bool ResLoaderPak::loadFile(const Common::String &filename, Common::SeekableReadStream &stream, FileList &files) const {
- uint32 filesize = stream.size();
-
- uint32 startoffset = 0, endoffset = 0;
- bool switchEndian = false;
- bool firstFile = true;
-
- startoffset = stream.readUint32LE();
- if (startoffset > filesize) {
- switchEndian = true;
- startoffset = SWAP_BYTES_32(startoffset);
- }
-
- Common::String file = "";
- while (!stream.eos()) {
- // The start offset of a file should never be in the filelist
- if (startoffset < stream.pos() || startoffset > filesize) {
- warning("PAK file '%s' is corrupted", filename.c_str());
- return false;
- }
-
- file = "";
- byte c = 0;
-
- while (!stream.eos() && (c = stream.readByte()) != 0)
- file += c;
-
- if (stream.eos()) {
- warning("PAK file '%s' is corrupted", filename.c_str());
- return false;
- }
-
- // Quit now if we encounter an empty string
- if (file.empty()) {
- if (firstFile) {
- warning("PAK file '%s' is corrupted", filename.c_str());
- return false;
} else {
- break;
- }
- }
-
- firstFile = false;
- endoffset = switchEndian ? stream.readUint32BE() : stream.readUint32LE();
-
- if (!endoffset)
- endoffset = filesize;
-
- if (startoffset != endoffset) {
- ResFileEntry entry;
- entry.size = endoffset - startoffset;
- entry.offset = startoffset;
- entry.parent = filename;
- entry.parentEntry = 0;
- entry.type = ResFileEntry::kAutoDetect;
- entry.mounted = false;
- entry.prot = false;
- entry.preload = false;
-
- files.push_back(File(file, entry));
- }
-
- if (endoffset == filesize)
- break;
-
- startoffset = endoffset;
- }
-
- FileList::const_iterator iter = Common::find(files.begin(), files.end(), Common::String("LINKLIST"));
- if (iter != files.end()) {
- stream.seek(iter->entry.offset, SEEK_SET);
-
- uint32 magic = stream.readUint32BE();
-
- if (magic != MKID_BE('SCVM'))
- error("LINKLIST file does not contain 'SCVM' header");
-
- uint32 links = stream.readUint32BE();
- for (uint i = 0; i < links; ++i) {
- Common::String linksTo = readString(stream);
- uint32 sources = stream.readUint32BE();
-
- iter = Common::find(files.begin(), files.end(), linksTo);
- if (iter == files.end())
- error("PAK file link destination '%s' not found", linksTo.c_str());
-
- for (uint j = 0; j < sources; ++j) {
- Common::String dest = readString(stream);
- files.push_back(File(dest, iter->entry));
- // Better safe than sorry, we update the 'iter' value, in case push_back invalidated it
- iter = Common::find(files.begin(), files.end(), linksTo);
- }
- }
- }
-
- return true;
-}
-
-Common::SeekableReadStream *ResLoaderPak::loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileEntry entry) const {
- assert(archive);
-
- archive->seek(entry.offset, SEEK_SET);
- Common::SeekableSubReadStream *stream = new Common::SeekableSubReadStream(archive, entry.offset, entry.offset + entry.size, true);
- assert(stream);
- return stream;
-}
-
-class ResLoaderInsMalcolm : public ResArchiveLoader {
-public:
- bool checkFilename(Common::String filename) const;
- bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const;
- bool loadFile(const Common::String &filename, Common::SeekableReadStream &stream, FileList &files) const;
- Common::SeekableReadStream *loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileEntry entry) const;
-
- ResFileEntry::kType getType() const {
- return ResFileEntry::kInsMal;
- }
-};
-
-bool ResLoaderInsMalcolm::checkFilename(Common::String filename) const {
- filename.toUppercase();
- if (!filename.hasSuffix(".001"))
- return false;
- return true;
-}
-
-bool ResLoaderInsMalcolm::isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const {
- stream.seek(3);
- uint32 size = stream.readUint32LE();
-
- if (size+7 > stream.size())
- return false;
-
- stream.seek(size+5, SEEK_SET);
- uint8 buffer[2];
- stream.read(&buffer, 2);
-
- return (buffer[0] == 0x0D && buffer[1] == 0x0A);
-}
-
-bool ResLoaderInsMalcolm::loadFile(const Common::String &filename, Common::SeekableReadStream &stream, FileList &files) const {
- Common::List<Common::String> filenames;
-
- // thanks to eriktorbjorn for this code (a bit modified though)
- stream.seek(3, SEEK_SET);
-
- // first file is the index table
- uint32 size = stream.readUint32LE();
- Common::String temp = "";
-
- for (uint32 i = 0; i < size; ++i) {
- byte c = stream.readByte();
-
- if (c == '\\') {
- temp = "";
- } else if (c == 0x0D) {
- // line endings are CRLF
- c = stream.readByte();
- assert(c == 0x0A);
- ++i;
-
- filenames.push_back(temp);
- } else {
- temp += (char)c;
- }
- }
-
- stream.seek(3, SEEK_SET);
-
- for (Common::List<Common::String>::iterator file = filenames.begin(); file != filenames.end(); ++file) {
- ResFileEntry entry;
- entry.parent = filename;
- entry.parentEntry = 0;
- entry.type = ResFileEntry::kAutoDetect;
- entry.mounted = false;
- entry.preload = false;
- entry.prot = false;
- entry.size = stream.readUint32LE();
- entry.offset = stream.pos();
- stream.seek(entry.size, SEEK_CUR);
- files.push_back(File(*file, entry));
- }
-
- return true;
-}
-
-Common::SeekableReadStream *ResLoaderInsMalcolm::loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileEntry entry) const {
- assert(archive);
-
- archive->seek(entry.offset, SEEK_SET);
- Common::SeekableSubReadStream *stream = new Common::SeekableSubReadStream(archive, entry.offset, entry.offset + entry.size, true);
- assert(stream);
- return stream;
-}
-
-class ResLoaderTlk : public ResArchiveLoader {
-public:
- bool checkFilename(Common::String filename) const;
- bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const;
- bool loadFile(const Common::String &filename, Common::SeekableReadStream &stream, FileList &files) const;
- Common::SeekableReadStream *loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileEntry entry) const;
-
- ResFileEntry::kType getType() const {
- return ResFileEntry::kTlk;
- }
-
-private:
- static bool sortTlkFileList(const File &l, const File &r);
- static FileList::const_iterator nextFile(const FileList &list, FileList::const_iterator iter);
-};
-
-bool ResLoaderTlk::checkFilename(Common::String filename) const {
- filename.toUppercase();
- return (filename.hasSuffix(".TLK"));
-}
-
-bool ResLoaderTlk::isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const {
- uint16 entries = stream.readUint16LE();
- uint32 entryTableSize = (entries * 8);
-
- if (entryTableSize + 2 > stream.size())
- return false;
-
- uint32 offset = 0;
-
- for (uint i = 0; i < entries; ++i) {
- stream.readUint32LE();
- offset = stream.readUint32LE();
-
- if (offset > stream.size())
- return false;
- }
-
- return true;
-}
-
-bool ResLoaderTlk::loadFile(const Common::String &filename, Common::SeekableReadStream &stream, FileList &files) const {
- uint16 entries = stream.readUint16LE();
-
- for (uint i = 0; i < entries; ++i) {
- ResFileEntry entry;
- entry.parent = filename;
- entry.parentEntry = 0;
- entry.type = ResFileEntry::kAutoDetect;
- entry.mounted = false;
- entry.preload = false;
- entry.prot = false;
-
- uint32 resFilename = stream.readUint32LE();
- uint32 resOffset = stream.readUint32LE();
-
- entry.offset = resOffset+4;
-
- char realFilename[20];
- snprintf(realFilename, 20, "%.08u.AUD", resFilename);
-
- uint32 curOffset = stream.pos();
- stream.seek(resOffset, SEEK_SET);
- entry.size = stream.readUint32LE();
- stream.seek(curOffset, SEEK_SET);
-
- files.push_back(FileList::value_type(realFilename, entry));
- }
-
- return true;
-}
-
-Common::SeekableReadStream *ResLoaderTlk::loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileEntry entry) const {
- assert(archive);
-
- archive->seek(entry.offset, SEEK_SET);
- Common::SeekableSubReadStream *stream = new Common::SeekableSubReadStream(archive, entry.offset, entry.offset + entry.size, true);
- assert(stream);
- return stream;
-}
-
-#pragma mark -
-#pragma mark - CompFileLoader
-#pragma mark -
-
-class FileExpanderSource {
-public:
- FileExpanderSource(const uint8 *data, int dataSize) : _dataPtr(data), _endofBuffer(data + dataSize), _bitsLeft(8), _key(0), _index(0) {}
- ~FileExpanderSource() {}
-
- void advSrcRefresh();
- void advSrcBitsBy1();
- void advSrcBitsByIndex(uint8 newIndex);
-
- uint8 getKeyLower() { return _key & 0xff; }
- void setIndex(uint8 index) { _index = index; }
- uint16 getKeyMasked(uint8 newIndex);
- uint16 keyMaskedAlign(uint16 val);
-
- void copyBytes(uint8 *& dst);
-
-private:
- const uint8 *_dataPtr;
- const uint8 *_endofBuffer;
- uint16 _key;
- int8 _bitsLeft;
- uint8 _index;
-};
-
-void FileExpanderSource::advSrcBitsBy1() {
- _key >>= 1;
- if (!--_bitsLeft) {
- if (_dataPtr < _endofBuffer)
- _key = ((*_dataPtr++) << 8 ) | (_key & 0xff);
- _bitsLeft = 8;
- }
-}
-
-void FileExpanderSource::advSrcBitsByIndex(uint8 newIndex) {
- _index = newIndex;
- _bitsLeft -= _index;
- if (_bitsLeft <= 0) {
- _key >>= (_index + _bitsLeft);
- _index = -_bitsLeft;
- _bitsLeft = 8 - _index;
- if (_dataPtr < _endofBuffer)
- _key = (*_dataPtr++ << 8) | (_key & 0xff);
- }
- _key >>= _index;
-}
-
-uint16 FileExpanderSource::getKeyMasked(uint8 newIndex) {
- static const uint8 mskTable[] = { 0x0F, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF };
- _index = newIndex;
- uint16 res = 0;
-
- if (_index > 8) {
- newIndex = _index - 8;
- res = (_key & 0xff) & mskTable[8];
- advSrcBitsByIndex(8);
- _index = newIndex;
- res |= (((_key & 0xff) & mskTable[_index]) << 8);
- advSrcBitsByIndex(_index);
- } else {
- res = (_key & 0xff) & mskTable[_index];
- advSrcBitsByIndex(_index);
- }
-
- return res;
-}
-
-void FileExpanderSource::copyBytes(uint8 *& dst) {
- advSrcBitsByIndex(_bitsLeft);
- uint16 r = (READ_LE_UINT16(_dataPtr) ^ _key) + 1;
- _dataPtr += 2;
-
- if (r)
- error("decompression failure");
-
- memcpy(dst, _dataPtr, _key);
- _dataPtr += _key;
- dst += _key;
-}
-
-uint16 FileExpanderSource::keyMaskedAlign(uint16 val) {
- val -= 0x101;
- _index = (val & 0xff) >> 2;
- int16 b = ((_bitsLeft << 8) | _index) - 1;
- _bitsLeft = b >> 8;
- _index = b & 0xff;
- uint16 res = (((val & 3) + 4) << _index) + 0x101;
- return res + getKeyMasked(_index);
-}
-
-void FileExpanderSource::advSrcRefresh() {
- _key = READ_LE_UINT16(_dataPtr);
- if (_dataPtr < _endofBuffer - 1)
- _dataPtr += 2;
- _bitsLeft = 8;
-}
-
-class FileExpander {
-public:
- FileExpander();
- ~FileExpander();
-
- bool process(uint8 *dst, const uint8 *src, uint32 outsize, uint32 insize);
-
-private:
- void generateTables(uint8 srcIndex, uint8 dstIndex, uint8 dstIndex2, int cnt);
- uint8 calcCmdAndIndex(const uint8 *tbl, int16 &para);
-
- FileExpanderSource *_src;
- uint8 *_tables[9];
- uint16 *_tables16[3];
-};
-
-FileExpander::FileExpander() : _src(0) {
- _tables[0] = new uint8[3914];
- assert(_tables[0]);
-
- _tables[1] = _tables[0] + 320;
- _tables[2] = _tables[0] + 352;
- _tables[3] = _tables[0] + 864;
- _tables[4] = _tables[0] + 2016;
- _tables[5] = _tables[0] + 2528;
- _tables[6] = _tables[0] + 2656;
- _tables[7] = _tables[0] + 2736;
- _tables[8] = _tables[0] + 2756;
-
- _tables16[0] = (uint16 *)(_tables[0] + 3268);
- _tables16[1] = (uint16 *)(_tables[0] + 3302);
- _tables16[2] = (uint16 *)(_tables[0] + 3338);
-}
-
-FileExpander::~FileExpander() {
- delete _src;
- delete[] _tables[0];
-}
-
-bool FileExpander::process(uint8 *dst, const uint8 *src, uint32 outsize, uint32 compressedSize) {
- static const uint8 indexTable[] = {
- 0x10, 0x11, 0x12, 0x00, 0x08, 0x07, 0x09, 0x06, 0x0A,
- 0x05, 0x0B, 0x04, 0x0C, 0x03, 0x0D, 0x02, 0x0E, 0x01, 0x0F
- };
-
- memset(_tables[0], 0, 3914);
-
- uint8 *d = dst;
- uint16 tableSize0 = 0;
- uint16 tableSize1 = 0;
- bool needrefresh = true;
- bool postprocess = false;
-
- _src = new FileExpanderSource(src, compressedSize);
-
- while (d < dst + outsize) {
-
- if (needrefresh) {
- needrefresh = false;
- _src->advSrcRefresh();
- }
-
- _src->advSrcBitsBy1();
-
- int mode = _src->getKeyMasked(2) - 1;
- if (mode == 1) {
- tableSize0 = _src->getKeyMasked(5) + 257;
- tableSize1 = _src->getKeyMasked(5) + 1;
- memset(_tables[7], 0, 19);
-
- const uint8 *itbl = indexTable;
- int numbytes = _src->getKeyMasked(4) + 4;
-
- while (numbytes--)
- _tables[7][*itbl++] = _src->getKeyMasked(3);
-
- generateTables(7, 8, 255, 19);
-
- int cnt = tableSize0 + tableSize1;
- uint8 *tmp = _tables[0];
-
- while (cnt) {
- uint16 cmd = _src->getKeyLower();
- cmd = READ_LE_UINT16(&_tables[8][cmd << 1]);
- _src->advSrcBitsByIndex(_tables[7][cmd]);
-
- if (cmd < 16) {
- *tmp++ = cmd;
- cnt--;
- } else {
- uint8 tmpI = 0;
- if (cmd == 16) {
- cmd = _src->getKeyMasked(2) + 3;
- tmpI = *(tmp - 1);
- } else if (cmd == 17) {
- cmd = _src->getKeyMasked(3) + 3;
- } else {
- cmd = _src->getKeyMasked(7) + 11;
- }
- _src->setIndex(tmpI);
- memset(tmp, tmpI, cmd);
- tmp += cmd;
-
- cnt -= cmd;
- if (cnt < 0)
- error("decompression failure");
- }
+ stream->seek(0, SEEK_SET);
}
-
- memcpy(_tables[1], _tables[0] + tableSize0, tableSize1);
- generateTables(0, 2, 3, tableSize0);
- generateTables(1, 4, 5, tableSize1);
- postprocess = true;
- } else if (mode < 0) {
- _src->copyBytes(d);
- postprocess = false;
- needrefresh = true;
- } else if (mode == 0){
- uint8 *d2 = _tables[0];
- memset(d2, 8, 144);
- memset(d2 + 144, 9, 112);
- memset(d2 + 256, 7, 24);
- memset(d2 + 280, 8, 8);
- d2 = _tables[1];
- memset(d2, 5, 32);
- tableSize0 = 288;
- tableSize1 = 32;
-
- generateTables(0, 2, 3, tableSize0);
- generateTables(1, 4, 5, tableSize1);
- postprocess = true;
- } else {
- error("decompression failure");
}
-
- if (!postprocess)
- continue;
-
- int16 cmd = 0;
-
- do {
- cmd = ((int16*) _tables[2])[_src->getKeyLower()];
- _src->advSrcBitsByIndex(cmd < 0 ? calcCmdAndIndex(_tables[3], cmd) : _tables[0][cmd]);
-
- if (cmd == 0x11d) {
- cmd = 0x200;
- } else if (cmd > 0x108) {
- cmd = _src->keyMaskedAlign(cmd);
- }
-
- if (!(cmd >> 8)) {
- *d++ = cmd & 0xff;
- } else if (cmd != 0x100) {
- cmd -= 0xfe;
- int16 offset = ((int16*) _tables[4])[_src->getKeyLower()];
- _src->advSrcBitsByIndex(offset < 0 ? calcCmdAndIndex(_tables[5], offset) : _tables[1][offset]);
- if ((offset & 0xff) >= 4) {
- uint8 newIndex = ((offset & 0xff) >> 1) - 1;
- offset = (((offset & 1) + 2) << newIndex);
- offset += _src->getKeyMasked(newIndex);
- }
-
- uint8 *s2 = d - 1 - offset;
- if (s2 >= dst) {
- while (cmd--)
- *d++ = *s2++;
- } else {
- uint32 pos = dst - s2;
- s2 += (d - dst);
-
- if (pos < (uint32) cmd) {
- cmd -= pos;
- while (pos--)
- *d++ = *s2++;
- s2 = dst;
- }
- while (cmd--)
- *d++ = *s2++;
- }
- }
- } while (cmd != 0x100);
}
- delete _src;
- _src = 0;
-
- return true;
-}
-
-void FileExpander::generateTables(uint8 srcIndex, uint8 dstIndex, uint8 dstIndex2, int cnt) {
- const uint8 *tbl1 = _tables[srcIndex];
- uint8 *tbl2 = _tables[dstIndex];
- const uint8 *tbl3 = dstIndex2 == 0xff ? 0 : _tables[dstIndex2];
-
- if (!cnt)
- return;
-
- const uint8 *s = tbl1;
- memset(_tables16[0], 0, 32);
-
- for (int i = 0; i < cnt; i++)
- _tables16[0][(*s++)]++;
-
- _tables16[1][1] = 0;
-
- for (uint16 i = 1, r = 0; i < 16; i++) {
- r = (r + _tables16[0][i]) << 1;
- _tables16[1][i + 1] = r;
- }
-
- if (_tables16[1][16]) {
- uint16 r = 0;
- for (uint16 i = 1; i < 16; i++)
- r += _tables16[0][i];
- if (r > 1)
- error("decompression failure");
- }
-
- s = tbl1;
- uint16 *d = _tables16[2];
- for (int i = 0; i < cnt; i++) {
- uint16 t = *s++;
- if (t) {
- _tables16[1][t]++;
- t = _tables16[1][t] - 1;
- }
- *d++ = t;
- }
-
- s = tbl1;
- d = _tables16[2];
- for (int i = 0; i < cnt; i++) {
- int8 t = ((int8)(*s++)) - 1;
- if (t > 0) {
- uint16 v1 = *d;
- uint16 v2 = 0;
-
- do {
- v2 = (v2 << 1) | (v1 & 1);
- v1 >>= 1;
- } while (--t && v1);
-
- t++;
- uint8 c1 = (v1 & 1);
- while (t--) {
- uint8 c2 = v2 >> 15;
- v2 = (v2 << 1) | c1;
- c1 = c2;
- };
-
- *d++ = v2;
- } else {
- d++;
- }
- }
-
- memset(tbl2, 0, 512);
-
- cnt--;
- s = tbl1 + cnt;
- d = &_tables16[2][cnt];
- uint16 * bt = (uint16*) tbl3;
- uint16 inc = 0;
- uint16 cnt2 = 0;
-
- do {
- uint8 t = *s--;
- uint16 *s2 = (uint16*) tbl2;
-
- if (t && t < 9) {
- inc = 1 << t;
- uint16 o = *d;
-
- do {
- s2[o] = cnt;
- o += inc;
- } while (!(o & 0xf00));
-
- } else if (t > 8) {
- if (!bt)
- error("decompression failure");
-
- t -= 8;
- uint8 shiftCnt = 1;
- uint8 v = (*d) >> 8;
- s2 = &((uint16*) tbl2)[*d & 0xff];
-
- do {
- if (!*s2) {
- *s2 = (uint16)(~cnt2);
- *(uint32*)&bt[cnt2] = 0;
- cnt2 += 2;
- }
-
- s2 = &bt[(uint16)(~*s2)];
- if (v & shiftCnt)
- s2++;
-
- shiftCnt <<= 1;
- } while (--t);
- *s2 = cnt;
- }
- d--;
- } while (--cnt >= 0);
-}
-
-uint8 FileExpander::calcCmdAndIndex(const uint8 *tbl, int16 &para) {
- const uint16 *t = (const uint16*)tbl;
- _src->advSrcBitsByIndex(8);
- uint8 newIndex = 0;
- uint16 v = _src->getKeyLower();
-
- do {
- newIndex++;
- para = t[((~para) & 0xfffe) | (v & 1)];
- v >>= 1;
- } while (para < 0);
-
- return newIndex;
-}
-
-class CompLoaderInsHof : public CompArchiveLoader {
-public:
- CompLoaderInsHof() {
- _fileExtP = "%03d";
- _checkFile1 = "WESTWOOD.001";
- _checkFile2 = "WESTWOOD.002";
- _containerOffset = 6;
- }
+ delete stream;
- virtual bool checkForFiles() const;
- virtual bool loadFile(CompFileMap &loadTo) const;
-
-protected:
- struct Archive {
- Common::String filename;
- uint32 firstFile;
- uint32 startOffset;
- uint32 lastFile;
- uint32 endOffset;
- uint32 totalSize;
- };
-
- const char *_fileExtP;
- const char *_checkFile1;
- const char *_checkFile2;
- uint8 _containerOffset;
-};
-
-class CompLoaderInsLol : public CompLoaderInsHof {
-public:
- CompLoaderInsLol() {
- _fileExtP = "%d";
- _checkFile1 = "WESTWOOD.1";
- _checkFile2 = "WESTWOOD.2";
- _containerOffset = 0;
- }
-};
+ if (!archive)
+ return Common::ArchivePtr();
-bool CompLoaderInsHof::checkForFiles() const {
- return (Common::File::exists(_checkFile1) && Common::File::exists(_checkFile2));
+ _archiveCache[file] = archive;
+ return archive;
}
-bool CompLoaderInsHof::loadFile(CompFileMap &loadTo) const {
- Common::File tmpFile;
-
- uint32 pos = 0;
- uint32 bytesleft = 0;
- bool startFile = true;
-
- Common::String filenameBase = "WESTWOOD.";
- Common::String filenameTemp;
- char filenameExt[4];
-
- while (filenameBase.lastChar() != '.')
- filenameBase.deleteLastChar();
-
- Archive newArchive;
-
- Common::List<Archive> archives;
-
- for (int8 currentFile = 1; currentFile; currentFile++) {
- sprintf(filenameExt, _fileExtP, currentFile);
- filenameTemp = filenameBase + Common::String(filenameExt);
-
- if (!tmpFile.open(filenameTemp)) {
- debug(3, "couldn't open file '%s'\n", filenameTemp.c_str());
- break;
- }
-
- tmpFile.seek(pos);
- uint8 fileId = tmpFile.readByte();
- pos++;
-
- uint32 size = tmpFile.size() - 1;
- if (startFile) {
- size -= 4;
- if (fileId == currentFile) {
- size -= _containerOffset;
- pos += _containerOffset;
- tmpFile.seek(_containerOffset, SEEK_CUR);
- } else {
- size = size + 1 - pos;
- }
- newArchive.filename = filenameBase;
- bytesleft = newArchive.totalSize = tmpFile.readUint32LE();
- pos += 4;
- newArchive.firstFile = currentFile;
- newArchive.startOffset = pos;
- startFile = false;
- }
-
- uint32 cs = MIN(size, bytesleft);
- bytesleft -= cs;
-
- tmpFile.close();
-
- pos += cs;
- if (cs == size) {
- if (!bytesleft) {
- newArchive.lastFile = currentFile;
- newArchive.endOffset = --pos;
- archives.push_back(newArchive);
- currentFile = -1;
- } else {
- pos = 0;
- }
- } else {
- startFile = true;
- bytesleft = size - cs;
- newArchive.lastFile = currentFile--;
- newArchive.endOffset = --pos;
- archives.push_back(newArchive);
- }
- }
-
- FileExpander exp;
- CompFileEntry newEntry;
- uint32 insize = 0;
- uint32 outsize = 0;
- uint8 *inbuffer = 0;
- uint8 *outbuffer = 0;
- uint32 inPart1 = 0;
- uint32 inPart2 = 0;
- uint8 compressionType = 0;
- Common::String entryStr;
-
- pos = 0;
-
- const uint32 kExecSize = 0x0bba;
- const uint32 kHeaderSize = 30;
- const uint32 kHeaderSize2 = 46;
-
- for (Common::List<Archive>::iterator a = archives.begin(); a != archives.end(); ++a) {
- startFile = true;
- for (uint32 i = a->firstFile; i != (a->lastFile + 1); i++) {
- sprintf(filenameExt, _fileExtP, i);
- filenameTemp = a->filename + Common::String(filenameExt);
-
- if (!tmpFile.open(filenameTemp)) {
- debug(3, "couldn't open file '%s'\n", filenameTemp.c_str());
- break;
- }
-
- uint32 size = (i == a->lastFile) ? a->endOffset : tmpFile.size();
-
- if (startFile) {
- startFile = false;
- pos = a->startOffset + kExecSize;
- if (pos > size) {
- pos -= size;
- tmpFile.close();
- continue;
- }
- } else {
- if (inPart2) {
- tmpFile.seek(1);
- tmpFile.read(inbuffer + inPart1, inPart2);
- inPart2 = 0;
-
- if (compressionType > 0)
- exp.process(outbuffer, inbuffer, outsize, insize);
- else
- memcpy(outbuffer, inbuffer, outsize);
-
- delete[] inbuffer;
- inbuffer = 0;
- newEntry.data = outbuffer;
- newEntry.size = outsize;
- loadTo[entryStr] = newEntry;
- }
- pos++;
- }
+Common::ArchivePtr Resource::loadInstallerArchive(const Common::String &file, const Common::String &ext, const uint8 offset) {
+ ArchiveMap::iterator cachedArchive = _archiveCache.find(file);
+ if (cachedArchive != _archiveCache.end())
+ return cachedArchive->_value;
- while (pos < size) {
- uint8 hdr[43];
- uint32 m = 0;
- tmpFile.seek(pos);
-
- if (pos + 42 > size) {
- m = size - pos;
- uint32 b = 42 - m;
-
- if (m >= 4) {
- uint32 id = tmpFile.readUint32LE();
- if (id == 0x06054B50) {
- startFile = true;
- break;
- } else {
- tmpFile.seek(pos);
- }
- }
-
- sprintf(filenameExt, _fileExtP, i + 1);
- filenameTemp = a->filename + Common::String(filenameExt);
-
- Common::File tmpFile2;
- tmpFile2.open(filenameTemp);
- tmpFile.read(hdr, m);
- tmpFile2.read(hdr + m, b);
- tmpFile2.close();
-
- } else {
- tmpFile.read(hdr, 42);
- }
-
- uint32 id = READ_LE_UINT32(hdr);
-
- if (id == 0x04034B50) {
- compressionType = hdr[8];
- insize = READ_LE_UINT32(hdr + 18);
- outsize = READ_LE_UINT32(hdr + 22);
-
- uint16 filestrlen = READ_LE_UINT16(hdr + 26);
- *(hdr + 30 + filestrlen) = 0;
- entryStr = Common::String((const char *)(hdr + 30));
- pos += (kHeaderSize + filestrlen - m);
- tmpFile.seek(pos);
-
- outbuffer = new uint8[outsize];
- if (!outbuffer)
- error("Out of memory: Can't uncompress installer files");
-
- if (!inbuffer) {
- inbuffer = new uint8[insize];
- if (!inbuffer)
- error("Out of memory: Can't uncompress installer files");
- }
-
- if ((pos + insize) > size) {
- // this is for files that are split between two archive files
- inPart1 = size - pos;
- inPart2 = insize - inPart1;
- tmpFile.read(inbuffer, inPart1);
- } else {
- tmpFile.read(inbuffer, insize);
- inPart2 = 0;
-
- if (compressionType > 0)
- exp.process(outbuffer, inbuffer, outsize, insize);
- else
- memcpy(outbuffer, inbuffer, outsize);
-
- delete[] inbuffer;
- inbuffer = 0;
- newEntry.data = outbuffer;
- newEntry.size = outsize;
- loadTo[entryStr] = newEntry;
- }
-
- pos += insize;
- if (pos > size) {
- pos -= size;
- break;
- }
- } else {
- uint32 filestrlen = READ_LE_UINT32(hdr + 28);
- pos += (kHeaderSize2 + filestrlen - m);
- }
- }
- tmpFile.close();
- }
- }
+ Common::ArchivePtr archive(InstallerLoader::load(this, file, ext, offset));
+ if (!archive)
+ return Common::ArchivePtr();
- archives.clear();
- return true;
+ _archiveCache[file] = archive;
+ return archive;
}
#pragma mark -
@@ -1581,17 +320,6 @@ void Resource::initializeLoaders() {
_loaders.push_back(LoaderList::value_type(new ResLoaderPak()));
_loaders.push_back(LoaderList::value_type(new ResLoaderInsMalcolm()));
_loaders.push_back(LoaderList::value_type(new ResLoaderTlk()));
-
- _compLoaders.push_back(CompLoaderList::value_type(new CompLoaderInsHof()));
- _compLoaders.push_back(CompLoaderList::value_type(new CompLoaderInsLol()));
-}
-
-const ResArchiveLoader *Resource::getLoader(ResFileEntry::kType type) const {
- for (CLoaderIterator i = _loaders.begin(); i != _loaders.end(); ++i) {
- if ((*i)->getType() == type)
- return (*i).get();
- }
- return 0;
}
} // end of namespace Kyra
diff --git a/engines/kyra/resource.h b/engines/kyra/resource.h
index 5ecbd145fe..cfefa6e3b0 100644
--- a/engines/kyra/resource.h
+++ b/engines/kyra/resource.h
@@ -35,77 +35,16 @@
#include "common/hashmap.h"
#include "common/stream.h"
#include "common/ptr.h"
+#include "common/archive.h"
#include "kyra/kyra_v1.h"
#include "kyra/kyra_hof.h"
namespace Kyra {
-struct ResFileEntry {
- Common::String parent;
- mutable ResFileEntry *parentEntry; // Cache to avoid lookup by string in the map
- // No smart invalidation is needed because the map is cleared globally
- // or expanded but no element is ever removed
- uint32 size;
-
- bool preload;
- bool mounted;
- bool prot;
-
- enum kType {
- kRaw = 0,
- kPak = 1,
- kInsMal = 2,
- kTlk = 3,
- kAutoDetect
- };
- kType type;
- uint32 offset;
-};
-
-struct CompFileEntry {
- uint32 size;
- uint8 *data;
-};
-
-typedef Common::HashMap<Common::String, ResFileEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> ResFileMap;
-typedef Common::HashMap<Common::String, CompFileEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> CompFileMap;
class Resource;
-class ResArchiveLoader {
-public:
- struct File {
- File() : filename(), entry() {}
- File(const Common::String &f, const ResFileEntry &e) : filename(f), entry(e) {}
-
- bool operator ==(const Common::String &r) const {
- return filename.equalsIgnoreCase(r);
- }
-
- Common::String filename;
- ResFileEntry entry;
- };
- typedef Common::List<File> FileList;
-
- virtual ~ResArchiveLoader() {}
-
- virtual bool checkFilename(Common::String filename) const = 0;
- virtual bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const = 0;
- virtual bool loadFile(const Common::String &filename, Common::SeekableReadStream &stream, FileList &files) const = 0;
- // parameter 'archive' can be deleted by this method and it may not be deleted from the caller
- virtual Common::SeekableReadStream *loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileEntry entry) const = 0;
-
- virtual ResFileEntry::kType getType() const = 0;
-protected:
-};
-
-class CompArchiveLoader {
-public:
- virtual ~CompArchiveLoader() {}
-
- virtual bool checkForFiles() const = 0;
- virtual bool loadFile(CompFileMap &loadTo) const = 0;
-};
+class ResArchiveLoader;
class Resource {
public:
@@ -114,9 +53,9 @@ public:
bool reset();
- bool loadPakFile(const Common::String &filename);
- void unloadPakFile(const Common::String &filename);
- bool isInPakList(const Common::String &filename);
+ bool loadPakFile(Common::String filename);
+ void unloadPakFile(Common::String filename);
+ bool isInPakList(Common::String filename);
bool loadFileList(const Common::String &filedata);
bool loadFileList(const char * const *filelist, uint32 numFiles);
@@ -130,32 +69,20 @@ public:
bool loadFileToBuf(const char *file, void *buf, uint32 maxSize);
protected:
- void checkFile(const Common::String &file);
- bool isAccessible(const Common::String &file);
- bool isAccessible(const ResFileEntry *fileEntry);
+ typedef Common::HashMap<Common::String, Common::ArchivePtr, Common::CaseSensitiveString_Hash, Common::CaseSensitiveString_EqualTo> ArchiveMap;
+ ArchiveMap _archiveCache;
- void detectFileTypes();
- void detectFileType(const Common::String &filename, ResFileEntry *fileEntry);
+ Common::SearchSet _files;
+ Common::SharedPtr<Common::SearchSet> _archiveFiles;
+ Common::SharedPtr<Common::SearchSet> _protectedFiles;
+
+ Common::ArchivePtr loadArchive(const Common::String &file);
+ Common::ArchivePtr loadInstallerArchive(const Common::String &file, const Common::String &ext, const uint8 offset);
void initializeLoaders();
- const ResArchiveLoader *getLoader(ResFileEntry::kType type) const;
+
typedef Common::List<Common::SharedPtr<ResArchiveLoader> > LoaderList;
- typedef LoaderList::iterator LoaderIterator;
- typedef LoaderList::const_iterator CLoaderIterator;
LoaderList _loaders;
- ResFileMap _map;
-
- ResFileEntry *getParentEntry(const ResFileEntry *entry) const;
- ResFileEntry *getParentEntry(const Common::String &filename) const;
-
- typedef Common::List<Common::SharedPtr<CompArchiveLoader> > CompLoaderList;
- typedef CompLoaderList::iterator CompLoaderIterator;
- typedef CompLoaderList::const_iterator CCompLoaderIterator;
- CompLoaderList _compLoaders;
- CompFileMap _compFiles;
-
- void tryLoadCompFiles();
- void clearCompFileList();
KyraEngine_v1 *_vm;
};
diff --git a/engines/kyra/resource_intern.cpp b/engines/kyra/resource_intern.cpp
new file mode 100644
index 0000000000..e6b3b14c8a
--- /dev/null
+++ b/engines/kyra/resource_intern.cpp
@@ -0,0 +1,1072 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "kyra/resource_intern.h"
+#include "kyra/resource.h"
+
+#include "common/stream.h"
+#include "common/endian.h"
+
+namespace Kyra {
+
+// Implementation of various Archive subclasses
+
+// -> PlainArchive implementation
+
+PlainArchive::PlainArchive(Resource *owner, const Common::String &filename, const FileInputList &files)
+ : _owner(owner), _filename(filename), _files() {
+ for (FileInputList::iterator i = files.begin(); i != files.end(); ++i) {
+ Entry entry;
+
+ entry.offset = i->offset;
+ entry.size = i->size;
+
+ _files[i->name] = entry;
+ }
+}
+
+bool PlainArchive::hasFile(const Common::String &name) {
+ return (_files.find(name) != _files.end());
+}
+
+int PlainArchive::getAllNames(Common::StringList &list) {
+ int count = 0;
+
+ for (FileMap::const_iterator i = _files.begin(); i != _files.end(); ++i) {
+ list.push_back(i->_key);
+ ++count;
+ }
+
+ return count;
+}
+
+Common::SeekableReadStream *PlainArchive::openFile(const Common::String &name) {
+ FileMap::const_iterator fDesc = _files.find(name);
+ if (fDesc == _files.end())
+ return 0;
+
+ Common::SeekableReadStream *parent = _owner->getFileStream(_filename);
+ if (!parent)
+ return 0;
+
+ return new Common::SeekableSubReadStream(parent, fDesc->_value.offset, fDesc->_value.offset + fDesc->_value.size, true);
+}
+
+// -> CachedArchive implementation
+
+CachedArchive::CachedArchive(const FileInputList &files)
+ : _files() {
+ for (FileInputList::iterator i = files.begin(); i != files.end(); ++i) {
+ Entry entry;
+
+ entry.data = i->data;
+ entry.size = i->size;
+
+ _files[i->name] = entry;
+ }
+}
+
+CachedArchive::~CachedArchive() {
+ for (FileMap::iterator i = _files.begin(); i != _files.end(); ++i)
+ delete[] i->_value.data;
+ _files.clear();
+}
+
+bool CachedArchive::hasFile(const Common::String &name) {
+ return (_files.find(name) != _files.end());
+}
+
+int CachedArchive::getAllNames(Common::StringList &list) {
+ int count = 0;
+
+ for (FileMap::const_iterator i = _files.begin(); i != _files.end(); ++i) {
+ list.push_back(i->_key);
+ ++count;
+ }
+
+ return count;
+}
+
+Common::SeekableReadStream *CachedArchive::openFile(const Common::String &name) {
+ FileMap::const_iterator fDesc = _files.find(name);
+ if (fDesc == _files.end())
+ return 0;
+
+ return new Common::MemoryReadStream(fDesc->_value.data, fDesc->_value.size, false);
+}
+
+// ResFileLoader implementations
+
+// -> ResLoaderPak implementation
+
+bool ResLoaderPak::checkFilename(Common::String filename) const {
+ filename.toUppercase();
+ return (filename.hasSuffix(".PAK") || filename.hasSuffix(".APK") || filename.hasSuffix(".VRM") || filename.hasSuffix(".TLK") || filename.equalsIgnoreCase(StaticResource::staticDataFilename()));
+}
+
+bool ResLoaderPak::isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const {
+ uint32 filesize = stream.size();
+ uint32 offset = 0;
+ bool switchEndian = false;
+ bool firstFile = true;
+
+ offset = stream.readUint32LE();
+ if (offset > filesize) {
+ switchEndian = true;
+ offset = SWAP_BYTES_32(offset);
+ }
+
+ Common::String file = "";
+ while (!stream.eos()) {
+ // The start offset of a file should never be in the filelist
+ if (offset < stream.pos() || offset > filesize)
+ return false;
+
+ byte c = 0;
+
+ file = "";
+
+ while (!stream.eos() && (c = stream.readByte()) != 0)
+ file += c;
+
+ if (stream.eos())
+ return false;
+
+ // Quit now if we encounter an empty string
+ if (file.empty()) {
+ if (firstFile)
+ return false;
+ else
+ break;
+ }
+
+ firstFile = false;
+ offset = switchEndian ? stream.readUint32BE() : stream.readUint32LE();
+
+ if (!offset || offset == filesize)
+ break;
+ }
+
+ return true;
+}
+
+namespace {
+
+Common::String readString(Common::SeekableReadStream &stream) {
+ Common::String result;
+ char c = 0;
+
+ while ((c = stream.readByte()) != 0)
+ result += c;
+
+ return result;
+}
+
+struct PlainArchiveListSearch {
+ PlainArchiveListSearch(const Common::String &search) : _search(search) {}
+
+ bool operator()(const PlainArchive::InputEntry &entry) {
+ return _search.equalsIgnoreCase(entry.name);
+ }
+ Common::String _search;
+};
+
+} // end of anonymous namespace
+
+Common::Archive *ResLoaderPak::load(Resource *owner, const Common::String &filename, Common::SeekableReadStream &stream) const {
+ uint32 filesize = stream.size();
+
+ uint32 startoffset = 0, endoffset = 0;
+ bool switchEndian = false;
+ bool firstFile = true;
+
+ startoffset = stream.readUint32LE();
+ if (startoffset > filesize) {
+ switchEndian = true;
+ startoffset = SWAP_BYTES_32(startoffset);
+ }
+
+ PlainArchive::FileInputList files;
+
+ Common::String file = "";
+ while (!stream.eos()) {
+ // The start offset of a file should never be in the filelist
+ if (startoffset < stream.pos() || startoffset > filesize) {
+ warning("PAK file '%s' is corrupted", filename.c_str());
+ return false;
+ }
+
+ file = "";
+ byte c = 0;
+
+ while (!stream.eos() && (c = stream.readByte()) != 0)
+ file += c;
+
+ if (stream.eos()) {
+ warning("PAK file '%s' is corrupted", filename.c_str());
+ return false;
+ }
+
+ // Quit now if we encounter an empty string
+ if (file.empty()) {
+ if (firstFile) {
+ warning("PAK file '%s' is corrupted", filename.c_str());
+ return false;
+ } else {
+ break;
+ }
+ }
+
+ firstFile = false;
+ endoffset = switchEndian ? stream.readUint32BE() : stream.readUint32LE();
+
+ if (!endoffset)
+ endoffset = filesize;
+
+ if (startoffset != endoffset) {
+ PlainArchive::InputEntry entry;
+ entry.size = endoffset - startoffset;
+ entry.offset = startoffset;
+ entry.name = file;
+
+ files.push_back(entry);
+ }
+
+ if (endoffset == filesize)
+ break;
+
+ startoffset = endoffset;
+ }
+
+ PlainArchive::FileInputList::const_iterator iter = Common::find_if(files.begin(), files.end(), PlainArchiveListSearch("LINKLIST"));
+ if (iter != files.end()) {
+ stream.seek(iter->offset, SEEK_SET);
+
+ uint32 magic = stream.readUint32BE();
+
+ if (magic != MKID_BE('SCVM'))
+ error("LINKLIST file does not contain 'SCVM' header");
+
+ uint32 links = stream.readUint32BE();
+ for (uint i = 0; i < links; ++i) {
+ Common::String linksTo = readString(stream);
+ uint32 sources = stream.readUint32BE();
+
+ iter = Common::find_if(files.begin(), files.end(), PlainArchiveListSearch(linksTo));
+ if (iter == files.end())
+ error("PAK file link destination '%s' not found", linksTo.c_str());
+
+ for (uint j = 0; j < sources; ++j) {
+ Common::String dest = readString(stream);
+ files.push_back(*iter);
+ // Better safe than sorry, we update the 'iter' value, in case push_back invalidated it
+ iter = Common::find_if(files.begin(), files.end(), PlainArchiveListSearch(linksTo));
+ }
+ }
+ }
+
+ return new PlainArchive(owner, filename, files);
+}
+
+// -> ResLoaderInsMalcolm implementation
+
+bool ResLoaderInsMalcolm::checkFilename(Common::String filename) const {
+ filename.toUppercase();
+ if (!filename.hasSuffix(".001"))
+ return false;
+ return true;
+}
+
+bool ResLoaderInsMalcolm::isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const {
+ stream.seek(3, SEEK_SET);
+ uint32 size = stream.readUint32LE();
+
+ if (size+7 > stream.size())
+ return false;
+
+ stream.seek(size+5, SEEK_SET);
+ uint8 buffer[2];
+ stream.read(&buffer, 2);
+
+ return (buffer[0] == 0x0D && buffer[1] == 0x0A);
+}
+
+Common::Archive *ResLoaderInsMalcolm::load(Resource *owner, const Common::String &filename, Common::SeekableReadStream &stream) const {
+ Common::List<Common::String> filenames;
+ PlainArchive::FileInputList files;
+
+ // thanks to eriktorbjorn for this code (a bit modified though)
+ stream.seek(3, SEEK_SET);
+
+ // first file is the index table
+ uint32 size = stream.readUint32LE();
+ Common::String temp = "";
+
+ for (uint32 i = 0; i < size; ++i) {
+ byte c = stream.readByte();
+
+ if (c == '\\') {
+ temp = "";
+ } else if (c == 0x0D) {
+ // line endings are CRLF
+ c = stream.readByte();
+ assert(c == 0x0A);
+ ++i;
+
+ filenames.push_back(temp);
+ } else {
+ temp += (char)c;
+ }
+ }
+
+ stream.seek(3, SEEK_SET);
+
+ for (Common::List<Common::String>::iterator file = filenames.begin(); file != filenames.end(); ++file) {
+ PlainArchive::InputEntry entry;
+ entry.size = stream.readUint32LE();
+ entry.offset = stream.pos();
+ entry.name = *file;
+ stream.seek(entry.size, SEEK_CUR);
+
+ files.push_back(entry);
+ }
+
+ return new PlainArchive(owner, filename, files);
+}
+
+bool ResLoaderTlk::checkFilename(Common::String filename) const {
+ filename.toUppercase();
+ return (filename.hasSuffix(".TLK"));
+}
+
+bool ResLoaderTlk::isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const {
+ uint16 entries = stream.readUint16LE();
+ uint32 entryTableSize = (entries * 8);
+
+ if (entryTableSize + 2 > stream.size())
+ return false;
+
+ uint32 offset = 0;
+
+ for (uint i = 0; i < entries; ++i) {
+ stream.readUint32LE();
+ offset = stream.readUint32LE();
+
+ if (offset > stream.size())
+ return false;
+ }
+
+ return true;
+}
+
+Common::Archive *ResLoaderTlk::load(Resource *owner, const Common::String &filename, Common::SeekableReadStream &stream) const {
+ uint16 entries = stream.readUint16LE();
+ PlainArchive::FileInputList files;
+
+ for (uint i = 0; i < entries; ++i) {
+ PlainArchive::InputEntry entry;
+
+ uint32 resFilename = stream.readUint32LE();
+ uint32 resOffset = stream.readUint32LE();
+
+ entry.offset = resOffset+4;
+
+ char realFilename[20];
+ snprintf(realFilename, 20, "%.08u.AUD", resFilename);
+ entry.name = realFilename;
+
+ uint32 curOffset = stream.pos();
+ stream.seek(resOffset, SEEK_SET);
+ entry.size = stream.readUint32LE();
+ stream.seek(curOffset, SEEK_SET);
+
+ files.push_back(entry);
+ }
+
+ return new PlainArchive(owner, filename, files);
+}
+
+// InstallerLoader implementation
+
+class FileExpanderSource {
+public:
+ FileExpanderSource(const uint8 *data, int dataSize) : _dataPtr(data), _endofBuffer(data + dataSize), _bitsLeft(8), _key(0), _index(0) {}
+ ~FileExpanderSource() {}
+
+ void advSrcRefresh();
+ void advSrcBitsBy1();
+ void advSrcBitsByIndex(uint8 newIndex);
+
+ uint8 getKeyLower() { return _key & 0xff; }
+ void setIndex(uint8 index) { _index = index; }
+ uint16 getKeyMasked(uint8 newIndex);
+ uint16 keyMaskedAlign(uint16 val);
+
+ void copyBytes(uint8 *& dst);
+
+private:
+ const uint8 *_dataPtr;
+ const uint8 *_endofBuffer;
+ uint16 _key;
+ int8 _bitsLeft;
+ uint8 _index;
+};
+
+void FileExpanderSource::advSrcBitsBy1() {
+ _key >>= 1;
+ if (!--_bitsLeft) {
+ if (_dataPtr < _endofBuffer)
+ _key = ((*_dataPtr++) << 8 ) | (_key & 0xff);
+ _bitsLeft = 8;
+ }
+}
+
+void FileExpanderSource::advSrcBitsByIndex(uint8 newIndex) {
+ _index = newIndex;
+ _bitsLeft -= _index;
+ if (_bitsLeft <= 0) {
+ _key >>= (_index + _bitsLeft);
+ _index = -_bitsLeft;
+ _bitsLeft = 8 - _index;
+ if (_dataPtr < _endofBuffer)
+ _key = (*_dataPtr++ << 8) | (_key & 0xff);
+ }
+ _key >>= _index;
+}
+
+uint16 FileExpanderSource::getKeyMasked(uint8 newIndex) {
+ static const uint8 mskTable[] = { 0x0F, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF };
+ _index = newIndex;
+ uint16 res = 0;
+
+ if (_index > 8) {
+ newIndex = _index - 8;
+ res = (_key & 0xff) & mskTable[8];
+ advSrcBitsByIndex(8);
+ _index = newIndex;
+ res |= (((_key & 0xff) & mskTable[_index]) << 8);
+ advSrcBitsByIndex(_index);
+ } else {
+ res = (_key & 0xff) & mskTable[_index];
+ advSrcBitsByIndex(_index);
+ }
+
+ return res;
+}
+
+void FileExpanderSource::copyBytes(uint8 *& dst) {
+ advSrcBitsByIndex(_bitsLeft);
+ uint16 r = (READ_LE_UINT16(_dataPtr) ^ _key) + 1;
+ _dataPtr += 2;
+
+ if (r)
+ error("decompression failure");
+
+ memcpy(dst, _dataPtr, _key);
+ _dataPtr += _key;
+ dst += _key;
+}
+
+uint16 FileExpanderSource::keyMaskedAlign(uint16 val) {
+ val -= 0x101;
+ _index = (val & 0xff) >> 2;
+ int16 b = ((_bitsLeft << 8) | _index) - 1;
+ _bitsLeft = b >> 8;
+ _index = b & 0xff;
+ uint16 res = (((val & 3) + 4) << _index) + 0x101;
+ return res + getKeyMasked(_index);
+}
+
+void FileExpanderSource::advSrcRefresh() {
+ _key = READ_LE_UINT16(_dataPtr);
+ if (_dataPtr < _endofBuffer - 1)
+ _dataPtr += 2;
+ _bitsLeft = 8;
+}
+
+class FileExpander {
+public:
+ FileExpander();
+ ~FileExpander();
+
+ bool process(uint8 *dst, const uint8 *src, uint32 outsize, uint32 insize);
+
+private:
+ void generateTables(uint8 srcIndex, uint8 dstIndex, uint8 dstIndex2, int cnt);
+ uint8 calcCmdAndIndex(const uint8 *tbl, int16 &para);
+
+ FileExpanderSource *_src;
+ uint8 *_tables[9];
+ uint16 *_tables16[3];
+};
+
+FileExpander::FileExpander() : _src(0) {
+ _tables[0] = new uint8[3914];
+ assert(_tables[0]);
+
+ _tables[1] = _tables[0] + 320;
+ _tables[2] = _tables[0] + 352;
+ _tables[3] = _tables[0] + 864;
+ _tables[4] = _tables[0] + 2016;
+ _tables[5] = _tables[0] + 2528;
+ _tables[6] = _tables[0] + 2656;
+ _tables[7] = _tables[0] + 2736;
+ _tables[8] = _tables[0] + 2756;
+
+ _tables16[0] = (uint16 *)(_tables[0] + 3268);
+ _tables16[1] = (uint16 *)(_tables[0] + 3302);
+ _tables16[2] = (uint16 *)(_tables[0] + 3338);
+}
+
+FileExpander::~FileExpander() {
+ delete _src;
+ delete[] _tables[0];
+}
+
+bool FileExpander::process(uint8 *dst, const uint8 *src, uint32 outsize, uint32 compressedSize) {
+ static const uint8 indexTable[] = {
+ 0x10, 0x11, 0x12, 0x00, 0x08, 0x07, 0x09, 0x06, 0x0A,
+ 0x05, 0x0B, 0x04, 0x0C, 0x03, 0x0D, 0x02, 0x0E, 0x01, 0x0F
+ };
+
+ memset(_tables[0], 0, 3914);
+
+ uint8 *d = dst;
+ uint16 tableSize0 = 0;
+ uint16 tableSize1 = 0;
+ bool needrefresh = true;
+ bool postprocess = false;
+
+ _src = new FileExpanderSource(src, compressedSize);
+
+ while (d < dst + outsize) {
+
+ if (needrefresh) {
+ needrefresh = false;
+ _src->advSrcRefresh();
+ }
+
+ _src->advSrcBitsBy1();
+
+ int mode = _src->getKeyMasked(2) - 1;
+ if (mode == 1) {
+ tableSize0 = _src->getKeyMasked(5) + 257;
+ tableSize1 = _src->getKeyMasked(5) + 1;
+ memset(_tables[7], 0, 19);
+
+ const uint8 *itbl = indexTable;
+ int numbytes = _src->getKeyMasked(4) + 4;
+
+ while (numbytes--)
+ _tables[7][*itbl++] = _src->getKeyMasked(3);
+
+ generateTables(7, 8, 255, 19);
+
+ int cnt = tableSize0 + tableSize1;
+ uint8 *tmp = _tables[0];
+
+ while (cnt) {
+ uint16 cmd = _src->getKeyLower();
+ cmd = READ_LE_UINT16(&_tables[8][cmd << 1]);
+ _src->advSrcBitsByIndex(_tables[7][cmd]);
+
+ if (cmd < 16) {
+ *tmp++ = cmd;
+ cnt--;
+ } else {
+ uint8 tmpI = 0;
+ if (cmd == 16) {
+ cmd = _src->getKeyMasked(2) + 3;
+ tmpI = *(tmp - 1);
+ } else if (cmd == 17) {
+ cmd = _src->getKeyMasked(3) + 3;
+ } else {
+ cmd = _src->getKeyMasked(7) + 11;
+ }
+ _src->setIndex(tmpI);
+ memset(tmp, tmpI, cmd);
+ tmp += cmd;
+
+ cnt -= cmd;
+ if (cnt < 0)
+ error("decompression failure");
+ }
+ }
+
+ memcpy(_tables[1], _tables[0] + tableSize0, tableSize1);
+ generateTables(0, 2, 3, tableSize0);
+ generateTables(1, 4, 5, tableSize1);
+ postprocess = true;
+ } else if (mode < 0) {
+ _src->copyBytes(d);
+ postprocess = false;
+ needrefresh = true;
+ } else if (mode == 0){
+ uint8 *d2 = _tables[0];
+ memset(d2, 8, 144);
+ memset(d2 + 144, 9, 112);
+ memset(d2 + 256, 7, 24);
+ memset(d2 + 280, 8, 8);
+ d2 = _tables[1];
+ memset(d2, 5, 32);
+ tableSize0 = 288;
+ tableSize1 = 32;
+
+ generateTables(0, 2, 3, tableSize0);
+ generateTables(1, 4, 5, tableSize1);
+ postprocess = true;
+ } else {
+ error("decompression failure");
+ }
+
+ if (!postprocess)
+ continue;
+
+ int16 cmd = 0;
+
+ do {
+ cmd = ((int16*) _tables[2])[_src->getKeyLower()];
+ _src->advSrcBitsByIndex(cmd < 0 ? calcCmdAndIndex(_tables[3], cmd) : _tables[0][cmd]);
+
+ if (cmd == 0x11d) {
+ cmd = 0x200;
+ } else if (cmd > 0x108) {
+ cmd = _src->keyMaskedAlign(cmd);
+ }
+
+ if (!(cmd >> 8)) {
+ *d++ = cmd & 0xff;
+ } else if (cmd != 0x100) {
+ cmd -= 0xfe;
+ int16 offset = ((int16*) _tables[4])[_src->getKeyLower()];
+ _src->advSrcBitsByIndex(offset < 0 ? calcCmdAndIndex(_tables[5], offset) : _tables[1][offset]);
+ if ((offset & 0xff) >= 4) {
+ uint8 newIndex = ((offset & 0xff) >> 1) - 1;
+ offset = (((offset & 1) + 2) << newIndex);
+ offset += _src->getKeyMasked(newIndex);
+ }
+
+ uint8 *s2 = d - 1 - offset;
+ if (s2 >= dst) {
+ while (cmd--)
+ *d++ = *s2++;
+ } else {
+ uint32 pos = dst - s2;
+ s2 += (d - dst);
+
+ if (pos < (uint32) cmd) {
+ cmd -= pos;
+ while (pos--)
+ *d++ = *s2++;
+ s2 = dst;
+ }
+ while (cmd--)
+ *d++ = *s2++;
+ }
+ }
+ } while (cmd != 0x100);
+ }
+
+ delete _src;
+ _src = 0;
+
+ return true;
+}
+
+void FileExpander::generateTables(uint8 srcIndex, uint8 dstIndex, uint8 dstIndex2, int cnt) {
+ const uint8 *tbl1 = _tables[srcIndex];
+ uint8 *tbl2 = _tables[dstIndex];
+ const uint8 *tbl3 = dstIndex2 == 0xff ? 0 : _tables[dstIndex2];
+
+ if (!cnt)
+ return;
+
+ const uint8 *s = tbl1;
+ memset(_tables16[0], 0, 32);
+
+ for (int i = 0; i < cnt; i++)
+ _tables16[0][(*s++)]++;
+
+ _tables16[1][1] = 0;
+
+ for (uint16 i = 1, r = 0; i < 16; i++) {
+ r = (r + _tables16[0][i]) << 1;
+ _tables16[1][i + 1] = r;
+ }
+
+ if (_tables16[1][16]) {
+ uint16 r = 0;
+ for (uint16 i = 1; i < 16; i++)
+ r += _tables16[0][i];
+ if (r > 1)
+ error("decompression failure");
+ }
+
+ s = tbl1;
+ uint16 *d = _tables16[2];
+ for (int i = 0; i < cnt; i++) {
+ uint16 t = *s++;
+ if (t) {
+ _tables16[1][t]++;
+ t = _tables16[1][t] - 1;
+ }
+ *d++ = t;
+ }
+
+ s = tbl1;
+ d = _tables16[2];
+ for (int i = 0; i < cnt; i++) {
+ int8 t = ((int8)(*s++)) - 1;
+ if (t > 0) {
+ uint16 v1 = *d;
+ uint16 v2 = 0;
+
+ do {
+ v2 = (v2 << 1) | (v1 & 1);
+ v1 >>= 1;
+ } while (--t && v1);
+
+ t++;
+ uint8 c1 = (v1 & 1);
+ while (t--) {
+ uint8 c2 = v2 >> 15;
+ v2 = (v2 << 1) | c1;
+ c1 = c2;
+ };
+
+ *d++ = v2;
+ } else {
+ d++;
+ }
+ }
+
+ memset(tbl2, 0, 512);
+
+ cnt--;
+ s = tbl1 + cnt;
+ d = &_tables16[2][cnt];
+ uint16 * bt = (uint16*) tbl3;
+ uint16 inc = 0;
+ uint16 cnt2 = 0;
+
+ do {
+ uint8 t = *s--;
+ uint16 *s2 = (uint16*) tbl2;
+
+ if (t && t < 9) {
+ inc = 1 << t;
+ uint16 o = *d;
+
+ do {
+ s2[o] = cnt;
+ o += inc;
+ } while (!(o & 0xf00));
+
+ } else if (t > 8) {
+ if (!bt)
+ error("decompression failure");
+
+ t -= 8;
+ uint8 shiftCnt = 1;
+ uint8 v = (*d) >> 8;
+ s2 = &((uint16*) tbl2)[*d & 0xff];
+
+ do {
+ if (!*s2) {
+ *s2 = (uint16)(~cnt2);
+ *(uint32*)&bt[cnt2] = 0;
+ cnt2 += 2;
+ }
+
+ s2 = &bt[(uint16)(~*s2)];
+ if (v & shiftCnt)
+ s2++;
+
+ shiftCnt <<= 1;
+ } while (--t);
+ *s2 = cnt;
+ }
+ d--;
+ } while (--cnt >= 0);
+}
+
+uint8 FileExpander::calcCmdAndIndex(const uint8 *tbl, int16 &para) {
+ const uint16 *t = (const uint16*)tbl;
+ _src->advSrcBitsByIndex(8);
+ uint8 newIndex = 0;
+ uint16 v = _src->getKeyLower();
+
+ do {
+ newIndex++;
+ para = t[((~para) & 0xfffe) | (v & 1)];
+ v >>= 1;
+ } while (para < 0);
+
+ return newIndex;
+}
+
+namespace {
+
+struct InsArchive {
+ Common::String filename;
+ uint32 firstFile;
+ uint32 startOffset;
+ uint32 lastFile;
+ uint32 endOffset;
+ uint32 totalSize;
+};
+
+} // end of anonymouse namespace
+
+Common::Archive *InstallerLoader::load(Resource *owner, const Common::String &filename, const Common::String &extension, const uint8 containerOffset) {
+ uint32 pos = 0;
+ uint32 bytesleft = 0;
+ bool startFile = true;
+
+ Common::String filenameBase =filename;
+ Common::String filenameTemp;
+ char filenameExt[4];
+
+ if (filenameBase.lastChar() != '.')
+ filenameBase += '.';
+
+ InsArchive newArchive;
+ Common::List<InsArchive> archives;
+
+ Common::SeekableReadStream *tmpFile = 0;
+
+ for (int8 currentFile = 1; currentFile; currentFile++) {
+ sprintf(filenameExt, extension.c_str(), currentFile);
+ filenameTemp = filenameBase + Common::String(filenameExt);
+
+ if (!(tmpFile = owner->getFileStream(filenameTemp))) {
+ debug(3, "couldn't open file '%s'\n", filenameTemp.c_str());
+ break;
+ }
+
+ tmpFile->seek(pos, SEEK_SET);
+ uint8 fileId = tmpFile->readByte();
+ pos++;
+
+ uint32 size = tmpFile->size() - 1;
+ if (startFile) {
+ size -= 4;
+ if (fileId == currentFile) {
+ size -= containerOffset;
+ pos += containerOffset;
+ tmpFile->seek(containerOffset, SEEK_CUR);
+ } else {
+ size = size + 1 - pos;
+ }
+ newArchive.filename = filenameBase;
+ bytesleft = newArchive.totalSize = tmpFile->readUint32LE();
+ pos += 4;
+ newArchive.firstFile = currentFile;
+ newArchive.startOffset = pos;
+ startFile = false;
+ }
+
+ uint32 cs = MIN(size, bytesleft);
+ bytesleft -= cs;
+
+ delete tmpFile;
+ tmpFile = 0;
+
+ pos += cs;
+ if (cs == size) {
+ if (!bytesleft) {
+ newArchive.lastFile = currentFile;
+ newArchive.endOffset = --pos;
+ archives.push_back(newArchive);
+ currentFile = -1;
+ } else {
+ pos = 0;
+ }
+ } else {
+ startFile = true;
+ bytesleft = size - cs;
+ newArchive.lastFile = currentFile--;
+ newArchive.endOffset = --pos;
+ archives.push_back(newArchive);
+ }
+ }
+
+ FileExpander exp;
+ CachedArchive::InputEntry newEntry;
+ uint32 insize = 0;
+ uint32 outsize = 0;
+ uint8 *inbuffer = 0;
+ uint8 *outbuffer = 0;
+ uint32 inPart1 = 0;
+ uint32 inPart2 = 0;
+ uint8 compressionType = 0;
+ Common::String entryStr;
+
+ CachedArchive::FileInputList fileList;
+
+ pos = 0;
+
+ const uint32 kExecSize = 0x0bba;
+ const uint32 kHeaderSize = 30;
+ const uint32 kHeaderSize2 = 46;
+
+ for (Common::List<InsArchive>::iterator a = archives.begin(); a != archives.end(); ++a) {
+ startFile = true;
+ for (uint32 i = a->firstFile; i != (a->lastFile + 1); i++) {
+ sprintf(filenameExt, extension.c_str(), i);
+ filenameTemp = a->filename + Common::String(filenameExt);
+
+ if (!(tmpFile = owner->getFileStream(filenameTemp))) {
+ debug(3, "couldn't open file '%s'\n", filenameTemp.c_str());
+ break;
+ }
+
+ uint32 size = (i == a->lastFile) ? a->endOffset : tmpFile->size();
+
+ if (startFile) {
+ startFile = false;
+ pos = a->startOffset + kExecSize;
+ if (pos > size) {
+ pos -= size;
+ delete tmpFile;
+ tmpFile = 0;
+ continue;
+ }
+ } else {
+ if (inPart2) {
+ tmpFile->seek(1, SEEK_SET);
+ tmpFile->read(inbuffer + inPart1, inPart2);
+ inPart2 = 0;
+
+ if (compressionType > 0)
+ exp.process(outbuffer, inbuffer, outsize, insize);
+ else
+ memcpy(outbuffer, inbuffer, outsize);
+
+ delete[] inbuffer;
+ inbuffer = 0;
+ newEntry.data = outbuffer;
+ newEntry.size = outsize;
+ newEntry.name = entryStr;
+ fileList.push_back(newEntry);
+ }
+ pos++;
+ }
+
+ while (pos < size) {
+ uint8 hdr[43];
+ uint32 m = 0;
+ tmpFile->seek(pos, SEEK_SET);
+
+ if (pos + 42 > size) {
+ m = size - pos;
+ uint32 b = 42 - m;
+
+ if (m >= 4) {
+ uint32 id = tmpFile->readUint32LE();
+ if (id == 0x06054B50) {
+ startFile = true;
+ break;
+ } else {
+ tmpFile->seek(pos, SEEK_SET);
+ }
+ }
+
+ sprintf(filenameExt, extension.c_str(), i + 1);
+ filenameTemp = a->filename + Common::String(filenameExt);
+
+ Common::SeekableReadStream *tmpFile2 = owner->getFileStream(filenameTemp);
+ tmpFile->read(hdr, m);
+ tmpFile2->read(hdr + m, b);
+ delete tmpFile2;
+ } else {
+ tmpFile->read(hdr, 42);
+ }
+
+ uint32 id = READ_LE_UINT32(hdr);
+
+ if (id == 0x04034B50) {
+ compressionType = hdr[8];
+ insize = READ_LE_UINT32(hdr + 18);
+ outsize = READ_LE_UINT32(hdr + 22);
+
+ uint16 filestrlen = READ_LE_UINT16(hdr + 26);
+ *(hdr + 30 + filestrlen) = 0;
+ entryStr = Common::String((const char *)(hdr + 30));
+ pos += (kHeaderSize + filestrlen - m);
+ tmpFile->seek(pos, SEEK_SET);
+
+ outbuffer = new uint8[outsize];
+ if (!outbuffer)
+ error("Out of memory: Can't uncompress installer files");
+
+ if (!inbuffer) {
+ inbuffer = new uint8[insize];
+ if (!inbuffer)
+ error("Out of memory: Can't uncompress installer files");
+ }
+
+ if ((pos + insize) > size) {
+ // this is for files that are split between two archive files
+ inPart1 = size - pos;
+ inPart2 = insize - inPart1;
+ tmpFile->read(inbuffer, inPart1);
+ } else {
+ tmpFile->read(inbuffer, insize);
+ inPart2 = 0;
+
+ if (compressionType > 0)
+ exp.process(outbuffer, inbuffer, outsize, insize);
+ else
+ memcpy(outbuffer, inbuffer, outsize);
+
+ delete[] inbuffer;
+ inbuffer = 0;
+ newEntry.data = outbuffer;
+ newEntry.size = outsize;
+ newEntry.name = entryStr;
+ fileList.push_back(newEntry);
+ }
+
+ pos += insize;
+ if (pos > size) {
+ pos -= size;
+ break;
+ }
+ } else {
+ uint32 filestrlen = READ_LE_UINT32(hdr + 28);
+ pos += (kHeaderSize2 + filestrlen - m);
+ }
+ }
+ delete tmpFile;
+ tmpFile = 0;
+ }
+ }
+
+ archives.clear();
+ return new CachedArchive(fileList);
+}
+
+} // end of namespace Kyra
diff --git a/engines/kyra/resource_intern.h b/engines/kyra/resource_intern.h
new file mode 100644
index 0000000000..13546d7768
--- /dev/null
+++ b/engines/kyra/resource_intern.h
@@ -0,0 +1,131 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef KYRA_RESOURCE_INTERN_H
+#define KYRA_RESOURCE_INTERN_H
+
+#include "common/archive.h"
+#include "common/hash-str.h"
+#include "common/hashmap.h"
+#include "common/str.h"
+#include "common/list.h"
+
+namespace Kyra {
+
+class Resource;
+
+class PlainArchive : public Common::Archive {
+public:
+ struct InputEntry {
+ Common::String name;
+
+ uint32 offset;
+ uint32 size;
+ };
+
+ typedef Common::List<InputEntry> FileInputList;
+
+ PlainArchive(Resource *owner, const Common::String &filename, const FileInputList &files);
+
+ bool hasFile(const Common::String &name);
+ int getAllNames(Common::StringList &list);
+ Common::SeekableReadStream *openFile(const Common::String &name);
+private:
+ struct Entry {
+ uint32 offset;
+ uint32 size;
+ };
+
+ typedef Common::HashMap<Common::String, Entry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap;
+
+ Resource *_owner;
+ Common::String _filename;
+ FileMap _files;
+};
+
+class CachedArchive : public Common::Archive {
+public:
+ struct InputEntry {
+ Common::String name;
+
+ byte *data;
+ uint32 size;
+ };
+
+ typedef Common::List<InputEntry> FileInputList;
+
+ CachedArchive(const FileInputList &files);
+ ~CachedArchive();
+
+ bool hasFile(const Common::String &name);
+ int getAllNames(Common::StringList &list);
+ Common::SeekableReadStream *openFile(const Common::String &name);
+private:
+ struct Entry {
+ byte *data;
+ uint32 size;
+ };
+
+ typedef Common::HashMap<Common::String, Entry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap;
+ FileMap _files;
+};
+
+
+class ResArchiveLoader {
+public:
+ virtual bool checkFilename(Common::String filename) const = 0;
+ virtual bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const = 0;
+ virtual Common::Archive *load(Resource *owner, const Common::String &filename, Common::SeekableReadStream &stream) const = 0;
+};
+
+class ResLoaderPak : public ResArchiveLoader {
+public:
+ bool checkFilename(Common::String filename) const;
+ bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const;
+ Common::Archive *load(Resource *owner, const Common::String &filename, Common::SeekableReadStream &stream) const;
+};
+
+class ResLoaderInsMalcolm : public ResArchiveLoader {
+public:
+ bool checkFilename(Common::String filename) const;
+ bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const;
+ Common::Archive *load(Resource *owner, const Common::String &filename, Common::SeekableReadStream &stream) const;
+};
+
+class ResLoaderTlk : public ResArchiveLoader {
+public:
+ bool checkFilename(Common::String filename) const;
+ bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const;
+ Common::Archive *load(Resource *owner, const Common::String &filename, Common::SeekableReadStream &stream) const;
+};
+
+class InstallerLoader {
+public:
+ static Common::Archive *load(Resource *owner, const Common::String &filename, const Common::String &extension, const uint8 offset);
+};
+
+} // end of namespace Kyra
+
+#endif