aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWillem Jan Palenstijn2011-09-11 21:31:13 +0200
committerWillem Jan Palenstijn2011-09-11 21:31:55 +0200
commitf6f8b456e70fe7bcf24f4322f43644d4a4e34ba2 (patch)
treeba698cb6bfdf4c0c2af1e93bbb46b0ead600a710
parent3e453675e04a99043e0244d9c092cb0359f0585b (diff)
parent3b9b89a78bf65db18bdc6d0c3a1268c892d3db2c (diff)
downloadscummvm-rg350-f6f8b456e70fe7bcf24f4322f43644d4a4e34ba2.tar.gz
scummvm-rg350-f6f8b456e70fe7bcf24f4322f43644d4a4e34ba2.tar.bz2
scummvm-rg350-f6f8b456e70fe7bcf24f4322f43644d4a4e34ba2.zip
Merge branch 'cge'
This adds the CGE engine for the game Soltys. It is based on pull request https://github.com/scummvm/scummvm/pull/81 .
-rw-r--r--base/plugins.cpp3
-rwxr-xr-xconfigure1
-rw-r--r--engines/cge/bitmap.cpp391
-rw-r--r--engines/cge/bitmap.h96
-rw-r--r--engines/cge/cge.cpp226
-rw-r--r--engines/cge/cge.h275
-rw-r--r--engines/cge/cge_main.cpp1572
-rw-r--r--engines/cge/cge_main.h140
-rw-r--r--engines/cge/console.cpp34
-rw-r--r--engines/cge/console.h43
-rw-r--r--engines/cge/detection.cpp237
-rw-r--r--engines/cge/events.cpp378
-rw-r--r--engines/cge/events.h155
-rw-r--r--engines/cge/fileio.cpp420
-rw-r--r--engines/cge/fileio.h168
-rw-r--r--engines/cge/game.cpp72
-rw-r--r--engines/cge/game.h52
-rw-r--r--engines/cge/general.cpp99
-rw-r--r--engines/cge/general.h59
-rw-r--r--engines/cge/module.mk31
-rw-r--r--engines/cge/snail.cpp1182
-rw-r--r--engines/cge/snail.h84
-rw-r--r--engines/cge/snddrv.h63
-rw-r--r--engines/cge/sound.cpp273
-rw-r--r--engines/cge/sound.h126
-rw-r--r--engines/cge/talk.cpp302
-rw-r--r--engines/cge/talk.h96
-rw-r--r--engines/cge/text.cpp215
-rw-r--r--engines/cge/text.h71
-rw-r--r--engines/cge/vga13h.cpp1011
-rw-r--r--engines/cge/vga13h.h274
-rw-r--r--engines/cge/vmenu.cpp142
-rw-r--r--engines/cge/vmenu.h74
-rw-r--r--engines/cge/walk.cpp269
-rw-r--r--engines/cge/walk.h95
-rw-r--r--engines/engines.mk5
36 files changed, 8734 insertions, 0 deletions
diff --git a/base/plugins.cpp b/base/plugins.cpp
index 4fa1a961da..3e18e0ec4e 100644
--- a/base/plugins.cpp
+++ b/base/plugins.cpp
@@ -94,6 +94,9 @@ public:
#if PLUGIN_ENABLED_STATIC(AGOS)
LINK_PLUGIN(AGOS)
#endif
+ #if PLUGIN_ENABLED_STATIC(CGE)
+ LINK_PLUGIN(CGE)
+ #endif
#if PLUGIN_ENABLED_STATIC(CINE)
LINK_PLUGIN(CINE)
#endif
diff --git a/configure b/configure
index 278b917ea1..617a1209ee 100755
--- a/configure
+++ b/configure
@@ -83,6 +83,7 @@ add_engine he "HE71+ games" yes
add_engine agi "AGI" yes
add_engine agos "AGOS" yes "agos2"
add_engine agos2 "AGOS 2 games" yes
+add_engine cge "CGE" no
add_engine cine "Cinematique evo 1" yes
add_engine composer "Magic Composer" no
add_engine cruise "Cinematique evo 2" yes
diff --git a/engines/cge/bitmap.cpp b/engines/cge/bitmap.cpp
new file mode 100644
index 0000000000..cd440e08b4
--- /dev/null
+++ b/engines/cge/bitmap.cpp
@@ -0,0 +1,391 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#include "cge/bitmap.h"
+#include "cge/vga13h.h"
+#include "cge/cge_main.h"
+#include "common/system.h"
+#include "common/debug.h"
+#include "common/debug-channels.h"
+
+namespace CGE {
+
+Dac *Bitmap::_pal = NULL;
+
+void Bitmap::init() {
+ _pal = NULL;
+}
+
+void Bitmap::deinit() {
+}
+
+Bitmap::Bitmap(const char *fname) : _m(NULL), _v(NULL), _map(0) {
+ debugC(1, kCGEDebugBitmap, "Bitmap::Bitmap(%s)", fname);
+
+ char pat[kMaxPath];
+ forceExt(pat, fname, ".VBM");
+
+ if (_cat->exist(pat)) {
+ VFile file(pat);
+ if ((file._error == 0) && (!loadVBM(&file)))
+ error("Bad VBM [%s]", fname);
+ } else {
+ error("Bad VBM [%s]", fname);
+ }
+}
+
+Bitmap::Bitmap(uint16 w, uint16 h, uint8 *map) : _w(w), _h(h), _m(map), _v(NULL), _map(0) {
+ debugC(1, kCGEDebugBitmap, "Bitmap::Bitmap(%d, %d, map)", w, h);
+ if (map)
+ code();
+}
+
+// following routine creates filled rectangle
+// immediately as VGA video chunks, in near memory as fast as possible,
+// especially for text line real time display
+Bitmap::Bitmap(uint16 w, uint16 h, uint8 fill)
+ : _w((w + 3) & ~3), // only full uint32 allowed!
+ _h(h),
+ _m(NULL),
+ _map(0) {
+ debugC(1, kCGEDebugBitmap, "Bitmap::Bitmap(%d, %d, %d)", w, h, fill);
+
+ uint16 dsiz = _w >> 2; // data size (1 plane line size)
+ uint16 lsiz = 2 + dsiz + 2; // uint16 for line header, uint16 for gap
+ uint16 psiz = _h * lsiz; // - last gape, but + plane trailer
+ uint8 *v = new uint8[4 * psiz + _h * sizeof(*_b)];// the same for 4 planes
+ // + room for wash table
+ assert(v != NULL);
+
+ *(uint16 *) v = TO_LE_16(kBmpCPY | dsiz); // data chunk hader
+ memset(v + 2, fill, dsiz); // data bytes
+ *(uint16 *)(v + lsiz - 2) = TO_LE_16(kBmpSKP | ((kScrWidth / 4) - dsiz)); // gap
+
+ // Replicate lines
+ byte *destP;
+ for (destP = v + lsiz; destP < (v + psiz); destP += lsiz)
+ Common::copy(v, v + lsiz, destP);
+
+ *(uint16 *)(v + psiz - 2) = TO_LE_16(kBmpEOI); // plane trailer uint16
+
+ // Replicate planes
+ for (destP = v + psiz; destP < (v + 4 * psiz); destP += psiz)
+ Common::copy(v, v + psiz, destP);
+
+ HideDesc *b = (HideDesc *)(v + 4 * psiz);
+ b->_skip = (kScrWidth - _w) >> 2;
+ b->_hide = _w >> 2;
+
+ // Replicate across the entire table
+ for (HideDesc *hdP = b + 1; hdP < (b + _h); hdP++)
+ *hdP = *b;
+
+ b->_skip = 0; // fix the first entry
+ _v = v;
+ _b = b;
+}
+
+Bitmap::Bitmap(const Bitmap &bmp) : _w(bmp._w), _h(bmp._h), _m(NULL), _v(NULL), _map(0) {
+ debugC(1, kCGEDebugBitmap, "Bitmap::Bitmap(bmp)");
+ uint8 *v0 = bmp._v;
+ if (!v0)
+ return;
+
+ uint16 vsiz = (uint8 *)(bmp._b) - (uint8 *)(v0);
+ uint16 siz = vsiz + _h * sizeof(HideDesc);
+ uint8 *v1 = new uint8[siz];
+ assert(v1 != NULL);
+ memcpy(v1, v0, siz);
+ _b = (HideDesc *)((_v = v1) + vsiz);
+}
+
+Bitmap::~Bitmap() {
+ debugC(6, kCGEDebugBitmap, "Bitmap::~Bitmap()");
+
+ free(_m);
+ delete[] _v;
+}
+
+Bitmap &Bitmap::operator = (const Bitmap &bmp) {
+ debugC(1, kCGEDebugBitmap, "&Bitmap::operator =");
+
+ uint8 *v0 = bmp._v;
+ _w = bmp._w;
+ _h = bmp._h;
+ _m = NULL;
+ _map = 0;
+ delete[] _v;
+
+ if (v0 == NULL) {
+ _v = NULL;
+ } else {
+ uint16 vsiz = (uint8 *)bmp._b - (uint8 *)v0;
+ uint16 siz = vsiz + _h * sizeof(HideDesc);
+ uint8 *v1 = (uint8 *)malloc(sizeof(uint8) * siz);
+ assert(v1 != NULL);
+ memcpy(v1, v0, siz);
+ _b = (HideDesc *)((_v = v1) + vsiz);
+ }
+ return *this;
+}
+
+uint16 Bitmap::moveVmap(uint8 *buf) {
+ debugC(1, kCGEDebugBitmap, "Bitmap::moveVmap(buf)");
+
+ if (!_v)
+ return 0;
+
+ uint16 vsiz = (uint8 *)_b - (uint8 *)_v;
+ uint16 siz = vsiz + _h * sizeof(HideDesc);
+ memcpy(buf, _v, siz);
+ delete[] _v;
+ _b = (HideDesc *)((_v = buf) + vsiz);
+ return siz;
+}
+
+BitmapPtr Bitmap::code() {
+ debugC(1, kCGEDebugBitmap, "Bitmap::code()");
+
+ if (!_m)
+ return false;
+
+ uint16 cnt;
+
+ if (_v) { // old X-map exists, so remove it
+ delete[] _v;
+ _v = NULL;
+ }
+
+ while (true) { // at most 2 times: for (V == NULL) & for allocated block;
+ uint8 *im = _v + 2;
+ uint16 *cp = (uint16 *) _v;
+ int bpl;
+
+ if (_v) { // 2nd pass - fill the hide table
+ for (uint16 i = 0; i < _h; i++) {
+ _b[i]._skip = 0xFFFF;
+ _b[i]._hide = 0x0000;
+ }
+ }
+ for (bpl = 0; bpl < 4; bpl++) { // once per each bitplane
+ uint8 *bm = _m;
+ bool skip = (bm[bpl] == kPixelTransp);
+ uint16 j;
+
+ cnt = 0;
+ for (uint16 i = 0; i < _h; i++) { // once per each line
+ uint8 pix;
+ for (j = bpl; j < _w; j += 4) {
+ pix = bm[j];
+ if (_v && pix != kPixelTransp) {
+ if (j < _b[i]._skip)
+ _b[i]._skip = j;
+
+ if (j >= _b[i]._hide)
+ _b[i]._hide = j + 1;
+ }
+ if ((pix == kPixelTransp) != skip || cnt >= 0x3FF0) { // end of block
+ cnt |= (skip) ? kBmpSKP : kBmpCPY;
+ if (_v)
+ *cp = TO_LE_16(cnt); // store block description uint16
+
+ cp = (uint16 *) im;
+ im += 2;
+ skip = (pix == kPixelTransp);
+ cnt = 0;
+ }
+ if (!skip) {
+ if (_v)
+ *im = pix;
+ im++;
+ }
+ cnt++;
+ }
+
+ bm += _w;
+ if (_w < kScrWidth) {
+ if (skip) {
+ cnt += (kScrWidth - j + 3) / 4;
+ } else {
+ cnt |= kBmpCPY;
+ if (_v)
+ *cp = TO_LE_16(cnt);
+
+ cp = (uint16 *) im;
+ im += 2;
+ skip = true;
+ cnt = (kScrWidth - j + 3) / 4;
+ }
+ }
+ }
+ if (cnt && ! skip) {
+ cnt |= kBmpCPY;
+ if (_v)
+ *cp = TO_LE_16(cnt);
+
+ cp = (uint16 *) im;
+ im += 2;
+ }
+ if (_v)
+ *cp = TO_LE_16(kBmpEOI);
+ cp = (uint16 *) im;
+ im += 2;
+ }
+ if (_v)
+ break;
+
+ uint16 sizV = (uint16)(im - 2 - _v);
+ _v = new uint8[sizV + _h * sizeof(*_b)];
+ assert(_v != NULL);
+
+ _b = (HideDesc *)(_v + sizV);
+ }
+ cnt = 0;
+ for (uint16 i = 0; i < _h; i++) {
+ if (_b[i]._skip == 0xFFFF) { // whole line is skipped
+ _b[i]._skip = (cnt + kScrWidth) >> 2;
+ cnt = 0;
+ } else {
+ uint16 s = _b[i]._skip & ~3;
+ uint16 h = (_b[i]._hide + 3) & ~3;
+ _b[i]._skip = (cnt + s) >> 2;
+ _b[i]._hide = (h - s) >> 2;
+ cnt = kScrWidth - h;
+ }
+ }
+
+ return this;
+}
+
+bool Bitmap::solidAt(int16 x, int16 y) {
+ debugC(6, kCGEDebugBitmap, "Bitmap::solidAt(%d, %d)", x, y);
+
+ if ((x >= _w) || (y >= _h))
+ return false;
+
+ uint8 *m = _v;
+ uint16 r = static_cast<uint16>(x) % 4;
+ uint16 n0 = (kScrWidth * y + x) / 4;
+ uint16 n = 0;
+
+ while (r) {
+ uint16 w, t;
+
+ w = READ_LE_UINT16(m);
+ m += 2;
+ t = w & 0xC000;
+ w &= 0x3FFF;
+
+ switch (t) {
+ case kBmpEOI:
+ r--;
+ case kBmpSKP:
+ w = 0;
+ break;
+ case kBmpREP:
+ w = 1;
+ break;
+ }
+ m += w;
+ }
+
+ while (true) {
+ uint16 w, t;
+
+ w = READ_LE_UINT16(m);
+ m += 2;
+ t = w & 0xC000;
+ w &= 0x3FFF;
+
+ if (n > n0)
+ return false;
+
+ n += w;
+ switch (t) {
+ case kBmpEOI:
+ return false;
+ case kBmpSKP:
+ w = 0;
+ break;
+ case kBmpREP:
+ case kBmpCPY:
+ if (n - w <= n0 && n > n0)
+ return true;
+ break;
+ }
+ m += ((t == kBmpREP) ? 1 : w);
+ }
+}
+
+bool Bitmap::loadVBM(VFile *f) {
+ debugC(5, kCGEDebugBitmap, "Bitmap::loadVBM(f)");
+
+ uint16 p = 0, n = 0;
+ if (f->_error == 0)
+ f->read((uint8 *)&p, sizeof(p));
+ p = FROM_LE_16(p);
+
+ if (f->_error == 0)
+ f->read((uint8 *)&n, sizeof(n));
+ n = FROM_LE_16(n);
+
+ if (f->_error == 0)
+ f->read((uint8 *)&_w, sizeof(_w));
+ _w = FROM_LE_16(_w);
+
+ if (f->_error == 0)
+ f->read((uint8 *)&_h, sizeof(_h));
+ _h = FROM_LE_16(_h);
+
+ if (f->_error == 0) {
+ if (p) {
+ if (_pal) {
+ // Read in the palette
+ byte palData[kPalSize];
+ f->read(palData, kPalSize);
+
+ const byte *srcP = palData;
+ for (int idx = 0; idx < kPalCount; idx++, srcP += 3) {
+ _pal[idx]._r = *srcP;
+ _pal[idx]._g = *(srcP + 1);
+ _pal[idx]._b = *(srcP + 2);
+ }
+ } else
+ f->seek(f->mark() + kPalSize);
+ }
+ }
+ if ((_v = new uint8[n]) == NULL)
+ return false;
+
+ if (f->_error == 0)
+ f->read(_v, n);
+
+ _b = (HideDesc *)(_v + n - _h * sizeof(HideDesc));
+ return (f->_error == 0);
+}
+
+} // End of namespace CGE
diff --git a/engines/cge/bitmap.h b/engines/cge/bitmap.h
new file mode 100644
index 0000000000..78907dc4d7
--- /dev/null
+++ b/engines/cge/bitmap.h
@@ -0,0 +1,96 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#ifndef CGE_BITMAP_H
+#define CGE_BITMAP_H
+
+#include "cge/fileio.h"
+//#include "cge/general.h"
+
+namespace CGE {
+
+#define kBmpEOI 0x0000
+#define kBmpSKP 0x4000
+#define kBmpREP 0x8000
+#define kBmpCPY 0xC000
+
+#define kMaxPath 128
+
+#include "common/pack-start.h"
+
+struct Bgr4 {
+ uint16 _b : 2;
+ uint16 _B : 6;
+ uint16 _g : 2;
+ uint16 _G : 6;
+ uint16 _r : 2;
+ uint16 _R : 6;
+ uint16 _Z : 8;
+};
+
+
+struct HideDesc {
+ uint16 _skip;
+ uint16 _hide;
+};
+
+#include "common/pack-end.h"
+
+class Bitmap {
+ bool loadVBM(VFile *f);
+public:
+ static Dac *_pal;
+ uint16 _w;
+ uint16 _h;
+ uint8 *_m;
+ uint8 *_v;
+ int32 _map;
+ HideDesc *_b;
+
+ Bitmap(const char *fname);
+ Bitmap(uint16 w, uint16 h, uint8 *map);
+ Bitmap(uint16 w, uint16 h, uint8 fill);
+ Bitmap(const Bitmap &bmp);
+ ~Bitmap();
+
+ static void init();
+ static void deinit();
+ Bitmap *code();
+ Bitmap &operator = (const Bitmap &bmp);
+ void hide(int16 x, int16 y);
+ void show(int16 x, int16 y);
+ void xShow(int16 x, int16 y);
+ bool solidAt(int16 x, int16 y);
+ uint16 moveVmap(uint8 *buf);
+};
+
+
+typedef Bitmap *BitmapPtr;
+
+} // End of namespace CGE
+
+#endif
diff --git a/engines/cge/cge.cpp b/engines/cge/cge.cpp
new file mode 100644
index 0000000000..ade3071497
--- /dev/null
+++ b/engines/cge/cge.cpp
@@ -0,0 +1,226 @@
+/* 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.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "common/config-manager.h"
+#include "common/debug.h"
+#include "common/debug-channels.h"
+#include "common/error.h"
+#include "common/EventRecorder.h"
+#include "common/file.h"
+#include "common/fs.h"
+#include "engines/util.h"
+#include "cge/cge.h"
+#include "cge/vga13h.h"
+#include "cge/cge_main.h"
+#include "cge/talk.h"
+#include "cge/text.h"
+#include "cge/walk.h"
+
+namespace CGE {
+
+const int CGEEngine::_maxCaveArr[5] = {1, 8, 16, 23, 24};
+
+CGEEngine::CGEEngine(OSystem *syst, const ADGameDescription *gameDescription)
+ : Engine(syst), _gameDescription(gameDescription), _randomSource("cge") {
+
+ // Debug/console setup
+ DebugMan.addDebugChannel(kCGEDebugBitmap, "bitmap", "CGE Bitmap debug channel");
+ DebugMan.addDebugChannel(kCGEDebugFile, "file", "CGE IO debug channel");
+ DebugMan.addDebugChannel(kCGEDebugEngine, "engine", "CGE Engine debug channel");
+
+ _startupMode = 1;
+ _demoText = kDemo;
+ _oldLev = 0;
+ _pocPtr = 0;
+
+}
+
+void CGEEngine::initCaveValues() {
+ for (int i = 0; i < kCaveMax; i++) {
+ _heroXY[i].x = 0;
+ _heroXY[i].y = 0;
+ }
+
+ for (int i = 0; i < kCaveMax + 1; i++) {
+ _barriers[i]._horz = 0xFF;
+ _barriers[i]._vert = 0xFF;
+ }
+}
+
+void CGEEngine::init() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::setup()");
+
+ // Initialise fields
+ _lastFrame = 0;
+ _lastTick = 0;
+ _hero = NULL;
+ _shadow = NULL;
+ _miniCave = NULL;
+ _miniShp = NULL;
+ _miniShpList = NULL;
+ _sprite = NULL;
+ _dat = new CFile(kDatName, XCrypt);
+ _cat = new BtFile(kCatName, XCrypt);
+
+ // Create debugger console
+ _console = new CGEConsole(this);
+
+ // Initialise classes that have static members
+ Bitmap::init();
+ Talk::init();
+ Cluster::init(this);
+
+ // Initialise engine objects
+ _text = new Text(this, "CGE");
+ _vga = new Vga();
+ _sys = new System(this);
+ _pocLight = new PocLight(this);
+ for (int i = 0; i < kPocketNX; i++)
+ _pocket[i] = NULL;
+ _horzLine = new HorizLine(this);
+ _infoLine = new InfoLine(this, kInfoW);
+ _cavLight = new CavLight(this);
+ _debugLine = new InfoLine(this, kScrWidth);
+ _snail = new Snail(this, false);
+ _snail_ = new Snail(this, true);
+
+ _mouse = new Mouse(this);
+ _keyboard = new Keyboard(this);
+ _eventManager = new EventManager();
+ _fx = new Fx(16); // must precede SOUND!!
+ _sound = new Sound(this);
+
+ _offUseCount = atoi(_text->getText(kOffUseCount));
+ _music = true;
+
+ for (int i = 0; i < kPocketNX; i++)
+ _pocref[i] = -1;
+ _volume[0] = 0;
+ _volume[1] = 0;
+
+ initCaveValues();
+
+ _maxCave = 0;
+ _dark = false;
+ _game = false;
+ _finis = false;
+ _now = 1;
+ _lev = -1;
+ _recentStep = -2;
+
+ for (int i = 0; i < 4; i++)
+ _flag[i] = false;
+
+ _mode = 0;
+ _soundOk = 1;
+ _sprTv = NULL;
+ _gameCase2Cpt = 0;
+ _offUseCount = 0;
+
+ _startGameSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1;
+}
+
+void CGEEngine::deinit() {
+ // Call classes with static members to clear them up
+ Talk::deinit();
+ Bitmap::deinit();
+ Cluster::init(this);
+
+ // Remove all of our debug levels here
+ DebugMan.clearAllDebugChannels();
+
+ delete _console;
+ _midiPlayer.killMidi();
+
+ // Delete engine objects
+ delete _vga;
+ delete _sys;
+ delete _sprite;
+ delete _miniCave;
+ delete _shadow;
+ delete _horzLine;
+ delete _infoLine;
+ delete _cavLight;
+ delete _debugLine;
+ delete _text;
+ delete _pocLight;
+ delete _keyboard;
+ delete _mouse;
+ delete _eventManager;
+ delete _fx;
+ delete _sound;
+ delete _snail;
+ delete _snail_;
+ delete _hero;
+ delete _dat;
+ delete _cat;
+
+ if (_miniShpList) {
+ for (int i = 0; _miniShpList[i]; ++i)
+ delete _miniShpList[i];
+ delete[] _miniShpList;
+ }
+}
+
+CGEEngine::~CGEEngine() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::~CGEEngine()");
+}
+
+Common::Error CGEEngine::run() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::run()");
+
+ if (_gameDescription->flags & ADGF_DEMO) {
+ warning("Demos of Soltys are not supported.\nPlease get a free version on ScummVM download page");
+ return Common::kUnsupportedGameidError;
+ }
+
+ // Initialize graphics using following:
+ initGraphics(320, 200, false);
+
+ // Setup necessary game objects
+ init();
+ // Run the game
+ cge_main();
+
+ // Remove game objects
+ deinit();
+
+ return Common::kNoError;
+}
+
+bool CGEEngine::hasFeature(EngineFeature f) const {
+ return
+ (f == kSupportsRTL) ||
+ (f == kSupportsLoadingDuringRuntime) ||
+ (f == kSupportsSavingDuringRuntime);
+}
+
+bool CGEEngine::canLoadGameStateCurrently() {
+ return (_startupMode == 0) && _mouse->_active;
+}
+
+bool CGEEngine::canSaveGameStateCurrently() {
+ return (_startupMode == 0) && _mouse->_active;
+}
+
+} // End of namespace CGE
diff --git a/engines/cge/cge.h b/engines/cge/cge.h
new file mode 100644
index 0000000000..5a4c0bf86d
--- /dev/null
+++ b/engines/cge/cge.h
@@ -0,0 +1,275 @@
+/* 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.
+*
+*/
+
+#ifndef CGE_H
+#define CGE_H
+
+#include "cge/general.h"
+#include "common/random.h"
+#include "common/savefile.h"
+#include "common/serializer.h"
+#include "common/str.h"
+#include "common/rect.h"
+#include "engines/engine.h"
+#include "gui/debugger.h"
+#include "graphics/surface.h"
+#include "engines/advancedDetector.h"
+#include "cge/console.h"
+#include "cge/bitmap.h"
+#include "cge/sound.h"
+
+namespace CGE {
+
+class Console;
+class Sprite;
+
+#define kSavegameVersion 2
+#define kSavegameStrSize 11
+#define kPocketX 174
+#define kPocketY 176
+#define kPocketDX 18
+#define kPocketDY 22
+#define kPocketNX 8
+#define kPocketNY 1
+#define kPocketSX 8
+#define kPocketSY 3
+#define kCaveDx 9
+#define kCaveDy 10
+#define kCaveNx 8
+#define kCaveNy 3
+#define kCaveMax kCaveNx * kCaveNy
+
+
+// our engine debug channels
+enum {
+ kCGEDebugBitmap = 1 << 0,
+ kCGEDebugFile = 1 << 1,
+ kCGEDebugEngine = 1 << 2
+};
+
+enum SnList {
+ kNear, kTake
+};
+
+enum CallbackType {
+ kNullCB = 0, kQGame, kMiniStep, kXCave, kSndSetVolume
+};
+
+struct SavegameHeader {
+ uint8 version;
+ Common::String saveName;
+ Graphics::Surface *thumbnail;
+ int saveYear, saveMonth, saveDay;
+ int saveHour, saveMinutes;
+ int totalFrames;
+};
+
+extern const char *savegameStr;
+
+struct Bar {
+ uint8 _horz;
+ uint8 _vert;
+};
+
+class CGEEngine : public Engine {
+private:
+ uint32 _lastFrame, _lastTick;
+ void tick();
+ void syncHeader(Common::Serializer &s);
+ static void writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader &header);
+ void syncGame(Common::SeekableReadStream *readStream, Common::WriteStream *writeStream, bool tiny = false);
+ bool savegameExists(int slotNumber);
+ Common::String generateSaveName(int slot);
+public:
+ CGEEngine(OSystem *syst, const ADGameDescription *gameDescription);
+ ~CGEEngine();
+ virtual bool hasFeature(EngineFeature f) const;
+ virtual bool canLoadGameStateCurrently();
+ virtual bool canSaveGameStateCurrently();
+ virtual Common::Error loadGameState(int slot);
+ virtual Common::Error saveGameState(int slot, const Common::String &desc);
+
+ static const int _maxCaveArr[5];
+
+ const ADGameDescription *_gameDescription;
+ int _startupMode;
+ int _demoText;
+ int _oldLev;
+ int _pocPtr;
+ bool _music;
+ int _pocref[kPocketNX];
+ uint8 _volume[2];
+ int _maxCave;
+ bool _flag[4];
+ bool _dark;
+ bool _game;
+ bool _finis;
+ int _now;
+ int _lev;
+ int _mode;
+ int _soundOk;
+ int _gameCase2Cpt;
+ int _offUseCount;
+
+ Sprite *_sprTv;
+ Sprite *_sprK1;
+ Sprite *_sprK2;
+ Sprite *_sprK3;
+
+ Common::Point _heroXY[kCaveMax];
+ Bar _barriers[kCaveMax];
+
+ Common::RandomSource _randomSource;
+ MusicPlayer _midiPlayer;
+ BitmapPtr *_miniShp;
+ BitmapPtr *_miniShpList;
+ int _startGameSlot;
+
+ virtual Common::Error run();
+ GUI::Debugger *getDebugger() {
+ return _console;
+ }
+
+ void cge_main();
+ void switchCave(int cav);
+ void startCountDown();
+ void quit();
+ void resetQSwitch();
+ void optionTouch(int opt, uint16 mask);
+ void resetGame();
+ bool loadGame(int slotNumber, SavegameHeader *header = NULL, bool tiny = false);
+ void setMapBrick(int x, int z);
+ void switchMapping();
+ void loadSprite(const char *fname, int ref, int cav, int col, int row, int pos);
+ void loadScript(const char *fname);
+ void loadUser();
+ void runGame();
+ bool showTitle(const char *name);
+ void movie(const char *ext);
+ void inf(const char *text);
+ void selectSound();
+ void dummy() {}
+ void NONE();
+ void SB();
+ void caveDown();
+ void caveUp();
+ void xCave();
+ void qGame();
+ void SBM();
+ void GUS();
+ void GUSM();
+ void MIDI();
+ void AUTO();
+ void setPortD();
+ void setPortM();
+ void setIRQ();
+ void setDMA();
+ void mainLoop();
+ void handleFrame();
+ void saveGame(int slotNumber, const Common::String &desc);
+ static bool readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header);
+ void switchMusic();
+ void selectPocket(int n);
+ void expandSprite(Sprite *spr);
+ void contractSprite(Sprite *spr);
+ int findPocket(Sprite *spr);
+ void feedSnail(Sprite *spr, SnList snq);
+ void pocFul();
+ void hide1(Sprite *spr);
+ void loadMapping();
+ void saveSound();
+ void heroCover(int cvr);
+ void trouble(int seq, int text);
+ void offUse();
+ void tooFar();
+ void loadHeroXY();
+ void keyClick();
+ void switchColorMode();
+ void killSprite();
+ void switchDebug();
+ void miniStep(int stp);
+ void postMiniStep(int stp);
+ void showBak(int ref);
+ void initCaveValues();
+
+ void snBackPt(Sprite *spr, int stp);
+ void snHBarrier(const int cave, const int barX);
+ void snVBarrier(const int cave, const int barY);
+ void snCover(Sprite *spr, int xref);
+ void snFlag(int indx, bool v);
+ void snFlash(bool on);
+ void snGame(Sprite *spr, int num);
+ void snGhost(Bitmap *bmp);
+ void snGive(Sprite *spr, int stp);
+ void snHide(Sprite *spr, int val);
+ void snKeep(Sprite *spr, int stp);
+ void snKill(Sprite *spr);
+ void snLevel(Sprite *spr, int lev);
+ void snLight(bool in);
+ void snMouse(bool on);
+ void snNNext(Sprite *spr, int p);
+ void snPort(Sprite *spr, int port);
+ void snReach(Sprite *spr, int mode);
+ void snRelZ(Sprite *spr, int z);
+ void snRNNext(Sprite *spr, int p);
+ void snRTNext(Sprite *spr, int p);
+ void snSend(Sprite *spr, int val);
+ void snRelX(Sprite *spr, int x);
+ void snRelY(Sprite *spr, int y);
+ void snRmNear(Sprite *spr);
+ void snRmTake(Sprite *spr);
+ void snRSeq(Sprite *spr, int val);
+ void snSeq(Sprite *spr, int val);
+ void snSetRef(Sprite *spr, int nr);
+ void snSetX(Sprite *spr, int x);
+ void snSetX0(int cav, int x0);
+ void snSetXY(Sprite *spr, uint16 xy);
+ void snSetY(Sprite *spr, int y);
+ void snSetY0(int cav, int y0);
+ void snSetZ(Sprite *spr, int z);
+ void snSlave(Sprite *spr, int ref);
+ void snSound(Sprite *spr, int wav);
+ void snSwap(Sprite *spr, int xref);
+ void snTNext(Sprite *spr, int p);
+ void snTrans(Sprite *spr, int trans);
+ void snUncover(Sprite *spr, Sprite *xspr);
+ void snWalk(Sprite *spr, int x, int y);
+ void snZTrim(Sprite *spr);
+protected:
+ int _recentStep;
+
+private:
+ CGEConsole *_console;
+ void init();
+ void deinit();
+};
+
+// Example console class
+class Console : public GUI::Debugger {
+public:
+ Console(CGEEngine *vm) {}
+ virtual ~Console() {}
+};
+
+} // End of namespace CGE
+
+#endif
diff --git a/engines/cge/cge_main.cpp b/engines/cge/cge_main.cpp
new file mode 100644
index 0000000000..d3c88845c2
--- /dev/null
+++ b/engines/cge/cge_main.cpp
@@ -0,0 +1,1572 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#include "common/scummsys.h"
+#include "common/endian.h"
+#include "common/memstream.h"
+#include "common/savefile.h"
+#include "common/serializer.h"
+#include "common/str.h"
+#include "graphics/palette.h"
+#include "graphics/scaler.h"
+#include "graphics/thumbnail.h"
+#include "cge/general.h"
+#include "cge/sound.h"
+#include "cge/vga13h.h"
+#include "cge/snail.h"
+#include "cge/text.h"
+#include "cge/game.h"
+#include "cge/events.h"
+#include "cge/talk.h"
+#include "cge/vmenu.h"
+#include "cge/cge_main.h"
+#include "cge/cge.h"
+#include "cge/walk.h"
+#include "cge/sound.h"
+
+namespace CGE {
+
+uint16 _stklen = (kStackSize * 2);
+
+Vga *_vga;
+System *_sys;
+Sprite *_pocLight;
+EventManager *_eventManager;
+Keyboard *_keyboard;
+Mouse *_mouse;
+Sprite *_pocket[kPocketNX];
+Sprite *_sprite;
+Sprite *_miniCave;
+Sprite *_shadow;
+HorizLine *_horzLine;
+InfoLine *_infoLine;
+Sprite *_cavLight;
+InfoLine *_debugLine;
+
+Snail *_snail;
+Snail *_snail_;
+
+Fx *_fx;
+Sound *_sound;
+CFile *_dat;
+BtFile *_cat;
+
+// 0.75 - 17II95 - full sound support
+// 0.76 - 18II95 - small MiniEMS in DEMO,
+// unhide CavLight in SNLEVEL
+// keyclick suppress in startup
+// keyclick on key service in: SYSTEM, GET_TEXT
+// 1.01 - 17VII95 - default savegame with sound ON
+// coditionals EVA for 2-month evaluation version
+
+const char *savegameStr = "SCUMMVM_CGE";
+
+//--------------------------------------------------------------------------
+
+const Dac g_stdPal[] = {// R G B
+ { 0, 60, 0}, // 198
+ { 0, 104, 0}, // 199
+ { 20, 172, 0}, // 200
+ { 82, 82, 0}, // 201
+ { 0, 132, 82}, // 202
+ { 132, 173, 82}, // 203
+ { 82, 0, 0}, // 204
+ { 206, 0, 24}, // 205
+ { 255, 33, 33}, // 206
+ { 123, 41, 0}, // 207
+ { 0, 41, 0}, // 208
+ { 0, 0, 82}, // 209
+ { 132, 0, 0}, // 210
+ { 255, 0, 0}, // 211
+ { 255, 66, 66}, // 212
+ { 148, 66, 16}, // 213
+ { 0, 82, 0}, // 214
+ { 0, 0, 132}, // 215
+ { 173, 0, 0}, // 216
+ { 255, 49, 0}, // 217
+ { 255, 99, 99}, // 218
+ { 181, 107, 49}, // 219
+ { 0, 132, 0}, // 220
+ { 0, 0, 255}, // 221
+ { 173, 41, 0}, // 222
+ { 255, 82, 0}, // 223
+ { 255, 132, 132}, // 224
+ { 214, 148, 74}, // 225
+ { 41, 214, 0}, // 226
+ { 0, 82, 173}, // 227
+ { 255, 214, 0}, // 228
+ { 247, 132, 49}, // 229
+ { 255, 165, 165}, // 230
+ { 239, 198, 123}, // 231
+ { 173, 214, 0}, // 232
+ { 0, 132, 214}, // 233
+ { 57, 57, 57}, // 234
+ { 247, 189, 74}, // 235
+ { 255, 198, 198}, // 236
+ { 255, 239, 173}, // 237
+ { 214, 255, 173}, // 238
+ { 82, 173, 255}, // 239
+ { 107, 107, 107}, // 240
+ { 247, 222, 99}, // 241
+ { 255, 0, 255}, // 242
+ { 255, 132, 255}, // 243
+ { 132, 132, 173}, // 244
+ { 148, 247, 255}, // 245
+ { 148, 148, 148}, // 246
+ { 82, 0, 82}, // 247
+ { 112, 68, 112}, // 248
+ { 176, 88, 144}, // 249
+ { 214, 132, 173}, // 250
+ { 206, 247, 255}, // 251
+ { 198, 198, 198}, // 252
+ { 0, 214, 255}, // 253
+ { 96, 224, 96 }, // 254
+ { 255, 255, 255}, // 255
+};
+
+void CGEEngine::syncHeader(Common::Serializer &s) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::syncHeader(s)");
+
+ int i;
+
+ s.syncAsUint16LE(_now);
+ s.syncAsUint16LE(_oldLev);
+ s.syncAsUint16LE(_demoText);
+ for (i = 0; i < 5; i++)
+ s.syncAsUint16LE(_game);
+ s.syncAsSint16LE(i); // unused VGA::Mono variable
+ s.syncAsUint16LE(_music);
+ s.syncBytes(_volume, 2);
+ for (i = 0; i < 4; i++)
+ s.syncAsUint16LE(_flag[i]);
+
+ if (s.isLoading()) {
+ // Reset cave values
+ initCaveValues();
+ }
+
+ for (i = 0; i < kCaveMax; i++) {
+ s.syncAsSint16LE(_heroXY[i].x);
+ s.syncAsUint16LE(_heroXY[i].y);
+ }
+ for (i = 0; i < 1 + kCaveMax; i++) {
+ s.syncAsByte(_barriers[i]._horz);
+ s.syncAsByte(_barriers[i]._vert);
+ }
+ for (i = 0; i < kPocketNX; i++)
+ s.syncAsUint16LE(_pocref[i]);
+
+ if (s.isSaving()) {
+ // Write checksum
+ int checksum = kSavegameCheckSum;
+ s.syncAsUint16LE(checksum);
+ } else {
+ // Read checksum and validate it
+ uint16 checksum;
+ s.syncAsUint16LE(checksum);
+ if (checksum != kSavegameCheckSum)
+ error("%s", _text->getText(kBadSVG));
+ }
+}
+
+bool CGEEngine::loadGame(int slotNumber, SavegameHeader *header, bool tiny) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::loadgame(%d, header, %s)", slotNumber, tiny ? "true" : "false");
+
+ Common::MemoryReadStream *readStream;
+ SavegameHeader saveHeader;
+
+ if (slotNumber == -1) {
+ // Loading the data for the initial game state
+ kSavegame0File file = kSavegame0File(kSavegame0Name);
+ int size = file.size();
+ byte *dataBuffer = (byte *)malloc(size);
+ file.read(dataBuffer, size);
+ readStream = new Common::MemoryReadStream(dataBuffer, size, DisposeAfterUse::YES);
+
+ } else {
+ // Open up the savgame file
+ Common::String slotName = generateSaveName(slotNumber);
+ Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(slotName);
+
+ // Read the data into a data buffer
+ int size = saveFile->size();
+ byte *dataBuffer = (byte *)malloc(size);
+ saveFile->read(dataBuffer, size);
+ readStream = new Common::MemoryReadStream(dataBuffer, size, DisposeAfterUse::YES);
+ }
+
+ // Check to see if it's a ScummVM savegame or not
+ char buffer[kSavegameStrSize + 1];
+ readStream->read(buffer, kSavegameStrSize + 1);
+
+ if (strncmp(buffer, savegameStr, kSavegameStrSize + 1) != 0) {
+ // It's not, so rewind back to the start
+ readStream->seek(0);
+
+ if (header)
+ // Header wanted where none exists, so return false
+ return false;
+ } else {
+ // Found header
+ if (!readSavegameHeader(readStream, saveHeader)) {
+ delete readStream;
+ return false;
+ }
+
+ if (header) {
+ *header = saveHeader;
+ delete readStream;
+ return true;
+ }
+
+ // Delete the thumbnail
+ saveHeader.thumbnail->free();
+ delete saveHeader.thumbnail;
+ }
+
+ // Get in the savegame
+ syncGame(readStream, NULL, tiny);
+
+ delete readStream;
+ return true;
+}
+
+/**
+ * Returns true if a given savegame exists
+ */
+bool CGEEngine::savegameExists(int slotNumber) {
+ Common::String slotName = generateSaveName(slotNumber);
+
+ Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(slotName);
+ bool result = saveFile != NULL;
+ delete saveFile;
+ return result;
+}
+
+/**
+ * Support method that generates a savegame name
+ * @param slot Slot number
+ */
+Common::String CGEEngine::generateSaveName(int slot) {
+ return Common::String::format("%s.%03d", _targetName.c_str(), slot);
+}
+
+Common::Error CGEEngine::loadGameState(int slot) {
+ // Clear current game activity
+ caveDown();
+ resetGame();
+
+ // Load the game
+ loadGame(slot, NULL);
+ _snail->addCom(kSnLevel, -1, _oldLev, &_cavLight);
+ _cavLight->gotoxy(kCaveX + ((_now - 1) % kCaveNx) * kCaveDx + kCaveSX,
+ kCaveY + ((_now - 1) / kCaveNx) * kCaveDy + kCaveSY);
+ caveUp();
+
+ return Common::kNoError;
+}
+
+void CGEEngine::resetGame() {
+ _vga->_spareQ->clear();
+}
+
+Common::Error CGEEngine::saveGameState(int slot, const Common::String &desc) {
+ caveDown();
+ _oldLev = _lev;
+ saveSound();
+
+ // Write out the user's progress
+ saveGame(slot, desc);
+
+ // Reload the scene
+ caveUp();
+
+ return Common::kNoError;
+}
+
+void CGEEngine::saveSound() {
+ warning("STUB: CGEEngine::saveSound");
+}
+
+void CGEEngine::saveGame(int slotNumber, const Common::String &desc) {
+ // Set up the serializer
+ Common::String slotName = generateSaveName(slotNumber);
+ Common::OutSaveFile *saveFile = g_system->getSavefileManager()->openForSaving(slotName);
+
+ // Write out the ScummVM savegame header
+ SavegameHeader header;
+ header.saveName = desc;
+ header.version = kSavegameVersion;
+ writeSavegameHeader(saveFile, header);
+
+ // Write out the data of the savegame
+ syncGame(NULL, saveFile, false);
+
+ // Finish writing out game data
+ saveFile->finalize();
+ delete saveFile;
+}
+
+void CGEEngine::writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader &header) {
+ // Write out a savegame header
+ out->write(savegameStr, kSavegameStrSize + 1);
+
+ out->writeByte(kSavegameVersion);
+
+ // Write savegame name
+ out->write(header.saveName.c_str(), header.saveName.size() + 1);
+
+ // Get the active palette
+ uint8 thumbPalette[256 * 3];
+ g_system->getPaletteManager()->grabPalette(thumbPalette, 0, 256);
+
+ // Create a thumbnail and save it
+ Graphics::Surface *thumb = new Graphics::Surface();
+ Graphics::Surface *s = _vga->_page[0];
+ ::createThumbnail(thumb, (const byte *)s->pixels, kScrWidth, kScrHeight, thumbPalette);
+ Graphics::saveThumbnail(*out, *thumb);
+ thumb->free();
+ delete thumb;
+
+ // Write out the save date/time
+ TimeDate td;
+ g_system->getTimeAndDate(td);
+ out->writeSint16LE(td.tm_year + 1900);
+ out->writeSint16LE(td.tm_mon + 1);
+ out->writeSint16LE(td.tm_mday);
+ out->writeSint16LE(td.tm_hour);
+ out->writeSint16LE(td.tm_min);
+}
+
+void CGEEngine::syncGame(Common::SeekableReadStream *readStream, Common::WriteStream *writeStream, bool tiny) {
+ Common::Serializer s(readStream, writeStream);
+
+ if (s.isSaving()) {
+ for (int i = 0; i < kPocketNX; i++) {
+ register Sprite *pocSpr = _pocket[i];
+ _pocref[i] = (pocSpr) ? pocSpr->_ref : -1;
+ }
+
+ // Skip Digital and Midi volumes, useless under ScummVM
+ _volume[0] = 0;
+ _volume[1] = 0;
+ }
+
+ // Synchronise header data
+ syncHeader(s);
+
+ if (s.isSaving()) {
+ // Loop through saving the sprite data
+ for (Sprite *spr = _vga->_spareQ->first(); spr; spr = spr->_next) {
+ if (!s.err())
+ spr->sync(s);
+ }
+ } else {
+ // Loading game
+ if (_soundOk == 1 && _mode == 0) {
+ // Skip Digital and Midi volumes, useless under ScummVM
+ sndSetVolume();
+ }
+
+ if (!tiny) { // load sprites & pocket
+ while (readStream->pos() < readStream->size()) {
+ Sprite S(this, NULL);
+ S.sync(s);
+
+ S._prev = S._next = NULL;
+ Sprite *spr = (scumm_stricmp(S._file + 2, "MUCHA") == 0) ? new Fly(this, NULL)
+ : new Sprite(this, NULL);
+ assert(spr != NULL);
+ *spr = S;
+ _vga->_spareQ->append(spr);
+ }
+
+ for (int i = 0; i < kPocketNX; i++) {
+ register int r = _pocref[i];
+ _pocket[i] = (r < 0) ? NULL : _vga->_spareQ->locate(r);
+ }
+ }
+ }
+}
+
+bool CGEEngine::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header) {
+ header.thumbnail = NULL;
+
+ // Get the savegame version
+ header.version = in->readByte();
+ if (header.version > kSavegameVersion)
+ return false;
+
+ // Read in the string
+ header.saveName.clear();
+ char ch;
+ while ((ch = (char)in->readByte()) != '\0')
+ header.saveName += ch;
+
+ // Get the thumbnail
+ header.thumbnail = Graphics::loadThumbnail(*in);
+ if (!header.thumbnail) {
+ delete header.thumbnail;
+ header.thumbnail = NULL;
+ return false;
+ }
+
+ // Read in save date/time
+ header.saveYear = in->readSint16LE();
+ header.saveMonth = in->readSint16LE();
+ header.saveDay = in->readSint16LE();
+ header.saveHour = in->readSint16LE();
+ header.saveMinutes = in->readSint16LE();
+
+ return true;
+}
+
+void CGEEngine::heroCover(int cvr) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::heroCover(%d)", cvr);
+
+ _snail->addCom(kSnCover, 1, cvr, NULL);
+}
+
+void CGEEngine::trouble(int seq, int text) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::trouble(%d, %d)", seq, text);
+
+ _hero->park();
+ _snail->addCom(kSnWait, -1, -1, _hero);
+ _snail->addCom(kSnSeq, -1, seq, _hero);
+ _snail->addCom(kSnSound, -1, 2, _hero);
+ _snail->addCom(kSnWait, -1, -1, _hero);
+ _snail->addCom(kSnSay, 1, text, _hero);
+}
+
+void CGEEngine::offUse() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::offUse()");
+
+ trouble(kSeqOffUse, kOffUse + newRandom(_offUseCount));
+}
+
+void CGEEngine::tooFar() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::tooFar()");
+
+ trouble(kSeqTooFar, kTooFar);
+}
+
+void CGEEngine::loadHeroXY() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::loadHeroXY()");
+
+ VFile cf("CGE.HXY");
+ uint16 x, y;
+
+ memset(_heroXY, 0, sizeof(_heroXY));
+ if (!cf._error) {
+ for (int i = 0; i < kCaveMax; ++i) {
+ cf.read((byte *)&x, 2);
+ cf.read((byte *)&y, 2);
+
+ _heroXY[i].x = (int16)FROM_LE_16(x);
+ _heroXY[i].y = (int16)FROM_LE_16(y);
+ }
+ }
+}
+
+void CGEEngine::loadMapping() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::loadMapping()");
+
+ if (_now <= kCaveMax) {
+ VFile cf("CGE.TAB");
+ if (!cf._error) {
+ // Move to the data for the given room
+ cf.seek((_now - 1) * kMapArrSize);
+
+ // Read in the data
+ for (int z = 0; z < kMapZCnt; ++z) {
+ cf.read(&Cluster::_map[z][0], kMapXCnt);
+ }
+ }
+ }
+}
+
+Square::Square(CGEEngine *vm) : Sprite(vm, NULL), _vm(vm) {
+ _flags._kill = true;
+ _flags._bDel = false;
+
+ BitmapPtr *MB = new BitmapPtr[2];
+ MB[0] = new Bitmap("BRICK");
+ MB[1] = NULL;
+ setShapeList(MB);
+}
+
+void Square::touch(uint16 mask, int x, int y) {
+ Sprite::touch(mask, x, y);
+ if (mask & kMouseLeftUp) {
+ XZ(_x + x, _y + y).cell() = 0;
+ _snail_->addCom(kSnKill, -1, 0, this);
+ }
+}
+
+void CGEEngine::setMapBrick(int x, int z) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::setMapBrick(%d, %d)", x, z);
+
+ Square *s = new Square(this);
+ if (s) {
+ char n[6];
+ s->gotoxy(x * kMapGridX, kMapTop + z * kMapGridZ);
+ sprintf(n, "%02d:%02d", x, z);
+ Cluster::_map[z][x] = 1;
+ s->setName(n);
+ _vga->_showQ->insert(s, _vga->_showQ->first());
+ }
+}
+
+void CGEEngine::keyClick() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::keyClick()");
+
+ _snail_->addCom(kSnSound, -1, 5, NULL);
+}
+
+void CGEEngine::resetQSwitch() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::resetQSwitch()");
+
+ _snail_->addCom(kSnSeq, 123, 0, NULL);
+ keyClick();
+}
+
+void CGEEngine::quit() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::quit()");
+
+ static Choice QuitMenu[] = {
+ { NULL, &CGEEngine::startCountDown },
+ { NULL, &CGEEngine::resetQSwitch },
+ { NULL, &CGEEngine::dummy }
+ };
+
+ if (_snail->idle() && !_hero->_flags._hide) {
+ if (Vmenu::_addr) {
+ _snail_->addCom(kSnKill, -1, 0, Vmenu::_addr);
+ resetQSwitch();
+ } else {
+ QuitMenu[0]._text = _text->getText(kQuit);
+ QuitMenu[1]._text = _text->getText(kNoQuit);
+ (new Vmenu(this, QuitMenu, -1, -1))->setName(_text->getText(kQuitTitle));
+ _snail_->addCom(kSnSeq, 123, 1, NULL);
+ keyClick();
+ }
+ }
+}
+
+void CGEEngine::miniStep(int stp) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::miniStep(%d)", stp);
+
+ if (stp < 0) {
+ _miniCave->_flags._hide = true;
+ } else {
+ *_miniShp[0] = *_miniShpList[stp];
+ if (_fx->_current)
+ &*(_fx->_current->addr());
+
+ _miniCave->_flags._hide = false;
+ }
+}
+
+void CGEEngine::postMiniStep(int step) {
+ debugC(6, kCGEDebugEngine, "CGEEngine::postMiniStep(%d)", step);
+
+ if (_miniCave && step != _recentStep)
+ _snail_->addCom2(kSnExec, -1, _recentStep = step, kMiniStep);
+}
+
+void CGEEngine::showBak(int ref) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::showBack(%d)", ref);
+
+ Sprite *spr = _vga->_spareQ->locate(ref);
+ if (!spr)
+ return;
+
+ Bitmap::_pal = _vga->_sysPal;
+ spr->expand();
+ Bitmap::_pal = NULL;
+ spr->show(2);
+ _vga->copyPage(1, 2);
+ _sys->setPal();
+ spr->contract();
+}
+
+void CGEEngine::caveUp() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::caveUp()");
+
+ const int BakRef = 1000 * _now;
+ if (_music)
+ _midiPlayer.loadMidi(_now);
+
+ showBak(BakRef);
+ loadMapping();
+ Sprite *spr = _vga->_spareQ->first();
+ while (spr) {
+ Sprite *n = spr->_next;
+ if (spr->_cave == _now || spr->_cave == 0)
+ if (spr->_ref != BakRef) {
+ if (spr->_flags._back)
+ spr->backShow();
+ else
+ expandSprite(spr);
+ }
+ spr = n;
+ }
+
+ _sound->stop();
+ _fx->clear();
+ _fx->preload(0);
+ _fx->preload(BakRef);
+
+ if (_hero) {
+ _hero->gotoxy(_heroXY[_now - 1].x, _heroXY[_now - 1].y);
+ // following 2 lines trims Hero's Z position!
+ _hero->tick();
+ _hero->_time = 1;
+ _hero->_flags._hide = false;
+ }
+
+ if (!_dark)
+ _vga->sunset();
+
+ _vga->copyPage(0, 1);
+ selectPocket(-1);
+ if (_hero)
+ _vga->_showQ->insert(_vga->_showQ->remove(_hero));
+
+ if (_shadow) {
+ _vga->_showQ->remove(_shadow);
+ _shadow->makeXlat(glass(_vga->_sysPal, 204, 204, 204));
+ _vga->_showQ->insert(_shadow, _hero);
+ _shadow->_z = _hero->_z;
+ }
+ feedSnail(_vga->_showQ->locate(BakRef + 999), kTake);
+ _vga->show();
+ _vga->copyPage(1, 0);
+ _vga->show();
+ _vga->sunrise(_vga->_sysPal);
+ _dark = false;
+ if (!_startupMode)
+ _mouse->on();
+}
+
+void CGEEngine::caveDown() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::caveDown()");
+
+ if (_horzLine && !_horzLine->_flags._hide)
+ switchMapping();
+
+ for (Sprite *spr = _vga->_showQ->first(); spr;) {
+ Sprite *n = spr->_next;
+ if (spr->_ref >= 1000 /*&& spr->_cave*/) {
+ if (spr->_ref % 1000 == 999)
+ feedSnail(spr, kTake);
+ _vga->_spareQ->append(_vga->_showQ->remove(spr));
+ }
+ spr = n;
+ }
+}
+
+void CGEEngine::xCave() {
+ debugC(6, kCGEDebugEngine, "CGEEngine::xCave()");
+
+ caveDown();
+ caveUp();
+}
+
+void CGEEngine::qGame() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::qGame()");
+
+ caveDown();
+ _oldLev = _lev;
+ saveSound();
+
+ // Write out the user's progress
+ saveGame(0, Common::String("Automatic Savegame"));
+
+ _vga->sunset();
+ _finis = true;
+}
+
+void CGEEngine::switchCave(int cav) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::switchCave(%d)", cav);
+
+ if (cav == _now)
+ return;
+
+ if (cav < 0) {
+ _snail->addCom(kSnLabel, -1, 0, NULL); // wait for repaint
+ _snail->addCom2(kSnExec, -1, 0, kQGame); // switch cave
+ } else {
+ _now = cav;
+ _mouse->off();
+ if (_hero) {
+ _hero->park();
+ _hero->step(0);
+ _vga->_spareQ->_show = 0;
+ }
+ _cavLight->gotoxy(kCaveX + ((_now - 1) % kCaveNx) * kCaveDx + kCaveSX,
+ kCaveY + ((_now - 1) / kCaveNx) * kCaveDy + kCaveSY);
+ killText();
+ if (!_startupMode)
+ keyClick();
+ _snail->addCom(kSnLabel, -1, 0, NULL); // wait for repaint
+ _snail->addCom2(kSnExec, 0, 0, kXCave); // switch cave
+ }
+}
+
+System::System(CGEEngine *vm) : Sprite(vm, NULL), _vm(vm) {
+ _funDel = kHeroFun0;
+ setPal();
+ tick();
+}
+
+void System::setPal() {
+ Dac *p = _vga->_sysPal + 256 - ARRAYSIZE(g_stdPal);
+ for (uint i = 0; i < ARRAYSIZE(g_stdPal); i++) {
+ p[i]._r = g_stdPal[i]._r >> 2;
+ p[i]._g = g_stdPal[i]._g >> 2;
+ p[i]._b = g_stdPal[i]._b >> 2;
+ }
+}
+
+void System::funTouch() {
+ uint16 n = (_vm->_flag[0]) ? kHeroFun1 : kHeroFun0; // PAIN flag
+ if (_talk == NULL || n > _funDel)
+ _funDel = n;
+}
+
+void System::touch(uint16 mask, int x, int y) {
+ funTouch();
+
+ if (mask & kEventKeyb) {
+ _vm->keyClick();
+ killText();
+ if (_vm->_startupMode == 1) {
+ _snail->addCom(kSnClear, -1, 0, NULL);
+ return;
+ }
+ switch (x) {
+ case 'X':
+ if (_keyboard->_key[kKeyAlt])
+ _vm->quit();
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ if (_keyboard->_key[kKeyAlt]) {
+ _snail->addCom(kSnLevel, -1, x - '0', NULL);
+ break;
+ }
+ break;
+ }
+ } else {
+ if (_vm->_startupMode)
+ return;
+ int cav = 0;
+ _infoLine->update(NULL);
+ if (y >= kWorldHeight ) {
+ if (x < kButtonX) { // select cave?
+ if (y >= kCaveY && y < kCaveY + kCaveNy * kCaveDy &&
+ x >= kCaveX && x < kCaveX + kCaveNx * kCaveDx && !_vm->_game) {
+ cav = ((y - kCaveY) / kCaveDy) * kCaveNx + (x - kCaveX) / kCaveDx + 1;
+ if (cav > _vm->_maxCave)
+ cav = 0;
+ } else {
+ cav = 0;
+ }
+ } else if (mask & kMouseLeftUp) {
+ if (y >= kPocketY && y < kPocketY + kPocketNY * kPocketDY &&
+ x >= kPocketX && x < kPocketX + kPocketNX * kPocketDX) {
+ int n = ((y - kPocketY) / kPocketDY) * kPocketNX + (x - kPocketX) / kPocketDX;
+ _vm->selectPocket(n);
+ }
+ }
+ }
+
+ _vm->postMiniStep(cav - 1);
+
+ if (mask & kMouseLeftUp) {
+ if (cav && _snail->idle() && _hero->_tracePtr < 0)
+ _vm->switchCave(cav);
+
+ if (_horzLine && !_horzLine->_flags._hide) {
+ if (y >= kMapTop && y < kMapTop + kMapHig) {
+ Cluster tmpCluster = XZ(x, y);
+ int16 x1 = tmpCluster._pt.x;
+ int16 z1 = tmpCluster._pt.y;
+ Cluster::_map[z1][x1] = 1;
+ _vm->setMapBrick(x1, z1);
+ }
+ } else {
+ if (!_talk && _snail->idle() && _hero
+ && y >= kMapTop && y < kMapTop + kMapHig && !_vm->_game) {
+ _hero->findWay(XZ(x, y));
+ }
+ }
+ }
+ }
+}
+
+void System::tick() {
+ if (!_vm->_startupMode)
+ if (--_funDel == 0) {
+ killText();
+ if (_snail->idle()) {
+ if (_vm->_flag[0]) // Pain flag
+ _vm->heroCover(9);
+ else { // CHECKME: Before, was: if (Startup::_core >= CORE_MID) {
+ int n = newRandom(100);
+ if (n > 96)
+ _vm->heroCover(6 + (_hero->_x + _hero->_w / 2 < kScrWidth / 2));
+ else if (n > 90)
+ _vm->heroCover(5);
+ else if (n > 60)
+ _vm->heroCover(4);
+ else
+ _vm->heroCover(3);
+ }
+ }
+ funTouch();
+ }
+ _time = kSystemRate;
+}
+
+void CGEEngine::switchColorMode() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::switchColorMode()");
+
+ _snail_->addCom(kSnSeq, 121, _vga->_mono = !_vga->_mono, NULL);
+ keyClick();
+ _vga->setColors(_vga->_sysPal, 64);
+}
+
+void CGEEngine::switchMusic() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::switchMusic()");
+
+ _snail_->addCom(kSnSeq, 122, (_music = !_music), NULL);
+ keyClick();
+
+ if (_music)
+ _midiPlayer.loadMidi(_now);
+ else
+ _midiPlayer.killMidi();
+}
+
+void CGEEngine::startCountDown() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::startCountDown()");
+
+ //SNPOST(SNSEQ, 123, 0, NULL);
+ switchCave(-1);
+}
+
+void CGEEngine::switchMapping() {
+ assert(_horzLine);
+ debugC(1, kCGEDebugEngine, "CGEEngine::switchMapping()");
+
+ if (_horzLine && _horzLine->_flags._hide) {
+ for (int i = 0; i < kMapZCnt; i++) {
+ for (int j = 0; j < kMapXCnt; j++) {
+ if (Cluster::_map[i][j])
+ setMapBrick(j, i);
+ }
+ }
+ } else {
+ for (Sprite *s = _vga->_showQ->first(); s; s = s->_next)
+ if (s->_w == kMapGridX && s->_h == kMapGridZ)
+ _snail_->addCom(kSnKill, -1, 0, s);
+ }
+ _horzLine->_flags._hide = !_horzLine->_flags._hide;
+}
+
+void CGEEngine::killSprite() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::killSprite()");
+
+ _sprite->_flags._kill = true;
+ _sprite->_flags._bDel = true;
+ _snail_->addCom(kSnKill, -1, 0, _sprite);
+ _sprite = NULL;
+}
+
+void CGEEngine::optionTouch(int opt, uint16 mask) {
+ switch (opt) {
+ case 1:
+ if (mask & kMouseLeftUp)
+ switchColorMode();
+ break;
+ case 2:
+ if (mask & kMouseLeftUp)
+ switchMusic();
+ else if (mask & kMouseRightUp)
+ warning("TODO: Use ScummVM sound dialog");
+ break;
+ case 3:
+ if (mask & kMouseLeftUp)
+ quit();
+ break;
+ }
+}
+
+#pragma argsused
+void Sprite::touch(uint16 mask, int x, int y) {
+ _sys->funTouch();
+
+ if ((mask & kEventAttn) != 0)
+ return;
+
+ _infoLine->update(name());
+
+ if (mask & (kMouseRightDown | kMouseLeftDown))
+ _sprite = this;
+
+ if (_ref / 10 == 12) {
+ _vm->optionTouch(_ref % 10, mask);
+ return;
+ }
+
+ if (_flags._syst)
+ return; // cannot access system sprites
+
+ if (_vm->_game)
+ if (mask & kMouseLeftUp) {
+ mask &= ~kMouseLeftUp;
+ mask |= kMouseRightUp;
+ }
+
+ if ((mask & kMouseRightUp) && _snail->idle()) {
+ Sprite *ps = (_pocLight->_seqPtr) ? _pocket[_vm->_pocPtr] : NULL;
+ if (ps) {
+ if (_flags._kept || _hero->distance(this) < kDistMax) {
+ if (works(ps)) {
+ _vm->feedSnail(ps, kTake);
+ } else
+ _vm->offUse();
+ _vm->selectPocket(-1);
+ } else
+ _vm->tooFar();
+ } else {
+ if (_flags._kept) {
+ mask |= kMouseLeftUp;
+ } else {
+ if (_hero->distance(this) < kDistMax) {
+ if (_flags._port) {
+ if (_vm->findPocket(NULL) < 0) {
+ _vm->pocFul();
+ } else {
+ _snail->addCom(kSnReach, -1, -1, this);
+ _snail->addCom(kSnKeep, -1, -1, this);
+ _flags._port = false;
+ }
+ } else {
+ if (_takePtr != kNoPtr) {
+ if (snList(kTake)[_takePtr]._com == kSnNext)
+ _vm->offUse();
+ else
+ _vm->feedSnail(this, kTake);
+ } else {
+ _vm->offUse();
+ }
+ }
+ } else {
+ _vm->tooFar();
+ }
+ }
+ }
+ }
+
+ if ((mask & kMouseLeftUp) && _snail->idle()) {
+ if (_flags._kept) {
+ for (int n = 0; n < kPocketNX; n++) {
+ if (_pocket[n] == this) {
+ _vm->selectPocket(n);
+ break;
+ }
+ }
+ } else {
+ _snail->addCom(kSnWalk, -1, -1, this); // Hero->FindWay(this);
+ }
+ }
+}
+
+void CGEEngine::loadSprite(const char *fname, int ref, int cav, int col = 0, int row = 0, int pos = 0) {
+ static const char *Comd[] = { "Name", "Type", "Phase", "East",
+ "Left", "Right", "Top", "Bottom",
+ "Seq", "Near", "Take",
+ "Portable", "Transparent",
+ NULL
+ };
+ static const char *Type[] = { "DEAD", "AUTO", "WALK", "NEWTON", "LISSAJOUS",
+ "FLY", NULL
+ };
+
+ int shpcnt = 0;
+ int type = 0; // DEAD
+ bool east = false;
+ bool port = false;
+ bool tran = false;
+ int i, lcnt = 0;
+
+ char line[kLineMax];
+ mergeExt(line, fname, kSprExt);
+
+ if (_cat->exist(line)) { // sprite description file exist
+ VFile sprf(line);
+ if (sprf._error)
+ error("Bad SPR [%s]", line);
+
+ uint16 len;
+ while ((len = sprf.read((uint8 *)line)) != 0) {
+ lcnt++;
+ if (len && line[len - 1] == '\n')
+ line[--len] = '\0';
+ if (len == 0 || *line == '.')
+ continue;
+
+ if ((i = takeEnum(Comd, strtok(line, " =\t"))) < 0)
+ error("Bad line %d [%s]", lcnt, fname);
+
+
+ switch (i) {
+ case 0 : // Name - will be taken in Expand routine
+ break;
+ case 1 : // Type
+ if ((type = takeEnum(Type, strtok(NULL, " \t,;/"))) < 0)
+ error("Bad line %d [%s]", lcnt, fname);
+ break;
+ case 2 : // Phase
+ shpcnt++;
+ break;
+ case 3 : // East
+ east = (atoi(strtok(NULL, " \t,;/")) != 0);
+ break;
+ case 11 : // Portable
+ port = (atoi(strtok(NULL, " \t,;/")) != 0);
+ break;
+ case 12 : // Transparent
+ tran = (atoi(strtok(NULL, " \t,;/")) != 0);
+ break;
+ }
+ }
+ if (! shpcnt)
+ error("No shapes [%s]", fname);
+ } else { // no sprite description: mono-shaped sprite with only .BMP file
+ ++shpcnt;
+ }
+
+ // make sprite of choosen type
+ switch (type) {
+ case 1:
+ // AUTO
+ _sprite = new Sprite(this, NULL);
+ if (_sprite) {
+ _sprite->gotoxy(col, row);
+ //Sprite->Time = 1;//-----------$$$$$$$$$$$$$$$$
+ }
+ break;
+ case 2:
+ { // WALK
+ Walk *w = new Walk(this, NULL);
+ if (w && ref == 1) {
+ w->gotoxy(col, row);
+ if (_hero)
+ error("2nd HERO [%s]", fname);
+ _hero = w;
+ }
+ _sprite = w;
+ break;
+ }
+ /*
+ case 3 : // NEWTON
+ NEWTON * n = new NEWTON(NULL);
+ if (n)
+ {
+ n->Ay = (bottom-n->H);
+ n->By = 90;
+ n->Cy = 3;
+ n->Bx = 99;
+ n->Cx = 3;
+ n->Goto(col, row);
+ }
+ _sprite = n;
+ break;
+ */
+ case 4:
+ // LISSAJOUS
+ error("Bad type [%s]", fname);
+ /*
+ LISSAJOUS * l = new LISSAJOUS(NULL);
+ if (l)
+ {
+ l->Ax = SCR_WID/2;
+ l->Ay = SCR_HIG/2;
+ l->Bx = 7;
+ l->By = 13;
+ l->Cx = 300;
+ l->Cy = 500;
+ *(long *) &l->Dx = 0; // movex * cnt
+ l->Goto(col, row);
+ }
+ _sprite = l;
+ */
+ break;
+ case 5:
+ { // FLY
+ Fly *f = new Fly(this, NULL);
+ _sprite = f;
+ //////Sprite->Time = 1;//-----------$$$$$$$$$$$$$$
+ break;
+ }
+ default:
+ // DEAD
+ _sprite = new Sprite(this, NULL);
+ if (_sprite)
+ _sprite->gotoxy(col, row);
+ break;
+ }
+
+ if (_sprite) {
+ _sprite->_ref = ref;
+ _sprite->_cave = cav;
+ _sprite->_z = pos;
+ _sprite->_flags._east = east;
+ _sprite->_flags._port = port;
+ _sprite->_flags._tran = tran;
+ _sprite->_flags._kill = true;
+ _sprite->_flags._bDel = true;
+
+ // Extract the filename, without the extension
+ strcpy(_sprite->_file, fname);
+ char *p = strchr(_sprite->_file, '.');
+ if (p)
+ *p = '\0';
+
+ _sprite->_shpCnt = shpcnt;
+ _vga->_spareQ->append(_sprite);
+ }
+}
+
+void CGEEngine::loadScript(const char *fname) {
+ VFile scrf(fname);
+
+ if (scrf._error)
+ return;
+
+ bool ok = true;
+ int lcnt = 0;
+
+ char line[kLineMax];
+ while (scrf.read((uint8 *)line) != 0) {
+ char *p;
+
+ lcnt++;
+ if (*line == 0 || *line == '\n' || *line == '.')
+ continue;
+
+ ok = false; // not OK if break
+
+ // sprite ident number
+ if ((p = strtok(line, " \t\n")) == NULL)
+ break;
+ int SpI = atoi(p);
+
+ // sprite file name
+ char *SpN;
+ if ((SpN = strtok(NULL, " ,;/\t\n")) == NULL)
+ break;
+
+ // sprite cave
+ if ((p = strtok(NULL, " ,;/\t\n")) == NULL)
+ break;
+ int SpA = atoi(p);
+
+ // sprite column
+ if ((p = strtok(NULL, " ,;/\t\n")) == NULL)
+ break;
+ int SpX = atoi(p);
+
+ // sprite row
+ if ((p = strtok(NULL, " ,;/\t\n")) == NULL)
+ break;
+ int SpY = atoi(p);
+
+ // sprite Z pos
+ if ((p = strtok(NULL, " ,;/\t\n")) == NULL)
+ break;
+ int SpZ = atoi(p);
+
+ // sprite life
+ if ((p = strtok(NULL, " ,;/\t\n")) == NULL)
+ break;
+ bool BkG = atoi(p) == 0;
+
+ ok = true; // no break: OK
+
+ _sprite = NULL;
+ loadSprite(SpN, SpI, SpA, SpX, SpY, SpZ);
+ if (_sprite && BkG)
+ _sprite->_flags._back = true;
+ }
+
+ if (!ok)
+ error("Bad INI line %d [%s]", lcnt, fname);
+}
+
+void CGEEngine::mainLoop() {
+ _vga->show();
+ _snail_->runCom();
+ _snail->runCom();
+
+ // Handle a delay between game frames
+ handleFrame();
+
+ // Handle any pending events
+ _eventManager->poll();
+}
+
+void CGEEngine::handleFrame() {
+ // Game frame delay
+ uint32 millis = g_system->getMillis();
+ while (!_eventManager->_quitFlag && (millis < (_lastFrame + kGameFrameDelay))) {
+ // Handle any pending events
+ _eventManager->poll();
+
+ if (millis >= (_lastTick + kGameTickDelay)) {
+ // Dispatch the tick to any active objects
+ tick();
+ _lastTick = millis;
+ }
+
+ // Slight delay
+ g_system->delayMillis(5);
+ millis = g_system->getMillis();
+ }
+ _lastFrame = millis;
+
+ if (millis >= (_lastTick + kGameTickDelay)) {
+ // Dispatch the tick to any active objects
+ tick();
+ _lastTick = millis;
+ }
+}
+
+void CGEEngine::tick() {
+ for (Sprite *spr = _vga->_showQ->first(); spr; spr = spr->_next) {
+ if (spr->_time) {
+ if (!spr->_flags._hide) {
+ if (--spr->_time == 0)
+ spr->tick();
+ }
+ }
+ }
+}
+
+void CGEEngine::loadUser() {
+ // set scene
+ if (_mode == 0) {
+ // user .SVG file found - load it from slot 0
+ loadGame(0, NULL);
+ } else if (_mode == 1) {
+ // Load either initial game state savegame or launcher specified savegame
+ loadGame(_startGameSlot, NULL);
+ } else {
+ error("Creating setup savegames not supported");
+ }
+ loadScript("CGE.IN0");
+}
+
+void CGEEngine::runGame() {
+ if (_eventManager->_quitFlag)
+ return;
+
+ loadHeroXY();
+
+ _cavLight->_flags._tran = true;
+ _vga->_showQ->append(_cavLight);
+ _cavLight->_flags._hide = true;
+
+ const Seq pocSeq[] = {
+ { 0, 0, 0, 0, 20 },
+ { 1, 2, 0, 0, 4 },
+ { 2, 3, 0, 0, 4 },
+ { 3, 4, 0, 0, 16 },
+ { 2, 5, 0, 0, 4 },
+ { 1, 6, 0, 0, 4 },
+ { 0, 1, 0, 0, 16 },
+ };
+ Seq *seq = (Seq *)malloc(7 * sizeof(Seq));
+ Common::copy(pocSeq, pocSeq + 7, seq);
+ _pocLight->setSeq(seq);
+
+ _pocLight->_flags._tran = true;
+ _pocLight->_time = 1;
+ _pocLight->_z = 120;
+ _vga->_showQ->append(_pocLight);
+ selectPocket(-1);
+
+ _vga->_showQ->append(_mouse);
+
+// ___________
+ loadUser();
+// ~~~~~~~~~~~
+
+ if ((_sprite = _vga->_spareQ->locate(121)) != NULL)
+ _snail_->addCom(kSnSeq, -1, _vga->_mono, _sprite);
+ if ((_sprite = _vga->_spareQ->locate(122)) != NULL)
+ _sprite->step(_music);
+ _snail_->addCom(kSnSeq, -1, _music, _sprite);
+ if (!_music)
+ _midiPlayer.killMidi();
+
+ if (_cat->exist("MINI.SPR")) {
+ _miniShp = new BitmapPtr[2];
+ _miniShp[0] = _miniShp[1] = NULL;
+
+ loadSprite("MINI", -1, 0, kMiniX, kMiniY);
+ expandSprite(_miniCave = _sprite); // NULL is ok
+ if (_miniCave) {
+ _miniCave->_flags._kill = false;
+ _miniCave->_flags._hide = true;
+ _miniShp[0] = new Bitmap(*_miniCave->shp());
+ _miniShpList = _miniCave->setShapeList(_miniShp);
+ postMiniStep(-1);
+ }
+ }
+
+ if (_hero) {
+ expandSprite(_hero);
+ _hero->gotoxy(_heroXY[_now - 1].x, _heroXY[_now - 1].y);
+ if (_cat->exist("00SHADOW.SPR")) {
+ loadSprite("00SHADOW", -1, 0, _hero->_x + 14, _hero->_y + 51);
+ delete _shadow;
+ if ((_shadow = _sprite) != NULL) {
+ _shadow->_ref = 2;
+ _shadow->_flags._tran = true;
+ _shadow->_flags._kill = false;
+ _hero->_flags._shad = true;
+ _vga->_showQ->insert(_vga->_spareQ->remove(_shadow), _hero);
+ }
+ }
+ }
+
+ _infoLine->gotoxy(kInfoX, kInfoY);
+ _infoLine->_flags._tran = true;
+ _infoLine->update(NULL);
+ _vga->_showQ->insert(_infoLine);
+
+ _debugLine->_z = 126;
+ _vga->_showQ->insert(_debugLine);
+
+ if (_horzLine) {
+ _horzLine->_y = kMapTop - (kMapTop > 0);
+ _horzLine->_z = 126;
+ _vga->_showQ->insert(_horzLine);
+ }
+
+ _mouse->_busy = _vga->_spareQ->locate(kBusyRef);
+ if (_mouse->_busy)
+ expandSprite(_mouse->_busy);
+
+ _startupMode = 0;
+
+ _snail->addCom(kSnLevel, -1, _oldLev, &_cavLight);
+ _cavLight->gotoxy(kCaveX + ((_now - 1) % kCaveNx) * kCaveDx + kCaveSX,
+ kCaveY + ((_now - 1) / kCaveNx) * kCaveDy + kCaveSY);
+ caveUp();
+
+ _keyboard->setClient(_sys);
+ // main loop
+ while (!_finis && !_eventManager->_quitFlag) {
+ if (_flag[3])
+ _snail->addCom2(kSnExec, -1, 0, kQGame);
+ mainLoop();
+ }
+
+ // If finishing game due to closing ScummVM window, explicitly save the game
+ if (!_finis && canSaveGameStateCurrently())
+ qGame();
+
+ _keyboard->setClient(NULL);
+ _snail->addCom(kSnClear, -1, 0, NULL);
+ _snail_->addCom(kSnClear, -1, 0, NULL);
+ _mouse->off();
+ _vga->_showQ->clear();
+ _vga->_spareQ->clear();
+ _hero = NULL;
+ _shadow = NULL;
+}
+
+void CGEEngine::movie(const char *ext) {
+ assert(ext);
+
+ if (_eventManager->_quitFlag)
+ return;
+
+ char fn[12];
+ sprintf(fn, "CGE.%s", (*ext == '.') ? ext +1 : ext);
+
+ if (_cat->exist(fn)) {
+ loadScript(fn);
+ expandSprite(_vga->_spareQ->locate(999));
+ feedSnail(_vga->_showQ->locate(999), kTake);
+ _vga->_showQ->append(_mouse);
+ _keyboard->setClient(_sys);
+ while (!_snail->idle() && !_eventManager->_quitFlag)
+ mainLoop();
+
+ _keyboard->setClient(NULL);
+ _snail->addCom(kSnClear, -1, 0, NULL);
+ _snail_->addCom(kSnClear, -1, 0, NULL);
+ _vga->_showQ->clear();
+ _vga->_spareQ->clear();
+ }
+}
+
+bool CGEEngine::showTitle(const char *name) {
+ if (_eventManager->_quitFlag)
+ return false;
+
+ Bitmap::_pal = _vga->_sysPal;
+ BitmapPtr *LB = new BitmapPtr[2];
+ LB[0] = new Bitmap(name);
+ LB[1] = NULL;
+ Bitmap::_pal = NULL;
+
+ Sprite D(this, LB);
+ D._flags._kill = true;
+ D._flags._bDel = true;
+ D.center();
+ D.show(2);
+
+ if (_mode == 2) {
+ inf(kSavegame0Name);
+ _talk->show(2);
+ }
+
+ _vga->sunset();
+ _vga->copyPage(1, 2);
+ _vga->copyPage(0, 1);
+ selectPocket(-1);
+ _vga->sunrise(_vga->_sysPal);
+
+ if (_mode < 2 && !_soundOk) {
+ _vga->copyPage(1, 2);
+ _vga->copyPage(0, 1);
+ _vga->_showQ->append(_mouse);
+ _mouse->on();
+ for (; !_snail->idle() || Vmenu::_addr;) {
+ mainLoop();
+ if (_eventManager->_quitFlag)
+ return false;
+ }
+
+ _mouse->off();
+ _vga->_showQ->clear();
+ _vga->copyPage(0, 2);
+ _soundOk = 2;
+ if (_music)
+ _midiPlayer.loadMidi(0);
+ }
+
+ if (_mode < 2) {
+ // At this point the game originally set the protection variables
+ // used by the copy protection check
+ movie(kPaylistExt); // paylist
+ _vga->copyPage(1, 2);
+ _vga->copyPage(0, 1);
+ _vga->_showQ->append(_mouse);
+ // In the original game, the user had to enter his name
+ // As it was only used to name savegames, it has been removed
+ _vga->_showQ->clear();
+ _vga->copyPage(0, 2);
+
+ if (_mode == 0) {
+// The auto-load of savegame #0 is currently disabled
+#if 0
+ if (savegameExists(0)) {
+ // Load the savegame
+ loadGame(0, NULL, true); // only system vars
+ _vga->setColors(_vga->_sysPal, 64);
+ _vga->update();
+ if (_flag[3]) { //flag FINIS
+ _mode++;
+ _flag[3] = false;
+ }
+ } else
+#endif
+ _mode++;
+ }
+ }
+
+ if (_mode < 2)
+ movie(kWinkExt);
+
+ _vga->copyPage(0, 2);
+
+ return true;
+}
+
+void CGEEngine::cge_main() {
+ memset(_barriers, 0xFF, sizeof(_barriers));
+
+ if (!_mouse->_exist)
+ error("%s", _text->getText(kTextNoMouse));
+
+ if (!_cat->exist(kSavegame0Name))
+ _mode = 2;
+
+ _debugLine->_flags._hide = true;
+ if (_horzLine)
+ _horzLine->_flags._hide = true;
+
+ if (_music && _soundOk)
+ _midiPlayer.loadMidi(0);
+
+ if (_startGameSlot != -1) {
+ // Starting up a savegame from the launcher
+ _mode++;
+ runGame();
+
+ _startupMode = 2;
+ if (_flag[3]) // Flag FINIS
+ movie(kEndgExt);
+ } else {
+ if (_mode < 2)
+ movie(kLgoExt);
+
+ if (showTitle("WELCOME")) {
+ if (_mode == 1)
+ movie(kIntroExt);
+ runGame();
+ _startupMode = 2;
+ if (_flag[3]) // Flag FINIS
+ movie(kEndgExt);
+ } else
+ _vga->sunset();
+ }
+}
+
+} // End of namespace CGE
diff --git a/engines/cge/cge_main.h b/engines/cge/cge_main.h
new file mode 100644
index 0000000000..d6f1a996d1
--- /dev/null
+++ b/engines/cge/cge_main.h
@@ -0,0 +1,140 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#ifndef CGE_CGEMAIN_H
+#define CGE_CGEMAIN_H
+
+#include "cge/vga13h.h"
+#include "cge/events.h"
+#include "cge/sound.h"
+
+namespace CGE {
+
+#define kCaveX 4
+#define kCaveY 166
+#define kCaveSX 0
+#define kCaveSY 0
+#define kInfoX 177
+#define kInfoY 164
+#define kInfoW 140
+#define kButtonX 151
+#define kButtonY 164
+#define kMiniX 86
+#define kMiniY 162
+#define kLineMax 512
+#define kDistMax 3
+#define kLgoExt ".LGO"
+#define kSvgExt ".SVG"
+#define kPaylistExt ".X00"
+#define kWinkExt ".X01"
+#define kIntroExt ".X02"
+#define kEndgExt ".X03"
+#define kWalkSide 10
+#define kBusyRef 500
+#define kSystemRate 6 // 12 Hz
+#define kHeroFun0 (40 * 12)
+#define kHeroFun1 ( 2 * 12)
+#define kGetNamePrompt 50
+#define kGetNameTitle 51
+#define kTSeq 96
+//Useless?
+//#define kBadSnd 97
+//#define kBadMidi 98
+#define kNoMusic 98
+#define kBadSVG 99
+#define kSeqHTalk (kTSeq + 4)
+#define kSeqTooFar (kTSeq + 5)
+#define kSeqNoWay (kTSeq + 5)
+#define kSeqPocketFull (kTSeq + 5)
+#define kSeqOffUse (kTSeq + 6)
+#define kQuitTitle 200
+#define kQuit 201
+#define kNoQuit 202
+#define kDemo 300
+#define kOffUseCount 600
+#define kOffUse 601
+#define kNoWay 671
+#define kTooFar 681
+#define kPocketFull 691
+#define kPanHeight 40
+#define kScrWidth 320
+#define kScrHeight 200
+#define kWorldHeight (kScrHeight - kPanHeight)
+#define kStackSize 2048
+#define kSavegameCheckSum (1956 + _now + _oldLev + _game + _music + _demoText)
+#define kSavegame0Name ("{{INIT}}" kSvgExt)
+#define kSavegame0File VFile
+#define kSavegameStrSize 11
+#define kGameFrameDelay (1000 / 50)
+#define kGameTickDelay (1000 / 62)
+
+
+
+class System : public Sprite {
+public:
+ int _funDel;
+
+ System(CGEEngine *vm);
+
+ void setPal();
+ void funTouch();
+ virtual void touch(uint16 mask, int x, int y);
+ void tick();
+private:
+ CGEEngine *_vm;
+};
+
+class Square : public Sprite {
+public:
+ Square(CGEEngine *vm);
+ virtual void touch(uint16 mask, int x, int y);
+private:
+ CGEEngine *_vm;
+};
+
+extern Vga *_vga;
+extern System *_sys;
+extern Sprite *_pocLight;
+extern Keyboard *_keyboard;
+extern Mouse *_mouse;
+extern EventManager *_eventManager;
+extern Sprite *_pocket[];
+extern Sprite *_sprite;
+extern Sprite *_miniCave;
+extern Sprite *_shadow;
+extern HorizLine *_horzLine;
+extern InfoLine *_infoLine;
+extern Sprite *_cavLight;
+extern InfoLine *_debugLine;
+extern Snail *_snail;
+extern Snail *_snail_;
+extern Fx *_fx;
+extern Sound *_sound;
+
+} // End of namespace CGE
+
+#endif
diff --git a/engines/cge/console.cpp b/engines/cge/console.cpp
new file mode 100644
index 0000000000..71eedf34ea
--- /dev/null
+++ b/engines/cge/console.cpp
@@ -0,0 +1,34 @@
+/* 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.
+ *
+ */
+
+#include "cge/console.h"
+#include "cge/cge.h"
+
+namespace CGE {
+
+CGEConsole::CGEConsole(CGEEngine *vm) : GUI::Debugger(), _vm(vm) {
+}
+
+CGEConsole::~CGEConsole() {
+}
+
+} // End of namespace CGE
diff --git a/engines/cge/console.h b/engines/cge/console.h
new file mode 100644
index 0000000000..25a1a4fae3
--- /dev/null
+++ b/engines/cge/console.h
@@ -0,0 +1,43 @@
+/* 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.
+ *
+ */
+
+#ifndef CGE_CONSOLE_H
+#define CGE_CONSOLE_H
+
+#include "gui/debugger.h"
+
+namespace CGE {
+
+class CGEEngine;
+
+class CGEConsole : public GUI::Debugger {
+public:
+ CGEConsole(CGEEngine *vm);
+ virtual ~CGEConsole();
+
+private:
+ CGEEngine *_vm;
+};
+
+} // End of namespace CGE
+
+#endif
diff --git a/engines/cge/detection.cpp b/engines/cge/detection.cpp
new file mode 100644
index 0000000000..abb0cf5efb
--- /dev/null
+++ b/engines/cge/detection.cpp
@@ -0,0 +1,237 @@
+/* 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.
+ *
+ */
+
+#include "common/config-manager.h"
+#include "engines/advancedDetector.h"
+#include "common/savefile.h"
+#include "common/system.h"
+#include "base/plugins.h"
+#include "graphics/thumbnail.h"
+#include "cge/cge.h"
+
+static const PlainGameDescriptor CGEGames[] = {
+ { "soltys", "Soltys" },
+ { 0, 0 }
+};
+
+namespace CGE {
+
+using Common::GUIO_NONE;
+
+static const ADGameDescription gameDescriptions[] = {
+
+ {
+ "soltys", "",
+ {
+ {"vol.cat", 0, "0c33e2c304821a2444d297fc5e2d67c6", 50176},
+ {"vol.dat", 0, "f9ae2e7f8f7cac91378cdafca43faf1e", 8437572},
+ AD_LISTEND
+ },
+ Common::PL_POL, Common::kPlatformPC, ADGF_NO_FLAGS, GUIO_NONE
+ },
+ {
+ "soltys", "Soltys Freeware",
+ {
+ {"vol.cat", 0, "0c33e2c304821a2444d297fc5e2d67c6", 50176},
+ {"vol.dat", 0, "f9ae2e7f8f7cac91378cdafca43faf1e", 8437676},
+ AD_LISTEND
+ },
+ Common::PL_POL, Common::kPlatformPC, ADGF_NO_FLAGS, GUIO_NONE
+ },
+ // English ScummVM version
+ {
+ "soltys", "",
+ {
+ {"vol.cat", 0, "bd08969b5f1acea0f92d195f750c17d5", 50176},
+ {"vol.dat", 0, "f9ae2e7f8f7cac91378cdafca43faf1e", 8428832},
+ AD_LISTEND
+ },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_NO_FLAGS, GUIO_NONE
+ },
+ {
+ "soltys", "Soltys Demo (not supported)",
+ {
+ {"vol.cat", 0, "1e077c8ff58109a187f07ac54b0c873a", 18788},
+ {"vol.dat", 0, "75d385a6074c58b69f7730481f256051", 1796710},
+ AD_LISTEND
+ },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO , GUIO_NONE
+ },
+ {
+ "soltys", "Soltys Demo (not supported)",
+ {
+ {"vol.cat", 0, "f17987487fab1ebddd781d8d02fedecc", 7168},
+ {"vol.dat", 0, "c5d9b15863cab61dc125551576dece04", 1075272},
+ AD_LISTEND
+ },
+ Common::PL_POL, Common::kPlatformPC, ADGF_DEMO , GUIO_NONE
+ },
+ AD_TABLE_END_MARKER
+};
+
+static const ADFileBasedFallback fileBasedFallback[] = {
+ { &gameDescriptions[0], { "vol.cat", "vol.dat", 0 } },
+ { 0, { 0 } }
+};
+
+} // End of namespace CGE
+
+class CGEMetaEngine : public AdvancedMetaEngine {
+public:
+ CGEMetaEngine() : AdvancedMetaEngine(CGE::gameDescriptions, sizeof(ADGameDescription), CGEGames) {
+ _singleid = "Soltys";
+ }
+
+ virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
+ return detectGameFilebased(allFiles, CGE::fileBasedFallback);
+ }
+
+ virtual const char *getName() const {
+ return "CGE";
+ }
+
+ virtual const char *getOriginalCopyright() const {
+ return "Soltys (c) 1994-1996 L.K. Avalon";
+ }
+
+
+
+ virtual bool hasFeature(MetaEngineFeature f) const;
+ virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
+ virtual int getMaximumSaveSlot() const;
+ virtual SaveStateList listSaves(const char *target) const;
+ SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
+ virtual void removeSaveState(const char *target, int slot) const;
+};
+
+bool CGEMetaEngine::hasFeature(MetaEngineFeature f) const {
+ return
+ (f == kSupportsListSaves) ||
+ (f == kSupportsLoadingDuringStartup) ||
+ (f == kSupportsDeleteSave) ||
+ (f == kSavesSupportMetaInfo) ||
+ (f == kSavesSupportThumbnail) ||
+ (f == kSavesSupportCreationDate);
+}
+
+void CGEMetaEngine::removeSaveState(const char *target, int slot) const {
+ Common::String fileName = Common::String::format("%s.%03d", target, slot);
+ g_system->getSavefileManager()->removeSavefile(fileName);
+}
+
+int CGEMetaEngine::getMaximumSaveSlot() const {
+ return 99;
+}
+
+SaveStateList CGEMetaEngine::listSaves(const char *target) const {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Common::StringArray filenames;
+ Common::String pattern = target;
+ pattern += ".???";
+
+ filenames = saveFileMan->listSavefiles(pattern);
+ sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
+
+ SaveStateList saveList;
+ int slotNum = 0;
+ for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) {
+ // Obtain the last 3 digits of the filename, since they correspond to the save slot
+ slotNum = atoi(filename->c_str() + filename->size() - 3);
+
+ if (slotNum >= 0 && slotNum <= 99) {
+
+ Common::InSaveFile *file = saveFileMan->openForLoading(*filename);
+ if (file) {
+ CGE::SavegameHeader header;
+
+ // Check to see if it's a ScummVM savegame or not
+ char buffer[kSavegameStrSize + 1];
+ file->read(buffer, kSavegameStrSize + 1);
+
+ if (!strncmp(buffer, CGE::savegameStr, kSavegameStrSize + 1)) {
+ // Valid savegame
+ if (CGE::CGEEngine::readSavegameHeader(file, header)) {
+ saveList.push_back(SaveStateDescriptor(slotNum, header.saveName));
+ delete header.thumbnail;
+ }
+ } else {
+ // Must be an original format savegame
+ saveList.push_back(SaveStateDescriptor(slotNum, "Unknown"));
+ }
+
+ delete file;
+ }
+ }
+ }
+
+ return saveList;
+}
+
+SaveStateDescriptor CGEMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+ Common::String fileName = Common::String::format("%s.%03d", target, slot);
+ Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(fileName);
+ assert(f);
+
+ CGE::SavegameHeader header;
+
+ // Check to see if it's a ScummVM savegame or not
+ char buffer[kSavegameStrSize + 1];
+ f->read(buffer, kSavegameStrSize + 1);
+
+ bool hasHeader = !strncmp(buffer, CGE::savegameStr, kSavegameStrSize + 1) &&
+ CGE::CGEEngine::readSavegameHeader(f, header);
+ delete f;
+
+ if (!hasHeader) {
+ // Original savegame perhaps?
+ SaveStateDescriptor desc(slot, "Unknown");
+ return desc;
+ } else {
+ // Create the return descriptor
+ SaveStateDescriptor desc(slot, header.saveName);
+ desc.setDeletableFlag(true);
+ desc.setWriteProtectedFlag(false);
+ desc.setThumbnail(header.thumbnail);
+ desc.setSaveDate(header.saveYear, header.saveMonth, header.saveDay);
+ desc.setSaveTime(header.saveHour, header.saveMinutes);
+
+ // Slot 0 is used for the 'automatic save on exit' save in Soltys, thus
+ // we prevent it from being deleted or overwritten by accident.
+ desc.setDeletableFlag(slot != 0);
+ desc.setWriteProtectedFlag(slot == 0);
+
+ return desc;
+ }
+}
+
+bool CGEMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ if (desc) {
+ *engine = new CGE::CGEEngine(syst, desc);
+ }
+ return desc != 0;
+}
+
+#if PLUGIN_ENABLED_DYNAMIC(CGE)
+REGISTER_PLUGIN_DYNAMIC(CGE, PLUGIN_TYPE_ENGINE, CGEMetaEngine);
+#else
+REGISTER_PLUGIN_STATIC(CGE, PLUGIN_TYPE_ENGINE, CGEMetaEngine);
+#endif
diff --git a/engines/cge/events.cpp b/engines/cge/events.cpp
new file mode 100644
index 0000000000..072771ebac
--- /dev/null
+++ b/engines/cge/events.cpp
@@ -0,0 +1,378 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#include "gui/saveload.h"
+#include "gui/about.h"
+#include "gui/message.h"
+#include "common/config-manager.h"
+#include "common/events.h"
+#include "cge/events.h"
+#include "cge/events.h"
+#include "cge/text.h"
+#include "cge/cge_main.h"
+
+namespace CGE {
+
+/*----------------- KEYBOARD interface -----------------*/
+
+const uint16 Keyboard::_code[0x60] = {
+ 0, Esc, '1', '2', '3',
+ '4', '5', '6', '7', '8',
+ '9', '0', '-', '+', BSp,
+ Tab, 'Q', 'W', 'E', 'R',
+ 'T', 'Y', 'U', 'I', 'O',
+ 'P', '[', ']', Enter, 0/*Ctrl*/,
+ 'A', 'S', 'D', 'F', 'G',
+ 'H', 'J', 'K', 'L', ';',
+ '\'', '`', 0/*LShift*/, '\\', 'Z',
+ 'X', 'C', 'V', 'B', 'N',
+ 'M', ',', '.', '/', 0/*RShift*/,
+ '*', 0/*Alt*/, ' ', 0/*Caps*/, F1,
+ F2, F3, F4, F5, F6,
+ F7, F8, F9, F10, 0/*NumLock*/,
+ 0/*ScrollLock*/, Home, Up, PgUp, '-',
+ Left, Ctr, Right, '+', End,
+ Down, PgDn, Ins, Del, 0 * 0x54,
+ 0 * 0x55, 0 * 0x56, F11, F12, 0 * 0x59,
+ 0 * 0x5A, 0 * 0x5B, 0 * 0x5C, 0 * 0x5D, 0 * 0x5E,
+ 0 * 0x5F
+};
+
+const uint16 Keyboard::_scummVmCodes[0x60] = {
+ 0, Common::KEYCODE_ESCAPE, Common::KEYCODE_1, Common::KEYCODE_2, Common::KEYCODE_3,
+ Common::KEYCODE_4, Common::KEYCODE_5, Common::KEYCODE_6, Common::KEYCODE_7, Common::KEYCODE_8,
+ Common::KEYCODE_9, Common::KEYCODE_0, Common::KEYCODE_MINUS, Common::KEYCODE_PLUS, Common::KEYCODE_BACKSPACE,
+ Common::KEYCODE_TAB, Common::KEYCODE_q, Common::KEYCODE_w, Common::KEYCODE_e, Common::KEYCODE_r,
+ Common::KEYCODE_t, Common::KEYCODE_y, Common::KEYCODE_u, Common::KEYCODE_i, Common::KEYCODE_o,
+ Common::KEYCODE_p, Common::KEYCODE_LEFTBRACKET, Common::KEYCODE_RIGHTBRACKET, Common::KEYCODE_RETURN, 0/*Ctrl*/,
+ Common::KEYCODE_a, Common::KEYCODE_s, Common::KEYCODE_d, Common::KEYCODE_f, Common::KEYCODE_g,
+ Common::KEYCODE_h, Common::KEYCODE_j, Common::KEYCODE_k, Common::KEYCODE_l, Common::KEYCODE_SEMICOLON,
+ Common::KEYCODE_BACKSLASH, Common::KEYCODE_TILDE, Common::KEYCODE_LSHIFT, Common::KEYCODE_BACKSLASH, Common::KEYCODE_z,
+ Common::KEYCODE_x, Common::KEYCODE_c, Common::KEYCODE_v, Common::KEYCODE_b, Common::KEYCODE_n,
+ Common::KEYCODE_m, Common::KEYCODE_COMMA, Common::KEYCODE_PERIOD, Common::KEYCODE_SLASH, Common::KEYCODE_RSHIFT,
+ Common::KEYCODE_KP_MULTIPLY, 0 /*Alt*/, Common::KEYCODE_SPACE, Common::KEYCODE_CAPSLOCK, Common::KEYCODE_F1,
+ Common::KEYCODE_F2, Common::KEYCODE_F3, Common::KEYCODE_F4, Common::KEYCODE_F5, Common::KEYCODE_F6,
+ Common::KEYCODE_F7, Common::KEYCODE_F8, Common::KEYCODE_F9, Common::KEYCODE_F10, Common::KEYCODE_NUMLOCK,
+ Common::KEYCODE_SCROLLOCK, Common::KEYCODE_KP7, Common::KEYCODE_KP8, Common::KEYCODE_KP9, Common::KEYCODE_KP_MINUS,
+ Common::KEYCODE_KP4, Common::KEYCODE_KP5, Common::KEYCODE_KP6, Common::KEYCODE_KP_PLUS, Common::KEYCODE_KP1,
+ Common::KEYCODE_KP2, Common::KEYCODE_KP3, Common::KEYCODE_KP0, Common::KEYCODE_KP_PERIOD, 0,
+ 0, 0, Common::KEYCODE_F11, Common::KEYCODE_F12, 0,
+ 0, 0, 0, 0, 0,
+ 0
+};
+
+Keyboard::Keyboard(CGEEngine *vm) : _client(NULL), _vm(vm) {
+ Common::set_to(&_key[0], &_key[0x60], false);
+ _current = 0;
+}
+
+Keyboard::~Keyboard() {
+}
+
+Sprite *Keyboard::setClient(Sprite *spr) {
+ SWAP(_client, spr);
+ return spr;
+}
+
+bool Keyboard::getKey(Common::Event &event, int &cgeCode) {
+ Common::KeyCode keycode = event.kbd.keycode;
+ if ((keycode == Common::KEYCODE_LCTRL) || (keycode == Common::KEYCODE_RCTRL)) {
+ cgeCode = kKeyCtrl;
+ return true;
+ }
+ if ((keycode == Common::KEYCODE_LALT) || (keycode == Common::KEYCODE_RALT)) {
+ cgeCode = kKeyAlt;
+ return true;
+ }
+ if (keycode == Common::KEYCODE_KP_ENTER) {
+ cgeCode = 28;
+ return true;
+ }
+ if (keycode == Common::KEYCODE_F5) {
+ warning("keycode %d", event.kbd.ascii);
+ if (_vm->canSaveGameStateCurrently()) {
+ const EnginePlugin *plugin = NULL;
+ EngineMan.findGame(_vm->_gameDescription->gameid, &plugin);
+
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Save game:", "Save");
+ dialog->setSaveMode(true);
+ int16 savegameId = dialog->runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName());
+ Common::String savegameDescription = dialog->getResultString();
+ delete dialog;
+ _vm->saveGameState(savegameId, savegameDescription);
+ }
+ return false;
+ } else if (keycode == Common::KEYCODE_F7) {
+ if (_vm->canLoadGameStateCurrently()) {
+ const EnginePlugin *plugin = NULL;
+ EngineMan.findGame(_vm->_gameDescription->gameid, &plugin);
+
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Restore game:", "Restore");
+ dialog->setSaveMode(false);
+ int16 savegameId = dialog->runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName());
+ delete dialog;
+ _vm->loadGameState(savegameId);
+ }
+ return false;
+ }
+
+ // Scan through the ScummVM mapping list
+ for (int idx = 0; idx < 0x60; idx++) {
+ if (_scummVmCodes[idx] == event.kbd.ascii) {
+ cgeCode = idx;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Keyboard::newKeyboard(Common::Event &event) {
+ int keycode;
+ if (!getKey(event, keycode))
+ return;
+
+ if (event.type == Common::EVENT_KEYUP) {
+ // Key release
+ _key[keycode] = false;
+ } else if (event.type == Common::EVENT_KEYDOWN) {
+ // Key press
+ _key[keycode] = true;
+ _current = Keyboard::_code[keycode];
+
+ if (_client) {
+ CGEEvent &evt = _eventManager->getNextEvent();
+ evt._x = _current; // Keycode
+ evt._mask = kEventKeyb; // Event mask
+ evt._spritePtr = _client; // Sprite pointer
+ }
+ }
+}
+
+uint16 Keyboard::lastKey() {
+ uint16 cur = _current;
+ _current = 0;
+ return cur;
+}
+
+/*----------------- MOUSE interface -----------------*/
+
+Mouse::Mouse(CGEEngine *vm) : Sprite(vm, NULL), _busy(NULL), _hold(NULL), _hx(0), _vm(vm) {
+ _hold = NULL;
+ _hx = 0;
+ _hy = 0;
+ _exist = true;
+ _buttons = 0;
+ _busy = NULL;
+ _active = false;
+ _flags._kill = false;
+
+ const Seq ms[] = {
+ { 0, 0, 0, 0, 1 },
+ { 1, 1, 0, 0, 1 }
+ };
+ Seq *seq = (Seq *)malloc(2 * sizeof(Seq));
+ Common::copy(ms, ms + 2, seq);
+ setSeq(seq);
+
+ BitmapPtr *MC = new BitmapPtr[3];
+ MC[0] = new Bitmap("MOUSE");
+ MC[1] = new Bitmap("DUMMY");
+ MC[2] = NULL;
+ setShapeList(MC);
+
+ gotoxy(kScrWidth / 2, kScrHeight / 2);
+ _z = 127;
+ step(1);
+}
+
+Mouse::~Mouse() {
+ off();
+}
+
+void Mouse::on() {
+ if (_seqPtr && _exist) {
+ _active = true;
+ step(0);
+ if (_busy)
+ _busy->step(0);
+ }
+}
+
+void Mouse::off() {
+ if (_seqPtr == 0) {
+ if (_exist) {
+ _active = false;
+ }
+
+ step(1);
+ if (_busy)
+ _busy->step(1);
+ }
+}
+
+void Mouse::newMouse(Common::Event &event) {
+ if (!_active)
+ return;
+
+ CGEEvent &evt = _eventManager->getNextEvent();
+ evt._x = event.mouse.x;
+ evt._y = event.mouse.y;
+ evt._spritePtr = spriteAt(evt._x, evt._y);
+
+ switch (event.type) {
+ case Common::EVENT_MOUSEMOVE:
+ evt._mask = kMouseRoll;
+ break;
+ case Common::EVENT_LBUTTONDOWN:
+ evt._mask = kMouseLeftDown;
+ _buttons |= 1;
+ break;
+ case Common::EVENT_LBUTTONUP:
+ evt._mask = kMouseLeftUp;
+ _buttons &= ~1;
+ break;
+ case Common::EVENT_RBUTTONDOWN:
+ evt._mask = kMouseRightDown;
+ _buttons |= 2;
+ break;
+ case Common::EVENT_RBUTTONUP:
+ evt._mask = kMouseRightUp;
+ _buttons &= ~2;
+ break;
+ default:
+ break;
+ }
+}
+
+/*----------------- EventManager interface -----------------*/
+
+EventManager::EventManager() {
+ _quitFlag = false;
+ _eventQueueHead = 0;
+ _eventQueueTail = 0;
+ memset(&_eventQueue, 0, kEventMax * sizeof(CGEEvent));
+ memset(&_event, 0, sizeof(Common::Event));
+}
+
+void EventManager::poll() {
+ while (g_system->getEventManager()->pollEvent(_event)) {
+ switch (_event.type) {
+ case Common::EVENT_QUIT:
+ // Signal to quit
+ _quitFlag = true;
+ return;
+ case Common::EVENT_KEYDOWN:
+ case Common::EVENT_KEYUP:
+ // Handle keyboard events
+ _keyboard->newKeyboard(_event);
+ handleEvents();
+ break;
+ case Common::EVENT_MOUSEMOVE:
+ case Common::EVENT_LBUTTONDOWN:
+ case Common::EVENT_LBUTTONUP:
+ case Common::EVENT_RBUTTONDOWN:
+ case Common::EVENT_RBUTTONUP:
+ // Handle mouse events
+ _mouse->newMouse(_event);
+ handleEvents();
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void EventManager::handleEvents() {
+ while (_eventQueueTail != _eventQueueHead) {
+ CGEEvent e = _eventQueue[_eventQueueTail];
+ if (e._mask) {
+ if (_mouse->_hold && e._spritePtr != _mouse->_hold)
+ _mouse->_hold->touch(e._mask | kEventAttn, e._x - _mouse->_hold->_x, e._y - _mouse->_hold->_y);
+
+ // update mouse cursor position
+ if (e._mask & kMouseRoll)
+ _mouse->gotoxy(e._x, e._y);
+
+ // activate current touched SPRITE
+ if (e._spritePtr) {
+ if (e._mask & kEventKeyb)
+ e._spritePtr->touch(e._mask, e._x, e._y);
+ else
+ e._spritePtr->touch(e._mask, e._x - e._spritePtr->_x, e._y - e._spritePtr->_y);
+ } else if (_sys)
+ _sys->touch(e._mask, e._x, e._y);
+
+ if (e._mask & kMouseLeftDown) {
+ _mouse->_hold = e._spritePtr;
+ if (_mouse->_hold) {
+ _mouse->_hold->_flags._hold = true;
+
+ if (_mouse->_hold->_flags._drag) {
+ _mouse->_hx = e._x - _mouse->_hold->_x;
+ _mouse->_hy = e._y - _mouse->_hold->_y;
+ }
+ }
+ }
+
+ if (e._mask & kMouseLeftUp) {
+ if (_mouse->_hold) {
+ _mouse->_hold->_flags._hold = false;
+ _mouse->_hold = NULL;
+ }
+ }
+ ///Touched = e.Ptr;
+
+ // discard Text if button released
+ if (e._mask & (kMouseLeftUp | kMouseRightUp))
+ killText();
+ }
+ _eventQueueTail = (_eventQueueTail + 1) % kEventMax;
+ }
+ if (_mouse->_hold) {
+ if (_mouse->_hold->_flags._drag)
+ _mouse->_hold->gotoxy(_mouse->_x - _mouse->_hx, _mouse->_y - _mouse->_hy);
+ }
+}
+
+void EventManager::clearEvent(Sprite *spr) {
+ if (spr) {
+ for (uint16 e = _eventQueueTail; e != _eventQueueHead; e = (e + 1) % kEventMax)
+ if (_eventQueue[e]._spritePtr == spr)
+ _eventQueue[e]._mask = 0;
+ } else
+ _eventQueueTail = _eventQueueHead;
+}
+
+CGEEvent &EventManager::getNextEvent() {
+ CGEEvent &evt = _eventQueue[_eventQueueHead];
+ _eventQueueHead = (_eventQueueHead + 1) % kEventMax;
+
+ return evt;
+}
+
+} // End of namespace CGE
diff --git a/engines/cge/events.h b/engines/cge/events.h
new file mode 100644
index 0000000000..f170455fa7
--- /dev/null
+++ b/engines/cge/events.h
@@ -0,0 +1,155 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#ifndef CGE_EVENTS_H
+#define CGE_EVENTS_H
+
+#include "common/events.h"
+#include "cge/game.h"
+#include "cge/talk.h"
+#include "cge/vga13h.h"
+
+namespace CGE {
+
+/*----------------- KEYBOARD interface -----------------*/
+
+#define kKeyCtrl 29
+#define kKeyAlt 56
+#define kEventMax 256
+
+enum EventMask {
+ kMouseRoll = 1 << 0,
+ kMouseLeftDown = 1 << 1,
+ kMouseLeftUp = 1 << 2,
+ kMouseRightDown = 1 << 3,
+ kMouseRightUp = 1 << 4,
+ kEventAttn = 1 << 5,
+ kEventKeyb = 1 << 7
+};
+
+enum Keys {
+ NoKey = 0, CtrlA, CtrlB, CtrlC, CtrlD, CtrlE, CtrlF, CtrlG, CtrlH,
+ CtrlI, CtrlJ, CtrlK, CtrlL, CtrlM, CtrlN, CtrlO, CtrlP,
+ CtrlQ, CtrlR, CtrlS, CtrlT, CtrlU, CtrlV, CtrlW, CtrlX,
+ CtrlY, CtrlZ,
+ BSp = 8, Tab,
+ Enter = 13,
+ Eof = 26, Esc,
+ AltQ = 256 + 16, AltW, AltE, AltR, AltT, AltY, AltU, AltI, AltO, AltP,
+ AltA = 256 + 30, AltS, AltD, AltF, AltG, AltH, AltJ, AltK, AltL,
+ AltZ = 256 + 44, AltX, AltC, AltV, AltB, AltN, AltM,
+ F11 = 256 + 87, F12,
+ F1 = 256 + 59, F2, F3, F4, F5, F6, F7, F8, F9, F10,
+ ShiftTab = 256 + 15,
+ ShiftF1 = 256 + 84, ShiftF2, ShiftF3, ShiftF4, ShiftF5,
+ ShiftF6, ShiftF7, ShiftF8, ShiftF9, ShiftF10,
+ CtrlF1 = 256 + 94, CtrlF2, CtrlF3, CtrlF4, CtrlF5,
+ CtrlF6, CtrlF7, CtrlF8, CtrlF9, CtrlF10,
+ AltF1 = 256 + 104, AltF2, AltF3, AltF4, AltF5,
+ AltF6, AltF7, AltF8, AltF9, AltF10,
+ Home = 256 + 71, Up, PgUp,
+ Left = 256 + 75, Ctr, Right,
+ End = 256 + 79, Down, PgDn, Ins, Del,
+ CtrlLeft = 256 + 115, CtrlRight, CtrlEnd, CtrlPgDn, CtrlHome,
+ CtrlPgUp = 256 + 132,
+ MouseLeft = 512 + 1, MouseRight,
+ TwiceLeft = 512 + 256 + 1, TwiceRight
+};
+
+class Keyboard {
+private:
+ bool getKey(Common::Event &event, int &cgeCode);
+ uint16 _current;
+ CGEEngine *_vm;
+public:
+ static const uint16 _code[0x60];
+ static const uint16 _scummVmCodes[0x60];
+
+ Sprite *_client;
+ bool _key[0x60];
+
+ void newKeyboard(Common::Event &event);
+ uint16 lastKey();
+ Sprite *setClient(Sprite *spr);
+
+ Keyboard(CGEEngine *vm);
+ ~Keyboard();
+};
+
+/*----------------- MOUSE interface -----------------*/
+
+extern Talk *_talk;
+
+struct CGEEvent {
+ uint16 _mask;
+ uint16 _x;
+ uint16 _y;
+ Sprite *_spritePtr;
+};
+
+class Mouse : public Sprite {
+public:
+ Sprite *_hold;
+ bool _active;
+ int _hx;
+ int _hy;
+ bool _exist;
+ int _buttons;
+ Sprite *_busy;
+ //Sprite *Touched;
+ Mouse(CGEEngine *vm);
+ ~Mouse();
+ void on();
+ void off();
+ void newMouse(Common::Event &event);
+private:
+ CGEEngine *_vm;
+};
+
+/*----------------- EventManager interface -----------------*/
+
+class EventManager {
+private:
+ Common::Event _event;
+ CGEEvent _eventQueue[kEventMax];
+ uint16 _eventQueueHead;
+ uint16 _eventQueueTail;
+
+ void handleEvents();
+public:
+ bool _quitFlag;
+
+ EventManager();
+ void poll();
+ void clearEvent(Sprite *spr);
+
+ CGEEvent &getNextEvent();
+};
+
+} // End of namespace CGE
+
+#endif
diff --git a/engines/cge/fileio.cpp b/engines/cge/fileio.cpp
new file mode 100644
index 0000000000..34c7c6510f
--- /dev/null
+++ b/engines/cge/fileio.cpp
@@ -0,0 +1,420 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#include "common/system.h"
+#include "common/str.h"
+#include "common/debug.h"
+#include "common/debug-channels.h"
+#include "common/memstream.h"
+#include "cge/cge.h"
+#include "cge/fileio.h"
+
+namespace CGE {
+
+/*-----------------------------------------------------------------------
+ * IOHand
+ *-----------------------------------------------------------------------*/
+IoHand::IoHand(Crypt *crypt) : _error(0), _crypt(crypt), _seed(kCryptSeed) {
+ _file = new Common::File();
+}
+
+IoHand::IoHand(const char *name, Crypt *crypt)
+ : _error(0), _crypt(crypt), _seed(kCryptSeed) {
+ _file = new Common::File();
+ _file->open(name);
+}
+
+IoHand::~IoHand() {
+ _file->close();
+ delete _file;
+}
+
+uint16 IoHand::read(void *buf, uint16 len) {
+ if (!_file->isOpen())
+ return 0;
+
+ uint16 bytesRead = _file->read(buf, len);
+ if (!bytesRead)
+ error("Read %s - %d bytes", _file->getName(), len);
+ if (_crypt)
+ _seed = _crypt(buf, len);
+ return bytesRead;
+}
+
+long IoHand::mark() {
+ return _file->pos();
+}
+
+long IoHand::seek(long pos) {
+ _file->seek(pos, SEEK_SET);
+ return _file->pos();
+}
+
+long IoHand::size() {
+ return _file->size();
+}
+
+/*-----------------------------------------------------------------------
+ * IoBuf
+ *-----------------------------------------------------------------------*/
+IoBuf::IoBuf(Crypt *crypt)
+ : IoHand(crypt),
+ _bufMark(0),
+ _ptr(0),
+ _lim(0) {
+ debugC(1, kCGEDebugFile, "IoBuf::IoBuf(crypt)");
+
+ _buff = (uint8 *)malloc(sizeof(uint8) * kBufferSize);
+ assert(_buff != NULL);
+}
+
+IoBuf::IoBuf(const char *name, Crypt *crypt)
+ : IoHand(name, crypt),
+ _bufMark(0),
+ _ptr(0),
+ _lim(0) {
+ debugC(1, kCGEDebugFile, "IoBuf::IoBuf(%s, crypt)", name);
+
+ _buff = (uint8 *)malloc(sizeof(uint8) * kBufferSize);
+ assert(_buff != NULL);
+}
+
+IoBuf::~IoBuf() {
+ debugC(6, kCGEDebugFile, "IoBuf::~IoBuf()");
+ free(_buff);
+}
+
+void IoBuf::readBuf() {
+ debugC(4, kCGEDebugFile, "IoBuf::readBuf()");
+
+ _bufMark = IoHand::mark();
+ _lim = IoHand::read(_buff, kBufferSize);
+ _ptr = 0;
+}
+
+uint16 IoBuf::read(void *buf, uint16 len) {
+ debugC(4, kCGEDebugFile, "IoBuf::read(buf, %d)", len);
+
+ uint16 total = 0;
+ while (len) {
+ if (_ptr >= _lim)
+ readBuf();
+ uint16 n = _lim - _ptr;
+ if (n) {
+ if (len < n)
+ n = len;
+ memcpy(buf, _buff + _ptr, n);
+ buf = (uint8 *)buf + n;
+ len -= n;
+ total += n;
+ _ptr += n;
+ } else
+ break;
+ }
+ return total;
+}
+
+uint16 IoBuf::read(uint8 *buf) {
+ debugC(3, kCGEDebugFile, "IoBuf::read(buf)");
+
+ uint16 total = 0;
+
+ while (total < kLineMaxSize - 2) {
+ if (_ptr >= _lim)
+ readBuf();
+ uint8 *p = _buff + _ptr;
+ uint16 n = _lim - _ptr;
+ if (n) {
+ if (total + n >= kLineMaxSize - 2)
+ n = kLineMaxSize - 2 - total;
+ uint8 *eol = (uint8 *) memchr(p, '\r', n);
+ if (eol)
+ n = (uint16)(eol - p);
+ uint8 *eof = (uint8 *) memchr(p, '\32', n);
+ if (eof) { // end-of-file
+ n = (uint16)(eof - p);
+ _ptr = (uint16)(eof - _buff);
+ }
+ if (n)
+ memcpy(buf, p, n);
+ buf += n;
+ total += n;
+ if (eof)
+ break;
+ _ptr += n;
+ if (eol) {
+ _ptr++;
+ *(buf++) = '\n';
+ total++;
+ if (_ptr >= _lim)
+ readBuf();
+ if (_ptr < _lim)
+ if (_buff[_ptr] == '\n')
+ ++_ptr;
+ break;
+ }
+ } else
+ break;
+ }
+ *buf = '\0';
+ return total;
+}
+
+int IoBuf::read() {
+ debugC(1, kCGEDebugFile, "IoBuf::read()");
+
+ if (_ptr >= _lim) {
+ readBuf();
+ if (_lim == 0)
+ return -1;
+ }
+ return _buff[_ptr++];
+}
+
+/*-----------------------------------------------------------------------
+ * CFile
+ *-----------------------------------------------------------------------*/
+CFile::CFile(const char *name, Crypt *crypt) : IoBuf(name, crypt) {
+ debugC(1, kCGEDebugFile, "CFile::CFile(%s, crypt)", name);
+}
+
+CFile::~CFile() {
+}
+
+long CFile::mark() {
+ debugC(5, kCGEDebugFile, "CFile::mark()");
+
+ return _bufMark + _ptr;
+}
+
+long CFile::seek(long pos) {
+ debugC(1, kCGEDebugFile, "CFile::seek(%ld)", pos);
+
+ if (pos >= _bufMark && pos < _bufMark + _lim) {
+ _ptr = (uint16)(pos - _bufMark);
+ return pos;
+ } else {
+ _lim = 0;
+ _ptr = 0;
+ return _bufMark = IoHand::seek(pos);
+ }
+}
+
+/*-----------------------------------------------------------------------
+ * BtPage
+ *-----------------------------------------------------------------------*/
+void BtPage::read(Common::ReadStream &s) {
+ _header._count = s.readUint16LE();
+ _header._down = s.readUint16LE();
+
+ if (_header._down == kBtValNone) {
+ // Leaf list
+ for (int i = 0; i < kBtLeafCount; ++i) {
+ s.read(_leaf[i]._key, kBtKeySize);
+ _leaf[i]._mark = s.readUint32LE();
+ _leaf[i]._size = s.readUint16LE();
+ }
+ } else {
+ // Root index
+ for (int i = 0; i < kBtInnerCount; ++i) {
+ s.read(_inner[i]._key, kBtKeySize);
+ _inner[i]._down = s.readUint16LE();
+ }
+ }
+}
+
+/*-----------------------------------------------------------------------
+ * BtFile
+ *-----------------------------------------------------------------------*/
+BtFile::BtFile(const char *name, Crypt *crpt)
+ : IoHand(name, crpt) {
+ debugC(1, kCGEDebugFile, "BtFile::BtFile(%s, crpt)", name);
+
+ for (int i = 0; i < kBtLevel; i++) {
+ _buff[i]._page = new BtPage;
+ _buff[i]._pgNo = kBtValNone;
+ _buff[i]._indx = -1;
+ assert(_buff[i]._page != NULL);
+ }
+}
+
+BtFile::~BtFile() {
+ debugC(1, kCGEDebugFile, "BtFile::~BtFile()");
+ for (int i = 0; i < kBtLevel; i++)
+ delete _buff[i]._page;
+}
+
+BtPage *BtFile::getPage(int lev, uint16 pgn) {
+ debugC(1, kCGEDebugFile, "BtFile::getPage(%d, %d)", lev, pgn);
+
+ if (_buff[lev]._pgNo != pgn) {
+ int32 pos = pgn * kBtSize;
+ _buff[lev]._pgNo = pgn;
+ assert(size() > pos);
+ // In the original, there was a check verifying if the
+ // purpose was to write a new file. This should only be
+ // to create a new file, thus it was removed.
+ seek((uint32) pgn * kBtSize);
+
+ // Read in the page
+ byte buffer[kBtSize];
+ int bytesRead = read(buffer, kBtSize);
+
+ // Unpack it into the page structure
+ Common::MemoryReadStream stream(buffer, bytesRead, DisposeAfterUse::NO);
+ _buff[lev]._page->read(stream);
+
+ _buff[lev]._indx = -1;
+ }
+ return _buff[lev]._page;
+}
+
+BtKeypack *BtFile::find(const char *key) {
+ debugC(1, kCGEDebugFile, "BtFile::find(%s)", key);
+
+ int lev = 0;
+ uint16 nxt = kBtValRoot;
+ while (!_error) {
+ BtPage *pg = getPage(lev, nxt);
+ // search
+ if (pg->_header._down != kBtValNone) {
+ int i;
+ for (i = 0; i < pg->_header._count; i++) {
+ // Does this work, or does it have to compare the entire buffer?
+ if (scumm_strnicmp((const char *)key, (const char*)pg->_inner[i]._key, kBtKeySize) < 0)
+ break;
+ }
+ nxt = (i) ? pg->_inner[i - 1]._down : pg->_header._down;
+ _buff[lev]._indx = i - 1;
+ lev++;
+ } else {
+ int i;
+ for (i = 0; i < pg->_header._count - 1; i++) {
+ if (scumm_stricmp((const char *)key, (const char *)pg->_leaf[i]._key) <= 0)
+ break;
+ }
+ _buff[lev]._indx = i;
+ return &pg->_leaf[i];
+ }
+ }
+ return NULL;
+}
+
+bool BtFile::exist(const char *name) {
+ debugC(1, kCGEDebugFile, "BtFile::exist(%s)", name);
+
+ return scumm_stricmp(find(name)->_key, name) == 0;
+}
+
+/*-----------------------------------------------------------------------
+ * VFile
+ *-----------------------------------------------------------------------*/
+VFile::VFile(const char *name) : IoBuf(NULL) {
+ debugC(3, kCGEDebugFile, "VFile::VFile(%s)", name);
+
+ if (_dat->_error || _cat->_error)
+ error("Bad volume data");
+ BtKeypack *kp = _cat->find(name);
+ if (scumm_stricmp(kp->_key, name) != 0)
+ _error = 1;
+ _endMark = (_bufMark = _begMark = kp->_mark) + kp->_size;
+}
+
+VFile::~VFile() {
+}
+
+void VFile::readBuf() {
+ debugC(3, kCGEDebugFile, "VFile::readBuf()");
+
+ _dat->seek(_bufMark + _lim);
+ _bufMark = _dat->mark();
+ long n = _endMark - _bufMark;
+ if (n > kBufferSize)
+ n = kBufferSize;
+ _lim = _dat->read(_buff, (uint16) n);
+ _ptr = 0;
+}
+
+long VFile::mark() {
+ debugC(5, kCGEDebugFile, "VFile::mark()");
+
+ return (_bufMark + _ptr) - _begMark;
+}
+
+long VFile::size() {
+ debugC(1, kCGEDebugFile, "VFile::size()");
+
+ return _endMark - _begMark;
+}
+
+long VFile::seek(long pos) {
+ debugC(1, kCGEDebugFile, "VFile::seek(%ld)", pos);
+
+ _lim = 0;
+ return (_bufMark = _begMark + pos);
+}
+
+/*-----------------------------------------------------------------------
+ * EncryptedStream
+ *-----------------------------------------------------------------------*/
+EncryptedStream::EncryptedStream(const char *name) {
+ debugC(3, kCGEDebugFile, "EncryptedStream::EncryptedStream(%s)", name);
+
+ _error = false;
+ if (_dat->_error || _cat->_error)
+ error("Bad volume data");
+ BtKeypack *kp = _cat->find(name);
+ if (scumm_stricmp(kp->_key, name) != 0)
+ _error = true;
+
+ _dat->_file->seek(kp->_mark);
+ byte *dataBuffer = (byte *)malloc(kp->_size);
+ _dat->_file->read(dataBuffer, kp->_size);
+ XCrypt(dataBuffer, kp->_size);
+ _readStream = new Common::MemoryReadStream(dataBuffer, kp->_size, DisposeAfterUse::YES);
+}
+
+uint32 EncryptedStream::read(void *dataPtr, uint32 dataSize) {
+ return _readStream->read(dataPtr, dataSize);
+}
+
+bool EncryptedStream::err() {
+ return (_error & _readStream->err());
+}
+
+bool EncryptedStream::eos() {
+ return _readStream->eos();
+}
+
+Common::String EncryptedStream::readLine() {
+ return _readStream->readLine();
+}
+
+EncryptedStream::~EncryptedStream() {
+}
+
+} // End of namespace CGE
diff --git a/engines/cge/fileio.h b/engines/cge/fileio.h
new file mode 100644
index 0000000000..1f0756a276
--- /dev/null
+++ b/engines/cge/fileio.h
@@ -0,0 +1,168 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#ifndef CGE_FILEIO_H
+#define CGE_FILEIO_H
+
+#include "cge/general.h"
+#include "common/stream.h"
+
+namespace CGE {
+
+#define kBtSize 1024
+#define kBtKeySize 13
+#define kBtLevel 2
+#define kBtInnerCount ((kBtSize - 4 /*sizeof(Hea) */) / (kBtKeySize + 2 /*sizeof(Inner) */))
+#define kBtLeafCount ((kBtSize - 4 /*sizeof(Hea) */) / (kBtKeySize + 4 + 2 /*sizeof(BtKeypack) */))
+#define kBtValNone 0xFFFF
+#define kBtValRoot 0
+#define kLineMaxSize 512
+#define kBufferSize 2048
+#define kCatName "VOL.CAT"
+#define kDatName "VOL.DAT"
+
+struct BtKeypack {
+ char _key[kBtKeySize];
+ uint32 _mark;
+ uint16 _size;
+};
+
+struct Inner {
+ uint8 _key[kBtKeySize];
+ uint16 _down;
+};
+
+struct Header {
+ uint16 _count;
+ uint16 _down;
+};
+
+class IoHand {
+protected:
+ uint16 _seed;
+ Crypt *_crypt;
+public:
+ Common::File *_file;
+ uint16 _error;
+
+ IoHand(const char *name, Crypt crypt);
+ IoHand(Crypt *crypt);
+ virtual ~IoHand();
+ uint16 read(void *buf, uint16 len);
+ long mark();
+ long size();
+ long seek(long pos);
+};
+
+class IoBuf : public IoHand {
+protected:
+ uint8 *_buff;
+ uint16 _ptr;
+ uint16 _lim;
+ long _bufMark;
+ virtual void readBuf();
+public:
+ IoBuf(Crypt *crpt);
+ IoBuf(const char *name, Crypt *crpt);
+ virtual ~IoBuf();
+ uint16 read(void *buf, uint16 len);
+ uint16 read(uint8 *buf);
+ int read();
+};
+
+
+class CFile : public IoBuf {
+public:
+ CFile(const char *name, Crypt *crpt);
+ virtual ~CFile();
+ long mark();
+ long seek(long pos);
+};
+
+struct BtPage {
+ Header _header;
+ union {
+ // dummy filler to make proper size of union
+ uint8 _data[kBtSize - 4]; /* 4 is the size of struct Header */
+ // inner version of data: key + word-sized page link
+ Inner _inner[kBtInnerCount];
+ // leaf version of data: key + all user data
+ BtKeypack _leaf[kBtLeafCount];
+ };
+
+ void read(Common::ReadStream &s);
+};
+
+class BtFile : public IoHand {
+ struct {
+ BtPage *_page;
+ uint16 _pgNo;
+ int _indx;
+ } _buff[kBtLevel];
+
+ BtPage *getPage(int lev, uint16 pgn);
+public:
+ BtFile(const char *name, Crypt *crpt);
+ virtual ~BtFile();
+ BtKeypack *find(const char *key);
+ bool exist(const char *name);
+};
+
+class VFile : public IoBuf {
+private:
+ long _begMark;
+ long _endMark;
+
+ void readBuf();
+public:
+ VFile(const char *name);
+ ~VFile();
+
+ long mark();
+ long size();
+ long seek(long pos);
+};
+
+class EncryptedStream {
+private:
+ Common::SeekableReadStream *_readStream;
+ bool _error;
+public:
+ EncryptedStream(const char *name);
+ ~EncryptedStream();
+ bool err();
+ bool eos();
+ uint32 read(void *dataPtr, uint32 dataSize);
+ Common::String readLine();
+};
+
+extern CFile *_dat;
+extern BtFile *_cat;
+
+} // End of namespace CGE
+
+#endif
diff --git a/engines/cge/game.cpp b/engines/cge/game.cpp
new file mode 100644
index 0000000000..0c4f5971bc
--- /dev/null
+++ b/engines/cge/game.cpp
@@ -0,0 +1,72 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#include "cge/game.h"
+#include "cge/events.h"
+
+namespace CGE {
+
+uint8 *glass(Dac *pal, uint8 r, uint8 g, uint8 b) {
+ uint8 *x = (uint8 *)malloc(256);
+ if (x) {
+ uint16 i;
+ for (i = 0; i < 256; i++) {
+ x[i] = closest(pal, mkDac(((uint16)(pal[i]._r) * r) / 255,
+ ((uint16)(pal[i]._g) * g) / 255,
+ ((uint16)(pal[i]._b) * b) / 255));
+ }
+ }
+ return x;
+}
+
+const int Fly::_l = 20,
+ Fly::_t = 40,
+ Fly::_r = 110,
+ Fly::_b = 100;
+
+Fly::Fly(CGEEngine *vm, Bitmap **shpl)
+ : Sprite(vm, shpl), _tx(0), _ty(0), _vm(vm) {
+ step(newRandom(2));
+ gotoxy(_l + newRandom(_r - _l - _w), _t + newRandom(_b - _t - _h));
+}
+
+void Fly::tick() {
+ step();
+ if (_flags._kept)
+ return;
+ if (newRandom(10) < 1) {
+ _tx = newRandom(3) - 1;
+ _ty = newRandom(3) - 1;
+ }
+ if (_x + _tx < _l || _x + _tx + _w > _r)
+ _tx = -_tx;
+ if (_y + _ty < _t || _y + _ty + _h > _b)
+ _ty = -_ty;
+ gotoxy(_x + _tx, _y + _ty);
+}
+
+} // End of namespace CGE
diff --git a/engines/cge/game.h b/engines/cge/game.h
new file mode 100644
index 0000000000..63d686239e
--- /dev/null
+++ b/engines/cge/game.h
@@ -0,0 +1,52 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#ifndef CGE_GAME_H
+#define CGE_GAME_H
+
+#include "cge/vga13h.h"
+
+namespace CGE {
+
+uint8 *glass(Dac *pal, uint8 r, uint8 g, uint8 b);
+
+class Fly : public Sprite {
+ static const int _l;
+ static const int _t;
+ static const int _r;
+ static const int _b;
+public:
+ int _tx, _ty;
+ Fly(CGEEngine *vm, Bitmap **shpl);
+ void tick();
+private:
+ CGEEngine *_vm;
+};
+
+} // End of namespace CGE
+
+#endif
diff --git a/engines/cge/general.cpp b/engines/cge/general.cpp
new file mode 100644
index 0000000000..7db61818ab
--- /dev/null
+++ b/engines/cge/general.cpp
@@ -0,0 +1,99 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#include "cge/cge.h"
+#include "cge/general.h"
+#include "cge/snddrv.h"
+
+namespace CGE {
+
+uint16 XCrypt(void *buf, uint16 siz) {
+ byte *b = static_cast<byte *>(buf);
+
+ for (uint16 i = 0; i < siz; i++)
+ *b++ ^= kCryptSeed;
+
+ return kCryptSeed;
+}
+
+char *mergeExt(char *buf, const char *name, const char *ext) {
+ strcpy(buf, name);
+ char *dot = strrchr(buf, '.');
+ if (!dot)
+ strcat(buf, ext);
+
+ return buf;
+}
+
+char *forceExt(char *buf, const char *name, const char *ext) {
+ strcpy(buf, name);
+ char *dot = strrchr(buf, '.');
+ if (dot)
+ *dot = '\0';
+ strcat(buf, ext);
+
+ return buf;
+}
+
+void sndSetVolume() {
+ // USeless for ScummVM
+}
+
+DataCk *loadWave(VFile *file) {
+ byte *data = (byte *)malloc(file->size());
+ file->read(data, file->size());
+
+ return new DataCk(data, file->size());
+}
+
+int takeEnum(const char **tab, const char *text) {
+ const char **e;
+ if (text) {
+ for (e = tab; *e; e++) {
+ if (scumm_stricmp(text, *e) == 0) {
+ return e - tab;
+ }
+ }
+ }
+ return -1;
+}
+
+int newRandom(int range) {
+ return ((CGEEngine *)g_engine)->_randomSource.getRandomNumber(range - 1);
+}
+
+DataCk::DataCk(byte *buf, int bufSize) {
+ _buf = buf;
+ _ckSize = bufSize;
+}
+
+DataCk::~DataCk() {
+ free(_buf);
+}
+
+} // End of namespace CGE
+
diff --git a/engines/cge/general.h b/engines/cge/general.h
new file mode 100644
index 0000000000..1793594d07
--- /dev/null
+++ b/engines/cge/general.h
@@ -0,0 +1,59 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#ifndef CGE_GENERAL_H
+#define CGE_GENERAL_H
+
+#include "common/system.h"
+#include "common/file.h"
+#include "common/random.h"
+#include "common/textconsole.h"
+#include "common/str.h"
+
+namespace CGE {
+
+#define kCryptSeed 0xA5
+#define kMaxFile 128
+
+struct Dac {
+ uint8 _r;
+ uint8 _g;
+ uint8 _b;
+};
+
+typedef uint16 Crypt(void *buf, uint16 siz);
+
+uint16 XCrypt(void *buf, uint16 siz);
+int takeEnum(const char **tab, const char *text);
+uint16 chkSum(void *m, uint16 n);
+char *mergeExt(char *buf, const char *name, const char *ext);
+char *forceExt(char *buf, const char *name, const char *ext);
+int newRandom(int range);
+
+} // End of namespace CGE
+
+#endif
diff --git a/engines/cge/module.mk b/engines/cge/module.mk
new file mode 100644
index 0000000000..62b319e190
--- /dev/null
+++ b/engines/cge/module.mk
@@ -0,0 +1,31 @@
+MODULE := engines/cge
+
+MODULE_OBJS := \
+ bitmap.o \
+ cge.o \
+ cge_main.o \
+ console.o \
+ detection.o \
+ events.o \
+ fileio.o \
+ game.o \
+ general.o \
+ snail.o \
+ sound.o \
+ talk.o \
+ text.o \
+ vga13h.o \
+ vmenu.o \
+ walk.o
+
+MODULE_DIRS += \
+ engines/cge
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_CGE), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
+
diff --git a/engines/cge/snail.cpp b/engines/cge/snail.cpp
new file mode 100644
index 0000000000..3605ad94bd
--- /dev/null
+++ b/engines/cge/snail.cpp
@@ -0,0 +1,1182 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#include "cge/general.h"
+#include "cge/sound.h"
+#include "cge/snail.h"
+#include "cge/vga13h.h"
+#include "cge/text.h"
+#include "cge/cge_main.h"
+#include "cge/events.h"
+#include "cge/walk.h"
+
+namespace CGE {
+
+void CGEEngine::snGame(Sprite *spr, int num) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snGame(spr, %d)", num);
+
+ switch (num) {
+ case 1: {
+ static Sprite *dup[3] = { NULL, NULL, NULL };
+ int buref = 0;
+ int Stage = 0;
+
+ for (dup[0] = _vga->_showQ->first(); dup[0]; dup[0] = dup[0]->_next) {
+ buref = dup[0]->_ref;
+ if (buref / 1000 == 16 && buref % 100 == 6) {
+ Stage = (buref / 100) % 10;
+ break;
+ }
+ }
+ if (dup[1] == NULL) {
+ dup[1] = _vga->_showQ->locate(16003); // pan
+ dup[2] = _vga->_showQ->locate(16004); // pani
+ }
+
+ if (_game) { // continue game
+ int i = newRandom(3), hand = (dup[0]->_shpCnt == 6);
+ Stage++;
+ if (hand && Stage > kDressed)
+ ++hand;
+ if (i >= 0 || (dup[i] == spr && newRandom(3) == 0)) {
+ _snail->addCom(kSnSeq, -1, 3, dup[0]); // yes
+ _snail->addCom(kSnSeq, -1, 3, dup[1]); // yes
+ _snail->addCom(kSnSeq, -1, 3, dup[2]); // yes
+ _snail->addCom(kSnTNext, -1, 0, dup[0]); // reset Take
+ _snail->addCom(kSnTNext, -1, 0, dup[1]); // reset Take
+ _snail->addCom(kSnTNext, -1, 0, dup[2]); // reset Take
+ _snail->addCom(kSnNNext, -1, 0, dup[0]); // reset Near
+ _snail->addCom(kSnPause, -1, 72, NULL); // little rest
+ _snail->addCom(kSnSay, 1, 16009, NULL); // hura
+ _snail->addCom(kSnSay, buref, 16010, NULL); // siadaj
+ _snail->addCom(kSnSay, 1, 16011, NULL); // postoj‘
+
+ if (hand) {
+ _snail->addCom(kSnSend, 16060 + hand, 16, NULL); // dawaj r‘k‘
+ _snail->addCom(kSnSeq, buref, 4, NULL); // zdejmowanie
+ _snail->addCom(kSnSeq, 16060 + hand, 1, NULL); // ruch
+ _snail->addCom(kSnSound, 16060 + hand, 16002, NULL); // szelest
+ _snail->addCom(kSnWait, 16060 + hand, 3, NULL); // podniesie
+ _snail->addCom(kSnSwap, buref, buref + 100, NULL); // rozdziana
+ _snail->addCom(kSnSeq, 16016, Stage, NULL); // rožnie kupa
+ _snail->addCom(kSnSend, 16060 + hand, -1, NULL); // chowaj r‘k‘
+ _snail->addCom(kSnWait, 16060 + hand, -1, NULL); // r‘ka zamar’a
+ } else {
+ _snail->addCom(kSnSeq, buref, 4, NULL); // zdejmowanie
+ _snail->addCom(kSnSound, 16060 + hand, 16002, NULL); // szelest
+ _snail->addCom(kSnWait, buref, -1, NULL); // zdejmie
+ _snail->addCom(kSnSwap, buref, buref + 100, NULL); // rozdziana
+ _snail->addCom(kSnSeq, 16016, Stage, NULL); // rožnie kupa
+ }
+ //SNPOST(SNSEQ, buref+100, 0, NULL); // reset
+ _snail->addCom(kSnPause, -1, 72, NULL); // chwilk‘...
+ _snail->addCom(kSnSeq, -1, 0, dup[1]); // odstaw Go
+ _snail->addCom(kSnSetXY, -1, 203 + kScrWidth * 49, dup[1]);
+ _snail->addCom(kSnSetZ, -1, 7, dup[1]);
+ _snail->addCom(kSnSeq, -1, 0, dup[2]); // odstaw J†
+ _snail->addCom(kSnSetXY, -1, 182 + kScrWidth * 62, dup[2]);
+ _snail->addCom(kSnSetZ, -1, 9, dup[2]);
+ _game = 0;
+ return;
+ } else {
+ _snail->addCom(kSnSeq, -1, 2, dup[0]); // no
+ _snail->addCom(kSnSeq, -1, 2, dup[1]); // no
+ _snail->addCom(kSnSeq, -1, 2, dup[2]); // no
+ _snail->addCom(kSnPause, -1, 72, NULL); // 1 sec
+ }
+ }
+ _snail->addCom(kSnWalk, 198, 134, NULL); // na miejsce
+ _snail->addCom(kSnWait, 1, -1, NULL); // stoi
+ _snail->addCom(kSnCover, 1, 16101, NULL); // ch’op do bicia
+ _snail->addCom(kSnSeq, 16101, 1, NULL); // wystaw
+ _snail->addCom(kSnWait, 16101, 5, NULL); // czekaj
+ _snail->addCom(kSnPause, 16101, 24, NULL); // czekaj chwil‘
+ _snail->addCom(kSnSeq, 16040, 1, NULL); // plask
+ _snail->addCom(kSnSound, 16101, 16001, NULL); // plask!
+ _snail->addCom(kSnPause, 16101, 24, NULL); // czekaj chwil‘
+ _snail->addCom(kSnSeq, 16040, 0, NULL); // schowaj plask
+ _snail->addCom(kSnWait, 16101, -1, NULL); // stoi
+ _snail->addCom(kSnUncover, 1, 16101, NULL); // SDS
+ if (!_game) {
+ _snail->addCom(kSnSay, buref, 16008, NULL); // zgadnij!
+ _game = true;
+ }
+ }
+ break;
+ case 2:
+ if (_sprTv == NULL) {
+ _sprTv = _vga->_showQ->locate(20700);
+ _sprK1 = _vga->_showQ->locate(20701);
+ _sprK2 = _vga->_showQ->locate(20702);
+ _sprK3 = _vga->_showQ->locate(20703);
+ }
+
+ if (!_game) { // init
+ _snail->addCom(kSnGame, 20002, 2, NULL);
+ _game = true;
+ break;
+ }
+
+ // cont
+ _sprK1->step(newRandom(6));
+ _sprK2->step(newRandom(6));
+ _sprK3->step(newRandom(6));
+
+ if (spr->_ref == 1 && _keyboard->_key[kKeyAlt]) {
+ _sprK1->step(5);
+ _sprK2->step(5);
+ _sprK3->step(5);
+ }
+
+ _snail->addCom(kSnSetZ, 20700, 0, NULL);
+ bool hit = (_sprK1->_seqPtr + _sprK2->_seqPtr + _sprK3->_seqPtr == 15);
+ if (hit) {
+ if (spr->_ref == 1) {
+ _snail->addCom(kSnSay, 1, 20003, NULL); // hura!
+ _snail->addCom(kSnSeq, 20011, 2, NULL); // kamera won
+ _snail->addCom(kSnSend, 20701, -1, NULL); // k1 won
+ _snail->addCom(kSnSend, 20702, -1, NULL); // k2 won
+ _snail->addCom(kSnSend, 20703, -1, NULL); // k3 won
+ _snail->addCom(kSnSend, 20700, -1, NULL); // tv won
+ _snail->addCom(kSnKeep, 20007, 0, NULL); // do kieszeni
+ _snail->addCom(kSnSend, 20006, 20, NULL); // bilon
+ _snail->addCom(kSnSound, 20006, 20002, NULL); // bilon!
+ _snail->addCom(kSnSay, 20002, 20004, NULL);
+ _snail->addCom(kSnSend, 20010, 20, NULL); // papier
+ _snail->addCom(kSnSound, 20010, 20003, NULL); // papier!
+ _snail->addCom(kSnSay, 20001, 20005, NULL);
+ _game = false;
+ return;
+ } else
+ _sprK3->step(newRandom(5));
+ }
+
+ if (_gameCase2Cpt < 100) {
+ switch (_gameCase2Cpt) {
+ case 15:
+ _snail->addCom(kSnSay, 20003, 20021, NULL);
+ break;
+ case 30:
+ case 45:
+ case 60:
+ case 75:
+ _snail->addCom(kSnSay, 20003, 20022, NULL);
+ break;
+ }
+ _gameCase2Cpt++;
+ }
+
+ switch (spr->_ref) {
+ case 1:
+ _snail->addCom(kSnSay, 20001, 20011, NULL); // zapro
+ _snail->addCom(kSnSeq, 20001, 1, NULL); // rzu
+ _snail->addCom(kSnWait, 20001, 1, NULL); // czekaj
+ _snail->addCom(kSnSetZ, 20700, 2, NULL); // skryj k
+ _snail->addCom(kSnHide, 20007, 1, NULL); // skryj k
+ _snail->addCom(kSnWait, 20001, 16, NULL); // czekaj
+ _snail->addCom(kSnSeq, 20007, 1, NULL); // lec†
+ _snail->addCom(kSnHide, 20007, 0, NULL); // poka§
+ _snail->addCom(kSnSound, 20007, 20001, NULL); // grzech
+ _snail->addCom(kSnWait, 20007, -1, NULL); // koniec
+ _snail->addCom(kSnGame, 20001, 2, NULL); // again!
+ break;
+
+ case 20001:
+ _snail->addCom(kSnSay, 20002, 20012, NULL); // zapro
+ _snail->addCom(kSnSeq, 20002, 1, NULL); // rzu
+ _snail->addCom(kSnWait, 20002, 3, NULL); // czekaj
+ _snail->addCom(kSnSetZ, 20700, 2, NULL); // skryj k
+ _snail->addCom(kSnHide, 20007, 1, NULL); // skryj k
+ _snail->addCom(kSnWait, 20002, 10, NULL); // czekaj
+ _snail->addCom(kSnSeq, 20007, 2, NULL); // lec†
+ _snail->addCom(kSnHide, 20007, 0, NULL); // poka§
+ _snail->addCom(kSnSound, 20007, 20001, NULL); // grzech
+ _snail->addCom(kSnWait, 20007, -1, NULL); // koniec
+ _snail->addCom(kSnGame, 20002, 2, NULL); // again!
+ break;
+
+ case 20002:
+ _snail->addCom(kSnSay, 20002, 20010, NULL); // zapro
+ _snail->addCom(kSnWalk, 20005, -1, NULL); // do stol
+ _snail->addCom(kSnWait, 1, -1, NULL); // stoi
+ _snail->addCom(kSnCover, 1, 20101, NULL); // grasol
+ _snail->addCom(kSnSeq, 20101, 1, NULL); // rzu
+ _snail->addCom(kSnWait, 20101, 5, NULL); // czekaj
+ _snail->addCom(kSnSetZ, 20700, 2, NULL); // skryj k
+ _snail->addCom(kSnHide, 20007, 1, NULL); // skryj k
+ _snail->addCom(kSnWait, 20101, 15, NULL); // czekaj
+ _snail->addCom(kSnSeq, 20007, 1, NULL); // lec†
+ _snail->addCom(kSnHide, 20007, 0, NULL); // poka§
+ _snail->addCom(kSnSound, 20007, 20001, NULL); // grzech
+ _snail->addCom(kSnWait, 20101, -1, NULL); // koniec
+ _snail->addCom(kSnUncover, 1, 20101, NULL); // SDS
+ _snail->addCom(kSnGame, 1, 2, NULL); // again!
+ break;
+ }
+ }
+}
+
+void CGEEngine::expandSprite(Sprite *spr) {
+ debugC(5, kCGEDebugEngine, "CGEEngine::expandSprite(spr)");
+
+ if (spr)
+ _vga->_showQ->insert(_vga->_spareQ->remove(spr));
+}
+
+void CGEEngine::contractSprite(Sprite *spr) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::contractSprite(spr)");
+
+ if (spr)
+ _vga->_spareQ->append(_vga->_showQ->remove(spr));
+}
+
+int CGEEngine::findPocket(Sprite *spr) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::findPocket(spr)");
+
+ for (int i = 0; i < kPocketNX; i++)
+ if (_pocket[i] == spr)
+ return i;
+ return -1;
+}
+
+void CGEEngine::selectPocket(int n) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::selectPocket(%d)", n);
+
+ if (n < 0 || (_pocLight->_seqPtr && _pocPtr == n)) {
+ _pocLight->step(0);
+ n = findPocket(NULL);
+ if (n >= 0)
+ _pocPtr = n;
+ } else {
+ if (_pocket[n] != NULL) {
+ _pocPtr = n;
+ _pocLight->step(1);
+ }
+ }
+ _pocLight->gotoxy(kPocketX + _pocPtr * kPocketDX + kPocketSX, kPocketY + kPocketSY);
+}
+
+void CGEEngine::pocFul() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::pocFul()");
+
+ _hero->park();
+ _snail->addCom(kSnWait, -1, -1, _hero);
+ _snail->addCom(kSnSeq, -1, kSeqPocketFull, _hero);
+ _snail->addCom(kSnSound, -1, 2, _hero);
+ _snail->addCom(kSnWait, -1, -1, _hero);
+ _snail->addCom(kSnSay, 1, kPocketFull, _hero);
+}
+
+void CGEEngine::hide1(Sprite *spr) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::hide1(spr)");
+
+ _snail_->addCom(kSnGhost, -1, 0, spr->ghost());
+}
+
+void CGEEngine::snGhost(Bitmap *bmp) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snGhost(bmp)");
+
+ bmp->hide(bmp->_map & 0xFFFF, bmp->_map >> 16);
+ bmp->_m = NULL;
+ bmp->_map = 0;
+ delete bmp;
+}
+
+void CGEEngine::feedSnail(Sprite *spr, SnList snq) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::feedSnail(spr, snq)");
+
+ if (!spr || !spr->active())
+ return;
+
+ uint8 ptr = (snq == kTake) ? spr->_takePtr : spr->_nearPtr;
+
+ if (ptr == kNoPtr)
+ return;
+
+ Snail::Com *comtab = spr->snList(snq);
+ Snail::Com *c = comtab + ptr;
+
+ if (findPocket(NULL) < 0) { // no empty pockets?
+ Snail::Com *p;
+ for (p = c; p->_com != kSnNext; p++) { // find KEEP command
+ if (p->_com == kSnKeep) {
+ pocFul();
+ return;
+ }
+ if (p->_ptr)
+ break;
+ }
+ }
+ while (true) {
+ if (c->_com == kSnTalk) {
+ if ((_snail->_talkEnable = (c->_val != 0)) == false)
+ killText();
+ }
+ if (c->_com == kSnNext) {
+ Sprite *s = (c->_ref < 0) ? spr : locate(c->_ref);
+ if (s) {
+ uint8 *idx = (snq == kTake) ? &s->_takePtr : &s->_nearPtr;
+ if (*idx != kNoPtr) {
+ int v;
+ switch (c->_val) {
+ case -1 :
+ v = c - comtab + 1;
+ break;
+ case -2 :
+ v = c - comtab;
+ break;
+ case -3 :
+ v = -1;
+ break;
+ default :
+ v = c->_val;
+ break;
+ }
+ if (v >= 0)
+ *idx = v;
+ }
+ }
+ if (s == spr)
+ break;
+ }
+ if (c->_com == kSnIf) {
+ Sprite *s = (c->_ref < 0) ? spr : locate(c->_ref);
+ if (s) { // sprite extsts
+ if (! s->seqTest(-1))
+ c = comtab + c->_val; // not parked
+ else
+ ++c;
+ } else
+ ++c;
+ } else {
+ _snail->addCom(c->_com, c->_ref, c->_val, spr);
+ if (c->_ptr)
+ break;
+ else
+ c++;
+ }
+ }
+}
+
+const char *Snail::_comText[] = {
+ "LABEL", "PAUSE", "WAIT", "LEVEL", "HIDE",
+ "SAY", "INF", "TIME", "CAVE", "KILL",
+ "RSEQ", "SEQ", "SEND", "SWAP", "KEEP",
+ "GIVE", "IF", "GAME", "SETX0", "SETY0",
+ "SLAVE", "SETXY", "RELX", "RELY", "RELZ",
+ "SETX", "SETY", "SETZ", "TRANS", "PORT",
+ "NEXT", "NNEXT", "TNEXT", "RNNEXT", "RTNEXT",
+ "RMNEAR", "RMTAKE", "FLAG", "SETREF", "BACKPT",
+ "FLASH", "LIGHT", "SETHB", "SETVB", "WALK",
+ "REACH", "COVER", "UNCOVER", "CLEAR", "TALK",
+ "MOUSE", "SOUND", "COUNT", NULL
+};
+
+Snail::Snail(CGEEngine *vm, bool turbo)
+ : _turbo(turbo), _busy(false), _textDelay(false),
+ _timerExpiry(0), _talkEnable(true),
+ _head(0), _tail(0), _snList((Com *)malloc(sizeof(Com) * 256)), _vm(vm) {
+}
+
+Snail::~Snail() {
+ free(_snList);
+}
+
+void Snail::addCom(SnCom com, int ref, int val, void *ptr) {
+ Com *snc = &_snList[_head++];
+ snc->_com = com;
+ snc->_ref = ref;
+ snc->_val = val;
+ snc->_ptr = ptr;
+ snc->_cbType = kNullCB;
+ if (com == kSnClear) {
+ _tail = _head;
+ killText();
+ _timerExpiry = 0;
+ }
+}
+
+void Snail::addCom2(SnCom com, int ref, int val, CallbackType cbType) {
+ Com *snc = &_snList[_head++];
+ snc->_com = com;
+ snc->_ref = ref;
+ snc->_val = val;
+ snc->_ptr = NULL;
+ snc->_cbType = cbType;
+ if (com == kSnClear) {
+ _tail = _head;
+ killText();
+ _timerExpiry = 0;
+ }
+}
+
+void Snail::insCom(SnCom com, int ref, int val, void *ptr) {
+ Com *snc;
+
+ if (_busy) {
+ _snList[(_tail - 1) & 0xFF] = _snList[_tail];
+ snc = &_snList[_tail];
+ } else
+ snc = &_snList[(_tail - 1) & 0xFF];
+ _tail--;
+ snc->_com = com;
+ snc->_ref = ref;
+ snc->_val = val;
+ snc->_ptr = ptr;
+ if (com == kSnClear) {
+ _tail = _head;
+ killText();
+ _timerExpiry = 0;
+ }
+}
+
+void CGEEngine::snNNext(Sprite *spr, int p) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snNNext(spr, %d)", p);
+
+ if (spr)
+ if (spr->_nearPtr != kNoPtr)
+ spr->_nearPtr = p;
+}
+
+void CGEEngine::snTNext(Sprite *spr, int p) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snTNext(spr, %d)", p);
+
+ if (spr)
+ if (spr->_takePtr != kNoPtr)
+ spr->_takePtr = p;
+}
+
+void CGEEngine::snRNNext(Sprite *spr, int p) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snRNNext(spr, %d)", p);
+
+ if (spr)
+ if (spr->_nearPtr != kNoPtr)
+ spr->_nearPtr += p;
+}
+
+
+void CGEEngine::snRTNext(Sprite *spr, int p) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snRTNext(spr, %d)", p);
+
+ if (spr)
+ if (spr->_takePtr != kNoPtr)
+ spr->_takePtr += p;
+}
+
+void CGEEngine::snZTrim(Sprite *spr) {
+ debugC(4, kCGEDebugEngine, "CGEEngine::snZTrim(spr)");
+
+ if (!spr || !spr->active())
+ return;
+
+ Sprite *s = (spr->_flags._shad) ? spr->_prev : NULL;
+ _vga->_showQ->insert(_vga->_showQ->remove(spr));
+ if (s) {
+ s->_z = spr->_z;
+ _vga->_showQ->insert(_vga->_showQ->remove(s), spr);
+ }
+}
+
+void CGEEngine::snHide(Sprite *spr, int val) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snHide(spr, %d)", val);
+
+ if (spr) {
+ spr->_flags._hide = (val >= 0) ? (val != 0) : (!spr->_flags._hide);
+ if (spr->_flags._shad)
+ spr->_prev->_flags._hide = spr->_flags._hide;
+ }
+}
+
+void CGEEngine::snRmNear(Sprite *spr) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snRmNear(spr)");
+
+ if (spr)
+ spr->_nearPtr = kNoPtr;
+}
+
+void CGEEngine::snRmTake(Sprite *spr) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snRmTake(spr)");
+
+ if (spr)
+ spr->_takePtr = kNoPtr;
+}
+
+void CGEEngine::snSeq(Sprite *spr, int val) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snSeq(spr, %d)", val);
+
+ if (spr) {
+ if (spr == _hero && val == 0)
+ _hero->park();
+ else
+ spr->step(val);
+ }
+}
+
+void CGEEngine::snRSeq(Sprite *spr, int val) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snRSeq(spr, %d)", val);
+
+ if (spr)
+ snSeq(spr, spr->_seqPtr + val);
+}
+
+void CGEEngine::snSend(Sprite *spr, int val) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snSend(spr, %d)", val);
+
+ if (!spr)
+ return;
+
+ int was = spr->_cave;
+ bool was1 = (was == 0 || was == _now);
+ bool val1 = (val == 0 || val == _now);
+ spr->_cave = val;
+ if (val1 != was1) {
+ if (was1) {
+ if (spr->_flags._kept) {
+ int n = findPocket(spr);
+ if (n >= 0)
+ _pocket[n] = NULL;
+ }
+ hide1(spr);
+ contractSprite(spr);
+ spr->_flags._slav = false;
+ } else {
+ if (spr->_ref % 1000 == 0)
+ Bitmap::_pal = _vga->_sysPal;
+ if (spr->_flags._back)
+ spr->backShow(true);
+ else
+ expandSprite(spr);
+ Bitmap::_pal = NULL;
+ }
+ }
+}
+
+void CGEEngine::snSwap(Sprite *spr, int xref) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snSwap(spr, %d)", xref);
+
+ Sprite *xspr = locate(xref);
+ if (!spr || !xspr)
+ return;
+
+ int was = spr->_cave;
+ int xwas = xspr->_cave;
+ bool was1 = (was == 0 || was == _now);
+ bool xwas1 = (xwas == 0 || xwas == _now);
+
+ SWAP(spr->_cave, xspr->_cave);
+ SWAP(spr->_x, xspr->_x);
+ SWAP(spr->_y, xspr->_y);
+ SWAP(spr->_z, xspr->_z);
+ if (spr->_flags._kept) {
+ int n = findPocket(spr);
+ if (n >= 0)
+ _pocket[n] = xspr;
+ xspr->_flags._kept = true;
+ xspr->_flags._port = false;
+ }
+ if (xwas1 != was1) {
+ if (was1) {
+ hide1(spr);
+ contractSprite(spr);
+ } else
+ expandSprite(spr);
+ if (xwas1) {
+ hide1(xspr);
+ contractSprite(xspr);
+ } else
+ expandSprite(xspr);
+ }
+}
+
+void CGEEngine::snCover(Sprite *spr, int xref) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snCover(spr, %d)", xref);
+
+ Sprite *xspr = locate(xref);
+ if (!spr || !xspr)
+ return;
+
+ spr->_flags._hide = true;
+ xspr->_z = spr->_z;
+ xspr->_cave = spr->_cave;
+ xspr->gotoxy(spr->_x, spr->_y);
+ expandSprite(xspr);
+ if ((xspr->_flags._shad = spr->_flags._shad) == 1) {
+ _vga->_showQ->insert(_vga->_showQ->remove(spr->_prev), xspr);
+ spr->_flags._shad = false;
+ }
+ feedSnail(xspr, kNear);
+}
+
+void CGEEngine::snUncover(Sprite *spr, Sprite *xspr) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snUncover(spr, xspr)");
+
+ if (!spr || !xspr)
+ return;
+
+ spr->_flags._hide = false;
+ spr->_cave = xspr->_cave;
+ spr->gotoxy(xspr->_x, xspr->_y);
+ if ((spr->_flags._shad = xspr->_flags._shad) == 1) {
+ _vga->_showQ->insert(_vga->_showQ->remove(xspr->_prev), spr);
+ xspr->_flags._shad = false;
+ }
+ spr->_z = xspr->_z;
+ snSend(xspr, -1);
+ if (spr->_time == 0)
+ spr->_time++;
+}
+
+void CGEEngine::snSetX0(int cav, int x0) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snSetX0(%d, %d)", cav, x0);
+
+ _heroXY[cav - 1].x = x0;
+}
+
+void CGEEngine::snSetY0(int cav, int y0) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snSetY0(%d, %d)", cav, y0);
+
+ _heroXY[cav - 1].y = y0;
+}
+
+void CGEEngine::snSetXY(Sprite *spr, uint16 xy) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snSetXY(spr, %d)", xy);
+
+ if (spr)
+ spr->gotoxy(xy % kScrWidth, xy / kScrWidth);
+}
+
+void CGEEngine::snRelX(Sprite *spr, int x) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snRelX(spr, %d)", x);
+
+ if (spr && _hero)
+ spr->gotoxy(_hero->_x + x, spr->_y);
+}
+
+void CGEEngine::snRelY(Sprite *spr, int y) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snRelY(spr, %d)", y);
+
+ if (spr && _hero)
+ spr->gotoxy(spr->_x, _hero->_y + y);
+}
+
+void CGEEngine::snRelZ(Sprite *spr, int z) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snRelZ(spr, %d)", z);
+
+ if (spr && _hero) {
+ spr->_z = _hero->_z + z;
+ snZTrim(spr);
+ }
+}
+
+void CGEEngine::snSetX(Sprite *spr, int x) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snSetX(spr, %d)", x);
+
+ if (spr)
+ spr->gotoxy(x, spr->_y);
+}
+
+void CGEEngine::snSetY(Sprite *spr, int y) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snSetY(spr, %d)", y);
+
+ if (spr)
+ spr->gotoxy(spr->_x, y);
+}
+
+void CGEEngine::snSetZ(Sprite *spr, int z) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snSetZ(spr, %d)", z);
+
+ if (spr) {
+ spr->_z = z;
+ //SNPOST_(SNZTRIM, -1, 0, spr);
+ snZTrim(spr);
+ }
+}
+
+void CGEEngine::snSlave(Sprite *spr, int ref) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snSlave(spr, %d)", ref);
+
+ Sprite *slv = locate(ref);
+ if (spr && slv) {
+ if (spr->active()) {
+ snSend(slv, spr->_cave);
+ slv->_flags._slav = true;
+ slv->_z = spr->_z;
+ _vga->_showQ->insert(_vga->_showQ->remove(slv), spr->_next);
+ }
+ }
+}
+
+void CGEEngine::snTrans(Sprite *spr, int trans) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snTrans(spr, %d)", trans);
+
+ if (spr)
+ spr->_flags._tran = (trans < 0) ? !spr->_flags._tran : (trans != 0);
+}
+
+void CGEEngine::snPort(Sprite *spr, int port) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snPort(spr, %d)", port);
+
+ if (spr)
+ spr->_flags._port = (port < 0) ? !spr->_flags._port : (port != 0);
+}
+
+void CGEEngine::snKill(Sprite *spr) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snKill(spr)");
+
+ if (!spr)
+ return;
+
+ if (spr->_flags._kept) {
+ int n = findPocket(spr);
+ if (n >= 0)
+ _pocket[n] = NULL;
+ }
+ Sprite *nx = spr->_next;
+ hide1(spr);
+ _vga->_showQ->remove(spr);
+ _eventManager->clearEvent(spr);
+ if (spr->_flags._kill) {
+ delete spr;
+ } else {
+ spr->_cave = -1;
+ _vga->_spareQ->append(spr);
+ }
+ if (nx) {
+ if (nx->_flags._slav)
+ snKill(nx);
+ }
+}
+
+void CGEEngine::snSound(Sprite *spr, int wav) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snSound(spr, %d)", wav);
+
+ if (wav == -1)
+ _sound->stop();
+ else
+ _sound->play((*_fx)[wav], (spr) ? ((spr->_x + spr->_w / 2) / (kScrWidth / 16)) : 8);
+
+ _sound->setRepeat(1);
+}
+
+void CGEEngine::snKeep(Sprite *spr, int stp) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snKeep(spr, %d)", stp);
+
+ selectPocket(-1);
+ if (spr && ! spr->_flags._kept && _pocket[_pocPtr] == NULL) {
+ int16 oldRepeat = _sound->getRepeat();
+ _sound->setRepeat(1);
+ snSound(spr, 3);
+ _sound->setRepeat(oldRepeat);
+ _pocket[_pocPtr] = spr;
+ spr->_cave = 0;
+ spr->_flags._kept = true;
+ spr->gotoxy(kPocketX + kPocketDX * _pocPtr + kPocketDX / 2 - spr->_w / 2,
+ kPocketY + kPocketDY / 2 - spr->_h / 2);
+ if (stp >= 0)
+ spr->step(stp);
+ }
+ selectPocket(-1);
+}
+
+void CGEEngine::snGive(Sprite *spr, int stp) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snGive(spr, %d)", stp);
+
+ if (spr) {
+ int p = findPocket(spr);
+ if (p >= 0) {
+ _pocket[p] = NULL;
+ spr->_cave = _now;
+ spr->_flags._kept = false;
+ if (stp >= 0)
+ spr->step(stp);
+ }
+ }
+ selectPocket(-1);
+}
+
+void CGEEngine::snBackPt(Sprite *spr, int stp) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snBackPt(spr, %d)", stp);
+
+ if (spr) {
+ if (stp >= 0)
+ spr->step(stp);
+ spr->backShow(true);
+ }
+}
+
+void CGEEngine::snLevel(Sprite *spr, int lev) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snLevel(spr, %d)", lev);
+
+ assert((lev >= 0) && (lev < 5));
+
+ for (int i = 0; i < 5; i++) {
+ spr = _vga->_spareQ->locate(100 + i);
+ if (spr) {
+ if (i <= lev) {
+ spr->backShow(true);
+ spr->_cave = 0;
+ spr->_flags._hide = false;
+ } else {
+ spr->_flags._hide = true;
+ spr->_cave = -1;
+ }
+ } else {
+ warning("SPR not found! ref: %d", 100 + i);
+ }
+ }
+
+ _lev = lev;
+ _maxCave = _maxCaveArr[_lev];
+}
+
+void CGEEngine::snFlag(int indx, bool v) {
+ _flag[indx] = v;
+}
+
+void CGEEngine::snSetRef(Sprite *spr, int nr) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snSetRef(spr, %d)", nr);
+
+ if (spr)
+ spr->_ref = nr;
+}
+
+void CGEEngine::snFlash(bool on) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snFlash(%s)", on ? "true" : "false");
+
+ if (on) {
+ Dac *pal = (Dac *)malloc(sizeof(Dac) * kPalCount);
+ if (pal) {
+ memcpy(pal, _vga->_sysPal, kPalSize);
+ for (int i = 0; i < kPalCount; i++) {
+ register int c;
+ c = pal[i]._r << 1;
+ pal[i]._r = (c < 64) ? c : 63;
+ c = pal[i]._g << 1;
+ pal[i]._g = (c < 64) ? c : 63;
+ c = pal[i]._b << 1;
+ pal[i]._b = (c < 64) ? c : 63;
+ }
+ _vga->setColors(pal, 64);
+ }
+ } else
+ _vga->setColors(_vga->_sysPal, 64);
+ _dark = false;
+}
+
+void CGEEngine::snLight(bool in) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snLight(%s)", in ? "true" : "false");
+
+ if (in)
+ _vga->sunrise(_vga->_sysPal);
+ else
+ _vga->sunset();
+ _dark = !in;
+}
+
+void CGEEngine::snHBarrier(const int cave, const int barX) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snHBarrier(%d, %d)", cave, barX);
+
+ _barriers[(cave > 0) ? cave : _now]._horz = barX;
+}
+
+void CGEEngine::snVBarrier(const int cave, const int barY) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snVBarrier(%d, %d)", cave, barY);
+
+ _barriers[(cave > 0) ? cave : _now]._vert = barY;
+}
+
+void CGEEngine::snWalk(Sprite *spr, int x, int y) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snWalk(spr, %d, %d)", x, y);
+
+ if (_hero) {
+ if (spr && y < 0)
+ _hero->findWay(spr);
+ else
+ _hero->findWay(XZ(x, y));
+ }
+}
+
+void CGEEngine::snReach(Sprite *spr, int mode) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snReach(spr, %d)", mode);
+
+ if (_hero)
+ _hero->reach(spr, mode);
+}
+
+void CGEEngine::snMouse(bool on) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snMouse(%s)", on ? "true" : "false");
+
+ if (on)
+ _mouse->on();
+ else
+ _mouse->off();
+}
+
+void Snail::runCom() {
+ if (_busy)
+ return;
+
+ _busy = true;
+ uint8 tmpHead = _head;
+ while (_tail != tmpHead) {
+ Com *snc = &_snList[_tail];
+
+ if (!_turbo) { // only for the slower one
+ if (_timerExpiry) {
+ // Delay in progress
+ if (_timerExpiry > g_system->getMillis())
+ // Delay not yet ended
+ break;
+
+ // Delay is finished
+ _timerExpiry = 0;
+ } else {
+ if (_textDelay) {
+ killText();
+ _textDelay = false;
+ }
+ }
+ if (_talk && snc->_com != kSnPause)
+ break;
+ }
+
+ Sprite *spr = ((snc->_ref >= 0) ? locate(snc->_ref) : ((Sprite *) snc->_ptr));
+ switch (snc->_com) {
+ case kSnLabel:
+ break;
+ case kSnPause :
+ _timerExpiry = g_system->getMillis() + snc->_val * kSnailFrameDelay;
+ if (_talk)
+ _textDelay = true;
+ break;
+ case kSnWait:
+ if (spr) {
+ if (spr->seqTest(snc->_val) &&
+ (snc->_val >= 0 || spr != _hero || _hero->_tracePtr < 0)) {
+ _timerExpiry = g_system->getMillis() + spr->_time * kSnailFrameDelay;
+ } else
+ goto xit;
+ }
+ break;
+ case kSnLevel:
+ _vm->snLevel(spr, snc->_val);
+ break;
+ case kSnHide:
+ _vm->snHide(spr, snc->_val);
+ break;
+ case kSnSay:
+ if (spr && _talkEnable) {
+ if (spr == _hero && spr->seqTest(-1))
+ spr->step(kSeqHTalk);
+ _text->say(_text->getText(snc->_val), spr);
+ _sys->_funDel = kHeroFun0;
+ }
+ break;
+ case kSnInf:
+ if (_talkEnable) {
+ _vm->inf(_text->getText(snc->_val));
+ _sys->_funDel = kHeroFun0;
+ }
+ break;
+ case kSnTime:
+ if (spr && _talkEnable) {
+ if (spr == _hero && spr->seqTest(-1))
+ spr->step(kSeqHTalk);
+ _text->sayTime(spr);
+ }
+ break;
+ case kSnCave:
+ _vm->switchCave(snc->_val);
+ break;
+ case kSnKill:
+ _vm->snKill(spr);
+ break;
+ case kSnSeq:
+ _vm->snSeq(spr, snc->_val);
+ break;
+ case kSnRSeq:
+ _vm->snRSeq(spr, snc->_val);
+ break;
+ case kSnSend:
+ _vm->snSend(spr, snc->_val);
+ break;
+ case kSnSwap:
+ _vm->snSwap(spr, snc->_val);
+ break;
+ case kSnCover:
+ _vm->snCover(spr, snc->_val);
+ break;
+ case kSnUncover:
+ _vm->snUncover(spr, (snc->_val >= 0) ? locate(snc->_val) : ((Sprite *) snc->_ptr));
+ break;
+ case kSnKeep:
+ _vm->snKeep(spr, snc->_val);
+ break;
+ case kSnGive:
+ _vm->snGive(spr, snc->_val);
+ break;
+ case kSnGame:
+ _vm->snGame(spr, snc->_val);
+ break;
+ case kSnSetX0:
+ _vm->snSetX0(snc->_ref, snc->_val);
+ break;
+ case kSnSetY0:
+ _vm->snSetY0(snc->_ref, snc->_val);
+ break;
+ case kSnSetXY:
+ _vm->snSetXY(spr, snc->_val);
+ break;
+ case kSnRelX:
+ _vm->snRelX(spr, snc->_val);
+ break;
+ case kSnRelY:
+ _vm->snRelY(spr, snc->_val);
+ break;
+ case kSnRelZ:
+ _vm->snRelZ(spr, snc->_val);
+ break;
+ case kSnSetX:
+ _vm->snSetX(spr, snc->_val);
+ break;
+ case kSnSetY:
+ _vm->snSetY(spr, snc->_val);
+ break;
+ case kSnSetZ:
+ _vm->snSetZ(spr, snc->_val);
+ break;
+ case kSnSlave:
+ _vm->snSlave(spr, snc->_val);
+ break;
+ case kSnTrans:
+ _vm->snTrans(spr, snc->_val);
+ break;
+ case kSnPort:
+ _vm->snPort(spr, snc->_val);
+ break;
+ case kSnNext:
+ case kSnIf:
+ case kSnTalk:
+ break;
+ case kSnMouse:
+ _vm->snMouse(snc->_val != 0);
+ break;
+ case kSnNNext:
+ _vm->snNNext(spr, snc->_val);
+ break;
+ case kSnTNext:
+ _vm->snTNext(spr, snc->_val);
+ break;
+ case kSnRNNext:
+ _vm->snRNNext(spr, snc->_val);
+ break;
+ case kSnRTNext:
+ _vm->snRTNext(spr, snc->_val);
+ break;
+ case kSnRMNear:
+ _vm->snRmNear(spr);
+ break;
+ case kSnRmTake:
+ _vm->snRmTake(spr);
+ break;
+ case kSnFlag:
+ _vm->snFlag(snc->_ref & 3, snc->_val != 0);
+ break;
+ case kSnSetRef:
+ _vm->snSetRef(spr, snc->_val);
+ break;
+ case kSnBackPt:
+ _vm->snBackPt(spr, snc->_val);
+ break;
+ case kSnFlash:
+ _vm->snFlash(snc->_val != 0);
+ break;
+ case kSnLight:
+ _vm->snLight(snc->_val != 0);
+ break;
+ case kSnSetHBarrier:
+ _vm->snHBarrier(snc->_ref, snc->_val);
+ break;
+ case kSnSetVBarrier:
+ _vm->snVBarrier(snc->_ref, snc->_val);
+ break;
+ case kSnWalk:
+ _vm->snWalk(spr, snc->_ref, snc->_val);
+ break;
+ case kSnReach:
+ _vm->snReach(spr, snc->_val);
+ break;
+ case kSnSound:
+ _vm->snSound(spr, snc->_val);
+ break;
+ case kSnCount:
+ _sound->setRepeat(snc->_val);
+ break;
+ case kSnExec:
+ switch (snc->_cbType) {
+ case kQGame:
+ _vm->qGame();
+ break;
+ case kMiniStep:
+ _vm->miniStep(snc->_val);
+ break;
+ case kXCave:
+ _vm->xCave();
+ break;
+ case kSndSetVolume:
+ sndSetVolume();
+ break;
+ default:
+ error("Unknown Callback Type in SNEXEC");
+ }
+ break;
+ case kSnStep:
+ spr->step();
+ break;
+ case kSnZTrim:
+ _vm->snZTrim(spr);
+ break;
+ case kSnGhost:
+ _vm->snGhost((Bitmap *) snc->_ptr);
+ break;
+ default:
+ warning("Unhandled snc->_com in SNMouse(bool)");
+ break;
+ }
+ _tail++;
+ if (!_turbo)
+ break;
+ }
+xit:
+ _busy = false;
+}
+
+bool Snail::idle() {
+ return (_head == _tail);
+}
+
+} // End of namespace CGE
diff --git a/engines/cge/snail.h b/engines/cge/snail.h
new file mode 100644
index 0000000000..c6157a0096
--- /dev/null
+++ b/engines/cge/snail.h
@@ -0,0 +1,84 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#ifndef CGE_SNAIL_H
+#define CGE_SNAIL_H
+
+#include "cge/cge.h"
+
+namespace CGE {
+
+#define kSnailFrameRate 80
+#define kSnailFrameDelay (1000 / kSnailFrameRate)
+#define kDressed 3
+
+enum SnCom {
+ kSnLabel, kSnPause, kSnWait, kSnLevel, kSnHide,
+ kSnSay, kSnInf, kSnTime, kSnCave, kSnKill,
+ kSnRSeq, kSnSeq, kSnSend, kSnSwap, kSnKeep,
+ kSnGive, kSnIf, kSnGame, kSnSetX0, kSnSetY0,
+ kSnSlave, kSnSetXY, kSnRelX, kSnRelY, kSnRelZ,
+ kSnSetX, kSnSetY, kSnSetZ, kSnTrans, kSnPort,
+ kSnNext, kSnNNext, kSnTNext, kSnRNNext, kSnRTNext,
+ kSnRMNear, kSnRmTake, kSnFlag, kSnSetRef, kSnBackPt,
+ kSnFlash, kSnLight, kSnSetHBarrier, kSnSetVBarrier, kSnWalk,
+ kSnReach, kSnCover, kSnUncover, kSnClear, kSnTalk,
+ kSnMouse, kSnSound, kSnCount, kSnExec, kSnStep,
+ kSnZTrim, kSnGhost
+};
+
+class Snail {
+public:
+ struct Com {
+ SnCom _com;
+ int _ref;
+ int _val;
+ void *_ptr;
+ CallbackType _cbType;
+ } *_snList;
+ uint8 _head;
+ uint8 _tail;
+ bool _turbo;
+ bool _busy;
+ bool _textDelay;
+ uint32 _timerExpiry;
+ static const char *_comText[];
+ bool _talkEnable;
+ Snail(CGEEngine *vm, bool turbo);
+ ~Snail();
+ void runCom();
+ void addCom(SnCom com, int ref, int val, void *ptr);
+ void addCom2(SnCom com, int ref, int val, CallbackType cbType);
+ void insCom(SnCom com, int ref, int val, void *ptr);
+ bool idle();
+private:
+ CGEEngine *_vm;
+};
+
+} // End of namespace CGE
+
+#endif
diff --git a/engines/cge/snddrv.h b/engines/cge/snddrv.h
new file mode 100644
index 0000000000..93793de2fe
--- /dev/null
+++ b/engines/cge/snddrv.h
@@ -0,0 +1,63 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#ifndef CGE_SNDDRV_H
+#define CGE_SNDDRV_H
+
+namespace CGE {
+
+// ******************************************************
+// * Constants *
+// ******************************************************
+
+// sample info
+struct SmpInfo {
+ const uint8 *_saddr; // address
+ uint16 _slen; // length
+ uint16 _span; // left/right pan (0-15)
+ int _counter; // number of time the sample should be played
+};
+
+// ******************************************************
+// * Data *
+// ******************************************************
+
+// midi player flag (1 means we are playing)
+extern uint16 _midiPlayFlag;
+
+// midi song end flag (1 means we have crossed end mark)
+extern uint16 _midiEndFlag;
+
+// ******************************************************
+// * Driver Code *
+// ******************************************************
+// Set Volume
+void sndSetVolume();
+
+} // End of namespace CGE
+
+#endif
diff --git a/engines/cge/sound.cpp b/engines/cge/sound.cpp
new file mode 100644
index 0000000000..a7b2d34b0b
--- /dev/null
+++ b/engines/cge/sound.cpp
@@ -0,0 +1,273 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#include "cge/general.h"
+#include "cge/sound.h"
+#include "cge/text.h"
+#include "cge/cge_main.h"
+#include "common/config-manager.h"
+#include "common/memstream.h"
+#include "audio/decoders/raw.h"
+#include "audio/audiostream.h"
+
+namespace CGE {
+
+Sound::Sound(CGEEngine *vm) : _vm(vm) {
+ _audioStream = NULL;
+ _soundRepeatCount = 1;
+ open();
+}
+
+Sound::~Sound() {
+ close();
+}
+
+void Sound::close() {
+ _vm->_midiPlayer.killMidi();
+}
+
+void Sound::open() {
+ setRepeat(1);
+ play((*_fx)[30000], 8);
+}
+
+void Sound::setRepeat(int16 count) {
+ _soundRepeatCount = count;
+}
+
+int16 Sound::getRepeat() {
+ return _soundRepeatCount;
+}
+
+void Sound::play(DataCk *wav, int pan) {
+ if (wav) {
+ stop();
+ _smpinf._saddr = &*(wav->addr());
+ _smpinf._slen = (uint16)wav->size();
+ _smpinf._span = pan;
+ _smpinf._counter = getRepeat();
+ sndDigiStart(&_smpinf);
+ }
+}
+
+void Sound::sndDigiStart(SmpInfo *PSmpInfo) {
+ // Create an audio stream wrapper for sound
+ Common::MemoryReadStream *stream = new Common::MemoryReadStream(PSmpInfo->_saddr,
+ PSmpInfo->_slen, DisposeAfterUse::NO);
+ _audioStream = Audio::makeWAVStream(stream, DisposeAfterUse::YES);
+
+ // Start the new sound
+ _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle,
+ Audio::makeLoopingAudioStream(_audioStream, (uint)PSmpInfo->_counter));
+}
+
+void Sound::stop() {
+ sndDigiStop(&_smpinf);
+}
+
+void Sound::sndDigiStop(SmpInfo *PSmpInfo) {
+ if (_vm->_mixer->isSoundHandleActive(_soundHandle))
+ _vm->_mixer->stopHandle(_soundHandle);
+ _audioStream = NULL;
+}
+
+Fx::Fx(int size) : _current(NULL) {
+ _cache = new Handler[size];
+ for (_size = 0; _size < size; _size++) {
+ _cache[_size]._ref = 0;
+ _cache[_size]._wav = NULL;
+ }
+}
+
+Fx::~Fx() {
+ clear();
+ delete[] _cache;
+}
+
+void Fx::clear() {
+ for (Handler *p = _cache, *q = p + _size; p < q; p++) {
+ if (p->_ref) {
+ p->_ref = 0;
+ delete p->_wav;
+ p->_wav = NULL;
+ }
+ }
+ _current = NULL;
+}
+
+int Fx::find(int ref) {
+ int i = 0;
+ for (Handler *p = _cache, *q = p + _size; p < q; p++) {
+ if (p->_ref == ref)
+ break;
+ else
+ ++i;
+ }
+ return i;
+}
+
+void Fx::preload(int ref0) {
+ Handler *cacheLim = _cache + _size;
+ char filename[12];
+
+ for (int ref = ref0; ref < ref0 + 10; ref++) {
+ sprintf(filename, "FX%05d.WAV", ref);
+ VFile file = VFile(filename);
+ DataCk *wav = loadWave(&file);
+ if (wav) {
+ Handler *p = &_cache[find(0)];
+ if (p >= cacheLim)
+ break;
+ p->_wav = wav;
+ p->_ref = ref;
+ } else {
+ warning("Unable to load %s", filename);
+ }
+ }
+}
+
+DataCk *Fx::load(int idx, int ref) {
+ char filename[12];
+ sprintf(filename, "FX%05d.WAV", ref);
+
+ VFile file = VFile(filename);
+ DataCk *wav = loadWave(&file);
+ if (wav) {
+ Handler *p = &_cache[idx];
+ p->_wav = wav;
+ p->_ref = ref;
+ } else {
+ warning("Unable to load %s", filename);
+ }
+ return wav;
+}
+
+DataCk *Fx::operator [](int ref) {
+ int i;
+ if ((i = find(ref)) < _size)
+ _current = _cache[i]._wav;
+ else {
+ if ((i = find(0)) >= _size) {
+ clear();
+ i = 0;
+ }
+ _current = load(i, ref);
+ }
+ return _current;
+}
+
+MusicPlayer::MusicPlayer() {
+ _data = NULL;
+ _isGM = false;
+
+ MidiPlayer::createDriver();
+
+ int ret = _driver->open();
+ if (ret == 0) {
+ if (_nativeMT32)
+ _driver->sendMT32Reset();
+ else
+ _driver->sendGMReset();
+
+ // TODO: Load cmf.ins with the instrument table. It seems that an
+ // interface for such an operation is supported for AdLib. Maybe for
+ // this card, setting instruments is necessary.
+
+ _driver->setTimerCallback(this, &timerCallback);
+ }
+}
+
+MusicPlayer::~MusicPlayer() {
+ killMidi();
+}
+
+void MusicPlayer::killMidi() {
+ Audio::MidiPlayer::stop();
+
+ free(_data);
+ _data = NULL;
+}
+
+void MusicPlayer::loadMidi(int ref) {
+ // Work out the filename and check the given MIDI file exists
+ Common::String filename = Common::String::format("%.2d.MID", ref);
+ if (!_cat->exist(filename.c_str()))
+ return;
+
+ // Stop any currently playing MIDI file
+ killMidi();
+
+ // Read in the data for the file
+ VFile mid(filename.c_str());
+ _dataSize = mid.size();
+ _data = (byte *)malloc(_dataSize);
+ mid.read(_data, _dataSize);
+
+ // Start playing the music
+ sndMidiStart();
+}
+
+void MusicPlayer::sndMidiStart() {
+ _isGM = true;
+
+ MidiParser *parser = MidiParser::createParser_SMF();
+ if (parser->loadMusic(_data, _dataSize)) {
+ parser->setTrack(0);
+ parser->setMidiDriver(this);
+ parser->setTimerRate(_driver->getBaseTempo());
+ parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
+
+ _parser = parser;
+
+ syncVolume();
+
+ _isPlaying = true;
+ }
+}
+
+void MusicPlayer::send(uint32 b) {
+ if ((b & 0xF0) == 0xC0 && !_isGM && !_nativeMT32) {
+ b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8;
+ }
+
+ Audio::MidiPlayer::send(b);
+}
+
+void MusicPlayer::sendToChannel(byte channel, uint32 b) {
+ if (!_channelsTable[channel]) {
+ _channelsTable[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
+ // If a new channel is allocated during the playback, make sure
+ // its volume is correctly initialized.
+ if (_channelsTable[channel])
+ _channelsTable[channel]->volume(_channelsVolume[channel] * _masterVolume / 255);
+ }
+
+ if (_channelsTable[channel])
+ _channelsTable[channel]->send(b);
+}
+
+} // End of namespace CGE
diff --git a/engines/cge/sound.h b/engines/cge/sound.h
new file mode 100644
index 0000000000..0a7d018c81
--- /dev/null
+++ b/engines/cge/sound.h
@@ -0,0 +1,126 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#ifndef CGE_SOUND_H
+#define CGE_SOUND_H
+
+#include "cge/fileio.h"
+#include "cge/snddrv.h"
+#include "audio/audiostream.h"
+#include "audio/decoders/wave.h"
+#include "audio/fmopl.h"
+#include "audio/mididrv.h"
+#include "audio/midiparser.h"
+#include "audio/midiplayer.h"
+#include "audio/mixer.h"
+#include "common/memstream.h"
+
+namespace CGE {
+
+class CGEEngine;
+
+class DataCk {
+ byte *_buf;
+ int _ckSize;
+public:
+ DataCk(byte *buf, int bufSize);
+ ~DataCk();
+ inline const byte *addr() {
+ return _buf;
+ }
+ inline int size() {
+ return _ckSize;
+ }
+};
+
+DataCk *loadWave(VFile *file);
+
+class Sound {
+public:
+ SmpInfo _smpinf;
+ Sound(CGEEngine *vm);
+ ~Sound();
+ void open();
+ void close();
+ void play(DataCk *wav, int pan);
+ int16 getRepeat();
+ void setRepeat(int16 count);
+ void stop();
+private:
+ int _soundRepeatCount;
+ CGEEngine *_vm;
+ Audio::SoundHandle _soundHandle;
+ Audio::RewindableAudioStream *_audioStream;
+
+ void sndDigiStart(SmpInfo *PSmpInfo);
+ void sndDigiStop(SmpInfo *PSmpInfo);
+};
+
+
+class Fx {
+ struct Handler {
+ int _ref;
+ DataCk *_wav;
+ } *_cache;
+ int _size;
+ DataCk *load(int idx, int ref);
+ int find(int ref);
+public:
+ DataCk *_current;
+ Fx(int size);
+ ~Fx();
+ void clear();
+ void preload(int ref0);
+ DataCk *operator[](int ref);
+};
+
+class MusicPlayer: public Audio::MidiPlayer {
+private:
+ byte *_data;
+ int _dataSize;
+ bool _isGM;
+
+ // Start MIDI File
+ void sndMidiStart();
+
+ // Stop MIDI File
+ void sndMidiStop();
+public:
+ MusicPlayer();
+ ~MusicPlayer();
+
+ void loadMidi(int ref);
+ void killMidi();
+
+ virtual void send(uint32 b);
+ virtual void sendToChannel(byte channel, uint32 b);
+};
+
+} // End of namespace CGE
+
+#endif
+
diff --git a/engines/cge/talk.cpp b/engines/cge/talk.cpp
new file mode 100644
index 0000000000..8fd425d328
--- /dev/null
+++ b/engines/cge/talk.cpp
@@ -0,0 +1,302 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#include "cge/general.h"
+#include "cge/talk.h"
+#include "cge/game.h"
+#include "cge/events.h"
+#include "cge/cge_main.h"
+
+namespace CGE {
+
+Font::Font(const char *name) {
+ _map = (uint8 *)malloc(kMapSize);
+ _pos = (uint16 *)malloc(kPosSize * sizeof(uint16));
+ _widthArr = (uint8 *)malloc(kWidSize);
+
+ assert((_map != NULL) && (_pos != NULL) && (_widthArr != NULL));
+ mergeExt(_path, name, kFontExt);
+ load();
+}
+
+Font::~Font() {
+ free(_map);
+ free(_pos);
+ free(_widthArr);
+}
+
+void Font::load() {
+ EncryptedStream f = _path;
+ assert(!f.err());
+
+ f.read(_widthArr, kWidSize);
+ assert(!f.err());
+
+ uint16 p = 0;
+ for (uint16 i = 0; i < kPosSize; i++) {
+ _pos[i] = p;
+ p += _widthArr[i];
+ }
+ f.read(_map, p);
+}
+
+uint16 Font::width(const char *text) {
+ uint16 w = 0;
+ if (!text)
+ return 0;
+ while (*text)
+ w += _widthArr[(unsigned char)*(text++)];
+ return w;
+}
+
+Talk::Talk(CGEEngine *vm, const char *text, TextBoxStyle mode)
+ : Sprite(vm, NULL), _mode(mode), _vm(vm) {
+ _ts = NULL;
+ _flags._syst = true;
+ update(text);
+}
+
+
+Talk::Talk(CGEEngine *vm)
+ : Sprite(vm, NULL), _mode(kTBPure), _vm(vm) {
+ _ts = NULL;
+ _flags._syst = true;
+}
+
+Font *Talk::_font;
+
+void Talk::init() {
+ _font = new Font("CGE");
+}
+
+void Talk::deinit() {
+ delete _font;
+}
+
+void Talk::update(const char *text) {
+ const uint16 vmarg = (_mode) ? kTextVMargin : 0;
+ const uint16 hmarg = (_mode) ? kTextHMargin : 0;
+ uint16 mw = 0;
+ uint16 ln = vmarg;
+ uint8 *m;
+
+ if (!_ts) {
+ uint16 k = 2 * hmarg;
+ uint16 mh = 2 * vmarg + kFontHigh;
+ for (const char *p = text; *p; p++) {
+ if (*p == '|' || *p == '\n') {
+ mh += kFontHigh + kTextLineSpace;
+ if (k > mw)
+ mw = k;
+ k = 2 * hmarg;
+ } else
+ k += _font->_widthArr[(unsigned char)*p];
+ }
+ if (k > mw)
+ mw = k;
+
+ _ts = new BitmapPtr[2];
+ _ts[0] = box(mw, mh);
+ _ts[1] = NULL;
+ }
+
+ m = _ts[0]->_m + ln * mw + hmarg;
+
+ while (*text) {
+ if (*text == '|' || *text == '\n') {
+ m = _ts[0]->_m + (ln += kFontHigh + kTextLineSpace) * mw + hmarg;
+ } else {
+ int cw = _font->_widthArr[(unsigned char)*text];
+ uint8 *f = _font->_map + _font->_pos[(unsigned char)*text];
+ for (int i = 0; i < cw; i++) {
+ uint8 *pp = m;
+ uint16 n;
+ uint16 b = *(f++);
+ for (n = 0; n < kFontHigh; n++) {
+ if (b & 1)
+ *pp = kTextColFG;
+ b >>= 1;
+ pp += mw;
+ }
+ m++;
+ }
+ }
+ text++;
+ }
+ _ts[0]->code();
+ setShapeList(_ts);
+}
+
+Bitmap *Talk::box(uint16 w, uint16 h) {
+ if (w < 8)
+ w = 8;
+ if (h < 8)
+ h = 8;
+ uint16 n = w * h;
+ uint8 *b = (uint8 *)malloc(n);
+ assert(b != NULL);
+ memset(b, kTextColBG, n);
+
+ if (_mode) {
+ uint8 *p = b;
+ uint8 *q = b + n - w;
+ memset(p, kVgaColLightGray, w);
+ memset(q, kVgaColDarkGray, w);
+ while (p < q) {
+ p += w;
+ *(p - 1) = kVgaColDarkGray;
+ *p = kVgaColLightGray;
+ }
+ p = b;
+ const uint16 r = (_mode == kTBRound) ? kTextRoundCorner : 0;
+ for (int i = 0; i < r; i++) {
+ int j;
+ for (j = 0; j < r - i; j++) {
+ p[j] = kPixelTransp;
+ p[w - j - 1] = kPixelTransp;
+ q[j] = kPixelTransp;
+ q[w - j - 1] = kPixelTransp;
+ }
+ p[j] = kVgaColLightGray;
+ p[w - j - 1] = kVgaColDarkGray;
+ q[j] = kVgaColLightGray;
+ q[w - j - 1] = kVgaColDarkGray;
+ p += w;
+ q -= w;
+ }
+ }
+ return new Bitmap(w, h, b);
+}
+
+void Talk::putLine(int line, const char *text) {
+ // Note: (_ts[0]._w % 4) must be 0
+ uint16 w = _ts[0]->_w;
+ uint16 h = _ts[0]->_h;
+ uint8 *v = _ts[0]->_v;
+ uint16 dsiz = w >> 2; // data size (1 plane line size)
+ uint16 lsiz = 2 + dsiz + 2; // uint16 for line header, uint16 for gap
+ uint16 psiz = h * lsiz; // - last gap, but + plane trailer
+ uint16 size = 4 * psiz; // whole map size
+ uint16 rsiz = kFontHigh * lsiz; // length of whole text row map
+
+ // set desired line pointer
+ v += (kTextVMargin + (kFontHigh + kTextLineSpace) * line) * lsiz;
+ uint8 *p = v; // assume blanked line above text
+
+ // clear whole rectangle
+ assert((rsiz % lsiz) == 0);
+ for (int planeCtr = 0; planeCtr < 4; planeCtr++, p += psiz) {
+ for (byte *pDest = p; pDest < (p + (rsiz - lsiz)); pDest += lsiz)
+ Common::copy(p - lsiz, p, pDest);
+ }
+
+ // paint text line
+ if (!text)
+ return;
+ p = v + 2 + (kTextHMargin / 4) + (kTextHMargin % 4) * psiz;
+ uint8 *q = v + size;
+
+ while (*text) {
+ uint16 cw = _font->_widthArr[(unsigned char)*text], i;
+ uint8 *fp = _font->_map + _font->_pos[(unsigned char)*text];
+
+ for (i = 0; i < cw; i++) {
+ uint16 b = fp[i];
+ uint16 n;
+ for (n = 0; n < kFontHigh; n++) {
+ if (b & 1)
+ *p = kTextColFG;
+ b >>= 1;
+ p += lsiz;
+ }
+ p = p - rsiz + psiz;
+ if (p >= q)
+ p = p - size + 1;
+ }
+ text++;
+ }
+}
+
+InfoLine::InfoLine(CGEEngine *vm, uint16 w) : Talk(vm), _oldText(NULL), _vm(vm) {
+ if (!_ts) {
+ _ts = new BitmapPtr[2];
+ _ts[1] = NULL;
+ }
+
+ _ts[0] = new Bitmap(w, kFontHigh, kTextColBG);
+ setShapeList(_ts);
+}
+
+void InfoLine::update(const char *text) {
+ if (text == _oldText)
+ return;
+
+ uint16 w = _ts[0]->_w;
+ uint16 h = _ts[0]->_h;
+ uint8 *v = (uint8 *)_ts[0]->_v;
+ uint16 dsiz = w >> 2; // data size (1 plane line size)
+ uint16 lsiz = 2 + dsiz + 2; // uint16 for line header, uint16 for gap
+ uint16 psiz = h * lsiz; // - last gape, but + plane trailer
+ uint16 size = 4 * psiz; // whole map size
+
+ // clear whole rectangle
+ memset(v + 2, kTextColBG, dsiz); // data bytes
+ for (byte *pDest = v + lsiz; pDest < (v + psiz); pDest += lsiz) {
+ Common::copy(v, v + lsiz, pDest);
+ }
+ *(uint16 *)(v + psiz - 2) = TO_LE_16(kBmpEOI); // plane trailer uint16
+ for (byte *pDest = v + psiz; pDest < (v + 4 * psiz); pDest += psiz) {
+ Common::copy(v, v + psiz, pDest);
+ }
+
+ // paint text line
+ if (text) {
+ uint8 *p = v + 2, * q = p + size;
+
+ while (*text) {
+ uint16 cw = _font->_widthArr[(unsigned char)*text];
+ uint8 *fp = _font->_map + _font->_pos[(unsigned char)*text];
+
+ for (uint16 i = 0; i < cw; i++) {
+ uint16 b = fp[i];
+ for (uint16 n = 0; n < kFontHigh; n++) {
+ if (b & 1)
+ *p = kTextColFG;
+ b >>= 1;
+ p += lsiz;
+ }
+ if (p >= q)
+ p = p - size + 1;
+ }
+ text++;
+ }
+ }
+
+ _oldText = text;
+}
+
+} // End of namespace CGE
diff --git a/engines/cge/talk.h b/engines/cge/talk.h
new file mode 100644
index 0000000000..23ef9c9c07
--- /dev/null
+++ b/engines/cge/talk.h
@@ -0,0 +1,96 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#ifndef CGE_TALK_H
+#define CGE_TALK_H
+
+#include "cge/general.h"
+#include "cge/vga13h.h"
+
+namespace CGE {
+
+#define kTextColFG kVgaColDark // foreground color
+#define kTextColBG kVgaColGray // background color
+#define kTextHMargin (6&~1) // EVEN horizontal margins!
+#define kTextVMargin 5 // vertical margins
+#define kTextLineSpace 2 // line spacing
+#define kTextRoundCorner 3 // rounded corners
+#define kWidSize 256
+#define kPosSize 256
+#define kMapSize (256*8)
+#define kFontHigh 8
+#define kFontExt ".CFT"
+#define kPathMax 128
+
+enum TextBoxStyle { kTBPure, kTBRect, kTBRound };
+
+class Font {
+ char _path[kPathMax];
+ void load();
+public:
+ uint8 *_widthArr;
+ uint16 *_pos;
+ uint8 *_map;
+ Font(const char *name);
+ ~Font();
+ uint16 width(const char *text);
+ void save();
+};
+
+class Talk : public Sprite {
+protected:
+ TextBoxStyle _mode;
+ BitmapPtr *_ts;
+ Bitmap *box(uint16 w, uint16 h);
+public:
+ Talk(CGEEngine *vm, const char *text, TextBoxStyle mode);
+ Talk(CGEEngine *vm);
+ //~TALK();
+
+ static Font *_font;
+ static void init();
+ static void deinit();
+
+ virtual void update(const char *text);
+ virtual void update() {}
+ void putLine(int line, const char *text);
+private:
+ CGEEngine *_vm;
+};
+
+class InfoLine : public Talk {
+ const char *_oldText;
+public:
+ InfoLine(CGEEngine *vm, uint16 wid);
+ void update(const char *text);
+private:
+ CGEEngine *_vm;
+};
+
+} // End of namespace CGE
+
+#endif
diff --git a/engines/cge/text.cpp b/engines/cge/text.cpp
new file mode 100644
index 0000000000..71bb411f11
--- /dev/null
+++ b/engines/cge/text.cpp
@@ -0,0 +1,215 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#include "cge/general.h"
+#include "cge/text.h"
+#include "cge/talk.h"
+#include "cge/game.h"
+#include "cge/snail.h"
+#include "cge/cge_main.h"
+#include "common/str.h"
+
+namespace CGE {
+
+Text *_text;
+Talk *_talk = NULL;
+
+Text::Text(CGEEngine *vm, const char *fname) : _vm(vm) {
+ mergeExt(_fileName, fname, kSayExt);
+ if (!_cat->exist(_fileName))
+ error("No talk (%s)\n", _fileName);
+ int16 txtCount = count() + 1;
+ warning("Number of texts: %d", txtCount);
+
+ _cache = new Handler[txtCount];
+ for (_size = 0; _size < txtCount; _size++) {
+ _cache[_size]._ref = 0;
+ _cache[_size]._text = NULL;
+ }
+ load();
+}
+
+Text::~Text() {
+ clear();
+ delete[] _cache;
+}
+
+int16 Text::count() {
+ EncryptedStream tf = _fileName;
+ if (tf.err())
+ return NULL;
+
+ Common::String line;
+ char tmpStr[kLineMax + 1];
+ int n, count = 0;
+
+ for (line = tf.readLine(); !tf.eos(); line = tf.readLine()) {
+ n = line.size();
+ char *s;
+
+ strcpy(tmpStr, line.c_str());
+ if ((s = strtok(tmpStr, " =,;/\t\n")) == NULL)
+ continue;
+ if (!isdigit(*s))
+ continue;
+
+ count++;
+ }
+ return count;
+}
+
+void Text::clear() {
+ for (Handler *p = _cache, *q = p + _size; p < q; p++) {
+ if (p->_ref) {
+ p->_ref = 0;
+ delete[] p->_text;
+ p->_text = NULL;
+ }
+ }
+}
+
+void Text::load() {
+ EncryptedStream tf = _fileName;
+ assert(!tf.err());
+
+ Common::String line;
+ char tmpStr[kLineMax + 1];
+ int idx;
+
+ for (idx = 0, line = tf.readLine(); !tf.eos(); line = tf.readLine()) {
+ int n = line.size();
+ char *s;
+
+ strcpy(tmpStr, line.c_str());
+ if ((s = strtok(tmpStr, " =,;/\t\n")) == NULL)
+ continue;
+ if (!isdigit(*s))
+ continue;
+
+ int r = atoi(s);
+
+ s += strlen(s);
+ if (s < tmpStr + n)
+ ++s;
+
+ _cache[idx]._ref = r;
+ _cache[idx]._text = new char[strlen(s) + 1];
+ strcpy(_cache[idx]._text, s);
+ idx++;
+ }
+}
+
+char *Text::getText(int ref) {
+ int i;
+ for (i = 0; (i < _size) && (_cache[i]._ref != ref); i++)
+ ;
+
+ if (i < _size)
+ return _cache[i]._text;
+
+ warning("getText: Unable to find ref %d", ref);
+ return NULL;
+}
+
+void Text::say(const char *text, Sprite *spr) {
+ killText();
+ _talk = new Talk(_vm, text, kTBRound);
+ if (!_talk)
+ return;
+
+ bool east = spr->_flags._east;
+ int x = (east) ? (spr->_x + spr->_w - 2) : (spr->_x + 2);
+ int y = spr->_y + 2;
+ Sprite *spike = new Spike(_vm);
+ uint16 sw = spike->_w;
+
+ if (east) {
+ if (x + sw + kTextRoundCorner + 5 >= kScrWidth)
+ east = false;
+ } else {
+ if (x <= 5 + kTextRoundCorner + sw)
+ east = true;
+ }
+ x = (east) ? (spr->_x + spr->_w - 2) : (spr->_x + 2 - sw);
+ if (spr->_ref == 1)
+ x += ((east) ? -10 : 10); // Hero
+
+ _talk->_flags._kill = true;
+ _talk->_flags._bDel = true;
+ _talk->setName(_text->getText(kSayName));
+ _talk->gotoxy(x - (_talk->_w - sw) / 2 - 3 + 6 * east, y - spike->_h - _talk->_h + 1);
+ _talk->_z = 125;
+ _talk->_ref = kSayRef;
+
+ spike->gotoxy(x, _talk->_y + _talk->_h - 1);
+ spike->_z = 126;
+ spike->_flags._slav = true;
+ spike->_flags._kill = true;
+ spike->setName(_text->getText(kSayName));
+ spike->step(east);
+ spike->_ref = kSayRef;
+
+ _vga->_showQ->insert(_talk, _vga->_showQ->last());
+ _vga->_showQ->insert(spike, _vga->_showQ->last());
+}
+
+void CGEEngine::inf(const char *text) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::inf(%s)", text);
+
+ killText();
+ _talk = new Talk(this, text, kTBRect);
+ if (!_talk)
+ return;
+
+ _talk->_flags._kill = true;
+ _talk->_flags._bDel = true;
+ _talk->setName(_text->getText(kInfName));
+ _talk->center();
+ _talk->gotoxy(_talk->_x, _talk->_y - 20);
+ _talk->_z = 126;
+ _talk->_ref = kInfRef;
+ _vga->_showQ->insert(_talk, _vga->_showQ->last());
+}
+
+void Text::sayTime(Sprite *spr) {
+ TimeDate curTime;
+ _vm->_system->getTimeAndDate(curTime);
+
+ char t[6];
+ sprintf(t, "%d:%02d", curTime.tm_hour, curTime.tm_min);
+ say(t, spr);
+}
+
+void killText() {
+ if (!_talk)
+ return;
+
+ _snail_->addCom(kSnKill, -1, 0, _talk);
+ _talk = NULL;
+}
+
+} // End of namespace CGE
diff --git a/engines/cge/text.h b/engines/cge/text.h
new file mode 100644
index 0000000000..668f307f72
--- /dev/null
+++ b/engines/cge/text.h
@@ -0,0 +1,71 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#ifndef CGE_TEXT_H
+#define CGE_TEXT_H
+
+#include "cge/talk.h"
+
+namespace CGE {
+
+#define kSayExt ".SAY"
+#define kSysTextMax 1000
+#define kTextNoMouse 95
+#define kInfName 101
+#define kSayName 102
+#define kInfRef 301
+#define kSayRef 302
+
+
+class Text {
+ struct Handler {
+ int _ref;
+ char *_text;
+ } *_cache;
+ int _size;
+ char _fileName[kPathMax];
+ void load();
+ int16 count();
+public:
+ Text(CGEEngine *vm, const char *fname);
+ ~Text();
+ void clear();
+ char *getText(int ref);
+ void say(const char *text, Sprite *spr);
+ void sayTime(Sprite *spr);
+private:
+ CGEEngine *_vm;
+};
+
+extern Talk *_talk;
+extern Text *_text;
+
+void killText();
+
+} // End of namespace CGE
+
+#endif
diff --git a/engines/cge/vga13h.cpp b/engines/cge/vga13h.cpp
new file mode 100644
index 0000000000..727cc72e39
--- /dev/null
+++ b/engines/cge/vga13h.cpp
@@ -0,0 +1,1011 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#include "common/array.h"
+#include "common/rect.h"
+#include "graphics/palette.h"
+#include "cge/general.h"
+#include "cge/vga13h.h"
+#include "cge/bitmap.h"
+#include "cge/text.h"
+#include "cge/cge_main.h"
+#include "cge/cge.h"
+
+namespace CGE {
+
+Seq *getConstantSeq(bool seqFlag) {
+ const Seq seq1[] = { { 0, 0, 0, 0, 0 } };
+ const Seq seq2[] = { { 0, 1, 0, 0, 0 }, { 1, 0, 0, 0, 0 } };
+
+ Seq *seq;
+ if (seqFlag) {
+ seq = (Seq *)malloc(1 * sizeof(Seq));
+ *seq = seq1[0];
+ } else {
+ seq = (Seq *)malloc(2 * sizeof(Seq));
+ seq[0] = seq2[0];
+ seq[1] = seq2[1];
+ }
+
+ return seq;
+}
+
+extern "C" void SNDMIDIPlay();
+
+Dac mkDac(uint8 r, uint8 g, uint8 b) {
+ static Dac x;
+ x._r = r;
+ x._g = g;
+ x._b = b;
+ return x;
+}
+
+Sprite *locate(int ref) {
+ Sprite *spr = _vga->_showQ->locate(ref);
+ return (spr) ? spr : _vga->_spareQ->locate(ref);
+}
+
+Sprite::Sprite(CGEEngine *vm, BitmapPtr *shpP)
+ : _x(0), _y(0), _z(0), _nearPtr(0), _takePtr(0),
+ _next(NULL), _prev(NULL), _seqPtr(kNoSeq), _time(0),
+ _ext(NULL), _ref(-1), _cave(0), _vm(vm) {
+ memset(_file, 0, sizeof(_file));
+ *((uint16 *)&_flags) = 0;
+ _ref = 0;
+ _x = _y = 0;
+ _w = _h = 0;
+ _time = 0;
+ _seqPtr = 0;
+ _shpCnt = 0;
+ _prev = _next = NULL;
+
+ setShapeList(shpP);
+}
+
+Sprite::~Sprite() {
+ if (_sprite == this)
+ _sprite = NULL;
+
+ contract();
+}
+
+BitmapPtr Sprite::shp() {
+ SprExt *e = _ext;
+ if (!e || !e->_seq)
+ return NULL;
+
+ int i = e->_seq[_seqPtr]._now;
+ if (i >= _shpCnt)
+ error("Invalid PHASE in SPRITE::Shp() %s", _file);
+ return e->_shpList[i];
+}
+
+BitmapPtr *Sprite::setShapeList(BitmapPtr *shpP) {
+ BitmapPtr *r = (_ext) ? _ext->_shpList : NULL;
+
+ _shpCnt = 0;
+ _w = 0;
+ _h = 0;
+
+ if (shpP) {
+ BitmapPtr *p;
+ for (p = shpP; *p; p++) {
+ BitmapPtr b = (*p); // ->Code();
+ if (b->_w > _w)
+ _w = b->_w;
+ if (b->_h > _h)
+ _h = b->_h;
+ _shpCnt++;
+ }
+ expand();
+ _ext->_shpList = shpP;
+ _flags._bDel = true;
+ if (!_ext->_seq)
+ setSeq(getConstantSeq(_shpCnt < 2));
+ }
+ return r;
+}
+
+void Sprite::moveShapes(uint8 *buf) {
+ BitmapPtr *p;
+ for (p = _ext->_shpList; *p; p++) {
+ buf += (*p)->moveVmap(buf);
+ }
+}
+
+bool Sprite::works(Sprite *spr) {
+ if (!spr || !spr->_ext)
+ return false;
+
+ Snail::Com *c = spr->_ext->_take;
+ if (c != NULL) {
+ c += spr->_takePtr;
+ if (c->_ref == _ref)
+ if (c->_com != kSnLabel || (c->_val == 0 || c->_val == _vm->_now))
+ return true;
+ }
+
+ return false;
+}
+
+Seq *Sprite::setSeq(Seq *seq) {
+ if (_ext) {
+ free(_ext->_seq);
+ _ext->_seq = NULL;
+ }
+
+ expand();
+
+ Seq *s = _ext->_seq;
+ _ext->_seq = seq;
+ if (_seqPtr == kNoSeq)
+ step(0);
+ else if (_time == 0)
+ step(_seqPtr);
+ return s;
+}
+
+bool Sprite::seqTest(int n) {
+ if (n >= 0)
+ return (_seqPtr == n);
+ if (_ext)
+ return (_ext->_seq[_seqPtr]._next == _seqPtr);
+ return true;
+}
+
+Snail::Com *Sprite::snList(SnList type) {
+ SprExt *e = _ext;
+ if (e)
+ return (type == kNear) ? e->_near : e->_take;
+ return NULL;
+}
+
+void Sprite::setName(char *newName) {
+ if (!_ext)
+ return;
+
+ if (_ext->_name) {
+ delete[] _ext->_name;
+ _ext->_name = NULL;
+ }
+ if (newName) {
+ _ext->_name = new char[strlen(newName) + 1];
+ assert(_ext->_name != NULL);
+ strcpy(_ext->_name, newName);
+ }
+}
+
+Sprite *Sprite::expand() {
+ if (_ext)
+ return this;
+
+ _ext = new SprExt;
+ assert(_ext != NULL);
+ if (!*_file)
+ return this;
+
+ static const char *Comd[] = { "Name", "Phase", "Seq", "Near", "Take", NULL };
+ char line[kLineMax], fname[kPathMax];
+
+ Common::Array<BitmapPtr> shplist;
+ for (int i = 0; i < _shpCnt + 1; ++i)
+ shplist.push_back(NULL);
+
+ Seq *seq = NULL;
+ int shpcnt = 0,
+ seqcnt = 0,
+ neacnt = 0,
+ takcnt = 0,
+ maxnow = 0,
+ maxnxt = 0;
+
+ Snail::Com *nea = NULL;
+ Snail::Com *tak = NULL;
+ mergeExt(fname, _file, kSprExt);
+ if (_cat->exist(fname)) { // sprite description file exist
+ VFile sprf(fname);
+ if (!(sprf._error==0))
+ error("Bad SPR [%s]", fname);
+ int len = 0, lcnt = 0;
+ while ((len = sprf.read((uint8 *)line)) != 0) {
+ lcnt++;
+ if (len && line[len - 1] == '\n')
+ line[--len] = '\0';
+ if (len == 0 || *line == '.')
+ continue;
+
+ Snail::Com *c;
+ switch (takeEnum(Comd, strtok(line, " =\t"))) {
+ case 0:
+ // Name
+ setName(strtok(NULL, ""));
+ break;
+ case 1:
+ // Phase
+ // In case the shape index gets too high, increase the array size
+ while ((shpcnt + 1) >= (int)shplist.size()) {
+ shplist.push_back(NULL);
+ ++_shpCnt;
+ }
+ shplist[shpcnt++] = new Bitmap(strtok(NULL, " \t,;/"));
+ break;
+ case 2:
+ // Seq
+ seq = (Seq *) realloc(seq, (seqcnt + 1) * sizeof(*seq));
+ assert(seq != NULL);
+ Seq *s;
+ s = &seq[seqcnt++];
+ s->_now = atoi(strtok(NULL, " \t,;/"));
+ if (s->_now > maxnow)
+ maxnow = s->_now;
+ s->_next = atoi(strtok(NULL, " \t,;/"));
+ switch (s->_next) {
+ case 0xFF:
+ s->_next = seqcnt;
+ break;
+ case 0xFE:
+ s->_next = seqcnt - 1;
+ break;
+ }
+ if (s->_next > maxnxt)
+ maxnxt = s->_next;
+ s->_dx = atoi(strtok(NULL, " \t,;/"));
+ s->_dy = atoi(strtok(NULL, " \t,;/"));
+ s->_dly = atoi(strtok(NULL, " \t,;/"));
+ break;
+ case 3:
+ // Near
+ if (_nearPtr == kNoPtr)
+ break;
+ nea = (Snail::Com *) realloc(nea, (neacnt + 1) * sizeof(*nea));
+ assert(nea != NULL);
+ c = &nea[neacnt++];
+ if ((c->_com = (SnCom)takeEnum(Snail::_comText, strtok(NULL, " \t,;/"))) < 0)
+ error("Bad NEAR in %d [%s]", lcnt, fname);
+ c->_ref = atoi(strtok(NULL, " \t,;/"));
+ c->_val = atoi(strtok(NULL, " \t,;/"));
+ c->_ptr = NULL;
+ break;
+ case 4:
+ // Take
+ if (_takePtr == kNoPtr)
+ break;
+ tak = (Snail::Com *) realloc(tak, (takcnt + 1) * sizeof(*tak));
+ assert(tak != NULL);
+ c = &tak[takcnt++];
+ if ((c->_com = (SnCom)takeEnum(Snail::_comText, strtok(NULL, " \t,;/"))) < 0)
+ error("Bad NEAR in %d [%s]", lcnt, fname);
+ c->_ref = atoi(strtok(NULL, " \t,;/"));
+ c->_val = atoi(strtok(NULL, " \t,;/"));
+ c->_ptr = NULL;
+ break;
+ }
+ }
+ } else {
+ // no sprite description: try to read immediately from .BMP
+ shplist[shpcnt++] = new Bitmap(_file);
+ }
+
+ shplist[shpcnt] = NULL;
+ if (seq) {
+ if (maxnow >= shpcnt)
+ error("Bad PHASE in SEQ [%s]", fname);
+ if (maxnxt >= seqcnt)
+ error("Bad JUMP in SEQ [%s]", fname);
+ setSeq(seq);
+ } else
+ setSeq(getConstantSeq(_shpCnt == 1));
+
+ // Set the shape list
+ BitmapPtr *shapeList = new BitmapPtr[shplist.size()];
+ for (uint i = 0; i < shplist.size(); ++i)
+ shapeList[i] = shplist[i];
+
+ setShapeList(shapeList);
+
+ if (nea)
+ nea[neacnt - 1]._ptr = _ext->_near = nea;
+ else
+ _nearPtr = kNoPtr;
+ if (tak)
+ tak[takcnt - 1]._ptr = _ext->_take = tak;
+ else
+ _takePtr = kNoPtr;
+
+ return this;
+}
+
+Sprite *Sprite::contract() {
+ SprExt *e = _ext;
+ if (!e)
+ return this;
+
+ if (e->_name)
+ delete[] e->_name;
+ if (_flags._bDel && e->_shpList) {
+ for (int i = 0; e->_shpList[i]; i++)
+ delete e->_shpList[i];
+ delete[] e->_shpList;
+ }
+
+ free(e->_seq);
+ free(e->_near);
+ free(e->_take);
+
+ delete e;
+ _ext = NULL;
+
+ return this;
+}
+
+Sprite *Sprite::backShow(bool fast) {
+ expand();
+ show(2);
+ show(1);
+ if (fast)
+ show(0);
+ contract();
+ return this;
+}
+
+void Sprite::step(int nr) {
+ if (nr >= 0)
+ _seqPtr = nr;
+ if (_ext) {
+ Seq *seq;
+ if (nr < 0)
+ _seqPtr = _ext->_seq[_seqPtr]._next;
+ seq = _ext->_seq + _seqPtr;
+ if (seq->_dly >= 0) {
+ gotoxy(_x + (seq->_dx), _y + (seq->_dy));
+ _time = seq->_dly;
+ }
+ }
+}
+
+void Sprite::tick() {
+ step();
+}
+
+void Sprite::makeXlat(uint8 *x) {
+ if (!_ext)
+ return;
+
+ if (_flags._xlat)
+ killXlat();
+ for (BitmapPtr *b = _ext->_shpList; *b; b++)
+ (*b)->_m = x;
+ _flags._xlat = true;
+}
+
+void Sprite::killXlat() {
+ if (!_flags._xlat || !_ext)
+ return;
+
+ uint8 *m = (*_ext->_shpList)->_m;
+ free(m);
+
+ for (BitmapPtr *b = _ext->_shpList; *b; b++)
+ (*b)->_m = NULL;
+ _flags._xlat = false;
+}
+
+void Sprite::gotoxy(int x, int y) {
+ int xo = _x, yo = _y;
+ if (_x < kScrWidth) {
+ if (x < 0)
+ x = 0;
+ if (x + _w > kScrWidth)
+ x = (kScrWidth - _w);
+ _x = x;
+ }
+ if (_h < kScrHeight) {
+ if (y < 0)
+ y = 0;
+ if (y + _h > kScrHeight)
+ y = (kScrHeight - _h);
+ _y = y;
+ }
+ if (_next)
+ if (_next->_flags._slav)
+ _next->gotoxy(_next->_x - xo + _x, _next->_y - yo + _y);
+ if (_flags._shad)
+ _prev->gotoxy(_prev->_x - xo + _x, _prev->_y - yo + _y);
+}
+
+void Sprite::center() {
+ gotoxy((kScrWidth - _w) / 2, (kScrHeight - _h) / 2);
+}
+
+void Sprite::show() {
+ SprExt *e;
+ e = _ext;
+ e->_x0 = e->_x1;
+ e->_y0 = e->_y1;
+ e->_b0 = e->_b1;
+ e->_x1 = _x;
+ e->_y1 = _y;
+ e->_b1 = shp();
+ if (!_flags._hide) {
+ if (_flags._xlat)
+ e->_b1->xShow(e->_x1, e->_y1);
+ else
+ e->_b1->show(e->_x1, e->_y1);
+ }
+}
+
+void Sprite::show(uint16 pg) {
+ Graphics::Surface *a = _vga->_page[1];
+ _vga->_page[1] = _vga->_page[pg & 3];
+ shp()->show(_x, _y);
+ _vga->_page[1] = a;
+}
+
+void Sprite::hide() {
+ SprExt *e = _ext;
+ if (e->_b0)
+ e->_b0->hide(e->_x0, e->_y0);
+}
+
+BitmapPtr Sprite::ghost() {
+ SprExt *e = _ext;
+ if (!e->_b1)
+ return NULL;
+
+ BitmapPtr bmp = new Bitmap(0, 0, (uint8 *)NULL);
+ assert(bmp != NULL);
+ bmp->_w = e->_b1->_w;
+ bmp->_h = e->_b1->_h;
+ bmp->_b = new HideDesc[bmp->_h];
+ assert(bmp->_b != NULL);
+ bmp->_v = (uint8 *) memcpy(bmp->_b, e->_b1->_b, sizeof(HideDesc) * bmp->_h);
+ bmp->_map = (e->_y1 << 16) + e->_x1;
+ return bmp;
+}
+
+void Sprite::sync(Common::Serializer &s) {
+ uint16 unused = 0;
+
+ s.syncAsUint16LE(unused);
+ s.syncAsUint16LE(unused); // _ext
+ s.syncAsUint16LE(_ref);
+ s.syncAsByte(_cave);
+
+ // bitfield in-memory storage is unpredictable, so to avoid
+ // any issues, pack/unpack everything manually
+ uint16 flags = 0;
+ if (s.isLoading()) {
+ s.syncAsUint16LE(flags);
+ _flags._hide = flags & 0x0001 ? true : false;
+ _flags._near = flags & 0x0002 ? true : false;
+ _flags._drag = flags & 0x0004 ? true : false;
+ _flags._hold = flags & 0x0008 ? true : false;
+ _flags._____ = flags & 0x0010 ? true : false;
+ _flags._slav = flags & 0x0020 ? true : false;
+ _flags._syst = flags & 0x0040 ? true : false;
+ _flags._kill = flags & 0x0080 ? true : false;
+ _flags._xlat = flags & 0x0100 ? true : false;
+ _flags._port = flags & 0x0200 ? true : false;
+ _flags._kept = flags & 0x0400 ? true : false;
+ _flags._east = flags & 0x0800 ? true : false;
+ _flags._shad = flags & 0x1000 ? true : false;
+ _flags._back = flags & 0x2000 ? true : false;
+ _flags._bDel = flags & 0x4000 ? true : false;
+ _flags._tran = flags & 0x8000 ? true : false;
+ } else {
+ flags = (flags << 1) | _flags._tran;
+ flags = (flags << 1) | _flags._bDel;
+ flags = (flags << 1) | _flags._back;
+ flags = (flags << 1) | _flags._shad;
+ flags = (flags << 1) | _flags._east;
+ flags = (flags << 1) | _flags._kept;
+ flags = (flags << 1) | _flags._port;
+ flags = (flags << 1) | _flags._xlat;
+ flags = (flags << 1) | _flags._kill;
+ flags = (flags << 1) | _flags._syst;
+ flags = (flags << 1) | _flags._slav;
+ flags = (flags << 1) | _flags._____;
+ flags = (flags << 1) | _flags._hold;
+ flags = (flags << 1) | _flags._drag;
+ flags = (flags << 1) | _flags._near;
+ flags = (flags << 1) | _flags._hide;
+ s.syncAsUint16LE(flags);
+ }
+
+ s.syncAsUint16LE(_x);
+ s.syncAsUint16LE(_y);
+ s.syncAsByte(_z);
+ s.syncAsUint16LE(_w);
+ s.syncAsUint16LE(_h);
+ s.syncAsUint16LE(_time);
+ s.syncAsByte(_nearPtr);
+ s.syncAsByte(_takePtr);
+ s.syncAsSint16LE(_seqPtr);
+ s.syncAsUint16LE(_shpCnt);
+ s.syncBytes((byte *)&_file[0], 9);
+ _file[8] = '\0';
+
+ s.syncAsUint16LE(unused); // _prev
+ s.syncAsUint16LE(unused); // _next
+}
+
+Sprite *spriteAt(int x, int y) {
+ Sprite *spr = NULL, * tail = _vga->_showQ->last();
+ if (tail) {
+ for (spr = tail->_prev; spr; spr = spr->_prev) {
+ if (! spr->_flags._hide && ! spr->_flags._tran) {
+ if (spr->shp()->solidAt(x - spr->_x, y - spr->_y))
+ break;
+ }
+ }
+ }
+ return spr;
+}
+
+Queue::Queue(bool show) : _head(NULL), _tail(NULL), _show(show) {
+}
+
+Queue::~Queue() {
+ clear();
+}
+
+void Queue::clear() {
+ while (_head) {
+ Sprite *s = remove(_head);
+ if (s->_flags._kill)
+ delete s;
+ }
+}
+
+void Queue::forAll(void (*fun)(Sprite *)) {
+ Sprite *s = _head;
+ while (s) {
+ Sprite *n = s->_next;
+ fun(s);
+ s = n;
+ }
+}
+
+void Queue::append(Sprite *spr) {
+ if (_tail) {
+ spr->_prev = _tail;
+ _tail->_next = spr;
+ } else
+ _head = spr;
+ _tail = spr;
+ if (_show)
+ spr->expand();
+ else
+ spr->contract();
+}
+
+void Queue::insert(Sprite *spr, Sprite *nxt) {
+ if (nxt == _head) {
+ spr->_next = _head;
+ _head = spr;
+ if (!_tail)
+ _tail = spr;
+ } else {
+ assert(nxt);
+ spr->_next = nxt;
+ spr->_prev = nxt->_prev;
+ if (spr->_prev)
+ spr->_prev->_next = spr;
+ }
+ if (spr->_next)
+ spr->_next->_prev = spr;
+ if (_show)
+ spr->expand();
+ else
+ spr->contract();
+}
+
+void Queue::insert(Sprite *spr) {
+ Sprite *s;
+ for (s = _head; s; s = s->_next)
+ if (s->_z > spr->_z)
+ break;
+ if (s)
+ insert(spr, s);
+ else
+ append(spr);
+ if (_show)
+ spr->expand();
+ else
+ spr->contract();
+}
+
+template<typename T>
+inline bool contains(const Common::List<T> &l, const T &v) {
+ return (Common::find(l.begin(), l.end(), v) != l.end());
+}
+
+Sprite *Queue::remove(Sprite *spr) {
+ if (spr == _head)
+ _head = spr->_next;
+ if (spr == _tail)
+ _tail = spr->_prev;
+ if (spr->_next)
+ spr->_next->_prev = spr->_prev;
+ if (spr->_prev)
+ spr->_prev->_next = spr->_next;
+ spr->_prev = NULL;
+ spr->_next = NULL;
+ return spr;
+}
+
+Sprite *Queue::locate(int ref) {
+ for (Sprite *spr = _head; spr; spr = spr->_next) {
+ if (spr->_ref == ref)
+ return spr;
+ }
+ return NULL;
+}
+
+Vga::Vga() : _frmCnt(0), _msg(NULL), _name(NULL), _setPal(false), _mono(0) {
+ _oldColors = NULL;
+ _newColors = NULL;
+ _showQ = new Queue(true);
+ _spareQ = new Queue(false);
+ _sysPal = new Dac[kPalCount];
+
+ for (int idx = 0; idx < 4; idx++) {
+ _page[idx] = new Graphics::Surface();
+ _page[idx]->create(320, 200, Graphics::PixelFormat::createFormatCLUT8());
+ }
+
+ for (int i = 10; i < 20; i++) {
+ char *text = _text->getText(i);
+ if (text) {
+ debugN(1, "%s\n", text);
+ }
+ }
+
+ _oldColors = (Dac *)malloc(sizeof(Dac) * kPalCount);
+ _newColors = (Dac *)malloc(sizeof(Dac) * kPalCount);
+ getColors(_oldColors);
+ sunset();
+ setColors();
+ clear(0);
+}
+
+Vga::~Vga() {
+ _mono = 0;
+
+ Common::String buffer = "";
+/*
+ clear(0);
+ setMode(_oldMode);
+ setColors();
+ restoreScreen(_oldScreen);
+ sunrise(_oldColors);
+*/
+ free(_oldColors);
+ free(_newColors);
+ if (_msg)
+ buffer = Common::String(_msg);
+ if (_name)
+ buffer = buffer + " [" + _name + "]";
+
+ debugN("%s", buffer.c_str());
+
+ delete _showQ;
+ delete _spareQ;
+ delete[] _sysPal;
+
+ for (int idx = 0; idx < 4; idx++) {
+ _page[idx]->free();
+ delete _page[idx];
+ }
+}
+
+void Vga::waitVR() {
+ // Since some of the game parts rely on using vertical sync as a delay mechanism,
+ // we're introducing a short delay to simulate it
+ g_system->delayMillis(5);
+}
+
+void Vga::getColors(Dac *tab) {
+ byte palData[kPalSize];
+ g_system->getPaletteManager()->grabPalette(palData, 0, kPalCount);
+ palToDac(palData, tab);
+}
+
+void Vga::palToDac(const byte *palData, Dac *tab) {
+ const byte *colP = palData;
+ for (int idx = 0; idx < kPalCount; idx++, colP += 3) {
+ tab[idx]._r = *colP >> 2;
+ tab[idx]._g = *(colP + 1) >> 2;
+ tab[idx]._b = *(colP + 2) >> 2;
+ }
+}
+
+void Vga::dacToPal(const Dac *tab, byte *palData) {
+ for (int idx = 0; idx < kPalCount; idx++, palData += 3) {
+ *palData = tab[idx]._r << 2;
+ *(palData + 1) = tab[idx]._g << 2;
+ *(palData + 2) = tab[idx]._b << 2;
+ }
+}
+
+void Vga::setColors(Dac *tab, int lum) {
+ Dac *palP = tab, *destP = _newColors;
+ for (int idx = 0; idx < kPalCount; idx++, palP++, destP++) {
+ destP->_r = (palP->_r * lum) >> 6;
+ destP->_g = (palP->_g * lum) >> 6;
+ destP->_b = (palP->_b * lum) >> 6;
+ }
+
+ if (_mono) {
+ destP = _newColors;
+ for (int idx = 0; idx < kPalCount; idx++, destP++) {
+ // Form a greyscalce colour from 30% R, 59% G, 11% B
+ uint8 intensity = (((int)destP->_r * 77) + ((int)destP->_g * 151) + ((int)destP->_b * 28)) >> 8;
+ destP->_r = intensity;
+ destP->_g = intensity;
+ destP->_b = intensity;
+ }
+ }
+
+ _setPal = true;
+}
+
+void Vga::setColors() {
+ memset(_newColors, 0, kPalSize);
+ updateColors();
+}
+
+void Vga::sunrise(Dac *tab) {
+ for (int i = 0; i <= 64; i += kFadeStep) {
+ setColors(tab, i);
+ waitVR();
+ updateColors();
+ }
+}
+
+void Vga::sunset() {
+ Dac tab[256];
+ getColors(tab);
+ for (int i = 64; i >= 0; i -= kFadeStep) {
+ setColors(tab, i);
+ waitVR();
+ updateColors();
+ }
+}
+
+void Vga::show() {
+ for (Sprite *spr = _showQ->first(); spr; spr = spr->_next)
+ spr->show();
+ update();
+ for (Sprite *spr = _showQ->first(); spr; spr = spr->_next)
+ spr->hide();
+
+ _frmCnt++;
+}
+
+void Vga::updateColors() {
+ byte palData[kPalSize];
+ dacToPal(_newColors, palData);
+ g_system->getPaletteManager()->setPalette(palData, 0, 256);
+}
+
+void Vga::update() {
+ SWAP(Vga::_page[0], Vga::_page[1]);
+
+ if (_setPal) {
+ updateColors();
+ _setPal = false;
+ }
+
+ g_system->copyRectToScreen((const byte *)Vga::_page[0]->getBasePtr(0, 0), kScrWidth, 0, 0, kScrWidth, kScrHeight);
+ g_system->updateScreen();
+}
+
+void Vga::clear(uint8 color) {
+ for (int paneNum = 0; paneNum < 4; paneNum++)
+ _page[paneNum]->fillRect(Common::Rect(0, 0, kScrWidth, kScrHeight), color);
+}
+
+void Vga::copyPage(uint16 d, uint16 s) {
+ _page[d]->copyFrom(*_page[s]);
+}
+
+//--------------------------------------------------------------------------
+
+void Bitmap::xShow(int16 x, int16 y) {
+ debugC(4, kCGEDebugBitmap, "Bitmap::xShow(%d, %d)", x, y);
+
+ const byte *srcP = (const byte *)_v;
+ byte *destEndP = (byte *)_vga->_page[1]->pixels + (kScrWidth * kScrHeight);
+ byte *lookupTable = _m;
+
+ // Loop through processing data for each plane. The game originally ran in plane mapped mode, where a
+ // given plane holds each fourth pixel sequentially. So to handle an entire picture, each plane's data
+ // must be decompressed and inserted into the surface
+ for (int planeCtr = 0; planeCtr < 4; planeCtr++) {
+ byte *destP = (byte *)_vga->_page[1]->getBasePtr(x + planeCtr, y);
+
+ for (;;) {
+ uint16 v = READ_LE_UINT16(srcP);
+ srcP += 2;
+ int cmd = v >> 14;
+ int count = v & 0x3FFF;
+
+ if (cmd == 0) {
+ // End of image
+ break;
+ }
+
+ assert(destP < destEndP);
+
+ if (cmd == 2)
+ srcP++;
+ else if (cmd == 3)
+ srcP += count;
+
+ // Handle a set of pixels
+ while (count-- > 0) {
+ // Transfer operation
+ switch (cmd) {
+ case 1:
+ // SKIP
+ break;
+ case 2:
+ case 3:
+ // TINT
+ *destP = lookupTable[*destP];
+ break;
+ }
+
+ // Move to next dest position
+ destP += 4;
+ }
+ }
+ }
+}
+
+
+void Bitmap::show(int16 x, int16 y) {
+ debugC(5, kCGEDebugBitmap, "Bitmap::show(%d, %d)", x, y);
+
+ const byte *srcP = (const byte *)_v;
+ byte *destEndP = (byte *)_vga->_page[1]->pixels + (kScrWidth * kScrHeight);
+
+ // Loop through processing data for each plane. The game originally ran in plane mapped mode, where a
+ // given plane holds each fourth pixel sequentially. So to handle an entire picture, each plane's data
+ // must be decompressed and inserted into the surface
+ for (int planeCtr = 0; planeCtr < 4; planeCtr++) {
+ byte *destP = (byte *)_vga->_page[1]->getBasePtr(x + planeCtr, y);
+
+ for (;;) {
+ uint16 v = READ_LE_UINT16(srcP);
+ srcP += 2;
+ int cmd = v >> 14;
+ int count = v & 0x3FFF;
+
+ if (cmd == 0) {
+ // End of image
+ break;
+ }
+
+ assert(destP < destEndP);
+
+ // Handle a set of pixels
+ while (count-- > 0) {
+ // Transfer operation
+ switch (cmd) {
+ case 1:
+ // SKIP
+ break;
+ case 2:
+ // REPEAT
+ *destP = *srcP;
+ break;
+ case 3:
+ // COPY
+ *destP = *srcP++;
+ break;
+ }
+
+ // Move to next dest position
+ destP += 4;
+ }
+
+ if (cmd == 2)
+ srcP++;
+ }
+ }
+/*
+ DEBUG code to display image immediately
+ // Temporary
+ g_system->copyRectToScreen((const byte *)VGA::Page[1]->getBasePtr(0, 0), SCR_WID, 0, 0, SCR_WID, SCR_HIG);
+ byte palData[PAL_SIZ];
+ VGA::DAC2pal(VGA::SysPal, palData);
+ g_system->getPaletteManager()->setPalette(palData, 0, PAL_CNT);
+
+ g_system->updateScreen();
+ g_system->delayMillis(5000);
+*/
+}
+
+
+void Bitmap::hide(int16 x, int16 y) {
+ debugC(5, kCGEDebugBitmap, "Bitmap::hide(%d, %d)", x, y);
+
+ for (int yp = y; yp < y + _h; yp++) {
+ const byte *srcP = (const byte *)_vga->_page[2]->getBasePtr(x, yp);
+ byte *destP = (byte *)_vga->_page[1]->getBasePtr(x, yp);
+
+ Common::copy(srcP, srcP + _w, destP);
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+HorizLine::HorizLine(CGEEngine *vm): Sprite(vm, NULL) {
+ // Set the sprite list
+ BitmapPtr *HL = new BitmapPtr[2];
+ HL[0] = new Bitmap("HLINE");
+ HL[1] = NULL;
+
+ setShapeList(HL);
+}
+
+CavLight::CavLight(CGEEngine *vm): Sprite(vm, NULL) {
+ // Set the sprite list
+ BitmapPtr *PR = new BitmapPtr[2];
+ PR[0] = new Bitmap("PRESS");
+ PR[1] = NULL;
+
+ setShapeList(PR);
+}
+
+Spike::Spike(CGEEngine *vm): Sprite(vm, NULL) {
+ // Set the sprite list
+ BitmapPtr *SP = new BitmapPtr[3];
+ SP[0] = new Bitmap("SPK_L");
+ SP[1] = new Bitmap("SPK_R");
+ SP[2] = NULL;
+
+ setShapeList(SP);
+}
+
+PocLight::PocLight(CGEEngine *vm): Sprite(vm, NULL) {
+ // Set the sprite list
+ BitmapPtr *LI = new BitmapPtr[5];
+ LI[0] = new Bitmap("LITE0");
+ LI[1] = new Bitmap("LITE1");
+ LI[2] = new Bitmap("LITE2");
+ LI[3] = new Bitmap("LITE3");
+ LI[4] = NULL;
+
+ setShapeList(LI);
+
+ _flags._kill = false;
+}
+
+} // End of namespace CGE
diff --git a/engines/cge/vga13h.h b/engines/cge/vga13h.h
new file mode 100644
index 0000000000..3312065492
--- /dev/null
+++ b/engines/cge/vga13h.h
@@ -0,0 +1,274 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#ifndef CGE_VGA13H_H
+#define CGE_VGA13H_H
+
+#include "common/serializer.h"
+#include "graphics/surface.h"
+#include "cge/general.h"
+#include "cge/bitmap.h"
+#include "cge/snail.h"
+#include "cge/cge.h"
+
+namespace CGE {
+
+#define kFadeStep 2
+#define kVgaColDark 207
+#define kVgaColDarkGray 225 /*219*/
+#define kVgaColGray 231
+#define kVgaColLightGray 237
+#define kPixelTransp 0xFE
+#define kNoSeq (-1)
+#define kNoPtr ((uint8)-1)
+#define kSprExt ".SPR"
+#define kPalCount 256
+#define kPalSize (kPalCount * 3)
+
+
+struct Seq {
+ uint8 _now;
+ uint8 _next;
+ int8 _dx;
+ int8 _dy;
+ int _dly;
+};
+
+extern Seq _seq1[];
+extern Seq _seq2[];
+
+class SprExt {
+public:
+ int _x0;
+ int _y0;
+ int _x1;
+ int _y1;
+ BitmapPtr _b0;
+ BitmapPtr _b1;
+ BitmapPtr *_shpList;
+ Seq *_seq;
+ char *_name;
+ Snail::Com *_near;
+ Snail::Com *_take;
+ SprExt() :
+ _x0(0), _y0(0),
+ _x1(0), _y1(0),
+ _b0(NULL), _b1(NULL),
+ _shpList(NULL), _seq(NULL),
+ _name(NULL), _near(NULL), _take(NULL)
+ {}
+};
+
+class Sprite {
+protected:
+ SprExt *_ext;
+public:
+ int _ref;
+ signed char _cave;
+ struct Flags {
+ uint16 _hide : 1; // general visibility switch
+ uint16 _near : 1; // Near action lock
+ uint16 _drag : 1; // sprite is moveable
+ uint16 _hold : 1; // sprite is held with mouse
+ uint16 _____ : 1; // intrrupt driven animation
+ uint16 _slav : 1; // slave object
+ uint16 _syst : 1; // system object
+ uint16 _kill : 1; // dispose memory after remove
+ uint16 _xlat : 1; // 2nd way display: xlat table
+ uint16 _port : 1; // portable
+ uint16 _kept : 1; // kept in pocket
+ uint16 _east : 1; // talk to east (in opposite to west)
+ uint16 _shad : 1; // shadow
+ uint16 _back : 1; // 'send to background' request
+ uint16 _bDel : 1; // delete bitmaps in ~SPRITE
+ uint16 _tran : 1; // transparent (untouchable)
+ } _flags;
+ int _x;
+ int _y;
+ signed char _z;
+ uint16 _w;
+ uint16 _h;
+ uint16 _time;
+ uint8 _nearPtr;
+ uint8 _takePtr;
+ int _seqPtr;
+ int _shpCnt;
+ char _file[kMaxFile];
+ Sprite *_prev;
+ Sprite *_next;
+
+ bool works(Sprite *spr);
+ bool seqTest(int n);
+ inline bool active() {
+ return _ext != NULL;
+ }
+
+ Sprite(CGEEngine *vm, BitmapPtr *shp);
+ virtual ~Sprite();
+ BitmapPtr shp();
+ BitmapPtr *setShapeList(BitmapPtr *shp);
+ void moveShapes(uint8 *buf);
+ Sprite *expand();
+ Sprite *contract();
+ Sprite *backShow(bool fast = false);
+ void setName(char *newName);
+ inline char *name() {
+ return (_ext) ? _ext->_name : NULL;
+ }
+ void gotoxy(int x, int y);
+ void center();
+ void show();
+ void hide();
+ BitmapPtr ghost();
+ void show(uint16 pg);
+ void makeXlat(uint8 *x);
+ void killXlat();
+ void step(int nr = -1);
+ Seq *setSeq(Seq *seq);
+ Snail::Com *snList(SnList type);
+ virtual void touch(uint16 mask, int x, int y);
+ virtual void tick();
+ void sync(Common::Serializer &s);
+private:
+ CGEEngine *_vm;
+};
+
+class Queue {
+ Sprite *_head;
+ Sprite *_tail;
+public:
+ Queue(bool show);
+ ~Queue();
+
+ bool _show;
+
+ void append(Sprite *spr);
+ void insert(Sprite *spr, Sprite *nxt);
+ void insert(Sprite *spr);
+ Sprite *remove(Sprite *spr);
+ void forAll(void (*fun)(Sprite *));
+ Sprite *first() {
+ return _head;
+ }
+ Sprite *last() {
+ return _tail;
+ }
+ Sprite *locate(int ref);
+ void clear();
+};
+
+class Vga {
+ bool _setPal;
+ Dac *_oldColors;
+ Dac *_newColors;
+ const char *_msg;
+ const char *_name;
+
+ void updateColors();
+ void setColors();
+ void waitVR();
+public:
+ uint32 _frmCnt;
+ Queue *_showQ;
+ Queue *_spareQ;
+ int _mono;
+ Graphics::Surface *_page[4];
+ Dac *_sysPal;
+
+ Vga();
+ ~Vga();
+
+ void getColors(Dac *tab);
+ void setColors(Dac *tab, int lum);
+ void clear(uint8 color);
+ void copyPage(uint16 d, uint16 s);
+ void sunrise(Dac *tab);
+ void sunset();
+ void show();
+ void update();
+
+ static void palToDac(const byte *palData, Dac *tab);
+ static void dacToPal(const Dac *tab, byte *palData);
+};
+
+class HorizLine: public Sprite {
+public:
+ HorizLine(CGEEngine *vm);
+};
+
+class CavLight: public Sprite {
+public:
+ CavLight(CGEEngine *vm);
+};
+
+class Spike: public Sprite {
+public:
+ Spike(CGEEngine *vm);
+};
+
+class PocLight: public Sprite {
+public:
+ PocLight(CGEEngine *vm);
+};
+
+Dac mkDac(uint8 r, uint8 g, uint8 b);
+
+template <class CBLK>
+uint8 closest(CBLK *pal, CBLK x) {
+#define f(col, lum) ((((uint16)(col)) << 8) / lum)
+ uint16 i, dif = 0xFFFF, found = 0;
+ uint16 L = x._r + x._g + x._b;
+ if (!L)
+ L++;
+ uint16 R = f(x._r, L), G = f(x._g, L), B = f(x._b, L);
+ for (i = 0; i < 256; i++) {
+ uint16 l = pal[i]._r + pal[i]._g + pal[i]._b;
+ if (!l)
+ l++;
+ int r = f(pal[i]._r, l), g = f(pal[i]._g, l), b = f(pal[i]._b, l);
+ uint16 D = ((r > R) ? (r - R) : (R - r)) +
+ ((g > G) ? (g - G) : (G - g)) +
+ ((b > B) ? (b - B) : (B - b)) +
+ ((l > L) ? (l - L) : (L - l)) * 10 ;
+
+ if (D < dif) {
+ found = i;
+ dif = D;
+ if (D == 0)
+ break; // exact!
+ }
+ }
+ return found;
+#undef f
+}
+
+Sprite *spriteAt(int x, int y);
+Sprite *locate(int ref);
+
+} // End of namespace CGE
+
+#endif
diff --git a/engines/cge/vmenu.cpp b/engines/cge/vmenu.cpp
new file mode 100644
index 0000000000..c213b997a3
--- /dev/null
+++ b/engines/cge/vmenu.cpp
@@ -0,0 +1,142 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#include "cge/vmenu.h"
+#include "cge/events.h"
+#include "cge/cge_main.h"
+
+namespace CGE {
+
+MenuBar::MenuBar(CGEEngine *vm, uint16 w) : Talk(vm), _vm(vm) {
+ int h = kFontHigh + 2 * kMenuBarVM;
+ int i = (w += 2 * kMenuBarHM) * h;
+ uint8 *p = (uint8 *)malloc(sizeof(uint8) * i);
+
+ memset(p + w, kPixelTransp, i - 2 * w);
+ memset(p, kMenuBarLT, w);
+ memset(p + i - w, kMenuBarRB, w);
+ uint8 *p1 = p;
+ uint8 *p2 = p + i - 1;
+ for (int cpt = 0; cpt < h; cpt++) {
+ *p1 = kMenuBarLT;
+ *p2 = kMenuBarRB;
+ p1 += w;
+ p2 -= w;
+ }
+
+ _ts = new BitmapPtr[2];
+ _ts[0] = new Bitmap(w, h, p);
+ _ts[1] = NULL;
+ setShapeList(_ts);
+
+ _flags._slav = true;
+ _flags._tran = true;
+ _flags._kill = true;
+ _flags._bDel = true;
+}
+
+Vmenu *Vmenu::_addr = NULL;
+int Vmenu::_recent = -1;
+
+Vmenu::Vmenu(CGEEngine *vm, Choice *list, int x, int y)
+ : Talk(vm, VMGather(list), kTBRect), _menu(list), _bar(NULL), _vm(vm) {
+ Choice *cp;
+
+ _addr = this;
+ delete[] _vmgt;
+ _items = 0;
+ for (cp = list; cp->_text; cp++)
+ _items++;
+ _flags._bDel = true;
+ _flags._kill = true;
+ if (x < 0 || y < 0)
+ center();
+ else
+ gotoxy(x - _w / 2, y - (kTextVMargin + kFontHigh / 2));
+ _vga->_showQ->insert(this, _vga->_showQ->last());
+ _bar = new MenuBar(_vm, _w - 2 * kTextHMargin);
+ _bar->gotoxy(_x + kTextHMargin - kMenuBarHM, _y + kTextVMargin - kMenuBarVM);
+ _vga->_showQ->insert(_bar, _vga->_showQ->last());
+}
+
+Vmenu::~Vmenu() {
+ _addr = NULL;
+}
+
+#define CALL_MEMBER_FN(object,ptrToMember) ((object).*(ptrToMember))
+
+void Vmenu::touch(uint16 mask, int x, int y) {
+ if (!_items)
+ return;
+
+ Sprite::touch(mask, x, y);
+
+ y -= kTextVMargin - 1;
+ int n = 0;
+ bool ok = false;
+ uint16 h = kFontHigh + kTextLineSpace;
+
+ if (y >= 0) {
+ n = y / h;
+ if (n < _items)
+ ok = (x >= kTextHMargin && x < _w - kTextHMargin/* && y % h < FONT_HIG*/);
+ else
+ n = _items - 1;
+ }
+
+ _bar->gotoxy(_x + kTextHMargin - kMenuBarHM, _y + kTextVMargin + n * h - kMenuBarVM);
+
+ if (ok && (mask & kMouseLeftUp)) {
+ _items = 0;
+ _snail_->addCom(kSnKill, -1, 0, this);
+ _recent = n;
+ assert(_menu[n].Proc);
+ CALL_MEMBER_FN(*_vm, _menu[n].Proc)();
+ }
+}
+
+char *Vmenu::VMGather(Choice *list) {
+ Choice *cp;
+ int len = 0, h = 0;
+
+ for (cp = list; cp->_text; cp++) {
+ len += strlen(cp->_text);
+ h++;
+ }
+ _vmgt = new char[len + h];
+ if (_vmgt) {
+ *_vmgt = '\0';
+ for (cp = list; cp->_text; cp++) {
+ if (*_vmgt)
+ strcat(_vmgt, "|");
+ strcat(_vmgt, cp->_text);
+ h++;
+ }
+ }
+ return _vmgt;
+}
+} // End of namespace CGE
diff --git a/engines/cge/vmenu.h b/engines/cge/vmenu.h
new file mode 100644
index 0000000000..b8740a9e93
--- /dev/null
+++ b/engines/cge/vmenu.h
@@ -0,0 +1,74 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#ifndef CGE_VMENU_H
+#define CGE_VMENU_H
+
+#include "cge/talk.h"
+
+namespace CGE {
+
+#define kMenuBarVM 1
+#define kMenuBarHM 3
+#define kMenuBarLT kVgaColLightGray
+#define kMenuBarRB kVgaColDarkGray
+
+
+struct Choice {
+ const char *_text;
+ void (CGEEngine::*Proc)();
+};
+
+
+class MenuBar : public Talk {
+public:
+ MenuBar(CGEEngine *vm, uint16 w);
+private:
+ CGEEngine *_vm;
+};
+
+
+class Vmenu : public Talk {
+public:
+ static Vmenu *_addr;
+ static int _recent;
+ MenuBar *_bar;
+ Vmenu(CGEEngine *vm, Choice *list, int x, int y);
+ ~Vmenu();
+ virtual void touch(uint16 mask, int x, int y);
+private:
+ char *_vmgt;
+ CGEEngine *_vm;
+ uint16 _items;
+ Choice *_menu;
+
+ char *VMGather(Choice *list);
+};
+
+} // End of namespace CGE
+
+#endif
diff --git a/engines/cge/walk.cpp b/engines/cge/walk.cpp
new file mode 100644
index 0000000000..e2b9a8961f
--- /dev/null
+++ b/engines/cge/walk.cpp
@@ -0,0 +1,269 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#include "cge/walk.h"
+#include "cge/cge_main.h"
+
+namespace CGE {
+
+Walk *_hero;
+
+uint8 Cluster::_map[kMapZCnt][kMapXCnt];
+CGEEngine *Cluster::_vm;
+
+void Cluster::init(CGEEngine *vm) {
+ _vm = vm;
+}
+
+uint8 &Cluster::cell() {
+ return _map[_pt.y][_pt.x];
+}
+
+bool Cluster::isValid() const {
+ return (_pt.x >= 0) && (_pt.x < kMapXCnt) && (_pt.y >= 0) && (_pt.y < kMapZCnt);
+}
+
+Cluster XZ(int16 x, int16 y) {
+ if (y < kMapTop)
+ y = kMapTop;
+
+ if (y > kMapTop + kMapHig - kMapGridZ)
+ y = kMapTop + kMapHig - kMapGridZ;
+
+ return Cluster(x / kMapGridX, (y - kMapTop) / kMapGridZ);
+}
+
+Walk::Walk(CGEEngine *vm, BitmapPtr *shpl)
+ : Sprite(vm, shpl), _dir(kDirNone), _tracePtr(-1), _level(0), _target(-1, -1), _findLevel(-1), _vm(vm) {
+}
+
+void Walk::tick() {
+ if (_flags._hide)
+ return;
+
+ _here = XZ(_x + _w / 2, _y + _h);
+
+ if (_dir != kDirNone) {
+ _sys->funTouch();
+ for (Sprite *spr = _vga->_showQ->first(); spr; spr = spr->_next) {
+ if (distance(spr) < 2) {
+ if (!spr->_flags._near) {
+ _vm->feedSnail(spr, kNear);
+ spr->_flags._near = true;
+ }
+ } else {
+ spr->_flags._near = false;
+ }
+ }
+ }
+
+ if (_flags._hold || _tracePtr < 0) {
+ park();
+ } else {
+ if (_here._pt == _trace[_tracePtr]._pt) {
+ if (--_tracePtr < 0)
+ park();
+ } else {
+ Common::Point tmpPoint = _trace[_tracePtr]._pt - _here._pt;
+ int16 dx = tmpPoint.x;
+ int16 dz = tmpPoint.y;
+ Dir d = (dx) ? ((dx > 0) ? kDirEast : kDirWest) : ((dz > 0) ? kDirSouth : kDirNorth);
+ turn(d);
+ }
+ }
+
+ step();
+
+ if ((_dir == kDirWest && _x <= 0) ||
+ (_dir == kDirEast && _x + _w >= kScrWidth) ||
+ (_dir == kDirSouth && _y + _w >= kWorldHeight - 2)) {
+ park();
+ } else {
+ // take current Z position
+ _z = _here._pt.y;
+ _snail_->addCom(kSnZTrim, -1, 0, this); // update Hero's pos in show queue
+ }
+}
+
+int Walk::distance(Sprite *spr) {
+ int dx = spr->_x - (_x + _w - kWalkSide);
+ if (dx < 0)
+ dx = (_x + kWalkSide) - (spr->_x + spr->_w);
+
+ if (dx < 0)
+ dx = 0;
+
+ dx /= kMapGridX;
+ int dz = spr->_z - _z;
+ if (dz < 0)
+ dz = - dz;
+
+ dx = dx * dx + dz * dz;
+ for (dz = 1; dz * dz < dx; dz++)
+ ;
+
+ return dz - 1;
+}
+
+void Walk::turn(Dir d) {
+ Dir dir = (_dir == kDirNone) ? kDirSouth : _dir;
+ if (d != _dir) {
+ step((d == dir) ? (1 + dir + dir) : (9 + 4 * dir + d));
+ _dir = d;
+ }
+}
+
+void Walk::park() {
+ if (_time == 0)
+ _time++;
+
+ if (_dir != kDirNone) {
+ step(9 + 4 * _dir + _dir);
+ _dir = kDirNone;
+ _tracePtr = -1;
+ }
+}
+
+void Walk::findWay(Cluster c) {
+ if (c._pt == _here._pt)
+ return;
+
+ for (_findLevel = 1; _findLevel <= kMaxFindLevel; _findLevel++) {
+ _target = _here._pt;
+ int16 x = c._pt.x;
+ int16 z = c._pt.y;
+
+ if (find1Way(Cluster(x, z)))
+ break;
+ }
+
+ _tracePtr = (_findLevel > kMaxFindLevel) ? -1 : (_findLevel - 1);
+ if (_tracePtr < 0)
+ noWay();
+ _time = 1;
+}
+
+void Walk::findWay(Sprite *spr) {
+ if (!spr || spr == this)
+ return;
+
+ int x = spr->_x;
+ int z = spr->_z;
+ if (spr->_flags._east)
+ x += spr->_w + _w / 2 - kWalkSide;
+ else
+ x -= _w / 2 - kWalkSide;
+
+ findWay(Cluster((x / kMapGridX),
+ ((z < kMapZCnt - kDistMax) ? (z + 1)
+ : (z - 1))));
+}
+
+bool Walk::lower(Sprite *spr) {
+ return (spr->_y > _y + (_h * 3) / 5);
+}
+
+void Walk::reach(Sprite *spr, int mode) {
+ if (spr) {
+ _hero->findWay(spr);
+ if (mode < 0) {
+ mode = spr->_flags._east;
+ if (lower(spr))
+ mode += 2;
+ }
+ }
+ // note: insert SNAIL commands in reverse order
+ _snail->insCom(kSnPause, -1, 64, NULL);
+ _snail->insCom(kSnSeq, -1, kTSeq + mode, this);
+ if (spr) {
+ _snail->insCom(kSnWait, -1, -1, _hero);
+ //SNINSERT(SNWALK, -1, -1, spr);
+ }
+ // sequence is not finished,
+ // now it is just at sprite appear (disappear) point
+}
+
+void Walk::noWay() {
+ _vm->trouble(kSeqNoWay, kNoWay);
+}
+
+bool Cluster::chkBar() const {
+ assert(_vm->_now <= kCaveMax);
+ return (_pt.x == _vm->_barriers[_vm->_now]._horz) || (_pt.y == _vm->_barriers[_vm->_now]._vert);
+}
+
+bool Walk::find1Way(Cluster c) {
+ const Cluster tab[4] = { Cluster(-1, 0), Cluster(1, 0), Cluster(0, -1), Cluster(0, 1)};
+ const int tabLen = 4;
+
+ if (c._pt == _target)
+ // Found destination
+ return true;
+
+ if (_level >= _findLevel)
+ // Nesting limit
+ return false;
+
+ // Look for barriers
+ if (c.chkBar())
+ return false;
+
+ if (c.cell())
+ // Location is occupied
+ return false;
+
+ // Loop through each direction
+ Cluster start = c;
+ for (int i = 0; i < tabLen; i++) {
+ // Reset to starting position
+ c = start;
+
+ do {
+ c._pt += tab[i]._pt;
+ if (!c.isValid())
+ // Break to check next direction
+ break;
+
+ // Recursively check for further paths
+ ++_level;
+ ++start.cell();
+ bool foundPath = find1Way(c);
+ --start.cell();
+ --_level;
+
+ if (foundPath) {
+ // Set route point
+ _trace[_level] = start;
+ return true;
+ }
+ } while (!c.chkBar() && !c.cell());
+ }
+
+ return false;
+}
+
+} // End of namespace CGE
diff --git a/engines/cge/walk.h b/engines/cge/walk.h
new file mode 100644
index 0000000000..9b94120bb4
--- /dev/null
+++ b/engines/cge/walk.h
@@ -0,0 +1,95 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Soltys source code
+ * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
+ */
+
+#ifndef CGE_WALK_H
+#define CGE_WALK_H
+
+#include "common/rect.h"
+#include "cge/vga13h.h"
+#include "cge/events.h"
+
+namespace CGE {
+
+#define kMapXCnt 40
+#define kMapZCnt 20
+#define kMapArrSize (kMapZCnt * kMapXCnt)
+#define kMapTop 80
+#define kMapHig 80
+#define kMapGridX (kScrWidth / kMapXCnt)
+#define kMapGridZ (kMapHig / kMapZCnt)
+#define kMaxFindLevel 3
+
+enum Dir { kDirNone = -1, kDirNorth, kDirEast, kDirSouth, kDirWest };
+
+class Cluster {
+public:
+ static uint8 _map[kMapZCnt][kMapXCnt];
+ static CGEEngine *_vm;
+ Common::Point _pt;
+
+ static void init(CGEEngine *vm);
+public:
+ uint8 &cell();
+ Cluster(int16 a, int16 b) { _pt = Common::Point(a, b); }
+ Cluster() { _pt = Common::Point(-1, -1); }
+ bool chkBar() const;
+ bool isValid() const;
+};
+
+class Walk : public Sprite {
+private:
+ CGEEngine *_vm;
+public:
+ Cluster _here;
+ int _tracePtr;
+ int _level;
+ int _findLevel;
+ Common::Point _target;
+ Cluster _trace[kMaxFindLevel];
+
+ Dir _dir;
+ Walk(CGEEngine *vm, BitmapPtr *shpl);
+ void tick();
+ void findWay(Cluster c);
+ void findWay(Sprite *spr);
+ int distance(Sprite *spr);
+ void turn(Dir d);
+ void park();
+ bool lower(Sprite *spr);
+ void reach(Sprite *spr, int mode = -1);
+
+ void noWay();
+ bool find1Way(Cluster c);
+};
+
+Cluster XZ(int16 x, int16 y);
+
+extern Walk *_hero;
+
+} // End of namespace CGE
+
+#endif
diff --git a/engines/engines.mk b/engines/engines.mk
index 063ed75919..eddd22039d 100644
--- a/engines/engines.mk
+++ b/engines/engines.mk
@@ -26,6 +26,11 @@ DEFINES += -DENABLE_AGOS2
endif
endif
+ifdef ENABLE_CGE
+DEFINES += -DENABLE_CGE=$(ENABLE_CGE)
+MODULES += engines/cge
+endif
+
ifdef ENABLE_CINE
DEFINES += -DENABLE_CINE=$(ENABLE_CINE)
MODULES += engines/cine