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