Initial commit
This commit is contained in:
1
engines/composer/POTFILES
Normal file
1
engines/composer/POTFILES
Normal file
@@ -0,0 +1 @@
|
||||
engines/composer/metaengine.cpp
|
||||
688
engines/composer/composer.cpp
Normal file
688
engines/composer/composer.cpp
Normal file
@@ -0,0 +1,688 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/events.h"
|
||||
#include "common/random.h"
|
||||
#include "common/keyboard.h"
|
||||
|
||||
#include "graphics/cursorman.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
#include "graphics/wincursor.h"
|
||||
|
||||
#include "engines/util.h"
|
||||
|
||||
#include "composer/composer.h"
|
||||
#include "composer/graphics.h"
|
||||
#include "composer/resource.h"
|
||||
#include "composer/console.h"
|
||||
|
||||
namespace Composer {
|
||||
|
||||
ComposerEngine::ComposerEngine(OSystem *syst, const ComposerGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) {
|
||||
_rnd = new Common::RandomSource("composer");
|
||||
_audioStream = nullptr;
|
||||
_currSoundPriority = 0;
|
||||
_currentTime = 0;
|
||||
_lastTime = 0;
|
||||
_needsUpdate = true;
|
||||
_directoriesToStrip = 1;
|
||||
_mouseVisible = true;
|
||||
_mouseEnabled = false;
|
||||
_mouseSpriteId = 0;
|
||||
_lastButton = nullptr;
|
||||
}
|
||||
|
||||
ComposerEngine::~ComposerEngine() {
|
||||
stopPipes();
|
||||
for (Common::List<OldScript *>::iterator i = _oldScripts.begin(); i != _oldScripts.end(); i++)
|
||||
delete *i;
|
||||
for (Common::List<Animation *>::iterator i = _anims.begin(); i != _anims.end(); i++)
|
||||
delete *i;
|
||||
for (Common::List<Library>::iterator i = _libraries.begin(); i != _libraries.end(); i++)
|
||||
delete i->_archive;
|
||||
for (Common::List<Sprite>::iterator i = _sprites.begin(); i != _sprites.end(); i++)
|
||||
i->_surface.free();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!loadDetectedConfigFile(_bookIni)) {
|
||||
// Config files for Darby the Dragon are located in subdirectory
|
||||
_directoriesToStrip = 0;
|
||||
if (!_bookIni.loadFromFile("programs/book.ini")) {
|
||||
error("failed to find book.ini");
|
||||
}
|
||||
}
|
||||
|
||||
Common::String gameId(getGameId());
|
||||
if (getPlatform() == Common::kPlatformMacintosh && (gameId == "darby" || gameId == "gregory")) {
|
||||
_directoriesToStrip = 0;
|
||||
}
|
||||
|
||||
if (getPlatform() == Common::kPlatformMacintosh) {
|
||||
const Common::FSNode gameDataDir(ConfMan.getPath("path"));
|
||||
if (gameId == "sleepingcub")
|
||||
SearchMan.addSubDirectoryMatching(gameDataDir, "sleepcub");
|
||||
if (gameId == "princess")
|
||||
SearchMan.addSubDirectoryMatching(gameDataDir, "princess");
|
||||
if (gameId == "liam")
|
||||
SearchMan.addSubDirectoryMatching(gameDataDir, "liam");
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
_screen.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
|
||||
Graphics::Cursor *cursor = Graphics::makeDefaultWinCursor();
|
||||
CursorMan.replaceCursor(cursor);
|
||||
delete cursor;
|
||||
|
||||
setDebugger(new Console(this));
|
||||
|
||||
loadLibrary(0);
|
||||
|
||||
uint fps;
|
||||
if (_bookIni.hasKey("FPS", "Common"))
|
||||
fps = atoi(getStringFromConfig("Common", "FPS").c_str());
|
||||
else {
|
||||
// On Macintosh version there is no FPS key
|
||||
if (getPlatform() != Common::kPlatformMacintosh)
|
||||
warning("there is no FPS key in book.ini. Defaulting to 8...");
|
||||
fps = 8;
|
||||
}
|
||||
uint frameTime = 125; // Default to 125ms (1000/8)
|
||||
if (fps != 0)
|
||||
frameTime = 1000 / fps;
|
||||
else
|
||||
warning("FPS in book.ini is zero. Defaulting to 8...");
|
||||
uint32 lastDrawTime = 0;
|
||||
|
||||
bool loadFromLauncher = ConfMan.hasKey("save_slot");
|
||||
|
||||
while (!shouldQuit()) {
|
||||
for (uint i = 0; i < _pendingPageChanges.size(); i++) {
|
||||
if (_pendingPageChanges[i]._remove)
|
||||
unloadLibrary(_pendingPageChanges[i]._pageId);
|
||||
else
|
||||
loadLibrary(_pendingPageChanges[i]._pageId);
|
||||
|
||||
lastDrawTime = 0;
|
||||
}
|
||||
_pendingPageChanges.clear();
|
||||
|
||||
uint32 thisTime = _system->getMillis();
|
||||
// maintain our own internal timing, since otherwise we get
|
||||
// confused when starved of CPU (for example when the user
|
||||
// is dragging the scummvm window around)
|
||||
if (thisTime > _lastTime + frameTime)
|
||||
_currentTime += frameTime;
|
||||
else
|
||||
_currentTime += thisTime - _lastTime;
|
||||
_lastTime = thisTime;
|
||||
|
||||
for (uint i = 0; i < _queuedScripts.size(); i++) {
|
||||
QueuedScript &script = _queuedScripts[i];
|
||||
if (!script._count)
|
||||
continue;
|
||||
if (script._baseTime + script._duration > _currentTime)
|
||||
continue;
|
||||
if (script._count != 0xffffffff)
|
||||
script._count--;
|
||||
script._baseTime = _currentTime;
|
||||
runScript(script._scriptId, i, 0, 0);
|
||||
}
|
||||
|
||||
if (lastDrawTime + frameTime <= thisTime) {
|
||||
// catch up if we're more than 2 frames behind
|
||||
if (lastDrawTime + (frameTime * 2) <= thisTime)
|
||||
lastDrawTime = thisTime;
|
||||
else
|
||||
lastDrawTime += frameTime;
|
||||
|
||||
tickOldScripts();
|
||||
|
||||
redraw();
|
||||
|
||||
processAnimFrame();
|
||||
} else if (_needsUpdate) {
|
||||
redraw();
|
||||
}
|
||||
if (loadFromLauncher) {
|
||||
loadGameState(ConfMan.getInt("save_slot"));
|
||||
loadFromLauncher = false;
|
||||
}
|
||||
|
||||
while (_eventMan->pollEvent(event)) {
|
||||
switch (event.type) {
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
onMouseDown(event.mouse);
|
||||
break;
|
||||
|
||||
case Common::EVENT_LBUTTONUP:
|
||||
break;
|
||||
|
||||
case Common::EVENT_RBUTTONDOWN:
|
||||
break;
|
||||
|
||||
case Common::EVENT_MOUSEMOVE:
|
||||
onMouseMove(event.mouse);
|
||||
break;
|
||||
|
||||
case Common::EVENT_KEYDOWN:
|
||||
onKeyDown(event.kbd.keycode);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_system->delayMillis(20);
|
||||
}
|
||||
|
||||
_screen.free();
|
||||
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
void ComposerEngine::onMouseDown(const Common::Point &pos) {
|
||||
if (!_mouseEnabled || !_mouseVisible)
|
||||
return;
|
||||
|
||||
const Sprite *sprite = getSpriteAtPos(pos);
|
||||
const Button *button = getButtonFor(sprite, pos);
|
||||
if (!button)
|
||||
return;
|
||||
|
||||
debug(3, "mouseDown on button id %d", button->_id);
|
||||
|
||||
// TODO: other buttons?
|
||||
uint16 buttonsDown = 1; // MK_LBUTTON
|
||||
|
||||
uint16 spriteId = sprite ? sprite->_id : 0;
|
||||
runScript(button->_scriptId, (getGameType() == GType_ComposerV1) ? 0 : button->_id, buttonsDown, spriteId);
|
||||
}
|
||||
|
||||
void ComposerEngine::onMouseMove(const Common::Point &pos) {
|
||||
_lastMousePos = pos;
|
||||
|
||||
if (!_mouseEnabled || !_mouseVisible)
|
||||
return;
|
||||
|
||||
// TODO: do we need to keep track of this?
|
||||
uint buttonsDown = 0;
|
||||
|
||||
const Sprite *sprite = getSpriteAtPos(pos);
|
||||
const Button *button = getButtonFor(sprite, pos);
|
||||
if (_lastButton != button) {
|
||||
if (_lastButton && _lastButton->_scriptIdRollOff)
|
||||
runScript(_lastButton->_scriptIdRollOff, (getGameType() == GType_ComposerV1) ? 0 : _lastButton->_id, buttonsDown, 0);
|
||||
_lastButton = button;
|
||||
if (_lastButton && _lastButton->_scriptIdRollOn)
|
||||
runScript(_lastButton->_scriptIdRollOn, (getGameType() == GType_ComposerV1) ? 0 : _lastButton->_id, buttonsDown, 0);
|
||||
}
|
||||
|
||||
if (_mouseSpriteId) {
|
||||
addSprite(_mouseSpriteId, 0, 0, _lastMousePos - _mouseOffset);
|
||||
}
|
||||
_needsUpdate = true;
|
||||
}
|
||||
|
||||
void ComposerEngine::onKeyDown(uint16 keyCode) {
|
||||
runEvent(kEventKeyDown, keyCode, 0, 0);
|
||||
runEvent(kEventChar, keyCode, 0, 0);
|
||||
|
||||
for (Common::List<Library>::iterator i = _libraries.begin(); i != _libraries.end(); i++) {
|
||||
for (Common::List<KeyboardHandler>::iterator j = i->_keyboardHandlers.begin(); j != i->_keyboardHandlers.end(); j++) {
|
||||
const KeyboardHandler &handler = *j;
|
||||
if (keyCode != handler.keyId)
|
||||
continue;
|
||||
|
||||
int modifiers = g_system->getEventManager()->getModifierState();
|
||||
switch (handler.modifierId) {
|
||||
case 0x10: // shift
|
||||
if (!(modifiers & Common::KBD_SHIFT))
|
||||
continue;
|
||||
break;
|
||||
case 0x11: // control
|
||||
if (!(modifiers & Common::KBD_CTRL))
|
||||
continue;
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
warning("unknown keyb modifier %d", handler.modifierId);
|
||||
continue;
|
||||
}
|
||||
|
||||
runScript(handler.scriptId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ComposerEngine::setCursor(uint16 id, const Common::Point &offset) {
|
||||
_mouseOffset = offset;
|
||||
if (_mouseSpriteId == id)
|
||||
return;
|
||||
|
||||
if (_mouseSpriteId && _mouseVisible) {
|
||||
removeSprite(_mouseSpriteId, 0);
|
||||
}
|
||||
_mouseSpriteId = id;
|
||||
if (_mouseSpriteId && _mouseVisible) {
|
||||
addSprite(_mouseSpriteId, 0, 0, _lastMousePos - _mouseOffset);
|
||||
}
|
||||
}
|
||||
|
||||
void ComposerEngine::setCursorVisible(bool visible) {
|
||||
if (visible && !_mouseVisible) {
|
||||
_mouseVisible = true;
|
||||
if (_mouseSpriteId)
|
||||
addSprite(_mouseSpriteId, 0, 0, _lastMousePos - _mouseOffset);
|
||||
else
|
||||
CursorMan.showMouse(true);
|
||||
onMouseMove(_lastMousePos);
|
||||
} else if (!visible && _mouseVisible) {
|
||||
_mouseVisible = false;
|
||||
if (_mouseSpriteId)
|
||||
removeSprite(_mouseSpriteId, 0);
|
||||
else
|
||||
CursorMan.showMouse(false);
|
||||
}
|
||||
}
|
||||
|
||||
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::Path ComposerEngine::getFilename(const Common::String §ion, uint id) {
|
||||
Common::String key = Common::String::format("%d", id);
|
||||
Common::String filename = getStringFromConfig(section, key);
|
||||
|
||||
return mangleFilename(filename);
|
||||
}
|
||||
|
||||
Common::Path ComposerEngine::mangleFilename(Common::String filename) {
|
||||
while (filename.size() && (filename[0] == '~' || filename[0] == ':' || filename[0] == '\\'))
|
||||
filename = filename.c_str() + 1;
|
||||
|
||||
uint slashesToStrip = _directoriesToStrip;
|
||||
|
||||
if (filename.hasPrefix(".."))
|
||||
slashesToStrip = 1;
|
||||
|
||||
while (slashesToStrip--) {
|
||||
for (uint i = 0; i < filename.size(); i++) {
|
||||
if (filename[i] != '\\' && filename[i] != ':')
|
||||
continue;
|
||||
filename = filename.c_str() + i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Common::String outFilename;
|
||||
for (uint i = 0; i < filename.size(); i++) {
|
||||
if (filename[i] == '\\' || filename[i] == ':')
|
||||
outFilename += '/';
|
||||
else
|
||||
outFilename += filename[i];
|
||||
}
|
||||
return Common::Path(outFilename, '/');
|
||||
}
|
||||
|
||||
void ComposerEngine::loadLibrary(uint id) {
|
||||
if (getGameType() == GType_ComposerV1 && !_libraries.empty()) {
|
||||
// kill the previous page, starting with any scripts running on it
|
||||
|
||||
for (Common::List<OldScript *>::iterator i = _oldScripts.begin(); i != _oldScripts.end(); i++)
|
||||
delete *i;
|
||||
_oldScripts.clear();
|
||||
|
||||
Library *library = &_libraries.front();
|
||||
unloadLibrary(library->_id);
|
||||
}
|
||||
|
||||
Common::Path path;
|
||||
Common::String oldGroup = _bookGroup;
|
||||
if (getGameType() == GType_ComposerV1) {
|
||||
Common::String filename;
|
||||
if (getPlatform() == Common::kPlatformMacintosh) {
|
||||
if (!id || _bookGroup.empty())
|
||||
filename = getStringFromConfig("splash.rsc", "100");
|
||||
else
|
||||
filename = getStringFromConfig(_bookGroup + ".rsc", Common::String::format("%d", id));
|
||||
}
|
||||
else {
|
||||
if (!id || _bookGroup.empty())
|
||||
filename = getStringFromConfig("Common", "StartPage");
|
||||
else
|
||||
filename = getStringFromConfig(_bookGroup, Common::String::format("%d", id));
|
||||
}
|
||||
path = mangleFilename(filename);
|
||||
|
||||
// bookGroup is the basename of the path.
|
||||
_bookGroup.clear();
|
||||
_bookGroup = path.baseName();
|
||||
uint i = _bookGroup.findFirstOf('.');
|
||||
if (i != Common::String::npos) {
|
||||
_bookGroup.erase(i);
|
||||
}
|
||||
} else {
|
||||
if (!id)
|
||||
id = atoi(getStringFromConfig("Common", "StartUp").c_str());
|
||||
path = getFilename("Libs", id);
|
||||
}
|
||||
|
||||
Library library;
|
||||
|
||||
library._id = id;
|
||||
library._group = oldGroup;
|
||||
library._archive = new ComposerArchive();
|
||||
if (!library._archive->openFile(path))
|
||||
error("failed to open '%s'", path.toString(Common::Path::kNativeSeparator).c_str());
|
||||
_libraries.push_front(library);
|
||||
|
||||
Library &newLib = _libraries.front();
|
||||
|
||||
Common::Array<uint16> buttonResources = library._archive->getResourceIDList(ID_BUTN);
|
||||
for (uint i = 0; i < buttonResources.size(); i++) {
|
||||
uint16 buttonId = buttonResources[i];
|
||||
Common::SeekableReadStream *stream = library._archive->getResource(ID_BUTN, buttonId);
|
||||
Button button(stream, buttonId, getGameType());
|
||||
|
||||
bool inserted = false;
|
||||
for (Common::List<Button>::iterator b = newLib._buttons.begin(); b != newLib._buttons.end(); b++) {
|
||||
if (button._zorder < b->_zorder)
|
||||
continue;
|
||||
newLib._buttons.insert(b, button);
|
||||
inserted = true;
|
||||
break;
|
||||
}
|
||||
if (!inserted)
|
||||
newLib._buttons.push_back(button);
|
||||
}
|
||||
|
||||
Common::Array<uint16> ambientResources = library._archive->getResourceIDList(ID_AMBI);
|
||||
for (uint i = 0; i < ambientResources.size(); i++) {
|
||||
Common::SeekableReadStream *stream = library._archive->getResource(ID_AMBI, ambientResources[i]);
|
||||
Button button(stream);
|
||||
newLib._buttons.insert(newLib._buttons.begin(), button);
|
||||
}
|
||||
|
||||
Common::Array<uint16> accelResources = library._archive->getResourceIDList(ID_ACEL);
|
||||
for (uint i = 0; i < accelResources.size(); i++) {
|
||||
Common::SeekableReadStream *stream = library._archive->getResource(ID_ACEL, accelResources[i]);
|
||||
KeyboardHandler handler;
|
||||
handler.keyId = stream->readUint16LE();
|
||||
handler.modifierId = stream->readUint16LE();
|
||||
handler.scriptId = stream->readUint16LE();
|
||||
newLib._keyboardHandlers.push_back(handler);
|
||||
}
|
||||
|
||||
Common::Array<uint16> randResources = library._archive->getResourceIDList(ID_RAND);
|
||||
for (uint i = 0; i < randResources.size(); i++) {
|
||||
Common::SeekableReadStream *stream = library._archive->getResource(ID_RAND, randResources[i]);
|
||||
Common::Array<RandomEvent> &events = _randomEvents[randResources[i]];
|
||||
uint16 count = stream->readUint16LE();
|
||||
for (uint j = 0; j < count; j++) {
|
||||
RandomEvent random;
|
||||
random.scriptId = stream->readUint16LE();
|
||||
random.weight = stream->readUint16LE();
|
||||
events.push_back(random);
|
||||
}
|
||||
delete stream;
|
||||
}
|
||||
|
||||
// add background sprite, if it exists
|
||||
if (hasResource(ID_BMAP, 1000))
|
||||
setBackground(1000);
|
||||
|
||||
// TODO: better CTBL logic
|
||||
loadCTBL(1000, 100);
|
||||
|
||||
// Run the startup script.
|
||||
runScript(1000, 0, 0, 0);
|
||||
|
||||
_mouseEnabled = true;
|
||||
onMouseMove(_lastMousePos);
|
||||
|
||||
runEvent(kEventLoad, id, 0, 0);
|
||||
}
|
||||
|
||||
void ComposerEngine::unloadLibrary(uint id) {
|
||||
for (Common::List<Library>::iterator i = _libraries.begin(); i != _libraries.end(); i++) {
|
||||
if (i->_id != id)
|
||||
continue;
|
||||
|
||||
for (Common::List<Animation *>::iterator j = _anims.begin(); j != _anims.end(); j++) {
|
||||
delete *j;
|
||||
}
|
||||
_anims.clear();
|
||||
stopPipes();
|
||||
|
||||
_randomEvents.clear();
|
||||
|
||||
for (Common::List<Sprite>::iterator j = _sprites.begin(); j != _sprites.end(); j++) {
|
||||
j->_surface.free();
|
||||
}
|
||||
_sprites.clear();
|
||||
i->_buttons.clear();
|
||||
|
||||
_lastButton = nullptr;
|
||||
|
||||
_mixer->stopAll();
|
||||
_audioStream = nullptr;
|
||||
|
||||
for (uint j = 0; j < _queuedScripts.size(); j++) {
|
||||
_queuedScripts[j]._count = 0;
|
||||
_queuedScripts[j]._scriptId = 0;
|
||||
}
|
||||
|
||||
delete i->_archive;
|
||||
_libraries.erase(i);
|
||||
|
||||
runEvent(kEventUnload, id, 0, 0);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
warning("tried to unload library %d, which isn't loaded", id);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
Button::Button(Common::SeekableReadStream *stream, uint16 id, uint gameType) {
|
||||
_id = id;
|
||||
|
||||
_type = stream->readUint16LE();
|
||||
_active = (_type & 0x8000) ? true : false;
|
||||
bool hasRollover = (gameType == GType_ComposerV1) && (_type & 0x4000);
|
||||
_type &= 0xfff;
|
||||
debug(9, "button %d: type %d, active %d", id, _type, _active);
|
||||
|
||||
uint16 size = 4;
|
||||
if (gameType == GType_ComposerV1) {
|
||||
stream->skip(2);
|
||||
|
||||
_zorder = 0;
|
||||
_scriptId = stream->readUint16LE();
|
||||
_scriptIdRollOn = 0;
|
||||
_scriptIdRollOff = 0;
|
||||
} else {
|
||||
_zorder = stream->readUint16LE();
|
||||
_scriptId = stream->readUint16LE();
|
||||
_scriptIdRollOn = stream->readUint16LE();
|
||||
_scriptIdRollOff = stream->readUint16LE();
|
||||
|
||||
stream->skip(4);
|
||||
|
||||
size = stream->readUint16LE();
|
||||
}
|
||||
|
||||
switch (_type) {
|
||||
case kButtonRect:
|
||||
case kButtonEllipse:
|
||||
if (size != 4)
|
||||
error("button %d of type %d had %d points, not 4", id, _type, size);
|
||||
_rect.left = stream->readSint16LE();
|
||||
_rect.top = stream->readSint16LE();
|
||||
_rect.right = stream->readSint16LE();
|
||||
_rect.bottom = stream->readSint16LE();
|
||||
break;
|
||||
case kButtonSprites:
|
||||
if (gameType == GType_ComposerV1)
|
||||
error("encountered kButtonSprites in V1 data");
|
||||
for (uint i = 0; i < size; i++) {
|
||||
_spriteIds.push_back(stream->readUint16LE());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error("unknown button type %d", _type);
|
||||
}
|
||||
|
||||
if (hasRollover) {
|
||||
_scriptIdRollOn = stream->readUint16LE();
|
||||
_scriptIdRollOff = stream->readUint16LE();
|
||||
}
|
||||
|
||||
delete stream;
|
||||
}
|
||||
|
||||
// AMBI-style button
|
||||
Button::Button(Common::SeekableReadStream *stream) {
|
||||
_id = 0;
|
||||
_zorder = 0;
|
||||
_active = true;
|
||||
_type = kButtonSprites;
|
||||
_scriptIdRollOn = 0;
|
||||
_scriptIdRollOff = 0;
|
||||
|
||||
_scriptId = stream->readUint16LE();
|
||||
|
||||
uint16 count = stream->readUint16LE();
|
||||
for (uint j = 0; j < count; j++) {
|
||||
uint16 spriteId = stream->readUint16LE();
|
||||
_spriteIds.push_back(spriteId);
|
||||
}
|
||||
|
||||
delete stream;
|
||||
}
|
||||
|
||||
bool Button::contains(const Common::Point &pos) const {
|
||||
switch (_type) {
|
||||
case kButtonRect:
|
||||
return _rect.contains(pos);
|
||||
case kButtonEllipse:
|
||||
if (!_rect.contains(pos))
|
||||
return false;
|
||||
{
|
||||
int16 a = _rect.width() / 2;
|
||||
int16 b = _rect.height() / 2;
|
||||
if (!a || !b)
|
||||
return false;
|
||||
Common::Point adjustedPos = pos - Common::Point(_rect.left + a, _rect.top + b);
|
||||
return ((adjustedPos.x*adjustedPos.x)/(a*a) + (adjustedPos.y*adjustedPos.y)/(b*b) < 1);
|
||||
}
|
||||
case kButtonSprites:
|
||||
return false;
|
||||
default:
|
||||
error("internal error (button type %d)", _type);
|
||||
}
|
||||
}
|
||||
|
||||
const Button *ComposerEngine::getButtonFor(const Sprite *sprite, const Common::Point &pos) {
|
||||
for (Common::List<Library>::iterator l = _libraries.begin(); l != _libraries.end(); l++) {
|
||||
for (Common::List<Button>::iterator i = l->_buttons.reverse_begin(); i != l->_buttons.end(); --i) {
|
||||
if (!i->_active)
|
||||
continue;
|
||||
|
||||
if (i->_spriteIds.empty()) {
|
||||
if (i->contains(pos))
|
||||
return &(*i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!sprite)
|
||||
continue;
|
||||
|
||||
for (uint j = 0; j < i->_spriteIds.size(); j++) {
|
||||
if (i->_spriteIds[j] == sprite->_id)
|
||||
return &(*i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ComposerEngine::setButtonActive(uint16 id, bool active) {
|
||||
for (Common::List<Library>::iterator l = _libraries.begin(); l != _libraries.end(); l++) {
|
||||
for (Common::List<Button>::iterator i = l->_buttons.begin(); i != l->_buttons.end(); i++) {
|
||||
if (i->_id != id)
|
||||
continue;
|
||||
i->_active = active;
|
||||
}
|
||||
}
|
||||
|
||||
onMouseMove(_lastMousePos);
|
||||
}
|
||||
|
||||
} // End of namespace Composer
|
||||
270
engines/composer/composer.h
Normal file
270
engines/composer/composer.h
Normal file
@@ -0,0 +1,270 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef COMPOSER_COMPOSER_H
|
||||
#define COMPOSER_COMPOSER_H
|
||||
|
||||
#include "common/formats/ini-file.h"
|
||||
#include "common/random.h"
|
||||
#include "common/system.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/debug-channels.h"
|
||||
#include "common/error.h"
|
||||
#include "common/serializer.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/rect.h"
|
||||
|
||||
#include "engines/engine.h"
|
||||
#include "engines/util.h"
|
||||
|
||||
#include "gui/debugger.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
|
||||
#include "composer/resource.h"
|
||||
#include "composer/console.h"
|
||||
#include "composer/detection.h"
|
||||
|
||||
namespace Audio {
|
||||
class QueuingAudioStream;
|
||||
}
|
||||
|
||||
namespace Composer {
|
||||
|
||||
class Archive;
|
||||
struct Animation;
|
||||
class ComposerEngine;
|
||||
class Pipe;
|
||||
struct Sprite;
|
||||
|
||||
enum {
|
||||
kButtonRect = 0,
|
||||
kButtonEllipse = 1,
|
||||
kButtonSprites = 4
|
||||
};
|
||||
|
||||
class Button {
|
||||
public:
|
||||
Button() { }
|
||||
Button(Common::SeekableReadStream *stream, uint16 id, uint gameType);
|
||||
Button(Common::SeekableReadStream *stream);
|
||||
|
||||
bool contains(const Common::Point &pos) const;
|
||||
|
||||
uint16 _id;
|
||||
uint16 _type;
|
||||
uint16 _zorder;
|
||||
uint16 _scriptId;
|
||||
uint16 _scriptIdRollOn;
|
||||
uint16 _scriptIdRollOff;
|
||||
bool _active;
|
||||
|
||||
Common::Rect _rect;
|
||||
Common::Array<uint16> _spriteIds;
|
||||
};
|
||||
|
||||
enum {
|
||||
kEventAnimStarted = 1,
|
||||
kEventAnimDone = 2,
|
||||
kEventLoad = 3,
|
||||
kEventUnload = 4,
|
||||
kEventKeyDown = 5,
|
||||
kEventChar = 6,
|
||||
kEventKeyUp = 7
|
||||
};
|
||||
|
||||
struct KeyboardHandler {
|
||||
uint16 keyId;
|
||||
uint16 modifierId;
|
||||
uint16 scriptId;
|
||||
};
|
||||
|
||||
struct RandomEvent {
|
||||
uint16 weight;
|
||||
uint16 scriptId;
|
||||
};
|
||||
|
||||
struct Library {
|
||||
uint _id;
|
||||
Archive *_archive;
|
||||
|
||||
Common::String _group;
|
||||
Common::List<Button> _buttons;
|
||||
Common::List<KeyboardHandler> _keyboardHandlers;
|
||||
};
|
||||
|
||||
struct QueuedScript {
|
||||
uint32 _baseTime;
|
||||
uint32 _duration;
|
||||
uint32 _count;
|
||||
uint16 _scriptId;
|
||||
};
|
||||
|
||||
struct PendingPageChange {
|
||||
PendingPageChange() { }
|
||||
PendingPageChange(uint16 id, bool remove) : _pageId(id), _remove(remove) { }
|
||||
|
||||
uint16 _pageId;
|
||||
bool _remove;
|
||||
};
|
||||
|
||||
struct OldScript {
|
||||
OldScript(uint16 id, Common::SeekableReadStream *stream);
|
||||
~OldScript();
|
||||
|
||||
uint16 _id;
|
||||
|
||||
uint32 _size;
|
||||
Common::SeekableReadStream *_stream;
|
||||
|
||||
uint16 _zorder;
|
||||
uint32 _currDelay;
|
||||
};
|
||||
|
||||
class ComposerEngine : public Engine {
|
||||
protected:
|
||||
Common::Error run() override;
|
||||
|
||||
template <typename T>
|
||||
void syncArray(Common::Serializer &ser, Common::Array<T> &data, Common::Serializer::Version minVersion = 0, Common::Serializer::Version maxVersion = Common::Serializer::kLastVersion);
|
||||
template <typename T>
|
||||
void syncList(Common::Serializer &ser, Common::List<T> &data, Common::Serializer::Version minVersion = 0, Common::Serializer::Version maxVersion = Common::Serializer::kLastVersion);
|
||||
template <typename T>
|
||||
void syncListReverse(Common::Serializer &ser, Common::List<T> &data, Common::Serializer::Version minVersion = 0, Common::Serializer::Version maxVersion = Common::Serializer::kLastVersion);
|
||||
template <typename T>
|
||||
void sync(Common::Serializer &ser, T &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion);
|
||||
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override { return true; }
|
||||
Common::Error loadGameState(int slot) override;
|
||||
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override { return true; }
|
||||
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override;
|
||||
|
||||
public:
|
||||
ComposerEngine(OSystem *syst, const ComposerGameDescription *gameDesc);
|
||||
~ComposerEngine() override;
|
||||
|
||||
bool hasFeature(EngineFeature f) const override;
|
||||
|
||||
int getGameType() const;
|
||||
const char *getGameId() const;
|
||||
uint32 getFeatures() const;
|
||||
Common::Language getLanguage() const;
|
||||
Common::Platform getPlatform() const;
|
||||
bool loadDetectedConfigFile(Common::INIFile &configFile) const;
|
||||
|
||||
const ComposerGameDescription *_gameDescription;
|
||||
|
||||
private:
|
||||
Common::RandomSource *_rnd;
|
||||
|
||||
Audio::SoundHandle _soundHandle;
|
||||
Audio::QueuingAudioStream *_audioStream;
|
||||
uint16 _currSoundPriority;
|
||||
|
||||
uint32 _currentTime, _lastTime, _timeDelta;
|
||||
|
||||
bool _needsUpdate;
|
||||
Common::Array<Common::Rect> _dirtyRects;
|
||||
Graphics::Surface _screen;
|
||||
Common::List<Sprite> _sprites;
|
||||
|
||||
uint _directoriesToStrip;
|
||||
Common::INIFile _bookIni;
|
||||
Common::String _bookGroup;
|
||||
Common::List<Library> _libraries;
|
||||
Common::Array<PendingPageChange> _pendingPageChanges;
|
||||
|
||||
Common::Array<uint16> _stack;
|
||||
Common::Array<uint16> _vars;
|
||||
|
||||
Common::List<OldScript *> _oldScripts;
|
||||
Common::Array<QueuedScript> _queuedScripts;
|
||||
Common::List<Animation *> _anims;
|
||||
Common::List<Pipe *> _pipes;
|
||||
Common::Array<Common::SeekableReadStream *> _pipeStreams;
|
||||
|
||||
Common::HashMap<uint16, Common::Array<RandomEvent> > _randomEvents;
|
||||
|
||||
void onMouseDown(const Common::Point &pos);
|
||||
void onMouseMove(const Common::Point &pos);
|
||||
void onKeyDown(uint16 keyCode);
|
||||
void setCursor(uint16 id, const Common::Point &offset);
|
||||
void setCursorVisible(bool visible);
|
||||
|
||||
bool _mouseEnabled;
|
||||
bool _mouseVisible;
|
||||
Common::Point _lastMousePos;
|
||||
const Button *_lastButton;
|
||||
uint16 _mouseSpriteId;
|
||||
Common::Point _mouseOffset;
|
||||
|
||||
Common::String getSaveStateName(int slot) const override;
|
||||
Common::String getStringFromConfig(const Common::String §ion, const Common::String &key);
|
||||
Common::Path getFilename(const Common::String §ion, uint id);
|
||||
Common::Path mangleFilename(Common::String filename);
|
||||
void loadLibrary(uint id);
|
||||
void unloadLibrary(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 runOldScript(uint16 id, uint16 wait);
|
||||
void stopOldScript(uint16 id);
|
||||
void tickOldScripts();
|
||||
bool tickOldScript(OldScript *script);
|
||||
|
||||
void loadAnimation(Animation *&anim, uint16 animId, int16 x, int16 y, int16 eventParam, int32 size = 0);
|
||||
void playAnimation(uint16 animId, int16 param1, int16 param2, int16 param3);
|
||||
void stopAnimation(Animation *anim, bool localOnly = false, bool pipesOnly = false);
|
||||
void playWaveForAnim(uint16 id, uint16 priority, bool bufferingOnly);
|
||||
void processAnimFrame();
|
||||
|
||||
void playPipe(uint16 id);
|
||||
void stopPipes();
|
||||
|
||||
bool spriteVisible(uint16 id, uint16 animId);
|
||||
Sprite *addSprite(uint16 id, uint16 animId, uint16 zorder, const Common::Point &pos);
|
||||
void removeSprite(uint16 id, uint16 animId);
|
||||
const Sprite *getSpriteAtPos(const Common::Point &pos);
|
||||
const Button *getButtonFor(const Sprite *sprite, const Common::Point &pos);
|
||||
void setButtonActive(uint16 id, bool active);
|
||||
|
||||
void dirtySprite(const Sprite &sprite);
|
||||
void redraw();
|
||||
void loadCTBL(uint16 id, uint fadePercent);
|
||||
void setBackground(uint16 id);
|
||||
void decompressBitmap(uint16 type, Common::SeekableReadStream *stream, byte *buffer, uint32 size, uint width, uint height);
|
||||
bool initSprite(Sprite &sprite);
|
||||
Common::SeekableReadStream *getStreamForSprite(uint16 id);
|
||||
void drawSprite(const Sprite &sprite);
|
||||
};
|
||||
|
||||
} // End of namespace Composer
|
||||
|
||||
#endif
|
||||
3
engines/composer/configure.engine
Normal file
3
engines/composer/configure.engine
Normal file
@@ -0,0 +1,3 @@
|
||||
# This file is included from the main "configure" script
|
||||
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
|
||||
add_engine composer "Magic Composer" yes "" "" "highres"
|
||||
30
engines/composer/console.cpp
Normal file
30
engines/composer/console.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "composer/composer.h"
|
||||
|
||||
namespace Composer {
|
||||
|
||||
Console::Console(ComposerEngine *vm) : GUI::Debugger() {
|
||||
_vm = vm;
|
||||
}
|
||||
|
||||
} // End of namespace Composer
|
||||
39
engines/composer/console.h
Normal file
39
engines/composer/console.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef COMPOSER_CONSOLE_H
|
||||
#define COMPOSER_CONSOLE_H
|
||||
|
||||
namespace Composer {
|
||||
|
||||
class ComposerEngine;
|
||||
|
||||
class Console : public GUI::Debugger {
|
||||
public:
|
||||
Console(ComposerEngine *vm);
|
||||
|
||||
private:
|
||||
ComposerEngine *_vm;
|
||||
};
|
||||
|
||||
} // End of namespace Composer
|
||||
|
||||
#endif /* COMPOSER_CONSOLE_H */
|
||||
3
engines/composer/credits.pl
Normal file
3
engines/composer/credits.pl
Normal file
@@ -0,0 +1,3 @@
|
||||
begin_section("Composer");
|
||||
add_person("Alyssa Milburn", "fuzzie", "");
|
||||
end_section();
|
||||
74
engines/composer/detection.cpp
Normal file
74
engines/composer/detection.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "base/plugins.h"
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
#include "composer/detection.h"
|
||||
|
||||
static const PlainGameDescriptor composerGames[] = {
|
||||
{"babayaga", "Magic Tales: Baba Yaga and the Magic Geese"},
|
||||
{"darby", "Darby the Dragon"},
|
||||
{"gregory", "Gregory and the Hot Air Balloon"},
|
||||
{"imoking", "Magic Tales: Imo and the King"},
|
||||
{"liam", "Magic Tales: Liam Finds a Story"},
|
||||
{"littlesamurai", "Magic Tales: The Little Samurai"},
|
||||
{"magictales", "Magic Tales"},
|
||||
{"princess", "Magic Tales: The Princess and the Crab"},
|
||||
{"sleepingcub", "Magic Tales: Sleeping Cub's Test of Courage"},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
#include "composer/detection_tables.h"
|
||||
|
||||
using namespace Composer;
|
||||
|
||||
// we match from data too, to stop detection from a non-top-level directory
|
||||
static const char *const directoryGlobs[] = {
|
||||
"data",
|
||||
"liam",
|
||||
"programs",
|
||||
"princess",
|
||||
"sleepcub",
|
||||
nullptr
|
||||
};
|
||||
|
||||
class ComposerMetaEngineDetection : public AdvancedMetaEngineDetection<Composer::ComposerGameDescription> {
|
||||
public:
|
||||
ComposerMetaEngineDetection() : AdvancedMetaEngineDetection(Composer::gameDescriptions, composerGames) {
|
||||
_maxScanDepth = 2;
|
||||
_directoryGlobs = directoryGlobs;
|
||||
}
|
||||
|
||||
const char *getName() const override {
|
||||
return "composer";
|
||||
}
|
||||
|
||||
const char *getEngineName() const override {
|
||||
return "Magic Composer";
|
||||
}
|
||||
|
||||
const char *getOriginalCopyright() const override {
|
||||
return "Copyright (C) 1995-1999 Animation Magic";
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_PLUGIN_STATIC(COMPOSER_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, ComposerMetaEngineDetection);
|
||||
50
engines/composer/detection.h
Normal file
50
engines/composer/detection.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef COMPOSER_DETECTION_H
|
||||
#define COMPOSER_DETECTION_H
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
namespace Composer {
|
||||
|
||||
enum GameType {
|
||||
GType_ComposerV1,
|
||||
GType_ComposerV2
|
||||
};
|
||||
|
||||
enum GameFileTypes {
|
||||
GAME_CONFIGFILE = 1 << 0, // Game configuration
|
||||
GAME_SCRIPTFILE = 1 << 1, // Game script
|
||||
GAME_EXECUTABLE = 1 << 2 // Game executable
|
||||
};
|
||||
|
||||
struct ComposerGameDescription {
|
||||
AD_GAME_DESCRIPTION_HELPERS(desc);
|
||||
|
||||
ADGameDescription desc;
|
||||
|
||||
int gameType;
|
||||
};
|
||||
|
||||
} // End of namespace Composer
|
||||
|
||||
#endif // COMPOSER_DETECTION_H
|
||||
727
engines/composer/detection_tables.h
Normal file
727
engines/composer/detection_tables.h
Normal file
@@ -0,0 +1,727 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Composer {
|
||||
|
||||
static const ComposerGameDescription gameDescriptions[] = {
|
||||
// Magic Tales: Baba Yaga and the Magic Geese - from bug #5964
|
||||
{
|
||||
{
|
||||
"babayaga",
|
||||
0,
|
||||
{
|
||||
{"book.ini", GAME_CONFIGFILE, "412b7f4b0ef07f442009d28e3a819974", 3852},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// Magic Tales: Baba Yaga and the Magic Geese Mac - from bug #5934, #7025
|
||||
{
|
||||
{
|
||||
"babayaga",
|
||||
0,
|
||||
{
|
||||
{"book.mac", GAME_CONFIGFILE, "d:d82143cbc4a36093250c7d6f80af1147", 1877},
|
||||
{"Baba Yaga", GAME_EXECUTABLE, "d:ae3a4445f42fe10253da7ee4ea0d37d6", 44321},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// Magic Tales: Baba Yaga and the Magic Geese German- from bug #10171
|
||||
{
|
||||
{
|
||||
"babayaga",
|
||||
0,
|
||||
{
|
||||
{"book.ini", GAME_CONFIGFILE, "2a20e73d33ecd0f2fa8123d4f9862f90", 3814},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// Magic Tales: Baba Yaga and the Magic Geese French- from bug #12122
|
||||
{
|
||||
{
|
||||
"babayaga",
|
||||
0,
|
||||
{
|
||||
{"book.ini", GAME_CONFIGFILE, "966a4ee9226d96085c953bb4f9426c06", 3940},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// Magic Tales: Imo and the King - from bug #5964
|
||||
{
|
||||
{
|
||||
"imoking",
|
||||
0,
|
||||
{
|
||||
{"book.ini", GAME_CONFIGFILE, "62b52a1763cce7d7d6ccde9f9d32fd4b", 3299},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// Magic Tales: Imo and the King Hebrew Windows
|
||||
{
|
||||
{
|
||||
"imoking",
|
||||
0,
|
||||
{
|
||||
{"book.ini", GAME_CONFIGFILE, "2b3eb997e8f55a03e81f67563c40adf4", 3337},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::HE_ISR,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// Magic Tales: Imo and the King French Windows. Bugreport #12123
|
||||
{
|
||||
{
|
||||
"imoking",
|
||||
0,
|
||||
{
|
||||
{"book.ini", GAME_CONFIGFILE, "753d3be27f54d564d8d1bff2b9f0d14f", 3375},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// Magic Tales: Imo and the King Hebrew Macintosh
|
||||
{
|
||||
{
|
||||
"imoking",
|
||||
0,
|
||||
{
|
||||
{"book.mac", GAME_CONFIGFILE, "d:4896a22874bb660f5ba26a0af111f9c0", 1868},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::HE_ISR,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// Baba Yaga Demo from Imo and the King Hebrew CD
|
||||
{
|
||||
{
|
||||
"babayaga",
|
||||
"Magic Tales: Baba Yaga and the Magic Geese Demo",
|
||||
{
|
||||
{"by_demo.ini", GAME_CONFIGFILE, "4a87806683add232916298d6b62b9420", 224},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_USEEXTRAASTITLE | ADGF_DEMO,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// Little Samurai Demo from Imo and the King Hebrew CD
|
||||
{
|
||||
{
|
||||
"littlesamurai",
|
||||
"Magic Tales: The Little Samurai Demo",
|
||||
{
|
||||
{"ls_demo.ini", GAME_CONFIGFILE, "462cad83006721d2491dde7ef7a2d243", 223},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_USEEXTRAASTITLE | ADGF_DEMO,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// Baba Yaga Demo from Imo and the King Hebrew CD
|
||||
{
|
||||
{
|
||||
"babayaga",
|
||||
"Magic Tales: Baba Yaga and the Magic Geese Demo",
|
||||
{
|
||||
{"book.mac", GAME_CONFIGFILE, "d:ed4a902df3b26d58e9c013f814a30ee8", 134},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_USEEXTRAASTITLE | ADGF_DEMO,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// Little Samurai Demo from Imo and the King Hebrew CD
|
||||
{
|
||||
{
|
||||
"littlesamurai",
|
||||
"Magic Tales: The Little Samurai Demo",
|
||||
{
|
||||
{"book.mac", GAME_CONFIGFILE, "d:57a82d563800001ed88b2742c3650a2d", 136},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_USEEXTRAASTITLE | ADGF_DEMO,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// Magic Tales: Imo and the King Mac - from bug #5934
|
||||
{
|
||||
{
|
||||
"imoking",
|
||||
0,
|
||||
{
|
||||
{"book.mac", GAME_CONFIGFILE, "d:190158751630f69c2b6cf146aa2f1efc", 1874},
|
||||
{"imo and the king", GAME_EXECUTABLE, "d:b0277885fec943b5f19409f35b33964c", 44242},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// Magic Tales: Imo and the King Mac Demo from Baba Yaga CD
|
||||
{
|
||||
{
|
||||
"imoking",
|
||||
"Magic Tales: Imo and the King Demo",
|
||||
{
|
||||
{"book.mac", GAME_CONFIGFILE, "d:b3456692eab14e9bdac64db6dad27be9", 136},
|
||||
{"imo and the king", GAME_EXECUTABLE, "d:764e0feea86cc201471f2b443161023b", 44321},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_USEEXTRAASTITLE | ADGF_DEMO,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// Magic Tales: Imo and the King German - from bug #10199
|
||||
{
|
||||
{
|
||||
"imoking",
|
||||
0,
|
||||
{
|
||||
{"book.ini", GAME_CONFIGFILE, "5925c6d4bf85d89b17208be4fcace5e8", 3274},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// Magic Tales: Imo and the King Italian - from bug #16343
|
||||
{
|
||||
{
|
||||
"imoking",
|
||||
0,
|
||||
{
|
||||
{"book.ini", GAME_CONFIGFILE, "c9bb23d35125b0721bd3ff439790cfe6", 3297},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::IT_ITA,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// Magic Tales: The Little Samurai - from bug #5964
|
||||
{
|
||||
{
|
||||
"littlesamurai",
|
||||
0,
|
||||
{
|
||||
{"book.ini", GAME_CONFIGFILE, "7a851869d022a9041e0dd11e5bace09b", 3747},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// Magic Tales: The Little Samurai - French from bug #12124
|
||||
{
|
||||
{
|
||||
"littlesamurai",
|
||||
0,
|
||||
{
|
||||
{"book.ini", GAME_CONFIGFILE, "1e9199e31b4f93c5e5caeedd3c2e7c0b", 3833},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// Magic Tales: The Little Samurai Mac - from bug #5934
|
||||
{
|
||||
{
|
||||
"littlesamurai",
|
||||
0,
|
||||
{
|
||||
{"book.mac", GAME_CONFIGFILE, "d:190158751630f69c2b6cf146aa2f1efc", 1874},
|
||||
{"The Little Samurai", GAME_EXECUTABLE, "d:38121dd649c24e8676aa108cf35d44b5", 44128},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// Magic Tales: The Little Samurai Hebrew Windows
|
||||
{
|
||||
{
|
||||
"littlesamurai",
|
||||
0,
|
||||
{
|
||||
{"book.ini", GAME_CONFIGFILE, "c5f2c84df04780e7e67c70ec85b780a8", 3789},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::HE_ISR,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// Magic Tales: The Little Samurai Hebrew Macintosh
|
||||
{
|
||||
{
|
||||
"littlesamurai",
|
||||
0,
|
||||
{
|
||||
{"book.mac", GAME_CONFIGFILE, "d:190158751630f69c2b6cf146aa2f1efc", 1874},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::HE_ISR,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// from Liam Finds a Story CD
|
||||
{
|
||||
{
|
||||
"magictales",
|
||||
"Magic Tales Demo: Baba Yaga, Samurai, Imo",
|
||||
{
|
||||
{"book.ini", GAME_CONFIGFILE, "dbc98c566f4ac61b544443524585dccb", AD_NO_SIZE},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_USEEXTRAASTITLE | ADGF_DEMO,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// Windows Demo from Little Samurai Hebrew CD
|
||||
{
|
||||
{
|
||||
"magictales",
|
||||
"Magic Tales Demo: Baba Yaga, Samurai, Imo",
|
||||
{
|
||||
{"demo.ini", GAME_CONFIGFILE, "ea784af960375834d655eb7281cd4500", 734},
|
||||
{"ik_demo.rsc", GAME_SCRIPTFILE, "31a2d65c4d22d327b8f5f205ab6e7ea3", 6680135},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::HE_ISR,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_USEEXTRAASTITLE | ADGF_DEMO,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
// Macintosh Demo from Little Samurai Hebrew CD
|
||||
{
|
||||
{
|
||||
"magictales",
|
||||
"Magic Tales Demo: Baba Yaga, Samurai, Imo",
|
||||
{
|
||||
{"demo.mac", GAME_CONFIGFILE, "d:6e775cda6539102d1ddee852bebf32c1", 488},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::HE_ISR,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_USEEXTRAASTITLE | ADGF_DEMO,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV1
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
"liam",
|
||||
0,
|
||||
{
|
||||
{"book.ini", GAME_CONFIGFILE, "fc9d9b9e72e7301d011b808606eaa15b", 834},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV2
|
||||
},
|
||||
|
||||
// Liam Finds a Story Mac - from bug #5931
|
||||
{
|
||||
{
|
||||
"liam",
|
||||
0,
|
||||
{
|
||||
{"liam finds a story.ini", GAME_CONFIGFILE, "d:85a1ca6002ded8572920bbdb73d35b0a", 747},
|
||||
{"page99.rsc", GAME_SCRIPTFILE, "d:11b0a19c6b6d73c39e2bd289a457c1dc", 355159},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV2
|
||||
},
|
||||
|
||||
// from Liam Finds a Story CD
|
||||
{
|
||||
{
|
||||
"magictales",
|
||||
"Magic Tales Demo: Sleeping Cub, Princess & Crab",
|
||||
{
|
||||
{"book.ini", GAME_CONFIGFILE, "3dede2522bb0886c95667b082987a87f", AD_NO_SIZE},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_USEEXTRAASTITLE | ADGF_DEMO,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV2
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
"darby",
|
||||
0,
|
||||
{
|
||||
{"book.ini", GAME_CONFIGFILE, "7e3404c559d058521fff2aebe5c427a8", 2545},
|
||||
{"page99.rsc", GAME_SCRIPTFILE, "49cc6b16caa1c5ec7d94a3c47eed9a02", 1286480},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV2
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
"darby",
|
||||
0,
|
||||
{
|
||||
{"Darby the Dragon.ini", GAME_CONFIGFILE, "d:d81f9214936fa70d42fc578908d4bb3d", 2950},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV2
|
||||
},
|
||||
|
||||
{ // Provided by msSeven - from bug Trac #10399
|
||||
{
|
||||
"darby",
|
||||
0,
|
||||
{
|
||||
{"page99.rsc", GAME_SCRIPTFILE, "d:ca350397f0c009649afc0cb6145921f0", 1286480},
|
||||
{"Dragor le dragon.ini", GAME_CONFIGFILE, "d:b0ccebb50cf8377486d86cb65ee7215d", 2240},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV2
|
||||
},
|
||||
|
||||
{ // Provided by Strangerke, "CD-Rom 100% Malin" Pack
|
||||
{
|
||||
"darby",
|
||||
0,
|
||||
{
|
||||
{"book.ini", GAME_CONFIGFILE, "285308372f7dddff2ca5a25c9192cf5c", AD_NO_SIZE},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV2
|
||||
},
|
||||
|
||||
{ // Provided by WindlePoons, "100% Kids Darby & Gregor" Pack. Bugreport #6825
|
||||
{
|
||||
"darby",
|
||||
0,
|
||||
{
|
||||
{"book.ini", GAME_CONFIGFILE, "285308372f7dddff2ca5a25c9192cf5c", 2545},
|
||||
{"page99.rsc", GAME_SCRIPTFILE, "40b4879e9ba6a34d6aa2a9d2e30c5ef7", 1286480},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV2
|
||||
},
|
||||
|
||||
{ // Provided by Niv Baehr, Bugreport #6878
|
||||
{
|
||||
"darby",
|
||||
0,
|
||||
{
|
||||
{"page99.rsc", GAME_SCRIPTFILE, "183463d18c050563dcdec2d9f9670515", AD_NO_SIZE},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::HE_ISR,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV2
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
"gregory",
|
||||
0,
|
||||
{
|
||||
{"book.ini", GAME_CONFIGFILE, "14a562dcf361773445255af9f3e94790", 2234},
|
||||
{"page99.rsc", GAME_SCRIPTFILE, "01f9381162467e052dfd4c704169ef3e", 388644},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV2
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
"gregory",
|
||||
0,
|
||||
{
|
||||
{"Gregory.ini", GAME_CONFIGFILE, "d:fa82f14731f28c7379c5a106df07a0d6", 2753},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV2
|
||||
},
|
||||
|
||||
{ // Provided by Strangerke, "CD-Rom 100% Malin" Pack
|
||||
{
|
||||
"gregory",
|
||||
0,
|
||||
{
|
||||
{"book.ini", GAME_CONFIGFILE, "e54fc5c00de5f94e908a969e445af5d0", AD_NO_SIZE},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV2
|
||||
},
|
||||
|
||||
{ // Provided by WindlePoons, "100% Kids Darby & Gregor" Pack. Bugreport #6825
|
||||
{
|
||||
"gregory",
|
||||
0,
|
||||
{
|
||||
{"book.ini", GAME_CONFIGFILE, "e54fc5c00de5f94e908a969e445af5d0", 2234},
|
||||
{"page99.rsc", GAME_SCRIPTFILE, "1ae6610de621a9901bf87b874fbf331f", 388644},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV2
|
||||
},
|
||||
|
||||
{ // Provided by sev
|
||||
{
|
||||
"princess",
|
||||
0,
|
||||
{
|
||||
{"book.ini", GAME_CONFIGFILE, "fb32572577b9a41ba299825ef1e3181e", 966},
|
||||
{"page99.rsc", GAME_SCRIPTFILE, "fd5ebd3b5e36c4651c50241619525355", 45418},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV2
|
||||
},
|
||||
|
||||
// The Princess and the Crab Mac - From Bug #5928
|
||||
{
|
||||
{
|
||||
"princess",
|
||||
0,
|
||||
{
|
||||
{"the princess and the crab.ini", GAME_CONFIGFILE, "d:f6b551a7304643004bd5e4df7ac1e76e", 877},
|
||||
{"page99.rsc", GAME_SCRIPTFILE, "d:fd5ebd3b5e36c4651c50241619525355", 45418},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV2
|
||||
},
|
||||
|
||||
{ // Provided by sev
|
||||
{
|
||||
"sleepingcub",
|
||||
0,
|
||||
{
|
||||
{"book.ini", GAME_CONFIGFILE, "0d329e592387009c6387a733a3ea2235", 964},
|
||||
{"page99.rsc", GAME_SCRIPTFILE, "219fbd9bd2ff87c7023814405d753145", 46916},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV2
|
||||
},
|
||||
|
||||
// Sleeping Cub Mac - From Bug #5927
|
||||
{
|
||||
{
|
||||
"sleepingcub",
|
||||
0,
|
||||
{
|
||||
{"sleeping cub.ini", GAME_CONFIGFILE, "d:39642a4036cb51443f5e90052c3ad0b2", 877},
|
||||
{"page99.rsc", GAME_SCRIPTFILE, "d:219fbd9bd2ff87c7023814405d753145", 46916},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOASPECT, GUIO_NOMIDI)
|
||||
},
|
||||
GType_ComposerV2
|
||||
},
|
||||
|
||||
{ AD_TABLE_END_MARKER, 0 }
|
||||
};
|
||||
|
||||
} // End of namespace Composer
|
||||
846
engines/composer/graphics.cpp
Normal file
846
engines/composer/graphics.cpp
Normal file
@@ -0,0 +1,846 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#include "graphics/paletteman.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
|
||||
#include "composer/composer.h"
|
||||
#include "composer/graphics.h"
|
||||
#include "composer/resource.h"
|
||||
|
||||
namespace Composer {
|
||||
|
||||
bool Sprite::contains(const Common::Point &pos) const {
|
||||
Common::Point adjustedPos = pos - _pos;
|
||||
|
||||
if (adjustedPos.x < 0 || adjustedPos.x >= _surface.w)
|
||||
return false;
|
||||
if (adjustedPos.y < 0 || adjustedPos.y >= _surface.h)
|
||||
return false;
|
||||
const byte *pixels = (const byte *)_surface.getPixels();
|
||||
return (pixels[(_surface.h - adjustedPos.y - 1) * _surface.w + adjustedPos.x] != 0);
|
||||
}
|
||||
|
||||
enum {
|
||||
kAnimOpEvent = 1,
|
||||
kAnimOpPlayWave = 2,
|
||||
kAnimOpPlayAnim = 3,
|
||||
kAnimOpDrawSprite = 4
|
||||
};
|
||||
|
||||
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();
|
||||
_size = unknown;
|
||||
|
||||
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.priority = _stream->readUint16LE();
|
||||
entry.state = _stream->readUint16LE();
|
||||
entry.counter = 0;
|
||||
entry.prevValue = 0;
|
||||
debug(8, "anim entry: %04x, %04x, %04x", entry.op, entry.priority, entry.state);
|
||||
_entries.push_back(entry);
|
||||
}
|
||||
|
||||
_offset = _stream->pos();
|
||||
}
|
||||
|
||||
Animation::~Animation() {
|
||||
delete _stream;
|
||||
}
|
||||
|
||||
void Animation::seekToCurrPos() {
|
||||
_stream->seek(_offset, SEEK_SET);
|
||||
}
|
||||
|
||||
void ComposerEngine::loadAnimation(Animation *&anim, uint16 animId, int16 x, int16 y, int16 eventParam, int32 size) {
|
||||
Common::SeekableReadStream *stream = nullptr;
|
||||
Pipe *newPipe = nullptr;
|
||||
|
||||
// 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);
|
||||
|
||||
// When loading from savegame, make sure we have the correct stream
|
||||
if ((!size) || (stream->size() >= size))
|
||||
break;
|
||||
stream = nullptr;
|
||||
}
|
||||
|
||||
// If we didn't find it, try the libraries.
|
||||
if (!stream) {
|
||||
if (!hasResource(ID_ANIM, animId)) {
|
||||
warning("ignoring attempt to play invalid anim %d", animId);
|
||||
return;
|
||||
}
|
||||
Common::List<Library>::iterator j;
|
||||
for (j = _libraries.begin(); j != _libraries.end(); j++) {
|
||||
if (!j->_archive->hasResource(ID_ANIM, animId))
|
||||
continue;
|
||||
|
||||
stream = j->_archive->getResource(ID_ANIM, animId);
|
||||
|
||||
// When loading from savegame, make sure we have the correct stream
|
||||
if ((!size) || (stream->size() >= size))
|
||||
break;
|
||||
stream = nullptr;
|
||||
}
|
||||
|
||||
uint32 type = j->_archive->getResourceFlags(ID_ANIM, animId);
|
||||
|
||||
// If the resource is a pipe itself, then load the pipe
|
||||
// and then fish the requested animation out of it.
|
||||
if (type != 1) {
|
||||
_pipeStreams.push_back(stream);
|
||||
newPipe = new Pipe(stream, animId);
|
||||
_pipes.push_front(newPipe);
|
||||
newPipe->nextFrame();
|
||||
stream = newPipe->getResource(ID_ANIM, animId, false);
|
||||
}
|
||||
}
|
||||
|
||||
anim = new Animation(stream, animId, Common::Point(x, y), eventParam);
|
||||
if (newPipe)
|
||||
newPipe->_anim = anim;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
stopAnimation(*i);
|
||||
}
|
||||
|
||||
Animation *anim = nullptr;
|
||||
loadAnimation(anim, animId, x, y, eventParam);
|
||||
if (anim != nullptr) {
|
||||
_anims.push_back(anim);
|
||||
runEvent(kEventAnimStarted, animId, eventParam, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void ComposerEngine::stopAnimation(Animation *anim, bool localOnly, bool pipesOnly) {
|
||||
// disable the animation
|
||||
anim->_state = 0;
|
||||
|
||||
// stop any animations it may have spawned
|
||||
for (uint j = 0; j < anim->_entries.size(); j++) {
|
||||
AnimationEntry &entry = anim->_entries[j];
|
||||
if (!entry.prevValue)
|
||||
continue;
|
||||
if (localOnly) {
|
||||
if (pipesOnly)
|
||||
continue;
|
||||
if (entry.op == kAnimOpDrawSprite) {
|
||||
removeSprite(entry.prevValue, anim->_id);
|
||||
} else if (entry.op == kAnimOpPlayWave) {
|
||||
if (_currSoundPriority >= entry.priority) {
|
||||
_mixer->stopAll();
|
||||
_audioStream = nullptr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (entry.op != kAnimOpPlayAnim)
|
||||
continue;
|
||||
for (Common::List<Animation *>::iterator i = _anims.begin(); i != _anims.end(); i++) {
|
||||
if ((*i)->_id == entry.prevValue)
|
||||
stopAnimation(*i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// kill any pipe 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;
|
||||
}
|
||||
}
|
||||
|
||||
void ComposerEngine::playWaveForAnim(uint16 id, uint16 priority, bool bufferingOnly) {
|
||||
if (_audioStream && _audioStream->numQueuedStreams() != 0) {
|
||||
if (_currSoundPriority < priority)
|
||||
return;
|
||||
if (_currSoundPriority > priority) {
|
||||
_mixer->stopAll();
|
||||
_audioStream = nullptr;
|
||||
}
|
||||
}
|
||||
Common::SeekableReadStream *stream = nullptr;
|
||||
bool fromPipe = true;
|
||||
if (!bufferingOnly && hasResource(ID_WAVE, id)) {
|
||||
stream = getResource(ID_WAVE, id);
|
||||
fromPipe = false;
|
||||
} 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;
|
||||
|
||||
uint32 size = stream->size();
|
||||
if (!fromPipe) {
|
||||
// non-pipe buffers have fixed wav header (data at +44, size at +40)
|
||||
stream->skip(40);
|
||||
size = stream->readUint32LE();
|
||||
}
|
||||
byte *buffer = (byte *)malloc(size);
|
||||
stream->read(buffer, size);
|
||||
if (!_audioStream)
|
||||
_audioStream = Audio::makeQueuingAudioStream(22050, false);
|
||||
_audioStream->queueBuffer(buffer, size, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED);
|
||||
_currSoundPriority = priority;
|
||||
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;
|
||||
|
||||
anim->seekToCurrPos();
|
||||
|
||||
if (anim->_state <= 1) {
|
||||
bool normalEnd = (anim->_state == 1);
|
||||
if (normalEnd) {
|
||||
runEvent(kEventAnimDone, anim->_id, anim->_eventParam, 0);
|
||||
}
|
||||
stopAnimation(anim, true, normalEnd);
|
||||
delete anim;
|
||||
i = _anims.reverse_erase(i);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint j = 0; j < anim->_entries.size(); j++) {
|
||||
AnimationEntry &entry = anim->_entries[j];
|
||||
if (entry.op != kAnimOpEvent)
|
||||
break;
|
||||
if (entry.counter) {
|
||||
entry.counter--;
|
||||
} else {
|
||||
if ((anim->_state > 1) && (anim->_stream->pos() + 2 > anim->_stream->size())) {
|
||||
warning("anim with id %d ended too soon", anim->_id);
|
||||
anim->_state = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
uint16 event = anim->_stream->readUint16LE();
|
||||
anim->_offset += 2;
|
||||
if (event == 0xffff) {
|
||||
entry.counter = anim->_stream->readUint16LE() - 1;
|
||||
anim->_offset += 2;
|
||||
} else {
|
||||
debug(4, "anim: event %d", event);
|
||||
runEvent(event, anim->_id, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Common::List<Animation *>::iterator i = _anims.begin(); i != _anims.end(); i++) {
|
||||
Animation *anim = *i;
|
||||
|
||||
// did the anim get disabled?
|
||||
if (anim->_state == 0) {
|
||||
stopAnimation(anim, true, false);
|
||||
delete anim;
|
||||
i = _anims.reverse_erase(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
anim->_state--;
|
||||
|
||||
bool foundWait = false;
|
||||
for (uint j = 0; j < anim->_entries.size(); j++) {
|
||||
AnimationEntry &entry = anim->_entries[j];
|
||||
|
||||
// only skip these at the start
|
||||
if (!foundWait && (entry.op == kAnimOpEvent))
|
||||
continue;
|
||||
foundWait = true;
|
||||
|
||||
if (entry.counter) {
|
||||
entry.counter--;
|
||||
if ((entry.op == kAnimOpPlayWave) && entry.prevValue) {
|
||||
debug(4, "anim: continue play wave %d", entry.prevValue);
|
||||
playWaveForAnim(entry.prevValue, entry.priority, true);
|
||||
}
|
||||
} else {
|
||||
anim->seekToCurrPos();
|
||||
if ((anim->_state > 1) && (anim->_stream->pos() + 2 > anim->_stream->size())) {
|
||||
warning("anim with id %d ended too soon", anim->_id);
|
||||
anim->_state = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
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 kAnimOpEvent:
|
||||
debug(4, "anim: event %d", data);
|
||||
runEvent(data, anim->_id, 0, 0);
|
||||
break;
|
||||
case kAnimOpPlayWave:
|
||||
debug(4, "anim: play wave %d", data);
|
||||
playWaveForAnim(data, entry.priority, false);
|
||||
break;
|
||||
case kAnimOpPlayAnim:
|
||||
debug(4, "anim: play anim %d", data);
|
||||
playAnimation(data, anim->_basePos.x, anim->_basePos.y, 1);
|
||||
break;
|
||||
case kAnimOpDrawSprite:
|
||||
if (!data || (entry.prevValue && (data != entry.prevValue))) {
|
||||
debug(4, "anim: erase sprite %d", entry.prevValue);
|
||||
removeSprite(entry.prevValue, anim->_id);
|
||||
}
|
||||
if (data) {
|
||||
int16 x = anim->_stream->readSint16LE();
|
||||
int16 y = anim->_stream->readSint16LE();
|
||||
Common::Point pos(x, y);
|
||||
anim->_offset += 4;
|
||||
uint16 animId = anim->_id;
|
||||
if (anim->_state == entry.state)
|
||||
animId = 0;
|
||||
debug(4, "anim: draw sprite %d at (relative) %d,%d", data, x, y);
|
||||
bool wasVisible = spriteVisible(data, animId);
|
||||
addSprite(data, animId, entry.priority, anim->_basePos + pos);
|
||||
if (wasVisible) {
|
||||
// make sure modified sprite isn't removed by another entry
|
||||
for (uint k = 0; k < anim->_entries.size(); k++) {
|
||||
if (anim->_entries[k].op != kAnimOpDrawSprite)
|
||||
continue;
|
||||
if (anim->_entries[k].prevValue == data)
|
||||
anim->_entries[k].prevValue = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
warning("unknown anim op %d", entry.op);
|
||||
}
|
||||
|
||||
entry.prevValue = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Common::List<Pipe *>::iterator j = _pipes.begin(); j != _pipes.end(); j++) {
|
||||
Pipe *pipe = *j;
|
||||
pipe->nextFrame();
|
||||
|
||||
// V1 pipe audio; see OldPipe
|
||||
if (pipe->hasResource(ID_WAVE, 0xffff))
|
||||
playWaveForAnim(0xffff, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
void ComposerEngine::playPipe(uint16 id) {
|
||||
stopPipes();
|
||||
|
||||
if (!hasResource(ID_PIPE, id)) {
|
||||
error("couldn't find pipe %d", id);
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *stream = getResource(ID_PIPE, id);
|
||||
OldPipe *pipe = new OldPipe(stream, id);
|
||||
_pipes.push_front(pipe);
|
||||
//pipe->nextFrame();
|
||||
|
||||
const Common::Array<uint16> *scripts = pipe->getScripts();
|
||||
if (scripts && !scripts->empty())
|
||||
runScript((*scripts)[0], 1, 0, 0);
|
||||
}
|
||||
|
||||
void ComposerEngine::stopPipes() {
|
||||
for (Common::List<Pipe *>::iterator j = _pipes.begin(); j != _pipes.end(); j++) {
|
||||
const Common::Array<uint16> *scripts = (*j)->getScripts();
|
||||
if (scripts) {
|
||||
for (uint i = 0; i < scripts->size(); i++) {
|
||||
removeSprite((*scripts)[i], 0);
|
||||
stopOldScript((*scripts)[i]);
|
||||
}
|
||||
}
|
||||
delete *j;
|
||||
}
|
||||
|
||||
_pipes.clear();
|
||||
|
||||
// substreams may need to remain valid until the end of a page
|
||||
for (uint i = 0; i < _pipeStreams.size(); i++)
|
||||
delete _pipeStreams[i];
|
||||
_pipeStreams.clear();
|
||||
}
|
||||
|
||||
bool ComposerEngine::spriteVisible(uint16 id, uint16 animId) {
|
||||
for (Common::List<Sprite>::iterator i = _sprites.begin(); i != _sprites.end(); i++) {
|
||||
if (i->_id != id)
|
||||
continue;
|
||||
if (i->_animId && animId && (i->_animId != animId))
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Sprite *ComposerEngine::addSprite(uint16 id, uint16 animId, uint16 zorder, const Common::Point &pos) {
|
||||
Sprite sprite;
|
||||
bool foundSprite = false;
|
||||
|
||||
// re-use old sprite, if any (the BMAP for this id might well have
|
||||
// changed in the meantime, but the scripts depend on that!)
|
||||
for (Common::List<Sprite>::iterator i = _sprites.begin(); i != _sprites.end(); i++) {
|
||||
if (i->_id != id)
|
||||
continue;
|
||||
if (getGameType() == GType_ComposerV1) {
|
||||
if (i->_animId != animId)
|
||||
continue;
|
||||
} else if (i->_animId && animId && (i->_animId != animId))
|
||||
continue;
|
||||
|
||||
dirtySprite(*i);
|
||||
|
||||
// if the zordering is identical, modify it in-place
|
||||
if (i->_zorder == zorder) {
|
||||
i->_animId = animId;
|
||||
i->_pos = pos;
|
||||
dirtySprite(*i);
|
||||
return &(*i);
|
||||
}
|
||||
|
||||
// otherwise, take a copy and remove it from the list
|
||||
sprite = *i;
|
||||
foundSprite = true;
|
||||
_sprites.erase(i);
|
||||
break;
|
||||
}
|
||||
|
||||
sprite._animId = animId;
|
||||
sprite._zorder = zorder;
|
||||
sprite._pos = pos;
|
||||
|
||||
if (!foundSprite) {
|
||||
sprite._id = id;
|
||||
if (!initSprite(sprite)) {
|
||||
debug(1, "ignoring addSprite on invalid sprite %d", id);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
dirtySprite(sprite);
|
||||
|
||||
for (Common::List<Sprite>::iterator i = _sprites.begin(); i != _sprites.end(); i++) {
|
||||
if (sprite._zorder <= i->_zorder)
|
||||
continue;
|
||||
// insert *before* this sprite
|
||||
_sprites.insert(i, sprite);
|
||||
--i;
|
||||
return &(*i);
|
||||
}
|
||||
_sprites.push_back(sprite);
|
||||
return &_sprites.back();
|
||||
}
|
||||
|
||||
void ComposerEngine::removeSprite(uint16 id, uint16 animId) {
|
||||
for (Common::List<Sprite>::iterator i = _sprites.begin(); i != _sprites.end(); i++) {
|
||||
if (!i->_id || (id && i->_id != id))
|
||||
continue;
|
||||
if (getGameType() == GType_ComposerV1) {
|
||||
if (i->_animId != animId)
|
||||
continue;
|
||||
} else if (i->_animId && animId && (i->_animId != animId))
|
||||
continue;
|
||||
dirtySprite(*i);
|
||||
i->_surface.free();
|
||||
i = _sprites.reverse_erase(i);
|
||||
if (id)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const Sprite *ComposerEngine::getSpriteAtPos(const Common::Point &pos) {
|
||||
for (Common::List<Sprite>::iterator i = _sprites.reverse_begin(); i != _sprites.end(); --i) {
|
||||
// avoid highest-level objects (e.g. the cursor)
|
||||
if (!i->_zorder)
|
||||
continue;
|
||||
|
||||
if (i->contains(pos))
|
||||
return &(*i);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ComposerEngine::dirtySprite(const Sprite &sprite) {
|
||||
Common::Rect rect(sprite._pos.x, sprite._pos.y, sprite._pos.x + sprite._surface.w, sprite._pos.y + sprite._surface.h);
|
||||
rect.clip(_screen.w, _screen.h);
|
||||
if (rect.isEmpty())
|
||||
return;
|
||||
|
||||
for (uint i = 0; i < _dirtyRects.size(); i++) {
|
||||
if (!_dirtyRects[i].intersects(rect))
|
||||
continue;
|
||||
_dirtyRects[i].extend(rect);
|
||||
return;
|
||||
}
|
||||
|
||||
_dirtyRects.push_back(rect);
|
||||
}
|
||||
|
||||
void ComposerEngine::redraw() {
|
||||
if (!_needsUpdate && _dirtyRects.empty())
|
||||
return;
|
||||
|
||||
for (Common::List<Sprite>::iterator i = _sprites.begin(); i != _sprites.end(); i++) {
|
||||
Common::Rect rect(i->_pos.x, i->_pos.y, i->_pos.x + i->_surface.w, i->_pos.y + i->_surface.h);
|
||||
bool intersects = false;
|
||||
for (uint j = 0; j < _dirtyRects.size(); j++) {
|
||||
if (!_dirtyRects[j].intersects(rect))
|
||||
continue;
|
||||
intersects = true;
|
||||
break;
|
||||
}
|
||||
if (!intersects)
|
||||
continue;
|
||||
drawSprite(*i);
|
||||
}
|
||||
|
||||
for (uint i = 0; i < _dirtyRects.size(); i++) {
|
||||
const Common::Rect &rect = _dirtyRects[i];
|
||||
byte *pixels = (byte *)_screen.getBasePtr(rect.left, rect.top);
|
||||
_system->copyRectToScreen(pixels, _screen.pitch, rect.left, rect.top, rect.width(), rect.height());
|
||||
}
|
||||
_system->updateScreen();
|
||||
|
||||
_needsUpdate = false;
|
||||
_dirtyRects.clear();
|
||||
}
|
||||
|
||||
void ComposerEngine::loadCTBL(uint16 id, uint fadePercent) {
|
||||
Common::SeekableReadStream *stream = getResource(ID_CTBL, id);
|
||||
|
||||
uint16 numEntries = stream->readUint16LE();
|
||||
debug(1, "CTBL: %d entries", numEntries);
|
||||
|
||||
if ((numEntries > 256) || (stream->size() < 2 + (numEntries * 3)))
|
||||
error("CTBL %d was invalid (%d entries, size %d)", id, numEntries, (int)stream->size());
|
||||
|
||||
byte buffer[256 * 3];
|
||||
stream->read(buffer, numEntries * 3);
|
||||
delete stream;
|
||||
|
||||
for (uint16 i = 0; i < numEntries * 3; i++)
|
||||
buffer[i] = ((unsigned int)buffer[i] * fadePercent) / 100;
|
||||
|
||||
_system->getPaletteManager()->setPalette(buffer, 0, numEntries);
|
||||
_needsUpdate = true;
|
||||
}
|
||||
|
||||
void ComposerEngine::setBackground(uint16 id) {
|
||||
for (Common::List<Sprite>::iterator i = _sprites.begin(); i != _sprites.end(); i++) {
|
||||
if (i->_id)
|
||||
continue;
|
||||
dirtySprite(*i);
|
||||
i->_surface.free();
|
||||
i->_id = id;
|
||||
if (!initSprite(*i))
|
||||
error("failed to set background %d", id);
|
||||
dirtySprite(*i);
|
||||
i->_id = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
Sprite *background = addSprite(id, 0, 0xffff, Common::Point());
|
||||
if (background)
|
||||
background->_id = 0;
|
||||
}
|
||||
|
||||
static void decompressSLWM(byte *buffer, Common::SeekableReadStream *stream) {
|
||||
uint bitsLeft = 0;
|
||||
uint16 lastBits = 0;
|
||||
byte currBit;
|
||||
while (true) {
|
||||
if (bitsLeft == 0) { bitsLeft = 16; lastBits = stream->readUint16LE(); }
|
||||
currBit = (lastBits & 1); lastBits >>= 1; bitsLeft--;
|
||||
|
||||
if (currBit) {
|
||||
// single byte
|
||||
*buffer++ = stream->readByte();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bitsLeft == 0) { bitsLeft = 16; lastBits = stream->readUint16LE(); }
|
||||
currBit = (lastBits & 1); lastBits >>= 1; bitsLeft--;
|
||||
|
||||
uint start;
|
||||
uint count;
|
||||
if (currBit) {
|
||||
uint orMask = stream->readByte();
|
||||
uint in = stream->readByte();
|
||||
count = in & 7;
|
||||
start = ((in & ~7) << 5) | orMask;
|
||||
if (!count) {
|
||||
count = stream->readByte();
|
||||
if (!count)
|
||||
break;
|
||||
count -= 2;
|
||||
}
|
||||
} else {
|
||||
// count encoded in the next two bits
|
||||
count = 0;
|
||||
|
||||
if (bitsLeft == 0) { bitsLeft = 16; lastBits = stream->readUint16LE(); }
|
||||
currBit = (lastBits & 1); lastBits >>= 1; bitsLeft--;
|
||||
|
||||
count = (count << 1) | currBit;
|
||||
|
||||
if (bitsLeft == 0) { bitsLeft = 16; lastBits = stream->readUint16LE(); }
|
||||
currBit = (lastBits & 1); lastBits >>= 1; bitsLeft--;
|
||||
|
||||
count = (count << 1) | currBit;
|
||||
|
||||
start = stream->readByte();
|
||||
}
|
||||
|
||||
count += 2;
|
||||
start++;
|
||||
for (uint i = 0; i < count; i++) {
|
||||
*buffer = *(buffer - start);
|
||||
buffer++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// bitmap compression types
|
||||
enum {
|
||||
kBitmapUncompressed = 0,
|
||||
kBitmapSpp32 = 1,
|
||||
kBitmapSLW8 = 3,
|
||||
kBitmapRLESLWM = 4,
|
||||
kBitmapSLWM = 5
|
||||
};
|
||||
|
||||
void ComposerEngine::decompressBitmap(uint16 type, Common::SeekableReadStream *stream, byte *buffer, uint32 size, uint width, uint height) {
|
||||
uint outSize = width * height;
|
||||
|
||||
switch (type) {
|
||||
case kBitmapUncompressed:
|
||||
if (stream->size() - (uint)stream->pos() != size)
|
||||
error("kBitmapUncompressed stream had %d bytes left, supposed to be %d",
|
||||
(int)(stream->size() - stream->pos()), size);
|
||||
if (size != outSize)
|
||||
error("kBitmapUncompressed size %d doesn't match required size %d",
|
||||
size, outSize);
|
||||
stream->read(buffer, size);
|
||||
break;
|
||||
case kBitmapSpp32:
|
||||
byte lookup[16];
|
||||
stream->read(lookup, 16);
|
||||
while (size--) {
|
||||
uint in = stream->readByte();
|
||||
byte lowBits = in & 0xF;
|
||||
byte highBits = (in & 0xF0) >> 4;
|
||||
if (highBits == 0xf) {
|
||||
// run of a single color
|
||||
uint count = (uint)stream->readByte() + 3;
|
||||
size--;
|
||||
if (outSize < count)
|
||||
error("kBitmapSpp32 only needed %d bytes, but got run of %d",
|
||||
outSize, count);
|
||||
outSize -= count;
|
||||
memset(buffer, lookup[lowBits], count);
|
||||
buffer += count;
|
||||
} else {
|
||||
// two pixels
|
||||
if (!outSize)
|
||||
error("kBitmapSpp32 had too many pixels");
|
||||
*buffer++ = lookup[highBits];
|
||||
outSize--;
|
||||
if (outSize) {
|
||||
*buffer++ = lookup[lowBits];
|
||||
outSize--;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kBitmapSLW8:
|
||||
while (size--) {
|
||||
byte val = stream->readByte();
|
||||
if (val != 0xff) {
|
||||
*buffer++ = val;
|
||||
continue;
|
||||
}
|
||||
uint count = stream->readByte();
|
||||
size--;
|
||||
|
||||
uint16 step;
|
||||
if (!(count & 0x80)) {
|
||||
step = stream->readByte();
|
||||
size--;
|
||||
} else {
|
||||
count = (count ^ 0x80);
|
||||
step = stream->readUint16LE();
|
||||
size -= 2;
|
||||
}
|
||||
count += 4;
|
||||
// this is often overlapping (for repeating patterns)
|
||||
for (uint i = 0; i < count; i++) {
|
||||
*buffer = *(buffer - step - 1);
|
||||
buffer++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kBitmapRLESLWM:
|
||||
{
|
||||
uint32 bufSize = stream->readUint32LE();
|
||||
byte *tempBuf = new byte[bufSize];
|
||||
decompressSLWM(tempBuf, stream);
|
||||
|
||||
uint instrPos = tempBuf[0] + 1;
|
||||
instrPos += READ_LE_UINT16(tempBuf + instrPos) + 2;
|
||||
byte *instr = tempBuf + instrPos;
|
||||
|
||||
uint line = 0;
|
||||
while (line++ < height) {
|
||||
uint pixels = 0;
|
||||
|
||||
while (pixels < width) {
|
||||
byte data = *instr++;
|
||||
byte color = tempBuf[(data & 0x7F) + 1];
|
||||
if (!(data & 0x80)) {
|
||||
*buffer++ = color;
|
||||
pixels++;
|
||||
} else {
|
||||
byte count = *instr++;
|
||||
if (!count) {
|
||||
while (pixels++ < width)
|
||||
*buffer++ = color;
|
||||
break;
|
||||
}
|
||||
for (uint i = 0; i < count; i++) {
|
||||
*buffer++ = color;
|
||||
pixels++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete[] tempBuf;
|
||||
}
|
||||
break;
|
||||
case kBitmapSLWM:
|
||||
decompressSLWM(buffer, stream);
|
||||
break;
|
||||
default:
|
||||
error("decompressBitmap can't handle type %d", type);
|
||||
}
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *ComposerEngine::getStreamForSprite(uint16 id) {
|
||||
for (Common::List<Pipe *>::iterator k = _pipes.begin(); k != _pipes.end(); k++) {
|
||||
Pipe *pipe = *k;
|
||||
if (!pipe->hasResource(ID_BMAP, id))
|
||||
continue;
|
||||
return pipe->getResource(ID_BMAP, id, true);
|
||||
}
|
||||
if (hasResource(ID_BMAP, id))
|
||||
return getResource(ID_BMAP, id);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ComposerEngine::initSprite(Sprite &sprite) {
|
||||
Common::SeekableReadStream *stream = getStreamForSprite(sprite._id);
|
||||
if (!stream)
|
||||
return false;
|
||||
|
||||
uint16 type = stream->readUint16LE();
|
||||
int16 height = stream->readSint16LE();
|
||||
int16 width = stream->readSint16LE();
|
||||
uint32 size = stream->readUint32LE();
|
||||
debug(1, "loading BMAP: type %d, width %d, height %d, size %d", type, width, height, size);
|
||||
|
||||
if (width > 0 && height > 0) {
|
||||
sprite._surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
decompressBitmap(type, stream, (byte *)sprite._surface.getPixels(), size, width, height);
|
||||
// sprite is BMP-style (bottom-up), so flip it
|
||||
sprite._surface.flipVertical(Common::Rect(width, height));
|
||||
} else {
|
||||
// there are some sprites (e.g. a -998x-998 one in Gregory's title screen)
|
||||
// which have an invalid size, but the original engine doesn't notice for
|
||||
// RLE sprites since the width/height is ignored until the actual draw
|
||||
if (type != kBitmapRLESLWM)
|
||||
error("sprite (type %d) had invalid size %dx%d", type, width, height);
|
||||
delete stream;
|
||||
return false;
|
||||
}
|
||||
delete stream;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ComposerEngine::drawSprite(const Sprite &sprite) {
|
||||
Common::Rect srcRect(sprite._surface.w, sprite._surface.h);
|
||||
Common::Rect dstRect(
|
||||
sprite._pos.x,
|
||||
sprite._pos.y,
|
||||
sprite._pos.x + sprite._surface.w,
|
||||
sprite._pos.y + sprite._surface.h);
|
||||
|
||||
if (!_screen.clip(srcRect, dstRect))
|
||||
return;
|
||||
|
||||
_screen.copyRectToSurfaceWithKey(sprite._surface, dstRect.left, dstRect.top, srcRect, 0x00);
|
||||
}
|
||||
|
||||
} // End of namespace Composer
|
||||
71
engines/composer/graphics.h
Normal file
71
engines/composer/graphics.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef COMPOSER_GRAPHICS_H
|
||||
#define COMPOSER_GRAPHICS_H
|
||||
|
||||
#include "common/rect.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Composer {
|
||||
|
||||
class ComposerEngine;
|
||||
|
||||
struct Sprite {
|
||||
uint16 _id;
|
||||
uint16 _animId;
|
||||
uint16 _zorder;
|
||||
Common::Point _pos;
|
||||
Graphics::Surface _surface;
|
||||
|
||||
bool contains(const Common::Point &pos) const;
|
||||
};
|
||||
|
||||
struct AnimationEntry {
|
||||
uint32 state;
|
||||
uint16 op;
|
||||
uint16 priority;
|
||||
uint16 counter;
|
||||
uint16 prevValue;
|
||||
};
|
||||
|
||||
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;
|
||||
uint32 _size;
|
||||
|
||||
Common::Array<AnimationEntry> _entries;
|
||||
|
||||
uint32 _offset;
|
||||
Common::SeekableReadStream *_stream;
|
||||
};
|
||||
|
||||
} // End of namespace Composer
|
||||
|
||||
#endif
|
||||
184
engines/composer/metaengine.cpp
Normal file
184
engines/composer/metaengine.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "base/plugins.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/serializer.h"
|
||||
#include "common/str-array.h"
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
#include "common/translation.h"
|
||||
|
||||
#include "backends/keymapper/action.h"
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
#include "backends/keymapper/standard-actions.h"
|
||||
|
||||
#include "composer/composer.h"
|
||||
#include "composer/detection.h"
|
||||
|
||||
namespace Composer {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Common::Platform ComposerEngine::getPlatform() const {
|
||||
return _gameDescription->desc.platform;
|
||||
}
|
||||
|
||||
bool ComposerEngine::loadDetectedConfigFile(Common::INIFile &configFile) const {
|
||||
const ADGameFileDescription *res = _gameDescription->desc.filesDescriptions;
|
||||
while (res->fileName != nullptr) {
|
||||
if (res->fileType == GAME_CONFIGFILE) {
|
||||
return configFile.loadFromFileOrDataFork(res->fileName);
|
||||
}
|
||||
res++;
|
||||
}
|
||||
// default config file name
|
||||
return configFile.loadFromFileOrDataFork("book.ini") || configFile.loadFromFileOrDataFork("book.mac");
|
||||
}
|
||||
|
||||
} // End of namespace Composer
|
||||
|
||||
class ComposerMetaEngine : public AdvancedMetaEngine<Composer::ComposerGameDescription> {
|
||||
public:
|
||||
const char *getName() const override {
|
||||
return "composer";
|
||||
}
|
||||
|
||||
Common::Error createInstance(OSystem *syst, Engine **engine, const Composer::ComposerGameDescription *desc) const override;
|
||||
bool hasFeature(MetaEngineFeature f) const override;
|
||||
|
||||
Common::KeymapArray initKeymaps(const char *target) const override;
|
||||
|
||||
int getMaximumSaveSlot() const override;
|
||||
SaveStateList listSaves(const char* target) const override;
|
||||
Common::String getSavegameFile(int saveGameIdx, const char *target) const override {
|
||||
if (!target)
|
||||
target = getName();
|
||||
if (saveGameIdx == kSavegameFilePattern)
|
||||
return Common::String::format("%s.##", target);
|
||||
else
|
||||
return Common::String::format("%s.%02d", target, saveGameIdx);
|
||||
}
|
||||
};
|
||||
|
||||
Common::Error ComposerMetaEngine::createInstance(OSystem *syst, Engine **engine, const Composer::ComposerGameDescription *desc) const {
|
||||
*engine = new Composer::ComposerEngine(syst,desc);
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
bool ComposerMetaEngine::hasFeature(MetaEngineFeature f) const {
|
||||
// FIXME: Disabled loading from launcher currently due to issues with loading Darby The Dragon (and probably others) savegames. See bug #16432
|
||||
return ((f == kSupportsListSaves) /*|| (f == kSupportsLoadingDuringStartup)*/);
|
||||
}
|
||||
|
||||
Common::KeymapArray ComposerMetaEngine::initKeymaps(const char *target) const {
|
||||
using namespace Common;
|
||||
using namespace Composer;
|
||||
|
||||
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "composer-engine", "Composer engine");
|
||||
|
||||
Action *act;
|
||||
|
||||
act = new Action(kStandardActionLeftClick, _("Left click"));
|
||||
act->setLeftClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_LEFT");
|
||||
act->addDefaultInputMapping("JOY_A");
|
||||
engineKeyMap->addAction(act);
|
||||
|
||||
act = new Action(kStandardActionRightClick, _("Right click"));
|
||||
act->setRightClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_RIGHT");
|
||||
act->addDefaultInputMapping("JOY_B");
|
||||
engineKeyMap->addAction(act);
|
||||
|
||||
act = new Action(kStandardActionSkip, _("Skip"));
|
||||
act->setKeyEvent(KeyState(KEYCODE_ESCAPE, ASCII_ESCAPE));
|
||||
act->addDefaultInputMapping("ESCAPE");
|
||||
act->addDefaultInputMapping("JOY_Y");
|
||||
act->allowKbdRepeats();
|
||||
engineKeyMap->addAction(act);
|
||||
|
||||
return Keymap::arrayOf(engineKeyMap);
|
||||
}
|
||||
|
||||
Common::String getSaveName(Common::InSaveFile *in) {
|
||||
Common::Serializer ser(in, nullptr);
|
||||
Common::String name;
|
||||
uint32 tmp;
|
||||
ser.syncAsUint32LE(tmp);
|
||||
ser.syncAsUint32LE(tmp);
|
||||
ser.syncString(name);
|
||||
return name;
|
||||
}
|
||||
int ComposerMetaEngine::getMaximumSaveSlot() const {
|
||||
return 99;
|
||||
}
|
||||
SaveStateList ComposerMetaEngine::listSaves(const char *target) const {
|
||||
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
|
||||
Common::StringArray filenames;
|
||||
Common::String saveDesc;
|
||||
|
||||
filenames = saveFileMan->listSavefiles(getSavegameFilePattern(target));
|
||||
|
||||
SaveStateList saveList;
|
||||
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
|
||||
// Obtain the last 3 digits of the filename, since they correspond to the save slot
|
||||
int slotNum = atoi(file->c_str() + file->size() - 2);
|
||||
|
||||
if (slotNum >= 0 && slotNum <= 99) {
|
||||
Common::InSaveFile *in = saveFileMan->openForLoading(*file);
|
||||
if (in) {
|
||||
saveDesc = getSaveName(in);
|
||||
saveList.push_back(SaveStateDescriptor(this, slotNum, saveDesc));
|
||||
delete in;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
|
||||
return saveList;
|
||||
}
|
||||
|
||||
bool Composer::ComposerEngine::hasFeature(EngineFeature f) const {
|
||||
return (f == kSupportsReturnToLauncher
|
||||
|| f == kSupportsSavingDuringRuntime
|
||||
|| f == kSupportsLoadingDuringRuntime);
|
||||
}
|
||||
|
||||
#if PLUGIN_ENABLED_DYNAMIC(COMPOSER)
|
||||
REGISTER_PLUGIN_DYNAMIC(COMPOSER, PLUGIN_TYPE_ENGINE, ComposerMetaEngine);
|
||||
#else
|
||||
REGISTER_PLUGIN_STATIC(COMPOSER, PLUGIN_TYPE_ENGINE, ComposerMetaEngine);
|
||||
#endif
|
||||
21
engines/composer/module.mk
Normal file
21
engines/composer/module.mk
Normal file
@@ -0,0 +1,21 @@
|
||||
MODULE := engines/composer
|
||||
|
||||
MODULE_OBJS = \
|
||||
console.o \
|
||||
composer.o \
|
||||
graphics.o \
|
||||
metaengine.o \
|
||||
resource.o \
|
||||
saveload.o \
|
||||
scripting.o
|
||||
|
||||
# This module can be built as a plugin
|
||||
ifeq ($(ENABLE_COMPOSER), DYNAMIC_PLUGIN)
|
||||
PLUGIN := 1
|
||||
endif
|
||||
|
||||
# Include common rules
|
||||
include $(srcdir)/rules.mk
|
||||
|
||||
# Detection objects
|
||||
DETECT_OBJS += $(MODULE)/detection.o
|
||||
416
engines/composer/resource.cpp
Normal file
416
engines/composer/resource.cpp
Normal file
@@ -0,0 +1,416 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "composer/resource.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/substream.h"
|
||||
#include "common/util.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/macresman.h"
|
||||
|
||||
namespace Composer {
|
||||
|
||||
// Base Archive code
|
||||
// (copied from clone2727's mohawk code)
|
||||
|
||||
Archive::Archive() {
|
||||
_stream = nullptr;
|
||||
}
|
||||
|
||||
Archive::~Archive() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool Archive::openFile(const Common::Path &fileName) {
|
||||
Common::SeekableReadStream *file
|
||||
= Common::MacResManager::openFileOrDataFork(fileName);
|
||||
|
||||
if (!file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!openStream(file)) {
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Archive::close() {
|
||||
_types.clear();
|
||||
delete _stream; _stream = nullptr;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Pipe::Pipe(Common::SeekableReadStream *stream, uint16 id) {
|
||||
_offset = 0;
|
||||
_stream = stream;
|
||||
_anim = nullptr;
|
||||
_pipeId = id;
|
||||
}
|
||||
|
||||
Pipe::~Pipe() {
|
||||
}
|
||||
|
||||
void Pipe::nextFrame() {
|
||||
if (_offset == (uint)_stream->size())
|
||||
return;
|
||||
|
||||
_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);
|
||||
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 (res.entries.size() == 1) {
|
||||
Common::SeekableReadStream *stream = new Common::SeekableSubReadStream(_stream,
|
||||
res.entries[0].offset, res.entries[0].offset + res.entries[0].size);
|
||||
if (buffering) {
|
||||
_types[tag].erase(id);
|
||||
bool found = false;
|
||||
for (Common::List<uint16>::const_iterator i = _bufferedResources[tag].begin(); !found && (i != _bufferedResources[tag].end()); i++)
|
||||
if ((*i) == id) found = true;
|
||||
if (!found)
|
||||
_bufferedResources[tag].push_back(id);
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
// If there are multiple entries in the pipe, we have to concaternate them together.
|
||||
|
||||
uint32 size = 0;
|
||||
for (uint i = 0; i < res.entries.size(); i++)
|
||||
size += res.entries[i].size;
|
||||
|
||||
byte *buffer = (byte *)malloc(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;
|
||||
}
|
||||
if (buffering) {
|
||||
_types[tag].erase(id);
|
||||
bool found = false;
|
||||
for (Common::List<uint16>::const_iterator i = _bufferedResources[tag].begin(); !found && (i != _bufferedResources[tag].end()); i++)
|
||||
if ((*i) == id) found = true;
|
||||
if (!found)
|
||||
_bufferedResources[tag].push_back(id);
|
||||
}
|
||||
return new Common::MemoryReadStream(buffer, size, DisposeAfterUse::YES);
|
||||
}
|
||||
|
||||
OldPipe::OldPipe(Common::SeekableReadStream *stream, uint16 pipeId) : Pipe(stream, pipeId), _currFrame(0) {
|
||||
uint32 tag = _stream->readUint32BE();
|
||||
if (tag != ID_PIPE)
|
||||
error("invalid tag for pipe (%08x)", tag);
|
||||
|
||||
_numFrames = _stream->readUint32LE();
|
||||
uint16 scriptCount = _stream->readUint16LE();
|
||||
_scripts.reserve(scriptCount);
|
||||
for (uint i = 0; i < scriptCount; i++)
|
||||
_scripts.push_back(_stream->readUint16LE());
|
||||
|
||||
_offset = _stream->pos();
|
||||
}
|
||||
|
||||
void OldPipe::nextFrame() {
|
||||
if (_currFrame >= _numFrames)
|
||||
return;
|
||||
|
||||
_stream->seek(_offset, SEEK_SET);
|
||||
|
||||
uint32 tag = _stream->readUint32BE();
|
||||
if (tag != ID_FRME)
|
||||
error("invalid tag for pipe (%08x)", tag);
|
||||
|
||||
uint16 spriteCount = _stream->readUint16LE();
|
||||
uint32 spriteSize = _stream->readUint32LE();
|
||||
uint32 audioSize = _stream->readUint32LE();
|
||||
|
||||
Common::Array<uint16> spriteIds;
|
||||
Common::Array<PipeResourceEntry> spriteEntries;
|
||||
for (uint i = 0; i < spriteCount; i++) {
|
||||
spriteIds.push_back(_stream->readUint16LE());
|
||||
PipeResourceEntry entry;
|
||||
entry.size = _stream->readUint32LE();
|
||||
entry.offset = _stream->readUint32LE();
|
||||
spriteEntries.push_back(entry);
|
||||
}
|
||||
|
||||
uint32 spriteDataOffset = _stream->pos();
|
||||
_stream->skip(spriteSize);
|
||||
|
||||
ResourceMap &spriteResMap = _types[ID_BMAP];
|
||||
spriteResMap.clear();
|
||||
for (uint i = 0; i < spriteIds.size(); i++) {
|
||||
PipeResourceEntry &entry = spriteEntries[i];
|
||||
entry.offset += spriteDataOffset;
|
||||
spriteResMap[spriteIds[i]].entries.push_back(entry);
|
||||
}
|
||||
|
||||
ResourceMap &audioResMap = _types[ID_WAVE];
|
||||
audioResMap.clear();
|
||||
|
||||
if (audioSize > 0) {
|
||||
PipeResourceEntry entry;
|
||||
entry.size = audioSize;
|
||||
entry.offset = _stream->pos();
|
||||
// we use 0xffff for per-frame pipe audio
|
||||
audioResMap[0xffff].entries.push_back(entry);
|
||||
_stream->skip(audioSize);
|
||||
}
|
||||
|
||||
_offset = _stream->pos();
|
||||
_currFrame++;
|
||||
}
|
||||
|
||||
} // End of namespace Composer
|
||||
151
engines/composer/resource.h
Normal file
151
engines/composer/resource.h
Normal file
@@ -0,0 +1,151 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#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 {
|
||||
|
||||
struct Animation;
|
||||
|
||||
#define ID_LBRC MKTAG('L','B','R','C') // Main FourCC
|
||||
|
||||
#define ID_ACEL MKTAG('A','C','E','L') // Keyboard Accelerator (v1)
|
||||
#define ID_AMBI MKTAG('A','M','B','I') // Ambient (v1 sprite button)
|
||||
#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_RAND MKTAG('R','A','N','D') // Random Object
|
||||
#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
|
||||
|
||||
#define ID_FRME MKTAG('F','R','M','E') // Frame
|
||||
|
||||
class Archive {
|
||||
public:
|
||||
Archive();
|
||||
virtual ~Archive();
|
||||
|
||||
bool openFile(const Common::Path &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() override {}
|
||||
|
||||
bool openStream(Common::SeekableReadStream *stream) override;
|
||||
};
|
||||
|
||||
struct PipeResourceEntry {
|
||||
uint32 size;
|
||||
uint32 offset;
|
||||
};
|
||||
|
||||
struct PipeResource {
|
||||
Common::Array<PipeResourceEntry> entries;
|
||||
};
|
||||
|
||||
class Pipe {
|
||||
public:
|
||||
Pipe(Common::SeekableReadStream *stream, uint16 id);
|
||||
virtual ~Pipe();
|
||||
virtual void nextFrame();
|
||||
|
||||
Animation *_anim;
|
||||
|
||||
bool hasResource(uint32 tag, uint16 id) const;
|
||||
Common::SeekableReadStream *getResource(uint32 tag, uint16 id, bool buffering);
|
||||
|
||||
virtual const Common::Array<uint16> *getScripts() { return NULL; }
|
||||
uint16 getPipeId() const { return _pipeId; }
|
||||
virtual uint32 getOffset() const { return _offset; }
|
||||
virtual void setOffset(uint32 offset) { while (_offset < offset) nextFrame(); }
|
||||
typedef Common::HashMap<uint32, Common::List<uint16> > DelMap;
|
||||
DelMap _bufferedResources;
|
||||
|
||||
protected:
|
||||
Common::SeekableReadStream *_stream;
|
||||
|
||||
typedef Common::HashMap<uint16, PipeResource> ResourceMap;
|
||||
typedef Common::HashMap<uint32, ResourceMap> TypeMap;
|
||||
TypeMap _types;
|
||||
uint16 _pipeId;
|
||||
|
||||
uint32 _offset;
|
||||
};
|
||||
|
||||
class OldPipe : public Pipe {
|
||||
public:
|
||||
OldPipe(Common::SeekableReadStream *stream, uint16 pipeId);
|
||||
void nextFrame() override;
|
||||
|
||||
const Common::Array<uint16> *getScripts() override { return &_scripts; }
|
||||
uint32 getOffset() const override { return _currFrame; }
|
||||
void setOffset(uint32 offset) override { while (_currFrame < offset) nextFrame(); }
|
||||
|
||||
protected:
|
||||
uint32 _currFrame, _numFrames;
|
||||
Common::Array<uint16> _scripts;
|
||||
};
|
||||
|
||||
} // End of namespace Composer
|
||||
|
||||
#endif
|
||||
425
engines/composer/saveload.cpp
Normal file
425
engines/composer/saveload.cpp
Normal file
@@ -0,0 +1,425 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/serializer.h"
|
||||
#include "common/system.h"
|
||||
#include "common/compression/deflate.h"
|
||||
#include "graphics/paletteman.h"
|
||||
|
||||
#include "composer/composer.h"
|
||||
#include "composer/graphics.h"
|
||||
|
||||
namespace Composer {
|
||||
|
||||
template<class T>
|
||||
void ComposerEngine::syncArray(Common::Serializer &ser, Common::Array<T> &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
|
||||
if (ser.isSaving()) {
|
||||
uint32 size = data.size();
|
||||
ser.syncAsUint32LE(size, minVersion, maxVersion);
|
||||
for (typename Common::Array<T>::iterator i = data.begin(); i != data.end(); i++) {
|
||||
sync<T>(ser, *i, minVersion, maxVersion);
|
||||
}
|
||||
} else {
|
||||
uint32 size = 0;
|
||||
data.clear();
|
||||
ser.syncAsUint32LE(size, minVersion, maxVersion);
|
||||
for (uint32 i = 0; i < size; i++) {
|
||||
T item;
|
||||
sync<T>(ser, item, minVersion, maxVersion);
|
||||
data.push_back(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
template<class T>
|
||||
void ComposerEngine::syncList(Common::Serializer &ser, Common::List<T> &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
|
||||
if (ser.isSaving()) {
|
||||
uint32 size = data.size();
|
||||
ser.syncAsUint32LE(size, minVersion, maxVersion);
|
||||
for (typename Common::List<T>::iterator i = data.begin(); i != data.end(); i++) {
|
||||
sync<T>(ser, *i, minVersion, maxVersion);
|
||||
}
|
||||
} else {
|
||||
uint32 size = 0;
|
||||
data.clear();
|
||||
ser.syncAsUint32LE(size, minVersion, maxVersion);
|
||||
for (uint32 i = 0; i < size; i++) {
|
||||
T item;
|
||||
sync<T>(ser, item, minVersion, maxVersion);
|
||||
data.push_back(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
template<class T>
|
||||
void ComposerEngine::syncListReverse(Common::Serializer &ser, Common::List<T> &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
|
||||
if (ser.isSaving()) {
|
||||
uint32 size = data.size();
|
||||
ser.syncAsUint32LE(size, minVersion, maxVersion);
|
||||
for (typename Common::List<T>::iterator i = data.reverse_begin(); i != data.end(); i--) {
|
||||
sync<T>(ser, *i, minVersion, maxVersion);
|
||||
}
|
||||
} else {
|
||||
uint32 size = 0;
|
||||
data.clear();
|
||||
ser.syncAsUint32LE(size, minVersion, maxVersion);
|
||||
for (uint32 i = 0; i < size; i++) {
|
||||
T item;
|
||||
sync<T>(ser, item, minVersion, maxVersion);
|
||||
data.push_front(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
template<>
|
||||
void ComposerEngine::sync<uint16>(Common::Serializer &ser, uint16 &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
|
||||
ser.syncAsUint16LE(data, minVersion, maxVersion);
|
||||
}
|
||||
template<>
|
||||
void ComposerEngine::sync<uint32>(Common::Serializer &ser, uint32 &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
|
||||
ser.syncAsUint32LE(data, minVersion, maxVersion);
|
||||
}
|
||||
template<>
|
||||
void ComposerEngine::sync<Library>(Common::Serializer &ser, Library &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
|
||||
if (ser.isSaving()) {
|
||||
ser.syncAsUint16LE(data._id, minVersion, maxVersion);
|
||||
ser.syncString(data._group, minVersion, maxVersion);
|
||||
} else {
|
||||
uint16 id = 0;
|
||||
ser.syncAsUint16LE(id, minVersion, maxVersion);
|
||||
ser.syncString(_bookGroup, minVersion, maxVersion);
|
||||
loadLibrary(id);
|
||||
}
|
||||
}
|
||||
template<>
|
||||
void ComposerEngine::syncListReverse<Library>(Common::Serializer &ser, Common::List<Library> &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
|
||||
if (ser.isSaving()) {
|
||||
uint32 size = data.size();
|
||||
ser.syncAsUint32LE(size, minVersion, maxVersion);
|
||||
for (Common::List<Library>::iterator i = data.reverse_begin(); i != data.end(); i--) {
|
||||
sync<Library>(ser, *i, minVersion, maxVersion);
|
||||
}
|
||||
} else {
|
||||
uint32 size = 0;
|
||||
ser.syncAsUint32LE(size, minVersion, maxVersion);
|
||||
for (uint32 i = 0; i < size; i++) {
|
||||
Library item;
|
||||
sync<Library>(ser, item, minVersion, maxVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
template<>
|
||||
void ComposerEngine::sync<PendingPageChange>(Common::Serializer &ser, PendingPageChange &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
|
||||
ser.syncAsUint16LE(data._pageId, minVersion, maxVersion);
|
||||
ser.syncAsByte(data._remove, minVersion, maxVersion);
|
||||
}
|
||||
template<>
|
||||
void ComposerEngine::sync<OldScript *>(Common::Serializer &ser, OldScript *&data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
|
||||
uint16 id = 0;
|
||||
uint32 pos = 0, delay = 0;
|
||||
if (ser.isSaving()) {
|
||||
pos = data->_stream->pos();
|
||||
id = data->_id;
|
||||
delay = data->_currDelay;
|
||||
}
|
||||
ser.syncAsUint32LE(pos);
|
||||
ser.syncAsUint16LE(id);
|
||||
ser.syncAsUint32LE(delay);
|
||||
if (ser.isLoading()) {
|
||||
data = new OldScript(id, getResource(ID_SCRP, id));
|
||||
data->_currDelay = delay;
|
||||
data->_stream->seek(pos, SEEK_SET);
|
||||
}
|
||||
}
|
||||
template<>
|
||||
void ComposerEngine::sync<QueuedScript>(Common::Serializer &ser, QueuedScript &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
|
||||
ser.syncAsUint32LE(data._baseTime);
|
||||
ser.syncAsUint32LE(data._duration);
|
||||
ser.syncAsUint32LE(data._count);
|
||||
ser.syncAsUint16LE(data._scriptId);
|
||||
if (ser.isLoading()) data._baseTime += _timeDelta;
|
||||
}
|
||||
template<>
|
||||
void ComposerEngine::sync<Pipe *>(Common::Serializer &ser, Pipe *&data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
|
||||
uint16 id = 0;
|
||||
uint32 offset = 0, tmp = 0;
|
||||
if (ser.isSaving()) {
|
||||
id = data->getPipeId();
|
||||
offset = data->getOffset();
|
||||
tmp = data->_bufferedResources.size();
|
||||
}
|
||||
ser.syncAsUint16LE(id);
|
||||
ser.syncAsUint32LE(offset);
|
||||
|
||||
if (ser.isLoading()) {
|
||||
// On load, get and initialize streams
|
||||
Common::SeekableReadStream *stream;
|
||||
if (getGameType() == GType_ComposerV1) {
|
||||
stream = getResource(ID_PIPE, id);
|
||||
data = new OldPipe(stream, id);
|
||||
} else {
|
||||
stream = getResource(ID_ANIM, id);
|
||||
data = new Pipe(stream, id);
|
||||
}
|
||||
_pipeStreams.push_back(stream);
|
||||
data->setOffset(offset);
|
||||
ser.syncAsUint32LE(tmp);
|
||||
for (uint32 j = tmp; j > 0; j--) {
|
||||
uint32 tag = 0;
|
||||
ser.syncAsUint32LE(tag);
|
||||
ser.syncAsUint32LE(tmp);
|
||||
for (uint32 k = tmp; k > 0; k--) {
|
||||
ser.syncAsUint16LE(id);
|
||||
if (data->hasResource(tag, id)) {
|
||||
Common::SeekableReadStream *s = data->getResource(tag, id, true);
|
||||
delete s;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ser.syncAsUint32LE(tmp);
|
||||
for (Pipe::DelMap::iterator i = data->_bufferedResources.begin(); i != data->_bufferedResources.end(); i++) {
|
||||
uint32 key = (*i)._key;
|
||||
ser.syncAsUint32LE(key);
|
||||
syncList<uint16>(ser, (*i)._value, minVersion, maxVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
template<>
|
||||
void ComposerEngine::sync<AnimationEntry>(Common::Serializer &ser, AnimationEntry &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
|
||||
ser.syncAsUint32LE(data.state);
|
||||
ser.syncAsUint16LE(data.counter);
|
||||
ser.syncAsUint16LE(data.prevValue);
|
||||
}
|
||||
template<>
|
||||
void ComposerEngine::sync<Animation *>(Common::Serializer &ser, Animation *&data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
|
||||
uint16 animId = 0, x = 0, y = 0;
|
||||
uint32 offset = 0, state = 0, param = 0;
|
||||
int32 size = 0;
|
||||
if (ser.isSaving()) {
|
||||
animId = data->_id;
|
||||
offset = data->_offset;
|
||||
x = data->_basePos.x;
|
||||
y = data->_basePos.x;
|
||||
state = data->_state;
|
||||
param = data->_eventParam;
|
||||
size = data->_size;
|
||||
}
|
||||
ser.syncAsUint16LE(animId);
|
||||
ser.syncAsUint32LE(offset);
|
||||
ser.syncAsUint16LE(x);
|
||||
ser.syncAsUint16LE(y);
|
||||
ser.syncAsUint32LE(state);
|
||||
ser.syncAsUint32LE(param);
|
||||
ser.syncAsUint32LE(size);
|
||||
if (ser.isLoading()) {
|
||||
// On load, get and initialize streams
|
||||
loadAnimation(data, animId, x, y, param, size);
|
||||
data->_offset = offset;
|
||||
data->_state = state;
|
||||
uint32 tmp = 0;
|
||||
ser.syncAsUint32LE(tmp);
|
||||
for (uint32 i = 0; i < tmp; i++) {
|
||||
sync<AnimationEntry>(ser, data->_entries[i], minVersion, maxVersion);
|
||||
}
|
||||
} else {
|
||||
syncArray<AnimationEntry>(ser, data->_entries, minVersion, maxVersion);
|
||||
}
|
||||
}
|
||||
template<>
|
||||
void ComposerEngine::sync<Sprite>(Common::Serializer &ser, Sprite &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
|
||||
ser.syncAsUint16LE(data._id);
|
||||
ser.syncAsUint16LE(data._animId);
|
||||
ser.syncAsSint16LE(data._pos.x);
|
||||
ser.syncAsSint16LE(data._pos.y);
|
||||
ser.syncAsUint16LE(data._surface.w);
|
||||
ser.syncAsUint16LE(data._surface.h);
|
||||
ser.syncAsUint16LE(data._surface.pitch);
|
||||
ser.syncAsUint16LE(data._zorder);
|
||||
if (ser.isLoading())
|
||||
data._surface.setPixels(malloc(data._surface.h * data._surface.pitch));
|
||||
byte *pix = static_cast<byte *>(data._surface.getPixels());
|
||||
for (uint16 y = 0; y < data._surface.h; y++) {
|
||||
for (uint16 x = 0; x < data._surface.w; x++) {
|
||||
ser.syncAsByte(pix[x]);
|
||||
}
|
||||
pix += data._surface.pitch;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Common::String ComposerEngine::getSaveStateName(int slot) const {
|
||||
return Common::String::format("%s.%02d", _targetName.c_str(), slot);
|
||||
}
|
||||
|
||||
Common::Error ComposerEngine::loadGameState(int slot) {
|
||||
Common::String filename = getSaveStateName(slot);
|
||||
Common::InSaveFile *in;
|
||||
if (!(in = _saveFileMan->openForLoading(filename)))
|
||||
return Common::kPathNotFile;
|
||||
|
||||
Common::Serializer ser(in, nullptr);
|
||||
byte magic[4];
|
||||
ser.syncBytes(magic, 4);
|
||||
if (magic[0] != 'C' || magic[1] != 'M' || magic[2] != 'P' || magic[3] != 'S')
|
||||
return Common::kUnknownError;
|
||||
|
||||
ser.syncVersion(0);
|
||||
Common::String desc;
|
||||
ser.syncString(desc);
|
||||
uint32 tmp;
|
||||
ser.syncAsUint32LE(tmp);
|
||||
_rnd->setSeed(tmp);
|
||||
ser.syncAsUint32LE(_currentTime);
|
||||
_timeDelta = _system->getMillis() - _currentTime;
|
||||
_currentTime += _timeDelta;
|
||||
ser.syncAsUint32LE(_lastTime);
|
||||
_lastTime += _timeDelta;
|
||||
|
||||
// Unload all Libraries
|
||||
Common::Array<uint16> libIds;
|
||||
for (Common::List<Library>::iterator i = _libraries.begin(); i != _libraries.end(); i++)
|
||||
libIds.push_back((*i)._id);
|
||||
for (uint32 i = 0; i < libIds.size(); i++)
|
||||
unloadLibrary(libIds[i]);
|
||||
|
||||
syncListReverse<Library>(ser, _libraries);
|
||||
ser.syncString(_bookGroup);
|
||||
|
||||
syncArray<PendingPageChange>(ser, _pendingPageChanges);
|
||||
syncArray<uint16>(ser, _stack);
|
||||
syncArray<uint16>(ser, _vars);
|
||||
|
||||
// Free outdated pointers
|
||||
for (Common::List<OldScript *>::iterator i = _oldScripts.begin(); i != _oldScripts.end(); i++) {
|
||||
delete *i;
|
||||
}
|
||||
|
||||
syncList<OldScript *>(ser, _oldScripts);
|
||||
syncArray<QueuedScript>(ser, _queuedScripts);
|
||||
|
||||
ser.syncAsSint16LE(_lastMousePos.x);
|
||||
ser.syncAsSint16LE(_lastMousePos.y);
|
||||
g_system->warpMouse(_lastMousePos.x, _lastMousePos.y);
|
||||
ser.syncAsByte(_mouseEnabled);
|
||||
ser.syncAsByte(_mouseVisible);
|
||||
ser.syncAsUint16LE(_mouseSpriteId);
|
||||
|
||||
// Free outdated pointers
|
||||
for (Common::List<Pipe *>::iterator i = _pipes.begin(); i != _pipes.end(); i++) {
|
||||
delete *i;
|
||||
}
|
||||
for (Common::Array<Common::SeekableReadStream *>::iterator i = _pipeStreams.begin(); i != _pipeStreams.end(); i++) {
|
||||
delete *i;
|
||||
}
|
||||
|
||||
_pipeStreams.clear();
|
||||
syncListReverse<Pipe *>(ser, _pipes);
|
||||
|
||||
// Free outdated pointers
|
||||
for (Common::List<Animation *>::iterator i = _anims.begin(); i != _anims.end(); i++) {
|
||||
delete *i;
|
||||
}
|
||||
|
||||
syncList<Animation *>(ser, _anims);
|
||||
syncList<Sprite>(ser, _sprites);
|
||||
|
||||
_dirtyRects.clear();
|
||||
|
||||
// Redraw the whole screen
|
||||
_dirtyRects.push_back(Common::Rect(0, 0, 640, 480));
|
||||
byte palbuf[256 * 3];
|
||||
ser.syncBytes(palbuf, 256 * 3);
|
||||
_system->getPaletteManager()->setPalette(palbuf, 0, 256);
|
||||
_needsUpdate = true;
|
||||
|
||||
_mixer->stopAll();
|
||||
_audioStream = nullptr;
|
||||
|
||||
// Restore the buffered audio
|
||||
ser.syncAsSint16LE(_currSoundPriority);
|
||||
int32 numSamples = 0;
|
||||
ser.syncAsSint32LE(numSamples);
|
||||
int16 *audioBuffer = (int16 *)malloc(numSamples * 2);
|
||||
for (int32 i = 0; i < numSamples; i++)
|
||||
ser.syncAsSint16LE(audioBuffer[i]);
|
||||
_audioStream = Audio::makeQueuingAudioStream(22050, false);
|
||||
_audioStream->queueBuffer((byte *)audioBuffer, numSamples * 2, DisposeAfterUse::YES, Audio::FLAG_16BITS);
|
||||
if (!_mixer->isSoundHandleActive(_soundHandle))
|
||||
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, _audioStream);
|
||||
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
Common::Error ComposerEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
|
||||
Common::String filename = getSaveStateName(slot);
|
||||
Common::OutSaveFile *out;
|
||||
if (!(out = _saveFileMan->openForSaving(filename)))
|
||||
return Common::kWritingFailed;
|
||||
|
||||
Common::Serializer ser(nullptr, out);
|
||||
byte magic[4] = {'C', 'M', 'P', 'S'};
|
||||
ser.syncBytes(magic, 4);
|
||||
ser.syncVersion(0);
|
||||
Common::String desctmp = desc;
|
||||
ser.syncString(desctmp);
|
||||
uint32 tmp = _rnd->getSeed();
|
||||
ser.syncAsUint32LE(tmp);
|
||||
ser.syncAsUint32LE(_currentTime);
|
||||
ser.syncAsUint32LE(_lastTime);
|
||||
|
||||
syncListReverse<Library>(ser, _libraries);
|
||||
ser.syncString(_bookGroup);
|
||||
|
||||
syncArray<PendingPageChange>(ser, _pendingPageChanges);
|
||||
syncArray<uint16>(ser, _stack);
|
||||
syncArray<uint16>(ser, _vars);
|
||||
syncList<OldScript *>(ser, _oldScripts);
|
||||
syncArray<QueuedScript>(ser, _queuedScripts);
|
||||
|
||||
ser.syncAsSint16LE(_lastMousePos.x);
|
||||
ser.syncAsSint16LE(_lastMousePos.y);
|
||||
ser.syncAsByte(_mouseEnabled);
|
||||
ser.syncAsByte(_mouseVisible);
|
||||
ser.syncAsUint16LE(_mouseSpriteId);
|
||||
|
||||
syncListReverse<Pipe *>(ser, _pipes);
|
||||
syncList<Animation *>(ser, _anims);
|
||||
syncList<Sprite>(ser, _sprites);
|
||||
|
||||
byte paletteBuffer[256 * 3];
|
||||
_system->getPaletteManager()->grabPalette(paletteBuffer, 0, 256);
|
||||
ser.syncBytes(paletteBuffer, 256 * 3);
|
||||
|
||||
ser.syncAsSint16LE(_currSoundPriority);
|
||||
int16 audioBuffer[22050];
|
||||
int32 numSamples = _audioStream->readBuffer(audioBuffer, 22050);
|
||||
if (numSamples == -1) numSamples = 0;
|
||||
ser.syncAsSint32LE(numSamples);
|
||||
for (int32 i = 0; i < numSamples; i++)
|
||||
ser.syncAsSint16LE(audioBuffer[i]);
|
||||
|
||||
out->finalize();
|
||||
return Common::kNoError;
|
||||
}
|
||||
} // End of namespace Composer
|
||||
958
engines/composer/scripting.cpp
Normal file
958
engines/composer/scripting.cpp
Normal file
@@ -0,0 +1,958 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "common/scummsys.h"
|
||||
#include "common/savefile.h"
|
||||
|
||||
#include "composer/composer.h"
|
||||
#include "composer/graphics.h"
|
||||
#include "composer/resource.h"
|
||||
#include "common/macresman.h"
|
||||
|
||||
namespace Composer {
|
||||
|
||||
// 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,
|
||||
kOpNot = 0xF,
|
||||
kOpSqrt = 0x10,
|
||||
kOpRandom = 0x11,
|
||||
kOpExecuteScript = 0x12,
|
||||
kOpCallFunc = 0x13,
|
||||
kOpBoolLessThanEq = 0x17,
|
||||
kOpBoolLessThan = 0x16,
|
||||
kOpBoolGreaterThanEq = 0x15,
|
||||
kOpBoolGreaterThan = 0x14,
|
||||
kOpBoolEqual = 0x18,
|
||||
kOpBoolNotEqual = 0x19,
|
||||
kOpSaveArgs = 0x1A,
|
||||
kOpRestoreArgs = 0x1B,
|
||||
kOpReturn = 0x20,
|
||||
kOpLessThanEq = 0x22,
|
||||
kOpLessThan = 0x21,
|
||||
kOpGreaterThanEq = 0x24,
|
||||
kOpGreaterThan = 0x23,
|
||||
kOpEqual = 0x25,
|
||||
kOpNotEqual = 0x26,
|
||||
kOpJump = 0x80,
|
||||
kOpJumpIfNot = 0x81,
|
||||
kOpJumpIf = 0x82,
|
||||
kOpJumpIfNotValue = 0x83,
|
||||
kOpJumpIfValue = 0x84
|
||||
};
|
||||
|
||||
enum {
|
||||
kFuncPlayAnim = 35001,
|
||||
kFuncStopAnim = 35002,
|
||||
// (no 35003)
|
||||
kFuncQueueScript = 35004,
|
||||
kFuncDequeueScript = 35005,
|
||||
kFuncSetCursor = 35006,
|
||||
kFuncGetCursor = 35007,
|
||||
kFuncShowCursor = 35008,
|
||||
kFuncHideCursor = 35009,
|
||||
// (no 35010)
|
||||
kFuncActivateButton = 35011,
|
||||
kFuncDeactivateButton = 35012,
|
||||
kFuncNewPage = 35013,
|
||||
kFuncLoadPage = 35014,
|
||||
kFuncUnloadPage = 35015,
|
||||
kFuncSetPalette = 35016,
|
||||
kFuncSaveVars = 35017,
|
||||
kFuncLoadVars = 35018,
|
||||
kFuncQueueScriptOnce = 35019,
|
||||
kFuncGetMousePos = 35020,
|
||||
kFuncChangeBackground = 35021,
|
||||
kFuncSetBackgroundColor = 35022,
|
||||
kFuncClearSprites = 35023,
|
||||
kFuncAddSprite = 35024,
|
||||
kFuncRemoveSprite = 35025,
|
||||
kFuncQuit = 35026,
|
||||
kFuncSaveData = 35027,
|
||||
kFuncLoadData = 35028,
|
||||
kFuncGetSpriteSize = 35029
|
||||
};
|
||||
|
||||
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", (int)stream->size());
|
||||
uint16 scriptId = stream->readUint16LE();
|
||||
delete stream;
|
||||
|
||||
if (!scriptId)
|
||||
return;
|
||||
|
||||
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) {
|
||||
if (getGameType() == GType_ComposerV1) {
|
||||
runOldScript(id, param1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_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 (getGameType() == GType_ComposerV1) {
|
||||
runOldScript(id, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasResource(ID_SCRP, id)) {
|
||||
debug(1, "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)", (int)stream->size());
|
||||
uint16 size = stream->readUint16LE();
|
||||
if ((int)stream->size() < 2 + 2*size)
|
||||
error("SCRP was too small (%d, but claimed %d entries)", (int)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:
|
||||
if (numParams != 1)
|
||||
error("kOpPlusPlus had wrong number of params (%d)", numParams);
|
||||
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 kOpMinusMinus:
|
||||
if (numParams != 1)
|
||||
error("kOpMinusMinus had wrong number of params (%d)", numParams);
|
||||
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 kOpAssign:
|
||||
if (numParams != 2)
|
||||
error("kOpAssign had wrong number of params (%d)", numParams);
|
||||
val2 = getArg(script[pos + 2], arg2);
|
||||
debug(9, "[%d/%d] = [%d/%d] (%d)", script[pos + 1], arg1, script[pos + 2], arg2, val2);
|
||||
setArg(script[pos + 1], arg1, val2);
|
||||
break;
|
||||
case kOpAdd:
|
||||
if (numParams != 3)
|
||||
error("kOpAdd had wrong number of params (%d)", numParams);
|
||||
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 kOpSubtract:
|
||||
if (numParams != 3)
|
||||
error("kOpSubtract had wrong number of params (%d)", numParams);
|
||||
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 kOpMultiply:
|
||||
if (numParams != 3)
|
||||
error("kOpMultiply had wrong number of params (%d)", numParams);
|
||||
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 kOpDivide:
|
||||
if (numParams != 3)
|
||||
error("kOpDivide had wrong number of params (%d)", numParams);
|
||||
val2 = getArg(script[pos + 2], arg2);
|
||||
val3 = getArg(script[pos + 3], arg3);
|
||||
if (val3 == 0)
|
||||
error("script tried to divide by zero");
|
||||
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 kOpModulo:
|
||||
if (numParams != 3)
|
||||
error("kOpModulo had wrong number of params (%d)", numParams);
|
||||
val2 = getArg(script[pos + 2], arg2);
|
||||
val3 = getArg(script[pos + 3], arg3);
|
||||
if (val3 == 0)
|
||||
error("script tried to divide by zero (modulo)");
|
||||
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 kOpMaybeAlsoAssign:
|
||||
if (numParams != 2)
|
||||
error("kOpMaybeAlsoAssign had wrong number of params (%d)", numParams);
|
||||
val2 = getArg(script[pos + 2], arg2);
|
||||
debug(9, "[%d/%d] =(?) [%d/%d] (%d)", script[pos + 1], arg1, script[pos + 2], arg2, val2);
|
||||
setArg(script[pos + 1], arg1, val2);
|
||||
break;
|
||||
case kOpBooleanAssign:
|
||||
if (numParams != 2)
|
||||
error("kOpBooleanAssign had wrong number of params (%d)", numParams);
|
||||
val2 = getArg(script[pos + 2], arg2);
|
||||
debug(9, "[%d/%d] = [%d/%d] (%d) ? 1 : 0", script[pos + 1], arg1, script[pos + 2], arg2, val2);
|
||||
setArg(script[pos + 1], arg1, val2 ? 1 : 0);
|
||||
break;
|
||||
case kOpNegate:
|
||||
if (numParams != 2)
|
||||
error("kOpNegate had wrong number of params (%d)", numParams);
|
||||
val2 = getArg(script[pos + 2], arg2);
|
||||
debug(9, "[%d/%d] = -[%d/%d] (%d)", script[pos + 1], arg1, script[pos + 2], arg2, val2);
|
||||
setArg(script[pos + 1], arg1, -val2);
|
||||
break;
|
||||
case kOpAnd:
|
||||
if (numParams != 3)
|
||||
error("kOpAnd had wrong number of params (%d)", numParams);
|
||||
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 kOpOr:
|
||||
if (numParams != 3)
|
||||
error("kOpOr had wrong number of params (%d)", numParams);
|
||||
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 kOpXor:
|
||||
if (numParams != 3)
|
||||
error("kOpXor had wrong number of params (%d)", numParams);
|
||||
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 kOpNot:
|
||||
if (numParams != 2)
|
||||
error("kOpNot had wrong number of params (%d)", numParams);
|
||||
val2 = getArg(script[pos + 2], arg2);
|
||||
debug(9, "[%d/%d] = ![%d/%d] (!%d)", script[pos + 1], arg1, script[pos + 2], arg2, val2);
|
||||
setArg(script[pos + 1], arg1, val2 ? 0 : 1);
|
||||
break;
|
||||
case kOpSqrt:
|
||||
if (numParams != 2)
|
||||
error("kOpSqrt had wrong number of params (%d)", numParams);
|
||||
val2 = getArg(script[pos + 2], arg2);
|
||||
debug(9, "[%d/%d] = sqrt([%d/%d] (%d))", script[pos + 1], arg1, script[pos + 2], arg2, val2);
|
||||
setArg(script[pos + 1], arg1, (int16)sqrt((double)val2));
|
||||
break;
|
||||
case kOpRandom:
|
||||
if (numParams != 3)
|
||||
error("kOpRandom had wrong number of params (%d)", numParams);
|
||||
val2 = getArg(script[pos + 2], arg2);
|
||||
val3 = getArg(script[pos + 3], arg3);
|
||||
val1 = _rnd->getRandomNumberRng(val2, val3);
|
||||
debug(9, "[%d/%d] = rnd([%d/%d]=%d, [%d/%d]=%d) (%d)", script[pos + 1], arg1, script[pos + 2], arg2, val2, script[pos+3], arg3, val3, val1);
|
||||
setArg(script[pos + 1], arg1, val1);
|
||||
break;
|
||||
case kOpExecuteScript:
|
||||
if (numParams != 1)
|
||||
error("kOpExecuteScript had wrong number of params (%d)", numParams);
|
||||
val1 = getArg(script[pos + 1], arg1);
|
||||
debug(8, "run script [%d/%d]=%d", script[pos + 1], arg1, val1);
|
||||
runScript(val1);
|
||||
debug(8, "done run script");
|
||||
break;
|
||||
case kOpCallFunc:
|
||||
if (numParams != 1)
|
||||
error("kOpCallFunc had wrong number of params (%d)", numParams);
|
||||
val1 = getArg(script[pos + 1], arg1);
|
||||
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 kOpBoolLessThanEq:
|
||||
if (numParams != 2)
|
||||
error("kOpBoolLessThanEq had wrong number of params (%d)", numParams);
|
||||
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 kOpBoolLessThan:
|
||||
if (numParams != 2)
|
||||
error("kOpBoolLessThan had wrong number of params (%d)", numParams);
|
||||
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 kOpBoolGreaterThanEq:
|
||||
if (numParams != 2)
|
||||
error("kOpBoolGreaterThanEq had wrong number of params (%d)", numParams);
|
||||
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 kOpBoolGreaterThan:
|
||||
if (numParams != 2)
|
||||
error("kOpBoolGreaterThan had wrong number of params (%d)", numParams);
|
||||
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 kOpBoolEqual:
|
||||
if (numParams != 2)
|
||||
error("kOpBoolEqual had wrong number of params (%d)", numParams);
|
||||
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 kOpBoolNotEqual:
|
||||
if (numParams != 2)
|
||||
error("kOpBoolNotEqual had wrong number of params (%d)", numParams);
|
||||
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:
|
||||
if (numParams != 0)
|
||||
error("kOpSaveArgs had wrong number of params (%d)", numParams);
|
||||
debug(9, "save args");
|
||||
for (uint i = 1; i < 19; i++)
|
||||
_stack[stackBase + i] = _vars[i];
|
||||
break;
|
||||
case kOpRestoreArgs:
|
||||
if (numParams != 0)
|
||||
error("kOpRestoreArgs had wrong number of params (%d)", numParams);
|
||||
debug(9, "restore args");
|
||||
for (uint i = 1; i < 19; i++)
|
||||
_vars[i] = _stack[stackBase + i];
|
||||
break;
|
||||
case kOpReturn:
|
||||
if (numParams != 1)
|
||||
error("kOpReturn had wrong number of params (%d)", numParams);
|
||||
val1 = getArg(script[pos + 1], arg1);
|
||||
debug(9, "return [%d/%d]=%d", script[pos + 1], arg1, val1);
|
||||
_vars[0] = val1;
|
||||
break;
|
||||
case kOpLessThanEq:
|
||||
if (numParams != 3)
|
||||
error("kOpLessThanEq had wrong number of params (%d)", numParams);
|
||||
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, val3, val2);
|
||||
setArg(script[pos + 1], arg1, (val3 <= val2) ? 1 : 0);
|
||||
break;
|
||||
case kOpLessThan:
|
||||
if (numParams != 3)
|
||||
error("kOpLessThan had wrong number of params (%d)", numParams);
|
||||
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, val3, val2);
|
||||
setArg(script[pos + 1], arg1, (val3 < val2) ? 1 : 0);
|
||||
break;
|
||||
case kOpGreaterThanEq:
|
||||
if (numParams != 3)
|
||||
error("kOpGreaterThanEq had wrong number of params (%d)", numParams);
|
||||
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, val3, val2);
|
||||
setArg(script[pos + 1], arg1, (val3 >= val2) ? 1 : 0);
|
||||
break;
|
||||
case kOpGreaterThan:
|
||||
if (numParams != 3)
|
||||
error("kOpGreaterThan had wrong number of params (%d)", numParams);
|
||||
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, val3, val2);
|
||||
setArg(script[pos + 1], arg1, (val3 > val2) ? 1 : 0);
|
||||
break;
|
||||
case kOpEqual:
|
||||
if (numParams != 3)
|
||||
error("kOpEqual had wrong number of params (%d)", numParams);
|
||||
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 kOpNotEqual:
|
||||
if (numParams != 3)
|
||||
error("kOpNotEqual had wrong number of params (%d)", numParams);
|
||||
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:
|
||||
if (numParams != 1)
|
||||
error("kOpJump had wrong number of params (%d)", numParams);
|
||||
val1 = getArg(script[pos + 1], arg1);
|
||||
debug(9, "jump by [%d/%d]=%d", script[pos + 1], arg1, val1);
|
||||
pos += val1;
|
||||
break;
|
||||
case kOpJumpIfNot:
|
||||
if (numParams != 1)
|
||||
error("kOpJumpIfNot had wrong number of params (%d)", numParams);
|
||||
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 kOpJumpIf:
|
||||
if (numParams != 1)
|
||||
error("kOpJumpIf had wrong number of params (%d)", numParams);
|
||||
if (!lastResult)
|
||||
break;
|
||||
val1 = getArg(script[pos + 1], arg1);
|
||||
debug(9, "jump if, by [%d/%d]=%d", script[pos + 1], arg1, val1);
|
||||
pos += val1;
|
||||
break;
|
||||
case kOpJumpIfNotValue:
|
||||
if (numParams != 2)
|
||||
error("kOpJumpIfNotValue had wrong number of params (%d)", numParams);
|
||||
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;
|
||||
case kOpJumpIfValue:
|
||||
if (numParams != 2)
|
||||
error("kOpJumpIfValue had wrong number of params (%d)", numParams);
|
||||
val1 = getArg(script[pos + 1], arg1);
|
||||
val2 = getArg(script[pos + 2], arg2);
|
||||
debug(9, "jump if [%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);
|
||||
|
||||
if (op == kOpReturn)
|
||||
break;
|
||||
}
|
||||
|
||||
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);
|
||||
return 1; // TODO: return 0 on failure
|
||||
case kFuncStopAnim:
|
||||
debug(3, "kFuncStopAnim(%d)", param1);
|
||||
for (Common::List<Animation *>::iterator i = _anims.begin(); i != _anims.end(); i++) {
|
||||
if ((*i)->_id == param1)
|
||||
stopAnimation(*i);
|
||||
}
|
||||
return 0;
|
||||
case kFuncQueueScript:
|
||||
debug(3, "kFuncQueueScript(%d, %d, %d)", param1, param2, param3);
|
||||
_queuedScripts[param1]._baseTime = _currentTime;
|
||||
_queuedScripts[param1]._duration = 10 * param2;
|
||||
_queuedScripts[param1]._count = 0xffffffff;
|
||||
_queuedScripts[param1]._scriptId = param3;
|
||||
return 0;
|
||||
case kFuncDequeueScript:
|
||||
debug(3, "kFuncDequeueScript(%d)", param1);
|
||||
_queuedScripts[param1]._count = 0;
|
||||
_queuedScripts[param1]._scriptId = 0;
|
||||
return 0;
|
||||
case kFuncSetCursor:
|
||||
debug(3, "kSetCursor(%d, (%d, %d))", param1, param2, param3);
|
||||
{
|
||||
uint16 oldCursor = _mouseSpriteId;
|
||||
setCursor(param1, Common::Point(param2, param3));
|
||||
return oldCursor;
|
||||
}
|
||||
case kFuncGetCursor:
|
||||
debug(3, "kFuncGetCursor()");
|
||||
return _mouseSpriteId;
|
||||
case kFuncShowCursor:
|
||||
debug(3, "kFuncShowCursor()");
|
||||
setCursorVisible(true);
|
||||
return 0;
|
||||
case kFuncHideCursor:
|
||||
debug(3, "kFuncHideCursor()");
|
||||
setCursorVisible(false);
|
||||
return 0;
|
||||
case kFuncActivateButton:
|
||||
debug(3, "kFuncActivateButton(%d)", param1);
|
||||
setButtonActive(param1, true);
|
||||
return 1;
|
||||
case kFuncDeactivateButton:
|
||||
debug(3, "kFuncDeactivateButton(%d)", param1);
|
||||
setButtonActive(param1, false);
|
||||
return 1;
|
||||
case kFuncNewPage:
|
||||
debug(3, "kFuncNewPage(%d, %d)", param1, param2);
|
||||
_pendingPageChanges.push_back(PendingPageChange(param1, true));
|
||||
_pendingPageChanges.push_back(PendingPageChange(param2, false));
|
||||
return 1;
|
||||
case kFuncLoadPage:
|
||||
debug(3, "kFuncLoadPage(%d)", param1);
|
||||
_pendingPageChanges.push_back(PendingPageChange(param1, false));
|
||||
return 1;
|
||||
case kFuncUnloadPage:
|
||||
debug(3, "kFuncUnloadPage(%d)", param1);
|
||||
_pendingPageChanges.push_back(PendingPageChange(param1, true));
|
||||
return 1;
|
||||
case kFuncSetPalette:
|
||||
// TODO: return 0 if not disabling (0) and doesn't exist
|
||||
debug(4, "kFuncSetPalette(%d, %d)", param1, param2);
|
||||
// this seems only needed for a script bug in the Gregory credits, sigh
|
||||
if ((uint16)param2 > 100)
|
||||
param2 = 100;
|
||||
loadCTBL(param1, param2);
|
||||
return 1;
|
||||
case kFuncSaveVars:
|
||||
debug(3, "kFuncSaveVars(%d)", param1);
|
||||
{
|
||||
Common::String filename = getSaveStateName(param1);
|
||||
Common::WriteStream *stream = _saveFileMan->openForSaving(filename);
|
||||
for (uint i = 0; i < 1000; i++) {
|
||||
stream->writeUint16LE(_vars[i]);
|
||||
}
|
||||
delete stream;
|
||||
}
|
||||
return 1;
|
||||
case kFuncLoadVars:
|
||||
debug(3, "kFuncLoadVars(%d, %d, %d)", param1, param2, param3);
|
||||
{
|
||||
Common::String filename = getSaveStateName(param1);
|
||||
Common::SeekableReadStream *stream = _saveFileMan->openForLoading(filename);
|
||||
if (!stream) {
|
||||
if (!_bookIni.hasKey(Common::String::format("%d", param1), "Data"))
|
||||
return 0;
|
||||
Common::Path path = getFilename("Data", param1);
|
||||
Common::SeekableReadStream *file =
|
||||
Common::MacResManager::openFileOrDataFork(path);
|
||||
filename = path.toString(Common::Path::kNativeSeparator);
|
||||
if (!file)
|
||||
error("couldn't open '%s' to get vars id '%d'", filename.c_str(), param1);
|
||||
stream = file;
|
||||
}
|
||||
if (param3 == 0)
|
||||
param3 = 1000;
|
||||
if (param2 < 0 || param3 < 0 || param2 + param3 > 1000)
|
||||
error("can't read %d entries into %d from file '%s' for vars id '%d'", param3, param2, filename.c_str(), param1);
|
||||
stream->skip(param2 * 2);
|
||||
for (uint i = 0; i < (uint)param3; i++) {
|
||||
if (stream->pos() + 1 > stream->size())
|
||||
break;
|
||||
_vars[param2 + i] = stream->readUint16LE();
|
||||
}
|
||||
delete stream;
|
||||
}
|
||||
return 1;
|
||||
case kFuncQueueScriptOnce:
|
||||
debug(3, "kFuncQueueScriptOnce(%d, %d, %d)", param1, param2, param3);
|
||||
_queuedScripts[param1]._baseTime = _currentTime;
|
||||
_queuedScripts[param1]._duration = 10 * param2;
|
||||
_queuedScripts[param1]._count = 1;
|
||||
_queuedScripts[param1]._scriptId = param3;
|
||||
return 0;
|
||||
case kFuncGetMousePos:
|
||||
debug(3, "kFuncGetMousePos(%d, %d)", param1, param2);
|
||||
_vars[param1] = _lastMousePos.x;
|
||||
_vars[param2] = _lastMousePos.y;
|
||||
return 0;
|
||||
case kFuncChangeBackground:
|
||||
debug(3, "kFuncChangeBackground(%d)", param1);
|
||||
// TODO: return 1 if background existed, else 0
|
||||
setBackground(param1);
|
||||
return 1;
|
||||
case kFuncSetBackgroundColor:
|
||||
// TODO
|
||||
warning("ignoring kFuncSetBackgroundColor(%d)", param1);
|
||||
return 0;
|
||||
case kFuncClearSprites:
|
||||
debug(3, "kFuncClearSprites()");
|
||||
removeSprite(0, 0);
|
||||
return 0;
|
||||
case kFuncAddSprite:
|
||||
{
|
||||
Common::Point pos(_vars[param3], _vars[param3 + 1]);
|
||||
int16 zorder = _vars[param3 + 2];
|
||||
debug(3, "kFuncAddSprite(%d, %d, [%d = (%d, %d), %d])", param1, param2, param3, pos.x, pos.y, zorder);
|
||||
addSprite(param1, param2, zorder, pos);
|
||||
}
|
||||
return 0;
|
||||
case kFuncRemoveSprite:
|
||||
debug(3, "kFuncRemoveSprite(%d, %d)", param1, param2);
|
||||
removeSprite(param1, param2);
|
||||
return 0;
|
||||
case kFuncQuit:
|
||||
debug(3, "kFuncQuit()");
|
||||
quitGame();
|
||||
return 0;
|
||||
case kFuncSaveData:
|
||||
// TODO
|
||||
warning("ignoring kFuncSaveData(%d, %d, %d)", param1, param2, param3);
|
||||
return 1;
|
||||
case kFuncLoadData:
|
||||
debug(3, "kFuncLoadData(%d, %d, %d)", param1, param2, param3);
|
||||
{
|
||||
Common::Path filename = getFilename("Data", param1);
|
||||
Common::File *file = new Common::File();
|
||||
if (!file->open(filename))
|
||||
error("couldn't open '%s' to get data id '%d'", filename.toString(Common::Path::kNativeSeparator).c_str(), param1);
|
||||
if (param3 == 0)
|
||||
param3 = 1000;
|
||||
if (param2 < 0 || param3 < 0 || param2 + param3 > 1000)
|
||||
error("can't read %d entries into %d from file '%s' for data id '%d'", param3, param2, filename.toString(Common::Path::kNativeSeparator).c_str(), param1);
|
||||
for (uint i = 0; i < (uint)param3; i++) {
|
||||
if (file->pos() + 1 > file->size())
|
||||
break;
|
||||
_vars[param2 + i] = file->readUint16LE();
|
||||
}
|
||||
delete file;
|
||||
}
|
||||
return 1;
|
||||
case kFuncGetSpriteSize:
|
||||
debug(3, "kFuncGetSpriteSize(%d, %d, %d)", param1, param2, param3);
|
||||
int16 width, height;
|
||||
width = 0;
|
||||
height = 0;
|
||||
{
|
||||
Common::SeekableReadStream *stream = getStreamForSprite(param1);
|
||||
if (stream) {
|
||||
stream->readUint16LE();
|
||||
height = stream->readSint16LE();
|
||||
width = stream->readSint16LE();
|
||||
delete stream;
|
||||
}
|
||||
}
|
||||
_vars[param2] = width;
|
||||
_vars[param3] = height;
|
||||
return 0;
|
||||
default:
|
||||
error("unknown scriptFuncCall %d(%d, %d, %d)", (uint32)id, param1, param2, param3);
|
||||
}
|
||||
}
|
||||
|
||||
OldScript::OldScript(uint16 id, Common::SeekableReadStream *stream) : _id(id), _stream(stream) {
|
||||
_size = _stream->readUint32LE();
|
||||
_stream->skip(2);
|
||||
_currDelay = 0;
|
||||
_zorder = 10;
|
||||
}
|
||||
|
||||
OldScript::~OldScript() {
|
||||
delete _stream;
|
||||
}
|
||||
|
||||
void ComposerEngine::runOldScript(uint16 id, uint16 wait) {
|
||||
stopOldScript(id);
|
||||
|
||||
Common::SeekableReadStream *stream = getResource(ID_SCRP, id);
|
||||
OldScript *script = new OldScript(id, stream);
|
||||
script->_currDelay = wait;
|
||||
_oldScripts.push_back(script);
|
||||
}
|
||||
|
||||
void ComposerEngine::stopOldScript(uint16 id) {
|
||||
// FIXME: this could potentially (in the case of buggy script) be called on an in-use script
|
||||
|
||||
for (Common::List<OldScript *>::iterator i = _oldScripts.begin(); i != _oldScripts.end(); i++) {
|
||||
if ((*i)->_id == id) {
|
||||
removeSprite(0, id);
|
||||
delete *i;
|
||||
i = _oldScripts.reverse_erase(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ComposerEngine::tickOldScripts() {
|
||||
for (Common::List<OldScript *>::iterator i = _oldScripts.begin(); i != _oldScripts.end(); i++) {
|
||||
if (!tickOldScript(*i)) {
|
||||
delete *i;
|
||||
i = _oldScripts.reverse_erase(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum {
|
||||
kOldOpNoOp = 0,
|
||||
kOldOpReplaceSprite = 1,
|
||||
kOldOpPlayWav = 2,
|
||||
kOldOpRunScript = 3,
|
||||
kOldOpStopScript = 4,
|
||||
kOldOpActivateButton = 5,
|
||||
kOldOpDeactivateButton = 6,
|
||||
kOldOpDrawSprite = 7,
|
||||
kOldOpRemoveSprite = 8,
|
||||
kOldOpDisableMouseInput = 9,
|
||||
kOldOpEnableMouseInput = 10,
|
||||
kOldOpWait = 11,
|
||||
kOldOpRandWait = 12,
|
||||
kOldOpDrawGlobalSprite = 13,
|
||||
kOldOpRemoveGlobalSprite = 14,
|
||||
kOldOpSetZOrder = 15,
|
||||
kOldOpPlayPipe = 16,
|
||||
kOldOpStopPipe = 17,
|
||||
kOldOpNewScreen = 20,
|
||||
kOldOpRunRandom = 22
|
||||
};
|
||||
|
||||
bool ComposerEngine::tickOldScript(OldScript *script) {
|
||||
if (script->_currDelay) {
|
||||
script->_currDelay--;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool running = true;
|
||||
bool erasedOldSprite = false;
|
||||
while (running && script->_stream->pos() < (int)script->_size) {
|
||||
uint16 spriteId, scriptId, buttonId, pipeId;
|
||||
Common::Point spritePos;
|
||||
|
||||
script->_stream->skip(0);
|
||||
byte op = script->_stream->readByte();
|
||||
switch (op) {
|
||||
case kOldOpNoOp:
|
||||
debug(3, "kOldOpNoOp()");
|
||||
running = false;
|
||||
break;
|
||||
case kOldOpReplaceSprite:
|
||||
if (!erasedOldSprite) {
|
||||
removeSprite(0, script->_id);
|
||||
erasedOldSprite = true;
|
||||
}
|
||||
|
||||
spriteId = script->_stream->readUint16LE();
|
||||
spritePos.x = script->_stream->readSint16LE();
|
||||
spritePos.y = script->_stream->readSint16LE();
|
||||
debug(3, "kOldOpReplaceSprite(%d, %d, %d)", spriteId, spritePos.x, spritePos.y);
|
||||
addSprite(spriteId, script->_id, script->_zorder, spritePos);
|
||||
break;
|
||||
case kOldOpPlayWav:
|
||||
uint16 wavId, prio;
|
||||
wavId = script->_stream->readUint16LE();
|
||||
prio = script->_stream->readUint16LE();
|
||||
debug(3, "kOldOpPlayWav(%d, %d)", wavId, prio);
|
||||
playWaveForAnim(wavId, prio, false);
|
||||
break;
|
||||
case kOldOpRunScript:
|
||||
scriptId = script->_stream->readUint16LE();
|
||||
debug(3, "kOldOpRunScript(%d)", scriptId);
|
||||
if (scriptId == script->_id) {
|
||||
// reset ourselves
|
||||
removeSprite(0, script->_id);
|
||||
script->_stream->seek(6);
|
||||
} else {
|
||||
runScript(scriptId);
|
||||
}
|
||||
break;
|
||||
case kOldOpStopScript:
|
||||
scriptId = script->_stream->readUint16LE();
|
||||
debug(3, "kOldOpStopScript(%d)", scriptId);
|
||||
removeSprite(0, scriptId);
|
||||
stopOldScript(scriptId);
|
||||
break;
|
||||
case kOldOpActivateButton:
|
||||
buttonId = script->_stream->readUint16LE();
|
||||
debug(3, "kOldOpActivateButton(%d)", buttonId);
|
||||
setButtonActive(buttonId, true);
|
||||
break;
|
||||
case kOldOpDeactivateButton:
|
||||
buttonId = script->_stream->readUint16LE();
|
||||
debug(3, "kOldOpDeactivateButton(%d)", buttonId);
|
||||
setButtonActive(buttonId, false);
|
||||
break;
|
||||
case kOldOpDrawSprite:
|
||||
spriteId = script->_stream->readUint16LE();
|
||||
spritePos.x = script->_stream->readSint16LE();
|
||||
spritePos.y = script->_stream->readSint16LE();
|
||||
debug(3, "kOldOpDrawSprite(%d, %d, %d)", spriteId, spritePos.x, spritePos.y);
|
||||
addSprite(spriteId, script->_id, script->_zorder, spritePos);
|
||||
break;
|
||||
case kOldOpRemoveSprite:
|
||||
spriteId = script->_stream->readUint16LE();
|
||||
debug(3, "kOldOpRemoveSprite(%d)", spriteId);
|
||||
removeSprite(spriteId, script->_id);
|
||||
break;
|
||||
case kOldOpDisableMouseInput:
|
||||
debug(3, "kOldOpDisableMouseInput()");
|
||||
setCursorVisible(false);
|
||||
break;
|
||||
case kOldOpEnableMouseInput:
|
||||
debug(3, "kOldOpEnableMouseInput()");
|
||||
setCursorVisible(true);
|
||||
break;
|
||||
case kOldOpWait:
|
||||
script->_currDelay = script->_stream->readUint16LE();
|
||||
debug(3, "kOldOpWait(%d)", script->_currDelay);
|
||||
break;
|
||||
case kOldOpRandWait:
|
||||
uint16 min, max;
|
||||
min = script->_stream->readUint16LE();
|
||||
max = script->_stream->readUint16LE();
|
||||
debug(3, "kOldOpRandWait(%d, %d)", min, max);
|
||||
script->_currDelay = _rnd->getRandomNumberRng(min, max);
|
||||
break;
|
||||
case kOldOpDrawGlobalSprite:
|
||||
spriteId = script->_stream->readUint16LE();
|
||||
spritePos.x = script->_stream->readSint16LE();
|
||||
spritePos.y = script->_stream->readSint16LE();
|
||||
debug(3, "kOldOpDrawGlobalSprite(%d, %d, %d)", spriteId, spritePos.x, spritePos.y);
|
||||
addSprite(spriteId, 0, script->_zorder, spritePos);
|
||||
break;
|
||||
case kOldOpRemoveGlobalSprite:
|
||||
spriteId = script->_stream->readUint16LE();
|
||||
debug(3, "kOldOpRemoveGlobalSprite(%d)", spriteId);
|
||||
removeSprite(spriteId, 0);
|
||||
break;
|
||||
case kOldOpSetZOrder:
|
||||
script->_zorder = script->_stream->readUint16LE();
|
||||
debug(3, "kOldOpSetZOrder(%d)", script->_zorder);
|
||||
break;
|
||||
case kOldOpPlayPipe:
|
||||
pipeId = script->_stream->readUint16LE();
|
||||
debug(3, "kOldOpPlayPipe(%d)", pipeId);
|
||||
playPipe(pipeId);
|
||||
break;
|
||||
case kOldOpStopPipe:
|
||||
pipeId = script->_stream->readUint16LE();
|
||||
debug(3, "kOldOpStopPipe(%d)", pipeId);
|
||||
// yes, pipeId is ignored here..
|
||||
stopPipes();
|
||||
break;
|
||||
case kOldOpNewScreen:
|
||||
uint16 newScreenId;
|
||||
newScreenId = script->_stream->readUint16LE();
|
||||
debug(3, "kOldOpNewScreen(%d)", newScreenId);
|
||||
if (!newScreenId) {
|
||||
quitGame();
|
||||
} else {
|
||||
_pendingPageChanges.clear();
|
||||
_pendingPageChanges.push_back(PendingPageChange(newScreenId, false));
|
||||
}
|
||||
break;
|
||||
case kOldOpRunRandom:
|
||||
uint16 randomId;
|
||||
randomId = script->_stream->readUint16LE();
|
||||
debug(3, "kOldOpRunRandom(%d)", randomId);
|
||||
if (!_randomEvents.contains(randomId)) {
|
||||
warning("kOldOpRunRandom found no entries for id %d", randomId);
|
||||
} else {
|
||||
uint32 randValue = _rnd->getRandomNumberRng(0, 32767);
|
||||
const Common::Array<RandomEvent> &events = _randomEvents[randomId];
|
||||
uint i = 0;
|
||||
for (i = 0; i < events.size(); i++) {
|
||||
if ((i + 1 == events.size()) || (randValue <= events[i].weight)) {
|
||||
runScript(events[i].scriptId);
|
||||
break;
|
||||
} else {
|
||||
randValue -= events[i].weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error("unknown oldScript op %d", op);
|
||||
}
|
||||
}
|
||||
|
||||
if (script->_stream->pos() >= (int)script->_size) {
|
||||
// stop running if we ran out of script
|
||||
removeSprite(0, script->_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Composer
|
||||
Reference in New Issue
Block a user