From 5842c5098fbd00c8ab5245ba1d6ca9c2675e3da4 Mon Sep 17 00:00:00 2001 From: RichieSams Date: Fri, 1 Nov 2013 00:21:08 -0500 Subject: ZVISION: Create a folder structure for ZVision source files I personally used filters within my IDE, but since others are now joining the project, it was brought to my attention that some better organization would be nice. --- engines/zvision/actions.cpp | 401 ---------------- engines/zvision/actions.h | 346 -------------- engines/zvision/animation/rlf_animation.cpp | 331 +++++++++++++ engines/zvision/animation/rlf_animation.h | 163 +++++++ engines/zvision/animation_control.cpp | 263 ----------- engines/zvision/animation_control.h | 87 ---- engines/zvision/archives/zfs_archive.cpp | 157 ++++++ engines/zvision/archives/zfs_archive.h | 126 +++++ engines/zvision/clock.cpp | 70 --- engines/zvision/clock.h | 78 --- engines/zvision/console.cpp | 218 --------- engines/zvision/console.h | 55 --- engines/zvision/control.cpp | 124 ----- engines/zvision/control.h | 146 ------ engines/zvision/core/console.cpp | 218 +++++++++ engines/zvision/core/console.h | 55 +++ engines/zvision/core/events.cpp | 186 ++++++++ engines/zvision/core/menu.h | 28 ++ engines/zvision/core/save_manager.cpp | 206 ++++++++ engines/zvision/core/save_manager.h | 91 ++++ engines/zvision/cursor.cpp | 94 ---- engines/zvision/cursor.h | 66 --- engines/zvision/cursor_manager.cpp | 152 ------ engines/zvision/cursor_manager.h | 114 ----- engines/zvision/cursors/cursor.cpp | 94 ++++ engines/zvision/cursors/cursor.h | 66 +++ engines/zvision/cursors/cursor_manager.cpp | 152 ++++++ engines/zvision/cursors/cursor_manager.h | 114 +++++ engines/zvision/events.cpp | 186 -------- engines/zvision/fonts/truetype_font.cpp | 116 +++++ engines/zvision/fonts/truetype_font.h | 81 ++++ engines/zvision/graphics/render_manager.cpp | 526 +++++++++++++++++++++ engines/zvision/graphics/render_manager.h | 328 +++++++++++++ engines/zvision/graphics/render_table.cpp | 240 ++++++++++ engines/zvision/graphics/render_table.h | 85 ++++ engines/zvision/input_control.cpp | 142 ------ engines/zvision/input_control.h | 60 --- engines/zvision/inventory/inventory_manager.h | 28 ++ engines/zvision/inventory_manager.h | 28 -- engines/zvision/lever_control.cpp | 402 ---------------- engines/zvision/lever_control.h | 127 ----- engines/zvision/lzss_read_stream.cpp | 103 ---- engines/zvision/lzss_read_stream.h | 72 --- engines/zvision/menu.h | 28 -- engines/zvision/push_toggle_control.cpp | 98 ---- engines/zvision/push_toggle_control.h | 67 --- engines/zvision/puzzle.h | 74 --- engines/zvision/render_manager.cpp | 526 --------------------- engines/zvision/render_manager.h | 328 ------------- engines/zvision/render_table.cpp | 240 ---------- engines/zvision/render_table.h | 85 ---- engines/zvision/rlf_animation.cpp | 331 ------------- engines/zvision/rlf_animation.h | 163 ------- engines/zvision/save_manager.cpp | 206 -------- engines/zvision/save_manager.h | 91 ---- engines/zvision/scr_file_handling.cpp | 302 ------------ engines/zvision/script_manager.cpp | 444 ----------------- engines/zvision/script_manager.h | 224 --------- engines/zvision/scripting/actions.cpp | 401 ++++++++++++++++ engines/zvision/scripting/actions.h | 346 ++++++++++++++ engines/zvision/scripting/control.cpp | 124 +++++ engines/zvision/scripting/control.h | 146 ++++++ .../scripting/controls/animation_control.cpp | 263 +++++++++++ .../zvision/scripting/controls/animation_control.h | 87 ++++ .../zvision/scripting/controls/input_control.cpp | 142 ++++++ engines/zvision/scripting/controls/input_control.h | 60 +++ .../zvision/scripting/controls/lever_control.cpp | 402 ++++++++++++++++ engines/zvision/scripting/controls/lever_control.h | 127 +++++ .../scripting/controls/push_toggle_control.cpp | 98 ++++ .../scripting/controls/push_toggle_control.h | 67 +++ engines/zvision/scripting/controls/timer_node.cpp | 73 +++ engines/zvision/scripting/controls/timer_node.h | 55 +++ engines/zvision/scripting/puzzle.h | 74 +++ engines/zvision/scripting/scr_file_handling.cpp | 302 ++++++++++++ engines/zvision/scripting/script_manager.cpp | 444 +++++++++++++++++ engines/zvision/scripting/script_manager.h | 224 +++++++++ engines/zvision/single_value_container.cpp | 348 -------------- engines/zvision/single_value_container.h | 183 ------- engines/zvision/sound/zork_raw.cpp | 217 +++++++++ engines/zvision/sound/zork_raw.h | 120 +++++ engines/zvision/string_manager.cpp | 255 ---------- engines/zvision/string_manager.h | 85 ---- engines/zvision/strings/string_manager.cpp | 255 ++++++++++ engines/zvision/strings/string_manager.h | 85 ++++ engines/zvision/subtitles.h | 29 -- engines/zvision/subtitles/subtitles.h | 29 ++ engines/zvision/timer_node.cpp | 73 --- engines/zvision/timer_node.h | 55 --- engines/zvision/truetype_font.cpp | 116 ----- engines/zvision/truetype_font.h | 81 ---- engines/zvision/utility.cpp | 237 ---------- engines/zvision/utility.h | 115 ----- engines/zvision/utility/clock.cpp | 70 +++ engines/zvision/utility/clock.h | 78 +++ engines/zvision/utility/lzss_read_stream.cpp | 103 ++++ engines/zvision/utility/lzss_read_stream.h | 72 +++ engines/zvision/utility/single_value_container.cpp | 348 ++++++++++++++ engines/zvision/utility/single_value_container.h | 183 +++++++ engines/zvision/utility/utility.cpp | 237 ++++++++++ engines/zvision/utility/utility.h | 115 +++++ engines/zvision/video.cpp | 171 ------- engines/zvision/video/video.cpp | 171 +++++++ engines/zvision/video/zork_avi_decoder.cpp | 53 +++ engines/zvision/video/zork_avi_decoder.h | 60 +++ engines/zvision/zfs_archive.cpp | 157 ------ engines/zvision/zfs_archive.h | 126 ----- engines/zvision/zork_avi_decoder.cpp | 53 --- engines/zvision/zork_avi_decoder.h | 60 --- engines/zvision/zork_raw.cpp | 217 --------- engines/zvision/zork_raw.h | 120 ----- 110 files changed, 9022 insertions(+), 9022 deletions(-) delete mode 100644 engines/zvision/actions.cpp delete mode 100644 engines/zvision/actions.h create mode 100644 engines/zvision/animation/rlf_animation.cpp create mode 100644 engines/zvision/animation/rlf_animation.h delete mode 100644 engines/zvision/animation_control.cpp delete mode 100644 engines/zvision/animation_control.h create mode 100644 engines/zvision/archives/zfs_archive.cpp create mode 100644 engines/zvision/archives/zfs_archive.h delete mode 100644 engines/zvision/clock.cpp delete mode 100644 engines/zvision/clock.h delete mode 100644 engines/zvision/console.cpp delete mode 100644 engines/zvision/console.h delete mode 100644 engines/zvision/control.cpp delete mode 100644 engines/zvision/control.h create mode 100644 engines/zvision/core/console.cpp create mode 100644 engines/zvision/core/console.h create mode 100644 engines/zvision/core/events.cpp create mode 100644 engines/zvision/core/menu.h create mode 100644 engines/zvision/core/save_manager.cpp create mode 100644 engines/zvision/core/save_manager.h delete mode 100644 engines/zvision/cursor.cpp delete mode 100644 engines/zvision/cursor.h delete mode 100644 engines/zvision/cursor_manager.cpp delete mode 100644 engines/zvision/cursor_manager.h create mode 100644 engines/zvision/cursors/cursor.cpp create mode 100644 engines/zvision/cursors/cursor.h create mode 100644 engines/zvision/cursors/cursor_manager.cpp create mode 100644 engines/zvision/cursors/cursor_manager.h delete mode 100644 engines/zvision/events.cpp create mode 100644 engines/zvision/fonts/truetype_font.cpp create mode 100644 engines/zvision/fonts/truetype_font.h create mode 100644 engines/zvision/graphics/render_manager.cpp create mode 100644 engines/zvision/graphics/render_manager.h create mode 100644 engines/zvision/graphics/render_table.cpp create mode 100644 engines/zvision/graphics/render_table.h delete mode 100644 engines/zvision/input_control.cpp delete mode 100644 engines/zvision/input_control.h create mode 100644 engines/zvision/inventory/inventory_manager.h delete mode 100644 engines/zvision/inventory_manager.h delete mode 100644 engines/zvision/lever_control.cpp delete mode 100644 engines/zvision/lever_control.h delete mode 100644 engines/zvision/lzss_read_stream.cpp delete mode 100644 engines/zvision/lzss_read_stream.h delete mode 100644 engines/zvision/menu.h delete mode 100644 engines/zvision/push_toggle_control.cpp delete mode 100644 engines/zvision/push_toggle_control.h delete mode 100644 engines/zvision/puzzle.h delete mode 100644 engines/zvision/render_manager.cpp delete mode 100644 engines/zvision/render_manager.h delete mode 100644 engines/zvision/render_table.cpp delete mode 100644 engines/zvision/render_table.h delete mode 100644 engines/zvision/rlf_animation.cpp delete mode 100644 engines/zvision/rlf_animation.h delete mode 100644 engines/zvision/save_manager.cpp delete mode 100644 engines/zvision/save_manager.h delete mode 100644 engines/zvision/scr_file_handling.cpp delete mode 100644 engines/zvision/script_manager.cpp delete mode 100644 engines/zvision/script_manager.h create mode 100644 engines/zvision/scripting/actions.cpp create mode 100644 engines/zvision/scripting/actions.h create mode 100644 engines/zvision/scripting/control.cpp create mode 100644 engines/zvision/scripting/control.h create mode 100644 engines/zvision/scripting/controls/animation_control.cpp create mode 100644 engines/zvision/scripting/controls/animation_control.h create mode 100644 engines/zvision/scripting/controls/input_control.cpp create mode 100644 engines/zvision/scripting/controls/input_control.h create mode 100644 engines/zvision/scripting/controls/lever_control.cpp create mode 100644 engines/zvision/scripting/controls/lever_control.h create mode 100644 engines/zvision/scripting/controls/push_toggle_control.cpp create mode 100644 engines/zvision/scripting/controls/push_toggle_control.h create mode 100644 engines/zvision/scripting/controls/timer_node.cpp create mode 100644 engines/zvision/scripting/controls/timer_node.h create mode 100644 engines/zvision/scripting/puzzle.h create mode 100644 engines/zvision/scripting/scr_file_handling.cpp create mode 100644 engines/zvision/scripting/script_manager.cpp create mode 100644 engines/zvision/scripting/script_manager.h delete mode 100644 engines/zvision/single_value_container.cpp delete mode 100644 engines/zvision/single_value_container.h create mode 100644 engines/zvision/sound/zork_raw.cpp create mode 100644 engines/zvision/sound/zork_raw.h delete mode 100644 engines/zvision/string_manager.cpp delete mode 100644 engines/zvision/string_manager.h create mode 100644 engines/zvision/strings/string_manager.cpp create mode 100644 engines/zvision/strings/string_manager.h delete mode 100644 engines/zvision/subtitles.h create mode 100644 engines/zvision/subtitles/subtitles.h delete mode 100644 engines/zvision/timer_node.cpp delete mode 100644 engines/zvision/timer_node.h delete mode 100644 engines/zvision/truetype_font.cpp delete mode 100644 engines/zvision/truetype_font.h delete mode 100644 engines/zvision/utility.cpp delete mode 100644 engines/zvision/utility.h create mode 100644 engines/zvision/utility/clock.cpp create mode 100644 engines/zvision/utility/clock.h create mode 100644 engines/zvision/utility/lzss_read_stream.cpp create mode 100644 engines/zvision/utility/lzss_read_stream.h create mode 100644 engines/zvision/utility/single_value_container.cpp create mode 100644 engines/zvision/utility/single_value_container.h create mode 100644 engines/zvision/utility/utility.cpp create mode 100644 engines/zvision/utility/utility.h delete mode 100644 engines/zvision/video.cpp create mode 100644 engines/zvision/video/video.cpp create mode 100644 engines/zvision/video/zork_avi_decoder.cpp create mode 100644 engines/zvision/video/zork_avi_decoder.h delete mode 100644 engines/zvision/zfs_archive.cpp delete mode 100644 engines/zvision/zfs_archive.h delete mode 100644 engines/zvision/zork_avi_decoder.cpp delete mode 100644 engines/zvision/zork_avi_decoder.h delete mode 100644 engines/zvision/zork_raw.cpp delete mode 100644 engines/zvision/zork_raw.h (limited to 'engines') diff --git a/engines/zvision/actions.cpp b/engines/zvision/actions.cpp deleted file mode 100644 index 0bb553a559..0000000000 --- a/engines/zvision/actions.cpp +++ /dev/null @@ -1,401 +0,0 @@ -/* 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 "zvision/actions.h" - -#include "zvision/zvision.h" -#include "zvision/script_manager.h" -#include "zvision/render_manager.h" -#include "zvision/zork_raw.h" -#include "zvision/zork_avi_decoder.h" -#include "zvision/timer_node.h" -#include "zvision/animation_control.h" - -#include "common/file.h" - -#include "audio/decoders/wave.h" - - -namespace ZVision { - -////////////////////////////////////////////////////////////////////////////// -// ActionAdd -////////////////////////////////////////////////////////////////////////////// - -ActionAdd::ActionAdd(const Common::String &line) { - sscanf(line.c_str(), "%*[^(](%u,%u)", &_key, &_value); -} - -bool ActionAdd::execute(ZVision *engine) { - engine->getScriptManager()->addToStateValue(_key, _value); - return true; -} - - -////////////////////////////////////////////////////////////////////////////// -// ActionAssign -////////////////////////////////////////////////////////////////////////////// - -ActionAssign::ActionAssign(const Common::String &line) { - sscanf(line.c_str(), "%*[^(](%u, %u)", &_key, &_value); -} - -bool ActionAssign::execute(ZVision *engine) { - engine->getScriptManager()->setStateValue(_key, _value); - return true; -} - - -////////////////////////////////////////////////////////////////////////////// -// ActionAttenuate -////////////////////////////////////////////////////////////////////////////// - -ActionAttenuate::ActionAttenuate(const Common::String &line) { - sscanf(line.c_str(), "%*[^(](%u, %d)", &_key, &_attenuation); -} - -bool ActionAttenuate::execute(ZVision *engine) { - // TODO: Implement - return true; -} - - -////////////////////////////////////////////////////////////////////////////// -// ActionChangeLocation -////////////////////////////////////////////////////////////////////////////// - -ActionChangeLocation::ActionChangeLocation(const Common::String &line) { - sscanf(line.c_str(), "%*[^(](%c,%c,%c%c,%u)", &_world, &_room, &_node, &_view, &_offset); -} - -bool ActionChangeLocation::execute(ZVision *engine) { - // We can't directly call ScriptManager::ChangeLocationIntern() because doing so clears all the Puzzles, and thus would corrupt the current puzzle checking - engine->getScriptManager()->changeLocation(_world, _room, _node, _view, _offset); - // Tell the puzzle system to stop checking any more puzzles - return false; -} - - -////////////////////////////////////////////////////////////////////////////// -// ActionCrossfade -////////////////////////////////////////////////////////////////////////////// - -ActionCrossfade::ActionCrossfade(const Common::String &line) { - sscanf(line.c_str(), - "%*[^(](%u %u %u %u %u %u %u)", - &_keyOne, &_keyTwo, &_oneStartVolume, &_twoStartVolume, &_oneEndVolume, &_twoEndVolume, &_timeInMillis); -} - -bool ActionCrossfade::execute(ZVision *engine) { - // TODO: Implement - return true; -} - - -////////////////////////////////////////////////////////////////////////////// -// ActionDisableControl -////////////////////////////////////////////////////////////////////////////// - -ActionDisableControl::ActionDisableControl(const Common::String &line) { - sscanf(line.c_str(), "%*[^(](%u)", &_key); -} - -bool ActionDisableControl::execute(ZVision *engine) { - debug("Disabling control %u", _key); - - ScriptManager *scriptManager = engine->getScriptManager(); - scriptManager->setStateFlags(_key, scriptManager->getStateFlags(_key) | StateFlags::DISABLED); - - return true; -} - - -////////////////////////////////////////////////////////////////////////////// -// ActionEnableControl -////////////////////////////////////////////////////////////////////////////// - -ActionEnableControl::ActionEnableControl(const Common::String &line) { - sscanf(line.c_str(), "%*[^(](%u)", &_key); -} - -bool ActionEnableControl::execute(ZVision *engine) { - debug("Enabling control %u", _key); - - ScriptManager *scriptManager = engine->getScriptManager(); - scriptManager->setStateFlags(_key, scriptManager->getStateFlags(_key) & ~StateFlags::DISABLED); - - return true; -} - - -////////////////////////////////////////////////////////////////////////////// -// ActionMusic -////////////////////////////////////////////////////////////////////////////// - -ActionMusic::ActionMusic(const Common::String &line) : _volume(255) { - uint type; - char fileNameBuffer[25]; - uint loop; - uint volume = 255; - - sscanf(line.c_str(), "%*[^:]:%*[^:]:%u(%u %25s %u %u)", &_key, &type, fileNameBuffer, &loop, &volume); - - // type 4 are midi sound effect files - if (type == 4) { - _soundType = Audio::Mixer::kSFXSoundType; - _fileName = Common::String::format("midi/%s/%u.wav", fileNameBuffer, loop); - _loop = false; - } else { - // TODO: See what the other types are so we can specify the correct Mixer::SoundType. In the meantime use kPlainSoundType - _soundType = Audio::Mixer::kPlainSoundType; - _fileName = Common::String(fileNameBuffer); - _loop = loop == 1 ? true : false; - } - - // Volume is optional. If it doesn't appear, assume full volume - if (volume != 255) { - // Volume in the script files is mapped to [0, 100], but the ScummVM mixer uses [0, 255] - _volume = volume * 255 / 100; - } -} - -bool ActionMusic::execute(ZVision *engine) { - Audio::RewindableAudioStream *audioStream; - - if (_fileName.contains(".wav")) { - Common::File *file = new Common::File(); - if (file->open(_fileName)) { - audioStream = Audio::makeWAVStream(file, DisposeAfterUse::YES); - } else { - warning("Unable to open %s", _fileName.c_str()); - return false; - } - } else { - audioStream = makeRawZorkStream(_fileName, engine); - } - - if (_loop) { - Audio::LoopingAudioStream *loopingAudioStream = new Audio::LoopingAudioStream(audioStream, 0, DisposeAfterUse::YES); - engine->_mixer->playStream(_soundType, 0, loopingAudioStream, -1, _volume); - } else { - engine->_mixer->playStream(_soundType, 0, audioStream, -1, _volume); - } - - return true; -} - - -////////////////////////////////////////////////////////////////////////////// -// ActionPreloadAnimation -////////////////////////////////////////////////////////////////////////////// - -ActionPreloadAnimation::ActionPreloadAnimation(const Common::String &line) { - char fileName[25]; - - // The two %*u are always 0 and dont seem to have a use - sscanf(line.c_str(), "%*[^:]:%*[^:]:%u(%25s %*u %*u %u %u)", &_key, fileName, &_mask, &_framerate); - - _fileName = Common::String(fileName); -} - -bool ActionPreloadAnimation::execute(ZVision *engine) { - // TODO: We ignore the mask and framerate atm. Mask refers to a key color used for binary alpha. We assume the framerate is the default framerate embedded in the videos - - // TODO: Check if the Control already exists - - // Create the control, but disable it until PlayPreload is called - ScriptManager *scriptManager = engine->getScriptManager(); - scriptManager->addControl(new AnimationControl(engine, _key, _fileName)); - scriptManager->setStateFlags(_key, scriptManager->getStateFlags(_key) | StateFlags::DISABLED); - - return true; -} - - -////////////////////////////////////////////////////////////////////////////// -// ActionPlayAnimation -////////////////////////////////////////////////////////////////////////////// - -ActionPlayAnimation::ActionPlayAnimation(const Common::String &line) { - char fileName[25]; - - // The two %*u are always 0 and dont seem to have a use - sscanf(line.c_str(), - "%*[^:]:%*[^:]:%u(%25s %u %u %u %u %u %u %u %*u %*u %u %u)", - &_key, fileName, &_x, &_y, &_width, &_height, &_start, &_end, &_loopCount, &_mask, &_framerate); - - _fileName = Common::String(fileName); -} - -bool ActionPlayAnimation::execute(ZVision *engine) { - // TODO: Implement - return true; -} - - -////////////////////////////////////////////////////////////////////////////// -// ActionPlayPreloadAnimation -////////////////////////////////////////////////////////////////////////////// - -ActionPlayPreloadAnimation::ActionPlayPreloadAnimation(const Common::String &line) { - sscanf(line.c_str(), - "%*[^:]:%*[^:]:%u(%u %u %u %u %u %u %u %u)", - &_animationKey, &_controlKey, &_x1, &_y1, &_x2, &_y2, &_startFrame, &_endFrame, &_loopCount); -} - -bool ActionPlayPreloadAnimation::execute(ZVision *engine) { - // Find the control - AnimationControl *control = (AnimationControl *)engine->getScriptManager()->getControl(_controlKey); - - // Set the needed values within the control - control->setAnimationKey(_animationKey); - control->setLoopCount(_loopCount); - control->setXPos(_x1); - control->setYPost(_y1); - - // Enable the control. ScriptManager will take care of the rest - control->enable(); - - return true; -} - - -////////////////////////////////////////////////////////////////////////////// -// ActionQuit -////////////////////////////////////////////////////////////////////////////// - -bool ActionQuit::execute(ZVision *engine) { - engine->quitGame(); - - return true; -} - - -////////////////////////////////////////////////////////////////////////////// -// ActionRandom -////////////////////////////////////////////////////////////////////////////// - -ActionRandom::ActionRandom(const Common::String &line) { - sscanf(line.c_str(), "%*[^:]:%*[^:]:%u, %u)", &_key, &_max); -} - -bool ActionRandom::execute(ZVision *engine) { - uint randNumber = engine->getRandomSource()->getRandomNumber(_max); - engine->getScriptManager()->setStateValue(_key, randNumber); - return true; -} - - -////////////////////////////////////////////////////////////////////////////// -// ActionSetPartialScreen -////////////////////////////////////////////////////////////////////////////// - -ActionSetPartialScreen::ActionSetPartialScreen(const Common::String &line) { - char fileName[25]; - uint color; - - sscanf(line.c_str(), "%*[^(](%u %u %25s %*u %u)", &_x, &_y, fileName, &color); - - _fileName = Common::String(fileName); - - if (color > 0xFFFF) { - warning("Background color for ActionSetPartialScreen is bigger than a uint16"); - } - _backgroundColor = color; -} - -bool ActionSetPartialScreen::execute(ZVision *engine) { - RenderManager *renderManager = engine->getRenderManager(); - - if (_backgroundColor > 0) { - renderManager->clearWorkingWindowTo555Color(_backgroundColor); - } - renderManager->renderImageToScreen(_fileName, _x, _y); - - return true; -} - - -////////////////////////////////////////////////////////////////////////////// -// ActionSetScreen -////////////////////////////////////////////////////////////////////////////// - -ActionSetScreen::ActionSetScreen(const Common::String &line) { - char fileName[25]; - sscanf(line.c_str(), "%*[^(](%25[^)])", fileName); - - _fileName = Common::String(fileName); -} - -bool ActionSetScreen::execute(ZVision *engine) { - engine->getRenderManager()->setBackgroundImage(_fileName); - - return true; -} - - -////////////////////////////////////////////////////////////////////////////// -// ActionStreamVideo -////////////////////////////////////////////////////////////////////////////// - -ActionStreamVideo::ActionStreamVideo(const Common::String &line) { - char fileName[25]; - uint skippable; - - sscanf(line.c_str(), "%*[^(](%25s %u %u %u %u %u %u)", fileName, &_x1, &_y1, &_x2, &_y2, &_flags, &skippable); - - _fileName = Common::String(fileName); - _skippable = (skippable == 0) ? false : true; -} - -bool ActionStreamVideo::execute(ZVision *engine) { - ZorkAVIDecoder decoder; - if (!decoder.loadFile(_fileName)) { - return true; - } - - Common::Rect destRect; - if ((_flags & DIFFERENT_DIMENSIONS) == DIFFERENT_DIMENSIONS) { - destRect = Common::Rect(_x1, _y1, _x2, _y2); - } - - engine->playVideo(decoder, destRect, _skippable); - return true; -} - - -////////////////////////////////////////////////////////////////////////////// -// ActionTimer -////////////////////////////////////////////////////////////////////////////// - -ActionTimer::ActionTimer(const Common::String &line) { - sscanf(line.c_str(), "%*[^:]:%*[^:]:%u(%u)", &_key, &_time); -} - -bool ActionTimer::execute(ZVision *engine) { - engine->getScriptManager()->addControl(new TimerNode(engine, _key, _time)); - return true; -} - -} // End of namespace ZVision diff --git a/engines/zvision/actions.h b/engines/zvision/actions.h deleted file mode 100644 index afa3e3a2ac..0000000000 --- a/engines/zvision/actions.h +++ /dev/null @@ -1,346 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef ZVISION_ACTIONS_H -#define ZVISION_ACTIONS_H - -#include "common/str.h" - -#include "audio/mixer.h" - - -namespace ZVision { - -// Forward declaration of ZVision. This file is included before ZVision is declared -class ZVision; - -/** - * The base class that represents any action that a Puzzle can take. - * This class is purely virtual. - */ -class ResultAction { -public: - virtual ~ResultAction() {} - /** - * This is called by the script system whenever a Puzzle's criteria are found to be true. - * It should execute any necessary actions and return a value indicating whether the script - * system should continue to test puzzles. In 99% of cases this will be 'true'. - * - * @param engine A pointer to the base engine so the ResultAction can access all the necessary methods - * @return Should the script system continue to test any remaining puzzles (true) or immediately break and go on to the next frame (false) - */ - virtual bool execute(ZVision *engine) = 0; -}; - - -// The different types of actions -// DEBUG, -// DISABLE_CONTROL, -// DISABLE_VENUS, -// DISPLAY_MESSAGE, -// DISSOLVE, -// DISTORT, -// ENABLE_CONTROL, -// FLUSH_MOUSE_EVENTS, -// INVENTORY, -// KILL, -// MENU_BAR_ENABLE, -// MUSIC, -// PAN_TRACK, -// PLAY_PRELOAD, -// PREFERENCES, -// QUIT, -// RANDOM, -// REGION, -// RESTORE_GAME, -// ROTATE_TO, -// SAVE_GAME, -// SET_PARTIAL_SCREEN, -// SET_SCREEN, -// SET_VENUS, -// STOP, -// STREAM_VIDEO, -// SYNC_SOUND, -// TTY_TEXT, -// UNIVERSE_MUSIC, - -class ActionAdd : public ResultAction { -public: - ActionAdd(const Common::String &line); - bool execute(ZVision *engine); - -private: - uint32 _key; - uint _value; -}; - -class ActionAssign : public ResultAction { -public: - ActionAssign(const Common::String &line); - bool execute(ZVision *engine); - -private: - uint32 _key; - uint _value; -}; - -class ActionAttenuate : public ResultAction { -public: - ActionAttenuate(const Common::String &line); - bool execute(ZVision *engine); - -private: - uint32 _key; - int _attenuation; -}; - -class ActionChangeLocation : public ResultAction { -public: - ActionChangeLocation(const Common::String &line); - bool execute(ZVision *engine); - -private: - char _world; - char _room; - char _node; - char _view; - uint32 _offset; -}; - -class ActionCrossfade : public ResultAction { -public: - ActionCrossfade(const Common::String &line); - bool execute(ZVision *engine); - -private: - uint32 _keyOne; - uint32 _keyTwo; - uint _oneStartVolume; - uint _twoStartVolume; - uint _oneEndVolume; - uint _twoEndVolume; - uint _timeInMillis; -}; - -class ActionDebug : public ResultAction { -public: - ActionDebug(const Common::String &line); - bool execute(ZVision *engine); - -private: -}; - -class ActionDelayRender : public ResultAction { -public: - ActionDelayRender(const Common::String &line); - bool execute(ZVision *engine); - -private: - // TODO: Check if this should actually be frames or if it should be milliseconds/seconds - uint32 framesToDelay; -}; - -class ActionDisableControl : public ResultAction { -public: - ActionDisableControl(const Common::String &line); - bool execute(ZVision *engine); - -private: - uint32 _key; -}; - -class ActionDisableVenus : public ResultAction { -public: - ActionDisableVenus(const Common::String &line); - bool execute(ZVision *engine); - -private: -}; - -class ActionDisplayMessage : public ResultAction { -public: - ActionDisplayMessage(const Common::String &line); - bool execute(ZVision *engine); - -private: -}; - -class ActionDissolve : public ResultAction { -public: - ActionDissolve(); - bool execute(ZVision *engine); -}; - -class ActionDistort : public ResultAction { -public: - ActionDistort(const Common::String &line); - bool execute(ZVision *engine); - -private: -}; - -class ActionEnableControl : public ResultAction { -public: - ActionEnableControl(const Common::String &line); - bool execute(ZVision *engine); - -private: - uint32 _key; -}; - -class ActionMusic : public ResultAction { -public: - ActionMusic(const Common::String &line); - bool execute(ZVision *engine); - -private: - uint32 _key; - Audio::Mixer::SoundType _soundType; - Common::String _fileName; - bool _loop; - byte _volume; -}; - -class ActionPlayAnimation : public ResultAction { -public: - ActionPlayAnimation(const Common::String &line); - bool execute(ZVision *engine); - -private: - uint32 _key; - Common::String _fileName; - uint32 _x; - uint32 _y; - uint32 _width; - uint32 _height; - uint32 _start; - uint32 _end; - uint _mask; - uint _framerate; - uint _loopCount; -}; - -class ActionPlayPreloadAnimation : public ResultAction { -public: - ActionPlayPreloadAnimation(const Common::String &line); - bool execute(ZVision *engine); - -private: - uint32 _animationKey; - uint32 _controlKey; - uint32 _x1; - uint32 _y1; - uint32 _x2; - uint32 _y2; - uint _startFrame; - uint _endFrame; - uint _loopCount; -}; - -class ActionPreloadAnimation : public ResultAction { -public: - ActionPreloadAnimation(const Common::String &line); - bool execute(ZVision *engine); - -private: - uint32 _key; - Common::String _fileName; - uint _mask; - uint _framerate; -}; - -class ActionQuit : public ResultAction { -public: - ActionQuit() {} - bool execute(ZVision *engine); -}; - -// TODO: See if this exists in ZGI. It doesn't in ZNem -class ActionUnloadAnimation : public ResultAction { -public: - ActionUnloadAnimation(const Common::String &line); - bool execute(ZVision *engine); -}; - -class ActionRandom : public ResultAction { -public: - ActionRandom(const Common::String &line); - bool execute(ZVision *engine); - -private: - uint32 _key; - uint _max; -}; - -class ActionSetPartialScreen : public ResultAction { -public: - ActionSetPartialScreen(const Common::String &line); - bool execute(ZVision *engine); - -private: - uint _x; - uint _y; - Common::String _fileName; - uint16 _backgroundColor; -}; - -class ActionSetScreen : public ResultAction { -public: - ActionSetScreen(const Common::String &line); - bool execute(ZVision *engine); - -private: - Common::String _fileName; -}; - -class ActionStreamVideo : public ResultAction { -public: - ActionStreamVideo(const Common::String &line); - bool execute(ZVision *engine); - -private: - enum { - DIFFERENT_DIMENSIONS = 0x1 // 0x1 flags that the destRect dimensions are different from the original video dimensions - }; - - Common::String _fileName; - uint _x1; - uint _y1; - uint _x2; - uint _y2; - uint _flags; - bool _skippable; -}; - -class ActionTimer : public ResultAction { -public: - ActionTimer(const Common::String &line); - bool execute(ZVision *engine); - -private: - uint32 _key; - uint _time; -}; - -} // End of namespace ZVision - -#endif diff --git a/engines/zvision/animation/rlf_animation.cpp b/engines/zvision/animation/rlf_animation.cpp new file mode 100644 index 0000000000..5f1d41daae --- /dev/null +++ b/engines/zvision/animation/rlf_animation.cpp @@ -0,0 +1,331 @@ +/* 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 "zvision/rlf_animation.h" + +#include "common/str.h" +#include "common/file.h" +#include "common/textconsole.h" +#include "common/debug.h" +#include "common/endian.h" + +#include "graphics/colormasks.h" + + +namespace ZVision { + +RlfAnimation::RlfAnimation(const Common::String &fileName, bool stream) + : _stream(stream), + _lastFrameRead(0), + _frameCount(0), + _width(0), + _height(0), + _frameTime(0), + _frames(0), + _currentFrame(-1), + _frameBufferByteSize(0) { + if (!_file.open(fileName)) { + warning("RLF animation file %s could not be opened", fileName.c_str()); + return; + } + + if (!readHeader()) { + warning("%s is not a RLF animation file. Wrong magic number", fileName.c_str()); + return; + } + + _currentFrameBuffer.create(_width, _height, Graphics::createPixelFormat<565>()); + _frameBufferByteSize = _width * _height * sizeof(uint16); + + if (!stream) { + _frames = new Frame[_frameCount]; + + // Read in each frame + for (uint i = 0; i < _frameCount; ++i) { + _frames[i] = readNextFrame(); + } + } +} + +RlfAnimation::~RlfAnimation() { + for (uint i = 0; i < _frameCount; ++i) { + delete[] _frames[i].encodedData; + } + delete[] _frames; + _currentFrameBuffer.free(); +} + +bool RlfAnimation::readHeader() { + if (_file.readUint32BE() != MKTAG('F', 'E', 'L', 'R')) { + return false; + } + + // Read the header + _file.readUint32LE(); // Size1 + _file.readUint32LE(); // Unknown1 + _file.readUint32LE(); // Unknown2 + _frameCount = _file.readUint32LE(); // Frame count + + // Since we don't need any of the data, we can just seek right to the + // entries we need rather than read in all the individual entries. + _file.seek(136, SEEK_CUR); + + //// Read CIN header + //_file.readUint32BE(); // Magic number FNIC + //_file.readUint32LE(); // Size2 + //_file.readUint32LE(); // Unknown3 + //_file.readUint32LE(); // Unknown4 + //_file.readUint32LE(); // Unknown5 + //_file.seek(0x18, SEEK_CUR); // VRLE + //_file.readUint32LE(); // LRVD + //_file.readUint32LE(); // Unknown6 + //_file.seek(0x18, SEEK_CUR); // HRLE + //_file.readUint32LE(); // ELHD + //_file.readUint32LE(); // Unknown7 + //_file.seek(0x18, SEEK_CUR); // HKEY + //_file.readUint32LE(); // ELRH + + //// Read MIN info header + //_file.readUint32BE(); // Magic number FNIM + //_file.readUint32LE(); // Size3 + //_file.readUint32LE(); // OEDV + //_file.readUint32LE(); // Unknown8 + //_file.readUint32LE(); // Unknown9 + //_file.readUint32LE(); // Unknown10 + _width = _file.readUint32LE(); // Width + _height = _file.readUint32LE(); // Height + + // Read time header + _file.readUint32BE(); // Magic number EMIT + _file.readUint32LE(); // Size4 + _file.readUint32LE(); // Unknown11 + _frameTime = _file.readUint32LE() / 10; // Frame time in microseconds + + return true; +} + +RlfAnimation::Frame RlfAnimation::readNextFrame() { + RlfAnimation::Frame frame; + + _file.readUint32BE(); // Magic number MARF + uint32 size = _file.readUint32LE(); // Size + _file.readUint32LE(); // Unknown1 + _file.readUint32LE(); // Unknown2 + uint32 type = _file.readUint32BE(); // Either ELHD or ELRH + uint32 headerSize = _file.readUint32LE(); // Offset from the beginning of this frame to the frame data. Should always be 28 + _file.readUint32LE(); // Unknown3 + + frame.encodedSize = size - headerSize; + frame.encodedData = new int8[frame.encodedSize]; + _file.read(frame.encodedData, frame.encodedSize); + + if (type == MKTAG('E', 'L', 'H', 'D')) { + frame.type = Masked; + } else if (type == MKTAG('E', 'L', 'R', 'H')) { + frame.type = Simple; + _completeFrames.push_back(_lastFrameRead); + } else { + warning("Frame %u doesn't have type that can be decoded", _lastFrameRead); + } + + _lastFrameRead++; + return frame; +} + +void RlfAnimation::seekToFrame(int frameNumber) { + assert(!_stream); + assert(frameNumber < (int)_frameCount || frameNumber >= -1); + + if (frameNumber == -1) { + _currentFrame = -1; + return; + } + + int closestFrame = _currentFrame; + int distance = (int)frameNumber - _currentFrame; + for (uint i = 0; i < _completeFrames.size(); ++i) { + int newDistance = (int)frameNumber - (int)(_completeFrames[i]); + if (newDistance > 0 && (closestFrame == -1 || newDistance < distance)) { + closestFrame = _completeFrames[i]; + distance = newDistance; + } + } + + for (int i = closestFrame; i <= frameNumber; ++i) { + applyFrameToCurrent(i); + } + + _currentFrame = frameNumber; +} + +const Graphics::Surface *RlfAnimation::getFrameData(uint frameNumber) { + assert(!_stream); + assert(frameNumber < _frameCount); + + // Since this method is so expensive, first check to see if we can use + // getNextFrame() it's cheap. + if ((int)frameNumber == _currentFrame) { + return &_currentFrameBuffer; + } else if (_currentFrame + 1 == (int)frameNumber) { + return getNextFrame(); + } + + seekToFrame(frameNumber); + return &_currentFrameBuffer; +} + +const Graphics::Surface *RlfAnimation::getNextFrame() { + assert(_currentFrame + 1 < (int)_frameCount); + + if (_stream) { + applyFrameToCurrent(readNextFrame()); + } else { + applyFrameToCurrent(_currentFrame + 1); + } + + _currentFrame++; + return &_currentFrameBuffer; +} + +void RlfAnimation::applyFrameToCurrent(uint frameNumber) { + if (_frames[frameNumber].type == Masked) { + decodeMaskedRunLengthEncoding(_frames[frameNumber].encodedData, (int8 *)_currentFrameBuffer.getPixels(), _frames[frameNumber].encodedSize, _frameBufferByteSize); + } else if (_frames[frameNumber].type == Simple) { + decodeSimpleRunLengthEncoding(_frames[frameNumber].encodedData, (int8 *)_currentFrameBuffer.getPixels(), _frames[frameNumber].encodedSize, _frameBufferByteSize); + } +} + +void RlfAnimation::applyFrameToCurrent(const RlfAnimation::Frame &frame) { + if (frame.type == Masked) { + decodeMaskedRunLengthEncoding(frame.encodedData, (int8 *)_currentFrameBuffer.getPixels(), frame.encodedSize, _frameBufferByteSize); + } else if (frame.type == Simple) { + decodeSimpleRunLengthEncoding(frame.encodedData, (int8 *)_currentFrameBuffer.getPixels(), frame.encodedSize, _frameBufferByteSize); + } +} + +void RlfAnimation::decodeMaskedRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const { + uint32 sourceOffset = 0; + uint32 destOffset = 0; + + while (sourceOffset < sourceSize) { + int8 numberOfSamples = source[sourceOffset]; + sourceOffset++; + + // If numberOfSamples is negative, the next abs(numberOfSamples) samples should + // be copied directly from source to dest + if (numberOfSamples < 0) { + numberOfSamples = ABS(numberOfSamples); + + while (numberOfSamples > 0) { + if (sourceOffset + 1 >= sourceSize) { + return; + } else if (destOffset + 1 >= destSize) { + debug(2, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize); + return; + } + + byte r, g, b; + Graphics::colorToRGB >(READ_LE_UINT16(source + sourceOffset), r, g, b); + uint16 destColor = Graphics::RGBToColor >(r, g, b); + WRITE_UINT16(dest + destOffset, destColor); + + sourceOffset += 2; + destOffset += 2; + numberOfSamples--; + } + + // If numberOfSamples is >= 0, move destOffset forward ((numberOfSamples * 2) + 2) + // This function assumes the dest buffer has been memset with 0's. + } else { + if (sourceOffset + 1 >= sourceSize) { + return; + } else if (destOffset + 1 >= destSize) { + debug(2, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize); + return; + } + + destOffset += (numberOfSamples * 2) + 2; + } + } +} + +void RlfAnimation::decodeSimpleRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const { + uint32 sourceOffset = 0; + uint32 destOffset = 0; + + while (sourceOffset < sourceSize) { + int8 numberOfSamples = source[sourceOffset]; + sourceOffset++; + + // If numberOfSamples is negative, the next abs(numberOfSamples) samples should + // be copied directly from source to dest + if (numberOfSamples < 0) { + numberOfSamples = ABS(numberOfSamples); + + while (numberOfSamples > 0) { + if (sourceOffset + 1 >= sourceSize) { + return; + } else if (destOffset + 1 >= destSize) { + debug(2, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize); + return; + } + + byte r, g, b; + Graphics::colorToRGB >(READ_LE_UINT16(source + sourceOffset), r, g, b); + uint16 destColor = Graphics::RGBToColor >(r, g, b); + WRITE_UINT16(dest + destOffset, destColor); + + sourceOffset += 2; + destOffset += 2; + numberOfSamples--; + } + + // If numberOfSamples is >= 0, copy one sample from source to the + // next (numberOfSamples + 2) dest spots + } else { + if (sourceOffset + 1 >= sourceSize) { + return; + } + + byte r, g, b; + Graphics::colorToRGB >(READ_LE_UINT16(source + sourceOffset), r, g, b); + uint16 sampleColor = Graphics::RGBToColor >(r, g, b); + sourceOffset += 2; + + numberOfSamples += 2; + while (numberOfSamples > 0) { + if (destOffset + 1 >= destSize) { + debug(2, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize); + return; + } + + WRITE_UINT16(dest + destOffset, sampleColor); + destOffset += 2; + numberOfSamples--; + } + } + } +} + +} // End of namespace ZVision diff --git a/engines/zvision/animation/rlf_animation.h b/engines/zvision/animation/rlf_animation.h new file mode 100644 index 0000000000..fe5b0d68b4 --- /dev/null +++ b/engines/zvision/animation/rlf_animation.h @@ -0,0 +1,163 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ZVISION_RLF_ANIMATION_H +#define ZVISION_RLF_ANIMATION_H + +#include "common/file.h" + +#include "graphics/surface.h" + + +namespace Common { +class String; +} + +namespace ZVision { + +class RlfAnimation { +public: + RlfAnimation(const Common::String &fileName, bool stream = true); + ~RlfAnimation(); + +private: + enum EncodingType { + Masked, + Simple + }; + + struct Frame { + EncodingType type; + int8 *encodedData; + uint32 encodedSize; + }; + +private: + Common::File _file; + bool _stream; + uint _lastFrameRead; + + uint _frameCount; + uint _width; + uint _height; + uint32 _frameTime; // In milliseconds + Frame *_frames; + Common::Array _completeFrames; + + int _currentFrame; + Graphics::Surface _currentFrameBuffer; + uint32 _frameBufferByteSize; + +public: + uint frameCount() { return _frameCount; } + uint width() { return _width; } + uint height() { return _height; } + uint32 frameTime() { return _frameTime; } + + /** + * Seeks to the frameNumber and updates the internal Surface with + * the new frame data. If frameNumber == -1, it only sets _currentFrame, + * the internal Surface is unchanged. This function requires _stream = false + * + * @param frameNumber The frame number to seek to + */ + void seekToFrame(int frameNumber); + + /** + * Returns the pixel data of the frame specified. It will try to use + * getNextFrame() if possible. If not, it uses seekToFrame() to + * update the internal Surface and then returns a pointer to it. + * This function requires _stream = false + * + * @param frameNumber The frame number to get data for + * @return A pointer to the pixel data. Do NOT delete this. + */ + const Graphics::Surface *getFrameData(uint frameNumber); + /** + * Returns the pixel data of the next frame. It is up to the user to + * check if the next frame is valid before calling this. + * IE. Use endOfAnimation() + * + * @return A pointer to the pixel data. Do NOT delete this. + */ + const Graphics::Surface *getNextFrame(); + + /** + * @return Is the currentFrame is the last frame in the animation? + */ + bool endOfAnimation() { return _currentFrame == (int)_frameCount - 1; } + +private: + /** + * Reads in the header of the RLF file + * + * @return Will return false if the header magic number is wrong + */ + bool readHeader(); + /** + * Reads the next frame from the RLF file, stores the data in + * a Frame object, then returns the object + * + * @return A Frame object representing the frame data + */ + Frame readNextFrame(); + + /** + * Applies the frame corresponding to frameNumber on top of _currentFrameBuffer. + * This function requires _stream = false so it can look up the Frame object + * referenced by frameNumber. + * + * @param frameNumber The frame number to apply to _currentFrameBuffer + */ + void applyFrameToCurrent(uint frameNumber); + /** + * Applies the data from a Frame object on top of a _currentFrameBuffer. + * + * @param frame A Frame object to apply to _currentFrameBuffer + */ + void applyFrameToCurrent(const RlfAnimation::Frame &frame); + + /** + * Decode frame data that uses masked run length encoding. This is the encoding + * used by P-frames. + * + * @param source The source pixel data + * @param dest The destination buffer + * @param sourceSize The size of the source pixel data + * @param destSize The size of the destination buffer + */ + void decodeMaskedRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const; + /** + * Decode frame data that uses simple run length encoding. This is the encoding + * used by I-frames. + * + * @param source The source pixel data + * @param dest The destination buffer + * @param sourceSize The size of the source pixel data + * @param destSize The size of the destination buffer + */ + void decodeSimpleRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const; +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/animation_control.cpp b/engines/zvision/animation_control.cpp deleted file mode 100644 index 1143380501..0000000000 --- a/engines/zvision/animation_control.cpp +++ /dev/null @@ -1,263 +0,0 @@ -/* 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 "zvision/animation_control.h" - -#include "zvision/zvision.h" -#include "zvision/render_manager.h" -#include "zvision/script_manager.h" -#include "zvision/rlf_animation.h" -#include "zvision/zork_avi_decoder.h" - -#include "video/video_decoder.h" - -#include "graphics/surface.h" - - -namespace ZVision { - -AnimationControl::AnimationControl(ZVision *engine, uint32 controlKey, const Common::String &fileName) - : Control(engine, controlKey), - _fileType(RLF), - _loopCount(1), - _currentLoop(0), - _accumulatedTime(0), - _cachedFrame(0), - _cachedFrameNeedsDeletion(false) { - if (fileName.hasSuffix(".rlf")) { - _fileType = RLF; - _animation.rlf = new RlfAnimation(fileName, false); - } else if (fileName.hasSuffix(".avi")) { - _fileType = AVI; - _animation.avi = new ZorkAVIDecoder(); - _animation.avi->loadFile(fileName); - } else { - warning("Unrecognized animation file type: %s", fileName.c_str()); - } - - _cachedFrame = new Graphics::Surface(); -} - -AnimationControl::~AnimationControl() { - if (_fileType == RLF) { - delete _animation.rlf; - } else if (_fileType == AVI) { - delete _animation.avi; - } - - _cachedFrame->free(); - delete _cachedFrame; -} - -bool AnimationControl::process(uint32 deltaTimeInMillis) { - if (!_enabled) { - return false; - } - - bool finished = false; - - if (_fileType == RLF) { - _accumulatedTime += deltaTimeInMillis; - - uint32 frameTime = _animation.rlf->frameTime(); - if (_accumulatedTime >= frameTime) { - while (_accumulatedTime >= frameTime) { - _accumulatedTime -= frameTime; - - // Make sure the frame is inside the working window - // If it's not, then just return - - RenderManager *renderManager = _engine->getRenderManager(); - Common::Point workingWindowPoint = renderManager->imageSpaceToWorkingWindowSpace(Common::Point(_x, _y)); - Common::Rect subRect(workingWindowPoint.x, workingWindowPoint.y, workingWindowPoint.x + _animation.rlf->width(), workingWindowPoint.y + _animation.rlf->height()); - - // If the clip returns false, it means the animation is outside the working window - if (!renderManager->clipRectToWorkingWindow(subRect)) { - return false; - } - - const Graphics::Surface *frame = _animation.rlf->getNextFrame(); - - // Animation frames for PANORAMAs are transposed, so un-transpose them - RenderTable::RenderState state = renderManager->getRenderTable()->getRenderState(); - if (state == RenderTable::PANORAMA) { - Graphics::Surface *tranposedFrame = RenderManager::tranposeSurface(frame); - - renderManager->copyRectToWorkingWindow((uint16 *)tranposedFrame->getBasePtr(tranposedFrame->w - subRect.width(), tranposedFrame->h - subRect.height()), subRect.left, subRect.top, _animation.rlf->width(), subRect.width(), subRect.height()); - - // If the background can move, we need to cache the last frame so it can be rendered during background movement - if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { - if (_cachedFrameNeedsDeletion) { - _cachedFrame->free(); - delete _cachedFrame; - _cachedFrameNeedsDeletion = false; - } - _cachedFrame = tranposedFrame; - _cachedFrameNeedsDeletion = true; - } else { - // Cleanup - tranposedFrame->free(); - delete tranposedFrame; - } - } else { - renderManager->copyRectToWorkingWindow((const uint16 *)frame->getBasePtr(frame->w - subRect.width(), frame->h - subRect.height()), subRect.left, subRect.top, _animation.rlf->width(), subRect.width(), subRect.height()); - - // If the background can move, we need to cache the last frame so it can be rendered during background movement - if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { - if (_cachedFrameNeedsDeletion) { - _cachedFrame->free(); - delete _cachedFrame; - _cachedFrameNeedsDeletion = false; - } - _cachedFrame->copyFrom(*frame); - } - } - - // Check if we should continue looping - if (_animation.rlf->endOfAnimation()) { - _animation.rlf->seekToFrame(-1); - if (_loopCount > 0) { - _currentLoop++; - if (_currentLoop >= _loopCount) { - finished = true; - } - } - } - } - } else { - // If the background can move, we have to keep rendering animation frames, otherwise the animation flickers during background movement - RenderManager *renderManager = _engine->getRenderManager(); - RenderTable::RenderState state = renderManager->getRenderTable()->getRenderState(); - - if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { - Common::Point workingWindowPoint = renderManager->imageSpaceToWorkingWindowSpace(Common::Point(_x, _y)); - Common::Rect subRect(workingWindowPoint.x, workingWindowPoint.y, workingWindowPoint.x + _cachedFrame->w, workingWindowPoint.y + _cachedFrame->h); - - // If the clip returns false, it means the animation is outside the working window - if (!renderManager->clipRectToWorkingWindow(subRect)) { - return false; - } - - renderManager->copyRectToWorkingWindow((uint16 *)_cachedFrame->getBasePtr(_cachedFrame->w - subRect.width(), _cachedFrame->h - subRect.height()), subRect.left, subRect.top, _cachedFrame->w, subRect.width(), subRect.height()); - } - } - } else if (_fileType == AVI) { - if (!_animation.avi->isPlaying()) { - _animation.avi->start(); - } - - if (_animation.avi->needsUpdate()) { - const Graphics::Surface *frame = _animation.avi->decodeNextFrame(); - - if (frame) { - // Make sure the frame is inside the working window - // If it's not, then just return - - RenderManager *renderManager = _engine->getRenderManager(); - Common::Point workingWindowPoint = renderManager->imageSpaceToWorkingWindowSpace(Common::Point(_x, _y)); - Common::Rect subRect(workingWindowPoint.x, workingWindowPoint.y, workingWindowPoint.x + frame->w, workingWindowPoint.y + frame->h); - - // If the clip returns false, it means the animation is outside the working window - if (!renderManager->clipRectToWorkingWindow(subRect)) { - return false; - } - - // Animation frames for PANORAMAs are transposed, so un-transpose them - RenderTable::RenderState state = renderManager->getRenderTable()->getRenderState(); - if (state == RenderTable::PANORAMA) { - Graphics::Surface *tranposedFrame = RenderManager::tranposeSurface(frame); - - renderManager->copyRectToWorkingWindow((uint16 *)tranposedFrame->getBasePtr(tranposedFrame->w - subRect.width(), tranposedFrame->h - subRect.height()), subRect.left, subRect.top, frame->w, subRect.width(), subRect.height()); - - // If the background can move, we need to cache the last frame so it can be rendered during background movement - if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { - if (_cachedFrameNeedsDeletion) { - _cachedFrame->free(); - delete _cachedFrame; - _cachedFrameNeedsDeletion = false; - } - _cachedFrame = tranposedFrame; - _cachedFrameNeedsDeletion = true; - } else { - // Cleanup - tranposedFrame->free(); - delete tranposedFrame; - } - } else { - renderManager->copyRectToWorkingWindow((const uint16 *)frame->getBasePtr(frame->w - subRect.width(), frame->h - subRect.height()), subRect.left, subRect.top, frame->w, subRect.width(), subRect.height()); - - // If the background can move, we need to cache the last frame so it can be rendered during background movement - if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { - if (_cachedFrameNeedsDeletion) { - _cachedFrame->free(); - delete _cachedFrame; - _cachedFrameNeedsDeletion = false; - } - _cachedFrame->copyFrom(*frame); - } - } - } else { - // If the background can move, we have to keep rendering animation frames, otherwise the animation flickers during background movement - RenderManager *renderManager = _engine->getRenderManager(); - RenderTable::RenderState state = renderManager->getRenderTable()->getRenderState(); - - if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { - Common::Point workingWindowPoint = renderManager->imageSpaceToWorkingWindowSpace(Common::Point(_x, _y)); - Common::Rect subRect(workingWindowPoint.x, workingWindowPoint.y, workingWindowPoint.x + _cachedFrame->w, workingWindowPoint.y + _cachedFrame->h); - - // If the clip returns false, it means the animation is outside the working window - if (!renderManager->clipRectToWorkingWindow(subRect)) { - return false; - } - - renderManager->copyRectToWorkingWindow((uint16 *)_cachedFrame->getBasePtr(_cachedFrame->w - subRect.width(), _cachedFrame->h - subRect.height()), subRect.left, subRect.top, _cachedFrame->w, subRect.width(), subRect.height()); - } - } - } - - // Check if we should continue looping - if (_animation.avi->endOfVideo()) { - _animation.avi->rewind(); - if (_loopCount > 0) { - _currentLoop++; - if (_currentLoop >= _loopCount) { - _animation.avi->stop(); - finished = true; - } - } - } - } - - // If we're done, set _animation key = 2 (Why 2? I don't know. It's just the value that they used) - // Then disable the control. DON'T delete it. It can be re-used - if (finished) { - _engine->getScriptManager()->setStateValue(_animationKey, 2); - disable(); - _currentLoop = 0; - } - - return false; -} - -} // End of namespace ZVision diff --git a/engines/zvision/animation_control.h b/engines/zvision/animation_control.h deleted file mode 100644 index 2ac3691483..0000000000 --- a/engines/zvision/animation_control.h +++ /dev/null @@ -1,87 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef ZVISION_ANIMATION_CONTROL_H -#define ZVISION_ANIMATION_CONTROL_H - -#include "zvision/control.h" - - -namespace Common { -class String; -} - -namespace Video { -class VideoDecoder; -} - -namespace Graphics { -struct Surface; -} - -namespace ZVision { - -class ZVision; -class RlfAnimation; - -class AnimationControl : public Control { -public: - AnimationControl(ZVision *engine, uint32 controlKey, const Common::String &fileName); - ~AnimationControl(); - -private: - enum FileType { - RLF = 1, - AVI = 2 - }; - -private: - uint32 _animationKey; - - union { - RlfAnimation *rlf; - Video::VideoDecoder *avi; - } _animation; - - FileType _fileType; - uint _loopCount; - int32 _x; - int32 _y; - - uint _accumulatedTime; - uint _currentLoop; - - Graphics::Surface *_cachedFrame; - bool _cachedFrameNeedsDeletion; - -public: - bool process(uint32 deltaTimeInMillis); - - void setAnimationKey(uint32 animationKey) { _animationKey = animationKey; } - void setLoopCount(uint loopCount) { _loopCount = loopCount; } - void setXPos(int32 x) { _x = x; } - void setYPost(int32 y) { _y = y; } -}; - -} // End of namespace ZVision - -#endif diff --git a/engines/zvision/archives/zfs_archive.cpp b/engines/zvision/archives/zfs_archive.cpp new file mode 100644 index 0000000000..24cff27fc4 --- /dev/null +++ b/engines/zvision/archives/zfs_archive.cpp @@ -0,0 +1,157 @@ +/* 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 "zvision/zfs_archive.h" + +#include "common/memstream.h" +#include "common/debug.h" +#include "common/file.h" + +namespace ZVision { + +ZfsArchive::ZfsArchive(const Common::String &fileName) : _fileName(fileName) { + Common::File zfsFile; + + if (!zfsFile.open(_fileName)) { + warning("ZFSArchive::ZFSArchive(): Could not find the archive file"); + return; + } + + readHeaders(&zfsFile); + + debug(1, "ZfsArchive::ZfsArchive(%s): Located %d files", _fileName.c_str(), _entryHeaders.size()); +} + +ZfsArchive::ZfsArchive(const Common::String &fileName, Common::SeekableReadStream *stream) : _fileName(fileName) { + readHeaders(stream); + + debug(1, "ZfsArchive::ZfsArchive(%s): Located %d files", _fileName.c_str(), _entryHeaders.size()); +} + +ZfsArchive::~ZfsArchive() { + debug(1, "ZfsArchive Destructor Called"); + ZfsEntryHeaderMap::iterator it = _entryHeaders.begin(); + for ( ; it != _entryHeaders.end(); ++it) { + delete it->_value; + } +} + +void ZfsArchive::readHeaders(Common::SeekableReadStream *stream) { + // Don't do a straight struct cast since we can't guarantee endianness + _header.magic = stream->readUint32LE(); + _header.unknown1 = stream->readUint32LE(); + _header.maxNameLength = stream->readUint32LE(); + _header.filesPerBlock = stream->readUint32LE(); + _header.fileCount = stream->readUint32LE(); + _header.xorKey[0] = stream->readByte(); + _header.xorKey[1] = stream->readByte(); + _header.xorKey[2] = stream->readByte(); + _header.xorKey[3] = stream->readByte(); + _header.fileSectionOffset = stream->readUint32LE(); + + uint32 nextOffset; + + do { + // Read the offset to the next block + nextOffset = stream->readUint32LE(); + + // Read in each entry header + for (uint32 i = 0; i < _header.filesPerBlock; ++i) { + ZfsEntryHeader entryHeader; + + entryHeader.name = readEntryName(stream); + entryHeader.offset = stream->readUint32LE(); + entryHeader.id = stream->readUint32LE(); + entryHeader.size = stream->readUint32LE(); + entryHeader.time = stream->readUint32LE(); + entryHeader.unknown = stream->readUint32LE(); + + if (entryHeader.size != 0) + _entryHeaders[entryHeader.name] = new ZfsEntryHeader(entryHeader); + } + + // Seek to the next block of headers + stream->seek(nextOffset); + } while (nextOffset != 0); +} + +Common::String ZfsArchive::readEntryName(Common::SeekableReadStream *stream) const { + // Entry Names are at most 16 bytes and are null padded + char buffer[16]; + stream->read(buffer, 16); + + return Common::String(buffer); +} + +bool ZfsArchive::hasFile(const Common::String &name) const { + return _entryHeaders.contains(name); +} + +int ZfsArchive::listMembers(Common::ArchiveMemberList &list) const { + int matches = 0; + + for (ZfsEntryHeaderMap::const_iterator it = _entryHeaders.begin(); it != _entryHeaders.end(); ++it) { + list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(it->_value->name, this))); + matches++; + } + + return matches; +} + +const Common::ArchiveMemberPtr ZfsArchive::getMember(const Common::String &name) const { + if (!_entryHeaders.contains(name)) + return Common::ArchiveMemberPtr(); + + return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this)); +} + +Common::SeekableReadStream *ZfsArchive::createReadStreamForMember(const Common::String &name) const { + if (!_entryHeaders.contains(name)) { + return 0; + } + + ZfsEntryHeader *entryHeader = _entryHeaders[name]; + + Common::File zfsArchive; + zfsArchive.open(_fileName); + zfsArchive.seek(entryHeader->offset); + + // This *HAS* to be malloc (not new[]) because MemoryReadStream uses free() to free the memory + byte* buffer = (byte *)malloc(entryHeader->size); + zfsArchive.read(buffer, entryHeader->size); + // Decrypt the data in place + if (_header.xorKey != 0) + unXor(buffer, entryHeader->size, _header.xorKey); + + return new Common::MemoryReadStream(buffer, entryHeader->size, DisposeAfterUse::YES); +} + +void ZfsArchive::unXor(byte *buffer, uint32 length, const byte *xorKey) const { + for (uint32 i = 0; i < length; ++i) + buffer[i] ^= xorKey[i % 4]; +} + +} // End of namespace ZVision + + diff --git a/engines/zvision/archives/zfs_archive.h b/engines/zvision/archives/zfs_archive.h new file mode 100644 index 0000000000..d7b45e4b47 --- /dev/null +++ b/engines/zvision/archives/zfs_archive.h @@ -0,0 +1,126 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ZVISION_ZFS_ARCHIVE_H +#define ZVISION_ZFS_ARCHIVE_H + +#include "common/archive.h" +#include "common/hashmap.h" +#include "common/hash-str.h" + + +namespace Common { +class String; +} + +namespace ZVision { + +struct ZfsHeader { + uint32 magic; + uint32 unknown1; + uint32 maxNameLength; + uint32 filesPerBlock; + uint32 fileCount; + byte xorKey[4]; + uint32 fileSectionOffset; +}; + +struct ZfsEntryHeader { + Common::String name; + uint32 offset; + uint32 id; + uint32 size; + uint32 time; + uint32 unknown; +}; + +typedef Common::HashMap ZfsEntryHeaderMap; + +class ZfsArchive : public Common::Archive { +public: + ZfsArchive(const Common::String &fileName); + ZfsArchive(const Common::String &fileName, Common::SeekableReadStream *stream); + ~ZfsArchive(); + + /** + * Check if a member with the given name is present in the Archive. + * Patterns are not allowed, as this is meant to be a quick File::exists() + * replacement. + */ + bool hasFile(const Common::String &fileName) const; + + /** + * Add all members of the Archive to list. + * Must only append to list, and not remove elements from it. + * + * @return The number of names added to list + */ + int listMembers(Common::ArchiveMemberList &list) const; + + /** + * Returns a ArchiveMember representation of the given file. + */ + const Common::ArchiveMemberPtr getMember(const Common::String &name) const; + + /** + * Create a stream bound to a member with the specified name in the + * archive. If no member with this name exists, 0 is returned. + * + * @return The newly created input stream + */ + Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const; + +private: + const Common::String _fileName; + ZfsHeader _header; + ZfsEntryHeaderMap _entryHeaders; + + /** + * Parses the zfs file into file entry headers that can be used later + * to get the entry data. + * + * @param stream The contents of the zfs file + */ + void readHeaders(Common::SeekableReadStream *stream); + + /** + * Entry names are contained within a 16 byte block. This reads the block + * and converts it the name to a Common::String + * + * @param stream The zfs file stream + * @return The entry file name + */ + Common::String readEntryName(Common::SeekableReadStream *stream) const; + + /** + * ZFS file entries can be encrypted using XOR encoding. This method + * decodes the buffer in place using the supplied xorKey. + * + * @param buffer The data to decode + * @param length Length of buffer + */ + void unXor(byte *buffer, uint32 length, const byte *xorKey) const; +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/clock.cpp b/engines/zvision/clock.cpp deleted file mode 100644 index c8ee717a33..0000000000 --- a/engines/zvision/clock.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* 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 "zvision/clock.h" - -#include "common/system.h" - - -namespace ZVision { - -Clock::Clock(OSystem *system) - : _system(system), - _lastTime(0), - _deltaTime(0), - _pausedTime(0), - _paused(false) { -} - -void Clock::update() { - uint32 currentTime = _system->getMillis(); - - _deltaTime = (currentTime - _lastTime); - if (_paused) { - _deltaTime -= (currentTime - _pausedTime); - } - - if (_deltaTime < 0) { - _deltaTime = 0; - } - - _lastTime = currentTime; -} - -void Clock::start() { - if (_paused) { - _lastTime = _system->getMillis(); - _paused = false; - } -} - -void Clock::stop() { - if (!_paused) { - _pausedTime = _system->getMillis(); - _paused = true; - } -} - -} // End of namespace ZVision diff --git a/engines/zvision/clock.h b/engines/zvision/clock.h deleted file mode 100644 index 3939ba1612..0000000000 --- a/engines/zvision/clock.h +++ /dev/null @@ -1,78 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * - */ - -#ifndef ZVISION_CLOCK_H -#define ZVISION_CLOCK_H - -#include "common/types.h" - -class OSystem; - -namespace ZVision { - -/* Class for handling frame to frame deltaTime while keeping track of time pauses/un-pauses */ -class Clock { -public: - Clock(OSystem *system); - -private: - OSystem *_system; - uint32 _lastTime; - int32 _deltaTime; - uint32 _pausedTime; - bool _paused; - -public: - /** - * Updates _deltaTime with the difference between the current time and - * when the last update() was called. - */ - void update(); - /** - * Get the delta time since the last frame. (The time between update() calls) - * - * @return Delta time since the last frame (in milliseconds) - */ - uint32 getDeltaTime() const { return _deltaTime; } - /** - * Get the time from the program starting to the last update() call - * - * @return Time from program start to last update() call (in milliseconds) - */ - uint32 getLastMeasuredTime() { return _lastTime; } - - /** - * Pause the clock. Any future delta times will take this pause into account. - * Has no effect if the clock is already paused. - */ - void start(); - /** - * Un-pause the clock. - * Has no effect if the clock is already un-paused. - */ - void stop(); -}; - -} // End of namespace ZVision - -#endif diff --git a/engines/zvision/console.cpp b/engines/zvision/console.cpp deleted file mode 100644 index a095d3fa6a..0000000000 --- a/engines/zvision/console.cpp +++ /dev/null @@ -1,218 +0,0 @@ -/* 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 "zvision/console.h" - -#include "zvision/zvision.h" -#include "zvision/script_manager.h" -#include "zvision/render_manager.h" -#include "zvision/string_manager.h" -#include "zvision/zork_avi_decoder.h" -#include "zvision/zork_raw.h" -#include "zvision/utility.h" -#include "zvision/cursor.h" - -#include "common/system.h" -#include "common/file.h" -#include "common/bufferedstream.h" - -#include "gui/debugger.h" - -#include "audio/mixer.h" - - -namespace ZVision { - -Console::Console(ZVision *engine) : GUI::Debugger(), _engine(engine) { - DCmd_Register("loadimage", WRAP_METHOD(Console, cmdLoadImage)); - DCmd_Register("loadvideo", WRAP_METHOD(Console, cmdLoadVideo)); - DCmd_Register("loadsound", WRAP_METHOD(Console, cmdLoadSound)); - DCmd_Register("raw2wav", WRAP_METHOD(Console, cmdRawToWav)); - DCmd_Register("setrenderstate", WRAP_METHOD(Console, cmdSetRenderState)); - DCmd_Register("generaterendertable", WRAP_METHOD(Console, cmdGenerateRenderTable)); - DCmd_Register("setpanoramafov", WRAP_METHOD(Console, cmdSetPanoramaFoV)); - DCmd_Register("setpanoramascale", WRAP_METHOD(Console, cmdSetPanoramaScale)); - DCmd_Register("changelocation", WRAP_METHOD(Console, cmdChangeLocation)); - DCmd_Register("dumpfile", WRAP_METHOD(Console, cmdDumpFile)); - DCmd_Register("parseallscrfiles", WRAP_METHOD(Console, cmdParseAllScrFiles)); - DCmd_Register("rendertext", WRAP_METHOD(Console, cmdRenderText)); -} - -bool Console::cmdLoadImage(int argc, const char **argv) { - if (argc == 4) - _engine->getRenderManager()->renderImageToScreen(argv[1], atoi(argv[2]), atoi(argv[3])); - else { - DebugPrintf("Use loadimage to load an image to the screen\n"); - return true; - } - - return true; -} - -bool Console::cmdLoadVideo(int argc, const char **argv) { - if (argc != 2) { - DebugPrintf("Use loadvideo to load a video to the screen\n"); - return true; - } - - ZorkAVIDecoder videoDecoder; - if (videoDecoder.loadFile(argv[1])) { - _engine->playVideo(videoDecoder); - } - - return true; -} - -bool Console::cmdLoadSound(int argc, const char **argv) { - if (!Common::File::exists(argv[1])) { - DebugPrintf("File does not exist\n"); - return true; - } - - if (argc == 2) { - Audio::AudioStream *soundStream = makeRawZorkStream(argv[1], _engine); - Audio::SoundHandle handle; - _engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, soundStream, -1, 100, 0, DisposeAfterUse::YES, false, false); - - } else if (argc == 4) { - int isStereo = atoi(argv[3]); - - Common::File *file = new Common::File(); - file->open(argv[1]); - - Audio::AudioStream *soundStream = makeRawZorkStream(file, atoi(argv[2]), isStereo == 0 ? false : true); - Audio::SoundHandle handle; - _engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, soundStream, -1, 100, 0, DisposeAfterUse::YES, false, false); - } else { - DebugPrintf("Use loadsound [ ] to load a sound\n"); - return true; - } - - return true; -} - -bool Console::cmdRawToWav(int argc, const char **argv) { - if (argc != 3) { - DebugPrintf("Use raw2wav to dump a .RAW file to .WAV\n"); - return true; - } - - convertRawToWav(argv[1], _engine, argv[2]); - return true; -} - -bool Console::cmdSetRenderState(int argc, const char **argv) { - if (argc != 2) { - DebugPrintf("Use setrenderstate to change the current render state\n"); - return true; - } - - Common::String renderState(argv[1]); - - if (renderState.matchString("panorama", true)) - _engine->getRenderManager()->getRenderTable()->setRenderState(RenderTable::PANORAMA); - else if (renderState.matchString("tilt", true)) - _engine->getRenderManager()->getRenderTable()->setRenderState(RenderTable::TILT); - else if (renderState.matchString("flat", true)) - _engine->getRenderManager()->getRenderTable()->setRenderState(RenderTable::FLAT); - else - DebugPrintf("Use setrenderstate to change the current render state\n"); - - return true; -} - -bool Console::cmdGenerateRenderTable(int argc, const char **argv) { - _engine->getRenderManager()->getRenderTable()->generateRenderTable(); - - return true; -} - -bool Console::cmdSetPanoramaFoV(int argc, const char **argv) { - if (argc != 2) { - DebugPrintf("Use setpanoramafov to change the current panorama field of view\n"); - return true; - } - - _engine->getRenderManager()->getRenderTable()->setPanoramaFoV(atof(argv[1])); - - return true; -} - -bool Console::cmdSetPanoramaScale(int argc, const char **argv) { - if (argc != 2) { - DebugPrintf("Use setpanoramascale to change the current panorama scale\n"); - return true; - } - - _engine->getRenderManager()->getRenderTable()->setPanoramaScale(atof(argv[1])); - - return true; -} - -bool Console::cmdChangeLocation(int argc, const char **argv) { - if (argc != 6) { - DebugPrintf("Use changelocation to change your location\n"); - return true; - } - - _engine->getScriptManager()->changeLocation(*(argv[1]), *(argv[2]), *(argv[3]), *(argv[4]), atoi(argv[5])); - - return true; -} - -bool Console::cmdDumpFile(int argc, const char **argv) { - if (argc != 2) { - DebugPrintf("Use dumpfile to dump a file\n"); - return true; - } - - writeFileContentsToFile(argv[1], argv[1]); - - return true; -} - -bool Console::cmdParseAllScrFiles(int argc, const char **argv) { - Common::ArchiveMemberList list; - SearchMan.listMatchingMembers(list, "*.scr"); - - for (Common::ArchiveMemberList::iterator iter = list.begin(); iter != list.end(); ++iter) { - _engine->getScriptManager()->parseScrFile((*iter)->getName()); - } - - return true; -} - -bool Console::cmdRenderText(int argc, const char **argv) { - if (argc != 7) { - DebugPrintf("Use rendertext <1 or 0: wrap> to render text\n"); - return true; - } - - StringManager::TextStyle style = _engine->getStringManager()->getTextStyle(atoi(argv[2])); - _engine->getRenderManager()->renderTextToWorkingWindow(333, Common::String(argv[1]), style.font, atoi(argv[3]), atoi(argv[4]), style.color, atoi(argv[5]), -1, Graphics::kTextAlignLeft, atoi(argv[6]) == 0 ? false : true); - - return true; -} - -} // End of namespace ZVision diff --git a/engines/zvision/console.h b/engines/zvision/console.h deleted file mode 100644 index 0ca1b8cc70..0000000000 --- a/engines/zvision/console.h +++ /dev/null @@ -1,55 +0,0 @@ -/* ScummVM - Graphic Adventure Engine -* -* ScummVM is the legal property of its developers, whose names -* are too numerous to list here. Please refer to the COPYRIGHT -* file distributed with this source distribution. -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* as published by the Free Software Foundation; either version 2 -* of the License, or (at your option) any later version. - -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. - -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -* -*/ - -#ifndef ZVISION_CONSOLE_H -#define ZVISION_CONSOLE_H - -#include "gui/debugger.h" - -namespace ZVision { - -class ZVision; - -class Console : public GUI::Debugger { -public: - Console(ZVision *engine); - virtual ~Console() {} - -private: - ZVision *_engine; - - bool cmdLoadImage(int argc, const char **argv); - bool cmdLoadVideo(int argc, const char **argv); - bool cmdLoadSound(int argc, const char **argv); - bool cmdRawToWav(int argc, const char **argv); - bool cmdSetRenderState(int argc, const char **argv); - bool cmdGenerateRenderTable(int argc, const char **argv); - bool cmdSetPanoramaFoV(int argc, const char **argv); - bool cmdSetPanoramaScale(int argc, const char **argv); - bool cmdChangeLocation(int argc, const char **argv); - bool cmdDumpFile(int argc, const char **argv); - bool cmdParseAllScrFiles(int argc, const char **argv); - bool cmdRenderText(int argc, const char **argv); -}; - -} // End of namespace ZVision -#endif diff --git a/engines/zvision/control.cpp b/engines/zvision/control.cpp deleted file mode 100644 index bcbdabc143..0000000000 --- a/engines/zvision/control.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* 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 "zvision/control.h" - -#include "zvision/zvision.h" -#include "zvision/render_manager.h" -#include "zvision/utility.h" - -#include "common/stream.h" - - -namespace ZVision { - -void Control::enable() { - if (!_enabled) { - _enabled = true; - return; - } - - debug("Control %u is already enabled", _key); -} - -void Control::disable() { - if (_enabled) { - _enabled = false; - return; - } - - debug("Control %u is already disabled", _key); -} - -void Control::parseFlatControl(ZVision *engine) { - engine->getRenderManager()->getRenderTable()->setRenderState(RenderTable::FLAT); -} - -void Control::parsePanoramaControl(ZVision *engine, Common::SeekableReadStream &stream) { - RenderTable *renderTable = engine->getRenderManager()->getRenderTable(); - renderTable->setRenderState(RenderTable::PANORAMA); - - // Loop until we find the closing brace - Common::String line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - - while (!stream.eos() && !line.contains('}')) { - if (line.matchString("angle*", true)) { - float fov; - sscanf(line.c_str(), "angle(%f)", &fov); - renderTable->setPanoramaFoV(fov); - } else if (line.matchString("linscale*", true)) { - float scale; - sscanf(line.c_str(), "linscale(%f)", &scale); - renderTable->setPanoramaScale(scale); - } else if (line.matchString("reversepana*", true)) { - uint reverse; - sscanf(line.c_str(), "reversepana(%u)", &reverse); - if (reverse == 1) { - renderTable->setPanoramaReverse(true); - } - } else if (line.matchString("zeropoint*", true)) { - // TODO: Implement - } - - line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - } - - renderTable->generateRenderTable(); -} - -void Control::parseTiltControl(ZVision *engine, Common::SeekableReadStream &stream) { - RenderTable *renderTable = engine->getRenderManager()->getRenderTable(); - renderTable->setRenderState(RenderTable::TILT); - - // Loop until we find the closing brace - Common::String line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - - while (!stream.eos() && !line.contains('}')) { - if (line.matchString("angle*", true)) { - float fov; - sscanf(line.c_str(), "angle(%f)", &fov); - renderTable->setTiltFoV(fov); - } else if (line.matchString("linscale*", true)) { - float scale; - sscanf(line.c_str(), "linscale(%f)", &scale); - renderTable->setTiltScale(scale); - } else if (line.matchString("reversepana*", true)) { - uint reverse; - sscanf(line.c_str(), "reversepana(%u)", &reverse); - if (reverse == 1) { - renderTable->setTiltReverse(true); - } - } - - line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - } - - renderTable->generateRenderTable(); -} - -} // End of namespace ZVision diff --git a/engines/zvision/control.h b/engines/zvision/control.h deleted file mode 100644 index 770c540a12..0000000000 --- a/engines/zvision/control.h +++ /dev/null @@ -1,146 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef ZVISION_CONTROL_H -#define ZVISION_CONTROL_H - -#include "common/keyboard.h" - - -namespace Common { -class SeekableReadStream; -struct Point; -class WriteStream; -} - -namespace ZVision { - -class ZVision; - -class Control { -public: - Control() : _engine(0), _key(0), _enabled(false) {} - Control(ZVision *engine, uint32 key) : _engine(engine), _key(key), _enabled(false) {} - virtual ~Control() {} - - uint32 getKey() { return _key; } - - virtual void enable(); - virtual void disable(); - virtual void focus() {} - virtual void unfocus() {} - /** - * Called when LeftMouse is pushed. Default is NOP. - * - * @param screenSpacePos The position of the mouse in screen space - * @param backgroundImageSpacePos The position of the mouse in background image space - */ - virtual void onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {} - /** - * Called when LeftMouse is lifted. Default is NOP. - * - * @param screenSpacePos The position of the mouse in screen space - * @param backgroundImageSpacePos The position of the mouse in background image space - */ - virtual void onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {} - /** - * Called on every MouseMove. Default is NOP. - * - * @param screenSpacePos The position of the mouse in screen space - * @param backgroundImageSpacePos The position of the mouse in background image space - * @return Was the cursor changed? - */ - virtual bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { return false; } - /** - * Called when a key is pressed. Default is NOP. - * - * @param keycode The key that was pressed - */ - virtual void onKeyDown(Common::KeyState keyState) {} - /** - * Called when a key is released. Default is NOP. - * - * @param keycode The key that was pressed - */ - virtual void onKeyUp(Common::KeyState keyState) {} - /** - * Processes the node given the deltaTime since last frame. Default is NOP. - * - * @param deltaTimeInMillis The number of milliseconds that have passed since last frame - * @return If true, the node can be deleted after process() finishes - */ - virtual bool process(uint32 deltaTimeInMillis) { return false; } - /** - * Serialize a Control for save game use. This should only be used if a Control needs - * to save values that would be different from initialization. AKA a TimerNode needs to - * store the amount of time left on the timer. Any Controls overriding this *MUST* write - * their key as the first data outputted. The default implementation is NOP. - * - * NOTE: If this method is overridden, you MUST also override deserialize() - * and needsSerialization() - * - * @param stream Stream to write any needed data to - */ - virtual void serialize(Common::WriteStream *stream) {} - /** - * De-serialize data from a save game stream. This should only be implemented if the - * Control also implements serialize(). The calling method assumes the size of the - * data read from the stream exactly equals that written in serialize(). The default - * implementation is NOP. - * - * NOTE: If this method is overridden, you MUST also override serialize() - * and needsSerialization() - * - * @param stream Save game file stream - */ - virtual void deserialize(Common::SeekableReadStream *stream) {} - /** - * If a Control overrides serialize() and deserialize(), this should return true - * - * @return Does the Control need save game serialization? - */ - virtual inline bool needsSerialization() { return false; } - -protected: - ZVision * _engine; - uint32 _key; - bool _enabled; - -// Static member functions -public: - static void parseFlatControl(ZVision *engine); - static void parsePanoramaControl(ZVision *engine, Common::SeekableReadStream &stream); - static void parseTiltControl(ZVision *engine, Common::SeekableReadStream &stream); -}; - -// TODO: Implement InputControl -// TODO: Implement SaveControl -// TODO: Implement SlotControl -// TODO: Implement SafeControl -// TODO: Implement FistControl -// TODO: Implement HotMovieControl -// TODO: Implement PaintControl -// TODO: Implement TilterControl - -} // End of namespace ZVision - -#endif diff --git a/engines/zvision/core/console.cpp b/engines/zvision/core/console.cpp new file mode 100644 index 0000000000..a095d3fa6a --- /dev/null +++ b/engines/zvision/core/console.cpp @@ -0,0 +1,218 @@ +/* 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 "zvision/console.h" + +#include "zvision/zvision.h" +#include "zvision/script_manager.h" +#include "zvision/render_manager.h" +#include "zvision/string_manager.h" +#include "zvision/zork_avi_decoder.h" +#include "zvision/zork_raw.h" +#include "zvision/utility.h" +#include "zvision/cursor.h" + +#include "common/system.h" +#include "common/file.h" +#include "common/bufferedstream.h" + +#include "gui/debugger.h" + +#include "audio/mixer.h" + + +namespace ZVision { + +Console::Console(ZVision *engine) : GUI::Debugger(), _engine(engine) { + DCmd_Register("loadimage", WRAP_METHOD(Console, cmdLoadImage)); + DCmd_Register("loadvideo", WRAP_METHOD(Console, cmdLoadVideo)); + DCmd_Register("loadsound", WRAP_METHOD(Console, cmdLoadSound)); + DCmd_Register("raw2wav", WRAP_METHOD(Console, cmdRawToWav)); + DCmd_Register("setrenderstate", WRAP_METHOD(Console, cmdSetRenderState)); + DCmd_Register("generaterendertable", WRAP_METHOD(Console, cmdGenerateRenderTable)); + DCmd_Register("setpanoramafov", WRAP_METHOD(Console, cmdSetPanoramaFoV)); + DCmd_Register("setpanoramascale", WRAP_METHOD(Console, cmdSetPanoramaScale)); + DCmd_Register("changelocation", WRAP_METHOD(Console, cmdChangeLocation)); + DCmd_Register("dumpfile", WRAP_METHOD(Console, cmdDumpFile)); + DCmd_Register("parseallscrfiles", WRAP_METHOD(Console, cmdParseAllScrFiles)); + DCmd_Register("rendertext", WRAP_METHOD(Console, cmdRenderText)); +} + +bool Console::cmdLoadImage(int argc, const char **argv) { + if (argc == 4) + _engine->getRenderManager()->renderImageToScreen(argv[1], atoi(argv[2]), atoi(argv[3])); + else { + DebugPrintf("Use loadimage to load an image to the screen\n"); + return true; + } + + return true; +} + +bool Console::cmdLoadVideo(int argc, const char **argv) { + if (argc != 2) { + DebugPrintf("Use loadvideo to load a video to the screen\n"); + return true; + } + + ZorkAVIDecoder videoDecoder; + if (videoDecoder.loadFile(argv[1])) { + _engine->playVideo(videoDecoder); + } + + return true; +} + +bool Console::cmdLoadSound(int argc, const char **argv) { + if (!Common::File::exists(argv[1])) { + DebugPrintf("File does not exist\n"); + return true; + } + + if (argc == 2) { + Audio::AudioStream *soundStream = makeRawZorkStream(argv[1], _engine); + Audio::SoundHandle handle; + _engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, soundStream, -1, 100, 0, DisposeAfterUse::YES, false, false); + + } else if (argc == 4) { + int isStereo = atoi(argv[3]); + + Common::File *file = new Common::File(); + file->open(argv[1]); + + Audio::AudioStream *soundStream = makeRawZorkStream(file, atoi(argv[2]), isStereo == 0 ? false : true); + Audio::SoundHandle handle; + _engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, soundStream, -1, 100, 0, DisposeAfterUse::YES, false, false); + } else { + DebugPrintf("Use loadsound [ ] to load a sound\n"); + return true; + } + + return true; +} + +bool Console::cmdRawToWav(int argc, const char **argv) { + if (argc != 3) { + DebugPrintf("Use raw2wav to dump a .RAW file to .WAV\n"); + return true; + } + + convertRawToWav(argv[1], _engine, argv[2]); + return true; +} + +bool Console::cmdSetRenderState(int argc, const char **argv) { + if (argc != 2) { + DebugPrintf("Use setrenderstate to change the current render state\n"); + return true; + } + + Common::String renderState(argv[1]); + + if (renderState.matchString("panorama", true)) + _engine->getRenderManager()->getRenderTable()->setRenderState(RenderTable::PANORAMA); + else if (renderState.matchString("tilt", true)) + _engine->getRenderManager()->getRenderTable()->setRenderState(RenderTable::TILT); + else if (renderState.matchString("flat", true)) + _engine->getRenderManager()->getRenderTable()->setRenderState(RenderTable::FLAT); + else + DebugPrintf("Use setrenderstate to change the current render state\n"); + + return true; +} + +bool Console::cmdGenerateRenderTable(int argc, const char **argv) { + _engine->getRenderManager()->getRenderTable()->generateRenderTable(); + + return true; +} + +bool Console::cmdSetPanoramaFoV(int argc, const char **argv) { + if (argc != 2) { + DebugPrintf("Use setpanoramafov to change the current panorama field of view\n"); + return true; + } + + _engine->getRenderManager()->getRenderTable()->setPanoramaFoV(atof(argv[1])); + + return true; +} + +bool Console::cmdSetPanoramaScale(int argc, const char **argv) { + if (argc != 2) { + DebugPrintf("Use setpanoramascale to change the current panorama scale\n"); + return true; + } + + _engine->getRenderManager()->getRenderTable()->setPanoramaScale(atof(argv[1])); + + return true; +} + +bool Console::cmdChangeLocation(int argc, const char **argv) { + if (argc != 6) { + DebugPrintf("Use changelocation to change your location\n"); + return true; + } + + _engine->getScriptManager()->changeLocation(*(argv[1]), *(argv[2]), *(argv[3]), *(argv[4]), atoi(argv[5])); + + return true; +} + +bool Console::cmdDumpFile(int argc, const char **argv) { + if (argc != 2) { + DebugPrintf("Use dumpfile to dump a file\n"); + return true; + } + + writeFileContentsToFile(argv[1], argv[1]); + + return true; +} + +bool Console::cmdParseAllScrFiles(int argc, const char **argv) { + Common::ArchiveMemberList list; + SearchMan.listMatchingMembers(list, "*.scr"); + + for (Common::ArchiveMemberList::iterator iter = list.begin(); iter != list.end(); ++iter) { + _engine->getScriptManager()->parseScrFile((*iter)->getName()); + } + + return true; +} + +bool Console::cmdRenderText(int argc, const char **argv) { + if (argc != 7) { + DebugPrintf("Use rendertext <1 or 0: wrap> to render text\n"); + return true; + } + + StringManager::TextStyle style = _engine->getStringManager()->getTextStyle(atoi(argv[2])); + _engine->getRenderManager()->renderTextToWorkingWindow(333, Common::String(argv[1]), style.font, atoi(argv[3]), atoi(argv[4]), style.color, atoi(argv[5]), -1, Graphics::kTextAlignLeft, atoi(argv[6]) == 0 ? false : true); + + return true; +} + +} // End of namespace ZVision diff --git a/engines/zvision/core/console.h b/engines/zvision/core/console.h new file mode 100644 index 0000000000..0ca1b8cc70 --- /dev/null +++ b/engines/zvision/core/console.h @@ -0,0 +1,55 @@ +/* ScummVM - Graphic Adventure Engine +* +* ScummVM is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the COPYRIGHT +* file distributed with this source distribution. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. + +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. + +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +*/ + +#ifndef ZVISION_CONSOLE_H +#define ZVISION_CONSOLE_H + +#include "gui/debugger.h" + +namespace ZVision { + +class ZVision; + +class Console : public GUI::Debugger { +public: + Console(ZVision *engine); + virtual ~Console() {} + +private: + ZVision *_engine; + + bool cmdLoadImage(int argc, const char **argv); + bool cmdLoadVideo(int argc, const char **argv); + bool cmdLoadSound(int argc, const char **argv); + bool cmdRawToWav(int argc, const char **argv); + bool cmdSetRenderState(int argc, const char **argv); + bool cmdGenerateRenderTable(int argc, const char **argv); + bool cmdSetPanoramaFoV(int argc, const char **argv); + bool cmdSetPanoramaScale(int argc, const char **argv); + bool cmdChangeLocation(int argc, const char **argv); + bool cmdDumpFile(int argc, const char **argv); + bool cmdParseAllScrFiles(int argc, const char **argv); + bool cmdRenderText(int argc, const char **argv); +}; + +} // End of namespace ZVision +#endif diff --git a/engines/zvision/core/events.cpp b/engines/zvision/core/events.cpp new file mode 100644 index 0000000000..1103dc3000 --- /dev/null +++ b/engines/zvision/core/events.cpp @@ -0,0 +1,186 @@ +/* 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 "zvision/zvision.h" + +#include "zvision/console.h" +#include "zvision/cursor_manager.h" +#include "zvision/render_manager.h" +#include "zvision/script_manager.h" +#include "zvision/rlf_animation.h" + +#include "common/events.h" +#include "common/system.h" +#include "common/rational.h" + +#include "engines/util.h" + + +namespace ZVision { + +void ZVision::processEvents() { + while (_eventMan->pollEvent(_event)) { + switch (_event.type) { + case Common::EVENT_LBUTTONDOWN: + onMouseDown(_event.mouse); + break; + + case Common::EVENT_LBUTTONUP: + onMouseUp(_event.mouse); + break; + + case Common::EVENT_RBUTTONDOWN: + // TODO: Inventory logic + 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 + _console->attach(); + _console->onFrame(); + } + break; + case Common::KEYCODE_q: + if (_event.kbd.hasFlags(Common::KBD_CTRL)) + quitGame(); + break; + default: + break; + } + + _scriptManager->onKeyDown(_event.kbd); + break; + case Common::EVENT_KEYUP: + _scriptManager->onKeyUp(_event.kbd); + break; + default: + break; + } + } +} + +void ZVision::onMouseDown(const Common::Point &pos) { + _cursorManager->cursorDown(true); + + Common::Point imageCoord(_renderManager->screenSpaceToImageSpace(pos)); + _scriptManager->onMouseDown(pos, imageCoord); +} + +void ZVision::onMouseUp(const Common::Point &pos) { + _cursorManager->cursorDown(false); + + Common::Point imageCoord(_renderManager->screenSpaceToImageSpace(pos)); + _scriptManager->onMouseUp(pos, imageCoord); +} + +void ZVision::onMouseMove(const Common::Point &pos) { + Common::Point imageCoord(_renderManager->screenSpaceToImageSpace(pos)); + + bool cursorWasChanged = _scriptManager->onMouseMove(pos, imageCoord); + + // Graph of the function governing rotation velocity: + // + // |---------------- working window ------------------| + // ^ |---------| + // | | + // +Max velocity | rotation screen edge offset + // | /| + // | / | + // | / | + // | / | + // | / | + // | / | + // | / | + // | / | + // | / | + // Zero velocity |______________________________ ______________________________/_________|__________________________> + // | Position -> | / + // | | / + // | | / + // | | / + // | | / + // | | / + // | | / + // | | / + // | | / + // -Max velocity | |/ + // | + // | + // ^ + + if (_workingWindow.contains(pos)) { + RenderTable::RenderState renderState = _renderManager->getRenderTable()->getRenderState(); + if (renderState == RenderTable::PANORAMA) { + if (pos.x >= _workingWindow.left && pos.x < _workingWindow.left + ROTATION_SCREEN_EDGE_OFFSET) { + // Linear function of distance to the left edge (y = -mx + b) + // We use fixed point math to get better accuracy + Common::Rational velocity = (Common::Rational(MAX_ROTATION_SPEED, ROTATION_SCREEN_EDGE_OFFSET) * (pos.x - _workingWindow.left)) - MAX_ROTATION_SPEED; + _renderManager->setBackgroundVelocity(velocity.toInt()); + _cursorManager->setLeftCursor(); + cursorWasChanged = true; + } else if (pos.x <= _workingWindow.right && pos.x > _workingWindow.right - ROTATION_SCREEN_EDGE_OFFSET) { + // Linear function of distance to the right edge (y = mx) + // We use fixed point math to get better accuracy + Common::Rational velocity = Common::Rational(MAX_ROTATION_SPEED, ROTATION_SCREEN_EDGE_OFFSET) * (pos.x - _workingWindow.right + ROTATION_SCREEN_EDGE_OFFSET); + _renderManager->setBackgroundVelocity(velocity.toInt()); + _cursorManager->setRightCursor(); + cursorWasChanged = true; + } else { + _renderManager->setBackgroundVelocity(0); + } + } else if (renderState == RenderTable::TILT) { + if (pos.y >= _workingWindow.top && pos.y < _workingWindow.top + ROTATION_SCREEN_EDGE_OFFSET) { + // Linear function of distance to top edge + // We use fixed point math to get better accuracy + Common::Rational velocity = (Common::Rational(MAX_ROTATION_SPEED, ROTATION_SCREEN_EDGE_OFFSET) * (pos.y - _workingWindow.top)) - MAX_ROTATION_SPEED; + _renderManager->setBackgroundVelocity(velocity.toInt()); + _cursorManager->setUpCursor(); + cursorWasChanged = true; + } else if (pos.y <= _workingWindow.bottom && pos.y > _workingWindow.bottom - ROTATION_SCREEN_EDGE_OFFSET) { + // Linear function of distance to the bottom edge (y = mx) + // We use fixed point math to get better accuracy + Common::Rational velocity = Common::Rational(MAX_ROTATION_SPEED, ROTATION_SCREEN_EDGE_OFFSET) * (pos.y - _workingWindow.bottom + ROTATION_SCREEN_EDGE_OFFSET); + _renderManager->setBackgroundVelocity(velocity.toInt()); + _cursorManager->setDownCursor(); + cursorWasChanged = true; + } else { + _renderManager->setBackgroundVelocity(0); + } + } + } else { + _renderManager->setBackgroundVelocity(0); + } + + if (!cursorWasChanged) { + _cursorManager->revertToIdle(); + } +} + +} // End of namespace ZVision diff --git a/engines/zvision/core/menu.h b/engines/zvision/core/menu.h new file mode 100644 index 0000000000..affc69abd5 --- /dev/null +++ b/engines/zvision/core/menu.h @@ -0,0 +1,28 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ZVISION_MENU_H +#define ZVISION_MENU_H + +// TODO: Implement MenuHandler + +#endif diff --git a/engines/zvision/core/save_manager.cpp b/engines/zvision/core/save_manager.cpp new file mode 100644 index 0000000000..c3deadd703 --- /dev/null +++ b/engines/zvision/core/save_manager.cpp @@ -0,0 +1,206 @@ +/* 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 "zvision/save_manager.h" + +#include "zvision/zvision.h" +#include "zvision/script_manager.h" +#include "zvision/render_manager.h" + +#include "common/system.h" + +#include "graphics/surface.h" +#include "graphics/thumbnail.h" + +#include "gui/message.h" + + +namespace ZVision { + +const uint32 SaveManager::SAVEGAME_ID = MKTAG('Z', 'E', 'N', 'G'); + +void SaveManager::saveGame(uint slot, const Common::String &saveName) { + // The games only support 20 slots + assert(slot <= 1 && slot <= 20); + + Common::SaveFileManager *saveFileManager = g_system->getSavefileManager(); + Common::OutSaveFile *file = saveFileManager->openForSaving(_engine->generateSaveFileName(slot)); + + // Write out the savegame header + file->writeUint32BE(SAVEGAME_ID); + + // Write version + file->writeByte(SAVE_VERSION); + + // Write savegame name + file->writeString(saveName); + file->writeByte(0); + + // We can't call writeGameSaveData because the save menu is actually + // a room, so writeGameSaveData would save us in the save menu. + // However, an auto save is performed before each room change, so we + // can copy the data from there. We can guarantee that an auto save file will + // exist before this is called because the save menu can only be accessed + // after the first room (the main menu) has loaded. + Common::InSaveFile *autoSaveFile = saveFileManager->openForLoading(_engine->generateAutoSaveFileName()); + + // Skip over the header info + autoSaveFile->readSint32BE(); // SAVEGAME_ID + autoSaveFile->readByte(); // Version + autoSaveFile->seek(5, SEEK_CUR); // The string "auto" with terminating NULL + + // Read the rest to a buffer + uint32 size = autoSaveFile->size() - autoSaveFile->pos(); + byte *buffer = new byte[size]; + autoSaveFile->read(buffer, size); + + // Then write the buffer to the new file + file->write(buffer, size); + + // Cleanup + delete[] buffer; + file->finalize(); + delete file; +} + +void SaveManager::autoSave() { + Common::OutSaveFile *file = g_system->getSavefileManager()->openForSaving(_engine->generateAutoSaveFileName()); + + // Write out the savegame header + file->writeUint32BE(SAVEGAME_ID); + + // Version + file->writeByte(SAVE_VERSION); + + file->writeString("auto"); + file->writeByte(0); + + writeSaveGameData(file); + + // Cleanup + file->finalize(); + delete file; +} + +void SaveManager::writeSaveGameData(Common::OutSaveFile *file) { + // Create a thumbnail and save it + Graphics::saveThumbnail(*file); + + // Write out the save date/time + TimeDate td; + g_system->getTimeAndDate(td); + file->writeSint16LE(td.tm_year + 1900); + file->writeSint16LE(td.tm_mon + 1); + file->writeSint16LE(td.tm_mday); + file->writeSint16LE(td.tm_hour); + file->writeSint16LE(td.tm_min); + + ScriptManager *scriptManager = _engine->getScriptManager(); + // Write out the current location + Location currentLocation = scriptManager->getCurrentLocation(); + file->writeByte(currentLocation.world); + file->writeByte(currentLocation.room); + file->writeByte(currentLocation.node); + file->writeByte(currentLocation.view); + file->writeUint32LE(currentLocation.offset); + + // Write out the current state table values + scriptManager->serializeStateTable(file); + + // Write out any controls needing to save state + scriptManager->serializeControls(file); +} + +Common::Error SaveManager::loadGame(uint slot) { + // The games only support 20 slots + assert(slot <= 1 && slot <= 20); + + Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(_engine->generateSaveFileName(slot)); + if (saveFile == 0) { + return Common::kPathDoesNotExist; + } + + // Read the header + SaveGameHeader header; + if (!readSaveGameHeader(saveFile, header)) { + return Common::kUnknownError; + } + + char world = (char)saveFile->readByte(); + char room = (char)saveFile->readByte(); + char node = (char)saveFile->readByte(); + char view = (char)saveFile->readByte(); + uint32 offset = (char)saveFile->readUint32LE(); + + ScriptManager *scriptManager = _engine->getScriptManager(); + // Update the state table values + scriptManager->deserializeStateTable(saveFile); + + // Load the room + scriptManager->changeLocation(world, room, node, view, offset); + + // Update the controls + scriptManager->deserializeControls(saveFile); + + return Common::kNoError; +} + +bool SaveManager::readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &header) { + if (in->readUint32BE() != SAVEGAME_ID) { + warning("File is not a ZVision save file. Aborting load"); + return false; + } + + // Read in the version + header.version = in->readByte(); + + // Check that the save version isn't newer than this binary + if (header.version > SAVE_VERSION) { + uint tempVersion = header.version; + GUI::MessageDialog dialog(Common::String::format("This save file uses version %u, but this engine only supports up to version %d. You will need an updated version of the engine to use this save file.", tempVersion, SAVE_VERSION), "OK"); + dialog.runModal(); + } + + // Read in the save name + header.saveName.clear(); + char ch; + while ((ch = (char)in->readByte()) != '\0') + header.saveName += ch; + + // Get the thumbnail + header.thumbnail = Graphics::loadThumbnail(*in); + if (!header.thumbnail) + return false; + + // Read in save date/time + header.saveYear = in->readSint16LE(); + header.saveMonth = in->readSint16LE(); + header.saveDay = in->readSint16LE(); + header.saveHour = in->readSint16LE(); + header.saveMinutes = in->readSint16LE(); + + return true; +} + +} // End of namespace ZVision diff --git a/engines/zvision/core/save_manager.h b/engines/zvision/core/save_manager.h new file mode 100644 index 0000000000..b4770e68b2 --- /dev/null +++ b/engines/zvision/core/save_manager.h @@ -0,0 +1,91 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ZVISION_SAVE_MANAGER_H +#define ZVISION_SAVE_MANAGER_H + +#include "common/savefile.h" + +namespace Common { +class String; +} + +namespace Graphics { +struct Surface; +} + +namespace ZVision { + +class ZVision; + +struct SaveGameHeader { + byte version; + Common::String saveName; + Graphics::Surface *thumbnail; + int saveYear, saveMonth, saveDay; + int saveHour, saveMinutes; +}; + +class SaveManager { +public: + SaveManager(ZVision *engine) : _engine(engine) {} + +private: + ZVision *_engine; + static const uint32 SAVEGAME_ID; + + enum { + SAVE_VERSION = 1 + }; + +public: + /** + * Called every room change. Saves the state of the room just before + * we switched rooms. Uses ZVision::generateAutoSaveFileName() to + * create the save file name. + */ + void autoSave(); + /** + * Copies the data from the last auto-save into a new save file. We + * can't use the current state data because the save menu *IS* a room. + * The file is named using ZVision::generateSaveFileName(slot) + * + * @param slot The save slot this save pertains to. Must be [1, 20] + * @param saveName The internal name for this save. This is NOT the name of the actual save file. + */ + void saveGame(uint slot, const Common::String &saveName); + /** + * Loads the state data from the save file that slot references. Uses + * ZVision::generateSaveFileName(slot) to get the save file name. + * + * @param slot The save slot to load. Must be [1, 20] + */ + Common::Error loadGame(uint slot); + +private: + void writeSaveGameData(Common::OutSaveFile *file); + bool readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &header); +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/cursor.cpp b/engines/zvision/cursor.cpp deleted file mode 100644 index 9023d97e0d..0000000000 --- a/engines/zvision/cursor.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* 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 "zvision/cursor.h" - -#include "common/str.h" -#include "common/file.h" - - -namespace ZVision { - -ZorkCursor::ZorkCursor() - : _width(0), - _height(0), - _hotspotX(0), - _hotspotY(0) { -} - -ZorkCursor::ZorkCursor(const Common::String &fileName) - : _width(0), - _height(0), - _hotspotX(0), - _hotspotY(0) { - Common::File file; - if (!file.open(fileName)) - return; - - uint32 magic = file.readUint32BE(); - if (magic != MKTAG('Z', 'C', 'R', '1')) { - warning("%s is not a Zork Cursor file", fileName.c_str()); - return; - } - - _hotspotX = file.readUint16LE(); - _hotspotY = file.readUint16LE(); - _width = file.readUint16LE(); - _height = file.readUint16LE(); - - uint dataSize = _width * _height * sizeof(uint16); - _surface.create(_width, _height, Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)); - uint32 bytesRead = file.read(_surface.getPixels(), dataSize); - assert(bytesRead == dataSize); - - // Convert to RGB 565 - _surface.convertToInPlace(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); -} - -ZorkCursor::ZorkCursor(const ZorkCursor &other) { - _width = other._width; - _height = other._height; - _hotspotX = other._hotspotX; - _hotspotY = other._hotspotY; - - _surface.copyFrom(other._surface); -} - -ZorkCursor &ZorkCursor::operator=(const ZorkCursor &other) { - _width = other._width; - _height = other._height; - _hotspotX = other._hotspotX; - _hotspotY = other._hotspotY; - - _surface.free(); - _surface.copyFrom(other._surface); - - return *this; -} - -ZorkCursor::~ZorkCursor() { - _surface.free(); -} - -} // End of namespace ZVision diff --git a/engines/zvision/cursor.h b/engines/zvision/cursor.h deleted file mode 100644 index 18ac28ce8b..0000000000 --- a/engines/zvision/cursor.h +++ /dev/null @@ -1,66 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef ZVISION_CURSOR_H -#define ZVISION_CURSOR_H - -#include "graphics/surface.h" - - -namespace Common { -class String; -} - -namespace ZVision { - -/** - * Utility class to parse and hold cursor data - * Modeled off Graphics::Cursor - */ -class ZorkCursor { -public: - ZorkCursor(); - ZorkCursor(const Common::String &fileName); - ZorkCursor(const ZorkCursor &other); - ~ZorkCursor(); - -private: - uint16 _width; - uint16 _height; - uint16 _hotspotX; - uint16 _hotspotY; - Graphics::Surface _surface; - -public: - ZorkCursor &operator=(const ZorkCursor &other); - - uint16 getWidth() const { return _width; } - uint16 getHeight() const { return _height; } - uint16 getHotspotX() const { return _hotspotX; } - uint16 getHotspotY() const { return _hotspotY; } - byte getKeyColor() const { return 0; } - const byte *getSurface() const { return (const byte *)_surface.getPixels(); } -}; - -} // End of namespace ZVision - -#endif diff --git a/engines/zvision/cursor_manager.cpp b/engines/zvision/cursor_manager.cpp deleted file mode 100644 index c411013150..0000000000 --- a/engines/zvision/cursor_manager.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* 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 "zvision/cursor_manager.h" - -#include "zvision/zvision.h" - -#include "common/system.h" - -#include "graphics/pixelformat.h" -#include "graphics/cursorman.h" - - -namespace ZVision { - -const char *CursorManager::_cursorNames[NUM_CURSORS] = { "active", "arrow", "backward", "downarrow", "forward", "handpt", "handpu", "hdown", "hleft", - "hright", "hup", "idle", "leftarrow", "rightarrow", "suggest_surround", "suggest_tilt", "turnaround", "zuparrow" }; - -const char *CursorManager::_zgiCursorFileNames[NUM_CURSORS] = { "g0gbc011.zcr", "g0gac001.zcr", "g0gac021.zcr", "g0gac031.zcr", "g0gac041.zcr", "g0gac051.zcr", "g0gac061.zcr", "g0gac071.zcr", "g0gac081.zcr", - "g0gac091.zcr", "g0gac101.zcr", "g0gac011.zcr", "g0gac111.zcr", "g0gac121.zcr", "g0gac131.zcr", "g0gac141.zcr", "g0gac151.zcr", "g0gac161.zcr" }; - -const char *CursorManager::_zNemCursorFileNames[NUM_CURSORS] = { "00act", "arrow", "back", "down", "forw", "handpt", "handpu", "hdown", "hleft", - "hright", "hup", "00idle", "left", "right", "ssurr", "stilt", "turn", "up" }; - - -CursorManager::CursorManager(ZVision *engine, const Graphics::PixelFormat *pixelFormat) - : _engine(engine), - _pixelFormat(pixelFormat), - _cursorIsPushed(false) { - // WARNING: The index IDLE_CURSOR_INDEX is hardcoded. If you change the order of _cursorNames/_zgiCursorFileNames/_zNemCursorFileNames, you HAVE to change the index accordingly - if (_engine->getGameId() == GID_NEMESIS) { - Common::String name(Common::String::format("%sa.zcr", _zNemCursorFileNames[IDLE_CURSOR_INDEX])); - _idleCursor = ZorkCursor(name); - } else if (_engine->getGameId() == GID_GRANDINQUISITOR) { - _idleCursor = ZorkCursor(_zgiCursorFileNames[IDLE_CURSOR_INDEX]); - } -} - -void CursorManager::initialize() { - revertToIdle(); - CursorMan.showMouse(true); -} - -void CursorManager::changeCursor(const Common::String &cursorName) { - changeCursor(cursorName, _cursorIsPushed); -} - -void CursorManager::changeCursor(const Common::String &cursorName, bool pushed) { - if (_currentCursor.equals(cursorName) && _cursorIsPushed == pushed) - return; - - if (_cursorIsPushed != pushed) - _cursorIsPushed = pushed; - - if (cursorName == "idle" && !pushed) { - CursorMan.replaceCursor(_idleCursor.getSurface(), _idleCursor.getWidth(), _idleCursor.getHeight(), _idleCursor.getHotspotX(), _idleCursor.getHotspotY(), _idleCursor.getKeyColor(), false, _pixelFormat); - return; - } - - for (int i = 0; i < NUM_CURSORS; ++i) { - if (_engine->getGameId() == GID_NEMESIS) { - if (cursorName.equals(_cursorNames[i])) { - _currentCursor = cursorName; - - // ZNem uses a/b at the end of the file to signify not pushed/pushed respectively - Common::String pushedFlag = pushed ? "b" : "a"; - Common::String name = Common::String::format("%s%s.zcr", _zNemCursorFileNames[i], pushedFlag.c_str()); - - changeCursor(ZorkCursor(name)); - return; - } - } else if (_engine->getGameId() == GID_GRANDINQUISITOR) { - if (cursorName.equals(_cursorNames[i])) { - _currentCursor = cursorName; - - if (!pushed) { - changeCursor(ZorkCursor(_zgiCursorFileNames[i])); - } else { - // ZGI flips not pushed/pushed between a/c and b/d - // It flips the 4th character of the name - char buffer[25]; - strcpy(buffer, _zgiCursorFileNames[i]); - buffer[3] += 2; - changeCursor(ZorkCursor(buffer)); - } - return; - } - } - } - - // If we get here, something went wrong - warning("No cursor found for identifier %s", cursorName.c_str()); -} - -void CursorManager::changeCursor(const ZorkCursor &cursor) { - CursorMan.replaceCursor(cursor.getSurface(), cursor.getWidth(), cursor.getHeight(), cursor.getHotspotX(), cursor.getHotspotY(), cursor.getKeyColor(), false, _pixelFormat); -} - -void CursorManager::cursorDown(bool pushed) { - if (_cursorIsPushed == pushed) - return; - - _cursorIsPushed = pushed; - changeCursor(_currentCursor, pushed); -} - -void CursorManager::setLeftCursor() { - changeCursor("leftarrow"); -} - -void CursorManager::setRightCursor() { - changeCursor("rightarrow"); -} - -void CursorManager::setUpCursor() { - changeCursor("zuparrow"); -} - -void CursorManager::setDownCursor() { - changeCursor("downarrow"); -} - -void CursorManager::revertToIdle() { - _currentCursor = "idle"; - if (!_cursorIsPushed) - CursorMan.replaceCursor(_idleCursor.getSurface(), _idleCursor.getWidth(), _idleCursor.getHeight(), _idleCursor.getHotspotX(), _idleCursor.getHotspotY(), _idleCursor.getKeyColor(), false, _pixelFormat); - else - changeCursor(_currentCursor, _cursorIsPushed); -} - -} // End of namespace ZVision diff --git a/engines/zvision/cursor_manager.h b/engines/zvision/cursor_manager.h deleted file mode 100644 index 0a369aaf9e..0000000000 --- a/engines/zvision/cursor_manager.h +++ /dev/null @@ -1,114 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef ZVISION_CURSOR_MANAGER_H -#define ZVISION_CURSOR_MANAGER_H - -#include "zvision/cursor.h" - -#include "common/str.h" - - -namespace Graphics { -struct PixelFormat; -} - -namespace ZVision { - -class ZVision; - -/** - * Class to manage cursor changes. The actual changes have to be done - * through CursorMan. Otherwise the cursor will disappear after GMM - * or debug console. - * TODO: Figure out a way to get rid of the extraneous data copying due to having to use CursorMan - */ -class CursorManager { -public: - CursorManager(ZVision *engine, const Graphics::PixelFormat *pixelFormat); - -private: - enum { - NUM_CURSORS = 18, - // WARNING: The index 11 is hardcoded. If you change the order of _cursorNames/_zgiCursorFileNames/_zNemCursorFileNames, you HAVE to change the index accordingly - IDLE_CURSOR_INDEX = 11 - }; - - ZVision *_engine; - const Graphics::PixelFormat *_pixelFormat; - ZorkCursor _idleCursor; - Common::String _currentCursor; - bool _cursorIsPushed; - - static const char *_cursorNames[]; - static const char *_zgiCursorFileNames[]; - static const char *_zNemCursorFileNames[]; - -public: - /** Creates the idle cursor and shows it */ - void initialize(); - - /** - * Parses a cursor name into a cursor file then creates and shows that cursor. - * It will use the current _isCursorPushed state to choose the correct cursor - * - * @param cursorName The name of a cursor. This *HAS* to correspond to one of the entries in _cursorNames[] - */ - void changeCursor(const Common::String &cursorName); - /** - * Parses a cursor name into a cursor file then creates and shows that cursor. - * - * @param cursorName The name of a cursor. This *HAS* to correspond to one of the entries in _cursorNames[] - * @param pushed Should the cursor be pushed (true) or not pushed (false) (Another way to say it: down or up) - */ - void changeCursor(const Common::String &cursorName, bool pushed); - /** - * Change the cursor to a certain push state. If the cursor is already in the specified push state, nothing will happen. - * - * @param pushed Should the cursor be pushed (true) or not pushed (false) (Another way to say it: down or up) - */ - void cursorDown(bool pushed); - - /** Set the cursor to 'Left Arrow'. It will retain the current _isCursorPushed state */ - void setLeftCursor(); - /** Set the cursor to 'Right Arrow'. It will retain the current _isCursorPushed state */ - void setRightCursor(); - /** Set the cursor to 'Up Arrow'. It will retain the current _isCursorPushed state */ - void setUpCursor(); - /** Set the cursor to 'Down Arrow'. It will retain the current _isCursorPushed state */ - void setDownCursor(); - - /** Set the cursor to 'Idle'. It will retain the current _isCursorPushed state */ - void revertToIdle(); - -private: - /** - * Calls CursorMan.replaceCursor() using the data in cursor - * - * @param cursor The cursor to show - */ - void changeCursor(const ZorkCursor &cursor); -}; - -} // End of namespace ZVision - -#endif diff --git a/engines/zvision/cursors/cursor.cpp b/engines/zvision/cursors/cursor.cpp new file mode 100644 index 0000000000..9023d97e0d --- /dev/null +++ b/engines/zvision/cursors/cursor.cpp @@ -0,0 +1,94 @@ +/* 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 "zvision/cursor.h" + +#include "common/str.h" +#include "common/file.h" + + +namespace ZVision { + +ZorkCursor::ZorkCursor() + : _width(0), + _height(0), + _hotspotX(0), + _hotspotY(0) { +} + +ZorkCursor::ZorkCursor(const Common::String &fileName) + : _width(0), + _height(0), + _hotspotX(0), + _hotspotY(0) { + Common::File file; + if (!file.open(fileName)) + return; + + uint32 magic = file.readUint32BE(); + if (magic != MKTAG('Z', 'C', 'R', '1')) { + warning("%s is not a Zork Cursor file", fileName.c_str()); + return; + } + + _hotspotX = file.readUint16LE(); + _hotspotY = file.readUint16LE(); + _width = file.readUint16LE(); + _height = file.readUint16LE(); + + uint dataSize = _width * _height * sizeof(uint16); + _surface.create(_width, _height, Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)); + uint32 bytesRead = file.read(_surface.getPixels(), dataSize); + assert(bytesRead == dataSize); + + // Convert to RGB 565 + _surface.convertToInPlace(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); +} + +ZorkCursor::ZorkCursor(const ZorkCursor &other) { + _width = other._width; + _height = other._height; + _hotspotX = other._hotspotX; + _hotspotY = other._hotspotY; + + _surface.copyFrom(other._surface); +} + +ZorkCursor &ZorkCursor::operator=(const ZorkCursor &other) { + _width = other._width; + _height = other._height; + _hotspotX = other._hotspotX; + _hotspotY = other._hotspotY; + + _surface.free(); + _surface.copyFrom(other._surface); + + return *this; +} + +ZorkCursor::~ZorkCursor() { + _surface.free(); +} + +} // End of namespace ZVision diff --git a/engines/zvision/cursors/cursor.h b/engines/zvision/cursors/cursor.h new file mode 100644 index 0000000000..18ac28ce8b --- /dev/null +++ b/engines/zvision/cursors/cursor.h @@ -0,0 +1,66 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ZVISION_CURSOR_H +#define ZVISION_CURSOR_H + +#include "graphics/surface.h" + + +namespace Common { +class String; +} + +namespace ZVision { + +/** + * Utility class to parse and hold cursor data + * Modeled off Graphics::Cursor + */ +class ZorkCursor { +public: + ZorkCursor(); + ZorkCursor(const Common::String &fileName); + ZorkCursor(const ZorkCursor &other); + ~ZorkCursor(); + +private: + uint16 _width; + uint16 _height; + uint16 _hotspotX; + uint16 _hotspotY; + Graphics::Surface _surface; + +public: + ZorkCursor &operator=(const ZorkCursor &other); + + uint16 getWidth() const { return _width; } + uint16 getHeight() const { return _height; } + uint16 getHotspotX() const { return _hotspotX; } + uint16 getHotspotY() const { return _hotspotY; } + byte getKeyColor() const { return 0; } + const byte *getSurface() const { return (const byte *)_surface.getPixels(); } +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/cursors/cursor_manager.cpp b/engines/zvision/cursors/cursor_manager.cpp new file mode 100644 index 0000000000..c411013150 --- /dev/null +++ b/engines/zvision/cursors/cursor_manager.cpp @@ -0,0 +1,152 @@ +/* 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 "zvision/cursor_manager.h" + +#include "zvision/zvision.h" + +#include "common/system.h" + +#include "graphics/pixelformat.h" +#include "graphics/cursorman.h" + + +namespace ZVision { + +const char *CursorManager::_cursorNames[NUM_CURSORS] = { "active", "arrow", "backward", "downarrow", "forward", "handpt", "handpu", "hdown", "hleft", + "hright", "hup", "idle", "leftarrow", "rightarrow", "suggest_surround", "suggest_tilt", "turnaround", "zuparrow" }; + +const char *CursorManager::_zgiCursorFileNames[NUM_CURSORS] = { "g0gbc011.zcr", "g0gac001.zcr", "g0gac021.zcr", "g0gac031.zcr", "g0gac041.zcr", "g0gac051.zcr", "g0gac061.zcr", "g0gac071.zcr", "g0gac081.zcr", + "g0gac091.zcr", "g0gac101.zcr", "g0gac011.zcr", "g0gac111.zcr", "g0gac121.zcr", "g0gac131.zcr", "g0gac141.zcr", "g0gac151.zcr", "g0gac161.zcr" }; + +const char *CursorManager::_zNemCursorFileNames[NUM_CURSORS] = { "00act", "arrow", "back", "down", "forw", "handpt", "handpu", "hdown", "hleft", + "hright", "hup", "00idle", "left", "right", "ssurr", "stilt", "turn", "up" }; + + +CursorManager::CursorManager(ZVision *engine, const Graphics::PixelFormat *pixelFormat) + : _engine(engine), + _pixelFormat(pixelFormat), + _cursorIsPushed(false) { + // WARNING: The index IDLE_CURSOR_INDEX is hardcoded. If you change the order of _cursorNames/_zgiCursorFileNames/_zNemCursorFileNames, you HAVE to change the index accordingly + if (_engine->getGameId() == GID_NEMESIS) { + Common::String name(Common::String::format("%sa.zcr", _zNemCursorFileNames[IDLE_CURSOR_INDEX])); + _idleCursor = ZorkCursor(name); + } else if (_engine->getGameId() == GID_GRANDINQUISITOR) { + _idleCursor = ZorkCursor(_zgiCursorFileNames[IDLE_CURSOR_INDEX]); + } +} + +void CursorManager::initialize() { + revertToIdle(); + CursorMan.showMouse(true); +} + +void CursorManager::changeCursor(const Common::String &cursorName) { + changeCursor(cursorName, _cursorIsPushed); +} + +void CursorManager::changeCursor(const Common::String &cursorName, bool pushed) { + if (_currentCursor.equals(cursorName) && _cursorIsPushed == pushed) + return; + + if (_cursorIsPushed != pushed) + _cursorIsPushed = pushed; + + if (cursorName == "idle" && !pushed) { + CursorMan.replaceCursor(_idleCursor.getSurface(), _idleCursor.getWidth(), _idleCursor.getHeight(), _idleCursor.getHotspotX(), _idleCursor.getHotspotY(), _idleCursor.getKeyColor(), false, _pixelFormat); + return; + } + + for (int i = 0; i < NUM_CURSORS; ++i) { + if (_engine->getGameId() == GID_NEMESIS) { + if (cursorName.equals(_cursorNames[i])) { + _currentCursor = cursorName; + + // ZNem uses a/b at the end of the file to signify not pushed/pushed respectively + Common::String pushedFlag = pushed ? "b" : "a"; + Common::String name = Common::String::format("%s%s.zcr", _zNemCursorFileNames[i], pushedFlag.c_str()); + + changeCursor(ZorkCursor(name)); + return; + } + } else if (_engine->getGameId() == GID_GRANDINQUISITOR) { + if (cursorName.equals(_cursorNames[i])) { + _currentCursor = cursorName; + + if (!pushed) { + changeCursor(ZorkCursor(_zgiCursorFileNames[i])); + } else { + // ZGI flips not pushed/pushed between a/c and b/d + // It flips the 4th character of the name + char buffer[25]; + strcpy(buffer, _zgiCursorFileNames[i]); + buffer[3] += 2; + changeCursor(ZorkCursor(buffer)); + } + return; + } + } + } + + // If we get here, something went wrong + warning("No cursor found for identifier %s", cursorName.c_str()); +} + +void CursorManager::changeCursor(const ZorkCursor &cursor) { + CursorMan.replaceCursor(cursor.getSurface(), cursor.getWidth(), cursor.getHeight(), cursor.getHotspotX(), cursor.getHotspotY(), cursor.getKeyColor(), false, _pixelFormat); +} + +void CursorManager::cursorDown(bool pushed) { + if (_cursorIsPushed == pushed) + return; + + _cursorIsPushed = pushed; + changeCursor(_currentCursor, pushed); +} + +void CursorManager::setLeftCursor() { + changeCursor("leftarrow"); +} + +void CursorManager::setRightCursor() { + changeCursor("rightarrow"); +} + +void CursorManager::setUpCursor() { + changeCursor("zuparrow"); +} + +void CursorManager::setDownCursor() { + changeCursor("downarrow"); +} + +void CursorManager::revertToIdle() { + _currentCursor = "idle"; + if (!_cursorIsPushed) + CursorMan.replaceCursor(_idleCursor.getSurface(), _idleCursor.getWidth(), _idleCursor.getHeight(), _idleCursor.getHotspotX(), _idleCursor.getHotspotY(), _idleCursor.getKeyColor(), false, _pixelFormat); + else + changeCursor(_currentCursor, _cursorIsPushed); +} + +} // End of namespace ZVision diff --git a/engines/zvision/cursors/cursor_manager.h b/engines/zvision/cursors/cursor_manager.h new file mode 100644 index 0000000000..0a369aaf9e --- /dev/null +++ b/engines/zvision/cursors/cursor_manager.h @@ -0,0 +1,114 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ZVISION_CURSOR_MANAGER_H +#define ZVISION_CURSOR_MANAGER_H + +#include "zvision/cursor.h" + +#include "common/str.h" + + +namespace Graphics { +struct PixelFormat; +} + +namespace ZVision { + +class ZVision; + +/** + * Class to manage cursor changes. The actual changes have to be done + * through CursorMan. Otherwise the cursor will disappear after GMM + * or debug console. + * TODO: Figure out a way to get rid of the extraneous data copying due to having to use CursorMan + */ +class CursorManager { +public: + CursorManager(ZVision *engine, const Graphics::PixelFormat *pixelFormat); + +private: + enum { + NUM_CURSORS = 18, + // WARNING: The index 11 is hardcoded. If you change the order of _cursorNames/_zgiCursorFileNames/_zNemCursorFileNames, you HAVE to change the index accordingly + IDLE_CURSOR_INDEX = 11 + }; + + ZVision *_engine; + const Graphics::PixelFormat *_pixelFormat; + ZorkCursor _idleCursor; + Common::String _currentCursor; + bool _cursorIsPushed; + + static const char *_cursorNames[]; + static const char *_zgiCursorFileNames[]; + static const char *_zNemCursorFileNames[]; + +public: + /** Creates the idle cursor and shows it */ + void initialize(); + + /** + * Parses a cursor name into a cursor file then creates and shows that cursor. + * It will use the current _isCursorPushed state to choose the correct cursor + * + * @param cursorName The name of a cursor. This *HAS* to correspond to one of the entries in _cursorNames[] + */ + void changeCursor(const Common::String &cursorName); + /** + * Parses a cursor name into a cursor file then creates and shows that cursor. + * + * @param cursorName The name of a cursor. This *HAS* to correspond to one of the entries in _cursorNames[] + * @param pushed Should the cursor be pushed (true) or not pushed (false) (Another way to say it: down or up) + */ + void changeCursor(const Common::String &cursorName, bool pushed); + /** + * Change the cursor to a certain push state. If the cursor is already in the specified push state, nothing will happen. + * + * @param pushed Should the cursor be pushed (true) or not pushed (false) (Another way to say it: down or up) + */ + void cursorDown(bool pushed); + + /** Set the cursor to 'Left Arrow'. It will retain the current _isCursorPushed state */ + void setLeftCursor(); + /** Set the cursor to 'Right Arrow'. It will retain the current _isCursorPushed state */ + void setRightCursor(); + /** Set the cursor to 'Up Arrow'. It will retain the current _isCursorPushed state */ + void setUpCursor(); + /** Set the cursor to 'Down Arrow'. It will retain the current _isCursorPushed state */ + void setDownCursor(); + + /** Set the cursor to 'Idle'. It will retain the current _isCursorPushed state */ + void revertToIdle(); + +private: + /** + * Calls CursorMan.replaceCursor() using the data in cursor + * + * @param cursor The cursor to show + */ + void changeCursor(const ZorkCursor &cursor); +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/events.cpp b/engines/zvision/events.cpp deleted file mode 100644 index 1103dc3000..0000000000 --- a/engines/zvision/events.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* 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 "zvision/zvision.h" - -#include "zvision/console.h" -#include "zvision/cursor_manager.h" -#include "zvision/render_manager.h" -#include "zvision/script_manager.h" -#include "zvision/rlf_animation.h" - -#include "common/events.h" -#include "common/system.h" -#include "common/rational.h" - -#include "engines/util.h" - - -namespace ZVision { - -void ZVision::processEvents() { - while (_eventMan->pollEvent(_event)) { - switch (_event.type) { - case Common::EVENT_LBUTTONDOWN: - onMouseDown(_event.mouse); - break; - - case Common::EVENT_LBUTTONUP: - onMouseUp(_event.mouse); - break; - - case Common::EVENT_RBUTTONDOWN: - // TODO: Inventory logic - 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 - _console->attach(); - _console->onFrame(); - } - break; - case Common::KEYCODE_q: - if (_event.kbd.hasFlags(Common::KBD_CTRL)) - quitGame(); - break; - default: - break; - } - - _scriptManager->onKeyDown(_event.kbd); - break; - case Common::EVENT_KEYUP: - _scriptManager->onKeyUp(_event.kbd); - break; - default: - break; - } - } -} - -void ZVision::onMouseDown(const Common::Point &pos) { - _cursorManager->cursorDown(true); - - Common::Point imageCoord(_renderManager->screenSpaceToImageSpace(pos)); - _scriptManager->onMouseDown(pos, imageCoord); -} - -void ZVision::onMouseUp(const Common::Point &pos) { - _cursorManager->cursorDown(false); - - Common::Point imageCoord(_renderManager->screenSpaceToImageSpace(pos)); - _scriptManager->onMouseUp(pos, imageCoord); -} - -void ZVision::onMouseMove(const Common::Point &pos) { - Common::Point imageCoord(_renderManager->screenSpaceToImageSpace(pos)); - - bool cursorWasChanged = _scriptManager->onMouseMove(pos, imageCoord); - - // Graph of the function governing rotation velocity: - // - // |---------------- working window ------------------| - // ^ |---------| - // | | - // +Max velocity | rotation screen edge offset - // | /| - // | / | - // | / | - // | / | - // | / | - // | / | - // | / | - // | / | - // | / | - // Zero velocity |______________________________ ______________________________/_________|__________________________> - // | Position -> | / - // | | / - // | | / - // | | / - // | | / - // | | / - // | | / - // | | / - // | | / - // -Max velocity | |/ - // | - // | - // ^ - - if (_workingWindow.contains(pos)) { - RenderTable::RenderState renderState = _renderManager->getRenderTable()->getRenderState(); - if (renderState == RenderTable::PANORAMA) { - if (pos.x >= _workingWindow.left && pos.x < _workingWindow.left + ROTATION_SCREEN_EDGE_OFFSET) { - // Linear function of distance to the left edge (y = -mx + b) - // We use fixed point math to get better accuracy - Common::Rational velocity = (Common::Rational(MAX_ROTATION_SPEED, ROTATION_SCREEN_EDGE_OFFSET) * (pos.x - _workingWindow.left)) - MAX_ROTATION_SPEED; - _renderManager->setBackgroundVelocity(velocity.toInt()); - _cursorManager->setLeftCursor(); - cursorWasChanged = true; - } else if (pos.x <= _workingWindow.right && pos.x > _workingWindow.right - ROTATION_SCREEN_EDGE_OFFSET) { - // Linear function of distance to the right edge (y = mx) - // We use fixed point math to get better accuracy - Common::Rational velocity = Common::Rational(MAX_ROTATION_SPEED, ROTATION_SCREEN_EDGE_OFFSET) * (pos.x - _workingWindow.right + ROTATION_SCREEN_EDGE_OFFSET); - _renderManager->setBackgroundVelocity(velocity.toInt()); - _cursorManager->setRightCursor(); - cursorWasChanged = true; - } else { - _renderManager->setBackgroundVelocity(0); - } - } else if (renderState == RenderTable::TILT) { - if (pos.y >= _workingWindow.top && pos.y < _workingWindow.top + ROTATION_SCREEN_EDGE_OFFSET) { - // Linear function of distance to top edge - // We use fixed point math to get better accuracy - Common::Rational velocity = (Common::Rational(MAX_ROTATION_SPEED, ROTATION_SCREEN_EDGE_OFFSET) * (pos.y - _workingWindow.top)) - MAX_ROTATION_SPEED; - _renderManager->setBackgroundVelocity(velocity.toInt()); - _cursorManager->setUpCursor(); - cursorWasChanged = true; - } else if (pos.y <= _workingWindow.bottom && pos.y > _workingWindow.bottom - ROTATION_SCREEN_EDGE_OFFSET) { - // Linear function of distance to the bottom edge (y = mx) - // We use fixed point math to get better accuracy - Common::Rational velocity = Common::Rational(MAX_ROTATION_SPEED, ROTATION_SCREEN_EDGE_OFFSET) * (pos.y - _workingWindow.bottom + ROTATION_SCREEN_EDGE_OFFSET); - _renderManager->setBackgroundVelocity(velocity.toInt()); - _cursorManager->setDownCursor(); - cursorWasChanged = true; - } else { - _renderManager->setBackgroundVelocity(0); - } - } - } else { - _renderManager->setBackgroundVelocity(0); - } - - if (!cursorWasChanged) { - _cursorManager->revertToIdle(); - } -} - -} // End of namespace ZVision diff --git a/engines/zvision/fonts/truetype_font.cpp b/engines/zvision/fonts/truetype_font.cpp new file mode 100644 index 0000000000..289b5fbbaf --- /dev/null +++ b/engines/zvision/fonts/truetype_font.cpp @@ -0,0 +1,116 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/scummsys.h" + +#include "zvision/truetype_font.h" + +#include "zvision/zvision.h" +#include "zvision/render_manager.h" + +#include "common/debug.h" +#include "common/file.h" +#include "common/system.h" + +#include "graphics/font.h" +#include "graphics/fonts/ttf.h" +#include "graphics/surface.h" + + +namespace ZVision { + +TruetypeFont::TruetypeFont(ZVision *engine, int32 fontHeight) + : _engine(engine), + _fontHeight(fontHeight), + _font(0), + _lineHeight(0), + _maxCharWidth(0), + _maxCharHeight(0) { +} + +TruetypeFont::~TruetypeFont(void) { + delete _font; +} + +bool TruetypeFont::loadFile(const Common::String &filename) { + Common::File file; + + bool fileOpened = false; + if (!Common::File::exists(filename)) { + debug("TTF font file %s was not found. Reverting to arial.ttf", filename.c_str()); + fileOpened = file.open("arial.ttf"); + } else { + fileOpened = file.open(filename); + } + + if (!fileOpened) { + debug("TTF file could not be opened"); + return false; + } + + _font = Graphics::loadTTFFont(file, _fontHeight); + _lineHeight = _font->getFontHeight(); + + return true; +} + +Graphics::Surface *TruetypeFont::drawTextToSurface(const Common::String &text, uint16 textColor, int maxWidth, int maxHeight, Graphics::TextAlign align, bool wrap) { + if (text.equals("")) { + return nullptr; + } + + Graphics::Surface *surface = new Graphics::Surface(); + + if (!wrap) { + int width = MIN(_font->getStringWidth(text), maxWidth); + surface->create(width, _lineHeight, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); + // TODO: Add better alpha support by getting the pixels from the backbuffer. + // However doing that requires some kind of caching system so future text doesn't try to use this text as it's alpha background. + surface->fillRect(Common::Rect(0, 0, surface->w, surface->h), 0); + + _font->drawString(surface, text, 0, 0, maxWidth, textColor, align); + return surface; + } + + Common::Array lines; + _font->wordWrapText(text, maxWidth, lines); + + while (maxHeight > 0 && (int)lines.size() * _lineHeight > maxHeight) { + lines.pop_back(); + } + if (lines.size() == 0) { + return nullptr; + } + + surface->create(maxWidth, lines.size() * _lineHeight, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); + surface->fillRect(Common::Rect(0, 0, surface->w, surface->h), 0); + + int heightOffset = 0; + for (Common::Array::iterator it = lines.begin(); it != lines.end(); it++) { + _font->drawString(surface, *it, 0, 0 + heightOffset, maxWidth, textColor, align); + heightOffset += _lineHeight; + } + + return surface; +} + +} // End of namespace ZVision diff --git a/engines/zvision/fonts/truetype_font.h b/engines/zvision/fonts/truetype_font.h new file mode 100644 index 0000000000..33f016cffd --- /dev/null +++ b/engines/zvision/fonts/truetype_font.h @@ -0,0 +1,81 @@ +/* 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. + * + */ + +// This file is based on engines/wintermute/base/fonts/base_font_truetype.h/.cpp + +#ifndef ZVISION_TRUETYPE_FONT_H +#define ZVISION_TRUETYPE_FONT_H + +#include "graphics/font.h" +#include "graphics/pixelformat.h" + + +namespace Graphics { +struct Surface; +} + +namespace ZVision { + +class ZVision; + +class TruetypeFont { +public: + TruetypeFont(ZVision *engine, int32 fontHeight); + ~TruetypeFont(); + +private: + ZVision *_engine; + Graphics::Font *_font; + int _lineHeight; + + size_t _maxCharWidth; + size_t _maxCharHeight; + +public: + int32 _fontHeight; + +public: + /** + * Loads a .ttf file into memory. This must be called + * before any calls to drawTextToSurface + * + * @param filename The file name of the .ttf file to load + */ + bool loadFile(const Common::String &filename); + /** + * Renders the supplied text to a Surface using 0x0 as the + * background color. + * + * @param text The to render + * @param textColor The color to render the text with + * @param maxWidth The max width the text should take up. + * @param maxHeight The max height the text should take up. + * @param align The alignment of the text within the bounds of maxWidth + * @param wrap If true, any words extending past maxWidth will wrap to a new line. If false, ellipses will be rendered to show that the text didn't fit + * @return A Surface containing the rendered text + */ + Graphics::Surface *drawTextToSurface(const Common::String &text, uint16 textColor, int maxWidth, int maxHeight, Graphics::TextAlign align, bool wrap); +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/graphics/render_manager.cpp b/engines/zvision/graphics/render_manager.cpp new file mode 100644 index 0000000000..af8ca7fd64 --- /dev/null +++ b/engines/zvision/graphics/render_manager.cpp @@ -0,0 +1,526 @@ +/* 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 "zvision/render_manager.h" + +#include "zvision/lzss_read_stream.h" + +#include "common/file.h" +#include "common/system.h" +#include "common/stream.h" + +#include "engines/util.h" + +#include "graphics/decoders/tga.h" + + +namespace ZVision { + +RenderManager::RenderManager(OSystem *system, uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow, const Graphics::PixelFormat pixelFormat) + : _system(system), + _workingWidth(workingWindow.width()), + _workingHeight(workingWindow.height()), + _screenCenterX(_workingWidth / 2), + _screenCenterY(_workingHeight / 2), + _workingWindow(workingWindow), + _pixelFormat(pixelFormat), + _backgroundWidth(0), + _backgroundHeight(0), + _backgroundInverseVelocity(0), + _backgroundOffset(0, 0), + _accumulatedVelocityMilliseconds(0), + _renderTable(_workingWidth, _workingHeight) { + + _workingWindowBuffer.create(_workingWidth, _workingHeight, _pixelFormat); + _backBuffer.create(windowWidth, windowHeight, pixelFormat); +} + +RenderManager::~RenderManager() { + _workingWindowBuffer.free(); + _currentBackground.free(); + _backBuffer.free(); + + for (AlphaEntryMap::iterator iter = _alphaDataEntries.begin(); iter != _alphaDataEntries.end(); ++iter) { + iter->_value.data->free(); + delete iter->_value.data; + } +} + +void RenderManager::update(uint deltaTimeInMillis) { + // An inverse velocity of 0 would be infinitely fast, so we'll let 0 mean no velocity. + if (_backgroundInverseVelocity != 0) { + _accumulatedVelocityMilliseconds += deltaTimeInMillis; + + uint absVelocity = uint(abs(_backgroundInverseVelocity)); + + int numberOfSteps = 0; + while (_accumulatedVelocityMilliseconds >= absVelocity) { + _accumulatedVelocityMilliseconds -= absVelocity; + numberOfSteps++; + } + + // Choose the direction of movement using the sign of the velocity + moveBackground(_backgroundInverseVelocity < 0 ? -numberOfSteps : numberOfSteps); + } +} + +void RenderManager::renderBackbufferToScreen() { + if (!_workingWindowDirtyRect.isEmpty()) { + RenderTable::RenderState state = _renderTable.getRenderState(); + if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { + _renderTable.mutateImage((uint16 *)_workingWindowBuffer.getPixels(), (uint16 *)_backBuffer.getBasePtr(_workingWindow.left + _workingWindowDirtyRect.left, _workingWindow.top + _workingWindowDirtyRect.top), _backBuffer.w, _workingWindowDirtyRect); + } else { + _backBuffer.copyRectToSurface(_workingWindowBuffer.getBasePtr(_workingWindowDirtyRect.left, _workingWindowDirtyRect.top), _workingWindowBuffer.pitch, _workingWindow.left + _workingWindowDirtyRect.left, _workingWindow.top + _workingWindowDirtyRect.top, _workingWindowDirtyRect.width(), _workingWindowDirtyRect.height()); + } + + // Translate the working window dirty rect to screen coords + _workingWindowDirtyRect.translate(_workingWindow.left, _workingWindow.top); + // Then extend the backbuffer dirty rect to contain it + if (_backBufferDirtyRect.isEmpty()) { + _backBufferDirtyRect = _workingWindowDirtyRect; + } else { + _backBufferDirtyRect.extend(_workingWindowDirtyRect); + } + + // Clear the dirty rect + _workingWindowDirtyRect = Common::Rect(); + } + + // TODO: Add menu rendering + + // Render alpha entries + processAlphaEntries(); + + if (!_backBufferDirtyRect.isEmpty()) { + _system->copyRectToScreen(_backBuffer.getBasePtr(_backBufferDirtyRect.left, _backBufferDirtyRect.top), _backBuffer.pitch, _backBufferDirtyRect.left, _backBufferDirtyRect.top, _backBufferDirtyRect.width(), _backBufferDirtyRect.height()); + _backBufferDirtyRect = Common::Rect(); + } +} + +void RenderManager::processAlphaEntries() { + // TODO: Add dirty rectangling support. AKA only draw an entry if the _backbufferDirtyRect intersects/contains the entry Rect + + for (AlphaEntryMap::iterator iter = _alphaDataEntries.begin(); iter != _alphaDataEntries.end(); ++iter) { + uint32 destOffset = 0; + uint32 sourceOffset = 0; + uint16 *backbufferPtr = (uint16 *)_backBuffer.getBasePtr(iter->_value.destX + _workingWindow.left, iter->_value.destY + _workingWindow.top); + uint16 *entryPtr = (uint16 *)iter->_value.data->getPixels(); + + for (int32 y = 0; y < iter->_value.height; ++y) { + for (int32 x = 0; x < iter->_value.width; ++x) { + uint16 color = entryPtr[sourceOffset + x]; + if (color != iter->_value.alphaColor) { + backbufferPtr[destOffset + x] = color; + } + } + + destOffset += _backBuffer.w; + sourceOffset += iter->_value.width; + } + + if (_backBufferDirtyRect.isEmpty()) { + _backBufferDirtyRect = Common::Rect(iter->_value.destX + _workingWindow.left, iter->_value.destY + _workingWindow.top, iter->_value.destX + _workingWindow.left + iter->_value.width, iter->_value.destY + _workingWindow.top + iter->_value.height); + } else { + _backBufferDirtyRect.extend(Common::Rect(iter->_value.destX + _workingWindow.left, iter->_value.destY + _workingWindow.top, iter->_value.destX + _workingWindow.left + iter->_value.width, iter->_value.destY + _workingWindow.top + iter->_value.height)); + } + } +} + +void RenderManager::clearWorkingWindowTo555Color(uint16 color) { + uint32 workingWindowSize = _workingWidth * _workingHeight; + byte r, g, b; + Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0).colorToRGB(color, r, g, b); + uint16 colorIn565 = _pixelFormat.RGBToColor(r, g, b); + uint16 *bufferPtr = (uint16 *)_workingWindowBuffer.getPixels(); + + for (uint32 i = 0; i < workingWindowSize; ++i) { + bufferPtr[i] = colorIn565; + } +} + +void RenderManager::renderSubRectToScreen(Graphics::Surface &surface, int16 destinationX, int16 destinationY, bool wrap) { + int16 subRectX = 0; + int16 subRectY = 0; + + // Take care of negative destinations + if (destinationX < 0) { + subRectX = -destinationX; + destinationX = 0; + } else if (destinationX >= surface.w) { + // Take care of extreme positive destinations + destinationX -= surface.w; + } + + // Take care of negative destinations + if (destinationY < 0) { + subRectY = -destinationY; + destinationY = 0; + } else if (destinationY >= surface.h) { + // Take care of extreme positive destinations + destinationY -= surface.h; + } + + if (wrap) { + _backgroundWidth = surface.w; + _backgroundHeight = surface.h; + + if (destinationX > 0) { + // Move destinationX to 0 + subRectX = surface.w - destinationX; + destinationX = 0; + } + + if (destinationY > 0) { + // Move destinationY to 0 + subRectY = surface.h - destinationY; + destinationY = 0; + } + } + + // Clip subRect to working window bounds + Common::Rect subRect(subRectX, subRectY, subRectX + _workingWidth, subRectY + _workingHeight); + + if (!wrap) { + // Clip to image bounds + subRect.clip(surface.w, surface.h); + } + + // Check destRect for validity + if (!subRect.isValidRect() || subRect.isEmpty()) + return; + + copyRectToWorkingWindow((const uint16 *)surface.getBasePtr(subRect.left, subRect.top), destinationX, destinationY, surface.w, subRect.width(), subRect.height()); +} + +void RenderManager::renderImageToScreen(const Common::String &fileName, int16 destinationX, int16 destinationY, bool wrap) { + Graphics::Surface surface; + readImageToSurface(fileName, surface); + + renderSubRectToScreen(surface, destinationX, destinationY, wrap); +} + +void RenderManager::renderImageToScreen(Graphics::Surface &surface, int16 destinationX, int16 destinationY, bool wrap) { + renderSubRectToScreen(surface, destinationX, destinationY, wrap); +} + +void RenderManager::readImageToSurface(const Common::String &fileName, Graphics::Surface &destination) { + Common::File file; + + if (!file.open(fileName)) { + warning("Could not open file %s", fileName.c_str()); + return; + } + + // Read the magic number + // Some files are true TGA, while others are TGZ + uint32 fileType = file.readUint32BE(); + + uint32 imageWidth; + uint32 imageHeight; + Graphics::TGADecoder tga; + uint16 *buffer; + bool isTransposed = _renderTable.getRenderState() == RenderTable::PANORAMA; + // All ZVision images are in RGB 555 + Graphics::PixelFormat pixelFormat555 = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); + destination.format = pixelFormat555; + + bool isTGZ; + + // Check for TGZ files + if (fileType == MKTAG('T', 'G', 'Z', '\0')) { + isTGZ = true; + + // TGZ files have a header and then Bitmap data that is compressed with LZSS + uint32 decompressedSize = file.readSint32LE(); + imageWidth = file.readSint32LE(); + imageHeight = file.readSint32LE(); + + LzssReadStream lzssStream(&file); + buffer = (uint16 *)(new uint16[decompressedSize]); + lzssStream.read(buffer, decompressedSize); + } else { + isTGZ = false; + + // Reset the cursor + file.seek(0); + + // Decode + if (!tga.loadStream(file)) { + warning("Error while reading TGA image"); + return; + } + + Graphics::Surface tgaSurface = *(tga.getSurface()); + imageWidth = tgaSurface.w; + imageHeight = tgaSurface.h; + + buffer = (uint16 *)tgaSurface.getPixels(); + } + + // Flip the width and height if transposed + if (isTransposed) { + uint16 temp = imageHeight; + imageHeight = imageWidth; + imageWidth = temp; + } + + // If the destination internal buffer is the same size as what we're copying into it, + // there is no need to free() and re-create + if (imageWidth != destination.w || imageHeight != destination.h) { + destination.create(imageWidth, imageHeight, pixelFormat555); + } + + // If transposed, 'un-transpose' the data while copying it to the destination + // Otherwise, just do a simple copy + if (isTransposed) { + uint16 *dest = (uint16 *)destination.getPixels(); + + for (uint32 y = 0; y < imageHeight; ++y) { + uint32 columnIndex = y * imageWidth; + + for (uint32 x = 0; x < imageWidth; ++x) { + dest[columnIndex + x] = buffer[x * imageHeight + y]; + } + } + } else { + memcpy(destination.getPixels(), buffer, imageWidth * imageHeight * _pixelFormat.bytesPerPixel); + } + + // Cleanup + if (isTGZ) { + delete[] buffer; + } else { + tga.destroy(); + } + + // Convert in place to RGB 565 from RGB 555 + destination.convertToInPlace(_pixelFormat); +} + +void RenderManager::copyRectToWorkingWindow(const uint16 *buffer, int32 destX, int32 destY, int32 imageWidth, int32 width, int32 height) { + uint32 destOffset = 0; + uint32 sourceOffset = 0; + uint16 *workingWindowBufferPtr = (uint16 *)_workingWindowBuffer.getBasePtr(destX, destY); + + for (int32 y = 0; y < height; ++y) { + for (int32 x = 0; x < width; ++x) { + workingWindowBufferPtr[destOffset + x] = buffer[sourceOffset + x]; + } + + destOffset += _workingWidth; + sourceOffset += imageWidth; + } + + if (_workingWindowDirtyRect.isEmpty()) { + _workingWindowDirtyRect = Common::Rect(destX, destY, destX + width, destY + height); + } else { + _workingWindowDirtyRect.extend(Common::Rect(destX, destY, destX + width, destY + height)); + } + + // TODO: Remove this from release. It's here to make sure code that uses this function clips their destinations correctly + assert(_workingWindowDirtyRect.width() <= _workingWidth && _workingWindowDirtyRect.height() <= _workingHeight); +} + +void RenderManager::copyRectToWorkingWindow(const uint16 *buffer, int32 destX, int32 destY, int32 imageWidth, int32 width, int32 height, int16 alphaColor, uint32 idNumber) { + AlphaDataEntry entry; + entry.alphaColor = alphaColor; + entry.data = new Graphics::Surface(); + entry.data->create(width, height, _pixelFormat); + entry.destX = destX; + entry.destY = destY; + entry.width = width; + entry.height = height; + + uint32 sourceOffset = 0; + uint32 destOffset = 0; + uint16 *surfacePtr = (uint16 *)entry.data->getPixels(); + + for (int32 y = 0; y < height; ++y) { + for (int32 x = 0; x < width; ++x) { + surfacePtr[destOffset + x] = buffer[sourceOffset + x]; + } + + destOffset += width; + sourceOffset += imageWidth; + } + + _alphaDataEntries[idNumber] = entry; +} + +Common::Rect RenderManager::renderTextToWorkingWindow(uint32 idNumber, const Common::String &text, TruetypeFont *font, int destX, int destY, uint16 textColor, int maxWidth, int maxHeight, Graphics::TextAlign align, bool wrap) { + AlphaDataEntry entry; + entry.alphaColor = 0; + entry.destX = destX; + entry.destY = destY; + + // Draw the text to the working window + entry.data = font->drawTextToSurface(text, textColor, maxWidth, maxHeight, align, wrap); + entry.width = entry.data->w; + entry.height = entry.data->h; + + _alphaDataEntries[idNumber] = entry; + + return Common::Rect(destX, destY, destX + entry.width, destY + entry.height); +} + +const Common::Point RenderManager::screenSpaceToImageSpace(const Common::Point &point) { + // Convert from screen space to working window space + Common::Point newPoint(point - Common::Point(_workingWindow.left, _workingWindow.top)); + + RenderTable::RenderState state = _renderTable.getRenderState(); + if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { + newPoint = _renderTable.convertWarpedCoordToFlatCoord(newPoint); + } + + if (state == RenderTable::PANORAMA) { + newPoint -= (Common::Point(_screenCenterX, 0) - _backgroundOffset); + } else if (state == RenderTable::TILT) { + newPoint -= (Common::Point(0, _screenCenterY) - _backgroundOffset); + } + + if (newPoint.x < 0) + newPoint.x += _backgroundWidth; + else if (newPoint.x >= _backgroundWidth) + newPoint.x -= _backgroundWidth; + if (newPoint.y < 0) + newPoint.y += _backgroundHeight; + else if (newPoint.y >= _backgroundHeight) + newPoint.y -= _backgroundHeight; + + return newPoint; +} + +const Common::Point RenderManager::imageSpaceToWorkingWindowSpace(const Common::Point &point) { + Common::Point newPoint(point); + + RenderTable::RenderState state = _renderTable.getRenderState(); + if (state == RenderTable::PANORAMA) { + newPoint += (Common::Point(_screenCenterX, 0) - _backgroundOffset); + } else if (state == RenderTable::TILT) { + newPoint += (Common::Point(0, _screenCenterY) - _backgroundOffset); + } + + return newPoint; +} + +bool RenderManager::clipRectToWorkingWindow(Common::Rect &rect) { + if (!_workingWindow.contains(rect)) { + return false; + } + + // We can't clip against the actual working window rect because it's in screen space + // But rect is in working window space + rect.clip(_workingWidth, _workingHeight); + return true; +} + +RenderTable *RenderManager::getRenderTable() { + return &_renderTable; +} + +void RenderManager::setBackgroundImage(const Common::String &fileName) { + readImageToSurface(fileName, _currentBackground); + + moveBackground(0); +} + +void RenderManager::setBackgroundPosition(int offset) { + RenderTable::RenderState state = _renderTable.getRenderState(); + if (state == RenderTable::TILT) { + _backgroundOffset.x = 0; + _backgroundOffset.y = offset; + } else if (state == RenderTable::PANORAMA) { + _backgroundOffset.x = offset; + _backgroundOffset.y = 0; + } else { + _backgroundOffset.x = 0; + _backgroundOffset.y = 0; + } +} + +void RenderManager::setBackgroundVelocity(int velocity) { + // setBackgroundVelocity(0) will be called quite often, so make sure + // _backgroundInverseVelocity isn't already 0 to prevent an extraneous assignment + if (velocity == 0) { + if (_backgroundInverseVelocity != 0) { + _backgroundInverseVelocity = 0; + } + } else { + _backgroundInverseVelocity = 1000 / velocity; + } +} + +void RenderManager::moveBackground(int offset) { + RenderTable::RenderState state = _renderTable.getRenderState(); + if (state == RenderTable::TILT) { + _backgroundOffset += Common::Point(0, offset); + + _backgroundOffset.y = CLIP(_backgroundOffset.y, _screenCenterY, (int16)_backgroundHeight - _screenCenterY); + + renderImageToScreen(_currentBackground, 0, _screenCenterY - _backgroundOffset.y, true); + } else if (state == RenderTable::PANORAMA) { + _backgroundOffset += Common::Point(offset, 0); + + if (_backgroundOffset.x <= -_backgroundWidth) + _backgroundOffset.x += _backgroundWidth; + else if (_backgroundOffset.x >= _backgroundWidth) + _backgroundOffset.x -= _backgroundWidth; + + renderImageToScreen(_currentBackground, _screenCenterX - _backgroundOffset.x, 0, true); + } else { + renderImageToScreen(_currentBackground, 0, 0); + } +} + +uint32 RenderManager::getCurrentBackgroundOffset() { + RenderTable::RenderState state = _renderTable.getRenderState(); + + if (state == RenderTable::PANORAMA) { + return _backgroundOffset.x; + } else if (state == RenderTable::TILT) { + return _backgroundOffset.y; + } else { + return 0; + } +} + +Graphics::Surface *RenderManager::tranposeSurface(const Graphics::Surface *surface) { + Graphics::Surface *tranposedSurface = new Graphics::Surface(); + tranposedSurface->create(surface->h, surface->w, surface->format); + + const uint16 *source = (const uint16 *)surface->getPixels(); + uint16 *dest = (uint16 *)tranposedSurface->getPixels(); + + for (uint32 y = 0; y < tranposedSurface->h; ++y) { + uint32 columnIndex = y * tranposedSurface->w; + + for (uint32 x = 0; x < tranposedSurface->w; ++x) { + dest[columnIndex + x] = source[x * surface->w + y]; + } + } + + return tranposedSurface; +} + +} // End of namespace ZVision diff --git a/engines/zvision/graphics/render_manager.h b/engines/zvision/graphics/render_manager.h new file mode 100644 index 0000000000..111bf6276c --- /dev/null +++ b/engines/zvision/graphics/render_manager.h @@ -0,0 +1,328 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ZVISION_RENDER_MANAGER_H +#define ZVISION_RENDER_MANAGER_H + +#include "zvision/render_table.h" +#include "zvision/truetype_font.h" + +#include "common/rect.h" +#include "common/hashmap.h" + +#include "graphics/surface.h" + + +class OSystem; + +namespace Common { +class String; +class SeekableReadStream; +} + +namespace Video { +class VideoDecoder; +} + +namespace ZVision { + +class RenderManager { +public: + RenderManager(OSystem *system, uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow, const Graphics::PixelFormat pixelFormat); + ~RenderManager(); + +private: + struct AlphaDataEntry { + Graphics::Surface *data; + uint16 alphaColor; + uint16 destX; + uint16 destY; + uint16 width; + uint16 height; + }; + + typedef Common::HashMap AlphaEntryMap; + +private: + OSystem *_system; + const Graphics::PixelFormat _pixelFormat; + + // A buffer the exact same size as the workingWindow + // This buffer stores everything un-warped, then does a warp at the end of the frame + Graphics::Surface _workingWindowBuffer; + // A buffer representing the entire screen. Any graphical updates are first done with this buffer + // before actually being blitted to the screen + Graphics::Surface _backBuffer; + // A list of Alpha Entries that need to be blitted to the backbuffer + AlphaEntryMap _alphaDataEntries; + + // A rectangle representing the portion of the working window where the pixels have been changed since last frame + Common::Rect _workingWindowDirtyRect; + // A rectangle representing the portion of the backbuffer where the pixels have been changed since last frame + Common::Rect _backBufferDirtyRect; + + /** Width of the working window. Saved to prevent extraneous calls to _workingWindow.width() */ + const int _workingWidth; + /** Height of the working window. Saved to prevent extraneous calls to _workingWindow.height() */ + const int _workingHeight; + /** Center of the screen in the x direction */ + const int _screenCenterX; + /** Center of the screen in the y direction */ + const int _screenCenterY; + + /** + * A Rectangle centered inside the actual window. All in-game coordinates + * are given in this coordinate space. Also, all images are clipped to the + * edges of this Rectangle + */ + const Common::Rect _workingWindow; + /** Used to warp the background image */ + RenderTable _renderTable; + + Graphics::Surface _currentBackground; + /** The (x1,y1) coordinates of the subRectangle of the background that is currently displayed on the screen */ + Common::Point _backgroundOffset; + /** The width of the current background image */ + uint16 _backgroundWidth; + /** The height of the current background image */ + uint16 _backgroundHeight; + + /** + * The "velocity" at which the background image is panning. We actually store the inverse of velocity (ms/pixel instead of pixels/ms) + * because it allows you to accumulate whole pixels 'steps' instead of rounding pixels every frame + */ + int _backgroundInverseVelocity; + /** Holds any 'leftover' milliseconds between frames */ + uint _accumulatedVelocityMilliseconds; + +public: + void initialize(); + /** + * Rotates the background image in accordance to the current _backgroundInverseVelocity + * + * @param deltaTimeInMillis The amount of time that has passed since the last frame + */ + void update(uint deltaTimeInMillis); + + /** + * Renders the current state of the backbuffer to the screen + */ + void renderBackbufferToScreen(); + + /** + * Renders all AlphaEntries to the backbuffer + */ + void processAlphaEntries(); + /** + * Clears the AlphaEntry list + */ + void clearAlphaEntries() { _alphaDataEntries.clear(); } + /** + * Removes a specific AlphaEntry from the list + * + * @param idNumber The id number identifing the AlphaEntry + */ + void removeAlphaEntry(uint32 idNumber) { _alphaDataEntries.erase(idNumber); } + + /** + * Copies a sub-rectangle of a buffer to the working window + * + * @param buffer The pixel data to copy to the working window + * @param destX The X destination in the working window where the subRect of data should be put + * @param destY The Y destination in the working window where the subRect of data should be put + * @param imageWidth The width of the source image + * @param width The width of the sub rectangle + * @param height The height of the sub rectangle + */ + void copyRectToWorkingWindow(const uint16 *buffer, int32 destX, int32 destY, int32 imageWidth, int32 width, int32 height); + /** + * Copies a sub-rectangle of a buffer to the working window with binary alpha support. + * + * @param buffer The pixel data to copy to the working window + * @param destX The X destination in the working window where the subRect of data should be put + * @param destY The Y destination in the working window where the subRect of data should be put + * @param imageWidth The width of the source image + * @param width The width of the sub rectangle + * @param height The height of the sub rectangle + * @param alphaColor The color to interpret as meaning 'transparent' + * @param idNumber A unique identifier for the data being copied over. + */ + void copyRectToWorkingWindow(const uint16 *buffer, int32 destX, int32 destY, int32 imageWidth, int32 width, int32 height, int16 alphaColor, uint32 idNumber); + + /** + * Renders the supplied text to the working window + * + * @param idNumber A unique identifier for the text + * @param text The text to be rendered + * @param font The font to use to render the text + * @param destX The X destination in the working window where the text should be rendered + * @param destY The Y destination in the working window where the text should be rendered + * @param textColor The color to render the text with (in RBG 565) + * @param maxWidth The max width the text should take up. + * @param maxHeight The max height the text should take up. + * @param align The alignment of the text within the bounds of maxWidth + * @param wrap If true, any words extending past maxWidth will wrap to a new line. If false, ellipses will be rendered to show that the text didn't fit + * @return A rectangle representing where the text was drawn in the working window + */ + Common::Rect renderTextToWorkingWindow(uint32 idNumber, const Common::String &text, TruetypeFont *font, int destX, int destY, uint16 textColor, int maxWidth, int maxHeight = -1, Graphics::TextAlign align = Graphics::kTextAlignLeft, bool wrap = true); + + /** + * Fills the entire workingWindow with the specified color. Internally, the color + * will be converted to RGB 565 and then blitted. + * + * @param color The color to fill the working window with. (In RGB 555) + */ + void clearWorkingWindowTo555Color(uint16 color); + + /** + * Blits the image or a portion of the image to the backbuffer. Actual screen updates won't happen until the end of the frame. + * The image will be clipped to fit inside the working window. Coords are in working window space, not screen space! + * + * @param fileName Name of the image file + * @param destinationX X position where the image should be put. Coords are in working window space, not screen space! + * @param destinationY Y position where the image should be put. Coords are in working window space, not screen space! + */ + void renderImageToScreen(const Common::String &fileName, int16 destinationX, int16 destinationY, bool wrap = false); + + /** + * Blits the image or a portion of the image to the backbuffer. Actual screen updates won't happen until the end of the frame. + * The image will be clipped to fit inside the working window. Coords are in working window space, not screen space! + * + * @param stream Surface to read the image data from + * @param destinationX X position where the image should be put. Coords are in working window space, not screen space! + * @param destinationY Y position where the image should be put. Coords are in working window space, not screen space! + */ + void renderImageToScreen(Graphics::Surface &surface, int16 destinationX, int16 destinationY, bool wrap = false); + + /** + * Sets the current background image to be used by the RenderManager and immediately + * blits it to the screen. (It won't show up until the end of the frame) + * + * @param fileName The name of the image file + */ + void setBackgroundImage(const Common::String &fileName); + + /** + * Set the background position (_backgroundOffset). If the current RenderState is PANORAMA, the offset + * will be in the horizontal direction. If the current RenderState is TILT, the offset will be in the + * vertical direction. + * + * This method will not render anything on the screen. So if nothing else is called that renders the + * background, the change won't be seen until next frame. + * + * @param offset The amount to offset the background + */ + void setBackgroundPosition(int offset); + + /** + * Set the background scroll velocity. Negative velocities correspond to left / up scrolling and + * positive velocities correspond to right / down scrolling + * + * @param velocity Velocity + */ + void setBackgroundVelocity(int velocity); + + /** + * Converts a point in screen coordinate space to image coordinate space + * + * @param point Point in screen coordinate space + * @return Point in image coordinate space + */ + const Common::Point screenSpaceToImageSpace(const Common::Point &point); + /** + * Converts a point in image coordinate space to ***PRE-WARP*** + * working window coordinate space + * + * @param point Point in image coordinate space + * @return Point in PRE-WARP working window coordinate space + */ + const Common::Point imageSpaceToWorkingWindowSpace(const Common::Point &point); + + /** + * Clip a rectangle to the working window. If it returns false, the original rect + * is not inside the working window. + * + * @param rect The rectangle to clip against the working window + * @return Is rect at least partially inside the working window (true) or completely outside (false) + */ + bool clipRectToWorkingWindow(Common::Rect &rect); + + RenderTable *getRenderTable(); + uint32 getCurrentBackgroundOffset(); + const Graphics::Surface *getBackBuffer() { return &_backBuffer; } + + /** + * Creates a copy of surface and transposes the data. + * + * Note: The user is responsible for calling free() on the returned surface + * and then deleting it + * + * @param surface The data to be transposed + * @return A copy of the surface with the data transposed + */ + static Graphics::Surface *tranposeSurface(const Graphics::Surface *surface); + +private: + /** + * Renders a subRectangle of an image to the backbuffer. The destinationRect and SubRect + * will be clipped to image bound and to working window bounds + * + * @param buffer Pointer to (0, 0) of the image data + * @param imageWidth The width of the original image (not of the subRectangle) + * @param imageHeight The width of the original image (not of the subRectangle) + * @param horizontalPitch The horizontal pitch of the original image + * @param destinationX The x coordinate (in working window space) of where to put the final image + * @param destinationY The y coordinate (in working window space) of where to put the final image + * @param subRectangle A rectangle representing the part of the image that should be rendered + * @param wrap Should the image wrap (tile) if it doesn't completely fill the screen? + */ + void renderSubRectToScreen(Graphics::Surface &surface, int16 destinationX, int16 destinationY, bool wrap); + + /** + * Reads an image file pixel data into a Surface buffer. In the process + * it converts the pixel data from RGB 555 to RGB 565. Also, if the image + * is transposed, it will un-transpose the pixel data. The function will + * call destination::create() if the dimensions of destination do not match + * up with the dimensions of the image. + * + * @param fileName The name of a .tga file + * @param destination A reference to the Surface to store the pixel data in + */ + void readImageToSurface(const Common::String &fileName, Graphics::Surface &destination); + + /** + * Move the background image by an offset. If we are currently in Panorama mode, + * the offset will correspond to a horizontal motion. If we are currently in Tilt mode, + * the offset will correspond to a vertical motion. This function should not be called + * if we are in Flat mode. + * + * The RenderManager will take care of wrapping the image. + * Ex: If the image has width 1400px, it is legal to offset 1500px. + * + * @param offset The amount to move the background + */ + void moveBackground(int offset); +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/graphics/render_table.cpp b/engines/zvision/graphics/render_table.cpp new file mode 100644 index 0000000000..b6a6a3d2bb --- /dev/null +++ b/engines/zvision/graphics/render_table.cpp @@ -0,0 +1,240 @@ +/* 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 "zvision/render_table.h" + +#include "common/rect.h" + +#include "graphics/colormasks.h" + + +namespace ZVision { + +RenderTable::RenderTable(uint numColumns, uint numRows) + : _numRows(numRows), + _numColumns(numColumns), + _renderState(FLAT) { + assert(numRows != 0 && numColumns != 0); + + _internalBuffer = new Common::Point[numRows * numColumns]; +} + +RenderTable::~RenderTable() { + delete[] _internalBuffer; +} + +void RenderTable::setRenderState(RenderState newState) { + _renderState = newState; + + switch (newState) { + case PANORAMA: + _panoramaOptions.fieldOfView = 27.0f; + _panoramaOptions.linearScale = 0.55f; + _panoramaOptions.reverse = false; + break; + case TILT: + _tiltOptions.fieldOfView = 27.0f; + _tiltOptions.linearScale = 0.55f; + _tiltOptions.reverse = false; + break; + case FLAT: + // Intentionally left empty + break; + } +} + +const Common::Point RenderTable::convertWarpedCoordToFlatCoord(const Common::Point &point) { + // If we're outside the range of the RenderTable, no warping is happening. Return the maximum image coords + if (point.x >= (int16)_numColumns || point.y >= (int16)_numRows || point.x < 0 || point.y < 0) { + int16 x = CLIP(point.x, 0, (int16)_numColumns); + int16 y = CLIP(point.y, 0, (int16)_numRows); + return Common::Point(x, y); + } + + uint32 index = point.y * _numColumns + point.x; + + Common::Point newPoint(point); + newPoint.x += _internalBuffer[index].x; + newPoint.y += _internalBuffer[index].y; + + return newPoint; +} + +uint16 mixTwoRGB(uint16 colorOne, uint16 colorTwo, float percentColorOne) { + assert(percentColorOne < 1.0f); + + float rOne = float((colorOne & Graphics::ColorMasks<555>::kRedMask) >> Graphics::ColorMasks<555>::kRedShift); + float rTwo = float((colorTwo & Graphics::ColorMasks<555>::kRedMask) >> Graphics::ColorMasks<555>::kRedShift); + float gOne = float((colorOne & Graphics::ColorMasks<555>::kGreenMask) >> Graphics::ColorMasks<555>::kGreenShift); + float gTwo = float((colorTwo & Graphics::ColorMasks<555>::kGreenMask) >> Graphics::ColorMasks<555>::kGreenShift); + float bOne = float((colorOne & Graphics::ColorMasks<555>::kBlueMask) >> Graphics::ColorMasks<555>::kBlueShift); + float bTwo = float((colorTwo & Graphics::ColorMasks<555>::kBlueMask) >> Graphics::ColorMasks<555>::kBlueShift); + + float rFinal = rOne * percentColorOne + rTwo * (1.0f - percentColorOne); + float gFinal = gOne * percentColorOne + gTwo * (1.0f - percentColorOne); + float bFinal = bOne * percentColorOne + bTwo * (1.0f - percentColorOne); + + uint16 returnColor = (byte(rFinal + 0.5f) << Graphics::ColorMasks<555>::kRedShift) | + (byte(gFinal + 0.5f) << Graphics::ColorMasks<555>::kGreenShift) | + (byte(bFinal + 0.5f) << Graphics::ColorMasks<555>::kBlueShift); + + return returnColor; +} + +void RenderTable::mutateImage(uint16 *sourceBuffer, uint16* destBuffer, uint32 destWidth, const Common::Rect &subRect) { + uint32 destOffset = 0; + + for (int16 y = subRect.top; y < subRect.bottom; ++y) { + uint32 sourceOffset = y * _numColumns; + + for (int16 x = subRect.left; x < subRect.right; ++x) { + uint32 normalizedX = x - subRect.left; + uint32 index = sourceOffset + x; + + // RenderTable only stores offsets from the original coordinates + uint32 sourceYIndex = y + _internalBuffer[index].y; + uint32 sourceXIndex = x + _internalBuffer[index].x; + + destBuffer[destOffset + normalizedX] = sourceBuffer[sourceYIndex * _numColumns + sourceXIndex]; + } + + destOffset += destWidth; + } +} + +void RenderTable::generateRenderTable() { + switch (_renderState) { + case ZVision::RenderTable::PANORAMA: + generatePanoramaLookupTable(); + break; + case ZVision::RenderTable::TILT: + generateTiltLookupTable(); + break; + case ZVision::RenderTable::FLAT: + // Intentionally left empty + break; + } +} + +void RenderTable::generatePanoramaLookupTable() { + memset(_internalBuffer, 0, _numRows * _numColumns * sizeof(uint16)); + + float halfWidth = (float)_numColumns / 2.0f; + float halfHeight = (float)_numRows / 2.0f; + + float fovInRadians = (_panoramaOptions.fieldOfView * M_PI / 180.0f); + float cylinderRadius = halfHeight / tan(fovInRadians); + + for (uint x = 0; x < _numColumns; ++x) { + // Add an offset of 0.01 to overcome zero tan/atan issue (vertical line on half of screen) + // Alpha represents the horizontal angle between the viewer at the center of a cylinder and x + float alpha = atan(((float)x - halfWidth + 0.01f) / cylinderRadius); + + // To get x in cylinder coordinates, we just need to calculate the arc length + // We also scale it by _panoramaOptions.linearScale + int32 xInCylinderCoords = int32(floor((cylinderRadius * _panoramaOptions.linearScale * alpha) + halfWidth)); + + float cosAlpha = cos(alpha); + + for (uint y = 0; y < _numRows; ++y) { + // To calculate y in cylinder coordinates, we can do similar triangles comparison, + // comparing the triangle from the center to the screen and from the center to the edge of the cylinder + int32 yInCylinderCoords = int32(floor(halfHeight + ((float)y - halfHeight) * cosAlpha)); + + uint32 index = y * _numColumns + x; + + // Only store the (x,y) offsets instead of the absolute positions + _internalBuffer[index].x = xInCylinderCoords - x; + _internalBuffer[index].y = yInCylinderCoords - y; + } + } +} + +void RenderTable::generateTiltLookupTable() { + float halfWidth = (float)_numColumns / 2.0f; + float halfHeight = (float)_numRows / 2.0f; + + float fovInRadians = (_tiltOptions.fieldOfView * M_PI / 180.0f); + float cylinderRadius = halfWidth / tan(fovInRadians); + + for (uint y = 0; y < _numRows; ++y) { + + // Add an offset of 0.01 to overcome zero tan/atan issue (horizontal line on half of screen) + // Alpha represents the vertical angle between the viewer at the center of a cylinder and y + float alpha = atan(((float)y - halfHeight + 0.01f) / cylinderRadius); + + // To get y in cylinder coordinates, we just need to calculate the arc length + // We also scale it by _tiltOptions.linearScale + int32 yInCylinderCoords = int32(floor((cylinderRadius * _tiltOptions.linearScale * alpha) + halfHeight)); + + float cosAlpha = cos(alpha); + uint32 columnIndex = y * _numColumns; + + for (uint x = 0; x < _numColumns; ++x) { + // To calculate x in cylinder coordinates, we can do similar triangles comparison, + // comparing the triangle from the center to the screen and from the center to the edge of the cylinder + int32 xInCylinderCoords = int32(floor(halfWidth + ((float)x - halfWidth) * cosAlpha)); + + uint32 index = columnIndex + x; + + // Only store the (x,y) offsets instead of the absolute positions + _internalBuffer[index].x = xInCylinderCoords - x; + _internalBuffer[index].y = yInCylinderCoords - y; + } + } +} + +void RenderTable::setPanoramaFoV(float fov) { + assert(fov > 0.0f); + + _panoramaOptions.fieldOfView = fov; +} + +void RenderTable::setPanoramaScale(float scale) { + assert(scale > 0.0f); + + _panoramaOptions.linearScale = scale; +} + +void RenderTable::setPanoramaReverse(bool reverse) { + _panoramaOptions.reverse = reverse; +} + +void RenderTable::setTiltFoV(float fov) { + assert(fov > 0.0f); + + _tiltOptions.fieldOfView = fov; +} + +void RenderTable::setTiltScale(float scale) { + assert(scale > 0.0f); + + _tiltOptions.linearScale = scale; +} + +void RenderTable::setTiltReverse(bool reverse) { + _tiltOptions.reverse = reverse; +} + +} // End of namespace ZVision diff --git a/engines/zvision/graphics/render_table.h b/engines/zvision/graphics/render_table.h new file mode 100644 index 0000000000..898091193a --- /dev/null +++ b/engines/zvision/graphics/render_table.h @@ -0,0 +1,85 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ZVISION_RENDER_TABLE_H +#define ZVISION_RENDER_TABLE_H + +#include "common/rect.h" + + +namespace ZVision { + +class RenderTable { +public: + RenderTable(uint numRows, uint numColumns); + ~RenderTable(); + +public: + enum RenderState { + PANORAMA, + TILT, + FLAT + }; + +private: + uint _numColumns, _numRows; + Common::Point *_internalBuffer; + RenderState _renderState; + + struct { + float fieldOfView; + float linearScale; + bool reverse; + } _panoramaOptions; + + // TODO: See if tilt and panorama need to have separate options + struct { + float fieldOfView; + float linearScale; + bool reverse; + } _tiltOptions; + +public: + RenderState getRenderState() { return _renderState; } + void setRenderState(RenderState newState); + + const Common::Point convertWarpedCoordToFlatCoord(const Common::Point &point); + + void mutateImage(uint16 *sourceBuffer, uint16* destBuffer, uint32 destWidth, const Common::Rect &subRect); + void generateRenderTable(); + + void setPanoramaFoV(float fov); + void setPanoramaScale(float scale); + void setPanoramaReverse(bool reverse); + + void setTiltFoV(float fov); + void setTiltScale(float scale); + void setTiltReverse(bool reverse); + +private: + void generatePanoramaLookupTable(); + void generateTiltLookupTable(); +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/input_control.cpp b/engines/zvision/input_control.cpp deleted file mode 100644 index a445e1aae5..0000000000 --- a/engines/zvision/input_control.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* 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 "zvision/input_control.h" - -#include "zvision/zvision.h" -#include "zvision/script_manager.h" -#include "zvision/string_manager.h" -#include "zvision/render_manager.h" -#include "zvision/utility.h" - -#include "common/str.h" -#include "common/stream.h" -#include "common/rect.h" - - -namespace ZVision { - -InputControl::InputControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) - : Control(engine, key), - _nextTabstop(0), - _focused(false), - _textChanged(false), - _cursorOffset(0) { - // Loop until we find the closing brace - Common::String line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - - while (!stream.eos() && !line.contains('}')) { - if (line.matchString("*rectangle*", true)) { - int x1; - int y1; - int x2; - int y2; - - sscanf(line.c_str(), "%*[^(](%d %d %d %d)", &x1, &y1, &x2, &y2); - - _textRectangle = Common::Rect(x1, y1, x2, y2); - } else if (line.matchString("*aux_hotspot*", true)) { - int x1; - int y1; - int x2; - int y2; - - sscanf(line.c_str(), "%*[^(](%d %d %d %d)", &x1, &y1, &x2, &y2); - - _headerRectangle = Common::Rect(x1, y1, x2, y2); - } else if (line.matchString("*string_init*", true)) { - uint fontFormatNumber; - - sscanf(line.c_str(), "%*[^(](%u)", &fontFormatNumber); - - _textStyle = _engine->getStringManager()->getTextStyle(fontFormatNumber); - } else if (line.matchString("*next_tabstop*", true)) { - sscanf(line.c_str(), "%*[^(](%u)", &_nextTabstop); - } else if (line.matchString("*cursor_animation*", true)) { - char fileName[25]; - - sscanf(line.c_str(), "%*[^(](%25s %*u)", fileName); - - _cursorAnimationFileName = Common::String(fileName); - } else if (line.matchString("*cursor_dimensions*", true)) { - // Ignore, use the dimensions in the animation file - } else if (line.matchString("*cursor_animation_frames*", true)) { - // Ignore, use the frame count in the animation file - } else if (line.matchString("*focus*", true)) { - _focused = true; - } - - line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - } -} - -void InputControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - _engine->getScriptManager()->focusControl(_key); -} - -void InputControl::onKeyDown(Common::KeyState keyState) { - if (!_focused) { - return; - } - - if (keyState.keycode == Common::KEYCODE_BACKSPACE) { - _currentInputText.deleteLastChar(); - } else if (keyState.keycode == Common::KEYCODE_TAB) { - _focused = false; - // Focus the next input control - _engine->getScriptManager()->focusControl(_nextTabstop); - } else { - // Otherwise, append the new character to the end of the current text - - uint16 asciiValue = keyState.ascii; - // We only care about text values - if (asciiValue >= 32 && asciiValue <= 126) { - _currentInputText += (char)asciiValue; - _textChanged = true; - } - } -} - -bool InputControl::process(uint32 deltaTimeInMillis) { - if (!_focused) { - return false; - } - - // First see if we need to render the text - if (_textChanged) { - // Blit the text using the RenderManager - Common::Rect destRect = _engine->getRenderManager()->renderTextToWorkingWindow(_key, _currentInputText, _textStyle.font, _textRectangle.left, _textRectangle.top, _textStyle.color, _textRectangle.width()); - - _cursorOffset = destRect.left - _textRectangle.left; - } - - // Render the next frame of the animation - // TODO: Implement animation handling - - return false; -} - -} // End of namespace ZVision diff --git a/engines/zvision/input_control.h b/engines/zvision/input_control.h deleted file mode 100644 index aab2c991dc..0000000000 --- a/engines/zvision/input_control.h +++ /dev/null @@ -1,60 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef ZVISION_INPUT_CONTROL_H -#define ZVISION_INPUT_CONTROL_H - -#include "zvision/control.h" -#include "zvision/string_manager.h" - -#include "common/rect.h" - - -namespace ZVision { - -class InputControl : public Control { -public: - InputControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); - -private: - Common::Rect _textRectangle; - Common::Rect _headerRectangle; - StringManager::TextStyle _textStyle; - uint32 _nextTabstop; - Common::String _cursorAnimationFileName; - bool _focused; - - Common::String _currentInputText; - bool _textChanged; - uint _cursorOffset; - -public: - void focus() { _focused = true; } - void unfocus() { _focused = false; } - void onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); - void onKeyDown(Common::KeyState keyState); - bool process(uint32 deltaTimeInMillis); -}; - -} // End of namespace ZVision - -#endif diff --git a/engines/zvision/inventory/inventory_manager.h b/engines/zvision/inventory/inventory_manager.h new file mode 100644 index 0000000000..ae6d116b18 --- /dev/null +++ b/engines/zvision/inventory/inventory_manager.h @@ -0,0 +1,28 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ZVISION_INVENTORY_MANAGER_H +#define ZVISION_INVENTORY_MANAGER_H + +// TODO: Implement InventoryManager + +#endif diff --git a/engines/zvision/inventory_manager.h b/engines/zvision/inventory_manager.h deleted file mode 100644 index ae6d116b18..0000000000 --- a/engines/zvision/inventory_manager.h +++ /dev/null @@ -1,28 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef ZVISION_INVENTORY_MANAGER_H -#define ZVISION_INVENTORY_MANAGER_H - -// TODO: Implement InventoryManager - -#endif diff --git a/engines/zvision/lever_control.cpp b/engines/zvision/lever_control.cpp deleted file mode 100644 index 557dec0a83..0000000000 --- a/engines/zvision/lever_control.cpp +++ /dev/null @@ -1,402 +0,0 @@ -/* 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 "zvision/lever_control.h" - -#include "zvision/zvision.h" -#include "zvision/script_manager.h" -#include "zvision/render_manager.h" -#include "zvision/cursor_manager.h" -#include "zvision/rlf_animation.h" -#include "zvision/zork_avi_decoder.h" -#include "zvision/utility.h" - -#include "common/stream.h" -#include "common/file.h" -#include "common/tokenizer.h" -#include "common/system.h" - -#include "graphics/surface.h" - - -namespace ZVision { - -LeverControl::LeverControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) - : Control(engine, key), - _frameInfo(0), - _frameCount(0), - _startFrame(0), - _currentFrame(0), - _lastRenderedFrame(0), - _mouseIsCaptured(false), - _isReturning(false), - _accumulatedTime(0), - _returnRoutesCurrentFrame(0) { - - // Loop until we find the closing brace - Common::String line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - - while (!stream.eos() && !line.contains('}')) { - if (line.matchString("*descfile*", true)) { - char levFileName[25]; - sscanf(line.c_str(), "%*[^(](%25[^)])", levFileName); - - parseLevFile(levFileName); - } else if (line.matchString("*cursor*", true)) { - char cursorName[25]; - sscanf(line.c_str(), "%*[^(](%25[^)])", cursorName); - - _cursorName = Common::String(cursorName); - } - - line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - } - - renderFrame(_currentFrame); -} - -LeverControl::~LeverControl() { - if (_fileType == AVI) { - delete _animation.avi; - } else if (_fileType == RLF) { - delete _animation.rlf; - } - - delete[] _frameInfo; -} - -void LeverControl::parseLevFile(const Common::String &fileName) { - Common::File file; - if (!file.open(fileName)) { - warning("LEV file %s could could be opened", fileName.c_str()); - return; - } - - Common::String line = file.readLine(); - - while (!file.eos()) { - if (line.matchString("*animation_id*", true)) { - // Not used - } else if (line.matchString("*filename*", true)) { - char fileNameBuffer[25]; - sscanf(line.c_str(), "%*[^:]:%25[^~]~", fileNameBuffer); - - Common::String animationFileName(fileNameBuffer); - - if (animationFileName.hasSuffix(".avi")) { - _animation.avi = new ZorkAVIDecoder(); - _animation.avi->loadFile(animationFileName); - _fileType = AVI; - } else if (animationFileName.hasSuffix(".rlf")) { - _animation.rlf = new RlfAnimation(animationFileName, false); - _fileType = RLF; - } - } else if (line.matchString("*skipcolor*", true)) { - // Not used - } else if (line.matchString("*anim_coords*", true)) { - int left, top, right, bottom; - sscanf(line.c_str(), "%*[^:]:%d %d %d %d~", &left, &top, &right, &bottom); - - _animationCoords.left = left; - _animationCoords.top = top; - _animationCoords.right = right; - _animationCoords.bottom = bottom; - } else if (line.matchString("*mirrored*", true)) { - uint mirrored; - sscanf(line.c_str(), "%*[^:]:%u~", &mirrored); - - _mirrored = mirrored == 0 ? false : true; - } else if (line.matchString("*frames*", true)) { - sscanf(line.c_str(), "%*[^:]:%u~", &_frameCount); - - _frameInfo = new FrameInfo[_frameCount]; - } else if (line.matchString("*elsewhere*", true)) { - // Not used - } else if (line.matchString("*out_of_control*", true)) { - // Not used - } else if (line.matchString("*start_pos*", true)) { - sscanf(line.c_str(), "%*[^:]:%u~", &_startFrame); - _currentFrame = _startFrame; - } else if (line.matchString("*hotspot_deltas*", true)) { - uint x; - uint y; - sscanf(line.c_str(), "%*[^:]:%u %u~", &x, &y); - - _hotspotDelta.x = x; - _hotspotDelta.y = y; - } else { - uint frameNumber; - uint x, y; - - if (sscanf(line.c_str(), "%u:%u %u", &frameNumber, &x, &y) == 3) { - _frameInfo[frameNumber].hotspot.left = x; - _frameInfo[frameNumber].hotspot.top = y; - _frameInfo[frameNumber].hotspot.right = x + _hotspotDelta.x; - _frameInfo[frameNumber].hotspot.bottom = y + _hotspotDelta.y; - } - - Common::StringTokenizer tokenizer(line, " ^=()"); - tokenizer.nextToken(); - tokenizer.nextToken(); - - Common::String token = tokenizer.nextToken(); - while (!tokenizer.empty()) { - if (token == "D") { - token = tokenizer.nextToken(); - - uint angle; - uint toFrame; - sscanf(token.c_str(), "%u,%u", &toFrame, &angle); - - _frameInfo[frameNumber].directions.push_back(Direction(angle, toFrame)); - } else if (token.hasPrefix("P")) { - // Format: P( to ) - tokenizer.nextToken(); - tokenizer.nextToken(); - token = tokenizer.nextToken(); - uint to = atoi(token.c_str()); - - _frameInfo[frameNumber].returnRoute.push_back(to); - } - - token = tokenizer.nextToken(); - } - } - - line = file.readLine(); - } -} - -void LeverControl::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - if (!_enabled) { - return; - } - - if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) { - _mouseIsCaptured = true; - _lastMousePos = backgroundImageSpacePos; - } -} - -void LeverControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - if (!_enabled) { - return; - } - - if (_mouseIsCaptured) { - _mouseIsCaptured = false; - _engine->getScriptManager()->setStateValue(_key, _currentFrame); - - _isReturning = true; - _returnRoutesCurrentProgress = _frameInfo[_currentFrame].returnRoute.begin(); - _returnRoutesCurrentFrame = _currentFrame; - } -} - -bool LeverControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - if (!_enabled) { - return false; - } - - bool cursorWasChanged = false; - - if (_mouseIsCaptured) { - // Make sure the square distance between the last point and the current point is greater than 64 - // This is a heuristic. This determines how responsive the lever is to mouse movement. - // TODO: Fiddle with the heuristic to get a good lever responsiveness 'feel' - if (_lastMousePos.sqrDist(backgroundImageSpacePos) >= 64) { - int angle = calculateVectorAngle(_lastMousePos, backgroundImageSpacePos); - _lastMousePos = backgroundImageSpacePos; - - for (Common::List::iterator iter = _frameInfo[_currentFrame].directions.begin(); iter != _frameInfo[_currentFrame].directions.end(); ++iter) { - if (angle >= (int)iter->angle - ANGLE_DELTA && angle <= (int)iter->angle + ANGLE_DELTA) { - _currentFrame = iter->toFrame; - renderFrame(_currentFrame); - break; - } - } - } - } else if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) { - _engine->getCursorManager()->changeCursor(_cursorName); - cursorWasChanged = true; - } - - return cursorWasChanged; -} - -bool LeverControl::process(uint32 deltaTimeInMillis) { - if (!_enabled) { - return false; - } - - if (_isReturning) { - _accumulatedTime += deltaTimeInMillis; - while (_accumulatedTime >= ANIMATION_FRAME_TIME) { - _accumulatedTime -= ANIMATION_FRAME_TIME; - if (_returnRoutesCurrentFrame == *_returnRoutesCurrentProgress) { - _returnRoutesCurrentProgress++; - } - if (_returnRoutesCurrentProgress == _frameInfo[_currentFrame].returnRoute.end()) { - _isReturning = false; - _currentFrame = _returnRoutesCurrentFrame; - return false; - } - - uint toFrame = *_returnRoutesCurrentProgress; - if (_returnRoutesCurrentFrame < toFrame) { - _returnRoutesCurrentFrame++; - } else if (_returnRoutesCurrentFrame > toFrame) { - _returnRoutesCurrentFrame--; - } - - _engine->getScriptManager()->setStateValue(_key, _returnRoutesCurrentFrame); - renderFrame(_returnRoutesCurrentFrame); - } - } - - return false; -} - -int LeverControl::calculateVectorAngle(const Common::Point &pointOne, const Common::Point &pointTwo) { - // Check for the easy angles first - if (pointOne.x == pointTwo.x && pointOne.y == pointTwo.y) - return -1; // This should never happen - else if (pointOne.x == pointTwo.x) { - if (pointTwo.y < pointOne.y) - return 90; - else - return 270; - } else if (pointOne.y == pointTwo.y) { - if (pointTwo.x > pointOne.x) - return 0; - else - return 180; - } else { - // Calculate the angle with trig - int16 xDist = pointTwo.x - pointOne.x; - int16 yDist = pointTwo.y - pointOne.y; - - // Calculate the angle using arctan - // Then convert to degrees. (180 / 3.14159 = 57.2958) - int angle = int(atan((float)yDist / (float)xDist) * 57); - - // Calculate what quadrant pointTwo is in - uint quadrant = ((yDist > 0 ? 1 : 0) << 1) | (xDist < 0 ? 1 : 0); - - // Explanation of quadrants: - // - // yDist > 0 | xDist < 0 | Quadrant number - // 0 | 0 | 0 - // 0 | 1 | 1 - // 1 | 0 | 2 - // 1 | 1 | 3 - // - // Note: I know this doesn't line up with traditional mathematical quadrants - // but doing it this way allows you can use a switch and is a bit cleaner IMO. - // - // The graph below shows the 4 quadrants pointTwo can end up in as well - // as what the angle as calculated above refers to. - // Note: The calculated angle in quadrants 0 and 3 is negative - // due to arctan(-x) = -theta - // - // Origin => (pointOne.x, pointOne.y) - // * => (pointTwo.x, pointTwo.y) - // - // 90 | - // ^ | - // * | * | - // \ | / | - // \ | / | - // \ | / | - // Quadrant 1 \ | / Quadrant 0 | - // \ | / | - // \ | / | - // angle ( \|/ ) -angle | - // 180 <----------------------------------------> 0 | - // -angle ( /|\ ) angle | - // / | \ | - // / | \ | - // Quadrant 3 / | \ Quadrant 2 | - // / | \ | - // / | \ | - // / | \ | - // * | * | - // ^ | - // 270 | - - // Convert the local angles to unit circle angles - switch (quadrant) { - case 0: - angle = 180 + angle; - break; - case 1: - // Do nothing - break; - case 2: - angle = 180 + angle; - break; - case 3: - angle = 360 + angle; - break; - } - - return angle; - } -} - -void LeverControl::renderFrame(uint frameNumber) { - if (frameNumber == 0) { - _lastRenderedFrame = frameNumber; - } else if (frameNumber < _lastRenderedFrame && _mirrored) { - _lastRenderedFrame = frameNumber; - frameNumber = (_frameCount * 2) - frameNumber - 1; - } else { - _lastRenderedFrame = frameNumber; - } - - const uint16 *frameData = 0; - int x = _animationCoords.left; - int y = _animationCoords.top; - int width = 0; - int height = 0; - - if (_fileType == RLF) { - // getFrameData() will automatically optimize to getNextFrame() / getPreviousFrame() if it can - frameData = (const uint16 *)_animation.rlf->getFrameData(frameNumber)->getPixels(); - width = _animation.rlf->width(); // Use the animation width instead of _animationCoords.width() - height = _animation.rlf->height(); // Use the animation height instead of _animationCoords.height() - } else if (_fileType == AVI) { - _animation.avi->seekToFrame(frameNumber); - const Graphics::Surface *surface = _animation.avi->decodeNextFrame(); - frameData = (const uint16 *)surface->getPixels(); - width = surface->w; - height = surface->h; - } - - _engine->getRenderManager()->copyRectToWorkingWindow(frameData, x, y, width, width, height); -} - -} // End of namespace ZVision diff --git a/engines/zvision/lever_control.h b/engines/zvision/lever_control.h deleted file mode 100644 index 8ef8f06fec..0000000000 --- a/engines/zvision/lever_control.h +++ /dev/null @@ -1,127 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef ZVISION_LEVER_CONTROL_H -#define ZVISION_LEVER_CONTROL_H - -#include "zvision/control.h" - -#include "common/list.h" -#include "common/rect.h" - - -namespace ZVision { - -class ZorkAVIDecoder; -class RlfAnimation; - -class LeverControl : public Control { -public: - LeverControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); - ~LeverControl(); - -private: - enum FileType { - RLF = 1, - AVI = 2 - }; - - struct Direction { - Direction(uint a, uint t) : angle(a), toFrame(t) {} - - uint angle; - uint toFrame; - }; - - struct FrameInfo { - Common::Rect hotspot; - Common::List directions; - Common::List returnRoute; - }; - - enum { - ANGLE_DELTA = 30, // How far off a mouse angle can be and still be considered valid. This is in both directions, so the total buffer zone is (2 * ANGLE_DELTA) - ANIMATION_FRAME_TIME = 30 // In millis - }; - -private: - union { - RlfAnimation *rlf; - ZorkAVIDecoder *avi; - } _animation; - FileType _fileType; - - Common::String _cursorName; - Common::Rect _animationCoords; - bool _mirrored; - uint _frameCount; - uint _startFrame; - Common::Point _hotspotDelta; - FrameInfo *_frameInfo; - - uint _currentFrame; - uint _lastRenderedFrame; - bool _mouseIsCaptured; - bool _isReturning; - Common::Point _lastMousePos; - Common::List::iterator _returnRoutesCurrentProgress; - uint _returnRoutesCurrentFrame; - uint32 _accumulatedTime; - -public: - void onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); - void onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); - bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); - bool process(uint32 deltaTimeInMillis); - -private: - void parseLevFile(const Common::String &fileName); - /** - * Calculates the angle a vector makes with the negative y-axis - * - * 90 - * pointTwo * ^ - * \ | - * \ | - * \ | - * \ | - * angle ( \|pointOne - * 180 <-----------*-----------> 0 - * | - * | - * | - * | - * | - * ^ - * 270 - * - * @param pointOne The origin of the vector - * @param pointTwo The end of the vector - * @return The angle the vector makes with the negative y-axis - */ - static int calculateVectorAngle(const Common::Point &pointOne, const Common::Point &pointTwo); - void renderFrame(uint frameNumber); -}; - -} // End of namespace ZVision - -#endif diff --git a/engines/zvision/lzss_read_stream.cpp b/engines/zvision/lzss_read_stream.cpp deleted file mode 100644 index bbbda6f526..0000000000 --- a/engines/zvision/lzss_read_stream.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* 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 "zvision/lzss_read_stream.h" - - -namespace ZVision { - -LzssReadStream::LzssReadStream(Common::SeekableReadStream *source) - : _source(source), - // It's convention to set the starting cursor position to blockSize - 16 - _windowCursor(0x0FEE), - _eosFlag(false) { - // Clear the window to null - memset(_window, 0, BLOCK_SIZE); -} - -uint32 LzssReadStream::decompressBytes(byte *destination, uint32 numberOfBytes) { - uint32 destinationCursor = 0; - - while (destinationCursor < numberOfBytes) { - byte flagbyte = _source->readByte(); - if (_source->eos()) - break; - uint mask = 1; - - for (int i = 0; i < 8; ++i) { - if ((flagbyte & mask) == mask) { - byte data = _source->readByte(); - if (_source->eos()) { - return destinationCursor; - } - - _window[_windowCursor] = data; - destination[destinationCursor++] = data; - - // Increment and wrap the window cursor - _windowCursor = (_windowCursor + 1) & 0xFFF; - } else { - byte low = _source->readByte(); - if (_source->eos()) { - return destinationCursor; - } - - byte high = _source->readByte(); - if (_source->eos()) { - return destinationCursor; - } - - uint16 length = (high & 0xF) + 2; - uint16 offset = low | ((high & 0xF0)<<4); - - for(int j = 0; j <= length; ++j) { - byte temp = _window[(offset + j) & 0xFFF]; - _window[_windowCursor] = temp; - destination[destinationCursor++] = temp; - _windowCursor = (_windowCursor + 1) & 0xFFF; - } - } - - mask = mask << 1; - } - } - - return destinationCursor; -} - -bool LzssReadStream::eos() const { - return _eosFlag; -} - -uint32 LzssReadStream::read(void *dataPtr, uint32 dataSize) { - uint32 bytesRead = decompressBytes(static_cast(dataPtr), dataSize); - if (bytesRead < dataSize) { - // Flag that we're at EOS - _eosFlag = true; - } - - return dataSize; -} - -} // End of namespace ZVision diff --git a/engines/zvision/lzss_read_stream.h b/engines/zvision/lzss_read_stream.h deleted file mode 100644 index f6b1eb1a65..0000000000 --- a/engines/zvision/lzss_read_stream.h +++ /dev/null @@ -1,72 +0,0 @@ -/* ScummVM - Graphic Adventure Engine -* -* ScummVM is the legal property of its developers, whose names -* are too numerous to list here. Please refer to the COPYRIGHT -* file distributed with this source distribution. -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* as published by the Free Software Foundation; either version 2 -* of the License, or (at your option) any later version. - -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. - -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -* -*/ - -#ifndef ZVISION_LZSS_STREAM_H -#define ZVISION_LZSS_STREAM_H - -#include "common/stream.h" -#include "common/array.h" - - -namespace Common { -class SeekableReadStream; -} - -namespace ZVision { - -class LzssReadStream : public Common::ReadStream { -public: - /** - * A class that decompresses LZSS data and implements ReadStream for easy access - * to the decompiled data. - * - * @param source The source data - */ - LzssReadStream(Common::SeekableReadStream *source); - -private: - enum { - BLOCK_SIZE = 0x1000 - }; - -private: - Common::SeekableReadStream *_source; - byte _window[BLOCK_SIZE]; - uint _windowCursor; - bool _eosFlag; - -public: - bool eos() const; - uint32 read(void *dataPtr, uint32 dataSize); - -private: - /** - * Decompress the next from the source stream. Or until EOS - * - * @param numberOfBytes How many bytes to decompress. This is a count of source bytes, not destination bytes - */ - uint32 decompressBytes(byte* destination, uint32 numberOfBytes); -}; - -} - -#endif diff --git a/engines/zvision/menu.h b/engines/zvision/menu.h deleted file mode 100644 index affc69abd5..0000000000 --- a/engines/zvision/menu.h +++ /dev/null @@ -1,28 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef ZVISION_MENU_H -#define ZVISION_MENU_H - -// TODO: Implement MenuHandler - -#endif diff --git a/engines/zvision/push_toggle_control.cpp b/engines/zvision/push_toggle_control.cpp deleted file mode 100644 index 689311ba5b..0000000000 --- a/engines/zvision/push_toggle_control.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* 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 "zvision/push_toggle_control.h" - -#include "zvision/zvision.h" -#include "zvision/script_manager.h" -#include "zvision/cursor_manager.h" -#include "zvision/utility.h" - -#include "common/stream.h" - - -namespace ZVision { - -PushToggleControl::PushToggleControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) - : Control(engine, key) { - // Loop until we find the closing brace - Common::String line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - - while (!stream.eos() && !line.contains('}')) { - if (line.matchString("*_hotspot*", true)) { - uint x; - uint y; - uint width; - uint height; - - sscanf(line.c_str(), "%*[^(](%u,%u,%u,%u)", &x, &y, &width, &height); - - _hotspot = Common::Rect(x, y, x + width, y + height); - } else if (line.matchString("cursor*", true)) { - char nameBuffer[25]; - - sscanf(line.c_str(), "%*[^(](%25[^)])", nameBuffer); - - _hoverCursor = Common::String(nameBuffer); - } - - line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - } - - if (_hotspot.isEmpty() || _hoverCursor.empty()) { - warning("Push_toggle cursor %u was parsed incorrectly", key); - } -} - -PushToggleControl::~PushToggleControl() { - // Clear the state value back to 0 - _engine->getScriptManager()->setStateValue(_key, 0); -} - -void PushToggleControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - if (!_enabled) { - return; - } - - if (_hotspot.contains(backgroundImageSpacePos)) { - _engine->getScriptManager()->setStateValue(_key, 1); - } -} - -bool PushToggleControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - if (!_enabled) { - return false; - } - - if (_hotspot.contains(backgroundImageSpacePos)) { - _engine->getCursorManager()->changeCursor(_hoverCursor); - return true; - } - - return false; -} - -} // End of namespace ZVision diff --git a/engines/zvision/push_toggle_control.h b/engines/zvision/push_toggle_control.h deleted file mode 100644 index 2a407cada9..0000000000 --- a/engines/zvision/push_toggle_control.h +++ /dev/null @@ -1,67 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef ZVISION_PUSH_TOGGLE_CONTROL_H -#define ZVISION_PUSH_TOGGLE_CONTROL_H - -#include "zvision/control.h" - -#include "common/rect.h" - - -namespace ZVision { - -class PushToggleControl : public Control { -public: - PushToggleControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); - ~PushToggleControl(); - - /** - * Called when LeftMouse is lifted. Calls ScriptManager::setStateValue(_key, 1); - * - * @param screenSpacePos The position of the mouse in screen space - * @param backgroundImageSpacePos The position of the mouse in background image space - */ - void onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); - /** - * Called on every MouseMove. Tests if the mouse is inside _hotspot, and if so, sets the cursor. - * - * @param engine The base engine - * @param screenSpacePos The position of the mouse in screen space - * @param backgroundImageSpacePos The position of the mouse in background image space - * @return Was the cursor changed? - */ - bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); - -private: - /** - * The area that will trigger the event - * This is in image space coordinates, NOT screen space - */ - Common::Rect _hotspot; - /** The cursor to use when hovering over _hotspot */ - Common::String _hoverCursor; -}; - -} // End of namespace ZVision - -#endif diff --git a/engines/zvision/puzzle.h b/engines/zvision/puzzle.h deleted file mode 100644 index d468da1886..0000000000 --- a/engines/zvision/puzzle.h +++ /dev/null @@ -1,74 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef ZVISION_PUZZLE_H -#define ZVISION_PUZZLE_H - -#include "zvision/actions.h" - -#include "common/list.h" -#include "common/ptr.h" - - -namespace ZVision { - -struct Puzzle { - Puzzle() : key(0) {} - - ~Puzzle() { - for (Common::List::iterator iter = resultActions.begin(); iter != resultActions.end(); ++iter) { - delete *iter; - } - } - - /** How criteria should be decided */ - enum CriteriaOperator { - EQUAL_TO, - NOT_EQUAL_TO, - GREATER_THAN, - LESS_THAN - }; - - /** Criteria for a Puzzle result to be fired */ - struct CriteriaEntry { - /** The key of a global state */ - uint32 key; - /** - * What we're comparing the value of the global state against - * This can either be a pure value or it can be the key of another global state - */ - uint32 argument; - /** How to do the comparison */ - CriteriaOperator criteriaOperator; - /** Whether 'argument' is the key of a global state (true) or a pure value (false) */ - bool argumentIsAKey; - }; - - uint32 key; - Common::List > criteriaList; - // This has to be list of pointers because ResultAction is abstract - Common::List resultActions; -}; - -} // End of namespace ZVision - -#endif diff --git a/engines/zvision/render_manager.cpp b/engines/zvision/render_manager.cpp deleted file mode 100644 index af8ca7fd64..0000000000 --- a/engines/zvision/render_manager.cpp +++ /dev/null @@ -1,526 +0,0 @@ -/* 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 "zvision/render_manager.h" - -#include "zvision/lzss_read_stream.h" - -#include "common/file.h" -#include "common/system.h" -#include "common/stream.h" - -#include "engines/util.h" - -#include "graphics/decoders/tga.h" - - -namespace ZVision { - -RenderManager::RenderManager(OSystem *system, uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow, const Graphics::PixelFormat pixelFormat) - : _system(system), - _workingWidth(workingWindow.width()), - _workingHeight(workingWindow.height()), - _screenCenterX(_workingWidth / 2), - _screenCenterY(_workingHeight / 2), - _workingWindow(workingWindow), - _pixelFormat(pixelFormat), - _backgroundWidth(0), - _backgroundHeight(0), - _backgroundInverseVelocity(0), - _backgroundOffset(0, 0), - _accumulatedVelocityMilliseconds(0), - _renderTable(_workingWidth, _workingHeight) { - - _workingWindowBuffer.create(_workingWidth, _workingHeight, _pixelFormat); - _backBuffer.create(windowWidth, windowHeight, pixelFormat); -} - -RenderManager::~RenderManager() { - _workingWindowBuffer.free(); - _currentBackground.free(); - _backBuffer.free(); - - for (AlphaEntryMap::iterator iter = _alphaDataEntries.begin(); iter != _alphaDataEntries.end(); ++iter) { - iter->_value.data->free(); - delete iter->_value.data; - } -} - -void RenderManager::update(uint deltaTimeInMillis) { - // An inverse velocity of 0 would be infinitely fast, so we'll let 0 mean no velocity. - if (_backgroundInverseVelocity != 0) { - _accumulatedVelocityMilliseconds += deltaTimeInMillis; - - uint absVelocity = uint(abs(_backgroundInverseVelocity)); - - int numberOfSteps = 0; - while (_accumulatedVelocityMilliseconds >= absVelocity) { - _accumulatedVelocityMilliseconds -= absVelocity; - numberOfSteps++; - } - - // Choose the direction of movement using the sign of the velocity - moveBackground(_backgroundInverseVelocity < 0 ? -numberOfSteps : numberOfSteps); - } -} - -void RenderManager::renderBackbufferToScreen() { - if (!_workingWindowDirtyRect.isEmpty()) { - RenderTable::RenderState state = _renderTable.getRenderState(); - if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { - _renderTable.mutateImage((uint16 *)_workingWindowBuffer.getPixels(), (uint16 *)_backBuffer.getBasePtr(_workingWindow.left + _workingWindowDirtyRect.left, _workingWindow.top + _workingWindowDirtyRect.top), _backBuffer.w, _workingWindowDirtyRect); - } else { - _backBuffer.copyRectToSurface(_workingWindowBuffer.getBasePtr(_workingWindowDirtyRect.left, _workingWindowDirtyRect.top), _workingWindowBuffer.pitch, _workingWindow.left + _workingWindowDirtyRect.left, _workingWindow.top + _workingWindowDirtyRect.top, _workingWindowDirtyRect.width(), _workingWindowDirtyRect.height()); - } - - // Translate the working window dirty rect to screen coords - _workingWindowDirtyRect.translate(_workingWindow.left, _workingWindow.top); - // Then extend the backbuffer dirty rect to contain it - if (_backBufferDirtyRect.isEmpty()) { - _backBufferDirtyRect = _workingWindowDirtyRect; - } else { - _backBufferDirtyRect.extend(_workingWindowDirtyRect); - } - - // Clear the dirty rect - _workingWindowDirtyRect = Common::Rect(); - } - - // TODO: Add menu rendering - - // Render alpha entries - processAlphaEntries(); - - if (!_backBufferDirtyRect.isEmpty()) { - _system->copyRectToScreen(_backBuffer.getBasePtr(_backBufferDirtyRect.left, _backBufferDirtyRect.top), _backBuffer.pitch, _backBufferDirtyRect.left, _backBufferDirtyRect.top, _backBufferDirtyRect.width(), _backBufferDirtyRect.height()); - _backBufferDirtyRect = Common::Rect(); - } -} - -void RenderManager::processAlphaEntries() { - // TODO: Add dirty rectangling support. AKA only draw an entry if the _backbufferDirtyRect intersects/contains the entry Rect - - for (AlphaEntryMap::iterator iter = _alphaDataEntries.begin(); iter != _alphaDataEntries.end(); ++iter) { - uint32 destOffset = 0; - uint32 sourceOffset = 0; - uint16 *backbufferPtr = (uint16 *)_backBuffer.getBasePtr(iter->_value.destX + _workingWindow.left, iter->_value.destY + _workingWindow.top); - uint16 *entryPtr = (uint16 *)iter->_value.data->getPixels(); - - for (int32 y = 0; y < iter->_value.height; ++y) { - for (int32 x = 0; x < iter->_value.width; ++x) { - uint16 color = entryPtr[sourceOffset + x]; - if (color != iter->_value.alphaColor) { - backbufferPtr[destOffset + x] = color; - } - } - - destOffset += _backBuffer.w; - sourceOffset += iter->_value.width; - } - - if (_backBufferDirtyRect.isEmpty()) { - _backBufferDirtyRect = Common::Rect(iter->_value.destX + _workingWindow.left, iter->_value.destY + _workingWindow.top, iter->_value.destX + _workingWindow.left + iter->_value.width, iter->_value.destY + _workingWindow.top + iter->_value.height); - } else { - _backBufferDirtyRect.extend(Common::Rect(iter->_value.destX + _workingWindow.left, iter->_value.destY + _workingWindow.top, iter->_value.destX + _workingWindow.left + iter->_value.width, iter->_value.destY + _workingWindow.top + iter->_value.height)); - } - } -} - -void RenderManager::clearWorkingWindowTo555Color(uint16 color) { - uint32 workingWindowSize = _workingWidth * _workingHeight; - byte r, g, b; - Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0).colorToRGB(color, r, g, b); - uint16 colorIn565 = _pixelFormat.RGBToColor(r, g, b); - uint16 *bufferPtr = (uint16 *)_workingWindowBuffer.getPixels(); - - for (uint32 i = 0; i < workingWindowSize; ++i) { - bufferPtr[i] = colorIn565; - } -} - -void RenderManager::renderSubRectToScreen(Graphics::Surface &surface, int16 destinationX, int16 destinationY, bool wrap) { - int16 subRectX = 0; - int16 subRectY = 0; - - // Take care of negative destinations - if (destinationX < 0) { - subRectX = -destinationX; - destinationX = 0; - } else if (destinationX >= surface.w) { - // Take care of extreme positive destinations - destinationX -= surface.w; - } - - // Take care of negative destinations - if (destinationY < 0) { - subRectY = -destinationY; - destinationY = 0; - } else if (destinationY >= surface.h) { - // Take care of extreme positive destinations - destinationY -= surface.h; - } - - if (wrap) { - _backgroundWidth = surface.w; - _backgroundHeight = surface.h; - - if (destinationX > 0) { - // Move destinationX to 0 - subRectX = surface.w - destinationX; - destinationX = 0; - } - - if (destinationY > 0) { - // Move destinationY to 0 - subRectY = surface.h - destinationY; - destinationY = 0; - } - } - - // Clip subRect to working window bounds - Common::Rect subRect(subRectX, subRectY, subRectX + _workingWidth, subRectY + _workingHeight); - - if (!wrap) { - // Clip to image bounds - subRect.clip(surface.w, surface.h); - } - - // Check destRect for validity - if (!subRect.isValidRect() || subRect.isEmpty()) - return; - - copyRectToWorkingWindow((const uint16 *)surface.getBasePtr(subRect.left, subRect.top), destinationX, destinationY, surface.w, subRect.width(), subRect.height()); -} - -void RenderManager::renderImageToScreen(const Common::String &fileName, int16 destinationX, int16 destinationY, bool wrap) { - Graphics::Surface surface; - readImageToSurface(fileName, surface); - - renderSubRectToScreen(surface, destinationX, destinationY, wrap); -} - -void RenderManager::renderImageToScreen(Graphics::Surface &surface, int16 destinationX, int16 destinationY, bool wrap) { - renderSubRectToScreen(surface, destinationX, destinationY, wrap); -} - -void RenderManager::readImageToSurface(const Common::String &fileName, Graphics::Surface &destination) { - Common::File file; - - if (!file.open(fileName)) { - warning("Could not open file %s", fileName.c_str()); - return; - } - - // Read the magic number - // Some files are true TGA, while others are TGZ - uint32 fileType = file.readUint32BE(); - - uint32 imageWidth; - uint32 imageHeight; - Graphics::TGADecoder tga; - uint16 *buffer; - bool isTransposed = _renderTable.getRenderState() == RenderTable::PANORAMA; - // All ZVision images are in RGB 555 - Graphics::PixelFormat pixelFormat555 = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); - destination.format = pixelFormat555; - - bool isTGZ; - - // Check for TGZ files - if (fileType == MKTAG('T', 'G', 'Z', '\0')) { - isTGZ = true; - - // TGZ files have a header and then Bitmap data that is compressed with LZSS - uint32 decompressedSize = file.readSint32LE(); - imageWidth = file.readSint32LE(); - imageHeight = file.readSint32LE(); - - LzssReadStream lzssStream(&file); - buffer = (uint16 *)(new uint16[decompressedSize]); - lzssStream.read(buffer, decompressedSize); - } else { - isTGZ = false; - - // Reset the cursor - file.seek(0); - - // Decode - if (!tga.loadStream(file)) { - warning("Error while reading TGA image"); - return; - } - - Graphics::Surface tgaSurface = *(tga.getSurface()); - imageWidth = tgaSurface.w; - imageHeight = tgaSurface.h; - - buffer = (uint16 *)tgaSurface.getPixels(); - } - - // Flip the width and height if transposed - if (isTransposed) { - uint16 temp = imageHeight; - imageHeight = imageWidth; - imageWidth = temp; - } - - // If the destination internal buffer is the same size as what we're copying into it, - // there is no need to free() and re-create - if (imageWidth != destination.w || imageHeight != destination.h) { - destination.create(imageWidth, imageHeight, pixelFormat555); - } - - // If transposed, 'un-transpose' the data while copying it to the destination - // Otherwise, just do a simple copy - if (isTransposed) { - uint16 *dest = (uint16 *)destination.getPixels(); - - for (uint32 y = 0; y < imageHeight; ++y) { - uint32 columnIndex = y * imageWidth; - - for (uint32 x = 0; x < imageWidth; ++x) { - dest[columnIndex + x] = buffer[x * imageHeight + y]; - } - } - } else { - memcpy(destination.getPixels(), buffer, imageWidth * imageHeight * _pixelFormat.bytesPerPixel); - } - - // Cleanup - if (isTGZ) { - delete[] buffer; - } else { - tga.destroy(); - } - - // Convert in place to RGB 565 from RGB 555 - destination.convertToInPlace(_pixelFormat); -} - -void RenderManager::copyRectToWorkingWindow(const uint16 *buffer, int32 destX, int32 destY, int32 imageWidth, int32 width, int32 height) { - uint32 destOffset = 0; - uint32 sourceOffset = 0; - uint16 *workingWindowBufferPtr = (uint16 *)_workingWindowBuffer.getBasePtr(destX, destY); - - for (int32 y = 0; y < height; ++y) { - for (int32 x = 0; x < width; ++x) { - workingWindowBufferPtr[destOffset + x] = buffer[sourceOffset + x]; - } - - destOffset += _workingWidth; - sourceOffset += imageWidth; - } - - if (_workingWindowDirtyRect.isEmpty()) { - _workingWindowDirtyRect = Common::Rect(destX, destY, destX + width, destY + height); - } else { - _workingWindowDirtyRect.extend(Common::Rect(destX, destY, destX + width, destY + height)); - } - - // TODO: Remove this from release. It's here to make sure code that uses this function clips their destinations correctly - assert(_workingWindowDirtyRect.width() <= _workingWidth && _workingWindowDirtyRect.height() <= _workingHeight); -} - -void RenderManager::copyRectToWorkingWindow(const uint16 *buffer, int32 destX, int32 destY, int32 imageWidth, int32 width, int32 height, int16 alphaColor, uint32 idNumber) { - AlphaDataEntry entry; - entry.alphaColor = alphaColor; - entry.data = new Graphics::Surface(); - entry.data->create(width, height, _pixelFormat); - entry.destX = destX; - entry.destY = destY; - entry.width = width; - entry.height = height; - - uint32 sourceOffset = 0; - uint32 destOffset = 0; - uint16 *surfacePtr = (uint16 *)entry.data->getPixels(); - - for (int32 y = 0; y < height; ++y) { - for (int32 x = 0; x < width; ++x) { - surfacePtr[destOffset + x] = buffer[sourceOffset + x]; - } - - destOffset += width; - sourceOffset += imageWidth; - } - - _alphaDataEntries[idNumber] = entry; -} - -Common::Rect RenderManager::renderTextToWorkingWindow(uint32 idNumber, const Common::String &text, TruetypeFont *font, int destX, int destY, uint16 textColor, int maxWidth, int maxHeight, Graphics::TextAlign align, bool wrap) { - AlphaDataEntry entry; - entry.alphaColor = 0; - entry.destX = destX; - entry.destY = destY; - - // Draw the text to the working window - entry.data = font->drawTextToSurface(text, textColor, maxWidth, maxHeight, align, wrap); - entry.width = entry.data->w; - entry.height = entry.data->h; - - _alphaDataEntries[idNumber] = entry; - - return Common::Rect(destX, destY, destX + entry.width, destY + entry.height); -} - -const Common::Point RenderManager::screenSpaceToImageSpace(const Common::Point &point) { - // Convert from screen space to working window space - Common::Point newPoint(point - Common::Point(_workingWindow.left, _workingWindow.top)); - - RenderTable::RenderState state = _renderTable.getRenderState(); - if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { - newPoint = _renderTable.convertWarpedCoordToFlatCoord(newPoint); - } - - if (state == RenderTable::PANORAMA) { - newPoint -= (Common::Point(_screenCenterX, 0) - _backgroundOffset); - } else if (state == RenderTable::TILT) { - newPoint -= (Common::Point(0, _screenCenterY) - _backgroundOffset); - } - - if (newPoint.x < 0) - newPoint.x += _backgroundWidth; - else if (newPoint.x >= _backgroundWidth) - newPoint.x -= _backgroundWidth; - if (newPoint.y < 0) - newPoint.y += _backgroundHeight; - else if (newPoint.y >= _backgroundHeight) - newPoint.y -= _backgroundHeight; - - return newPoint; -} - -const Common::Point RenderManager::imageSpaceToWorkingWindowSpace(const Common::Point &point) { - Common::Point newPoint(point); - - RenderTable::RenderState state = _renderTable.getRenderState(); - if (state == RenderTable::PANORAMA) { - newPoint += (Common::Point(_screenCenterX, 0) - _backgroundOffset); - } else if (state == RenderTable::TILT) { - newPoint += (Common::Point(0, _screenCenterY) - _backgroundOffset); - } - - return newPoint; -} - -bool RenderManager::clipRectToWorkingWindow(Common::Rect &rect) { - if (!_workingWindow.contains(rect)) { - return false; - } - - // We can't clip against the actual working window rect because it's in screen space - // But rect is in working window space - rect.clip(_workingWidth, _workingHeight); - return true; -} - -RenderTable *RenderManager::getRenderTable() { - return &_renderTable; -} - -void RenderManager::setBackgroundImage(const Common::String &fileName) { - readImageToSurface(fileName, _currentBackground); - - moveBackground(0); -} - -void RenderManager::setBackgroundPosition(int offset) { - RenderTable::RenderState state = _renderTable.getRenderState(); - if (state == RenderTable::TILT) { - _backgroundOffset.x = 0; - _backgroundOffset.y = offset; - } else if (state == RenderTable::PANORAMA) { - _backgroundOffset.x = offset; - _backgroundOffset.y = 0; - } else { - _backgroundOffset.x = 0; - _backgroundOffset.y = 0; - } -} - -void RenderManager::setBackgroundVelocity(int velocity) { - // setBackgroundVelocity(0) will be called quite often, so make sure - // _backgroundInverseVelocity isn't already 0 to prevent an extraneous assignment - if (velocity == 0) { - if (_backgroundInverseVelocity != 0) { - _backgroundInverseVelocity = 0; - } - } else { - _backgroundInverseVelocity = 1000 / velocity; - } -} - -void RenderManager::moveBackground(int offset) { - RenderTable::RenderState state = _renderTable.getRenderState(); - if (state == RenderTable::TILT) { - _backgroundOffset += Common::Point(0, offset); - - _backgroundOffset.y = CLIP(_backgroundOffset.y, _screenCenterY, (int16)_backgroundHeight - _screenCenterY); - - renderImageToScreen(_currentBackground, 0, _screenCenterY - _backgroundOffset.y, true); - } else if (state == RenderTable::PANORAMA) { - _backgroundOffset += Common::Point(offset, 0); - - if (_backgroundOffset.x <= -_backgroundWidth) - _backgroundOffset.x += _backgroundWidth; - else if (_backgroundOffset.x >= _backgroundWidth) - _backgroundOffset.x -= _backgroundWidth; - - renderImageToScreen(_currentBackground, _screenCenterX - _backgroundOffset.x, 0, true); - } else { - renderImageToScreen(_currentBackground, 0, 0); - } -} - -uint32 RenderManager::getCurrentBackgroundOffset() { - RenderTable::RenderState state = _renderTable.getRenderState(); - - if (state == RenderTable::PANORAMA) { - return _backgroundOffset.x; - } else if (state == RenderTable::TILT) { - return _backgroundOffset.y; - } else { - return 0; - } -} - -Graphics::Surface *RenderManager::tranposeSurface(const Graphics::Surface *surface) { - Graphics::Surface *tranposedSurface = new Graphics::Surface(); - tranposedSurface->create(surface->h, surface->w, surface->format); - - const uint16 *source = (const uint16 *)surface->getPixels(); - uint16 *dest = (uint16 *)tranposedSurface->getPixels(); - - for (uint32 y = 0; y < tranposedSurface->h; ++y) { - uint32 columnIndex = y * tranposedSurface->w; - - for (uint32 x = 0; x < tranposedSurface->w; ++x) { - dest[columnIndex + x] = source[x * surface->w + y]; - } - } - - return tranposedSurface; -} - -} // End of namespace ZVision diff --git a/engines/zvision/render_manager.h b/engines/zvision/render_manager.h deleted file mode 100644 index 111bf6276c..0000000000 --- a/engines/zvision/render_manager.h +++ /dev/null @@ -1,328 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef ZVISION_RENDER_MANAGER_H -#define ZVISION_RENDER_MANAGER_H - -#include "zvision/render_table.h" -#include "zvision/truetype_font.h" - -#include "common/rect.h" -#include "common/hashmap.h" - -#include "graphics/surface.h" - - -class OSystem; - -namespace Common { -class String; -class SeekableReadStream; -} - -namespace Video { -class VideoDecoder; -} - -namespace ZVision { - -class RenderManager { -public: - RenderManager(OSystem *system, uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow, const Graphics::PixelFormat pixelFormat); - ~RenderManager(); - -private: - struct AlphaDataEntry { - Graphics::Surface *data; - uint16 alphaColor; - uint16 destX; - uint16 destY; - uint16 width; - uint16 height; - }; - - typedef Common::HashMap AlphaEntryMap; - -private: - OSystem *_system; - const Graphics::PixelFormat _pixelFormat; - - // A buffer the exact same size as the workingWindow - // This buffer stores everything un-warped, then does a warp at the end of the frame - Graphics::Surface _workingWindowBuffer; - // A buffer representing the entire screen. Any graphical updates are first done with this buffer - // before actually being blitted to the screen - Graphics::Surface _backBuffer; - // A list of Alpha Entries that need to be blitted to the backbuffer - AlphaEntryMap _alphaDataEntries; - - // A rectangle representing the portion of the working window where the pixels have been changed since last frame - Common::Rect _workingWindowDirtyRect; - // A rectangle representing the portion of the backbuffer where the pixels have been changed since last frame - Common::Rect _backBufferDirtyRect; - - /** Width of the working window. Saved to prevent extraneous calls to _workingWindow.width() */ - const int _workingWidth; - /** Height of the working window. Saved to prevent extraneous calls to _workingWindow.height() */ - const int _workingHeight; - /** Center of the screen in the x direction */ - const int _screenCenterX; - /** Center of the screen in the y direction */ - const int _screenCenterY; - - /** - * A Rectangle centered inside the actual window. All in-game coordinates - * are given in this coordinate space. Also, all images are clipped to the - * edges of this Rectangle - */ - const Common::Rect _workingWindow; - /** Used to warp the background image */ - RenderTable _renderTable; - - Graphics::Surface _currentBackground; - /** The (x1,y1) coordinates of the subRectangle of the background that is currently displayed on the screen */ - Common::Point _backgroundOffset; - /** The width of the current background image */ - uint16 _backgroundWidth; - /** The height of the current background image */ - uint16 _backgroundHeight; - - /** - * The "velocity" at which the background image is panning. We actually store the inverse of velocity (ms/pixel instead of pixels/ms) - * because it allows you to accumulate whole pixels 'steps' instead of rounding pixels every frame - */ - int _backgroundInverseVelocity; - /** Holds any 'leftover' milliseconds between frames */ - uint _accumulatedVelocityMilliseconds; - -public: - void initialize(); - /** - * Rotates the background image in accordance to the current _backgroundInverseVelocity - * - * @param deltaTimeInMillis The amount of time that has passed since the last frame - */ - void update(uint deltaTimeInMillis); - - /** - * Renders the current state of the backbuffer to the screen - */ - void renderBackbufferToScreen(); - - /** - * Renders all AlphaEntries to the backbuffer - */ - void processAlphaEntries(); - /** - * Clears the AlphaEntry list - */ - void clearAlphaEntries() { _alphaDataEntries.clear(); } - /** - * Removes a specific AlphaEntry from the list - * - * @param idNumber The id number identifing the AlphaEntry - */ - void removeAlphaEntry(uint32 idNumber) { _alphaDataEntries.erase(idNumber); } - - /** - * Copies a sub-rectangle of a buffer to the working window - * - * @param buffer The pixel data to copy to the working window - * @param destX The X destination in the working window where the subRect of data should be put - * @param destY The Y destination in the working window where the subRect of data should be put - * @param imageWidth The width of the source image - * @param width The width of the sub rectangle - * @param height The height of the sub rectangle - */ - void copyRectToWorkingWindow(const uint16 *buffer, int32 destX, int32 destY, int32 imageWidth, int32 width, int32 height); - /** - * Copies a sub-rectangle of a buffer to the working window with binary alpha support. - * - * @param buffer The pixel data to copy to the working window - * @param destX The X destination in the working window where the subRect of data should be put - * @param destY The Y destination in the working window where the subRect of data should be put - * @param imageWidth The width of the source image - * @param width The width of the sub rectangle - * @param height The height of the sub rectangle - * @param alphaColor The color to interpret as meaning 'transparent' - * @param idNumber A unique identifier for the data being copied over. - */ - void copyRectToWorkingWindow(const uint16 *buffer, int32 destX, int32 destY, int32 imageWidth, int32 width, int32 height, int16 alphaColor, uint32 idNumber); - - /** - * Renders the supplied text to the working window - * - * @param idNumber A unique identifier for the text - * @param text The text to be rendered - * @param font The font to use to render the text - * @param destX The X destination in the working window where the text should be rendered - * @param destY The Y destination in the working window where the text should be rendered - * @param textColor The color to render the text with (in RBG 565) - * @param maxWidth The max width the text should take up. - * @param maxHeight The max height the text should take up. - * @param align The alignment of the text within the bounds of maxWidth - * @param wrap If true, any words extending past maxWidth will wrap to a new line. If false, ellipses will be rendered to show that the text didn't fit - * @return A rectangle representing where the text was drawn in the working window - */ - Common::Rect renderTextToWorkingWindow(uint32 idNumber, const Common::String &text, TruetypeFont *font, int destX, int destY, uint16 textColor, int maxWidth, int maxHeight = -1, Graphics::TextAlign align = Graphics::kTextAlignLeft, bool wrap = true); - - /** - * Fills the entire workingWindow with the specified color. Internally, the color - * will be converted to RGB 565 and then blitted. - * - * @param color The color to fill the working window with. (In RGB 555) - */ - void clearWorkingWindowTo555Color(uint16 color); - - /** - * Blits the image or a portion of the image to the backbuffer. Actual screen updates won't happen until the end of the frame. - * The image will be clipped to fit inside the working window. Coords are in working window space, not screen space! - * - * @param fileName Name of the image file - * @param destinationX X position where the image should be put. Coords are in working window space, not screen space! - * @param destinationY Y position where the image should be put. Coords are in working window space, not screen space! - */ - void renderImageToScreen(const Common::String &fileName, int16 destinationX, int16 destinationY, bool wrap = false); - - /** - * Blits the image or a portion of the image to the backbuffer. Actual screen updates won't happen until the end of the frame. - * The image will be clipped to fit inside the working window. Coords are in working window space, not screen space! - * - * @param stream Surface to read the image data from - * @param destinationX X position where the image should be put. Coords are in working window space, not screen space! - * @param destinationY Y position where the image should be put. Coords are in working window space, not screen space! - */ - void renderImageToScreen(Graphics::Surface &surface, int16 destinationX, int16 destinationY, bool wrap = false); - - /** - * Sets the current background image to be used by the RenderManager and immediately - * blits it to the screen. (It won't show up until the end of the frame) - * - * @param fileName The name of the image file - */ - void setBackgroundImage(const Common::String &fileName); - - /** - * Set the background position (_backgroundOffset). If the current RenderState is PANORAMA, the offset - * will be in the horizontal direction. If the current RenderState is TILT, the offset will be in the - * vertical direction. - * - * This method will not render anything on the screen. So if nothing else is called that renders the - * background, the change won't be seen until next frame. - * - * @param offset The amount to offset the background - */ - void setBackgroundPosition(int offset); - - /** - * Set the background scroll velocity. Negative velocities correspond to left / up scrolling and - * positive velocities correspond to right / down scrolling - * - * @param velocity Velocity - */ - void setBackgroundVelocity(int velocity); - - /** - * Converts a point in screen coordinate space to image coordinate space - * - * @param point Point in screen coordinate space - * @return Point in image coordinate space - */ - const Common::Point screenSpaceToImageSpace(const Common::Point &point); - /** - * Converts a point in image coordinate space to ***PRE-WARP*** - * working window coordinate space - * - * @param point Point in image coordinate space - * @return Point in PRE-WARP working window coordinate space - */ - const Common::Point imageSpaceToWorkingWindowSpace(const Common::Point &point); - - /** - * Clip a rectangle to the working window. If it returns false, the original rect - * is not inside the working window. - * - * @param rect The rectangle to clip against the working window - * @return Is rect at least partially inside the working window (true) or completely outside (false) - */ - bool clipRectToWorkingWindow(Common::Rect &rect); - - RenderTable *getRenderTable(); - uint32 getCurrentBackgroundOffset(); - const Graphics::Surface *getBackBuffer() { return &_backBuffer; } - - /** - * Creates a copy of surface and transposes the data. - * - * Note: The user is responsible for calling free() on the returned surface - * and then deleting it - * - * @param surface The data to be transposed - * @return A copy of the surface with the data transposed - */ - static Graphics::Surface *tranposeSurface(const Graphics::Surface *surface); - -private: - /** - * Renders a subRectangle of an image to the backbuffer. The destinationRect and SubRect - * will be clipped to image bound and to working window bounds - * - * @param buffer Pointer to (0, 0) of the image data - * @param imageWidth The width of the original image (not of the subRectangle) - * @param imageHeight The width of the original image (not of the subRectangle) - * @param horizontalPitch The horizontal pitch of the original image - * @param destinationX The x coordinate (in working window space) of where to put the final image - * @param destinationY The y coordinate (in working window space) of where to put the final image - * @param subRectangle A rectangle representing the part of the image that should be rendered - * @param wrap Should the image wrap (tile) if it doesn't completely fill the screen? - */ - void renderSubRectToScreen(Graphics::Surface &surface, int16 destinationX, int16 destinationY, bool wrap); - - /** - * Reads an image file pixel data into a Surface buffer. In the process - * it converts the pixel data from RGB 555 to RGB 565. Also, if the image - * is transposed, it will un-transpose the pixel data. The function will - * call destination::create() if the dimensions of destination do not match - * up with the dimensions of the image. - * - * @param fileName The name of a .tga file - * @param destination A reference to the Surface to store the pixel data in - */ - void readImageToSurface(const Common::String &fileName, Graphics::Surface &destination); - - /** - * Move the background image by an offset. If we are currently in Panorama mode, - * the offset will correspond to a horizontal motion. If we are currently in Tilt mode, - * the offset will correspond to a vertical motion. This function should not be called - * if we are in Flat mode. - * - * The RenderManager will take care of wrapping the image. - * Ex: If the image has width 1400px, it is legal to offset 1500px. - * - * @param offset The amount to move the background - */ - void moveBackground(int offset); -}; - -} // End of namespace ZVision - -#endif diff --git a/engines/zvision/render_table.cpp b/engines/zvision/render_table.cpp deleted file mode 100644 index b6a6a3d2bb..0000000000 --- a/engines/zvision/render_table.cpp +++ /dev/null @@ -1,240 +0,0 @@ -/* 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 "zvision/render_table.h" - -#include "common/rect.h" - -#include "graphics/colormasks.h" - - -namespace ZVision { - -RenderTable::RenderTable(uint numColumns, uint numRows) - : _numRows(numRows), - _numColumns(numColumns), - _renderState(FLAT) { - assert(numRows != 0 && numColumns != 0); - - _internalBuffer = new Common::Point[numRows * numColumns]; -} - -RenderTable::~RenderTable() { - delete[] _internalBuffer; -} - -void RenderTable::setRenderState(RenderState newState) { - _renderState = newState; - - switch (newState) { - case PANORAMA: - _panoramaOptions.fieldOfView = 27.0f; - _panoramaOptions.linearScale = 0.55f; - _panoramaOptions.reverse = false; - break; - case TILT: - _tiltOptions.fieldOfView = 27.0f; - _tiltOptions.linearScale = 0.55f; - _tiltOptions.reverse = false; - break; - case FLAT: - // Intentionally left empty - break; - } -} - -const Common::Point RenderTable::convertWarpedCoordToFlatCoord(const Common::Point &point) { - // If we're outside the range of the RenderTable, no warping is happening. Return the maximum image coords - if (point.x >= (int16)_numColumns || point.y >= (int16)_numRows || point.x < 0 || point.y < 0) { - int16 x = CLIP(point.x, 0, (int16)_numColumns); - int16 y = CLIP(point.y, 0, (int16)_numRows); - return Common::Point(x, y); - } - - uint32 index = point.y * _numColumns + point.x; - - Common::Point newPoint(point); - newPoint.x += _internalBuffer[index].x; - newPoint.y += _internalBuffer[index].y; - - return newPoint; -} - -uint16 mixTwoRGB(uint16 colorOne, uint16 colorTwo, float percentColorOne) { - assert(percentColorOne < 1.0f); - - float rOne = float((colorOne & Graphics::ColorMasks<555>::kRedMask) >> Graphics::ColorMasks<555>::kRedShift); - float rTwo = float((colorTwo & Graphics::ColorMasks<555>::kRedMask) >> Graphics::ColorMasks<555>::kRedShift); - float gOne = float((colorOne & Graphics::ColorMasks<555>::kGreenMask) >> Graphics::ColorMasks<555>::kGreenShift); - float gTwo = float((colorTwo & Graphics::ColorMasks<555>::kGreenMask) >> Graphics::ColorMasks<555>::kGreenShift); - float bOne = float((colorOne & Graphics::ColorMasks<555>::kBlueMask) >> Graphics::ColorMasks<555>::kBlueShift); - float bTwo = float((colorTwo & Graphics::ColorMasks<555>::kBlueMask) >> Graphics::ColorMasks<555>::kBlueShift); - - float rFinal = rOne * percentColorOne + rTwo * (1.0f - percentColorOne); - float gFinal = gOne * percentColorOne + gTwo * (1.0f - percentColorOne); - float bFinal = bOne * percentColorOne + bTwo * (1.0f - percentColorOne); - - uint16 returnColor = (byte(rFinal + 0.5f) << Graphics::ColorMasks<555>::kRedShift) | - (byte(gFinal + 0.5f) << Graphics::ColorMasks<555>::kGreenShift) | - (byte(bFinal + 0.5f) << Graphics::ColorMasks<555>::kBlueShift); - - return returnColor; -} - -void RenderTable::mutateImage(uint16 *sourceBuffer, uint16* destBuffer, uint32 destWidth, const Common::Rect &subRect) { - uint32 destOffset = 0; - - for (int16 y = subRect.top; y < subRect.bottom; ++y) { - uint32 sourceOffset = y * _numColumns; - - for (int16 x = subRect.left; x < subRect.right; ++x) { - uint32 normalizedX = x - subRect.left; - uint32 index = sourceOffset + x; - - // RenderTable only stores offsets from the original coordinates - uint32 sourceYIndex = y + _internalBuffer[index].y; - uint32 sourceXIndex = x + _internalBuffer[index].x; - - destBuffer[destOffset + normalizedX] = sourceBuffer[sourceYIndex * _numColumns + sourceXIndex]; - } - - destOffset += destWidth; - } -} - -void RenderTable::generateRenderTable() { - switch (_renderState) { - case ZVision::RenderTable::PANORAMA: - generatePanoramaLookupTable(); - break; - case ZVision::RenderTable::TILT: - generateTiltLookupTable(); - break; - case ZVision::RenderTable::FLAT: - // Intentionally left empty - break; - } -} - -void RenderTable::generatePanoramaLookupTable() { - memset(_internalBuffer, 0, _numRows * _numColumns * sizeof(uint16)); - - float halfWidth = (float)_numColumns / 2.0f; - float halfHeight = (float)_numRows / 2.0f; - - float fovInRadians = (_panoramaOptions.fieldOfView * M_PI / 180.0f); - float cylinderRadius = halfHeight / tan(fovInRadians); - - for (uint x = 0; x < _numColumns; ++x) { - // Add an offset of 0.01 to overcome zero tan/atan issue (vertical line on half of screen) - // Alpha represents the horizontal angle between the viewer at the center of a cylinder and x - float alpha = atan(((float)x - halfWidth + 0.01f) / cylinderRadius); - - // To get x in cylinder coordinates, we just need to calculate the arc length - // We also scale it by _panoramaOptions.linearScale - int32 xInCylinderCoords = int32(floor((cylinderRadius * _panoramaOptions.linearScale * alpha) + halfWidth)); - - float cosAlpha = cos(alpha); - - for (uint y = 0; y < _numRows; ++y) { - // To calculate y in cylinder coordinates, we can do similar triangles comparison, - // comparing the triangle from the center to the screen and from the center to the edge of the cylinder - int32 yInCylinderCoords = int32(floor(halfHeight + ((float)y - halfHeight) * cosAlpha)); - - uint32 index = y * _numColumns + x; - - // Only store the (x,y) offsets instead of the absolute positions - _internalBuffer[index].x = xInCylinderCoords - x; - _internalBuffer[index].y = yInCylinderCoords - y; - } - } -} - -void RenderTable::generateTiltLookupTable() { - float halfWidth = (float)_numColumns / 2.0f; - float halfHeight = (float)_numRows / 2.0f; - - float fovInRadians = (_tiltOptions.fieldOfView * M_PI / 180.0f); - float cylinderRadius = halfWidth / tan(fovInRadians); - - for (uint y = 0; y < _numRows; ++y) { - - // Add an offset of 0.01 to overcome zero tan/atan issue (horizontal line on half of screen) - // Alpha represents the vertical angle between the viewer at the center of a cylinder and y - float alpha = atan(((float)y - halfHeight + 0.01f) / cylinderRadius); - - // To get y in cylinder coordinates, we just need to calculate the arc length - // We also scale it by _tiltOptions.linearScale - int32 yInCylinderCoords = int32(floor((cylinderRadius * _tiltOptions.linearScale * alpha) + halfHeight)); - - float cosAlpha = cos(alpha); - uint32 columnIndex = y * _numColumns; - - for (uint x = 0; x < _numColumns; ++x) { - // To calculate x in cylinder coordinates, we can do similar triangles comparison, - // comparing the triangle from the center to the screen and from the center to the edge of the cylinder - int32 xInCylinderCoords = int32(floor(halfWidth + ((float)x - halfWidth) * cosAlpha)); - - uint32 index = columnIndex + x; - - // Only store the (x,y) offsets instead of the absolute positions - _internalBuffer[index].x = xInCylinderCoords - x; - _internalBuffer[index].y = yInCylinderCoords - y; - } - } -} - -void RenderTable::setPanoramaFoV(float fov) { - assert(fov > 0.0f); - - _panoramaOptions.fieldOfView = fov; -} - -void RenderTable::setPanoramaScale(float scale) { - assert(scale > 0.0f); - - _panoramaOptions.linearScale = scale; -} - -void RenderTable::setPanoramaReverse(bool reverse) { - _panoramaOptions.reverse = reverse; -} - -void RenderTable::setTiltFoV(float fov) { - assert(fov > 0.0f); - - _tiltOptions.fieldOfView = fov; -} - -void RenderTable::setTiltScale(float scale) { - assert(scale > 0.0f); - - _tiltOptions.linearScale = scale; -} - -void RenderTable::setTiltReverse(bool reverse) { - _tiltOptions.reverse = reverse; -} - -} // End of namespace ZVision diff --git a/engines/zvision/render_table.h b/engines/zvision/render_table.h deleted file mode 100644 index 898091193a..0000000000 --- a/engines/zvision/render_table.h +++ /dev/null @@ -1,85 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef ZVISION_RENDER_TABLE_H -#define ZVISION_RENDER_TABLE_H - -#include "common/rect.h" - - -namespace ZVision { - -class RenderTable { -public: - RenderTable(uint numRows, uint numColumns); - ~RenderTable(); - -public: - enum RenderState { - PANORAMA, - TILT, - FLAT - }; - -private: - uint _numColumns, _numRows; - Common::Point *_internalBuffer; - RenderState _renderState; - - struct { - float fieldOfView; - float linearScale; - bool reverse; - } _panoramaOptions; - - // TODO: See if tilt and panorama need to have separate options - struct { - float fieldOfView; - float linearScale; - bool reverse; - } _tiltOptions; - -public: - RenderState getRenderState() { return _renderState; } - void setRenderState(RenderState newState); - - const Common::Point convertWarpedCoordToFlatCoord(const Common::Point &point); - - void mutateImage(uint16 *sourceBuffer, uint16* destBuffer, uint32 destWidth, const Common::Rect &subRect); - void generateRenderTable(); - - void setPanoramaFoV(float fov); - void setPanoramaScale(float scale); - void setPanoramaReverse(bool reverse); - - void setTiltFoV(float fov); - void setTiltScale(float scale); - void setTiltReverse(bool reverse); - -private: - void generatePanoramaLookupTable(); - void generateTiltLookupTable(); -}; - -} // End of namespace ZVision - -#endif diff --git a/engines/zvision/rlf_animation.cpp b/engines/zvision/rlf_animation.cpp deleted file mode 100644 index 5f1d41daae..0000000000 --- a/engines/zvision/rlf_animation.cpp +++ /dev/null @@ -1,331 +0,0 @@ -/* 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 "zvision/rlf_animation.h" - -#include "common/str.h" -#include "common/file.h" -#include "common/textconsole.h" -#include "common/debug.h" -#include "common/endian.h" - -#include "graphics/colormasks.h" - - -namespace ZVision { - -RlfAnimation::RlfAnimation(const Common::String &fileName, bool stream) - : _stream(stream), - _lastFrameRead(0), - _frameCount(0), - _width(0), - _height(0), - _frameTime(0), - _frames(0), - _currentFrame(-1), - _frameBufferByteSize(0) { - if (!_file.open(fileName)) { - warning("RLF animation file %s could not be opened", fileName.c_str()); - return; - } - - if (!readHeader()) { - warning("%s is not a RLF animation file. Wrong magic number", fileName.c_str()); - return; - } - - _currentFrameBuffer.create(_width, _height, Graphics::createPixelFormat<565>()); - _frameBufferByteSize = _width * _height * sizeof(uint16); - - if (!stream) { - _frames = new Frame[_frameCount]; - - // Read in each frame - for (uint i = 0; i < _frameCount; ++i) { - _frames[i] = readNextFrame(); - } - } -} - -RlfAnimation::~RlfAnimation() { - for (uint i = 0; i < _frameCount; ++i) { - delete[] _frames[i].encodedData; - } - delete[] _frames; - _currentFrameBuffer.free(); -} - -bool RlfAnimation::readHeader() { - if (_file.readUint32BE() != MKTAG('F', 'E', 'L', 'R')) { - return false; - } - - // Read the header - _file.readUint32LE(); // Size1 - _file.readUint32LE(); // Unknown1 - _file.readUint32LE(); // Unknown2 - _frameCount = _file.readUint32LE(); // Frame count - - // Since we don't need any of the data, we can just seek right to the - // entries we need rather than read in all the individual entries. - _file.seek(136, SEEK_CUR); - - //// Read CIN header - //_file.readUint32BE(); // Magic number FNIC - //_file.readUint32LE(); // Size2 - //_file.readUint32LE(); // Unknown3 - //_file.readUint32LE(); // Unknown4 - //_file.readUint32LE(); // Unknown5 - //_file.seek(0x18, SEEK_CUR); // VRLE - //_file.readUint32LE(); // LRVD - //_file.readUint32LE(); // Unknown6 - //_file.seek(0x18, SEEK_CUR); // HRLE - //_file.readUint32LE(); // ELHD - //_file.readUint32LE(); // Unknown7 - //_file.seek(0x18, SEEK_CUR); // HKEY - //_file.readUint32LE(); // ELRH - - //// Read MIN info header - //_file.readUint32BE(); // Magic number FNIM - //_file.readUint32LE(); // Size3 - //_file.readUint32LE(); // OEDV - //_file.readUint32LE(); // Unknown8 - //_file.readUint32LE(); // Unknown9 - //_file.readUint32LE(); // Unknown10 - _width = _file.readUint32LE(); // Width - _height = _file.readUint32LE(); // Height - - // Read time header - _file.readUint32BE(); // Magic number EMIT - _file.readUint32LE(); // Size4 - _file.readUint32LE(); // Unknown11 - _frameTime = _file.readUint32LE() / 10; // Frame time in microseconds - - return true; -} - -RlfAnimation::Frame RlfAnimation::readNextFrame() { - RlfAnimation::Frame frame; - - _file.readUint32BE(); // Magic number MARF - uint32 size = _file.readUint32LE(); // Size - _file.readUint32LE(); // Unknown1 - _file.readUint32LE(); // Unknown2 - uint32 type = _file.readUint32BE(); // Either ELHD or ELRH - uint32 headerSize = _file.readUint32LE(); // Offset from the beginning of this frame to the frame data. Should always be 28 - _file.readUint32LE(); // Unknown3 - - frame.encodedSize = size - headerSize; - frame.encodedData = new int8[frame.encodedSize]; - _file.read(frame.encodedData, frame.encodedSize); - - if (type == MKTAG('E', 'L', 'H', 'D')) { - frame.type = Masked; - } else if (type == MKTAG('E', 'L', 'R', 'H')) { - frame.type = Simple; - _completeFrames.push_back(_lastFrameRead); - } else { - warning("Frame %u doesn't have type that can be decoded", _lastFrameRead); - } - - _lastFrameRead++; - return frame; -} - -void RlfAnimation::seekToFrame(int frameNumber) { - assert(!_stream); - assert(frameNumber < (int)_frameCount || frameNumber >= -1); - - if (frameNumber == -1) { - _currentFrame = -1; - return; - } - - int closestFrame = _currentFrame; - int distance = (int)frameNumber - _currentFrame; - for (uint i = 0; i < _completeFrames.size(); ++i) { - int newDistance = (int)frameNumber - (int)(_completeFrames[i]); - if (newDistance > 0 && (closestFrame == -1 || newDistance < distance)) { - closestFrame = _completeFrames[i]; - distance = newDistance; - } - } - - for (int i = closestFrame; i <= frameNumber; ++i) { - applyFrameToCurrent(i); - } - - _currentFrame = frameNumber; -} - -const Graphics::Surface *RlfAnimation::getFrameData(uint frameNumber) { - assert(!_stream); - assert(frameNumber < _frameCount); - - // Since this method is so expensive, first check to see if we can use - // getNextFrame() it's cheap. - if ((int)frameNumber == _currentFrame) { - return &_currentFrameBuffer; - } else if (_currentFrame + 1 == (int)frameNumber) { - return getNextFrame(); - } - - seekToFrame(frameNumber); - return &_currentFrameBuffer; -} - -const Graphics::Surface *RlfAnimation::getNextFrame() { - assert(_currentFrame + 1 < (int)_frameCount); - - if (_stream) { - applyFrameToCurrent(readNextFrame()); - } else { - applyFrameToCurrent(_currentFrame + 1); - } - - _currentFrame++; - return &_currentFrameBuffer; -} - -void RlfAnimation::applyFrameToCurrent(uint frameNumber) { - if (_frames[frameNumber].type == Masked) { - decodeMaskedRunLengthEncoding(_frames[frameNumber].encodedData, (int8 *)_currentFrameBuffer.getPixels(), _frames[frameNumber].encodedSize, _frameBufferByteSize); - } else if (_frames[frameNumber].type == Simple) { - decodeSimpleRunLengthEncoding(_frames[frameNumber].encodedData, (int8 *)_currentFrameBuffer.getPixels(), _frames[frameNumber].encodedSize, _frameBufferByteSize); - } -} - -void RlfAnimation::applyFrameToCurrent(const RlfAnimation::Frame &frame) { - if (frame.type == Masked) { - decodeMaskedRunLengthEncoding(frame.encodedData, (int8 *)_currentFrameBuffer.getPixels(), frame.encodedSize, _frameBufferByteSize); - } else if (frame.type == Simple) { - decodeSimpleRunLengthEncoding(frame.encodedData, (int8 *)_currentFrameBuffer.getPixels(), frame.encodedSize, _frameBufferByteSize); - } -} - -void RlfAnimation::decodeMaskedRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const { - uint32 sourceOffset = 0; - uint32 destOffset = 0; - - while (sourceOffset < sourceSize) { - int8 numberOfSamples = source[sourceOffset]; - sourceOffset++; - - // If numberOfSamples is negative, the next abs(numberOfSamples) samples should - // be copied directly from source to dest - if (numberOfSamples < 0) { - numberOfSamples = ABS(numberOfSamples); - - while (numberOfSamples > 0) { - if (sourceOffset + 1 >= sourceSize) { - return; - } else if (destOffset + 1 >= destSize) { - debug(2, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize); - return; - } - - byte r, g, b; - Graphics::colorToRGB >(READ_LE_UINT16(source + sourceOffset), r, g, b); - uint16 destColor = Graphics::RGBToColor >(r, g, b); - WRITE_UINT16(dest + destOffset, destColor); - - sourceOffset += 2; - destOffset += 2; - numberOfSamples--; - } - - // If numberOfSamples is >= 0, move destOffset forward ((numberOfSamples * 2) + 2) - // This function assumes the dest buffer has been memset with 0's. - } else { - if (sourceOffset + 1 >= sourceSize) { - return; - } else if (destOffset + 1 >= destSize) { - debug(2, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize); - return; - } - - destOffset += (numberOfSamples * 2) + 2; - } - } -} - -void RlfAnimation::decodeSimpleRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const { - uint32 sourceOffset = 0; - uint32 destOffset = 0; - - while (sourceOffset < sourceSize) { - int8 numberOfSamples = source[sourceOffset]; - sourceOffset++; - - // If numberOfSamples is negative, the next abs(numberOfSamples) samples should - // be copied directly from source to dest - if (numberOfSamples < 0) { - numberOfSamples = ABS(numberOfSamples); - - while (numberOfSamples > 0) { - if (sourceOffset + 1 >= sourceSize) { - return; - } else if (destOffset + 1 >= destSize) { - debug(2, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize); - return; - } - - byte r, g, b; - Graphics::colorToRGB >(READ_LE_UINT16(source + sourceOffset), r, g, b); - uint16 destColor = Graphics::RGBToColor >(r, g, b); - WRITE_UINT16(dest + destOffset, destColor); - - sourceOffset += 2; - destOffset += 2; - numberOfSamples--; - } - - // If numberOfSamples is >= 0, copy one sample from source to the - // next (numberOfSamples + 2) dest spots - } else { - if (sourceOffset + 1 >= sourceSize) { - return; - } - - byte r, g, b; - Graphics::colorToRGB >(READ_LE_UINT16(source + sourceOffset), r, g, b); - uint16 sampleColor = Graphics::RGBToColor >(r, g, b); - sourceOffset += 2; - - numberOfSamples += 2; - while (numberOfSamples > 0) { - if (destOffset + 1 >= destSize) { - debug(2, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize); - return; - } - - WRITE_UINT16(dest + destOffset, sampleColor); - destOffset += 2; - numberOfSamples--; - } - } - } -} - -} // End of namespace ZVision diff --git a/engines/zvision/rlf_animation.h b/engines/zvision/rlf_animation.h deleted file mode 100644 index fe5b0d68b4..0000000000 --- a/engines/zvision/rlf_animation.h +++ /dev/null @@ -1,163 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef ZVISION_RLF_ANIMATION_H -#define ZVISION_RLF_ANIMATION_H - -#include "common/file.h" - -#include "graphics/surface.h" - - -namespace Common { -class String; -} - -namespace ZVision { - -class RlfAnimation { -public: - RlfAnimation(const Common::String &fileName, bool stream = true); - ~RlfAnimation(); - -private: - enum EncodingType { - Masked, - Simple - }; - - struct Frame { - EncodingType type; - int8 *encodedData; - uint32 encodedSize; - }; - -private: - Common::File _file; - bool _stream; - uint _lastFrameRead; - - uint _frameCount; - uint _width; - uint _height; - uint32 _frameTime; // In milliseconds - Frame *_frames; - Common::Array _completeFrames; - - int _currentFrame; - Graphics::Surface _currentFrameBuffer; - uint32 _frameBufferByteSize; - -public: - uint frameCount() { return _frameCount; } - uint width() { return _width; } - uint height() { return _height; } - uint32 frameTime() { return _frameTime; } - - /** - * Seeks to the frameNumber and updates the internal Surface with - * the new frame data. If frameNumber == -1, it only sets _currentFrame, - * the internal Surface is unchanged. This function requires _stream = false - * - * @param frameNumber The frame number to seek to - */ - void seekToFrame(int frameNumber); - - /** - * Returns the pixel data of the frame specified. It will try to use - * getNextFrame() if possible. If not, it uses seekToFrame() to - * update the internal Surface and then returns a pointer to it. - * This function requires _stream = false - * - * @param frameNumber The frame number to get data for - * @return A pointer to the pixel data. Do NOT delete this. - */ - const Graphics::Surface *getFrameData(uint frameNumber); - /** - * Returns the pixel data of the next frame. It is up to the user to - * check if the next frame is valid before calling this. - * IE. Use endOfAnimation() - * - * @return A pointer to the pixel data. Do NOT delete this. - */ - const Graphics::Surface *getNextFrame(); - - /** - * @return Is the currentFrame is the last frame in the animation? - */ - bool endOfAnimation() { return _currentFrame == (int)_frameCount - 1; } - -private: - /** - * Reads in the header of the RLF file - * - * @return Will return false if the header magic number is wrong - */ - bool readHeader(); - /** - * Reads the next frame from the RLF file, stores the data in - * a Frame object, then returns the object - * - * @return A Frame object representing the frame data - */ - Frame readNextFrame(); - - /** - * Applies the frame corresponding to frameNumber on top of _currentFrameBuffer. - * This function requires _stream = false so it can look up the Frame object - * referenced by frameNumber. - * - * @param frameNumber The frame number to apply to _currentFrameBuffer - */ - void applyFrameToCurrent(uint frameNumber); - /** - * Applies the data from a Frame object on top of a _currentFrameBuffer. - * - * @param frame A Frame object to apply to _currentFrameBuffer - */ - void applyFrameToCurrent(const RlfAnimation::Frame &frame); - - /** - * Decode frame data that uses masked run length encoding. This is the encoding - * used by P-frames. - * - * @param source The source pixel data - * @param dest The destination buffer - * @param sourceSize The size of the source pixel data - * @param destSize The size of the destination buffer - */ - void decodeMaskedRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const; - /** - * Decode frame data that uses simple run length encoding. This is the encoding - * used by I-frames. - * - * @param source The source pixel data - * @param dest The destination buffer - * @param sourceSize The size of the source pixel data - * @param destSize The size of the destination buffer - */ - void decodeSimpleRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const; -}; - -} // End of namespace ZVision - -#endif diff --git a/engines/zvision/save_manager.cpp b/engines/zvision/save_manager.cpp deleted file mode 100644 index c3deadd703..0000000000 --- a/engines/zvision/save_manager.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* 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 "zvision/save_manager.h" - -#include "zvision/zvision.h" -#include "zvision/script_manager.h" -#include "zvision/render_manager.h" - -#include "common/system.h" - -#include "graphics/surface.h" -#include "graphics/thumbnail.h" - -#include "gui/message.h" - - -namespace ZVision { - -const uint32 SaveManager::SAVEGAME_ID = MKTAG('Z', 'E', 'N', 'G'); - -void SaveManager::saveGame(uint slot, const Common::String &saveName) { - // The games only support 20 slots - assert(slot <= 1 && slot <= 20); - - Common::SaveFileManager *saveFileManager = g_system->getSavefileManager(); - Common::OutSaveFile *file = saveFileManager->openForSaving(_engine->generateSaveFileName(slot)); - - // Write out the savegame header - file->writeUint32BE(SAVEGAME_ID); - - // Write version - file->writeByte(SAVE_VERSION); - - // Write savegame name - file->writeString(saveName); - file->writeByte(0); - - // We can't call writeGameSaveData because the save menu is actually - // a room, so writeGameSaveData would save us in the save menu. - // However, an auto save is performed before each room change, so we - // can copy the data from there. We can guarantee that an auto save file will - // exist before this is called because the save menu can only be accessed - // after the first room (the main menu) has loaded. - Common::InSaveFile *autoSaveFile = saveFileManager->openForLoading(_engine->generateAutoSaveFileName()); - - // Skip over the header info - autoSaveFile->readSint32BE(); // SAVEGAME_ID - autoSaveFile->readByte(); // Version - autoSaveFile->seek(5, SEEK_CUR); // The string "auto" with terminating NULL - - // Read the rest to a buffer - uint32 size = autoSaveFile->size() - autoSaveFile->pos(); - byte *buffer = new byte[size]; - autoSaveFile->read(buffer, size); - - // Then write the buffer to the new file - file->write(buffer, size); - - // Cleanup - delete[] buffer; - file->finalize(); - delete file; -} - -void SaveManager::autoSave() { - Common::OutSaveFile *file = g_system->getSavefileManager()->openForSaving(_engine->generateAutoSaveFileName()); - - // Write out the savegame header - file->writeUint32BE(SAVEGAME_ID); - - // Version - file->writeByte(SAVE_VERSION); - - file->writeString("auto"); - file->writeByte(0); - - writeSaveGameData(file); - - // Cleanup - file->finalize(); - delete file; -} - -void SaveManager::writeSaveGameData(Common::OutSaveFile *file) { - // Create a thumbnail and save it - Graphics::saveThumbnail(*file); - - // Write out the save date/time - TimeDate td; - g_system->getTimeAndDate(td); - file->writeSint16LE(td.tm_year + 1900); - file->writeSint16LE(td.tm_mon + 1); - file->writeSint16LE(td.tm_mday); - file->writeSint16LE(td.tm_hour); - file->writeSint16LE(td.tm_min); - - ScriptManager *scriptManager = _engine->getScriptManager(); - // Write out the current location - Location currentLocation = scriptManager->getCurrentLocation(); - file->writeByte(currentLocation.world); - file->writeByte(currentLocation.room); - file->writeByte(currentLocation.node); - file->writeByte(currentLocation.view); - file->writeUint32LE(currentLocation.offset); - - // Write out the current state table values - scriptManager->serializeStateTable(file); - - // Write out any controls needing to save state - scriptManager->serializeControls(file); -} - -Common::Error SaveManager::loadGame(uint slot) { - // The games only support 20 slots - assert(slot <= 1 && slot <= 20); - - Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(_engine->generateSaveFileName(slot)); - if (saveFile == 0) { - return Common::kPathDoesNotExist; - } - - // Read the header - SaveGameHeader header; - if (!readSaveGameHeader(saveFile, header)) { - return Common::kUnknownError; - } - - char world = (char)saveFile->readByte(); - char room = (char)saveFile->readByte(); - char node = (char)saveFile->readByte(); - char view = (char)saveFile->readByte(); - uint32 offset = (char)saveFile->readUint32LE(); - - ScriptManager *scriptManager = _engine->getScriptManager(); - // Update the state table values - scriptManager->deserializeStateTable(saveFile); - - // Load the room - scriptManager->changeLocation(world, room, node, view, offset); - - // Update the controls - scriptManager->deserializeControls(saveFile); - - return Common::kNoError; -} - -bool SaveManager::readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &header) { - if (in->readUint32BE() != SAVEGAME_ID) { - warning("File is not a ZVision save file. Aborting load"); - return false; - } - - // Read in the version - header.version = in->readByte(); - - // Check that the save version isn't newer than this binary - if (header.version > SAVE_VERSION) { - uint tempVersion = header.version; - GUI::MessageDialog dialog(Common::String::format("This save file uses version %u, but this engine only supports up to version %d. You will need an updated version of the engine to use this save file.", tempVersion, SAVE_VERSION), "OK"); - dialog.runModal(); - } - - // Read in the save name - header.saveName.clear(); - char ch; - while ((ch = (char)in->readByte()) != '\0') - header.saveName += ch; - - // Get the thumbnail - header.thumbnail = Graphics::loadThumbnail(*in); - if (!header.thumbnail) - return false; - - // Read in save date/time - header.saveYear = in->readSint16LE(); - header.saveMonth = in->readSint16LE(); - header.saveDay = in->readSint16LE(); - header.saveHour = in->readSint16LE(); - header.saveMinutes = in->readSint16LE(); - - return true; -} - -} // End of namespace ZVision diff --git a/engines/zvision/save_manager.h b/engines/zvision/save_manager.h deleted file mode 100644 index b4770e68b2..0000000000 --- a/engines/zvision/save_manager.h +++ /dev/null @@ -1,91 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef ZVISION_SAVE_MANAGER_H -#define ZVISION_SAVE_MANAGER_H - -#include "common/savefile.h" - -namespace Common { -class String; -} - -namespace Graphics { -struct Surface; -} - -namespace ZVision { - -class ZVision; - -struct SaveGameHeader { - byte version; - Common::String saveName; - Graphics::Surface *thumbnail; - int saveYear, saveMonth, saveDay; - int saveHour, saveMinutes; -}; - -class SaveManager { -public: - SaveManager(ZVision *engine) : _engine(engine) {} - -private: - ZVision *_engine; - static const uint32 SAVEGAME_ID; - - enum { - SAVE_VERSION = 1 - }; - -public: - /** - * Called every room change. Saves the state of the room just before - * we switched rooms. Uses ZVision::generateAutoSaveFileName() to - * create the save file name. - */ - void autoSave(); - /** - * Copies the data from the last auto-save into a new save file. We - * can't use the current state data because the save menu *IS* a room. - * The file is named using ZVision::generateSaveFileName(slot) - * - * @param slot The save slot this save pertains to. Must be [1, 20] - * @param saveName The internal name for this save. This is NOT the name of the actual save file. - */ - void saveGame(uint slot, const Common::String &saveName); - /** - * Loads the state data from the save file that slot references. Uses - * ZVision::generateSaveFileName(slot) to get the save file name. - * - * @param slot The save slot to load. Must be [1, 20] - */ - Common::Error loadGame(uint slot); - -private: - void writeSaveGameData(Common::OutSaveFile *file); - bool readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &header); -}; - -} // End of namespace ZVision - -#endif diff --git a/engines/zvision/scr_file_handling.cpp b/engines/zvision/scr_file_handling.cpp deleted file mode 100644 index 0c952ae0ce..0000000000 --- a/engines/zvision/scr_file_handling.cpp +++ /dev/null @@ -1,302 +0,0 @@ -/* 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 "zvision/script_manager.h" - -#include "zvision/utility.h" -#include "zvision/puzzle.h" -#include "zvision/actions.h" -#include "zvision/push_toggle_control.h" -#include "zvision/lever_control.h" - -#include "common/textconsole.h" -#include "common/file.h" -#include "common/tokenizer.h" - - -namespace ZVision { - -void ScriptManager::parseScrFile(const Common::String &fileName, bool isGlobal) { - Common::File file; - if (!file.open(fileName)) { - warning("Script file not found: %s", fileName.c_str()); - return; - } - - while(!file.eos()) { - Common::String line = file.readLine(); - if (file.err()) { - warning("Error parsing scr file: %s", fileName.c_str()); - return; - } - - trimCommentsAndWhiteSpace(&line); - if (line.empty()) - continue; - - if (line.matchString("puzzle:*", true)) { - Puzzle *puzzle = new Puzzle(); - sscanf(line.c_str(),"puzzle:%u",&(puzzle->key)); - - parsePuzzle(puzzle, file); - if (isGlobal) { - _globalPuzzles.push_back(puzzle); - } else { - _activePuzzles.push_back(puzzle); - } - } else if (line.matchString("control:*", true)) { - parseControl(line, file); - } - } -} - -void ScriptManager::parsePuzzle(Puzzle *puzzle, Common::SeekableReadStream &stream) { - Common::String line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - - while (!stream.eos() && !line.contains('}')) { - if (line.matchString("criteria {", true)) { - parseCriteria(stream, puzzle->criteriaList); - } else if (line.matchString("results {", true)) { - parseResults(stream, puzzle->resultActions); - } else if (line.matchString("flags {", true)) { - setStateFlags(puzzle->key, parseFlags(stream)); - } - - line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - } -} - -bool ScriptManager::parseCriteria(Common::SeekableReadStream &stream, Common::List > &criteriaList) const { - // Loop until we find the closing brace - Common::String line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - - // Criteria can be empty - if (line.contains('}')) { - return false; - } - - // Create a new List to hold the CriteriaEntries - criteriaList.push_back(Common::List()); - - while (!stream.eos() && !line.contains('}')) { - Puzzle::CriteriaEntry entry; - - // Split the string into tokens using ' ' as a delimiter - Common::StringTokenizer tokenizer(line); - Common::String token; - - // Parse the id out of the first token - token = tokenizer.nextToken(); - sscanf(token.c_str(), "[%u]", &(entry.key)); - - // Parse the operator out of the second token - token = tokenizer.nextToken(); - if (token.c_str()[0] == '=') - entry.criteriaOperator = Puzzle::EQUAL_TO; - else if (token.c_str()[0] == '!') - entry.criteriaOperator = Puzzle::NOT_EQUAL_TO; - else if (token.c_str()[0] == '>') - entry.criteriaOperator = Puzzle::GREATER_THAN; - else if (token.c_str()[0] == '<') - entry.criteriaOperator = Puzzle::LESS_THAN; - - // First determine if the last token is an id or a value - // Then parse it into 'argument' - token = tokenizer.nextToken(); - if (token.contains('[')) { - sscanf(token.c_str(), "[%u]", &(entry.argument)); - entry.argumentIsAKey = true; - } else { - sscanf(token.c_str(), "%u", &(entry.argument)); - entry.argumentIsAKey = false; - } - - criteriaList.back().push_back(entry); - - line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - } - - return true; -} - -void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::List &actionList) const { - // Loop until we find the closing brace - Common::String line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - - // TODO: Re-order the if-then statements in order of highest occurrence - while (!stream.eos() && !line.contains('}')) { - if (line.empty()) { - line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - - continue; - } - - // Parse for the action type - if (line.matchString("*:add*", true)) { - actionList.push_back(new ActionAdd(line)); - } else if (line.matchString("*:animplay*", true)) { - actionList.push_back(new ActionPlayAnimation(line)); - } else if (line.matchString("*:animpreload*", true)) { - actionList.push_back(new ActionPreloadAnimation(line)); - } else if (line.matchString("*:animunload*", true)) { - //actionList.push_back(new ActionUnloadAnimation(line)); - } else if (line.matchString("*:attenuate*", true)) { - // TODO: Implement ActionAttenuate - } else if (line.matchString("*:assign*", true)) { - actionList.push_back(new ActionAssign(line)); - } else if (line.matchString("*:change_location*", true)) { - actionList.push_back(new ActionChangeLocation(line)); - } else if (line.matchString("*:crossfade*", true)) { - // TODO: Implement ActionCrossfade - } else if (line.matchString("*:debug*", true)) { - // TODO: Implement ActionDebug - } else if (line.matchString("*:delay_render*", true)) { - // TODO: Implement ActionDelayRender - } else if (line.matchString("*:disable_control*", true)) { - actionList.push_back(new ActionDisableControl(line)); - } else if (line.matchString("*:disable_venus*", true)) { - // TODO: Implement ActionDisableVenus - } else if (line.matchString("*:display_message*", true)) { - // TODO: Implement ActionDisplayMessage - } else if (line.matchString("*:dissolve*", true)) { - // TODO: Implement ActionDissolve - } else if (line.matchString("*:distort*", true)) { - // TODO: Implement ActionDistort - } else if (line.matchString("*:enable_control*", true)) { - actionList.push_back(new ActionEnableControl(line)); - } else if (line.matchString("*:flush_mouse_events*", true)) { - // TODO: Implement ActionFlushMouseEvents - } else if (line.matchString("*:inventory*", true)) { - // TODO: Implement ActionInventory - } else if (line.matchString("*:kill*", true)) { - // TODO: Implement ActionKill - } else if (line.matchString("*:menu_bar_enable*", true)) { - // TODO: Implement ActionMenuBarEnable - } else if (line.matchString("*:music*", true)) { - actionList.push_back(new ActionMusic(line)); - } else if (line.matchString("*:pan_track*", true)) { - // TODO: Implement ActionPanTrack - } else if (line.matchString("*:playpreload*", true)) { - actionList.push_back(new ActionPlayPreloadAnimation(line)); - } else if (line.matchString("*:preferences*", true)) { - // TODO: Implement ActionPreferences - } else if (line.matchString("*:quit*", true)) { - actionList.push_back(new ActionQuit()); - } else if (line.matchString("*:random*", true)) { - actionList.push_back(new ActionRandom(line)); - } else if (line.matchString("*:region*", true)) { - // TODO: Implement ActionRegion - } else if (line.matchString("*:restore_game*", true)) { - // TODO: Implement ActionRestoreGame - } else if (line.matchString("*:rotate_to*", true)) { - // TODO: Implement ActionRotateTo - } else if (line.matchString("*:save_game*", true)) { - // TODO: Implement ActionSaveGame - } else if (line.matchString("*:set_partial_screen*", true)) { - actionList.push_back(new ActionSetPartialScreen(line)); - } else if (line.matchString("*:set_screen*", true)) { - actionList.push_back(new ActionSetScreen(line)); - } else if (line.matchString("*:set_venus*", true)) { - // TODO: Implement ActionSetVenus - } else if (line.matchString("*:stop*", true)) { - // TODO: Implement ActionStop - } else if (line.matchString("*:streamvideo*", true)) { - actionList.push_back(new ActionStreamVideo(line)); - } else if (line.matchString("*:syncsound*", true)) { - // TODO: Implement ActionSyncSound - } else if (line.matchString("*:timer*", true)) { - actionList.push_back(new ActionTimer(line)); - } else if (line.matchString("*:ttytext*", true)) { - // TODO: Implement ActionTTYText - } else if (line.matchString("*:universe_music*", true)) { - // TODO: Implement ActionUniverseMusic - } else if (line.matchString("*:copy_file*", true)) { - // Not used. Purposely left empty - } else { - warning("Unhandled result action type: %s", line.c_str()); - } - - line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - } - - return; -} - -uint ScriptManager::parseFlags(Common::SeekableReadStream &stream) const { - uint flags = 0; - - // Loop until we find the closing brace - Common::String line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - - while (!stream.eos() && !line.contains('}')) { - if (line.matchString("ONCE_PER_INST", true)) { - flags |= ONCE_PER_INST; - } else if (line.matchString("DO_ME_NOW", true)) { - flags |= DO_ME_NOW; - } else if (line.matchString("DISABLED", true)) { - flags |= DISABLED; - } - - line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - } - - return flags; -} - -void ScriptManager::parseControl(Common::String &line, Common::SeekableReadStream &stream) { - uint32 key; - char controlTypeBuffer[20]; - - sscanf(line.c_str(), "control:%u %s {", &key, controlTypeBuffer); - - Common::String controlType(controlTypeBuffer); - - if (controlType.equalsIgnoreCase("push_toggle")) { - _activeControls.push_back(new PushToggleControl(_engine, key, stream)); - return; - } else if (controlType.equalsIgnoreCase("flat")) { - Control::parseFlatControl(_engine); - return; - } else if (controlType.equalsIgnoreCase("pana")) { - Control::parsePanoramaControl(_engine, stream); - return; - } else if (controlType.equalsIgnoreCase("tilt")) { - Control::parseTiltControl(_engine, stream); - return; - } else if (controlType.equalsIgnoreCase("lever")) { - _activeControls.push_back(new LeverControl(_engine, key, stream)); - return; - } -} - -} // End of namespace ZVision diff --git a/engines/zvision/script_manager.cpp b/engines/zvision/script_manager.cpp deleted file mode 100644 index 0e08345f07..0000000000 --- a/engines/zvision/script_manager.cpp +++ /dev/null @@ -1,444 +0,0 @@ -/* 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 "zvision/script_manager.h" - -#include "zvision/zvision.h" -#include "zvision/render_manager.h" -#include "zvision/cursor_manager.h" -#include "zvision/save_manager.h" -#include "zvision/actions.h" -#include "zvision/utility.h" - -#include "common/algorithm.h" -#include "common/hashmap.h" -#include "common/debug.h" -#include "common/stream.h" - - -namespace ZVision { - -ScriptManager::ScriptManager(ZVision *engine) - : _engine(engine), - _currentlyFocusedControl(0) { -} - -ScriptManager::~ScriptManager() { - for (PuzzleList::iterator iter = _activePuzzles.begin(); iter != _activePuzzles.end(); ++iter) { - delete (*iter); - } - for (PuzzleList::iterator iter = _globalPuzzles.begin(); iter != _globalPuzzles.end(); ++iter) { - delete (*iter); - } - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - delete (*iter); - } -} - -void ScriptManager::initialize() { - parseScrFile("universe.scr", true); - changeLocation('g', 'a', 'r', 'y', 0); -} - -void ScriptManager::update(uint deltaTimeMillis) { - updateNodes(deltaTimeMillis); - checkPuzzleCriteria(); -} - -void ScriptManager::createReferenceTable() { - // Iterate through each local Puzzle - for (PuzzleList::iterator activePuzzleIter = _activePuzzles.begin(); activePuzzleIter != _activePuzzles.end(); ++activePuzzleIter) { - Puzzle *puzzlePtr = (*activePuzzleIter); - - // Iterate through each CriteriaEntry and add a reference from the criteria key to the Puzzle - for (Common::List >::iterator criteriaIter = (*activePuzzleIter)->criteriaList.begin(); criteriaIter != (*activePuzzleIter)->criteriaList.end(); ++criteriaIter) { - for (Common::List::iterator entryIter = criteriaIter->begin(); entryIter != criteriaIter->end(); ++entryIter) { - _referenceTable[entryIter->key].push_back(puzzlePtr); - - // If the argument is a key, add a reference to it as well - if (entryIter->argumentIsAKey) { - _referenceTable[entryIter->argument].push_back(puzzlePtr); - } - } - } - } - - // Iterate through each global Puzzle - for (PuzzleList::iterator globalPuzzleIter = _globalPuzzles.begin(); globalPuzzleIter != _globalPuzzles.end(); ++globalPuzzleIter) { - Puzzle *puzzlePtr = (*globalPuzzleIter); - - // Iterate through each CriteriaEntry and add a reference from the criteria key to the Puzzle - for (Common::List >::iterator criteriaIter = (*globalPuzzleIter)->criteriaList.begin(); criteriaIter != (*globalPuzzleIter)->criteriaList.end(); ++criteriaIter) { - for (Common::List::iterator entryIter = criteriaIter->begin(); entryIter != criteriaIter->end(); ++entryIter) { - _referenceTable[entryIter->key].push_back(puzzlePtr); - - // If the argument is a key, add a reference to it as well - if (entryIter->argumentIsAKey) { - _referenceTable[entryIter->argument].push_back(puzzlePtr); - } - } - } - } - - // Remove duplicate entries - for (PuzzleMap::iterator referenceTableIter = _referenceTable.begin(); referenceTableIter != _referenceTable.end(); ++referenceTableIter) { - removeDuplicateEntries(referenceTableIter->_value); - } -} - -void ScriptManager::updateNodes(uint deltaTimeMillis) { - // If process() returns true, it means the node can be deleted - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end();) { - if ((*iter)->process(deltaTimeMillis)) { - delete (*iter); - // Remove the node - iter = _activeControls.erase(iter); - } else { - ++iter; - } - } -} - -void ScriptManager::checkPuzzleCriteria() { - while (!_puzzlesToCheck.empty()) { - Puzzle *puzzle = _puzzlesToCheck.pop(); - - // Check if the puzzle is already finished - // Also check that the puzzle isn't disabled - if (getStateValue(puzzle->key) == 1 && (getStateFlags(puzzle->key) & DISABLED) == 0) { - continue; - } - - // Check each Criteria - - bool criteriaMet = false; - for (Common::List >::iterator criteriaIter = puzzle->criteriaList.begin(); criteriaIter != puzzle->criteriaList.end(); ++criteriaIter) { - criteriaMet = false; - - for (Common::List::iterator entryIter = criteriaIter->begin(); entryIter != criteriaIter->end(); ++entryIter) { - // Get the value to compare against - uint argumentValue; - if (entryIter->argumentIsAKey) - argumentValue = getStateValue(entryIter->argument); - else - argumentValue = entryIter->argument; - - // Do the comparison - switch (entryIter->criteriaOperator) { - case Puzzle::EQUAL_TO: - criteriaMet = getStateValue(entryIter->key) == argumentValue; - break; - case Puzzle::NOT_EQUAL_TO: - criteriaMet = getStateValue(entryIter->key) != argumentValue; - break; - case Puzzle::GREATER_THAN: - criteriaMet = getStateValue(entryIter->key) > argumentValue; - break; - case Puzzle::LESS_THAN: - criteriaMet = getStateValue(entryIter->key) < argumentValue; - break; - } - - // If one check returns false, don't keep checking - if (!criteriaMet) { - break; - } - } - - // If any of the Criteria are *fully* met, then execute the results - if (criteriaMet) { - break; - } - } - - // criteriaList can be empty. Aka, the puzzle should be executed immediately - if (puzzle->criteriaList.empty() || criteriaMet) { - debug(1, "Puzzle %u criteria passed. Executing its ResultActions", puzzle->key); - - // Set the puzzle as completed - setStateValue(puzzle->key, 1); - - bool shouldContinue = true; - for (Common::List::iterator resultIter = puzzle->resultActions.begin(); resultIter != puzzle->resultActions.end(); ++resultIter) { - shouldContinue = shouldContinue && (*resultIter)->execute(_engine); - if (!shouldContinue) { - break; - } - } - - if (!shouldContinue) { - break; - } - } - } -} - -void ScriptManager::cleanStateTable() { - for (StateMap::iterator iter = _globalState.begin(); iter != _globalState.end(); ++iter) { - // If the value is equal to zero, we can purge it since getStateValue() - // will return zero if _globalState doesn't contain a key - if (iter->_value == 0) { - // Remove the node - _globalState.erase(iter); - } - } -} - -uint ScriptManager::getStateValue(uint32 key) { - if (_globalState.contains(key)) - return _globalState[key]; - else - return 0; -} - -void ScriptManager::setStateValue(uint32 key, uint value) { - _globalState[key] = value; - - if (_referenceTable.contains(key)) { - for (Common::Array::iterator iter = _referenceTable[key].begin(); iter != _referenceTable[key].end(); ++iter) { - _puzzlesToCheck.push((*iter)); - } - } -} - -uint ScriptManager::getStateFlags(uint32 key) { - if (_globalStateFlags.contains(key)) - return _globalStateFlags[key]; - else - return 0; -} - -void ScriptManager::setStateFlags(uint32 key, uint flags) { - _globalStateFlags[key] = flags; - - if (_referenceTable.contains(key)) { - for (Common::Array::iterator iter = _referenceTable[key].begin(); iter != _referenceTable[key].end(); ++iter) { - _puzzlesToCheck.push((*iter)); - } - } -} - -void ScriptManager::addToStateValue(uint32 key, uint valueToAdd) { - _globalState[key] += valueToAdd; -} - -void ScriptManager::addControl(Control *control) { - _activeControls.push_back(control); -} - -Control *ScriptManager::getControl(uint32 key) { - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - if ((*iter)->getKey() == key) { - return (*iter); - } - } - - return nullptr; -} - -void ScriptManager::focusControl(uint32 key) { - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - uint32 controlKey = (*iter)->getKey(); - - if (controlKey == key) { - (*iter)->focus(); - } else if (controlKey == _currentlyFocusedControl) { - (*iter)->unfocus(); - } - } - - _currentlyFocusedControl = key; -} - -void ScriptManager::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - (*iter)->onMouseDown(screenSpacePos, backgroundImageSpacePos); - } -} - -void ScriptManager::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - (*iter)->onMouseUp(screenSpacePos, backgroundImageSpacePos); - } -} - -bool ScriptManager::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - bool cursorWasChanged = false; - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - cursorWasChanged = cursorWasChanged || (*iter)->onMouseMove(screenSpacePos, backgroundImageSpacePos); - } - - return cursorWasChanged; -} - -void ScriptManager::onKeyDown(Common::KeyState keyState) { - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - (*iter)->onKeyDown(keyState); - } -} - -void ScriptManager::onKeyUp(Common::KeyState keyState) { - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - (*iter)->onKeyUp(keyState); - } -} - -void ScriptManager::changeLocation(char world, char room, char node, char view, uint32 offset) { - assert(world != 0); - debug(1, "Changing location to: %c %c %c %c %u", world, room, node, view, offset); - - // Auto save - _engine->getSaveManager()->autoSave(); - - // Clear all the containers - _referenceTable.clear(); - _puzzlesToCheck.clear(); - for (PuzzleList::iterator iter = _activePuzzles.begin(); iter != _activePuzzles.end(); ++iter) { - delete (*iter); - } - _activePuzzles.clear(); - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - delete (*iter); - } - _activeControls.clear(); - - // Revert to the idle cursor - _engine->getCursorManager()->revertToIdle(); - - // Reset the background velocity - _engine->getRenderManager()->setBackgroundVelocity(0); - - // Remove any alphaEntries - _engine->getRenderManager()->clearAlphaEntries(); - - // Clean the global state table - cleanStateTable(); - - // Parse into puzzles and controls - Common::String fileName = Common::String::format("%c%c%c%c.scr", world, room, node, view); - parseScrFile(fileName); - - // Change the background position - _engine->getRenderManager()->setBackgroundPosition(offset); - - // Enable all the controls - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - (*iter)->enable(); - } - - // Add all the local puzzles to the queue to be checked - for (PuzzleList::iterator iter = _activePuzzles.begin(); iter != _activePuzzles.end(); ++iter) { - // Reset any Puzzles that have the flag ONCE_PER_INST - if ((getStateFlags((*iter)->key) & ONCE_PER_INST) == ONCE_PER_INST) { - setStateValue((*iter)->key, 0); - } - - _puzzlesToCheck.push((*iter)); - } - - // Add all the global puzzles to the queue to be checked - for (PuzzleList::iterator iter = _globalPuzzles.begin(); iter != _globalPuzzles.end(); ++iter) { - // Reset any Puzzles that have the flag ONCE_PER_INST - if ((getStateFlags((*iter)->key) & ONCE_PER_INST) == ONCE_PER_INST) { - setStateValue((*iter)->key, 0); - } - - _puzzlesToCheck.push((*iter)); - } - - // Create the puzzle reference table - createReferenceTable(); - - // Update _currentLocation - _currentLocation.world = world; - _currentLocation.room = room; - _currentLocation.node = node; - _currentLocation.view = view; - _currentLocation.offset = offset; -} - -void ScriptManager::serializeStateTable(Common::WriteStream *stream) { - // Write the number of state value entries - stream->writeUint32LE(_globalState.size()); - - for (StateMap::iterator iter = _globalState.begin(); iter != _globalState.end(); ++iter) { - // Write out the key/value pair - stream->writeUint32LE(iter->_key); - stream->writeUint32LE(iter->_value); - } -} - -void ScriptManager::deserializeStateTable(Common::SeekableReadStream *stream) { - // Clear out the current table values - _globalState.clear(); - - // Read the number of key/value pairs - uint32 numberOfPairs = stream->readUint32LE(); - - for (uint32 i = 0; i < numberOfPairs; ++i) { - uint32 key = stream->readUint32LE(); - uint32 value = stream->readUint32LE(); - // Directly access the state table so we don't trigger Puzzle checks - _globalState[key] = value; - } -} - -void ScriptManager::serializeControls(Common::WriteStream *stream) { - // Count how many controls need to save their data - // Because WriteStream isn't seekable - uint32 numberOfControlsNeedingSerialization = 0; - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - if ((*iter)->needsSerialization()) { - numberOfControlsNeedingSerialization++; - } - } - stream->writeUint32LE(numberOfControlsNeedingSerialization); - - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - (*iter)->serialize(stream); - } -} - -void ScriptManager::deserializeControls(Common::SeekableReadStream *stream) { - uint32 numberOfControls = stream->readUint32LE(); - - for (uint32 i = 0; i < numberOfControls; ++i) { - uint32 key = stream->readUint32LE(); - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - if ((*iter)->getKey() == key) { - (*iter)->deserialize(stream); - break; - } - } - } -} - -Location ScriptManager::getCurrentLocation() const { - Location location = _currentLocation; - location.offset = _engine->getRenderManager()->getCurrentBackgroundOffset(); - - return location; -} - -} // End of namespace ZVision diff --git a/engines/zvision/script_manager.h b/engines/zvision/script_manager.h deleted file mode 100644 index a5079e3332..0000000000 --- a/engines/zvision/script_manager.h +++ /dev/null @@ -1,224 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef ZVISION_SCRIPT_MANAGER_H -#define ZVISION_SCRIPT_MANAGER_H - -#include "zvision/puzzle.h" -#include "zvision/control.h" - -#include "common/hashmap.h" -#include "common/queue.h" - - -namespace Common { -class String; -class SeekableReadStream; -} - -namespace ZVision { - -class ZVision; - -struct Location { - Location() : world('g'), room('a'), node('r'), view('y'), offset(0) {} - - char world; - char room; - char node; - char view; - uint32 offset; -}; - -enum StateFlags { - ONCE_PER_INST = 0x01, - DO_ME_NOW = 0x02, // Somewhat useless flag since anything that needs to be done immediately has no criteria - DISABLED = 0x04 -}; - -typedef Common::HashMap > PuzzleMap; -typedef Common::List PuzzleList; -typedef Common::Queue PuzzleQueue; -typedef Common::List ControlList; -typedef Common::HashMap StateMap; -typedef Common::HashMap StateFlagMap; - -class ScriptManager { -public: - ScriptManager(ZVision *engine); - ~ScriptManager(); - -private: - ZVision *_engine; - /** - * Holds the global state variable. Do NOT directly modify this. Use the accessors and - * mutators getStateValue() and setStateValue(). This ensures that Puzzles that reference a - * particular state key are checked after the key is modified. - */ - StateMap _globalState; - /** - * Holds the flags for the global states. This is used to enable/disable puzzles and/or - * controls as well as which puzzles should are allowed to be re-executed - */ - StateFlagMap _globalStateFlags; - /** References _globalState keys to Puzzles */ - PuzzleMap _referenceTable; - /** Holds the Puzzles that should be checked this frame */ - PuzzleQueue _puzzlesToCheck; - /** Holds the currently active puzzles */ - PuzzleList _activePuzzles; - /** Holds the global puzzles */ - PuzzleList _globalPuzzles; - /** Holds the currently active controls */ - ControlList _activeControls; - - Location _currentLocation; - - uint32 _currentlyFocusedControl; - -public: - void initialize(); - void update(uint deltaTimeMillis); - - uint getStateValue(uint32 key); - void setStateValue(uint32 key, uint value); - void addToStateValue(uint32 key, uint valueToAdd); - - uint getStateFlags(uint32 key); - void setStateFlags(uint32 key, uint flags); - - void addControl(Control *control); - Control *getControl(uint32 key); - - void focusControl(uint32 key); - - /** - * Called when LeftMouse is pushed. - * - * @param screenSpacePos The position of the mouse in screen space - * @param backgroundImageSpacePos The position of the mouse in background image space - */ - void onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); - /** - * Called when LeftMouse is lifted. - * - * @param screenSpacePos The position of the mouse in screen space - * @param backgroundImageSpacePos The position of the mouse in background image space - */ - void onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); - /** - * Called on every MouseMove. - * - * @param screenSpacePos The position of the mouse in screen space - * @param backgroundImageSpacePos The position of the mouse in background image space - * @return Was the cursor changed? - */ - bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); - /** - * Called when a key is pressed. - * - * @param keycode The key that was pressed - */ - void onKeyDown(Common::KeyState keyState); - /** - * Called when a key is released. - * - * @param keycode The key that was pressed - */ - void onKeyUp(Common::KeyState keyState); - - void changeLocation(char world, char room, char node, char view, uint32 offset); - - void serializeStateTable(Common::WriteStream *stream); - void deserializeStateTable(Common::SeekableReadStream *stream); - void serializeControls(Common::WriteStream *stream); - void deserializeControls(Common::SeekableReadStream *stream); - - Location getCurrentLocation() const; - -private: - void createReferenceTable(); - void updateNodes(uint deltaTimeMillis); - void checkPuzzleCriteria(); - void cleanStateTable(); - -// TODO: Make this private. It was only made public so Console::cmdParseAllScrFiles() could use it -public: - /** - * Parses a script file into triggers and events - * - * @param fileName Name of the .scr file - * @param isGlobal Are the puzzles included in the file global (true). AKA, the won't be purged during location changes - */ - void parseScrFile(const Common::String &fileName, bool isGlobal = false); - -private: - /** - * Parses the stream into a Puzzle object - * Helper method for parseScrFile. - * - * @param puzzle The object to store what is parsed - * @param stream Scr file stream - */ - void parsePuzzle(Puzzle *puzzle, Common::SeekableReadStream &stream); - - /** - * Parses the stream into a Criteria object - * Helper method for parsePuzzle. - * - * @param criteria Pointer to the Criteria object to fill - * @param stream Scr file stream - * @return Whether any criteria were read - */ - bool parseCriteria(Common::SeekableReadStream &stream, Common::List > &criteriaList) const; - - /** - * Parses the stream into a ResultAction objects - * Helper method for parsePuzzle. - * - * @param stream Scr file stream - * @param actionList The list where the results will be added - * @return Created Results object - */ - void parseResults(Common::SeekableReadStream &stream, Common::List &actionList) const; - - /** - * Helper method for parsePuzzle. Parses the stream into a bitwise or of the StateFlags enum - * - * @param stream Scr file stream - * @return Bitwise OR of all the flags set within the puzzle - */ - uint parseFlags(Common::SeekableReadStream &stream) const; - - /** - * Helper method for parseScrFile. Parses the stream into a Control object - * - * @param line The line initially read - * @param stream Scr file stream - */ - void parseControl(Common::String &line, Common::SeekableReadStream &stream); -}; - - -} // End of namespace ZVision - -#endif diff --git a/engines/zvision/scripting/actions.cpp b/engines/zvision/scripting/actions.cpp new file mode 100644 index 0000000000..0bb553a559 --- /dev/null +++ b/engines/zvision/scripting/actions.cpp @@ -0,0 +1,401 @@ +/* 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 "zvision/actions.h" + +#include "zvision/zvision.h" +#include "zvision/script_manager.h" +#include "zvision/render_manager.h" +#include "zvision/zork_raw.h" +#include "zvision/zork_avi_decoder.h" +#include "zvision/timer_node.h" +#include "zvision/animation_control.h" + +#include "common/file.h" + +#include "audio/decoders/wave.h" + + +namespace ZVision { + +////////////////////////////////////////////////////////////////////////////// +// ActionAdd +////////////////////////////////////////////////////////////////////////////// + +ActionAdd::ActionAdd(const Common::String &line) { + sscanf(line.c_str(), "%*[^(](%u,%u)", &_key, &_value); +} + +bool ActionAdd::execute(ZVision *engine) { + engine->getScriptManager()->addToStateValue(_key, _value); + return true; +} + + +////////////////////////////////////////////////////////////////////////////// +// ActionAssign +////////////////////////////////////////////////////////////////////////////// + +ActionAssign::ActionAssign(const Common::String &line) { + sscanf(line.c_str(), "%*[^(](%u, %u)", &_key, &_value); +} + +bool ActionAssign::execute(ZVision *engine) { + engine->getScriptManager()->setStateValue(_key, _value); + return true; +} + + +////////////////////////////////////////////////////////////////////////////// +// ActionAttenuate +////////////////////////////////////////////////////////////////////////////// + +ActionAttenuate::ActionAttenuate(const Common::String &line) { + sscanf(line.c_str(), "%*[^(](%u, %d)", &_key, &_attenuation); +} + +bool ActionAttenuate::execute(ZVision *engine) { + // TODO: Implement + return true; +} + + +////////////////////////////////////////////////////////////////////////////// +// ActionChangeLocation +////////////////////////////////////////////////////////////////////////////// + +ActionChangeLocation::ActionChangeLocation(const Common::String &line) { + sscanf(line.c_str(), "%*[^(](%c,%c,%c%c,%u)", &_world, &_room, &_node, &_view, &_offset); +} + +bool ActionChangeLocation::execute(ZVision *engine) { + // We can't directly call ScriptManager::ChangeLocationIntern() because doing so clears all the Puzzles, and thus would corrupt the current puzzle checking + engine->getScriptManager()->changeLocation(_world, _room, _node, _view, _offset); + // Tell the puzzle system to stop checking any more puzzles + return false; +} + + +////////////////////////////////////////////////////////////////////////////// +// ActionCrossfade +////////////////////////////////////////////////////////////////////////////// + +ActionCrossfade::ActionCrossfade(const Common::String &line) { + sscanf(line.c_str(), + "%*[^(](%u %u %u %u %u %u %u)", + &_keyOne, &_keyTwo, &_oneStartVolume, &_twoStartVolume, &_oneEndVolume, &_twoEndVolume, &_timeInMillis); +} + +bool ActionCrossfade::execute(ZVision *engine) { + // TODO: Implement + return true; +} + + +////////////////////////////////////////////////////////////////////////////// +// ActionDisableControl +////////////////////////////////////////////////////////////////////////////// + +ActionDisableControl::ActionDisableControl(const Common::String &line) { + sscanf(line.c_str(), "%*[^(](%u)", &_key); +} + +bool ActionDisableControl::execute(ZVision *engine) { + debug("Disabling control %u", _key); + + ScriptManager *scriptManager = engine->getScriptManager(); + scriptManager->setStateFlags(_key, scriptManager->getStateFlags(_key) | StateFlags::DISABLED); + + return true; +} + + +////////////////////////////////////////////////////////////////////////////// +// ActionEnableControl +////////////////////////////////////////////////////////////////////////////// + +ActionEnableControl::ActionEnableControl(const Common::String &line) { + sscanf(line.c_str(), "%*[^(](%u)", &_key); +} + +bool ActionEnableControl::execute(ZVision *engine) { + debug("Enabling control %u", _key); + + ScriptManager *scriptManager = engine->getScriptManager(); + scriptManager->setStateFlags(_key, scriptManager->getStateFlags(_key) & ~StateFlags::DISABLED); + + return true; +} + + +////////////////////////////////////////////////////////////////////////////// +// ActionMusic +////////////////////////////////////////////////////////////////////////////// + +ActionMusic::ActionMusic(const Common::String &line) : _volume(255) { + uint type; + char fileNameBuffer[25]; + uint loop; + uint volume = 255; + + sscanf(line.c_str(), "%*[^:]:%*[^:]:%u(%u %25s %u %u)", &_key, &type, fileNameBuffer, &loop, &volume); + + // type 4 are midi sound effect files + if (type == 4) { + _soundType = Audio::Mixer::kSFXSoundType; + _fileName = Common::String::format("midi/%s/%u.wav", fileNameBuffer, loop); + _loop = false; + } else { + // TODO: See what the other types are so we can specify the correct Mixer::SoundType. In the meantime use kPlainSoundType + _soundType = Audio::Mixer::kPlainSoundType; + _fileName = Common::String(fileNameBuffer); + _loop = loop == 1 ? true : false; + } + + // Volume is optional. If it doesn't appear, assume full volume + if (volume != 255) { + // Volume in the script files is mapped to [0, 100], but the ScummVM mixer uses [0, 255] + _volume = volume * 255 / 100; + } +} + +bool ActionMusic::execute(ZVision *engine) { + Audio::RewindableAudioStream *audioStream; + + if (_fileName.contains(".wav")) { + Common::File *file = new Common::File(); + if (file->open(_fileName)) { + audioStream = Audio::makeWAVStream(file, DisposeAfterUse::YES); + } else { + warning("Unable to open %s", _fileName.c_str()); + return false; + } + } else { + audioStream = makeRawZorkStream(_fileName, engine); + } + + if (_loop) { + Audio::LoopingAudioStream *loopingAudioStream = new Audio::LoopingAudioStream(audioStream, 0, DisposeAfterUse::YES); + engine->_mixer->playStream(_soundType, 0, loopingAudioStream, -1, _volume); + } else { + engine->_mixer->playStream(_soundType, 0, audioStream, -1, _volume); + } + + return true; +} + + +////////////////////////////////////////////////////////////////////////////// +// ActionPreloadAnimation +////////////////////////////////////////////////////////////////////////////// + +ActionPreloadAnimation::ActionPreloadAnimation(const Common::String &line) { + char fileName[25]; + + // The two %*u are always 0 and dont seem to have a use + sscanf(line.c_str(), "%*[^:]:%*[^:]:%u(%25s %*u %*u %u %u)", &_key, fileName, &_mask, &_framerate); + + _fileName = Common::String(fileName); +} + +bool ActionPreloadAnimation::execute(ZVision *engine) { + // TODO: We ignore the mask and framerate atm. Mask refers to a key color used for binary alpha. We assume the framerate is the default framerate embedded in the videos + + // TODO: Check if the Control already exists + + // Create the control, but disable it until PlayPreload is called + ScriptManager *scriptManager = engine->getScriptManager(); + scriptManager->addControl(new AnimationControl(engine, _key, _fileName)); + scriptManager->setStateFlags(_key, scriptManager->getStateFlags(_key) | StateFlags::DISABLED); + + return true; +} + + +////////////////////////////////////////////////////////////////////////////// +// ActionPlayAnimation +////////////////////////////////////////////////////////////////////////////// + +ActionPlayAnimation::ActionPlayAnimation(const Common::String &line) { + char fileName[25]; + + // The two %*u are always 0 and dont seem to have a use + sscanf(line.c_str(), + "%*[^:]:%*[^:]:%u(%25s %u %u %u %u %u %u %u %*u %*u %u %u)", + &_key, fileName, &_x, &_y, &_width, &_height, &_start, &_end, &_loopCount, &_mask, &_framerate); + + _fileName = Common::String(fileName); +} + +bool ActionPlayAnimation::execute(ZVision *engine) { + // TODO: Implement + return true; +} + + +////////////////////////////////////////////////////////////////////////////// +// ActionPlayPreloadAnimation +////////////////////////////////////////////////////////////////////////////// + +ActionPlayPreloadAnimation::ActionPlayPreloadAnimation(const Common::String &line) { + sscanf(line.c_str(), + "%*[^:]:%*[^:]:%u(%u %u %u %u %u %u %u %u)", + &_animationKey, &_controlKey, &_x1, &_y1, &_x2, &_y2, &_startFrame, &_endFrame, &_loopCount); +} + +bool ActionPlayPreloadAnimation::execute(ZVision *engine) { + // Find the control + AnimationControl *control = (AnimationControl *)engine->getScriptManager()->getControl(_controlKey); + + // Set the needed values within the control + control->setAnimationKey(_animationKey); + control->setLoopCount(_loopCount); + control->setXPos(_x1); + control->setYPost(_y1); + + // Enable the control. ScriptManager will take care of the rest + control->enable(); + + return true; +} + + +////////////////////////////////////////////////////////////////////////////// +// ActionQuit +////////////////////////////////////////////////////////////////////////////// + +bool ActionQuit::execute(ZVision *engine) { + engine->quitGame(); + + return true; +} + + +////////////////////////////////////////////////////////////////////////////// +// ActionRandom +////////////////////////////////////////////////////////////////////////////// + +ActionRandom::ActionRandom(const Common::String &line) { + sscanf(line.c_str(), "%*[^:]:%*[^:]:%u, %u)", &_key, &_max); +} + +bool ActionRandom::execute(ZVision *engine) { + uint randNumber = engine->getRandomSource()->getRandomNumber(_max); + engine->getScriptManager()->setStateValue(_key, randNumber); + return true; +} + + +////////////////////////////////////////////////////////////////////////////// +// ActionSetPartialScreen +////////////////////////////////////////////////////////////////////////////// + +ActionSetPartialScreen::ActionSetPartialScreen(const Common::String &line) { + char fileName[25]; + uint color; + + sscanf(line.c_str(), "%*[^(](%u %u %25s %*u %u)", &_x, &_y, fileName, &color); + + _fileName = Common::String(fileName); + + if (color > 0xFFFF) { + warning("Background color for ActionSetPartialScreen is bigger than a uint16"); + } + _backgroundColor = color; +} + +bool ActionSetPartialScreen::execute(ZVision *engine) { + RenderManager *renderManager = engine->getRenderManager(); + + if (_backgroundColor > 0) { + renderManager->clearWorkingWindowTo555Color(_backgroundColor); + } + renderManager->renderImageToScreen(_fileName, _x, _y); + + return true; +} + + +////////////////////////////////////////////////////////////////////////////// +// ActionSetScreen +////////////////////////////////////////////////////////////////////////////// + +ActionSetScreen::ActionSetScreen(const Common::String &line) { + char fileName[25]; + sscanf(line.c_str(), "%*[^(](%25[^)])", fileName); + + _fileName = Common::String(fileName); +} + +bool ActionSetScreen::execute(ZVision *engine) { + engine->getRenderManager()->setBackgroundImage(_fileName); + + return true; +} + + +////////////////////////////////////////////////////////////////////////////// +// ActionStreamVideo +////////////////////////////////////////////////////////////////////////////// + +ActionStreamVideo::ActionStreamVideo(const Common::String &line) { + char fileName[25]; + uint skippable; + + sscanf(line.c_str(), "%*[^(](%25s %u %u %u %u %u %u)", fileName, &_x1, &_y1, &_x2, &_y2, &_flags, &skippable); + + _fileName = Common::String(fileName); + _skippable = (skippable == 0) ? false : true; +} + +bool ActionStreamVideo::execute(ZVision *engine) { + ZorkAVIDecoder decoder; + if (!decoder.loadFile(_fileName)) { + return true; + } + + Common::Rect destRect; + if ((_flags & DIFFERENT_DIMENSIONS) == DIFFERENT_DIMENSIONS) { + destRect = Common::Rect(_x1, _y1, _x2, _y2); + } + + engine->playVideo(decoder, destRect, _skippable); + return true; +} + + +////////////////////////////////////////////////////////////////////////////// +// ActionTimer +////////////////////////////////////////////////////////////////////////////// + +ActionTimer::ActionTimer(const Common::String &line) { + sscanf(line.c_str(), "%*[^:]:%*[^:]:%u(%u)", &_key, &_time); +} + +bool ActionTimer::execute(ZVision *engine) { + engine->getScriptManager()->addControl(new TimerNode(engine, _key, _time)); + return true; +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/actions.h b/engines/zvision/scripting/actions.h new file mode 100644 index 0000000000..afa3e3a2ac --- /dev/null +++ b/engines/zvision/scripting/actions.h @@ -0,0 +1,346 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ZVISION_ACTIONS_H +#define ZVISION_ACTIONS_H + +#include "common/str.h" + +#include "audio/mixer.h" + + +namespace ZVision { + +// Forward declaration of ZVision. This file is included before ZVision is declared +class ZVision; + +/** + * The base class that represents any action that a Puzzle can take. + * This class is purely virtual. + */ +class ResultAction { +public: + virtual ~ResultAction() {} + /** + * This is called by the script system whenever a Puzzle's criteria are found to be true. + * It should execute any necessary actions and return a value indicating whether the script + * system should continue to test puzzles. In 99% of cases this will be 'true'. + * + * @param engine A pointer to the base engine so the ResultAction can access all the necessary methods + * @return Should the script system continue to test any remaining puzzles (true) or immediately break and go on to the next frame (false) + */ + virtual bool execute(ZVision *engine) = 0; +}; + + +// The different types of actions +// DEBUG, +// DISABLE_CONTROL, +// DISABLE_VENUS, +// DISPLAY_MESSAGE, +// DISSOLVE, +// DISTORT, +// ENABLE_CONTROL, +// FLUSH_MOUSE_EVENTS, +// INVENTORY, +// KILL, +// MENU_BAR_ENABLE, +// MUSIC, +// PAN_TRACK, +// PLAY_PRELOAD, +// PREFERENCES, +// QUIT, +// RANDOM, +// REGION, +// RESTORE_GAME, +// ROTATE_TO, +// SAVE_GAME, +// SET_PARTIAL_SCREEN, +// SET_SCREEN, +// SET_VENUS, +// STOP, +// STREAM_VIDEO, +// SYNC_SOUND, +// TTY_TEXT, +// UNIVERSE_MUSIC, + +class ActionAdd : public ResultAction { +public: + ActionAdd(const Common::String &line); + bool execute(ZVision *engine); + +private: + uint32 _key; + uint _value; +}; + +class ActionAssign : public ResultAction { +public: + ActionAssign(const Common::String &line); + bool execute(ZVision *engine); + +private: + uint32 _key; + uint _value; +}; + +class ActionAttenuate : public ResultAction { +public: + ActionAttenuate(const Common::String &line); + bool execute(ZVision *engine); + +private: + uint32 _key; + int _attenuation; +}; + +class ActionChangeLocation : public ResultAction { +public: + ActionChangeLocation(const Common::String &line); + bool execute(ZVision *engine); + +private: + char _world; + char _room; + char _node; + char _view; + uint32 _offset; +}; + +class ActionCrossfade : public ResultAction { +public: + ActionCrossfade(const Common::String &line); + bool execute(ZVision *engine); + +private: + uint32 _keyOne; + uint32 _keyTwo; + uint _oneStartVolume; + uint _twoStartVolume; + uint _oneEndVolume; + uint _twoEndVolume; + uint _timeInMillis; +}; + +class ActionDebug : public ResultAction { +public: + ActionDebug(const Common::String &line); + bool execute(ZVision *engine); + +private: +}; + +class ActionDelayRender : public ResultAction { +public: + ActionDelayRender(const Common::String &line); + bool execute(ZVision *engine); + +private: + // TODO: Check if this should actually be frames or if it should be milliseconds/seconds + uint32 framesToDelay; +}; + +class ActionDisableControl : public ResultAction { +public: + ActionDisableControl(const Common::String &line); + bool execute(ZVision *engine); + +private: + uint32 _key; +}; + +class ActionDisableVenus : public ResultAction { +public: + ActionDisableVenus(const Common::String &line); + bool execute(ZVision *engine); + +private: +}; + +class ActionDisplayMessage : public ResultAction { +public: + ActionDisplayMessage(const Common::String &line); + bool execute(ZVision *engine); + +private: +}; + +class ActionDissolve : public ResultAction { +public: + ActionDissolve(); + bool execute(ZVision *engine); +}; + +class ActionDistort : public ResultAction { +public: + ActionDistort(const Common::String &line); + bool execute(ZVision *engine); + +private: +}; + +class ActionEnableControl : public ResultAction { +public: + ActionEnableControl(const Common::String &line); + bool execute(ZVision *engine); + +private: + uint32 _key; +}; + +class ActionMusic : public ResultAction { +public: + ActionMusic(const Common::String &line); + bool execute(ZVision *engine); + +private: + uint32 _key; + Audio::Mixer::SoundType _soundType; + Common::String _fileName; + bool _loop; + byte _volume; +}; + +class ActionPlayAnimation : public ResultAction { +public: + ActionPlayAnimation(const Common::String &line); + bool execute(ZVision *engine); + +private: + uint32 _key; + Common::String _fileName; + uint32 _x; + uint32 _y; + uint32 _width; + uint32 _height; + uint32 _start; + uint32 _end; + uint _mask; + uint _framerate; + uint _loopCount; +}; + +class ActionPlayPreloadAnimation : public ResultAction { +public: + ActionPlayPreloadAnimation(const Common::String &line); + bool execute(ZVision *engine); + +private: + uint32 _animationKey; + uint32 _controlKey; + uint32 _x1; + uint32 _y1; + uint32 _x2; + uint32 _y2; + uint _startFrame; + uint _endFrame; + uint _loopCount; +}; + +class ActionPreloadAnimation : public ResultAction { +public: + ActionPreloadAnimation(const Common::String &line); + bool execute(ZVision *engine); + +private: + uint32 _key; + Common::String _fileName; + uint _mask; + uint _framerate; +}; + +class ActionQuit : public ResultAction { +public: + ActionQuit() {} + bool execute(ZVision *engine); +}; + +// TODO: See if this exists in ZGI. It doesn't in ZNem +class ActionUnloadAnimation : public ResultAction { +public: + ActionUnloadAnimation(const Common::String &line); + bool execute(ZVision *engine); +}; + +class ActionRandom : public ResultAction { +public: + ActionRandom(const Common::String &line); + bool execute(ZVision *engine); + +private: + uint32 _key; + uint _max; +}; + +class ActionSetPartialScreen : public ResultAction { +public: + ActionSetPartialScreen(const Common::String &line); + bool execute(ZVision *engine); + +private: + uint _x; + uint _y; + Common::String _fileName; + uint16 _backgroundColor; +}; + +class ActionSetScreen : public ResultAction { +public: + ActionSetScreen(const Common::String &line); + bool execute(ZVision *engine); + +private: + Common::String _fileName; +}; + +class ActionStreamVideo : public ResultAction { +public: + ActionStreamVideo(const Common::String &line); + bool execute(ZVision *engine); + +private: + enum { + DIFFERENT_DIMENSIONS = 0x1 // 0x1 flags that the destRect dimensions are different from the original video dimensions + }; + + Common::String _fileName; + uint _x1; + uint _y1; + uint _x2; + uint _y2; + uint _flags; + bool _skippable; +}; + +class ActionTimer : public ResultAction { +public: + ActionTimer(const Common::String &line); + bool execute(ZVision *engine); + +private: + uint32 _key; + uint _time; +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/scripting/control.cpp b/engines/zvision/scripting/control.cpp new file mode 100644 index 0000000000..bcbdabc143 --- /dev/null +++ b/engines/zvision/scripting/control.cpp @@ -0,0 +1,124 @@ +/* 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 "zvision/control.h" + +#include "zvision/zvision.h" +#include "zvision/render_manager.h" +#include "zvision/utility.h" + +#include "common/stream.h" + + +namespace ZVision { + +void Control::enable() { + if (!_enabled) { + _enabled = true; + return; + } + + debug("Control %u is already enabled", _key); +} + +void Control::disable() { + if (_enabled) { + _enabled = false; + return; + } + + debug("Control %u is already disabled", _key); +} + +void Control::parseFlatControl(ZVision *engine) { + engine->getRenderManager()->getRenderTable()->setRenderState(RenderTable::FLAT); +} + +void Control::parsePanoramaControl(ZVision *engine, Common::SeekableReadStream &stream) { + RenderTable *renderTable = engine->getRenderManager()->getRenderTable(); + renderTable->setRenderState(RenderTable::PANORAMA); + + // Loop until we find the closing brace + Common::String line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + + while (!stream.eos() && !line.contains('}')) { + if (line.matchString("angle*", true)) { + float fov; + sscanf(line.c_str(), "angle(%f)", &fov); + renderTable->setPanoramaFoV(fov); + } else if (line.matchString("linscale*", true)) { + float scale; + sscanf(line.c_str(), "linscale(%f)", &scale); + renderTable->setPanoramaScale(scale); + } else if (line.matchString("reversepana*", true)) { + uint reverse; + sscanf(line.c_str(), "reversepana(%u)", &reverse); + if (reverse == 1) { + renderTable->setPanoramaReverse(true); + } + } else if (line.matchString("zeropoint*", true)) { + // TODO: Implement + } + + line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + } + + renderTable->generateRenderTable(); +} + +void Control::parseTiltControl(ZVision *engine, Common::SeekableReadStream &stream) { + RenderTable *renderTable = engine->getRenderManager()->getRenderTable(); + renderTable->setRenderState(RenderTable::TILT); + + // Loop until we find the closing brace + Common::String line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + + while (!stream.eos() && !line.contains('}')) { + if (line.matchString("angle*", true)) { + float fov; + sscanf(line.c_str(), "angle(%f)", &fov); + renderTable->setTiltFoV(fov); + } else if (line.matchString("linscale*", true)) { + float scale; + sscanf(line.c_str(), "linscale(%f)", &scale); + renderTable->setTiltScale(scale); + } else if (line.matchString("reversepana*", true)) { + uint reverse; + sscanf(line.c_str(), "reversepana(%u)", &reverse); + if (reverse == 1) { + renderTable->setTiltReverse(true); + } + } + + line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + } + + renderTable->generateRenderTable(); +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/control.h b/engines/zvision/scripting/control.h new file mode 100644 index 0000000000..770c540a12 --- /dev/null +++ b/engines/zvision/scripting/control.h @@ -0,0 +1,146 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ZVISION_CONTROL_H +#define ZVISION_CONTROL_H + +#include "common/keyboard.h" + + +namespace Common { +class SeekableReadStream; +struct Point; +class WriteStream; +} + +namespace ZVision { + +class ZVision; + +class Control { +public: + Control() : _engine(0), _key(0), _enabled(false) {} + Control(ZVision *engine, uint32 key) : _engine(engine), _key(key), _enabled(false) {} + virtual ~Control() {} + + uint32 getKey() { return _key; } + + virtual void enable(); + virtual void disable(); + virtual void focus() {} + virtual void unfocus() {} + /** + * Called when LeftMouse is pushed. Default is NOP. + * + * @param screenSpacePos The position of the mouse in screen space + * @param backgroundImageSpacePos The position of the mouse in background image space + */ + virtual void onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {} + /** + * Called when LeftMouse is lifted. Default is NOP. + * + * @param screenSpacePos The position of the mouse in screen space + * @param backgroundImageSpacePos The position of the mouse in background image space + */ + virtual void onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {} + /** + * Called on every MouseMove. Default is NOP. + * + * @param screenSpacePos The position of the mouse in screen space + * @param backgroundImageSpacePos The position of the mouse in background image space + * @return Was the cursor changed? + */ + virtual bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { return false; } + /** + * Called when a key is pressed. Default is NOP. + * + * @param keycode The key that was pressed + */ + virtual void onKeyDown(Common::KeyState keyState) {} + /** + * Called when a key is released. Default is NOP. + * + * @param keycode The key that was pressed + */ + virtual void onKeyUp(Common::KeyState keyState) {} + /** + * Processes the node given the deltaTime since last frame. Default is NOP. + * + * @param deltaTimeInMillis The number of milliseconds that have passed since last frame + * @return If true, the node can be deleted after process() finishes + */ + virtual bool process(uint32 deltaTimeInMillis) { return false; } + /** + * Serialize a Control for save game use. This should only be used if a Control needs + * to save values that would be different from initialization. AKA a TimerNode needs to + * store the amount of time left on the timer. Any Controls overriding this *MUST* write + * their key as the first data outputted. The default implementation is NOP. + * + * NOTE: If this method is overridden, you MUST also override deserialize() + * and needsSerialization() + * + * @param stream Stream to write any needed data to + */ + virtual void serialize(Common::WriteStream *stream) {} + /** + * De-serialize data from a save game stream. This should only be implemented if the + * Control also implements serialize(). The calling method assumes the size of the + * data read from the stream exactly equals that written in serialize(). The default + * implementation is NOP. + * + * NOTE: If this method is overridden, you MUST also override serialize() + * and needsSerialization() + * + * @param stream Save game file stream + */ + virtual void deserialize(Common::SeekableReadStream *stream) {} + /** + * If a Control overrides serialize() and deserialize(), this should return true + * + * @return Does the Control need save game serialization? + */ + virtual inline bool needsSerialization() { return false; } + +protected: + ZVision * _engine; + uint32 _key; + bool _enabled; + +// Static member functions +public: + static void parseFlatControl(ZVision *engine); + static void parsePanoramaControl(ZVision *engine, Common::SeekableReadStream &stream); + static void parseTiltControl(ZVision *engine, Common::SeekableReadStream &stream); +}; + +// TODO: Implement InputControl +// TODO: Implement SaveControl +// TODO: Implement SlotControl +// TODO: Implement SafeControl +// TODO: Implement FistControl +// TODO: Implement HotMovieControl +// TODO: Implement PaintControl +// TODO: Implement TilterControl + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/scripting/controls/animation_control.cpp b/engines/zvision/scripting/controls/animation_control.cpp new file mode 100644 index 0000000000..1143380501 --- /dev/null +++ b/engines/zvision/scripting/controls/animation_control.cpp @@ -0,0 +1,263 @@ +/* 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 "zvision/animation_control.h" + +#include "zvision/zvision.h" +#include "zvision/render_manager.h" +#include "zvision/script_manager.h" +#include "zvision/rlf_animation.h" +#include "zvision/zork_avi_decoder.h" + +#include "video/video_decoder.h" + +#include "graphics/surface.h" + + +namespace ZVision { + +AnimationControl::AnimationControl(ZVision *engine, uint32 controlKey, const Common::String &fileName) + : Control(engine, controlKey), + _fileType(RLF), + _loopCount(1), + _currentLoop(0), + _accumulatedTime(0), + _cachedFrame(0), + _cachedFrameNeedsDeletion(false) { + if (fileName.hasSuffix(".rlf")) { + _fileType = RLF; + _animation.rlf = new RlfAnimation(fileName, false); + } else if (fileName.hasSuffix(".avi")) { + _fileType = AVI; + _animation.avi = new ZorkAVIDecoder(); + _animation.avi->loadFile(fileName); + } else { + warning("Unrecognized animation file type: %s", fileName.c_str()); + } + + _cachedFrame = new Graphics::Surface(); +} + +AnimationControl::~AnimationControl() { + if (_fileType == RLF) { + delete _animation.rlf; + } else if (_fileType == AVI) { + delete _animation.avi; + } + + _cachedFrame->free(); + delete _cachedFrame; +} + +bool AnimationControl::process(uint32 deltaTimeInMillis) { + if (!_enabled) { + return false; + } + + bool finished = false; + + if (_fileType == RLF) { + _accumulatedTime += deltaTimeInMillis; + + uint32 frameTime = _animation.rlf->frameTime(); + if (_accumulatedTime >= frameTime) { + while (_accumulatedTime >= frameTime) { + _accumulatedTime -= frameTime; + + // Make sure the frame is inside the working window + // If it's not, then just return + + RenderManager *renderManager = _engine->getRenderManager(); + Common::Point workingWindowPoint = renderManager->imageSpaceToWorkingWindowSpace(Common::Point(_x, _y)); + Common::Rect subRect(workingWindowPoint.x, workingWindowPoint.y, workingWindowPoint.x + _animation.rlf->width(), workingWindowPoint.y + _animation.rlf->height()); + + // If the clip returns false, it means the animation is outside the working window + if (!renderManager->clipRectToWorkingWindow(subRect)) { + return false; + } + + const Graphics::Surface *frame = _animation.rlf->getNextFrame(); + + // Animation frames for PANORAMAs are transposed, so un-transpose them + RenderTable::RenderState state = renderManager->getRenderTable()->getRenderState(); + if (state == RenderTable::PANORAMA) { + Graphics::Surface *tranposedFrame = RenderManager::tranposeSurface(frame); + + renderManager->copyRectToWorkingWindow((uint16 *)tranposedFrame->getBasePtr(tranposedFrame->w - subRect.width(), tranposedFrame->h - subRect.height()), subRect.left, subRect.top, _animation.rlf->width(), subRect.width(), subRect.height()); + + // If the background can move, we need to cache the last frame so it can be rendered during background movement + if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { + if (_cachedFrameNeedsDeletion) { + _cachedFrame->free(); + delete _cachedFrame; + _cachedFrameNeedsDeletion = false; + } + _cachedFrame = tranposedFrame; + _cachedFrameNeedsDeletion = true; + } else { + // Cleanup + tranposedFrame->free(); + delete tranposedFrame; + } + } else { + renderManager->copyRectToWorkingWindow((const uint16 *)frame->getBasePtr(frame->w - subRect.width(), frame->h - subRect.height()), subRect.left, subRect.top, _animation.rlf->width(), subRect.width(), subRect.height()); + + // If the background can move, we need to cache the last frame so it can be rendered during background movement + if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { + if (_cachedFrameNeedsDeletion) { + _cachedFrame->free(); + delete _cachedFrame; + _cachedFrameNeedsDeletion = false; + } + _cachedFrame->copyFrom(*frame); + } + } + + // Check if we should continue looping + if (_animation.rlf->endOfAnimation()) { + _animation.rlf->seekToFrame(-1); + if (_loopCount > 0) { + _currentLoop++; + if (_currentLoop >= _loopCount) { + finished = true; + } + } + } + } + } else { + // If the background can move, we have to keep rendering animation frames, otherwise the animation flickers during background movement + RenderManager *renderManager = _engine->getRenderManager(); + RenderTable::RenderState state = renderManager->getRenderTable()->getRenderState(); + + if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { + Common::Point workingWindowPoint = renderManager->imageSpaceToWorkingWindowSpace(Common::Point(_x, _y)); + Common::Rect subRect(workingWindowPoint.x, workingWindowPoint.y, workingWindowPoint.x + _cachedFrame->w, workingWindowPoint.y + _cachedFrame->h); + + // If the clip returns false, it means the animation is outside the working window + if (!renderManager->clipRectToWorkingWindow(subRect)) { + return false; + } + + renderManager->copyRectToWorkingWindow((uint16 *)_cachedFrame->getBasePtr(_cachedFrame->w - subRect.width(), _cachedFrame->h - subRect.height()), subRect.left, subRect.top, _cachedFrame->w, subRect.width(), subRect.height()); + } + } + } else if (_fileType == AVI) { + if (!_animation.avi->isPlaying()) { + _animation.avi->start(); + } + + if (_animation.avi->needsUpdate()) { + const Graphics::Surface *frame = _animation.avi->decodeNextFrame(); + + if (frame) { + // Make sure the frame is inside the working window + // If it's not, then just return + + RenderManager *renderManager = _engine->getRenderManager(); + Common::Point workingWindowPoint = renderManager->imageSpaceToWorkingWindowSpace(Common::Point(_x, _y)); + Common::Rect subRect(workingWindowPoint.x, workingWindowPoint.y, workingWindowPoint.x + frame->w, workingWindowPoint.y + frame->h); + + // If the clip returns false, it means the animation is outside the working window + if (!renderManager->clipRectToWorkingWindow(subRect)) { + return false; + } + + // Animation frames for PANORAMAs are transposed, so un-transpose them + RenderTable::RenderState state = renderManager->getRenderTable()->getRenderState(); + if (state == RenderTable::PANORAMA) { + Graphics::Surface *tranposedFrame = RenderManager::tranposeSurface(frame); + + renderManager->copyRectToWorkingWindow((uint16 *)tranposedFrame->getBasePtr(tranposedFrame->w - subRect.width(), tranposedFrame->h - subRect.height()), subRect.left, subRect.top, frame->w, subRect.width(), subRect.height()); + + // If the background can move, we need to cache the last frame so it can be rendered during background movement + if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { + if (_cachedFrameNeedsDeletion) { + _cachedFrame->free(); + delete _cachedFrame; + _cachedFrameNeedsDeletion = false; + } + _cachedFrame = tranposedFrame; + _cachedFrameNeedsDeletion = true; + } else { + // Cleanup + tranposedFrame->free(); + delete tranposedFrame; + } + } else { + renderManager->copyRectToWorkingWindow((const uint16 *)frame->getBasePtr(frame->w - subRect.width(), frame->h - subRect.height()), subRect.left, subRect.top, frame->w, subRect.width(), subRect.height()); + + // If the background can move, we need to cache the last frame so it can be rendered during background movement + if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { + if (_cachedFrameNeedsDeletion) { + _cachedFrame->free(); + delete _cachedFrame; + _cachedFrameNeedsDeletion = false; + } + _cachedFrame->copyFrom(*frame); + } + } + } else { + // If the background can move, we have to keep rendering animation frames, otherwise the animation flickers during background movement + RenderManager *renderManager = _engine->getRenderManager(); + RenderTable::RenderState state = renderManager->getRenderTable()->getRenderState(); + + if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { + Common::Point workingWindowPoint = renderManager->imageSpaceToWorkingWindowSpace(Common::Point(_x, _y)); + Common::Rect subRect(workingWindowPoint.x, workingWindowPoint.y, workingWindowPoint.x + _cachedFrame->w, workingWindowPoint.y + _cachedFrame->h); + + // If the clip returns false, it means the animation is outside the working window + if (!renderManager->clipRectToWorkingWindow(subRect)) { + return false; + } + + renderManager->copyRectToWorkingWindow((uint16 *)_cachedFrame->getBasePtr(_cachedFrame->w - subRect.width(), _cachedFrame->h - subRect.height()), subRect.left, subRect.top, _cachedFrame->w, subRect.width(), subRect.height()); + } + } + } + + // Check if we should continue looping + if (_animation.avi->endOfVideo()) { + _animation.avi->rewind(); + if (_loopCount > 0) { + _currentLoop++; + if (_currentLoop >= _loopCount) { + _animation.avi->stop(); + finished = true; + } + } + } + } + + // If we're done, set _animation key = 2 (Why 2? I don't know. It's just the value that they used) + // Then disable the control. DON'T delete it. It can be re-used + if (finished) { + _engine->getScriptManager()->setStateValue(_animationKey, 2); + disable(); + _currentLoop = 0; + } + + return false; +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/animation_control.h b/engines/zvision/scripting/controls/animation_control.h new file mode 100644 index 0000000000..2ac3691483 --- /dev/null +++ b/engines/zvision/scripting/controls/animation_control.h @@ -0,0 +1,87 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ZVISION_ANIMATION_CONTROL_H +#define ZVISION_ANIMATION_CONTROL_H + +#include "zvision/control.h" + + +namespace Common { +class String; +} + +namespace Video { +class VideoDecoder; +} + +namespace Graphics { +struct Surface; +} + +namespace ZVision { + +class ZVision; +class RlfAnimation; + +class AnimationControl : public Control { +public: + AnimationControl(ZVision *engine, uint32 controlKey, const Common::String &fileName); + ~AnimationControl(); + +private: + enum FileType { + RLF = 1, + AVI = 2 + }; + +private: + uint32 _animationKey; + + union { + RlfAnimation *rlf; + Video::VideoDecoder *avi; + } _animation; + + FileType _fileType; + uint _loopCount; + int32 _x; + int32 _y; + + uint _accumulatedTime; + uint _currentLoop; + + Graphics::Surface *_cachedFrame; + bool _cachedFrameNeedsDeletion; + +public: + bool process(uint32 deltaTimeInMillis); + + void setAnimationKey(uint32 animationKey) { _animationKey = animationKey; } + void setLoopCount(uint loopCount) { _loopCount = loopCount; } + void setXPos(int32 x) { _x = x; } + void setYPost(int32 y) { _y = y; } +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/scripting/controls/input_control.cpp b/engines/zvision/scripting/controls/input_control.cpp new file mode 100644 index 0000000000..a445e1aae5 --- /dev/null +++ b/engines/zvision/scripting/controls/input_control.cpp @@ -0,0 +1,142 @@ +/* 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 "zvision/input_control.h" + +#include "zvision/zvision.h" +#include "zvision/script_manager.h" +#include "zvision/string_manager.h" +#include "zvision/render_manager.h" +#include "zvision/utility.h" + +#include "common/str.h" +#include "common/stream.h" +#include "common/rect.h" + + +namespace ZVision { + +InputControl::InputControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) + : Control(engine, key), + _nextTabstop(0), + _focused(false), + _textChanged(false), + _cursorOffset(0) { + // Loop until we find the closing brace + Common::String line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + + while (!stream.eos() && !line.contains('}')) { + if (line.matchString("*rectangle*", true)) { + int x1; + int y1; + int x2; + int y2; + + sscanf(line.c_str(), "%*[^(](%d %d %d %d)", &x1, &y1, &x2, &y2); + + _textRectangle = Common::Rect(x1, y1, x2, y2); + } else if (line.matchString("*aux_hotspot*", true)) { + int x1; + int y1; + int x2; + int y2; + + sscanf(line.c_str(), "%*[^(](%d %d %d %d)", &x1, &y1, &x2, &y2); + + _headerRectangle = Common::Rect(x1, y1, x2, y2); + } else if (line.matchString("*string_init*", true)) { + uint fontFormatNumber; + + sscanf(line.c_str(), "%*[^(](%u)", &fontFormatNumber); + + _textStyle = _engine->getStringManager()->getTextStyle(fontFormatNumber); + } else if (line.matchString("*next_tabstop*", true)) { + sscanf(line.c_str(), "%*[^(](%u)", &_nextTabstop); + } else if (line.matchString("*cursor_animation*", true)) { + char fileName[25]; + + sscanf(line.c_str(), "%*[^(](%25s %*u)", fileName); + + _cursorAnimationFileName = Common::String(fileName); + } else if (line.matchString("*cursor_dimensions*", true)) { + // Ignore, use the dimensions in the animation file + } else if (line.matchString("*cursor_animation_frames*", true)) { + // Ignore, use the frame count in the animation file + } else if (line.matchString("*focus*", true)) { + _focused = true; + } + + line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + } +} + +void InputControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + _engine->getScriptManager()->focusControl(_key); +} + +void InputControl::onKeyDown(Common::KeyState keyState) { + if (!_focused) { + return; + } + + if (keyState.keycode == Common::KEYCODE_BACKSPACE) { + _currentInputText.deleteLastChar(); + } else if (keyState.keycode == Common::KEYCODE_TAB) { + _focused = false; + // Focus the next input control + _engine->getScriptManager()->focusControl(_nextTabstop); + } else { + // Otherwise, append the new character to the end of the current text + + uint16 asciiValue = keyState.ascii; + // We only care about text values + if (asciiValue >= 32 && asciiValue <= 126) { + _currentInputText += (char)asciiValue; + _textChanged = true; + } + } +} + +bool InputControl::process(uint32 deltaTimeInMillis) { + if (!_focused) { + return false; + } + + // First see if we need to render the text + if (_textChanged) { + // Blit the text using the RenderManager + Common::Rect destRect = _engine->getRenderManager()->renderTextToWorkingWindow(_key, _currentInputText, _textStyle.font, _textRectangle.left, _textRectangle.top, _textStyle.color, _textRectangle.width()); + + _cursorOffset = destRect.left - _textRectangle.left; + } + + // Render the next frame of the animation + // TODO: Implement animation handling + + return false; +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/input_control.h b/engines/zvision/scripting/controls/input_control.h new file mode 100644 index 0000000000..aab2c991dc --- /dev/null +++ b/engines/zvision/scripting/controls/input_control.h @@ -0,0 +1,60 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ZVISION_INPUT_CONTROL_H +#define ZVISION_INPUT_CONTROL_H + +#include "zvision/control.h" +#include "zvision/string_manager.h" + +#include "common/rect.h" + + +namespace ZVision { + +class InputControl : public Control { +public: + InputControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); + +private: + Common::Rect _textRectangle; + Common::Rect _headerRectangle; + StringManager::TextStyle _textStyle; + uint32 _nextTabstop; + Common::String _cursorAnimationFileName; + bool _focused; + + Common::String _currentInputText; + bool _textChanged; + uint _cursorOffset; + +public: + void focus() { _focused = true; } + void unfocus() { _focused = false; } + void onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + void onKeyDown(Common::KeyState keyState); + bool process(uint32 deltaTimeInMillis); +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/scripting/controls/lever_control.cpp b/engines/zvision/scripting/controls/lever_control.cpp new file mode 100644 index 0000000000..557dec0a83 --- /dev/null +++ b/engines/zvision/scripting/controls/lever_control.cpp @@ -0,0 +1,402 @@ +/* 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 "zvision/lever_control.h" + +#include "zvision/zvision.h" +#include "zvision/script_manager.h" +#include "zvision/render_manager.h" +#include "zvision/cursor_manager.h" +#include "zvision/rlf_animation.h" +#include "zvision/zork_avi_decoder.h" +#include "zvision/utility.h" + +#include "common/stream.h" +#include "common/file.h" +#include "common/tokenizer.h" +#include "common/system.h" + +#include "graphics/surface.h" + + +namespace ZVision { + +LeverControl::LeverControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) + : Control(engine, key), + _frameInfo(0), + _frameCount(0), + _startFrame(0), + _currentFrame(0), + _lastRenderedFrame(0), + _mouseIsCaptured(false), + _isReturning(false), + _accumulatedTime(0), + _returnRoutesCurrentFrame(0) { + + // Loop until we find the closing brace + Common::String line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + + while (!stream.eos() && !line.contains('}')) { + if (line.matchString("*descfile*", true)) { + char levFileName[25]; + sscanf(line.c_str(), "%*[^(](%25[^)])", levFileName); + + parseLevFile(levFileName); + } else if (line.matchString("*cursor*", true)) { + char cursorName[25]; + sscanf(line.c_str(), "%*[^(](%25[^)])", cursorName); + + _cursorName = Common::String(cursorName); + } + + line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + } + + renderFrame(_currentFrame); +} + +LeverControl::~LeverControl() { + if (_fileType == AVI) { + delete _animation.avi; + } else if (_fileType == RLF) { + delete _animation.rlf; + } + + delete[] _frameInfo; +} + +void LeverControl::parseLevFile(const Common::String &fileName) { + Common::File file; + if (!file.open(fileName)) { + warning("LEV file %s could could be opened", fileName.c_str()); + return; + } + + Common::String line = file.readLine(); + + while (!file.eos()) { + if (line.matchString("*animation_id*", true)) { + // Not used + } else if (line.matchString("*filename*", true)) { + char fileNameBuffer[25]; + sscanf(line.c_str(), "%*[^:]:%25[^~]~", fileNameBuffer); + + Common::String animationFileName(fileNameBuffer); + + if (animationFileName.hasSuffix(".avi")) { + _animation.avi = new ZorkAVIDecoder(); + _animation.avi->loadFile(animationFileName); + _fileType = AVI; + } else if (animationFileName.hasSuffix(".rlf")) { + _animation.rlf = new RlfAnimation(animationFileName, false); + _fileType = RLF; + } + } else if (line.matchString("*skipcolor*", true)) { + // Not used + } else if (line.matchString("*anim_coords*", true)) { + int left, top, right, bottom; + sscanf(line.c_str(), "%*[^:]:%d %d %d %d~", &left, &top, &right, &bottom); + + _animationCoords.left = left; + _animationCoords.top = top; + _animationCoords.right = right; + _animationCoords.bottom = bottom; + } else if (line.matchString("*mirrored*", true)) { + uint mirrored; + sscanf(line.c_str(), "%*[^:]:%u~", &mirrored); + + _mirrored = mirrored == 0 ? false : true; + } else if (line.matchString("*frames*", true)) { + sscanf(line.c_str(), "%*[^:]:%u~", &_frameCount); + + _frameInfo = new FrameInfo[_frameCount]; + } else if (line.matchString("*elsewhere*", true)) { + // Not used + } else if (line.matchString("*out_of_control*", true)) { + // Not used + } else if (line.matchString("*start_pos*", true)) { + sscanf(line.c_str(), "%*[^:]:%u~", &_startFrame); + _currentFrame = _startFrame; + } else if (line.matchString("*hotspot_deltas*", true)) { + uint x; + uint y; + sscanf(line.c_str(), "%*[^:]:%u %u~", &x, &y); + + _hotspotDelta.x = x; + _hotspotDelta.y = y; + } else { + uint frameNumber; + uint x, y; + + if (sscanf(line.c_str(), "%u:%u %u", &frameNumber, &x, &y) == 3) { + _frameInfo[frameNumber].hotspot.left = x; + _frameInfo[frameNumber].hotspot.top = y; + _frameInfo[frameNumber].hotspot.right = x + _hotspotDelta.x; + _frameInfo[frameNumber].hotspot.bottom = y + _hotspotDelta.y; + } + + Common::StringTokenizer tokenizer(line, " ^=()"); + tokenizer.nextToken(); + tokenizer.nextToken(); + + Common::String token = tokenizer.nextToken(); + while (!tokenizer.empty()) { + if (token == "D") { + token = tokenizer.nextToken(); + + uint angle; + uint toFrame; + sscanf(token.c_str(), "%u,%u", &toFrame, &angle); + + _frameInfo[frameNumber].directions.push_back(Direction(angle, toFrame)); + } else if (token.hasPrefix("P")) { + // Format: P( to ) + tokenizer.nextToken(); + tokenizer.nextToken(); + token = tokenizer.nextToken(); + uint to = atoi(token.c_str()); + + _frameInfo[frameNumber].returnRoute.push_back(to); + } + + token = tokenizer.nextToken(); + } + } + + line = file.readLine(); + } +} + +void LeverControl::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (!_enabled) { + return; + } + + if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) { + _mouseIsCaptured = true; + _lastMousePos = backgroundImageSpacePos; + } +} + +void LeverControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (!_enabled) { + return; + } + + if (_mouseIsCaptured) { + _mouseIsCaptured = false; + _engine->getScriptManager()->setStateValue(_key, _currentFrame); + + _isReturning = true; + _returnRoutesCurrentProgress = _frameInfo[_currentFrame].returnRoute.begin(); + _returnRoutesCurrentFrame = _currentFrame; + } +} + +bool LeverControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (!_enabled) { + return false; + } + + bool cursorWasChanged = false; + + if (_mouseIsCaptured) { + // Make sure the square distance between the last point and the current point is greater than 64 + // This is a heuristic. This determines how responsive the lever is to mouse movement. + // TODO: Fiddle with the heuristic to get a good lever responsiveness 'feel' + if (_lastMousePos.sqrDist(backgroundImageSpacePos) >= 64) { + int angle = calculateVectorAngle(_lastMousePos, backgroundImageSpacePos); + _lastMousePos = backgroundImageSpacePos; + + for (Common::List::iterator iter = _frameInfo[_currentFrame].directions.begin(); iter != _frameInfo[_currentFrame].directions.end(); ++iter) { + if (angle >= (int)iter->angle - ANGLE_DELTA && angle <= (int)iter->angle + ANGLE_DELTA) { + _currentFrame = iter->toFrame; + renderFrame(_currentFrame); + break; + } + } + } + } else if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) { + _engine->getCursorManager()->changeCursor(_cursorName); + cursorWasChanged = true; + } + + return cursorWasChanged; +} + +bool LeverControl::process(uint32 deltaTimeInMillis) { + if (!_enabled) { + return false; + } + + if (_isReturning) { + _accumulatedTime += deltaTimeInMillis; + while (_accumulatedTime >= ANIMATION_FRAME_TIME) { + _accumulatedTime -= ANIMATION_FRAME_TIME; + if (_returnRoutesCurrentFrame == *_returnRoutesCurrentProgress) { + _returnRoutesCurrentProgress++; + } + if (_returnRoutesCurrentProgress == _frameInfo[_currentFrame].returnRoute.end()) { + _isReturning = false; + _currentFrame = _returnRoutesCurrentFrame; + return false; + } + + uint toFrame = *_returnRoutesCurrentProgress; + if (_returnRoutesCurrentFrame < toFrame) { + _returnRoutesCurrentFrame++; + } else if (_returnRoutesCurrentFrame > toFrame) { + _returnRoutesCurrentFrame--; + } + + _engine->getScriptManager()->setStateValue(_key, _returnRoutesCurrentFrame); + renderFrame(_returnRoutesCurrentFrame); + } + } + + return false; +} + +int LeverControl::calculateVectorAngle(const Common::Point &pointOne, const Common::Point &pointTwo) { + // Check for the easy angles first + if (pointOne.x == pointTwo.x && pointOne.y == pointTwo.y) + return -1; // This should never happen + else if (pointOne.x == pointTwo.x) { + if (pointTwo.y < pointOne.y) + return 90; + else + return 270; + } else if (pointOne.y == pointTwo.y) { + if (pointTwo.x > pointOne.x) + return 0; + else + return 180; + } else { + // Calculate the angle with trig + int16 xDist = pointTwo.x - pointOne.x; + int16 yDist = pointTwo.y - pointOne.y; + + // Calculate the angle using arctan + // Then convert to degrees. (180 / 3.14159 = 57.2958) + int angle = int(atan((float)yDist / (float)xDist) * 57); + + // Calculate what quadrant pointTwo is in + uint quadrant = ((yDist > 0 ? 1 : 0) << 1) | (xDist < 0 ? 1 : 0); + + // Explanation of quadrants: + // + // yDist > 0 | xDist < 0 | Quadrant number + // 0 | 0 | 0 + // 0 | 1 | 1 + // 1 | 0 | 2 + // 1 | 1 | 3 + // + // Note: I know this doesn't line up with traditional mathematical quadrants + // but doing it this way allows you can use a switch and is a bit cleaner IMO. + // + // The graph below shows the 4 quadrants pointTwo can end up in as well + // as what the angle as calculated above refers to. + // Note: The calculated angle in quadrants 0 and 3 is negative + // due to arctan(-x) = -theta + // + // Origin => (pointOne.x, pointOne.y) + // * => (pointTwo.x, pointTwo.y) + // + // 90 | + // ^ | + // * | * | + // \ | / | + // \ | / | + // \ | / | + // Quadrant 1 \ | / Quadrant 0 | + // \ | / | + // \ | / | + // angle ( \|/ ) -angle | + // 180 <----------------------------------------> 0 | + // -angle ( /|\ ) angle | + // / | \ | + // / | \ | + // Quadrant 3 / | \ Quadrant 2 | + // / | \ | + // / | \ | + // / | \ | + // * | * | + // ^ | + // 270 | + + // Convert the local angles to unit circle angles + switch (quadrant) { + case 0: + angle = 180 + angle; + break; + case 1: + // Do nothing + break; + case 2: + angle = 180 + angle; + break; + case 3: + angle = 360 + angle; + break; + } + + return angle; + } +} + +void LeverControl::renderFrame(uint frameNumber) { + if (frameNumber == 0) { + _lastRenderedFrame = frameNumber; + } else if (frameNumber < _lastRenderedFrame && _mirrored) { + _lastRenderedFrame = frameNumber; + frameNumber = (_frameCount * 2) - frameNumber - 1; + } else { + _lastRenderedFrame = frameNumber; + } + + const uint16 *frameData = 0; + int x = _animationCoords.left; + int y = _animationCoords.top; + int width = 0; + int height = 0; + + if (_fileType == RLF) { + // getFrameData() will automatically optimize to getNextFrame() / getPreviousFrame() if it can + frameData = (const uint16 *)_animation.rlf->getFrameData(frameNumber)->getPixels(); + width = _animation.rlf->width(); // Use the animation width instead of _animationCoords.width() + height = _animation.rlf->height(); // Use the animation height instead of _animationCoords.height() + } else if (_fileType == AVI) { + _animation.avi->seekToFrame(frameNumber); + const Graphics::Surface *surface = _animation.avi->decodeNextFrame(); + frameData = (const uint16 *)surface->getPixels(); + width = surface->w; + height = surface->h; + } + + _engine->getRenderManager()->copyRectToWorkingWindow(frameData, x, y, width, width, height); +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/lever_control.h b/engines/zvision/scripting/controls/lever_control.h new file mode 100644 index 0000000000..8ef8f06fec --- /dev/null +++ b/engines/zvision/scripting/controls/lever_control.h @@ -0,0 +1,127 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ZVISION_LEVER_CONTROL_H +#define ZVISION_LEVER_CONTROL_H + +#include "zvision/control.h" + +#include "common/list.h" +#include "common/rect.h" + + +namespace ZVision { + +class ZorkAVIDecoder; +class RlfAnimation; + +class LeverControl : public Control { +public: + LeverControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); + ~LeverControl(); + +private: + enum FileType { + RLF = 1, + AVI = 2 + }; + + struct Direction { + Direction(uint a, uint t) : angle(a), toFrame(t) {} + + uint angle; + uint toFrame; + }; + + struct FrameInfo { + Common::Rect hotspot; + Common::List directions; + Common::List returnRoute; + }; + + enum { + ANGLE_DELTA = 30, // How far off a mouse angle can be and still be considered valid. This is in both directions, so the total buffer zone is (2 * ANGLE_DELTA) + ANIMATION_FRAME_TIME = 30 // In millis + }; + +private: + union { + RlfAnimation *rlf; + ZorkAVIDecoder *avi; + } _animation; + FileType _fileType; + + Common::String _cursorName; + Common::Rect _animationCoords; + bool _mirrored; + uint _frameCount; + uint _startFrame; + Common::Point _hotspotDelta; + FrameInfo *_frameInfo; + + uint _currentFrame; + uint _lastRenderedFrame; + bool _mouseIsCaptured; + bool _isReturning; + Common::Point _lastMousePos; + Common::List::iterator _returnRoutesCurrentProgress; + uint _returnRoutesCurrentFrame; + uint32 _accumulatedTime; + +public: + void onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + void onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + bool process(uint32 deltaTimeInMillis); + +private: + void parseLevFile(const Common::String &fileName); + /** + * Calculates the angle a vector makes with the negative y-axis + * + * 90 + * pointTwo * ^ + * \ | + * \ | + * \ | + * \ | + * angle ( \|pointOne + * 180 <-----------*-----------> 0 + * | + * | + * | + * | + * | + * ^ + * 270 + * + * @param pointOne The origin of the vector + * @param pointTwo The end of the vector + * @return The angle the vector makes with the negative y-axis + */ + static int calculateVectorAngle(const Common::Point &pointOne, const Common::Point &pointTwo); + void renderFrame(uint frameNumber); +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/scripting/controls/push_toggle_control.cpp b/engines/zvision/scripting/controls/push_toggle_control.cpp new file mode 100644 index 0000000000..689311ba5b --- /dev/null +++ b/engines/zvision/scripting/controls/push_toggle_control.cpp @@ -0,0 +1,98 @@ +/* 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 "zvision/push_toggle_control.h" + +#include "zvision/zvision.h" +#include "zvision/script_manager.h" +#include "zvision/cursor_manager.h" +#include "zvision/utility.h" + +#include "common/stream.h" + + +namespace ZVision { + +PushToggleControl::PushToggleControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) + : Control(engine, key) { + // Loop until we find the closing brace + Common::String line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + + while (!stream.eos() && !line.contains('}')) { + if (line.matchString("*_hotspot*", true)) { + uint x; + uint y; + uint width; + uint height; + + sscanf(line.c_str(), "%*[^(](%u,%u,%u,%u)", &x, &y, &width, &height); + + _hotspot = Common::Rect(x, y, x + width, y + height); + } else if (line.matchString("cursor*", true)) { + char nameBuffer[25]; + + sscanf(line.c_str(), "%*[^(](%25[^)])", nameBuffer); + + _hoverCursor = Common::String(nameBuffer); + } + + line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + } + + if (_hotspot.isEmpty() || _hoverCursor.empty()) { + warning("Push_toggle cursor %u was parsed incorrectly", key); + } +} + +PushToggleControl::~PushToggleControl() { + // Clear the state value back to 0 + _engine->getScriptManager()->setStateValue(_key, 0); +} + +void PushToggleControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (!_enabled) { + return; + } + + if (_hotspot.contains(backgroundImageSpacePos)) { + _engine->getScriptManager()->setStateValue(_key, 1); + } +} + +bool PushToggleControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (!_enabled) { + return false; + } + + if (_hotspot.contains(backgroundImageSpacePos)) { + _engine->getCursorManager()->changeCursor(_hoverCursor); + return true; + } + + return false; +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/push_toggle_control.h b/engines/zvision/scripting/controls/push_toggle_control.h new file mode 100644 index 0000000000..2a407cada9 --- /dev/null +++ b/engines/zvision/scripting/controls/push_toggle_control.h @@ -0,0 +1,67 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ZVISION_PUSH_TOGGLE_CONTROL_H +#define ZVISION_PUSH_TOGGLE_CONTROL_H + +#include "zvision/control.h" + +#include "common/rect.h" + + +namespace ZVision { + +class PushToggleControl : public Control { +public: + PushToggleControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); + ~PushToggleControl(); + + /** + * Called when LeftMouse is lifted. Calls ScriptManager::setStateValue(_key, 1); + * + * @param screenSpacePos The position of the mouse in screen space + * @param backgroundImageSpacePos The position of the mouse in background image space + */ + void onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + /** + * Called on every MouseMove. Tests if the mouse is inside _hotspot, and if so, sets the cursor. + * + * @param engine The base engine + * @param screenSpacePos The position of the mouse in screen space + * @param backgroundImageSpacePos The position of the mouse in background image space + * @return Was the cursor changed? + */ + bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + +private: + /** + * The area that will trigger the event + * This is in image space coordinates, NOT screen space + */ + Common::Rect _hotspot; + /** The cursor to use when hovering over _hotspot */ + Common::String _hoverCursor; +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/scripting/controls/timer_node.cpp b/engines/zvision/scripting/controls/timer_node.cpp new file mode 100644 index 0000000000..b529a661cf --- /dev/null +++ b/engines/zvision/scripting/controls/timer_node.cpp @@ -0,0 +1,73 @@ +/* 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 "zvision/timer_node.h" + +#include "zvision/zvision.h" +#include "zvision/script_manager.h" + +#include "common/stream.h" + + +namespace ZVision { + +TimerNode::TimerNode(ZVision *engine, uint32 key, uint timeInSeconds) + : Control(engine, key) { + if (_engine->getGameId() == GID_NEMESIS) { + _timeLeft = timeInSeconds * 1000; + } else if (_engine->getGameId() == GID_GRANDINQUISITOR) { + _timeLeft = timeInSeconds * 100; + } + + _engine->getScriptManager()->setStateValue(_key, 1); +} + +TimerNode::~TimerNode() { + if (_timeLeft <= 0) + _engine->getScriptManager()->setStateValue(_key, 2); + else + _engine->getScriptManager()->setStateValue(_key, _timeLeft); // If timer was stopped by stop or kill +} + +bool TimerNode::process(uint32 deltaTimeInMillis) { + _timeLeft -= deltaTimeInMillis; + + if (_timeLeft <= 0) { + // Let the destructor reset the state value + return true; + } + + return false; +} + +void TimerNode::serialize(Common::WriteStream *stream) { + stream->writeUint32LE(_key); + stream->writeUint32LE(_timeLeft); +} + +void TimerNode::deserialize(Common::SeekableReadStream *stream) { + _timeLeft = stream->readUint32LE(); +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/timer_node.h b/engines/zvision/scripting/controls/timer_node.h new file mode 100644 index 0000000000..a95373353b --- /dev/null +++ b/engines/zvision/scripting/controls/timer_node.h @@ -0,0 +1,55 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ZVISION_TIMER_NODE_H +#define ZVISION_TIMER_NODE_H + +#include "zvision/control.h" + +namespace ZVision { + +class ZVision; + +class TimerNode : public Control { +public: + TimerNode(ZVision *engine, uint32 key, uint timeInSeconds); + ~TimerNode(); + + /** + * Decrement the timer by the delta time. If the timer is finished, set the status + * in _globalState and let this node be deleted + * + * @param deltaTimeInMillis The number of milliseconds that have passed since last frame + * @return If true, the node can be deleted after process() finishes + */ + bool process(uint32 deltaTimeInMillis); + void serialize(Common::WriteStream *stream); + void deserialize(Common::SeekableReadStream *stream); + inline bool needsSerialization() { return true; } + +private: + int32 _timeLeft; +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/scripting/puzzle.h b/engines/zvision/scripting/puzzle.h new file mode 100644 index 0000000000..d468da1886 --- /dev/null +++ b/engines/zvision/scripting/puzzle.h @@ -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 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ZVISION_PUZZLE_H +#define ZVISION_PUZZLE_H + +#include "zvision/actions.h" + +#include "common/list.h" +#include "common/ptr.h" + + +namespace ZVision { + +struct Puzzle { + Puzzle() : key(0) {} + + ~Puzzle() { + for (Common::List::iterator iter = resultActions.begin(); iter != resultActions.end(); ++iter) { + delete *iter; + } + } + + /** How criteria should be decided */ + enum CriteriaOperator { + EQUAL_TO, + NOT_EQUAL_TO, + GREATER_THAN, + LESS_THAN + }; + + /** Criteria for a Puzzle result to be fired */ + struct CriteriaEntry { + /** The key of a global state */ + uint32 key; + /** + * What we're comparing the value of the global state against + * This can either be a pure value or it can be the key of another global state + */ + uint32 argument; + /** How to do the comparison */ + CriteriaOperator criteriaOperator; + /** Whether 'argument' is the key of a global state (true) or a pure value (false) */ + bool argumentIsAKey; + }; + + uint32 key; + Common::List > criteriaList; + // This has to be list of pointers because ResultAction is abstract + Common::List resultActions; +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/scripting/scr_file_handling.cpp b/engines/zvision/scripting/scr_file_handling.cpp new file mode 100644 index 0000000000..0c952ae0ce --- /dev/null +++ b/engines/zvision/scripting/scr_file_handling.cpp @@ -0,0 +1,302 @@ +/* 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 "zvision/script_manager.h" + +#include "zvision/utility.h" +#include "zvision/puzzle.h" +#include "zvision/actions.h" +#include "zvision/push_toggle_control.h" +#include "zvision/lever_control.h" + +#include "common/textconsole.h" +#include "common/file.h" +#include "common/tokenizer.h" + + +namespace ZVision { + +void ScriptManager::parseScrFile(const Common::String &fileName, bool isGlobal) { + Common::File file; + if (!file.open(fileName)) { + warning("Script file not found: %s", fileName.c_str()); + return; + } + + while(!file.eos()) { + Common::String line = file.readLine(); + if (file.err()) { + warning("Error parsing scr file: %s", fileName.c_str()); + return; + } + + trimCommentsAndWhiteSpace(&line); + if (line.empty()) + continue; + + if (line.matchString("puzzle:*", true)) { + Puzzle *puzzle = new Puzzle(); + sscanf(line.c_str(),"puzzle:%u",&(puzzle->key)); + + parsePuzzle(puzzle, file); + if (isGlobal) { + _globalPuzzles.push_back(puzzle); + } else { + _activePuzzles.push_back(puzzle); + } + } else if (line.matchString("control:*", true)) { + parseControl(line, file); + } + } +} + +void ScriptManager::parsePuzzle(Puzzle *puzzle, Common::SeekableReadStream &stream) { + Common::String line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + + while (!stream.eos() && !line.contains('}')) { + if (line.matchString("criteria {", true)) { + parseCriteria(stream, puzzle->criteriaList); + } else if (line.matchString("results {", true)) { + parseResults(stream, puzzle->resultActions); + } else if (line.matchString("flags {", true)) { + setStateFlags(puzzle->key, parseFlags(stream)); + } + + line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + } +} + +bool ScriptManager::parseCriteria(Common::SeekableReadStream &stream, Common::List > &criteriaList) const { + // Loop until we find the closing brace + Common::String line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + + // Criteria can be empty + if (line.contains('}')) { + return false; + } + + // Create a new List to hold the CriteriaEntries + criteriaList.push_back(Common::List()); + + while (!stream.eos() && !line.contains('}')) { + Puzzle::CriteriaEntry entry; + + // Split the string into tokens using ' ' as a delimiter + Common::StringTokenizer tokenizer(line); + Common::String token; + + // Parse the id out of the first token + token = tokenizer.nextToken(); + sscanf(token.c_str(), "[%u]", &(entry.key)); + + // Parse the operator out of the second token + token = tokenizer.nextToken(); + if (token.c_str()[0] == '=') + entry.criteriaOperator = Puzzle::EQUAL_TO; + else if (token.c_str()[0] == '!') + entry.criteriaOperator = Puzzle::NOT_EQUAL_TO; + else if (token.c_str()[0] == '>') + entry.criteriaOperator = Puzzle::GREATER_THAN; + else if (token.c_str()[0] == '<') + entry.criteriaOperator = Puzzle::LESS_THAN; + + // First determine if the last token is an id or a value + // Then parse it into 'argument' + token = tokenizer.nextToken(); + if (token.contains('[')) { + sscanf(token.c_str(), "[%u]", &(entry.argument)); + entry.argumentIsAKey = true; + } else { + sscanf(token.c_str(), "%u", &(entry.argument)); + entry.argumentIsAKey = false; + } + + criteriaList.back().push_back(entry); + + line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + } + + return true; +} + +void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::List &actionList) const { + // Loop until we find the closing brace + Common::String line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + + // TODO: Re-order the if-then statements in order of highest occurrence + while (!stream.eos() && !line.contains('}')) { + if (line.empty()) { + line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + + continue; + } + + // Parse for the action type + if (line.matchString("*:add*", true)) { + actionList.push_back(new ActionAdd(line)); + } else if (line.matchString("*:animplay*", true)) { + actionList.push_back(new ActionPlayAnimation(line)); + } else if (line.matchString("*:animpreload*", true)) { + actionList.push_back(new ActionPreloadAnimation(line)); + } else if (line.matchString("*:animunload*", true)) { + //actionList.push_back(new ActionUnloadAnimation(line)); + } else if (line.matchString("*:attenuate*", true)) { + // TODO: Implement ActionAttenuate + } else if (line.matchString("*:assign*", true)) { + actionList.push_back(new ActionAssign(line)); + } else if (line.matchString("*:change_location*", true)) { + actionList.push_back(new ActionChangeLocation(line)); + } else if (line.matchString("*:crossfade*", true)) { + // TODO: Implement ActionCrossfade + } else if (line.matchString("*:debug*", true)) { + // TODO: Implement ActionDebug + } else if (line.matchString("*:delay_render*", true)) { + // TODO: Implement ActionDelayRender + } else if (line.matchString("*:disable_control*", true)) { + actionList.push_back(new ActionDisableControl(line)); + } else if (line.matchString("*:disable_venus*", true)) { + // TODO: Implement ActionDisableVenus + } else if (line.matchString("*:display_message*", true)) { + // TODO: Implement ActionDisplayMessage + } else if (line.matchString("*:dissolve*", true)) { + // TODO: Implement ActionDissolve + } else if (line.matchString("*:distort*", true)) { + // TODO: Implement ActionDistort + } else if (line.matchString("*:enable_control*", true)) { + actionList.push_back(new ActionEnableControl(line)); + } else if (line.matchString("*:flush_mouse_events*", true)) { + // TODO: Implement ActionFlushMouseEvents + } else if (line.matchString("*:inventory*", true)) { + // TODO: Implement ActionInventory + } else if (line.matchString("*:kill*", true)) { + // TODO: Implement ActionKill + } else if (line.matchString("*:menu_bar_enable*", true)) { + // TODO: Implement ActionMenuBarEnable + } else if (line.matchString("*:music*", true)) { + actionList.push_back(new ActionMusic(line)); + } else if (line.matchString("*:pan_track*", true)) { + // TODO: Implement ActionPanTrack + } else if (line.matchString("*:playpreload*", true)) { + actionList.push_back(new ActionPlayPreloadAnimation(line)); + } else if (line.matchString("*:preferences*", true)) { + // TODO: Implement ActionPreferences + } else if (line.matchString("*:quit*", true)) { + actionList.push_back(new ActionQuit()); + } else if (line.matchString("*:random*", true)) { + actionList.push_back(new ActionRandom(line)); + } else if (line.matchString("*:region*", true)) { + // TODO: Implement ActionRegion + } else if (line.matchString("*:restore_game*", true)) { + // TODO: Implement ActionRestoreGame + } else if (line.matchString("*:rotate_to*", true)) { + // TODO: Implement ActionRotateTo + } else if (line.matchString("*:save_game*", true)) { + // TODO: Implement ActionSaveGame + } else if (line.matchString("*:set_partial_screen*", true)) { + actionList.push_back(new ActionSetPartialScreen(line)); + } else if (line.matchString("*:set_screen*", true)) { + actionList.push_back(new ActionSetScreen(line)); + } else if (line.matchString("*:set_venus*", true)) { + // TODO: Implement ActionSetVenus + } else if (line.matchString("*:stop*", true)) { + // TODO: Implement ActionStop + } else if (line.matchString("*:streamvideo*", true)) { + actionList.push_back(new ActionStreamVideo(line)); + } else if (line.matchString("*:syncsound*", true)) { + // TODO: Implement ActionSyncSound + } else if (line.matchString("*:timer*", true)) { + actionList.push_back(new ActionTimer(line)); + } else if (line.matchString("*:ttytext*", true)) { + // TODO: Implement ActionTTYText + } else if (line.matchString("*:universe_music*", true)) { + // TODO: Implement ActionUniverseMusic + } else if (line.matchString("*:copy_file*", true)) { + // Not used. Purposely left empty + } else { + warning("Unhandled result action type: %s", line.c_str()); + } + + line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + } + + return; +} + +uint ScriptManager::parseFlags(Common::SeekableReadStream &stream) const { + uint flags = 0; + + // Loop until we find the closing brace + Common::String line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + + while (!stream.eos() && !line.contains('}')) { + if (line.matchString("ONCE_PER_INST", true)) { + flags |= ONCE_PER_INST; + } else if (line.matchString("DO_ME_NOW", true)) { + flags |= DO_ME_NOW; + } else if (line.matchString("DISABLED", true)) { + flags |= DISABLED; + } + + line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + } + + return flags; +} + +void ScriptManager::parseControl(Common::String &line, Common::SeekableReadStream &stream) { + uint32 key; + char controlTypeBuffer[20]; + + sscanf(line.c_str(), "control:%u %s {", &key, controlTypeBuffer); + + Common::String controlType(controlTypeBuffer); + + if (controlType.equalsIgnoreCase("push_toggle")) { + _activeControls.push_back(new PushToggleControl(_engine, key, stream)); + return; + } else if (controlType.equalsIgnoreCase("flat")) { + Control::parseFlatControl(_engine); + return; + } else if (controlType.equalsIgnoreCase("pana")) { + Control::parsePanoramaControl(_engine, stream); + return; + } else if (controlType.equalsIgnoreCase("tilt")) { + Control::parseTiltControl(_engine, stream); + return; + } else if (controlType.equalsIgnoreCase("lever")) { + _activeControls.push_back(new LeverControl(_engine, key, stream)); + return; + } +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/script_manager.cpp b/engines/zvision/scripting/script_manager.cpp new file mode 100644 index 0000000000..0e08345f07 --- /dev/null +++ b/engines/zvision/scripting/script_manager.cpp @@ -0,0 +1,444 @@ +/* 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 "zvision/script_manager.h" + +#include "zvision/zvision.h" +#include "zvision/render_manager.h" +#include "zvision/cursor_manager.h" +#include "zvision/save_manager.h" +#include "zvision/actions.h" +#include "zvision/utility.h" + +#include "common/algorithm.h" +#include "common/hashmap.h" +#include "common/debug.h" +#include "common/stream.h" + + +namespace ZVision { + +ScriptManager::ScriptManager(ZVision *engine) + : _engine(engine), + _currentlyFocusedControl(0) { +} + +ScriptManager::~ScriptManager() { + for (PuzzleList::iterator iter = _activePuzzles.begin(); iter != _activePuzzles.end(); ++iter) { + delete (*iter); + } + for (PuzzleList::iterator iter = _globalPuzzles.begin(); iter != _globalPuzzles.end(); ++iter) { + delete (*iter); + } + for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { + delete (*iter); + } +} + +void ScriptManager::initialize() { + parseScrFile("universe.scr", true); + changeLocation('g', 'a', 'r', 'y', 0); +} + +void ScriptManager::update(uint deltaTimeMillis) { + updateNodes(deltaTimeMillis); + checkPuzzleCriteria(); +} + +void ScriptManager::createReferenceTable() { + // Iterate through each local Puzzle + for (PuzzleList::iterator activePuzzleIter = _activePuzzles.begin(); activePuzzleIter != _activePuzzles.end(); ++activePuzzleIter) { + Puzzle *puzzlePtr = (*activePuzzleIter); + + // Iterate through each CriteriaEntry and add a reference from the criteria key to the Puzzle + for (Common::List >::iterator criteriaIter = (*activePuzzleIter)->criteriaList.begin(); criteriaIter != (*activePuzzleIter)->criteriaList.end(); ++criteriaIter) { + for (Common::List::iterator entryIter = criteriaIter->begin(); entryIter != criteriaIter->end(); ++entryIter) { + _referenceTable[entryIter->key].push_back(puzzlePtr); + + // If the argument is a key, add a reference to it as well + if (entryIter->argumentIsAKey) { + _referenceTable[entryIter->argument].push_back(puzzlePtr); + } + } + } + } + + // Iterate through each global Puzzle + for (PuzzleList::iterator globalPuzzleIter = _globalPuzzles.begin(); globalPuzzleIter != _globalPuzzles.end(); ++globalPuzzleIter) { + Puzzle *puzzlePtr = (*globalPuzzleIter); + + // Iterate through each CriteriaEntry and add a reference from the criteria key to the Puzzle + for (Common::List >::iterator criteriaIter = (*globalPuzzleIter)->criteriaList.begin(); criteriaIter != (*globalPuzzleIter)->criteriaList.end(); ++criteriaIter) { + for (Common::List::iterator entryIter = criteriaIter->begin(); entryIter != criteriaIter->end(); ++entryIter) { + _referenceTable[entryIter->key].push_back(puzzlePtr); + + // If the argument is a key, add a reference to it as well + if (entryIter->argumentIsAKey) { + _referenceTable[entryIter->argument].push_back(puzzlePtr); + } + } + } + } + + // Remove duplicate entries + for (PuzzleMap::iterator referenceTableIter = _referenceTable.begin(); referenceTableIter != _referenceTable.end(); ++referenceTableIter) { + removeDuplicateEntries(referenceTableIter->_value); + } +} + +void ScriptManager::updateNodes(uint deltaTimeMillis) { + // If process() returns true, it means the node can be deleted + for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end();) { + if ((*iter)->process(deltaTimeMillis)) { + delete (*iter); + // Remove the node + iter = _activeControls.erase(iter); + } else { + ++iter; + } + } +} + +void ScriptManager::checkPuzzleCriteria() { + while (!_puzzlesToCheck.empty()) { + Puzzle *puzzle = _puzzlesToCheck.pop(); + + // Check if the puzzle is already finished + // Also check that the puzzle isn't disabled + if (getStateValue(puzzle->key) == 1 && (getStateFlags(puzzle->key) & DISABLED) == 0) { + continue; + } + + // Check each Criteria + + bool criteriaMet = false; + for (Common::List >::iterator criteriaIter = puzzle->criteriaList.begin(); criteriaIter != puzzle->criteriaList.end(); ++criteriaIter) { + criteriaMet = false; + + for (Common::List::iterator entryIter = criteriaIter->begin(); entryIter != criteriaIter->end(); ++entryIter) { + // Get the value to compare against + uint argumentValue; + if (entryIter->argumentIsAKey) + argumentValue = getStateValue(entryIter->argument); + else + argumentValue = entryIter->argument; + + // Do the comparison + switch (entryIter->criteriaOperator) { + case Puzzle::EQUAL_TO: + criteriaMet = getStateValue(entryIter->key) == argumentValue; + break; + case Puzzle::NOT_EQUAL_TO: + criteriaMet = getStateValue(entryIter->key) != argumentValue; + break; + case Puzzle::GREATER_THAN: + criteriaMet = getStateValue(entryIter->key) > argumentValue; + break; + case Puzzle::LESS_THAN: + criteriaMet = getStateValue(entryIter->key) < argumentValue; + break; + } + + // If one check returns false, don't keep checking + if (!criteriaMet) { + break; + } + } + + // If any of the Criteria are *fully* met, then execute the results + if (criteriaMet) { + break; + } + } + + // criteriaList can be empty. Aka, the puzzle should be executed immediately + if (puzzle->criteriaList.empty() || criteriaMet) { + debug(1, "Puzzle %u criteria passed. Executing its ResultActions", puzzle->key); + + // Set the puzzle as completed + setStateValue(puzzle->key, 1); + + bool shouldContinue = true; + for (Common::List::iterator resultIter = puzzle->resultActions.begin(); resultIter != puzzle->resultActions.end(); ++resultIter) { + shouldContinue = shouldContinue && (*resultIter)->execute(_engine); + if (!shouldContinue) { + break; + } + } + + if (!shouldContinue) { + break; + } + } + } +} + +void ScriptManager::cleanStateTable() { + for (StateMap::iterator iter = _globalState.begin(); iter != _globalState.end(); ++iter) { + // If the value is equal to zero, we can purge it since getStateValue() + // will return zero if _globalState doesn't contain a key + if (iter->_value == 0) { + // Remove the node + _globalState.erase(iter); + } + } +} + +uint ScriptManager::getStateValue(uint32 key) { + if (_globalState.contains(key)) + return _globalState[key]; + else + return 0; +} + +void ScriptManager::setStateValue(uint32 key, uint value) { + _globalState[key] = value; + + if (_referenceTable.contains(key)) { + for (Common::Array::iterator iter = _referenceTable[key].begin(); iter != _referenceTable[key].end(); ++iter) { + _puzzlesToCheck.push((*iter)); + } + } +} + +uint ScriptManager::getStateFlags(uint32 key) { + if (_globalStateFlags.contains(key)) + return _globalStateFlags[key]; + else + return 0; +} + +void ScriptManager::setStateFlags(uint32 key, uint flags) { + _globalStateFlags[key] = flags; + + if (_referenceTable.contains(key)) { + for (Common::Array::iterator iter = _referenceTable[key].begin(); iter != _referenceTable[key].end(); ++iter) { + _puzzlesToCheck.push((*iter)); + } + } +} + +void ScriptManager::addToStateValue(uint32 key, uint valueToAdd) { + _globalState[key] += valueToAdd; +} + +void ScriptManager::addControl(Control *control) { + _activeControls.push_back(control); +} + +Control *ScriptManager::getControl(uint32 key) { + for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { + if ((*iter)->getKey() == key) { + return (*iter); + } + } + + return nullptr; +} + +void ScriptManager::focusControl(uint32 key) { + for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { + uint32 controlKey = (*iter)->getKey(); + + if (controlKey == key) { + (*iter)->focus(); + } else if (controlKey == _currentlyFocusedControl) { + (*iter)->unfocus(); + } + } + + _currentlyFocusedControl = key; +} + +void ScriptManager::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { + (*iter)->onMouseDown(screenSpacePos, backgroundImageSpacePos); + } +} + +void ScriptManager::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { + (*iter)->onMouseUp(screenSpacePos, backgroundImageSpacePos); + } +} + +bool ScriptManager::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + bool cursorWasChanged = false; + for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { + cursorWasChanged = cursorWasChanged || (*iter)->onMouseMove(screenSpacePos, backgroundImageSpacePos); + } + + return cursorWasChanged; +} + +void ScriptManager::onKeyDown(Common::KeyState keyState) { + for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { + (*iter)->onKeyDown(keyState); + } +} + +void ScriptManager::onKeyUp(Common::KeyState keyState) { + for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { + (*iter)->onKeyUp(keyState); + } +} + +void ScriptManager::changeLocation(char world, char room, char node, char view, uint32 offset) { + assert(world != 0); + debug(1, "Changing location to: %c %c %c %c %u", world, room, node, view, offset); + + // Auto save + _engine->getSaveManager()->autoSave(); + + // Clear all the containers + _referenceTable.clear(); + _puzzlesToCheck.clear(); + for (PuzzleList::iterator iter = _activePuzzles.begin(); iter != _activePuzzles.end(); ++iter) { + delete (*iter); + } + _activePuzzles.clear(); + for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { + delete (*iter); + } + _activeControls.clear(); + + // Revert to the idle cursor + _engine->getCursorManager()->revertToIdle(); + + // Reset the background velocity + _engine->getRenderManager()->setBackgroundVelocity(0); + + // Remove any alphaEntries + _engine->getRenderManager()->clearAlphaEntries(); + + // Clean the global state table + cleanStateTable(); + + // Parse into puzzles and controls + Common::String fileName = Common::String::format("%c%c%c%c.scr", world, room, node, view); + parseScrFile(fileName); + + // Change the background position + _engine->getRenderManager()->setBackgroundPosition(offset); + + // Enable all the controls + for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { + (*iter)->enable(); + } + + // Add all the local puzzles to the queue to be checked + for (PuzzleList::iterator iter = _activePuzzles.begin(); iter != _activePuzzles.end(); ++iter) { + // Reset any Puzzles that have the flag ONCE_PER_INST + if ((getStateFlags((*iter)->key) & ONCE_PER_INST) == ONCE_PER_INST) { + setStateValue((*iter)->key, 0); + } + + _puzzlesToCheck.push((*iter)); + } + + // Add all the global puzzles to the queue to be checked + for (PuzzleList::iterator iter = _globalPuzzles.begin(); iter != _globalPuzzles.end(); ++iter) { + // Reset any Puzzles that have the flag ONCE_PER_INST + if ((getStateFlags((*iter)->key) & ONCE_PER_INST) == ONCE_PER_INST) { + setStateValue((*iter)->key, 0); + } + + _puzzlesToCheck.push((*iter)); + } + + // Create the puzzle reference table + createReferenceTable(); + + // Update _currentLocation + _currentLocation.world = world; + _currentLocation.room = room; + _currentLocation.node = node; + _currentLocation.view = view; + _currentLocation.offset = offset; +} + +void ScriptManager::serializeStateTable(Common::WriteStream *stream) { + // Write the number of state value entries + stream->writeUint32LE(_globalState.size()); + + for (StateMap::iterator iter = _globalState.begin(); iter != _globalState.end(); ++iter) { + // Write out the key/value pair + stream->writeUint32LE(iter->_key); + stream->writeUint32LE(iter->_value); + } +} + +void ScriptManager::deserializeStateTable(Common::SeekableReadStream *stream) { + // Clear out the current table values + _globalState.clear(); + + // Read the number of key/value pairs + uint32 numberOfPairs = stream->readUint32LE(); + + for (uint32 i = 0; i < numberOfPairs; ++i) { + uint32 key = stream->readUint32LE(); + uint32 value = stream->readUint32LE(); + // Directly access the state table so we don't trigger Puzzle checks + _globalState[key] = value; + } +} + +void ScriptManager::serializeControls(Common::WriteStream *stream) { + // Count how many controls need to save their data + // Because WriteStream isn't seekable + uint32 numberOfControlsNeedingSerialization = 0; + for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { + if ((*iter)->needsSerialization()) { + numberOfControlsNeedingSerialization++; + } + } + stream->writeUint32LE(numberOfControlsNeedingSerialization); + + for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { + (*iter)->serialize(stream); + } +} + +void ScriptManager::deserializeControls(Common::SeekableReadStream *stream) { + uint32 numberOfControls = stream->readUint32LE(); + + for (uint32 i = 0; i < numberOfControls; ++i) { + uint32 key = stream->readUint32LE(); + for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { + if ((*iter)->getKey() == key) { + (*iter)->deserialize(stream); + break; + } + } + } +} + +Location ScriptManager::getCurrentLocation() const { + Location location = _currentLocation; + location.offset = _engine->getRenderManager()->getCurrentBackgroundOffset(); + + return location; +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/script_manager.h b/engines/zvision/scripting/script_manager.h new file mode 100644 index 0000000000..a5079e3332 --- /dev/null +++ b/engines/zvision/scripting/script_manager.h @@ -0,0 +1,224 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ZVISION_SCRIPT_MANAGER_H +#define ZVISION_SCRIPT_MANAGER_H + +#include "zvision/puzzle.h" +#include "zvision/control.h" + +#include "common/hashmap.h" +#include "common/queue.h" + + +namespace Common { +class String; +class SeekableReadStream; +} + +namespace ZVision { + +class ZVision; + +struct Location { + Location() : world('g'), room('a'), node('r'), view('y'), offset(0) {} + + char world; + char room; + char node; + char view; + uint32 offset; +}; + +enum StateFlags { + ONCE_PER_INST = 0x01, + DO_ME_NOW = 0x02, // Somewhat useless flag since anything that needs to be done immediately has no criteria + DISABLED = 0x04 +}; + +typedef Common::HashMap > PuzzleMap; +typedef Common::List PuzzleList; +typedef Common::Queue PuzzleQueue; +typedef Common::List ControlList; +typedef Common::HashMap StateMap; +typedef Common::HashMap StateFlagMap; + +class ScriptManager { +public: + ScriptManager(ZVision *engine); + ~ScriptManager(); + +private: + ZVision *_engine; + /** + * Holds the global state variable. Do NOT directly modify this. Use the accessors and + * mutators getStateValue() and setStateValue(). This ensures that Puzzles that reference a + * particular state key are checked after the key is modified. + */ + StateMap _globalState; + /** + * Holds the flags for the global states. This is used to enable/disable puzzles and/or + * controls as well as which puzzles should are allowed to be re-executed + */ + StateFlagMap _globalStateFlags; + /** References _globalState keys to Puzzles */ + PuzzleMap _referenceTable; + /** Holds the Puzzles that should be checked this frame */ + PuzzleQueue _puzzlesToCheck; + /** Holds the currently active puzzles */ + PuzzleList _activePuzzles; + /** Holds the global puzzles */ + PuzzleList _globalPuzzles; + /** Holds the currently active controls */ + ControlList _activeControls; + + Location _currentLocation; + + uint32 _currentlyFocusedControl; + +public: + void initialize(); + void update(uint deltaTimeMillis); + + uint getStateValue(uint32 key); + void setStateValue(uint32 key, uint value); + void addToStateValue(uint32 key, uint valueToAdd); + + uint getStateFlags(uint32 key); + void setStateFlags(uint32 key, uint flags); + + void addControl(Control *control); + Control *getControl(uint32 key); + + void focusControl(uint32 key); + + /** + * Called when LeftMouse is pushed. + * + * @param screenSpacePos The position of the mouse in screen space + * @param backgroundImageSpacePos The position of the mouse in background image space + */ + void onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + /** + * Called when LeftMouse is lifted. + * + * @param screenSpacePos The position of the mouse in screen space + * @param backgroundImageSpacePos The position of the mouse in background image space + */ + void onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + /** + * Called on every MouseMove. + * + * @param screenSpacePos The position of the mouse in screen space + * @param backgroundImageSpacePos The position of the mouse in background image space + * @return Was the cursor changed? + */ + bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + /** + * Called when a key is pressed. + * + * @param keycode The key that was pressed + */ + void onKeyDown(Common::KeyState keyState); + /** + * Called when a key is released. + * + * @param keycode The key that was pressed + */ + void onKeyUp(Common::KeyState keyState); + + void changeLocation(char world, char room, char node, char view, uint32 offset); + + void serializeStateTable(Common::WriteStream *stream); + void deserializeStateTable(Common::SeekableReadStream *stream); + void serializeControls(Common::WriteStream *stream); + void deserializeControls(Common::SeekableReadStream *stream); + + Location getCurrentLocation() const; + +private: + void createReferenceTable(); + void updateNodes(uint deltaTimeMillis); + void checkPuzzleCriteria(); + void cleanStateTable(); + +// TODO: Make this private. It was only made public so Console::cmdParseAllScrFiles() could use it +public: + /** + * Parses a script file into triggers and events + * + * @param fileName Name of the .scr file + * @param isGlobal Are the puzzles included in the file global (true). AKA, the won't be purged during location changes + */ + void parseScrFile(const Common::String &fileName, bool isGlobal = false); + +private: + /** + * Parses the stream into a Puzzle object + * Helper method for parseScrFile. + * + * @param puzzle The object to store what is parsed + * @param stream Scr file stream + */ + void parsePuzzle(Puzzle *puzzle, Common::SeekableReadStream &stream); + + /** + * Parses the stream into a Criteria object + * Helper method for parsePuzzle. + * + * @param criteria Pointer to the Criteria object to fill + * @param stream Scr file stream + * @return Whether any criteria were read + */ + bool parseCriteria(Common::SeekableReadStream &stream, Common::List > &criteriaList) const; + + /** + * Parses the stream into a ResultAction objects + * Helper method for parsePuzzle. + * + * @param stream Scr file stream + * @param actionList The list where the results will be added + * @return Created Results object + */ + void parseResults(Common::SeekableReadStream &stream, Common::List &actionList) const; + + /** + * Helper method for parsePuzzle. Parses the stream into a bitwise or of the StateFlags enum + * + * @param stream Scr file stream + * @return Bitwise OR of all the flags set within the puzzle + */ + uint parseFlags(Common::SeekableReadStream &stream) const; + + /** + * Helper method for parseScrFile. Parses the stream into a Control object + * + * @param line The line initially read + * @param stream Scr file stream + */ + void parseControl(Common::String &line, Common::SeekableReadStream &stream); +}; + + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/single_value_container.cpp b/engines/zvision/single_value_container.cpp deleted file mode 100644 index 837bd8d7fc..0000000000 --- a/engines/zvision/single_value_container.cpp +++ /dev/null @@ -1,348 +0,0 @@ -/* 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 "zvision/single_value_container.h" - -#include "common/textconsole.h" -#include "common/str.h" - - -namespace ZVision { - -SingleValueContainer::SingleValueContainer(ValueType type) : _objectType(type) { } - -SingleValueContainer::SingleValueContainer(bool value) : _objectType(BOOL) { - _value.boolVal = value; -} - -SingleValueContainer::SingleValueContainer(byte value) : _objectType(BYTE) { - _value.byteVal = value; -} - -SingleValueContainer::SingleValueContainer(int16 value) : _objectType(INT16) { - _value.int16Val = value; -} - -SingleValueContainer::SingleValueContainer(uint16 value) : _objectType(UINT16) { - _value.uint16Val = value; -} - -SingleValueContainer::SingleValueContainer(int32 value) : _objectType(INT32) { - _value.int32Val = value; -} - -SingleValueContainer::SingleValueContainer(uint32 value) : _objectType(UINT32) { - _value.uint32Val = value; -} - -SingleValueContainer::SingleValueContainer(float value) : _objectType(FLOAT) { - _value.floatVal = value; -} - -SingleValueContainer::SingleValueContainer(double value) : _objectType(DOUBLE) { - _value.doubleVal = value; -} - -SingleValueContainer::SingleValueContainer(Common::String value) : _objectType(BYTE) { - _value.stringVal = new char[value.size() + 1]; - memcpy(_value.stringVal, value.c_str(), value.size() + 1); -} - -SingleValueContainer::SingleValueContainer(const SingleValueContainer &other) { - _objectType = other._objectType; - - switch (_objectType) { - case BOOL: - _value.boolVal = other._value.boolVal; - break; - case BYTE: - _value.byteVal = other._value.byteVal; - break; - case INT16: - _value.int16Val = other._value.int16Val; - break; - case UINT16: - _value.uint16Val = other._value.uint16Val; - break; - case INT32: - _value.int32Val = other._value.int32Val; - break; - case UINT32: - _value.uint32Val = other._value.uint32Val; - break; - case FLOAT: - _value.floatVal = other._value.floatVal; - break; - case DOUBLE: - _value.doubleVal = other._value.doubleVal; - break; - case STRING: - uint32 length = strlen(other._value.stringVal); - _value.stringVal = new char[length + 1]; - memcpy(_value.stringVal, other._value.stringVal, length + 1); - break; - } -} - -SingleValueContainer::~SingleValueContainer() { - deleteCharPointer(); -} - -void SingleValueContainer::deleteCharPointer() { - if (_objectType == STRING) - delete[] _value.stringVal; -} - - -SingleValueContainer &SingleValueContainer::operator=(const bool &rhs) { - if (_objectType == BOOL) { - _value.boolVal = rhs; - return *this; - } - - deleteCharPointer(); - _objectType = BOOL; - _value.boolVal = rhs; - - return *this; -} - -SingleValueContainer &SingleValueContainer::operator=(const byte &rhs) { - if (_objectType == BYTE) { - _value.byteVal = rhs; - return *this; - } - - deleteCharPointer(); - _objectType = BYTE; - _value.byteVal = rhs; - - return *this; -} - -SingleValueContainer &SingleValueContainer::operator=(const int16 &rhs) { - if (_objectType == INT16) { - _value.int16Val = rhs; - return *this; - } - - deleteCharPointer(); - _objectType = INT16; - _value.int16Val = rhs; - - return *this; -} - -SingleValueContainer &SingleValueContainer::operator=(const uint16 &rhs) { - if (_objectType == UINT16) { - _value.uint16Val = rhs; - return *this; - } - - deleteCharPointer(); - _objectType = UINT16; - _value.uint16Val = rhs; - - return *this; -} - -SingleValueContainer &SingleValueContainer::operator=(const int32 &rhs) { - if (_objectType == INT32) { - _value.int32Val = rhs; - return *this; - } - - deleteCharPointer(); - _objectType = INT32; - _value.int32Val = rhs; - - return *this; -} - -SingleValueContainer &SingleValueContainer::operator=(const uint32 &rhs) { - if (_objectType == UINT32) { - _value.uint32Val = rhs; - return *this; - } - - deleteCharPointer(); - _objectType = UINT32; - _value.uint32Val = rhs; - - return *this; -} - -SingleValueContainer &SingleValueContainer::operator=(const float &rhs) { - if (_objectType == FLOAT) { - _value.floatVal = rhs; - return *this; - } - - deleteCharPointer(); - _objectType = FLOAT; - _value.floatVal = rhs; - - return *this; -} - -SingleValueContainer &SingleValueContainer::operator=(const double &rhs) { - if (_objectType == DOUBLE) { - _value.doubleVal = rhs; - return *this; - } - - deleteCharPointer(); - _objectType = DOUBLE; - _value.doubleVal = rhs; - - return *this; -} - -SingleValueContainer &SingleValueContainer::operator=(const Common::String &rhs) { - if (_objectType != STRING) { - _objectType = STRING; - _value.stringVal = new char[rhs.size() + 1]; - memcpy(_value.stringVal, rhs.c_str(), rhs.size() + 1); - - return *this; - } - - uint32 length = strlen(_value.stringVal); - if (length <= rhs.size() + 1) { - memcpy(_value.stringVal, rhs.c_str(), rhs.size() + 1); - } else { - delete[] _value.stringVal; - _value.stringVal = new char[rhs.size() + 1]; - memcpy(_value.stringVal, rhs.c_str(), rhs.size() + 1); - } - - return *this; -} - -SingleValueContainer &SingleValueContainer::operator=(const SingleValueContainer &rhs) { - switch (_objectType) { - case BOOL: - return operator=(rhs._value.boolVal); - case BYTE: - return operator=(rhs._value.byteVal); - case INT16: - return operator=(rhs._value.int16Val); - case UINT16: - return operator=(rhs._value.uint16Val); - case INT32: - return operator=(rhs._value.int32Val); - case UINT32: - return operator=(rhs._value.uint32Val); - case FLOAT: - return operator=(rhs._value.floatVal); - case DOUBLE: - return operator=(rhs._value.doubleVal); - case STRING: - uint32 length = strlen(rhs._value.stringVal); - - _value.stringVal = new char[length + 1]; - memcpy(_value.stringVal, rhs._value.stringVal, length + 1); - - return *this; - } - - return *this; -} - - -bool SingleValueContainer::getBoolValue(bool *returnValue) const { - if (_objectType != BOOL) { - warning("'Object' is not storing a bool."); - return false; - } - - *returnValue = _value.boolVal; - return true; -} - -bool SingleValueContainer::getByteValue(byte *returnValue) const { - if (_objectType != BYTE) - warning("'Object' is not storing a byte."); - - *returnValue = _value.byteVal; - return true; -} - -bool SingleValueContainer::getInt16Value(int16 *returnValue) const { - if (_objectType != INT16) - warning("'Object' is not storing an int16."); - - *returnValue = _value.int16Val; - return true; -} - -bool SingleValueContainer::getUInt16Value(uint16 *returnValue) const { - if (_objectType != UINT16) - warning("'Object' is not storing a uint16."); - - *returnValue = _value.uint16Val; - return true; -} - -bool SingleValueContainer::getInt32Value(int32 *returnValue) const { - if (_objectType != INT32) - warning("'Object' is not storing an int32."); - - *returnValue = _value.int32Val; - return true; -} - -bool SingleValueContainer::getUInt32Value(uint32 *returnValue) const { - if (_objectType != UINT32) - warning("'Object' is not storing a uint32."); - - *returnValue = _value.uint32Val; - return true; -} - -bool SingleValueContainer::getFloatValue(float *returnValue) const { - if (_objectType != FLOAT) - warning("'Object' is not storing a float."); - - *returnValue = _value.floatVal; - return true; -} - -bool SingleValueContainer::getDoubleValue(double *returnValue) const { - if (_objectType != DOUBLE) - warning("'Object' is not storing a double."); - - *returnValue = _value.doubleVal; - return true; -} - -bool SingleValueContainer::getStringValue(Common::String *returnValue) const { - if (_objectType != STRING) - warning("'Object' is not storing a Common::String."); - - *returnValue = _value.stringVal; - return true; -} - -} // End of namespace ZVision diff --git a/engines/zvision/single_value_container.h b/engines/zvision/single_value_container.h deleted file mode 100644 index 45b5a89e95..0000000000 --- a/engines/zvision/single_value_container.h +++ /dev/null @@ -1,183 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef ZVISION_SINGLE_VALUE_CONTAINER_H -#define ZVISION_SINGLE_VALUE_CONTAINER_H - -namespace Common { -class String; -} - -namespace ZVision { - -/** - * A generic single value storage class. It is useful for storing different - * value types in a single List, Hashmap, etc. - */ -class SingleValueContainer { -public: - enum ValueType { - BOOL, - BYTE, - INT16, - UINT16, - INT32, - UINT32, - FLOAT, - DOUBLE, - STRING - }; - - // Constructors - explicit SingleValueContainer(ValueType type); - explicit SingleValueContainer(bool value); - explicit SingleValueContainer(byte value); - explicit SingleValueContainer(int16 value); - explicit SingleValueContainer(uint16 value); - explicit SingleValueContainer(int32 value); - explicit SingleValueContainer(uint32 value); - explicit SingleValueContainer(float value); - explicit SingleValueContainer(double value); - explicit SingleValueContainer(Common::String value); - - // Copy constructor - explicit SingleValueContainer(const SingleValueContainer& other); - - // Destructor - ~SingleValueContainer(); - -private: - ValueType _objectType; - - union { - bool boolVal; - byte byteVal; - int16 int16Val; - uint16 uint16Val; - int32 int32Val; - uint32 uint32Val; - float floatVal; - double doubleVal; - char *stringVal; - } _value; - -public: - SingleValueContainer &operator=(const bool &rhs); - SingleValueContainer &operator=(const byte &rhs); - SingleValueContainer &operator=(const int16 &rhs); - SingleValueContainer &operator=(const uint16 &rhs); - SingleValueContainer &operator=(const int32 &rhs); - SingleValueContainer &operator=(const uint32 &rhs); - SingleValueContainer &operator=(const float &rhs); - SingleValueContainer &operator=(const double &rhs); - SingleValueContainer &operator=(const Common::String &rhs); - - SingleValueContainer& operator=(const SingleValueContainer &rhs); - - /** - * Retrieve a bool from the container. If the container is not storing a - * bool, this will return false and display a warning(). - * - * @param returnValue Pointer to where you want the value stored - * @return Value indicating whether the value assignment was successful - */ - bool getBoolValue(bool *returnValue) const; - /** - * Retrieve a byte from the container. If the container is not storing a - * byte, this will return false and display a warning(). - * - * @param returnValue Pointer to where you want the value stored - * @return Value indicating whether the value assignment was successful - */ - bool getByteValue(byte *returnValue) const; - /** - * Retrieve an int16 from the container. If the container is not storing an - * int16, this will return false and display a warning(). - * - * @param returnValue Pointer to where you want the value stored - * @return Value indicating whether the value assignment was successful - */ - bool getInt16Value(int16 *returnValue) const; - /** - * Retrieve a uint16 from the container. If the container is not storing a - * uint16, this will return false and display a warning(). - * - * @param returnValue Pointer to where you want the value stored - * @return Value indicating whether the value assignment was successful - */ - bool getUInt16Value(uint16 *returnValue) const; - /** - * Retrieve an int32 from the container. If the container is not storing an - * int32, this will return false and display a warning(). - * - * @param returnValue Pointer to where you want the value stored - * @return Value indicating whether the value assignment was successful - */ - bool getInt32Value(int32 *returnValue) const; - /** - * Retrieve a uint32 from the container. If the container is not storing a - * uint32, this will return false and display a warning(). - * - * @param returnValue Pointer to where you want the value stored - * @return Value indicating whether the value assignment was successful - */ - bool getUInt32Value(uint32 *returnValue) const; - /** - * Retrieve a float from the container. If the container is not storing a - * float, this will return false and display a warning(). - * - * @param returnValue Pointer to where you want the value stored - * @return Value indicating whether the value assignment was successful - */ - bool getFloatValue(float *returnValue) const; - /** - * Retrieve a double from the container. If the container is not storing a - * double, this will return false and display a warning(). - * - * @param returnValue Pointer to where you want the value stored - * @return Value indicating whether the value assignment was successful - */ - bool getDoubleValue(double *returnValue) const; - /** - * Retrieve a String from the container. If the container is not storing a - * string, this will return false and display a warning(). - * - * Caution: Strings are internally stored as char[]. getStringValue uses - * Common::String::operator=(char *) to do the assigment, which uses both - * strlen() AND memmove(). - * - * @param returnValue Pointer to where you want the value stored - * @return Value indicating whether the value assignment was successful - */ - bool getStringValue(Common::String *returnValue) const; - -private: - /** - * Helper method for destruction and assignment. It checks to see - * if the char pointer is being used, and if so calls delete on it - */ - void deleteCharPointer(); -}; - -} // End of namespace ZVision - -#endif diff --git a/engines/zvision/sound/zork_raw.cpp b/engines/zvision/sound/zork_raw.cpp new file mode 100644 index 0000000000..e64feca900 --- /dev/null +++ b/engines/zvision/sound/zork_raw.cpp @@ -0,0 +1,217 @@ +/* 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 "zvision/zork_raw.h" + +#include "zvision/zvision.h" +#include "zvision/detection.h" +#include "zvision/utility.h" + +#include "common/file.h" +#include "common/str.h" +#include "common/stream.h" +#include "common/memstream.h" +#include "common/bufferedstream.h" +#include "common/util.h" + +#include "audio/audiostream.h" +#include "audio/decoders/raw.h" + + +namespace ZVision { + +const int16 RawZorkStream::_stepAdjustmentTable[8] = {-1, -1, -1, 1, 4, 7, 10, 12}; + +const int32 RawZorkStream::_amplitudeLookupTable[89] = {0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, + 0x0010, 0x0011, 0x0013, 0x0015, 0x0017, 0x0019, 0x001C, 0x001F, + 0x0022, 0x0025, 0x0029, 0x002D, 0x0032, 0x0037, 0x003C, 0x0042, + 0x0049, 0x0050, 0x0058, 0x0061, 0x006B, 0x0076, 0x0082, 0x008F, + 0x009D, 0x00AD, 0x00BE, 0x00D1, 0x00E6, 0x00FD, 0x0117, 0x0133, + 0x0151, 0x0173, 0x0198, 0x01C1, 0x01EE, 0x0220, 0x0256, 0x0292, + 0x02D4, 0x031C, 0x036C, 0x03C3, 0x0424, 0x048E, 0x0502, 0x0583, + 0x0610, 0x06AB, 0x0756, 0x0812, 0x08E0, 0x09C3, 0x0ABD, 0x0BD0, + 0x0CFF, 0x0E4C, 0x0FBA, 0x114C, 0x1307, 0x14EE, 0x1706, 0x1954, + 0x1BDC, 0x1EA5, 0x21B6, 0x2515, 0x28CA, 0x2CDF, 0x315B, 0x364B, + 0x3BB9, 0x41B2, 0x4844, 0x4F7E, 0x5771, 0x602F, 0x69CE, 0x7462, 0x7FFF}; + +const SoundParams RawZorkStream::_zNemSoundParamLookupTable[6] = {{'6', 0x2B11, false, false}, + {'a', 0x5622, false, true}, + {'b', 0x5622, true, true}, + {'n', 0x2B11, false, true}, + {'s', 0x5622, false, true}, + {'t', 0x5622, true, true} +}; + +const SoundParams RawZorkStream::_zgiSoundParamLookupTable[5] = {{'a',0x5622, false, false}, + {'k',0x2B11, true, true}, + {'p',0x5622, false, true}, + {'q',0x5622, true, true}, + {'u',0xAC44, true, true} +}; + +RawZorkStream::RawZorkStream(uint32 rate, bool stereo, DisposeAfterUse::Flag disposeStream, Common::SeekableReadStream *stream) + : _rate(rate), + _stereo(0), + _stream(stream, disposeStream), + _endOfData(false) { + if (stereo) + _stereo = 1; + + _lastSample[0].index = 0; + _lastSample[0].sample = 0; + _lastSample[1].index = 0; + _lastSample[1].sample = 0; + + // Calculate the total playtime of the stream + if (stereo) + _playtime = Audio::Timestamp(0, _stream->size() / 2, rate); + else + _playtime = Audio::Timestamp(0, _stream->size(), rate); +} + +int RawZorkStream::readBuffer(int16 *buffer, const int numSamples) { + int bytesRead = 0; + + // 0: Left, 1: Right + uint channel = 0; + + while (bytesRead < numSamples) { + byte encodedSample = _stream->readByte(); + if (_stream->eos()) { + _endOfData = true; + return bytesRead; + } + bytesRead++; + + int16 index = _lastSample[channel].index; + uint32 lookUpSample = _amplitudeLookupTable[index]; + + int32 sample = 0; + + if (encodedSample & 0x40) + sample += lookUpSample; + if (encodedSample & 0x20) + sample += lookUpSample >> 1; + if (encodedSample & 0x10) + sample += lookUpSample >> 2; + if (encodedSample & 8) + sample += lookUpSample >> 3; + if (encodedSample & 4) + sample += lookUpSample >> 4; + if (encodedSample & 2) + sample += lookUpSample >> 5; + if (encodedSample & 1) + sample += lookUpSample >> 6; + if (encodedSample & 0x80) + sample = -sample; + + sample += _lastSample[channel].sample; + sample = CLIP(sample, -32768, 32767); + + buffer[bytesRead - 1] = (int16)sample; + + index += _stepAdjustmentTable[(encodedSample >> 4) & 7]; + index = CLIP(index, 0, 88); + + _lastSample[channel].sample = sample; + _lastSample[channel].index = index; + + // Increment and wrap the channel + channel = (channel + 1) & _stereo; + } + + return bytesRead; +} + +bool RawZorkStream::rewind() { + _stream->seek(0, 0); + _stream->clearErr(); + _endOfData = false; + _lastSample[0].index = 0; + _lastSample[0].sample = 0; + _lastSample[1].index = 0; + _lastSample[1].sample = 0; + + return true; +} + +Audio::RewindableAudioStream *makeRawZorkStream(Common::SeekableReadStream *stream, + int rate, + bool stereo, + DisposeAfterUse::Flag disposeAfterUse) { + if (stereo) + assert(stream->size() % 2 == 0); + + return new RawZorkStream(rate, stereo, disposeAfterUse, stream); +} + +Audio::RewindableAudioStream *makeRawZorkStream(const byte *buffer, uint32 size, + int rate, + bool stereo, + DisposeAfterUse::Flag disposeAfterUse) { + return makeRawZorkStream(new Common::MemoryReadStream(buffer, size, disposeAfterUse), rate, stereo, DisposeAfterUse::YES); +} + +Audio::RewindableAudioStream *makeRawZorkStream(const Common::String &filePath, ZVision *engine) { + Common::File *file = new Common::File(); + assert(file->open(filePath)); + + Common::String fileName = getFileName(filePath); + fileName.toLowercase(); + + SoundParams soundParams = { ' ', 0, false, false }; + bool foundParams = false; + char fileIdentifier = (engine->getGameId() == GID_NEMESIS) ? fileName[6] : fileName[7]; + + if (engine->getGameId() == GID_NEMESIS) { + for (int i = 0; i < 6; ++i) { + if (RawZorkStream::_zNemSoundParamLookupTable[i].identifier == fileIdentifier) { + soundParams = RawZorkStream::_zNemSoundParamLookupTable[i]; + foundParams = true; + } + } + } else if (engine->getGameId() == GID_GRANDINQUISITOR) { + for (int i = 0; i < 6; ++i) { + if (RawZorkStream::_zgiSoundParamLookupTable[i].identifier == fileIdentifier) { + soundParams = RawZorkStream::_zgiSoundParamLookupTable[i]; + foundParams = true; + } + } + } + + if (!foundParams) + error("Unable to find sound params for file '%s'. File identifier is '%c'", filePath.c_str(), fileIdentifier); + + if (soundParams.packed) { + return makeRawZorkStream(wrapBufferedSeekableReadStream(file, 2048, DisposeAfterUse::YES), soundParams.rate, soundParams.stereo, DisposeAfterUse::YES); + } else { + byte flags = 0; + if (soundParams.stereo) + flags |= Audio::FLAG_STEREO; + + return Audio::makeRawStream(file, soundParams.rate, flags, DisposeAfterUse::YES); + } +} + +} // End of namespace ZVision diff --git a/engines/zvision/sound/zork_raw.h b/engines/zvision/sound/zork_raw.h new file mode 100644 index 0000000000..481ea79f2d --- /dev/null +++ b/engines/zvision/sound/zork_raw.h @@ -0,0 +1,120 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ZVISION_ZORK_RAW_H +#define ZVISION_ZORK_RAW_H + +#include "audio/audiostream.h" + + +namespace Common { +class SeekableReadStream; +} + +namespace ZVision { + +class ZVision; + +struct SoundParams { + char identifier; + uint32 rate; + bool stereo; + bool packed; +}; + +/** + * This is a stream, which allows for playing raw ADPCM data from a stream. + */ +class RawZorkStream : public Audio::RewindableAudioStream { +public: + RawZorkStream(uint32 rate, bool stereo, DisposeAfterUse::Flag disposeStream, Common::SeekableReadStream *stream); + + ~RawZorkStream() { + } + +public: + static const SoundParams _zNemSoundParamLookupTable[6]; + static const SoundParams _zgiSoundParamLookupTable[5]; + +private: + const int _rate; // Sample rate of stream + Audio::Timestamp _playtime; // Calculated total play time + Common::DisposablePtr _stream; // Stream to read data from + bool _endOfData; // Whether the stream end has been reached + uint _stereo; + + /** + * Holds the frequency and index from the last sample + * 0 holds the left channel, 1 holds the right channel + */ + struct { + int32 sample; + int16 index; + } _lastSample[2]; + + static const int16 _stepAdjustmentTable[8]; + static const int32 _amplitudeLookupTable[89]; + +public: + int readBuffer(int16 *buffer, const int numSamples); + + bool isStereo() const { return true; } + bool endOfData() const { return _endOfData; } + + int getRate() const { return _rate; } + Audio::Timestamp getLength() const { return _playtime; } + + bool rewind(); +}; + +/** + * Creates an audio stream, which plays from the given buffer. + * + * @param buffer Buffer to play from. + * @param size Size of the buffer in bytes. + * @param rate Rate of the sound data. + * @param dispose AfterUse Whether to free the buffer after use (with free!). + * @return The new SeekableAudioStream (or 0 on failure). + */ +Audio::RewindableAudioStream *makeRawZorkStream(const byte *buffer, uint32 size, + int rate, + bool stereo, + DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); + +/** + * Creates an audio stream, which plays from the given stream. + * + * @param stream Stream object to play from. + * @param rate Rate of the sound data. + * @param dispose AfterUse Whether to delete the stream after use. + * @return The new SeekableAudioStream (or 0 on failure). + */ +Audio::RewindableAudioStream *makeRawZorkStream(Common::SeekableReadStream *stream, + int rate, + bool stereo, + DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); + +Audio::RewindableAudioStream *makeRawZorkStream(const Common::String &filePath, ZVision *engine); + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/string_manager.cpp b/engines/zvision/string_manager.cpp deleted file mode 100644 index ab42f3d3e0..0000000000 --- a/engines/zvision/string_manager.cpp +++ /dev/null @@ -1,255 +0,0 @@ -/* 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 "zvision/string_manager.h" - -#include "zvision/truetype_font.h" - -#include "common/file.h" -#include "common/tokenizer.h" -#include "common/debug.h" - -#include "graphics/fontman.h" -#include "graphics/colormasks.h" - - -namespace ZVision { - -StringManager::StringManager(ZVision *engine) - : _engine(engine) { -} - -StringManager::~StringManager() { - for (Common::HashMap::iterator iter = _fonts.begin(); iter != _fonts.end(); ++iter) { - delete iter->_value; - } -} - -void StringManager::initialize(ZVisionGameId gameId) { - if (gameId == GID_NEMESIS) { - // TODO: Check this hardcoded filename against all versions of Nemesis - parseStrFile("nemesis.str"); - } else if (gameId == GID_GRANDINQUISITOR) { - // TODO: Check this hardcoded filename against all versions of Grand Inquisitor - parseStrFile("inquis.str"); - } -} - -void StringManager::parseStrFile(const Common::String &fileName) { - Common::File file; - if (!file.open(fileName)) { - warning("%s does not exist. String parsing failed", fileName.c_str()); - return; - } - - uint lineNumber = 0; - while (!file.eos()) { - _lastStyle.align = Graphics::kTextAlignLeft; - _lastStyle.color = 0; - _lastStyle.font = nullptr; - - Common::String asciiLine = readWideLine(file); - if (asciiLine.empty()) { - continue; - } - - char tagString[150]; - uint tagStringCursor = 0; - char textString[150]; - uint textStringCursor = 0; - bool inTag = false; - - for (uint i = 0; i < asciiLine.size(); ++i) { - switch (asciiLine[i]) { - case '<': - inTag = true; - if (!_inGameText[lineNumber].fragments.empty()) { - _inGameText[lineNumber].fragments.back().text = Common::String(textString, textStringCursor); - textStringCursor = 0; - } - break; - case '>': - inTag = false; - parseTag(Common::String(tagString, tagStringCursor), lineNumber); - tagStringCursor = 0; - break; - default: - if (inTag) { - tagString[tagStringCursor] = asciiLine[i]; - tagStringCursor++; - } else { - textString[textStringCursor] = asciiLine[i]; - textStringCursor++; - } - break; - } - } - - if (textStringCursor > 0) { - _inGameText[lineNumber].fragments.back().text = Common::String(textString, textStringCursor); - } - - lineNumber++; - assert(lineNumber <= NUM_TEXT_LINES); - } -} - -void StringManager::parseTag(const Common::String &tagString, uint lineNumber) { - Common::StringTokenizer tokenizer(tagString); - - Common::String token = tokenizer.nextToken(); - - Common::String fontName; - bool bold = false; - Graphics::TextAlign align = _lastStyle.align; - int point = _lastStyle.font != nullptr ? _lastStyle.font->_fontHeight : 12; - int red = 0; - int green = 0; - int blue = 0; - - while (!token.empty()) { - if (token.matchString("font", true)) { - fontName = tokenizer.nextToken(); - } else if (token.matchString("bold", true)) { - token = tokenizer.nextToken(); - if (token.matchString("on", false)) { - bold = true; - } - } else if (token.matchString("justify", true)) { - token = tokenizer.nextToken(); - if (token.matchString("center", false)) { - align = Graphics::kTextAlignCenter; - } else if (token.matchString("right", false)) { - align = Graphics::kTextAlignRight; - } - } else if (token.matchString("point", true)) { - point = atoi(tokenizer.nextToken().c_str()); - } else if (token.matchString("red", true)) { - red = atoi(tokenizer.nextToken().c_str()); - } else if (token.matchString("green", true)) { - green = atoi(tokenizer.nextToken().c_str()); - } else if (token.matchString("blue", true)) { - blue = atoi(tokenizer.nextToken().c_str()); - } - - token = tokenizer.nextToken(); - } - - TextFragment fragment; - - if (fontName.empty()) { - fragment.style.font = _lastStyle.font; - } else { - Common::String newFontName; - if (fontName.matchString("*times new roman*", true)) { - if (bold) { - newFontName = "timesbd.ttf"; - } else { - newFontName = "times.ttf"; - } - } else if (fontName.matchString("*courier new*", true)) { - if (bold) { - newFontName = "courbd.ttf"; - } else { - newFontName = "cour.ttf"; - } - } else if (fontName.matchString("*century schoolbook*", true)) { - if (bold) { - newFontName = "censcbkbd.ttf"; - } else { - newFontName = "censcbk.ttf"; - } - } else if (fontName.matchString("*garamond*", true)) { - if (bold) { - newFontName = "garabd.ttf"; - } else { - newFontName = "gara.ttf"; - } - } else { - debug("Could not identify font: %s. Reverting to Arial", fontName.c_str()); - if (bold) { - newFontName = "zorknorm.ttf"; - } else { - newFontName = "arial.ttf"; - } - } - - Common::String fontKey = Common::String::format("%s-%d", newFontName.c_str(), point); - if (_fonts.contains(fontKey)) { - fragment.style.font = _fonts[fontKey]; - } else { - fragment.style.font = new TruetypeFont(_engine, point); - fragment.style.font->loadFile(newFontName); - _fonts[fontKey] = fragment.style.font; - } - } - - fragment.style.align = align; - fragment.style.color = Graphics::ARGBToColor >(0, red, green, blue); - _inGameText[lineNumber].fragments.push_back(fragment); - - _lastStyle = fragment.style; -} - -Common::String StringManager::readWideLine(Common::SeekableReadStream &stream) { - Common::String asciiString; - - // Don't spam the user with warnings about UTF-16 support. - // Just do one warning per String - bool charOverflowWarning = false; - - uint16 value = stream.readUint16LE(); - while (!stream.eos()) { - // Check for CRLF - if (value == 0x0A0D) { - // Read in the extra NULL char - stream.readByte(); // \0 - // End of the line. Break - break; - } - - // Crush each octet pair to a single octet with a simple cast - if (value > 255) { - charOverflowWarning = true; - value = '?'; - } - char charValue = (char)value; - - asciiString += charValue; - - value = stream.readUint16LE(); - } - - if (charOverflowWarning) { - warning("UTF-16 is not supported. Characters greater than 255 are replaced with '?'"); - } - - return asciiString; -} - -StringManager::TextStyle StringManager::getTextStyle(uint stringNumber) { - return _inGameText[stringNumber].fragments.front().style; -} - -} // End of namespace ZVision diff --git a/engines/zvision/string_manager.h b/engines/zvision/string_manager.h deleted file mode 100644 index 9cfed5261b..0000000000 --- a/engines/zvision/string_manager.h +++ /dev/null @@ -1,85 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * - */ - -#ifndef ZVISION_STRING_MANAGER_H -#define ZVISION_STRING_MANAGER_H - -#include "zvision/detection.h" -#include "zvision/truetype_font.h" - - -namespace Graphics { -class FontManager; -} - -namespace ZVision { - -class ZVision; - -class StringManager { -public: - StringManager(ZVision *engine); - ~StringManager(); - -public: - struct TextStyle { - TruetypeFont *font; - uint16 color; // In RBG 565 - Graphics::TextAlign align; - }; - - struct TextFragment { - TextStyle style; - Common::String text; - }; - -private: - struct InGameText { - Common::List fragments; - }; - - enum { - NUM_TEXT_LINES = 56 // Max number of lines in a .str file. We hardcode this number because we know ZNem uses 42 strings and ZGI uses 56 - }; - -private: - ZVision *_engine; - InGameText _inGameText[NUM_TEXT_LINES]; - Common::HashMap _fonts; - - TextStyle _lastStyle; - -public: - void initialize(ZVisionGameId gameId); - StringManager::TextStyle getTextStyle(uint stringNumber); - -private: - void parseStrFile(const Common::String &fileName); - void parseTag(const Common::String &tagString, uint lineNumber); - - static Common::String readWideLine(Common::SeekableReadStream &stream); -}; - -} // End of namespace ZVision - -#endif diff --git a/engines/zvision/strings/string_manager.cpp b/engines/zvision/strings/string_manager.cpp new file mode 100644 index 0000000000..ab42f3d3e0 --- /dev/null +++ b/engines/zvision/strings/string_manager.cpp @@ -0,0 +1,255 @@ +/* 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 "zvision/string_manager.h" + +#include "zvision/truetype_font.h" + +#include "common/file.h" +#include "common/tokenizer.h" +#include "common/debug.h" + +#include "graphics/fontman.h" +#include "graphics/colormasks.h" + + +namespace ZVision { + +StringManager::StringManager(ZVision *engine) + : _engine(engine) { +} + +StringManager::~StringManager() { + for (Common::HashMap::iterator iter = _fonts.begin(); iter != _fonts.end(); ++iter) { + delete iter->_value; + } +} + +void StringManager::initialize(ZVisionGameId gameId) { + if (gameId == GID_NEMESIS) { + // TODO: Check this hardcoded filename against all versions of Nemesis + parseStrFile("nemesis.str"); + } else if (gameId == GID_GRANDINQUISITOR) { + // TODO: Check this hardcoded filename against all versions of Grand Inquisitor + parseStrFile("inquis.str"); + } +} + +void StringManager::parseStrFile(const Common::String &fileName) { + Common::File file; + if (!file.open(fileName)) { + warning("%s does not exist. String parsing failed", fileName.c_str()); + return; + } + + uint lineNumber = 0; + while (!file.eos()) { + _lastStyle.align = Graphics::kTextAlignLeft; + _lastStyle.color = 0; + _lastStyle.font = nullptr; + + Common::String asciiLine = readWideLine(file); + if (asciiLine.empty()) { + continue; + } + + char tagString[150]; + uint tagStringCursor = 0; + char textString[150]; + uint textStringCursor = 0; + bool inTag = false; + + for (uint i = 0; i < asciiLine.size(); ++i) { + switch (asciiLine[i]) { + case '<': + inTag = true; + if (!_inGameText[lineNumber].fragments.empty()) { + _inGameText[lineNumber].fragments.back().text = Common::String(textString, textStringCursor); + textStringCursor = 0; + } + break; + case '>': + inTag = false; + parseTag(Common::String(tagString, tagStringCursor), lineNumber); + tagStringCursor = 0; + break; + default: + if (inTag) { + tagString[tagStringCursor] = asciiLine[i]; + tagStringCursor++; + } else { + textString[textStringCursor] = asciiLine[i]; + textStringCursor++; + } + break; + } + } + + if (textStringCursor > 0) { + _inGameText[lineNumber].fragments.back().text = Common::String(textString, textStringCursor); + } + + lineNumber++; + assert(lineNumber <= NUM_TEXT_LINES); + } +} + +void StringManager::parseTag(const Common::String &tagString, uint lineNumber) { + Common::StringTokenizer tokenizer(tagString); + + Common::String token = tokenizer.nextToken(); + + Common::String fontName; + bool bold = false; + Graphics::TextAlign align = _lastStyle.align; + int point = _lastStyle.font != nullptr ? _lastStyle.font->_fontHeight : 12; + int red = 0; + int green = 0; + int blue = 0; + + while (!token.empty()) { + if (token.matchString("font", true)) { + fontName = tokenizer.nextToken(); + } else if (token.matchString("bold", true)) { + token = tokenizer.nextToken(); + if (token.matchString("on", false)) { + bold = true; + } + } else if (token.matchString("justify", true)) { + token = tokenizer.nextToken(); + if (token.matchString("center", false)) { + align = Graphics::kTextAlignCenter; + } else if (token.matchString("right", false)) { + align = Graphics::kTextAlignRight; + } + } else if (token.matchString("point", true)) { + point = atoi(tokenizer.nextToken().c_str()); + } else if (token.matchString("red", true)) { + red = atoi(tokenizer.nextToken().c_str()); + } else if (token.matchString("green", true)) { + green = atoi(tokenizer.nextToken().c_str()); + } else if (token.matchString("blue", true)) { + blue = atoi(tokenizer.nextToken().c_str()); + } + + token = tokenizer.nextToken(); + } + + TextFragment fragment; + + if (fontName.empty()) { + fragment.style.font = _lastStyle.font; + } else { + Common::String newFontName; + if (fontName.matchString("*times new roman*", true)) { + if (bold) { + newFontName = "timesbd.ttf"; + } else { + newFontName = "times.ttf"; + } + } else if (fontName.matchString("*courier new*", true)) { + if (bold) { + newFontName = "courbd.ttf"; + } else { + newFontName = "cour.ttf"; + } + } else if (fontName.matchString("*century schoolbook*", true)) { + if (bold) { + newFontName = "censcbkbd.ttf"; + } else { + newFontName = "censcbk.ttf"; + } + } else if (fontName.matchString("*garamond*", true)) { + if (bold) { + newFontName = "garabd.ttf"; + } else { + newFontName = "gara.ttf"; + } + } else { + debug("Could not identify font: %s. Reverting to Arial", fontName.c_str()); + if (bold) { + newFontName = "zorknorm.ttf"; + } else { + newFontName = "arial.ttf"; + } + } + + Common::String fontKey = Common::String::format("%s-%d", newFontName.c_str(), point); + if (_fonts.contains(fontKey)) { + fragment.style.font = _fonts[fontKey]; + } else { + fragment.style.font = new TruetypeFont(_engine, point); + fragment.style.font->loadFile(newFontName); + _fonts[fontKey] = fragment.style.font; + } + } + + fragment.style.align = align; + fragment.style.color = Graphics::ARGBToColor >(0, red, green, blue); + _inGameText[lineNumber].fragments.push_back(fragment); + + _lastStyle = fragment.style; +} + +Common::String StringManager::readWideLine(Common::SeekableReadStream &stream) { + Common::String asciiString; + + // Don't spam the user with warnings about UTF-16 support. + // Just do one warning per String + bool charOverflowWarning = false; + + uint16 value = stream.readUint16LE(); + while (!stream.eos()) { + // Check for CRLF + if (value == 0x0A0D) { + // Read in the extra NULL char + stream.readByte(); // \0 + // End of the line. Break + break; + } + + // Crush each octet pair to a single octet with a simple cast + if (value > 255) { + charOverflowWarning = true; + value = '?'; + } + char charValue = (char)value; + + asciiString += charValue; + + value = stream.readUint16LE(); + } + + if (charOverflowWarning) { + warning("UTF-16 is not supported. Characters greater than 255 are replaced with '?'"); + } + + return asciiString; +} + +StringManager::TextStyle StringManager::getTextStyle(uint stringNumber) { + return _inGameText[stringNumber].fragments.front().style; +} + +} // End of namespace ZVision diff --git a/engines/zvision/strings/string_manager.h b/engines/zvision/strings/string_manager.h new file mode 100644 index 0000000000..9cfed5261b --- /dev/null +++ b/engines/zvision/strings/string_manager.h @@ -0,0 +1,85 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef ZVISION_STRING_MANAGER_H +#define ZVISION_STRING_MANAGER_H + +#include "zvision/detection.h" +#include "zvision/truetype_font.h" + + +namespace Graphics { +class FontManager; +} + +namespace ZVision { + +class ZVision; + +class StringManager { +public: + StringManager(ZVision *engine); + ~StringManager(); + +public: + struct TextStyle { + TruetypeFont *font; + uint16 color; // In RBG 565 + Graphics::TextAlign align; + }; + + struct TextFragment { + TextStyle style; + Common::String text; + }; + +private: + struct InGameText { + Common::List fragments; + }; + + enum { + NUM_TEXT_LINES = 56 // Max number of lines in a .str file. We hardcode this number because we know ZNem uses 42 strings and ZGI uses 56 + }; + +private: + ZVision *_engine; + InGameText _inGameText[NUM_TEXT_LINES]; + Common::HashMap _fonts; + + TextStyle _lastStyle; + +public: + void initialize(ZVisionGameId gameId); + StringManager::TextStyle getTextStyle(uint stringNumber); + +private: + void parseStrFile(const Common::String &fileName); + void parseTag(const Common::String &tagString, uint lineNumber); + + static Common::String readWideLine(Common::SeekableReadStream &stream); +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/subtitles.h b/engines/zvision/subtitles.h deleted file mode 100644 index 13426e03e4..0000000000 --- a/engines/zvision/subtitles.h +++ /dev/null @@ -1,29 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * - */ - -#ifndef ZVISION_SUBTITLES_H -#define ZVISION_SUBTITLES_H - -// TODO: Implement Subtitles - -#endif diff --git a/engines/zvision/subtitles/subtitles.h b/engines/zvision/subtitles/subtitles.h new file mode 100644 index 0000000000..13426e03e4 --- /dev/null +++ b/engines/zvision/subtitles/subtitles.h @@ -0,0 +1,29 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef ZVISION_SUBTITLES_H +#define ZVISION_SUBTITLES_H + +// TODO: Implement Subtitles + +#endif diff --git a/engines/zvision/timer_node.cpp b/engines/zvision/timer_node.cpp deleted file mode 100644 index b529a661cf..0000000000 --- a/engines/zvision/timer_node.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* 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 "zvision/timer_node.h" - -#include "zvision/zvision.h" -#include "zvision/script_manager.h" - -#include "common/stream.h" - - -namespace ZVision { - -TimerNode::TimerNode(ZVision *engine, uint32 key, uint timeInSeconds) - : Control(engine, key) { - if (_engine->getGameId() == GID_NEMESIS) { - _timeLeft = timeInSeconds * 1000; - } else if (_engine->getGameId() == GID_GRANDINQUISITOR) { - _timeLeft = timeInSeconds * 100; - } - - _engine->getScriptManager()->setStateValue(_key, 1); -} - -TimerNode::~TimerNode() { - if (_timeLeft <= 0) - _engine->getScriptManager()->setStateValue(_key, 2); - else - _engine->getScriptManager()->setStateValue(_key, _timeLeft); // If timer was stopped by stop or kill -} - -bool TimerNode::process(uint32 deltaTimeInMillis) { - _timeLeft -= deltaTimeInMillis; - - if (_timeLeft <= 0) { - // Let the destructor reset the state value - return true; - } - - return false; -} - -void TimerNode::serialize(Common::WriteStream *stream) { - stream->writeUint32LE(_key); - stream->writeUint32LE(_timeLeft); -} - -void TimerNode::deserialize(Common::SeekableReadStream *stream) { - _timeLeft = stream->readUint32LE(); -} - -} // End of namespace ZVision diff --git a/engines/zvision/timer_node.h b/engines/zvision/timer_node.h deleted file mode 100644 index a95373353b..0000000000 --- a/engines/zvision/timer_node.h +++ /dev/null @@ -1,55 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef ZVISION_TIMER_NODE_H -#define ZVISION_TIMER_NODE_H - -#include "zvision/control.h" - -namespace ZVision { - -class ZVision; - -class TimerNode : public Control { -public: - TimerNode(ZVision *engine, uint32 key, uint timeInSeconds); - ~TimerNode(); - - /** - * Decrement the timer by the delta time. If the timer is finished, set the status - * in _globalState and let this node be deleted - * - * @param deltaTimeInMillis The number of milliseconds that have passed since last frame - * @return If true, the node can be deleted after process() finishes - */ - bool process(uint32 deltaTimeInMillis); - void serialize(Common::WriteStream *stream); - void deserialize(Common::SeekableReadStream *stream); - inline bool needsSerialization() { return true; } - -private: - int32 _timeLeft; -}; - -} // End of namespace ZVision - -#endif diff --git a/engines/zvision/truetype_font.cpp b/engines/zvision/truetype_font.cpp deleted file mode 100644 index 289b5fbbaf..0000000000 --- a/engines/zvision/truetype_font.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* 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 "zvision/truetype_font.h" - -#include "zvision/zvision.h" -#include "zvision/render_manager.h" - -#include "common/debug.h" -#include "common/file.h" -#include "common/system.h" - -#include "graphics/font.h" -#include "graphics/fonts/ttf.h" -#include "graphics/surface.h" - - -namespace ZVision { - -TruetypeFont::TruetypeFont(ZVision *engine, int32 fontHeight) - : _engine(engine), - _fontHeight(fontHeight), - _font(0), - _lineHeight(0), - _maxCharWidth(0), - _maxCharHeight(0) { -} - -TruetypeFont::~TruetypeFont(void) { - delete _font; -} - -bool TruetypeFont::loadFile(const Common::String &filename) { - Common::File file; - - bool fileOpened = false; - if (!Common::File::exists(filename)) { - debug("TTF font file %s was not found. Reverting to arial.ttf", filename.c_str()); - fileOpened = file.open("arial.ttf"); - } else { - fileOpened = file.open(filename); - } - - if (!fileOpened) { - debug("TTF file could not be opened"); - return false; - } - - _font = Graphics::loadTTFFont(file, _fontHeight); - _lineHeight = _font->getFontHeight(); - - return true; -} - -Graphics::Surface *TruetypeFont::drawTextToSurface(const Common::String &text, uint16 textColor, int maxWidth, int maxHeight, Graphics::TextAlign align, bool wrap) { - if (text.equals("")) { - return nullptr; - } - - Graphics::Surface *surface = new Graphics::Surface(); - - if (!wrap) { - int width = MIN(_font->getStringWidth(text), maxWidth); - surface->create(width, _lineHeight, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); - // TODO: Add better alpha support by getting the pixels from the backbuffer. - // However doing that requires some kind of caching system so future text doesn't try to use this text as it's alpha background. - surface->fillRect(Common::Rect(0, 0, surface->w, surface->h), 0); - - _font->drawString(surface, text, 0, 0, maxWidth, textColor, align); - return surface; - } - - Common::Array lines; - _font->wordWrapText(text, maxWidth, lines); - - while (maxHeight > 0 && (int)lines.size() * _lineHeight > maxHeight) { - lines.pop_back(); - } - if (lines.size() == 0) { - return nullptr; - } - - surface->create(maxWidth, lines.size() * _lineHeight, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); - surface->fillRect(Common::Rect(0, 0, surface->w, surface->h), 0); - - int heightOffset = 0; - for (Common::Array::iterator it = lines.begin(); it != lines.end(); it++) { - _font->drawString(surface, *it, 0, 0 + heightOffset, maxWidth, textColor, align); - heightOffset += _lineHeight; - } - - return surface; -} - -} // End of namespace ZVision diff --git a/engines/zvision/truetype_font.h b/engines/zvision/truetype_font.h deleted file mode 100644 index 33f016cffd..0000000000 --- a/engines/zvision/truetype_font.h +++ /dev/null @@ -1,81 +0,0 @@ -/* 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. - * - */ - -// This file is based on engines/wintermute/base/fonts/base_font_truetype.h/.cpp - -#ifndef ZVISION_TRUETYPE_FONT_H -#define ZVISION_TRUETYPE_FONT_H - -#include "graphics/font.h" -#include "graphics/pixelformat.h" - - -namespace Graphics { -struct Surface; -} - -namespace ZVision { - -class ZVision; - -class TruetypeFont { -public: - TruetypeFont(ZVision *engine, int32 fontHeight); - ~TruetypeFont(); - -private: - ZVision *_engine; - Graphics::Font *_font; - int _lineHeight; - - size_t _maxCharWidth; - size_t _maxCharHeight; - -public: - int32 _fontHeight; - -public: - /** - * Loads a .ttf file into memory. This must be called - * before any calls to drawTextToSurface - * - * @param filename The file name of the .ttf file to load - */ - bool loadFile(const Common::String &filename); - /** - * Renders the supplied text to a Surface using 0x0 as the - * background color. - * - * @param text The to render - * @param textColor The color to render the text with - * @param maxWidth The max width the text should take up. - * @param maxHeight The max height the text should take up. - * @param align The alignment of the text within the bounds of maxWidth - * @param wrap If true, any words extending past maxWidth will wrap to a new line. If false, ellipses will be rendered to show that the text didn't fit - * @return A Surface containing the rendered text - */ - Graphics::Surface *drawTextToSurface(const Common::String &text, uint16 textColor, int maxWidth, int maxHeight, Graphics::TextAlign align, bool wrap); -}; - -} // End of namespace ZVision - -#endif diff --git a/engines/zvision/utility.cpp b/engines/zvision/utility.cpp deleted file mode 100644 index d973cb2f4c..0000000000 --- a/engines/zvision/utility.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/* 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 "zvision/utility.h" - -#include "zvision/zvision.h" -#include "zvision/zork_raw.h" - -#include "common/tokenizer.h" -#include "common/file.h" - - -namespace ZVision { - -void writeFileContentsToFile(const Common::String &sourceFile, const Common::String &destFile) { - Common::File f; - if (!f.open(sourceFile)) { - return; - } - - byte* buffer = new byte[f.size()]; - f.read(buffer, f.size()); - - Common::DumpFile dumpFile; - dumpFile.open(destFile); - - dumpFile.write(buffer, f.size()); - dumpFile.flush(); - dumpFile.close(); - - delete[] buffer; -} - -void trimCommentsAndWhiteSpace(Common::String *string) { - for (int i = string->size() - 1; i >= 0; i--) { - if ((*string)[i] == '#') { - string->erase(i); - } - } - - string->trim(); -} - -void tryToDumpLine(const Common::String &key, - Common::String &line, - Common::HashMap *count, - Common::HashMap *fileAlreadyUsed, - Common::DumpFile &output) { - const byte numberOfExamplesPerType = 8; - - if ((*count)[key] < numberOfExamplesPerType && !(*fileAlreadyUsed)[key]) { - output.writeString(line); - output.writeByte('\n'); - (*count)[key]++; - (*fileAlreadyUsed)[key] = true; - } -} - -void dumpEveryResultAction(const Common::String &destFile) { - Common::HashMap count; - Common::HashMap fileAlreadyUsed; - - Common::DumpFile output; - output.open(destFile); - - // Find scr files - Common::ArchiveMemberList list; - SearchMan.listMatchingMembers(list, "*.scr"); - - for (Common::ArchiveMemberList::iterator iter = list.begin(); iter != list.end(); ++iter) { - Common::SeekableReadStream *stream = (*iter)->createReadStream(); - - Common::String line = stream->readLine(); - trimCommentsAndWhiteSpace(&line); - - while (!stream->eos()) { - if (line.matchString("*:add*", true)) { - tryToDumpLine("add", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:animplay*", true)) { - tryToDumpLine("animplay", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:animpreload*", true)) { - tryToDumpLine("animpreload", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:animunload*", true)) { - tryToDumpLine("animunload", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:attenuate*", true)) { - tryToDumpLine("attenuate", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:assign*", true)) { - tryToDumpLine("assign", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:change_location*", true)) { - tryToDumpLine("change_location", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:crossfade*", true) && !fileAlreadyUsed["add"]) { - tryToDumpLine("crossfade", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:debug*", true)) { - tryToDumpLine("debug", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:delay_render*", true)) { - tryToDumpLine("delay_render", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:disable_control*", true)) { - tryToDumpLine("disable_control", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:disable_venus*", true)) { - tryToDumpLine("disable_venus", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:display_message*", true)) { - tryToDumpLine("display_message", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:dissolve*", true)) { - tryToDumpLine("dissolve", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:distort*", true)) { - tryToDumpLine("distort", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:enable_control*", true)) { - tryToDumpLine("enable_control", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:flush_mouse_events*", true)) { - tryToDumpLine("flush_mouse_events", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:inventory*", true)) { - tryToDumpLine("inventory", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:kill*", true)) { - tryToDumpLine("kill", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:menu_bar_enable*", true)) { - tryToDumpLine("menu_bar_enable", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:music*", true)) { - tryToDumpLine("music", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:pan_track*", true)) { - tryToDumpLine("pan_track", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:playpreload*", true)) { - tryToDumpLine("playpreload", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:preferences*", true)) { - tryToDumpLine("preferences", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:quit*", true)) { - tryToDumpLine("quit", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:random*", true)) { - tryToDumpLine("random", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:region*", true)) { - tryToDumpLine("region", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:restore_game*", true)) { - tryToDumpLine("restore_game", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:rotate_to*", true)) { - tryToDumpLine("rotate_to", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:save_game*", true)) { - tryToDumpLine("save_game", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:set_partial_screen*", true)) { - tryToDumpLine("set_partial_screen", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:set_screen*", true)) { - tryToDumpLine("set_screen", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:set_venus*", true)) { - tryToDumpLine("set_venus", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:stop*", true)) { - tryToDumpLine("stop", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:streamvideo*", true)) { - tryToDumpLine("streamvideo", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:syncsound*", true)) { - tryToDumpLine("syncsound", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:timer*", true)) { - tryToDumpLine("timer", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:ttytext*", true)) { - tryToDumpLine("ttytext", line, &count, &fileAlreadyUsed, output); - } else if (line.matchString("*:universe_music*", true)) { - tryToDumpLine("universe_music", line, &count, &fileAlreadyUsed, output); - } - - line = stream->readLine(); - trimCommentsAndWhiteSpace(&line); - } - - for (Common::HashMap::iterator fileUsedIter = fileAlreadyUsed.begin(); fileUsedIter != fileAlreadyUsed.end(); ++fileUsedIter) { - fileUsedIter->_value = false; - } - } - - output.close(); -} - -Common::String getFileName(const Common::String &fullPath) { - Common::StringTokenizer tokenizer(fullPath, "/\\"); - Common::String token; - while (!tokenizer.empty()) { - token = tokenizer.nextToken(); - } - - return token; -} - -void convertRawToWav(const Common::String &inputFile, ZVision *engine, const Common::String &outputFile) { - Common::File file; - if (!file.open(inputFile)) - return; - - Audio::AudioStream *audioStream = makeRawZorkStream(inputFile, engine); - - Common::DumpFile output; - output.open(outputFile); - - output.writeUint32BE(MKTAG('R', 'I', 'F', 'F')); - output.writeUint32LE(file.size() * 2 + 36); - output.writeUint32BE(MKTAG('W', 'A', 'V', 'E')); - output.writeUint32BE(MKTAG('f', 'm', 't', ' ')); - output.writeUint32LE(16); - output.writeUint16LE(1); - uint16 numChannels; - if (audioStream->isStereo()) { - numChannels = 2; - output.writeUint16LE(2); - } else { - numChannels = 1; - output.writeUint16LE(1); - } - output.writeUint32LE(audioStream->getRate()); - output.writeUint32LE(audioStream->getRate() * numChannels * 2); - output.writeUint16LE(numChannels * 2); - output.writeUint16LE(16); - output.writeUint32BE(MKTAG('d', 'a', 't', 'a')); - output.writeUint32LE(file.size() * 2); - int16 *buffer = new int16[file.size()]; - audioStream->readBuffer(buffer, file.size()); - output.write(buffer, file.size() * 2); - - delete[] buffer; -} - -} // End of namespace ZVision diff --git a/engines/zvision/utility.h b/engines/zvision/utility.h deleted file mode 100644 index fb571f3fe8..0000000000 --- a/engines/zvision/utility.h +++ /dev/null @@ -1,115 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * - */ - -#ifndef ZVISION_UTILITY_H -#define ZVISION_UTILITY_H - -#include "common/array.h" - - -namespace Common { -class String; -} - -namespace ZVision { - -class ZVision; - -/** - * Opens the sourceFile utilizing Common::File (aka SearchMan) and writes the - * contents to destFile. destFile is created in the working directory - * - * @param sourceFile The 'file' you want the contents of - * @param destFile The name of the file where the content will be written to - */ -void writeFileContentsToFile(const Common::String &sourceFile, const Common::String &destFile); - -/** - * Removes any line comments using '#' as a sequence start. - * Then removes any trailing and leading 'whitespace' using String::trim() - * Note: String::trim uses isspace() to determine what is whitespace and what is not. - * - * @param string The string to modify. It is modified in place - */ -void trimCommentsAndWhiteSpace(Common::String *string); - -/** - * Searches through all the .scr files and dumps 'numberOfExamplesPerType' examples of each type of ResultAction - * ZVision::initialize() must have been called before this function can be used. - * - * @param destFile Where to write the examples - */ -void dumpEveryResultAction(const Common::String &destFile); - -/** - * Removes all duplicate entries from container. Relative order will be preserved. - * - * @param container The Array to remove duplicate entries from - */ -template -void removeDuplicateEntries(Common::Array &container) { - // Length of modified array - uint newLength = 1; - uint j; - - for(uint i = 1; i < container.size(); i++) { - for(j = 0; j < newLength; j++) { - if (container[i] == container[j]) { - break; - } - } - - // If none of the values in index[0..j] of container are the same as array[i], - // then copy the current value to corresponding new position in array - if (j == newLength) { - container[newLength++] = container[i]; - } - } - - // Actually remove the unneeded space - while (container.size() < newLength) { - container.pop_back(); - } -} - -/** - * Gets the name of the file (including extension). Forward or back slashes - * are interpreted as directory changes - * - * @param fullPath A full or partial path to the file. Ex: folderOne/folderTwo/file.txt - * @return The name of the file without any preceding directories. Ex: file.txt - */ -Common::String getFileName(const Common::String &fullPath); - -/** - * Converts a ZVision .RAW file to a .WAV - * The .WAV will be created in the working directory and will overwrite any existing file - * - * @param inputFile The path to the input .RAW file - * @param outputFile The name of the output .WAV file - */ -void convertRawToWav(const Common::String &inputFile, ZVision *engine, const Common::String &outputFile); - -} // End of namespace ZVision - -#endif diff --git a/engines/zvision/utility/clock.cpp b/engines/zvision/utility/clock.cpp new file mode 100644 index 0000000000..c8ee717a33 --- /dev/null +++ b/engines/zvision/utility/clock.cpp @@ -0,0 +1,70 @@ +/* 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 "zvision/clock.h" + +#include "common/system.h" + + +namespace ZVision { + +Clock::Clock(OSystem *system) + : _system(system), + _lastTime(0), + _deltaTime(0), + _pausedTime(0), + _paused(false) { +} + +void Clock::update() { + uint32 currentTime = _system->getMillis(); + + _deltaTime = (currentTime - _lastTime); + if (_paused) { + _deltaTime -= (currentTime - _pausedTime); + } + + if (_deltaTime < 0) { + _deltaTime = 0; + } + + _lastTime = currentTime; +} + +void Clock::start() { + if (_paused) { + _lastTime = _system->getMillis(); + _paused = false; + } +} + +void Clock::stop() { + if (!_paused) { + _pausedTime = _system->getMillis(); + _paused = true; + } +} + +} // End of namespace ZVision diff --git a/engines/zvision/utility/clock.h b/engines/zvision/utility/clock.h new file mode 100644 index 0000000000..3939ba1612 --- /dev/null +++ b/engines/zvision/utility/clock.h @@ -0,0 +1,78 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef ZVISION_CLOCK_H +#define ZVISION_CLOCK_H + +#include "common/types.h" + +class OSystem; + +namespace ZVision { + +/* Class for handling frame to frame deltaTime while keeping track of time pauses/un-pauses */ +class Clock { +public: + Clock(OSystem *system); + +private: + OSystem *_system; + uint32 _lastTime; + int32 _deltaTime; + uint32 _pausedTime; + bool _paused; + +public: + /** + * Updates _deltaTime with the difference between the current time and + * when the last update() was called. + */ + void update(); + /** + * Get the delta time since the last frame. (The time between update() calls) + * + * @return Delta time since the last frame (in milliseconds) + */ + uint32 getDeltaTime() const { return _deltaTime; } + /** + * Get the time from the program starting to the last update() call + * + * @return Time from program start to last update() call (in milliseconds) + */ + uint32 getLastMeasuredTime() { return _lastTime; } + + /** + * Pause the clock. Any future delta times will take this pause into account. + * Has no effect if the clock is already paused. + */ + void start(); + /** + * Un-pause the clock. + * Has no effect if the clock is already un-paused. + */ + void stop(); +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/utility/lzss_read_stream.cpp b/engines/zvision/utility/lzss_read_stream.cpp new file mode 100644 index 0000000000..bbbda6f526 --- /dev/null +++ b/engines/zvision/utility/lzss_read_stream.cpp @@ -0,0 +1,103 @@ +/* ScummVM - Graphic Adventure Engine +* +* ScummVM is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the COPYRIGHT +* file distributed with this source distribution. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. + +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. + +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +*/ + +#include "common/scummsys.h" + +#include "zvision/lzss_read_stream.h" + + +namespace ZVision { + +LzssReadStream::LzssReadStream(Common::SeekableReadStream *source) + : _source(source), + // It's convention to set the starting cursor position to blockSize - 16 + _windowCursor(0x0FEE), + _eosFlag(false) { + // Clear the window to null + memset(_window, 0, BLOCK_SIZE); +} + +uint32 LzssReadStream::decompressBytes(byte *destination, uint32 numberOfBytes) { + uint32 destinationCursor = 0; + + while (destinationCursor < numberOfBytes) { + byte flagbyte = _source->readByte(); + if (_source->eos()) + break; + uint mask = 1; + + for (int i = 0; i < 8; ++i) { + if ((flagbyte & mask) == mask) { + byte data = _source->readByte(); + if (_source->eos()) { + return destinationCursor; + } + + _window[_windowCursor] = data; + destination[destinationCursor++] = data; + + // Increment and wrap the window cursor + _windowCursor = (_windowCursor + 1) & 0xFFF; + } else { + byte low = _source->readByte(); + if (_source->eos()) { + return destinationCursor; + } + + byte high = _source->readByte(); + if (_source->eos()) { + return destinationCursor; + } + + uint16 length = (high & 0xF) + 2; + uint16 offset = low | ((high & 0xF0)<<4); + + for(int j = 0; j <= length; ++j) { + byte temp = _window[(offset + j) & 0xFFF]; + _window[_windowCursor] = temp; + destination[destinationCursor++] = temp; + _windowCursor = (_windowCursor + 1) & 0xFFF; + } + } + + mask = mask << 1; + } + } + + return destinationCursor; +} + +bool LzssReadStream::eos() const { + return _eosFlag; +} + +uint32 LzssReadStream::read(void *dataPtr, uint32 dataSize) { + uint32 bytesRead = decompressBytes(static_cast(dataPtr), dataSize); + if (bytesRead < dataSize) { + // Flag that we're at EOS + _eosFlag = true; + } + + return dataSize; +} + +} // End of namespace ZVision diff --git a/engines/zvision/utility/lzss_read_stream.h b/engines/zvision/utility/lzss_read_stream.h new file mode 100644 index 0000000000..f6b1eb1a65 --- /dev/null +++ b/engines/zvision/utility/lzss_read_stream.h @@ -0,0 +1,72 @@ +/* ScummVM - Graphic Adventure Engine +* +* ScummVM is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the COPYRIGHT +* file distributed with this source distribution. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. + +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. + +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +*/ + +#ifndef ZVISION_LZSS_STREAM_H +#define ZVISION_LZSS_STREAM_H + +#include "common/stream.h" +#include "common/array.h" + + +namespace Common { +class SeekableReadStream; +} + +namespace ZVision { + +class LzssReadStream : public Common::ReadStream { +public: + /** + * A class that decompresses LZSS data and implements ReadStream for easy access + * to the decompiled data. + * + * @param source The source data + */ + LzssReadStream(Common::SeekableReadStream *source); + +private: + enum { + BLOCK_SIZE = 0x1000 + }; + +private: + Common::SeekableReadStream *_source; + byte _window[BLOCK_SIZE]; + uint _windowCursor; + bool _eosFlag; + +public: + bool eos() const; + uint32 read(void *dataPtr, uint32 dataSize); + +private: + /** + * Decompress the next from the source stream. Or until EOS + * + * @param numberOfBytes How many bytes to decompress. This is a count of source bytes, not destination bytes + */ + uint32 decompressBytes(byte* destination, uint32 numberOfBytes); +}; + +} + +#endif diff --git a/engines/zvision/utility/single_value_container.cpp b/engines/zvision/utility/single_value_container.cpp new file mode 100644 index 0000000000..837bd8d7fc --- /dev/null +++ b/engines/zvision/utility/single_value_container.cpp @@ -0,0 +1,348 @@ +/* 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 "zvision/single_value_container.h" + +#include "common/textconsole.h" +#include "common/str.h" + + +namespace ZVision { + +SingleValueContainer::SingleValueContainer(ValueType type) : _objectType(type) { } + +SingleValueContainer::SingleValueContainer(bool value) : _objectType(BOOL) { + _value.boolVal = value; +} + +SingleValueContainer::SingleValueContainer(byte value) : _objectType(BYTE) { + _value.byteVal = value; +} + +SingleValueContainer::SingleValueContainer(int16 value) : _objectType(INT16) { + _value.int16Val = value; +} + +SingleValueContainer::SingleValueContainer(uint16 value) : _objectType(UINT16) { + _value.uint16Val = value; +} + +SingleValueContainer::SingleValueContainer(int32 value) : _objectType(INT32) { + _value.int32Val = value; +} + +SingleValueContainer::SingleValueContainer(uint32 value) : _objectType(UINT32) { + _value.uint32Val = value; +} + +SingleValueContainer::SingleValueContainer(float value) : _objectType(FLOAT) { + _value.floatVal = value; +} + +SingleValueContainer::SingleValueContainer(double value) : _objectType(DOUBLE) { + _value.doubleVal = value; +} + +SingleValueContainer::SingleValueContainer(Common::String value) : _objectType(BYTE) { + _value.stringVal = new char[value.size() + 1]; + memcpy(_value.stringVal, value.c_str(), value.size() + 1); +} + +SingleValueContainer::SingleValueContainer(const SingleValueContainer &other) { + _objectType = other._objectType; + + switch (_objectType) { + case BOOL: + _value.boolVal = other._value.boolVal; + break; + case BYTE: + _value.byteVal = other._value.byteVal; + break; + case INT16: + _value.int16Val = other._value.int16Val; + break; + case UINT16: + _value.uint16Val = other._value.uint16Val; + break; + case INT32: + _value.int32Val = other._value.int32Val; + break; + case UINT32: + _value.uint32Val = other._value.uint32Val; + break; + case FLOAT: + _value.floatVal = other._value.floatVal; + break; + case DOUBLE: + _value.doubleVal = other._value.doubleVal; + break; + case STRING: + uint32 length = strlen(other._value.stringVal); + _value.stringVal = new char[length + 1]; + memcpy(_value.stringVal, other._value.stringVal, length + 1); + break; + } +} + +SingleValueContainer::~SingleValueContainer() { + deleteCharPointer(); +} + +void SingleValueContainer::deleteCharPointer() { + if (_objectType == STRING) + delete[] _value.stringVal; +} + + +SingleValueContainer &SingleValueContainer::operator=(const bool &rhs) { + if (_objectType == BOOL) { + _value.boolVal = rhs; + return *this; + } + + deleteCharPointer(); + _objectType = BOOL; + _value.boolVal = rhs; + + return *this; +} + +SingleValueContainer &SingleValueContainer::operator=(const byte &rhs) { + if (_objectType == BYTE) { + _value.byteVal = rhs; + return *this; + } + + deleteCharPointer(); + _objectType = BYTE; + _value.byteVal = rhs; + + return *this; +} + +SingleValueContainer &SingleValueContainer::operator=(const int16 &rhs) { + if (_objectType == INT16) { + _value.int16Val = rhs; + return *this; + } + + deleteCharPointer(); + _objectType = INT16; + _value.int16Val = rhs; + + return *this; +} + +SingleValueContainer &SingleValueContainer::operator=(const uint16 &rhs) { + if (_objectType == UINT16) { + _value.uint16Val = rhs; + return *this; + } + + deleteCharPointer(); + _objectType = UINT16; + _value.uint16Val = rhs; + + return *this; +} + +SingleValueContainer &SingleValueContainer::operator=(const int32 &rhs) { + if (_objectType == INT32) { + _value.int32Val = rhs; + return *this; + } + + deleteCharPointer(); + _objectType = INT32; + _value.int32Val = rhs; + + return *this; +} + +SingleValueContainer &SingleValueContainer::operator=(const uint32 &rhs) { + if (_objectType == UINT32) { + _value.uint32Val = rhs; + return *this; + } + + deleteCharPointer(); + _objectType = UINT32; + _value.uint32Val = rhs; + + return *this; +} + +SingleValueContainer &SingleValueContainer::operator=(const float &rhs) { + if (_objectType == FLOAT) { + _value.floatVal = rhs; + return *this; + } + + deleteCharPointer(); + _objectType = FLOAT; + _value.floatVal = rhs; + + return *this; +} + +SingleValueContainer &SingleValueContainer::operator=(const double &rhs) { + if (_objectType == DOUBLE) { + _value.doubleVal = rhs; + return *this; + } + + deleteCharPointer(); + _objectType = DOUBLE; + _value.doubleVal = rhs; + + return *this; +} + +SingleValueContainer &SingleValueContainer::operator=(const Common::String &rhs) { + if (_objectType != STRING) { + _objectType = STRING; + _value.stringVal = new char[rhs.size() + 1]; + memcpy(_value.stringVal, rhs.c_str(), rhs.size() + 1); + + return *this; + } + + uint32 length = strlen(_value.stringVal); + if (length <= rhs.size() + 1) { + memcpy(_value.stringVal, rhs.c_str(), rhs.size() + 1); + } else { + delete[] _value.stringVal; + _value.stringVal = new char[rhs.size() + 1]; + memcpy(_value.stringVal, rhs.c_str(), rhs.size() + 1); + } + + return *this; +} + +SingleValueContainer &SingleValueContainer::operator=(const SingleValueContainer &rhs) { + switch (_objectType) { + case BOOL: + return operator=(rhs._value.boolVal); + case BYTE: + return operator=(rhs._value.byteVal); + case INT16: + return operator=(rhs._value.int16Val); + case UINT16: + return operator=(rhs._value.uint16Val); + case INT32: + return operator=(rhs._value.int32Val); + case UINT32: + return operator=(rhs._value.uint32Val); + case FLOAT: + return operator=(rhs._value.floatVal); + case DOUBLE: + return operator=(rhs._value.doubleVal); + case STRING: + uint32 length = strlen(rhs._value.stringVal); + + _value.stringVal = new char[length + 1]; + memcpy(_value.stringVal, rhs._value.stringVal, length + 1); + + return *this; + } + + return *this; +} + + +bool SingleValueContainer::getBoolValue(bool *returnValue) const { + if (_objectType != BOOL) { + warning("'Object' is not storing a bool."); + return false; + } + + *returnValue = _value.boolVal; + return true; +} + +bool SingleValueContainer::getByteValue(byte *returnValue) const { + if (_objectType != BYTE) + warning("'Object' is not storing a byte."); + + *returnValue = _value.byteVal; + return true; +} + +bool SingleValueContainer::getInt16Value(int16 *returnValue) const { + if (_objectType != INT16) + warning("'Object' is not storing an int16."); + + *returnValue = _value.int16Val; + return true; +} + +bool SingleValueContainer::getUInt16Value(uint16 *returnValue) const { + if (_objectType != UINT16) + warning("'Object' is not storing a uint16."); + + *returnValue = _value.uint16Val; + return true; +} + +bool SingleValueContainer::getInt32Value(int32 *returnValue) const { + if (_objectType != INT32) + warning("'Object' is not storing an int32."); + + *returnValue = _value.int32Val; + return true; +} + +bool SingleValueContainer::getUInt32Value(uint32 *returnValue) const { + if (_objectType != UINT32) + warning("'Object' is not storing a uint32."); + + *returnValue = _value.uint32Val; + return true; +} + +bool SingleValueContainer::getFloatValue(float *returnValue) const { + if (_objectType != FLOAT) + warning("'Object' is not storing a float."); + + *returnValue = _value.floatVal; + return true; +} + +bool SingleValueContainer::getDoubleValue(double *returnValue) const { + if (_objectType != DOUBLE) + warning("'Object' is not storing a double."); + + *returnValue = _value.doubleVal; + return true; +} + +bool SingleValueContainer::getStringValue(Common::String *returnValue) const { + if (_objectType != STRING) + warning("'Object' is not storing a Common::String."); + + *returnValue = _value.stringVal; + return true; +} + +} // End of namespace ZVision diff --git a/engines/zvision/utility/single_value_container.h b/engines/zvision/utility/single_value_container.h new file mode 100644 index 0000000000..45b5a89e95 --- /dev/null +++ b/engines/zvision/utility/single_value_container.h @@ -0,0 +1,183 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ZVISION_SINGLE_VALUE_CONTAINER_H +#define ZVISION_SINGLE_VALUE_CONTAINER_H + +namespace Common { +class String; +} + +namespace ZVision { + +/** + * A generic single value storage class. It is useful for storing different + * value types in a single List, Hashmap, etc. + */ +class SingleValueContainer { +public: + enum ValueType { + BOOL, + BYTE, + INT16, + UINT16, + INT32, + UINT32, + FLOAT, + DOUBLE, + STRING + }; + + // Constructors + explicit SingleValueContainer(ValueType type); + explicit SingleValueContainer(bool value); + explicit SingleValueContainer(byte value); + explicit SingleValueContainer(int16 value); + explicit SingleValueContainer(uint16 value); + explicit SingleValueContainer(int32 value); + explicit SingleValueContainer(uint32 value); + explicit SingleValueContainer(float value); + explicit SingleValueContainer(double value); + explicit SingleValueContainer(Common::String value); + + // Copy constructor + explicit SingleValueContainer(const SingleValueContainer& other); + + // Destructor + ~SingleValueContainer(); + +private: + ValueType _objectType; + + union { + bool boolVal; + byte byteVal; + int16 int16Val; + uint16 uint16Val; + int32 int32Val; + uint32 uint32Val; + float floatVal; + double doubleVal; + char *stringVal; + } _value; + +public: + SingleValueContainer &operator=(const bool &rhs); + SingleValueContainer &operator=(const byte &rhs); + SingleValueContainer &operator=(const int16 &rhs); + SingleValueContainer &operator=(const uint16 &rhs); + SingleValueContainer &operator=(const int32 &rhs); + SingleValueContainer &operator=(const uint32 &rhs); + SingleValueContainer &operator=(const float &rhs); + SingleValueContainer &operator=(const double &rhs); + SingleValueContainer &operator=(const Common::String &rhs); + + SingleValueContainer& operator=(const SingleValueContainer &rhs); + + /** + * Retrieve a bool from the container. If the container is not storing a + * bool, this will return false and display a warning(). + * + * @param returnValue Pointer to where you want the value stored + * @return Value indicating whether the value assignment was successful + */ + bool getBoolValue(bool *returnValue) const; + /** + * Retrieve a byte from the container. If the container is not storing a + * byte, this will return false and display a warning(). + * + * @param returnValue Pointer to where you want the value stored + * @return Value indicating whether the value assignment was successful + */ + bool getByteValue(byte *returnValue) const; + /** + * Retrieve an int16 from the container. If the container is not storing an + * int16, this will return false and display a warning(). + * + * @param returnValue Pointer to where you want the value stored + * @return Value indicating whether the value assignment was successful + */ + bool getInt16Value(int16 *returnValue) const; + /** + * Retrieve a uint16 from the container. If the container is not storing a + * uint16, this will return false and display a warning(). + * + * @param returnValue Pointer to where you want the value stored + * @return Value indicating whether the value assignment was successful + */ + bool getUInt16Value(uint16 *returnValue) const; + /** + * Retrieve an int32 from the container. If the container is not storing an + * int32, this will return false and display a warning(). + * + * @param returnValue Pointer to where you want the value stored + * @return Value indicating whether the value assignment was successful + */ + bool getInt32Value(int32 *returnValue) const; + /** + * Retrieve a uint32 from the container. If the container is not storing a + * uint32, this will return false and display a warning(). + * + * @param returnValue Pointer to where you want the value stored + * @return Value indicating whether the value assignment was successful + */ + bool getUInt32Value(uint32 *returnValue) const; + /** + * Retrieve a float from the container. If the container is not storing a + * float, this will return false and display a warning(). + * + * @param returnValue Pointer to where you want the value stored + * @return Value indicating whether the value assignment was successful + */ + bool getFloatValue(float *returnValue) const; + /** + * Retrieve a double from the container. If the container is not storing a + * double, this will return false and display a warning(). + * + * @param returnValue Pointer to where you want the value stored + * @return Value indicating whether the value assignment was successful + */ + bool getDoubleValue(double *returnValue) const; + /** + * Retrieve a String from the container. If the container is not storing a + * string, this will return false and display a warning(). + * + * Caution: Strings are internally stored as char[]. getStringValue uses + * Common::String::operator=(char *) to do the assigment, which uses both + * strlen() AND memmove(). + * + * @param returnValue Pointer to where you want the value stored + * @return Value indicating whether the value assignment was successful + */ + bool getStringValue(Common::String *returnValue) const; + +private: + /** + * Helper method for destruction and assignment. It checks to see + * if the char pointer is being used, and if so calls delete on it + */ + void deleteCharPointer(); +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/utility/utility.cpp b/engines/zvision/utility/utility.cpp new file mode 100644 index 0000000000..d973cb2f4c --- /dev/null +++ b/engines/zvision/utility/utility.cpp @@ -0,0 +1,237 @@ +/* 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 "zvision/utility.h" + +#include "zvision/zvision.h" +#include "zvision/zork_raw.h" + +#include "common/tokenizer.h" +#include "common/file.h" + + +namespace ZVision { + +void writeFileContentsToFile(const Common::String &sourceFile, const Common::String &destFile) { + Common::File f; + if (!f.open(sourceFile)) { + return; + } + + byte* buffer = new byte[f.size()]; + f.read(buffer, f.size()); + + Common::DumpFile dumpFile; + dumpFile.open(destFile); + + dumpFile.write(buffer, f.size()); + dumpFile.flush(); + dumpFile.close(); + + delete[] buffer; +} + +void trimCommentsAndWhiteSpace(Common::String *string) { + for (int i = string->size() - 1; i >= 0; i--) { + if ((*string)[i] == '#') { + string->erase(i); + } + } + + string->trim(); +} + +void tryToDumpLine(const Common::String &key, + Common::String &line, + Common::HashMap *count, + Common::HashMap *fileAlreadyUsed, + Common::DumpFile &output) { + const byte numberOfExamplesPerType = 8; + + if ((*count)[key] < numberOfExamplesPerType && !(*fileAlreadyUsed)[key]) { + output.writeString(line); + output.writeByte('\n'); + (*count)[key]++; + (*fileAlreadyUsed)[key] = true; + } +} + +void dumpEveryResultAction(const Common::String &destFile) { + Common::HashMap count; + Common::HashMap fileAlreadyUsed; + + Common::DumpFile output; + output.open(destFile); + + // Find scr files + Common::ArchiveMemberList list; + SearchMan.listMatchingMembers(list, "*.scr"); + + for (Common::ArchiveMemberList::iterator iter = list.begin(); iter != list.end(); ++iter) { + Common::SeekableReadStream *stream = (*iter)->createReadStream(); + + Common::String line = stream->readLine(); + trimCommentsAndWhiteSpace(&line); + + while (!stream->eos()) { + if (line.matchString("*:add*", true)) { + tryToDumpLine("add", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:animplay*", true)) { + tryToDumpLine("animplay", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:animpreload*", true)) { + tryToDumpLine("animpreload", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:animunload*", true)) { + tryToDumpLine("animunload", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:attenuate*", true)) { + tryToDumpLine("attenuate", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:assign*", true)) { + tryToDumpLine("assign", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:change_location*", true)) { + tryToDumpLine("change_location", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:crossfade*", true) && !fileAlreadyUsed["add"]) { + tryToDumpLine("crossfade", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:debug*", true)) { + tryToDumpLine("debug", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:delay_render*", true)) { + tryToDumpLine("delay_render", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:disable_control*", true)) { + tryToDumpLine("disable_control", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:disable_venus*", true)) { + tryToDumpLine("disable_venus", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:display_message*", true)) { + tryToDumpLine("display_message", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:dissolve*", true)) { + tryToDumpLine("dissolve", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:distort*", true)) { + tryToDumpLine("distort", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:enable_control*", true)) { + tryToDumpLine("enable_control", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:flush_mouse_events*", true)) { + tryToDumpLine("flush_mouse_events", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:inventory*", true)) { + tryToDumpLine("inventory", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:kill*", true)) { + tryToDumpLine("kill", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:menu_bar_enable*", true)) { + tryToDumpLine("menu_bar_enable", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:music*", true)) { + tryToDumpLine("music", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:pan_track*", true)) { + tryToDumpLine("pan_track", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:playpreload*", true)) { + tryToDumpLine("playpreload", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:preferences*", true)) { + tryToDumpLine("preferences", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:quit*", true)) { + tryToDumpLine("quit", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:random*", true)) { + tryToDumpLine("random", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:region*", true)) { + tryToDumpLine("region", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:restore_game*", true)) { + tryToDumpLine("restore_game", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:rotate_to*", true)) { + tryToDumpLine("rotate_to", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:save_game*", true)) { + tryToDumpLine("save_game", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:set_partial_screen*", true)) { + tryToDumpLine("set_partial_screen", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:set_screen*", true)) { + tryToDumpLine("set_screen", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:set_venus*", true)) { + tryToDumpLine("set_venus", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:stop*", true)) { + tryToDumpLine("stop", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:streamvideo*", true)) { + tryToDumpLine("streamvideo", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:syncsound*", true)) { + tryToDumpLine("syncsound", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:timer*", true)) { + tryToDumpLine("timer", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:ttytext*", true)) { + tryToDumpLine("ttytext", line, &count, &fileAlreadyUsed, output); + } else if (line.matchString("*:universe_music*", true)) { + tryToDumpLine("universe_music", line, &count, &fileAlreadyUsed, output); + } + + line = stream->readLine(); + trimCommentsAndWhiteSpace(&line); + } + + for (Common::HashMap::iterator fileUsedIter = fileAlreadyUsed.begin(); fileUsedIter != fileAlreadyUsed.end(); ++fileUsedIter) { + fileUsedIter->_value = false; + } + } + + output.close(); +} + +Common::String getFileName(const Common::String &fullPath) { + Common::StringTokenizer tokenizer(fullPath, "/\\"); + Common::String token; + while (!tokenizer.empty()) { + token = tokenizer.nextToken(); + } + + return token; +} + +void convertRawToWav(const Common::String &inputFile, ZVision *engine, const Common::String &outputFile) { + Common::File file; + if (!file.open(inputFile)) + return; + + Audio::AudioStream *audioStream = makeRawZorkStream(inputFile, engine); + + Common::DumpFile output; + output.open(outputFile); + + output.writeUint32BE(MKTAG('R', 'I', 'F', 'F')); + output.writeUint32LE(file.size() * 2 + 36); + output.writeUint32BE(MKTAG('W', 'A', 'V', 'E')); + output.writeUint32BE(MKTAG('f', 'm', 't', ' ')); + output.writeUint32LE(16); + output.writeUint16LE(1); + uint16 numChannels; + if (audioStream->isStereo()) { + numChannels = 2; + output.writeUint16LE(2); + } else { + numChannels = 1; + output.writeUint16LE(1); + } + output.writeUint32LE(audioStream->getRate()); + output.writeUint32LE(audioStream->getRate() * numChannels * 2); + output.writeUint16LE(numChannels * 2); + output.writeUint16LE(16); + output.writeUint32BE(MKTAG('d', 'a', 't', 'a')); + output.writeUint32LE(file.size() * 2); + int16 *buffer = new int16[file.size()]; + audioStream->readBuffer(buffer, file.size()); + output.write(buffer, file.size() * 2); + + delete[] buffer; +} + +} // End of namespace ZVision diff --git a/engines/zvision/utility/utility.h b/engines/zvision/utility/utility.h new file mode 100644 index 0000000000..fb571f3fe8 --- /dev/null +++ b/engines/zvision/utility/utility.h @@ -0,0 +1,115 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef ZVISION_UTILITY_H +#define ZVISION_UTILITY_H + +#include "common/array.h" + + +namespace Common { +class String; +} + +namespace ZVision { + +class ZVision; + +/** + * Opens the sourceFile utilizing Common::File (aka SearchMan) and writes the + * contents to destFile. destFile is created in the working directory + * + * @param sourceFile The 'file' you want the contents of + * @param destFile The name of the file where the content will be written to + */ +void writeFileContentsToFile(const Common::String &sourceFile, const Common::String &destFile); + +/** + * Removes any line comments using '#' as a sequence start. + * Then removes any trailing and leading 'whitespace' using String::trim() + * Note: String::trim uses isspace() to determine what is whitespace and what is not. + * + * @param string The string to modify. It is modified in place + */ +void trimCommentsAndWhiteSpace(Common::String *string); + +/** + * Searches through all the .scr files and dumps 'numberOfExamplesPerType' examples of each type of ResultAction + * ZVision::initialize() must have been called before this function can be used. + * + * @param destFile Where to write the examples + */ +void dumpEveryResultAction(const Common::String &destFile); + +/** + * Removes all duplicate entries from container. Relative order will be preserved. + * + * @param container The Array to remove duplicate entries from + */ +template +void removeDuplicateEntries(Common::Array &container) { + // Length of modified array + uint newLength = 1; + uint j; + + for(uint i = 1; i < container.size(); i++) { + for(j = 0; j < newLength; j++) { + if (container[i] == container[j]) { + break; + } + } + + // If none of the values in index[0..j] of container are the same as array[i], + // then copy the current value to corresponding new position in array + if (j == newLength) { + container[newLength++] = container[i]; + } + } + + // Actually remove the unneeded space + while (container.size() < newLength) { + container.pop_back(); + } +} + +/** + * Gets the name of the file (including extension). Forward or back slashes + * are interpreted as directory changes + * + * @param fullPath A full or partial path to the file. Ex: folderOne/folderTwo/file.txt + * @return The name of the file without any preceding directories. Ex: file.txt + */ +Common::String getFileName(const Common::String &fullPath); + +/** + * Converts a ZVision .RAW file to a .WAV + * The .WAV will be created in the working directory and will overwrite any existing file + * + * @param inputFile The path to the input .RAW file + * @param outputFile The name of the output .WAV file + */ +void convertRawToWav(const Common::String &inputFile, ZVision *engine, const Common::String &outputFile); + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/video.cpp b/engines/zvision/video.cpp deleted file mode 100644 index 894ae3a055..0000000000 --- a/engines/zvision/video.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* 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 "zvision/zvision.h" - -#include "zvision/clock.h" -#include "zvision/render_manager.h" - -#include "common/system.h" - -#include "video/video_decoder.h" - -#include "engines/util.h" - -#include "graphics/surface.h" - - -namespace ZVision { - -// Taken/modified from SCI -void scaleBuffer(const byte *src, byte *dst, uint32 srcWidth, uint32 srcHeight, byte bytesPerPixel, uint scaleAmount) { - assert(bytesPerPixel == 1 || bytesPerPixel == 2); - - const uint32 newWidth = srcWidth * scaleAmount; - const uint32 pitch = newWidth * bytesPerPixel; - const byte *srcPtr = src; - - if (bytesPerPixel == 1) { - for (uint32 y = 0; y < srcHeight; ++y) { - for (uint32 x = 0; x < srcWidth; ++x) { - const byte color = *srcPtr++; - - for (uint i = 0; i < scaleAmount; ++i) { - dst[i] = color; - dst[pitch + i] = color; - } - dst += scaleAmount; - } - dst += pitch; - } - } else if (bytesPerPixel == 2) { - for (uint32 y = 0; y < srcHeight; ++y) { - for (uint32 x = 0; x < srcWidth; ++x) { - const byte color = *srcPtr++; - const byte color2 = *srcPtr++; - - for (uint i = 0; i < scaleAmount; ++i) { - uint index = i *2; - - dst[index] = color; - dst[index + 1] = color2; - dst[pitch + index] = color; - dst[pitch + index + 1] = color2; - } - dst += 2 * scaleAmount; - } - dst += pitch; - } - } -} - -void ZVision::playVideo(Video::VideoDecoder &videoDecoder, const Common::Rect &destRect, bool skippable) { - byte bytesPerPixel = videoDecoder.getPixelFormat().bytesPerPixel; - - uint16 origWidth = videoDecoder.getWidth(); - uint16 origHeight = videoDecoder.getHeight(); - - uint scale = 1; - // If destRect is empty, no specific scaling was requested. However, we may choose to do scaling anyway - if (destRect.isEmpty()) { - // Most videos are very small. Therefore we do a simple 2x scale - if (origWidth * 2 <= 640 && origHeight * 2 <= 480) { - scale = 2; - } - } else { - // Assume bilinear scaling. AKA calculate the scale from just the width. - // Also assume that the scaling is in integral intervals. AKA no 1.5x scaling - // TODO: Test ^these^ assumptions - scale = destRect.width() / origWidth; - - // TODO: Test if we need to support downscale. - } - - uint16 pitch = origWidth * bytesPerPixel; - - uint16 finalWidth = origWidth * scale; - uint16 finalHeight = origHeight * scale; - - byte *scaledVideoFrameBuffer = 0; - if (scale != 1) { - scaledVideoFrameBuffer = new byte[finalWidth * finalHeight * bytesPerPixel]; - } - - uint16 x = ((WINDOW_WIDTH - finalWidth) / 2) + destRect.left; - uint16 y = ((WINDOW_HEIGHT - finalHeight) / 2) + destRect.top; - - _clock.stop(); - videoDecoder.start(); - - // Only continue while the video is still playing - while (!shouldQuit() && !videoDecoder.endOfVideo() && videoDecoder.isPlaying()) { - // Check for engine quit and video stop key presses - while (!videoDecoder.endOfVideo() && videoDecoder.isPlaying() && _eventMan->pollEvent(_event)) { - switch (_event.type) { - case Common::EVENT_KEYDOWN: - switch (_event.kbd.keycode) { - case Common::KEYCODE_q: - if (_event.kbd.hasFlags(Common::KBD_CTRL)) - quitGame(); - break; - case Common::KEYCODE_SPACE: - if (skippable) { - videoDecoder.stop(); - } - break; - default: - break; - } - default: - break; - } - } - - if (videoDecoder.needsUpdate()) { - const Graphics::Surface *frame = videoDecoder.decodeNextFrame(); - - if (frame) { - if (scale != 1) { - scaleBuffer((const byte *)frame->getPixels(), scaledVideoFrameBuffer, origWidth, origHeight, bytesPerPixel, scale); - _system->copyRectToScreen(scaledVideoFrameBuffer, pitch * 2, x, y, finalWidth, finalHeight); - } else { - _system->copyRectToScreen((const byte *)frame->getPixels(), pitch, x, y, finalWidth, finalHeight); - } - } - } - - // Always update the screen so the mouse continues to render - _system->updateScreen(); - - _system->delayMillis(videoDecoder.getTimeToNextFrame()); - } - - _clock.start(); - - if (scale != 1) { - delete[] scaledVideoFrameBuffer; - } -} - -} // End of namespace ZVision diff --git a/engines/zvision/video/video.cpp b/engines/zvision/video/video.cpp new file mode 100644 index 0000000000..894ae3a055 --- /dev/null +++ b/engines/zvision/video/video.cpp @@ -0,0 +1,171 @@ +/* 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 "zvision/zvision.h" + +#include "zvision/clock.h" +#include "zvision/render_manager.h" + +#include "common/system.h" + +#include "video/video_decoder.h" + +#include "engines/util.h" + +#include "graphics/surface.h" + + +namespace ZVision { + +// Taken/modified from SCI +void scaleBuffer(const byte *src, byte *dst, uint32 srcWidth, uint32 srcHeight, byte bytesPerPixel, uint scaleAmount) { + assert(bytesPerPixel == 1 || bytesPerPixel == 2); + + const uint32 newWidth = srcWidth * scaleAmount; + const uint32 pitch = newWidth * bytesPerPixel; + const byte *srcPtr = src; + + if (bytesPerPixel == 1) { + for (uint32 y = 0; y < srcHeight; ++y) { + for (uint32 x = 0; x < srcWidth; ++x) { + const byte color = *srcPtr++; + + for (uint i = 0; i < scaleAmount; ++i) { + dst[i] = color; + dst[pitch + i] = color; + } + dst += scaleAmount; + } + dst += pitch; + } + } else if (bytesPerPixel == 2) { + for (uint32 y = 0; y < srcHeight; ++y) { + for (uint32 x = 0; x < srcWidth; ++x) { + const byte color = *srcPtr++; + const byte color2 = *srcPtr++; + + for (uint i = 0; i < scaleAmount; ++i) { + uint index = i *2; + + dst[index] = color; + dst[index + 1] = color2; + dst[pitch + index] = color; + dst[pitch + index + 1] = color2; + } + dst += 2 * scaleAmount; + } + dst += pitch; + } + } +} + +void ZVision::playVideo(Video::VideoDecoder &videoDecoder, const Common::Rect &destRect, bool skippable) { + byte bytesPerPixel = videoDecoder.getPixelFormat().bytesPerPixel; + + uint16 origWidth = videoDecoder.getWidth(); + uint16 origHeight = videoDecoder.getHeight(); + + uint scale = 1; + // If destRect is empty, no specific scaling was requested. However, we may choose to do scaling anyway + if (destRect.isEmpty()) { + // Most videos are very small. Therefore we do a simple 2x scale + if (origWidth * 2 <= 640 && origHeight * 2 <= 480) { + scale = 2; + } + } else { + // Assume bilinear scaling. AKA calculate the scale from just the width. + // Also assume that the scaling is in integral intervals. AKA no 1.5x scaling + // TODO: Test ^these^ assumptions + scale = destRect.width() / origWidth; + + // TODO: Test if we need to support downscale. + } + + uint16 pitch = origWidth * bytesPerPixel; + + uint16 finalWidth = origWidth * scale; + uint16 finalHeight = origHeight * scale; + + byte *scaledVideoFrameBuffer = 0; + if (scale != 1) { + scaledVideoFrameBuffer = new byte[finalWidth * finalHeight * bytesPerPixel]; + } + + uint16 x = ((WINDOW_WIDTH - finalWidth) / 2) + destRect.left; + uint16 y = ((WINDOW_HEIGHT - finalHeight) / 2) + destRect.top; + + _clock.stop(); + videoDecoder.start(); + + // Only continue while the video is still playing + while (!shouldQuit() && !videoDecoder.endOfVideo() && videoDecoder.isPlaying()) { + // Check for engine quit and video stop key presses + while (!videoDecoder.endOfVideo() && videoDecoder.isPlaying() && _eventMan->pollEvent(_event)) { + switch (_event.type) { + case Common::EVENT_KEYDOWN: + switch (_event.kbd.keycode) { + case Common::KEYCODE_q: + if (_event.kbd.hasFlags(Common::KBD_CTRL)) + quitGame(); + break; + case Common::KEYCODE_SPACE: + if (skippable) { + videoDecoder.stop(); + } + break; + default: + break; + } + default: + break; + } + } + + if (videoDecoder.needsUpdate()) { + const Graphics::Surface *frame = videoDecoder.decodeNextFrame(); + + if (frame) { + if (scale != 1) { + scaleBuffer((const byte *)frame->getPixels(), scaledVideoFrameBuffer, origWidth, origHeight, bytesPerPixel, scale); + _system->copyRectToScreen(scaledVideoFrameBuffer, pitch * 2, x, y, finalWidth, finalHeight); + } else { + _system->copyRectToScreen((const byte *)frame->getPixels(), pitch, x, y, finalWidth, finalHeight); + } + } + } + + // Always update the screen so the mouse continues to render + _system->updateScreen(); + + _system->delayMillis(videoDecoder.getTimeToNextFrame()); + } + + _clock.start(); + + if (scale != 1) { + delete[] scaledVideoFrameBuffer; + } +} + +} // End of namespace ZVision diff --git a/engines/zvision/video/zork_avi_decoder.cpp b/engines/zvision/video/zork_avi_decoder.cpp new file mode 100644 index 0000000000..a614f77bb7 --- /dev/null +++ b/engines/zvision/video/zork_avi_decoder.cpp @@ -0,0 +1,53 @@ +/* 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 "zvision/zork_avi_decoder.h" + +#include "zvision/zork_raw.h" + +#include "common/stream.h" + +#include "audio/audiostream.h" + + +namespace ZVision { + +Video::AVIDecoder::AVIAudioTrack *ZorkAVIDecoder::createAudioTrack(Video::AVIDecoder::AVIStreamHeader sHeader, Video::AVIDecoder::PCMWaveFormat wvInfo) { + ZorkAVIDecoder::ZorkAVIAudioTrack *audioTrack = new ZorkAVIDecoder::ZorkAVIAudioTrack(sHeader, wvInfo, _soundType); + return (Video::AVIDecoder::AVIAudioTrack *)audioTrack; +} + +void ZorkAVIDecoder::ZorkAVIAudioTrack::queueSound(Common::SeekableReadStream *stream) { + if (_audStream) { + if (_wvInfo.tag == kWaveFormatZorkPCM) { + assert(_wvInfo.size == 8); + _audStream->queueAudioStream(makeRawZorkStream(stream, _wvInfo.samplesPerSec, _audStream->isStereo(), DisposeAfterUse::YES), DisposeAfterUse::YES); + } + } else { + delete stream; + } +} + +} // End of namespace ZVision diff --git a/engines/zvision/video/zork_avi_decoder.h b/engines/zvision/video/zork_avi_decoder.h new file mode 100644 index 0000000000..ec2be1bb13 --- /dev/null +++ b/engines/zvision/video/zork_avi_decoder.h @@ -0,0 +1,60 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef ZORK_AVI_DECODER_H +#define ZORK_AVI_DECODER_H + +#include "video/avi_decoder.h" + + +namespace ZVision { + +class ZorkAVIDecoder : public Video::AVIDecoder { +public: + ZorkAVIDecoder(Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType) : + Video::AVIDecoder(soundType) {} + + virtual ~ZorkAVIDecoder() {} + +private: + class ZorkAVIAudioTrack : public Video::AVIDecoder::AVIAudioTrack { + public: + ZorkAVIAudioTrack(const AVIStreamHeader &streamHeader, const PCMWaveFormat &waveFormat, Audio::Mixer::SoundType soundType) : + Video::AVIDecoder::AVIAudioTrack(streamHeader, waveFormat, soundType) {} + virtual ~ZorkAVIAudioTrack() {} + + void queueSound(Common::SeekableReadStream *stream); + }; + + Video::AVIDecoder::AVIAudioTrack *createAudioTrack(Video::AVIDecoder::AVIStreamHeader sHeader, Video::AVIDecoder::PCMWaveFormat wvInfo); + +private: + // Audio Codecs + enum { + kWaveFormatZorkPCM = 17 // special Zork PCM audio format (clashes with MS IMA ADPCM) + }; +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/zfs_archive.cpp b/engines/zvision/zfs_archive.cpp deleted file mode 100644 index 24cff27fc4..0000000000 --- a/engines/zvision/zfs_archive.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* 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 "zvision/zfs_archive.h" - -#include "common/memstream.h" -#include "common/debug.h" -#include "common/file.h" - -namespace ZVision { - -ZfsArchive::ZfsArchive(const Common::String &fileName) : _fileName(fileName) { - Common::File zfsFile; - - if (!zfsFile.open(_fileName)) { - warning("ZFSArchive::ZFSArchive(): Could not find the archive file"); - return; - } - - readHeaders(&zfsFile); - - debug(1, "ZfsArchive::ZfsArchive(%s): Located %d files", _fileName.c_str(), _entryHeaders.size()); -} - -ZfsArchive::ZfsArchive(const Common::String &fileName, Common::SeekableReadStream *stream) : _fileName(fileName) { - readHeaders(stream); - - debug(1, "ZfsArchive::ZfsArchive(%s): Located %d files", _fileName.c_str(), _entryHeaders.size()); -} - -ZfsArchive::~ZfsArchive() { - debug(1, "ZfsArchive Destructor Called"); - ZfsEntryHeaderMap::iterator it = _entryHeaders.begin(); - for ( ; it != _entryHeaders.end(); ++it) { - delete it->_value; - } -} - -void ZfsArchive::readHeaders(Common::SeekableReadStream *stream) { - // Don't do a straight struct cast since we can't guarantee endianness - _header.magic = stream->readUint32LE(); - _header.unknown1 = stream->readUint32LE(); - _header.maxNameLength = stream->readUint32LE(); - _header.filesPerBlock = stream->readUint32LE(); - _header.fileCount = stream->readUint32LE(); - _header.xorKey[0] = stream->readByte(); - _header.xorKey[1] = stream->readByte(); - _header.xorKey[2] = stream->readByte(); - _header.xorKey[3] = stream->readByte(); - _header.fileSectionOffset = stream->readUint32LE(); - - uint32 nextOffset; - - do { - // Read the offset to the next block - nextOffset = stream->readUint32LE(); - - // Read in each entry header - for (uint32 i = 0; i < _header.filesPerBlock; ++i) { - ZfsEntryHeader entryHeader; - - entryHeader.name = readEntryName(stream); - entryHeader.offset = stream->readUint32LE(); - entryHeader.id = stream->readUint32LE(); - entryHeader.size = stream->readUint32LE(); - entryHeader.time = stream->readUint32LE(); - entryHeader.unknown = stream->readUint32LE(); - - if (entryHeader.size != 0) - _entryHeaders[entryHeader.name] = new ZfsEntryHeader(entryHeader); - } - - // Seek to the next block of headers - stream->seek(nextOffset); - } while (nextOffset != 0); -} - -Common::String ZfsArchive::readEntryName(Common::SeekableReadStream *stream) const { - // Entry Names are at most 16 bytes and are null padded - char buffer[16]; - stream->read(buffer, 16); - - return Common::String(buffer); -} - -bool ZfsArchive::hasFile(const Common::String &name) const { - return _entryHeaders.contains(name); -} - -int ZfsArchive::listMembers(Common::ArchiveMemberList &list) const { - int matches = 0; - - for (ZfsEntryHeaderMap::const_iterator it = _entryHeaders.begin(); it != _entryHeaders.end(); ++it) { - list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(it->_value->name, this))); - matches++; - } - - return matches; -} - -const Common::ArchiveMemberPtr ZfsArchive::getMember(const Common::String &name) const { - if (!_entryHeaders.contains(name)) - return Common::ArchiveMemberPtr(); - - return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this)); -} - -Common::SeekableReadStream *ZfsArchive::createReadStreamForMember(const Common::String &name) const { - if (!_entryHeaders.contains(name)) { - return 0; - } - - ZfsEntryHeader *entryHeader = _entryHeaders[name]; - - Common::File zfsArchive; - zfsArchive.open(_fileName); - zfsArchive.seek(entryHeader->offset); - - // This *HAS* to be malloc (not new[]) because MemoryReadStream uses free() to free the memory - byte* buffer = (byte *)malloc(entryHeader->size); - zfsArchive.read(buffer, entryHeader->size); - // Decrypt the data in place - if (_header.xorKey != 0) - unXor(buffer, entryHeader->size, _header.xorKey); - - return new Common::MemoryReadStream(buffer, entryHeader->size, DisposeAfterUse::YES); -} - -void ZfsArchive::unXor(byte *buffer, uint32 length, const byte *xorKey) const { - for (uint32 i = 0; i < length; ++i) - buffer[i] ^= xorKey[i % 4]; -} - -} // End of namespace ZVision - - diff --git a/engines/zvision/zfs_archive.h b/engines/zvision/zfs_archive.h deleted file mode 100644 index d7b45e4b47..0000000000 --- a/engines/zvision/zfs_archive.h +++ /dev/null @@ -1,126 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef ZVISION_ZFS_ARCHIVE_H -#define ZVISION_ZFS_ARCHIVE_H - -#include "common/archive.h" -#include "common/hashmap.h" -#include "common/hash-str.h" - - -namespace Common { -class String; -} - -namespace ZVision { - -struct ZfsHeader { - uint32 magic; - uint32 unknown1; - uint32 maxNameLength; - uint32 filesPerBlock; - uint32 fileCount; - byte xorKey[4]; - uint32 fileSectionOffset; -}; - -struct ZfsEntryHeader { - Common::String name; - uint32 offset; - uint32 id; - uint32 size; - uint32 time; - uint32 unknown; -}; - -typedef Common::HashMap ZfsEntryHeaderMap; - -class ZfsArchive : public Common::Archive { -public: - ZfsArchive(const Common::String &fileName); - ZfsArchive(const Common::String &fileName, Common::SeekableReadStream *stream); - ~ZfsArchive(); - - /** - * Check if a member with the given name is present in the Archive. - * Patterns are not allowed, as this is meant to be a quick File::exists() - * replacement. - */ - bool hasFile(const Common::String &fileName) const; - - /** - * Add all members of the Archive to list. - * Must only append to list, and not remove elements from it. - * - * @return The number of names added to list - */ - int listMembers(Common::ArchiveMemberList &list) const; - - /** - * Returns a ArchiveMember representation of the given file. - */ - const Common::ArchiveMemberPtr getMember(const Common::String &name) const; - - /** - * Create a stream bound to a member with the specified name in the - * archive. If no member with this name exists, 0 is returned. - * - * @return The newly created input stream - */ - Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const; - -private: - const Common::String _fileName; - ZfsHeader _header; - ZfsEntryHeaderMap _entryHeaders; - - /** - * Parses the zfs file into file entry headers that can be used later - * to get the entry data. - * - * @param stream The contents of the zfs file - */ - void readHeaders(Common::SeekableReadStream *stream); - - /** - * Entry names are contained within a 16 byte block. This reads the block - * and converts it the name to a Common::String - * - * @param stream The zfs file stream - * @return The entry file name - */ - Common::String readEntryName(Common::SeekableReadStream *stream) const; - - /** - * ZFS file entries can be encrypted using XOR encoding. This method - * decodes the buffer in place using the supplied xorKey. - * - * @param buffer The data to decode - * @param length Length of buffer - */ - void unXor(byte *buffer, uint32 length, const byte *xorKey) const; -}; - -} // End of namespace ZVision - -#endif diff --git a/engines/zvision/zork_avi_decoder.cpp b/engines/zvision/zork_avi_decoder.cpp deleted file mode 100644 index a614f77bb7..0000000000 --- a/engines/zvision/zork_avi_decoder.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* 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 "zvision/zork_avi_decoder.h" - -#include "zvision/zork_raw.h" - -#include "common/stream.h" - -#include "audio/audiostream.h" - - -namespace ZVision { - -Video::AVIDecoder::AVIAudioTrack *ZorkAVIDecoder::createAudioTrack(Video::AVIDecoder::AVIStreamHeader sHeader, Video::AVIDecoder::PCMWaveFormat wvInfo) { - ZorkAVIDecoder::ZorkAVIAudioTrack *audioTrack = new ZorkAVIDecoder::ZorkAVIAudioTrack(sHeader, wvInfo, _soundType); - return (Video::AVIDecoder::AVIAudioTrack *)audioTrack; -} - -void ZorkAVIDecoder::ZorkAVIAudioTrack::queueSound(Common::SeekableReadStream *stream) { - if (_audStream) { - if (_wvInfo.tag == kWaveFormatZorkPCM) { - assert(_wvInfo.size == 8); - _audStream->queueAudioStream(makeRawZorkStream(stream, _wvInfo.samplesPerSec, _audStream->isStereo(), DisposeAfterUse::YES), DisposeAfterUse::YES); - } - } else { - delete stream; - } -} - -} // End of namespace ZVision diff --git a/engines/zvision/zork_avi_decoder.h b/engines/zvision/zork_avi_decoder.h deleted file mode 100644 index ec2be1bb13..0000000000 --- a/engines/zvision/zork_avi_decoder.h +++ /dev/null @@ -1,60 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * - */ - -#ifndef ZORK_AVI_DECODER_H -#define ZORK_AVI_DECODER_H - -#include "video/avi_decoder.h" - - -namespace ZVision { - -class ZorkAVIDecoder : public Video::AVIDecoder { -public: - ZorkAVIDecoder(Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType) : - Video::AVIDecoder(soundType) {} - - virtual ~ZorkAVIDecoder() {} - -private: - class ZorkAVIAudioTrack : public Video::AVIDecoder::AVIAudioTrack { - public: - ZorkAVIAudioTrack(const AVIStreamHeader &streamHeader, const PCMWaveFormat &waveFormat, Audio::Mixer::SoundType soundType) : - Video::AVIDecoder::AVIAudioTrack(streamHeader, waveFormat, soundType) {} - virtual ~ZorkAVIAudioTrack() {} - - void queueSound(Common::SeekableReadStream *stream); - }; - - Video::AVIDecoder::AVIAudioTrack *createAudioTrack(Video::AVIDecoder::AVIStreamHeader sHeader, Video::AVIDecoder::PCMWaveFormat wvInfo); - -private: - // Audio Codecs - enum { - kWaveFormatZorkPCM = 17 // special Zork PCM audio format (clashes with MS IMA ADPCM) - }; -}; - -} // End of namespace ZVision - -#endif diff --git a/engines/zvision/zork_raw.cpp b/engines/zvision/zork_raw.cpp deleted file mode 100644 index e64feca900..0000000000 --- a/engines/zvision/zork_raw.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/* 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 "zvision/zork_raw.h" - -#include "zvision/zvision.h" -#include "zvision/detection.h" -#include "zvision/utility.h" - -#include "common/file.h" -#include "common/str.h" -#include "common/stream.h" -#include "common/memstream.h" -#include "common/bufferedstream.h" -#include "common/util.h" - -#include "audio/audiostream.h" -#include "audio/decoders/raw.h" - - -namespace ZVision { - -const int16 RawZorkStream::_stepAdjustmentTable[8] = {-1, -1, -1, 1, 4, 7, 10, 12}; - -const int32 RawZorkStream::_amplitudeLookupTable[89] = {0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, - 0x0010, 0x0011, 0x0013, 0x0015, 0x0017, 0x0019, 0x001C, 0x001F, - 0x0022, 0x0025, 0x0029, 0x002D, 0x0032, 0x0037, 0x003C, 0x0042, - 0x0049, 0x0050, 0x0058, 0x0061, 0x006B, 0x0076, 0x0082, 0x008F, - 0x009D, 0x00AD, 0x00BE, 0x00D1, 0x00E6, 0x00FD, 0x0117, 0x0133, - 0x0151, 0x0173, 0x0198, 0x01C1, 0x01EE, 0x0220, 0x0256, 0x0292, - 0x02D4, 0x031C, 0x036C, 0x03C3, 0x0424, 0x048E, 0x0502, 0x0583, - 0x0610, 0x06AB, 0x0756, 0x0812, 0x08E0, 0x09C3, 0x0ABD, 0x0BD0, - 0x0CFF, 0x0E4C, 0x0FBA, 0x114C, 0x1307, 0x14EE, 0x1706, 0x1954, - 0x1BDC, 0x1EA5, 0x21B6, 0x2515, 0x28CA, 0x2CDF, 0x315B, 0x364B, - 0x3BB9, 0x41B2, 0x4844, 0x4F7E, 0x5771, 0x602F, 0x69CE, 0x7462, 0x7FFF}; - -const SoundParams RawZorkStream::_zNemSoundParamLookupTable[6] = {{'6', 0x2B11, false, false}, - {'a', 0x5622, false, true}, - {'b', 0x5622, true, true}, - {'n', 0x2B11, false, true}, - {'s', 0x5622, false, true}, - {'t', 0x5622, true, true} -}; - -const SoundParams RawZorkStream::_zgiSoundParamLookupTable[5] = {{'a',0x5622, false, false}, - {'k',0x2B11, true, true}, - {'p',0x5622, false, true}, - {'q',0x5622, true, true}, - {'u',0xAC44, true, true} -}; - -RawZorkStream::RawZorkStream(uint32 rate, bool stereo, DisposeAfterUse::Flag disposeStream, Common::SeekableReadStream *stream) - : _rate(rate), - _stereo(0), - _stream(stream, disposeStream), - _endOfData(false) { - if (stereo) - _stereo = 1; - - _lastSample[0].index = 0; - _lastSample[0].sample = 0; - _lastSample[1].index = 0; - _lastSample[1].sample = 0; - - // Calculate the total playtime of the stream - if (stereo) - _playtime = Audio::Timestamp(0, _stream->size() / 2, rate); - else - _playtime = Audio::Timestamp(0, _stream->size(), rate); -} - -int RawZorkStream::readBuffer(int16 *buffer, const int numSamples) { - int bytesRead = 0; - - // 0: Left, 1: Right - uint channel = 0; - - while (bytesRead < numSamples) { - byte encodedSample = _stream->readByte(); - if (_stream->eos()) { - _endOfData = true; - return bytesRead; - } - bytesRead++; - - int16 index = _lastSample[channel].index; - uint32 lookUpSample = _amplitudeLookupTable[index]; - - int32 sample = 0; - - if (encodedSample & 0x40) - sample += lookUpSample; - if (encodedSample & 0x20) - sample += lookUpSample >> 1; - if (encodedSample & 0x10) - sample += lookUpSample >> 2; - if (encodedSample & 8) - sample += lookUpSample >> 3; - if (encodedSample & 4) - sample += lookUpSample >> 4; - if (encodedSample & 2) - sample += lookUpSample >> 5; - if (encodedSample & 1) - sample += lookUpSample >> 6; - if (encodedSample & 0x80) - sample = -sample; - - sample += _lastSample[channel].sample; - sample = CLIP(sample, -32768, 32767); - - buffer[bytesRead - 1] = (int16)sample; - - index += _stepAdjustmentTable[(encodedSample >> 4) & 7]; - index = CLIP(index, 0, 88); - - _lastSample[channel].sample = sample; - _lastSample[channel].index = index; - - // Increment and wrap the channel - channel = (channel + 1) & _stereo; - } - - return bytesRead; -} - -bool RawZorkStream::rewind() { - _stream->seek(0, 0); - _stream->clearErr(); - _endOfData = false; - _lastSample[0].index = 0; - _lastSample[0].sample = 0; - _lastSample[1].index = 0; - _lastSample[1].sample = 0; - - return true; -} - -Audio::RewindableAudioStream *makeRawZorkStream(Common::SeekableReadStream *stream, - int rate, - bool stereo, - DisposeAfterUse::Flag disposeAfterUse) { - if (stereo) - assert(stream->size() % 2 == 0); - - return new RawZorkStream(rate, stereo, disposeAfterUse, stream); -} - -Audio::RewindableAudioStream *makeRawZorkStream(const byte *buffer, uint32 size, - int rate, - bool stereo, - DisposeAfterUse::Flag disposeAfterUse) { - return makeRawZorkStream(new Common::MemoryReadStream(buffer, size, disposeAfterUse), rate, stereo, DisposeAfterUse::YES); -} - -Audio::RewindableAudioStream *makeRawZorkStream(const Common::String &filePath, ZVision *engine) { - Common::File *file = new Common::File(); - assert(file->open(filePath)); - - Common::String fileName = getFileName(filePath); - fileName.toLowercase(); - - SoundParams soundParams = { ' ', 0, false, false }; - bool foundParams = false; - char fileIdentifier = (engine->getGameId() == GID_NEMESIS) ? fileName[6] : fileName[7]; - - if (engine->getGameId() == GID_NEMESIS) { - for (int i = 0; i < 6; ++i) { - if (RawZorkStream::_zNemSoundParamLookupTable[i].identifier == fileIdentifier) { - soundParams = RawZorkStream::_zNemSoundParamLookupTable[i]; - foundParams = true; - } - } - } else if (engine->getGameId() == GID_GRANDINQUISITOR) { - for (int i = 0; i < 6; ++i) { - if (RawZorkStream::_zgiSoundParamLookupTable[i].identifier == fileIdentifier) { - soundParams = RawZorkStream::_zgiSoundParamLookupTable[i]; - foundParams = true; - } - } - } - - if (!foundParams) - error("Unable to find sound params for file '%s'. File identifier is '%c'", filePath.c_str(), fileIdentifier); - - if (soundParams.packed) { - return makeRawZorkStream(wrapBufferedSeekableReadStream(file, 2048, DisposeAfterUse::YES), soundParams.rate, soundParams.stereo, DisposeAfterUse::YES); - } else { - byte flags = 0; - if (soundParams.stereo) - flags |= Audio::FLAG_STEREO; - - return Audio::makeRawStream(file, soundParams.rate, flags, DisposeAfterUse::YES); - } -} - -} // End of namespace ZVision diff --git a/engines/zvision/zork_raw.h b/engines/zvision/zork_raw.h deleted file mode 100644 index 481ea79f2d..0000000000 --- a/engines/zvision/zork_raw.h +++ /dev/null @@ -1,120 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef ZVISION_ZORK_RAW_H -#define ZVISION_ZORK_RAW_H - -#include "audio/audiostream.h" - - -namespace Common { -class SeekableReadStream; -} - -namespace ZVision { - -class ZVision; - -struct SoundParams { - char identifier; - uint32 rate; - bool stereo; - bool packed; -}; - -/** - * This is a stream, which allows for playing raw ADPCM data from a stream. - */ -class RawZorkStream : public Audio::RewindableAudioStream { -public: - RawZorkStream(uint32 rate, bool stereo, DisposeAfterUse::Flag disposeStream, Common::SeekableReadStream *stream); - - ~RawZorkStream() { - } - -public: - static const SoundParams _zNemSoundParamLookupTable[6]; - static const SoundParams _zgiSoundParamLookupTable[5]; - -private: - const int _rate; // Sample rate of stream - Audio::Timestamp _playtime; // Calculated total play time - Common::DisposablePtr _stream; // Stream to read data from - bool _endOfData; // Whether the stream end has been reached - uint _stereo; - - /** - * Holds the frequency and index from the last sample - * 0 holds the left channel, 1 holds the right channel - */ - struct { - int32 sample; - int16 index; - } _lastSample[2]; - - static const int16 _stepAdjustmentTable[8]; - static const int32 _amplitudeLookupTable[89]; - -public: - int readBuffer(int16 *buffer, const int numSamples); - - bool isStereo() const { return true; } - bool endOfData() const { return _endOfData; } - - int getRate() const { return _rate; } - Audio::Timestamp getLength() const { return _playtime; } - - bool rewind(); -}; - -/** - * Creates an audio stream, which plays from the given buffer. - * - * @param buffer Buffer to play from. - * @param size Size of the buffer in bytes. - * @param rate Rate of the sound data. - * @param dispose AfterUse Whether to free the buffer after use (with free!). - * @return The new SeekableAudioStream (or 0 on failure). - */ -Audio::RewindableAudioStream *makeRawZorkStream(const byte *buffer, uint32 size, - int rate, - bool stereo, - DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); - -/** - * Creates an audio stream, which plays from the given stream. - * - * @param stream Stream object to play from. - * @param rate Rate of the sound data. - * @param dispose AfterUse Whether to delete the stream after use. - * @return The new SeekableAudioStream (or 0 on failure). - */ -Audio::RewindableAudioStream *makeRawZorkStream(Common::SeekableReadStream *stream, - int rate, - bool stereo, - DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); - -Audio::RewindableAudioStream *makeRawZorkStream(const Common::String &filePath, ZVision *engine); - -} // End of namespace ZVision - -#endif -- cgit v1.2.3