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