aboutsummaryrefslogtreecommitdiff
path: root/engines/kyra/resource.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/kyra/resource.cpp')
-rw-r--r--engines/kyra/resource.cpp808
1 files changed, 780 insertions, 28 deletions
diff --git a/engines/kyra/resource.cpp b/engines/kyra/resource.cpp
index b2564221fc..afd7eacfda 100644
--- a/engines/kyra/resource.cpp
+++ b/engines/kyra/resource.cpp
@@ -30,22 +30,24 @@
#include "common/fs.h"
#include "common/func.h"
-#include "gui/message.h"
-
#include "kyra/resource.h"
namespace Kyra {
-Resource::Resource(KyraEngine *vm) : _loaders(), _map(), _vm(vm) {
+Resource::Resource(KyraEngine_v1 *vm) : _loaders(), _map(), _vm(vm) {
initializeLoaders();
}
Resource::~Resource() {
_map.clear();
_loaders.clear();
+
+ clearCompFileList();
+ _compLoaders.clear();
}
bool Resource::reset() {
+ clearCompFileList();
unloadAllPakFiles();
FilesystemNode dir(ConfMan.get("path"));
@@ -53,13 +55,10 @@ bool Resource::reset() {
if (!dir.exists() || !dir.isDirectory())
error("invalid game path '%s'", dir.getPath().c_str());
- if (_vm->game() != GI_KYRA3) {
- if (!loadPakFile(StaticResource::staticDataFilename()) || !StaticResource::checkKyraDat()) {
- Common::String errorMessage = "You're missing the '" + StaticResource::staticDataFilename() + "' file or it got corrupted, (re)get it from the ScummVM website";
- ::GUI::MessageDialog errorMsg(errorMessage);
- errorMsg.runModal();
- error(errorMessage.c_str());
- }
+ if (!loadPakFile(StaticResource::staticDataFilename()) || !StaticResource::checkKyraDat()) {
+ Common::String errorMessage = "You're missing the '" + StaticResource::staticDataFilename() + "' file or it got corrupted, (re)get it from the ScummVM website";
+ _vm->GUIErrorMessage(errorMessage);
+ error(errorMessage.c_str());
}
if (_vm->game() == GI_KYRA1) {
@@ -71,17 +70,32 @@ bool Resource::reset() {
if (_vm->gameFlags().isTalkie)
loadPakFile("CHAPTER1.VRM");
} else if (_vm->game() == GI_KYRA2) {
+ if (_vm->gameFlags().useInstallerPackage)
+ tryLoadCompFiles();
+
// 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");
}
return true;
} else if (_vm->game() == GI_KYRA3) {
- loadPakFile("WESTWOOD.001");
+ if (_vm->gameFlags().useInstallerPackage)
+ loadPakFile("WESTWOOD.001");
+
+ // Add default file directories
+ Common::File::addDefaultDirectory(ConfMan.get("path") + "malcolm");
+ Common::File::addDefaultDirectory(ConfMan.get("path") + "MALCOLM");
+
loadFileList("FILEDATA.FDT");
return true;
@@ -212,7 +226,7 @@ bool Resource::loadFileList(const Common::String &filedata) {
buffer[12] = 0;
f.seek(offset + 16, SEEK_SET);
- Common::String filename = (char*)buffer;
+ Common::String filename = Common::String((char*)buffer);
filename.toUppercase();
if (filename.hasSuffix(".PAK")) {
@@ -253,6 +267,13 @@ void Resource::unloadPakFile(const Common::String &filename) {
}
}
+void Resource::clearCompFileList() {
+ for (CompFileMap::iterator i = _compFiles.begin(); i != _compFiles.end(); ++i)
+ delete[] i->_value.data;
+
+ _compFiles.clear();
+}
+
bool Resource::isInPakList(const Common::String &filename) {
if (!isAccessable(filename))
return false;
@@ -293,6 +314,8 @@ 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))
@@ -314,12 +337,14 @@ bool Resource::loadFileToBuf(const char *file, void *buf, uint32 maxSize) {
return false;
memset(buf, 0, maxSize);
- stream->read(buf, stream->size());
+ stream->read(buf, (maxSize <= stream->size()) ? maxSize : stream->size());
delete stream;
return true;
}
Common::SeekableReadStream *Resource::getFileStream(const Common::String &file) {
+ CompFileMap::iterator compEntry;
+
if (Common::File::exists(file)) {
Common::File *stream = new Common::File();
if (!stream->open(file)) {
@@ -328,6 +353,8 @@ Common::SeekableReadStream *Resource::getFileStream(const Common::String &file)
error("Couldn't open file '%s'", file.c_str());
}
return stream;
+ } else if ((compEntry = _compFiles.find(file)) != _compFiles.end()) {
+ return new Common::MemoryReadStream(compEntry->_value.data, compEntry->_value.size, false);
} else {
if (!isAccessable(file))
return 0;
@@ -376,19 +403,35 @@ bool Resource::isAccessable(const Common::String &file) {
}
void Resource::checkFile(const Common::String &file) {
- if (_map.find(file) == _map.end() && Common::File::exists(file)) {
- Common::File temp;
- if (temp.open(file)) {
+ if (_map.find(file) == _map.end()) {
+ CompFileMap::const_iterator iter;
+
+ if (Common::File::exists(file)) {
+ Common::File temp;
+ if (temp.open(file)) {
+ ResFileEntry entry;
+ entry.parent = "";
+ entry.size = temp.size();
+ entry.mounted = file.compareToIgnoreCase(StaticResource::staticDataFilename()) != 0;
+ entry.preload = false;
+ entry.prot = false;
+ entry.type = ResFileEntry::kAutoDetect;
+ entry.offset = 0;
+ _map[file] = entry;
+ temp.close();
+
+ detectFileTypes();
+ }
+ } else if ((iter = _compFiles.find(file)) != _compFiles.end()) {
ResFileEntry entry;
entry.parent = "";
- entry.size = temp.size();
- entry.mounted = file.compareToIgnoreCase(StaticResource::staticDataFilename()) != 0;
+ entry.size = iter->_value.size;
+ entry.mounted = false;
entry.preload = false;
entry.prot = false;
entry.type = ResFileEntry::kAutoDetect;
entry.offset = 0;
_map[file] = entry;
- temp.close();
detectFileTypes();
}
@@ -425,6 +468,13 @@ void Resource::detectFileTypes() {
}
}
+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 -
@@ -492,6 +542,20 @@ bool ResLoaderPak::isLoadable(const Common::String &filename, Common::SeekableRe
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();
@@ -559,6 +623,33 @@ bool ResLoaderPak::loadFile(const Common::String &filename, Common::SeekableRead
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;
}
@@ -571,7 +662,7 @@ Common::SeekableReadStream *ResLoaderPak::loadFileFromArchive(const Common::Stri
return stream;
}
-class ResLoaderIns : public ResArchiveLoader {
+class ResLoaderInsMalcolm : public ResArchiveLoader {
public:
bool checkFilename(Common::String filename) const;
bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const;
@@ -579,16 +670,18 @@ public:
Common::SeekableReadStream *loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileEntry entry) const;
ResFileEntry::kType getType() const {
- return ResFileEntry::kIns;
+ return ResFileEntry::kInsMal;
}
};
-bool ResLoaderIns::checkFilename(Common::String filename) const {
+bool ResLoaderInsMalcolm::checkFilename(Common::String filename) const {
filename.toUppercase();
- return (filename.hasSuffix(".001"));
+ if (!filename.hasSuffix(".001"))
+ return false;
+ return true;
}
-bool ResLoaderIns::isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const {
+bool ResLoaderInsMalcolm::isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const {
stream.seek(3);
uint32 size = stream.readUint32LE();
@@ -602,7 +695,7 @@ bool ResLoaderIns::isLoadable(const Common::String &filename, Common::SeekableRe
return (buffer[0] == 0x0D && buffer[1] == 0x0A);
}
-bool ResLoaderIns::loadFile(const Common::String &filename, Common::SeekableReadStream &stream, FileList &files) const {
+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)
@@ -647,7 +740,7 @@ bool ResLoaderIns::loadFile(const Common::String &filename, Common::SeekableRead
return true;
}
-Common::SeekableReadStream *ResLoaderIns::loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileEntry entry) const {
+Common::SeekableReadStream *ResLoaderInsMalcolm::loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileEntry entry) const {
assert(archive);
archive->seek(entry.offset, SEEK_SET);
@@ -714,7 +807,7 @@ bool ResLoaderTlk::loadFile(const Common::String &filename, Common::SeekableRead
entry.offset = resOffset+4;
char realFilename[20];
- snprintf(realFilename, 20, "%u.AUD", resFilename);
+ snprintf(realFilename, 20, "%.08u.AUD", resFilename);
uint32 curOffset = stream.pos();
stream.seek(resOffset, SEEK_SET);
@@ -737,11 +830,669 @@ Common::SeekableReadStream *ResLoaderTlk::loadFileFromArchive(const Common::Stri
}
#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];
+ const 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((void*) 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:
+ bool checkForFiles() const;
+ bool loadFile(CompFileMap &loadTo) const;
+
+private:
+ struct Archive {
+ Common::String filename;
+ uint32 firstFile;
+ uint32 startOffset;
+ uint32 lastFile;
+ uint32 endOffset;
+ uint32 totalSize;
+ };
+};
+
+bool CompLoaderInsHof::checkForFiles() const {
+ return (Common::File::exists("WESTWOOD.001") && Common::File::exists("WESTWOOD.002"));
+}
+
+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, "%03d", 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 -= 6;
+ pos += 6;
+ tmpFile.seek(6, 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;
+ 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, "%03d", 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;
+ exp.process(outbuffer, inbuffer, outsize, insize);
+ 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, "%03d", 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) {
+ if (hdr[8] != 8)
+ error("compression type not implemented");
+ 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;
+ exp.process(outbuffer, inbuffer, outsize, insize);
+ 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 ResLoaderIns()));
+ _loaders.push_back(LoaderList::value_type(new ResLoaderInsMalcolm()));
_loaders.push_back(LoaderList::value_type(new ResLoaderTlk()));
+
+ _compLoaders.push_back(CompLoaderList::value_type(new CompLoaderInsHof()));
}
const ResArchiveLoader *Resource::getLoader(ResFileEntry::kType type) const {
@@ -755,3 +1506,4 @@ const ResArchiveLoader *Resource::getLoader(ResFileEntry::kType type) const {
} // end of namespace Kyra
+