COMPOSER: Add a first attempt at an engine.
This commit is contained in:
parent
482fcea8f3
commit
40a6c232e9
9 changed files with 1591 additions and 0 deletions
|
@ -97,6 +97,9 @@ public:
|
||||||
#if PLUGIN_ENABLED_STATIC(CINE)
|
#if PLUGIN_ENABLED_STATIC(CINE)
|
||||||
LINK_PLUGIN(CINE)
|
LINK_PLUGIN(CINE)
|
||||||
#endif
|
#endif
|
||||||
|
#if PLUGIN_ENABLED_STATIC(COMPOSER)
|
||||||
|
LINK_PLUGIN(COMPOSER)
|
||||||
|
#endif
|
||||||
#if PLUGIN_ENABLED_STATIC(CRUISE)
|
#if PLUGIN_ENABLED_STATIC(CRUISE)
|
||||||
LINK_PLUGIN(CRUISE)
|
LINK_PLUGIN(CRUISE)
|
||||||
#endif
|
#endif
|
||||||
|
|
1
configure
vendored
1
configure
vendored
|
@ -84,6 +84,7 @@ add_engine agi "AGI" yes
|
||||||
add_engine agos "AGOS" yes "agos2"
|
add_engine agos "AGOS" yes "agos2"
|
||||||
add_engine agos2 "AGOS 2 games" yes
|
add_engine agos2 "AGOS 2 games" yes
|
||||||
add_engine cine "Cinematique evo 1" yes
|
add_engine cine "Cinematique evo 1" yes
|
||||||
|
add_engine composer "Magic Composer" no
|
||||||
add_engine cruise "Cinematique evo 2" yes
|
add_engine cruise "Cinematique evo 2" yes
|
||||||
add_engine draci "Dragon History" yes
|
add_engine draci "Dragon History" yes
|
||||||
add_engine drascula "Drascula: The Vampire Strikes Back" yes
|
add_engine drascula "Drascula: The Vampire Strikes Back" yes
|
||||||
|
|
889
engines/composer/composer.cpp
Normal file
889
engines/composer/composer.cpp
Normal file
|
@ -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<Animation *>::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<Pipe *>::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<Pipe *>::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<Library>::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<Pipe *>::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<Animation *>::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<Animation *>::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<Pipe *>::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<Library>::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<Library>::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<Library>::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
|
197
engines/composer/composer.h
Normal file
197
engines/composer/composer.h
Normal file
|
@ -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<AnimationEntry> _entries;
|
||||||
|
|
||||||
|
uint32 _offset;
|
||||||
|
Common::SeekableReadStream *_stream;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PipeResourceEntry {
|
||||||
|
uint32 size;
|
||||||
|
uint32 offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PipeResource {
|
||||||
|
Common::Array<PipeResourceEntry> 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<uint16, PipeResource> ResourceMap;
|
||||||
|
typedef Common::HashMap<uint32, ResourceMap> 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<Library> _libraries;
|
||||||
|
|
||||||
|
Common::Array<uint16> _stack;
|
||||||
|
Common::Array<uint16> _vars;
|
||||||
|
|
||||||
|
Common::Array<QueuedScript> _queuedScripts;
|
||||||
|
Common::List<Animation *> _anims;
|
||||||
|
Common::List<Pipe *> _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
|
139
engines/composer/detection.cpp
Normal file
139
engines/composer/detection.cpp
Normal file
|
@ -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
|
14
engines/composer/module.mk
Normal file
14
engines/composer/module.mk
Normal file
|
@ -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
|
250
engines/composer/resource.cpp
Normal file
250
engines/composer/resource.cpp
Normal file
|
@ -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<uint32> Archive::getResourceTypeList() const {
|
||||||
|
Common::Array<uint32> typeList;
|
||||||
|
|
||||||
|
for (TypeMap::const_iterator it = _types.begin(); it != _types.end(); it++)
|
||||||
|
typeList.push_back(it->_key);
|
||||||
|
|
||||||
|
return typeList;
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::Array<uint16> Archive::getResourceIDList(uint32 type) const {
|
||||||
|
Common::Array<uint16> 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
|
93
engines/composer/resource.h
Normal file
93
engines/composer/resource.h
Normal file
|
@ -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<uint32> getResourceTypeList() const;
|
||||||
|
Common::Array<uint16> getResourceIDList(uint32 type) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Common::SeekableReadStream *_stream;
|
||||||
|
|
||||||
|
struct Resource {
|
||||||
|
uint32 offset;
|
||||||
|
uint32 size;
|
||||||
|
Common::String name;
|
||||||
|
uint32 flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef Common::HashMap<uint16, Resource> ResourceMap;
|
||||||
|
typedef Common::HashMap<uint32, ResourceMap> TypeMap;
|
||||||
|
TypeMap _types;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ComposerArchive : public Archive {
|
||||||
|
public:
|
||||||
|
ComposerArchive() : Archive() {}
|
||||||
|
~ComposerArchive() {}
|
||||||
|
|
||||||
|
bool openStream(Common::SeekableReadStream *stream);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // End of namespace Composer
|
||||||
|
|
||||||
|
#endif
|
|
@ -31,6 +31,11 @@ DEFINES += -DENABLE_CINE=$(ENABLE_CINE)
|
||||||
MODULES += engines/cine
|
MODULES += engines/cine
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifdef ENABLE_COMPOSER
|
||||||
|
DEFINES += -DENABLE_COMPOSER=$(ENABLE_COMPOSER)
|
||||||
|
MODULES += engines/composer
|
||||||
|
endif
|
||||||
|
|
||||||
ifdef ENABLE_CRUISE
|
ifdef ENABLE_CRUISE
|
||||||
DEFINES += -DENABLE_CRUISE=$(ENABLE_CRUISE)
|
DEFINES += -DENABLE_CRUISE=$(ENABLE_CRUISE)
|
||||||
MODULES += engines/cruise
|
MODULES += engines/cruise
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue