aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
Diffstat (limited to 'engines')
-rw-r--r--engines/mads/assets.cpp95
-rw-r--r--engines/mads/assets.h69
-rw-r--r--engines/mads/compression.cpp184
-rw-r--r--engines/mads/compression.h80
-rw-r--r--engines/mads/configure.engine3
-rw-r--r--engines/mads/debugger.cpp49
-rw-r--r--engines/mads/debugger.h45
-rw-r--r--engines/mads/detection.cpp178
-rw-r--r--engines/mads/detection_tables.h47
-rw-r--r--engines/mads/dialogs.cpp344
-rw-r--r--engines/mads/dialogs.h196
-rw-r--r--engines/mads/events.cpp160
-rw-r--r--engines/mads/events.h123
-rw-r--r--engines/mads/font.cpp208
-rw-r--r--engines/mads/font.h75
-rw-r--r--engines/mads/game.cpp208
-rw-r--r--engines/mads/game.h125
-rw-r--r--engines/mads/game_data.cpp116
-rw-r--r--engines/mads/game_data.h126
-rw-r--r--engines/mads/graphics.cpp30
-rw-r--r--engines/mads/graphics.h36
-rw-r--r--engines/mads/mads.cpp112
-rw-r--r--engines/mads/mads.h118
-rw-r--r--engines/mads/module.mk35
-rw-r--r--engines/mads/msprite.cpp107
-rw-r--r--engines/mads/msprite.h114
-rw-r--r--engines/mads/msurface.cpp262
-rw-r--r--engines/mads/msurface.h179
-rw-r--r--engines/mads/nebular/dialogs_nebular.cpp118
-rw-r--r--engines/mads/nebular/dialogs_nebular.h76
-rw-r--r--engines/mads/nebular/game_nebular.cpp190
-rw-r--r--engines/mads/nebular/game_nebular.h69
-rw-r--r--engines/mads/nebular/nebular_scenes.cpp49
-rw-r--r--engines/mads/nebular/nebular_scenes.h91
-rw-r--r--engines/mads/nebular/nebular_scenes8.cpp50
-rw-r--r--engines/mads/nebular/nebular_scenes8.h55
-rw-r--r--engines/mads/nebular/sound_nebular.cpp1207
-rw-r--r--engines/mads/nebular/sound_nebular.h412
-rw-r--r--engines/mads/palette.cpp447
-rw-r--r--engines/mads/palette.h265
-rw-r--r--engines/mads/resources.cpp326
-rw-r--r--engines/mads/resources.h76
-rw-r--r--engines/mads/scene.cpp179
-rw-r--r--engines/mads/scene.h144
-rw-r--r--engines/mads/scene_data.cpp358
-rw-r--r--engines/mads/scene_data.h305
-rw-r--r--engines/mads/sound.cpp112
-rw-r--r--engines/mads/sound.h103
-rw-r--r--engines/mads/user_interface.cpp43
-rw-r--r--engines/mads/user_interface.h46
50 files changed, 8145 insertions, 0 deletions
diff --git a/engines/mads/assets.cpp b/engines/mads/assets.cpp
new file mode 100644
index 0000000000..4c075d054e
--- /dev/null
+++ b/engines/mads/assets.cpp
@@ -0,0 +1,95 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "mads/mads.h"
+#include "mads/assets.h"
+#include "mads/compression.h"
+#include "mads/events.h"
+
+namespace MADS {
+
+SpriteAsset::SpriteAsset(MADSEngine *vm, const Common::String &resourceName, int flags):
+ _vm(vm) {
+ Common::String resName = resourceName;
+ if (!resName.hasSuffix(".SS"))
+ resName += ".SS";
+
+ File file(resName);
+ MadsPack sprites(&file);
+
+ int curFrame = 0;
+ uint32 frameOffset = 0;
+ _frameRate = 0;
+ _pixelSpeed = 0;
+ _maxWidth = 0;
+ _maxHeight = 0;
+
+ Common::SeekableReadStream *spriteStream = sprites.getItemStream(0);
+ for (int i = 0; i < 19; i++) {
+ spriteStream->readUint16LE();
+ }
+ _frameCount = spriteStream->readUint16LE();
+ // we skip the rest of the data
+ delete spriteStream;
+
+ // Get the palette data
+ spriteStream = sprites.getItemStream(2);
+ int numColors = 0;
+ byte *palData = _vm->_palette->decodePalette(spriteStream, &numColors);
+
+ Common::copy(palData, &palData[numColors], &_palette[0]);
+ if (numColors < 256)
+ Common::fill(&_palette[numColors * 3], &_palette[PALETTE_SIZE], 0);
+ _colorCount = numColors;
+ delete[] palData;
+ delete spriteStream;
+
+ spriteStream = sprites.getItemStream(1);
+ Common::SeekableReadStream *spriteDataStream = sprites.getItemStream(3);
+
+ SpriteAssetFrame frame;
+ for (curFrame = 0; curFrame < _frameCount; curFrame++) {
+ frame.comp = 0;
+ frameOffset = spriteStream->readUint32LE();
+ _frameOffsets.push_back(frameOffset);
+ spriteStream->readUint32LE(); // frame size
+ frame.x = spriteStream->readUint16LE();
+ frame.y = spriteStream->readUint16LE();
+ frame.w = spriteStream->readUint16LE();
+ frame.h = spriteStream->readUint16LE();
+ if (curFrame == 0) {
+ debugN(kDebugGraphics, "%i frames, x = %i, y = %i, w = %i, h = %i\n",
+ _frameCount, frame.x, frame.y, frame.w, frame.h);
+ }
+
+ frame.frame = new MSprite(spriteDataStream, Common::Point(frame.x, frame.y),
+ frame.w, frame.h, false);
+ _frames.push_back(frame);
+ }
+
+ delete spriteStream;
+ delete spriteDataStream;
+ file.close();
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/assets.h b/engines/mads/assets.h
new file mode 100644
index 0000000000..c64209eab2
--- /dev/null
+++ b/engines/mads/assets.h
@@ -0,0 +1,69 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_ASSETS_H
+#define MADS_ASSETS_H
+
+#include "common/scummsys.h"
+#include "common/array.h"
+#include "mads/msprite.h"
+#include "mads/palette.h"
+#include "mads/msprite.h"
+
+namespace MADS {
+
+struct SpriteAssetFrame {
+ uint32 stream;
+ int x, y, w, h;
+ uint32 comp;
+ MSprite *frame;
+};
+
+class SpriteAsset {
+private:
+ MADSEngine *_vm;
+ byte _palette[PALETTE_SIZE];
+ int _colorCount;
+ uint32 _srcSize;
+ int _frameRate, _pixelSpeed;
+ int _maxWidth, _maxHeight;
+ int _frameCount;
+ Common::Array<uint32> _frameOffsets;
+ Common::Array<SpriteAssetFrame> _frames;
+ uint32 _frameStartOffset;
+public:
+ SpriteAsset(MADSEngine *vm, const Common::String &resourceName, int flags);
+ int getCount() { return _frameCount; }
+ int getFrameRate() const { return _frameRate; }
+ int getPixelSpeed() const { return _pixelSpeed; }
+ int getFrameWidth(int index);
+ int getFrameHeight(int index);
+ int getMaxFrameWidth() const { return _maxWidth; }
+ int getMaxFrameHeight() const { return _maxHeight; }
+ MSprite *getFrame(int frameIndex);
+ byte *getPalette() { return _palette; }
+ int getColorCount() { return _colorCount; }
+};
+
+} // End of namespace MADS
+
+#endif /* MADS_ASSETS_H */
diff --git a/engines/mads/compression.cpp b/engines/mads/compression.cpp
new file mode 100644
index 0000000000..2190fc4ac0
--- /dev/null
+++ b/engines/mads/compression.cpp
@@ -0,0 +1,184 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mads/compression.h"
+
+namespace MADS {
+
+const char *const madsPackString = "MADSPACK";
+const char *const FabInputExceededError = "FabDecompressor - Passed end of input buffer during decompression";
+const char *const FabOutputExceededError = "FabDecompressor - Decompressed data exceeded specified size";
+
+bool MadsPack::isCompressed(Common::SeekableReadStream *stream) {
+ // Check whether the passed stream is packed
+
+ char tempBuffer[8];
+ stream->seek(0);
+ if (stream->read(tempBuffer, 8) == 8) {
+ if (!strncmp(tempBuffer, madsPackString, 8))
+ return true;
+ }
+
+ return false;
+}
+
+MadsPack::MadsPack(Common::SeekableReadStream *stream) {
+ initialise(stream);
+}
+
+MadsPack::MadsPack(const Common::String &resourceName, MADSEngine *vm) {
+ File file(resourceName);
+ initialise(&file);
+ file.close();
+}
+
+void MadsPack::initialise(Common::SeekableReadStream *stream) {
+ if (!MadsPack::isCompressed(stream))
+ error("Attempted to decompress a resource that was not MadsPacked");
+
+ stream->seek(14);
+ _count = stream->readUint16LE();
+ _items = new MadsPackEntry[_count];
+
+ byte *headerData = new byte[0xA0];
+ byte *header = headerData;
+ stream->read(headerData, 0xA0);
+
+ for (int i = 0; i < _count; ++i, header += 10) {
+ // Get header data
+ _items[i].hash = READ_LE_UINT16(header);
+ _items[i].size = READ_LE_UINT32(header + 2);
+ _items[i].compressedSize = READ_LE_UINT32(header + 6);
+
+ _items[i].data = new byte[_items[i].size];
+ if (_items[i].size == _items[i].compressedSize) {
+ // Entry isn't compressed
+ stream->read(_items[i].data, _items[i].size);
+ } else {
+ // Decompress the entry
+ byte *compressedData = new byte[_items[i].compressedSize];
+ stream->read(compressedData, _items[i].compressedSize);
+
+ FabDecompressor fab;
+ fab.decompress(compressedData, _items[i].compressedSize, _items[i].data, _items[i].size);
+ delete[] compressedData;
+ }
+ }
+
+ delete[] headerData;
+ _dataOffset = stream->pos();
+}
+
+MadsPack::~MadsPack() {
+ for (int i = 0; i < _count; ++i)
+ delete[] _items[i].data;
+ delete[] _items;
+}
+
+//--------------------------------------------------------------------------
+
+void FabDecompressor::decompress(const byte *srcData, int srcSize, byte *destData, int destSize) {
+ byte copyLen, copyOfsShift, copyOfsMask, copyLenMask;
+ unsigned long copyOfs;
+ byte *destP;
+
+ // Validate that the data starts with the FAB header
+ if (strncmp((const char *)srcData, "FAB", 3) != 0)
+ error("FabDecompressor - Invalid compressed data");
+
+ int shiftVal = srcData[3];
+ if ((shiftVal < 10) || (shiftVal > 13))
+ error("FabDecompressor - Invalid shift start");
+
+ copyOfsShift = 16 - shiftVal;
+ copyOfsMask = 0xFF << (shiftVal - 8);
+ copyLenMask = (1 << copyOfsShift) - 1;
+ copyOfs = 0xFFFF0000;
+ destP = destData;
+
+ // Initialise data fields
+ _srcData = srcData;
+ _srcP = _srcData + 6;
+ _srcSize = srcSize;
+ _bitsLeft = 16;
+ _bitBuffer = READ_LE_UINT16(srcData + 4);
+
+ for (;;) {
+ if (getBit() == 0) {
+ if (getBit() == 0) {
+ copyLen = ((getBit() << 1) | getBit()) + 2;
+ copyOfs = *_srcP++ | 0xFFFFFF00;
+ } else {
+ copyOfs = (((_srcP[1] >> copyOfsShift) | copyOfsMask) << 8) | _srcP[0];
+ copyLen = _srcP[1] & copyLenMask;
+ _srcP += 2;
+ if (copyLen == 0) {
+ copyLen = *_srcP++;
+ if (copyLen == 0)
+ break;
+ else if (copyLen == 1)
+ continue;
+ else
+ copyLen++;
+ } else {
+ copyLen += 2;
+ }
+ copyOfs |= 0xFFFF0000;
+ }
+ while (copyLen-- > 0) {
+ if (destP - destData == destSize)
+ error(FabOutputExceededError);
+
+ *destP = destP[(signed int)copyOfs];
+ destP++;
+ }
+ } else {
+ if (_srcP - srcData == srcSize)
+ error(FabInputExceededError);
+ if (destP - destData == destSize)
+ error(FabOutputExceededError);
+
+ *destP++ = *_srcP++;
+ }
+ }
+
+ if (destP - destData != destSize)
+ error("FabDecompressor - Decompressed data does not match header decompressed size");
+}
+
+int FabDecompressor::getBit() {
+ _bitsLeft--;
+ if (_bitsLeft == 0) {
+ if (_srcP - _srcData == _srcSize)
+ error(FabInputExceededError);
+
+ _bitBuffer = (READ_LE_UINT16(_srcP) << 1) | (_bitBuffer & 1);
+ _srcP += 2;
+ _bitsLeft = 16;
+ }
+
+ int bit = _bitBuffer & 1;
+ _bitBuffer >>= 1;
+ return bit;
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/compression.h b/engines/mads/compression.h
new file mode 100644
index 0000000000..bf690dcc46
--- /dev/null
+++ b/engines/mads/compression.h
@@ -0,0 +1,80 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_COMPRESSION_H
+#define MADS_COMPRESSION_H
+
+#include "common/scummsys.h"
+#include "common/endian.h"
+#include "common/memstream.h"
+#include "common/stream.h"
+
+#include "mads/mads.h"
+
+namespace MADS {
+
+struct MadsPackEntry {
+public:
+ uint16 hash;
+ uint32 size;
+ uint32 compressedSize;
+ byte *data;
+};
+
+class MadsPack {
+private:
+ MadsPackEntry *_items;
+ int _count;
+ int _dataOffset;
+
+ void initialise(Common::SeekableReadStream *stream);
+public:
+ static bool isCompressed(Common::SeekableReadStream *stream);
+ MadsPack(Common::SeekableReadStream *stream);
+ MadsPack(const Common::String &resourceName, MADSEngine *_vm);
+ ~MadsPack();
+
+ int getCount() const { return _count; }
+ MadsPackEntry &getItem(int index) const { return _items[index]; }
+ MadsPackEntry &operator[](int index) const { return _items[index]; }
+ Common::MemoryReadStream *getItemStream(int index) {
+ return new Common::MemoryReadStream(_items[index].data, _items[index].size,
+ DisposeAfterUse::NO);
+ }
+ int getDataOffset() const { return _dataOffset; }
+};
+
+class FabDecompressor {
+private:
+ int _bitsLeft;
+ uint32 _bitBuffer;
+ const byte *_srcData, *_srcP;
+ int _srcSize;
+
+ int getBit();
+public:
+ void decompress(const byte *srcData, int srcSize, byte *destData, int destSize);
+};
+
+} // End of namespace MADS
+
+#endif
diff --git a/engines/mads/configure.engine b/engines/mads/configure.engine
new file mode 100644
index 0000000000..60d833e9e8
--- /dev/null
+++ b/engines/mads/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 mads "Rex Nebular and the Cosmic Gender Bender" no
diff --git a/engines/mads/debugger.cpp b/engines/mads/debugger.cpp
new file mode 100644
index 0000000000..ceaeeaa5dd
--- /dev/null
+++ b/engines/mads/debugger.cpp
@@ -0,0 +1,49 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mads/mads.h"
+#include "mads/debugger.h"
+
+namespace MADS {
+
+Debugger::Debugger(MADSEngine *vm) : GUI::Debugger(), _vm(vm) {
+ DCmd_Register("continue", WRAP_METHOD(Debugger, Cmd_Exit));
+}
+/*
+static int strToInt(const char *s) {
+ if (!*s)
+ // No string at all
+ return 0;
+ else if (toupper(s[strlen(s) - 1]) != 'H')
+ // Standard decimal string
+ return atoi(s);
+
+ // Hexadecimal string
+ uint tmp = 0;
+ int read = sscanf(s, "%xh", &tmp);
+ if (read < 1)
+ error("strToInt failed on string \"%s\"", s);
+ return (int)tmp;
+}
+*/
+
+} // End of namespace MADS
diff --git a/engines/mads/debugger.h b/engines/mads/debugger.h
new file mode 100644
index 0000000000..044151c0bb
--- /dev/null
+++ b/engines/mads/debugger.h
@@ -0,0 +1,45 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_DEBUGGER_H
+#define MADS_DEBUGGER_H
+
+#include "common/scummsys.h"
+#include "gui/debugger.h"
+
+namespace MADS {
+
+class MADSEngine;
+
+class Debugger : public GUI::Debugger {
+private:
+ MADSEngine *_vm;
+public:
+ Debugger(MADSEngine *vm);
+ virtual ~Debugger() {}
+
+protected:
+};
+
+} // End of namespace MADS
+
+#endif /* MADS_DEBUGGER_H */
diff --git a/engines/mads/detection.cpp b/engines/mads/detection.cpp
new file mode 100644
index 0000000000..015859f827
--- /dev/null
+++ b/engines/mads/detection.cpp
@@ -0,0 +1,178 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+
+#include "mads/mads.h"
+
+#include "base/plugins.h"
+#include "common/savefile.h"
+#include "common/str-array.h"
+#include "common/memstream.h"
+#include "engines/advancedDetector.h"
+#include "common/system.h"
+#include "graphics/colormasks.h"
+#include "graphics/surface.h"
+
+#define MAX_SAVES 99
+
+namespace MADS {
+
+struct MADSGameDescription {
+ ADGameDescription desc;
+
+ int gameID;
+ uint32 features;
+};
+
+uint32 MADSEngine::getGameID() const {
+ return _gameDescription->gameID;
+}
+
+uint32 MADSEngine::getGameFeatures() const {
+ return _gameDescription->features;
+}
+
+uint32 MADSEngine::getFeatures() const {
+ return _gameDescription->desc.flags;
+}
+
+Common::Language MADSEngine::getLanguage() const {
+ return _gameDescription->desc.language;
+}
+
+Common::Platform MADSEngine::getPlatform() const {
+ return _gameDescription->desc.platform;
+}
+
+} // End of namespace MADS
+
+static const PlainGameDescriptor MADSGames[] = {
+ {"MADS", "MADS"},
+ {"nebular", "Rex Nebular and the Cosmic Gender Bender"},
+ {0, 0}
+};
+
+#include "mads/detection_tables.h"
+
+class MADSMetaEngine : public AdvancedMetaEngine {
+public:
+ MADSMetaEngine() : AdvancedMetaEngine(MADS::gameDescriptions, sizeof(MADS::MADSGameDescription), MADSGames) {
+ _maxScanDepth = 3;
+ }
+
+ virtual const char *getName() const {
+ return "MADS Engine";
+ }
+
+ virtual const char *getOriginalCopyright() const {
+ return "MADS (c)";
+ }
+
+ virtual bool hasFeature(MetaEngineFeature f) const;
+ virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
+ virtual SaveStateList listSaves(const char *target) const;
+ virtual int getMaximumSaveSlot() const;
+ virtual void removeSaveState(const char *target, int slot) const;
+ SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
+};
+
+bool MADSMetaEngine::hasFeature(MetaEngineFeature f) const {
+ return
+ (f == kSupportsListSaves) ||
+ (f == kSupportsLoadingDuringStartup) ||
+ (f == kSupportsDeleteSave) ||
+ (f == kSavesSupportMetaInfo) ||
+ (f == kSavesSupportThumbnail);
+}
+
+bool MADS::MADSEngine::hasFeature(EngineFeature f) const {
+ return
+ (f == kSupportsRTL) ||
+ (f == kSupportsLoadingDuringRuntime) ||
+ (f == kSupportsSavingDuringRuntime);
+}
+
+bool MADSMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ const MADS::MADSGameDescription *gd = (const MADS::MADSGameDescription *)desc;
+ if (gd) {
+ *engine = new MADS::MADSEngine(syst, gd);
+ }
+ return gd != 0;
+}
+
+SaveStateList MADSMetaEngine::listSaves(const char *target) const {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Common::StringArray filenames;
+ Common::String saveDesc;
+ Common::String pattern = Common::String::format("%s.0??", target);
+
+ filenames = saveFileMan->listSavefiles(pattern);
+ sort(filenames.begin(), filenames.end()); // Sort to get the files in numerical order
+
+ SaveStateList saveList;
+ for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
+ const char *ext = strrchr(file->c_str(), '.');
+ int slot = ext ? atoi(ext + 1) : -1;
+
+ if (slot >= 0 && slot < MAX_SAVES) {
+ Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(*file);
+
+ if (in) {
+ delete in;
+ }
+ }
+ }
+
+ return saveList;
+}
+
+int MADSMetaEngine::getMaximumSaveSlot() const {
+ return MAX_SAVES;
+}
+
+void MADSMetaEngine::removeSaveState(const char *target, int slot) const {
+ Common::String filename = Common::String::format("%s.%03d", target, slot);
+ g_system->getSavefileManager()->removeSavefile(filename);
+}
+
+SaveStateDescriptor MADSMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+ Common::String filename = Common::String::format("%s.%03d", target, slot);
+ Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(filename);
+
+ if (f) {
+ delete f;
+
+ // Create the return descriptor
+ SaveStateDescriptor desc(slot, "");
+
+ return desc;
+ }
+
+ return SaveStateDescriptor();
+}
+
+
+#if PLUGIN_ENABLED_DYNAMIC(MADS)
+ REGISTER_PLUGIN_DYNAMIC(MADS, PLUGIN_TYPE_ENGINE, MADSMetaEngine);
+#else
+ REGISTER_PLUGIN_STATIC(MADS, PLUGIN_TYPE_ENGINE, MADSMetaEngine);
+#endif
diff --git a/engines/mads/detection_tables.h b/engines/mads/detection_tables.h
new file mode 100644
index 0000000000..ac5316d4b4
--- /dev/null
+++ b/engines/mads/detection_tables.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.
+ *
+ */
+
+namespace MADS {
+
+static const MADSGameDescription gameDescriptions[] = {
+ {
+ // Rex Nebular and the Cosmic Gender Bender DOS English
+ {
+ "nebular",
+ 0,
+ {
+ {"mpslabs.001", 0, "4df5c557b52abb5b661cf4befe5ae301", 1315354},
+ AD_LISTEND
+ },
+ Common::EN_ANY,
+ Common::kPlatformDOS,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ GType_RexNebular,
+ 0
+ },
+
+ { AD_TABLE_END_MARKER }
+};
+
+} // End of namespace MADS
diff --git a/engines/mads/dialogs.cpp b/engines/mads/dialogs.cpp
new file mode 100644
index 0000000000..e4c7682a38
--- /dev/null
+++ b/engines/mads/dialogs.cpp
@@ -0,0 +1,344 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "common/config-manager.h"
+#include "mads/mads.h"
+#include "mads/graphics.h"
+#include "mads/msurface.h"
+#include "mads/nebular/dialogs_nebular.h"
+
+namespace MADS {
+
+Dialog::Dialog(MADSEngine *vm): _vm(vm), _savedSurface(nullptr),
+ _position(Common::Point(-1, -1)), _width(0), _height(0) {
+}
+
+Dialog::~Dialog() {
+ restore(_vm->_screen);
+}
+
+
+void Dialog::save(MSurface *s) {
+ _savedSurface = new MSurface(_width, _height);
+ s->copyTo(_savedSurface,
+ Common::Rect(_position.x, _position.y, _position.x + _width, _position.y + _height),
+ Common::Point());
+}
+
+void Dialog::restore(MSurface *s) {
+ if (_savedSurface) {
+ _savedSurface->copyTo(s, _position);
+ delete _savedSurface;
+ _savedSurface = nullptr;
+ }
+}
+
+void Dialog::draw() {
+ // Save the screen portion the dialog will overlap
+ save(_vm->_screen);
+
+ // Draw the dialog
+ // Fill entire content of dialog
+ _vm->_screen->fillRect(Common::Rect(_position.x, _position.y,
+ _position.x + _width, _position.y + _height), TEXTDIALOG_BACKGROUND);
+
+ // Draw the outer edge lines
+ _vm->_screen->hLine(_position.x + 1, _position.y + _height - 2,
+ _position.x + _width - 2, TEXTDIALOG_EDGE);
+ _vm->_screen->hLine(_position.x, _position.y + _height - 1,
+ _position.x + _width - 1, TEXTDIALOG_EDGE);
+ _vm->_screen->vLine(_position.x + _width - 2, _position.y + 2,
+ _position.y + _height - 2, TEXTDIALOG_EDGE);
+ _vm->_screen->vLine(_position.x + _width - 1, _position.y + 1,
+ _position.y + _height - 1, TEXTDIALOG_EDGE);
+
+ // Draw the gravelly dialog content
+ drawContent(Common::Rect(_position.x + 2, _position.y + 2,
+ _position.x + _width - 2, _position.y + _height - 2), 0,
+ TEXTDIALOG_CONTENT1, TEXTDIALOG_CONTENT2);
+}
+
+void Dialog::drawContent(const Common::Rect &r, int seed, byte color1, byte color2) {
+ uint16 currSeed = seed ? seed : 0xB78E;
+
+ for (int yp = 0; yp < r.height(); ++yp) {
+ byte *destP = _vm->_screen->getBasePtr(r.left, r.top + yp);
+
+ for (int xp = 0; xp < r.width(); ++xp) {
+ uint16 seedAdjust = currSeed;
+ currSeed += 0x181D;
+ seedAdjust = (seedAdjust >> 9) | ((seedAdjust & 0x1ff) << 7);
+ currSeed ^= seedAdjust;
+ seedAdjust = (seedAdjust >> 3) | ((seedAdjust & 7) << 13);
+ currSeed += seedAdjust;
+
+ *destP++ = (currSeed & 0x10) ? color2 : color1;
+ }
+ }
+}
+
+/*------------------------------------------------------------------------*/
+
+TextDialog::TextDialog(MADSEngine *vm, const Common::String &fontName,
+ const Common::Point &pos, int maxChars):
+ Dialog(vm) {
+ _vm = vm;
+ _fontName = fontName;
+ _position = pos;
+
+ _vm->_font->setFont(FONT_INTERFACE);
+ _vm->_font->setColors(TEXTDIALOG_BLACK, TEXTDIALOG_BLACK, TEXTDIALOG_BLACK, TEXTDIALOG_BLACK);
+
+ _innerWidth = (_vm->_font->maxWidth() + 1) * maxChars;
+ _width = _innerWidth + 10;
+ _lineSize = maxChars * 2;
+ _lineWidth = 0;
+ _currentX = 0;
+ _numLines = 0;
+ Common::fill(&_lineXp[0], &_lineXp[TEXT_DIALOG_MAX_LINES], 0);
+ _askLineNum = -1;
+ _askXp = 0;
+
+ // Save the high end of the palette, and set up the entries for dialog display
+ Common::copy(&_vm->_palette->_mainPalette[TEXTDIALOG_CONTENT1 * 3],
+ &_vm->_palette->_mainPalette[TEXTDIALOG_CONTENT1 * 3 + 8 * 3],
+ &_savedPalette[0]);
+ Palette::setGradient(_vm->_palette->_mainPalette, TEXTDIALOG_CONTENT1, 2, 0x90, 0x80);
+ Palette::setGradient(_vm->_palette->_mainPalette, TEXTDIALOG_EDGE, 2, 0x9C, 0x70);
+ Palette::setGradient(_vm->_palette->_mainPalette, TEXTDIALOG_FC, 2, 0x90, 0x80);
+ Palette::setGradient(_vm->_palette->_mainPalette, TEXTDIALOG_FE, 1, 0xDC, 0xDC);
+
+ _vm->_palette->setPalette(_vm->_palette->_mainPalette + (TEXTDIALOG_CONTENT1 * 3),
+ TEXTDIALOG_CONTENT1, 8);
+}
+
+TextDialog::~TextDialog() {
+ restorePalette();
+}
+
+void TextDialog::addLine(const Common::String &line, bool underline) {
+ if (_lineWidth > 0 || _currentX > 0)
+ incNumLines();
+
+ int stringWidth = _vm->_font->getWidth(line, 1);
+ if (stringWidth >= _innerWidth || (int)line.size() >= _lineSize) {
+ wordWrap(line);
+ } else {
+ _lineXp[_numLines] = (_innerWidth / 2) - (stringWidth / 2);
+ _lines[_numLines] = line;
+
+ if (underline)
+ underlineLine();
+ }
+
+ incNumLines();
+}
+
+void TextDialog::underlineLine() {
+ _lineXp[_numLines] |= 0x80;
+}
+
+void TextDialog::incNumLines() {
+ _lineWidth = 0;
+ _currentX = 0;
+ if (++_numLines == TEXT_DIALOG_MAX_LINES)
+ error("Exceeded text dialog line max");
+}
+
+void TextDialog::wordWrap(const Common::String &line) {
+ Common::String tempLine;
+
+ if (!line.empty()) {
+ const char *srcP = line.c_str();
+
+ do {
+ tempLine = "";
+ bool endWord = false;
+ bool newLine = false;
+ bool continueFlag = true;
+
+ do {
+ if (!*srcP) {
+ continueFlag = false;
+ } else {
+ tempLine += *srcP;
+
+ if (*srcP == 10) {
+ continueFlag = false;
+ newLine = true;
+ ++srcP;
+ tempLine.deleteLastChar();
+ } else if (*srcP == ' ') {
+ ++srcP;
+ endWord = true;
+ } else if (!endWord) {
+ ++srcP;
+ } else {
+ tempLine.deleteLastChar();
+ continueFlag = false;
+ }
+ }
+ } while (continueFlag);
+
+ if (tempLine.hasSuffix(" "))
+ tempLine.deleteLastChar();
+
+ Common::String tempLine2;
+ if (_currentX > 0)
+ tempLine2 += ' ';
+ tempLine2 += tempLine;
+
+ int lineWidth = _vm->_font->getWidth(tempLine2, 1);
+ if (((_currentX + (int)tempLine2.size()) > _lineSize) ||
+ ((_lineWidth + lineWidth) > _innerWidth)) {
+ incNumLines();
+ appendLine(tempLine);
+ } else {
+ appendLine(tempLine2);
+ }
+
+ if (newLine)
+ incNumLines();
+ } while (*srcP);
+ }
+}
+
+void TextDialog::appendLine(const Common::String &line) {
+ _currentX += line.size();
+ _lineWidth += _vm->_font->getWidth(line, 1);
+ _lines[_numLines] += line;
+}
+
+void TextDialog::addInput() {
+ _askXp = _currentX + 1;
+ _askLineNum = _numLines;
+ incNumLines();
+}
+
+void TextDialog::draw() {
+ if (!_lineWidth)
+ --_numLines;
+
+ // Figure out the size and position for the dialog
+ _height = (_vm->_font->getHeight() + 1) * (_numLines + 1) + 10;
+ if (_position.x == -1)
+ _position.x = 160 - (_width / 2);
+ if (_position.y == -1)
+ _position.y = 100 - (_height / 2);
+
+ if ((_position.x + _width) > _vm->_screen->getWidth())
+ _position.x = _vm->_screen->getWidth() - (_position.x + _width);
+ if ((_position.y + _height) > _vm->_screen->getHeight())
+ _position.y = _vm->_screen->getHeight() - (_position.y + _height);
+
+ // Draw the underlying dialog
+ Dialog::draw();
+
+ // Draw the text lines
+ int lineYp = _position.y + 5;
+ for (int lineNum = 0; lineNum < _numLines; ++lineNum) {
+ if (_lineXp[lineNum] == -1) {
+ // Draw a line across the entire dialog
+ _vm->_screen->hLine(_position.x + 2,
+ lineYp + (_vm->_font->getHeight() + 1) / 2,
+ _position.x + _width - 4, TEXTDIALOG_BLACK);
+ } else {
+ // Draw a text line
+ int xp = (_lineXp[lineNum] & 0x7F) + _position.x + 5;
+ int yp = lineYp;
+ if (_lineXp[lineNum] & 0x40)
+ ++yp;
+
+ _vm->_font->writeString(_vm->_screen, _lines[lineNum],
+ Common::Point(xp, yp), 0, 1);
+
+ if (_lineXp[lineNum] & 0x80) {
+ // Draw an underline under the text
+ int lineWidth = _vm->_font->getWidth(_lines[lineNum], 1);
+ _vm->_screen->hLine(xp, yp + _vm->_font->getHeight(), xp + lineWidth,
+ TEXTDIALOG_BLACK);
+ }
+ }
+
+ lineYp += _vm->_font->getHeight() + 1;
+ }
+}
+
+void TextDialog::drawWithInput() {
+ //int innerWidth = _innerWidth;
+ //int lineHeight = _vm->_font->getHeight() + 1;
+ //int xp = _position.x + 5;
+
+ // Draw the content of the dialog
+ drawContent(Common::Rect(_position.x + 2, _position.y + 2,
+ _position.x + _width - 2, _position.y + _height - 2), 0,
+ TEXTDIALOG_CONTENT1, TEXTDIALOG_CONTENT2);
+
+ error("TODO: drawWithInput");
+}
+
+void TextDialog::restorePalette() {
+ Common::copy(&_savedPalette[0], &_savedPalette[8 * 3],
+ &_vm->_palette->_mainPalette[248 * 3]);
+ _vm->_palette->setPalette(_vm->_palette->_mainPalette, 248, 8);
+}
+
+/*------------------------------------------------------------------------*/
+
+MessageDialog::MessageDialog(MADSEngine *vm, int maxChars, ...):
+ TextDialog(vm, FONT_INTERFACE, Common::Point(-1, -1), maxChars) {
+ // Add in passed line list
+ va_list va;
+ va_start(va, maxChars);
+
+ const char *line = va_arg(va, const char *);
+ while (line) {
+ addLine(line);
+ line = va_arg(va, const char *);
+ }
+ va_end(va);
+}
+
+void MessageDialog::show() {
+ draw();
+ _vm->_events->showCursor();
+
+ while (!_vm->shouldQuit() && !_vm->_events->_keyPressed &&
+ !_vm->_events->_mouseClicked) {
+ _vm->_events->delay(1);
+ }
+}
+
+/*------------------------------------------------------------------------*/
+
+Dialogs *Dialogs::init(MADSEngine *vm) {
+ if (vm->getGameID() == GType_RexNebular)
+ return new Nebular::DialogsNebular(vm);
+
+ error("Unknown game");
+}
+
+Dialogs::Dialogs(MADSEngine *vm): _vm(vm) {
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/dialogs.h b/engines/mads/dialogs.h
new file mode 100644
index 0000000000..0f9a098ee1
--- /dev/null
+++ b/engines/mads/dialogs.h
@@ -0,0 +1,196 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_DIALOGS_H
+#define MADS_DIALOGS_H
+
+#include "common/scummsys.h"
+#include "mads/game.h"
+#include "mads/msurface.h"
+
+namespace MADS {
+
+class Dialog {
+protected:
+ MADSEngine *_vm;
+ MSurface *_savedSurface;
+ Common::Point _position;
+ int _width;
+ int _height;
+
+ /**
+ * Save the section of the passed surface the dialog will cover.
+ * @param s Screen surface to save
+ */
+ void save(MSurface *s);
+
+ /**
+ * Restore saved dialog surface
+ * @param s Screen surface to restore to.
+ */
+ void restore(MSurface *s);
+
+ /**
+ * Draws the content of a dialog with a gravelly alternating color.
+ */
+ void drawContent(const Common::Rect &r, int seed, byte color1, byte color2);
+protected:
+ /**
+ * Draw the dialog
+ */
+ virtual void draw();
+public:
+ /**
+ * Constructor
+ */
+ Dialog(MADSEngine *vm);
+
+ /**
+ * Destructor
+ */
+ virtual ~Dialog();
+};
+
+enum {
+ TEXTDIALOG_CONTENT1 = 0XF8,
+ TEXTDIALOG_CONTENT2 = 0XF9,
+ TEXTDIALOG_EDGE = 0XFA,
+ TEXTDIALOG_BACKGROUND = 0XFB,
+ TEXTDIALOG_FC = 0XFC,
+ TEXTDIALOG_FD = 0XFD,
+ TEXTDIALOG_FE = 0XFE,
+ TEXTDIALOG_BLACK = 0
+};
+
+#define TEXT_DIALOG_MAX_LINES 20
+
+class TextDialog: protected Dialog {
+private:
+ /**
+ * Increments the number of text lines the text dialog uses
+ */
+ void incNumLines();
+
+ /**
+ * Flags the previously added line to be underlined
+ */
+ void underlineLine();
+
+ /**
+ * Append text to the currently end line.
+ */
+ void appendLine(const Common::String &line);
+
+ /**
+ * Clean up after finishing displaying the dialog
+ */
+ void restorePalette();
+protected:
+ Common::String _fontName;
+ int _innerWidth;
+ int _lineWidth;
+ int _currentX;
+ int _numLines;
+ int _lineSize;
+ int _askXp;
+ int _askLineNum;
+ Common::String _lines[TEXT_DIALOG_MAX_LINES];
+ int _lineXp[TEXT_DIALOG_MAX_LINES];
+ byte _savedPalette[8 * 3];
+
+ /**
+ * Add a new line to the dialog
+ */
+ void addLine(const Common::String &line, bool underline = false);
+
+ /**
+ * Adds one or more lines, word wrapping the passed text
+ */
+ void wordWrap(const Common::String &line);
+
+ /**
+ * Adds an input area following previously added text
+ */
+ void addInput();
+public:
+ /**
+ * Constructor
+ * @param vm Engine reference
+ * @param fontName Font to use for display
+ * @param pos Position for window top-left
+ * @param maxChars Horizontal width of window in characters
+ */
+ TextDialog(MADSEngine *vm, const Common::String &fontName, const Common::Point &pos,
+ int maxChars);
+
+ /**
+ * Destructor
+ */
+ virtual ~TextDialog();
+
+ /**
+ * Draw the dialog
+ */
+ virtual void draw();
+
+ /**
+ * Draw the dialog along with any input box
+ */
+ void drawWithInput();
+};
+
+class MessageDialog: protected TextDialog {
+public:
+ MessageDialog(MADSEngine *vm, int lines, ...);
+
+ virtual ~MessageDialog() {}
+
+ /**
+ * Show the dialog, and wait until a key or mouse press.
+ */
+ void show();
+};
+
+enum DialogId {
+ DIALOG_NONE = 0, DIALOG_GAME_MENU = 1, DIALOG_SAVE = 2, DIALOG_RESTORE = 3,
+ DIALOG_OPTIONS = 4, DIALOG_DIFFICULTY = 5, DIALOG_ERROR = 6
+};
+
+class Dialogs {
+protected:
+ MADSEngine *_vm;
+
+ Dialogs(MADSEngine *vm);
+public:
+ static Dialogs *init(MADSEngine *vm);
+public:
+ Common::Point _defaultPosition;
+ DialogId _pendingDialog;
+
+ virtual ~Dialogs() {}
+
+ virtual void showDialog() = 0;
+};
+
+} // End of namespace MADS
+
+#endif /* MADS_DIALOGS_H */
diff --git a/engines/mads/events.cpp b/engines/mads/events.cpp
new file mode 100644
index 0000000000..8f177f2c20
--- /dev/null
+++ b/engines/mads/events.cpp
@@ -0,0 +1,160 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "graphics/cursorman.h"
+#include "common/events.h"
+#include "engines/util.h"
+#include "mads/mads.h"
+#include "mads/events.h"
+
+#define GAME_FRAME_RATE 50
+#define GAME_FRAME_TIME (1000 / GAME_FRAME_RATE)
+
+namespace MADS {
+
+EventsManager::EventsManager(MADSEngine *vm) {
+ _vm = vm;
+ _cursorSprites = nullptr;
+ _gameCounter = 0;
+ _priorFrameTime = 0;
+ _keyPressed = false;
+ _mouseClicked = false;
+}
+
+EventsManager::~EventsManager() {
+ freeCursors();
+}
+
+void EventsManager::loadCursors(const Common::String &spritesName) {
+ delete _cursorSprites;
+ _cursorSprites = new SpriteAsset(_vm, spritesName, 0x4000);
+}
+
+void EventsManager::setCursor(CursorType cursorId) {
+ _cursorId = cursorId;
+ changeCursor();
+}
+
+void EventsManager::setCursor2(CursorType cursorId) {
+ _cursorId = cursorId;
+ _newCursorId = cursorId;
+ changeCursor();
+}
+
+void EventsManager::showCursor() {
+ CursorMan.showMouse(true);
+}
+
+void EventsManager::hideCursor() {
+ CursorMan.showMouse(false);
+}
+
+void EventsManager::resetCursor() {
+ CursorType cursorId = (CursorType)MIN(_cursorSprites->getCount(), (int)CURSOR_WAIT);
+ _newCursorId = cursorId;
+ if (_cursorId != _newCursorId) {
+ changeCursor();
+ _cursorId = _newCursorId;
+ }
+}
+
+void EventsManager::changeCursor() {
+ warning("TODO: changeCursor");
+}
+
+void EventsManager::freeCursors() {
+ delete _cursorSprites;
+ _cursorSprites = nullptr;
+}
+
+void EventsManager::pollEvents() {
+ checkForNextFrameCounter();
+
+ Common::Event event;
+ while (g_system->getEventManager()->pollEvent(event)) {
+ // Handle keypress
+ switch (event.type) {
+ case Common::EVENT_QUIT:
+ case Common::EVENT_RTL:
+ return;
+
+ case Common::EVENT_KEYDOWN:
+ // Check for debugger
+ if (event.kbd.keycode == Common::KEYCODE_d && (event.kbd.flags & Common::KBD_CTRL)) {
+ // Attach to the debugger
+ _vm->_debugger->attach();
+ _vm->_debugger->onFrame();
+ } else {
+ _keyPressed = true;
+ }
+ return;
+ case Common::EVENT_KEYUP:
+ _keyPressed = false;
+ return;
+ case Common::EVENT_LBUTTONDOWN:
+ case Common::EVENT_RBUTTONDOWN:
+ _mouseClicked = true;
+ return;
+ case Common::EVENT_LBUTTONUP:
+ case Common::EVENT_RBUTTONUP:
+ _mouseClicked = false;
+ return;
+ case Common::EVENT_MOUSEMOVE:
+ _mousePos = event.mouse;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void EventsManager::checkForNextFrameCounter() {
+ // Check for next game frame
+ uint32 milli = g_system->getMillis();
+ if ((milli - _priorFrameTime) >= GAME_FRAME_TIME) {
+ ++_gameCounter;
+ _priorFrameTime = milli;
+
+ // Give time to the debugger
+ _vm->_debugger->onFrame();
+
+ // Display the frame
+ _vm->_screen->updateScreen();
+
+ // Signal the ScummVM debugger
+ _vm->_debugger->onFrame();
+ }
+}
+
+void EventsManager::delay(int cycles) {
+ uint32 totalMilli = cycles * 1000 / GAME_FRAME_RATE;
+ uint32 delayEnd = g_system->getMillis() + totalMilli;
+
+ while (!_vm->shouldQuit() && g_system->getMillis() < delayEnd) {
+ g_system->delayMillis(10);
+
+ pollEvents();
+ }
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/events.h b/engines/mads/events.h
new file mode 100644
index 0000000000..01f48170d4
--- /dev/null
+++ b/engines/mads/events.h
@@ -0,0 +1,123 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_EVENTS_H
+#define MADS_EVENTS_H
+
+#include "common/scummsys.h"
+#include "mads/msprite.h"
+#include "mads/assets.h"
+
+namespace MADS {
+
+enum CursorType { CURSOR_NONE = 0, CURSOR_ARROW = 1, CURSOR_WAIT = 2, CURSOR_GO_DOWN = 3,
+ CURSOR_GO_UP = 4, CURSOR_GO_LEFT = 5, CURSOR_GO_RIGHT = 6 };
+
+class MADSEngine;
+
+class EventsManager {
+private:
+ MADSEngine *_vm;
+ CursorType _cursorId;
+ CursorType _newCursorId;
+ uint32 _gameCounter;
+ uint32 _priorFrameTime;
+ Common::Point _mousePos;
+
+ /**
+ * Updates the cursor image when the current cursor changes
+ */
+ void changeCursor();
+
+ /**
+ * Checks for whether the next game frame number has been reached.
+ */
+ void checkForNextFrameCounter();
+public:
+ SpriteAsset *_cursorSprites;
+ bool _mouseClicked;
+ bool _keyPressed;
+public:
+ /**
+ * Constructor
+ */
+ EventsManager(MADSEngine *vm);
+
+ /**
+ * Destructor
+ */
+ ~EventsManager();
+
+ /**
+ * Loads the sprite set containing the cursors
+ */
+ void loadCursors(const Common::String &spritesName);
+
+ /**
+ * Sets the cursor
+ */
+ void setCursor(CursorType cursorId);
+
+ /**
+ * Sets the cursor
+ */
+ void setCursor2(CursorType cursorId);
+
+ /**
+ * Show the mouse cursor
+ */
+ void showCursor();
+
+ /**
+ * Hide the mouse cursor
+ */
+ void hideCursor();
+
+ /**
+ * Resets the cursor, if necessary
+ */
+ void resetCursor();
+
+ /**
+ * Free currently loaded cursors
+ */
+ void freeCursors();
+
+ /**
+ * Poll any pending events
+ */
+ void pollEvents();
+
+ /**
+ * Return the current mouse position
+ */
+ Common::Point mousePos() const { return _mousePos; }
+
+ /**
+ * Delay for a given number of frames
+ */
+ void delay(int amount);
+};
+
+} // End of namespace MADS
+
+#endif /* MADS_EVENTS_H */
diff --git a/engines/mads/font.cpp b/engines/mads/font.cpp
new file mode 100644
index 0000000000..52541500c9
--- /dev/null
+++ b/engines/mads/font.cpp
@@ -0,0 +1,208 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "mads/mads.h"
+#include "mads/compression.h"
+#include "mads/font.h"
+#include "mads/msurface.h"
+
+namespace MADS {
+
+Font::Font(MADSEngine *vm) : _vm(vm) {
+ _sysFont = true;
+
+ _fontColors[0] = _vm->_palette->BLACK;
+ _fontColors[1] = _vm->_palette->WHITE;
+ _fontColors[2] = _vm->_palette->BLACK;
+ _fontColors[3] = _vm->_palette->DARK_GRAY;
+}
+
+Font::~Font() {
+ if (!_sysFont) {
+ delete[] _charWidths;
+ delete[] _charOffs;
+ delete[] _charData;
+ }
+}
+
+void Font::setFont(const Common::String &filename) {
+ if (!_filename.empty() && (filename == _filename))
+ // Already using specified font, so don't bother reloading
+ return;
+
+ _sysFont = false;
+ _filename = filename;
+
+ MadsPack fontData(filename, _vm);
+ Common::SeekableReadStream *fontFile = fontData.getItemStream(0);
+
+ _maxHeight = fontFile->readByte();
+ _maxWidth = fontFile->readByte();
+
+ _charWidths = new uint8[128];
+ // Char data is shifted by 1
+ _charWidths[0] = 0;
+ fontFile->read(_charWidths + 1, 127);
+ fontFile->readByte(); // remainder
+
+ _charOffs = new uint16[128];
+
+ uint startOffs = 2 + 128 + 256;
+ uint fontSize = fontFile->size() - startOffs;
+
+ // Char data is shifted by 1
+ _charOffs[0] = 0;
+ for (int i = 1; i < 128; i++)
+ _charOffs[i] = fontFile->readUint16LE() - startOffs;
+ fontFile->readUint16LE(); // remainder
+
+ _charData = new uint8[fontSize];
+ fontFile->read(_charData, fontSize);
+
+ delete fontFile;
+}
+
+void Font::setColor(uint8 color) {
+ if (_sysFont)
+ _fontColors[1] = color;
+ else
+ _fontColors[3] = color;
+}
+
+void Font::setColors(uint8 v1, uint8 v2, uint8 v3, uint8 v4) {
+ _fontColors[0] = v1;
+ _fontColors[1] = v2;
+ _fontColors[2] = v3;
+ _fontColors[3] = v4;
+}
+
+int Font::write(MSurface *surface, const Common::String &msg, const Common::Point &pt, int width, int spaceWidth, uint8 colors[]) {
+
+ /*TODO
+ if (custom_ascii_converter) { // if there is a function to convert the extended ASCII characters
+ custom_ascii_converter(out_string); // call it with the string
+ }
+ */
+
+ if (width > 0)
+ width = MIN(surface->getWidth(), pt.x + width);
+ else
+ width = surface->getWidth();
+
+ int x = pt.x + 1;
+ int y = pt.y + 1;
+
+ int skipY = 0;
+ if (y < 0) {
+ skipY = -y;
+ y = 0;
+ }
+
+ int height = MAX(0, _maxHeight - skipY);
+ if (height == 0)
+ return x;
+
+ int bottom = y + height - 1;
+ if (bottom > surface->getHeight() - 1) {
+ height -= MIN(height, bottom - (surface->getHeight() - 1));
+ }
+
+ if (height <= 0)
+ return x;
+
+ byte *destPtr = surface->getBasePtr(x, y);
+ uint8 *oldDestPtr = destPtr;
+
+ int xPos = x;
+
+ const char *text = msg.c_str();
+ while (*text) {
+ char theChar = (*text++) & 0x7F;
+ int charWidth = _charWidths[(byte)theChar];
+
+ if (charWidth > 0) {
+
+ if (xPos + charWidth >= width)
+ return xPos;
+
+ uint8 *charData = &_charData[_charOffs[(byte)theChar]];
+ int bpp = getBpp(charWidth);
+
+ if (skipY != 0)
+ charData += bpp * skipY;
+
+ for (int i = 0; i < height; i++) {
+ for (int j = 0; j < bpp; j++) {
+ if (*charData & 0xc0)
+ *destPtr = colors[(*charData & 0xc0) >> 6];
+ destPtr++;
+ if (*charData & 0x30)
+ *destPtr = colors[(*charData & 0x30) >> 4];
+ destPtr++;
+ if (*charData & 0x0C)
+ *destPtr = colors[(*charData & 0x0C) >> 2];
+ destPtr++;
+ if (*charData & 0x03)
+ *destPtr = colors[*charData & 0x03];
+ destPtr++;
+ charData++;
+ }
+
+ destPtr += surface->getWidth() - bpp * 4;
+
+ }
+
+ destPtr = oldDestPtr + charWidth + spaceWidth;
+ oldDestPtr = destPtr;
+
+ }
+
+ xPos += charWidth + spaceWidth;
+
+ }
+
+ return xPos;
+
+}
+
+int Font::getWidth(const Common::String &msg, int spaceWidth) {
+ int width = 0;
+ const char *text = msg.c_str();
+
+ while (*text)
+ width += _charWidths[*text++ & 0x7F] + spaceWidth;
+ return width;
+}
+
+int Font::getBpp(int charWidth) {
+ if (charWidth > 12)
+ return 4;
+ else if (charWidth > 8)
+ return 3;
+ else if (charWidth > 4)
+ return 2;
+ else
+ return 1;
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/font.h b/engines/mads/font.h
new file mode 100644
index 0000000000..3deb3f4d2d
--- /dev/null
+++ b/engines/mads/font.h
@@ -0,0 +1,75 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_FONT_H
+#define MADS_FONT_H
+
+#include "common/scummsys.h"
+#include "common/util.h"
+#include "common/endian.h"
+#include "mads/msurface.h"
+
+namespace MADS {
+
+#define FONT_CONVERSATION "*FONTCONV.FF"
+#define FONT_INTERFACE "*FONTINTR.FF"
+#define FONT_MAIN "*FONTMAIN.FF"
+#define FONT_MENU "*FONTMENU.FF" // Not in Rex (uses bitmap files for menu strings)
+#define FONT_MISC "*FONTMISC.FF"
+#define FONT_TELE "*FONTTELE.FF" // Not in Phantom
+#define FONT_PHAN "*FONTPHAN.FF" // Phantom only
+
+class MADSEngine;
+
+class Font {
+protected:
+ MADSEngine *_vm;
+
+ uint8 _maxWidth, _maxHeight;
+ uint8 *_charWidths;
+ uint16 *_charOffs;
+ uint8 *_charData;
+ bool _sysFont;
+ Common::String _filename;
+ uint8 _fontColors[4];
+
+ int getBpp(int charWidth);
+public:
+ Font(MADSEngine *vm);
+ virtual ~Font();
+
+ void setFont(const Common::String &filename);
+ void setColor(uint8 color);
+ void setColors(uint8 v1, uint8 v2, uint8 v3, uint8 v4);
+
+ int maxWidth() const { return _maxWidth; }
+ int getWidth(const Common::String &msg, int spaceWidth = -1);
+ int getHeight() const { return _maxHeight; }
+ int write(MSurface *surface, const Common::String &msg, const Common::Point &pt, int width, int spaceWidth, uint8 colors[]);
+ int writeString(MSurface *surface, const Common::String &msg, const Common::Point &pt, int width = 0, int spaceWidth = -1) {
+ return write(surface, msg, pt, width, spaceWidth, _fontColors);
+ }
+};
+
+} // End of namespace MADS
+
+#endif /* MADS_FONT_H */
diff --git a/engines/mads/game.cpp b/engines/mads/game.cpp
new file mode 100644
index 0000000000..08d548e235
--- /dev/null
+++ b/engines/mads/game.cpp
@@ -0,0 +1,208 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "mads/mads.h"
+#include "mads/game.h"
+#include "mads/game_data.h"
+#include "mads/nebular/game_nebular.h"
+#include "mads/graphics.h"
+#include "mads/msurface.h"
+#include "mads/resources.h"
+
+namespace MADS {
+
+Game *Game::init(MADSEngine *vm) {
+ if (vm->getGameID() == GType_RexNebular)
+ return new Nebular::GameNebular(vm);
+
+ return nullptr;
+}
+
+Game::Game(MADSEngine *vm): _vm(vm), _surface(nullptr),
+ _objects(vm), _scene(vm) {
+ _sectionNumber = _priorSectionNumber = 0;
+ _difficultyLevel = DIFFICULTY_HARD;
+ _saveSlot = -1;
+ _statusFlag = 0;
+ _sectionHandler = nullptr;
+ _sectionNumber = 1;
+ _priorSectionNumber = 0;
+ _currentSectionNumber = -1;
+ _v1 = _v2 = 0;
+ _v3 = _v4 = 0;
+ _v5 = _v6 = 0;
+ _aaName = "*I0.AA";
+ _playerSpritesFlag = false;
+}
+
+Game::~Game() {
+ delete _surface;
+ delete _sectionHandler;
+}
+
+void Game::run() {
+ _statusFlag = true;
+ int protectionResult = checkCopyProtection();
+ switch (protectionResult) {
+ case 1:
+ // Copy protection failed
+ _scene._nextSceneId = 804;
+ initialiseGlobals();
+ _globalFlags[5] = 0xFFFF;
+ _saveSlot = -1;
+ break;
+ case 2:
+ _statusFlag = 0;
+ break;
+ default:
+ break;
+ }
+
+ if (_saveSlot == -1 && protectionResult != -1 && protectionResult != -2) {
+ initSection(_sectionNumber);
+ _statusFlag = true;
+
+ _vm->_dialogs->_pendingDialog = DIALOG_DIFFICULTY;
+ _vm->_dialogs->showDialog();
+ _vm->_dialogs->_pendingDialog = DIALOG_NONE;
+
+ _priorSectionNumber = 0;
+ _priorSectionNumber = -1;
+ _scene._priorSceneId = 0;
+ _scene._currentSceneId = -1;
+ }
+
+ if (protectionResult != 1 && protectionResult != 2) {
+ initialiseGlobals();
+
+ if (_saveSlot != -1) {
+ warning("TODO: loadGame(\"REX.SAV\", 210)");
+ _statusFlag = false;
+ }
+ }
+
+ if (_statusFlag)
+ gameLoop();
+}
+
+void Game::gameLoop() {
+ while (!_vm->shouldQuit() && _statusFlag) {
+ setSectionHandler();
+ _sectionHandler->preLoadSection();
+ initSection(_sectionNumber);
+ _sectionHandler->postLoadSection();
+
+ _scene._spriteSlots.clear(true);
+
+ if (_sectionNumber == _currentSectionNumber) {
+ sectionLoop();
+ }
+
+ // TODO: Extra reset methods
+ _vm->_events->resetCursor();
+ _vm->_events->freeCursors();
+ _vm->_sound->closeDriver();
+ }
+
+ _vm->_palette->close();
+}
+
+void Game::sectionLoop() {
+ while (!_vm->shouldQuit() && _statusFlag && _sectionNumber == _currentSectionNumber) {
+ _v1 = 3;
+ _player._spritesChanged = true;
+ _v5 = 0;
+ _v6 = 0;
+ _vm->_events->resetCursor();
+
+ _quotes = nullptr;
+ _scene.clearVocab();
+ _scene.loadSceneLogic();
+
+ _v4 = 0;
+ _player._stepEnabled = true;
+ _player._visible = true;
+ _vm->_dialogs->_defaultPosition = Common::Point(-1, -1);
+ _visitedScenes.add(_scene._nextSceneId);
+
+ _scene._screenObjects._v8333C = -1;
+ _scene._screenObjects._v832EC = 0;
+ _scene._screenObjects._yp = 0;
+ _v3 = -1;
+
+ _scene._sceneLogic->setup();
+ if (_player._spritesChanged || _v3) {
+ if (_player._spritesLoaded)
+ _scene._spriteSlots.releasePlayerSprites();
+ _vm->_palette->resetGamePalette(18, 10);
+ _scene._spriteSlots.clear(true);
+ } else {
+ _vm->_palette->initGamePalette();
+ }
+
+ _vm->_palette->_paletteUsage.load(3, 0xF0, 0xF1, 0xF2);
+
+ _scene.loadScene(_scene._nextSceneId, _aaName, 0);
+ _vm->_sound->pauseNewCommands();
+
+ if (!_player._spritesLoaded) {
+ _player.loadSprites("");
+ _playerSpritesFlag = false;
+ }
+
+
+ // TODO: main section loop logic goes here
+
+ // Clear the scene
+ _scene.free();
+ _sectionNumber = _scene._nextSceneId / 100;
+
+ // TODO: sub_1DD46(3)
+
+ // Check whether to show a dialog
+ if (_vm->_dialogs->_pendingDialog && _player._stepEnabled && !_globalFlags[5]) {
+ _scene._spriteSlots.releasePlayerSprites();
+ _vm->_dialogs->showDialog();
+ _vm->_dialogs->_pendingDialog = DIALOG_NONE;
+ }
+ }
+}
+
+void Game::initSection(int sectionNumber) {
+ _priorSectionNumber = _currentSectionNumber;
+ _currentSectionNumber = sectionNumber;
+
+ _vm->_palette->resetGamePalette(18, 10);
+ _vm->_palette->setLowRange();
+ _vm->_events->loadCursors("*CURSOR.SS");
+
+ assert(_vm->_events->_cursorSprites);
+ _vm->_events->setCursor2((_vm->_events->_cursorSprites->getCount() <= 1) ?
+ CURSOR_ARROW : CURSOR_WAIT);
+}
+
+void Game::loadResourceSequence(const Common::String prefix, int v) {
+ warning("TODO: loadResourceSequence");
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/game.h b/engines/mads/game.h
new file mode 100644
index 0000000000..b8add9ab00
--- /dev/null
+++ b/engines/mads/game.h
@@ -0,0 +1,125 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_GAME_H
+#define MADS_GAME_H
+
+#include "common/scummsys.h"
+#include "mads/scene.h"
+#include "mads/game_data.h"
+
+namespace MADS {
+
+class MADSEngine;
+
+enum {
+ PLAYER_INVENTORY = 2
+};
+
+enum Difficulty {
+ DIFFICULTY_HARD = 1, DIFFICULTY_MEDIUM = 2, DIFFICULTY_EASY = 3
+};
+
+class Game {
+private:
+ /**
+ * Main game loop
+ */
+ void gameLoop();
+
+ /**
+ * Inner game loop for executing gameplay within a game section
+ */
+ void sectionLoop();
+protected:
+ MADSEngine *_vm;
+ MSurface *_surface;
+ Difficulty _difficultyLevel;
+ Player _player;
+ int _saveSlot;
+ int _statusFlag;
+ SectionHandler *_sectionHandler;
+ VisitedScenes _visitedScenes;
+ byte *_quotes;
+ int _v1;
+ int _v3;
+ int _v4;
+ int _v5;
+ int _v6;
+ Common::String _aaName;
+ bool _playerSpritesFlag;
+
+ /**
+ * Constructor
+ */
+ Game(MADSEngine *vm);
+
+ /**
+ * Initialises the current section number of the game
+ */
+ void initSection(int sectionNumber);
+
+ void loadResourceSequence(const Common::String prefix, int v);
+
+ //@{
+ /** @name Virtual Method list */
+
+ /**
+ * Perform any copy protection check
+ */
+ virtual int checkCopyProtection() = 0;
+
+ /**
+ * Initialises global variables for a new game
+ */
+ virtual void initialiseGlobals() = 0;
+
+ /**
+ * Set up the section handler specific to each section
+ */
+ virtual void setSectionHandler() = 0;
+ //@}
+
+public:
+ static Game *init(MADSEngine *vm);
+public:
+ int _sectionNumber;
+ int _priorSectionNumber;
+ int _currentSectionNumber;
+ Common::Array<uint16> _globalFlags;
+ InventoryObjects _objects;
+ Scene _scene;
+ int _v2;
+public:
+ virtual ~Game();
+
+ /**
+ * Run the game
+ */
+ void run();
+
+ Player &player() { return _player; }
+};
+
+} // End of namespace MADS
+
+#endif /* MADS_GAME_H */
diff --git a/engines/mads/game_data.cpp b/engines/mads/game_data.cpp
new file mode 100644
index 0000000000..df4a902c25
--- /dev/null
+++ b/engines/mads/game_data.cpp
@@ -0,0 +1,116 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "mads/mads.h"
+#include "mads/game.h"
+#include "mads/nebular/game_nebular.h"
+#include "mads/graphics.h"
+#include "mads/msurface.h"
+#include "mads/resources.h"
+
+namespace MADS {
+
+void VisitedScenes::add(int sceneId) {
+ if (!exists(sceneId))
+ push_back(sceneId);
+}
+
+bool VisitedScenes::exists(int sceneId) {
+ for (uint i = 0; i < size(); ++i) {
+ if ((*this)[i] == sceneId)
+ return true;
+ }
+
+ return false;
+}
+
+void InventoryObject::load(Common::SeekableReadStream &f) {
+ _descId = f.readUint16LE();
+ _roomNumber = f.readUint16LE();
+ _article = f.readByte();
+ _vocabCount = f.readByte();
+
+ for (int i = 0; i < 3; ++i) {
+ _vocabList[i]._actionFlags1 = f.readByte();
+ _vocabList[i]._actionFlags2 = f.readByte();
+ _vocabList[i]._vocabId = f.readByte();
+ }
+
+ f.skip(4); // field12
+ f.read(&_mutilateString[0], 10);
+ f.skip(16);
+}
+
+/*------------------------------------------------------------------------*/
+
+void InventoryObjects::load() {
+ File f("*OBJECTS.DAT");
+
+ // Get the total numer of inventory objects
+ int count = f.readUint16LE();
+ reserve(count);
+
+ // Read in each object
+ for (int i = 0; i < count; ++i) {
+ InventoryObject obj;
+ obj.load(f);
+ push_back(obj);
+
+ // If it's for the player's inventory, add the index to the inventory list
+ if (obj._roomNumber == PLAYER_INVENTORY) {
+ _inventoryList.push_back(i);
+ assert(_inventoryList.size() <= 32);
+ }
+ }
+}
+
+void InventoryObjects::setData(int objIndex, int id, const byte *p) {
+ // TODO: This whole method seems weird. Check it out more thoroughly once
+ // more of the engine is implemented
+ for (int i = 0; i < (int)size(); ++i) {
+ InventoryObject &obj = (*this)[i];
+ if (obj._vocabList[0]._actionFlags1 <= i)
+ break;
+
+ if (obj._mutilateString[6 + i] == id) {
+ (*this)[objIndex]._objFolder = p;
+ }
+ }
+}
+
+void InventoryObjects::setRoom(int objectId, int roomNumber) {
+ warning("TODO: setObjectRoom");
+}
+
+/*------------------------------------------------------------------------*/
+
+Player::Player() {
+ _direction = 8;
+ _newDirection = 8;
+ _spritesLoaded = false;
+ _spritesStart = _numSprites = 0;
+ _stepEnabled = false;
+ _visible = false;
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/game_data.h b/engines/mads/game_data.h
new file mode 100644
index 0000000000..2d2badd38b
--- /dev/null
+++ b/engines/mads/game_data.h
@@ -0,0 +1,126 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_GAME_DATA_H
+#define MADS_GAME_DATA_H
+
+#include "common/scummsys.h"
+#include "common/array.h"
+
+namespace MADS {
+
+class MADSEngine;
+class Game;
+
+class VisitedScenes: public Common::Array<int> {
+public:
+ /**
+ * Returns true if a given Scene Id exists in the listed of previously visited scenes.
+ */
+ bool exists(int sceneId);
+
+ /**
+ * Adds a scene Id to the list of previously visited scenes, if it doesn't already exist
+ */
+ void add(int sceneId);
+};
+
+class InventoryObject {
+public:
+ int _descId;
+ int _roomNumber;
+ int _article;
+ int _vocabCount;
+ struct {
+ int _actionFlags1;
+ int _actionFlags2;
+ int _vocabId;
+ } _vocabList[3];
+ char _mutilateString[10]; // ???
+ const byte *_objFolder; // ???
+
+ /**
+ * Loads the data for a given object
+ */
+ void load(Common::SeekableReadStream &f);
+};
+
+class InventoryObjects: public Common::Array<InventoryObject> {
+private:
+ MADSEngine *_vm;
+public:
+ Common::Array<int> _inventoryList;
+
+ /**
+ * Constructor
+ */
+ InventoryObjects(MADSEngine *vm): _vm(vm) {}
+
+ /**
+ * Loads the game's object list
+ */
+ void load();
+
+ /**
+ * Set the associated data? pointer with an inventory object
+ */
+ void setData(int objIndex, int id, const byte *p);
+
+ /**
+ * Sets the room number
+ */
+ void setRoom(int objectId, int roomNumber);
+};
+
+class Player {
+public:
+ int _direction;
+ int _newDirection;
+ bool _spritesLoaded;
+ int _spritesStart;
+ int _numSprites;
+ bool _stepEnabled;
+ bool _spritesChanged;
+ bool _visible;
+public:
+ Player();
+
+ void loadSprites(const Common::String &prefix) {
+ warning("TODO: Player::loadSprites");
+ }
+};
+
+class SectionHandler {
+protected:
+ MADSEngine *_vm;
+public:
+ SectionHandler(MADSEngine *vm): _vm(vm) {}
+ virtual ~SectionHandler() {}
+
+ virtual void preLoadSection() = 0;
+ virtual void sectionPtr2() = 0;
+ virtual void postLoadSection() = 0;
+};
+
+} // End of namespace MADS
+
+#endif /* MADS_GAME_DATA_H */
diff --git a/engines/mads/graphics.cpp b/engines/mads/graphics.cpp
new file mode 100644
index 0000000000..267785b76b
--- /dev/null
+++ b/engines/mads/graphics.cpp
@@ -0,0 +1,30 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "mads/mads.h"
+#include "mads/graphics.h"
+
+namespace MADS {
+
+
+} // End of namespace MADS
diff --git a/engines/mads/graphics.h b/engines/mads/graphics.h
new file mode 100644
index 0000000000..87cd8be3c9
--- /dev/null
+++ b/engines/mads/graphics.h
@@ -0,0 +1,36 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_GRAPHICS_H
+#define MADS_GRAPHICS_H
+
+#include "common/scummsys.h"
+
+namespace MADS {
+
+#define MADS_SCREEN_WIDTH 320
+#define MADS_SCREEN_HEIGHT 200
+#define MADS_INTERFACE_HEIGHT 44
+
+} // End of namespace MADS
+
+#endif /* MADS_GRAPHICS_H */
diff --git a/engines/mads/mads.cpp b/engines/mads/mads.cpp
new file mode 100644
index 0000000000..d78f37dd8b
--- /dev/null
+++ b/engines/mads/mads.cpp
@@ -0,0 +1,112 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "common/config-manager.h"
+#include "common/debug-channels.h"
+#include "common/events.h"
+#include "engines/util.h"
+#include "mads/mads.h"
+#include "mads/graphics.h"
+#include "mads/resources.h"
+#include "mads/sound.h"
+#include "mads/msurface.h"
+#include "mads/msprite.h"
+
+namespace MADS {
+
+MADSEngine::MADSEngine(OSystem *syst, const MADSGameDescription *gameDesc) :
+ _gameDescription(gameDesc), Engine(syst), _randomSource("MADS") {
+
+ // Initialise fields
+ _easyMouse = true;
+ _invObjectStill = false;
+ _textWindowStill = false;
+
+ _debugger = nullptr;
+ _dialogs = nullptr;
+ _events = nullptr;
+ _font = nullptr;
+ _game = nullptr;
+ _palette = nullptr;
+ _resources = nullptr;
+ _screen = nullptr;
+ _sound = nullptr;
+ _userInterface = nullptr;
+}
+
+MADSEngine::~MADSEngine() {
+ delete _debugger;
+ delete _dialogs;
+ delete _events;
+ delete _font;
+ delete _game;
+ delete _palette;
+ delete _resources;
+ delete _screen;
+ delete _sound;
+ delete _userInterface;
+}
+
+void MADSEngine::initialise() {
+ // Set up debug channels
+ DebugMan.addDebugChannel(kDebugPath, "Path", "Pathfinding debug level");
+ DebugMan.addDebugChannel(kDebugScripts, "scripts", "Game scripts");
+ DebugMan.addDebugChannel(kDebugGraphics, "graphics", "Graphics handling");
+
+ // Initial sub-system engine references
+ MSurface::setVm(this);
+ MSprite::setVm(this);
+
+ Resources::init(this);
+ _debugger = new Debugger(this);
+ _dialogs = Dialogs::init(this);
+ _events = new EventsManager(this);
+ _palette = new Palette(this);
+ _font = new Font(this);
+ _screen = new MSurface(g_system->getWidth(), g_system->getHeight());
+ _sound = new SoundManager(this, _mixer);
+ _userInterface = UserInterface::init(this);
+ _game = Game::init(this);
+
+ _events->loadCursors("*CURSOR.SS");
+ _screen->empty();
+}
+
+Common::Error MADSEngine::run() {
+ initGraphics(MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT, false);
+ initialise();
+
+ // Run the game
+ _game->run();
+
+ // Dummy loop to keep application active
+ _events->delay(9999);
+
+ return Common::kNoError;
+}
+
+int MADSEngine::getRandomNumber(int maxNumber) {
+ return _randomSource.getRandomNumber(maxNumber);
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/mads.h b/engines/mads/mads.h
new file mode 100644
index 0000000000..44630ca135
--- /dev/null
+++ b/engines/mads/mads.h
@@ -0,0 +1,118 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_MADS_H
+#define MADS_MADS_H
+
+#include "common/scummsys.h"
+#include "common/system.h"
+#include "common/error.h"
+#include "common/random.h"
+#include "common/util.h"
+#include "engines/engine.h"
+#include "graphics/surface.h"
+#include "mads/debugger.h"
+#include "mads/dialogs.h"
+#include "mads/events.h"
+#include "mads/font.h"
+#include "mads/game.h"
+#include "mads/msurface.h"
+#include "mads/resources.h"
+#include "mads/sound.h"
+#include "mads/user_interface.h"
+
+/**
+ * This is the namespace of the MADS engine.
+ *
+ * Status of this engine: In Development
+ *
+ * Games using this engine:
+ * - Rex Nebular and the Cosmic Gender Bender
+ */
+namespace MADS {
+
+#define DEBUG_BASIC 1
+#define DEBUG_INTERMEDIATE 2
+#define DEBUG_DETAILED 3
+
+enum MADSDebugChannels {
+ kDebugPath = 1 << 0,
+ kDebugScripts = 1 << 1,
+ kDebugGraphics = 1 << 2
+};
+
+enum {
+ GType_RexNebular = 0,
+ GType_DragonSphere = 1,
+ GType_Phantom = 2,
+ GType_Riddle = 3
+};
+
+struct MADSGameDescription;
+
+
+class MADSEngine : public Engine {
+private:
+ const MADSGameDescription *_gameDescription;
+ Common::RandomSource _randomSource;
+
+ bool _easyMouse;
+ bool _invObjectStill;
+ bool _textWindowStill;
+
+ /**
+ * Handles basic initialisation
+ */
+ void initialise();
+protected:
+ // Engine APIs
+ virtual Common::Error run();
+ virtual bool hasFeature(EngineFeature f) const;
+public:
+ Debugger *_debugger;
+ Dialogs *_dialogs;
+ EventsManager *_events;
+ Font *_font;
+ Game *_game;
+ Palette *_palette;
+ Resources *_resources;
+ MSurface *_screen;
+ SoundManager *_sound;
+ UserInterface *_userInterface;
+
+public:
+ MADSEngine(OSystem *syst, const MADSGameDescription *gameDesc);
+ virtual ~MADSEngine();
+
+ uint32 getFeatures() const;
+ Common::Language getLanguage() const;
+ Common::Platform getPlatform() const;
+ uint16 getVersion() const;
+ uint32 getGameID() const;
+ uint32 getGameFeatures() const;
+
+ int getRandomNumber(int maxNumber);
+};
+
+} // End of namespace MADS
+
+#endif /* MADS_MADS_H */
diff --git a/engines/mads/module.mk b/engines/mads/module.mk
new file mode 100644
index 0000000000..62441ab3c9
--- /dev/null
+++ b/engines/mads/module.mk
@@ -0,0 +1,35 @@
+MODULE := engines/mads
+
+MODULE_OBJS := \
+ nebular/dialogs_nebular.o \
+ nebular/game_nebular.o \
+ nebular/sound_nebular.o \
+ nebular/nebular_scenes.o \
+ nebular/nebular_scenes8.o \
+ assets.o \
+ compression.o \
+ debugger.o \
+ detection.o \
+ dialogs.o \
+ events.o \
+ font.o \
+ game.o \
+ game_data.o \
+ graphics.o \
+ mads.o \
+ msprite.o \
+ msurface.o \
+ palette.o \
+ resources.o \
+ scene.o \
+ scene_data.o \
+ sound.o \
+ user_interface.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_MADS), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/mads/msprite.cpp b/engines/mads/msprite.cpp
new file mode 100644
index 0000000000..279192fbdc
--- /dev/null
+++ b/engines/mads/msprite.cpp
@@ -0,0 +1,107 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "engines/util.h"
+#include "graphics/palette.h"
+#include "mads/mads.h"
+#include "mads/msurface.h"
+#include "mads/msprite.h"
+
+namespace MADS {
+
+enum {
+ kEndOfLine = 0,
+ kEndOfSprite = 1,
+ kMarker = 2
+};
+
+MSprite::MSprite(): MSurface() {
+ _encoding = 0;
+}
+
+MSprite::MSprite(Common::SeekableReadStream *source, const Common::Point &offset,
+ int widthVal, int heightVal, bool decodeRle, uint8 encodingVal)
+ : MSurface(widthVal, heightVal),
+ _encoding(encodingVal), _offset(offset) {
+
+ // Load the sprite data
+ loadSprite(source);
+}
+
+MSprite::~MSprite() {
+}
+
+
+// TODO: The sprite outlines (pixel value 0xFD) are not shown
+void MSprite::loadSprite(Common::SeekableReadStream *source) {
+ byte *outp, *lineStart;
+ bool newLine = false;
+
+ outp = getData();
+ lineStart = getData();
+
+ while (1) {
+ byte cmd1, cmd2, count, pixel;
+
+ if (newLine) {
+ outp = lineStart + getWidth();
+ lineStart = outp;
+ newLine = false;
+ }
+
+ cmd1 = source->readByte();
+
+ if (cmd1 == 0xFC)
+ break;
+ else if (cmd1 == 0xFF)
+ newLine = true;
+ else if (cmd1 == 0xFD) {
+ while (!newLine) {
+ count = source->readByte();
+ if (count == 0xFF) {
+ newLine = true;
+ } else {
+ pixel = source->readByte();
+ while (count--)
+ *outp++ = (pixel == 0xFD) ? 0 : pixel;
+ }
+ }
+ } else {
+ while (!newLine) {
+ cmd2 = source->readByte();
+ if (cmd2 == 0xFF) {
+ newLine = true;
+ } else if (cmd2 == 0xFE) {
+ count = source->readByte();
+ pixel = source->readByte();
+ while (count--)
+ *outp++ = (pixel == 0xFD) ? 0 : pixel;
+ } else {
+ *outp++ = (cmd2 == 0xFD) ? 0 : cmd2;
+ }
+ }
+ }
+ }
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/msprite.h b/engines/mads/msprite.h
new file mode 100644
index 0000000000..f2194dab08
--- /dev/null
+++ b/engines/mads/msprite.h
@@ -0,0 +1,114 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_MSPRITE_H
+#define MADS_MSPRITE_H
+
+#include "common/scummsys.h"
+#include "mads/msurface.h"
+
+namespace MADS {
+
+class MADSEngine;
+
+struct BGR8 {
+ uint8 b, g, r;
+};
+
+typedef struct {
+ int32 x; // x position relative to GrBuff(0, 0)
+ int32 y; // y position relative to GrBuff(0, 0)
+ int32 scale_x; // x scale factor (can be negative for reverse draw)
+ int32 scale_y; // y scale factor (can't be negative)
+ uint8* depth_map; // depth code array for destination (doesn't care if srcDepth is 0)
+ BGR8* Pal; // palette for shadow draw (doesn't care if SHADOW bit is not set in Src.encoding)
+ uint8* ICT; // Inverse Color Table (doesn't care if SHADOW bit is not set in Src.encoding)
+ uint8 depth; // depth code for source (0 if no depth processing)
+} DrawRequestX;
+
+typedef struct
+{
+ uint32 Pack;
+ uint32 Stream;
+ long hot_x;
+ long hot_y;
+ uint32 Width;
+ uint32 Height;
+ uint32 Comp;
+ uint32 Reserved[8];
+ uint8* data;
+} RendCell;
+
+#define SS_HEADER_NUM_FIELDS 14
+struct SpriteSeriesHeader {
+ uint32 header;
+ uint32 size;
+ uint32 packing;
+ uint32 frameRate;
+ uint32 pixSpeed;
+ uint32 maxWidth;
+ uint32 maxHeight;
+ uint32 reserved3;
+ uint32 reserved4;
+ uint32 reserved5;
+ uint32 reserved6;
+ uint32 reserved7;
+ uint32 reserved8;
+ uint32 count;
+};
+
+#define SF_HEADER_NUM_FIELDS 15
+struct SpriteFrameHeader {
+ uint32 pack;
+ uint32 stream;
+ uint32 x;
+ uint32 y;
+ uint32 width;
+ uint32 height;
+ uint32 comp;
+ uint32 reserved1;
+ uint32 reserved2;
+ uint32 reserved3;
+ uint32 reserved4;
+ uint32 reserved5;
+ uint32 reserved6;
+ uint32 reserved7;
+ uint32 reserved8;
+};
+
+class MSprite: public MSurface {
+private:
+ void loadSprite(Common::SeekableReadStream *source);
+public:
+ MSprite();
+ MSprite(Common::SeekableReadStream *source, const Common::Point &offset,
+ int widthVal, int heightVal, bool decodeRle = true, uint8 encodingVal = 0);
+ virtual ~MSprite();
+
+ Common::Point _pos;
+ Common::Point _offset;
+ uint8 _encoding;
+};
+
+} // End of namespace MADS
+
+#endif /* MADS_MSPRITE_H */
diff --git a/engines/mads/msurface.cpp b/engines/mads/msurface.cpp
new file mode 100644
index 0000000000..ca75a50bde
--- /dev/null
+++ b/engines/mads/msurface.cpp
@@ -0,0 +1,262 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/util.h"
+#include "mads/compression.h"
+#include "mads/graphics.h"
+#include "mads/mads.h"
+#include "mads/msprite.h"
+#include "mads/msurface.h"
+#include "mads/resources.h"
+
+namespace MADS {
+
+MADSEngine *MSurface::_vm = nullptr;
+
+MSurface::MSurface() {
+ pixels = nullptr;
+}
+
+MSurface::MSurface(int width, int height) {
+ pixels = nullptr;
+ setSize(width, height);
+}
+
+MSurface::~MSurface() {
+ Graphics::Surface::free();
+}
+
+void MSurface::setSize(int width, int height) {
+ Graphics::Surface::free();
+ Graphics::Surface::create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+}
+
+int MSurface::scaleValue(int value, int scale, int err) {
+ int scaled = 0;
+ while (value--) {
+ err -= scale;
+ while (err < 0) {
+ scaled++;
+ err += 100;
+ }
+ }
+ return scaled;
+}
+
+void MSurface::drawSprite(const Common::Point &pt, SpriteInfo &info, const Common::Rect &clipRect) {
+
+ enum {
+ kStatusSkip,
+ kStatusScale,
+ kStatusDraw
+ };
+
+ // NOTE: The current clipping code assumes that the top left corner of the clip
+ // rectangle is always 0, 0
+ assert(clipRect.top == 0 && clipRect.left == 0);
+
+ // TODO: Put err* and scaled* into SpriteInfo
+ int errX = info.hotX * info.scaleX % 100;
+ int errY = info.hotY * info.scaleY % 100;
+ int scaledWidth = scaleValue(info.width, info.scaleX, errX);
+ int scaledHeight = scaleValue(info.height, info.scaleY, errY);
+
+ int x = pt.x, y = pt.y;
+ int clipX = 0, clipY = 0;
+ // Clip the sprite's width and height according to the clip rectangle's dimensions
+ // This clips the sprite to the bottom and right
+ if (x >= 0) {
+ scaledWidth = MIN<int>(x + scaledWidth, clipRect.right) - x;
+ } else {
+ clipX = x;
+ scaledWidth = x + scaledWidth;
+ }
+ if (y >= 0) {
+ scaledHeight = MIN<int>(y + scaledHeight, clipRect.bottom) - y;
+ } else {
+ clipY = y;
+ scaledHeight = y + scaledHeight;
+ }
+
+ // Check if sprite is inside the screen. If it's not, there's no need to draw it
+ if (scaledWidth + x <= 0 || scaledHeight + y <= 0) // check left and top (in case x,y are negative)
+ return;
+ if (scaledWidth <= 0 || scaledHeight <= 0) // check right and bottom
+ return;
+ int heightAmt = scaledHeight;
+
+ byte *src = info.sprite->getData();
+ byte *dst = getBasePtr(x - info.hotX - clipX, y - info.hotY - clipY);
+
+ int status = kStatusSkip;
+ byte *scaledLineBuf = new byte[scaledWidth];
+
+ while (heightAmt > 0) {
+
+ if (status == kStatusSkip) {
+ // Skip line
+ errY -= info.scaleY;
+ if (errY < 0)
+ status = kStatusScale;
+ else
+ src += info.width;
+ } else {
+
+ if (status == kStatusScale) {
+ // Scale current line
+ byte *lineDst = scaledLineBuf;
+ int curErrX = errX;
+ int width = scaledWidth;
+ byte *tempSrc = src;
+ int startX = clipX;
+ while (width > 0) {
+ byte pixel = *tempSrc++;
+ curErrX -= info.scaleX;
+ while (curErrX < 0) {
+ if (startX == 0) {
+ *lineDst++ = pixel;
+ width--;
+ } else {
+ startX++;
+ }
+ curErrX += 100;
+ }
+ }
+ src += info.width;
+ status = kStatusDraw;
+ }
+
+ if (status == kStatusDraw && clipY == 0) {
+ // Draw previously scaled line
+ // TODO Implement different drawing types (depth, shadow etc.)
+ byte *tempDst = dst;
+ for (int lineX = 0; lineX < scaledWidth; lineX++) {
+ byte pixel = scaledLineBuf[lineX];
+
+ if (info.encoding & 0x80) {
+
+ if (pixel == 0x80) {
+ pixel = 0;
+ } else {
+ byte destPixel = *tempDst;
+ byte r, g, b;
+ r = CLIP((info.palette[destPixel * 3] * pixel) >> 10, 0, 31);
+ g = CLIP((info.palette[destPixel * 3 + 1] * pixel) >> 10, 0, 31);
+ b = CLIP((info.palette[destPixel * 3 + 2] * pixel) >> 10, 0, 31);
+ pixel = info.inverseColorTable[(b << 10) | (g << 5) | r];
+ }
+ }
+
+ if (pixel)
+ *tempDst = pixel;
+
+ tempDst++;
+ }
+ dst += pitch;
+ heightAmt--;
+ // TODO depth etc.
+ //depthAddress += Destination -> Width;
+
+ errY += 100;
+ if (errY >= 0)
+ status = kStatusSkip;
+ } else if (status == kStatusDraw && clipY < 0) {
+ clipY++;
+
+ errY += 100;
+ if (errY >= 0)
+ status = kStatusSkip;
+ }
+
+ }
+
+ }
+
+ delete[] scaledLineBuf;
+
+}
+
+void MSurface::empty() {
+ Common::fill(getBasePtr(0, 0), getBasePtr(0, h), _vm->_palette->BLACK);
+}
+
+void MSurface::updateScreen() {
+ g_system->copyRectToScreen((const byte *)pixels, pitch, 0, 0, w, h);
+ g_system->updateScreen();
+}
+
+void MSurface::copyFrom(MSurface *src, const Common::Rect &srcBounds,
+ const Common::Point &destPos, int transparentColor) {
+ // Validation of the rectangle and position
+ int destX = destPos.x, destY = destPos.y;
+ if ((destX >= w) || (destY >= h))
+ return;
+
+ Common::Rect copyRect = srcBounds;
+ if (destX < 0) {
+ copyRect.left += -destX;
+ destX = 0;
+ } else if (destX + copyRect.width() > w) {
+ copyRect.right -= destX + copyRect.width() - w;
+ }
+ if (destY < 0) {
+ copyRect.top += -destY;
+ destY = 0;
+ } else if (destY + copyRect.height() > h) {
+ copyRect.bottom -= destY + copyRect.height() - h;
+ }
+
+ if (!copyRect.isValidRect())
+ return;
+
+ // Copy the specified area
+
+ byte *data = src->getData();
+ byte *srcPtr = data + (src->getWidth() * copyRect.top + copyRect.left);
+ byte *destPtr = (byte *)pixels + (destY * getWidth()) + destX;
+
+ for (int rowCtr = 0; rowCtr < copyRect.height(); ++rowCtr) {
+ if (transparentColor == -1)
+ // No transparency, so copy line over
+ Common::copy(srcPtr, srcPtr + copyRect.width(), destPtr);
+ else {
+ // Copy each byte one at a time checking for the transparency color
+ for (int xCtr = 0; xCtr < copyRect.width(); ++xCtr)
+ if (srcPtr[xCtr] != transparentColor) destPtr[xCtr] = srcPtr[xCtr];
+ }
+
+ srcPtr += src->getWidth();
+ destPtr += getWidth();
+ }
+}
+
+void MSurface::translate(Common::Array<RGB6> &palette) {
+ for (int y = 0; y < this->h; ++y) {
+ byte *pDest = getBasePtr(0, y);
+
+ for (int x = 0; x < this->w; ++x, ++pDest) {
+ *pDest = palette[*pDest].palIndex;
+ }
+ }
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/msurface.h b/engines/mads/msurface.h
new file mode 100644
index 0000000000..7f4cec3c1c
--- /dev/null
+++ b/engines/mads/msurface.h
@@ -0,0 +1,179 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_MSURFACE_H
+#define MADS_MSURFACE_H
+
+#include "common/scummsys.h"
+#include "common/rect.h"
+#include "graphics/surface.h"
+#include "mads/palette.h"
+
+namespace MADS {
+
+class MADSEngine;
+class MSprite;
+
+/**
+ * Basic sprite information
+ */
+struct SpriteInfo {
+ MSprite *sprite;
+ int hotX, hotY;
+ int width, height;
+ int scaleX, scaleY;
+ uint8 encoding;
+ byte *inverseColorTable;
+ byte *palette;
+};
+
+/*
+ * MADS graphics surface
+ */
+class MSurface : public Graphics::Surface {
+private:
+ static MADSEngine *_vm;
+public:
+ /**
+ * Sets the engine refrence used all surfaces
+ */
+ static void setVm(MADSEngine *vm) { _vm = vm; }
+
+ /**
+ * Helper method for calculating new dimensions when scaling a sprite
+ */
+ static int scaleValue(int value, int scale, int err);
+public:
+ /**
+ * Basic constructor
+ */
+ MSurface();
+
+ /**
+ * Constructor for a surface with fixed dimensions
+ */
+ MSurface(int width, int height);
+
+ /**
+ * Destructor
+ */
+ virtual ~MSurface();
+
+ /**
+ * Reinitialises a surface to have a given set of dimensions
+ */
+ void setSize(int width, int height);
+
+ /**
+ * Draws an arbitrary line on the screen using a specified color
+ * @param startPos Starting position
+ * @param endPos Ending position
+ * @param color Color to use
+ */
+ void line(const Common::Point &startPos, const Common::Point &endPos, byte color);
+
+ /**
+ * Draws a sprite
+ * @param pt Position to draw sprite at
+ * @param info General sprite details
+ * @param clipRect Clipping rectangle to constrain sprite drawing within
+ */
+ void drawSprite(const Common::Point &pt, SpriteInfo &info, const Common::Rect &clipRect);
+
+ /**
+ * Returns the width of the surface
+ */
+ int getWidth() const { return w; }
+
+ /**
+ * Returns the height of the surface
+ */
+ int getHeight() const { return h; }
+
+ /**
+ * Returns the size of the surface as a Rect
+ */
+ Common::Rect getBounds() const {
+ return Common::Rect(0, 0, w, h);
+ }
+
+ /**
+ * Returns a pointer to the surface data
+ */
+ byte *getData() { return (byte *)Graphics::Surface::getPixels(); }
+
+ /**
+ * Returns a pointer to a given position within the surface
+ */
+ byte *getBasePtr(int x, int y) { return (byte *)Graphics::Surface::getBasePtr(x, y); }
+
+ /**
+ * Clears the surface
+ */
+ void empty();
+
+ /**
+ * Updates the screen with the contents of the surface
+ */
+ void updateScreen();
+
+ /**
+ * Copys a sub-section of another surface into the current one.
+ * @param src Source surface
+ * @param srcBounds Area of source surface to copy
+ * @param destPos Destination position to draw in current surface
+ * @param transparentColor Transparency palette index
+ */
+ void copyFrom(MSurface *src, const Common::Rect &srcBounds, const Common::Point &destPos,
+ int transparentColor = -1);
+
+ /**
+ * Copies the surface to a given destination surface
+ */
+ void copyTo(MSurface *dest, int transparentColor = -1) {
+ dest->copyFrom(this, Common::Rect(w, h), Common::Point(), transparentColor);
+ }
+
+ /**
+ * Copies the surface to a given destination surface
+ */
+ void copyTo(MSurface *dest, const Common::Point &pt, int transparentColor = -1) {
+ dest->copyFrom(this, Common::Rect(w, h), pt, transparentColor);
+ }
+
+ /**
+ * Copies the surface to a given destination surface
+ */
+ void copyTo(MSurface *dest, const Common::Rect &srcBounds, const Common::Point &destPos,
+ int transparentColor = -1) {
+ dest->copyFrom(this, srcBounds, destPos, transparentColor);
+ }
+
+ /**
+ * Translates the pixels of an image used the passed palette with RGB mapping
+ */
+ void translate(Common::Array<RGB6> &palette);
+};
+
+} // End of namespace MADS
+
+#endif /* MADS_MSURFACE_H */
diff --git a/engines/mads/nebular/dialogs_nebular.cpp b/engines/mads/nebular/dialogs_nebular.cpp
new file mode 100644
index 0000000000..6d2321eae2
--- /dev/null
+++ b/engines/mads/nebular/dialogs_nebular.cpp
@@ -0,0 +1,118 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "common/config-manager.h"
+#include "mads/mads.h"
+#include "mads/graphics.h"
+#include "mads/msurface.h"
+#include "mads/nebular/dialogs_nebular.h"
+
+namespace MADS {
+
+namespace Nebular {
+
+CopyProtectionDialog::CopyProtectionDialog(MADSEngine *vm, bool priorAnswerWrong):
+ TextDialog(vm, FONT_INTERFACE, Common::Point(-1, -1), 32) {
+ getHogAnusEntry(_hogEntry);
+
+ if (priorAnswerWrong) {
+ addLine("ANSWER INCORRECT!", true);
+ wordWrap("\n");
+ addLine("(But we'll give you another chance!)");
+ } else {
+ addLine("REX NEBULAR version 8.43", true);
+ wordWrap("\n");
+ addLine("(Copy Protection, for your convenience)");
+ }
+ wordWrap("\n");
+
+ wordWrap("Now comes the part that everybody hates. But if we don't");
+ wordWrap("do this, nasty rodent-like people will pirate this game");
+ wordWrap("and a whole generation of talented designers, programmers,");
+ wordWrap("artists, and playtesters will go hungry, and will wander");
+ wordWrap("aimlessly through the land at night searching for peace.");
+ wordWrap("So let's grit our teeth and get it over with. Just get");
+
+ Common::String line = "out your copy of ";
+ line += _hogEntry._bookId == 103 ? "the GAME MANUAL" : "REX'S LOGBOOK";
+ line += ". See! That was easy. ";
+ wordWrap(line);
+
+ line = Common::String::format("Next, just turn to page %d. On line %d, find word number %d, ",
+ _hogEntry._pageNum, _hogEntry._lineNum, _hogEntry._wordNum);
+ wordWrap(line);
+
+ wordWrap("and type it on the line below (we',27h,'ve even given you");
+ wordWrap("first letter as a hint). As soon as you do that, we can get");
+ wordWrap("right into this really COOL adventure game!\n");
+ wordWrap("\n");
+ wordWrap(" ");
+ addInput();
+ wordWrap("\n");
+}
+
+bool CopyProtectionDialog::show() {
+ draw();
+ _vm->_events->showCursor();
+
+ // TODO: Replace with text input
+ while (!_vm->shouldQuit() && !_vm->_events->_keyPressed &&
+ !_vm->_events->_mouseClicked) {
+ _vm->_events->delay(1);
+ }
+
+ return true;
+}
+
+bool CopyProtectionDialog::getHogAnusEntry(HOGANUS &entry) {
+ File f;
+ f.open("*HOGANUS.DAT");
+
+ // Read in the total number of entries, and randomly pick an entry to use
+ int numEntries = f.readUint16LE();
+ int entryIndex = _vm->getRandomNumber(numEntries - 2) + 1;
+
+ // Read in the encrypted entry
+ f.seek(28 * entryIndex + 2);
+ byte entryData[28];
+ f.read(entryData, 28);
+
+ // Decrypt it
+ for (int i = 0; i < 28; ++i)
+ entryData[i] = ~entryData[i];
+
+ // Fill out the fields
+ entry._bookId = entryData[0];
+ entry._pageNum = READ_LE_UINT16(&entryData[2]);
+ entry._lineNum = READ_LE_UINT16(&entryData[4]);
+ entry._wordNum = READ_LE_UINT16(&entryData[6]);
+ entry._word = Common::String((char *)&entryData[8]);
+
+ f.close();
+ return true;
+}
+
+
+} // End of namespace Nebular
+
+} // End of namespace MADS
diff --git a/engines/mads/nebular/dialogs_nebular.h b/engines/mads/nebular/dialogs_nebular.h
new file mode 100644
index 0000000000..d4e4fe921e
--- /dev/null
+++ b/engines/mads/nebular/dialogs_nebular.h
@@ -0,0 +1,76 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_DIALOGS_NEBULAR_H
+#define MADS_DIALOGS_NEBULAR_H
+
+#include "common/scummsys.h"
+#include "mads/game.h"
+#include "mads/dialogs.h"
+
+namespace MADS {
+
+namespace Nebular {
+
+class DialogsNebular: public Dialogs {
+ friend class Dialogs;
+protected:
+ DialogsNebular(MADSEngine *vm): Dialogs(vm) {}
+public:
+ virtual void showDialog() {
+ warning("TODO: showDialog");
+ }
+};
+
+struct HOGANUS {
+ int _bookId;
+ int _pageNum;
+ int _lineNum;
+ int _wordNum;
+ Common::String _word;
+};
+
+class CopyProtectionDialog: public TextDialog {
+private:
+ HOGANUS _hogEntry;
+
+ /**
+ * Get a random copy protection entry from the HOGANUS resource
+ */
+ bool getHogAnusEntry(HOGANUS &entry);
+public:
+ /**
+ * Constructor
+ */
+ CopyProtectionDialog(MADSEngine *vm, bool priorAnswerWrong);
+
+ /**
+ * Show the dialog
+ */
+ bool show();
+};
+
+} // End of namespace Nebular
+
+} // End of namespace MADS
+
+#endif /* MADS_DIALOGS_NEBULAR_H */
diff --git a/engines/mads/nebular/game_nebular.cpp b/engines/mads/nebular/game_nebular.cpp
new file mode 100644
index 0000000000..c55abf5601
--- /dev/null
+++ b/engines/mads/nebular/game_nebular.cpp
@@ -0,0 +1,190 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "common/config-manager.h"
+#include "mads/mads.h"
+#include "mads/game.h"
+#include "mads/graphics.h"
+#include "mads/msurface.h"
+#include "mads/nebular/game_nebular.h"
+#include "mads/nebular/dialogs_nebular.h"
+
+namespace MADS {
+
+namespace Nebular {
+
+GameNebular::GameNebular(MADSEngine *vm): Game(vm) {
+ _surface = new MSurface(MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT - MADS_INTERFACE_HEIGHT);
+}
+
+int GameNebular::checkCopyProtection() {
+ if (!ConfMan.getBool("copy_protection"))
+ return true;
+
+ /* DEBUG: Disabled for now
+ CopyProtectionDialog *dlg = new CopyProtectionDialog(_vm, false);
+ dlg->show();
+ delete dlg;
+ */
+
+ // DEBUG: Return that copy protection failed
+ return 1;
+}
+
+void GameNebular::initialiseGlobals() {
+ // Allocate globals space
+ _globalFlags.resize(210);
+ for (int i = 0; i < 210; ++i)
+ _globalFlags[i] = 0;
+
+ // Set specific values needed by the game
+ _globalFlags[4] = 8;
+ _globalFlags[33] = 1;
+ _globalFlags[10] = 0xFFFF;
+ _globalFlags[13] = 0xFFFF;
+ _globalFlags[15] = 0xFFFF;
+ _globalFlags[19] = 0xFFFF;
+ _globalFlags[20] = 0xFFFF;
+ _globalFlags[21] = 0xFFFF;
+ _globalFlags[95] = 0xFFFF;
+
+ _objects.setData(3, 17, nullptr);
+
+ // Put the values 0 through 3 in a random order in global slots 83 to 86
+ for (int i = 0; i < 4;) {
+ int randomVal = _vm->getRandomNumber(3);
+ _globalFlags[83 + i] = randomVal;
+
+ bool flag = false;
+ for (int idx2 = 0; idx2 < i; ++idx2) {
+ if (_globalFlags[83 + idx2] == randomVal)
+ flag = true;
+ }
+
+ if (!flag)
+ ++i;
+ }
+
+ // Put the values 0 through 3 in a random order in global slots 87 to 90
+ for (int i = 0; i < 4;) {
+ int randomVal = _vm->getRandomNumber(3);
+ _globalFlags[87 + i] = randomVal;
+
+ bool flag = false;
+ for (int idx2 = 0; idx2 < i; ++idx2) {
+ if (_globalFlags[87 + idx2] == randomVal)
+ flag = true;
+ }
+
+ if (!flag)
+ ++i;
+ }
+
+ _globalFlags[120] = 501;
+ _globalFlags[121] = 0xFFFF;
+ _globalFlags[55] = 0xFFFF;
+ _globalFlags[119] = 1;
+ _globalFlags[134] = 4;
+
+ // Fill out the globals 200 to 209 with unique random values less than 10000
+ for (int i = 0; i < 10; ++i) {
+ int randomVal = _vm->getRandomNumber(9999);
+ _globalFlags[200 + i] = randomVal;
+
+ bool flag = false;
+ for (int idx2 = 0; idx2 < i; ++idx2) {
+ if (_globalFlags[200 + idx2] == randomVal)
+ flag = true;
+ }
+
+ if (!flag)
+ ++i;
+ }
+
+ // Difficulty level control
+ switch (_difficultyLevel) {
+ case DIFFICULTY_HARD:
+ _globalFlags[35] = 0;
+ _objects.setRoom(9, 1);
+ _objects.setRoom(50, 1);
+ _globalFlags[137] = 5;
+ _globalFlags[136] = 0;
+ break;
+ case DIFFICULTY_MEDIUM:
+ _globalFlags[35] = 0;
+ _objects.setRoom(8, 1);
+ _globalFlags[137] = 0xFFFF;
+ _globalFlags[136] = 6;
+ break;
+ case DIFFICULTY_EASY:
+ _globalFlags[35] = 2;
+ _objects.setRoom(8, 1);
+ _objects.setRoom(27, 1);
+ break;
+ default:
+ break;
+ }
+
+ _player._direction = 8;
+ _player._newDirection = 8;
+
+ loadResourceSequence("RXM", 1);
+ loadResourceSequence("ROX", 1);
+}
+
+void GameNebular::setSectionHandler() {
+ delete _sectionHandler;
+
+ switch (_sectionNumber) {
+ case 1:
+ _sectionHandler = new Section1Handler(_vm);
+ break;
+ case 2:
+ _sectionHandler = new Section2Handler(_vm);
+ break;
+ case 3:
+ _sectionHandler = new Section3Handler(_vm);
+ break;
+ case 4:
+ _sectionHandler = new Section4Handler(_vm);
+ break;
+ case 5:
+ _sectionHandler = new Section5Handler(_vm);
+ break;
+ case 6:
+ _sectionHandler = new Section6Handler(_vm);
+ break;
+ case 7:
+ _sectionHandler = new Section7Handler(_vm);
+ break;
+ case 8:
+ _sectionHandler = new Section8Handler(_vm);
+ break;
+ default:
+ break;
+ }
+}
+
+} // End of namespace Nebular
+
+} // End of namespace MADS
diff --git a/engines/mads/nebular/game_nebular.h b/engines/mads/nebular/game_nebular.h
new file mode 100644
index 0000000000..6395ba0e4e
--- /dev/null
+++ b/engines/mads/nebular/game_nebular.h
@@ -0,0 +1,69 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_GAME_NEBULAR_H
+#define MADS_GAME_NEBULAR_H
+
+#include "common/scummsys.h"
+#include "mads/game.h"
+
+namespace MADS {
+
+namespace Nebular {
+
+class GameNebular: public Game {
+ friend class Game;
+protected:
+ GameNebular(MADSEngine *vm);
+
+ virtual int checkCopyProtection();
+
+ virtual void initialiseGlobals();
+
+ virtual void setSectionHandler();
+};
+
+
+class Section1Handler: public SectionHandler {
+public:
+ Section1Handler(MADSEngine *vm): SectionHandler(vm) {}
+
+ // TODO: Properly implement handler methods
+ virtual void preLoadSection() {}
+ virtual void sectionPtr2() {}
+ virtual void postLoadSection() {}
+};
+
+// TODO: Properly implement handler classes
+typedef Section1Handler Section2Handler;
+typedef Section1Handler Section3Handler;
+typedef Section1Handler Section4Handler;
+typedef Section1Handler Section5Handler;
+typedef Section1Handler Section6Handler;
+typedef Section1Handler Section7Handler;
+typedef Section1Handler Section8Handler;
+
+} // End of namespace Nebular
+
+} // End of namespace MADS
+
+#endif /* MADS_GAME_NEBULAR_H */
diff --git a/engines/mads/nebular/nebular_scenes.cpp b/engines/mads/nebular/nebular_scenes.cpp
new file mode 100644
index 0000000000..efd831a4b7
--- /dev/null
+++ b/engines/mads/nebular/nebular_scenes.cpp
@@ -0,0 +1,49 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "common/config-manager.h"
+#include "mads/mads.h"
+#include "mads/scene.h"
+#include "mads/nebular/nebular_scenes.h"
+#include "mads/nebular/nebular_scenes8.h"
+
+namespace MADS {
+
+namespace Nebular {
+
+SceneLogic *SceneFactory::createScene(Scene *scene) {
+ scene->addActiveVocab(NOUN_DROP);
+ scene->addActiveVocab(NOUN_DOLLOP);
+ scene->addActiveVocab(NOUN_DASH);
+ scene->addActiveVocab(NOUN_SPLASH);
+ scene->addActiveVocab(NOUN_ALCOHOL);
+
+ // TODO: Implement all the game scenes
+ assert(scene->_nextSceneId == 804);
+
+ return new Scene804(scene);
+}
+
+} // End of namespace Nebular
+
+} // End of namespace MADS
diff --git a/engines/mads/nebular/nebular_scenes.h b/engines/mads/nebular/nebular_scenes.h
new file mode 100644
index 0000000000..28d24f090f
--- /dev/null
+++ b/engines/mads/nebular/nebular_scenes.h
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_NEBULAR_SCENES_H
+#define MADS_NEBULAR_SCENES_H
+
+#include "common/scummsys.h"
+#include "mads/game.h"
+#include "mads/scene.h"
+
+namespace MADS {
+
+namespace Nebular {
+
+enum Noun {
+ NOUN_BLOWGUN = 0x29,
+ NOUN_BURGER = 0x35,
+ NOUN_CHAIR = 0x47,
+ NOUN_DEAD_FISH = 0x65,
+ NOUN_DOOR = 0x6E,
+ NOUN_EAT = 0x75,
+ NOUN_EXAMINE = 0x7D,
+ NOUN_FRONT_WINDOW = 0x8E,
+ NOUN_FUZZY_DICE = 0x91,
+ NOUN_HOSE_DOWN = 0x0A6,
+ NOUN_HOTPANTS = 0x0A7,
+ NOUN_HULL = 0x0A8,
+ NOUN_HURL = 0x0A9,
+ NOUN_IGNITE = 0x0B4,
+ NOUN_INFLATE = 0x0B5,
+ NOUN_INSERT = 0x0B6,
+ NOUN_INSPECT = 0x0B7,
+ NOUN_JUNGLE = 0x0B8,
+ NOUN_LIFE_SUPPORT_SECTION = 0x0CC,
+ NOUN_LOG = 0x0D0,
+ NOUN_LOOK_AT = 0x0D1,
+ NOUN_LOOK_IN = 0x0D2,
+ NOUN_LOOK_THROUGH = 0x0D3,
+ NOUN_MONKEY = 0x0E3,
+ NOUN_OUTER_HULL = 0x0F8,
+ NOUN_OUTSIDE = 0x0F9,
+ NOUN_PEER_THROUGH = 0x103,
+ NOUN_PLANT_STALK = 0x10F,
+ NOUN_READ = 0x11F,
+ NOUN_REFRIDGERATOR = 0x122,
+ NOUN_ROBO_KITCHEN = 0x127,
+ NOUN_SHIELD_ACCESS_PANEL = 0x135,
+ NOUN_SHIELD_MODULATOR = 0x137,
+ NOUN_SHOOT = 0x13A,
+ NOUN_SIT_IN = 0x13F,
+ NOUN_SMELL = 0x147,
+ NOUN_STUFFED_FISH = 0x157,
+ NOUN_VIEW_SCREEN = 0x180,
+ NOUN_CAPTIVE_CREATURE = 0x1C3,
+ NOUN_NATIVE_WOMAN = 0x1DC,
+ NOUN_ALCOHOL = 0x310,
+ NOUN_DOLLOP = 0x3AC,
+ NOUN_DROP = 0x3AD,
+ NOUN_DASH = 0x3AE,
+ NOUN_SPLASH = 0x3AF
+};
+
+class SceneFactory {
+public:
+ static SceneLogic *createScene(Scene *scene);
+};
+
+} // End of namespace Nebular
+
+} // End of namespace MADS
+
+#endif /* MADS_NEBULAR_SCENES_H */
diff --git a/engines/mads/nebular/nebular_scenes8.cpp b/engines/mads/nebular/nebular_scenes8.cpp
new file mode 100644
index 0000000000..8feabc8037
--- /dev/null
+++ b/engines/mads/nebular/nebular_scenes8.cpp
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "mads/nebular/nebular_scenes8.h"
+
+namespace MADS {
+
+namespace Nebular {
+
+void Scene804::setup() {
+}
+
+void Scene804::enter() {
+}
+
+void Scene804::step() {
+}
+
+void Scene804::preActions() {
+}
+
+void Scene804::actions() {
+}
+
+void Scene804::postActions() {
+}
+
+} // End of namespace Nebular
+
+} // End of namespace MADS
diff --git a/engines/mads/nebular/nebular_scenes8.h b/engines/mads/nebular/nebular_scenes8.h
new file mode 100644
index 0000000000..11bb8b0d81
--- /dev/null
+++ b/engines/mads/nebular/nebular_scenes8.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.
+ *
+ */
+
+#ifndef MADS_NEBULAR_SCENES8_H
+#define MADS_NEBULAR_SCENES8_H
+
+#include "common/scummsys.h"
+#include "mads/game.h"
+#include "mads/scene.h"
+
+namespace MADS {
+
+namespace Nebular {
+
+class Scene804: public SceneLogic {
+public:
+ Scene804(Scene *scene): SceneLogic(scene) {}
+
+ virtual void setup();
+
+ virtual void enter();
+
+ virtual void step();
+
+ virtual void preActions();
+
+ virtual void actions();
+
+ virtual void postActions();
+};
+
+} // End of namespace Nebular
+
+} // End of namespace MADS
+
+#endif /* MADS_NEBULAR_SCENES8_H */
diff --git a/engines/mads/nebular/sound_nebular.cpp b/engines/mads/nebular/sound_nebular.cpp
new file mode 100644
index 0000000000..5f1d80513e
--- /dev/null
+++ b/engines/mads/nebular/sound_nebular.cpp
@@ -0,0 +1,1207 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "audio/audiostream.h"
+#include "audio/decoders/raw.h"
+#include "common/algorithm.h"
+#include "common/debug.h"
+#include "common/memstream.h"
+#include "mads/sound.h"
+#include "mads/nebular/sound_nebular.h"
+
+namespace MADS {
+
+namespace Nebular {
+
+AdlibChannel::AdlibChannel() {
+ _activeCount = 0;
+ _field1 = 0;
+ _field2 = 0;
+ _field3 = 0;
+ _field4 = 0;
+ _sampleIndex = 0;
+ _volume = 0;
+ _field7 = 0;
+ _field8 = 0;
+ _field9 = 0;
+ _fieldA = 0;
+ _fieldB = 0;
+ _fieldC = 0;
+ _fieldD = 0;
+ _fieldE = 0;
+ _ptr1 = nullptr;
+ _pSrc = nullptr;
+ _ptr3 = nullptr;
+ _ptr4 = nullptr;
+ _field17 = 0;
+ _field19 = 0;
+ _soundData = nullptr;
+ _field1D = 0;
+ _field1E = 0;
+ _field1F = 0;
+}
+
+void AdlibChannel::reset() {
+ _activeCount = 0;
+ _field1 = 0;
+ _field2 = 0;
+ _field3 = 0;
+}
+
+void AdlibChannel::enable(int flag) {
+ if (_activeCount) {
+ _fieldE = flag;
+
+ // WORKAROUND: Original set _soundData pointer to flag. Since this seems
+ // just intended to invalidate any prior pointer, I've replaced it with
+ // a simple null pointer
+ _soundData = nullptr;
+ }
+}
+
+void AdlibChannel::setPtr2(byte *pData) {
+ _pSrc = pData;
+ _field2 = 0xFF;
+ _fieldA = 1;
+ _field9 = 1;
+}
+
+void AdlibChannel::load(byte *pData) {
+ _ptr1 = _pSrc = _ptr3 = pData;
+ _ptr4 = _soundData = pData;
+ _fieldA = 0xFF;
+ _activeCount = 1;
+ _fieldD = 64;
+ _field1 = 0;
+ _field1F = 0;
+ _field2 = _field3 = 0;
+ _volume = _field7 = 0;
+ _field1D = _field1E = 0;
+ _fieldE = 0;
+ _field9 = 0;
+ _fieldB = 0;
+ _field17 = 0;
+ _field19 = 0;
+}
+
+void AdlibChannel::check(byte *nullPtr) {
+ if (_activeCount && _fieldE) {
+ if (!_field1E) {
+ _pSrc = nullPtr;
+ _fieldE = 0;
+ } else {
+ _field2 = 0xFF;
+ _fieldA = 4;
+ if (!_field9)
+ _field9 = 1;
+ }
+ }
+}
+
+/*-----------------------------------------------------------------------*/
+
+AdlibSample::AdlibSample(Common::SeekableReadStream &s) {
+ _attackRate = s.readByte();
+ _decayRate = s.readByte();
+ _sustainLevel = s.readByte();
+ _releaseRate = s.readByte();
+ _egTyp = s.readByte() != 0;
+ _ksr = s.readByte() != 0;
+ _totalLevel = s.readByte();
+ _scalingLevel = s.readByte();
+ _waveformSelect = s.readByte();
+ _freqMultiple = s.readByte();
+ _feedback = s.readByte();
+ _ampMod = s.readByte() != 0;
+ _vib = s.readByte();
+ _alg = s.readByte();
+ _fieldE = s.readByte();
+ s.skip(1);
+ _freqMask = s.readUint16LE();
+ _freqBase = s.readUint16LE();
+ _field14 = s.readUint16LE();
+}
+
+/*-----------------------------------------------------------------------*/
+
+ASound::ASound(Audio::Mixer *mixer, const Common::String &filename, int dataOffset) {
+ // Open up the appropriate sound file
+ if (!_soundFile.open(filename))
+ error("Could not open file - %s", filename.c_str());
+
+ // Initialise fields
+ _activeChannelPtr = nullptr;
+ _samplePtr = nullptr;
+ _frameCounter = 0;
+ _isDisabled = false;
+ _v1 = 0;
+ _v2 = 0;
+ _activeChannelNumber = 0;
+ _freqMask1 = _freqMask2 = 0;
+ _freqBase1 = _freqBase2 = 0;
+ _channelNum1 = _channelNum2 = 0;
+ _v7 = 0;
+ _v8 = 0;
+ _v9 = 0;
+ _v10 = 0;
+ _pollResult = 0;
+ _resultFlag = 0;
+ _nullData[0] = _nullData[1] = 0;
+ Common::fill(&_ports[0], &_ports[256], 0);
+ _stateFlag = false;
+ _activeChannelReg = 0;
+ _v11 = 0;
+ _randomSeed = 1234;
+ _amDep = _vibDep = _splitPoint = true;
+
+ _samplesTillCallback = 0;
+ _samplesTillCallbackRemainder = 0;
+ _samplesPerCallback = getRate() / CALLBACKS_PER_SECOND;
+ _samplesPerCallbackRemainder = getRate() % CALLBACKS_PER_SECOND;
+
+ // Store passed parameters, and setup OPL
+ _dataOffset = dataOffset;
+ _mixer = mixer;
+ _opl = OPL::Config::create();
+ assert(_opl);
+
+ _opl->init(getRate());
+ _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1,
+ Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+
+ // Initialise the Adlib
+ adlibInit();
+
+ // Reset the adlib
+ command0();
+}
+
+ASound::~ASound() {
+ Common::List<CachedDataEntry>::iterator i;
+ for (i = _dataCache.begin(); i != _dataCache.end(); ++i)
+ delete[] (*i)._data;
+
+ _mixer->stopHandle(_soundHandle);
+ delete _opl;
+}
+
+void ASound::adlibInit() {
+ write(4, 0x60);
+ write(4, 0x80);
+ write(2, 0xff);
+ write(4, 0x21);
+ write(4, 0x60);
+ write(4, 0x80);
+}
+
+int ASound::stop() {
+ command0();
+ int result = _pollResult;
+ _pollResult = 0;
+ return result;
+}
+
+int ASound::poll() {
+ // Update any playing sounds
+ update();
+
+ // Return result
+ int result = _pollResult;
+ _pollResult = 0;
+ return result;
+}
+
+void ASound::noise() {
+ int randomVal = getRandomNumber();
+
+ if (_v1) {
+ setFrequency(_channelNum1, ((randomVal ^ 0xFFFF) & _freqMask1) + _freqBase1);
+ }
+
+ if (_v2) {
+ setFrequency(_channelNum2, (randomVal & _freqMask2) + _freqBase2);
+ }
+}
+
+void ASound::write(int reg, int val) {
+ _queue.push(RegisterValue(reg, val));
+}
+
+int ASound::write2(int state, int reg, int val) {
+ // TODO: Original has a state parameter, not used when in Adlib mode?
+ _ports[reg] = val;
+ write(reg, val);
+ return state;
+}
+
+void ASound::flush() {
+ Common::StackLock slock(_driverMutex);
+
+ while (!_queue.empty()) {
+ RegisterValue v = _queue.pop();
+ _opl->writeReg(v._regNum, v._value);
+ }
+}
+
+void ASound::channelOn(int reg, int volume) {
+ write2(8, reg, (_ports[reg] & 0xC0) | (volume & 0x3F));
+}
+
+void ASound::channelOff(int reg) {
+ write2(8, reg, _ports[reg] | 0x3F);
+}
+
+void ASound::resultCheck() {
+ if (_resultFlag != 1) {
+ _resultFlag = 1;
+ _pollResult = 1;
+ }
+}
+
+byte *ASound::loadData(int offset, int size) {
+ // First scan for an existing copy
+ Common::List<CachedDataEntry>::iterator i;
+ for (i = _dataCache.begin(); i != _dataCache.end(); ++i) {
+ CachedDataEntry &e = *i;
+ if (e._offset == offset)
+ return e._data;
+ }
+
+ // No existing entry found, so load up data and store as a new entry
+ CachedDataEntry rec;
+ rec._offset = offset;
+ rec._data = new byte[size];
+ _soundFile.seek(_dataOffset + offset);
+ _soundFile.read(rec._data, size);
+ _dataCache.push_back(rec);
+
+ // Return the data
+ return rec._data;
+}
+
+void ASound::playSound(int offset, int size) {
+ // Load the specified data block
+ playSoundData(loadData(offset, size));
+}
+
+void ASound::playSoundData(byte *pData, int startingChannel) {
+ // Scan for a high level free channel
+ for (int i = startingChannel; i < ADLIB_CHANNEL_COUNT; ++i) {
+ if (!_channels[i]._activeCount) {
+ _channels[i].load(pData);
+ return;
+ }
+ }
+
+ // None found, do a secondary scan for an interruptable channel
+ for (int i = ADLIB_CHANNEL_COUNT - 1; i >= startingChannel; --i) {
+ if (_channels[i]._fieldE == 0xFF) {
+ _channels[i].load(pData);
+ return;
+ }
+ }
+}
+
+bool ASound::isSoundActive(byte *pData) {
+ for (int i = 0; i < ADLIB_CHANNEL_MIDWAY; ++i) {
+ if (_channels[i]._activeCount && _channels[i]._soundData == pData)
+ return true;
+ }
+
+ return false;
+}
+
+void ASound::setFrequency(int channel, int freq) {
+ write2(8, 0xA0 + channel, freq & 0xFF);
+ write2(8, 0xB0 + channel, (freq >> 8) | 0x20);
+}
+
+int ASound::getRandomNumber() {
+ int v = 0x9248 + (int)_randomSeed;
+ _randomSeed = ((v >> 3) | (v << 13)) & 0xFFFF;
+ return _randomSeed;
+}
+
+void ASound::update() {
+ getRandomNumber();
+ if (_isDisabled)
+ return;
+
+ ++_frameCounter;
+ pollChannels();
+ checkChannels();
+
+ if (_v1 == _v2) {
+ if (_resultFlag != -1) {
+ _resultFlag = -1;
+ _pollResult = -1;
+ }
+ } else {
+ if (_v1) {
+ _freqBase1 += _v7;
+ if (!--_v1) {
+ if (!_v2 || _channelNum1 != _channelNum2) {
+ write2(8, 0xA0 + _channelNum1, 0);
+ write2(8, 0xB0 + _channelNum1, 0);
+ }
+ }
+ }
+
+ if (_v2) {
+ _freqBase2 += _v8;
+ if (!--_v2) {
+ if (!_v1 || _channelNum2 != _channelNum1) {
+ write2(8, 0xA0 + _channelNum2, 0);
+ write2(8, 0xB0 + _channelNum2, 0);
+ }
+ }
+ }
+ }
+}
+
+void ASound::pollChannels() {
+ _activeChannelNumber = 0;
+ for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i) {
+ _activeChannelPtr = &_channels[i];
+ pollActiveChannel();
+ }
+}
+
+void ASound::checkChannels() {
+ for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
+ _channels[i].check(_nullData);
+}
+
+void ASound::pollActiveChannel() {
+ AdlibChannel *chan = _activeChannelPtr;
+ bool updateFlag = true;
+
+ if (chan->_activeCount) {
+ if (chan->_field8 > 0 && --chan->_field8 == 0)
+ updateOctave();
+
+ if (--_activeChannelPtr->_activeCount <= 0) {
+ for (;;) {
+ byte *pSrc = chan->_pSrc;
+ if (!(*pSrc & 0x80) || (*pSrc <= 0xF0)) {
+ if (updateFlag)
+ updateActiveChannel();
+
+ chan->_field4 = *pSrc++;
+ chan->_activeCount = *pSrc++;
+ chan->_pSrc += 2;
+
+ if (!chan->_field4 || !chan->_activeCount) {
+ updateOctave();
+ } else {
+ chan->_field8 = chan->_activeCount - chan->_field7;
+ updateChannelState();
+ }
+
+ // Break out of processing loop
+ break;
+ } else {
+ updateFlag = false;
+
+ switch ((~*pSrc) & 0xF) {
+ case 0:
+ if (!chan->_field17) {
+ if (*++pSrc == 0) {
+ chan->_pSrc += 2;
+ chan->_ptr3 = chan->_pSrc;
+ chan->_field17 = 0;
+ } else {
+ chan->_field17 = *pSrc;
+ chan->_pSrc = chan->_ptr3;
+ }
+ } else if (--chan->_field17) {
+ chan->_pSrc = chan->_ptr3;
+ } else {
+ chan->_pSrc += 2;
+ chan->_ptr3 = chan->_pSrc;
+ }
+ break;
+
+ case 1:
+ if (!chan->_field19) {
+ if (*++pSrc == 0) {
+ chan->_pSrc += 2;
+ chan->_ptr4 = chan->_pSrc;
+ chan->_ptr3 = chan->_pSrc;
+ chan->_field17 = 0;
+ chan->_field19 = 0;
+ } else {
+ chan->_field19 = *pSrc;
+ chan->_pSrc = chan->_ptr4;
+ chan->_ptr3 = chan->_ptr4;
+ }
+ } else if (--chan->_field19) {
+ chan->_ptr4 = chan->_pSrc;
+ chan->_ptr3 = chan->_pSrc;
+ } else {
+ chan->_pSrc += 2;
+ chan->_ptr4 = chan->_pSrc;
+ chan->_ptr3 = chan->_pSrc;
+ }
+ break;
+
+ case 2:
+ // Loop sound data
+ chan->_field1 = 0;
+ chan->_field2 = chan->_field3 = 0;
+ chan->_volume = chan->_field7 = 0;
+ chan->_field1D = chan->_field1E = 0;
+ chan->_field8 = 0;
+ chan->_field9 = 0;
+ chan->_fieldB = 0;
+ chan->_field17 = 0;
+ chan->_field19 = 0;
+ chan->_fieldD = 0x40;
+ chan->_ptr1 = chan->_soundData;
+ chan->_pSrc = chan->_soundData;
+ chan->_ptr3 = chan->_soundData;
+ chan->_ptr4 = chan->_soundData;
+
+ chan->_pSrc += 2;
+ break;
+
+ case 3:
+ chan->_sampleIndex = *++pSrc;
+ chan->_pSrc += 2;
+ loadSample(chan->_sampleIndex);
+ break;
+
+ case 4:
+ chan->_field7 = *++pSrc;
+ chan->_pSrc += 2;
+ break;
+
+ case 5:
+ chan->_field1 = *++pSrc;
+ chan->_pSrc += 2;
+ break;
+
+ case 6:
+ ++pSrc;
+ if (chan->_fieldE) {
+ chan->_pSrc += 2;
+ } else {
+ chan->_volume = *pSrc >> 1;
+ updateFlag = true;
+ chan->_pSrc += 2;
+ }
+ break;
+
+ case 7:
+ ++pSrc;
+ if (!chan->_fieldE) {
+ chan->_fieldA = *pSrc;
+ chan->_field2 = *++pSrc;
+ chan->_field9 = 1;
+ }
+
+ chan->_pSrc += 3;
+ break;
+
+ case 8:
+ chan->_field1D = *++pSrc;
+ chan->_pSrc += 2;
+ break;
+
+ case 9: {
+ int v1 = *++pSrc;
+ ++pSrc;
+ int v2 = (v1 - 1) & getRandomNumber();
+ int v3 = pSrc[v2];
+ int v4 = pSrc[v1];
+
+ pSrc[v4 + v1 + 1] = v3;
+ chan->_pSrc += v1 + 3;
+ break;
+ }
+
+ case 10:
+ ++pSrc;
+ if (chan->_fieldE) {
+ chan->_pSrc += 2;
+ } else {
+ chan->_field1E = *pSrc >> 1;
+ updateFlag = true;
+ chan->_pSrc += 2;
+ }
+ break;
+
+ case 11:
+ chan->_fieldD = *++pSrc;
+ updateFlag = true;
+ chan->_pSrc += 2;
+ break;
+
+ case 12:
+ chan->_fieldC = *++pSrc;
+ chan->_field3 = *++pSrc;
+ chan->_fieldB = 1;
+ chan->_pSrc += 2;
+ break;
+
+ case 13:
+ ++pSrc;
+ chan->_pSrc += 2;
+ break;
+
+ case 14:
+ chan->_field1F = *++pSrc;
+ chan->_pSrc += 2;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ if (chan->_field1)
+ updateFNumber();
+
+ updateFlag = false;
+ if (chan->_field9 || chan->_fieldB) {
+ if (!--chan->_field9) {
+ chan->_field9 = chan->_fieldA;
+ if (chan->_field2) {
+ int8 newVal = (int8)chan->_field2 + (int8)chan->_field1E;
+ if (newVal < 0) {
+ chan->_field9 = 0;
+ newVal = 0;
+ } else if (newVal > 63) {
+ chan->_field9 = 0;
+ newVal = 63;
+ }
+
+ chan->_field1E = newVal;
+ updateFlag = true;
+ }
+ }
+
+ if (!--chan->_fieldB) {
+ chan->_fieldB = chan->_fieldC;
+ if (chan->_field3) {
+ chan->_fieldD = chan->_field3;
+ updateFlag = true;
+ }
+ }
+
+ if (updateFlag)
+ updateActiveChannel();
+ }
+ }
+
+ ++_activeChannelNumber;
+}
+
+void ASound::updateOctave() {
+ int reg = 0xB0 + _activeChannelNumber;
+ write2(8, reg, _ports[reg] & 0xDF);
+}
+
+static int _vList1[] = {
+ 0x200, 0x21E, 0x23F, 0x261, 0x285, 0x2AB,
+ 0x2D4, 0x2FF, 0x32D, 0x35D, 0x390, 0x3C7
+};
+
+void ASound::updateChannelState() {
+ updateActiveChannel();
+
+ if (_channelData[_activeChannelNumber]._field0) {
+ if (_channelNum1 == _activeChannelNumber)
+ _stateFlag = 0;
+ if (_channelNum2 == _activeChannelNumber)
+ _stateFlag = 1;
+
+ if (!_stateFlag) {
+ _stateFlag = 1;
+ if (_v1)
+ write2(8, 0xB0 + _channelNum1, _ports[0xB0 + _channelNum1] & 0xDF);
+
+ _channelNum1 = _activeChannelNumber;
+ _v1 = _channelData[_channelNum1]._field0;
+ _freqMask1 = _channelData[_channelNum1]._freqMask;
+ _freqBase1 = _channelData[_channelNum1]._freqBase;
+ _v7 = _channelData[_channelNum1]._field6;
+ } else {
+ _stateFlag = 0;
+ if (_v2)
+ write2(8, 0xB0 + _channelNum2, _ports[0xB0 + _channelNum2] & 0xDF);
+
+ _channelNum2 = _activeChannelNumber;
+ _v2 = _channelData[_channelNum2]._field0;
+ _freqMask2 = _channelData[_channelNum2]._freqMask;
+ _freqBase2 = _channelData[_channelNum2]._freqBase;
+ _v8 = _channelData[_channelNum2]._field6;
+ }
+
+ resultCheck();
+ } else {
+ int reg = 0xA0 + _activeChannelNumber;
+ int vTimes = (_activeChannelPtr->_field4 + _activeChannelPtr->_field1F) / 12;
+ int vOffset = (_activeChannelPtr->_field4 + _activeChannelPtr->_field1F) % 12;
+ int val = _vList1[vOffset] + _activeChannelPtr->_field1D;
+ write2(8, reg, val & 0xFF);
+
+ reg += 0x10;
+ write2(8, reg, (_ports[reg] & 0x20) | (vTimes << 2) | (val >> 8));
+
+ write2(8, reg, _ports[reg] | 0x20);
+ }
+}
+
+static const int outputIndexes[] = {
+ 3, 1, 4, 2, 5, 6, 9, 7, 10, 8, 11, 12, 15, 13, 16, 14, 17
+};
+static const int outputChannels[] = {
+ 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21, 0
+};
+static const int volumeList[] = {
+ 0x3F, 0x3F, 0x36, 0x31, 0x2D, 0x2A, 0x28, 0x26, 0x24, 0x22, 0x21, 0x20, 0x1F, 0x1E, 0x1D, 0x1C,
+ 0x1B, 0x1A, 0x19, 0x19, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x14, 0x14, 0x13, 0x12, 0x12,
+ 0x11, 0x11, 0x10, 0x10, 0x0F, 0x0F, 0x0E, 0x0E, 0x0D, 0x0D, 0x0C, 0x0C, 0x0B, 0x0B, 0x0A, 0x0A,
+ 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+void ASound::updateActiveChannel() {
+ int reg = 0x40 + outputChannels[outputIndexes[_activeChannelNumber * 2]];
+ int portVal = _ports[reg] & 0xFFC0;
+ int newVolume = CLIP(_activeChannelPtr->_volume + _activeChannelPtr->_field1E, 0, 63);
+
+ // Note: Original had a whole block not seeming to be used, since the initialisation
+ // sets a variable to 5660h, and doesn't change it, so the branch is never taken
+ int val = CLIP(newVolume - volumeList[_activeChannelPtr->_fieldD], 0, 63);
+ val = (63 - val) | portVal;
+
+ int val2 = CLIP(newVolume - volumeList[-(_activeChannelPtr->_fieldD - 127)], 0, 63);
+ val2 = (63 - val2) | portVal;
+ write2(0, reg, val);
+ write2(2, reg, val2);
+}
+
+void ASound::loadSample(int sampleIndex) {
+ _activeChannelReg = 0xB0 + _activeChannelNumber;
+ write2(8, _activeChannelReg, _ports[_activeChannelReg] & 0xDF);
+
+ _activeChannelReg = _activeChannelNumber;
+ _samplePtr = &_samples[sampleIndex * 2];
+ _v11 = outputChannels[outputIndexes[_activeChannelReg * 2 - 1]];
+ processSample();
+
+ AdlibChannelData &cd = _channelData[_activeChannelNumber];
+ cd._field6 = _samplePtr->_field14;
+ cd._freqBase = _samplePtr->_freqBase;
+ cd._freqMask = _samplePtr->_freqMask;
+ cd._field0 = _samplePtr->_fieldE;
+
+ _samplePtr = &_samples[sampleIndex * 2 + 1];
+ _v11 = outputChannels[outputIndexes[_activeChannelReg * 2]];
+ processSample();
+}
+
+void ASound::processSample() {
+ // Write out vib flags and split point
+ write2(8, 0x40 + _v11, 0x3F);
+ int depthRhythm = (_ports[0xBD] & 0x3F) | (_amDep ? 0x80 : 0) |
+ (_vibDep ? 0x40 : 0);
+ write2(8, 0xBD, depthRhythm);
+ write2(8, 8, _splitPoint ? 0x40 : 0);
+
+ // Write out feedback & Alg
+ int val = (_samplePtr->_feedback << 1) | (1 - _samplePtr->_alg);
+ write2(8, 0xC0 + _activeChannelReg, val);
+
+ // Write out attack/decay rate
+ val = (_samplePtr->_attackRate << 4) | (_samplePtr->_decayRate & 0xF);
+ write2(8, 0x60 + _v11, val);
+
+ // Write out sustain level/release rate
+ val = (_samplePtr->_sustainLevel << 4) | (_samplePtr->_releaseRate & 0xF);
+ write2(8, 0x80 + _v11, val);
+
+ // Write out misc flags
+ val = (_samplePtr->_ampMod ? 0x80 : 0) | (_samplePtr->_vib ? 0x40 : 0)
+ | (_samplePtr->_egTyp ? 0x20 : 0) | (_samplePtr->_ksr ? 0x10 : 0)
+ | (_samplePtr->_freqMultiple & 0xF);
+ write2(8, 0x20 + _v11, val);
+
+ // Write out waveform select
+ write2(8, 0xE0 + _v11, _samplePtr->_waveformSelect & 3);
+
+ // Write out total level & scaling level
+ val = -((_samplePtr->_totalLevel & 0x3F) - 0x3F) | (_samplePtr->_scalingLevel << 6);
+ write2(8, 0x40 + _v11, val);
+}
+
+void ASound::updateFNumber() {
+ int loReg = 0xA0 + _activeChannelNumber;
+ int hiReg = 0xB0 + _activeChannelNumber;
+ int val1 = (_ports[hiReg] & 0x1F) << 8;
+ val1 += _ports[loReg] + _activeChannelPtr->_field1;
+ write2(8, loReg, val1);
+
+ int val2 = (_ports[hiReg] & 0x20) | (val1 >> 8);
+ write2(8, hiReg, val2);
+}
+
+int ASound::readBuffer(int16 *buffer, const int numSamples) {
+ Common::StackLock slock(_driverMutex);
+
+ int32 samplesLeft = numSamples;
+ memset(buffer, 0, sizeof(int16) * numSamples);
+ while (samplesLeft) {
+ if (!_samplesTillCallback) {
+ poll();
+ flush();
+
+ _samplesTillCallback = _samplesPerCallback;
+ _samplesTillCallbackRemainder += _samplesPerCallbackRemainder;
+ if (_samplesTillCallbackRemainder >= CALLBACKS_PER_SECOND) {
+ _samplesTillCallback++;
+ _samplesTillCallbackRemainder -= CALLBACKS_PER_SECOND;
+ }
+ }
+
+ int32 render = MIN<int>(samplesLeft, _samplesTillCallback);
+ samplesLeft -= render;
+ _samplesTillCallback -= render;
+
+ _opl->readBuffer(buffer, render);
+ buffer += render;
+ }
+ return numSamples;
+}
+
+int ASound::command0() {
+ bool isDisabled = _isDisabled;
+ _isDisabled = true;
+
+ for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
+ _channels[i].reset();
+
+ _v1 = 0;
+ _v2 = 0;
+ _freqMask1 = _freqMask2 = 0;
+ _freqBase1 = _freqBase2 = 0;
+ _v7 = 0;
+ _v8 = 0;
+
+ // Reset Adlib port registers
+ for (int reg = 0x4F; reg >= 0x40; --reg)
+ write2(8, reg, 0x3F);
+ for (int reg = 0xFF; reg >= 0x60; --reg)
+ write2(8, reg, 0);
+ for (int reg = 0x3F; reg > 0; --reg)
+ write2(8, reg, 0);
+ write2(8, 1, 0x20);
+
+ _isDisabled = isDisabled;
+ return 0;
+}
+
+int ASound::command1() {
+ for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
+ _channels[i].enable(0xFF);
+ return 0;
+}
+
+int ASound::command2() {
+ for (int i = 0; i < ADLIB_CHANNEL_MIDWAY; ++i)
+ _channels[i].setPtr2(_nullData);
+ return 0;
+}
+
+int ASound::command3() {
+ for (int i = 0; i < ADLIB_CHANNEL_MIDWAY; ++i)
+ _channels[i].enable(0xFF);
+ return 0;
+}
+
+int ASound::command4() {
+ for (int i = ADLIB_CHANNEL_MIDWAY; i < ADLIB_CHANNEL_COUNT; ++i)
+ _channels[i].setPtr2(_nullData);
+ return 0;
+}
+
+int ASound::command5() {
+ for (int i = 5; i < ADLIB_CHANNEL_COUNT; ++i)
+ _channels[i].enable(0xFF);
+ return 0;
+}
+
+int ASound::command6() {
+ _v9 = _v1;
+ _v1 = 0;
+ _v10 = _v2;
+ _v2 = 0;
+
+ channelOff(0x43);
+ channelOff(0x44);
+ channelOff(0x45);
+ channelOff(0x4B);
+ channelOff(0x4C);
+ channelOff(0x4D);
+ channelOff(0x53);
+ channelOff(0x54);
+ channelOff(0x55);
+
+ return 0;
+}
+
+int ASound::command7() {
+ channelOn(0x43, _channels[0]._volume);
+ channelOn(0x44, _channels[1]._volume);
+ channelOn(0x45, _channels[2]._volume);
+ channelOn(0x4B, _channels[3]._volume);
+ channelOn(0x4C, _channels[4]._volume);
+ channelOn(0x4D, _channels[5]._volume);
+
+ _v1 = _v9;
+ _v2 = _v10;
+
+ if (_v9 != _v10)
+ resultCheck();
+
+ _isDisabled = 0;
+ return _v10;
+}
+
+int ASound::command8() {
+ int result = 0;
+ for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
+ result |= _channels[i]._activeCount;
+
+ return result;
+}
+
+/*-----------------------------------------------------------------------*/
+
+const ASound1::CommandPtr ASound1::_commandList[42] = {
+ &ASound1::command0, &ASound1::command1, &ASound1::command2, &ASound1::command3,
+ &ASound1::command4, &ASound1::command5, &ASound1::command6, &ASound1::command7,
+ &ASound1::command8, &ASound1::command9, &ASound1::command10, &ASound1::command11,
+ &ASound1::command12, &ASound1::command13, &ASound1::command14, &ASound1::command15,
+ &ASound1::command16, &ASound1::command17, &ASound1::command18, &ASound1::command19,
+ &ASound1::command20, &ASound1::command21, &ASound1::command22, &ASound1::command23,
+ &ASound1::command24, &ASound1::command25, &ASound1::command26, &ASound1::command27,
+ &ASound1::command28, &ASound1::command29, &ASound1::command30, &ASound1::command31,
+ &ASound1::command32, &ASound1::command33, &ASound1::command34, &ASound1::command35,
+ &ASound1::command36, &ASound1::command37, &ASound1::command38, &ASound1::command39,
+ &ASound1::command40, &ASound1::command41
+};
+
+ASound1::ASound1(Audio::Mixer *mixer): ASound(mixer, "asound.001", 0x1520) {
+ _cmd23Toggle = false;
+
+ // Load sound samples
+ _soundFile.seek(_dataOffset + 0x12C);
+ for (int i = 0; i < 98; ++i)
+ _samples.push_back(AdlibSample(_soundFile));
+}
+
+int ASound1::command(int commandId, int param) {
+ if (commandId > 41)
+ return 0;
+
+ _commandParam = param;
+ _frameCounter = 0;
+ return (this->*_commandList[commandId])();
+}
+
+int ASound1::command9() {
+ playSound(0xC68, 12);
+ return 0;
+}
+
+int ASound1::command10() {
+ byte *pData1 = loadData(0x130E, 48);
+ if (!isSoundActive(pData1)) {
+ command1();
+ _channels[0].load(pData1);
+ _channels[1].load(loadData(0x133E, 392));
+ _channels[2].load(loadData(0x14C6, 46));
+ _channels[3].load(loadData(0x14F4, 48));
+ }
+
+ return 0;
+}
+
+int ASound1::command11() {
+ command111213();
+ _channels[0]._field1E = 0;
+ _channels[1]._field1E = 0;
+ return 0;
+}
+
+int ASound1::command12() {
+ command111213();
+ _channels[0]._field1E = 40;
+ _channels[1]._field1E = 0;
+ return 0;
+}
+
+int ASound1::command13() {
+ command111213();
+ _channels[0]._field1E = 40;
+ _channels[1]._field1E = 50;
+ return 0;
+}
+
+int ASound1::command14() {
+ playSound(0x1216, 248);
+ return 0;
+}
+
+int ASound1::command15() {
+ byte *pData1 = loadData(0x1524, 152);
+ if (!isSoundActive(pData1)) {
+ command1();
+ _channels[4].load(pData1);
+ _channels[5].load(loadData(0x15BC, 94));
+ _channels[6].load(loadData(0x161A, 94));
+ _channels[7].load(loadData(0x1678, 42));
+ _channels[8].load(loadData(0x16A2, 42));
+ }
+
+ return 0;
+}
+
+int ASound1::command16() {
+ playSound(0xC74, 14);
+ return 0;
+}
+
+int ASound1::command17() {
+ playSound(0xE9A, 10);
+ return 0;
+}
+
+int ASound1::command18() {
+ command1();
+ playSound(0xCA6, 20);
+ return 0;
+}
+
+int ASound1::command19() {
+ command1();
+ playSound(0xCBA, 74);
+ return 0;
+}
+
+int ASound1::command20() {
+ byte *pData = loadData(0xD18, 28);
+ if (!isSoundActive(pData))
+ playSoundData(pData);
+ return 0;
+}
+
+int ASound1::command21() {
+ playSound(0xD04, 20);
+ return 0;
+}
+
+int ASound1::command22() {
+ byte *pData = loadData(0xD34, 10);
+ pData[6] = (getRandomNumber() & 7) + 85;
+
+ if (!isSoundActive(pData))
+ playSoundData(pData);
+
+ return 0;
+}
+
+int ASound1::command23() {
+ _cmd23Toggle = !_cmd23Toggle;
+ playSound(_cmd23Toggle ? 0xD3E : 0xD46, 8);
+ return 0;
+}
+
+int ASound1::command24() {
+ playSound(0xD4E, 18);
+ playSound(0xD60, 20);
+ playSound(0xD74, 14);
+ return 0;
+}
+
+int ASound1::command25() {
+ byte *pData = loadData(0xD82, 16);
+ if (!isSoundActive(pData))
+ playSoundData(pData);
+
+ return 0;
+}
+
+int ASound1::command26() {
+ byte *pData = loadData(0xEEC, 10);
+ pData[5] = (command2627293032() + 0x7F) & 0xFF;
+
+ if (!isSoundActive(pData))
+ _channels[6].load(pData);
+
+ return 0;
+}
+
+int ASound1::command27() {
+ byte *pData = loadData(0xEE2, 10);
+ pData[5] = (command2627293032() + 0x40) & 0xFF;
+
+ if (!isSoundActive(pData))
+ _channels[7].load(pData);
+
+ return 0;
+}
+
+int ASound1::command28() {
+ playSound(0xD92, 28);
+ return 0;
+}
+
+int ASound1::command29() {
+ byte *pData = loadData(0xC82, 36);
+ byte v = (command2627293032() + 0x40) & 0xFF;
+ pData[7] = pData[13] = pData[21] = pData[27] = v;
+
+ if (!isSoundActive(pData))
+ playSoundData(pData, 0);
+
+ return 0;
+}
+
+int ASound1::command30() {
+ byte *pData = loadData(0xEA6, 16);
+ pData[7] = (command2627293032() + 0x40) & 0xFF;
+
+ if (!isSoundActive(pData))
+ playSoundData(pData, 0);
+
+ return 0;
+}
+
+int ASound1::command31() {
+ byte *pData = loadData(0xDAE, 14);
+ if (!isSoundActive(pData))
+ playSoundData(pData);
+
+ return 0;
+}
+
+int ASound1::command32() {
+ byte *pData = loadData(0xEB4, 46);
+ int v = command2627293032() + 0x40;
+ pData[9] = pData[17] = pData[25] = pData[33] = v & 0xFF;
+ pData[11] = pData[19] = pData[27] = pData[35] = v >> 8;
+
+ if (!isSoundActive(pData))
+ playSoundData(pData, 0);
+
+ return 0;
+}
+
+int ASound1::command33() {
+ playSound(0xDBC, 10);
+ playSound(0xDC6, 10);
+ return 0;
+}
+
+int ASound1::command34() {
+ int v = getRandomNumber() & 0x20;
+ if (!v)
+ v = 0x60;
+
+ byte *pData = loadData(0xDD0, 22);
+ pData[8] = pData[15] = v;
+ playSoundData(pData);
+ return 0;
+}
+
+int ASound1::command35() {
+ playSound(0xDE6, 16);
+ return 0;
+}
+
+int ASound1::command36() {
+ playSound(0xE10, 10);
+ command34();
+
+ return 0;
+}
+
+int ASound1::command37() {
+ playSound(0xE1A, 14);
+ return 0;
+}
+
+int ASound1::command38() {
+ playSound(0xE28, 114);
+ return 0;
+}
+
+int ASound1::command39() {
+ byte *pData1 = loadData(0x16CC, 82);
+ if (!isSoundActive(pData1)) {
+ _channels[5].load(pData1);
+ _channels[6].load(loadData(0x171E, 30));
+ _channels[7].load(loadData(0x173C, 40));
+ _channels[8].load(loadData(0x1764, 64));
+ }
+ return 0;
+}
+
+int ASound1::command40() {
+ playSound(0xDF6, 26);
+ return 0;
+}
+
+int ASound1::command41() {
+ playSound(0xC32, 34);
+ playSound(0xC54, 20);
+ return 0;
+}
+
+void ASound1::command111213() {
+ byte *pData1 = loadData(0xEF6, 408);
+ if (!isSoundActive(pData1)) {
+ command1();
+ _channels[0].load(pData1);
+ _channels[1].load(loadData(0x108E, 266));
+ _channels[2].load(loadData(0x1198, 66));
+ _channels[2].load(loadData(0x11DA, 60));
+ }
+}
+
+int ASound1::command2627293032() {
+ return (_commandParam > 0x40) ? _commandParam - 0x40 : _commandParam & 0xff00;
+}
+
+} // End of namespace Nebular
+
+} // End of namespace MADS
diff --git a/engines/mads/nebular/sound_nebular.h b/engines/mads/nebular/sound_nebular.h
new file mode 100644
index 0000000000..caafdcaf77
--- /dev/null
+++ b/engines/mads/nebular/sound_nebular.h
@@ -0,0 +1,412 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_SOUND_NEBULAR_H
+#define MADS_SOUND_NEBULAR_H
+
+#include "common/scummsys.h"
+#include "common/file.h"
+#include "common/mutex.h"
+#include "common/queue.h"
+#include "audio/audiostream.h"
+#include "audio/fmopl.h"
+#include "audio/mixer.h"
+
+namespace MADS {
+
+class SoundManager;
+
+namespace Nebular {
+
+/**
+ * Represents the data for a channel on the Adlib
+ */
+class AdlibChannel {
+public:
+ int _activeCount;
+ int _field1;
+ int _field2;
+ int _field3;
+ int _field4;
+ int _sampleIndex;
+ int _volume;
+ int _field7;
+ int _field8;
+ int _field9;
+ int _fieldA;
+ uint8 _fieldB;
+ int _fieldC;
+ int _fieldD;
+ int _fieldE;
+ byte *_ptr1;
+ byte *_pSrc;
+ byte *_ptr3;
+ byte *_ptr4;
+ int _field17;
+ int _field19;
+ byte *_soundData;
+ int _field1D;
+ int _field1E;
+ int _field1F;
+public:
+ AdlibChannel();
+
+ void reset();
+ void enable(int flag);
+ void setPtr2(byte *pData);
+ void load(byte *pData);
+ void check(byte *nullPtr);
+};
+
+class AdlibChannelData {
+public:
+ int _field0;
+ int _freqMask;
+ int _freqBase;
+ int _field6;
+};
+
+class AdlibSample {
+public:
+ int _attackRate;
+ int _decayRate;
+ int _sustainLevel;
+ int _releaseRate;
+ bool _egTyp;
+ bool _ksr;
+ int _totalLevel;
+ int _scalingLevel;
+ int _waveformSelect;
+ int _freqMultiple;
+ int _feedback;
+ bool _ampMod;
+ int _vib;
+ int _alg;
+ int _fieldE;
+ int _freqMask;
+ int _freqBase;
+ int _field14;
+
+ AdlibSample() {}
+ AdlibSample(Common::SeekableReadStream &s);
+};
+
+struct RegisterValue {
+ uint8 _regNum;
+ uint8 _value;
+
+ RegisterValue(int regNum, int value) {
+ _regNum = regNum; _value = value;
+ }
+};
+
+#define ADLIB_CHANNEL_COUNT 9
+#define ADLIB_CHANNEL_MIDWAY 5
+#define CALLBACKS_PER_SECOND 60
+
+/**
+ * Base class for the sound player resource files
+ */
+class ASound: public Audio::AudioStream {
+private:
+ struct CachedDataEntry {
+ int _offset;
+ byte *_data;
+ };
+ Common::List<CachedDataEntry> _dataCache;
+ uint16 _randomSeed;
+
+ /**
+ * Does the initial Adlib initialisation
+ */
+ void adlibInit();
+
+ /**
+ * Does on-going processing for the Adlib sounds being played
+ */
+ void update();
+
+ /**
+ * Polls each of the channels for updates
+ */
+ void pollChannels();
+
+ /**
+ * Checks the status of the channels
+ */
+ void checkChannels();
+
+ /**
+ * Polls the currently active channel
+ */
+ void pollActiveChannel();
+
+ /**
+ * Updates the octave of the currently active channel
+ */
+ void updateOctave();
+
+ void updateChannelState();
+ void updateActiveChannel();
+
+ /**
+ * Loads up the specified sample
+ */
+ void loadSample(int sampleIndex);
+
+ /**
+ * Writes out the data of the selected sample to the Adlib
+ */
+ void processSample();
+
+ void updateFNumber();
+protected:
+ /**
+ * Queue a byte for an Adlib register
+ */
+ void write(int reg, int val);
+
+ /**
+ * Queue a byte for an Adlib register, and store it in the _ports array
+ */
+ int write2(int state, int reg, int val);
+
+ /**
+ * Flush any pending Adlib register values to the OPL driver
+ */
+ void flush();
+
+ /**
+ * Turn a channel on
+ */
+ void channelOn(int reg, int volume);
+
+ /**
+ * Turn a channel off
+ */
+ void channelOff(int reg);
+
+ /**
+ * Checks for whether a poll result needs to be set
+ */
+ void resultCheck();
+
+ /**
+ * Loads a data block from the sound file, caching the result for any future
+ * calls for the same data
+ */
+ byte *loadData(int offset, int size);
+
+ /**
+ * Play the specified sound
+ * @param offset Offset of sound data within sound player data segment
+ * @param size Size of sound data block
+ */
+ void playSound(int offset, int size);
+
+ /**
+ * Play the specified raw sound data
+ * @param pData Pointer to data block containing sound data
+ * @param startingChannel Channel to start scan from
+ */
+ void playSoundData(byte *pData, int startingChannel = ADLIB_CHANNEL_MIDWAY);
+
+ /**
+ * Checks to see whether the given block of data is already loaded into a channel.
+ */
+ bool isSoundActive(byte *pData);
+
+ /**
+ * Sets the frequency for a given channel.
+ */
+ void setFrequency(int channel, int freq);
+
+ /**
+ * Returns a 16-bit random number
+ */
+ int getRandomNumber();
+
+ int command0();
+ int command1();
+ int command2();
+ int command3();
+ int command4();
+ int command5();
+ int command6();
+ int command7();
+ int command8();
+public:
+ Audio::Mixer *_mixer;
+ FM_OPL *_opl;
+ Audio::SoundHandle _soundHandle;
+ AdlibChannel _channels[ADLIB_CHANNEL_COUNT];
+ AdlibChannel *_activeChannelPtr;
+ AdlibChannelData _channelData[11];
+ Common::Array<AdlibSample> _samples;
+ AdlibSample *_samplePtr;
+ Common::File _soundFile;
+ Common::Queue<RegisterValue> _queue;
+ Common::Mutex _driverMutex;
+ int _dataOffset;
+ int _frameCounter;
+ bool _isDisabled;
+ int _v1;
+ int _v2;
+ int _activeChannelNumber;
+ int _freqMask1;
+ int _freqMask2;
+ int _freqBase1;
+ int _freqBase2;
+ int _channelNum1, _channelNum2;
+ int _v7;
+ int _v8;
+ int _v9;
+ int _v10;
+ int _pollResult;
+ int _resultFlag;
+ byte _nullData[2];
+ int _ports[256];
+ bool _stateFlag;
+ int _activeChannelReg;
+ int _v11;
+ bool _amDep, _vibDep, _splitPoint;
+ int _samplesPerCallback;
+ int _samplesPerCallbackRemainder;
+ int _samplesTillCallback;
+ int _samplesTillCallbackRemainder;
+public:
+ /**
+ * Constructor
+ * @param filename Specifies the adlib sound player file to use
+ * @param dataOffset Offset in the file of the data segment
+ */
+ ASound(Audio::Mixer *mixer, const Common::String &filename, int dataOffset);
+
+ /**
+ * Destructor
+ */
+ virtual ~ASound();
+
+ /**
+ * Execute a player command. Most commands represent sounds to play, but some
+ * low number commands also provide control operations.
+ * @param commandId Player ommand to execute.
+ * @param param Optional parameter used by a few commands
+ */
+ virtual int command(int commandId, int param) = 0;
+
+ /**
+ * Stop all currently playing sounds
+ */
+ int stop();
+
+ /**
+ * Main poll method to allow sounds to progress
+ */
+ int poll();
+
+ /**
+ * General noise/note output
+ */
+ void noise();
+
+ /**
+ * Return the current frame counter
+ */
+ int getFrameCounter() { return _frameCounter; }
+
+ // AudioStream interface
+ /**
+ * Main buffer read
+ */
+ virtual int readBuffer(int16 *buffer, const int numSamples);
+
+ /**
+ * Mono sound only
+ */
+ virtual bool isStereo() const { return false; }
+
+ /**
+ * Data is continuously pushed, so definitive end
+ */
+ virtual bool endOfData() const { return false; }
+
+ /**
+ * Return sample rate
+ */
+ virtual int getRate() const { return 11025; }
+};
+
+class ASound1: public ASound {
+private:
+ typedef int (ASound1::*CommandPtr)();
+ static const CommandPtr _commandList[42];
+ bool _cmd23Toggle;
+ int _commandParam;
+
+ int command9();
+ int command10();
+ int command11();
+ int command12();
+ int command13();
+ int command14();
+ int command15();
+ int command16();
+ int command17();
+ int command18();
+ int command19();
+ int command20();
+ int command21();
+ int command22();
+ int command23();
+ int command24();
+ int command25();
+ int command26();
+ int command27();
+ int command28();
+ int command29();
+ int command30();
+ int command31();
+ int command32();
+ int command33();
+ int command34();
+ int command35();
+ int command36();
+ int command37();
+ int command38();
+ int command39();
+ int command40();
+ int command41();
+
+ void command111213();
+ int command2627293032();
+public:
+ ASound1(Audio::Mixer *mixer);
+
+ virtual int command(int commandId, int param);
+};
+
+} // End of namespace Nebular
+
+} // End of namespace MADS
+
+#endif /* MADS_SOUND_NEBULAR_H */
diff --git a/engines/mads/palette.cpp b/engines/mads/palette.cpp
new file mode 100644
index 0000000000..aca1298a23
--- /dev/null
+++ b/engines/mads/palette.cpp
@@ -0,0 +1,447 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "engines/util.h"
+#include "graphics/palette.h"
+#include "mads/mads.h"
+#include "mads/msurface.h"
+
+namespace MADS {
+
+void RGB6::load(Common::SeekableReadStream *f) {
+ r = f->readByte();
+ g = f->readByte();
+ b = f->readByte();
+ palIndex = f->readByte();
+ u2 = f->readByte();
+ flags = f->readByte();
+}
+
+RGBList::RGBList(int numEntries, byte *srcData, bool freeData) {
+ _size = numEntries;
+ assert(numEntries <= PALETTE_COUNT);
+
+ if (srcData == NULL) {
+ _data = new byte[numEntries * 3];
+ _freeData = true;
+ } else {
+ _data = srcData;
+ _freeData = freeData;
+ }
+
+ _palIndexes = new byte[numEntries];
+ Common::fill(&_palIndexes[0], &_palIndexes[numEntries], 0);
+}
+
+RGBList::~RGBList() {
+ if (_freeData)
+ delete[] _data;
+ delete[] _palIndexes;
+}
+
+/*------------------------------------------------------------------------*/
+
+PaletteUsage::PaletteUsage() {
+}
+
+void PaletteUsage::load(int count, ...) {
+ va_list va;
+ va_start(va, count);
+
+ _data.clear();
+ for (int i = 0; i < count; ++i)
+ _data.push_back(va_arg(va, int));
+
+ va_end(va);
+}
+
+
+void PaletteUsage::getKeyEntries(Common::Array<RGB6> &palette) {
+ _data.clear();
+
+ for (uint i = 0; i < palette.size(); ++i) {
+ byte *uPtr = &palette[i].flags;
+ if ((*uPtr & 0x10) && _data.size() < 3) {
+ _data.push_back(i);
+ }
+ }
+}
+
+void PaletteUsage::prioritize(Common::Array<RGB6> &palette) {
+ int lst[3];
+
+ for (uint i = 0; i < _data.size(); ++i) {
+ RGB6 &palEntry = palette[_data[i]];
+ lst[i] = rgbMerge(palEntry);
+ }
+
+ prioritizeFromList(lst);
+}
+
+int PaletteUsage::rgbMerge(RGB6 &palEntry) {
+ return palEntry.r * 38 + palEntry.g * 76 + palEntry.b * 14;
+}
+
+void PaletteUsage::prioritizeFromList(int lst[3]) {
+ int idx1 = _data.size() - 1;
+ bool continueFlag;
+ int count2;
+
+ do {
+ continueFlag = false;
+ count2 = 0;
+
+ if (idx1 > 0) {
+ int numEntries = _data.size() - 1;
+ int usageIndex = 0, lstIndex = 0;
+
+ do {
+ if (lst[lstIndex] < lst[lstIndex + 1]) {
+ int lstVal = lst[lstIndex];
+ int usageVal = _data[usageIndex];
+
+ if (numEntries > 0) {
+ Common::copy(&lst[lstIndex + 1], &lst[lstIndex + numEntries], &lst[lstIndex]);
+ _data.remove_at(usageIndex);
+ _data.push_back(0);
+ }
+
+ int newIdx = 0;
+ if (idx1 > 0 && !continueFlag) {
+ for (newIdx = 0; newIdx <= idx1; ++newIdx) {
+ if (lst[newIdx] > lstVal)
+ break;
+ }
+ }
+
+ continueFlag = true;
+ int idxDiff = _data.size() - newIdx - 1;
+ if (idxDiff > 0) {
+ Common::copy_backward(&lst[0], &lst[2], &lst[1]);
+ _data.remove_at(2);
+ _data.insert_at(0, 0);
+ }
+
+ lst[newIdx] = lstVal;
+ _data[newIdx] = usageVal;
+ }
+
+ ++usageIndex;
+ --numEntries;
+ ++lstIndex;
+ ++count2;
+ } while (count2 > idx1 && !continueFlag);
+ }
+ } while (continueFlag);
+}
+
+void PaletteUsage::transform(Common::Array<RGB6> &palette) {
+ if (!empty()) {
+ for (uint i = 0; i < _data.size(); ++i) {
+ int palIndex = _data[i];
+ _data[i] = palette[palIndex].palIndex;
+ }
+ }
+}
+
+/*------------------------------------------------------------------------*/
+
+#define VGA_COLOR_TRANS(x) (x == 0x3f ? 255 : x << 2)
+
+Palette::Palette(MADSEngine *vm) : _vm(vm) {
+ reset();
+ _fading_in_progress = false;
+ Common::fill(&_usageCount[0], &_usageCount[PALETTE_COUNT], 0);
+ Common::fill(&_mainPalette[0], &_mainPalette[PALETTE_SIZE], 0);
+}
+
+void Palette::setPalette(const byte *colors, uint start, uint num) {
+ g_system->getPaletteManager()->setPalette(colors, start, num);
+ reset();
+}
+
+void Palette::grabPalette(byte *colors, uint start, uint num) {
+ g_system->getPaletteManager()->grabPalette(colors, start, num);
+ reset();
+}
+
+uint8 Palette::palIndexFromRgb(byte r, byte g, byte b, byte *paletteData) {
+ byte index = 0;
+ int32 minDist = 0x7fffffff;
+ byte palData[PALETTE_SIZE];
+ int Rdiff, Gdiff, Bdiff;
+
+ if (paletteData == NULL) {
+ g_system->getPaletteManager()->grabPalette(palData, 0, PALETTE_COUNT);
+ paletteData = &palData[0];
+ }
+
+ for (int palIndex = 0; palIndex < PALETTE_COUNT; ++palIndex) {
+ Rdiff = r - paletteData[palIndex * 3];
+ Gdiff = g - paletteData[palIndex * 3 + 1];
+ Bdiff = b - paletteData[palIndex * 3 + 2];
+
+ if (Rdiff * Rdiff + Gdiff * Gdiff + Bdiff * Bdiff < minDist) {
+ minDist = Rdiff * Rdiff + Gdiff * Gdiff + Bdiff * Bdiff;
+ index = (uint8)palIndex;
+ }
+ }
+
+ return (uint8)index;
+}
+
+void Palette::reset() {
+ byte palData[PALETTE_SIZE];
+ g_system->getPaletteManager()->grabPalette(palData, 0, PALETTE_COUNT);
+
+ BLACK = palIndexFromRgb(0, 0, 0, palData);
+ BLUE = palIndexFromRgb(0, 0, 255, palData);
+ GREEN = palIndexFromRgb(0, 255, 0, palData);
+ CYAN = palIndexFromRgb(0, 255, 255, palData);
+ RED = palIndexFromRgb(255, 0, 0, palData);
+ VIOLET = palIndexFromRgb(255, 0, 255, palData);
+ BROWN = palIndexFromRgb(168, 84, 84, palData);
+ LIGHT_GRAY = palIndexFromRgb(168, 168, 168, palData);
+ DARK_GRAY = palIndexFromRgb(84, 84, 84, palData);
+ LIGHT_BLUE = palIndexFromRgb(0, 0, 127, palData);
+ LIGHT_GREEN = palIndexFromRgb(0, 127, 0, palData);
+ LIGHT_CYAN = palIndexFromRgb(0, 127, 127, palData);
+ LIGHT_RED = palIndexFromRgb(84, 0, 0, palData);
+ PINK = palIndexFromRgb(84, 0, 0, palData);
+ YELLOW = palIndexFromRgb(0, 84, 84, palData);
+ WHITE = palIndexFromRgb(255, 255, 255, palData);
+}
+
+void Palette::resetColorCounts() {
+ Common::fill(&_usageCount[0], &_usageCount[PALETTE_COUNT], 0);
+}
+
+void Palette::blockRange(int startIndex, int size) {
+ // Use a reference count of -1 to signal a palette index shouldn't be used
+ Common::fill(&_usageCount[startIndex], &_usageCount[startIndex + size], -1);
+}
+
+void Palette::addRange(RGBList *list) {
+ byte *data = list->data();
+ byte *palIndexes = list->palIndexes();
+ byte palData[PALETTE_COUNT];
+ g_system->getPaletteManager()->grabPalette(palData, 0, PALETTE_COUNT);
+ bool paletteChanged = false;
+
+ for (int colIndex = 0; colIndex < list->size(); ++colIndex) {
+ // Scan through for an existing copy of the RGB value
+ int palIndex = -1;
+ while (++palIndex < PALETTE_COUNT) {
+ if (_usageCount[palIndex] <= 0)
+ // Palette index is to be skipped
+ continue;
+
+ if ((palData[palIndex * 3] == data[colIndex * 3]) &&
+ (palData[palIndex * 3 + 1] == data[colIndex * 3 + 1]) &&
+ (palData[palIndex * 3 + 2] == data[colIndex * 3 + 2]))
+ // Match found
+ break;
+ }
+
+ if (palIndex == PALETTE_COUNT) {
+ // No match found, so find a free slot to use
+ palIndex = -1;
+ while (++palIndex < PALETTE_COUNT) {
+ if (_usageCount[palIndex] == 0)
+ break;
+ }
+
+ if (palIndex == PALETTE_COUNT)
+ error("addRange - Ran out of palette space to allocate");
+
+ palData[palIndex * 3] = data[colIndex * 3];
+ palData[palIndex * 3 + 1] = data[colIndex * 3 + 1];
+ palData[palIndex * 3 + 2] = data[colIndex * 3 + 2];
+ paletteChanged = true;
+ }
+
+ palIndexes[colIndex] = palIndex;
+ ++_usageCount[palIndex];
+ }
+
+ if (paletteChanged) {
+ g_system->getPaletteManager()->setPalette(&palData[0], 0, 256);
+ reset();
+ }
+}
+
+void Palette::deleteRange(RGBList *list) {
+ // Release the reference count on each of the palette entries
+ for (int colIndex = 0; colIndex < list->size(); ++colIndex) {
+ int palIndex = list->palIndexes()[colIndex];
+ assert(_usageCount[palIndex] > 0);
+ --_usageCount[palIndex];
+ }
+}
+
+void Palette::deleteAllRanges() {
+ for (int colIndex = 0; colIndex < 255; ++colIndex)
+ _usageCount[colIndex] = 0;
+}
+
+void Palette::setGradient(byte *palette, int start, int count, int rgbValue1, int rgbValue2) {
+ int rgbCtr = 0;
+ int rgbCurrent = rgbValue2;
+ int rgbDiff = -(rgbValue2 - rgbValue1);
+
+ if (count > 0) {
+ byte *pDest = palette + start * 3;
+ int endVal = count - 1;
+ int numLeft = count;
+
+ do {
+ pDest[0] = pDest[1] = pDest[2] = rgbCurrent;
+
+ if (numLeft > 1) {
+ rgbCtr += rgbDiff;
+ if (rgbCtr >= endVal) {
+ do {
+ ++rgbCurrent;
+ rgbCtr += 1 - numLeft;
+ } while (rgbCtr >= endVal);
+ }
+ }
+
+ pDest += 3;
+ } while (--numLeft > 0);
+ }
+}
+
+byte *Palette::decodePalette(Common::SeekableReadStream *palStream, int *numColors) {
+ *numColors = palStream->readUint16LE();
+ assert(*numColors <= 252);
+
+ byte *palData = new byte[*numColors * 3];
+ Common::fill(&palData[0], &palData[*numColors * 3], 0);
+
+ for (int i = 0; i < *numColors; ++i) {
+ byte r = palStream->readByte();
+ byte g = palStream->readByte();
+ byte b = palStream->readByte();
+ palData[i * 3] = VGA_COLOR_TRANS(r);
+ palData[i * 3 + 1] = VGA_COLOR_TRANS(g);
+ palData[i * 3 + 2] = VGA_COLOR_TRANS(b);
+
+ // The next 3 bytes are unused
+ palStream->skip(3);
+ }
+
+ return palData;
+}
+
+int Palette::loadPalette(Common::SeekableReadStream *palStream, int indexStart) {
+ int colorCount;
+ byte *palData = decodePalette(palStream, &colorCount);
+ _vm->_palette->setPalette(palData, indexStart, colorCount);
+
+ delete palData;
+ return colorCount;
+}
+
+void Palette::setSystemPalette() {
+ resetColorCounts();
+
+ byte palData[4 * 3];
+ palData[0 * 3] = palData[0 * 3 + 1] = palData[0 * 3 + 2] = 0;
+ palData[1 * 3] = palData[1 * 3 + 1] = palData[1 * 3 + 2] = 0x54;
+ palData[2 * 3] = palData[2 * 3 + 1] = palData[2 * 3 + 2] = 0xb4;
+ palData[3 * 3] = palData[3 * 3 + 1] = palData[3 * 3 + 2] = 0xff;
+
+ setPalette(palData, 0, 4);
+ blockRange(0, 4);
+}
+
+void Palette::resetGamePalette(int lowRange, int highRange) {
+ Common::fill((byte *)&_gamePalette[0], (byte *)&_gamePalette[PALETTE_COUNT], 0);
+ initRange(_mainPalette);
+
+ // Init low range to common RGB values
+ if (lowRange) {
+ _gamePalette[0].r = 1;
+ _gamePalette[0].b = 0;
+
+ Common::fill(&_gamePalette[1], &_gamePalette[lowRange - 1], _gamePalette[0]);
+ }
+
+ // Init high range to common RGB values
+ if (highRange) {
+ _gamePalette[255].r = 1;
+ _gamePalette[255].b = 0;
+
+ Common::fill(&_gamePalette[255 - highRange], &_gamePalette[254], _gamePalette[255]);
+ }
+}
+
+void Palette::initGamePalette() {
+ // TODO
+}
+
+void Palette::initRange(byte *palette) {
+ int var6 = 0;
+ int vdx = 0;
+ int vbx = 0;
+ do {
+ int vdi = (vdx == 1) ? 0 : 0x2A;
+ int var8 = 0;
+ int varE = vbx;
+ int var10 = vdx;
+ do {
+ vdx = 0;
+ do {
+ int vcx = 0;
+ int var4 = vdx;
+ do {
+ int var2 = var6 + vcx;
+ byte *destP = &palette[var2 * 3];
+
+ destP[0] = (var8) ? vdi & 0xFF : vbx & 0XFF;
+ destP[1] = (var4) ? vdi & 0xFF : vbx & 0XFF;
+ destP[2] = (vcx) ? vdi & 0xFF : vbx & 0XFF;
+ } while (++vcx < 2);
+
+ var6 += 2;
+ vdx = var4;
+ } while (++vdx < 2);
+ } while (++var8 < 2);
+
+ vdx = var10 + 1;
+ vbx = varE + 21;
+ } while (vbx < 42);
+
+ palette[19] = 21;
+}
+
+void Palette::setLowRange() {
+ _mainPalette[0] = _mainPalette[1] = _mainPalette[2] = 0;
+ _mainPalette[3] = _mainPalette[4] = _mainPalette[5] = 0x15;
+ _mainPalette[6] = _mainPalette[7] = _mainPalette[8] = 0x2A;
+ _mainPalette[9] = _mainPalette[10] = _mainPalette[11] = 0x3F;
+ _vm->_palette->setPalette(_mainPalette, 0, 4);
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/palette.h b/engines/mads/palette.h
new file mode 100644
index 0000000000..820e50fb21
--- /dev/null
+++ b/engines/mads/palette.h
@@ -0,0 +1,265 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_PALETTE_H
+#define MADS_PALETTE_H
+
+#include "common/scummsys.h"
+#include "common/stream.h"
+
+namespace MADS {
+
+class MADSEngine;
+
+struct RGB4 {
+ byte r;
+ byte g;
+ byte b;
+ byte u;
+};
+
+struct RGB6 {
+ byte r;
+ byte g;
+ byte b;
+ byte palIndex;
+ byte u2;
+ byte flags;
+
+ void load(Common::SeekableReadStream *f);
+};
+
+/**
+ * Used to store a list of RGB values
+ */
+class RGBList {
+private:
+ int _size;
+ byte *_data;
+ byte *_palIndexes;
+ bool _freeData;
+public:
+ /**
+ * Constructor
+ */
+ RGBList(int numEntries = 256, byte *srcData = NULL, bool freeData = true);
+
+ /**
+ * Destructor
+ */
+ ~RGBList();
+
+ /**
+ * Returns the raw data containing the RGB values as 3 bytes per entry
+ */
+ byte *data() { return _data; }
+
+ /**
+ * Returns the list of palette indexes each RGB tuple maps to in the current palette
+ */
+ byte *palIndexes() { return _palIndexes; }
+
+ /**
+ * Returns the size of the palette
+ */
+ int size() const { return _size; }
+};
+
+class PaletteUsage {
+private:
+ Common::Array<int> _data;
+
+ int rgbMerge(RGB6 &palEntry);
+
+ void prioritizeFromList(int lst[3]);
+public:
+ PaletteUsage();
+
+ void load(int count, ...);
+
+ /**
+ * Returns whether the usage list is empty
+ */
+ bool empty() const { return _data.size() == 0; }
+
+ /**
+ * Gets key entries from the passed palette
+ * @param palette 6-bit per entry read in palette
+ */
+ void getKeyEntries(Common::Array<RGB6> &palette);
+
+ /**
+ * Prioritizes the palette index list based on the intensity of the
+ * RGB values of the palette entries that they refer to
+ */
+ void prioritize(Common::Array<RGB6> &palette);
+
+ bool process(Common::Array<RGB6> &palette, int v) {
+ warning("TODO: PaletteUsage::process");
+ return 0;
+ }
+
+ void transform(Common::Array<RGB6> &palette);
+};
+
+
+#define PALETTE_COUNT 256
+#define PALETTE_SIZE (256 * 3)
+
+class Palette {
+private:
+ /**
+ * Initialises a stanadrd range of colours for the given palette
+ */
+ void initRange(byte *palette);
+protected:
+ MADSEngine *_vm;
+ bool _colorsChanged;
+
+ bool _fading_in_progress;
+ byte _originalPalette[PALETTE_COUNT * 4];
+ byte _fadedPalette[PALETTE_COUNT * 4];
+ int _usageCount[PALETTE_COUNT];
+
+ void reset();
+public:
+ byte _mainPalette[PALETTE_SIZE];
+ RGB4 _gamePalette[PALETTE_COUNT];
+ PaletteUsage _paletteUsage;
+public:
+ /**
+ * Constructor
+ */
+ Palette(MADSEngine *vm);
+
+ /**
+ * Destructor
+ */
+ virtual ~Palette() {}
+
+ /**
+ * Sets a new palette
+ */
+ void setPalette(const byte *colors, uint start, uint num);
+
+ /**
+ * Returns a subset of the currently loaded palette
+ */
+ void grabPalette(byte *colors, uint start, uint num);
+
+ /**
+ * Returns the palette index in the palette that most closely matches the
+ * specified RGB pair
+ */
+ uint8 palIndexFromRgb(byte r, byte g, byte b, byte *paletteData = nullptr);
+
+ // Methods used for reference counting color usage
+ /**
+ * Resets the usage counts for the palette
+ */
+ void resetColorCounts();
+
+ /**
+ * Blocks out a range of the palette from being used
+ */
+ void blockRange(int startIndex, int size);
+
+ /**
+ * Adds the data of an RGBList into the current palette and increment usage counts.
+ */
+ void addRange(RGBList *list);
+
+ /**
+ * Delets a range from the current palette, dercementing the accompanying usage counts.
+ */
+ void deleteRange(RGBList *list);
+
+ /**
+ * Deletes all loaded RGB lists are their usage references.
+ */
+ void deleteAllRanges();
+
+ // Virtual method table
+ /**
+ * Decode a palette and return it, without affecting the Palette itself
+ */
+ byte *decodePalette(Common::SeekableReadStream *palStream, int *numColors);
+
+ /**
+ * Loads a palette from a stream
+ */
+ int loadPalette(Common::SeekableReadStream *palStream, int indexStart = 0);
+
+ /**
+ * Sets a small set of system/core colors needed by the game
+ */
+ void setSystemPalette();
+
+ /**
+ * Update a range of an arbitrary palette
+ */
+ static void setGradient(byte *palette, int start, int count, int rgbValue1, int rgbValue2);
+
+ /**
+ * Resets the game palette
+ */
+ void resetGamePalette(int v1, int v2);
+
+ /**
+ * Initialises game palette
+ */
+ void initGamePalette();
+
+ /**
+ * Set the first four palette entries with preset values
+ */
+ void setLowRange();
+
+ /**
+ * Set up the palette as the game ends
+ */
+ void close() {
+ warning("TODO: Palette::close");
+ }
+
+ // Color indexes
+ uint8 BLACK;
+ uint8 BLUE;
+ uint8 GREEN;
+ uint8 CYAN;
+ uint8 RED;
+ uint8 VIOLET;
+ uint8 BROWN;
+ uint8 LIGHT_GRAY;
+ uint8 DARK_GRAY;
+ uint8 LIGHT_BLUE;
+ uint8 LIGHT_GREEN;
+ uint8 LIGHT_CYAN;
+ uint8 LIGHT_RED;
+ uint8 PINK;
+ uint8 YELLOW;
+ uint8 WHITE;
+};
+
+} // End of namespace MADS
+
+#endif /* MADS_PALETTE_H */
diff --git a/engines/mads/resources.cpp b/engines/mads/resources.cpp
new file mode 100644
index 0000000000..8cfc1a290a
--- /dev/null
+++ b/engines/mads/resources.cpp
@@ -0,0 +1,326 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "common/archive.h"
+#include "common/textconsole.h"
+#include "mads/mads.h"
+#include "mads/resources.h"
+
+namespace MADS {
+
+enum ResourceType {RESTYPE_ROOM, RESTYPE_SC, RESTYPE_TEXT, RESTYPE_QUO, RESTYPE_I,
+ RESTYPE_OB, RESTYPE_FONT, RESTYPE_SOUND, RESTYPE_SPEECH, RESTYPE_HAS_EXT, RESTYPE_NO_EXT};
+
+/**
+ * HAG Archives implementation
+ */
+class HagArchive : public Common::Archive {
+private:
+ /**
+ * Details of a single entry in a HAG file index
+ */
+ struct HagEntry {
+ Common::String _resourceName;
+ uint32 _offset;
+ uint32 _size;
+
+ HagEntry(): _offset(0), _size(0) {}
+ HagEntry(Common::String resourceName, uint32 offset, uint32 size):
+ _resourceName(resourceName), _offset(offset), _size(size) {}
+ };
+
+ class HagIndex {
+ public:
+ Common::List<HagEntry> _entries;
+ Common::String _filename;
+ };
+
+ Common::Array<HagIndex> _index;
+
+ /**
+ * Load the index of all the game's HAG files
+ */
+ void loadIndex();
+
+ /**
+ * Given a resource name, opens up the correct HAG file and returns whether
+ * an entry with the given name exists.
+ */
+ bool getHeaderEntry(const Common::String &resourceName, HagIndex &hagIndex, HagEntry &hagEntry) const;
+
+ /**
+ * Returns the HAG resource filename that will contain a given resource
+ */
+ Common::String getResourceFilename(const Common::String &resourceName) const;
+
+ /**
+ * Return a resource type given a resource name
+ */
+ ResourceType getResourceType(const Common::String &resourceName) const;
+public:
+ HagArchive();
+ virtual ~HagArchive();
+
+ // Archive implementation
+ virtual bool hasFile(const Common::String &name) const;
+ virtual int listMembers(Common::ArchiveMemberList &list) const;
+ virtual const Common::ArchiveMemberPtr getMember(const Common::String &name) const;
+ virtual Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const;
+};
+
+const char *const MADSCONCAT_STRING = "MADSCONCAT";
+
+HagArchive::HagArchive() {
+ loadIndex();
+}
+
+HagArchive::~HagArchive() {
+}
+
+// Archive implementation
+bool HagArchive::hasFile(const Common::String &name) const {
+ HagIndex hagIndex;
+ HagEntry hagEntry;
+ return getHeaderEntry(name, hagIndex, hagEntry);
+}
+
+int HagArchive::listMembers(Common::ArchiveMemberList &list) const {
+ int members = 0;
+
+ for (uint hagCtr = 0; hagCtr < _index.size(); ++hagCtr) {
+ HagIndex hagIndex = _index[hagCtr];
+ Common::List<HagEntry>::iterator i;
+
+ for (i = hagIndex._entries.begin(); i != hagIndex._entries.end(); ++i) {
+ list.push_back(Common::ArchiveMemberList::value_type(
+ new Common::GenericArchiveMember((*i)._resourceName, this)));
+ ++members;
+ }
+ }
+
+ return members;
+}
+
+const Common::ArchiveMemberPtr HagArchive::getMember(const Common::String &name) const {
+ if (!hasFile(name))
+ return Common::ArchiveMemberPtr();
+
+ return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
+}
+
+Common::SeekableReadStream *HagArchive::createReadStreamForMember(const Common::String &name) const {
+ HagIndex hagIndex;
+ HagEntry hagEntry;
+
+ if (getHeaderEntry(name, hagIndex, hagEntry)) {
+ // Entry found. If the correct file is not already open, open it
+ Common::File f;
+ if (!f.open(hagIndex._filename))
+ error("Could not open HAG file");
+
+ // Return a new stream for the specific resource
+ f.seek(hagEntry._offset);
+ return f.readStream(hagEntry._size);
+ }
+
+ return nullptr;
+}
+
+void HagArchive::loadIndex() {
+ Common::File hagFile;
+
+ for (int sectionIndex = -1; sectionIndex < 10; ++sectionIndex) {
+ Common::String filename = (sectionIndex == -1) ? "GLOBAL.HAG" :
+ Common::String::format("SECTION%d.HAG", sectionIndex);
+ if (!hagFile.open(filename))
+ error("Could not locate HAG file - %s", filename.c_str());
+
+ // Check for header
+ char headerBuffer[16];
+ if ((hagFile.read(headerBuffer, 16) != 16) ||
+ (strncmp(headerBuffer, MADSCONCAT_STRING, 10) != 0))
+ error("Invalid HAG file opened");
+
+ // Scan through the HAG index
+ int numEntries = hagFile.readUint16LE();
+
+ HagIndex hagIndex;
+ hagIndex._filename = filename;
+
+ for (int idx = 0; idx < numEntries; ++idx) {
+ // Read in the details of the next resource
+ char resourceBuffer[14];
+ uint32 offset = hagFile.readUint32LE();
+ uint32 size = hagFile.readUint32LE();
+ hagFile.read(resourceBuffer, 14);
+
+ hagIndex._entries.push_back(HagEntry(resourceBuffer, offset, size));
+ }
+
+ hagFile.close();
+ _index.push_back(hagIndex);
+ }
+}
+
+bool HagArchive::getHeaderEntry(const Common::String &resourceName,
+ HagIndex &hagIndex, HagEntry &hagEntry) const {
+ Common::String resName = resourceName;
+ resName.toUppercase();
+ if (resName[0] == '*')
+ resName.deleteChar(0);
+
+ Common::String hagFilename = getResourceFilename(resName);
+
+ // Find the index for the given file
+ for (uint hagCtr = 0; hagCtr < _index.size(); ++hagCtr) {
+ hagIndex = _index[hagCtr];
+
+ if (hagIndex._filename == hagFilename) {
+ Common::List<HagEntry>::iterator ei;
+ for (ei = hagIndex._entries.begin(); ei != hagIndex._entries.end(); ++ei) {
+ hagEntry = *ei;
+ if (hagEntry._resourceName == resName)
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+Common::String HagArchive::getResourceFilename(const Common::String &resourceName) const {
+ ResourceType resType = getResourceType(resourceName);
+ Common::String outputFilename = "GLOBAL.HAG";
+
+ if ((resType == RESTYPE_ROOM) || (resType == RESTYPE_SC)) {
+ int value = atoi(resourceName.c_str() + 2);
+ int hagFileNum = (resType == RESTYPE_ROOM) ? value / 100 : value;
+
+ if (hagFileNum > 0)
+ outputFilename = Common::String::format("SECTION%d.HAG", hagFileNum);
+ }
+
+ if (resType == RESTYPE_SPEECH)
+ outputFilename = "SPEECH.HAG";
+
+ return outputFilename;
+}
+
+ResourceType HagArchive::getResourceType(const Common::String &resourceName) const {
+ if (resourceName.hasPrefix("RM")) {
+ // Room resource
+ return RESTYPE_ROOM;
+ } else if (resourceName.hasPrefix("SC")) {
+ // SC resource
+ return RESTYPE_SC;
+ } else if (resourceName.hasSuffix(".TXT")) {
+ // Text resource
+ return RESTYPE_TEXT;
+ } else if (resourceName.hasSuffix(".QUO")) {
+ // QUO resource
+ return RESTYPE_QUO;
+ } else if (resourceName.hasPrefix("I")) {
+ // I resource
+ return RESTYPE_I;
+ } else if (resourceName.hasPrefix("OB")) {
+ // OB resource
+ return RESTYPE_OB;
+ } else if (resourceName.hasPrefix("FONT")) {
+ // FONT resource
+ return RESTYPE_FONT;
+ } else if (resourceName.hasPrefix("SOUND")) {
+ // SOUND resource
+ return RESTYPE_SOUND;
+ } else if (resourceName.hasPrefix("SPCHC")) {
+ // SPEECH resource
+ return RESTYPE_SPEECH;
+ }
+
+ // Check for a known extension
+ const char *extPos = strchr(resourceName.c_str(), '.');
+ if (extPos) {
+ ++extPos;
+ if (!strcmp(extPos, "FL") || !strcmp(extPos, "LBM") || !strcmp(extPos, "ANM") ||
+ !strcmp(extPos, "AA") || !strcmp(extPos, "SS")) {
+ return RESTYPE_HAS_EXT;
+ }
+ }
+
+ return RESTYPE_NO_EXT;
+}
+
+/*------------------------------------------------------------------------*/
+
+void Resources::init(MADSEngine *vm) {
+ SearchMan.add("HAG", new HagArchive());
+}
+
+Common::String Resources::formatName(RESPREFIX resType, int id, const Common::String &ext) {
+ Common::String result = "*";
+
+ if (resType == 3 && !id) {
+ id = id / 100;
+ }
+
+ if (!ext.empty()) {
+ switch (resType) {
+ case RESPREFIX_GL:
+ result += "GL000";
+ break;
+ case RESPREFIX_SC:
+ result += Common::String::format("SC%.3d", id);
+ break;
+ case RESPREFIX_RM:
+ result += Common::String::format("RM%.3d", id);
+ break;
+ default:
+ break;
+ }
+
+ result += ext;
+ }
+
+ return result;
+}
+
+Common::String Resources::formatResource(const Common::String &resName,
+ const Common::String &hagFilename) {
+// int v1 = 0, v2 = 0;
+
+ if (resName.hasPrefix("*")) {
+ // Resource file specified
+ error("TODO: formatResource");
+ } else {
+ // File outside of hag file
+ return resName;
+ }
+}
+
+/*------------------------------------------------------------------------*/
+
+void File::openFile(const Common::String &filename) {
+ if (!Common::File::open(filename))
+ error("Could not open file - %s", filename.c_str());
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/resources.h b/engines/mads/resources.h
new file mode 100644
index 0000000000..8fed0ab437
--- /dev/null
+++ b/engines/mads/resources.h
@@ -0,0 +1,76 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_RESOURCES_H
+#define MADS_RESOURCES_H
+
+#include "common/scummsys.h"
+#include "common/file.h"
+#include "common/str.h"
+
+namespace MADS {
+
+class MADSEngine;
+
+enum RESPREFIX {
+ RESPREFIX_GL = 1, RESPREFIX_SC = 2, RESPREFIX_RM = 3
+};
+
+enum EXTTYPE {
+ EXT_SS = 1, EXT_AA = 2, EXT_DAT = 3, EXT_HH = 4, EXT_ART = 5, EXT_INT = 6
+};
+
+class Resources {
+public:
+ /**
+ * Instantiates the resource manager
+ */
+ static void init(MADSEngine *vm);
+
+ static Common::String formatName(RESPREFIX resType, int id, const Common::String &ext);
+ static Common::String formatResource(const Common::String &resName, const Common::String &hagFilename);
+};
+
+/**
+ * Derived file class
+ */
+class File: public Common::File {
+public:
+ /**
+ * Constructor
+ */
+ File(): Common::File() {}
+
+ /**
+ * Constructor
+ */
+ File(const Common::String &filename) { openFile(filename); }
+
+ /**
+ * Opens the given file, throwing an error if it can't be opened
+ */
+ void openFile(const Common::String &filename);
+};
+
+} // End of namespace MADS
+
+#endif /* MADS_RESOURCES_H */
diff --git a/engines/mads/scene.cpp b/engines/mads/scene.cpp
new file mode 100644
index 0000000000..e9ae3e6ddd
--- /dev/null
+++ b/engines/mads/scene.cpp
@@ -0,0 +1,179 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "mads/scene.h"
+#include "mads/mads.h"
+#include "mads/nebular/nebular_scenes.h"
+
+namespace MADS {
+
+Scene::Scene(MADSEngine *vm): _vm(vm), _spriteSlots(vm) {
+ _priorSceneId = 0;
+ _nextSceneId = 0;
+ _currentSceneId = 0;
+ _vocabBuffer = nullptr;
+ _sceneLogic = nullptr;
+ _sceneInfo = nullptr;
+
+ _verbList.push_back(VerbInit(VERB_LOOK, 2, 0));
+ _verbList.push_back(VerbInit(VERB_TAKE, 2, 0));
+ _verbList.push_back(VerbInit(VERB_PUSH, 2, 0));
+ _verbList.push_back(VerbInit(VERB_OPEN, 2, 0));
+ _verbList.push_back(VerbInit(VERB_PUT, 1, -1));
+ _verbList.push_back(VerbInit(VERB_TALKTO, 2, 0));
+ _verbList.push_back(VerbInit(VERB_GIVE, 1, 2));
+ _verbList.push_back(VerbInit(VERB_PULL, 2, 0));
+ _verbList.push_back(VerbInit(VERB_CLOSE, 2, 0));
+ _verbList.push_back(VerbInit(VERB_THROW, 1, 2));
+}
+
+Scene::~Scene() {
+ delete[] _vocabBuffer;
+ delete _sceneLogic;
+ delete _sceneInfo;
+}
+
+void Scene::clearDynamicHotspots() {
+ _dynamicHotspots.clear();
+ _dynamicHotspotsChanged = false;
+}
+
+void Scene::clearVocab() {
+ freeVocab();
+ _activeVocabs.clear();
+}
+
+void Scene::freeVocab() {
+ delete[] _vocabBuffer;
+ _vocabBuffer = nullptr;
+}
+
+void Scene::addActiveVocab(int vocabId) {
+ if (activeVocabIndexOf(vocabId) == -1) {
+ assert(_activeVocabs.size() < 200);
+ _activeVocabs.push_back(vocabId);
+ }
+}
+
+int Scene::activeVocabIndexOf(int vocabId) {
+ for (uint i = 0; i < _activeVocabs.size(); ++i) {
+ if (_activeVocabs[i] == vocabId)
+ return i;
+ }
+
+ return -1;
+}
+
+void Scene::clearSequenceList() {
+ _sequences.clear();
+}
+
+void Scene::clearMessageList() {
+ _messages.clear();
+ _talkFont = "*FONTCONV.FF";
+ _textSpacing = -1;
+}
+
+void Scene::loadSceneLogic() {
+ delete _sceneLogic;
+
+ switch (_vm->getGameID()) {
+ case GType_RexNebular:
+ _sceneLogic = Nebular::SceneFactory::createScene(this);
+ break;
+ default:
+ error("Unknown game");
+ }
+}
+
+void Scene::loadScene(int sceneId, const Common::String &prefix, bool palFlag) {
+ // Store the previously active scene number and set the new one
+ _priorSceneId = _currentSceneId;
+ _currentSceneId = sceneId;
+
+ _v1 = 0;
+ if (palFlag)
+ _vm->_palette->resetGamePalette(18, 10);
+
+ _spriteSlots.clear(false);
+ _sequences.clear();
+ _messages.clear();
+
+ // TODO: palletteUsage reset? setPalette(_nullPalette);
+ _sceneInfo = SceneInfo::load(_vm, _currentSceneId, _v1, Common::String(), _vm->_game->_v2 ? 17 : 16,
+ _depthSurface, _backgroundSurface);
+}
+
+void Scene::loadHotspots() {
+ File f(Resources::formatName(RESPREFIX_RM, _currentSceneId, ".HH"));
+ int count = f.readUint16LE();
+
+ _hotspots.clear();
+ for (int i = 0; i < count; ++i)
+ _hotspots.push_back(Hotspot(f));
+}
+
+void Scene::loadVocab() {
+ // Add all the verbs to the active vocab list
+ for (uint i = 0; i < _verbList.size(); ++i)
+ addActiveVocab(_verbList[i]._id);
+
+ // Load the vocabs for any object descriptions and custom actions
+ for (uint objIndex = 0; objIndex < _vm->_game->_objects.size(); ++objIndex) {
+ InventoryObject &io = _vm->_game->_objects[objIndex];
+ addActiveVocab(io._descId);
+
+ for (int vocabIndex = 0; vocabIndex <io._vocabCount; ++vocabIndex) {
+ addActiveVocab(io._vocabList[vocabIndex]._vocabId);
+ }
+ }
+
+ // Load scene hotspot list vocabs and verbs
+ for (uint i = 0; i < _hotspots.size(); ++i) {
+ addActiveVocab(_hotspots[i]._vocabId);
+ if (_hotspots[i]._verbId)
+ addActiveVocab(_hotspots[i]._verbId);
+ }
+
+ loadVocabStrings();
+}
+
+void Scene::loadVocabStrings() {
+ freeVocab();
+ File f("*VOCAB.DAT");
+
+ byte *d = new byte[ f.size()];
+ f.read(d, f.size());
+
+
+// int vocabId = 1;
+ for (uint strIndex = 0; strIndex < _activeVocabs.size(); ++strIndex) {
+ // TODO: Rest of this method
+ }
+}
+
+void Scene::free() {
+ warning("TODO: Scene::free");
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/scene.h b/engines/mads/scene.h
new file mode 100644
index 0000000000..a1edff5bdf
--- /dev/null
+++ b/engines/mads/scene.h
@@ -0,0 +1,144 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_SCENE_H
+#define MADS_SCENE_H
+
+#include "common/scummsys.h"
+#include "common/array.h"
+#include "common/rect.h"
+#include "mads/assets.h"
+#include "mads/msurface.h"
+#include "mads/scene_data.h"
+
+namespace MADS {
+
+class Scene {
+private:
+ /**
+ * Free the voculary list buffer
+ */
+ void freeVocab();
+
+ /**
+ * Return the index of a given Vocab in the active vocab list
+ */
+ int activeVocabIndexOf(int vocabId);
+
+ /**
+ * Secondary loading vocab list
+ */
+ void loadVocabStrings();
+protected:
+ MADSEngine *_vm;
+public:
+ SceneLogic *_sceneLogic;
+ int _priorSceneId;
+ int _nextSceneId;
+ int _currentSceneId;
+ Common::Array<VerbInit> _verbList;
+ Common::Array<TextDisplay> _textDisplay;
+ SpriteSlots _spriteSlots;
+ Common::Array<SpriteAsset *> _sprites;
+ int _spritesIndex;
+ Common::Array<DynamicHotspot> _dynamicHotspots;
+ bool _dynamicHotspotsChanged;
+ byte *_vocabBuffer;
+ Common::Array<int> _activeVocabs;
+ Common::Array<SequenceEntry> _sequences;
+ Common::Array<KernelMessage> _messages;
+ Common::String _talkFont;
+ int _textSpacing;
+ Common::Array<Hotspot> _hotspots;
+ ScreenObjects _screenObjects;
+ int _v1;
+ SceneInfo *_sceneInfo;
+ MSurface _backgroundSurface;
+ MSurface _depthSurface;
+
+ /**
+ * Constructor
+ */
+ Scene(MADSEngine *vm);
+
+ /**
+ * Destructor
+ */
+ ~Scene();
+
+ /**
+ * Clear the dynamic hotspot list
+ */
+ void clearDynamicHotspots();
+
+ /**
+ * Clear the vocabulary list
+ */
+ void clearVocab();
+
+ /**
+ * Add a given vocab entry to the active list
+ */
+ void addActiveVocab(int vocabId);
+
+ /**
+ * Clear the sequence list
+ */
+ void clearSequenceList();
+
+ /**
+ * Clear the message list
+ */
+ void clearMessageList();
+
+ /**
+ * Loads the scene logic for a given scene
+ */
+ void loadSceneLogic();
+
+ /**
+ * Loads the resources associated with the given scene
+ * @param sceneId Scene to load
+ * @param prefix Prefix to use for retrieving animation data
+ * @param palFlag Flag for whether to reset the high/lo palette areas
+ */
+ void loadScene(int sceneId, const Common::String &prefix, bool palFlag);
+
+ /**
+ * Loads the hotstpots for the scene
+ */
+ void loadHotspots();
+
+ /**
+ * Loads the vocab list
+ */
+ void loadVocab();
+
+ /**
+ * Clear the data for the scene
+ */
+ void free();
+};
+
+} // End of namespace MADS
+
+#endif /* MADS_SCENE_H */
diff --git a/engines/mads/scene_data.cpp b/engines/mads/scene_data.cpp
new file mode 100644
index 0000000000..e0c25d89fb
--- /dev/null
+++ b/engines/mads/scene_data.cpp
@@ -0,0 +1,358 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "mads/scene_data.h"
+#include "mads/mads.h"
+#include "mads/compression.h"
+#include "mads/resources.h"
+#include "mads/nebular/nebular_scenes.h"
+
+namespace MADS {
+
+SpriteSlot::SpriteSlot() {
+ _spriteType = ST_NONE;
+ _seqIndex = 0;
+ _spritesIndex = 0;
+ _frameNumber = 0;
+ _depth = 0;
+ _scale = 0;
+}
+
+SpriteSlot::SpriteSlot(SpriteType type, int seqIndex) {
+ _spriteType = type;
+ _seqIndex = seqIndex;
+ _spritesIndex = 0;
+ _frameNumber = 0;
+ _depth = 0;
+ _scale = 0;
+}
+
+/*------------------------------------------------------------------------*/
+
+void SpriteSlots::clear(bool flag) {
+ _vm->_game->_scene._textDisplay.clear();
+
+ if (flag)
+ _vm->_game->_scene._sprites.clear();
+
+ Common::Array<SpriteSlot>::clear();
+ push_back(SpriteSlot(ST_FULL_SCREEN_REFRESH, -1));
+}
+
+/**
+ * Releases any sprites used by the player
+ */
+void SpriteSlots::releasePlayerSprites() {
+ Player &player = _vm->_game->player();
+
+ if (player._spritesLoaded && player._numSprites > 0) {
+ int spriteEnd = player._spritesStart + player._numSprites - 1;
+ do {
+ deleteEntry(spriteEnd);
+ } while (--spriteEnd >= player._spritesStart);
+ }
+}
+
+void SpriteSlots::deleteEntry(int index) {
+ remove_at(index);
+}
+
+
+/*------------------------------------------------------------------------*/
+
+TextDisplay::TextDisplay() {
+ _active = false;
+ _spacing = 0;
+ _expire = 0;
+ _col1 = _col2 = 0;
+}
+
+/*------------------------------------------------------------------------*/
+
+DynamicHotspot::DynamicHotspot() {
+ _seqIndex = 0;
+ _facing = 0;
+ _descId = 0;
+ _field14 = 0;
+ _articleNumber = 0;
+ _cursor = 0;
+}
+
+/*------------------------------------------------------------------------*/
+
+SequenceEntry::SequenceEntry() {
+ _spritesIndex = 0;
+ _flipped =0;
+ _frameIndex = 0;
+ _frameStart = 0;
+ _numSprites = 0;
+ _animType = 0;
+ _frameInc = 0;
+ _depth = 0;
+ _scale = 0;
+ _dynamicHotspotIndex = -1;
+ _triggerCountdown = 0;
+ _doneFlag = 0;
+ _entries._count = 0;
+ _abortMode = 0;
+ _actionNouns[0] = _actionNouns[1] = _actionNouns[2] = 0;
+ _numTicks = 0;
+ _extraTicks = 0;
+ _timeout = 0;
+}
+
+KernelMessage::KernelMessage() {
+ _flags = 0;
+ _seqInex = 0;
+ _asciiChar = '\0';
+ _asciiChar2 = '\0';
+ _colors = 0;
+ _msgOffset = 0;
+ _numTicks = 0;
+ _frameTimer2 = 0;
+ _frameTimer = 0;
+ _timeout = 0;
+ _field1C = 0;
+ _abortMode = 0;
+ _nounList[0] = _nounList[1] = _nounList[2] = 0;
+}
+
+/*------------------------------------------------------------------------*/
+
+Hotspot::Hotspot() {
+ _facing = 0;
+ _articleNumber = 0;
+ _cursor = 0;
+ _vocabId = 0;
+ _verbId = 0;
+}
+
+Hotspot::Hotspot(Common::SeekableReadStream &f) {
+ _bounds.left = f.readSint16LE();
+ _bounds.top = f.readSint16LE();
+ _bounds.right = f.readSint16LE();
+ _bounds.bottom = f.readSint16LE();
+ _feetPos.x = f.readSint16LE();
+ _feetPos.y = f.readSint16LE();
+ _facing = f.readByte();
+ _articleNumber = f.readByte();
+ f.skip(1);
+ _cursor = f.readByte();
+ _vocabId = f.readUint16LE();
+ _verbId = f.readUint16LE();
+}
+
+/*------------------------------------------------------------------------*/
+
+void ARTHeader::load(Common::SeekableReadStream *f) {
+ // Read in dimensions of image
+ _width = f->readUint16LE();
+ _height = f->readUint16LE();
+
+ // Read in palette information
+ int palCount = f->readUint16LE();
+ for (int i = 0; i < palCount; ++i) {
+ RGB6 rgb;
+ rgb.load(f);
+ _palette.push_back(rgb);
+ }
+ f->skip(6 * (256 - palCount));
+
+ // Read unknown???
+ palCount = f->readUint16LE();
+ for (int i = 0; i < palCount; ++i) {
+ RGB4 rgb;
+ rgb.r = f->readByte();
+ rgb.g = f->readByte();
+ rgb.b = f->readByte();
+ rgb.u = f->readByte();
+
+ _palData.push_back(rgb);
+ }
+}
+
+/*------------------------------------------------------------------------*/
+
+SceneInfo *SceneInfo::load(MADSEngine *vm, int sceneId, int v1, const Common::String &resName,
+ int v3, MSurface &depthSurface, MSurface &bgSurface) {
+ return new SceneInfo(vm, sceneId, v1, resName, v3, depthSurface, bgSurface);
+}
+
+SceneInfo::SceneInfo(MADSEngine *vm, int sceneId, int v1, const Common::String &resName,
+ int flags, MSurface &depthSurface, MSurface &bgSurface) {
+ bool flag = true;
+ bool sceneFlag = sceneId >= 0;
+ bool ssFlag = false, wsFlag = false;
+
+ Common::Array<SpriteAsset *> spriteSets;
+ Common::Array<int> xpList;
+
+ // Figure out the resource to use
+ Common::String resourceName;
+ if (sceneFlag) {
+ resourceName = Resources::formatName(RESPREFIX_RM, sceneId, ".DAT");
+ } else {
+ resourceName = "*" + Resources::formatResource(resName, resName);
+ }
+
+ // Open the scene info resource for access
+ File infoFile(resName);
+
+ // Read in basic data
+ _sceneId = infoFile.readUint16LE();
+ _artFileNum = infoFile.readUint16LE();
+ _depthStyle = infoFile.readUint16LE();
+ _width = infoFile.readUint16LE();
+ _height = infoFile.readUint16LE();
+ infoFile.skip(24);
+ _nodeCount = infoFile.readUint16LE();
+ _yBandsEnd = infoFile.readUint16LE();
+ _yBandsStart = infoFile.readUint16LE();
+ _maxScale = infoFile.readUint16LE();
+ _minScale = infoFile.readUint16LE();
+ for (int i = 0; i < 15; ++i)
+ _depthList[i] = infoFile.readUint16LE();
+ _field4A = infoFile.readUint16LE();
+
+ // Load the set of objects that are associated with the scene
+ for (int i = 0; i < 20; ++i) {
+ InventoryObject obj;
+ obj.load(infoFile);
+ _objects.push_back(obj);
+ }
+
+ int setCount = infoFile.readUint16LE();
+ int field40E = infoFile.readUint16LE();
+
+ for (int i = 0; i < 20; ++i) {
+ char name[64];
+ infoFile.read(name, 64);
+ _setNames.push_back(Common::String(name));
+ }
+
+ infoFile.close();
+ int width = _width;
+ int height = _height;
+
+ if (!bgSurface.getPixels()) {
+ bgSurface.setSize(width, height);
+ ssFlag = true;
+ }
+
+ if (_depthStyle == 2)
+ width >>= 2;
+ if (!depthSurface.getPixels()) {
+ depthSurface.setSize(width, height);
+ wsFlag = true;
+ }
+
+ // Load the depth surface with the scene codes
+ loadCodes(depthSurface);
+
+ // Get the ART resource
+ if (sceneFlag) {
+ resourceName = Resources::formatName(RESPREFIX_RM, sceneId, ".ART");
+ } else {
+ resourceName = "*" + Resources::formatResource(resName, resName);
+ }
+
+ // Load in the ART header and palette
+ File artFile(resourceName);
+ MadsPack artResource(&artFile);
+ Common::SeekableReadStream *stream = artResource.getItemStream(0);
+
+ ARTHeader artHeader;
+ artHeader.load(stream);
+ artFile.close();
+
+ // Copy out the palette data
+ for (uint i = 0; i < artHeader._palData.size(); ++i)
+ _palette.push_back(artHeader._palData[i]);
+
+ if (!(flags & 1)) {
+ if (!_vm->_palette->_paletteUsage.empty()) {
+ _vm->_palette->_paletteUsage.getKeyEntries(artHeader._palette);
+ _vm->_palette->_paletteUsage.prioritize(artHeader._palette);
+ }
+
+ _field4C = _vm->_palette->_paletteUsage.process(artHeader._palette, 0xF800);
+ if (_field4C > 0) {
+ _vm->_palette->_paletteUsage.transform(artHeader._palette);
+
+ for (uint i = 0; i < _palette.size(); ++i) {
+ byte g = _palette[i].g;
+ _palette[g].b = artHeader._palette[g].palIndex;
+ }
+ }
+ }
+
+ // Read in the background surface data
+ assert(_width == bgSurface.w && _height == bgSurface.h);
+ stream->read(bgSurface.getPixels(), bgSurface.w * bgSurface.h);
+
+ if (flags & 1) {
+ for (uint i = 0; i < _setNames.size(); ++i) {
+ Common::String setResName;
+ if (sceneFlag || resName.hasPrefix("*"))
+ setResName += "*";
+ setResName += _setNames[i];
+
+ SpriteAsset *sprites = new SpriteAsset(_vm, setResName, flags);
+ spriteSets.push_back(sprites);
+ xpList.push_back(-1); // TODO:: sprites->_field6
+ }
+ }
+
+
+
+ warning("TODO");
+}
+
+void SceneInfo::loadCodes(MSurface &depthSurface) {
+ File f(Resources::formatName(RESPREFIX_RM, _sceneId, ".DAT"));
+
+ uint16 width = _width;
+ uint16 height = _height;
+ byte *walkMap = new byte[f.size()];
+
+ depthSurface.setSize(width, height);
+ f.read(walkMap, f.size());
+
+ byte *ptr = (byte *)depthSurface.getPixels();
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ int ofs = x + (y * width);
+ if ((walkMap[ofs / 8] << (ofs % 8)) & 0x80)
+ *ptr++ = 1; // walkable
+ else
+ *ptr++ = 0;
+ }
+ }
+
+ delete[] walkMap;
+}
+
+/*------------------------------------------------------------------------*/
+
+} // End of namespace MADS
diff --git a/engines/mads/scene_data.h b/engines/mads/scene_data.h
new file mode 100644
index 0000000000..74a1ff8e68
--- /dev/null
+++ b/engines/mads/scene_data.h
@@ -0,0 +1,305 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_SCENE_DATA_H
+#define MADS_SCENE_DATA_H
+
+#include "common/scummsys.h"
+#include "common/array.h"
+#include "common/str.h"
+#include "common/str-array.h"
+#include "common/rect.h"
+#include "mads/assets.h"
+#include "mads/game_data.h"
+
+namespace MADS {
+
+class MADSEngine;
+class Scene;
+
+enum {
+ VERB_LOOK = 3,
+ VERB_TAKE = 4,
+ VERB_PUSH = 5,
+ VERB_OPEN = 6,
+ VERB_PUT = 7,
+ VERB_TALKTO = 8,
+ VERB_GIVE = 9,
+ VERB_PULL = 10,
+ VERB_CLOSE = 11,
+ VERB_THROW = 12,
+ VERB_WALKTO = 13
+};
+
+class VerbInit {
+public:
+ int _id;
+ int _action1;
+ int _action2;
+
+ VerbInit() {}
+ VerbInit(int id, int action1, int action2): _id(id), _action1(action1), _action2(action2) {}
+};
+
+class ScreenObjects {
+public:
+ int _v8333C;
+ int _v832EC;
+ int _yp;
+};
+
+enum SpriteType {
+ ST_NONE = 0, ST_FOREGROUND = 1, ST_BACKGROUND = -4,
+ ST_FULL_SCREEN_REFRESH = -2, ST_EXPIRED = -1
+};
+
+class SpriteSlot {
+public:
+ SpriteType _spriteType;
+ int _seqIndex;
+ int _spritesIndex;
+ int _frameNumber;
+ Common::Point _position;
+ int _depth;
+ int _scale;
+public:
+ SpriteSlot();
+ SpriteSlot(SpriteType type, int seqIndex);
+};
+
+class SpriteSlots: public Common::Array<SpriteSlot> {
+private:
+ MADSEngine *_vm;
+public:
+ SpriteSlots(MADSEngine *vm): _vm(vm) {}
+
+ /**
+ * Clears any pending slot data and schedules a full screen refresh.
+ * @param flag Also reset sprite list
+ */
+ void clear(bool flag);
+
+ /**
+ * Delete any sprites used by the player
+ */
+ void releasePlayerSprites();
+
+ /**
+ * Delete a sprite entry
+ * @param index Specifies the index in the array
+ */
+ void deleteEntry(int index);
+};
+
+class TextDisplay {
+public:
+ bool _active;
+ int _spacing;
+ Common::Rect _bounds;
+ int _expire;
+ int _col1;
+ int _col2;
+ Common::String _fontName;
+ Common::String _msg;
+
+ TextDisplay();
+};
+
+class DynamicHotspot {
+public:
+ int _seqIndex;
+ Common::Rect _bounds;
+ Common::Point _feetPos;
+ int _facing;
+ int _descId;
+ int _field14;
+ int _articleNumber;
+ int _cursor;
+
+ DynamicHotspot();
+};
+
+class SequenceEntry {
+public:
+ int _spritesIndex;
+ int _flipped;
+ int _frameIndex;
+ int _frameStart;
+ int _numSprites;
+ int _animType;
+ int _frameInc;
+ int _depth;
+ int _scale;
+ int _dynamicHotspotIndex;
+
+ Common::Point _msgPos;
+
+ int _triggerCountdown;
+ bool _doneFlag;
+ struct {
+ int _count;
+ int _mode[5];
+ int _frameIndex[5];
+ int _abortVal[5];
+ } _entries;
+ int _abortMode;
+ int _actionNouns[3];
+ int _numTicks;
+ int _extraTicks;
+ int _timeout;
+
+ SequenceEntry();
+};
+
+class KernelMessage {
+public:
+ int _flags;
+ int _seqInex;
+ char _asciiChar;
+ char _asciiChar2;
+ int _colors;
+ Common::Point _posiition;
+ int _msgOffset;
+ int _numTicks;
+ int _frameTimer2;
+ int _frameTimer;
+ int _timeout;
+ int _field1C;
+ int _abortMode;
+ int _nounList[3];
+ Common::String _msg;
+
+ KernelMessage();
+};
+
+class Hotspot {
+public:
+ Common::Rect _bounds;
+ Common::Point _feetPos;
+ int _facing;
+ int _articleNumber;
+ int _cursor;
+ int _vocabId;
+ int _verbId;
+
+ Hotspot();
+ Hotspot(Common::SeekableReadStream &f);
+};
+
+class SceneLogic {
+protected:
+ Scene *_scene;
+public:
+ /**
+ * Constructor
+ */
+ SceneLogic(Scene *scene): _scene(scene) {}
+
+ /**
+ * Destructor
+ */
+ virtual ~SceneLogic() {}
+
+ /**
+ * Called to initially setup a scene
+ */
+ virtual void setup() = 0;
+
+ /**
+ * Called as the scene is entered (made active)
+ */
+ virtual void enter() = 0;
+
+ /**
+ * Called one per frame
+ */
+ virtual void step() = 0;
+
+ /**
+ * Called before an action is started
+ */
+ virtual void preActions() = 0;
+
+ /**
+ * Handles scene actions
+ */
+ virtual void actions() = 0;
+
+ /**
+ * Post-action handling
+ */
+ virtual void postActions() = 0;
+};
+
+struct ARTHeader {
+ int _width;
+ int _height;
+ Common::Array<RGB6> _palette;
+ Common::Array<RGB4> _palData;
+
+ void load(Common::SeekableReadStream *f);
+};
+
+/**
+ * Handles general data for a given scene
+ */
+class SceneInfo {
+private:
+ MADSEngine *_vm;
+
+ SceneInfo(MADSEngine *vm, int sceneId, int v1, const Common::String &resName,
+ int v3, MSurface &depthSurface, MSurface &bgSurface);
+
+ /**
+ * Loads the given surface with depth information of a given scene
+ */
+ void loadCodes(MSurface &depthSurface);
+public:
+ int _sceneId;
+ int _artFileNum;
+ int _depthStyle;
+ int _width;
+ int _height;
+
+ int _nodeCount;
+ int _yBandsEnd;
+ int _yBandsStart;
+ int _maxScale;
+ int _minScale;
+ int _depthList[15];
+ int _field4A;
+
+ int _field4C;
+ Common::Array<InventoryObject> _objects;
+ Common::StringArray _setNames;
+ Common::Array<RGB4> _palette;
+public:
+ /**
+ * Instantiates the class and loads the data
+ */
+ static SceneInfo *load(MADSEngine *vm, int sceneId, int flags,
+ const Common::String &resName, int v3, MSurface &depthSurface, MSurface &bgSurface);
+};
+
+} // End of namespace MADS
+
+#endif /* MADS_SCENE_DATA_H */
diff --git a/engines/mads/sound.cpp b/engines/mads/sound.cpp
new file mode 100644
index 0000000000..ea0239076b
--- /dev/null
+++ b/engines/mads/sound.cpp
@@ -0,0 +1,112 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "audio/audiostream.h"
+#include "audio/decoders/raw.h"
+#include "common/memstream.h"
+#include "mads/sound.h"
+#include "mads/mads.h"
+#include "mads/nebular/sound_nebular.h"
+
+namespace MADS {
+
+SoundManager::SoundManager(MADSEngine *vm, Audio::Mixer *mixer) {
+ _vm = vm;
+ _mixer = mixer;
+ _driver = nullptr;
+ _pollSoundEnabled = false;
+ _soundPollFlag = false;
+ _newSoundsPaused = false;
+}
+
+SoundManager::~SoundManager() {
+ delete _driver;
+}
+
+void SoundManager::init(int sectionNumber) {
+ assert(sectionNumber > 0 && sectionNumber < 10);
+
+ switch (_vm->getGameID()) {
+ case GType_RexNebular:
+ // TODO: Other Rex Adlib section drivers
+ assert(sectionNumber == 1);
+ _driver = new Nebular::ASound1(_mixer);
+ break;
+
+ default:
+ error("Unknown game");
+ }
+}
+
+void SoundManager::closeDriver() {
+ if (_driver) {
+ command(0);
+ setEnabled(false);
+ stop();
+
+ removeDriver();
+ }
+}
+
+void SoundManager::removeDriver() {
+ delete _driver;
+ _driver = nullptr;
+}
+
+void SoundManager::setEnabled(bool flag) {
+ _pollSoundEnabled = flag;
+ _soundPollFlag = false;
+}
+
+void SoundManager::pauseNewCommands() {
+ _newSoundsPaused = true;
+}
+
+void SoundManager::startQueuedCommands() {
+ _newSoundsPaused = false;
+
+ while (!_queuedCommands.empty()) {
+ int commandId = _queuedCommands.front();
+ command(commandId);
+ }
+}
+
+void SoundManager::command(int commandId, int param) {
+ if (_newSoundsPaused) {
+ if (_queuedCommands.size() < 8)
+ _queuedCommands.push(commandId);
+ } else if (_driver) {
+ _driver->command(commandId, param);
+ }
+}
+
+void SoundManager::stop() {
+ if (_driver)
+ _driver->stop();
+}
+
+void SoundManager::noise() {
+ if (_driver)
+ _driver->noise();
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/sound.h b/engines/mads/sound.h
new file mode 100644
index 0000000000..0fd9ac1095
--- /dev/null
+++ b/engines/mads/sound.h
@@ -0,0 +1,103 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_SOUND_H
+#define MADS_SOUND_H
+
+#include "common/scummsys.h"
+#include "common/queue.h"
+#include "audio/audiostream.h"
+#include "audio/mixer.h"
+#include "mads/nebular/sound_nebular.h"
+
+namespace MADS {
+
+class MADSEngine;
+
+class SoundManager {
+private:
+ MADSEngine *_vm;
+ Audio::Mixer *_mixer;
+ Nebular::ASound *_driver;
+ bool _pollSoundEnabled;
+ bool _soundPollFlag;
+ bool _newSoundsPaused;
+ Common::Queue<int> _queuedCommands;
+public:
+ SoundManager(MADSEngine *vm, Audio::Mixer *mixer);
+ ~SoundManager();
+
+ /**
+ * Initialises the sound driver for a given game section
+ */
+ void init(int sectionNumber);
+
+ /**
+ * Stop any currently active sound and remove the driver
+ */
+ void closeDriver();
+
+ /**
+ * Remove the driver
+ */
+ void removeDriver();
+
+ /**
+ * Sets the enabled status of the sound
+ * @flag True if sound should be enabled
+ */
+ void setEnabled(bool flag);
+
+ /**
+ * Temporarily pause the playback of any new sound commands
+ */
+ void pauseNewCommands();
+
+ /**
+ * Stop queueing sound commands, and execute any previously queued ones
+ */
+ void startQueuedCommands();
+
+ //@{
+ /**
+ * Executes a command on the sound driver
+ * @param commandid Command Id to execute
+ * @param param Optional paramater specific to a few commands
+ */
+ void command(int commandId, int param = 0);
+
+ /**
+ * Stops any currently playing sound
+ */
+ void stop();
+
+ /**
+ * Noise
+ * Some sort of random noise generation?
+ */
+ void noise();
+ //@}
+};
+
+} // End of namespace MADS
+
+#endif /* MADS_SOUND_H */
diff --git a/engines/mads/user_interface.cpp b/engines/mads/user_interface.cpp
new file mode 100644
index 0000000000..3bbf6a0b2b
--- /dev/null
+++ b/engines/mads/user_interface.cpp
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "mads/mads.h"
+#include "mads/graphics.h"
+#include "mads/user_interface.h"
+#include "mads/msurface.h"
+
+namespace MADS {
+
+UserInterface *UserInterface::init(MADSEngine *vm) {
+ return new UserInterface(vm);
+}
+
+UserInterface::UserInterface(MADSEngine *vm): _vm(vm), _surface(
+ new MSurface(MADS_SCREEN_WIDTH, MADS_INTERFACE_HEIGHT)) {
+}
+
+UserInterface::~UserInterface() {
+ delete _surface;
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/user_interface.h b/engines/mads/user_interface.h
new file mode 100644
index 0000000000..838638ba0b
--- /dev/null
+++ b/engines/mads/user_interface.h
@@ -0,0 +1,46 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_USER_INTERFACE_H
+#define MADS_USER_INTERFACE_H
+
+#include "common/scummsys.h"
+
+namespace MADS {
+
+class MADSEngine;
+
+class UserInterface {
+private:
+ MADSEngine *_vm;
+ MSurface *_surface;
+
+ UserInterface(MADSEngine *vm);
+public:
+ static UserInterface *init(MADSEngine *vm);
+public:
+ ~UserInterface();
+};
+
+} // End of namespace MADS
+
+#endif /* MADS_USER_INTERFACE_H */