From 40a6c232e9169a9cfe0d6a698b728c8565cd8959 Mon Sep 17 00:00:00 2001 From: Alyssa Milburn Date: Thu, 14 Jul 2011 20:08:06 +0200 Subject: COMPOSER: Add a first attempt at an engine. --- base/plugins.cpp | 3 + configure | 1 + engines/composer/composer.cpp | 889 +++++++++++++++++++++++++++++++++++++++++ engines/composer/composer.h | 197 +++++++++ engines/composer/detection.cpp | 139 +++++++ engines/composer/module.mk | 14 + engines/composer/resource.cpp | 250 ++++++++++++ engines/composer/resource.h | 93 +++++ engines/engines.mk | 5 + 9 files changed, 1591 insertions(+) create mode 100644 engines/composer/composer.cpp create mode 100644 engines/composer/composer.h create mode 100644 engines/composer/detection.cpp create mode 100644 engines/composer/module.mk create mode 100644 engines/composer/resource.cpp create mode 100644 engines/composer/resource.h diff --git a/base/plugins.cpp b/base/plugins.cpp index 4413995b88..4fa1a961da 100644 --- a/base/plugins.cpp +++ b/base/plugins.cpp @@ -97,6 +97,9 @@ public: #if PLUGIN_ENABLED_STATIC(CINE) LINK_PLUGIN(CINE) #endif + #if PLUGIN_ENABLED_STATIC(COMPOSER) + LINK_PLUGIN(COMPOSER) + #endif #if PLUGIN_ENABLED_STATIC(CRUISE) LINK_PLUGIN(CRUISE) #endif diff --git a/configure b/configure index 74541e3d98..b161c5a81e 100755 --- a/configure +++ b/configure @@ -84,6 +84,7 @@ add_engine agi "AGI" yes add_engine agos "AGOS" yes "agos2" add_engine agos2 "AGOS 2 games" yes add_engine cine "Cinematique evo 1" yes +add_engine composer "Magic Composer" no add_engine cruise "Cinematique evo 2" yes add_engine draci "Dragon History" yes add_engine drascula "Drascula: The Vampire Strikes Back" yes diff --git a/engines/composer/composer.cpp b/engines/composer/composer.cpp new file mode 100644 index 0000000000..b2dcc3b36c --- /dev/null +++ b/engines/composer/composer.cpp @@ -0,0 +1,889 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ +#include "common/scummsys.h" + +#include "common/config-manager.h" +#include "common/events.h" +#include "common/file.h" +#include "common/random.h" +#include "common/fs.h" +#include "common/keyboard.h" +#include "common/substream.h" +#include "common/memstream.h" + +#include "graphics/cursorman.h" +#include "graphics/surface.h" +#include "graphics/pixelformat.h" +#include "graphics/palette.h" + +#include "engines/util.h" +#include "engines/advancedDetector.h" + +#include "audio/audiostream.h" +#include "audio/decoders/raw.h" + +#include "composer/composer.h" +#include "composer/resource.h" + +namespace Composer { + +// bitmap compression types +enum { + kBitmapUncompressed = 0, + kBitmapSpp32 = 1, + kBitmapSLW8 = 3, + kBitmapRLESLWM = 4, + kBitmapSLWM = 5 +}; + +// new script ops +enum { + kOpPlusPlus = 0x1, + kOpMinusMinus = 0x2, + kOpAssign = 0x3, + kOpAdd = 0x4, + kOpSubtract = 0x5, + kOpMultiply = 0x6, + kOpDivide = 0x7, + kOpModulo = 0x8, + kOpMaybeAlsoAssign = 0x9, + kOpBooleanAssign = 0xA, + kOpNegate = 0xB, + kOpAnd = 0xC, + kOpOr = 0xD, + kOpXor = 0xE, + kOpNotPositive = 0xF, + kOpSqrt = 0x10, + kOpRandom = 0x11, + kOpExecuteScript = 0x12, + kOpCallFunc = 0x13, + kOpBoolLessThanEq = 0x14, + kOpBoolLessThan = 0x15, + kOpBoolGreaterThanEq = 0x16, + kOpBoolGreaterThan = 0x17, + kOpBoolEqual = 0x18, + kOpBoolNotEqual = 0x19, + kOpSaveArgs = 0x1A, + kOpRestoreArgs = 0x1B, + kOpSetReturnValue = 0x20, + kOpLessThanEq = 0x21, + kOpLessThan = 0x22, + kOpGreaterThanEq = 0x23, + kOpGreaterThan = 0x24, + kOpEqual = 0x25, + kOpNotEqual = 0x26, + kOpJump = 0x80, + kOpJumpIfNot = 0x81, + kOpJumpIf = 0x82, + kOpJumpIfNotValue = 0x83, + kOpJumpIfValue = 0x84 +}; + +enum { + kFuncPlayAnim = 35001, + kFuncStopAnim = 35002, + kFuncQueueScript = 35004, + kFuncDequeueScript = 35005, + kFuncHideMouse = 35009, + kFuncLoadPage = 35014, + kFuncUnloadPage = 35015, + kFuncSetPalette = 35016, + kFuncQueueScriptOnce = 35019 +}; + +// TODO: params: x, y, event param for done +Animation::Animation(Common::SeekableReadStream *stream, uint16 id, Common::Point basePos, uint32 eventParam) + : _stream(stream), _id(id), _basePos(basePos), _eventParam(eventParam) { + uint32 size = _stream->readUint32LE(); + _state = _stream->readUint32LE() + 1; + + // probably total size? + uint32 unknown = _stream->readUint32LE(); + + debug(8, "anim: size %d, state %08x, unknown %08x", size, _state, unknown); + + for (uint i = 0; i < size; i++) { + AnimationEntry entry; + entry.op = _stream->readUint16LE(); + entry.word6 = _stream->readUint16LE(); + entry.dword0 = _stream->readUint16LE(); + entry.counter = 0; + entry.word10 = 0; + debug(8, "anim entry: %04x, %04x, %04x", entry.op, entry.word6, entry.dword0); + _entries.push_back(entry); + } + + _offset = _stream->pos(); +} + +Animation::~Animation() { + delete _stream; +} + +void Animation::seekToCurrPos() { + _stream->seek(_offset, SEEK_SET); +} + +Pipe::Pipe(Common::SeekableReadStream *stream) { + _offset = 0; + _stream = stream; + + nextFrame(); +} + +void Pipe::nextFrame() { + _stream->seek(_offset, SEEK_SET); + + uint32 tagCount = _stream->readUint32LE(); + _offset += 4; + for (uint i = 0; i < tagCount; i++) { + uint32 tag = _stream->readUint32BE(); + uint32 count = _stream->readUint32LE(); + _offset += 8; + + ResourceMap &resMap = _types[tag]; + + _offset += (12 * count); + //uint32 baseOffset = _offset; + for (uint j = 0; j < count; j++) { + uint32 offset = _stream->readUint32LE(); + uint32 size = _stream->readUint32LE(); + uint16 id = _stream->readUint16LE(); + uint32 unknown = _stream->readUint16LE(); // frame id? + debug(9, "pipe: %s/%d: offset %d, size %d, unknown %d", tag2str(tag), id, offset, size, unknown); + + PipeResourceEntry entry; + entry.size = size; + entry.offset = _offset; + resMap[id].entries.push_back(entry); + + _offset += size; + } + _stream->seek(_offset, SEEK_SET); + } +} + +bool Pipe::hasResource(uint32 tag, uint16 id) const { + if (!_types.contains(tag)) + return false; + + return _types[tag].contains(id); +} + +Common::SeekableReadStream *Pipe::getResource(uint32 tag, uint16 id, bool buffering) { + if (!_types.contains(tag)) + error("Pipe does not contain '%s' %04x", tag2str(tag), id); + + const ResourceMap &resMap = _types[tag]; + + if (!resMap.contains(id)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); + + const PipeResource &res = resMap[id]; + + if (!buffering) { + assert(res.entries.size() == 1); + return new Common::SeekableSubReadStream(_stream, res.entries[0].offset, res.entries[0].offset + res.entries[0].size); + } + + uint32 size = 0; + for (uint i = 0; i < res.entries.size(); i++) + size += res.entries[i].size; + + byte *buffer = new byte[size]; + uint32 offset = 0; + for (uint i = 0; i < res.entries.size(); i++) { + _stream->seek(res.entries[i].offset, SEEK_SET); + _stream->read(buffer + offset, res.entries[i].size); + offset += res.entries[i].size; + } + _types[tag].erase(id); + return new Common::MemoryReadStream(buffer, size, DisposeAfterUse::YES); +} + +void ComposerEngine::playAnimation(uint16 animId, int16 x, int16 y, int16 eventParam) { + // First, we check if this animation is already playing, + // and if it is, we sabotage that running one first. + for (Common::List::iterator i = _anims.begin(); i != _anims.end(); i++) { + Animation *anim = *i; + if (anim->_id != animId) + continue; + + // disable the animation + anim->_state = 0; + + // stop any animations it may have spawned + for (uint j = 0; j < anim->_entries.size(); j++) { + if (anim->_entries[j].op == 3) + ; // TODO: stop anim + } + + // kill any pipes owned by the animation + for (Common::List::iterator j = _pipes.begin(); j != _pipes.end(); j++) { + Pipe *pipe = *j; + if (pipe->_anim != anim) + continue; + j = _pipes.reverse_erase(j); + delete pipe; + break; + } + + break; + } + + Common::SeekableReadStream *stream = NULL; + Pipe *newPipe = NULL; + + // First, check the existing pipes. + for (Common::List::iterator j = _pipes.begin(); j != _pipes.end(); j++) { + Pipe *pipe = *j; + if (!pipe->hasResource(ID_ANIM, animId)) + continue; + stream = pipe->getResource(ID_ANIM, animId, false); + break; + } + + // If we didn't find it, try the libraries. + if (!stream) { + stream = getResource(ID_ANIM, animId); + + uint32 type = 0; + for (Common::List::iterator i = _libraries.begin(); i != _libraries.end(); i++) + if (i->_archive->hasResource(ID_ANIM, animId)) { + type = i->_archive->getResourceFlags(ID_ANIM, animId); + break; + } + + // If the resource is a pipe itself, then load the pipe + // and then fish the requested animation out of it. + if (type != 1) { + newPipe = new Pipe(stream); + _pipes.push_front(newPipe); + stream = newPipe->getResource(ID_ANIM, animId, false); + } + } + + Animation *anim = new Animation(stream, animId, Common::Point(x, y), eventParam); + _anims.push_back(anim); + if (newPipe) + newPipe->_anim = anim; + runEvent(1, animId, eventParam, 0); +} + +void ComposerEngine::playWaveForAnim(uint16 id, bool bufferingOnly) { + Common::SeekableReadStream *stream = NULL; + if (!bufferingOnly && hasResource(ID_WAVE, id)) { + stream = getResource(ID_WAVE, id); + } else { + for (Common::List::iterator k = _pipes.begin(); k != _pipes.end(); k++) { + Pipe *pipe = *k; + if (!pipe->hasResource(ID_WAVE, id)) + continue; + stream = pipe->getResource(ID_WAVE, id, true); + break; + } + } + if (!stream) + return; + // FIXME: non-pipe buffers have fixed wav header (data at +44, size at +40) + // FIXME: deal with word6 (priority) + byte *buffer = (byte *)malloc(stream->size()); + stream->read(buffer, stream->size()); + _audioStream->queueBuffer(buffer, stream->size(), DisposeAfterUse::YES, Audio::FLAG_UNSIGNED); + delete stream; + if (!_mixer->isSoundHandleActive(_soundHandle)) + _mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, _audioStream); +} + +void ComposerEngine::processAnimFrame() { + for (Common::List::iterator i = _anims.begin(); i != _anims.end(); i++) { + Animation *anim = *i; + + if (anim->_state <= 1) { + if (anim->_state == 1) { + runEvent(2, anim->_id, anim->_eventParam, 0); + } else { + // TODO: stop anything which was running + } + // TODO: kill pipes + delete anim; + i = _anims.reverse_erase(i); + + continue; + } + + anim->seekToCurrPos(); + + for (uint j = 0; j < anim->_entries.size(); j++) { + AnimationEntry &entry = anim->_entries[j]; + if (entry.op != 1) + break; + if (entry.counter) { + entry.counter--; + } else { + uint16 event = anim->_stream->readUint16LE(); + anim->_offset += 2; + if (event == 0xffff) { + entry.counter = anim->_stream->readUint16LE() - 1; + anim->_offset += 2; + } else { + runEvent(event, anim->_id, 0, 0); + } + } + } + } + + for (Common::List::iterator i = _anims.begin(); i != _anims.end(); i++) { + Animation *anim = *i; + + anim->_state--; + + for (uint j = 0; j < anim->_entries.size(); j++) { + AnimationEntry &entry = anim->_entries[j]; + + // TODO: only skip these at the start + if (entry.op == 1) + continue; + + if (entry.counter) { + entry.counter--; + if (entry.op == 2 && entry.word10) { + debug(4, "anim: continue play wave %d", entry.word10); + playWaveForAnim(entry.word10, true); + } + } else { + anim->seekToCurrPos(); + + uint16 data = anim->_stream->readUint16LE(); + anim->_offset += 2; + if (data == 0xffff) { + entry.counter = anim->_stream->readUint16LE() - 1; + anim->_offset += 2; + } else { + switch (entry.op) { + case 1: + // TODO + warning("ignoring tingie (%d)", data); + break; + case 2: + debug(4, "anim: play wave %d", data); + playWaveForAnim(data, false); + break; + case 3: + debug(4, "anim: play anim %d", data); + playAnimation(data, anim->_basePos.x, anim->_basePos.y, 1); + break; + case 4: + if (entry.word10 && (!data || data != entry.word10)) { + // TODO: erase old sprite + warning("ignoring anim sprite erase (%d)", entry.word10); + } + if (data) { + uint16 x = anim->_stream->readUint16LE(); + uint16 y = anim->_stream->readUint16LE(); + anim->_offset += 4; + // TODO: sprite change + warning("ignoring anim sprite draw (%d @ %d,%d)", data, x, y); + } + break; + default: + warning("unknown anim op %d", entry.op); + } + + entry.word10 = data; + } + } + } + } + + for (Common::List::iterator j = _pipes.begin(); j != _pipes.end(); j++) { + Pipe *pipe = *j; + pipe->nextFrame(); + } +} + +ComposerEngine::ComposerEngine(OSystem *syst, const ComposerGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) { + _rnd = new Common::RandomSource("composer"); + _audioStream = NULL; +} + +ComposerEngine::~ComposerEngine() { + DebugMan.clearAllDebugChannels(); + + for (Common::List::iterator i = _libraries.begin(); i != _libraries.end(); i++) + delete i->_archive; + + delete _audioStream; + delete _rnd; +} + +Common::Error ComposerEngine::run() { + Common::Event event; + + _vars.resize(1000); + for (uint i = 0; i < _vars.size(); i++) + _vars[i] = 0; + + _queuedScripts.resize(10); + for (uint i = 0; i < _queuedScripts.size(); i++) { + _queuedScripts[i]._count = 0; + _queuedScripts[i]._scriptId = 0; + } + + _bookIni.loadFromFile("programs/book.ini"); + + uint width = 640; + if (_bookIni.hasKey("Width", "Common")) + width = atoi(getStringFromConfig("Common", "Width").c_str()); + uint height = 480; + if (_bookIni.hasKey("Height", "Common")) + height = atoi(getStringFromConfig("Common", "Height").c_str()); + initGraphics(width, height, true); + _surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8()); + + _audioStream = Audio::makeQueuingAudioStream(22050, false); + + loadLibrary(0); + + uint fps = atoi(getStringFromConfig("Common", "FPS").c_str()); + uint frameTime = 1000 / fps; + uint32 lastDrawTime = 0; + while (!shouldQuit()) { + if (hasResource(ID_BMAP, 1000)) + drawBMAP(1000, 0, 0); + + _system->copyRectToScreen((byte *)_surface.pixels, _surface.pitch, 0, 0, _surface.w, _surface.h); + _system->updateScreen(); + + uint32 thisTime = _system->getMillis(); + for (uint i = 0; i < _queuedScripts.size(); i++) { + QueuedScript &script = _queuedScripts[i]; + if (!script._count) + continue; + if (script._baseTime + script._duration > thisTime) + continue; + if (script._count != 0xffff) + script._count--; + script._baseTime = thisTime; + runScript(script._scriptId, i, 0, 0); + } + + // _system->delayMillis(frameTime + lastDrawTime - thisTime); + if (lastDrawTime + frameTime <= thisTime) { + lastDrawTime += frameTime; + + processAnimFrame(); + } + + while (_eventMan->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_LBUTTONDOWN: + break; + + case Common::EVENT_LBUTTONUP: + break; + + case Common::EVENT_RBUTTONDOWN: + break; + + case Common::EVENT_MOUSEMOVE: + break; + + case Common::EVENT_KEYDOWN: + switch (event.kbd.keycode) { + case Common::KEYCODE_d: + /*if (event.kbd.hasFlags(Common::KBD_CTRL)) { + // Start the debugger + getDebugger()->attach(); + getDebugger()->onFrame(); + }*/ + break; + + case Common::KEYCODE_ESCAPE: + quitGame(); + break; + + default: + break; + } + break; + + case Common::EVENT_QUIT: + case Common::EVENT_RTL: + quitGame(); + break; + + default: + break; + } + } + + _system->delayMillis(20); + } + + return Common::kNoError; +} + +Common::String ComposerEngine::getStringFromConfig(const Common::String §ion, const Common::String &key) { + Common::String value; + if (!_bookIni.getKey(key, section, value)) + error("failed to find key '%s' in section '%s' of book config", key.c_str(), section.c_str()); + return value; +} + +Common::String ComposerEngine::getFilename(const Common::String §ion, uint id) { + Common::String key = Common::String::format("%d", id); + Common::String filename = getStringFromConfig(section, key); + while (filename.size() && (filename[0] == '~' || filename[0] == ':' || filename[0] == '\\')) + filename = filename.c_str() + 1; + Common::String outFilename; + for (uint i = 0; i < filename.size(); i++) { + if (filename[i] == '\\') + outFilename += '/'; + else + outFilename += filename[i]; + } + return outFilename; +} + +void ComposerEngine::loadLibrary(uint id) { + if (!id) + id = atoi(getStringFromConfig("Common", "StartUp").c_str()); + Common::String filename = getFilename("Libs", id); + + Library library; + + library._id = id; + library._archive = new ComposerArchive(); + if (!library._archive->openFile(filename)) + error("failed to open '%s'", filename.c_str()); + // FIXME: push in front? + _libraries.push_front(library); + + /*Common::SeekableReadStream *stream = _archive->getResource(ID_ANIM, 1004); + byte buf[stream->size()]; + stream->read(buf, stream->size()); + Common::hexdump(buf, stream->size()); + delete stream;*/ + + // TODO: set background properly + // + // TODO: better CTBL logic + loadCTBL(1000, 100); + + // Run the startup script. + runScript(1000, 0, 0, 0); + + runEvent(3, id, 0, 0); +} + +bool ComposerEngine::hasResource(uint32 tag, uint16 id) { + for (Common::List::iterator i = _libraries.begin(); i != _libraries.end(); i++) + if (i->_archive->hasResource(tag, id)) + return true; + + return false; +} + +Common::SeekableReadStream *ComposerEngine::getResource(uint32 tag, uint16 id) { + for (Common::List::iterator i = _libraries.begin(); i != _libraries.end(); i++) + if (i->_archive->hasResource(tag, id)) + return i->_archive->getResource(tag, id); + + error("No loaded library contains '%s' %04x", tag2str(tag), id); +} + +void ComposerEngine::runEvent(uint16 id, int16 param1, int16 param2, int16 param3) { + if (!hasResource(ID_EVNT, id)) + return; + + Common::SeekableReadStream *stream = getResource(ID_EVNT, id); + if (stream->size() != 2) + error("bad EVNT size %d", stream->size()); + uint16 scriptId = stream->readUint16LE(); + delete stream; + + debug(2, "running event %d via script %d(%d, %d, %d)", id, scriptId, param1, param2, param3); + + runScript(scriptId, param1, param2, param3); +} + +int16 ComposerEngine::runScript(uint16 id, int16 param1, int16 param2, int16 param3) { + _vars[1] = param1; + _vars[2] = param2; + _vars[3] = param3; + + runScript(id); + + return _vars[0]; +} + +int16 ComposerEngine::getArg(uint16 arg, uint16 type) { + switch (type) { + case 0: + return (int16)arg; + case 1: + return (int16)_vars[arg]; + case 2: + return (int16)_vars[_vars[arg]]; + default: + error("invalid argument type %d (getting arg %d)", type, arg); + } +} + +void ComposerEngine::setArg(uint16 arg, uint16 type, uint16 val) { + switch (type) { + case 1: + _vars[arg] = val; + break; + case 2: + _vars[_vars[arg]] = val; + break; + default: + error("invalid argument type %d (setting arg %d)", type, arg); + } + +} + +void ComposerEngine::runScript(uint16 id) { + if (!hasResource(ID_SCRP, id)) { + warning("ignoring attempt to run script %d, because it doesn't exist", id); + return; + } + + uint stackBase = _stack.size(); + _stack.resize(_stack.size() + 19); + + Common::SeekableReadStream *stream = getResource(ID_SCRP, id); + if (stream->size() < 2) + error("SCRP was too small (%d)", stream->size()); + uint16 size = stream->readUint16LE(); + if (stream->size() < 2 + 2*size) + error("SCRP was too small (%d, but claimed %d entries)", stream->size(), size); + uint16 *script = new uint16[size]; + for (uint i = 0; i < size; i++) + script[i] = stream->readUint16LE(); + delete stream; + + uint16 pos = 0; + bool lastResult = false; + while (pos < size) { + int16 val1, val2, val3; + + byte op = (byte)script[pos]; + uint numParams = (script[pos] & 0x300) >> 8; // 2 bits + if (pos + numParams >= size) + error("script ran out of content"); + uint arg1 = (script[pos] & 0xc00) >> 10; // 2 bits + uint arg2 = (script[pos] & 0x3000) >> 12; // 2 bits + uint arg3 = (script[pos] & 0xC000) >> 14; // 2 bits + switch (op) { + case kOpPlusPlus: + val1 = getArg(script[pos + 1], arg1); + debug(9, "[%d/%d]++ (now %d)", script[pos + 1], arg1, val1 + 1); + setArg(script[pos + 1], arg1, val1 + 1); + break; + case kOpAdd: + val2 = getArg(script[pos + 2], arg2); + val3 = getArg(script[pos + 3], arg3); + debug(9, "[%d/%d] = [%d/%d]=%d + [%d/%d]=%d (%d)", script[pos + 1], arg1, script[pos + 2], arg2, val2, script[pos+3], arg3, val3, val2 + val3); + setArg(script[pos + 1], arg1, val2 + val3); + break; + case kOpAssign: + val2 = getArg(script[pos + 2], arg2); + if (numParams != 2) + error("kOpAssign had wrong number of params (%d)", numParams); + debug(9, "[%d/%d] = [%d/%d] (%d)", script[pos + 1], arg1, script[pos + 2], arg2, val2); + setArg(script[pos + 1], arg1, val2); + break; + case kOpCallFunc: + val1 = getArg(script[pos + 1], arg1); + if (numParams != 1) + error("kOpCallFunc had wrong number of params (%d)", numParams); + debug(8, "%d(%d, %d, %d)", (uint16)val1, _vars[1], _vars[2], _vars[3]); + _vars[0] = scriptFuncCall(val1, _vars[1], _vars[2], _vars[3]); + break; + /*case kOpBoolGreaterThan: + lastResult = (getArg(script[pos + 1], arg1) > getArg(script[pos + 2], arg2)); + break;*/ + case kOpBoolEqual: + val1 = getArg(script[pos + 1], arg1); + val2 = getArg(script[pos + 2], arg2); + debug(9, "[%d/%d] == [%d/%d]? (%d > %d)", script[pos + 1], arg1, script[pos + 2], arg2, val1, val2); + lastResult = (val1 == val2); + break; + case kOpSaveArgs: + debug(9, "save args"); + for (uint i = 1; i < 19; i++) + _stack[stackBase + i] = _vars[i]; + break; + case kOpRestoreArgs: + debug(9, "restore args"); + for (uint i = 1; i < 19; i++) + _vars[i] = _stack[stackBase + i]; + break; + case kOpGreaterThan: + val2 = getArg(script[pos + 2], arg2); + val3 = getArg(script[pos + 3], arg3); + debug(9, "[%d/%d] = [%d/%d] > [%d/%d]? (%d > %d)", script[pos + 1], arg1, script[pos + 2], arg2, script[pos + 3], arg3, val2, val3); + setArg(script[pos + 1], arg1, (val3 > val2) ? 1 : 0); + break; + case kOpJump: + val1 = getArg(script[pos + 1], arg1); + debug(9, "jump by [%d/%d]=%d", script[pos + 1], arg1, val1); + pos += val1; + break; + case kOpJumpIfNot: + if (lastResult) + break; + val1 = getArg(script[pos + 1], arg1); + debug(9, "jump if not, by [%d/%d]=%d", script[pos + 1], arg1, val1); + pos += val1; + break; + case kOpJumpIfNotValue: + val1 = getArg(script[pos + 1], arg1); + val2 = getArg(script[pos + 2], arg2); + debug(9, "jump if not [%d/%d]=%d", script[pos + 1], arg1, val1); + if (val1) + break; + debug(9, "--> jump by [%d/%d]=%d", script[pos + 2], arg2, val2); + pos += val2; + break; + default: + error("unknown script op 0x%02x", op); + } + pos += (1 + numParams); + } + + delete[] script; + _stack.resize(_stack.size() - 19); +} + +int16 ComposerEngine::scriptFuncCall(uint16 id, int16 param1, int16 param2, int16 param3) { + switch (id) { + case kFuncPlayAnim: + debug(3, "kFuncPlayAnim(%d, %d, %d)", param1, param2, param3); + playAnimation(param1, param2, param3, 0); + break; + case kFuncStopAnim: + // TODO + warning("ignoring kFuncStopAnim(%d)", param1); + break; + case kFuncQueueScript: + debug(3, "kFuncQueueScript(%d, %d, %d)", param1, param2, param3); + _queuedScripts[param1]._baseTime = _system->getMillis(); + _queuedScripts[param1]._duration = 10 * param2; + _queuedScripts[param1]._count = 0xffff; + _queuedScripts[param1]._scriptId = param3; + return 0; + case kFuncDequeueScript: + debug(3, "kFuncDequeueScript(%d)", param1); + _queuedScripts[param1]._count = 0; + _queuedScripts[param1]._scriptId = 0; + return 0; + case kFuncHideMouse: + // TODO + warning("ignoring kFuncHideMouse(%d)", param1); + return 0; + break; + case kFuncLoadPage: + debug(3, "kFuncLoadPage(%d)", param1); + loadLibrary(param1); + return 1; + case kFuncUnloadPage: + // TODO + warning("ignoring kFuncUnloadPage(%d)", param1); + return 1; + case kFuncSetPalette: + // TODO: incomplete? + debug(4, "kFuncSetPalette(%d, %d)", param1, param2); + loadCTBL(param1, param2); + return 1; + break; + case kFuncQueueScriptOnce: + debug(3, "kFuncQueueScriptOnce(%d, %d, %d)", param1, param2, param3); + _queuedScripts[param1]._baseTime = _system->getMillis(); + _queuedScripts[param1]._duration = 10 * param2; + _queuedScripts[param1]._count = 1; + _queuedScripts[param1]._scriptId = param3; + return 0; + default: + error("unknown scriptFuncCall %d(%d, %d, %d)", (uint32)id, param1, param2, param3); + } +} + +void ComposerEngine::loadCTBL(uint id, uint fadePercent) { + Common::SeekableReadStream *stream = getResource(ID_CTBL, id); + + uint16 numEntries = stream->readUint16LE(); + debug(1, "CTBL: %d entries", numEntries); + + assert(numEntries <= 256); + assert(stream->size() == 2 + (numEntries * 3)); + + byte buffer[256 * 3]; + stream->read(buffer, numEntries * 3); + delete stream; + + for (uint i = 0; i < numEntries * 3; i++) + buffer[i] = ((unsigned int)buffer[i] * fadePercent) / 100; + + _system->getPaletteManager()->setPalette(buffer, 0, numEntries); +} + +void ComposerEngine::decompressBitmap(uint16 type, Common::SeekableReadStream *stream, byte *buffer, uint32 size) { + switch (type) { + case kBitmapUncompressed: + assert(stream->size() - (uint)stream->pos() == size); + stream->read(buffer, size); + break; + default: + error("decompressBitmap can't handle type %d", type); + } +} + +void ComposerEngine::drawBMAP(uint id, uint x, uint y) { + Common::SeekableReadStream *stream = getResource(ID_BMAP, id); + + uint16 type = stream->readUint16LE(); + uint16 height = stream->readUint16LE(); + uint16 width = stream->readUint16LE(); + uint32 size = stream->readUint32LE(); + debug(1, "BMAP: type %d, width %d, height %d, size %d", type, width, height, size); + + byte *buffer = new byte[width * height]; + decompressBitmap(type, stream, buffer, size); + + // incoming data is BMP-style (bottom-up), so flip it + byte *pixels = (byte *)_surface.pixels; + for (uint j = 0; j < height; j++) { + memcpy(pixels + (j * width), buffer + (height - j - 1) * width, width); + } + + delete[] buffer; + delete stream; +} + +} // End of namespace Composer diff --git a/engines/composer/composer.h b/engines/composer/composer.h new file mode 100644 index 0000000000..5d4233a1ba --- /dev/null +++ b/engines/composer/composer.h @@ -0,0 +1,197 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef COMPOSER_H +#define COMPOSER_H + +#include "common/config-file.h" +#include "common/random.h" +#include "common/system.h" +#include "common/debug.h" +#include "common/debug-channels.h" +#include "common/textconsole.h" +#include "common/rect.h" + +#include "engines/engine.h" +#include "engines/util.h" + +#include "graphics/surface.h" + +#include "audio/mixer.h" + +#include "composer/resource.h" + +namespace Audio { + class QueuingAudioStream; +} + +namespace Composer { + +struct ComposerGameDescription; + +enum GameType { + GType_ComposerV1, + GType_ComposerV2 +}; + +class Archive; +class ComposerEngine; + +class Sprite { +}; + +struct AnimationEntry { + uint32 dword0; + uint16 op; + uint16 word6; + uint16 counter; + uint16 word10; +}; + +struct Animation { + Animation(Common::SeekableReadStream *stream, uint16 id, Common::Point basePos, uint32 eventParam); + ~Animation(); + + void seekToCurrPos(); + + uint16 _id; + Common::Point _basePos; + uint32 _eventParam; + + uint32 _state; + + Common::Array _entries; + + uint32 _offset; + Common::SeekableReadStream *_stream; +}; + +struct PipeResourceEntry { + uint32 size; + uint32 offset; +}; + +struct PipeResource { + Common::Array entries; +}; + +class Pipe { +public: + Pipe(Common::SeekableReadStream *stream); + void nextFrame(); + + Animation *_anim; + + bool hasResource(uint32 tag, uint16 id) const; + Common::SeekableReadStream *getResource(uint32 tag, uint16 id, bool buffering); + +protected: + Common::SeekableReadStream *_stream; + + typedef Common::HashMap ResourceMap; + typedef Common::HashMap TypeMap; + TypeMap _types; + + uint32 _offset; +}; + +class Button { +public: + Button(ComposerEngine *vm, uint16 id); +}; + +struct Library { + uint _id; + Archive *_archive; +}; + +struct QueuedScript { + uint32 _baseTime; + uint32 _duration; + uint32 _count; + uint16 _scriptId; +}; + +class ComposerEngine : public Engine { +protected: + Common::Error run(); + +public: + ComposerEngine(OSystem *syst, const ComposerGameDescription *gameDesc); + virtual ~ComposerEngine(); + + virtual bool hasFeature(EngineFeature f) const; + + int getGameType() const; + const char *getGameId() const; + uint32 getFeatures() const; + Common::Language getLanguage() const; + + const ComposerGameDescription *_gameDescription; + +private: + Common::RandomSource *_rnd; + + Audio::SoundHandle _soundHandle; + Audio::QueuingAudioStream *_audioStream; + + Graphics::Surface _surface; + Common::ConfigFile _bookIni; + Common::List _libraries; + + Common::Array _stack; + Common::Array _vars; + + Common::Array _queuedScripts; + Common::List _anims; + Common::List _pipes; + + Common::String getStringFromConfig(const Common::String §ion, const Common::String &key); + Common::String getFilename(const Common::String §ion, uint id); + void loadLibrary(uint id); + + bool hasResource(uint32 tag, uint16 id); + Common::SeekableReadStream *getResource(uint32 tag, uint16 id); + + void runEvent(uint16 id, int16 param1, int16 param2, int16 param3); + int16 runScript(uint16 id, int16 param1, int16 param2, int16 param3); + + int16 getArg(uint16 arg, uint16 type); + void setArg(uint16 arg, uint16 type, uint16 val); + void runScript(uint16 id); + int16 scriptFuncCall(uint16 id, int16 param1, int16 param2, int16 param3); + + void playAnimation(uint16 animId, int16 param1, int16 param2, int16 param3); + void playWaveForAnim(uint16 id, bool bufferingOnly); + void processAnimFrame(); + + void loadCTBL(uint id, uint fadePercent); + void decompressBitmap(uint16 type, Common::SeekableReadStream *stream, byte *buffer, uint32 size); + void drawBMAP(uint id, uint x, uint y); +}; + +} // End of namespace Composer + +#endif diff --git a/engines/composer/detection.cpp b/engines/composer/detection.cpp new file mode 100644 index 0000000000..8704a5eabc --- /dev/null +++ b/engines/composer/detection.cpp @@ -0,0 +1,139 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "base/plugins.h" +#include "engines/advancedDetector.h" + +#include "composer/composer.h" + +namespace Composer { + +struct ComposerGameDescription { + ADGameDescription desc; + + int gameType; +}; + +int ComposerEngine::getGameType() const { + return _gameDescription->gameType; +} + +const char *ComposerEngine::getGameId() const { + return _gameDescription->desc.gameid; +} + +uint32 ComposerEngine::getFeatures() const { + return _gameDescription->desc.flags; +} + +Common::Language ComposerEngine::getLanguage() const { + return _gameDescription->desc.language; +} + +} + +static const PlainGameDescriptor composerGames[] = { + {"darby", "Darby the Dragon"}, + {"demo1", "Magic Tales Demo 1"}, + {0, 0} +}; + +namespace Composer { + +using Common::GUIO_NONE; + +static const ComposerGameDescription gameDescriptions[] = { + { + { + "demo1", + 0, + {{"book.ini", 0, "dbc98c566f4ac61b544443524585dccb", -1}, + AD_LISTEND}, + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + Common::GUIO_NONE + }, + GType_ComposerV1 + }, + + { + { + "darby", + 0, + {{"install.inf", 0, "e83cc20ee18a2e138da1aadfc640dff2", -1}, + AD_LISTEND}, + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + Common::GUIO_NONE + }, + GType_ComposerV2 + }, + + { AD_TABLE_END_MARKER, 0 } +}; + +} // End of namespace Composer + +using namespace Composer; + +class ComposerMetaEngine : public AdvancedMetaEngine { +public: + ComposerMetaEngine() : AdvancedMetaEngine(Composer::gameDescriptions, sizeof(Composer::ComposerGameDescription), composerGames) {} + + virtual const char *getName() const { + return "Magic Composer Engine"; + } + + virtual const char *getOriginalCopyright() const { + return "Animation Magic"; + } + + virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; + virtual bool hasFeature(MetaEngineFeature f) const; +}; + +bool ComposerMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { + const Composer::ComposerGameDescription *gd = (const Composer::ComposerGameDescription *)desc; + if (gd) { + *engine = new Composer::ComposerEngine(syst, gd); + } + return gd != 0; +} + +bool ComposerMetaEngine::hasFeature(MetaEngineFeature f) const { + return false; +} + +bool Composer::ComposerEngine::hasFeature(EngineFeature f) const { + return (f == kSupportsRTL); +} + +#if PLUGIN_ENABLED_DYNAMIC(COMPOSER) +REGISTER_PLUGIN_DYNAMIC(COMPOSER, PLUGIN_TYPE_ENGINE, ComposerMetaEngine); +#else +REGISTER_PLUGIN_STATIC(COMPOSER, PLUGIN_TYPE_ENGINE, ComposerMetaEngine); +#endif diff --git a/engines/composer/module.mk b/engines/composer/module.mk new file mode 100644 index 0000000000..5637ffbc6e --- /dev/null +++ b/engines/composer/module.mk @@ -0,0 +1,14 @@ +MODULE := engines/composer + +MODULE_OBJS = \ + composer.o \ + detection.o \ + resource.o + +# This module can be built as a plugin +ifdef BUILD_PLUGINS +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk diff --git a/engines/composer/resource.cpp b/engines/composer/resource.cpp new file mode 100644 index 0000000000..390649ba52 --- /dev/null +++ b/engines/composer/resource.cpp @@ -0,0 +1,250 @@ +/* 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 "composer/resource.h" + +#include "common/debug.h" +#include "common/substream.h" +#include "common/util.h" +#include "common/textconsole.h" + +namespace Composer { + +// Base Archive code +// (copied from clone2727's mohawk code) + +Archive::Archive() { + _stream = 0; +} + +Archive::~Archive() { + close(); +} + +bool Archive::openFile(const Common::String &fileName) { + Common::File *file = new Common::File(); + + if (!file->open(fileName)) { + delete file; + return false; + } + + if (!openStream(file)) { + close(); + return false; + } + + return true; +} + +void Archive::close() { + _types.clear(); + delete _stream; _stream = 0; +} + +bool Archive::hasResource(uint32 tag, uint16 id) const { + if (!_types.contains(tag)) + return false; + + return _types[tag].contains(id); +} + +bool Archive::hasResource(uint32 tag, const Common::String &resName) const { + if (!_types.contains(tag) || resName.empty()) + return false; + + const ResourceMap &resMap = _types[tag]; + + for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++) + if (it->_value.name.matchString(resName)) + return true; + + return false; +} + +Common::SeekableReadStream *Archive::getResource(uint32 tag, uint16 id) { + if (!_types.contains(tag)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); + + const ResourceMap &resMap = _types[tag]; + + if (!resMap.contains(id)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); + + const Resource &res = resMap[id]; + + return new Common::SeekableSubReadStream(_stream, res.offset, res.offset + res.size); +} + +uint32 Archive::getResourceFlags(uint32 tag, uint16 id) const { + if (!_types.contains(tag)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); + + const ResourceMap &resMap = _types[tag]; + + if (!resMap.contains(id)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); + + const Resource &res = resMap[id]; + + return res.flags; +} + +uint32 Archive::getOffset(uint32 tag, uint16 id) const { + if (!_types.contains(tag)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); + + const ResourceMap &resMap = _types[tag]; + + if (!resMap.contains(id)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); + + return resMap[id].offset; +} + +uint16 Archive::findResourceID(uint32 tag, const Common::String &resName) const { + if (!_types.contains(tag) || resName.empty()) + return 0xFFFF; + + const ResourceMap &resMap = _types[tag]; + + for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++) + if (it->_value.name.matchString(resName)) + return it->_key; + + return 0xFFFF; +} + +Common::String Archive::getName(uint32 tag, uint16 id) const { + if (!_types.contains(tag)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); + + const ResourceMap &resMap = _types[tag]; + + if (!resMap.contains(id)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); + + return resMap[id].name; +} + +Common::Array Archive::getResourceTypeList() const { + Common::Array typeList; + + for (TypeMap::const_iterator it = _types.begin(); it != _types.end(); it++) + typeList.push_back(it->_key); + + return typeList; +} + +Common::Array Archive::getResourceIDList(uint32 type) const { + Common::Array idList; + + if (!_types.contains(type)) + return idList; + + const ResourceMap &resMap = _types[type]; + + for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++) + idList.push_back(it->_key); + + return idList; +} + +// Composer Archive code + +bool ComposerArchive::openStream(Common::SeekableReadStream *stream) { + // Make sure no other file is open... + close(); + + bool newStyle = false; + uint32 headerSize = stream->readUint32LE(); + if (headerSize == SWAP_CONSTANT_32(ID_LBRC)) { + // new-style file + newStyle = true; + headerSize = stream->readUint32LE(); + uint32 zeros = stream->readUint32LE(); + if (zeros != 0) + error("invalid LBRC header (%d instead of zeros)", zeros); + } + + uint16 numResourceTypes = stream->readUint16LE(); + if (newStyle) { + uint16 unknown = stream->readUint16LE(); + debug(4, "skipping unknown %04x", unknown); + } + + debug(4, "Reading LBRC resource table with %d entries", numResourceTypes); + for (uint i = 0; i < numResourceTypes; i++) { + uint32 tag = stream->readUint32BE(); + uint32 tableOffset = stream->readUint32LE(); + debug(4, "Type '%s' at offset %d", tag2str(tag), tableOffset); + // starting from the start of the resource table, which differs + // according to whether we have the 10 extra bytes for newStyle + if (newStyle) + tableOffset += 16; + else + tableOffset += 6; + + ResourceMap &resMap = _types[tag]; + + uint32 oldPos = stream->pos(); + stream->seek(tableOffset); + + while (true) { + if (stream->eos()) + error("LBRC file ran out of stream"); + + uint32 offset, size, id, flags; + if (newStyle) { + offset = stream->readUint32LE(); + if (!offset) + break; + size = stream->readUint32LE(); + id = stream->readUint16LE(); + flags = stream->readUint16LE(); // set to 1 for preload, otherwise no preload + /*uint32 junk = */ stream->readUint32LE(); + } else { + id = stream->readUint16LE(); + if (!id) + break; + offset = stream->readUint32LE(); + offset += headerSize; + size = stream->readUint32LE(); + flags = stream->readUint16LE(); // FIXME + + } + + Resource &res = resMap[id]; + res.offset = offset; + res.size = size; + res.flags = flags; + debug(4, "Id %d, offset %d, size %d, flags %08x", id, offset, size, flags); + } + + stream->seek(oldPos); + } + + _stream = stream; + return true; +} + +} // End of namespace Composer diff --git a/engines/composer/resource.h b/engines/composer/resource.h new file mode 100644 index 0000000000..4cadd80e22 --- /dev/null +++ b/engines/composer/resource.h @@ -0,0 +1,93 @@ +/* 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/endian.h" +#include "common/hashmap.h" +#include "common/file.h" +#include "common/str.h" + +#ifndef COMPOSER_RESOURCE_H +#define COMPOSER_RESOURCE_H + +namespace Composer { + +#define ID_LBRC MKTAG('L','B','R','C') // Main FourCC + +#define ID_ANIM MKTAG('A','N','I','M') // Animation +#define ID_BMAP MKTAG('B','M','A','P') // Bitmap +#define ID_BUTN MKTAG('B','U','T','N') // Button +#define ID_CTBL MKTAG('C','T','B','L') // Color Table +#define ID_EVNT MKTAG('E','V','N','T') // Event +#define ID_PIPE MKTAG('P','I','P','E') // Pipe +#define ID_SCRP MKTAG('S','C','R','P') // Script +#define ID_VARI MKTAG('V','A','R','I') // Variables +#define ID_WAVE MKTAG('W','A','V','E') // Wave + +class Archive { +public: + Archive(); + virtual ~Archive(); + + bool openFile(const Common::String &fileName); + virtual bool openStream(Common::SeekableReadStream *stream) = 0; + void close(); + + bool isOpen() const { return _stream != 0; } + + bool hasResource(uint32 tag, uint16 id) const; + bool hasResource(uint32 tag, const Common::String &resName) const; + Common::SeekableReadStream *getResource(uint32 tag, uint16 id); + uint32 getResourceFlags(uint32 tag, uint16 id) const; + uint32 getOffset(uint32 tag, uint16 id) const; + uint16 findResourceID(uint32 tag, const Common::String &resName) const; + Common::String getName(uint32 tag, uint16 id) const; + + Common::Array getResourceTypeList() const; + Common::Array getResourceIDList(uint32 type) const; + +protected: + Common::SeekableReadStream *_stream; + + struct Resource { + uint32 offset; + uint32 size; + Common::String name; + uint32 flags; + }; + + typedef Common::HashMap ResourceMap; + typedef Common::HashMap TypeMap; + TypeMap _types; +}; + +class ComposerArchive : public Archive { +public: + ComposerArchive() : Archive() {} + ~ComposerArchive() {} + + bool openStream(Common::SeekableReadStream *stream); +}; + +} // End of namespace Composer + +#endif diff --git a/engines/engines.mk b/engines/engines.mk index dc09fbd54e..de11496b7b 100644 --- a/engines/engines.mk +++ b/engines/engines.mk @@ -31,6 +31,11 @@ DEFINES += -DENABLE_CINE=$(ENABLE_CINE) MODULES += engines/cine endif +ifdef ENABLE_COMPOSER +DEFINES += -DENABLE_COMPOSER=$(ENABLE_COMPOSER) +MODULES += engines/composer +endif + ifdef ENABLE_CRUISE DEFINES += -DENABLE_CRUISE=$(ENABLE_CRUISE) MODULES += engines/cruise -- cgit v1.2.3