diff options
305 files changed, 12775 insertions, 11266 deletions
diff --git a/.gitignore b/.gitignore index 6e3e61134c..074a92a397 100644 --- a/.gitignore +++ b/.gitignore @@ -62,21 +62,21 @@ lib*.a /dists/rpl.exe +/dists/codeblocks/*.cbp +/dists/codeblocks/*.depend +/dists/codeblocks/*.layout +/dists/codeblocks/scummvm* + /dists/iphone/build /dists/iphone/scummvm.xcodeproj/*.mode1v3 /dists/iphone/scummvm.xcodeproj/*.pbxuser -/dists/msvc8/[Dd]ebug*/ -/dists/msvc8/[Rr]elease*/ -/dists/msvc8/*.lib - -/dists/msvc9/[Dd]ebug*/ -/dists/msvc9/[Rr]elease*/ -/dists/msvc9/*.lib - -/dists/msvc10/[Dd]ebug*/ -/dists/msvc10/[Rr]elease*/ -/dists/msvc10/*.lib +/dists/msvc*/[Dd]ebug*/ +/dists/msvc*/[Rr]elease*/ +/dists/msvc*/*.lib +/dists/msvc*/*.SAV +/dists/msvc*/*.dat +/dists/msvc*/*.dll /doc/*.aux /doc/*.dvi @@ -187,6 +187,7 @@ ScummVM Team Backend Teams ------------- Android: + Andre Heider Angus Lees Dreamcast: @@ -265,6 +266,18 @@ ScummVM Team -------------- Fredrik Wendel - (retired) + Website (maintenance) + --------------------- + James Brown - IRC Logs maintainer + Thierry Crozat - Wiki maintainer + Andre Heider - Buildbot maintainer + Max Horn - Forum, IRC channel and Mailing list maintainer + Joost Peters - Doxygen Project Documentation maintainer + Jordi Vilalta Prat - Wiki maintainer + Eugene Sandulenko - Forum, IRC channel, Screen Shots and Mailing + list maintainer + John Willis + Website (content) ----------------- All active team members @@ -396,6 +409,7 @@ Other contributions David Jensen - SVG logo conversion Jean Marc Gimenez - ScummVM logo Raina - ScummVM forum buttons + William Claydon - Skins for doxygen and wiki Code contributions ------------------ @@ -405,7 +419,7 @@ Other contributions engine) Paolo Costabel - PSP port contributions Martin Doucha - CinE engine objectification - Thomas Fach-Pedersen - ProTracker module player + Thomas Fach-Pedersen - ProTracker module player, Smacker video decoder Tobias Gunkel - Sound support for C64 version of MM/Zak, Loom PCE support Janne Huttunen - V3 actor mask support, Dig/FT SMUSH audio @@ -1,5 +1,6 @@ -For a more comprehensive changelog for the latest experimental SVN code, see: - http://scummvm.svn.sourceforge.net/viewvc/scummvm/?view=log +For a more comprehensive changelog of the latest experimental code, see: + https://github.com/scummvm/scummvm/commits/ + 1.3.0 (????-??-??) New Games: - Added support for Backyard Baseball. @@ -24,14 +25,14 @@ For a more comprehensive changelog for the latest experimental SVN code, see: Cine: - Corrected memory leaks and invalid memory accesses. Future Wars should be more stable. - - Operation Stealth is now completable, though significant - graphical glitches remain so not official supported. + - Made Operation Stealth completable, though significant graphical + glitches remain so not official supported. Drascula: - Added German and French subtitles in the Von Braun cutscene (#3069981: no subtitles in scene with "von Braun"). - Improved French translation of the game. - - Return To Launcher now supported. + - Added support for "Return To Launcher". Gob: - Fixed "Goblin Stuck On Reload" bugs affecting Gobliiins. @@ -41,7 +42,7 @@ For a more comprehensive changelog for the latest experimental SVN code, see: Parallaction: - Corrected issue which could cause crash at engine exit. - - Leak fixes in Nippon Safes Amiga. + - Closed memory leaks in Nippon Safes Amiga. SCI: - Added a CMS music driver for SCI1 - SCI1.1 games. @@ -78,14 +79,15 @@ For a more comprehensive changelog for the latest experimental SVN code, see: - Improved support for FM-TOWNS versions of games. Sky: - - Fixed crashes on sequences for several backends - (e.g. OpenGL, including Android) + - Fixed crashes on sequences for several ports (Android, OpenGL, ...) Teenagent: - Closed memory leaks. Tinsel: - Closed memory leaks in Coroutines. + - Added enhanced music support for the German CD "Neon Edition" re-release + of Discworld 1. Touche: - Corrected memory leaks and minor issues. @@ -94,6 +96,14 @@ For a more comprehensive changelog for the latest experimental SVN code, see: - Added support for OpenGL. (GSoC Task) - Closed memory leaks in Mouse Surfaces. + Android port: + - Switched to the official NDK toolchain for building. + - Fixed GFX output for various devices. + - Fixed various crashes. + + Nintendo DS port: + - Added support for loadable modules. + PSP port: - Added support for loadable modules. - Added image viewer. @@ -101,22 +111,14 @@ For a more comprehensive changelog for the latest experimental SVN code, see: PS2 port: - Added support for loadable modules. - Nintendo DS port: - - Added support for loadable modules. - Wii/GameCube port: - Added support for loadable modules. - Fixed 16bit mouse cursors on HE games. - Android port: - - Switched to the official NDK toolchain for building. - - Fixed GFX output for various devices. - - Fixed various crashes. - 1.2.1 (2010-12-19) General - - Add Hungarian translation. - - Add Brazilian Portuguese translation. + - Added Hungarian translation. + - Added Brazilian Portuguese translation. Cruise: - Fixed a problem with Raoul appearing when examining the Book. diff --git a/audio/mididrv.h b/audio/mididrv.h index 9e649cba3d..eed8c419f3 100644 --- a/audio/mididrv.h +++ b/audio/mididrv.h @@ -71,9 +71,14 @@ enum MusicType { * A set of flags to be passed to detectDevice() which can be used to * specify what kind of music driver is preferred / accepted. * - * The flags (except for MDT_PREFER_MT32 and MDT_PREFER_GM) indicate whether a given driver - * type is acceptable. E.g. the TOWNS music driver could be returned by - * detectDevice if and only if MDT_TOWNS is specified. + * The flags (except for MDT_PREFER_MT32 and MDT_PREFER_GM) indicate whether a + * given driver type is acceptable. E.g. the TOWNS music driver could be + * returned by detectDevice if and only if MDT_TOWNS is specified. + * + * MDT_PREFER_MT32 and MDT_PREFER_GM indicate the MIDI device type to use when + * no device is selected in the music options, or when the MIDI device selected + * does not match the requirements of a game engine. With these flags, more + * priority is given to an MT-32 device, or a GM device respectively. * * @todo Rename MidiDriverFlags to MusicDriverFlags */ diff --git a/audio/mixer.cpp b/audio/mixer.cpp index c2271b1059..dc0287e3fb 100644 --- a/audio/mixer.cpp +++ b/audio/mixer.cpp @@ -54,8 +54,9 @@ public: * @param len number of sample *pairs*. So a value of * 10 means that the buffer contains twice 10 sample, each * 16 bits, for a total of 40 bytes. + * @return number of sample pairs processed (which can still be silence!) */ - void mix(int16 *data, uint len); + int mix(int16 *data, uint len); /** * Queries whether the channel is still playing or not. @@ -257,7 +258,7 @@ void MixerImpl::playStream( insertChannel(handle, chan); } -void MixerImpl::mixCallback(byte *samples, uint len) { +int MixerImpl::mixCallback(byte *samples, uint len) { assert(samples); Common::StackLock lock(_mutex); @@ -272,14 +273,21 @@ void MixerImpl::mixCallback(byte *samples, uint len) { memset(buf, 0, 2 * len * sizeof(int16)); // mix all channels + int res = 0, tmp; for (int i = 0; i != NUM_CHANNELS; i++) if (_channels[i]) { if (_channels[i]->isFinished()) { delete _channels[i]; _channels[i] = 0; - } else if (!_channels[i]->isPaused()) - _channels[i]->mix(buf, len); + } else if (!_channels[i]->isPaused()) { + tmp = _channels[i]->mix(buf, len); + + if (tmp > res) + res = tmp; + } } + + return res; } void MixerImpl::stopAll() { @@ -538,19 +546,23 @@ Timestamp Channel::getElapsedTime() { return ts; } -void Channel::mix(int16 *data, uint len) { +int Channel::mix(int16 *data, uint len) { assert(_stream); + int res = 0; + if (_stream->endOfData()) { // TODO: call drain method } else { assert(_converter); - _samplesConsumed = _samplesDecoded; _mixerTimeStamp = g_system->getMillis(); _pauseTime = 0; - _samplesDecoded += _converter->flow(*_stream, data, len, _volL, _volR); + res = _converter->flow(*_stream, data, len, _volL, _volR); + _samplesDecoded += res; } + + return res; } } // End of namespace Audio diff --git a/audio/mixer_intern.h b/audio/mixer_intern.h index c8df9a594d..dd2746e9ea 100644 --- a/audio/mixer_intern.h +++ b/audio/mixer_intern.h @@ -118,8 +118,10 @@ public: * The mixer callback function, to be called at regular intervals by * the backend (e.g. from an audio mixing thread). All the actual mixing * work is done from here. + * + * @return number of sample pairs processed (which can still be silence!) */ - void mixCallback(byte *samples, uint len); + int mixCallback(byte *samples, uint len); /** * Set the internal 'is ready' flag of the mixer. diff --git a/audio/rate_arm.cpp b/audio/rate_arm.cpp index 41944ef698..43172f64c8 100644 --- a/audio/rate_arm.cpp +++ b/audio/rate_arm.cpp @@ -60,7 +60,7 @@ namespace Audio { * ARM routine we call doesn't respect those definitions. */ #define FRAC_BITS 16 -#define FRAC_ONE (1<<FRAC_BITS) +#define FRAC_ONE (1 << FRAC_BITS) /** * The size of the intermediate input cache. Bigger values may increase @@ -138,34 +138,33 @@ extern "C" { } extern "C" st_sample_t *ARM_SimpleRate_M( - AudioStream &input, - int (*fn)(Audio::AudioStream&,int16*,int), - SimpleRateDetails *sr, - st_sample_t *obuf, - st_size_t osamp, - st_volume_t vol_l, - st_volume_t vol_r); + AudioStream &input, + int (*fn)(Audio::AudioStream&,int16*,int), + SimpleRateDetails *sr, + st_sample_t *obuf, + st_size_t osamp, + st_volume_t vol_l, + st_volume_t vol_r); extern "C" st_sample_t *ARM_SimpleRate_S( - AudioStream &input, - int (*fn)(Audio::AudioStream&,int16*,int), - SimpleRateDetails *sr, - st_sample_t *obuf, - st_size_t osamp, - st_volume_t vol_l, - st_volume_t vol_r); + AudioStream &input, + int (*fn)(Audio::AudioStream&,int16*,int), + SimpleRateDetails *sr, + st_sample_t *obuf, + st_size_t osamp, + st_volume_t vol_l, + st_volume_t vol_r); extern "C" st_sample_t *ARM_SimpleRate_R( - AudioStream &input, - int (*fn)(Audio::AudioStream&,int16*,int), - SimpleRateDetails *sr, - st_sample_t *obuf, - st_size_t osamp, - st_volume_t vol_l, - st_volume_t vol_r); - -extern "C" int SimpleRate_readFudge(Audio::AudioStream &input, - int16 *a, int b) + AudioStream &input, + int (*fn)(Audio::AudioStream&,int16*,int), + SimpleRateDetails *sr, + st_sample_t *obuf, + st_size_t osamp, + st_volume_t vol_l, + st_volume_t vol_r); + +extern "C" int SimpleRate_readFudge(Audio::AudioStream &input, int16 *a, int b) { #ifdef DEBUG_RATECONV debug("Reading ptr=%x n%d", a, b); @@ -183,21 +182,22 @@ int SimpleRateConverter<stereo, reverseStereo>::flow(AudioStream &input, st_samp if (!stereo) { obuf = ARM_SimpleRate_M(input, - &SimpleRate_readFudge, - &sr, - obuf, osamp, vol_l, vol_r); + &SimpleRate_readFudge, + &sr, + obuf, osamp, vol_l, vol_r); } else if (reverseStereo) { obuf = ARM_SimpleRate_R(input, - &SimpleRate_readFudge, - &sr, - obuf, osamp, vol_l, vol_r); + &SimpleRate_readFudge, + &sr, + obuf, osamp, vol_l, vol_r); } else { obuf = ARM_SimpleRate_S(input, - &SimpleRate_readFudge, - &sr, - obuf, osamp, vol_l, vol_r); + &SimpleRate_readFudge, + &sr, + obuf, osamp, vol_l, vol_r); } - return (obuf-ostart)/2; + + return (obuf - ostart) / 2; } /** @@ -240,31 +240,31 @@ extern "C" { } extern "C" st_sample_t *ARM_LinearRate_M( - AudioStream &input, - int (*fn)(Audio::AudioStream&,int16*,int), - LinearRateDetails *lr, - st_sample_t *obuf, - st_size_t osamp, - st_volume_t vol_l, - st_volume_t vol_r); + AudioStream &input, + int (*fn)(Audio::AudioStream&,int16*,int), + LinearRateDetails *lr, + st_sample_t *obuf, + st_size_t osamp, + st_volume_t vol_l, + st_volume_t vol_r); extern "C" st_sample_t *ARM_LinearRate_S( - AudioStream &input, - int (*fn)(Audio::AudioStream&,int16*,int), - LinearRateDetails *lr, - st_sample_t *obuf, - st_size_t osamp, - st_volume_t vol_l, - st_volume_t vol_r); + AudioStream &input, + int (*fn)(Audio::AudioStream&,int16*,int), + LinearRateDetails *lr, + st_sample_t *obuf, + st_size_t osamp, + st_volume_t vol_l, + st_volume_t vol_r); extern "C" st_sample_t *ARM_LinearRate_R( - AudioStream &input, - int (*fn)(Audio::AudioStream&,int16*,int), - LinearRateDetails *lr, - st_sample_t *obuf, - st_size_t osamp, - st_volume_t vol_l, - st_volume_t vol_r); + AudioStream &input, + int (*fn)(Audio::AudioStream&,int16*,int), + LinearRateDetails *lr, + st_sample_t *obuf, + st_size_t osamp, + st_volume_t vol_l, + st_volume_t vol_r); template<bool stereo, bool reverseStereo> class LinearRateConverter : public RateConverter { @@ -320,23 +320,29 @@ int LinearRateConverter<stereo, reverseStereo>::flow(AudioStream &input, st_samp #endif st_sample_t *ostart = obuf; + if (vol_l > 0xff) + vol_l = 0xff; + + if (vol_r > 0xff) + vol_r = 0xff; + if (!stereo) { obuf = ARM_LinearRate_M(input, - &SimpleRate_readFudge, - &lr, - obuf, osamp, vol_l, vol_r); + &SimpleRate_readFudge, + &lr, + obuf, osamp, vol_l, vol_r); } else if (reverseStereo) { obuf = ARM_LinearRate_R(input, - &SimpleRate_readFudge, - &lr, - obuf, osamp, vol_l, vol_r); + &SimpleRate_readFudge, + &lr, + obuf, osamp, vol_l, vol_r); } else { obuf = ARM_LinearRate_S(input, - &SimpleRate_readFudge, - &lr, - obuf, osamp, vol_l, vol_r); + &SimpleRate_readFudge, + &lr, + obuf, osamp, vol_l, vol_r); } - return (obuf-ostart)/2; + return (obuf - ostart) / 2; } @@ -355,31 +361,32 @@ extern "C" { } extern "C" st_sample_t *ARM_CopyRate_M( - st_size_t len, - st_sample_t *obuf, - st_volume_t vol_l, - st_volume_t vol_r, - st_sample_t *_buffer); + st_size_t len, + st_sample_t *obuf, + st_volume_t vol_l, + st_volume_t vol_r, + st_sample_t *_buffer); extern "C" st_sample_t *ARM_CopyRate_S( - st_size_t len, - st_sample_t *obuf, - st_volume_t vol_l, - st_volume_t vol_r, - st_sample_t *_buffer); + st_size_t len, + st_sample_t *obuf, + st_volume_t vol_l, + st_volume_t vol_r, + st_sample_t *_buffer); extern "C" st_sample_t *ARM_CopyRate_R( - st_size_t len, - st_sample_t *obuf, - st_volume_t vol_l, - st_volume_t vol_r, - st_sample_t *_buffer); + st_size_t len, + st_sample_t *obuf, + st_volume_t vol_l, + st_volume_t vol_r, + st_sample_t *_buffer); template<bool stereo, bool reverseStereo> class CopyRateConverter : public RateConverter { st_sample_t *_buffer; st_size_t _bufferSize; + public: CopyRateConverter() : _buffer(0), _bufferSize(0) {} ~CopyRateConverter() { @@ -393,7 +400,7 @@ public: debug("Copy st=%d rev=%d", stereo, reverseStereo); #endif st_size_t len; - st_sample_t *ostart = obuf; + st_sample_t *ostart = obuf; if (stereo) osamp *= 2; @@ -418,8 +425,9 @@ public: else obuf = ARM_CopyRate_M(len, obuf, vol_l, vol_r, _buffer); - return (obuf-ostart)/2; + return (obuf - ostart) / 2; } + virtual int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) { return (ST_SUCCESS); } @@ -463,3 +471,4 @@ RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate, bool stere } } // End of namespace Audio + diff --git a/backends/base-backend.cpp b/backends/base-backend.cpp index 42ab7b887a..f349cc8005 100644 --- a/backends/base-backend.cpp +++ b/backends/base-backend.cpp @@ -101,7 +101,4 @@ AudioCDManager *BaseBackend::getAudioCDManager() { } void BaseBackend::resetGraphicsScale() { - // As a hack, we use 0 here. Backends should override this method - // and provide their own. - setGraphicsMode(0); } diff --git a/backends/events/wincesdl/wincesdl-events.cpp b/backends/events/wincesdl/wincesdl-events.cpp new file mode 100644 index 0000000000..2505b0fb31 --- /dev/null +++ b/backends/events/wincesdl/wincesdl-events.cpp @@ -0,0 +1,332 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/scummsys.h" + +#ifdef _WIN32_WCE + +#include "common/config-manager.h" + +#include "backends/events/wincesdl/wincesdl-events.h" +#include "backends/platform/wince/CEActionsPocket.h" +#include "backends/platform/wince/CEActionsSmartphone.h" +#include "backends/platform/wince/CEDevice.h" + +#include "backends/platform/sdl/sdl.h" + +WINCESdlEventSource::WINCESdlEventSource() + : _tapTime(0), _closeClick(false), _rbutton(false), + _freeLook(false), _graphicsMan(0) { +} + +void WINCESdlEventSource::init(WINCESdlGraphicsManager *graphicsMan) { + assert(graphicsMan); + _graphicsMan = graphicsMan; +} + +void WINCESdlEventSource::fillMouseEvent(Common::Event &event, int x, int y) { + event.mouse.x = x; + event.mouse.y = y; + + // Update the "keyboard mouse" coords + _km.x = event.mouse.x; + _km.y = event.mouse.y; + + // Adjust for the screen scaling + if (_graphicsMan->_zoomDown) + event.mouse.y += 240; + + event.mouse.x = event.mouse.x * _graphicsMan->_scaleFactorXd / _graphicsMan->_scaleFactorXm; + event.mouse.y = event.mouse.y * _graphicsMan->_scaleFactorYd / _graphicsMan->_scaleFactorYm; +} + +bool WINCESdlEventSource::pollEvent(Common::Event &event) { + SDL_Event ev; + ev.type = SDL_NOEVENT; + DWORD currentTime; + bool keyEvent = false; + int deltaX, deltaY; + + memset(&event, 0, sizeof(Common::Event)); + + handleKbdMouse(); + + // If the screen changed, send an Common::EVENT_SCREEN_CHANGED + int screenID = _graphicsMan->getScreenChangeID(); + if (screenID != _lastScreenID) { + _lastScreenID = screenID; + event.type = Common::EVENT_SCREEN_CHANGED; + return true; + } + + CEDevice::wakeUp(); + + currentTime = GetTickCount(); + + while (SDL_PollEvent(&ev)) { + switch (ev.type) { + case SDL_KEYDOWN: + debug(1, "Key down %X %s", ev.key.keysym.sym, SDL_GetKeyName((SDLKey)ev.key.keysym.sym)); + // KMOD_RESERVED is used if the key has been injected by an external buffer + if (ev.key.keysym.mod != KMOD_RESERVED && !GUI::Actions::Instance()->mappingActive()) { + keyEvent = true; + _graphicsMan->_lastKeyPressed = ev.key.keysym.sym; + _graphicsMan->_keyRepeatTime = currentTime; + _graphicsMan->_keyRepeat = 0; + + if (!GUI_Actions::Instance()->mappingActive() && GUI_Actions::Instance()->performMapped(ev.key.keysym.sym, true)) + return true; + } + + if (GUI_Actions::Instance()->mappingActive()) + event.kbd.flags = 0xFF; + else if (ev.key.keysym.sym == SDLK_PAUSE) { + _graphicsMan->_lastKeyPressed = 0; + event.type = Common::EVENT_PREDICTIVE_DIALOG; + return true; + } + event.type = Common::EVENT_KEYDOWN; + if (!GUI::Actions::Instance()->mappingActive()) + event.kbd.keycode = (Common::KeyCode)ev.key.keysym.sym; + else + event.kbd.keycode = (Common::KeyCode)mapKeyCE(ev.key.keysym.sym, ev.key.keysym.mod, ev.key.keysym.unicode, GUI::Actions::Instance()->mappingActive()); + event.kbd.ascii = mapKeyCE(ev.key.keysym.sym, ev.key.keysym.mod, ev.key.keysym.unicode, GUI::Actions::Instance()->mappingActive()); + + if (ev.key.keysym.mod == KMOD_RESERVED && ev.key.keysym.unicode == KMOD_SHIFT) { + event.kbd.ascii ^= 0x20; + event.kbd.flags = Common::KBD_SHIFT; + } + + return true; + + case SDL_KEYUP: + debug(1, "Key up %X %s", ev.key.keysym.sym, SDL_GetKeyName((SDLKey)ev.key.keysym.sym)); + // KMOD_RESERVED is used if the key has been injected by an external buffer + if (ev.key.keysym.mod != KMOD_RESERVED && !GUI::Actions::Instance()->mappingActive()) { + keyEvent = true; + _graphicsMan->_lastKeyPressed = 0; + + if (!GUI_Actions::Instance()->mappingActive() && GUI_Actions::Instance()->performMapped(ev.key.keysym.sym, false)) + return true; + } + + if (GUI_Actions::Instance()->mappingActive()) + event.kbd.flags = 0xFF; + else if (ev.key.keysym.sym == SDLK_PAUSE) { + _graphicsMan->_lastKeyPressed = 0; + return false; // chew up the show agi dialog key up event + } + + event.type = Common::EVENT_KEYUP; + if (!GUI::Actions::Instance()->mappingActive()) + event.kbd.keycode = (Common::KeyCode)ev.key.keysym.sym; + else + event.kbd.keycode = (Common::KeyCode)mapKeyCE(ev.key.keysym.sym, ev.key.keysym.mod, ev.key.keysym.unicode, GUI::Actions::Instance()->mappingActive()); + event.kbd.ascii = mapKeyCE(ev.key.keysym.sym, ev.key.keysym.mod, ev.key.keysym.unicode, GUI::Actions::Instance()->mappingActive()); + + if (ev.key.keysym.mod == KMOD_RESERVED && ev.key.keysym.unicode == KMOD_SHIFT) { + event.kbd.ascii ^= 0x20; + event.kbd.flags = Common::KBD_SHIFT; + } + + return true; + + case SDL_MOUSEMOTION: + event.type = Common::EVENT_MOUSEMOVE; + fillMouseEvent(event, ev.motion.x, ev.motion.y); + _graphicsMan->setMousePos(event.mouse.x, event.mouse.y); + + return true; + + case SDL_MOUSEBUTTONDOWN: + if (ev.button.button == SDL_BUTTON_LEFT) + event.type = Common::EVENT_LBUTTONDOWN; + else if (ev.button.button == SDL_BUTTON_RIGHT) + event.type = Common::EVENT_RBUTTONDOWN; + else + break; + fillMouseEvent(event, ev.button.x, ev.button.y); + + + if (event.mouse.x > _tapX) + deltaX = event.mouse.x - _tapX; + else + deltaX = _tapX - event.mouse.x; + if (event.mouse.y > _tapY) + deltaY = event.mouse.y - _tapY; + else + deltaY = _tapY - event.mouse.y; + _closeClick = (deltaX <= 5 && deltaY <= 5); + + if (!_isSmartphone) { + // handle double-taps + if (_tapTime) { // second tap + if (_closeClick && (GetTickCount() - _tapTime < 1000)) { + if (event.mouse.y <= 20 && + _graphicsMan->_panelInitialized) { + // top of screen (show panel) + _graphicsMan->swap_panel_visibility(); + } else if (!_graphicsMan->_noDoubleTapRMB) { + // right click + event.type = Common::EVENT_RBUTTONDOWN; + _rbutton = true; + } + } + _tapTime = 0; + } else { + _tapTime = GetTickCount(); + _tapX = event.mouse.x; + _tapY = event.mouse.y; + } + } + + if (_freeLook && !_closeClick) { + _rbutton = false; + _tapTime = 0; + _tapX = event.mouse.x; + _tapY = event.mouse.y; + event.type = Common::EVENT_MOUSEMOVE; + _graphicsMan->setMousePos(event.mouse.x, event.mouse.y); + } + + + if (_graphicsMan->_toolbarHandler.action(event.mouse.x, event.mouse.y, true)) { + if (!_graphicsMan->_toolbarHandler.drawn()) { + _graphicsMan->_toolbarHighDrawn = false; + _graphicsMan->internUpdateScreen(); + } + if (_graphicsMan->_newOrientation != _graphicsMan->_orientationLandscape) { + _graphicsMan->_orientationLandscape = _graphicsMan->_newOrientation; + _graphicsMan->_toolbarHighDrawn = false; + ConfMan.setInt("landscape", _graphicsMan->_orientationLandscape); + ConfMan.flushToDisk(); + _graphicsMan->hotswapGFXMode(); + } + return false; + } + + return true; + + case SDL_MOUSEBUTTONUP: + if (ev.button.button == SDL_BUTTON_LEFT) + event.type = Common::EVENT_LBUTTONUP; + else if (ev.button.button == SDL_BUTTON_RIGHT) + event.type = Common::EVENT_RBUTTONUP; + else + break; + + if (_rbutton) { + event.type = Common::EVENT_RBUTTONUP; + _rbutton = false; + } + + fillMouseEvent(event, ev.button.x, ev.button.y); + + if (_freeLook && !_closeClick) { + _tapX = event.mouse.x; + _tapY = event.mouse.y; + event.type = Common::EVENT_MOUSEMOVE; + _graphicsMan->setMousePos(event.mouse.x, event.mouse.y); + } + + if (_graphicsMan->_toolbarHandler.action(event.mouse.x, event.mouse.y, false)) { + if (!_graphicsMan->_toolbarHandler.drawn()) { + _graphicsMan->_toolbarHighDrawn = false; + _graphicsMan->internUpdateScreen(); + } + return false; + + } + return true; + + case SDL_VIDEOEXPOSE: + // HACK: Send a fake event, handled by SdlGraphicsManager + event.type = (Common::EventType)OSystem_SDL::kSdlEventExpose; + break; + + case SDL_QUIT: + event.type = Common::EVENT_QUIT; + return true; + + case SDL_ACTIVEEVENT: + if (ev.active.state & SDL_APPMOUSEFOCUS) + debug(2, "%s mouse focus.", ev.active.gain ? "Got" : "Lost"); + if (ev.active.state & SDL_APPINPUTFOCUS) + debug(2, "%s input focus.", ev.active.gain ? "Got" : "Lost"); + if (ev.active.state & SDL_APPACTIVE) + debug(2, "%s total focus.", ev.active.gain ? "Got" : "Lost"); + if (ev.active.state & SDL_APPINPUTFOCUS) { + _graphicsMan->_hasfocus = ev.active.gain; + SDL_PauseAudio(!_graphicsMan->_hasfocus); + if (_graphicsMan->_hasfocus) { + event.type = (Common::EventType)OSystem_SDL::kSdlEventExpose; + } + } + break; + } + } + + // Simulate repeated key for backend + if (!keyEvent && _graphicsMan->_lastKeyPressed && (int)currentTime > _graphicsMan->_keyRepeatTime + _graphicsMan->_keyRepeatTrigger) { + _graphicsMan->_keyRepeatTime = currentTime; + _graphicsMan->_keyRepeat++; + GUI_Actions::Instance()->performMapped(_graphicsMan->_lastKeyPressed, true); + } + + return false; +} + +int WINCESdlEventSource::mapKeyCE(SDLKey key, SDLMod mod, Uint16 unicode, bool unfilter) { + if (GUI::Actions::Instance()->mappingActive()) + return key; + + if (unfilter) { + switch (key) { + case SDLK_ESCAPE: + return SDLK_BACKSPACE; + case SDLK_F8: + return SDLK_ASTERISK; + case SDLK_F9: + return SDLK_HASH; + default: + return key; + } + } + + if (key >= SDLK_KP0 && key <= SDLK_KP9) { + return key - SDLK_KP0 + '0'; + } else if (key >= SDLK_UP && key <= SDLK_PAGEDOWN) { + return key; + } else if (key >= SDLK_NUMLOCK && key <= SDLK_EURO) { + return 0; + } + return key; +} + +void WINCESdlEventSource::swap_freeLook() { + _freeLook = !_freeLook; +} + +#endif /* _WIN32_WCE */ diff --git a/backends/events/wincesdl/wincesdl-events.h b/backends/events/wincesdl/wincesdl-events.h new file mode 100644 index 0000000000..f5b1026c46 --- /dev/null +++ b/backends/events/wincesdl/wincesdl-events.h @@ -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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef BACKENDS_EVENTS_SDL_WINCE_H +#define BACKENDS_EVENTS_SDL_WINCE_H + +#include "common/scummsys.h" + +#ifdef _WIN32_WCE + +#include "backends/events/sdl/sdl-events.h" +#include "backends/graphics/wincesdl/wincesdl-graphics.h" + +extern bool _isSmartphone; + +class WINCESdlEventSource : public SdlEventSource { +public: + WINCESdlEventSource(); + + void init(WINCESdlGraphicsManager *graphicsMan); + + void loadDeviceConfiguration(); + + // Overloaded from SDL backend (toolbar handling) + bool pollEvent(Common::Event &event); + // Overloaded from SDL backend (mouse and new scaler handling) + void fillMouseEvent(Common::Event &event, int x, int y); + + void swap_freeLook(); + +protected: + +private: + int mapKeyCE(SDLKey key, SDLMod mod, Uint16 unicode, bool unfilter); + + WINCESdlGraphicsManager *_graphicsMan; + + // Keyboard tap + int _tapX; + int _tapY; + long _tapTime; + + bool _closeClick; // flag when taps are spatially close together + bool _rbutton; // double tap -> right button simulation + bool _freeLook; // freeLook mode (do not send mouse button events) + +}; + +#endif + +#endif /* BACKENDS_EVENTS_SDL_WINCE_H */ diff --git a/backends/graphics/opengl/gltexture.cpp b/backends/graphics/opengl/gltexture.cpp index cd9e23cb71..7b7d40f174 100644 --- a/backends/graphics/opengl/gltexture.cpp +++ b/backends/graphics/opengl/gltexture.cpp @@ -149,7 +149,7 @@ void GLTexture::updateBuffer(const void *buf, int pitch, GLuint x, GLuint y, GLu glBindTexture(GL_TEXTURE_2D, _textureName); CHECK_GL_ERROR(); // Check if the buffer has its data contiguously - if (static_cast<int>(w) * _bytesPerPixel == pitch && w == _textureWidth) { + if (static_cast<int>(w) * _bytesPerPixel == pitch) { glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, _glFormat, _glType, buf); CHECK_GL_ERROR(); } else { diff --git a/backends/graphics/opengl/opengl-graphics.cpp b/backends/graphics/opengl/opengl-graphics.cpp index beac2f6d3e..9a2efe3eec 100644 --- a/backends/graphics/opengl/opengl-graphics.cpp +++ b/backends/graphics/opengl/opengl-graphics.cpp @@ -136,6 +136,8 @@ int OpenGLGraphicsManager::getDefaultGraphicsMode() const { bool OpenGLGraphicsManager::setGraphicsMode(int mode) { assert(_transactionMode == kTransactionActive); + setScale(2); + if (_oldVideoMode.setup && _oldVideoMode.mode == mode) return true; @@ -166,11 +168,9 @@ void OpenGLGraphicsManager::resetGraphicsScale() { } #ifdef USE_RGB_COLOR - Graphics::PixelFormat OpenGLGraphicsManager::getScreenFormat() const { return _screenFormat; } - #endif void OpenGLGraphicsManager::initSize(uint width, uint height, const Graphics::PixelFormat *format) { @@ -308,7 +308,7 @@ int16 OpenGLGraphicsManager::getWidth() { void OpenGLGraphicsManager::setPalette(const byte *colors, uint start, uint num) { assert(colors); - + #ifdef USE_RGB_COLOR assert(_screenFormat.bytesPerPixel == 1); #endif @@ -324,7 +324,7 @@ void OpenGLGraphicsManager::setPalette(const byte *colors, uint start, uint num) void OpenGLGraphicsManager::grabPalette(byte *colors, uint start, uint num) { assert(colors); - + #ifdef USE_RGB_COLOR assert(_screenFormat.bytesPerPixel == 1); #endif @@ -341,9 +341,9 @@ void OpenGLGraphicsManager::copyRectToScreen(const byte *buf, int pitch, int x, // Copy buffer data to game screen internal buffer const byte *src = buf; - byte *dst = (byte *)_screenData.pixels + y * _screenData.pitch; + byte *dst = (byte *)_screenData.pixels + y * _screenData.pitch + x * _screenData.bytesPerPixel; for (int i = 0; i < h; i++) { - memcpy(dst + x * _screenData.bytesPerPixel, src, w * _screenData.bytesPerPixel); + memcpy(dst, src, w * _screenData.bytesPerPixel); src += pitch; dst += _screenData.pitch; } @@ -367,6 +367,7 @@ void OpenGLGraphicsManager::fillScreen(uint32 col) { if (_gameTexture == NULL) return; +#ifdef USE_RGB_COLOR if (_screenFormat.bytesPerPixel == 1) { memset(_screenData.pixels, col, _screenData.h * _screenData.pitch); } else if (_screenFormat.bytesPerPixel == 2) { @@ -392,7 +393,9 @@ void OpenGLGraphicsManager::fillScreen(uint32 col) { pixels[i] = col; } } - +#else + memset(_screenData.pixels, col, _screenData.h * _screenData.pitch); +#endif _screenNeedsRedraw = true; } @@ -558,7 +561,8 @@ void OpenGLGraphicsManager::setMouseCursor(const byte *buf, uint w, uint h, int #endif // Allocate space for cursor data - if (_cursorData.w != w || _cursorData.h != h) + if (_cursorData.w != w || _cursorData.h != h || + _cursorData.bytesPerPixel != _cursorFormat.bytesPerPixel) _cursorData.create(w, h, _cursorFormat.bytesPerPixel); // Save cursor data @@ -754,11 +758,17 @@ void OpenGLGraphicsManager::refreshOverlay() { void OpenGLGraphicsManager::refreshCursor() { _cursorNeedsRedraw = false; - if (_cursorFormat.bytesPerPixel == 1) { - // Create a temporary RGBA8888 surface - byte *surface = new byte[_cursorState.w * _cursorState.h * 4]; - memset(surface, 0, _cursorState.w * _cursorState.h * 4); + // Allocate a texture big enough for cursor + _cursorTexture->allocBuffer(_cursorState.w, _cursorState.h); + // Create a temporary RGBA8888 surface + byte *surface = new byte[_cursorState.w * _cursorState.h * 4]; + memset(surface, 0, _cursorState.w * _cursorState.h * 4); + + byte *dst = surface; + + // Convert the paletted cursor to RGBA8888 + if (_cursorFormat.bytesPerPixel == 1) { // Select palette byte *palette; if (_cursorPaletteDisabled) @@ -768,7 +778,6 @@ void OpenGLGraphicsManager::refreshCursor() { // Convert the paletted cursor to RGBA8888 const byte *src = (byte *)_cursorData.pixels; - byte *dst = surface; for (int i = 0; i < _cursorState.w * _cursorState.h; i++) { // Check for keycolor if (src[i] != _cursorKeyColor) { @@ -779,16 +788,42 @@ void OpenGLGraphicsManager::refreshCursor() { } dst += 4; } + } else { + const bool gotNoAlpha = (_cursorFormat.aLoss == 8); + + // Convert the RGB cursor to RGBA8888 + if (_cursorFormat.bytesPerPixel == 2) { + const uint16 *src = (uint16 *)_cursorData.pixels; + for (int i = 0; i < _cursorState.w * _cursorState.h; i++) { + // Check for keycolor + if (src[i] != _cursorKeyColor) { + _cursorFormat.colorToARGB(src[i], dst[3], dst[0], dst[1], dst[2]); + + if (gotNoAlpha) + dst[3] = 255; + } + dst += 4; + } + } else if (_cursorFormat.bytesPerPixel == 4) { + const uint32 *src = (uint32 *)_cursorData.pixels; + for (int i = 0; i < _cursorState.w * _cursorState.h; i++) { + // Check for keycolor + if (src[i] != _cursorKeyColor) { + _cursorFormat.colorToARGB(src[i], dst[3], dst[0], dst[1], dst[2]); + + if (gotNoAlpha) + dst[3] = 255; + } + dst += 4; + } + } + } - // Allocate a texture big enough for cursor - _cursorTexture->allocBuffer(_cursorState.w, _cursorState.h); - - // Update the texture with new cursor - _cursorTexture->updateBuffer(surface, _cursorState.w * 4, 0, 0, _cursorState.w, _cursorState.h); + // Update the texture with new cursor + _cursorTexture->updateBuffer(surface, _cursorState.w * 4, 0, 0, _cursorState.w, _cursorState.h); - // Free the temp surface - delete[] surface; - } + // Free the temp surface + delete[] surface; } void OpenGLGraphicsManager::refreshCursorScale() { @@ -811,7 +846,8 @@ void OpenGLGraphicsManager::refreshCursorScale() { } else { // Otherwise, scale the cursor for the overlay int targetScaleFactor = MIN(_cursorTargetScale, _videoMode.scaleFactor); - int actualFactor = screenScaleFactor - (targetScaleFactor - 1) * 10000; + // We limit the maximum scale to 3 here to avoid too big cursors, for large overlay resolutions + int actualFactor = MIN<uint>(3, screenScaleFactor - (targetScaleFactor - 1)) * 10000; _cursorState.rW = (int16)(_cursorState.w * actualFactor / 10000); _cursorState.rH = (int16)(_cursorState.h * actualFactor / 10000); _cursorState.rHotX = (int16)(_cursorState.hotX * actualFactor / 10000); @@ -873,6 +909,11 @@ void OpenGLGraphicsManager::getGLPixelFormat(Graphics::PixelFormat pixelFormat, intFormat = GL_RGBA; glFormat = GL_RGBA; gltype = GL_UNSIGNED_SHORT_5_5_5_1; + } else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)) { // RGB555 + bpp = 2; + intFormat = GL_RGB; + glFormat = GL_BGRA; + gltype = GL_UNSIGNED_SHORT_1_5_5_5_REV; } else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0)) { // RGBA4444 bpp = 2; intFormat = GL_RGBA; @@ -963,7 +1004,7 @@ void OpenGLGraphicsManager::internUpdateScreen() { refreshOverlay(); // Draw the overlay - _overlayTexture->drawTexture(_displayX, _displayY, _displayWidth, _displayHeight); + _overlayTexture->drawTexture(0, 0, _videoMode.overlayWidth, _videoMode.overlayHeight); } if (_cursorVisible) { @@ -1059,10 +1100,14 @@ void OpenGLGraphicsManager::initGL() { void OpenGLGraphicsManager::loadTextures() { #ifdef USE_RGB_COLOR - if (_transactionDetails.formatChanged && _gameTexture) + if (_transactionDetails.formatChanged && _gameTexture) { delete _gameTexture; + _gameTexture = 0; + } #endif + uint gameScreenBPP = 0; + if (!_gameTexture) { byte bpp; GLenum intformat; @@ -1073,6 +1118,7 @@ void OpenGLGraphicsManager::loadTextures() { #else getGLPixelFormat(Graphics::PixelFormat::createFormatCLUT8(), bpp, intformat, format, type); #endif + gameScreenBPP = bpp; _gameTexture = new GLTexture(bpp, intformat, format, type); } else _gameTexture->refresh(); @@ -1093,7 +1139,7 @@ void OpenGLGraphicsManager::loadTextures() { _cursorTexture = new GLTexture(4, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE); else _cursorTexture->refresh(); - + GLint filter = _videoMode.antialiasing ? GL_LINEAR : GL_NEAREST; _gameTexture->setFilter(filter); _overlayTexture->setFilter(filter); @@ -1104,21 +1150,38 @@ void OpenGLGraphicsManager::loadTextures() { _overlayTexture->allocBuffer(_videoMode.overlayWidth, _videoMode.overlayHeight); _cursorTexture->allocBuffer(_cursorState.w, _cursorState.h); - if (_transactionDetails.formatChanged || + if ( +#ifdef USE_RGB_COLOR + _transactionDetails.formatChanged || +#endif _oldVideoMode.screenWidth != _videoMode.screenWidth || _oldVideoMode.screenHeight != _videoMode.screenHeight) _screenData.create(_videoMode.screenWidth, _videoMode.screenHeight, - _screenFormat.bytesPerPixel); +#ifdef USE_RGB_COLOR + _screenFormat.bytesPerPixel +#else + 1 +#endif + ); + if (_oldVideoMode.overlayWidth != _videoMode.overlayWidth || _oldVideoMode.overlayHeight != _videoMode.overlayHeight) _overlayData.create(_videoMode.overlayWidth, _videoMode.overlayHeight, _overlayFormat.bytesPerPixel); - + _screenNeedsRedraw = true; _overlayNeedsRedraw = true; _cursorNeedsRedraw = true; + // We need to setup a proper unpack alignment value here, else we will + // get problems with the texture updates, in case the surface data is + // not properly aligned. + // For now we use the gcd of the game screen format and 2, since 2 is + // the BPP value for the overlay and the OSD. + if (gameScreenBPP) + glPixelStorei(GL_UNPACK_ALIGNMENT, Common::gcd<uint>(gameScreenBPP, 2)); + #ifdef USE_OSD if (!_osdTexture) _osdTexture = new GLTexture(2, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1); @@ -1166,32 +1229,20 @@ uint OpenGLGraphicsManager::getAspectRatio() { } void OpenGLGraphicsManager::adjustMousePosition(int16 &x, int16 &y) { - if (_videoMode.mode == OpenGL::GFX_NORMAL) { - if (_videoMode.hardwareWidth != _videoMode.overlayWidth) - x = x * _videoMode.overlayWidth / _videoMode.hardwareWidth; - if (_videoMode.hardwareHeight != _videoMode.overlayHeight) - y = y * _videoMode.overlayHeight / _videoMode.hardwareHeight; - - if (!_overlayVisible) { - x /= _videoMode.scaleFactor; - y /= _videoMode.scaleFactor; - } + if (_overlayVisible) + return; - } else { + if (_videoMode.mode == OpenGL::GFX_NORMAL) { + x /= _videoMode.scaleFactor; + y /= _videoMode.scaleFactor; + } else if (!_overlayVisible) { x -= _displayX; y -= _displayY; - if (_overlayVisible) { - if (_displayWidth != _videoMode.overlayWidth) - x = x * _videoMode.overlayWidth / _displayWidth; - if (_displayHeight != _videoMode.overlayHeight) - y = y * _videoMode.overlayHeight / _displayHeight; - } else { - if (_displayWidth != _videoMode.screenWidth) - x = x * _videoMode.screenWidth / _displayWidth; - if (_displayHeight != _videoMode.screenHeight) - y = y * _videoMode.screenHeight / _displayHeight; - } + if (_displayWidth != _videoMode.screenWidth) + x = x * _videoMode.screenWidth / _displayWidth; + if (_displayHeight != _videoMode.screenHeight) + y = y * _videoMode.screenHeight / _displayHeight; } } diff --git a/backends/graphics/openglsdl/openglsdl-graphics.cpp b/backends/graphics/openglsdl/openglsdl-graphics.cpp index c7ce0aa7de..cbc152a4a3 100644 --- a/backends/graphics/openglsdl/openglsdl-graphics.cpp +++ b/backends/graphics/openglsdl/openglsdl-graphics.cpp @@ -121,6 +121,7 @@ void OpenGLSdlGraphicsManager::detectSupportedFormats() { #endif Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0), // RGB565 Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0), // RGB5551 + Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0), // RGB555 Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0), // RGBA4444 #ifndef USE_GLES Graphics::PixelFormat(2, 4, 4, 4, 4, 8, 4, 0, 12) // ARGB4444 @@ -344,13 +345,10 @@ bool OpenGLSdlGraphicsManager::loadGFXMode() { if (_aspectRatioCorrection) _videoMode.mode = OpenGL::GFX_4_3; - _videoMode.overlayWidth = _videoMode.screenWidth * _videoMode.scaleFactor; - _videoMode.overlayHeight = _videoMode.screenHeight * _videoMode.scaleFactor; - // If the screen was resized, do not change its size if (!_screenResized) { - _videoMode.hardwareWidth = _videoMode.overlayWidth; - _videoMode.hardwareHeight = _videoMode.overlayHeight; + _videoMode.overlayWidth = _videoMode.hardwareWidth = _videoMode.screenWidth * _videoMode.scaleFactor; + _videoMode.overlayHeight = _videoMode.hardwareHeight = _videoMode.screenHeight * _videoMode.scaleFactor; int screenAspectRatio = _videoMode.screenWidth * 10000 / _videoMode.screenHeight; int desiredAspectRatio = getAspectRatio(); @@ -365,6 +363,9 @@ bool OpenGLSdlGraphicsManager::loadGFXMode() { // the width is modified it can break the overlay. if (_videoMode.hardwareHeight > _videoMode.overlayHeight) _videoMode.overlayHeight = _videoMode.hardwareHeight; + } else { + _videoMode.overlayWidth = _videoMode.hardwareWidth; + _videoMode.overlayHeight = _videoMode.hardwareHeight; } _screenResized = false; @@ -376,11 +377,15 @@ bool OpenGLSdlGraphicsManager::loadGFXMode() { SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - if (_videoMode.fullscreen) + if (_videoMode.fullscreen) { if (!setupFullscreenMode()) // Failed setuping a fullscreen mode return false; + _videoMode.overlayWidth = _videoMode.hardwareWidth; + _videoMode.overlayHeight = _videoMode.hardwareHeight; + } + uint32 flags = SDL_OPENGL; if (_videoMode.fullscreen) diff --git a/backends/graphics/sdl/sdl-graphics.cpp b/backends/graphics/sdl/sdl-graphics.cpp index 09c4f39636..c0d93b3bf2 100644 --- a/backends/graphics/sdl/sdl-graphics.cpp +++ b/backends/graphics/sdl/sdl-graphics.cpp @@ -165,13 +165,6 @@ SdlGraphicsManager::SdlGraphicsManager(SdlEventSource *sdlEventSource) _graphicsMutex = g_system->createMutex(); -#ifdef _WIN32_WCE - if (ConfMan.hasKey("use_GDI") && ConfMan.getBool("use_GDI")) { - SDL_VideoInit("windib", 0); - sdlFlags ^= SDL_INIT_VIDEO; - } -#endif - #ifdef USE_SDL_DEBUG_FOCUSRECT if (ConfMan.hasKey("use_sdl_debug_focusrect")) _enableFocusRectDebugCode = ConfMan.getBool("use_sdl_debug_focusrect"); @@ -1248,25 +1241,25 @@ void SdlGraphicsManager::copyRectToScreen(const byte *src, int pitch, int x, int error("SDL_LockSurface failed: %s", SDL_GetError()); #ifdef USE_RGB_COLOR - byte *dst = (byte *)_screen->pixels + y * _videoMode.screenWidth * _screenFormat.bytesPerPixel + x * _screenFormat.bytesPerPixel; - if (_videoMode.screenWidth == w && pitch == w * _screenFormat.bytesPerPixel) { - memcpy(dst, src, h*w*_screenFormat.bytesPerPixel); + byte *dst = (byte *)_screen->pixels + y * _screen->pitch + x * _screenFormat.bytesPerPixel; + if (_videoMode.screenWidth == w && pitch == _screen->pitch) { + memcpy(dst, src, h*pitch); } else { do { memcpy(dst, src, w * _screenFormat.bytesPerPixel); src += pitch; - dst += _videoMode.screenWidth * _screenFormat.bytesPerPixel; + dst += _screen->pitch; } while (--h); } #else - byte *dst = (byte *)_screen->pixels + y * _videoMode.screenWidth + x; - if (_videoMode.screenWidth == pitch && pitch == w) { + byte *dst = (byte *)_screen->pixels + y * _screen->pitch + x; + if (_screen->pitch == pitch && pitch == w) { memcpy(dst, src, h*w); } else { do { memcpy(dst, src, w); src += pitch; - dst += _videoMode.screenWidth; + dst += _screen->pitch; } while (--h); } #endif diff --git a/backends/graphics/wincesdl/wincesdl-graphics.cpp b/backends/graphics/wincesdl/wincesdl-graphics.cpp new file mode 100644 index 0000000000..37c6624560 --- /dev/null +++ b/backends/graphics/wincesdl/wincesdl-graphics.cpp @@ -0,0 +1,1639 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/scummsys.h" + +#ifdef _WIN32_WCE + +#include "common/system.h" +#include "common/translation.h" +#include "common/mutex.h" + +#include "graphics/scaler/downscaler.h" +#include "graphics/scaler/aspect.h" +#include "backends/graphics/wincesdl/wincesdl-graphics.h" +#include "backends/events/wincesdl/wincesdl-events.h" +#include "backends/platform/wince/wince-sdl.h" + +#include "backends/platform/wince/resource.h" +#include "backends/platform/wince/CEActionsPocket.h" +#include "backends/platform/wince/CEActionsSmartphone.h" +#include "backends/platform/wince/CEDevice.h" +#include "backends/platform/wince/CEScaler.h" +#include "backends/platform/wince/CEgui/ItemAction.h" + +WINCESdlGraphicsManager::WINCESdlGraphicsManager(SdlEventSource *sdlEventSource) + : SdlGraphicsManager(sdlEventSource), + _panelInitialized(false), _noDoubleTapRMB(false), + _toolbarHighDrawn(false), _newOrientation(0), _orientationLandscape(0), + _panelVisible(true), _saveActiveToolbar(NAME_MAIN_PANEL), _panelStateForced(false), + _canBeAspectScaled(false), _scalersChanged(false), _saveToolbarState(false), + _mouseBackupOld(NULL), _mouseBackupDim(0), _mouseBackupToolbar(NULL), + _usesEmulatedMouse(false), _forceHideMouse(false), _hasfocus(true), + _zoomUp(false), _zoomDown(false) { + memset(&_mouseCurState, 0, sizeof(_mouseCurState)); + if (_isSmartphone) { + _mouseCurState.x = 20; + _mouseCurState.y = 20; + } + + loadDeviceConfigurationElement("repeatTrigger", _keyRepeatTrigger, 200); + loadDeviceConfigurationElement("repeatX", _repeatX, 4); + loadDeviceConfigurationElement("repeatY", _repeatY, 4); + loadDeviceConfigurationElement("stepX1", _stepX1, 2); + loadDeviceConfigurationElement("stepX2", _stepX2, 10); + loadDeviceConfigurationElement("stepX3", _stepX3, 40); + loadDeviceConfigurationElement("stepY1", _stepY1, 2); + loadDeviceConfigurationElement("stepY2", _stepY2, 10); + loadDeviceConfigurationElement("stepY3", _stepY3, 20); + ConfMan.flushToDisk(); + + _isSmartphone = CEDevice::isSmartphone(); + + // Query SDL for screen size and init screen dependent stuff + OSystem_WINCE3::initScreenInfos(); + create_toolbar(); + _hasSmartphoneResolution = CEDevice::hasSmartphoneResolution() || CEDevice::isSmartphone(); + if (_hasSmartphoneResolution) + _panelVisible = false; // init correctly in smartphones + + _screen = NULL; +} + +// Graphics mode consts + +// Low end devices 240x320 + +static const OSystem::GraphicsMode s_supportedGraphicsModesLow[] = { + {"1x", _s("Normal (no scaling)"), GFX_NORMAL}, + {0, 0, 0} +}; + +// High end device 480x640 + +static const OSystem::GraphicsMode s_supportedGraphicsModesHigh[] = { + {"1x", _s("Normal (no scaling)"), GFX_NORMAL}, + {"2x", "2x", GFX_DOUBLESIZE}, +#ifndef _MSC_VER // EVC breaks template functions, and I'm tired of fixing them :) + {"2xsai", "2xSAI", GFX_2XSAI}, + {"super2xsai", "Super2xSAI", GFX_SUPER2XSAI}, + {"supereagle", "SuperEagle", GFX_SUPEREAGLE}, +#endif + {"advmame2x", "AdvMAME2x", GFX_ADVMAME2X}, +#ifndef _MSC_VER + {"hq2x", "HQ2x", GFX_HQ2X}, + {"tv2x", "TV2x", GFX_TV2X}, +#endif + {"dotmatrix", "DotMatrix", GFX_DOTMATRIX}, + {0, 0, 0} +}; + +const OSystem::GraphicsMode *WINCESdlGraphicsManager::getSupportedGraphicsModes() const { + if (CEDevice::hasWideResolution()) + return s_supportedGraphicsModesHigh; + else + return s_supportedGraphicsModesLow; +} + +bool WINCESdlGraphicsManager::hasFeature(OSystem::Feature f) { + return (f == OSystem::kFeatureVirtualKeyboard); +} + +void WINCESdlGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) { + switch (f) { + case OSystem::kFeatureFullscreenMode: + return; + + case OSystem::kFeatureVirtualKeyboard: + if (_hasSmartphoneResolution) + return; + _toolbarHighDrawn = false; + if (enable) { + _panelStateForced = true; + if (!_toolbarHandler.visible()) swap_panel_visibility(); + //_saveToolbarState = _toolbarHandler.visible(); + _saveActiveToolbar = _toolbarHandler.activeName(); + _toolbarHandler.setActive(NAME_PANEL_KEYBOARD); + _toolbarHandler.setVisible(true); + } else if (_panelStateForced) { + _panelStateForced = false; + _toolbarHandler.setActive(_saveActiveToolbar); + //_toolbarHandler.setVisible(_saveToolbarState); + } + return; + + case OSystem::kFeatureDisableKeyFiltering: + if (_hasSmartphoneResolution) { + GUI::Actions::Instance()->beginMapping(enable); + } + return; + + default: + SdlGraphicsManager::setFeatureState(f, enable); + } +} + +bool WINCESdlGraphicsManager::getFeatureState(OSystem::Feature f) { + switch (f) { + case OSystem::kFeatureFullscreenMode: + return false; + case OSystem::kFeatureVirtualKeyboard: + return (_panelStateForced); + default: + return SdlGraphicsManager::getFeatureState(f); + } +} + +int WINCESdlGraphicsManager::getDefaultGraphicsMode() const { + return GFX_NORMAL; +} + +void WINCESdlGraphicsManager::initSize(uint w, uint h, const Graphics::PixelFormat *format) { + if (_hasSmartphoneResolution && h == 240) + h = 200; // mainly for the launcher + + if (_isSmartphone && !ConfMan.hasKey("landscape")) { + ConfMan.setInt("landscape", 1); + ConfMan.flushToDisk(); + } + + _canBeAspectScaled = false; + if (w == 320 && h == 200 && !_hasSmartphoneResolution) { + _canBeAspectScaled = true; + h = 240; // use the extra 40 pixels height for the toolbar + } + + if (h == 400) // touche engine fixup + h += 80; + + if (!_hasSmartphoneResolution) { + if (h == 240) + _toolbarHandler.setOffset(200); + else + _toolbarHandler.setOffset(400); + } else { + if (h == 240) + _toolbarHandler.setOffset(200); + else // 176x220 + _toolbarHandler.setOffset(0); + } + + if (w != (uint) _videoMode.screenWidth || h != (uint) _videoMode.screenHeight) + _scalersChanged = false; + + _videoMode.overlayWidth = w; + _videoMode.overlayHeight = h; + + SdlGraphicsManager::initSize(w, h, format); + + if (_scalersChanged) { + unloadGFXMode(); + loadGFXMode(); + _scalersChanged = false; + } + + update_game_settings(); +} + +void WINCESdlGraphicsManager::loadDeviceConfigurationElement(Common::String element, int &value, int defaultValue) { + value = ConfMan.getInt(element, ConfMan.kApplicationDomain); + if (!value) { + value = defaultValue; + ConfMan.setInt(element, value, ConfMan.kApplicationDomain); + } +} + +void WINCESdlGraphicsManager::move_cursor_up() { + int x, y; + _usesEmulatedMouse = true; + retrieve_mouse_location(x, y); + if (_keyRepeat > _repeatY) + y -= _stepY3; + else if (_keyRepeat) + y -= _stepY2; + else + y -= _stepY1; + + if (y < 0) + y = 0; + + EventsBuffer::simulateMouseMove(x, y); +} + +void WINCESdlGraphicsManager::move_cursor_down() { + int x, y; + _usesEmulatedMouse = true; + retrieve_mouse_location(x, y); + if (_keyRepeat > _repeatY) + y += _stepY3; + else if (_keyRepeat) + y += _stepY2; + else + y += _stepY1; + + if (y > _videoMode.screenHeight * _scaleFactorYm / _scaleFactorYd) + y = _videoMode.screenHeight * _scaleFactorYm / _scaleFactorYd; + + EventsBuffer::simulateMouseMove(x, y); +} + +void WINCESdlGraphicsManager::move_cursor_left() { + int x, y; + _usesEmulatedMouse = true; + retrieve_mouse_location(x, y); + if (_keyRepeat > _repeatX) + x -= _stepX3; + else if (_keyRepeat) + x -= _stepX2; + else + x -= _stepX1; + + if (x < 0) + x = 0; + + EventsBuffer::simulateMouseMove(x, y); +} + +void WINCESdlGraphicsManager::move_cursor_right() { + int x, y; + _usesEmulatedMouse = true; + retrieve_mouse_location(x, y); + if (_keyRepeat > _repeatX) + x += _stepX3; + else if (_keyRepeat) + x += _stepX2; + else + x += _stepX1; + + if (x > _videoMode.screenWidth * _scaleFactorXm / _scaleFactorXd) + x = _videoMode.screenWidth * _scaleFactorXm / _scaleFactorXd; + + EventsBuffer::simulateMouseMove(x, y); +} + +void WINCESdlGraphicsManager::retrieve_mouse_location(int &x, int &y) { + x = _mouseCurState.x; + y = _mouseCurState.y; + + x = x * _scaleFactorXm / _scaleFactorXd; + y = y * _scaleFactorYm / _scaleFactorYd; + + if (_zoomDown) + y -= 240; +} + +void WINCESdlGraphicsManager::switch_zone() { + int x, y; + int i; + retrieve_mouse_location(x, y); + + for (i = 0; i < TOTAL_ZONES; i++) + if (x >= _zones[i].x && y >= _zones[i].y && + x <= _zones[i].x + _zones[i].width && y <= _zones[i].y + _zones[i].height) { + _mouseXZone[i] = x; + _mouseYZone[i] = y; + break; + } + _currentZone = i + 1; + if (_currentZone >= TOTAL_ZONES) + _currentZone = 0; + + EventsBuffer::simulateMouseMove(_mouseXZone[_currentZone], _mouseYZone[_currentZone]); +} + +void WINCESdlGraphicsManager::add_right_click(bool pushed) { + int x, y; + retrieve_mouse_location(x, y); + EventsBuffer::simulateMouseRightClick(x, y, pushed); +} + +void WINCESdlGraphicsManager::add_left_click(bool pushed) { + int x, y; + retrieve_mouse_location(x, y); + EventsBuffer::simulateMouseLeftClick(x, y, pushed); +} + +bool WINCESdlGraphicsManager::update_scalers() { + _videoMode.aspectRatioCorrection = false; + + if (CEDevice::hasPocketPCResolution()) { + if (_videoMode.mode != GFX_NORMAL) + return false; + + if ((!_orientationLandscape && (_videoMode.screenWidth == 320 || !_videoMode.screenWidth)) + || CEDevice::hasSquareQVGAResolution()) { + if (OSystem_WINCE3::getScreenWidth() != 320) { + _scaleFactorXm = 3; + _scaleFactorXd = 4; + _scaleFactorYm = 1; + _scaleFactorYd = 1; + _scalerProc = DownscaleHorizByThreeQuarters; + } else { + _scaleFactorXm = 1; + _scaleFactorXd = 1; + _scaleFactorYm = 1; + _scaleFactorYd = 1; + _scalerProc = Normal1x; + } + } else if (_orientationLandscape && (_videoMode.screenWidth == 320 || !_videoMode.screenWidth)) { + if (!_panelVisible && !_hasSmartphoneResolution && !_overlayVisible && _canBeAspectScaled) { + _scaleFactorXm = 1; + _scaleFactorXd = 1; + _scaleFactorYm = 6; + _scaleFactorYd = 5; + _scalerProc = Normal1xAspect; + _videoMode.aspectRatioCorrection = true; + } else { + _scaleFactorXm = 1; + _scaleFactorXd = 1; + _scaleFactorYm = 1; + _scaleFactorYd = 1; + _scalerProc = Normal1x; + } + } else if (_videoMode.screenWidth == 640 && !(OSystem_WINCE3::isOzone() && (OSystem_WINCE3::getScreenWidth() >= 640 || OSystem_WINCE3::getScreenHeight() >= 640))) { + _scaleFactorXm = 1; + _scaleFactorXd = 2; + _scaleFactorYm = 1; + _scaleFactorYd = 2; + _scalerProc = DownscaleAllByHalf; + } else if (_videoMode.screenWidth == 640 && (OSystem_WINCE3::isOzone() && (OSystem_WINCE3::getScreenWidth() >= 640 || OSystem_WINCE3::getScreenHeight() >= 640))) { + _scaleFactorXm = 1; + _scaleFactorXd = 1; + _scaleFactorYm = 1; + _scaleFactorYd = 1; + _scalerProc = Normal1x; + } + + return true; + } else if (CEDevice::hasWideResolution()) { +#ifdef USE_ARM_SCALER_ASM + if (_videoMode.mode == GFX_DOUBLESIZE && (_videoMode.screenWidth == 320 || !_videoMode.screenWidth)) { + if (!_panelVisible && !_overlayVisible && _canBeAspectScaled) { + _scaleFactorXm = 2; + _scaleFactorXd = 1; + _scaleFactorYm = 12; + _scaleFactorYd = 5; + _scalerProc = Normal2xAspect; + _videoMode.aspectRatioCorrection = true; + } else if ((_panelVisible || _overlayVisible) && _canBeAspectScaled) { + _scaleFactorXm = 2; + _scaleFactorXd = 1; + _scaleFactorYm = 2; + _scaleFactorYd = 1; + _scalerProc = Normal2x; + } + return true; + } +#endif + } else if (CEDevice::hasSmartphoneResolution()) { + if (_videoMode.mode != GFX_NORMAL) + return false; + + if (_videoMode.screenWidth > 320) + error("Game resolution not supported on Smartphone"); +#ifdef ARM + _scaleFactorXm = 11; + _scaleFactorXd = 16; +#else + _scaleFactorXm = 2; + _scaleFactorXd = 3; +#endif + _scaleFactorYm = 7; + _scaleFactorYd = 8; + _scalerProc = SmartphoneLandscape; + initZones(); + return true; + } + + return false; +} + +void WINCESdlGraphicsManager::update_game_settings() { + Common::String gameid(ConfMan.get("gameid")); + + // Finish panel initialization + if (!_panelInitialized && !gameid.empty()) { + CEGUI::Panel *panel; + _panelInitialized = true; + // Add the main panel + panel = new CEGUI::Panel(0, 32); + panel->setBackground(IMAGE_PANEL); + + // Save + panel->add(NAME_ITEM_OPTIONS, new CEGUI::ItemAction(ITEM_OPTIONS, POCKET_ACTION_SAVE)); + // Skip + panel->add(NAME_ITEM_SKIP, new CEGUI::ItemAction(ITEM_SKIP, POCKET_ACTION_SKIP)); + // sound +//__XXX__ panel->add(NAME_ITEM_SOUND, new CEGUI::ItemSwitch(ITEM_SOUND_OFF, ITEM_SOUND_ON, &_soundMaster)); + panel->add(NAME_ITEM_SOUND, new CEGUI::ItemSwitch(ITEM_SOUND_OFF, ITEM_SOUND_ON, &OSystem_WINCE3::_soundMaster)); + + // bind keys + panel->add(NAME_ITEM_BINDKEYS, new CEGUI::ItemAction(ITEM_BINDKEYS, POCKET_ACTION_BINDKEYS)); + // portrait/landscape - screen dependent + // FIXME : will still display the portrait/landscape icon when using a scaler (but will be disabled) + if (ConfMan.hasKey("landscape")) { + if (ConfMan.get("landscape")[0] > 57) { + _newOrientation = _orientationLandscape = ConfMan.getBool("landscape"); + //ConfMan.removeKey("landscape", ""); + ConfMan.setInt("landscape", _orientationLandscape); + } else + _newOrientation = _orientationLandscape = ConfMan.getInt("landscape"); + } else { + _newOrientation = _orientationLandscape = 0; + } + panel->add(NAME_ITEM_ORIENTATION, new CEGUI::ItemSwitch(ITEM_VIEW_LANDSCAPE, ITEM_VIEW_PORTRAIT, &_newOrientation, 2)); + _toolbarHandler.add(NAME_MAIN_PANEL, *panel); + _toolbarHandler.setActive(NAME_MAIN_PANEL); + _toolbarHandler.setVisible(true); + + if (_videoMode.mode == GFX_NORMAL && ConfMan.hasKey("landscape") && ConfMan.getInt("landscape")) { + setGraphicsMode(GFX_NORMAL); + hotswapGFXMode(); + } + + if (_hasSmartphoneResolution) + panel->setVisible(false); + + _saveToolbarState = true; + } + + if (ConfMan.hasKey("no_doubletap_rightclick")) + _noDoubleTapRMB = ConfMan.getBool("no_doubletap_rightclick"); +} + +void WINCESdlGraphicsManager::internUpdateScreen() { + SDL_Surface *srcSurf, *origSurf; + static bool old_overlayVisible = false; + int numRectsOut = 0; + int16 routx, routy, routw, routh, stretch, shakestretch; + + assert(_hwscreen != NULL); + + // bail if the application is minimized, be nice to OS + if (!_hasfocus) { + Sleep(20); + return; + } + + // If the shake position changed, fill the dirty area with blackness + if (_currentShakePos != _newShakePos) { + SDL_Rect blackrect = {0, 0, _videoMode.screenWidth *_scaleFactorXm / _scaleFactorXd, _newShakePos *_scaleFactorYm / _scaleFactorYd}; + if (_videoMode.aspectRatioCorrection) + blackrect.h = real2Aspect(blackrect.h - 1) + 1; + SDL_FillRect(_hwscreen, &blackrect, 0); + _currentShakePos = _newShakePos; + _forceFull = true; + } + + // Make sure the mouse is drawn, if it should be drawn. + drawMouse(); + + // Check whether the palette was changed in the meantime and update the + // screen surface accordingly. + if (_paletteDirtyEnd != 0) { + SDL_SetColors(_screen, _currentPalette + _paletteDirtyStart, _paletteDirtyStart, _paletteDirtyEnd - _paletteDirtyStart); + _paletteDirtyEnd = 0; + _forceFull = true; + } + + if (!_overlayVisible) { + origSurf = _screen; + srcSurf = _tmpscreen; + } else { + origSurf = _overlayscreen; + srcSurf = _tmpscreen2; + } + + if (old_overlayVisible != _overlayVisible) { + old_overlayVisible = _overlayVisible; + update_scalers(); + } + + // Force a full redraw if requested + if (_forceFull) { + _numDirtyRects = 1; + + _dirtyRectList[0].x = 0; + if (!_zoomDown) + _dirtyRectList[0].y = 0; + else + _dirtyRectList[0].y = _videoMode.screenHeight / 2; + _dirtyRectList[0].w = _videoMode.screenWidth; + if (!_zoomUp && !_zoomDown) + _dirtyRectList[0].h = _videoMode.screenHeight; + else + _dirtyRectList[0].h = _videoMode.screenHeight / 2; + + _toolbarHandler.forceRedraw(); + } + + // Only draw anything if necessary + if (_numDirtyRects > 0) { + + SDL_Rect *r, *rout; + SDL_Rect dst; + uint32 srcPitch, dstPitch; + SDL_Rect *last_rect = _dirtyRectList + _numDirtyRects; + bool toolbarVisible = _toolbarHandler.visible(); + int toolbarOffset = _toolbarHandler.getOffset(); + + for (r = _dirtyRectList; r != last_rect; ++r) { + dst = *r; + dst.x++; // Shift rect by one since 2xSai needs to access the data around + dst.y++; // any pixel to scale it, and we want to avoid mem access crashes. + // NOTE: This is also known as BLACK MAGIC, copied from the sdl backend + if (SDL_BlitSurface(origSurf, r, srcSurf, &dst) != 0) + error("SDL_BlitSurface failed: %s", SDL_GetError()); + } + + SDL_LockSurface(srcSurf); + SDL_LockSurface(_hwscreen); + + srcPitch = srcSurf->pitch; + dstPitch = _hwscreen->pitch; + + for (r = _dirtyRectList, rout = _dirtyRectOut; r != last_rect; ++r) { + + // always clamp to enclosing, downsampled-grid-aligned rect in the downscaled image + if (_scaleFactorXd != 1) { + stretch = r->x % _scaleFactorXd; + r->x -= stretch; + r->w += stretch; + r->w = (r->x + r->w + _scaleFactorXd - 1) / _scaleFactorXd * _scaleFactorXd - r->x; + } + if (_scaleFactorYd != 1) { + stretch = r->y % _scaleFactorYd; + r->y -= stretch; + r->h += stretch; + r->h = (r->y + r->h + _scaleFactorYd - 1) / _scaleFactorYd * _scaleFactorYd - r->y; + } + + // transform + shakestretch = _currentShakePos * _scaleFactorYm / _scaleFactorYd; + routx = r->x * _scaleFactorXm / _scaleFactorXd; // locate position in scaled screen + routy = r->y * _scaleFactorYm / _scaleFactorYd + shakestretch; // adjust for shake offset + routw = r->w * _scaleFactorXm / _scaleFactorXd; + routh = r->h * _scaleFactorYm / _scaleFactorYd - shakestretch; + + // clipping destination rectangle inside device screen (more strict, also more tricky but more stable) + // note that all current scalers do not make dst rect exceed left/right, unless chosen badly (FIXME) + if (_zoomDown) routy -= 240; // adjust for zoom position + if (routy + routh < 0) continue; + if (routy < 0) { + routh += routy; + r->y -= routy * _scaleFactorYd / _scaleFactorYm; + routy = 0; + r->h = routh * _scaleFactorYd / _scaleFactorYm; + } + if (_orientationLandscape) { + if (routy > OSystem_WINCE3::getScreenWidth()) continue; + if (routy + routh > OSystem_WINCE3::getScreenWidth()) { + routh = OSystem_WINCE3::getScreenWidth() - routy; + r->h = routh * _scaleFactorYd / _scaleFactorYm; + } + } else { + if (routy > OSystem_WINCE3::getScreenHeight()) continue; + if (routy + routh > OSystem_WINCE3::getScreenHeight()) { + routh = OSystem_WINCE3::getScreenHeight() - routy; + r->h = routh * _scaleFactorYd / _scaleFactorYm; + } + } + + // check if the toolbar is overwritten + if (toolbarVisible && r->y + r->h >= toolbarOffset) + _toolbarHandler.forceRedraw(); + + // blit it (with added voodoo from the sdl backend, shifting the source rect again) + _scalerProc((byte *)srcSurf->pixels + (r->x * 2 + 2) + (r->y + 1) * srcPitch, srcPitch, + (byte *)_hwscreen->pixels + routx * 2 + routy * dstPitch, dstPitch, + r->w, r->h - _currentShakePos); + + // add this rect to output + rout->x = routx; + rout->y = routy - shakestretch; + rout->w = routw; + rout->h = routh + shakestretch; + numRectsOut++; + rout++; + + } + SDL_UnlockSurface(srcSurf); + SDL_UnlockSurface(_hwscreen); + } + // Add the toolbar if needed + SDL_Rect toolbar_rect[1]; + if (_panelVisible && _toolbarHandler.draw(_toolbarLow, &toolbar_rect[0])) { + // It can be drawn, scale it + uint32 srcPitch, dstPitch; + SDL_Surface *toolbarSurface; + ScalerProc *toolbarScaler; + + if (_videoMode.screenHeight > 240) { + if (!_toolbarHighDrawn) { + // Resize the toolbar + SDL_LockSurface(_toolbarLow); + SDL_LockSurface(_toolbarHigh); + Normal2x((byte *)_toolbarLow->pixels, _toolbarLow->pitch, (byte *)_toolbarHigh->pixels, _toolbarHigh->pitch, toolbar_rect[0].w, toolbar_rect[0].h); + SDL_UnlockSurface(_toolbarHigh); + SDL_UnlockSurface(_toolbarLow); + _toolbarHighDrawn = true; + } + toolbar_rect[0].w *= 2; + toolbar_rect[0].h *= 2; + toolbarSurface = _toolbarHigh; + } else + toolbarSurface = _toolbarLow; + + drawToolbarMouse(toolbarSurface, true); // draw toolbar mouse if applicable + + // Apply the appropriate scaler + SDL_LockSurface(toolbarSurface); + SDL_LockSurface(_hwscreen); + srcPitch = toolbarSurface->pitch; + dstPitch = _hwscreen->pitch; + + toolbarScaler = _scalerProc; + if (_videoMode.scaleFactor == 2) + toolbarScaler = Normal2x; + else if (_videoMode.scaleFactor == 3) + toolbarScaler = Normal3x; + toolbarScaler((byte *)toolbarSurface->pixels, srcPitch, + (byte *)_hwscreen->pixels + (_toolbarHandler.getOffset() * _scaleFactorYm / _scaleFactorYd * dstPitch), + dstPitch, toolbar_rect[0].w, toolbar_rect[0].h); + SDL_UnlockSurface(toolbarSurface); + SDL_UnlockSurface(_hwscreen); + + // And blit it + toolbar_rect[0].y = _toolbarHandler.getOffset(); + toolbar_rect[0].x = toolbar_rect[0].x * _scaleFactorXm / _scaleFactorXd; + toolbar_rect[0].y = toolbar_rect[0].y * _scaleFactorYm / _scaleFactorYd; + toolbar_rect[0].w = toolbar_rect[0].w * _scaleFactorXm / _scaleFactorXd; + toolbar_rect[0].h = toolbar_rect[0].h * _scaleFactorYm / _scaleFactorYd; + + SDL_UpdateRects(_hwscreen, 1, toolbar_rect); + + drawToolbarMouse(toolbarSurface, false); // undraw toolbar mouse + } + + // Finally, blit all our changes to the screen + if (numRectsOut > 0) + SDL_UpdateRects(_hwscreen, numRectsOut, _dirtyRectOut); + + _numDirtyRects = 0; + _forceFull = false; +} + +bool WINCESdlGraphicsManager::setGraphicsMode(int mode) { + + Common::StackLock lock(_graphicsMutex); + int oldScaleFactorXm = _scaleFactorXm; + int oldScaleFactorXd = _scaleFactorXd; + int oldScaleFactorYm = _scaleFactorYm; + int oldScaleFactorYd = _scaleFactorYd; + + _scaleFactorXm = -1; + _scaleFactorXd = -1; + _scaleFactorYm = -1; + _scaleFactorYd = -1; + + if (ConfMan.hasKey("landscape")) + if (ConfMan.get("landscape")[0] > 57) { + _newOrientation = _orientationLandscape = ConfMan.getBool("landscape"); + ConfMan.setInt("landscape", _orientationLandscape); + } else + _newOrientation = _orientationLandscape = ConfMan.getInt("landscape"); + else + _newOrientation = _orientationLandscape = 0; + + if (OSystem_WINCE3::isOzone() && (OSystem_WINCE3::getScreenWidth() >= 640 || OSystem_WINCE3::getScreenHeight() >= 640) && mode) + _scaleFactorXm = -1; + + if (CEDevice::hasPocketPCResolution() && !CEDevice::hasWideResolution() && _orientationLandscape) + _videoMode.mode = GFX_NORMAL; + else + _videoMode.mode = mode; + + if (_scaleFactorXm < 0) { + /* Standard scalers, from the SDL backend */ + switch (_videoMode.mode) { + case GFX_NORMAL: + _videoMode.scaleFactor = 1; + _scalerProc = Normal1x; + break; + case GFX_DOUBLESIZE: + _videoMode.scaleFactor = 2; + _scalerProc = Normal2x; + break; + case GFX_TRIPLESIZE: + _videoMode.scaleFactor = 3; + _scalerProc = Normal3x; + break; + case GFX_2XSAI: + _videoMode.scaleFactor = 2; + _scalerProc = _2xSaI; + break; + case GFX_SUPER2XSAI: + _videoMode.scaleFactor = 2; + _scalerProc = Super2xSaI; + break; + case GFX_SUPEREAGLE: + _videoMode.scaleFactor = 2; + _scalerProc = SuperEagle; + break; + case GFX_ADVMAME2X: + _videoMode.scaleFactor = 2; + _scalerProc = AdvMame2x; + break; + case GFX_ADVMAME3X: + _videoMode.scaleFactor = 3; + _scalerProc = AdvMame3x; + break; +#ifdef USE_HQ_SCALERS + case GFX_HQ2X: + _videoMode.scaleFactor = 2; + _scalerProc = HQ2x; + break; + case GFX_HQ3X: + _videoMode.scaleFactor = 3; + _scalerProc = HQ3x; + break; +#endif + case GFX_TV2X: + _videoMode.scaleFactor = 2; + _scalerProc = TV2x; + break; + case GFX_DOTMATRIX: + _videoMode.scaleFactor = 2; + _scalerProc = DotMatrix; + break; + + default: + error("unknown gfx mode %d", mode); + } + } + + // Check if the scaler can be accepted, if not get back to normal scaler + if (_videoMode.scaleFactor && ((_videoMode.scaleFactor * _videoMode.screenWidth > OSystem_WINCE3::getScreenWidth() && _videoMode.scaleFactor * _videoMode.screenWidth > OSystem_WINCE3::getScreenHeight()) + || (_videoMode.scaleFactor * _videoMode.screenHeight > OSystem_WINCE3::getScreenWidth() && _videoMode.scaleFactor * _videoMode.screenHeight > OSystem_WINCE3::getScreenHeight()))) { + _videoMode.scaleFactor = 1; + _scalerProc = Normal1x; + } + + // Common scaler system was used + if (_scaleFactorXm < 0) { + _scaleFactorXm = _videoMode.scaleFactor; + _scaleFactorXd = 1; + _scaleFactorYm = _videoMode.scaleFactor; + _scaleFactorYd = 1; + } + + _forceFull = true; + + if (oldScaleFactorXm != _scaleFactorXm || + oldScaleFactorXd != _scaleFactorXd || + oldScaleFactorYm != _scaleFactorYm || + oldScaleFactorYd != _scaleFactorYd) { + _scalersChanged = true; + } else + _scalersChanged = false; + + + return true; + +} + +bool WINCESdlGraphicsManager::loadGFXMode() { + int displayWidth; + int displayHeight; + unsigned int flags = SDL_FULLSCREEN | SDL_SWSURFACE; + + _videoMode.fullscreen = true; // forced + _forceFull = true; + + _tmpscreen = NULL; + + // Recompute scalers if necessary + update_scalers(); + + // Create the surface that contains the 8 bit game data + _screen = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.screenWidth, _videoMode.screenHeight, 8, 0, 0, 0, 0); + if (_screen == NULL) + error("_screen failed (%s)", SDL_GetError()); + + // Create the surface that contains the scaled graphics in 16 bit mode + // Always use full screen mode to have a "clean screen" + if (!_videoMode.aspectRatioCorrection) { + displayWidth = _videoMode.screenWidth * _scaleFactorXm / _scaleFactorXd; + displayHeight = _videoMode.screenHeight * _scaleFactorYm / _scaleFactorYd; + } else { + displayWidth = _videoMode.screenWidth * _videoMode.scaleFactor; + displayHeight = _videoMode.screenHeight * _videoMode.scaleFactor; + } + + switch (_orientationLandscape) { + case 1: + flags |= SDL_LANDSCVIDEO; + break; + case 2: + flags |= SDL_INVLNDVIDEO; + break; + default: + flags |= SDL_PORTRTVIDEO; + } + _hwscreen = SDL_SetVideoMode(displayWidth, displayHeight, 16, flags); + + if (_hwscreen == NULL) { + warning("SDL_SetVideoMode says we can't switch to that mode (%s)", SDL_GetError()); + g_system->quit(); + } + + // see what orientation sdl finally accepted + if (_hwscreen->flags & SDL_PORTRTVIDEO) + _orientationLandscape = _newOrientation = 0; + else if (_hwscreen->flags & SDL_LANDSCVIDEO) + _orientationLandscape = _newOrientation = 1; + else + _orientationLandscape = _newOrientation = 2; + + // Create the surface used for the graphics in 16 bit before scaling, and also the overlay + // Distinguish 555 and 565 mode + if (_hwscreen->format->Rmask == 0x7C00) + InitScalers(555); + else + InitScalers(565); + _overlayFormat.bytesPerPixel = _hwscreen->format->BytesPerPixel; + _overlayFormat.rLoss = _hwscreen->format->Rloss; + _overlayFormat.gLoss = _hwscreen->format->Gloss; + _overlayFormat.bLoss = _hwscreen->format->Bloss; + _overlayFormat.aLoss = _hwscreen->format->Aloss; + _overlayFormat.rShift = _hwscreen->format->Rshift; + _overlayFormat.gShift = _hwscreen->format->Gshift; + _overlayFormat.bShift = _hwscreen->format->Bshift; + _overlayFormat.aShift = _hwscreen->format->Ashift; + + // Need some extra bytes around when using 2xSaI + _tmpscreen = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.screenWidth + 3, _videoMode.screenHeight + 3, 16, _hwscreen->format->Rmask, _hwscreen->format->Gmask, _hwscreen->format->Bmask, _hwscreen->format->Amask); + + if (_tmpscreen == NULL) + error("_tmpscreen creation failed (%s)", SDL_GetError()); + + // Overlay + if (CEDevice::hasDesktopResolution()) { + _overlayscreen = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.overlayWidth * _scaleFactorXm / _scaleFactorXd, _videoMode.overlayHeight * _scaleFactorYm / _scaleFactorYd, 16, 0, 0, 0, 0); + if (_overlayscreen == NULL) + error("_overlayscreen failed (%s)", SDL_GetError()); + _tmpscreen2 = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.overlayWidth * _scaleFactorXm / _scaleFactorXd + 3, _videoMode.overlayHeight * _scaleFactorYm / _scaleFactorYd + 3, 16, 0, 0, 0, 0); + if (_tmpscreen2 == NULL) + error("_tmpscreen2 failed (%s)", SDL_GetError()); + } else { + _overlayscreen = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.overlayWidth, _videoMode.overlayHeight, 16, 0, 0, 0, 0); + if (_overlayscreen == NULL) + error("_overlayscreen failed (%s)", SDL_GetError()); + _tmpscreen2 = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.overlayWidth + 3, _videoMode.overlayHeight + 3, 16, 0, 0, 0, 0); + if (_tmpscreen2 == NULL) + error("_tmpscreen2 failed (%s)", SDL_GetError()); + } + + // Toolbar + _toolbarHighDrawn = false; + uint16 *toolbar_screen = (uint16 *)calloc(320 * 40, sizeof(uint16)); // *not* leaking memory here + _toolbarLow = SDL_CreateRGBSurfaceFrom(toolbar_screen, 320, 40, 16, 320 * 2, _hwscreen->format->Rmask, _hwscreen->format->Gmask, _hwscreen->format->Bmask, _hwscreen->format->Amask); + + if (_toolbarLow == NULL) + error("_toolbarLow failed (%s)", SDL_GetError()); + + if (_videoMode.screenHeight > 240) { + uint16 *toolbar_screen_high = (uint16 *)calloc(640 * 80, sizeof(uint16)); + _toolbarHigh = SDL_CreateRGBSurfaceFrom(toolbar_screen_high, 640, 80, 16, 640 * 2, _hwscreen->format->Rmask, _hwscreen->format->Gmask, _hwscreen->format->Bmask, _hwscreen->format->Amask); + + if (_toolbarHigh == NULL) + error("_toolbarHigh failed (%s)", SDL_GetError()); + } else + _toolbarHigh = NULL; + + // keyboard cursor control, some other better place for it? + _sdlEventSource->resetKeyboadEmulation(_videoMode.screenWidth * _scaleFactorXm / _scaleFactorXd - 1, _videoMode.screenHeight * _scaleFactorXm / _scaleFactorXd - 1); + + return true; +} + +void WINCESdlGraphicsManager::unloadGFXMode() { + if (_screen) { + SDL_FreeSurface(_screen); + _screen = NULL; + } + + if (_hwscreen) { + SDL_FreeSurface(_hwscreen); + _hwscreen = NULL; + } + + if (_tmpscreen) { + SDL_FreeSurface(_tmpscreen); + _tmpscreen = NULL; + } +} + +bool WINCESdlGraphicsManager::hotswapGFXMode() { + if (!_screen) + return false; + + // Keep around the old _screen & _tmpscreen so we can restore the screen data + // after the mode switch. (also for the overlay) + SDL_Surface *old_screen = _screen; + SDL_Surface *old_tmpscreen = _tmpscreen; + SDL_Surface *old_overlayscreen = _overlayscreen; + SDL_Surface *old_tmpscreen2 = _tmpscreen2; + + // Release the HW screen surface + SDL_FreeSurface(_hwscreen); + + // Release toolbars + free(_toolbarLow->pixels); + SDL_FreeSurface(_toolbarLow); + if (_toolbarHigh) { + free(_toolbarHigh->pixels); + SDL_FreeSurface(_toolbarHigh); + } + + // Setup the new GFX mode + if (!loadGFXMode()) { + unloadGFXMode(); + + _screen = old_screen; + _overlayscreen = old_overlayscreen; + + return false; + } + + // reset palette + SDL_SetColors(_screen, _currentPalette, 0, 256); + + // Restore old screen content + SDL_BlitSurface(old_screen, NULL, _screen, NULL); + SDL_BlitSurface(old_tmpscreen, NULL, _tmpscreen, NULL); + if (_overlayVisible) { + SDL_BlitSurface(old_overlayscreen, NULL, _overlayscreen, NULL); + SDL_BlitSurface(old_tmpscreen2, NULL, _tmpscreen2, NULL); + } + + // Free the old surfaces + SDL_FreeSurface(old_screen); + SDL_FreeSurface(old_tmpscreen); + SDL_FreeSurface(old_overlayscreen); + SDL_FreeSurface(old_tmpscreen2); + + // Blit everything back to the screen + _toolbarHighDrawn = false; + internUpdateScreen(); + + // Make sure that a Common::EVENT_SCREEN_CHANGED gets sent later -> FIXME this crashes when no game has been loaded. +// _modeChanged = true; + + return true; +} + +bool WINCESdlGraphicsManager::saveScreenshot(const char *filename) { + assert(_hwscreen != NULL); + + Common::StackLock lock(_graphicsMutex); // Lock the mutex until this function ends + SDL_SaveBMP(_hwscreen, filename); + return true; +} + +void WINCESdlGraphicsManager::copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h) { + assert(_transactionMode == kTransactionNone); + + if (_overlayscreen == NULL) + return; + + // Clip the coordinates + if (x < 0) { + w += x; + buf -= x; + x = 0; + } + + if (y < 0) { + h += y; + buf -= y * pitch; + y = 0; + } + + if (w > _videoMode.overlayWidth - x) { + w = _videoMode.overlayWidth - x; + } + + if (h > _videoMode.overlayHeight - y) { + h = _videoMode.overlayHeight - y; + } + + if (w <= 0 || h <= 0) + return; + + // Mark the modified region as dirty + addDirtyRect(x, y, w, h); + + undrawMouse(); + + if (SDL_LockSurface(_overlayscreen) == -1) + error("SDL_LockSurface failed: %s", SDL_GetError()); + + byte *dst = (byte *)_overlayscreen->pixels + y * _overlayscreen->pitch + x * 2; + do { + memcpy(dst, buf, w * 2); + dst += _overlayscreen->pitch; + buf += pitch; + } while (--h); + + SDL_UnlockSurface(_overlayscreen); +} + +void WINCESdlGraphicsManager::copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h) { + assert(_transactionMode == kTransactionNone); + assert(src); + + if (_screen == NULL) + return; + + Common::StackLock lock(_graphicsMutex); // Lock the mutex until this function ends + + /* Clip the coordinates */ + if (x < 0) { + w += x; + src -= x; + x = 0; + } + + if (y < 0) { + h += y; + src -= y * pitch; + y = 0; + } + + if (w > _videoMode.screenWidth - x) { + w = _videoMode.screenWidth - x; + } + + if (h > _videoMode.screenHeight - y) { + h = _videoMode.screenHeight - y; + } + + if (w <= 0 || h <= 0) + return; + + addDirtyRect(x, y, w, h); + + undrawMouse(); + + // Try to lock the screen surface + if (SDL_LockSurface(_screen) == -1) + error("SDL_LockSurface failed: %s", SDL_GetError()); + + byte *dst = (byte *)_screen->pixels + y * _videoMode.screenWidth + x; + + if (_videoMode.screenWidth == pitch && pitch == w) { + memcpy(dst, src, h * w); + } else { + do { + memcpy(dst, src, w); + src += pitch; + dst += _videoMode.screenWidth; + } while (--h); + } + + // Unlock the screen surface + SDL_UnlockSurface(_screen); +} + +void WINCESdlGraphicsManager::setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y, uint32 keycolor, int cursorTargetScale, const Graphics::PixelFormat *format) { + + undrawMouse(); + if (w == 0 || h == 0) + return; + + _mouseCurState.w = w; + _mouseCurState.h = h; + + _mouseHotspotX = hotspot_x; + _mouseHotspotY = hotspot_y; + + _mouseKeyColor = keycolor; + + free(_mouseData); + + _mouseData = (byte *) malloc(w * h); + memcpy(_mouseData, buf, w * h); + + if (w > _mouseBackupDim || h > _mouseBackupDim) { + // mouse has been undrawn, adjust sprite backup area + free(_mouseBackupOld); + free(_mouseBackupToolbar); + uint16 tmp = (w > h) ? w : h; + _mouseBackupOld = (byte *) malloc(tmp * tmp * 2); // can hold 8bpp (playfield) or 16bpp (overlay) data + _mouseBackupToolbar = (uint16 *) malloc(tmp * tmp * 2); // 16 bpp + _mouseBackupDim = tmp; + } +} + +void WINCESdlGraphicsManager::adjustMouseEvent(const Common::Event &event) { + if (!event.synthetic) { + Common::Event newEvent(event); + newEvent.synthetic = true; + if (!_overlayVisible) { + /* + newEvent.mouse.x = newEvent.mouse.x * _scaleFactorXd / _scaleFactorXm; + newEvent.mouse.y = newEvent.mouse.y * _scaleFactorYd / _scaleFactorYm; + newEvent.mouse.x /= _videoMode.scaleFactor; + newEvent.mouse.y /= _videoMode.scaleFactor; + */ + if (_videoMode.aspectRatioCorrection) + newEvent.mouse.y = aspect2Real(newEvent.mouse.y); + } + g_system->getEventManager()->pushEvent(newEvent); + } +} + +void WINCESdlGraphicsManager::setMousePos(int x, int y) { + if (x != _mouseCurState.x || y != _mouseCurState.y) { + undrawMouse(); + _mouseCurState.x = x; + _mouseCurState.y = y; + updateScreen(); + } +} + +Graphics::Surface *WINCESdlGraphicsManager::lockScreen() { + // Make sure mouse pointer is not painted over the playfield at the time of locking + undrawMouse(); + return SdlGraphicsManager::lockScreen(); +} + +void WINCESdlGraphicsManager::showOverlay() { + assert(_transactionMode == kTransactionNone); + + if (_overlayVisible) + return; + + undrawMouse(); + _overlayVisible = true; + update_scalers(); + clearOverlay(); +} + +void WINCESdlGraphicsManager::hideOverlay() { + assert(_transactionMode == kTransactionNone); + + if (!_overlayVisible) + return; + + undrawMouse(); + _overlayVisible = false; + clearOverlay(); + _forceFull = true; +} + +void WINCESdlGraphicsManager::blitCursor() { +} + +void WINCESdlGraphicsManager::drawToolbarMouse(SDL_Surface *surf, bool draw) { + + if (!_mouseData || !_usesEmulatedMouse) + return; + + int x = _mouseCurState.x - _mouseHotspotX; + int y = _mouseCurState.y - _mouseHotspotY - _toolbarHandler.getOffset(); + int w = _mouseCurState.w; + int h = _mouseCurState.h; + byte color; + const byte *src = _mouseData; + int width; + + // clip + if (x < 0) { + w += x; + src -= x; + x = 0; + } + if (y < 0) { + h += y; + src -= y * _mouseCurState.w; + y = 0; + } + if (w > surf->w - x) + w = surf->w - x; + if (h > surf->h - y) + h = surf->h - y; + if (w <= 0 || h <= 0) + return; + + if (SDL_LockSurface(surf) == -1) + error("SDL_LockSurface failed at internDrawToolbarMouse: %s", SDL_GetError()); + + uint16 *bak = _mouseBackupToolbar; // toolbar surfaces are 16bpp + uint16 *dst; + dst = (uint16 *)surf->pixels + y * surf->w + x; + + if (draw) { // blit it + while (h > 0) { + width = w; + while (width > 0) { + *bak++ = *dst; + color = *src++; + if (color != _mouseKeyColor) // transparent color + *dst = 0xFFFF; + dst++; + width--; + } + src += _mouseCurState.w - w; + bak += _mouseBackupDim - w; + dst += surf->w - w; + h--; + } + } else { // restore bg + for (y = 0; y < h; ++y, bak += _mouseBackupDim, dst += surf->w) + memcpy(dst, bak, w << 1); + } + + SDL_UnlockSurface(surf); +} + +void WINCESdlGraphicsManager::warpMouse(int x, int y) { + if (_mouseCurState.x != x || _mouseCurState.y != y) { + SDL_WarpMouse(x * _scaleFactorXm / _scaleFactorXd, y * _scaleFactorYm / _scaleFactorYd); + + // SDL_WarpMouse() generates a mouse movement event, so + // set_mouse_pos() would be called eventually. However, the + // cannon script in CoMI calls this function twice each time + // the cannon is reloaded. Unless we update the mouse position + // immediately the second call is ignored, causing the cannon + // to change its aim. + + setMousePos(x, y); + } +} + +void WINCESdlGraphicsManager::unlockScreen() { + SdlGraphicsManager::unlockScreen(); +} + +void WINCESdlGraphicsManager::internDrawMouse() { + if (!_mouseNeedsRedraw || !_mouseVisible || !_mouseData) + return; + + int x = _mouseCurState.x - _mouseHotspotX; + int y = _mouseCurState.y - _mouseHotspotY; + int w = _mouseCurState.w; + int h = _mouseCurState.h; + byte color; + const byte *src = _mouseData; // Image representing the mouse + int width; + + // clip the mouse rect, and adjust the src pointer accordingly + if (x < 0) { + w += x; + src -= x; + x = 0; + } + if (y < 0) { + h += y; + src -= y * _mouseCurState.w; + y = 0; + } + + if (w > _videoMode.screenWidth - x) + w = _videoMode.screenWidth - x; + if (h > _videoMode.screenHeight - y) + h = _videoMode.screenHeight - y; + + // Quick check to see if anything has to be drawn at all + if (w <= 0 || h <= 0) + return; + + // Draw the mouse cursor; backup the covered area in "bak" + if (SDL_LockSurface(_overlayVisible ? _overlayscreen : _screen) == -1) + error("SDL_LockSurface failed: %s", SDL_GetError()); + + // Mark as dirty + addDirtyRect(x, y, w, h); + + if (!_overlayVisible) { + byte *bak = _mouseBackupOld; // Surface used to backup the area obscured by the mouse + byte *dst; // Surface we are drawing into + + dst = (byte *)_screen->pixels + y * _videoMode.screenWidth + x; + while (h > 0) { + width = w; + while (width > 0) { + *bak++ = *dst; + color = *src++; + if (color != _mouseKeyColor) // transparent, don't draw + *dst = color; + dst++; + width--; + } + src += _mouseCurState.w - w; + bak += _mouseBackupDim - w; + dst += _videoMode.screenWidth - w; + h--; + } + + } else { + uint16 *bak = (uint16 *)_mouseBackupOld; // Surface used to backup the area obscured by the mouse + byte *dst; // Surface we are drawing into + + dst = (byte *)_overlayscreen->pixels + (y + 1) * _overlayscreen->pitch + (x + 1) * 2; + while (h > 0) { + width = w; + while (width > 0) { + *bak++ = *(uint16 *)dst; + color = *src++; + if (color != 0xFF) // 0xFF = transparent, don't draw + *(uint16 *)dst = SDL_MapRGB(_overlayscreen->format, _currentPalette[color].r, _currentPalette[color].g, _currentPalette[color].b); + dst += 2; + width--; + } + src += _mouseCurState.w - w; + bak += _mouseBackupDim - w; + dst += _overlayscreen->pitch - w * 2; + h--; + } + } + + SDL_UnlockSurface(_overlayVisible ? _overlayscreen : _screen); + + // Finally, set the flag to indicate the mouse has been drawn + _mouseNeedsRedraw = false; +} + +void WINCESdlGraphicsManager::undrawMouse() { + assert(_transactionMode == kTransactionNone); + + if (_mouseNeedsRedraw) + return; + + int old_mouse_x = _mouseCurState.x - _mouseHotspotX; + int old_mouse_y = _mouseCurState.y - _mouseHotspotY; + int old_mouse_w = _mouseCurState.w; + int old_mouse_h = _mouseCurState.h; + + // clip the mouse rect, and adjust the src pointer accordingly + if (old_mouse_x < 0) { + old_mouse_w += old_mouse_x; + old_mouse_x = 0; + } + if (old_mouse_y < 0) { + old_mouse_h += old_mouse_y; + old_mouse_y = 0; + } + + if (old_mouse_w > _videoMode.screenWidth - old_mouse_x) + old_mouse_w = _videoMode.screenWidth - old_mouse_x; + if (old_mouse_h > _videoMode.screenHeight - old_mouse_y) + old_mouse_h = _videoMode.screenHeight - old_mouse_y; + + // Quick check to see if anything has to be drawn at all + if (old_mouse_w <= 0 || old_mouse_h <= 0) + return; + + + if (SDL_LockSurface(_overlayVisible ? _overlayscreen : _screen) == -1) + error("SDL_LockSurface failed: %s", SDL_GetError()); + + int y; + if (!_overlayVisible) { + byte *dst, *bak = _mouseBackupOld; + + // No need to do clipping here, since drawMouse() did that already + dst = (byte *)_screen->pixels + old_mouse_y * _videoMode.screenWidth + old_mouse_x; + for (y = 0; y < old_mouse_h; ++y, bak += _mouseBackupDim, dst += _videoMode.screenWidth) + memcpy(dst, bak, old_mouse_w); + } else { + byte *dst; + uint16 *bak = (uint16 *)_mouseBackupOld; + + // No need to do clipping here, since drawMouse() did that already + dst = (byte *)_overlayscreen->pixels + (old_mouse_y + 1) * _overlayscreen->pitch + (old_mouse_x + 1) * 2; + for (y = 0; y < old_mouse_h; ++y, bak += _mouseBackupDim, dst += _overlayscreen->pitch) + memcpy(dst, bak, old_mouse_w << 1); + } + + addDirtyRect(old_mouse_x, old_mouse_y, old_mouse_w, old_mouse_h); + + SDL_UnlockSurface(_overlayVisible ? _overlayscreen : _screen); + + _mouseNeedsRedraw = true; +} + +void WINCESdlGraphicsManager::drawMouse() { + if (!(_toolbarHandler.visible() && _mouseCurState.y >= _toolbarHandler.getOffset() && !_usesEmulatedMouse) && !_forceHideMouse) + internDrawMouse(); +} + +bool WINCESdlGraphicsManager::showMouse(bool visible) { + if (_mouseVisible == visible) + return visible; + + if (visible == false) + undrawMouse(); + + bool last = _mouseVisible; + _mouseVisible = visible; + _mouseNeedsRedraw = true; + + return last; +} + +void WINCESdlGraphicsManager::addDirtyRect(int x, int y, int w, int h, bool mouseRect) { + + if (_forceFull || _paletteDirtyEnd) + return; + + SdlGraphicsManager::addDirtyRect(x, y, w, h, false); +} + +void WINCESdlGraphicsManager::swap_panel_visibility() { + //if (!_forcePanelInvisible && !_panelStateForced) { + if (_zoomDown || _zoomUp) + return; + + if (_panelVisible) { + if (_toolbarHandler.activeName() == NAME_PANEL_KEYBOARD) + _panelVisible = !_panelVisible; + else + _toolbarHandler.setActive(NAME_PANEL_KEYBOARD); + } else { + _toolbarHandler.setActive(NAME_MAIN_PANEL); + _panelVisible = !_panelVisible; + } + _toolbarHandler.setVisible(_panelVisible); + _toolbarHighDrawn = false; + + if (_videoMode.screenHeight > 240) + addDirtyRect(0, 400, 640, 80); + else + addDirtyRect(0, 200, 320, 40); + + if (_toolbarHandler.activeName() == NAME_PANEL_KEYBOARD && _panelVisible) + internUpdateScreen(); + else { + update_scalers(); + hotswapGFXMode(); + } + //} +} + +void WINCESdlGraphicsManager::swap_panel() { + _toolbarHighDrawn = false; + //if (!_panelStateForced) { + if (_toolbarHandler.activeName() == NAME_PANEL_KEYBOARD && _panelVisible) + _toolbarHandler.setActive(NAME_MAIN_PANEL); + else + _toolbarHandler.setActive(NAME_PANEL_KEYBOARD); + + if (_videoMode.screenHeight > 240) + addDirtyRect(0, 400, 640, 80); + else + addDirtyRect(0, 200, 320, 40); + + _toolbarHandler.setVisible(true); + if (!_panelVisible) { + _panelVisible = true; + update_scalers(); + hotswapGFXMode(); + } + //} +} + +void WINCESdlGraphicsManager::swap_smartphone_keyboard() { + _toolbarHandler.setActive(NAME_PANEL_KEYBOARD); + _panelVisible = !_panelVisible; + _toolbarHandler.setVisible(_panelVisible); + if (_videoMode.screenHeight > 240) + addDirtyRect(0, 0, 640, 80); + else + addDirtyRect(0, 0, 320, 40); + internUpdateScreen(); +} + +void WINCESdlGraphicsManager::swap_zoom_up() { + if (_zoomUp) { + // restore visibility + _toolbarHandler.setVisible(_saveToolbarZoom); + // restore scaler + _scaleFactorYd = 2; + _scalerProc = DownscaleAllByHalf; + _zoomUp = false; + _zoomDown = false; + } else { + // only active if running on a PocketPC + if (_scalerProc != DownscaleAllByHalf && _scalerProc != DownscaleHorizByHalf) + return; + if (_scalerProc == DownscaleAllByHalf) { + _saveToolbarZoom = _toolbarHandler.visible(); + _toolbarHandler.setVisible(false); + // set zoom scaler + _scaleFactorYd = 1; + _scalerProc = DownscaleHorizByHalf; + } + + _zoomDown = false; + _zoomUp = true; + } + // redraw whole screen + addDirtyRect(0, 0, 640, 480); + internUpdateScreen(); +} + +void WINCESdlGraphicsManager::swap_zoom_down() { + if (_zoomDown) { + // restore visibility + _toolbarHandler.setVisible(_saveToolbarZoom); + // restore scaler + _scaleFactorYd = 2; + _scalerProc = DownscaleAllByHalf; + _zoomDown = false; + _zoomUp = false; + } else { + // only active if running on a PocketPC + if (_scalerProc != DownscaleAllByHalf && _scalerProc != DownscaleHorizByHalf) + return; + if (_scalerProc == DownscaleAllByHalf) { + _saveToolbarZoom = _toolbarHandler.visible(); + _toolbarHandler.setVisible(false); + // set zoom scaler + _scaleFactorYd = 1; + _scalerProc = DownscaleHorizByHalf; + } + + _zoomUp = false; + _zoomDown = true; + } + // redraw whole screen + addDirtyRect(0, 0, 640, 480); + internUpdateScreen(); +} + +void WINCESdlGraphicsManager::swap_mouse_visibility() { + _forceHideMouse = !_forceHideMouse; + if (_forceHideMouse) + undrawMouse(); +} + +// Smartphone actions +void WINCESdlGraphicsManager::initZones() { + int i; + + _currentZone = 0; + for (i = 0; i < TOTAL_ZONES; i++) { + _mouseXZone[i] = (_zones[i].x + (_zones[i].width / 2)) * _scaleFactorXm / _scaleFactorXd; + _mouseYZone[i] = (_zones[i].y + (_zones[i].height / 2)) * _scaleFactorYm / _scaleFactorYd; + } +} + +void WINCESdlGraphicsManager::smartphone_rotate_display() { + _orientationLandscape = _newOrientation = _orientationLandscape == 1 ? 2 : 1; + ConfMan.setInt("landscape", _orientationLandscape); + ConfMan.flushToDisk(); + hotswapGFXMode(); +} + +void WINCESdlGraphicsManager::create_toolbar() { + CEGUI::PanelKeyboard *keyboard; + + // Add the keyboard + keyboard = new CEGUI::PanelKeyboard(PANEL_KEYBOARD); + _toolbarHandler.add(NAME_PANEL_KEYBOARD, *keyboard); + _toolbarHandler.setVisible(false); +} + +WINCESdlGraphicsManager::zoneDesc WINCESdlGraphicsManager::_zones[TOTAL_ZONES] = { + { 0, 0, 320, 145 }, + { 0, 145, 150, 55 }, + { 150, 145, 170, 55 } +}; + +#endif /* _WIN32_WCE */ + diff --git a/backends/graphics/wincesdl/wincesdl-graphics.h b/backends/graphics/wincesdl/wincesdl-graphics.h new file mode 100644 index 0000000000..c8d683b158 --- /dev/null +++ b/backends/graphics/wincesdl/wincesdl-graphics.h @@ -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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef BACKENDS_GRAPHICS_WINCE_SDL_H +#define BACKENDS_GRAPHICS_WINCE_SDL_H + +#include "backends/graphics/sdl/sdl-graphics.h" +#include "backends/platform/wince/CEgui/CEGUI.h" + +// Internal GUI names +#define NAME_MAIN_PANEL "MainPanel" +#define NAME_PANEL_KEYBOARD "Keyboard" +#define NAME_ITEM_OPTIONS "Options" +#define NAME_ITEM_SKIP "Skip" +#define NAME_ITEM_SOUND "Sound" +#define NAME_ITEM_ORIENTATION "Orientation" +#define NAME_ITEM_BINDKEYS "Bindkeys" + +#define TOTAL_ZONES 3 + +extern bool _hasSmartphoneResolution; + +class WINCESdlGraphicsManager : public SdlGraphicsManager { +public: + WINCESdlGraphicsManager(SdlEventSource *sdlEventSource); + + const OSystem::GraphicsMode *getSupportedGraphicsModes() const; + void initSize(uint w, uint h, const Graphics::PixelFormat *format = NULL); + + bool hasFeature(OSystem::Feature f); + void setFeatureState(OSystem::Feature f, bool enable); + bool getFeatureState(OSystem::Feature f); + + int getDefaultGraphicsMode() const; + bool setGraphicsMode(int mode); + bool loadGFXMode(); + void unloadGFXMode(); + bool hotswapGFXMode(); + + // Overloaded from SDL backend (toolbar handling) + void drawMouse(); + // Overloaded from SDL backend (new scaler handling) + void addDirtyRect(int x, int y, int w, int h, bool mouseRect = false); + // Overloaded from SDL backend (new scaler handling) + void warpMouse(int x, int y); + + // Update the dirty areas of the screen + void internUpdateScreen(); + bool saveScreenshot(const char *filename); + + // Overloaded from SDL_Common (FIXME) + void internDrawMouse(); + void undrawMouse(); + bool showMouse(bool visible); + void setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y, uint32 keycolor, int cursorTargetScale, const Graphics::PixelFormat *format); // overloaded by CE backend + void copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h); + void copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h); // overloaded by CE backend (FIXME) + Graphics::Surface *lockScreen(); + void unlockScreen(); + void blitCursor(); + void showOverlay(); + void hideOverlay(); + void setMousePos(int x, int y); + + // GUI and action stuff + void swap_panel_visibility(); + void swap_panel(); + void swap_smartphone_keyboard(); + void swap_zoom_up(); + void swap_zoom_down(); + void swap_mouse_visibility(); + + +//#ifdef WIN32_PLATFORM_WFSP + void move_cursor_up(); + void move_cursor_down(); + void move_cursor_left(); + void move_cursor_right(); + + void retrieve_mouse_location(int &x, int &y); + void switch_zone(); + + void add_right_click(bool pushed); + void add_left_click(bool pushed); + + void initZones(); + void smartphone_rotate_display(); +//#endif + + bool _panelInitialized; // only initialize the toolbar once + bool _noDoubleTapRMB; // disable double tap -> rmb click + + CEGUI::ToolbarHandler _toolbarHandler; + + bool _toolbarHighDrawn; // cache toolbar 640x80 + int _newOrientation; // new orientation + int _orientationLandscape; // current orientation + + int _scaleFactorXm; // scaler X * + int _scaleFactorXd; // scaler X / + int _scaleFactorYm; // scaler Y * + int _scaleFactorYd; // scaler Y / + + bool _hasfocus; // scummvm has the top window + + bool hasPocketPCResolution(); + bool hasDesktopResolution(); + bool hasSquareQVGAResolution(); + bool hasWideResolution() const; + + MousePos _mouseCurState; + + bool _zoomUp; // zooming up mode + bool _zoomDown; // zooming down mode + + bool _usesEmulatedMouse; // emulated mousemove ever been used in this session + + int _mouseXZone[TOTAL_ZONES]; + int _mouseYZone[TOTAL_ZONES]; + int _currentZone; + + // Smartphone specific variables + int _lastKeyPressed; // last key pressed + int _keyRepeat; // number of time the last key was repeated + int _keyRepeatTime; // elapsed time since the key was pressed + int _keyRepeatTrigger; // minimum time to consider the key was repeated + + struct zoneDesc { + int x; + int y; + int width; + int height; + }; + + static zoneDesc _zones[TOTAL_ZONES]; + +protected: + virtual void adjustMouseEvent(const Common::Event &event); + +private: + bool update_scalers(); + void update_game_settings(); + void drawToolbarMouse(SDL_Surface *surf, bool draw); + + void create_toolbar(); + bool _panelVisible; // panel visibility + bool _panelStateForced; // panel visibility forced by external call + String _saveActiveToolbar; // save active toolbar when forced + + bool _canBeAspectScaled; // game screen size allows for aspect scaling + + SDL_Rect _dirtyRectOut[NUM_DIRTY_RECT]; + bool _scalersChanged; + + bool isOzone(); + + bool _saveToolbarState; // save visibility when forced + bool _saveToolbarZoom; // save visibility when zooming + + SDL_Surface *_toolbarLow; // toolbar 320x40 + SDL_Surface *_toolbarHigh; // toolbar 640x80 + + // Mouse + int _mouseHotspotX, _mouseHotspotY; + byte *_mouseBackupOld; + uint16 *_mouseBackupToolbar; + uint16 _mouseBackupDim; + + bool _forceHideMouse; // force invisible mouse cursor + + // Smartphone specific variables + void loadDeviceConfigurationElement(Common::String element, int &value, int defaultValue); + int _repeatX; // repeat trigger for left and right cursor moves + int _repeatY; // repeat trigger for up and down cursor moves + int _stepX1; // offset for left and right cursor moves (slowest) + int _stepX2; // offset for left and right cursor moves (faster) + int _stepX3; // offset for left and right cursor moves (fastest) + int _stepY1; // offset for up and down cursor moves (slowest) + int _stepY2; // offset for up and down cursor moves (faster) + int _stepY3; // offset for up and down cursor moves (fastest) +}; + +#endif /* BACKENDS_GRAPHICS_WINCE_SDL_H */ + diff --git a/backends/midi/alsa.cpp b/backends/midi/alsa.cpp index 953b088958..feed60eaef 100644 --- a/backends/midi/alsa.cpp +++ b/backends/midi/alsa.cpp @@ -44,7 +44,7 @@ #if SND_LIB_MAJOR >= 1 || SND_LIB_MINOR >= 6 #define snd_seq_flush_output(x) snd_seq_drain_output(x) -#define snd_seq_set_client_group(x,name) /*nop */ +#define snd_seq_set_client_group(x,name) /*nop */ #define my_snd_seq_open(seqp) snd_seq_open(seqp, "hw", SND_SEQ_OPEN_DUPLEX, 0) #else /* SND_SEQ_OPEN_OUT causes oops on early version of ALSA */ @@ -53,9 +53,8 @@ #define perm_ok(pinfo,bits) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits)) -static int check_permission(snd_seq_port_info_t *pinfo) -{ - if (perm_ok(pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE)) { +static int check_permission(snd_seq_port_info_t *pinfo) { + if (perm_ok(pinfo, SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)) { if (!(snd_seq_port_info_get_capability(pinfo) & SND_SEQ_PORT_CAP_NO_EXPORT)) return 1; } @@ -68,7 +67,7 @@ static int check_permission(snd_seq_port_info_t *pinfo) #define ADDR_DELIM ".:" -class MidiDriver_ALSA:public MidiDriver_MPU401 { +class MidiDriver_ALSA : public MidiDriver_MPU401 { public: MidiDriver_ALSA(int client, int port); int open(); @@ -83,11 +82,12 @@ private: snd_seq_t *seq_handle; int seq_client, seq_port; int my_client, my_port; + // The volume controller value of the first MIDI channel + int8 _channel0Volume; }; MidiDriver_ALSA::MidiDriver_ALSA(int client, int port) - : _isOpen(false), seq_handle(0), seq_client(client), seq_port(port), my_client(0), my_port(0) -{ + : _isOpen(false), seq_handle(0), seq_client(client), seq_port(port), my_client(0), my_port(0), _channel0Volume(127) { memset(&ev, 0, sizeof(ev)); } @@ -114,7 +114,7 @@ int MidiDriver_ALSA::open() { // with those capabilities. my_port = snd_seq_create_simple_port(seq_handle, "SCUMMVM port 0", 0, - SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION); + SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION); if (my_port < 0) { snd_seq_close(seq_handle); @@ -208,24 +208,41 @@ void MidiDriver_ALSA::send(uint32 b) { case 0xB0: /* is it this simple ? Wow... */ snd_seq_ev_set_controller(&ev, chanID, midiCmd[1], midiCmd[2]); + + // We save the volume of the first MIDI channel here to utilize it in + // our workaround for broken USB-MIDI cables. + if (chanID == 0 && midiCmd[1] == 0x07) + _channel0Volume = midiCmd[2]; + send_event(1); break; case 0xC0: snd_seq_ev_set_pgmchange(&ev, chanID, midiCmd[1]); send_event(0); + + // Send a volume change command to work around a firmware bug in common + // USB-MIDI cables. If the first MIDI command in a USB packet is a + // Cx or Dx command, the second command in the packet is dropped + // somewhere. + send(0x07B0 | (_channel0Volume << 16)); break; case 0xD0: snd_seq_ev_set_chanpress(&ev, chanID, midiCmd[1]); send_event(1); + + // Send a volume change command to work around a firmware bug in common + // USB-MIDI cables. If the first MIDI command in a USB packet is a + // Cx or Dx command, the second command in the packet is dropped + // somewhere. + send(0x07B0 | (_channel0Volume << 16)); break; - case 0xE0:{ - // long theBend = ((((long)midiCmd[1] + (long)(midiCmd[2] << 7))) - 0x2000) / 4; - // snd_seq_ev_set_pitchbend(&ev, chanID, theBend); - long theBend = ((long)midiCmd[1] + (long)(midiCmd[2] << 7)) - 0x2000; - snd_seq_ev_set_pitchbend(&ev, chanID, theBend); - send_event(1); - } - break; + case 0xE0: { + // long theBend = ((((long)midiCmd[1] + (long)(midiCmd[2] << 7))) - 0x2000) / 4; + // snd_seq_ev_set_pitchbend(&ev, chanID, theBend); + long theBend = ((long)midiCmd[1] + (long)(midiCmd[2] << 7)) - 0x2000; + snd_seq_ev_set_pitchbend(&ev, chanID, theBend); + send_event(1); + } break; default: warning("Unknown MIDI Command: %08x", (int)b); @@ -280,6 +297,9 @@ typedef Common::List<AlsaDevice> AlsaDevices; AlsaDevice::AlsaDevice(Common::String name, MusicType mt, int client) : _name(name), _type(mt), _client(client) { + // Make sure we do not get any trailing spaces to avoid problems when + // storing the name in the configuration file. + _name.trim(); } Common::String AlsaDevice::getName() { diff --git a/backends/mixer/wincesdl/wincesdl-mixer.cpp b/backends/mixer/wincesdl/wincesdl-mixer.cpp new file mode 100644 index 0000000000..fb8a7d10c7 --- /dev/null +++ b/backends/mixer/wincesdl/wincesdl-mixer.cpp @@ -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. + * + * $URL$ + * $Id$ + * + */ + +#ifdef _WIN32_WCE + +// Disable symbol overrides so that we can use system headers. +#define FORBIDDEN_SYMBOL_ALLOW_ALL + +#include "common/config-manager.h" +#include "backends/platform/wince/wince-sdl.h" +#include "backends/mixer/wincesdl/wincesdl-mixer.h" +#include "common/system.h" + +#ifdef USE_VORBIS +#ifndef USE_TREMOR +#include <vorbis/vorbisfile.h> +#else +#include <tremor/ivorbisfile.h> +#endif +#endif + +#define SAMPLES_PER_SEC_OLD 11025 +#define SAMPLES_PER_SEC_NEW 22050 + +WINCESdlMixerManager::WINCESdlMixerManager() { + +} + +WINCESdlMixerManager::~WINCESdlMixerManager() { + +} + +void WINCESdlMixerManager::init() { + SDL_AudioSpec desired; + int thread_priority; + + uint32 sampleRate = compute_sample_rate(); + if (sampleRate == 0) + warning("OSystem_WINCE3::setupMixer called with sample rate 0 - audio will not work"); + else if (_mixer && _mixer->getOutputRate() == sampleRate) { + debug(1, "Skipping sound mixer re-init: samplerate is good"); + return; + } + + memset(&desired, 0, sizeof(desired)); + desired.freq = sampleRate; + desired.format = AUDIO_S16SYS; + desired.channels = 2; + desired.samples = 128; + desired.callback = private_sound_proc; + desired.userdata = this; + + // Create the mixer instance + if (_mixer == 0) + _mixer = new Audio::MixerImpl(g_system, sampleRate); + + // Add sound thread priority + if (!ConfMan.hasKey("sound_thread_priority")) + thread_priority = THREAD_PRIORITY_NORMAL; + else + thread_priority = ConfMan.getInt("sound_thread_priority"); + + desired.thread_priority = thread_priority; + + SDL_CloseAudio(); + if (SDL_OpenAudio(&desired, NULL) != 0) { + warning("Could not open audio device: %s", SDL_GetError()); + _mixer->setReady(false); + + } else { + debug(1, "Sound opened OK, mixing at %d Hz", sampleRate); + + // Re-create mixer to match the output rate + int vol1 = _mixer->getVolumeForSoundType(Audio::Mixer::kPlainSoundType); + int vol2 = _mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType); + int vol3 = _mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType); + int vol4 = _mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType); + delete _mixer; + _mixer = new Audio::MixerImpl(g_system, sampleRate); + _mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, vol1); + _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, vol2); + _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, vol3); + _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, vol4); + _mixer->setReady(true); + SDL_PauseAudio(0); + } +} + +void WINCESdlMixerManager::private_sound_proc(void *param, byte *buf, int len) { + WINCESdlMixerManager *this_ = (WINCESdlMixerManager *)param; + assert(this_); + + if (this_->_mixer) + this_->_mixer->mixCallback(buf, len); + if (!OSystem_WINCE3::_soundMaster) + memset(buf, 0, len); +} + +uint32 WINCESdlMixerManager::compute_sample_rate() { + uint32 sampleRate; + + // Force at least medium quality FM synthesis for FOTAQ + Common::String gameid(ConfMan.get("gameid")); + if (gameid == "queen") { + if (!((ConfMan.hasKey("FM_high_quality") && ConfMan.getBool("FM_high_quality")) || + (ConfMan.hasKey("FM_medium_quality") && ConfMan.getBool("FM_medium_quality")))) { + ConfMan.setBool("FM_medium_quality", true); + ConfMan.flushToDisk(); + } + } + // See if the output frequency is forced by the game + if (gameid == "ft" || gameid == "dig" || gameid == "comi" || gameid == "queen" || gameid == "sword" || gameid == "agi") + sampleRate = SAMPLES_PER_SEC_NEW; + else { + if (ConfMan.hasKey("high_sample_rate") && ConfMan.getBool("high_sample_rate")) + sampleRate = SAMPLES_PER_SEC_NEW; + else + sampleRate = SAMPLES_PER_SEC_OLD; + } + +#ifdef USE_VORBIS + // Modify the sample rate on the fly if OGG is involved + if (sampleRate == SAMPLES_PER_SEC_OLD) + if (checkOggHighSampleRate()) + sampleRate = SAMPLES_PER_SEC_NEW; +#endif + + return sampleRate; +} + +#ifdef USE_VORBIS +bool WINCESdlMixerManager::checkOggHighSampleRate() { + char trackFile[255]; + FILE *testFile; + OggVorbis_File *test_ov_file = new OggVorbis_File; + + // FIXME: The following sprintf assumes that "path" is always + // terminated by a path separator. This is *not* true in general. + // This code really should check for the path separator, or even + // better, use the FSNode API. + sprintf(trackFile, "%sTrack1.ogg", ConfMan.get("path").c_str()); + // Check if we have an OGG audio track + testFile = fopen(trackFile, "rb"); + if (testFile) { + if (!ov_open(testFile, test_ov_file, NULL, 0)) { + bool highSampleRate = (ov_info(test_ov_file, -1)->rate == 22050); + ov_clear(test_ov_file); + delete test_ov_file; + return highSampleRate; + } + } + + // Do not test for OGG samples - too big and too slow anyway :) + + delete test_ov_file; + return false; +} +#endif + +#endif + diff --git a/backends/mixer/wincesdl/wincesdl-mixer.h b/backends/mixer/wincesdl/wincesdl-mixer.h new file mode 100644 index 0000000000..6c2f1efeee --- /dev/null +++ b/backends/mixer/wincesdl/wincesdl-mixer.h @@ -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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef BACKENDS_MIXER_WINCE_SDL_H +#define BACKENDS_MIXER_WINCE_SDL_H + +#include "backends/mixer/sdl/sdl-mixer.h" + +/** + * SDL mixer manager for WinCE + */ +class WINCESdlMixerManager : public SdlMixerManager { +public: + WINCESdlMixerManager(); + virtual ~WINCESdlMixerManager(); + + virtual void init(); + +private: + +#ifdef USE_VORBIS + bool checkOggHighSampleRate(); +#endif + + static void private_sound_proc(void *param, byte *buf, int len); + uint32 compute_sample_rate(); + +}; + +#endif + diff --git a/backends/module.mk b/backends/module.mk index 5a8a63c2a2..484f804ee0 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -12,6 +12,7 @@ MODULE_OBJS := \ events/samsungtvsdl/samsungtvsdl-events.o \ events/sdl/sdl-events.o \ events/symbiansdl/symbiansdl-events.o \ + events/wincesdl/wincesdl-events.o \ fs/abstract-fs.o \ fs/stdiostream.o \ fs/amigaos4/amigaos4-fs-factory.o \ @@ -27,6 +28,7 @@ MODULE_OBJS := \ graphics/openglsdl/openglsdl-graphics.o \ graphics/sdl/sdl-graphics.o \ graphics/symbiansdl/symbiansdl-graphics.o \ + graphics/wincesdl/wincesdl-graphics.o \ keymapper/action.o \ keymapper/keymap.o \ keymapper/keymapper.o \ @@ -44,6 +46,7 @@ MODULE_OBJS := \ mixer/doublebuffersdl/doublebuffersdl-mixer.o \ mixer/sdl/sdl-mixer.o \ mixer/symbiansdl/symbiansdl-mixer.o \ + mixer/wincesdl/wincesdl-mixer.o \ mutex/sdl/sdl-mutex.o \ plugins/elf/elf-loader.o \ plugins/elf/mips-loader.o \ diff --git a/backends/platform/android/android.cpp b/backends/platform/android/android.cpp index c49745f8bd..4e1373a1b1 100644 --- a/backends/platform/android/android.cpp +++ b/backends/platform/android/android.cpp @@ -25,22 +25,11 @@ #if defined(__ANDROID__) -#include "backends/base-backend.h" -#include "base/main.h" -#include "graphics/surface.h" - -#include "backends/platform/android/android.h" -#include "backends/platform/android/video.h" - -#include <jni.h> - -#include <string.h> -#include <unistd.h> -#include <pthread.h> #include <sys/time.h> +#include <sys/resource.h> #include <time.h> +#include <unistd.h> -#include "common/archive.h" #include "common/util.h" #include "common/rect.h" #include "common/queue.h" @@ -48,14 +37,12 @@ #include "common/events.h" #include "common/config-manager.h" -#include "backends/fs/posix/posix-fs-factory.h" #include "backends/keymapper/keymapper.h" #include "backends/saves/default/default-saves.h" #include "backends/timer/default/default-timer.h" -#include "backends/plugins/posix/posix-provider.h" -#include "audio/mixer_intern.h" -#include "backends/platform/android/asset-archive.h" +#include "backends/platform/android/jni.h" +#include "backends/platform/android/android.h" const char *android_log_tag = "ScummVM"; @@ -68,7 +55,8 @@ extern "C" { expr, file, line); } - void __assert2(const char *file, int line, const char *func, const char *expr) { + void __assert2(const char *file, int line, const char *func, + const char *expr) { __android_log_assert(expr, android_log_tag, "Assertion failure: '%s' in %s:%d (%s)", expr, file, line, func); @@ -106,235 +94,24 @@ void checkGlError(const char *expr, const char *file, int line) { } #endif -static JavaVM *cached_jvm; -static jfieldID FID_Event_type; -static jfieldID FID_Event_synthetic; -static jfieldID FID_Event_kbd_keycode; -static jfieldID FID_Event_kbd_ascii; -static jfieldID FID_Event_kbd_flags; -static jfieldID FID_Event_mouse_x; -static jfieldID FID_Event_mouse_y; -static jfieldID FID_Event_mouse_relative; -static jfieldID FID_ScummVM_nativeScummVM; -static jmethodID MID_Object_wait; - -JNIEnv *JNU_GetEnv() { - JNIEnv *env = 0; - - jint res = cached_jvm->GetEnv((void **)&env, JNI_VERSION_1_2); - - if (res != JNI_OK) { - LOGE("GetEnv() failed: %d", res); - abort(); - } - - return env; -} - -static void JNU_ThrowByName(JNIEnv *env, const char *name, const char *msg) { - jclass cls = env->FindClass(name); - - // if cls is 0, an exception has already been thrown - if (cls != 0) - env->ThrowNew(cls, msg); - - env->DeleteLocalRef(cls); -} - -// floating point. use sparingly -template <class T> -static inline T scalef(T in, float numerator, float denominator) { - return static_cast<float>(in) * numerator / denominator; -} - -static inline GLfixed xdiv(int numerator, int denominator) { - assert(numerator < (1 << 16)); - return (numerator << 16) / denominator; -} - -#ifdef DYNAMIC_MODULES -class AndroidPluginProvider : public POSIXPluginProvider { -protected: - virtual void addCustomDirectories(Common::FSList &dirs) const; -}; -#endif - -class OSystem_Android : public BaseBackend, public PaletteManager { -private: - // back pointer to (java) peer instance - jobject _back_ptr; - - jmethodID MID_displayMessageOnOSD; - jmethodID MID_setWindowCaption; - jmethodID MID_initBackend; - jmethodID MID_audioSampleRate; - jmethodID MID_showVirtualKeyboard; - jmethodID MID_getSysArchives; - jmethodID MID_getPluginDirectories; - jmethodID MID_setupScummVMSurface; - jmethodID MID_destroyScummVMSurface; - jmethodID MID_swapBuffers; - - int _screen_changeid; - int _egl_surface_width; - int _egl_surface_height; - - bool _force_redraw; - - // Game layer - GLESPaletteTexture *_game_texture; - int _shake_offset; - Common::Rect _focus_rect; - - // Overlay layer - GLES4444Texture *_overlay_texture; - bool _show_overlay; - - // Mouse layer - GLESPaletteATexture *_mouse_texture; - Common::Point _mouse_hotspot; - int _mouse_targetscale; - bool _show_mouse; - bool _use_mouse_palette; - - Common::Queue<Common::Event> _event_queue; - MutexRef _event_queue_lock; - - bool _timer_thread_exit; - pthread_t _timer_thread; - static void *timerThreadFunc(void *arg); - - bool _enable_zoning; - bool _virtkeybd_on; - - Common::SaveFileManager *_savefile; - Audio::MixerImpl *_mixer; - Common::TimerManager *_timer; - FilesystemFactory *_fsFactory; - Common::Archive *_asset_archive; - timeval _startTime; - - void setupScummVMSurface(); - void destroyScummVMSurface(); - void setupKeymapper(); - void _setCursorPalette(const byte *colors, uint start, uint num); - -public: - OSystem_Android(jobject am); - virtual ~OSystem_Android(); - bool initJavaHooks(JNIEnv *env, jobject self); - - static OSystem_Android *fromJavaObject(JNIEnv *env, jobject obj); - virtual void initBackend(); - void addPluginDirectories(Common::FSList &dirs) const; - void enableZoning(bool enable) { _enable_zoning = enable; } - void setSurfaceSize(int width, int height) { - _egl_surface_width = width; - _egl_surface_height = height; - } - - virtual bool hasFeature(Feature f); - virtual void setFeatureState(Feature f, bool enable); - virtual bool getFeatureState(Feature f); - virtual const GraphicsMode *getSupportedGraphicsModes() const; - virtual int getDefaultGraphicsMode() const; - bool setGraphicsMode(const char *name); - virtual bool setGraphicsMode(int mode); - virtual int getGraphicsMode() const; - virtual void initSize(uint width, uint height, - const Graphics::PixelFormat *format); - - virtual int getScreenChangeID() const { - return _screen_changeid; - } - - virtual int16 getHeight(); - virtual int16 getWidth(); - - virtual PaletteManager *getPaletteManager() { - return this; - } - -protected: - // PaletteManager API - virtual void setPalette(const byte *colors, uint start, uint num); - virtual void grabPalette(byte *colors, uint start, uint num); - -public: - virtual void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h); - virtual void updateScreen(); - virtual Graphics::Surface *lockScreen(); - virtual void unlockScreen(); - virtual void setShakePos(int shakeOffset); - virtual void fillScreen(uint32 col); - virtual void setFocusRectangle(const Common::Rect& rect); - virtual void clearFocusRectangle(); - - virtual void showOverlay(); - virtual void hideOverlay(); - virtual void clearOverlay(); - virtual void grabOverlay(OverlayColor *buf, int pitch); - virtual void copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h); - virtual int16 getOverlayHeight(); - virtual int16 getOverlayWidth(); - - // RGBA 4444 - virtual Graphics::PixelFormat getOverlayFormat() const { - Graphics::PixelFormat format; - - format.bytesPerPixel = 2; - format.rLoss = 8 - 4; - format.gLoss = 8 - 4; - format.bLoss = 8 - 4; - format.aLoss = 8 - 4; - format.rShift = 3 * 4; - format.gShift = 2 * 4; - format.bShift = 1 * 4; - format.aShift = 0 * 4; - - return format; - } - - virtual bool showMouse(bool visible); - - virtual void warpMouse(int x, int y); - virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int cursorTargetScale, const Graphics::PixelFormat *format); - virtual void setCursorPalette(const byte *colors, uint start, uint num); - virtual void disableCursorPalette(bool disable); - - virtual bool pollEvent(Common::Event &event); - void pushEvent(const Common::Event& event); - virtual uint32 getMillis(); - virtual void delayMillis(uint msecs); - - virtual MutexRef createMutex(void); - virtual void lockMutex(MutexRef mutex); - virtual void unlockMutex(MutexRef mutex); - virtual void deleteMutex(MutexRef mutex); - - virtual void quit(); - - virtual void setWindowCaption(const char *caption); - virtual void displayMessageOnOSD(const char *msg); - virtual void showVirtualKeyboard(bool enable); - - virtual Common::SaveFileManager *getSavefileManager(); - virtual Audio::Mixer *getMixer(); - virtual void getTimeAndDate(TimeDate &t) const; - virtual Common::TimerManager *getTimerManager(); - virtual FilesystemFactory *getFilesystemFactory(); - virtual void logMessage(LogMessageType::Type type, const char *message); - virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0); -}; - -OSystem_Android::OSystem_Android(jobject am) : - _back_ptr(0), +OSystem_Android::OSystem_Android(int audio_sample_rate, int audio_buffer_size) : + _audio_sample_rate(audio_sample_rate), + _audio_buffer_size(audio_buffer_size), _screen_changeid(0), + _egl_surface_width(0), + _egl_surface_height(0), _force_redraw(false), _game_texture(0), _overlay_texture(0), _mouse_texture(0), + _mouse_texture_palette(0), + _mouse_texture_rgb(0), + _mouse_hotspot(), + _mouse_keycolor(0), _use_mouse_palette(false), + _fullscreen(false), + _graphicsMode(0), + _ar_correction(false), _show_mouse(false), _show_overlay(false), _enable_zoning(false), @@ -342,876 +119,288 @@ OSystem_Android::OSystem_Android(jobject am) : _mixer(0), _timer(0), _fsFactory(new POSIXFilesystemFactory()), - _asset_archive(new AndroidAssetArchive(am)), _shake_offset(0), - _event_queue_lock(createMutex()) { + _event_queue_lock(createMutex()), + _touch_pt_down(), + _touch_pt_dt(), + _eventScaleX(100), + _eventScaleY(100), + // TODO put these values in some option dlg? + _touchpad_mode(true), + _touchpad_scale(50), + _dpad_scale(4), + _trackball_scale(2) { } OSystem_Android::~OSystem_Android() { ENTER(); - delete _game_texture; - delete _overlay_texture; - delete _mouse_texture; - - destroyScummVMSurface(); - - JNIEnv *env = JNU_GetEnv(); - //env->DeleteWeakGlobalRef(_back_ptr); - env->DeleteGlobalRef(_back_ptr); - delete _savefile; - delete _mixer; delete _timer; + delete _mixer; delete _fsFactory; - delete _asset_archive; deleteMutex(_event_queue_lock); } -OSystem_Android *OSystem_Android::fromJavaObject(JNIEnv *env, jobject obj) { - jlong peer = env->GetLongField(obj, FID_ScummVM_nativeScummVM); - return (OSystem_Android *)peer; -} - -bool OSystem_Android::initJavaHooks(JNIEnv *env, jobject self) { - // weak global ref to allow class to be unloaded - // ... except dalvik doesn't implement NewWeakGlobalRef (yet) - //_back_ptr = env->NewWeakGlobalRef(self); - _back_ptr = env->NewGlobalRef(self); - - jclass cls = env->GetObjectClass(_back_ptr); - -#define FIND_METHOD(name, signature) do { \ - MID_ ## name = env->GetMethodID(cls, #name, signature); \ - if (MID_ ## name == 0) \ - return false; \ - } while (0) - - FIND_METHOD(setWindowCaption, "(Ljava/lang/String;)V"); - FIND_METHOD(displayMessageOnOSD, "(Ljava/lang/String;)V"); - FIND_METHOD(initBackend, "()V"); - FIND_METHOD(audioSampleRate, "()I"); - FIND_METHOD(showVirtualKeyboard, "(Z)V"); - FIND_METHOD(getSysArchives, "()[Ljava/lang/String;"); - FIND_METHOD(getPluginDirectories, "()[Ljava/lang/String;"); - FIND_METHOD(setupScummVMSurface, "()V"); - FIND_METHOD(destroyScummVMSurface, "()V"); - FIND_METHOD(swapBuffers, "()Z"); - -#undef FIND_METHOD - - return true; -} - -static void ScummVM_create(JNIEnv *env, jobject self, jobject am) { - OSystem_Android *cpp_obj = new OSystem_Android(am); - - // Exception already thrown by initJavaHooks? - if (!cpp_obj->initJavaHooks(env, self)) - return; - - env->SetLongField(self, FID_ScummVM_nativeScummVM, (jlong)cpp_obj); - -#ifdef DYNAMIC_MODULES - PluginManager::instance().addPluginProvider(new AndroidPluginProvider()); -#endif -} - -static void ScummVM_nativeDestroy(JNIEnv *env, jobject self) { - OSystem_Android *cpp_obj = OSystem_Android::fromJavaObject(env, self); - delete cpp_obj; -} - -static void ScummVM_audioMixCallback(JNIEnv *env, jobject self, - jbyteArray jbuf) { - OSystem_Android *cpp_obj = OSystem_Android::fromJavaObject(env, self); - jsize len = env->GetArrayLength(jbuf); - jbyte *buf = env->GetByteArrayElements(jbuf, 0); - - if (buf == 0) { - warning("Unable to get Java audio byte array. Skipping"); - return; - } - - Audio::MixerImpl *mixer = - static_cast<Audio::MixerImpl *>(cpp_obj->getMixer()); - assert(mixer); - mixer->mixCallback(reinterpret_cast<byte *>(buf), len); - - env->ReleaseByteArrayElements(jbuf, buf, 0); -} - -static void ScummVM_setConfManInt(JNIEnv *env, jclass cls, - jstring key_obj, jint value) { - ENTER("%p, %d", key_obj, (int)value); - - const char *key = env->GetStringUTFChars(key_obj, 0); - - if (key == 0) - return; - - ConfMan.setInt(key, value); - - env->ReleaseStringUTFChars(key_obj, key); -} - -static void ScummVM_setConfManString(JNIEnv *env, jclass cls, jstring key_obj, - jstring value_obj) { - ENTER("%p, %p", key_obj, value_obj); - - const char *key = env->GetStringUTFChars(key_obj, 0); - - if (key == 0) - return; - - const char *value = env->GetStringUTFChars(value_obj, 0); - - if (value == 0) { - env->ReleaseStringUTFChars(key_obj, key); - return; - } - - ConfMan.set(key, value); - - env->ReleaseStringUTFChars(value_obj, value); - env->ReleaseStringUTFChars(key_obj, key); -} - void *OSystem_Android::timerThreadFunc(void *arg) { OSystem_Android *system = (OSystem_Android *)arg; DefaultTimerManager *timer = (DefaultTimerManager *)(system->_timer); - JNIEnv *env = 0; - jint res = cached_jvm->AttachCurrentThread(&env, 0); + // renice this thread to boost the audio thread + if (setpriority(PRIO_PROCESS, 0, 19) < 0) + LOGW("couldn't renice the timer thread"); - if (res != JNI_OK) { - LOGE("AttachCurrentThread() failed: %d", res); - abort(); - } + JNI::attachThread(); struct timespec tv; tv.tv_sec = 0; tv.tv_nsec = 100 * 1000 * 1000; // 100ms while (!system->_timer_thread_exit) { + if (JNI::pause) { + LOGD("timer thread going to sleep"); + sem_wait(&JNI::pause_sem); + LOGD("timer thread woke up"); + } + timer->handler(); nanosleep(&tv, 0); } - res = cached_jvm->DetachCurrentThread(); - - if (res != JNI_OK) { - LOGE("DetachCurrentThread() failed: %d", res); - abort(); - } + JNI::detachThread(); return 0; } -void OSystem_Android::initBackend() { - ENTER(); - - JNIEnv *env = JNU_GetEnv(); - - ConfMan.setInt("autosave_period", 0); - ConfMan.setInt("FM_medium_quality", true); - - // must happen before creating TimerManager to avoid race in - // creating EventManager - setupKeymapper(); - - // BUG: "transient" ConfMan settings get nuked by the options - // screen. Passing the savepath in this way makes it stick - // (via ConfMan.registerDefault) - _savefile = new DefaultSaveFileManager(ConfMan.get("savepath")); - _timer = new DefaultTimerManager(); - - gettimeofday(&_startTime, 0); - - jint sample_rate = env->CallIntMethod(_back_ptr, MID_audioSampleRate); - if (env->ExceptionCheck()) { - warning("Error finding audio sample rate - assuming 11025HZ"); - - env->ExceptionDescribe(); - env->ExceptionClear(); - - sample_rate = 11025; - } - - _mixer = new Audio::MixerImpl(this, sample_rate); - _mixer->setReady(true); - - env->CallVoidMethod(_back_ptr, MID_initBackend); +void *OSystem_Android::audioThreadFunc(void *arg) { + JNI::attachThread(); - if (env->ExceptionCheck()) { - error("Error in Java initBackend"); - - env->ExceptionDescribe(); - env->ExceptionClear(); - } - - _timer_thread_exit = false; - pthread_create(&_timer_thread, 0, timerThreadFunc, this); + OSystem_Android *system = (OSystem_Android *)arg; + Audio::MixerImpl *mixer = system->_mixer; - OSystem::initBackend(); + uint buf_size = system->_audio_buffer_size; - setupScummVMSurface(); -} + JNIEnv *env = JNI::getEnv(); -void OSystem_Android::addPluginDirectories(Common::FSList &dirs) const { - ENTER(); + jbyteArray bufa = env->NewByteArray(buf_size); - JNIEnv *env = JNU_GetEnv(); + bool paused = true; - jobjectArray array = - (jobjectArray)env->CallObjectMethod(_back_ptr, MID_getPluginDirectories); - if (env->ExceptionCheck()) { - warning("Error finding plugin directories"); + byte *buf; + int offset, left, written; + int samples, i; - env->ExceptionDescribe(); - env->ExceptionClear(); + struct timespec tv_delay; + tv_delay.tv_sec = 0; + tv_delay.tv_nsec = 20 * 1000 * 1000; - return; - } + uint msecs_full = buf_size * 1000 / (mixer->getOutputRate() * 2 * 2); - jsize size = env->GetArrayLength(array); - for (jsize i = 0; i < size; ++i) { - jstring path_obj = (jstring)env->GetObjectArrayElement(array, i); + struct timespec tv_full; + tv_full.tv_sec = 0; + tv_full.tv_nsec = msecs_full * 1000 * 1000; - if (path_obj == 0) - continue; + bool silence; + uint silence_count = 33; - const char *path = env->GetStringUTFChars(path_obj, 0); - if (path == 0) { - warning("Error getting string characters from plugin directory"); + while (!system->_audio_thread_exit) { + if (JNI::pause) { + JNI::setAudioStop(); - env->ExceptionClear(); - env->DeleteLocalRef(path_obj); + paused = true; + silence_count = 33; - continue; + LOGD("audio thread going to sleep"); + sem_wait(&JNI::pause_sem); + LOGD("audio thread woke up"); } - dirs.push_back(Common::FSNode(path)); - - env->ReleaseStringUTFChars(path_obj, path); - env->DeleteLocalRef(path_obj); - } -} - -bool OSystem_Android::hasFeature(Feature f) { - return (f == kFeatureCursorHasPalette || - f == kFeatureVirtualKeyboard || - f == kFeatureOverlaySupportsAlpha); -} - -void OSystem_Android::setFeatureState(Feature f, bool enable) { - ENTER("%d, %d", f, enable); - - switch (f) { - case kFeatureVirtualKeyboard: - _virtkeybd_on = enable; - showVirtualKeyboard(enable); - break; - default: - break; - } -} - -bool OSystem_Android::getFeatureState(Feature f) { - switch (f) { - case kFeatureVirtualKeyboard: - return _virtkeybd_on; - default: - return false; - } -} - -const OSystem::GraphicsMode *OSystem_Android::getSupportedGraphicsModes() const { - static const OSystem::GraphicsMode s_supportedGraphicsModes[] = { - { "default", "Default", 1 }, - { 0, 0, 0 }, - }; - - return s_supportedGraphicsModes; -} - - -int OSystem_Android::getDefaultGraphicsMode() const { - return 1; -} - -bool OSystem_Android::setGraphicsMode(const char *mode) { - ENTER("%s", mode); - return true; -} - -bool OSystem_Android::setGraphicsMode(int mode) { - ENTER("%d", mode); - return true; -} - -int OSystem_Android::getGraphicsMode() const { - return 1; -} - -void OSystem_Android::setupScummVMSurface() { - ENTER(); - - JNIEnv *env = JNU_GetEnv(); - env->CallVoidMethod(_back_ptr, MID_setupScummVMSurface); - - if (env->ExceptionCheck()) - return; - - // EGL set up with a new surface. Initialise OpenGLES context. - GLESTexture::initGLExtensions(); - - // Turn off anything that looks like 3D ;) - GLCALL(glDisable(GL_CULL_FACE)); - GLCALL(glDisable(GL_DEPTH_TEST)); - GLCALL(glDisable(GL_LIGHTING)); - GLCALL(glDisable(GL_FOG)); - GLCALL(glDisable(GL_DITHER)); - - GLCALL(glShadeModel(GL_FLAT)); - GLCALL(glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST)); - - GLCALL(glEnable(GL_BLEND)); - GLCALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - - GLCALL(glEnableClientState(GL_VERTEX_ARRAY)); - GLCALL(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); - - GLCALL(glEnable(GL_TEXTURE_2D)); - - if (!_game_texture) - _game_texture = new GLESPaletteTexture(); - else - _game_texture->reinitGL(); - - if (!_overlay_texture) - _overlay_texture = new GLES4444Texture(); - else - _overlay_texture->reinitGL(); - - if (!_mouse_texture) - _mouse_texture = new GLESPaletteATexture(); - else - _mouse_texture->reinitGL(); - - GLCALL(glViewport(0, 0, _egl_surface_width, _egl_surface_height)); - - GLCALL(glMatrixMode(GL_PROJECTION)); - GLCALL(glLoadIdentity()); - GLCALL(glOrthof(0, _egl_surface_width, _egl_surface_height, 0, -1, 1)); - GLCALL(glMatrixMode(GL_MODELVIEW)); - GLCALL(glLoadIdentity()); - - clearFocusRectangle(); -} - -void OSystem_Android::destroyScummVMSurface() { - JNIEnv *env = JNU_GetEnv(); - env->CallVoidMethod(_back_ptr, MID_destroyScummVMSurface); - // Can't use OpenGLES functions after this -} - -void OSystem_Android::initSize(uint width, uint height, - const Graphics::PixelFormat *format) { - ENTER("%d, %d, %p", width, height, format); - - _game_texture->allocBuffer(width, height); - - // Cap at 320x200 or the ScummVM themes abort :/ - GLuint overlay_width = MIN(_egl_surface_width, 320); - GLuint overlay_height = MIN(_egl_surface_height, 200); - _overlay_texture->allocBuffer(overlay_width, overlay_height); - - // Don't know mouse size yet - it gets reallocated in - // setMouseCursor. We need the palette allocated before - // setMouseCursor however, so just take a guess at the desired - // size (it's small). - _mouse_texture->allocBuffer(20, 20); -} - -int16 OSystem_Android::getHeight() { - return _game_texture->height(); -} - -int16 OSystem_Android::getWidth() { - return _game_texture->width(); -} - -void OSystem_Android::setPalette(const byte *colors, uint start, uint num) { - ENTER("%p, %u, %u", colors, start, num); + buf = (byte *)env->GetPrimitiveArrayCritical(bufa, 0); + assert(buf); - if (!_use_mouse_palette) - _setCursorPalette(colors, start, num); + samples = mixer->mixCallback(buf, buf_size); - memcpy(_game_texture->palette() + start * 3, colors, num * 3); -} - -void OSystem_Android::grabPalette(byte *colors, uint start, uint num) { - ENTER("%p, %u, %u", colors, start, num); - memcpy(colors, _game_texture->palette_const() + start * 3, num * 3); -} - -void OSystem_Android::copyRectToScreen(const byte *buf, int pitch, - int x, int y, int w, int h) { - ENTER("%p, %d, %d, %d, %d, %d", buf, pitch, x, y, w, h); - - _game_texture->updateBuffer(x, y, w, h, buf, pitch); -} - -void OSystem_Android::updateScreen() { - //ENTER(); - - if (!_force_redraw && - !_game_texture->dirty() && - !_overlay_texture->dirty() && - !_mouse_texture->dirty()) - return; - - _force_redraw = false; - - GLCALL(glPushMatrix()); - - if (_shake_offset != 0 || - (!_focus_rect.isEmpty() && - !Common::Rect(_game_texture->width(), - _game_texture->height()).contains(_focus_rect))) { - // These are the only cases where _game_texture doesn't - // cover the entire screen. - GLCALL(glClearColorx(0, 0, 0, 1 << 16)); - GLCALL(glClear(GL_COLOR_BUFFER_BIT)); - - // Move everything up by _shake_offset (game) pixels - GLCALL(glTranslatex(0, -_shake_offset << 16, 0)); - } - - if (_focus_rect.isEmpty()) { - _game_texture->drawTexture(0, 0, - _egl_surface_width, _egl_surface_height); - } else { - GLCALL(glPushMatrix()); - GLCALL(glScalex(xdiv(_egl_surface_width, _focus_rect.width()), - xdiv(_egl_surface_height, _focus_rect.height()), - 1 << 16)); - GLCALL(glTranslatex(-_focus_rect.left << 16, - -_focus_rect.top << 16, 0)); - GLCALL(glScalex(xdiv(_game_texture->width(), _egl_surface_width), - xdiv(_game_texture->height(), _egl_surface_height), - 1 << 16)); - - _game_texture->drawTexture(0, 0, - _egl_surface_width, _egl_surface_height); - GLCALL(glPopMatrix()); - } - - int cs = _mouse_targetscale; - - if (_show_overlay) { - // ugly, but the modern theme sets a wacko factor, only god knows why - cs = 1; - - GLCALL(_overlay_texture->drawTexture(0, 0, - _egl_surface_width, - _egl_surface_height)); - } - - if (_show_mouse) { - GLCALL(glPushMatrix()); + silence = samples < 1; - // Scale up ScummVM -> OpenGL (pixel) coordinates - int texwidth, texheight; + // looks stupid, and it is, but currently there's no way to detect + // silence-only buffers from the mixer + if (!silence) { + silence = true; - if (_show_overlay) { - texwidth = getOverlayWidth(); - texheight = getOverlayHeight(); - } else { - texwidth = getWidth(); - texheight = getHeight(); + for (i = 0; i < samples; i += 2) + // SID streams constant crap + if (READ_UINT16(buf + i) > 32) { + silence = false; + break; + } } - GLCALL(glScalex(xdiv(_egl_surface_width, texwidth), - xdiv(_egl_surface_height, texheight), - 1 << 16)); - - GLCALL(glTranslatex((-_mouse_hotspot.x * cs) << 16, - (-_mouse_hotspot.y * cs) << 16, - 0)); - - // Note the extra half texel to position the mouse in - // the middle of the x,y square: - const Common::Point& mouse = getEventManager()->getMousePos(); - GLCALL(glTranslatex((mouse.x << 16) | 1 << 15, - (mouse.y << 16) | 1 << 15, 0)); - - GLCALL(glScalex(cs << 16, cs << 16, 1 << 16)); - - _mouse_texture->drawTexture(); + env->ReleasePrimitiveArrayCritical(bufa, buf, 0); - GLCALL(glPopMatrix()); - } - - GLCALL(glPopMatrix()); + if (silence) { + if (!paused) + silence_count++; - JNIEnv *env = JNU_GetEnv(); - if (!env->CallBooleanMethod(_back_ptr, MID_swapBuffers)) { - // Context lost -> need to reinit GL - destroyScummVMSurface(); - setupScummVMSurface(); - } -} + // only pause after a while to prevent toggle mania + if (silence_count > 32) { + if (!paused) { + LOGD("AudioTrack pause"); -Graphics::Surface *OSystem_Android::lockScreen() { - ENTER(); + JNI::setAudioPause(); + paused = true; + } - Graphics::Surface *surface = _game_texture->surface(); - assert(surface->pixels); + nanosleep(&tv_full, 0); - return surface; -} + continue; + } + } -void OSystem_Android::unlockScreen() { - ENTER(); + if (paused) { + LOGD("AudioTrack play"); - assert(_game_texture->dirty()); -} + JNI::setAudioPlay(); + paused = false; -void OSystem_Android::setShakePos(int shake_offset) { - ENTER("%d", shake_offset); + silence_count = 0; + } - if (_shake_offset != shake_offset) { - _shake_offset = shake_offset; - _force_redraw = true; - } -} + offset = 0; + left = buf_size; + written = 0; -void OSystem_Android::fillScreen(uint32 col) { - ENTER("%u", col); + while (left > 0) { + written = JNI::writeAudio(env, bufa, offset, left); - assert(col < 256); - _game_texture->fillBuffer(col); -} + if (written < 0) { + LOGE("AudioTrack error: %d", written); + break; + } -void OSystem_Android::setFocusRectangle(const Common::Rect& rect) { - ENTER("%d, %d, %d, %d", rect.left, rect.top, rect.right, rect.bottom); + // buffer full + if (written < left) + nanosleep(&tv_delay, 0); - if (_enable_zoning) { - _focus_rect = rect; - _force_redraw = true; - } -} + offset += written; + left -= written; + } -void OSystem_Android::clearFocusRectangle() { - ENTER(); + if (written < 0) + break; - if (_enable_zoning) { - _focus_rect = Common::Rect(); - _force_redraw = true; + // prepare the next buffer, and run into the blocking AudioTrack.write } -} -void OSystem_Android::showOverlay() { - ENTER(); + JNI::setAudioStop(); - _show_overlay = true; - _force_redraw = true; -} + env->DeleteLocalRef(bufa); -void OSystem_Android::hideOverlay() { - ENTER(); + JNI::detachThread(); - _show_overlay = false; - _force_redraw = true; + return 0; } -void OSystem_Android::clearOverlay() { +void OSystem_Android::initBackend() { ENTER(); - _overlay_texture->fillBuffer(0); - - // Shouldn't need this, but works around a 'blank screen' bug on Nexus1 - updateScreen(); -} - -void OSystem_Android::grabOverlay(OverlayColor *buf, int pitch) { - ENTER("%p, %d", buf, pitch); - - // We support overlay alpha blending, so the pixel data here - // shouldn't actually be used. Let's fill it with zeros, I'm sure - // it will be fine... - const Graphics::Surface *surface = _overlay_texture->surface_const(); - assert(surface->bytesPerPixel == sizeof(buf[0])); - - int h = surface->h; - - do { - memset(buf, 0, surface->w * sizeof(buf[0])); - - // This 'pitch' is pixels not bytes - buf += pitch; - } while (--h); -} - -void OSystem_Android::copyRectToOverlay(const OverlayColor *buf, int pitch, - int x, int y, int w, int h) { - ENTER("%p, %d, %d, %d, %d, %d", buf, pitch, x, y, w, h); - - const Graphics::Surface *surface = _overlay_texture->surface_const(); - assert(surface->bytesPerPixel == sizeof(buf[0])); - - // This 'pitch' is pixels not bytes - _overlay_texture->updateBuffer(x, y, w, h, buf, pitch * sizeof(buf[0])); - - // Shouldn't need this, but works around a 'blank screen' bug on Nexus1? - updateScreen(); -} + _main_thread = pthread_self(); -int16 OSystem_Android::getOverlayHeight() { - return _overlay_texture->height(); -} - -int16 OSystem_Android::getOverlayWidth() { - return _overlay_texture->width(); -} - -bool OSystem_Android::showMouse(bool visible) { - ENTER("%d", visible); - - _show_mouse = visible; - - return true; -} - -void OSystem_Android::warpMouse(int x, int y) { - ENTER("%d, %d", x, y); - - // We use only the eventmanager's idea of the current mouse - // position, so there is nothing extra to do here. -} - -void OSystem_Android::setMouseCursor(const byte *buf, uint w, uint h, - int hotspotX, int hotspotY, - uint32 keycolor, int cursorTargetScale, - const Graphics::PixelFormat *format) { - ENTER("%p, %u, %u, %d, %d, %u, %d, %p", buf, w, h, hotspotX, hotspotY, - keycolor, cursorTargetScale, format); + ConfMan.registerDefault("fullscreen", true); + ConfMan.registerDefault("aspect_ratio", true); - assert(keycolor < 256); + ConfMan.setInt("autosave_period", 0); + ConfMan.setBool("FM_high_quality", false); + ConfMan.setBool("FM_medium_quality", true); - _mouse_texture->allocBuffer(w, h); + // TODO hackity hack + if (ConfMan.hasKey("multi_midi")) + _touchpad_mode = !ConfMan.getBool("multi_midi"); - // Update palette alpha based on keycolor - byte *palette = _mouse_texture->palette(); - int i = 256; + // must happen before creating TimerManager to avoid race in + // creating EventManager + setupKeymapper(); - do { - palette[3] = 0xff; - palette += 4; - } while (--i); + // BUG: "transient" ConfMan settings get nuked by the options + // screen. Passing the savepath in this way makes it stick + // (via ConfMan.registerDefault) + _savefile = new DefaultSaveFileManager(ConfMan.get("savepath")); + _timer = new DefaultTimerManager(); - palette = _mouse_texture->palette(); - palette[keycolor * 4 + 3] = 0x00; + gettimeofday(&_startTime, 0); - _mouse_texture->updateBuffer(0, 0, w, h, buf, w); + _mixer = new Audio::MixerImpl(this, _audio_sample_rate); + _mixer->setReady(true); - _mouse_hotspot = Common::Point(hotspotX, hotspotY); - _mouse_targetscale = cursorTargetScale; -} + _timer_thread_exit = false; + pthread_create(&_timer_thread, 0, timerThreadFunc, this); -void OSystem_Android::_setCursorPalette(const byte *colors, - uint start, uint num) { - byte *palette = _mouse_texture->palette() + start * 4; + _audio_thread_exit = false; + pthread_create(&_audio_thread, 0, audioThreadFunc, this); - do { - for (int i = 0; i < 3; ++i) - palette[i] = colors[i]; + initSurface(); + initViewport(); - // Leave alpha untouched to preserve keycolor + _game_texture = new GLESFakePalette565Texture(); + _overlay_texture = new GLES4444Texture(); + _mouse_texture_palette = new GLESPalette5551Texture(); + _mouse_texture = _mouse_texture_palette; - palette += 4; - colors += 3; - } while (--num); -} + initOverlay(); -void OSystem_Android::setCursorPalette(const byte *colors, - uint start, uint num) { - ENTER("%p, %u, %u", colors, start, num); + // renice this thread to boost the audio thread + if (setpriority(PRIO_PROCESS, 0, 19) < 0) + warning("couldn't renice the main thread"); - _setCursorPalette(colors, start, num); - _use_mouse_palette = true; + JNI::setReadyForEvents(true); } -void OSystem_Android::disableCursorPalette(bool disable) { - ENTER("%d", disable); +void OSystem_Android::addPluginDirectories(Common::FSList &dirs) const { + ENTER(); - _use_mouse_palette = !disable; + JNI::getPluginDirectories(dirs); } -void OSystem_Android::setupKeymapper() { -#ifdef ENABLE_KEYMAPPER - using namespace Common; - - Keymapper *mapper = getEventManager()->getKeymapper(); - - HardwareKeySet *keySet = new HardwareKeySet(); - - keySet->addHardwareKey( - new HardwareKey("n", KeyState(KEYCODE_n), "n (vk)", - kTriggerLeftKeyType, - kVirtualKeyboardActionType)); - - mapper->registerHardwareKeySet(keySet); - - Keymap *globalMap = new Keymap("global"); - Action *act; - - act = new Action(globalMap, "VIRT", "Display keyboard", - kVirtualKeyboardActionType); - act->addKeyEvent(KeyState(KEYCODE_F7, ASCII_F7, 0)); - - mapper->addGlobalKeymap(globalMap); - - mapper->pushKeymap("global"); -#endif +bool OSystem_Android::hasFeature(Feature f) { + return (f == kFeatureFullscreenMode || + f == kFeatureAspectRatioCorrection || + f == kFeatureCursorHasPalette || + f == kFeatureVirtualKeyboard || + f == kFeatureOverlaySupportsAlpha); } -bool OSystem_Android::pollEvent(Common::Event &event) { - //ENTER(); - - lockMutex(_event_queue_lock); - - if (_event_queue.empty()) { - unlockMutex(_event_queue_lock); - return false; - } +void OSystem_Android::setFeatureState(Feature f, bool enable) { + ENTER("%d, %d", f, enable); - event = _event_queue.pop(); - unlockMutex(_event_queue_lock); - - switch (event.type) { - case Common::EVENT_MOUSEMOVE: - // TODO: only dirty/redraw move bounds - _force_redraw = true; - // fallthrough - case Common::EVENT_LBUTTONDOWN: - case Common::EVENT_LBUTTONUP: - case Common::EVENT_RBUTTONDOWN: - case Common::EVENT_RBUTTONUP: - case Common::EVENT_WHEELUP: - case Common::EVENT_WHEELDOWN: - case Common::EVENT_MBUTTONDOWN: - case Common::EVENT_MBUTTONUP: { - // relative mouse hack - if (event.kbd.flags == 1) { - // Relative (trackball) mouse hack. - const Common::Point& mouse_pos = - getEventManager()->getMousePos(); - event.mouse.x += mouse_pos.x; - event.mouse.y += mouse_pos.y; - event.mouse.x = CLIP(event.mouse.x, (int16)0, _show_overlay ? - getOverlayWidth() : getWidth()); - event.mouse.y = CLIP(event.mouse.y, (int16)0, _show_overlay ? - getOverlayHeight() : getHeight()); - } else { - // Touchscreen events need to be converted - // from device to game coords first. - const GLESTexture *tex = _show_overlay - ? static_cast<GLESTexture *>(_overlay_texture) - : static_cast<GLESTexture *>(_game_texture); - event.mouse.x = scalef(event.mouse.x, tex->width(), - _egl_surface_width); - event.mouse.y = scalef(event.mouse.y, tex->height(), - _egl_surface_height); - event.mouse.x -= _shake_offset; - } + switch (f) { + case kFeatureFullscreenMode: + _fullscreen = enable; + updateScreenRect(); break; - } - case Common::EVENT_SCREEN_CHANGED: - debug("EVENT_SCREEN_CHANGED"); - _screen_changeid++; - destroyScummVMSurface(); - setupScummVMSurface(); + case kFeatureAspectRatioCorrection: + _ar_correction = enable; + updateScreenRect(); + break; + case kFeatureVirtualKeyboard: + _virtkeybd_on = enable; + showVirtualKeyboard(enable); break; default: break; } - - return true; -} - -void OSystem_Android::pushEvent(const Common::Event& event) { - lockMutex(_event_queue_lock); - - // Try to combine multiple queued mouse move events - if (event.type == Common::EVENT_MOUSEMOVE && - !_event_queue.empty() && - _event_queue.back().type == Common::EVENT_MOUSEMOVE) { - Common::Event tail = _event_queue.back(); - if (event.kbd.flags) { - // relative movement hack - tail.mouse.x += event.mouse.x; - tail.mouse.y += event.mouse.y; - } else { - // absolute position, clear relative flag - tail.kbd.flags = 0; - tail.mouse.x = event.mouse.x; - tail.mouse.y = event.mouse.y; - } - } else { - _event_queue.push(event); - } - - unlockMutex(_event_queue_lock); } -static void ScummVM_pushEvent(JNIEnv *env, jobject self, jobject java_event) { - OSystem_Android *cpp_obj = OSystem_Android::fromJavaObject(env, self); - - Common::Event event; - event.type = (Common::EventType)env->GetIntField(java_event, - FID_Event_type); - - event.synthetic = - env->GetBooleanField(java_event, FID_Event_synthetic); - - switch (event.type) { - case Common::EVENT_KEYDOWN: - case Common::EVENT_KEYUP: - event.kbd.keycode = (Common::KeyCode)env->GetIntField( - java_event, FID_Event_kbd_keycode); - event.kbd.ascii = static_cast<int>(env->GetIntField( - java_event, FID_Event_kbd_ascii)); - event.kbd.flags = static_cast<int>(env->GetIntField( - java_event, FID_Event_kbd_flags)); - break; - case Common::EVENT_MOUSEMOVE: - case Common::EVENT_LBUTTONDOWN: - case Common::EVENT_LBUTTONUP: - case Common::EVENT_RBUTTONDOWN: - case Common::EVENT_RBUTTONUP: - case Common::EVENT_WHEELUP: - case Common::EVENT_WHEELDOWN: - case Common::EVENT_MBUTTONDOWN: - case Common::EVENT_MBUTTONUP: - event.mouse.x = - env->GetIntField(java_event, FID_Event_mouse_x); - event.mouse.y = - env->GetIntField(java_event, FID_Event_mouse_y); - // This is a terrible hack. We stash "relativeness" - // in the kbd.flags field until pollEvent() can work - // it out. - event.kbd.flags = env->GetBooleanField( - java_event, FID_Event_mouse_relative) ? 1 : 0; - break; +bool OSystem_Android::getFeatureState(Feature f) { + switch (f) { + case kFeatureFullscreenMode: + return _fullscreen; + case kFeatureAspectRatioCorrection: + return _ar_correction; + case kFeatureVirtualKeyboard: + return _virtkeybd_on; default: - break; + return false; } - - cpp_obj->pushEvent(event); } uint32 OSystem_Android::getMillis() { @@ -1219,7 +408,7 @@ uint32 OSystem_Android::getMillis() { gettimeofday(&curTime, 0); - return (uint32)(((curTime.tv_sec - _startTime.tv_sec) * 1000) + \ + return (uint32)(((curTime.tv_sec - _startTime.tv_sec) * 1000) + ((curTime.tv_usec - _startTime.tv_usec) / 1000)); } @@ -1229,6 +418,7 @@ void OSystem_Android::delayMillis(uint msecs) { OSystem::MutexRef OSystem_Android::createMutex() { pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); @@ -1267,58 +457,38 @@ void OSystem_Android::deleteMutex(MutexRef mutex) { void OSystem_Android::quit() { ENTER(); + JNI::setReadyForEvents(false); + + _audio_thread_exit = true; + pthread_join(_audio_thread, 0); + _timer_thread_exit = true; pthread_join(_timer_thread, 0); + + delete _game_texture; + delete _overlay_texture; + delete _mouse_texture_palette; + delete _mouse_texture_rgb; + + deinitSurface(); } void OSystem_Android::setWindowCaption(const char *caption) { ENTER("%s", caption); - JNIEnv *env = JNU_GetEnv(); - jstring java_caption = env->NewStringUTF(caption); - env->CallVoidMethod(_back_ptr, MID_setWindowCaption, java_caption); - - if (env->ExceptionCheck()) { - warning("Failed to set window caption"); - - env->ExceptionDescribe(); - env->ExceptionClear(); - } - - env->DeleteLocalRef(java_caption); + JNI::setWindowCaption(caption); } void OSystem_Android::displayMessageOnOSD(const char *msg) { ENTER("%s", msg); - JNIEnv *env = JNU_GetEnv(); - jstring java_msg = env->NewStringUTF(msg); - - env->CallVoidMethod(_back_ptr, MID_displayMessageOnOSD, java_msg); - - if (env->ExceptionCheck()) { - warning("Failed to display OSD message"); - - env->ExceptionDescribe(); - env->ExceptionClear(); - } - - env->DeleteLocalRef(java_msg); + JNI::displayMessageOnOSD(msg); } void OSystem_Android::showVirtualKeyboard(bool enable) { ENTER("%d", enable); - JNIEnv *env = JNU_GetEnv(); - - env->CallVoidMethod(_back_ptr, MID_showVirtualKeyboard, enable); - - if (env->ExceptionCheck()) { - error("Error trying to show virtual keyboard"); - - env->ExceptionDescribe(); - env->ExceptionClear(); - } + JNI::showVirtualKeyboard(enable); } Common::SaveFileManager *OSystem_Android::getSavefileManager() { @@ -1355,37 +525,13 @@ FilesystemFactory *OSystem_Android::getFilesystemFactory() { void OSystem_Android::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) { - s.add("ASSET", _asset_archive, priority, false); - - JNIEnv *env = JNU_GetEnv(); - - jobjectArray array = - (jobjectArray)env->CallObjectMethod(_back_ptr, MID_getSysArchives); - - if (env->ExceptionCheck()) { - warning("Error finding system archive path"); - - env->ExceptionDescribe(); - env->ExceptionClear(); - - return; - } - - jsize size = env->GetArrayLength(array); - for (jsize i = 0; i < size; ++i) { - jstring path_obj = (jstring)env->GetObjectArrayElement(array, i); - const char *path = env->GetStringUTFChars(path_obj, 0); - - if (path != 0) { - s.addDirectory(path, path, priority); - env->ReleaseStringUTFChars(path_obj, path); - } + ENTER(""); - env->DeleteLocalRef(path_obj); - } + JNI::addSysArchivesToSearchSet(s, priority); } -void OSystem_Android::logMessage(LogMessageType::Type type, const char *message) { +void OSystem_Android::logMessage(LogMessageType::Type type, + const char *message) { switch (type) { case LogMessageType::kDebug: __android_log_write(ANDROID_LOG_DEBUG, android_log_tag, message); @@ -1401,177 +547,11 @@ void OSystem_Android::logMessage(LogMessageType::Type type, const char *message) } } -static jint ScummVM_scummVMMain(JNIEnv *env, jobject self, jobjectArray args) { - OSystem_Android *cpp_obj = OSystem_Android::fromJavaObject(env, self); - - const int MAX_NARGS = 32; - int res = -1; - - int argc = env->GetArrayLength(args); - if (argc > MAX_NARGS) { - JNU_ThrowByName(env, "java/lang/IllegalArgumentException", - "too many arguments"); - return 0; - } - - char *argv[MAX_NARGS]; - - // note use in cleanup loop below - int nargs; - - for (nargs = 0; nargs < argc; ++nargs) { - jstring arg = (jstring)env->GetObjectArrayElement(args, nargs); - - if (arg == 0) { - argv[nargs] = 0; - } else { - const char *cstr = env->GetStringUTFChars(arg, 0); - - argv[nargs] = const_cast<char *>(cstr); - - // exception already thrown? - if (cstr == 0) - goto cleanup; - } - - env->DeleteLocalRef(arg); - } - - g_system = cpp_obj; - assert(g_system); - - LOGI("Entering scummvm_main with %d args", argc); - - res = scummvm_main(argc, argv); - - LOGI("Exiting scummvm_main"); - - g_system->quit(); - -cleanup: - nargs--; - - for (int i = 0; i < nargs; ++i) { - if (argv[i] == 0) - continue; - - jstring arg = (jstring)env->GetObjectArrayElement(args, nargs); - - // Exception already thrown? - if (arg == 0) - return res; - - env->ReleaseStringUTFChars(arg, argv[i]); - env->DeleteLocalRef(arg); - } - - return res; -} - #ifdef DYNAMIC_MODULES void AndroidPluginProvider::addCustomDirectories(Common::FSList &dirs) const { - OSystem_Android *g_system_android = (OSystem_Android *)g_system; - g_system_android->addPluginDirectories(dirs); + ((OSystem_Android *)g_system)->addPluginDirectories(dirs); } #endif -static void ScummVM_enableZoning(JNIEnv *env, jobject self, jboolean enable) { - OSystem_Android *cpp_obj = OSystem_Android::fromJavaObject(env, self); - cpp_obj->enableZoning(enable); -} - -static void ScummVM_setSurfaceSize(JNIEnv *env, jobject self, - jint width, jint height) { - OSystem_Android *cpp_obj = OSystem_Android::fromJavaObject(env, self); - cpp_obj->setSurfaceSize(width, height); -} - -const static JNINativeMethod gMethods[] = { - { "create", "(Landroid/content/res/AssetManager;)V", - (void *)ScummVM_create }, - { "nativeDestroy", "()V", - (void *)ScummVM_nativeDestroy }, - { "scummVMMain", "([Ljava/lang/String;)I", - (void *)ScummVM_scummVMMain }, - { "pushEvent", "(Lorg/inodes/gus/scummvm/Event;)V", - (void *)ScummVM_pushEvent }, - { "audioMixCallback", "([B)V", - (void *)ScummVM_audioMixCallback }, - { "setConfMan", "(Ljava/lang/String;I)V", - (void *)ScummVM_setConfManInt }, - { "setConfMan", "(Ljava/lang/String;Ljava/lang/String;)V", - (void *)ScummVM_setConfManString }, - { "enableZoning", "(Z)V", - (void *)ScummVM_enableZoning }, - { "setSurfaceSize", "(II)V", - (void *)ScummVM_setSurfaceSize }, -}; - -JNIEXPORT jint JNICALL -JNI_OnLoad(JavaVM *jvm, void *reserved) { - cached_jvm = jvm; - - JNIEnv *env; - - if (jvm->GetEnv((void **)&env, JNI_VERSION_1_2)) - return JNI_ERR; - - jclass cls = env->FindClass("org/inodes/gus/scummvm/ScummVM"); - if (cls == 0) - return JNI_ERR; - - if (env->RegisterNatives(cls, gMethods, ARRAYSIZE(gMethods)) < 0) - return JNI_ERR; - - FID_ScummVM_nativeScummVM = env->GetFieldID(cls, "nativeScummVM", "J"); - if (FID_ScummVM_nativeScummVM == 0) - return JNI_ERR; - - jclass event = env->FindClass("org/inodes/gus/scummvm/Event"); - if (event == 0) - return JNI_ERR; - - FID_Event_type = env->GetFieldID(event, "type", "I"); - if (FID_Event_type == 0) - return JNI_ERR; - - FID_Event_synthetic = env->GetFieldID(event, "synthetic", "Z"); - if (FID_Event_synthetic == 0) - return JNI_ERR; - - FID_Event_kbd_keycode = env->GetFieldID(event, "kbd_keycode", "I"); - if (FID_Event_kbd_keycode == 0) - return JNI_ERR; - - FID_Event_kbd_ascii = env->GetFieldID(event, "kbd_ascii", "I"); - if (FID_Event_kbd_ascii == 0) - return JNI_ERR; - - FID_Event_kbd_flags = env->GetFieldID(event, "kbd_flags", "I"); - if (FID_Event_kbd_flags == 0) - return JNI_ERR; - - FID_Event_mouse_x = env->GetFieldID(event, "mouse_x", "I"); - if (FID_Event_mouse_x == 0) - return JNI_ERR; - - FID_Event_mouse_y = env->GetFieldID(event, "mouse_y", "I"); - if (FID_Event_mouse_y == 0) - return JNI_ERR; - - FID_Event_mouse_relative = env->GetFieldID(event, "mouse_relative", "Z"); - if (FID_Event_mouse_relative == 0) - return JNI_ERR; - - cls = env->FindClass("java/lang/Object"); - if (cls == 0) - return JNI_ERR; - - MID_Object_wait = env->GetMethodID(cls, "wait", "()V"); - if (MID_Object_wait == 0) - return JNI_ERR; - - return JNI_VERSION_1_2; -} - #endif + diff --git a/backends/platform/android/android.h b/backends/platform/android/android.h index 855fb04b5d..7ff96186f9 100644 --- a/backends/platform/android/android.h +++ b/backends/platform/android/android.h @@ -23,8 +23,23 @@ * */ +#ifndef _ANDROID_H_ +#define _ANDROID_H_ + #if defined(__ANDROID__) +#include "common/fs.h" +#include "common/archive.h" +#include "audio/mixer_intern.h" +#include "graphics/surface.h" +#include "backends/base-backend.h" +#include "backends/plugins/posix/posix-provider.h" +#include "backends/fs/posix/posix-fs-factory.h" + +#include "backends/platform/android/texture.h" + +#include <pthread.h> + #include <android/log.h> #include <GLES/gl.h> @@ -46,7 +61,7 @@ extern const char *android_log_tag; #ifdef ANDROID_DEBUG_ENTER #define ENTER(fmt, args...) LOGD("%s(" fmt ")", __FUNCTION__, ##args) #else -#define ENTER(fmt, args...) /**/ +#define ENTER(fmt, args...) do { } while (false) #endif #ifdef ANDROID_DEBUG_GL @@ -58,13 +73,215 @@ extern void checkGlError(const char *expr, const char *file, int line); checkGlError(#x, __FILE__, __LINE__); \ } while (false) +#define GLTHREADCHECK \ + do { \ + assert(pthread_self() == _main_thread); \ + } while (false) + #else #define GLCALL(x) do { (x); } while (false) +#define GLTHREADCHECK do { } while (false) +#endif + +#ifdef DYNAMIC_MODULES +class AndroidPluginProvider : public POSIXPluginProvider { +protected: + virtual void addCustomDirectories(Common::FSList &dirs) const; +}; +#endif + +class OSystem_Android : public BaseBackend, public PaletteManager { +private: + // passed from the dark side + int _audio_sample_rate; + int _audio_buffer_size; + + int _screen_changeid; + int _egl_surface_width; + int _egl_surface_height; + + bool _force_redraw; + + // Game layer + GLESBaseTexture *_game_texture; + int _shake_offset; + Common::Rect _focus_rect; + + // Overlay layer + GLES4444Texture *_overlay_texture; + bool _show_overlay; + + // Mouse layer + GLESBaseTexture *_mouse_texture; + GLESPaletteTexture *_mouse_texture_palette; + GLES5551Texture *_mouse_texture_rgb; + Common::Point _mouse_hotspot; + uint32 _mouse_keycolor; + int _mouse_targetscale; + bool _show_mouse; + bool _use_mouse_palette; + + int _graphicsMode; + bool _fullscreen; + bool _ar_correction; + + pthread_t _main_thread; + + bool _timer_thread_exit; + pthread_t _timer_thread; + static void *timerThreadFunc(void *arg); + + bool _audio_thread_exit; + pthread_t _audio_thread; + static void *audioThreadFunc(void *arg); + + bool _enable_zoning; + bool _virtkeybd_on; + + Common::SaveFileManager *_savefile; + Audio::MixerImpl *_mixer; + Common::TimerManager *_timer; + FilesystemFactory *_fsFactory; + timeval _startTime; + + void initSurface(); + void deinitSurface(); + void initViewport(); + + void initOverlay(); + +#ifdef USE_RGB_COLOR + Common::String getPixelFormatName(const Graphics::PixelFormat &format) const; + void initTexture(GLESBaseTexture **texture, uint width, uint height, + const Graphics::PixelFormat *format); +#endif + + void setupKeymapper(); + void setCursorPaletteInternal(const byte *colors, uint start, uint num); + +public: + OSystem_Android(int audio_sample_rate, int audio_buffer_size); + virtual ~OSystem_Android(); + + virtual void initBackend(); + void addPluginDirectories(Common::FSList &dirs) const; + void enableZoning(bool enable) { _enable_zoning = enable; } + + virtual bool hasFeature(Feature f); + virtual void setFeatureState(Feature f, bool enable); + virtual bool getFeatureState(Feature f); + + virtual const GraphicsMode *getSupportedGraphicsModes() const; + virtual int getDefaultGraphicsMode() const; + virtual bool setGraphicsMode(int mode); + virtual int getGraphicsMode() const; + +#ifdef USE_RGB_COLOR + virtual Graphics::PixelFormat getScreenFormat() const; + virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const; #endif -// Fix JNIEXPORT declaration to actually do something useful -#undef JNIEXPORT -#define JNIEXPORT __attribute__ ((visibility("default"))) + virtual void initSize(uint width, uint height, + const Graphics::PixelFormat *format); + + enum FixupType { + kClear = 0, // glClear + kClearSwap, // glClear + swapBuffers + kClearUpdate // glClear + updateScreen + }; + + void clearScreen(FixupType type, byte count = 1); + + void updateScreenRect(); + virtual int getScreenChangeID() const; + + virtual int16 getHeight(); + virtual int16 getWidth(); + + virtual PaletteManager *getPaletteManager() { + return this; + } + +public: + void pushEvent(int type, int arg1, int arg2, int arg3, int arg4, int arg5); + +private: + Common::Queue<Common::Event> _event_queue; + MutexRef _event_queue_lock; + Common::Point _touch_pt_down, _touch_pt_dt; + int _eventScaleX; + int _eventScaleY; + bool _touchpad_mode; + int _touchpad_scale; + int _trackball_scale; + int _dpad_scale; + + void clipMouse(Common::Point &p); + void scaleMouse(Common::Point &p, int x, int y, bool deductDrawRect = true); + void updateEventScale(); + +protected: + // PaletteManager API + virtual void setPalette(const byte *colors, uint start, uint num); + virtual void grabPalette(byte *colors, uint start, uint num); + +public: + virtual void copyRectToScreen(const byte *buf, int pitch, int x, int y, + int w, int h); + virtual void updateScreen(); + virtual Graphics::Surface *lockScreen(); + virtual void unlockScreen(); + virtual void setShakePos(int shakeOffset); + virtual void fillScreen(uint32 col); + virtual void setFocusRectangle(const Common::Rect& rect); + virtual void clearFocusRectangle(); + + virtual void showOverlay(); + virtual void hideOverlay(); + virtual void clearOverlay(); + virtual void grabOverlay(OverlayColor *buf, int pitch); + virtual void copyRectToOverlay(const OverlayColor *buf, int pitch, + int x, int y, int w, int h); + virtual int16 getOverlayHeight(); + virtual int16 getOverlayWidth(); + virtual Graphics::PixelFormat getOverlayFormat() const; + + virtual bool showMouse(bool visible); + + virtual void warpMouse(int x, int y); + virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, + int hotspotY, uint32 keycolor, + int cursorTargetScale, + const Graphics::PixelFormat *format); + virtual void setCursorPalette(const byte *colors, uint start, uint num); + virtual void disableCursorPalette(bool disable); + + virtual bool pollEvent(Common::Event &event); + virtual uint32 getMillis(); + virtual void delayMillis(uint msecs); + + virtual MutexRef createMutex(void); + virtual void lockMutex(MutexRef mutex); + virtual void unlockMutex(MutexRef mutex); + virtual void deleteMutex(MutexRef mutex); + + virtual void quit(); + + virtual void setWindowCaption(const char *caption); + virtual void displayMessageOnOSD(const char *msg); + virtual void showVirtualKeyboard(bool enable); + + virtual Common::SaveFileManager *getSavefileManager(); + virtual Audio::Mixer *getMixer(); + virtual void getTimeAndDate(TimeDate &t) const; + virtual Common::TimerManager *getTimerManager(); + virtual FilesystemFactory *getFilesystemFactory(); + virtual void logMessage(LogMessageType::Type type, const char *message); + virtual void addSysArchivesToSearchSet(Common::SearchSet &s, + int priority = 0); +}; + +#endif #endif diff --git a/backends/platform/android/android.mk b/backends/platform/android/android.mk index 1bc3c3d21a..cb39f8acfe 100644 --- a/backends/platform/android/android.mk +++ b/backends/platform/android/android.mk @@ -6,6 +6,7 @@ ANDROID_PLUGIN_VERSIONCODE = 6 JAVA_FILES = \ ScummVM.java \ + ScummVMEvents.java \ ScummVMApplication.java \ ScummVMActivity.java \ EditableSurfaceView.java \ @@ -163,6 +164,10 @@ release/%.apk: %.apk androidrelease: $(addprefix release/, $(APK_MAIN) $(APK_PLUGINS)) +androidtestmain: $(APK_MAIN) + $(ADB) install -r $(APK_MAIN) + $(ADB) shell am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -n org.inodes.gus.scummvm/.Unpacker + androidtest: $(APK_MAIN) $(APK_PLUGINS) @set -e; for apk in $^; do \ $(ADB) install -r $$apk; \ diff --git a/backends/platform/android/asset-archive.cpp b/backends/platform/android/asset-archive.cpp index 71ce25aa72..26b1a6ad39 100644 --- a/backends/platform/android/asset-archive.cpp +++ b/backends/platform/android/asset-archive.cpp @@ -36,10 +36,9 @@ #include "common/archive.h" #include "common/debug.h" +#include "backends/platform/android/jni.h" #include "backends/platform/android/asset-archive.h" -extern JNIEnv *JNU_GetEnv(); - // Must match android.content.res.AssetManager.ACCESS_* const jint ACCESS_UNKNOWN = 0; const jint ACCESS_RANDOM = 1; @@ -100,7 +99,7 @@ JavaInputStream::JavaInputStream(JNIEnv *env, jobject is) : { _input_stream = env->NewGlobalRef(is); _buflen = 8192; - _buf = static_cast<jbyteArray>(env->NewGlobalRef(env->NewByteArray(_buflen))); + _buf = (jbyteArray)env->NewGlobalRef(env->NewByteArray(_buflen)); jclass cls = env->GetObjectClass(_input_stream); MID_mark = env->GetMethodID(cls, "mark", "(I)V"); @@ -124,7 +123,7 @@ JavaInputStream::JavaInputStream(JNIEnv *env, jobject is) : } JavaInputStream::~JavaInputStream() { - JNIEnv *env = JNU_GetEnv(); + JNIEnv *env = JNI::getEnv(); close(env); env->DeleteGlobalRef(_buf); @@ -139,11 +138,11 @@ void JavaInputStream::close(JNIEnv *env) { } uint32 JavaInputStream::read(void *dataPtr, uint32 dataSize) { - JNIEnv *env = JNU_GetEnv(); + JNIEnv *env = JNI::getEnv(); if (_buflen < jint(dataSize)) { _buflen = dataSize; - + env->DeleteGlobalRef(_buf); _buf = static_cast<jbyteArray>(env->NewGlobalRef(env->NewByteArray(_buflen))); } @@ -171,7 +170,7 @@ uint32 JavaInputStream::read(void *dataPtr, uint32 dataSize) { } bool JavaInputStream::seek(int32 offset, int whence) { - JNIEnv *env = JNU_GetEnv(); + JNIEnv *env = JNI::getEnv(); uint32 newpos; switch (whence) { @@ -305,7 +304,8 @@ AssetFdReadStream::AssetFdReadStream(JNIEnv *env, jobject assetfd) : _declared_len = env->CallLongMethod(_assetfd, MID_getDeclaredLength); jmethodID MID_getFileDescriptor = - env->GetMethodID(cls, "getFileDescriptor", "()Ljava/io/FileDescriptor;"); + env->GetMethodID(cls, "getFileDescriptor", + "()Ljava/io/FileDescriptor;"); assert(MID_getFileDescriptor); jobject javafd = env->CallObjectMethod(_assetfd, MID_getFileDescriptor); assert(javafd); @@ -318,7 +318,7 @@ AssetFdReadStream::AssetFdReadStream(JNIEnv *env, jobject assetfd) : } AssetFdReadStream::~AssetFdReadStream() { - JNIEnv *env = JNU_GetEnv(); + JNIEnv *env = JNI::getEnv(); env->CallVoidMethod(_assetfd, MID_close); if (env->ExceptionCheck()) @@ -369,7 +369,7 @@ bool AssetFdReadStream::seek(int32 offset, int whence) { } AndroidAssetArchive::AndroidAssetArchive(jobject am) { - JNIEnv *env = JNU_GetEnv(); + JNIEnv *env = JNI::getEnv(); _am = env->NewGlobalRef(am); jclass cls = env->GetObjectClass(_am); @@ -377,8 +377,8 @@ AndroidAssetArchive::AndroidAssetArchive(jobject am) { "(Ljava/lang/String;I)Ljava/io/InputStream;"); assert(MID_open); - MID_openFd = env->GetMethodID(cls, "openFd", - "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;"); + MID_openFd = env->GetMethodID(cls, "openFd", "(Ljava/lang/String;)" + "Landroid/content/res/AssetFileDescriptor;"); assert(MID_openFd); MID_list = env->GetMethodID(cls, "list", @@ -387,12 +387,12 @@ AndroidAssetArchive::AndroidAssetArchive(jobject am) { } AndroidAssetArchive::~AndroidAssetArchive() { - JNIEnv *env = JNU_GetEnv(); + JNIEnv *env = JNI::getEnv(); env->DeleteGlobalRef(_am); } bool AndroidAssetArchive::hasFile(const Common::String &name) { - JNIEnv *env = JNU_GetEnv(); + JNIEnv *env = JNI::getEnv(); jstring path = env->NewStringUTF(name.c_str()); jobject result = env->CallObjectMethod(_am, MID_open, path, ACCESS_UNKNOWN); if (env->ExceptionCheck()) { @@ -412,7 +412,7 @@ bool AndroidAssetArchive::hasFile(const Common::String &name) { } int AndroidAssetArchive::listMembers(Common::ArchiveMemberList &member_list) { - JNIEnv *env = JNU_GetEnv(); + JNIEnv *env = JNI::getEnv(); Common::List<Common::String> dirlist; dirlist.push_back(""); @@ -422,7 +422,8 @@ int AndroidAssetArchive::listMembers(Common::ArchiveMemberList &member_list) { dirlist.pop_back(); jstring jpath = env->NewStringUTF(dir.c_str()); - jobjectArray jpathlist = static_cast<jobjectArray>(env->CallObjectMethod(_am, MID_list, jpath)); + jobjectArray jpathlist = + (jobjectArray)env->CallObjectMethod(_am, MID_list, jpath); if (env->ExceptionCheck()) { warning("Error while calling AssetManager->list(%s). Ignoring.", @@ -469,7 +470,7 @@ Common::ArchiveMemberPtr AndroidAssetArchive::getMember(const Common::String &na } Common::SeekableReadStream *AndroidAssetArchive::createReadStreamForMember(const Common::String &path) const { - JNIEnv *env = JNU_GetEnv(); + JNIEnv *env = JNI::getEnv(); jstring jpath = env->NewStringUTF(path.c_str()); // Try openFd() first ... diff --git a/backends/platform/android/asset-archive.h b/backends/platform/android/asset-archive.h index 28e48426e9..6ec86e4cd0 100644 --- a/backends/platform/android/asset-archive.h +++ b/backends/platform/android/asset-archive.h @@ -23,6 +23,9 @@ * */ +#ifndef _ANDROID_ASSET_H_ +#define _ANDROID_ASSET_H_ + #if defined(__ANDROID__) #include <jni.h> @@ -51,3 +54,5 @@ private: }; #endif +#endif + diff --git a/backends/platform/android/events.cpp b/backends/platform/android/events.cpp new file mode 100644 index 0000000000..1715e6a8a7 --- /dev/null +++ b/backends/platform/android/events.cpp @@ -0,0 +1,661 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#if defined(__ANDROID__) + +#include "common/events.h" + +#include "backends/platform/android/android.h" +#include "backends/platform/android/jni.h" + +// $ANDROID_NDK/platforms/android-9/arch-arm/usr/include/android/keycodes.h +// http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=libs/ui/Input.cpp +// http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=core/java/android/view/KeyEvent.java + +// event type +enum { + JE_SYS_KEY = 0, + JE_KEY = 1, + JE_DOWN = 2, + JE_SCROLL = 3, + JE_TAP = 4, + JE_DOUBLE_TAP = 5, + JE_BALL = 6, + JE_QUIT = 0x1000 +}; + +// action type +enum { + JACTION_DOWN = 0, + JACTION_UP = 1, + JACTION_MULTIPLE = 2 +}; + +// system keys +enum { + JKEYCODE_SOFT_RIGHT = 2, + JKEYCODE_HOME = 3, + JKEYCODE_BACK = 4, + JKEYCODE_CALL = 5, + JKEYCODE_ENDCALL = 6, + JKEYCODE_VOLUME_UP = 24, + JKEYCODE_VOLUME_DOWN = 25, + JKEYCODE_POWER = 26, + JKEYCODE_CAMERA = 27, + JKEYCODE_HEADSETHOOK = 79, + JKEYCODE_FOCUS = 80, + JKEYCODE_MENU = 82, + JKEYCODE_SEARCH = 84, + JKEYCODE_MUTE = 91, + JKEYCODE_MEDIA_PLAY_PAUSE = 85, + JKEYCODE_MEDIA_STOP = 86, + JKEYCODE_MEDIA_NEXT = 87, + JKEYCODE_MEDIA_PREVIOUS = 88, + JKEYCODE_MEDIA_REWIND = 89, + JKEYCODE_MEDIA_FAST_FORWARD = 90 +}; + +// five-way navigation control +enum { + JKEYCODE_DPAD_UP = 19, + JKEYCODE_DPAD_DOWN = 20, + JKEYCODE_DPAD_LEFT = 21, + JKEYCODE_DPAD_RIGHT = 22, + JKEYCODE_DPAD_CENTER = 23 +}; + +// meta modifier +enum { + JMETA_SHIFT = 0x01, + JMETA_ALT = 0x02, + JMETA_SYM = 0x04, + JMETA_CTRL = 0x1000 +}; + +// map android key codes to our kbd codes +static const Common::KeyCode jkeymap[] = { + Common::KEYCODE_INVALID, // KEYCODE_UNKNOWN + Common::KEYCODE_INVALID, // KEYCODE_SOFT_LEFT + Common::KEYCODE_INVALID, // KEYCODE_SOFT_RIGHT + Common::KEYCODE_INVALID, // KEYCODE_HOME + Common::KEYCODE_INVALID, // KEYCODE_BACK + Common::KEYCODE_INVALID, // KEYCODE_CALL + Common::KEYCODE_INVALID, // KEYCODE_ENDCALL + Common::KEYCODE_0, // KEYCODE_0 + Common::KEYCODE_1, // KEYCODE_1 + Common::KEYCODE_2, // KEYCODE_2 + Common::KEYCODE_3, // KEYCODE_3 + Common::KEYCODE_4, // KEYCODE_4 + Common::KEYCODE_5, // KEYCODE_5 + Common::KEYCODE_6, // KEYCODE_6 + Common::KEYCODE_7, // KEYCODE_7 + Common::KEYCODE_8, // KEYCODE_8 + Common::KEYCODE_9, // KEYCODE_9 + Common::KEYCODE_ASTERISK, // KEYCODE_STAR + Common::KEYCODE_HASH, // KEYCODE_POUND + Common::KEYCODE_INVALID, // KEYCODE_DPAD_UP + Common::KEYCODE_INVALID, // KEYCODE_DPAD_DOWN + Common::KEYCODE_INVALID, // KEYCODE_DPAD_LEFT + Common::KEYCODE_INVALID, // KEYCODE_DPAD_RIGHT + Common::KEYCODE_INVALID, // KEYCODE_DPAD_CENTER + Common::KEYCODE_INVALID, // KEYCODE_VOLUME_UP + Common::KEYCODE_INVALID, // KEYCODE_VOLUME_DOWN + Common::KEYCODE_INVALID, // KEYCODE_POWER + Common::KEYCODE_INVALID, // KEYCODE_CAMERA + Common::KEYCODE_INVALID, // KEYCODE_CLEAR + Common::KEYCODE_a, // KEYCODE_A + Common::KEYCODE_b, // KEYCODE_B + Common::KEYCODE_c, // KEYCODE_C + Common::KEYCODE_d, // KEYCODE_D + Common::KEYCODE_e, // KEYCODE_E + Common::KEYCODE_f, // KEYCODE_F + Common::KEYCODE_g, // KEYCODE_G + Common::KEYCODE_h, // KEYCODE_H + Common::KEYCODE_i, // KEYCODE_I + Common::KEYCODE_j, // KEYCODE_J + Common::KEYCODE_k, // KEYCODE_K + Common::KEYCODE_l, // KEYCODE_L + Common::KEYCODE_m, // KEYCODE_M + Common::KEYCODE_n, // KEYCODE_N + Common::KEYCODE_o, // KEYCODE_O + Common::KEYCODE_p, // KEYCODE_P + Common::KEYCODE_q, // KEYCODE_Q + Common::KEYCODE_r, // KEYCODE_R + Common::KEYCODE_s, // KEYCODE_S + Common::KEYCODE_t, // KEYCODE_T + Common::KEYCODE_u, // KEYCODE_U + Common::KEYCODE_v, // KEYCODE_V + Common::KEYCODE_w, // KEYCODE_W + Common::KEYCODE_x, // KEYCODE_X + Common::KEYCODE_y, // KEYCODE_Y + Common::KEYCODE_z, // KEYCODE_Z + Common::KEYCODE_COMMA, // KEYCODE_COMMA + Common::KEYCODE_PERIOD, // KEYCODE_PERIOD + Common::KEYCODE_LALT, // KEYCODE_ALT_LEFT + Common::KEYCODE_RALT, // KEYCODE_ALT_RIGHT + Common::KEYCODE_LSHIFT, // KEYCODE_SHIFT_LEFT + Common::KEYCODE_RSHIFT, // KEYCODE_SHIFT_RIGHT + Common::KEYCODE_TAB, // KEYCODE_TAB + Common::KEYCODE_SPACE, // KEYCODE_SPACE + Common::KEYCODE_LCTRL, // KEYCODE_SYM + Common::KEYCODE_INVALID, // KEYCODE_EXPLORER + Common::KEYCODE_INVALID, // KEYCODE_ENVELOPE + Common::KEYCODE_RETURN, // KEYCODE_ENTER + Common::KEYCODE_BACKSPACE, // KEYCODE_DEL + Common::KEYCODE_BACKQUOTE, // KEYCODE_GRAVE + Common::KEYCODE_MINUS, // KEYCODE_MINUS + Common::KEYCODE_EQUALS, // KEYCODE_EQUALS + Common::KEYCODE_LEFTPAREN, // KEYCODE_LEFT_BRACKET + Common::KEYCODE_RIGHTPAREN, // KEYCODE_RIGHT_BRACKET + Common::KEYCODE_BACKSLASH, // KEYCODE_BACKSLASH + Common::KEYCODE_SEMICOLON, // KEYCODE_SEMICOLON + Common::KEYCODE_QUOTE, // KEYCODE_APOSTROPHE + Common::KEYCODE_SLASH, // KEYCODE_SLASH + Common::KEYCODE_AT, // KEYCODE_AT + Common::KEYCODE_INVALID, // KEYCODE_NUM + Common::KEYCODE_INVALID, // KEYCODE_HEADSETHOOK + Common::KEYCODE_INVALID, // KEYCODE_FOCUS + Common::KEYCODE_PLUS, // KEYCODE_PLUS + Common::KEYCODE_INVALID, // KEYCODE_MENU + Common::KEYCODE_INVALID, // KEYCODE_NOTIFICATION + Common::KEYCODE_INVALID, // KEYCODE_SEARCH + Common::KEYCODE_INVALID, // KEYCODE_MEDIA_PLAY_PAUSE + Common::KEYCODE_INVALID, // KEYCODE_MEDIA_STOP + Common::KEYCODE_INVALID, // KEYCODE_MEDIA_NEXT + Common::KEYCODE_INVALID, // KEYCODE_MEDIA_PREVIOUS + Common::KEYCODE_INVALID, // KEYCODE_MEDIA_REWIND + Common::KEYCODE_INVALID, // KEYCODE_MEDIA_FAST_FORWARD + Common::KEYCODE_INVALID, // KEYCODE_MUTE + Common::KEYCODE_PAGEUP, // KEYCODE_PAGE_UP + Common::KEYCODE_PAGEDOWN // KEYCODE_PAGE_DOWN +}; + +// floating point. use sparingly +template <class T> +static inline T scalef(T in, float numerator, float denominator) { + return static_cast<float>(in) * numerator / denominator; +} + +void OSystem_Android::setupKeymapper() { +#ifdef ENABLE_KEYMAPPER + using namespace Common; + + Keymapper *mapper = getEventManager()->getKeymapper(); + + HardwareKeySet *keySet = new HardwareKeySet(); + + keySet->addHardwareKey( + new HardwareKey("n", KeyState(KEYCODE_n), "n (vk)", + kTriggerLeftKeyType, + kVirtualKeyboardActionType)); + + mapper->registerHardwareKeySet(keySet); + + Keymap *globalMap = new Keymap("global"); + Action *act; + + act = new Action(globalMap, "VIRT", "Display keyboard", + kVirtualKeyboardActionType); + act->addKeyEvent(KeyState(KEYCODE_F7, ASCII_F7, 0)); + + mapper->addGlobalKeymap(globalMap); + + mapper->pushKeymap("global"); +#endif +} + +void OSystem_Android::warpMouse(int x, int y) { + ENTER("%d, %d", x, y); + + Common::Event e; + + e.type = Common::EVENT_MOUSEMOVE; + e.mouse.x = x; + e.mouse.y = y; + + clipMouse(e.mouse); + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); +} + +void OSystem_Android::clipMouse(Common::Point &p) { + const GLESBaseTexture *tex; + + if (_show_overlay) + tex = _overlay_texture; + else + tex = _game_texture; + + p.x = CLIP(p.x, int16(0), int16(tex->width())); + p.y = CLIP(p.y, int16(0), int16(tex->height())); +} + +void OSystem_Android::scaleMouse(Common::Point &p, int x, int y, + bool deductDrawRect) { + const GLESBaseTexture *tex; + + if (_show_overlay) + tex = _overlay_texture; + else + tex = _game_texture; + + const Common::Rect &r = tex->getDrawRect(); + + if (_touchpad_mode) { + x = x * 100 / _touchpad_scale; + y = y * 100 / _touchpad_scale; + } + + if (deductDrawRect) { + x -= r.left; + y -= r.top; + } + + p.x = scalef(x, tex->width(), r.width()); + p.y = scalef(y, tex->height(), r.height()); +} + +void OSystem_Android::updateEventScale() { + const GLESBaseTexture *tex; + + if (_show_overlay) + tex = _overlay_texture; + else + tex = _game_texture; + + _eventScaleY = 100 * 480 / tex->height(); + _eventScaleX = 100 * 640 / tex->width(); +} + +void OSystem_Android::pushEvent(int type, int arg1, int arg2, int arg3, + int arg4, int arg5) { + Common::Event e; + + switch (type) { + case JE_SYS_KEY: + switch (arg1) { + case JACTION_DOWN: + e.type = Common::EVENT_KEYDOWN; + break; + case JACTION_UP: + e.type = Common::EVENT_KEYUP; + break; + default: + LOGE("unhandled jaction on system key: %d", arg1); + return; + } + + switch (arg2) { + case JKEYCODE_BACK: + e.kbd.keycode = Common::KEYCODE_ESCAPE; + e.kbd.ascii = Common::ASCII_ESCAPE; + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + + return; + + // special case. we'll only get it's up event + case JKEYCODE_MENU: + e.type = Common::EVENT_MAINMENU; + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + + return; + + default: + LOGW("unmapped system key: %d", arg2); + return; + } + + break; + + case JE_KEY: + // five-way first + switch (arg2) { + case JKEYCODE_DPAD_UP: + case JKEYCODE_DPAD_DOWN: + case JKEYCODE_DPAD_LEFT: + case JKEYCODE_DPAD_RIGHT: + { + if (arg1 != JACTION_DOWN) + return; + + e.type = Common::EVENT_MOUSEMOVE; + e.synthetic = true; + + e.mouse = getEventManager()->getMousePos(); + + int16 *c; + int s; + + if (arg2 == JKEYCODE_DPAD_UP || arg2 == JKEYCODE_DPAD_DOWN) { + c = &e.mouse.y; + s = _eventScaleY; + } else { + c = &e.mouse.x; + s = _eventScaleX; + } + + // the longer the button held, the faster the pointer is + // TODO put these values in some option dlg? + int f = CLIP(arg5, 1, 8) * _dpad_scale * 100 / s; + + *c += ((arg2 == JKEYCODE_DPAD_UP || + arg2 == JKEYCODE_DPAD_LEFT) ? -1 : 1) * f; + + clipMouse(e.mouse); + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + } + + return; + + case JKEYCODE_DPAD_CENTER: + switch (arg1) { + case JACTION_DOWN: + e.type = Common::EVENT_LBUTTONDOWN; + break; + case JACTION_UP: + e.type = Common::EVENT_LBUTTONUP; + break; + default: + LOGE("unhandled jaction on dpad key: %d", arg1); + return; + } + + { + const Common::Point &m = getEventManager()->getMousePos(); + + e.mouse = m; + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + } + + return; + } + + switch (arg1) { + case JACTION_DOWN: + e.type = Common::EVENT_KEYDOWN; + break; + case JACTION_UP: + e.type = Common::EVENT_KEYUP; + break; + default: + LOGE("unhandled jaction on key: %d", arg1); + return; + } + + if (arg2 < 1 || arg2 > ARRAYSIZE(jkeymap)) { + LOGE("received invalid keycode: %d", arg2); + return; + } + + if (arg5 > 0) + e.synthetic = true; + + e.kbd.keycode = jkeymap[arg2]; + e.kbd.ascii = arg3; + + if (arg4 & JMETA_SHIFT) + e.kbd.flags |= Common::KBD_SHIFT; + if (arg4 & JMETA_ALT) + e.kbd.flags |= Common::KBD_ALT; + if (arg4 & (JMETA_SYM | JMETA_CTRL)) + e.kbd.flags |= Common::KBD_CTRL; + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + + return; + + case JE_DOWN: + _touch_pt_down = getEventManager()->getMousePos(); + break; + + case JE_SCROLL: + e.type = Common::EVENT_MOUSEMOVE; + + if (_touchpad_mode) { + scaleMouse(e.mouse, arg3 - arg1, arg4 - arg2, false); + e.mouse += _touch_pt_down; + clipMouse(e.mouse); + } else { + scaleMouse(e.mouse, arg3, arg4); + clipMouse(e.mouse); + } + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + + return; + + case JE_TAP: + e.type = Common::EVENT_MOUSEMOVE; + + if (_touchpad_mode) { + e.mouse = getEventManager()->getMousePos(); + } else { + scaleMouse(e.mouse, arg1, arg2); + clipMouse(e.mouse); + } + + { + Common::EventType down, up; + + // TODO put these values in some option dlg? + if (arg3 > 1000) { + down = Common::EVENT_MBUTTONDOWN; + up = Common::EVENT_MBUTTONUP; + } else if (arg3 > 500) { + down = Common::EVENT_RBUTTONDOWN; + up = Common::EVENT_RBUTTONUP; + } else { + down = Common::EVENT_LBUTTONDOWN; + up = Common::EVENT_LBUTTONUP; + } + + lockMutex(_event_queue_lock); + + if (!_touchpad_mode) + _event_queue.push(e); + + e.type = down; + _event_queue.push(e); + e.type = up; + _event_queue.push(e); + + unlockMutex(_event_queue_lock); + } + + return; + + case JE_DOUBLE_TAP: + e.type = Common::EVENT_MOUSEMOVE; + + if (_touchpad_mode) { + e.mouse = getEventManager()->getMousePos(); + } else { + scaleMouse(e.mouse, arg1, arg2); + clipMouse(e.mouse); + } + + { + Common::EventType dptype = Common::EVENT_INVALID; + + switch (arg3) { + case JACTION_DOWN: + dptype = Common::EVENT_LBUTTONDOWN; + _touch_pt_dt.x = arg1; + _touch_pt_dt.y = arg2; + break; + case JACTION_UP: + dptype = Common::EVENT_LBUTTONUP; + break; + case JACTION_MULTIPLE: + // held and moved + dptype = Common::EVENT_MOUSEMOVE; + + if (_touchpad_mode) { + scaleMouse(e.mouse, arg1 - _touch_pt_dt.x, + arg2 - _touch_pt_dt.y, false); + e.mouse += _touch_pt_down; + + clipMouse(e.mouse); + } + + break; + default: + LOGE("unhandled jaction on double tap: %d", arg3); + return; + } + + lockMutex(_event_queue_lock); + _event_queue.push(e); + e.type = dptype; + _event_queue.push(e); + unlockMutex(_event_queue_lock); + } + + return; + + case JE_BALL: + e.type = Common::EVENT_MOUSEMOVE; + + e.mouse = getEventManager()->getMousePos(); + + // already multiplied by 100 + e.mouse.x += arg1 * _trackball_scale / _eventScaleX; + e.mouse.y += arg2 * _trackball_scale / _eventScaleY; + + clipMouse(e.mouse); + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + + return; + + case JE_QUIT: + e.type = Common::EVENT_QUIT; + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + + return; + + default: + LOGE("unknown jevent type: %d", type); + + break; + } +} + +bool OSystem_Android::pollEvent(Common::Event &event) { + //ENTER(); + + if (pthread_self() == _main_thread) { + if (_screen_changeid != JNI::surface_changeid) { + if (JNI::egl_surface_width > 0 && JNI::egl_surface_height > 0) { + if (_egl_surface_width > 0 && _egl_surface_height > 0) { + // surface still alive but changed + _screen_changeid = JNI::surface_changeid; + _egl_surface_width = JNI::egl_surface_width; + _egl_surface_height = JNI::egl_surface_height; + + initViewport(); + updateScreenRect(); + updateEventScale(); + + // double buffered, flip twice + clearScreen(kClearUpdate, 2); + + event.type = Common::EVENT_SCREEN_CHANGED; + + return true; + } else { + // new surface + initSurface(); + updateScreenRect(); + updateEventScale(); + + // double buffered, flip twice + clearScreen(kClearUpdate, 2); + + event.type = Common::EVENT_SCREEN_CHANGED; + + return true; + } + } else { + // surface lost + deinitSurface(); + } + } + + if (JNI::pause) { + deinitSurface(); + + LOGD("main thread going to sleep"); + sem_wait(&JNI::pause_sem); + LOGD("main thread woke up"); + } + } + + lockMutex(_event_queue_lock); + + if (_event_queue.empty()) { + unlockMutex(_event_queue_lock); + return false; + } + + event = _event_queue.pop(); + + unlockMutex(_event_queue_lock); + + if (event.type == Common::EVENT_MOUSEMOVE) { + const Common::Point &m = getEventManager()->getMousePos(); + + if (m != event.mouse) + _force_redraw = true; + } + + return true; +} + +#endif + diff --git a/backends/platform/android/gfx.cpp b/backends/platform/android/gfx.cpp new file mode 100644 index 0000000000..d7e31fcad7 --- /dev/null +++ b/backends/platform/android/gfx.cpp @@ -0,0 +1,810 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#if defined(__ANDROID__) + +#include "common/endian.h" +#include "graphics/conversion.h" + +#include "backends/platform/android/android.h" +#include "backends/platform/android/jni.h" + +static inline GLfixed xdiv(int numerator, int denominator) { + assert(numerator < (1 << 16)); + return (numerator << 16) / denominator; +} + +const OSystem::GraphicsMode *OSystem_Android::getSupportedGraphicsModes() const { + static const OSystem::GraphicsMode s_supportedGraphicsModes[] = { + { "default", "Default", 0 }, + { "filter", "Linear filtering", 1 }, + { 0, 0, 0 }, + }; + + return s_supportedGraphicsModes; +} + +int OSystem_Android::getDefaultGraphicsMode() const { + return 0; +} + +bool OSystem_Android::setGraphicsMode(int mode) { + ENTER("%d", mode); + + if (_game_texture) + _game_texture->setLinearFilter(mode == 1); + + if (_overlay_texture) + _overlay_texture->setLinearFilter(mode == 1); + + _graphicsMode = mode; + + return true; +} + +int OSystem_Android::getGraphicsMode() const { + return _graphicsMode; +} + +#ifdef USE_RGB_COLOR +Graphics::PixelFormat OSystem_Android::getScreenFormat() const { + return _game_texture->getPixelFormat(); +} + +Common::List<Graphics::PixelFormat> OSystem_Android::getSupportedFormats() const { + Common::List<Graphics::PixelFormat> res; + res.push_back(GLES565Texture::pixelFormat()); + res.push_back(GLES5551Texture::pixelFormat()); + res.push_back(GLES4444Texture::pixelFormat()); + res.push_back(Graphics::PixelFormat::createFormatCLUT8()); + + return res; +} + +Common::String OSystem_Android::getPixelFormatName(const Graphics::PixelFormat &format) const { + if (format.bytesPerPixel == 1) + return "CLUT8"; + + if (format.aLoss == 8) + return Common::String::format("RGB%u%u%u", + 8 - format.rLoss, + 8 - format.gLoss, + 8 - format.bLoss); + + return Common::String::format("RGBA%u%u%u%u", + 8 - format.rLoss, + 8 - format.gLoss, + 8 - format.bLoss, + 8 - format.aLoss); +} + +void OSystem_Android::initTexture(GLESBaseTexture **texture, + uint width, uint height, + const Graphics::PixelFormat *format) { + assert(texture); + Graphics::PixelFormat format_clut8 = + Graphics::PixelFormat::createFormatCLUT8(); + Graphics::PixelFormat format_current; + Graphics::PixelFormat format_new; + + if (*texture) + format_current = (*texture)->getPixelFormat(); + else + format_current = Graphics::PixelFormat(); + + if (format) + format_new = *format; + else + format_new = format_clut8; + + if (format_current != format_new) { + if (*texture) + LOGD("switching pixel format from: %s", + getPixelFormatName((*texture)->getPixelFormat()).c_str()); + + delete *texture; + + if (format_new == GLES565Texture::pixelFormat()) + *texture = new GLES565Texture(); + else if (format_new == GLES5551Texture::pixelFormat()) + *texture = new GLES5551Texture(); + else if (format_new == GLES4444Texture::pixelFormat()) + *texture = new GLES4444Texture(); + else { + // TODO what now? + if (format_new != format_clut8) + LOGE("unsupported pixel format: %s", + getPixelFormatName(format_new).c_str()); + + *texture = new GLESFakePalette565Texture; + } + + LOGD("new pixel format: %s", + getPixelFormatName((*texture)->getPixelFormat()).c_str()); + } + + (*texture)->allocBuffer(width, height); + (*texture)->fillBuffer(0); +} +#endif + +void OSystem_Android::initSurface() { + LOGD("initializing surface"); + + assert(!JNI::haveSurface()); + + _screen_changeid = JNI::surface_changeid; + _egl_surface_width = JNI::egl_surface_width; + _egl_surface_height = JNI::egl_surface_height; + + assert(_egl_surface_width > 0 && _egl_surface_height > 0); + + JNI::initSurface(); + + // Initialise OpenGLES context. + GLESTexture::initGLExtensions(); + + if (_game_texture) + _game_texture->reinit(); + + if (_overlay_texture) { + _overlay_texture->reinit(); + initOverlay(); + } + + if (_mouse_texture) + _mouse_texture->reinit(); +} + +void OSystem_Android::deinitSurface() { + if (!JNI::haveSurface()) + return; + + LOGD("deinitializing surface"); + + _screen_changeid = JNI::surface_changeid; + _egl_surface_width = 0; + _egl_surface_height = 0; + + // release texture resources + if (_game_texture) + _game_texture->release(); + + if (_overlay_texture) + _overlay_texture->release(); + + if (_mouse_texture) + _mouse_texture->release(); + + JNI::deinitSurface(); +} + +void OSystem_Android::initViewport() { + LOGD("initializing viewport"); + + assert(JNI::haveSurface()); + + // Turn off anything that looks like 3D ;) + GLCALL(glDisable(GL_CULL_FACE)); + GLCALL(glDisable(GL_DEPTH_TEST)); + GLCALL(glDisable(GL_LIGHTING)); + GLCALL(glDisable(GL_FOG)); + GLCALL(glDisable(GL_DITHER)); + + GLCALL(glShadeModel(GL_FLAT)); + GLCALL(glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST)); + + GLCALL(glEnable(GL_BLEND)); + GLCALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + + GLCALL(glEnableClientState(GL_VERTEX_ARRAY)); + GLCALL(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); + + GLCALL(glEnable(GL_TEXTURE_2D)); + + GLCALL(glViewport(0, 0, _egl_surface_width, _egl_surface_height)); + + GLCALL(glMatrixMode(GL_PROJECTION)); + GLCALL(glLoadIdentity()); + GLCALL(glOrthof(0, _egl_surface_width, _egl_surface_height, 0, -1, 1)); + GLCALL(glMatrixMode(GL_MODELVIEW)); + GLCALL(glLoadIdentity()); + + clearFocusRectangle(); +} + +void OSystem_Android::initOverlay() { + int overlay_width = _egl_surface_width; + int overlay_height = _egl_surface_height; + + // the 'normal' theme layout uses a max height of 400 pixels. if the + // surface is too big we use only a quarter of the size so that the widgets + // don't get too small. if the surface height has less than 800 pixels, this + // enforces the 'lowres' layout, which will be scaled back up by factor 2x, + // but this looks way better than the 'normal' layout scaled by some + // calculated factors + if (overlay_height > 480) { + overlay_width /= 2; + overlay_height /= 2; + } + + LOGI("overlay size is %ux%u", overlay_width, overlay_height); + + _overlay_texture->allocBuffer(overlay_width, overlay_height); + _overlay_texture->fillBuffer(0); + _overlay_texture->setDrawRect(0, 0, + _egl_surface_width, _egl_surface_height); +} + +void OSystem_Android::initSize(uint width, uint height, + const Graphics::PixelFormat *format) { + ENTER("%d, %d, %p", width, height, format); + + GLTHREADCHECK; + +#ifdef USE_RGB_COLOR + initTexture(&_game_texture, width, height, format); +#else + _game_texture->allocBuffer(width, height); + _game_texture->fillBuffer(0); +#endif + + updateScreenRect(); + updateEventScale(); + + // Don't know mouse size yet - it gets reallocated in + // setMouseCursor. We need the palette allocated before + // setMouseCursor however, so just take a guess at the desired + // size (it's small). + _mouse_texture_palette->allocBuffer(20, 20); + + clearScreen(kClear); +} + +void OSystem_Android::clearScreen(FixupType type, byte count) { + assert(count > 0); + + GLCALL(glDisable(GL_SCISSOR_TEST)); + + for (byte i = 0; i < count; ++i) { + // clear screen + GLCALL(glClearColorx(0, 0, 0, 1 << 16)); + GLCALL(glClear(GL_COLOR_BUFFER_BIT)); + + switch (type) { + case kClear: + break; + + case kClearSwap: + JNI::swapBuffers(); + break; + + case kClearUpdate: + _force_redraw = true; + updateScreen(); + break; + } + } + + if (!_show_overlay) + GLCALL(glEnable(GL_SCISSOR_TEST)); +} + +void OSystem_Android::updateScreenRect() { + Common::Rect rect(0, 0, _egl_surface_width, _egl_surface_height); + + _overlay_texture->setDrawRect(rect); + + uint16 w = _game_texture->width(); + uint16 h = _game_texture->height(); + + if (w && h && !_fullscreen) { + if (_ar_correction && w == 320 && h == 200) + h = 240; + + float dpi[2]; + JNI::getDPI(dpi); + + float screen_ar; + if (dpi[0] != 0.0 && dpi[1] != 0.0) { + // horizontal orientation + screen_ar = (dpi[1] * _egl_surface_width) / + (dpi[0] * _egl_surface_height); + } else { + screen_ar = float(_egl_surface_width) / float(_egl_surface_height); + } + + float game_ar = float(w) / float(h); + + if (screen_ar > game_ar) { + rect.setWidth(round(_egl_surface_height * game_ar)); + rect.moveTo((_egl_surface_width - rect.width()) / 2, 0); + } else { + rect.setHeight(round(_egl_surface_width / game_ar)); + rect.moveTo((_egl_surface_height - rect.height()) / 2, 0); + } + } + + glScissor(rect.left, rect.top, rect.width(), rect.height()); + + _game_texture->setDrawRect(rect); +} + +int OSystem_Android::getScreenChangeID() const { + return _screen_changeid; +} + +int16 OSystem_Android::getHeight() { + return _game_texture->height(); +} + +int16 OSystem_Android::getWidth() { + return _game_texture->width(); +} + +void OSystem_Android::setPalette(const byte *colors, uint start, uint num) { + ENTER("%p, %u, %u", colors, start, num); + +#ifdef USE_RGB_COLOR + assert(_game_texture->hasPalette()); +#endif + + GLTHREADCHECK; + + if (!_use_mouse_palette) + setCursorPaletteInternal(colors, start, num); + + const Graphics::PixelFormat &pf = _game_texture->getPalettePixelFormat(); + byte *p = _game_texture->palette() + start * 2; + + for (uint i = 0; i < num; ++i, colors += 3, p += 2) + WRITE_UINT16(p, pf.RGBToColor(colors[0], colors[1], colors[2])); +} + +void OSystem_Android::grabPalette(byte *colors, uint start, uint num) { + ENTER("%p, %u, %u", colors, start, num); + +#ifdef USE_RGB_COLOR + assert(_game_texture->hasPalette()); +#endif + + GLTHREADCHECK; + + const Graphics::PixelFormat &pf = _game_texture->getPalettePixelFormat(); + const byte *p = _game_texture->palette_const() + start * 2; + + for (uint i = 0; i < num; ++i, colors += 3, p += 2) + pf.colorToRGB(READ_UINT16(p), colors[0], colors[1], colors[2]); +} + +void OSystem_Android::copyRectToScreen(const byte *buf, int pitch, + int x, int y, int w, int h) { + ENTER("%p, %d, %d, %d, %d, %d", buf, pitch, x, y, w, h); + + GLTHREADCHECK; + + _game_texture->updateBuffer(x, y, w, h, buf, pitch); +} + +void OSystem_Android::updateScreen() { + //ENTER(); + + GLTHREADCHECK; + + if (!JNI::haveSurface()) + return; + + if (!_force_redraw && + !_game_texture->dirty() && + !_overlay_texture->dirty() && + !_mouse_texture->dirty()) + return; + + _force_redraw = false; + + // clear pointer leftovers in dead areas + if (_show_overlay && !_fullscreen) + clearScreen(kClear); + + GLCALL(glPushMatrix()); + + if (_shake_offset != 0 || + (!_focus_rect.isEmpty() && + !Common::Rect(_game_texture->width(), + _game_texture->height()).contains(_focus_rect))) { + // These are the only cases where _game_texture doesn't + // cover the entire screen. + clearScreen(kClear); + + // Move everything up by _shake_offset (game) pixels + GLCALL(glTranslatex(0, -_shake_offset << 16, 0)); + } + +// TODO this doesnt work on those sucky drivers, do it differently +// if (_show_overlay) +// GLCALL(glColor4ub(0x9f, 0x9f, 0x9f, 0x9f)); + + if (_focus_rect.isEmpty()) { + _game_texture->drawTextureRect(); + } else { + GLCALL(glPushMatrix()); + + GLCALL(glScalex(xdiv(_egl_surface_width, _focus_rect.width()), + xdiv(_egl_surface_height, _focus_rect.height()), + 1 << 16)); + GLCALL(glTranslatex(-_focus_rect.left << 16, + -_focus_rect.top << 16, 0)); + GLCALL(glScalex(xdiv(_game_texture->width(), _egl_surface_width), + xdiv(_game_texture->height(), _egl_surface_height), + 1 << 16)); + + _game_texture->drawTextureRect(); + + GLCALL(glPopMatrix()); + } + + int cs = _mouse_targetscale; + + if (_show_overlay) { +// TODO see above +// GLCALL(glColor4ub(0xff, 0xff, 0xff, 0xff)); + + // ugly, but the modern theme sets a wacko factor, only god knows why + cs = 1; + + GLCALL(_overlay_texture->drawTextureRect()); + } + + if (_show_mouse) { + GLCALL(glPushMatrix()); + + const Common::Point &mouse = getEventManager()->getMousePos(); + + // Scale up ScummVM -> OpenGL (pixel) coordinates + if (_show_overlay) { + GLCALL(glScalex(xdiv(_egl_surface_width, + _overlay_texture->width()), + xdiv(_egl_surface_height, + _overlay_texture->height()), + 1 << 16)); + } else { + const Common::Rect &r = _game_texture->getDrawRect(); + + GLCALL(glTranslatex(r.left << 16, + r.top << 16, + 0)); + GLCALL(glScalex(xdiv(r.width(), _game_texture->width()), + xdiv(r.height(), _game_texture->height()), + 1 << 16)); + } + + GLCALL(glTranslatex((-_mouse_hotspot.x * cs) << 16, + (-_mouse_hotspot.y * cs) << 16, + 0)); + + // Note the extra half texel to position the mouse in + // the middle of the x,y square: + GLCALL(glTranslatex((mouse.x << 16) | 1 << 15, + (mouse.y << 16) | 1 << 15, 0)); + + GLCALL(glScalex(cs << 16, cs << 16, 1 << 16)); + + _mouse_texture->drawTextureOrigin(); + + GLCALL(glPopMatrix()); + } + + GLCALL(glPopMatrix()); + + if (!JNI::swapBuffers()) + LOGW("swapBuffers failed: 0x%x", glGetError()); +} + +Graphics::Surface *OSystem_Android::lockScreen() { + ENTER(); + + GLTHREADCHECK; + + Graphics::Surface *surface = _game_texture->surface(); + assert(surface->pixels); + + return surface; +} + +void OSystem_Android::unlockScreen() { + ENTER(); + + GLTHREADCHECK; + + assert(_game_texture->dirty()); +} + +void OSystem_Android::setShakePos(int shake_offset) { + ENTER("%d", shake_offset); + + if (_shake_offset != shake_offset) { + _shake_offset = shake_offset; + _force_redraw = true; + } +} + +void OSystem_Android::fillScreen(uint32 col) { + ENTER("%u", col); + + GLTHREADCHECK; + + _game_texture->fillBuffer(col); +} + +void OSystem_Android::setFocusRectangle(const Common::Rect& rect) { + ENTER("%d, %d, %d, %d", rect.left, rect.top, rect.right, rect.bottom); + + if (_enable_zoning) { + _focus_rect = rect; + _force_redraw = true; + } +} + +void OSystem_Android::clearFocusRectangle() { + ENTER(); + + if (_enable_zoning) { + _focus_rect = Common::Rect(); + _force_redraw = true; + } +} + +void OSystem_Android::showOverlay() { + ENTER(); + + _show_overlay = true; + _force_redraw = true; + + updateEventScale(); + + warpMouse(_overlay_texture->width() / 2, _overlay_texture->height() / 2); + + GLCALL(glDisable(GL_SCISSOR_TEST)); +} + +void OSystem_Android::hideOverlay() { + ENTER(); + + clearScreen(kClear); + + _show_overlay = false; + _force_redraw = true; + + updateEventScale(); + + warpMouse(_game_texture->width() / 2, _game_texture->height() / 2); + + GLCALL(glEnable(GL_SCISSOR_TEST)); +} + +void OSystem_Android::clearOverlay() { + ENTER(); + + GLTHREADCHECK; + + _overlay_texture->fillBuffer(0); + + // breaks more than it fixes, disabled for now + // Shouldn't need this, but works around a 'blank screen' bug on Nexus1 + //updateScreen(); +} + +void OSystem_Android::grabOverlay(OverlayColor *buf, int pitch) { + ENTER("%p, %d", buf, pitch); + + GLTHREADCHECK; + + const Graphics::Surface *surface = _overlay_texture->surface_const(); + assert(surface->bytesPerPixel == sizeof(buf[0])); + + const byte *src = (const byte *)surface->pixels; + uint h = surface->h; + + do { + memcpy(buf, src, surface->w * surface->bytesPerPixel); + src += surface->pitch; + // This 'pitch' is pixels not bytes + buf += pitch; + } while (--h); +} + +void OSystem_Android::copyRectToOverlay(const OverlayColor *buf, int pitch, + int x, int y, int w, int h) { + ENTER("%p, %d, %d, %d, %d, %d", buf, pitch, x, y, w, h); + + GLTHREADCHECK; + + // This 'pitch' is pixels not bytes + _overlay_texture->updateBuffer(x, y, w, h, buf, pitch * sizeof(buf[0])); + + // Shouldn't need this, but works around a 'blank screen' bug on Nexus1? + //updateScreen(); +} + +int16 OSystem_Android::getOverlayHeight() { + return _overlay_texture->height(); +} + +int16 OSystem_Android::getOverlayWidth() { + return _overlay_texture->width(); +} + +Graphics::PixelFormat OSystem_Android::getOverlayFormat() const { + return _overlay_texture->getPixelFormat(); +} + +bool OSystem_Android::showMouse(bool visible) { + ENTER("%d", visible); + + _show_mouse = visible; + + return true; +} + +void OSystem_Android::setMouseCursor(const byte *buf, uint w, uint h, + int hotspotX, int hotspotY, + uint32 keycolor, int cursorTargetScale, + const Graphics::PixelFormat *format) { + ENTER("%p, %u, %u, %d, %d, %u, %d, %p", buf, w, h, hotspotX, hotspotY, + keycolor, cursorTargetScale, format); + + GLTHREADCHECK; + +#ifdef USE_RGB_COLOR + if (format && format->bytesPerPixel > 1) { + if (_mouse_texture != _mouse_texture_rgb) + LOGD("switching to rgb mouse cursor"); + + _mouse_texture_rgb = new GLES5551Texture(); + _mouse_texture = _mouse_texture_rgb; + } else { + if (_mouse_texture != _mouse_texture_palette) + LOGD("switching to paletted mouse cursor"); + + _mouse_texture = _mouse_texture_palette; + + delete _mouse_texture_rgb; + _mouse_texture_rgb = 0; + } +#endif + + _mouse_texture->allocBuffer(w, h); + + if (_mouse_texture == _mouse_texture_palette) { + assert(keycolor < 256); + + byte *p = _mouse_texture_palette->palette() + _mouse_keycolor * 2; + + WRITE_UINT16(p, READ_UINT16(p) | 1); + _mouse_keycolor = keycolor; + WRITE_UINT16(_mouse_texture_palette->palette() + keycolor * 2, 0); + } + + if (w == 0 || h == 0) { + _show_mouse = false; + return; + } + + if (_mouse_texture == _mouse_texture_palette) { + _mouse_texture->updateBuffer(0, 0, w, h, buf, w); + } else { + uint16 pitch = _mouse_texture->pitch(); + + byte *tmp = new byte[pitch * h]; + + // meh, a 16bit cursor without alpha bits... this is so silly + if (!crossBlit(tmp, buf, pitch, w * 2, w, h, + _mouse_texture->getPixelFormat(), + *format)) { + LOGE("crossblit failed"); + + delete[] tmp; + + _show_mouse = false; + + return; + } + + uint16 *s = (uint16 *)buf; + uint16 *d = (uint16 *)tmp; + for (uint16 y = 0; y < h; ++y, d += pitch / 2 - w) + for (uint16 x = 0; x < w; ++x, d++) + if (*s++ != (keycolor & 0xffff)) + *d |= 1; + + _mouse_texture->updateBuffer(0, 0, w, h, tmp, pitch); + + delete[] tmp; + } + + _mouse_hotspot = Common::Point(hotspotX, hotspotY); + _mouse_targetscale = cursorTargetScale; +} + +void OSystem_Android::setCursorPaletteInternal(const byte *colors, + uint start, uint num) { + const Graphics::PixelFormat &pf = + _mouse_texture_palette->getPalettePixelFormat(); + byte *p = _mouse_texture_palette->palette() + start * 2; + + for (uint i = 0; i < num; ++i, colors += 3, p += 2) + WRITE_UINT16(p, pf.RGBToColor(colors[0], colors[1], colors[2])); + + WRITE_UINT16(_mouse_texture_palette->palette() + _mouse_keycolor * 2, 0); +} + +void OSystem_Android::setCursorPalette(const byte *colors, + uint start, uint num) { + ENTER("%p, %u, %u", colors, start, num); + + GLTHREADCHECK; + + if (!_mouse_texture->hasPalette()) { + LOGD("switching to paletted mouse cursor"); + + _mouse_texture = _mouse_texture_palette; + + delete _mouse_texture_rgb; + _mouse_texture_rgb = 0; + } + + setCursorPaletteInternal(colors, start, num); + _use_mouse_palette = true; +} + +void OSystem_Android::disableCursorPalette(bool disable) { + ENTER("%d", disable); + + // when disabling the cursor palette, and we're running a clut8 game, + // it expects the game palette to be used for the cursor + if (disable && _game_texture->hasPalette()) { + const byte *src = _game_texture->palette_const(); + byte *dst = _mouse_texture_palette->palette(); + + const Graphics::PixelFormat &pf_src = + _game_texture->getPalettePixelFormat(); + const Graphics::PixelFormat &pf_dst = + _mouse_texture_palette->getPalettePixelFormat(); + + uint8 r, g, b; + + for (uint i = 0; i < 256; ++i, src += 2, dst += 2) { + pf_src.colorToRGB(READ_UINT16(src), r, g, b); + WRITE_UINT16(dst, pf_dst.RGBToColor(r, g, b)); + } + + WRITE_UINT16(_mouse_texture_palette->palette() + _mouse_keycolor * 2, 0); + } + + _use_mouse_palette = !disable; +} + +#endif + diff --git a/backends/platform/android/jni.cpp b/backends/platform/android/jni.cpp new file mode 100644 index 0000000000..92cb04904c --- /dev/null +++ b/backends/platform/android/jni.cpp @@ -0,0 +1,602 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#if defined(__ANDROID__) + +#include "base/main.h" +#include "common/config-manager.h" +#include "engines/engine.h" + +#include "backends/platform/android/android.h" +#include "backends/platform/android/asset-archive.h" +#include "backends/platform/android/jni.h" + +__attribute__ ((visibility("default"))) +jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { + return JNI::onLoad(vm); +} + +JavaVM *JNI::_vm = 0; +jobject JNI::_jobj = 0; +jobject JNI::_jobj_audio_track = 0; +jobject JNI::_jobj_egl = 0; +jobject JNI::_jobj_egl_display = 0; +jobject JNI::_jobj_egl_surface = 0; + +Common::Archive *JNI::_asset_archive = 0; +OSystem_Android *JNI::_system = 0; + +bool JNI::pause = false; +sem_t JNI::pause_sem = { 0 }; + +int JNI::surface_changeid = 0; +int JNI::egl_surface_width = 0; +int JNI::egl_surface_height = 0; +bool JNI::_ready_for_events = 0; + +jmethodID JNI::_MID_getDPI = 0; +jmethodID JNI::_MID_displayMessageOnOSD = 0; +jmethodID JNI::_MID_setWindowCaption = 0; +jmethodID JNI::_MID_showVirtualKeyboard = 0; +jmethodID JNI::_MID_getSysArchives = 0; +jmethodID JNI::_MID_getPluginDirectories = 0; +jmethodID JNI::_MID_initSurface = 0; +jmethodID JNI::_MID_deinitSurface = 0; + +jmethodID JNI::_MID_EGL10_eglSwapBuffers = 0; + +jmethodID JNI::_MID_AudioTrack_flush = 0; +jmethodID JNI::_MID_AudioTrack_pause = 0; +jmethodID JNI::_MID_AudioTrack_play = 0; +jmethodID JNI::_MID_AudioTrack_stop = 0; +jmethodID JNI::_MID_AudioTrack_write = 0; + +const JNINativeMethod JNI::_natives[] = { + { "create", "(Landroid/content/res/AssetManager;" + "Ljavax/microedition/khronos/egl/EGL10;" + "Ljavax/microedition/khronos/egl/EGLDisplay;" + "Landroid/media/AudioTrack;II)V", + (void *)JNI::create }, + { "destroy", "()V", + (void *)JNI::destroy }, + { "setSurface", "(II)V", + (void *)JNI::setSurface }, + { "main", "([Ljava/lang/String;)I", + (void *)JNI::main }, + { "pushEvent", "(IIIIII)V", + (void *)JNI::pushEvent }, + { "enableZoning", "(Z)V", + (void *)JNI::enableZoning }, + { "setPause", "(Z)V", + (void *)JNI::setPause } +}; + +JNI::JNI() { +} + +JNI::~JNI() { +} + +jint JNI::onLoad(JavaVM *vm) { + _vm = vm; + + JNIEnv *env; + + if (_vm->GetEnv((void **)&env, JNI_VERSION_1_2)) + return JNI_ERR; + + jclass cls = env->FindClass("org/inodes/gus/scummvm/ScummVM"); + if (cls == 0) + return JNI_ERR; + + if (env->RegisterNatives(cls, _natives, ARRAYSIZE(_natives)) < 0) + return JNI_ERR; + + return JNI_VERSION_1_2; +} + +JNIEnv *JNI::getEnv() { + JNIEnv *env = 0; + + jint res = _vm->GetEnv((void **)&env, JNI_VERSION_1_2); + + if (res != JNI_OK) { + LOGE("GetEnv() failed: %d", res); + abort(); + } + + return env; +} + +void JNI::attachThread() { + JNIEnv *env = 0; + + jint res = _vm->AttachCurrentThread(&env, 0); + + if (res != JNI_OK) { + LOGE("AttachCurrentThread() failed: %d", res); + abort(); + } +} + +void JNI::detachThread() { + jint res = _vm->DetachCurrentThread(); + + if (res != JNI_OK) { + LOGE("DetachCurrentThread() failed: %d", res); + abort(); + } +} + +void JNI::setReadyForEvents(bool ready) { + _ready_for_events = ready; +} + +void JNI::throwByName(JNIEnv *env, const char *name, const char *msg) { + jclass cls = env->FindClass(name); + + // if cls is 0, an exception has already been thrown + if (cls != 0) + env->ThrowNew(cls, msg); + + env->DeleteLocalRef(cls); +} + +void JNI::throwRuntimeException(JNIEnv *env, const char *msg) { + throwByName(env, "java/lang/RuntimeException", msg); +} + +// calls to the dark side + +void JNI::getDPI(float *values) { + values[0] = 0.0; + values[1] = 0.0; + + JNIEnv *env = JNI::getEnv(); + + jfloatArray array = env->NewFloatArray(2); + + env->CallVoidMethod(_jobj, _MID_getDPI, array); + + if (env->ExceptionCheck()) { + LOGE("Failed to get DPIs"); + + env->ExceptionDescribe(); + env->ExceptionClear(); + } else { + jfloat *res = env->GetFloatArrayElements(array, 0); + + if (res) { + values[0] = res[0]; + values[1] = res[1]; + + env->ReleaseFloatArrayElements(array, res, 0); + } + } + + env->DeleteLocalRef(array); +} + +void JNI::displayMessageOnOSD(const char *msg) { + JNIEnv *env = JNI::getEnv(); + jstring java_msg = env->NewStringUTF(msg); + + env->CallVoidMethod(_jobj, _MID_displayMessageOnOSD, java_msg); + + if (env->ExceptionCheck()) { + LOGE("Failed to display OSD message"); + + env->ExceptionDescribe(); + env->ExceptionClear(); + } + + env->DeleteLocalRef(java_msg); +} + +void JNI::setWindowCaption(const char *caption) { + JNIEnv *env = JNI::getEnv(); + jstring java_caption = env->NewStringUTF(caption); + + env->CallVoidMethod(_jobj, _MID_setWindowCaption, java_caption); + + if (env->ExceptionCheck()) { + LOGE("Failed to set window caption"); + + env->ExceptionDescribe(); + env->ExceptionClear(); + } + + env->DeleteLocalRef(java_caption); +} + +void JNI::showVirtualKeyboard(bool enable) { + JNIEnv *env = JNI::getEnv(); + + env->CallVoidMethod(_jobj, _MID_showVirtualKeyboard, enable); + + if (env->ExceptionCheck()) { + LOGE("Error trying to show virtual keyboard"); + + env->ExceptionDescribe(); + env->ExceptionClear(); + } +} + +void JNI::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) { + JNIEnv *env = JNI::getEnv(); + + s.add("ASSET", _asset_archive, priority, false); + + jobjectArray array = + (jobjectArray)env->CallObjectMethod(_jobj, _MID_getSysArchives); + + if (env->ExceptionCheck()) { + LOGE("Error finding system archive path"); + + env->ExceptionDescribe(); + env->ExceptionClear(); + + return; + } + + jsize size = env->GetArrayLength(array); + for (jsize i = 0; i < size; ++i) { + jstring path_obj = (jstring)env->GetObjectArrayElement(array, i); + const char *path = env->GetStringUTFChars(path_obj, 0); + + if (path != 0) { + s.addDirectory(path, path, priority); + env->ReleaseStringUTFChars(path_obj, path); + } + + env->DeleteLocalRef(path_obj); + } +} + +void JNI::getPluginDirectories(Common::FSList &dirs) { + JNIEnv *env = JNI::getEnv(); + + jobjectArray array = + (jobjectArray)env->CallObjectMethod(_jobj, _MID_getPluginDirectories); + + if (env->ExceptionCheck()) { + LOGE("Error finding plugin directories"); + + env->ExceptionDescribe(); + env->ExceptionClear(); + + return; + } + + jsize size = env->GetArrayLength(array); + for (jsize i = 0; i < size; ++i) { + jstring path_obj = (jstring)env->GetObjectArrayElement(array, i); + + if (path_obj == 0) + continue; + + const char *path = env->GetStringUTFChars(path_obj, 0); + + if (path == 0) { + LOGE("Error getting string characters from plugin directory"); + + env->ExceptionClear(); + env->DeleteLocalRef(path_obj); + + continue; + } + + dirs.push_back(Common::FSNode(path)); + + env->ReleaseStringUTFChars(path_obj, path); + env->DeleteLocalRef(path_obj); + } +} + +bool JNI::initSurface() { + JNIEnv *env = JNI::getEnv(); + + jobject obj = env->CallObjectMethod(_jobj, _MID_initSurface); + + if (!obj || env->ExceptionCheck()) { + LOGE("initSurface failed"); + + env->ExceptionDescribe(); + env->ExceptionClear(); + + return false; + } + + _jobj_egl_surface = env->NewGlobalRef(obj); + + return true; +} + +void JNI::deinitSurface() { + JNIEnv *env = JNI::getEnv(); + + env->CallVoidMethod(_jobj, _MID_deinitSurface); + + if (env->ExceptionCheck()) { + LOGE("deinitSurface failed"); + + env->ExceptionDescribe(); + env->ExceptionClear(); + } + + env->DeleteGlobalRef(_jobj_egl_surface); + _jobj_egl_surface = 0; +} + +void JNI::setAudioPause() { + JNIEnv *env = JNI::getEnv(); + + env->CallVoidMethod(_jobj_audio_track, _MID_AudioTrack_flush); + + if (env->ExceptionCheck()) { + LOGE("Error flushing AudioTrack"); + + env->ExceptionDescribe(); + env->ExceptionClear(); + } + + env->CallVoidMethod(_jobj_audio_track, _MID_AudioTrack_pause); + + if (env->ExceptionCheck()) { + LOGE("Error setting AudioTrack: pause"); + + env->ExceptionDescribe(); + env->ExceptionClear(); + } +} + +void JNI::setAudioPlay() { + JNIEnv *env = JNI::getEnv(); + + env->CallVoidMethod(_jobj_audio_track, _MID_AudioTrack_play); + + if (env->ExceptionCheck()) { + LOGE("Error setting AudioTrack: play"); + + env->ExceptionDescribe(); + env->ExceptionClear(); + } +} + +void JNI::setAudioStop() { + JNIEnv *env = JNI::getEnv(); + + env->CallVoidMethod(_jobj_audio_track, _MID_AudioTrack_stop); + + if (env->ExceptionCheck()) { + LOGE("Error setting AudioTrack: stop"); + + env->ExceptionDescribe(); + env->ExceptionClear(); + } +} + +// natives for the dark side + +void JNI::create(JNIEnv *env, jobject self, jobject asset_manager, + jobject egl, jobject egl_display, + jobject at, jint audio_sample_rate, jint audio_buffer_size) { + assert(!_system); + + pause = false; + // initial value of zero! + sem_init(&pause_sem, 0, 0); + + _asset_archive = new AndroidAssetArchive(asset_manager); + assert(_asset_archive); + + _system = new OSystem_Android(audio_sample_rate, audio_buffer_size); + assert(_system); + + // weak global ref to allow class to be unloaded + // ... except dalvik implements NewWeakGlobalRef only on froyo + //_jobj = env->NewWeakGlobalRef(self); + + _jobj = env->NewGlobalRef(self); + + jclass cls = env->GetObjectClass(_jobj); + +#define FIND_METHOD(prefix, name, signature) do { \ + _MID_ ## prefix ## name = env->GetMethodID(cls, #name, signature); \ + if (_MID_ ## prefix ## name == 0) \ + return; \ + } while (0) + + FIND_METHOD(, setWindowCaption, "(Ljava/lang/String;)V"); + FIND_METHOD(, getDPI, "([F)V"); + FIND_METHOD(, displayMessageOnOSD, "(Ljava/lang/String;)V"); + FIND_METHOD(, showVirtualKeyboard, "(Z)V"); + FIND_METHOD(, getSysArchives, "()[Ljava/lang/String;"); + FIND_METHOD(, getPluginDirectories, "()[Ljava/lang/String;"); + FIND_METHOD(, initSurface, "()Ljavax/microedition/khronos/egl/EGLSurface;"); + FIND_METHOD(, deinitSurface, "()V"); + + _jobj_egl = env->NewGlobalRef(egl); + _jobj_egl_display = env->NewGlobalRef(egl_display); + + cls = env->GetObjectClass(_jobj_egl); + + FIND_METHOD(EGL10_, eglSwapBuffers, + "(Ljavax/microedition/khronos/egl/EGLDisplay;" + "Ljavax/microedition/khronos/egl/EGLSurface;)Z"); + + _jobj_audio_track = env->NewGlobalRef(at); + + cls = env->GetObjectClass(_jobj_audio_track); + + FIND_METHOD(AudioTrack_, flush, "()V"); + FIND_METHOD(AudioTrack_, pause, "()V"); + FIND_METHOD(AudioTrack_, play, "()V"); + FIND_METHOD(AudioTrack_, stop, "()V"); + FIND_METHOD(AudioTrack_, write, "([BII)I"); + +#undef FIND_METHOD + + g_system = _system; +} + +void JNI::destroy(JNIEnv *env, jobject self) { + delete _asset_archive; + _asset_archive = 0; + + delete _system; + g_system = 0; + _system = 0; + + sem_destroy(&pause_sem); + + // see above + //JNI::getEnv()->DeleteWeakGlobalRef(_jobj); + + JNI::getEnv()->DeleteGlobalRef(_jobj_egl_display); + JNI::getEnv()->DeleteGlobalRef(_jobj_egl); + JNI::getEnv()->DeleteGlobalRef(_jobj_audio_track); + JNI::getEnv()->DeleteGlobalRef(_jobj); +} + +void JNI::setSurface(JNIEnv *env, jobject self, jint width, jint height) { + egl_surface_width = width; + egl_surface_height = height; + surface_changeid++; +} + +jint JNI::main(JNIEnv *env, jobject self, jobjectArray args) { + assert(_system); + + const int MAX_NARGS = 32; + int res = -1; + + int argc = env->GetArrayLength(args); + if (argc > MAX_NARGS) { + throwByName(env, "java/lang/IllegalArgumentException", + "too many arguments"); + return 0; + } + + char *argv[MAX_NARGS]; + + // note use in cleanup loop below + int nargs; + + for (nargs = 0; nargs < argc; ++nargs) { + jstring arg = (jstring)env->GetObjectArrayElement(args, nargs); + + if (arg == 0) { + argv[nargs] = 0; + } else { + const char *cstr = env->GetStringUTFChars(arg, 0); + + argv[nargs] = const_cast<char *>(cstr); + + // exception already thrown? + if (cstr == 0) + goto cleanup; + } + + env->DeleteLocalRef(arg); + } + +#ifdef DYNAMIC_MODULES + PluginManager::instance().addPluginProvider(new AndroidPluginProvider()); +#endif + + LOGI("Entering scummvm_main with %d args", argc); + + res = scummvm_main(argc, argv); + + LOGI("scummvm_main exited with code %d", res); + + _system->quit(); + +cleanup: + nargs--; + + for (int i = 0; i < nargs; ++i) { + if (argv[i] == 0) + continue; + + jstring arg = (jstring)env->GetObjectArrayElement(args, nargs); + + // Exception already thrown? + if (arg == 0) + return res; + + env->ReleaseStringUTFChars(arg, argv[i]); + env->DeleteLocalRef(arg); + } + + return res; +} + +void JNI::pushEvent(JNIEnv *env, jobject self, int type, int arg1, int arg2, + int arg3, int arg4, int arg5) { + // drop events until we're ready and after we quit + if (!_ready_for_events) { + LOGW("dropping event"); + return; + } + + assert(_system); + + _system->pushEvent(type, arg1, arg2, arg3, arg4, arg5); +} + +void JNI::enableZoning(JNIEnv *env, jobject self, jboolean enable) { + assert(_system); + + _system->enableZoning(enable); +} + +void JNI::setPause(JNIEnv *env, jobject self, jboolean value) { + if (!_system) + return; + + if (g_engine) { + LOGD("pauseEngine: %d", value); + + g_engine->pauseEngine(value); + + if (value && + g_engine->hasFeature(Engine::kSupportsSavingDuringRuntime) && + g_engine->canSaveGameStateCurrently()) + g_engine->saveGameState(0, "Android parachute"); + } + + pause = value; + + if (!pause) { + // wake up all threads + for (uint i = 0; i < 3; ++i) + sem_post(&pause_sem); + } +} + +#endif + diff --git a/backends/platform/android/jni.h b/backends/platform/android/jni.h new file mode 100644 index 0000000000..d029f1a2a8 --- /dev/null +++ b/backends/platform/android/jni.h @@ -0,0 +1,151 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef _ANDROID_JNI_H_ +#define _ANDROID_JNI_H_ + +#if defined(__ANDROID__) + +#include <jni.h> +#include <semaphore.h> + +#include "common/fs.h" +#include "common/archive.h" + +class OSystem_Android; + +class JNI { +private: + JNI(); + virtual ~JNI(); + +public: + static bool pause; + static sem_t pause_sem; + + static int surface_changeid; + static int egl_surface_width; + static int egl_surface_height; + + static jint onLoad(JavaVM *vm); + + static JNIEnv *getEnv(); + + static void attachThread(); + static void detachThread(); + + static void setReadyForEvents(bool ready); + + static void getPluginDirectories(Common::FSList &dirs); + static void setWindowCaption(const char *caption); + static void getDPI(float *values); + static void displayMessageOnOSD(const char *msg); + static void showVirtualKeyboard(bool enable); + static void addSysArchivesToSearchSet(Common::SearchSet &s, int priority); + + static inline bool haveSurface(); + static inline bool swapBuffers(); + static bool initSurface(); + static void deinitSurface(); + + static void setAudioPause(); + static void setAudioPlay(); + static void setAudioStop(); + + static inline int writeAudio(JNIEnv *env, jbyteArray &data, int offset, + int size); + +private: + static JavaVM *_vm; + // back pointer to (java) peer instance + static jobject _jobj; + static jobject _jobj_audio_track; + static jobject _jobj_egl; + static jobject _jobj_egl_display; + static jobject _jobj_egl_surface; + + static Common::Archive *_asset_archive; + static OSystem_Android *_system; + + static bool _ready_for_events; + + static jmethodID _MID_getDPI; + static jmethodID _MID_displayMessageOnOSD; + static jmethodID _MID_setWindowCaption; + static jmethodID _MID_showVirtualKeyboard; + static jmethodID _MID_getSysArchives; + static jmethodID _MID_getPluginDirectories; + static jmethodID _MID_initSurface; + static jmethodID _MID_deinitSurface; + + static jmethodID _MID_EGL10_eglSwapBuffers; + + static jmethodID _MID_AudioTrack_flush; + static jmethodID _MID_AudioTrack_pause; + static jmethodID _MID_AudioTrack_play; + static jmethodID _MID_AudioTrack_stop; + static jmethodID _MID_AudioTrack_write; + + static const JNINativeMethod _natives[]; + + static void throwByName(JNIEnv *env, const char *name, const char *msg); + static void throwRuntimeException(JNIEnv *env, const char *msg); + + // natives for the dark side + static void create(JNIEnv *env, jobject self, jobject asset_manager, + jobject egl, jobject egl_display, + jobject at, jint audio_sample_rate, + jint audio_buffer_size); + static void destroy(JNIEnv *env, jobject self); + + static void setSurface(JNIEnv *env, jobject self, jint width, jint height); + static jint main(JNIEnv *env, jobject self, jobjectArray args); + + static void pushEvent(JNIEnv *env, jobject self, int type, int arg1, + int arg2, int arg3, int arg4, int arg5); + static void enableZoning(JNIEnv *env, jobject self, jboolean enable); + + static void setPause(JNIEnv *env, jobject self, jboolean value); +}; + +inline bool JNI::haveSurface() { + return _jobj_egl_surface != 0; +} + +inline bool JNI::swapBuffers() { + JNIEnv *env = JNI::getEnv(); + + return env->CallBooleanMethod(_jobj_egl, _MID_EGL10_eglSwapBuffers, + _jobj_egl_display, _jobj_egl_surface); +} + +inline int JNI::writeAudio(JNIEnv *env, jbyteArray &data, int offset, int size) { + return env->CallIntMethod(_jobj_audio_track, _MID_AudioTrack_write, data, + offset, size); +} + +#endif +#endif + diff --git a/backends/platform/android/module.mk b/backends/platform/android/module.mk index 8b120b21ff..2fe4b40585 100644 --- a/backends/platform/android/module.mk +++ b/backends/platform/android/module.mk @@ -1,9 +1,12 @@ MODULE := backends/platform/android MODULE_OBJS := \ - android.o \ + jni.o \ + texture.o \ asset-archive.o \ - video.o + android.o \ + gfx.o \ + events.o # We don't use rules.mk but rather manually update OBJS and MODULE_DIRS. MODULE_OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS)) diff --git a/backends/platform/android/org/inodes/gus/scummvm/EditableSurfaceView.java b/backends/platform/android/org/inodes/gus/scummvm/EditableSurfaceView.java index 5b71d4a3a5..cede7eedd4 100644 --- a/backends/platform/android/org/inodes/gus/scummvm/EditableSurfaceView.java +++ b/backends/platform/android/org/inodes/gus/scummvm/EditableSurfaceView.java @@ -19,13 +19,13 @@ public class EditableSurfaceView extends SurfaceView { } public EditableSurfaceView(Context context, AttributeSet attrs, - int defStyle) { + int defStyle) { super(context, attrs, defStyle); } @Override public boolean onCheckIsTextEditor() { - return true; + return false; } private class MyInputConnection extends BaseInputConnection { @@ -40,7 +40,9 @@ public class EditableSurfaceView extends SurfaceView { getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(getWindowToken(), 0); } - return super.performEditorAction(actionCode); // Sends enter key + + // Sends enter key + return super.performEditorAction(actionCode); } } @@ -49,11 +51,12 @@ public class EditableSurfaceView extends SurfaceView { outAttrs.initialCapsMode = 0; outAttrs.initialSelEnd = outAttrs.initialSelStart = -1; outAttrs.inputType = (InputType.TYPE_CLASS_TEXT | - InputType.TYPE_TEXT_VARIATION_NORMAL | - InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE); + InputType.TYPE_TEXT_VARIATION_NORMAL | + InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE); outAttrs.imeOptions = (EditorInfo.IME_ACTION_DONE | - EditorInfo.IME_FLAG_NO_EXTRACT_UI); + EditorInfo.IME_FLAG_NO_EXTRACT_UI); return new MyInputConnection(); } } + diff --git a/backends/platform/android/org/inodes/gus/scummvm/Event.java b/backends/platform/android/org/inodes/gus/scummvm/Event.java deleted file mode 100644 index f9c7aba93b..0000000000 --- a/backends/platform/android/org/inodes/gus/scummvm/Event.java +++ /dev/null @@ -1,330 +0,0 @@ -package org.inodes.gus.scummvm; - -import android.view.KeyEvent; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -public class Event { - // Common::EventType enum. - // Must be kept in sync with common/events.h - public final static int EVENT_INVALID = 0; - public final static int EVENT_KEYDOWN = 1; - public final static int EVENT_KEYUP = 2; - public final static int EVENT_MOUSEMOVE = 3; - public final static int EVENT_LBUTTONDOWN = 4; - public final static int EVENT_LBUTTONUP = 5; - public final static int EVENT_RBUTTONDOWN = 6; - public final static int EVENT_RBUTTONUP = 7; - public final static int EVENT_WHEELUP = 8; - public final static int EVENT_WHEELDOWN = 9; - public final static int EVENT_QUIT = 10; - public final static int EVENT_SCREEN_CHANGED = 11; - public final static int EVENT_PREDICTIVE_DIALOG = 12; - public final static int EVENT_MBUTTONDOWN = 13; - public final static int EVENT_MBUTTONUP = 14; - public final static int EVENT_MAINMENU = 15; - public final static int EVENT_RTL = 16; - - // common/keyboard.h - public final static int ASCII_F1 = 315; - public final static int ASCII_F2 = 316; - public final static int ASCII_F3 = 317; - public final static int ASCII_F4 = 318; - public final static int ASCII_F5 = 319; - public final static int ASCII_F6 = 320; - public final static int ASCII_F7 = 321; - public final static int ASCII_F8 = 322; - public final static int ASCII_F9 = 323; - public final static int ASCII_F10 = 324; - public final static int ASCII_F11 = 325; - public final static int ASCII_F12 = 326; - public final static int KBD_CTRL = 1 << 0; - public final static int KBD_ALT = 1 << 1; - public final static int KBD_SHIFT = 1 << 2; - - public final static int KEYCODE_INVALID = 0; - public final static int KEYCODE_BACKSPACE = 8; - public final static int KEYCODE_TAB = 9; - public final static int KEYCODE_CLEAR = 12; - public final static int KEYCODE_RETURN = 13; - public final static int KEYCODE_PAUSE = 19; - public final static int KEYCODE_ESCAPE = 27; - public final static int KEYCODE_SPACE = 32; - public final static int KEYCODE_EXCLAIM = 33; - public final static int KEYCODE_QUOTEDBL = 34; - public final static int KEYCODE_HASH = 35; - public final static int KEYCODE_DOLLAR = 36; - public final static int KEYCODE_AMPERSAND = 38; - public final static int KEYCODE_QUOTE = 39; - public final static int KEYCODE_LEFTPAREN = 40; - public final static int KEYCODE_RIGHTPAREN = 41; - public final static int KEYCODE_ASTERISK = 42; - public final static int KEYCODE_PLUS = 43; - public final static int KEYCODE_COMMA = 44; - public final static int KEYCODE_MINUS = 45; - public final static int KEYCODE_PERIOD = 46; - public final static int KEYCODE_SLASH = 47; - public final static int KEYCODE_0 = 48; - public final static int KEYCODE_1 = 49; - public final static int KEYCODE_2 = 50; - public final static int KEYCODE_3 = 51; - public final static int KEYCODE_4 = 52; - public final static int KEYCODE_5 = 53; - public final static int KEYCODE_6 = 54; - public final static int KEYCODE_7 = 55; - public final static int KEYCODE_8 = 56; - public final static int KEYCODE_9 = 57; - public final static int KEYCODE_COLON = 58; - public final static int KEYCODE_SEMICOLON = 59; - public final static int KEYCODE_LESS = 60; - public final static int KEYCODE_EQUALS = 61; - public final static int KEYCODE_GREATER = 62; - public final static int KEYCODE_QUESTION = 63; - public final static int KEYCODE_AT = 64; - public final static int KEYCODE_LEFTBRACKET = 91; - public final static int KEYCODE_BACKSLASH = 92; - public final static int KEYCODE_RIGHTBRACKET = 93; - public final static int KEYCODE_CARET = 94; - public final static int KEYCODE_UNDERSCORE = 95; - public final static int KEYCODE_BACKQUOTE = 96; - public final static int KEYCODE_a = 97; - public final static int KEYCODE_b = 98; - public final static int KEYCODE_c = 99; - public final static int KEYCODE_d = 100; - public final static int KEYCODE_e = 101; - public final static int KEYCODE_f = 102; - public final static int KEYCODE_g = 103; - public final static int KEYCODE_h = 104; - public final static int KEYCODE_i = 105; - public final static int KEYCODE_j = 106; - public final static int KEYCODE_k = 107; - public final static int KEYCODE_l = 108; - public final static int KEYCODE_m = 109; - public final static int KEYCODE_n = 110; - public final static int KEYCODE_o = 111; - public final static int KEYCODE_p = 112; - public final static int KEYCODE_q = 113; - public final static int KEYCODE_r = 114; - public final static int KEYCODE_s = 115; - public final static int KEYCODE_t = 116; - public final static int KEYCODE_u = 117; - public final static int KEYCODE_v = 118; - public final static int KEYCODE_w = 119; - public final static int KEYCODE_x = 120; - public final static int KEYCODE_y = 121; - public final static int KEYCODE_z = 122; - public final static int KEYCODE_DELETE = 127; - // Numeric keypad - public final static int KEYCODE_KP0 = 256; - public final static int KEYCODE_KP1 = 257; - public final static int KEYCODE_KP2 = 258; - public final static int KEYCODE_KP3 = 259; - public final static int KEYCODE_KP4 = 260; - public final static int KEYCODE_KP5 = 261; - public final static int KEYCODE_KP6 = 262; - public final static int KEYCODE_KP7 = 263; - public final static int KEYCODE_KP8 = 264; - public final static int KEYCODE_KP9 = 265; - public final static int KEYCODE_KP_PERIOD = 266; - public final static int KEYCODE_KP_DIVIDE = 267; - public final static int KEYCODE_KP_MULTIPLY = 268; - public final static int KEYCODE_KP_MINUS = 269; - public final static int KEYCODE_KP_PLUS = 270; - public final static int KEYCODE_KP_ENTER = 271; - public final static int KEYCODE_KP_EQUALS = 272; - // Arrows + Home/End pad - public final static int KEYCODE_UP = 273; - public final static int KEYCODE_DOWN = 274; - public final static int KEYCODE_RIGHT = 275; - public final static int KEYCODE_LEFT = 276; - public final static int KEYCODE_INSERT = 277; - public final static int KEYCODE_HOME = 278; - public final static int KEYCODE_END = 279; - public final static int KEYCODE_PAGEUP = 280; - public final static int KEYCODE_PAGEDOWN = 281; - // Function keys - public final static int KEYCODE_F1 = 282; - public final static int KEYCODE_F2 = 283; - public final static int KEYCODE_F3 = 284; - public final static int KEYCODE_F4 = 285; - public final static int KEYCODE_F5 = 286; - public final static int KEYCODE_F6 = 287; - public final static int KEYCODE_F7 = 288; - public final static int KEYCODE_F8 = 289; - public final static int KEYCODE_F9 = 290; - public final static int KEYCODE_F10 = 291; - public final static int KEYCODE_F11 = 292; - public final static int KEYCODE_F12 = 293; - public final static int KEYCODE_F13 = 294; - public final static int KEYCODE_F14 = 295; - public final static int KEYCODE_F15 = 296; - // Key state modifier keys - public final static int KEYCODE_NUMLOCK = 300; - public final static int KEYCODE_CAPSLOCK = 301; - public final static int KEYCODE_SCROLLOCK = 302; - public final static int KEYCODE_RSHIFT = 303; - public final static int KEYCODE_LSHIFT = 304; - public final static int KEYCODE_RCTRL = 305; - public final static int KEYCODE_LCTRL = 306; - public final static int KEYCODE_RALT = 307; - public final static int KEYCODE_LALT = 308; - public final static int KEYCODE_RMETA = 309; - public final static int KEYCODE_LMETA = 310; - public final static int KEYCODE_LSUPER = 311; // Left "Windows" key - public final static int KEYCODE_RSUPER = 312; // Right "Windows" key - public final static int KEYCODE_MODE = 313; // "Alt Gr" key - public final static int KEYCODE_COMPOSE = 314; // Multi-key compose key - // Miscellaneous function keys - public final static int KEYCODE_HELP = 315; - public final static int KEYCODE_PRINT = 316; - public final static int KEYCODE_SYSREQ = 317; - public final static int KEYCODE_BREAK = 318; - public final static int KEYCODE_MENU = 319; - public final static int KEYCODE_POWER = 320; // Power Macintosh power key - public final static int KEYCODE_EURO = 321; // Some european keyboards - public final static int KEYCODE_UNDO = 322; // Atari keyboard has Undo - - // Android KeyEvent keycode -> ScummVM keycode - public final static Map<Integer, Integer> androidKeyMap; - static { - Map<Integer, Integer> map = new HashMap<Integer, Integer>(); - - map.put(KeyEvent.KEYCODE_DEL, KEYCODE_BACKSPACE); - map.put(KeyEvent.KEYCODE_TAB, KEYCODE_TAB); - map.put(KeyEvent.KEYCODE_CLEAR, KEYCODE_CLEAR); - map.put(KeyEvent.KEYCODE_ENTER, KEYCODE_RETURN); - //map.put(??, KEYCODE_PAUSE); - map.put(KeyEvent.KEYCODE_BACK, KEYCODE_ESCAPE); - map.put(KeyEvent.KEYCODE_SPACE, KEYCODE_SPACE); - //map.put(??, KEYCODE_EXCLAIM); - //map.put(??, KEYCODE_QUOTEDBL); - map.put(KeyEvent.KEYCODE_POUND, KEYCODE_HASH); - //map.put(??, KEYCODE_DOLLAR); - //map.put(??, KEYCODE_AMPERSAND); - map.put(KeyEvent.KEYCODE_APOSTROPHE, KEYCODE_QUOTE); - //map.put(??, KEYCODE_LEFTPAREN); - //map.put(??, KEYCODE_RIGHTPAREN); - //map.put(??, KEYCODE_ASTERISK); - map.put(KeyEvent.KEYCODE_PLUS, KEYCODE_PLUS); - map.put(KeyEvent.KEYCODE_COMMA, KEYCODE_COMMA); - map.put(KeyEvent.KEYCODE_MINUS, KEYCODE_MINUS); - map.put(KeyEvent.KEYCODE_PERIOD, KEYCODE_PERIOD); - map.put(KeyEvent.KEYCODE_SLASH, KEYCODE_SLASH); - map.put(KeyEvent.KEYCODE_0, KEYCODE_0); - map.put(KeyEvent.KEYCODE_1, KEYCODE_1); - map.put(KeyEvent.KEYCODE_2, KEYCODE_2); - map.put(KeyEvent.KEYCODE_3, KEYCODE_3); - map.put(KeyEvent.KEYCODE_4, KEYCODE_4); - map.put(KeyEvent.KEYCODE_5, KEYCODE_5); - map.put(KeyEvent.KEYCODE_6, KEYCODE_6); - map.put(KeyEvent.KEYCODE_7, KEYCODE_7); - map.put(KeyEvent.KEYCODE_8, KEYCODE_8); - map.put(KeyEvent.KEYCODE_9, KEYCODE_9); - //map.put(??, KEYCODE_COLON); - map.put(KeyEvent.KEYCODE_SEMICOLON, KEYCODE_SEMICOLON); - //map.put(??, KEYCODE_LESS); - map.put(KeyEvent.KEYCODE_EQUALS, KEYCODE_EQUALS); - //map.put(??, KEYCODE_GREATER); - //map.put(??, KEYCODE_QUESTION); - map.put(KeyEvent.KEYCODE_AT, KEYCODE_AT); - map.put(KeyEvent.KEYCODE_LEFT_BRACKET, KEYCODE_LEFTBRACKET); - map.put(KeyEvent.KEYCODE_BACKSLASH, KEYCODE_BACKSLASH); - map.put(KeyEvent.KEYCODE_RIGHT_BRACKET, KEYCODE_RIGHTBRACKET); - //map.put(??, KEYCODE_CARET); - //map.put(??, KEYCODE_UNDERSCORE); - //map.put(??, KEYCODE_BACKQUOTE); - map.put(KeyEvent.KEYCODE_A, KEYCODE_a); - map.put(KeyEvent.KEYCODE_B, KEYCODE_b); - map.put(KeyEvent.KEYCODE_C, KEYCODE_c); - map.put(KeyEvent.KEYCODE_D, KEYCODE_d); - map.put(KeyEvent.KEYCODE_E, KEYCODE_e); - map.put(KeyEvent.KEYCODE_F, KEYCODE_f); - map.put(KeyEvent.KEYCODE_G, KEYCODE_g); - map.put(KeyEvent.KEYCODE_H, KEYCODE_h); - map.put(KeyEvent.KEYCODE_I, KEYCODE_i); - map.put(KeyEvent.KEYCODE_J, KEYCODE_j); - map.put(KeyEvent.KEYCODE_K, KEYCODE_k); - map.put(KeyEvent.KEYCODE_L, KEYCODE_l); - map.put(KeyEvent.KEYCODE_M, KEYCODE_m); - map.put(KeyEvent.KEYCODE_N, KEYCODE_n); - map.put(KeyEvent.KEYCODE_O, KEYCODE_o); - map.put(KeyEvent.KEYCODE_P, KEYCODE_p); - map.put(KeyEvent.KEYCODE_Q, KEYCODE_q); - map.put(KeyEvent.KEYCODE_R, KEYCODE_r); - map.put(KeyEvent.KEYCODE_S, KEYCODE_s); - map.put(KeyEvent.KEYCODE_T, KEYCODE_t); - map.put(KeyEvent.KEYCODE_U, KEYCODE_u); - map.put(KeyEvent.KEYCODE_V, KEYCODE_v); - map.put(KeyEvent.KEYCODE_W, KEYCODE_w); - map.put(KeyEvent.KEYCODE_X, KEYCODE_x); - map.put(KeyEvent.KEYCODE_Y, KEYCODE_y); - map.put(KeyEvent.KEYCODE_Z, KEYCODE_z); - //map.put(KeyEvent.KEYCODE_DEL, KEYCODE_DELETE); use BACKSPACE instead - //map.put(??, KEYCODE_KP_*); - map.put(KeyEvent.KEYCODE_DPAD_UP, KEYCODE_UP); - map.put(KeyEvent.KEYCODE_DPAD_DOWN, KEYCODE_DOWN); - map.put(KeyEvent.KEYCODE_DPAD_RIGHT, KEYCODE_RIGHT); - map.put(KeyEvent.KEYCODE_DPAD_LEFT, KEYCODE_LEFT); - //map.put(??, KEYCODE_INSERT); - //map.put(??, KEYCODE_HOME); - //map.put(??, KEYCODE_END); - //map.put(??, KEYCODE_PAGEUP); - //map.put(??, KEYCODE_PAGEDOWN); - //map.put(??, KEYCODE_F{1-15}); - map.put(KeyEvent.KEYCODE_NUM, KEYCODE_NUMLOCK); - //map.put(??, KEYCODE_CAPSLOCK); - //map.put(??, KEYCODE_SCROLLLOCK); - map.put(KeyEvent.KEYCODE_SHIFT_RIGHT, KEYCODE_RSHIFT); - map.put(KeyEvent.KEYCODE_SHIFT_LEFT, KEYCODE_LSHIFT); - //map.put(??, KEYCODE_RCTRL); - //map.put(??, KEYCODE_LCTRL); - map.put(KeyEvent.KEYCODE_ALT_RIGHT, KEYCODE_RALT); - map.put(KeyEvent.KEYCODE_ALT_LEFT, KEYCODE_LALT); - // ?? META, SUPER - // ?? MODE, COMPOSE - // ?? HELP, PRINT, SYSREQ, BREAK, EURO, UNDO - map.put(KeyEvent.KEYCODE_MENU, KEYCODE_MENU); - map.put(KeyEvent.KEYCODE_POWER, KEYCODE_POWER); - - androidKeyMap = Collections.unmodifiableMap(map); - } - - public int type; - public boolean synthetic; - public int kbd_keycode; - public int kbd_ascii; - public int kbd_flags; - public int mouse_x; - public int mouse_y; - public boolean mouse_relative; // Used for trackball events - - public Event() { - type = EVENT_INVALID; - synthetic = false; - } - - public Event(int type) { - this.type = type; - synthetic = false; - } - - public static Event KeyboardEvent(int type, int keycode, int ascii, - int flags) { - Event e = new Event(); - e.type = type; - e.kbd_keycode = keycode; - e.kbd_ascii = ascii; - e.kbd_flags = flags; - return e; - } - - public static Event MouseEvent(int type, int x, int y) { - Event e = new Event(); - e.type = type; - e.mouse_x = x; - e.mouse_y = y; - return e; - } -} diff --git a/backends/platform/android/org/inodes/gus/scummvm/PluginProvider.java b/backends/platform/android/org/inodes/gus/scummvm/PluginProvider.java index c94ab0a3ff..3c91d9f5dc 100644 --- a/backends/platform/android/org/inodes/gus/scummvm/PluginProvider.java +++ b/backends/platform/android/org/inodes/gus/scummvm/PluginProvider.java @@ -28,7 +28,7 @@ public class PluginProvider extends BroadcastReceiver { try { info = context.getPackageManager() .getReceiverInfo(new ComponentName(context, this.getClass()), - PackageManager.GET_META_DATA); + PackageManager.GET_META_DATA); } catch (PackageManager.NameNotFoundException e) { Log.e(LOG_TAG, "Error finding my own info?", e); return; @@ -38,17 +38,17 @@ public class PluginProvider extends BroadcastReceiver { if (mylib != null) { ArrayList<String> all_libs = extras.getStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS); - all_libs.add(new Uri.Builder() - .scheme("plugin") - .authority(context.getPackageName()) - .path(mylib) - .toString()); + .scheme("plugin") + .authority(context.getPackageName()) + .path(mylib) + .toString()); extras.putStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS, - all_libs); + all_libs); } setResultExtras(extras); } } + diff --git a/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java b/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java index 0e905f43a5..fe225af48b 100644 --- a/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java +++ b/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java @@ -1,446 +1,427 @@ package org.inodes.gus.scummvm; -import android.content.Context; +import android.util.Log; import android.content.res.AssetManager; +import android.view.SurfaceHolder; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioTrack; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Process; -import android.util.Log; -import android.view.Surface; -import android.view.SurfaceHolder; -import javax.microedition.khronos.opengles.GL; import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.egl.EGL10; -import javax.microedition.khronos.egl.EGL11; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLContext; import javax.microedition.khronos.egl.EGLDisplay; import javax.microedition.khronos.egl.EGLSurface; import java.io.File; -import java.util.concurrent.Semaphore; -import java.util.Map; import java.util.LinkedHashMap; +public abstract class ScummVM implements SurfaceHolder.Callback, Runnable { + final protected static String LOG_TAG = "ScummVM"; + final private AssetManager _asset_manager; + final private Object _sem_surface; + + private EGL10 _egl; + private EGLDisplay _egl_display = EGL10.EGL_NO_DISPLAY; + private EGLConfig _egl_config; + private EGLContext _egl_context = EGL10.EGL_NO_CONTEXT; + private EGLSurface _egl_surface = EGL10.EGL_NO_SURFACE; + + private SurfaceHolder _surface_holder; + private AudioTrack _audio_track; + private int _sample_rate = 0; + private int _buffer_size = 0; + + private String[] _args; + + final private native void create(AssetManager _asset_manager, + EGL10 egl, EGLDisplay egl_display, + AudioTrack audio_track, + int sample_rate, int buffer_size); + final private native void destroy(); + final private native void setSurface(int width, int height); + final private native int main(String[] args); + + // pause the engine and all native threads + final public native void setPause(boolean pause); + final public native void enableZoning(boolean enable); + // Feed an event to ScummVM. Safe to call from other threads. + final public native void pushEvent(int type, int arg1, int arg2, int arg3, + int arg4, int arg5); -// At least in Android 2.1, eglCreateWindowSurface() requires an -// EGLNativeWindowSurface object, which is hidden deep in the bowels -// of libui. Until EGL is properly exposed, it's probably safer to -// use the Java versions of most EGL functions :( - -public class ScummVM implements SurfaceHolder.Callback { - protected final static String LOG_TAG = "ScummVM"; + // Callbacks from C++ peer instance + abstract protected void getDPI(float[] values); + abstract protected void displayMessageOnOSD(String msg); + abstract protected void setWindowCaption(String caption); + abstract protected String[] getPluginDirectories(); + abstract protected void showVirtualKeyboard(boolean enable); + abstract protected String[] getSysArchives(); - private final int AUDIO_FRAME_SIZE = 2 * 2; // bytes. 16bit audio * stereo - public static class AudioSetupException extends Exception {} + public ScummVM(AssetManager asset_manager, SurfaceHolder holder) { + _asset_manager = asset_manager; + _sem_surface = new Object(); - private long nativeScummVM; // native code hangs itself here - boolean scummVMRunning = false; + holder.addCallback(this); + } - private native void create(AssetManager am); + // SurfaceHolder callback + final public void surfaceCreated(SurfaceHolder holder) { + Log.d(LOG_TAG, "surfaceCreated"); - public ScummVM(Context context) { - create(context.getAssets()); // Init C++ code, set nativeScummVM + // no need to do anything, surfaceChanged() will be called in any case } - private native void nativeDestroy(); + // SurfaceHolder callback + final public void surfaceChanged(SurfaceHolder holder, int format, + int width, int height) { + Log.d(LOG_TAG, String.format("surfaceChanged: %dx%d (%d)", + width, height, format)); - public synchronized void destroy() { - if (nativeScummVM != 0) { - nativeDestroy(); - nativeScummVM = 0; + synchronized(_sem_surface) { + _surface_holder = holder; + _sem_surface.notifyAll(); } - } - protected void finalize() { - destroy(); - } - // Surface creation: - // GUI thread: create surface, release lock - // ScummVM thread: acquire lock (block), read surface - // - // Surface deletion: - // GUI thread: post event, acquire lock (block), return - // ScummVM thread: read event, free surface, release lock - // - // In other words, ScummVM thread does this: - // acquire lock - // setup surface - // when SCREEN_CHANGED arrives: - // destroy surface - // release lock - // back to acquire lock - static final int configSpec[] = { - EGL10.EGL_RED_SIZE, 5, - EGL10.EGL_GREEN_SIZE, 5, - EGL10.EGL_BLUE_SIZE, 5, - EGL10.EGL_DEPTH_SIZE, 0, - EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT, - EGL10.EGL_NONE, - }; - EGL10 egl; - EGLDisplay eglDisplay = EGL10.EGL_NO_DISPLAY; - EGLConfig eglConfig; - EGLContext eglContext = EGL10.EGL_NO_CONTEXT; - EGLSurface eglSurface = EGL10.EGL_NO_SURFACE; - Semaphore surfaceLock = new Semaphore(0, true); - SurfaceHolder nativeSurface; - - public void surfaceCreated(SurfaceHolder holder) { - nativeSurface = holder; - surfaceLock.release(); + // store values for the native code + setSurface(width, height); } - public void surfaceChanged(SurfaceHolder holder, int format, - int width, int height) { - // Disabled while I debug GL problems - pushEvent(new Event(Event.EVENT_SCREEN_CHANGED)); - } + // SurfaceHolder callback + final public void surfaceDestroyed(SurfaceHolder holder) { + Log.d(LOG_TAG, "surfaceDestroyed"); - public void surfaceDestroyed(SurfaceHolder holder) { - try { - surfaceLock.acquire(); - } catch (InterruptedException e) { - Log.e(LOG_TAG, "Interrupted while waiting for surface lock", e); + synchronized(_sem_surface) { + _surface_holder = null; + _sem_surface.notifyAll(); } + + // clear values for the native code + setSurface(0, 0); } - // For debugging - private static final Map<String, Integer> attribs; - static { - attribs = new LinkedHashMap<String, Integer>(); - attribs.put("CONFIG_ID", EGL10.EGL_CONFIG_ID); - attribs.put("BUFFER_SIZE", EGL10.EGL_BUFFER_SIZE); - attribs.put("RED_SIZE", EGL10.EGL_RED_SIZE); - attribs.put("GREEN_SIZE", EGL10.EGL_GREEN_SIZE); - attribs.put("BLUE_SIZE", EGL10.EGL_BLUE_SIZE); - attribs.put("ALPHA_SIZE", EGL10.EGL_ALPHA_SIZE); - //attribs.put("BIND_TO_RGB", EGL10.EGL_BIND_TO_TEXTURE_RGB); - //attribs.put("BIND_TO_RGBA", EGL10.EGL_BIND_TO_TEXTURE_RGBA); - attribs.put("CONFIG_CAVEAT", EGL10.EGL_CONFIG_CAVEAT); - attribs.put("DEPTH_SIZE", EGL10.EGL_DEPTH_SIZE); - attribs.put("LEVEL", EGL10.EGL_LEVEL); - attribs.put("MAX_PBUFFER_WIDTH", EGL10.EGL_MAX_PBUFFER_WIDTH); - attribs.put("MAX_PBUFFER_HEIGHT", EGL10.EGL_MAX_PBUFFER_HEIGHT); - attribs.put("MAX_PBUFFER_PIXELS", EGL10.EGL_MAX_PBUFFER_PIXELS); - //attribs.put("MAX_SWAP_INTERVAL", EGL10.EGL_MAX_SWAP_INTERVAL); - //attribs.put("MIN_SWAP_INTERVAL", EGL10.EGL_MIN_SWAP_INTERVAL); - attribs.put("NATIVE_RENDERABLE", EGL10.EGL_NATIVE_RENDERABLE); - attribs.put("NATIVE_VISUAL_ID", EGL10.EGL_NATIVE_VISUAL_ID); - attribs.put("NATIVE_VISUAL_TYPE", EGL10.EGL_NATIVE_VISUAL_TYPE); - attribs.put("SAMPLE_BUFFERS", EGL10.EGL_SAMPLE_BUFFERS); - attribs.put("SAMPLES", EGL10.EGL_SAMPLES); - attribs.put("STENCIL_SIZE", EGL10.EGL_STENCIL_SIZE); - attribs.put("SURFACE_TYPE", EGL10.EGL_SURFACE_TYPE); - attribs.put("TRANSPARENT_TYPE", EGL10.EGL_TRANSPARENT_TYPE); - attribs.put("TRANSPARENT_RED_VALUE", EGL10.EGL_TRANSPARENT_RED_VALUE); - attribs.put("TRANSPARENT_GREEN_VALUE", EGL10.EGL_TRANSPARENT_GREEN_VALUE); - attribs.put("TRANSPARENT_BLUE_VALUE", EGL10.EGL_TRANSPARENT_BLUE_VALUE); + final public void setArgs(String[] args) { + _args = args; } - private void dumpEglConfig(EGLConfig config) { - int[] value = new int[1]; - for (Map.Entry<String, Integer> entry : attribs.entrySet()) { - egl.eglGetConfigAttrib(eglDisplay, config, - entry.getValue(), value); - if (value[0] == EGL10.EGL_NONE) - Log.d(LOG_TAG, entry.getKey() + ": NONE"); - else - Log.d(LOG_TAG, String.format("%s: %d", entry.getKey(), value[0])); + + final public void run() { + try { + initAudio(); + initEGL(); + + // wait for the surfaceChanged callback + synchronized(_sem_surface) { + while (_surface_holder == null) + _sem_surface.wait(); + } + } catch (Exception e) { + deinitEGL(); + deinitAudio(); + + throw new RuntimeException("Error preparing the ScummVM thread", e); } + + create(_asset_manager, _egl, _egl_display, + _audio_track, _sample_rate, _buffer_size); + + int res = main(_args); + + destroy(); + + deinitEGL(); + deinitAudio(); + + // On exit, tear everything down for a fresh restart next time. + System.exit(res); } - // Called by ScummVM thread (from initBackend) - private void createScummVMGLContext() { - egl = (EGL10)EGLContext.getEGL(); - eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); + final private void initEGL() throws Exception { + _egl = (EGL10)EGLContext.getEGL(); + _egl_display = _egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); + int[] version = new int[2]; - egl.eglInitialize(eglDisplay, version); + _egl.eglInitialize(_egl_display, version); + int[] num_config = new int[1]; - egl.eglChooseConfig(eglDisplay, configSpec, null, 0, num_config); + _egl.eglGetConfigs(_egl_display, null, 0, num_config); final int numConfigs = num_config[0]; + if (numConfigs <= 0) - throw new IllegalArgumentException("No configs match configSpec"); + throw new IllegalArgumentException("No EGL configs"); EGLConfig[] configs = new EGLConfig[numConfigs]; - egl.eglChooseConfig(eglDisplay, configSpec, configs, numConfigs, - num_config); - - if (false) { - Log.d(LOG_TAG, String.format("Found %d EGL configurations.", numConfigs)); - for (EGLConfig config : configs) - dumpEglConfig(config); - } + _egl.eglGetConfigs(_egl_display, configs, numConfigs, num_config); // Android's eglChooseConfig is busted in several versions and - // devices so we have to filter/rank the configs again ourselves. - eglConfig = chooseEglConfig(configs); - if (false) { - Log.d(LOG_TAG, String.format("Chose EGL config from %d possibilities.", numConfigs)); - dumpEglConfig(eglConfig); - } + // devices so we have to filter/rank the configs ourselves. + _egl_config = chooseEglConfig(configs); - eglContext = egl.eglCreateContext(eglDisplay, eglConfig, - EGL10.EGL_NO_CONTEXT, null); - if (eglContext == EGL10.EGL_NO_CONTEXT) - throw new RuntimeException("Failed to create context"); - } + _egl_context = _egl.eglCreateContext(_egl_display, _egl_config, + EGL10.EGL_NO_CONTEXT, null); - private EGLConfig chooseEglConfig(EGLConfig[] configs) { - int best = 0; - int bestScore = -1; - int[] value = new int[1]; + if (_egl_context == EGL10.EGL_NO_CONTEXT) + throw new Exception(String.format("Failed to create context: 0x%x", + _egl.eglGetError())); + } - for (int i = 0; i < configs.length; i++) { - EGLConfig config = configs[i]; - int score = 10000; - egl.eglGetConfigAttrib(eglDisplay, config, - EGL10.EGL_SURFACE_TYPE, value); - if ((value[0] & EGL10.EGL_WINDOW_BIT) == 0) - continue; // must have - - egl.eglGetConfigAttrib(eglDisplay, config, - EGL10.EGL_CONFIG_CAVEAT, value); - if (value[0] != EGL10.EGL_NONE) - score -= 1000; + // Callback from C++ peer instance + final protected EGLSurface initSurface() throws Exception { + _egl_surface = _egl.eglCreateWindowSurface(_egl_display, _egl_config, + _surface_holder, null); - // Must be at least 555, but then smaller is better - final int[] colorBits = {EGL10.EGL_RED_SIZE, - EGL10.EGL_GREEN_SIZE, - EGL10.EGL_BLUE_SIZE, - EGL10.EGL_ALPHA_SIZE}; - for (int component : colorBits) { - egl.eglGetConfigAttrib(eglDisplay, config, - component, value); - if (value[0] >= 5) - score += 10; // boost if >5 bits accuracy - score -= value[0]; // penalize for wasted bits - } + if (_egl_surface == EGL10.EGL_NO_SURFACE) + throw new Exception(String.format( + "eglCreateWindowSurface failed: 0x%x", _egl.eglGetError())); - egl.eglGetConfigAttrib(eglDisplay, config, - EGL10.EGL_DEPTH_SIZE, value); - score -= value[0]; // penalize for wasted bits + _egl.eglMakeCurrent(_egl_display, _egl_surface, _egl_surface, + _egl_context); - if (score > bestScore) { - best = i; - bestScore = score; - } - } + GL10 gl = (GL10)_egl_context.getGL(); - if (bestScore < 0) { - Log.e(LOG_TAG, "Unable to find an acceptable EGL config, expect badness."); - return configs[0]; - } + Log.i(LOG_TAG, String.format("Using EGL %s (%s); GL %s/%s (%s)", + _egl.eglQueryString(_egl_display, EGL10.EGL_VERSION), + _egl.eglQueryString(_egl_display, EGL10.EGL_VENDOR), + gl.glGetString(GL10.GL_VERSION), + gl.glGetString(GL10.GL_RENDERER), + gl.glGetString(GL10.GL_VENDOR))); - return configs[best]; + return _egl_surface; } - // Called by ScummVM thread - static private boolean _log_version = true; - protected void setupScummVMSurface() { - try { - surfaceLock.acquire(); - } catch (InterruptedException e) { - Log.e(LOG_TAG, "Interrupted while waiting for surface lock", e); - return; - } - eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig, - nativeSurface, null); - if (eglSurface == EGL10.EGL_NO_SURFACE) - Log.e(LOG_TAG, "CreateWindowSurface failed!"); - egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext); - - GL10 gl = (GL10)eglContext.getGL(); - - if (_log_version) { - Log.i(LOG_TAG, String.format("Using EGL %s (%s); GL %s/%s (%s)", - egl.eglQueryString(eglDisplay, EGL10.EGL_VERSION), - egl.eglQueryString(eglDisplay, EGL10.EGL_VENDOR), - gl.glGetString(GL10.GL_VERSION), - gl.glGetString(GL10.GL_RENDERER), - gl.glGetString(GL10.GL_VENDOR))); - _log_version = false; // only log this once + // Callback from C++ peer instance + final protected void deinitSurface() { + if (_egl_display != EGL10.EGL_NO_DISPLAY) { + _egl.eglMakeCurrent(_egl_display, EGL10.EGL_NO_SURFACE, + EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); + + if (_egl_surface != EGL10.EGL_NO_SURFACE) + _egl.eglDestroySurface(_egl_display, _egl_surface); } - int[] value = new int[1]; - egl.eglQuerySurface(eglDisplay, eglSurface, EGL10.EGL_WIDTH, value); - int width = value[0]; - egl.eglQuerySurface(eglDisplay, eglSurface, EGL10.EGL_HEIGHT, value); - int height = value[0]; - Log.i(LOG_TAG, String.format("New surface is %dx%d", width, height)); - setSurfaceSize(width, height); + _egl_surface = EGL10.EGL_NO_SURFACE; } - // Called by ScummVM thread - protected void destroyScummVMSurface() { - if (eglSurface != null) { - egl.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE, - EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); - egl.eglDestroySurface(eglDisplay, eglSurface); - eglSurface = EGL10.EGL_NO_SURFACE; + final private void deinitEGL() { + if (_egl_display != EGL10.EGL_NO_DISPLAY) { + _egl.eglMakeCurrent(_egl_display, EGL10.EGL_NO_SURFACE, + EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); + + if (_egl_surface != EGL10.EGL_NO_SURFACE) + _egl.eglDestroySurface(_egl_display, _egl_surface); + + if (_egl_context != EGL10.EGL_NO_CONTEXT) + _egl.eglDestroyContext(_egl_display, _egl_context); + + _egl.eglTerminate(_egl_display); } - surfaceLock.release(); + _egl_surface = EGL10.EGL_NO_SURFACE; + _egl_context = EGL10.EGL_NO_CONTEXT; + _egl_config = null; + _egl_display = EGL10.EGL_NO_DISPLAY; + _egl = null; } - public void setSurface(SurfaceHolder holder) { - holder.addCallback(this); - } + final private void initAudio() throws Exception { + _sample_rate = AudioTrack.getNativeOutputSampleRate( + AudioManager.STREAM_MUSIC); + _buffer_size = AudioTrack.getMinBufferSize(_sample_rate, + AudioFormat.CHANNEL_CONFIGURATION_STEREO, + AudioFormat.ENCODING_PCM_16BIT); + + // ~50ms + int buffer_size_want = (_sample_rate * 2 * 2 / 20) & ~1023; - final public boolean swapBuffers() { - if (!egl.eglSwapBuffers(eglDisplay, eglSurface)) { - int error = egl.eglGetError(); - Log.w(LOG_TAG, String.format("eglSwapBuffers exited with error 0x%x", error)); - if (error == EGL11.EGL_CONTEXT_LOST) - return false; + if (_buffer_size < buffer_size_want) { + Log.w(LOG_TAG, String.format( + "adjusting audio buffer size (was: %d)", _buffer_size)); + + _buffer_size = buffer_size_want; } - return true; - } - // Set scummvm config options - final public native static void loadConfigFile(String path); - final public native static void setConfMan(String key, int value); - final public native static void setConfMan(String key, String value); - final public native void enableZoning(boolean enable); - final public native void setSurfaceSize(int width, int height); + Log.i(LOG_TAG, String.format("Using %d bytes buffer for %dHz audio", + _buffer_size, _sample_rate)); - // Feed an event to ScummVM. Safe to call from other threads. - final public native void pushEvent(Event e); + _audio_track = new AudioTrack(AudioManager.STREAM_MUSIC, + _sample_rate, + AudioFormat.CHANNEL_CONFIGURATION_STEREO, + AudioFormat.ENCODING_PCM_16BIT, + _buffer_size, + AudioTrack.MODE_STREAM); - final private native void audioMixCallback(byte[] buf); + if (_audio_track.getState() != AudioTrack.STATE_INITIALIZED) + throw new Exception( + String.format("Error initialising AudioTrack: %d", + _audio_track.getState())); + } - // Runs the actual ScummVM program and returns when it does. - // This should not be called from multiple threads simultaneously... - final public native int scummVMMain(String[] argv); + final private void deinitAudio() { + if (_audio_track != null) + _audio_track.stop(); - // Callbacks from C++ peer instance - //protected GraphicsMode[] getSupportedGraphicsModes() {} - protected void displayMessageOnOSD(String msg) {} - protected void setWindowCaption(String caption) {} - protected void showVirtualKeyboard(boolean enable) {} - protected String[] getSysArchives() { return new String[0]; } - protected String[] getPluginDirectories() { return new String[0]; } - protected void initBackend() throws AudioSetupException { - createScummVMGLContext(); - initAudio(); + _audio_track = null; + _buffer_size = 0; + _sample_rate = 0; } - private static class AudioThread extends Thread { - final private int buf_size; - private boolean is_paused = false; - final private ScummVM scummvm; - final private AudioTrack audio_track; - - AudioThread(ScummVM scummvm, AudioTrack audio_track, int buf_size) { - super("AudioThread"); - this.scummvm = scummvm; - this.audio_track = audio_track; - this.buf_size = buf_size; - setPriority(Thread.MAX_PRIORITY); - setDaemon(true); - } + private static final int[] s_eglAttribs = { + EGL10.EGL_CONFIG_ID, + EGL10.EGL_BUFFER_SIZE, + EGL10.EGL_RED_SIZE, + EGL10.EGL_GREEN_SIZE, + EGL10.EGL_BLUE_SIZE, + EGL10.EGL_ALPHA_SIZE, + EGL10.EGL_CONFIG_CAVEAT, + EGL10.EGL_DEPTH_SIZE, + EGL10.EGL_LEVEL, + EGL10.EGL_MAX_PBUFFER_WIDTH, + EGL10.EGL_MAX_PBUFFER_HEIGHT, + EGL10.EGL_MAX_PBUFFER_PIXELS, + EGL10.EGL_NATIVE_RENDERABLE, + EGL10.EGL_NATIVE_VISUAL_ID, + EGL10.EGL_NATIVE_VISUAL_TYPE, + EGL10.EGL_SAMPLE_BUFFERS, + EGL10.EGL_SAMPLES, + EGL10.EGL_STENCIL_SIZE, + EGL10.EGL_SURFACE_TYPE, + EGL10.EGL_TRANSPARENT_TYPE, + EGL10.EGL_TRANSPARENT_RED_VALUE, + EGL10.EGL_TRANSPARENT_GREEN_VALUE, + EGL10.EGL_TRANSPARENT_BLUE_VALUE + }; - public void pauseAudio() { - synchronized (this) { - is_paused = true; + final private class EglAttribs extends LinkedHashMap<Integer, Integer> { + public EglAttribs(EGLConfig config) { + super(s_eglAttribs.length); + + int[] value = new int[1]; + + for (int i : s_eglAttribs) { + _egl.eglGetConfigAttrib(_egl_display, config, i, value); + + put(i, value[0]); } - audio_track.pause(); } - public void resumeAudio() { - synchronized (this) { - is_paused = false; - notifyAll(); - } - audio_track.play(); + private int weightBits(int attr, int size) { + final int value = get(attr); + + int score = 0; + + if (value == size || (size > 0 && value > size)) + score += 10; + + // penalize for wasted bits + score -= value - size; + + return score; } - public void run() { - byte[] buf = new byte[buf_size]; - audio_track.play(); - int offset = 0; - try { - while (true) { - synchronized (this) { - while (is_paused) - wait(); - } - - if (offset == buf.length) { - // Grab new audio data - scummvm.audioMixCallback(buf); - offset = 0; - } - int len = buf.length - offset; - int ret = audio_track.write(buf, offset, len); - if (ret < 0) { - Log.w(LOG_TAG, String.format( - "AudioTrack.write(%dB) returned error %d", - buf.length, ret)); - break; - } else if (ret != len) { - Log.w(LOG_TAG, String.format( - "Short audio write. Wrote %dB, not %dB", - ret, buf.length)); - // Buffer is full, so yield cpu for a while - Thread.sleep(100); - } - offset += ret; - } - } catch (InterruptedException e) { - Log.e(LOG_TAG, "Audio thread interrupted", e); - } + public int weight() { + int score = 10000; + + if (get(EGL10.EGL_CONFIG_CAVEAT) != EGL10.EGL_NONE) + score -= 1000; + + // less MSAA is better + score -= get(EGL10.EGL_SAMPLES) * 100; + + // Must be at least 565, but then smaller is better + score += weightBits(EGL10.EGL_RED_SIZE, 5); + score += weightBits(EGL10.EGL_GREEN_SIZE, 6); + score += weightBits(EGL10.EGL_BLUE_SIZE, 5); + score += weightBits(EGL10.EGL_ALPHA_SIZE, 0); + score += weightBits(EGL10.EGL_DEPTH_SIZE, 0); + score += weightBits(EGL10.EGL_STENCIL_SIZE, 0); + + return score; } - } - private AudioThread audio_thread; - final public int audioSampleRate() { - return AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC); - } + public String toString() { + String s; - private void initAudio() throws AudioSetupException { - int sample_rate = audioSampleRate(); - int buf_size = - AudioTrack.getMinBufferSize(sample_rate, - AudioFormat.CHANNEL_CONFIGURATION_STEREO, - AudioFormat.ENCODING_PCM_16BIT); - if (buf_size < 0) { - int guess = AUDIO_FRAME_SIZE * sample_rate / 100; // 10ms of audio - Log.w(LOG_TAG, String.format( - "Unable to get min audio buffer size (error %d). Guessing %dB.", - buf_size, guess)); - buf_size = guess; + if (get(EGL10.EGL_ALPHA_SIZE) > 0) + s = String.format("[%d] RGBA%d%d%d%d", + get(EGL10.EGL_CONFIG_ID), + get(EGL10.EGL_RED_SIZE), + get(EGL10.EGL_GREEN_SIZE), + get(EGL10.EGL_BLUE_SIZE), + get(EGL10.EGL_ALPHA_SIZE)); + else + s = String.format("[%d] RGB%d%d%d", + get(EGL10.EGL_CONFIG_ID), + get(EGL10.EGL_RED_SIZE), + get(EGL10.EGL_GREEN_SIZE), + get(EGL10.EGL_BLUE_SIZE)); + + if (get(EGL10.EGL_DEPTH_SIZE) > 0) + s += String.format(" D%d", get(EGL10.EGL_DEPTH_SIZE)); + + if (get(EGL10.EGL_STENCIL_SIZE) > 0) + s += String.format(" S%d", get(EGL10.EGL_STENCIL_SIZE)); + + if (get(EGL10.EGL_SAMPLES) > 0) + s += String.format(" MSAAx%d", get(EGL10.EGL_SAMPLES)); + + if ((get(EGL10.EGL_SURFACE_TYPE) & EGL10.EGL_WINDOW_BIT) > 0) + s += " W"; + if ((get(EGL10.EGL_SURFACE_TYPE) & EGL10.EGL_PBUFFER_BIT) > 0) + s += " P"; + if ((get(EGL10.EGL_SURFACE_TYPE) & EGL10.EGL_PIXMAP_BIT) > 0) + s += " X"; + + switch (get(EGL10.EGL_CONFIG_CAVEAT)) { + case EGL10.EGL_NONE: + break; + + case EGL10.EGL_SLOW_CONFIG: + s += " SLOW"; + break; + + case EGL10.EGL_NON_CONFORMANT_CONFIG: + s += " NON_CONFORMANT"; + + default: + s += String.format(" unknown CAVEAT 0x%x", + get(EGL10.EGL_CONFIG_CAVEAT)); + } + + return s; } - Log.d(LOG_TAG, String.format("Using %dB buffer for %dHZ audio", - buf_size, sample_rate)); - AudioTrack audio_track = - new AudioTrack(AudioManager.STREAM_MUSIC, - sample_rate, - AudioFormat.CHANNEL_CONFIGURATION_STEREO, - AudioFormat.ENCODING_PCM_16BIT, - buf_size, - AudioTrack.MODE_STREAM); - if (audio_track.getState() != AudioTrack.STATE_INITIALIZED) { - Log.e(LOG_TAG, "Error initialising Android audio system."); - throw new AudioSetupException(); + }; + + final private EGLConfig chooseEglConfig(EGLConfig[] configs) { + EGLConfig res = configs[0]; + int bestScore = -1; + + Log.d(LOG_TAG, "EGL configs:"); + + for (EGLConfig config : configs) { + EglAttribs attr = new EglAttribs(config); + + // must have + if ((attr.get(EGL10.EGL_SURFACE_TYPE) & EGL10.EGL_WINDOW_BIT) == 0) + continue; + + int score = attr.weight(); + + Log.d(LOG_TAG, String.format("%s (%d)", attr.toString(), score)); + + if (score > bestScore) { + res = config; + bestScore = score; + } } - audio_thread = new AudioThread(this, audio_track, buf_size); - audio_thread.start(); - } + if (bestScore < 0) + Log.e(LOG_TAG, + "Unable to find an acceptable EGL config, expect badness."); - public void pause() { - audio_thread.pauseAudio(); - // TODO: need to pause engine too - } + Log.d(LOG_TAG, String.format("Chosen EGL config: %s", + new EglAttribs(res).toString())); - public void resume() { - // TODO: need to resume engine too - audio_thread.resumeAudio(); + return res; } static { @@ -448,15 +429,16 @@ public class ScummVM implements SurfaceHolder.Callback { final boolean sleep_for_debugger = false; if (sleep_for_debugger) { try { - Thread.sleep(20*1000); + Thread.sleep(20 * 1000); } catch (InterruptedException e) { } } - //System.loadLibrary("scummvm"); File cache_dir = ScummVMApplication.getLastCacheDir(); String libname = System.mapLibraryName("scummvm"); File libpath = new File(cache_dir, libname); + System.load(libpath.getPath()); } } + diff --git a/backends/platform/android/org/inodes/gus/scummvm/ScummVMActivity.java b/backends/platform/android/org/inodes/gus/scummvm/ScummVMActivity.java index fae35b6695..1978b690d0 100644 --- a/backends/platform/android/org/inodes/gus/scummvm/ScummVMActivity.java +++ b/backends/platform/android/org/inodes/gus/scummvm/ScummVMActivity.java @@ -3,41 +3,27 @@ package org.inodes.gus.scummvm; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; -import android.content.res.Configuration; import android.media.AudioManager; import android.os.Bundle; import android.os.Environment; -import android.os.Handler; -import android.os.Message; import android.util.DisplayMetrics; import android.util.Log; -import android.view.KeyEvent; -import android.view.MotionEvent; import android.view.SurfaceView; -import android.view.View; -import android.view.ViewConfiguration; +import android.view.SurfaceHolder; +import android.view.MotionEvent; import android.view.inputmethod.InputMethodManager; import android.widget.Toast; -import java.io.IOException; - public class ScummVMActivity extends Activity { - private boolean _do_right_click; - private boolean _last_click_was_right; - - // game pixels to move per trackball/dpad event. - // FIXME: replace this with proper mouse acceleration - private final static int TRACKBALL_SCALE = 2; private class MyScummVM extends ScummVM { - private boolean scummvmRunning = false; - private boolean usingSmallScreen() { // Multiple screen sizes came in with Android 1.6. Have // to use reflection in order to continue supporting 1.5 // devices :( DisplayMetrics metrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics); + try { // This 'density' term is very confusing. int DENSITY_LOW = metrics.getClass().getField("DENSITY_LOW").getInt(null); @@ -48,27 +34,22 @@ public class ScummVMActivity extends Activity { } } - public MyScummVM() { - super(ScummVMActivity.this); + public MyScummVM(SurfaceHolder holder) { + super(ScummVMActivity.this.getAssets(), holder); // Enable ScummVM zoning on 'small' screens. - enableZoning(usingSmallScreen()); + // FIXME make this optional for the user + // disabled for now since it crops too much + //enableZoning(usingSmallScreen()); } @Override - protected void initBackend() throws ScummVM.AudioSetupException { - synchronized (this) { - scummvmRunning = true; - notifyAll(); - } - super.initBackend(); - } + protected void getDPI(float[] values) { + DisplayMetrics metrics = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getMetrics(metrics); - public void waitUntilRunning() throws InterruptedException { - synchronized (this) { - while (!scummvmRunning) - wait(); - } + values[0] = metrics.xdpi; + values[1] = metrics.ydpi; } @Override @@ -95,24 +76,28 @@ public class ScummVMActivity extends Activity { @Override protected void showVirtualKeyboard(final boolean enable) { - if (getResources().getConfiguration().keyboard == - Configuration.KEYBOARD_NOKEYS) { - runOnUiThread(new Runnable() { - public void run() { - showKeyboard(enable); - } - }); - } + runOnUiThread(new Runnable() { + public void run() { + showKeyboard(enable); + } + }); + } + + @Override + protected String[] getSysArchives() { + return new String[0]; } + } - private MyScummVM scummvm; - private Thread scummvm_thread; + + private MyScummVM _scummvm; + private ScummVMEvents _events; + private Thread _scummvm_thread; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - _do_right_click = false; setVolumeControlStream(AudioManager.STREAM_MUSIC); setContentView(R.layout.main); @@ -126,336 +111,110 @@ public class ScummVMActivity extends Activity { .setIcon(android.R.drawable.ic_dialog_alert) .setMessage(R.string.no_sdcard) .setNegativeButton(R.string.quit, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, - int which) { - finish(); - } - }) + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int which) { + finish(); + } + }) .show(); + return; } SurfaceView main_surface = (SurfaceView)findViewById(R.id.main_surface); - - main_surface.setOnTouchListener(new View.OnTouchListener() { - public boolean onTouch(View v, MotionEvent event) { - return onTouchEvent(event); - } - }); - main_surface.setOnKeyListener(new View.OnKeyListener() { - public boolean onKey(View v, int code, KeyEvent ev) { - return onKeyDown(code, ev); - } - }); + main_surface.requestFocus(); - // Start ScummVM - scummvm = new MyScummVM(); - scummvm_thread = new Thread(new Runnable() { - public void run() { - try { - runScummVM(); - } catch (Exception e) { - Log.e(ScummVM.LOG_TAG, "Fatal error in ScummVM thread", e); - new AlertDialog.Builder(ScummVMActivity.this) - .setTitle("Error") - .setMessage(e.toString()) - .setIcon(android.R.drawable.ic_dialog_alert) - .show(); - finish(); - } - } - }, "ScummVM"); - scummvm_thread.start(); - - // Block UI thread until ScummVM has started. In particular, - // this means that surface and event callbacks should be safe - // after this point. - try { - scummvm.waitUntilRunning(); - } catch (InterruptedException e) { - Log.e(ScummVM.LOG_TAG, "Interrupted while waiting for ScummVM.initBackend", e); - finish(); - } + getFilesDir().mkdirs(); - scummvm.setSurface(main_surface.getHolder()); - } + // Start ScummVM + _scummvm = new MyScummVM(main_surface.getHolder()); - // Runs in another thread - private void runScummVM() throws IOException { - getFilesDir().mkdirs(); - String[] args = { - "ScummVM-lib", + _scummvm.setArgs(new String[] { + "ScummVM", "--config=" + getFileStreamPath("scummvmrc").getPath(), "--path=" + Environment.getExternalStorageDirectory().getPath(), "--gui-theme=scummmodern", "--savepath=" + getDir("saves", 0).getPath() - }; + }); - int ret = scummvm.scummVMMain(args); + _events = new ScummVMEvents(this, _scummvm); - // On exit, tear everything down for a fresh - // restart next time. - System.exit(ret); - } + main_surface.setOnKeyListener(_events); + main_surface.setOnTouchListener(_events); - private boolean was_paused = false; + _scummvm_thread = new Thread(_scummvm, "ScummVM"); + _scummvm_thread.start(); + } @Override - public void onPause() { - if (scummvm != null) { - was_paused = true; - scummvm.pause(); - } - super.onPause(); + public void onStart() { + Log.d(ScummVM.LOG_TAG, "onStart"); + + super.onStart(); } @Override public void onResume() { + Log.d(ScummVM.LOG_TAG, "onResume"); + super.onResume(); - if (scummvm != null && was_paused) - scummvm.resume(); - was_paused = false; + + if (_scummvm != null) + _scummvm.setPause(false); } @Override - public void onStop() { - if (scummvm != null) { - scummvm.pushEvent(new Event(Event.EVENT_QUIT)); - try { - scummvm_thread.join(1000); // 1s timeout - } catch (InterruptedException e) { - Log.i(ScummVM.LOG_TAG, "Error while joining ScummVM thread", e); - } - } - super.onStop(); - } + public void onPause() { + Log.d(ScummVM.LOG_TAG, "onPause"); - static final int MSG_MENU_LONG_PRESS = 1; - private final Handler keycodeMenuTimeoutHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - if (msg.what == MSG_MENU_LONG_PRESS) { - InputMethodManager imm = (InputMethodManager) - getSystemService(INPUT_METHOD_SERVICE); - if (imm != null) - imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); - } - } - }; + super.onPause(); - @Override - public boolean onKeyUp(int keyCode, KeyEvent kevent) { - return onKeyDown(keyCode, kevent); + if (_scummvm != null) + _scummvm.setPause(true); } @Override - public boolean onKeyMultiple(int keyCode, int repeatCount, - KeyEvent kevent) { - return onKeyDown(keyCode, kevent); + public void onStop() { + Log.d(ScummVM.LOG_TAG, "onStop"); + + super.onStop(); } @Override - public boolean onKeyDown(int keyCode, KeyEvent kevent) { - // Filter out "special" keys - switch (keyCode) { - case KeyEvent.KEYCODE_MENU: - // Have to reimplement hold-down-menu-brings-up-softkeybd - // ourselves, since we are otherwise hijacking the menu - // key :( - // See com.android.internal.policy.impl.PhoneWindow.onKeyDownPanel() - // for the usual Android implementation of this feature. - if (kevent.getRepeatCount() > 0) - // Ignore keyrepeat for menu - return false; - boolean timeout_fired = false; - if (getResources().getConfiguration().keyboard == - Configuration.KEYBOARD_NOKEYS) { - timeout_fired = !keycodeMenuTimeoutHandler.hasMessages(MSG_MENU_LONG_PRESS); - keycodeMenuTimeoutHandler.removeMessages(MSG_MENU_LONG_PRESS); - if (kevent.getAction() == KeyEvent.ACTION_DOWN) { - keycodeMenuTimeoutHandler.sendMessageDelayed( - keycodeMenuTimeoutHandler.obtainMessage(MSG_MENU_LONG_PRESS), - ViewConfiguration.getLongPressTimeout()); - return true; - } - } - if (kevent.getAction() == KeyEvent.ACTION_UP) { - if (!timeout_fired) - scummvm.pushEvent(new Event(Event.EVENT_MAINMENU)); - return true; - } - return false; - case KeyEvent.KEYCODE_CAMERA: - case KeyEvent.KEYCODE_SEARCH: - _do_right_click = (kevent.getAction() == KeyEvent.ACTION_DOWN); - return true; - case KeyEvent.KEYCODE_DPAD_CENTER: - case KeyEvent.KEYCODE_DPAD_UP: - case KeyEvent.KEYCODE_DPAD_DOWN: - case KeyEvent.KEYCODE_DPAD_LEFT: - case KeyEvent.KEYCODE_DPAD_RIGHT: { - // HTC Hero doesn't seem to generate - // MotionEvent.ACTION_DOWN events on trackball press :( - // We'll have to just fake one here. - // Some other handsets lack a trackball, so the DPAD is - // the only way of moving the cursor. - int motion_action; - // FIXME: this logic is a mess. - if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { - switch (kevent.getAction()) { - case KeyEvent.ACTION_DOWN: - motion_action = MotionEvent.ACTION_DOWN; - break; - case KeyEvent.ACTION_UP: - motion_action = MotionEvent.ACTION_UP; - break; - default: // ACTION_MULTIPLE - return false; - } - } else - motion_action = MotionEvent.ACTION_MOVE; - - Event e = new Event(getEventType(motion_action)); - e.mouse_x = 0; - e.mouse_y = 0; - e.mouse_relative = true; - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_UP: - e.mouse_y = -TRACKBALL_SCALE; - break; - case KeyEvent.KEYCODE_DPAD_DOWN: - e.mouse_y = TRACKBALL_SCALE; - break; - case KeyEvent.KEYCODE_DPAD_LEFT: - e.mouse_x = -TRACKBALL_SCALE; - break; - case KeyEvent.KEYCODE_DPAD_RIGHT: - e.mouse_x = TRACKBALL_SCALE; - break; - } - scummvm.pushEvent(e); - return true; - } - case KeyEvent.KEYCODE_BACK: - // skip isSystem() check and fall through to main code - break; - default: - if (kevent.isSystem()) - return false; - } + public void onDestroy() { + Log.d(ScummVM.LOG_TAG, "onDestroy"); - // FIXME: what do I need to do for composed characters? - - Event e = new Event(); - - switch (kevent.getAction()) { - case KeyEvent.ACTION_DOWN: - e.type = Event.EVENT_KEYDOWN; - e.synthetic = false; - break; - case KeyEvent.ACTION_UP: - e.type = Event.EVENT_KEYUP; - e.synthetic = false; - break; - case KeyEvent.ACTION_MULTIPLE: - // e.type is handled below - e.synthetic = true; - break; - default: - return false; - } + super.onDestroy(); - e.kbd_keycode = Event.androidKeyMap.containsKey(keyCode) ? - Event.androidKeyMap.get(keyCode) : Event.KEYCODE_INVALID; - e.kbd_ascii = kevent.getUnicodeChar(); - if (e.kbd_ascii == 0) - e.kbd_ascii = e.kbd_keycode; // scummvm keycodes are mostly ascii - - - e.kbd_flags = 0; - if (kevent.isAltPressed()) - e.kbd_flags |= Event.KBD_ALT; - if (kevent.isSymPressed()) // no ctrl key in android, so use sym (?) - e.kbd_flags |= Event.KBD_CTRL; - if (kevent.isShiftPressed()) { - if (keyCode >= KeyEvent.KEYCODE_0 && - keyCode <= KeyEvent.KEYCODE_9) { - // Shift+number -> convert to F* key - int offset = keyCode == KeyEvent.KEYCODE_0 ? - 10 : keyCode - KeyEvent.KEYCODE_1; // turn 0 into 10 - e.kbd_keycode = Event.KEYCODE_F1 + offset; - e.kbd_ascii = Event.ASCII_F1 + offset; - } else - e.kbd_flags |= Event.KBD_SHIFT; - } + if (_events != null) { + _events.sendQuitEvent(); - if (kevent.getAction() == KeyEvent.ACTION_MULTIPLE) { - for (int i = 0; i <= kevent.getRepeatCount(); i++) { - e.type = Event.EVENT_KEYDOWN; - scummvm.pushEvent(e); - e.type = Event.EVENT_KEYUP; - scummvm.pushEvent(e); + try { + // 1s timeout + _scummvm_thread.join(1000); + } catch (InterruptedException e) { + Log.i(ScummVM.LOG_TAG, "Error while joining ScummVM thread", e); } - } else - scummvm.pushEvent(e); - - return true; - } - private int getEventType(int action) { - switch (action) { - case MotionEvent.ACTION_DOWN: - _last_click_was_right = _do_right_click; - return _last_click_was_right ? - Event.EVENT_RBUTTONDOWN : Event.EVENT_LBUTTONDOWN; - case MotionEvent.ACTION_UP: - return _last_click_was_right ? - Event.EVENT_RBUTTONUP : Event.EVENT_LBUTTONUP; - case MotionEvent.ACTION_MOVE: - return Event.EVENT_MOUSEMOVE; - default: - return Event.EVENT_INVALID; + _scummvm = null; } } @Override - public boolean onTrackballEvent(MotionEvent event) { - int type = getEventType(event.getAction()); - if (type == Event.EVENT_INVALID) - return false; - - Event e = new Event(type); - e.mouse_x = - (int)(event.getX() * event.getXPrecision()) * TRACKBALL_SCALE; - e.mouse_y = - (int)(event.getY() * event.getYPrecision()) * TRACKBALL_SCALE; - e.mouse_relative = true; - scummvm.pushEvent(e); - - return true; - } + public boolean onTrackballEvent(MotionEvent e) { + if (_events != null) + return _events.onTrackballEvent(e); - @Override - public boolean onTouchEvent(MotionEvent event) { - int type = getEventType(event.getAction()); - if (type == Event.EVENT_INVALID) - return false; - - Event e = new Event(type); - e.mouse_x = (int)event.getX(); - e.mouse_y = (int)event.getY(); - e.mouse_relative = false; - scummvm.pushEvent(e); - - return true; + return false; } private void showKeyboard(boolean show) { SurfaceView main_surface = (SurfaceView)findViewById(R.id.main_surface); InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); + if (show) imm.showSoftInput(main_surface, InputMethodManager.SHOW_IMPLICIT); else @@ -463,3 +222,4 @@ public class ScummVMActivity extends Activity { InputMethodManager.HIDE_IMPLICIT_ONLY); } } + diff --git a/backends/platform/android/org/inodes/gus/scummvm/ScummVMApplication.java b/backends/platform/android/org/inodes/gus/scummvm/ScummVMApplication.java index 37a9d09e1a..f9eec72eac 100644 --- a/backends/platform/android/org/inodes/gus/scummvm/ScummVMApplication.java +++ b/backends/platform/android/org/inodes/gus/scummvm/ScummVMApplication.java @@ -8,22 +8,24 @@ public class ScummVMApplication extends Application { public final static String ACTION_PLUGIN_QUERY = "org.inodes.gus.scummvm.action.PLUGIN_QUERY"; public final static String EXTRA_UNPACK_LIBS = "org.inodes.gus.scummvm.extra.UNPACK_LIBS"; - private static File cache_dir; + private static File _cache_dir; @Override public void onCreate() { super.onCreate(); + // This is still on /data :( - cache_dir = getCacheDir(); + _cache_dir = getCacheDir(); // This is mounted noexec :( //cache_dir = new File(Environment.getExternalStorageDirectory(), - // "/.ScummVM.tmp"); + // "/.ScummVM.tmp"); // This is owned by download manager and requires special // permissions to access :( //cache_dir = Environment.getDownloadCacheDirectory(); } public static File getLastCacheDir() { - return cache_dir; + return _cache_dir; } } + diff --git a/backends/platform/android/org/inodes/gus/scummvm/ScummVMEvents.java b/backends/platform/android/org/inodes/gus/scummvm/ScummVMEvents.java new file mode 100644 index 0000000000..3d3fdeece4 --- /dev/null +++ b/backends/platform/android/org/inodes/gus/scummvm/ScummVMEvents.java @@ -0,0 +1,201 @@ +package org.inodes.gus.scummvm; + +import android.os.Handler; +import android.os.Message; +import android.util.Log; +import android.content.Context; +import android.view.KeyEvent; +import android.view.KeyCharacterMap; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.GestureDetector; +import android.view.inputmethod.InputMethodManager; + +public class ScummVMEvents implements + android.view.View.OnKeyListener, + android.view.View.OnTouchListener, + android.view.GestureDetector.OnGestureListener, + android.view.GestureDetector.OnDoubleTapListener { + + public static final int JE_SYS_KEY = 0; + public static final int JE_KEY = 1; + public static final int JE_DOWN = 2; + public static final int JE_SCROLL = 3; + public static final int JE_TAP = 4; + public static final int JE_DOUBLE_TAP = 5; + public static final int JE_BALL = 6; + public static final int JE_QUIT = 0x1000; + + final protected Context _context; + final protected ScummVM _scummvm; + final protected GestureDetector _gd; + final protected int _longPress; + final protected int _slop; + + public ScummVMEvents(Context context, ScummVM scummvm) { + _context = context; + _scummvm = scummvm; + + _gd = new GestureDetector(context, this); + _gd.setOnDoubleTapListener(this); + _gd.setIsLongpressEnabled(false); + + _longPress = ViewConfiguration.getLongPressTimeout(); + _slop = ViewConfiguration.get(context).getScaledTouchSlop(); + + } + + final public void sendQuitEvent() { + _scummvm.pushEvent(JE_QUIT, 0, 0, 0, 0, 0); + } + + public boolean onTrackballEvent(MotionEvent e) { + _scummvm.pushEvent(JE_BALL, (int)(e.getX() * e.getXPrecision() * 100), + (int)(e.getY() * e.getYPrecision() * 100), 0, 0, 0); + return true; + } + + final static int MSG_MENU_LONG_PRESS = 1; + + final private Handler keyHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + if (msg.what == MSG_MENU_LONG_PRESS) { + InputMethodManager imm = (InputMethodManager) + _context.getSystemService(_context.INPUT_METHOD_SERVICE); + + if (imm != null) + imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); + } + } + }; + + // OnKeyListener + final public boolean onKey(View v, int keyCode, KeyEvent e) { + final int action = e.getAction(); + + if (e.isSystem()) { + // filter what we handle + switch (keyCode) { + case KeyEvent.KEYCODE_BACK: + case KeyEvent.KEYCODE_MENU: + break; + + default: + return false; + } + + // no repeats for system keys + if (e.getRepeatCount() > 0) + return false; + + // Have to reimplement hold-down-menu-brings-up-softkeybd + // ourselves, since we are otherwise hijacking the menu key :( + // See com.android.internal.policy.impl.PhoneWindow.onKeyDownPanel() + // for the usual Android implementation of this feature. + if (keyCode == KeyEvent.KEYCODE_MENU) { + final boolean fired = + !keyHandler.hasMessages(MSG_MENU_LONG_PRESS); + + keyHandler.removeMessages(MSG_MENU_LONG_PRESS); + + if (action == KeyEvent.ACTION_DOWN) { + keyHandler.sendMessageDelayed(keyHandler.obtainMessage( + MSG_MENU_LONG_PRESS), _longPress); + return true; + } + + if (fired) + return true; + + // only send up events of the menu button to the native side + if (action != KeyEvent.ACTION_UP) + return true; + } + + _scummvm.pushEvent(JE_SYS_KEY, action, keyCode, 0, 0, 0); + + return true; + } + + // sequence of characters + if (action == KeyEvent.ACTION_MULTIPLE && + keyCode == KeyEvent.KEYCODE_UNKNOWN) { + KeyCharacterMap m = KeyCharacterMap.load(e.getDeviceId()); + + for (KeyEvent s : m.getEvents(e.getCharacters().toCharArray())) { + _scummvm.pushEvent(JE_KEY, s.getAction(), s.getKeyCode(), + s.getUnicodeChar() & KeyCharacterMap.COMBINING_ACCENT_MASK, + s.getMetaState(), s.getRepeatCount()); + } + + return true; + } + + _scummvm.pushEvent(JE_KEY, action, keyCode, + e.getUnicodeChar() & KeyCharacterMap.COMBINING_ACCENT_MASK, + e.getMetaState(), e.getRepeatCount()); + + return true; + } + + // OnTouchListener + final public boolean onTouch(View v, MotionEvent e) { + return _gd.onTouchEvent(e); + } + + // OnGestureListener + final public boolean onDown(MotionEvent e) { + _scummvm.pushEvent(JE_DOWN, (int)e.getX(), (int)e.getY(), 0, 0, 0); + return true; + } + + final public boolean onFling(MotionEvent e1, MotionEvent e2, + float velocityX, float velocityY) { + //Log.d(ScummVM.LOG_TAG, String.format("onFling: %s -> %s (%.3f %.3f)", + // e1.toString(), e2.toString(), + // velocityX, velocityY)); + + return true; + } + + final public void onLongPress(MotionEvent e) { + // disabled, interferes with drag&drop + } + + final public boolean onScroll(MotionEvent e1, MotionEvent e2, + float distanceX, float distanceY) { + _scummvm.pushEvent(JE_SCROLL, (int)e1.getX(), (int)e1.getY(), + (int)e2.getX(), (int)e2.getY(), _slop); + + return true; + } + + final public void onShowPress(MotionEvent e) { + } + + final public boolean onSingleTapUp(MotionEvent e) { + _scummvm.pushEvent(JE_TAP, (int)e.getX(), (int)e.getY(), + (int)(e.getEventTime() - e.getDownTime()), 0, 0); + + return true; + } + + // OnDoubleTapListener + final public boolean onDoubleTap(MotionEvent e) { + return true; + } + + final public boolean onDoubleTapEvent(MotionEvent e) { + _scummvm.pushEvent(JE_DOUBLE_TAP, (int)e.getX(), (int)e.getY(), + e.getAction(), _slop, 0); + + return true; + } + + final public boolean onSingleTapConfirmed(MotionEvent e) { + return true; + } +} + diff --git a/backends/platform/android/org/inodes/gus/scummvm/Unpacker.java b/backends/platform/android/org/inodes/gus/scummvm/Unpacker.java index 8811b1f3ae..c4b2ad7f5d 100644 --- a/backends/platform/android/org/inodes/gus/scummvm/Unpacker.java +++ b/backends/platform/android/org/inodes/gus/scummvm/Unpacker.java @@ -370,3 +370,4 @@ public class Unpacker extends Activity { } } } + diff --git a/backends/platform/android/texture.cpp b/backends/platform/android/texture.cpp new file mode 100644 index 0000000000..691c384a18 --- /dev/null +++ b/backends/platform/android/texture.cpp @@ -0,0 +1,571 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#if defined(__ANDROID__) + +#include "base/main.h" +#include "graphics/surface.h" + +#include "common/rect.h" +#include "common/array.h" +#include "common/util.h" +#include "common/tokenizer.h" + +#include "backends/platform/android/texture.h" +#include "backends/platform/android/android.h" + +// Supported GL extensions +static bool npot_supported = false; +#ifdef GL_OES_draw_texture +static bool draw_tex_supported = false; +#endif + +static inline GLfixed xdiv(int numerator, int denominator) { + assert(numerator < (1 << 16)); + return (numerator << 16) / denominator; +} + +template <class T> +static T nextHigher2(T k) { + if (k == 0) + return 1; + --k; + + for (uint i = 1; i < sizeof(T) * CHAR_BIT; i <<= 1) + k = k | k >> i; + + return k + 1; +} + +void GLESBaseTexture::initGLExtensions() { + const char *ext_string = + reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)); + + LOGI("Extensions: %s", ext_string); + + Common::StringTokenizer tokenizer(ext_string, " "); + while (!tokenizer.empty()) { + Common::String token = tokenizer.nextToken(); + + if (token == "GL_ARB_texture_non_power_of_two") + npot_supported = true; + +#ifdef GL_OES_draw_texture + if (token == "GL_OES_draw_texture") + draw_tex_supported = true; +#endif + } +} + +GLESBaseTexture::GLESBaseTexture(GLenum glFormat, GLenum glType, + Graphics::PixelFormat pixelFormat) : + _glFormat(glFormat), + _glType(glType), + _glFilter(GL_NEAREST), + _texture_name(0), + _surface(), + _texture_width(0), + _texture_height(0), + _draw_rect(), + _all_dirty(false), + _dirty_rect(), + _pixelFormat(pixelFormat), + _palettePixelFormat() +{ + GLCALL(glGenTextures(1, &_texture_name)); +} + +GLESBaseTexture::~GLESBaseTexture() { + release(); +} + +void GLESBaseTexture::release() { + LOGD("Destroying texture %u", _texture_name); + + GLCALL(glDeleteTextures(1, &_texture_name)); + _texture_name = 0; +} + +void GLESBaseTexture::reinit() { + GLCALL(glGenTextures(1, &_texture_name)); + + initSize(); + + setDirty(); +} + +void GLESBaseTexture::initSize() { + // Allocate room for the texture now, but pixel data gets uploaded + // later (perhaps with multiple TexSubImage2D operations). + GLCALL(glBindTexture(GL_TEXTURE_2D, _texture_name)); + GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter)); + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter)); + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, _glFormat, + _texture_width, _texture_height, + 0, _glFormat, _glType, 0)); +} + +void GLESBaseTexture::setLinearFilter(bool value) { + if (value) + _glFilter = GL_LINEAR; + else + _glFilter = GL_NEAREST; + + GLCALL(glBindTexture(GL_TEXTURE_2D, _texture_name)); + + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter)); + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter)); +} + +void GLESBaseTexture::allocBuffer(GLuint w, GLuint h) { + _surface.w = w; + _surface.h = h; + _surface.bytesPerPixel = _pixelFormat.bytesPerPixel; + + if (w == _texture_width && h == _texture_height) + return; + + if (npot_supported) { + _texture_width = _surface.w; + _texture_height = _surface.h; + } else { + _texture_width = nextHigher2(_surface.w); + _texture_height = nextHigher2(_surface.h); + } + + initSize(); +} + +void GLESBaseTexture::drawTexture(GLshort x, GLshort y, GLshort w, GLshort h) { + GLCALL(glBindTexture(GL_TEXTURE_2D, _texture_name)); + +#ifdef GL_OES_draw_texture + // Great extension, but only works under specific conditions. + // Still a work-in-progress - disabled for now. + if (false && draw_tex_supported && !hasPalette()) { + //GLCALL(glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)); + const GLint crop[4] = { 0, _surface.h, _surface.w, -_surface.h }; + + GLCALL(glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop)); + + // Android GLES bug? + GLCALL(glColor4ub(0xff, 0xff, 0xff, 0xff)); + + GLCALL(glDrawTexiOES(x, y, 0, w, h)); + } else +#endif + { + const GLfixed tex_width = xdiv(_surface.w, _texture_width); + const GLfixed tex_height = xdiv(_surface.h, _texture_height); + const GLfixed texcoords[] = { + 0, 0, + tex_width, 0, + 0, tex_height, + tex_width, tex_height, + }; + + GLCALL(glTexCoordPointer(2, GL_FIXED, 0, texcoords)); + + const GLshort vertices[] = { + x, y, + x + w, y, + x, y + h, + x + w, y + h, + }; + + GLCALL(glVertexPointer(2, GL_SHORT, 0, vertices)); + + assert(ARRAYSIZE(vertices) == ARRAYSIZE(texcoords)); + GLCALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, ARRAYSIZE(vertices) / 2)); + } + + clearDirty(); +} + +const Graphics::PixelFormat &GLESBaseTexture::getPixelFormat() const { + return _pixelFormat; +} + +GLESTexture::GLESTexture(GLenum glFormat, GLenum glType, + Graphics::PixelFormat pixelFormat) : + GLESBaseTexture(glFormat, glType, pixelFormat), + _pixels(0), + _buf(0) { +} + +GLESTexture::~GLESTexture() { + delete[] _buf; + delete[] _pixels; +} + +void GLESTexture::allocBuffer(GLuint w, GLuint h) { + GLuint oldw = _surface.w; + GLuint oldh = _surface.h; + + GLESBaseTexture::allocBuffer(w, h); + + if (_surface.w == oldw && _surface.h == oldh) + return; + + delete[] _buf; + delete[] _pixels; + + _pixels = new byte[w * h * _surface.bytesPerPixel]; + assert(_pixels); + + _surface.pixels = _pixels; + _surface.pitch = w * _pixelFormat.bytesPerPixel; + + _buf = new byte[w * h * _surface.bytesPerPixel]; + assert(_buf); +} + +void GLESTexture::updateBuffer(GLuint x, GLuint y, GLuint w, GLuint h, + const void *buf, int pitch_buf) { + setDirtyRect(Common::Rect(x, y, x + w, y + h)); + + const byte *src = (const byte *)buf; + byte *dst = _pixels + y * _surface.pitch + x * _surface.bytesPerPixel; + + do { + memcpy(dst, src, w * _surface.bytesPerPixel); + dst += _surface.pitch; + src += pitch_buf; + } while (--h); +} + +void GLESTexture::fillBuffer(uint32 color) { + assert(_surface.pixels); + + if (_pixelFormat.bytesPerPixel == 1 || + ((color & 0xff) == ((color >> 8) & 0xff))) + memset(_pixels, color & 0xff, _surface.pitch * _surface.h); + else + Common::set_to(_pixels, _pixels + _surface.pitch * _surface.h, + (uint16)color); + + setDirty(); +} + +void GLESTexture::drawTexture(GLshort x, GLshort y, GLshort w, GLshort h) { + if (_all_dirty) { + _dirty_rect.top = 0; + _dirty_rect.left = 0; + _dirty_rect.bottom = _surface.h; + _dirty_rect.right = _surface.w; + + _all_dirty = false; + } + + if (!_dirty_rect.isEmpty()) { + byte *_tex; + + int16 dwidth = _dirty_rect.width(); + int16 dheight = _dirty_rect.height(); + + if (dwidth == _surface.w) { + _tex = _pixels + _dirty_rect.top * _surface.pitch; + } else { + _tex = _buf; + + byte *src = _pixels + _dirty_rect.top * _surface.pitch + + _dirty_rect.left * _surface.bytesPerPixel; + byte *dst = _buf; + + uint16 l = dwidth * _surface.bytesPerPixel; + + for (uint16 i = 0; i < dheight; ++i) { + memcpy(dst, src, l); + src += _surface.pitch; + dst += l; + } + } + + GLCALL(glBindTexture(GL_TEXTURE_2D, _texture_name)); + GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); + + GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, + _dirty_rect.left, _dirty_rect.top, + dwidth, dheight, _glFormat, _glType, _tex)); + } + + GLESBaseTexture::drawTexture(x, y, w, h); +} + +GLES4444Texture::GLES4444Texture() : + GLESTexture(GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, pixelFormat()) { +} + +GLES4444Texture::~GLES4444Texture() { +} + +GLES5551Texture::GLES5551Texture() : + GLESTexture(GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, pixelFormat()) { +} + +GLES5551Texture::~GLES5551Texture() { +} + +GLES565Texture::GLES565Texture() : + GLESTexture(GL_RGB, GL_UNSIGNED_SHORT_5_6_5, pixelFormat()) { +} + +GLES565Texture::~GLES565Texture() { +} + +GLESPaletteTexture::GLESPaletteTexture(GLenum glFormat, GLenum glType, + Graphics::PixelFormat palettePixelFormat) : + GLESBaseTexture(glFormat, glType, + Graphics::PixelFormat::createFormatCLUT8()), + _texture(0) +{ + _palettePixelFormat = palettePixelFormat; + _paletteSize = _palettePixelFormat.bytesPerPixel * 256; +} + +GLESPaletteTexture::~GLESPaletteTexture() { + delete[] _texture; +} + +void GLESPaletteTexture::allocBuffer(GLuint w, GLuint h) { + GLuint oldw = _surface.w; + GLuint oldh = _surface.h; + + GLESBaseTexture::allocBuffer(w, h); + + if (_surface.w == oldw && _surface.h == oldh) + return; + + byte *new_buffer = new byte[_paletteSize + + _texture_width * _texture_height]; + assert(new_buffer); + + if (_texture) { + // preserve palette + memcpy(new_buffer, _texture, _paletteSize); + delete[] _texture; + } + + _texture = new_buffer; + _surface.pixels = _texture + _paletteSize; + _surface.pitch = _texture_width; +} + +void GLESPaletteTexture::fillBuffer(uint32 color) { + assert(_surface.pixels); + memset(_surface.pixels, color & 0xff, _surface.pitch * _surface.h); + setDirty(); +} + +void GLESPaletteTexture::updateBuffer(GLuint x, GLuint y, GLuint w, GLuint h, + const void *buf, int pitch_buf) { + setDirtyRect(Common::Rect(x, y, x + w, y + h)); + + const byte * src = static_cast<const byte *>(buf); + byte *dst = static_cast<byte *>(_surface.getBasePtr(x, y)); + + do { + memcpy(dst, src, w); + dst += _surface.pitch; + src += pitch_buf; + } while (--h); +} + +void GLESPaletteTexture::drawTexture(GLshort x, GLshort y, GLshort w, + GLshort h) { + if (dirty()) { + GLCALL(glBindTexture(GL_TEXTURE_2D, _texture_name)); + + const size_t texture_size = _paletteSize + + _texture_width * _texture_height; + + GLCALL(glCompressedTexImage2D(GL_TEXTURE_2D, 0, _glType, + _texture_width, _texture_height, + 0, texture_size, _texture)); + } + + GLESBaseTexture::drawTexture(x, y, w, h); +} + +GLESPalette888Texture::GLESPalette888Texture() : + GLESPaletteTexture(GL_RGB, GL_PALETTE8_RGB8_OES, + Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0)) { +} + +GLESPalette888Texture::~GLESPalette888Texture() { +} + +GLESPalette8888Texture::GLESPalette8888Texture() : + GLESPaletteTexture(GL_RGBA, GL_PALETTE8_RGBA8_OES, + Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)) { +} + +GLESPalette8888Texture::~GLESPalette8888Texture() { +} + +GLESPalette565Texture::GLESPalette565Texture() : + GLESPaletteTexture(GL_RGB, GL_PALETTE8_R5_G6_B5_OES, + Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)) { +} + +GLESPalette565Texture::~GLESPalette565Texture() { +} + +GLESPalette4444Texture::GLESPalette4444Texture() : + GLESPaletteTexture(GL_RGBA, GL_PALETTE8_RGBA4_OES, + Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0)) { +} + +GLESPalette4444Texture::~GLESPalette4444Texture() { +} + +GLESPalette5551Texture::GLESPalette5551Texture() : + GLESPaletteTexture(GL_RGBA, GL_PALETTE8_RGB5_A1_OES, + Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0)) { +} + +GLESPalette5551Texture::~GLESPalette5551Texture() { +} + +GLESFakePaletteTexture::GLESFakePaletteTexture(GLenum glFormat, GLenum glType, + Graphics::PixelFormat pixelFormat) : + GLESBaseTexture(glFormat, glType, pixelFormat), + _palette(0), + _pixels(0), + _buf(0) +{ + _palettePixelFormat = pixelFormat; + _fake_format = Graphics::PixelFormat::createFormatCLUT8(); + + _palette = new uint16[256]; + assert(_palette); + + memset(_palette, 0, 256 * 2); +} + +GLESFakePaletteTexture::~GLESFakePaletteTexture() { + delete[] _buf; + delete[] _pixels; + delete[] _palette; +} + +void GLESFakePaletteTexture::allocBuffer(GLuint w, GLuint h) { + GLuint oldw = _surface.w; + GLuint oldh = _surface.h; + + GLESBaseTexture::allocBuffer(w, h); + + if (_surface.w == oldw && _surface.h == oldh) + return; + + delete[] _buf; + delete[] _pixels; + + _pixels = new byte[w * h]; + assert(_pixels); + + // fixup surface, for the outside this is a CLUT8 surface + _surface.pixels = _pixels; + _surface.bytesPerPixel = 1; + _surface.pitch = w; + + _buf = new uint16[w * h]; + assert(_buf); +} + +void GLESFakePaletteTexture::fillBuffer(uint32 color) { + assert(_surface.pixels); + memset(_surface.pixels, color & 0xff, _surface.pitch * _surface.h); + setDirty(); +} + +void GLESFakePaletteTexture::updateBuffer(GLuint x, GLuint y, GLuint w, + GLuint h, const void *buf, + int pitch_buf) { + setDirtyRect(Common::Rect(x, y, x + w, y + h)); + + const byte *src = (const byte *)buf; + byte *dst = _pixels + y * _surface.pitch + x; + + do { + memcpy(dst, src, w); + dst += _surface.pitch; + src += pitch_buf; + } while (--h); +} + +void GLESFakePaletteTexture::drawTexture(GLshort x, GLshort y, GLshort w, + GLshort h) { + if (_all_dirty) { + _dirty_rect.top = 0; + _dirty_rect.left = 0; + _dirty_rect.bottom = _surface.h; + _dirty_rect.right = _surface.w; + + _all_dirty = false; + } + + if (!_dirty_rect.isEmpty()) { + int16 dwidth = _dirty_rect.width(); + int16 dheight = _dirty_rect.height(); + + byte *src = _pixels + _dirty_rect.top * _surface.pitch + + _dirty_rect.left; + uint16 *dst = _buf; + uint pitch_delta = _surface.pitch - dwidth; + + for (uint16 j = 0; j < dheight; ++j) { + for (uint16 i = 0; i < dwidth; ++i) + *dst++ = _palette[*src++]; + src += pitch_delta; + } + + GLCALL(glBindTexture(GL_TEXTURE_2D, _texture_name)); + + GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, + _dirty_rect.left, _dirty_rect.top, + dwidth, dheight, _glFormat, _glType, _buf)); + } + + GLESBaseTexture::drawTexture(x, y, w, h); +} + +const Graphics::PixelFormat &GLESFakePaletteTexture::getPixelFormat() const { + return _fake_format; +} + +GLESFakePalette565Texture::GLESFakePalette565Texture() : + GLESFakePaletteTexture(GL_RGB, GL_UNSIGNED_SHORT_5_6_5, + GLES565Texture::pixelFormat()) { +} + +GLESFakePalette565Texture::~GLESFakePalette565Texture() { +} + +#endif + diff --git a/backends/platform/android/texture.h b/backends/platform/android/texture.h new file mode 100644 index 0000000000..64489da0a4 --- /dev/null +++ b/backends/platform/android/texture.h @@ -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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef _ANDROID_TEXTURE_H_ +#define _ANDROID_TEXTURE_H_ + +#if defined(__ANDROID__) + +#include <GLES/gl.h> + +#include "graphics/surface.h" +#include "graphics/pixelformat.h" + +#include "common/rect.h" +#include "common/array.h" + +class GLESBaseTexture { +public: + static void initGLExtensions(); + +protected: + GLESBaseTexture(GLenum glFormat, GLenum glType, + Graphics::PixelFormat pixelFormat); + +public: + virtual ~GLESBaseTexture(); + + void release(); + void reinit(); + void initSize(); + + void setLinearFilter(bool value); + + virtual void allocBuffer(GLuint w, GLuint h); + + virtual void updateBuffer(GLuint x, GLuint y, GLuint width, GLuint height, + const void *buf, int pitch_buf) = 0; + virtual void fillBuffer(uint32 color) = 0; + + virtual void drawTexture(GLshort x, GLshort y, GLshort w, GLshort h); + + inline void setDrawRect(const Common::Rect &rect) { + _draw_rect = rect; + } + + inline void setDrawRect(int16 w, int16 h) { + _draw_rect = Common::Rect(w, h); + } + + inline void setDrawRect(int16 x1, int16 y1, int16 x2, int16 y2) { + _draw_rect = Common::Rect(x1, y1, x2, y2); + } + + inline const Common::Rect &getDrawRect() const { + return _draw_rect; + } + + inline void drawTextureRect() { + drawTexture(_draw_rect.left, _draw_rect.top, + _draw_rect.width(), _draw_rect.height()); + } + + inline void drawTextureOrigin() { + drawTexture(0, 0, _surface.w, _surface.h); + } + + inline GLuint width() const { + return _surface.w; + } + + inline GLuint height() const { + return _surface.h; + } + + inline uint16 pitch() const { + return _surface.pitch; + } + + inline const Graphics::Surface *surface_const() const { + return &_surface; + } + + inline Graphics::Surface *surface() { + setDirty(); + return &_surface; + } + + virtual const byte *palette_const() const { + return 0; + }; + + virtual byte *palette() { + return 0; + }; + + inline bool hasPalette() const { + return _palettePixelFormat.bytesPerPixel > 0; + } + + inline bool dirty() const { + return _all_dirty || !_dirty_rect.isEmpty(); + } + + virtual const Graphics::PixelFormat &getPixelFormat() const; + + inline const Graphics::PixelFormat &getPalettePixelFormat() const { + return _palettePixelFormat; + } + +protected: + inline void setDirty() { + _all_dirty = true; + } + + inline void clearDirty() { + _all_dirty = false; + _dirty_rect.top = 0; + _dirty_rect.left = 0; + _dirty_rect.bottom = 0; + _dirty_rect.right = 0; + } + + inline void setDirtyRect(const Common::Rect& r) { + if (!_all_dirty) { + if (_dirty_rect.isEmpty()) + _dirty_rect = r; + else + _dirty_rect.extend(r); + } + } + + GLenum _glFormat; + GLenum _glType; + GLint _glFilter; + + GLuint _texture_name; + Graphics::Surface _surface; + GLuint _texture_width; + GLuint _texture_height; + + Common::Rect _draw_rect; + + bool _all_dirty; + Common::Rect _dirty_rect; + + Graphics::PixelFormat _pixelFormat; + Graphics::PixelFormat _palettePixelFormat; +}; + +class GLESTexture : public GLESBaseTexture { +protected: + GLESTexture(GLenum glFormat, GLenum glType, + Graphics::PixelFormat pixelFormat); + +public: + virtual ~GLESTexture(); + + virtual void allocBuffer(GLuint w, GLuint h); + + virtual void updateBuffer(GLuint x, GLuint y, GLuint width, GLuint height, + const void *buf, int pitch_buf); + virtual void fillBuffer(uint32 color); + + virtual void drawTexture(GLshort x, GLshort y, GLshort w, GLshort h); + +protected: + byte *_pixels; + byte *_buf; +}; + +// RGBA4444 texture +class GLES4444Texture : public GLESTexture { +public: + GLES4444Texture(); + virtual ~GLES4444Texture(); + + static Graphics::PixelFormat pixelFormat() { + return Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0); + } +}; + +// RGBA5551 texture +class GLES5551Texture : public GLESTexture { +public: + GLES5551Texture(); + virtual ~GLES5551Texture(); + + static inline Graphics::PixelFormat pixelFormat() { + return Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0); + } +}; + +// RGB565 texture +class GLES565Texture : public GLESTexture { +public: + GLES565Texture(); + virtual ~GLES565Texture(); + + static inline Graphics::PixelFormat pixelFormat() { + return Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0); + } +}; + +class GLESPaletteTexture : public GLESBaseTexture { +protected: + GLESPaletteTexture(GLenum glFormat, GLenum glType, + Graphics::PixelFormat palettePixelFormat); + +public: + virtual ~GLESPaletteTexture(); + + virtual void allocBuffer(GLuint w, GLuint h); + virtual void updateBuffer(GLuint x, GLuint y, GLuint width, GLuint height, + const void *buf, int pitch_buf); + virtual void fillBuffer(uint32 color); + + virtual void drawTexture(GLshort x, GLshort y, GLshort w, GLshort h); + + virtual const byte *palette_const() const { + return _texture; + }; + + virtual byte *palette() { + setDirty(); + return _texture; + }; + +protected: + byte *_texture; + size_t _paletteSize; +}; + +// RGB888 256-entry paletted texture +class GLESPalette888Texture : public GLESPaletteTexture { +public: + GLESPalette888Texture(); + virtual ~GLESPalette888Texture(); +}; + +// RGBA8888 256-entry paletted texture +class GLESPalette8888Texture : public GLESPaletteTexture { +public: + GLESPalette8888Texture(); + virtual ~GLESPalette8888Texture(); +}; + +// RGB565 256-entry paletted texture +class GLESPalette565Texture : public GLESPaletteTexture { +public: + GLESPalette565Texture(); + virtual ~GLESPalette565Texture(); +}; + +// RGBA4444 256-entry paletted texture +class GLESPalette4444Texture : public GLESPaletteTexture { +public: + GLESPalette4444Texture(); + virtual ~GLESPalette4444Texture(); +}; + +// RGBA5551 256-entry paletted texture +class GLESPalette5551Texture : public GLESPaletteTexture { +public: + GLESPalette5551Texture(); + virtual ~GLESPalette5551Texture(); +}; + +class GLESFakePaletteTexture : public GLESBaseTexture { +protected: + GLESFakePaletteTexture(GLenum glFormat, GLenum glType, + Graphics::PixelFormat pixelFormat); + +public: + virtual ~GLESFakePaletteTexture(); + + virtual void allocBuffer(GLuint w, GLuint h); + virtual void updateBuffer(GLuint x, GLuint y, GLuint width, GLuint height, + const void *buf, int pitch_buf); + virtual void fillBuffer(uint32 color); + + virtual void drawTexture(GLshort x, GLshort y, GLshort w, GLshort h); + + virtual const byte *palette_const() const { + return (byte *)_palette; + }; + + virtual byte *palette() { + setDirty(); + return (byte *)_palette; + }; + + virtual const Graphics::PixelFormat &getPixelFormat() const; + +protected: + Graphics::PixelFormat _fake_format; + uint16 *_palette; + byte *_pixels; + uint16 *_buf; +}; + +class GLESFakePalette565Texture : public GLESFakePaletteTexture { +public: + GLESFakePalette565Texture(); + virtual ~GLESFakePalette565Texture(); +}; + +#endif +#endif + diff --git a/backends/platform/android/video.cpp b/backends/platform/android/video.cpp deleted file mode 100644 index f8427c2ac8..0000000000 --- a/backends/platform/android/video.cpp +++ /dev/null @@ -1,342 +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. - * - * $URL$ - * $Id$ - * - */ - -#if defined(__ANDROID__) - -#include "base/main.h" -#include "graphics/surface.h" - -#include "common/rect.h" -#include "common/array.h" -#include "common/util.h" -#include "common/tokenizer.h" - -#include "backends/platform/android/android.h" -#include "backends/platform/android/video.h" - -// Unfortunately, Android devices are too varied to make broad assumptions :/ -#define TEXSUBIMAGE_IS_EXPENSIVE 0 - -// Supported GL extensions -static bool npot_supported = false; -#ifdef GL_OES_draw_texture -static bool draw_tex_supported = false; -#endif - -static inline GLfixed xdiv(int numerator, int denominator) { - assert(numerator < (1 << 16)); - return (numerator << 16) / denominator; -} - -template <class T> -static T nextHigher2(T k) { - if (k == 0) - return 1; - --k; - - for (uint i = 1; i < sizeof(T) * CHAR_BIT; i <<= 1) - k = k | k >> i; - - return k + 1; -} - -void GLESTexture::initGLExtensions() { - const char *ext_string = - reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)); - - LOGI("Extensions: %s", ext_string); - - Common::StringTokenizer tokenizer(ext_string, " "); - while (!tokenizer.empty()) { - Common::String token = tokenizer.nextToken(); - - if (token == "GL_ARB_texture_non_power_of_two") - npot_supported = true; - -#ifdef GL_OES_draw_texture - if (token == "GL_OES_draw_texture") - draw_tex_supported = true; -#endif - } -} - -GLESTexture::GLESTexture() : - _texture_width(0), - _texture_height(0), - _all_dirty(true) -{ - GLCALL(glGenTextures(1, &_texture_name)); - - // This all gets reset later in allocBuffer: - _surface.w = 0; - _surface.h = 0; - _surface.pitch = _texture_width; - _surface.pixels = 0; - _surface.bytesPerPixel = 0; -} - -GLESTexture::~GLESTexture() { - debug("Destroying texture %u", _texture_name); - GLCALL(glDeleteTextures(1, &_texture_name)); -} - -void GLESTexture::reinitGL() { - GLCALL(glDeleteTextures(1, &_texture_name)); - GLCALL(glGenTextures(1, &_texture_name)); - - // bypass allocBuffer() shortcut to reinit the texture properly - _texture_width = 0; - _texture_height = 0; - - allocBuffer(_surface.w, _surface.h); - setDirty(); -} - -void GLESTexture::allocBuffer(GLuint w, GLuint h) { - int bpp = bytesPerPixel(); - _surface.w = w; - _surface.h = h; - _surface.bytesPerPixel = bpp; - - // Already allocated a sufficiently large buffer? - if (w <= _texture_width && h <= _texture_height) - return; - - if (npot_supported) { - _texture_width = _surface.w; - _texture_height = _surface.h; - } else { - _texture_width = nextHigher2(_surface.w); - _texture_height = nextHigher2(_surface.h); - } - - _surface.pitch = _texture_width * bpp; - - // Allocate room for the texture now, but pixel data gets uploaded - // later (perhaps with multiple TexSubImage2D operations). - GLCALL(glBindTexture(GL_TEXTURE_2D, _texture_name)); - GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); - GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); - GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); - GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, glFormat(), - _texture_width, _texture_height, - 0, glFormat(), glType(), 0)); -} - -void GLESTexture::updateBuffer(GLuint x, GLuint y, GLuint w, GLuint h, - const void *buf, int pitch) { - ENTER("%u, %u, %u, %u, %p, %d", x, y, w, h, buf, pitch); - - GLCALL(glBindTexture(GL_TEXTURE_2D, _texture_name)); - GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); - - setDirtyRect(Common::Rect(x, y, x+w, y+h)); - - if (static_cast<int>(w) * bytesPerPixel() == pitch) { - GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, - glFormat(), glType(), buf)); - } else { - // GLES removed the ability to specify pitch, so we - // have to do this ourselves. - if (h == 0) - return; - -#if TEXSUBIMAGE_IS_EXPENSIVE - byte tmpbuf[w * h * bytesPerPixel()]; - const byte *src = static_cast<const byte *>(buf); - byte *dst = tmpbuf; - GLuint count = h; - - do { - memcpy(dst, src, w * bytesPerPixel()); - dst += w * bytesPerPixel(); - src += pitch; - } while (--count); - - GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, - glFormat(), glType(), tmpbuf)); -#else - // This version avoids the intermediate copy at the expense of - // repeat glTexSubImage2D calls. On some devices this is worse. - const byte *src = static_cast<const byte *>(buf); - do { - GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, - w, 1, glFormat(), glType(), src)); - ++y; - src += pitch; - } while (--h); -#endif - } -} - -void GLESTexture::fillBuffer(byte x) { - int rowbytes = _surface.w * bytesPerPixel(); - byte tmpbuf[_surface.h * rowbytes]; - memset(tmpbuf, x, _surface.h * rowbytes); - updateBuffer(0, 0, _surface.w, _surface.h, tmpbuf, rowbytes); -} - -void GLESTexture::drawTexture(GLshort x, GLshort y, GLshort w, GLshort h) { - GLCALL(glBindTexture(GL_TEXTURE_2D, _texture_name)); - -#ifdef GL_OES_draw_texture - // Great extension, but only works under specific conditions. - // Still a work-in-progress - disabled for now. - if (false && draw_tex_supported && paletteSize() == 0) { - //GLCALL(glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)); - const GLint crop[4] = { 0, _surface.h, _surface.w, -_surface.h }; - - GLCALL(glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop)); - - // Android GLES bug? - GLCALL(glColor4ub(0xff, 0xff, 0xff, 0xff)); - - GLCALL(glDrawTexiOES(x, y, 0, w, h)); - } else -#endif - { - const GLfixed tex_width = xdiv(_surface.w, _texture_width); - const GLfixed tex_height = xdiv(_surface.h, _texture_height); - const GLfixed texcoords[] = { - 0, 0, - tex_width, 0, - 0, tex_height, - tex_width, tex_height, - }; - - GLCALL(glTexCoordPointer(2, GL_FIXED, 0, texcoords)); - - const GLshort vertices[] = { - x, y, - x + w, y, - x, y + h, - x + w, y + h, - }; - - GLCALL(glVertexPointer(2, GL_SHORT, 0, vertices)); - - assert(ARRAYSIZE(vertices) == ARRAYSIZE(texcoords)); - GLCALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, ARRAYSIZE(vertices) / 2)); - } - - _all_dirty = false; - _dirty_rect = Common::Rect(); -} - -GLESPaletteTexture::GLESPaletteTexture() : - GLESTexture(), - _texture(0) -{ -} - -GLESPaletteTexture::~GLESPaletteTexture() { - delete[] _texture; -} - -void GLESPaletteTexture::allocBuffer(GLuint w, GLuint h) { - int bpp = bytesPerPixel(); - _surface.w = w; - _surface.h = h; - _surface.bytesPerPixel = bpp; - - // Already allocated a sufficiently large buffer? - if (w <= _texture_width && h <= _texture_height) - return; - - if (npot_supported) { - _texture_width = _surface.w; - _texture_height = _surface.h; - } else { - _texture_width = nextHigher2(_surface.w); - _texture_height = nextHigher2(_surface.h); - } - _surface.pitch = _texture_width * bpp; - - // Texture gets uploaded later (from drawTexture()) - - byte *new_buffer = new byte[paletteSize() + - _texture_width * _texture_height * bytesPerPixel()]; - if (_texture) { - // preserve palette - memcpy(new_buffer, _texture, paletteSize()); - delete[] _texture; - } - - _texture = new_buffer; - _surface.pixels = _texture + paletteSize(); -} - -void GLESPaletteTexture::fillBuffer(byte x) { - assert(_surface.pixels); - memset(_surface.pixels, x, _surface.pitch * _surface.h); - setDirty(); -} - -void GLESPaletteTexture::updateBuffer(GLuint x, GLuint y, - GLuint w, GLuint h, - const void *buf, int pitch) { - _all_dirty = true; - - const byte * src = static_cast<const byte *>(buf); - byte *dst = static_cast<byte *>(_surface.getBasePtr(x, y)); - - do { - memcpy(dst, src, w * bytesPerPixel()); - dst += _surface.pitch; - src += pitch; - } while (--h); -} - -void GLESPaletteTexture::uploadTexture() const { - const size_t texture_size = - paletteSize() + _texture_width * _texture_height * bytesPerPixel(); - - GLCALL(glCompressedTexImage2D(GL_TEXTURE_2D, 0, glType(), - _texture_width, _texture_height, - 0, texture_size, _texture)); -} - -void GLESPaletteTexture::drawTexture(GLshort x, GLshort y, GLshort w, GLshort h) { - if (_all_dirty) { - GLCALL(glBindTexture(GL_TEXTURE_2D, _texture_name)); - GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, - GL_NEAREST)); - GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, - GL_NEAREST)); - GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, - GL_CLAMP_TO_EDGE)); - GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, - GL_CLAMP_TO_EDGE)); - uploadTexture(); - _all_dirty = false; - } - - GLESTexture::drawTexture(x, y, w, h); -} - -#endif - diff --git a/backends/platform/android/video.h b/backends/platform/android/video.h deleted file mode 100644 index da42ea876d..0000000000 --- a/backends/platform/android/video.h +++ /dev/null @@ -1,209 +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. - * - * $URL$ - * $Id$ - * - */ - -#if defined(__ANDROID__) - -#include <GLES/gl.h> - -#include "graphics/surface.h" - -#include "common/rect.h" -#include "common/array.h" - -class GLESTexture { -public: - static void initGLExtensions(); - - GLESTexture(); - virtual ~GLESTexture(); - - virtual void reinitGL(); - virtual void allocBuffer(GLuint width, GLuint height); - - virtual void updateBuffer(GLuint x, GLuint y, GLuint width, GLuint height, - const void *buf, int pitch); - virtual void fillBuffer(byte x); - - virtual void drawTexture(GLshort x, GLshort y, GLshort w, GLshort h); - - inline GLuint width() const { - return _surface.w; - } - - inline GLuint height() const { - return _surface.h; - } - - inline GLuint texture_name() const { - return _texture_name; - } - - inline const Graphics::Surface *surface_const() const { - return &_surface; - } - - inline Graphics::Surface *surface() { - setDirty(); - return &_surface; - } - - inline bool dirty() const { - return _all_dirty || !_dirty_rect.isEmpty(); - } - - inline void drawTexture() { - drawTexture(0, 0, _surface.w, _surface.h); - } - -protected: - virtual byte bytesPerPixel() const = 0; - virtual GLenum glFormat() const = 0; - virtual GLenum glType() const = 0; - - virtual size_t paletteSize() const { - return 0; - } - - inline void setDirty() { - _all_dirty = true; - _dirty_rect = Common::Rect(); - } - - inline void setDirtyRect(const Common::Rect& r) { - if (!_all_dirty) { - if (_dirty_rect.isEmpty()) - _dirty_rect = r; - else - _dirty_rect.extend(r); - } - } - - GLuint _texture_name; - Graphics::Surface _surface; - GLuint _texture_width; - GLuint _texture_height; - bool _all_dirty; - - // Covers dirty area - Common::Rect _dirty_rect; -}; - -// RGBA4444 texture -class GLES4444Texture : public GLESTexture { -protected: - virtual byte bytesPerPixel() const { - return 2; - } - - virtual GLenum glFormat() const { - return GL_RGBA; - } - - virtual GLenum glType() const { - return GL_UNSIGNED_SHORT_4_4_4_4; - } -}; - -// RGB565 texture -class GLES565Texture : public GLESTexture { -protected: - virtual byte bytesPerPixel() const { - return 2; - } - - virtual GLenum glFormat() const { - return GL_RGB; - } - - virtual GLenum glType() const { - return GL_UNSIGNED_SHORT_5_6_5; - } -}; - -// RGB888 256-entry paletted texture -class GLESPaletteTexture : public GLESTexture { -public: - GLESPaletteTexture(); - virtual ~GLESPaletteTexture(); - - virtual void allocBuffer(GLuint width, GLuint height); - virtual void updateBuffer(GLuint x, GLuint y, GLuint width, GLuint height, - const void *buf, int pitch); - virtual void fillBuffer(byte x); - - virtual void drawTexture(GLshort x, GLshort y, GLshort w, GLshort h); - - inline void drawTexture() { - drawTexture(0, 0, _surface.w, _surface.h); - } - - inline const byte *palette_const() const { - return _texture; - }; - - inline byte *palette() { - setDirty(); - return _texture; - }; - -protected: - virtual byte bytesPerPixel() const { - return 1; - } - - virtual GLenum glFormat() const { - return GL_RGB; - } - - virtual GLenum glType() const { - return GL_PALETTE8_RGB8_OES; - } - - virtual size_t paletteSize() const { - return 256 * 3; - } - - void uploadTexture() const; - - byte *_texture; -}; - -// RGBA8888 256-entry paletted texture -class GLESPaletteATexture : public GLESPaletteTexture { -protected: - virtual GLenum glFormat() const { - return GL_RGBA; - } - - virtual GLenum glType() const { - return GL_PALETTE8_RGBA8_OES; - } - - virtual size_t paletteSize() const { - return 256 * 4; - } -}; - -#endif diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp index dc91bd9fe7..bddb48ca95 100644 --- a/backends/platform/sdl/sdl.cpp +++ b/backends/platform/sdl/sdl.cpp @@ -125,10 +125,10 @@ void OSystem_SDL::init() { if (_timerManager == 0) _timerManager = new SdlTimerManager(); - #ifdef USE_OPENGL - // Setup a list with both SDL and OpenGL graphics modes - setupGraphicsModes(); - #endif +#ifdef USE_OPENGL + // Setup a list with both SDL and OpenGL graphics modes + setupGraphicsModes(); +#endif } void OSystem_SDL::initBackend() { @@ -148,11 +148,15 @@ void OSystem_SDL::initBackend() { Common::String gfxMode(ConfMan.get("gfx_mode")); bool use_opengl = false; const OSystem::GraphicsMode *mode = OpenGLSdlGraphicsManager::supportedGraphicsModes(); + int i = 0; while (mode->name) { - if (scumm_stricmp(mode->name, gfxMode.c_str()) == 0) + if (scumm_stricmp(mode->name, gfxMode.c_str()) == 0) { + _graphicsMode = i + _sdlModesCount; use_opengl = true; + } mode++; + ++i; } // If the gfx_mode is from OpenGL, create the OpenGL graphics manager @@ -464,6 +468,7 @@ int OSystem_SDL::getDefaultGraphicsMode() const { bool OSystem_SDL::setGraphicsMode(int mode) { const OSystem::GraphicsMode *srcMode; int i; + // Check if mode is from SDL or OpenGL if (mode < _sdlModesCount) { srcMode = SdlGraphicsManager::supportedGraphicsModes(); @@ -472,28 +477,34 @@ bool OSystem_SDL::setGraphicsMode(int mode) { srcMode = OpenGLSdlGraphicsManager::supportedGraphicsModes(); i = _sdlModesCount; } + // Loop through modes while (srcMode->name) { if (i == mode) { // If the new mode and the current mode are not from the same graphics // manager, delete and create the new mode graphics manager if (_graphicsMode >= _sdlModesCount && mode < _sdlModesCount) { + debug(1, "switching to plain SDL graphics"); delete _graphicsManager; _graphicsManager = new SdlGraphicsManager(_eventSource); ((SdlGraphicsManager *)_graphicsManager)->initEventObserver(); _graphicsManager->beginGFXTransaction(); } else if (_graphicsMode < _sdlModesCount && mode >= _sdlModesCount) { + debug(1, "switching to OpenGL graphics"); delete _graphicsManager; _graphicsManager = new OpenGLSdlGraphicsManager(); ((OpenGLSdlGraphicsManager *)_graphicsManager)->initEventObserver(); _graphicsManager->beginGFXTransaction(); } + _graphicsMode = mode; return _graphicsManager->setGraphicsMode(srcMode->id); } + i++; srcMode++; } + return false; } diff --git a/backends/platform/symbian/S60v3/ScummVM_A0000658_S60v3.mmp.in b/backends/platform/symbian/S60v3/ScummVM_A0000658_S60v3.mmp.in index c7623d09a8..12b4cb3610 100644 --- a/backends/platform/symbian/S60v3/ScummVM_A0000658_S60v3.mmp.in +++ b/backends/platform/symbian/S60v3/ScummVM_A0000658_S60v3.mmp.in @@ -91,7 +91,7 @@ STATICLIBRARY esdl.lib // *** Include paths USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\engines -USERINCLUDE ..\..\..\..\backends\fs ..\src ..\..\..\..\backends\platform\sdl ..\..\..\..\sound +USERINCLUDE ..\..\..\..\backends\fs ..\src ..\..\..\..\backends\platform\sdl ..\..\..\..\audio SYSTEMINCLUDE \epoc32\include\ESDL SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version diff --git a/backends/platform/symbian/S60v3/ScummVM_S60v3.mmp.in b/backends/platform/symbian/S60v3/ScummVM_S60v3.mmp.in index 6f14e858b0..9af6535b22 100644 --- a/backends/platform/symbian/S60v3/ScummVM_S60v3.mmp.in +++ b/backends/platform/symbian/S60v3/ScummVM_S60v3.mmp.in @@ -91,7 +91,7 @@ STATICLIBRARY esdl.lib // *** Include paths USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\engines -USERINCLUDE ..\..\..\..\backends\fs ..\src ..\..\..\..\backends\platform\sdl ..\..\..\..\sound +USERINCLUDE ..\..\..\..\backends\fs ..\src ..\..\..\..\backends\platform\sdl ..\..\..\..\audio SYSTEMINCLUDE \epoc32\include\ESDL SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version diff --git a/backends/platform/symbian/mmp/scummvm_agi.mmp.in b/backends/platform/symbian/mmp/scummvm_agi.mmp.in index 044326405d..e2d98b9ae4 100644 --- a/backends/platform/symbian/mmp/scummvm_agi.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_agi.mmp.in @@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\agi // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_agos.mmp.in b/backends/platform/symbian/mmp/scummvm_agos.mmp.in index e2d1e5eff4..077111d783 100644 --- a/backends/platform/symbian/mmp/scummvm_agos.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_agos.mmp.in @@ -65,6 +65,6 @@ SOURCEPATH ..\..\..\..\engines\agos // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_base.mmp.in b/backends/platform/symbian/mmp/scummvm_base.mmp.in index d25cef4ffe..b3bfbab530 100644 --- a/backends/platform/symbian/mmp/scummvm_base.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_base.mmp.in @@ -51,7 +51,7 @@ ALWAYS_BUILD_AS_ARM // *** Include paths -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio USERINCLUDE ..\..\..\..\backends\fs ..\src ..\..\..\..\backends\platform\sdl SYSTEMINCLUDE \epoc32\include\ESDL @@ -95,7 +95,7 @@ SOURCEPATH ..\..\..\..\gui //SOURCE KeysDialog.cpp //SOURCE Actions.cpp -SOURCEPATH ..\..\..\..\sound +SOURCEPATH ..\..\..\..\audio //START_AUTO_OBJECTS_SOUND_// // empty base file, will be updated by Perl build scripts @@ -110,6 +110,12 @@ SOURCE rate_arm.cpp // ARM version: add ASM .cpp wrapper SOURCE rate_arm_asm.s // ARM version: add ASM routines #endif +SOURCEPATH ..\..\..\..\video +//START_AUTO_OBJECTS_VIDEO_// + + // empty base file, will be updated by Perl build scripts + +//STOP_AUTO_OBJECTS_VIDEO_// // add a few files manually, since they are not parsed from modules.mk files SOURCEPATH ..\..\..\.. diff --git a/backends/platform/symbian/mmp/scummvm_cine.mmp.in b/backends/platform/symbian/mmp/scummvm_cine.mmp.in index d114ec554b..2c8118ef13 100644 --- a/backends/platform/symbian/mmp/scummvm_cine.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_cine.mmp.in @@ -59,5 +59,5 @@ SOURCEPATH ..\..\..\..\engines\cine // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_cruise.mmp.in b/backends/platform/symbian/mmp/scummvm_cruise.mmp.in index e4c836a68b..b43a867da3 100644 --- a/backends/platform/symbian/mmp/scummvm_cruise.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_cruise.mmp.in @@ -59,5 +59,5 @@ SOURCEPATH ..\..\..\..\engines\cruise // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_draci.mmp.in b/backends/platform/symbian/mmp/scummvm_draci.mmp.in index b10eb0b5a8..9f24927f27 100644 --- a/backends/platform/symbian/mmp/scummvm_draci.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_draci.mmp.in @@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\draci // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_drascula.mmp.in b/backends/platform/symbian/mmp/scummvm_drascula.mmp.in index 529eeb9e0b..d8cc6da6ae 100644 --- a/backends/platform/symbian/mmp/scummvm_drascula.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_drascula.mmp.in @@ -59,5 +59,5 @@ SOURCEPATH ..\..\..\..\engines\drascula // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_gob.mmp.in b/backends/platform/symbian/mmp/scummvm_gob.mmp.in index fa2ce0dedf..ce94f85283 100644 --- a/backends/platform/symbian/mmp/scummvm_gob.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_gob.mmp.in @@ -59,5 +59,5 @@ SOURCEPATH ..\..\..\..\engines\gob // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_groovie.mmp.in b/backends/platform/symbian/mmp/scummvm_groovie.mmp.in index 7a03cc1745..7229edf4aa 100644 --- a/backends/platform/symbian/mmp/scummvm_groovie.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_groovie.mmp.in @@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\groovie // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_hugo.mmp.in b/backends/platform/symbian/mmp/scummvm_hugo.mmp.in index 301d23cab4..b3f9edd1e6 100644 --- a/backends/platform/symbian/mmp/scummvm_hugo.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_hugo.mmp.in @@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\hugo // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_kyra.mmp.in b/backends/platform/symbian/mmp/scummvm_kyra.mmp.in index 5fa5fdac6a..654632c229 100644 --- a/backends/platform/symbian/mmp/scummvm_kyra.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_kyra.mmp.in @@ -65,5 +65,5 @@ SOURCEPATH ..\..\..\..\engines\kyra // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_lastexpress.mmp.in b/backends/platform/symbian/mmp/scummvm_lastexpress.mmp.in index a297137f02..418730c064 100644 --- a/backends/platform/symbian/mmp/scummvm_lastexpress.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_lastexpress.mmp.in @@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\lastexpress // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_lure.mmp.in b/backends/platform/symbian/mmp/scummvm_lure.mmp.in index c4a3900a05..e1a63b602b 100644 --- a/backends/platform/symbian/mmp/scummvm_lure.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_lure.mmp.in @@ -59,5 +59,5 @@ SOURCEPATH ..\..\..\..\engines\lure // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_m4.mmp.in b/backends/platform/symbian/mmp/scummvm_m4.mmp.in index f4430f2e58..c2e1ce4e8b 100644 --- a/backends/platform/symbian/mmp/scummvm_m4.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_m4.mmp.in @@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\m4 // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_made.mmp.in b/backends/platform/symbian/mmp/scummvm_made.mmp.in index bf50157224..91b9ca756d 100644 --- a/backends/platform/symbian/mmp/scummvm_made.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_made.mmp.in @@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\made // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_mohawk.mmp.in b/backends/platform/symbian/mmp/scummvm_mohawk.mmp.in index 78e931b06f..0edba5eb4d 100644 --- a/backends/platform/symbian/mmp/scummvm_mohawk.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_mohawk.mmp.in @@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\mohawk // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_parallaction.mmp.in b/backends/platform/symbian/mmp/scummvm_parallaction.mmp.in index e96ab907cd..744a756f4e 100644 --- a/backends/platform/symbian/mmp/scummvm_parallaction.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_parallaction.mmp.in @@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\parallaction // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_queen.mmp.in b/backends/platform/symbian/mmp/scummvm_queen.mmp.in index 4f9d9319e0..bf88744701 100644 --- a/backends/platform/symbian/mmp/scummvm_queen.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_queen.mmp.in @@ -59,5 +59,5 @@ SOURCEPATH ..\..\..\..\engines\queen // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_saga.mmp.in b/backends/platform/symbian/mmp/scummvm_saga.mmp.in index e5beefc0c5..a95ee1e7fd 100644 --- a/backends/platform/symbian/mmp/scummvm_saga.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_saga.mmp.in @@ -71,5 +71,5 @@ SOURCEPATH ..\..\..\..\engines\saga // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_sci.mmp.in b/backends/platform/symbian/mmp/scummvm_sci.mmp.in index eb0d1b020f..ca9e712f3f 100644 --- a/backends/platform/symbian/mmp/scummvm_sci.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_sci.mmp.in @@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\sci // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_scumm.mmp.in b/backends/platform/symbian/mmp/scummvm_scumm.mmp.in index 6b5d249422..0a3cd9bb7d 100644 --- a/backends/platform/symbian/mmp/scummvm_scumm.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_scumm.mmp.in @@ -82,7 +82,7 @@ SOURCE smush/codec47ARM.s // ARM version: add ASM routines // *** Include paths USERINCLUDE ..\..\..\..\engines ..\..\..\..\engines\scumm\smush ..\..\..\..\engines\scumm\insane -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version SYSTEMINCLUDE \epoc32\include\libc diff --git a/backends/platform/symbian/mmp/scummvm_sky.mmp.in b/backends/platform/symbian/mmp/scummvm_sky.mmp.in index d1d222b1fa..1bc2301745 100644 --- a/backends/platform/symbian/mmp/scummvm_sky.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_sky.mmp.in @@ -59,5 +59,5 @@ SOURCEPATH ..\..\..\..\engines\sky // *** Include paths USERINCLUDE ..\..\..\..\engines ..\..\..\..\engines\sky\music -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_sword1.mmp.in b/backends/platform/symbian/mmp/scummvm_sword1.mmp.in index 0aaf9d504b..6bf543070b 100644 --- a/backends/platform/symbian/mmp/scummvm_sword1.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_sword1.mmp.in @@ -59,5 +59,5 @@ SOURCEPATH ..\..\..\..\engines\sword1 // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_sword2.mmp.in b/backends/platform/symbian/mmp/scummvm_sword2.mmp.in index 4a7181709f..cee4143f94 100644 --- a/backends/platform/symbian/mmp/scummvm_sword2.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_sword2.mmp.in @@ -59,5 +59,5 @@ SOURCEPATH ..\..\..\..\engines\sword2 // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_teenagent.mmp.in b/backends/platform/symbian/mmp/scummvm_teenagent.mmp.in index 7832fc4880..fa4ef79692 100644 --- a/backends/platform/symbian/mmp/scummvm_teenagent.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_teenagent.mmp.in @@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\teenagent // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_tinsel.mmp.in b/backends/platform/symbian/mmp/scummvm_tinsel.mmp.in index 6190ec8152..569b79ba3c 100644 --- a/backends/platform/symbian/mmp/scummvm_tinsel.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_tinsel.mmp.in @@ -59,5 +59,5 @@ SOURCEPATH ..\..\..\..\engines\tinsel // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_toon.mmp.in b/backends/platform/symbian/mmp/scummvm_toon.mmp.in index f2301c4ae2..f309e6d0fa 100644 --- a/backends/platform/symbian/mmp/scummvm_toon.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_toon.mmp.in @@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\toon // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_touche.mmp.in b/backends/platform/symbian/mmp/scummvm_touche.mmp.in index 9e4c3d0496..ab42afe304 100644 --- a/backends/platform/symbian/mmp/scummvm_touche.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_touche.mmp.in @@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\touche // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/symbian/mmp/scummvm_tucker.mmp.in b/backends/platform/symbian/mmp/scummvm_tucker.mmp.in index d193ac49a9..434072bc96 100644 --- a/backends/platform/symbian/mmp/scummvm_tucker.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_tucker.mmp.in @@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\tucker // *** Include paths USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src +USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src diff --git a/backends/platform/wince/CEActionsPocket.cpp b/backends/platform/wince/CEActionsPocket.cpp index 99d0be2bdc..3895c7d6fa 100644 --- a/backends/platform/wince/CEActionsPocket.cpp +++ b/backends/platform/wince/CEActionsPocket.cpp @@ -38,7 +38,7 @@ #ifdef _WIN32_WCE -#define KEY_ALL_SKIP 3457 +#define KEY_ALL_SKIP 3457 #endif const Common::String pocketActionNames[] = { @@ -85,14 +85,14 @@ int CEActionsPocket::version() { } CEActionsPocket::CEActionsPocket(const Common::String &gameid) : -GUI::Actions() { + GUI::Actions() { int i; _right_click_needed = false; _hide_toolbar_needed = false; _zoom_needed = false; - for (i=0; i<POCKET_ACTION_LAST; i++) { + for (i = 0; i < POCKET_ACTION_LAST; i++) { _action_mapping[i] = 0; _action_enabled[i] = false; } @@ -112,7 +112,7 @@ GUI::Actions() { void CEActionsPocket::initInstanceMain(OSystem *mainSystem) { // Nothing generic to do for Pocket PC - _CESystem = static_cast<OSystem_WINCE3*>(mainSystem); + _CESystem = static_cast<OSystem_WINCE3 *>(mainSystem); GUI_Actions::initInstanceMain(mainSystem); } @@ -126,7 +126,7 @@ void CEActionsPocket::initInstanceGame() { bool is_comi = (strncmp(gameid.c_str(), "comi", 4) == 0); bool is_gob = (strncmp(gameid.c_str(), "gob", 3) == 0); bool is_saga = (gameid == "saga"); - bool is_kyra = (strncmp(gameid.c_str(), "kyra",4) == 0); + bool is_kyra = (strncmp(gameid.c_str(), "kyra", 4) == 0); bool is_samnmax = (gameid == "samnmax"); bool is_cine = (gameid == "cine"); bool is_touche = (gameid == "touche"); @@ -134,7 +134,7 @@ void CEActionsPocket::initInstanceGame() { bool is_parallaction = (gameid == "parallaction"); bool is_lure = (gameid == "lure"); bool is_feeble = (gameid == "feeble"); - bool is_drascula = (strncmp(gameid.c_str(), "drascula",8) == 0); + bool is_drascula = (strncmp(gameid.c_str(), "drascula", 8) == 0); bool is_tucker = (gameid == "tucker"); bool is_groovie = (gameid == "groovie"); bool is_tinsel = (gameid == "tinsel"); @@ -145,7 +145,7 @@ void CEActionsPocket::initInstanceGame() { // See if a right click mapping could be needed if (is_sword1 || is_sword2 || is_sky || is_queen || is_comi || is_gob || is_tinsel || - is_samnmax || is_cine || is_touche || is_parallaction || is_drascula || is_cruise) + is_samnmax || is_cine || is_touche || is_parallaction || is_drascula || is_cruise) _right_click_needed = true; // See if a "hide toolbar" mapping could be needed @@ -187,7 +187,7 @@ void CEActionsPocket::initInstanceGame() { if (!is_cine && !is_parallaction && !is_groovie && !is_cruise && !is_made) _action_enabled[POCKET_ACTION_SKIP] = true; if (is_simon || is_sky || is_sword2 || is_queen || is_sword1 || is_gob || is_tinsel || - is_saga || is_kyra || is_touche || is_lure || is_feeble || is_drascula || is_tucker) + is_saga || is_kyra || is_touche || is_lure || is_feeble || is_drascula || is_tucker) _key_action[POCKET_ACTION_SKIP].setKey(VK_ESCAPE); else _key_action[POCKET_ACTION_SKIP].setKey(KEY_ALL_SKIP); @@ -239,10 +239,12 @@ bool CEActionsPocket::perform(GUI::ActionType action, bool pushed) { if (!pushed) { switch (action) { case POCKET_ACTION_RIGHTCLICK: - _CESystem->add_right_click(false); + //_CESystem->add_right_click(false); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->add_right_click(false); return true; case POCKET_ACTION_LEFTCLICK: - _CESystem->add_left_click(false); + //_CESystem->add_left_click(false); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->add_left_click(false); return true; case POCKET_ACTION_PAUSE: case POCKET_ACTION_SAVE: @@ -272,43 +274,55 @@ bool CEActionsPocket::perform(GUI::ActionType action, bool pushed) { EventsBuffer::simulateKey(&_key_action[action], true); return true; case POCKET_ACTION_KEYBOARD: - _CESystem->swap_panel(); + //_CESystem->swap_panel(); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->swap_panel(); return true; case POCKET_ACTION_HIDE: - _CESystem->swap_panel_visibility(); + //_CESystem->swap_panel_visibility(); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->swap_panel_visibility(); return true; case POCKET_ACTION_SOUND: _CESystem->swap_sound_master(); return true; case POCKET_ACTION_RIGHTCLICK: - _CESystem->add_right_click(true); + //_CESystem->add_right_click(true); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->add_right_click(true); return true; case POCKET_ACTION_CURSOR: - _CESystem->swap_mouse_visibility(); + //_CESystem->swap_mouse_visibility(); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->swap_mouse_visibility(); return true; case POCKET_ACTION_FREELOOK: - _CESystem->swap_freeLook(); + //_CESystem->swap_freeLook(); + ((WINCESdlEventSource *)((OSystem_SDL *)g_system)->getEventManager())->swap_freeLook(); return true; case POCKET_ACTION_ZOOM_UP: - _CESystem->swap_zoom_up(); + //_CESystem->swap_zoom_up(); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->swap_zoom_up(); return true; case POCKET_ACTION_ZOOM_DOWN: - _CESystem->swap_zoom_down(); + //_CESystem->swap_zoom_down(); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->swap_zoom_down(); return true; case POCKET_ACTION_LEFTCLICK: - _CESystem->add_left_click(true); + //_CESystem->add_left_click(true); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->add_left_click(true); return true; case POCKET_ACTION_UP: - _CESystem->move_cursor_up(); + //_CESystem->move_cursor_up(); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->move_cursor_up(); return true; case POCKET_ACTION_DOWN: - _CESystem->move_cursor_down(); + //_CESystem->move_cursor_down(); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->move_cursor_down(); return true; case POCKET_ACTION_LEFT: - _CESystem->move_cursor_left(); + //_CESystem->move_cursor_left(); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->move_cursor_left(); return true; case POCKET_ACTION_RIGHT: - _CESystem->move_cursor_right(); + //_CESystem->move_cursor_right(); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->move_cursor_right(); return true; case POCKET_ACTION_QUIT: if (!quitdialog) { diff --git a/backends/platform/wince/CEActionsSmartphone.cpp b/backends/platform/wince/CEActionsSmartphone.cpp index 5c7feb4950..413c52af2b 100644 --- a/backends/platform/wince/CEActionsSmartphone.cpp +++ b/backends/platform/wince/CEActionsSmartphone.cpp @@ -36,7 +36,7 @@ #include "common/translation.h" -#define KEY_ALL_SKIP 3457 +#define KEY_ALL_SKIP 3457 const String smartphoneActionNames[] = { _s("Up"), @@ -79,10 +79,10 @@ int CEActionsSmartphone::version() { } CEActionsSmartphone::CEActionsSmartphone() -: GUI::Actions() { + : GUI::Actions() { int i; - for (i=0; i<SMARTPHONE_ACTION_LAST; i++) { + for (i = 0; i < SMARTPHONE_ACTION_LAST; i++) { _action_mapping[i] = ACTIONS_SMARTPHONE_DEFAULT[i]; _action_enabled[i] = false; } @@ -90,7 +90,7 @@ CEActionsSmartphone::CEActionsSmartphone() } void CEActionsSmartphone::initInstanceMain(OSystem *mainSystem) { - _CESystem = static_cast<OSystem_WINCE3*>(mainSystem); + _CESystem = static_cast<OSystem_WINCE3 *>(mainSystem); GUI_Actions::initInstanceMain(mainSystem); @@ -117,7 +117,7 @@ void CEActionsSmartphone::initInstanceGame() { bool is_comi = (strncmp(gameid.c_str(), "comi", 4) == 0); bool is_gob = (strncmp(gameid.c_str(), "gob", 3) == 0); bool is_saga = (gameid == "saga"); - bool is_kyra = (strncmp(gameid.c_str(), "kyra",4) == 0); + bool is_kyra = (strncmp(gameid.c_str(), "kyra", 4) == 0); bool is_samnmax = (gameid == "samnmax"); bool is_cine = (gameid == "cine"); bool is_touche = (gameid == "touche"); @@ -125,7 +125,7 @@ void CEActionsSmartphone::initInstanceGame() { bool is_parallaction = (gameid == "parallaction"); bool is_lure = (gameid == "lure"); bool is_feeble = (gameid == "feeble"); - bool is_drascula = (strncmp(gameid.c_str(), "drascula",8) == 0); + bool is_drascula = (strncmp(gameid.c_str(), "drascula", 8) == 0); bool is_tucker = (gameid == "tucker"); bool is_groovie = (gameid == "groovie"); bool is_tinsel = (gameid == "tinsel"); @@ -136,7 +136,7 @@ void CEActionsSmartphone::initInstanceGame() { // See if a right click mapping could be needed if (is_sword1 || is_sword2 || is_sky || is_queen || is_comi || is_gob || is_tinsel || - is_samnmax || is_cine || is_touche || is_parallaction || is_drascula || is_cruise) + is_samnmax || is_cine || is_touche || is_parallaction || is_drascula || is_cruise) _right_click_needed = true; // Initialize keys for different actions @@ -168,8 +168,8 @@ void CEActionsSmartphone::initInstanceGame() { // Skip _action_enabled[SMARTPHONE_ACTION_SKIP] = true; if (is_simon || is_sky || is_sword2 || is_queen || is_sword1 || is_gob || is_tinsel || - is_saga || is_kyra || is_touche || is_lure || is_feeble || is_drascula || is_tucker || - is_groovie || is_cruise || is_made) + is_saga || is_kyra || is_touche || is_lure || is_feeble || is_drascula || is_tucker || + is_groovie || is_cruise || is_made) _key_action[SMARTPHONE_ACTION_SKIP].setKey(VK_ESCAPE); else _key_action[SMARTPHONE_ACTION_SKIP].setKey(KEY_ALL_SKIP); @@ -204,81 +204,92 @@ bool CEActionsSmartphone::perform(GUI::ActionType action, bool pushed) { if (!pushed) { switch (action) { - case SMARTPHONE_ACTION_RIGHTCLICK: - _CESystem->add_right_click(false); - return true; - case SMARTPHONE_ACTION_LEFTCLICK: - _CESystem->add_left_click(false); - return true; - case SMARTPHONE_ACTION_SAVE: - case SMARTPHONE_ACTION_SKIP: - case SMARTPHONE_ACTION_MULTI: - EventsBuffer::simulateKey(&_key_action[action], false); - return true; - } - return false; - } - - switch (action) { - case SMARTPHONE_ACTION_SAVE: - case SMARTPHONE_ACTION_SKIP: - case SMARTPHONE_ACTION_MULTI: - if (action == SMARTPHONE_ACTION_SAVE && ConfMan.get("gameid") == "parallaction") { - // FIXME: This is a temporary solution. The engine should handle its own menus. - // Note that the user can accomplish this via the virtual keyboard as well, this is just for convenience - GUI::MessageDialog alert(_("Do you want to load or save the game?"), _("Load"), _("Save")); - if (alert.runModal() == GUI::kMessageOK) - _key_action[action].setKey(SDLK_l); - else - _key_action[action].setKey(SDLK_s); - } - EventsBuffer::simulateKey(&_key_action[action], true); - return true; case SMARTPHONE_ACTION_RIGHTCLICK: - _CESystem->add_right_click(true); + //_CESystem->add_right_click(false); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->add_right_click(false); return true; case SMARTPHONE_ACTION_LEFTCLICK: - _CESystem->add_left_click(true); - return true; - case SMARTPHONE_ACTION_UP: - _CESystem->move_cursor_up(); - return true; - case SMARTPHONE_ACTION_DOWN: - _CESystem->move_cursor_down(); - return true; - case SMARTPHONE_ACTION_LEFT: - _CESystem->move_cursor_left(); - return true; - case SMARTPHONE_ACTION_RIGHT: - _CESystem->move_cursor_right(); - return true; - case SMARTPHONE_ACTION_ZONE: - _CESystem->switch_zone(); + //_CESystem->add_left_click(false); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->add_left_click(false); return true; - case SMARTPHONE_ACTION_BINDKEYS: - if (!keydialogrunning) { - keydialogrunning = true; - GUI::KeysDialog *keysDialog = new GUI::KeysDialog(); - keysDialog->runModal(); - delete keysDialog; - keydialogrunning = false; - } - return true; - case SMARTPHONE_ACTION_KEYBOARD: - _CESystem->swap_smartphone_keyboard(); - return true; - case SMARTPHONE_ACTION_ROTATE: - _CESystem->smartphone_rotate_display(); - return true; - case SMARTPHONE_ACTION_QUIT: - if (!quitdialog) { - quitdialog = true; - GUI::MessageDialog alert(_(" Are you sure you want to quit ? "), _("Yes"), _("No")); - if (alert.runModal() == GUI::kMessageOK) - _mainSystem->quit(); - quitdialog = false; - } + case SMARTPHONE_ACTION_SAVE: + case SMARTPHONE_ACTION_SKIP: + case SMARTPHONE_ACTION_MULTI: + EventsBuffer::simulateKey(&_key_action[action], false); return true; + } + return false; + } + + switch (action) { + case SMARTPHONE_ACTION_SAVE: + case SMARTPHONE_ACTION_SKIP: + case SMARTPHONE_ACTION_MULTI: + if (action == SMARTPHONE_ACTION_SAVE && ConfMan.get("gameid") == "parallaction") { + // FIXME: This is a temporary solution. The engine should handle its own menus. + // Note that the user can accomplish this via the virtual keyboard as well, this is just for convenience + GUI::MessageDialog alert(_("Do you want to load or save the game?"), _("Load"), _("Save")); + if (alert.runModal() == GUI::kMessageOK) + _key_action[action].setKey(SDLK_l); + else + _key_action[action].setKey(SDLK_s); + } + EventsBuffer::simulateKey(&_key_action[action], true); + return true; + case SMARTPHONE_ACTION_RIGHTCLICK: + //_CESystem->add_right_click(true); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->add_right_click(true); + return true; + case SMARTPHONE_ACTION_LEFTCLICK: + //_CESystem->add_left_click(true); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->add_left_click(true); + return true; + case SMARTPHONE_ACTION_UP: + //_CESystem->move_cursor_up(); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->move_cursor_up(); + return true; + case SMARTPHONE_ACTION_DOWN: + //_CESystem->move_cursor_down(); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->move_cursor_down(); + return true; + case SMARTPHONE_ACTION_LEFT: + //_CESystem->move_cursor_left(); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->move_cursor_left(); + return true; + case SMARTPHONE_ACTION_RIGHT: + //_CESystem->move_cursor_right(); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->move_cursor_right(); + return true; + case SMARTPHONE_ACTION_ZONE: + //_CESystem->switch_zone(); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->switch_zone(); + return true; + case SMARTPHONE_ACTION_BINDKEYS: + if (!keydialogrunning) { + keydialogrunning = true; + GUI::KeysDialog *keysDialog = new GUI::KeysDialog(); + keysDialog->runModal(); + delete keysDialog; + keydialogrunning = false; + } + return true; + case SMARTPHONE_ACTION_KEYBOARD: + //_CESystem->swap_smartphone_keyboard(); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->swap_smartphone_keyboard(); + return true; + case SMARTPHONE_ACTION_ROTATE: + //_CESystem->smartphone_rotate_display(); + ((WINCESdlGraphicsManager *)((OSystem_SDL *)g_system)->getGraphicsManager())->smartphone_rotate_display(); + return true; + case SMARTPHONE_ACTION_QUIT: + if (!quitdialog) { + quitdialog = true; + GUI::MessageDialog alert(_(" Are you sure you want to quit ? "), _("Yes"), _("No")); + if (alert.runModal() == GUI::kMessageOK) + _mainSystem->quit(); + quitdialog = false; + } + return true; } return false; diff --git a/backends/platform/wince/CEDevice.cpp b/backends/platform/wince/CEDevice.cpp index d94ce6cde7..ffe2523b47 100644 --- a/backends/platform/wince/CEDevice.cpp +++ b/backends/platform/wince/CEDevice.cpp @@ -29,22 +29,22 @@ #include "backends/platform/wince/wince-sdl.h" -static void (WINAPI* _SHIdleTimerReset)(void) = NULL; -static HANDLE (WINAPI* _SetPowerRequirement)(PVOID,int,ULONG,PVOID,ULONG) = NULL; -static DWORD (WINAPI* _ReleasePowerRequirement)(HANDLE) = NULL; +static void (WINAPI *_SHIdleTimerReset)(void) = NULL; +static HANDLE(WINAPI *_SetPowerRequirement)(PVOID, int, ULONG, PVOID, ULONG) = NULL; +static DWORD (WINAPI *_ReleasePowerRequirement)(HANDLE) = NULL; static HANDLE _hPowerManagement = NULL; static DWORD _lastTime = 0; static DWORD REG_bat = 0, REG_ac = 0, REG_disp = 0, bat_timeout = 0; static bool REG_tampered = false; #ifdef __GNUC__ extern "C" void WINAPI SystemIdleTimerReset(void); -#define SPI_SETBATTERYIDLETIMEOUT 251 -#define SPI_GETBATTERYIDLETIMEOUT 252 +#define SPI_SETBATTERYIDLETIMEOUT 251 +#define SPI_GETBATTERYIDLETIMEOUT 252 #endif #define TIMER_TRIGGER 9000 -DWORD CEDevice::reg_access(TCHAR *key, TCHAR *val, DWORD data) { +DWORD CEDevice::reg_access(const TCHAR *key, const TCHAR *val, DWORD data) { HKEY regkey; DWORD tmpval, cbdata; @@ -70,7 +70,7 @@ DWORD CEDevice::reg_access(TCHAR *key, TCHAR *val, DWORD data) { void CEDevice::backlight_xchg() { HANDLE h; - REG_bat = reg_access(TEXT("ControlPanel\\BackLight"), TEXT("BatteryTimeout"), REG_bat); + REG_bat = reg_access(TEXT("ControlPanel\\BackLight"), (const TCHAR *)TEXT("BatteryTimeout"), REG_bat); REG_ac = reg_access(TEXT("ControlPanel\\BackLight"), TEXT("ACTimeout"), REG_ac); REG_disp = reg_access(TEXT("ControlPanel\\Power"), TEXT("Display"), REG_disp); @@ -85,20 +85,19 @@ void CEDevice::init() { // 2003+ power management code borrowed from MoDaCo & Betaplayer. Thanks ! HINSTANCE dll = LoadLibrary(TEXT("aygshell.dll")); if (dll) { - *(FARPROC*)&_SHIdleTimerReset = GetProcAddress(dll, MAKEINTRESOURCE(2006)); + _SHIdleTimerReset = (void (*)())GetProcAddress(dll, MAKEINTRESOURCE(2006)); } dll = LoadLibrary(TEXT("coredll.dll")); if (dll) { - *(FARPROC*)&_SetPowerRequirement = GetProcAddress(dll, TEXT("SetPowerRequirement")); - *(FARPROC*)&_ReleasePowerRequirement = GetProcAddress(dll, TEXT("ReleasePowerRequirement")); - + _SetPowerRequirement = (HANDLE (*)(PVOID, int, ULONG, PVOID, ULONG))GetProcAddress(dll, TEXT("SetPowerRequirement")); + _ReleasePowerRequirement = (DWORD (*)(HANDLE))GetProcAddress(dll, TEXT("ReleasePowerRequirement")); } if (_SetPowerRequirement) _hPowerManagement = _SetPowerRequirement((PVOID) TEXT("BKL1:"), 0, 1, (PVOID) NULL, 0); _lastTime = GetTickCount(); // older devices - REG_bat = REG_ac = REG_disp = 2 * 60 * 60 * 1000; // 2hrs should do it + REG_bat = REG_ac = REG_disp = 2 * 60 * 60 * 1000; // 2hrs should do it backlight_xchg(); REG_tampered = true; SystemParametersInfo(SPI_GETBATTERYIDLETIMEOUT, 0, (void *) &bat_timeout, 0); @@ -127,6 +126,10 @@ bool CEDevice::hasSquareQVGAResolution() { return (OSystem_WINCE3::getScreenWidth() == 240 && OSystem_WINCE3::getScreenHeight() == 240); } +bool CEDevice::hasWideResolution() { + return (OSystem_WINCE3::getScreenWidth() >= 640 || OSystem_WINCE3::getScreenHeight() >= 640); +} + bool CEDevice::hasPocketPCResolution() { if (OSystem_WINCE3::isOzone() && hasWideResolution()) return true; @@ -139,10 +142,6 @@ bool CEDevice::hasDesktopResolution() { return (OSystem_WINCE3::getScreenWidth() > 320); } -bool CEDevice::hasWideResolution() { - return (OSystem_WINCE3::getScreenWidth() >= 640 || OSystem_WINCE3::getScreenHeight() >= 640); -} - bool CEDevice::hasSmartphoneResolution() { return (OSystem_WINCE3::getScreenWidth() < 240); } diff --git a/backends/platform/wince/CEDevice.h b/backends/platform/wince/CEDevice.h index b2b20d05ce..24dffca0b3 100644 --- a/backends/platform/wince/CEDevice.h +++ b/backends/platform/wince/CEDevice.h @@ -43,7 +43,7 @@ public: static bool isSmartphone(); private: - static DWORD reg_access(TCHAR *key, TCHAR *val, DWORD data); + static DWORD reg_access(const TCHAR *key, const TCHAR *val, DWORD data); static void backlight_xchg(); }; diff --git a/backends/platform/wince/CEException.cpp b/backends/platform/wince/CEException.cpp index 421db4960c..08a327136f 100644 --- a/backends/platform/wince/CEException.cpp +++ b/backends/platform/wince/CEException.cpp @@ -36,7 +36,7 @@ void CEException::writeBreak(HANDLE file) { int i; memset(tempo, 0, sizeof(tempo)); - for (i=0; i<40; i++) + for (i = 0; i < 40; i++) tempo[i] = '-'; writeString(file, tempo); } @@ -51,23 +51,23 @@ void CEException::dumpContext(HANDLE file, HANDLE hProcess, CONTEXT *context) { writeBreak(file); writeString(file, "Context dump"); sprintf(tempo, "R0=%.8x R1=%.8x R2=%.8x R3=%.8x R4=%.8x", context->R0, context->R1, - context->R2, context->R3, context->R4); + context->R2, context->R3, context->R4); writeString(file, tempo); sprintf(tempo, "R5=%.8x R6=%.8x R7=%.8x R8=%.8x R9=%.8x", context->R5, context->R6, - context->R7, context->R8, context->R9); + context->R7, context->R8, context->R9); writeString(file, tempo); sprintf(tempo, "R10=%.8x R11=%.8x R12=%.8x", context->R10, context->R11, - context->R12); + context->R12); writeString(file, tempo); sprintf(tempo, "Sp=%.8x Lr=%.8x Pc=%.8x Psr=%.8x", context->Sp, context->Lr, - context->Pc, context->Psr); + context->Pc, context->Psr); writeString(file, tempo); writeBreak(file); sprintf(tempo, "Memory dump at %.8x", context->Pc - (sizeof(memoryDump) / 2)); writeString(file, tempo); if (ReadProcessMemory(hProcess, (LPCVOID)(context->Pc - (sizeof(memoryDump) / 2)), memoryDump, sizeof(memoryDump), &size)) { - for (i=0; i<size; i+=8) { + for (i = 0; i < size; i += 8) { int j; char digit[4]; int max; @@ -75,7 +75,7 @@ void CEException::dumpContext(HANDLE file, HANDLE hProcess, CONTEXT *context) { if (max > 8) max = 8; tempo[0] = '\0'; - for (j=0; j<max; j++) { + for (j = 0; j < max; j++) { sprintf(digit, "%.2x ", memoryDump[i + j]); strcat(tempo, digit); } @@ -121,10 +121,10 @@ void CEException::dumpException(HANDLE file, EXCEPTION_RECORD *exceptionRecord) break; } sprintf(tempo, "Exception %s Flags %.8x Address %.8x", exceptionName, exceptionRecord->ExceptionFlags, - exceptionRecord->ExceptionAddress); + exceptionRecord->ExceptionAddress); writeString(file, tempo); if (exceptionRecord->NumberParameters) { - for (i=0; i<exceptionRecord->NumberParameters; i++) { + for (i = 0; i < exceptionRecord->NumberParameters; i++) { sprintf(tempo, "Parameter %d %.8x", i, exceptionRecord->ExceptionInformation[i]); writeString(file, tempo); } @@ -144,8 +144,8 @@ bool CEException::writeException(TCHAR *path, EXCEPTION_POINTERS *exceptionPoint GetSystemTime(&systemTime); wsprintf(dumpFileName, TEXT("%s_%.2d_%.2d_%.4d_%.2d_%.2d_%.2d.txt"), - path, systemTime.wDay, systemTime.wMonth, systemTime.wYear, - systemTime.wHour, systemTime.wMinute, systemTime.wSecond); + path, systemTime.wDay, systemTime.wMonth, systemTime.wYear, + systemTime.wHour, systemTime.wMinute, systemTime.wSecond); dumpFile = CreateFile(dumpFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (dumpFile == INVALID_HANDLE_VALUE) return false; diff --git a/backends/platform/wince/CELauncherDialog.cpp b/backends/platform/wince/CELauncherDialog.cpp index de1bb2f1f6..8824af732f 100644 --- a/backends/platform/wince/CELauncherDialog.cpp +++ b/backends/platform/wince/CELauncherDialog.cpp @@ -48,14 +48,14 @@ using namespace Common; class CEAboutDialog : public Dialog { public: CEAboutDialog() - : Dialog(10, 60, 300, 77) { + : Dialog(10, 60, 300, 77) { char tempo[100]; // FIXME: Fingolfin asks: why is there a FIXME here? Please either clarify what // needs fixing, or remove it! const int buttonWidth = g_gui.xmlEval()->getVar("Globals.Button.Width", 0); const int buttonHeight = g_gui.xmlEval()->getVar("Globals.Button.Height", 0); - new ButtonWidget(this, (_w - buttonWidth) / 2, 45, buttonWidth, buttonHeight, _("OK"), 0, kCloseCmd, '\r'); // Close dialog - FIXME + new ButtonWidget(this, (_w - buttonWidth) / 2, 45, buttonWidth, buttonHeight, _("OK"), 0, kCloseCmd, '\r'); // Close dialog - FIXME Common::String videoDriver(_("Using SDL driver ")); SDL_VideoDriverName(tempo, sizeof(tempo)); @@ -106,14 +106,13 @@ void CELauncherDialog::addGame() { MessageDialog alert(_("Do you want to perform an automatic scan ?"), _("Yes"), _("No")); if (alert.runModal() == kMessageOK && _browser->runModal() > 0) { // Clear existing domains - ConfigManager::DomainMap &domains = (ConfigManager::DomainMap&)ConfMan.getGameDomains(); + ConfigManager::DomainMap &domains = (ConfigManager::DomainMap &)ConfMan.getGameDomains(); domains.clear(); ConfMan.flushToDisk(); automaticScanDirectory(_browser->getResult()); ConfMan.flushToDisk(); updateListing(); draw(); - } - else + } else GUILauncherDialog::addGame(); } diff --git a/backends/platform/wince/CEgui/GUIElement.cpp b/backends/platform/wince/CEgui/GUIElement.cpp index dd463c22a1..e7ffc8d473 100644 --- a/backends/platform/wince/CEgui/GUIElement.cpp +++ b/backends/platform/wince/CEgui/GUIElement.cpp @@ -32,7 +32,7 @@ namespace CEGUI { GUIElement::GUIElement(int x, int y, int width, int height) : -_background(0), _drawn(false), _visible(true), _x(x), _y(y), _width(width), _height(height) { + _background(0), _drawn(false), _visible(true), _x(x), _y(y), _width(width), _height(height) { } bool GUIElement::setBackground(WORD backgroundReference) { @@ -45,9 +45,7 @@ bool GUIElement::setBackground(WORD backgroundReference) { if (!_height && !_width) { _height = _background->height(); _width = _background->width(); - } - else - if (_background->height() != _height || _background->width() != _width) { + } else if (_background->height() != _height || _background->width() != _width) { delete _background; _background = NULL; return false; @@ -74,8 +72,7 @@ bool GUIElement::draw(SDL_Surface *surface) { _drawn = true; return true; - } - else + } else return false; } diff --git a/backends/platform/wince/CEgui/ItemAction.cpp b/backends/platform/wince/CEgui/ItemAction.cpp index 55805744e6..efbc43ce00 100644 --- a/backends/platform/wince/CEgui/ItemAction.cpp +++ b/backends/platform/wince/CEgui/ItemAction.cpp @@ -28,7 +28,7 @@ namespace CEGUI { ItemAction::ItemAction(WORD reference, GUI::ActionType action) : -PanelItem(reference) { + PanelItem(reference) { _action = action; if (!GUI::Actions::Instance()->isEnabled(_action)) _visible = false; diff --git a/backends/platform/wince/CEgui/ItemSwitch.cpp b/backends/platform/wince/CEgui/ItemSwitch.cpp index d4648f7556..8eb62bf365 100644 --- a/backends/platform/wince/CEgui/ItemSwitch.cpp +++ b/backends/platform/wince/CEgui/ItemSwitch.cpp @@ -40,7 +40,7 @@ void ItemSwitch::init(WORD referenceTrue, WORD referenceFalse) { } ItemSwitch::ItemSwitch(WORD referenceTrue, WORD referenceFalse, bool *item) : -PanelItem(referenceTrue) { + PanelItem(referenceTrue) { init(referenceTrue, referenceFalse); _item = item; _itemmax = -1; @@ -49,8 +49,8 @@ PanelItem(referenceTrue) { } ItemSwitch::ItemSwitch(WORD referenceTrue, WORD referenceFalse, int *item, int max) : -PanelItem(referenceTrue) { - init(referenceTrue, referenceFalse); + PanelItem(referenceTrue) { + init(referenceTrue, referenceFalse); _itemmultiple = item; _itemmax = max; if (!*item) diff --git a/backends/platform/wince/CEgui/Panel.cpp b/backends/platform/wince/CEgui/Panel.cpp index dfdd6526be..576da23029 100644 --- a/backends/platform/wince/CEgui/Panel.cpp +++ b/backends/platform/wince/CEgui/Panel.cpp @@ -34,7 +34,7 @@ Panel::Panel(int interleave_first, int interleave) : Toolbar() { bool Panel::add(const String &name, const PanelItem *item) { - _itemsMap[name] = (PanelItem*)item; + _itemsMap[name] = (PanelItem *)item; _itemsMap[name]->move(_currentItem, _y + 10); _itemsMap[name]->setPanel(this); _currentItem += _interleave; @@ -47,11 +47,10 @@ bool Panel::draw(SDL_Surface *surface) { if (!_drawn && _visible) { GUIElement::draw(surface); for (iterator = _itemsMap.begin(); iterator != _itemsMap.end(); ++iterator) { - ((GUIElement*)(iterator->_value))->draw(surface); + ((GUIElement *)(iterator->_value))->draw(surface); } return true; - } - else + } else return false; } @@ -59,7 +58,7 @@ void Panel::forceRedraw() { ItemMap::const_iterator iterator; GUIElement::forceRedraw(); for (iterator = _itemsMap.begin(); iterator != _itemsMap.end(); ++iterator) - ((GUIElement*)(iterator->_value))->forceRedraw(); + ((GUIElement *)(iterator->_value))->forceRedraw(); } bool Panel::action(int x, int y, bool pushed) { @@ -69,7 +68,7 @@ bool Panel::action(int x, int y, bool pushed) { return false; for (iterator = _itemsMap.begin(); !result && iterator != _itemsMap.end(); ++iterator) - result = ((GUIElement*)(iterator->_value))->action(x, y, pushed); + result = ((GUIElement *)(iterator->_value))->action(x, y, pushed); return result; } diff --git a/backends/platform/wince/CEgui/Panel.h b/backends/platform/wince/CEgui/Panel.h index e6b693360d..1a8a580dba 100644 --- a/backends/platform/wince/CEgui/Panel.h +++ b/backends/platform/wince/CEgui/Panel.h @@ -51,7 +51,7 @@ public: virtual bool action(int x, int y, bool pushed); private: - typedef HashMap<String, PanelItem*, Common::IgnoreCase_Hash , Common::IgnoreCase_EqualTo> ItemMap; + typedef HashMap<String, PanelItem *, Common::IgnoreCase_Hash , Common::IgnoreCase_EqualTo> ItemMap; ItemMap _itemsMap; int _interleave; diff --git a/backends/platform/wince/CEgui/PanelItem.h b/backends/platform/wince/CEgui/PanelItem.h index 14b62f0f20..9968aa8d5e 100644 --- a/backends/platform/wince/CEgui/PanelItem.h +++ b/backends/platform/wince/CEgui/PanelItem.h @@ -36,7 +36,7 @@ namespace CEGUI { class Panel; class PanelItem : public GUIElement { -friend class Panel; + friend class Panel; public: PanelItem(WORD reference); virtual ~PanelItem(); diff --git a/backends/platform/wince/CEgui/PanelKeyboard.cpp b/backends/platform/wince/CEgui/PanelKeyboard.cpp index 5ca125898d..3512b34da3 100644 --- a/backends/platform/wince/CEgui/PanelKeyboard.cpp +++ b/backends/platform/wince/CEgui/PanelKeyboard.cpp @@ -30,8 +30,9 @@ namespace CEGUI { const char KEYBOARD_MAPPING_ALPHA[][14] = { {"abcdefghijklm"}, {"nopqrstuvwxyz"} }; const char KEYBOARD_MAPPING_NUMERIC[][6] = { {"12345"}, {"67890"} }; -const int KEYBOARD_MAPPING_SPECIAL[][3][2] = { { {1,SDLK_ESCAPE}, {224,SDLK_UP}, {32,SDLK_SPACE} }, - { {224,SDLK_LEFT}, {224,SDLK_DOWN}, {224,SDLK_RIGHT} } }; +const int KEYBOARD_MAPPING_SPECIAL[][3][2] = { { {1, SDLK_ESCAPE}, {224, SDLK_UP}, {32, SDLK_SPACE} }, + { {224, SDLK_LEFT}, {224, SDLK_DOWN}, {224, SDLK_RIGHT} } +}; PanelKeyboard::PanelKeyboard(WORD reference) : Toolbar() { setBackground(reference); @@ -51,21 +52,23 @@ bool PanelKeyboard::action(int x, int y, bool pushed) { int keyCode = 0; if (x < 185) { // Alpha selection - keyCode = keyAscii = KEYBOARD_MAPPING_ALPHA[y >= _y+20][((x + 10) / 14) - 1]; + keyCode = keyAscii = KEYBOARD_MAPPING_ALPHA[y >= _y + 20][((x + 10) / 14) - 1]; } else if (x >= 186 && x <= 255) { // Numeric selection - keyCode = keyAscii = KEYBOARD_MAPPING_NUMERIC[y >= _y+20][((x - 187 + 10) / 14) - 1]; + keyCode = keyAscii = KEYBOARD_MAPPING_NUMERIC[y >= _y + 20][((x - 187 + 10) / 14) - 1]; } else if (x >= 258 && x <= 300) { // Special keys - keyAscii = KEYBOARD_MAPPING_SPECIAL[y >= _y+20][((x - 259 + 10) / 14) - 1][0]; - keyCode = KEYBOARD_MAPPING_SPECIAL[y >= _y+20][((x - 259 + 10) / 14) - 1][1]; + keyAscii = KEYBOARD_MAPPING_SPECIAL[y >= _y + 20][((x - 259 + 10) / 14) - 1][0]; + keyCode = KEYBOARD_MAPPING_SPECIAL[y >= _y + 20][((x - 259 + 10) / 14) - 1][1]; } else if (x >= 302 && x <= 316) { - if (y < _y +20) { + if (y < _y + 20) { // Backspace - keyAscii = VK_BACK; keyCode = keyAscii; + keyAscii = VK_BACK; + keyCode = keyAscii; } else { // Enter - keyAscii = 13; keyCode = 13; + keyAscii = 13; + keyCode = 13; } } diff --git a/backends/platform/wince/CEgui/SDL_ImageResource.cpp b/backends/platform/wince/CEgui/SDL_ImageResource.cpp index 8dad5f0a0c..872b94a442 100644 --- a/backends/platform/wince/CEgui/SDL_ImageResource.cpp +++ b/backends/platform/wince/CEgui/SDL_ImageResource.cpp @@ -32,7 +32,7 @@ SDL_ImageResource::SDL_ImageResource() : _surface(0) { } -SDL_Surface* SDL_ImageResource::load(WORD resourceID) { +SDL_Surface *SDL_ImageResource::load(WORD resourceID) { HRSRC resource; HGLOBAL resourceGlobal; LPVOID resourcePointer; @@ -62,7 +62,7 @@ SDL_Surface* SDL_ImageResource::load(WORD resourceID) { return _surface; } -SDL_Surface* SDL_ImageResource::get() { +SDL_Surface *SDL_ImageResource::get() { return _surface; } diff --git a/backends/platform/wince/CEgui/SDL_ImageResource.h b/backends/platform/wince/CEgui/SDL_ImageResource.h index 5affd5c33c..397ced1434 100644 --- a/backends/platform/wince/CEgui/SDL_ImageResource.h +++ b/backends/platform/wince/CEgui/SDL_ImageResource.h @@ -36,8 +36,8 @@ namespace CEGUI { class SDL_ImageResource { public: SDL_ImageResource(); - SDL_Surface* load(WORD resourceID); - SDL_Surface* get(); + SDL_Surface *load(WORD resourceID); + SDL_Surface *get(); int height(); int width(); virtual ~SDL_ImageResource(); diff --git a/backends/platform/wince/CEgui/ToolbarHandler.cpp b/backends/platform/wince/CEgui/ToolbarHandler.cpp index 78f69119c3..ed2a72245a 100644 --- a/backends/platform/wince/CEgui/ToolbarHandler.cpp +++ b/backends/platform/wince/CEgui/ToolbarHandler.cpp @@ -29,15 +29,15 @@ namespace CEGUI { ToolbarHandler::ToolbarHandler(): -_current(""), _active(NULL) { + _current(""), _active(NULL) { } bool ToolbarHandler::add(const String &name, const Toolbar &toolbar) { - _toolbarMap[name] = (Toolbar*)&toolbar; + _toolbarMap[name] = (Toolbar *)&toolbar; if (!_active) { - _active = &((Toolbar&)toolbar); + _active = &((Toolbar &)toolbar); _current = name; } @@ -53,7 +53,7 @@ bool ToolbarHandler::setActive(const String &name) { return false; if (_current == name) return true; - _active->action(0, 0, false); // make sure any items are unpushed when changing toolbars (e.g. forced VK->main panel) + _active->action(0, 0, false); // make sure any items are unpushed when changing toolbars (e.g. forced VK->main panel) _current = name; _active = _toolbarMap[name]; _active->forceRedraw(); @@ -67,8 +67,7 @@ bool ToolbarHandler::action(int x, int y, bool pushed) { return _active->action(x / 2, (y - _offset) / 2, pushed); else return _active->action(x, y - _offset, pushed); - } - else + } else return false; } @@ -118,7 +117,7 @@ int ToolbarHandler::getOffset() { return _offset; } -Toolbar* ToolbarHandler::active() { +Toolbar *ToolbarHandler::active() { return _active; } diff --git a/backends/platform/wince/CEgui/ToolbarHandler.h b/backends/platform/wince/CEgui/ToolbarHandler.h index e3bf590768..0f794d7d61 100644 --- a/backends/platform/wince/CEgui/ToolbarHandler.h +++ b/backends/platform/wince/CEgui/ToolbarHandler.h @@ -57,7 +57,7 @@ public: virtual ~ToolbarHandler(); private: - HashMap<String, Toolbar*, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _toolbarMap; + HashMap<String, Toolbar *, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _toolbarMap; String _current; Toolbar *_active; int _offset; diff --git a/backends/platform/wince/CEkeys/EventsBuffer.cpp b/backends/platform/wince/CEkeys/EventsBuffer.cpp index c9f4af2304..beea272d2b 100644 --- a/backends/platform/wince/CEkeys/EventsBuffer.cpp +++ b/backends/platform/wince/CEkeys/EventsBuffer.cpp @@ -29,55 +29,55 @@ namespace CEKEYS { - bool EventsBuffer::simulateKey(GUI::Key *key, bool pushed) { - SDL_Event ev = {0}; - - if (!key->keycode()) - key->setKey(key->ascii(), key->ascii()); - else if (!key->ascii()) - key->setKey(key->keycode()); - - ev.type = (pushed ? SDL_KEYDOWN : SDL_KEYUP); - ev.key.keysym.unicode = (SDLMod)key->flags(); // HACK: put the flags into the unused unicode field - ev.key.keysym.sym = (SDLKey)key->keycode(); - ev.key.keysym.mod = KMOD_RESERVED; - return (SDL_PushEvent(&ev) == 0); - } - - bool EventsBuffer::simulateMouseMove(int x, int y) { - SDL_Event ev = {0}; - - ev.type = SDL_MOUSEMOTION; - ev.motion.x = x; - ev.motion.y = y; - return (SDL_PushEvent(&ev) == 0); - } - - bool EventsBuffer::simulateMouseLeftClick(int x, int y, bool pushed) { - SDL_Event ev = {0}; - static bool state = false; - - if (pushed == state) return 0; - state = pushed; - ev.type = (pushed ? SDL_MOUSEBUTTONDOWN : SDL_MOUSEBUTTONUP); - ev.button.button = SDL_BUTTON_LEFT; - ev.button.x = x; - ev.button.y = y; - return (SDL_PushEvent(&ev) == 0); - } - - bool EventsBuffer::simulateMouseRightClick(int x, int y, bool pushed) { - SDL_Event ev = {0}; - static bool state = false; - - if (pushed == state) return 0; - state = pushed; - ev.type = (pushed ? SDL_MOUSEBUTTONDOWN : SDL_MOUSEBUTTONUP); - ev.button.button = SDL_BUTTON_RIGHT; - ev.button.x = x; - ev.button.y = y; - return (SDL_PushEvent(&ev) == 0); - } +bool EventsBuffer::simulateKey(GUI::Key *key, bool pushed) { + SDL_Event ev = {0}; + + if (!key->keycode()) + key->setKey(key->ascii(), key->ascii()); + else if (!key->ascii()) + key->setKey(key->keycode()); + + ev.type = (pushed ? SDL_KEYDOWN : SDL_KEYUP); + ev.key.keysym.unicode = (SDLMod)key->flags(); // HACK: put the flags into the unused unicode field + ev.key.keysym.sym = (SDLKey)key->keycode(); + ev.key.keysym.mod = KMOD_RESERVED; + return (SDL_PushEvent(&ev) == 0); +} + +bool EventsBuffer::simulateMouseMove(int x, int y) { + SDL_Event ev = {0}; + + ev.type = SDL_MOUSEMOTION; + ev.motion.x = x; + ev.motion.y = y; + return (SDL_PushEvent(&ev) == 0); +} + +bool EventsBuffer::simulateMouseLeftClick(int x, int y, bool pushed) { + SDL_Event ev = {0}; + static bool state = false; + + if (pushed == state) return 0; + state = pushed; + ev.type = (pushed ? SDL_MOUSEBUTTONDOWN : SDL_MOUSEBUTTONUP); + ev.button.button = SDL_BUTTON_LEFT; + ev.button.x = x; + ev.button.y = y; + return (SDL_PushEvent(&ev) == 0); +} + +bool EventsBuffer::simulateMouseRightClick(int x, int y, bool pushed) { + SDL_Event ev = {0}; + static bool state = false; + + if (pushed == state) return 0; + state = pushed; + ev.type = (pushed ? SDL_MOUSEBUTTONDOWN : SDL_MOUSEBUTTONUP); + ev.button.button = SDL_BUTTON_RIGHT; + ev.button.x = x; + ev.button.y = y; + return (SDL_PushEvent(&ev) == 0); +} } diff --git a/backends/platform/wince/CEkeys/EventsBuffer.h b/backends/platform/wince/CEkeys/EventsBuffer.h index 22590db03c..9b766c6ca9 100644 --- a/backends/platform/wince/CEkeys/EventsBuffer.h +++ b/backends/platform/wince/CEkeys/EventsBuffer.h @@ -34,14 +34,14 @@ namespace CEKEYS { - class EventsBuffer { - public: - static bool simulateKey(GUI::Key *key, bool pushed); - static bool simulateMouseMove(int x, int y); - static bool simulateMouseLeftClick(int x, int y, bool pushed); - static bool simulateMouseRightClick(int x, int y, bool pushed); - - }; +class EventsBuffer { +public: + static bool simulateKey(GUI::Key *key, bool pushed); + static bool simulateMouseMove(int x, int y); + static bool simulateMouseLeftClick(int x, int y, bool pushed); + static bool simulateMouseRightClick(int x, int y, bool pushed); + +}; } #endif diff --git a/backends/platform/wince/Makefile b/backends/platform/wince/Makefile index 8ad134648b..21bff06f95 100644 --- a/backends/platform/wince/Makefile +++ b/backends/platform/wince/Makefile @@ -113,7 +113,7 @@ INCLUDES := -I$(srcdir) -I. -I$(srcdir)/engines -Imissing/gcc -Ilibs/include -Il CFLAGS := ifndef UNOPTIMIZED_BUILD -CFLAGS += -O3 -march=armv4 -mtune=xscale +CFLAGS += -O3 -fno-inline-functions -march=armv4 -mtune=xscale endif LDFLAGS := -Wl,-Map,scummvm.exe.map -Wl,--stack,65536 diff --git a/backends/platform/wince/missing/io.h b/backends/platform/wince/missing/io.h index 8c66e9405b..96bc6a9ea1 100644 --- a/backends/platform/wince/missing/io.h +++ b/backends/platform/wince/missing/io.h @@ -5,9 +5,9 @@ #define strdup _strdup #ifndef _FILE_DEFINED - typedef void FILE; - #define _FILE_DEFINED +typedef void FILE; +#define _FILE_DEFINED #endif -FILE* wce_fopen(const char* fname, const char* fmode); +FILE *wce_fopen(const char *fname, const char *fmode); #define fopen wce_fopen diff --git a/backends/platform/wince/missing/missing.cpp b/backends/platform/wince/missing/missing.cpp index 92af3e6961..c855247ae1 100644 --- a/backends/platform/wince/missing/missing.cpp +++ b/backends/platform/wince/missing/missing.cpp @@ -50,7 +50,7 @@ char *strdup(const char *strSource); // common missing functions required by both gcc and evc void *bsearch(const void *key, const void *base, size_t nmemb, - size_t size, int (*compar)(const void *, const void *)) { + size_t size, int (*compar)(const void *, const void *)) { // Perform binary search size_t lo = 0; size_t hi = nmemb; @@ -63,17 +63,17 @@ void *bsearch(const void *key, const void *base, size_t nmemb, else if (tmp > 0) lo = mid + 1; else - return (void *)p; + return const_cast<void *>(p); } return NULL; } -static char cwd[MAX_PATH+1] = ""; +static char cwd[MAX_PATH + 1] = ""; EXT_C char *getcwd(char *buffer, int maxlen) { - TCHAR fileUnc[MAX_PATH+1]; - char* plast; + TCHAR fileUnc[MAX_PATH + 1]; + char *plast; if (cwd[0] == 0) { GetModuleFileName(NULL, fileUnc, MAX_PATH); @@ -94,7 +94,7 @@ EXT_C char *getcwd(char *buffer, int maxlen) { #undef GetCurrentDirectory #endif EXT_C void GetCurrentDirectory(int len, char *buf) { - getcwd(buf,len); + getcwd(buf, len); } /* @@ -103,8 +103,8 @@ fully qualified paths refer to root folder rather than current folder (concept not implemented in CE). */ #undef fopen -EXT_C FILE *wce_fopen(const char* fname, const char* fmode) { - char fullname[MAX_PATH+1]; +EXT_C FILE *wce_fopen(const char *fname, const char *fmode) { + char fullname[MAX_PATH + 1]; if (!fname || fname[0] == '\0') return NULL; @@ -118,8 +118,8 @@ EXT_C FILE *wce_fopen(const char* fname, const char* fmode) { } /* Remove file by name */ -int remove(const char* path) { - TCHAR pathUnc[MAX_PATH+1]; +int remove(const char *path) { + TCHAR pathUnc[MAX_PATH + 1]; MultiByteToWideChar(CP_ACP, 0, path, -1, pathUnc, MAX_PATH); return !DeleteFile(pathUnc); } @@ -128,15 +128,15 @@ int remove(const char* path) { /* check out file access permissions */ int _access(const char *path, int mode) { TCHAR fname[MAX_PATH]; - char fullname[MAX_PATH+1]; + char fullname[MAX_PATH + 1]; if (path[0] != '\\' && path[0] != '/') { getcwd(fullname, MAX_PATH); strcat(fullname, "\\"); strcat(fullname, path); - MultiByteToWideChar(CP_ACP, 0, fullname, -1, fname, sizeof(fname)/sizeof(TCHAR)); + MultiByteToWideChar(CP_ACP, 0, fullname, -1, fname, sizeof(fname) / sizeof(TCHAR)); } else - MultiByteToWideChar(CP_ACP, 0, path, -1, fname, sizeof(fname)/sizeof(TCHAR)); + MultiByteToWideChar(CP_ACP, 0, path, -1, fname, sizeof(fname) / sizeof(TCHAR)); WIN32_FIND_DATA ffd; HANDLE h = FindFirstFile(fname, &ffd); @@ -144,10 +144,10 @@ int _access(const char *path, int mode) { if (h == INVALID_HANDLE_VALUE) { // WORKAROUND: WinCE 3.0 doesn't find paths ending in '\' - if (path[strlen(path)-1] == '\\') { + if (path[strlen(path) - 1] == '\\') { char p2[MAX_PATH]; - strncpy(p2, path, strlen(path)-1); - p2[strlen(path) - 1]= '\0'; + strncpy(p2, path, strlen(path) - 1); + p2[strlen(path) - 1] = '\0'; return _access(p2, mode); } else return -1; //Can't find file @@ -158,7 +158,7 @@ int _access(const char *path, int mode) { // hits for files that don't exist. TRIPLE checking for the same fname // seems to weed out those false positives. // Exhibited in kyra engine. - HANDLE h = FindFirstFile(fname, &ffd); + h = FindFirstFile(fname, &ffd); FindClose(h); if (h == INVALID_HANDLE_VALUE) return -1; //Can't find file @@ -170,13 +170,13 @@ int _access(const char *path, int mode) { return 0; //Always return success if target is directory and exists } switch (mode) { - case 00: //Check existence - return 0; - case 06: //Check Read & Write permission - case 02: //Check Write permission - return ffd.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? -1 : 0; - case 04: //Check Read permission - return 0; //Assume always have read permission + case 00: //Check existence + return 0; + case 06: //Check Read & Write permission + case 02: //Check Write permission + return ffd.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? -1 : 0; + case 04: //Check Read permission + return 0; //Assume always have read permission } //Bad mode value supplied, return failure return -1; @@ -188,7 +188,7 @@ int _access(const char *path, int mode) { char *strdup(const char *strSource) { char *buffer; size_z len = strlen(strSource) + 1; - buffer = (char*)malloc(len); + buffer = (char *)malloc(len); if (buffer) memcpy(buffer, strSource, len); return buffer; @@ -199,25 +199,25 @@ char *strdup(const char *strSource) { #ifndef __MINGW32CE__ int islower(int c) { - return (c>='a' && c<='z'); + return (c >= 'a' && c <= 'z'); } int isspace(int c) { - return (c==' ' || c=='\f' || c=='\n' || c=='\r' || c=='\t' || c=='\v'); + return (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v'); } int isalpha(int c) { - return ((c>='a' && c<='z') || (c>='A' && c<='Z')); + return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); } int isalnum(int c) { - return ((c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9')); + return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')); } int isprint(int c) { //static const char punct[] = "!\"#%&'();<=>?[\\]*+,-./:^_{|}~"; //return (isalnum(c) || strchr(punct, c)); - return (32 <= c && c <= 126); // based on BSD manpage + return (32 <= c && c <= 126); // based on BSD manpage } #endif diff --git a/backends/platform/wince/missing/time.h b/backends/platform/wince/missing/time.h index f2bc5e4f89..a0ba6c246e 100644 --- a/backends/platform/wince/missing/time.h +++ b/backends/platform/wince/missing/time.h @@ -6,8 +6,7 @@ #include <stdlib.h> #ifndef __MINGW32CE__ -struct tm -{ +struct tm { short tm_year; short tm_mon; short tm_mday; @@ -23,8 +22,8 @@ struct tm #define EXT_C #endif -EXT_C time_t time(time_t* dummy); -EXT_C struct tm* localtime(time_t* dummy); +EXT_C time_t time(time_t *dummy); +EXT_C struct tm *localtime(time_t *dummy); unsigned int clock(); diff --git a/backends/platform/wince/portdefs.h b/backends/platform/wince/portdefs.h index cbf2006be2..8ad643946f 100644 --- a/backends/platform/wince/portdefs.h +++ b/backends/platform/wince/portdefs.h @@ -34,10 +34,10 @@ int isprint(int c); int isspace(int c); char *strrchr(const char *s, int c); char *strdup(const char *s); -int _stricmp( const char *string1, const char *string2 ); -int stricmp( const char *string1, const char *string2 ); -void assert( void* expression ); -void assert( int expression ); +int _stricmp(const char *string1, const char *string2); +int stricmp(const char *string1, const char *string2); +void assert(void *expression); +void assert(int expression); long int strtol(const char *nptr, char **endptr, int base); char *_strdup(const char *s); char *strpbrk(const char *s, const char *accept); @@ -47,20 +47,20 @@ char *strpbrk(const char *s, const char *accept); #ifdef _WIN32_WCE #ifndef __GNUC__ - void *bsearch(const void *, const void *, size_t, size_t, int (*x) (const void *, const void *)); - char *getcwd(char *buf, int size); - typedef int ptrdiff_t; - void GetCurrentDirectory(int len, char *buf); - #define INVALID_FILE_ATTRIBUTES 0xffffffff +void *bsearch(const void *, const void *, size_t, size_t, int (*x)(const void *, const void *)); +char *getcwd(char *buf, int size); +typedef int ptrdiff_t; +void GetCurrentDirectory(int len, char *buf); +#define INVALID_FILE_ATTRIBUTES 0xffffffff #else - #include <math.h> - #undef GetCurrentDirectory - extern "C" void GetCurrentDirectory(int len, char *buf); - #define stricmp _stricmp - #define strnicmp _strnicmp - #define snprintf _snprintf - #define strdup _strdup - #define fopen wce_fopen +#include <math.h> +#undef GetCurrentDirectory +extern "C" void GetCurrentDirectory(int len, char *buf); +#define stricmp _stricmp +#define strnicmp _strnicmp +#define snprintf _snprintf +#define strdup _strdup +#define fopen wce_fopen #endif #include <windows.h> @@ -75,12 +75,12 @@ char *strpbrk(const char *s, const char *accept); //#include <direct.h> #ifdef __MINGW32CE__ - void *bsearch(const void *, const void *, size_t, size_t, int (*x) (const void *, const void *)); +void *bsearch(const void *, const void *, size_t, size_t, int (*x)(const void *, const void *)); #endif int remove(const char *path); int _access(const char *path, int mode); -void drawError(char*); +void drawError(char *); #define vsnprintf _vsnprintf diff --git a/backends/platform/wince/wince-sdl.cpp b/backends/platform/wince/wince-sdl.cpp index ade90b2dfb..b829686cb0 100644 --- a/backends/platform/wince/wince-sdl.cpp +++ b/backends/platform/wince/wince-sdl.cpp @@ -23,6 +23,7 @@ * */ + // Disable symbol overrides so that we can use system headers. #define FORBIDDEN_SYMBOL_ALLOW_ALL @@ -43,13 +44,12 @@ #include "audio/mixer_intern.h" #include "audio/fmopl.h" -#include "backends/timer/default/default-timer.h" +#include "backends/timer/sdl/sdl-timer.h" #include "gui/Actions.h" #include "gui/KeysDialog.h" #include "gui/message.h" -#include "backends/platform/wince/resource.h" #include "backends/platform/wince/CEActionsPocket.h" #include "backends/platform/wince/CEActionsSmartphone.h" #include "backends/platform/wince/CEgui/ItemAction.h" @@ -60,39 +60,22 @@ #include "backends/platform/wince/CEException.h" #include "backends/platform/wince/CEScaler.h" -#ifdef USE_VORBIS -#ifndef USE_TREMOR -#include <vorbis/vorbisfile.h> -#else -#include <tremor/ivorbisfile.h> -#endif -#endif +#include "backends/graphics/wincesdl/wincesdl-graphics.h" +#include "backends/events/wincesdl/wincesdl-events.h" +#include "backends/mixer/wincesdl/wincesdl-mixer.h" #ifdef DYNAMIC_MODULES #include "backends/plugins/win32/win32-provider.h" #endif #ifdef __GNUC__ -extern "C" _CRTIMP FILE* __cdecl _wfreopen (const wchar_t*, const wchar_t*, FILE*); +extern "C" _CRTIMP FILE *__cdecl _wfreopen(const wchar_t *, const wchar_t *, FILE *); #endif -#define SAMPLES_PER_SEC_OLD 11025 -#define SAMPLES_PER_SEC_NEW 22050 - using namespace CEGUI; // ******************************************************************************************** -// Internal GUI names - -#define NAME_MAIN_PANEL "MainPanel" -#define NAME_PANEL_KEYBOARD "Keyboard" -#define NAME_ITEM_OPTIONS "Options" -#define NAME_ITEM_SKIP "Skip" -#define NAME_ITEM_SOUND "Sound" -#define NAME_ITEM_ORIENTATION "Orientation" -#define NAME_ITEM_BINDKEYS "Bindkeys" - // stdin/err redirection #define STDOUT_FNAME "\\scummvm_stdout.txt" #define STDERR_FNAME "\\scummvm_stderr.txt" @@ -106,34 +89,6 @@ bool OSystem_WINCE3::_soundMaster = true; bool _isSmartphone = false; bool _hasSmartphoneResolution = false; -// Graphics mode consts - -// Low end devices 240x320 - -static const OSystem::GraphicsMode s_supportedGraphicsModesLow[] = { - {"1x", _s("Normal (no scaling)"), GFX_NORMAL}, - {0, 0, 0} -}; - -// High end device 480x640 - -static const OSystem::GraphicsMode s_supportedGraphicsModesHigh[] = { - {"1x", _s("Normal (no scaling)"), GFX_NORMAL}, - {"2x", "2x", GFX_DOUBLESIZE}, -#ifndef _MSC_VER // EVC breaks template functions, and I'm tired of fixing them :) - {"2xsai", "2xSAI", GFX_2XSAI}, - {"super2xsai", "Super2xSAI", GFX_SUPER2XSAI}, - {"supereagle", "SuperEagle", GFX_SUPEREAGLE}, -#endif - {"advmame2x", "AdvMAME2x", GFX_ADVMAME2X}, -#ifndef _MSC_VER - {"hq2x", "HQ2x", GFX_HQ2X}, - {"tv2x", "TV2x", GFX_TV2X}, -#endif - {"dotmatrix", "DotMatrix", GFX_DOTMATRIX}, - {0, 0, 0} -}; - #define DEFAULT_CONFIG_FILE "scummvm.ini" // ******************************************************************************************** @@ -144,7 +99,7 @@ bool isSmartphone() { } const TCHAR *ASCIItoUnicode(const char *str) { - static TCHAR ustr[MAX_PATH]; // size good enough + static TCHAR ustr[MAX_PATH]; // size good enough MultiByteToWideChar(CP_ACP, 0, str, strlen(str) + 1, ustr, sizeof(ustr) / sizeof(TCHAR)); return ustr; @@ -173,7 +128,7 @@ int SDL_main(int argc, char **argv) { // thanks to joostp and DJWillis extern void (*__CTOR_LIST__)(); void (**constructor)() = &__CTOR_LIST__; - constructor++; // First item in list of constructors has special meaning (platform dependent), ignore it. + constructor++; // First item in list of constructors has special meaning (platform dependent), ignore it. while (*constructor) { (*constructor)(); constructor++; @@ -238,10 +193,10 @@ int SDL_main(int argc, char **argv) { res = scummvm_main(argc, argv); // Free OSystem - delete (OSystem_WINCE3 *)g_system; + delete(OSystem_WINCE3 *)g_system; #if !defined(DEBUG) && !defined(__GNUC__) } - __except (handleException(GetExceptionInformation())) { + __except(handleException(GetExceptionInformation())) { } #endif @@ -264,22 +219,22 @@ int console_main(int argc, char *argv[]) { char *bufp, *appname; appname = argv[0]; - if ( (bufp=strrchr(argv[0], '\\')) != NULL ) + if ((bufp = strrchr(argv[0], '\\')) != NULL) appname = bufp + 1; - else if ( (bufp=strrchr(argv[0], '/')) != NULL ) + else if ((bufp = strrchr(argv[0], '/')) != NULL) appname = bufp + 1; - if ( (bufp=strrchr(appname, '.')) == NULL ) + if ((bufp = strrchr(appname, '.')) == NULL) n = strlen(appname); else - n = (bufp-appname); + n = (bufp - appname); bufp = (char *) alloca(n + 1); strncpy(bufp, appname, n); bufp[n] = '\0'; appname = bufp; - if ( SDL_Init(SDL_INIT_NOPARACHUTE) < 0 ) { + if (SDL_Init(SDL_INIT_NOPARACHUTE) < 0) { error("WinMain() error: %d", SDL_GetError()); return(FALSE); } @@ -344,31 +299,31 @@ int dynamic_modules_main(HINSTANCE hInst, HINSTANCE hPrev, LPWSTR szCmdLine, int int nLen; if (wcsncmp(szCmdLine, TEXT("\\"), 1)) { - nLen = wcslen(szCmdLine)+128+1; - bufp = (wchar_t *) alloca(nLen*2); - wcscpy (bufp, TEXT("\"")); - GetModuleFileName(NULL, bufp+1, 128-3); - wcscpy (bufp+wcslen(bufp), TEXT("\" ")); - wcsncpy(bufp+wcslen(bufp), szCmdLine,nLen-wcslen(bufp)); + nLen = wcslen(szCmdLine) + 128 + 1; + bufp = (wchar_t *) alloca(nLen * 2); + wcscpy(bufp, TEXT("\"")); + GetModuleFileName(NULL, bufp + 1, 128 - 3); + wcscpy(bufp + wcslen(bufp), TEXT("\" ")); + wcsncpy(bufp + wcslen(bufp), szCmdLine, nLen - wcslen(bufp)); } else bufp = szCmdLine; - nLen = wcslen(bufp)+1; + nLen = wcslen(bufp) + 1; cmdline = (char *) alloca(nLen); WideCharToMultiByte(CP_ACP, 0, bufp, -1, cmdline, nLen, NULL, NULL); // Parse command line into argv and argc argc = ParseCommandLine(cmdline, NULL); - argv = (char **) alloca((argc+1)*(sizeof *argv)); + argv = (char **) alloca((argc + 1) * (sizeof * argv)); ParseCommandLine(cmdline, argv); // fix gdb-emulator combo while (argc > 1 && !strstr(argv[0], ".exe")) { OutputDebugString(TEXT("SDL: gdb argv[0] fixup\n")); - *(argv[1]-1) = ' '; + *(argv[1] - 1) = ' '; int i; - for (i=1; i<argc; i++) - argv[i] = argv[i+1]; + for (i = 1; i < argc; i++) + argv[i] = argv[i + 1]; argc--; } @@ -380,7 +335,6 @@ int dynamic_modules_main(HINSTANCE hInst, HINSTANCE hPrev, LPWSTR szCmdLine, int // ******************************************************************************************** - // ******************************************************************************************** void pumpMessages() { @@ -407,47 +361,53 @@ static Uint32 timer_handler_wrapper(Uint32 interval) { } void OSystem_WINCE3::initBackend() { - // Instantiate our own sound mixer - // mixer init is rerun when a game engine is selected. - setupMixer(); + + assert(!_inited); + + // Create the backend custom managers + if (_eventSource == 0) + _eventSource = new WINCESdlEventSource(); + + if (_mixerManager == 0) { + _mixerManager = new WINCESdlMixerManager(); + + // Setup and start mixer + _mixerManager->init(); + } + + if (_graphicsManager == 0) + _graphicsManager = new WINCESdlGraphicsManager(_eventSource); + + ((WINCESdlEventSource *)_eventSource)->init((WINCESdlGraphicsManager *)_graphicsManager); // Create the timer. CE SDL does not support multiple timers (SDL_AddTimer). // We work around this by using the SetTimer function, since we only use // one timer in scummvm (for the time being) _timer = _int_timer = new DefaultTimerManager(); - _timerID = NULL; // OSystem_SDL will call removetimer with this, it's ok + //_timerID = NULL; // OSystem_SDL will call removetimer with this, it's ok SDL_SetTimer(10, &timer_handler_wrapper); // Chain init OSystem_SDL::initBackend(); - // Query SDL for screen size and init screen dependent stuff - OSystem_WINCE3::initScreenInfos(); - _isSmartphone = CEDevice::isSmartphone(); - create_toolbar(); - _hasSmartphoneResolution = CEDevice::hasSmartphoneResolution() || CEDevice::isSmartphone(); - if (_hasSmartphoneResolution) - _panelVisible = false; // init correctly in smartphones - // Initialize global key mapping GUI::Actions::init(); GUI_Actions::Instance()->initInstanceMain(this); - if (!GUI_Actions::Instance()->loadMapping()) { // error during loading means not present/wrong version + if (!GUI_Actions::Instance()->loadMapping()) { // error during loading means not present/wrong version warning("Setting default action mappings"); - GUI_Actions::Instance()->saveMapping(); // write defaults + GUI_Actions::Instance()->saveMapping(); // write defaults } - loadDeviceConfiguration(); + // Call parent implementation of this method + //OSystem_SDL::initBackend(); + + _inited = true; } int OSystem_WINCE3::getScreenWidth() { return _platformScreenWidth; } -int OSystem_WINCE3::getScreenHeight() { - return _platformScreenHeight; -} - void OSystem_WINCE3::initScreenInfos() { // sdl port ensures that we use correctly full screen _isOzone = 0; @@ -457,6 +417,10 @@ void OSystem_WINCE3::initScreenInfos() { _platformScreenHeight = r[0]->h; } +int OSystem_WINCE3::getScreenHeight() { + return _platformScreenHeight; +} + bool OSystem_WINCE3::isOzone() { return _isOzone; } @@ -473,496 +437,37 @@ Common::String OSystem_WINCE3::getDefaultConfigFileName() { OSystem_WINCE3::OSystem_WINCE3() : OSystem_SDL(), - _orientationLandscape(0), _newOrientation(0), _panelInitialized(false), _canBeAspectScaled(false), - _panelVisible(true), _panelStateForced(false), _forceHideMouse(false), _unfilteredkeys(false), - _freeLook(false), _forcePanelInvisible(false), _toolbarHighDrawn(false), _zoomUp(false), _zoomDown(false), - _scalersChanged(false), _lastKeyPressed(0), _tapTime(0), _closeClick(false), _noDoubleTapRMB(false), - _saveToolbarState(false), _saveActiveToolbar(NAME_MAIN_PANEL), _rbutton(false), _hasfocus(true), - _usesEmulatedMouse(false), _mouseBackupOld(NULL), _mouseBackupToolbar(NULL), _mouseBackupDim(0) -{ - memset(&_mouseCurState, 0, sizeof(_mouseCurState)); - if (_isSmartphone) { - _mouseCurState.x = 20; - _mouseCurState.y = 20; - } - + _forcePanelInvisible(false) { + // Initialze File System Factory + _fsFactory = new WindowsFilesystemFactory(); _mixer = 0; - _screen = NULL; } -void OSystem_WINCE3::swap_panel_visibility() { - //if (!_forcePanelInvisible && !_panelStateForced) { - if (_zoomDown || _zoomUp) return; - - if (_panelVisible) { - if (_toolbarHandler.activeName() == NAME_PANEL_KEYBOARD) - _panelVisible = !_panelVisible; - else - _toolbarHandler.setActive(NAME_PANEL_KEYBOARD); - } else { - _toolbarHandler.setActive(NAME_MAIN_PANEL); - _panelVisible = !_panelVisible; - } - _toolbarHandler.setVisible(_panelVisible); - _toolbarHighDrawn = false; - - if (_videoMode.screenHeight > 240) - addDirtyRect(0, 400, 640, 80); - else - addDirtyRect(0, 200, 320, 40); - - if (_toolbarHandler.activeName() == NAME_PANEL_KEYBOARD && _panelVisible) - internUpdateScreen(); - else { - update_scalers(); - hotswapGFXMode(); - } - //} +OSystem_WINCE3::~OSystem_WINCE3() { + delete _fsFactory; + delete _mixer; } -void OSystem_WINCE3::swap_panel() { - _toolbarHighDrawn = false; - //if (!_panelStateForced) { - if (_toolbarHandler.activeName() == NAME_PANEL_KEYBOARD && _panelVisible) - _toolbarHandler.setActive(NAME_MAIN_PANEL); - else - _toolbarHandler.setActive(NAME_PANEL_KEYBOARD); - - if (_videoMode.screenHeight > 240) - addDirtyRect(0, 400, 640, 80); - else - addDirtyRect(0, 200, 320, 40); - - _toolbarHandler.setVisible(true); - if (!_panelVisible) { - _panelVisible = true; - update_scalers(); - hotswapGFXMode(); - } - //} -} - -void OSystem_WINCE3::swap_smartphone_keyboard() { - _toolbarHandler.setActive(NAME_PANEL_KEYBOARD); - _panelVisible = !_panelVisible; - _toolbarHandler.setVisible(_panelVisible); - if (_videoMode.screenHeight > 240) - addDirtyRect(0, 0, 640, 80); - else - addDirtyRect(0, 0, 320, 40); - internUpdateScreen(); -} - -void OSystem_WINCE3::smartphone_rotate_display() { - _orientationLandscape = _newOrientation = _orientationLandscape == 1 ? 2 : 1; - ConfMan.setInt("landscape", _orientationLandscape); - ConfMan.flushToDisk(); - hotswapGFXMode(); +FilesystemFactory *OSystem_WINCE3::getFilesystemFactory() { + return _fsFactory; } void OSystem_WINCE3::swap_sound_master() { _soundMaster = !_soundMaster; - if (_toolbarHandler.activeName() == NAME_MAIN_PANEL) - _toolbarHandler.forceRedraw(); // redraw sound icon -} - -void OSystem_WINCE3::add_right_click(bool pushed) { - int x, y; - retrieve_mouse_location(x, y); - EventsBuffer::simulateMouseRightClick(x, y, pushed); -} - -void OSystem_WINCE3::swap_mouse_visibility() { - _forceHideMouse = !_forceHideMouse; - if (_forceHideMouse) - undrawMouse(); -} - -void OSystem_WINCE3::swap_freeLook() { - _freeLook = !_freeLook; -} -void OSystem_WINCE3::swap_zoom_up() { - if (_zoomUp) { - // restore visibility - _toolbarHandler.setVisible(_saveToolbarZoom); - // restore scaler - _scaleFactorYd = 2; - _scalerProc = DownscaleAllByHalf; - _zoomUp = false; - _zoomDown = false; - } else { - // only active if running on a PocketPC - if (_scalerProc != DownscaleAllByHalf && _scalerProc != DownscaleHorizByHalf) - return; - if (_scalerProc == DownscaleAllByHalf) { - _saveToolbarZoom = _toolbarHandler.visible(); - _toolbarHandler.setVisible(false); - // set zoom scaler - _scaleFactorYd = 1; - _scalerProc = DownscaleHorizByHalf; - } + //WINCESdlGraphicsManager _graphicsManager - _zoomDown = false; - _zoomUp = true; - } - // redraw whole screen - addDirtyRect(0, 0, 640, 480); - internUpdateScreen(); + if (((WINCESdlGraphicsManager *)_graphicsManager)->_toolbarHandler.activeName() == NAME_MAIN_PANEL) + ((WINCESdlGraphicsManager *)_graphicsManager)->_toolbarHandler.forceRedraw(); // redraw sound icon } -void OSystem_WINCE3::swap_zoom_down() { - if (_zoomDown) { - // restore visibility - _toolbarHandler.setVisible(_saveToolbarZoom); - // restore scaler - _scaleFactorYd = 2; - _scalerProc = DownscaleAllByHalf; - _zoomDown = false; - _zoomUp = false; - } else { - // only active if running on a PocketPC - if (_scalerProc != DownscaleAllByHalf && _scalerProc != DownscaleHorizByHalf) - return; - if (_scalerProc == DownscaleAllByHalf) { - _saveToolbarZoom = _toolbarHandler.visible(); - _toolbarHandler.setVisible(false); - // set zoom scaler - _scaleFactorYd = 1; - _scalerProc = DownscaleHorizByHalf; - } - - _zoomUp = false; - _zoomDown = true; - } - // redraw whole screen - addDirtyRect(0, 0, 640, 480); - internUpdateScreen(); -} - -// Smartphone actions -void OSystem_WINCE3::initZones() { - int i; - - _currentZone = 0; - for (i = 0; i < TOTAL_ZONES; i++) { - _mouseXZone[i] = (_zones[i].x + (_zones[i].width / 2)) * _scaleFactorXm / _scaleFactorXd; - _mouseYZone[i] = (_zones[i].y + (_zones[i].height / 2)) * _scaleFactorYm / _scaleFactorYd; - } -} - -void OSystem_WINCE3::loadDeviceConfigurationElement(String element, int &value, int defaultValue) { - value = ConfMan.getInt(element, ConfMan.kApplicationDomain); - if (!value) { - value = defaultValue; - ConfMan.setInt(element, value, ConfMan.kApplicationDomain); - } -} - -void OSystem_WINCE3::loadDeviceConfiguration() { - loadDeviceConfigurationElement("repeatTrigger", _keyRepeatTrigger, 200); - loadDeviceConfigurationElement("repeatX", _repeatX, 4); - loadDeviceConfigurationElement("repeatY", _repeatY, 4); - loadDeviceConfigurationElement("stepX1", _stepX1, 2); - loadDeviceConfigurationElement("stepX2", _stepX2, 10); - loadDeviceConfigurationElement("stepX3", _stepX3, 40); - loadDeviceConfigurationElement("stepY1", _stepY1, 2); - loadDeviceConfigurationElement("stepY2", _stepY2, 10); - loadDeviceConfigurationElement("stepY3", _stepY3, 20); - ConfMan.flushToDisk(); -} - -void OSystem_WINCE3::add_left_click(bool pushed) { - int x, y; - retrieve_mouse_location(x, y); - EventsBuffer::simulateMouseLeftClick(x, y, pushed); -} - -void OSystem_WINCE3::move_cursor_up() { - int x, y; - _usesEmulatedMouse = true; - retrieve_mouse_location(x, y); - if (_keyRepeat > _repeatY) - y -= _stepY3; - else if (_keyRepeat) - y -= _stepY2; - else - y -= _stepY1; - - if (y < 0) - y = 0; - - EventsBuffer::simulateMouseMove(x, y); -} - -void OSystem_WINCE3::move_cursor_down() { - int x, y; - _usesEmulatedMouse = true; - retrieve_mouse_location(x, y); - if (_keyRepeat > _repeatY) - y += _stepY3; - else if (_keyRepeat) - y += _stepY2; - else - y += _stepY1; - - if (y > _videoMode.screenHeight * _scaleFactorYm / _scaleFactorYd) - y = _videoMode.screenHeight * _scaleFactorYm / _scaleFactorYd; - - EventsBuffer::simulateMouseMove(x, y); -} - -void OSystem_WINCE3::move_cursor_left() { - int x, y; - _usesEmulatedMouse = true; - retrieve_mouse_location(x, y); - if (_keyRepeat > _repeatX) - x -= _stepX3; - else if (_keyRepeat) - x -= _stepX2; - else - x -= _stepX1; - - if (x < 0) - x = 0; - - EventsBuffer::simulateMouseMove(x, y); -} - -void OSystem_WINCE3::move_cursor_right() { - int x, y; - _usesEmulatedMouse = true; - retrieve_mouse_location(x, y); - if (_keyRepeat > _repeatX) - x += _stepX3; - else if (_keyRepeat) - x += _stepX2; - else - x += _stepX1; - - if (x > _videoMode.screenWidth * _scaleFactorXm / _scaleFactorXd) - x = _videoMode.screenWidth * _scaleFactorXm / _scaleFactorXd; - - EventsBuffer::simulateMouseMove(x, y); -} - -void OSystem_WINCE3::switch_zone() { - int x, y; - int i; - retrieve_mouse_location(x, y); - - for (i = 0; i < TOTAL_ZONES; i++) - if (x >= _zones[i].x && y >= _zones[i].y && - x <= _zones[i].x + _zones[i].width && y <= _zones[i].y + _zones[i].height) { - _mouseXZone[i] = x; - _mouseYZone[i] = y; - break; - } - _currentZone = i + 1; - if (_currentZone >= TOTAL_ZONES) - _currentZone = 0; - - EventsBuffer::simulateMouseMove(_mouseXZone[_currentZone], _mouseYZone[_currentZone]); -} - -void OSystem_WINCE3::create_toolbar() { - PanelKeyboard *keyboard; - - // Add the keyboard - keyboard = new PanelKeyboard(PANEL_KEYBOARD); - _toolbarHandler.add(NAME_PANEL_KEYBOARD, *keyboard); - _toolbarHandler.setVisible(false); -} - -void OSystem_WINCE3::setupMixer() { - SDL_AudioSpec desired; - int thread_priority; - - uint32 sampleRate = compute_sample_rate(); - if (sampleRate == 0) - warning("OSystem_WINCE3::setupMixer called with sample rate 0 - audio will not work"); - else if (_mixer && _mixer->getOutputRate() == sampleRate) { - debug(1, "Skipping sound mixer re-init: samplerate is good"); - return; - } - - memset(&desired, 0, sizeof(desired)); - desired.freq = sampleRate; - desired.format = AUDIO_S16SYS; - desired.channels = 2; - desired.samples = 128; - desired.callback = private_sound_proc; - desired.userdata = this; - - // Create the mixer instance - if (_mixer == 0) - _mixer = new Audio::MixerImpl(this, sampleRate); - - // Add sound thread priority - if (!ConfMan.hasKey("sound_thread_priority")) - thread_priority = THREAD_PRIORITY_NORMAL; - else - thread_priority = ConfMan.getInt("sound_thread_priority"); - - desired.thread_priority = thread_priority; - - SDL_CloseAudio(); - if (SDL_OpenAudio(&desired, NULL) != 0) { - warning("Could not open audio device: %s", SDL_GetError()); - _mixer->setReady(false); - - } else { - debug(1, "Sound opened OK, mixing at %d Hz", sampleRate); - - // Re-create mixer to match the output rate - int vol1 = _mixer->getVolumeForSoundType(Audio::Mixer::kPlainSoundType); - int vol2 = _mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType); - int vol3 = _mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType); - int vol4 = _mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType); - delete _mixer; - _mixer = new Audio::MixerImpl(this, sampleRate); - _mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, vol1); - _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, vol2); - _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, vol3); - _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, vol4); - _mixer->setReady(true); - SDL_PauseAudio(0); - } -} - -void OSystem_WINCE3::private_sound_proc(void *param, byte *buf, int len) { - OSystem_WINCE3 *this_ = (OSystem_WINCE3 *)param; - assert(this_); - - if (this_->_mixer) - this_->_mixer->mixCallback(buf, len); - if (!_soundMaster) - memset(buf, 0, len); -} - -#ifdef USE_VORBIS -bool OSystem_WINCE3::checkOggHighSampleRate() { - char trackFile[255]; - FILE *testFile; - OggVorbis_File *test_ov_file = new OggVorbis_File; - - // FIXME: The following sprintf assumes that "path" is always - // terminated by a path separator. This is *not* true in general. - // This code really should check for the path separator, or even - // better, use the FSNode API. - sprintf(trackFile, "%sTrack1.ogg", ConfMan.get("path").c_str()); - // Check if we have an OGG audio track - testFile = fopen(trackFile, "rb"); - if (testFile) { - if (!ov_open(testFile, test_ov_file, NULL, 0)) { - bool highSampleRate = (ov_info(test_ov_file, -1)->rate == 22050); - ov_clear(test_ov_file); - delete test_ov_file; - return highSampleRate; - } - } - - // Do not test for OGG samples - too big and too slow anyway :) - - delete test_ov_file; - return false; -} -#endif - -uint32 OSystem_WINCE3::compute_sample_rate() { - uint32 sampleRate; - - // Force at least medium quality FM synthesis for FOTAQ - Common::String gameid(ConfMan.get("gameid")); - if (gameid == "queen") { - if (!((ConfMan.hasKey("FM_high_quality") && ConfMan.getBool("FM_high_quality")) || - (ConfMan.hasKey("FM_medium_quality") && ConfMan.getBool("FM_medium_quality")))) { - ConfMan.setBool("FM_medium_quality", true); - ConfMan.flushToDisk(); - } - } - // See if the output frequency is forced by the game - if (gameid == "ft" || gameid == "dig" || gameid == "comi" || gameid == "queen" || gameid == "sword" || gameid == "agi") - sampleRate = SAMPLES_PER_SEC_NEW; - else { - if (ConfMan.hasKey("high_sample_rate") && ConfMan.getBool("high_sample_rate")) - sampleRate = SAMPLES_PER_SEC_NEW; - else - sampleRate = SAMPLES_PER_SEC_OLD; - } - -#ifdef USE_VORBIS - // Modify the sample rate on the fly if OGG is involved - if (sampleRate == SAMPLES_PER_SEC_OLD) - if (checkOggHighSampleRate()) - sampleRate = SAMPLES_PER_SEC_NEW; -#endif - - return sampleRate; -} void OSystem_WINCE3::engineInit() { check_mappings(); // called here to initialize virtual keys handling //update_game_settings(); // finalize mixer init - setupMixer(); -} - -const OSystem::GraphicsMode *OSystem_WINCE3::getSupportedGraphicsModes() const { - if (CEDevice::hasWideResolution()) - return s_supportedGraphicsModesHigh; - else - return s_supportedGraphicsModesLow; -} - -bool OSystem_WINCE3::hasFeature(Feature f) { - return (f == kFeatureVirtualKeyboard); -} - -void OSystem_WINCE3::setFeatureState(Feature f, bool enable) { - switch (f) { - case kFeatureFullscreenMode: - return; - - case kFeatureVirtualKeyboard: - if (_hasSmartphoneResolution) - return; - _toolbarHighDrawn = false; - if (enable) { - _panelStateForced = true; - if (!_toolbarHandler.visible()) swap_panel_visibility(); - //_saveToolbarState = _toolbarHandler.visible(); - _saveActiveToolbar = _toolbarHandler.activeName(); - _toolbarHandler.setActive(NAME_PANEL_KEYBOARD); - _toolbarHandler.setVisible(true); - } else - if (_panelStateForced) { - _panelStateForced = false; - _toolbarHandler.setActive(_saveActiveToolbar); - //_toolbarHandler.setVisible(_saveToolbarState); - } - return; - - case kFeatureDisableKeyFiltering: - if (_hasSmartphoneResolution) - _unfilteredkeys = enable; - return; - - default: - OSystem_SDL::setFeatureState(f, enable); - } -} - -bool OSystem_WINCE3::getFeatureState(Feature f) { - switch (f) { - case kFeatureFullscreenMode: - return false; - case kFeatureVirtualKeyboard: - return (_panelStateForced); - default: - return OSystem_SDL::getFeatureState(f); - } + _mixerManager->init(); } void OSystem_WINCE3::check_mappings() { @@ -974,7 +479,7 @@ void OSystem_WINCE3::check_mappings() { return; GUI_Actions::Instance()->initInstanceGame(); - instance = (CEActionsPocket*)GUI_Actions::Instance(); + instance = (CEActionsPocket *)GUI_Actions::Instance(); // Some games need to map the right click button, signal it here if it wasn't done if (instance->needsRightClickMapping()) { @@ -1014,1472 +519,39 @@ void OSystem_WINCE3::check_mappings() { // Extra warning for Zak Mc Kracken if (strncmp(gameid.c_str(), "zak", 3) == 0 && - !GUI_Actions::Instance()->getMapping(POCKET_ACTION_HIDE)) { + !GUI_Actions::Instance()->getMapping(POCKET_ACTION_HIDE)) { GUI::MessageDialog alert(_("Don't forget to map a key to 'Hide Toolbar' action to see the whole inventory")); alert.runModal(); } } -void OSystem_WINCE3::update_game_settings() { - Common::String gameid(ConfMan.get("gameid")); - - // Finish panel initialization - if (!_panelInitialized && !gameid.empty()) { - Panel *panel; - _panelInitialized = true; - // Add the main panel - panel = new Panel(0, 32); - panel->setBackground(IMAGE_PANEL); - // Save - panel->add(NAME_ITEM_OPTIONS, new ItemAction(ITEM_OPTIONS, POCKET_ACTION_SAVE)); - // Skip - panel->add(NAME_ITEM_SKIP, new ItemAction(ITEM_SKIP, POCKET_ACTION_SKIP)); - // sound - panel->add(NAME_ITEM_SOUND, new ItemSwitch(ITEM_SOUND_OFF, ITEM_SOUND_ON, &_soundMaster)); - // bind keys - panel->add(NAME_ITEM_BINDKEYS, new ItemAction(ITEM_BINDKEYS, POCKET_ACTION_BINDKEYS)); - // portrait/landscape - screen dependent - // FIXME : will still display the portrait/landscape icon when using a scaler (but will be disabled) - if (ConfMan.hasKey("landscape")) { - if (ConfMan.get("landscape")[0] > 57) { - _newOrientation = _orientationLandscape = ConfMan.getBool("landscape"); - //ConfMan.removeKey("landscape", ""); - ConfMan.setInt("landscape", _orientationLandscape); - } else - _newOrientation = _orientationLandscape = ConfMan.getInt("landscape"); - } else { - _newOrientation = _orientationLandscape = 0; - } - panel->add(NAME_ITEM_ORIENTATION, new ItemSwitch(ITEM_VIEW_LANDSCAPE, ITEM_VIEW_PORTRAIT, &_newOrientation, 2)); - _toolbarHandler.add(NAME_MAIN_PANEL, *panel); - _toolbarHandler.setActive(NAME_MAIN_PANEL); - _toolbarHandler.setVisible(true); - - if (_videoMode.mode == GFX_NORMAL && ConfMan.hasKey("landscape") && ConfMan.getInt("landscape")) { - setGraphicsMode(GFX_NORMAL); - hotswapGFXMode(); - } - - if (_hasSmartphoneResolution) - panel->setVisible(false); - - _saveToolbarState = true; - } - - if (ConfMan.hasKey("no_doubletap_rightclick")) - _noDoubleTapRMB = ConfMan.getBool("no_doubletap_rightclick"); -} - -void OSystem_WINCE3::initSize(uint w, uint h, const Graphics::PixelFormat *format) { - if (_hasSmartphoneResolution && h == 240) - h = 200; // mainly for the launcher - - if (_isSmartphone && !ConfMan.hasKey("landscape")) { - ConfMan.setInt("landscape", 1); - ConfMan.flushToDisk(); - } - - _canBeAspectScaled = false; - if (w == 320 && h == 200 && !_hasSmartphoneResolution) { - _canBeAspectScaled = true; - h = 240; // use the extra 40 pixels height for the toolbar - } - - if (h == 400) // touche engine fixup - h += 80; - - if (!_hasSmartphoneResolution) { - if (h == 240) - _toolbarHandler.setOffset(200); - else - _toolbarHandler.setOffset(400); - } else { - if (h == 240) - _toolbarHandler.setOffset(200); - else // 176x220 - _toolbarHandler.setOffset(0); - } - - if (w != (uint) _videoMode.screenWidth || h != (uint) _videoMode.screenHeight) - _scalersChanged = false; - - _videoMode.overlayWidth = w; - _videoMode.overlayHeight = h; - - OSystem_SDL::initSize(w, h, format); - - if (_scalersChanged) { - unloadGFXMode(); - loadGFXMode(); - _scalersChanged = false; - } - - update_game_settings(); -} - - -int OSystem_WINCE3::getDefaultGraphicsMode() const { - return GFX_NORMAL; -} - void OSystem_WINCE3::setGraphicsModeIntern() { // Scalers have been pre-selected for the desired mode. // No further tuning required. } -bool OSystem_WINCE3::update_scalers() { - _videoMode.aspectRatioCorrection = false; - - if (CEDevice::hasPocketPCResolution()) { - if (_videoMode.mode != GFX_NORMAL) - return false; - - if ((!_orientationLandscape && (_videoMode.screenWidth == 320 || !_videoMode.screenWidth)) - || CEDevice::hasSquareQVGAResolution() ) { - if (getScreenWidth() != 320) { - _scaleFactorXm = 3; - _scaleFactorXd = 4; - _scaleFactorYm = 1; - _scaleFactorYd = 1; - _scalerProc = DownscaleHorizByThreeQuarters; - } else { - _scaleFactorXm = 1; - _scaleFactorXd = 1; - _scaleFactorYm = 1; - _scaleFactorYd = 1; - _scalerProc = Normal1x; - } - } else if ( _orientationLandscape && (_videoMode.screenWidth == 320 || !_videoMode.screenWidth)) { - if (!_panelVisible && !_hasSmartphoneResolution && !_overlayVisible && _canBeAspectScaled) { - _scaleFactorXm = 1; - _scaleFactorXd = 1; - _scaleFactorYm = 6; - _scaleFactorYd = 5; - _scalerProc = Normal1xAspect; - _videoMode.aspectRatioCorrection = true; - } else { - _scaleFactorXm = 1; - _scaleFactorXd = 1; - _scaleFactorYm = 1; - _scaleFactorYd = 1; - _scalerProc = Normal1x; - } - } else if (_videoMode.screenWidth == 640 && !(isOzone() && (getScreenWidth() >= 640 || getScreenHeight() >= 640))) { - _scaleFactorXm = 1; - _scaleFactorXd = 2; - _scaleFactorYm = 1; - _scaleFactorYd = 2; - _scalerProc = DownscaleAllByHalf; - } else if (_videoMode.screenWidth == 640 && (isOzone() && (getScreenWidth() >= 640 || getScreenHeight() >= 640))) { - _scaleFactorXm = 1; - _scaleFactorXd = 1; - _scaleFactorYm = 1; - _scaleFactorYd = 1; - _scalerProc = Normal1x; - } - - return true; - } else if (CEDevice::hasWideResolution()) { -#ifdef USE_ARM_SCALER_ASM - if ( _videoMode.mode == GFX_DOUBLESIZE && (_videoMode.screenWidth == 320 || !_videoMode.screenWidth) ) { - if ( !_panelVisible && !_overlayVisible && _canBeAspectScaled ) { - _scaleFactorXm = 2; - _scaleFactorXd = 1; - _scaleFactorYm = 12; - _scaleFactorYd = 5; - _scalerProc = Normal2xAspect; - _videoMode.aspectRatioCorrection = true; - } else if ( (_panelVisible || _overlayVisible) && _canBeAspectScaled ) { - _scaleFactorXm = 2; - _scaleFactorXd = 1; - _scaleFactorYm = 2; - _scaleFactorYd = 1; - _scalerProc = Normal2x; - } - return true; - } -#endif - } else if (CEDevice::hasSmartphoneResolution()) { - if (_videoMode.mode != GFX_NORMAL) - return false; - - if (_videoMode.screenWidth > 320) - error("Game resolution not supported on Smartphone"); -#ifdef ARM - _scaleFactorXm = 11; - _scaleFactorXd = 16; -#else - _scaleFactorXm = 2; - _scaleFactorXd = 3; -#endif - _scaleFactorYm = 7; - _scaleFactorYd = 8; - _scalerProc = SmartphoneLandscape; - initZones(); - return true; - } - - return false; -} - -bool OSystem_WINCE3::setGraphicsMode(int mode) { - - Common::StackLock lock(_graphicsMutex); - int oldScaleFactorXm = _scaleFactorXm; - int oldScaleFactorXd = _scaleFactorXd; - int oldScaleFactorYm = _scaleFactorYm; - int oldScaleFactorYd = _scaleFactorYd; - - _scaleFactorXm = -1; - _scaleFactorXd = -1; - _scaleFactorYm = -1; - _scaleFactorYd = -1; - - if (ConfMan.hasKey("landscape")) - if (ConfMan.get("landscape")[0] > 57) { - _newOrientation = _orientationLandscape = ConfMan.getBool("landscape"); - ConfMan.setInt("landscape", _orientationLandscape); - } else - _newOrientation = _orientationLandscape = ConfMan.getInt("landscape"); - else - _newOrientation = _orientationLandscape = 0; - - if (isOzone() && (getScreenWidth() >= 640 || getScreenHeight() >= 640) && mode) - _scaleFactorXm = -1; - - if (CEDevice::hasPocketPCResolution() && !CEDevice::hasWideResolution() && _orientationLandscape) - _videoMode.mode = GFX_NORMAL; - else - _videoMode.mode = mode; - - if (_scaleFactorXm < 0) { - /* Standard scalers, from the SDL backend */ - switch (_videoMode.mode) { - case GFX_NORMAL: - _videoMode.scaleFactor = 1; - _scalerProc = Normal1x; - break; - case GFX_DOUBLESIZE: - _videoMode.scaleFactor = 2; - _scalerProc = Normal2x; - break; - case GFX_TRIPLESIZE: - _videoMode.scaleFactor = 3; - _scalerProc = Normal3x; - break; - case GFX_2XSAI: - _videoMode.scaleFactor = 2; - _scalerProc = _2xSaI; - break; - case GFX_SUPER2XSAI: - _videoMode.scaleFactor = 2; - _scalerProc = Super2xSaI; - break; - case GFX_SUPEREAGLE: - _videoMode.scaleFactor = 2; - _scalerProc = SuperEagle; - break; - case GFX_ADVMAME2X: - _videoMode.scaleFactor = 2; - _scalerProc = AdvMame2x; - break; - case GFX_ADVMAME3X: - _videoMode.scaleFactor = 3; - _scalerProc = AdvMame3x; - break; -#ifdef USE_HQ_SCALERS - case GFX_HQ2X: - _videoMode.scaleFactor = 2; - _scalerProc = HQ2x; - break; - case GFX_HQ3X: - _videoMode.scaleFactor = 3; - _scalerProc = HQ3x; - break; -#endif - case GFX_TV2X: - _videoMode.scaleFactor = 2; - _scalerProc = TV2x; - break; - case GFX_DOTMATRIX: - _videoMode.scaleFactor = 2; - _scalerProc = DotMatrix; - break; - - default: - error("unknown gfx mode %d", mode); - } - } - - // Check if the scaler can be accepted, if not get back to normal scaler - if (_videoMode.scaleFactor && ((_videoMode.scaleFactor * _videoMode.screenWidth > getScreenWidth() && _videoMode.scaleFactor * _videoMode.screenWidth > getScreenHeight()) - || (_videoMode.scaleFactor * _videoMode.screenHeight > getScreenWidth() && _videoMode.scaleFactor * _videoMode.screenHeight > getScreenHeight()))) { - _videoMode.scaleFactor = 1; - _scalerProc = Normal1x; - } - - // Common scaler system was used - if (_scaleFactorXm < 0) { - _scaleFactorXm = _videoMode.scaleFactor; - _scaleFactorXd = 1; - _scaleFactorYm = _videoMode.scaleFactor; - _scaleFactorYd = 1; - } - - _forceFull = true; - - if (oldScaleFactorXm != _scaleFactorXm || - oldScaleFactorXd != _scaleFactorXd || - oldScaleFactorYm != _scaleFactorYm || - oldScaleFactorYd != _scaleFactorYd) { - _scalersChanged = true; - } - else - _scalersChanged = false; - - - return true; - -} - -bool OSystem_WINCE3::loadGFXMode() { - int displayWidth; - int displayHeight; - unsigned int flags = SDL_FULLSCREEN | SDL_SWSURFACE; - - _videoMode.fullscreen = true; // forced - _forceFull = true; - - _tmpscreen = NULL; - - // Recompute scalers if necessary - update_scalers(); - - // Create the surface that contains the 8 bit game data - _screen = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.screenWidth, _videoMode.screenHeight, 8, 0, 0, 0, 0); - if (_screen == NULL) - error("_screen failed (%s)", SDL_GetError()); - - // Create the surface that contains the scaled graphics in 16 bit mode - // Always use full screen mode to have a "clean screen" - if (!_videoMode.aspectRatioCorrection) { - displayWidth = _videoMode.screenWidth * _scaleFactorXm / _scaleFactorXd; - displayHeight = _videoMode.screenHeight * _scaleFactorYm / _scaleFactorYd; - } else { - displayWidth = _videoMode.screenWidth * _videoMode.scaleFactor; - displayHeight = _videoMode.screenHeight* _videoMode.scaleFactor; - } - - switch (_orientationLandscape) { - case 1: - flags |= SDL_LANDSCVIDEO; - break; - case 2: - flags |= SDL_INVLNDVIDEO; - break; - default: - flags |= SDL_PORTRTVIDEO; - } - _hwscreen = SDL_SetVideoMode(displayWidth, displayHeight, 16, flags); - - if (_hwscreen == NULL) { - warning("SDL_SetVideoMode says we can't switch to that mode (%s)", SDL_GetError()); - quit(); - } - - // see what orientation sdl finally accepted - if (_hwscreen->flags & SDL_PORTRTVIDEO) - _orientationLandscape = _newOrientation = 0; - else if (_hwscreen->flags & SDL_LANDSCVIDEO) - _orientationLandscape = _newOrientation = 1; - else - _orientationLandscape = _newOrientation = 2; - - // Create the surface used for the graphics in 16 bit before scaling, and also the overlay - // Distinguish 555 and 565 mode - if (_hwscreen->format->Rmask == 0x7C00) - InitScalers(555); - else - InitScalers(565); - _overlayFormat.bytesPerPixel = _hwscreen->format->BytesPerPixel; - _overlayFormat.rLoss = _hwscreen->format->Rloss; - _overlayFormat.gLoss = _hwscreen->format->Gloss; - _overlayFormat.bLoss = _hwscreen->format->Bloss; - _overlayFormat.aLoss = _hwscreen->format->Aloss; - _overlayFormat.rShift = _hwscreen->format->Rshift; - _overlayFormat.gShift = _hwscreen->format->Gshift; - _overlayFormat.bShift = _hwscreen->format->Bshift; - _overlayFormat.aShift = _hwscreen->format->Ashift; - - // Need some extra bytes around when using 2xSaI - _tmpscreen = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.screenWidth + 3, _videoMode.screenHeight + 3, 16, _hwscreen->format->Rmask, _hwscreen->format->Gmask, _hwscreen->format->Bmask, _hwscreen->format->Amask); - - if (_tmpscreen == NULL) - error("_tmpscreen creation failed (%s)", SDL_GetError()); - - // Overlay - if (CEDevice::hasDesktopResolution()) { - _overlayscreen = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.overlayWidth * _scaleFactorXm / _scaleFactorXd, _videoMode.overlayHeight * _scaleFactorYm / _scaleFactorYd, 16, 0, 0, 0, 0); - if (_overlayscreen == NULL) - error("_overlayscreen failed (%s)", SDL_GetError()); - _tmpscreen2 = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.overlayWidth * _scaleFactorXm / _scaleFactorXd + 3, _videoMode.overlayHeight * _scaleFactorYm / _scaleFactorYd + 3, 16, 0, 0, 0, 0); - if (_tmpscreen2 == NULL) - error("_tmpscreen2 failed (%s)", SDL_GetError()); - } else { - _overlayscreen = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.overlayWidth, _videoMode.overlayHeight, 16, 0, 0, 0, 0); - if (_overlayscreen == NULL) - error("_overlayscreen failed (%s)", SDL_GetError()); - _tmpscreen2 = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.overlayWidth + 3, _videoMode.overlayHeight + 3, 16, 0, 0, 0, 0); - if (_tmpscreen2 == NULL) - error("_tmpscreen2 failed (%s)", SDL_GetError()); - } - - // Toolbar - _toolbarHighDrawn = false; - uint16 *toolbar_screen = (uint16 *)calloc(320 * 40, sizeof(uint16)); // *not* leaking memory here - _toolbarLow = SDL_CreateRGBSurfaceFrom(toolbar_screen, 320, 40, 16, 320 * 2, _hwscreen->format->Rmask, _hwscreen->format->Gmask, _hwscreen->format->Bmask, _hwscreen->format->Amask); - - if (_toolbarLow == NULL) - error("_toolbarLow failed (%s)", SDL_GetError()); - - if (_videoMode.screenHeight > 240) { - uint16 *toolbar_screen = (uint16 *)calloc(640 * 80, sizeof(uint16)); - _toolbarHigh = SDL_CreateRGBSurfaceFrom(toolbar_screen, 640, 80, 16, 640 * 2, _hwscreen->format->Rmask, _hwscreen->format->Gmask, _hwscreen->format->Bmask, _hwscreen->format->Amask); - - if (_toolbarHigh == NULL) - error("_toolbarHigh failed (%s)", SDL_GetError()); - } else - _toolbarHigh = NULL; - - - // keyboard cursor control, some other better place for it? - _km.x_max = _videoMode.screenWidth * _scaleFactorXm / _scaleFactorXd - 1; - _km.y_max = _videoMode.screenHeight * _scaleFactorXm / _scaleFactorXd - 1; - _km.delay_time = 25; - _km.last_time = 0; - - return true; -} - -void OSystem_WINCE3::unloadGFXMode() { - if (_screen) { - SDL_FreeSurface(_screen); - _screen = NULL; - } - - if (_hwscreen) { - SDL_FreeSurface(_hwscreen); - _hwscreen = NULL; - } - - if (_tmpscreen) { - SDL_FreeSurface(_tmpscreen); - _tmpscreen = NULL; - } -} - -bool OSystem_WINCE3::hotswapGFXMode() { - if (!_screen) - return false; - - // Keep around the old _screen & _tmpscreen so we can restore the screen data - // after the mode switch. (also for the overlay) - SDL_Surface *old_screen = _screen; - SDL_Surface *old_tmpscreen = _tmpscreen; - SDL_Surface *old_overlayscreen = _overlayscreen; - SDL_Surface *old_tmpscreen2 = _tmpscreen2; - - // Release the HW screen surface - SDL_FreeSurface(_hwscreen); - - // Release toolbars - free(_toolbarLow->pixels); - SDL_FreeSurface(_toolbarLow); - if (_toolbarHigh) { - free(_toolbarHigh->pixels); - SDL_FreeSurface(_toolbarHigh); - } - - // Setup the new GFX mode - if (!loadGFXMode()) { - unloadGFXMode(); - - _screen = old_screen; - _overlayscreen = old_overlayscreen; - - return false; - } - - // reset palette - SDL_SetColors(_screen, _currentPalette, 0, 256); - - // Restore old screen content - SDL_BlitSurface(old_screen, NULL, _screen, NULL); - SDL_BlitSurface(old_tmpscreen, NULL, _tmpscreen, NULL); - if (_overlayVisible) { - SDL_BlitSurface(old_overlayscreen, NULL, _overlayscreen, NULL); - SDL_BlitSurface(old_tmpscreen2, NULL, _tmpscreen2, NULL); - } - - // Free the old surfaces - SDL_FreeSurface(old_screen); - SDL_FreeSurface(old_tmpscreen); - SDL_FreeSurface(old_overlayscreen); - SDL_FreeSurface(old_tmpscreen2); - - // Blit everything back to the screen - _toolbarHighDrawn = false; - internUpdateScreen(); - - // Make sure that a Common::EVENT_SCREEN_CHANGED gets sent later -> FIXME this crashes when no game has been loaded. -// _modeChanged = true; - - return true; -} - -void OSystem_WINCE3::internUpdateScreen() { - SDL_Surface *srcSurf, *origSurf; - static bool old_overlayVisible = false; - int numRectsOut = 0; - int16 routx, routy, routw, routh, stretch, shakestretch; - - assert(_hwscreen != NULL); - - // bail if the application is minimized, be nice to OS - if (!_hasfocus) { - Sleep(20); - return; - } - - // If the shake position changed, fill the dirty area with blackness - if (_currentShakePos != _newShakePos) { - SDL_Rect blackrect = {0, 0, _videoMode.screenWidth * _scaleFactorXm / _scaleFactorXd, _newShakePos * _scaleFactorYm / _scaleFactorYd}; - if (_videoMode.aspectRatioCorrection) - blackrect.h = real2Aspect(blackrect.h - 1) + 1; - SDL_FillRect(_hwscreen, &blackrect, 0); - _currentShakePos = _newShakePos; - _forceFull = true; - } - - // Make sure the mouse is drawn, if it should be drawn. - drawMouse(); - - // Check whether the palette was changed in the meantime and update the - // screen surface accordingly. - if (_paletteDirtyEnd != 0) { - SDL_SetColors(_screen, _currentPalette + _paletteDirtyStart, _paletteDirtyStart, _paletteDirtyEnd - _paletteDirtyStart); - _paletteDirtyEnd = 0; - _forceFull = true; - } +void OSystem_WINCE3::initSDL() { + // Check if SDL has not been initialized + if (!_initedSDL) { + uint32 sdlFlags = SDL_INIT_EVENTTHREAD; + if (ConfMan.hasKey("disable_sdl_parachute")) + sdlFlags |= SDL_INIT_NOPARACHUTE; - if (!_overlayVisible) { - origSurf = _screen; - srcSurf = _tmpscreen; - } else { - origSurf = _overlayscreen; - srcSurf = _tmpscreen2; - } - - if (old_overlayVisible != _overlayVisible) { - old_overlayVisible = _overlayVisible; - update_scalers(); - } - - // Force a full redraw if requested - if (_forceFull) { - _numDirtyRects = 1; - - _dirtyRectList[0].x = 0; - if (!_zoomDown) - _dirtyRectList[0].y = 0; - else - _dirtyRectList[0].y = _videoMode.screenHeight / 2; - _dirtyRectList[0].w = _videoMode.screenWidth; - if (!_zoomUp && !_zoomDown) - _dirtyRectList[0].h = _videoMode.screenHeight; - else - _dirtyRectList[0].h = _videoMode.screenHeight / 2; - - _toolbarHandler.forceRedraw(); - } - - // Only draw anything if necessary - if (_numDirtyRects > 0) { - - SDL_Rect *r, *rout; - SDL_Rect dst; - uint32 srcPitch, dstPitch; - SDL_Rect *last_rect = _dirtyRectList + _numDirtyRects; - bool toolbarVisible = _toolbarHandler.visible(); - int toolbarOffset = _toolbarHandler.getOffset(); - - for (r = _dirtyRectList; r != last_rect; ++r) { - dst = *r; - dst.x++; // Shift rect by one since 2xSai needs to access the data around - dst.y++; // any pixel to scale it, and we want to avoid mem access crashes. - // NOTE: This is also known as BLACK MAGIC, copied from the sdl backend - if (SDL_BlitSurface(origSurf, r, srcSurf, &dst) != 0) - error("SDL_BlitSurface failed: %s", SDL_GetError()); + if (ConfMan.hasKey("use_GDI") && ConfMan.getBool("use_GDI")) { + SDL_VideoInit("windib", 0); + sdlFlags ^= SDL_INIT_VIDEO; } - SDL_LockSurface(srcSurf); - SDL_LockSurface(_hwscreen); - - srcPitch = srcSurf->pitch; - dstPitch = _hwscreen->pitch; - - for (r = _dirtyRectList, rout = _dirtyRectOut; r != last_rect; ++r) { - - // always clamp to enclosing, downsampled-grid-aligned rect in the downscaled image - if (_scaleFactorXd != 1) { - stretch = r->x % _scaleFactorXd; - r->x -= stretch; - r->w += stretch; - r->w = (r->x + r->w + _scaleFactorXd - 1) / _scaleFactorXd * _scaleFactorXd - r->x; - } - if (_scaleFactorYd != 1) { - stretch = r->y % _scaleFactorYd; - r->y -= stretch; - r->h += stretch; - r->h = (r->y + r->h + _scaleFactorYd - 1) / _scaleFactorYd * _scaleFactorYd - r->y; - } - - // transform - shakestretch = _currentShakePos * _scaleFactorYm / _scaleFactorYd; - routx = r->x * _scaleFactorXm / _scaleFactorXd; // locate position in scaled screen - routy = r->y * _scaleFactorYm / _scaleFactorYd + shakestretch; // adjust for shake offset - routw = r->w * _scaleFactorXm / _scaleFactorXd; - routh = r->h * _scaleFactorYm / _scaleFactorYd - shakestretch; - - // clipping destination rectangle inside device screen (more strict, also more tricky but more stable) - // note that all current scalers do not make dst rect exceed left/right, unless chosen badly (FIXME) - if (_zoomDown) routy -= 240; // adjust for zoom position - if (routy + routh < 0) continue; - if (routy < 0) { - routh += routy; - r->y -= routy * _scaleFactorYd / _scaleFactorYm; - routy = 0; - r->h = routh * _scaleFactorYd / _scaleFactorYm; - } - if (_orientationLandscape) { - if (routy > _platformScreenWidth) continue; - if (routy + routh > _platformScreenWidth) { - routh = _platformScreenWidth - routy; - r->h = routh * _scaleFactorYd / _scaleFactorYm; - } - } else { - if (routy > _platformScreenHeight) continue; - if (routy + routh > _platformScreenHeight) { - routh = _platformScreenHeight - routy; - r->h = routh * _scaleFactorYd / _scaleFactorYm; - } - } - - // check if the toolbar is overwritten - if (toolbarVisible && r->y + r->h >= toolbarOffset) - _toolbarHandler.forceRedraw(); + // Initialize SDL (SDL Subsystems are initiliazed in the corresponding sdl managers) + if (SDL_Init(sdlFlags) == -1) + error("Could not initialize SDL: %s", SDL_GetError()); - // blit it (with added voodoo from the sdl backend, shifting the source rect again) - _scalerProc( (byte *)srcSurf->pixels + (r->x * 2 + 2)+ (r->y + 1) * srcPitch, srcPitch, - (byte *)_hwscreen->pixels + routx * 2 + routy * dstPitch, dstPitch, - r->w, r->h - _currentShakePos); + // Enable unicode support if possible + SDL_EnableUNICODE(1); - // add this rect to output - rout->x = routx; rout->y = routy - shakestretch; - rout->w = routw; rout->h = routh + shakestretch; - numRectsOut++; - rout++; - - } - SDL_UnlockSurface(srcSurf); - SDL_UnlockSurface(_hwscreen); - } - // Add the toolbar if needed - SDL_Rect toolbar_rect[1]; - if (_panelVisible && _toolbarHandler.draw(_toolbarLow, &toolbar_rect[0])) { - // It can be drawn, scale it - uint32 srcPitch, dstPitch; - SDL_Surface *toolbarSurface; - ScalerProc *toolbarScaler; - - if (_videoMode.screenHeight > 240) { - if (!_toolbarHighDrawn) { - // Resize the toolbar - SDL_LockSurface(_toolbarLow); - SDL_LockSurface(_toolbarHigh); - Normal2x((byte*)_toolbarLow->pixels, _toolbarLow->pitch, (byte*)_toolbarHigh->pixels, _toolbarHigh->pitch, toolbar_rect[0].w, toolbar_rect[0].h); - SDL_UnlockSurface(_toolbarHigh); - SDL_UnlockSurface(_toolbarLow); - _toolbarHighDrawn = true; - } - toolbar_rect[0].w *= 2; - toolbar_rect[0].h *= 2; - toolbarSurface = _toolbarHigh; - } - else - toolbarSurface = _toolbarLow; - - drawToolbarMouse(toolbarSurface, true); // draw toolbar mouse if applicable - - // Apply the appropriate scaler - SDL_LockSurface(toolbarSurface); - SDL_LockSurface(_hwscreen); - srcPitch = toolbarSurface->pitch; - dstPitch = _hwscreen->pitch; - - toolbarScaler = _scalerProc; - if (_videoMode.scaleFactor == 2) - toolbarScaler = Normal2x; - else if (_videoMode.scaleFactor == 3) - toolbarScaler = Normal3x; - toolbarScaler((byte *)toolbarSurface->pixels, srcPitch, - (byte *)_hwscreen->pixels + (_toolbarHandler.getOffset() * _scaleFactorYm / _scaleFactorYd * dstPitch), - dstPitch, toolbar_rect[0].w, toolbar_rect[0].h); - SDL_UnlockSurface(toolbarSurface); - SDL_UnlockSurface(_hwscreen); - - // And blit it - toolbar_rect[0].y = _toolbarHandler.getOffset(); - toolbar_rect[0].x = toolbar_rect[0].x * _scaleFactorXm / _scaleFactorXd; - toolbar_rect[0].y = toolbar_rect[0].y * _scaleFactorYm / _scaleFactorYd; - toolbar_rect[0].w = toolbar_rect[0].w * _scaleFactorXm / _scaleFactorXd; - toolbar_rect[0].h = toolbar_rect[0].h * _scaleFactorYm / _scaleFactorYd; - - SDL_UpdateRects(_hwscreen, 1, toolbar_rect); - - drawToolbarMouse(toolbarSurface, false); // undraw toolbar mouse - } - - // Finally, blit all our changes to the screen - if (numRectsOut > 0) - SDL_UpdateRects(_hwscreen, numRectsOut, _dirtyRectOut); - - _numDirtyRects = 0; - _forceFull = false; -} - -Graphics::Surface *OSystem_WINCE3::lockScreen() { - // Make sure mouse pointer is not painted over the playfield at the time of locking - undrawMouse(); - return OSystem_SDL::lockScreen(); -} - -void OSystem_WINCE3::unlockScreen() { - OSystem_SDL::unlockScreen(); -} - -bool OSystem_WINCE3::saveScreenshot(const char *filename) { - assert(_hwscreen != NULL); - - Common::StackLock lock(_graphicsMutex); // Lock the mutex until this function ends - SDL_SaveBMP(_hwscreen, filename); - return true; -} - -void OSystem_WINCE3::copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h) { - assert (_transactionMode == kTransactionNone); - - if (_overlayscreen == NULL) - return; - - // Clip the coordinates - if (x < 0) { - w += x; - buf -= x; - x = 0; - } - - if (y < 0) { - h += y; buf -= y * pitch; - y = 0; + _initedSDL = true; } - - if (w > _videoMode.overlayWidth - x) { - w = _videoMode.overlayWidth - x; - } - - if (h > _videoMode.overlayHeight - y) { - h = _videoMode.overlayHeight - y; - } - - if (w <= 0 || h <= 0) - return; - - // Mark the modified region as dirty - addDirtyRect(x, y, w, h); - - undrawMouse(); - - if (SDL_LockSurface(_overlayscreen) == -1) - error("SDL_LockSurface failed: %s", SDL_GetError()); - - byte *dst = (byte *)_overlayscreen->pixels + y * _overlayscreen->pitch + x * 2; - do { - memcpy(dst, buf, w * 2); - dst += _overlayscreen->pitch; - buf += pitch; - } while (--h); - - SDL_UnlockSurface(_overlayscreen); -} - -void OSystem_WINCE3::copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h) { - assert (_transactionMode == kTransactionNone); - assert(src); - - if (_screen == NULL) - return; - - Common::StackLock lock(_graphicsMutex); // Lock the mutex until this function ends - - /* Clip the coordinates */ - if (x < 0) { - w += x; - src -= x; - x = 0; - } - - if (y < 0) { - h += y; - src -= y * pitch; - y = 0; - } - - if (w > _videoMode.screenWidth - x) { - w = _videoMode.screenWidth - x; - } - - if (h > _videoMode.screenHeight - y) { - h = _videoMode.screenHeight - y; - } - - if (w <= 0 || h <= 0) - return; - - addDirtyRect(x, y, w, h); - - undrawMouse(); - - // Try to lock the screen surface - if (SDL_LockSurface(_screen) == -1) - error("SDL_LockSurface failed: %s", SDL_GetError()); - - byte *dst = (byte *)_screen->pixels + y * _videoMode.screenWidth + x; - - if (_videoMode.screenWidth == pitch && pitch == w) { - memcpy(dst, src, h*w); - } else { - do { - memcpy(dst, src, w); - src += pitch; - dst += _videoMode.screenWidth; - } while (--h); - } - - // Unlock the screen surface - SDL_UnlockSurface(_screen); -} - -void OSystem_WINCE3::setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y, uint32 keycolor, int cursorTargetScale, const Graphics::PixelFormat *format) { - - undrawMouse(); - if (w == 0 || h == 0) - return; - - _mouseCurState.w = w; - _mouseCurState.h = h; - - _mouseHotspotX = hotspot_x; - _mouseHotspotY = hotspot_y; - - _mouseKeyColor = keycolor; - - free(_mouseData); - - _mouseData = (byte *) malloc(w * h); - memcpy(_mouseData, buf, w * h); - - if (w > _mouseBackupDim || h > _mouseBackupDim) { - // mouse has been undrawn, adjust sprite backup area - free(_mouseBackupOld); - free(_mouseBackupToolbar); - uint16 tmp = (w > h) ? w : h; - _mouseBackupOld = (byte *) malloc(tmp * tmp * 2); // can hold 8bpp (playfield) or 16bpp (overlay) data - _mouseBackupToolbar = (uint16 *) malloc(tmp * tmp * 2); // 16 bpp - _mouseBackupDim = tmp; - } -} - -void OSystem_WINCE3::setMousePos(int x, int y) { - if (x != _mouseCurState.x || y != _mouseCurState.y) { - undrawMouse(); - _mouseCurState.x = x; - _mouseCurState.y = y; - updateScreen(); - } -} - - -void OSystem_WINCE3::internDrawMouse() { - if (!_mouseNeedsRedraw || !_mouseVisible || !_mouseData) - return; - - int x = _mouseCurState.x - _mouseHotspotX; - int y = _mouseCurState.y - _mouseHotspotY; - int w = _mouseCurState.w; - int h = _mouseCurState.h; - byte color; - const byte *src = _mouseData; // Image representing the mouse - int width; - - // clip the mouse rect, and adjust the src pointer accordingly - if (x < 0) { - w += x; - src -= x; - x = 0; - } - if (y < 0) { - h += y; - src -= y * _mouseCurState.w; - y = 0; - } - - if (w > _videoMode.screenWidth - x) - w = _videoMode.screenWidth - x; - if (h > _videoMode.screenHeight - y) - h = _videoMode.screenHeight - y; - - // Quick check to see if anything has to be drawn at all - if (w <= 0 || h <= 0) - return; - - // Draw the mouse cursor; backup the covered area in "bak" - if (SDL_LockSurface(_overlayVisible ? _overlayscreen : _screen) == -1) - error("SDL_LockSurface failed: %s", SDL_GetError()); - - // Mark as dirty - addDirtyRect(x, y, w, h); - - if (!_overlayVisible) { - byte *bak = _mouseBackupOld; // Surface used to backup the area obscured by the mouse - byte *dst; // Surface we are drawing into - - dst = (byte *)_screen->pixels + y * _videoMode.screenWidth + x; - while (h > 0) { - width = w; - while (width > 0) { - *bak++ = *dst; - color = *src++; - if (color != _mouseKeyColor) // transparent, don't draw - *dst = color; - dst++; - width--; - } - src += _mouseCurState.w - w; - bak += _mouseBackupDim - w; - dst += _videoMode.screenWidth - w; - h--; - } - - } else { - uint16 *bak = (uint16 *)_mouseBackupOld; // Surface used to backup the area obscured by the mouse - byte *dst; // Surface we are drawing into - - dst = (byte *)_overlayscreen->pixels + (y + 1) * _overlayscreen->pitch + (x + 1) * 2; - while (h > 0) { - width = w; - while (width > 0) { - *bak++ = *(uint16 *)dst; - color = *src++; - if (color != 0xFF) // 0xFF = transparent, don't draw - *(uint16 *)dst = SDL_MapRGB(_overlayscreen->format, _currentPalette[color].r, _currentPalette[color].g, _currentPalette[color].b); - dst += 2; - width--; - } - src += _mouseCurState.w - w; - bak += _mouseBackupDim - w; - dst += _overlayscreen->pitch - w * 2; - h--; - } - } - - SDL_UnlockSurface(_overlayVisible ? _overlayscreen : _screen); - - // Finally, set the flag to indicate the mouse has been drawn - _mouseNeedsRedraw = false; -} - -void OSystem_WINCE3::undrawMouse() { - assert (_transactionMode == kTransactionNone); - - if (_mouseNeedsRedraw) - return; - - int old_mouse_x = _mouseCurState.x - _mouseHotspotX; - int old_mouse_y = _mouseCurState.y - _mouseHotspotY; - int old_mouse_w = _mouseCurState.w; - int old_mouse_h = _mouseCurState.h; - - // clip the mouse rect, and adjust the src pointer accordingly - if (old_mouse_x < 0) { - old_mouse_w += old_mouse_x; - old_mouse_x = 0; - } - if (old_mouse_y < 0) { - old_mouse_h += old_mouse_y; - old_mouse_y = 0; - } - - if (old_mouse_w > _videoMode.screenWidth - old_mouse_x) - old_mouse_w = _videoMode.screenWidth - old_mouse_x; - if (old_mouse_h > _videoMode.screenHeight - old_mouse_y) - old_mouse_h = _videoMode.screenHeight - old_mouse_y; - - // Quick check to see if anything has to be drawn at all - if (old_mouse_w <= 0 || old_mouse_h <= 0) - return; - - - if (SDL_LockSurface(_overlayVisible ? _overlayscreen : _screen) == -1) - error("SDL_LockSurface failed: %s", SDL_GetError()); - - int y; - if (!_overlayVisible) { - byte *dst, *bak = _mouseBackupOld; - - // No need to do clipping here, since drawMouse() did that already - dst = (byte *)_screen->pixels + old_mouse_y * _videoMode.screenWidth + old_mouse_x; - for (y = 0; y < old_mouse_h; ++y, bak += _mouseBackupDim, dst += _videoMode.screenWidth) - memcpy(dst, bak, old_mouse_w); - } else { - byte *dst; - uint16 *bak = (uint16 *)_mouseBackupOld; - - // No need to do clipping here, since drawMouse() did that already - dst = (byte *)_overlayscreen->pixels + (old_mouse_y + 1) * _overlayscreen->pitch + (old_mouse_x + 1) * 2; - for (y = 0; y < old_mouse_h; ++y, bak += _mouseBackupDim, dst += _overlayscreen->pitch) - memcpy(dst, bak, old_mouse_w << 1); - } - - addDirtyRect(old_mouse_x, old_mouse_y, old_mouse_w, old_mouse_h); - - SDL_UnlockSurface(_overlayVisible ? _overlayscreen : _screen); - - _mouseNeedsRedraw = true; -} - -bool OSystem_WINCE3::showMouse(bool visible) { - if (_mouseVisible == visible) - return visible; - - if (visible == false) - undrawMouse(); - - bool last = _mouseVisible; - _mouseVisible = visible; - _mouseNeedsRedraw = true; - - return last; -} - -void OSystem_WINCE3::drawToolbarMouse(SDL_Surface *surf, bool draw) { - - if (!_mouseData || !_usesEmulatedMouse) - return; - - int x = _mouseCurState.x - _mouseHotspotX; - int y = _mouseCurState.y - _mouseHotspotY - _toolbarHandler.getOffset(); - int w = _mouseCurState.w; - int h = _mouseCurState.h; - byte color; - const byte *src = _mouseData; - int width; - - // clip - if (x < 0) { - w += x; - src -= x; - x = 0; - } - if (y < 0) { - h += y; - src -= y * _mouseCurState.w; - y = 0; - } - if (w > surf->w - x) - w = surf->w - x; - if (h > surf->h - y) - h = surf->h - y; - if (w <= 0 || h <= 0) - return; - - if (SDL_LockSurface(surf) == -1) - error("SDL_LockSurface failed at internDrawToolbarMouse: %s", SDL_GetError()); - - uint16 *bak = _mouseBackupToolbar; // toolbar surfaces are 16bpp - uint16 *dst; - dst = (uint16 *)surf->pixels + y * surf->w + x; - - if (draw) { // blit it - while (h > 0) { - width = w; - while (width > 0) { - *bak++ = *dst; - color = *src++; - if (color != _mouseKeyColor) // transparent color - *dst = 0xFFFF; - dst++; - width--; - } - src += _mouseCurState.w - w; - bak += _mouseBackupDim - w; - dst += surf->w - w; - h--; - } - } else { // restore bg - for (y = 0; y < h; ++y, bak += _mouseBackupDim, dst += surf->w) - memcpy(dst, bak, w << 1); - } - - SDL_UnlockSurface(surf); -} - -void OSystem_WINCE3::blitCursor() { -} - -void OSystem_WINCE3::showOverlay() { - assert (_transactionMode == kTransactionNone); - - if (_overlayVisible) - return; - - undrawMouse(); - _overlayVisible = true; - update_scalers(); - clearOverlay(); -} - -void OSystem_WINCE3::hideOverlay() { - assert (_transactionMode == kTransactionNone); - - if (!_overlayVisible) - return; - - undrawMouse(); - _overlayVisible = false; - clearOverlay(); - _forceFull = true; -} - -void OSystem_WINCE3::drawMouse() { - if (!(_toolbarHandler.visible() && _mouseCurState.y >= _toolbarHandler.getOffset() && !_usesEmulatedMouse) && !_forceHideMouse) - internDrawMouse(); -} - -void OSystem_WINCE3::fillMouseEvent(Common::Event &event, int x, int y) { - event.mouse.x = x; - event.mouse.y = y; - - // Update the "keyboard mouse" coords - _km.x = event.mouse.x; - _km.y = event.mouse.y; - - // Adjust for the screen scaling - if (_zoomDown) - event.mouse.y += 240; - - event.mouse.x = event.mouse.x * _scaleFactorXd / _scaleFactorXm; - event.mouse.y = event.mouse.y * _scaleFactorYd / _scaleFactorYm; -} - -void OSystem_WINCE3::retrieve_mouse_location(int &x, int &y) { - x = _mouseCurState.x; - y = _mouseCurState.y; - - x = x * _scaleFactorXm / _scaleFactorXd; - y = y * _scaleFactorYm / _scaleFactorYd; - - if (_zoomDown) - y -= 240; -} - -void OSystem_WINCE3::warpMouse(int x, int y) { - if (_mouseCurState.x != x || _mouseCurState.y != y) { - SDL_WarpMouse(x * _scaleFactorXm / _scaleFactorXd, y * _scaleFactorYm / _scaleFactorYd); - - // SDL_WarpMouse() generates a mouse movement event, so - // set_mouse_pos() would be called eventually. However, the - // cannon script in CoMI calls this function twice each time - // the cannon is reloaded. Unless we update the mouse position - // immediately the second call is ignored, causing the cannon - // to change its aim. - - setMousePos(x, y); - } -} - -void OSystem_WINCE3::addDirtyRect(int x, int y, int w, int h, bool mouseRect) { - - if (_forceFull || _paletteDirtyEnd) - return; - - OSystem_SDL::addDirtyRect(x, y, w, h, false); -} - -static int mapKeyCE(SDLKey key, SDLMod mod, Uint16 unicode, bool unfilter) { - if (GUI::Actions::Instance()->mappingActive()) - return key; - - if (unfilter) { - switch (key) { - case SDLK_ESCAPE: - return SDLK_BACKSPACE; - case SDLK_F8: - return SDLK_ASTERISK; - case SDLK_F9: - return SDLK_HASH; - default: - return key; - } - } - - if (key >= SDLK_KP0 && key <= SDLK_KP9) { - return key - SDLK_KP0 + '0'; - } else if (key >= SDLK_UP && key <= SDLK_PAGEDOWN) { - return key; - } else if (key >= SDLK_NUMLOCK && key <= SDLK_EURO) { - return 0; - } - return key; -} - -bool OSystem_WINCE3::pollEvent(Common::Event &event) { - SDL_Event ev; - ev.type = SDL_NOEVENT; - DWORD currentTime; - bool keyEvent = false; - int deltaX, deltaY; - - memset(&event, 0, sizeof(Common::Event)); - - handleKbdMouse(); - - // If the screen mode changed, send an Common::EVENT_SCREEN_CHANGED - if (_modeChanged) { - _modeChanged = false; - event.type = Common::EVENT_SCREEN_CHANGED; - _screenChangeCount++; - return true; - } - - CEDevice::wakeUp(); - - currentTime = GetTickCount(); - - while (SDL_PollEvent(&ev)) { - switch (ev.type) { - case SDL_KEYDOWN: - debug(1, "Key down %X %s", ev.key.keysym.sym, SDL_GetKeyName((SDLKey)ev.key.keysym.sym)); - // KMOD_RESERVED is used if the key has been injected by an external buffer - if (ev.key.keysym.mod != KMOD_RESERVED && !_unfilteredkeys) { - keyEvent = true; - _lastKeyPressed = ev.key.keysym.sym; - _keyRepeatTime = currentTime; - _keyRepeat = 0; - - if (!GUI_Actions::Instance()->mappingActive() && GUI_Actions::Instance()->performMapped(ev.key.keysym.sym, true)) - return true; - } - - if (GUI_Actions::Instance()->mappingActive()) - event.kbd.flags = 0xFF; - else if (ev.key.keysym.sym == SDLK_PAUSE) { - _lastKeyPressed = 0; - event.type = Common::EVENT_PREDICTIVE_DIALOG; - return true; - } event.type = Common::EVENT_KEYDOWN; - if (!_unfilteredkeys) - event.kbd.keycode = (Common::KeyCode)ev.key.keysym.sym; - else - event.kbd.keycode = (Common::KeyCode)mapKeyCE(ev.key.keysym.sym, ev.key.keysym.mod, ev.key.keysym.unicode, _unfilteredkeys); - event.kbd.ascii = mapKeyCE(ev.key.keysym.sym, ev.key.keysym.mod, ev.key.keysym.unicode, _unfilteredkeys); - - if (ev.key.keysym.mod == KMOD_RESERVED && ev.key.keysym.unicode == KMOD_SHIFT) { - event.kbd.ascii ^= 0x20; - event.kbd.flags = Common::KBD_SHIFT; - } - - return true; - - case SDL_KEYUP: - debug(1, "Key up %X %s", ev.key.keysym.sym, SDL_GetKeyName((SDLKey)ev.key.keysym.sym)); - // KMOD_RESERVED is used if the key has been injected by an external buffer - if (ev.key.keysym.mod != KMOD_RESERVED && !_unfilteredkeys) { - keyEvent = true; - _lastKeyPressed = 0; - - if (!GUI_Actions::Instance()->mappingActive() && GUI_Actions::Instance()->performMapped(ev.key.keysym.sym, false)) - return true; - } - - if (GUI_Actions::Instance()->mappingActive()) - event.kbd.flags = 0xFF; - else if (ev.key.keysym.sym == SDLK_PAUSE) { - _lastKeyPressed = 0; - return false; // chew up the show agi dialog key up event - } - - event.type = Common::EVENT_KEYUP; - if (!_unfilteredkeys) - event.kbd.keycode = (Common::KeyCode)ev.key.keysym.sym; - else - event.kbd.keycode = (Common::KeyCode)mapKeyCE(ev.key.keysym.sym, ev.key.keysym.mod, ev.key.keysym.unicode, _unfilteredkeys); - event.kbd.ascii = mapKeyCE(ev.key.keysym.sym, ev.key.keysym.mod, ev.key.keysym.unicode, _unfilteredkeys); - - if (ev.key.keysym.mod == KMOD_RESERVED && ev.key.keysym.unicode == KMOD_SHIFT) { - event.kbd.ascii ^= 0x20; - event.kbd.flags = Common::KBD_SHIFT; - } - - return true; - - case SDL_MOUSEMOTION: - event.type = Common::EVENT_MOUSEMOVE; - fillMouseEvent(event, ev.motion.x, ev.motion.y); - setMousePos(event.mouse.x, event.mouse.y); - return true; - - case SDL_MOUSEBUTTONDOWN: - if (ev.button.button == SDL_BUTTON_LEFT) - event.type = Common::EVENT_LBUTTONDOWN; - else if (ev.button.button == SDL_BUTTON_RIGHT) - event.type = Common::EVENT_RBUTTONDOWN; - else - break; - fillMouseEvent(event, ev.button.x, ev.button.y); - - - if (event.mouse.x > _tapX) - deltaX = event.mouse.x - _tapX; - else - deltaX = _tapX - event.mouse.x; - if (event.mouse.y > _tapY) - deltaY = event.mouse.y - _tapY; - else - deltaY = _tapY - event.mouse.y; - _closeClick = (deltaX <= 5 && deltaY <= 5); - - if (!_isSmartphone) { - // handle double-taps - if (_tapTime) { // second tap - if (_closeClick && (GetTickCount() - _tapTime < 1000)) { - if (event.mouse.y <= 20 && _panelInitialized) { // top of screen (show panel) - swap_panel_visibility(); - } else if (!_noDoubleTapRMB) { // right click - event.type = Common::EVENT_RBUTTONDOWN; - _rbutton = true; - } - } - _tapTime = 0; - } else { - _tapTime = GetTickCount(); - _tapX = event.mouse.x; - _tapY = event.mouse.y; - } - } - - if (_freeLook && !_closeClick) { - _rbutton = false; - _tapTime = 0; - _tapX = event.mouse.x; - _tapY = event.mouse.y; - event.type = Common::EVENT_MOUSEMOVE; - setMousePos(event.mouse.x, event.mouse.y); - } - - - if (_toolbarHandler.action(event.mouse.x, event.mouse.y, true)) { - if (!_toolbarHandler.drawn()) { - _toolbarHighDrawn = false; - internUpdateScreen(); - } - if (_newOrientation != _orientationLandscape){ - _orientationLandscape = _newOrientation; - _toolbarHighDrawn = false; - ConfMan.setInt("landscape", _orientationLandscape); - ConfMan.flushToDisk(); - hotswapGFXMode(); - } - return false; - } - - return true; - - case SDL_MOUSEBUTTONUP: - if (ev.button.button == SDL_BUTTON_LEFT) - event.type = Common::EVENT_LBUTTONUP; - else if (ev.button.button == SDL_BUTTON_RIGHT) - event.type = Common::EVENT_RBUTTONUP; - else - break; - - if (_rbutton) { - event.type = Common::EVENT_RBUTTONUP; - _rbutton = false; - } - - fillMouseEvent(event, ev.button.x, ev.button.y); - - if (_freeLook && !_closeClick) { - _tapX = event.mouse.x; - _tapY = event.mouse.y; - event.type = Common::EVENT_MOUSEMOVE; - setMousePos(event.mouse.x, event.mouse.y); - } - - if (_toolbarHandler.action(event.mouse.x, event.mouse.y, false)) { - if (!_toolbarHandler.drawn()) { - _toolbarHighDrawn = false; - internUpdateScreen(); - } - return false; - - } - return true; - - case SDL_VIDEOEXPOSE: - _forceFull = true; - break; - - case SDL_QUIT: - event.type = Common::EVENT_QUIT; - return true; - - case SDL_ACTIVEEVENT: - if (ev.active.state & SDL_APPMOUSEFOCUS) - debug(2, "%s mouse focus.", ev.active.gain ? "Got" : "Lost"); - if (ev.active.state & SDL_APPINPUTFOCUS) - debug(2, "%s input focus.", ev.active.gain ? "Got" : "Lost"); - if (ev.active.state & SDL_APPACTIVE) - debug(2, "%s total focus.", ev.active.gain ? "Got" : "Lost"); - if (ev.active.state & SDL_APPINPUTFOCUS) { - _hasfocus = ev.active.gain; - SDL_PauseAudio(!_hasfocus); - _forceFull |= _hasfocus; - } - break; - } - } - - // Simulate repeated key for backend - if (!keyEvent && _lastKeyPressed && currentTime > _keyRepeatTime + _keyRepeatTrigger) { - _keyRepeatTime = currentTime; - _keyRepeat++; - GUI_Actions::Instance()->performMapped(_lastKeyPressed, true); - } - - return false; } void OSystem_WINCE3::quit() { @@ -2497,19 +569,14 @@ void OSystem_WINCE3::getTimeAndDate(TimeDate &t) const { SYSTEMTIME systime; GetLocalTime(&systime); - t.tm_year = systime.wYear - 1900; - t.tm_mon = systime.wMonth - 1; - t.tm_mday = systime.wDay; - t.tm_hour = systime.wHour; - t.tm_min = systime.wMinute; - t.tm_sec = systime.wSecond; + t.tm_year = systime.wYear - 1900; + t.tm_mon = systime.wMonth - 1; + t.tm_mday = systime.wDay; + t.tm_hour = systime.wHour; + t.tm_min = systime.wMinute; + t.tm_sec = systime.wSecond; } int OSystem_WINCE3::_platformScreenWidth; int OSystem_WINCE3::_platformScreenHeight; bool OSystem_WINCE3::_isOzone; -OSystem_WINCE3::zoneDesc OSystem_WINCE3::_zones[TOTAL_ZONES] = { - { 0, 0, 320, 145 }, - { 0, 145, 150, 55 }, - { 150, 145, 170, 55 } -}; diff --git a/backends/platform/wince/wince-sdl.h b/backends/platform/wince/wince-sdl.h index 6cc6e538e1..d7ede8ca81 100644 --- a/backends/platform/wince/wince-sdl.h +++ b/backends/platform/wince/wince-sdl.h @@ -35,7 +35,10 @@ #include "backends/platform/wince/CEkeys/CEKeys.h" #include "backends/platform/wince/CEDevice.h" -#define TOTAL_ZONES 3 +#include "backends/graphics/wincesdl/wincesdl-graphics.h" +#include "backends/events/wincesdl/wincesdl-events.h" +#include "backends/timer/default/default-timer.h" +#include "backends/fs/windows/windows-fs-factory.h" // defines used for implementing the raw frame buffer access method (2003+) #define GETRAWFRAMEBUFFER 0x00020001 @@ -46,202 +49,45 @@ class OSystem_WINCE3 : public OSystem_SDL { public: OSystem_WINCE3(); - - // Update the dirty areas of the screen - void internUpdateScreen(); + virtual ~OSystem_WINCE3(); void setGraphicsModeIntern(); - void initSize(uint w, uint h, const Graphics::PixelFormat *format); void initBackend(); - // Overloaded from SDL backend (toolbar handling) - bool pollEvent(Common::Event &event); - // Overloaded from SDL backend (toolbar handling) - void drawMouse(); - // Overloaded from SDL backend (mouse and new scaler handling) - void fillMouseEvent(Common::Event &event, int x, int y); - // Overloaded from SDL backend (new scaler handling) - void addDirtyRect(int x, int y, int w, int h, bool mouseRect = false); - // Overloaded from SDL backend (new scaler handling) - void warpMouse(int x, int y); // Overloaded from SDL backend void quit(); - // Overloaded from SDL backend (master volume and sample rate subtleties) - void setupMixer(); // Overloaded from OSystem void engineInit(); void getTimeAndDate(TimeDate &t) const; virtual Common::String getDefaultConfigFileName(); + virtual FilesystemFactory *getFilesystemFactory(); - - // Overloaded from SDL_Common (FIXME) - void setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y, uint32 keycolor, int cursorTargetScale, const Graphics::PixelFormat *format); // overloaded by CE backend - void undrawMouse(); - void blitCursor(); - bool showMouse(bool visible); - void setMousePos(int x, int y); - void copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h); // overloaded by CE backend (FIXME) - void copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h); - void showOverlay(); - void hideOverlay(); - Graphics::Surface *lockScreen(); - void unlockScreen(); - - // GUI and action stuff - void swap_panel_visibility(); - void swap_panel(); void swap_sound_master(); - void add_right_click(bool pushed); - void swap_mouse_visibility(); - void swap_freeLook(); - void swap_zoom_up(); - void swap_zoom_down(); - void swap_smartphone_keyboard(); - -//#ifdef WIN32_PLATFORM_WFSP - // Smartphone actions - - void initZones(); - void loadDeviceConfigurationElement(String element, int &value, int defaultValue); - void loadDeviceConfiguration(); - void add_left_click(bool pushed); - void move_cursor_up(); - void move_cursor_down(); - void move_cursor_left(); - void move_cursor_right(); - void switch_zone(); - void smartphone_rotate_display(); -//#endif static int getScreenWidth(); static int getScreenHeight(); static void initScreenInfos(); static bool isOzone(); -protected: - bool loadGFXMode(); - void unloadGFXMode(); - bool hotswapGFXMode(); - bool saveScreenshot(const char *filename); - + static bool _soundMaster; // turn off sound after all calculations + // static since needed by the SDL callback - const GraphicsMode *getSupportedGraphicsModes() const; - bool setGraphicsMode(int mode); - //int getGraphicsMode() const; - int getDefaultGraphicsMode() const; - - bool hasFeature(Feature f); - void setFeatureState(Feature f, bool enable); - bool getFeatureState(Feature f); - - void internDrawMouse(); - void drawToolbarMouse(SDL_Surface *surf, bool draw); +protected: + void initSDL(); + Audio::MixerImpl *_mixer; + DefaultTimerManager *_timer; + FilesystemFactory *_fsFactory; private: - -#ifdef USE_VORBIS - bool checkOggHighSampleRate(); -#endif - - static void private_sound_proc(void *param, byte *buf, int len); - - bool update_scalers(); - void create_toolbar(); - void update_game_settings(); void check_mappings(); - uint32 compute_sample_rate(); - - void retrieve_mouse_location(int &x, int &y); - - CEGUI::ToolbarHandler _toolbarHandler; - SDL_Surface *_toolbarLow; // toolbar 320x40 - SDL_Surface *_toolbarHigh; // toolbar 640x80 - bool _toolbarHighDrawn; // cache toolbar 640x80 - - bool _freeLook; // freeLook mode (do not send mouse button events) - - bool _forceHideMouse; // force invisible mouse cursor - - bool _forcePanelInvisible; // force panel visibility for some cases - bool _panelVisible; // panel visibility - bool _panelStateForced; // panel visibility forced by external call - - bool _panelInitialized; // only initialize the toolbar once - - bool _unfilteredkeys; // discard key mapping temporarily (agi pred. dialog) - static bool _soundMaster; // turn off sound after all calculations - // static since needed by the SDL callback - int _orientationLandscape; // current orientation - int _newOrientation; // new orientation - - bool _saveToolbarState; // save visibility when forced - String _saveActiveToolbar; // save active toolbar when forced - - bool _saveToolbarZoom; // save visibility when zooming - bool _zoomUp; // zooming up mode - bool _zoomDown; // zooming down mode - - bool _noDoubleTapRMB; // disable double tap -> rmb click - bool _rbutton; // double tap -> right button simulation - bool _closeClick; // flag when taps are spatially close together - - bool _usesEmulatedMouse; // emulated mousemove ever been used in this session - - bool _canBeAspectScaled; // game screen size allows for aspect scaling - - int _scaleFactorXm; // scaler X * - int _scaleFactorXd; // scaler X / - int _scaleFactorYm; // scaler Y * - int _scaleFactorYd; // scaler Y / - SDL_Rect _dirtyRectOut[NUM_DIRTY_RECT]; - bool _scalersChanged; - bool _hasfocus; // scummvm has the top window + bool _forcePanelInvisible; // force panel visibility for some cases static int _platformScreenWidth; static int _platformScreenHeight; - static bool _isOzone; // true if running on Windows 2003 SE - - // Keyboard tap - int _tapX; - int _tapY; - long _tapTime; - - // Mouse - int _mouseHotspotX, _mouseHotspotY; - byte *_mouseBackupOld; - uint16 *_mouseBackupToolbar; - uint16 _mouseBackupDim; - - // Smartphone specific variables - - int _lastKeyPressed; // last key pressed - int _keyRepeat; // number of time the last key was repeated - int _keyRepeatTime; // elapsed time since the key was pressed - int _keyRepeatTrigger; // minimum time to consider the key was repeated - - int _repeatX; // repeat trigger for left and right cursor moves - int _repeatY; // repeat trigger for up and down cursor moves - int _stepX1; // offset for left and right cursor moves (slowest) - int _stepX2; // offset for left and right cursor moves (faster) - int _stepX3; // offset for left and right cursor moves (fastest) - int _stepY1; // offset for up and down cursor moves (slowest) - int _stepY2; // offset for up and down cursor moves (faster) - int _stepY3; // offset for up and down cursor moves (fastest) - - int _mouseXZone[TOTAL_ZONES]; - int _mouseYZone[TOTAL_ZONES]; - int _currentZone; - - struct zoneDesc { - int x; - int y; - int width; - int height; - }; - - static zoneDesc _zones[TOTAL_ZONES]; + static bool _isOzone; // true if running on Windows 2003 SE + }; #endif diff --git a/base/commandLine.cpp b/base/commandLine.cpp index 36218376c8..f920dd0170 100644 --- a/base/commandLine.cpp +++ b/base/commandLine.cpp @@ -178,6 +178,9 @@ void registerDefaults() { ConfMan.registerDefault("midi_gain", 100); // ConfMan.registerDefault("music_driver", ???); + ConfMan.registerDefault("mt32_device", "null"); + ConfMan.registerDefault("gm_device", "null"); + ConfMan.registerDefault("cdrom", 0); // Game specific diff --git a/common/config-manager.cpp b/common/config-manager.cpp index 0561f390a4..c8c0999a25 100644 --- a/common/config-manager.cpp +++ b/common/config-manager.cpp @@ -103,9 +103,9 @@ void ConfigManager::loadConfigFile(const String &filename) { FSNode node(filename); File cfg_file; if (!cfg_file.open(node)) { - debug("Creating configuration file: %s\n", filename.c_str()); + debug("Creating configuration file: %s", filename.c_str()); } else { - debug("Using configuration file: %s\n", _filename.c_str()); + debug("Using configuration file: %s", _filename.c_str()); loadFromStream(cfg_file); } } diff --git a/common/macresman.cpp b/common/macresman.cpp index 4fd4e72ee3..e7d4a30789 100644 --- a/common/macresman.cpp +++ b/common/macresman.cpp @@ -550,118 +550,110 @@ void MacResManager::readMap() { } } -void MacResManager::convertCrsrCursor(byte *data, int datasize, byte **cursor, int *w, int *h, - int *hotspot_x, int *hotspot_y, int *keycolor, bool colored, byte **palette, int *palSize) { - MemoryReadStream dis(data, datasize); - int i, b; - byte imageByte; - byte *iconData; - int pixelsPerByte, bpp; - int ctSize; - byte bitmask; - int iconRowBytes, iconBounds[4]; - int iconDataSize; - - dis.readUint16BE(); // type - dis.readUint32BE(); // offset to pixel map - dis.readUint32BE(); // offset to pixel data - dis.readUint32BE(); // expanded cursor data - dis.readUint16BE(); // expanded data depth - dis.readUint32BE(); // reserved +void MacResManager::convertCrsrCursor(SeekableReadStream *data, byte **cursor, int &w, int &h, int &hotspotX, + int &hotspotY, int &keycolor, bool colored, byte **palette, int &palSize) { + + data->readUint16BE(); // type + data->readUint32BE(); // offset to pixel map + data->readUint32BE(); // offset to pixel data + data->readUint32BE(); // expanded cursor data + data->readUint16BE(); // expanded data depth + data->readUint32BE(); // reserved // Grab B/W icon data - *cursor = (byte *)malloc(16 * 16); - for (i = 0; i < 32; i++) { - imageByte = dis.readByte(); - for (b = 0; b < 8; b++) - cursor[0][i*8+b] = (byte)((imageByte & (0x80 >> b)) > 0? 0x0F: 0x00); + *cursor = new byte[16 * 16]; + for (int i = 0; i < 32; i++) { + byte imageByte = data->readByte(); + for (int b = 0; b < 8; b++) + cursor[0][i * 8 + b] = (byte)((imageByte & (0x80 >> b)) > 0 ? 0x0F : 0x00); } // Apply mask data - for (i = 0; i < 32; i++) { - imageByte = dis.readByte(); - for (b = 0; b < 8; b++) + for (int i = 0; i < 32; i++) { + byte imageByte = data->readByte(); + for (int b = 0; b < 8; b++) if ((imageByte & (0x80 >> b)) == 0) - cursor[0][i*8+b] = 0xff; + cursor[0][i * 8 + b] = 0xff; } - *hotspot_y = dis.readUint16BE(); - *hotspot_x = dis.readUint16BE(); - *w = *h = 16; - *keycolor = 0xff; + hotspotY = data->readUint16BE(); + hotspotX = data->readUint16BE(); + w = h = 16; + keycolor = 0xff; // Use b/w cursor on backends which don't support cursor palettes if (!colored) return; - dis.readUint32BE(); // reserved - dis.readUint32BE(); // cursorID + data->readUint32BE(); // reserved + data->readUint32BE(); // cursorID // Color version of cursor - dis.readUint32BE(); // baseAddr + data->readUint32BE(); // baseAddr // Keep only lowbyte for now - dis.readByte(); - iconRowBytes = dis.readByte(); + data->readByte(); + int iconRowBytes = data->readByte(); if (!iconRowBytes) return; - iconBounds[0] = dis.readUint16BE(); - iconBounds[1] = dis.readUint16BE(); - iconBounds[2] = dis.readUint16BE(); - iconBounds[3] = dis.readUint16BE(); + int iconBounds[4]; + iconBounds[0] = data->readUint16BE(); + iconBounds[1] = data->readUint16BE(); + iconBounds[2] = data->readUint16BE(); + iconBounds[3] = data->readUint16BE(); - dis.readUint16BE(); // pmVersion - dis.readUint16BE(); // packType - dis.readUint32BE(); // packSize + data->readUint16BE(); // pmVersion + data->readUint16BE(); // packType + data->readUint32BE(); // packSize - dis.readUint32BE(); // hRes - dis.readUint32BE(); // vRes + data->readUint32BE(); // hRes + data->readUint32BE(); // vRes - dis.readUint16BE(); // pixelType - dis.readUint16BE(); // pixelSize - dis.readUint16BE(); // cmpCount - dis.readUint16BE(); // cmpSize + data->readUint16BE(); // pixelType + data->readUint16BE(); // pixelSize + data->readUint16BE(); // cmpCount + data->readUint16BE(); // cmpSize - dis.readUint32BE(); // planeByte - dis.readUint32BE(); // pmTable - dis.readUint32BE(); // reserved + data->readUint32BE(); // planeByte + data->readUint32BE(); // pmTable + data->readUint32BE(); // reserved // Pixel data for cursor - iconDataSize = iconRowBytes * (iconBounds[3] - iconBounds[1]); - iconData = (byte *)malloc(iconDataSize); - dis.read(iconData, iconDataSize); + int iconDataSize = iconRowBytes * (iconBounds[3] - iconBounds[1]); + byte *iconData = new byte[iconDataSize]; + data->read(iconData, iconDataSize); // Color table - dis.readUint32BE(); // ctSeed - dis.readUint16BE(); // ctFlag - ctSize = dis.readUint16BE() + 1; + data->readUint32BE(); // ctSeed + data->readUint16BE(); // ctFlag + uint16 ctSize = data->readUint16BE() + 1; - *palette = (byte *)malloc(ctSize * 3); + *palette = new byte[ctSize * 3]; // Read just high byte of 16-bit color for (int c = 0; c < ctSize; c++) { // We just use indices 0..ctSize, so ignore color ID - dis.readUint16BE(); // colorID[c] + data->readUint16BE(); // colorID[c] - palette[0][c * 3 + 0] = dis.readByte(); - dis.readByte(); + palette[0][c * 3 + 0] = data->readByte(); + data->readByte(); - palette[0][c * 3 + 1] = dis.readByte(); - dis.readByte(); + palette[0][c * 3 + 1] = data->readByte(); + data->readByte(); - palette[0][c * 3 + 2] = dis.readByte(); - dis.readByte(); + palette[0][c * 3 + 2] = data->readByte(); + data->readByte(); } - *palSize = ctSize; + palSize = ctSize; - pixelsPerByte = (iconBounds[2] - iconBounds[0]) / iconRowBytes; - bpp = 8 / pixelsPerByte; + int pixelsPerByte = (iconBounds[2] - iconBounds[0]) / iconRowBytes; + int bpp = 8 / pixelsPerByte; // build a mask to make sure the pixels are properly shifted out - bitmask = 0; + int bitmask = 0; for (int m = 0; m < bpp; m++) { bitmask <<= 1; bitmask |= 1; @@ -669,16 +661,16 @@ void MacResManager::convertCrsrCursor(byte *data, int datasize, byte **cursor, i // Extract pixels from bytes for (int j = 0; j < iconDataSize; j++) - for (b = 0; b < pixelsPerByte; b++) { + for (int b = 0; b < pixelsPerByte; b++) { int idx = j * pixelsPerByte + (pixelsPerByte - 1 - b); if (cursor[0][idx] != 0xff) // if mask is not there cursor[0][idx] = (byte)((iconData[j] >> (b * bpp)) & bitmask); } - free(iconData); + delete[] iconData; - assert(datasize - dis.pos() == 0); + assert(data->size() - data->pos() == 0); } } // End of namespace Common diff --git a/common/macresman.h b/common/macresman.h index 1cbebbcf4b..2ad0b608a1 100644 --- a/common/macresman.h +++ b/common/macresman.h @@ -154,14 +154,13 @@ public: /** * Convert cursor from crsr format to format suitable for feeding to CursorMan - * @param data Pointer to the cursor data - * @param datasize Size of the cursor data + * @param data Pointer to the cursor datax * @param cursor Pointer to memory where result cursor will be stored. The memory * block will be malloc()'ed * @param w Pointer to int where the cursor width will be stored * @param h Pointer to int where the cursor height will be stored - * @param hotspot_x Storage for cursor hotspot X coordinate - * @param hotspot_Y Storage for cursor hotspot Y coordinate + * @param hotspotX Storage for cursor hotspot X coordinate + * @param hotspotY Storage for cursor hotspot Y coordinate * @param keycolor Pointer to int where the transpared color value will be stored * @param colored If set to true then colored cursor will be returned (if any). * b/w version will be used otherwise @@ -169,8 +168,8 @@ public: * The memory will be malloc()'ed * @param palSize Pointer to integer where the palette size will be stored. */ - static void convertCrsrCursor(byte *data, int datasize, byte **cursor, int *w, int *h, - int *hotspot_x, int *hotspot_y, int *keycolor, bool colored, byte **palette, int *palSize); + static void convertCrsrCursor(SeekableReadStream *data, byte **cursor, int &w, int &h, int &hotspotX, + int &hotspotY, int &keycolor, bool colored, byte **palette, int &palSize); /** * Return list of resource IDs with specified type ID diff --git a/common/module.mk b/common/module.mk index a4cea5c23f..a57de6a4b8 100644 --- a/common/module.mk +++ b/common/module.mk @@ -17,7 +17,6 @@ MODULE_OBJS := \ memorypool.o \ md5.o \ mutex.o \ - ne_exe.o \ random.o \ rational.o \ str.o \ @@ -29,6 +28,9 @@ MODULE_OBJS := \ unarj.o \ unzip.o \ util.o \ + winexe.o \ + winexe_ne.o \ + winexe_pe.o \ xmlparser.o \ zlib.o diff --git a/common/ne_exe.cpp b/common/ne_exe.cpp deleted file mode 100644 index 34bb551b06..0000000000 --- a/common/ne_exe.cpp +++ /dev/null @@ -1,612 +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. - * - * $URL$ - * $Id$ - * - */ - -#include "common/debug.h" -#include "common/file.h" -#include "common/memstream.h" -#include "common/ne_exe.h" -#include "common/str.h" -#include "common/stream.h" - -namespace Common { - -NECursor::NECursor() { - _width = 0; - _height = 0; - _hotspotX = 0; - _hotspotY = 0; - _surface = 0; - memset(_palette, 0, 256 * 3); -} - -NECursor::~NECursor() { - clear(); -} - -uint16 NECursor::getWidth() const { - return _width; -} - -uint16 NECursor::getHeight() const { - return _height; -} - -uint16 NECursor::getHotspotX() const { - return _hotspotX; -} - -uint16 NECursor::getHotspotY() const { - return _hotspotY; -} - -void NECursor::setDimensions(uint16 width, uint16 height) { - _width = width; - _height = height; -} - -void NECursor::setHotspot(uint16 x, uint16 y) { - _hotspotX = x; - _hotspotY = y; -} - -bool NECursor::readCursor(SeekableReadStream &stream, uint32 count) { - clear(); - - SeekableReadStream *bitmap = stream.readStream(count); - _surface = new byte[_width * _height]; - - uint32 width = _width; - uint32 height = _height * 2; - - // Sanity checks - assert((width > 0) && (height > 0)); - - // Check header size - if (bitmap->readUint32LE() != 40) - return false; - - // Check dimensions - if (bitmap->readUint32LE() != width) - return false; - if (bitmap->readUint32LE() != height) - return false; - - // Color planes - if (bitmap->readUint16LE() != 1) - return false; - // Bits per pixel - if (bitmap->readUint16LE() != 1) - return false; - // Compression - if (bitmap->readUint32LE() != 0) - return false; - - // Image size + X resolution + Y resolution - bitmap->skip(12); - - uint32 numColors = bitmap->readUint32LE(); - - if (numColors == 0) - numColors = 2; - else if (numColors > 2) - return false; - - // Assert that enough data is there for the whole cursor - if ((uint32)bitmap->size() < 40 + numColors * 4 + width * height / 8) - return false; - - // Height includes AND-mask and XOR-mask - height /= 2; - - // Standard palette: transparent, black, white - _palette[6] = 0xff; - _palette[7] = 0xff; - _palette[8] = 0xff; - - // Reading the palette - bitmap->seek(40); - for (uint32 i = 0 ; i < numColors; i++) { - _palette[(i + 1) * 3 + 2] = bitmap->readByte(); - _palette[(i + 1) * 3 + 1] = bitmap->readByte(); - _palette[(i + 1) * 3 + 0] = bitmap->readByte(); - bitmap->readByte(); - } - - // Reading the bitmap data - uint32 dataSize = bitmap->size() - 40 - numColors * 4; - byte *initialSource = new byte[dataSize]; - bitmap->read(initialSource, dataSize); - const byte *srcP = initialSource; - const byte *srcM = srcP + ((width * height) / 8); - byte *dest = _surface + width * (height - 1); - - for (uint32 i = 0; i < height; i++) { - byte *rowDest = dest; - - for (uint32 j = 0; j < (width / 8); j++) { - byte p = srcP[j]; - byte m = srcM[j]; - - for (int k = 0; k < 8; k++, rowDest++, p <<= 1, m <<= 1) { - if ((m & 0x80) != 0x80) { - if ((p & 0x80) == 0x80) - *rowDest = 2; - else - *rowDest = 1; - } else - *rowDest = 0; - } - } - - dest -= width; - srcP += width / 8; - srcM += width / 8; - } - - delete bitmap; - delete[] initialSource; - return true; -} - -void NECursor::clear() { - delete[] _surface; _surface = 0; -} - -NEResourceID &NEResourceID::operator=(String string) { - _name = string; - _idType = kIDTypeString; - return *this; -} - -NEResourceID &NEResourceID::operator=(uint16 x) { - _id = x; - _idType = kIDTypeNumerical; - return *this; -} - -bool NEResourceID::operator==(const String &x) const { - return _idType == kIDTypeString && _name.equalsIgnoreCase(x); -} - -bool NEResourceID::operator==(const uint16 &x) const { - return _idType == kIDTypeNumerical && _id == x; -} - -bool NEResourceID::operator==(const NEResourceID &x) const { - if (_idType != x._idType) - return false; - if (_idType == kIDTypeString) - return _name.equalsIgnoreCase(x._name); - if (_idType == kIDTypeNumerical) - return _id == x._id; - return true; -} - -String NEResourceID::getString() const { - if (_idType != kIDTypeString) - return ""; - - return _name; -} - -uint16 NEResourceID::getID() const { - if (_idType != kIDTypeNumerical) - return 0xffff; - - return _idType; -} - -String NEResourceID::toString() const { - if (_idType == kIDTypeString) - return _name; - else if (_idType == kIDTypeNumerical) - return String::format("%04x", _id); - - return ""; -} - -NEResources::NEResources() { - _exe = 0; -} - -NEResources::~NEResources() { - clear(); -} - -void NEResources::clear() { - if (_exe) { - delete _exe; - _exe = 0; - } - - _resources.clear(); - - for (uint32 i = 0; i < _cursors.size(); i++) - for (uint32 j = 0; j < _cursors[i].cursors.size(); j++) - delete _cursors[i].cursors[j]; - - _cursors.clear(); -} - -const Array<NECursorGroup> &NEResources::getCursors() const { - return _cursors; -} - -bool NEResources::loadFromEXE(const String &fileName) { - if (fileName.empty()) - return false; - - File *file = new File(); - - if (!file->open(fileName)) { - delete file; - return false; - } - - return loadFromEXE(file); -} - -bool NEResources::loadFromEXE(SeekableReadStream *stream) { - clear(); - - if (!stream) - return false; - - _exe = stream; - - uint32 offsetResourceTable = getResourceTableOffset(); - if (offsetResourceTable == 0xFFFFFFFF) - return false; - if (offsetResourceTable == 0) - return true; - - if (!readResourceTable(offsetResourceTable)) - return false; - - if (!readCursors()) - return false; - - return true; -} - -bool NEResources::loadFromCompressedEXE(const String &fileName) { - // Based on http://www.cabextract.org.uk/libmspack/doc/szdd_kwaj_format.html - - File file; - - if (!file.open(fileName)) - return false; - - // First part of the signature - if (file.readUint32BE() != MKID_BE('SZDD')) - return false; - - // Second part of the signature - if (file.readUint32BE() != 0x88F02733) - return false; - - // Compression mode must be 'A' - if (file.readByte() != 'A') - return false; - - file.readByte(); // file name character change - uint32 unpackedLength = file.readUint32LE(); - - byte *window = new byte[0x1000]; - int pos = 0x1000 - 16; - memset(window, 0x20, 0x1000); // Initialize to all spaces - - byte *unpackedData = (byte *)malloc(unpackedLength); - byte *dataPos = unpackedData; - - // Apply simple LZSS decompression - for (;;) { - byte controlByte = file.readByte(); - - if (file.eos()) - break; - - for (byte i = 0; i < 8; i++) { - if (controlByte & (1 << i)) { - *dataPos++ = window[pos++] = file.readByte(); - pos &= 0xFFF; - } else { - int matchPos = file.readByte(); - int matchLen = file.readByte(); - matchPos |= (matchLen & 0xF0) << 4; - matchLen = (matchLen & 0xF) + 3; - while (matchLen--) { - *dataPos++ = window[pos++] = window[matchPos++]; - pos &= 0xFFF; - matchPos &= 0xFFF; - } - } - - } - } - - delete[] window; - SeekableReadStream *stream = new MemoryReadStream(unpackedData, unpackedLength); - - return loadFromEXE(stream); -} - -uint32 NEResources::getResourceTableOffset() { - if (!_exe) - return 0xFFFFFFFF; - - if (!_exe->seek(0)) - return 0xFFFFFFFF; - - // 'MZ' - if (_exe->readUint16BE() != 0x4D5A) - return 0xFFFFFFFF; - - if (!_exe->seek(60)) - return 0xFFFFFFFF; - - uint32 offsetSegmentEXE = _exe->readUint16LE(); - if (!_exe->seek(offsetSegmentEXE)) - return 0xFFFFFFFF; - - // 'NE' - if (_exe->readUint16BE() != 0x4E45) - return 0xFFFFFFFF; - - if (!_exe->seek(offsetSegmentEXE + 36)) - return 0xFFFFFFFF; - - uint32 offsetResourceTable = _exe->readUint16LE(); - if (offsetResourceTable == 0) - // No resource table - return 0; - - // Offset relative to the segment _exe header - offsetResourceTable += offsetSegmentEXE; - if (!_exe->seek(offsetResourceTable)) - return 0xFFFFFFFF; - - return offsetResourceTable; -} - -static const char *s_resTypeNames[] = { - "", "cursor", "bitmap", "icon", "menu", "dialog", "string", - "font_dir", "font", "accelerator", "rc_data", "msg_table", - "group_cursor", "group_icon", "version", "dlg_include", - "plug_play", "vxd", "ani_cursor", "ani_icon", "html", - "manifest" -}; - -bool NEResources::readResourceTable(uint32 offset) { - if (!_exe) - return false; - - if (!_exe->seek(offset)) - return false; - - uint32 align = 1 << _exe->readUint16LE(); - - uint16 typeID = _exe->readUint16LE(); - while (typeID != 0) { - uint16 resCount = _exe->readUint16LE(); - - _exe->skip(4); // reserved - - for (int i = 0; i < resCount; i++) { - Resource res; - - // Resource properties - res.offset = _exe->readUint16LE() * align; - res.size = _exe->readUint16LE() * align; - res.flags = _exe->readUint16LE(); - uint16 id = _exe->readUint16LE(); - res.handle = _exe->readUint16LE(); - res.usage = _exe->readUint16LE(); - - res.type = typeID; - - if ((id & 0x8000) == 0) - res.id = getResourceString(*_exe, offset + id); - else - res.id = id & 0x7FFF; - - if (typeID & 0x8000 && ((typeID & 0x7FFF) < ARRAYSIZE(s_resTypeNames))) - debug(2, "Found resource %s %s", s_resTypeNames[typeID & 0x7FFF], res.id.toString().c_str()); - else - debug(2, "Found resource %04x %s", typeID, res.id.toString().c_str()); - - _resources.push_back(res); - } - - typeID = _exe->readUint16LE(); - } - - return true; -} - -String NEResources::getResourceString(SeekableReadStream &exe, uint32 offset) { - uint32 curPos = exe.pos(); - - if (!exe.seek(offset)) { - exe.seek(curPos); - return ""; - } - - uint8 length = exe.readByte(); - - String string; - for (uint16 i = 0; i < length; i++) - string += (char)exe.readByte(); - - exe.seek(curPos); - return string; -} - -const NEResources::Resource *NEResources::findResource(uint16 type, NEResourceID id) const { - for (List<Resource>::const_iterator it = _resources.begin(); it != _resources.end(); ++it) - if (it->type == type && it->id == id) - return &*it; - - return 0; -} - -SeekableReadStream *NEResources::getResource(uint16 type, NEResourceID id) { - const Resource *res = findResource(type, id); - - if (!res) - return 0; - - _exe->seek(res->offset); - return _exe->readStream(res->size); -} - -const Array<NEResourceID> NEResources::getIDList(uint16 type) const { - Array<NEResourceID> idArray; - - for (List<Resource>::const_iterator it = _resources.begin(); it != _resources.end(); ++it) - if (it->type == type) - idArray.push_back(it->id); - - return idArray; -} - -bool NEResources::readCursors() { - uint32 cursorCount = 0; - - for (List<Resource>::const_iterator it = _resources.begin(); it != _resources.end(); ++it) - if (it->type == kNEGroupCursor) - cursorCount++; - - if (cursorCount == 0) { - _cursors.clear(); - return true; - } - - _cursors.resize(cursorCount); - - Array<NECursorGroup>::iterator cursorGroup = _cursors.begin(); - for (List<Resource>::const_iterator it = _resources.begin(); it != _resources.end(); ++it) { - if (it->type == kNEGroupCursor) { - if (!readCursorGroup(*cursorGroup, *it)) - return false; - - ++cursorGroup; - } - } - - return true; -} - -bool NEResources::readCursorGroup(NECursorGroup &group, const Resource &resource) { - if (!_exe) - return false; - - if (resource.size <= 6) - return false; - - if (!_exe->seek(resource.offset)) - return false; - - byte *data = new byte[resource.size]; - - if (!_exe->read(data, resource.size)) { - delete[] data; - return false; - } - - uint32 cursorCount = READ_LE_UINT16(data + 4); - if (resource.size < (6 + cursorCount * 16)) { - delete[] data; - return false; - } - - group.cursors.resize(cursorCount); - - uint32 offset = 6; - for (uint32 i = 0; i < cursorCount; i++) { - group.cursors[i] = new NECursor(); - NECursor *cursor = group.cursors[i]; - - // Plane count - if (READ_LE_UINT16(data + offset + 4) != 1) { - delete[] data; - return false; - } - - // Bit count - if (READ_LE_UINT16(data + offset + 6) != 1) { - delete[] data; - return false; - } - - uint32 id = READ_LE_UINT32(data + offset + 12); - const Resource *cursorResource = findResource(kNECursor, id); - if (!cursorResource) { - delete[] data; - return false; - } - - cursor->setDimensions(READ_LE_UINT16(data + offset), READ_LE_UINT16(data + offset + 2) / 2); - - uint32 dataSize = READ_LE_UINT32(data + offset + 8); - if (!readCursor(*cursor, *cursorResource, dataSize)) { - delete[] data; - return false; - } - - offset += 16; - } - - group.id = resource.id; - - delete[] data; - return true; -} - -bool NEResources::readCursor(NECursor &cursor, const Resource &resource, uint32 size) { - if (!_exe) - return false; - - if (size <= 4) - return false; - if (resource.size < size) - return false; - - if (!_exe->seek(resource.offset)) - return false; - - uint32 hotspotX = _exe->readUint16LE(); - uint32 hotspotY = _exe->readUint16LE(); - cursor.setHotspot(hotspotX, hotspotY); - - size -= 4; - - if (!cursor.readCursor(*_exe, size)) - return false; - - return true; -} - -} // End of namespace Common diff --git a/common/winexe.cpp b/common/winexe.cpp new file mode 100644 index 0000000000..9602e84c88 --- /dev/null +++ b/common/winexe.cpp @@ -0,0 +1,84 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/str.h" +#include "common/winexe.h" + +namespace Common { + +WinResourceID &WinResourceID::operator=(const String &x) { + _name = x; + _idType = kIDTypeString; + return *this; +} + +WinResourceID &WinResourceID::operator=(uint32 x) { + _id = x; + _idType = kIDTypeNumerical; + return *this; +} + +bool WinResourceID::operator==(const String &x) const { + return _idType == kIDTypeString && _name.equalsIgnoreCase(x); +} + +bool WinResourceID::operator==(const uint32 &x) const { + return _idType == kIDTypeNumerical && _id == x; +} + +bool WinResourceID::operator==(const WinResourceID &x) const { + if (_idType != x._idType) + return false; + if (_idType == kIDTypeString) + return _name.equalsIgnoreCase(x._name); + if (_idType == kIDTypeNumerical) + return _id == x._id; + return true; +} + +String WinResourceID::getString() const { + if (_idType != kIDTypeString) + return ""; + + return _name; +} + +uint32 WinResourceID::getID() const { + if (_idType != kIDTypeNumerical) + return 0xffffffff; + + return _id; +} + +String WinResourceID::toString() const { + if (_idType == kIDTypeString) + return _name; + else if (_idType == kIDTypeNumerical) + return String::format("%08x", _id); + + return ""; +} + +} // End of namespace Common diff --git a/common/winexe.h b/common/winexe.h new file mode 100644 index 0000000000..af0d70c555 --- /dev/null +++ b/common/winexe.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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef COMMON_WINEXE_H +#define COMMON_WINEXE_H + +#include "common/hash-str.h" + +namespace Common { + +class String; + +class WinResourceID { +public: + WinResourceID() { _idType = kIDTypeNull; } + WinResourceID(String x) { _idType = kIDTypeString; _name = x; } + WinResourceID(uint32 x) { _idType = kIDTypeNumerical; _id = x; } + + WinResourceID &operator=(const String &x); + WinResourceID &operator=(uint32 x); + + bool operator==(const String &x) const; + bool operator==(const uint32 &x) const; + bool operator==(const WinResourceID &x) const; + + String getString() const; + uint32 getID() const; + String toString() const; + +private: + /** An ID Type. */ + enum IDType { + kIDTypeNull, ///< No type set + kIDTypeNumerical, ///< A numerical ID. + kIDTypeString ///< A string ID. + } _idType; + + String _name; ///< The resource's string ID. + uint32 _id; ///< The resource's numerical ID. +}; + +struct WinResourceID_Hash { + uint operator()(const WinResourceID &id) const { return hashit(id.toString()); } +}; + +struct WinResourceID_EqualTo { + bool operator()(const WinResourceID &id1, const WinResourceID &id2) const { return id1 == id2; } +}; + +} // End of namespace Common + +#endif diff --git a/common/winexe_ne.cpp b/common/winexe_ne.cpp new file mode 100644 index 0000000000..24e51f1c9e --- /dev/null +++ b/common/winexe_ne.cpp @@ -0,0 +1,291 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/debug.h" +#include "common/file.h" +#include "common/memstream.h" +#include "common/str.h" +#include "common/stream.h" +#include "common/winexe_ne.h" + +namespace Common { + +NEResources::NEResources() { + _exe = 0; +} + +NEResources::~NEResources() { + clear(); +} + +void NEResources::clear() { + if (_exe) { + delete _exe; + _exe = 0; + } + + _resources.clear(); +} + +bool NEResources::loadFromEXE(const String &fileName) { + if (fileName.empty()) + return false; + + File *file = new File(); + + if (!file->open(fileName)) { + delete file; + return false; + } + + return loadFromEXE(file); +} + +bool NEResources::loadFromEXE(SeekableReadStream *stream) { + clear(); + + if (!stream) + return false; + + _exe = stream; + + uint32 offsetResourceTable = getResourceTableOffset(); + if (offsetResourceTable == 0xFFFFFFFF) + return false; + if (offsetResourceTable == 0) + return true; + + if (!readResourceTable(offsetResourceTable)) + return false; + + return true; +} + +bool NEResources::loadFromCompressedEXE(const String &fileName) { + // Based on http://www.cabextract.org.uk/libmspack/doc/szdd_kwaj_format.html + + // TODO: Merge this with with loadFromEXE() so the handling of the compressed + // EXE's is transparent + + File file; + + if (!file.open(fileName)) + return false; + + // First part of the signature + if (file.readUint32BE() != MKID_BE('SZDD')) + return false; + + // Second part of the signature + if (file.readUint32BE() != 0x88F02733) + return false; + + // Compression mode must be 'A' + if (file.readByte() != 'A') + return false; + + file.readByte(); // file name character change + uint32 unpackedLength = file.readUint32LE(); + + byte *window = new byte[0x1000]; + int pos = 0x1000 - 16; + memset(window, 0x20, 0x1000); // Initialize to all spaces + + byte *unpackedData = (byte *)malloc(unpackedLength); + assert(unpackedData); + byte *dataPos = unpackedData; + + // Apply simple LZSS decompression + for (;;) { + byte controlByte = file.readByte(); + + if (file.eos()) + break; + + for (byte i = 0; i < 8; i++) { + if (controlByte & (1 << i)) { + *dataPos++ = window[pos++] = file.readByte(); + pos &= 0xFFF; + } else { + int matchPos = file.readByte(); + int matchLen = file.readByte(); + matchPos |= (matchLen & 0xF0) << 4; + matchLen = (matchLen & 0xF) + 3; + while (matchLen--) { + *dataPos++ = window[pos++] = window[matchPos++]; + pos &= 0xFFF; + matchPos &= 0xFFF; + } + } + + } + } + + delete[] window; + SeekableReadStream *stream = new MemoryReadStream(unpackedData, unpackedLength); + + return loadFromEXE(stream); +} + +uint32 NEResources::getResourceTableOffset() { + if (!_exe) + return 0xFFFFFFFF; + + if (!_exe->seek(0)) + return 0xFFFFFFFF; + + // 'MZ' + if (_exe->readUint16BE() != 0x4D5A) + return 0xFFFFFFFF; + + if (!_exe->seek(60)) + return 0xFFFFFFFF; + + uint32 offsetSegmentEXE = _exe->readUint16LE(); + if (!_exe->seek(offsetSegmentEXE)) + return 0xFFFFFFFF; + + // 'NE' + if (_exe->readUint16BE() != 0x4E45) + return 0xFFFFFFFF; + + if (!_exe->seek(offsetSegmentEXE + 36)) + return 0xFFFFFFFF; + + uint32 offsetResourceTable = _exe->readUint16LE(); + if (offsetResourceTable == 0) + // No resource table + return 0; + + // Offset relative to the segment _exe header + offsetResourceTable += offsetSegmentEXE; + if (!_exe->seek(offsetResourceTable)) + return 0xFFFFFFFF; + + return offsetResourceTable; +} + +static const char *s_resTypeNames[] = { + "", "cursor", "bitmap", "icon", "menu", "dialog", "string", + "font_dir", "font", "accelerator", "rc_data", "msg_table", + "group_cursor", "group_icon", "version", "dlg_include", + "plug_play", "vxd", "ani_cursor", "ani_icon", "html", + "manifest" +}; + +bool NEResources::readResourceTable(uint32 offset) { + if (!_exe) + return false; + + if (!_exe->seek(offset)) + return false; + + uint32 align = 1 << _exe->readUint16LE(); + + uint16 typeID = _exe->readUint16LE(); + while (typeID != 0) { + uint16 resCount = _exe->readUint16LE(); + + _exe->skip(4); // reserved + + for (int i = 0; i < resCount; i++) { + Resource res; + + // Resource properties + res.offset = _exe->readUint16LE() * align; + res.size = _exe->readUint16LE() * align; + res.flags = _exe->readUint16LE(); + uint16 id = _exe->readUint16LE(); + res.handle = _exe->readUint16LE(); + res.usage = _exe->readUint16LE(); + + res.type = typeID; + + if ((id & 0x8000) == 0) + res.id = getResourceString(*_exe, offset + id); + else + res.id = id & 0x7FFF; + + if (typeID & 0x8000 && ((typeID & 0x7FFF) < ARRAYSIZE(s_resTypeNames))) + debug(2, "Found resource %s %s", s_resTypeNames[typeID & 0x7FFF], res.id.toString().c_str()); + else + debug(2, "Found resource %04x %s", typeID, res.id.toString().c_str()); + + _resources.push_back(res); + } + + typeID = _exe->readUint16LE(); + } + + return true; +} + +String NEResources::getResourceString(SeekableReadStream &exe, uint32 offset) { + uint32 curPos = exe.pos(); + + if (!exe.seek(offset)) { + exe.seek(curPos); + return ""; + } + + uint8 length = exe.readByte(); + + String string; + for (uint16 i = 0; i < length; i++) + string += (char)exe.readByte(); + + exe.seek(curPos); + return string; +} + +const NEResources::Resource *NEResources::findResource(uint16 type, WinResourceID id) const { + for (List<Resource>::const_iterator it = _resources.begin(); it != _resources.end(); ++it) + if (it->type == type && it->id == id) + return &*it; + + return 0; +} + +SeekableReadStream *NEResources::getResource(uint16 type, WinResourceID id) { + const Resource *res = findResource(type, id); + + if (!res) + return 0; + + _exe->seek(res->offset); + return _exe->readStream(res->size); +} + +const Array<WinResourceID> NEResources::getIDList(uint16 type) const { + Array<WinResourceID> idArray; + + for (List<Resource>::const_iterator it = _resources.begin(); it != _resources.end(); ++it) + if (it->type == type) + idArray.push_back(it->id); + + return idArray; +} + +} // End of namespace Common diff --git a/common/ne_exe.h b/common/winexe_ne.h index 545c8e5eff..c1d04080ba 100644 --- a/common/ne_exe.h +++ b/common/winexe_ne.h @@ -23,11 +23,12 @@ * */ -#ifndef COMMON_NE_EXE_H -#define COMMON_NE_EXE_H +#ifndef COMMON_WINEXE_NE_H +#define COMMON_WINEXE_NE_H #include "common/array.h" #include "common/list.h" +#include "common/winexe.h" namespace Common { @@ -35,80 +36,6 @@ class MemoryReadStream; class SeekableReadStream; class String; -/** A New Executable cursor. */ -class NECursor { -public: - NECursor(); - ~NECursor(); - - /** Return the cursor's width. */ - uint16 getWidth() const; - /** Return the cursor's height. */ - uint16 getHeight() const; - /** Return the cursor's hotspot's x coordinate. */ - uint16 getHotspotX() const; - /** Return the cursor's hotspot's y coordinate. */ - uint16 getHotspotY() const; - - const byte *getSurface() const { return _surface; } - const byte *getPalette() const { return _palette; } - - /** Set the cursor's dimensions. */ - void setDimensions(uint16 width, uint16 height); - /** Set the cursor's hotspot. */ - void setHotspot(uint16 x, uint16 y); - - /** Read the cursor's data out of a stream. */ - bool readCursor(SeekableReadStream &stream, uint32 count); - -private: - byte *_surface; - byte _palette[256 * 3]; - - uint16 _width; ///< The cursor's width. - uint16 _height; ///< The cursor's height. - uint16 _hotspotX; ///< The cursor's hotspot's x coordinate. - uint16 _hotspotY; ///< The cursor's hotspot's y coordinate. - - /** Clear the cursor. */ - void clear(); -}; - -class NEResourceID { -public: - NEResourceID() { _idType = kIDTypeNull; } - NEResourceID(String x) { _idType = kIDTypeString; _name = x; } - NEResourceID(uint16 x) { _idType = kIDTypeNumerical; _id = x; } - - NEResourceID &operator=(String string); - NEResourceID &operator=(uint16 x); - - bool operator==(const String &x) const; - bool operator==(const uint16 &x) const; - bool operator==(const NEResourceID &x) const; - - String getString() const; - uint16 getID() const; - String toString() const; - -private: - /** An ID Type. */ - enum IDType { - kIDTypeNull, ///< No type set - kIDTypeNumerical, ///< A numerical ID. - kIDTypeString ///< A string ID. - } _idType; - - String _name; ///< The resource's string ID. - uint16 _id; ///< The resource's numerical ID. -}; - -/** A New Executable cursor group. */ -struct NECursorGroup { - NEResourceID id; - Array<NECursor *> cursors; ///< The cursors. -}; - /** The default Windows resources. */ enum NEResourceType { kNECursor = 0x8001, @@ -157,19 +84,16 @@ public: /** Load from a stream. */ bool loadFromEXE(SeekableReadStream *stream); - /** Get all cursor's read from the New Executable. */ - const Array<NECursorGroup> &getCursors() const; - /** Return a list of resources for a given type. */ - const Array<NEResourceID> getIDList(uint16 type) const; + const Array<WinResourceID> getIDList(uint16 type) const; /** Return a stream to the specified resource (or 0 if non-existent). */ - SeekableReadStream *getResource(uint16 type, NEResourceID id); + SeekableReadStream *getResource(uint16 type, WinResourceID id); private: /** A resource. */ struct Resource { - NEResourceID id; + WinResourceID id; uint16 type; ///< Type of the resource. @@ -186,21 +110,13 @@ private: /** All resources. */ List<Resource> _resources; - /** All cursor resources. */ - Array<NECursorGroup> _cursors; - /** Read the offset to the resource table. */ uint32 getResourceTableOffset(); /** Read the resource table. */ bool readResourceTable(uint32 offset); - // Cursor reading helpers - bool readCursors(); - bool readCursorGroup(NECursorGroup &group, const Resource &resource); - bool readCursor(NECursor &cursor, const Resource &resource, uint32 size); - /** Find a specific resource. */ - const Resource *findResource(uint16 type, NEResourceID id) const; + const Resource *findResource(uint16 type, WinResourceID id) const; /** Read a resource string. */ static String getResourceString(SeekableReadStream &exe, uint32 offset); @@ -208,4 +124,4 @@ private: } // End of namespace Common -#endif // COMMON_NE_EXE_H +#endif diff --git a/common/winexe_pe.cpp b/common/winexe_pe.cpp new file mode 100644 index 0000000000..456093f5b4 --- /dev/null +++ b/common/winexe_pe.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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/debug.h" +#include "common/file.h" +#include "common/memstream.h" +#include "common/str.h" +#include "common/stream.h" +#include "common/winexe_pe.h" + +namespace Common { + +PEResources::PEResources() { + _exe = 0; +} + +PEResources::~PEResources() { + clear(); +} + +void PEResources::clear() { + _sections.clear(); + _resources.clear(); + delete _exe; _exe = 0; +} + +bool PEResources::loadFromEXE(const String &fileName) { + if (fileName.empty()) + return false; + + File *file = new File(); + + if (!file->open(fileName)) { + delete file; + return false; + } + + return loadFromEXE(file); +} + +bool PEResources::loadFromEXE(SeekableReadStream *stream) { + clear(); + + if (!stream) + return false; + + if (stream->readUint16BE() != 'MZ') + return false; + + stream->skip(58); + + uint32 peOffset = stream->readUint32LE(); + + if (!peOffset || peOffset >= (uint32)stream->size()) + return false; + + stream->seek(peOffset); + + if (stream->readUint32BE() != MKID_BE('PE\0\0')) + return false; + + stream->skip(2); + uint16 sectionCount = stream->readUint16LE(); + stream->skip(12); + uint16 optionalHeaderSize = stream->readUint16LE(); + stream->skip(optionalHeaderSize + 2); + + // Read in all the sections + for (uint16 i = 0; i < sectionCount; i++) { + char sectionName[9]; + stream->read(sectionName, 8); + sectionName[8] = 0; + + Section section; + stream->skip(4); + section.virtualAddress = stream->readUint32LE(); + section.size = stream->readUint32LE(); + section.offset = stream->readUint32LE(); + stream->skip(16); + + _sections[sectionName] = section; + } + + // Currently, we require loading a resource section + if (!_sections.contains(".rsrc")) { + clear(); + return false; + } + + _exe = stream; + + Section &resSection = _sections[".rsrc"]; + parseResourceLevel(resSection, resSection.offset, 0); + + return true; +} + +void PEResources::parseResourceLevel(Section §ion, uint32 offset, int level) { + _exe->seek(offset + 12); + + uint16 namedEntryCount = _exe->readUint16LE(); + uint16 intEntryCount = _exe->readUint16LE(); + + for (uint32 i = 0; i < namedEntryCount + intEntryCount; i++) { + uint32 value = _exe->readUint32LE(); + + WinResourceID id; + + if (value & 0x80000000) { + value &= 0x7fffffff; + + uint32 startPos = _exe->pos(); + _exe->seek(section.offset + (value & 0x7fffffff)); + + // Read in the name, truncating from unicode to ascii + Common::String name; + uint16 nameLength = _exe->readUint16LE(); + while (nameLength--) + name += (char)(_exe->readUint16LE() & 0xff); + + _exe->seek(startPos); + + id = name; + } else { + id = value; + } + + uint32 nextOffset = _exe->readUint32LE(); + uint32 lastOffset = _exe->pos(); + + if (level == 0) + _curType = id; + else if (level == 1) + _curName = id; + else if (level == 2) + _curLang = id; + + if (level < 2) { + // Time to dive down further + parseResourceLevel(section, section.offset + (nextOffset & 0x7fffffff), level + 1); + } else { + _exe->seek(section.offset + nextOffset); + + Resource resource; + resource.offset = _exe->readUint32LE() + section.offset - section.virtualAddress; + resource.size = _exe->readUint32LE(); + + debug(4, "Found resource '%s' '%s' '%s' at %d of size %d", _curType.toString().c_str(), + _curName.toString().c_str(), _curLang.toString().c_str(), resource.offset, resource.size); + + _resources[_curType][_curName][_curLang] = resource; + } + + _exe->seek(lastOffset); + } +} + +const Array<WinResourceID> PEResources::getTypeList() const { + Array<WinResourceID> array; + + if (!_exe) + return array; + + for (TypeMap::const_iterator it = _resources.begin(); it != _resources.end(); it++) + array.push_back(it->_key); + + return array; +} + +const Array<WinResourceID> PEResources::getNameList(const WinResourceID &type) const { + Array<WinResourceID> array; + + if (!_exe || !_resources.contains(type)) + return array; + + const NameMap &nameMap = _resources[type]; + + for (NameMap::const_iterator it = nameMap.begin(); it != nameMap.end(); it++) + array.push_back(it->_key); + + return array; +} + +const Array<WinResourceID> PEResources::getLangList(const WinResourceID &type, const WinResourceID &name) const { + Array<WinResourceID> array; + + if (!_exe || !_resources.contains(type)) + return array; + + const NameMap &nameMap = _resources[type]; + + if (!nameMap.contains(name)) + return array; + + const LangMap &langMap = nameMap[name]; + + for (LangMap::const_iterator it = langMap.begin(); it != langMap.end(); it++) + array.push_back(it->_key); + + return array; +} + +SeekableReadStream *PEResources::getResource(const WinResourceID &type, const WinResourceID &name) { + Array<WinResourceID> langList = getLangList(type, name); + + if (langList.empty()) + return 0; + + const Resource &resource = _resources[type][name][langList[0]]; + _exe->seek(resource.offset); + return _exe->readStream(resource.size); +} + +SeekableReadStream *PEResources::getResource(const WinResourceID &type, const WinResourceID &name, const WinResourceID &lang) { + if (!_exe || !_resources.contains(type)) + return 0; + + const NameMap &nameMap = _resources[type]; + + if (!nameMap.contains(name)) + return 0; + + const LangMap &langMap = nameMap[name]; + + if (!langMap.contains(lang)) + return 0; + + const Resource &resource = langMap[lang]; + _exe->seek(resource.offset); + return _exe->readStream(resource.size); +} + +} // End of namespace Common diff --git a/common/winexe_pe.h b/common/winexe_pe.h new file mode 100644 index 0000000000..5298e993ad --- /dev/null +++ b/common/winexe_pe.h @@ -0,0 +1,122 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef COMMON_WINEXE_PE_H +#define COMMON_WINEXE_PE_H + +#include "common/array.h" +#include "common/hashmap.h" +#include "common/winexe.h" + +namespace Common { + +class SeekableReadStream; +class String; + +/** The default Windows PE resources. */ +enum PEResourceType { + kPECursor = 0x01, + kPEBitmap = 0x02, + kPEIcon = 0x03, + kPEMenu = 0x04, + kPEDialog = 0x05, + kPEString = 0x06, + kPEFontDir = 0x07, + kPEFont = 0x08, + kPEAccelerator = 0x09, + kPERCData = 0x0A, + kPEMessageTable = 0x0B, + kPEGroupCursor = 0x0C, + kPEGroupIcon = 0x0E, + kPEVersion = 0x10, + kPEDlgInclude = 0x11, + kPEPlugPlay = 0x13, + kPEVXD = 0x14, + kPEAniCursor = 0x15, + kPEAniIcon = 0x16 +}; + +/** + * A class able to load resources from a Windows Portable Executable, such + * as cursors, bitmaps, and sounds. + */ +class PEResources { +public: + PEResources(); + ~PEResources(); + + /** Clear all information. */ + void clear(); + + /** Load from an EXE file. */ + bool loadFromEXE(const String &fileName); + + /** Load from a stream. */ + bool loadFromEXE(SeekableReadStream *stream); + + /** Return a list of resource types. */ + const Array<WinResourceID> getTypeList() const; + + /** Return a list of names for a given type. */ + const Array<WinResourceID> getNameList(const WinResourceID &type) const; + + /** Return a list of languages for a given type and name. */ + const Array<WinResourceID> getLangList(const WinResourceID &type, const WinResourceID &name) const; + + /** Return a stream to the specified resource, taking the first language found (or 0 if non-existent). */ + SeekableReadStream *getResource(const WinResourceID &type, const WinResourceID &name); + + /** Return a stream to the specified resource (or 0 if non-existent). */ + SeekableReadStream *getResource(const WinResourceID &type, const WinResourceID &name, const WinResourceID &lang); + +private: + struct Section { + uint32 virtualAddress; + uint32 size; + uint32 offset; + }; + + HashMap<String, Section, IgnoreCase_Hash, IgnoreCase_EqualTo> _sections; + + SeekableReadStream *_exe; + + void parseResourceLevel(Section §ion, uint32 offset, int level); + WinResourceID _curType, _curName, _curLang; + + struct Resource { + uint32 offset; + uint32 size; + }; + + typedef HashMap<WinResourceID, Resource, WinResourceID_Hash, WinResourceID_EqualTo> LangMap; + typedef HashMap<WinResourceID, LangMap, WinResourceID_Hash, WinResourceID_EqualTo> NameMap; + typedef HashMap<WinResourceID, NameMap, WinResourceID_Hash, WinResourceID_EqualTo> TypeMap; + + TypeMap _resources; +}; + +} // End of namespace Common + +#endif @@ -976,7 +976,7 @@ get_system_exe_extension $guessed_host NATIVEEXEEXT=$_exeext case $_host in -android) +android | android-v7a) _host_os=android _host_cpu=arm _host_alias=arm-linux-androideabi @@ -1504,15 +1504,23 @@ case $_host_os in add_line_to_config_mk 'AMIGAOS = 1' ;; android) + case $_host in + android) + CXXFLAGS="$CXXFLAGS -march=armv5te -mtune=xscale -msoft-float" + ;; + android-v7a) + CXXFLAGS="$CXXFLAGS -march=armv7-a -mfloat-abi=softfp -mfpu=vfp" + LDFLAGS="$LDFLAGS -Wl,--fix-cortex-a8" + ;; + esac CXXFLAGS="$CXXFLAGS --sysroot=$ANDROID_NDK/platforms/android-4/arch-arm" - CXXFLAGS="$CXXFLAGS -march=armv5te -mtune=xscale -msoft-float" CXXFLAGS="$CXXFLAGS -fpic -ffunction-sections -funwind-tables" - if test "$_release_build" = yes; then - CXXFLAGS="$CXXFLAGS -fomit-frame-pointer -fstrict-aliasing" + if test "$_debug_build" = yes; then + CXXFLAGS="$CXXFLAGS -fno-omit-frame-pointer -fno-strict-aliasing" else - CXXFLAGS="$CXXFLAGS -fno-omit-frame-pointer -fno-strict-aliasing" + CXXFLAGS="$CXXFLAGS -fomit-frame-pointer -fstrict-aliasing" fi - CXXFLAGS="$CXXFLAGS -finline-limit=300" + CXXFLAGS="$CXXFLAGS -finline-limit=300" CXXFLAGS="$CXXFLAGS -Os -mthumb-interwork" CXXFLAGS="$CXXFLAGS -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__" CXXFLAGS="$CXXFLAGS -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__" @@ -1559,8 +1567,8 @@ case $_host_os in CXXFLAGS="$CXXFLAGS -fuse-cxa-atexit" LDFLAGS="$LDFLAGS -specs=ds_arm9.specs -mthumb-interwork -mno-fpu -Wl,-Map,map.txt" if test "$_dynamic_modules" = no ; then - LDFLAGS="$LDFLAGS -Wl,--gc-sections" - else + LDFLAGS="$LDFLAGS -Wl,--gc-sections" + else LDFLAGS="$LDFLAGS -Wl,--no-gc-sections" # TODO automate this required 2 step linking phase # LDFLAGS="$LDFLAGS -Wl,--retain-symbols-file,ds.syms" @@ -1653,7 +1661,7 @@ case $_host_os in fi ;; wince) - CXXFLAGS="$CXXFLAGS -O3 -march=armv4 -mtune=xscale" + CXXFLAGS="$CXXFLAGS -O3 -fno-inline-functions -march=armv4 -mtune=xscale" DEFINES="$DEFINES -D_WIN32_WCE=300 -D__ARM__ -D_ARM_ -DUNICODE -DFPM_DEFAULT -DNONSTANDARD_PORT" DEFINES="$DEFINES -DWIN32 -Dcdecl= -D__cdecl__=" ;; @@ -1668,7 +1676,7 @@ if test -n "$_host"; then # Cross-compiling mode - add your target here if needed echo "Cross-compiling to $_host" case "$_host" in - android) + android | android-v7a) _unix=yes _need_memalign=yes # we link a .so as default @@ -2104,7 +2112,7 @@ fi # Enable 16bit support only for backends which support it # case $_backend in - dingux | dreamcast | gph | openpandora | psp | samsungtv | sdl | wii) + android | dingux | dreamcast | gph | openpandora | psp | samsungtv | sdl | wii) if test "$_16bit" = auto ; then _16bit=yes else @@ -2965,7 +2973,12 @@ fi case $_backend in android) # ssp at this point so the cxxtests link - CXXFLAGS="$CXXFLAGS -fstack-protector -Wa,--noexecstack" + if test "$_debug_build" = yes; then + CXXFLAGS="$CXXFLAGS -fstack-protector" + else + CXXFLAGS="$CXXFLAGS -fno-stack-protector" + fi + CXXFLAGS="$CXXFLAGS -Wa,--noexecstack" LDFLAGS="$LDFLAGS -Wl,-z,noexecstack" static_libs='' @@ -3052,8 +3065,8 @@ case $_backend in DEFINES="$DEFINES -D_EE -DFORCE_RTL" INCLUDES="$INCLUDES -I$PS2SDK/ee/include -I$PS2SDK/common/include -I$PS2SDK/ports/include" if test "$_dynamic_modules" = no ; then - LDFLAGS="$LDFLAGS -mno-crt0 $PS2SDK/ee/startup/crt0.o -T $PS2SDK/ee/startup/linkfile" - fi + LDFLAGS="$LDFLAGS -mno-crt0 $PS2SDK/ee/startup/crt0.o -T $PS2SDK/ee/startup/linkfile" + fi LDFLAGS="$LDFLAGS -L$PS2SDK/ee/lib -L$PS2SDK/ports/lib" LIBS="$LIBS -lmc -lpad -lmouse -lhdd -lpoweroff -lsjpcm -lm -lc -lfileXio -lkernel -lstdc++ " ;; diff --git a/dists/android/AndroidManifest.xml b/dists/android/AndroidManifest.xml index 2ea2b484ac..68c58d9aea 100644 --- a/dists/android/AndroidManifest.xml +++ b/dists/android/AndroidManifest.xml @@ -1,58 +1,61 @@ -<?xml version="1.0" encoding="utf-8"?> <!-- -*- xml -*- --> +<?xml version="1.0" encoding="utf-8"?> <!-- NB: android:versionCode needs to be bumped for formal releases --> + <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="org.inodes.gus.scummvm" - android:versionCode="6" android:versionName="1.3.0git" - android:installLocation="preferExternal"> - - <!-- This version works on Android 1.5 (SDK 3) and newer, but we - want Android 2.2 (SDK 8) defaults and features. - --> - <uses-sdk android:minSdkVersion="3" - android:targetSdkVersion="8" /> - - <application android:name=".ScummVMApplication" - android:label="@string/app_name" - android:description="@string/app_desc" - android:icon="@drawable/scummvm" - android:persistent="true"> - <activity android:name=".ScummVMActivity" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen" - android:screenOrientation="landscape" - android:configChanges="orientation|keyboardHidden" - android:windowSoftInputMode="adjustResize"> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - </intent-filter> - </activity> - <activity android:name=".Unpacker" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen" - android:screenOrientation="landscape" - android:configChanges="orientation|keyboardHidden"> - <meta-data android:name="org.inodes.gus.unpacker.nextActivity" - android:value="org.inodes.gus.scummvm/.ScummVMActivity" /> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - </activity> - </application> - - <permission android:name="org.inodes.gus.scummvm.permission.SCUMMVM_PLUGIN" - android:label="@string/scummvm_perm_plugin_label" - android:description="@string/scummvm_perm_plugin_desc" - android:protectionLevel="signature" /> - - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - - - <!-- Always needs some sort of qwerty keyboard. - Can work with a D-pad / trackball --> - <uses-configuration android:reqFiveWayNav="true" - android:reqKeyboardType="qwerty"/> - <!-- .. or touchscreen --> - <uses-configuration android:reqTouchScreen="finger" - android:reqKeyboardType="qwerty"/> - <uses-configuration android:reqTouchScreen="stylus" - android:reqKeyboardType="qwerty"/> + package="org.inodes.gus.scummvm" + android:versionCode="6" + android:versionName="1.3.0git" + android:installLocation="preferExternal"> + + <!-- This version works on Android 1.5 (SDK 3) and newer, but we + want Android 2.2 (SDK 8) defaults and features. --> + <uses-sdk android:minSdkVersion="3" + android:targetSdkVersion="8"/> + + <application android:name=".ScummVMApplication" + android:label="@string/app_name" + android:description="@string/app_desc" + android:icon="@drawable/scummvm"> + <activity android:name=".ScummVMActivity" + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:screenOrientation="landscape" + android:configChanges="orientation|keyboardHidden" + android:windowSoftInputMode="adjustResize"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + </intent-filter> + </activity> + + <activity android:name=".Unpacker" + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:screenOrientation="landscape" + android:configChanges="orientation|keyboardHidden"> + <meta-data android:name="org.inodes.gus.unpacker.nextActivity" + android:value="org.inodes.gus.scummvm/.ScummVMActivity"/> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + </application> + + <permission android:name="org.inodes.gus.scummvm.permission.SCUMMVM_PLUGIN" + android:label="@string/scummvm_perm_plugin_label" + android:description="@string/scummvm_perm_plugin_desc" + android:protectionLevel="signature"/> + + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> + + <!-- Always needs some sort of qwerty keyboard. + Can work with a D-pad / trackball --> + <uses-configuration android:reqFiveWayNav="true" + android:reqKeyboardType="qwerty"/> + + <!-- .. or touchscreen --> + <uses-configuration android:reqTouchScreen="finger" + android:reqKeyboardType="qwerty"/> + + <uses-configuration android:reqTouchScreen="stylus" + android:reqKeyboardType="qwerty"/> </manifest> + diff --git a/dists/android/AndroidManifest.xml.in b/dists/android/AndroidManifest.xml.in index 26a94f957b..7a75b6fd0b 100644 --- a/dists/android/AndroidManifest.xml.in +++ b/dists/android/AndroidManifest.xml.in @@ -1,58 +1,61 @@ -<?xml version="1.0" encoding="utf-8"?> <!-- -*- xml -*- --> +<?xml version="1.0" encoding="utf-8"?> <!-- NB: android:versionCode needs to be bumped for formal releases --> + <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="org.inodes.gus.scummvm" - android:versionCode="6" android:versionName="@VERSION@" - android:installLocation="preferExternal"> - - <!-- This version works on Android 1.5 (SDK 3) and newer, but we - want Android 2.2 (SDK 8) defaults and features. - --> - <uses-sdk android:minSdkVersion="3" - android:targetSdkVersion="8" /> - - <application android:name=".ScummVMApplication" - android:label="@string/app_name" - android:description="@string/app_desc" - android:icon="@drawable/scummvm" - android:persistent="true"> - <activity android:name=".ScummVMActivity" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen" - android:screenOrientation="landscape" - android:configChanges="orientation|keyboardHidden" - android:windowSoftInputMode="adjustResize"> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - </intent-filter> - </activity> - <activity android:name=".Unpacker" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen" - android:screenOrientation="landscape" - android:configChanges="orientation|keyboardHidden"> - <meta-data android:name="org.inodes.gus.unpacker.nextActivity" - android:value="org.inodes.gus.scummvm/.ScummVMActivity" /> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - </activity> - </application> - - <permission android:name="org.inodes.gus.scummvm.permission.SCUMMVM_PLUGIN" - android:label="@string/scummvm_perm_plugin_label" - android:description="@string/scummvm_perm_plugin_desc" - android:protectionLevel="signature" /> - - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - - - <!-- Always needs some sort of qwerty keyboard. - Can work with a D-pad / trackball --> - <uses-configuration android:reqFiveWayNav="true" - android:reqKeyboardType="qwerty"/> - <!-- .. or touchscreen --> - <uses-configuration android:reqTouchScreen="finger" - android:reqKeyboardType="qwerty"/> - <uses-configuration android:reqTouchScreen="stylus" - android:reqKeyboardType="qwerty"/> + package="org.inodes.gus.scummvm" + android:versionCode="6" + android:versionName="@VERSION@" + android:installLocation="preferExternal"> + + <!-- This version works on Android 1.5 (SDK 3) and newer, but we + want Android 2.2 (SDK 8) defaults and features. --> + <uses-sdk android:minSdkVersion="3" + android:targetSdkVersion="8"/> + + <application android:name=".ScummVMApplication" + android:label="@string/app_name" + android:description="@string/app_desc" + android:icon="@drawable/scummvm"> + <activity android:name=".ScummVMActivity" + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:screenOrientation="landscape" + android:configChanges="orientation|keyboardHidden" + android:windowSoftInputMode="adjustResize"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + </intent-filter> + </activity> + + <activity android:name=".Unpacker" + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:screenOrientation="landscape" + android:configChanges="orientation|keyboardHidden"> + <meta-data android:name="org.inodes.gus.unpacker.nextActivity" + android:value="org.inodes.gus.scummvm/.ScummVMActivity"/> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + </application> + + <permission android:name="org.inodes.gus.scummvm.permission.SCUMMVM_PLUGIN" + android:label="@string/scummvm_perm_plugin_label" + android:description="@string/scummvm_perm_plugin_desc" + android:protectionLevel="signature"/> + + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> + + <!-- Always needs some sort of qwerty keyboard. + Can work with a D-pad / trackball --> + <uses-configuration android:reqFiveWayNav="true" + android:reqKeyboardType="qwerty"/> + + <!-- .. or touchscreen --> + <uses-configuration android:reqTouchScreen="finger" + android:reqKeyboardType="qwerty"/> + + <uses-configuration android:reqTouchScreen="stylus" + android:reqKeyboardType="qwerty"/> </manifest> + diff --git a/dists/android/res/layout/main.xml b/dists/android/res/layout/main.xml index f5276ce41b..b6164edc96 100644 --- a/dists/android/res/layout/main.xml +++ b/dists/android/res/layout/main.xml @@ -1,10 +1,13 @@ <?xml version="1.0" encoding="utf-8"?> + <org.inodes.gus.scummvm.EditableSurfaceView - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" android:layout_height="fill_parent" - android:id="@+id/main_surface" - android:gravity="center" - android:keepScreenOn="true" - android:focusable="true" - android:focusableInTouchMode="true" + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/main_surface" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:gravity="center" + android:keepScreenOn="true" + android:focusable="true" + android:focusableInTouchMode="true" /> + diff --git a/dists/scummvm.rc b/dists/scummvm.rc index 34e1ff2ed8..61981b30c5 100644 --- a/dists/scummvm.rc +++ b/dists/scummvm.rc @@ -7,21 +7,21 @@ IDI_ICON ICON DISCARDABLE "../../icons/scummvm.ico" #endif VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,3,0,0 - PRODUCTVERSION 1,3,0,0 - FILEFLAGSMASK 0x3fL + FILEVERSION 1,3,0,0 + PRODUCTVERSION 1,3,0,0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK #ifdef _DEBUG - FILEFLAGS 0x1L + FILEFLAGS VS_FF_DEBUG #else - FILEFLAGS 0x0L + FILEFLAGS 0 #endif - FILEOS 0x40004L - FILETYPE 0x1L - FILESUBTYPE 0x0L + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN - BLOCK "040904b0" + BLOCK "040904b0" // US English, Unicode BEGIN VALUE "Comments", "Look! A three headed monkey (TM)! .. Nice use of the TM!\0" VALUE "FileDescription", "http://www.scummvm.org/\0" @@ -34,8 +34,9 @@ BEGIN VALUE "ProductVersion", "1.3.0git\0" END END + BLOCK "VarFileInfo" BEGIN - VALUE "Translation", 0x409, 1200 + VALUE "Translation", 0x409, 1200 // US English, Unicode END END diff --git a/dists/scummvm.rc.in b/dists/scummvm.rc.in index 17415f3d12..a0fcf82dbb 100644 --- a/dists/scummvm.rc.in +++ b/dists/scummvm.rc.in @@ -7,21 +7,21 @@ IDI_ICON ICON DISCARDABLE "../../icons/scummvm.ico" #endif VS_VERSION_INFO VERSIONINFO - FILEVERSION @VER_MAJOR@,@VER_MINOR@,@VER_PATCH@,0 - PRODUCTVERSION @VER_MAJOR@,@VER_MINOR@,@VER_PATCH@,0 - FILEFLAGSMASK 0x3fL + FILEVERSION @VER_MAJOR@,@VER_MINOR@,@VER_PATCH@,0 + PRODUCTVERSION @VER_MAJOR@,@VER_MINOR@,@VER_PATCH@,0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK #ifdef _DEBUG - FILEFLAGS 0x1L + FILEFLAGS VS_FF_DEBUG #else - FILEFLAGS 0x0L + FILEFLAGS 0 #endif - FILEOS 0x40004L - FILETYPE 0x1L - FILESUBTYPE 0x0L + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN - BLOCK "040904b0" + BLOCK "040904b0" // US English, Unicode BEGIN VALUE "Comments", "Look! A three headed monkey (TM)! .. Nice use of the TM!\0" VALUE "FileDescription", "http://www.scummvm.org/\0" @@ -34,8 +34,9 @@ BEGIN VALUE "ProductVersion", "@VERSION@\0" END END + BLOCK "VarFileInfo" BEGIN - VALUE "Translation", 0x409, 1200 + VALUE "Translation", 0x409, 1200 // US English, Unicode END END diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp index fdc05f0ba9..632587f7f7 100644 --- a/engines/agi/agi.cpp +++ b/engines/agi/agi.cpp @@ -273,23 +273,16 @@ void AgiEngine::processEvents() { } void AgiEngine::pollTimer() { - uint32 dm; + _lastTick += 50; - if (_tickTimer < _lastTickTimer) - _lastTickTimer = 0; - - while ((dm = _tickTimer - _lastTickTimer) < 5) { + while (_system->getMillis() < _lastTick) { processEvents(); _console->onFrame(); _system->delayMillis(10); _system->updateScreen(); } - _lastTickTimer = _tickTimer; -} -void AgiEngine::agiTimerFunctionLow(void *refCon) { - AgiEngine *self = (AgiEngine *)refCon; - self->_tickTimer++; + _lastTick = _system->getMillis(); } void AgiEngine::pause(uint32 msec) { @@ -361,12 +354,12 @@ int AgiEngine::agiInit() { switch (getVersion() >> 12) { case 2: - debug("Emulating Sierra AGI v%x.%03x\n", + debug("Emulating Sierra AGI v%x.%03x", (int)(getVersion() >> 12) & 0xF, (int)(getVersion()) & 0xFFF); break; case 3: - debug("Emulating Sierra AGI v%x.002.%03x\n", + debug("Emulating Sierra AGI v%x.002.%03x", (int)(getVersion() >> 12) & 0xF, (int)(getVersion()) & 0xFFF); break; @@ -532,9 +525,6 @@ AgiEngine::AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc) : AgiBas _allowSynthetic = false; - _tickTimer = 0; - _lastTickTimer = 0; - _intobj = NULL; _menu = NULL; @@ -646,11 +636,10 @@ void AgiEngine::initialize() { _lastSaveTime = 0; - _timer->installTimerProc(agiTimerFunctionLow, 10 * 1000, this); + _lastTick = _system->getMillis(); debugC(2, kDebugLevelMain, "Detect game"); - if (agiDetectGame() == errOK) { _game.state = STATE_LOADED; debugC(2, kDebugLevelMain, "game loaded"); @@ -662,8 +651,6 @@ void AgiEngine::initialize() { } AgiEngine::~AgiEngine() { - _timer->removeTimerProc(agiTimerFunctionLow); - // If the engine hasn't been initialized yet via AgiEngine::initialize(), don't attempt to free any resources, // as they haven't been allocated. Fixes bug #1742432 - AGI: Engine crashes if no game is detected if (_game.state == STATE_INIT) { @@ -719,7 +706,7 @@ void AgiEngine::parseFeatures() { /* FIXME: Seems this method doesn't really do anything. It might be a leftover that could be removed, except that some of its intended purpose may still need to be reimplemented. - + [0:29] <Fingolfin> can you tell me what the point behind AgiEngine::parseFeatures() is? [0:30] <_sev> when games are created with WAGI studio [0:31] <_sev> it creates .wag site with game-specific features such as full game title, whether to use AGIMOUSE etc diff --git a/engines/agi/agi.h b/engines/agi/agi.h index 89b116daec..df19f55b52 100644 --- a/engines/agi/agi.h +++ b/engines/agi/agi.h @@ -810,9 +810,7 @@ public: Common::Error saveGameState(int slot, const char *desc); private: - - uint32 _tickTimer; - uint32 _lastTickTimer; + uint32 _lastTick; int _keyQueue[KEY_QUEUE_SIZE]; int _keyQueueStart; @@ -883,7 +881,6 @@ public: virtual bool isKeypress(); virtual void clearKeyQueue(); - static void agiTimerFunctionLow(void *refCon); void initPriTable(); void newInputMode(InputMode mode); diff --git a/engines/agi/detection_tables.h b/engines/agi/detection_tables.h index 711701f55a..fb1b830e24 100644 --- a/engines/agi/detection_tables.h +++ b/engines/agi/detection_tables.h @@ -569,6 +569,7 @@ static const AGIGameDescription gameDescriptions[] = { GAME_PS("xmascard", "", "25ad35e9628fc77e5e0dd35852a272b6", 768, 0x2440, GID_XMASCARD, Common::kPlatformCoCo3), FANMADE_F("2 Player Demo", "4279f46b3cebd855132496476b1d2cca", GF_AGIMOUSE), + FANMADE("AGI Combat", "0be6a8a9e19203dcca0067d280798871"), FANMADE("AGI Contest 1 Template", "d879aed25da6fc655564b29567358ae2"), FANMADE("AGI Contest 2 Template", "5a2fb2894207eff36c72f5c1b08bcc07"), FANMADE("AGI Mouse Demo 0.60 demo 1", "c07e2519de674c67386cb2cc6f2e3904"), diff --git a/engines/agi/preagi.cpp b/engines/agi/preagi.cpp index fe864d7659..1aa6ef5cc4 100644 --- a/engines/agi/preagi.cpp +++ b/engines/agi/preagi.cpp @@ -122,9 +122,6 @@ void PreAgiEngine::initialize() { _mixer->playStream(Audio::Mixer::kSFXSoundType, &_speakerHandle, _speakerStream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); - - //_timer->installTimerProc(agiTimerFunctionLow, 10 * 1000, NULL); - debugC(2, kDebugLevelMain, "Detect game"); // clear all resources and events diff --git a/engines/agos/agos.h b/engines/agos/agos.h index 7201dfd9d3..9c2a2929e3 100644 --- a/engines/agos/agos.h +++ b/engines/agos/agos.h @@ -2018,7 +2018,7 @@ protected: void scrollOracleUp(); void scrollOracleDown(); - void listSaveGames(int n); + void listSaveGamesFeeble(); void saveUserGame(int slot); void windowBackSpace(WindowBlock *window); diff --git a/engines/agos/animation.cpp b/engines/agos/animation.cpp index d39ca377dc..10d3d7f1ff 100644 --- a/engines/agos/animation.cpp +++ b/engines/agos/animation.cpp @@ -368,7 +368,7 @@ void MoviePlayerDXA::handleNextFrame() { bool MoviePlayerDXA::processFrame() { Graphics::Surface *screen = _vm->_system->lockScreen(); - copyFrameToBuffer((byte *)screen->pixels, (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, _vm->_screenWidth); + copyFrameToBuffer((byte *)screen->pixels, (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, screen->pitch); _vm->_system->unlockScreen(); Common::Rational soundTime(_mixer->getSoundElapsedTime(_bgSound), 1000); @@ -482,7 +482,7 @@ void MoviePlayerSMK::nextFrame() { bool MoviePlayerSMK::processFrame() { Graphics::Surface *screen = _vm->_system->lockScreen(); - copyFrameToBuffer((byte *)screen->pixels, (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, _vm->_screenWidth); + copyFrameToBuffer((byte *)screen->pixels, (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, screen->pitch); _vm->_system->unlockScreen(); uint32 waitTime = getTimeToNextFrame(); diff --git a/engines/agos/oracle.cpp b/engines/agos/oracle.cpp index 0a4ab8246f..8ff79965e4 100644 --- a/engines/agos/oracle.cpp +++ b/engines/agos/oracle.cpp @@ -369,7 +369,7 @@ void AGOSEngine_Feeble::swapCharacterLogo() { } } -void AGOSEngine_Feeble::listSaveGames(int n) { +void AGOSEngine_Feeble::listSaveGamesFeeble() { char b[108]; Common::InSaveFile *in; uint16 j, k, z, maxFiles; @@ -377,8 +377,8 @@ void AGOSEngine_Feeble::listSaveGames(int n) { memset(b, 0, 108); maxFiles = countSaveGames() - 1; - j = maxFiles - n + 1; - k = maxFiles - j + 1; + j = maxFiles; + k = 1; z = maxFiles; if (getBitFlag(95)) { j++; diff --git a/engines/agos/script_ff.cpp b/engines/agos/script_ff.cpp index 3198b1b499..dbd89cebf5 100644 --- a/engines/agos/script_ff.cpp +++ b/engines/agos/script_ff.cpp @@ -430,8 +430,7 @@ void AGOSEngine_Feeble::off_loadUserGame() { } void AGOSEngine_Feeble::off_listSaveGames() { - // 134: dummy opcode? - listSaveGames(1); + listSaveGamesFeeble(); } void AGOSEngine_Feeble::off_checkCD() { diff --git a/engines/cine/script_os.cpp b/engines/cine/script_os.cpp index ab1ad7ff9c..9ee3a892a9 100644 --- a/engines/cine/script_os.cpp +++ b/engines/cine/script_os.cpp @@ -124,7 +124,7 @@ void OSScript::setupTable() { { 0, 0 }, { &FWScript::o2_loadPart, "s" }, /* 40 */ - { 0, 0 }, /* o1_closePart, triggered by some scripts (STARTA.PRC 4 for ex.) */ + { &FWScript::o1_closePart, "" }, { &FWScript::o1_loadNewPrcName, "bs" }, { &FWScript::o1_requestCheckPendingDataLoad, "" }, { 0, 0 }, diff --git a/engines/drascula/drascula.cpp b/engines/drascula/drascula.cpp index e84e80ccd3..b59ab6f566 100644 --- a/engines/drascula/drascula.cpp +++ b/engines/drascula/drascula.cpp @@ -512,7 +512,7 @@ bool DrasculaEngine::runCurrentChapter() { checkObjects(); #ifdef _WIN32_WCE - if (rightMouseButton) + if (rightMouseButton) { if (_menuScreen) { #else if (rightMouseButton == 1 && _menuScreen) { @@ -570,6 +570,9 @@ bool DrasculaEngine::runCurrentChapter() { #endif selectVerb(kVerbNone); } +#ifdef _WIN32_WCE + } +#endif if (leftMouseButton == 1 && _menuBar) { delay(100); diff --git a/engines/engine.cpp b/engines/engine.cpp index 0d92c1aef1..d773f370f5 100644 --- a/engines/engine.cpp +++ b/engines/engine.cpp @@ -145,7 +145,11 @@ void initCommonGFX(bool defaultTo1XScaler) { assert(transientDomain); const bool useDefaultGraphicsMode = - !transientDomain->contains("gfx_mode") && + (!transientDomain->contains("gfx_mode") || + !scumm_stricmp(transientDomain->getVal("gfx_mode").c_str(), "normal") || + !scumm_stricmp(transientDomain->getVal("gfx_mode").c_str(), "default") + ) + && ( !gameDomain || !gameDomain->contains("gfx_mode") || @@ -155,10 +159,7 @@ void initCommonGFX(bool defaultTo1XScaler) { // See if the game should default to 1x scaler if (useDefaultGraphicsMode && defaultTo1XScaler) { - // FIXME: As a hack, we use "1x" here. Would be nicer to use - // getDefaultGraphicsMode() instead, but right now, we do not specify - // whether that is a 1x scaler or not... - g_system->setGraphicsMode("1x"); + g_system->resetGraphicsScale(); } else { // Override global scaler with any game-specific define if (ConfMan.hasKey("gfx_mode")) { diff --git a/engines/gob/save/saveload.h b/engines/gob/save/saveload.h index c231c1dbbb..dc1c184504 100644 --- a/engines/gob/save/saveload.h +++ b/engines/gob/save/saveload.h @@ -305,7 +305,7 @@ protected: int getSlot(int32 offset) const; int getSlotRemainder(int32 offset) const; - void buildIndex(byte *buffer) const; + void buildScreenshotIndex(byte *buffer) const; protected: uint32 _shotSize; @@ -430,7 +430,7 @@ protected: int getSlot(int32 offset) const; int getSlotRemainder(int32 offset) const; - void buildIndex(byte *buffer) const; + void buildScreenshotIndex(byte *buffer) const; }; File *_file; diff --git a/engines/gob/save/saveload_inca2.cpp b/engines/gob/save/saveload_inca2.cpp index 68c76c3f2b..5fa1b69fa7 100644 --- a/engines/gob/save/saveload_inca2.cpp +++ b/engines/gob/save/saveload_inca2.cpp @@ -260,7 +260,7 @@ int SaveLoad_Inca2::ScreenshotHandler::File::getSlotRemainder(int32 offset) cons return (offset - 80) % 15168; } -void SaveLoad_Inca2::ScreenshotHandler::File::buildIndex(byte *buffer) const { +void SaveLoad_Inca2::ScreenshotHandler::File::buildScreenshotIndex(byte *buffer) const { Common::SaveFileManager *saveMan = g_system->getSavefileManager(); Common::InSaveFile *in; @@ -306,7 +306,7 @@ bool SaveLoad_Inca2::ScreenshotHandler::load(int16 dataVar, int32 size, int32 of } // Create/Fake the index - _file->buildIndex(_index + 40); + _file->buildScreenshotIndex(_index + 40); _vm->_inter->_variables->copyFrom(dataVar, _index + offset, size); diff --git a/engines/gob/save/saveload_v3.cpp b/engines/gob/save/saveload_v3.cpp index 098b8e1160..39edddb66f 100644 --- a/engines/gob/save/saveload_v3.cpp +++ b/engines/gob/save/saveload_v3.cpp @@ -367,7 +367,7 @@ int SaveLoad_v3::ScreenshotHandler::File::getSlotRemainder(int32 offset) const { return ((offset - _shotIndexSize) % _shotSize); } -void SaveLoad_v3::ScreenshotHandler::File::buildIndex(byte *buffer) const { +void SaveLoad_v3::ScreenshotHandler::File::buildScreenshotIndex(byte *buffer) const { Common::SaveFileManager *saveMan = g_system->getSavefileManager(); Common::InSaveFile *in; @@ -418,12 +418,12 @@ bool SaveLoad_v3::ScreenshotHandler::load(int16 dataVar, int32 size, int32 offse if (_sShotType == kScreenshotTypeGob3) { // Create/Fake the index - _file->buildIndex(_index + 40); + _file->buildScreenshotIndex(_index + 40); // The last 10 bytes are 0 memset(_index + 70, 0, 10); } else if (_sShotType == kScreenshotTypeLost) { // Create/Fake the index - _file->buildIndex(_index); + _file->buildScreenshotIndex(_index); // The last byte is 0 _index[30] = 0; } diff --git a/engines/gob/sound/bgatmosphere.cpp b/engines/gob/sound/bgatmosphere.cpp index f0977aa45b..8850a727d3 100644 --- a/engines/gob/sound/bgatmosphere.cpp +++ b/engines/gob/sound/bgatmosphere.cpp @@ -47,7 +47,7 @@ BackgroundAtmosphere::~BackgroundAtmosphere() { queueClear(); } -void BackgroundAtmosphere::play() { +void BackgroundAtmosphere::playBA() { Common::StackLock slock(_mutex); _queuePos = -1; @@ -59,7 +59,7 @@ void BackgroundAtmosphere::play() { SoundMixer::play(*_queue[_queuePos], 1, 0); } -void BackgroundAtmosphere::stop() { +void BackgroundAtmosphere::stopBA() { SoundMixer::stop(0); } diff --git a/engines/gob/sound/bgatmosphere.h b/engines/gob/sound/bgatmosphere.h index 71a2263341..7e58c0b4e9 100644 --- a/engines/gob/sound/bgatmosphere.h +++ b/engines/gob/sound/bgatmosphere.h @@ -46,8 +46,8 @@ public: BackgroundAtmosphere(Audio::Mixer &mixer); ~BackgroundAtmosphere(); - void play(); - void stop(); + void playBA(); + void stopBA(); void setPlayMode(PlayMode mode); diff --git a/engines/gob/sound/sound.cpp b/engines/gob/sound/sound.cpp index 5950388c28..1aa63eb940 100644 --- a/engines/gob/sound/sound.cpp +++ b/engines/gob/sound/sound.cpp @@ -648,7 +648,7 @@ void Sound::bgPlay(const char *file, SoundType type) { debugC(1, kDebugSound, "BackgroundAtmosphere: Playing \"%s\"", file); - _bgatmos->stop(); + _bgatmos->stopBA(); _bgatmos->queueClear(); SoundDesc *sndDesc = new SoundDesc; @@ -658,7 +658,7 @@ void Sound::bgPlay(const char *file, SoundType type) { } _bgatmos->queueSample(*sndDesc); - _bgatmos->play(); + _bgatmos->playBA(); } void Sound::bgPlay(const char *base, const char *ext, SoundType type, int count) { @@ -667,7 +667,7 @@ void Sound::bgPlay(const char *base, const char *ext, SoundType type, int count) debugC(1, kDebugSound, "BackgroundAtmosphere: Playing \"%s\" (%d)", base, count); - _bgatmos->stop(); + _bgatmos->stopBA(); _bgatmos->queueClear(); int length = strlen(base) + 7; @@ -684,7 +684,7 @@ void Sound::bgPlay(const char *base, const char *ext, SoundType type, int count) delete sndDesc; } - _bgatmos->play(); + _bgatmos->playBA(); } void Sound::bgStop() { @@ -693,7 +693,7 @@ void Sound::bgStop() { debugC(1, kDebugSound, "BackgroundAtmosphere: Stopping playback"); - _bgatmos->stop(); + _bgatmos->stopBA(); _bgatmos->queueClear(); } diff --git a/engines/groovie/music.h b/engines/groovie/music.h index 74bb6f98e5..5974559c53 100644 --- a/engines/groovie/music.h +++ b/engines/groovie/music.h @@ -94,14 +94,14 @@ public: ~MusicPlayerMidi(); // MidiDriver interface - int open(); - void close(); - void send(uint32 b); - void metaEvent(byte type, byte *data, uint16 length); - void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc); - uint32 getBaseTempo(); - MidiChannel *allocateChannel(); - MidiChannel *getPercussionChannel(); + virtual int open(); + virtual void close(); + virtual void send(uint32 b); + virtual void metaEvent(byte type, byte *data, uint16 length); + virtual void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc); + virtual uint32 getBaseTempo(); + virtual MidiChannel *allocateChannel(); + virtual MidiChannel *getPercussionChannel(); private: // Channel volumes @@ -115,7 +115,7 @@ protected: MidiParser *_midiParser; MidiDriver *_driver; - void onTimerInternal(); + virtual void onTimerInternal(); void updateVolume(); void unload(); diff --git a/engines/hugo/detection.cpp b/engines/hugo/detection.cpp index c99db2433f..e862e339ce 100644 --- a/engines/hugo/detection.cpp +++ b/engines/hugo/detection.cpp @@ -292,6 +292,11 @@ SaveStateDescriptor HugoMetaEngine::querySaveMetaInfos(const char *target, int s desc.setSaveTime(hour, minutes); + // Slot 0 is used for the 'restart game' save in all Hugo games, thus + // we prevent it from being deleted. + desc.setDeletableFlag(slot != 0); + desc.setWriteProtectedFlag(slot == 0); + delete file; return desc; } diff --git a/engines/hugo/menu.cpp b/engines/hugo/dialogs.cpp index 428a3e096a..ead432c5df 100644 --- a/engines/hugo/menu.cpp +++ b/engines/hugo/dialogs.cpp @@ -196,8 +196,6 @@ void TopMenu::handleCommand(GUI::CommandSender *sender, uint32 command, uint32 d case kCmdLoad: close(); _vm->_file->restoreGame(-1); - _vm->_scheduler->restoreScreen(*_vm->_screen_p); - _vm->getGameStatus().viewState = kViewPlay; break; case kCmdRecall: close(); @@ -232,4 +230,33 @@ void TopMenu::handleMouseUp(int x, int y, int button, int clickCount) { Dialog::handleMouseUp(x, y, button, clickCount); } +EntryDialog::EntryDialog(const Common::String &title, const Common::String &buttonLabel, const Common::String &defaultValue) : GUI::Dialog(20, 20, 100, 50) { + new GUI::StaticTextWidget(this, 0, 0, 10, 10, title, Graphics::kTextAlignCenter); + + _text = new GUI::EditTextWidget(this, 0, 0, 50, 10, ""); + _text->setEditString(defaultValue); + + new GUI::ButtonWidget(this, 20, 20, 30, 10, buttonLabel, 0, kCmdButton); +} + +EntryDialog::~EntryDialog() { +} + +void EntryDialog::handleCommand(GUI::CommandSender *sender, uint32 command, uint32 data) { + switch (command) { + case kCmdButton: + close(); + break; + default: + Dialog::handleCommand(sender, command, data); + } +} + +void EntryDialog::reflowLayout() { +} + +void EntryDialog::init() { +} + + } // End of namespace Hugo diff --git a/engines/hugo/menu.h b/engines/hugo/dialogs.h index 6e2a9063bc..1ef196a708 100644 --- a/engines/hugo/menu.h +++ b/engines/hugo/dialogs.h @@ -23,10 +23,11 @@ * */ -#ifndef HUGO_TOPMENU_H -#define HUGO_TOPMENU_H +#ifndef HUGO_DIALOGS_H +#define HUGO_DIALOGS_H #include "gui/dialog.h" +#include "gui/widgets/edittext.h" namespace Hugo { @@ -54,6 +55,7 @@ enum { }; enum { + // TopMenu commands kCmdWhat = 'WHAT', kCmdMusic = 'MUZK', kCmdSoundFX = 'SOUN', @@ -62,7 +64,10 @@ enum { kCmdRecall = 'RECL', kCmdTurbo = 'TURB', kCmdLook = 'LOOK', - kCmdInvent = 'INVT' + kCmdInvent = 'INVT', + + // EntryDialog commands + kCmdButton = 'BTNP' }; class TopMenu : public GUI::Dialog { @@ -95,6 +100,21 @@ protected: uint16 arraySize; }; +class EntryDialog : public GUI::Dialog { + EntryDialog(const Common::String &title, const Common::String &buttonLabel, const Common::String &defaultValue); + virtual ~EntryDialog(); + + void reflowLayout(); + void handleCommand(GUI::CommandSender *sender, uint32 command, uint32 data); + + const Common::String &getEditString() const { return _text->getEditString(); } + +protected: + void init(); + + GUI::EditTextWidget *_text; +}; + } -#endif // HUGO_TOPMENU_H +#endif // HUGO_DIALOGS_H diff --git a/engines/hugo/display.cpp b/engines/hugo/display.cpp index 0210eb8f04..aa4d4384c1 100644 --- a/engines/hugo/display.cpp +++ b/engines/hugo/display.cpp @@ -189,13 +189,13 @@ void Screen::displayRect(const int16 x, const int16 y, const int16 dx, const int * Alse save the new color in the current palette. */ void Screen::remapPal(const uint16 oldIndex, const uint16 newIndex) { - debugC(1, kDebugDisplay, "Remap_pal(%d, %d)", oldIndex, newIndex); + debugC(1, kDebugDisplay, "RemapPal(%d, %d)", oldIndex, newIndex); _curPalette[3 * oldIndex + 0] = _mainPalette[newIndex * 3 + 0]; _curPalette[3 * oldIndex + 1] = _mainPalette[newIndex * 3 + 1]; _curPalette[3 * oldIndex + 2] = _mainPalette[newIndex * 3 + 2]; - g_system->getPaletteManager()->setPalette(_curPalette, oldIndex, 1); + g_system->getPaletteManager()->setPalette(_curPalette, 0, _paletteSize / 3); } /** @@ -214,10 +214,10 @@ void Screen::savePal(Common::WriteStream *f) const { void Screen::restorePal(Common::ReadStream *f) { debugC(1, kDebugDisplay, "restorePal()"); - for (int i = 0; i < _paletteSize; i++) { + for (int i = 0; i < _paletteSize; i++) _curPalette[i] = f->readByte(); - g_system->getPaletteManager()->setPalette(_curPalette, i, 1); - } + + g_system->getPaletteManager()->setPalette(_curPalette, 0, _paletteSize / 3); } @@ -233,24 +233,6 @@ void Screen::setBackgroundColor(const uint16 color) { } /** - * Return the overlay state (Foreground/Background) of the currently - * processed object by looking down the current column for an overlay - * base bit set (in which case the object is foreground). - */ -overlayState_t Screen::findOvl(seq_t *seq_p, image_pt dst_p, uint16 y) { - debugC(4, kDebugDisplay, "findOvl()"); - - for (; y < seq_p->lines; y++) { // Each line in object - byte ovb = _vm->_object->getBaseBoundary((uint16)(dst_p - _frontBuffer) >> 3); // Ptr into overlay bits - if (ovb & (0x80 >> ((uint16)(dst_p - _frontBuffer) & 7))) // Overlay bit is set - return kOvlForeground; // Found a bit - must be foreground - dst_p += kXPix; - } - - return kOvlBackground; // No bits set, must be background -} - -/** * Merge an object frame into _frontBuffer at sx, sy and update rectangle list. * If fore TRUE, force object above any overlay */ @@ -261,7 +243,7 @@ void Screen::displayFrame(const int sx, const int sy, seq_t *seq, const bool for image_pt subFrontBuffer = &_frontBuffer[sy * kXPix + sx]; // Ptr to offset in _frontBuffer int16 frontBufferwrap = kXPix - seq->x2 - 1; // Wraps dest_p after each line int16 imageWrap = seq->bytesPerLine8 - seq->x2 - 1; - overlayState_t overlayState = kOvlUndef; // Overlay state of object + overlayState_t overlayState = (foreFl) ? kOvlForeground : kOvlUndef; // Overlay state of object for (uint16 y = 0; y < seq->lines; y++) { // Each line in object for (uint16 x = 0; x <= seq->x2; x++) { if (*image) { // Non-transparent @@ -269,7 +251,7 @@ void Screen::displayFrame(const int sx, const int sy, seq_t *seq, const bool for if (ovlBound & (0x80 >> ((uint16)(subFrontBuffer - _frontBuffer) & 7))) { // Overlay bit is set if (overlayState == kOvlUndef) // Overlay defined yet? overlayState = findOvl(seq, subFrontBuffer, y);// No, find it. - if (foreFl || overlayState == kOvlForeground) // Object foreground + if (overlayState == kOvlForeground) // Object foreground *subFrontBuffer = *image; // Copy pixel } else { // No overlay *subFrontBuffer = *image; // Copy pixel @@ -740,6 +722,25 @@ void Screen_v1d::loadFontArr(Common::ReadStream &in) { } } +/** + * Return the overlay state (Foreground/Background) of the currently + * processed object by looking down the current column for an overlay + * base byte set (in which case the object is foreground). + */ +overlayState_t Screen_v1d::findOvl(seq_t *seq_p, image_pt dst_p, uint16 y) { + debugC(4, kDebugDisplay, "findOvl()"); + + uint16 index = (uint16)(dst_p - _frontBuffer) >> 3; + + for (int i = 0; i < seq_p->lines-y; i++) { // Each line in object + if (_vm->_object->getBaseBoundary(index)) // If any overlay base byte is non-zero then the object is foreground, else back. + return kOvlForeground; + index += kCompLineSize; + } + + return kOvlBackground; // No bits set, must be background +} + Screen_v1w::Screen_v1w(HugoEngine *vm) : Screen(vm) { } @@ -790,5 +791,23 @@ void Screen_v1w::loadFontArr(Common::ReadStream &in) { } } +/** + * Return the overlay state (Foreground/Background) of the currently + * processed object by looking down the current column for an overlay + * base bit set (in which case the object is foreground). + */ +overlayState_t Screen_v1w::findOvl(seq_t *seq_p, image_pt dst_p, uint16 y) { + debugC(4, kDebugDisplay, "findOvl()"); + + for (; y < seq_p->lines; y++) { // Each line in object + byte ovb = _vm->_object->getBaseBoundary((uint16)(dst_p - _frontBuffer) >> 3); // Ptr into overlay bits + if (ovb & (0x80 >> ((uint16)(dst_p - _frontBuffer) & 7))) // Overlay bit is set + return kOvlForeground; // Found a bit - must be foreground + dst_p += kXPix; + } + + return kOvlBackground; // No bits set, must be background +} + } // End of namespace Hugo diff --git a/engines/hugo/display.h b/engines/hugo/display.h index f234f76019..91e1752df0 100644 --- a/engines/hugo/display.h +++ b/engines/hugo/display.h @@ -101,10 +101,6 @@ protected: static const byte stdMouseCursorHeight = 20; static const byte stdMouseCursorWidth = 12; - inline bool isInX(const int16 x, const rect_t *rect) const; - inline bool isInY(const int16 y, const rect_t *rect) const; - inline bool isOverlapping(const rect_t *rectA, const rect_t *rectB) const; - bool fontLoadedFl[kNumFonts]; // Fonts used in dib (non-GDI) @@ -115,6 +111,14 @@ protected: byte *_mainPalette; int16 _arrayFontSize[kNumFonts]; + viewdib_t _frontBuffer; + + inline bool isInX(const int16 x, const rect_t *rect) const; + inline bool isInY(const int16 y, const rect_t *rect) const; + inline bool isOverlapping(const rect_t *rectA, const rect_t *rectB) const; + + virtual overlayState_t findOvl(seq_t *seq_p, image_pt dst_p, uint16 y) = 0; + private: byte *_curPalette; byte _iconImage[kInvDx * kInvDy]; @@ -125,9 +129,6 @@ private: int16 mergeLists(rect_t *list, rect_t *blist, const int16 len, int16 blen); int16 center(const char *s) const; - overlayState_t findOvl(seq_t *seq_p, image_pt dst_p, uint16 y); - - viewdib_t _frontBuffer; viewdib_t _backBuffer; viewdib_t _GUIBuffer; // User interface images viewdib_t _backBufferBackup; // Backup _backBuffer during inventory @@ -151,6 +152,8 @@ public: void loadFont(int16 fontId); void loadFontArr(Common::ReadStream &in); +protected: + overlayState_t findOvl(seq_t *seq_p, image_pt dst_p, uint16 y); }; class Screen_v1w : public Screen { @@ -160,6 +163,8 @@ public: void loadFont(int16 fontId); void loadFontArr(Common::ReadStream &in); +protected: + overlayState_t findOvl(seq_t *seq_p, image_pt dst_p, uint16 y); }; } // End of namespace Hugo diff --git a/engines/hugo/file.cpp b/engines/hugo/file.cpp index 740513d7b2..94e1756a0d 100644 --- a/engines/hugo/file.cpp +++ b/engines/hugo/file.cpp @@ -411,6 +411,8 @@ bool FileManager::saveGame(const int16 slot, const Common::String &descrip) { out->writeSint16BE(_vm->_maze.x4); out->writeByte(_vm->_maze.firstScreenIndex); + out->writeByte((byte)_vm->getGameStatus().viewState); + out->finalize(); delete out; @@ -507,6 +509,11 @@ bool FileManager::restoreGame(const int16 slot) { _vm->_maze.x4 = in->readSint16BE(); _vm->_maze.firstScreenIndex = in->readByte(); + _vm->_scheduler->restoreScreen(*_vm->_screen_p); + if ((_vm->getGameStatus().viewState = (vstate_t) in->readByte()) != kViewPlay) + _vm->_screen->hideCursor(); + + delete in; return true; } @@ -563,6 +570,8 @@ void FileManager::readBootFile() { if (_vm->_gameVariant == kGameVariantH1Dos) { //TODO initialize properly _boot structure warning("readBootFile - Skipping as H1 Dos may be a freeware"); + memset(_vm->_boot.distrib, '\0', sizeof(_vm->_boot.distrib)); + _vm->_boot.registered = kRegFreeware; return; } else { error("Missing startup file"); diff --git a/engines/hugo/hugo.cpp b/engines/hugo/hugo.cpp index 2143b8cfde..231b2a5d51 100644 --- a/engines/hugo/hugo.cpp +++ b/engines/hugo/hugo.cpp @@ -28,6 +28,7 @@ #include "common/events.h" #include "common/EventRecorder.h" #include "common/debug-channels.h" +#include "common/config-manager.h" #include "hugo/hugo.h" #include "hugo/game.h" @@ -253,14 +254,20 @@ Common::Error HugoEngine::run() { // Start the state machine _status.viewState = kViewIntroInit; - _status.doQuitFl = false; + int16 loadSlot = Common::ConfigManager::instance().getInt("save_slot"); + if (loadSlot >= 0) { + _status.skipIntroFl = true; + _file->restoreGame(loadSlot); + } else { + _file->saveGame(0, "New Game"); + } while (!_status.doQuitFl) { _screen->drawBoundaries(); - g_system->updateScreen(); runMachine(); + // Handle input Common::Event event; while (_eventMan->pollEvent(event)) { @@ -285,6 +292,7 @@ Common::Error HugoEngine::run() { break; } } + _mouse->mouseHandler(); // Mouse activity - adds to display list _screen->displayList(kDisplayDisplay); // Blit the display list to screen _status.doQuitFl |= shouldQuit(); // update game quit flag @@ -623,6 +631,13 @@ void HugoEngine::readScreenFiles(const int screenNum) { _file->readBackground(screenNum); // Scenery file memcpy(_screen->getBackBuffer(), _screen->getFrontBuffer(), sizeof(_screen->getFrontBuffer())); // Make a copy + + // Workaround for graphic glitches in DOS versions. Cleaning the overlays fix the problem + memset(_object->_objBound, '\0', sizeof(overlay_t)); + memset(_object->_boundary, '\0', sizeof(overlay_t)); + memset(_object->_overlay, '\0', sizeof(overlay_t)); + memset(_object->_ovlBase, '\0', sizeof(overlay_t)); + _file->readOverlay(screenNum, _object->_boundary, kOvlBoundary); // Boundary file _file->readOverlay(screenNum, _object->_overlay, kOvlOverlay); // Overlay file _file->readOverlay(screenNum, _object->_ovlBase, kOvlBase); // Overlay base file @@ -658,7 +673,7 @@ void HugoEngine::calcMaxScore() { void HugoEngine::endGame() { debugC(1, kDebugEngine, "endGame"); - if (!_boot.registered) + if (_boot.registered != kRegRegistered) Utils::Box(kBoxAny, "%s", _text->getTextEngine(kEsAdvertise)); Utils::Box(kBoxAny, "%s\n%s", _episode, getCopyrightString()); _status.viewState = kViewExit; diff --git a/engines/hugo/hugo.h b/engines/hugo/hugo.h index b2739c829e..a137df786f 100644 --- a/engines/hugo/hugo.h +++ b/engines/hugo/hugo.h @@ -29,7 +29,7 @@ #include "engines/engine.h" #include "common/file.h" #include "hugo/console.h" -#include "hugo/menu.h" +#include "hugo/dialogs.h" // This include is here temporarily while the engine is being refactored. #include "hugo/game.h" @@ -58,7 +58,7 @@ class RandomSource; */ namespace Hugo { -static const int kSavegameVersion = 3; +static const int kSavegameVersion = 6; static const int kInvDx = 32; // Width of an inventory icon static const int kInvDy = 32; // Height of inventory icon static const int kMaxTunes = 16; // Max number of tunes @@ -125,6 +125,12 @@ enum HugoDebugChannels { kDebugMusic = 1 << 9 }; +enum HugoRegistered { + kRegShareware = 0, + kRegRegistered, + kRegFreeware +}; + /** * Ways to dismiss a text/prompt box */ @@ -242,7 +248,6 @@ public: object_t *_hero; byte *_screen_p; byte _heroImage; - byte *_screenStates; command_t _line; // Line of user text input config_t _config; // User's config diff --git a/engines/hugo/intro.cpp b/engines/hugo/intro.cpp index 689dfbfd7c..7551476300 100644 --- a/engines/hugo/intro.cpp +++ b/engines/hugo/intro.cpp @@ -119,15 +119,19 @@ bool intro_v1d::introPlay() { error("Unable to load font TMSRB.FON, face 'Tms Rmn', size 8"); char buffer[80]; - if (_vm->_boot.registered) + if (_vm->_boot.registered == kRegRegistered) strcpy(buffer, "Registered Version"); - else + else if (_vm->_boot.registered == kRegShareware) strcpy(buffer, "Shareware Version"); + else if (_vm->_boot.registered == kRegFreeware) + strcpy(buffer, "Freeware Version"); + else + error("Unknown registration flag in hugo.bsf: %d", _vm->_boot.registered); font.drawString(&surf, buffer, 0, 163, 320, _TLIGHTMAGENTA, Graphics::kTextAlignCenter); font.drawString(&surf, _vm->getCopyrightString(), 0, 176, 320, _TLIGHTMAGENTA, Graphics::kTextAlignCenter); - if (scumm_stricmp(_vm->_boot.distrib, "David P. Gray")) { + if ((*_vm->_boot.distrib != '\0') && (scumm_stricmp(_vm->_boot.distrib, "David P. Gray"))) { sprintf(buffer, "Distributed by %s.", _vm->_boot.distrib); font.drawString(&surf, buffer, 0, 75, 320, _TMAGENTA, Graphics::kTextAlignCenter); } diff --git a/engines/hugo/module.mk b/engines/hugo/module.mk index 3a1ceded73..2ded997437 100644 --- a/engines/hugo/module.mk +++ b/engines/hugo/module.mk @@ -3,6 +3,7 @@ MODULE := engines/hugo MODULE_OBJS := \ console.o \ detection.o \ + dialogs.o \ display.o \ file.o \ file_v1d.o \ @@ -13,7 +14,6 @@ MODULE_OBJS := \ hugo.o \ intro.o \ inventory.o \ - menu.o \ mouse.o \ object.o \ object_v1d.o \ diff --git a/engines/hugo/object.cpp b/engines/hugo/object.cpp index 04e3449cbb..f82a6a53c6 100644 --- a/engines/hugo/object.cpp +++ b/engines/hugo/object.cpp @@ -418,20 +418,25 @@ void ObjectHandler::readUse(Common::ReadStream &in, uses_t &curUse) { */ void ObjectHandler::loadObjectUses(Common::ReadStream &in) { uses_t tmpUse; + tmpUse.targets = 0; + //Read _uses for (int varnt = 0; varnt < _vm->_numVariant; varnt++) { - tmpUse.targets = 0; uint16 numElem = in.readUint16BE(); if (varnt == _vm->_gameVariant) { _usesSize = numElem; _uses = (uses_t *)malloc(sizeof(uses_t) * numElem); } - for (int i = 0; i < numElem; i++) - readUse(in, (varnt == _vm->_gameVariant) ? _uses[i] : tmpUse); - - if (tmpUse.targets) - free(tmpUse.targets); + for (int i = 0; i < numElem; i++) { + if (varnt == _vm->_gameVariant) + readUse(in, _uses[i]); + else { + readUse(in, tmpUse); + free(tmpUse.targets); + tmpUse.targets = 0; + } + } } } @@ -497,20 +502,26 @@ void ObjectHandler::readObject(Common::ReadStream &in, object_t &curObject) { void ObjectHandler::loadObjectArr(Common::ReadStream &in) { debugC(6, kDebugObject, "loadObject(&in)"); object_t tmpObject; + tmpObject.stateDataIndex = 0; for (int varnt = 0; varnt < _vm->_numVariant; varnt++) { uint16 numElem = in.readUint16BE(); - tmpObject.stateDataIndex = 0; + if (varnt == _vm->_gameVariant) { _objCount = numElem; _objects = (object_t *)malloc(sizeof(object_t) * numElem); } - for (int i = 0; i < numElem; i++) - readObject(in, (varnt == _vm->_gameVariant) ? _objects[i] : tmpObject); - - if (tmpObject.stateDataIndex) - free(tmpObject.stateDataIndex); + for (int i = 0; i < numElem; i++) { + if (varnt == _vm->_gameVariant) + readObject(in, _objects[i]); + else { + // Skip over uneeded objects. + readObject(in, tmpObject); + free(tmpObject.stateDataIndex); + tmpObject.stateDataIndex = 0; + } + } } } diff --git a/engines/hugo/parser.cpp b/engines/hugo/parser.cpp index c845e03eee..a0a3ee3350 100644 --- a/engines/hugo/parser.cpp +++ b/engines/hugo/parser.cpp @@ -286,11 +286,10 @@ void Parser::keyHandler(Common::Event event) { break; case Common::KEYCODE_l: _vm->_file->restoreGame(-1); - _vm->_scheduler->restoreScreen(*_vm->_screen_p); - gameStatus.viewState = kViewPlay; break; case Common::KEYCODE_n: - warning("STUB: CTRL-N (WIN) - New Game"); + if (Utils::Box(kBoxYesNo, "%s", "Are you sure you want to start a new game?") != 0) + _vm->_file->restoreGame(0); break; case Common::KEYCODE_s: if (gameStatus.viewState == kViewPlay) { @@ -360,8 +359,6 @@ void Parser::keyHandler(Common::Event event) { break; case Common::KEYCODE_F5: // Restore game _vm->_file->restoreGame(-1); - _vm->_scheduler->restoreScreen(*_vm->_screen_p); - gameStatus.viewState = kViewPlay; break; case Common::KEYCODE_F6: // Inventory showInventory(); diff --git a/engines/hugo/parser_v1d.cpp b/engines/hugo/parser_v1d.cpp index 424ca66b38..df3a73feb8 100644 --- a/engines/hugo/parser_v1d.cpp +++ b/engines/hugo/parser_v1d.cpp @@ -380,8 +380,6 @@ void Parser_v1d::lineHandler() { if (!strcmp("restore", _vm->_line)) { _vm->_file->restoreGame(-1); - _vm->_scheduler->restoreScreen(*_vm->_screen_p); - gameStatus.viewState = kViewPlay; return; } diff --git a/engines/hugo/parser_v1w.cpp b/engines/hugo/parser_v1w.cpp index 5d901e8ffe..d3424274f8 100644 --- a/engines/hugo/parser_v1w.cpp +++ b/engines/hugo/parser_v1w.cpp @@ -132,8 +132,6 @@ void Parser_v1w::lineHandler() { if (!strcmp("restore", _vm->_line) && (gameStatus.viewState == kViewPlay || gameStatus.viewState == kViewIdle)) { _vm->_file->restoreGame(-1); - _vm->_scheduler->restoreScreen(*_vm->_screen_p); - gameStatus.viewState = kViewPlay; return; } diff --git a/engines/hugo/parser_v2d.cpp b/engines/hugo/parser_v2d.cpp index 580d78100d..d19b8b1091 100644 --- a/engines/hugo/parser_v2d.cpp +++ b/engines/hugo/parser_v2d.cpp @@ -132,8 +132,6 @@ void Parser_v2d::lineHandler() { if (!strcmp("restore", _vm->_line)) { _vm->_config.soundFl = false; _vm->_file->restoreGame(-1); - _vm->_scheduler->restoreScreen(*_vm->_screen_p); - gameStatus.viewState = kViewPlay; return; } diff --git a/engines/hugo/parser_v3d.cpp b/engines/hugo/parser_v3d.cpp index b3d0125d20..fcd937808b 100644 --- a/engines/hugo/parser_v3d.cpp +++ b/engines/hugo/parser_v3d.cpp @@ -134,8 +134,6 @@ void Parser_v3d::lineHandler() { if (!strcmp("restore", _vm->_line)) { _vm->_config.soundFl = false; _vm->_file->restoreGame(-1); - _vm->_scheduler->restoreScreen(*_vm->_screen_p); - gameStatus.viewState = kViewPlay; return; } diff --git a/engines/hugo/schedule.cpp b/engines/hugo/schedule.cpp index 5843c2317e..1556f3a154 100644 --- a/engines/hugo/schedule.cpp +++ b/engines/hugo/schedule.cpp @@ -708,21 +708,19 @@ void Scheduler::saveEvents(Common::WriteStream *f) { f->writeSint16BE(tailIndex); // Convert event ptrs to indexes - event_t saveEventArr[kMaxEvents]; // Convert event ptrs to indexes for (int16 i = 0; i < kMaxEvents; i++) { event_t *wrkEvent = &_events[i]; - saveEventArr[i] = *wrkEvent; - // fix up action pointer (to do better) + // fix up action pointer (to do better) int16 index, subElem; - findAction(saveEventArr[i].action, &index, &subElem); - saveEventArr[i].action = (act*)((index << 16)| subElem); - - saveEventArr[i].prevEvent = (wrkEvent->prevEvent == 0) ? (event_t *) - 1 : (event_t *)(wrkEvent->prevEvent - _events); - saveEventArr[i].nextEvent = (wrkEvent->nextEvent == 0) ? (event_t *) - 1 : (event_t *)(wrkEvent->nextEvent - _events); + findAction(wrkEvent->action, &index, &subElem); + f->writeSint16BE(index); + f->writeSint16BE(subElem); + f->writeByte((wrkEvent->localActionFl) ? 1 : 0); + f->writeUint32BE(wrkEvent->time); + f->writeSint16BE((wrkEvent->prevEvent == 0) ? -1 : (wrkEvent->prevEvent - _events)); + f->writeSint16BE((wrkEvent->nextEvent == 0) ? -1 : (wrkEvent->nextEvent - _events)); } - - f->write(saveEventArr, sizeof(saveEventArr)); } /** @@ -730,27 +728,11 @@ void Scheduler::saveEvents(Common::WriteStream *f) { */ void Scheduler::restoreActions(Common::ReadStream *f) { - for (int i = 0; i < _actListArrSize; i++) { - - // read all the sub elems - int j = 0; - do { - - // handle special case for a3, keep list pointer - int* responsePtr = 0; - if (_actListArr[i][j].a3.actType == PROMPT) { - responsePtr = _actListArr[i][j].a3.responsePtr; - } - - f->read(&_actListArr[i][j], sizeof(act)); - - // handle special case for a3, reset list pointer - if (_actListArr[i][j].a3.actType == PROMPT) { - _actListArr[i][j].a3.responsePtr = responsePtr; - } - j++; - } while (_actListArr[i][j-1].a0.actType != ANULL); + uint16 numSubElem = f->readUint16BE(); + for (int j = 0; j < numSubElem; j++) { + readAct(*f, _actListArr[i][j]); + } } } @@ -764,23 +746,301 @@ int16 Scheduler::calcMaxPoints() const { /* * Save the action data in the file with handle f */ -void Scheduler::saveActions(Common::WriteStream* f) const { +void Scheduler::saveActions(Common::WriteStream *f) const { + byte subElemType; + int16 nbrCpt; + uint16 nbrSubElem; + for (int i = 0; i < _actListArrSize; i++) { // write all the sub elems data - - int j = 0; - do { - f->write(&_actListArr[i][j], sizeof(act)); - j++; - } while (_actListArr[i][j-1].a0.actType != ANULL); + for (nbrSubElem = 1; _actListArr[i][nbrSubElem - 1].a0.actType != ANULL; nbrSubElem++) + ; + + f->writeUint16BE(nbrSubElem); + for (int j = 0; j < nbrSubElem; j++) { + subElemType = _actListArr[i][j].a0.actType; + f->writeByte(subElemType); + switch (subElemType) { + case ANULL: // -1 + break; + case ASCHEDULE: // 0 + f->writeSint16BE(_actListArr[i][j].a0.timer); + f->writeUint16BE(_actListArr[i][j].a0.actIndex); + break; + case START_OBJ: // 1 + f->writeSint16BE(_actListArr[i][j].a1.timer); + f->writeSint16BE(_actListArr[i][j].a1.objIndex); + f->writeSint16BE(_actListArr[i][j].a1.cycleNumb); + f->writeByte(_actListArr[i][j].a1.cycle); + break; + case INIT_OBJXY: // 2 + f->writeSint16BE(_actListArr[i][j].a2.timer); + f->writeSint16BE(_actListArr[i][j].a2.objIndex); + f->writeSint16BE(_actListArr[i][j].a2.x); + f->writeSint16BE(_actListArr[i][j].a2.y); + break; + case PROMPT: // 3 + f->writeSint16BE(_actListArr[i][j].a3.timer); + f->writeSint16BE(_actListArr[i][j].a3.promptIndex); + for (nbrCpt = 0; _actListArr[i][j].a3.responsePtr[nbrCpt] != -1; nbrCpt++) + ; + nbrCpt++; + f->writeUint16BE(nbrCpt); + for (int k = 0; k < nbrCpt; k++) + f->writeSint16BE(_actListArr[i][j].a3.responsePtr[k]); + f->writeUint16BE(_actListArr[i][j].a3.actPassIndex); + f->writeUint16BE(_actListArr[i][j].a3.actFailIndex); + f->writeByte((_actListArr[i][j].a3.encodedFl) ? 1 : 0); + break; + case BKGD_COLOR: // 4 + f->writeSint16BE(_actListArr[i][j].a4.timer); + f->writeUint32BE(_actListArr[i][j].a4.newBackgroundColor); + break; + case INIT_OBJVXY: // 5 + f->writeSint16BE(_actListArr[i][j].a5.timer); + f->writeSint16BE(_actListArr[i][j].a5.objIndex); + f->writeSint16BE(_actListArr[i][j].a5.vx); + f->writeSint16BE(_actListArr[i][j].a5.vy); + break; + case INIT_CARRY: // 6 + f->writeSint16BE(_actListArr[i][j].a6.timer); + f->writeSint16BE(_actListArr[i][j].a6.objIndex); + f->writeByte((_actListArr[i][j].a6.carriedFl) ? 1 : 0); + break; + case INIT_HF_COORD: // 7 + f->writeSint16BE(_actListArr[i][j].a7.timer); + f->writeSint16BE(_actListArr[i][j].a7.objIndex); + break; + case NEW_SCREEN: // 8 + f->writeSint16BE(_actListArr[i][j].a8.timer); + f->writeSint16BE(_actListArr[i][j].a8.screenIndex); + break; + case INIT_OBJSTATE: // 9 + f->writeSint16BE(_actListArr[i][j].a9.timer); + f->writeSint16BE(_actListArr[i][j].a9.objIndex); + f->writeByte(_actListArr[i][j].a9.newState); + break; + case INIT_PATH: // 10 + f->writeSint16BE(_actListArr[i][j].a10.timer); + f->writeSint16BE(_actListArr[i][j].a10.objIndex); + f->writeSint16BE(_actListArr[i][j].a10.newPathType); + f->writeByte(_actListArr[i][j].a10.vxPath); + f->writeByte(_actListArr[i][j].a10.vyPath); + break; + case COND_R: // 11 + f->writeSint16BE(_actListArr[i][j].a11.timer); + f->writeSint16BE(_actListArr[i][j].a11.objIndex); + f->writeByte(_actListArr[i][j].a11.stateReq); + f->writeUint16BE(_actListArr[i][j].a11.actPassIndex); + f->writeUint16BE(_actListArr[i][j].a11.actFailIndex); + break; + case TEXT: // 12 + f->writeSint16BE(_actListArr[i][j].a12.timer); + f->writeSint16BE(_actListArr[i][j].a12.stringIndex); + break; + case SWAP_IMAGES: // 13 + f->writeSint16BE(_actListArr[i][j].a13.timer); + f->writeSint16BE(_actListArr[i][j].a13.objIndex1); + f->writeSint16BE(_actListArr[i][j].a13.objIndex2); + break; + case COND_SCR: // 14 + f->writeSint16BE(_actListArr[i][j].a14.timer); + f->writeSint16BE(_actListArr[i][j].a14.objIndex); + f->writeSint16BE(_actListArr[i][j].a14.screenReq); + f->writeUint16BE(_actListArr[i][j].a14.actPassIndex); + f->writeUint16BE(_actListArr[i][j].a14.actFailIndex); + break; + case AUTOPILOT: // 15 + f->writeSint16BE(_actListArr[i][j].a15.timer); + f->writeSint16BE(_actListArr[i][j].a15.objIndex1); + f->writeSint16BE(_actListArr[i][j].a15.objIndex2); + f->writeByte(_actListArr[i][j].a15.dx); + f->writeByte(_actListArr[i][j].a15.dy); + break; + case INIT_OBJ_SEQ: // 16 + f->writeSint16BE(_actListArr[i][j].a16.timer); + f->writeSint16BE(_actListArr[i][j].a16.objIndex); + f->writeSint16BE(_actListArr[i][j].a16.seqIndex); + break; + case SET_STATE_BITS: // 17 + f->writeSint16BE(_actListArr[i][j].a17.timer); + f->writeSint16BE(_actListArr[i][j].a17.objIndex); + f->writeSint16BE(_actListArr[i][j].a17.stateMask); + break; + case CLEAR_STATE_BITS: // 18 + f->writeSint16BE(_actListArr[i][j].a18.timer); + f->writeSint16BE(_actListArr[i][j].a18.objIndex); + f->writeSint16BE(_actListArr[i][j].a18.stateMask); + break; + case TEST_STATE_BITS: // 19 + f->writeSint16BE(_actListArr[i][j].a19.timer); + f->writeSint16BE(_actListArr[i][j].a19.objIndex); + f->writeSint16BE(_actListArr[i][j].a19.stateMask); + f->writeUint16BE(_actListArr[i][j].a19.actPassIndex); + f->writeUint16BE(_actListArr[i][j].a19.actFailIndex); + break; + case DEL_EVENTS: // 20 + f->writeSint16BE(_actListArr[i][j].a20.timer); + f->writeByte(_actListArr[i][j].a20.actTypeDel); + break; + case GAMEOVER: // 21 + f->writeSint16BE(_actListArr[i][j].a21.timer); + break; + case INIT_HH_COORD: // 22 + f->writeSint16BE(_actListArr[i][j].a22.timer); + f->writeSint16BE(_actListArr[i][j].a22.objIndex); + break; + case EXIT: // 23 + f->writeSint16BE(_actListArr[i][j].a23.timer); + break; + case BONUS: // 24 + f->writeSint16BE(_actListArr[i][j].a24.timer); + f->writeSint16BE(_actListArr[i][j].a24.pointIndex); + break; + case COND_BOX: // 25 + f->writeSint16BE(_actListArr[i][j].a25.timer); + f->writeSint16BE(_actListArr[i][j].a25.objIndex); + f->writeSint16BE(_actListArr[i][j].a25.x1); + f->writeSint16BE(_actListArr[i][j].a25.y1); + f->writeSint16BE(_actListArr[i][j].a25.x2); + f->writeSint16BE(_actListArr[i][j].a25.y2); + f->writeUint16BE(_actListArr[i][j].a25.actPassIndex); + f->writeUint16BE(_actListArr[i][j].a25.actFailIndex); + break; + case SOUND: // 26 + f->writeSint16BE(_actListArr[i][j].a26.timer); + f->writeSint16BE(_actListArr[i][j].a26.soundIndex); + break; + case ADD_SCORE: // 27 + f->writeSint16BE(_actListArr[i][j].a27.timer); + f->writeSint16BE(_actListArr[i][j].a27.objIndex); + break; + case SUB_SCORE: // 28 + f->writeSint16BE(_actListArr[i][j].a28.timer); + f->writeSint16BE(_actListArr[i][j].a28.objIndex); + break; + case COND_CARRY: // 29 + f->writeSint16BE(_actListArr[i][j].a29.timer); + f->writeSint16BE(_actListArr[i][j].a29.objIndex); + f->writeUint16BE(_actListArr[i][j].a29.actPassIndex); + f->writeUint16BE(_actListArr[i][j].a29.actFailIndex); + break; + case INIT_MAZE: // 30 + f->writeSint16BE(_actListArr[i][j].a30.timer); + f->writeByte(_actListArr[i][j].a30.mazeSize); + f->writeSint16BE(_actListArr[i][j].a30.x1); + f->writeSint16BE(_actListArr[i][j].a30.y1); + f->writeSint16BE(_actListArr[i][j].a30.x2); + f->writeSint16BE(_actListArr[i][j].a30.y2); + f->writeSint16BE(_actListArr[i][j].a30.x3); + f->writeSint16BE(_actListArr[i][j].a30.x4); + f->writeByte(_actListArr[i][j].a30.firstScreenIndex); + break; + case EXIT_MAZE: // 31 + f->writeSint16BE(_actListArr[i][j].a31.timer); + break; + case INIT_PRIORITY: // 32 + f->writeSint16BE(_actListArr[i][j].a32.timer); + f->writeSint16BE(_actListArr[i][j].a32.objIndex); + f->writeByte(_actListArr[i][j].a32.priority); + break; + case INIT_SCREEN: // 33 + f->writeSint16BE(_actListArr[i][j].a33.timer); + f->writeSint16BE(_actListArr[i][j].a33.objIndex); + f->writeSint16BE(_actListArr[i][j].a33.screenIndex); + break; + case AGSCHEDULE: // 34 + f->writeSint16BE(_actListArr[i][j].a34.timer); + f->writeUint16BE(_actListArr[i][j].a34.actIndex); + break; + case REMAPPAL: // 35 + f->writeSint16BE(_actListArr[i][j].a35.timer); + f->writeSint16BE(_actListArr[i][j].a35.oldColorIndex); + f->writeSint16BE(_actListArr[i][j].a35.newColorIndex); + break; + case COND_NOUN: // 36 + f->writeSint16BE(_actListArr[i][j].a36.timer); + f->writeUint16BE(_actListArr[i][j].a36.nounIndex); + f->writeUint16BE(_actListArr[i][j].a36.actPassIndex); + f->writeUint16BE(_actListArr[i][j].a36.actFailIndex); + break; + case SCREEN_STATE: // 37 + f->writeSint16BE(_actListArr[i][j].a37.timer); + f->writeSint16BE(_actListArr[i][j].a37.screenIndex); + f->writeByte(_actListArr[i][j].a37.newState); + break; + case INIT_LIPS: // 38 + f->writeSint16BE(_actListArr[i][j].a38.timer); + f->writeSint16BE(_actListArr[i][j].a38.lipsObjIndex); + f->writeSint16BE(_actListArr[i][j].a38.objIndex); + f->writeByte(_actListArr[i][j].a38.dxLips); + f->writeByte(_actListArr[i][j].a38.dyLips); + break; + case INIT_STORY_MODE: // 39 + f->writeSint16BE(_actListArr[i][j].a39.timer); + f->writeByte((_actListArr[i][j].a39.storyModeFl) ? 1 : 0); + break; + case WARN: // 40 + f->writeSint16BE(_actListArr[i][j].a40.timer); + f->writeSint16BE(_actListArr[i][j].a40.stringIndex); + break; + case COND_BONUS: // 41 + f->writeSint16BE(_actListArr[i][j].a41.timer); + f->writeSint16BE(_actListArr[i][j].a41.BonusIndex); + f->writeUint16BE(_actListArr[i][j].a41.actPassIndex); + f->writeUint16BE(_actListArr[i][j].a41.actFailIndex); + break; + case TEXT_TAKE: // 42 + f->writeSint16BE(_actListArr[i][j].a42.timer); + f->writeSint16BE(_actListArr[i][j].a42.objIndex); + break; + case YESNO: // 43 + f->writeSint16BE(_actListArr[i][j].a43.timer); + f->writeSint16BE(_actListArr[i][j].a43.promptIndex); + f->writeUint16BE(_actListArr[i][j].a43.actYesIndex); + f->writeUint16BE(_actListArr[i][j].a43.actNoIndex); + break; + case STOP_ROUTE: // 44 + f->writeSint16BE(_actListArr[i][j].a44.timer); + break; + case COND_ROUTE: // 45 + f->writeSint16BE(_actListArr[i][j].a45.timer); + f->writeSint16BE(_actListArr[i][j].a45.routeIndex); + f->writeUint16BE(_actListArr[i][j].a45.actPassIndex); + f->writeUint16BE(_actListArr[i][j].a45.actFailIndex); + break; + case INIT_JUMPEXIT: // 46 + f->writeSint16BE(_actListArr[i][j].a46.timer); + f->writeByte((_actListArr[i][j].a46.jumpExitFl) ? 1 : 0); + break; + case INIT_VIEW: // 47 + f->writeSint16BE(_actListArr[i][j].a47.timer); + f->writeSint16BE(_actListArr[i][j].a47.objIndex); + f->writeSint16BE(_actListArr[i][j].a47.viewx); + f->writeSint16BE(_actListArr[i][j].a47.viewy); + f->writeSint16BE(_actListArr[i][j].a47.direction); + break; + case INIT_OBJ_FRAME: // 48 + f->writeSint16BE(_actListArr[i][j].a48.timer); + f->writeSint16BE(_actListArr[i][j].a48.objIndex); + f->writeSint16BE(_actListArr[i][j].a48.seqIndex); + f->writeSint16BE(_actListArr[i][j].a48.frameIndex); + break; + case OLD_SONG: // 49, Added by Strangerke for DOS versions + f->writeSint16BE(_actListArr[i][j].a49.timer); + f->writeUint16BE(_actListArr[i][j].a49.songIndex); + break; + default: + error("Unknown action %d", subElemType); + } + } } } /* * Find the index in the action list to be able to serialize the action to save game */ - -void Scheduler::findAction(act* action, int16* index, int16* subElem) { +void Scheduler::findAction(const act* action, int16* index, int16* subElem) { assert(index && subElem); if (!action) { @@ -831,28 +1091,30 @@ void Scheduler::restoreSchedulerData(Common::ReadStream *in) { void Scheduler::restoreEvents(Common::ReadStream *f) { debugC(1, kDebugSchedule, "restoreEvents"); - event_t savedEvents[kMaxEvents]; // Convert event ptrs to indexes - uint32 saveTime = f->readUint32BE(); // time of save int16 freeIndex = f->readSint16BE(); // Free list index int16 headIndex = f->readSint16BE(); // Head of list index int16 tailIndex = f->readSint16BE(); // Tail of list index - f->read(savedEvents, sizeof(savedEvents)); - event_t *wrkEvent; // Restore events indexes to pointers for (int i = 0; i < kMaxEvents; i++) { - wrkEvent = &savedEvents[i]; - _events[i] = *wrkEvent; + int16 index = f->readSint16BE(); + int16 subElem = f->readSint16BE(); + // fix up action pointer (to do better) - int32 val = (size_t)_events[i].action; - if ((val & 0xffff) == 0xffff) { + if ((index == -1) && (subElem == -1)) _events[i].action = 0; - } else { - _events[i].action = (act*)&_actListArr[val >> 16][val & 0xffff]; - } - _events[i].prevEvent = (wrkEvent->prevEvent == (event_t *) - 1) ? (event_t *)0 : &_events[(size_t)wrkEvent->prevEvent ]; - _events[i].nextEvent = (wrkEvent->nextEvent == (event_t *) - 1) ? (event_t *)0 : &_events[(size_t)wrkEvent->nextEvent ]; + else + _events[i].action = (act*)&_actListArr[index][subElem]; + + _events[i].localActionFl = (f->readByte() == 1) ? true : false; + _events[i].time = f->readUint32BE(); + + int16 prevIndex = f->readSint16BE(); + int16 nextIndex = f->readSint16BE(); + + _events[i].prevEvent = (prevIndex == -1) ? (event_t *)0 : &_events[prevIndex]; + _events[i].nextEvent = (nextIndex == -1) ? (event_t *)0 : &_events[nextIndex]; } _freeEvent = (freeIndex == -1) ? 0 : &_events[freeIndex]; _headEvent = (headIndex == -1) ? 0 : &_events[headIndex]; @@ -860,7 +1122,7 @@ void Scheduler::restoreEvents(Common::ReadStream *f) { // Adjust times to fit our time uint32 curTime = getTicks(); - wrkEvent = _headEvent; // The earliest event + event_t *wrkEvent = _headEvent; // The earliest event while (wrkEvent) { // While mature events found wrkEvent->time = wrkEvent->time - saveTime + curTime; wrkEvent = wrkEvent->nextEvent; diff --git a/engines/hugo/schedule.h b/engines/hugo/schedule.h index 953e9affea..a066fc63c4 100644 --- a/engines/hugo/schedule.h +++ b/engines/hugo/schedule.h @@ -583,7 +583,7 @@ protected: void delEventType(const action_t actTypeDel); void delQueue(event_t *curEvent); - void findAction(act* action, int16* index, int16* subElem); + void findAction(const act* action, int16* index, int16* subElem); void insertAction(act *action); void readAct(Common::ReadStream &in, act &curAct); void restoreActions(Common::ReadStream *f); diff --git a/engines/mohawk/cstime_game.cpp b/engines/mohawk/cstime_game.cpp index 5dfc9c4cf6..14e5d99e51 100644 --- a/engines/mohawk/cstime_game.cpp +++ b/engines/mohawk/cstime_game.cpp @@ -470,8 +470,8 @@ void CSTimeConversation::end(bool useLastClicked, bool runEvents) { _vm->getCase()->getCurrScene()->getChar(_sourceChar)->setupAmbientAnims(true); } - CSTimeInterface *interface = _vm->getInterface(); - CSTimeInventoryDisplay *invDisplay = interface->getInventoryDisplay(); + CSTimeInterface *iface = _vm->getInterface(); + CSTimeInventoryDisplay *invDisplay = iface->getInventoryDisplay(); if (invDisplay->getState() == 4) { invDisplay->hide(); invDisplay->setState(0); @@ -480,8 +480,8 @@ void CSTimeConversation::end(bool useLastClicked, bool runEvents) { setState((uint)~0); _currHover = 0xffff; - interface->clearTextLine(); - interface->clearDialogArea(); + iface->clearTextLine(); + iface->clearDialogArea(); invDisplay->show(); // TODO: stupid case 20 stuff diff --git a/engines/mohawk/cursors.cpp b/engines/mohawk/cursors.cpp index eb11eb175e..4a9fcb2ff2 100644 --- a/engines/mohawk/cursors.cpp +++ b/engines/mohawk/cursors.cpp @@ -28,12 +28,13 @@ #include "mohawk/resource.h" #include "mohawk/graphics.h" #include "mohawk/myst.h" -#include "mohawk/riven_cursors.h" #include "common/macresman.h" -#include "common/ne_exe.h" #include "common/system.h" +#include "common/winexe_ne.h" +#include "common/winexe_pe.h" #include "graphics/cursorman.h" +#include "graphics/wincursor.h" namespace Mohawk { @@ -83,15 +84,16 @@ void CursorManager::setCursor(uint16 id) { setDefaultCursor(); } -void CursorManager::decodeMacXorCursor(Common::SeekableReadStream *stream, byte *cursor) { +void CursorManager::setMacXorCursor(Common::SeekableReadStream *stream) { assert(stream); - assert(cursor); + + byte cursorBitmap[16 * 16]; // Get black and white data for (int i = 0; i < 32; i++) { byte imageByte = stream->readByte(); for (int b = 0; b < 8; b++) - cursor[i * 8 + b] = (imageByte & (0x80 >> b)) ? 1 : 2; + cursorBitmap[i * 8 + b] = (imageByte & (0x80 >> b)) ? 1 : 2; } // Apply mask data @@ -99,28 +101,18 @@ void CursorManager::decodeMacXorCursor(Common::SeekableReadStream *stream, byte byte imageByte = stream->readByte(); for (int b = 0; b < 8; b++) if ((imageByte & (0x80 >> b)) == 0) - cursor[i * 8 + b] = 0; + cursorBitmap[i * 8 + b] = 0; } -} -void CursorManager::setStandardCursor(Common::SeekableReadStream *stream) { - // The Broderbund devs decided to rip off the Mac format, it seems. - // However, they reversed the x/y hotspot. That makes it totally different!!!! - assert(stream); - - byte cursorBitmap[16 * 16]; - decodeMacXorCursor(stream, cursorBitmap); uint16 hotspotY = stream->readUint16BE(); uint16 hotspotX = stream->readUint16BE(); CursorMan.replaceCursor(cursorBitmap, 16, 16, hotspotX, hotspotY, 0); CursorMan.replaceCursorPalette(s_bwPalette, 1, 2); - - delete stream; } void DefaultCursorManager::setCursor(uint16 id) { - setStandardCursor(_vm->getResource(_tag, id)); + setMacXorCursor(_vm->getResource(_tag, id)); } MystCursorManager::MystCursorManager(MohawkEngine_Myst *vm) : _vm(vm) { @@ -167,119 +159,6 @@ void MystCursorManager::setDefaultCursor() { setCursor(kDefaultMystCursor); } -void RivenCursorManager::setCursor(uint16 id) { - // All of Riven's cursors are hardcoded. See riven_cursors.h for these definitions. - - switch (id) { - case 1002: - // Zip Mode - CursorMan.replaceCursor(s_zipModeCursor, 16, 16, 8, 8, 0); - CursorMan.replaceCursorPalette(s_zipModeCursorPalette, 1, ARRAYSIZE(s_zipModeCursorPalette) / 3); - break; - case 2003: - // Hand Over Object - CursorMan.replaceCursor(s_objectHandCursor, 16, 16, 8, 8, 0); - CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 3); - break; - case 2004: - // Grabbing/Using Object - CursorMan.replaceCursor(s_grabbingHandCursor, 13, 13, 6, 6, 0); - CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 3); - break; - case 3000: - // Standard Hand - CursorMan.replaceCursor(s_standardHandCursor, 15, 16, 6, 0, 0); - CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 3); - break; - case 3001: - // Pointing Left - CursorMan.replaceCursor(s_pointingLeftCursor, 15, 13, 0, 3, 0); - CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 3); - break; - case 3002: - // Pointing Right - CursorMan.replaceCursor(s_pointingRightCursor, 15, 13, 14, 3, 0); - CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 3); - break; - case 3003: - // Pointing Down (Palm Up) - CursorMan.replaceCursor(s_pointingDownCursorPalmUp, 13, 16, 3, 15, 0); - CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 3); - break; - case 3004: - // Pointing Up (Palm Up) - CursorMan.replaceCursor(s_pointingUpCursorPalmUp, 13, 16, 3, 0, 0); - CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 3); - break; - case 3005: - // Pointing Left (Curved) - CursorMan.replaceCursor(s_pointingLeftCursorBent, 15, 13, 0, 5, 0); - CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 3); - break; - case 3006: - // Pointing Right (Curved) - CursorMan.replaceCursor(s_pointingRightCursorBent, 15, 13, 14, 5, 0); - CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 3); - break; - case 3007: - // Pointing Down (Palm Down) - CursorMan.replaceCursor(s_pointingDownCursorPalmDown, 15, 16, 7, 15, 0); - CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 3); - break; - case 4001: - // Red Marble - CursorMan.replaceCursor(s_redMarbleCursor, 12, 12, 5, 5, 0); - CursorMan.replaceCursorPalette(s_redMarbleCursorPalette, 1, ARRAYSIZE(s_redMarbleCursorPalette) / 3); - break; - case 4002: - // Orange Marble - CursorMan.replaceCursor(s_orangeMarbleCursor, 12, 12, 5, 5, 0); - CursorMan.replaceCursorPalette(s_orangeMarbleCursorPalette, 1, ARRAYSIZE(s_orangeMarbleCursorPalette) / 3); - break; - case 4003: - // Yellow Marble - CursorMan.replaceCursor(s_yellowMarbleCursor, 12, 12, 5, 5, 0); - CursorMan.replaceCursorPalette(s_yellowMarbleCursorPalette, 1, ARRAYSIZE(s_yellowMarbleCursorPalette) / 3); - break; - case 4004: - // Green Marble - CursorMan.replaceCursor(s_greenMarbleCursor, 12, 12, 5, 5, 0); - CursorMan.replaceCursorPalette(s_greenMarbleCursorPalette, 1, ARRAYSIZE(s_greenMarbleCursorPalette) / 3); - break; - case 4005: - // Blue Marble - CursorMan.replaceCursor(s_blueMarbleCursor, 12, 12, 5, 5, 0); - CursorMan.replaceCursorPalette(s_blueMarbleCursorPalette, 1, ARRAYSIZE(s_blueMarbleCursorPalette) / 3); - break; - case 4006: - // Violet Marble - CursorMan.replaceCursor(s_violetMarbleCursor, 12, 12, 5, 5, 0); - CursorMan.replaceCursorPalette(s_violetMarbleCursorPalette, 1, ARRAYSIZE(s_violetMarbleCursorPalette) / 3); - break; - case 5000: - // Pellet - CursorMan.replaceCursor(s_pelletCursor, 8, 8, 4, 4, 0); - CursorMan.replaceCursorPalette(s_pelletCursorPalette, 1, ARRAYSIZE(s_pelletCursorPalette) / 3); - break; - case 9000: - // Hide Cursor - CursorMan.showMouse(false); - break; - default: - error("Cursor %d does not exist!", id); - } - - if (id != 9000) // Show Cursor - CursorMan.showMouse(true); - - // Should help in cases where we need to hide the cursor immediately. - g_system->updateScreen(); -} - -void RivenCursorManager::setDefaultCursor() { - setCursor(kRivenMainCursor); -} - NECursorManager::NECursorManager(const Common::String &appName) { _exe = new Common::NEResources(); @@ -295,16 +174,14 @@ NECursorManager::~NECursorManager() { } void NECursorManager::setCursor(uint16 id) { - if (!_exe) { - Common::Array<Common::NECursorGroup> cursors = _exe->getCursors(); - - for (uint32 i = 0; i < cursors.size(); i++) { - if (cursors[i].id == id) { - Common::NECursor *cursor = cursors[i].cursors[0]; - CursorMan.replaceCursor(cursor->getSurface(), cursor->getWidth(), cursor->getHeight(), cursor->getHotspotX(), cursor->getHotspotY(), 0); - CursorMan.replaceCursorPalette(cursor->getPalette(), 0, 256); - return; - } + if (_exe) { + Graphics::WinCursorGroup *cursorGroup = Graphics::WinCursorGroup::createCursorGroup(*_exe, id); + + if (cursorGroup) { + Graphics::WinCursor *cursor = cursorGroup->cursors[0].cursor; + CursorMan.replaceCursor(cursor->getSurface(), cursor->getWidth(), cursor->getHeight(), cursor->getHotspotX(), cursor->getHotspotY(), cursor->getKeyColor()); + CursorMan.replaceCursorPalette(cursor->getPalette(), 0, 256); + return; } } @@ -321,6 +198,8 @@ MacCursorManager::MacCursorManager(const Common::String &appName) { delete _resFork; _resFork = 0; } + } else { + _resFork = 0; } } @@ -334,22 +213,33 @@ void MacCursorManager::setCursor(uint16 id) { return; } - Common::SeekableReadStream *stream = _resFork->getResource(MKID_BE('CURS'), id); + // Try a color cursor first + Common::SeekableReadStream *stream = _resFork->getResource(MKID_BE('crsr'), id); - if (!stream) { - setDefaultCursor(); + if (stream) { + byte *cursor, *palette; + int width, height, hotspotX, hotspotY, keyColor, palSize; + + _resFork->convertCrsrCursor(stream, &cursor, width, height, hotspotX, hotspotY, keyColor, true, &palette, palSize); + + CursorMan.replaceCursor(cursor, width, height, hotspotX, hotspotY, keyColor); + CursorMan.replaceCursorPalette(palette, 0, palSize); + + delete[] cursor; + delete[] palette; + delete stream; return; } - byte cursorBitmap[16 * 16]; - decodeMacXorCursor(stream, cursorBitmap); - uint16 hotspotX = stream->readUint16BE(); - uint16 hotspotY = stream->readUint16BE(); + // Fall back to b&w cursors + stream = _resFork->getResource(MKID_BE('CURS'), id); - CursorMan.replaceCursor(cursorBitmap, 16, 16, hotspotX, hotspotY, 0); - CursorMan.replaceCursorPalette(s_bwPalette, 1, 2); - - delete stream; + if (stream) { + setMacXorCursor(stream); + delete stream; + } else { + setDefaultCursor(); + } } LivingBooksCursorManager_v2::LivingBooksCursorManager_v2() { @@ -368,10 +258,40 @@ LivingBooksCursorManager_v2::~LivingBooksCursorManager_v2() { void LivingBooksCursorManager_v2::setCursor(uint16 id) { if (_sysArchive && _sysArchive->hasResource(ID_TCUR, id)) { - setStandardCursor(_sysArchive->getResource(ID_TCUR, id)); + setMacXorCursor(_sysArchive->getResource(ID_TCUR, id)); } else { // TODO: Handle generated cursors } } +PECursorManager::PECursorManager(const Common::String &appName) { + _exe = new Common::PEResources(); + + if (!_exe->loadFromEXE(appName)) { + // Not all have cursors anyway, so this is not a problem + delete _exe; + _exe = 0; + } +} + +PECursorManager::~PECursorManager() { + delete _exe; +} + +void PECursorManager::setCursor(uint16 id) { + if (_exe) { + Graphics::WinCursorGroup *cursorGroup = Graphics::WinCursorGroup::createCursorGroup(*_exe, id); + + if (cursorGroup) { + Graphics::WinCursor *cursor = cursorGroup->cursors[0].cursor; + CursorMan.replaceCursor(cursor->getSurface(), cursor->getWidth(), cursor->getHeight(), cursor->getHotspotX(), cursor->getHotspotY(), cursor->getKeyColor()); + CursorMan.replaceCursorPalette(cursor->getPalette(), 0, 256); + return; + } + } + + // Last resort (not all have cursors) + setDefaultCursor(); +} + } // End of namespace Mohawk diff --git a/engines/mohawk/cursors.h b/engines/mohawk/cursors.h index a542ddb7c3..e6c417948f 100644 --- a/engines/mohawk/cursors.h +++ b/engines/mohawk/cursors.h @@ -31,6 +31,7 @@ namespace Common { class MacResManager; class NEResources; + class PEResources; class SeekableReadStream; class String; } @@ -80,13 +81,11 @@ public: virtual void hideCursor(); virtual void setCursor(uint16 id); virtual void setDefaultCursor(); + virtual bool hasSource() const { return false; } protected: - // Handles the Mac version of the xor/and map cursor - void decodeMacXorCursor(Common::SeekableReadStream *stream, byte *cursor); - - // Set a tCUR resource as the current cursor - void setStandardCursor(Common::SeekableReadStream *stream); + // Set a Mac XOR/AND map cursor to the screen + void setMacXorCursor(Common::SeekableReadStream *stream); }; // The default Mohawk cursor manager @@ -97,6 +96,7 @@ public: ~DefaultCursorManager() {} void setCursor(uint16 id); + bool hasSource() const { return true; } private: MohawkEngine *_vm; @@ -114,31 +114,21 @@ public: void hideCursor(); void setCursor(uint16 id); void setDefaultCursor(); + bool hasSource() const { return true; } private: MohawkEngine_Myst *_vm; MystBitmap *_bmpDecoder; }; - -// The cursor manager for Riven -// Uses hardcoded cursors -class RivenCursorManager : public CursorManager { -public: - RivenCursorManager() {} - ~RivenCursorManager() {} - - void setCursor(uint16 id); - void setDefaultCursor(); -}; - -// The cursor manager for NE exe's +// The cursor manager for NE EXE's class NECursorManager : public CursorManager { public: NECursorManager(const Common::String &appName); ~NECursorManager(); void setCursor(uint16 id); + bool hasSource() const { return _exe != 0; } private: Common::NEResources *_exe; @@ -151,6 +141,7 @@ public: ~MacCursorManager(); void setCursor(uint16 id); + bool hasSource() const { return _resFork != 0; } private: Common::MacResManager *_resFork; @@ -164,11 +155,25 @@ public: ~LivingBooksCursorManager_v2(); void setCursor(uint16 id); + bool hasSource() const { return _sysArchive != 0; } private: MohawkArchive *_sysArchive; }; +// The cursor manager for PE EXE's +class PECursorManager : public CursorManager { +public: + PECursorManager(const Common::String &appName); + ~PECursorManager(); + + void setCursor(uint16 id); + bool hasSource() const { return _exe != 0; } + +private: + Common::PEResources *_exe; +}; + } // End of namespace Mohawk #endif diff --git a/engines/mohawk/graphics.cpp b/engines/mohawk/graphics.cpp index 897ca79d0e..19882c4f1c 100644 --- a/engines/mohawk/graphics.cpp +++ b/engines/mohawk/graphics.cpp @@ -246,6 +246,13 @@ void GraphicsManager::copyAnimImageSectionToScreen(MohawkSurface *image, Common: getVM()->_system->unlockScreen(); } +void GraphicsManager::addImageToCache(uint16 id, MohawkSurface *surface) { + if (_cache.contains(id)) + error("Image %d already in cache", id); + + _cache[id] = surface; +} + MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) { _bmpDecoder = new MystBitmap(); @@ -630,6 +637,9 @@ RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : GraphicsManager(), _vm(vm _scheduledTransition = -1; // no transition _dirtyScreen = false; _inventoryDrawn = false; + + _creditsImage = 302; + _creditsPos = 0; } RivenGraphics::~RivenGraphics() { @@ -840,6 +850,17 @@ void RivenGraphics::runScheduledTransition() { _scheduledTransition = -1; // Clear scheduled transition } +void RivenGraphics::clearMainScreen() { + _mainScreen->fillRect(Common::Rect(0, 0, 608, 392), _pixelFormat.RGBToColor(0, 0, 0)); +} + +void RivenGraphics::fadeToBlack() { + // Self-explanatory + scheduleTransition(16); + clearMainScreen(); + runScheduledTransition(); +} + void RivenGraphics::showInventory() { // Don't redraw the inventory if (_inventoryDrawn) @@ -955,6 +976,60 @@ void RivenGraphics::drawExtrasImage(uint16 id, Common::Rect dstRect) { _dirtyScreen = true; } +void RivenGraphics::beginCredits() { + // Clear the old cache + clearCache(); + + // Now cache all the credits images + for (uint16 i = 302; i <= 320; i++) { + MohawkSurface *surface = _bitmapDecoder->decodeImage(_vm->getExtrasResource(ID_TBMP, i)); + surface->convertToTrueColor(); + addImageToCache(i, surface); + } + + // And clear our screen too + clearMainScreen(); +} + +void RivenGraphics::updateCredits() { + if ((_creditsImage == 303 || _creditsImage == 304) && _creditsPos == 0) + fadeToBlack(); + + if (_creditsImage < 304) { + // For the first two credit images, they are faded from black to the image and then out again + scheduleTransition(16); + + Graphics::Surface *frame = findImage(_creditsImage++)->getSurface(); + + for (int y = 0; y < frame->h; y++) + memcpy(_mainScreen->getBasePtr(124, y), frame->getBasePtr(0, y), frame->pitch); + + runScheduledTransition(); + } else { + // Otheriwse, we're scrolling + // Move the screen up one row + memmove(_mainScreen->pixels, _mainScreen->getBasePtr(0, 1), _mainScreen->pitch * (_mainScreen->h - 1)); + + // Only update as long as we're not before the last frame + // Otherwise, we're just moving up a row (which we already did) + if (_creditsImage <= 320) { + // Copy the next row to the bottom of the screen + Graphics::Surface *frame = findImage(_creditsImage)->getSurface(); + memcpy(_mainScreen->getBasePtr(124, _mainScreen->h - 1), frame->getBasePtr(0, _creditsPos), frame->pitch); + _creditsPos++; + + if (_creditsPos == _mainScreen->h) { + _creditsImage++; + _creditsPos = 0; + } + } + + // Now flush the new screen + _vm->_system->copyRectToScreen((byte *)_mainScreen->pixels, _mainScreen->pitch, 0, 0, _mainScreen->w, _mainScreen->h); + _vm->_system->updateScreen(); + } +} + LBGraphics::LBGraphics(MohawkEngine_LivingBooks *vm, uint16 width, uint16 height) : GraphicsManager(), _vm(vm) { _bmpDecoder = _vm->isPreMohawk() ? new OldMohawkBitmap() : new MohawkBitmap(); diff --git a/engines/mohawk/graphics.h b/engines/mohawk/graphics.h index 89189d442a..fbac2f2ea1 100644 --- a/engines/mohawk/graphics.h +++ b/engines/mohawk/graphics.h @@ -110,6 +110,7 @@ protected: virtual Common::Array<MohawkSurface *> decodeImages(uint16 id); virtual MohawkEngine *getVM() = 0; + void addImageToCache(uint16 id, MohawkSurface *surface); private: // An image cache that stores images until clearCache() is called @@ -195,11 +196,17 @@ public: // Transitions void scheduleTransition(uint16 id, Common::Rect rect = Common::Rect(0, 0, 608, 392)); void runScheduledTransition(); + void fadeToBlack(); // Inventory void showInventory(); void hideInventory(); + // Credits + void beginCredits(); + void updateCredits(); + uint getCurCreditsImage() { return _creditsImage; } + protected: MohawkSurface *decodeImage(uint16 id); MohawkEngine *getVM() { return (MohawkEngine *)_vm; } @@ -224,6 +231,10 @@ private: Graphics::Surface *_mainScreen; bool _dirtyScreen; Graphics::PixelFormat _pixelFormat; + void clearMainScreen(); + + // Credits + uint _creditsImage, _creditsPos; }; class LBGraphics : public GraphicsManager { diff --git a/engines/mohawk/livingbooks.cpp b/engines/mohawk/livingbooks.cpp index 20f7684521..482aade99d 100644 --- a/engines/mohawk/livingbooks.cpp +++ b/engines/mohawk/livingbooks.cpp @@ -555,8 +555,9 @@ void MohawkEngine_LivingBooks::queueDelayedEvent(DelayedEvent event) { uint16 MohawkEngine_LivingBooks::getResourceVersion() { Common::SeekableReadStream *versionStream = getResource(ID_VRSN, 1000); + // FIXME: some V2 games have very strange version entries if (versionStream->size() != 2) - warning("Version Record size mismatch"); + debug(1, "Version Record size mismatch"); uint16 version = versionStream->readUint16BE(); @@ -864,7 +865,6 @@ void MohawkEngine_LivingBooks::handleUIPoetryMenuClick(uint controlId) { break; case 7: - case 0xA: item = getItemById(10); if (item) item->destroySelf(); @@ -874,7 +874,18 @@ void MohawkEngine_LivingBooks::handleUIPoetryMenuClick(uint controlId) { item = getItemById(12); if (item) { item->setVisible(true); - item->togglePlaying(controlId == 7); + item->togglePlaying(true); + } + break; + + case 0xA: + item = getItemById(10); + if (item) + item->destroySelf(); + item = getItemById(11); + if (item) { + item->setVisible(true); + item->togglePlaying(true); } break; @@ -1251,7 +1262,7 @@ NodeState LBAnimationNode::update(bool seeking) { uint16 strLen = READ_BE_UINT16(entry.data + 2); if (strLen) - error("String length for unnamed wave file"); + warning("Named wave file encountered"); switch (entry.opcode) { case kLBAnimOpPlaySound: @@ -2433,7 +2444,7 @@ void LBItem::runScriptEntry(LBScriptEntry *entry) { break; case kLBOpRewind: - target->seek(0); + target->seek(1); break; case kLBOpStop: diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp index f842269893..1aba820fed 100644 --- a/engines/mohawk/myst.cpp +++ b/engines/mohawk/myst.cpp @@ -1143,20 +1143,6 @@ void MohawkEngine_Myst::loadResources() { delete rlstStream; } -void MohawkEngine_Myst::runLoadDialog() { - const Common::String gameId = ConfMan.get("gameid"); - - const EnginePlugin *plugin = 0; - EngineMan.findGame(gameId, &plugin); - - pauseEngine(true); - int slot = _loadDialog->runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName()); - if (slot >= 0) { - // TODO - } - pauseEngine(false); -} - Common::Error MohawkEngine_Myst::loadGameState(int slot) { if (_gameState->load(_gameState->generateSaveGameList()[slot])) return Common::kNoError; diff --git a/engines/mohawk/myst.h b/engines/mohawk/myst.h index 919509384b..47e8a6562c 100644 --- a/engines/mohawk/myst.h +++ b/engines/mohawk/myst.h @@ -155,8 +155,6 @@ public: Common::String wrapMovieFilename(const Common::String &movieName, uint16 stack); void reloadSaveList(); - void runLoadDialog(); - void runSaveDialog(); void changeToStack(uint16 stack, uint16 card, uint16 linkSrcSound, uint16 linkDstSound); void changeToCard(uint16 card, bool updateScreen); diff --git a/engines/mohawk/myst_stacks/mechanical.cpp b/engines/mohawk/myst_stacks/mechanical.cpp index 01390f0ea6..3dab2f7939 100644 --- a/engines/mohawk/myst_stacks/mechanical.cpp +++ b/engines/mohawk/myst_stacks/mechanical.cpp @@ -59,7 +59,7 @@ void Mechanical::setupOpcodes() { OPCODE(107, o_elevatorRotationMove); OPCODE(108, o_elevatorRotationStop); OPCODE(121, o_elevatorWindowMovie); - OPCODE(122, opcode_122); + OPCODE(122, o_elevatorGoMiddle); OPCODE(123, o_elevatorTopMovie); OPCODE(124, opcode_124); OPCODE(125, o_mystStaircaseMovie); @@ -82,7 +82,7 @@ void Mechanical::setupOpcodes() { OPCODE(209, opcode_209); // "Exit" Opcodes - OPCODE(300, opcode_300); + OPCODE(300, NOP); } #undef OPCODE @@ -92,6 +92,7 @@ void Mechanical::disablePersistentScripts() { opcode_205_disable(); opcode_206_disable(); opcode_209_disable(); + _elevatorGoingMiddle = false; } void Mechanical::runPersistentScripts() { @@ -100,6 +101,9 @@ void Mechanical::runPersistentScripts() { if (_elevatorRotationLeverMoving) elevatorRotation_run(); + if (_elevatorGoingMiddle) + elevatorGoMiddle_run(); + opcode_205_run(); opcode_206_run(); opcode_209_run(); @@ -139,8 +143,13 @@ uint16 Mechanical::getVar(uint16 var) { return _state.elevatorRotation; case 12: // Fortress Elevator Rotation Cog Position return 5 - (uint16)(_elevatorRotationGearPosition + 0.5) % 6; + case 13: // Elevator position + return _elevatorPosition; case 14: // Elevator going down when at top - return _elevatorGoingDown; // TODO add too late value (2) + if (_elevatorGoingDown && _elevatorTooLate) + return 2; + else + return _elevatorGoingDown; case 15: // Code Lock Execute Button Script if (_mystStaircaseState) return 0; @@ -187,6 +196,9 @@ void Mechanical::toggleVar(uint16 var) { case 19: // Code Lock Shape #4 - Right _state.codeShape[var - 16] = (_state.codeShape[var - 16] + 1) % 10; break; + case 23: // Elevator player is in cabin + _elevatorInCabin = false; + break; case 102: // Red page if (!(_globals.redPagesInBook & 4)) { if (_globals.heldPage == 9) @@ -213,6 +225,8 @@ bool Mechanical::setVarValue(uint16 var, uint16 value) { bool refresh = false; switch (var) { + case 13: + _elevatorPosition = value; case 14: // Elevator going down when at top _elevatorGoingDown = value; break; @@ -342,14 +356,63 @@ void Mechanical::o_elevatorWindowMovie(uint16 op, uint16 var, uint16 argc, uint1 _vm->_video->waitUntilMovieEnds(window); } -void Mechanical::opcode_122(uint16 op, uint16 var, uint16 argc, uint16 *argv) { - if (argc == 0) { - // Used on Card 6120 (Elevator) - // Called when Exit Midde Button Pressed - - // TODO: hcelev? Movie of Elevator? - } else - unknown(op, var, argc, argv); +void Mechanical::o_elevatorGoMiddle(uint16 op, uint16 var, uint16 argc, uint16 *argv) { + debugC(kDebugScript, "Opcode %d: Elevator go middle from top", op); + + _elevatorTooLate = false; + _elevatorTopCounter = 5; + _elevatorGoingMiddle = true; + _elevatorInCabin = true; + _elevatorNextTime = _vm->_system->getMillis() + 1000; +} + +void Mechanical::elevatorGoMiddle_run() { + uint32 time = _vm->_system->getMillis(); + if (_elevatorNextTime < time) { + _elevatorNextTime = time + 1000; + _elevatorTopCounter--; + + if (_elevatorTopCounter > 0) { + // Draw button pressed + if (_elevatorInCabin) { + _vm->_gfx->copyImageSectionToScreen(6332, Common::Rect(0, 35, 51, 63), Common::Rect(10, 137, 61, 165)); + _vm->_system->updateScreen(); + } + + // Blip + _vm->_sound->playSoundBlocking(14120); + + // Restore button + if (_elevatorInCabin) { + _vm->_gfx->copyBackBufferToScreen(Common::Rect(10, 137, 61, 165)); + _vm->_system->updateScreen(); + } + } else if (_elevatorInCabin) { + _elevatorTooLate = true; + + // Elevator going to middle animation + _vm->_cursor->hideCursor(); + _vm->_sound->playSoundBlocking(11120); + _vm->_gfx->copyImageToBackBuffer(6118, Common::Rect(544, 333)); + _vm->_sound->replaceSoundMyst(12120); + _vm->_gfx->runTransition(2, Common::Rect(177, 0, 370, 333), 25, 0); + _vm->_sound->playSoundBlocking(13120); + _vm->_sound->replaceSoundMyst(8120); + _vm->_gfx->copyImageToBackBuffer(6327, Common::Rect(544, 333)); + _vm->_system->delayMillis(500); + _vm->_sound->replaceSoundMyst(9120); + static uint16 moviePos[2] = { 3540, 5380 }; + o_elevatorWindowMovie(121, 0, 2, moviePos); + _vm->_gfx->copyBackBufferToScreen(Common::Rect(544, 333)); + _vm->_sound->replaceSoundMyst(10120); + _vm->_cursor->showCursor(); + + _elevatorGoingMiddle = false; + _elevatorPosition = 1; + + _vm->changeToCard(6327, true); + } + } } void Mechanical::o_elevatorTopMovie(uint16 op, uint16 var, uint16 argc, uint16 *argv) { @@ -600,11 +663,5 @@ void Mechanical::opcode_209(uint16 op, uint16 var, uint16 argc, uint16 *argv) { unknown(op, var, argc, argv); } -void Mechanical::opcode_300(uint16 op, uint16 var, uint16 argc, uint16 *argv) { - // Used in Card 6156 (Fortress Elevator View) - varUnusedCheck(op, var); - // TODO: Fill in Logic. Clearing Variable for View? -} - } // End of namespace MystStacks } // End of namespace Mohawk diff --git a/engines/mohawk/myst_stacks/mechanical.h b/engines/mohawk/myst_stacks/mechanical.h index 24c6c73471..6a0aa30f5c 100644 --- a/engines/mohawk/myst_stacks/mechanical.h +++ b/engines/mohawk/myst_stacks/mechanical.h @@ -55,6 +55,7 @@ private: void opcode_202_run(); void opcode_202_disable(); void elevatorRotation_run(); + void elevatorGoMiddle_run(); void opcode_205_run(); void opcode_205_disable(); void opcode_206_run(); @@ -69,7 +70,7 @@ private: DECLARE_OPCODE(o_elevatorRotationMove); DECLARE_OPCODE(o_elevatorRotationStop); DECLARE_OPCODE(o_elevatorWindowMovie); - DECLARE_OPCODE(opcode_122); + DECLARE_OPCODE(o_elevatorGoMiddle); DECLARE_OPCODE(o_elevatorTopMovie); DECLARE_OPCODE(opcode_124); DECLARE_OPCODE(o_mystStaircaseMovie); @@ -90,8 +91,6 @@ private: DECLARE_OPCODE(opcode_206); DECLARE_OPCODE(opcode_209); - DECLARE_OPCODE(opcode_300); - MystGameState::Mechanical &_state; bool _mystStaircaseState; // 76 @@ -105,6 +104,13 @@ private: uint16 _elevatorRotationSoundId; // 128 bool _elevatorRotationLeverMoving; // 184 + bool _elevatorGoingMiddle; // 148 + bool _elevatorTooLate; + uint16 _elevatorPosition; // 104 + bool _elevatorInCabin; // 108 + uint16 _elevatorTopCounter; + uint32 _elevatorNextTime; + uint16 _crystalLit; // 130 MystResourceType6 *_snakeBox; // 156 diff --git a/engines/mohawk/myst_stacks/myst.cpp b/engines/mohawk/myst_stacks/myst.cpp index 29048eab39..caadf09f56 100644 --- a/engines/mohawk/myst_stacks/myst.cpp +++ b/engines/mohawk/myst_stacks/myst.cpp @@ -50,6 +50,7 @@ Myst::Myst(MohawkEngine_Myst *vm) : _towerRotationBlinkLabel = false; _libraryBookcaseChanged = false; _dockVaultState = 0; + _cabinDoorOpened = 0; _cabinMatchState = 2; _matchBurning = false; _tree = 0; diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp index abc7f5304e..e9742e0bc2 100644 --- a/engines/mohawk/riven.cpp +++ b/engines/mohawk/riven.cpp @@ -120,21 +120,44 @@ Common::Error MohawkEngine_Riven::run() { _externalScriptHandler = new RivenExternal(this); _optionsDialog = new RivenOptionsDialog(this); _scriptMan = new RivenScriptManager(this); - _cursor = new RivenCursorManager(); _rnd = new Common::RandomSource(); g_eventRec.registerRandomSource(*_rnd, "riven"); + // Create the cursor manager + if (Common::File::exists("rivendmo.exe")) + _cursor = new PECursorManager("rivendmo.exe"); + else if (Common::File::exists("riven.exe")) + _cursor = new PECursorManager("riven.exe"); + else // last resort: try the Mac executable + _cursor = new MacCursorManager("Riven"); + initVars(); + // We need to have a cursor source, or the game won't work + if (!_cursor->hasSource()) { + Common::String message = "You're missing a Riven executable. The Windows executable is 'riven.exe' or 'rivendmo.exe'. "; + message += "Using the 'arcriven.z' installer file also works. In addition, you can use the Mac 'Riven' executable."; + GUIErrorMessage(message); + warning("%s", message.c_str()); + return Common::kNoGameDataFoundError; + } + // Open extras.mhk for common images _extrasFile = new MohawkArchive(); - if (!_extrasFile->open("extras.mhk")) - error("Could not open extras.mhk"); + // We need extras.mhk for inventory images, marble images, and credits images + if (!_extrasFile->open("extras.mhk")) { + Common::String message = "You're missing 'extras.mhk'. Using the 'arcriven.z' installer file also works."; + GUIErrorMessage(message); + warning("%s", message.c_str()); + return Common::kNoGameDataFoundError; + } // Start at main cursor _cursor->setCursor(kRivenMainCursor); + _cursor->showCursor(); + _system->updateScreen(); // Let's begin, shall we? if (getFeatures() & GF_DEMO) { @@ -488,10 +511,12 @@ void MohawkEngine_Riven::checkHotspotChange() { if (_curHotspot != hotspotIndex) { _curHotspot = hotspotIndex; _cursor->setCursor(_hotspots[_curHotspot].mouse_cursor); + _system->updateScreen(); } } else { _curHotspot = -1; _cursor->setCursor(kRivenMainCursor); + _system->updateScreen(); } } diff --git a/engines/mohawk/riven_cursors.h b/engines/mohawk/riven_cursors.h deleted file mode 100644 index dadfdf0549..0000000000 --- a/engines/mohawk/riven_cursors.h +++ /dev/null @@ -1,670 +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. - * - * $URL$ - * $Id$ - * - */ - -namespace Mohawk { - -////////////////////////////////////////////// -// Cursors and Cursor Palettes -////////////////////////////////////////////// - -//////////////////////////////////////// -// Zip Mode Cursor (16x16): -// Shown when a zip mode spot is active -// -// 0 = Transparent -// 1 = Black (0x000000) -// 2 = Yellow (0xDCFF00) -//////////////////////////////////////// -static const byte s_zipModeCursor[] = { - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 2, 1, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 2, 1, 1, 2, 2, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 2, 2, 1, 2, 2, 2, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 2, 2, 2, 1, 2, 2, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 2, 2, 1, 1, 2, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 1, 2, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 -}; - - -//////////////////////////////////////// -// Zip Mode Cursor Palette: -// Palette For The Zip Mode Cursor -//////////////////////////////////////// -static const byte s_zipModeCursorPalette[] = { - 0x00, 0x00, 0x00, // Black - 0xDC, 0xFF, 0x00 // Yellow -}; - - -//////////////////////////////////////// -// Hand Over Object Cursor (16x16): -// Shown when over a hotspot that's interactive -// -// 0 = Transparent -// 1 = Black (0x000000) -// 2 = Light Peach (0xEDCD96) -// 3 = Brown (0x8A672F) -// 4 = Dark Peach (0xE89A62) -//////////////////////////////////////// -static const byte s_objectHandCursor[] = { - 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 1, 2, 3, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 2, 3, 1, 1, 2, 3, 1, 2, 3, 1, 0, 0, 0, - 0, 0, 1, 2, 3, 1, 1, 4, 3, 1, 2, 3, 1, 0, 1, 0, - 0, 0, 0, 1, 2, 3, 1, 2, 3, 1, 4, 3, 1, 1, 2, 1, - 0, 0, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, - 0, 1, 1, 0, 1, 2, 2, 2, 2, 2, 2, 3, 1, 2, 3, 1, - 1, 2, 2, 1, 1, 2, 2, 2, 4, 2, 4, 2, 2, 4, 2, 1, - 1, 3, 4, 2, 1, 2, 4, 2, 2, 2, 2, 2, 4, 4, 1, 0, - 0, 1, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 3, 1, 0, - 0, 0, 1, 3, 2, 2, 2, 2, 2, 2, 2, 4, 4, 3, 1, 0, - 0, 0, 1, 3, 2, 2, 2, 2, 2, 2, 2, 4, 3, 1, 0, 0, - 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 1, 0, 0, - 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 4, 3, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 4, 3, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 4, 3, 1, 0, 0, 0 -}; - - -//////////////////////////////////////// -// Grabbing Hand Cursor (13x13): -// Shown when interacting with an object -// -// 0 = Transparent -// 1 = Black (0x000000) -// 2 = Light Peach (0xEDCD96) -// 3 = Brown (0x8A672F) -// 4 = Dark Peach (0xE89A62) -//////////////////////////////////////// -static const byte s_grabbingHandCursor[] = { - 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 2, 3, 1, 1, 1, 0, 0, 0, - 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 1, 0, - 0, 1, 2, 2, 2, 2, 2, 2, 2, 3, 1, 2, 1, - 0, 1, 1, 2, 2, 2, 4, 2, 4, 2, 2, 4, 1, - 1, 2, 1, 2, 4, 2, 2, 2, 2, 2, 4, 4, 1, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 3, 1, - 1, 3, 2, 2, 2, 2, 2, 2, 2, 4, 4, 3, 1, - 1, 3, 2, 2, 2, 2, 2, 2, 2, 4, 3, 1, 0, - 0, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 1, 0, - 0, 0, 1, 2, 2, 2, 2, 2, 4, 3, 1, 0, 0, - 0, 0, 0, 1, 2, 2, 2, 2, 4, 3, 1, 0, 0, - 0, 0, 0, 1, 2, 2, 2, 2, 4, 3, 1, 0, 0 -}; - - -//////////////////////////////////////// -// Standard Hand Cursor (15x16): -// Standard Cursor -// -// 0 = Transparent -// 1 = Black (0x000000) -// 2 = Light Peach (0xEDCD96) -// 3 = Brown (0x8A672F) -// 4 = Dark Peach (0xE89A62) -//////////////////////////////////////// -static const byte s_standardHandCursor[] = { - 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 4, 4, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 2, 3, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 2, 4, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 2, 4, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 2, 4, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 2, 4, 1, 1, 1, 1, 1, 0, 0, - 1, 4, 2, 1, 0, 1, 2, 4, 1, 4, 1, 4, 1, 1, 1, - 0, 1, 3, 2, 1, 1, 2, 4, 1, 4, 1, 4, 1, 4, 1, - 0, 0, 1, 4, 2, 1, 2, 2, 4, 2, 4, 2, 1, 4, 1, - 0, 0, 1, 4, 2, 1, 2, 2, 2, 2, 2, 2, 2, 3, 1, - 0, 0, 0, 1, 4, 2, 2, 2, 2, 2, 2, 2, 4, 3, 1, - 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 4, 3, 1, - 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 3, 1, 0, - 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 4, 3, 1, 0, - 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 4, 3, 1, 0 -}; - - -//////////////////////////////////////// -// Pointing Left Cursor (15x13): -// Cursor When Over A Hotspot That Allows You To Move Left -// -// 0 = Transparent -// 1 = Black (0x000000) -// 2 = Light Peach (0xEDCD96) -// 3 = Brown (0x8A672F) -// 4 = Dark Peach (0xE89A62) -//////////////////////////////////////// -static const byte s_pointingLeftCursor[] = { - 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 3, 2, 2, 2, 1, 1, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 3, 1, 1, - 1, 4, 2, 2, 2, 2, 1, 2, 3, 2, 2, 2, 4, 4, 4, - 1, 4, 4, 4, 4, 4, 1, 2, 1, 3, 4, 2, 2, 2, 2, - 0, 1, 1, 1, 1, 1, 1, 2, 1, 3, 3, 4, 2, 2, 2, - 0, 0, 0, 0, 0, 1, 1, 1, 1, 3, 3, 3, 4, 4, 2, - 0, 0, 0, 0, 0, 1, 2, 2, 2, 4, 1, 3, 4, 2, 2, - 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 3, 4, 2, 2, - 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 1, 4, 2, 4, - 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 4, 4, 1, - 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0 -}; - - -//////////////////////////////////////// -// Pointing Right Cursor (15x13): -// Cursor When Over A Hotspot That Allows You To Move Right -// -// 0 = Transparent -// 1 = Black (0x000000) -// 2 = Light Peach (0xEDCD96) -// 3 = Brown (0x8A672F) -// 4 = Dark Peach (0xE89A62) -//////////////////////////////////////// -static const byte s_pointingRightCursor[] = { - 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 2, 2, 2, 3, 1, 0, 0, 0, 0, 0, 0, - 1, 1, 3, 4, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0, - 4, 4, 4, 2, 2, 2, 3, 2, 1, 4, 4, 4, 4, 4, 1, - 2, 2, 2, 2, 4, 3, 1, 2, 1, 2, 2, 2, 2, 4, 1, - 2, 2, 2, 4, 3, 3, 1, 2, 1, 1, 1, 1, 1, 1, 0, - 2, 4, 4, 3, 3, 3, 1, 1, 1, 1, 0, 0, 0, 0, 0, - 2, 2, 4, 3, 1, 4, 2, 2, 2, 1, 0, 0, 0, 0, 0, - 2, 2, 4, 3, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, - 4, 2, 4, 1, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, - 1, 4, 4, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 -}; - - -//////////////////////////////////////// -// Pointing Down Cursor (Palm Up)(13x16): -// Cursor When Over A Hotspot That Allows You To Move Down -// -// 0 = Transparent -// 1 = Black (0x000000) -// 2 = Light Peach (0xEDCD96) -// 3 = Brown (0x8A672F) -// 4 = Dark Peach (0xE89A62) -//////////////////////////////////////// -static const byte s_pointingDownCursorPalmUp[] = { - 0, 0, 1, 4, 2, 2, 2, 2, 2, 4, 1, 0, 0, - 0, 0, 1, 4, 2, 2, 4, 2, 2, 2, 4, 1, 0, - 0, 1, 3, 4, 2, 2, 4, 4, 4, 4, 4, 1, 0, - 0, 1, 4, 2, 2, 4, 3, 3, 3, 1, 1, 1, 1, - 1, 2, 2, 2, 4, 3, 3, 1, 1, 2, 1, 2, 1, - 1, 2, 2, 2, 3, 3, 3, 4, 1, 2, 1, 2, 1, - 1, 2, 2, 3, 1, 1, 1, 2, 1, 2, 1, 2, 1, - 1, 3, 2, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, - 0, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, - 0, 0, 1, 2, 4, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 2, 4, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 2, 4, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 2, 4, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 2, 4, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 4, 4, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 -}; - - -//////////////////////////////////////// -// Pointing Up Cursor (Palm Up)(13x16): -// Cursor When Over A Hotspot That Allows You To Move Up -// -// 0 = Transparent -// 1 = Black (0x000000) -// 2 = Light Peach (0xEDCD96) -// 3 = Brown (0x8A672F) -// 4 = Dark Peach (0xE89A62) -//////////////////////////////////////// -static const byte s_pointingUpCursorPalmUp[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 4, 4, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 1, 2, 4, 1, 0, 0, - 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 0, - 1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 2, 3, 1, - 1, 2, 1, 2, 1, 2, 1, 1, 1, 3, 2, 2, 1, - 1, 2, 1, 2, 1, 4, 3, 3, 3, 2, 2, 2, 1, - 1, 2, 1, 2, 1, 1, 3, 3, 4, 2, 2, 2, 1, - 1, 1, 1, 1, 3, 3, 3, 4, 2, 2, 4, 1, 0, - 0, 1, 4, 4, 4, 4, 4, 2, 2, 4, 3, 1, 0, - 0, 1, 4, 2, 2, 2, 4, 2, 2, 4, 1, 0, 0, - 0, 0, 1, 4, 2, 2, 2, 2, 2, 4, 1, 0, 0 -}; - - -//////////////////////////////////////// -// Pointing Left Cursor (Bent)(15x13): -// Cursor When Over A Hotspot That Allows You To Turn Left 180 Degrees -// -// 0 = Transparent -// 1 = Black (0x000000) -// 2 = Light Peach (0xEDCD96) -// 3 = Brown (0x8A672F) -// 4 = Dark Peach (0xE89A62) -//////////////////////////////////////// -static const byte s_pointingLeftCursorBent[] = { - 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 3, 2, 2, 2, 1, 1, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 3, 1, 1, - 1, 3, 2, 4, 4, 2, 1, 2, 3, 3, 2, 2, 4, 4, 4, - 1, 2, 4, 3, 3, 4, 1, 2, 1, 3, 4, 2, 2, 2, 2, - 1, 4, 4, 1, 1, 1, 1, 2, 1, 1, 3, 4, 2, 2, 2, - 1, 1, 1, 0, 0, 1, 1, 1, 1, 3, 3, 3, 4, 4, 2, - 0, 0, 0, 0, 0, 1, 2, 2, 2, 4, 1, 3, 4, 3, 2, - 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 3, 4, 2, 2, - 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 1, 4, 2, 4, - 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 4, 4, 1, - 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0 -}; - - -//////////////////////////////////////// -// Pointing Right Cursor (Bent)(15x13): -// Cursor When Over A Hotspot That Allows You To Turn Right 180 Degrees -// -// 0 = Transparent -// 1 = Black (0x000000) -// 2 = Light Peach (0xEDCD96) -// 3 = Brown (0x8A672F) -// 4 = Dark Peach (0xE89A62) -//////////////////////////////////////// -static const byte s_pointingRightCursorBent[] = { - 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 2, 2, 2, 3, 1, 0, 0, 0, 0, 0, 0, - 1, 1, 3, 4, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0, - 4, 4, 4, 2, 2, 3, 3, 2, 1, 2, 4, 4, 2, 3, 1, - 2, 2, 2, 2, 4, 3, 1, 2, 1, 4, 3, 3, 4, 2, 1, - 2, 2, 2, 4, 3, 1, 1, 2, 1, 1, 1, 1, 4, 4, 1, - 2, 4, 4, 3, 3, 3, 1, 1, 1, 1, 0, 0, 1, 1, 1, - 2, 3, 4, 3, 1, 4, 2, 2, 2, 1, 0, 0, 0, 0, 0, - 2, 2, 4, 3, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, - 4, 2, 4, 1, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, - 1, 4, 4, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 -}; - - -//////////////////////////////////////// -// Pointing Down Cursor (Palm Down)(15x16): -// Similar to Standard Cursor -// -// 0 = Transparent -// 1 = Black (0x000000) -// 2 = Light Peach (0xEDCD96) -// 3 = Brown (0x8A672F) -// 4 = Dark Peach (0xE89A62) -//////////////////////////////////////// -static const byte s_pointingDownCursorPalmDown[] = { - 0, 1, 3, 4, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, - 0, 1, 3, 4, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, - 0, 1, 3, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, - 1, 3, 4, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, - 1, 3, 4, 2, 2, 2, 2, 2, 2, 2, 4, 1, 0, 0, 0, - 1, 3, 2, 3, 2, 2, 2, 2, 2, 1, 2, 4, 1, 0, 0, - 1, 4, 1, 2, 2, 3, 2, 3, 2, 1, 2, 4, 1, 0, 0, - 1, 4, 1, 4, 1, 4, 1, 4, 4, 1, 1, 2, 3, 1, 0, - 0, 1, 1, 4, 1, 4, 1, 4, 2, 1, 0, 1, 2, 4, 1, - 0, 0, 1, 1, 1, 1, 1, 4, 2, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 4, 4, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 4, 2, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 4, 2, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 3, 2, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 4, 4, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 -}; - -//////////////////////////////////////// -// Hand Cursor Palette: -// Palette For All Hand Cursors -//////////////////////////////////////// -static const byte s_handCursorPalette[] = { - 0x00, 0x00, 0x00, // Black - 0xED, 0xCD, 0x96, // Light Peach - 0x8A, 0x67, 0x2F, // Brown - 0xE8, 0x9A, 0x62 // Dark Peach -}; - - -//////////////////////////////////////// -// Pellet Cursor (8x8): -// Cursor When Using The Pellet In The Frog Trap -// -// 0 = Transparent -// 1 = Light Olive Green (0x5D6730) -// 2 = Maroon (0x5E3333) -// 3 = Light Gray (0x555555) -// 4 = Medium Gray (0x444444) -// 5 = Dark Gray (0x333333) -// 6 = Dark Green (0x2D3300) -// 7 = Darkest Gray (0x222222) -//////////////////////////////////////// -static const byte s_pelletCursor[] = { - 0, 0, 1, 1, 2, 3, 0, 0, - 0, 2, 1, 4, 1, 2, 5, 0, - 4, 1, 4, 1, 2, 1, 5, 4, - 4, 2, 1, 2, 1, 1, 2, 6, - 6, 4, 2, 1, 4, 4, 1, 5, - 5, 6, 5, 2, 1, 2, 4, 4, - 0, 7, 5, 5, 4, 2, 5, 0, - 0, 0, 5, 6, 6, 5, 0, 0 -}; - -//////////////////////////////////////// -// Pellet Cursor Palette: -// Palette For The Pellet Cursor -//////////////////////////////////////// -static const byte s_pelletCursorPalette[] = { - 0x5D, 0x67, 0x30, - 0x5E, 0x33, 0x33, - 0x55, 0x55, 0x55, - 0x44, 0x44, 0x44, - 0x33, 0x33, 0x33, - 0x2D, 0x33, 0x00, - 0x22, 0x22, 0x22 -}; - -//////////////////////////////////////// -// Red Marble Cursor (12x12): -// Cursor When Holding The Red Marble -//////////////////////////////////////// -static const byte s_redMarbleCursor[] = { - 0, 0, 0, 0, 1, 2, 2, 2, 0, 0, 0, 0, - 0, 0, 3, 4, 2, 5, 2, 2, 2, 2, 0, 0, - 0, 6, 1, 1, 2, 2, 5, 2, 2, 2, 2, 0, - 0, 6, 3, 4, 5, 2, 2, 7, 8, 5, 2, 0, - 9, 6, 10,11,2, 2, 2, 12,13,2, 2, 2, - 14,10,6, 4, 1, 2, 8, 2, 2, 5, 2, 2, - 15,16,6, 3, 1, 2, 2, 2, 2, 2, 2, 5, - 17,9,18, 3, 4, 4, 4, 5, 2, 5, 1, 2, - 0, 16,9, 6, 6, 19,1, 20,1, 4, 11,0, - 0, 17,15,18,9, 10,6, 10,3, 21,4, 0, - 0, 0, 18,15,9, 18,6, 22,10,23,0, 0, - 0, 0, 0, 0, 15,15,16,9, 0, 0, 0, 0 -}; - - -//////////////////////////////////////// -// Red Marble Cursor Palette: -// Palette For The Red Marble Cursor -//////////////////////////////////////// -static const byte s_redMarbleCursorPalette[] = { - 0xb8, 0x33, 0x32, - 0xe5, 0x33, 0x31, - 0x98, 0x06, 0x00, - 0xb8, 0x00, 0x34, - 0xe6, 0x00, 0x34, - 0x7a, 0x04, 0x00, - 0xe8, 0x9a, 0x62, - 0xea, 0x31, 0x67, - 0x6a, 0x03, 0x00, - 0x8c, 0x00, 0x35, - 0xb6, 0x36, 0x00, - 0xed, 0xcd, 0x96, - 0xe9, 0x66, 0x65, - 0x5b, 0x35, 0x00, - 0x5b, 0x02, 0x00, - 0x5f, 0x00, 0x35, - 0x4c, 0x01, 0x00, - 0x5e, 0x33, 0x33, - 0x89, 0x05, 0x00, - 0xb6, 0x08, 0x00, - 0xa7, 0x07, 0x00, - 0x88, 0x36, 0x00, - 0x8b, 0x33, 0x33 -}; - -//////////////////////////////////////// -// Orange Marble Cursor (12x12): -// Cursor When Holding The Orange Marble -//////////////////////////////////////// -static const byte s_orangeMarbleCursor[] = { - 0, 0, 0, 0, 1, 2, 2, 3, 0, 0, 0, 0, - 0, 0, 4, 5, 2, 2, 3, 3, 3, 3, 0, 0, - 0, 6, 7, 4, 2, 1, 2, 2, 3, 3, 3, 0, - 0, 6, 6, 7, 1, 2, 3, 8, 9, 2, 10,0, - 11,12,7, 4, 2, 3, 3, 13,9, 2, 2, 1, - 14,15,6, 4, 2, 16,3, 3, 2, 1, 1, 1, - 14,14,12,17,4, 2, 2, 1, 2, 1, 2, 1, - 14,18,12,6, 4, 4, 4, 19,2, 19,20,4, - 0, 14,14,15,6, 15,6, 4, 4, 4, 4, 0, - 0, 14,11,14,14,12,12,12,17,6, 17,0, - 0, 0, 14,14,17,14,17,6, 6, 17,0, 0, - 0, 0, 0, 0, 14,11,14,11,0, 0, 0, 0 -}; - -//////////////////////////////////////// -// Orange Marble Cursor Palette: -// Palette For The Orange Marble Cursor -//////////////////////////////////////// -static const byte s_orangeMarbleCursorPalette[] = { - 0xe1, 0x9e, 0x00, - 0xe3, 0x9b, 0x28, - 0xe2, 0xcf, 0x20, - 0xb5, 0x6a, 0x00, - 0xb6, 0x9b, 0x29, - 0x87, 0x69, 0x00, - 0xb7, 0x67, 0x2f, - 0xe9, 0xff, 0x93, - 0xe1, 0xff, 0x5a, - 0xe0, 0xd0, 0x00, - 0x5e, 0x33, 0x33, - 0x88, 0x36, 0x00, - 0xf3, 0xff, 0xc9, - 0x5b, 0x35, 0x00, - 0x8b, 0x33, 0x33, - 0xe6, 0xce, 0x5f, - 0x8a, 0x67, 0x2f, - 0x5d, 0x67, 0x30, - 0xe2, 0x6a, 0x00, - 0xb3, 0x9d, 0x00 -}; - -//////////////////////////////////////// -// Yellow Marble Cursor (12x12): -// Cursor When Holding The Yellow Marble -//////////////////////////////////////// -static const byte s_yellowMarbleCursor[] = { - 0, 0, 0, 0, 1, 1, 1, 2, 0, 0, 0, 0, - 0, 0, 3, 4, 1, 1, 1, 5, 6, 6, 0, 0, - 0, 3, 3, 7, 1, 1, 1, 1, 2, 1, 6, 0, - 0, 3, 3, 3, 3, 1, 1, 8, 6, 1, 6, 0, - 9, 9, 3, 3, 1, 1, 2, 10,8, 1, 1, 2, - 11,9, 3, 3, 1, 1, 1, 1, 1, 1, 5, 1, - 9, 9, 12,3, 3, 1, 1, 1, 1, 1, 1, 1, - 9, 9, 9, 3, 3, 3, 3, 3, 1, 1, 1, 1, - 0, 11,9, 9, 12,3, 3, 3, 3, 3, 3, 0, - 0, 9, 9, 13,9, 14,12,3, 3, 3, 3, 0, - 0, 0, 9, 9, 9, 12,14,3, 13,3, 0, 0, - 0, 0, 0, 0, 11,9, 11,9, 0, 0, 0, 0 -}; - -//////////////////////////////////////// -// Yellow Marble Cursor Palette: -// Palette For The Yellow Marble Cursor -//////////////////////////////////////// -static const byte s_yellowMarbleCursorPalette[] = { - 0xb3, 0xd0, 0x00, - 0xb0, 0xff, 0x00, - 0x86, 0x9c, 0x00, - 0x87, 0xd0, 0x00, - 0xe0, 0xd0, 0x00, - 0xdc, 0xff, 0x00, - 0xb3, 0x9d, 0x00, - 0xdc, 0xff, 0x11, - 0x5a, 0x68, 0x00, - 0xe1, 0xff, 0x5a, - 0x5d, 0x67, 0x30, - 0x87, 0x69, 0x00, - 0x88, 0x9b, 0x2a, - 0x5a, 0x9c, 0x00 -}; - -//////////////////////////////////////// -// Green Marble Cursor (12x12): -// Cursor When Holding The Green Marble -//////////////////////////////////////// -static const byte s_greenMarbleCursor[] = { - 0, 0, 0, 0, 1, 2, 3, 3, 0, 0, 0, 0, - 0, 0, 4, 5, 2, 1, 2, 3, 6, 6, 0, 0, - 0, 7, 5, 8, 8, 1, 1, 2, 3, 6, 6, 0, - 0, 7, 7, 4, 8, 1, 2, 9, 6, 2, 6, 0, - 10,7, 7, 4, 1, 2, 3, 11,12,2, 2, 3, - 13,13,7, 4, 1, 2, 3, 2, 1, 2, 2, 3, - 14,13,7, 7, 5, 1, 1, 8, 2, 1, 1, 2, - 15,16,13,7, 4, 4, 5, 5, 1, 8, 1, 1, - 0, 15,13,7, 7, 7, 4, 4, 4, 5, 8, 0, - 0, 14,16,15,13, 7, 7, 7, 4,17,5, 0, - 0, 0, 10,16,13,13,13,17,18,17,0, 0, - 0, 0, 0, 0, 15,10,19,10,0, 0, 0, 0 -}; - -//////////////////////////////////////// -// Green Marble Cursor Palette: -// Palette For The Green Marble Cursor -//////////////////////////////////////// -static const byte s_greenMarbleCursorPalette[] = { - 0x0e, 0xd0, 0x00, - 0x0f, 0xe1, 0x00, - 0x10, 0xf2, 0x00, - 0x0b, 0x9c, 0x00, - 0x0c, 0xad, 0x00, - 0x11, 0xff, 0x00, - 0x09, 0x8a, 0x00, - 0x0d, 0xbe, 0x00, - 0x30, 0xff, 0x5a, - 0x0d, 0x67, 0x30, - 0x6b, 0xff, 0x92, - 0x00, 0xff, 0x28, - 0x08, 0x79, 0x00, - 0x05, 0x57, 0x00, - 0x30, 0x67, 0x30, - 0x06, 0x68, 0x00, - 0x00, 0x9b, 0x2c, - 0x2e, 0x9c, 0x00, - 0x2e, 0x68, 0x00 -}; - -//////////////////////////////////////// -// Blue Marble Cursor (12x12): -// Cursor When Holding The Blue Marble -//////////////////////////////////////// -static const byte s_blueMarbleCursor[] = { - 0, 0, 0, 0, 1, 2, 3, 3, 0, 0, 0, 0, - 0, 0, 4, 5, 2, 2, 6, 3, 7, 3, 0, 0, - 0, 8, 9, 5, 10,11,2, 6, 3, 3, 7, 0, - 0, 12,13,9, 10,11,6, 14,7, 6, 3, 0, - 15,8, 4, 13,2, 6, 3, 16,17,6, 6, 3, - 18,15,19,13,10,7, 3, 6, 2, 2, 6, 7, - 20,8, 18,4, 21,11,2, 10,6, 2, 2, 2, - 15,15,18,8, 13,9, 21,5, 11,10,2, 1, - 0, 8, 15,19,15,13,13,21,21,5, 9, 0, - 0, 22,20,15, 8,19,15,19,4, 9, 4, 0, - 0, 0, 15,20,15,15,19,15,9, 15,0, 0, - 0, 0, 0, 0, 20,15, 8,15,0, 0, 0, 0 -}; - -//////////////////////////////////////// -// Blue Marble Cursor Palette: -// Palette For The Blue Marble Cursor -//////////////////////////////////////// -static const byte s_blueMarbleCursorPalette[] = { - 0x6b, 0x00, 0xd2, - 0x66, 0x00, 0xe3, - 0x72, 0x00, 0xff, - 0x53, 0x2d, 0x9d, - 0x4e, 0x00, 0xaf, - 0x6d, 0x00, 0xf5, - 0x7d, 0x00, 0xff, - 0x44, 0x00, 0x69, - 0x56, 0x00, 0x9d, - 0x56, 0x00, 0xc0, - 0x5e, 0x00, 0xd2, - 0x2b, 0x31, 0x68, - 0x3f, 0x00, 0x8c, - 0x91, 0x22, 0xff, - 0x41, 0x31, 0x68, - 0xd7, 0x95, 0xff, - 0x77, 0x22, 0xff, - 0x2f, 0x00, 0x69, - 0x37, 0x00, 0x7a, - 0x27, 0x00, 0x58, - 0x46, 0x00, 0x9d, - 0x33, 0x33, 0x33 -}; - -//////////////////////////////////////// -// Violet Marble Cursor (12x12): -// Cursor When Holding The Violet Marble -//////////////////////////////////////// -static const byte s_violetMarbleCursor[] = { - 0, 0, 0, 0, 1, 1, 1, 2, 0, 0, 0, 0, - 0, 0, 3, 3, 1, 1, 1, 4, 2, 4, 0, 0, - 0, 3, 3, 3, 1, 5, 1, 1, 4, 2, 4, 0, - 0, 3, 3, 3, 3, 1, 1, 6, 4, 1, 2, 0, - 3, 7, 8, 3, 1, 1, 4, 9, 4, 1, 1, 4, - 8, 7, 8, 3, 10,4, 1, 1, 1, 1, 4, 1, - 8, 3, 8, 7, 3, 1, 1, 5, 1, 1, 1, 1, - 7, 7, 11,3, 3, 3, 3, 3, 1, 3, 1, 1, - 0, 8, 7, 7, 8, 8, 7, 3, 3, 3, 1, 0, - 0, 7, 8, 3, 11,7, 3, 11,3, 10,3, 0, - 0, 0, 8, 7, 3, 3, 7, 3, 3, 3, 0, 0, - 0, 0, 0, 0, 8, 7, 11,3, 0, 0, 0, 0 -}; - -//////////////////////////////////////// -// Violet Marble Cursor Palette: -// Palette For The Violet Marble Cursor -//////////////////////////////////////// -static const byte s_violetMarbleCursorPalette[] = { - 0xaa, 0x00, 0xd1, - 0xd8, 0x00, 0xff, - 0x76, 0x00, 0x9d, - 0xb5, 0x00, 0xff, - 0x87, 0x00, 0xd2, - 0xd7, 0x22, 0xff, - 0x68, 0x00, 0x69, - 0x44, 0x00, 0x69, - 0xd7, 0x5e, 0xff, - 0x9c, 0x00, 0x9d, - 0x56, 0x00, 0x9d -}; - -} // End of namespace Mohawk diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp index 7da5515411..5424a07a3c 100644 --- a/engines/mohawk/riven_external.cpp +++ b/engines/mohawk/riven_external.cpp @@ -216,20 +216,37 @@ void RivenExternal::runDemoBoundaryDialog() { dialog.runModal(); } -void RivenExternal::runEndGame(uint16 video) { +void RivenExternal::runEndGame(uint16 video, uint32 delay) { _vm->_sound->stopAllSLST(); _vm->_video->playMovieRiven(video); - runCredits(video); + runCredits(video, delay); } -void RivenExternal::runCredits(uint16 video) { - // TODO: Play until the last frame and then run the credits +void RivenExternal::runCredits(uint16 video, uint32 delay) { + // Initialize our credits state + _vm->_cursor->hideCursor(); + _vm->_gfx->beginCredits(); + uint nextCreditsFrameStart = 0; VideoHandle videoHandle = _vm->_video->findVideoHandleRiven(video); - while (!_vm->_video->endOfVideo(videoHandle) && !_vm->shouldQuit()) { - if (_vm->_video->updateMovies()) - _vm->_system->updateScreen(); + while (!_vm->shouldQuit() && _vm->_gfx->getCurCreditsImage() <= 320) { + if (_vm->_video->getCurFrame(videoHandle) >= (int32)_vm->_video->getFrameCount(videoHandle) - 1) { + if (nextCreditsFrameStart == 0) { + // Set us up to start after delay ms + nextCreditsFrameStart = _vm->_system->getMillis() + delay; + } else if (_vm->_system->getMillis() >= nextCreditsFrameStart) { + // the first two frames stay on for 5 seconds + // the rest of the scroll updates happen at 30Hz + if (_vm->_gfx->getCurCreditsImage() < 304) + nextCreditsFrameStart = _vm->_system->getMillis() + 5000; + else + nextCreditsFrameStart = _vm->_system->getMillis() + 1000 / 30; + + _vm->_gfx->updateCredits(); + } + } else if (_vm->_video->updateMovies()) + _vm->_system->updateScreen(); Common::Event event; while (_vm->_system->getEventManager()->pollEvent(event)) @@ -316,6 +333,7 @@ void RivenExternal::checkSliderCursorChange(uint16 startHotspot) { _vm->_cursor->setCursor(kRivenOpenHandCursor); else _vm->_cursor->setCursor(kRivenMainCursor); + _vm->_system->updateScreen(); break; } } @@ -341,6 +359,7 @@ void RivenExternal::dragDomeSlider(uint16 soundId, uint16 resetSlidersHotspot, u // We've clicked down, so show the closed hand cursor _vm->_cursor->setCursor(kRivenClosedHandCursor); + _vm->_system->updateScreen(); bool done = false; while (!done) { @@ -862,6 +881,7 @@ void RivenExternal::xbcheckcatch(uint16 argc, uint16 *argv) { void RivenExternal::xbait(uint16 argc, uint16 *argv) { // Set the cursor to the pellet _vm->_cursor->setCursor(kRivenPelletCursor); + _vm->_system->updateScreen(); // Loop until the player lets go (or quits) Common::Event event; @@ -881,6 +901,7 @@ void RivenExternal::xbait(uint16 argc, uint16 *argv) { // Set back the cursor _vm->_cursor->setCursor(kRivenMainCursor); + _vm->_system->updateScreen(); // Set the bait if we put it on the plate if (_vm->_hotspots[9].rect.contains(_vm->_system->getEventManager()->getMousePos())) { @@ -899,8 +920,8 @@ void RivenExternal::xbfreeytram(uint16 argc, uint16 *argv) { void RivenExternal::xbaitplate(uint16 argc, uint16 *argv) { // Remove the pellet from the plate and put it in your hand _vm->_gfx->drawPLST(3); - _vm->_gfx->updateScreen(); _vm->_cursor->setCursor(kRivenPelletCursor); + _vm->_gfx->updateScreen(); // Loop until the player lets go (or quits) Common::Event event; @@ -920,6 +941,7 @@ void RivenExternal::xbaitplate(uint16 argc, uint16 *argv) { // Set back the cursor _vm->_cursor->setCursor(kRivenMainCursor); + _vm->_system->updateScreen(); // Set the bait if we put it on the plate, remove otherwise if (_vm->_hotspots[9].rect.contains(_vm->_system->getEventManager()->getMousePos())) { @@ -988,23 +1010,27 @@ void RivenExternal::xvalvecontrol(uint16 argc, uint16 *argv) { if (*valve == 0 && changeY <= -10) { *valve = 1; _vm->_cursor->setCursor(kRivenHideCursor); + _vm->_system->updateScreen(); _vm->_video->playMovieBlockingRiven(2); _vm->refreshCard(); } else if (*valve == 1) { if (changeX >= 0 && changeY >= 10) { *valve = 0; _vm->_cursor->setCursor(kRivenHideCursor); + _vm->_system->updateScreen(); _vm->_video->playMovieBlockingRiven(3); _vm->refreshCard(); } else if (changeX <= -10 && changeY <= 10) { *valve = 2; _vm->_cursor->setCursor(kRivenHideCursor); + _vm->_system->updateScreen(); _vm->_video->playMovieBlockingRiven(1); _vm->refreshCard(); } } else if (*valve == 2 && changeX >= 10) { *valve = 1; _vm->_cursor->setCursor(kRivenHideCursor); + _vm->_system->updateScreen(); _vm->_video->playMovieBlockingRiven(4); _vm->refreshCard(); } @@ -1219,6 +1245,7 @@ void RivenExternal::xgisland1490_domecheck(uint16 argc, uint16 *argv) { void RivenExternal::xgplateau3160_dopools(uint16 argc, uint16 *argv) { // Play the deactivation of a pool if one is active and a different one is activated _vm->_cursor->setCursor(kRivenHideCursor); + _vm->_system->updateScreen(); _vm->_video->playMovieBlockingRiven(*_vm->getVar("glkbtns") * 2); } @@ -1522,11 +1549,13 @@ void RivenExternal::xvga1300_carriage(uint16 argc, uint16 *argv) { // Run the gallows's carriage _vm->_cursor->setCursor(kRivenHideCursor); // Hide the cursor - _vm->_video->playMovieBlockingRiven(1); // Play handle movie + _vm->_system->updateScreen(); // Update + _vm->_video->playMovieBlockingRiven(1); // Play handle movie _vm->_gfx->scheduleTransition(15); // Set pan down transition _vm->changeToCard(_vm->matchRMAPToCard(0x18e77)); // Change to card facing up _vm->_cursor->setCursor(kRivenHideCursor); // Hide the cursor (again) - _vm->_video->playMovieBlockingRiven(4); // Play carriage beginning to drop + _vm->_system->updateScreen(); // Update + _vm->_video->playMovieBlockingRiven(4); // Play carriage beginning to drop _vm->_gfx->scheduleTransition(14); // Set pan up transition _vm->changeToCard(_vm->matchRMAPToCard(0x183a9)); // Change to card looking straight again _vm->_video->playMovieBlockingRiven(2); @@ -1560,19 +1589,22 @@ void RivenExternal::xvga1300_carriage(uint16 argc, uint16 *argv) { } _vm->_cursor->setCursor(kRivenHideCursor); // Hide the cursor + _vm->_system->updateScreen(); // Update if (gotClick) { _vm->_gfx->scheduleTransition(16); // Schedule dissolve transition _vm->changeToCard(_vm->matchRMAPToCard(0x18d4d)); // Move forward _vm->_cursor->setCursor(kRivenHideCursor); // Hide the cursor + _vm->_system->updateScreen(); // Update _vm->_system->delayMillis(500); // Delay a half second before changing again _vm->_gfx->scheduleTransition(12); // Schedule pan left transition _vm->changeToCard(_vm->matchRMAPToCard(0x18ab5)); // Turn right _vm->_cursor->setCursor(kRivenHideCursor); // Hide the cursor - _vm->_video->playMovieBlockingRiven(1); // Play carriage ride movie + _vm->_system->updateScreen(); // Update + _vm->_video->playMovieBlockingRiven(1); // Play carriage ride movie _vm->changeToCard(_vm->matchRMAPToCard(0x17167)); // We have arrived at the top } else - _vm->_video->playMovieBlockingRiven(3); // Too slow! + _vm->_video->playMovieBlockingRiven(3); // Too slow! } void RivenExternal::xjdome25_resetsliders(uint16 argc, uint16 *argv) { @@ -1603,6 +1635,7 @@ int RivenExternal::jspitElevatorLoop() { _vm->_cursor->setCursor(kRivenClosedHandCursor); _vm->_system->updateScreen(); + for (;;) { while (_vm->_system->getEventManager()->pollEvent(event)) { switch (event.type) { @@ -1766,6 +1799,7 @@ void RivenExternal::xschool280_playwhark(uint16 argc, uint16 *argv) { // Hide the cursor _vm->_cursor->setCursor(kRivenHideCursor); + _vm->_system->updateScreen(); // Play the spin movie _vm->_video->playMovieBlockingRiven(spinMLST); @@ -1821,16 +1855,17 @@ void RivenExternal::xorollcredittime(uint16 argc, uint16 *argv) { uint32 *gehnState = _vm->getVar("agehn"); if (*gehnState == 0) // Gehn who? - runEndGame(1); + runEndGame(1, 9500); else if (*gehnState == 4) // You freed him? Are you kidding me? - runEndGame(2); + runEndGame(2, 12000); else // You already spoke with Gehn. What were you thinking? - runEndGame(3); + runEndGame(3, 8000); } void RivenExternal::xbookclick(uint16 argc, uint16 *argv) { // Hide the cursor _vm->_cursor->setCursor(kRivenHideCursor); + _vm->_system->updateScreen(); // Let's hook onto our video VideoHandle video = _vm->_video->findVideoHandleRiven(argv[0]); @@ -1873,6 +1908,8 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) { _vm->_cursor->setCursor(kRivenOpenHandCursor); else _vm->_cursor->setCursor(kRivenMainCursor); + + _vm->_system->updateScreen(); // OK, Gehn has opened the trap book and has asked us to go in. Let's watch // and see what the player will do... @@ -1887,7 +1924,7 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) { _vm->_cursor->setCursor(kRivenOpenHandCursor); else _vm->_cursor->setCursor(kRivenMainCursor); - updateScreen = false; // Don't update twice, changing the cursor already updates the screen + updateScreen = true; break; case Common::EVENT_LBUTTONUP: if (hotspotRect.contains(_vm->_system->getEventManager()->getMousePos())) { @@ -1899,11 +1936,11 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) { _vm->_gfx->updateScreen(); // Update the screen _vm->_sound->playSound(0); // Play the link sound _vm->_video->activateMLST(7, _vm->getCurCard()); // Activate Gehn Link Video - _vm->_video->playMovieBlockingRiven(1); // Play Gehn Link Video + _vm->_video->playMovieBlockingRiven(1); // Play Gehn Link Video *_vm->getVar("agehn") = 4; // Set Gehn to the trapped state *_vm->getVar("atrapbook") = 1; // We've got the trap book again _vm->_sound->playSound(0); // Play the link sound again - _vm->changeToCard(_vm->matchRMAPToCard(0x2885)); // Link out! (TODO: Shouldn't this card change?) + _vm->changeToCard(_vm->matchRMAPToCard(0x2885)); // Link out! return; } break; @@ -1924,13 +1961,14 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) { // Hide the cursor again _vm->_cursor->setCursor(kRivenHideCursor); + _vm->_system->updateScreen(); // If there was no click and this is the third time Gehn asks us to // use the trap book, he will shoot the player. Dead on arrival. // Run the credits from here. if (*_vm->getVar("agehn") == 3) { _vm->_scriptMan->stopAllScripts(); - runCredits(argv[0]); + runCredits(argv[0], 5000); return; } @@ -2012,6 +2050,7 @@ uint16 RivenExternal::getComboDigit(uint32 correctCombo, uint32 digit) { void RivenExternal::xgwatch(uint16 argc, uint16 *argv) { // Hide the cursor _vm->_cursor->setCursor(kRivenHideCursor); + _vm->_system->updateScreen(); uint32 *prisonCombo = _vm->getVar("pcorrectorder"); uint32 soundTime = _vm->_system->getMillis() - 500; // Start the first sound instantly @@ -2104,7 +2143,7 @@ void RivenExternal::xrcredittime(uint16 argc, uint16 *argv) { // For the record, when agehn == 4, Gehn will thank you for // showing him the rebel age and then leave you to die. // Otherwise, the rebels burn the book. Epic fail either way. - runEndGame(1); + runEndGame(1, 1500); } void RivenExternal::xrshowinventory(uint16 argc, uint16 *argv) { @@ -2144,35 +2183,32 @@ void RivenExternal::xtexterior300_telescopedown(uint16 argc, uint16 *argv) { if (*_vm->getVar("pcage") == 2) { // The best ending: Catherine is free, Gehn is trapped, Atrus comes to rescue you. // And now we fall back to Earth... all the way... - warning("xtexterior300_telescopedown: Good ending"); _vm->_video->activateMLST(8, _vm->getCurCard()); - runEndGame(8); + runEndGame(8, 5000); } else if (*_vm->getVar("agehn") == 4) { // The ok ending: Catherine is still trapped, Gehn is trapped, Atrus comes to rescue you. // Nice going! Catherine and the islanders are all dead now! Just go back to your home... - warning("xtexterior300_telescopedown: OK ending"); _vm->_video->activateMLST(9, _vm->getCurCard()); - runEndGame(9); + runEndGame(9, 5000); } else if (*_vm->getVar("atrapbook") == 1) { // The bad ending: Catherine is trapped, Gehn is free, Atrus gets shot by Gehn, // And then you get shot by Cho. Nice going! Catherine and the islanders are dead // and you have just set Gehn free from Riven, not to mention you're dead. - warning("xtexterior300_telescopedown: Bad ending"); _vm->_video->activateMLST(10, _vm->getCurCard()); - runEndGame(10); + runEndGame(10, 5000); } else { // The impossible ending: You don't have Catherine's journal and yet you were somehow // able to open the hatch on the telescope. The game provides an ending for those who // cheat, load a saved game with the combo, or just guess the telescope combo. Atrus // doesn't come and you just fall into the fissure. - warning("xtexterior300_telescopedown: Wtf ending"); _vm->_video->activateMLST(11, _vm->getCurCard()); - runEndGame(11); + runEndGame(11, 5000); } } else { // ...the telescope can't move down anymore. // Play the sound of not being able to move _vm->_cursor->setCursor(kRivenHideCursor); + _vm->_system->updateScreen(); _vm->_sound->playSoundBlocking(13); } } else { @@ -2206,6 +2242,7 @@ void RivenExternal::xtexterior300_telescopeup(uint16 argc, uint16 *argv) { if (*telescopePos == 5) { // Play the sound of not being able to move _vm->_cursor->setCursor(kRivenHideCursor); + _vm->_system->updateScreen(); _vm->_sound->playSoundBlocking(13); return; } diff --git a/engines/mohawk/riven_external.h b/engines/mohawk/riven_external.h index 818bc06c54..90fdc664c1 100644 --- a/engines/mohawk/riven_external.h +++ b/engines/mohawk/riven_external.h @@ -61,8 +61,8 @@ private: // Supplementary Functions int jspitElevatorLoop(); void runDemoBoundaryDialog(); - void runEndGame(uint16 video); - void runCredits(uint16 video); + void runEndGame(uint16 video, uint32 delay); + void runCredits(uint16 video, uint32 delay); void runDomeCheck(); void runDomeButtonMovie(); void resetDomeSliders(uint16 soundId, uint16 startHotspot); diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp index 5d459354e9..98452e1e09 100644 --- a/engines/mohawk/riven_scripts.cpp +++ b/engines/mohawk/riven_scripts.cpp @@ -424,6 +424,7 @@ void RivenScript::stopSound(uint16 op, uint16 argc, uint16 *argv) { void RivenScript::changeCursor(uint16 op, uint16 argc, uint16 *argv) { debug(2, "Change to cursor %d", argv[0]); _vm->_cursor->setCursor(argv[0]); + _vm->_system->updateScreen(); } // Command 14: pause script execution (delay in ms, u1) diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index d77ac858c8..db93333b81 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -40,12 +40,14 @@ #include "sci/sound/music.h" #include "sci/sound/drivers/mididriver.h" #include "sci/sound/drivers/map-mt32-to-gm.h" +#include "sci/graphics/cache.h" #include "sci/graphics/cursor.h" #include "sci/graphics/screen.h" #include "sci/graphics/paint.h" #include "sci/graphics/paint16.h" #include "sci/graphics/paint32.h" #include "sci/graphics/palette.h" +#include "sci/graphics/view.h" #include "sci/parser/vocabulary.h" @@ -1434,7 +1436,7 @@ bool Console::cmdSaid(int argc, const char **argv) { _engine->getVocabulary()->dumpParseTree(); _engine->getVocabulary()->parserIsValid = true; - int ret = said(_engine->_gamestate, (byte*)spec, true); + int ret = said((byte*)spec, true); DebugPrintf("kSaid: %s\n", (ret == SAID_NO_MATCH ? "No match" : "Match")); } @@ -1503,7 +1505,14 @@ bool Console::cmdDrawCel(int argc, const char **argv) { uint16 loopNo = atoi(argv[2]); uint16 celNo = atoi(argv[3]); - _engine->_gfxPaint->kernelDrawCel(resourceId, loopNo, celNo, 50, 50, 0, 0, false, NULL_REG); + if (_engine->_gfxPaint16) { + _engine->_gfxPaint16->kernelDrawCel(resourceId, loopNo, celNo, 50, 50, 0, 0, 128, 128, false, NULL_REG); + } else { + GfxView *view = _engine->_gfxCache->getView(resourceId); + Common::Rect celRect(50, 50, 50 + view->getWidth(loopNo, celNo), 50 + view->getHeight(loopNo, celNo)); + view->draw(celRect, celRect, celRect, loopNo, celNo, 255, 0, false); + _engine->_gfxScreen->copyRectToScreen(celRect); + } return true; } @@ -2189,7 +2198,7 @@ bool Console::cmdStack(int argc, const char **argv) { return true; } - ExecStack &xs = _engine->_gamestate->_executionStack.back(); + const ExecStack &xs = _engine->_gamestate->_executionStack.back(); int nr = atoi(argv[1]); for (int i = nr; i > 0; i--) { @@ -2438,12 +2447,12 @@ bool Console::cmdScriptSteps(int argc, const char **argv) { bool Console::cmdBacktrace(int argc, const char **argv) { DebugPrintf("Call stack (current base: 0x%x):\n", _engine->_gamestate->executionStackBase); - Common::List<ExecStack>::iterator iter; + Common::List<ExecStack>::const_iterator iter; uint i = 0; for (iter = _engine->_gamestate->_executionStack.begin(); iter != _engine->_gamestate->_executionStack.end(); ++iter, ++i) { - ExecStack &call = *iter; + const ExecStack &call = *iter; const char *objname = _engine->_gamestate->_segMan->getObjectName(call.sendp); int paramc, totalparamc; @@ -2607,7 +2616,7 @@ bool Console::cmdDisassemble(int argc, const char **argv) { const Object *obj = _engine->_gamestate->_segMan->getObject(objAddr); int selectorId = _engine->getKernel()->findSelector(argv[2]); - reg_t addr; + reg_t addr = NULL_REG; if (!obj) { DebugPrintf("Not an object."); @@ -2626,13 +2635,22 @@ bool Console::cmdDisassemble(int argc, const char **argv) { for (int i = 3; i < argc; i++) { if (!scumm_stricmp(argv[i], "bwt")) - printBytecode = true; - else if (!scumm_stricmp(argv[i], "bc")) printBWTag = true; + else if (!scumm_stricmp(argv[i], "bc")) + printBytecode = true; } + reg_t farthestTarget = addr; do { + reg_t prevAddr = addr; + reg_t jumpTarget; + if (isJumpOpcode(_engine->_gamestate, addr, jumpTarget)) { + if (jumpTarget > farthestTarget) + farthestTarget = jumpTarget; + } addr = disassemble(_engine->_gamestate, addr, printBWTag, printBytecode); + if (addr.isNull() && prevAddr < farthestTarget) + addr = prevAddr + 1; // skip past the ret } while (addr.offset > 0); return true; @@ -2699,26 +2717,29 @@ void Console::printKernelCallsFound(int kernelFuncNum, bool showFoundScripts) { int scriptSegment; Script *script; - SegManager *segMan = _engine->getEngineState()->_segMan; + // Create a custom segment manager here, so that the game's segment + // manager won't be affected by loading and unloading scripts here. + SegManager *customSegMan = new SegManager(_engine->getResMan()); while (itr != resources->end()) { - if (_engine->getGameId() == GID_KQ5 && itr->getNumber() == 980) { - // Ignore script 980 in KQ5. Seems to be a leftover, as it - // uses a superclass from script 988, which doesn't exist + // Ignore specific leftover scripts, which require other non-existing scripts + if ((_engine->getGameId() == GID_HOYLE3 && itr->getNumber() == 995) || + (_engine->getGameId() == GID_KQ5 && itr->getNumber() == 980) || + (_engine->getGameId() == GID_SLATER && itr->getNumber() == 947)) { itr++; continue; } // Load script - scriptSegment = segMan->instantiateScript(itr->getNumber()); - script = segMan->getScript(scriptSegment); + scriptSegment = customSegMan->instantiateScript(itr->getNumber()); + script = customSegMan->getScript(scriptSegment); // Iterate through all the script's objects ObjMap::iterator it; const ObjMap::iterator end = script->_objects.end(); for (it = script->_objects.begin(); it != end; ++it) { - const Object *obj = segMan->getObject(it->_value.getPos()); - const char *objName = segMan->getObjectName(it->_value.getPos()); + const Object *obj = customSegMan->getObject(it->_value.getPos()); + const char *objName = customSegMan->getObjectName(it->_value.getPos()); // Now dissassemble each method of the script object for (uint16 i = 0; i < obj->getMethodCount(); i++) { @@ -2761,10 +2782,12 @@ void Console::printKernelCallsFound(int kernelFuncNum, bool showFoundScripts) { } // for (uint16 i = 0; i < obj->getMethodCount(); i++) } // for (it = script->_objects.begin(); it != end; ++it) - segMan->uninstantiateScript(itr->getNumber()); + customSegMan->uninstantiateScript(itr->getNumber()); ++itr; } + delete customSegMan; + delete resources; } @@ -3758,11 +3781,16 @@ int Console::printObject(reg_t pos) { return 0; } +static void printChar(byte c) { + if (c < 32 || c >= 127) + c = '.'; + debugN("%c", c); +} + void Console::hexDumpReg(const reg_t *data, int len, int regsPerLine, int startOffset, bool isArray) { // reg_t version of Common::hexdump assert(1 <= regsPerLine && regsPerLine <= 8); int i; - byte c; int offset = startOffset; while (len >= regsPerLine) { debugN("%06x: ", offset); @@ -3771,14 +3799,13 @@ void Console::hexDumpReg(const reg_t *data, int len, int regsPerLine, int startO } debugN(" |"); for (i = 0; i < regsPerLine; i++) { - c = data[i].toUint16() & 0xff; - if (c < 32 || c >= 127) - c = '.'; - debugN("%c", c); - c = data[i].toUint16() >> 8; - if (c < 32 || c >= 127) - c = '.'; - debugN("%c", c); + if (g_sci->isBE()) { + printChar(data[i].toUint16() >> 8); + printChar(data[i].toUint16() & 0xff); + } else { + printChar(data[i].toUint16() & 0xff); + printChar(data[i].toUint16() >> 8); + } } debugN("|\n"); data += regsPerLine; @@ -3798,14 +3825,13 @@ void Console::hexDumpReg(const reg_t *data, int len, int regsPerLine, int startO } debugN(" |"); for (i = 0; i < len; i++) { - c = data[i].toUint16() & 0xff; - if (c < 32 || c >= 127) - c = '.'; - debugN("%c", c); - c = data[i].toUint16() >> 8; - if (c < 32 || c >= 127) - c = '.'; - debugN("%c", c); + if (g_sci->isBE()) { + printChar(data[i].toUint16() >> 8); + printChar(data[i].toUint16() & 0xff); + } else { + printChar(data[i].toUint16() & 0xff); + printChar(data[i].toUint16() >> 8); + } } for (; i < regsPerLine; i++) debugN(" "); diff --git a/engines/sci/console.h b/engines/sci/console.h index b0a1de6ebd..d45454376a 100644 --- a/engines/sci/console.h +++ b/engines/sci/console.h @@ -37,6 +37,7 @@ class SciEngine; struct List; reg_t disassemble(EngineState *s, reg_t pos, bool printBWTag, bool printBytecode); +bool isJumpOpcode(EngineState *s, reg_t pos, reg_t& jumpOffset); class Console : public GUI::Debugger { public: diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h index c1c13a0894..923d35fe16 100644 --- a/engines/sci/detection_tables.h +++ b/engines/sci/detection_tables.h @@ -1992,31 +1992,31 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH }, - // Larry 6 - English/German/French DOS CD - LORES + // Larry 6 - English/German/French DOS CD - LOWRES // SCI interpreter version 1.001.115 {"lsl6", "", { {"resource.map", 0, "0b91234b7112782962cb480b7791b6e2", 7263}, {"resource.000", 0, "57d5fe8bb9e044158514476ea7678eb0", 5754790}, AD_LISTEND}, - Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE }, + Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO_NONE }, - // Larry 6 - German DOS CD - LORES (provided by richiefs in bug report #2670691) + // Larry 6 - German DOS CD - LOWRES (provided by richiefs in bug report #2670691) // SCI interpreter version 1.001.115 {"lsl6", "", { {"resource.map", 0, "bafe85f32738854135991d4324ad147e", 7268}, {"resource.000", 0, "f6cbc6da7b90ea135883e0759848ca2c", 5773160}, AD_LISTEND}, - Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NONE }, + Common::DE_DEU, Common::kPlatformPC, ADGF_CD, GUIO_NONE }, - // Larry 6 - French DOS CD - LORES (provided by richiefs in bug report #2670691) + // Larry 6 - French DOS CD - LOWRES (provided by richiefs in bug report #2670691) // SCI interpreter version 1.001.115 {"lsl6", "", { {"resource.map", 0, "97797ea775baaf18a1907d357d3c0ea6", 7268}, {"resource.000", 0, "f6cbc6da7b90ea135883e0759848ca2c", 5776092}, AD_LISTEND}, - Common::FR_FRA, Common::kPlatformPC, 0, GUIO_NONE }, + Common::FR_FRA, Common::kPlatformPC, ADGF_CD, GUIO_NONE }, - // Larry 6 - Spanish DOS - LORES (from the Leisure Suit Larry Collection) + // Larry 6 - Spanish DOS - LOWRES (from the Leisure Suit Larry Collection) // Executable scanning reports "1.001.113", VERSION file reports "1.000, 11.06.93, FIVE PATCHES ADDED TO DISK 6 ON 11-18-93" {"lsl6", "", { {"resource.map", 0, "633bf8f42170b6271019917c8009989b", 6943}, @@ -2628,6 +2628,14 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH }, + // Police Quest: SWAT - English DOS (from GOG.com) + // Executable scanning reports "2.100.002", VERSION file reports "1.0c" + {"pqswat", "", { + {"resmap.000", 0, "1c2563fee189885e29d9348f37306d94", 12175}, + {"ressci.000", 0, "b2e1826ca81ce2e7e764587f5a14eee9", 127149181}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE }, + // Police Quest: SWAT - English Windows (from the Police Quest Collection) // Executable scanning reports "2.100.002", VERSION file reports "1.0c" // Original DOS/Windows release VERSION file reports "1.000" is the same @@ -2641,7 +2649,7 @@ static const struct ADGameDescription SciGameDescriptions[] = { {"resmap.004", 0, "4228038906f041623e65789500b22285", 6835}, {"ressci.004", 0, "b7e619e6ecf62fe65d5116a3a422e5f0", 46223872}, AD_LISTEND}, - Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NOSPEECH }, + Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NONE }, #endif // ENABLE_SCI32 // Quest for Glory 1 / Hero's Quest - English DOS 3.5" Floppy (supplied by merkur in bug report #2718784) @@ -2656,6 +2664,17 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH }, + // Quest for Glory 1 / Hero's Quest - English DOS 3.5" Floppy (supplied by alonzotg in bug report #3206006) + {"qfg1", "", { + {"resource.map", 0, "85512508ed4e4ef1e3b309adabceeda9", 6486}, + {"resource.000", 0, "481b034132106390cb5160fe61dd5f58", 80334}, + {"resource.001", 0, "4d67acf52833ff45c7f753d6663532e8", 462729}, + {"resource.002", 0, "439ba9b6dde216e6eb97ef3a9830fbe4", 647244}, + {"resource.003", 0, "7ab2bf8e224b57f75e0cd6e4ba790761", 642203}, + {"resource.004", 0, "7ab2bf8e224b57f75e0cd6e4ba790761", 641688}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH }, + // Quest for Glory 1 / Hero's Quest - English DOS 5.25" Floppy (supplied by markcoolio in bug report #2723843) // Executable scanning reports "0.000.566" {"qfg1", "", { @@ -2709,7 +2728,7 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH }, - // Quest for Glory 1 - Japanese PC-98 5.25" Floppy (also includes english language) + // Quest for Glory 1 - Japanese PC-98 5.25" Floppy (also includes English language) // Executable scanning reports "S.old.201" {"qfg1", "8 Colors", { {"resource.map", 0, "5cbeb95dd2a4b7cb242b415cc6ec1c47", 6444}, @@ -2719,7 +2738,7 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::JA_JPN, Common::kPlatformPC98, ADGF_ADDENGLISH, GUIO_NOSPEECH }, - // Quest for Glory 1 - Japanese PC-98 5.25" Floppy (also includes english language) + // Quest for Glory 1 - Japanese PC-98 5.25" Floppy (also includes English language) // Executable scanning reports "S.old.201" {"qfg1", "16 Colors", { {"resource.map", 0, "3ecaba33bf77cb434067a0b8aee15097", 6444}, diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp index 206624f87e..964097f57d 100644 --- a/engines/sci/engine/features.cpp +++ b/engines/sci/engine/features.cpp @@ -285,20 +285,16 @@ SciVersion GameFeatures::detectLofsType() { } // Find a function of the "Game" object (which is the game super class) which invokes lofsa/lofss - reg_t gameSuperClass = g_sci->getGameSuperClassAddress(); + const Object *gameObject = _segMan->getObject(g_sci->getGameObject()); + const Object *gameSuperObject = _segMan->getObject(gameObject->getSuperClassSelector()); bool found = false; - if (!gameSuperClass.isNull()) { - Common::String gameSuperClassName = _segMan->getObjectName(gameSuperClass); - const Object *gameSuperObject = _segMan->getObject(gameSuperClass); + if (gameSuperObject) { + Common::String gameSuperClassName = _segMan->getObjectName(gameObject->getSuperClassSelector()); - if (gameSuperObject) { - for (uint m = 0; m < gameSuperObject->getMethodCount(); m++) { - found = autoDetectLofsType(gameSuperClassName, m); - if (found) - break; - } - } else { - warning("detectLofsType(): Could not get superclass object"); + for (uint m = 0; m < gameSuperObject->getMethodCount(); m++) { + found = autoDetectLofsType(gameSuperClassName, m); + if (found) + break; } } else { warning("detectLofsType(): Could not find superclass of game object"); diff --git a/engines/sci/engine/gc.cpp b/engines/sci/engine/gc.cpp index 7692613ee5..d205763051 100644 --- a/engines/sci/engine/gc.cpp +++ b/engines/sci/engine/gc.cpp @@ -25,11 +25,30 @@ #include "sci/engine/gc.h" #include "common/array.h" +#include "sci/graphics/ports.h" namespace Sci { //#define GC_DEBUG_CODE +#ifdef GC_DEBUG_CODE +const char *segmentTypeNames[] = { + "invalid", // 0 + "script", // 1 + "clones", // 2 + "locals", // 3 + "stack", // 4 + "obsolete", // 5: obsolete system strings + "lists", // 6 + "nodes", // 7 + "hunk", // 8 + "dynmem", // 9 + "obsolete", // 10: obsolete string fragments + "array", // 11: SCI32 arrays + "string" // 12: SCI32 strings +}; +#endif + struct WorklistManager { Common::Array<reg_t> _worklist; AddrSet _map; // used for 2 contains() calls, inside push() and run_gc() @@ -84,6 +103,18 @@ static void processWorkList(SegManager *segMan, WorklistManager &wm, const Commo } } +static void processEngineHunkList(WorklistManager &wm) { + PortList windowList = g_sci->_gfxPorts->_windowList; + + for (PortList::const_iterator it = windowList.begin(); it != windowList.end(); ++it) { + if ((*it)->isWindow()) { + Window *wnd = ((Window *)*it); + wm.push(wnd->hSaved1); + wm.push(wnd->hSaved2); + } + } +} + AddrSet *findAllActiveReferences(EngineState *s) { assert(!s->_executionStack.empty()); @@ -95,7 +126,7 @@ AddrSet *findAllActiveReferences(EngineState *s) { // Initialize value stack // We do this one by hand since the stack doesn't know the current execution stack - Common::List<ExecStack>::iterator iter = s->_executionStack.reverse_begin(); + Common::List<ExecStack>::const_iterator iter = s->_executionStack.reverse_begin(); // Skip fake kernel stack frame if it's on top if ((*iter).type == EXEC_STACK_TYPE_KERNEL) @@ -103,9 +134,9 @@ AddrSet *findAllActiveReferences(EngineState *s) { assert((iter != s->_executionStack.end()) && ((*iter).type != EXEC_STACK_TYPE_KERNEL)); - ExecStack &xs = *iter; + const StackPtr sp = iter->sp; - for (reg_t *pos = s->stack_base; pos < xs.sp; pos++) + for (reg_t *pos = s->stack_base; pos < sp; pos++) wm.push(*pos); debugC(kDebugLevelGC, "[GC] -- Finished adding value stack"); @@ -113,7 +144,7 @@ AddrSet *findAllActiveReferences(EngineState *s) { // Init: Execution Stack for (iter = s->_executionStack.begin(); iter != s->_executionStack.end(); ++iter) { - ExecStack &es = *iter; + const ExecStack &es = *iter; if (es.type != EXEC_STACK_TYPE_KERNEL) { wm.push(es.objp); @@ -143,6 +174,9 @@ AddrSet *findAllActiveReferences(EngineState *s) { processWorkList(s->_segMan, wm, heap); + if (getSciVersion() <= SCI_VERSION_1_1) + processEngineHunkList(wm); + return normalizeAddresses(s->_segMan, wm._map); } @@ -170,7 +204,7 @@ void run_gc(EngineState *s) { if (mobj != NULL) { #ifdef GC_DEBUG_CODE const SegmentType type = mobj->getType(); - segnames[type] = SegmentObj::getSegmentTypeName(type); + segnames[type] = segmentTypeNames[type]; #endif // Get a list of all deallocatable objects in this segment, diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index 202fc40ba4..9a55ef630b 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -255,6 +255,11 @@ private: Common::StringArray checkStaticSelectorNames(); /** + * Automatically find specific selectors + */ + void findSpecificSelectors(Common::StringArray &selectorNames); + + /** * Maps special selectors. */ void mapSelectors(); diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index 71892a8bea..16d56d10a8 100644 --- a/engines/sci/engine/kernel_tables.h +++ b/engines/sci/engine/kernel_tables.h @@ -56,7 +56,7 @@ struct SciKernelMapSubEntry { #define SIG_SCIALL SCI_VERSION_NONE, SCI_VERSION_NONE #define SIG_SCI0 SCI_VERSION_NONE, SCI_VERSION_01 -#define SIG_SCI1 SCI_VERSION_1_EGA, SCI_VERSION_1_LATE +#define SIG_SCI1 SCI_VERSION_1_EGA_ONLY, SCI_VERSION_1_LATE #define SIG_SCI11 SCI_VERSION_1_1, SCI_VERSION_1_1 #define SIG_SINCE_SCI11 SCI_VERSION_1_1, SCI_VERSION_NONE #define SIG_SCI21 SCI_VERSION_2_1, SCI_VERSION_3 @@ -348,7 +348,7 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(FindKey), SIG_EVERYWHERE, "l.", NULL, kFindKey_workarounds }, { MAP_CALL(FirstNode), SIG_EVERYWHERE, "[l0]", NULL, NULL }, { MAP_CALL(FlushResources), SIG_EVERYWHERE, "i", NULL, NULL }, - { MAP_CALL(Format), SIG_EVERYWHERE, "r(.*)", NULL, NULL }, + { MAP_CALL(Format), SIG_EVERYWHERE, "r[ri](.*)", NULL, NULL }, { MAP_CALL(GameIsRestarting), SIG_EVERYWHERE, "(i)", NULL, NULL }, { MAP_CALL(GetAngle), SIG_EVERYWHERE, "iiii", NULL, kGetAngle_workarounds }, { MAP_CALL(GetCWD), SIG_EVERYWHERE, "r", NULL, NULL }, @@ -434,7 +434,7 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(Sort), SIG_EVERYWHERE, "ooo", NULL, NULL }, { MAP_CALL(Sqrt), SIG_EVERYWHERE, "i", NULL, NULL }, { MAP_CALL(StrAt), SIG_EVERYWHERE, "ri(i)", NULL, kStrAt_workarounds }, - { MAP_CALL(StrCat), SIG_EVERYWHERE, "rr", NULL, kStrCat_workarounds }, + { MAP_CALL(StrCat), SIG_EVERYWHERE, "rr", NULL, NULL }, { MAP_CALL(StrCmp), SIG_EVERYWHERE, "rr(i)", NULL, NULL }, { MAP_CALL(StrCpy), SIG_EVERYWHERE, "r[r0](i)", NULL, NULL }, { MAP_CALL(StrEnd), SIG_EVERYWHERE, "r", NULL, NULL }, @@ -490,6 +490,12 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(ListFirstTrue), SIG_EVERYWHERE, "li(.*)", NULL, NULL }, { MAP_CALL(ListIndexOf), SIG_EVERYWHERE, "l[o0]", NULL, NULL }, { "OnMe", kIsOnMe, SIG_EVERYWHERE, "iioi", NULL, NULL }, + // Purge is used by the memory manager in SSCI to ensure that X number of bytes (the so called "unmovable + // memory") are available when the current room changes. This is similar to the SCI0-SCI1.1 FlushResources + // call, with the added functionality of ensuring that a specific amount of memory is available. We have + // our own memory manager and garbage collector, thus we simply call FlushResources, which in turn invokes + // our garbage collector (i.e. the SCI0-SCI1.1 semantics). + { "Purge", kFlushResources, SIG_EVERYWHERE, "i", NULL, NULL }, { MAP_CALL(RepaintPlane), SIG_EVERYWHERE, "o", NULL, NULL }, { MAP_CALL(SetShowStyle), SIG_EVERYWHERE, "ioiiiii([ri])(i)", NULL, NULL }, { MAP_CALL(String), SIG_EVERYWHERE, "(.*)", NULL, NULL }, @@ -508,12 +514,6 @@ static SciKernelMapEntry s_kernelMap[] = { // MakeSaveCatName - used in the Save/Load dialog of GK1CD (SRDialog, script 64990) // MakeSaveFileName - used in the Save/Load dialog of GK1CD (SRDialog, script 64990) - // SCI2 empty functions - - // Purge is used by the memory manager in SSCI to ensure that X number of bytes (the so called "unmovable - // memory") are available. We have our own memory manager and garbage collector, thus we ignore this call. - { MAP_EMPTY(Purge), SIG_EVERYWHERE, "i", NULL, NULL }, - // Unused / debug SCI2 unused functions, always mapped to kDummy { MAP_DUMMY(InspectObject), SIG_EVERYWHERE, "(.*)", NULL, NULL }, // Profiler (same as SCI0-SCI1.1) diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp index e5a9931605..725b78341b 100644 --- a/engines/sci/engine/kevent.cpp +++ b/engines/sci/engine/kevent.cpp @@ -35,6 +35,7 @@ #include "sci/event.h" #include "sci/graphics/coordadjuster.h" #include "sci/graphics/cursor.h" +#include "sci/graphics/maciconbar.h" namespace Sci { @@ -46,17 +47,25 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { SegManager *segMan = s->_segMan; Common::Point mousePos; - // Limit the mouse cursor position, if necessary - g_sci->_gfxCursor->refreshPosition(); - mousePos = g_sci->_gfxCursor->getPosition(); -#ifdef ENABLE_SCI32 - if (getSciVersion() >= SCI_VERSION_2_1) - g_sci->_gfxCoordAdjuster->fromDisplayToScript(mousePos.y, mousePos.x); -#endif + // For Mac games with an icon bar, handle possible icon bar events first + if (g_sci->hasMacIconBar()) { + reg_t iconObj = g_sci->_gfxMacIconBar->handleEvents(); + if (!iconObj.isNull()) + invokeSelector(s, iconObj, SELECTOR(select), argc, argv, 0, NULL); + } // If there's a simkey pending, and the game wants a keyboard event, use the // simkey instead of a normal event if (g_debug_simulated_key && (mask & SCI_EVENT_KEYBOARD)) { + // In case we use a simulated event we query the current mouse position + mousePos = g_sci->_gfxCursor->getPosition(); +#ifdef ENABLE_SCI32 + if (getSciVersion() >= SCI_VERSION_2_1) + g_sci->_gfxCoordAdjuster->fromDisplayToScript(mousePos.y, mousePos.x); +#endif + // Limit the mouse cursor position, if necessary + g_sci->_gfxCursor->refreshPosition(); + writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event writeSelectorValue(segMan, obj, SELECTOR(message), g_debug_simulated_key); writeSelectorValue(segMan, obj, SELECTOR(modifiers), SCI_KEYMOD_NUMLOCK); // Numlock on @@ -68,6 +77,15 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { curEvent = g_sci->getEventManager()->getSciEvent(mask); + // For a real event we use its associated mouse position + mousePos = curEvent.mousePos; +#ifdef ENABLE_SCI32 + if (getSciVersion() >= SCI_VERSION_2_1) + g_sci->_gfxCoordAdjuster->fromDisplayToScript(mousePos.y, mousePos.x); +#endif + // Limit the mouse cursor position, if necessary + g_sci->_gfxCursor->refreshPosition(); + if (g_sci->getVocabulary()) g_sci->getVocabulary()->parser_event = NULL_REG; // Invalidate parser event @@ -135,7 +153,11 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { break; default: - s->r_acc = NULL_REG; // Unknown or no event + // Return a null event + writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_NONE); + writeSelectorValue(segMan, obj, SELECTOR(message), 0); + writeSelectorValue(segMan, obj, SELECTOR(modifiers), curEvent.modifiers & modifier_mask); + s->r_acc = NULL_REG; } if ((s->r_acc.offset) && (g_sci->_debugState.stopOnEvent)) { diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp index f6dec5da64..82522a6e77 100644 --- a/engines/sci/engine/kfile.cpp +++ b/engines/sci/engine/kfile.cpp @@ -253,6 +253,7 @@ reg_t kFGets(EngineState *s, int argc, reg_t *argv) { debugC(kDebugLevelFile, "kFGets(%d, %d)", handle, maxsize); int readBytes = fgets_wrapper(s, buf, maxsize, handle); s->_segMan->memcpy(argv[0], (const byte*)buf, maxsize); + delete[] buf; return readBytes ? argv[0] : NULL_REG; } @@ -1155,15 +1156,14 @@ reg_t kCD(EngineState *s, int argc, reg_t *argv) { reg_t kSave(EngineState *s, int argc, reg_t *argv) { switch (argv[0].toUint16()) { - case 0: // Called by kq7 when starting chapters + case 0: return kSaveGame(s, argc - 1,argv + 1); - case 2: // GetSaveDir - // Yay! Reusing the old kernel function! + case 1: + return kRestoreGame(s, argc - 1,argv + 1); + case 2: return kGetSaveDir(s, argc - 1, argv + 1); case 5: - // TODO - // 3 parameters: game ID, a string and an array - return s->r_acc; + return kGetSaveFiles(s, argc - 1, argv + 1); case 8: // TODO // This is a timer callback, with 1 parameter: the timer object diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index 9034bd1d5c..c7a2a26c3d 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -58,6 +58,17 @@ namespace Sci { +static int16 adjustGraphColor(int16 color) { + // WORKAROUND: SCI1 EGA and Amiga games can set invalid colors (above 0 - 15). + // Colors above 15 are all white in SCI1 EGA games, which is why this was never + // observed. We clip them all to (0, 15) instead, as colors above 15 are used + // for the undithering algorithm in EGA games - bug #3048908. + if (getSciVersion() >= SCI_VERSION_1_EARLY && g_sci->getResMan()->getViewType() == kViewEga) + return color & 0x0F; // 0 - 15 + else + return color; +} + void showScummVMDialog(const Common::String &message) { GUI::MessageDialog dialog(message, "OK"); dialog.runModal(); @@ -242,23 +253,14 @@ reg_t kGraph(EngineState *s, int argc, reg_t *argv) { } reg_t kGraphGetColorCount(EngineState *s, int argc, reg_t *argv) { - if (g_sci->getResMan()->isAmiga32color()) - return make_reg(0, 32); - return make_reg(0, !g_sci->getResMan()->isVGA() ? 16 : 256); + return make_reg(0, g_sci->_gfxPalette->getTotalColorCount()); } reg_t kGraphDrawLine(EngineState *s, int argc, reg_t *argv) { - int16 color = argv[4].toSint16(); + int16 color = adjustGraphColor(argv[4].toSint16()); int16 priority = (argc > 5) ? argv[5].toSint16() : -1; int16 control = (argc > 6) ? argv[6].toSint16() : -1; - // WORKAROUND: SCI1 EGA games can set invalid colors (above 0 - 15). - // Colors above 15 are all white in SCI1 EGA games, which is why this was never - // observed. We clip them all to (0, 15) instead, as colors above 15 are used - // for the undithering algorithm in EGA games - bug #3048908. - if (g_sci->getResMan()->getViewType() == kViewEga && getSciVersion() >= SCI_VERSION_1_EARLY) - color &= 0x0F; - g_sci->_gfxPaint16->kernelGraphDrawLine(getGraphPoint(argv), getGraphPoint(argv + 2), color, priority, control); return s->r_acc; } @@ -290,17 +292,10 @@ reg_t kGraphFillBoxForeground(EngineState *s, int argc, reg_t *argv) { reg_t kGraphFillBoxAny(EngineState *s, int argc, reg_t *argv) { Common::Rect rect = getGraphRect(argv); int16 colorMask = argv[4].toUint16(); - int16 color = argv[5].toSint16(); + int16 color = adjustGraphColor(argv[5].toSint16()); int16 priority = argv[6].toSint16(); // yes, we may read from stack sometimes here int16 control = argv[7].toSint16(); // sierra did the same - // WORKAROUND: SCI1 EGA games can set invalid colors (above 0 - 15). - // Colors above 15 are all white in SCI1 EGA games, which is why this was never - // observed. We clip them all to (0, 15) instead, as colors above 15 are used - // for the undithering algorithm in EGA games - bug #3048908. - if (g_sci->getResMan()->getViewType() == kViewEga && getSciVersion() >= SCI_VERSION_1_EARLY) - color &= 0x0F; - g_sci->_gfxPaint16->kernelGraphFillBox(rect, colorMask, color, priority, control); return s->r_acc; } @@ -364,6 +359,7 @@ reg_t kTextSize(EngineState *s, int argc, reg_t *argv) { if (!g_sci->_gfxText16) { // TODO: Implement this textWidth = 0; textHeight = 0; + warning("TODO: implement kTextSize for SCI32"); } else #endif g_sci->_gfxText16->kernelTextSize(g_sci->strSplit(text.c_str(), sep).c_str(), font_nr, maxwidth, &textWidth, &textHeight); @@ -553,8 +549,6 @@ reg_t kSetNowSeen(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } -// we are called on EGA/amiga games as well, this doesnt make sense. -// doing this would actually break the system EGA/amiga palette reg_t kPalette(EngineState *s, int argc, reg_t *argv) { if (!s) return make_reg(0, getSciVersion()); @@ -562,86 +556,85 @@ reg_t kPalette(EngineState *s, int argc, reg_t *argv) { } reg_t kPaletteSetFromResource(EngineState *s, int argc, reg_t *argv) { - if (g_sci->getResMan()->isVGA()) { - GuiResourceId resourceId = argv[0].toUint16(); - bool force = false; - if (argc == 2) - force = argv[1].toUint16() == 2 ? true : false; - g_sci->_gfxPalette->kernelSetFromResource(resourceId, force); - } + GuiResourceId resourceId = argv[0].toUint16(); + bool force = false; + if (argc == 2) + force = argv[1].toUint16() == 2 ? true : false; + + // Non-VGA games don't use palette resources. + // This has been changed to 64 colors because Longbow Amiga does have + // one palette (palette 999). + if (g_sci->_gfxPalette->getTotalColorCount() < 64) + return s->r_acc; + + g_sci->_gfxPalette->kernelSetFromResource(resourceId, force); return s->r_acc; } reg_t kPaletteSetFlag(EngineState *s, int argc, reg_t *argv) { - if (g_sci->getResMan()->isVGA()) { - uint16 fromColor = CLIP<uint16>(argv[0].toUint16(), 1, 255); - uint16 toColor = CLIP<uint16>(argv[1].toUint16(), 1, 255); - uint16 flags = argv[2].toUint16(); - g_sci->_gfxPalette->kernelSetFlag(fromColor, toColor, flags); - } + uint16 fromColor = CLIP<uint16>(argv[0].toUint16(), 1, 255); + uint16 toColor = CLIP<uint16>(argv[1].toUint16(), 1, 255); + uint16 flags = argv[2].toUint16(); + g_sci->_gfxPalette->kernelSetFlag(fromColor, toColor, flags); return s->r_acc; } reg_t kPaletteUnsetFlag(EngineState *s, int argc, reg_t *argv) { - if (g_sci->getResMan()->isVGA()) { - uint16 fromColor = CLIP<uint16>(argv[0].toUint16(), 1, 255); - uint16 toColor = CLIP<uint16>(argv[1].toUint16(), 1, 255); - uint16 flags = argv[2].toUint16(); - g_sci->_gfxPalette->kernelUnsetFlag(fromColor, toColor, flags); - } + uint16 fromColor = CLIP<uint16>(argv[0].toUint16(), 1, 255); + uint16 toColor = CLIP<uint16>(argv[1].toUint16(), 1, 255); + uint16 flags = argv[2].toUint16(); + g_sci->_gfxPalette->kernelUnsetFlag(fromColor, toColor, flags); return s->r_acc; } reg_t kPaletteSetIntensity(EngineState *s, int argc, reg_t *argv) { - if (g_sci->getResMan()->isVGA()) { - uint16 fromColor = CLIP<uint16>(argv[0].toUint16(), 1, 255); - uint16 toColor = CLIP<uint16>(argv[1].toUint16(), 1, 255); - uint16 intensity = argv[2].toUint16(); - bool setPalette = (argc < 4) ? true : (argv[3].isNull()) ? true : false; + uint16 fromColor = CLIP<uint16>(argv[0].toUint16(), 1, 255); + uint16 toColor = CLIP<uint16>(argv[1].toUint16(), 1, 255); + uint16 intensity = argv[2].toUint16(); + bool setPalette = (argc < 4) ? true : (argv[3].isNull()) ? true : false; - g_sci->_gfxPalette->kernelSetIntensity(fromColor, toColor, intensity, setPalette); - } + // Palette intensity in non-VGA SCI1 games has been removed + if (g_sci->_gfxPalette->getTotalColorCount() < 256) + return s->r_acc; + + g_sci->_gfxPalette->kernelSetIntensity(fromColor, toColor, intensity, setPalette); return s->r_acc; } reg_t kPaletteFindColor(EngineState *s, int argc, reg_t *argv) { - if (g_sci->getResMan()->isVGA()) { - uint16 r = argv[0].toUint16(); - uint16 g = argv[1].toUint16(); - uint16 b = argv[2].toUint16(); - return make_reg(0, g_sci->_gfxPalette->kernelFindColor(r, g, b)); - } - return NULL_REG; + uint16 r = argv[0].toUint16(); + uint16 g = argv[1].toUint16(); + uint16 b = argv[2].toUint16(); + return make_reg(0, g_sci->_gfxPalette->kernelFindColor(r, g, b)); } reg_t kPaletteAnimate(EngineState *s, int argc, reg_t *argv) { - if (g_sci->getResMan()->isVGA()) { - int16 argNr; - bool paletteChanged = false; - for (argNr = 0; argNr < argc; argNr += 3) { - uint16 fromColor = argv[argNr].toUint16(); - uint16 toColor = argv[argNr + 1].toUint16(); - int16 speed = argv[argNr + 2].toSint16(); - if (g_sci->_gfxPalette->kernelAnimate(fromColor, toColor, speed)) - paletteChanged = true; - } - if (paletteChanged) - g_sci->_gfxPalette->kernelAnimateSet(); + int16 argNr; + bool paletteChanged = false; + + // Palette animation in non-VGA SCI1 games has been removed + if (g_sci->_gfxPalette->getTotalColorCount() < 256) + return s->r_acc; + + for (argNr = 0; argNr < argc; argNr += 3) { + uint16 fromColor = argv[argNr].toUint16(); + uint16 toColor = argv[argNr + 1].toUint16(); + int16 speed = argv[argNr + 2].toSint16(); + if (g_sci->_gfxPalette->kernelAnimate(fromColor, toColor, speed)) + paletteChanged = true; } + if (paletteChanged) + g_sci->_gfxPalette->kernelAnimateSet(); + return s->r_acc; } reg_t kPaletteSave(EngineState *s, int argc, reg_t *argv) { - if (g_sci->getResMan()->isVGA()) { - return g_sci->_gfxPalette->kernelSave(); - } - return NULL_REG; + return g_sci->_gfxPalette->kernelSave(); } reg_t kPaletteRestore(EngineState *s, int argc, reg_t *argv) { - if (g_sci->getResMan()->isVGA()) { - g_sci->_gfxPalette->kernelRestore(argv[0]); - } + g_sci->_gfxPalette->kernelRestore(argv[0]); return argv[0]; } @@ -1082,22 +1075,11 @@ reg_t kNewWindow(EngineState *s, int argc, reg_t *argv) { int argextra = argc >= 13 ? 4 : 0; // Triggers in PQ3 and SCI1.1 games, argc 13 for DOS argc 15 for mac int style = argv[5 + argextra].toSint16(); int priority = (argc > 6 + argextra) ? argv[6 + argextra].toSint16() : -1; - int colorPen = (argc > 7 + argextra) ? argv[7 + argextra].toSint16() : 0; - int colorBack = (argc > 8 + argextra) ? argv[8 + argextra].toSint16() : 255; + int colorPen = adjustGraphColor((argc > 7 + argextra) ? argv[7 + argextra].toSint16() : 0); + int colorBack = adjustGraphColor((argc > 8 + argextra) ? argv[8 + argextra].toSint16() : 255); - // WORKAROUND: SCI1 EGA games can set invalid colors (above 0 - 15). - // Colors above 15 are all white in SCI1 EGA games, which is why this was never - // observed. We clip them all to (0, 15) instead, as colors above 15 are used - // for the undithering algorithm in EGA games - bug #3048908. - if (g_sci->getResMan()->getViewType() == kViewEga && getSciVersion() >= SCI_VERSION_1_EARLY) { - colorPen &= 0x0F; - colorBack &= 0x0F; - } - - // const char *title = argv[4 + argextra].segment ? kernel_dereference_char_pointer(s, argv[4 + argextra], 0) : NULL; - if (argc>=13) { + if (argc >= 13) rect2 = Common::Rect (argv[5].toSint16(), argv[4].toSint16(), argv[7].toSint16(), argv[6].toSint16()); - } Common::String title; if (argv[4 + argextra].segment) { @@ -1220,12 +1202,19 @@ reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) { } break; case 2: { // remap by percent - // NOTE: This adjusts the alpha value of a specific color, and it operates on - // an RGBA palette - int16 color = argv[1].toSint16(); // this is subtracted from a maximum color value, and can be offset by 10 + // This adjusts the alpha value of a specific color, and it operates on + // an RGBA palette. Since we're operating on an RGB palette, we just + // modify the color intensity instead + // TODO: From what I understand, palette remapping should be placed + // separately, so that it can be reset by case 0 above. Thus, we + // should adjust the functionality of the Palette class accordingly. + int16 color = argv[1].toSint16(); + if (color >= 10) + color -= 10; uint16 percent = argv[2].toUint16(); // 0 - 100 - uint16 unk3 = (argc >= 4) ? argv[3].toUint16() : 0; - warning("kRemapColors: RemapByPercent color %d by %d percent (unk3 = %d)", color, percent, unk3); + if (argc >= 4) + warning("RemapByPercent called with 4 parameters, unknown parameter is %d", argv[3].toUint16()); + g_sci->_gfxPalette->kernelSetIntensity(color, 255, percent, false); } break; case 3: { // remap to gray diff --git a/engines/sci/engine/kmenu.cpp b/engines/sci/engine/kmenu.cpp index 428c27ca73..3986966a71 100644 --- a/engines/sci/engine/kmenu.cpp +++ b/engines/sci/engine/kmenu.cpp @@ -29,6 +29,7 @@ #include "sci/engine/kernel.h" #include "sci/graphics/cursor.h" #include "sci/graphics/menu.h" +#include "sci/graphics/screen.h" namespace Sci { @@ -71,7 +72,7 @@ reg_t kDrawStatus(EngineState *s, int argc, reg_t *argv) { reg_t textReference = argv[0]; Common::String text; int16 colorPen = (argc > 1) ? argv[1].toSint16() : 0; - int16 colorBack = (argc > 2) ? argv[2].toSint16() : g_sci->getResMan()->isVGA() ? 255 : 15; + int16 colorBack = (argc > 2) ? argv[2].toSint16() : g_sci->_gfxScreen->getColorWhite(); if (!textReference.isNull()) { // Sometimes this is called without giving text, if thats the case dont process it. diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp index 6d7c4580e6..723aece819 100644 --- a/engines/sci/engine/kmisc.cpp +++ b/engines/sci/engine/kmisc.cpp @@ -298,12 +298,9 @@ reg_t kMemory(EngineState *s, int argc, reg_t *argv) { error("Attempt to peek invalid memory at %04x:%04x", PRINT_REG(argv[1])); return s->r_acc; } - if (ref.isRaw) { - if (g_sci->getPlatform() == Common::kPlatformAmiga) - return make_reg(0, (int16)READ_BE_UINT16(ref.raw)); // Amiga versions are BE - else - return make_reg(0, (int16)READ_LE_UINT16(ref.raw)); - } else { + if (ref.isRaw) + return make_reg(0, (int16)READ_SCIENDIAN_UINT16(ref.raw)); + else { if (ref.skipByte) error("Attempt to peek memory at odd offset %04X:%04X", PRINT_REG(argv[1])); return *(ref.reg); @@ -323,10 +320,7 @@ reg_t kMemory(EngineState *s, int argc, reg_t *argv) { error("Attempt to poke memory reference %04x:%04x to %04x:%04x", PRINT_REG(argv[2]), PRINT_REG(argv[1])); return s->r_acc; } - if (g_sci->getPlatform() == Common::kPlatformAmiga) - WRITE_BE_UINT16(ref.raw, argv[2].offset); // Amiga versions are BE - else - WRITE_LE_UINT16(ref.raw, argv[2].offset); + WRITE_SCIENDIAN_UINT16(ref.raw, argv[2].offset); // Amiga versions are BE } else { if (ref.skipByte) error("Attempt to poke memory at odd offset %04X:%04X", PRINT_REG(argv[1])); @@ -365,23 +359,22 @@ reg_t kIconBar(EngineState *s, int argc, reg_t *argv) { case 0: // InitIconBar for (int i = 0; i < argv[1].toUint16(); i++) g_sci->_gfxMacIconBar->addIcon(argv[i + 2]); - - // TODO: Should return icon bar handle - // Said handle is then used by DisposeIconBar break; case 1: // DisposeIconBar warning("kIconBar(Dispose)"); break; - case 2: // EnableIconBar (0xffff = all) - debug(0, "kIconBar(Enable, %d)", argv[1].toUint16()); - g_sci->_gfxMacIconBar->setIconEnabled(argv[1].toUint16(), true); + case 2: // EnableIconBar (-1 = all) + debug(0, "kIconBar(Enable, %i)", argv[1].toSint16()); + g_sci->_gfxMacIconBar->setIconEnabled(argv[1].toSint16(), true); break; - case 3: // DisableIconBar (0xffff = all) - debug(0, "kIconBar(Disable, %d)", argv[1].toUint16()); - g_sci->_gfxMacIconBar->setIconEnabled(argv[1].toUint16(), false); + case 3: // DisableIconBar (-1 = all) + debug(0, "kIconBar(Disable, %i)", argv[1].toSint16()); + g_sci->_gfxMacIconBar->setIconEnabled(argv[1].toSint16(), false); break; case 4: // SetIconBarIcon - warning("kIconBar(SetIcon, %d, %d)", argv[1].toUint16(), argv[2].toUint16()); + debug(0, "kIconBar(SetIcon, %d, %d)", argv[1].toUint16(), argv[2].toUint16()); + if (argv[2].toSint16() == -1) + g_sci->_gfxMacIconBar->setInventoryIcon(argv[2].toSint16()); break; default: error("Unknown kIconBar(%d)", argv[0].toUint16()); @@ -503,7 +496,7 @@ reg_t kStub(EngineState *s, int argc, reg_t *argv) { Kernel *kernel = g_sci->getKernel(); int kernelCallNr = -1; - Common::List<ExecStack>::iterator callIterator = s->_executionStack.end(); + Common::List<ExecStack>::const_iterator callIterator = s->_executionStack.end(); if (callIterator != s->_executionStack.begin()) { callIterator--; ExecStack lastCall = *callIterator; diff --git a/engines/sci/engine/kmovement.cpp b/engines/sci/engine/kmovement.cpp index 3c516f63f2..49d2900835 100644 --- a/engines/sci/engine/kmovement.cpp +++ b/engines/sci/engine/kmovement.cpp @@ -166,17 +166,6 @@ reg_t kSetJump(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } -// TODO/FIXME: There is a notable regression with the new kInitBresed/kDoBresen -// functions below in a death scene of LB1 - the shower scene, room 215 (bug -// #3122075). There is a hack to get around this bug by modifying the actor's -// position for that scene in kScriptID. The actual bug should be found, but -// since only this death scene has an issue, it's not really worth the effort. -// The new kInitBresen/kDoBresen functions have been enabled in r52467. The -// old ones are based on observations, so there are many differences in the -// way that they behave. Check the hack in kScriptID for more info. Note that -// the actual issue might not be with kInitBresen/kDoBresen, and there might -// be another underlying problem here. - reg_t kInitBresen(EngineState *s, int argc, reg_t *argv) { SegManager *segMan = s->_segMan; reg_t mover = argv[0]; @@ -275,7 +264,7 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) { bool completed = false; bool handleMoveCount = g_sci->_features->handleMoveCount(); - if (getSciVersion() >= SCI_VERSION_1_EGA) { + if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY) { uint client_signal = readSelectorValue(segMan, client, SELECTOR(signal)); writeSelectorValue(segMan, client, SELECTOR(signal), client_signal & ~kSignalHitObstacle); } @@ -292,8 +281,6 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) { mover_moveCnt = 0; int16 client_x = readSelectorValue(segMan, client, SELECTOR(x)); int16 client_y = readSelectorValue(segMan, client, SELECTOR(y)); - int16 client_org_x = client_x; - int16 client_org_y = client_y; int16 mover_x = readSelectorValue(segMan, mover, SELECTOR(x)); int16 mover_y = readSelectorValue(segMan, mover, SELECTOR(y)); int16 mover_xAxis = readSelectorValue(segMan, mover, SELECTOR(b_xAxis)); @@ -307,12 +294,19 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) { int16 mover_org_i2 = mover_i2; int16 mover_org_di = mover_di; - if ((getSciVersion() >= SCI_VERSION_1_EGA)) { + if ((getSciVersion() >= SCI_VERSION_1_EGA_ONLY)) { // save current position into mover writeSelectorValue(segMan, mover, SELECTOR(xLast), client_x); writeSelectorValue(segMan, mover, SELECTOR(yLast), client_y); } - // sierra sci saves full client selector variables here + + // Store backups of all client selector variables. We will restore them + // in case of a collision. + Object* clientObject = segMan->getObject(client); + uint clientVarNum = clientObject->getVarCount(); + reg_t* clientBackup = new reg_t[clientVarNum]; + for (uint i = 0; i < clientVarNum; ++i) + clientBackup[i] = clientObject->getVariable(i); if (mover_xAxis) { if (ABS(mover_x - client_x) < ABS(mover_dx)) @@ -360,9 +354,10 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) { } if (collision) { - // sierra restores full client variables here, seems that restoring x/y is enough - writeSelectorValue(segMan, client, SELECTOR(x), client_org_x); - writeSelectorValue(segMan, client, SELECTOR(y), client_org_y); + // We restore the backup of the client variables + for (uint i = 0; i < clientVarNum; ++i) + clientObject->getVariableRef(i) = clientBackup[i]; + mover_i1 = mover_org_i1; mover_i2 = mover_org_i2; mover_di = mover_org_di; @@ -370,30 +365,29 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) { uint16 client_signal = readSelectorValue(segMan, client, SELECTOR(signal)); writeSelectorValue(segMan, client, SELECTOR(signal), client_signal | kSignalHitObstacle); } + delete[] clientBackup; + writeSelectorValue(segMan, mover, SELECTOR(b_i1), mover_i1); writeSelectorValue(segMan, mover, SELECTOR(b_i2), mover_i2); writeSelectorValue(segMan, mover, SELECTOR(b_di), mover_di); - if ((getSciVersion() >= SCI_VERSION_1_EGA)) { - // this calling code here was right before the last return in - // sci1ega and got changed to this position since sci1early - // this was an uninitialized issue in sierra sci - if ((handleMoveCount) && (getSciVersion() >= SCI_VERSION_1_EARLY)) + if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY) { + // In sci1egaonly this block of code was outside of the main if, + // but client_x/client_y aren't set there, so it was an + // uninitialized read in SSCI. (This issue was fixed in sci1early.) + if (handleMoveCount) writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), mover_moveCnt); // We need to compare directly in here, complete may have happened during // the current move if ((client_x == mover_x) && (client_y == mover_y)) invokeSelector(s, mover, SELECTOR(moveDone), argc, argv); - if (getSciVersion() >= SCI_VERSION_1_EARLY) - return s->r_acc; + return s->r_acc; } } - if (handleMoveCount) { - if (getSciVersion() <= SCI_VERSION_1_EGA) - writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), mover_moveCnt); - else - writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), client_moveSpeed); - } + + if (handleMoveCount) + writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), mover_moveCnt); + return s->r_acc; } diff --git a/engines/sci/engine/kparse.cpp b/engines/sci/engine/kparse.cpp index e8f8ee7152..09cf7744b2 100644 --- a/engines/sci/engine/kparse.cpp +++ b/engines/sci/engine/kparse.cpp @@ -70,7 +70,7 @@ reg_t kSaid(EngineState *s, int argc, reg_t *argv) { return NULL_REG; } - new_lastmatch = said(s, said_block, debug_parser); + new_lastmatch = said(said_block, debug_parser); if (new_lastmatch != SAID_NO_MATCH) { /* Build and possibly display a parse tree */ #ifdef DEBUG_PARSER @@ -169,7 +169,7 @@ reg_t kSetSynonyms(EngineState *s, int argc, reg_t *argv) { Vocabulary *voc = g_sci->getVocabulary(); // Only SCI0-SCI1 EGA games had a parser. In newer versions, this is a stub - if (getSciVersion() > SCI_VERSION_1_EGA) + if (getSciVersion() > SCI_VERSION_1_EGA_ONLY) return s->r_acc; voc->clearSynonyms(); diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp index cb70cf91e0..7786f9b093 100644 --- a/engines/sci/engine/kpathing.cpp +++ b/engines/sci/engine/kpathing.cpp @@ -264,9 +264,9 @@ struct PathfindingState { static Common::Point readPoint(SegmentRef list_r, int offset) { Common::Point point; - if (list_r.isRaw) { - point.x = (int16)READ_LE_UINT16(list_r.raw + offset * POLY_POINT_SIZE); - point.y = (int16)READ_LE_UINT16(list_r.raw + offset * POLY_POINT_SIZE + 2); + if (list_r.isRaw) { // dynmem blocks are raw + point.x = (int16)READ_SCIENDIAN_UINT16(list_r.raw + offset * POLY_POINT_SIZE); + point.y = (int16)READ_SCIENDIAN_UINT16(list_r.raw + offset * POLY_POINT_SIZE + 2); } else { point.x = list_r.reg[offset * 2].toUint16(); point.y = list_r.reg[offset * 2 + 1].toUint16(); @@ -275,9 +275,9 @@ static Common::Point readPoint(SegmentRef list_r, int offset) { } static void writePoint(SegmentRef ref, int offset, const Common::Point &point) { - if (ref.isRaw) { - WRITE_LE_UINT16(ref.raw + offset * POLY_POINT_SIZE, point.x); - WRITE_LE_UINT16(ref.raw + offset * POLY_POINT_SIZE + 2, point.y); + if (ref.isRaw) { // dynmem blocks are raw + WRITE_SCIENDIAN_UINT16(ref.raw + offset * POLY_POINT_SIZE, point.x); + WRITE_SCIENDIAN_UINT16(ref.raw + offset * POLY_POINT_SIZE + 2, point.y); } else { ref.reg[offset * 2] = make_reg(0, point.x); ref.reg[offset * 2 + 1] = make_reg(0, point.y); diff --git a/engines/sci/engine/kscripts.cpp b/engines/sci/engine/kscripts.cpp index 810e8a13ee..b9baa3540a 100644 --- a/engines/sci/engine/kscripts.cpp +++ b/engines/sci/engine/kscripts.cpp @@ -56,14 +56,6 @@ reg_t kUnLoad(EngineState *s, int argc, reg_t *argv) { ResourceType restype = g_sci->getResMan()->convertResType(argv[0].toUint16()); reg_t resnr = argv[1]; - // WORKAROUND for a broken script in room 320 in Castle of Dr. Brain. - // Script 377 tries to free the hunk memory allocated for the saved area - // (underbits) beneath the pop up window, which results in having the - // window stay on screen even when it's closed. Ignore this request here. - if (restype == kResourceTypeMemory && g_sci->getGameId() == GID_CASTLEBRAIN && - s->currentRoomNumber() == 320) - return s->r_acc; - if (restype == kResourceTypeMemory) s->_segMan->freeHunkEntry(resnr); } @@ -257,33 +249,9 @@ reg_t kScriptID(EngineState *s, int argc, reg_t *argv) { // is used for timing during the intro, and in the problematic version it's // initialized to 0, whereas it's 6 in other versions. Thus, we assign it // to 6 here, fixing the speed of the introduction. Refer to bug #3102071. - if (g_sci->getGameId() == GID_PQ2 && script == 200) { - if (s->variables[VAR_GLOBAL][3].isNull()) { - warning("Fixing speed in the intro of PQ2, version 1.002.011"); - s->variables[VAR_GLOBAL][3] = make_reg(0, 6); - } - } - - // HACK: Prevent the murderer from getting stuck behind the door in - // Colonel's Bequest, room 215. A temporary fix for bug #3122075. - // TODO/FIXME: Add a proper fix for this. There is a regression in this - // scene with the new kInitBresen and kDoBresen functions (r52467). Using - // just the "old" kInitBresen works. This hack is added for now because the - // two functions are quite complex. The "old" versions were created based - // on observations, and not on the interpreter itself, thus they have a lot - // of differences in the way they behave and set variables to the mover object. - // Since this is just a death scene where Laura is supposed to die anyway, - // figuring out the exact cause of this is just not worth the effort. - // Differences between the new and the old kInitBresen to the MoveTo object: - // dy: 1 (new) - 2 (old) - // b-i1: 20 (new) - 12 (old) - // b-di: 65526 (new) - 65516 (old) - // Performing the changes above to MoveTo (0017:033a) allows the killer to - // move. Note that the actual issue might not be with kInitBresen/kDoBresen, - // and there might be another underlying problem here. - if (g_sci->getGameId() == GID_LAURABOW && script == 215) { - warning("Moving actor position for the shower scene of Colonel's Bequest"); - writeSelectorValue(s->_segMan, s->_segMan->findObjectByName("killer"), SELECTOR(x), 6); + if (g_sci->getGameId() == GID_PQ2 && script == 200 && + s->variables[VAR_GLOBAL][3].isNull()) { + s->variables[VAR_GLOBAL][3] = make_reg(0, 6); } return make_reg(scriptSeg, address); diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp index 5c6ef06910..d9bb1c3531 100644 --- a/engines/sci/engine/kstring.cpp +++ b/engines/sci/engine/kstring.cpp @@ -113,7 +113,7 @@ reg_t kStrAt(EngineState *s, int argc, reg_t *argv) { reg_t &tmp = dest_r.reg[offset / 2]; bool oddOffset = offset & 1; - if (g_sci->getPlatform() == Common::kPlatformAmiga) + if (g_sci->isBE()) oddOffset = !oddOffset; if (!oddOffset) { @@ -159,17 +159,9 @@ reg_t kReadNumber(EngineState *s, int argc, reg_t *argv) { source++; } while (*source) { - if ((*source < '0') || (*source > '9')) { - // Sierra's atoi stopped processing at anything which is not - // a digit. Sometimes the input has a trailing space, that's - // fine (example: lsl3) - if (*source != ' ') { - // TODO: this happens in lsl5 right in the intro -> we get '1' '3' 0xCD 0xCD 0xCD 0xCD 0xCD - // find out why this happens and fix it - warning("Invalid character in kReadNumber input"); - } + if ((*source < '0') || (*source > '9')) + // Stop if we encounter anything other than a digit (like atoi) break; - } result *= 10; result += *source - 0x30; source++; @@ -198,7 +190,6 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) { char targetbuf[4096]; char *target = targetbuf; reg_t position = argv[1]; /* source */ - int index = argv[2].toUint16(); int mode = 0; int paramindex = 0; /* Next parameter to evaluate */ char xfer; @@ -209,9 +200,16 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) { if (position.segment) startarg = 2; - else + else { + // WORKAROUND: QFG1 VGA Mac calls this without the first parameter (dest). It then + // treats the source as the dest and overwrites the source string with an empty string. + if (argc < 3) + return NULL_REG; + startarg = 3; /* First parameter to use for formatting */ + } + int index = (startarg == 3) ? argv[2].toUint16() : 0; Common::String source_str = g_sci->getKernel()->lookupText(position, index); const char* source = source_str.c_str(); diff --git a/engines/sci/engine/object.cpp b/engines/sci/engine/object.cpp index bc79e30129..267ba35e3c 100644 --- a/engines/sci/engine/object.cpp +++ b/engines/sci/engine/object.cpp @@ -172,11 +172,73 @@ bool Object::initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClas const Object *baseObj = segMan->getObject(getSpeciesSelector()); if (baseObj) { - _variables.resize(baseObj->getVarCount()); + uint originalVarCount = _variables.size(); + + if (_variables.size() != baseObj->getVarCount()) + _variables.resize(baseObj->getVarCount()); // Copy base from species class, as we need its selector IDs _baseObj = baseObj->_baseObj; if (doInitSuperClass) initSuperClass(segMan, addr); + + if (_variables.size() != originalVarCount) { + // These objects are probably broken. + // An example is 'witchCage' in script 200 in KQ5 (#3034714), + // but also 'girl' in script 216 and 'door' in script 22. + // In LSL3 a number of sound objects trigger this right away. + // SQ4-floppy's bug #3037938 also seems related. + + // The effect is that a number of its method selectors may be + // treated as variable selectors, causing unpredictable effects. + int objScript = segMan->getScript(_pos.segment)->getScriptNumber(); + + // We have to do a little bit of work to get the name of the object + // before any relocations are done. + reg_t nameReg = getNameSelector(); + const char *name; + if (nameReg.isNull()) { + name = "<no name>"; + } else { + nameReg.segment = _pos.segment; + name = segMan->derefString(nameReg); + if (!name) + name = "<invalid name>"; + } + + warning("Object %04x:%04x (name %s, script %d) varnum doesn't " + "match baseObj's: obj %d, base %d", PRINT_REG(_pos), + name, objScript, originalVarCount, baseObj->getVarCount()); + +#if 0 + // We enumerate the methods selectors which could be hidden here + if (getSciVersion() <= SCI_VERSION_2_1) { + const SegmentRef objRef = segMan->dereference(baseObj->_pos); + assert(objRef.isRaw); + uint segBound = objRef.maxSize/2 - baseObj->getVarCount(); + const byte* buf = (const byte *)baseObj->_baseVars; + if (!buf) { + // While loading this may happen due to objects being loaded + // out of order, and we can't proceed then, unfortunately. + segBound = 0; + } + for (uint i = baseObj->getVarCount(); + i < originalVarCount && i < segBound; ++i) { + uint16 slc = READ_SCI11ENDIAN_UINT16(buf + 2*i); + // Skip any numbers which happen to be varselectors too + bool found = false; + for (uint j = 0; j < baseObj->getVarCount() && !found; ++j) + found = READ_SCI11ENDIAN_UINT16(buf + 2*j) == slc; + if (found) continue; + // Skip any selectors which aren't method selectors, + // so couldn't be mistaken for varselectors + if (lookupSelector(segMan, _pos, slc, 0, 0) != kSelectorMethod) continue; + warning(" Possibly affected selector: %02x (%s)", slc, + g_sci->getKernel()->getSelectorName(slc).c_str()); + } + } +#endif + } + return true; } diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index ab355cebb4..43d00ebc15 100644 --- a/engines/sci/engine/savegame.cpp +++ b/engines/sci/engine/savegame.cpp @@ -328,14 +328,14 @@ void Object::saveLoadWithSerializer(Common::Serializer &s) { } template <> -void syncWithSerializer(Common::Serializer &s, Table<Clone>::Entry &obj) { +void syncWithSerializer(Common::Serializer &s, SegmentObjTable<Clone>::Entry &obj) { s.syncAsSint32LE(obj.next_free); syncWithSerializer<Object>(s, obj); } template <> -void syncWithSerializer(Common::Serializer &s, Table<List>::Entry &obj) { +void syncWithSerializer(Common::Serializer &s, SegmentObjTable<List>::Entry &obj) { s.syncAsSint32LE(obj.next_free); syncWithSerializer(s, obj.first); @@ -343,7 +343,7 @@ void syncWithSerializer(Common::Serializer &s, Table<List>::Entry &obj) { } template <> -void syncWithSerializer(Common::Serializer &s, Table<Node>::Entry &obj) { +void syncWithSerializer(Common::Serializer &s, SegmentObjTable<Node>::Entry &obj) { s.syncAsSint32LE(obj.next_free); syncWithSerializer(s, obj.pred); @@ -354,7 +354,7 @@ void syncWithSerializer(Common::Serializer &s, Table<Node>::Entry &obj) { #ifdef ENABLE_SCI32 template <> -void syncWithSerializer(Common::Serializer &s, Table<SciArray<reg_t> >::Entry &obj) { +void syncWithSerializer(Common::Serializer &s, SegmentObjTable<SciArray<reg_t> >::Entry &obj) { s.syncAsSint32LE(obj.next_free); byte type = 0; @@ -390,7 +390,7 @@ void syncWithSerializer(Common::Serializer &s, Table<SciArray<reg_t> >::Entry &o } template <> -void syncWithSerializer(Common::Serializer &s, Table<SciString>::Entry &obj) { +void syncWithSerializer(Common::Serializer &s, SegmentObjTable<SciString>::Entry &obj) { s.syncAsSint32LE(obj.next_free); uint32 size = 0; diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp index 6719b73aa5..fb96518f19 100644 --- a/engines/sci/engine/script.cpp +++ b/engines/sci/engine/script.cpp @@ -70,7 +70,7 @@ void Script::init(int script_nr, ResourceManager *resMan) { Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0); if (!script) - error("Script %d not found\n", script_nr); + error("Script %d not found", script_nr); _localsOffset = 0; _localsBlock = NULL; @@ -129,6 +129,17 @@ void Script::load(ResourceManager *resMan) { Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, _nr), 0); assert(script != 0); + uint extraLocalsWorkaround = 0; + if (g_sci->getGameId() == GID_FANMADE && _nr == 1 && script->size == 11140) { + // WORKAROUND: Script 1 in Ocean Battle doesn't have enough locals to + // fit the string showing how many shots are left (a nasty script bug, + // corrupting heap memory). We add 10 more locals so that it has enough + // space to use as the target for its kFormat operation. Fixes bug + // #3059871. + extraLocalsWorkaround = 10; + } + _bufSize += extraLocalsWorkaround * 2; + _buf = (byte *)malloc(_bufSize); assert(_buf); @@ -187,6 +198,9 @@ void Script::load(ResourceManager *resMan) { _localsOffset = 24 + _numExports * 2; } + // WORKAROUND: Increase locals, if needed (check above) + _localsCount += extraLocalsWorkaround; + if (getSciVersion() == SCI_VERSION_0_EARLY) { // SCI0 early // Old script block. There won't be a localvar block in this case. @@ -202,7 +216,7 @@ void Script::load(ResourceManager *resMan) { if (_localsOffset + _localsCount * 2 + 1 >= (int)_bufSize) { error("Locals extend beyond end of script: offset %04x, count %d vs size %d", _localsOffset, _localsCount, _bufSize); - _localsCount = (_bufSize - _localsOffset) >> 1; + //_localsCount = (_bufSize - _localsOffset) >> 1; } } } @@ -243,9 +257,8 @@ Object *Script::scriptObjInit(reg_t obj_pos, bool fullObjectInit) { if (getSciVersion() < SCI_VERSION_1_1 && fullObjectInit) obj_pos.offset += 8; // magic offset (SCRIPT_OBJECT_MAGIC_OFFSET) - VERIFY(obj_pos.offset < _bufSize, "Attempt to initialize object beyond end of script\n"); - - VERIFY(obj_pos.offset + kOffsetFunctionArea < (int)_bufSize, "Function area pointer stored beyond end of script\n"); + if (obj_pos.offset >= _bufSize) + error("Attempt to initialize object beyond end of script"); // Get the object at the specified position and init it. This will // automatically "allocate" space for it in the _objects map if necessary. @@ -313,8 +326,9 @@ void Script::relocateSci0Sci21(reg_t block) { heapOffset = _scriptSize; } - VERIFY(block.offset < (uint16)heapSize && READ_SCI11ENDIAN_UINT16(heap + block.offset) * 2 + block.offset < (uint16)heapSize, - "Relocation block outside of script\n"); + if (block.offset >= (uint16)heapSize || + READ_SCI11ENDIAN_UINT16(heap + block.offset) * 2 + block.offset >= (uint16)heapSize) + error("Relocation block outside of script"); int count = READ_SCI11ENDIAN_UINT16(heap + block.offset); int exportIndex = 0; @@ -404,7 +418,8 @@ uint16 Script::validateExportFunc(int pubfunct, bool relocate) { offset = relocateOffsetSci3(pubfunct * 2 + 22); } - VERIFY(offset < _bufSize, "invalid export function pointer"); + if (offset >= _bufSize) + error("Invalid export function pointer"); // Check if the offset found points to a second export table (e.g. script 912 // in Camelot and script 306 in KQ4). Such offsets are usually small (i.e. < 10), @@ -418,7 +433,8 @@ uint16 Script::validateExportFunc(int pubfunct, bool relocate) { if (secondExportTable) { secondExportTable += 3; // skip header plus 2 bytes (secondExportTable is a uint16 pointer) offset = READ_SCI11ENDIAN_UINT16(secondExportTable + pubfunct); - VERIFY(offset < _bufSize, "invalid export function pointer"); + if (offset >= _bufSize) + error("Invalid export function pointer"); } } diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp index a5679fac54..33e62b21ae 100644 --- a/engines/sci/engine/script_patches.cpp +++ b/engines/sci/engine/script_patches.cpp @@ -60,55 +60,6 @@ struct SciScriptSignature { // - if not EOS, an adjust offset and the actual bytes // - rinse and repeat -#if 0 - -// =========================================================================== -// Castle of Dr. Brain -// cipher::init (script 391) is called on room 380 init. This resets the word -// cipher puzzle. The puzzle sadly operates on some hep strings, which aren't -// saved in our sci. So saving/restoring in this room will break the puzzle -// Because of this issue, we just init the puzzle each time it's accessed. -// this is not 100% sierra behaviour, in fact we will actually reset the puzzle -// during each access which makes it impossible to cheat. -const byte castlebrainSignatureCipherPuzzle[] = { - 22, - 0x35, 0x00, // ldi 00 - 0xa3, 0x26, // sal local[26] - 0xa3, 0x25, // sal local[25] - 0x35, 0x00, // ldi 00 - 0xa3, 0x2a, // sal local[2a] (local is not used) - 0xa3, 0x29, // sal local[29] (local is not used) - 0x35, 0xff, // ldi ff - 0xa3, 0x2c, // sal local[2c] - 0xa3, 0x2b, // sal local[2b] - 0x35, 0x00, // ldi 00 - 0x65, 0x16, // aTop highlightedIcon - 0 -}; - -const uint16 castlebrainPatchCipherPuzzle[] = { - 0x39, 0x6b, // pushi 6b (selector init) - 0x76, // push0 - 0x55, 0x04, // self 04 - 0x35, 0x00, // ldi 00 - 0xa3, 0x25, // sal local[25] - 0xa3, 0x26, // sal local[26] - 0xa3, 0x29, // sal local[29] - 0x65, 0x16, // aTop highlightedIcon - 0x34, 0xff, 0xff, // ldi ffff - 0xa3, 0x2b, // sal local[2b] - 0xa3, 0x2c, // sal local[2c] - PATCH_END -}; - -// script, description, magic DWORD, adjust -const SciScriptSignature castlebrainSignatures[] = { - { 391, "cipher puzzle save/restore break", 1, PATCH_MAGICDWORD(0xa3, 0x26, 0xa3, 0x25), -2, castlebrainSignatureCipherPuzzle, castlebrainPatchCipherPuzzle }, - SCI_SIGNATUREENTRY_TERMINATOR -}; - -#endif - // =========================================================================== // stayAndHelp::changeState (0) is called when ego swims to the left or right // boundaries of room 660. Normally a textbox is supposed to get on screen @@ -497,74 +448,6 @@ const SciScriptSignature gk1Signatures[] = { SCI_SIGNATUREENTRY_TERMINATOR }; -#if 0 - -// =========================================================================== -// this here gets called on entry and when going out of game windows -// uEvt::port will not get changed after kDisposeWindow but a bit later, so -// we would get an invalid port handle to a kSetPort call. We just patch in -// resetting of the port selector. We destroy the stop/fade code in there, -// it seems it isn't used at all in the game. -const byte hoyle4SignaturePortFix[] = { - 28, - 0x39, 0x09, // pushi 09 - 0x89, 0x0b, // lsg 0b - 0x39, 0x64, // pushi 64 - 0x38, 0xc8, 0x00, // pushi 00c8 - 0x38, 0x2c, 0x01, // pushi 012c - 0x38, 0x90, 0x01, // pushi 0190 - 0x38, 0xf4, 0x01, // pushi 01f4 - 0x38, 0x58, 0x02, // pushi 0258 - 0x38, 0xbc, 0x02, // pushi 02bc - 0x38, 0x20, 0x03, // pushi 0320 - 0x46, // calle [xxxx] [xxxx] [xx] - +5, 43, // [skip 5 bytes] - 0x30, 0x27, 0x00, // bnt 0027 -> end of routine - 0x87, 0x00, // lap 00 - 0x30, 0x19, 0x00, // bnt 0019 -> fade out - 0x87, 0x01, // lap 01 - 0x30, 0x14, 0x00, // bnt 0014 -> fade out - 0x38, 0xa7, 0x00, // pushi 00a7 - 0x76, // push0 - 0x80, 0x29, 0x01, // lag 0129 - 0x4a, 0x04, // send 04 - call song::stop - 0x39, 0x27, // pushi 27 - 0x78, // push1 - 0x8f, 0x01, // lsp 01 - 0x51, 0x54, // class 54 - 0x4a, 0x06, // send 06 - call PlaySong::play - 0x33, 0x09, // jmp 09 -> end of routine - 0x38, 0xaa, 0x00, // pushi 00aa - 0x76, // push0 - 0x80, 0x29, 0x01, // lag 0129 - 0x4a, 0x04, // send 04 - 0x48, // ret - 0 -}; - -const uint16 hoyle4PatchPortFix[] = { - PATCH_ADDTOOFFSET | +33, - 0x38, 0x31, 0x01, // pushi 0131 (selector curEvent) - 0x76, // push0 - 0x80, 0x50, 0x00, // lag 0050 (global var 80h, "User") - 0x4a, 0x04, // send 04 - read User::curEvent - - 0x38, 0x93, 0x00, // pushi 0093 (selector port) - 0x78, // push1 - 0x76, // push0 - 0x4a, 0x06, // send 06 - write 0 to that object::port - 0x48, // ret - PATCH_END -}; - -// script, description, magic DWORD, adjust -const SciScriptSignature hoyle4Signatures[] = { - { 0, "port fix when disposing windows", PATCH_MAGICDWORD(0x64, 0x38, 0xC8, 0x00), -5, hoyle4SignaturePortFix, hoyle4PatchPortFix }, - { 0, NULL, 0, 0, NULL, NULL } -}; - -#endif - // =========================================================================== // at least during harpy scene export 29 of script 0 is called in kq5cd and // has an issue for those calls, where temp 3 won't get inititialized, but @@ -615,9 +498,45 @@ const uint16 kq5PatchCdHarpyVolume[] = { PATCH_END }; +// This is a heap patch, and it modifies the properties of an object, instead +// of patching script code. +// +// The witchCage object in script 200 is broken and claims to have 12 +// variables instead of the 8 it should have because it is a Cage. +// Additionally its top,left,bottom,right properties are set to 0 rather +// than the right values. We fix the object by setting the right values. +// If they are all zero, this causes an impossible position check in +// witch::cantBeHere and an infinite loop when entering room 22 (bug #3034714). +// +// This bug is accidentally not triggered in SSCI because the invalid number +// of variables effectively hides witchCage::doit, causing this position check +// to be bypassed entirely. +// See also the warning+comment in Object::initBaseObject +const byte kq5SignatureWitchCageInit[] = { + 16, + 0x00, 0x00, // top + 0x00, 0x00, // left + 0x00, 0x00, // bottom + 0x00, 0x00, // right + 0x00, 0x00, // extra property #1 + 0x7a, 0x00, // extra property #2 + 0xc8, 0x00, // extra property #3 + 0xa3, 0x00, // extra property #4 + 0 +}; + +const uint16 kq5PatchWitchCageInit[] = { + 0x00, 0x00, // top + 0x7a, 0x00, // left + 0xc8, 0x00, // bottom + 0xa3, 0x00, // right + PATCH_END +}; + // script, description, magic DWORD, adjust const SciScriptSignature kq5Signatures[] = { { 0, "CD: harpy volume change", 1, PATCH_MAGICDWORD(0x80, 0x91, 0x01, 0x18), 0, kq5SignatureCdHarpyVolume, kq5PatchCdHarpyVolume }, + { 200, "CD: witch cage init", 1, PATCH_MAGICDWORD(0x7a, 0x00, 0xc8, 0x00), -10, kq5SignatureWitchCageInit, kq5PatchWitchCageInit }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -652,6 +571,52 @@ const SciScriptSignature kq6Signatures[] = { }; // =========================================================================== +// Script 210 in the German version of Longbow handles the case where Robin +// hands out the scroll to Marion and then types his name using the hand code. +// The German version script contains a typo (probably a copy/paste error), +// and the function that is used to show each letter is called twice. The +// second time that the function is called, the second parameter passed to +// the function is undefined, thus kStrCat() that is called inside the function +// reads a random pointer and crashes. We patch all of the 5 function calls +// (one for each letter typed from "R", "O", "B", "I", "N") so that they are +// the same as the English version. Fixes bug #3048054. +const byte longbowSignatureShowHandCode[] = { + 3, + 0x78, // push1 + 0x78, // push1 + 0x72, // lofsa + +2, 2, // skip 2 bytes, offset of lofsa (the letter typed) + 0x36, // push + 0x40, // call + +2, 3, // skip 2 bytes, offset of call + 0x02, // perform the call above with 2 parameters + 0x36, // push + 0x40, // call + +2, 8, // skip 2 bytes, offset of call + 0x02, // perform the call above with 2 parameters + 0x38, 0x1c, 0x01, // pushi 011c (setMotion) + 0x39, 0x04, // pushi 04 (x) + 0x51, 0x1e, // class MoveTo + 0 +}; + +const uint16 longbowPatchShowHandCode[] = { + 0x39, 0x01, // pushi 1 (combine the two push1's in one, like in the English version) + PATCH_ADDTOOFFSET | +3, // leave the lofsa call untouched + // The following will remove the duplicate call + 0x32, 0x02, 0x00, // jmp 02 - skip 2 bytes (the remainder of the first call) + 0x48, // ret (dummy, should never be reached) + 0x48, // ret (dummy, should never be reached) + PATCH_END +}; + +// script, description, magic DWORD, adjust +const SciScriptSignature longbowSignatures[] = { + { 210, "hand code crash", 5, PATCH_MAGICDWORD(0x02, 0x38, 0x1c, 0x01), -14, longbowSignatureShowHandCode, longbowPatchShowHandCode }, + SCI_SIGNATUREENTRY_TERMINATOR +}; + +// =========================================================================== // this is called on every death dialog. Problem is at least the german // version of lsl6 gets title text that is far too long for the // available temp space resulting in temp space corruption @@ -703,7 +668,7 @@ const SciScriptSignature larry6Signatures[] = { }; // =========================================================================== -// rm560::doit was supposed to close the painting, when heimlich enters the +// rm560::doit was supposed to close the painting, when Heimlich enters the // room. The code is buggy, so it actually closes the painting, when heimlich // is not in the room. We fix that. const byte laurabow2SignaturePaintingClosing[] = { @@ -826,44 +791,10 @@ const uint16 qfg1vgaPatchFightEvents[] = { PATCH_END }; -// When QFG1VGA and QFG3 dispose of a child window. For example, when choosing -// a spell (parent window), if the spell can't be casted, a subsequent window -// opens, notifying that it can't be casted. When showing the child window, the -// scripts restore the area below the parent window, draw the child window, and -// then attempt to redraw the parent window, which leads to the background -// picture (which has just been restored) overwriting the child window. It -// appers that kGraph(redrawBox) is different in QFG1VGA and QFG3. However, we -// can just remove the window redraw and update calls when the window is -// supposed to be disposed, and the window is disposed of correctly. Fixes bug -// #3053093. -const byte qfg1vgaWindowDispose[] = { - 17, - 0x39, 0x05, // pushi 05 - 0x39, 0x0d, // pushi 0d - 0x67, 0x2e, // pTos 2e - 0x67, 0x30, // pTos 30 - 0x67, 0x32, // pTos 32 - 0x67, 0x34, // pTos 34 - 0x43, 0x6c, 0x0a, // callk kGraph 10 - 0x39, 0x06, // pushi 06 - 0 -}; - -const uint16 qfg1vgaPatchWindowDispose[] = { - 0x34, 0x00, 0x00, // ldi 0000 (dummy) - 0x34, 0x00, 0x00, // ldi 0000 (dummy) - 0x34, 0x00, 0x00, // ldi 0000 (dummy) - 0x34, 0x00, 0x00, // ldi 0000 (dummy) - 0x34, 0x00, 0x00, // ldi 0000 (dummy) - 0x33, 0x3e, // jmp 0x3e (skip 62 bytes - this skips the subsequent 2 kGraph(update) calls, before kDisposeWindow is invoked) - PATCH_END -}; - // script, description, magic DWORD, adjust const SciScriptSignature qfg1vgaSignatures[] = { { 215, "fight event issue", 1, PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents }, { 216, "weapon master event issue", 1, PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents }, - { 559, "window dispose", 1, PATCH_MAGICDWORD(0x39, 0x05, 0x39, 0x0d), 0, qfg1vgaWindowDispose, qfg1vgaPatchWindowDispose }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -927,37 +858,6 @@ const uint16 qfg3PatchImportDialog[] = { PATCH_END }; -// When QFG1VGA and QFG3 dispose of a child window. For example, when choosing -// a spell (parent window), if the spell can't be casted, a subsequent window -// opens, notifying that it can't be casted. When showing the child window, the -// scripts restore the area below the parent window, draw the child window, and -// then attempt to redraw the parent window, which leads to the background -// picture (which has just been restored) overwriting the child window. It -// appers that kGraph(redrawBox) is different in QFG1VGA and QFG3. However, we -// can just remove the window redraw and update calls when the window is -// supposed to be disposed, and the window is disposed of correctly. Fixes bug -// #3053093. -const byte qfg3WindowDispose[] = { - 15, - 0x39, 0x05, // pushi 05 - 0x39, 0x0d, // pushi 0d - 0x67, 0x2e, // pTos 2e - 0x67, 0x30, // pTos 30 - 0x67, 0x32, // pTos 32 - 0x67, 0x34, // pTos 34 - 0x43, 0x6c, 0x0a, // callk kGraph 10 - 0 -}; - -const uint16 qfg3PatchWindowDispose[] = { - 0x34, 0x00, 0x00, // ldi 0000 (dummy) - 0x34, 0x00, 0x00, // ldi 0000 (dummy) - 0x34, 0x00, 0x00, // ldi 0000 (dummy) - 0x34, 0x00, 0x00, // ldi 0000 (dummy) - 0x34, 0x00, 0x00, // ldi 0000 (dummy) - PATCH_END -}; - // Script 23 in QFG3 has a typo/bug which makes it loop endlessly and // read garbage. Fixes bug #3040722. const byte qfg3DialogCrash[] = { @@ -976,7 +876,6 @@ const uint16 qfg3PatchDialogCrash[] = { // script, description, magic DWORD, adjust const SciScriptSignature qfg3Signatures[] = { - { 22, "window dispose", 1, PATCH_MAGICDWORD(0x39, 0x05, 0x39, 0x0d), 0, qfg3WindowDispose, qfg3PatchWindowDispose }, { 23, "dialog crash", 1, PATCH_MAGICDWORD(0xe7, 0x03, 0x22, 0x33), -1, qfg3DialogCrash, qfg3PatchDialogCrash }, { 944, "import dialog continuous calls", 1, PATCH_MAGICDWORD(0x2a, 0x31, 0x0b, 0x7a), -1, qfg3SignatureImportDialog, qfg3PatchImportDialog }, SCI_SIGNATUREENTRY_TERMINATOR @@ -1095,55 +994,6 @@ const SciScriptSignature sq4Signatures[] = { SCI_SIGNATUREENTRY_TERMINATOR }; -// =========================================================================== -// It seems to scripts warp ego outside the screen somehow (or maybe kDoBresen?) -// ego::mover is set to 0 and rm119::doit will crash in that case. This here -// fixes part of the problem and actually checks ego::mover to be 0 and skips -// TODO: this should get further investigated by waltervn and maybe properly -// patched. For now ego will shortly disappear and reappear a bit after -// this isn't good, but sierra sci also "crashed" (endless looped) so this -// is at least better than the original code -const byte sq5SignatureScrubbing[] = { - 19, - 0x18, // not - 0x31, 0x37, // bnt 37 - 0x78, // push1 (selector x) - 0x76, // push0 - 0x39, 0x38, // pushi 38 (selector mover) - 0x76, // push0 - 0x81, 0x00, // lag 00 - 0x4a, 0x04, // send 04 - read ego::mover - 0x4a, 0x04, // send 04 - read ego::mover::x - 0x36, // push - 0x34, 0xa0, 0x00, // ldi 00a0 - 0x1c, // ne? - 0 -}; - -const uint16 sq5PatchScrubbing[] = { - 0x18, // not - 0x31, 0x37, // bnt 37 -// 0x2f, 0x38, // bt 37 (would save another byte, isn't needed - 0x39, 0x38, // pushi 38 (selector mover) - 0x76, // push0 - 0x81, 0x00, // lag 00 - 0x4a, 0x04, // send 04 - read ego::mover - 0x31, 0x2e, // bnt 2e (jump if ego::mover is 0) - 0x78, // push1 (selector x) - 0x76, // push0 - 0x4a, 0x04, // send 04 - read ego::mover::x - 0x39, 0xa0, // pushi a0 (saving 2 bytes) - 0x1c, // ne? - PATCH_END -}; - -// script, description, magic DWORD, adjust -const SciScriptSignature sq5Signatures[] = { - { 119, "scrubbing send crash", 1, PATCH_MAGICDWORD(0x18, 0x31, 0x37, 0x78), 0, sq5SignatureScrubbing, sq5PatchScrubbing }, - SCI_SIGNATUREENTRY_TERMINATOR -}; - - // will actually patch previously found signature area void Script::applyPatch(const uint16 *patch, byte *scriptData, const uint32 scriptSize, int32 signatureOffset) { byte orgData[PATCH_VALUELIMIT]; @@ -1236,12 +1086,6 @@ int32 Script::findSignature(const SciScriptSignature *signature, const byte *scr void Script::matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uint32 scriptSize) { const SciScriptSignature *signatureTable = NULL; switch (g_sci->getGameId()) { - // Dr. Brain now works because we properly maintain the state of the string heap in savegames -#if 0 - case GID_CASTLEBRAIN: - signatureTable = castlebrainSignatures; - break; -#endif case GID_ECOQUEST: signatureTable = ecoquest1Signatures; break; @@ -1257,12 +1101,6 @@ void Script::matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uin case GID_GK1: signatureTable = gk1Signatures; break; - // hoyle4 now works due to workaround inside GfxPorts -#if 0 - case GID_HOYLE4: - signatureTable = hoyle4Signatures; - break; -#endif case GID_KQ5: signatureTable = kq5Signatures; break; @@ -1272,6 +1110,9 @@ void Script::matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uin case GID_LAURABOW2: signatureTable = laurabow2Signatures; break; + case GID_LONGBOW: + signatureTable = longbowSignatures; + break; case GID_LSL6: signatureTable = larry6Signatures; break; @@ -1290,9 +1131,6 @@ void Script::matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uin case GID_SQ4: signatureTable = sq4Signatures; break; - case GID_SQ5: - signatureTable = sq5Signatures; - break; default: break; } diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp index 184f81bb99..76490217c3 100644 --- a/engines/sci/engine/scriptdebug.cpp +++ b/engines/sci/engine/scriptdebug.cpp @@ -281,6 +281,35 @@ reg_t disassemble(EngineState *s, reg_t pos, bool printBWTag, bool printBytecode return retval; } +bool isJumpOpcode(EngineState *s, reg_t pos, reg_t& jumpTarget) +{ + SegmentObj *mobj = s->_segMan->getSegment(pos.segment, SEG_TYPE_SCRIPT); + if (!mobj) + return false; + Script *script_entity = (Script *)mobj; + + const byte *scr = script_entity->getBuf(); + int scr_size = script_entity->getBufSize(); + + if (pos.offset >= scr_size) + return false; + + int16 opparams[4]; + byte opsize; + int bytecount = readPMachineInstruction(scr + pos.offset, opsize, opparams); + const byte opcode = opsize >> 1; + + switch (opcode) { + case op_bt: + case op_bnt: + case op_jmp: + jumpTarget = pos + bytecount + opparams[0]; + return true; + default: + return false; + } +} + void SciEngine::scriptDebug() { EngineState *s = _gamestate; diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp index ffc81f0fde..0dc245a991 100644 --- a/engines/sci/engine/seg_manager.cpp +++ b/engines/sci/engine/seg_manager.cpp @@ -63,7 +63,7 @@ void SegManager::resetSegMan() { // Free memory for (uint i = 0; i < _heap.size(); i++) { if (_heap[i]) - deallocate(i, false); + deallocate(i); } _heap.clear(); @@ -151,16 +151,17 @@ Script *SegManager::allocateScript(int script_nr, SegmentId *segid) { return (Script *)mem; } -void SegManager::deallocate(SegmentId seg, bool recursive) { - VERIFY(check(seg), "invalid seg id"); +void SegManager::deallocate(SegmentId seg) { + if (!check(seg)) + error("SegManager::deallocate(): invalid segment ID"); SegmentObj *mobj = _heap[seg]; if (mobj->getType() == SEG_TYPE_SCRIPT) { Script *scr = (Script *)mobj; _scriptSegMap.erase(scr->getScriptNumber()); - if (recursive && scr->_localsSegment) - deallocate(scr->_localsSegment, recursive); + if (scr->_localsSegment) + deallocate(scr->_localsSegment); } delete mobj; @@ -176,8 +177,7 @@ bool SegManager::isHeapObject(reg_t pos) const { } void SegManager::deallocateScript(int script_nr) { - SegmentId seg = getScriptSegment(script_nr); - deallocate(seg, true); + deallocate(getScriptSegment(script_nr)); } Script *SegManager::getScript(const SegmentId seg) { @@ -360,9 +360,8 @@ LocalVariables *SegManager::allocLocalsSegment(Script *scr) { if (scr->_localsSegment) { locals = (LocalVariables *)_heap[scr->_localsSegment]; - VERIFY(locals != NULL, "Re-used locals segment was NULL'd out"); - VERIFY(locals->getType() == SEG_TYPE_LOCALS, "Re-used locals segment did not consist of local variables"); - VERIFY(locals->script_id == scr->getScriptNumber(), "Re-used locals segment belonged to other script"); + if (!locals || locals->getType() != SEG_TYPE_LOCALS || locals->script_id != scr->getScriptNumber()) + error("Invalid script locals segment while allocating locals"); } else locals = (LocalVariables *)allocSegment(new LocalVariables(), &scr->_localsSegment); @@ -403,7 +402,7 @@ void SegManager::freeHunkEntry(reg_t addr) { return; } - ht->freeEntry(addr.offset); + ht->freeEntryContents(addr.offset); } reg_t SegManager::allocateHunkEntry(const char *hunk_type, int size) { @@ -602,13 +601,13 @@ static inline char getChar(const SegmentRef &ref, uint offset) { warning("Attempt to read character from non-raw data"); bool oddOffset = offset & 1; - if (g_sci->getPlatform() == Common::kPlatformAmiga) - oddOffset = !oddOffset; // Amiga versions are BE + if (g_sci->isBE()) + oddOffset = !oddOffset; return (oddOffset ? val.offset >> 8 : val.offset & 0xff); } -static inline void setChar(const SegmentRef &ref, uint offset, char value) { +static inline void setChar(const SegmentRef &ref, uint offset, byte value) { if (ref.skipByte) offset++; @@ -617,8 +616,8 @@ static inline void setChar(const SegmentRef &ref, uint offset, char value) { val->segment = 0; bool oddOffset = offset & 1; - if (g_sci->getPlatform() == Common::kPlatformAmiga) - oddOffset = !oddOffset; // Amiga versions are BE + if (g_sci->isBE()) + oddOffset = !oddOffset; if (oddOffset) val->offset = (val->offset & 0x00ff) | (value << 8); @@ -864,7 +863,7 @@ bool SegManager::freeDynmem(reg_t addr) { if (addr.segment < 1 || addr.segment >= _heap.size() || !_heap[addr.segment] || _heap[addr.segment]->getType() != SEG_TYPE_DYNMEM) return false; // error - deallocate(addr.segment, true); + deallocate(addr.segment); return true; // OK } @@ -1022,7 +1021,7 @@ void SegManager::uninstantiateScript(int script_nr) { SegmentId segmentId = getScriptSegment(script_nr); Script *scr = getScriptIfLoaded(segmentId); - if (!scr) { // Is it already unloaded? + if (!scr || scr->isMarkedAsDeleted()) { // Is it already unloaded? //warning("unloading script 0x%x requested although not loaded", script_nr); // This is perfectly valid SCI behaviour return; @@ -1079,15 +1078,7 @@ void SegManager::uninstantiateScriptSci0(int script_nr) { if (scr->getLockers()) scr->decrementLockers(); // Decrease lockers if this is us ourselves } else { - if (g_sci->getGameId() == GID_HOYLE3 && (superclass_script == 0 || superclass_script >= 990)) { - // HACK for Hoyle 3: when exiting Checkers or Pachisi, scripts 0, 999 and some others - // are deleted but are never instantiated again. We ignore deletion of these scripts - // here for Hoyle 3 - bug #3038837 - // TODO/FIXME: find out why this happens, seems like there is a problem with the object - // lock code - } else { - uninstantiateScript(superclass_script); - } + uninstantiateScript(superclass_script); } // Recurse to assure that the superclass lockers number gets decreased } diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h index 61fa2e2245..d402afbf1a 100644 --- a/engines/sci/engine/seg_manager.h +++ b/engines/sci/engine/seg_manager.h @@ -36,15 +36,6 @@ namespace Sci { /** - * Verify the the given condition is true, output the message if condition is false, and exit. - * @param cond condition to be verified - * @param msg the message to be printed if condition fails - */ -#define VERIFY( cond, msg ) if (!(cond)) {\ - error("%s, line, %d, %s", __FILE__, __LINE__, msg); \ - } - -/** * Parameters for getScriptSegment(). */ enum ScriptLoadType { @@ -477,7 +468,7 @@ private: private: SegmentObj *allocSegment(SegmentObj *mem, SegmentId *segid); - void deallocate(SegmentId seg, bool recursive); + void deallocate(SegmentId seg); void createClassTable(); SegmentId findFreeSegment() const; diff --git a/engines/sci/engine/segment.cpp b/engines/sci/engine/segment.cpp index 05d914cffb..2bb77c707a 100644 --- a/engines/sci/engine/segment.cpp +++ b/engines/sci/engine/segment.cpp @@ -39,7 +39,6 @@ namespace Sci { //#define GC_DEBUG // Debug garbage collection //#define GC_DEBUG_VERBOSE // Debug garbage verbosely - SegmentObj *SegmentObj::createSegmentObj(SegmentType type) { SegmentObj *mem = 0; switch (type) { @@ -85,116 +84,12 @@ SegmentObj *SegmentObj::createSegmentObj(SegmentType type) { return mem; } -const char *SegmentObj::getSegmentTypeName(SegmentType type) { - switch (type) { - case SEG_TYPE_SCRIPT: - return "script"; - break; - case SEG_TYPE_CLONES: - return "clones"; - break; - case SEG_TYPE_LOCALS: - return "locals"; - break; - case SEG_TYPE_STACK: - return "stack"; - break; - case SEG_TYPE_HUNK: - return "hunk"; - break; - case SEG_TYPE_LISTS: - return "lists"; - break; - case SEG_TYPE_NODES: - return "nodes"; - break; - case SEG_TYPE_DYNMEM: - return "dynmem"; - break; -#ifdef ENABLE_SCI32 - case SEG_TYPE_ARRAY: - return "array"; - break; - case SEG_TYPE_STRING: - return "string"; - break; -#endif - default: - error("Unknown SegmentObj type %d", type); - break; - } - return NULL; -} - SegmentRef SegmentObj::dereference(reg_t pointer) { error("Error: Trying to dereference pointer %04x:%04x to inappropriate segment", PRINT_REG(pointer)); return SegmentRef(); } - -bool LocalVariables::isValidOffset(uint16 offset) const { - return offset < _locals.size() * 2; -} - -SegmentRef LocalVariables::dereference(reg_t pointer) { - SegmentRef ret; - ret.isRaw = false; // reg_t based data! - ret.maxSize = (_locals.size() - pointer.offset / 2) * 2; - - if (pointer.offset & 1) { - ret.maxSize -= 1; - ret.skipByte = true; - } - - if (ret.maxSize > 0) { - ret.reg = &_locals[pointer.offset / 2]; - } else { - if ((g_sci->getEngineState()->currentRoomNumber() == 660 || g_sci->getEngineState()->currentRoomNumber() == 660) - && g_sci->getGameId() == GID_LAURABOW2) { - // Happens in two places during the intro of LB2CD, both from kMemory(peek): - // - room 160: Heap 160 has 83 local variables (0-82), and the game - // asks for variables at indices 83 - 90 too. - // - room 220: Heap 220 has 114 local variables (0-113), and the - // game asks for variables at indices 114-120 too. - } else { - error("LocalVariables::dereference: Offset at end or out of bounds %04x:%04x", PRINT_REG(pointer)); - } - ret.reg = 0; - } - return ret; -} - -bool DataStack::isValidOffset(uint16 offset) const { - return offset < _capacity * 2; -} - -SegmentRef DataStack::dereference(reg_t pointer) { - SegmentRef ret; - ret.isRaw = false; // reg_t based data! - ret.maxSize = (_capacity - pointer.offset / 2) * 2; - - if (pointer.offset & 1) { - ret.maxSize -= 1; - ret.skipByte = true; - } - - ret.reg = &_entries[pointer.offset / 2]; - return ret; -} - -bool DynMem::isValidOffset(uint16 offset) const { - return offset < _size; -} - -SegmentRef DynMem::dereference(reg_t pointer) { - SegmentRef ret; - ret.isRaw = true; - ret.maxSize = _size - pointer.offset; - ret.raw = _buf + pointer.offset; - return ret; -} - //-------------------- clones -------------------- Common::Array<reg_t> CloneTable::listAllOutgoingReferences(reg_t addr) const { @@ -220,8 +115,6 @@ Common::Array<reg_t> CloneTable::listAllOutgoingReferences(reg_t addr) const { void CloneTable::freeAtAddress(SegManager *segMan, reg_t addr) { #ifdef GC_DEBUG - // assert(addr.segment == _segId); - Object *victim_obj = &(_table[addr.offset]); if (!(victim_obj->_flags & OBJECT_FLAG_FREED)) @@ -229,30 +122,54 @@ void CloneTable::freeAtAddress(SegManager *segMan, reg_t addr) { #ifdef GC_DEBUG_VERBOSE else warning("[GC-DEBUG] Clone %04x:%04x: Freeing", PRINT_REG(addr)); + + warning("[GC] Clone had pos %04x:%04x", PRINT_REG(victim_obj->pos)); #endif #endif - /* - warning("[GC] Clone %04x:%04x: Freeing", PRINT_REG(addr)); - warning("[GC] Clone had pos %04x:%04x", PRINT_REG(victim_obj->pos)); - */ + freeEntry(addr.offset); } //-------------------- locals -------------------- + +SegmentRef LocalVariables::dereference(reg_t pointer) { + SegmentRef ret; + ret.isRaw = false; // reg_t based data! + ret.maxSize = (_locals.size() - pointer.offset / 2) * 2; + + if (pointer.offset & 1) { + ret.maxSize -= 1; + ret.skipByte = true; + } + + if (ret.maxSize > 0) { + ret.reg = &_locals[pointer.offset / 2]; + } else { + if ((g_sci->getEngineState()->currentRoomNumber() == 660 || g_sci->getEngineState()->currentRoomNumber() == 660) + && g_sci->getGameId() == GID_LAURABOW2) { + // Happens in two places during the intro of LB2CD, both from kMemory(peek): + // - room 160: Heap 160 has 83 local variables (0-82), and the game + // asks for variables at indices 83 - 90 too. + // - room 220: Heap 220 has 114 local variables (0-113), and the + // game asks for variables at indices 114-120 too. + } else { + error("LocalVariables::dereference: Offset at end or out of bounds %04x:%04x", PRINT_REG(pointer)); + } + ret.reg = 0; + } + return ret; +} + reg_t LocalVariables::findCanonicAddress(SegManager *segMan, reg_t addr) const { // Reference the owning script SegmentId owner_seg = segMan->getScriptSegment(script_id); - assert(owner_seg > 0); - return make_reg(owner_seg, 0); } Common::Array<reg_t> LocalVariables::listAllOutgoingReferences(reg_t addr) const { Common::Array<reg_t> tmp; -// assert(addr.segment == _segId); - for (uint i = 0; i < _locals.size(); i++) tmp.push_back(_locals[i]); @@ -261,9 +178,19 @@ Common::Array<reg_t> LocalVariables::listAllOutgoingReferences(reg_t addr) const //-------------------- stack -------------------- -reg_t DataStack::findCanonicAddress(SegManager *segMan, reg_t addr) const { - addr.offset = 0; - return addr; + +SegmentRef DataStack::dereference(reg_t pointer) { + SegmentRef ret; + ret.isRaw = false; // reg_t based data! + ret.maxSize = (_capacity - pointer.offset / 2) * 2; + + if (pointer.offset & 1) { + ret.maxSize -= 1; + ret.skipByte = true; + } + + ret.reg = &_entries[pointer.offset / 2]; + return ret; } Common::Array<reg_t> DataStack::listAllOutgoingReferences(reg_t object) const { @@ -274,11 +201,7 @@ Common::Array<reg_t> DataStack::listAllOutgoingReferences(reg_t object) const { return tmp; } - //-------------------- lists -------------------- -void ListTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) { - freeEntry(sub_addr.offset); -} Common::Array<reg_t> ListTable::listAllOutgoingReferences(reg_t addr) const { Common::Array<reg_t> tmp; @@ -296,11 +219,7 @@ Common::Array<reg_t> ListTable::listAllOutgoingReferences(reg_t addr) const { return tmp; } - //-------------------- nodes -------------------- -void NodeTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) { - freeEntry(sub_addr.offset); -} Common::Array<reg_t> NodeTable::listAllOutgoingReferences(reg_t addr) const { Common::Array<reg_t> tmp; @@ -321,14 +240,12 @@ Common::Array<reg_t> NodeTable::listAllOutgoingReferences(reg_t addr) const { //-------------------- dynamic memory -------------------- -reg_t DynMem::findCanonicAddress(SegManager *segMan, reg_t addr) const { - addr.offset = 0; - return addr; -} - -Common::Array<reg_t> DynMem::listAllDeallocatable(SegmentId segId) const { - const reg_t r = make_reg(segId, 0); - return Common::Array<reg_t>(&r, 1); +SegmentRef DynMem::dereference(reg_t pointer) { + SegmentRef ret; + ret.isRaw = true; + ret.maxSize = _size - pointer.offset; + ret.raw = _buf + pointer.offset; + return ret; } #ifdef ENABLE_SCI32 @@ -393,11 +310,6 @@ SegmentRef StringTable::dereference(reg_t pointer) { return ret; } -void StringTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) { - _table[sub_addr.offset].destroy(); - freeEntry(sub_addr.offset); -} - #endif } // End of namespace Sci diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h index 9aaa3a4b08..ffde01f934 100644 --- a/engines/sci/engine/segment.h +++ b/engines/sci/engine/segment.h @@ -84,7 +84,6 @@ struct SegmentObj : public Common::Serializable { public: static SegmentObj *createSegmentObj(SegmentType type); - static const char *getSegmentTypeName(SegmentType type); public: SegmentObj(SegmentType type) : _type(type) {} @@ -150,11 +149,11 @@ struct LocalVariables : public SegmentObj { Common::Array<reg_t> _locals; public: - LocalVariables(): SegmentObj(SEG_TYPE_LOCALS) { - script_id = 0; - } + LocalVariables(): SegmentObj(SEG_TYPE_LOCALS), script_id(0) { } - virtual bool isValidOffset(uint16 offset) const; + virtual bool isValidOffset(uint16 offset) const { + return offset < _locals.size() * 2; + } virtual SegmentRef dereference(reg_t pointer); virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const; virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const; @@ -168,18 +167,19 @@ struct DataStack : SegmentObj { reg_t *_entries; public: - DataStack() : SegmentObj(SEG_TYPE_STACK) { - _capacity = 0; - _entries = NULL; - } + DataStack() : SegmentObj(SEG_TYPE_STACK), _capacity(0), _entries(NULL) { } ~DataStack() { free(_entries); _entries = NULL; } - virtual bool isValidOffset(uint16 offset) const; + virtual bool isValidOffset(uint16 offset) const { + return offset < _capacity * 2; + } virtual SegmentRef dereference(reg_t pointer); - virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const; + virtual reg_t findCanonicAddress(SegManager *segMan, reg_t addr) const { + return make_reg(addr.segment, 0); + } virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const; virtual void saveLoadWithSerializer(Common::Serializer &ser); @@ -211,7 +211,7 @@ struct Hunk { }; template<typename T> -struct Table : public SegmentObj { +struct SegmentObjTable : public SegmentObj { typedef T value_type; struct Entry : public T { int next_free; /* Only used for free entries */ @@ -225,7 +225,7 @@ struct Table : public SegmentObj { Common::Array<Entry> _table; public: - Table(SegmentType type) : SegmentObj(type) { + SegmentObjTable(SegmentType type) : SegmentObj(type) { initTable(); } @@ -279,8 +279,8 @@ public: /* CloneTable */ -struct CloneTable : public Table<Clone> { - CloneTable() : Table<Clone>(SEG_TYPE_CLONES) {} +struct CloneTable : public SegmentObjTable<Clone> { + CloneTable() : SegmentObjTable<Clone>(SEG_TYPE_CLONES) {} virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr); virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const; @@ -290,10 +290,12 @@ struct CloneTable : public Table<Clone> { /* NodeTable */ -struct NodeTable : public Table<Node> { - NodeTable() : Table<Node>(SEG_TYPE_NODES) {} +struct NodeTable : public SegmentObjTable<Node> { + NodeTable() : SegmentObjTable<Node>(SEG_TYPE_NODES) {} - virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr); + virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr) { + freeEntry(sub_addr.offset); + } virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const; virtual void saveLoadWithSerializer(Common::Serializer &ser); @@ -301,10 +303,12 @@ struct NodeTable : public Table<Node> { /* ListTable */ -struct ListTable : public Table<List> { - ListTable() : Table<List>(SEG_TYPE_LISTS) {} +struct ListTable : public SegmentObjTable<List> { + ListTable() : SegmentObjTable<List>(SEG_TYPE_LISTS) {} - virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr); + virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr) { + freeEntry(sub_addr.offset); + } virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const; virtual void saveLoadWithSerializer(Common::Serializer &ser); @@ -312,18 +316,23 @@ struct ListTable : public Table<List> { /* HunkTable */ -struct HunkTable : public Table<Hunk> { - HunkTable() : Table<Hunk>(SEG_TYPE_HUNK) {} - - virtual void freeEntry(int idx) { - Table<Hunk>::freeEntry(idx); +struct HunkTable : public SegmentObjTable<Hunk> { + HunkTable() : SegmentObjTable<Hunk>(SEG_TYPE_HUNK) {} - if (!_table[idx].mem) - warning("Attempt to free an already freed hunk"); + void freeEntryContents(int idx) { free(_table[idx].mem); _table[idx].mem = 0; } + virtual void freeEntry(int idx) { + SegmentObjTable<Hunk>::freeEntry(idx); + freeEntryContents(idx); + } + + virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr) { + freeEntry(sub_addr.offset); + } + virtual void saveLoadWithSerializer(Common::Serializer &ser); }; @@ -341,10 +350,17 @@ public: _buf = NULL; } - virtual bool isValidOffset(uint16 offset) const; + virtual bool isValidOffset(uint16 offset) const { + return offset < _size; + } virtual SegmentRef dereference(reg_t pointer); - virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const; - virtual Common::Array<reg_t> listAllDeallocatable(SegmentId segId) const; + virtual reg_t findCanonicAddress(SegManager *segMan, reg_t addr) const { + return make_reg(addr.segment, 0); + } + virtual Common::Array<reg_t> listAllDeallocatable(SegmentId segId) const { + const reg_t r = make_reg(segId, 0); + return Common::Array<reg_t>(&r, 1); + } virtual void saveLoadWithSerializer(Common::Serializer &ser); }; @@ -354,12 +370,7 @@ public: template <typename T> class SciArray { public: - SciArray() { - _type = -1; - _data = NULL; - _size = 0; - _actualSize = 0; - } + SciArray() : _type(-1), _data(NULL), _size(0), _actualSize(0) { } SciArray(const SciArray<T> &array) { _type = array._type; @@ -474,8 +485,8 @@ public: void fromString(const Common::String &string); }; -struct ArrayTable : public Table<SciArray<reg_t> > { - ArrayTable() : Table<SciArray<reg_t> >(SEG_TYPE_ARRAY) {} +struct ArrayTable : public SegmentObjTable<SciArray<reg_t> > { + ArrayTable() : SegmentObjTable<SciArray<reg_t> >(SEG_TYPE_ARRAY) {} virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr); virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const; @@ -484,10 +495,13 @@ struct ArrayTable : public Table<SciArray<reg_t> > { SegmentRef dereference(reg_t pointer); }; -struct StringTable : public Table<SciString> { - StringTable() : Table<SciString>(SEG_TYPE_STRING) {} +struct StringTable : public SegmentObjTable<SciString> { + StringTable() : SegmentObjTable<SciString>(SEG_TYPE_STRING) {} - virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr); + virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr) { + _table[sub_addr.offset].destroy(); + freeEntry(sub_addr.offset); + } void saveLoadWithSerializer(Common::Serializer &ser); SegmentRef dereference(reg_t pointer); diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp index 798dbf529c..957a836e3e 100644 --- a/engines/sci/engine/selector.cpp +++ b/engines/sci/engine/selector.cpp @@ -165,6 +165,7 @@ void Kernel::mapSelectors() { FIND_SELECTOR(vanishingX); FIND_SELECTOR(vanishingY); FIND_SELECTOR(iconIndex); + FIND_SELECTOR(select); #ifdef ENABLE_SCI32 FIND_SELECTOR(data); diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h index 6038ad0c36..8a47984204 100644 --- a/engines/sci/engine/selector.h +++ b/engines/sci/engine/selector.h @@ -130,6 +130,7 @@ struct SelectorCache { // SCI1.1 Mac icon bar selectors Selector iconIndex; ///< Used to index icon bar objects + Selector select; #ifdef ENABLE_SCI32 Selector data; // Used by Array()/String() diff --git a/engines/sci/engine/static_selectors.cpp b/engines/sci/engine/static_selectors.cpp index 4bb61a0658..23241de330 100644 --- a/engines/sci/engine/static_selectors.cpp +++ b/engines/sci/engine/static_selectors.cpp @@ -28,6 +28,7 @@ #include "sci/engine/kernel.h" #include "sci/engine/seg_manager.h" +#include "sci/engine/vm.h" namespace Sci { @@ -118,6 +119,33 @@ static const SelectorRemap sciSelectorRemap[] = { { SCI_VERSION_NONE, SCI_VERSION_NONE, 0, 0 } }; +struct ClassReference { + int script; + const char *className; + const char *selectorName; + SelectorType selectorType; + uint selectorOffset; +}; + +// For variable selectors, we ignore the global selectors and start off from +// the object's selectors (i.e. from the name selector onwards). Thus, the +// following are not taken into consideration when calculating the indices of +// variable selectors in this array: +// SCI0 - SCI1: species, superClass, -info- +// SCI1.1: -objID-, -size-, -propDict-, -methDict-, -classScript-, -script-, +// -super-, -info- +static const ClassReference classReferences[] = { + { 0, "Character", "say", kSelectorMethod, 5 }, // Crazy Nick's Soft Picks + { 928, "Narrator", "say", kSelectorMethod, 4 }, + { 928, "Narrator", "startText", kSelectorMethod, 5 }, + { 929, "Sync", "syncTime", kSelectorVariable, 1 }, + { 929, "Sync", "syncCue", kSelectorVariable, 2 }, + { 981, "SysWindow", "open", kSelectorMethod, 1 }, + { 999, "Script", "init", kSelectorMethod, 0 }, + { 999, "Script", "dispose", kSelectorMethod, 2 }, + { 999, "Script", "changeState", kSelectorMethod, 3 } +}; + Common::StringArray Kernel::checkStaticSelectorNames() { Common::StringArray names; const int offset = (getSciVersion() < SCI_VERSION_1_1) ? 3 : 0; @@ -158,133 +186,115 @@ Common::StringArray Kernel::checkStaticSelectorNames() { names[i] = sci11Selectors[i - count - countSci1]; } - // Now, we need to find out selectors which keep changing place... - // We do that by dissecting game objects, and looking for selectors at - // specified locations. + findSpecificSelectors(names); - // We need to initialize script 0 here, to make sure that it's always - // located at segment 1. - _segMan->instantiateScript(0); - - // The Actor class contains the init, xLast and yLast selectors, which - // we reference directly. It's always in script 998, so we need to - // explicitly load it here. - if (_resMan->testResource(ResourceId(kResourceTypeScript, 998))) { - _segMan->instantiateScript(998); - - const Object *actorClass = _segMan->getObject(_segMan->findObjectByName("Actor")); - - if (actorClass) { - // The init selector is always the first function - int initSelectorPos = actorClass->getFuncSelector(0); +#ifdef ENABLE_SCI32 + } else { + // SCI2+ + for (int i = 0; i < count; i++) + names[i] = sci2Selectors[i]; +#endif + } - if (names.size() < (uint32)initSelectorPos + 2) - names.resize((uint32)initSelectorPos + 2); + for (const SelectorRemap *selectorRemap = sciSelectorRemap; selectorRemap->slot; ++selectorRemap) { + if (getSciVersion() >= selectorRemap->minVersion && getSciVersion() <= selectorRemap->maxVersion) { + const uint32 slot = selectorRemap->slot; + if (slot >= names.size()) + names.resize(slot + 1); + names[slot] = selectorRemap->name; + } + } - names[initSelectorPos] = "init"; - // dispose comes right after init - names[initSelectorPos + 1] = "dispose"; + return names; +} - if ((getSciVersion() >= SCI_VERSION_1_EGA)) { - // Find the xLast and yLast selectors, used in kDoBresen +void Kernel::findSpecificSelectors(Common::StringArray &selectorNames) { + // Now, we need to find out selectors which keep changing place... + // We do that by dissecting game objects, and looking for selectors at + // specified locations. - // xLast and yLast always come between illegalBits and xStep - int illegalBitsSelectorPos = actorClass->locateVarSelector(_segMan, 15 + offset); // illegalBits - int xStepSelectorPos = actorClass->locateVarSelector(_segMan, 51 + offset); // xStep - if (xStepSelectorPos - illegalBitsSelectorPos != 3) { - error("illegalBits and xStep selectors aren't found in " - "known locations. illegalBits = %d, xStep = %d", - illegalBitsSelectorPos, xStepSelectorPos); - } + // We need to initialize script 0 here, to make sure that it's always + // located at segment 1. + _segMan->instantiateScript(0); - int xLastSelectorPos = actorClass->getVarSelector(illegalBitsSelectorPos + 1); - int yLastSelectorPos = actorClass->getVarSelector(illegalBitsSelectorPos + 2); + // The Actor class contains the init, xLast and yLast selectors, which + // we reference directly. It's always in script 998, so we need to + // explicitly load it here. + if ((getSciVersion() >= SCI_VERSION_1_EGA_ONLY)) { + if (_resMan->testResource(ResourceId(kResourceTypeScript, 998))) { + _segMan->instantiateScript(998); - if (names.size() < (uint32)yLastSelectorPos + 1) - names.resize((uint32)yLastSelectorPos + 1); + const Object *actorClass = _segMan->getObject(_segMan->findObjectByName("Actor")); - names[xLastSelectorPos] = "xLast"; - names[yLastSelectorPos] = "yLast"; - } // if ((getSciVersion() >= SCI_VERSION_1_EGA)) + if (actorClass) { + // Find the xLast and yLast selectors, used in kDoBresen + + const int offset = (getSciVersion() < SCI_VERSION_1_1) ? 3 : 0; + // xLast and yLast always come between illegalBits and xStep + int illegalBitsSelectorPos = actorClass->locateVarSelector(_segMan, 15 + offset); // illegalBits + int xStepSelectorPos = actorClass->locateVarSelector(_segMan, 51 + offset); // xStep + if (xStepSelectorPos - illegalBitsSelectorPos != 3) { + error("illegalBits and xStep selectors aren't found in " + "known locations. illegalBits = %d, xStep = %d", + illegalBitsSelectorPos, xStepSelectorPos); + } + + int xLastSelectorPos = actorClass->getVarSelector(illegalBitsSelectorPos + 1); + int yLastSelectorPos = actorClass->getVarSelector(illegalBitsSelectorPos + 2); + + if (selectorNames.size() < (uint32)yLastSelectorPos + 1) + selectorNames.resize((uint32)yLastSelectorPos + 1); + + selectorNames[xLastSelectorPos] = "xLast"; + selectorNames[yLastSelectorPos] = "yLast"; } // if (actorClass) _segMan->uninstantiateScript(998); } // if (_resMan->testResource(ResourceId(kResourceTypeScript, 998))) + } // if ((getSciVersion() >= SCI_VERSION_1_EGA_ONLY)) + + // Find selectors from specific classes + + for (int i = 0; i < ARRAYSIZE(classReferences); i++) { + if (!_resMan->testResource(ResourceId(kResourceTypeScript, classReferences[i].script))) + continue; - if (_resMan->testResource(ResourceId(kResourceTypeScript, 981))) { - // The SysWindow class contains the open selectors, which we - // reference directly. It's always in script 981, so we need to - // explicitly load it here - _segMan->instantiateScript(981); + _segMan->instantiateScript(classReferences[i].script); - const Object *sysWindowClass = _segMan->getObject(_segMan->findObjectByName("SysWindow")); + const Object *targetClass = _segMan->getObject(_segMan->findObjectByName(classReferences[i].className)); + int targetSelectorPos = 0; + uint selectorOffset = classReferences[i].selectorOffset; - if (sysWindowClass) { - if (sysWindowClass->getMethodCount() < 2) - error("The SysWindow class has less than 2 methods"); + if (targetClass) { + if (classReferences[i].selectorType == kSelectorMethod) { + if (targetClass->getMethodCount() < selectorOffset + 1) + error("The %s class has less than %d methods (%d)", + classReferences[i].className, selectorOffset + 1, + targetClass->getMethodCount()); - // The open selector is always the second function - int openSelectorPos = sysWindowClass->getFuncSelector(1); + targetSelectorPos = targetClass->getFuncSelector(selectorOffset); + } else { + // Add the global selectors to the selector ID + selectorOffset += (getSciVersion() <= SCI_VERSION_1_LATE) ? 3 : 8; - if (names.size() < (uint32)openSelectorPos + 1) - names.resize((uint32)openSelectorPos + 1); + if (targetClass->getVarCount() < selectorOffset + 1) + error("The %s class has less than %d variables (%d)", + classReferences[i].className, selectorOffset + 1, + targetClass->getVarCount()); - names[openSelectorPos] = "open"; + targetSelectorPos = targetClass->getVarSelector(selectorOffset); } - _segMan->uninstantiateScript(981); - } // if (_resMan->testResource(ResourceId(kResourceTypeScript, 981))) - - if (g_sci->getGameId() == GID_HOYLE4) { - // The demo of Hoyle 4 is one of the few demos with lip syncing and no selector vocabulary. - // This needs two selectors, "syncTime" and "syncCue", which keep changing positions in each - // game. Usually, games with speech and lip sync have a selector vocabulary, so we don't need - // to set these two selectors, but we need for Hoyle... - if (names.size() < 276) - names.resize(276); - - names[274] = "syncTime"; - names[275] = "syncCue"; - } else if (g_sci->getGameId() == GID_PEPPER) { - // Same as above for the non-interactive demo of Pepper - if (names.size() < 539) - names.resize(539); - - names[263] = "syncTime"; - names[264] = "syncCue"; - names[538] = "startText"; - } else if (g_sci->getGameId() == GID_LAURABOW2) { - // The floppy of version needs the changeState selector set to match up with the - // CD version's workarounds. - if (names.size() < 251) - names.resize(251); - - names[144] = "changeState"; - } else if (g_sci->getGameId() == GID_CNICK_KQ) { - if (names.size() < 447) - names.resize(447); - - names[446] = "say"; - } + if (selectorNames.size() < (uint32)targetSelectorPos + 1) + selectorNames.resize((uint32)targetSelectorPos + 1); -#ifdef ENABLE_SCI32 - } else { - // SCI2+ - for (int i = 0; i < count; i++) - names[i] = sci2Selectors[i]; -#endif - } - for (const SelectorRemap *selectorRemap = sciSelectorRemap; selectorRemap->slot; ++selectorRemap) { - if (getSciVersion() >= selectorRemap->minVersion && getSciVersion() <= selectorRemap->maxVersion) { - const uint32 slot = selectorRemap->slot; - if (slot >= names.size()) - names.resize(slot + 1); - names[slot] = selectorRemap->name; + selectorNames[targetSelectorPos] = classReferences[i].selectorName; } } - return names; + // Reset the segment manager + _segMan->resetSegMan(); } } // End of namespace Sci diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp index 43d38a4979..24f3c96f62 100644 --- a/engines/sci/engine/vm.cpp +++ b/engines/sci/engine/vm.cpp @@ -118,8 +118,8 @@ static reg_t &validate_property(EngineState *s, Object *obj, int index) { if (index < 0 || (uint)index >= obj->getVarCount()) { // This is same way sierra does it and there are some games, that contain such scripts like // iceman script 998 (fred::canBeHere, executed right at the start) - debugC(kDebugLevelVM, "[VM] Invalid property #%d (out of [0..%d]) requested!", - index, obj->getVarCount()); + debugC(kDebugLevelVM, "[VM] Invalid property #%d (out of [0..%d]) requested from object %04x:%04x (%s)", + index, obj->getVarCount(), PRINT_REG(obj->getPos()), s->_segMan->getObjectName(obj->getPos())); return dummyReg; } @@ -632,18 +632,41 @@ static void logKernelCall(const KernelFunction *kernelCall, const KernelSubFunct debugN(" (%s)", s->_segMan->getObjectName(argv[parmNr])); break; case SIG_TYPE_REFERENCE: - if (kernelCall->function == kSaid) { - SegmentRef saidSpec = s->_segMan->dereference(argv[parmNr]); - if (saidSpec.isRaw) { - debugN(" ('"); - g_sci->getVocabulary()->debugDecipherSaidBlock(saidSpec.raw); - debugN("')"); + { + SegmentObj *mobj = s->_segMan->getSegmentObj(argv[parmNr].segment); + switch (mobj->getType()) { + case SEG_TYPE_HUNK: + { + HunkTable *ht = (HunkTable*)mobj; + int index = argv[parmNr].offset; + if (ht->isValidEntry(index)) { + // NOTE: This ", deleted" isn't as useful as it could + // be because it prints the status _after_ the kernel + // call. + debugN(" ('%s' hunk%s)", ht->_table[index].type, ht->_table[index].mem ? "" : ", deleted"); + } else + debugN(" (INVALID hunk ref)"); + break; + } + default: + // TODO: Any other segment types which could + // use special handling? + + if (kernelCall->function == kSaid) { + SegmentRef saidSpec = s->_segMan->dereference(argv[parmNr]); + if (saidSpec.isRaw) { + debugN(" ('"); + g_sci->getVocabulary()->debugDecipherSaidBlock(saidSpec.raw); + debugN("')"); + } else { + debugN(" (non-raw said-spec)"); + } } else { - debugN(" (non-raw said-spec)"); + debugN(" ('%s')", s->_segMan->getString(argv[parmNr]).c_str()); } - } else { - debugN(" ('%s')", s->_segMan->getString(argv[parmNr]).c_str()); + break; } + } default: break; } diff --git a/engines/sci/engine/vm_types.cpp b/engines/sci/engine/vm_types.cpp index dea4d63bf0..e606fa1f86 100644 --- a/engines/sci/engine/vm_types.cpp +++ b/engines/sci/engine/vm_types.cpp @@ -31,13 +31,11 @@ namespace Sci { -extern const char *opcodeNames[]; // from scriptdebug.cpp - reg_t reg_t::lookForWorkaround(const reg_t right) const { SciTrackOriginReply originReply; SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, arithmeticWorkarounds, &originReply); if (solution.type == WORKAROUND_NONE) - error("arithmetic operation on non-integer (%04x:%04x, %04x:%04x) from method %s::%s (script %d, room %d, localCall %x)", + error("Invalid arithmetic operation (params: %04x:%04x and %04x:%04x) from method %s::%s (script %d, room %d, localCall %x)", PRINT_REG(*this), PRINT_REG(right), originReply.objectName.c_str(), originReply.methodName.c_str(), originReply.scriptNr, g_sci->getEngineState()->currentRoomNumber(), originReply.localCallOffset); @@ -46,7 +44,7 @@ reg_t reg_t::lookForWorkaround(const reg_t right) const { } reg_t reg_t::operator+(const reg_t right) const { - if (isPointer() && isInitialized()) { + if (isPointer() && right.isNumber()) { // Pointer arithmetics. Only some pointer types make sense here SegmentObj *mobj = g_sci->getEngineState()->_segMan->getSegmentObj(segment); @@ -58,29 +56,18 @@ reg_t reg_t::operator+(const reg_t right) const { case SEG_TYPE_SCRIPT: case SEG_TYPE_STACK: case SEG_TYPE_DYNMEM: - // Make sure that we are adding an offset to the pointer - if (right.isPointer()) - return lookForWorkaround(right); return make_reg(segment, offset + right.toSint16()); default: return lookForWorkaround(right); } - } else if (isNumber() && isInitialized() && right.isPointer()) { + } else if (isNumber() && right.isPointer()) { // Adding a pointer to a number, flip the order return right + *this; + } else if (isNumber() && right.isNumber()) { + // Normal arithmetics + return make_reg(0, toSint16() + right.toSint16()); } else { - // Normal arithmetics. Make sure we're adding a number - if (right.isPointer()) - return lookForWorkaround(right); - // If the current variable is uninitialized, it'll be set - // to zero in order to perform the operation. Such a case - // happens in SQ1, room 28, when throwing the water at Orat. - if (!isInitialized()) - return make_reg(0, right.toSint16()); - else if (!right.isInitialized()) - return *this; - else - return make_reg(0, toSint16() + right.toSint16()); + return lookForWorkaround(right); } } @@ -97,24 +84,19 @@ reg_t reg_t::operator-(const reg_t right) const { reg_t reg_t::operator*(const reg_t right) const { if (isNumber() && right.isNumber()) return make_reg(0, toSint16() * right.toSint16()); - else if (!isInitialized() || !right.isInitialized()) - return NULL_REG; // unitialized variables - always return 0 else return lookForWorkaround(right); } reg_t reg_t::operator/(const reg_t right) const { - if (isNumber() && right.isNumber()) { - if (right.isNull()) - return NULL_REG; // division by zero - else - return make_reg(0, toSint16() / right.toSint16()); - } else + if (isNumber() && right.isNumber() && !right.isNull()) + return make_reg(0, toSint16() / right.toSint16()); + else return lookForWorkaround(right); } reg_t reg_t::operator%(const reg_t right) const { - if (isNumber() && right.isNumber()) { + if (isNumber() && right.isNumber() && !right.isNull()) { // Support for negative numbers was added in Iceman, and perhaps in // SCI0 0.000.685 and later. Theoretically, this wasn't really used // in SCI0, so the result is probably unpredictable. Such a case @@ -125,7 +107,7 @@ reg_t reg_t::operator%(const reg_t right) const { warning("Modulo of a negative number has been requested for SCI0. This *could* lead to issues"); int16 value = toSint16(); int16 modulo = ABS(right.toSint16()); - int16 result = (modulo != 0 ? value % modulo : 0); + int16 result = value % modulo; if (result < 0) result += modulo; return make_reg(0, result); @@ -159,6 +141,8 @@ uint16 reg_t::requireUint16() const { if (isNumber()) return toUint16(); else + // The right parameter is NULL_REG because + // we're not comparing *this with anything here. return lookForWorkaround(NULL_REG).toUint16(); } @@ -166,6 +150,8 @@ int16 reg_t::requireSint16() const { if (isNumber()) return toSint16(); else + // The right parameter is NULL_REG because + // we're not comparing *this with anything here. return lookForWorkaround(NULL_REG).toSint16(); } @@ -190,76 +176,51 @@ reg_t reg_t::operator^(const reg_t right) const { return lookForWorkaround(right); } -bool reg_t::operator>(const reg_t right) const { - if (isNumber() && right.isNumber()) - return toSint16() > right.toSint16(); - else if (isPointer() && segment == right.segment) - return toUint16() > right.toUint16(); // pointer comparison - else if (pointerComparisonWithInteger(right)) - return true; - else if (right.pointerComparisonWithInteger(*this)) - return false; - else - return lookForWorkaround(right).toSint16(); -} - -bool reg_t::operator<(const reg_t right) const { - if (isNumber() && right.isNumber()) - return toSint16() < right.toSint16(); - else if (isPointer() && segment == right.segment) - return toUint16() < right.toUint16(); // pointer comparison - else if (pointerComparisonWithInteger(right)) - return false; - else if (right.pointerComparisonWithInteger(*this)) - return true; - else - return lookForWorkaround(right).toSint16(); -} - -bool reg_t::gtU(const reg_t right) const { - if (isNumber() && right.isNumber()) - return toUint16() > right.toUint16(); - else if (isPointer() && segment == right.segment) - return toUint16() > right.toUint16(); // pointer comparison - else if (pointerComparisonWithInteger(right)) - return true; - else if (right.pointerComparisonWithInteger(*this)) - return false; - else - return lookForWorkaround(right).toSint16(); -} - -bool reg_t::ltU(const reg_t right) const { - if (isNumber() && right.isNumber()) - return toUint16() < right.toUint16(); - else if (isPointer() && segment == right.segment) - return toUint16() < right.toUint16(); // pointer comparison - else if (pointerComparisonWithInteger(right)) - return false; - else if (right.pointerComparisonWithInteger(*this)) - return true; - else +int reg_t::cmp(const reg_t right, bool treatAsUnsigned) const { + if (segment == right.segment) { // can compare things in the same segment + if (treatAsUnsigned || !isNumber()) + return toUint16() - right.toUint16(); + else + return toSint16() - right.toSint16(); + } else if (pointerComparisonWithInteger(right)) { + return 1; + } else if (right.pointerComparisonWithInteger(*this)) { + return -1; + } else return lookForWorkaround(right).toSint16(); } bool reg_t::pointerComparisonWithInteger(const reg_t right) const { - // SCI0 - SCI1.1 scripts use this to check whether a parameter is a pointer - // or a far text reference. It is used e.g. by the standard library Print - // function to distinguish two ways of calling it: + // This function handles the case where a script tries to compare a pointer + // to a number. Normally, we would not want to allow that. However, SCI0 - + // SCI1.1 scripts do this in order to distinguish references to + // external resources (which are numbers) from pointers. In + // our SCI implementation, such a check may seem pointless, as + // one can simply use the segment value to achieve this goal. + // But Sierra's SCI did not have the notion of segment IDs, so + // both pointer and numbers were simple integers. + // + // But for some things, scripts had (and have) to distinguish between + // numbers and pointers. Lacking the segment information, Sierra's + // developers resorted to a hack: If an integer is smaller than a certain + // bound, it can be assumed to be a number, otherwise it is assumed to be a + // pointer. This allowed them to implement polymorphic functions, such as + // the Print function, which can be called in two different ways, with a + // pointer or a far text reference: // // (Print "foo") // Pointer to a string // (Print 420 5) // Reference to the fifth message in text resource 420 // It works because in those games, the maximum resource number is 999, // so any parameter value above that threshold must be a pointer. // PQ2 japanese compares pointers to 2000 to find out if its a pointer - // or a resource ID. - // There are cases where game scripts check for arbitrary numbers against - // pointers, e.g.: + // or a resource ID. Thus, we check for all integers <= 2000. + // + // Some examples where game scripts check for arbitrary numbers against + // pointers: // Hoyle 3, Pachisi, when any opponent is about to talk // SQ1, room 28, when throwing water at the Orat // SQ1, room 58, when giving the ID card to the robot - // QFG3, room 440, when talking to Uhura - // Thus we check for all integers <= 2000 + // SQ4 CD, at the first game screen, when the narrator is about to speak return (isPointer() && right.isNumber() && right.offset <= 2000 && getSciVersion() <= SCI_VERSION_1_1); } diff --git a/engines/sci/engine/vm_types.h b/engines/sci/engine/vm_types.h index ac23bbe7dc..b927df339e 100644 --- a/engines/sci/engine/vm_types.h +++ b/engines/sci/engine/vm_types.h @@ -37,20 +37,20 @@ struct reg_t { SegmentId segment; uint16 offset; - bool isNull() const { - return !(offset || segment); + inline bool isNull() const { + return (offset | segment) == 0; } - uint16 toUint16() const { + inline uint16 toUint16() const { return offset; } - int16 toSint16() const { - return (int16) offset; + inline int16 toSint16() const { + return (int16)offset; } bool isNumber() const { - return !segment; + return segment == 0; } bool isPointer() const { @@ -60,7 +60,7 @@ struct reg_t { uint16 requireUint16() const; int16 requireSint16() const; - bool isInitialized() const { + inline bool isInitialized() const { return segment != 0xFFFF; } @@ -73,35 +73,39 @@ struct reg_t { return (offset != x.offset) || (segment != x.segment); } - bool operator>(const reg_t right) const; + bool operator>(const reg_t right) const { + return cmp(right, false) > 0; + } + bool operator>=(const reg_t right) const { - if (*this == right) - return true; - return *this > right; + return cmp(right, false) >= 0; + } + + bool operator<(const reg_t right) const { + return cmp(right, false) < 0; } - bool operator<(const reg_t right) const; + bool operator<=(const reg_t right) const { - if (*this == right) - return true; - return *this < right; + return cmp(right, false) <= 0; } // Same as the normal operators, but perform unsigned // integer checking - bool gtU(const reg_t right) const; + bool gtU(const reg_t right) const { + return cmp(right, true) > 0; + } + bool geU(const reg_t right) const { - if (*this == right) - return true; - return gtU(right); + return cmp(right, true) >= 0; } - bool ltU(const reg_t right) const; - bool leU(const reg_t right) const { - if (*this == right) - return true; - return ltU(right); + + bool ltU(const reg_t right) const { + return cmp(right, true) < 0; } - bool pointerComparisonWithInteger(const reg_t right) const; + bool leU(const reg_t right) const { + return cmp(right, true) <= 0; + } // Arithmetic operators reg_t operator+(const reg_t right) const; @@ -125,7 +129,17 @@ struct reg_t { reg_t operator|(const reg_t right) const; reg_t operator^(const reg_t right) const; +private: + /** + * Compares two reg_t's. + * Returns: + * - a positive number if *this > right + * - 0 if *this == right + * - a negative number if *this < right + */ + int cmp(const reg_t right, bool treatAsUnsigned) const; reg_t lookForWorkaround(const reg_t right) const; + bool pointerComparisonWithInteger(const reg_t right) const; }; static inline reg_t make_reg(SegmentId segment, uint16 offset) { diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp index 64cbc5ec90..17c9f9fa0f 100644 --- a/engines/sci/engine/workarounds.cpp +++ b/engines/sci/engine/workarounds.cpp @@ -44,8 +44,6 @@ const SciWorkaroundEntry arithmeticWorkarounds[] = { { GID_MOTHERGOOSEHIRES,90, 90, 0, "newGameButton", "select", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_ge: MUMG Deluxe, when selecting "New Game" in the main menu. It tries to compare an integer with a list. Needs to return false for the game to continue. { GID_QFG1VGA, 301, 928, 0, "Blink", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_div: when entering the inn, gets called with 1 parameter, but 2nd parameter is used for div which happens to be an object { GID_QFG2, 200, 200, 0, "astro", "messages", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_lsi: when getting asked for your name by the astrologer bug #3039879 - // TODO: The SQ5 workaround below may no longer be necessary - { GID_SQ5, 200, 939, 0, "Osc", "cycleDone", -1, 0, { WORKAROUND_FAKE, 1 } }, // op_dpToa: when going back to bridge the crew is goofing off, we get an object as cycle count SCI_WORKAROUNDENTRY_TERMINATOR }; @@ -120,6 +118,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = { { GID_MOTHERGOOSEHIRES,-1,64950, 1, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // see above { GID_PEPPER, -1, 894, 0, "Package", "doVerb", -1, 3, { WORKAROUND_FAKE, 0 } }, // using the hand on the book in the inventory - bug #3040012 { GID_PEPPER, 150, 928, 0, "Narrator", "startText", -1, 0, { WORKAROUND_FAKE, 0 } }, // happens during the non-interactive demo of Pepper + { GID_PQSWAT, -1, 64950, 0, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // Using the menu in the beginning { GID_QFG1, -1, 210, 0, "Encounter", "init", 0xbd0, 0, { WORKAROUND_FAKE, 0 } }, // hq1: going to the brigands hideout { GID_QFG1, -1, 210, 0, "Encounter", "init", 0xbe4, 0, { WORKAROUND_FAKE, 0 } }, // qfg1: going to the brigands hideout { GID_QFG1VGA, 16, 16, 0, "lassoFailed", "changeState", -1, -1, { WORKAROUND_FAKE, 0 } }, // qfg1vga: casting the "fetch" spell in the screen with the flowers, temps 0 and 1 - bug #3053268 @@ -273,10 +272,6 @@ const SciWorkaroundEntry kGraphSaveBox_workarounds[] = { // gameID, room,script,lvl, object-name, method-name, call,index, workaround const SciWorkaroundEntry kGraphRestoreBox_workarounds[] = { - { GID_LSL6, -1, 85, 0, "rScroller", "hide", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when restoring (sometimes), same as the one below - { GID_LSL6, -1, 85, 0, "lScroller", "hide", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when restoring (sometimes), same as the one below - { GID_LSL6, -1, 86, 0, "LL6Inv", "show", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when restoring, is called with hunk segment, but hunk is not allocated at that time - // ^^ TODO: check, if this is really a script error or an issue with our restore code { GID_LSL6, -1, 86, 0, "LL6Inv", "hide", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens during the game, gets called with 1 extra parameter { GID_SQ5, 850, 850, 0, NULL, "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens while playing Battle Cruiser (invalid segment) - bug #3056811 SCI_WORKAROUNDENTRY_TERMINATOR @@ -316,7 +311,7 @@ const SciWorkaroundEntry kGraphRedrawBox_workarounds[] = { const SciWorkaroundEntry kGraphUpdateBox_workarounds[] = { { GID_ECOQUEST2, 100, 333, 0, "showEcorder", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // necessary workaround for our ecorder script patch, because there isn't enough space to patch the function { GID_PQ3, 202, 202, 0, "MapEdit", "movePt", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #3038077 - { GID_PQ3, 202, 202, 0, "MapEdit", "addPt", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #3038077 + { GID_PQ3, 202, 202, 0, "MapEdit", "addPt", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #3038077 SCI_WORKAROUNDENTRY_TERMINATOR }; @@ -330,7 +325,7 @@ const SciWorkaroundEntry kIsObject_workarounds[] = { // gameID, room,script,lvl, object-name, method-name, call,index, workaround const SciWorkaroundEntry kMemory_workarounds[] = { - { GID_LAURABOW2, -1, 999, 0, "", "export 6", -1, 0, { WORKAROUND_FAKE, 0 } }, // during the intro, when exiting the train, talking to Mr. Augustini, etc. - bug #3034490 + { GID_LAURABOW2, -1, 999, 0, "", "export 6", -1, 0, { WORKAROUND_FAKE, 0 } }, // during the intro, when exiting the train (room 160), talking to Mr. Augustini, etc. - bug #3034490 SCI_WORKAROUNDENTRY_TERMINATOR }; @@ -361,7 +356,7 @@ const SciWorkaroundEntry kSetCursor_workarounds[] = { // gameID, room,script,lvl, object-name, method-name, call,index, workaround const SciWorkaroundEntry kSetPort_workarounds[] = { { GID_LSL6, 740, 740, 0, "rm740", "drawPic", -1, 0, { WORKAROUND_IGNORE, 0 } }, // ending scene, is called with additional 3 (!) parameters - { GID_QFG3, 830, 830, 0, "portalOpens", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when the portal appears during the end, bug #3040844 + { GID_QFG3, 830, 830, 0, "portalOpens", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when the portal appears during the end, gets called with 4 parameters (bug #3040844) SCI_WORKAROUNDENTRY_TERMINATOR }; @@ -373,12 +368,6 @@ const SciWorkaroundEntry kStrAt_workarounds[] = { }; // gameID, room,script,lvl, object-name, method-name, call,index, workaround -const SciWorkaroundEntry kStrCat_workarounds[] = { - { GID_LONGBOW, 210, 210, 0, "giveScroll", "changeState",0x3294, 0, { WORKAROUND_FAKE, 0 } }, // German version, when handing the scroll with the druid hand code to Marion - bug #3048054 - SCI_WORKAROUNDENTRY_TERMINATOR -}; - -// gameID, room,script,lvl, object-name, method-name, call,index, workaround const SciWorkaroundEntry kStrLen_workarounds[] = { { GID_QFG2, 210, 2, 0, "", "export 21", 0xdeb, 0, { WORKAROUND_FAKE, 0 } }, // When saying something incorrect at the WIT, an integer is passed instead of a reference - bug #3100292 SCI_WORKAROUNDENTRY_TERMINATOR @@ -386,23 +375,13 @@ const SciWorkaroundEntry kStrLen_workarounds[] = { // gameID, room,script,lvl, object-name, method-name, call,index, workaround const SciWorkaroundEntry kUnLoad_workarounds[] = { - { GID_CAMELOT, 921, 921, 1, "Script", "changeState", 0x36, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: While showing Camelot (and other places), the reference is invalid - bug #3035000 - { GID_CAMELOT, 921, 921, 1, "Script", "init", 0x36, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: When being attacked by the boar (and other places), the reference is invalid - bug #3035000 - { GID_CASTLEBRAIN, 320, 377, 0, "SWord", "upDate", -1, 0, { WORKAROUND_IGNORE, 0 } }, // after solving the cross-word-puzzle, trying to unload invalid reference - { GID_CASTLEBRAIN, 320, 377, 0, "theWord", "show", -1, 0, { WORKAROUND_IGNORE, 0 } }, // 2nd word puzzle, when exiting before solving, trying to unload invalid reference - bug #3034473 - { GID_ECOQUEST, 380, 61, 0, "gotIt", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // after talking to the dolphin the first time - { GID_ECOQUEST, 380, 69, 0, "lookAtBlackBoard", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // German version, when closing the blackboard closeup in the dolphin room - bug #3098353 - { GID_LAURABOW2, 1, 1, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #3034902 - { GID_LAURABOW2, 2, 2, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #3034902 - { GID_LAURABOW2, 4, 4, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: inside the museum, a 3rd parameter is passed by accident - bug #3034902 - { GID_LAURABOW2, 6, 6, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: after the murder, a 3rd parameter is passed by accident - bug #3034902 - { GID_LAURABOW2, 7, 7, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: after the logo is shown, a 3rd parameter is passed by accident - bug #3034902 + { GID_ECOQUEST, 380, 61, 0, "gotIt", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // CD version: after talking to the dolphin the first time, a 3rd parameter is passed by accident + { GID_ECOQUEST, 380, 69, 0, "lookAtBlackBoard", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // German version, when closing the blackboard closeup in the dolphin room, a 3rd parameter is passed by accident - bug #3098353 + { GID_LAURABOW2, -1, 1, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #3034902 { GID_LSL6, 130, 130, 0, "recruitLarryScr", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident { GID_LSL6, 740, 740, 0, "showCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during ending, 4 additional parameters are passed by accident { GID_LSL6HIRES, 130, 130, 0, "recruitLarryScr", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident - { GID_PQ3, 877, 998, 0, "View", "delete", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when getting run over on the freeway, the reference is invalid { GID_SQ1, 43, 303, 0, "slotGuy", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving ulence flats bar, parameter 1 is not passed - script error - { GID_SQ3, 2, 998, 0, "View", "delete", -1, 0, { WORKAROUND_IGNORE, 0 } }, // clicking the mouse button during the intro, after the escape pod gets pulled into the garbage freighter, the reference is invalid - bug #3050856 SCI_WORKAROUNDENTRY_TERMINATOR }; @@ -416,14 +395,14 @@ SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroun return sci3IgnoreForNow; } - EngineState *state = g_sci->getEngineState(); + const EngineState *state = g_sci->getEngineState(); ExecStack *lastCall = state->xs; - Script *local_script = state->_segMan->getScriptIfLoaded(lastCall->local_segment); - int curScriptNr = local_script->getScriptNumber(); + const Script *localScript = state->_segMan->getScriptIfLoaded(lastCall->local_segment); + int curScriptNr = localScript->getScriptNumber(); if (lastCall->debugLocalCallOffset != -1) { // if lastcall was actually a local call search back for a real call - Common::List<ExecStack>::iterator callIterator = state->_executionStack.end(); + Common::List<ExecStack>::const_iterator callIterator = state->_executionStack.end(); while (callIterator != state->_executionStack.begin()) { callIterator--; ExecStack loopCall = *callIterator; diff --git a/engines/sci/engine/workarounds.h b/engines/sci/engine/workarounds.h index 7ab73cdff2..c7721aa787 100644 --- a/engines/sci/engine/workarounds.h +++ b/engines/sci/engine/workarounds.h @@ -95,7 +95,6 @@ extern const SciWorkaroundEntry kPaletteUnsetFlag_workarounds[]; extern const SciWorkaroundEntry kSetCursor_workarounds[]; extern const SciWorkaroundEntry kSetPort_workarounds[]; extern const SciWorkaroundEntry kStrAt_workarounds[]; -extern const SciWorkaroundEntry kStrCat_workarounds[]; extern const SciWorkaroundEntry kStrLen_workarounds[]; extern const SciWorkaroundEntry kUnLoad_workarounds[]; diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp index d607a5314f..50244919c9 100644 --- a/engines/sci/event.cpp +++ b/engines/sci/event.cpp @@ -32,15 +32,10 @@ #include "sci/console.h" #include "sci/engine/state.h" #include "sci/engine/kernel.h" +#include "sci/graphics/screen.h" namespace Sci { -EventManager::EventManager(bool fontIsExtended) : _fontIsExtended(fontIsExtended), _modifierStates(0) { -} - -EventManager::~EventManager() { -} - struct ScancodeRow { int offset; const char *keys; @@ -52,27 +47,6 @@ const ScancodeRow s_scancodeRows[] = { { 0x2c, "ZXCVBNM,./" } }; -static int altify(int ch) { - // Calculates a PC keyboard scancode from a character */ - int row; - int c = toupper((char)ch); - - for (row = 0; row < ARRAYSIZE(s_scancodeRows); row++) { - const char *keys = s_scancodeRows[row].keys; - int offset = s_scancodeRows[row].offset; - - while (*keys) { - if (*keys == c) - return offset << 8; - - offset++; - keys++; - } - } - - return ch; -} - const byte codepagemap_88591toDOS[0x80] = { '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x8x '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x9x @@ -120,147 +94,182 @@ const SciKeyConversion keyMappings[] = { { Common::KEYCODE_KP_DIVIDE , '/' , '/' }, }; +struct MouseEventConversion { + Common::EventType commonType; + short sciType; + short data; +}; + +const MouseEventConversion mouseEventMappings[] = { + { Common::EVENT_LBUTTONDOWN, SCI_EVENT_MOUSE_PRESS, 1 }, + { Common::EVENT_RBUTTONDOWN, SCI_EVENT_MOUSE_PRESS, 2 }, + { Common::EVENT_MBUTTONDOWN, SCI_EVENT_MOUSE_PRESS, 3 }, + { Common::EVENT_LBUTTONUP, SCI_EVENT_MOUSE_RELEASE, 1 }, + { Common::EVENT_LBUTTONUP, SCI_EVENT_MOUSE_RELEASE, 2 }, + { Common::EVENT_LBUTTONUP, SCI_EVENT_MOUSE_RELEASE, 3 } +}; + +EventManager::EventManager(bool fontIsExtended) : _fontIsExtended(fontIsExtended) { +} + +EventManager::~EventManager() { +} + +static int altify(int ch) { + // Calculates a PC keyboard scancode from a character */ + int row; + int c = toupper((char)ch); + + for (row = 0; row < ARRAYSIZE(s_scancodeRows); row++) { + const char *keys = s_scancodeRows[row].keys; + int offset = s_scancodeRows[row].offset; + + while (*keys) { + if (*keys == c) + return offset << 8; + + offset++; + keys++; + } + } + + return ch; +} + SciEvent EventManager::getScummVMEvent() { - SciEvent input = { SCI_EVENT_NONE, 0, 0, 0 }; + SciEvent input = { SCI_EVENT_NONE, 0, 0, 0, Common::Point(0, 0) }; + SciEvent noEvent = { SCI_EVENT_NONE, 0, 0, 0, Common::Point(0, 0) }; Common::EventManager *em = g_system->getEventManager(); Common::Event ev; bool found = em->pollEvent(ev); - Common::Point p = ev.mouse; // Don't generate events for mouse movement - while (found && ev.type == Common::EVENT_MOUSEMOVE) { + while (found && ev.type == Common::EVENT_MOUSEMOVE) found = em->pollEvent(ev); + + // Save the mouse position + // + // We call getMousePos of the event manager here, since we also want to + // store the mouse position in case of keyboard events, which do not feature + // any mouse position information itself. + // This should be safe, since the mouse position in the event manager should + // only be updated when a mouse related event has been taken from the queue + // via pollEvent. + // We also adjust the position based on the scaling of the screen. + Common::Point mousePos = em->getMousePos(); + g_sci->_gfxScreen->adjustBackUpscaledCoordinates(mousePos.y, mousePos.x); + + noEvent.mousePos = input.mousePos = mousePos; + + if (!found || ev.type == Common::EVENT_MOUSEMOVE) + return noEvent; + + if (ev.type == Common::EVENT_QUIT) { + input.type = SCI_EVENT_QUIT; + return input; } - if (found && ev.type != Common::EVENT_MOUSEMOVE) { - int modifiers = em->getModifierState(); - bool numlockOn = (ev.kbd.flags & Common::KBD_NUM); - - // We add the modifier key status to buckybits - //TODO: SCI_EVM_INSERT - - input.modifiers = - ((modifiers & Common::KBD_ALT) ? SCI_KEYMOD_ALT : 0) | - ((modifiers & Common::KBD_CTRL) ? SCI_KEYMOD_CTRL : 0) | - ((modifiers & Common::KBD_SHIFT) ? SCI_KEYMOD_LSHIFT | SCI_KEYMOD_RSHIFT : 0) | - ((ev.kbd.flags & Common::KBD_CAPS) ? SCI_KEYMOD_CAPSLOCK : 0) | - ((ev.kbd.flags & Common::KBD_SCRL) ? SCI_KEYMOD_SCRLOCK : 0) | - _modifierStates; - - switch (ev.type) { - // Keyboard events - case Common::EVENT_KEYDOWN: - input.data = ev.kbd.keycode; - input.character = ev.kbd.ascii; - - // Debug console - if (ev.kbd.hasFlags(Common::KBD_CTRL) && ev.kbd.keycode == Common::KEYCODE_d) { - // Open debug console - Console *con = g_sci->getSciDebugger(); - con->attach(); - - // Clear keyboard event - input.type = SCI_EVENT_NONE; - input.character = 0; - input.data = 0; - input.modifiers = 0; - - return input; - } + // Handle mouse events + for (int i = 0; i < ARRAYSIZE(mouseEventMappings); i++) { + if (mouseEventMappings[i].commonType == ev.type) { + input.type = mouseEventMappings[i].sciType; + input.data = mouseEventMappings[i].data; + return input; + } + } - if (!(input.data & 0xFF00)) { - // Directly accept most common keys without conversion - input.type = SCI_EVENT_KEYBOARD; - if ((input.character >= 0x80) && (input.character <= 0xFF)) { - // If there is no extended font, we will just clear the current event - // Sierra SCI actually accepted those characters, but didn't display them inside textedit-controls - // because the characters were missing inside the font(s) - // We filter them out for non-multilingual games because of that - if (!_fontIsExtended) { - input.type = SCI_EVENT_NONE; - input.character = 0; - input.data = 0; - input.modifiers = 0; - return input; - } - // We get a 8859-1 character, we need dos (cp850/437) character for multilingual sci01 games - input.character = codepagemap_88591toDOS[input.character & 0x7f]; - } - if (input.data == Common::KEYCODE_TAB) { - // Tab - input.type = SCI_EVENT_KEYBOARD; - input.data = SCI_KEY_TAB; - if (input.modifiers & (SCI_KEYMOD_LSHIFT | SCI_KEYMOD_RSHIFT)) - input.character = SCI_KEY_SHIFT_TAB; - else - input.character = SCI_KEY_TAB; - } - if (input.data == Common::KEYCODE_DELETE) { - // Delete key - input.type = SCI_EVENT_KEYBOARD; - input.data = input.character = SCI_KEY_DELETE; - } - } else if ((input.data >= Common::KEYCODE_F1) && input.data <= Common::KEYCODE_F10) { - // F1-F10 - input.type = SCI_EVENT_KEYBOARD; - // SCI_K_F1 == 59 << 8 - // SCI_K_SHIFT_F1 == 84 << 8 - input.data = SCI_KEY_F1 + ((input.data - Common::KEYCODE_F1)<<8); - if (input.modifiers & (SCI_KEYMOD_LSHIFT | SCI_KEYMOD_RSHIFT)) - input.character = input.data + 0x1900; - else - input.character = input.data; - } else { - // Special keys that need conversion - input.type = SCI_EVENT_KEYBOARD; - for (int i = 0; i < ARRAYSIZE(keyMappings); i++) { - if (keyMappings[i].scummVMKey == ev.kbd.keycode) { - input.data = numlockOn ? keyMappings[i].sciKeyNumlockOn : keyMappings[i].sciKeyNumlockOff; - break; - } - } - input.character = input.data; - } - break; + // If we reached here, make sure that it's a keydown event + if (ev.type != Common::EVENT_KEYDOWN) + return noEvent; - // Mouse events - case Common::EVENT_LBUTTONDOWN: - input.type = SCI_EVENT_MOUSE_PRESS; - input.data = 1; - break; - case Common::EVENT_RBUTTONDOWN: - input.type = SCI_EVENT_MOUSE_PRESS; - input.data = 2; - break; - case Common::EVENT_MBUTTONDOWN: - input.type = SCI_EVENT_MOUSE_PRESS; - input.data = 3; - break; - case Common::EVENT_LBUTTONUP: - input.type = SCI_EVENT_MOUSE_RELEASE; - input.data = 1; - break; - case Common::EVENT_RBUTTONUP: - input.type = SCI_EVENT_MOUSE_RELEASE; - input.data = 2; - break; - case Common::EVENT_MBUTTONUP: - input.type = SCI_EVENT_MOUSE_RELEASE; - input.data = 3; - break; - - // Misc events - case Common::EVENT_QUIT: - input.type = SCI_EVENT_QUIT; - break; + // Check for Control-D (debug console) + if (ev.kbd.hasFlags(Common::KBD_CTRL) && ev.kbd.keycode == Common::KEYCODE_d) { + // Open debug console + Console *con = g_sci->getSciDebugger(); + con->attach(); + return noEvent; + } - default: - break; + // Process keyboard events + + int modifiers = em->getModifierState(); + bool numlockOn = (ev.kbd.flags & Common::KBD_NUM); + + input.data = ev.kbd.keycode; + input.character = ev.kbd.ascii; + input.type = SCI_EVENT_KEYBOARD; + + input.modifiers = + ((modifiers & Common::KBD_ALT) ? SCI_KEYMOD_ALT : 0) | + ((modifiers & Common::KBD_CTRL) ? SCI_KEYMOD_CTRL : 0) | + ((modifiers & Common::KBD_SHIFT) ? SCI_KEYMOD_LSHIFT | SCI_KEYMOD_RSHIFT : 0); + + // Caps lock and Scroll lock have been removed, cause we already handle upper + // case keys ad Scroll lock doesn't seem to be used anywhere + //((ev.kbd.flags & Common::KBD_CAPS) ? SCI_KEYMOD_CAPSLOCK : 0) | + //((ev.kbd.flags & Common::KBD_SCRL) ? SCI_KEYMOD_SCRLOCK : 0) | + + if (!(input.data & 0xFF00)) { + // Directly accept most common keys without conversion + if ((input.character >= 0x80) && (input.character <= 0xFF)) { + // If there is no extended font, we will just clear the + // current event. + // Sierra SCI actually accepted those characters, but + // didn't display them inside text edit controls because + // the characters were missing inside the font(s). + // We filter them out for non-multilingual games because + // of that. + if (!_fontIsExtended) + return noEvent; + // Convert 8859-1 characters to DOS (cp850/437) for + // multilingual SCI01 games + input.character = codepagemap_88591toDOS[input.character & 0x7f]; + } + if (input.data == Common::KEYCODE_TAB) { + input.character = input.data = SCI_KEY_TAB; + if (modifiers & Common::KBD_SHIFT) + input.character = SCI_KEY_SHIFT_TAB; + } + if (input.data == Common::KEYCODE_DELETE) + input.data = input.character = SCI_KEY_DELETE; + } else if ((input.data >= Common::KEYCODE_F1) && input.data <= Common::KEYCODE_F10) { + // SCI_K_F1 == 59 << 8 + // SCI_K_SHIFT_F1 == 84 << 8 + input.character = input.data = SCI_KEY_F1 + ((input.data - Common::KEYCODE_F1)<<8); + if (modifiers & Common::KBD_SHIFT) + input.character = input.data + 0x1900; + } else { + // Special keys that need conversion + for (int i = 0; i < ARRAYSIZE(keyMappings); i++) { + if (keyMappings[i].scummVMKey == ev.kbd.keycode) { + input.character = input.data = numlockOn ? keyMappings[i].sciKeyNumlockOn : keyMappings[i].sciKeyNumlockOff; + break; + } } } + + // When Ctrl AND Alt are pressed together with a regular key, Linux will give us control-key, Windows will give + // us the actual key. My opinion is that windows is right, because under DOS the keys worked the same, anyway + // we support the other case as well + if ((modifiers & Common::KBD_SHIFT) && input.character > 0 && input.character < 27) + input.character += 96; // 0x01 -> 'a' + + if (getSciVersion() <= SCI_VERSION_1_MIDDLE) { + // TODO: find out if altify is also not needed for sci1late+, couldnt find any game that uses those keys + // Scancodify if appropriate + if (modifiers & Common::KBD_ALT) + input.character = altify(input.character); + else if ((modifiers & Common::KBD_CTRL) && input.character > 0 && input.character < 27) + input.character += 96; // 0x01 -> 'a' + } + // If no actual key was pressed (e.g. if only a modifier key was pressed), + // ignore the event + if (!input.character) + return noEvent; + return input; } @@ -282,8 +291,7 @@ void EventManager::updateScreen() { } SciEvent EventManager::getSciEvent(unsigned int mask) { - //sci_event_t error_event = { SCI_EVT_ERROR, 0, 0, 0 }; - SciEvent event = { 0, 0, 0, 0 }; + SciEvent event = { 0, 0, 0, 0, Common::Point(0, 0) }; EventManager::updateScreen(); @@ -304,9 +312,8 @@ SciEvent EventManager::getSciEvent(unsigned int mask) { event = *iter; // If not peeking at the queue, remove the event - if (!(mask & SCI_EVENT_PEEK)) { + if (!(mask & SCI_EVENT_PEEK)) _events.erase(iter); - } } else { // No event found: we must return a SCI_EVT_NONE event. @@ -314,29 +321,6 @@ SciEvent EventManager::getSciEvent(unsigned int mask) { // there is no need to change it. } - if (event.type == SCI_EVENT_KEYBOARD) { - // Do we still have to translate the key? - - // When Ctrl AND Alt are pressed together with a regular key, Linux will give us control-key, Windows will give - // us the actual key. My opinion is that windows is right, because under DOS the keys worked the same, anyway - // we support the other case as well - if (event.modifiers & SCI_KEYMOD_ALT) { - if (event.character < 27) - event.character += 96; // 0x01 -> 'a' - } - - if (getSciVersion() <= SCI_VERSION_1_MIDDLE) { - // TODO: find out if altify is also not needed for sci1late+, couldnt find any game that uses those keys - // Scancodify if appropriate - if (event.modifiers & SCI_KEYMOD_ALT) { - event.character = altify(event.character); - } else if (event.modifiers & SCI_KEYMOD_CTRL) { - if (event.character < 27) - event.character += 96; // 0x01 -> 'a' - } - } - } - return event; } diff --git a/engines/sci/event.h b/engines/sci/event.h index 7c83476294..be0322f2a4 100644 --- a/engines/sci/event.h +++ b/engines/sci/event.h @@ -27,6 +27,7 @@ #define SCI_EVENT_H #include "common/list.h" +#include "common/rect.h" namespace Sci { @@ -46,6 +47,13 @@ struct SciEvent { * PC keyboard scancodes. */ short character; + + /** + * The mouse position at the time the event was created. + * + * These are display coordinates! + */ + Common::Point mousePos; }; /*Values for type*/ @@ -56,11 +64,8 @@ struct SciEvent { #define SCI_EVENT_DIRECTION (1<<6) #define SCI_EVENT_SAID (1<<7) /*Fake values for other events*/ -#define SCI_EVENT_ERROR (1<<10) #define SCI_EVENT_QUIT (1<<11) #define SCI_EVENT_PEEK (1<<15) -/* The QUIT event may be used to signal an external 'quit' command being -** issued to the gfx driver. */ #define SCI_EVENT_ANY 0x7fff /* Keycodes of special keys: */ @@ -121,7 +126,6 @@ private: SciEvent getScummVMEvent(); const bool _fontIsExtended; - int _modifierStates; Common::List<SciEvent> _events; }; diff --git a/engines/sci/graphics/animate.cpp b/engines/sci/graphics/animate.cpp index 5f9c5b86d6..e02b27c788 100644 --- a/engines/sci/graphics/animate.cpp +++ b/engines/sci/graphics/animate.cpp @@ -352,14 +352,7 @@ void GfxAnimate::update() { it->signal &= ~(kSignalViewUpdated | kSignalNoUpdate); } else if (it->signal & kSignalStopUpdate) { it->signal &= ~kSignalStopUpdate; - if (g_sci->getGameId() == GID_HOYLE3 && g_sci->isDemo()) { - // WORKAROUND: The demo of Hoyle 3 doesn't seem to set this - // flag in this case. Not setting this fixes a large number - // of incorrect animate entries being drawn on top of dialog - // boxes (bug #3036763) - } else { - it->signal |= kSignalNoUpdate; - } + it->signal |= kSignalNoUpdate; } } @@ -371,7 +364,7 @@ void GfxAnimate::update() { it->showBitsFlag = true; it->signal &= ~(kSignalStopUpdate | kSignalViewUpdated | kSignalNoUpdate | kSignalForceUpdate); - if ((it->signal & kSignalIgnoreActor) == 0) { + if (!(it->signal & kSignalIgnoreActor)) { rect = it->celRect; rect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(it->priority) - 1, rect.top, rect.bottom - 1); _paint16->fillRect(rect, GFX_SCREEN_MASK_CONTROL, 0, 0, 15); @@ -563,7 +556,7 @@ void GfxAnimate::addToPicDrawCels() { // draw corresponding cel _paint16->drawCel(view, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY); - if ((it->signal & kSignalIgnoreActor) == 0) { + if (!(it->signal & kSignalIgnoreActor)) { it->celRect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(it->priority) - 1, it->celRect.top, it->celRect.bottom - 1); _paint16->fillRect(it->celRect, GFX_SCREEN_MASK_CONTROL, 0, 0, 15); } @@ -637,10 +630,10 @@ void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t // beginUpdate()/endUpdate() were introduced SCI1. // Calling those for SCI0 will work most of the time but breaks minor // stuff like percentage bar of qfg1ega at the character skill screen. - if (getSciVersion() >= SCI_VERSION_1_EGA) + if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY) _ports->beginUpdate(_ports->_picWind); update(); - if (getSciVersion() >= SCI_VERSION_1_EGA) + if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY) _ports->endUpdate(_ports->_picWind); } diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp index b085654d02..3b95a5c955 100644 --- a/engines/sci/graphics/cursor.cpp +++ b/engines/sci/graphics/cursor.cpp @@ -26,6 +26,7 @@ #include "common/config-manager.h" #include "common/events.h" #include "common/macresman.h" +#include "common/memstream.h" #include "common/system.h" #include "common/util.h" #include "graphics/cursorman.h" @@ -38,6 +39,7 @@ #include "sci/graphics/coordadjuster.h" #include "sci/graphics/view.h" #include "sci/graphics/cursor.h" +#include "sci/graphics/maciconbar.h" namespace Sci { @@ -434,9 +436,17 @@ void GfxCursor::kernelSetMacCursor(GuiResourceId viewNum, int loopNum, int celNu if (_macCursorRemap.empty()) { // QFG1/Freddy/Hoyle4 use a straight viewNum->cursor ID mapping - // KQ6 seems to use this mapping for its cursors - if (g_sci->getGameId() == GID_KQ6) - viewNum = loopNum * 1000 + celNum; + // KQ6 uses this mapping for its cursors + if (g_sci->getGameId() == GID_KQ6) { + if (viewNum == 990) // Inventory Cursors + viewNum = loopNum * 16 + celNum + 2000; + else if (viewNum == 998) // Regular Cursors + viewNum = celNum + 1000; + else // Unknown cursor, ignored + return; + } + if (g_sci->hasMacIconBar()) + g_sci->_gfxMacIconBar->setInventoryIcon(viewNum); } else { // If we do have the list, we'll be using a remap based on what the // scripts have given us. @@ -485,8 +495,8 @@ void GfxCursor::kernelSetMacCursor(GuiResourceId viewNum, int loopNum, int celNu cursorBitmap[i * 8 + b] = 0; // Doesn't matter, just is transparent } - uint16 hotspotX = READ_BE_UINT16(data); - uint16 hotspotY = READ_BE_UINT16(data + 2); + uint16 hotspotY = READ_BE_UINT16(data); + uint16 hotspotX = READ_BE_UINT16(data + 2); static const byte cursorPalette[] = { 0x00, 0x00, 0x00, 0xff, 0xff, 0xff }; @@ -498,11 +508,12 @@ void GfxCursor::kernelSetMacCursor(GuiResourceId viewNum, int loopNum, int celNu // Mac crsr cursor byte *cursorBitmap, *palette; int width, height, hotspotX, hotspotY, palSize, keycolor; - Common::MacResManager::convertCrsrCursor(resource->data, resource->size, &cursorBitmap, &width, &height, &hotspotX, &hotspotY, &keycolor, true, &palette, &palSize); + Common::MemoryReadStream resStream(resource->data, resource->size); + Common::MacResManager::convertCrsrCursor(&resStream, &cursorBitmap, width, height, hotspotX, hotspotY, keycolor, true, &palette, palSize); CursorMan.replaceCursor(cursorBitmap, width, height, hotspotX, hotspotY, keycolor); CursorMan.replaceCursorPalette(palette, 0, palSize); - free(cursorBitmap); - free(palette); + delete[] cursorBitmap; + delete[] palette; } kernelShow(); diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index ab4a2c9c1a..fbfd140e6b 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -337,6 +337,8 @@ static int16 GetLongest(const char *text, int16 maxWidth, GfxFont *font) { maxChars = curCharCount; // return count up to (but not including) breaking space break; } + if (width + font->getCharWidth(curChar) > maxWidth) + break; width += font->getCharWidth(curChar); curCharCount++; } @@ -592,9 +594,9 @@ void GfxFrameout::kernelFrameout() { const char *txt = text.c_str(); // HACK. The plane sometimes doesn't contain the correct width. This // hack breaks the dialog options when speaking with Grace, but it's - // the best we got up to now. - // TODO: Remove this, and figure out why the plane in question isn't - // initialized correctly (its width is 0). + // the best we got up to now. This happens because of the unimplemented + // kTextWidth function in SCI32. + // TODO: Remove this once kTextWidth has been implemented. uint16 w = it->planeRect.width() >= 20 ? it->planeRect.width() : _screen->getWidth() - 10; int16 charCount; diff --git a/engines/sci/graphics/helpers.h b/engines/sci/graphics/helpers.h index f6cb214a2b..343f3c7e6e 100644 --- a/engines/sci/graphics/helpers.h +++ b/engines/sci/graphics/helpers.h @@ -45,6 +45,10 @@ typedef int GuiResourceId; // is a resource-number and -1 means no parameter giv typedef int16 TextAlignment; +#define PORTS_FIRSTWINDOWID 2 +#define PORTS_FIRSTSCRIPTWINDOWID 3 + + struct Port { uint16 id; int16 top, left; @@ -62,6 +66,8 @@ struct Port { fontHeight(0), fontId(0), greyedOutput(false), penClr(0), backClr(0xFF), penMode(0), counterTillFree(0) { } + + bool isWindow() const { return id >= PORTS_FIRSTWINDOWID && id != 0xFFFF; } }; struct Window : public Port, public Common::Serializable { @@ -132,12 +138,14 @@ struct PalSchedule { uint32 schedule; }; +// Game view types, sorted by the number of colors enum ViewType { - kViewUnknown, - kViewEga, - kViewVga, - kViewVga11, - kViewAmiga + kViewUnknown, // uninitialized, or non-SCI + kViewEga, // EGA SCI0/SCI1 and Amiga SCI0/SCI1 ECS 16 colors + kViewAmiga, // Amiga SCI1 ECS 32 colors + kViewAmiga64, // Amiga SCI1 AGA 64 colors (i.e. Longbow) + kViewVga, // VGA SCI1 256 colors + kViewVga11 // VGA SCI1.1 and newer 256 colors }; } // End of namespace Sci diff --git a/engines/sci/graphics/maciconbar.cpp b/engines/sci/graphics/maciconbar.cpp index f0931e0121..6cf4f269a7 100644 --- a/engines/sci/graphics/maciconbar.cpp +++ b/engines/sci/graphics/maciconbar.cpp @@ -27,6 +27,7 @@ #include "sci/engine/kernel.h" #include "sci/engine/selector.h" #include "sci/engine/state.h" +#include "sci/event.h" #include "sci/graphics/maciconbar.h" #include "sci/graphics/palette.h" #include "sci/graphics/screen.h" @@ -40,9 +41,22 @@ namespace Sci { GfxMacIconBar::GfxMacIconBar() { _lastX = 0; + + if (g_sci->getGameId() == GID_FREDDYPHARKAS) + _inventoryIndex = 5; + else + _inventoryIndex = 4; + + _inventoryIcon = 0; + _allDisabled = true; } GfxMacIconBar::~GfxMacIconBar() { + if (_inventoryIcon) { + _inventoryIcon->free(); + delete _inventoryIcon; + } + for (uint32 i = 0; i < _iconBarItems.size(); i++) { if (_iconBarItems[i].nonSelectedImage) { _iconBarItems[i].nonSelectedImage->free(); @@ -62,7 +76,12 @@ void GfxMacIconBar::addIcon(reg_t obj) { item.object = obj; item.nonSelectedImage = createImage(iconIndex, false); - item.selectedImage = createImage(iconIndex, true); + + if (iconIndex != _inventoryIndex) + item.selectedImage = createImage(iconIndex, true); + else + item.selectedImage = 0; + item.enabled = true; // Start after the main viewing window and add a two pixel buffer @@ -82,17 +101,33 @@ void GfxMacIconBar::drawIcons() { // Draw the icons to the bottom of the screen for (uint32 i = 0; i < _iconBarItems.size(); i++) - redrawIcon(i); + drawIcon(i, false); } -void GfxMacIconBar::redrawIcon(uint16 iconIndex) { +void GfxMacIconBar::drawIcon(uint16 iconIndex, bool selected) { if (iconIndex >= _iconBarItems.size()) return; - if (_iconBarItems[iconIndex].enabled) - drawEnabledImage(_iconBarItems[iconIndex].nonSelectedImage, _iconBarItems[iconIndex].rect); - else - drawDisabledImage(_iconBarItems[iconIndex].nonSelectedImage, _iconBarItems[iconIndex].rect); + Common::Rect rect = _iconBarItems[iconIndex].rect; + + if (isIconEnabled(iconIndex)) { + if (selected) + drawEnabledImage(_iconBarItems[iconIndex].selectedImage, rect); + else + drawEnabledImage(_iconBarItems[iconIndex].nonSelectedImage, rect); + } else + drawDisabledImage(_iconBarItems[iconIndex].nonSelectedImage, rect); + + if ((iconIndex == _inventoryIndex) && _inventoryIcon) { + Common::Rect invRect = Common::Rect(0, 0, _inventoryIcon->w, _inventoryIcon->h); + invRect.moveTo(rect.left, rect.top); + invRect.translate((rect.width() - invRect.width()) / 2, (rect.height() - invRect.height()) / 2); + + if (isIconEnabled(iconIndex)) + drawEnabledImage(_inventoryIcon, invRect); + else + drawDisabledImage(_inventoryIcon, invRect); + } } void GfxMacIconBar::drawEnabledImage(Graphics::Surface *surface, const Common::Rect &rect) { @@ -128,30 +163,49 @@ void GfxMacIconBar::drawDisabledImage(Graphics::Surface *surface, const Common:: void GfxMacIconBar::drawSelectedImage(uint16 iconIndex) { assert(iconIndex <= _iconBarItems.size()); - // TODO + drawEnabledImage(_iconBarItems[iconIndex].selectedImage, _iconBarItems[iconIndex].rect); } bool GfxMacIconBar::isIconEnabled(uint16 iconIndex) const { if (iconIndex >= _iconBarItems.size()) return false; - return _iconBarItems[iconIndex].enabled; + return !_allDisabled && _iconBarItems[iconIndex].enabled; } -void GfxMacIconBar::setIconEnabled(uint16 iconIndex, bool enabled) { - if (iconIndex == 0xffff) { - for (uint32 i = 0; i < _iconBarItems.size(); i++) - _iconBarItems[i].enabled = enabled; - } else if (iconIndex < _iconBarItems.size()) { +void GfxMacIconBar::setIconEnabled(int16 iconIndex, bool enabled) { + if (iconIndex < 0) + _allDisabled = !enabled; + else if (iconIndex < (int)_iconBarItems.size()) { _iconBarItems[iconIndex].enabled = enabled; } } -Graphics::Surface *GfxMacIconBar::createImage(uint32 iconIndex, bool isSelected) { - Graphics::PictDecoder pictDecoder(Graphics::PixelFormat::createFormatCLUT8()); - ResourceType type = isSelected ? kResourceTypeMacIconBarPictS : kResourceTypeMacIconBarPictN; +void GfxMacIconBar::setInventoryIcon(int16 icon) { + Graphics::Surface *surface = 0; + + if (icon >= 0) + surface = loadPict(ResourceId(kResourceTypeMacPict, icon)); + + if (_inventoryIcon) { + // Free old inventory icon if we're removing the inventory icon + // or setting a new one. + if ((icon < 0) || surface) { + _inventoryIcon->free(); + delete _inventoryIcon; + _inventoryIcon = 0; + } + } - Resource *res = g_sci->getResMan()->findResource(ResourceId(type, iconIndex + 1), false); + if (surface) + _inventoryIcon = surface; + + drawIcon(_inventoryIndex, false); +} + +Graphics::Surface *GfxMacIconBar::loadPict(ResourceId id) { + Graphics::PictDecoder pictDecoder(Graphics::PixelFormat::createFormatCLUT8()); + Resource *res = g_sci->getResMan()->findResource(id, false); if (!res || res->size == 0) return 0; @@ -165,6 +219,11 @@ Graphics::Surface *GfxMacIconBar::createImage(uint32 iconIndex, bool isSelected) return surface; } +Graphics::Surface *GfxMacIconBar::createImage(uint32 iconIndex, bool isSelected) { + ResourceType type = isSelected ? kResourceTypeMacIconBarPictS : kResourceTypeMacIconBarPictN; + return loadPict(ResourceId(type, iconIndex + 1)); +} + void GfxMacIconBar::remapColors(Graphics::Surface *surf, byte *palette) { byte *pixels = (byte *)surf->pixels; @@ -180,4 +239,59 @@ void GfxMacIconBar::remapColors(Graphics::Surface *surf, byte *palette) { } } +bool GfxMacIconBar::pointOnIcon(uint32 iconIndex, Common::Point point) { + return _iconBarItems[iconIndex].rect.contains(point); +} + +reg_t GfxMacIconBar::handleEvents() { + // Peek event queue for a mouse button press + EventManager *evtMgr = g_sci->getEventManager(); + SciEvent evt = evtMgr->getSciEvent(SCI_EVENT_MOUSE_PRESS | SCI_EVENT_PEEK); + + // No mouse press found + if (evt.type == SCI_EVENT_NONE) + return NULL_REG; + + // If the mouse is not over the icon bar, return + if (evt.mousePos.y < g_sci->_gfxScreen->getHeight()) + return NULL_REG; + + // Remove event from queue + evtMgr->getSciEvent(SCI_EVENT_MOUSE_PRESS); + + // Mouse press on the icon bar, check the icon rectangles + uint iconNr; + for (iconNr = 0; iconNr < _iconBarItems.size(); iconNr++) { + if (pointOnIcon(iconNr, evt.mousePos) && isIconEnabled(iconNr)) + break; + } + + // Mouse press not on an icon + if (iconNr == _iconBarItems.size()) + return NULL_REG; + + drawIcon(iconNr, true); + bool isSelected = true; + + // Wait for mouse release + while (evt.type != SCI_EVENT_MOUSE_RELEASE) { + // Mimic behavior of SSCI when moving mouse with button held down + if (isSelected != pointOnIcon(iconNr, evt.mousePos)) { + isSelected = !isSelected; + drawIcon(iconNr, isSelected); + } + + evt = evtMgr->getSciEvent(SCI_EVENT_MOUSE_RELEASE); + g_system->delayMillis(10); + } + + drawIcon(iconNr, false); + + // If user moved away from the icon, we do nothing + if (pointOnIcon(iconNr, evt.mousePos)) + return _iconBarItems[iconNr].object; + + return NULL_REG; +} + } // End of namespace Sci diff --git a/engines/sci/graphics/maciconbar.h b/engines/sci/graphics/maciconbar.h index 0db9454eb7..3ac5475147 100644 --- a/engines/sci/graphics/maciconbar.h +++ b/engines/sci/graphics/maciconbar.h @@ -43,10 +43,9 @@ public: void addIcon(reg_t obj); void drawIcons(); - void redrawIcon(uint16 index); - void drawSelectedImage(uint16 index); - bool isIconEnabled(uint16 index) const; - void setIconEnabled(uint16 index, bool enabled); + void setIconEnabled(int16 index, bool enabled); + void setInventoryIcon(int16 icon); + reg_t handleEvents(); private: struct IconBarItem { @@ -59,12 +58,20 @@ private: Common::Array<IconBarItem> _iconBarItems; uint32 _lastX; + uint16 _inventoryIndex; + Graphics::Surface *_inventoryIcon; + bool _allDisabled; + Graphics::Surface *loadPict(ResourceId id); Graphics::Surface *createImage(uint32 iconIndex, bool isSelected); void remapColors(Graphics::Surface *surf, byte *palette); + void drawIcon(uint16 index, bool selected); + void drawSelectedImage(uint16 index); + bool isIconEnabled(uint16 index) const; void drawEnabledImage(Graphics::Surface *surface, const Common::Rect &rect); void drawDisabledImage(Graphics::Surface *surface, const Common::Rect &rect); + bool pointOnIcon(uint32 iconIndex, Common::Point point); }; } // End of namespace Sci diff --git a/engines/sci/graphics/menu.cpp b/engines/sci/graphics/menu.cpp index 3b9119c52f..50ba77e832 100644 --- a/engines/sci/graphics/menu.cpp +++ b/engines/sci/graphics/menu.cpp @@ -399,12 +399,10 @@ void GfxMenu::calculateMenuAndItemWidth() { reg_t GfxMenu::kernelSelect(reg_t eventObject, bool pauseSound) { int16 eventType = readSelectorValue(_segMan, eventObject, SELECTOR(type)); int16 keyPress, keyModifier; - Common::Point mousePosition; GuiMenuItemList::iterator itemIterator = _itemList.begin(); GuiMenuItemList::iterator itemEnd = _itemList.end(); GuiMenuItemEntry *itemEntry = NULL; bool forceClaimed = false; - EngineState *s; switch (eventType) { case SCI_EVENT_KEYBOARD: @@ -438,8 +436,6 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject, bool pauseSound) { break; case SCI_EVENT_SAID: - // HACK: should be removed as soon as said() is cleaned up - s = g_sci->getEngineState(); while (itemIterator != itemEnd) { itemEntry = *itemIterator; @@ -451,7 +447,7 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject, bool pauseSound) { continue; } - if (said(s, saidSpec, 0) != SAID_NO_MATCH) + if (said(saidSpec, 0) != SAID_NO_MATCH) break; } itemIterator++; @@ -460,15 +456,17 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject, bool pauseSound) { itemEntry = NULL; break; - case SCI_EVENT_MOUSE_PRESS: - mousePosition = _cursor->getPosition(); + case SCI_EVENT_MOUSE_PRESS: { + Common::Point mousePosition; + mousePosition.x = readSelectorValue(_segMan, eventObject, SELECTOR(x)); + mousePosition.y = readSelectorValue(_segMan, eventObject, SELECTOR(y)); if (mousePosition.y < 10) { interactiveStart(pauseSound); itemEntry = interactiveWithMouse(); interactiveEnd(pauseSound); forceClaimed = true; } - break; + } break; } if (!_menuSaveHandle.isNull()) { @@ -718,7 +716,6 @@ GuiMenuItemEntry *GfxMenu::interactiveWithKeyboard() { uint16 newItemId = _curItemId; GuiMenuItemEntry *curItemEntry = findItem(_curMenuId, _curItemId); GuiMenuItemEntry *newItemEntry = curItemEntry; - Common::Point mousePosition; // We don't 100% follow Sierra here: we select last item instead of // selecting first item of first menu every time. Also sierra sci didn't @@ -796,9 +793,9 @@ GuiMenuItemEntry *GfxMenu::interactiveWithKeyboard() { } break; - case SCI_EVENT_MOUSE_PRESS: - mousePosition = _cursor->getPosition(); - if (_cursor->getPosition().y < 10) { + case SCI_EVENT_MOUSE_PRESS: { + Common::Point mousePosition = curEvent.mousePos; + if (mousePosition.y < 10) { // Somewhere on the menubar newMenuId = mouseFindMenuSelection(mousePosition); if (newMenuId) { @@ -827,7 +824,8 @@ GuiMenuItemEntry *GfxMenu::interactiveWithKeyboard() { } newItemId = curItemEntry->id; } - break; + } break; + case SCI_EVENT_NONE: g_sci->sleep(2500 / 1000); break; @@ -843,7 +841,6 @@ GuiMenuItemEntry *GfxMenu::interactiveWithMouse() { SciEvent curEvent; uint16 newMenuId = 0, newItemId = 0; uint16 curMenuId = 0, curItemId = 0; - Common::Point mousePosition = _cursor->getPosition(); bool firstMenuChange = true; GuiMenuItemEntry *curItemEntry = NULL; @@ -874,7 +871,7 @@ GuiMenuItemEntry *GfxMenu::interactiveWithMouse() { } // Find out where mouse is currently pointing to - mousePosition = _cursor->getPosition(); + Common::Point mousePosition = curEvent.mousePos; if (mousePosition.y < 10) { // Somewhere on the menubar newMenuId = mouseFindMenuSelection(mousePosition); @@ -914,6 +911,13 @@ void GfxMenu::kernelDrawStatus(const char *text, int16 colorPen, int16 colorBack _ports->moveTo(0, 1); _text16->DrawStatus(text); _paint16->bitsShow(_ports->_menuBarRect); + // Also draw the line under the status bar. Normally, this is never drawn, + // but we need it to be drawn because Dr. Brain 1 Mac draws over it when + // it displays the icon bar. SSCI used negative rectangles to erase the + // area after drawing the icon bar, but this is a much cleaner way of + // achieving the same effect. + _paint16->fillRect(_ports->_menuLine, 1, 0); + _paint16->bitsShow(_ports->_menuLine); _ports->setPort(oldPort); } diff --git a/engines/sci/graphics/paint.cpp b/engines/sci/graphics/paint.cpp index 50b0534ba7..c347da3c0f 100644 --- a/engines/sci/graphics/paint.cpp +++ b/engines/sci/graphics/paint.cpp @@ -43,9 +43,6 @@ GfxPaint::~GfxPaint() { void GfxPaint::kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, bool animationBlackoutFlag, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo) { } -void GfxPaint::kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle) { -} - void GfxPaint::kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control) { } diff --git a/engines/sci/graphics/paint.h b/engines/sci/graphics/paint.h index 994bc4e5e9..a79e8993c2 100644 --- a/engines/sci/graphics/paint.h +++ b/engines/sci/graphics/paint.h @@ -36,7 +36,6 @@ public: virtual ~GfxPaint(); virtual void kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, bool animationBlackoutFlag, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo); - virtual void kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle); virtual void kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control); }; diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp index 935dd4e62e..33986f1196 100644 --- a/engines/sci/graphics/paint16.cpp +++ b/engines/sci/graphics/paint16.cpp @@ -360,7 +360,7 @@ void GfxPaint16::bitsRestore(reg_t memoryHandle) { if (memoryPtr) { _screen->bitsRestore(memoryPtr); - _segMan->freeHunkEntry(memoryHandle); + bitsFree(memoryHandle); } } } @@ -532,20 +532,7 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) { case SCI_DISPLAY_RESTOREUNDER: bitsGetRect(argv[0], &rect); rect.translate(-_ports->getPort()->left, -_ports->getPort()->top); - if (g_sci->getGameId() == GID_PQ3 && g_sci->getEngineState()->currentRoomNumber() == 29) { - // WORKAROUND: PQ3 calls this without calling the associated - // kDisplay(SCI_DISPLAY_SAVEUNDER) call before. Theoretically, - // this would result in no rect getting restored. However, we - // still maintain a pointer from the previous room, resulting - // in invalidated content being restored on screen, and causing - // graphics glitches. Thus, we simply don't restore a rect in - // that room. The correct fix for this would be to erase hunk - // pointers when changing rooms, but this will suffice for now, - // as restoring from a totally invalid pointer is very rare. - // Fixes bug #3037945. - } else { - bitsRestore(argv[0]); - } + bitsRestore(argv[0]); kernelGraphRedrawBox(rect); // finishing loop argc = 0; @@ -581,7 +568,10 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) { // now drawing the text _text16->Size(rect, text, -1, width); rect.moveTo(_ports->getPort()->curLeft, _ports->getPort()->curTop); - if (getSciVersion() >= SCI_VERSION_1_LATE) { + // Note: This code has been found in SCI1 middle and newer games. It was + // previously only for SCI1 late and newer, but the LSL1 interpreter contains + // this code. + if (getSciVersion() >= SCI_VERSION_1_MIDDLE) { int16 leftPos = rect.right <= _screen->getWidth() ? 0 : _screen->getWidth() - rect.right; int16 topPos = rect.bottom <= _screen->getHeight() ? 0 : _screen->getHeight() - rect.bottom; _ports->move(leftPos, topPos); diff --git a/engines/sci/graphics/paint32.cpp b/engines/sci/graphics/paint32.cpp index aa3bf8dfb3..69a278eb8b 100644 --- a/engines/sci/graphics/paint32.cpp +++ b/engines/sci/graphics/paint32.cpp @@ -65,17 +65,6 @@ void GfxPaint32::kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, b delete picture; } -// This is "hacked" together, because its only used by debug command -void GfxPaint32::kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle) { - GfxView *view = _cache->getView(viewId); - Common::Rect celRect(50, 50, 50, 50); - Common::Rect translatedRect; - celRect.bottom += view->getHeight(loopNo, celNo); - celRect.right += view->getWidth(loopNo, celNo); - view->draw(celRect, celRect, celRect, loopNo, celNo, 255, 0, false); - _screen->copyRectToScreen(celRect); -} - void GfxPaint32::kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control) { _screen->drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y, color, priority, control); } diff --git a/engines/sci/graphics/paint32.h b/engines/sci/graphics/paint32.h index 3fa9d11d9b..e412bdf1c4 100644 --- a/engines/sci/graphics/paint32.h +++ b/engines/sci/graphics/paint32.h @@ -45,7 +45,6 @@ public: void fillRect(Common::Rect rect, byte color); void kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, bool animationBlackoutFlag, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo); - void kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle); void kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control); private: diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp index 0f95a3f2fb..5a6b1859cd 100644 --- a/engines/sci/graphics/palette.cpp +++ b/engines/sci/graphics/palette.cpp @@ -76,6 +76,24 @@ GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen, bool useMergi #ifdef ENABLE_SCI32 _clutTable = 0; #endif + + switch (_resMan->getViewType()) { + case kViewEga: + _totalScreenColors = 16; + break; + case kViewAmiga: + _totalScreenColors = 32; + break; + case kViewAmiga64: + _totalScreenColors = 64; + break; + case kViewVga: + case kViewVga11: + _totalScreenColors = 256; + break; + default: + error("GfxPalette: Unknown view type"); + } } GfxPalette::~GfxPalette() { @@ -97,7 +115,7 @@ bool GfxPalette::isMerging() { void GfxPalette::setDefault() { if (_resMan->getViewType() == kViewEga) setEGA(); - else if (_resMan->isAmiga32color()) + else if (_resMan->getViewType() == kViewAmiga) setAmiga(); else kernelSetFromResource(999, true); @@ -418,10 +436,6 @@ void GfxPalette::getSys(Palette *pal) { } void GfxPalette::setOnScreen() { - // We dont change palette at all times for amiga - if (_resMan->isAmiga32color()) - return; - copySysPaletteToScreen(); } @@ -847,19 +861,28 @@ void GfxPalette::palVaryProcess(int signal, bool setPalette) { } } +static inline uint getMacColorDiff(byte r1, byte g1, byte b1, byte r2, byte g2, byte b2) { + // Use the difference of the top 4 bits and add together the differences + return ABS((r2 & 0xf0) - (r1 & 0xf0)) + ABS((g2 & 0xf0) - (g1 & 0xf0)) + ABS((b2 & 0xf0) - (b1 & 0xf0)); +} + byte GfxPalette::findMacIconBarColor(byte r, byte g, byte b) { // Find the best color for use with the Mac icon bar + // Check white, then the palette colors, and then black - byte found = 0xFF; - uint diff = 0xFFFFFFFF; + // Try white first + byte found = 0xff; + uint diff = getMacColorDiff(r, g, b, 0xff, 0xff, 0xff); + + if (diff == 0) + return found; - for (uint16 i = 0; i < 256; i++) { - // Use the difference of the top 4 bits - int dr = (_macClut[i * 3 ] & 0xf0) - (r & 0xf0); - int dg = (_macClut[i * 3 + 1] & 0xf0) - (g & 0xf0); - int db = (_macClut[i * 3 + 2] & 0xf0) - (b & 0xf0); + // Go through the main colors of the CLUT + for (uint16 i = 1; i < 255; i++) { + if (!colorIsFromMacClut(i)) + continue; - uint cdiff = ABS(dr) + ABS(dg) + ABS(db); + uint cdiff = getMacColorDiff(r, g, b, _macClut[i * 3], _macClut[i * 3 + 1], _macClut[i * 3 + 2]); if (cdiff == 0) return i; @@ -869,6 +892,10 @@ byte GfxPalette::findMacIconBarColor(byte r, byte g, byte b) { } } + // Also check black here + if (getMacColorDiff(r, g, b, 0, 0, 0) < diff) + return 0; + return found; } diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h index 317401ac1f..d2e5151d6a 100644 --- a/engines/sci/graphics/palette.h +++ b/engines/sci/graphics/palette.h @@ -54,6 +54,7 @@ public: bool merge(Palette *pFrom, bool force, bool forceRealMerge); uint16 matchColor(byte r, byte g, byte b); void getSys(Palette *pal); + uint16 getTotalColorCount() const { return _totalScreenColors; } void setOnScreen(); void copySysPaletteToScreen(); @@ -123,6 +124,7 @@ private: uint16 _palVaryTicks; int _palVaryPaused; int _palVarySignal; + uint16 _totalScreenColors; void loadMacIconBarPalette(); byte *_macClut; diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp index 7e71c1a258..38742b2510 100644 --- a/engines/sci/graphics/picture.cpp +++ b/engines/sci/graphics/picture.cpp @@ -36,6 +36,8 @@ namespace Sci { +//#define DEBUG_PICTURE_DRAW + GfxPicture::GfxPicture(ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxPorts *ports, GfxScreen *screen, GfxPalette *palette, GuiResourceId resourceId, bool EGAdrawingVisualize) : _resMan(resMan), _coordAdjuster(coordAdjuster), _ports(ports), _screen(screen), _palette(palette), _resourceId(resourceId), _EGAdrawingVisualize(EGAdrawingVisualize) { assert(resourceId != -1); @@ -222,19 +224,20 @@ void GfxPicture::drawSci32Vga(int16 celNo, int16 drawX, int16 drawY, int16 pictu } #endif +extern void unpackCelData(byte *inBuffer, byte *celBitmap, byte clearColor, int pixelCount, int rlePos, int literalPos, ViewType viewType, uint16 width, bool isMacSci11ViewData); + void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 drawX, int16 drawY, int16 pictureX) { byte *celBitmap = NULL; byte *ptr = NULL; byte *headerPtr = inbuffer + headerPos; byte *rlePtr = inbuffer + rlePos; - byte *literalPtr = inbuffer + literalPos; int16 displaceX, displaceY; byte priority = _addToFlag ? _priority : 0; byte clearColor; bool compression = true; - byte curByte, runLength; + byte curByte; int16 y, lastY, x, leftX, rightX; - int pixelNr, pixelCount; + int pixelCount; uint16 width, height; #ifdef ENABLE_SCI32 @@ -245,12 +248,11 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos height = READ_LE_UINT16(headerPtr + 2); displaceX = (signed char)headerPtr[4]; displaceY = (unsigned char)headerPtr[5]; - if (_resourceType == SCI_PICTURE_TYPE_SCI11) { + if (_resourceType == SCI_PICTURE_TYPE_SCI11) // SCI1.1 uses hardcoded clearcolor for pictures, even if cel header specifies otherwise clearColor = _screen->getColorWhite(); - } else { + else clearColor = headerPtr[6]; - } #ifdef ENABLE_SCI32 } else { width = READ_SCI11ENDIAN_UINT16(headerPtr + 0); @@ -266,91 +268,18 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos if (displaceX || displaceY) error("unsupported embedded cel-data in picture"); + // We will unpack cel-data into a temporary buffer and then plot it to screen + // That needs to be done cause a mirrored picture may be requested pixelCount = width * height; celBitmap = new byte[pixelCount]; if (!celBitmap) error("Unable to allocate temporary memory for picture drawing"); - if (compression) { - // We will unpack cel-data into a temporary buffer and then plot it to screen - // That needs to be done cause a mirrored picture may be requested - memset(celBitmap, clearColor, pixelCount); - pixelNr = 0; - ptr = celBitmap; - if (literalPos == 0) { - // decompression for data that has only one stream (vecor embedded view data) - switch (_resMan->getViewType()) { - case kViewEga: - while (pixelNr < pixelCount) { - curByte = *rlePtr++; - runLength = curByte >> 4; - memset(ptr + pixelNr, curByte & 0x0F, MIN<uint16>(runLength, pixelCount - pixelNr)); - pixelNr += runLength; - } - break; - case kViewVga: - case kViewVga11: - while (pixelNr < pixelCount) { - curByte = *rlePtr++; - runLength = curByte & 0x3F; - switch (curByte & 0xC0) { - case 0: // copy bytes as-is - while (runLength-- && pixelNr < pixelCount) - ptr[pixelNr++] = *rlePtr++; - break; - case 0x80: // fill with color - memset(ptr + pixelNr, *rlePtr++, MIN<uint16>(runLength, pixelCount - pixelNr)); - pixelNr += runLength; - break; - case 0xC0: // fill with transparent - pixelNr += runLength; - break; - } - } - break; - case kViewAmiga: - while (pixelNr < pixelCount) { - curByte = *rlePtr++; - if (curByte & 0x07) { // fill with color - runLength = curByte & 0x07; - curByte = curByte >> 3; - while (runLength-- && pixelNr < pixelCount) { - ptr[pixelNr++] = curByte; - } - } else { // fill with transparent - runLength = curByte >> 3; - pixelNr += runLength; - } - } - break; - - default: - error("Unsupported picture viewtype"); - } - } else { - // decompression for data that has two separate streams (probably SCI 1.1 picture) - while (pixelNr < pixelCount) { - curByte = *rlePtr++; - runLength = curByte & 0x3F; - switch (curByte & 0xC0) { - case 0: // copy bytes as-is - while (runLength-- && pixelNr < pixelCount) - ptr[pixelNr++] = *literalPtr++; - break; - case 0x80: // fill with color - memset(ptr + pixelNr, *literalPtr++, MIN<uint16>(runLength, pixelCount - pixelNr)); - pixelNr += runLength; - break; - case 0xC0: // fill with transparent - pixelNr += runLength; - break; - } - } - } - } else { + if (compression) + unpackCelData(inbuffer, celBitmap, clearColor, pixelCount, rlePos, literalPos, _resMan->getViewType(), width, false); + else // No compression (some SCI32 pictures) memcpy(celBitmap, rlePtr, pixelCount); - } Common::Rect displayArea = _coordAdjuster->pictureGetDisplayArea(); @@ -443,6 +372,7 @@ enum { PIC_OP_OPX = 0xfe, PIC_OP_TERMINATE = 0xff }; + #define PIC_OP_FIRST PIC_OP_SET_COLOR enum { @@ -465,6 +395,47 @@ enum { PIC_OPX_VGA_PRIORITY_TABLE_EXPLICIT = 4 }; +#ifdef DEBUG_PICTURE_DRAW +const char *picOpcodeNames[] = { + "Set color", + "Disable visual", + "Set priority", + "Disable priority", + "Short patterns", + "Medium lines", + "Long lines", + "Short lines", + "Fill", + "Set pattern", + "Absolute pattern", + "Set control", + "Disable control", + "Medium patterns", + "Extended opcode", + "Terminate" +}; + +const char *picExOpcodeNamesEGA[] = { + "Set palette entries", + "Set palette", + "Mono0", + "Mono1", + "Mono2", + "Mono3", + "Mono4", + "Embedded view", + "Set priority table" +}; + +const char *picExOpcodeNamesVGA[] = { + "Set palette entries", + "Embedded view", + "Set palette", + "Set priority table (eqdist)", + "Set priority table (explicit)" +}; +#endif + #define PIC_EGAPALETTE_COUNT 4 #define PIC_EGAPALETTE_SIZE 40 #define PIC_EGAPALETTE_TOTALSIZE PIC_EGAPALETTE_COUNT*PIC_EGAPALETTE_SIZE @@ -543,7 +514,9 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { // Drawing while (curPos < dataSize) { - //warning("%X at %d", data[curPos], curPos); +#ifdef DEBUG_PICTURE_DRAW + debug("Picture op: %X (%s) at %d", data[curPos], picOpcodeNames[data[curPos] - 0xF0], curPos); +#endif switch (pic_op = data[curPos++]) { case PIC_OP_SET_COLOR: pic_color = data[curPos++]; @@ -681,6 +654,9 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { case PIC_OP_OPX: // Extended functions if (isEGA) { +#ifdef DEBUG_PICTURE_DRAW + debug("* Picture ex op: %X (%s) at %d", data[curPos], picExOpcodeNamesEGA[data[curPos]], curPos); +#endif switch (pic_op = data[curPos++]) { case PIC_OPX_EGA_SET_PALETTE_ENTRIES: while (vectorIsNonOpcode(data[curPos])) { @@ -726,6 +702,9 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { error("Unsupported sci1 extended pic-operation %X", pic_op); } } else { +#ifdef DEBUG_PICTURE_DRAW + debug("* Picture ex op: %X (%s) at %d", data[curPos], picExOpcodeNamesVGA[data[curPos]], curPos); +#endif switch (pic_op = data[curPos++]) { case PIC_OPX_VGA_SET_PALETTE_ENTRIES: while (vectorIsNonOpcode(data[curPos])) { @@ -733,7 +712,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { } break; case PIC_OPX_VGA_SET_PALETTE: - if (_resMan->isAmiga32color()) { + if (_resMan->getViewType() == kViewAmiga || _resMan->getViewType() == kViewAmiga64) { if ((data[curPos] == 0x00) && (data[curPos + 1] == 0x01) && ((data[curPos + 32] & 0xF0) != 0xF0)) { // Left-Over VGA palette, we simply ignore it curPos += 256 + 4 + 1024; @@ -780,7 +759,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { case GID_SQ3: switch (_resourceId) { case 154: // SQ3: intro, ship gets sucked in - _screen->ditherForceMemorial(0xD0); + _screen->ditherForceDitheredColor(0xD0); break; default: break; @@ -866,6 +845,8 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by byte matchedMask, matchMask; int16 w, e, a_set, b_set; + bool isEGA = (_resMan->getViewType() == kViewEga); + p.x = x + curPort->left; p.y = y + curPort->top; stack.push(p); @@ -874,6 +855,18 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by byte searchPriority = _screen->getPriority(p.x, p.y); byte searchControl = _screen->getControl(p.x, p.y); + if (isEGA) { + // In EGA games a pixel in the framebuffer is only 4 bits. We store + // a full byte per pixel to allow undithering, but when comparing + // pixels for flood-fill purposes, we should only compare the + // visible color of a pixel. + + if ((x ^ y) & 1) + searchColor = (searchColor ^ (searchColor >> 4)) & 0x0F; + else + searchColor = searchColor & 0x0F; + } + // This logic was taken directly from sierra sci, floodfill will get aborted on various occations if (screenMask & GFX_SCREEN_MASK_VISUAL) { if ((color == _screen->getColorWhite()) || (searchColor != _screen->getColorWhite())) @@ -913,20 +906,20 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by int b = curPort->rect.bottom + curPort->top - 1; while (stack.size()) { p = stack.pop(); - if ((matchedMask = _screen->isFillMatch(p.x, p.y, matchMask, searchColor, searchPriority, searchControl)) == 0) // already filled + if ((matchedMask = _screen->isFillMatch(p.x, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA)) == 0) // already filled continue; _screen->putPixel(p.x, p.y, screenMask, color, priority, control); w = p.x; e = p.x; // moving west and east pointers as long as there is a matching color to fill - while (w > l && (matchedMask = _screen->isFillMatch(w - 1, p.y, matchMask, searchColor, searchPriority, searchControl))) + while (w > l && (matchedMask = _screen->isFillMatch(w - 1, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA))) _screen->putPixel(--w, p.y, screenMask, color, priority, control); - while (e < r && (matchedMask = _screen->isFillMatch(e + 1, p.y, matchMask, searchColor, searchPriority, searchControl))) + while (e < r && (matchedMask = _screen->isFillMatch(e + 1, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA))) _screen->putPixel(++e, p.y, screenMask, color, priority, control); // checking lines above and below for possible flood targets a_set = b_set = 0; while (w <= e) { - if (p.y > t && (matchedMask = _screen->isFillMatch(w, p.y - 1, matchMask, searchColor, searchPriority, searchControl))) { // one line above + if (p.y > t && (matchedMask = _screen->isFillMatch(w, p.y - 1, matchMask, searchColor, searchPriority, searchControl, isEGA))) { // one line above if (a_set == 0) { p1.x = w; p1.y = p.y - 1; @@ -936,7 +929,7 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by } else a_set = 0; - if (p.y < b && (matchedMask = _screen->isFillMatch(w, p.y + 1, matchMask, searchColor, searchPriority, searchControl))) { // one line below + if (p.y < b && (matchedMask = _screen->isFillMatch(w, p.y + 1, matchMask, searchColor, searchPriority, searchControl, isEGA))) { // one line below if (b_set == 0) { p1.x = w; p1.y = p.y + 1; diff --git a/engines/sci/graphics/ports.cpp b/engines/sci/graphics/ports.cpp index 57c76126c0..9ac8d103fc 100644 --- a/engines/sci/graphics/ports.cpp +++ b/engines/sci/graphics/ports.cpp @@ -246,8 +246,10 @@ void GfxPorts::beginUpdate(Window *wnd) { PortList::iterator it = _windowList.reverse_begin(); const PortList::iterator end = Common::find(_windowList.begin(), _windowList.end(), wnd); while (it != end) { - // FIXME: We also store Port objects in the window list. - // We should add a check that we really only pass windows here... + // We also store Port objects in the window list, but they + // shouldn't be encountered during this iteration. + assert((*it)->isWindow()); + updateWindow((Window *)*it); --it; } @@ -263,11 +265,16 @@ void GfxPorts::endUpdate(Window *wnd) { assert(it != end); while (++it != end) { - // FIXME: We also store Port objects in the window list. - // We should add a check that we really only pass windows here... + // We also store Port objects in the window list, but they + // shouldn't be encountered during this iteration. + assert((*it)->isWindow()); + updateWindow((Window *)*it); } + if (getSciVersion() < SCI_VERSION_1_EGA_ONLY) + g_sci->_gfxPaint16->kernelGraphRedrawBox(_curPort->rect); + setPort(oldPort); } @@ -300,7 +307,14 @@ Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restor } _windowsById[id] = pwnd; - if (style & SCI_WINDOWMGR_STYLE_TOPMOST) + + // KQ1sci, KQ4, iceman, QfG2 always add windows to the back of the list. + // KQ5CD checks style. + // Hoyle3-demo also always adds to the back (#3036763). + bool forceToBack = (getSciVersion() <= SCI_VERSION_1_EGA_ONLY) || + (g_sci->getGameId() == GID_HOYLE3 && g_sci->isDemo()); + + if (!forceToBack && (style & SCI_WINDOWMGR_STYLE_TOPMOST)) _windowList.push_front(pwnd); else _windowList.push_back(pwnd); @@ -311,7 +325,7 @@ Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restor // bit of the left dimension in their interpreter. It seems Sierra did it // for EGA byte alignment (EGA uses 1 byte for 2 pixels) and left it in // their interpreter even in the newer VGA games. - r.left = r.left & 0x7FFE; + r.left = r.left & 0xFFFE; if (r.width() > _screen->getWidth()) { // We get invalid dimensions at least at the end of sq3 (script bug!). @@ -347,25 +361,50 @@ Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restor } pwnd->dims = r; - const Common::Rect *wmprect = &_wmgrPort->rect; + + // Clip window, if needed + Common::Rect wmprect = _wmgrPort->rect; + // Handle a special case for Dr. Brain 1 Mac. When hovering the mouse cursor + // over the status line on top, the game scripts try to draw the game's icon + // bar above the current port, by specifying a negative window top, so that + // the end result will be drawn 10 pixels above the current port. This is a + // hack by Sierra, and is only limited to user style windows. Normally, we + // should not clip, same as what Sierra does. However, this will result in + // having invalid rectangles with negative coordinates. For this reason, we + // adjust the containing rectangle instead. + if (pwnd->dims.top < 0 && g_sci->getPlatform() == Common::kPlatformMacintosh && + (style & SCI_WINDOWMGR_STYLE_USER) && _wmgrPort->top + pwnd->dims.top >= 0) { + // Offset the final rect top by the requested pixels + wmprect.top += pwnd->dims.top; + } + int16 oldtop = pwnd->dims.top; int16 oldleft = pwnd->dims.left; - if (wmprect->top > pwnd->dims.top) - pwnd->dims.moveTo(pwnd->dims.left, wmprect->top); - if (wmprect->bottom < pwnd->dims.bottom) - pwnd->dims.moveTo(pwnd->dims.left, wmprect->bottom - pwnd->dims.bottom + pwnd->dims.top); + if (wmprect.top > pwnd->dims.top) + pwnd->dims.moveTo(pwnd->dims.left, wmprect.top); + + if (wmprect.bottom < pwnd->dims.bottom) + pwnd->dims.moveTo(pwnd->dims.left, wmprect.bottom - pwnd->dims.bottom + pwnd->dims.top); - if (wmprect->right < pwnd->dims.right) - pwnd->dims.moveTo(wmprect->right + pwnd->dims.left - pwnd->dims.right, pwnd->dims.top); + if (wmprect.right < pwnd->dims.right) + pwnd->dims.moveTo(wmprect.right + pwnd->dims.left - pwnd->dims.right, pwnd->dims.top); - if (wmprect->left > pwnd->dims.left) - pwnd->dims.moveTo(wmprect->left, pwnd->dims.top); + if (wmprect.left > pwnd->dims.left) + pwnd->dims.moveTo(wmprect.left, pwnd->dims.top); pwnd->rect.moveTo(pwnd->rect.left + pwnd->dims.left - oldleft, pwnd->rect.top + pwnd->dims.top - oldtop); + if (restoreRect == 0) pwnd->restoreRect = pwnd->dims; + if (pwnd->restoreRect.top < 0 && g_sci->getPlatform() == Common::kPlatformMacintosh && + (style & SCI_WINDOWMGR_STYLE_USER) && _wmgrPort->top + pwnd->restoreRect.top >= 0) { + // Special case for Dr. Brain 1 Mac (check above), applied to the + // restore rectangle. + pwnd->restoreRect.moveTo(pwnd->restoreRect.left, wmprect.top); + } + if (draw) drawWindow(pwnd); setPort((Port *)pwnd); diff --git a/engines/sci/graphics/ports.h b/engines/sci/graphics/ports.h index 21c6d31ebd..9faee2be8d 100644 --- a/engines/sci/graphics/ports.h +++ b/engines/sci/graphics/ports.h @@ -37,9 +37,6 @@ class GfxPaint16; class GfxScreen; class GfxText16; -#define PORTS_FIRSTWINDOWID 2 -#define PORTS_FIRSTSCRIPTWINDOWID 3 - // window styles enum { SCI_WINDOWMGR_STYLE_TRANSPARENT = (1 << 0), @@ -49,6 +46,9 @@ enum { SCI_WINDOWMGR_STYLE_USER = (1 << 7) }; +typedef Common::List<Port *> PortList; +typedef Common::Array<Port *> PortArray; + /** * Ports class, includes all port managment for SCI0->SCI1.1 games. Ports are some sort of windows in SCI * this class also handles adjusting coordinates to a specific port @@ -115,8 +115,12 @@ public: virtual void saveLoadWithSerializer(Common::Serializer &ser); + /** The list of open 'windows' (and ports), in visual order. */ + PortList _windowList; + private: - typedef Common::List<Port *> PortList; + /** The list of all open 'windows' (and ports), ordered by their id. */ + PortArray _windowsById; SegManager *_segMan; GfxPaint16 *_paint16; @@ -130,12 +134,6 @@ private: // counts windows that got disposed but are not freed yet uint16 _freeCounter; - /** The list of open 'windows' (and ports), in visual order. */ - PortList _windowList; - - /** The list of all open 'windows' (and ports), ordered by their id. */ - Common::Array<Port *> _windowsById; - Common::Rect _bounds; // Priority Bands related variables diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp index b019853a2b..89463badd0 100644 --- a/engines/sci/graphics/screen.cpp +++ b/engines/sci/graphics/screen.cpp @@ -113,7 +113,7 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { _unditherState = true; _fontIsUpscaled = false; - if (_resMan->isVGA() || (_resMan->isAmiga32color())) { + if (_resMan->getViewType() != kViewEga) { // It is not 100% accurate to set white to be 255 for Amiga 32-color // games. But 255 is defined as white in our SCI at all times, so it // doesn't matter. @@ -353,12 +353,30 @@ byte GfxScreen::getControl(int x, int y) { return _controlScreen[y * _width + x]; } -byte GfxScreen::isFillMatch(int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con) { +byte GfxScreen::isFillMatch(int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA) { int offset = y * _width + x; byte match = 0; - if ((screenMask & GFX_SCREEN_MASK_VISUAL) && *(_visualScreen + offset) == t_color) - match |= GFX_SCREEN_MASK_VISUAL; + // FIXME: + if (screenMask & GFX_SCREEN_MASK_VISUAL) { + if (!isEGA) { + if (*(_visualScreen + offset) == t_color) + match |= GFX_SCREEN_MASK_VISUAL; + } else { + // In EGA games a pixel in the framebuffer is only 4 bits. We store + // a full byte per pixel to allow undithering, but when comparing + // pixels for flood-fill purposes, we should only compare the + // visible color of a pixel. + + byte c = *(_visualScreen + offset); + if ((x ^ y) & 1) + c = (c ^ (c >> 4)) & 0x0F; + else + c = c & 0x0F; + if (c == t_color) + match |= GFX_SCREEN_MASK_VISUAL; + } + } if ((screenMask & GFX_SCREEN_MASK_PRIORITY) && *(_priorityScreen + offset) == t_pri) match |= GFX_SCREEN_MASK_PRIORITY; if ((screenMask & GFX_SCREEN_MASK_CONTROL) && *(_controlScreen + offset) == t_con) @@ -565,7 +583,7 @@ void GfxScreen::dither(bool addToFlag) { } } else { if (!addToFlag) - memset(&_unditherMemorial, 0, sizeof(_unditherMemorial)); + memset(&_ditheredPicColors, 0, sizeof(_ditheredPicColors)); // Do dithering on visual screen and put decoded but undithered byte onto display-screen for (y = 0; y < _height; y++) { for (x = 0; x < _width; x++) { @@ -573,7 +591,7 @@ void GfxScreen::dither(bool addToFlag) { if (color & 0xF0) { color ^= color << 4; // remember dither combination for cel-undithering - _unditherMemorial[color]++; + _ditheredPicColors[color]++; // if decoded color wants do dither with black on left side, we turn it around // otherwise the normal ega color would get used for display if (color & 0xF0) { @@ -600,18 +618,17 @@ void GfxScreen::dither(bool addToFlag) { } } -// Force a color combination into memorial -void GfxScreen::ditherForceMemorial(byte color) { - _unditherMemorial[color] = 256; +void GfxScreen::ditherForceDitheredColor(byte color) { + _ditheredPicColors[color] = 256; } void GfxScreen::debugUnditherSetState(bool flag) { _unditherState = flag; } -int16 *GfxScreen::unditherGetMemorial() { +int16 *GfxScreen::unditherGetDitheredBgColors() { if (_unditherState) - return (int16 *)&_unditherMemorial; + return (int16 *)&_ditheredPicColors; else return NULL; } @@ -725,6 +742,7 @@ uint16 GfxScreen::getLowResScreenHeight() { case GID_FREDDYPHARKAS: case GID_KQ5: case GID_KQ6: + case GID_SQ1: return 190; default: break; diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h index fc1f38ed41..0bb15eddf3 100644 --- a/engines/sci/graphics/screen.h +++ b/engines/sci/graphics/screen.h @@ -51,7 +51,7 @@ enum GfxScreenMasks { }; enum { - SCI_SCREEN_UNDITHERMEMORIAL_SIZE = 256 + DITHERED_BG_COLORS_SIZE = 256 }; /** @@ -99,7 +99,7 @@ public: byte getVisual(int x, int y); byte getPriority(int x, int y); byte getControl(int x, int y); - byte isFillMatch(int16 x, int16 y, byte drawMask, byte t_color, byte t_pri, byte t_con); + byte isFillMatch(int16 x, int16 y, byte drawMask, byte t_color, byte t_pri, byte t_con, bool isEGA); int bitsGetDataSize(Common::Rect rect, byte mask); void bitsSave(Common::Rect rect, byte mask, byte *memoryPtr); @@ -112,9 +112,11 @@ public: void adjustBackUpscaledCoordinates(int16 &y, int16 &x); void dither(bool addToFlag); - void ditherForceMemorial(byte color); + + // Force a color combination as a dithered color + void ditherForceDitheredColor(byte color); void debugUnditherSetState(bool flag); - int16 *unditherGetMemorial(); + int16 *unditherGetDitheredBgColors(); void debugShowMap(int mapNo); @@ -146,7 +148,7 @@ private: void setVerticalShakePos(uint16 shakePos); bool _unditherState; - int16 _unditherMemorial[SCI_SCREEN_UNDITHERMEMORIAL_SIZE]; + int16 _ditheredPicColors[DITHERED_BG_COLORS_SIZE]; // These screens have the real resolution of the game engine (320x200 for // SCI0/SCI1/SCI11 games, 640x480 for SCI2 games). SCI0 games will be diff --git a/engines/sci/graphics/text16.cpp b/engines/sci/graphics/text16.cpp index 21605ccb8e..6269a58492 100644 --- a/engines/sci/graphics/text16.cpp +++ b/engines/sci/graphics/text16.cpp @@ -204,19 +204,24 @@ int16 GfxText16::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgF maxChars = curCharCount; // return count up to (but not including) breaking space break; } + // Sometimes this can go off the screen, like for example bug #3040161. + // However, we only perform this for non-Japanese games, as these require + // special handling, done after this loop. + if (width + _font->getCharWidth(curChar) > maxWidth && g_sci->getLanguage() != Common::JA_JPN) + break; width += _font->getCharWidth(curChar); curCharCount++; } + + // Text without spaces, probably Kanji/Japanese if (maxChars == 0) { - // Text w/o space, supposingly kanji maxChars = curCharCount; uint16 nextChar; // We remove the last char only, if maxWidth was actually equal width // before adding the last char. Otherwise we won't get the same cutting - // as in sierra pc98 sci. Note: changing the while() instead will NOT - // WORK. it would break all sorts of regular sci games. + // as in sierra pc98 sci. if (maxWidth == (width - _font->getCharWidth(curChar))) { maxChars--; if (curChar > 0xFF) diff --git a/engines/sci/graphics/transitions.cpp b/engines/sci/graphics/transitions.cpp index e025a2f9ab..fac9a97efe 100644 --- a/engines/sci/graphics/transitions.cpp +++ b/engines/sci/graphics/transitions.cpp @@ -39,8 +39,8 @@ namespace Sci { //#define DISABLE_TRANSITIONS // uncomment to disable room transitions (for development only! helps in testing games quickly) -GfxTransitions::GfxTransitions(GfxScreen *screen, GfxPalette *palette, bool isVGA) - : _screen(screen), _palette(palette), _isVGA(isVGA) { +GfxTransitions::GfxTransitions(GfxScreen *screen, GfxPalette *palette) + : _screen(screen), _palette(palette) { init(); } @@ -124,6 +124,7 @@ void GfxTransitions::setup(int16 number, bool blackoutFlag) { _number = SCI_TRANSITIONS_NONE; #endif _blackoutFlag = blackoutFlag; + debugC(kDebugLevelGraphics, "Transition %d, blackout %d", number, blackoutFlag); } } @@ -271,7 +272,7 @@ void GfxTransitions::doTransition(int16 number, bool blackoutFlag) { } void GfxTransitions::setNewPalette(bool blackoutFlag) { - if (!blackoutFlag && _isVGA) + if (!blackoutFlag) _palette->setOnScreen(); } diff --git a/engines/sci/graphics/transitions.h b/engines/sci/graphics/transitions.h index 674b7a8173..a8f0ca6f07 100644 --- a/engines/sci/graphics/transitions.h +++ b/engines/sci/graphics/transitions.h @@ -65,7 +65,7 @@ class Screen; */ class GfxTransitions { public: - GfxTransitions(GfxScreen *screen, GfxPalette *palette, bool isVGA); + GfxTransitions(GfxScreen *screen, GfxPalette *palette); ~GfxTransitions(); void setup(int16 number, bool blackoutFlag); @@ -97,7 +97,6 @@ private: GfxScreen *_screen; GfxPalette *_palette; - bool _isVGA; const GfxTransitionTranslateEntry *_translationTable; int16 _number; bool _blackoutFlag; diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp index 3c1f417740..b5ab6182ad 100644 --- a/engines/sci/graphics/view.cpp +++ b/engines/sci/graphics/view.cpp @@ -104,9 +104,10 @@ void GfxView::initData(GuiResourceId resourceId) { } switch (curViewType) { - case kViewEga: // View-format SCI0 (and Amiga 16 colors) + case kViewEga: // SCI0 (and Amiga 16 colors) isEGA = true; - case kViewAmiga: // View-format Amiga (32 colors) + case kViewAmiga: // Amiga ECS (32 colors) + case kViewAmiga64: // Amiga AGA (64 colors) case kViewVga: // View-format SCI1 // LoopCount:WORD MirrorMask:WORD Version:WORD PaletteOffset:WORD LoopOffset0:WORD LoopOffset1:WORD... @@ -132,7 +133,7 @@ void GfxView::initData(GuiResourceId resourceId) { // SCI1 VGA conversion games (which will get detected as SCI1EARLY/MIDDLE/LATE) have some views // with broken mapping tables. I guess those games won't use the mapping, so I rather disable it // for them - if (getSciVersion() == SCI_VERSION_1_EGA) { + if (getSciVersion() == SCI_VERSION_1_EGA_ONLY) { _EGAmapping = &_resourceData[palOffset]; for (EGAmapNr = 0; EGAmapNr < SCI_VIEW_EGAMAPPING_COUNT; EGAmapNr++) { if (memcmp(_EGAmapping, EGAmappingStraight, SCI_VIEW_EGAMAPPING_SIZE) != 0) @@ -370,22 +371,157 @@ void GfxView::getCelScaledRect(int16 loopNo, int16 celNo, int16 x, int16 y, int1 outRect.top = outRect.bottom - scaledHeight; } +void unpackCelData(byte *inBuffer, byte *celBitmap, byte clearColor, int pixelCount, int rlePos, int literalPos, ViewType viewType, uint16 width, bool isMacSci11ViewData) { + byte *outPtr = celBitmap; + byte curByte, runLength; + byte *rlePtr = inBuffer + rlePos; + // The existence of a literal position pointer signifies data with two + // separate streams, most likely a SCI1.1 view + byte *literalPtr = inBuffer + literalPos; + int pixelNr = 0; + + memset(celBitmap, clearColor, pixelCount); + + // View unpacking: + // + // EGA: + // Each byte is like XXXXYYYY (XXXX: 0 - 15, YYYY: 0 - 15) + // Set the next XXXX pixels to YYYY + // + // Amiga: + // Each byte is like XXXXXYYY (XXXXX: 0 - 31, YYY: 0 - 7) + // - Case A: YYY != 0 + // Set the next YYY pixels to XXXXX + // - Case B: YYY == 0 + // Skip the next XXXXX pixels (i.e. transparency) + // + // Amiga 64: + // Each byte is like XXYYYYYY (XX: 0 - 3, YYYYYY: 0 - 63) + // - Case A: XX != 0 + // Set the next XX pixels to YYYYYY + // - Case B: XX == 0 + // Skip the next YYYYYY pixels (i.e. transparency) + // + // VGA: + // Each byte is like XXYYYYYY (YYYYY: 0 - 63) + // - Case A: XX == 00 (binary) + // Copy next YYYYYY bytes as-is + // - Case B: XX == 01 (binary) + // Same as above, copy YYYYYY + 64 bytes as-is + // - Case C: XX == 10 (binary) + // Set the next YYYYY pixels to the next byte value + // - Case D: XX == 11 (binary) + // Skip the next YYYYY pixels (i.e. transparency) + + if (literalPos && isMacSci11ViewData) { + // KQ6/Freddy Pharkas use byte lengths, all others use uint16 + // The SCI devs must have realized that a max of 255 pixels wide + // was not very good for 320 or 640 width games. + bool hasByteLengths = (g_sci->getGameId() == GID_KQ6 || g_sci->getGameId() == GID_FREDDYPHARKAS); + + // compression for SCI1.1+ Mac + while (pixelNr < pixelCount) { + uint32 pixelLine = pixelNr; + + if (hasByteLengths) { + pixelNr += *rlePtr++; + runLength = *rlePtr++; + } else { + pixelNr += READ_BE_UINT16(rlePtr); + runLength = READ_BE_UINT16(rlePtr + 2); + rlePtr += 4; + } + + while (runLength-- && pixelNr < pixelCount) + outPtr[pixelNr++] = *literalPtr++; + + pixelNr = pixelLine + width; + } + return; + } + + switch (viewType) { + case kViewEga: + while (pixelNr < pixelCount) { + curByte = *rlePtr++; + runLength = curByte >> 4; + memset(outPtr + pixelNr, curByte & 0x0F, MIN<uint16>(runLength, pixelCount - pixelNr)); + pixelNr += runLength; + } + break; + case kViewAmiga: + while (pixelNr < pixelCount) { + curByte = *rlePtr++; + if (curByte & 0x07) { // fill with color + runLength = curByte & 0x07; + curByte = curByte >> 3; + memset(outPtr + pixelNr, curByte, MIN<uint16>(runLength, pixelCount - pixelNr)); + } else { // skip the next pixels (transparency) + runLength = curByte >> 3; + } + pixelNr += runLength; + } + break; + case kViewAmiga64: + while (pixelNr < pixelCount) { + curByte = *rlePtr++; + if (curByte & 0xC0) { // fill with color + runLength = curByte >> 6; + memset(outPtr + pixelNr, curByte & 0x3F, MIN<uint16>(runLength, pixelCount - pixelNr)); + } else { // skip the next pixels (transparency) + runLength = curByte & 0x3F; + } + pixelNr += runLength; + } + break; + case kViewVga: + case kViewVga11: + // If we have no RLE data, the image is just uncompressed + if (rlePos == 0) { + memcpy(outPtr, literalPtr, pixelCount); + break; + } + + while (pixelNr < pixelCount) { + curByte = *rlePtr++; + runLength = curByte & 0x3F; + + switch (curByte & 0xC0) { + case 0x40: // copy bytes as is (In copy case, runLength can go up to 127 i.e. pixel & 0x40). Fixes bug #3135872. + runLength += 64; + case 0x00: // copy bytes as-is + if (!literalPos) { + memcpy(outPtr + pixelNr, rlePtr, MIN<uint16>(runLength, pixelCount - pixelNr)); + rlePtr += runLength; + } else { + memcpy(outPtr + pixelNr, literalPtr, MIN<uint16>(runLength, pixelCount - pixelNr)); + literalPtr += runLength; + } + break; + case 0x80: // fill with color + if (!literalPos) + memset(outPtr + pixelNr, *rlePtr++, MIN<uint16>(runLength, pixelCount - pixelNr)); + else + memset(outPtr + pixelNr, *literalPtr++, MIN<uint16>(runLength, pixelCount - pixelNr)); + break; + case 0xC0: // skip the next pixels (transparency) + break; + } + + pixelNr += runLength; + } + break; + default: + error("Unsupported picture viewtype"); + } +} + void GfxView::unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCount) { const CelInfo *celInfo = getCelInfo(loopNo, celNo); - byte *rlePtr; - byte *literalPtr; - uint32 pixelNo = 0, runLength; - byte pixel; if (celInfo->offsetEGA) { // decompression for EGA views - literalPtr = _resourceData + _loop[loopNo].cel[celNo].offsetEGA; - while (pixelNo < pixelCount) { - pixel = *literalPtr++; - runLength = pixel >> 4; - memset(outPtr + pixelNo, pixel & 0x0F, MIN<uint32>(runLength, pixelCount - pixelNo)); - pixelNo += runLength; - } + unpackCelData(_resourceData, outPtr, 0, pixelCount, celInfo->offsetEGA, 0, _resMan->getViewType(), celInfo->width, false); } else { // We fill the buffer with transparent pixels, so that we can later skip // over pixels to automatically have them transparent @@ -408,100 +544,8 @@ void GfxView::unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCou clearColor = 0; } - memset(outPtr, clearColor, pixelCount); - - rlePtr = _resourceData + celInfo->offsetRLE; - if (!celInfo->offsetLiteral) { // no additional literal data - if (_resMan->isAmiga32color()) { - // decompression for amiga views - while (pixelNo < pixelCount) { - pixel = *rlePtr++; - if (pixel & 0x07) { // fill with color - runLength = pixel & 0x07; - pixel = pixel >> 3; - while (runLength-- && pixelNo < pixelCount) { - outPtr[pixelNo++] = pixel; - } - } else { // fill with transparent - runLength = pixel >> 3; - pixelNo += runLength; - } - } - } else { - // decompression for data that has just one combined stream - while (pixelNo < pixelCount) { - pixel = *rlePtr++; - runLength = pixel & 0x3F; - switch (pixel & 0xC0) { - case 0x40: // copy bytes as is (In copy case, runLength can go upto 127 i.e. pixel & 0x40) - runLength += 64; - case 0x00: // copy bytes as-is - while (runLength-- && pixelNo < pixelCount) - outPtr[pixelNo++] = *rlePtr++; - break; - case 0x80: // fill with color - memset(outPtr + pixelNo, *rlePtr++, MIN<uint32>(runLength, pixelCount - pixelNo)); - pixelNo += runLength; - break; - case 0xC0: // fill with transparent - pixelNo += runLength; - break; - } - } - } - } else { - literalPtr = _resourceData + celInfo->offsetLiteral; - if (celInfo->offsetRLE) { - if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() == SCI_VERSION_1_1) { - // KQ6/Freddy Pharkas use byte lengths, all others use uint16 - // The SCI devs must have realized that a max of 255 pixels wide - // was not very good for 320 or 640 width games. - bool hasByteLengths = (g_sci->getGameId() == GID_KQ6 || g_sci->getGameId() == GID_FREDDYPHARKAS); - - // compression for SCI1.1+ Mac - while (pixelNo < pixelCount) { - uint32 pixelLine = pixelNo; - - if (hasByteLengths) { - pixelNo += *rlePtr++; - runLength = *rlePtr++; - } else { - pixelNo += READ_BE_UINT16(rlePtr); - runLength = READ_BE_UINT16(rlePtr + 2); - rlePtr += 4; - } - - while (runLength-- && pixelNo < pixelCount) - outPtr[pixelNo++] = *literalPtr++; - - pixelNo = pixelLine + celInfo->width; - } - } else { - // decompression for data that has separate rle and literal streams - while (pixelNo < pixelCount) { - pixel = *rlePtr++; - runLength = pixel & 0x3F; - switch (pixel & 0xC0) { - case 0: // copy bytes as-is - while (runLength-- && pixelNo < pixelCount) - outPtr[pixelNo++] = *literalPtr++; - break; - case 0x80: // fill with color - memset(outPtr + pixelNo, *literalPtr++, MIN<uint32>(runLength, pixelCount - pixelNo)); - pixelNo += runLength; - break; - case 0xC0: // fill with transparent - pixelNo += runLength; - break; - } - } - } - } else { - // literal stream only, so no compression - memcpy(outPtr, literalPtr, pixelCount); - pixelNo = pixelCount; - } - } + bool isMacSci11ViewData = g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() == SCI_VERSION_1_1; + unpackCelData(_resourceData, outPtr, clearColor, pixelCount, celInfo->offsetRLE, celInfo->offsetLiteral, _resMan->getViewType(), celInfo->width, isMacSci11ViewData); // Swap 0 and 0xff pixels for Mac SCI1.1+ games (see above) if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1) { @@ -531,9 +575,8 @@ const byte *GfxView::getBitmap(int16 loopNo, int16 celNo) { // unpack the actual cel bitmap data unpackCel(loopNo, celNo, pBitmap, pixelCount); - if (!_resMan->isVGA()) { + if (_resMan->getViewType() == kViewEga) unditherBitmap(pBitmap, width, height, _loop[loopNo].cel[celNo].clearKey); - } // mirroring the cel if needed if (_loop[loopNo].mirrorFlag) { @@ -549,19 +592,15 @@ const byte *GfxView::getBitmap(int16 loopNo, int16 celNo) { * cel if the dithering in here matches dithering used by the current picture. */ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte clearKey) { - int16 *unditherMemorial = _screen->unditherGetMemorial(); - - // It makes no sense to go further, if no memorial data from current picture - // is available - if (!unditherMemorial) - return; + int16 *ditheredPicColors = _screen->unditherGetDitheredBgColors(); - // Makes no sense to process bitmaps that are 3 pixels wide or less - if (width <= 3) + // It makes no sense to go further, if there isn't any dithered color data + // available for the current picture + if (!ditheredPicColors) return; - // We need at least 2 pixel lines - if (height < 2) + // We need at least a 4x2 bitmap for this algorithm to work + if (width < 4 || height < 2) return; // If EGA mapping is used for this view, dont do undithering as well @@ -569,13 +608,13 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl return; // Walk through the bitmap and remember all combinations of colors - int16 bitmapMemorial[SCI_SCREEN_UNDITHERMEMORIAL_SIZE]; + int16 ditheredBitmapColors[DITHERED_BG_COLORS_SIZE]; byte *curPtr; byte color1, color2; byte nextColor1, nextColor2; int16 y, x; - memset(&bitmapMemorial, 0, sizeof(bitmapMemorial)); + memset(&ditheredBitmapColors, 0, sizeof(ditheredBitmapColors)); // Count all seemingly dithered pixel-combinations as soon as at least 4 // pixels are adjacent and check pixels in the following line as well to @@ -594,17 +633,17 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl nextColor1 = (nextColor1 >> 4) | (nextColor2 << 4); nextColor2 = (nextColor2 >> 4) | *nextPtr++ << 4; if ((color1 == color2) && (color1 == nextColor1) && (color1 == nextColor2)) - bitmapMemorial[color1]++; + ditheredBitmapColors[color1]++; } } - // Now compare both memorial tables to find out matching - // dithering-combinations - bool unditherTable[SCI_SCREEN_UNDITHERMEMORIAL_SIZE]; + // Now compare both dither color tables to find out matching dithered color + // combinations + bool unditherTable[DITHERED_BG_COLORS_SIZE]; byte color, unditherCount = 0; memset(&unditherTable, false, sizeof(unditherTable)); for (color = 0; color < 255; color++) { - if ((bitmapMemorial[color] > 5) && (unditherMemorial[color] > 200)) { + if ((ditheredBitmapColors[color] > 5) && (ditheredPicColors[color] > 200)) { // match found, check if colorKey is contained -> if so, we ignore // of course color1 = color & 0x0F; color2 = color >> 4; diff --git a/engines/sci/parser/said.cpp b/engines/sci/parser/said.cpp index e9c6d9847f..666a235cf9 100644 --- a/engines/sci/parser/said.cpp +++ b/engines/sci/parser/said.cpp @@ -1020,7 +1020,7 @@ static int augment_parse_nodes(ParseTreeNode *parseT, ParseTreeNode *saidT) { /**** Main code ****/ /*******************/ -int said(EngineState *s, const byte *spec, bool verbose) { +int said(const byte *spec, bool verbose) { int retval; Vocabulary *voc = g_sci->getVocabulary(); diff --git a/engines/sci/parser/vocabulary.cpp b/engines/sci/parser/vocabulary.cpp index 8d59df5d58..25043401cc 100644 --- a/engines/sci/parser/vocabulary.cpp +++ b/engines/sci/parser/vocabulary.cpp @@ -63,7 +63,7 @@ Vocabulary::Vocabulary(ResourceManager *resMan, bool foreign) : _resMan(resMan), _resourceIdBranches += 10; } - if (getSciVersion() <= SCI_VERSION_1_EGA && loadParserWords()) { + if (getSciVersion() <= SCI_VERSION_1_EGA_ONLY && loadParserWords()) { loadSuffixes(); if (loadBranches()) // Now build a GNF grammar out of this diff --git a/engines/sci/parser/vocabulary.h b/engines/sci/parser/vocabulary.h index 3d644d88f7..6d3e0b301e 100644 --- a/engines/sci/parser/vocabulary.h +++ b/engines/sci/parser/vocabulary.h @@ -383,12 +383,11 @@ void vocab_dump_parse_tree(const char *tree_name, ParseTreeNode *nodes); /** * Builds a parse tree from a spec and compares it to a parse tree. - * @param s The affected state * @param spec Pointer to the spec to build * @param verbose Whether to display the parse tree after building it * @return 1 on a match, 0 otherwise */ -int said(EngineState *s, const byte *spec, bool verbose); +int said(const byte *spec, bool verbose); } // End of namespace Sci diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp index a16f7126c3..61df02acf9 100644 --- a/engines/sci/resource.cpp +++ b/engines/sci/resource.cpp @@ -67,7 +67,7 @@ const char *getSciVersionDesc(SciVersion version) { return "Late SCI0"; case SCI_VERSION_01: return "SCI01"; - case SCI_VERSION_1_EGA: + case SCI_VERSION_1_EGA_ONLY: return "SCI1 EGA"; case SCI_VERSION_1_EARLY: return "Early SCI1"; @@ -948,15 +948,18 @@ void ResourceManager::init(bool initFromFallbackDetector) { case kViewEga: debugC(1, kDebugLevelResMan, "resMan: Detected EGA graphic resources"); break; + case kViewAmiga: + debugC(1, kDebugLevelResMan, "resMan: Detected Amiga ECS graphic resources"); + break; + case kViewAmiga64: + debugC(1, kDebugLevelResMan, "resMan: Detected Amiga AGA graphic resources"); + break; case kViewVga: debugC(1, kDebugLevelResMan, "resMan: Detected VGA graphic resources"); break; case kViewVga11: debugC(1, kDebugLevelResMan, "resMan: Detected SCI1.1 VGA graphic resources"); break; - case kViewAmiga: - debugC(1, kDebugLevelResMan, "resMan: Detected Amiga graphic resources"); - break; default: #ifdef ENABLE_SCI32 error("resMan: Couldn't determine view type"); @@ -1535,10 +1538,18 @@ void ResourceManager::readResourcePatches() { mask += s_resourceTypeSuffixes[i]; SearchMan.listMatchingMembers(files, mask); - if (i == kResourceTypeScript && files.size() == 0) { - // SCI3 (we can't use getSciVersion() at this point) - mask = "*.csc"; - SearchMan.listMatchingMembers(files, mask); + if (i == kResourceTypeView) { + SearchMan.listMatchingMembers(files, "*.v16"); // EGA SCI1 view patches + SearchMan.listMatchingMembers(files, "*.v32"); // Amiga SCI1 view patches + SearchMan.listMatchingMembers(files, "*.v64"); // Amiga AGA SCI1 (i.e. Longbow) view patches + } else if (i == kResourceTypePic) { + SearchMan.listMatchingMembers(files, "*.p16"); // EGA SCI1 picture patches + SearchMan.listMatchingMembers(files, "*.p32"); // Amiga SCI1 picture patches + SearchMan.listMatchingMembers(files, "*.p64"); // Amiga AGA SCI1 (i.e. Longbow) picture patches + } else if (i == kResourceTypeScript) { + if (files.size() == 0) + // SCI3 (we can't use getSciVersion() at this point) + SearchMan.listMatchingMembers(files, "*.csc"); } for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { @@ -2049,7 +2060,13 @@ ViewType ResourceManager::detectViewType() { switch (res->data[1]) { case 128: - // If the 2nd byte is 128, it's a VGA game + // If the 2nd byte is 128, it's a VGA game. + // However, Longbow Amiga (AGA, 64 colors), also sets this byte + // to 128, but it's a mixed VGA/Amiga format. Detect this from + // the platform here. + if (g_sci && g_sci->getPlatform() == Common::kPlatformAmiga) + return kViewAmiga64; + return kViewVga; case 0: // EGA or Amiga, try to read as Amiga view @@ -2127,9 +2144,9 @@ void ResourceManager::detectSciVersion() { if (viewCompression != kCompLZW) { // If it's a different compression type from kCompLZW, the game is probably - // SCI_VERSION_1_EGA or later. If the views are uncompressed, it is + // SCI_VERSION_1_EGA_ONLY or later. If the views are uncompressed, it is // likely not an early disk game. - s_sciVersion = SCI_VERSION_1_EGA; + s_sciVersion = SCI_VERSION_1_EGA_ONLY; oldDecompressors = false; } @@ -2243,9 +2260,9 @@ void ResourceManager::detectSciVersion() { return; } - // New decompressors. It's either SCI_VERSION_1_EGA or SCI_VERSION_1_EARLY. + // New decompressors. It's either SCI_VERSION_1_EGA_ONLY or SCI_VERSION_1_EARLY. if (hasSci1Voc900()) { - s_sciVersion = SCI_VERSION_1_EGA; + s_sciVersion = SCI_VERSION_1_EGA_ONLY; return; } @@ -2255,6 +2272,12 @@ void ResourceManager::detectSciVersion() { case kResVersionSci1Middle: case kResVersionKQ5FMT: s_sciVersion = SCI_VERSION_1_MIDDLE; + // Amiga SCI1 middle games are actually SCI1 late + if (_viewType == kViewAmiga || _viewType == kViewAmiga64) + s_sciVersion = SCI_VERSION_1_LATE; + // Same goes for Mac SCI1 middle games + if (g_sci && g_sci->getPlatform() == Common::kPlatformMacintosh) + s_sciVersion = SCI_VERSION_1_LATE; return; case kResVersionSci1Late: if (_volVersion == kResVersionSci11) { diff --git a/engines/sci/resource.h b/engines/sci/resource.h index 76b5a421ee..e941f666d9 100644 --- a/engines/sci/resource.h +++ b/engines/sci/resource.h @@ -331,8 +331,6 @@ public: int getAudioLanguage() const; void changeAudioDirectory(Common::String path); bool isGMTrackIncluded(); - bool isVGA() const { return (_viewType == kViewVga) || (_viewType == kViewVga11); } - bool isAmiga32color() const { return _viewType == kViewAmiga; } bool isSci11Mac() const { return _volVersion == kResVersionSci11Mac; } ViewType getViewType() const { return _viewType; } const char *getMapVersionDesc() const { return versionDescription(_mapVersion); } diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index b8a04066ac..bcbb005eb0 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -213,7 +213,6 @@ Common::Error SciEngine::run() { // Add the after market GM patches for the specified game, if they exist _resMan->addNewGMPatch(_gameId); _gameObjectAddress = _resMan->findGameObject(); - _gameSuperClassAddress = NULL_REG; SegManager *segMan = new SegManager(_resMan); @@ -227,7 +226,7 @@ Common::Error SciEngine::run() { _features = new GameFeatures(segMan, _kernel); // Only SCI0, SCI01 and SCI1 EGA games used a parser - _vocabulary = (getSciVersion() <= SCI_VERSION_1_EGA) ? new Vocabulary(_resMan, false) : NULL; + _vocabulary = (getSciVersion() <= SCI_VERSION_1_EGA_ONLY) ? new Vocabulary(_resMan, false) : NULL; // Also, XMAS1990 apparently had a parser too. Refer to http://forums.scummvm.org/viewtopic.php?t=9135 if (getGameId() == GID_CHRISTMAS1990) _vocabulary = new Vocabulary(_resMan, false); @@ -250,7 +249,6 @@ Common::Error SciEngine::run() { warning("Could not get game object, aborting..."); return Common::kUnknownError; } - _gameSuperClassAddress = gameObject->getSuperClassSelector(); script_adjust_opcode_formats(); @@ -267,8 +265,6 @@ Common::Error SciEngine::run() { // Initialize all graphics related subsystems initGraphics(); - debug("Emulating SCI version %s\n", getSciVersionDesc(getSciVersion())); - // Patch in our save/restore code, so that dialogs are replaced patchGameSaveRestore(); setLauncherLanguage(); @@ -427,7 +423,7 @@ static byte patchGameRestoreSave[] = { 0x76, // push0 0x38, 0xff, 0xff, // pushi -1 0x76, // push0 - 0x43, 0xff, 0x06, // callk kRestoreGame/kSaveGame (will get fixed directly) + 0x43, 0xff, 0x06, // callk kRestoreGame/kSaveGame (will get changed afterwards) 0x48, // ret }; @@ -437,34 +433,56 @@ static byte patchGameRestoreSaveSci2[] = { 0x76, // push0 0x38, 0xff, 0xff, // pushi -1 0x76, // push0 - 0x43, 0xff, 0x06, 0x00, // callk kRestoreGame/kSaveGame (will get fixed directly) + 0x43, 0xff, 0x06, 0x00, // callk kRestoreGame/kSaveGame (will get changed afterwards) 0x48, // ret }; +// SCI21 version: Same as above, but the second parameter to callk is a word +static byte patchGameRestoreSaveSci21[] = { + 0x39, 0x04, // pushi 04 + 0x76, // push0 // 0: save, 1: restore (will get changed afterwards) + 0x76, // push0 + 0x38, 0xff, 0xff, // pushi -1 + 0x76, // push0 + 0x43, 0xff, 0x08, 0x00, // callk kSave (will get changed afterwards) + 0x48, // ret +}; + +static void patchGameSaveRestoreCode(SegManager *segMan, reg_t methodAddress, byte id) { + Script *script = segMan->getScript(methodAddress.segment); + byte *patchPtr = const_cast<byte *>(script->getBuf(methodAddress.offset)); + if (getSciVersion() <= SCI_VERSION_1_1) + memcpy(patchPtr, patchGameRestoreSave, sizeof(patchGameRestoreSave)); + else // SCI2+ + memcpy(patchPtr, patchGameRestoreSaveSci2, sizeof(patchGameRestoreSaveSci2)); + patchPtr[8] = id; +} + +static void patchGameSaveRestoreCodeSci21(SegManager *segMan, reg_t methodAddress, byte id, bool doRestore) { + Script *script = segMan->getScript(methodAddress.segment); + byte *patchPtr = const_cast<byte *>(script->getBuf(methodAddress.offset)); + memcpy(patchPtr, patchGameRestoreSaveSci21, sizeof(patchGameRestoreSaveSci21)); + if (doRestore) + patchPtr[2] = 0x78; // push1 + patchPtr[9] = id; +} + void SciEngine::patchGameSaveRestore() { SegManager *segMan = _gamestate->_segMan; const Object *gameObject = segMan->getObject(_gameObjectAddress); - const uint16 gameMethodCount = gameObject->getMethodCount(); - const Object *gameSuperObject = segMan->getObject(_gameSuperClassAddress); + const Object *gameSuperObject = segMan->getObject(gameObject->getSuperClassSelector()); if (!gameSuperObject) gameSuperObject = gameObject; // happens in KQ5CD, when loading saved games before r54510 - const uint16 gameSuperMethodCount = gameSuperObject->getMethodCount(); - reg_t methodAddress; - const uint16 kernelCount = _kernel->getKernelNamesSize(); - const byte *scriptRestorePtr = NULL; byte kernelIdRestore = 0; - const byte *scriptSavePtr = NULL; byte kernelIdSave = 0; - // This feature is currently not supported in SCI21 or SCI3 - if (getSciVersion() >= SCI_VERSION_2_1) - return; - switch (_gameId) { - case GID_MOTHERGOOSE256: // mother goose saves/restores directly and has no save/restore dialogs - case GID_JONES: // gets confused, when we patch us in, the game is only able to save to 1 slot, so hooking is not required case GID_HOYLE1: // gets confused, although the game doesnt support saving/restoring at all case GID_HOYLE2: // gets confused, see hoyle1 + case GID_JONES: // gets confused, when we patch us in, the game is only able to save to 1 slot, so hooking is not required + case GID_MOTHERGOOSE256: // mother goose saves/restores directly and has no save/restore dialogs + case GID_PHANTASMAGORIA: // has custom save/load code + case GID_SHIVERS: // has custom save/load code return; default: break; @@ -473,69 +491,53 @@ void SciEngine::patchGameSaveRestore() { if (ConfMan.getBool("sci_originalsaveload")) return; - for (uint16 kernelNr = 0; kernelNr < kernelCount; kernelNr++) { + uint16 kernelNamesSize = _kernel->getKernelNamesSize(); + for (uint16 kernelNr = 0; kernelNr < kernelNamesSize; kernelNr++) { Common::String kernelName = _kernel->getKernelName(kernelNr); if (kernelName == "RestoreGame") kernelIdRestore = kernelNr; if (kernelName == "SaveGame") kernelIdSave = kernelNr; + if (kernelName == "Save") + kernelIdSave = kernelIdRestore = kernelNr; } - // Search for gameobject-superclass ::restore - for (uint16 methodNr = 0; methodNr < gameSuperMethodCount; methodNr++) { + // Search for gameobject superclass ::restore + uint16 gameSuperObjectMethodCount = gameSuperObject->getMethodCount(); + for (uint16 methodNr = 0; methodNr < gameSuperObjectMethodCount; methodNr++) { uint16 selectorId = gameSuperObject->getFuncSelector(methodNr); Common::String methodName = _kernel->getSelectorName(selectorId); if (methodName == "restore") { - methodAddress = gameSuperObject->getFunction(methodNr); - Script *script = segMan->getScript(methodAddress.segment); - scriptRestorePtr = script->getBuf(methodAddress.offset); + if (kernelIdSave != kernelIdRestore) + patchGameSaveRestoreCode(segMan, gameSuperObject->getFunction(methodNr), kernelIdRestore); + else + patchGameSaveRestoreCodeSci21(segMan, gameSuperObject->getFunction(methodNr), kernelIdRestore, true); } - if (methodName == "save") { - methodAddress = gameSuperObject->getFunction(methodNr); - Script *script = segMan->getScript(methodAddress.segment); - scriptSavePtr = script->getBuf(methodAddress.offset); + else if (methodName == "save") { + if (_gameId != GID_FAIRYTALES) { // Fairy Tales saves automatically without a dialog + if (kernelIdSave != kernelIdRestore) + patchGameSaveRestoreCode(segMan, gameSuperObject->getFunction(methodNr), kernelIdSave); + else + patchGameSaveRestoreCodeSci21(segMan, gameSuperObject->getFunction(methodNr), kernelIdSave, false); + } } } - // Search for gameobject ::save, if there is one patch that one instead - for (uint16 methodNr = 0; methodNr < gameMethodCount; methodNr++) { + // Search for gameobject ::save, if there is one patch that one too + uint16 gameObjectMethodCount = gameObject->getMethodCount(); + for (uint16 methodNr = 0; methodNr < gameObjectMethodCount; methodNr++) { uint16 selectorId = gameObject->getFuncSelector(methodNr); Common::String methodName = _kernel->getSelectorName(selectorId); if (methodName == "save") { - methodAddress = gameObject->getFunction(methodNr); - Script *script = segMan->getScript(methodAddress.segment); - scriptSavePtr = script->getBuf(methodAddress.offset); + if (_gameId != GID_FAIRYTALES) { // Fairy Tales saves automatically without a dialog + if (kernelIdSave != kernelIdRestore) + patchGameSaveRestoreCode(segMan, gameObject->getFunction(methodNr), kernelIdSave); + else + patchGameSaveRestoreCodeSci21(segMan, gameObject->getFunction(methodNr), kernelIdSave, false); + } break; } } - - switch (_gameId) { - case GID_FAIRYTALES: // fairy tales automatically saves w/o dialog - scriptSavePtr = NULL; - default: - break; - } - - if (scriptRestorePtr) { - // Now patch in our code - byte *patchPtr = const_cast<byte *>(scriptRestorePtr); - if (getSciVersion() <= SCI_VERSION_1_1) - memcpy(patchPtr, patchGameRestoreSave, sizeof(patchGameRestoreSave)); - else if (getSciVersion() == SCI_VERSION_2) - memcpy(patchPtr, patchGameRestoreSaveSci2, sizeof(patchGameRestoreSaveSci2)); - // TODO: SCI21/SCI3 - patchPtr[8] = kernelIdRestore; - } - if (scriptSavePtr) { - // Now patch in our code - byte *patchPtr = const_cast<byte *>(scriptSavePtr); - if (getSciVersion() <= SCI_VERSION_1_1) - memcpy(patchPtr, patchGameRestoreSave, sizeof(patchGameRestoreSave)); - else if (getSciVersion() == SCI_VERSION_2) - memcpy(patchPtr, patchGameRestoreSaveSci2, sizeof(patchGameRestoreSaveSci2)); - // TODO: SCI21/SCI3 - patchPtr[8] = kernelIdSave; - } } bool SciEngine::initGame() { @@ -641,7 +643,7 @@ void SciEngine::initGraphics() { _gfxCoordAdjuster = new GfxCoordAdjuster16(_gfxPorts); _gfxCursor->init(_gfxCoordAdjuster, _eventMan); _gfxCompare = new GfxCompare(_gamestate->_segMan, _kernel, _gfxCache, _gfxScreen, _gfxCoordAdjuster); - _gfxTransitions = new GfxTransitions(_gfxScreen, _gfxPalette, _resMan->isVGA()); + _gfxTransitions = new GfxTransitions(_gfxScreen, _gfxPalette); _gfxPaint16 = new GfxPaint16(_resMan, _gamestate->_segMan, _kernel, _gfxCache, _gfxPorts, _gfxCoordAdjuster, _gfxScreen, _gfxPalette, _gfxTransitions, _audio); _gfxPaint = _gfxPaint16; _gfxAnimate = new GfxAnimate(_gamestate, _gfxCache, _gfxPorts, _gfxPaint16, _gfxScreen, _gfxPalette, _gfxCursor, _gfxTransitions); @@ -776,6 +778,16 @@ bool SciEngine::isCD() const { return _gameDescription->flags & ADGF_CD; } +bool SciEngine::isBE() const{ + switch(_gameDescription->platform) { + case Common::kPlatformAmiga: + case Common::kPlatformMacintosh: + return true; + default: + return false; + } +} + bool SciEngine::hasMacIconBar() const { return _resMan->isSci11Mac() && getSciVersion() == SCI_VERSION_1_1 && (getGameId() == GID_KQ6 || getGameId() == GID_FREDDYPHARKAS); diff --git a/engines/sci/sci.h b/engines/sci/sci.h index b3e398325f..26ddb00a01 100644 --- a/engines/sci/sci.h +++ b/engines/sci/sci.h @@ -180,20 +180,24 @@ enum SciGameId { GID_FANMADE // FIXME: Do we really need/want this? }; -/** SCI versions */ +/** + * SCI versions + * For more information, check here: + * http://wiki.scummvm.org/index.php/Sierra_Game_Versions#SCI_Games + */ enum SciVersion { SCI_VERSION_NONE, - SCI_VERSION_0_EARLY, // Early KQ4, 1988 xmas card + SCI_VERSION_0_EARLY, // KQ4 early, LSL2 early, XMAS card 1988 SCI_VERSION_0_LATE, // KQ4, LSL2, LSL3, SQ3 etc SCI_VERSION_01, // KQ1 and multilingual games (S.old.*) - SCI_VERSION_1_EGA, // EGA with parser, QFG2 - SCI_VERSION_1_EARLY, // KQ5. (EGA/VGA) - SCI_VERSION_1_MIDDLE, // LSL1, JONESCD. (EGA?/VGA) - SCI_VERSION_1_LATE, // ECO1, LSL5. (EGA/VGA) - SCI_VERSION_1_1, // KQ6, ECO2 - SCI_VERSION_2, // GK1, PQ4 (Floppy), QFG4 (Floppy) - SCI_VERSION_2_1, // GK2, KQ7, SQ6, Torin - SCI_VERSION_3 // LSL7, RAMA, Lighthouse + SCI_VERSION_1_EGA_ONLY, // SCI 1 EGA with parser (i.e. QFG2 only) + SCI_VERSION_1_EARLY, // KQ5 floppy, SQ4 floppy, XMAS card 1990, Fairy tales, Jones floppy + SCI_VERSION_1_MIDDLE, // LSL1, Jones CD + SCI_VERSION_1_LATE, // Dr. Brain 1, EcoQuest 1, Longbow, PQ3, SQ1, LSL5, KQ5 CD + SCI_VERSION_1_1, // Dr. Brain 2, EcoQuest 1 CD, EcoQuest 2, KQ6, QFG3, SQ4CD, XMAS 1992 and many more + SCI_VERSION_2, // GK1, PQ4 floppy, QFG4 floppy + SCI_VERSION_2_1, // GK2, KQ7, LSL6 hires, MUMG Deluxe, Phantasmagoria 1, PQ4CD, PQ:SWAT, QFG4CD, Shivers 1, SQ6, Torin + SCI_VERSION_3 // LSL7, Lighthouse, RAMA, Phantasmagoria 2 }; /** Supported languages */ @@ -234,6 +238,10 @@ public: Common::Platform getPlatform() const; bool isDemo() const; bool isCD() const; + + /** Returns true if the game's original platform is big-endian. */ + bool isBE() const; + bool hasMacIconBar() const; inline ResourceManager *getResMan() const { return _resMan; } @@ -242,7 +250,6 @@ public: inline Vocabulary *getVocabulary() const { return _vocabulary; } inline EventManager *getEventManager() const { return _eventMan; } inline reg_t getGameObject() const { return _gameObjectAddress; } - inline reg_t getGameSuperClassAddress() const { return _gameSuperClassAddress; } Common::RandomSource &getRNG() { return _rng; } @@ -371,7 +378,6 @@ private: int16 _vocabularyLanguage; EventManager *_eventMan; reg_t _gameObjectAddress; /**< Pointer to the game object */ - reg_t _gameSuperClassAddress; // Address of the super class of the game object Console *_console; Common::RandomSource _rng; Common::MacResManager _macExecutable; diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp index 057b7c177f..65a8e2e3da 100644 --- a/engines/sci/sound/drivers/adlib.cpp +++ b/engines/sci/sound/drivers/adlib.cpp @@ -55,7 +55,7 @@ public: virtual ~MidiDriver_AdLib() { } // MidiDriver - int open(bool isSCI0); + int openAdLib(bool isSCI0); void close(); void send(uint32 b); MidiChannel *allocateChannel() { return NULL; } @@ -215,7 +215,7 @@ static const int ym3812_note[13] = { 0x2ae }; -int MidiDriver_AdLib::open(bool isSCI0) { +int MidiDriver_AdLib::openAdLib(bool isSCI0) { int rate = _mixer->getOutputRate(); _stereo = STEREO; @@ -273,10 +273,6 @@ void MidiDriver_AdLib::send(uint32 b) { case 0x90: noteOn(channel, op1, op2); break; - case 0xe0: - _channels[channel].pitchWheel = (op1 & 0x7f) | ((op2 & 0x7f) << 7); - renewNotes(channel, true); - break; case 0xb0: switch (op1) { case 0x07: @@ -321,7 +317,9 @@ void MidiDriver_AdLib::send(uint32 b) { case 0xa0: // Polyphonic key pressure (aftertouch) case 0xd0: // Channel pressure (aftertouch) break; - case 0xf0: // SysEx, ignore it + case 0xe0: + _channels[channel].pitchWheel = (op1 & 0x7f) | ((op2 & 0x7f) << 7); + renewNotes(channel, true); break; default: warning("ADLIB: Unknown event %02x", command); @@ -828,7 +826,7 @@ int MidiPlayer_AdLib::open(ResourceManager *resMan) { return -1; } - return static_cast<MidiDriver_AdLib *>(_driver)->open(_version <= SCI_VERSION_0_LATE); + return static_cast<MidiDriver_AdLib *>(_driver)->openAdLib(_version <= SCI_VERSION_0_LATE); } void MidiPlayer_AdLib::close() { diff --git a/engines/sci/sound/drivers/amigamac.cpp b/engines/sci/sound/drivers/amigamac.cpp index 0ec4c283f7..d64dfac23c 100644 --- a/engines/sci/sound/drivers/amigamac.cpp +++ b/engines/sci/sound/drivers/amigamac.cpp @@ -34,7 +34,7 @@ namespace Sci { -/* #define DEBUG */ +//#define DEBUG class MidiDriver_AmigaMac : public MidiDriver_Emulated { public: @@ -289,9 +289,9 @@ void MidiDriver_AmigaMac::playInstrument(int16 *dest, Voice *channel, int count) void MidiDriver_AmigaMac::changeInstrument(int channel, int instrument) { #ifdef DEBUG if (_bank.instruments[instrument][0]) - debugN("[sfx:seq:amiga] Setting channel %i to \"%s\" (%i)\n", channel, _bank.instruments[instrument]->name, instrument); + debugN("Amiga/Mac driver: Setting channel %i to \"%s\" (%i)\n", channel, _bank.instruments[instrument].name, instrument); else - warning("[sfx:seq:amiga] instrument %i does not exist (channel %i)", instrument, channel); + warning("Amiga/Mac driver: instrument %i does not exist (channel %i)", instrument, channel); #endif _channels[channel].instrument = instrument; } @@ -326,7 +326,7 @@ void MidiDriver_AmigaMac::stopNote(int ch, int note) { if (channel == kChannels) { #ifdef DEBUG - warning("[sfx:seq:amiga] cannot stop note %i on channel %i", note, ch); + warning("Amiga/Mac driver: cannot stop note %i on channel %i", note, ch); #endif return; } @@ -366,7 +366,7 @@ void MidiDriver_AmigaMac::setOutputFrac(int voice) { fnote += instrument->transpose; if (fnote < 0 || fnote > 127) { - warning("[sfx:seq:amiga] illegal note %i", fnote); + warning("Amiga/Mac driver: illegal note %i", fnote); return; } } else @@ -403,14 +403,14 @@ void MidiDriver_AmigaMac::startNote(int ch, int note, int velocity) { int channel; if (_channels[ch].instrument < 0 || _channels[ch].instrument > 255) { - warning("[sfx:seq:amiga] invalid instrument %i on channel %i", _channels[ch].instrument, ch); + warning("Amiga/Mac driver: invalid instrument %i on channel %i", _channels[ch].instrument, ch); return; } InstrumentSample *instrument = findInstrument(_channels[ch].instrument, note); if (!instrument) { - warning("[sfx:seq:amiga] instrument %i does not exist", _channels[ch].instrument); + warning("Amiga/Mac driver: instrument %i does not exist", _channels[ch].instrument); return; } @@ -419,7 +419,7 @@ void MidiDriver_AmigaMac::startNote(int ch, int note, int velocity) { break; if (channel == kChannels) { - warning("[sfx:seq:amiga] could not find a free channel"); + warning("Amiga/Mac driver: could not find a free channel"); return; } @@ -447,14 +447,14 @@ MidiDriver_AmigaMac::InstrumentSample *MidiDriver_AmigaMac::readInstrumentSCI0(C byte header[61]; if (file.read(header, 61) < 61) { - warning("[sfx:seq:amiga] failed to read instrument header"); + warning("Amiga/Mac driver: failed to read instrument header"); return NULL; } int seg_size[3]; - seg_size[0] = READ_BE_UINT16(header + 35) * 2; - seg_size[1] = READ_BE_UINT16(header + 41) * 2; - seg_size[2] = READ_BE_UINT16(header + 47) * 2; + seg_size[0] = (int16)READ_BE_UINT16(header + 35) * 2; + seg_size[1] = (int16)READ_BE_UINT16(header + 41) * 2; + seg_size[2] = (int16)READ_BE_UINT16(header + 47) * 2; InstrumentSample *instrument = new InstrumentSample; @@ -489,18 +489,18 @@ MidiDriver_AmigaMac::InstrumentSample *MidiDriver_AmigaMac::readInstrumentSCI0(C instrument->name[29] = 0; #ifdef DEBUG - debugN("[sfx:seq:amiga] Reading instrument %i: \"%s\" (%i bytes)\n", + debugN("Amiga/Mac driver: Reading instrument %i: \"%s\" (%i bytes)\n", *id, instrument->name, size); debugN(" Mode: %02x\n", instrument->mode); debugN(" Looping: %s\n", instrument->mode & kModeLoop ? "on" : "off"); debugN(" Pitch changes: %s\n", instrument->mode & kModePitch ? "on" : "off"); debugN(" Segment sizes: %i %i %i\n", seg_size[0], seg_size[1], seg_size[2]); - debugN(" Segment offsets: 0 %i %i\n", loop_offset, read_int32(header + 43)); + debugN(" Segment offsets: 0 %i %i\n", loop_offset, (int32)READ_BE_UINT32(header + 43)); #endif instrument->samples = (int8 *) malloc(size + 1); if (file.read(instrument->samples, size) < (unsigned int)size) { - warning("[sfx:seq:amiga] failed to read instrument samples"); + warning("Amiga/Mac driver: failed to read instrument samples"); free(instrument->samples); delete instrument; return NULL; @@ -512,14 +512,14 @@ MidiDriver_AmigaMac::InstrumentSample *MidiDriver_AmigaMac::readInstrumentSCI0(C if (instrument->mode & kModeLoop) { if (loop_offset + seg_size[1] > size) { #ifdef DEBUG - warning("[sfx:seq:amiga] looping samples extend %i bytes past end of sample block", + warning("Amiga/Mac driver: looping samples extend %i bytes past end of sample block", loop_offset + seg_size[1] - size); #endif seg_size[1] = size - loop_offset; } if (seg_size[1] < 0) { - warning("[sfx:seq:amiga] invalid looping point"); + warning("Amiga/Mac driver: invalid looping point"); free(instrument->samples); delete instrument; return NULL; @@ -666,26 +666,42 @@ void MidiDriver_AmigaMac::send(uint32 b) { case 0x07: _channels[channel].volume = op2; break; - case 0x0a: + case 0x0a: // pan + // TODO #ifdef DEBUG - warning("[sfx:seq:amiga] ignoring pan 0x%02x event for channel %i", op2, channel); + warning("Amiga/Mac driver: ignoring pan 0x%02x event for channel %i", op2, channel); #endif break; + case 0x40: // hold + // TODO +#ifdef DEBUG + warning("Amiga/Mac driver: ignoring hold 0x%02x event for channel %i", op2, channel); +#endif + break; + case 0x4b: // voice mapping + break; + case 0x4e: // velocity + break; case 0x7b: stopChannel(channel); break; default: - warning("[sfx:seq:amiga] unknown control event 0x%02x", op1); + //warning("Amiga/Mac driver: unknown control event 0x%02x", op1); + break; } break; case 0xc0: changeInstrument(channel, op1); break; + // The original MIDI driver from sierra ignores aftertouch completely, so should we + case 0xa0: // Polyphonic key pressure (aftertouch) + case 0xd0: // Channel pressure (aftertouch) + break; case 0xe0: pitchWheel(channel, (op2 << 7) | op1); break; default: - warning("[sfx:seq:amiga] unknown event %02x", command); + warning("Amiga/Mac driver: unknown event %02x", command); } } @@ -738,7 +754,7 @@ bool MidiDriver_AmigaMac::loadInstrumentsSCI0(Common::File &file) { byte header[40]; if (file.read(header, 40) < 40) { - warning("[sfx:seq:amiga] failed to read header of file bank.001"); + warning("Amiga/Mac driver: failed to read header of file bank.001"); return false; } @@ -746,7 +762,7 @@ bool MidiDriver_AmigaMac::loadInstrumentsSCI0(Common::File &file) { strncpy(_bank.name, (char *) header + 8, 29); _bank.name[29] = 0; #ifdef DEBUG - debugN("[sfx:seq:amiga] Reading %i instruments from bank \"%s\"\n", _bank.size, _bank.name); + debugN("Amiga/Mac driver: Reading %i instruments from bank \"%s\"\n", _bank.size, _bank.name); #endif for (uint i = 0; i < _bank.size; i++) { @@ -754,12 +770,12 @@ bool MidiDriver_AmigaMac::loadInstrumentsSCI0(Common::File &file) { InstrumentSample *instrument = readInstrumentSCI0(file, &id); if (!instrument) { - warning("[sfx:seq:amiga] failed to read bank.001"); + warning("Amiga/Mac driver: failed to read bank.001"); return false; } if (id < 0 || id > 255) { - warning("[sfx:seq:amiga] Error: instrument ID out of bounds"); + warning("Amiga/Mac driver: Error: instrument ID out of bounds"); return false; } @@ -777,7 +793,7 @@ bool MidiDriver_AmigaMac::loadInstrumentsSCI0Mac(Common::SeekableReadStream &fil byte header[40]; if (file.read(header, 40) < 40) { - warning("[sfx:seq:amiga] failed to read header of file patch.200"); + warning("Amiga/Mac driver: failed to read header of file patch.200"); return false; } @@ -785,7 +801,7 @@ bool MidiDriver_AmigaMac::loadInstrumentsSCI0Mac(Common::SeekableReadStream &fil strncpy(_bank.name, (char *) header + 8, 29); _bank.name[29] = 0; #ifdef DEBUG - debugN("[sfx:seq:amiga] Reading %i instruments from bank \"%s\"\n", _bank.size, _bank.name); + debugN("Amiga/Mac driver: Reading %i instruments from bank \"%s\"\n", _bank.size, _bank.name); #endif Common::Array<uint32> instrumentOffsets; @@ -848,7 +864,7 @@ bool MidiDriver_AmigaMac::loadInstrumentsSCI0Mac(Common::SeekableReadStream &fil instrument->samples = (int8 *)malloc(size + 1); if (file.read(instrument->samples, size) < size) { - warning("[sfx:seq:amiga] failed to read instrument sample"); + warning("Amiga/Mac driver: failed to read instrument sample"); free(instrument->samples); delete instrument; continue; diff --git a/engines/sci/sound/drivers/midi.cpp b/engines/sci/sound/drivers/midi.cpp index 08e93d3213..d16655928e 100644 --- a/engines/sci/sound/drivers/midi.cpp +++ b/engines/sci/sound/drivers/midi.cpp @@ -248,11 +248,17 @@ void MidiPlayer_Midi::controlChange(int channel, int control, int value) { _channels[channel].hold = value; break; + case 0x4b: // voice mapping + break; + case 0x4e: // velocity + break; case 0x7b: if (!_channels[channel].playing) return; _channels[channel].playing = false; + default: + break; } _driver->send(0xb0 | channel, control, value); @@ -604,41 +610,83 @@ void MidiPlayer_Midi::readMt32DrvData() { if (f.open("MT32.DRV")) { int size = f.size(); - assert(size >= 166); - - // Send before-SysEx text - f.seek(0x59); + // Skip before-SysEx text + if (size == 1773 || size == 1759) // XMAS88 / KQ4 early + f.seek(0x59); + else if (size == 2771) // LSL2 early + f.seek(0x29); + else + error("Unknown MT32.DRV size (%d)", size); // Skip 2 extra 0 bytes in some drivers if (f.readUint16LE() != 0) f.seek(-2, SEEK_CUR); + // Send before-SysEx text sendMt32SysEx(0x200000, static_cast<Common::SeekableReadStream *>(&f), 20); - // Send after-SysEx text (SSCI sends this before every song) - sendMt32SysEx(0x200000, static_cast<Common::SeekableReadStream *>(&f), 20); + if (size != 2271) { + // Send after-SysEx text (SSCI sends this before every song). + // There aren't any SysEx calls in old drivers, so this can + // be sent right after the before-SysEx text. + sendMt32SysEx(0x200000, static_cast<Common::SeekableReadStream *>(&f), 20); + } else { + // Skip the after-SysEx text in the newer patch version, we'll send + // it after the SysEx messages are sent. + f.skip(20); + } - // Save goodbye message + // Save goodbye message. This isn't a C string, so it may not be + // nul-terminated. f.read(_goodbyeMsg, 20); // Set volume byte volume = CLIP<uint16>(f.readUint16LE(), 0, 100); setMt32Volume(volume); - byte reverbSysEx[13]; - // This old driver should have a full reverb SysEx - if ((f.read(reverbSysEx, 13) != 13) || (reverbSysEx[0] != 0xf0) || (reverbSysEx[12] != 0xf7)) - error("Error reading MT32.DRV"); + if (size == 2771) { + // MT32.DRV in LSL2 early contains more data, like a normal patch + byte reverb = f.readByte(); - // Send reverb SysEx - sysEx(reverbSysEx + 1, 11); - _hasReverb = false; + _hasReverb = true; - f.seek(0x29); + // Skip reverb SysEx message + f.skip(11); - // Read AdLib->MT-32 patch map - for (int i = 0; i < 48; i++) { - _patchMap[i] = f.readByte(); + // Read reverb data (stored vertically - patch #3117434) + for (int j = 0; j < 3; ++j) { + for (int i = 0; i < kReverbConfigNr; i++) { + _reverbConfig[i][j] = f.readByte(); + } + } + + f.skip(2235); // skip driver code + + // Patches 1-48 + sendMt32SysEx(0x50000, static_cast<Common::SeekableReadStream *>(&f), 256); + sendMt32SysEx(0x50200, static_cast<Common::SeekableReadStream *>(&f), 128); + + setReverb(reverb); + + // Send the after-SysEx text + f.seek(0x3d); + sendMt32SysEx(0x200000, static_cast<Common::SeekableReadStream *>(&f), 20); + } else { + byte reverbSysEx[13]; + // This old driver should have a full reverb SysEx + if ((f.read(reverbSysEx, 13) != 13) || (reverbSysEx[0] != 0xf0) || (reverbSysEx[12] != 0xf7)) + error("Error reading MT32.DRV"); + + // Send reverb SysEx + sysEx(reverbSysEx + 1, 11); + _hasReverb = false; + + f.seek(0x29); + + // Read AdLib->MT-32 patch map + for (int i = 0; i < 48; i++) { + _patchMap[i] = f.readByte(); + } } f.close(); @@ -913,7 +961,7 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) { // TODO: The MT-32 <-> GM mapping hasn't been worked on for SCI1 games. Throw // a warning to the user - if (getSciVersion() >= SCI_VERSION_1_EGA) + if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY) warning("The automatic mapping for General MIDI hasn't been worked on for " "SCI1 games. Music might sound wrong or broken. Please choose another " "music driver for this game (e.g. Adlib or MT-32) if you are " diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp index f0963e7d64..a028617e74 100644 --- a/engines/sci/sound/music.cpp +++ b/engines/sci/sound/music.cpp @@ -75,7 +75,7 @@ void SciMusic::init() { deviceFlags |= MDT_PREFER_GM; // Currently our CMS implementation only supports SCI1(.1) - if (getSciVersion() >= SCI_VERSION_1_EGA && getSciVersion() <= SCI_VERSION_1_1) + if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY && getSciVersion() <= SCI_VERSION_1_1) deviceFlags |= MDT_CMS; uint32 dev = MidiDriver::detectDevice(deviceFlags); @@ -318,8 +318,21 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) { channelFilterMask = pSnd->soundRes->getChannelFilterMask(_pMidiDrv->getPlayId(), _pMidiDrv->hasRhythmChannel()); pSnd->pMidiParser->mainThreadBegin(); + // loadMusic() below calls jumpToTick. + // Disable sound looping and hold before jumpToTick is called, + // otherwise the song may keep looping forever when it ends in + // jumpToTick (e.g. LSL3, when going left from room 210). + uint16 prevLoop = pSnd->loop; + int16 prevHold = pSnd->hold; + pSnd->loop = 0; + pSnd->hold = -1; + pSnd->pMidiParser->loadMusic(track, pSnd, channelFilterMask, _soundVersion); pSnd->reverb = pSnd->pMidiParser->getSongReverb(); + + // Restore looping and hold + pSnd->loop = prevLoop; + pSnd->hold = prevHold; pSnd->pMidiParser->mainThreadEnd(); _mutex.unlock(); } @@ -434,18 +447,26 @@ void SciMusic::soundPlay(MusicEntry *pSnd) { if (pSnd->status != kSoundPaused) pSnd->pMidiParser->sendInitCommands(); pSnd->pMidiParser->setVolume(pSnd->volume); - if (pSnd->status == kSoundStopped) { + + // Disable sound looping and hold before jumpToTick is called, + // otherwise the song may keep looping forever when it ends in jumpToTick. + // This is needed when loading saved games, or when a game + // stops the same sound twice (e.g. LSL3 Amiga, going left from + // room 210 to talk with Kalalau). Fixes bugs #3083151 and #3106107. + uint16 prevLoop = pSnd->loop; + int16 prevHold = pSnd->hold; + pSnd->loop = 0; + pSnd->hold = -1; + + if (pSnd->status == kSoundStopped) pSnd->pMidiParser->jumpToTick(0); - } else { - // Disable sound looping before fast forwarding to the last position, - // when loading a saved game. Fixes bug #3083151. - uint16 prevLoop = pSnd->loop; - pSnd->loop = 0; + else // Fast forward to the last position and perform associated events when loading pSnd->pMidiParser->jumpToTick(pSnd->ticker, true, true, true); - // Restore looping - pSnd->loop = prevLoop; - } + + // Restore looping and hold + pSnd->loop = prevLoop; + pSnd->hold = prevHold; pSnd->pMidiParser->mainThreadEnd(); _mutex.unlock(); } diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp index 45a3e09453..1e6d0aef87 100644 --- a/engines/sci/sound/soundcmd.cpp +++ b/engines/sci/sound/soundcmd.cpp @@ -445,8 +445,16 @@ void SoundCommandParser::processUpdateCues(reg_t obj) { if (musicSlot->fadeCompleted) { musicSlot->fadeCompleted = false; - // We need signal for sci0 at least in iceman as well (room 14, fireworks) - writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + // We need signal for sci0 at least in iceman as well (room 14, + // fireworks). + // It is also needed in other games, e.g. LSL6 when talking to the + // receptionist (bug #3192166). + if (g_sci->getGameId() == GID_LONGBOW && g_sci->getEngineState()->currentRoomNumber() == 95) { + // HACK: Don't set a signal here in the intro of Longbow, as that makes some dialog + // boxes disappear too soon (bug #3044844). + } else { + writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + } if (_soundVersion <= SCI_VERSION_0_LATE) { processStopSound(obj, false); } else { diff --git a/engines/sci/util.cpp b/engines/sci/util.cpp index f6a2465682..f346adddeb 100644 --- a/engines/sci/util.cpp +++ b/engines/sci/util.cpp @@ -30,25 +30,39 @@ namespace Sci { +uint16 READ_SCIENDIAN_UINT16(const void *ptr) { + if (g_sci->isBE()) + return READ_BE_UINT16(ptr); + else + return READ_LE_UINT16(ptr); +} + +void WRITE_SCIENDIAN_UINT16(void *ptr, uint16 val) { + if (g_sci->isBE()) + WRITE_BE_UINT16(ptr, val); + else + WRITE_LE_UINT16(ptr, val); +} + uint16 READ_SCI11ENDIAN_UINT16(const void *ptr) { if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1) return READ_BE_UINT16(ptr); - - return READ_LE_UINT16(ptr); + else + return READ_LE_UINT16(ptr); } uint16 READ_SCI32ENDIAN_UINT16(const void *ptr) { if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_2_1) return READ_BE_UINT16(ptr); - - return READ_LE_UINT16(ptr); + else + return READ_LE_UINT16(ptr); } uint32 READ_SCI11ENDIAN_UINT32(const void *ptr) { if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1) return READ_BE_UINT32(ptr); - - return READ_LE_UINT32(ptr); + else + return READ_LE_UINT32(ptr); } void WRITE_SCI11ENDIAN_UINT16(void *ptr, uint16 val) { diff --git a/engines/sci/util.h b/engines/sci/util.h index d9ced5c9f6..7a2abb1873 100644 --- a/engines/sci/util.h +++ b/engines/sci/util.h @@ -30,6 +30,11 @@ namespace Sci { +// Wrappers for reading/writing 16-bit values in the endianness +// of the original game platform. +uint16 READ_SCIENDIAN_UINT16(const void *ptr); +void WRITE_SCIENDIAN_UINT16(void *ptr, uint16 val); + // Wrappers for reading integer values for SCI1.1+. // Mac versions have big endian data for some fields. uint16 READ_SCI11ENDIAN_UINT16(const void *ptr); diff --git a/engines/sci/video/robot_decoder.cpp b/engines/sci/video/robot_decoder.cpp index bf52de67d5..ecdce3bd6b 100644 --- a/engines/sci/video/robot_decoder.cpp +++ b/engines/sci/video/robot_decoder.cpp @@ -251,6 +251,11 @@ const Graphics::Surface *RobotDecoder::decodeNextFrame() { _fileStream->skip(4); // unknown, almost always 0 uint16 frameX = _fileStream->readUint16(); uint16 frameY = _fileStream->readUint16(); + // TODO: In v4 robot files, frameX and frameY have a different meaning. + // Set them both to 0 for v4 for now, so that robots in PQ:SWAT show up + // correctly. + if (_header.version == 4) + frameX = frameY = 0; uint16 compressedSize = _fileStream->readUint16(); uint16 frameFragments = _fileStream->readUint16(); _fileStream->skip(4); // unknown diff --git a/engines/scumm/he/resource_he.cpp b/engines/scumm/he/resource_he.cpp index 72e7052034..9349e70eb9 100644 --- a/engines/scumm/he/resource_he.cpp +++ b/engines/scumm/he/resource_he.cpp @@ -4,10 +4,6 @@ * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * - * Parts of this code are heavily based on: - * icoutils - A set of programs dealing with MS Windows icons and cursors. - * Copyright (C) 1998-2001 Oskar Liljeblad - * * 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 @@ -36,6 +32,7 @@ #include "audio/decoders/wave.h" #include "graphics/cursorman.h" +#include "graphics/wincursor.h" #include "common/archive.h" #include "common/memstream.h" @@ -43,14 +40,6 @@ namespace Scumm { -#if defined(SCUMM_LITTLE_ENDIAN) -#define LE16(x) -#define LE32(x) -#elif defined(SCUMM_BIG_ENDIAN) -#define LE16(x) ((x) = TO_LE_16(x)) -#define LE32(x) ((x) = TO_LE_32(x)) -#endif - ResExtractor::ResExtractor(ScummEngine_v70he *scumm) : _vm(scumm) { @@ -65,1092 +54,112 @@ ResExtractor::~ResExtractor() { free(cc->palette); } } + memset(_cursorCache, 0, sizeof(_cursorCache)); } ResExtractor::CachedCursor *ResExtractor::findCachedCursor(int id) { - for (int i = 0; i < MAX_CACHED_CURSORS; ++i) { - CachedCursor *cc = &_cursorCache[i]; - if (cc->valid && cc->id == id) { - return cc; - } - } + for (int i = 0; i < MAX_CACHED_CURSORS; ++i) + if (_cursorCache[i].valid && _cursorCache[i].id == id) + return &_cursorCache[i]; + return NULL; } ResExtractor::CachedCursor *ResExtractor::getCachedCursorSlot() { - uint32 min_last_used = 0; + uint32 minLastUsed = 0; CachedCursor *r = NULL; + for (int i = 0; i < MAX_CACHED_CURSORS; ++i) { CachedCursor *cc = &_cursorCache[i]; - if (!cc->valid) { + if (!cc->valid) return cc; - } else { - if (min_last_used == 0 || cc->last_used < min_last_used) { - min_last_used = cc->last_used; - r = cc; - } + + if (minLastUsed == 0 || cc->lastUsed < minLastUsed) { + minLastUsed = cc->lastUsed; + r = cc; } } + assert(r); - free(r->bitmap); - free(r->palette); + delete[] r->bitmap; + delete[] r->palette; memset(r, 0, sizeof(CachedCursor)); return r; } void ResExtractor::setCursor(int id) { - byte *cursorRes = 0; - int cursorsize; - int keycolor = 0; CachedCursor *cc = findCachedCursor(id); + if (cc != NULL) { debug(7, "Found cursor %d in cache slot %lu", id, (long)(cc - _cursorCache)); } else { cc = getCachedCursorSlot(); assert(cc && !cc->valid); - cursorsize = extractResource(id, &cursorRes); - convertIcons(cursorRes, cursorsize, &cc->bitmap, &cc->w, &cc->h, &cc->hotspot_x, &cc->hotspot_y, &keycolor, &cc->palette, &cc->palSize); + + if (!extractResource(id, cc)) + error("Could not extract cursor %d", id); + debug(7, "Adding cursor %d to cache slot %lu", id, (long)(cc - _cursorCache)); - free(cursorRes); + cc->valid = true; cc->id = id; - cc->last_used = g_system->getMillis(); + cc->lastUsed = g_system->getMillis(); } if (cc->palette) CursorMan.replaceCursorPalette(cc->palette, 0, cc->palSize); - _vm->setCursorHotspot(cc->hotspot_x, cc->hotspot_y); - _vm->setCursorFromBuffer(cc->bitmap, cc->w, cc->h, cc->w); + _vm->setCursorHotspot(cc->hotspotX, cc->hotspotY); + _vm->setCursorFromBuffer(cc->bitmap, cc->width, cc->height, cc->width); } -/* - * Static variables - */ -const char *res_types[] = { - /* 0x01: */ - "cursor", "bitmap", "icon", "menu", "dialog", "string", - "fontdir", "font", "accelerator", "rcdata", "messagelist", - "group_cursor", NULL, "group_icon", NULL, - /* the following are not defined in winbase.h, but found in wrc. */ - /* 0x10: */ - "version", "dlginclude", NULL, "plugplay", "vxd", - "anicursor", "aniicon" -}; -#define RES_TYPE_COUNT (sizeof(res_types)/sizeof(char *)) - Win32ResExtractor::Win32ResExtractor(ScummEngine_v70he *scumm) : ResExtractor(scumm) { } -int Win32ResExtractor::extractResource(int resId, byte **data) { - char buf[20]; - - snprintf(buf, sizeof(buf), "%d", resId); - - return extractResource_("group_cursor", buf, data); -} - -int Win32ResExtractor::extractResource_(const char *resType, char *resName, byte **data) { - char *arg_language = NULL; - const char *arg_type = resType; - char *arg_name = resName; - int ressize = 0; - - _arg_raw = false; - - /* translate --type option from resource type string to integer */ - arg_type = res_type_string_to_id(arg_type); - - WinLibrary fi; - - /* initiate stuff */ - fi.memory = NULL; - fi.file = NULL; - +bool Win32ResExtractor::extractResource(int id, CachedCursor *cc) { if (_fileName.empty()) { // We are running for the first time _fileName = _vm->generateFilename(-3); - } - - /* get file size */ - fi.file = SearchMan.createReadStreamForMember(_fileName); - if (!fi.file) { - error("Cannot open file %s", _fileName.c_str()); - } - - fi.total_size = fi.file->size(); - if (fi.total_size == -1) { - error("Cannot get size of file %s", _fileName.c_str()); - goto cleanup; - } - if (fi.total_size == 0) { - error("%s: file has a size of 0", _fileName.c_str()); - goto cleanup; - } - - /* read all of file */ - fi.memory = (byte *)malloc(fi.total_size); - if (fi.file->read(fi.memory, fi.total_size) == 0) { - error("Cannot read from file %s", _fileName.c_str()); - goto cleanup; - } - - /* identify file and find resource table */ - if (!read_library(&fi)) { - /* error reported by read_library */ - goto cleanup; - } - - /* errors will be printed by the callback */ - ressize = do_resources(&fi, arg_type, arg_name, arg_language, data); - - /* free stuff and close file */ - cleanup: - delete fi.file; - free(fi.memory); - - return ressize; -} - - -/** - * Translate a numeric resource type to it's corresponding string type. - * (For informative-ness.) - */ -const char *Win32ResExtractor::res_type_id_to_string(int id) { - if (id == 241) - return "toolbar"; - if (id > 0 && id <= (int)RES_TYPE_COUNT) - return res_types[id-1]; - return NULL; -} - -/** - * Translate a resource type string to integer. - * (Used to convert the --type option.) - */ -const char *Win32ResExtractor::res_type_string_to_id(const char *type) { - static const char *res_type_ids[] = { - "-1", "-2", "-3", "-4", "-5", "-6", "-7", "-8", "-9", "-10", - "-11", "-12", NULL, "-14", NULL, "-16", "-17", NULL, "-19", - "-20", "-21", "-22" - }; - int c; - - if (type == NULL) - return NULL; - - for (c = 0; c < (int)RES_TYPE_COUNT; c++) { - if (res_types[c] != NULL && !scumm_stricmp(type, res_types[c])) - return res_type_ids[c]; - } - - return type; -} - -/** - * Return the resource id quoted if it is a string, otherwise (i.e. if - * it is numeric) just return it. - */ -Common::String Win32ResExtractor::WinResource::getQuotedResourceId() const { - if (numeric_id || id[0] == '\0') - return id; - return '"' + Common::String(id) + '"'; -} - -int Win32ResExtractor::extract_resources(WinLibrary *fi, WinResource *wr, - WinResource *type_wr, WinResource *name_wr, - WinResource *lang_wr, byte **data) { - int size; - bool free_it; - const char *type; - int32 id; - - if (*data) { - error("Win32ResExtractor::extract_resources() more than one cursor"); - return 0; - } - - *data = extract_resource(fi, wr, &size, &free_it, type_wr->id, (lang_wr == NULL ? NULL : lang_wr->id), _arg_raw); - - if (data == NULL) { - error("Win32ResExtractor::extract_resources() problem with resource extraction"); - return 0; - } - - /* get named resource type if possible */ - type = NULL; - if ((id = strtol(type_wr->id, 0, 10)) != 0) - type = res_type_id_to_string(id); - - if (lang_wr != NULL && lang_wr->id[0] != '\0') { - debugC(DEBUG_RESOURCE, "extractCursor(). Found cursor name: %s language: %s [size=%d]", - name_wr->getQuotedResourceId().c_str(), lang_wr->getQuotedResourceId().c_str(), size); - } else { - debugC(DEBUG_RESOURCE, "extractCursor(). Found cursor name: %s [size=%d]", - name_wr->getQuotedResourceId().c_str(), size); - } - return size; -} - -/** - * Extract a resource, returning pointer to data. - */ -byte *Win32ResExtractor::extract_resource(WinLibrary *fi, WinResource *wr, int *size, - bool *free_it, char *type, char *lang, bool raw) { - char *str; - int32 intval; - - /* just return pointer to data if raw */ - if (raw) { - *free_it = false; - /* get_resource_entry will print possible error */ - return get_resource_entry(fi, wr, size); - } - - /* find out how to extract */ - str = type; - if (str != NULL && (intval = strtol(STRIP_RES_ID_FORMAT(str), 0, 10))) { - if (intval == (int)RT_GROUP_ICON) { - *free_it = true; - return extract_group_icon_cursor_resource(fi, wr, lang, size, true); - } - if (intval == (int)RT_GROUP_CURSOR) { - *free_it = true; - return extract_group_icon_cursor_resource(fi, wr, lang, size, false); - } - } - - return NULL; -} - -/** - * Create a complete RT_GROUP_ICON resource, that can be written to - * an `.ico' file without modifications. Returns an allocated - * memory block that should be freed with free() once used. - * - * `root' is the offset in file that specifies the resource. - * `base' is the offset that string pointers are calculated from. - * `ressize' should point to an integer variable where the size of - * the returned memory block will be placed. - * `is_icon' indicates whether resource to be extracted is icon - * or cursor group. - */ -byte *Win32ResExtractor::extract_group_icon_cursor_resource(WinLibrary *fi, WinResource *wr, char *lang, - int *ressize, bool is_icon) { - Win32CursorIconDir *icondir; - Win32CursorIconFileDir *fileicondir; - byte *memory; - int c, offset, skipped; - int size; - - /* get resource data and size */ - icondir = (Win32CursorIconDir *)get_resource_entry(fi, wr, &size); - if (icondir == NULL) { - /* get_resource_entry will print error */ - return NULL; - } - - /* calculate total size of output file */ - RETURN_IF_BAD_POINTER(NULL, icondir->count); - skipped = 0; - for (c = 0; c < FROM_LE_16(icondir->count); c++) { - int level; - int iconsize; - char name[14]; - WinResource *fwr; - - RETURN_IF_BAD_POINTER(NULL, icondir->entries[c]); - /*debug("%d. bytes_in_res=%d width=%d height=%d planes=%d bit_count=%d", c, - FROM_LE_32(icondir->entries[c].bytes_in_res), - (is_icon ? icondir->entries[c].res_info.icon.width : FROM_LE_16(icondir->entries[c].res_info.cursor.width)), - (is_icon ? icondir->entries[c].res_info.icon.height : FROM_LE_16(icondir->entries[c].res_info.cursor.height)), - FROM_LE_16(icondir->entries[c].plane_count), - FROM_LE_16(icondir->entries[c].bit_count));*/ - - /* find the corresponding icon resource */ - snprintf(name, sizeof(name)/sizeof(char), "-%d", FROM_LE_16(icondir->entries[c].res_id)); - fwr = find_resource(fi, (is_icon ? "-3" : "-1"), name, lang, &level); - if (fwr == NULL) { - error("%s: could not find `%s' in `%s' resource.", - _fileName.c_str(), &name[1], (is_icon ? "group_icon" : "group_cursor")); - return NULL; - } - - if (get_resource_entry(fi, fwr, &iconsize) != NULL) { - if (iconsize == 0) { - debugC(DEBUG_RESOURCE, "%s: icon resource `%s' is empty, skipping", _fileName.c_str(), name); - skipped++; - continue; - } - if ((uint32)iconsize != FROM_LE_32(icondir->entries[c].bytes_in_res)) { - debugC(DEBUG_RESOURCE, "%s: mismatch of size in icon resource `%s' and group (%d != %d)", - _fileName.c_str(), name, iconsize, FROM_LE_32(icondir->entries[c].bytes_in_res)); - } - size += iconsize; /* size += FROM_LE_32(icondir->entries[c].bytes_in_res); */ - /* cursor resources have two additional WORDs that contain - * hotspot info */ - if (!is_icon) - size -= sizeof(uint16)*2; - } - } - offset = sizeof(Win32CursorIconFileDir) + (FROM_LE_16(icondir->count)-skipped) * sizeof(Win32CursorIconFileDirEntry); - size += offset; - *ressize = size; - - /* allocate that much memory */ - memory = (byte *)malloc(size); - fileicondir = (Win32CursorIconFileDir *)memory; - - /* transfer Win32CursorIconDir structure members */ - fileicondir->reserved = icondir->reserved; - fileicondir->type = icondir->type; - fileicondir->count = TO_LE_16(FROM_LE_16(icondir->count) - skipped); - - /* transfer each cursor/icon: Win32CursorIconDirEntry and data */ - skipped = 0; - for (c = 0; c < FROM_LE_16(icondir->count); c++) { - int level; - char name[14]; - WinResource *fwr; - byte *data; - - /* find the corresponding icon resource */ - snprintf(name, sizeof(name)/sizeof(char), "-%d", FROM_LE_16(icondir->entries[c].res_id)); - fwr = find_resource(fi, (is_icon ? "-3" : "-1"), name, lang, &level); - if (fwr == NULL) { - error("%s: could not find `%s' in `%s' resource.", - _fileName.c_str(), &name[1], (is_icon ? "group_icon" : "group_cursor")); - return NULL; - } - - /* get data and size of that resource */ - data = (byte *)get_resource_entry(fi, fwr, &size); - if (data == NULL) { - /* get_resource_entry has printed error */ - return NULL; - } - if (size == 0) { - skipped++; - continue; - } - - /* copy ICONDIRENTRY (not including last dwImageOffset) */ - memcpy(&fileicondir->entries[c-skipped], &icondir->entries[c], - sizeof(Win32CursorIconFileDirEntry)-sizeof(uint32)); - - /* special treatment for cursors */ - if (!is_icon) { - fileicondir->entries[c-skipped].width = icondir->entries[c].res_info.cursor.width; - fileicondir->entries[c-skipped].height = TO_LE_16(FROM_LE_16(icondir->entries[c].res_info.cursor.height) / 2); - fileicondir->entries[c-skipped].color_count = 0; - fileicondir->entries[c-skipped].reserved = 0; - } - - /* set image offset and increase it */ - fileicondir->entries[c-skipped].dib_offset = TO_LE_32(offset); - - /* transfer resource into file memory */ - if (is_icon) { - memcpy(&memory[offset], data, FROM_LE_32(icondir->entries[c].bytes_in_res)); - } else { - fileicondir->entries[c-skipped].hotspot_x = ((uint16 *) data)[0]; - fileicondir->entries[c-skipped].hotspot_y = ((uint16 *) data)[1]; - memcpy(&memory[offset], data+sizeof(uint16)*2, - FROM_LE_32(icondir->entries[c].bytes_in_res)-sizeof(uint16)*2); - offset -= sizeof(uint16)*2; - } - - /* increase the offset pointer */ - offset += FROM_LE_32(icondir->entries[c].bytes_in_res); + if (!_exe.loadFromEXE(_fileName)) + error("Cannot open file %s", _fileName.c_str()); } - return memory; -} - -/** - * Check if a chunk of data (determined by offset and size) - * is within the bounds of the WinLibrary file. - * Usually not called directly. - */ -bool Win32ResExtractor::check_offset(byte *memory, int total_size, const char *name, void *offset, int size) { - int need_size = (int)((byte *)offset - memory + size); - - debugC(DEBUG_RESOURCE, "check_offset: size=%x vs %x offset=%x size=%x", - need_size, total_size, (uint)((byte *)offset - memory), size); + Graphics::WinCursorGroup *group = Graphics::WinCursorGroup::createCursorGroup(_exe, id); - if (need_size < 0 || need_size > total_size) { - error("%s: premature end", name); + if (!group) return false; - } - - return true; -} - - -/** - * Do something for each resource matching type, name and lang. - */ -int Win32ResExtractor::do_resources(WinLibrary *fi, const char *type, char *name, char *lang, byte **data) { - WinResource *type_wr; - WinResource *name_wr; - WinResource *lang_wr; - int size; - - type_wr = (WinResource *)calloc(3, sizeof(WinResource)); - name_wr = type_wr + 1; - lang_wr = type_wr + 2; - - size = do_resources_recurs(fi, NULL, type_wr, name_wr, lang_wr, type, name, lang, data); - - free(type_wr); - - return size; -} - -/* what is each entry in this directory level for? type, name or language? */ -#define WINRESOURCE_BY_LEVEL(x) ((x)==0 ? type_wr : ((x)==1 ? name_wr : lang_wr)) - -/* does the id of this entry match the specified id? */ -#define LEVEL_MATCHES(x) (x == NULL || x ## _wr->id[0] == '\0' || compare_resource_id(x ## _wr, x)) -int Win32ResExtractor::do_resources_recurs(WinLibrary *fi, WinResource *base, - WinResource *type_wr, WinResource *name_wr, WinResource *lang_wr, - const char *type, char *name, char *lang, byte **data) { - int c, rescnt; - WinResource *wr; - uint32 size = 0; + Graphics::WinCursor *cursor = group->cursors[0].cursor; - /* get a list of all resources at this level */ - wr = list_resources(fi, base, &rescnt); - if (wr == NULL) { - return size; - } - - /* process each resource listed */ - for (c = 0; c < rescnt; c++) { - /* (over)write the corresponding WinResource holder with the current */ - memcpy(WINRESOURCE_BY_LEVEL(wr[c].level), wr+c, sizeof(WinResource)); - - /* go deeper unless there is something that does NOT match */ - if (LEVEL_MATCHES(type) && LEVEL_MATCHES(name) && LEVEL_MATCHES(lang)) { - if (wr->is_directory) - size = do_resources_recurs(fi, wr+c, type_wr, name_wr, lang_wr, type, name, lang, data); - else - size = extract_resources(fi, wr+c, type_wr, name_wr, lang_wr, data); - } - } - - /* since we're moving back one level after this, unset the - * WinResource holder used on this level */ - memset(WINRESOURCE_BY_LEVEL(wr[0].level), 0, sizeof(WinResource)); - - return size; -} - -bool Win32ResExtractor::compare_resource_id(WinResource *wr, const char *id) { - if (wr->numeric_id) { - int32 cmp1, cmp2; - if (id[0] == '+') - return false; - if (id[0] == '-') - id++; - cmp1 = strtol(wr->id, 0, 10); - cmp2 = strtol(id, 0, 10); - if (!cmp1 || !cmp2 || cmp1 != cmp2) - return false; - } else { - if (id[0] == '-') - return false; - if (id[0] == '+') - id++; - if (strcmp(wr->id, id)) - return false; - } + cc->bitmap = new byte[cursor->getWidth() * cursor->getHeight()]; + cc->width = cursor->getWidth(); + cc->height = cursor->getHeight(); + cc->hotspotX = cursor->getHotspotX(); + cc->hotspotY = cursor->getHotspotY(); - return true; -} + // Convert from the paletted format to the SCUMM palette + const byte *srcBitmap = cursor->getSurface(); -bool Win32ResExtractor::decode_pe_resource_id(WinLibrary *fi, WinResource *wr, uint32 value) { - if (value & IMAGE_RESOURCE_NAME_IS_STRING) { /* numeric id */ - int c, len; - uint16 *mem = (uint16 *) - (fi->first_resource + (value & ~IMAGE_RESOURCE_NAME_IS_STRING)); - - /* copy each char of the string, and terminate it */ - RETURN_IF_BAD_POINTER(false, *mem); - len = FROM_LE_16(mem[0]); - RETURN_IF_BAD_OFFSET(false, &mem[1], sizeof(uint16) * len); - - len = MIN(FROM_LE_16(mem[0]), (uint16)WINRES_ID_MAXLEN); - for (c = 0; c < len; c++) - wr->id[c] = FROM_LE_16(mem[c+1]) & 0x00FF; - wr->id[len] = '\0'; - wr->numeric_id = false; - } else { /* Unicode string id */ - /* translate id into a string */ - snprintf(wr->id, WINRES_ID_MAXLEN, "%d", value); - wr->numeric_id = true; + for (int i = 0; i < cursor->getWidth() * cursor->getHeight(); i++) { + if (srcBitmap[i] == cursor->getKeyColor()) // Transparent + cc->bitmap[i] = 255; + else if (srcBitmap[i] == 0) // Black + cc->bitmap[i] = 253; + else // White + cc->bitmap[i] = 254; } + delete group; return true; } -byte *Win32ResExtractor::get_resource_entry(WinLibrary *fi, WinResource *wr, int *size) { - byte *result; - - Win32ImageResourceDataEntry *dataent; - - dataent = (Win32ImageResourceDataEntry *) wr->children; - RETURN_IF_BAD_POINTER(NULL, *dataent); - *size = FROM_LE_32(dataent->size); - - result = fi->memory + FROM_LE_32(dataent->offset_to_data); - - RETURN_IF_BAD_OFFSET(NULL, result, *size); - - return result; -} - -Win32ResExtractor::WinResource *Win32ResExtractor::list_pe_resources(WinLibrary *fi, Win32ImageResourceDirectory *pe_res, int level, int *count) { - WinResource *wr; - int c, rescnt; - Win32ImageResourceDirectoryEntry *dirent - = (Win32ImageResourceDirectoryEntry *)(pe_res + 1); - - /* count number of `type' resources */ - RETURN_IF_BAD_POINTER(NULL, *dirent); - rescnt = FROM_LE_16(pe_res->number_of_named_entries) + FROM_LE_16(pe_res->number_of_id_entries); - *count = rescnt; - - /* allocate WinResource's */ - wr = (WinResource *)malloc(sizeof(WinResource) * rescnt); - - /* fill in the WinResource's */ - for (c = 0; c < rescnt; c++) { - RETURN_IF_BAD_POINTER(NULL, dirent[c]); - wr[c].this_ = pe_res; - wr[c].level = level; - wr[c].is_directory = ((FROM_LE_32(dirent[c].offset_to_data) & IMAGE_RESOURCE_DATA_IS_DIRECTORY) != 0); - wr[c].children = fi->first_resource + (FROM_LE_32(dirent[c].offset_to_data) & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY); - - /* fill in wr->id, wr->numeric_id */ - if (!decode_pe_resource_id(fi, wr + c, FROM_LE_32(dirent[c].name))) { - free(wr); - return NULL; - } - } - - return wr; -} - - -/** - * Return an array of WinResource's in the current - * resource level specified by _res-> - */ -Win32ResExtractor::WinResource *Win32ResExtractor::list_resources(WinLibrary *fi, WinResource *res, int *count) { - if (res != NULL && !res->is_directory) - return NULL; - - return list_pe_resources(fi, (Win32ImageResourceDirectory *) - (res == NULL ? fi->first_resource : res->children), - (res == NULL ? 0 : res->level+1), - count); -} - -/** - * Read header and get resource directory offset in a Windows library - * (AKA module). - */ -bool Win32ResExtractor::read_library(WinLibrary *fi) { - /* check for DOS header signature `MZ' */ - RETURN_IF_BAD_POINTER(false, MZ_HEADER(fi->memory)->magic); - if (FROM_LE_16(MZ_HEADER(fi->memory)->magic) == IMAGE_DOS_SIGNATURE) { - DOSImageHeader *mz_header = MZ_HEADER(fi->memory); - - RETURN_IF_BAD_POINTER(false, mz_header->lfanew); - - // Apply endian fix (currently only lfanew is used from the DOSImageHeader, - // so we don't bother to 'fix' the rest). - LE32(mz_header->lfanew); - - if (mz_header->lfanew < sizeof(DOSImageHeader)) { - error("%s: not a Windows library", _fileName.c_str()); - return false; - } - } - - /* check for NT header signature `PE' */ - RETURN_IF_BAD_POINTER(false, PE_HEADER(fi->memory)->signature); - if (FROM_LE_32(PE_HEADER(fi->memory)->signature) == IMAGE_NT_SIGNATURE) { - Win32ImageNTHeaders *pe_header; - int d; - - // Fix image header endianess - fix_win32_image_header_endian(PE_HEADER(fi->memory)); - - /* allocate new memory */ - fi->total_size = calc_vma_size(fi); - if (fi->total_size == 0) { - /* calc_vma_size has reported error */ - return false; - } - byte *ptr = (byte *)realloc(fi->memory, fi->total_size); - assert(ptr); - fi->memory = ptr; - - /* relocate memory, start from last section */ - pe_header = PE_HEADER(fi->memory); - RETURN_IF_BAD_POINTER(false, pe_header->file_header.number_of_sections); - - /* we don't need to do OFFSET checking for the sections. - * calc_vma_size has already done that */ - for (d = pe_header->file_header.number_of_sections - 1; d >= 0; d--) { - Win32ImageSectionHeader *pe_sec = PE_SECTIONS(fi->memory) + d; - - if (pe_sec->characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) - continue; - - //if (pe_sec->virtual_address + pe_sec->size_of_raw_data > fi->total_size) - - RETURN_IF_BAD_OFFSET(0, fi->memory + pe_sec->virtual_address, pe_sec->size_of_raw_data); - RETURN_IF_BAD_OFFSET(0, fi->memory + pe_sec->pointer_to_raw_data, pe_sec->size_of_raw_data); - if (FROM_LE_32(pe_sec->virtual_address) != pe_sec->pointer_to_raw_data) { - memmove(fi->memory + pe_sec->virtual_address, - fi->memory + pe_sec->pointer_to_raw_data, - pe_sec->size_of_raw_data); - } - } - - /* find resource directory */ - RETURN_IF_BAD_POINTER(false, pe_header->optional_header.data_directory[IMAGE_DIRECTORY_ENTRY_RESOURCE]); - Win32ImageDataDirectory *dir = pe_header->optional_header.data_directory + IMAGE_DIRECTORY_ENTRY_RESOURCE; - if (dir->size == 0) { - error("%s: file contains no resources", _fileName.c_str()); - return false; - } - - fix_win32_image_data_directory(dir); - - fi->first_resource = fi->memory + dir->virtual_address; - return true; - } - - /* other (unknown) header signature was found */ - error("%s: not a Windows library", _fileName.c_str()); - return false; -} - -/** - * Calculate the total amount of memory needed for a 32-bit Windows - * module. Returns -1 if file was too small. - */ -int Win32ResExtractor::calc_vma_size(WinLibrary *fi) { - Win32ImageSectionHeader *seg; - int c, segcount, size; - - size = 0; - RETURN_IF_BAD_POINTER(-1, PE_HEADER(fi->memory)->file_header.number_of_sections); - segcount = PE_HEADER(fi->memory)->file_header.number_of_sections; - - /* If there are no segments, just process file like it is. - * This is (probably) not the right thing to do, but problems - * will be delt with later anyway. - */ - if (segcount == 0) - return fi->total_size; - - seg = PE_SECTIONS(fi->memory); - RETURN_IF_BAD_POINTER(-1, *seg); - for (c = 0; c < segcount; c++) { - RETURN_IF_BAD_POINTER(0, *seg); - fix_win32_image_section_header(seg); - - size = MAX((uint32)size, seg->virtual_address + seg->size_of_raw_data); - /* I have no idea what misc.virtual_size is for... */ - size = MAX((uint32)size, seg->virtual_address + seg->misc.virtual_size); - seg++; - } - - return size; -} - -Win32ResExtractor::WinResource *Win32ResExtractor::find_with_resource_array(WinLibrary *fi, WinResource *wr, const char *id) { - int c, rescnt; - WinResource *return_wr; - - wr = list_resources(fi, wr, &rescnt); - if (wr == NULL) - return NULL; - - for (c = 0; c < rescnt; c++) { - if (compare_resource_id(&wr[c], id)) { - /* duplicate WinResource and return it */ - return_wr = (WinResource *)malloc(sizeof(WinResource)); - memcpy(return_wr, &wr[c], sizeof(WinResource)); - - /* free old WinResource */ - free(wr); - return return_wr; - } - } - - return NULL; -} - -Win32ResExtractor::WinResource *Win32ResExtractor::find_resource(WinLibrary *fi, const char *type, const char *name, const char *language, int *level) { - WinResource *wr; - - *level = 0; - if (type == NULL) - return NULL; - wr = find_with_resource_array(fi, NULL, type); - if (wr == NULL || !wr->is_directory) - return wr; - - *level = 1; - if (name == NULL) - return wr; - wr = find_with_resource_array(fi, wr, name); - if (wr == NULL || !wr->is_directory) - return wr; - - *level = 2; - if (language == NULL) - return wr; - wr = find_with_resource_array(fi, wr, language); - return wr; -} - -#define ROW_BYTES(bits) ((((bits) + 31) >> 5) << 2) - - -int Win32ResExtractor::convertIcons(byte *data, int datasize, byte **cursor, int *w, int *h, - int *hotspot_x, int *hotspot_y, int *keycolor, byte **pal, int *palSize) { - Win32CursorIconFileDir dir; - Win32CursorIconFileDirEntry *entries = NULL; - uint32 offset; - uint32 c, d; - int completed; - int matched = 0; - Common::MemoryReadStream *in = new Common::MemoryReadStream(data, datasize); - - if (!in->read(&dir, sizeof(Win32CursorIconFileDir)- sizeof(Win32CursorIconFileDirEntry))) - goto cleanup; - fix_win32_cursor_icon_file_dir_endian(&dir); - - if (dir.reserved != 0) { - error("not an icon or cursor file (reserved non-zero)"); - goto cleanup; - } - if (dir.type != 1 && dir.type != 2) { - error("not an icon or cursor file (wrong type)"); - goto cleanup; - } - - entries = (Win32CursorIconFileDirEntry *)malloc(dir.count * sizeof(Win32CursorIconFileDirEntry)); - for (c = 0; c < dir.count; c++) { - if (!in->read(&entries[c], sizeof(Win32CursorIconFileDirEntry))) - goto cleanup; - fix_win32_cursor_icon_file_dir_entry_endian(&entries[c]); - if (entries[c].reserved != 0) - error("reserved is not zero"); - } - - offset = sizeof(Win32CursorIconFileDir) + (dir.count - 1) * (sizeof(Win32CursorIconFileDirEntry)); - - for (completed = 0; completed < dir.count; ) { - uint32 min_offset = 0x7fffffff; - int previous = completed; - - for (c = 0; c < dir.count; c++) { - if (entries[c].dib_offset == offset) { - Win32BitmapInfoHeader bitmap; - Win32RGBQuad *palette = NULL; - uint32 palette_count = 0; - uint32 image_size, mask_size; - uint32 width, height; - byte *image_data = NULL, *mask_data = NULL; - byte *row = NULL; - - if (!in->read(&bitmap, sizeof(Win32BitmapInfoHeader))) - goto local_cleanup; - - fix_win32_bitmap_info_header_endian(&bitmap); - if (bitmap.size < sizeof(Win32BitmapInfoHeader)) { - error("bitmap header is too short"); - goto local_cleanup; - } - if (bitmap.compression != 0) { - error("compressed image data not supported"); - goto local_cleanup; - } - if (bitmap.x_pels_per_meter != 0) - error("x_pels_per_meter field in bitmap should be zero"); - if (bitmap.y_pels_per_meter != 0) - error("y_pels_per_meter field in bitmap should be zero"); - if (bitmap.clr_important != 0) - error("clr_important field in bitmap should be zero"); - if (bitmap.planes != 1) - error("planes field in bitmap should be one"); - if (bitmap.size != sizeof(Win32BitmapInfoHeader)) { - uint32 skip = bitmap.size - sizeof(Win32BitmapInfoHeader); - error("skipping %d bytes of extended bitmap header", skip); - in->seek(skip, SEEK_CUR); - } - offset += bitmap.size; - - if (bitmap.clr_used != 0 || bitmap.bit_count < 24) { - palette_count = (bitmap.clr_used != 0 ? bitmap.clr_used : 1 << bitmap.bit_count); - palette = (Win32RGBQuad *)malloc(sizeof(Win32RGBQuad) * palette_count); - if (!in->read(palette, sizeof(Win32RGBQuad) * palette_count)) - goto local_cleanup; - offset += sizeof(Win32RGBQuad) * palette_count; - } - - width = bitmap.width; - height = ABS(bitmap.height)/2; - - image_size = height * ROW_BYTES(width * bitmap.bit_count); - mask_size = height * ROW_BYTES(width); - - if (entries[c].dib_size != bitmap.size + image_size + mask_size + palette_count * sizeof(Win32RGBQuad)) - debugC(DEBUG_RESOURCE, "incorrect total size of bitmap (%d specified; %d real)", - entries[c].dib_size, - (int)(bitmap.size + image_size + mask_size + palette_count * sizeof(Win32RGBQuad)) - ); - - image_data = (byte *)malloc(image_size); - if (!in->read(image_data, image_size)) - goto local_cleanup; - - mask_data = (byte *)malloc(mask_size); - if (!in->read(mask_data, mask_size)) - goto local_cleanup; - - offset += image_size; - offset += mask_size; - completed++; - matched++; - - *hotspot_x = entries[c].hotspot_x; - *hotspot_y = entries[c].hotspot_y; - *w = width; - *h = height; - *keycolor = 0; - *cursor = (byte *)malloc(width * height); - - row = (byte *)malloc(width * 4); - - for (d = 0; d < height; d++) { - uint32 x; - uint32 y = (bitmap.height < 0 ? d : height - d - 1); - uint32 imod = y * (image_size / height) * 8 / bitmap.bit_count; - uint32 mmod = y * (mask_size / height) * 8; - - for (x = 0; x < width; x++) { - - uint32 color = simple_vec(image_data, x + imod, bitmap.bit_count); - - // We set up cursor palette for default cursor, so use it - if (!simple_vec(mask_data, x + mmod, 1)) { - if (color) { - cursor[0][width * d + x] = 254; // white - } else { - cursor[0][width * d + x] = 253; // black - } - } else { - cursor[0][width * d + x] = 255; // transparent - } - /* - - if (bitmap.bit_count <= 16) { - if (color >= palette_count) { - error("color out of range in image data"); - goto local_cleanup; - } - row[4*x+0] = palette[color].red; - row[4*x+1] = palette[color].green; - row[4*x+2] = palette[color].blue; - - } else { - row[4*x+0] = (color >> 16) & 0xFF; - row[4*x+1] = (color >> 8) & 0xFF; - row[4*x+2] = (color >> 0) & 0xFF; - } - if (bitmap.bit_count == 32) - row[4*x+3] = (color >> 24) & 0xFF; - else - row[4*x+3] = simple_vec(mask_data, x + mmod, 1) ? 0 : 0xFF; - */ - } - - } - - free(row); - free(palette); - if (image_data != NULL) { - free(image_data); - free(mask_data); - } - continue; - - local_cleanup: - - free(row); - free(palette); - if (image_data != NULL) { - free(image_data); - free(mask_data); - } - goto cleanup; - } else { - if (entries[c].dib_offset > offset) - min_offset = MIN(min_offset, entries[c].dib_offset); - } - } - - if (previous == completed) { - if (min_offset < offset) { - error("offset of bitmap header incorrect (too low)"); - goto cleanup; - } - assert(min_offset != 0x7fffffff); - debugC(DEBUG_RESOURCE, "skipping %d bytes of garbage at %d", min_offset-offset, offset); - in->seek(min_offset - offset, SEEK_CUR); - offset = min_offset; - } - } - - free(entries); - return matched; - -cleanup: - - free(entries); - return -1; -} - -uint32 Win32ResExtractor::simple_vec(byte *data, uint32 ofs, byte size) { - switch (size) { - case 1: - return (data[ofs/8] >> (7 - ofs%8)) & 1; - case 2: - return (data[ofs/4] >> ((3 - ofs%4) << 1)) & 3; - case 4: - return (data[ofs/2] >> ((1 - ofs%2) << 2)) & 15; - case 8: - return data[ofs]; - case 16: - return data[2*ofs] | data[2*ofs+1] << 8; - case 24: - return data[3*ofs] | data[3*ofs+1] << 8 | data[3*ofs+2] << 16; - case 32: - return data[4*ofs] | data[4*ofs+1] << 8 | data[4*ofs+2] << 16 | data[4*ofs+3] << 24; - } - - return 0; -} - -void Win32ResExtractor::fix_win32_cursor_icon_file_dir_endian(Win32CursorIconFileDir *obj) { - LE16(obj->reserved); - LE16(obj->type); - LE16(obj->count); -} - -void Win32ResExtractor::fix_win32_bitmap_info_header_endian(Win32BitmapInfoHeader *obj) { - LE32(obj->size); - LE32(obj->width); - LE32(obj->height); - LE16(obj->planes); - LE16(obj->bit_count); - LE32(obj->compression); - LE32(obj->size_image); - LE32(obj->x_pels_per_meter); - LE32(obj->y_pels_per_meter); - LE32(obj->clr_used); - LE32(obj->clr_important); -} - -void Win32ResExtractor::fix_win32_cursor_icon_file_dir_entry_endian(Win32CursorIconFileDirEntry *obj) { - LE16(obj->hotspot_x); - LE16(obj->hotspot_y); - LE32(obj->dib_size); - LE32(obj->dib_offset); -} - -void Win32ResExtractor::fix_win32_image_section_header(Win32ImageSectionHeader *obj) { - LE32(obj->misc.physical_address); - LE32(obj->virtual_address); - LE32(obj->size_of_raw_data); - LE32(obj->pointer_to_raw_data); - LE32(obj->pointer_to_relocations); - LE32(obj->pointer_to_linenumbers); - LE16(obj->number_of_relocations); - LE16(obj->number_of_linenumbers); - LE32(obj->characteristics); -} - -/* fix_win32_image_header_endian: - * NOTE: This assumes that the optional header is always available. - */ -void Win32ResExtractor::fix_win32_image_header_endian(Win32ImageNTHeaders *obj) { - LE32(obj->signature); - LE16(obj->file_header.machine); - LE16(obj->file_header.number_of_sections); - LE32(obj->file_header.time_date_stamp); - LE32(obj->file_header.pointer_to_symbol_table); - LE32(obj->file_header.number_of_symbols); - LE16(obj->file_header.size_of_optional_header); - LE16(obj->file_header.characteristics); - - // FIXME: Does this assert ever trigger? If so, we should modify this function - // to properly deal with it. - assert(obj->file_header.size_of_optional_header >= sizeof(obj->optional_header)); - LE16(obj->optional_header.magic); - LE32(obj->optional_header.size_of_code); - LE32(obj->optional_header.size_of_initialized_data); - LE32(obj->optional_header.size_of_uninitialized_data); - LE32(obj->optional_header.address_of_entry_point); - LE32(obj->optional_header.base_of_code); - LE32(obj->optional_header.base_of_data); - LE32(obj->optional_header.image_base); - LE32(obj->optional_header.section_alignment); - LE32(obj->optional_header.file_alignment); - LE16(obj->optional_header.major_operating_system_version); - LE16(obj->optional_header.minor_operating_system_version); - LE16(obj->optional_header.major_image_version); - LE16(obj->optional_header.minor_image_version); - LE16(obj->optional_header.major_subsystem_version); - LE16(obj->optional_header.minor_subsystem_version); - LE32(obj->optional_header.win32_version_value); - LE32(obj->optional_header.size_of_image); - LE32(obj->optional_header.size_of_headers); - LE32(obj->optional_header.checksum); - LE16(obj->optional_header.subsystem); - LE16(obj->optional_header.dll_characteristics); - LE32(obj->optional_header.size_of_stack_reserve); - LE32(obj->optional_header.size_of_stack_commit); - LE32(obj->optional_header.size_of_heap_reserve); - LE32(obj->optional_header.size_of_heap_commit); - LE32(obj->optional_header.loader_flags); - LE32(obj->optional_header.number_of_rva_and_sizes); -} - -void Win32ResExtractor::fix_win32_image_data_directory(Win32ImageDataDirectory *obj) { - LE32(obj->virtual_address); - LE32(obj->size); -} - - MacResExtractor::MacResExtractor(ScummEngine_v70he *scumm) : ResExtractor(scumm) { _resMgr = NULL; } -int MacResExtractor::extractResource(int id, byte **buf) { +bool MacResExtractor::extractResource(int id, CachedCursor *cc) { // Create the MacResManager if not created already if (_resMgr == NULL) { _resMgr = new Common::MacResManager(); @@ -1158,25 +167,18 @@ int MacResExtractor::extractResource(int id, byte **buf) { error("Cannot open file %s", _fileName.c_str()); } - Common::SeekableReadStream *dataStream = _resMgr->getResource('crsr', 1000 + id); + Common::SeekableReadStream *dataStream = _resMgr->getResource('crsr', id + 1000); if (!dataStream) - error("There is no cursor ID #%d", 1000 + id); - - uint32 size = dataStream->size(); - *buf = (byte *)malloc(size); - dataStream->read(*buf, size); - delete dataStream; + return false; - return size; -} + int keyColor; // HACK: key color is ignored + _resMgr->convertCrsrCursor(dataStream, &cc->bitmap, cc->width, cc->height, cc->hotspotX, cc->hotspotY, + keyColor, _vm->_system->hasFeature(OSystem::kFeatureCursorHasPalette), + &cc->palette, cc->palSize); -int MacResExtractor::convertIcons(byte *data, int datasize, byte **cursor, int *w, int *h, - int *hotspot_x, int *hotspot_y, int *keycolor, byte **palette, int *palSize) { - - _resMgr->convertCrsrCursor(data, datasize, cursor, w, h, hotspot_x, hotspot_y, keycolor, - _vm->_system->hasFeature(OSystem::kFeatureCursorHasPalette), palette, palSize); - return 1; + delete dataStream; + return true; } void ScummEngine_v70he::readRoomsOffsets() { diff --git a/engines/scumm/he/resource_he.h b/engines/scumm/he/resource_he.h index 6b4c3fe493..5d7c70db76 100644 --- a/engines/scumm/he/resource_he.h +++ b/engines/scumm/he/resource_he.h @@ -4,10 +4,6 @@ * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * - * Parts of this code are heavily based on: - * icoutils - A set of programs dealing with MS Windows icons and cursors. - * Copyright (C) 1998-2001 Oskar Liljeblad - * * 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 @@ -31,89 +27,10 @@ #define SCUMM_HE_RESOURCE_HE_H #include "common/macresman.h" +#include "common/winexe_pe.h" namespace Scumm { -#define WINRES_ID_MAXLEN (256) - -/* - * Definitions - */ - -#define MZ_HEADER(x) ((DOSImageHeader *)(x)) - -#define STRIP_RES_ID_FORMAT(x) (x != NULL && (x[0] == '-' || x[0] == '+') ? ++x : x) - -#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 -#define IMAGE_SIZEOF_SHORT_NAME 8 - -#define IMAGE_RESOURCE_NAME_IS_STRING 0x80000000 -#define IMAGE_RESOURCE_DATA_IS_DIRECTORY 0x80000000 - -#define PE_HEADER(module) \ - ((Win32ImageNTHeaders*)((byte *)(module) + \ - (((DOSImageHeader*)(module))->lfanew))) - -#define PE_SECTIONS(module) \ - ((Win32ImageSectionHeader *)((byte *) &PE_HEADER(module)->optional_header + \ - PE_HEADER(module)->file_header.size_of_optional_header)) - -#define IMAGE_DOS_SIGNATURE 0x5A4D /* MZ */ -#define IMAGE_NT_SIGNATURE 0x00004550 /* PE00 */ - -/* The following symbols below and another group a few lines below are defined in - * the windows header, at least in wince and most likely in plain win32 as well. - * Defining them out silences a redefinition warning in gcc. - * If the same problem arises in win32 builds as well, please replace - * _WIN32_WCE with _WIN32 which is also defined in the wince platform. - */ -#ifndef _WIN32_WCE -#define IMAGE_SCN_CNT_CODE 0x00000020 -#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 -#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 -#endif - -// Only IMAGE_DIRECTORY_ENTRY_RESOURCE is used: -#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 -#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 -#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 -#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 -#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 -#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 -#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 -#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 -#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 /* (MIPS GP) */ -#define IMAGE_DIRECTORY_ENTRY_TLS 9 -#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 -#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 -#define IMAGE_DIRECTORY_ENTRY_IAT 12 /* Import Address Table */ -#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 -#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 - -#ifndef _WIN32_WCE -// Only RT_GROUP_CURSOR and RT_GROUP_ICON are used -#define RT_CURSOR 1 -#define RT_BITMAP 2 -#define RT_ICON 3 -#define RT_MENU 4 -#define RT_DIALOG 5 -#define RT_STRING 6 -#define RT_FONTDIR 7 -#define RT_FONT 8 -#define RT_ACCELERATOR 9 -#define RT_RCDATA 10 -#define RT_MESSAGELIST 11 -#define RT_GROUP_CURSOR 12 -#define RT_GROUP_ICON 14 -#endif - -#define RETURN_IF_BAD_POINTER(r, x) \ - if (!check_offset(fi->memory, fi->total_size, _fileName.c_str(), &(x), sizeof(x))) \ - return (r); -#define RETURN_IF_BAD_OFFSET(r, x, s) \ - if (!check_offset(fi->memory, fi->total_size, _fileName.c_str(), x, s)) \ - return (r); - class ScummEngine_v70he; class ResExtractor { @@ -123,312 +40,54 @@ public: void setCursor(int id); - virtual int extractResource(int id, byte **buf) { return 0; } - virtual int convertIcons(byte *data, int datasize, byte **cursor, int *w, int *h, - int *hotspot_x, int *hotspot_y, int *keycolor, - byte **palette, int *palSize) { return 0; } - - enum { - MAX_CACHED_CURSORS = 10 - }; - +protected: struct CachedCursor { bool valid; int id; byte *bitmap; - int w, h; - int hotspot_x, hotspot_y; - uint32 last_used; + int width, height; + int hotspotX, hotspotY; + uint32 lastUsed; byte *palette; int palSize; }; + Common::String _fileName; ScummEngine_v70he *_vm; + virtual bool extractResource(int id, CachedCursor *cc) = 0; + +private: + enum { + MAX_CACHED_CURSORS = 10 + }; + ResExtractor::CachedCursor *findCachedCursor(int id); ResExtractor::CachedCursor *getCachedCursorSlot(); - - bool _arg_raw; - Common::String _fileName; + CachedCursor _cursorCache[MAX_CACHED_CURSORS]; }; class Win32ResExtractor : public ResExtractor { - public: +public: Win32ResExtractor(ScummEngine_v70he *scumm); ~Win32ResExtractor() {} - int extractResource(int id, byte **data); - void setCursor(int id); - int convertIcons(byte *data, int datasize, byte **cursor, int *w, int *h, - int *hotspot_x, int *hotspot_y, int *keycolor, byte **palette, int *palSize); - private: - int extractResource_(const char *resType, char *resName, byte **data); -/* - * Structures - */ - -#include "common/pack-start.h" // START STRUCT PACKING - - struct WinLibrary { - Common::SeekableReadStream *file; - byte *memory; - byte *first_resource; - int total_size; - } PACKED_STRUCT; - - struct WinResource { - char id[256]; - void *this_; - void *children; - int level; - bool numeric_id; - bool is_directory; - - Common::String getQuotedResourceId() const; - } PACKED_STRUCT; - - - struct Win32IconResDir { - byte width; - byte height; - byte color_count; - byte reserved; - } PACKED_STRUCT; - - struct Win32CursorDir { - uint16 width; - uint16 height; - } PACKED_STRUCT; - - struct Win32CursorIconDirEntry { - union { - Win32IconResDir icon; - Win32CursorDir cursor; - } res_info; - uint16 plane_count; - uint16 bit_count; - uint32 bytes_in_res; - uint16 res_id; - } PACKED_STRUCT; - - struct Win32CursorIconDir { - uint16 reserved; - uint16 type; - uint16 count; - Win32CursorIconDirEntry entries[1]; - } PACKED_STRUCT; - - struct Win32CursorIconFileDirEntry { - byte width; - byte height; - byte color_count; - byte reserved; - uint16 hotspot_x; - uint16 hotspot_y; - uint32 dib_size; - uint32 dib_offset; - } PACKED_STRUCT; - - struct Win32CursorIconFileDir { - uint16 reserved; - uint16 type; - uint16 count; - Win32CursorIconFileDirEntry entries[1]; - } PACKED_STRUCT; - - struct Win32BitmapInfoHeader { - uint32 size; - int32 width; - int32 height; - uint16 planes; - uint16 bit_count; - uint32 compression; - uint32 size_image; - int32 x_pels_per_meter; - int32 y_pels_per_meter; - uint32 clr_used; - uint32 clr_important; - } PACKED_STRUCT; - - struct Win32RGBQuad { - byte blue; - byte green; - byte red; - byte reserved; - } PACKED_STRUCT; - - struct Win32ImageResourceDirectoryEntry { - uint32 name; - uint32 offset_to_data; - } PACKED_STRUCT; - - struct Win16NETypeInfo { - uint16 type_id; - uint16 count; - uint32 resloader; // FARPROC16 - smaller? uint16? - } PACKED_STRUCT; - - struct DOSImageHeader { - uint16 magic; - uint16 cblp; - uint16 cp; - uint16 crlc; - uint16 cparhdr; - uint16 minalloc; - uint16 maxalloc; - uint16 ss; - uint16 sp; - uint16 csum; - uint16 ip; - uint16 cs; - uint16 lfarlc; - uint16 ovno; - uint16 res[4]; - uint16 oemid; - uint16 oeminfo; - uint16 res2[10]; - uint32 lfanew; - } PACKED_STRUCT; - - struct Win32ImageFileHeader { - uint16 machine; - uint16 number_of_sections; - uint32 time_date_stamp; - uint32 pointer_to_symbol_table; - uint32 number_of_symbols; - uint16 size_of_optional_header; - uint16 characteristics; - } PACKED_STRUCT; - - struct Win32ImageDataDirectory { - uint32 virtual_address; - uint32 size; - } PACKED_STRUCT; - - struct Win32ImageOptionalHeader { - uint16 magic; - byte major_linker_version; - byte minor_linker_version; - uint32 size_of_code; - uint32 size_of_initialized_data; - uint32 size_of_uninitialized_data; - uint32 address_of_entry_point; - uint32 base_of_code; - uint32 base_of_data; - uint32 image_base; - uint32 section_alignment; - uint32 file_alignment; - uint16 major_operating_system_version; - uint16 minor_operating_system_version; - uint16 major_image_version; - uint16 minor_image_version; - uint16 major_subsystem_version; - uint16 minor_subsystem_version; - uint32 win32_version_value; - uint32 size_of_image; - uint32 size_of_headers; - uint32 checksum; - uint16 subsystem; - uint16 dll_characteristics; - uint32 size_of_stack_reserve; - uint32 size_of_stack_commit; - uint32 size_of_heap_reserve; - uint32 size_of_heap_commit; - uint32 loader_flags; - uint32 number_of_rva_and_sizes; - Win32ImageDataDirectory data_directory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; - } PACKED_STRUCT; - - struct Win32ImageNTHeaders { - uint32 signature; - Win32ImageFileHeader file_header; - Win32ImageOptionalHeader optional_header; - } PACKED_STRUCT; - - struct Win32ImageSectionHeader { - byte name[IMAGE_SIZEOF_SHORT_NAME]; - union { - uint32 physical_address; - uint32 virtual_size; - } misc; - uint32 virtual_address; - uint32 size_of_raw_data; - uint32 pointer_to_raw_data; - uint32 pointer_to_relocations; - uint32 pointer_to_linenumbers; - uint16 number_of_relocations; - uint16 number_of_linenumbers; - uint32 characteristics; - } PACKED_STRUCT; - - struct Win32ImageResourceDataEntry { - uint32 offset_to_data; - uint32 size; - uint32 code_page; - uint32 resource_handle; - } PACKED_STRUCT; - - struct Win32ImageResourceDirectory { - uint32 characteristics; - uint32 time_date_stamp; - uint16 major_version; - uint16 minor_version; - uint16 number_of_named_entries; - uint16 number_of_id_entries; - } PACKED_STRUCT; - -#include "common/pack-end.h" // END STRUCT PACKING - -/* - * Function Prototypes - */ - - WinResource *list_resources(WinLibrary *, WinResource *, int *); - bool read_library(WinLibrary *); - WinResource *find_resource(WinLibrary *, const char *, const char *, const char *, int *); - byte *get_resource_entry(WinLibrary *, WinResource *, int *); - int do_resources(WinLibrary *, const char *, char *, char *, byte **); - bool compare_resource_id(WinResource *, const char *); - const char *res_type_string_to_id(const char *); - - const char *res_type_id_to_string(int); - char *get_destination_name(WinLibrary *, char *, char *, char *); - - byte *extract_resource(WinLibrary *, WinResource *, int *, bool *, char *, char *, bool); - int extract_resources(WinLibrary *, WinResource *, WinResource *, WinResource *, WinResource *, byte **); - byte *extract_group_icon_cursor_resource(WinLibrary *, WinResource *, char *, int *, bool); - - bool decode_pe_resource_id(WinLibrary *, WinResource *, uint32); - WinResource *list_pe_resources(WinLibrary *, Win32ImageResourceDirectory *, int, int *); - int calc_vma_size(WinLibrary *); - int do_resources_recurs(WinLibrary *, WinResource *, WinResource *, WinResource *, WinResource *, const char *, char *, char *, byte **); - WinResource *find_with_resource_array(WinLibrary *, WinResource *, const char *); - - bool check_offset(byte *, int, const char *, void *, int); - - uint32 simple_vec(byte *data, uint32 ofs, byte size); +private: + Common::PEResources _exe; - void fix_win32_cursor_icon_file_dir_endian(Win32CursorIconFileDir *obj); - void fix_win32_bitmap_info_header_endian(Win32BitmapInfoHeader *obj); - void fix_win32_cursor_icon_file_dir_entry_endian(Win32CursorIconFileDirEntry *obj); - void fix_win32_image_section_header(Win32ImageSectionHeader *obj); - void fix_win32_image_header_endian(Win32ImageNTHeaders *obj); - void fix_win32_image_data_directory(Win32ImageDataDirectory *obj); + bool extractResource(int id, CachedCursor *cc); }; class MacResExtractor : public ResExtractor { - public: MacResExtractor(ScummEngine_v70he *scumm); - ~MacResExtractor() { } + ~MacResExtractor() {} private: Common::MacResManager *_resMgr; - int extractResource(int id, byte **buf); - int convertIcons(byte *data, int datasize, byte **cursor, int *w, int *h, - int *hotspot_x, int *hotspot_y, int *keycolor, byte **palette, int *palSize); + bool extractResource(int id, CachedCursor *cc); }; } // End of namespace Scumm diff --git a/engines/sword1/mouse.cpp b/engines/sword1/mouse.cpp index 751b27efc0..87e476e504 100644 --- a/engines/sword1/mouse.cpp +++ b/engines/sword1/mouse.cpp @@ -197,6 +197,7 @@ void Mouse::createPointer(uint32 ptrId, uint32 luggageId) { free(_currentPtr); _currentPtr = NULL; } + if (ptrId) { MousePtr *lugg = NULL; MousePtr *ptr = (MousePtr*)_resMan->openFetchRes(ptrId); @@ -284,6 +285,7 @@ void Mouse::createPointer(uint32 ptrId, uint32 luggageId) { void Mouse::setPointer(uint32 resId, uint32 rate) { _currentPtrId = resId; _frame = 0; + _activeFrame = -1; createPointer(resId, _currentLuggageId); @@ -298,15 +300,24 @@ void Mouse::setPointer(uint32 resId, uint32 rate) { void Mouse::setLuggage(uint32 resId, uint32 rate) { _currentLuggageId = resId; _frame = 0; + _activeFrame = -1; + createPointer(_currentPtrId, resId); } void Mouse::animate() { if ((Logic::_scriptVars[MOUSE_STATUS] == 1) || (_mouseOverride && _currentPtr)) { _frame = (_frame + 1) % _currentPtr->numFrames; + + if (_activeFrame == _frame) + return; + uint8 *ptrData = (uint8*)_currentPtr + sizeof(MousePtr); ptrData += _frame * _currentPtr->sizeX * _currentPtr->sizeY; + CursorMan.replaceCursor(ptrData, _currentPtr->sizeX, _currentPtr->sizeY, _currentPtr->hotSpotX, _currentPtr->hotSpotY, 255); + + _activeFrame = _frame; } } diff --git a/engines/sword1/mouse.h b/engines/sword1/mouse.h index 50b7431c48..1b4ca12183 100644 --- a/engines/sword1/mouse.h +++ b/engines/sword1/mouse.h @@ -99,8 +99,9 @@ private: ObjectMan *_objMan; Common::Point _mouse; - uint32 _currentPtrId, _currentLuggageId, _frame; + uint32 _currentPtrId, _currentLuggageId; MousePtr *_currentPtr; + int _frame, _activeFrame; uint16 _numObjs; uint16 _lastState, _state; uint32 _getOff; diff --git a/engines/sword2/animation.cpp b/engines/sword2/animation.cpp index 3c506c0dae..8d1a9836f4 100644 --- a/engines/sword2/animation.cpp +++ b/engines/sword2/animation.cpp @@ -110,7 +110,7 @@ void MoviePlayer::play(MovieText *movieTexts, uint32 numMovieTexts, uint32 leadI terminated = !playVideo(); - closeTextObject(_currentMovieText, NULL); + closeTextObject(_currentMovieText, NULL, 0); if (terminated) { _snd->stopHandle(*_bgSoundHandle); @@ -165,7 +165,7 @@ void MoviePlayer::openTextObject(uint32 index) { } } -void MoviePlayer::closeTextObject(uint32 index, byte *screen) { +void MoviePlayer::closeTextObject(uint32 index, byte *screen, uint16 pitch) { if (index < _numMovieTexts) { MovieText *text = &_movieTexts[index]; @@ -182,20 +182,21 @@ void MoviePlayer::closeTextObject(uint32 index, byte *screen) { int frameHeight = _decoder->getHeight(); int frameX = (_system->getWidth() - frameWidth) / 2; int frameY = (_system->getHeight() - frameHeight) / 2; + byte black = findBlackPalIndex(); - byte *dst = screen + _textY * _system->getWidth(); + byte *dst = screen + _textY * pitch; for (int y = 0; y < text->_textSprite.h; y++) { if (_textY + y < frameY || _textY + y >= frameY + frameHeight) { - memset(dst + _textX, findBlackPalIndex(), text->_textSprite.w); + memset(dst + _textX, black, text->_textSprite.w); } else { if (frameX > _textX) - memset(dst + _textX, findBlackPalIndex(), frameX - _textX); + memset(dst + _textX, black, frameX - _textX); if (frameX + frameWidth < _textX + text->_textSprite.w) - memset(dst + frameX + frameWidth, findBlackPalIndex(), _textX + text->_textSprite.w - (frameX + frameWidth)); + memset(dst + frameX + frameWidth, black, _textX + text->_textSprite.w - (frameX + frameWidth)); } - dst += _system->getWidth(); + dst += pitch; } } @@ -205,7 +206,7 @@ void MoviePlayer::closeTextObject(uint32 index, byte *screen) { } } -void MoviePlayer::drawTextObject(uint32 index, byte *screen) { +void MoviePlayer::drawTextObject(uint32 index, byte *screen, uint16 pitch) { MovieText *text = &_movieTexts[index]; byte white = findWhitePalIndex(); @@ -217,14 +218,15 @@ void MoviePlayer::drawTextObject(uint32 index, byte *screen) { uint16 height = text->_textSprite.h; // Resize text sprites for PSX version + byte *psxSpriteBuffer = 0; if (Sword2Engine::isPsx()) { height *= 2; - byte *buffer = (byte *)malloc(width * height); - Screen::resizePsxSprite(buffer, src, width, height); - src = buffer; + psxSpriteBuffer = (byte *)malloc(width * height); + Screen::resizePsxSprite(psxSpriteBuffer, src, width, height); + src = psxSpriteBuffer; } - byte *dst = screen + _textY * RENDERWIDE + _textX; + byte *dst = screen + _textY * pitch + _textX; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { @@ -234,12 +236,16 @@ void MoviePlayer::drawTextObject(uint32 index, byte *screen) { dst[x] = white; } src += width; - dst += RENDERWIDE; + dst += pitch; } + + // Free buffer used to resize psx sprite + if (Sword2Engine::isPsx()) + free(psxSpriteBuffer); } } -void MoviePlayer::performPostProcessing(byte *screen) { +void MoviePlayer::performPostProcessing(byte *screen, uint16 pitch) { MovieText *text; int frame = _decoder->getCurFrame(); @@ -261,9 +267,9 @@ void MoviePlayer::performPostProcessing(byte *screen) { _vm->_sound->playCompSpeech(text->_speechId, 16, 0); } if (frame < text->_endFrame) { - drawTextObject(_currentMovieText, screen); + drawTextObject(_currentMovieText, screen, pitch); } else { - closeTextObject(_currentMovieText, screen); + closeTextObject(_currentMovieText, screen, pitch); _currentMovieText++; } } @@ -313,7 +319,7 @@ bool MoviePlayer::playVideo() { } Graphics::Surface *screen = _vm->_system->lockScreen(); - performPostProcessing((byte *)screen->pixels); + performPostProcessing((byte *)screen->pixels, screen->pitch); _vm->_system->unlockScreen(); _vm->_system->updateScreen(); } diff --git a/engines/sword2/animation.h b/engines/sword2/animation.h index 550ac0fac4..afe7dfcc68 100644 --- a/engines/sword2/animation.h +++ b/engines/sword2/animation.h @@ -96,12 +96,12 @@ protected: uint32 _leadOut; int _leadOutFrame; - void performPostProcessing(byte *screen); + void performPostProcessing(byte *screen, uint16 pitch); bool playVideo(); void openTextObject(uint32 index); - void closeTextObject(uint32 index, byte *screen); - void drawTextObject(uint32 index, byte *screen); + void closeTextObject(uint32 index, byte *screen, uint16 pitch); + void drawTextObject(uint32 index, byte *screen, uint16 pitch); byte findBlackPalIndex(); byte findWhitePalIndex(); diff --git a/engines/testbed/graphics.cpp b/engines/testbed/graphics.cpp index a0e2754fe4..3cdff5f432 100644 --- a/engines/testbed/graphics.cpp +++ b/engines/testbed/graphics.cpp @@ -229,16 +229,16 @@ void rotatePalette(byte *palette, int size) { // Rotate the colors starting from address palette "size" times // take a temporary palette color - byte tColor[4] = {0}; + byte tColor[3] = {0}; // save first color in it. - memcpy(tColor, &palette[0], 4 * sizeof(byte)); + memcpy(tColor, &palette[0], 3 * sizeof(byte)); // Move each color upward by 1 for (int i = 0; i < size - 1; i++) { - memcpy(&palette[i * 4], &palette[(i + 1) * 4], 4 * sizeof(byte)); + memcpy(&palette[i * 3], &palette[(i + 1) * 3], 3 * sizeof(byte)); } // Assign last color to tcolor - memcpy(&palette[(size - 1) * 4], tColor, 4 * sizeof(byte)); + memcpy(&palette[(size - 1) * 3], tColor, 3 * sizeof(byte)); } /** diff --git a/engines/tinsel/music.cpp b/engines/tinsel/music.cpp index 29d4dbc92d..7461cfca72 100644 --- a/engines/tinsel/music.cpp +++ b/engines/tinsel/music.cpp @@ -115,26 +115,6 @@ static const int enhancedAudioSCNVersion[] = { 97, 98, 99, 99 // 151-154 }; -// TODO. This mapping is wrong -static const int enhancedAudioSCNVersionALT[] = { - 301, 302, 2, 1, 1, 301, 302, 3, 3, 4, // 1-10 - 4, 5, 6, 1, 7, 8, 9, 10, 8, 11, // 11-20 - 11, 12, 13, 13, 13, 13, 13, 14, 13, 13, // 21-30 - 15, 16, 17, 15, 18, 19, 20, 338, 21, 21, // 31-40 - 341, 342, 22, 22, 23, 24, 25, 26, 27, 28, // 41-50 - 29, 30, 31, 32, 33, 34, 35, 35, 36, 37, // 51-60 - 38, 39, 39, 39, 39, 40, 39, 41, 41, 42, // 61-70 - 43, 42, 44, 45, 41, 46, 48, 47, 48, 49, // 71-80 - 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, // 81-90 - 60, 61, 62, 63, 61, 64, 65, 66, 67, 68, // 91-100 - 69, 70, 68, 71, 72, 73, 74, 75, 12, 76, // 101-110 - 77, 78, 79, 80, 4, 4, 82, 83, 77, 4, // 111-120 - 84, 85, 86, 3124, 88, 89, 90, 88, 2, 2, // 121-130 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 131-140 - 3142, 91, 92, 93, 94, 94, 95, 96, 52, 4, // 141-150 - 97, 98, 99 // 151-153 -}; - int GetTrackNumber(SCNHANDLE hMidi) { for (int i = 0; i < ARRAYSIZE(midiOffsets); i++) if (midiOffsets[i] == hMidi) @@ -179,11 +159,13 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) { // Support for external music from the music enhancement project if (_vm->getFeatures() & GF_ENHANCED_AUDIO_SUPPORT) { int trackNumber = GetTrackNumber(dwFileOffset); + // Track 8 has been removed in the German CD re-release "Neon Edition" + if ((_vm->getFeatures() & GF_ALT_MIDI) && trackNumber >= 8) + trackNumber++; + int track = 0; if (trackNumber >= 0) { - if (_vm->getFeatures() & GF_ALT_MIDI) - track = enhancedAudioSCNVersionALT[trackNumber]; - else if (_vm->getFeatures() & GF_SCNFILES) + if (_vm->getFeatures() & GF_SCNFILES) track = enhancedAudioSCNVersion[trackNumber]; else track = enhancedAudioGRAVersion[trackNumber]; diff --git a/engines/toon/anim.cpp b/engines/toon/anim.cpp index 2e63d89f37..6004aacead 100644 --- a/engines/toon/anim.cpp +++ b/engines/toon/anim.cpp @@ -99,7 +99,7 @@ bool Animation::loadAnimation(Common::String file) { _frames[e]._data = new uint8[decompressedSize]; if (compressedSize < decompressedSize) { decompressLZSS(imageData, _frames[e]._data, decompressedSize); - } else { + } else { memcpy(_frames[e]._data, imageData, compressedSize); } } @@ -151,6 +151,8 @@ void Animation::drawFrame(Graphics::Surface &surface, int32 frame, int32 xx, int int32 offsX = 0; int32 offsY = 0; + _vm->addDirtyRect(xx + _x1 + _frames[frame]._x1, yy + _y1 + _frames[frame]._y1, xx + rectX + _x1 + _frames[frame]._x1 , yy + rectY + _y1 + _frames[frame]._y1); + if (xx + _x1 + _frames[frame]._x1 < 0) { offsX = -(xx + _x1 + _frames[frame]._x1); } @@ -212,14 +214,14 @@ void Animation::drawFrameWithMaskAndScale(Graphics::Surface &surface, int32 fram int32 finalWidth = rectX * scale / 1024; int32 finalHeight = rectY * scale / 1024; - // compute final x1,y1,x2,y2 + // compute final x1, y1, x2, y2 int32 xx1 = xx + _x1 + _frames[frame]._x1 * scale / 1024; int32 yy1 = yy + _y1 + _frames[frame]._y1 * scale / 1024; int32 xx2 = xx1 + finalWidth; int32 yy2 = yy1 + finalHeight; int32 w = _frames[frame]._x2 - _frames[frame]._x1; -// Strangerke - Commented (not used) -// int32 h = _frames[frame]._y2 - _frames[frame]._y1; + + _vm->addDirtyRect(xx1, yy1, xx2, yy2); int32 destPitch = surface.pitch; int32 destPitchMask = mask->getWidth(); @@ -442,7 +444,6 @@ AnimationInstance::AnimationInstance(ToonEngine *vm, AnimationInstanceType type) _layerZ = 0; } - void AnimationInstance::render() { debugC(5, kDebugAnim, "render()"); if (_visible && _animation) { @@ -574,7 +575,7 @@ void AnimationInstance::getRect(int32 *x1, int32 *y1, int32 *x2, int32 *y2) cons int32 finalWidth = rectX * _scale / 1024; int32 finalHeight = rectY * _scale / 1024; - // compute final x1,y1,x2,y2 + // compute final x1, y1, x2, y2 *x1 = _x + _animation->_x1 + _animation->_frames[_currentFrame]._x1 * _scale / 1024; *y1 = _y + _animation->_y1 + _animation->_frames[_currentFrame]._y1 * _scale / 1024; *x2 = *x1 + finalWidth; @@ -604,7 +605,7 @@ void AnimationInstance::setZ(int32 z, bool relative) { void AnimationInstance::setLayerZ(int32 z) { _layerZ = z; - if (_vm->getAnimationManager()->hasInstance(this)) + if (_vm->getAnimationManager()->hasInstance(this)) _vm->getAnimationManager()->updateInstance(this); } @@ -665,8 +666,6 @@ void AnimationInstance::load(Common::ReadStream *stream) { _useMask = stream->readSint32LE(); } - - void AnimationInstance::setLooping(bool enable) { debugC(6, kDebugAnim, "setLooping(%d)", (enable) ? 1 : 0); _looping = enable; @@ -682,7 +681,7 @@ AnimationManager::AnimationManager(ToonEngine *vm) : _vm(vm) { bool AnimationManager::hasInstance(AnimationInstance* instance) { for (uint32 i = 0; i < _instances.size(); i++) { - if(_instances[i] == instance) + if(_instances[i] == instance) return true; } return false; @@ -698,12 +697,12 @@ void AnimationManager::addInstance(AnimationInstance *instance) { // if the instance already exists, we skip the add for (uint32 i = 0; i < _instances.size(); i++) { - if(_instances[i] == instance) + if(_instances[i] == instance) return; } - + int found = -1; - + // here we now do an ordered insert (closer to the original game) for (uint32 i = 0; i < _instances.size(); i++) { if (_instances[i]->getLayerZ() >= instance->getLayerZ()) { @@ -717,7 +716,6 @@ void AnimationManager::addInstance(AnimationInstance *instance) { } else { _instances.insert_at(found, instance); } - } void AnimationManager::removeInstance(AnimationInstance *instance) { diff --git a/engines/toon/anim.h b/engines/toon/anim.h index d7f64ab687..dfb6842b0e 100644 --- a/engines/toon/anim.h +++ b/engines/toon/anim.h @@ -161,7 +161,7 @@ public: AnimationInstance *createNewInstance(AnimationInstanceType type); void addInstance(AnimationInstance *instance); void removeInstance(AnimationInstance *instance); - void updateInstance(AnimationInstance* instance); + void updateInstance(AnimationInstance* instance); void removeAllInstances(AnimationInstanceType type); void render(); void update(int32 timeIncrement); diff --git a/engines/toon/audio.cpp b/engines/toon/audio.cpp index e5e5b85832..d76a63c1ae 100644 --- a/engines/toon/audio.cpp +++ b/engines/toon/audio.cpp @@ -48,7 +48,7 @@ AudioManager::AudioManager(ToonEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixe for (int32 i = 0; i < 16; i++) _channels[i] = NULL; - for (int32 i = 0; i < 4; i++) + for (int32 i = 0; i < 4; i++) _audioPacks[i] = NULL; for (int32 i = 0; i < 4; i++) { @@ -63,6 +63,8 @@ AudioManager::AudioManager(ToonEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixe _voiceMuted = false; _musicMuted = false; _sfxMuted = false; + + _currentMusicChannel = 0; } AudioManager::~AudioManager(void) { @@ -113,24 +115,23 @@ void AudioManager::playMusic(Common::String dir, Common::String music) { return; // see what channel to take - if (_channels[0] && _channels[0]->isPlaying() && _channels[1] && _channels[1]->isPlaying()) { - // take the one that is fading - if (_channels[0]->isFading()) { - _channels[0]->stop(false); - _channels[1]->stop(true); - _currentMusicChannel = 0; - } else { - _channels[1]->stop(false); - _channels[0]->stop(true); - _currentMusicChannel = 1; + // if the current channel didn't really start. reuse this one + if (_channels[_currentMusicChannel] && _channels[_currentMusicChannel]->isPlaying()) { + if (_channels[_currentMusicChannel]->getPlayedSampleCount() < 500) { + _channels[_currentMusicChannel]->stop(false); + _currentMusicChannel = 1 - _currentMusicChannel; + } + else + { + _channels[_currentMusicChannel]->stop(true); } - } else if (_channels[0] && _channels[0]->isPlaying()) { - _channels[0]->stop(true); - _currentMusicChannel = 1; - } else { - if (_channels[1] && _channels[1]->isPlaying()) - _channels[1]->stop(true); - _currentMusicChannel = 0; + } + // go to the next channel + _currentMusicChannel = 1 - _currentMusicChannel; + + // if it's already playing.. stop it quickly (no fade) + if (_channels[_currentMusicChannel] && _channels[_currentMusicChannel]->isPlaying()) { + _channels[_currentMusicChannel]->stop(false); } // no need to delete instance here it will automatically deleted by the mixer is done with it @@ -208,7 +209,6 @@ void AudioManager::stopCurrentVoice() { _channels[2]->stop(false); } - void AudioManager::closeAudioPack(int32 id) { delete _audioPacks[id]; _audioPacks[id] = NULL; @@ -261,6 +261,7 @@ AudioStreamInstance::AudioStreamInstance(AudioManager *man, Audio::Mixer *mixer, _looping = looping; _musicAttenuation = 1000; _deleteFileStream = deleteFileStreamAtEnd; + _playedSamples = 0; // preload one packet if (_totalSize > 0) { @@ -286,7 +287,7 @@ AudioStreamInstance::~AudioStreamInstance() { int AudioStreamInstance::readBuffer(int16 *buffer, const int numSamples) { debugC(5, kDebugAudio, "readBuffer(buffer, %d)", numSamples); - if(_stopped) + if(_stopped) return 0; handleFade(numSamples); @@ -309,6 +310,8 @@ int AudioStreamInstance::readBuffer(int16 *buffer, const int numSamples) { _bufferOffset += leftSamples; } + _playedSamples += numSamples; + return numSamples; } @@ -423,7 +426,7 @@ void AudioStreamInstance::handleFade(int32 numSamples) { debugC(5, kDebugAudio, "handleFade(%d)", numSamples); // Fading enabled only for music - if (_soundType != Audio::Mixer::kMusicSoundType) + if (_soundType != Audio::Mixer::kMusicSoundType) return; int32 finalVolume = _volume; @@ -457,7 +460,7 @@ void AudioStreamInstance::handleFade(int32 numSamples) { _musicAttenuation = 250; } else { _musicAttenuation += numSamples >> 5; - if (_musicAttenuation > 1000) + if (_musicAttenuation > 1000) _musicAttenuation = 1000; } @@ -468,9 +471,11 @@ void AudioStreamInstance::stop(bool fade /*= false*/) { debugC(1, kDebugAudio, "stop(%d)", (fade) ? 1 : 0); if (fade) { - _fadingIn = false; - _fadingOut = true; - _fadeTime = 0; + if (!_fadingOut) { + _fadingIn = false; + _fadingOut = true; + _fadeTime = 0; + } } else { stopNow(); } @@ -551,7 +556,7 @@ void AudioManager::startAmbientSFX(int32 id, int32 delay, int32 mode, int32 volu } } - if (found < 0) + if (found < 0) return; _ambientSFXs[found]._lastTimer = _vm->getOldMilli() - 1; @@ -610,13 +615,14 @@ void AudioManager::killAllAmbientSFX() void AudioManager::updateAmbientSFX() { - if (_vm->getMoviePlayer()->isPlaying()) return; + if (_vm->getMoviePlayer()->isPlaying()) + return; for (int32 i = 0; i < 4; i++) { AudioAmbientSFX* ambient = &_ambientSFXs[i]; if (ambient->_enabled && (ambient->_channel < 0 || !(_channels[ambient->_channel] && _channels[ambient->_channel]->isPlaying()))) { if(ambient->_mode == 1) { - if (_vm->randRange(0, 32767) < ambient->_delay) { + if (_vm->randRange(0, 32767) < ambient->_delay) { ambient->_channel = playSFX(ambient->_id, ambient->_volume, false); } } else { @@ -629,6 +635,5 @@ void AudioManager::updateAmbientSFX() } } - } // End of namespace Toon diff --git a/engines/toon/audio.h b/engines/toon/audio.h index 7c1eedfee9..5feae9c5a1 100644 --- a/engines/toon/audio.h +++ b/engines/toon/audio.h @@ -52,6 +52,10 @@ public: return _fadingIn || _fadingOut; } + int32 getPlayedSampleCount() { + return _playedSamples; + } + void setVolume(int32 volume); protected: int readBuffer(int16 *buffer, const int numSamples); @@ -93,6 +97,7 @@ protected: int32 _volume; int32 _musicAttenuation; bool _deleteFileStream; + int32 _playedSamples; }; class AudioStreamPackage { @@ -133,14 +138,14 @@ public: void playVoice(int32 id, bool genericVoice); int32 playSFX(int32 id, int volume, bool genericSFX); void stopCurrentVoice(); - void stopAllSfxs(); + void stopAllSfxs(); void setMusicVolume(int32 volume); void stopMusic(); void muteVoice(bool mute); void muteMusic(bool mute); void muteSfx(bool mute); bool isVoiceMuted() { return _voiceMuted; } - bool isMusicMuted() { return _musicMuted; } + bool isMusicMuted() { return _musicMuted; } bool isSfxMuted() { return _sfxMuted; } void startAmbientSFX(int32 id, int32 delay, int32 mode, int32 volume); diff --git a/engines/toon/character.cpp b/engines/toon/character.cpp index f1f246e549..a1bd3334c5 100644 --- a/engines/toon/character.cpp +++ b/engines/toon/character.cpp @@ -104,7 +104,7 @@ void Character::setFacing(int32 facing) { _lastWalkTime = _vm->getSystem()->getMillis(); if ((_facing - facing + 8) % 8 > (facing - _facing + 8) % 8) dir = 1; - else + else dir = -1; while (_facing != facing) { @@ -125,14 +125,13 @@ void Character::setFacing(int32 facing) { if (_currentPathNode == 0) playStandingAnim(); else - playWalkAnim(0,0); + playWalkAnim(0, 0); _vm->doFrame(); }; _flags &= ~2; } - _facing = facing; } @@ -140,7 +139,7 @@ void Character::forcePosition(int32 x, int32 y) { debugC(5, kDebugCharacter, "forcePosition(%d, %d)", x, y); - setPosition(x,y); + setPosition(x, y); _finalX = x; _finalY = y; } @@ -176,7 +175,6 @@ bool Character::walkTo(int32 newPosX, int32 newPosY) { if (_x == _finalX && _y == _finalY) return true; - if (_vm->getPathFinding()->findPath(_x, _y, _finalX, _finalY)) { int32 localFinalX = _finalX; @@ -191,7 +189,7 @@ bool Character::walkTo(int32 newPosX, int32 newPosY) { _currentPathNodeCount = _vm->getPathFinding()->getPathNodeCount(); _currentPathNode = 0; stopSpecialAnim(); - + _lastWalkTime = _vm->getSystem()->getMillis(); _numPixelToWalk = 0; @@ -327,11 +325,6 @@ void Character::updateTimers(int32 relativeAdd) { void Character::stopSpecialAnim() { debugC(4, kDebugCharacter, "stopSpecialAnim()"); -// Strangerke - Commented (not used) -#if 0 - if (_animSpecialId != _animSpecialDefaultId) - delete anim; -#endif if (_animScriptId != -1) _vm->getSceneAnimationScript(_animScriptId)->_frozenForConversation = false; @@ -388,7 +381,6 @@ void Character::update(int32 timeIncrement) { if ((_flags & 4) == 0) return; - if (_animScriptId != -1) { _animationInstance = _vm->getSceneAnimation(this->) #endif @@ -423,19 +415,9 @@ void Character::update(int32 timeIncrement) { return; } -// Strangerke - Commented (not used) -#if 0 - if (_animFlags & 8) { - if (anim->_flags7 == 0xff && anim->_flags9 == 0xff) { - // start voice - } - } -#endif - if (_animScriptId != -1) _vm->getSceneAnimationScript(_animScriptId)->_frozenForConversation = true; - - + // TODO setup backup // _animFlags |= 0x10; @@ -494,17 +476,6 @@ void Character::update(int32 timeIncrement) { } } else { nextFrame = currentFrame + 1; -// Strangerke - Commented (not used) -#if 0 - if (!_vm->getAudioManager()->voiceStillPlaying()) { - if (_animFlags & 8) { - if ((anim->_flags9 == 0xff && nextFrame == anim->_flags6) || - (anim->_flags9 != 0xff && nextFrame >= anim->_flags9)) { - // start really talking - } - } - } -#endif if (nextFrame == anim->_flags7 + 1 && (_animFlags & 0x40) == 0) { if (anim->_flags8 != 1 && (_vm->randRange(0, 1) || anim->_flags8 == 2)) { _animFlags |= 0x20; @@ -525,7 +496,6 @@ void Character::update(int32 timeIncrement) { //label78 - #if 0 if (_id == 0) debug(" drew animation name %s / flag %d / frame %d", _specialAnim->_name, _animFlags, nextFrame); @@ -611,7 +581,6 @@ int32 Character::getScale() { } void Character::playWalkAnim(int32 startFrame, int32 endFrame) { - } void Character::setId(int32 id) { @@ -677,7 +646,6 @@ int32 Character::getAnimScript() { } void Character::playTalkAnim() { - } void Character::stopWalk() { @@ -1046,7 +1014,6 @@ void Character::playAnim(int32 animId, int32 unused, int32 flags) { *strchr(animName, '?') = '0' + facing; strcat(animName, ".CAF"); - if (_animScriptId != -1 && (flags & 8) == 0) _vm->getSceneAnimationScript(_animScriptId)->_frozenForConversation = true; @@ -1068,11 +1035,9 @@ void Character::playAnim(int32 animId, int32 unused, int32 flags) { } } - if (_sceneAnimationId > -1) setAnimationInstance(_vm->getSceneAnimation(_sceneAnimationId)->_animInstance); - _animFlags |= flags; delete _specialAnim; diff --git a/engines/toon/character.h b/engines/toon/character.h index d4079d82ef..e870d81813 100644 --- a/engines/toon/character.h +++ b/engines/toon/character.h @@ -49,7 +49,6 @@ struct SpecialCharacterAnimation { byte _flags9; // 25 }; - class Character { public: Character(ToonEngine *vm); @@ -106,7 +105,6 @@ public: int32 getFacingFromDirection(int32 dx, int32 dy); static const SpecialCharacterAnimation *getSpecialAnimation(int32 characterId, int32 animationId); - protected: ToonEngine *_vm; diff --git a/engines/toon/conversation.cpp b/engines/toon/conversation.cpp index 4678ccc1c8..fc846288ef 100644 --- a/engines/toon/conversation.cpp +++ b/engines/toon/conversation.cpp @@ -45,5 +45,4 @@ void Conversation::load(Common::ReadStream *stream, int16 *conversationDataBase) } } - } diff --git a/engines/toon/conversation.h b/engines/toon/conversation.h index 784c681055..0380210e02 100644 --- a/engines/toon/conversation.h +++ b/engines/toon/conversation.h @@ -33,10 +33,10 @@ namespace Toon { class Conversation { public: - int32 _enable; // 00 + int32 _enable; // 00 struct ConvState { - int32 _data2; // 04 + int32 _data2; // 04 int16 _data3; // 08 void *_data4; // 10 } state[10]; diff --git a/engines/toon/drew.cpp b/engines/toon/drew.cpp index eefb4b8efa..4f8152833b 100644 --- a/engines/toon/drew.cpp +++ b/engines/toon/drew.cpp @@ -111,11 +111,10 @@ void CharacterDrew::update(int32 timeIncrement) { _scale = _currentScale; } else if (_currentScale < _scale) { _scale -= timeIncrement * 2; - if (_scale < _currentScale) + if (_scale < _currentScale) _scale = _currentScale; } setPosition(_x, _y); - } int32 CharacterDrew::getRandomIdleAnim() { diff --git a/engines/toon/drew.h b/engines/toon/drew.h index d8091f2225..f248e4aa51 100644 --- a/engines/toon/drew.h +++ b/engines/toon/drew.h @@ -28,7 +28,6 @@ #include "toon/character.h" - namespace Toon { class ToonEngine; @@ -46,7 +45,6 @@ public: int32 getRandomIdleAnim(); protected: int32 _currentScale; - }; } // End of namespace Toon diff --git a/engines/toon/flux.cpp b/engines/toon/flux.cpp index 2b5551732b..034332af56 100644 --- a/engines/toon/flux.cpp +++ b/engines/toon/flux.cpp @@ -96,7 +96,7 @@ int32 CharacterFlux::fixFacingForAnimation(int32 originalFacing, int32 animation if (!facingMask) break; } - + return finalFacing; } diff --git a/engines/toon/flux.h b/engines/toon/flux.h index a90853cb02..136dedd415 100644 --- a/engines/toon/flux.h +++ b/engines/toon/flux.h @@ -26,7 +26,6 @@ #ifndef TOON_FLUX_H #define TOON_FLUX_H - #include "toon/character.h" class ToonEngine; diff --git a/engines/toon/font.cpp b/engines/toon/font.cpp index 8455ca7b61..8192a6f6f1 100644 --- a/engines/toon/font.cpp +++ b/engines/toon/font.cpp @@ -81,6 +81,8 @@ void FontRenderer::renderText(int32 x, int32 y, Common::String origText, int32 m x -= xx / 2; } + _vm->addDirtyRect(x, y, x + xx, y + yy); + int32 curX = x; int32 curY = y; int32 height = 0; @@ -263,8 +265,8 @@ void FontRenderer::renderMultiLineText(int32 x, int32 y, Common::String origText if (x - 30 - maxWidth / 2 < 0) x = maxWidth / 2 + 30; - if (x + 30 + (maxWidth / 2) > 640) - x = 640 - (maxWidth / 2) - 30; + if (x + 30 + (maxWidth / 2) > TOON_SCREEN_WIDTH) + x = TOON_SCREEN_WIDTH - (maxWidth / 2) - 30; // we have good coordinates now, we can render the multi line int32 curX = x; @@ -273,6 +275,8 @@ void FontRenderer::renderMultiLineText(int32 x, int32 y, Common::String origText for (int32 i = 0; i < numLines; i++) { const byte *line = lines[i]; curX = x - lineSize[i] / 2; + _vm->addDirtyRect(curX + _vm->state()->_currentScrollValue, y, curX + lineSize[i] + _vm->state()->_currentScrollValue, curY + height); + while (*line) { byte curChar = textToFont(*line); if (curChar != 32) _currentFont->drawFontFrame(_vm->getMainSurface(), curChar, curX + _vm->state()->_currentScrollValue, curY, _currentFontColor); diff --git a/engines/toon/hotspot.cpp b/engines/toon/hotspot.cpp index 687ea6ee83..df55eadd05 100644 --- a/engines/toon/hotspot.cpp +++ b/engines/toon/hotspot.cpp @@ -78,8 +78,6 @@ int32 Hotspots::Find(int32 x, int32 y) { debugC(6, kDebugHotspot, "Find(%d, %d)", x, y); int32 priority = -1; -// Strangerke - Commented (not used) -// bool found = false; int32 foundId = -1; int32 testId = -1; @@ -91,8 +89,6 @@ int32 Hotspots::Find(int32 x, int32 y) { testId = i; if (_items[testId].getPriority() > priority) { -// Strangerke - Commented (not used) -// found = true; foundId = testId; priority = _items[testId].getPriority(); } diff --git a/engines/toon/movie.cpp b/engines/toon/movie.cpp index c6b57d96e2..4305abb62b 100644 --- a/engines/toon/movie.cpp +++ b/engines/toon/movie.cpp @@ -38,8 +38,8 @@ void ToonstruckSmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, ui Video::SmackerDecoder::handleAudioTrack(track, chunkSize, unpackedSize); } -bool ToonstruckSmackerDecoder::loadFile(const Common::String &filename, int forcedflags) { - debugC(1, kDebugMovie, "loadFile(%s, %d)", filename.c_str(), forcedflags); +bool ToonstruckSmackerDecoder::loadFile(const Common::String &filename) { + debugC(1, kDebugMovie, "loadFile(%s)", filename.c_str()); _lowRes = false; @@ -56,8 +56,6 @@ bool ToonstruckSmackerDecoder::loadFile(const Common::String &filename, int forc return true; } - - return false; } @@ -88,7 +86,7 @@ void Movie::play(Common::String video, int32 flags) { _playing = true; if (flags & 1) _vm->getAudioManager()->setMusicVolume(0); - _decoder->loadFile(video.c_str(), flags); + _decoder->loadFile(video.c_str()); playVideo(isFirstIntroVideo); _vm->flushPalette(false); if (flags & 1) @@ -107,9 +105,9 @@ bool Movie::playVideo(bool isFirstIntroVideo) { if (_decoder->isLowRes()) { // handle manually 2x scaling here Graphics::Surface* surf = _vm->getSystem()->lockScreen(); - for (int y = 0; y < frame->h/2; y++) { - memcpy(surf->getBasePtr(0, y*2+0), frame->getBasePtr(0, y), frame->pitch); - memcpy(surf->getBasePtr(0, y*2+1), frame->getBasePtr(0, y), frame->pitch); + for (int y = 0; y < frame->h / 2; y++) { + memcpy(surf->getBasePtr(0, y * 2 + 0), frame->getBasePtr(0, y), frame->pitch); + memcpy(surf->getBasePtr(0, y * 2 + 1), frame->getBasePtr(0, y), frame->pitch); } _vm->getSystem()->unlockScreen(); } else { @@ -135,11 +133,13 @@ bool Movie::playVideo(bool isFirstIntroVideo) { Common::Event event; while (_vm->getSystem()->getEventManager()->pollEvent(event)) if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE)) { + _vm->dirtyAllScreen(); return false; } _vm->getSystem()->delayMillis(10); } + _vm->dirtyAllScreen(); return !_vm->shouldQuit(); } diff --git a/engines/toon/movie.h b/engines/toon/movie.h index 2a9173850f..bed2ceceae 100644 --- a/engines/toon/movie.h +++ b/engines/toon/movie.h @@ -36,7 +36,7 @@ public: ToonstruckSmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType); virtual ~ToonstruckSmackerDecoder() {} void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize); - bool loadFile(const Common::String &filename, int forcedflags); + bool loadFile(const Common::String &filename); bool isLowRes() { return _lowRes; } protected: bool _lowRes; diff --git a/engines/toon/path.cpp b/engines/toon/path.cpp index 3f948679f4..e0cdf87502 100644 --- a/engines/toon/path.cpp +++ b/engines/toon/path.cpp @@ -186,7 +186,7 @@ int32 PathFinding::findClosestWalkingPoint(int32 xx, int32 yy, int32 *fxx, int32 for (int y = 0; y < _height; y++) { for (int x = 0; x < _width; x++) { - if (isWalkable(x, y) && isLikelyWalkable(x,y)) { + if (isWalkable(x, y) && isLikelyWalkable(x, y)) { int32 ndist = (x - xx) * (x - xx) + (y - yy) * (y - yy); int32 ndist2 = (x - origX) * (x - origX) + (y - origY) * (y - origY); if (currentFound < 0 || ndist < dist || (ndist == dist && ndist2 < dist2)) { @@ -283,8 +283,8 @@ int32 PathFinding::findPath(int32 x, int32 y, int32 destx, int32 desty) { } // first test direct line - if (lineIsWalkable(x,y,destx,desty)) { - walkLine(x,y,destx,desty); + if (lineIsWalkable(x, y, destx, desty)) { + walkLine(x, y, destx, desty); return true; } @@ -317,7 +317,7 @@ int32 PathFinding::findPath(int32 x, int32 y, int32 destx, int32 desty) { int32 curPNode = px + py * _width; if (isWalkable(px, py)) { // walkable ? - int sum = sq[curNode] + wei * (1 + (isLikelyWalkable(px,py) ? 5 : 0)); + int sum = sq[curNode] + wei * (1 + (isLikelyWalkable(px, py) ? 5 : 0)); if (sq[curPNode] > sum || !sq[curPNode]) { int newWeight = abs(destx - px) + abs(desty - py); sq[curPNode] = sum; @@ -438,7 +438,6 @@ void PathFinding::addBlockingEllipse(int32 x1, int32 y1, int32 w, int32 h) { _numBlockingRects++; } - int32 PathFinding::getPathNodeCount() const { return _gridPathCount; } diff --git a/engines/toon/path.h b/engines/toon/path.h index a2b1b7bf92..03d2b188e5 100644 --- a/engines/toon/path.h +++ b/engines/toon/path.h @@ -91,7 +91,6 @@ protected: int32 _gridPathCount; ToonEngine *_vm; - }; } // End of namespace Toon diff --git a/engines/toon/picture.cpp b/engines/toon/picture.cpp index 18e6a8cf7f..b0932bd32a 100644 --- a/engines/toon/picture.cpp +++ b/engines/toon/picture.cpp @@ -23,7 +23,6 @@ * */ - #include "toon/picture.h" #include "toon/tools.h" #include "common/stack.h" @@ -32,7 +31,7 @@ namespace Toon { bool Picture::loadPicture(Common::String file, bool totalPalette /*= false*/) { debugC(1, kDebugPicture, "loadPicture(%s, %d)", file.c_str(), (totalPalette) ? 1 : 0); - + uint32 size = 0; uint8 *fileData = _vm->resources()->getFileData(file, &size); if (!fileData) @@ -49,12 +48,12 @@ bool Picture::loadPicture(Common::String file, bool totalPalette /*= false*/) { decompressLZSS(fileData + 8, _data, dstsize); // size can only be 640x400 or 1280x400 - if (dstsize > 640 * 400 + 768) - _width = 1280; + if (dstsize > TOON_SCREEN_WIDTH * TOON_SCREEN_HEIGHT + 768) + _width = TOON_BACKBUFFER_WIDTH; else - _width = 640; + _width = TOON_SCREEN_WIDTH; - _height = 400; + _height = TOON_SCREEN_HEIGHT; // do we have a palette ? _paletteEntries = (dstsize & 0x7ff) / 3; @@ -69,7 +68,7 @@ bool Picture::loadPicture(Common::String file, bool totalPalette /*= false*/) { } case kCompSPCN: { uint32 decSize = READ_LE_UINT32(fileData + 10); - _data = new uint8[decSize+100]; + _data = new uint8[decSize + 100]; _paletteEntries = READ_LE_UINT16(fileData + 14) / 3; if (_paletteEntries) { @@ -79,12 +78,12 @@ bool Picture::loadPicture(Common::String file, bool totalPalette /*= false*/) { } // size can only be 640x400 or 1280x400 - if (decSize > 640 * 400 + 768) - _width = 1280; + if (decSize > TOON_SCREEN_WIDTH * TOON_SCREEN_HEIGHT + 768) + _width = TOON_BACKBUFFER_WIDTH; else - _width = 640; + _width = TOON_SCREEN_WIDTH; - _height = 400; + _height = TOON_SCREEN_HEIGHT; // decompress the picture into our buffer decompressSPCN(fileData + 16 + _paletteEntries * 3, _data, decSize); @@ -101,12 +100,12 @@ bool Picture::loadPicture(Common::String file, bool totalPalette /*= false*/) { rnc.unpackM1(fileData, _data); // size can only be 640x400 or 1280x400 - if (decSize > 640 * 400 + 768) - _width = 1280; + if (decSize > TOON_SCREEN_WIDTH * TOON_SCREEN_HEIGHT + 768) + _width = TOON_BACKBUFFER_WIDTH; else - _width = 640; + _width = TOON_SCREEN_WIDTH; - _height = 400; + _height = TOON_SCREEN_HEIGHT; return true; } case kCompRNC2: { @@ -119,12 +118,12 @@ bool Picture::loadPicture(Common::String file, bool totalPalette /*= false*/) { decSize = rnc.unpackM2(fileData, _data); - if (decSize > 640 * 400 + 768) - _width = 1280; + if (decSize > TOON_SCREEN_WIDTH * TOON_SCREEN_HEIGHT + 768) + _width = TOON_BACKBUFFER_WIDTH; else - _width = 640; + _width = TOON_SCREEN_WIDTH; - _height = 400; + _height = TOON_SCREEN_HEIGHT; return true; } } @@ -187,6 +186,41 @@ void Picture::drawMask(Graphics::Surface &surface, int32 x, int32 y, int32 dx, i } } +void Picture::drawWithRectList(Graphics::Surface& surface, int32 x, int32 y, int32 dx, int32 dy, Common::Array<Common::Rect>& rectArray) { + + int32 rx = MIN(_width, surface.w - x); + int32 ry = MIN(_height, surface.h - y); + + if (rx < 0 || ry < 0) + return; + + int32 destPitch = surface.pitch; + int32 srcPitch = _width; + + for (uint32 i = 0; i < rectArray.size(); i++) { + + Common::Rect rect = rectArray[i]; + + int32 fillRx = MIN<int32>(rx, rect.right - rect.left); + int32 fillRy = MIN<int32>(ry, rect.bottom - rect.top); + + uint8 *c = _data + _width * (dy + rect.top) + (dx + rect.left); + uint8 *curRow = (uint8 *)surface.pixels + (y + rect.top) * destPitch + (x + rect.left); + + for (int32 yy = 0; yy < fillRy; yy++) { + uint8 *curSrc = c; + uint8 *cur = curRow; + for (int32 xx = 0; xx < fillRx; xx++) { + *cur = *curSrc; + curSrc++; + cur++; + } + curRow += destPitch; + c += srcPitch; + } + } +} + void Picture::draw(Graphics::Surface &surface, int32 x, int32 y, int32 dx, int32 dy) { debugC(6, kDebugPicture, "draw(surface, %d, %d, %d, %d)", x, y, dx, dy); @@ -279,7 +313,6 @@ void Picture::drawLineOnMask(int32 x, int32 y, int32 x2, int32 y2, bool walkable else t = adx; - int32 cdx = (dx << 16) / t; int32 cdy = (dy << 16) / t; @@ -289,15 +322,15 @@ void Picture::drawLineOnMask(int32 x, int32 y, int32 x2, int32 y2, bool walkable int32 rx = bx >> 16; int32 ry = by >> 16; - if( rx >= 0 && rx < _width-1 && ry >= 0 && ry < _height) { // sanity check: some lines in the game + if( rx >= 0 && rx < _width-1 && ry >= 0 && ry < _height) { // sanity check: some lines in the game // were drawing outside the screen causing corruption if (!walkable) { _data[_width * ry + rx] &= 0xe0; - _data[_width * ry + rx+1] &= 0xe0; + _data[_width * ry + rx + 1] &= 0xe0; } else { int32 v = _data[_width * (by >> 16) + rx - 1]; _data[_width * ry + rx] = v; - _data[_width * ry + rx+1] = v; + _data[_width * ry + rx + 1] = v; } } diff --git a/engines/toon/picture.h b/engines/toon/picture.h index 1b0fd7f550..6aca408364 100644 --- a/engines/toon/picture.h +++ b/engines/toon/picture.h @@ -44,6 +44,7 @@ public: bool loadPicture(Common::String file, bool totalPalette = false); void setupPalette(); void draw(Graphics::Surface &surface, int32 x, int32 y, int32 dx, int32 dy); + void drawWithRectList(Graphics::Surface& surface, int32 x, int32 y, int32 dx, int32 dy, Common::Array<Common::Rect>& rectArray); void drawMask(Graphics::Surface &surface, int32 x, int32 y, int32 dx, int32 dy); void drawLineOnMask(int32 x, int32 y, int32 x2, int32 y2, bool walkable); void floodFillNotWalkableOnMask(int32 x, int32 y); diff --git a/engines/toon/resource.cpp b/engines/toon/resource.cpp index 61e3ffb111..b29aa3b72d 100644 --- a/engines/toon/resource.cpp +++ b/engines/toon/resource.cpp @@ -29,13 +29,20 @@ #include "common/substream.h" #include "toon/toon.h" - namespace Toon { -Resources::Resources(ToonEngine *vm) : _vm(vm) { +Resources::Resources(ToonEngine *vm) : _vm(vm), _cacheSize(0) { + _resourceCache.clear(); } Resources::~Resources() { + + while (!_resourceCache.empty()) { + CacheEntry *temp = _resourceCache.back(); + _resourceCache.pop_back(); + delete temp; + } + while(!_pakFiles.empty()) { PakFile *temp = _pakFiles.back(); _pakFiles.pop_back(); @@ -45,8 +52,73 @@ Resources::~Resources() { purgeFileData(); } -void Resources::openPackage(Common::String fileName, bool preloadEntirePackage) { - debugC(1, kDebugResource, "openPackage(%s, %d)", fileName.c_str(), (preloadEntirePackage) ? 1 : 0); +void Resources::removePackageFromCache(Common::String packName) { + // I'm not sure what's a good strategy here. It seems unnecessary to + // actually remove the cached resources, because the player may be + // wandering back and forth between rooms. So for now, do nothing. +} + +bool Resources::getFromCache(Common::String fileName, uint32 *fileSize, uint8 **fileData) { + for (Common::Array<CacheEntry *>::iterator entry = _resourceCache.begin(); entry != _resourceCache.end(); ++entry) { + if ((*entry)->_data && (*entry)->_fileName.compareToIgnoreCase(fileName) == 0) { + debugC(5, kDebugResource, "getFromCache(%s) - Got %d bytes from %s", fileName.c_str(), (*entry)->_size, (*entry)->_packName.c_str()); + (*entry)->_age = 0; + *fileSize = (*entry)->_size; + *fileData = (*entry)->_data; + return true; + } + } + return false; +} + +void Resources::addToCache(Common::String packName, Common::String fileName, uint32 fileSize, uint8 *fileData) { + debugC(5, kDebugResource, "addToCache(%s, %s, %d) - Total Size: %d", packName.c_str(), fileName.c_str(), fileSize, _cacheSize + fileSize); + for (Common::Array<CacheEntry *>::iterator entry = _resourceCache.begin(); entry != _resourceCache.end(); ++entry) { + if ((*entry)->_data) { + (*entry)->_age++; + } + } + _cacheSize += fileSize; + + while (_cacheSize > MAX_CACHE_SIZE) { + CacheEntry *bestEntry = 0; + for (Common::Array<CacheEntry *>::iterator entry = _resourceCache.begin(); entry != _resourceCache.end(); ++entry) { + if ((*entry)->_data) { + if (!bestEntry || ((*entry)->_age >= bestEntry->_age && (*entry)->_size >= bestEntry->_size)) { + bestEntry = *entry; + } + } + } + if (!bestEntry) + break; + + free(bestEntry->_data); + bestEntry->_data = 0; + _cacheSize -= bestEntry->_size; + debugC(5, kDebugResource, "Freed %s (%s) to reclaim %d bytes", bestEntry->_fileName.c_str(), bestEntry->_packName.c_str(), bestEntry->_size); + } + + for (Common::Array<CacheEntry *>::iterator entry = _resourceCache.begin(); entry != _resourceCache.end(); ++entry) { + if (!(*entry)->_data) { + (*entry)->_packName = packName; + (*entry)->_fileName = fileName; + (*entry)->_age = 0; + (*entry)->_size = fileSize; + (*entry)->_data = fileData; + return; + } + } + + CacheEntry *entry = new CacheEntry(); + entry->_packName = packName; + entry->_fileName = fileName; + entry->_size = fileSize; + entry->_data = fileData; + _resourceCache.push_back(entry); +} + +void Resources::openPackage(Common::String fileName) { + debugC(1, kDebugResource, "openPackage(%s)", fileName.c_str()); Common::File file; bool opened = file.open(fileName); @@ -55,15 +127,16 @@ void Resources::openPackage(Common::String fileName, bool preloadEntirePackage) return; PakFile *pakFile = new PakFile(); - pakFile->open(&file, fileName, preloadEntirePackage); + pakFile->open(&file, fileName); - if (preloadEntirePackage) - file.close(); + file.close(); _pakFiles.push_back(pakFile); } void Resources::closePackage(Common::String fileName) { + + removePackageFromCache(fileName); for (uint32 i = 0; i < _pakFiles.size(); i++) { if (_pakFiles[i]->getPackName() == fileName) { delete _pakFiles[i]; @@ -91,13 +164,21 @@ uint8 *Resources::getFileData(Common::String fileName, uint32 *fileSize) { _allocatedFileData.push_back(memory); return memory; } else { + + uint32 locFileSize = 0; + uint8 *locFileData = 0; + + if (getFromCache(fileName, &locFileSize, &locFileData)) { + *fileSize = locFileSize; + return locFileData; + } + for (uint32 i = 0; i < _pakFiles.size(); i++) { - uint32 locFileSize = 0; - uint8 *locFileData = 0; locFileData = _pakFiles[i]->getFileData(fileName, &locFileSize); if (locFileData) { *fileSize = locFileSize; + addToCache(_pakFiles[i]->getPackName(), fileName, locFileSize, locFileData); return locFileData; } } @@ -136,25 +217,16 @@ void Resources::purgeFileData() { } _allocatedFileData.clear(); } + Common::SeekableReadStream *PakFile::createReadStream(Common::String fileName) { debugC(1, kDebugResource, "createReadStream(%s)", fileName.c_str()); - int32 offset = 0; - int32 size = 0; - for (uint32 i = 0; i < _numFiles; i++) { - if (fileName.compareToIgnoreCase(_files[i]._name) == 0) { - size = _files[i]._size; - offset = _files[i]._offset; - break; - } - } - if (!size) - return 0; - - if (_fileHandle) - return new Common::SeekableSubReadStream(_fileHandle, offset, offset + size); + uint32 fileSize = 0; + uint8 *buffer = getFileData(fileName, &fileSize); + if (buffer) + return new Common::MemoryReadStream(buffer, fileSize, DisposeAfterUse::YES); else - return new Common::MemoryReadStream(_buffer + offset, size); + return 0; } uint8 *PakFile::getFileData(Common::String fileName, uint32 *fileSize) { @@ -162,16 +234,26 @@ uint8 *PakFile::getFileData(Common::String fileName, uint32 *fileSize) { for (uint32 i = 0; i < _numFiles; i++) { if (fileName.compareToIgnoreCase(_files[i]._name) == 0) { - *fileSize = _files[i]._size; - return _buffer + _files[i]._offset; + Common::File file; + if (file.open(_packName)) { + *fileSize = _files[i]._size; + file.seek(_files[i]._offset); + + // Use malloc() because that's what MemoryReadStream + // uses to dispose of the memory when it's done. + uint8 *buffer = (uint8 *)malloc(*fileSize); + file.read(buffer, *fileSize); + file.close(); + return buffer; + } } } return 0; } -void PakFile::open(Common::SeekableReadStream *rs, Common::String packName, bool preloadEntirePackage) { - debugC(1, kDebugResource, "open(rs, %d)", (preloadEntirePackage) ? 1 : 0); +void PakFile::open(Common::SeekableReadStream *rs, Common::String packName) { + debugC(1, kDebugResource, "open(rs)"); char buffer[64]; int32 currentPos = 0; @@ -199,30 +281,12 @@ void PakFile::open(Common::SeekableReadStream *rs, Common::String packName, bool _numFiles++; _files.push_back(newFile); } - - if (preloadEntirePackage) { - _bufferSize = rs->size(); - delete[] _buffer; - _buffer = new uint8[_bufferSize]; - rs->seek(0); - rs->read(_buffer, _bufferSize); - } } void PakFile::close() { - delete[] _buffer; - - if (_fileHandle) { - _fileHandle->close(); - delete _fileHandle; - } } PakFile::PakFile() { - _bufferSize = 0; - _buffer = NULL; - - _fileHandle = NULL; } PakFile::~PakFile() { diff --git a/engines/toon/resource.h b/engines/toon/resource.h index e117c8e259..d6ed29b81b 100644 --- a/engines/toon/resource.h +++ b/engines/toon/resource.h @@ -31,6 +31,8 @@ #include "common/file.h" #include "common/stream.h" +#define MAX_CACHE_SIZE (4 * 1024 * 1024) + namespace Toon { class PakFile { @@ -38,7 +40,7 @@ public: PakFile(); ~PakFile(); - void open(Common::SeekableReadStream *rs, Common::String packName, bool preloadEntirePackage); + void open(Common::SeekableReadStream *rs, Common::String packName); uint8 *getFileData(Common::String fileName, uint32 *fileSize); Common::String getPackName() { return _packName; } Common::SeekableReadStream *createReadStream(Common::String fileName); @@ -52,9 +54,6 @@ protected: }; Common::String _packName; - uint8 *_buffer; - int32 _bufferSize; - uint32 _numFiles; Common::Array<File> _files; Common::File *_fileHandle; @@ -62,11 +61,25 @@ protected: class ToonEngine; +class CacheEntry { +public: + CacheEntry() : _age(0), _size(0), _data(0) {} + ~CacheEntry() { + free(_data); + } + + Common::String _packName; + Common::String _fileName; + uint32 _age; + uint32 _size; + uint8 *_data; +}; + class Resources { public: Resources(ToonEngine *vm); ~Resources(); - void openPackage(Common::String file, bool preloadEntirePackage); + void openPackage(Common::String file); void closePackage(Common::String fileName); Common::SeekableReadStream *openFile(Common::String file); uint8 *getFileData(Common::String fileName, uint32 *fileSize); // this memory must be copied to your own structures! @@ -76,6 +89,12 @@ protected: ToonEngine *_vm; Common::Array<uint8 *> _allocatedFileData; Common::Array<PakFile *> _pakFiles; + uint32 _cacheSize; + Common::Array<CacheEntry *> _resourceCache; + + void removePackageFromCache(Common::String packName); + bool getFromCache(Common::String fileName, uint32 *fileSize, uint8 **fileData); + void addToCache(Common::String packName, Common::String fileName, uint32 fileSize, uint8 *fileData); }; } // End of namespace Toon diff --git a/engines/toon/script.cpp b/engines/toon/script.cpp index 3cd56761f6..376b1db621 100644 --- a/engines/toon/script.cpp +++ b/engines/toon/script.cpp @@ -23,7 +23,6 @@ * */ - #include "common/endian.h" #include "common/stream.h" #include "common/util.h" @@ -177,7 +176,7 @@ bool EMCInterpreter::start(EMCState *script, int function) { if (functionOffset == 0xFFFF) return false; - script->ip = &script->dataPtr->data[functionOffset+1]; + script->ip = &script->dataPtr->data[functionOffset + 1]; return true; } diff --git a/engines/toon/script.h b/engines/toon/script.h index 6c46238238..2a74f9084f 100644 --- a/engines/toon/script.h +++ b/engines/toon/script.h @@ -31,7 +31,6 @@ #include "common/func.h" #include "common/iff_container.h" - // Based on Kyra script interpretor namespace Toon { diff --git a/engines/toon/script_func.cpp b/engines/toon/script_func.cpp index adf3a1c9cc..b181591bf0 100644 --- a/engines/toon/script_func.cpp +++ b/engines/toon/script_func.cpp @@ -265,7 +265,6 @@ int32 ScriptFunc::sys_Cmd_Draw_Actor_Standing(EMCState *state) { arg1 = 1; } - if (arg2 > -1) _vm->getDrew()->forceFacing(arg2); @@ -461,7 +460,7 @@ int32 ScriptFunc::sys_Cmd_Actor_Talks(EMCState *state) { } int32 ScriptFunc::sys_Cmd_Say_Lines(EMCState *state) { - + // WORKAROUND: In the scene 4 (Castle), if you click twice on the closed door, Drew disappears // the script makes him disappear for the custom animation and not reappear. if (_vm->state()->_currentScene == 4 && stackPos(1) == 562) { @@ -491,11 +490,11 @@ int32 ScriptFunc::sys_Cmd_Empty_Inventory(EMCState *state) { int32 ScriptFunc::sys_Cmd_Set_Anim_Scale_Size(EMCState *state) { int32 animID = stackPos(0); int32 scale = stackPos(1); - + SceneAnimation *sceneAnim = _vm->getSceneAnimation(animID); if (sceneAnim) { sceneAnim->_animInstance->setUseMask(true); - sceneAnim->_animInstance->setScale(scale,true); + sceneAnim->_animInstance->setScale(scale, true); } return 0; } @@ -939,8 +938,6 @@ int32 ScriptFunc::sys_Cmd_Init_Scene_Anim(EMCState *state) { sceneAnim->_animInstance->setAnimationRange(stackPos(11), stackPos(11)); sceneAnim->_animInstance->setFrame(stackPos(11)); - - debugC(0, 0xfff, "Init Anim %s %d %d %d %d %d %d %d %d %d %d %d %d %d\n", GetText(12, state), stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7), stackPos(8), stackPos(9), stackPos(10), stackPos(11), stackPos(12)); @@ -950,7 +947,7 @@ int32 ScriptFunc::sys_Cmd_Init_Scene_Anim(EMCState *state) { int32 layerZ = stackPos(3); if (dx == -2) - sceneAnim->_animInstance->moveRelative(640, 0, 0); + sceneAnim->_animInstance->moveRelative(TOON_SCREEN_WIDTH, 0, 0); else if (dx < 0) { dx = sceneAnim->_animation->_x1; } @@ -1028,7 +1025,7 @@ int32 ScriptFunc::sys_Cmd_Draw_Scene_Anim_WSA_Frame(EMCState *state) { else if (animId == 20 || animId == 15 || animId == 21 || animId == 16 || animId == 17 || animId == 18) _vm->pauseSceneAnimationScript(_vm->getCurrentUpdatingSceneAnimation(), 1); else if (animId == 9) { - _vm->pauseSceneAnimationScript(_vm->getCurrentUpdatingSceneAnimation(), 6); + _vm->pauseSceneAnimationScript(_vm->getCurrentUpdatingSceneAnimation(), 3); } } @@ -1091,13 +1088,13 @@ int32 ScriptFunc::sys_Cmd_Proceed_To_Next_Chapter(EMCState *state) { } int32 ScriptFunc::sys_Cmd_Play_Sfx_Plus(EMCState *state) { - //debugC(0,0xfff, "playSfx ( %d , %d, %d, %d, %d )", stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + //debugC(0, 0xfff, "playSfx ( %d , %d, %d, %d, %d )", stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); _vm->playSFX(stackPos(0), stackPos(1)); return 0; } int32 ScriptFunc::sys_Cmd_Play_Sfx(EMCState *state) { - //debugC(0,0xfff, "playSfx ( %d , %d)", stackPos(0), stackPos(1)); + //debugC(0, 0xfff, "playSfx ( %d , %d)", stackPos(0), stackPos(1)); _vm->playSFX(stackPos(0), stackPos(1)); return 0; } diff --git a/engines/toon/state.cpp b/engines/toon/state.cpp index f676a65025..0d6977842d 100644 --- a/engines/toon/state.cpp +++ b/engines/toon/state.cpp @@ -119,7 +119,6 @@ State::State(void) { } State::~State(void) { - } int32 State::getGameFlag(int32 flagId) { diff --git a/engines/toon/state.h b/engines/toon/state.h index d31ff4f3c2..63505fd5fb 100644 --- a/engines/toon/state.h +++ b/engines/toon/state.h @@ -81,7 +81,6 @@ public: int32 _nextSpecialEnterX; int32 _nextSpecialEnterY; - bool _timerEnabled[2]; int32 _timerTimeout[2]; int32 _timerDelay[2]; @@ -94,7 +93,6 @@ public: void loadConversations(Common::ReadStream *stream); void saveConversations(Common::WriteStream *stream); - }; } // End of namespace Toon diff --git a/engines/toon/tools.cpp b/engines/toon/tools.cpp index a03a2d57ce..e1478645db 100644 --- a/engines/toon/tools.cpp +++ b/engines/toon/tools.cpp @@ -125,7 +125,6 @@ uint32 decompressSPCN(byte *src, byte *dst, uint32 dstsize) { return (dstp - dst); } - //return codes #define NOT_PACKED 0 #define PACKED_CRC -1 @@ -296,7 +295,6 @@ int32 RncDecoder::unpackM1(const void *input, void *output) { uint16 crcUnpacked = 0; uint16 crcPacked = 0; - _bitBuffl = 0; _bitBuffh = 0; _bitCount = 0; @@ -397,9 +395,6 @@ int32 RncDecoder::unpackM2(const void *input, void *output) { uint16 crcUnpacked = 0; uint16 crcPacked = 0; -// Strangerke - Commented (not used) -// uint16 counts = 0; - _bitBuffl = 0; _bitCount = 0; @@ -429,7 +424,6 @@ int32 RncDecoder::unpackM2(const void *input, void *output) { _srcPtr = inputptr; _dstPtr = (uint8 *)output; - uint16 ofs, len; byte ofs_hi, ofs_lo; diff --git a/engines/toon/tools.h b/engines/toon/tools.h index 05fc5c9cda..1d8b4a6a5b 100644 --- a/engines/toon/tools.h +++ b/engines/toon/tools.h @@ -42,8 +42,8 @@ const uint32 kCompRNC2 = 0x524E4302; #define READ_LE_INT16(x) (int16) READ_LE_UINT16(x) #define READ_LE_INT32(x) (int32) READ_LE_UINT32(x) -#define WRITE_LE_INT16(x,y) WRITE_LE_UINT16(x,(int16)y) -#define WRITE_LE_INT32(x,y) WRITE_LE_UINT32(x,(int32)y) +#define WRITE_LE_INT16(x, y) WRITE_LE_UINT16(x, (int16)y) +#define WRITE_LE_INT32(x, y) WRITE_LE_UINT32(x, (int32)y) uint32 decompressSPCN(byte *src, byte *dst, uint32 dstsize); uint32 decompressLZSS(byte *src, byte *dst, int dstsize); diff --git a/engines/toon/toon.cpp b/engines/toon/toon.cpp index d65230df85..0c7989f317 100644 --- a/engines/toon/toon.cpp +++ b/engines/toon/toon.cpp @@ -49,7 +49,6 @@ namespace Toon { - void ToonEngine::init() { _currentScriptRegion = 0; _resources = new Resources(this); @@ -58,7 +57,7 @@ void ToonEngine::init() { _hotspots = new Hotspots(this); _mainSurface = new Graphics::Surface(); - _mainSurface->create(1280, 400, 1); + _mainSurface->create(TOON_BACKBUFFER_WIDTH, TOON_BACKBUFFER_HEIGHT, 1); _finalPalette = new uint8[768]; _backupPalette = new uint8[768]; @@ -101,14 +100,13 @@ void ToonEngine::init() { SearchMan.addSubDirectoryMatching(gameDataDir, "ACT1"); SearchMan.addSubDirectoryMatching(gameDataDir, "ACT2"); - syncSoundSettings(); _pathFinding = new PathFinding(this); - resources()->openPackage("LOCAL.PAK", true); - resources()->openPackage("ONETIME.PAK", true); - resources()->openPackage("DREW.PAK", true); + resources()->openPackage("LOCAL.PAK"); + resources()->openPackage("ONETIME.PAK"); + resources()->openPackage("DREW.PAK"); for (int32 i = 0; i < 32; i++) _characters[i] = NULL; @@ -262,7 +260,6 @@ void ToonEngine::parseInput() { selectHotspot(); clickEvent(); } - } void ToonEngine::enableTimer(int32 timerId) { @@ -325,8 +322,8 @@ void ToonEngine::updateScrolling(bool force, int32 timeIncrement) { if ((_gameState->_locations[_gameState->_currentScene]._flags & 0x80) == 0) { if (desiredScrollValue < 0) desiredScrollValue = 0; - if (desiredScrollValue >= _currentPicture->getWidth() - 640) - desiredScrollValue = _currentPicture->getWidth() - 640; + if (desiredScrollValue >= _currentPicture->getWidth() - TOON_SCREEN_WIDTH) + desiredScrollValue = _currentPicture->getWidth() - TOON_SCREEN_WIDTH; if (force) { _gameState->_currentScrollValue = desiredScrollValue; @@ -378,12 +375,23 @@ void ToonEngine::updateTimer(int32 timeIncrement) { } void ToonEngine::render() { - if (_gameState->_inCutaway) - _currentCutaway->draw(*_mainSurface, 0, 0, 0, 0); - else - _currentPicture->draw(*_mainSurface, 0, 0, 0, 0); - //_currentMask->drawMask(*_mainSurface,0,0,0,0); + if (_dirtyAll) { + if (_gameState->_inCutaway) + _currentCutaway->draw(*_mainSurface, 0, 0, 0, 0); + else + _currentPicture->draw(*_mainSurface, 0, 0, 0, 0); + _dirtyRects.push_back(Common::Rect(0, 0, TOON_BACKBUFFER_WIDTH, TOON_BACKBUFFER_HEIGHT)); + } else { + if (_gameState->_inCutaway) + _currentCutaway->drawWithRectList(*_mainSurface, 0, 0, 0, 0, _dirtyRects); + else + _currentPicture->drawWithRectList(*_mainSurface, 0, 0, 0, 0, _dirtyRects); + } + + clearDirtyRects(); + + //_currentMask->drawMask(*_mainSurface, 0, 0, 0, 0); _animationManager->render(); drawInfoLine(); @@ -399,7 +407,7 @@ void ToonEngine::render() { sprintf(test, "%d %d / mask %d layer %d z %d", _mouseX, _mouseY, getMask()->getData(_mouseX, _mouseY), getLayerAtPoint(_mouseX, _mouseY), getZAtPoint(_mouseX, _mouseY)); int32 c = *(uint8 *)_mainSurface->getBasePtr(_mouseX, _mouseY); - sprintf(test, "%d %d / color id %d %d,%d,%d", _mouseX, _mouseY, c, _finalPalette[c*3+0], _finalPalette[c*3+1], _finalPalette[c*3+2]); + sprintf(test, "%d %d / color id %d %d,%d,%d", _mouseX, _mouseY, c, _finalPalette[c * 3 + 0], _finalPalette[c * 3 + 1], _finalPalette[c * 3 + 2]); _fontRenderer->setFont(_fontToon); _fontRenderer->renderText(40, 150, Common::String(test), 0); @@ -422,8 +430,8 @@ void ToonEngine::render() { // add a little sleep here int32 newMillis = (int32)_system->getMillis(); int32 sleepMs = 1; // Minimum delay to allow thread scheduling - if ((newMillis - _lastRenderTime) < _tickLength) - sleepMs = _tickLength - (newMillis - _lastRenderTime); + if ((newMillis - _lastRenderTime) < _tickLength * 2) + sleepMs = _tickLength * 2 - (newMillis - _lastRenderTime); assert(sleepMs >= 0); _system->delayMillis(sleepMs); _lastRenderTime = _system->getMillis(); @@ -454,24 +462,24 @@ void ToonEngine::doMagnifierEffect() { 11, 11, 11, 11, 12 }; - byte tempBuffer[25*25]; + byte tempBuffer[25 * 25]; for (int32 y = -12; y <= 12; y++) { for (int32 x = -12; x <= 12; x++) { int32 destPitch = surface.pitch; uint8 *curRow = (uint8 *)surface.pixels + (posY + y) * destPitch + (posX + x); - tempBuffer[(y+12) * 25 + x + 12] = *curRow; + tempBuffer[(y + 12) * 25 + x + 12] = *curRow; } } for (int32 y = -12; y <= 12; y++) { for (int32 x = -12; x <= 12; x++) { int32 dist = y * y + x * x; - if (dist > 144) + if (dist > 144) continue; int32 destPitch = surface.pitch; uint8 *curRow = (uint8 *)surface.pixels + (posY + y) * destPitch + (posX + x); int32 lerp = (512 + intSqrt[dist] * 256 / 12); - *curRow = tempBuffer[(y*lerp/1024+12) * 25 + x*lerp/1024 + 12]; + *curRow = tempBuffer[(y * lerp / 1024 + 12) * 25 + x * lerp / 1024 + 12]; } } } @@ -484,7 +492,50 @@ void ToonEngine::copyToVirtualScreen(bool updateScreen) { _cursorAnimationInstance->setPosition(_mouseX - 40 + state()->_currentScrollValue - _cursorOffsetX, _mouseY - 40 - _cursorOffsetY, 0, false); _cursorAnimationInstance->render(); } - _system->copyRectToScreen((byte *)_mainSurface->pixels + state()->_currentScrollValue, 1280, 0, 0, 640, 400); + + // Handle dirty rects here + static int32 lastScroll = 0; + + if (_dirtyAll || _gameState->_currentScrollValue != lastScroll) { + // we have to refresh everything in case of scrolling. + _system->copyRectToScreen((byte *)_mainSurface->pixels + state()->_currentScrollValue, TOON_BACKBUFFER_WIDTH, 0, 0, TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT); + } else { + + int32 offX = 0; + for (uint i = 0; i < _oldDirtyRects.size(); i++) { + Common::Rect rect = _oldDirtyRects[i]; + rect.translate(-state()->_currentScrollValue, 0); + offX = 0; + if(rect.right <= 0) + continue; + if (rect.left < 0) { + offX = -rect.left; + rect.left = 0; + } + rect.clip(TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT); + if (rect.left >= 0 && rect.top >= 0 && rect.right - rect.left > 0 && rect.bottom - rect.top > 0) { + _system->copyRectToScreen((byte *)_mainSurface->pixels + _oldDirtyRects[i].left + offX + _oldDirtyRects[i].top * TOON_BACKBUFFER_WIDTH, TOON_BACKBUFFER_WIDTH, rect.left , rect.top, rect.right - rect.left, rect.bottom - rect.top); + } + } + + for (uint i = 0; i < _dirtyRects.size(); i++) { + Common::Rect rect = _dirtyRects[i]; + rect.translate(-state()->_currentScrollValue, 0); + offX = 0; + if (rect.right <= 0) + continue; + if (rect.left < 0) { + offX = -rect.left; + rect.left = 0; + } + rect.clip(TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT); + if (rect.left >= 0 && rect.top >= 0 && rect.right - rect.left > 0 && rect.bottom - rect.top > 0) { + _system->copyRectToScreen((byte *)_mainSurface->pixels + _dirtyRects[i].left + offX + _dirtyRects[i].top * TOON_BACKBUFFER_WIDTH, TOON_BACKBUFFER_WIDTH, rect.left , rect.top, rect.right - rect.left, rect.bottom - rect.top); + } + } + } + lastScroll = _gameState->_currentScrollValue; + if (updateScreen) { _system->updateScreen(); _shouldQuit = shouldQuit(); // update game quit flag - this shouldn't be called all the time, as it's a virtual function @@ -589,6 +640,7 @@ bool ToonEngine::showMainmenu(bool &loadedGame) { bool musicPlaying = false; _gameState->_inMenu = true; + dirtyAllScreen(); while (!doExit) { clickingOn = MAINMENUHOTSPOT_NONE; @@ -607,7 +659,15 @@ bool ToonEngine::showMainmenu(bool &loadedGame) { } while (!clickRelease) { - mainmenuPicture->draw(*_mainSurface, 0, 0, 0, 0); + + if(_dirtyAll) { + mainmenuPicture->draw(*_mainSurface, 0, 0, 0, 0); + addDirtyRect(0, 0, TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT); + } else { + mainmenuPicture->drawWithRectList(*_mainSurface, 0, 0, 0, 0, _dirtyRects); + } + + clearDirtyRects(); for (int entryNr = 0; entryNr < MAINMENU_ENTRYCOUNT; entryNr++) { if (entries[entryNr].menuMask & menuMask) { @@ -719,7 +779,7 @@ Common::Error ToonEngine::run() { g_eventRec.registerRandomSource(_rnd, "toon"); - initGraphics(640, 400, true); + initGraphics(TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT, true); init(); // do we need to load directly a game? @@ -817,7 +877,20 @@ ToonEngine::ToonEngine(OSystem *syst, const ADGameDescription *gameDescription) _inventoryIcons = NULL; _inventoryIconSlots = NULL; _genericTexts = NULL; - _audioManager = NULL; + _audioManager = NULL; + _gameState = NULL; + + _locationDirNotVisited = NULL; + _locationDirVisited = NULL; + _specialInfoLine = NULL; + + for (int i = 0; i < 64; i++) { + _sceneAnimations[i]._active = false; + } + + for (int i = 0; i < 32; i++) { + _characters[i] = NULL; + } memset(&_scriptData, 0, sizeof(EMCData)); @@ -859,7 +932,7 @@ ToonEngine::~ToonEngine() { _mainSurface->free(); delete _mainSurface; } - + delete[] _finalPalette; delete[] _backupPalette; delete[] _additionalPalette1; @@ -888,7 +961,6 @@ ToonEngine::~ToonEngine() { delete _pathFinding; - for (int32 i = 0; i < 64; i++) { if (_sceneAnimations[i]._active) { // see if one character shares this instance @@ -950,8 +1022,6 @@ void ToonEngine::simpleUpdate(bool waitCharacterToTalk) { _animationManager->update(elapsedTime); _audioManager->updateAmbientSFX(); render(); - - } void ToonEngine::fixPaletteEntries(uint8 *palette, int num) { @@ -967,13 +1037,8 @@ void ToonEngine::fixPaletteEntries(uint8 *palette, int num) { // adapted from KyraEngine void ToonEngine::updateAnimationSceneScripts(int32 timeElapsed) { - - static int32 numReentrant = 0; numReentrant++; - -// Strangerke - Commented (not used) -// uint32 nextTime = _system->getMillis() + _tickLength; const int startScript = _lastProcessedSceneScript; _updatingSceneScriptRunFlag = true; @@ -1006,10 +1071,8 @@ void ToonEngine::updateAnimationSceneScripts(int32 timeElapsed) { } while (_lastProcessedSceneScript != startScript && !_shouldQuit); - _updatingSceneScriptRunFlag = false; numReentrant--; - } void ToonEngine::loadScene(int32 SceneId, bool forGameLoad) { @@ -1077,10 +1140,9 @@ void ToonEngine::loadScene(int32 SceneId, bool forGameLoad) { _mouseButton = 0; _lastMouseButton = 0x3; - // load package strcpy(temp, createRoomFilename(Common::String::format("%s.PAK", _gameState->_locations[_gameState->_currentScene]._name).c_str()).c_str()); - resources()->openPackage(temp, true); + resources()->openPackage(temp); strcpy(temp, state()->_locations[SceneId]._name); strcat(temp, ".NPP"); @@ -1198,6 +1260,9 @@ void ToonEngine::loadScene(int32 SceneId, bool forGameLoad) { state()->_mouseHidden = false; + clearDirtyRects(); + dirtyAllScreen(); + if (!forGameLoad) { _script->start(&_scriptState[0], 0); @@ -1215,7 +1280,7 @@ void ToonEngine::loadScene(int32 SceneId, bool forGameLoad) { _gameState->_nextSpecialEnterX = -1; _gameState->_nextSpecialEnterY = -1; } - + _script->start(&_scriptState[0], 3); while (_script->run(&_scriptState[0])) @@ -1290,7 +1355,6 @@ void ToonEngine::initChapter() { _script->unload(&data); setupGeneralPalette(); - } void ToonEngine::loadCursor() { @@ -1412,10 +1476,9 @@ void ToonEngine::clickEvent() { return; } - int32 mouseX = _mouseX; if (_gameState->_inCutaway) { - mouseX += 1280; + mouseX += TOON_BACKBUFFER_WIDTH; } // find hotspot @@ -1443,7 +1506,6 @@ void ToonEngine::clickEvent() { } } - if (!currentHot) { int32 xx, yy; @@ -1471,9 +1533,6 @@ void ToonEngine::clickEvent() { int16 command = currentHot->getData(commandId); int16 argument = currentHot->getData(commandId + 1); int16 priority = currentHot->getPriority(); -// Strangerke - Commented (not used) -// int16 ref = currentHot->getRef(); -// int16 pad1 = currentHot->getData(6); if (!_gameState->_inCutaway && !_gameState->_inCloseUp) { if (leftButton && (currentHot->getData(4) != 2 || _gameState->_mouseState >= 0) && currentHot->getData(5) != -1) { @@ -1515,8 +1574,6 @@ void ToonEngine::clickEvent() { break; case 7: // switch to CloseUp -// Strangerke - Commented (not used) -// int closeup = 1; break; case 8: // face flux @@ -1524,11 +1581,6 @@ void ToonEngine::clickEvent() { break; case 9: case 10: -// Strangerke - Commented (not used) -// if (rand() % 1 == 1) { -// } else { -// } - // setFluxFacingPoint(x,y) sayLines(2, argument); break; case 11: @@ -1552,7 +1604,6 @@ void ToonEngine::clickEvent() { int32 val = _scriptState[_currentScriptRegion].regs[4]; currentHot->setData(4, currentHot->getData(4) & val); } - } void ToonEngine::selectHotspot() { @@ -1564,7 +1615,7 @@ void ToonEngine::selectHotspot() { int32 mouseX = _mouseX; if (_gameState->_inCutaway) - mouseX += 1280; + mouseX += TOON_BACKBUFFER_WIDTH; if (_gameState->_sackVisible) { if (_mouseX > 0 && _mouseX < 40 && _mouseY > 356 && _mouseY < 396) { @@ -1722,7 +1773,6 @@ void ToonEngine::exitScene() { strcpy(temp, createRoomFilename(Common::String::format("%s.PAK", _gameState->_locations[_gameState->_currentScene]._name).c_str()).c_str()); resources()->closePackage(temp); - _drew->stopWalk(); _flux->stopWalk(); @@ -1734,7 +1784,7 @@ void ToonEngine::flipScreens() { _gameState->_inCloseUp = !_gameState->_inCloseUp; if (_gameState->_inCloseUp) { - _gameState->_currentScrollValue = 640; + _gameState->_currentScrollValue = TOON_SCREEN_WIDTH; setPaletteEntries(_cutawayPalette, 1, 128); setPaletteEntries(_additionalPalette2, 232, 23); } else { @@ -1750,9 +1800,9 @@ void ToonEngine::fadeIn(int32 numFrames) { uint8 vmpalette[3 * 256]; for (int32 i = 0; i < 256; i++) { - vmpalette[i*3+0] = f * _finalPalette[i*3+0] / (numFrames - 1); - vmpalette[i*3+1] = f * _finalPalette[i*3+1] / (numFrames - 1); - vmpalette[i*3+2] = f * _finalPalette[i*3+2] / (numFrames - 1); + vmpalette[i * 3 + 0] = f * _finalPalette[i * 3 + 0] / (numFrames - 1); + vmpalette[i * 3 + 1] = f * _finalPalette[i * 3 + 1] / (numFrames - 1); + vmpalette[i * 3 + 2] = f * _finalPalette[i * 3 + 2] / (numFrames - 1); } _system->getPaletteManager()->setPalette(vmpalette, 0, 256); _system->updateScreen(); @@ -1768,9 +1818,9 @@ void ToonEngine::fadeOut(int32 numFrames) { for (int32 f = 0; f < numFrames; f++) { uint8 vmpalette[3 * 256]; for (int32 i = 0; i < 256; i++) { - vmpalette[i*3+0] = (numFrames - f - 1) * oldpalette[i*3+0] / (numFrames - 1); - vmpalette[i*3+1] = (numFrames - f - 1) * oldpalette[i*3+1] / (numFrames - 1); - vmpalette[i*3+2] = (numFrames - f - 1) * oldpalette[i*3+2] / (numFrames - 1); + vmpalette[i * 3 + 0] = (numFrames - f - 1) * oldpalette[i * 3 + 0] / (numFrames - 1); + vmpalette[i * 3 + 1] = (numFrames - f - 1) * oldpalette[i * 3 + 1] / (numFrames - 1); + vmpalette[i * 3 + 2] = (numFrames - f - 1) * oldpalette[i * 3 + 2] / (numFrames - 1); } _system->getPaletteManager()->setPalette(vmpalette, 0, 256); _system->updateScreen(); @@ -1824,11 +1874,11 @@ int32 ToonEngine::getScaleAtPoint(int32 x, int32 y) { return 1024; // clamp values - x = MIN<int32>(1279, MAX<int32>(0, x)); - y = MIN<int32>(399, MAX<int32>(0, y)); + x = MIN<int32>(TOON_BACKBUFFER_WIDTH - 1, MAX<int32>(0, x)); + y = MIN<int32>(TOON_BACKBUFFER_HEIGHT - 1, MAX<int32>(0, y)); int32 maskData = _currentMask->getData(x, y) & 0x1f; - return _roomScaleData[maskData+2] * 1024 / 100; + return _roomScaleData[maskData + 2] * 1024 / 100; } int32 ToonEngine::getLayerAtPoint(int32 x, int32 y) { @@ -1836,11 +1886,11 @@ int32 ToonEngine::getLayerAtPoint(int32 x, int32 y) { return 0; // clamp values - x = MIN<int32>(1279, MAX<int32>(0, x)); - y = MIN<int32>(399, MAX<int32>(0, y)); + x = MIN<int32>(TOON_BACKBUFFER_WIDTH - 1, MAX<int32>(0, x)); + y = MIN<int32>(TOON_BACKBUFFER_HEIGHT - 1, MAX<int32>(0, y)); int32 maskData = _currentMask->getData(x, y) & 0x1f; - return _roomScaleData[maskData+130] << 5; + return _roomScaleData[maskData + 130] << 5; } int32 ToonEngine::getZAtPoint(int32 x, int32 y) { @@ -1917,25 +1967,11 @@ void ToonEngine::sayLines(int numLines, int dialogId) { if (oldShowMouse) Game.MouseHiddenCount = 0; #endif - } int32 ToonEngine::simpleCharacterTalk(int32 dialogid) { int32 myId = 0; -// Strangerke - Commented (not used) -#if 0 - char *myLine; - if (dialogid < 1000) { - myLine = _roomTexts->getText(dialogid); - myId = dialogid; - } else { - myLine = _genericTexts->getText(dialogid - 1000); - myId = dialogid - 1000; - } - debugC(0, 0xfff, "Talker = %d will say '%s' \n", READ_LE_UINT16(c - 2), myLine); -#endif - if (_audioManager->voiceStillPlaying()) _audioManager->stopCurrentVoice(); @@ -1952,14 +1988,6 @@ int32 ToonEngine::simpleCharacterTalk(int32 dialogid) { void ToonEngine::playTalkAnimOnCharacter(int32 animID, int32 characterId, bool talker) { if (animID || talker) { -// Strangerke - Commented (not used) -#if 0 - if (_gameState->_inCutaway || _gameState->_inInventory) { - if (talker) { - // character talks - } - } else -#endif if (characterId == 0) { _drew->playAnim(animID, 0, (talker ? 8 : 0) + 2); } else if (characterId == 1) { @@ -2009,7 +2037,6 @@ int32 ToonEngine::characterTalk(int32 dialogid, bool blocking) { _gameState->_mouseHidden = true; } - // get what is before the string int a = READ_LE_UINT16(myLine - 2); char *b = myLine - 2 - 4 * a; @@ -2020,14 +2047,6 @@ int32 ToonEngine::characterTalk(int32 dialogid, bool blocking) { char *e = c - 2 - 4 * numParticipants; READ_LE_UINT16(e); -// Strangerke - Commented (not used) -// char *g = e - 2 * f; - - // flag as talking -// Strangerke - Commented (not used) -// char *h = c; - - // if one voice is still playing, wait ! if (blocking) { while (_audioManager->voiceStillPlaying() && !_shouldQuit) @@ -2085,8 +2104,6 @@ int32 ToonEngine::characterTalk(int32 dialogid, bool blocking) { _currentTextLine = myLine; _currentTextLineCharacterId = talkerId; _currentTextLineId = dialogid; - - } else { Character *character = getCharacterById(talkerId); if (character) @@ -2095,7 +2112,6 @@ int32 ToonEngine::characterTalk(int32 dialogid, bool blocking) { debugC(0, 0xfff, "Talker = %d (num participants : %d) will say '%s'", (int)talkerId , (int)numParticipants, myLine); - getTextPosition(talkerId, &_currentTextLineX, &_currentTextLineY); if (dialogid < 1000) { @@ -2115,8 +2131,6 @@ int32 ToonEngine::characterTalk(int32 dialogid, bool blocking) { if (character) character->setTalking(false); } - - return 1; } @@ -2131,7 +2145,7 @@ void ToonEngine::haveAConversation(int32 convId) { _gameState->_currentConversationId = convId; // change the music to the "conversation" music if needed. - playRoomMusic(); + playRoomMusic(); if (conv->_enable) { // fix dialog script based on new flags @@ -2149,7 +2163,6 @@ void ToonEngine::haveAConversation(int32 convId) { doFrame(); } - _mouseButton = 0; _gameState->_firstConverstationLine = true; @@ -2178,11 +2191,13 @@ void ToonEngine::haveAConversation(int32 convId) { a++; } } - if (_shouldQuit) return; + + if (_shouldQuit) + return; + _gameState->_showConversationIcons = false; _gameState->_mouseHidden = 1; - if (selected < 0 || selected == 1 || selected == 3) { if (_gameState->_firstConverstationLine) processConversationClick(conv, 3); @@ -2194,9 +2209,6 @@ void ToonEngine::haveAConversation(int32 convId) { } } -// Strangerke - Commented (not used) -// int cur = 0; - for (int i = 0; i < 10; i++) { if (conv->state[i]._data2 == 2) { if (i != 3) @@ -2211,8 +2223,7 @@ void ToonEngine::haveAConversation(int32 convId) { _gameState->_sackVisible = true; // switch back to original music - playRoomMusic(); - + playRoomMusic(); } void ToonEngine::drawConversationIcons() { @@ -2334,7 +2345,6 @@ retry: break; } } - } // hardcoded conversation flag to know if one dialogue icon must be displayed or not @@ -2495,7 +2505,7 @@ int32 ToonEngine::runConversationCommand(int16 **command) { int16 *v5 = *command; int v2 = READ_LE_INT16(v5); - int v4 = READ_LE_INT16(v5+1); + int v4 = READ_LE_INT16(v5 + 1); int result = v2 - 100; switch (v2) { case 100: @@ -2528,9 +2538,6 @@ int32 ToonEngine::runConversationCommand(int16 **command) { } int32 ToonEngine::waitTicks(int32 numTicks, bool breakOnMouseClick) { -// Strangerke - Commented (not used) -// Common::EventManager *_event = _system->getEventManager(); - uint32 nextTime = _system->getMillis() + numTicks * _tickLength; while (_system->getMillis() < nextTime || numTicks == -1) { //if (!_animationSceneScriptRunFlag) @@ -2549,7 +2556,13 @@ void ToonEngine::renderInventory() { if (!_gameState->_inInventory) return; - _inventoryPicture->draw(*_mainSurface, 0, 0, 0, 0); + if (!_dirtyAll) { + _inventoryPicture->drawWithRectList(*_mainSurface, 0, 0, 0, 0, _dirtyRects); + } else { + _inventoryPicture->draw(*_mainSurface, 0, 0, 0, 0); + _dirtyRects.push_back(Common::Rect(0, 0, TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT)); + } + clearDirtyRects(); // draw items on screen for (int32 i = 0; i < _gameState->_numInventoryItems; i++) { @@ -2577,13 +2590,13 @@ void ToonEngine::renderInventory() { int32 ToonEngine::showInventory() { int32 oldScrollValue = _gameState->_currentScrollValue; -// Strangerke - Commented (not used) -// Common::EventManager *_event = _system->getEventManager(); + delete _inventoryPicture; _inventoryPicture = new Picture(this); fadeOut(5); _inventoryPicture->loadPicture("SACK128.CPS", true); _inventoryPicture->setupPalette(); + dirtyAllScreen(); if (_gameState->_mouseState >= 0) { setCursor(_gameState->_mouseState, true, -18, -14); @@ -2632,7 +2645,6 @@ int32 ToonEngine::showInventory() { int32 modItem = getSpecialInventoryItem(item); if (modItem) { - if (modItem == -1) { _gameState->_mouseState = item; _gameState->_inventory[foundObj] = 0; @@ -2681,7 +2693,7 @@ int32 ToonEngine::showInventory() { } _gameState->_currentScrollValue = oldScrollValue; - _gameState->_inInventory = false; + _gameState->_inInventory = false; _mouseButton = 0; _lastMouseButton = 0x3; @@ -2697,6 +2709,7 @@ int32 ToonEngine::showInventory() { setupGeneralPalette(); } flushPalette(); + dirtyAllScreen(); _firstFrame = true; return 0; @@ -2771,6 +2784,7 @@ void ToonEngine::showCutaway(Common::String cutawayPicture) { _currentCutaway->setupPalette(); _oldScrollValue = _gameState->_currentScrollValue; _gameState->_currentScrollValue = 0; + dirtyAllScreen(); flushPalette(); } @@ -2781,6 +2795,7 @@ void ToonEngine::hideCutaway() { _gameState->_currentScrollValue = _oldScrollValue; _currentCutaway = 0; _currentPicture->setupPalette(); + dirtyAllScreen(); flushPalette(); } @@ -2805,7 +2820,7 @@ void ToonEngine::rearrangeInventory() { if (_gameState->_inventory[i] == 0) { // move all the following items from one for (int32 j = i + 1; j < _gameState->_numInventoryItems; j++) { - _gameState->_inventory[j-1] = _gameState->_inventory[j]; + _gameState->_inventory[j - 1] = _gameState->_inventory[j]; } _gameState->_numInventoryItems--; } @@ -2826,7 +2841,6 @@ void ToonEngine::newGame() { } } - void ToonEngine::playSFX(int32 id, int32 volume) { if (id < 0) _audioManager->playSFX(-id + 1, volume, true); @@ -2852,7 +2866,7 @@ void ToonEngine::getTextPosition(int32 characterId, int32 *retX, int32 *retY) { // drew int32 x = _drew->getX(); int32 y = _drew->getY(); - if (x >= _gameState->_currentScrollValue && x <= _gameState->_currentScrollValue + 640) { + if (x >= _gameState->_currentScrollValue && x <= _gameState->_currentScrollValue + TOON_SCREEN_WIDTH) { if (!_gameState->_inCutaway && !_gameState->_inInventory) { *retX = x; *retY = y - ((_drew->getScale() * 256 / 1024) >> 1) - 45; @@ -2862,7 +2876,7 @@ void ToonEngine::getTextPosition(int32 characterId, int32 *retX, int32 *retY) { // flux int32 x = _flux->getX(); int32 y = _flux->getY(); - if (x >= _gameState->_currentScrollValue && x <= _gameState->_currentScrollValue + 640) { + if (x >= _gameState->_currentScrollValue && x <= _gameState->_currentScrollValue + TOON_SCREEN_WIDTH) { if (!_gameState->_inCutaway) { *retX = x; *retY = y - ((_drew->getScale() * 100 / 1024) >> 1) - 30; @@ -2892,7 +2906,7 @@ void ToonEngine::getTextPosition(int32 characterId, int32 *retX, int32 *retY) { Character *character = getCharacterById(characterId); if (character && !_gameState->_inCutaway) { if (character->getAnimationInstance()) { - if (character->getX() >= _gameState->_currentScrollValue && character->getX() <= _gameState->_currentScrollValue + 640) { + if (character->getX() >= _gameState->_currentScrollValue && character->getX() <= _gameState->_currentScrollValue + TOON_SCREEN_WIDTH) { int32 x1, y1, x2, y2; character->getAnimationInstance()->getRect(&x1, &y1, &x2, &y2); *retX = (x1 + x2) / 2; @@ -2921,7 +2935,7 @@ void ToonEngine::drawConversationLine() { } void ToonEngine::pauseEngineIntern(bool pause) { - + Engine::pauseEngineIntern(pause); static int32 pauseStart = 0; @@ -3013,7 +3027,6 @@ bool ToonEngine::saveGame(int32 slot, Common::String saveGameDesc) { saveFile->writeUint32BE(saveDate); saveFile->writeUint16BE(saveTime); - // save global state _gameState->save(saveFile); _gameState->saveConversations(saveFile); @@ -3040,7 +3053,6 @@ bool ToonEngine::saveGame(int32 slot, Common::String saveGameDesc) { _sceneAnimations[i].save(this, saveFile); } - for (int32 i = 0; i < 8; i++) { if (_characters[i]) { saveFile->writeSByte(i); @@ -3120,7 +3132,7 @@ bool ToonEngine::loadGame(int32 slot) { _sceneAnimationScripts[i]._frozen = loadFile->readByte(); _sceneAnimationScripts[i]._frozenForConversation = false; int32 oldTimer = loadFile->readSint32BE(); - _sceneAnimationScripts[i]._lastTimer = MAX<int32>(0,oldTimer + timerDiff); + _sceneAnimationScripts[i]._lastTimer = MAX<int32>(0, oldTimer + timerDiff); _script->loadState(&_sceneAnimationScripts[i]._state, loadFile); } @@ -3159,7 +3171,7 @@ bool ToonEngine::loadGame(int32 slot) { // load "command buffer" int32 size = loadFile->readSint16BE(); if (size) { - uint8 *buf = new uint8[size+2]; + uint8 *buf = new uint8[size + 2]; loadFile->read(buf, size + 2); Common::MemoryReadStream rStr(buf, size + 2); @@ -3278,10 +3290,6 @@ void ToonEngine::initCharacter(int32 characterId, int32 animScriptId, int32 scen return; } -// Strangerke - Commented (not used) -// if (_characters[characterIndex]) -// delete current char - _characters[characterIndex] = new Character(this); _characters[characterIndex]->setId(characterId); _characters[characterIndex]->setAnimScript(animScriptId); @@ -3405,6 +3413,7 @@ void ToonEngine::viewInventoryItem(Common::String str, int32 lineId, int32 itemD Picture *pic = new Picture(this); pic->loadPicture(str, false); pic->setupPalette(); + dirtyAllScreen(); flushPalette(); if (lineId) { @@ -3428,7 +3437,13 @@ void ToonEngine::viewInventoryItem(Common::String str, int32 lineId, int32 itemD break; } - pic->draw(*_mainSurface, 0, 0, 0, 0); + if (!_dirtyAll) { + pic->drawWithRectList(*_mainSurface, 0, 0, 0, 0, _dirtyRects); + } else { + pic->draw(*_mainSurface, 0, 0, 0, 0); + _dirtyRects.push_back(Common::Rect(0, 0, TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT)); + } + clearDirtyRects(); drawConversationLine(); if (!_audioManager->voiceStillPlaying()) { @@ -3447,11 +3462,11 @@ void ToonEngine::viewInventoryItem(Common::String str, int32 lineId, int32 itemD } fadeOut(5); + dirtyAllScreen(); restorePalette(); _firstFrame = true; _gameState->_currentScrollValue = oldScrollValue; delete pic; - } int32 ToonEngine::handleInventoryOnInventory(int32 itemDest, int32 itemSrc) { @@ -3493,7 +3508,7 @@ int32 ToonEngine::handleInventoryOnInventory(int32 itemDest, int32 itemSrc) { case 11: if (itemSrc == 0xb) { _gameState->_mouseState = -1; - replaceItemFromInventory(11,12); + replaceItemFromInventory(11, 12); setCursor(0, false, 0, 0); rearrangeInventory(); return 1; @@ -4506,9 +4521,9 @@ void ToonEngine::createShadowLUT() { for (int32 i = 0; i < 255; i++) { // goal color - uint32 destR = _finalPalette[i*3+0] * scaleNum / scaleDenom; - uint32 destG = _finalPalette[i*3+1] * scaleNum / scaleDenom; - uint32 destB = _finalPalette[i*3+2] * scaleNum / scaleDenom; + uint32 destR = _finalPalette[i * 3 + 0] * scaleNum / scaleDenom; + uint32 destG = _finalPalette[i * 3 + 1] * scaleNum / scaleDenom; + uint32 destB = _finalPalette[i * 3 + 2] * scaleNum / scaleDenom; // search only in the "picture palette" which is in colors 1-128 and 200-255 int32 colorDist = 0xffffff; @@ -4516,9 +4531,9 @@ void ToonEngine::createShadowLUT() { for (int32 c = 1; c < 129; c++) { - int32 diffR = _finalPalette[c*3+0] - destR; - int32 diffG = _finalPalette[c*3+1] - destG; - int32 diffB = _finalPalette[c*3+2] - destB; + int32 diffR = _finalPalette[c * 3 + 0] - destR; + int32 diffG = _finalPalette[c * 3 + 1] - destG; + int32 diffB = _finalPalette[c * 3 + 2] - destB; if (colorDist > diffR * diffR + diffG * diffG + diffB * diffB) { colorDist = diffR * diffR + diffG * diffG + diffB * diffB; @@ -4528,9 +4543,9 @@ void ToonEngine::createShadowLUT() { for (int32 c = 200; c < 256; c++) { - int32 diffR = _finalPalette[c*3+0] - destR; - int32 diffG = _finalPalette[c*3+1] - destG; - int32 diffB = _finalPalette[c*3+2] - destB; + int32 diffR = _finalPalette[c * 3 + 0] - destR; + int32 diffG = _finalPalette[c * 3 + 1] - destG; + int32 diffB = _finalPalette[c * 3 + 2] - destB; if (colorDist > diffR * diffR + diffG * diffG + diffB * diffB) { colorDist = diffR * diffR + diffG * diffG + diffB * diffB; @@ -4660,6 +4675,47 @@ void ToonEngine::playRoomMusic() { _audioManager->playMusic(_gameState->_locations[_gameState->_currentScene]._name, _gameState->_locations[_gameState->_currentScene]._music); } +void ToonEngine::dirtyAllScreen() +{ + _dirtyRects.clear(); + _dirtyAll = true; +} + +void ToonEngine::addDirtyRect( int32 left, int32 top, int32 right, int32 bottom ) { + left = MIN<int32>(MAX<int32>(left, 0), TOON_BACKBUFFER_WIDTH); + right = MIN<int32>(MAX<int32>(right, 0), TOON_BACKBUFFER_WIDTH); + top = MIN<int32>(MAX<int32>(top, 0), TOON_BACKBUFFER_HEIGHT); + bottom = MIN<int32>(MAX<int32>(bottom, 0), TOON_BACKBUFFER_HEIGHT); + + Common::Rect rect(left, top, right, bottom); + + if (bottom - top <= 0 || right - left <= 0) + return; + + for (uint32 i = 0; i < _dirtyRects.size(); i++) { + if (_dirtyRects[i].contains(rect)) + return; + if (rect.contains(_dirtyRects[i])) { + _dirtyRects.remove_at(i); + i--; + } + } + + // check also in the old rect (of the old frame) + for (int32 i = _oldDirtyRects.size() - 1 ; i >= 0; i--) { + if (rect.contains(_oldDirtyRects[i])) { + _oldDirtyRects.remove_at(i); + } + } + + _dirtyRects.push_back(rect); +} + +void ToonEngine::clearDirtyRects() { + _oldDirtyRects = _dirtyRects; + _dirtyRects.clear(); + _dirtyAll = false; +} void SceneAnimation::save(ToonEngine *vm, Common::WriteStream *stream) { stream->writeByte(_active); stream->writeSint32BE(_id); @@ -4686,7 +4742,6 @@ void SceneAnimation::load(ToonEngine *vm, Common::ReadStream *stream) { _active = stream->readByte(); _id = stream->readSint32BE(); - if (!_active) return; diff --git a/engines/toon/toon.h b/engines/toon/toon.h index f3370aed5e..3554900684 100644 --- a/engines/toon/toon.h +++ b/engines/toon/toon.h @@ -52,6 +52,11 @@ class MemoryWriteStreamDynamic; #define TOON_SAVEGAME_VERSION 4 #define DATAALIGNMENT 4 +#define TOON_SCREEN_WIDTH 640 +#define TOON_SCREEN_HEIGHT 400 +#define TOON_BACKBUFFER_WIDTH 1280 +#define TOON_BACKBUFFER_HEIGHT 400 + /** * This is the namespace of the Toon engine. * @@ -206,8 +211,6 @@ public: void waitForScriptStep(); void doMagnifierEffect(); - - bool canSaveGameStateCurrently(); bool canLoadGameStateCurrently(); void pauseEngineIntern(bool pause); @@ -334,6 +337,10 @@ public: (f == kSupportsSavingDuringRuntime); } + void dirtyAllScreen(); + void addDirtyRect(int32 left, int32 top, int32 right, int32 bottom); + void clearDirtyRects(); + protected: OSystem *_system; int32 _tickLength; @@ -371,6 +378,11 @@ protected: bool _updatingSceneScriptRunFlag; Graphics::Surface *_mainSurface; + Common::Array<Common::Rect> _dirtyRects; + Common::Array<Common::Rect> _oldDirtyRects; + + bool _dirtyAll; + AnimationInstance *_cursorAnimationInstance; Animation *_cursorAnimation; diff --git a/graphics/fonts/winfont.cpp b/graphics/fonts/winfont.cpp index fb37c8ddef..12509fd9e1 100644 --- a/graphics/fonts/winfont.cpp +++ b/graphics/fonts/winfont.cpp @@ -23,8 +23,9 @@ */ #include "common/file.h" -#include "common/ne_exe.h" #include "common/str.h" +#include "common/winexe_ne.h" +#include "common/winexe_pe.h" #include "graphics/fonts/winfont.h" namespace Graphics { @@ -75,8 +76,15 @@ static WinFontDirEntry readDirEntry(Common::SeekableReadStream &stream) { } bool WinFont::loadFromFON(const Common::String &fileName, const WinFontDirEntry &dirEntry) { - // TODO: PE libraries (If it's used anywhere by a ScummVM game) + // First try loading via the NE code + if (loadFromNE(fileName, dirEntry)) + return true; + // Then try loading via the PE code + return loadFromPE(fileName, dirEntry); +} + +bool WinFont::loadFromNE(const Common::String &fileName, const WinFontDirEntry &dirEntry) { Common::NEResources exe; if (!exe.loadFromEXE(fileName)) @@ -89,44 +97,53 @@ bool WinFont::loadFromFON(const Common::String &fileName, const WinFontDirEntry return false; } - uint16 numFonts = fontDirectory->readUint16LE(); + uint32 fontId = getFontIndex(*fontDirectory, dirEntry); + + delete fontDirectory; - // Probably not possible, so this is really a sanity check - if (numFonts == 0) { - warning("No fonts in '%s'", fileName.c_str()); + // Couldn't match the face name + if (fontId == 0xffffffff) { + warning("Could not find face '%s' in '%s'", dirEntry.faceName.c_str(), fileName.c_str()); return false; } - // Scour the directory for our matching name - int fontId = -1; - for (uint16 i = 0; i < numFonts; i++) { - uint16 id = fontDirectory->readUint16LE(); + // Actually go get our font now... + Common::SeekableReadStream *fontStream = exe.getResource(Common::kNEFont, fontId); + if (!fontStream) { + warning("Could not find font %d in %s", fontId, fileName.c_str()); + return false; + } - if (dirEntry.faceName.empty()) { - // Use the first name when empty - fontId = id; - break; - } + bool ok = loadFromFNT(*fontStream); + delete fontStream; + return ok; +} - WinFontDirEntry entry = readDirEntry(*fontDirectory); +bool WinFont::loadFromPE(const Common::String &fileName, const WinFontDirEntry &dirEntry) { + Common::PEResources exe; - if (dirEntry.faceName.equalsIgnoreCase(entry.faceName) && dirEntry.points == entry.points) { - // Match! - fontId = id; - break; - } + if (!exe.loadFromEXE(fileName)) + return false; + + // Let's pull out the font directory + Common::SeekableReadStream *fontDirectory = exe.getResource(Common::kPEFontDir, Common::String("FONTDIR")); + if (!fontDirectory) { + warning("No font directory in '%s'", fileName.c_str()); + return false; } + uint32 fontId = getFontIndex(*fontDirectory, dirEntry); + delete fontDirectory; // Couldn't match the face name - if (fontId < 0) { + if (fontId == 0xffffffff) { warning("Could not find face '%s' in '%s'", dirEntry.faceName.c_str(), fileName.c_str()); return false; } // Actually go get our font now... - Common::SeekableReadStream *fontStream = exe.getResource(Common::kNEFont, fontId); + Common::SeekableReadStream *fontStream = exe.getResource(Common::kPEFont, fontId); if (!fontStream) { warning("Could not find font %d in %s", fontId, fileName.c_str()); return false; @@ -137,6 +154,32 @@ bool WinFont::loadFromFON(const Common::String &fileName, const WinFontDirEntry return ok; } +uint32 WinFont::getFontIndex(Common::SeekableReadStream &stream, const WinFontDirEntry &dirEntry) { + uint16 numFonts = stream.readUint16LE(); + + // Probably not possible, so this is really a sanity check + if (numFonts == 0) { + warning("No fonts in exe"); + return 0xffffffff; + } + + // Scour the directory for our matching name + for (uint16 i = 0; i < numFonts; i++) { + uint16 id = stream.readUint16LE(); + + // Use the first name when empty + if (dirEntry.faceName.empty()) + return id; + + WinFontDirEntry entry = readDirEntry(stream); + + if (dirEntry.faceName.equalsIgnoreCase(entry.faceName) && dirEntry.points == entry.points) // Match! + return id; + } + + return 0xffffffff; +} + bool WinFont::loadFromFNT(const Common::String &fileName) { Common::File file; diff --git a/graphics/fonts/winfont.h b/graphics/fonts/winfont.h index b23455e2d5..fbe4a778e2 100644 --- a/graphics/fonts/winfont.h +++ b/graphics/fonts/winfont.h @@ -69,6 +69,10 @@ public: void drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const; private: + bool loadFromPE(const Common::String &fileName, const WinFontDirEntry &dirEntry); + bool loadFromNE(const Common::String &fileName, const WinFontDirEntry &dirEntry); + + uint32 getFontIndex(Common::SeekableReadStream &stream, const WinFontDirEntry &dirEntry); bool loadFromFNT(Common::SeekableReadStream &stream); char indexToCharacter(uint16 index) const; uint16 characterToIndex(byte character) const; diff --git a/graphics/module.mk b/graphics/module.mk index c962f0617d..cb3a07e691 100644 --- a/graphics/module.mk +++ b/graphics/module.mk @@ -23,7 +23,8 @@ MODULE_OBJS := \ surface.o \ thumbnail.o \ VectorRenderer.o \ - VectorRendererSpec.o + VectorRendererSpec.o \ + wincursor.o ifdef USE_SCALERS MODULE_OBJS += \ diff --git a/graphics/wincursor.cpp b/graphics/wincursor.cpp new file mode 100644 index 0000000000..d3c9414e03 --- /dev/null +++ b/graphics/wincursor.cpp @@ -0,0 +1,317 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/debug.h" +#include "common/file.h" +#include "common/memstream.h" +#include "common/ptr.h" +#include "common/str.h" +#include "common/stream.h" +#include "common/winexe_ne.h" +#include "common/winexe_pe.h" + +#include "graphics/wincursor.h" + +namespace Graphics { + +WinCursor::WinCursor() { + _width = 0; + _height = 0; + _hotspotX = 0; + _hotspotY = 0; + _surface = 0; + _keyColor = 0; + memset(_palette, 0, 256 * 3); +} + +WinCursor::~WinCursor() { + clear(); +} + +uint16 WinCursor::getWidth() const { + return _width; +} + +uint16 WinCursor::getHeight() const { + return _height; +} + +uint16 WinCursor::getHotspotX() const { + return _hotspotX; +} + +uint16 WinCursor::getHotspotY() const { + return _hotspotY; +} + +byte WinCursor::getKeyColor() const { + return _keyColor; +} + +bool WinCursor::readFromStream(Common::SeekableReadStream &stream) { + clear(); + + _hotspotX = stream.readUint16LE(); + _hotspotY = stream.readUint16LE(); + + // Check header size + if (stream.readUint32LE() != 40) + return false; + + // Check dimensions + _width = stream.readUint32LE(); + _height = stream.readUint32LE() / 2; + + if (_width & 3) { + // Cursors should always be a power of 2 + // Of course, it wouldn't be hard to handle but if we have no examples... + warning("Non-divisible-by-4 width cursor found"); + return false; + } + + // Color planes + if (stream.readUint16LE() != 1) + return false; + + // Only 1bpp and 8bpp supported + uint16 bitsPerPixel = stream.readUint16LE(); + if (bitsPerPixel != 1 && bitsPerPixel != 8) + return false; + + // Compression + if (stream.readUint32LE() != 0) + return false; + + // Image size + X resolution + Y resolution + stream.skip(12); + + uint32 numColors = stream.readUint32LE(); + + // If the color count is 0, then it uses up the maximum amount + if (numColors == 0) + numColors = 1 << bitsPerPixel; + + // Reading the palette + stream.seek(40 + 4); + for (uint32 i = 0 ; i < numColors; i++) { + _palette[i * 3 + 2] = stream.readByte(); + _palette[i * 3 + 1] = stream.readByte(); + _palette[i * 3 ] = stream.readByte(); + stream.readByte(); + } + + // Reading the bitmap data + uint32 dataSize = stream.size() - stream.pos(); + byte *initialSource = new byte[dataSize]; + stream.read(initialSource, dataSize); + + // Parse the XOR map + const byte *src = initialSource; + _surface = new byte[_width * _height]; + byte *dest = _surface + _width * (_height - 1); + uint32 imagePitch = _width * bitsPerPixel / 8; + + for (uint32 i = 0; i < _height; i++) { + byte *rowDest = dest; + + if (bitsPerPixel == 1) { + // 1bpp + for (uint32 j = 0; j < (_width / 8); j++) { + byte p = src[j]; + + for (int k = 0; k < 8; k++, rowDest++, p <<= 1) { + if ((p & 0x80) == 0x80) + *rowDest = 1; + else + *rowDest = 0; + } + } + } else { + // 8bpp + memcpy(rowDest, src, _width); + } + + dest -= _width; + src += imagePitch; + } + + // Calculate our key color + if (numColors < 256) { + // If we're not using the maximum colors in a byte, we can fit it in + _keyColor = numColors; + } else { + // HACK: Try to find a color that's not being used so it can become + // our keycolor. It's quite impossible to fit 257 entries into 256... + for (uint32 i = 0; i < 256; i++) { + for (int j = 0; j < _width * _height; j++) { + // TODO: Also check to see if the space is transparent + + if (_surface[j] == i) + break; + + if (j == _width * _height - 1) { + _keyColor = i; + i = 256; + break; + } + } + } + } + + // Now go through and apply the AND map to get the transparency + uint32 andWidth = (_width + 7) / 8; + src += andWidth * (_height - 1); + + for (uint32 y = 0; y < _height; y++) { + for (uint32 x = 0; x < _width; x++) + if (src[x / 8] & (1 << (7 - x % 8))) + _surface[y * _width + x] = _keyColor; + + src -= andWidth; + } + + delete[] initialSource; + return true; +} + +void WinCursor::clear() { + delete[] _surface; _surface = 0; +} + +WinCursorGroup::WinCursorGroup() { +} + +WinCursorGroup::~WinCursorGroup() { + for (uint32 i = 0; i < cursors.size(); i++) + delete cursors[i].cursor; +} + +WinCursorGroup *WinCursorGroup::createCursorGroup(Common::NEResources &exe, const Common::WinResourceID &id) { + Common::ScopedPtr<Common::SeekableReadStream> stream(exe.getResource(Common::kNEGroupCursor, id)); + + if (!stream || stream->size() <= 6) + return 0; + + stream->skip(4); + uint32 cursorCount = stream->readUint16LE(); + if ((uint32)stream->size() < (6 + cursorCount * 16)) + return 0; + + WinCursorGroup *group = new WinCursorGroup(); + group->cursors.reserve(cursorCount); + + for (uint32 i = 0; i < cursorCount; i++) { + stream->readUint16LE(); // width + stream->readUint16LE(); // height + + // Plane count + if (stream->readUint16LE() != 1) { + delete group; + return 0; + } + + // Bits per pixel + // NE cursors can only be 1bpp + if (stream->readUint16LE() != 1) { + delete group; + return 0; + } + + stream->readUint32LE(); // data size + uint32 cursorId = stream->readUint32LE(); + + Common::ScopedPtr<Common::SeekableReadStream> cursorStream(exe.getResource(Common::kNECursor, cursorId)); + if (!cursorStream) { + delete group; + return 0; + } + + WinCursor *cursor = new WinCursor(); + if (!cursor->readFromStream(*cursorStream)) { + delete cursor; + delete group; + return 0; + } + + CursorItem item; + item.id = cursorId; + item.cursor = cursor; + group->cursors.push_back(item); + } + + return group; +} + +WinCursorGroup *WinCursorGroup::createCursorGroup(Common::PEResources &exe, const Common::WinResourceID &id) { + Common::ScopedPtr<Common::SeekableReadStream> stream(exe.getResource(Common::kPEGroupCursor, id)); + + if (!stream || stream->size() <= 6) + return 0; + + stream->skip(4); + uint32 cursorCount = stream->readUint16LE(); + if ((uint32)stream->size() < (6 + cursorCount * 14)) + return 0; + + WinCursorGroup *group = new WinCursorGroup(); + group->cursors.reserve(cursorCount); + + for (uint32 i = 0; i < cursorCount; i++) { + stream->readUint16LE(); // width + stream->readUint16LE(); // height + + // Plane count + if (stream->readUint16LE() != 1) { + delete group; + return 0; + } + + stream->readUint16LE(); // bits per pixel + stream->readUint32LE(); // data size + uint32 cursorId = stream->readUint16LE(); + + Common::ScopedPtr<Common::SeekableReadStream> cursorStream(exe.getResource(Common::kPECursor, cursorId)); + if (!cursorStream) { + delete group; + return 0; + } + + WinCursor *cursor = new WinCursor(); + if (!cursor->readFromStream(*cursorStream)) { + delete cursor; + delete group; + return 0; + } + + CursorItem item; + item.id = cursorId; + item.cursor = cursor; + group->cursors.push_back(item); + } + + return group; +} + +} // End of namespace Graphics diff --git a/graphics/wincursor.h b/graphics/wincursor.h new file mode 100644 index 0000000000..ca0abf6fe1 --- /dev/null +++ b/graphics/wincursor.h @@ -0,0 +1,105 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef GRAPHICS_WINCURSOR_H +#define GRAPHICS_WINCURSOR_H + +#include "common/array.h" +#include "common/winexe.h" + +namespace Common { + class NEResources; + class PEResources; + class SeekableReadStream; +} + +namespace Graphics { + +/** A Windows cursor. */ +class WinCursor { +public: + WinCursor(); + ~WinCursor(); + + /** Return the cursor's width. */ + uint16 getWidth() const; + /** Return the cursor's height. */ + uint16 getHeight() const; + /** Return the cursor's hotspot's x coordinate. */ + uint16 getHotspotX() const; + /** Return the cursor's hotspot's y coordinate. */ + uint16 getHotspotY() const; + /** Return the cursor's transparent key. */ + byte getKeyColor() const; + + const byte *getSurface() const { return _surface; } + const byte *getPalette() const { return _palette; } + + /** Read the cursor's data out of a stream. */ + bool readFromStream(Common::SeekableReadStream &stream); + +private: + byte *_surface; + byte _palette[256 * 3]; + + uint16 _width; ///< The cursor's width. + uint16 _height; ///< The cursor's height. + uint16 _hotspotX; ///< The cursor's hotspot's x coordinate. + uint16 _hotspotY; ///< The cursor's hotspot's y coordinate. + byte _keyColor; ///< The cursor's transparent key + + /** Clear the cursor. */ + void clear(); +}; + +/** + * A structure holding an array of cursors from a single Windows Executable cursor group. + * + * Windows lumps different versions of the same cursors/icons together and decides which one + * to use based on the screen's color depth and resolution. For instance, one cursor group + * could hold a 1bpp 16x16 cursorand a 8bpp 16x16 cursor. This will hold all cursors in the + * group. This class should be used to actually parse the cursors, whereas WinCursor is just + * the representation used by this struct to store the cursors. + */ +struct WinCursorGroup { + WinCursorGroup(); + ~WinCursorGroup(); + + struct CursorItem { + Common::WinResourceID id; + WinCursor *cursor; + }; + + Common::Array<CursorItem> cursors; + + /** Create a cursor group from an NE EXE, returns 0 on failure */ + static WinCursorGroup *createCursorGroup(Common::NEResources &exe, const Common::WinResourceID &id); + /** Create a cursor group from an PE EXE, returns 0 on failure */ + static WinCursorGroup *createCursorGroup(Common::PEResources &exe, const Common::WinResourceID &id); +}; + +} // End of namespace Graphics + +#endif diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp index ee83ca620c..026370abf1 100644 --- a/gui/ThemeEngine.cpp +++ b/gui/ThemeEngine.cpp @@ -1193,7 +1193,7 @@ void ThemeEngine::debugWidgetPosition(const char *name, const Common::Rect &r) { /********************************************************** * Screen/overlay management *********************************************************/ -void ThemeEngine::updateScreen() { +void ThemeEngine::updateScreen(bool render) { if (!_bufferQueue.empty()) { _vectorRenderer->setSurface(&_backBuffer); @@ -1218,7 +1218,8 @@ void ThemeEngine::updateScreen() { _screenQueue.clear(); } - renderDirtyScreen(); + if (render) + renderDirtyScreen(); } void ThemeEngine::addDirtyRect(Common::Rect r) { diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h index e852760e44..78ea06f3b6 100644 --- a/gui/ThemeEngine.h +++ b/gui/ThemeEngine.h @@ -32,7 +32,7 @@ #include "graphics/surface.h" #include "graphics/font.h" -#define SCUMMVM_THEME_VERSION_STR "SCUMMVM_STX0.8.2" +#define SCUMMVM_THEME_VERSION_STR "SCUMMVM_STX0.8.3" namespace Graphics { struct DrawStep; @@ -280,7 +280,7 @@ public: * It processes all the drawing queues and then copies dirty rects * in the current Screen surface to the overlay. */ - void updateScreen(); + void updateScreen(bool render = true); /** @name FONT MANAGEMENT METHODS */ diff --git a/gui/ThemeParser.cpp b/gui/ThemeParser.cpp index 70d81a962e..e3523d11e8 100644 --- a/gui/ThemeParser.cpp +++ b/gui/ThemeParser.cpp @@ -861,30 +861,50 @@ bool ThemeParser::resolutionCheck(const Common::String &resolution) { return true; Common::StringTokenizer globTokenizer(resolution, ", "); - Common::String cur, w, h; - bool definedRes = false; + Common::String cur; while (!globTokenizer.empty()) { - bool ignore = false; cur = globTokenizer.nextToken(); - if (cur[0] == '-') { - ignore = true; - cur.deleteChar(0); + bool lt; + int val; + + if (cur.size() < 5) { + warning("Invalid theme 'resolution' token '%s'", resolution.c_str()); + return false; + } + + if (cur[0] == 'x') { + val = g_system->getOverlayWidth(); + } else if (cur[0] == 'y') { + val = g_system->getOverlayHeight(); + } else { + warning("Error parsing theme 'resolution' token '%s'", resolution.c_str()); + return false; + } + + if (cur[1] == '<') { + lt = true; + } else if (cur[1] == '>') { + lt = false; } else { - definedRes = true; + warning("Error parsing theme 'resolution' token '%s'", resolution.c_str()); + return false; } - Common::StringTokenizer resTokenizer(cur, "x"); - w = resTokenizer.nextToken(); - h = resTokenizer.nextToken(); + int token = atoi(cur.c_str() + 2); - if ((w == "X" || atoi(w.c_str()) == g_system->getOverlayWidth()) && - (h == "Y" || atoi(h.c_str()) == g_system->getOverlayHeight())) - return !ignore; + // check inverse for unfulfilled requirements + if (lt) { + if (val >= token) + return false; + } else { + if (val <= token) + return false; + } } - return !definedRes; + return true; } } // End of namespace GUI diff --git a/gui/credits.h b/gui/credits.h index 926adf4c9c..93ce99acb6 100644 --- a/gui/credits.h +++ b/gui/credits.h @@ -210,6 +210,7 @@ static const char *credits[] = { "", "C1""Backend Teams", "C1""Android", +"C0""Andre Heider", "C0""Angus Lees", "", "C1""Dreamcast", @@ -294,6 +295,23 @@ static const char *credits[] = { "C0""Fredrik Wendel", "C2""(retired)", "", +"C1""Website (maintenance)", +"C0""James Brown", +"C2""IRC Logs maintainer", +"C0""Thierry Crozat", +"C2""Wiki maintainer", +"C0""Andre Heider", +"C2""Buildbot maintainer", +"C0""Max Horn", +"C2""Forum, IRC channel and Mailing list maintainer", +"C0""Joost Peters", +"C2""Doxygen Project Documentation maintainer", +"C0""Jordi Vilalta Prat", +"C2""Wiki maintainer", +"C0""Eugene Sandulenko", +"C2""Forum, IRC channel, Screen Shots and Mailing list maintainer", +"C0""John Willis", +"", "C1""Website (content)", "C0""All active team members", "C0""", @@ -445,6 +463,8 @@ static const char *credits[] = { "C2""ScummVM logo", "C0""Raina", "C2""ScummVM forum buttons", +"C0""William Claydon", +"C2""Skins for doxygen and wiki", "", "C1""Code contributions", "C0""Ori Avtalion", @@ -456,7 +476,7 @@ static const char *credits[] = { "C0""Martin Doucha", "C2""CinE engine objectification", "C0""Thomas Fach-Pedersen", -"C2""ProTracker module player", +"C2""ProTracker module player, Smacker video decoder", "C0""Tobias Gunkel", "C2""Sound support for C64 version of MM/Zak, Loom PCE support", "C0""Janne Huttunen", diff --git a/gui/gui-manager.cpp b/gui/gui-manager.cpp index 7644cbe7b2..3ad4b2ee18 100644 --- a/gui/gui-manager.cpp +++ b/gui/gui-manager.cpp @@ -201,14 +201,15 @@ void GuiManager::redraw() { _theme->clearAll(); _theme->openDialog(true, ThemeEngine::kShadingNone); - for (i = 0; i < _dialogStack.size() - 1; i++) { + for (i = 0; i < _dialogStack.size() - 1; i++) _dialogStack[i]->drawDialog(); - } _theme->finishBuffering(); + // fall through + case kRedrawOpenDialog: - _theme->updateScreen(); + _theme->updateScreen(false); _theme->openDialog(true, shading); _dialogStack.top()->drawDialog(); _theme->finishBuffering(); diff --git a/gui/themes/default.inc b/gui/themes/default.inc index 3236071055..2716e6ca72 100644 --- a/gui/themes/default.inc +++ b/gui/themes/default.inc @@ -1,5 +1,5 @@ "<?xml version = '1.0'?>" -"<layout_info resolution='-320xY,-256x240,-Xx272,-544x332,-Xx350'> " +"<layout_info resolution='y>399'> " "<globals> " "<def var='Line.Height' value='16' /> " "<def var='Font.Height' value='16' /> " @@ -789,7 +789,7 @@ "</layout> " "</dialog> " "</layout_info> " -"<layout_info resolution='320xY,256x240,Xx272,544x332,Xx350'> " +"<layout_info resolution='y<400'> " "<globals> " "<def var='Line.Height' value='12' /> " "<def var='Font.Height' value='10' /> " @@ -1602,21 +1602,21 @@ "<font id='text_default' " "file='helvb12.bdf' " "/> " -"<font resolution='320xY,256x240' " +"<font resolution='y<400' " "id='text_default' " "file='clR6x12.bdf' " "/> " "<font id='text_button' " "file='helvb12.bdf' " "/> " -"<font resolution='320xY,256x240' " +"<font resolution='y<400' " "id='text_button' " "file='clR6x12.bdf' " "/> " "<font id='text_normal' " "file='helvb12.bdf' " "/> " -"<font resolution='320xY,256x240' " +"<font resolution='y<400' " "id='text_normal' " "file='clR6x12.bdf' " "/> " diff --git a/gui/themes/scummclassic.zip b/gui/themes/scummclassic.zip Binary files differindex 9fd2c187fd..4dbedd4f14 100644 --- a/gui/themes/scummclassic.zip +++ b/gui/themes/scummclassic.zip diff --git a/gui/themes/scummclassic/THEMERC b/gui/themes/scummclassic/THEMERC index f0276969fe..17e934d5ef 100644 --- a/gui/themes/scummclassic/THEMERC +++ b/gui/themes/scummclassic/THEMERC @@ -1 +1 @@ -[SCUMMVM_STX0.8.2:ScummVM Classic Theme:No Author] +[SCUMMVM_STX0.8.3:ScummVM Classic Theme:No Author] diff --git a/gui/themes/scummclassic/classic_gfx.stx b/gui/themes/scummclassic/classic_gfx.stx index d672db2540..3fd00abbb9 100644 --- a/gui/themes/scummclassic/classic_gfx.stx +++ b/gui/themes/scummclassic/classic_gfx.stx @@ -46,21 +46,21 @@ <font id = 'text_default' file = 'helvb12.bdf' /> - <font resolution = '320xY, 256x240' + <font resolution = 'y<400' id = 'text_default' file = 'clR6x12.bdf' /> <font id = 'text_button' file = 'helvb12.bdf' /> - <font resolution = '320xY, 256x240' + <font resolution = 'y<400' id = 'text_button' file = 'clR6x12.bdf' /> <font id = 'text_normal' file = 'helvb12.bdf' /> - <font resolution = '320xY, 256x240' + <font resolution = 'y<400' id = 'text_normal' file = 'clR6x12.bdf' /> diff --git a/gui/themes/scummclassic/classic_layout.stx b/gui/themes/scummclassic/classic_layout.stx index 416ffb30eb..f09c29e360 100644 --- a/gui/themes/scummclassic/classic_layout.stx +++ b/gui/themes/scummclassic/classic_layout.stx @@ -23,7 +23,7 @@ - $Id$ - --> -<layout_info resolution = '-320xY, -256x240, -Xx272, -544x332, -Xx350'> +<layout_info resolution = 'y>399'> <globals> <def var = 'Line.Height' value = '16' /> <def var = 'Font.Height' value = '16' /> diff --git a/gui/themes/scummclassic/classic_layout_lowres.stx b/gui/themes/scummclassic/classic_layout_lowres.stx index fe0eb66b8d..a440be7694 100644 --- a/gui/themes/scummclassic/classic_layout_lowres.stx +++ b/gui/themes/scummclassic/classic_layout_lowres.stx @@ -23,7 +23,7 @@ - $Id$ - --> -<layout_info resolution = "320xY, 256x240, Xx272, 544x332, Xx350"> +<layout_info resolution = 'y<400'> <globals> <def var = 'Line.Height' value = '12' /> <def var = 'Font.Height' value = '10' /> diff --git a/gui/themes/scummmodern.zip b/gui/themes/scummmodern.zip Binary files differindex f4e18ef00c..77951475e6 100644 --- a/gui/themes/scummmodern.zip +++ b/gui/themes/scummmodern.zip diff --git a/gui/themes/scummmodern/THEMERC b/gui/themes/scummmodern/THEMERC index b8f41fc207..f947a5685a 100644 --- a/gui/themes/scummmodern/THEMERC +++ b/gui/themes/scummmodern/THEMERC @@ -1 +1 @@ -[SCUMMVM_STX0.8.2:ScummVM Modern Theme:No Author] +[SCUMMVM_STX0.8.3:ScummVM Modern Theme:No Author] diff --git a/gui/themes/scummmodern/scummmodern_gfx.stx b/gui/themes/scummmodern/scummmodern_gfx.stx index cfe00a7016..a325d4982b 100644 --- a/gui/themes/scummmodern/scummmodern_gfx.stx +++ b/gui/themes/scummmodern/scummmodern_gfx.stx @@ -108,21 +108,21 @@ <font id = 'text_default' file = 'helvb12.bdf' /> - <font resolution = '320xY, 256x240' + <font resolution = 'y<400' id = 'text_default' file = 'clR6x12.bdf' /> <font id = 'text_button' file = 'helvb12.bdf' /> - <font resolution = '320xY, 256x240' + <font resolution = 'y<400' id = 'text_button' file = 'clR6x12.bdf' /> <font id = 'text_normal' file = 'helvb12.bdf' /> - <font resolution = '320xY, 256x240' + <font resolution = 'y<400' id = 'text_normal' file = 'clR6x12.bdf' /> @@ -178,7 +178,7 @@ <!-- <defaults fill = 'gradient' fg_color = 'white'/> --> <cursor file = 'cursor.bmp' hotspot = '0, 0' scale = '3'/> - <cursor resolution = '320xY, 256x240' file = 'cursor_small.bmp' hotspot = '0, 0' scale = '3'/> + <cursor resolution = 'y<400' file = 'cursor_small.bmp' hotspot = '0, 0' scale = '3'/> <!-- Selection (text or list items) --> <drawdata id = 'text_selection' cache = 'false'> diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx index 879be2aafe..32d6d19d1a 100644 --- a/gui/themes/scummmodern/scummmodern_layout.stx +++ b/gui/themes/scummmodern/scummmodern_layout.stx @@ -23,7 +23,7 @@ - $Id$ - --> -<layout_info resolution = '-320xY, -256x240, -Xx272, -544x332, -Xx350'> +<layout_info resolution = 'y>399'> <globals> <def var = 'Line.Height' value = '16' /> <def var = 'Font.Height' value = '16' /> diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx index 3a7a4b63b9..06916a80f1 100644 --- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx +++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx @@ -23,7 +23,7 @@ - $Id$ - --> -<layout_info resolution = "320xY, 256x240, Xx272, 544x332, Xx350"> +<layout_info resolution = 'y<400'> <globals> <def var = 'Line.Height' value = '12' /> <def var = 'Font.Height' value = '10' /> diff --git a/tools/credits.pl b/tools/credits.pl index b72d38bcd1..bd1060fb76 100755 --- a/tools/credits.pl +++ b/tools/credits.pl @@ -702,6 +702,7 @@ begin_credits("Credits"); begin_section("Backend Teams"); begin_section("Android"); + add_person("Andre Heider", "dhewg", ""); add_person("Angus Lees", "Gus", ""); end_section(); @@ -796,6 +797,19 @@ begin_credits("Credits"); add_person("Fredrik Wendel", "", "(retired)"); end_persons(); end_section(); + + begin_section("Website (maintenance)"); + begin_persons(); + add_person("James Brown", "Ender", "IRC Logs maintainer"); + add_person("Thierry Crozat", "criezy", "Wiki maintainer"); + add_person("Andre Heider", "dhewg", "Buildbot maintainer"); + add_person("Max Horn", "Fingolfin", "Forum, IRC channel and Mailing list maintainer"); + add_person("Joost Peters", "JoostP", "Doxygen Project Documentation maintainer"); + add_person("Jordi Vilalta Prat", "jvprat", "Wiki maintainer"); + add_person("Eugene Sandulenko", "sev", "Forum, IRC channel, Screen Shots and Mailing list maintainer"); + add_person("John Willis", "DJWillis", ""); + end_persons(); + end_section(); begin_section("Website (content)"); add_paragraph("All active team members"); @@ -949,6 +963,7 @@ begin_credits("Credits"); add_person("David Jensen", "Tyst", "SVG logo conversion"); add_person("Jean Marc Gimenez", "", "ScummVM logo"); add_person("", "Raina", "ScummVM forum buttons"); + add_person("William Claydon", "billwashere", "Skins for doxygen and wiki"); end_persons(); end_section(); @@ -958,7 +973,7 @@ begin_credits("Credits"); add_person("Stuart Caie", "", "Decoders for Amiga and AtariST data files (AGOS engine)"); add_person("Paolo Costabel", "", "PSP port contributions"); add_person("Martin Doucha", "next_ghost", "CinE engine objectification"); - add_person("Thomas Fach-Pedersen", "madmoose", "ProTracker module player"); + add_person("Thomas Fach-Pedersen", "madmoose", "ProTracker module player, Smacker video decoder"); add_person("Tobias Gunkel", "hennymcc", "Sound support for C64 version of MM/Zak, Loom PCE support"); add_person("Janne Huttunen", "", "V3 actor mask support, Dig/FT SMUSH audio"); add_person("Kovács Endre János", "", "Several fixes for Simon1"); |