MADS: Initial implementation of MSurface class and dependant classes

This commit is contained in:
Paul Gilbert 2014-02-18 20:08:58 -05:00
parent ece3e9a220
commit 0e46c809d1
19 changed files with 2153 additions and 31 deletions

View File

@ -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

View File

@ -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

View File

@ -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

34
engines/mads/events.cpp Normal file
View File

@ -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

43
engines/mads/events.h Normal file
View File

@ -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 */

View File

@ -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()) {

View File

@ -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,13 +73,26 @@ 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);
};

View File

@ -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)

207
engines/mads/msprite.cpp Normal file
View File

@ -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

152
engines/mads/msprite.h Normal file
View File

@ -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 */

690
engines/mads/msurface.cpp Normal file
View File

@ -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

185
engines/mads/msurface.h Normal file
View File

@ -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 */

289
engines/mads/palette.cpp Normal file
View File

@ -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

110
engines/mads/palette.h Normal file
View File

@ -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 */

View File

@ -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

57
engines/mads/resources.h Normal file
View File

@ -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 */

View File

@ -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);

View File

@ -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();
};

View File

@ -33,10 +33,10 @@
namespace MADS {
namespace Nebular {
class SoundManager;
namespace Nebular {
/**
* Represents the data for a channel on the Adlib
*/