aboutsummaryrefslogtreecommitdiff
path: root/engines/cge
diff options
context:
space:
mode:
Diffstat (limited to 'engines/cge')
-rw-r--r--engines/cge/bitmap.cpp392
-rw-r--r--engines/cge/bitmap.h88
-rw-r--r--engines/cge/cge.cpp220
-rw-r--r--engines/cge/cge.h339
-rw-r--r--engines/cge/cge_main.cpp1570
-rw-r--r--engines/cge/cge_main.h114
-rw-r--r--engines/cge/console.cpp34
-rw-r--r--engines/cge/console.h43
-rw-r--r--engines/cge/detection.cpp240
-rw-r--r--engines/cge/events.cpp378
-rw-r--r--engines/cge/events.h154
-rw-r--r--engines/cge/fileio.cpp240
-rw-r--r--engines/cge/fileio.h120
-rw-r--r--engines/cge/game.cpp54
-rw-r--r--engines/cge/game.h53
-rw-r--r--engines/cge/general.h43
-rw-r--r--engines/cge/module.mk30
-rw-r--r--engines/cge/snail.cpp1222
-rw-r--r--engines/cge/snail.h85
-rw-r--r--engines/cge/sound.cpp290
-rw-r--r--engines/cge/sound.h136
-rw-r--r--engines/cge/talk.cpp292
-rw-r--r--engines/cge/talk.h76
-rw-r--r--engines/cge/text.cpp205
-rw-r--r--engines/cge/text.h66
-rw-r--r--engines/cge/vga13h.cpp1009
-rw-r--r--engines/cge/vga13h.h243
-rw-r--r--engines/cge/vmenu.cpp142
-rw-r--r--engines/cge/vmenu.h73
-rw-r--r--engines/cge/walk.cpp267
-rw-r--r--engines/cge/walk.h87
31 files changed, 8305 insertions, 0 deletions
diff --git a/engines/cge/bitmap.cpp b/engines/cge/bitmap.cpp
new file mode 100644
index 0000000000..39bafc5e98
--- /dev/null
+++ b/engines/cge/bitmap.cpp
@@ -0,0 +1,392 @@
+/* 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 {
+
+Bitmap::Bitmap(CGEEngine *vm, const char *fname) : _m(NULL), _v(NULL), _map(0), _vm(vm) {
+ debugC(1, kCGEDebugBitmap, "Bitmap::Bitmap(%s)", fname);
+
+ char pat[kMaxPath];
+ forceExt(pat, fname, ".VBM");
+
+ if (_vm->_resman->exist(pat)) {
+ EncryptedStream file(_vm, pat);
+ if (file.err())
+ error("Unable to find VBM [%s]", fname);
+ if (!loadVBM(&file))
+ error("Bad VBM [%s]", fname);
+ } else {
+ error("Bad VBM [%s]", fname);
+ }
+}
+
+Bitmap::Bitmap(CGEEngine *vm, uint16 w, uint16 h, uint8 *map) : _w(w), _h(h), _m(map), _v(NULL), _map(0), _vm(vm) {
+ 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(CGEEngine *vm, uint16 w, uint16 h, uint8 fill)
+ : _w((w + 3) & ~3), // only full uint32 allowed!
+ _h(h), _m(NULL), _map(0), _vm(vm) {
+ 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(CGEEngine *vm, const Bitmap &bmp) : _w(bmp._w), _h(bmp._h), _m(NULL), _v(NULL), _map(0), _vm(vm) {
+ 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;
+}
+
+char *Bitmap::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;
+}
+
+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(EncryptedStream *f) {
+ debugC(5, kCGEDebugBitmap, "Bitmap::loadVBM(f)");
+
+ uint16 p = 0, n = 0;
+ if (!f->err())
+ f->read((uint8 *)&p, sizeof(p));
+ p = FROM_LE_16(p);
+
+ if (!f->err())
+ f->read((uint8 *)&n, sizeof(n));
+ n = FROM_LE_16(n);
+
+ if (!f->err())
+ f->read((uint8 *)&_w, sizeof(_w));
+ _w = FROM_LE_16(_w);
+
+ if (!f->err())
+ f->read((uint8 *)&_h, sizeof(_h));
+ _h = FROM_LE_16(_h);
+
+ if (!f->err()) {
+ if (p) {
+ if (_vm->_bitmapPalette) {
+ // Read in the palette
+ byte palData[kPalSize];
+ f->read(palData, kPalSize);
+
+ const byte *srcP = palData;
+ for (int idx = 0; idx < kPalCount; idx++, srcP += 3) {
+ _vm->_bitmapPalette[idx]._r = *srcP;
+ _vm->_bitmapPalette[idx]._g = *(srcP + 1);
+ _vm->_bitmapPalette[idx]._b = *(srcP + 2);
+ }
+ } else
+ f->seek(f->pos() + kPalSize);
+ }
+ }
+ if ((_v = new uint8[n]) == NULL)
+ return false;
+
+ if (!f->err())
+ f->read(_v, n);
+
+ _b = (HideDesc *)(_v + n - _h * sizeof(HideDesc));
+ return (!f->err());
+}
+
+} // End of namespace CGE
diff --git a/engines/cge/bitmap.h b/engines/cge/bitmap.h
new file mode 100644
index 0000000000..aa6282705c
--- /dev/null
+++ b/engines/cge/bitmap.h
@@ -0,0 +1,88 @@
+/* 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/general.h"
+#include "common/file.h"
+
+namespace CGE {
+
+class CGEEngine;
+class EncryptedStream;
+
+#define kMaxPath 128
+enum {
+ kBmpEOI = 0x0000,
+ kBmpSKP = 0x4000,
+ kBmpREP = 0x8000,
+ kBmpCPY = 0xC000
+};
+
+#include "common/pack-start.h"
+
+struct HideDesc {
+ uint16 _skip;
+ uint16 _hide;
+};
+
+#include "common/pack-end.h"
+
+class Bitmap {
+ CGEEngine *_vm;
+ char *forceExt(char *buf, const char *name, const char *ext);
+ bool loadVBM(EncryptedStream *f);
+public:
+ uint16 _w;
+ uint16 _h;
+ uint8 *_m;
+ uint8 *_v;
+ int32 _map;
+ HideDesc *_b;
+
+ Bitmap(CGEEngine *vm, const char *fname);
+ Bitmap(CGEEngine *vm, uint16 w, uint16 h, uint8 *map);
+ Bitmap(CGEEngine *vm, uint16 w, uint16 h, uint8 fill);
+ Bitmap(CGEEngine *vm, const Bitmap &bmp);
+ ~Bitmap();
+
+ 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..4ed2932cd9
--- /dev/null
+++ b/engines/cge/cge.cpp
@@ -0,0 +1,220 @@
+/* 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::_maxSceneArr[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;
+ _bitmapPalette = NULL;
+
+
+
+}
+
+void CGEEngine::initSceneValues() {
+ for (int i = 0; i < kSceneMax; i++) {
+ _heroXY[i].x = 0;
+ _heroXY[i].y = 0;
+ }
+
+ for (int i = 0; i < kSceneMax + 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;
+ _miniScene = NULL;
+ _miniShp = NULL;
+ _miniShpList = NULL;
+ _sprite = NULL;
+ _resman = new ResourceManager();
+
+ // Create debugger console
+ _console = new CGEConsole(this);
+
+ // Initialise engine objects
+ _font = new Font(this, "CGE");
+ _text = new Text(this, "CGE");
+ _talk = NULL;
+ _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);
+ _sceneLight = new SceneLight(this);
+ _debugLine = new InfoLine(this, kScrWidth);
+ _commandHandler = new CommandHandler(this, false);
+ _commandHandlerTurbo = new CommandHandler(this, true);
+ _midiPlayer = new MusicPlayer(this);
+ _mouse = new Mouse(this);
+ _keyboard = new Keyboard(this);
+ _eventManager = new EventManager(this);
+ _fx = new Fx(this, 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;
+
+ initSceneValues();
+
+ _maxScene = 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() {
+ // Remove all of our debug levels here
+ DebugMan.clearAllDebugChannels();
+
+ delete _console;
+ _midiPlayer->killMidi();
+
+ // Delete engine objects
+ delete _vga;
+ delete _sys;
+ delete _sprite;
+ delete _miniScene;
+ delete _shadow;
+ delete _horzLine;
+ delete _infoLine;
+ delete _sceneLight;
+ delete _debugLine;
+ delete _text;
+ delete _pocLight;
+ delete _keyboard;
+ delete _mouse;
+ delete _eventManager;
+ delete _fx;
+ delete _sound;
+ delete _font;
+ delete _commandHandler;
+ delete _commandHandlerTurbo;
+ delete _hero;
+ delete _resman;
+
+ 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..2ce154a4fb
--- /dev/null
+++ b/engines/cge/cge.h
@@ -0,0 +1,339 @@
+/* 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 "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;
+class Cluster;
+class Vga;
+class System;
+class Keyboard;
+class Mouse;
+class HorizLine;
+class InfoLine;
+class SceneLight;
+class CommandHandler;
+class EventManager;
+class ResourceManager;
+class Walk;
+class Text;
+class Talk;
+
+#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 kSceneDx 9
+#define kSceneDy 10
+#define kSceneNx 8
+#define kSceneNy 3
+#define kSceneMax kSceneNx * kSceneNy
+#define kPathMax 128
+#define kCryptSeed 0xA5
+#define kMaxFile 128
+#define kMapXCnt 40
+#define kMapZCnt 20
+
+// our engine debug channels
+enum {
+ kCGEDebugBitmap = 1 << 0,
+ kCGEDebugFile = 1 << 1,
+ kCGEDebugEngine = 1 << 2
+};
+
+enum SnList {
+ kNear, kTake
+};
+
+enum CallbackType {
+ kNullCB = 0, kQGame, kMiniStep, kXScene, kSoundSetVolume
+};
+
+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 Font {
+ char _path[kPathMax];
+ void load();
+ CGEEngine *_vm;
+public:
+ uint8 *_widthArr;
+ uint16 *_pos;
+ uint8 *_map;
+ Font(CGEEngine *vm, const char *name);
+ ~Font();
+ uint16 width(const char *text);
+ void save();
+};
+
+class CGEEngine : public Engine {
+private:
+ uint32 _lastFrame, _lastTick;
+ void tick();
+ void syncHeader(Common::Serializer &s);
+ void writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader &header);
+ void syncGame(Common::SeekableReadStream *readStream, Common::WriteStream *writeStream, bool tiny);
+ 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 _maxSceneArr[5];
+
+ const ADGameDescription *_gameDescription;
+ int _startupMode;
+ int _demoText;
+ int _oldLev;
+ int _pocPtr;
+ bool _music;
+ int _pocref[kPocketNX];
+ uint8 _volume[2];
+ int _maxScene;
+ bool _flag[4];
+ bool _dark;
+ bool _game;
+ bool _finis;
+ int _now;
+ int _lev;
+ int _mode;
+ int _soundOk;
+ int _gameCase2Cpt;
+ int _offUseCount;
+ Dac *_bitmapPalette;
+ uint8 _clusterMap[kMapZCnt][kMapXCnt];
+
+ Sprite *_sprTv;
+ Sprite *_sprK1;
+ Sprite *_sprK2;
+ Sprite *_sprK3;
+
+ Common::Point _heroXY[kSceneMax];
+ Bar _barriers[kSceneMax + 1];
+ Font *_font;
+ Vga *_vga;
+ System *_sys;
+ Sprite *_pocLight;
+ Keyboard *_keyboard;
+ Mouse *_mouse;
+ Sprite *_sprite;
+ Sprite *_miniScene;
+ Sprite *_shadow;
+ HorizLine *_horzLine;
+ InfoLine *_infoLine;
+ InfoLine *_debugLine;
+ SceneLight *_sceneLight;
+ CommandHandler *_commandHandler;
+ CommandHandler *_commandHandlerTurbo;
+ EventManager *_eventManager;
+ Fx *_fx;
+ Sound *_sound;
+ ResourceManager *_resman;
+ Sprite *_pocket[kPocketNX];
+ Walk *_hero;
+ Text *_text;
+ Talk *_talk;
+
+
+ Common::RandomSource _randomSource;
+ MusicPlayer *_midiPlayer;
+ BitmapPtr *_miniShp;
+ BitmapPtr *_miniShpList;
+ int _startGameSlot;
+
+ virtual Common::Error run();
+ GUI::Debugger *getDebugger() {
+ return _console;
+ }
+
+ void cge_main();
+ void switchScene(int newScene);
+ 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 scene, 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 sceneDown();
+ void sceneUp();
+ void xScene();
+ 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 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 initSceneValues();
+ char *mergeExt(char *buf, const char *name, const char *ext);
+ int takeEnum(const char **tab, const char *text);
+ int newRandom(int range);
+ void sndSetVolume();
+ Sprite *locate(int ref);
+ Sprite *spriteAt(int x, int y);
+ Cluster XZ(int16 x, int16 y);
+ void killText();
+
+ void snBackPt(Sprite *spr, int stp);
+ void snHBarrier(const int scene, const int barX);
+ void snVBarrier(const int scene, 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 scene, int x0);
+ void snSetXY(Sprite *spr, uint16 xy);
+ void snSetY(Sprite *spr, int y);
+ void snSetY0(int scene, 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..51cf3bb621
--- /dev/null
+++ b/engines/cge/cge_main.cpp
@@ -0,0 +1,1570 @@
+/* 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/vga13h.h"
+#include "cge/cge.h"
+#include "cge/cge_main.h"
+#include "cge/general.h"
+#include "cge/sound.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/walk.h"
+#include "cge/sound.h"
+
+namespace CGE {
+
+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
+};
+
+char *CGEEngine::mergeExt(char *buf, const char *name, const char *ext) {
+ strcpy(buf, name);
+ char *dot = strrchr(buf, '.');
+ if (!dot)
+ strcat(buf, ext);
+
+ return buf;
+}
+
+int CGEEngine::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 CGEEngine::newRandom(int range) {
+ if (!range)
+ return 0;
+
+ return _randomSource.getRandomNumber(range - 1);
+}
+
+void CGEEngine::sndSetVolume() {
+ // USeless for ScummVM
+}
+
+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 scene values
+ initSceneValues();
+ }
+
+ for (i = 0; i < kSceneMax; i++) {
+ s.syncAsSint16LE(_heroXY[i].x);
+ s.syncAsUint16LE(_heroXY[i].y);
+ }
+ for (i = 0; i < 1 + kSceneMax; 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(this, 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
+ sceneDown();
+ resetGame();
+
+ // Load the game
+ loadGame(slot, NULL);
+ _commandHandler->addCommand(kCmdLevel, -1, _oldLev, &_sceneLight);
+ _sceneLight->gotoxy(kSceneX + ((_now - 1) % kSceneNx) * kSceneDx + kSceneSX,
+ kSceneY + ((_now - 1) / kSceneNx) * kSceneDy + kSceneSY);
+ sceneUp();
+
+ return Common::kNoError;
+}
+
+void CGEEngine::resetGame() {
+ _vga->_spareQ->clear();
+}
+
+Common::Error CGEEngine::saveGameState(int slot, const Common::String &desc) {
+ sceneDown();
+ _oldLev = _lev;
+
+ // Write out the user's progress
+ saveGame(slot, desc);
+
+ // Reload the scene
+ sceneUp();
+
+ return Common::kNoError;
+}
+
+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);
+
+ _commandHandler->addCommand(kCmdCover, 1, cvr, NULL);
+}
+
+void CGEEngine::trouble(int seq, int text) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::trouble(%d, %d)", seq, text);
+
+ _hero->park();
+ _commandHandler->addCommand(kCmdWait, -1, -1, _hero);
+ _commandHandler->addCommand(kCmdSeq, -1, seq, _hero);
+ _commandHandler->addCommand(kCmdSound, -1, 2, _hero);
+ _commandHandler->addCommand(kCmdWait, -1, -1, _hero);
+ _commandHandler->addCommand(kCmdSay, 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()");
+
+ EncryptedStream cf(this, "CGE.HXY");
+ uint16 x, y;
+
+ memset(_heroXY, 0, sizeof(_heroXY));
+ if (!cf.err()) {
+ for (int i = 0; i < kSceneMax; ++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 <= kSceneMax) {
+ EncryptedStream cf(this, "CGE.TAB");
+ if (!cf.err()) {
+ // 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(&_clusterMap[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(_vm, "BRICK");
+ MB[1] = NULL;
+ setShapeList(MB);
+}
+
+void Square::touch(uint16 mask, int x, int y) {
+ Sprite::touch(mask, x, y);
+ if (mask & kMouseLeftUp) {
+ _vm->XZ(_x + x, _y + y).cell() = 0;
+ _vm->_commandHandlerTurbo->addCommand(kCmdKill, -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);
+ _clusterMap[z][x] = 1;
+ s->setName(n);
+ _vga->_showQ->insert(s, _vga->_showQ->first());
+ }
+}
+
+void CGEEngine::keyClick() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::keyClick()");
+
+ _commandHandlerTurbo->addCommand(kCmdSound, -1, 5, NULL);
+}
+
+void CGEEngine::resetQSwitch() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::resetQSwitch()");
+
+ _commandHandlerTurbo->addCommand(kCmdSeq, 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 (_commandHandler->idle() && !_hero->_flags._hide) {
+ if (Vmenu::_addr) {
+ _commandHandlerTurbo->addCommand(kCmdKill, -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));
+ _commandHandlerTurbo->addCommand(kCmdSeq, 123, 1, NULL);
+ keyClick();
+ }
+ }
+}
+
+void CGEEngine::miniStep(int stp) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::miniStep(%d)", stp);
+
+ if (stp < 0) {
+ _miniScene->_flags._hide = true;
+ } else {
+ *_miniShp[0] = *_miniShpList[stp];
+ _miniScene->_flags._hide = false;
+ }
+}
+
+void CGEEngine::postMiniStep(int step) {
+ debugC(6, kCGEDebugEngine, "CGEEngine::postMiniStep(%d)", step);
+
+ if (_miniScene && step != _recentStep)
+ _commandHandlerTurbo->addCallback(kCmdExec, -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;
+
+ _bitmapPalette = _vga->_sysPal;
+ spr->expand();
+ _bitmapPalette = NULL;
+ spr->show(2);
+ _vga->copyPage(1, 2);
+ _sys->setPal();
+ spr->contract();
+}
+
+void CGEEngine::sceneUp() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::sceneUp()");
+
+ 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->_scene == _now || spr->_scene == 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(_vga->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::sceneDown() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::sceneDown()");
+
+ if (_horzLine && !_horzLine->_flags._hide)
+ switchMapping();
+
+ for (Sprite *spr = _vga->_showQ->first(); spr;) {
+ Sprite *n = spr->_next;
+ if (spr->_ref >= 1000 /*&& spr->_scene*/) {
+ if (spr->_ref % 1000 == 999)
+ feedSnail(spr, kTake);
+ _vga->_spareQ->append(_vga->_showQ->remove(spr));
+ }
+ spr = n;
+ }
+}
+
+void CGEEngine::xScene() {
+ debugC(6, kCGEDebugEngine, "CGEEngine::xScene()");
+
+ sceneDown();
+ sceneUp();
+}
+
+void CGEEngine::qGame() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::qGame()");
+
+ sceneDown();
+ _oldLev = _lev;
+
+ // Write out the user's progress
+ saveGame(0, Common::String("Automatic Savegame"));
+
+ _vga->sunset();
+ _finis = true;
+}
+
+void CGEEngine::switchScene(int newScene) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::switchScene(%d)", newScene);
+
+ if (newScene == _now)
+ return;
+
+ if (newScene < 0) {
+ _commandHandler->addCommand(kCmdLabel, -1, 0, NULL); // wait for repaint
+ _commandHandler->addCallback(kCmdExec, -1, 0, kQGame); // quit game
+ } else {
+ _now = newScene;
+ _mouse->off();
+ if (_hero) {
+ _hero->park();
+ _hero->step(0);
+ _vga->_spareQ->_show = 0;
+ }
+ _sceneLight->gotoxy(kSceneX + ((_now - 1) % kSceneNx) * kSceneDx + kSceneSX,
+ kSceneY + ((_now - 1) / kSceneNx) * kSceneDy + kSceneSY);
+ killText();
+ if (!_startupMode)
+ keyClick();
+ _commandHandler->addCommand(kCmdLabel, -1, 0, NULL); // wait for repaint
+ _commandHandler->addCallback(kCmdExec, 0, 0, kXScene); // switch scene
+ }
+}
+
+System::System(CGEEngine *vm) : Sprite(vm, NULL), _vm(vm) {
+ _funDel = kHeroFun0;
+ setPal();
+ tick();
+}
+
+void System::setPal() {
+ Dac *p = _vm->_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 (_vm->_talk == NULL || n > _funDel)
+ _funDel = n;
+}
+
+void System::touch(uint16 mask, int x, int y) {
+ funTouch();
+
+ if (mask & kEventKeyb) {
+ _vm->keyClick();
+ _vm->killText();
+ if (_vm->_startupMode == 1) {
+ _vm->_commandHandler->addCommand(kCmdClear, -1, 0, NULL);
+ return;
+ }
+ switch (x) {
+ case 'X':
+ if (_vm->_keyboard->_key[kKeyAlt])
+ _vm->quit();
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ if (_vm->_keyboard->_key[kKeyAlt]) {
+ _vm->_commandHandler->addCommand(kCmdLevel, -1, x - '0', NULL);
+ break;
+ }
+ break;
+ }
+ } else {
+ if (_vm->_startupMode)
+ return;
+ int selectedScene = 0;
+ _vm->_infoLine->update(NULL);
+ if (y >= kWorldHeight ) {
+ if (x < kButtonX) { // select scene?
+ if (y >= kSceneY && y < kSceneY + kSceneNy * kSceneDy &&
+ x >= kSceneX && x < kSceneX + kSceneNx * kSceneDx && !_vm->_game) {
+ selectedScene = ((y - kSceneY) / kSceneDy) * kSceneNx + (x - kSceneX) / kSceneDx + 1;
+ if (selectedScene > _vm->_maxScene)
+ selectedScene = 0;
+ } else {
+ selectedScene = 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(selectedScene - 1);
+
+ if (mask & kMouseLeftUp) {
+ if (selectedScene && _vm->_commandHandler->idle() && _vm->_hero->_tracePtr < 0)
+ _vm->switchScene(selectedScene);
+
+ if (_vm->_horzLine && !_vm->_horzLine->_flags._hide) {
+ if (y >= kMapTop && y < kMapTop + kMapHig) {
+ Cluster tmpCluster = _vm->XZ(x, y);
+ int16 x1 = tmpCluster._pt.x;
+ int16 z1 = tmpCluster._pt.y;
+ _vm->_clusterMap[z1][x1] = 1;
+ _vm->setMapBrick(x1, z1);
+ }
+ } else {
+ if (!_vm->_talk && _vm->_commandHandler->idle() && _vm->_hero
+ && y >= kMapTop && y < kMapTop + kMapHig && !_vm->_game) {
+ _vm->_hero->findWay(_vm->XZ(x, y));
+ }
+ }
+ }
+ }
+}
+
+void System::tick() {
+ if (!_vm->_startupMode)
+ if (--_funDel == 0) {
+ _vm->killText();
+ if (_vm->_commandHandler->idle()) {
+ if (_vm->_flag[0]) // Pain flag
+ _vm->heroCover(9);
+ else { // CHECKME: Before, was: if (Startup::_core >= CORE_MID) {
+ int n = _vm->newRandom(100);
+ if (n > 96)
+ _vm->heroCover(6 + (_vm->_hero->_x + _vm->_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()");
+
+ _commandHandlerTurbo->addCommand(kCmdSeq, 121, _vga->_mono = !_vga->_mono, NULL);
+ keyClick();
+ _vga->setColors(_vga->_sysPal, 64);
+}
+
+void CGEEngine::switchMusic() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::switchMusic()");
+
+ _commandHandlerTurbo->addCommand(kCmdSeq, 122, (_music = !_music), NULL);
+ keyClick();
+
+ if (_music)
+ _midiPlayer->loadMidi(_now);
+ else
+ _midiPlayer->killMidi();
+}
+
+void CGEEngine::startCountDown() {
+ debugC(1, kCGEDebugEngine, "CGEEngine::startCountDown()");
+
+ switchScene(-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 (_clusterMap[i][j])
+ setMapBrick(j, i);
+ }
+ }
+ } else {
+ for (Sprite *s = _vga->_showQ->first(); s; s = s->_next)
+ if (s->_w == kMapGridX && s->_h == kMapGridZ)
+ _commandHandlerTurbo->addCommand(kCmdKill, -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;
+ _commandHandlerTurbo->addCommand(kCmdKill, -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) {
+ _vm->_sys->funTouch();
+
+ if ((mask & kEventAttn) != 0)
+ return;
+
+ _vm->_infoLine->update(name());
+
+ if (mask & (kMouseRightDown | kMouseLeftDown))
+ _vm->_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) && _vm->_commandHandler->idle()) {
+ Sprite *ps = (_vm->_pocLight->_seqPtr) ? _vm->_pocket[_vm->_pocPtr] : NULL;
+ if (ps) {
+ if (_flags._kept || _vm->_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 (_vm->_hero->distance(this) < kDistMax) {
+ if (_flags._port) {
+ if (_vm->findPocket(NULL) < 0) {
+ _vm->pocFul();
+ } else {
+ _vm->_commandHandler->addCommand(kCmdReach, -1, -1, this);
+ _vm->_commandHandler->addCommand(kCmdKeep, -1, -1, this);
+ _flags._port = false;
+ }
+ } else {
+ if (_takePtr != kNoPtr) {
+ if (snList(kTake)[_takePtr]._commandType == kCmdNext)
+ _vm->offUse();
+ else
+ _vm->feedSnail(this, kTake);
+ } else {
+ _vm->offUse();
+ }
+ }
+ } else {
+ _vm->tooFar();
+ }
+ }
+ }
+ }
+
+ if ((mask & kMouseLeftUp) && _vm->_commandHandler->idle()) {
+ if (_flags._kept) {
+ for (int n = 0; n < kPocketNX; n++) {
+ if (_vm->_pocket[n] == this) {
+ _vm->selectPocket(n);
+ break;
+ }
+ }
+ } else {
+ _vm->_commandHandler->addCommand(kCmdWalk, -1, -1, this); // Hero->FindWay(this);
+ }
+ }
+}
+
+void CGEEngine::loadSprite(const char *fname, int ref, int scene, 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 tmpStr[kLineMax + 1];
+ Common::String line;
+ mergeExt(tmpStr, fname, kSprExt);
+
+ if (_resman->exist(tmpStr)) { // sprite description file exist
+ EncryptedStream sprf(this, tmpStr);
+ if (sprf.err())
+ error("Bad SPR [%s]", tmpStr);
+
+ uint16 len;
+ for (line = sprf.readLine(); !sprf.eos(); line = sprf.readLine()) {
+ len = line.size();
+ lcnt++;
+ strcpy(tmpStr, line.c_str());
+ if (len == 0 || *tmpStr == '.')
+ continue;
+
+ if ((i = takeEnum(Comd, strtok(tmpStr, " =\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);
+ }
+ 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
+ case 4: // LISSAJOUS
+ error("Bad type [%s]", fname);
+ break;
+ case 5:
+ { // FLY
+ Fly *f = new Fly(this, NULL);
+ _sprite = f;
+ break;
+ }
+ default:
+ // DEAD
+ _sprite = new Sprite(this, NULL);
+ if (_sprite)
+ _sprite->gotoxy(col, row);
+ break;
+ }
+
+ if (_sprite) {
+ _sprite->_ref = ref;
+ _sprite->_scene = scene;
+ _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) {
+ EncryptedStream scrf(this, fname);
+
+ if (scrf.err())
+ return;
+
+ bool ok = true;
+ int lcnt = 0;
+
+ char tmpStr[kLineMax+1];
+ Common::String line;
+
+ for (line = scrf.readLine(); !scrf.eos(); line = scrf.readLine()) {
+ char *p;
+
+ lcnt++;
+ strcpy(tmpStr, line.c_str());
+ if ((line.size() == 0) || (*tmpStr == '.'))
+ continue;
+
+ ok = false; // not OK if break
+
+ // sprite ident number
+ if ((p = strtok(tmpStr, " \t\n")) == NULL)
+ break;
+ int SpI = atoi(p);
+
+ // sprite file name
+ char *SpN;
+ if ((SpN = strtok(NULL, " ,;/\t\n")) == NULL)
+ break;
+
+ // sprite scene
+ 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);
+}
+
+Sprite *CGEEngine::locate(int ref) {
+ Sprite *spr = _vga->_showQ->locate(ref);
+ return (spr) ? spr : _vga->_spareQ->locate(ref);
+}
+
+Sprite *CGEEngine::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;
+}
+
+Cluster CGEEngine::XZ(int16 x, int16 y) {
+ if (y < kMapTop)
+ y = kMapTop;
+
+ if (y > kMapTop + kMapHig - kMapGridZ)
+ y = kMapTop + kMapHig - kMapGridZ;
+
+ return Cluster(this, x / kMapGridX, (y - kMapTop) / kMapGridZ);
+}
+
+void CGEEngine::killText() {
+ if (!_talk)
+ return;
+
+ _commandHandlerTurbo->addCommand(kCmdKill, -1, 0, _talk);
+ _talk = NULL;
+}
+
+void CGEEngine::mainLoop() {
+ _vga->show();
+ _commandHandlerTurbo->runCommand();
+ _commandHandler->runCommand();
+
+ // 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();
+
+ _sceneLight->_flags._tran = true;
+ _vga->_showQ->append(_sceneLight);
+ _sceneLight->_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)
+ _commandHandlerTurbo->addCommand(kCmdSeq, -1, _vga->_mono, _sprite);
+ if ((_sprite = _vga->_spareQ->locate(122)) != NULL)
+ _sprite->step(_music);
+ _commandHandlerTurbo->addCommand(kCmdSeq, -1, _music, _sprite);
+ if (!_music)
+ _midiPlayer->killMidi();
+
+ if (_resman->exist("MINI.SPR")) {
+ _miniShp = new BitmapPtr[2];
+ _miniShp[0] = _miniShp[1] = NULL;
+
+ loadSprite("MINI", -1, 0, kMiniX, kMiniY);
+ expandSprite(_miniScene = _sprite); // NULL is ok
+ if (_miniScene) {
+ _miniScene->_flags._kill = false;
+ _miniScene->_flags._hide = true;
+ _miniShp[0] = new Bitmap(this, *_miniScene->shp());
+ _miniShpList = _miniScene->setShapeList(_miniShp);
+ postMiniStep(-1);
+ }
+ }
+
+ if (_hero) {
+ expandSprite(_hero);
+ _hero->gotoxy(_heroXY[_now - 1].x, _heroXY[_now - 1].y);
+ if (_resman->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;
+
+ _commandHandler->addCommand(kCmdLevel, -1, _oldLev, &_sceneLight);
+ _sceneLight->gotoxy(kSceneX + ((_now - 1) % kSceneNx) * kSceneDx + kSceneSX,
+ kSceneY + ((_now - 1) / kSceneNx) * kSceneDy + kSceneSY);
+ sceneUp();
+
+ _keyboard->setClient(_sys);
+ // main loop
+ while (!_finis && !_eventManager->_quitFlag) {
+ if (_flag[3])
+ _commandHandler->addCallback(kCmdExec, -1, 0, kQGame);
+ mainLoop();
+ }
+
+ // If finishing game due to closing ScummVM window, explicitly save the game
+ if (!_finis && canSaveGameStateCurrently())
+ qGame();
+
+ _keyboard->setClient(NULL);
+ _commandHandler->addCommand(kCmdClear, -1, 0, NULL);
+ _commandHandlerTurbo->addCommand(kCmdClear, -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 (_resman->exist(fn)) {
+ loadScript(fn);
+ expandSprite(_vga->_spareQ->locate(999));
+ feedSnail(_vga->_showQ->locate(999), kTake);
+ _vga->_showQ->append(_mouse);
+ _keyboard->setClient(_sys);
+ while (!_commandHandler->idle() && !_eventManager->_quitFlag)
+ mainLoop();
+
+ _keyboard->setClient(NULL);
+ _commandHandler->addCommand(kCmdClear, -1, 0, NULL);
+ _commandHandlerTurbo->addCommand(kCmdClear, -1, 0, NULL);
+ _vga->_showQ->clear();
+ _vga->_spareQ->clear();
+ }
+}
+
+bool CGEEngine::showTitle(const char *name) {
+ if (_eventManager->_quitFlag)
+ return false;
+
+ _bitmapPalette = _vga->_sysPal;
+ BitmapPtr *LB = new BitmapPtr[2];
+ LB[0] = new Bitmap(this, name);
+ LB[1] = NULL;
+ _bitmapPalette = 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 (; !_commandHandler->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 (!_resman->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..bdb3121d63
--- /dev/null
+++ b/engines/cge/cge_main.h
@@ -0,0 +1,114 @@
+/* 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/events.h"
+
+namespace CGE {
+
+#define kSceneX 4
+#define kSceneY 166
+#define kSceneSX 0
+#define kSceneSY 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
+#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 EncryptedStream
+#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;
+};
+
+} // 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..8b90bd1483
--- /dev/null
+++ b/engines/cge/detection.cpp
@@ -0,0 +1,240 @@
+/* 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);
+
+ if (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;
+ }
+ }
+
+ return SaveStateDescriptor();
+}
+
+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..cc22d9075a
--- /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 = _vm->_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(_vm, "MOUSE");
+ MC[1] = new Bitmap(_vm, "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 = _vm->_eventManager->getNextEvent();
+ evt._x = event.mouse.x;
+ evt._y = event.mouse.y;
+ evt._spritePtr = _vm->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(CGEEngine *vm) : _vm(vm){
+ _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
+ _vm->_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
+ _vm->_mouse->newMouse(_event);
+ handleEvents();
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void EventManager::handleEvents() {
+ while (_eventQueueTail != _eventQueueHead) {
+ CGEEvent e = _eventQueue[_eventQueueTail];
+ if (e._mask) {
+ if (_vm->_mouse->_hold && e._spritePtr != _vm->_mouse->_hold)
+ _vm->_mouse->_hold->touch(e._mask | kEventAttn, e._x - _vm->_mouse->_hold->_x, e._y - _vm->_mouse->_hold->_y);
+
+ // update mouse cursor position
+ if (e._mask & kMouseRoll)
+ _vm->_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 (_vm->_sys)
+ _vm->_sys->touch(e._mask, e._x, e._y);
+
+ if (e._mask & kMouseLeftDown) {
+ _vm->_mouse->_hold = e._spritePtr;
+ if (_vm->_mouse->_hold) {
+ _vm->_mouse->_hold->_flags._hold = true;
+
+ if (_vm->_mouse->_hold->_flags._drag) {
+ _vm->_mouse->_hx = e._x - _vm->_mouse->_hold->_x;
+ _vm->_mouse->_hy = e._y - _vm->_mouse->_hold->_y;
+ }
+ }
+ }
+
+ if (e._mask & kMouseLeftUp) {
+ if (_vm->_mouse->_hold) {
+ _vm->_mouse->_hold->_flags._hold = false;
+ _vm->_mouse->_hold = NULL;
+ }
+ }
+ ///Touched = e.Ptr;
+
+ // discard Text if button released
+ if (e._mask & (kMouseLeftUp | kMouseRightUp))
+ _vm->killText();
+ }
+ _eventQueueTail = (_eventQueueTail + 1) % kEventMax;
+ }
+ if (_vm->_mouse->_hold) {
+ if (_vm->_mouse->_hold->_flags._drag)
+ _vm->_mouse->_hold->gotoxy(_vm->_mouse->_x - _vm->_mouse->_hx, _vm->_mouse->_y - _vm->_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..a4cdfed793
--- /dev/null
+++ b/engines/cge/events.h
@@ -0,0 +1,154 @@
+/* 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 -----------------*/
+
+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:
+ CGEEngine *_vm;
+ Common::Event _event;
+ CGEEvent _eventQueue[kEventMax];
+ uint16 _eventQueueHead;
+ uint16 _eventQueueTail;
+
+ void handleEvents();
+public:
+ bool _quitFlag;
+
+ EventManager(CGEEngine *vm);
+ 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..6db0818287
--- /dev/null
+++ b/engines/cge/fileio.cpp
@@ -0,0 +1,240 @@
+/* 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 {
+
+/*-----------------------------------------------------------------------
+ * BtPage
+ *-----------------------------------------------------------------------*/
+void BtPage::readBTree(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]._pos = 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();
+ }
+ }
+}
+
+/*-----------------------------------------------------------------------
+ * ResourceManager
+ *-----------------------------------------------------------------------*/
+ResourceManager::ResourceManager() {
+ debugC(1, kCGEDebugFile, "ResourceManager::ResourceManager()");
+
+ _datFile = new Common::File();
+ _datFile->open(kDatName);
+
+ _catFile = new Common::File();
+ _catFile->open(kCatName);
+
+ if ((!_datFile) || (!_catFile))
+ error("Unable to open data files");
+
+ for (int i = 0; i < kBtLevel; i++) {
+ _buff[i]._page = new BtPage;
+ _buff[i]._pageNo = kBtValNone;
+ _buff[i]._index = -1;
+ assert(_buff[i]._page != NULL);
+ }
+}
+
+ResourceManager::~ResourceManager() {
+ debugC(1, kCGEDebugFile, "ResourceManager::~ResourceManager()");
+ _datFile->close();
+ delete _datFile;
+
+ _catFile->close();
+ delete _catFile;
+
+ for (int i = 0; i < kBtLevel; i++)
+ delete _buff[i]._page;
+}
+
+uint16 ResourceManager::XCrypt(void *buf, uint16 length) {
+ byte *b = static_cast<byte *>(buf);
+
+ for (uint16 i = 0; i < length; i++)
+ *b++ ^= kCryptSeed;
+
+ return kCryptSeed;
+}
+
+bool ResourceManager::seek(int32 offs, int whence) {
+ return _datFile->seek(offs, whence);
+}
+
+uint16 ResourceManager::read(void *buf, uint16 length) {
+ if (!_datFile->isOpen())
+ return 0;
+
+ uint16 bytesRead = _datFile->read(buf, length);
+ if (!bytesRead)
+ error("Read %s - %d bytes", _datFile->getName(), length);
+ XCrypt(buf, length);
+ return bytesRead;
+}
+
+BtPage *ResourceManager::getPage(int level, uint16 pageId) {
+ debugC(1, kCGEDebugFile, "IoHand::getPage(%d, %d)", level, pageId);
+
+ if (_buff[level]._pageNo != pageId) {
+ int32 pos = pageId * kBtSize;
+ _buff[level]._pageNo = pageId;
+ assert(_catFile->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.
+ _catFile->seek(pageId * kBtSize, SEEK_SET);
+
+ // Read in the page
+ byte buffer[kBtSize];
+ int bytesRead = catRead(buffer, kBtSize);
+
+ // Unpack it into the page structure
+ Common::MemoryReadStream stream(buffer, bytesRead, DisposeAfterUse::NO);
+ _buff[level]._page->readBTree(stream);
+ _buff[level]._index = -1;
+ }
+ return _buff[level]._page;
+}
+
+BtKeypack *ResourceManager::find(const char *key) {
+ debugC(1, kCGEDebugFile, "IoHand::find(%s)", key);
+
+ int lev = 0;
+ uint16 nxt = kBtValRoot;
+ while (!_catFile->eos()) {
+ 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]._index = 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]._index = i;
+ return &pg->_leaf[i];
+ }
+ }
+ return NULL;
+}
+
+bool ResourceManager::exist(const char *name) {
+ debugC(1, kCGEDebugFile, "ResourceManager::exist(%s)", name);
+
+ return scumm_stricmp(find(name)->_key, name) == 0;
+}
+
+uint16 ResourceManager::catRead(void *buf, uint16 length) {
+ if (!_catFile->isOpen())
+ return 0;
+
+ uint16 bytesRead = _catFile->read(buf, length);
+ if (!bytesRead)
+ error("Read %s - %d bytes", _catFile->getName(), length);
+ XCrypt(buf, length);
+ return bytesRead;
+}
+
+/*-----------------------------------------------------------------------
+ * EncryptedStream
+ *-----------------------------------------------------------------------*/
+EncryptedStream::EncryptedStream(CGEEngine *vm, const char *name) : _vm(vm) {
+ debugC(3, kCGEDebugFile, "EncryptedStream::EncryptedStream(%s)", name);
+
+ _error = false;
+ BtKeypack *kp = _vm->_resman->find(name);
+ if (scumm_stricmp(kp->_key, name) != 0)
+ _error = true;
+
+ _vm->_resman->seek(kp->_pos);
+ byte *dataBuffer = (byte *)malloc(kp->_size);
+ _vm->_resman->read(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();
+}
+
+bool EncryptedStream::seek(int32 offset) {
+ return _readStream->seek(offset);
+}
+
+Common::String EncryptedStream::readLine() {
+ return _readStream->readLine();
+}
+
+int32 EncryptedStream::size() {
+ return _readStream->size();
+}
+
+int32 EncryptedStream::pos() {
+ return _readStream->pos();
+}
+
+EncryptedStream::~EncryptedStream() {
+}
+
+} // End of namespace CGE
diff --git a/engines/cge/fileio.h b/engines/cge/fileio.h
new file mode 100644
index 0000000000..cee1fa79ef
--- /dev/null
+++ b/engines/cge/fileio.h
@@ -0,0 +1,120 @@
+/* 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 {
+
+class CGEEngine;
+
+#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 kCatName "VOL.CAT"
+#define kDatName "VOL.DAT"
+
+struct BtKeypack {
+ char _key[kBtKeySize];
+ uint32 _pos;
+ uint16 _size;
+};
+
+struct Inner {
+ uint8 _key[kBtKeySize];
+ uint16 _down;
+};
+
+struct Header {
+ uint16 _count;
+ uint16 _down;
+};
+
+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 readBTree(Common::ReadStream &s);
+};
+
+class ResourceManager {
+ struct {
+ BtPage *_page;
+ uint16 _pageNo;
+ int _index;
+ } _buff[kBtLevel];
+
+ BtPage *getPage(int level, uint16 pageId);
+ uint16 catRead(void *buf, uint16 length);
+ Common::File *_catFile;
+ Common::File *_datFile;
+ uint16 XCrypt(void *buf, uint16 length);
+public:
+
+ ResourceManager();
+ ~ResourceManager();
+ uint16 read(void *buf, uint16 length);
+ bool seek(int32 offs, int whence = 0);
+
+ BtKeypack *find(const char *key);
+ bool exist(const char *name);
+};
+
+class EncryptedStream {
+private:
+ CGEEngine *_vm;
+ Common::SeekableReadStream *_readStream;
+ bool _error;
+public:
+ EncryptedStream(CGEEngine *vm, const char *name);
+ ~EncryptedStream();
+ bool err();
+ bool eos();
+ bool seek(int32 offset);
+ int32 pos();
+ int32 size();
+ uint32 read(void *dataPtr, uint32 dataSize);
+ Common::String readLine();
+};
+
+} // End of namespace CGE
+
+#endif
diff --git a/engines/cge/game.cpp b/engines/cge/game.cpp
new file mode 100644
index 0000000000..851f6c59fb
--- /dev/null
+++ b/engines/cge/game.cpp
@@ -0,0 +1,54 @@
+/* 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 {
+
+Fly::Fly(CGEEngine *vm, Bitmap **shpl)
+ : Sprite(vm, shpl), _tx(0), _ty(0), _vm(vm) {
+ step(_vm->newRandom(2));
+ gotoxy(kFlyL + _vm->newRandom(kFlyR - kFlyL - _w), kFlyT + _vm->newRandom(kFlyB - kFlyT - _h));
+}
+
+void Fly::tick() {
+ step();
+ if (_flags._kept)
+ return;
+ if (_vm->newRandom(10) < 1) {
+ _tx = _vm->newRandom(3) - 1;
+ _ty = _vm->newRandom(3) - 1;
+ }
+ if (_x + _tx < kFlyL || _x + _tx + _w > kFlyR)
+ _tx = -_tx;
+ if (_y + _ty < kFlyT || _y + _ty + _h > kFlyB)
+ _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..4d5acf7371
--- /dev/null
+++ b/engines/cge/game.h
@@ -0,0 +1,53 @@
+/* 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 {
+
+enum {
+ kFlyL = 20,
+ kFlyT = 40,
+ kFlyR = 110,
+ kFlyB = 100
+};
+
+class Fly : public Sprite {
+private:
+ CGEEngine *_vm;
+public:
+ int _tx, _ty;
+ Fly(CGEEngine *vm, Bitmap **shpl);
+ void tick();
+};
+
+} // End of namespace CGE
+
+#endif
diff --git a/engines/cge/general.h b/engines/cge/general.h
new file mode 100644
index 0000000000..9e3fc7f249
--- /dev/null
+++ b/engines/cge/general.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.
+ *
+ */
+
+/*
+ * 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/file.h"
+
+namespace CGE {
+
+struct Dac {
+ uint8 _r;
+ uint8 _g;
+ uint8 _b;
+};
+
+} // End of namespace CGE
+
+#endif
diff --git a/engines/cge/module.mk b/engines/cge/module.mk
new file mode 100644
index 0000000000..5745aa5d48
--- /dev/null
+++ b/engines/cge/module.mk
@@ -0,0 +1,30 @@
+MODULE := engines/cge
+
+MODULE_OBJS := \
+ bitmap.o \
+ cge.o \
+ cge_main.o \
+ console.o \
+ detection.o \
+ events.o \
+ fileio.o \
+ game.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..34adeb3a8e
--- /dev/null
+++ b/engines/cge/snail.cpp
@@ -0,0 +1,1222 @@
+/* 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 {
+
+const char *CommandHandler::_commandText[] = {
+ "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
+};
+
+CommandHandler::CommandHandler(CGEEngine *vm, bool turbo)
+ : _turbo(turbo), _busy(false), _textDelay(false),
+ _timerExpiry(0), _talkEnable(true),
+ _head(0), _tail(0), _commandList((Command *)malloc(sizeof(Command) * 256)), _vm(vm) {
+}
+
+CommandHandler::~CommandHandler() {
+ free(_commandList);
+}
+
+/**
+ * Add a Command on the head of _commandList
+ * @param com Command
+ * @param ref Reference
+ * @param val Value
+ * @param ptr Sprite pointer
+ */
+void CommandHandler::addCommand(CommandType com, int ref, int val, void *ptr) {
+ Command *headCmd = &_commandList[_head++];
+ headCmd->_commandType = com;
+ headCmd->_ref = ref;
+ headCmd->_val = val;
+ headCmd->_spritePtr = ptr;
+ headCmd->_cbType = kNullCB;
+ if (headCmd->_commandType == kCmdClear) {
+ _tail = _head;
+ _vm->killText();
+ _timerExpiry = 0;
+ }
+}
+
+/**
+ * Add a Callback on the head of _commandList
+ * @param com Command
+ * @param ref Reference
+ * @param val Value
+ * @param CallbackType Callback type
+ */
+void CommandHandler::addCallback(CommandType com, int ref, int val, CallbackType cbType) {
+ Command *headCmd = &_commandList[_head++];
+ headCmd->_commandType = com;
+ headCmd->_ref = ref;
+ headCmd->_val = val;
+ headCmd->_spritePtr = NULL;
+ headCmd->_cbType = cbType;
+ if (headCmd->_commandType == kCmdClear) {
+ _tail = _head;
+ _vm->killText();
+ _timerExpiry = 0;
+ }
+}
+
+/**
+ * Add a Command on the tail of _commandList
+ * @param com Command
+ * @param ref Reference
+ * @param val Value
+ * @param ptr Sprite pointer
+ */
+void CommandHandler::insertCommand(CommandType com, int ref, int val, void *ptr) {
+ Command *tailCmd;
+
+ if (_busy) {
+ _commandList[(_tail - 1) & 0xFF] = _commandList[_tail];
+ tailCmd = &_commandList[_tail];
+ } else
+ tailCmd = &_commandList[(_tail - 1) & 0xFF];
+ _tail--;
+ tailCmd->_commandType = com;
+ tailCmd->_ref = ref;
+ tailCmd->_val = val;
+ tailCmd->_spritePtr = ptr;
+ tailCmd->_cbType = kNullCB;
+ if (tailCmd->_commandType == kCmdClear) {
+ _tail = _head;
+ _vm->killText();
+ _timerExpiry = 0;
+ }
+}
+
+void CommandHandler::runCommand() {
+ if (_busy)
+ return;
+
+ _busy = true;
+ uint8 tmpHead = _head;
+ while (_tail != tmpHead) {
+ Command *tailCmd = &_commandList[_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) {
+ _vm->killText();
+ _textDelay = false;
+ }
+ }
+ if (_vm->_talk && tailCmd->_commandType != kCmdPause)
+ break;
+ }
+
+ Sprite *spr = ((tailCmd->_ref >= 0) ? _vm->locate(tailCmd->_ref) : ((Sprite *) tailCmd->_spritePtr));
+ switch (tailCmd->_commandType) {
+ case kCmdLabel:
+ break;
+ case kCmdPause:
+ _timerExpiry = g_system->getMillis() + tailCmd->_val * kCommandFrameDelay;
+ if (_vm->_talk)
+ _textDelay = true;
+ break;
+ case kCmdWait:
+ if (spr) {
+ if (spr->seqTest(tailCmd->_val) &&
+ (tailCmd->_val >= 0 || spr != _vm->_hero || _vm->_hero->_tracePtr < 0)) {
+ _timerExpiry = g_system->getMillis() + spr->_time * kCommandFrameDelay;
+ } else {
+ _busy = false;
+ return;
+ }
+ }
+ break;
+ case kCmdLevel:
+ _vm->snLevel(spr, tailCmd->_val);
+ break;
+ case kCmdHide:
+ _vm->snHide(spr, tailCmd->_val);
+ break;
+ case kCmdSay:
+ if (spr && _talkEnable) {
+ if (spr == _vm->_hero && spr->seqTest(-1))
+ spr->step(kSeqHTalk);
+ _vm->_text->say(_vm->_text->getText(tailCmd->_val), spr);
+ _vm->_sys->_funDel = kHeroFun0;
+ }
+ break;
+ case kCmdInf:
+ if (_talkEnable) {
+ _vm->inf(_vm->_text->getText(tailCmd->_val));
+ _vm->_sys->_funDel = kHeroFun0;
+ }
+ break;
+ case kCmdTime:
+ if (spr && _talkEnable) {
+ if (spr == _vm->_hero && spr->seqTest(-1))
+ spr->step(kSeqHTalk);
+ _vm->_text->sayTime(spr);
+ }
+ break;
+ case kCmdCave:
+ _vm->switchScene(tailCmd->_val);
+ break;
+ case kCmdKill:
+ _vm->snKill(spr);
+ break;
+ case kCmdSeq:
+ _vm->snSeq(spr, tailCmd->_val);
+ break;
+ case kCmdRSeq:
+ _vm->snRSeq(spr, tailCmd->_val);
+ break;
+ case kCmdSend:
+ _vm->snSend(spr, tailCmd->_val);
+ break;
+ case kCmdSwap:
+ _vm->snSwap(spr, tailCmd->_val);
+ break;
+ case kCmdCover:
+ _vm->snCover(spr, tailCmd->_val);
+ break;
+ case kCmdUncover:
+ _vm->snUncover(spr, (tailCmd->_val >= 0) ? _vm->locate(tailCmd->_val) : ((Sprite *) tailCmd->_spritePtr));
+ break;
+ case kCmdKeep:
+ _vm->snKeep(spr, tailCmd->_val);
+ break;
+ case kCmdGive:
+ _vm->snGive(spr, tailCmd->_val);
+ break;
+ case kCmdGame:
+ _vm->snGame(spr, tailCmd->_val);
+ break;
+ case kCmdSetX0:
+ _vm->snSetX0(tailCmd->_ref, tailCmd->_val);
+ break;
+ case kCmdSetY0:
+ _vm->snSetY0(tailCmd->_ref, tailCmd->_val);
+ break;
+ case kCmdSetXY:
+ _vm->snSetXY(spr, tailCmd->_val);
+ break;
+ case kCmdRelX:
+ _vm->snRelX(spr, tailCmd->_val);
+ break;
+ case kCmdRelY:
+ _vm->snRelY(spr, tailCmd->_val);
+ break;
+ case kCmdRelZ:
+ _vm->snRelZ(spr, tailCmd->_val);
+ break;
+ case kCmdSetX:
+ _vm->snSetX(spr, tailCmd->_val);
+ break;
+ case kCmdSetY:
+ _vm->snSetY(spr, tailCmd->_val);
+ break;
+ case kCmdSetZ:
+ _vm->snSetZ(spr, tailCmd->_val);
+ break;
+ case kCmdSlave:
+ _vm->snSlave(spr, tailCmd->_val);
+ break;
+ case kCmdTrans:
+ _vm->snTrans(spr, tailCmd->_val);
+ break;
+ case kCmdPort:
+ _vm->snPort(spr, tailCmd->_val);
+ break;
+ case kCmdNext:
+ case kCmdIf:
+ case kCmdTalk:
+ break;
+ case kCmdMouse:
+ _vm->snMouse(tailCmd->_val != 0);
+ break;
+ case kCmdNNext:
+ _vm->snNNext(spr, tailCmd->_val);
+ break;
+ case kCmdTNext:
+ _vm->snTNext(spr, tailCmd->_val);
+ break;
+ case kCmdRNNext:
+ _vm->snRNNext(spr, tailCmd->_val);
+ break;
+ case kCmdRTNext:
+ _vm->snRTNext(spr, tailCmd->_val);
+ break;
+ case kCmdRMNear:
+ _vm->snRmNear(spr);
+ break;
+ case kCmdRmTake:
+ _vm->snRmTake(spr);
+ break;
+ case kCmdFlag:
+ _vm->snFlag(tailCmd->_ref & 3, tailCmd->_val != 0);
+ break;
+ case kCmdSetRef:
+ _vm->snSetRef(spr, tailCmd->_val);
+ break;
+ case kCmdBackPt:
+ _vm->snBackPt(spr, tailCmd->_val);
+ break;
+ case kCmdFlash:
+ _vm->snFlash(tailCmd->_val != 0);
+ break;
+ case kCmdLight:
+ _vm->snLight(tailCmd->_val != 0);
+ break;
+ case kCmdSetHBarrier:
+ _vm->snHBarrier(tailCmd->_ref, tailCmd->_val);
+ break;
+ case kCmdSetVBarrier:
+ _vm->snVBarrier(tailCmd->_ref, tailCmd->_val);
+ break;
+ case kCmdWalk:
+ _vm->snWalk(spr, tailCmd->_ref, tailCmd->_val);
+ break;
+ case kCmdReach:
+ _vm->snReach(spr, tailCmd->_val);
+ break;
+ case kCmdSound:
+ _vm->snSound(spr, tailCmd->_val);
+ break;
+ case kCmdCount:
+ _vm->_sound->setRepeat(tailCmd->_val);
+ break;
+ case kCmdExec:
+ switch (tailCmd->_cbType) {
+ case kQGame:
+ _vm->qGame();
+ break;
+ case kMiniStep:
+ _vm->miniStep(tailCmd->_val);
+ break;
+ case kXScene:
+ _vm->xScene();
+ break;
+ case kSoundSetVolume:
+ _vm->sndSetVolume();
+ break;
+ default:
+ error("Unknown Callback Type in SNEXEC");
+ }
+ break;
+ case kCmdStep:
+ spr->step();
+ break;
+ case kCmdZTrim:
+ _vm->snZTrim(spr);
+ break;
+ case kCmdGhost:
+ _vm->snGhost((Bitmap *) tailCmd->_spritePtr);
+ break;
+ default:
+ warning("Unhandled snc->_com in SNMouse(bool)");
+ break;
+ }
+ _tail++;
+ if (!_turbo)
+ break;
+ }
+
+ _busy = false;
+}
+
+bool CommandHandler::idle() {
+ return (_head == _tail);
+}
+
+/**
+ * Handles mini-Games logic
+ * @param com Command
+ * @param num mini game number
+ */
+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)) {
+ _commandHandler->addCommand(kCmdSeq, -1, 3, dup[0]); // Yes
+ _commandHandler->addCommand(kCmdSeq, -1, 3, dup[1]); // Yes
+ _commandHandler->addCommand(kCmdSeq, -1, 3, dup[2]); // Yes
+ _commandHandler->addCommand(kCmdTNext, -1, 0, dup[0]); // Reset Take
+ _commandHandler->addCommand(kCmdTNext, -1, 0, dup[1]); // Reset Take
+ _commandHandler->addCommand(kCmdTNext, -1, 0, dup[2]); // Reset Take
+ _commandHandler->addCommand(kCmdNNext, -1, 0, dup[0]); // Reset Near
+ _commandHandler->addCommand(kCmdPause, -1, 72, NULL); // Pause the game for 72/80 second
+ _commandHandler->addCommand(kCmdSay, 1, 16009, NULL); // Say "I win.."
+ _commandHandler->addCommand(kCmdSay, buref, 16010, NULL); // Say "Go Sit..."
+ _commandHandler->addCommand(kCmdSay, 1, 16011, NULL); // Say "I prefer not"
+
+ if (hand) {
+ _commandHandler->addCommand(kCmdSend, 16060 + hand, 16, NULL); // Give hand
+ _commandHandler->addCommand(kCmdSeq, buref, 4, NULL); // Take off
+ _commandHandler->addCommand(kCmdSeq, 16060 + hand, 1, NULL); // start one of the Bartender animations
+ _commandHandler->addCommand(kCmdSound, 16060 + hand, 16002, NULL); // Play tear sound
+ _commandHandler->addCommand(kCmdWait, 16060 + hand, 3, NULL); // Take up
+ _commandHandler->addCommand(kCmdSwap, buref, buref + 100, NULL); // Open hand
+ _commandHandler->addCommand(kCmdSeq, 16016, Stage, NULL); // Start Belongings animation
+ _commandHandler->addCommand(kCmdSend, 16060 + hand, -1, NULL); // Hide hand
+ _commandHandler->addCommand(kCmdWait, 16060 + hand, -1, NULL); // Stop moving hand
+ } else {
+ _commandHandler->addCommand(kCmdSeq, buref, 4, NULL); // Take off
+ _commandHandler->addCommand(kCmdSound, 16060 + hand, 16002, NULL); // Play tear sound
+ _commandHandler->addCommand(kCmdWait, buref, -1, NULL); // Will take off
+ _commandHandler->addCommand(kCmdSwap, buref, buref + 100, NULL); // Open hand
+ _commandHandler->addCommand(kCmdSeq, 16016, Stage, NULL); // Start Belongings animation
+ }
+ _commandHandler->addCommand(kCmdPause, -1, 72, NULL); // Pause the game for 72/80 second
+ _commandHandler->addCommand(kCmdSeq, -1, 0, dup[1]); // Get away (Him)
+ _commandHandler->addCommand(kCmdSetXY, -1, 203 + kScrWidth * 49, dup[1]);
+ _commandHandler->addCommand(kCmdSetZ, -1, 7, dup[1]);
+ _commandHandler->addCommand(kCmdSeq, -1, 0, dup[2]); // Get Away (Her)
+ _commandHandler->addCommand(kCmdSetXY, -1, 182 + kScrWidth * 62, dup[2]);
+ _commandHandler->addCommand(kCmdSetZ, -1, 9, dup[2]);
+ _game = 0;
+ return;
+ } else {
+ _commandHandler->addCommand(kCmdSeq, -1, 2, dup[0]); // reset animation sequence
+ _commandHandler->addCommand(kCmdSeq, -1, 2, dup[1]); // reset animation sequence
+ _commandHandler->addCommand(kCmdSeq, -1, 2, dup[2]); // reset animation sequence
+ _commandHandler->addCommand(kCmdPause, -1, 72, NULL); // Pause the game for 72/80 second
+ }
+ }
+ _commandHandler->addCommand(kCmdWalk, 198, 134, NULL); // Go to place
+ _commandHandler->addCommand(kCmdWait, 1, -1, NULL); // Stop moving
+ _commandHandler->addCommand(kCmdCover, 1, 16101, NULL); // Man to beat
+ _commandHandler->addCommand(kCmdSeq, 16101, 1, NULL); // Start Chief animation (16dupnia)
+ _commandHandler->addCommand(kCmdWait, 16101, 5, NULL); // wait
+ _commandHandler->addCommand(kCmdPause, 16101, 24, NULL); // Pause the game for 24/80 second
+ _commandHandler->addCommand(kCmdSeq, 16040, 1, NULL); // Start Slap animation (16plask)
+ _commandHandler->addCommand(kCmdSound, 16101, 16001, NULL); // Play "Slap" sound
+ _commandHandler->addCommand(kCmdPause, 16101, 24, NULL); // Pause the game for 24/80 second
+ _commandHandler->addCommand(kCmdSeq, 16040, 0, NULL); // Reset animation sequence
+ _commandHandler->addCommand(kCmdWait, 16101, -1, NULL); // stay
+ _commandHandler->addCommand(kCmdUncover, 1, 16101, NULL); // SDS
+ if (!_game) {
+ _commandHandler->addCommand(kCmdSay, buref, 16008, NULL); // say "Guess!"
+ _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
+ _commandHandler->addCommand(kCmdGame, 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);
+ }
+
+ _commandHandler->addCommand(kCmdSetZ, 20700, 0, NULL);
+ bool hit = (_sprK1->_seqPtr + _sprK2->_seqPtr + _sprK3->_seqPtr == 15);
+ if (hit) {
+ if (spr->_ref == 1) {
+ _commandHandler->addCommand(kCmdSay, 1, 20003, NULL); // hurray!
+ _commandHandler->addCommand(kCmdSeq, 20011, 2, NULL); // Camera away
+ _commandHandler->addCommand(kCmdSend, 20701, -1, NULL); // move dice1 to scene -1
+ _commandHandler->addCommand(kCmdSend, 20702, -1, NULL); // move dice2 to scene -1
+ _commandHandler->addCommand(kCmdSend, 20703, -1, NULL); // move dice3 to scene -1
+ _commandHandler->addCommand(kCmdSend, 20700, -1, NULL); // move TV to scene -1
+ _commandHandler->addCommand(kCmdKeep, 20007, 0, NULL); // to pocket
+ _commandHandler->addCommand(kCmdSend, 20006, 20, NULL); // Move Coin to scene 20
+ _commandHandler->addCommand(kCmdSound, 20006, 20002, NULL); // Play Coin sound
+ _commandHandler->addCommand(kCmdSay, 20002, 20004, NULL); // Say "Luck guy..."
+ _commandHandler->addCommand(kCmdSend, 20010, 20, NULL); // Move Paper to scene 20
+ _commandHandler->addCommand(kCmdSound, 20010, 20003, NULL); // Play "ksh" sound! (fx20003.wav)
+ _commandHandler->addCommand(kCmdSay, 20001, 20005, NULL); // Say "Congratulations"
+ _game = false;
+ return;
+ } else
+ _sprK3->step(newRandom(5));
+ }
+
+ if (_gameCase2Cpt < 100) {
+ switch (_gameCase2Cpt) {
+ case 15:
+ // Give hint about ALTered dice
+ _commandHandler->addCommand(kCmdSay, 20003, 20021, NULL);
+ break;
+ case 30:
+ case 45:
+ case 60:
+ case 75:
+ // Tell to use ALT key
+ _commandHandler->addCommand(kCmdSay, 20003, 20022, NULL);
+ break;
+ }
+ _gameCase2Cpt++;
+ }
+
+ switch (spr->_ref) {
+ case 1:
+ _commandHandler->addCommand(kCmdSay, 20001, 20011, NULL); // Say "It'a my turn"
+ _commandHandler->addCommand(kCmdSeq, 20001, 1, NULL); // Throw dice
+ _commandHandler->addCommand(kCmdWait, 20001, 1, NULL); // wait
+ _commandHandler->addCommand(kCmdSetZ, 20700, 2, NULL); // hide dice
+ _commandHandler->addCommand(kCmdHide, 20007, 1, NULL); // hide dice
+ _commandHandler->addCommand(kCmdWait, 20001, 16, NULL); // wait
+ _commandHandler->addCommand(kCmdSeq, 20007, 1, NULL); // Start dice animation (20kosci)
+ _commandHandler->addCommand(kCmdHide, 20007, 0, NULL); // unhide
+ _commandHandler->addCommand(kCmdSound, 20007, 20001, NULL); // Play Dice sound
+ _commandHandler->addCommand(kCmdWait, 20007, -1, NULL); // the end
+ _commandHandler->addCommand(kCmdGame, 20001, 2, NULL); // again!
+ break;
+
+ case 20001:
+ _commandHandler->addCommand(kCmdSay, 20002, 20012, NULL); // Say "Now it's mine"
+ _commandHandler->addCommand(kCmdSeq, 20002, 1, NULL); // Throw dice
+ _commandHandler->addCommand(kCmdWait, 20002, 3, NULL); // wait
+ _commandHandler->addCommand(kCmdSetZ, 20700, 2, NULL); // hide dice
+ _commandHandler->addCommand(kCmdHide, 20007, 1, NULL); // hide dice
+ _commandHandler->addCommand(kCmdWait, 20002, 10, NULL); // wait
+ _commandHandler->addCommand(kCmdSeq, 20007, 2, NULL); // Start dice animation (20kosci)
+ _commandHandler->addCommand(kCmdHide, 20007, 0, NULL); // unhide
+ _commandHandler->addCommand(kCmdSound, 20007, 20001, NULL); // Play Dice sound
+ _commandHandler->addCommand(kCmdWait, 20007, -1, NULL); // the end
+ _commandHandler->addCommand(kCmdGame, 20002, 2, NULL); // again!
+ break;
+
+ case 20002:
+ _commandHandler->addCommand(kCmdSay, 20002, 20010, NULL); // "Roll the bones!"
+ _commandHandler->addCommand(kCmdWalk, 20005, -1, NULL); // Walk to table
+ _commandHandler->addCommand(kCmdWait, 1, -1, NULL); // Wait
+ _commandHandler->addCommand(kCmdCover, 1, 20101, NULL); // grasol ??
+ _commandHandler->addCommand(kCmdSeq, 20101, 1, NULL); // Start Chief animation (20solgra)
+ _commandHandler->addCommand(kCmdWait, 20101, 5, NULL); // Wait
+ _commandHandler->addCommand(kCmdSetZ, 20700, 2, NULL); // Hide dice
+ _commandHandler->addCommand(kCmdHide, 20007, 1, NULL); // Hide dice
+ _commandHandler->addCommand(kCmdWait, 20101, 15, NULL); // wait
+ _commandHandler->addCommand(kCmdSeq, 20007, 1, NULL); // Start dice animation (20kosci)
+ _commandHandler->addCommand(kCmdHide, 20007, 0, NULL); // Unhide
+ _commandHandler->addCommand(kCmdSound, 20007, 20001, NULL); // Play Dice sound
+ _commandHandler->addCommand(kCmdWait, 20101, -1, NULL); // the end
+ _commandHandler->addCommand(kCmdUncover, 1, 20101, NULL); // SDS ??
+ _commandHandler->addCommand(kCmdGame, 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));
+}
+
+/**
+ * Check if an item is in the inventory, and returns its position
+ * @param spr Sprite pointer
+ * @return -1 if not found, else index.
+ */
+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();
+ _commandHandler->addCommand(kCmdWait, -1, -1, _hero);
+ _commandHandler->addCommand(kCmdSeq, -1, kSeqPocketFull, _hero);
+ _commandHandler->addCommand(kCmdSound, -1, 2, _hero);
+ _commandHandler->addCommand(kCmdWait, -1, -1, _hero);
+ _commandHandler->addCommand(kCmdSay, 1, kPocketFull, _hero);
+}
+
+void CGEEngine::hide1(Sprite *spr) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::hide1(spr)");
+
+ _commandHandlerTurbo->addCommand(kCmdGhost, -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;
+
+ CommandHandler::Command *comtab = spr->snList(snq);
+ CommandHandler::Command *c = comtab + ptr;
+
+ if (findPocket(NULL) < 0) { // no empty pockets?
+ CommandHandler::Command *p;
+ for (p = c; p->_commandType != kCmdNext; p++) { // find KEEP command
+ if (p->_commandType == kCmdKeep) {
+ pocFul();
+ return;
+ }
+ if (p->_spritePtr)
+ break;
+ }
+ }
+ while (true) {
+ if (c->_commandType == kCmdTalk) {
+ if ((_commandHandler->_talkEnable = (c->_val != 0)) == false)
+ killText();
+ }
+ if (c->_commandType == kCmdNext) {
+ 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->_commandType == kCmdIf) {
+ 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 {
+ _commandHandler->addCommand(c->_commandType, c->_ref, c->_val, spr);
+ if (c->_spritePtr)
+ break;
+ else
+ c++;
+ }
+ }
+}
+
+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->_scene;
+ bool was1 = (was == 0 || was == _now);
+ bool val1 = (val == 0 || val == _now);
+ spr->_scene = 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)
+ _bitmapPalette = _vga->_sysPal;
+ if (spr->_flags._back)
+ spr->backShow(true);
+ else
+ expandSprite(spr);
+ _bitmapPalette = 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->_scene;
+ int xwas = xspr->_scene;
+ bool was1 = (was == 0 || was == _now);
+ bool xwas1 = (xwas == 0 || xwas == _now);
+
+ SWAP(spr->_scene, xspr->_scene);
+ 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->_scene = spr->_scene;
+ 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->_scene = xspr->_scene;
+ 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 scene, int x0) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snSetX0(%d, %d)", scene, x0);
+
+ _heroXY[scene - 1].x = x0;
+}
+
+void CGEEngine::snSetY0(int scene, int y0) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snSetY0(%d, %d)", scene, y0);
+
+ _heroXY[scene - 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->_scene);
+ 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->_scene = -1;
+ _vga->_spareQ->append(spr);
+ }
+ if (nx) {
+ if (nx->_flags._slav)
+ snKill(nx);
+ }
+}
+
+/**
+ * Play a FX sound
+ * @param spr Sprite pointer
+ * @param wav FX index
+ */
+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->_scene = 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->_scene = _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->_scene = 0;
+ spr->_flags._hide = false;
+ } else {
+ spr->_flags._hide = true;
+ spr->_scene = -1;
+ }
+ } else {
+ warning("SPR not found! ref: %d", 100 + i);
+ }
+ }
+
+ _lev = lev;
+ _maxScene = _maxSceneArr[_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 scene, const int barX) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snHBarrier(%d, %d)", scene, barX);
+
+ _barriers[(scene > 0) ? scene : _now]._horz = barX;
+}
+
+void CGEEngine::snVBarrier(const int scene, const int barY) {
+ debugC(1, kCGEDebugEngine, "CGEEngine::snVBarrier(%d, %d)", scene, barY);
+
+ _barriers[(scene > 0) ? scene : _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();
+}
+
+} // End of namespace CGE
diff --git a/engines/cge/snail.h b/engines/cge/snail.h
new file mode 100644
index 0000000000..3acbbd0e5f
--- /dev/null
+++ b/engines/cge/snail.h
@@ -0,0 +1,85 @@
+/* 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_COMMANDHANDLER_H
+#define CGE_COMMANDHANDLER_H
+
+#include "cge/cge.h"
+
+namespace CGE {
+
+#define kCommandFrameRate 80
+#define kCommandFrameDelay (1000 / kCommandFrameRate)
+#define kDressed 3
+
+enum CommandType {
+ kCmdLabel, kCmdPause, kCmdWait, kCmdLevel, kCmdHide,
+ kCmdSay, kCmdInf, kCmdTime, kCmdCave, kCmdKill,
+ kCmdRSeq, kCmdSeq, kCmdSend, kCmdSwap, kCmdKeep,
+ kCmdGive, kCmdIf, kCmdGame, kCmdSetX0, kCmdSetY0,
+ kCmdSlave, kCmdSetXY, kCmdRelX, kCmdRelY, kCmdRelZ,
+ kCmdSetX, kCmdSetY, kCmdSetZ, kCmdTrans, kCmdPort,
+ kCmdNext, kCmdNNext, kCmdTNext, kCmdRNNext, kCmdRTNext,
+ kCmdRMNear, kCmdRmTake, kCmdFlag, kCmdSetRef, kCmdBackPt,
+ kCmdFlash, kCmdLight, kCmdSetHBarrier, kCmdSetVBarrier, kCmdWalk,
+ kCmdReach, kCmdCover, kCmdUncover, kCmdClear, kCmdTalk,
+ kCmdMouse, kCmdSound, kCmdCount, kCmdExec, kCmdStep,
+ kCmdZTrim, kCmdGhost
+};
+
+class CommandHandler {
+public:
+ struct Command {
+ CommandType _commandType;
+ int _ref;
+ int _val;
+ void *_spritePtr;
+ CallbackType _cbType;
+ } *_commandList;
+ static const char *_commandText[];
+ bool _talkEnable;
+
+ CommandHandler(CGEEngine *vm, bool turbo);
+ ~CommandHandler();
+ void runCommand();
+ void addCommand(CommandType com, int ref, int val, void *ptr);
+ void addCallback(CommandType com, int ref, int val, CallbackType cbType);
+ void insertCommand(CommandType com, int ref, int val, void *ptr);
+ bool idle();
+private:
+ CGEEngine *_vm;
+ bool _turbo;
+ uint8 _head;
+ uint8 _tail;
+ bool _busy;
+ bool _textDelay;
+ uint32 _timerExpiry;
+};
+
+} // End of namespace CGE
+
+#endif
diff --git a/engines/cge/sound.cpp b/engines/cge/sound.cpp
new file mode 100644
index 0000000000..646689e99e
--- /dev/null
+++ b/engines/cge/sound.cpp
@@ -0,0 +1,290 @@
+/* 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/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 {
+
+DataCk::DataCk(byte *buf, int bufSize) {
+ _buf = buf;
+ _ckSize = bufSize;
+}
+
+DataCk::~DataCk() {
+ free(_buf);
+}
+
+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((*_vm->_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(CGEEngine *vm, int size) : _current(NULL), _vm(vm) {
+ _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);
+ EncryptedStream file(_vm, 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);
+
+ EncryptedStream file(_vm, 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::loadWave(EncryptedStream *file) {
+ byte *data = (byte *)malloc(file->size());
+ file->read(data, file->size());
+
+ return new DataCk(data, file->size());
+}
+
+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(CGEEngine *vm) : _vm(vm) {
+ _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 (!_vm->_resman->exist(filename.c_str()))
+ return;
+
+ // Stop any currently playing MIDI file
+ killMidi();
+
+ // Read in the data for the file
+ EncryptedStream mid(_vm, 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();
+
+ // Al the tracks are supposed to loop
+ _isLooping = true;
+ _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..a3f4d4d777
--- /dev/null
+++ b/engines/cge/sound.h
@@ -0,0 +1,136 @@
+/* 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 "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;
+
+// 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
+};
+
+class DataCk {
+ byte *_buf;
+ int _ckSize;
+public:
+ DataCk(byte *buf, int bufSize);
+ ~DataCk();
+ inline const byte *addr() {
+ return _buf;
+ }
+ inline int size() {
+ return _ckSize;
+ }
+};
+
+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 {
+ CGEEngine *_vm;
+ struct Handler {
+ int _ref;
+ DataCk *_wav;
+ } *_cache;
+ int _size;
+
+ DataCk *load(int idx, int ref);
+ DataCk *loadWave(EncryptedStream *file);
+ int find(int ref);
+public:
+ DataCk *_current;
+
+ Fx(CGEEngine *vm, int size);
+ ~Fx();
+ void clear();
+ void preload(int ref0);
+ DataCk *operator[](int ref);
+};
+
+class MusicPlayer: public Audio::MidiPlayer {
+private:
+ CGEEngine *_vm;
+ byte *_data;
+ int _dataSize;
+ bool _isGM;
+
+ // Start MIDI File
+ void sndMidiStart();
+
+ // Stop MIDI File
+ void sndMidiStop();
+public:
+ MusicPlayer(CGEEngine *vm);
+ ~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..467b39be40
--- /dev/null
+++ b/engines/cge/talk.cpp
@@ -0,0 +1,292 @@
+/* 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(CGEEngine *vm, const char *name) : _vm(vm) {
+ _map = (uint8 *)malloc(kMapSize);
+ _pos = (uint16 *)malloc(kPosSize * sizeof(uint16));
+ _widthArr = (uint8 *)malloc(kWidSize);
+
+ assert((_map != NULL) && (_pos != NULL) && (_widthArr != NULL));
+ _vm->mergeExt(_path, name, kFontExt);
+ load();
+}
+
+Font::~Font() {
+ free(_map);
+ free(_pos);
+ free(_widthArr);
+}
+
+void Font::load() {
+ EncryptedStream f(_vm, _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;
+}
+
+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 += _vm->_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 = _vm->_font->_widthArr[(unsigned char)*text];
+ uint8 *f = _vm->_font->_map + _vm->_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(_vm, 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 = _vm->_font->_widthArr[(unsigned char)*text], i;
+ uint8 *fp = _vm->_font->_map + _vm->_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(_vm, 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 = _vm->_font->_widthArr[(unsigned char)*text];
+ uint8 *fp = _vm->_font->_map + _vm->_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..55c529b7ea
--- /dev/null
+++ b/engines/cge/talk.h
@@ -0,0 +1,76 @@
+/* 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"
+
+enum TextBoxStyle { kTBPure, kTBRect, kTBRound };
+
+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);
+
+ virtual void update(const char *text);
+ 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..64f9959442
--- /dev/null
+++ b/engines/cge/text.cpp
@@ -0,0 +1,205 @@
+/* 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(CGEEngine *vm, const char *fname) : _vm(vm) {
+ _vm->mergeExt(_fileName, fname, kSayExt);
+ if (!_vm->_resman->exist(_fileName))
+ error("No talk (%s)\n", _fileName);
+ int16 txtCount = count() + 1;
+ if (!txtCount)
+ error("Unable to read dialog file %s", _fileName);
+
+ _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(_vm, _fileName);
+ if (tf.err())
+ return -1;
+
+ Common::String line;
+ char tmpStr[kLineMax + 1];
+
+ int counter = 0;
+
+ for (line = tf.readLine(); !tf.eos(); line = tf.readLine()) {
+ char *s;
+
+ strcpy(tmpStr, line.c_str());
+ if ((s = strtok(tmpStr, " =,;/\t\n")) == NULL)
+ continue;
+ if (!isdigit(*s))
+ continue;
+
+ counter++;
+ }
+ return counter;
+}
+
+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(_vm, _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) {
+ _vm->killText();
+ _vm->_talk = new Talk(_vm, text, kTBRound);
+ if (!_vm->_talk)
+ return;
+
+ bool east = spr->_flags._east;
+ int x = (east) ? (spr->_x + spr->_w - 2) : (spr->_x + 2);
+ int y = spr->_y + 2;
+ Speaker *speaker = new Speaker(_vm);
+ uint16 sw = speaker->_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
+
+ _vm->_talk->_flags._kill = true;
+ _vm->_talk->_flags._bDel = true;
+ _vm->_talk->setName(_vm->_text->getText(kSayName));
+ _vm->_talk->gotoxy(x - (_vm->_talk->_w - sw) / 2 - 3 + 6 * east, y - speaker->_h - _vm->_talk->_h + 1);
+ _vm->_talk->_z = 125;
+ _vm->_talk->_ref = kSayRef;
+
+ speaker->gotoxy(x, _vm->_talk->_y + _vm->_talk->_h - 1);
+ speaker->_z = 126;
+ speaker->_flags._slav = true;
+ speaker->_flags._kill = true;
+ speaker->setName(_vm->_text->getText(kSayName));
+ speaker->step(east);
+ speaker->_ref = kSayRef;
+
+ _vm->_vga->_showQ->insert(_vm->_talk, _vm->_vga->_showQ->last());
+ _vm->_vga->_showQ->insert(speaker, _vm->_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);
+}
+
+} // End of namespace CGE
diff --git a/engines/cge/text.h b/engines/cge/text.h
new file mode 100644
index 0000000000..13ce6bbfbb
--- /dev/null
+++ b/engines/cge/text.h
@@ -0,0 +1,66 @@
+/* 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;
+};
+
+} // End of namespace CGE
+
+#endif
diff --git a/engines/cge/vga13h.cpp b/engines/cge/vga13h.cpp
new file mode 100644
index 0000000000..49cfcd3084
--- /dev/null
+++ b/engines/cge/vga13h.cpp
@@ -0,0 +1,1009 @@
+/* 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;
+}
+
+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), _scene(0), _vm(vm) {
+ memset(_file, 0, sizeof(_file));
+ memset(&_flags, 0, sizeof(_flags));
+ _ref = 0;
+ _x = _y = 0;
+ _w = _h = 0;
+ _time = 0;
+ _seqPtr = 0;
+ _shpCnt = 0;
+ _prev = _next = NULL;
+
+ setShapeList(shpP);
+}
+
+Sprite::~Sprite() {
+ if (_vm->_sprite == this)
+ _vm->_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;
+
+ CommandHandler::Command *c = spr->_ext->_take;
+ if (c != NULL) {
+ c += spr->_takePtr;
+ if (c->_ref == _ref)
+ if (c->_commandType != kCmdLabel || (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;
+}
+
+CommandHandler::Command *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 fname[kPathMax];
+
+ Common::Array<BitmapPtr> shplist;
+ for (int i = 0; i < _shpCnt + 1; ++i)
+ shplist.push_back(NULL);
+
+ Seq *seq = NULL;
+ int shapeCount = 0,
+ seqCount = 0,
+ nearCount = 0,
+ takeCount = 0,
+ maxnow = 0,
+ maxnxt = 0;
+
+ CommandHandler::Command *nearList = NULL;
+ CommandHandler::Command *takeList = NULL;
+ _vm->mergeExt(fname, _file, kSprExt);
+ if (_vm->_resman->exist(fname)) { // sprite description file exist
+ EncryptedStream sprf(_vm, fname);
+ if (sprf.err())
+ error("Bad SPR [%s]", fname);
+ Common::String line;
+ char tmpStr[kLineMax + 1];
+ int len = 0, lcnt = 0;
+
+ for (line = sprf.readLine(); !sprf.eos(); line = sprf.readLine()) {
+ len = line.size();
+ strcpy(tmpStr, line.c_str());
+ lcnt++;
+ if (len == 0 || *tmpStr == '.')
+ continue;
+
+ CommandHandler::Command *c;
+ switch (_vm->takeEnum(Comd, strtok(tmpStr, " =\t"))) {
+ case 0:
+ // Name
+ setName(strtok(NULL, ""));
+ break;
+ case 1:
+ // Phase
+ // In case the shape index gets too high, increase the array size
+ while ((shapeCount + 1) >= (int)shplist.size()) {
+ shplist.push_back(NULL);
+ ++_shpCnt;
+ }
+ shplist[shapeCount++] = new Bitmap(_vm, strtok(NULL, " \t,;/"));
+ break;
+ case 2:
+ // Seq
+ seq = (Seq *)realloc(seq, (seqCount + 1) * sizeof(*seq));
+ assert(seq != NULL);
+ Seq *s;
+ s = &seq[seqCount++];
+ 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 = seqCount;
+ break;
+ case 0xFE:
+ s->_next = seqCount - 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;
+ nearList = (CommandHandler::Command *)realloc(nearList, (nearCount + 1) * sizeof(*nearList));
+ assert(nearList != NULL);
+ c = &nearList[nearCount++];
+ if ((c->_commandType = (CommandType)_vm->takeEnum(CommandHandler::_commandText, 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->_spritePtr = NULL;
+ break;
+ case 4:
+ // Take
+ if (_takePtr == kNoPtr)
+ break;
+ takeList = (CommandHandler::Command *)realloc(takeList, (takeCount + 1) * sizeof(*takeList));
+ assert(takeList != NULL);
+ c = &takeList[takeCount++];
+ if ((c->_commandType = (CommandType)_vm->takeEnum(CommandHandler::_commandText, 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->_spritePtr = NULL;
+ break;
+ }
+ }
+ } else {
+ // no sprite description: try to read immediately from .BMP
+ shplist[shapeCount++] = new Bitmap(_vm, _file);
+ }
+
+ shplist[shapeCount] = NULL;
+ if (seq) {
+ if (maxnow >= shapeCount)
+ error("Bad PHASE in SEQ [%s]", fname);
+ if (maxnxt >= seqCount)
+ 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 (nearList)
+ nearList[nearCount - 1]._spritePtr = _ext->_near = nearList;
+ else
+ _nearPtr = kNoPtr;
+ if (takeList)
+ takeList[takeCount - 1]._spritePtr = _ext->_take = takeList;
+ 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 = _vm->_vga->_page[1];
+ _vm->_vga->_page[1] = _vm->_vga->_page[pg & 3];
+ shp()->show(_x, _y);
+ _vm->_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(_vm, 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(_scene);
+
+ // 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
+}
+
+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::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());
+ }
+
+#if 0
+ // This part was used to display credits at the beginning of the game
+ for (int i = 10; i < 20; i++) {
+ char *text = _text->getText(i);
+ if (text) {
+ debugN(1, "%s\n", text);
+ }
+ }
+#endif
+ _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);
+}
+
+uint8 Vga::closest(Dac *pal, const uint8 colR, const uint8 colG, const uint8 colB) {
+#define f(col, lum) ((((uint16)(col)) << 8) / lum)
+ uint16 i, dif = 0xFFFF, found = 0;
+ uint16 L = colR + colG + colB;
+ if (!L)
+ L++;
+ uint16 R = f(colR, L), G = f(colG, L), B = f(colB, 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
+}
+
+uint8 *Vga::glass(Dac *pal, const uint8 colR, const uint8 colG, const uint8 colB) {
+ uint8 *x = (uint8 *)malloc(256);
+ if (x) {
+ uint16 i;
+ for (i = 0; i < 256; i++) {
+ x[i] = closest(pal, ((uint16)(pal[i]._r) * colR) / 255,
+ ((uint16)(pal[i]._g) * colG) / 255,
+ ((uint16)(pal[i]._b) * colB) / 255);
+ }
+ }
+ return x;
+}
+
+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 *)_vm->_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 *)_vm->_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 *)_vm->_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 *)_vm->_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++;
+ }
+ }
+}
+
+
+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 *)_vm->_vga->_page[2]->getBasePtr(x, yp);
+ byte *destP = (byte *)_vm->_vga->_page[1]->getBasePtr(x, yp);
+
+ Common::copy(srcP, srcP + _w, destP);
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+HorizLine::HorizLine(CGEEngine *vm) : Sprite(vm, NULL), _vm(vm) {
+ // Set the sprite list
+ BitmapPtr *HL = new BitmapPtr[2];
+ HL[0] = new Bitmap(_vm, "HLINE");
+ HL[1] = NULL;
+
+ setShapeList(HL);
+}
+
+SceneLight::SceneLight(CGEEngine *vm) : Sprite(vm, NULL), _vm(vm) {
+ // Set the sprite list
+ BitmapPtr *PR = new BitmapPtr[2];
+ PR[0] = new Bitmap(_vm, "PRESS");
+ PR[1] = NULL;
+
+ setShapeList(PR);
+}
+
+Speaker::Speaker(CGEEngine *vm): Sprite(vm, NULL), _vm(vm) {
+ // Set the sprite list
+ BitmapPtr *SP = new BitmapPtr[3];
+ SP[0] = new Bitmap(_vm, "SPK_L");
+ SP[1] = new Bitmap(_vm, "SPK_R");
+ SP[2] = NULL;
+
+ setShapeList(SP);
+}
+
+PocLight::PocLight(CGEEngine *vm): Sprite(vm, NULL), _vm(vm) {
+ // Set the sprite list
+ BitmapPtr *LI = new BitmapPtr[5];
+ LI[0] = new Bitmap(_vm, "LITE0");
+ LI[1] = new Bitmap(_vm, "LITE1");
+ LI[2] = new Bitmap(_vm, "LITE2");
+ LI[3] = new Bitmap(_vm, "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..0c514c4a66
--- /dev/null
+++ b/engines/cge/vga13h.h
@@ -0,0 +1,243 @@
+/* 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;
+};
+
+class SprExt {
+public:
+ int _x0;
+ int _y0;
+ int _x1;
+ int _y1;
+ BitmapPtr _b0;
+ BitmapPtr _b1;
+ BitmapPtr *_shpList;
+ Seq *_seq;
+ char *_name;
+ CommandHandler::Command *_near;
+ CommandHandler::Command *_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 _scene;
+ 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);
+ CommandHandler::Command *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);
+ 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();
+ uint8 closest(Dac *pal, const uint8 colR, const uint8 colG, const uint8 colB);
+
+public:
+ uint32 _frmCnt;
+ Queue *_showQ;
+ Queue *_spareQ;
+ int _mono;
+ Graphics::Surface *_page[4];
+ Dac *_sysPal;
+
+ Vga();
+ ~Vga();
+
+ uint8 *glass(Dac *pal, const uint8 colR, const uint8 colG, const uint8 colB);
+ 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();
+
+ void palToDac(const byte *palData, Dac *tab);
+ void dacToPal(const Dac *tab, byte *palData);
+};
+
+class HorizLine: public Sprite {
+ CGEEngine *_vm;
+public:
+ HorizLine(CGEEngine *vm);
+};
+
+class SceneLight: public Sprite {
+ CGEEngine *_vm;
+public:
+ SceneLight(CGEEngine *vm);
+};
+
+class Speaker: public Sprite {
+ CGEEngine *_vm;
+public:
+ Speaker(CGEEngine *vm);
+};
+
+class PocLight: public Sprite {
+ CGEEngine *_vm;
+public:
+ PocLight(CGEEngine *vm);
+};
+
+} // End of namespace CGE
+
+#endif
diff --git a/engines/cge/vmenu.cpp b/engines/cge/vmenu.cpp
new file mode 100644
index 0000000000..a317a765d4
--- /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(_vm, 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));
+ _vm->_vga->_showQ->insert(this, _vm->_vga->_showQ->last());
+ _bar = new MenuBar(_vm, _w - 2 * kTextHMargin);
+ _bar->gotoxy(_x + kTextHMargin - kMenuBarHM, _y + kTextVMargin - kMenuBarVM);
+ _vm->_vga->_showQ->insert(_bar, _vm->_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;
+ _vm->_commandHandlerTurbo->addCommand(kCmdKill, -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..89ef7a9484
--- /dev/null
+++ b/engines/cge/vmenu.h
@@ -0,0 +1,73 @@
+/* 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..31ea1909d8
--- /dev/null
+++ b/engines/cge/walk.cpp
@@ -0,0 +1,267 @@
+/* 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 {
+
+Cluster::Cluster(CGEEngine *vm, int16 a, int16 b) : _vm(vm) {
+ _pt = Common::Point(a, b);
+}
+
+Cluster::Cluster(CGEEngine *vm) : _vm(vm) {
+ _pt = Common::Point(-1, -1);
+}
+
+uint8 &Cluster::cell() {
+ return _vm->_clusterMap[_pt.y][_pt.x];
+}
+
+bool Cluster::isValid() const {
+ return (_pt.x >= 0) && (_pt.x < kMapXCnt) && (_pt.y >= 0) && (_pt.y < kMapZCnt);
+}
+
+Walk::Walk(CGEEngine *vm, BitmapPtr *shpl)
+ : Sprite(vm, shpl), _dir(kDirNone), _tracePtr(-1), _level(0), _target(-1, -1), _findLevel(-1), _here(vm), _vm(vm) {
+ for (int i = 0; i < kMaxFindLevel; i++) {
+ Cluster *tmpClust = new Cluster(_vm);
+ _trace.push_back(tmpClust);
+ }
+}
+
+Walk::~Walk() {
+ for (uint idx = 0; idx < _trace.size(); ++idx)
+ delete _trace[idx];
+}
+
+void Walk::tick() {
+ if (_flags._hide)
+ return;
+
+ _here = _vm->XZ(_x + _w / 2, _y + _h);
+
+ if (_dir != kDirNone) {
+ _vm->_sys->funTouch();
+ for (Sprite *spr = _vm->_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;
+ _vm->_commandHandlerTurbo->addCommand(kCmdZTrim, -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(_vm, 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(_vm, (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) {
+ _vm->_hero->findWay(spr);
+ if (mode < 0) {
+ mode = spr->_flags._east;
+ if (lower(spr))
+ mode += 2;
+ }
+ }
+ // note: insert SNAIL commands in reverse order
+ _vm->_commandHandler->insertCommand(kCmdPause, -1, 64, NULL);
+ _vm->_commandHandler->insertCommand(kCmdSeq, -1, kTSeq + mode, this);
+ if (spr) {
+ _vm->_commandHandler->insertCommand(kCmdWait, -1, -1, _vm->_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 <= kSceneMax);
+ 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(_vm, -1, 0), Cluster(_vm, 1, 0), Cluster(_vm, 0, -1), Cluster(_vm, 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]->_pt = start._pt;
+ 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..99dc362eec
--- /dev/null
+++ b/engines/cge/walk.h
@@ -0,0 +1,87 @@
+/* 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 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:
+ CGEEngine *_vm;
+ Common::Point _pt;
+public:
+ uint8 &cell();
+ Cluster(CGEEngine *vm, int16 a, int16 b);
+ Cluster(CGEEngine *vm);
+ 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;
+ Common::Array<Cluster *> _trace;
+
+ Dir _dir;
+ Walk(CGEEngine *vm, BitmapPtr *shpl);
+ ~Walk();
+ 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);
+};
+
+} // End of namespace CGE
+
+#endif