diff options
30 files changed, 6001 insertions, 81 deletions
diff --git a/engines/cge/cge.h b/engines/cge/cge.h index a65069ff46..c43358f252 100644 --- a/engines/cge/cge.h +++ b/engines/cge/cge.h @@ -80,12 +80,6 @@ class Talk; #define kSayTheEnd 41 -enum GameType { - kGameTypeNone = 0, - kGameTypeSoltys, - kGameTypeSfinx -}; - // our engine debug channels enum { kCGEDebugBitmap = 1 << 0, diff --git a/engines/cge/detection.cpp b/engines/cge/detection.cpp index 4c2f81c1ae..60558f147e 100644 --- a/engines/cge/detection.cpp +++ b/engines/cge/detection.cpp @@ -33,7 +33,6 @@ namespace CGE { struct CgeGameDescription { ADGameDescription desc; - GameType gameType; }; #define GAMEOPTION_COLOR_BLIND_DEFAULT_OFF GUIO_GAMEOPTIONS1 @@ -42,117 +41,81 @@ struct CgeGameDescription { static const PlainGameDescriptor CGEGames[] = { { "soltys", "Soltys" }, - { "sfinx", "Sfinx" }, { 0, 0 } }; namespace CGE { -static const CgeGameDescription gameDescriptions[] = { - - { - { - "soltys", "", - { - {"vol.cat", 0, "0c33e2c304821a2444d297fc5e2d67c6", 50176}, - {"vol.dat", 0, "f9ae2e7f8f7cac91378cdafca43faf1e", 8437572}, - AD_LISTEND - }, - Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO0() - }, - kGameTypeSoltys - }, +static const ADGameDescription gameDescriptions[] = { { + "soltys", "", { - "soltys", "Soltys Freeware", - { - {"vol.cat", 0, "0c33e2c304821a2444d297fc5e2d67c6", 50176}, - {"vol.dat", 0, "f9ae2e7f8f7cac91378cdafca43faf1e", 8437676}, - AD_LISTEND - }, - Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) + {"vol.cat", 0, "0c33e2c304821a2444d297fc5e2d67c6", 50176}, + {"vol.dat", 0, "f9ae2e7f8f7cac91378cdafca43faf1e", 8437572}, + AD_LISTEND }, - kGameTypeSoltys + Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO0() }, { + "soltys", "Soltys Freeware", { - "soltys", "Soltys Demo (not supported)", - { - {"vol.cat", 0, "1e077c8ff58109a187f07ac54b0c873a", 18788}, - {"vol.dat", 0, "75d385a6074c58b69f7730481f256051", 1796710}, - AD_LISTEND - }, - Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO , GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) + {"vol.cat", 0, "0c33e2c304821a2444d297fc5e2d67c6", 50176}, + {"vol.dat", 0, "f9ae2e7f8f7cac91378cdafca43faf1e", 8437676}, + AD_LISTEND }, - kGameTypeSoltys + Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) }, { + "soltys", "Soltys Demo (not supported)", { - "soltys", "Soltys Demo (not supported)", - { - {"vol.cat", 0, "f17987487fab1ebddd781d8d02fedecc", 7168}, - {"vol.dat", 0, "c5d9b15863cab61dc125551576dece04", 1075272}, - AD_LISTEND - }, - Common::PL_POL, Common::kPlatformDOS, ADGF_DEMO , GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) + {"vol.cat", 0, "1e077c8ff58109a187f07ac54b0c873a", 18788}, + {"vol.dat", 0, "75d385a6074c58b69f7730481f256051", 1796710}, + AD_LISTEND }, - kGameTypeSoltys + Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO , GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) }, { + "soltys", "Soltys Demo (not supported)", { - "soltys", "Soltys Freeware v1.0", - { - {"vol.cat", 0, "f1675684c68ab90272f5776f8f2c3974", 50176}, - {"vol.dat", 0, "4ffeff4abc99ac5999b55ccfc56ab1df", 8430868}, - AD_LISTEND - }, - Common::EN_ANY, Common::kPlatformDOS, ADGF_NO_FLAGS , GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) + {"vol.cat", 0, "f17987487fab1ebddd781d8d02fedecc", 7168}, + {"vol.dat", 0, "c5d9b15863cab61dc125551576dece04", 1075272}, + AD_LISTEND }, - kGameTypeSoltys + Common::PL_POL, Common::kPlatformDOS, ADGF_DEMO , GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) }, { + "soltys", "Soltys Freeware v1.0", { - "soltys", "Soltys Freeware v1.0", - { - {"vol.cat", 0, "20fdce799adb618100ef9ee2362be875", 50176}, - {"vol.dat", 0, "0e43331c846094d77f5dd201827e0a3b", 8439339}, - AD_LISTEND - }, - Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) + {"vol.cat", 0, "f1675684c68ab90272f5776f8f2c3974", 50176}, + {"vol.dat", 0, "4ffeff4abc99ac5999b55ccfc56ab1df", 8430868}, + AD_LISTEND }, - kGameTypeSoltys + Common::EN_ANY, Common::kPlatformDOS, ADGF_NO_FLAGS , GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) }, { + "soltys", "Soltys Freeware v1.0", { - "soltys", "Soltys Freeware v1.0", - { - {"vol.cat", 0, "fcae86b20eaa5cedec17b24fa5e85eb4", 50176}, - {"vol.dat", 0, "ff10d54acc2c95696c57e05819b6906f", 8450151}, - AD_LISTEND - }, - Common::ES_ESP, Common::kPlatformDOS, ADGF_NO_FLAGS , GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) + {"vol.cat", 0, "20fdce799adb618100ef9ee2362be875", 50176}, + {"vol.dat", 0, "0e43331c846094d77f5dd201827e0a3b", 8439339}, + AD_LISTEND }, - kGameTypeSoltys + Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) }, { + "soltys", "Soltys Freeware v1.0", { - // Polish version, provided by Strangerke - "sfinx", "Sfinx Freeware", - { - {"vol.cat", 0, "21197b287d397c53261b6616bf0dd880", 129024}, - {"vol.dat", 0, "de14291869a8eb7c2732ab783c7542ef", 34180844}, - AD_LISTEND - }, - Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) + {"vol.cat", 0, "fcae86b20eaa5cedec17b24fa5e85eb4", 50176}, + {"vol.dat", 0, "ff10d54acc2c95696c57e05819b6906f", 8450151}, + AD_LISTEND }, - kGameTypeSfinx + Common::ES_ESP, Common::kPlatformDOS, ADGF_NO_FLAGS , GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) }, - {AD_TABLE_END_MARKER, kGameTypeNone} + AD_TABLE_END_MARKER }; static const ADFileBasedFallback fileBasedFallback[] = { - { &gameDescriptions[0].desc, { "vol.cat", "vol.dat", 0 } }, + { &gameDescriptions[0], { "vol.cat", "vol.dat", 0 } }, { 0, { 0 } } }; } // End of namespace CGE diff --git a/engines/cge2/bitmap.cpp b/engines/cge2/bitmap.cpp new file mode 100644 index 0000000000..10fd6f1163 --- /dev/null +++ b/engines/cge2/bitmap.cpp @@ -0,0 +1,388 @@ +/* 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 Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "cge2/bitmap.h" +#include "cge2/cge2.h" +#include "cge2/vga13h.h" +//#include "cge/cge_main.h" +#include "common/system.h" +#include "common/debug.h" +#include "common/debug-channels.h" + +namespace CGE2 { + +Bitmap::Bitmap(CGE2Engine *vm, const char *fname) : _m(NULL), _v(NULL), _map(0), _vm(vm) { + char pat[kMaxPath]; + forceExt(pat, fname, ".VBM"); + + if (_vm->_resman->exist(pat)) { + EncryptedStream file(_vm, pat); + if (file.err()) + error("Unable to find VBM [%s]", fname); + if (!loadVBM(&file)) + error("Bad VBM [%s]", fname); + } else { + error("Bad VBM [%s]", fname); + } +} + +Bitmap::Bitmap(CGE2Engine *vm, uint16 w, uint16 h, uint8 *map) : _w(w), _h(h), _m(map), _v(NULL), _map(0), _b(NULL), _vm(vm) { + 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(CGE2Engine *vm, uint16 w, uint16 h, uint8 fill) + : _w((w + 3) & ~3), // only full uint32 allowed! + _h(h), _m(NULL), _map(0), _b(NULL), _vm(vm) { + + 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); + + WRITE_LE_UINT16(v, (kBmpCPY | dsiz)); // data chunk hader + memset(v + 2, fill, dsiz); // data bytes + WRITE_LE_UINT16(v + lsiz - 2, (kBmpSKP | ((kScrWidth / 4) - dsiz))); // gap + + // Replicate lines + byte *destP; + for (destP = v + lsiz; destP < (v + psiz); destP += lsiz) + Common::copy(v, v + lsiz, destP); + + WRITE_LE_UINT16(v + psiz - 2, 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(CGE2Engine *vm, const Bitmap &bmp) : _w(bmp._w), _h(bmp._h), _m(NULL), _v(NULL), _map(0), _b(NULL), _vm(vm) { + 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() { + release(); +} + +void Bitmap::release() { + free(_m); + delete[] _v; +} + +Bitmap &Bitmap::operator=(const Bitmap &bmp) { + if (this == &bmp) + return *this; + + uint8 *v0 = bmp._v; + _w = bmp._w; + _h = bmp._h; + _m = NULL; + _map = 0; + _vm = bmp._vm; + delete[] _v; + + if (v0 == NULL) { + _v = NULL; + } else { + 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); + } + return *this; +} + +char *Bitmap::forceExt(char *buf, const char *name, const char *ext) { + strcpy(buf, name); + char *dot = strrchr(buf, '.'); + if (dot) + *dot = '\0'; + strcat(buf, ext); + + return buf; +} + +BitmapPtr Bitmap::code() { + if (!_m) + return NULL; + + 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) + WRITE_LE_UINT16(cp, 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) + WRITE_LE_UINT16(cp, cnt); + + cp = (uint16 *) im; + im += 2; + skip = true; + cnt = (kScrWidth - j + 3) / 4; + } + } + } + if (cnt && ! skip) { + cnt |= kBmpCPY; + if (_v) + WRITE_LE_UINT16(cp, cnt); + + cp = (uint16 *) im; + im += 2; + } + if (_v) + WRITE_LE_UINT16(cp, 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) { + 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--; + // No break on purpose + case kBmpSKP: + w = 0; + break; + case kBmpREP: + w = 1; + break; + } + m += w; + } + + while (true) { + uint16 w, t; + + w = READ_LE_UINT16(m); + m += 2; + t = w & 0xC000; + w &= 0x3FFF; + + if (n > n0) + return false; + + n += w; + switch (t) { + case kBmpEOI: + return false; + case kBmpSKP: + w = 0; + break; + case kBmpREP: + case kBmpCPY: + if (n - w <= n0 && n > n0) + return true; + break; + } + m += ((t == kBmpREP) ? 1 : w); + } +} + +bool Bitmap::loadVBM(EncryptedStream *f) { + uint16 p = 0, n = 0; + if (!f->err()) + f->read((uint8 *)&p, sizeof(p)); + p = FROM_LE_16(p); + + if (!f->err()) + f->read((uint8 *)&n, sizeof(n)); + n = FROM_LE_16(n); + + if (!f->err()) + f->read((uint8 *)&_w, sizeof(_w)); + _w = FROM_LE_16(_w); + + if (!f->err()) + f->read((uint8 *)&_h, sizeof(_h)); + _h = FROM_LE_16(_h); + + if (!f->err()) { + if (p) { + if (_vm->_bitmapPalette) { + // Read in the palette + byte palData[kPalSize]; + f->read(palData, kPalSize); + + const byte *srcP = palData; + for (int idx = 0; idx < kPalCount; idx++, srcP += 3) { + _vm->_bitmapPalette[idx]._r = *srcP; + _vm->_bitmapPalette[idx]._g = *(srcP + 1); + _vm->_bitmapPalette[idx]._b = *(srcP + 2); + } + } else + f->seek(f->pos() + kPalSize); + } + } + if ((_v = new uint8[n]) == NULL) + return false; + + if (!f->err()) + f->read(_v, n); + + _b = (HideDesc *)(_v + n - _h * sizeof(HideDesc)); + return (!f->err()); +} + +void Bitmap::xLatPos(V2D& p) { + p.x -= (_w >> 1); + p.y = kWorldHeight - p.y - _h; +} + +bool Bitmap::moveHi(void) { + warning("STUB: Bitmap::moveHi()"); + return true; +} + +bool Bitmap::moveLo(void) { + warning("STUB: Bitmap::moveLo()"); + return true; +} + +} // End of namespace CGE2 diff --git a/engines/cge2/bitmap.h b/engines/cge2/bitmap.h new file mode 100644 index 0000000000..82c3b07e31 --- /dev/null +++ b/engines/cge2/bitmap.h @@ -0,0 +1,92 @@ +/* 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 Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_BITMAP_H +#define CGE2_BITMAP_H + +#include "cge2/general.h" +#include "common/file.h" + +namespace CGE2 { + +class CGE2Engine; +class EncryptedStream; +class V2D; + +#define kMaxPath 128 +enum { + kBmpEOI = 0x0000, + kBmpSKP = 0x4000, + kBmpREP = 0x8000, + kBmpCPY = 0xC000 +}; + +#include "common/pack-start.h" + +struct HideDesc { + uint16 _skip; + uint16 _hide; +}; + +#include "common/pack-end.h" + +class Bitmap { + CGE2Engine *_vm; + char *forceExt(char *buf, const char *name, const char *ext); + bool loadVBM(EncryptedStream *f); +public: + uint16 _w; + uint16 _h; + uint8 *_m; + uint8 *_v; + int32 _map; + HideDesc *_b; + + Bitmap(CGE2Engine *vm, const char *fname); + Bitmap(CGE2Engine *vm, uint16 w, uint16 h, uint8 *map); + Bitmap(CGE2Engine *vm, uint16 w, uint16 h, uint8 fill); + Bitmap(CGE2Engine *vm, const Bitmap &bmp); + ~Bitmap(); + + Bitmap *code(); + Bitmap &operator=(const Bitmap &bmp); + void release(); + void hide(int16 x, int16 y); + void show(int16 x, int16 y); + void xShow(int16 x, int16 y); + bool solidAt(int16 x, int16 y); + void xLatPos(V2D& p); + bool moveHi(void); + bool moveLo(void); +}; + + +typedef Bitmap *BitmapPtr; + +} // End of namespace CGE2 + +#endif // CGE2_BITMAP_H diff --git a/engines/cge2/cge2.cpp b/engines/cge2/cge2.cpp new file mode 100644 index 0000000000..273c9dac0d --- /dev/null +++ b/engines/cge2/cge2.cpp @@ -0,0 +1,182 @@ +/* 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 Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "engines/util.h" + +#include "cge2/cge2.h" +#include "cge2/bitmap.h" +#include "cge2/vga13h.h" +#include "cge2/sound.h" +#include "cge2/text.h" +#include "cge2/hero.h" +#include "cge2/general.h" +#include "cge2/spare.h" +#include "cge2/events.h" +#include "cge2/talk.h" +#include "cge2/cge2_main.h" + +namespace CGE2 { + +CGE2Engine::CGE2Engine(OSystem *syst, const ADGameDescription *gameDescription) + : Engine(syst), _gameDescription(gameDescription), _randomSource("cge") { + _resman = nullptr; + _vga = nullptr; + _sprite = nullptr; + _midiPlayer = nullptr; + _fx = nullptr; + _sound = nullptr; + _text = nullptr; + for (int i = 0; i < 2; i++) + _heroTab[i] = nullptr; + _eye = nullptr; + for (int i = 0; i < kCaveMax; i++) + _eyeTab[i] = nullptr; + _spare = nullptr; + _commandHandler = nullptr; + _commandHandlerTurbo = nullptr; + _infoLine = nullptr; + _mouse = nullptr; + _keyboard = nullptr; + _talk = nullptr; + for (int i = 0; i < kMaxPoint; i++) + _point[i] = nullptr; + _sys = nullptr; + _busyPtr = nullptr; + for (int i = 0; i < 2; i++) + _vol[i] = nullptr; + + _quitFlag = false; + _bitmapPalette = nullptr; + _mode = 0; + _music = true; + _startupMode = 1; + _now = 1; + _sex = true; + _mouseTop = kWorldHeight / 3; + _dark = false; + _lastFrame = 0; + _lastTick = 0; + _waitSeq = 0; + _waitRef = 0; + _commandStat._wait = nullptr; + _commandStat._ref[0] = 0; + _commandStat._ref[1] = 0; + _taken = false; + _endGame = false; + for (int i = 0; i < 4; i++) + _flag[i] = false; +} + +void CGE2Engine::init() { + _resman = new ResourceManager(); + _vga = new Vga(this); + _fx = new Fx(this, 16); + _sound = new Sound(this); + _midiPlayer = new MusicPlayer(this); + _text = new Text(this, "CGE"); + for (int i = 0; i < 2; i++) + _heroTab[i] = new HeroTab(this); + _eye = new V3D(); + for (int i = 0; i < kCaveMax; i++) + _eyeTab[i] = new V3D(); + _spare = new Spare(this); + _commandHandler = new CommandHandler(this, false); + _commandHandlerTurbo = new CommandHandler(this, true); + _infoLine = new InfoLine(this, kInfoW); + _mouse = new Mouse(this); + _keyboard = new Keyboard(this); + for (int i = 0; i < kMaxPoint; i++) + _point[i] = new V3D(); + _sys = new System(this); +} + +void CGE2Engine::deinit() { + delete _resman; + delete _vga; + delete _fx; + delete _sound; + delete _midiPlayer; + delete _text; + for (int i = 0; i < 2; i++) + delete _heroTab[i]; + for (int i = 0; i < kCaveMax; i++) { + if (_eye == _eyeTab[i]) + _eye = nullptr; + delete _eyeTab[i]; + } + if (_eye != nullptr) + delete _eye; + delete _spare; + delete _sprite; + delete _commandHandler; + delete _commandHandlerTurbo; + delete _infoLine; + delete _mouse; + delete _keyboard; + if (_talk != nullptr) + delete _talk; + for (int i = 0; i < kMaxPoint; i++) { + delete _point[i]; + } + delete _sys; +} + +bool CGE2Engine::hasFeature(EngineFeature f) const { + return false; +} + +bool CGE2Engine::canLoadGameStateCurrently() { + return false; +} +bool CGE2Engine::canSaveGameStateCurrently() { + return false; +} + +Common::Error CGE2Engine::loadGameState(int slot) { + warning("STUB: CGE2Engine::loadGameState()"); + return Common::kNoError; +} + +Common::Error CGE2Engine::saveGameState(int slot, const Common::String &desc) { + warning("STUB: CGE2Engine::saveGameState()"); + return Common::kNoError; +} + +Common::Error CGE2Engine::run() { + warning("STUB: CGE2Engine::run()"); + + initGraphics(kScrWidth, kScrHeight, false); + + init(); + + cge2_main(); + + deinit(); + return Common::kNoError; +} + +} // End of namespace CGE2 diff --git a/engines/cge2/cge2.h b/engines/cge2/cge2.h new file mode 100644 index 0000000000..66bcd911e9 --- /dev/null +++ b/engines/cge2/cge2.h @@ -0,0 +1,244 @@ +/* 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 Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_H +#define CGE2_H + +#include "common/random.h" +#include "engines/engine.h" +#include "engines/advancedDetector.h" +#include "common/system.h" +#include "cge2/fileio.h" + +namespace CGE2 { + +class Vga; +class Sprite; +class MusicPlayer; +class Fx; +class Sound; +class Text; +struct HeroTab; +class V3D; +class V2D; +struct Dac; +class Spare; +class CommandHandler; +class InfoLine; +class Mouse; +class Keyboard; +class Talk; +class Hero; +class Bitmap; +class System; + +#define kScrWidth 320 +#define kScrHeight 240 +#define kScrDepth 480 +#define kPanHeight 40 +#define kWorldHeight (kScrHeight - kPanHeight) +#define kMaxFile 128 +#define kPathMax 128 +#define kDimMax 8 +#define kWayMax 10 +#define kPocketMax 4 +#define kCaveMax 100 +#define kMaxPoint 4 +#define kInfoX 160 +#define kInfoY -11 +#define kInfoW 180 + +enum CallbackType { + kNullCB = 0, kQGame, kMiniStep, kXScene, kSoundSetVolume +}; + +enum Action { kNear, kMTake, kFTake, kActions }; + +class Font { + char _path[kPathMax]; + void load(); + CGE2Engine *_vm; +public: + uint8 *_widthArr; + uint16 *_pos; + uint8 *_map; + Font(CGE2Engine *vm, const char *name); + ~Font(); + uint16 width(const char *text); + void save(); +}; + +class CGE2Engine : public Engine { +private: + uint32 _lastFrame, _lastTick; + void tick(); +public: + CGE2Engine(OSystem *syst, const ADGameDescription *gameDescription); + 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); + virtual Common::Error run(); + + bool showTitle(const char *name); + void cge2_main(); + char *mergeExt(char *buf, const char *name, const char *ext); + void inf(const char *text, bool wideSpace = false); + void movie(const char *ext); + void runGame(); + void loadScript(const char *fname); + void loadSprite(const char *fname, int ref, int scene, V3D &pos); + void badLab(const char *fn); + void caveUp(int cav); + void switchCave(int cav); + void showBak(int ref); + void loadTab(); + int newRandom(int range); + void loadMap(int cav); + void openPocket(); + void selectPocket(int n); + void busy(bool on); + void feedSnail(Sprite *spr, Action snq, Hero *hero); + int freePockets(int sx); + int findActivePocket(int ref); + void pocFul(); + void killText(); + void mainLoop(); + void handleFrame(); + Sprite *locate(int ref); + bool isHero(Sprite *spr); + void loadUser(); + void checkSaySwitch(); + void qGame(); + void loadPos(); + void releasePocket(Sprite *spr); + void switchHero(bool sex); + + void setEye(V3D &e); + void setEye(const V2D& e2, int z = -kScrWidth); + void setEye(const char *s); + + int number(char *s); + char *token(char *s); + char *tail(char *s); + int takeEnum(const char **tab, const char *text); + ID ident(const char *s); + bool testBool(char *s); + + void snKill(Sprite *spr); + void snHide(Sprite *spr, int val); + void snMidi(int val); + void snSetDlg(int clr, int set); + void snMskDlg(int clr, int set); + void snSeq(Sprite *spr, int val); + void snRSeq(Sprite *spr, int val); + void snSend(Sprite *spr, int val); + void snSwap(Sprite *spr, int val); + void snCover(Sprite *spr, int val); + void snUncover(Sprite *spr, Sprite *spr2); + void snFocus(int val); + void snKeep(Sprite *spr, int val); + void snGive(Sprite *spr, int val); + void snGoto(Sprite *spr, int val); + void snMove(Sprite *spr, V3D pos); + void snSlave(Sprite *spr, int val); + void snTrans(Sprite *spr, int val); + void snPort(Sprite *spr, int val); + void snMouse(int val); + void snNNext(Sprite *spr, Action act, int val); + void snRNNext(Sprite *spr, int val); + void snRMTNext(Sprite *spr, int val); + void snRFTNext(Sprite *spr, int val); + void snRmNear(Sprite *spr); + void snRmMTake(Sprite *spr); + void snRmFTake(Sprite *spr); + void snFlag(int ref, int val); + void snSetRef(Sprite *spr, int val); + void snBackPt(Sprite *spr, int val); + void snFlash(int val); + void snLight(int val); + void snWalk(Sprite *spr, int val); + void snReach(Sprite *spr, int val); + void snSound(Sprite *spr, int wav); + void snRoom(Sprite *spr, int val); + void snGhost(Bitmap *bmp); + + void hide1(Sprite *spr); + + const ADGameDescription *_gameDescription; + + Common::RandomSource _randomSource; + + bool _quitFlag; + Dac *_bitmapPalette; + int _mode; + bool _music; + int _startupMode; + int _now; + bool _sex; + int _mouseTop; + bool _dark; + int _waitSeq; + int _waitRef; + struct CommandStat { + int *_wait; + int _ref[2]; + } _commandStat; + bool _taken; + bool _endGame; + bool _flag[4]; + + ResourceManager *_resman; + Vga *_vga; + Sprite *_sprite; + MusicPlayer *_midiPlayer; + Fx *_fx; + Sound *_sound; + Text *_text; + HeroTab *_heroTab[2]; + V3D *_eye; + V3D *_eyeTab[kCaveMax]; + Spare *_spare; + CommandHandler *_commandHandler; + CommandHandler *_commandHandlerTurbo; + InfoLine *_infoLine; + Mouse *_mouse; + Keyboard *_keyboard; + Talk *_talk; + V3D *_point[kMaxPoint]; + System *_sys; + Sprite *_busyPtr; + Sprite *_vol[2]; +private: + void init(); + void deinit(); +}; + +} // End of namespace CGE2 + +#endif // CGE2_H diff --git a/engines/cge2/cge2_main.cpp b/engines/cge2/cge2_main.cpp new file mode 100644 index 0000000000..9e9d6da76a --- /dev/null +++ b/engines/cge2/cge2_main.cpp @@ -0,0 +1,759 @@ +/* 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 Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "sound.h" +#include "cge2/cge2_main.h" +#include "cge2/cge2.h" +#include "cge2/vga13h.h" +#include "cge2/text.h" +#include "cge2/snail.h" +#include "cge2/hero.h" +#include "cge2/spare.h" +#include "cge2/events.h" + +namespace CGE2 { + +System::System(CGE2Engine *vm) : Sprite(vm), _vm(vm) { + warning("STUB: System::System()"); +} + +void System::touch(uint16 mask, int x, int y, Common::KeyCode keyCode) { + warning("STUB: System::touch()"); +} + +void System::tick() { + warning("STUB: System::tick()"); +} + +int CGE2Engine::number(char *s) { // TODO: Rework it later to include the preceding token() call! + int r = atoi(s); + char *pp = strchr(s, ':'); + if (pp) + r = (r << 8) + atoi(pp + 1); + return r; +} + +char *CGE2Engine::token(char *s) { + return strtok(s, " =\t,;/()"); +} + +char *CGE2Engine::tail(char *s) { + if (s && (*s == '=')) + s++; + return s; +} + +int CGE2Engine::takeEnum(const char **tab, const char *text) { + if (text) { + for (const char **e = tab; *e; e++) { + if (scumm_stricmp(text, *e) == 0) { + return e - tab; + } + } + } + return -1; +} + +ID CGE2Engine::ident(const char *s) { + return ID(takeEnum(EncryptedStream::kIdTab, s)); +} + +bool CGE2Engine::testBool(char *s) { + return number(s) != 0; +} + +void CGE2Engine::badLab(const char *fn) { + error("Misplaced label in %s!", fn); +} + +void CGE2Engine::loadSprite(const char *fname, int ref, int scene, V3D &pos) { + int shpcnt = 0; + int seqcnt = 0; + int cnt[kActions]; + for (int i = 0; i < kActions; i++) + cnt[i] = 0; + ID section = kIdPhase; + bool frnt = true; + bool east = false; + bool port = false; + bool tran = false; + Hero *h; + ID id; + + char tmpStr[kLineMax + 1]; + mergeExt(tmpStr, fname, kSprExt); + + if (_resman->exist(tmpStr)) { // sprite description file exist + EncryptedStream sprf(this, tmpStr); + if (sprf.err()) + error("Bad SPR [%s]", tmpStr); + + int label = kNoByte; + Common::String line; + + for (line = sprf.readLine(); !sprf.eos(); line = sprf.readLine()){ + if (line.size() == 0) + continue; + Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr)); + + char *p; + p = token(tmpStr); + if (*p == '@') { + if (label != kNoByte) + badLab(fname); + label = atoi(p + 1); + continue; + } + + id = ident(p); + switch (id) { + case kIdName: // will be taken in Expand routine + if (label != kNoByte) + badLab(fname); + break; + case kIdType: + if (label != kNoByte) + badLab(fname); + break; + case kIdNear: + case kIdMTake: + case kIdFTake: + case kIdPhase: + case kIdSeq: + if (label != kNoByte) + badLab(fname); + section = id; + break; + case kIdFront: + if (label != kNoByte) + badLab(fname); + p = token(nullptr); + frnt = testBool(p); + break; + case kIdEast: + if (label != kNoByte) + badLab(fname); + p = token(nullptr); + east = testBool(p); + break; + case kIdPortable: + if (label != kNoByte) + badLab(fname); + p = token(nullptr); + port = testBool(p); + break; + case kIdTransparent: + if (label != kNoByte) + badLab(fname); + p = token(nullptr); + tran = testBool(p); + break; + default: + if (id >= kIdNear) + break; + switch (section) { + case kIdNear: + case kIdMTake: + case kIdFTake: + if (_commandHandler->com(p) >= 0) + ++cnt[section]; + else + error("Bad line %d [%s]", sprf.getLineCount(), tmpStr); + break; + case kIdPhase: + if (label != kNoByte) + badLab(fname); + ++shpcnt; + break; + case kIdSeq: + if (label != kNoByte) + badLab(fname); + ++seqcnt; + break; + } + break; + } + label = kNoByte; + } + + if (!shpcnt) { + error("No shapes - %s", fname); + } + } else // No sprite description: mono-shaped sprite with only .BMP file. + ++shpcnt; + + // Make sprite of choosen type: + char c = *fname | 0x20; + if (c >= 'a' && c <= 'z' && fname[1] == '0' && fname[2] == '\0') { + h = new Hero(this); + if (h) { + h->gotoxyz(pos); + _sprite = h; + } + } else { + if (_sprite) + delete _sprite; + _sprite = new Sprite(this); + if (_sprite) + _sprite->gotoxyz(pos); + } + + if (_sprite) { + _sprite->_ref = ref; + _sprite->_scene = scene; + + _sprite->_flags._frnt = frnt; + _sprite->_flags._east = east; + _sprite->_flags._port = port; + _sprite->_flags._tran = tran; + _sprite->_flags._kill = true; + + // Extract the filename, without the extension + Common::strlcpy(_sprite->_file, fname, sizeof(_sprite->_file)); + char *p = strchr(_sprite->_file, '.'); + if (p) + *p = '\0'; + + _sprite->_shpCnt = shpcnt; + _sprite->_seqCnt = seqcnt; + + for (int i = 0; i < kActions; i++) + _sprite->_actionCtrl[i]._cnt = cnt[i]; + } +} + +void CGE2Engine::loadScript(const char *fname) { + EncryptedStream scrf(this, fname); + + if (scrf.err()) + return; + + bool ok = true; + int lcnt = 0; + + char tmpStr[kLineMax + 1]; + Common::String line; + + for (line = scrf.readLine(); !scrf.eos(); line = scrf.readLine()) { + if (line.size() == 0) + continue; + + char *p; + + lcnt++; + Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr)); + + ok = false; // not OK if break + + V3D P; + + // sprite ident number + if ((p = token(tmpStr)) == NULL) + break; + int SpI = number(p); + + // sprite file name + char *SpN; + if ((SpN = token(nullptr)) == NULL) + break; + + // sprite scene + if ((p = token(nullptr)) == NULL) + break; + int SpA = number(p); + + // sprite column + if ((p = token(nullptr)) == NULL) + break; + P._x = number(p); + + // sprite row + if ((p = token(nullptr)) == NULL) + break; + P._y = number(p); + + // sprite Z pos + if ((p = token(nullptr)) == NULL) + break; + P._z = number(p); + + // sprite life + if ((p = token(nullptr)) == NULL) + break; + bool BkG = number(p) == 0; + + ok = true; // no break: OK + + _sprite = NULL; + loadSprite(SpN, SpI, SpA, P); + if (_sprite) { + if (BkG) + _sprite->_flags._back = true; + + int n = _spare->count(); + if (_spare->locate(_sprite->_ref) == nullptr) + _spare->store(_sprite); + _sprite = nullptr; + if (_spare->count() == n) + error("Durplicated reference! %s", SpN); + } + } + + if (!ok) + error("Bad INI line %d [%s]", scrf.getLineCount(), fname); +} + +void CGE2Engine::movie(const char *ext) { + assert(ext); + + if (_quitFlag) + return; + + char fn[12]; + sprintf(fn, "CGE.%s", (*ext == '.') ? ext + 1 : ext); + + if (_resman->exist(fn)) { + int now = _now; + _now = atoi(ext + 2); + loadScript(fn); + caveUp(_now); + + while (!_commandHandler->idle() && !_quitFlag) + mainLoop(); + + warning("STUB: CGE2Engine::movie()"); + + _commandHandler->addCommand(kCmdClear, -1, 0, nullptr); + _commandHandlerTurbo->addCommand(kCmdClear, -1, 0, nullptr); + _vga->_showQ->clear(); + _spare->clear(); + _now = now; + } +} + +void CGE2Engine::caveUp(int cav) { + _now = cav; + int bakRef = _now << 8; + if (_music) + _midiPlayer->loadMidi(bakRef); + showBak(bakRef); + _eye = _eyeTab[_now]; + _mouseTop = V2D(this, V3D(0, 1, kScrDepth)).y; + _spare->takeCave(_now); + openPocket(); + + warning("STUB: CGE2Engine::caveUp()"); + // TODO: Implement "Hero" things here! + + _sound->stop(); + _fx->clear(); + + selectPocket(-1); + _infoLine->setText(nullptr); + busy(false); + + if (!_dark) + _vga->sunset(); + _vga->show(); + _vga->copyPage(1, 0); + _vga->show(); + + _sprite = _vga->_showQ->first(); + _vga->sunrise(_vga->_sysPal); + + feedSnail(_vga->_showQ->locate(bakRef + 255), kNear, _heroTab[_sex]->_ptr); + //setDrawColors(); - It's only for debugging purposes. Can be left out for now. +} + +void CGE2Engine::switchCave(int cav) { + warning("STUB: CGE2Engine::switchCave()"); +} + +void CGE2Engine::showBak(int ref) { + Sprite *spr = _spare->locate(ref); + if (spr != nullptr) { + _bitmapPalette = _vga->_sysPal; + spr->expand(); + _bitmapPalette = NULL; + spr->show(2); + _vga->copyPage(1, 2); + _spare->dispose(spr); + } +} + +void CGE2Engine::mainLoop() { + _vga->show(); + _commandHandlerTurbo->runCommand(); + _commandHandler->runCommand(); + + // Handle a delay between game frames + handleFrame(); + + // Handle any pending events + //_eventManager->poll(); + warning("STUB: CGE2Engine::mainLoop() - Event handling is missing!"); + + // Check shouldQuit() + _quitFlag = shouldQuit(); +} + +void CGE2Engine::handleFrame() { + // Game frame delay + uint32 millis = g_system->getMillis(); + while (!_quitFlag && (millis < (_lastFrame + kGameFrameDelay))) { + // Handle any pending events + //_eventManager->poll(); + warning("STUB: CGE2Engine::handleFrame() - Event handling is missing!"); + + 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; + } +} + +Sprite *CGE2Engine::locate(int ref) { + _taken = false; + Sprite *spr = _vga->_showQ->locate(ref); + if (!spr) { + spr = _spare->locate(ref); + if (spr) + _taken = true; + } + return spr; +} + +bool CGE2Engine::isHero(Sprite *spr) { + return spr && spr->_ref / 10 == 14; +} + +void CGE2Engine::tick() { + for (Sprite *spr = _vga->_showQ->first(); spr; spr = spr->_next) { + if (spr->_time) { + if (--spr->_time == 0) + spr->tick(); + } + if (_waitRef) { + if (_waitRef == _sprite->_ref) + if (spr->seqTest(_waitSeq)) + _waitRef = 0; + } + } + + //Mouse->Tick(); + warning("STUB: CGE2Engine::tick() - Mouse"); +} + +void CGE2Engine::loadMap(int cav) { + warning("STUB: CGE2Engine::loadMap()"); +} + +void CGE2Engine::openPocket() { + warning("STUB: CGE2Engine::openPocket()"); +} + +void CGE2Engine::selectPocket(int n) { + warning("STUB: CGE2Engine::selectPocket()"); +} + +void CGE2Engine::busy(bool on) { + warning("STUB: CGE2Engine::selectPocket()"); +} + +void CGE2Engine::runGame() { + if (_quitFlag) + return; + + selectPocket(-1); + + loadUser(); + + _commandHandlerTurbo->addCommand(kCmdSeq, kMusicRef, _music, nullptr); + if (!_music) + _midiPlayer->killMidi(); + + checkSaySwitch(); + + _infoLine->gotoxyz(V3D(kInfoX, kInfoY, 0)); + _infoLine->setText(nullptr); + //_vga->_showQ->insert(_infoLine); + warning("STUB: CGE2Engine::runGame() - Info Line is missing!"); + + caveUp(_now); + _startupMode = 0; + _mouse->center(); + _mouse->off(); + _mouse->on(); + + _keyboard->setClient(_sys); + _commandHandler->addCommand(kCmdSeq, kPowerRef, 1, nullptr); + + _busyPtr = _vga->_showQ->locate(kBusyRef); + + _vol[0] = _vga->_showQ->locate(kDvolRef); + _vol[1] = _vga->_showQ->locate(kMvolRef); + + // these sprites are loaded with SeqPtr==0 (why?!) + if (_vol[0]) + _vol[0]->step((/*(int)SNDDrvInfo.VOL4.DL * */ _vol[0]->_seqCnt + _vol[0]->_seqCnt / 2) >> 4); + if (_vol[1]) + _vol[1]->step((/*(int)SNDDrvInfo.VOL4.ML * */ _vol[1]->_seqCnt + _vol[1]->_seqCnt / 2) >> 4); + // TODO: Recheck these! ^ + + // main loop + while (!_endGame && !_quitFlag) { + if (_flag[3]) // Flag FINIS + _commandHandler->addCallback(kCmdExec, -1, 0, kQGame); + mainLoop(); + } + + // If finishing game due to closing ScummVM window, explicitly save the game + if (!_endGame && canSaveGameStateCurrently()) + qGame(); + + _keyboard->setClient(nullptr); + _commandHandler->addCommand(kCmdClear, -1, 0, nullptr); + _commandHandlerTurbo->addCommand(kCmdClear, -1, 0, nullptr); + _mouse->off(); + _vga->_showQ->clear(); + _vga->_spareQ->clear(); +} + +void CGE2Engine::loadUser() { + warning("STUB: CGE2Engine::loadUser()"); + // Missing loading from file. TODO: Implement it with the saving/loading! + loadScript("CGE.INI"); + loadPos(); +} + +void CGE2Engine::loadPos() { + if (_resman->exist("CGE.HXY")) { + for (int cav = 0; cav < kCaveMax; cav++) + _heroTab[1]->_posTab[cav] = new V2D(this, 180, 10); + + EncryptedStream file(this, "CGE.HXY"); + + for (int cav = 0; cav < kCaveMax; cav++) { + _heroTab[0]->_posTab[cav]->x = file.readSint16LE(); + _heroTab[0]->_posTab[cav]->y = file.readSint16LE(); + } + + for (int cav = 0; cav < 41; cav++) { // (564 - 400) / 4 = 41 + _heroTab[1]->_posTab[cav]->x = file.readSint16LE(); + _heroTab[1]->_posTab[cav]->y = file.readSint16LE(); + } + } else + error("Missing file: CGE.HXY"); +} + +void CGE2Engine::releasePocket(Sprite *spr) { + warning("STUB: CGE2Engine::releasePocket()"); +} + +void CGE2Engine::checkSaySwitch() { + warning("STUB: CGE2Engine::checkSaySwitch()"); +} + +void CGE2Engine::qGame() { + warning("STUB: CGE2Engine::qGame()"); + _endGame = true; +} + +void CGE2Engine::loadTab() { + setEye(_text->getText(240)); + for (int i = 0; i < kCaveMax; i++) + _eyeTab[i] = _eye; + + if (_resman->exist(kTabName)) { + EncryptedStream f(this, kTabName); + for (int i = 0; i < kCaveMax; i++) { + for (int j = 0; j < 3; j++) { + signed b = f.readSint16BE(); + unsigned a = f.readUint16BE(); + uint16 round = uint16((long(a) << 16) / 100); + + if (round > 0x7FFF) + b++; + + switch (j) { + case 0: + _eyeTab[i]->_x = b; + break; + case 1: + _eyeTab[i]->_y = b; + break; + case 2: + _eyeTab[i]->_z = b; + break; + } + + } + } + } + + warning("STUB: CGE2Engine::loadTab() - Recheck this"); +} + +void CGE2Engine::cge2_main() { + warning("STUB: CGE2Engine::cge2_main()"); + + loadTab(); + + _mode++; + + if (showTitle("WELCOME")) { +#if 0 + if (_mode == 1) + movie(kIntroExt); +#endif + if (_text->getText(255) != NULL) { + runGame(); + _startupMode = 2; + } else + _vga->sunset(); + } else + _vga->sunset(); +} + +char *CGE2Engine::mergeExt(char *buf, const char *name, const char *ext) { + strcpy(buf, name); + char *dot = strrchr(buf, '.'); + if (!dot) + strcat(buf, ext); + + return buf; +} + +void CGE2Engine::setEye(V3D &e) { + _eye = &e; +} + +void CGE2Engine::setEye(const V2D& e2, int z) { + _eye->_x = e2.x; + _eye->_y = e2.y; + _eye->_z = z; +} + +void CGE2Engine::setEye(const char *s) { + char tempStr[kLineMax]; + strcpy(tempStr, s); + _eye->_x = atoi(token(tempStr)); + _eye->_y = atoi(token(NULL)); + _eye->_z = atoi(token(NULL)); +} + +int CGE2Engine::newRandom(int range) { + if (!range) + return 0; + + return _randomSource.getRandomNumber(range - 1); +} + +bool CGE2Engine::showTitle(const char *name) { + if (_quitFlag) + return false; + + _bitmapPalette = _vga->_sysPal; + BitmapPtr *LB = new BitmapPtr[2]; + LB[0] = new Bitmap(this, name); + LB[1] = NULL; + _bitmapPalette = NULL; + + Sprite D(this, LB, 1); + D._flags._kill = true; + warning("STUB: Sprite::showTitle() - Flags changed compared to CGE1's Sprite type."); + D.gotoxyz(kScrWidth >> 1, -(kPanHeight >> 1)); + _vga->sunset(); + + D.show(2); + + _vga->copyPage(1, 2); + _vga->copyPage(0, 1); + + _vga->sunrise(_vga->_sysPal); + + _vga->update(); + + warning("STUB: CGE2Engine::showTitle()"); + + return true; +} + +int CGE2Engine::freePockets(int sx) { + int n = 0; + for (int i = 0; i < kPocketMax; i++){ + if (_heroTab[sx]->_pocket[i] == nullptr) + ++n; + } + return n; +} + +int CGE2Engine::findActivePocket(int ref) { + for (int i = 0; i < kPocketMax; i++) { + Sprite *spr = _heroTab[_sex]->_pocket[i]; + if (ref >= 0) { + if (spr && spr->_ref == ref) + return i; + } else if (!spr) + return i; + } + return -1; +} + +void CGE2Engine::pocFul() { + Hero *h = _heroTab[_sex]->_ptr; + h->park(); + _commandHandler->addCommand(kCmdWait, -1, -1, h); + _commandHandler->addCommand(kCmdSound, -1, 2, h); + _commandHandler->addCommand(kCmdSay, -1, kPocketFull + _sex, h); +} + +void CGE2Engine::killText() { + if (!_talk) + return; + + _commandHandlerTurbo->addCommand(kCmdKill, -1, 0, _talk); + _talk = NULL; +} + +void CGE2Engine::switchHero(bool sex) { + warning("STUB: CGE2Engine::switchHero()"); +} + +} // End of namespace CGE2 diff --git a/engines/cge2/cge2_main.h b/engines/cge2/cge2_main.h new file mode 100644 index 0000000000..272cdf79d5 --- /dev/null +++ b/engines/cge2/cge2_main.h @@ -0,0 +1,67 @@ +/* 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 Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_MAIN_H +#define CGE2_MAIN_H + +#include "cge2/events.h" + +namespace CGE2 { + +#define kLineMax 512 +#define kIntroExt ".I80" +#define kTabName "CGE.TAB" +#define kPocketFull 170 +#define kGameFrameDelay (750 / 50) +#define kGameTickDelay (750 / 62) + +#define kMusicRef 122 +#define kPowerRef 123 +#define kDvolRef 124 +#define kMvolRef 125 +#define kRef 126 +#define kBusyRef 127 +#define kCapRef 128 +#define kVoxRef 129 + +class System : public Sprite { +public: + int _funDel; + int _blinkCounter; + Sprite *_blinkSprite; + + System(CGE2Engine *vm); + + virtual void touch(uint16 mask, int x, int y, Common::KeyCode keyCode); + void tick(); +private: + CGE2Engine *_vm; +}; + +} // End of namespace CGE2 + +#endif // CGE2_MAIN_H diff --git a/engines/cge2/configure.engine b/engines/cge2/configure.engine new file mode 100644 index 0000000000..6ccbfee088 --- /dev/null +++ b/engines/cge2/configure.engine @@ -0,0 +1,3 @@ +# This file is included from the main "configure" script +# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] +add_engine cge2 "CGE2" no diff --git a/engines/cge2/detection.cpp b/engines/cge2/detection.cpp new file mode 100644 index 0000000000..9c63af1ec6 --- /dev/null +++ b/engines/cge2/detection.cpp @@ -0,0 +1,87 @@ +/* ScummVM - Graphic Adventure Engine +* +* ScummVM is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the COPYRIGHT +* file distributed with this source distribution. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +*/ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "cge2/cge2.h" +#include "engines/advancedDetector.h" + +namespace CGE2 { + +static const PlainGameDescriptor CGE2Games[] = { + { "sfinx", "Sfinx" }, + { 0, 0 } +}; + +static const ADGameDescription gameDescriptions[] = { // TODO: Add ENG version too. + { + "sfinx", "Sfinx Freeware", + { + { "vol.cat", 0, "21197b287d397c53261b6616bf0dd880", 129024 }, + { "vol.dat", 0, "de14291869a8eb7c2732ab783c7542ef", 34180844 }, + AD_LISTEND + }, + Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO0() + }, + + AD_TABLE_END_MARKER +}; + +class CGE2MetaEngine : public AdvancedMetaEngine { +public: + CGE2MetaEngine() : AdvancedMetaEngine(gameDescriptions, sizeof(ADGameDescription), CGE2Games) { + _singleid = "sfinx"; + } + + virtual const char *getName() const { + return "CGE2"; + } + + virtual const char *getOriginalCopyright() const { + return "Sfinx (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon"; + } + + virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; + virtual bool hasFeature(MetaEngineFeature f) const; +}; + +bool CGE2MetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { + if (desc) { + *engine = new CGE2::CGE2Engine(syst, desc); + } + return desc != 0; +} + +bool CGE2MetaEngine::hasFeature(MetaEngineFeature f) const { + return false; +} + +} // End of namespace CGE2 + +#if PLUGIN_ENABLED_DYNAMIC(CGE2) + REGISTER_PLUGIN_DYNAMIC(CGE2, PLUGIN_TYPE_ENGINE, CGE2::CGE2MetaEngine); +#else + REGISTER_PLUGIN_STATIC(CGE2, PLUGIN_TYPE_ENGINE, CGE2::CGE2MetaEngine); +#endif diff --git a/engines/cge2/events.cpp b/engines/cge2/events.cpp new file mode 100644 index 0000000000..670ea61c93 --- /dev/null +++ b/engines/cge2/events.cpp @@ -0,0 +1,85 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 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 "engines/advancedDetector.h" +#include "cge2/events.h" +#include "cge2/text.h" +#include "cge2/cge2_main.h" + +namespace CGE2 { + +/*----------------- KEYBOARD interface -----------------*/ + +Keyboard::Keyboard(CGE2Engine *vm) : _client(NULL), _vm(vm) { + warning("STUB: Keyboard::Keyboard() - Recheck the whole implementation!!!"); +} + +Keyboard::~Keyboard() { +} + +Sprite *Keyboard::setClient(Sprite *spr) { + warning("STUB: Keyboard::setClient()"); + return spr; +} + +bool Keyboard::getKey(Common::Event &event) { + warning("STUB: Keyboard::getKey()"); + return false; +} + +void Keyboard::newKeyboard(Common::Event &event) { + warning("STUB: Keyboard::newKeyboard()"); +} + +/*----------------- MOUSE interface -----------------*/ + +Mouse::Mouse(CGE2Engine *vm) : Sprite(vm), _busy(NULL), _hold(NULL), _hx(0), _vm(vm) { + warning("STUB: Mouse::Mouse() - Recheck the whole implementation!!!"); +} + +Mouse::~Mouse() { + warning("STUB: Mouse::~Mouse()"); +} + +void Mouse::on() { + warning("STUB: Mouse::on()"); +} + +void Mouse::off() { + warning("STUB: Mouse::off()"); +} + +void Mouse::newMouse(Common::Event &event) { + warning("STUB: Mouse::newMouse()"); +} + +} // End of namespace CGE2 diff --git a/engines/cge2/events.h b/engines/cge2/events.h new file mode 100644 index 0000000000..99f017bae3 --- /dev/null +++ b/engines/cge2/events.h @@ -0,0 +1,88 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_EVENTS_H +#define CGE2_EVENTS_H + +#include "common/events.h" +#include "cge2/talk.h" +#include "cge2/vga13h.h" + +namespace CGE2 { + +/*----------------- KEYBOARD interface -----------------*/ + +#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 +}; + +class Keyboard { +private: + bool getKey(Common::Event &event); + CGE2Engine *_vm; +public: + Sprite *_client; + bool _keyAlt; + + void newKeyboard(Common::Event &event); + Sprite *setClient(Sprite *spr); + + Keyboard(CGE2Engine *vm); + ~Keyboard(); +}; + +/*----------------- MOUSE interface -----------------*/ + +class Mouse : public Sprite { +public: + Sprite *_hold; + bool _active; + int _hx; + int _hy; + bool _exist; + int _buttons; + Sprite *_busy; + Mouse(CGE2Engine *vm); + ~Mouse(); + void on(); + void off(); + void newMouse(Common::Event &event); +private: + CGE2Engine *_vm; +}; + +} // End of namespace CGE + +#endif // #define CGE2_EVENTS_H diff --git a/engines/cge2/fileio.cpp b/engines/cge2/fileio.cpp new file mode 100644 index 0000000000..4fec4a67ca --- /dev/null +++ b/engines/cge2/fileio.cpp @@ -0,0 +1,270 @@ +/* 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 Sfinx source code + * Copyright (c) 1994-1997 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 "cge2/cge2.h" +#include "cge2/fileio.h" + +namespace CGE2 { + +/*----------------------------------------------------------------------- + * BtPage + *-----------------------------------------------------------------------*/ +void BtPage::readBTree(Common::ReadStream &s) { + _header._count = s.readUint16LE(); + _header._down = s.readUint16LE(); + + if (_header._down == kBtValNone) { + // Leaf list + for (int i = 0; i < kBtLeafCount; ++i) { + s.read(_leaf[i]._key, kBtKeySize); + _leaf[i]._pos = s.readUint32LE(); + _leaf[i]._size = s.readUint32LE(); + } + } else { + // Root index + for (int i = 0; i < kBtInnerCount; ++i) { + s.read(_inner[i]._key, kBtKeySize); + _inner[i]._down = s.readUint16LE(); + } + } +} + +/*----------------------------------------------------------------------- + * ResourceManager + *-----------------------------------------------------------------------*/ +ResourceManager::ResourceManager() { + _datFile = new Common::File(); + _datFile->open(kDatName); + + _catFile = new Common::File(); + _catFile->open(kCatName); + + if (!_datFile->isOpen() || !_catFile->isOpen()) + error("Unable to open data files"); + + for (int i = 0; i < kBtLevel; i++) { + _buff[i]._page = new BtPage; + _buff[i]._pageNo = kBtValNone; + _buff[i]._index = -1; + assert(_buff[i]._page != NULL); + } +} + +ResourceManager::~ResourceManager() { + _datFile->close(); + delete _datFile; + + _catFile->close(); + delete _catFile; + + for (int i = 0; i < kBtLevel; i++) + delete _buff[i]._page; +} + +void ResourceManager::XCrypt(byte *buf, uint16 length) { + byte *b = buf; + + for (uint16 i = 0; i < length; i++) + *b++ ^= kCryptSeed; +} + +bool ResourceManager::seek(int32 offs, int whence) { + return _datFile->seek(offs, whence); +} + +uint16 ResourceManager::read(byte *buf, uint16 length) { + if (!_datFile->isOpen()) + return 0; + + uint16 bytesRead = _datFile->read(buf, length); + if (!bytesRead) + error("Read %s - %d bytes", _datFile->getName(), length); + XCrypt(buf, length); + return bytesRead; +} + +BtPage *ResourceManager::getPage(int level, uint16 pageId) { + if (_buff[level]._pageNo != pageId) { + int32 pos = pageId * kBtSize; + _buff[level]._pageNo = pageId; + assert(_catFile->size() > pos); + // In the original, there was a check verifying if the + // purpose was to write a new file. This should only be + // to create a new file, thus it was removed. + _catFile->seek(pageId * kBtSize, SEEK_SET); + + // Read in the page + byte buffer[kBtSize]; + int bytesRead = catRead(buffer, kBtSize); + + // Unpack it into the page structure + Common::MemoryReadStream stream(buffer, bytesRead, DisposeAfterUse::NO); + _buff[level]._page->readBTree(stream); + _buff[level]._index = -1; + } + return _buff[level]._page; +} + +BtKeypack *ResourceManager::find(const char *key) { + int lev = 0; + uint16 nxt = kBtValRoot; + while (!_catFile->eos()) { + BtPage *pg = getPage(lev, nxt); + // search + if (pg->_header._down != kBtValNone) { + int i; + for (i = 0; i < pg->_header._count; i++) { + // Does this work, or does it have to compare the entire buffer? + if (scumm_strnicmp((const char *)key, (const char*)pg->_inner[i]._key, kBtKeySize) < 0) + break; + } + nxt = (i) ? pg->_inner[i - 1]._down : pg->_header._down; + _buff[lev]._index = i - 1; + lev++; + } else { + int i; + for (i = 0; i < pg->_header._count - 1; i++) { + if (scumm_stricmp((const char *)key, (const char *)pg->_leaf[i]._key) <= 0) + break; + } + _buff[lev]._index = i; + return &pg->_leaf[i]; + } + } + return NULL; +} + +bool ResourceManager::exist(const char *name) { + return scumm_stricmp(find(name)->_key, name) == 0; +} + +uint16 ResourceManager::catRead(byte *buf, uint16 length) { + if (!_catFile->isOpen()) + return 0; + + uint16 bytesRead = _catFile->read(buf, length); + if (!bytesRead) + error("Read %s - %d bytes", _catFile->getName(), length); + XCrypt(buf, length); + return bytesRead; +} + +/*----------------------------------------------------------------------- + * EncryptedStream + *-----------------------------------------------------------------------*/ +EncryptedStream::EncryptedStream(CGE2Engine *vm, const char *name) : _vm(vm), _lineCount(0) { + _error = false; + BtKeypack *kp = _vm->_resman->find(name); + if (scumm_stricmp(kp->_key, name) != 0) + _error = true; + + _vm->_resman->seek(kp->_pos); + byte *dataBuffer; + int bufSize; + + if ((strlen(name) > 4) && (scumm_stricmp(name + strlen(name) - 4, ".SPR") == 0)) { + // SPR files have some inconsistencies. Some have extra 0x1A at the end, some others + // do not have a carriage return at the end of the last line + // Therefore, we remove this ending 0x1A and add extra new lines. + // This fixes bug #3537527 + dataBuffer = (byte *)malloc(kp->_size + 2); + _vm->_resman->read(dataBuffer, kp->_size); + if (dataBuffer[kp->_size - 1] == 0x1A) + dataBuffer[kp->_size - 1] = '\n'; + dataBuffer[kp->_size] = '\n'; + dataBuffer[kp->_size + 1] = '\n'; + bufSize = kp->_size + 2; + } else { + dataBuffer = (byte *)malloc(kp->_size); + _vm->_resman->read(dataBuffer, kp->_size); + bufSize = kp->_size; + } + + _readStream = new Common::MemoryReadStream(dataBuffer, bufSize, DisposeAfterUse::YES); +} + +uint32 EncryptedStream::read(byte *dataPtr, uint32 dataSize) { + return _readStream->read(dataPtr, dataSize); +} + +unsigned EncryptedStream::readUint16BE() { + return _readStream->readUint16BE(); +} + +signed EncryptedStream::readSint16BE() { + return _readStream->readSint16BE(); +} + +signed EncryptedStream::readSint16LE() { + return _readStream->readSint16LE(); +} + +bool EncryptedStream::err() { + return (_error & _readStream->err()); +} + +bool EncryptedStream::eos() { + return _readStream->eos(); +} + +bool EncryptedStream::seek(int32 offset) { + return _readStream->seek(offset); +} + +Common::String EncryptedStream::readLine() { + _lineCount++; + Common::String line = _readStream->readLine(); + if (!line.empty() && (line[0] == ';' || line[0] == '.' || line[0] == '*')) + line.clear(); // Returns an empty string, if the line is invalid. + return line; +} + +int32 EncryptedStream::size() { + return _readStream->size(); +} + +int32 EncryptedStream::pos() { + return _readStream->pos(); +} + +EncryptedStream::~EncryptedStream() { + delete _readStream; +} + +const char *EncryptedStream::kIdTab[] = { + "[near]", "[mtake]", "[ftake]", "[phase]", "[seq]", + "Name", "Type", "Front", "East", + "Portable", "Transparent", + NULL +}; + +} // End of namespace CGE2 diff --git a/engines/cge2/fileio.h b/engines/cge2/fileio.h new file mode 100644 index 0000000000..83a14560ad --- /dev/null +++ b/engines/cge2/fileio.h @@ -0,0 +1,136 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_FILEIO_H +#define CGE2_FILEIO_H + +#include "common/file.h" + +namespace CGE2 { + +class CGE2Engine; + +#define kBtSize 2048 +#define kBtKeySize 13 +#define kBtLevel 2 +#define kBtInnerCount ((kBtSize - 4 /*sizeof(Header) */) / (kBtKeySize + 2 /*sizeof(Inner) */)) +#define kBtLeafCount ((kBtSize - 4 /*sizeof(Header) */) / (kBtKeySize + 4 + 4 /*sizeof(BtKeypack) */)) +#define kBtValNone 0xFFFF +#define kBtValRoot 0 +#define kCatName "VOL.CAT" +#define kDatName "VOL.DAT" +#define kCryptSeed 0xA5 + +enum ID { + kIdNear, kIdMTake, kIdFTake, kIdPhase, kIdSeq, + kIdName, kIdType, kIdFront, kIdEast, + kIdPortable, kIdTransparent, + kIdNone = -1 +}; + +struct BtKeypack { + char _key[kBtKeySize]; + uint32 _pos; + uint32 _size; +}; + +struct Inner { + uint8 _key[kBtKeySize]; + uint16 _down; +}; + +struct Header { + uint16 _count; + uint16 _down; +}; + +struct BtPage { + Header _header; + union { + // dummy filler to make proper size of union + uint8 _data[kBtSize - 4]; /* 4 is the size of struct Header */ + // inner version of data: key + word-sized page link + Inner _inner[kBtInnerCount]; + // leaf version of data: key + all user data + BtKeypack _leaf[kBtLeafCount]; + }; + + void readBTree(Common::ReadStream &s); +}; + +class ResourceManager { +private: + struct { + BtPage *_page; + uint16 _pageNo; + int _index; + } _buff[kBtLevel]; + + BtPage *getPage(int level, uint16 pageId); + uint16 catRead(byte *buf, uint16 length); + Common::File *_catFile; + Common::File *_datFile; + void XCrypt(byte *buf, uint16 length); +public: + ResourceManager(); + ~ResourceManager(); + uint16 read(byte *buf, uint16 length); + bool seek(int32 offs, int whence = SEEK_SET); + + BtKeypack *find(const char *key); + bool exist(const char *name); +}; + +// TODO: Revise the whole class! +class EncryptedStream { +private: + CGE2Engine *_vm; + Common::SeekableReadStream *_readStream; + const char **_tab; + int _lineCount; + bool _error; +public: + EncryptedStream(CGE2Engine *vm, const char *name); + ~EncryptedStream(); + bool err(); + bool eos(); + bool seek(int32 offset); + int32 pos(); + int32 size(); + uint32 read(byte *dataPtr, uint32 dataSize); + unsigned readUint16BE(); + signed readSint16BE(); + signed readSint16LE(); + Common::String readLine(); + int getLineCount() { return _lineCount; } + + static const char *kIdTab[]; +}; + +} // End of namespace CGE2 + +#endif // CGE2_FILEIO_H diff --git a/engines/cge2/general.h b/engines/cge2/general.h new file mode 100644 index 0000000000..0239402ea3 --- /dev/null +++ b/engines/cge2/general.h @@ -0,0 +1,47 @@ +/* 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 Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_GENERAL_H +#define CGE2_GENERAL_H + +#include "common/file.h" + +namespace CGE2 { + +#define SCR_WID_ 320 + +class CGE2Engine; + +struct Dac { + uint8 _r; + uint8 _g; + uint8 _b; +}; + +} // End of namespace CGE2 + +#endif // CGE2_GENERAL_H diff --git a/engines/cge2/hero.cpp b/engines/cge2/hero.cpp new file mode 100644 index 0000000000..3a6d1d2920 --- /dev/null +++ b/engines/cge2/hero.cpp @@ -0,0 +1,146 @@ +/* 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 Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "cge2/hero.h" + +namespace CGE2 { + +Hero::Hero(CGE2Engine *vm) : Sprite(vm) { + warning("STUB: Hero::Hero()"); +} + +Sprite *Hero::expand(void) { + warning("STUB: Hero::expand()"); + return this; +} + +void Hero::setCurrent(void) { + warning("STUB: Hero::setCurrent()"); +} + +void Hero::hStep(void) { + warning("STUB: Hero::hStep()"); +} + +Sprite *Hero::setContact(void) { + warning("STUB: Hero::setContact()"); + return this; +} + +void Hero::tick(void) { + warning("STUB: Hero::tick()"); +} + +int Hero::distance(V3D pos) { + warning("STUB: Hero::distance()"); + return 0; +} + +int Hero::distance(Sprite *spr) { + warning("STUB: Hero::distance()"); + return 0; +} + +void Hero::turn(Dir d) { + warning("STUB: Hero::turn()"); +} + +void Hero::park(void) { + warning("STUB: Hero::park()"); +} + +bool Hero::lower(Sprite * spr) { + warning("STUB: Hero::lower()"); + return false; +} + +void Hero::reach(int mode) { + warning("STUB: Hero::reach()"); +} + +void Hero::fun(void) { + warning("STUB: Hero::fun()"); +} + +void Hero::operator ++ (void) { + warning("STUB: Hero::operator ++()"); +} + +void Hero::operator -- (void) { + warning("STUB: Hero::operator --()"); +} + +uint32 Hero::len(V2D v) { + warning("STUB: Hero::works()"); + return 0; +} + +bool Hero::findWay(void){ + warning("STUB: Hero::findWay()"); + return false; +} + +int Hero::snap(int p, int q, int grid) { + warning("STUB: Hero::findWay()"); + return 0; +} + +void Hero::walkTo(V3D pos) { + warning("STUB: Hero::walkTo()"); +} + +void Hero::walkTo(Sprite *spr) { + warning("STUB: Hero::walkTo()"); +} + +V3D Hero::screenToGround(V2D pos) { + double z = _vm->_eye->_z + (_vm->_eye->_y * _vm->_eye->_z) / (double(pos.y) - _vm->_eye->_y); + double x = _vm->_eye->_x - ((double(pos.x) - _vm->_eye->_x) * (z - _vm->_eye->_z)) / _vm->_eye->_z; + return V3D(V2D::round(x), 0, V2D::round(z)); +} + + +int Hero::cross(const V2D &a, const V2D &b) { + warning("STUB: Hero::cross()"); + return 0; +} + +int Hero::mapCross(const V2D &a, const V2D &b) { + warning("STUB: Hero::mapCross()"); + return 0; +} + +int Hero::mapCross(const V3D &a, const V3D &b) { + warning("STUB: Hero::mapCross()"); + return 0; +} + +void Hero::setCave(int c) { + warning("STUB: Hero::mapCross()"); +} + +} // End of namespace CGE2 diff --git a/engines/cge2/hero.h b/engines/cge2/hero.h new file mode 100644 index 0000000000..032a46d1b3 --- /dev/null +++ b/engines/cge2/hero.h @@ -0,0 +1,105 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * 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 Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_HERO_H +#define CGE2_HERO_H + +#include "cge2/cge2.h" +#include "cge2/vga13h.h" +#include "cge2/snail.h" + +namespace CGE2 { + +#define kMaxTry 400 + +class Hero; + +struct HeroTab { + Hero *_ptr; + Sprite *_face; + Sprite *_pocket[kPocketMax + 1]; + int _pocPtr; + V2D *_posTab[kCaveMax]; + HeroTab(CGE2Engine *vm) { + for (int i = 0; i < kCaveMax; i++) + _posTab[i] = new V2D(vm); + } + ~HeroTab() { + for (int i = 0; i < kCaveMax; i++) + delete _posTab[i]; + } +}; + +class Hero : public Sprite { + int _hig[kDimMax]; + Sprite *_contact; +public: + BitmapPtr _dim[kDimMax]; + V3D _trace[kWayMax]; + enum Dir { kNoDir = -1, kSS, kWW, kNN, kEE } _dir; + int _curDim; + int _tracePtr; + int _reachStart, _reachCycle, _sayStart, _funStart; + int _funDel0, _funDel; + int _maxDist; + bool _ignoreMap; + Hero(CGE2Engine *vm); + void tick(void); + Sprite *expand(void); + Sprite *contract(void) { return this; } + Sprite *setContact(void); + int stepSize(void) { return _ext->_seq[7]._dx; } + int distance(V3D pos); + int distance(Sprite * spr); + void turn(Dir d); + void park(void); + static uint32 len(V2D v); + bool findWay(void); + static int snap(int p, int q, int grid); + void walkTo(V3D pos); + void walkTo(V2D pos) { walkTo(screenToGround(pos)); } + V3D screenToGround(V2D pos); + void walkTo(Sprite *spr); + void say(void) { step(_sayStart); } + void fun(void); + void resetFun(void) { _funDel = _funDel0; } + void hStep(void); + bool lower(Sprite * spr); + int cross(const V2D &a, const V2D &b); + int mapCross(const V2D &a, const V2D &b); + int mapCross(const V3D &a, const V3D &b); + Hero *other(void) { return _vm->_heroTab[!(_ref & 1)]->_ptr;} + Action action(void) { return (Action)(_ref % 10); } + void reach(int mode); + void setCurrent(void); + void setCave(int c); + void operator ++ (void); + void operator -- (void); +}; + +} // End of namespace CGE2 + +#endif // CGE2_HERO_H diff --git a/engines/cge2/module.mk b/engines/cge2/module.mk new file mode 100644 index 0000000000..7272ed3829 --- /dev/null +++ b/engines/cge2/module.mk @@ -0,0 +1,24 @@ +MODULE := engines/cge2 + +MODULE_OBJS = \ + cge2.o \ + detection.o \ + fileio.o \ + vga13h.o \ + bitmap.o \ + sound.o \ + cge2_main.o \ + text.o \ + hero.o \ + snail.o \ + spare.o \ + talk.o \ + events.o + +# This module can be built as a plugin +ifeq ($(ENABLE_CGE2), DYNAMIC_PLUGIN) +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk diff --git a/engines/cge2/snail.cpp b/engines/cge2/snail.cpp new file mode 100644 index 0000000000..f00b02c7bd --- /dev/null +++ b/engines/cge2/snail.cpp @@ -0,0 +1,689 @@ +/* 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 Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "cge2/snail.h" +#include "cge2/fileio.h" +#include "cge2/hero.h" +#include "cge2/text.h" +#include "cge2/sound.h" + +namespace CGE2 { + +const char *CommandHandler::_commandText[] = { + "NOP", "USE", "PAUSE", "INF", "CAVE", + "SLAVE", "FOCUS", "SETX", "SETY", "SETZ", + "ADD", "SUB", "MUL", "DIV", "IF", "FLAG", + "FLASH", "LIGHT", "CYCLE", + "CLEAR", "TALK", "MOUSE", + "MAP", "COUNT", "MIDI", + "SETDLG", "MSKDLG", + ".DUMMY.", + "WAIT", "HIDE", "ROOM", + "SAY", "SOUND", "TIME", "KILL", + "RSEQ", "SEQ", "SEND", "SWAP", + "KEEP", "GIVE", + "GETPOS", "GOTO", "MOVEX", "MOVEY", + "MOVEZ", "TRANS", "PORT", + "NEXT", "NNEXT", "MTNEXT", "FTNEXT", + "RNNEXT", "RMTNEXT", "RFTNEXT", + "RMNEAR", "RMMTAKE", "RMFTAKE", + "SETREF", "BACKPT", + "WALKTO", "REACH", "COVER", "UNCOVER", + NULL }; + +CommandHandler::CommandHandler(CGE2Engine *vm, bool turbo) + : _turbo(turbo), _textDelay(false), _timerExpiry(0), _talkEnable(true), + _head(0), _tail(0), _commandList((Command *)malloc(sizeof(Command)* 256)), + _count(1), _vm(vm) { +} + +CommandHandler::~CommandHandler() { + free(_commandList); +} + +void CommandHandler::runCommand() { + if (!_turbo && _vm->_commandStat._wait) { + if (*(_vm->_commandStat._wait)) + return; + else { + ++_vm->_commandStat._ref[0]; + warning("STUB: CommandHandler::runCommand() - Sound code missing!"); + _vm->_commandStat._wait = nullptr; + } + } + + uint8 tmpHead = _head; + while (_tail != tmpHead) { + Command tailCmd = _commandList[_tail]; + + if (!_turbo) { // only for the slower one + if (_vm->_waitRef) + break; + if (_timerExpiry) { + // Delay in progress + if (_timerExpiry > g_system->getMillis()) + // Delay not yet ended + break; + + // Delay is finished + _timerExpiry = 0; + } else if (_textDelay) { + if (_vm->_talk) { + _vm->snKill((Sprite *)_vm->_talk); + _vm->_talk = nullptr; + } + _textDelay = false; + } + + if (_vm->_talk && tailCmd._commandType != kCmdPause) + break; + } + ++_tail; + _vm->_taken = false; + Sprite *spr = nullptr; + if (tailCmd._commandType > kCmdSpr) + spr = (tailCmd._ref < 0) ? ((Sprite *)tailCmd._spritePtr) : _vm->locate(tailCmd._ref); + + switch (tailCmd._commandType) { + case kCmdUse: + break; + case kCmdPause: + _timerExpiry = g_system->getMillis() + tailCmd._val * kCommandFrameDelay; + if (_vm->_talk) + _textDelay = true; + break; + case kCmdWait: + if (spr && spr->active() && (spr->_scene == _vm->_now || spr->_scene == 0)) { + _vm->_waitSeq = tailCmd._val; + _vm->_waitRef = spr->_ref; + } + break; + case kCmdHide: + _vm->snHide(spr, tailCmd._val); + break; + case kCmdSay: + if (spr && spr->active() && _talkEnable) //-- mouth animation + warning("STUB: CommandHandler::runCommand() - Mouth animation missing!"); + break; + case kCmdInf: + if (_talkEnable) + _vm->inf(((tailCmd._val) >= 0) ? _vm->_text->getText(tailCmd._val) : (const char *)tailCmd._spritePtr); + break; + case kCmdTime: + warning("STUB: CommandHandler::runCommand() - Something missing connected to kCmdTime!"); + break; + case kCmdCave: + _vm->switchCave(tailCmd._val); + break; + case kCmdMidi: + _vm->snMidi(tailCmd._val); + break; + case kCmdSetDlg: + _vm->snSetDlg(tailCmd._ref, tailCmd._val); + break; + case kCmdMskDlg: + _vm->snMskDlg(tailCmd._ref, tailCmd._val); + break; + case kCmdKill: + _vm->snKill(spr); + break; + case kCmdSeq: + _vm->snSeq(spr, tailCmd._val); + break; + case kCmdRSeq: + _vm->snRSeq(spr, tailCmd._val); + break; + case kCmdSend: + _vm->snSend(spr, tailCmd._val); + break; + case kCmdSwap: + _vm->snSwap(spr, tailCmd._val); + break; + case kCmdCover: + _vm->snCover(spr, tailCmd._val); + break; + case kCmdUncover: + _vm->snUncover(spr, (tailCmd._val >= 0) ? _vm->locate(tailCmd._val) : ((Sprite *)tailCmd._spritePtr)); + break; + case kCmdFocus: + _vm->snFocus(tailCmd._val); + break; + case kCmdKeep: + _vm->snKeep(spr, tailCmd._val); + break; + case kCmdGive: + _vm->snGive(spr, tailCmd._val); + break; + case kCmdSetX: + _vm->_point[tailCmd._val]->_x = tailCmd._ref; + break; + case kCmdSetY: + _vm->_point[tailCmd._val]->_y = tailCmd._ref; + break; + case kCmdSetZ: + _vm->_point[tailCmd._val]->_z = tailCmd._ref; + break; + case kCmdAdd: + *(_vm->_point[tailCmd._ref]) = *(_vm->_point[tailCmd._ref]) + *(_vm->_point[tailCmd._val]); + break; + case kCmdSub: + *(_vm->_point[tailCmd._ref]) = *(_vm->_point[tailCmd._ref]) - *(_vm->_point[tailCmd._val]); + break; + case kCmdMul: + *(_vm->_point[tailCmd._ref]) = *(_vm->_point[tailCmd._ref]) * tailCmd._val; + break; + case kCmdDiv: + *(_vm->_point[tailCmd._ref]) = *(_vm->_point[tailCmd._ref]) / tailCmd._val; + break; + case kCmdGetPos: + if (spr) + *(_vm->_point[tailCmd._val]) = spr->_pos3D; + break; + case kCmdGoto: + _vm->snGoto(spr, tailCmd._val); + break; + case kCmdMoveX: + _vm->snMove(spr, V3D(tailCmd._val, 0, 0)); + break; + case kCmdMoveY: + _vm->snMove(spr, V3D(0, tailCmd._val, 0)); + break; + case kCmdMoveZ: + _vm->snMove(spr, V3D(0, 0, tailCmd._val)); + break; + case kCmdSlave: + _vm->snSlave(spr, tailCmd._val); + break; + case kCmdTrans: + _vm->snTrans(spr, tailCmd._val); + break; + case kCmdPort: + _vm->snPort(spr, tailCmd._val); + break; + case kCmdNext: + break; + case kCmdIf: + break; + case kCmdTalk: + break; + case kCmdMouse: + _vm->snMouse(tailCmd._val != 0); + break; + case kCmdNNext: + _vm->snNNext(spr, kNear, tailCmd._val); + break; + case kCmdMTNext: + _vm->snNNext(spr, kMTake, tailCmd._val); + break; + case kCmdFTNext: + _vm->snNNext(spr, kFTake, tailCmd._val); + break; + case kCmdRNNext: + _vm->snRNNext(spr, tailCmd._val); + break; + case kCmdRMTNext: + _vm->snRMTNext(spr, tailCmd._val); + break; + case kCmdRFTNext: + _vm->snRFTNext(spr, tailCmd._val); + break; + case kCmdRMNear: + _vm->snRmNear(spr); + break; + case kCmdRMMTake: + _vm->snRmMTake(spr); + break; + case kCmdRMFTake: + _vm->snRmFTake(spr); + break; + case kCmdFlag: + _vm->snFlag(tailCmd._ref & 3, tailCmd._val); + break; + case kCmdSetRef: + _vm->snSetRef(spr, tailCmd._val); + break; + case kCmdBackPt: + _vm->snBackPt(spr, tailCmd._val); + break; + case kCmdFlash: + _vm->snFlash(tailCmd._val != 0); + break; + case kCmdLight: + _vm->snLight(tailCmd._val != 0); + break; + case kCmdCycle: + warning("Unhandled command - kCmdCycle"); + break; + case kCmdWalk: + _vm->snWalk(spr, tailCmd._val); + break; + case kCmdReach: + _vm->snReach(spr, tailCmd._val); + break; + case kCmdSound: + _vm->snSound(spr, tailCmd._val); + _count = 1; + break; + case kCmdMap: + _vm->_heroTab[tailCmd._ref & 1]->_ptr->_ignoreMap = tailCmd._val == 0; + break; + case kCmdCount: + _count = tailCmd._val; + break; + case kCmdRoom: + _vm->snRoom(spr, tailCmd._val); + break; + case kCmdDim: + warning("Unhandled command - kCmdDim"); + break; + case kCmdExec: + warning("Unhandled command - kCmdExec"); + break; + case kCmdStep: + spr->step(); + break; + case kCmdGhost: + _vm->snGhost((Bitmap *)tailCmd._spritePtr); + break; + default: + warning("Unhandled command"); + break; + } + + if (_vm->_taken && spr) + _vm->_spare->dispose(spr); + + if (!_turbo) + break; + } +} + +void CGE2Engine::snKill(Sprite *spr) { + warning("STUB: CGE2Engine::snKill()"); +} + +void CGE2Engine::snHide(Sprite *spr, int 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 CGE2Engine::snMidi(int val) { + warning("STUB: CGE2Engine::snMidi()"); +} + +void CGE2Engine::snSetDlg(int clr, int set) { + warning("STUB: CGE2Engine::snSetDlg()"); +} + +void CGE2Engine::snMskDlg(int clr, int set) { + warning("STUB: CGE2Engine::snMskDlg()"); +} + +void CGE2Engine::snSeq(Sprite *spr, int val) { + if (spr) { + if (isHero(spr) && val == 0) + ((Hero*)spr)->park(); + else + spr->step(val); + } +} + +void CGE2Engine::snRSeq(Sprite *spr, int val) { + if (spr) + snSeq(spr, spr->_seqPtr + val); +} + +void CGE2Engine::snSend(Sprite *spr, int val) { + if (!spr) + return; + + // Sending", spr->File + // from cave", spr->Cave + // to cave", val + bool was1 = (_vga->_showQ->locate(spr->_ref) != nullptr); + bool val1 = (val == 0 || val == _now); + spr->_scene = val; + releasePocket(spr); + if (val1 != was1) { + if (was1) { + // deactivating + hide1(spr); + spr->_flags._slav = false; + if (spr == _heroTab[_sex]->_ptr) + if (_heroTab[!_sex]->_ptr->_scene == _now) + switchHero(!_sex); + _spare->dispose(spr); + } else { + // activating + if (byte(spr->_ref) == 0) + _bitmapPalette = _vga->_sysPal; + _vga->_showQ->insert(spr); + if (isHero(spr)) { + V2D p = *_heroTab[spr->_ref & 1]->_posTab[val]; + spr->gotoxyz(V3D(p.x, 0, p.y)); + ((Hero*)spr)->setCurrent(); + } + _taken = false; + _bitmapPalette = NULL; + } + } +} + +void CGE2Engine::snSwap(Sprite *spr, int val) { + warning("STUB: CGE2Engine::snSwap()"); +} +void CGE2Engine::snCover(Sprite *spr, int val) { + warning("STUB: CGE2Engine::snCover()"); +} + +void CGE2Engine::snUncover(Sprite *spr, Sprite *spr2) { + warning("STUB: CGE2Engine::snUncover()"); +} + +void CGE2Engine::snFocus(int val) { + warning("STUB: CGE2Engine::snFocus()"); +} + +void CGE2Engine::snKeep(Sprite *spr, int val) { + warning("STUB: CGE2Engine::snKeep()"); +} + +void CGE2Engine::snGive(Sprite *spr, int val) { + warning("STUB: CGE2Engine::snGive()"); +} + +void CGE2Engine::snGoto(Sprite *spr, int val) { + warning("STUB: CGE2Engine::snGoto()"); +} + +void CGE2Engine::snMove(Sprite *spr, V3D pos) { + warning("STUB: CGE2Engine::snMove()"); +} + +void CGE2Engine::snSlave(Sprite *spr, int val) { + warning("STUB: CGE2Engine::snSlave()"); +} + +void CGE2Engine::snTrans(Sprite *spr, int val) { + warning("STUB: CGE2Engine::snTrans()"); +} + +void CGE2Engine::snPort(Sprite *spr, int val) { + warning("STUB: CGE2Engine::snPort()"); +} + +void CGE2Engine::snMouse(int val) { + warning("STUB: CGE2Engine::snMouse()"); +} + +void CGE2Engine::snNNext(Sprite *spr, Action act, int val) { + warning("STUB: CGE2Engine::snNNext()"); +} + +void CGE2Engine::snRNNext(Sprite *spr, int val) { + warning("STUB: CGE2Engine::snRNNext()"); +} + +void CGE2Engine::snRMTNext(Sprite *spr, int val) { + warning("STUB: CGE2Engine::snRMTNext()"); +} + +void CGE2Engine::snRFTNext(Sprite *spr, int val) { + warning("STUB: CGE2Engine::snRFTNext()"); +} + +void CGE2Engine::snRmNear(Sprite *spr) { + warning("STUB: CGE2Engine::snRmNear()"); +} + +void CGE2Engine::snRmMTake(Sprite *spr) { + warning("STUB: CGE2Engine::snRmMTake()"); +} + +void CGE2Engine::snRmFTake(Sprite *spr) { + warning("STUB: CGE2Engine::snRmFTake()"); +} + +void CGE2Engine::snFlag(int ref, int val) { + warning("STUB: CGE2Engine::snFlag()"); +} + +void CGE2Engine::snSetRef(Sprite *spr, int val) { + warning("STUB: CGE2Engine::snSetRef()"); +} + +void CGE2Engine::snBackPt(Sprite *spr, int val) { + warning("STUB: CGE2Engine::snBackPt()"); +} + +void CGE2Engine::snFlash(int val) { + warning("STUB: CGE2Engine::snFlash()"); +} + +void CGE2Engine::snLight(int val) { + warning("STUB: CGE2Engine::snLight()"); +} + +void CGE2Engine::snWalk(Sprite *spr, int val) { + warning("STUB: CGE2Engine::snWalk()"); +} + +void CGE2Engine::snReach(Sprite *spr, int val) { + warning("STUB: CGE2Engine::snReach()"); +} + +void CGE2Engine::snSound(Sprite *spr, int wav) { + if (wav == -1) + _sound->stop(); + else { + if (_sound->_smpinf._counter && wav < 20) + return; + if (_commandStat._wait && ((wav & 255) > 80)) + return; + + _commandStat._ref[1] = wav; + _commandStat._ref[0] = !_fx->exist(_commandStat._ref[1]); + _sound->play(_fx->load(_commandStat._ref[1], _commandStat._ref[0]), + (spr) ? (spr->_pos2D.x / (kScrWidth / 16)) : 8); + } + +} + +void CGE2Engine::snRoom(Sprite *spr, int val) { + warning("STUB: CGE2Engine::snRoom()"); +} + +void CGE2Engine::snGhost(Bitmap *bmp) { + V2D p(this, *bmp->_v & 0xFFFF, *bmp->_v >> 16); + bmp->hide(p.x, p.y); + delete bmp->_b; + bmp->_v = nullptr; + bmp->_b = nullptr; + delete bmp; +} + +void CGE2Engine::hide1(Sprite *spr) { + _commandHandlerTurbo->addCommand(kCmdGhost, -1, 0, spr->ghost()); +} + +void CommandHandler::addCommand(CommandType com, int ref, int val, void *ptr) { + if (ref == 2) + ref = 142 - _vm->_sex; + Command *headCmd = &_commandList[_head++]; + headCmd->_commandType = com; + headCmd->_ref = ref; + headCmd->_val = val; + headCmd->_spritePtr = ptr; + headCmd->_cbType = kNullCB; + if (headCmd->_commandType == kCmdClear) { + clear(); + } +} + +void CommandHandler::addCallback(CommandType com, int ref, int val, CallbackType cbType) { + warning("STUB: CommandHandler::addCallback()"); +} + +void CommandHandler::insertCommand(CommandType com, int ref, int val, void *ptr) { + warning("STUB: CommandHandler::insertCommand()"); +} + +bool CommandHandler::idle() { + return (!_vm->_waitRef && _head == _tail); +} + +void CommandHandler::reset() { + warning("STUB: CommandHandler::reset()"); +} + +void CommandHandler::clear() { + _tail = _head; + _vm->killText(); + _timerExpiry = 0; +} + +int CommandHandler::com(const char *com) { + int i = _vm->takeEnum(_commandText, com); + return (i < 0) ? i : i + kCmdCom0 + 1; +} + +void CGE2Engine::feedSnail(Sprite *spr, Action snq, Hero *hero) { + if (!spr || !spr->active()) + return; + + int cnt = spr->_actionCtrl[snq]._cnt; + if (cnt) { + byte ptr = spr->_actionCtrl[snq]._ptr; + CommandHandler::Command *comtab = spr->snList(snq); + CommandHandler::Command *c = &comtab[ptr]; + CommandHandler::Command *q = &comtab[cnt]; + + warning("STUB: CGE2Engine::feedSnail()"); + // Dont bother with pockets (inventory system) for now... TODO: Implement it later!!! + /* + int pocFre = freePockets(hero->_ref & 1); + int pocReq = 0; + CommandHandler::Command *p = c; + for (; p < q && p->_commandType != kCmdNext; p++) { // scan commands + // drop from pocket? + if ((p->_commandType == kCmdSend && p->_val != _now) + || p->_commandType == kCmdGive) { + int ref = p->_ref; + if (ref < 0) + ref = spr->_ref; + if (findActivePocket(ref) >= 0) + --pocReq; + } + // make/dispose additional room? + if (p->_commandType == kCmdRoom) { + if (p->_val == 0) + ++pocReq; + else + --pocReq; + } + // put into pocket? + if (p->_commandType == kCmdKeep) + ++pocReq; + // overloaded? + if (pocReq > pocFre) { + pocFul(); + return; + } + }*/ + + while (c < q) { + if (c->_commandType == kCmdTalk) { + if ((_commandHandler->_talkEnable = (c->_val != 0)) == false) + killText(); + } + if (c->_commandType == kCmdWalk || c->_commandType == kCmdReach) { + if (c->_val == -1) + c->_val = spr->_ref; + } + if (c->_commandType == kCmdNext) { + Sprite *s; + + switch (c->_ref) { + case -2: + s = hero; + break; + case -1: + s = spr; + break; + default: + s = _vga->_showQ->locate(c->_ref); + break; + } + + if (s) { + if (s->_actionCtrl[snq]._cnt) { + int v; + switch (c->_val) { + case -1: + v = int(c - comtab + 1); + break; + case -2: + v = int(c - comtab); + break; + case -3: + v = -1; + break; + default: + v = c->_val; + if ((v > 255) && s) + v = s->labVal(snq, v >> 8); + break; + } + if (v >= 0) + s->_actionCtrl[snq]._ptr = v; + } + } + if (s == spr) + break; + } + if (c->_commandType == kCmdIf) { + Sprite *s = (c->_ref < 0) ? spr : _vga->_showQ->locate(c->_ref); + if (s) { // sprite extsts + if (!s->seqTest(-1)) { // not parked + int v = c->_val; + if (v > 255) if (s) v = s->labVal(snq, v >> 8); + c = comtab + (v - 1); + } + } + } else + _commandHandler->addCommand(c->_commandType, c->_ref, c->_val, spr); + + ++c; + } + } + +} + +} // End of namespace CGE2. diff --git a/engines/cge2/snail.h b/engines/cge2/snail.h new file mode 100644 index 0000000000..afd52dfd45 --- /dev/null +++ b/engines/cge2/snail.h @@ -0,0 +1,153 @@ +/* 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 Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_SNAIL_H +#define CGE2_SNAIL_H + +#include "cge2/cge2.h" + +namespace CGE2 { + +#define kCommandFrameRate 80 +#define kCommandFrameDelay (1000 / kCommandFrameRate) +#define kNoByte -1 // Recheck this! We have no proof for it's original value. + + +enum CommandType { + kCmdCom0 = 128, + kCmdNop, // NOP :: do nothing + kCmdUse, // USE <spr> <cav>|<lab> :: hint for using + kCmdPause, // PAUSE -1 <dly> :: delay <dly>/72 seconds + kCmdInf, // INF -1 <ref> :: show text referrenced by <ref> + kCmdCave, // CAVE -1 <cav> :: go to board <cav> + kCmdSlave, // SLAVE + kCmdFocus, // FOCUS :: change active hero + kCmdSetX, // SETX <x> <idx> :: set sprite shift in x axis + kCmdSetY, // SETX <y> <idx> :: set sprite shift in y axis + kCmdSetZ, // SETX <z> <idx> :: set sprite shift in z axis + kCmdAdd, // ADD <idx1> <idx2> :: sum vectors + kCmdSub, // SUB <idx1> <idx2> :: subtract vectors + kCmdMul, // MUL <idx> <nr> :: multiply vector by number + kCmdDiv, // DIV <idx> <nr> :: divide vector by number + kCmdIf, // IF + kCmdFlag, // FLAG <nr> <val> :: set flag <nr> to <val> + kCmdFlash, // FLASH -1 0|1 :: lighten whole image (on/off) + kCmdLight, // LIGHT + kCmdCycle, // CYCLE <cnt> :: rotate <cnt> colors from 1 + kCmdClear, // CLEAR -1 0 :: clear kCmdAIL queue + kCmdTalk, // TALK -1 0|1 :: enable speach (on/off) + kCmdMouse, // MOUSE -1 0|1 :: enable mouse (on/off) + kCmdMap, // MAP 0|1 0 :: temporarily turn off map for hero + kCmdCount, // COUNT + kCmdMidi, // MIDI -1 <midi> :: play MIDI referenced by <midi> (-1 = off) + kCmdSetDlg, // SETDLG 0..3 0..3 :: switch of speach mode + kCmdMskDlg, // MSKDLG 0..3 0..3 :: switch of speach mode mask + + kCmdSpr, + + kCmdWait, // WAIT <spr> <seq>|-1 :: wait for SEQ <seq> (-1 = freeze) + kCmdHide, // HIDE <spr> 0|1 :: visibility of sprite + kCmdRoom, // ROOM <hero> 0|1 :: additional room in pocket (no/yes) + kCmdSay, // SAY <spr> <ref> :: say text referenced by <ref> + kCmdSound, // SOUND <spr> <ref> :: play sound effect referenced by <ref> + kCmdTime, // TIME <spr> 0 :: say current time + kCmdKill, // KILL <spr> 0 :: remove sprite + kCmdRSeq, // RSEQ <spr> <nr> :: relative jump SEQ <nr> lines + kCmdSeq, // SEQ <spr> <seq> :: jump to certain SEQ + kCmdSend, // SEND <spr> <cav> :: move sprite to board <cav> + kCmdSwap, // SWAP <spr1> spr2> :: sprite exchange + kCmdKeep, // KEEP <spr> <seq> :: take sprite into pocket and jump to <seq> + kCmdGive, // GIVE <spr> <seq> :: remove sprite from pocket and jump to <seq> + kCmdGetPos, // GETPOS <spr> <idx> :: take sprite's position + kCmdGoto, // GOTO <spr> <idx> :: move sprite to position + kCmdMoveX, // MOVEX <spr> <dx> :: relative move along X axis + kCmdMoveY, // MOVEY <spr> <dy> :: relative move along Y axis + kCmdMoveZ, // MOVEZ <spr> <dz> :: relative move along Z axis + kCmdTrans, // TRANS <spr> 0|1 :: clear/set logical transparency + kCmdPort, // PORT <spr> 0|1 :: clear/set "takeability" of sprite + kCmdNext, // NEXT <spr> <nr> :: jump to <nr> - NEAR or TAKE + kCmdNNext, // NNEXT <spr> <nr> :: jump to <nr> - NEAR + kCmdMTNext, // MTNEXT <spr> <nr> :: jump to <nr> - TAKE + kCmdFTNext, // FTNEXT <spr> <nr> :: jump to <nr> - TAKE + kCmdRNNext, // RNNEXT <spr> <nr> :: relative jump to <nr> - NEAR + kCmdRMTNext, // RMTNEXT <spr> <nr> :: relative jump to <nr> - TAKE + kCmdRFTNext, // RFTNEXT <spr> <nr> :: relative jump to <nr> - TAKE + kCmdRMNear, // RMNEAR <spr> 0 :: remove NEAR list + kCmdRMMTake, // RMMTAKE <spr> 0 :: remove TAKE list + kCmdRMFTake, // RMFTAKE <spr> 0 :: remove TAKE list + kCmdSetRef, // RETREF <spr> <ref> :: change reference of sprite <spr> to <ref> + kCmdBackPt, // BACKPT <spr> 0 :: paint sprite onto the background + kCmdWalk, // WALK <hero> <ref>|<point> :: go close to the sprite or point + kCmdReach, // REACH <hero> <ref>|<m> :: reach the sprite or point with <m> method + kCmdCover, // COVER <sp1> <sp2> :: cover sprite <sp1> with sprite <sp2> + kCmdUncover, // UNCOVER <sp1> <sp2> :: restore the state before COVER + + kCmdDim, + kCmdExec, + kCmdStep, + kCmdGhost, + + kCmdNOne = kNoByte +}; + +class CommandHandler { +public: + struct Command { + CommandType _commandType; + byte _lab; + int _ref; + int _val; + void *_spritePtr; + CallbackType _cbType; + } *_commandList; + static const char *_commandText[]; + bool _talkEnable; + + CommandHandler(CGE2Engine *vm, bool turbo); + ~CommandHandler(); + void runCommand(); + void addCommand(CommandType com, int ref, int val, void *ptr); + void addCallback(CommandType com, int ref, int val, CallbackType cbType); + void insertCommand(CommandType com, int ref, int val, void *ptr); + bool idle(); + void reset(); + void clear(); + int com(const char *com); +private: + CGE2Engine *_vm; + bool _turbo; + uint8 _head; + uint8 _tail; + bool _busy; + bool _textDelay; + uint32 _timerExpiry; // "pause" in the original. + int _count; +}; + +} // End of namespace CGE2 + +#endif diff --git a/engines/cge2/sound.cpp b/engines/cge2/sound.cpp new file mode 100644 index 0000000000..9f5ec2bb6c --- /dev/null +++ b/engines/cge2/sound.cpp @@ -0,0 +1,254 @@ +/* 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 Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "cge2/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" +#include "cge2/cge2.h" + +namespace CGE2 { + +DataCk::DataCk(byte *buf, int bufSize) { + _buf = buf; + _ckSize = bufSize; +} + +DataCk::~DataCk() { + free(_buf); +} + +Sound::Sound(CGE2Engine *vm) : _vm(vm) { + _audioStream = NULL; + _soundRepeatCount = 1; + open(); +} + +Sound::~Sound() { + close(); +} + +void Sound::close() { + _vm->_midiPlayer->killMidi(); +} + +void Sound::open() { + setRepeat(1); + warning("STUB: Sound::open()"); + play(_vm->_fx->load(99, 99)); +} + +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)); + + // CGE pan: + // 8 = Center + // Less = Left + // More = Right + _vm->_mixer->setChannelBalance(_soundHandle, (int8)CLIP(((PSmpInfo->_span - 8) * 16), -127, 127)); +} + +void Sound::stop() { + sndDigiStop(&_smpinf); +} + +void Sound::sndDigiStop(SmpInfo *PSmpInfo) { + if (_vm->_mixer->isSoundHandleActive(_soundHandle)) + _vm->_mixer->stopHandle(_soundHandle); + _audioStream = NULL; +} + +Fx::Fx(CGE2Engine *vm, int size) : _current(NULL), _vm(vm) { +} + +Fx::~Fx() { + clear(); +} + +void Fx::clear() { + if (_current) + delete _current; + _current = NULL; +} + +Common::String Fx::name(int ref, int sub) { + char name[] = "%.2dfx%.2d.WAV\0"; + char subn[] = "%.2dfx%.2d?.WAV\0"; + char *p = (sub) ? subn : name; + Common::String filename = Common::String::format(p, ref >> 8, ref & 0xFF); + if (sub) + filename.setChar('@' + sub, 6); + return filename; +} + +bool Fx::exist(int ref, int sub) { + return _vm->_resman->exist(name(ref, sub).c_str()); +} + +DataCk *Fx::load(int ref, int sub) { + Common::String filename = name(ref, sub); + EncryptedStream file(_vm, filename.c_str()); + clear(); + return (_current = loadWave(&file)); +} + +DataCk *Fx::loadWave(EncryptedStream *file) { + byte *data = (byte *)malloc(file->size()); + + if (!data) + return 0; + + file->read(data, file->size()); + + return new DataCk(data, file->size()); +} + +MusicPlayer::MusicPlayer(CGE2Engine *vm) : _vm(vm) { + _data = NULL; + _isGM = false; + + MidiPlayer::createDriver(); + + int ret = _driver->open(); + if (ret == 0) { + if (_nativeMT32) + _driver->sendMT32Reset(); + else + _driver->sendGMReset(); + + // TODO: Load cmf.ins with the instrument table. It seems that an + // interface for such an operation is supported for AdLib. Maybe for + // this card, setting instruments is necessary. + + _driver->setTimerCallback(this, &timerCallback); + } + _dataSize = -1; +} + +MusicPlayer::~MusicPlayer() { + killMidi(); +} + +void MusicPlayer::killMidi() { + Audio::MidiPlayer::stop(); + + free(_data); + _data = NULL; +} + +void MusicPlayer::loadMidi(int ref) { + warning("STUB: MusicPlayer::loadMidi()"); // if (MidiNotify) MidiNotify(); + + // Work out the filename and check the given MIDI file exists + Common::String filename = Common::String::format("%.2dSG%.2d.MID", ref >> 8, ref & 0xFF); + if (!_vm->_resman->exist(filename.c_str())) + return; + + // Stop any currently playing MIDI file + killMidi(); + + // Read in the data for the file + EncryptedStream mid(_vm, filename.c_str()); + _dataSize = mid.size(); + _data = (byte *)malloc(_dataSize); + mid.read(_data, _dataSize); + + // Start playing the music + sndMidiStart(); +} + +void MusicPlayer::sndMidiStart() { + _isGM = true; + + MidiParser *parser = MidiParser::createParser_SMF(); + if (parser->loadMusic(_data, _dataSize)) { + parser->setTrack(0); + parser->setMidiDriver(this); + parser->setTimerRate(_driver->getBaseTempo()); + parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1); + + _parser = parser; + + syncVolume(); + + // Al the tracks are supposed to loop + _isLooping = true; + _isPlaying = true; + } +} + +void MusicPlayer::send(uint32 b) { + if ((b & 0xF0) == 0xC0 && !_isGM && !_nativeMT32) { + b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8; + } + + Audio::MidiPlayer::send(b); +} + +void MusicPlayer::sendToChannel(byte channel, uint32 b) { + if (!_channelsTable[channel]) { + _channelsTable[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel(); + // If a new channel is allocated during the playback, make sure + // its volume is correctly initialized. + if (_channelsTable[channel]) + _channelsTable[channel]->volume(_channelsVolume[channel] * _masterVolume / 255); + } + + if (_channelsTable[channel]) + _channelsTable[channel]->send(b); +} + +} // End of namespace CGE2 diff --git a/engines/cge2/sound.h b/engines/cge2/sound.h new file mode 100644 index 0000000000..6fa2b2a553 --- /dev/null +++ b/engines/cge2/sound.h @@ -0,0 +1,130 @@ +/* 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 Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_SOUND_H +#define CGE2_SOUND_H + +#include "cge2/fileio.h" +#include "audio/audiostream.h" +#include "audio/decoders/wave.h" +#include "audio/fmopl.h" +#include "audio/mididrv.h" +#include "audio/midiparser.h" +#include "audio/midiplayer.h" +#include "audio/mixer.h" +#include "common/memstream.h" + +namespace CGE2 { + +class CGE2Engine; + +// sample info +struct SmpInfo { + const uint8 *_saddr; // address + uint16 _slen; // length + uint16 _span; // left/right pan (0-15) + int _counter; // number of time the sample should be played +}; + +class DataCk { + byte *_buf; + int _ckSize; +public: + DataCk(byte *buf, int bufSize); + ~DataCk(); + inline const byte *addr() { + return _buf; + } + inline int size() { + return _ckSize; + } +}; + +class Sound { +public: + SmpInfo _smpinf; + + Sound(CGE2Engine *vm); + ~Sound(); + void open(); + void close(); + void play(DataCk *wav, int pan = 8); + int16 getRepeat(); + void setRepeat(int16 count); + void stop(); +private: + int _soundRepeatCount; + CGE2Engine *_vm; + Audio::SoundHandle _soundHandle; + Audio::RewindableAudioStream *_audioStream; + + void sndDigiStart(SmpInfo *PSmpInfo); + void sndDigiStop(SmpInfo *PSmpInfo); +}; + +class Fx { + CGE2Engine *_vm; + int _size; + + DataCk *loadWave(EncryptedStream *file); + Common::String name(int ref, int sub); +public: + DataCk *_current; + + Fx(CGE2Engine *vm, int size); + ~Fx(); + void clear(); + bool exist(int ref, int sub = 0); + DataCk *load(int ref, int sub = 0); +}; + +class MusicPlayer: public Audio::MidiPlayer { +private: + CGE2Engine *_vm; + byte *_data; + int _dataSize; + bool _isGM; + + // Start MIDI File + void sndMidiStart(); + + // Stop MIDI File + void sndMidiStop(); +public: + MusicPlayer(CGE2Engine *vm); + ~MusicPlayer(); + + void loadMidi(int ref); + void killMidi(); + + virtual void send(uint32 b); + virtual void sendToChannel(byte channel, uint32 b); +}; + +} // End of namespace CGE2 + +#endif // CGE2_SOUND_H diff --git a/engines/cge2/spare.cpp b/engines/cge2/spare.cpp new file mode 100644 index 0000000000..be2fc64d23 --- /dev/null +++ b/engines/cge2/spare.cpp @@ -0,0 +1,97 @@ +/* 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 Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "cge2/spare.h" + +namespace CGE2 { + +void Spare::synchronize() { + warning("STUB: Spare::Load"); +} + +void Spare::clear() { + _container.clear(); +} + +Sprite *Spare::locate(int ref) { + for (uint i = 0; i < _container.size(); ++i) { + if (_container[i]->_ref == ref) + return _container[i]; + } + return nullptr; +} + +void Spare::takeCave(int cav) { + int bakRef = cav << 8; + Common::Array<Sprite*> tempCont = _container; + for (uint i = 0; i < tempCont.size(); ++i) { + Sprite *spr = tempCont[i]; + int c = spr->_scene; + if ((c == _vm->_now || c == 0) && spr->_ref != bakRef) { + _vm->_vga->_showQ->insert(spr); + } + } +} + +void Spare::store(Sprite *spr) { + _container.insert_at(_container.size(), spr); +} + +void Spare::update(Sprite *spr) { + Sprite *sp = locate(spr->_ref); + if (sp == nullptr) + store(spr); +} + +void Spare::dispose(Sprite *spr) { + if (spr) { + _vm->_vga->_showQ->remove(spr); + update(spr->contract()); + if (!_vm->isHero(spr)) { + for (uint i = 0; i < _container.size(); ++i) { + if (spr == _container[i]) { + _container.remove_at(i); + delete spr; + break; + } + } + } + } +} + +void Spare::dispose(int ref) { + dispose(_vm->_vga->_showQ->locate(ref)); +} + +void Spare::dispose() { + for (uint i = 0; i < _container.size(); ++i) { + if (_container[i]->_ref > 255) + dispose(_container[i]); + } +} + +} // End of namespace CGE2 diff --git a/engines/cge2/spare.h b/engines/cge2/spare.h new file mode 100644 index 0000000000..7bd520d9d0 --- /dev/null +++ b/engines/cge2/spare.h @@ -0,0 +1,55 @@ +/* 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 Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_SPARE_H +#define CGE2_SPARE_H + +#include "cge2/vga13h.h" + +namespace CGE2 { + +class Spare { + CGE2Engine *_vm; + Common::Array<Sprite*> _container; +public: + Spare(CGE2Engine *vm) : _vm(vm) {} + ~Spare() { clear(); } + void store(Sprite *spr); + Sprite *locate(int ref); + void takeCave(int cav); + void update(Sprite *spr); + void dispose(Sprite *spr); + void dispose(int ref); + void dispose(); + void synchronize(); + uint16 count() { return _container.size(); } + void clear(); +}; + +} // End of namespace CGE2 + +#endif // CGE2_SPARE_H diff --git a/engines/cge2/talk.cpp b/engines/cge2/talk.cpp new file mode 100644 index 0000000000..2913327320 --- /dev/null +++ b/engines/cge2/talk.cpp @@ -0,0 +1,88 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "cge2/general.h" +#include "cge2/talk.h" +//#include "cge2/game.h" +//#include "cge2/events.h" +#include "cge2/cge2_main.h" + +namespace CGE2 { + +Font::Font(CGE2Engine *vm, const char *name) : _vm(vm) { + warning("STUB: Font::Font()"); +} + +Font::~Font() { + warning("STUB: Font::~Font()"); +} + +void Font::load() { + warning("STUB: Font::load()"); +} + +uint16 Font::width(const char *text) { + warning("STUB: Font::width()"); + return 0; +} + +Talk::Talk(CGE2Engine *vm, const char *text, TextBoxStyle mode, bool wideSpace) + : Sprite(vm), _mode(mode), _wideSpace(wideSpace), _vm(vm) { + warning("STUB: Talk::Talk()"); +} + +Talk::Talk(CGE2Engine *vm) + : Sprite(vm), _mode(kTBPure), _vm(vm) { + warning("STUB: Talk::Talk()"); +} + +void Talk::update(const char *text) { + warning("STUB: Talk::update()"); +} + +Bitmap *Talk::box(uint16 w, uint16 h) { + warning("STUB: Talk::box()"); + return *_ts; +} + +InfoLine::InfoLine(CGE2Engine *vm, uint16 w) : Talk(vm), _oldText(NULL), _vm(vm) { + warning("STUB: InfoLine::InfoLine()"); +} + +void InfoLine::update(const char *text) { + warning("STUB: InfoLine::update()"); +} + +void InfoLine::update() { + warning("STUB: InfoLine::update()"); +} + +void InfoLine::setText(const char *txt) { + warning("STUB: InfoLine::setText()"); +} + +} // End of namespace CGE2 diff --git a/engines/cge2/talk.h b/engines/cge2/talk.h new file mode 100644 index 0000000000..2d4c235d9d --- /dev/null +++ b/engines/cge2/talk.h @@ -0,0 +1,78 @@ +/* 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 Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_TALK_H +#define CGE2_TALK_H + +#include "cge2/general.h" +#include "cge2/vga13h.h" + +namespace CGE2 { + +#define kTextColFG kVgaColDark // foreground color +#define kTextColBG kVgaColGray // background color +#define kTextHMargin (6&~1) // EVEN horizontal margins! +#define kTextVMargin 5 // vertical margins +#define kTextLineSpace 2 // line spacing +#define kTextRoundCorner 3 // rounded corners +#define kWidSize 256 +#define kPosSize 256 +#define kMapSize (256*8) +#define kFontHigh 8 +#define kFontExt ".CFT" + +enum TextBoxStyle { kTBPure, kTBRect, kTBRound }; + +class Talk : public Sprite { +protected: + TextBoxStyle _mode; + BitmapPtr *_ts; + Bitmap *box(uint16 w, uint16 h); + bool _wideSpace; +public: + Talk(CGE2Engine *vm, const char *text, TextBoxStyle mode, bool wideSpace = false); + Talk(CGE2Engine *vm); + + virtual void update(const char *text); +private: + CGE2Engine *_vm; +}; + +class InfoLine : public Talk { + const char *_oldText; +public: + InfoLine(CGE2Engine *vm, uint16 wid); + void update(const char *text); + void update(); + void setText(const char *txt); +private: + CGE2Engine *_vm; +}; + +} // End of namespace CGE2 + +#endif // CGE2_TALK_H diff --git a/engines/cge2/text.cpp b/engines/cge2/text.cpp new file mode 100644 index 0000000000..2f8e9b4011 --- /dev/null +++ b/engines/cge2/text.cpp @@ -0,0 +1,152 @@ +/* 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 Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +//#include "cge/general.h" +#include "cge2/text.h" +//#include "cge/talk.h" +//#include "cge/game.h" +//#include "cge/snail.h" +#include "cge2/cge2_main.h" +#include "common/str.h" + +namespace CGE2 { + +Text::Text(CGE2Engine *vm, const char *fname) : _vm(vm) { + _vm->mergeExt(_fileName, fname, kSayExt); + if (!_vm->_resman->exist(_fileName)) + error("No talk (%s)", _fileName); + int16 txtCount = count() + 1; + if (!txtCount) + error("Unable to read dialog file %s", _fileName); + + _cache = new Handler[txtCount]; + for (_size = 0; _size < txtCount; _size++) { + _cache[_size]._ref = 0; + _cache[_size]._text = NULL; + } + load(); +} + +Text::~Text() { + clear(); + delete[] _cache; +} + +int16 Text::count() { + EncryptedStream tf(_vm, _fileName); + if (tf.err()) + return -1; + + Common::String line; + char tmpStr[kLineMax + 1]; + + int counter = 0; + + for (line = tf.readLine(); !tf.eos(); line = tf.readLine()) { + char *s; + assert(line.size() <= 513); + Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr)); + if ((s = strtok(tmpStr, " =,;/\t\n")) == NULL) + continue; + if (!Common::isDigit(*s)) + continue; + + counter++; + } + return counter; +} + +void Text::clear() { + for (Handler *p = _cache, *q = p + _size; p < q; p++) { + if (p->_ref) { + p->_ref = 0; + delete[] p->_text; + p->_text = NULL; + } + } +} + +void Text::load() { + EncryptedStream tf(_vm, _fileName); + assert(!tf.err()); + + Common::String line; + char tmpStr[kLineMax + 1]; + int idx; + + for (idx = 0, line = tf.readLine(); !tf.eos(); line = tf.readLine()) { + int n = line.size(); + char *s; + Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr)); + if ((s = strtok(tmpStr, " =,;/\t\n")) == NULL) + continue; + if (!Common::isDigit(*s)) + continue; + + int r = _vm->number(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) { + warning("STUB: Text::say()"); +} + +void CGE2Engine::inf(const char *text, bool wideSpace) { + warning("STUB: CGE2Engine::inf()"); +} + +void Text::sayTime(Sprite *spr) { + TimeDate curTime; + _vm->_system->getTimeAndDate(curTime); + + char t[6]; + sprintf(t, "%d:%02d", curTime.tm_hour, curTime.tm_min); + say(t, spr); +} + +} // End of namespace CGE2 diff --git a/engines/cge2/text.h b/engines/cge2/text.h new file mode 100644 index 0000000000..bca774357a --- /dev/null +++ b/engines/cge2/text.h @@ -0,0 +1,67 @@ +/* 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 Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_TEXT_H +#define CGE2_TEXT_H + +#include "cge2/talk.h" +#include "cge2/cge2.h" + +namespace CGE2 { + +#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(CGE2Engine *vm, const char *fname); + ~Text(); + void clear(); + char *getText(int ref); + void say(const char *text, Sprite *spr); + void sayTime(Sprite *spr); +private: + CGE2Engine *_vm; +}; + +} // End of namespace CGE2 + +#endif // CGE2_TEXT_H diff --git a/engines/cge2/vga13h.cpp b/engines/cge2/vga13h.cpp new file mode 100644 index 0000000000..618fb8a987 --- /dev/null +++ b/engines/cge2/vga13h.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 Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "common/array.h" +#include "common/config-manager.h" +#include "common/rect.h" +#include "graphics/palette.h" +#include "cge2/general.h" +#include "cge2/vga13h.h" +#include "cge2/bitmap.h" +#include "cge2/text.h" +#include "cge2/cge2_main.h" +#include "cge2/cge2.h" +#include "cge2/vga13h.h" + +namespace CGE2 { + +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; +} + +void(*Sprite::notify) (void) = nullptr; + +byte Sprite::_constY = 0; +byte Sprite::_follow = 0; + +Seq Sprite::_stdSeq8[] = +{ { 0, 0, 0, 0, 0, 0 }, + { 1, 1, 0, 0, 0, 0 }, + { 2, 2, 0, 0, 0, 0 }, + { 3, 3, 0, 0, 0, 0 }, + { 4, 4, 0, 0, 0, 0 }, + { 5, 5, 0, 0, 0, 0 }, + { 6, 6, 0, 0, 0, 0 }, + { 7, 7, 0, 0, 0, 0 }, +}; + +SprExt::SprExt(CGE2Engine *vm) + : _p0(vm, 0, 0), _p1(vm, 0, 0), + _b0(NULL), _b1(NULL), _shpList(NULL), + _location(0), _seq(NULL), _name(NULL) { + for (int i = 0; i < kActions; i++) + _actions[i] = nullptr; +} + +Sprite::Sprite(CGE2Engine *vm) + : _siz(_vm, 0, 0), _seqPtr(kNoSeq), _seqCnt(0), _shpCnt(0), + _next(NULL), _prev(NULL), _time(0), + _ext(NULL), _ref(-1), _scene(0), _vm(vm), + _pos2D(_vm, kScrWidth >> 1, 0), _pos3D(kScrWidth >> 1, 0, 0) { + memset(_actionCtrl, 0, sizeof(_actionCtrl)); + memset(_file, 0, sizeof(_file)); + memset(&_flags, 0, sizeof(_flags)); + _flags._frnt = 1; +} + +Sprite::Sprite(CGE2Engine *vm, BitmapPtr *shpP, int cnt) + : _siz(_vm, 0, 0), _seqPtr(kNoSeq), _seqCnt(0), _shpCnt(0), + _next(NULL), _prev(NULL), _time(0), + _ext(NULL), _ref(-1), _scene(0), _vm(vm), + _pos2D(_vm, kScrWidth >> 1, 0), _pos3D(kScrWidth >> 1, 0, 0) { + memset(_actionCtrl, 0, sizeof(_actionCtrl)); + memset(_file, 0, sizeof(_file)); + memset(&_flags, 0, sizeof(_flags)); + _flags._frnt = 1; + + setShapeList(shpP, cnt); +} + +Sprite::~Sprite() { + if (_vm->_sprite == this) + _vm->_sprite = NULL; + + contract(); +} + +BitmapPtr Sprite::shp() { + SprExt *e = _ext; + if (!e || !e->_seq) + return NULL; + + int i = e->_seq[_seqPtr]._now; + if (i >= _shpCnt) + error("Invalid PHASE in SPRITE::Shp() %s", _file); + return e->_shpList[i]; +} + +void Sprite::setShapeList(BitmapPtr *shp, int cnt) { + _shpCnt = cnt; + _siz.x = 0; + _siz.y = 0; + + if (shp) { + for (int i = 0; i < cnt; i++) { + BitmapPtr p = *shp + i; + if (p->_w > _siz.x) + _siz.x = p->_w; + if (p->_h > _siz.y) + _siz.y = p->_h; + } + expand(); + _ext->_shpList = shp; + if (!_ext->_seq) { + setSeq(_stdSeq8); + _seqCnt = (cnt < ARRAYSIZE(_stdSeq8)) ? cnt : ARRAYSIZE(_stdSeq8); + } + } +} + +bool Sprite::works(Sprite *spr) { + //if (!spr || !spr->_ext) + // return false; + + //CommandHandler::Command *c = spr->_ext->_take; + //if (c != NULL) { + // c += spr->_takePtr; + // if (c->_ref == _ref) + // if (c->_commandType != kCmdLabel || (c->_val == 0 || c->_val == _vm->_now)) + // return true; + //} + + warning("STUB: Sprite::works()"); + + return false; +} + +Seq *Sprite::setSeq(Seq *seq) { + 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; +} + +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); + } +} + +int Sprite::labVal(Action snq, int lab) { + warning("STUB: Sprite::labVal()"); + return 0; +} + +CommandHandler::Command *Sprite::snList(Action type) { + SprExt *e = _ext; + return (e) ? e->_actions[type] : NULL; +} + +Sprite *Sprite::expand() { + if (_ext) + return this; + + if (notify != nullptr) + notify(); + + _ext = new SprExt(_vm); + assert(_ext != NULL); + + if (!*_file) + return this; + + Common::Array<BitmapPtr> shplist; + for (int i = 0; i < _shpCnt; ++i) + shplist.push_back(NULL); + + int cnt[kActions], + shpcnt = 0, + seqcnt = 0, + maxnow = 0, + maxnxt = 0; + + char fname[kPathMax]; + _vm->mergeExt(fname, _file, kSprExt); + + Seq *seq; + if (_seqCnt) { + seq = new Seq[_seqCnt]; + if (seq == NULL) + error("No core %s", fname); + } else + seq = nullptr; + + for (int i = 0; i < kActions; i++) + cnt[i] = 0; + + for (int i = 0; i < kActions; i++){ + byte n = _actionCtrl[i]._cnt; + if (n) { + _ext->_actions[i] = new CommandHandler::Command[n]; + if (_ext->_actions[i] == nullptr) + error("No core %s", fname); + } else + _ext->_actions[i] = nullptr; + } + + if (_vm->_resman->exist(fname)) { // sprite description file exist + EncryptedStream sprf(_vm, fname); + if (sprf.err()) + error("Bad SPR [%s]", fname); + + int label = kNoByte; + ID section = kIdPhase; + ID id; + Common::String line; + char tmpStr[kLineMax + 1]; + + for (line = sprf.readLine(); !sprf.eos(); line = sprf.readLine()){ + if (line.size() == 0) + continue; + Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr)); + + char *p = _vm->token(tmpStr); + if (*p == '@') { + label = atoi(p + 1); + continue; + } + + id = _vm->ident(p); + switch (id) { + case kIdType: + break; + case kIdNear: + case kIdMTake: + case kIdFTake: + case kIdPhase: + case kIdSeq: + section = id; + break; + case kIdName: + Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr)); + for (p = tmpStr; *p != '='; p++); // We search for the = + setName(_vm->tail(p)); + break; + default: + if (id >= kIdNear) + break; + Seq *s; + switch (section) { + case kIdNear: + case kIdMTake: + case kIdFTake: + id = (ID)_vm->_commandHandler->com(p); + if (_actionCtrl[section]._cnt) { + CommandHandler::Command *c = &_ext->_actions[section][cnt[section]++]; + c->_commandType = CommandType(id); + c->_lab = label; + if ((p = _vm->token(nullptr)) == NULL) + error("Unexpected end of file! %s", fname); + c->_ref = _vm->number(p); + if ((p = _vm->token(nullptr)) == NULL) + error("Unexpected end of file! %s", fname); + c->_val = _vm->number(p); + c->_spritePtr = nullptr; + } + break; + case kIdSeq: + s = &seq[seqcnt++]; + s->_now = atoi(p); + if (s->_now > maxnow) + maxnow = s->_now; + if ((p = _vm->token(nullptr)) == NULL) + break; + s->_next = _vm->number(p); + switch (s->_next) { + case 0xFF: + s->_next = seqcnt; + break; + case 0xFE: + s->_next = seqcnt - 1; + break; + } + if (s->_next > maxnxt) + maxnxt = s->_next; + if ((p = _vm->token(nullptr)) == NULL) + error("Unexpected end of file! %s", fname); + s->_dx = _vm->number(p); + if ((p = _vm->token(nullptr)) == NULL) + error("Unexpected end of file! %s", fname); + s->_dy = _vm->number(p); + if ((p = _vm->token(nullptr)) == NULL) + error("Unexpected end of file! %s", fname); + s->_dz = _vm->number(p); + if ((p = _vm->token(nullptr)) == NULL) + error("Unexpected end of file! %s", fname); + s->_dly = _vm->number(p); + break; + case kIdPhase: + BitmapPtr bmp = new Bitmap(_vm, p); + shplist[shpcnt] = bmp; + if (!shplist[shpcnt]->moveHi()) + error("No EMS"); + shpcnt++; + break; + } + break; + } + label = kNoByte; + } + + if (!shpcnt) + error("No shapes - %s", fname); + } else // no sprite description: try to read immediately from .BMP + shplist[shpcnt++] = new Bitmap (_vm, _file); + + if (seq) { + if (maxnow >= shpcnt) + error("Bad PHASE in SEQ %s", fname); + if (maxnxt && maxnxt >= seqcnt) + error("Bad JUMP in SEQ %s", fname); + setSeq(seq); + } else { + setSeq(_stdSeq8); + _seqCnt = (shpcnt < ARRAYSIZE(_stdSeq8)) ? shpcnt : ARRAYSIZE(_stdSeq8); + } + + // Set the shape list + BitmapPtr *shapeList = new BitmapPtr[shplist.size()]; + for (uint i = 0; i < shplist.size(); ++i) + shapeList[i] = shplist[i]; + + setShapeList(shapeList, shpcnt); + + if (_file[2] == '~') { // FLY-type sprite + Seq *seq = _ext->_seq; + int x = (seq + 1)->_dx, y = (seq + 1)->_dy, z = (seq + 1)->_dz; + // random position + seq->_dx = _vm->newRandom(x + x) - x; + seq->_dy = _vm->newRandom(y + y) - y; + seq->_dz = _vm->newRandom(z + z) - z; + gotoxyz(_pos3D + V3D(seq->_dx, seq->_dy, seq->_dz)); + } + + return this; +} + +Sprite *Sprite::contract() { + SprExt *e = _ext; + if (!e) + return this; + + if (_file[2] == '~') { // FLY-type sprite + Seq *seq = _ext->_seq; + // return to middle + gotoxyz(_pos3D - V3D(seq->_dx, seq->_dy, seq->_dz)); + seq->_dx = seq->_dy = seq->_dz = 0; + } + + if (notify) + notify(); + + if (e->_name) + delete[] e->_name; + + if (e->_shpList) { + for (int i = 0; i < _shpCnt; i++) + e->_shpList[i]->release(); + delete[] e->_shpList; + } + + if (e->_seq) { + if (e->_seq == _stdSeq8) + _seqCnt = 0; + else + delete[] e->_seq; + } + + for (int i = 0; i < kActions; i++) + if (e->_actions[i]) + delete[] e->_actions[i]; + + _ext = nullptr; + + return this; +} + +void Sprite::backShow(void) { + expand(); + show(2); + show(1); + _vm->_spare->dispose(this); +} + +void Sprite::step(int nr) { + if (nr >= 0) + _seqPtr = nr; + + if (_ext) { + V3D p = _pos3D; + Seq *seq = nullptr; + + if (nr < 0) + _seqPtr = _ext->_seq[_seqPtr]._next; + + if (_file[2] == '~') { // FLY-type sprite + warning("STUB: Sprite::step() - FLY-type sprite"); + } else { + seq = _ext->_seq + _seqPtr; + if (seq->_dz == 127 && seq->_dx != 0) { + _vm->_commandHandlerTurbo->addCommand(kCmdSound, -1, 256 * seq->_dy + seq->_dx, this); + } else { + p._x += seq->_dx; + p._y += seq->_dy; + p._z += seq->_dz; + //if (!_flags._kept) + // gotoxyz(p); + warning("STUB: Sprite::step()"); + } + } + if (seq && (seq->_dly >= 0)) + _time = seq->_dly; + } else if (_vm->_waitRef && _vm->_waitRef == _ref) + _vm->_waitRef = 0; +} + +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::gotoxyz(int x, int y, int z) { + gotoxyz(V3D(x, y, z)); +} + +void Sprite::gotoxyz(void) { + gotoxyz(_pos3D); +} + +void Sprite::gotoxyz(V2D pos) { + V2D o = _pos2D; + int ctr = _siz.x >> 1; + int rem = _siz.x - ctr; + byte trim = 0; + + if (_ref / 10 == 14) { // HERO + int z = V2D::trunc(_pos3D._z); + ctr = (ctr * V2D::trunc(_vm->_eye->_z) / (V2D::trunc(_vm->_eye->_z) - z)); + rem = (rem * V2D::trunc(_vm->_eye->_z) / (V2D::trunc(_vm->_eye->_z) - z)); + ctr = (ctr * 3) / 4; + rem = (rem * 3) / 4; + } + + if (pos.x - ctr < 0) { + pos.x = ctr; + ++trim; + } + if (pos.x + rem > kScrWidth) { + pos.x = kScrWidth - rem; + ++trim; + } + _pos2D.x = pos.x; + + if (pos.y < -kPanHeight) { + pos.y = -kPanHeight; + ++trim; + } + if (pos.y + _siz.y > kWorldHeight) { + pos.y = kWorldHeight - _siz.y; + ++trim; + } + _pos2D.y = pos.y; + + _flags._trim = (trim != 0); + + if (!_follow) { + double m = _vm->_eye->_z / (_pos3D._z - _vm->_eye->_z); + _pos3D._x = (_vm->_eye->_x + V2D::round(_vm->_eye->_x - _pos2D.x) / m); + if (!_constY) + _pos3D._y = (_vm->_eye->_y + V2D::round(_vm->_eye->_y - _pos2D.y) / m); + } + + if (_next && _next->_flags._slav) + _next->gotoxyz(_next->_pos2D - o + _pos2D); + + if (_flags._shad) + _prev->gotoxyz(_prev->_pos2D - o + _pos2D); + + if (_ref == 141 && _pos3D._y >= 5) + warning("Sprite::gotoxyz - asm nop"); +} + +void Sprite::gotoxyz_(V2D pos) { + _constY++; + gotoxyz(pos); + --_constY; +} + +void Sprite::gotoxyz(V3D pos) { + _follow++; + if (pos._z != _pos3D._z) + _flags._zmov = true; + gotoxyz(V2D(_vm, _pos3D = pos)); + --_follow; +} + +void Sprite::center() { + gotoxyz(kScrWidth >> 1, (kWorldHeight - _siz.y) >> 1, 0); +} + +void Sprite::show() { + SprExt *e = _ext; + if (e) { + e->_p0 = e->_p1; + e->_b0 = e->_b1; + e->_p1 = _pos2D; + e->_b1 = shp(); + } + if (!_flags._hide) + e->_b1->show(e->_p1.x, e->_p1.y); +} + +void Sprite::show(uint16 pg) { + Graphics::Surface *a = _vm->_vga->_page[1]; + _vm->_vga->_page[1] = _vm->_vga->_page[pg]; + shp()->show(_pos2D.x, _pos2D.y); + _vm->_vga->_page[1] = a; +} + +void Sprite::hide() { + SprExt *e = _ext; + if (e->_b0) + e->_b0->hide(e->_p0.x, e->_p0.y); +} + +BitmapPtr Sprite::ghost() { + SprExt *e = _ext; + if (!e->_b1) + return nullptr; + + BitmapPtr bmp = new Bitmap(_vm, 0, 0, (uint8 *)nullptr); + assert(bmp != nullptr); + bmp->_w = e->_b1->_w; + bmp->_h = e->_b1->_h; + bmp->_b = new HideDesc[bmp->_h]; + assert(bmp->_b != nullptr); + memcpy(bmp->_b, e->_b1->_b, sizeof(HideDesc)* bmp->_h); + uint8 *v = new uint8; + *v = (e->_p1.y << 16) + e->_p1.x; + bmp->_v = v; + + return bmp; +} + +void Sprite::sync(Common::Serializer &s) { + uint16 unused = 0; + + s.syncAsUint16LE(unused); + s.syncAsUint16LE(unused); // _ext + s.syncAsUint16LE(_ref); + s.syncAsByte(_scene); + + // bitfield in-memory storage is unpredictable, so to avoid + // any issues, pack/unpack everything manually + uint16 flags = 0; + if (s.isLoading()) { + s.syncAsUint16LE(flags); + _flags._hide = flags & 0x0001 ? true : false; + _flags._near = flags & 0x0002 ? true : false; + _flags._drag = flags & 0x0004 ? true : false; + _flags._hold = flags & 0x0008 ? true : false; + //_flags._dummy = 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._dummy; + flags = (flags << 1) | _flags._hold; + flags = (flags << 1) | _flags._drag; + flags = (flags << 1) | _flags._near; + flags = (flags << 1) | _flags._hide; + s.syncAsUint16LE(flags); + } + + warning("STUB: Sprite::sync() - Flags changed compared to CGE1's Sprite type."); + + s.syncAsUint16LE(_pos3D._x); + s.syncAsUint16LE(_pos3D._y); + s.syncAsByte(_pos3D._z); + s.syncAsUint16LE(_time); + s.syncAsSint16LE(_seqPtr); + s.syncAsUint16LE(_shpCnt); + s.syncBytes((byte *)&_file[0], 9); + _file[8] = '\0'; + + s.syncAsUint16LE(unused); // _prev + s.syncAsUint16LE(unused); // _next +} + +Queue::Queue(bool show) : _head(NULL), _tail(NULL) { +} + +Queue::~Queue() { + clear(); +} + +void Queue::clear() { + while (_head) { + Sprite *s = remove(_head); + if (s->_flags._kill) + delete s; + } +} + +void Queue::append(Sprite *spr) { + if (spr->_flags._back) + spr->backShow(); + else { + spr->expand(); + if (_tail) { + spr->_prev = _tail; + _tail->_next = spr; + } else + _head = spr; + + _tail = spr; + } +} + +void Queue::insert(Sprite *spr, Sprite *nxt) { + if (spr->_flags._back) + spr->backShow(); + else { + spr->expand(); + if (nxt == _head) { + spr->_next = _head; + _head = spr; + if (!_tail) + _tail = spr; + } else { + spr->_next = nxt; + spr->_prev = nxt->_prev; + if (spr->_prev) + spr->_prev->_next = spr; + } + if (spr->_next) + spr->_next->_prev = spr; + } +} + +void Queue::insert(Sprite *spr) { + Sprite *s; + for (s = _head; s; s = s->_next) + if (s->_pos3D._z < spr->_pos3D._z) + break; + + if (s) + insert(spr, s); + else + append(spr); +} + +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; +} + +bool Queue::locate(Sprite *spr) { + Sprite *s; + for (s = _head; s; s = s->_next) + if (s == spr) + return true; + return false; +} + +Vga::Vga(CGE2Engine *vm) : _frmCnt(0), _msg(NULL), _name(NULL), _setPal(false), _mono(0), _vm(vm) { + _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(kScrWidth, kScrHeight, Graphics::PixelFormat::createFormatCLUT8()); + } + + /*if (ConfMan.getBool("enable_color_blind")) + _mono = 1;*/ + + warning("STUB: Vga::Vga()"); + + _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); +*/ + warning("STUB: Vga::~Vga()"); + + free(_oldColors); + free(_newColors); + if (_msg) + buffer = Common::String(_msg); + if (_name) + buffer = buffer + " [" + _name + "]"; + + debugN("%s", buffer.c_str()); + + delete _showQ; + delete _spareQ; + delete[] _sysPal; + + for (int idx = 0; idx < 4; idx++) { + _page[idx]->free(); + delete _page[idx]; + } +} + +void Vga::waitVR() { + // Since some of the game parts rely on using vertical sync as a delay mechanism, + // we're introducing a short delay to simulate it + g_system->delayMillis(5); +} + +void Vga::getColors(Dac *tab) { + byte palData[kPalSize]; + g_system->getPaletteManager()->grabPalette(palData, 0, kPalCount); + palToDac(palData, tab); +} + +uint8 Vga::closest(Dac *pal, const uint8 colR, const uint8 colG, const uint8 colB) { +#define f(col, lum) ((((uint16)(col)) << 8) / lum) + uint16 i, dif = 0xFFFF, found = 0; + uint16 L = colR + colG + colB; + if (!L) + L++; + uint16 R = f(colR, L), G = f(colG, L), B = f(colB, L); + for (i = 0; i < 256; i++) { + uint16 l = pal[i]._r + pal[i]._g + pal[i]._b; + if (!l) + l++; + int r = f(pal[i]._r, l), g = f(pal[i]._g, l), b = f(pal[i]._b, l); + uint16 D = ((r > R) ? (r - R) : (R - r)) + + ((g > G) ? (g - G) : (G - g)) + + ((b > B) ? (b - B) : (B - b)) + + ((l > L) ? (l - L) : (L - l)) * 10 ; + + if (D < dif) { + found = i; + dif = D; + if (D == 0) + break; // exact! + } + } + return found; +#undef f +} + +uint8 *Vga::glass(Dac *pal, const uint8 colR, const uint8 colG, const uint8 colB) { + uint8 *x = (uint8 *)malloc(256); + if (x) { + uint16 i; + for (i = 0; i < 256; i++) { + x[i] = closest(pal, ((uint16)(pal[i]._r) * colR) / 255, + ((uint16)(pal[i]._g) * colG) / 255, + ((uint16)(pal[i]._b) * colB) / 255); + } + } + return x; +} + +void Vga::palToDac(const byte *palData, Dac *tab) { + const byte *colP = palData; + for (int idx = 0; idx < kPalCount; idx++, colP += 3) { + tab[idx]._r = *colP >> 2; + tab[idx]._g = *(colP + 1) >> 2; + tab[idx]._b = *(colP + 2) >> 2; + } +} + +void Vga::dacToPal(const Dac *tab, byte *palData) { + for (int idx = 0; idx < kPalCount; idx++, palData += 3) { + *palData = tab[idx]._r << 2; + *(palData + 1) = tab[idx]._g << 2; + *(palData + 2) = tab[idx]._b << 2; + } +} + +void Vga::setColors(Dac *tab, int lum) { + Dac *palP = tab, *destP = _newColors; + for (int idx = 0; idx < kPalCount; idx++, palP++, destP++) { + destP->_r = (palP->_r * lum) >> 6; + destP->_g = (palP->_g * lum) >> 6; + destP->_b = (palP->_b * lum) >> 6; + } + + if (_mono) { + destP = _newColors; + for (int idx = 0; idx < kPalCount; idx++, destP++) { + // Form a greyscalce color 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; + } + /*if (_vm->_showBoundariesFl) { + Vga::_page[0]->hLine(0, 200 - kPanHeight, 320, 0xee); + if (_vm->_barriers[_vm->_now]._horz != 255) { + for (int i = 0; i < 8; i++) + Vga::_page[0]->vLine((_vm->_barriers[_vm->_now]._horz * 8) + i, 0, 200, 0xff); + } + if (_vm->_barriers[_vm->_now]._vert != 255) { + for (int i = 0; i < 4; i++) + Vga::_page[0]->hLine(0, 80 + (_vm->_barriers[_vm->_now]._vert * 4) + i, 320, 0xff); + } + }*/ + + warning("STUB: Vga::update()"); + + g_system->copyRectToScreen(Vga::_page[0]->getPixels(), 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) { + const byte *srcP = (const byte *)_v; + byte *destEndP = (byte *)_vm->_vga->_page[1]->getBasePtr(0, kScrHeight); + byte *lookupTable = _m; + + // Loop through processing data for each plane. The game originally ran in plane mapped mode, where a + // given plane holds each fourth pixel sequentially. So to handle an entire picture, each plane's data + // must be decompressed and inserted into the surface + for (int planeCtr = 0; planeCtr < 4; planeCtr++) { + byte *destP = (byte *)_vm->_vga->_page[1]->getBasePtr(x + planeCtr, y); + + for (;;) { + uint16 v = READ_LE_UINT16(srcP); + srcP += 2; + int cmd = v >> 14; + int count = v & 0x3FFF; + + if (cmd == 0) { + // End of image + break; + } + + assert(destP < destEndP); + + if (cmd == 2) + srcP++; + else if (cmd == 3) + srcP += count; + + // Handle a set of pixels + while (count-- > 0) { + // Transfer operation + switch (cmd) { + case 1: + // SKIP + break; + case 2: + case 3: + // TINT + *destP = lookupTable[*destP]; + break; + } + + // Move to next dest position + destP += 4; + } + } + } +} + + +void Bitmap::show(int16 x, int16 y) { + V2D pos(_vm, x, y); + xLatPos(pos); + x = pos.x; + y = pos.y; + const byte *srcP = (const byte *)_v; + byte *destEndP = (byte *)_vm->_vga->_page[1]->getBasePtr(0, kScrHeight); + + // Loop through processing data for each plane. The game originally ran in plane mapped mode, where a + // given plane holds each fourth pixel sequentially. So to handle an entire picture, each plane's data + // must be decompressed and inserted into the surface + for (int planeCtr = 0; planeCtr < 4; planeCtr++) { + byte *destP = (byte *)_vm->_vga->_page[1]->getBasePtr(x + planeCtr, y); + + for (;;) { + uint16 v = READ_LE_UINT16(srcP); + srcP += 2; + int cmd = v >> 14; + int count = v & 0x3FFF; + + if (cmd == 0) { + // End of image + break; + } + + assert(destP < destEndP); + + // Handle a set of pixels + while (count-- > 0) { + // Transfer operation + switch (cmd) { + case 1: + // SKIP + break; + case 2: + // REPEAT + *destP = *srcP; + break; + case 3: + // COPY + *destP = *srcP++; + break; + } + + // Move to next dest position + destP += 4; + } + + if (cmd == 2) + srcP++; + } + } +} + + +void Bitmap::hide(int16 x, int16 y) { + V2D pos(_vm, x, y); + xLatPos(pos); + x = pos.x; + y = pos.y; + for (int yp = y; yp < y + _h; yp++) { + const byte *srcP = (const byte *)_vm->_vga->_page[2]->getBasePtr(x, yp); + byte *destP = (byte *)_vm->_vga->_page[1]->getBasePtr(x, yp); + + Common::copy(srcP, srcP + _w, destP); + } +} + +/*--------------------------------------------------------------------------*/ + +HorizLine::HorizLine(CGE2Engine *vm) : Sprite(vm), _vm(vm) { + // Set the sprite list + BitmapPtr *HL = new BitmapPtr[2]; + HL[0] = new Bitmap(_vm, "HLINE"); + HL[1] = NULL; + + setShapeList(HL, 1); + + warning("HorizLine::HorizLine() - Recheck this!"); +} + +SceneLight::SceneLight(CGE2Engine *vm) : Sprite(vm), _vm(vm) { + // Set the sprite list + BitmapPtr *PR = new BitmapPtr[2]; + PR[0] = new Bitmap(_vm, "PRESS"); + PR[1] = NULL; + + setShapeList(PR, 1); + + warning("SceneLight::SceneLight() - Recheck this!"); +} + +Speaker::Speaker(CGE2Engine *vm): Sprite(vm), _vm(vm) { + // Set the sprite list + BitmapPtr *SP = new BitmapPtr[3]; + SP[0] = new Bitmap(_vm, "SPK_L"); + SP[1] = new Bitmap(_vm, "SPK_R"); + SP[2] = NULL; + + setShapeList(SP, 2); + + warning("Speaker::Speaker() - Recheck this!"); +} + +PocLight::PocLight(CGE2Engine *vm): Sprite(vm), _vm(vm) { + // Set the sprite list + BitmapPtr *LI = new BitmapPtr[5]; + LI[0] = new Bitmap(_vm, "LITE0"); + LI[1] = new Bitmap(_vm, "LITE1"); + LI[2] = new Bitmap(_vm, "LITE2"); + LI[3] = new Bitmap(_vm, "LITE3"); + LI[4] = NULL; + + setShapeList(LI, 4); + + _flags._kill = false; + + warning("PocLight::PocLight() - Recheck this!"); +} + +} // End of namespace CGE2 diff --git a/engines/cge2/vga13h.h b/engines/cge2/vga13h.h new file mode 100644 index 0000000000..20d97ee4eb --- /dev/null +++ b/engines/cge2/vga13h.h @@ -0,0 +1,295 @@ +/* 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 Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_VGA13H_H +#define CGE2_VGA13H_H + +#include "common/serializer.h" +#include "common/events.h" +#include "graphics/surface.h" +#include "cge2/general.h" +#include "cge2/bitmap.h" +#include "cge2/snail.h" +#include "cge2/spare.h" +#include "cge2/cge2.h" + +namespace CGE2 { + +#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) + +// From CGETYPE.H: +class V3D { +public: + double _x, _y, _z; + V3D(void) { } + V3D(double x, double y, double z = 0) : _x(x), _y(y), _z(z) { } + V3D(const V3D &p) : _x(p._x), _y(p._y), _z(p._z) { } + V3D operator+(const V3D &p) const { return V3D(_x + p._x, _y + p._y, _z + p._z); } + V3D operator-(const V3D &p) const { return V3D(_x - p._x, _y - p._y, _z - p._z); } + V3D operator*(long n) const { return V3D(_x * n, _y * n, _z * n); } + V3D operator/ (long n) const { return V3D(_x / n, _y / n, _z / n); } + bool operator==(V3D &p) const { return _x == p._x && _y == p._y && _z == p._z; } + bool operator!=(V3D &p) const { return _x != p._x || _y != p._y || _z != p._z; } + V3D& operator+=(const V3D &x) { return *this = *this + x; } + V3D& operator-=(const V3D &x) { return *this = *this - x; } +}; + +class V2D : public Common::Point { + CGE2Engine *_vm; +public: + V2D& operator=(const V3D &p3) { + double m = _vm->_eye->_z / (p3._z - _vm->_eye->_z); + x = round((_vm->_eye->_x + (_vm->_eye->_x - p3._x) * m)); + y = round((_vm->_eye->_y + (_vm->_eye->_y - p3._y) * m)); + return *this; + } + V2D(CGE2Engine *vm) : _vm(vm) { } + V2D(CGE2Engine *vm, const V3D &p3) : _vm(vm) { *this = p3; } + V2D(CGE2Engine *vm, int x, int y) : _vm(vm), Common::Point(x, y) { } + bool operator<(const V2D &p) const { return (x < p.x) && (y < p.y); } + bool operator<=(const V2D &p) const { return (x <= p.x) && (y <= p.y); } + bool operator>(const V2D &p) const { return (x > p.x) && (y > p.y); } + bool operator>=(const V2D &p) const { return (x >= p.x) && (y >= p.y); } + V2D operator+(const V2D &p) const { return V2D(_vm, x + p.x, y + p.y); } + V2D operator-(const V2D &p) const { return V2D(_vm, x - p.x, y - p.y); } + uint16 area(void) { return x * y; } + bool limited(const V2D &p) { + return (uint16(x) < uint16(p.x)) && (uint16(y) < uint16(p.y)); + } + V2D scale(int z) { + double m = _vm->_eye->_z / (_vm->_eye->_z - z); + return V2D(_vm, trunc(m * x), trunc(m * y)); + } + static double trunc(double d) { return (d > 0) ? floor(d) : ceil(d); } + static double round(double number) { return number < 0.0 ? ceil(number - 0.5) : floor(number + 0.5); } +}; + +struct Seq { + uint8 _now; + uint8 _next; + int8 _dx; + int8 _dy; + int8 _dz; + int _dly; +}; + +class SprExt { +public: + V2D _p0; + V2D _p1; + BitmapPtr _b0; + BitmapPtr _b1; + BitmapPtr *_shpList; + int _location; + Seq *_seq; + char *_name; + CommandHandler::Command *_actions[kActions]; + SprExt(CGE2Engine *vm); +}; + +class Sprite { +protected: + SprExt *_ext; + CGE2Engine *_vm; +public: + int _ref; + signed char _scene; + struct Flags { + uint16 _hide : 1; // general visibility switch + uint16 _drag : 1; // sprite is moveable + uint16 _hold : 1; // sprite is held with mouse + uint16 _trim : 1; // Trim flag + uint16 _slav : 1; // slave 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 _frnt : 1; // stay in front of sprite + uint16 _east : 1; // talk to east (in opposite to west) + uint16 _near : 1; // Near action lock + uint16 _shad : 1; // shadow + uint16 _back : 1; // 'send to background' request + uint16 _zmov : 1; // sprite needs Z-update in queue + uint16 _tran : 1; // transparent (untouchable) + } _flags; + V2D _pos2D; + V3D _pos3D; + V2D _siz; + uint16 _time; + struct { byte _ptr, _cnt; } _actionCtrl[kActions]; + int _seqPtr; + int _seqCnt; + int _shpCnt; + char _file[kMaxFile]; + // Following trailer is not saved with the game: + Sprite *_prev; + Sprite *_next; + static byte _constY; + static byte _follow; + static Seq _stdSeq8[]; + + bool works(Sprite *spr); + bool seqTest(int n); + inline bool active() { + return _ext != NULL; + } + Sprite(CGE2Engine *vm); + Sprite(CGE2Engine *vm, BitmapPtr *shp, int cnt); + virtual ~Sprite(); + BitmapPtr shp(); + void setShapeList(BitmapPtr *shp, int cnt); + void moveShapesHi(void); + void moveShapesLo(void); + int labVal(Action snq, int lab); + Sprite *expand(); + Sprite *contract(); + void backShow(void); + void setName(char *newName); + inline char *name() { + return (_ext) ? _ext->_name : NULL; + } + void gotoxyz(int x, int y, int z = 0); + void gotoxyz(void); + void gotoxyz(V2D pos); + void gotoxyz_(V2D pos); + void gotoxyz(V3D pos); + void center(); + void show(uint16 pg); + void hide(uint16 pg); + void show(); + void hide(); + BitmapPtr ghost(); + void makeXlat(uint8 *x); + void killXlat(); + void step(int nr = -1); + Seq *setSeq(Seq *seq); + CommandHandler::Command *snList(Action type); + //virtual void touch(uint16 mask, int x, int y, Common::KeyCode keyCode); + virtual void tick(); + void clrHide(void) { if (_ext) _ext->_b0 = NULL; } + void sync(Common::Serializer &s); + + static void (*notify) (void); +}; + +class Queue { + Sprite *_head; + Sprite *_tail; +public: + Queue(bool show); + ~Queue(); + + void append(Sprite *spr); + void insert(Sprite *spr, Sprite *nxt); + void insert(Sprite *spr); + Sprite *remove(Sprite *spr); + Sprite *first() { + return _head; + } + Sprite *last() { + return _tail; + } + Sprite *locate(int ref); + bool locate(Sprite *spr); + void clear(); +}; + +class Vga { + CGE2Engine *_vm; + bool _setPal; + Dac *_oldColors; + Dac *_newColors; + const char *_msg; + const char *_name; + + void updateColors(); + void setColors(); + void waitVR(); + uint8 closest(Dac *pal, const uint8 colR, const uint8 colG, const uint8 colB); + +public: + uint32 _frmCnt; + Queue *_showQ; + Queue *_spareQ; + int _mono; + Graphics::Surface *_page[4]; + Dac *_sysPal; + + Vga(CGE2Engine *vm); + ~Vga(); + + uint8 *glass(Dac *pal, const uint8 colR, const uint8 colG, const uint8 colB); + void getColors(Dac *tab); + void setColors(Dac *tab, int lum); + void clear(uint8 color); + void copyPage(uint16 d, uint16 s); + void sunrise(Dac *tab); + void sunset(); + void show(); + void update(); + + void palToDac(const byte *palData, Dac *tab); + void dacToPal(const Dac *tab, byte *palData); +}; + +class HorizLine: public Sprite { + CGE2Engine *_vm; +public: + HorizLine(CGE2Engine *vm); +}; + +class SceneLight: public Sprite { + CGE2Engine *_vm; +public: + SceneLight(CGE2Engine *vm); +}; + +class Speaker: public Sprite { + CGE2Engine *_vm; +public: + Speaker(CGE2Engine *vm); +}; + +class PocLight: public Sprite { + CGE2Engine *_vm; +public: + PocLight(CGE2Engine *vm); +}; + +} // End of namespace CGE2 + +#endif // CGE2_VGA13H_H |