[Scummvm-cvs-logs] SF.net SVN: scummvm:[34428] scummvm/trunk/engines/kyra

lordhoto at users.sourceforge.net lordhoto at users.sourceforge.net
Sun Sep 7 23:46:37 CEST 2008


Revision: 34428
          http://scummvm.svn.sourceforge.net/scummvm/?rev=34428&view=rev
Author:   lordhoto
Date:     2008-09-07 21:46:37 +0000 (Sun, 07 Sep 2008)

Log Message:
-----------
Moved Kyra resource code to a SearchSet/Archive based implementation, this removes dependencies on Common::File.

Modified Paths:
--------------
    scummvm/trunk/engines/kyra/module.mk
    scummvm/trunk/engines/kyra/resource.cpp
    scummvm/trunk/engines/kyra/resource.h

Added Paths:
-----------
    scummvm/trunk/engines/kyra/resource_intern.cpp
    scummvm/trunk/engines/kyra/resource_intern.h

Modified: scummvm/trunk/engines/kyra/module.mk
===================================================================
--- scummvm/trunk/engines/kyra/module.mk	2008-09-07 21:45:18 UTC (rev 34427)
+++ scummvm/trunk/engines/kyra/module.mk	2008-09-07 21:46:37 UTC (rev 34428)
@@ -23,6 +23,7 @@
 	kyra_mr.o \
 	lol.o \
 	resource.o \
+	resource_intern.o \
 	saveload.o \
 	saveload_lok.o \
 	saveload_hof.o \

Modified: scummvm/trunk/engines/kyra/resource.cpp
===================================================================
--- scummvm/trunk/engines/kyra/resource.cpp	2008-09-07 21:45:18 UTC (rev 34427)
+++ scummvm/trunk/engines/kyra/resource.cpp	2008-09-07 21:46:37 UTC (rev 34428)
@@ -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 "common/system.h"
 
-#include "kyra/resource.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 @@
 			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 @@
 				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 @@
 			"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 @@
 	return true;
 }
 
-bool Resource::loadPakFile(const Common::String &filename) {
-	if (!isAccessible(filename))
-		return false;
+bool Resource::loadPakFile(Common::String filename) {
+	filename.toUppercase();
 
-	ResFileMap::iterator iter = _map.find(filename);
-	if (iter == _map.end())
-		return false;
-
-	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 @@
 		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 @@
 	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::unloadPakFile(Common::String filename) {
+	filename.toUppercase();
+	_archiveFiles->remove(filename);
+	_protectedFiles->remove(filename);
 }
 
-void Resource::clearCompFileList() {
-	for (CompFileMap::iterator i = _compFiles.begin(); i != _compFiles.end(); ++i)
-		delete[] i->_value.data;
-
-	_compFiles.clear();
+bool Resource::isInPakList(Common::String filename) {
+	filename.toUppercase();
+	return (_archiveFiles->hasArchive(filename) || _protectedFiles->hasArchive(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);
-}
-
 void Resource::unloadAllPakFiles() {
-	// remove all entries
-	_map.clear();
+	_archiveFiles->clear();
+	_protectedFiles->clear();
 }
 
 uint8 *Resource::fileData(const char *file, uint32 *size) {
@@ -318,31 +238,21 @@
 }
 
 bool Resource::exists(const char *file, bool errorOutOnFail) {
-	if (Common::File::exists(file))
+	if (_files.hasFile(file))
 		return true;
-	else if (isAccessible(file))
-		return true;
 	else if (errorOutOnFail)
 		error("File '%s' can't be found", file);
 	return false;
 }
 
 uint32 Resource::getFileSize(const char *file) {
-	CompFileMap::iterator compEntry;
+	Common::SeekableReadStream *stream = getFileStream(file);
+	if (!stream)
+		return 0;
 
-	if (Common::File::exists(file)) {
-		Common::File f;
-		if (f.open(file))
-			return f.size();
-	} else {
-		if (!isAccessible(file))
-			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,1243 +267,61 @@
 }
 
 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;
+	return _files.openFile(file);
 }
 
-bool Resource::isAccessible(const Common::String &file) {
-	checkFile(file);
+Common::ArchivePtr Resource::loadArchive(const Common::String &file) {
+	ArchiveMap::iterator cachedArchive = _archiveCache.find(file);
+	if (cachedArchive != _archiveCache.end())
+		return cachedArchive->_value;
 
-	ResFileMap::const_iterator iter = _map.find(file);
-	if (iter == _map.end())
-		return false;
-	
-	return isAccessible(&iter->_value);
-}
+	Common::SeekableReadStream *stream = getFileStream(file);
+	if (!stream)
+		return Common::ArchivePtr();
 
-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;
+	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;
-			}
-		}
-		delete stream;
-		stream = 0;
-
-		if (fileEntry->type == ResFileEntry::kAutoDetect)
-			fileEntry->type = ResFileEntry::kRaw;
-	}
-}
-
-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::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;
-}
-
-} // 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;
+				stream->seek(0, SEEK_SET);
 			}
 		}
-
-		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);
+	delete stream;
 
-		uint32 magic = stream.readUint32BE();
+	if (!archive)
+		return Common::ArchivePtr();
 
-		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;
+	_archiveCache[file] = archive;
+	return archive;
 }
 
-Common::SeekableReadStream *ResLoaderPak::loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileEntry entry) const {
-	assert(archive);
+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;
 
-	archive->seek(entry.offset, SEEK_SET);
-	Common::SeekableSubReadStream *stream = new Common::SeekableSubReadStream(archive, entry.offset, entry.offset + entry.size, true);
-	assert(stream);
-	return stream;
-}
+	Common::ArchivePtr archive(InstallerLoader::load(this, file, ext, offset));
+	if (!archive)
+		return Common::ArchivePtr();
 
-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;
+	_archiveCache[file] = archive;
+	return archive;
 }
 
-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");
-				}
-			}
-				
-			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;
-	}
-
-	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;
-	}
-};
-
-bool CompLoaderInsHof::checkForFiles() const {
-	return (Common::File::exists(_checkFile1) && Common::File::exists(_checkFile2));
-}
-
-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++;
-			}
-
-			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();
-		}
-	}
-
-	archives.clear();	
-	return true;
-}
-
-#pragma mark -
-
 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
 
 

Modified: scummvm/trunk/engines/kyra/resource.h
===================================================================
--- scummvm/trunk/engines/kyra/resource.h	2008-09-07 21:45:18 UTC (rev 34427)
+++ scummvm/trunk/engines/kyra/resource.h	2008-09-07 21:46:37 UTC (rev 34428)
@@ -35,78 +35,17 @@
 #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) {}
+class ResArchiveLoader;
 
-		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 Resource {
 public:
 	Resource(KyraEngine_v1 *vm);
@@ -114,9 +53,9 @@
 
 	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,33 +69,21 @@
 
 	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;
 };
 

Added: scummvm/trunk/engines/kyra/resource_intern.cpp
===================================================================
--- scummvm/trunk/engines/kyra/resource_intern.cpp	                        (rev 0)
+++ scummvm/trunk/engines/kyra/resource_intern.cpp	2008-09-07 21:46:37 UTC (rev 34428)
@@ -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


Property changes on: scummvm/trunk/engines/kyra/resource_intern.cpp
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/engines/kyra/resource_intern.h
===================================================================
--- scummvm/trunk/engines/kyra/resource_intern.h	                        (rev 0)
+++ scummvm/trunk/engines/kyra/resource_intern.h	2008-09-07 21:46:37 UTC (rev 34428)
@@ -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


Property changes on: scummvm/trunk/engines/kyra/resource_intern.h
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list