/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "common/scummsys.h" #include "common/config-manager.h" #include "common/events.h" #include "common/file.h" #include "common/random.h" #include "common/fs.h" #include "common/keyboard.h" #include "common/substream.h" #include "graphics/cursorman.h" #include "graphics/surface.h" #include "graphics/pixelformat.h" #include "engines/util.h" #include "engines/advancedDetector.h" #include "audio/audiostream.h" #include "composer/composer.h" #include "composer/graphics.h" #include "composer/resource.h" namespace Composer { ComposerEngine::ComposerEngine(OSystem *syst, const ComposerGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) { _rnd = new Common::RandomSource("composer"); _audioStream = NULL; } ComposerEngine::~ComposerEngine() { DebugMan.clearAllDebugChannels(); for (Common::List::iterator i = _libraries.begin(); i != _libraries.end(); i++) delete i->_archive; for (Common::List::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; } _mouseVisible = true; _mouseEnabled = false; _mouseSpriteId = 0; _lastButton = NULL; _directoriesToStrip = 1; if (!_bookIni.loadFromFile("book.ini")) { _directoriesToStrip = 0; if (!_bookIni.loadFromFile("programs/book.ini")) error("failed to find book.ini"); } uint width = 640; if (_bookIni.hasKey("Width", "Common")) width = atoi(getStringFromConfig("Common", "Width").c_str()); uint height = 480; if (_bookIni.hasKey("Height", "Common")) height = atoi(getStringFromConfig("Common", "Height").c_str()); initGraphics(width, height, true); _surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8()); _needsUpdate = true; loadLibrary(0); uint fps = atoi(getStringFromConfig("Common", "FPS").c_str()); uint frameTime = 1000 / fps; uint32 lastDrawTime = 0; while (!shouldQuit()) { for (uint i = 0; i < _pendingPageChanges.size(); i++) { if (_pendingPageChanges[i]._remove) unloadLibrary(_pendingPageChanges[i]._pageId); else loadLibrary(_pendingPageChanges[i]._pageId); lastDrawTime = _system->getMillis(); } _pendingPageChanges.clear(); uint32 thisTime = _system->getMillis(); for (uint i = 0; i < _queuedScripts.size(); i++) { QueuedScript &script = _queuedScripts[i]; if (!script._count) continue; if (script._baseTime + script._duration > thisTime) continue; if (script._count != 0xffffffff) script._count--; script._baseTime = thisTime; 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; redraw(); processAnimFrame(); } else if (_needsUpdate) { redraw(); } 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: switch (event.kbd.keycode) { case Common::KEYCODE_d: /*if (event.kbd.hasFlags(Common::KBD_CTRL)) { // Start the debugger getDebugger()->attach(); getDebugger()->onFrame(); }*/ break; case Common::KEYCODE_q: if (event.kbd.hasFlags(Common::KBD_CTRL)) quitGame(); break; default: break; } onKeyDown(event.kbd.keycode); break; case Common::EVENT_QUIT: case Common::EVENT_RTL: quitGame(); break; default: break; } } _system->delayMillis(20); } 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; // TODO: other buttons? uint16 buttonsDown = 1; // MK_LBUTTON uint16 spriteId = sprite ? sprite->_id : 0; runScript(button->_scriptId, 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, _lastButton->_id, buttonsDown, 0); _lastButton = button; if (_lastButton && _lastButton->_scriptIdRollOn) runScript(_lastButton->_scriptIdRollOn, _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); } 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); onMouseMove(_lastMousePos); } else if (!visible && _mouseVisible) { _mouseVisible = false; if (_mouseSpriteId) removeSprite(_mouseSpriteId, 0); } } Common::String ComposerEngine::getStringFromConfig(const Common::String §ion, const Common::String &key) { Common::String value; if (!_bookIni.getKey(key, section, value)) error("failed to find key '%s' in section '%s' of book config", key.c_str(), section.c_str()); return value; } Common::String ComposerEngine::getFilename(const Common::String §ion, uint id) { Common::String key = Common::String::format("%d", id); Common::String filename = getStringFromConfig(section, key); return mangleFilename(filename); } Common::String ComposerEngine::mangleFilename(Common::String filename) { while (filename.size() && (filename[0] == '~' || filename[0] == ':' || filename[0] == '\\')) filename = filename.c_str() + 1; uint slashesToStrip = _directoriesToStrip; while (slashesToStrip--) { for (uint i = 0; i < filename.size(); i++) { if (filename[i] != '\\') continue; filename = filename.c_str() + i + 1; break; } } Common::String outFilename; for (uint i = 0; i < filename.size(); i++) { if (filename[i] == '\\') outFilename += '/'; else outFilename += filename[i]; } return outFilename; } void ComposerEngine::loadLibrary(uint id) { if (!id) id = atoi(getStringFromConfig("Common", "StartUp").c_str()); Common::String filename = getFilename("Libs", id); Library library; library._id = id; library._archive = new ComposerArchive(); if (!library._archive->openFile(filename)) error("failed to open '%s'", filename.c_str()); _libraries.push_front(library); Library &newLib = _libraries.front(); Common::Array 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