aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
Diffstat (limited to 'engines')
-rw-r--r--engines/kyra/detection.cpp103
-rw-r--r--engines/kyra/kyra_hof.cpp2
-rw-r--r--engines/kyra/kyra_v1.h11
-rw-r--r--engines/kyra/resource.cpp813
-rw-r--r--engines/kyra/resource.h9
-rw-r--r--engines/kyra/sequences_hof.cpp2
6 files changed, 904 insertions, 36 deletions
diff --git a/engines/kyra/detection.cpp b/engines/kyra/detection.cpp
index 3c4c87e12b..a3ca2f829e 100644
--- a/engines/kyra/detection.cpp
+++ b/engines/kyra/detection.cpp
@@ -41,22 +41,25 @@ struct KYRAGameDescription {
namespace {
-#define FLAGS(x, y, z, a, b, id) { Common::UNK_LANG, Common::kPlatformUnknown, x, y, z, a, b, id }
-
-#define KYRA1_FLOPPY_FLAGS FLAGS(false, false, false, false, false, Kyra::GI_KYRA1)
-#define KYRA1_AMIGA_FLAGS FLAGS(false, false, false, false, false, Kyra::GI_KYRA1)
-#define KYRA1_TOWNS_FLAGS FLAGS(false, true, false, false, false, Kyra::GI_KYRA1)
-#define KYRA1_TOWNS_SJIS_FLAGS FLAGS(false, true, false, true, false, Kyra::GI_KYRA1)
-#define KYRA1_CD_FLAGS FLAGS(false, true, true, false, false, Kyra::GI_KYRA1)
-#define KYRA1_DEMO_FLAGS FLAGS(true, false, false, false, false, Kyra::GI_KYRA1)
-
-#define KYRA2_CD_FLAGS FLAGS(false, false, true, false, false, Kyra::GI_KYRA2)
-#define KYRA2_CD_DEMO_FLAGS FLAGS(true, false, true, false, false, Kyra::GI_KYRA2)
-#define KYRA2_DEMO_FLAGS FLAGS(true, false, false, false, false, Kyra::GI_KYRA2)
-#define KYRA2_TOWNS_FLAGS FLAGS(false, false, false, false, false, Kyra::GI_KYRA2)
-#define KYRA2_TOWNS_SJIS_FLAGS FLAGS(false, false, false, true, false, Kyra::GI_KYRA2)
-
-#define KYRA3_CD_FLAGS FLAGS(false, false, true, false, true, Kyra::GI_KYRA3)
+#define FLAGS(x, y, z, a, b, c, id) { Common::UNK_LANG, Common::kPlatformUnknown, x, y, z, a, b, c, id }
+
+#define KYRA1_FLOPPY_FLAGS FLAGS(false, false, false, false, false, false, Kyra::GI_KYRA1)
+#define KYRA1_AMIGA_FLAGS FLAGS(false, false, false, false, false, false, Kyra::GI_KYRA1)
+#define KYRA1_TOWNS_FLAGS FLAGS(false, true, false, false, false, false, Kyra::GI_KYRA1)
+#define KYRA1_TOWNS_SJIS_FLAGS FLAGS(false, true, false, true, false, false, Kyra::GI_KYRA1)
+#define KYRA1_CD_FLAGS FLAGS(false, true, true, false, false, false, Kyra::GI_KYRA1)
+#define KYRA1_DEMO_FLAGS FLAGS(true, false, false, false, false, false, Kyra::GI_KYRA1)
+
+#define KYRA2_FLOPPY_FLAGS FLAGS(false, false, false, false, false, false, Kyra::GI_KYRA2)
+#define KYRA2_FLOPPY_CMP_FLAGS FLAGS(false, false, false, false, false, true, Kyra::GI_KYRA2)
+#define KYRA2_CD_FLAGS FLAGS(false, false, true, false, false, false, Kyra::GI_KYRA2)
+#define KYRA2_CD_DEMO_FLAGS FLAGS(true, false, true, false, false, false, Kyra::GI_KYRA2)
+#define KYRA2_DEMO_FLAGS FLAGS(true, false, false, false, false, false, Kyra::GI_KYRA2)
+#define KYRA2_TOWNS_FLAGS FLAGS(false, false, false, false, false, false, Kyra::GI_KYRA2)
+#define KYRA2_TOWNS_SJIS_FLAGS FLAGS(false, false, false, true, false, false, Kyra::GI_KYRA2)
+
+#define KYRA3_CD_FLAGS FLAGS(false, false, true, false, true, true, Kyra::GI_KYRA3)
+#define KYRA3_CD_INS_FLAGS FLAGS(false, false, true, false, true, true, Kyra::GI_KYRA3)
const KYRAGameDescription adGameDescs[] = {
{
@@ -296,6 +299,66 @@ const KYRAGameDescription adGameDescs[] = {
KYRA1_DEMO_FLAGS
},
+ { // Floppy version
+ {
+ "kyra2",
+ 0,
+ AD_ENTRY1("WESTWOOD.001", "3f52dda68c4f7696c8309038be9f4151"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ Common::ADGF_NO_FLAGS
+ },
+ KYRA2_FLOPPY_CMP_FLAGS
+ },
+
+ { // Floppy version
+ {
+ "kyra2",
+ 0,
+ AD_ENTRY1("WESTWOOD.001", "d787b9559afddfe058b84c0b3a787224"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ Common::ADGF_NO_FLAGS
+ },
+ KYRA2_FLOPPY_CMP_FLAGS
+ },
+
+ { // // Floppy version extracted
+ {
+ "kyra2",
+ "Extracted",
+ AD_ENTRY1("FATE.PAK", "1ba18be685ad8e5a0ab5d46a0ce4d345"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ Common::ADGF_NO_FLAGS
+ },
+ KYRA2_FLOPPY_FLAGS
+ },
+
+ { // Floppy version extracted
+ {
+ "kyra2",
+ "Extracted",
+ AD_ENTRY1("FATE.PAK", "262fb69dd8e52e596c7aefc6456f7c1b"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ Common::ADGF_NO_FLAGS
+ },
+ KYRA2_FLOPPY_FLAGS
+ },
+
+ { // Floppy version extracted
+ {
+ "kyra2",
+ "Extracted",
+ AD_ENTRY1("FATE.PAK", "f7de11506b4c8fdf64bc763206c3e4e7"),
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ Common::ADGF_NO_FLAGS
+ },
+ KYRA2_FLOPPY_FLAGS
+ },
+
{ // CD version
{
"kyra2",
@@ -417,7 +480,7 @@ const KYRAGameDescription adGameDescs[] = {
Common::kPlatformPC,
Common::ADGF_DROPLANGUAGE
},
- KYRA3_CD_FLAGS
+ KYRA3_CD_INS_FLAGS
},
{
{
@@ -432,7 +495,7 @@ const KYRAGameDescription adGameDescs[] = {
Common::kPlatformPC,
Common::ADGF_DROPLANGUAGE
},
- KYRA3_CD_FLAGS
+ KYRA3_CD_INS_FLAGS
},
{
{
@@ -447,7 +510,7 @@ const KYRAGameDescription adGameDescs[] = {
Common::kPlatformPC,
Common::ADGF_DROPLANGUAGE
},
- KYRA3_CD_FLAGS
+ KYRA3_CD_INS_FLAGS
},
// installed version
@@ -497,7 +560,7 @@ const KYRAGameDescription adGameDescs[] = {
KYRA3_CD_FLAGS
},
- { AD_TABLE_END_MARKER, FLAGS(0, 0, 0, 0, 0, 0) }
+ { AD_TABLE_END_MARKER, FLAGS(0, 0, 0, 0, 0, 0, 0) }
};
const PlainGameDescriptor gameList[] = {
diff --git a/engines/kyra/kyra_hof.cpp b/engines/kyra/kyra_hof.cpp
index 0bc5162e94..09388fb567 100644
--- a/engines/kyra/kyra_hof.cpp
+++ b/engines/kyra/kyra_hof.cpp
@@ -254,6 +254,8 @@ int KyraEngine_HoF::go() {
if (_menuChoice != 4) {
// load just the pak files needed for ingame
_res->loadPakFile(StaticResource::staticDataFilename());
+ if (_flags.useInstallerPackage)
+ _res->loadPakFile("WESTWOOD.001");
if (_flags.platform == Common::kPlatformPC && _flags.isTalkie)
_res->loadFileList("FILEDATA.FDT");
else
diff --git a/engines/kyra/kyra_v1.h b/engines/kyra/kyra_v1.h
index 2278cc81df..4f38ceca98 100644
--- a/engines/kyra/kyra_v1.h
+++ b/engines/kyra/kyra_v1.h
@@ -46,11 +46,12 @@ struct GameFlags {
Common::Language lang;
Common::Platform platform;
- bool isDemo : 1;
- bool useAltShapeHeader : 1; // alternative shape header (uses 2 bytes more, those are unused though)
- bool isTalkie : 1;
- bool useHiResOverlay : 1;
- bool useDigSound : 1;
+ bool isDemo : 1;
+ bool useAltShapeHeader : 1; // alternative shape header (uses 2 bytes more, those are unused though)
+ bool isTalkie : 1;
+ bool useHiResOverlay : 1;
+ bool useDigSound : 1;
+ bool useInstallerPackage : 1;
byte gameID;
};
diff --git a/engines/kyra/resource.cpp b/engines/kyra/resource.cpp
index 635d00f993..a65093915a 100644
--- a/engines/kyra/resource.cpp
+++ b/engines/kyra/resource.cpp
@@ -34,6 +34,8 @@
#include "kyra/resource.h"
+#define INS_CACHE_THRESHOLD 300000 // all files with file size greater than this will be cached
+
namespace Kyra {
Resource::Resource(KyraEngine_v1 *vm) : _loaders(), _map(), _vm(vm) {
@@ -69,6 +71,9 @@ bool Resource::reset() {
if (_vm->gameFlags().isTalkie)
loadPakFile("CHAPTER1.VRM");
} else if (_vm->game() == GI_KYRA2) {
+ if (_vm->gameFlags().useInstallerPackage)
+ loadPakFile("WESTWOOD.001");
+
// mouse pointer, fonts, etc. required for initializing
if (_vm->gameFlags().isDemo && !_vm->gameFlags().isTalkie) {
loadPakFile("GENERAL.PAK");
@@ -85,11 +90,12 @@ bool Resource::reset() {
return true;
} else if (_vm->game() == GI_KYRA3) {
+ 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");
- loadPakFile("WESTWOOD.001");
loadFileList("FILEDATA.FDT");
return true;
@@ -579,7 +585,788 @@ Common::SeekableReadStream *ResLoaderPak::loadFileFromArchive(const Common::Stri
return stream;
}
-class ResLoaderIns : public ResArchiveLoader {
+class ResLoaderInsKyra : 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::kInsKyra;
+ }
+};
+
+bool ResLoaderInsKyra::checkFilename(Common::String filename) const {
+ return false;
+}
+
+bool ResLoaderInsKyra::isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const {
+ return true;
+}
+
+bool ResLoaderInsKyra::loadFile(const Common::String &filename, Common::SeekableReadStream &stream, FileList &files) const {
+ return true;
+}
+
+Common::SeekableReadStream *ResLoaderInsKyra::loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileEntry entry) const {
+ return 0;
+}
+
+class FileExpanderSource {
+public:
+ FileExpanderSource(const uint8 *data) : _dataPtr(data), _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;
+ uint16 _key;
+ int8 _bitsLeft;
+ uint8 _index;
+};
+
+void FileExpanderSource::advSrcBitsBy1() {
+ _key >>= 1;
+ if (!--_bitsLeft) {
+ _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;
+ _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;
+ return (((val & 3) + 4) << _index) + 0x101 + getKeyMasked(_index);
+}
+
+void FileExpanderSource::advSrcRefresh() {
+ _key = READ_LE_UINT16(_dataPtr);
+ _dataPtr += 2;
+ _bitsLeft = 8;
+}
+
+class FileExpander {
+public:
+ FileExpander();
+ ~FileExpander();
+
+ bool process(const 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(const 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 = (uint8*) dst;
+ uint16 tableSize0 = 0;
+ uint16 tableSize1 = 0;
+ bool needrefresh = true;
+ bool postprocess = false;
+
+ _src = new FileExpanderSource(src);
+
+ 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 = (uint8*) 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){
+ uint16 cnt = 144;
+ uint8 *d2 = (uint8*) _tables[0];
+ memset(d2, 8, 144);
+ memset(d2 + 144, 9, 112);
+ memset(d2 + 256, 7, 24);
+ memset(d2 + 280, 8, 8);
+ d2 = (uint8*) _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) + _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 = (uint8*) 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;
+
+ uint8 *s = (uint8*) 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 = (uint8*) 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 = (uint8*) 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 = (uint8*) 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 = (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 FileCache {
+public:
+ FileCache() : _size(0) {}
+ ~FileCache() {}
+
+ void add(ResFileEntry entry, const uint8 *data);
+ bool getData(ResFileEntry entry, uint8 *dst);
+ void flush();
+
+private:
+ struct FileCacheEntry {
+ ResFileEntry entry;
+ const uint8 *data;
+ };
+
+ Common::List<FileCacheEntry> _cachedFileList;
+ uint32 _size;
+};
+
+void FileCache::add(ResFileEntry entry, const uint8 *data) {
+ FileCacheEntry fileCacheEntry;
+ fileCacheEntry.entry.compressedSize = entry.compressedSize;
+ fileCacheEntry.entry.mounted = entry.mounted;
+ fileCacheEntry.entry.offset = entry.offset;
+ fileCacheEntry.entry.parent = entry.parent;
+ fileCacheEntry.entry.preload = entry.preload;
+ fileCacheEntry.entry.prot = entry.prot;
+ fileCacheEntry.entry.size = entry.size;
+ fileCacheEntry.entry.type = entry.type;
+ fileCacheEntry.entry.fileIndex = entry.fileIndex;
+ uint8 *dst = new uint8[entry.size];
+ assert(dst);
+ memcpy(dst, data, entry.size);
+ fileCacheEntry.data = dst;
+ _cachedFileList.push_back(fileCacheEntry);
+ _size += entry.size;
+}
+
+bool FileCache::getData(ResFileEntry entry, uint8 *dst) {
+ for (Common::List<FileCacheEntry>::const_iterator c = _cachedFileList.begin(); c != _cachedFileList.end(); ++c) {
+ if (c->entry.offset == entry.offset && c->entry.compressedSize == entry.compressedSize &&
+ c->entry.fileIndex == entry.fileIndex && c->entry.size == entry.size) {
+ memcpy(dst, c->data, c->entry.size);
+ return true;
+ }
+ }
+ return false;
+}
+
+void FileCache::flush() {
+ debug("total amount of cache memory used: %d", _size);
+ for (Common::List<FileCacheEntry>::const_iterator c = _cachedFileList.begin(); c != _cachedFileList.end(); ++c)
+ delete [] c->data;
+ _cachedFileList.clear();
+ _size = 0;
+}
+
+class ResLoaderInsHof : public ResArchiveLoader {
+public:
+ ResLoaderInsHof() {}
+ ~ResLoaderInsHof() { _fileCache.flush(); }
+
+ 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::kInsHof;
+ }
+private:
+ mutable FileCache _fileCache;
+};
+
+bool ResLoaderInsHof::checkFilename(Common::String filename) const {
+ filename.toUppercase();
+ if (!filename.hasSuffix(".001"))
+ return false;
+ filename.insertChar('2', filename.size() - 1);
+ filename.deleteLastChar();
+ if (!Common::File::exists(filename))
+ return false;
+ return true;
+}
+
+bool ResLoaderInsHof::isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const {
+ uint8 fileId = stream.readByte();
+ uint32 size = stream.readUint32LE();
+
+ if (size < stream.size() + 1)
+ return false;
+
+ return (fileId == 1);
+}
+
+bool ResLoaderInsHof::loadFile(const Common::String &filename, Common::SeekableReadStream &stream, FileList &files) const {
+ Common::File tmpFile;
+
+ uint32 pos = 0;
+ uint32 bytesleft = 0;
+ bool startFile = true;
+
+ Common::String fn_base(filename.c_str(), 10);
+ Common::String fn_tmp;
+ char fn_ext[4];
+
+ int i = filename.size() - 1;
+ while (fn_base.lastChar() != '.')
+ fn_base.deleteLastChar();
+
+ struct InsHofArchive {
+ Common::String filename;
+ uint32 firstFile;
+ uint32 startOffset;
+ uint32 lastFile;
+ uint32 endOffset;
+ uint32 totalSize;
+ } newArchive;
+
+ Common::List<InsHofArchive> archives;
+
+ for (int8 currentFile = 1; currentFile; currentFile++) {
+ sprintf(fn_ext, "%03d", currentFile);
+ fn_tmp = fn_base + Common::String(fn_ext);
+
+ Common::File tmpFile;
+ if (!tmpFile.open(fn_tmp)) {
+ debug(3, "couldn't open file '%s'\n", fn_tmp);
+ 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 - pos;
+ }
+ newArchive.filename = fn_base;
+ 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);
+ }
+ }
+
+ ResFileEntry newEntry;
+ pos = 0;
+
+ const uint32 kExecSize = 0x0bba;
+ const uint32 kHeaderSize = 30;
+ const uint32 kHeaderSize2 = 46;
+
+ for (Common::List<InsHofArchive>::iterator a = archives.begin(); a != archives.end(); ++a) {
+ bool startFile = true;
+ for (uint32 i = a->firstFile; i != (a->lastFile + 1); i++) {
+ sprintf(fn_ext, "%03d", i);
+ fn_tmp = a->filename + Common::String(fn_ext);
+
+ if (!tmpFile.open(fn_tmp)) {
+ debug(3, "couldn't open file '%s'\n", fn_tmp);
+ 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 {
+ 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(fn_ext, "%03d", i + 1);
+ fn_tmp = a->filename + Common::String(fn_ext);
+
+ Common::File tmpFile2;
+ tmpFile2.open(fn_tmp);
+ 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");
+ newEntry.compressedSize = READ_LE_UINT32(hdr + 18);
+ newEntry.size = READ_LE_UINT32(hdr + 22);
+
+ uint16 filestrlen = READ_LE_UINT16(hdr + 26);
+ *(hdr + 30 + filestrlen) = 0;
+ pos += (kHeaderSize + filestrlen - m);
+
+ newEntry.parent = filename;
+ newEntry.offset = pos;
+ newEntry.type = ResFileEntry::kAutoDetect;
+ newEntry.mounted = false;
+ newEntry.preload = false;
+ newEntry.prot = false;
+ newEntry.fileIndex = i;
+ files.push_back(File(Common::String((const char*) (hdr + 30)), newEntry));
+
+ pos += newEntry.compressedSize;
+ if (pos > size) {
+ pos -= size;
+ break;
+ }
+ } else {
+ uint32 filestrlen = READ_LE_UINT32(hdr + 28);
+ pos += (kHeaderSize2 + filestrlen - m);
+ }
+ }
+ tmpFile.close();
+ }
+ }
+
+ archives.clear();
+
+ return true;
+}
+
+Common::SeekableReadStream *ResLoaderInsHof::loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileEntry entry) const {
+ if (archive) {
+ delete archive;
+ archive = 0;
+ }
+
+ bool resident = false;
+ uint8 * outbuffer = (uint8*) malloc(entry.size);
+ assert(outbuffer);
+
+ if (!_fileCache.getData(entry, outbuffer)) {
+ Common::File tmpFile;
+ char filename[13];
+ sprintf(filename, "WESTWOOD.%03d", entry.fileIndex);
+
+ if (!tmpFile.open(filename)) {
+ delete [] outbuffer;
+ return 0;
+ }
+
+ tmpFile.seek(entry.offset);
+
+ uint8 *inbuffer = new uint8[entry.compressedSize];
+ assert(inbuffer);
+
+ if ((entry.offset + entry.compressedSize) > tmpFile.size()) {
+ // this is for files that are split between two archive files
+ uint32 a = tmpFile.size() - entry.offset;
+ uint32 b = entry.compressedSize - a;
+
+ tmpFile.read(inbuffer, a);
+ tmpFile.close();
+
+ filename[strlen(filename) - 1]++;
+
+ if (!tmpFile.open(filename))
+ return 0;
+ tmpFile.seek(1);
+ tmpFile.read(inbuffer + a, b);
+
+ } else {
+ tmpFile.read(inbuffer, entry.compressedSize);
+ }
+
+ tmpFile.close();
+
+ FileExpander().process(outbuffer, inbuffer, entry.size, entry.compressedSize);
+ delete [] inbuffer;
+
+ if (entry.size > INS_CACHE_THRESHOLD)
+ _fileCache.add(entry, outbuffer);
+ }
+
+ Common::MemoryReadStream *stream = new Common::MemoryReadStream(outbuffer, entry.size, true);
+ assert(stream);
+
+ return stream;
+}
+
+class ResLoaderInsMalcolm : public ResArchiveLoader {
public:
bool checkFilename(Common::String filename) const;
bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const;
@@ -587,16 +1374,22 @@ 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;
+ filename.insertChar('2', filename.size() - 1);
+ filename.deleteLastChar();
+ if (Common::File::exists(filename))
+ 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();
@@ -610,7 +1403,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)
@@ -655,7 +1448,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);
@@ -748,7 +1541,9 @@ Common::SeekableReadStream *ResLoaderTlk::loadFileFromArchive(const Common::Stri
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 ResLoaderInsKyra()));
+ _loaders.push_back(LoaderList::value_type(new ResLoaderInsHof()));
+ _loaders.push_back(LoaderList::value_type(new ResLoaderInsMalcolm()));
_loaders.push_back(LoaderList::value_type(new ResLoaderTlk()));
}
diff --git a/engines/kyra/resource.h b/engines/kyra/resource.h
index a8de21b5ca..69ed8515c1 100644
--- a/engines/kyra/resource.h
+++ b/engines/kyra/resource.h
@@ -52,12 +52,17 @@ struct ResFileEntry {
enum kType {
kRaw = 0,
kPak = 1,
- kIns = 2,
- kTlk = 3,
+ kInsKyra = 2,
+ kInsHof = 3,
+ kInsMal = 4,
+ kTlk = 5,
kAutoDetect
};
kType type;
uint32 offset;
+
+ int fileIndex;
+ uint32 compressedSize;
};
typedef Common::HashMap<Common::String, ResFileEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> ResFileMap;
diff --git a/engines/kyra/sequences_hof.cpp b/engines/kyra/sequences_hof.cpp
index 68bcd1ded2..f44484cc8e 100644
--- a/engines/kyra/sequences_hof.cpp
+++ b/engines/kyra/sequences_hof.cpp
@@ -2651,6 +2651,8 @@ void KyraEngine_HoF::seq_init() {
_res->unloadAllPakFiles();
_res->loadPakFile(StaticResource::staticDataFilename());
+ if (_flags.useInstallerPackage)
+ _res->loadPakFile("WESTWOOD.001");
_res->loadFileList(_sequencePakList, _sequencePakListSize);
int numShp = -1;