aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Gilbert2014-02-18 20:08:58 -0500
committerPaul Gilbert2014-02-18 20:08:58 -0500
commit0e46c809d10dcd8fd766d7adcb966785e7955f5b (patch)
tree817e0f1c0c6f4b5ab86f6703cc09fb839173f3f5
parentece3e9a2200052cc65f60e0295b674095d7a6a66 (diff)
downloadscummvm-rg350-0e46c809d10dcd8fd766d7adcb966785e7955f5b.tar.gz
scummvm-rg350-0e46c809d10dcd8fd766d7adcb966785e7955f5b.tar.bz2
scummvm-rg350-0e46c809d10dcd8fd766d7adcb966785e7955f5b.zip
MADS: Initial implementation of MSurface class and dependant classes
-rw-r--r--engines/mads/compression.cpp185
-rw-r--r--engines/mads/compression.h80
-rw-r--r--engines/mads/detection.cpp19
-rw-r--r--engines/mads/events.cpp34
-rw-r--r--engines/mads/events.h43
-rw-r--r--engines/mads/mads.cpp42
-rw-r--r--engines/mads/mads.h34
-rw-r--r--engines/mads/module.mk9
-rw-r--r--engines/mads/msprite.cpp207
-rw-r--r--engines/mads/msprite.h152
-rw-r--r--engines/mads/msurface.cpp690
-rw-r--r--engines/mads/msurface.h185
-rw-r--r--engines/mads/palette.cpp289
-rw-r--r--engines/mads/palette.h110
-rw-r--r--engines/mads/resources.cpp32
-rw-r--r--engines/mads/resources.h57
-rw-r--r--engines/mads/sound.cpp9
-rw-r--r--engines/mads/sound.h3
-rw-r--r--engines/mads/sound_nebular.h4
19 files changed, 2153 insertions, 31 deletions
diff --git a/engines/mads/compression.cpp b/engines/mads/compression.cpp
new file mode 100644
index 0000000000..f8df5a5e43
--- /dev/null
+++ b/engines/mads/compression.cpp
@@ -0,0 +1,185 @@
+/* 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";
+
+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) {
+ Common::SeekableReadStream *stream = vm->_resources->get(resourceName);
+ initialise(stream);
+ vm->_resources->toss(resourceName);
+}
+
+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;
+}
+
+//--------------------------------------------------------------------------
+
+const char *FabInputExceededError = "FabDecompressor - Passed end of input buffer during decompression";
+const char *FabOutputExceededError = "FabDecompressor - Decompressed data exceeded specified size";
+
+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 M4
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/detection.cpp b/engines/mads/detection.cpp
index 4e56655f40..73a4b97931 100644
--- a/engines/mads/detection.cpp
+++ b/engines/mads/detection.cpp
@@ -38,8 +38,19 @@ namespace MADS {
struct MADSGameDescription {
ADGameDescription desc;
+
+ int gameID;
+ uint32 features;
};
+uint32 MADSEngine::getGameID() const {
+ return _gameDescription->gameID;
+}
+
+uint32 MADSEngine::getGameFeatures() const {
+ return _gameDescription->gameID;
+}
+
uint32 MADSEngine::getFeatures() const {
return _gameDescription->desc.flags;
}
@@ -52,10 +63,6 @@ Common::Platform MADSEngine::getPlatform() const {
return _gameDescription->desc.platform;
}
-bool MADSEngine::getIsDemo() const {
- return _gameDescription->desc.flags & ADGF_DEMO;
-}
-
} // End of namespace MADS
static const PlainGameDescriptor MADSGames[] = {
@@ -165,7 +172,7 @@ SaveStateDescriptor MADSMetaEngine::querySaveMetaInfos(const char *target, int s
#if PLUGIN_ENABLED_DYNAMIC(MADS)
-REGISTER_PLUGIN_DYNAMIC(MADS, PLUGIN_TYPE_ENGINE, MADSMetaEngine);
+ REGISTER_PLUGIN_DYNAMIC(MADS, PLUGIN_TYPE_ENGINE, MADSMetaEngine);
#else
-REGISTER_PLUGIN_STATIC(MADS, PLUGIN_TYPE_ENGINE, MADSMetaEngine);
+ REGISTER_PLUGIN_STATIC(MADS, PLUGIN_TYPE_ENGINE, MADSMetaEngine);
#endif
diff --git a/engines/mads/events.cpp b/engines/mads/events.cpp
new file mode 100644
index 0000000000..8d6262aec3
--- /dev/null
+++ b/engines/mads/events.cpp
@@ -0,0 +1,34 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "common/events.h"
+#include "mads/mads.h"
+#include "mads/events.h"
+
+namespace MADS {
+
+EventsManager::EventsManager(MADSEngine *vm) {
+ _vm = vm;
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/events.h b/engines/mads/events.h
new file mode 100644
index 0000000000..ea52c7ab9c
--- /dev/null
+++ b/engines/mads/events.h
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MADS_EVENTS_H
+#define MADS_EVENTS_H
+
+#include "common/scummsys.h"
+
+namespace MADS {
+
+class MADSEngine;
+
+class EventsManager {
+private:
+ MADSEngine *_vm;
+public:
+ EventsManager(MADSEngine *vm);
+
+ void handleEvents() { /* TODO */ }
+};
+
+} // End of namespace MADS
+
+#endif /* MADS_EVENTS_H */
diff --git a/engines/mads/mads.cpp b/engines/mads/mads.cpp
index 25730fa9e4..9998bc02e9 100644
--- a/engines/mads/mads.cpp
+++ b/engines/mads/mads.cpp
@@ -20,35 +20,57 @@
*
*/
-#include "mads/mads.h"
-#include "mads/sound.h"
#include "common/scummsys.h"
#include "common/config-manager.h"
#include "common/debug-channels.h"
-#include "engines/util.h"
#include "common/events.h"
+#include "engines/util.h"
+#include "mads/mads.h"
+#include "mads/resources.h"
+#include "mads/sound.h"
+#include "mads/msurface.h"
+#include "mads/msprite.h"
namespace MADS {
-MADSEngine *g_vm;
-
MADSEngine::MADSEngine(OSystem *syst, const MADSGameDescription *gameDesc) :
- Engine(syst), _randomSource("MADS") {
- DebugMan.addDebugChannel(kDebugPath, "Path", "Pathfinding debug level");
- DebugMan.addDebugChannel(kDebugScripts, "scripts", "Game scripts");
+ _gameDescription(gameDesc), Engine(syst), _randomSource("MADS") {
+
+ // Initialise fields
+ _easyMouse = true;
+ _invObjectStill = false;
+ _textWindowStill = false;
+ _palette = nullptr;
+ _resources = nullptr;
+ _screen = nullptr;
+ _sound = nullptr;
}
MADSEngine::~MADSEngine() {
+ delete _events;
+ delete _resources;
+ delete _screen;
+ delete _sound;
}
void MADSEngine::initialise() {
- _soundManager.setVm(this, _mixer);
+ // Set up debug channels
+ DebugMan.addDebugChannel(kDebugPath, "Path", "Pathfinding debug level");
+ DebugMan.addDebugChannel(kDebugScripts, "scripts", "Game scripts");
+
+ // Initial sub-system engine references
+ MSurface::setVm(this);
+ MSprite::setVm(this);
+
+ _events = new EventsManager(this);
+ _resources = new ResourcesManager(this);
+ _screen = MSurface::init();
+ _sound = new SoundManager(this, _mixer);
}
Common::Error MADSEngine::run() {
initGraphics(320, 200, false);
initialise();
- _soundManager.test();
Common::Event e;
while (!shouldQuit()) {
diff --git a/engines/mads/mads.h b/engines/mads/mads.h
index b54c46ea96..8ca181ea02 100644
--- a/engines/mads/mads.h
+++ b/engines/mads/mads.h
@@ -30,6 +30,9 @@
#include "common/util.h"
#include "engines/engine.h"
#include "graphics/surface.h"
+#include "mads/events.h"
+#include "mads/msurface.h"
+#include "mads/resources.h"
#include "mads/sound.h"
/**
@@ -46,13 +49,22 @@ namespace MADS {
#define DEBUG_INTERMEDIATE 2
#define DEBUG_DETAILED 3
-#define MAX_RESOLVE 1000
-
enum MADSDebugChannels {
kDebugPath = 1 << 0,
kDebugScripts = 1 << 1
};
+enum {
+ GType_RexNebular = 0,
+ GType_DragonSphere = 1,
+ GType_Phantom = 2,
+ GType_Riddle = 3
+};
+
+enum {
+ GF_MADS = 1 << 0,
+ GF_M4 = 1 << 1
+};
struct MADSGameDescription;
@@ -61,14 +73,27 @@ class MADSEngine : public Engine {
private:
const MADSGameDescription *_gameDescription;
Common::RandomSource _randomSource;
- SoundManager _soundManager;
+ 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:
+ EventsManager *_events;
+ Palette *_palette;
+ ResourcesManager *_resources;
+ MSurface *_screen;
+ SoundManager *_sound;
+
+public:
MADSEngine(OSystem *syst, const MADSGameDescription *gameDesc);
virtual ~MADSEngine();
@@ -76,7 +101,8 @@ public:
Common::Language getLanguage() const;
Common::Platform getPlatform() const;
uint16 getVersion() const;
- bool getIsDemo() const;
+ uint32 getGameID() const;
+ uint32 getGameFeatures() const;
int getRandomNumber(int maxNumber);
};
diff --git a/engines/mads/module.mk b/engines/mads/module.mk
index d8a26a2704..7ac919f246 100644
--- a/engines/mads/module.mk
+++ b/engines/mads/module.mk
@@ -1,10 +1,17 @@
MODULE := engines/mads
MODULE_OBJS := \
+ compression.o \
detection.o \
+ events.o \
+ mads.o \
+ msprite.o \
+ msurface.o \
+ palette.o \
+ resources.o \
sound.o \
sound_nebular.o \
- mads.o
+ sprite.o
# This module can be built as a plugin
ifeq ($(ENABLE_MADS), DYNAMIC_PLUGIN)
diff --git a/engines/mads/msprite.cpp b/engines/mads/msprite.cpp
new file mode 100644
index 0000000000..60c22d8c06
--- /dev/null
+++ b/engines/mads/msprite.cpp
@@ -0,0 +1,207 @@
+/* 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
+};
+
+MADSEngine *MSprite::_vm;
+
+MSprite *MSprite::init(MSurface &s) {
+ if (_vm->getGameFeatures() & GF_MADS) {
+ return new MSpriteMADS(s);
+ } else {
+ return new MSpriteM4(s);
+ }
+}
+
+MSprite *MSprite::init(Common::SeekableReadStream *source, const Common::Point &offset,
+ int widthVal, int heightVal, bool decodeRle, uint8 encodingVal) {
+
+ if (_vm->getGameFeatures() & GF_MADS) {
+ return new MSpriteMADS(source, offset, widthVal, heightVal, decodeRle, encodingVal);
+ } else {
+ return new MSpriteM4(source, offset, widthVal, heightVal, decodeRle, encodingVal);
+ }
+}
+
+MSprite::MSprite(MSurface &s): _surface(s) {
+ _encoding = 0;
+}
+
+MSprite::MSprite(Common::SeekableReadStream *source, const Common::Point &offset,
+ int widthVal, int heightVal, bool decodeRle, uint8 encodingVal)
+ : _surface(*MSurface::init(widthVal, heightVal)),
+ _encoding(encodingVal), _offset(offset) {
+
+ // Load the sprite data
+ load(source, widthVal, heightVal, decodeRle);
+}
+
+MSprite::~MSprite() {
+}
+
+/*------------------------------------------------------------------------*/
+
+void MSpriteMADS::load(Common::SeekableReadStream *stream, int widthVal, int heightVal,
+ bool decodeRle) {
+ loadSprite(stream);
+}
+
+// TODO: The sprite outlines (pixel value 0xFD) are not shown
+void MSpriteMADS::loadSprite(Common::SeekableReadStream *source) {
+ byte *outp, *lineStart;
+ bool newLine = false;
+
+ outp = _surface.getData();
+ lineStart = _surface.getData();
+
+ while (1) {
+ byte cmd1, cmd2, count, pixel;
+
+ if (newLine) {
+ outp = lineStart + _surface.w;
+ 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;
+ }
+ }
+ }
+ }
+}
+
+/*------------------------------------------------------------------------*/
+
+void MSpriteM4::load(Common::SeekableReadStream *stream, int widthVal, int heightVal,
+ bool decodeRle) {
+ if (decodeRle) {
+ loadRle(stream);
+ } else {
+ // Raw sprite data, load directly
+ byte *dst = _surface.getData();
+ stream->read(dst, widthVal * heightVal);
+ }
+}
+
+void MSpriteM4::loadRle(Common::SeekableReadStream* rleData) {
+ byte *dst = _surface.getData();
+ for (;;) {
+ byte len = rleData->readByte();
+ if (len == 0) {
+ len = rleData->readByte();
+ if (len <= kMarker) {
+ if (len == kEndOfSprite)
+ break;
+ } else {
+ while (len--) {
+ *dst++ = rleData->readByte();
+ }
+ }
+ } else {
+ byte value = rleData->readByte();
+ while (len--)
+ *dst++ = value;
+ }
+ }
+}
+
+void MSpriteM4::loadDeltaRle(Common::SeekableReadStream* rleData, int destX, int destY) {
+ int lineNum = 0;
+ byte *dst = _surface.getBasePtr(destX, destY);
+
+ for (;;) {
+ byte len = rleData->readByte();
+ if (len == 0) {
+ len = rleData->readByte();
+ if (len <= kMarker) {
+ if (len == kEndOfLine) {
+ dst = _surface.getBasePtr(destX, destY + lineNum);
+ lineNum++;
+ } else if (len == kEndOfSprite)
+ break;
+ } else {
+ while (len--) {
+ byte pixel = rleData->readByte();
+ if (pixel == 0)
+ dst++;
+ else
+ *dst++ = pixel;
+ /* NOTE: The change below behaved differently than the old code,
+ so I put the old code back in again above.
+ If the pixel value is 0, nothing should be written to the
+ output buffer, since 0 means transparent. */
+ //*dst++ = (pixel == 0xFD) ? 0 : pixel;
+ }
+ }
+ } else {
+ byte value = rleData->readByte();
+ if (value == 0)
+ dst += len;
+ else
+ while (len--)
+ *dst++ = value;
+ }
+ }
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/msprite.h b/engines/mads/msprite.h
new file mode 100644
index 0000000000..c49aac4fba
--- /dev/null
+++ b/engines/mads/msprite.h
@@ -0,0 +1,152 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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:
+ MSprite *init(MSurface &s);
+ MSprite *init(Common::SeekableReadStream *source, const Common::Point &offset, int widthVal,
+ int heightVal, bool decodeRle = true, uint8 encodingVal = 0);
+protected:
+ static MADSEngine *_vm;
+
+ MSprite(MSurface &s);
+ MSprite(Common::SeekableReadStream *source, const Common::Point &offset,
+ int widthVal, int heightVal, bool decodeRle = true, uint8 encodingVal = 0);
+
+ virtual void load(Common::SeekableReadStream *stream, int widthVal, int heightVal, bool decodeRle) {}
+public:
+ static void setVm(MADSEngine *vm) { _vm = vm; }
+ virtual ~MSprite();
+
+ MSurface &_surface;
+ Common::Point _pos;
+ Common::Point _offset;
+ uint8 _encoding;
+};
+
+class MSpriteMADS: public MSprite {
+ friend class MSprite;
+private:
+ void loadSprite(Common::SeekableReadStream *source);
+protected:
+ MSpriteMADS(MSurface &s): MSprite(s) {}
+ MSpriteMADS(Common::SeekableReadStream *source, const Common::Point &offset,
+ int widthVal, int heightVal, bool decodeRle = true, uint8 encodingVal = 0):
+ MSprite(source, offset, widthVal, heightVal, decodeRle, encodingVal) {}
+
+ virtual void load(Common::SeekableReadStream *stream, int widthVal, int heightVal, bool decodeRle);
+};
+
+class MSpriteM4: public MSprite {
+ friend class MSprite;
+private:
+ // Loads a sprite from the given stream, and optionally decompresses the RLE-encoded data
+ // Loads an RLE compressed sprite; the surface must have been created before
+ void loadRle(Common::SeekableReadStream *rleData);
+ void loadDeltaRle(Common::SeekableReadStream *rleData, int destX, int destY);
+protected:
+ MSpriteM4(MSurface &s): MSprite(s) {}
+ MSpriteM4(Common::SeekableReadStream *source, const Common::Point &offset,
+ int widthVal, int heightVal, bool decodeRle = true, uint8 encodingVal = 0):
+ MSprite(source, offset, widthVal, heightVal, decodeRle, encodingVal) {}
+
+ virtual void load(Common::SeekableReadStream *stream, int widthVal, int heightVal, bool decodeRle);
+};
+
+} // 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..810f9326f7
--- /dev/null
+++ b/engines/mads/msurface.cpp
@@ -0,0 +1,690 @@
+/* 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/mads.h"
+#include "mads/compression.h"
+#include "mads/msurface.h"
+#include "mads/msprite.h"
+
+namespace MADS {
+
+MADSEngine *MSurface::_vm = nullptr;
+
+MSurface *MSurface::init(bool isScreen) {
+ if (_vm->getGameID() == GType_RexNebular) {
+ return new MSurfaceNebular(isScreen);
+ } else if (_vm->getGameFeatures() & GF_MADS) {
+ return new MSurfaceMADS(isScreen);
+ } else {
+ return new MSurfaceM4(isScreen);
+ }
+}
+
+MSurface *MSurface::init(int w, int h) {
+ if (_vm->getGameID() == GType_RexNebular) {
+ return new MSurfaceNebular(w, h);
+ } else if (_vm->getGameFeatures() & GF_MADS) {
+ return new MSurfaceMADS(w, h);
+ } else {
+ return new MSurfaceM4(w, h);
+ }
+}
+
+MSurface::MSurface(bool isScreen) {
+ create(g_system->getWidth(), g_system->getHeight());
+ _isScreen = isScreen;
+}
+
+MSurface::MSurface(int Width, int Height) {
+ create(Width, Height);
+ _isScreen = false;
+}
+
+void MSurface::vLine(int x, int y1, int y2) {
+ Graphics::Surface::vLine(x, y1, y2, _color);
+}
+
+void MSurface::hLine(int x1, int x2, int y) {
+ Graphics::Surface::hLine(x1, y, x2, _color);
+}
+
+void MSurface::vLineXor(int x, int y1, int y2) {
+ // Clipping
+ if (x < 0 || x >= w)
+ return;
+
+ if (y2 < y1)
+ SWAP(y2, y1);
+
+ if (y1 < 0)
+ y1 = 0;
+ if (y2 >= h)
+ y2 = h - 1;
+
+ byte *ptr = (byte *)getBasePtr(x, y1);
+ while (y1++ <= y2) {
+ *ptr ^= 0xFF;
+ ptr += pitch;
+ }
+
+}
+
+void MSurface::hLineXor(int x1, int x2, int y) {
+ // Clipping
+ if (y < 0 || y >= h)
+ return;
+
+ if (x2 < x1)
+ SWAP(x2, x1);
+
+ if (x1 < 0)
+ x1 = 0;
+ if (x2 >= w)
+ x2 = w - 1;
+
+ if (x2 < x1)
+ return;
+
+ byte *ptr = (byte *)getBasePtr(x1, y);
+ while (x1++ <= x2)
+ *ptr++ ^= 0xFF;
+
+}
+
+void MSurface::line(int x1, int y1, int x2, int y2, byte color) {
+ Graphics::Surface::drawLine(x1, y1, x2, y2, color);
+}
+
+
+void MSurface::frameRect(int x1, int y1, int x2, int y2) {
+ Graphics::Surface::frameRect(Common::Rect(x1, y1, x2, y2), _color);
+}
+
+void MSurface::fillRect(int x1, int y1, int x2, int y2) {
+ Graphics::Surface::fillRect(Common::Rect(x1, y1, x2, y2), _color);
+}
+
+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(int x, int y, 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);
+
+ /*
+ printf("MSurface::drawSprite() info.width = %d; info.scaleX = %d; info.height = %d; info.scaleY = %d; scaledWidth = %d; scaledHeight = %d\n",
+ info.width, info.scaleX, info.height, info.scaleY, scaledWidth, scaledHeight); fflush(stdout);
+ */
+
+ 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;
+ }
+
+ //printf("MSurface::drawSprite() width = %d; height = %d; scaledWidth = %d; scaledHeight = %d\n", info.width, info.height, scaledWidth, scaledHeight); fflush(stdout);
+
+ // 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->_surface.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 widthVal = scaledWidth;
+ byte *tempSrc = src;
+ int startX = clipX;
+ while (widthVal > 0) {
+ byte pixel = *tempSrc++;
+ curErrX -= info.scaleX;
+ while (curErrX < 0) {
+ if (startX == 0) {
+ *lineDst++ = pixel;
+ widthVal--;
+ } 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].r * pixel) >> 10, 0, 31);
+ g = CLIP((info.palette[destPixel].g * pixel) >> 10, 0, 31);
+ b = CLIP((info.palette[destPixel].b * 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;
+
+}
+
+// Surface methods
+
+byte *MSurface::getData() {
+ return (byte *)Graphics::Surface::getPixels();
+}
+
+byte *MSurface::getBasePtr(int x, int y) {
+ return (byte *)Graphics::Surface::getBasePtr(x, y);
+}
+
+void MSurface::freeData() {
+}
+
+void MSurface::empty() {
+ Common::fill(getBasePtr(0, 0), getBasePtr(w, h), _vm->_palette->BLACK);
+}
+
+void MSurface::frameRect(const Common::Rect &r, uint8 color) {
+ Graphics::Surface::frameRect(r, color);
+}
+
+void MSurface::fillRect(const Common::Rect &r, uint8 color) {
+ Graphics::Surface::fillRect(r, color);
+}
+
+void MSurface::copyFrom(MSurface *src, const Common::Rect &srcBounds, int destX, int destY,
+ int transparentColor) {
+ // Validation of the rectangle and position
+ 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->width() * copyRect.top + copyRect.left);
+ byte *destPtr = (byte *)pixels + (destY * width()) + 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->width();
+ destPtr += width();
+ }
+
+ src->freeData();
+}
+
+#undef COL_TRANS
+
+void MSurface::translate(RGBList *list, bool isTransparent) {
+ byte *p = getBasePtr(0, 0);
+ byte *palIndexes = list->palIndexes();
+
+ for (int i = 0; i < width() * height(); ++i, ++p) {
+ if (!isTransparent || (*p != 0)) {
+ assert(*p < list->size());
+ *p = palIndexes[*p];
+ }
+ }
+
+ freeData();
+}
+
+/*------------------------------------------------------------------------*/
+
+void MSurfaceMADS::loadCodes(Common::SeekableReadStream *source) {
+ if (!source) {
+ free();
+ return;
+ }
+
+ uint16 widthVal = 320;
+ uint16 heightVal = 156;
+ byte *walkMap = new byte[source->size()];
+
+ create(widthVal, heightVal);
+ source->read(walkMap, source->size());
+
+ byte *ptr = (byte *)getBasePtr(0, 0);
+
+ for (int y = 0; y < heightVal; y++) {
+ for (int x = 0; x < widthVal; x++) {
+ int ofs = x + (y * widthVal);
+ if ((walkMap[ofs / 8] << (ofs % 8)) & 0x80)
+ *ptr++ = 1; // walkable
+ else
+ *ptr++ = 0;
+ }
+ }
+}
+
+void MSurfaceMADS::loadBackground(int roomNumber, RGBList **palData) {
+ // clear previous data
+ empty();
+
+ // Get a MadsPack reference to the tile set and mapping
+ char resourceName[20];
+ int i;
+
+ // Uncompressed tile map resource
+ sprintf(resourceName, "rm%d.mm", roomNumber);
+ MadsPack tileMapFile(resourceName, _vm);
+ Common::SeekableReadStream *mapStream = tileMapFile.getItemStream(0);
+
+ // Get the details of the tiles and map
+ mapStream->readUint32LE();
+ int tileCountX = mapStream->readUint16LE();
+ int tileCountY = mapStream->readUint16LE();
+ int tileWidthMap = mapStream->readUint16LE();
+ int tileHeightMap = mapStream->readUint16LE();
+ int screenWidth = mapStream->readUint16LE();
+ int screenHeight = mapStream->readUint16LE();
+ int tileCountMap = tileCountX * tileCountY;
+ delete mapStream;
+
+ // Obtain tile map information
+ typedef Common::List<Common::SharedPtr<MSurface> > TileSetList;
+ typedef TileSetList::iterator TileSetIterator;
+ TileSetList tileSet;
+ uint16 *tileMap = new uint16[tileCountMap];
+ mapStream = tileMapFile.getItemStream(1);
+ for (i = 0; i < tileCountMap; ++i)
+ tileMap[i] = mapStream->readUint16LE();
+ delete mapStream;
+
+ _vm->_resources->toss(resourceName);
+
+ // --------------------------------------------------------------------------------
+
+ // Tile map data, which needs to be kept compressed, as the tile offsets refer to
+ // the compressed data. Each tile is then uncompressed separately
+ sprintf(resourceName, "rm%d.tt", roomNumber);
+ Common::SeekableReadStream *tileDataComp = _vm->_resources->get(resourceName);
+ MadsPack tileData(tileDataComp);
+ Common::SeekableReadStream *tileDataUncomp = tileData.getItemStream(0);
+
+ // Validate that the data matches between the tiles and tile map file and is valid
+ int tileCount = tileDataUncomp->readUint16LE();
+ int tileWidth = tileDataUncomp->readUint16LE();
+ int tileHeight = tileDataUncomp->readUint16LE();
+ delete tileDataUncomp;
+ assert(tileCountMap == tileCount);
+ assert(tileWidth == tileWidthMap);
+ assert(tileHeight == tileHeightMap);
+ assert(screenWidth == _vm->_screen->width());
+ assert(screenHeight <= _vm->_screen->height());
+
+ // --------------------------------------------------------------------------------
+
+ // Get the palette to use
+ tileDataUncomp = tileData.getItemStream(2);
+ // Set palette
+ if (!palData) {
+ _vm->_palette->setMadsPalette(tileDataUncomp, 4);
+ } else {
+ int numColors;
+ RGB8 *rgbList = _vm->_palette->decodeMadsPalette(tileDataUncomp, &numColors);
+ *palData = new RGBList(numColors, rgbList, true);
+ }
+ delete tileDataUncomp;
+
+ // --------------------------------------------------------------------------------
+
+ // Get tile data
+
+ tileDataUncomp = tileData.getItemStream(1);
+ FabDecompressor fab;
+ uint32 compressedTileDataSize = 0;
+
+ for (i = 0; i < tileCount; i++) {
+ tileDataUncomp->seek(i * 4, SEEK_SET);
+ uint32 tileOfs = tileDataUncomp->readUint32LE();
+ MSurface *newTile = MSurface::init(tileWidth, tileHeight);
+
+ if (i == tileCount - 1)
+ compressedTileDataSize = tileDataComp->size() - tileOfs;
+ else
+ compressedTileDataSize = tileDataUncomp->readUint32LE() - tileOfs;
+
+ //printf("Tile: %i, compressed size: %i\n", i, compressedTileDataSize);
+
+ newTile->empty();
+
+ byte *compressedTileData = new byte[compressedTileDataSize];
+
+ tileDataComp->seek(tileData.getDataOffset() + tileOfs, SEEK_SET);
+ tileDataComp->read(compressedTileData, compressedTileDataSize);
+
+ fab.decompress(compressedTileData, compressedTileDataSize, newTile->getData(),
+ tileWidth * tileHeight);
+ tileSet.push_back(TileSetList::value_type(newTile));
+ delete[] compressedTileData;
+ }
+
+ delete tileDataUncomp;
+
+ // --------------------------------------------------------------------------------
+
+ // Loop through the mapping data to place the tiles on the screen
+
+ uint16 *tIndex = &tileMap[0];
+ for (int y = 0; y < tileCountY; y++) {
+ for (int x = 0; x < tileCountX; x++) {
+ int tileIndex = *tIndex++;
+ assert(tileIndex < tileCount);
+ TileSetIterator tile = tileSet.begin();
+ for (i = 0; i < tileIndex; i++)
+ ++tile;
+ ((*tile).get())->copyTo(this, x * tileWidth, y * tileHeight);
+ }
+ }
+ tileSet.clear();
+ _vm->_resources->toss(resourceName);
+}
+
+void MSurfaceMADS::loadInterface(int index, RGBList **palData) {
+ char resourceName[20];
+ sprintf(resourceName, "i%d.int", index);
+ MadsPack intFile(resourceName, _vm);
+ RGB8 *palette = new RGB8[16];
+
+ // Chunk 0, palette
+ Common::SeekableReadStream *intStream = intFile.getItemStream(0);
+
+ for (int i = 0; i < 16; i++) {
+ palette[i].r = intStream->readByte() << 2;
+ palette[i].g = intStream->readByte() << 2;
+ palette[i].b = intStream->readByte() << 2;
+ intStream->readByte();
+ intStream->readByte();
+ intStream->readByte();
+ }
+ *palData = new RGBList(16, palette, true);
+ delete intStream;
+
+ // Chunk 1, data
+ intStream = intFile.getItemStream(1);
+ create(320, 44);
+ intStream->read(pixels, 320 * 44);
+ delete intStream;
+}
+
+/*------------------------------------------------------------------------*/
+
+void MSurfaceNebular::loadBackground(int roomNumber, RGBList **palData) {
+ // clear previous data
+ empty();
+
+ Common::String resourceName = Common::String::format("rm%d.art", roomNumber);
+ Common::SeekableReadStream *stream = _vm->_resources->get(resourceName);
+ loadBackgroundStream(stream, palData);
+
+ _vm->_resources->toss(resourceName);
+}
+
+void MSurfaceNebular::loadBackgroundStream(Common::SeekableReadStream *source, RGBList **palData) {
+ MadsPack packData(source);
+ Common::MemoryReadStream *sourceUnc = packData.getItemStream(0);
+
+ int sceneWidth = sourceUnc->readUint16LE();
+ int sceneHeight = sourceUnc->readUint16LE();
+ int sceneSize = sceneWidth * sceneHeight;
+ if (sceneWidth > this->width()) {
+ warning("Background width is %i, too large to fit in screen. Setting it to %i", sceneWidth, this->width());
+ sceneWidth = this->width();
+ sceneSize = sceneWidth * sceneHeight;
+ }
+ if (sceneHeight > this->height()) {
+ warning("Background height is %i, too large to fit in screen.Setting it to %i", sceneHeight, this->height());
+ sceneHeight = this->height();
+ sceneSize = sceneWidth * sceneHeight;
+ }
+
+ // Set palette
+ if (!palData) {
+ _vm->_palette->setMadsPalette(sourceUnc, 4);
+ } else {
+ int numColors;
+ RGB8 *rgbList = _vm->_palette->decodeMadsPalette(sourceUnc, &numColors);
+ *palData = new RGBList(numColors, rgbList, true);
+ }
+ delete sourceUnc;
+
+ // Get the raw data for the background
+ sourceUnc = packData.getItemStream(1);
+ assert((int)sourceUnc->size() >= sceneSize);
+
+ byte *pData = (byte *)pixels;
+ sourceUnc->read(pData, sceneSize);
+
+ freeData();
+ delete sourceUnc;
+}
+
+/*------------------------------------------------------------------------*/
+
+void MSurfaceM4::loadCodes(Common::SeekableReadStream *source) {
+ if (!source) {
+ free();
+ return;
+ }
+
+ uint16 widthVal = source->readUint16LE();
+ uint16 heightVal = source->readUint16LE();
+
+ create(widthVal, heightVal);
+ source->read(pixels, widthVal * heightVal);
+}
+
+void MSurfaceM4::loadBackground(int roomNumber, RGBList **palData) {
+ if (palData)
+ *palData = NULL;
+ Common::String resourceName = Common::String::format("%i.tt", roomNumber);
+ Common::SeekableReadStream *stream = _vm->_resources->get(resourceName);
+ loadBackgroundStream(stream);
+
+ _vm->_resources->toss(resourceName);
+}
+
+void MSurfaceM4::loadBackgroundStream(Common::SeekableReadStream *source) {
+ MSurface *tileBuffer = MSurface::init();
+ uint curTileX = 0, curTileY = 0;
+ int clipX = 0, clipY = 0;
+ RGB8 palette[256];
+
+ source->skip(4);
+ /*uint32 size =*/ source->readUint32LE();
+ uint32 widthVal = source->readUint32LE();
+ uint32 heightVal = source->readUint32LE();
+ uint32 tilesX = source->readUint32LE();
+ uint32 tilesY = source->readUint32LE();
+ uint32 tileWidth = source->readUint32LE();
+ uint32 tileHeight = source->readUint32LE();
+ uint8 blackIndex = 0;
+
+ // BGR data, which is converted to RGB8
+ for (uint i = 0; i < 256; i++) {
+ palette[i].b = source->readByte() << 2;
+ palette[i].g = source->readByte() << 2;
+ palette[i].r = source->readByte() << 2;
+ palette[i].u = source->readByte() << 2;
+
+ if ((blackIndex == 0) && !palette[i].r && !palette[i].g && !palette[i].b)
+ blackIndex = i;
+ }
+
+ _vm->_palette->setPalette(palette, 0, 256);
+
+ // resize or create the surface
+ // Note that the height of the scene in game scenes is smaller than the screen height,
+ // as the bottom part of the screen is the inventory
+ assert(width() == (int)widthVal);
+
+ tileBuffer->create(tileWidth, tileHeight);
+
+ for (curTileY = 0; curTileY < tilesY; curTileY++) {
+ clipY = MIN(heightVal, (1 + curTileY) * tileHeight) - (curTileY * tileHeight);
+
+ for (curTileX = 0; curTileX < tilesX; curTileX++) {
+ clipX = MIN(widthVal, (1 + curTileX) * tileWidth) - (curTileX * tileWidth);
+
+ // Read a tile and copy it to the destination surface
+ source->read(tileBuffer->getData(), tileWidth * tileHeight);
+ Common::Rect srcBounds(0, 0, clipX, clipY);
+ copyFrom(tileBuffer, srcBounds, curTileX * tileWidth, curTileY * tileHeight);
+ }
+ }
+
+ if (heightVal < (uint)height())
+ fillRect(Common::Rect(0, heightVal, width(), height()), blackIndex);
+
+ delete tileBuffer;
+}
+
+/*------------------------------------------------------------------------*/
+
+void MSurfaceRiddle::loadBackground(const Common::String &sceneName) {
+ char resourceName[20];
+ Common::SeekableReadStream *stream;
+ // Loads a Riddle scene
+ Common::String resName = Common::String::format("%s.tt", sceneName.c_str());
+ stream = _vm->_resources->get(resourceName);
+
+ loadBackgroundStream(stream);
+
+ _vm->_resources->toss(resourceName);
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/msurface.h b/engines/mads/msurface.h
new file mode 100644
index 0000000000..9bb651b02b
--- /dev/null
+++ b/engines/mads/msurface.h
@@ -0,0 +1,185 @@
+/* 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;
+
+struct SpriteInfo {
+ MSprite *sprite;
+ int hotX, hotY;
+ int width, height;
+ int scaleX, scaleY;
+ uint8 encoding;
+ byte *inverseColorTable;
+ RGB8 *palette;
+};
+
+class MSurface : public Graphics::Surface {
+public:
+ static MADSEngine *_vm;
+
+ /**
+ * Sets the engine reference
+ */
+ static void setVm(MADSEngine *vm) { _vm = vm; }
+
+ /**
+ * Create a new surface the same size as the screen.
+ * @param isScreen Set to true for the screen surface
+ */
+ static MSurface *init(bool isScreen = false);
+
+ /**
+ * Create a surface
+ */
+ static MSurface *init(int w, int h);
+private:
+ byte _color;
+ bool _isScreen;
+protected:
+ MSurface(bool isScreen = false);
+ MSurface(int w, int h);
+public:
+ void create(int w, int h) {
+ Graphics::Surface::create(w, h, Graphics::PixelFormat::createFormatCLUT8());
+ }
+
+ void setColor(byte value) { _color = value; }
+ byte getColor() { return _color; }
+ void vLine(int x, int y1, int y2);
+ void hLine(int x1, int x2, int y);
+ void vLineXor(int x, int y1, int y2);
+ void hLineXor(int x1, int x2, int y);
+ void line(int x1, int y1, int x2, int y2, byte color);
+ void frameRect(int x1, int y1, int x2, int y2);
+ void fillRect(int x1, int y1, int x2, int y2);
+
+ static int scaleValue(int value, int scale, int err);
+ void drawSprite(int x, int y, SpriteInfo &info, const Common::Rect &clipRect);
+
+ // Surface methods
+ int width() { return w; }
+ int height() { return h; }
+ void setSize(int sizeX, int sizeY);
+ byte *getData();
+ byte *getBasePtr(int x, int y);
+ void freeData();
+ void empty();
+ void frameRect(const Common::Rect &r, uint8 color);
+ void fillRect(const Common::Rect &r, uint8 color);
+ void copyFrom(MSurface *src, const Common::Rect &srcBounds, int destX, int destY,
+ int transparentColor = -1);
+
+ void update() {
+ if (_isScreen) {
+ g_system->copyRectToScreen((const byte *)pixels, pitch, 0, 0, w, h);
+ g_system->updateScreen();
+ }
+ }
+
+ // copyTo methods
+ void copyTo(MSurface *dest, int transparentColor = -1) {
+ dest->copyFrom(this, Common::Rect(width(), height()), 0, 0, transparentColor);
+ }
+ void copyTo(MSurface *dest, int x, int y, int transparentColor = -1) {
+ dest->copyFrom(this, Common::Rect(width(), height()), x, y, transparentColor);
+ }
+ void copyTo(MSurface *dest, const Common::Rect &srcBounds, int destX, int destY,
+ int transparentColor = -1) {
+ dest->copyFrom(this, srcBounds, destX, destY, transparentColor);
+ }
+
+ void translate(RGBList *list, bool isTransparent = false);
+
+ // Base virtual methods
+ virtual void loadBackground(const Common::String &sceneName) {}
+ virtual void loadBackground(int roomNumber, RGBList **palData) = 0;
+ virtual void loadBackground(Common::SeekableReadStream *source, RGBList **palData) {}
+ virtual void loadCodes(Common::SeekableReadStream *source) = 0;
+ virtual void loadInterface(int index, RGBList **palData) {}
+};
+
+class MSurfaceMADS: public MSurface {
+ friend class MSurface;
+protected:
+ MSurfaceMADS(bool isScreen = false): MSurface(isScreen) {}
+ MSurfaceMADS(int w, int h): MSurface(w, h) {}
+public:
+ virtual void loadCodes(Common::SeekableReadStream *source);
+ virtual void loadBackground(const Common::String &sceneName) {}
+ virtual void loadBackground(int roomNumber, RGBList **palData);
+ virtual void loadInterface(int index, RGBList **palData);
+};
+
+class MSurfaceNebular: public MSurfaceMADS {
+ friend class MSurface;
+protected:
+ MSurfaceNebular(bool isScreen = false): MSurfaceMADS(isScreen) {}
+ MSurfaceNebular(int w, int h): MSurfaceMADS(w, h) {}
+private:
+ void loadBackgroundStream(Common::SeekableReadStream *source, RGBList **palData);
+public:
+ virtual void loadBackground(int roomNumber, RGBList **palData);
+};
+
+class MSurfaceM4: public MSurface {
+ friend class MSurface;
+protected:
+ MSurfaceM4(bool isScreen = false): MSurface(isScreen) {}
+ MSurfaceM4(int w, int h): MSurface(w, h) {}
+
+ void loadBackgroundStream(Common::SeekableReadStream *source);
+public:
+ virtual void loadCodes(Common::SeekableReadStream *source);
+ virtual void loadBackground(int roomNumber, RGBList **palData);
+};
+
+class MSurfaceRiddle: public MSurfaceM4 {
+ friend class MSurface;
+protected:
+ MSurfaceRiddle(bool isScreen = false): MSurfaceM4(isScreen) {}
+ MSurfaceRiddle(int w, int h): MSurfaceM4(w, h) {}
+public:
+ virtual void loadBackground(const Common::String &sceneName);
+};
+/*
+ void rexLoadBackground(Common::SeekableReadStream *source, RGBList **palData = NULL);
+ void madsLoadBackground(int roomNumber, RGBList **palData = NULL);
+ void m4LoadBackground(Common::SeekableReadStream *source);
+
+ void madsloadInterface(int index, RGBList **palData);
+
+ */
+
+} // End of namespace MADS
+
+#endif /* MADS_MSURFACE_H */
diff --git a/engines/mads/palette.cpp b/engines/mads/palette.cpp
new file mode 100644
index 0000000000..cfd8568407
--- /dev/null
+++ b/engines/mads/palette.cpp
@@ -0,0 +1,289 @@
+/* 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 {
+
+RGBList::RGBList(int numEntries, RGB8 *srcData, bool freeData) {
+ _size = numEntries;
+ assert(numEntries <= 256);
+
+ if (srcData == NULL) {
+ _data = new RGB8[numEntries];
+ _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;
+}
+
+/*------------------------------------------------------------------------*/
+
+#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[256], 0);
+}
+
+void Palette::setPalette(const byte *colors, uint start, uint num) {
+ g_system->getPaletteManager()->setPalette(colors, start, num);
+ reset();
+}
+
+void Palette::setPalette(const RGB8 *colors, uint start, uint num) {
+ g_system->getPaletteManager()->setPalette((const byte *)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, RGB8 *paletteData) {
+ byte index = 0;
+ int32 minDist = 0x7fffffff;
+ RGB8 palData[256];
+ int Rdiff, Gdiff, Bdiff;
+
+ if (paletteData == NULL) {
+ g_system->getPaletteManager()->grabPalette((byte *)palData, 0, 256);
+ paletteData = &palData[0];
+ }
+
+ for (int palIndex = 0; palIndex < 256; ++palIndex) {
+ Rdiff = r - paletteData[palIndex].r;
+ Gdiff = g - paletteData[palIndex].g;
+ Bdiff = b - paletteData[palIndex].b;
+
+ if (Rdiff * Rdiff + Gdiff * Gdiff + Bdiff * Bdiff < minDist) {
+ minDist = Rdiff * Rdiff + Gdiff * Gdiff + Bdiff * Bdiff;
+ index = (uint8)palIndex;
+ }
+ }
+
+ return (uint8)index;
+}
+
+void Palette::reset() {
+ RGB8 palData[256];
+ g_system->getPaletteManager()->grabPalette((byte *)palData, 0, 256);
+
+ 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::fadeIn(int numSteps, uint delayAmount, RGBList *destPalette) {
+ fadeIn(numSteps, delayAmount, destPalette->data(), destPalette->size());
+}
+
+void Palette::fadeIn(int numSteps, uint delayAmount, RGB8 *destPalette, int numColors) {
+ if (_fading_in_progress)
+ return;
+
+ _fading_in_progress = true;
+ RGB8 blackPalette[256];
+ Common::fill((byte *)&blackPalette[0], (byte *)&blackPalette[256], 0);
+
+ // Initially set the black palette
+ _vm->_palette->setPalette(blackPalette, 0, numColors);
+
+ // Handle the actual fading
+ fadeRange(blackPalette, destPalette, 0, numColors - 1, numSteps, delayAmount);
+
+ _fading_in_progress = false;
+}
+
+RGB8 *Palette::decodeMadsPalette(Common::SeekableReadStream *palStream, int *numColors) {
+ *numColors = palStream->readUint16LE();
+ assert(*numColors <= 252);
+
+ RGB8 *palData = new RGB8[*numColors];
+ Common::fill((byte *)&palData[0], (byte *)&palData[*numColors], 0);
+
+ for (int i = 0; i < *numColors; ++i) {
+ byte r = palStream->readByte();
+ byte g = palStream->readByte();
+ byte b = palStream->readByte();
+ palData[i].r = VGA_COLOR_TRANS(r);
+ palData[i].g = VGA_COLOR_TRANS(g);
+ palData[i].b = VGA_COLOR_TRANS(b);
+
+ // The next 3 bytes are unused
+ palStream->skip(3);
+ }
+
+ return palData;
+}
+
+int Palette::setMadsPalette(Common::SeekableReadStream *palStream, int indexStart) {
+ int colorCount;
+ RGB8 *palData = Palette::decodeMadsPalette(palStream, &colorCount);
+ _vm->_palette->setPalette(palData, indexStart, colorCount);
+ delete palData;
+ return colorCount;
+}
+
+void Palette::setMadsSystemPalette() {
+ // Rex Nebular default system palette
+ resetColorCounts();
+
+ RGB8 palData[4];
+ palData[0].r = palData[0].g = palData[0].b = 0;
+ palData[1].r = palData[1].g = palData[1].b = 0x54;
+ palData[2].r = palData[2].g = palData[2].b = 0xb4;
+ palData[3].r = palData[3].g = palData[3].b = 0xff;
+
+ setPalette(palData, 0, 4);
+ blockRange(0, 4);
+}
+
+void Palette::resetColorCounts() {
+ Common::fill(&_usageCount[0], &_usageCount[256], 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) {
+ RGB8 *data = list->data();
+ byte *palIndexes = list->palIndexes();
+ RGB8 palData[256];
+ g_system->getPaletteManager()->grabPalette((byte *)&palData[0], 0, 256);
+ 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 < 256) {
+ if (_usageCount[palIndex] <= 0)
+ // Palette index is to be skipped
+ continue;
+
+ if ((palData[palIndex].r == data[colIndex].r) &&
+ (palData[palIndex].g == data[colIndex].g) &&
+ (palData[palIndex].b == data[colIndex].b))
+ // Match found
+ break;
+ }
+
+ if (palIndex == 256) {
+ // No match found, so find a free slot to use
+ palIndex = -1;
+ while (++palIndex < 256) {
+ if (_usageCount[palIndex] == 0)
+ break;
+ }
+
+ if (palIndex == 256)
+ error("addRange - Ran out of palette space to allocate");
+
+ palData[palIndex].r = data[colIndex].r;
+ palData[palIndex].g = data[colIndex].g;
+ palData[palIndex].b = data[colIndex].b;
+ paletteChanged = true;
+ }
+
+ palIndexes[colIndex] = palIndex;
+ ++_usageCount[palIndex];
+ }
+
+ if (paletteChanged) {
+ g_system->getPaletteManager()->setPalette((byte *)&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::fadeRange(RGB8 *srcPal, RGB8 *destPal, int startIndex, int endIndex,
+ int numSteps, uint delayAmount) {
+ RGB8 tempPal[256];
+
+ // perform the fade
+ for(int stepCtr = 1; stepCtr <= numSteps; ++stepCtr) {
+ // Delay the specified amount
+ uint32 startTime = g_system->getMillis();
+ while ((g_system->getMillis() - startTime) < delayAmount) {
+ _vm->_events->handleEvents();
+ g_system->delayMillis(10);
+ }
+
+ for (int i = startIndex; i <= endIndex; ++i) {
+ // Handle the intermediate rgb values for fading
+ tempPal[i].r = (byte) (srcPal[i].r + (destPal[i].r - srcPal[i].r) * stepCtr / numSteps);
+ tempPal[i].g = (byte) (srcPal[i].g + (destPal[i].g - srcPal[i].g) * stepCtr / numSteps);
+ tempPal[i].b = (byte) (srcPal[i].b + (destPal[i].b - srcPal[i].b) * stepCtr / numSteps);
+ }
+
+ _vm->_palette->setPalette(&tempPal[startIndex], startIndex, endIndex - startIndex + 1);
+ }
+
+ // Make sure the end palette exactly matches what is wanted
+ _vm->_palette->setPalette(&destPal[startIndex], startIndex, endIndex - startIndex + 1);
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/palette.h b/engines/mads/palette.h
new file mode 100644
index 0000000000..ab03d8d20b
--- /dev/null
+++ b/engines/mads/palette.h
@@ -0,0 +1,110 @@
+/* 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"
+
+namespace MADS {
+
+class MADSEngine;
+
+struct RGB8 {
+ uint8 r, g, b, u;
+};
+
+class RGBList {
+private:
+ int _size;
+ RGB8 *_data;
+ byte *_palIndexes;
+ bool _freeData;
+public:
+ RGBList(int numEntries = 256, RGB8 *srcData = NULL, bool freeData = true);
+ ~RGBList();
+
+ RGB8 *data() { return _data; }
+ byte *palIndexes() { return _palIndexes; }
+ int size() { return _size; }
+};
+
+#define PALETTE_COUNT 256
+
+class Palette {
+private:
+ 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:
+ Palette(MADSEngine *vm);
+
+ void setPalette(const byte *colors, uint start, uint num);
+ void setPalette(const RGB8 *colors, uint start, uint num);
+ void grabPalette(byte *colors, uint start, uint num);
+ void grabPalette(RGB8 *colors, uint start, uint num) {
+ grabPalette((byte *)colors, start, num);
+ }
+ uint8 palIndexFromRgb(byte r, byte g, byte b, RGB8 *paletteData = NULL);
+
+ void fadeIn(int numSteps, uint delayAmount, RGB8 *destPalette, int numColors);
+ void fadeIn(int numSteps, uint delayAmount, RGBList *destPalette);
+ static RGB8 *decodeMadsPalette(Common::SeekableReadStream *palStream, int *numColors);
+ int setMadsPalette(Common::SeekableReadStream *palStream, int indexStart = 0);
+ void setMadsSystemPalette();
+ void fadeRange(RGB8 *srcPal, RGB8 *destPal, int startIndex, int endIndex,
+ int numSteps, uint delayAmount);
+
+ // Methods used for reference counting color usage
+ void resetColorCounts();
+ void blockRange(int startIndex, int size);
+ void addRange(RGBList *list);
+ void deleteRange(RGBList *list);
+ void deleteAllRanges();
+
+ // 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..27b94a9e55
--- /dev/null
+++ b/engines/mads/resources.cpp
@@ -0,0 +1,32 @@
+/* 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/resources.h"
+
+namespace MADS {
+
+ResourcesManager::ResourcesManager(MADSEngine *vm) {
+ _vm = vm;
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/resources.h b/engines/mads/resources.h
new file mode 100644
index 0000000000..6eee0eac88
--- /dev/null
+++ b/engines/mads/resources.h
@@ -0,0 +1,57 @@
+/* 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/stream.h"
+
+namespace MADS {
+
+class MADSEngine;
+
+class ResourcesManager {
+private:
+ MADSEngine *_vm;
+public:
+ ResourcesManager(MADSEngine *vm);
+
+ /**
+ * Return a named resource
+ */
+ Common::SeekableReadStream *get(const Common::String &resourceName) {
+ // TODO
+ return nullptr;
+ }
+
+ /**
+ * Release a previously loaded resource
+ */
+ void toss(const Common::String &resourceName) {
+ // TODO
+ }
+};
+
+} // End of namespace MADS
+
+#endif /* MADS_RESOURCES_H */
diff --git a/engines/mads/sound.cpp b/engines/mads/sound.cpp
index e3590f5ce7..46ba997198 100644
--- a/engines/mads/sound.cpp
+++ b/engines/mads/sound.cpp
@@ -27,7 +27,9 @@
namespace MADS {
-SoundManager::SoundManager() {
+SoundManager::SoundManager(MADSEngine *vm, Audio::Mixer *mixer) {
+ _vm = vm;
+ _mixer = mixer;
_asound = nullptr;
}
@@ -35,11 +37,6 @@ SoundManager::~SoundManager() {
delete _asound;
}
-void SoundManager::setVm(MADSEngine *vm, Audio::Mixer *mixer) {
- _vm = vm;
- _mixer = mixer;
-}
-
void SoundManager::test() {
_asound = new Nebular::ASound1(_mixer);
_asound->command(5);
diff --git a/engines/mads/sound.h b/engines/mads/sound.h
index 20b56ec4a1..cbd6511518 100644
--- a/engines/mads/sound.h
+++ b/engines/mads/sound.h
@@ -38,10 +38,9 @@ private:
Audio::Mixer *_mixer;
Nebular::ASound *_asound;
public:
- SoundManager();
+ SoundManager(MADSEngine *vm, Audio::Mixer *mixer);
~SoundManager();
- void setVm(MADSEngine *vm, Audio::Mixer *mixer);
void test();
void poll();
};
diff --git a/engines/mads/sound_nebular.h b/engines/mads/sound_nebular.h
index f9251c9329..11836e6559 100644
--- a/engines/mads/sound_nebular.h
+++ b/engines/mads/sound_nebular.h
@@ -33,10 +33,10 @@
namespace MADS {
-namespace Nebular {
-
class SoundManager;
+namespace Nebular {
+
/**
* Represents the data for a channel on the Adlib
*/