diff options
119 files changed, 5508 insertions, 2000 deletions
diff --git a/audio/mixer.cpp b/audio/mixer.cpp index 8ff364b98d..ab3ed9eb2d 100644 --- a/audio/mixer.cpp +++ b/audio/mixer.cpp @@ -20,6 +20,8 @@ * */ +#include "gui/EventRecorder.h" + #include "common/util.h" #include "common/system.h" #include "common/textconsole.h" @@ -427,6 +429,7 @@ void MixerImpl::pauseHandle(SoundHandle handle, bool paused) { bool MixerImpl::isSoundIDActive(int id) { Common::StackLock lock(_mutex); + g_eventRec.updateSubsystems(); for (int i = 0; i != NUM_CHANNELS; i++) if (_channels[i] && _channels[i]->getId() == id) return true; @@ -443,6 +446,7 @@ int MixerImpl::getSoundID(SoundHandle handle) { bool MixerImpl::isSoundHandleActive(SoundHandle handle) { Common::StackLock lock(_mutex); + g_eventRec.updateSubsystems(); const int index = handle._val % NUM_CHANNELS; return _channels[index] && _channels[index]->getHandle()._val == handle._val; } @@ -556,12 +560,12 @@ void Channel::pause(bool paused) { _pauseLevel++; if (_pauseLevel == 1) - _pauseStartTime = g_system->getMillis(); + _pauseStartTime = g_system->getMillis(true); } else if (_pauseLevel > 0) { _pauseLevel--; if (!_pauseLevel) { - _pauseTime = (g_system->getMillis() - _pauseStartTime); + _pauseTime = (g_system->getMillis(true) - _pauseStartTime); _pauseStartTime = 0; } } @@ -579,7 +583,7 @@ Timestamp Channel::getElapsedTime() { if (isPaused()) delta = _pauseStartTime - _mixerTimeStamp; else - delta = g_system->getMillis() - _mixerTimeStamp - _pauseTime; + delta = g_system->getMillis(true) - _mixerTimeStamp - _pauseTime; // Convert the number of samples into a time duration. @@ -599,13 +603,12 @@ 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(); + _mixerTimeStamp = g_system->getMillis(true); _pauseTime = 0; res = _converter->flow(*_stream, data, len, _volL, _volR); _samplesDecoded += res; diff --git a/audio/softsynth/mt32.cpp b/audio/softsynth/mt32.cpp index 00d0469356..3281e3bebf 100644 --- a/audio/softsynth/mt32.cpp +++ b/audio/softsynth/mt32.cpp @@ -62,7 +62,8 @@ protected: // Callback for debug messages, in vprintf() format void printDebug(const char *fmt, va_list list) { - debug(4, fmt, list); + Common::String out = Common::String::vformat(fmt, list); + debug(4, out.c_str()); } // Callbacks for reporting various errors and information diff --git a/backends/events/default/default-events.cpp b/backends/events/default/default-events.cpp index 38a0c8d46f..bf76bbc1cb 100644 --- a/backends/events/default/default-events.cpp +++ b/backends/events/default/default-events.cpp @@ -84,7 +84,8 @@ void DefaultEventManager::init() { } bool DefaultEventManager::pollEvent(Common::Event &event) { - uint32 time = g_system->getMillis(); + // Skip recording of these events + uint32 time = g_system->getMillis(true); bool result = false; _dispatcher.dispatch(); diff --git a/backends/events/sdl/sdl-events.cpp b/backends/events/sdl/sdl-events.cpp index 0ca5bbb059..e2ef7f6bf6 100644 --- a/backends/events/sdl/sdl-events.cpp +++ b/backends/events/sdl/sdl-events.cpp @@ -106,7 +106,9 @@ void SdlEventSource::processMouseEvent(Common::Event &event, int x, int y) { } void SdlEventSource::handleKbdMouse() { - uint32 curTime = g_system->getMillis(); + // Skip recording of these events + uint32 curTime = g_system->getMillis(true); + if (curTime >= _km.last_time + _km.delay_time) { _km.last_time = curTime; if (_km.x_down_count == 1) { diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp index 02e58ab319..1ebd019488 100644 --- a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp +++ b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp @@ -40,6 +40,7 @@ #include "graphics/scaler.h" #include "graphics/scaler/aspect.h" #include "graphics/surface.h" +#include "gui/EventRecorder.h" static const OSystem::GraphicsMode s_supportedGraphicsModes[] = { {"1x", _s("Normal (no scaling)"), GFX_NORMAL}, @@ -135,6 +136,7 @@ SurfaceSdlGraphicsManager::SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSou _paletteDirtyStart(0), _paletteDirtyEnd(0), _screenIsLocked(false), _graphicsMutex(0), + _displayDisabled(false), #ifdef USE_SDL_DEBUG_FOCUSRECT _enableFocusRectDebugCode(false), _enableFocusRect(false), _focusRect(), #endif @@ -765,9 +767,20 @@ bool SurfaceSdlGraphicsManager::loadGFXMode() { fixupResolutionForAspectRatio(_videoMode.desiredAspectRatio, _videoMode.hardwareWidth, _videoMode.hardwareHeight); } - _hwscreen = SDL_SetVideoMode(_videoMode.hardwareWidth, _videoMode.hardwareHeight, 16, - _videoMode.fullscreen ? (SDL_FULLSCREEN|SDL_SWSURFACE) : SDL_SWSURFACE - ); + +#ifdef ENABLE_EVENTRECORDER + _displayDisabled = ConfMan.getBool("disable_display"); + + if (_displayDisabled) { + _hwscreen = g_eventRec.getSurface(_videoMode.hardwareWidth, _videoMode.hardwareHeight); + } else +#endif + { + _hwscreen = SDL_SetVideoMode(_videoMode.hardwareWidth, _videoMode.hardwareHeight, 16, + _videoMode.fullscreen ? (SDL_FULLSCREEN|SDL_SWSURFACE) : SDL_SWSURFACE + ); + } + #ifdef USE_RGB_COLOR detectSupportedFormats(); #endif @@ -1188,7 +1201,9 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() { #endif // Finally, blit all our changes to the screen - SDL_UpdateRects(_hwscreen, _numDirtyRects, _dirtyRectList); + if (!_displayDisabled) { + SDL_UpdateRects(_hwscreen, _numDirtyRects, _dirtyRectList); + } } _numDirtyRects = 0; diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.h b/backends/graphics/surfacesdl/surfacesdl-graphics.h index 21444cc25d..97de0f9c97 100644 --- a/backends/graphics/surfacesdl/surfacesdl-graphics.h +++ b/backends/graphics/surfacesdl/surfacesdl-graphics.h @@ -232,6 +232,9 @@ protected: int _scalerType; int _transactionMode; + // Indicates whether it is needed to free _hwsurface in destructor + bool _displayDisabled; + bool _screenIsLocked; Graphics::Surface _framebuffer; diff --git a/backends/mixer/nullmixer/nullsdl-mixer.cpp b/backends/mixer/nullmixer/nullsdl-mixer.cpp new file mode 100644 index 0000000000..2fd652e19f --- /dev/null +++ b/backends/mixer/nullmixer/nullsdl-mixer.cpp @@ -0,0 +1,75 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "backends/mixer/nullmixer/nullsdl-mixer.h" +#include "common/savefile.h" + +NullSdlMixerManager::NullSdlMixerManager() : SdlMixerManager() { + _outputRate = 22050; + _callsCounter = 0; + _callbackPeriod = 10; + _samples = 8192; + while (_samples * 16 > _outputRate * 2) + _samples >>= 1; + _samplesBuf = new uint8[_samples * 4]; +} + +NullSdlMixerManager::~NullSdlMixerManager() { + delete _samplesBuf; +} + +void NullSdlMixerManager::init() { + _mixer = new Audio::MixerImpl(g_system, _outputRate); + assert(_mixer); + _mixer->setReady(true); +} + +void NullSdlMixerManager::suspendAudio() { + _audioSuspended = true; +} + +int NullSdlMixerManager::resumeAudio() { + if (!_audioSuspended) { + return -2; + } + _audioSuspended = false; + return 0; +} + + +void NullSdlMixerManager::startAudio() { +} + +void NullSdlMixerManager::callbackHandler(byte *samples, int len) { + assert(_mixer); + _mixer->mixCallback(samples, len); +} + +void NullSdlMixerManager::update() { + if (_audioSuspended) { + return; + } + _callsCounter++; + if ((_callsCounter % _callbackPeriod) == 0) { + callbackHandler(_samplesBuf, _samples); + } +} diff --git a/backends/mixer/nullmixer/nullsdl-mixer.h b/backends/mixer/nullmixer/nullsdl-mixer.h new file mode 100644 index 0000000000..94248ced66 --- /dev/null +++ b/backends/mixer/nullmixer/nullsdl-mixer.h @@ -0,0 +1,62 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef BACKENDS_MIXER_NULLSDL_H +#define BACKENDS_MIXER_NULLSDL_H + +#include "backends/mixer/sdl/sdl-mixer.h" +#include "common/str.h" + +/** Audio mixer which in fact does not output audio. + * + * It is used by events recorder since the recorder is intentionally + * turning sound off to avoid stuttering. + * + * It returns correct output and shoots callbacks, so all OSystem + * users could work without modifications. + */ + +class NullSdlMixerManager : public SdlMixerManager { +public: + NullSdlMixerManager(); + virtual ~NullSdlMixerManager(); + + virtual void init(); + void update(); + + virtual void suspendAudio(); + virtual int resumeAudio(); + +protected: + + virtual void startAudio(); + virtual void callbackHandler(byte *samples, int len); + +private: + uint32 _outputRate; + uint32 _callsCounter; + uint8 _callbackPeriod; + uint32 _samples; + uint8 *_samplesBuf; +}; + +#endif diff --git a/backends/modular-backend.cpp b/backends/modular-backend.cpp index 72a6699a06..d84df75439 100644 --- a/backends/modular-backend.cpp +++ b/backends/modular-backend.cpp @@ -26,6 +26,7 @@ #include "backends/graphics/graphics.h" #include "backends/mutex/mutex.h" +#include "gui/EventRecorder.h" #include "audio/mixer.h" #include "graphics/pixelformat.h" @@ -141,7 +142,9 @@ void ModularBackend::fillScreen(uint32 col) { } void ModularBackend::updateScreen() { + g_eventRec.preDrawOverlayGui(); _graphicsManager->updateScreen(); + g_eventRec.postDrawOverlayGui(); } void ModularBackend::setShakePos(int shakeOffset) { diff --git a/backends/module.mk b/backends/module.mk index 092b369ab0..31ac444750 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -70,7 +70,7 @@ MODULE_OBJS += \ mutex/sdl/sdl-mutex.o \ plugins/sdl/sdl-provider.o \ timer/sdl/sdl-timer.o - + # SDL 1.3 removed audio CD support ifndef USE_SDL13 MODULE_OBJS += \ @@ -214,5 +214,11 @@ MODULE_OBJS += \ plugins/wii/wii-provider.o endif +ifdef ENABLE_EVENTRECORDER +MODULE_OBJS += \ + mixer/nullmixer/nullsdl-mixer.o \ + saves/recorder/recorder-saves.o +endif + # Include common rules include $(srcdir)/rules.mk diff --git a/backends/mutex/sdl/sdl-mutex.cpp b/backends/mutex/sdl/sdl-mutex.cpp index 8491ae468c..a51e6f0e38 100644 --- a/backends/mutex/sdl/sdl-mutex.cpp +++ b/backends/mutex/sdl/sdl-mutex.cpp @@ -33,15 +33,15 @@ OSystem::MutexRef SdlMutexManager::createMutex() { } void SdlMutexManager::lockMutex(OSystem::MutexRef mutex) { - SDL_mutexP((SDL_mutex *) mutex); + SDL_mutexP((SDL_mutex *)mutex); } void SdlMutexManager::unlockMutex(OSystem::MutexRef mutex) { - SDL_mutexV((SDL_mutex *) mutex); + SDL_mutexV((SDL_mutex *)mutex); } void SdlMutexManager::deleteMutex(OSystem::MutexRef mutex) { - SDL_DestroyMutex((SDL_mutex *) mutex); + SDL_DestroyMutex((SDL_mutex *)mutex); } #endif diff --git a/backends/platform/android/android.cpp b/backends/platform/android/android.cpp index f06e4be19e..ad80ea7f8c 100644 --- a/backends/platform/android/android.cpp +++ b/backends/platform/android/android.cpp @@ -450,7 +450,7 @@ bool OSystem_Android::getFeatureState(Feature f) { } } -uint32 OSystem_Android::getMillis() { +uint32 OSystem_Android::getMillis(bool skipRecord) { timeval curTime; gettimeofday(&curTime, 0); diff --git a/backends/platform/android/android.h b/backends/platform/android/android.h index 5f2f40b726..b4813b3bdf 100644 --- a/backends/platform/android/android.h +++ b/backends/platform/android/android.h @@ -274,7 +274,7 @@ public: virtual void setCursorPalette(const byte *colors, uint start, uint num); virtual bool pollEvent(Common::Event &event); - virtual uint32 getMillis(); + virtual uint32 getMillis(bool skipRecord = false); virtual void delayMillis(uint msecs); virtual MutexRef createMutex(void); diff --git a/backends/platform/dc/dc.h b/backends/platform/dc/dc.h index d41839d961..d62ced02e1 100644 --- a/backends/platform/dc/dc.h +++ b/backends/platform/dc/dc.h @@ -151,7 +151,7 @@ public: void setShakePos(int shake_pos); // Get the number of milliseconds since the program was started. - uint32 getMillis(); + uint32 getMillis(bool skipRecord = false); // Delay for a specified amount of milliseconds void delayMillis(uint msecs); diff --git a/backends/platform/dc/time.cpp b/backends/platform/dc/time.cpp index 8cc3a71e8d..1e5f44ec85 100644 --- a/backends/platform/dc/time.cpp +++ b/backends/platform/dc/time.cpp @@ -26,7 +26,7 @@ #include "dc.h" -uint32 OSystem_Dreamcast::getMillis() +uint32 OSystem_Dreamcast::getMillis(bool skipRecord) { static uint32 msecs=0; static unsigned int t0=0; diff --git a/backends/platform/ds/arm9/source/dsmain.cpp b/backends/platform/ds/arm9/source/dsmain.cpp index 830c782b90..9dc66e80d7 100644 --- a/backends/platform/ds/arm9/source/dsmain.cpp +++ b/backends/platform/ds/arm9/source/dsmain.cpp @@ -2280,7 +2280,7 @@ void VBlankHandler(void) { //REG_IF = IRQ_VBLANK; } -int getMillis() { +int getMillis(bool skipRecord) { return currentTimeMillis; // return frameCount * FRAME_TIME; } diff --git a/backends/platform/ds/arm9/source/dsmain.h b/backends/platform/ds/arm9/source/dsmain.h index ad49ae276d..5e91fae13a 100644 --- a/backends/platform/ds/arm9/source/dsmain.h +++ b/backends/platform/ds/arm9/source/dsmain.h @@ -88,7 +88,7 @@ void setGamma(int gamma); // Timers void setTimerCallback(OSystem_DS::TimerProc proc, int interval); // Setup a callback function at a regular interval -int getMillis(); // Return the current runtime in milliseconds +int getMillis(bool skipRecord = false); // Return the current runtime in milliseconds void doTimerCallback(); // Call callback function if required // Sound diff --git a/backends/platform/ds/arm9/source/osystem_ds.cpp b/backends/platform/ds/arm9/source/osystem_ds.cpp index a4b9c842fc..2f6358d8ee 100644 --- a/backends/platform/ds/arm9/source/osystem_ds.cpp +++ b/backends/platform/ds/arm9/source/osystem_ds.cpp @@ -656,7 +656,7 @@ bool OSystem_DS::pollEvent(Common::Event &event) { return false; } -uint32 OSystem_DS::getMillis() { +uint32 OSystem_DS::getMillis(bool skipRecord) { return DS::getMillis(); } diff --git a/backends/platform/ds/arm9/source/osystem_ds.h b/backends/platform/ds/arm9/source/osystem_ds.h index a6001da764..4550e22b2c 100644 --- a/backends/platform/ds/arm9/source/osystem_ds.h +++ b/backends/platform/ds/arm9/source/osystem_ds.h @@ -117,7 +117,7 @@ public: virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, u32 keycolor, bool dontScale, const Graphics::PixelFormat *format); virtual bool pollEvent(Common::Event &event); - virtual uint32 getMillis(); + virtual uint32 getMillis(bool skipRecord = false); virtual void delayMillis(uint msecs); virtual void getTimeAndDate(TimeDate &t) const; diff --git a/backends/platform/iphone/osys_main.cpp b/backends/platform/iphone/osys_main.cpp index ed2c886213..460d3fd2ac 100644 --- a/backends/platform/iphone/osys_main.cpp +++ b/backends/platform/iphone/osys_main.cpp @@ -166,7 +166,7 @@ void OSystem_IPHONE::suspendLoop() { _timeSuspended += getMillis() - startTime; } -uint32 OSystem_IPHONE::getMillis() { +uint32 OSystem_IPHONE::getMillis(bool skipRecord) { //printf("getMillis()\n"); struct timeval currentTime; diff --git a/backends/platform/iphone/osys_main.h b/backends/platform/iphone/osys_main.h index 037125490d..811a8ddb2e 100644 --- a/backends/platform/iphone/osys_main.h +++ b/backends/platform/iphone/osys_main.h @@ -165,7 +165,7 @@ public: virtual void setCursorPalette(const byte *colors, uint start, uint num); virtual bool pollEvent(Common::Event &event); - virtual uint32 getMillis(); + virtual uint32 getMillis(bool skipRecord = false); virtual void delayMillis(uint msecs); virtual MutexRef createMutex(void); diff --git a/backends/platform/n64/osys_n64.h b/backends/platform/n64/osys_n64.h index bc6b3cb1a5..10138b230a 100644 --- a/backends/platform/n64/osys_n64.h +++ b/backends/platform/n64/osys_n64.h @@ -184,7 +184,7 @@ public: virtual void setCursorPalette(const byte *colors, uint start, uint num); virtual bool pollEvent(Common::Event &event); - virtual uint32 getMillis(); + virtual uint32 getMillis(bool skipRecord = false); virtual void delayMillis(uint msecs); virtual MutexRef createMutex(void); diff --git a/backends/platform/n64/osys_n64_base.cpp b/backends/platform/n64/osys_n64_base.cpp index 1e2aca9e51..afd93f5e09 100644 --- a/backends/platform/n64/osys_n64_base.cpp +++ b/backends/platform/n64/osys_n64_base.cpp @@ -810,7 +810,7 @@ void OSystem_N64::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, return; } -uint32 OSystem_N64::getMillis() { +uint32 OSystem_N64::getMillis(bool skipRecord) { return getMilliTick(); } diff --git a/backends/platform/null/null.cpp b/backends/platform/null/null.cpp index 4690a67c55..9e05539799 100644 --- a/backends/platform/null/null.cpp +++ b/backends/platform/null/null.cpp @@ -49,7 +49,7 @@ public: virtual bool pollEvent(Common::Event &event); - virtual uint32 getMillis(); + virtual uint32 getMillis(bool skipRecord = false); virtual void delayMillis(uint msecs); virtual void getTimeAndDate(TimeDate &t) const {} diff --git a/backends/platform/ps2/systemps2.cpp b/backends/platform/ps2/systemps2.cpp index 5628658381..a7d782b07c 100644 --- a/backends/platform/ps2/systemps2.cpp +++ b/backends/platform/ps2/systemps2.cpp @@ -571,7 +571,7 @@ void OSystem_PS2::displayMessageOnOSD(const char *msg) { printf("displayMessageOnOSD: %s\n", msg); } -uint32 OSystem_PS2::getMillis(void) { +uint32 OSystem_PS2::getMillis(bool skipRecord) { return msecCount; } diff --git a/backends/platform/ps2/systemps2.h b/backends/platform/ps2/systemps2.h index 99482d4da4..3ba40a70f9 100644 --- a/backends/platform/ps2/systemps2.h +++ b/backends/platform/ps2/systemps2.h @@ -82,7 +82,7 @@ public: virtual void warpMouse(int x, int y); virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspot_x, int hotspot_y, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = 0); - virtual uint32 getMillis(); + virtual uint32 getMillis(bool skipRecord = false); virtual void delayMillis(uint msecs); virtual bool pollEvent(Common::Event &event); diff --git a/backends/platform/psp/osys_psp.cpp b/backends/platform/psp/osys_psp.cpp index fb8c1c60bf..8559066e53 100644 --- a/backends/platform/psp/osys_psp.cpp +++ b/backends/platform/psp/osys_psp.cpp @@ -349,7 +349,7 @@ bool OSystem_PSP::pollEvent(Common::Event &event) { return _inputHandler.getAllInputs(event); } -uint32 OSystem_PSP::getMillis() { +uint32 OSystem_PSP::getMillis(bool skipRecord) { return PspRtc::instance().getMillis(); } diff --git a/backends/platform/psp/osys_psp.h b/backends/platform/psp/osys_psp.h index 2afdabd0fc..f4591e476d 100644 --- a/backends/platform/psp/osys_psp.h +++ b/backends/platform/psp/osys_psp.h @@ -125,7 +125,7 @@ public: bool processInput(Common::Event &event); // Time - uint32 getMillis(); + uint32 getMillis(bool skipRecord = false); void delayMillis(uint msecs); // Timer diff --git a/backends/platform/psp/rtc.cpp b/backends/platform/psp/rtc.cpp index cbbb7d3f80..4f15e45535 100644 --- a/backends/platform/psp/rtc.cpp +++ b/backends/platform/psp/rtc.cpp @@ -52,7 +52,7 @@ void PspRtc::init() { // init our starting ticks // Note that after we fill up 32 bits ie 50 days we'll loop back to 0, which may cause // unpredictable results -uint32 PspRtc::getMillis() { +uint32 PspRtc::getMillis(bool skipRecord) { uint32 ticks[2]; sceRtcGetCurrentTick((u64 *)ticks); // can introduce weird thread delays diff --git a/backends/platform/psp/rtc.h b/backends/platform/psp/rtc.h index 45885c3e66..d2689681dd 100644 --- a/backends/platform/psp/rtc.h +++ b/backends/platform/psp/rtc.h @@ -40,7 +40,7 @@ public: init(); } void init(); - uint32 getMillis(); + uint32 getMillis(bool skipRecord = false); uint32 getMicros(); }; diff --git a/backends/platform/sdl/sdl-sys.h b/backends/platform/sdl/sdl-sys.h index ca3c586e03..eccf73815d 100644 --- a/backends/platform/sdl/sdl-sys.h +++ b/backends/platform/sdl/sdl-sys.h @@ -35,8 +35,11 @@ // it with an alternate slightly less unfriendly override. #if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_FILE) #undef FILE +// Solaris has typedef __FILE FILE in several places already +#if !defined(__sun) typedef struct { int FAKE; } FAKE_FILE; #define FILE FAKE_FILE +#endif // (__sun) #endif #if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_strcasecmp) diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp index d54854352d..f55dd277c7 100644 --- a/backends/platform/sdl/sdl.cpp +++ b/backends/platform/sdl/sdl.cpp @@ -30,7 +30,7 @@ #include "backends/platform/sdl/sdl.h" #include "common/config-manager.h" -#include "common/EventRecorder.h" +#include "gui/EventRecorder.h" #include "common/taskbar.h" #include "common/textconsole.h" @@ -97,7 +97,9 @@ OSystem_SDL::~OSystem_SDL() { _audiocdManager = 0; delete _mixerManager; _mixerManager = 0; - delete _timerManager; + + delete g_eventRec.getTimerManager(); + _timerManager = 0; delete _mutexManager; _mutexManager = 0; @@ -131,9 +133,6 @@ void OSystem_SDL::init() { if (_mutexManager == 0) _mutexManager = new SdlMutexManager(); - if (_timerManager == 0) - _timerManager = new SdlTimerManager(); - #if defined(USE_TASKBAR) if (_taskbarManager == 0) _taskbarManager = new Common::TaskbarManager(); @@ -191,10 +190,12 @@ void OSystem_SDL::initBackend() { if (_mixerManager == 0) { _mixerManager = new SdlMixerManager(); - // Setup and start mixer _mixerManager->init(); } + g_eventRec.registerMixerManager(_mixerManager); + + g_eventRec.registerTimerManager(new SdlTimerManager()); if (_audiocdManager == 0) { // Audio CD support was removed with SDL 1.3 @@ -466,14 +467,15 @@ void OSystem_SDL::setupIcon() { free(icon); } -uint32 OSystem_SDL::getMillis() { + +uint32 OSystem_SDL::getMillis(bool skipRecord) { uint32 millis = SDL_GetTicks(); - g_eventRec.processMillis(millis); + g_eventRec.processMillis(millis, skipRecord); return millis; } void OSystem_SDL::delayMillis(uint msecs) { - if (!g_eventRec.processDelayMillis(msecs)) + if (!g_eventRec.processDelayMillis()) SDL_Delay(msecs); } @@ -491,12 +493,16 @@ void OSystem_SDL::getTimeAndDate(TimeDate &td) const { Audio::Mixer *OSystem_SDL::getMixer() { assert(_mixerManager); - return _mixerManager->getMixer(); + return getMixerManager()->getMixer(); } SdlMixerManager *OSystem_SDL::getMixerManager() { assert(_mixerManager); - return _mixerManager; + return g_eventRec.getMixerManager(); +} + +Common::TimerManager *OSystem_SDL::getTimerManager() { + return g_eventRec.getTimerManager(); } #ifdef USE_OPENGL diff --git a/backends/platform/sdl/sdl.h b/backends/platform/sdl/sdl.h index f05207b482..840e73ff09 100644 --- a/backends/platform/sdl/sdl.h +++ b/backends/platform/sdl/sdl.h @@ -68,10 +68,11 @@ public: virtual void setWindowCaption(const char *caption); virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0); - virtual uint32 getMillis(); + virtual uint32 getMillis(bool skipRecord = false); virtual void delayMillis(uint msecs); virtual void getTimeAndDate(TimeDate &td) const; virtual Audio::Mixer *getMixer(); + virtual Common::TimerManager *getTimerManager(); protected: bool _inited; diff --git a/backends/platform/tizen/system.cpp b/backends/platform/tizen/system.cpp index 3d966316c7..54d92146e5 100644 --- a/backends/platform/tizen/system.cpp +++ b/backends/platform/tizen/system.cpp @@ -400,7 +400,7 @@ bool TizenSystem::pollEvent(Common::Event &event) { return _appForm->pollEvent(event); } -uint32 TizenSystem::getMillis() { +uint32 TizenSystem::getMillis(bool skipRecord) { long long result, ticks = 0; SystemTime::GetTicks(ticks); result = ticks - _epoch; diff --git a/backends/platform/tizen/system.h b/backends/platform/tizen/system.h index 19394e1c92..b38940cc95 100644 --- a/backends/platform/tizen/system.h +++ b/backends/platform/tizen/system.h @@ -82,7 +82,7 @@ private: void updateScreen(); bool pollEvent(Common::Event &event); - uint32 getMillis(); + uint32 getMillis(bool skipRecord = false); void delayMillis(uint msecs); void getTimeAndDate(TimeDate &t) const; void fatalError(); diff --git a/backends/platform/wii/osystem.cpp b/backends/platform/wii/osystem.cpp index 22a6495f8f..9d3a7473e3 100644 --- a/backends/platform/wii/osystem.cpp +++ b/backends/platform/wii/osystem.cpp @@ -203,7 +203,7 @@ bool OSystem_Wii::getFeatureState(Feature f) { } } -uint32 OSystem_Wii::getMillis() { +uint32 OSystem_Wii::getMillis(bool skipRecord) { return ticks_to_millisecs(diff_ticks(_startup_time, gettime())); } diff --git a/backends/platform/wii/osystem.h b/backends/platform/wii/osystem.h index 5d6998d0b6..287c70ad6b 100644 --- a/backends/platform/wii/osystem.h +++ b/backends/platform/wii/osystem.h @@ -193,7 +193,7 @@ public: const Graphics::PixelFormat *format); virtual bool pollEvent(Common::Event &event); - virtual uint32 getMillis(); + virtual uint32 getMillis(bool skipRecord = false); virtual void delayMillis(uint msecs); virtual MutexRef createMutex(); diff --git a/backends/saves/recorder/recorder-saves.cpp b/backends/saves/recorder/recorder-saves.cpp new file mode 100644 index 0000000000..49b4672913 --- /dev/null +++ b/backends/saves/recorder/recorder-saves.cpp @@ -0,0 +1,35 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "backends/saves/recorder/recorder-saves.h" +#include "gui/EventRecorder.h" +#include "common/savefile.h" + +Common::InSaveFile *RecorderSaveFileManager::openForLoading(const Common::String &filename) { + Common::InSaveFile *result = g_eventRec.processSaveStream(filename); + return result; +} + +Common::StringArray RecorderSaveFileManager::listSaveFiles(const Common::String &pattern) { + return g_eventRec.listSaveFiles(pattern); +} + diff --git a/backends/saves/recorder/recorder-saves.h b/backends/saves/recorder/recorder-saves.h new file mode 100644 index 0000000000..692aeca329 --- /dev/null +++ b/backends/saves/recorder/recorder-saves.h @@ -0,0 +1,36 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef BACKEND_SAVES_RECORDER_H +#define BACKEND_SAVES_RECORDER_H + +#include "backends/saves/default/default-saves.h" + +/** + * Provides a savefile manager implementation for event recorder. + */ +class RecorderSaveFileManager : public DefaultSaveFileManager { + virtual Common::StringArray listSaveFiles(const Common::String &pattern); + virtual Common::InSaveFile *openForLoading(const Common::String &filename); +}; + +#endif diff --git a/backends/timer/default/default-timer.cpp b/backends/timer/default/default-timer.cpp index 9f56d58b12..ce93320f3d 100644 --- a/backends/timer/default/default-timer.cpp +++ b/backends/timer/default/default-timer.cpp @@ -80,7 +80,7 @@ DefaultTimerManager::~DefaultTimerManager() { void DefaultTimerManager::handler() { Common::StackLock lock(_mutex); - const uint32 curTime = g_system->getMillis(); + uint32 curTime = g_system->getMillis(true); // Repeat as long as there is a TimerSlot that is scheduled to fire. TimerSlot *slot = _head->next; diff --git a/base/commandLine.cpp b/base/commandLine.cpp index 42a3a64d34..6f173a594d 100644 --- a/base/commandLine.cpp +++ b/base/commandLine.cpp @@ -118,6 +118,13 @@ static const char HELP_STRING[] = " --aspect-ratio Enable aspect ratio correction\n" " --render-mode=MODE Enable additional render modes (cga, ega, hercGreen,\n" " hercAmber, amiga)\n" +#ifdef ENABLE_EVENTRECORDER + " --record-mode=MODE Specify record mode for event recorder (record, playback,\n" + " passthrough [default])\n" + " --record-file=FILE Specify record file name\n" + " --disable-display Disable any gfx output. Used for headless events\n" + " playback by Event Recorder\n" +#endif "\n" #if defined(ENABLE_SKY) || defined(ENABLE_QUEEN) " --alt-intro Use alternative intro for CD versions of Beneath a\n" @@ -232,10 +239,9 @@ void registerDefaults() { ConfMan.registerDefault("confirm_exit", false); ConfMan.registerDefault("disable_sdl_parachute", false); + ConfMan.registerDefault("disable_display", false); ConfMan.registerDefault("record_mode", "none"); ConfMan.registerDefault("record_file_name", "record.bin"); - ConfMan.registerDefault("record_temp_file_name", "record.tmp"); - ConfMan.registerDefault("record_time_file_name", "record.time"); ConfMan.registerDefault("gui_saveload_chooser", "grid"); ConfMan.registerDefault("gui_saveload_last_pos", "0"); @@ -424,6 +430,17 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha DO_OPTION_BOOL('f', "fullscreen") END_OPTION +#ifdef ENABLE_EVENTRECORDER + DO_LONG_OPTION_INT("disable-display") + END_OPTION + + DO_LONG_OPTION("record-mode") + END_OPTION + + DO_LONG_OPTION("record-file-name") + END_OPTION +#endif + DO_LONG_OPTION("opl-driver") END_OPTION @@ -569,18 +586,6 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha END_OPTION #endif - DO_LONG_OPTION("record-mode") - END_OPTION - - DO_LONG_OPTION("record-file-name") - END_OPTION - - DO_LONG_OPTION("record-temp-file-name") - END_OPTION - - DO_LONG_OPTION("record-time-file-name") - END_OPTION - #ifdef IPHONE // This is automatically set when launched from the Springboard. DO_LONG_OPTION_OPT("launchedFromSB", 0) diff --git a/base/main.cpp b/base/main.cpp index 355a65f883..3f51c97949 100644 --- a/base/main.cpp +++ b/base/main.cpp @@ -42,8 +42,11 @@ #include "common/debug.h" #include "common/debug-channels.h" /* for debug manager */ #include "common/events.h" -#include "common/EventRecorder.h" +#include "gui/EventRecorder.h" #include "common/fs.h" +#ifdef ENABLE_EVENTRECORDER +#include "common/recorderfile.h" +#endif #include "common/system.h" #include "common/textconsole.h" #include "common/tokenizer.h" @@ -409,7 +412,9 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) { settings["gfx-mode"] = "default"; } } - + if (settings.contains("disable-display")) { + ConfMan.setInt("disable-display", 1, Common::ConfigManager::kTransientDomain); + } setupGraphics(system); // Init the different managers that are used by the engines. @@ -428,7 +433,7 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) { // TODO: This is just to match the current behavior, when we further extend // our event recorder, we might do this at another place. Or even change // the whole API for that ;-). - g_eventRec.init(); + g_eventRec.RegisterEventSource(); // Now as the event manager is created, setup the keymapper setupKeymapper(system); @@ -448,6 +453,21 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) { // to save memory PluginManager::instance().unloadPluginsExcept(PLUGIN_TYPE_ENGINE, plugin); +#ifdef ENABLE_EVENTRECORDER + Common::String recordMode = ConfMan.get("record_mode"); + Common::String recordFileName = ConfMan.get("record_file_name"); + + if (recordMode == "record") { + g_eventRec.init(g_eventRec.generateRecordFileName(ConfMan.getActiveDomainName()), GUI::EventRecorder::kRecorderRecord); + } else if (recordMode == "playback") { + g_eventRec.init(recordFileName, GUI::EventRecorder::kRecorderPlayback); + } else if ((recordMode == "info") && (!recordFileName.empty())) { + Common::PlaybackFile record; + record.openRead(recordFileName); + debug("info:author=%s name=%s description=%s", record.getHeader().author.c_str(), record.getHeader().name.c_str(), record.getHeader().description.c_str()); + break; + } +#endif // Try to run the game Common::Error result = runGame(plugin, system, specialDebug); @@ -478,6 +498,11 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) { #ifdef FORCE_RTL g_system->getEventManager()->resetQuit(); #endif + #ifdef ENABLE_EVENTRECORDER + if (g_eventRec.checkForContinueGame()) { + continue; + } + #endif // Discard any command line options. It's unlikely that the user // wanted to apply them to *all* games ever launched. @@ -501,7 +526,7 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) { GUI::GuiManager::destroy(); Common::ConfigManager::destroy(); Common::DebugManager::destroy(); - Common::EventRecorder::destroy(); + GUI::EventRecorder::destroy(); Common::SearchManager::destroy(); #ifdef USE_TRANSLATION Common::TranslationManager::destroy(); diff --git a/common/EventDispatcher.cpp b/common/EventDispatcher.cpp index 012a2dfce5..dc123e8f8f 100644 --- a/common/EventDispatcher.cpp +++ b/common/EventDispatcher.cpp @@ -38,7 +38,9 @@ EventDispatcher::~EventDispatcher() { delete i->observer; } - delete _mapper; + if (_autoFreeMapper) { + delete _mapper; + } _mapper = 0; } @@ -68,11 +70,15 @@ void EventDispatcher::dispatch() { } } -void EventDispatcher::registerMapper(EventMapper *mapper) { - delete _mapper; +void EventDispatcher::registerMapper(EventMapper *mapper, bool autoFree) { + if (_autoFreeMapper) { + delete _mapper; + } _mapper = mapper; + _autoFreeMapper = autoFree; } + void EventDispatcher::registerSource(EventSource *source, bool autoFree) { SourceEntry newEntry; diff --git a/common/EventRecorder.cpp b/common/EventRecorder.cpp deleted file mode 100644 index 5e24f128c3..0000000000 --- a/common/EventRecorder.cpp +++ /dev/null @@ -1,428 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include "common/EventRecorder.h" - -#include "common/bufferedstream.h" -#include "common/config-manager.h" -#include "common/random.h" -#include "common/savefile.h" -#include "common/textconsole.h" - -namespace Common { - -DECLARE_SINGLETON(EventRecorder); - -#define RECORD_SIGNATURE 0x54455354 -#define RECORD_VERSION 1 - -uint32 readTime(ReadStream *inFile) { - uint32 d = inFile->readByte(); - if (d == 0xff) { - d = inFile->readUint32LE(); - } - - return d; -} - -void writeTime(WriteStream *outFile, uint32 d) { - //Simple RLE compression - if (d >= 0xff) { - outFile->writeByte(0xff); - outFile->writeUint32LE(d); - } else { - outFile->writeByte(d); - } -} - -void readRecord(SeekableReadStream *inFile, uint32 &diff, Event &event, uint32 &millis) { - millis = readTime(inFile); - - diff = inFile->readUint32LE(); - - event.type = (EventType)inFile->readUint32LE(); - - switch (event.type) { - case EVENT_KEYDOWN: - case EVENT_KEYUP: - event.kbd.keycode = (KeyCode)inFile->readSint32LE(); - event.kbd.ascii = inFile->readUint16LE(); - event.kbd.flags = inFile->readByte(); - break; - case EVENT_MOUSEMOVE: - case EVENT_LBUTTONDOWN: - case EVENT_LBUTTONUP: - case EVENT_RBUTTONDOWN: - case EVENT_RBUTTONUP: - case EVENT_WHEELUP: - case EVENT_WHEELDOWN: - case EVENT_MBUTTONDOWN: - case EVENT_MBUTTONUP: - event.mouse.x = inFile->readSint16LE(); - event.mouse.y = inFile->readSint16LE(); - break; - default: - break; - } -} - -void writeRecord(WriteStream *outFile, uint32 diff, const Event &event, uint32 millis) { - writeTime(outFile, millis); - - outFile->writeUint32LE(diff); - - outFile->writeUint32LE((uint32)event.type); - - switch (event.type) { - case EVENT_KEYDOWN: - case EVENT_KEYUP: - outFile->writeSint32LE(event.kbd.keycode); - outFile->writeUint16LE(event.kbd.ascii); - outFile->writeByte(event.kbd.flags); - break; - case EVENT_MOUSEMOVE: - case EVENT_LBUTTONDOWN: - case EVENT_LBUTTONUP: - case EVENT_RBUTTONDOWN: - case EVENT_RBUTTONUP: - case EVENT_WHEELUP: - case EVENT_WHEELDOWN: - case EVENT_MBUTTONDOWN: - case EVENT_MBUTTONUP: - outFile->writeSint16LE(event.mouse.x); - outFile->writeSint16LE(event.mouse.y); - break; - default: - break; - } -} - -EventRecorder::EventRecorder() { - _recordFile = NULL; - _recordTimeFile = NULL; - _playbackFile = NULL; - _playbackTimeFile = NULL; - _timeMutex = g_system->createMutex(); - _recorderMutex = g_system->createMutex(); - - _eventCount = 0; - _lastEventCount = 0; - _lastMillis = 0; - _lastEventMillis = 0; - - _recordMode = kPassthrough; -} - -EventRecorder::~EventRecorder() { - deinit(); - - g_system->deleteMutex(_timeMutex); - g_system->deleteMutex(_recorderMutex); -} - -void EventRecorder::init() { - String recordModeString = ConfMan.get("record_mode"); - if (recordModeString.compareToIgnoreCase("record") == 0) { - _recordMode = kRecorderRecord; - - debug(3, "EventRecorder: record"); - } else { - if (recordModeString.compareToIgnoreCase("playback") == 0) { - _recordMode = kRecorderPlayback; - debug(3, "EventRecorder: playback"); - } else { - _recordMode = kPassthrough; - debug(3, "EventRecorder: passthrough"); - } - } - - _recordFileName = ConfMan.get("record_file_name"); - if (_recordFileName.empty()) { - _recordFileName = "record.bin"; - } - _recordTempFileName = ConfMan.get("record_temp_file_name"); - if (_recordTempFileName.empty()) { - _recordTempFileName = "record.tmp"; - } - _recordTimeFileName = ConfMan.get("record_time_file_name"); - if (_recordTimeFileName.empty()) { - _recordTimeFileName = "record.time"; - } - - // recorder stuff - if (_recordMode == kRecorderRecord) { - _recordCount = 0; - _recordTimeCount = 0; - _recordFile = wrapBufferedWriteStream(g_system->getSavefileManager()->openForSaving(_recordTempFileName), 128 * 1024); - _recordTimeFile = wrapBufferedWriteStream(g_system->getSavefileManager()->openForSaving(_recordTimeFileName), 128 * 1024); - _recordSubtitles = ConfMan.getBool("subtitles"); - } - - uint32 sign; - uint32 randomSourceCount; - if (_recordMode == kRecorderPlayback) { - _playbackCount = 0; - _playbackTimeCount = 0; - _playbackFile = wrapBufferedSeekableReadStream(g_system->getSavefileManager()->openForLoading(_recordFileName), 128 * 1024, DisposeAfterUse::YES); - _playbackTimeFile = wrapBufferedSeekableReadStream(g_system->getSavefileManager()->openForLoading(_recordTimeFileName), 128 * 1024, DisposeAfterUse::YES); - - if (!_playbackFile) { - warning("Cannot open playback file %s. Playback was switched off", _recordFileName.c_str()); - _recordMode = kPassthrough; - } - - if (!_playbackTimeFile) { - warning("Cannot open playback time file %s. Playback was switched off", _recordTimeFileName.c_str()); - _recordMode = kPassthrough; - } - } - - if (_recordMode == kRecorderPlayback) { - sign = _playbackFile->readUint32LE(); - if (sign != RECORD_SIGNATURE) { - error("Unknown record file signature"); - } - - _playbackFile->readUint32LE(); // version - - // conf vars - ConfMan.setBool("subtitles", _playbackFile->readByte() != 0); - - _recordCount = _playbackFile->readUint32LE(); - _recordTimeCount = _playbackFile->readUint32LE(); - - randomSourceCount = _playbackFile->readUint32LE(); - for (uint i = 0; i < randomSourceCount; ++i) { - RandomSourceRecord rec; - rec.name = ""; - uint32 sLen = _playbackFile->readUint32LE(); - for (uint j = 0; j < sLen; ++j) { - char c = _playbackFile->readSByte(); - rec.name += c; - } - rec.seed = _playbackFile->readUint32LE(); - _randomSourceRecords.push_back(rec); - } - - _hasPlaybackEvent = false; - } - - g_system->getEventManager()->getEventDispatcher()->registerSource(this, false); - g_system->getEventManager()->getEventDispatcher()->registerObserver(this, EventManager::kEventRecorderPriority, false, true); -} - -void EventRecorder::deinit() { - debug(3, "EventRecorder: deinit"); - - g_system->getEventManager()->getEventDispatcher()->unregisterSource(this); - g_system->getEventManager()->getEventDispatcher()->unregisterObserver(this); - - g_system->lockMutex(_timeMutex); - g_system->lockMutex(_recorderMutex); - _recordMode = kPassthrough; - g_system->unlockMutex(_timeMutex); - g_system->unlockMutex(_recorderMutex); - - delete _playbackFile; - delete _playbackTimeFile; - - if (_recordFile != NULL) { - _recordFile->finalize(); - delete _recordFile; - _recordTimeFile->finalize(); - delete _recordTimeFile; - - _playbackFile = g_system->getSavefileManager()->openForLoading(_recordTempFileName); - - assert(_playbackFile); - - _recordFile = g_system->getSavefileManager()->openForSaving(_recordFileName); - _recordFile->writeUint32LE(RECORD_SIGNATURE); - _recordFile->writeUint32LE(RECORD_VERSION); - - // conf vars - _recordFile->writeByte(_recordSubtitles ? 1 : 0); - - _recordFile->writeUint32LE(_recordCount); - _recordFile->writeUint32LE(_recordTimeCount); - - _recordFile->writeUint32LE(_randomSourceRecords.size()); - for (uint i = 0; i < _randomSourceRecords.size(); ++i) { - _recordFile->writeUint32LE(_randomSourceRecords[i].name.size()); - _recordFile->writeString(_randomSourceRecords[i].name); - _recordFile->writeUint32LE(_randomSourceRecords[i].seed); - } - - for (uint i = 0; i < _recordCount; ++i) { - uint32 tempDiff; - Event tempEvent; - uint32 millis; - readRecord(_playbackFile, tempDiff, tempEvent, millis); - writeRecord(_recordFile, tempDiff, tempEvent, millis); - } - - _recordFile->finalize(); - delete _recordFile; - delete _playbackFile; - - //TODO: remove recordTempFileName'ed file - } -} - -void EventRecorder::registerRandomSource(RandomSource &rnd, const String &name) { - if (_recordMode == kRecorderRecord) { - RandomSourceRecord rec; - rec.name = name; - rec.seed = rnd.getSeed(); - _randomSourceRecords.push_back(rec); - } - - if (_recordMode == kRecorderPlayback) { - for (uint i = 0; i < _randomSourceRecords.size(); ++i) { - if (_randomSourceRecords[i].name == name) { - rnd.setSeed(_randomSourceRecords[i].seed); - _randomSourceRecords.remove_at(i); - break; - } - } - } -} - -void EventRecorder::processMillis(uint32 &millis) { - uint32 d; - if (_recordMode == kPassthrough) { - return; - } - - g_system->lockMutex(_timeMutex); - if (_recordMode == kRecorderRecord) { - d = millis - _lastMillis; - writeTime(_recordTimeFile, d); - - _recordTimeCount++; - } - - if (_recordMode == kRecorderPlayback) { - if (_recordTimeCount > _playbackTimeCount) { - d = readTime(_playbackTimeFile); - - while ((_lastMillis + d > millis) && (_lastMillis + d - millis > 50)) { - _recordMode = kPassthrough; - g_system->delayMillis(50); - millis = g_system->getMillis(); - _recordMode = kRecorderPlayback; - } - - millis = _lastMillis + d; - _playbackTimeCount++; - } - } - - _lastMillis = millis; - g_system->unlockMutex(_timeMutex); -} - -bool EventRecorder::processDelayMillis(uint &msecs) { - if (_recordMode == kRecorderPlayback) { - _recordMode = kPassthrough; - - uint32 millis = g_system->getMillis(); - - _recordMode = kRecorderPlayback; - - if (_lastMillis > millis) { - // Skip delay if we're getting late - return true; - } - } - - return false; -} - -bool EventRecorder::notifyEvent(const Event &ev) { - if (_recordMode != kRecorderRecord) - return false; - - StackLock lock(_recorderMutex); - ++_eventCount; - - writeRecord(_recordFile, _eventCount - _lastEventCount, ev, _lastMillis - _lastEventMillis); - - _recordCount++; - _lastEventCount = _eventCount; - _lastEventMillis = _lastMillis; - - return false; -} - -bool EventRecorder::notifyPoll() { - if (_recordMode != kRecorderRecord) - return false; - - ++_eventCount; - - return false; -} - -bool EventRecorder::pollEvent(Event &ev) { - uint32 millis; - - if (_recordMode != kRecorderPlayback) - return false; - - StackLock lock(_recorderMutex); - ++_eventCount; - - if (!_hasPlaybackEvent) { - if (_recordCount > _playbackCount) { - readRecord(_playbackFile, const_cast<uint32&>(_playbackDiff), _playbackEvent, millis); - _playbackCount++; - _hasPlaybackEvent = true; - } - } - - if (_hasPlaybackEvent) { - if (_playbackDiff <= (_eventCount - _lastEventCount)) { - switch (_playbackEvent.type) { - case EVENT_MOUSEMOVE: - case EVENT_LBUTTONDOWN: - case EVENT_LBUTTONUP: - case EVENT_RBUTTONDOWN: - case EVENT_RBUTTONUP: - case EVENT_WHEELUP: - case EVENT_WHEELDOWN: - g_system->warpMouse(_playbackEvent.mouse.x, _playbackEvent.mouse.y); - break; - default: - break; - } - ev = _playbackEvent; - _hasPlaybackEvent = false; - _lastEventCount = _eventCount; - return true; - } - } - - return false; -} - -} // End of namespace Common diff --git a/common/EventRecorder.h b/common/EventRecorder.h deleted file mode 100644 index 43a08b08cd..0000000000 --- a/common/EventRecorder.h +++ /dev/null @@ -1,110 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef COMMON_EVENTRECORDER_H -#define COMMON_EVENTRECORDER_H - -#include "common/scummsys.h" -#include "common/events.h" -#include "common/singleton.h" -#include "common/mutex.h" -#include "common/array.h" - -#define g_eventRec (Common::EventRecorder::instance()) - -namespace Common { - -class RandomSource; -class SeekableReadStream; -class WriteStream; - -/** - * Our generic event recorder. - * - * TODO: Add more documentation. - */ -class EventRecorder : private EventSource, private EventObserver, public Singleton<EventRecorder> { - friend class Singleton<SingletonBaseType>; - EventRecorder(); - ~EventRecorder(); -public: - void init(); - void deinit(); - - /** Register random source so it can be serialized in game test purposes */ - void registerRandomSource(RandomSource &rnd, const String &name); - - /** TODO: Add documentation, this is only used by the backend */ - void processMillis(uint32 &millis); - - /** TODO: Add documentation, this is only used by the backend */ - bool processDelayMillis(uint &msecs); - -private: - bool notifyEvent(const Event &ev); - bool notifyPoll(); - bool pollEvent(Event &ev); - bool allowMapping() const { return false; } - - class RandomSourceRecord { - public: - String name; - uint32 seed; - }; - Array<RandomSourceRecord> _randomSourceRecords; - - bool _recordSubtitles; - volatile uint32 _recordCount; - volatile uint32 _lastRecordEvent; - volatile uint32 _recordTimeCount; - volatile uint32 _lastEventMillis; - WriteStream *_recordFile; - WriteStream *_recordTimeFile; - MutexRef _timeMutex; - MutexRef _recorderMutex; - volatile uint32 _lastMillis; - - volatile uint32 _playbackCount; - volatile uint32 _playbackDiff; - volatile bool _hasPlaybackEvent; - volatile uint32 _playbackTimeCount; - Event _playbackEvent; - SeekableReadStream *_playbackFile; - SeekableReadStream *_playbackTimeFile; - - volatile uint32 _eventCount; - volatile uint32 _lastEventCount; - - enum RecordMode { - kPassthrough = 0, - kRecorderRecord = 1, - kRecorderPlayback = 2 - }; - volatile RecordMode _recordMode; - String _recordFileName; - String _recordTempFileName; - String _recordTimeFileName; -}; - -} // End of namespace Common - -#endif diff --git a/common/debug.h b/common/debug.h index dc94839082..859f3c41b3 100644 --- a/common/debug.h +++ b/common/debug.h @@ -117,5 +117,9 @@ void debugCN(uint32 debugChannels, const char *s, ...) GCC_PRINTF(2, 3); */ extern int gDebugLevel; +//Global constant for EventRecorder debug channel +enum GlobalDebugLevels { + kDebugLevelEventRec = 1 << 30 +}; #endif diff --git a/common/events.h b/common/events.h index 7366c51d36..9029a4096a 100644 --- a/common/events.h +++ b/common/events.h @@ -288,11 +288,14 @@ public: * to the EventDispatcher, thus it will be deleted * with "delete", when EventDispatcher is destroyed. * - * Note there is only one mapper per EventDispatcher - * possible, thus when this method is called twice, - * the former mapper will be destroied. + * @param autoFree Destroy previous mapper [default] + * Normally we allow only one event mapper to exists, + * However Event Recorder must intervent into normal + * event flow without altering its semantics. Thus during + * Event Recorder playback and recording we allow + * two mappers. */ - void registerMapper(EventMapper *mapper); + void registerMapper(EventMapper *mapper, bool autoFree = true); /** * Queries the setup event mapper. @@ -326,6 +329,7 @@ public: */ void unregisterObserver(EventObserver *obs); private: + bool _autoFreeMapper; EventMapper *_mapper; struct Entry { diff --git a/common/memstream.h b/common/memstream.h index 260fb64d84..7fa6500753 100644 --- a/common/memstream.h +++ b/common/memstream.h @@ -89,8 +89,9 @@ public: */ class MemoryWriteStream : public WriteStream { private: - byte *_ptr; const uint32 _bufSize; +protected: + byte *_ptr; uint32 _pos; bool _err; public: @@ -117,6 +118,40 @@ public: }; /** + * MemoryWriteStream subclass with ability to set stream position indicator. + */ +class SeekableMemoryWriteStream : public MemoryWriteStream { +private: + byte *_ptrOrig; +public: + SeekableMemoryWriteStream(byte *buf, uint32 len) : MemoryWriteStream(buf, len), _ptrOrig(buf) {} + uint32 seek(uint32 offset, int whence = SEEK_SET) { + switch (whence) { + case SEEK_END: + // SEEK_END works just like SEEK_SET, only 'reversed', + // i.e. from the end. + offset = size() + offset; + // Fall through + case SEEK_SET: + _ptr = _ptrOrig + offset; + _pos = offset; + break; + case SEEK_CUR: + _ptr += offset; + _pos += offset; + break; + } + // Post-Condition + if (_pos > size()) { + _pos = size(); + _ptr = _ptrOrig + _pos; + } + return _pos; + } +}; + + +/** * A sort of hybrid between MemoryWriteStream and Array classes. A stream * that grows as it's written to. */ diff --git a/common/module.mk b/common/module.mk index d96b11ee40..9f9126c8ef 100644 --- a/common/module.mk +++ b/common/module.mk @@ -10,7 +10,6 @@ MODULE_OBJS := \ error.o \ EventDispatcher.o \ EventMapper.o \ - EventRecorder.o \ file.o \ fs.o \ gui_options.o \ @@ -51,5 +50,10 @@ MODULE_OBJS += \ rdft.o \ sinetables.o +ifdef ENABLE_EVENTRECORDER +MODULE_OBJS += \ + recorderfile.o +endif + # Include common rules include $(srcdir)/rules.mk diff --git a/common/random.cpp b/common/random.cpp index fd75534c44..a74ab831ea 100644 --- a/common/random.cpp +++ b/common/random.cpp @@ -21,7 +21,7 @@ #include "common/random.h" #include "common/system.h" -#include "common/EventRecorder.h" +#include "gui/EventRecorder.h" namespace Common { @@ -30,13 +30,8 @@ RandomSource::RandomSource(const String &name) { // Use system time as RNG seed. Normally not a good idea, if you are using // a RNG for security purposes, but good enough for our purposes. assert(g_system); - uint32 seed = g_system->getMillis(); + uint32 seed = g_eventRec.getRandomSeed(name); setSeed(seed); - - // Register this random source with the event recorder. This may end - // up querying or resetting the current seed, so we must call it - // *after* the initial seed has been set. - g_eventRec.registerRandomSource(*this, name); } void RandomSource::setSeed(uint32 seed) { diff --git a/common/recorderfile.cpp b/common/recorderfile.cpp new file mode 100644 index 0000000000..60c47e11ce --- /dev/null +++ b/common/recorderfile.cpp @@ -0,0 +1,708 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/system.h" +#include "gui/EventRecorder.h" +#include "common/md5.h" +#include "common/recorderfile.h" +#include "common/savefile.h" +#include "common/bufferedstream.h" +#include "graphics/thumbnail.h" +#include "graphics/surface.h" +#include "graphics/scaler.h" + +#define RECORD_VERSION 1 + +namespace Common { + +PlaybackFile::PlaybackFile() : _tmpRecordFile(_tmpBuffer, kRecordBuffSize), _tmpPlaybackFile(_tmpBuffer, kRecordBuffSize) { + _readStream = NULL; + _writeStream = NULL; + _screenshotsFile = NULL; + _mode = kClosed; +} + +PlaybackFile::~PlaybackFile() { + close(); +} + +bool PlaybackFile::openWrite(const String &fileName) { + close(); + _header.fileName = fileName; + _writeStream = wrapBufferedWriteStream(g_system->getSavefileManager()->openForSaving(fileName), 128 * 1024); + _headerDumped = false; + _recordCount = 0; + if (_writeStream == NULL) { + return false; + } + _mode = kWrite; + return true; +} + +bool PlaybackFile::openRead(const String &fileName) { + close(); + _header.fileName = fileName; + _eventsSize = 0; + _tmpPlaybackFile.seek(0); + _readStream = wrapBufferedSeekableReadStream(g_system->getSavefileManager()->openForLoading(fileName), 128 * 1024, DisposeAfterUse::YES); + if (_readStream == NULL) { + debugC(1, kDebugLevelEventRec, "playback:action=\"Load File\" result=fail reason=\"file %s not found\"", fileName.c_str()); + return false; + } + if (!parseHeader()) { + debugC(1, kDebugLevelEventRec, "playback:action=\"Load File\" result=fail reason=\"header parsing failed\""); + return false; + } + _screenshotsFile = wrapBufferedWriteStream(g_system->getSavefileManager()->openForSaving("screenshots.bin"), 128 * 1024); + debugC(1, kDebugLevelEventRec, "playback:action=\"Load File\" result=success"); + _mode = kRead; + return true; +} + +void PlaybackFile::close() { + delete _readStream; + _readStream = NULL; + if (_writeStream != NULL) { + dumpRecordsToFile(); + _writeStream->finalize(); + delete _writeStream; + _writeStream = NULL; + updateHeader(); + } + if (_screenshotsFile != NULL) { + _screenshotsFile->finalize(); + delete _screenshotsFile; + _screenshotsFile = NULL; + } + for (HashMap<String, SaveFileBuffer>::iterator i = _header.saveFiles.begin(); i != _header.saveFiles.end(); ++i) { + free(i->_value.buffer); + } + _header.saveFiles.clear(); + _mode = kClosed; +} + +bool PlaybackFile::parseHeader() { + PlaybackFileHeader result; + ChunkHeader nextChunk; + _playbackParseState = kFileStateCheckFormat; + if (!readChunkHeader(nextChunk)) { + _playbackParseState = kFileStateError; + return false; + } + while ((_playbackParseState != kFileStateDone) && (_playbackParseState != kFileStateError)) { + if (processChunk(nextChunk)) { + if (!readChunkHeader(nextChunk)) { + warning("Error in header parsing"); + _playbackParseState = kFileStateError; + } + } + } + return _playbackParseState == kFileStateDone; +} + +bool PlaybackFile::checkPlaybackFileVersion() { + uint32 version; + version = _readStream->readUint32LE(); + if (version != RECORD_VERSION) { + warning("Incorrect playback file version. Expected version %d, but got %d.", RECORD_VERSION, version); + return false; + } + return true; +} + + +String PlaybackFile::readString(int len) { + String result; + char buf[50]; + int readSize = 49; + while (len > 0) { + if (len <= 49) { + readSize = len; + } + _readStream->read(buf, readSize); + buf[readSize] = 0; + result += buf; + len -= readSize; + } + return result; +} + +bool PlaybackFile::readChunkHeader(PlaybackFile::ChunkHeader &nextChunk) { + nextChunk.id = (FileTag)_readStream->readUint32LE(); + nextChunk.len = _readStream->readUint32LE(); + return !_readStream->err() && !_readStream->eos(); +} + +bool PlaybackFile::processChunk(ChunkHeader &nextChunk) { + switch (_playbackParseState) { + case kFileStateCheckFormat: + if (nextChunk.id == kFormatIdTag) { + _playbackParseState = kFileStateCheckVersion; + } else { + warning("Unknown playback file signature"); + _playbackParseState = kFileStateError; + } + break; + case kFileStateCheckVersion: + if ((nextChunk.id == kVersionTag) && checkPlaybackFileVersion()) { + _playbackParseState = kFileStateSelectSection; + } else { + _playbackParseState = kFileStateError; + } + break; + case kFileStateSelectSection: + switch (nextChunk.id) { + case kHeaderSectionTag: + _playbackParseState = kFileStateProcessHeader; + break; + case kHashSectionTag: + _playbackParseState = kFileStateProcessHash; + break; + case kRandomSectionTag: + _playbackParseState = kFileStateProcessRandom; + break; + case kEventTag: + case kScreenShotTag: + _readStream->seek(-8, SEEK_CUR); + _playbackParseState = kFileStateDone; + return false; + case kSaveTag: + _playbackParseState = kFileStateProcessSave; + break; + case kSettingsSectionTag: + _playbackParseState = kFileStateProcessSettings; + warning("Loading record header"); + break; + default: + _readStream->skip(nextChunk.len); + break; + } + break; + case kFileStateProcessSave: + if (nextChunk.id == kSaveRecordTag) { + readSaveRecord(); + } else { + _playbackParseState = kFileStateSelectSection; + return false; + } + break; + case kFileStateProcessHeader: + switch (nextChunk.id) { + case kAuthorTag: + _header.author = readString(nextChunk.len); + break; + case kCommentsTag: + _header.notes = readString(nextChunk.len); + break; + case kNameTag: + _header.name = readString(nextChunk.len); + break; + default: + _playbackParseState = kFileStateSelectSection; + return false; + } + break; + case kFileStateProcessHash: + if (nextChunk.id == kHashRecordTag) { + readHashMap(nextChunk); + } else { + _playbackParseState = kFileStateSelectSection; + return false; + } + break; + case kFileStateProcessRandom: + if (nextChunk.id == kRandomRecordTag) { + processRndSeedRecord(nextChunk); + } else { + _playbackParseState = kFileStateSelectSection; + return false; + } + break; + case kFileStateProcessSettings: + if (nextChunk.id == kSettingsRecordTag) { + if (!processSettingsRecord()) { + _playbackParseState = kFileStateError; + return false; + } + } else { + _playbackParseState = kFileStateSelectSection; + return false; + } + break; + default: + return false; + } + return true; +} + +void PlaybackFile::returnToChunkHeader() { + _readStream->seek(-8, SEEK_CUR); +} + +void PlaybackFile::readHashMap(ChunkHeader chunk) { + String hashName = readString(chunk.len - 32); + String hashMd5 = readString(32); + _header.hashRecords[hashName] = hashMd5; +} + +void PlaybackFile::processRndSeedRecord(ChunkHeader chunk) { + String randomSourceName = readString(chunk.len - 4); + uint32 randomSourceSeed = _readStream->readUint32LE(); + _header.randomSourceRecords[randomSourceName] = randomSourceSeed; +} + +bool PlaybackFile::processSettingsRecord() { + ChunkHeader keyChunk; + if (!readChunkHeader(keyChunk) || (keyChunk.id != kSettingsRecordKeyTag)) { + warning("Invalid format of settings section"); + return false; + } + String key = readString(keyChunk.len); + ChunkHeader valueChunk; + if (!readChunkHeader(valueChunk) || (valueChunk.id != kSettingsRecordValueTag)) { + warning("Invalid format of settings section"); + return false; + } + String value = readString(valueChunk.len); + _header.settingsRecords[key] = value; + return true; +} + + +bool PlaybackFile::readSaveRecord() { + ChunkHeader fileNameChunk; + if (!readChunkHeader(fileNameChunk) || (fileNameChunk.id != kSaveRecordNameTag)) { + warning("Invalid format of save section"); + return false; + } + String fileName = readString(fileNameChunk.len); + ChunkHeader saveBufferChunk; + if (!readChunkHeader(saveBufferChunk) || (saveBufferChunk.id != kSaveRecordBufferTag)) { + warning("Invalid format of save section"); + return false; + } + SaveFileBuffer buf; + buf.size = saveBufferChunk.len; + buf.buffer = (byte *)malloc(saveBufferChunk.len); + _readStream->read(buf.buffer, buf.size); + _header.saveFiles[fileName] = buf; + debugC(1, kDebugLevelEventRec, "playback:action=\"Load save file\" filename=%s len=%d", fileName.c_str(), buf.size); + return true; +} + + + +RecorderEvent PlaybackFile::getNextEvent() { + assert(_mode == kRead); + if (isEventsBufferEmpty()) { + PlaybackFile::ChunkHeader header; + header.id = kFormatIdTag; + while (header.id != kEventTag) { + if (!readChunkHeader(header) || _readStream->eos()) { + break; + } + switch (header.id) { + case kEventTag: + readEventsToBuffer(header.len); + break; + case kScreenShotTag: + _readStream->seek(-4, SEEK_CUR); + header.len = _readStream->readUint32BE(); + _readStream->skip(header.len-8); + break; + case kMD5Tag: + checkRecordedMD5(); + break; + default: + _readStream->skip(header.len); + break; + } + } + } + RecorderEvent result; + readEvent(result); + return result; +} + +bool PlaybackFile::isEventsBufferEmpty() { + return (uint32)_tmpPlaybackFile.pos() == _eventsSize; +} + +void PlaybackFile::readEvent(RecorderEvent& event) { + event.recordedtype = (RecorderEventType)_tmpPlaybackFile.readByte(); + switch (event.recordedtype) { + case kRecorderEventTypeTimer: + event.time = _tmpPlaybackFile.readUint32LE(); + break; + case kRecorderEventTypeNormal: + event.type = (EventType)_tmpPlaybackFile.readUint32LE(); + switch (event.type) { + case EVENT_KEYDOWN: + case EVENT_KEYUP: + event.time = _tmpPlaybackFile.readUint32LE(); + event.kbd.keycode = (KeyCode)_tmpPlaybackFile.readSint32LE(); + event.kbd.ascii = _tmpPlaybackFile.readUint16LE(); + event.kbd.flags = _tmpPlaybackFile.readByte(); + break; + case EVENT_MOUSEMOVE: + case EVENT_LBUTTONDOWN: + case EVENT_LBUTTONUP: + case EVENT_RBUTTONDOWN: + case EVENT_RBUTTONUP: + case EVENT_WHEELUP: + case EVENT_WHEELDOWN: + case EVENT_MBUTTONDOWN: + case EVENT_MBUTTONUP: + event.time = _tmpPlaybackFile.readUint32LE(); + event.mouse.x = _tmpPlaybackFile.readSint16LE(); + event.mouse.y = _tmpPlaybackFile.readSint16LE(); + break; + default: + event.time = _tmpPlaybackFile.readUint32LE(); + break; + } + break; + } + event.synthetic = true; +} + +void PlaybackFile::readEventsToBuffer(uint32 size) { + _readStream->read(_tmpBuffer, size); + _tmpPlaybackFile.seek(0); + _eventsSize = size; +} + +void PlaybackFile::saveScreenShot(Graphics::Surface &screen, byte md5[16]) { + dumpRecordsToFile(); + _writeStream->writeUint32LE(kMD5Tag); + _writeStream->writeUint32LE(16); + _writeStream->write(md5, 16); + Graphics::saveThumbnail(*_writeStream, screen); +} + +void PlaybackFile::dumpRecordsToFile() { + if (!_headerDumped) { + dumpHeaderToFile(); + _headerDumped = true; + } + if (_recordCount == 0) { + return; + } + _writeStream->writeUint32LE(kEventTag); + _writeStream->writeUint32LE(_tmpRecordFile.pos()); + _writeStream->write(_tmpBuffer, _tmpRecordFile.pos()); + _tmpRecordFile.seek(0); + _recordCount = 0; +} + +void PlaybackFile::dumpHeaderToFile() { + _writeStream->writeUint32LE(kFormatIdTag); + // Specify size for first tag as NULL since we cannot calculate + // size of the file at time of the header dumping + _writeStream->writeUint32LE(0); + _writeStream->writeUint32LE(kVersionTag); + _writeStream->writeUint32LE(4); + _writeStream->writeUint32LE(RECORD_VERSION); + writeHeaderSection(); + writeGameHash(); + writeRandomRecords(); + writeGameSettings(); + writeSaveFilesSection(); +} + +void PlaybackFile::writeHeaderSection() { + uint32 headerSize = 0; + if (!_header.author.empty()) { + headerSize = _header.author.size() + 8; + } + if (!_header.notes.empty()) { + headerSize += _header.notes.size() + 8; + } + if (!_header.name.empty()) { + headerSize += _header.name.size() + 8; + } + if (headerSize == 0) { + return; + } + _writeStream->writeUint32LE(kHeaderSectionTag); + _writeStream->writeUint32LE(headerSize); + if (!_header.author.empty()) { + _writeStream->writeUint32LE(kAuthorTag); + _writeStream->writeUint32LE(_header.author.size()); + _writeStream->writeString(_header.author); + } + if (!_header.notes.empty()) { + _writeStream->writeUint32LE(kCommentsTag); + _writeStream->writeUint32LE(_header.notes.size()); + _writeStream->writeString(_header.notes); + } + if (!_header.name.empty()) { + _writeStream->writeUint32LE(kNameTag); + _writeStream->writeUint32LE(_header.name.size()); + _writeStream->writeString(_header.name); + } +} + +void PlaybackFile::writeGameHash() { + uint32 hashSectionSize = 0; + for (StringMap::iterator i = _header.hashRecords.begin(); i != _header.hashRecords.end(); ++i) { + hashSectionSize = hashSectionSize + i->_key.size() + i->_value.size() + 8; + } + if (_header.hashRecords.size() == 0) { + return; + } + _writeStream->writeUint32LE(kHashSectionTag); + _writeStream->writeUint32LE(hashSectionSize); + for (StringMap::iterator i = _header.hashRecords.begin(); i != _header.hashRecords.end(); ++i) { + _writeStream->writeUint32LE(kHashRecordTag); + _writeStream->writeUint32LE(i->_key.size() + i->_value.size()); + _writeStream->writeString(i->_key); + _writeStream->writeString(i->_value); + } +} + +void PlaybackFile::writeRandomRecords() { + uint32 randomSectionSize = 0; + for (RandomSeedsDictionary::iterator i = _header.randomSourceRecords.begin(); i != _header.randomSourceRecords.end(); ++i) { + randomSectionSize = randomSectionSize + i->_key.size() + 12; + } + if (_header.randomSourceRecords.size() == 0) { + return; + } + _writeStream->writeUint32LE(kRandomSectionTag); + _writeStream->writeUint32LE(randomSectionSize); + for (RandomSeedsDictionary::iterator i = _header.randomSourceRecords.begin(); i != _header.randomSourceRecords.end(); ++i) { + _writeStream->writeUint32LE(kRandomRecordTag); + _writeStream->writeUint32LE(i->_key.size() + 4); + _writeStream->writeString(i->_key); + _writeStream->writeUint32LE(i->_value); + } +} + +void PlaybackFile::writeEvent(const RecorderEvent &event) { + assert(_mode == kWrite); + _recordCount++; + _tmpRecordFile.writeByte(event.recordedtype); + switch (event.recordedtype) { + case kRecorderEventTypeTimer: + _tmpRecordFile.writeUint32LE(event.time); + break; + case kRecorderEventTypeNormal: + _tmpRecordFile.writeUint32LE((uint32)event.type); + switch(event.type) { + case EVENT_KEYDOWN: + case EVENT_KEYUP: + _tmpRecordFile.writeUint32LE(event.time); + _tmpRecordFile.writeSint32LE(event.kbd.keycode); + _tmpRecordFile.writeUint16LE(event.kbd.ascii); + _tmpRecordFile.writeByte(event.kbd.flags); + break; + case EVENT_MOUSEMOVE: + case EVENT_LBUTTONDOWN: + case EVENT_LBUTTONUP: + case EVENT_RBUTTONDOWN: + case EVENT_RBUTTONUP: + case EVENT_WHEELUP: + case EVENT_WHEELDOWN: + case EVENT_MBUTTONDOWN: + case EVENT_MBUTTONUP: + _tmpRecordFile.writeUint32LE(event.time); + _tmpRecordFile.writeSint16LE(event.mouse.x); + _tmpRecordFile.writeSint16LE(event.mouse.y); + break; + default: + _tmpRecordFile.writeUint32LE(event.time); + break; + } + break; + } + if (_recordCount == kMaxBufferedRecords) { + dumpRecordsToFile(); + } +} + +void PlaybackFile::writeGameSettings() { + _writeStream->writeUint32LE(kSettingsSectionTag); + uint32 settingsSectionSize = 0; + for (StringMap::iterator i = _header.settingsRecords.begin(); i != _header.settingsRecords.end(); ++i) { + settingsSectionSize += i->_key.size() + i->_value.size() + 24; + } + _writeStream->writeUint32LE(settingsSectionSize); + for (StringMap::iterator i = _header.settingsRecords.begin(); i != _header.settingsRecords.end(); ++i) { + _writeStream->writeUint32LE(kSettingsRecordTag); + _writeStream->writeUint32LE(i->_key.size() + i->_value.size() + 16); + _writeStream->writeUint32LE(kSettingsRecordKeyTag); + _writeStream->writeUint32LE(i->_key.size()); + _writeStream->writeString(i->_key); + _writeStream->writeUint32LE(kSettingsRecordValueTag); + _writeStream->writeUint32LE(i->_value.size()); + _writeStream->writeString(i->_value); + } +} + +int PlaybackFile::getScreensCount() { + if (_mode != kRead) { + return 0; + } + _readStream->seek(0); + int result = 0; + while (skipToNextScreenshot()) { + uint32 size = _readStream->readUint32BE(); + _readStream->skip(size-8); + ++result; + } + return result; +} + +bool PlaybackFile::skipToNextScreenshot() { + while (true) { + FileTag id = (FileTag)_readStream->readUint32LE(); + if (_readStream->eos()) { + break; + } + if (id == kScreenShotTag) { + return true; + } + else { + uint32 size = _readStream->readUint32LE(); + _readStream->skip(size); + } + } + return false; +} + +Graphics::Surface *PlaybackFile::getScreenShot(int number) { + if (_mode != kRead) { + return NULL; + } + _readStream->seek(0); + int screenCount = 1; + while (skipToNextScreenshot()) { + if (screenCount == number) { + screenCount++; + _readStream->seek(-4, SEEK_CUR); + return Graphics::loadThumbnail(*_readStream); + } else { + uint32 size = _readStream->readUint32BE(); + _readStream->skip(size-8); + screenCount++; + } + } + return NULL; +} + +void PlaybackFile::updateHeader() { + if (_mode == kWrite) { + _readStream = g_system->getSavefileManager()->openForLoading(_header.fileName); + } + _readStream->seek(0); + skipHeader(); + String tmpFilename = "_" + _header.fileName; + _writeStream = g_system->getSavefileManager()->openForSaving(tmpFilename); + dumpHeaderToFile(); + uint32 readedSize = 0; + do { + readedSize = _readStream->read(_tmpBuffer, kRecordBuffSize); + _writeStream->write(_tmpBuffer, readedSize); + } while (readedSize != 0); + delete _writeStream; + _writeStream = NULL; + delete _readStream; + _readStream = NULL; + g_system->getSavefileManager()->removeSavefile(_header.fileName); + g_system->getSavefileManager()->renameSavefile(tmpFilename, _header.fileName); + if (_mode == kRead) { + openRead(_header.fileName); + } +} + +void PlaybackFile::skipHeader() { + while (true) { + uint32 id = _readStream->readUint32LE(); + if (_readStream->eos()) { + break; + } + if ((id == kScreenShotTag) || (id == kEventTag) || (id == kMD5Tag)) { + _readStream->seek(-4, SEEK_CUR); + return; + } + else { + uint32 size = _readStream->readUint32LE(); + _readStream->skip(size); + } + } +} + +void PlaybackFile::addSaveFile(const String &fileName, InSaveFile *saveStream) { + uint oldPos = saveStream->pos(); + saveStream->seek(0); + _header.saveFiles[fileName].buffer = (byte *)malloc(saveStream->size()); + _header.saveFiles[fileName].size = saveStream->size(); + saveStream->read(_header.saveFiles[fileName].buffer, saveStream->size()); + saveStream->seek(oldPos); +} + +void PlaybackFile::writeSaveFilesSection() { + uint size = 0; + for (HashMap<String, SaveFileBuffer>::iterator i = _header.saveFiles.begin(); i != _header.saveFiles.end(); ++i) { + size += i->_value.size + i->_key.size() + 24; + } + if (size == 0) { + return; + } + _writeStream->writeSint32LE(kSaveTag); + _writeStream->writeSint32LE(size); + for (HashMap<String, SaveFileBuffer>::iterator i = _header.saveFiles.begin(); i != _header.saveFiles.end(); ++i) { + _writeStream->writeSint32LE(kSaveRecordTag); + _writeStream->writeSint32LE(i->_key.size() + i->_value.size + 16); + _writeStream->writeSint32LE(kSaveRecordNameTag); + _writeStream->writeSint32LE(i->_key.size()); + _writeStream->writeString(i->_key); + _writeStream->writeSint32LE(kSaveRecordBufferTag); + _writeStream->writeSint32LE(i->_value.size); + _writeStream->write(i->_value.buffer, i->_value.size); + } +} + + +void PlaybackFile::checkRecordedMD5() { + uint8 currentMD5[16]; + uint8 savedMD5[16]; + Graphics::Surface screen; + _readStream->read(savedMD5, 16); + if (!g_eventRec.grabScreenAndComputeMD5(screen, currentMD5)) { + return; + } + uint32 seconds = g_system->getMillis(true) / 1000; + String screenTime = String::format("%.2d:%.2d:%.2d", seconds / 3600 % 24, seconds / 60 % 60, seconds % 60); + if (memcmp(savedMD5, currentMD5, 16) != 0) { + debugC(1, kDebugLevelEventRec, "playback:action=\"Check screenshot\" time=%s result = fail", screenTime.c_str()); + warning("Recorded and current screenshots are different"); + } else { + debugC(1, kDebugLevelEventRec, "playback:action=\"Check screenshot\" time=%s result = success", screenTime.c_str()); + } + Graphics::saveThumbnail(*_screenshotsFile, screen); + screen.free(); +} + + +} diff --git a/common/recorderfile.h b/common/recorderfile.h new file mode 100644 index 0000000000..1c95e5a915 --- /dev/null +++ b/common/recorderfile.h @@ -0,0 +1,180 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef COMMON_RECORDERFILE_H +#define COMMON_RECORDERFILE_H + +#include "common/scummsys.h" +#include "common/events.h" +#include "common/mutex.h" +#include "common/memstream.h" +#include "common/config-manager.h" +#include "common/savefile.h" + +//capacity of records buffer +#define kMaxBufferedRecords 10000 +#define kRecordBuffSize sizeof(RecorderEvent) * kMaxBufferedRecords + +namespace Common { + +enum RecorderEventType { + kRecorderEventTypeNormal = 0, + kRecorderEventTypeTimer = 1 +}; + +struct RecorderEvent : Event { + RecorderEventType recordedtype; + uint32 time; +}; + + + +class PlaybackFile { + typedef HashMap<String, uint32, IgnoreCase_Hash, IgnoreCase_EqualTo> RandomSeedsDictionary; + enum fileMode { + kRead = 0, + kWrite = 1, + kClosed = 2 + }; + enum PlaybackFileState { + kFileStateCheckFormat, + kFileStateCheckVersion, + kFileStateProcessHash, + kFileStateProcessHeader, + kFileStateProcessRandom, + kFileStateSelectSection, + kFileStateProcessSettings, + kFileStateProcessSave, + kFileStateDone, + kFileStateError + }; + enum FileTag { + kFormatIdTag = MKTAG('P','B','C','K'), + kVersionTag = MKTAG('V','E','R','S'), + kHeaderSectionTag = MKTAG('H','E','A','D'), + kHashSectionTag = MKTAG('H','A','S','H'), + kRandomSectionTag = MKTAG('R','A','N','D'), + kEventTag = MKTAG('E','V','N','T'), + kScreenShotTag = MKTAG('B','M','H','T'), + kSettingsSectionTag = MKTAG('S','E','T','T'), + kAuthorTag = MKTAG('H','A','U','T'), + kCommentsTag = MKTAG('H','C','M','T'), + kNameTag = MKTAG('H','N','A','M'), + kHashRecordTag = MKTAG('H','R','C','D'), + kRandomRecordTag = MKTAG('R','R','C','D'), + kSettingsRecordTag = MKTAG('S','R','E','C'), + kSettingsRecordKeyTag = MKTAG('S','K','E','Y'), + kSettingsRecordValueTag = MKTAG('S','V','A','L'), + kSaveTag = MKTAG('S','A','V','E'), + kSaveRecordTag = MKTAG('R','S','A','V'), + kSaveRecordNameTag = MKTAG('S','N','A','M'), + kSaveRecordBufferTag = MKTAG('S','B','U','F'), + kMD5Tag = MKTAG('M','D','5',' ') + }; + struct ChunkHeader { + FileTag id; + uint32 len; + }; +public: + struct SaveFileBuffer { + byte *buffer; + uint32 size; + }; + struct PlaybackFileHeader { + String fileName; + String author; + String name; + String notes; + String description; + StringMap hashRecords; + StringMap settingsRecords; + HashMap<String, SaveFileBuffer> saveFiles; + RandomSeedsDictionary randomSourceRecords; + }; + PlaybackFile(); + ~PlaybackFile(); + + bool openWrite(const String &fileName); + bool openRead(const String &fileName); + void close(); + + RecorderEvent getNextEvent(); + void writeEvent(const RecorderEvent &event); + + void saveScreenShot(Graphics::Surface &screen, byte md5[16]); + Graphics::Surface *getScreenShot(int number); + int getScreensCount(); + + bool isEventsBufferEmpty(); + PlaybackFileHeader &getHeader() {return _header;} + void updateHeader(); + void addSaveFile(const String &fileName, InSaveFile *saveStream); +private: + WriteStream *_recordFile; + WriteStream *_writeStream; + WriteStream *_screenshotsFile; + MemoryReadStream _tmpPlaybackFile; + SeekableReadStream *_readStream; + SeekableMemoryWriteStream _tmpRecordFile; + + fileMode _mode; + bool _headerDumped; + int _recordCount; + uint32 _eventsSize; + byte _tmpBuffer[kRecordBuffSize]; + PlaybackFileHeader _header; + PlaybackFileState _playbackParseState; + + void skipHeader(); + bool parseHeader(); + bool processChunk(ChunkHeader &nextChunk); + void returnToChunkHeader(); + + bool readSaveRecord(); + void checkRecordedMD5(); + bool readChunkHeader(ChunkHeader &nextChunk); + void processRndSeedRecord(ChunkHeader chunk); + bool processSettingsRecord(); + + bool checkPlaybackFileVersion(); + + void dumpHeaderToFile(); + void writeSaveFilesSection(); + void writeGameSettings(); + void writeHeaderSection(); + void writeGameHash(); + void writeRandomRecords(); + + void dumpRecordsToFile(); + + String readString(int len); + void readHashMap(ChunkHeader chunk); + + bool skipToNextScreenshot(); + void readEvent(RecorderEvent& event); + void readEventsToBuffer(uint32 size); + bool grabScreenAndComputeMD5(Graphics::Surface &screen, uint8 md5[16]); +}; + +} // End of namespace Common + +#endif diff --git a/common/system.cpp b/common/system.cpp index 59210544ab..b40072afad 100644 --- a/common/system.cpp +++ b/common/system.cpp @@ -30,6 +30,7 @@ #include "common/taskbar.h" #include "common/updates.h" #include "common/textconsole.h" +#include "gui/EventRecorder.h" #include "backends/audiocd/default/default-audiocd.h" #include "backends/fs/fs-factory.h" @@ -84,7 +85,7 @@ void OSystem::initBackend() { error("Backend failed to instantiate audio CD manager"); if (!_eventManager) error("Backend failed to instantiate event manager"); - if (!_timerManager) + if (!getTimerManager()) error("Backend failed to instantiate timer manager"); // TODO: We currently don't check _savefileManager, because at least @@ -152,3 +153,11 @@ Common::String OSystem::getDefaultConfigFileName() { Common::String OSystem::getSystemLanguage() const { return "en_US"; } + +Common::TimerManager *OSystem::getTimerManager() { + return _timerManager; +} + +Common::SaveFileManager *OSystem::getSavefileManager() { + return g_eventRec.getSaveManager(_savefileManager); +} diff --git a/common/system.h b/common/system.h index 99b947d7f3..81c4bdf34e 100644 --- a/common/system.h +++ b/common/system.h @@ -890,8 +890,14 @@ public: /** @name Events and Time */ //@{ - /** Get the number of milliseconds since the program was started. */ - virtual uint32 getMillis() = 0; + /** Get the number of milliseconds since the program was started. + + @param skipRecord Skip recording of this value by event recorder. + This could be needed particularly when we are in + an on-screen GUI loop where player can pause + the recording. + */ + virtual uint32 getMillis(bool skipRecord = false) = 0; /** Delay/sleep for the specified amount of milliseconds. */ virtual void delayMillis(uint msecs) = 0; @@ -907,9 +913,7 @@ public: * Return the timer manager singleton. For more information, refer * to the TimerManager documentation. */ - inline Common::TimerManager *getTimerManager() { - return _timerManager; - } + virtual Common::TimerManager *getTimerManager(); /** * Return the event manager singleton. For more information, refer @@ -1086,9 +1090,7 @@ public: * and other modifiable persistent game data. For more information, * refer to the SaveFileManager documentation. */ - inline Common::SaveFileManager *getSavefileManager() { - return _savefileManager; - } + Common::SaveFileManager *getSavefileManager(); #if defined(USE_TASKBAR) /** @@ -138,9 +138,10 @@ _build_hq_scalers=yes _enable_prof=no _global_constructors=no _bink=yes -# Default vkeybd/keymapper options +# Default vkeybd/keymapper/eventrec options _vkeybd=no _keymapper=no +_eventrec=auto # GUI translation options _translation=yes # Default platform settings @@ -1036,6 +1037,8 @@ for ac_option in $@; do --disable-vkeybd) _vkeybd=no ;; --enable-keymapper) _keymapper=yes ;; --disable-keymapper) _keymapper=no ;; + --enable-eventrecorder) _eventrec=yes ;; + --disable-eventrecorder) _eventrec=no ;; --enable-text-console) _text_console=yes ;; --disable-text-console) _text_console=no ;; --with-fluidsynth-prefix=*) @@ -2161,7 +2164,7 @@ case $_host_os in # When not cross-compiling, enable large file support, but don't # care if getconf doesn't exist or doesn't recognize LFS_CFLAGS. if test -z "$_host"; then - CXXFLAGS="$CXXFLAGS $(getconf LFS_CFLAGS 2>/dev/null)" + CXXFLAGS="$CXXFLAGS `getconf LFS_CFLAGS 2>/dev/null`" fi ;; maemo) @@ -2860,6 +2863,20 @@ case $_backend in esac # +# Enable Event Recorder only for backends that support it +# +case $_backend in + sdl) + if test "$_eventrec" = auto ; then + _eventrec=yes + fi + ;; + *) + _eventrec=no + ;; +esac + +# # Disable savegame timestamp support for backends which don't have a reliable real time clock # case $_backend in @@ -3541,8 +3558,8 @@ if test "$_libunity" = auto ; then ;; *) # Unity has a lots of dependencies, update the libs and cflags var with them - LIBUNITY_LIBS="$LIBUNITY_LIBS $(pkg-config --libs unity = 3.8.4 2>> "$TMPLOG")" - LIBUNITY_CFLAGS="$LIBUNITY_CFLAGS $(pkg-config --cflags unity = 3.8.4 2>> "$TMPLOG")" + LIBUNITY_LIBS="$LIBUNITY_LIBS `pkg-config --libs unity = 3.8.4 2>> "$TMPLOG"`" + LIBUNITY_CFLAGS="$LIBUNITY_CFLAGS `pkg-config --cflags unity = 3.8.4 2>> "$TMPLOG"`" _libunity=no cat > $TMPC << EOF #include <unity.h> @@ -3775,10 +3792,11 @@ fi define_in_config_if_yes $_nasm 'USE_NASM' # -# Enable vkeybd / keymapper +# Enable vkeybd / keymapper / event recorder # define_in_config_if_yes $_vkeybd 'ENABLE_VKEYBD' define_in_config_if_yes $_keymapper 'ENABLE_KEYMAPPER' +define_in_config_if_yes $_eventrec 'ENABLE_EVENTRECORDER' # Check whether to build translation support # @@ -3924,7 +3942,11 @@ if test "$_vkeybd" = yes ; then fi if test "$_keymapper" = yes ; then - echo ", keymapper" + echo_n ", keymapper" +fi + +if test "$_eventrec" = yes ; then + echo ", event recorder" else echo fi diff --git a/devtools/create_project/create_project.cpp b/devtools/create_project/create_project.cpp index 84a6185eda..6b6e86a187 100644 --- a/devtools/create_project/create_project.cpp +++ b/devtools/create_project/create_project.cpp @@ -588,7 +588,7 @@ void displayHelp(const char *exe) { " Additionally there are the following switches for changing various settings:\n" "\n" "Project specific settings:\n" - " --codeblock build Code::Blocks project files\n" + " --codeblocks build Code::Blocks project files\n" " --msvc build Visual Studio project files\n" " --xcode build XCode project files\n" " --file-prefix prefix allow overwriting of relative file prefix in the\n" diff --git a/devtools/create_project/msbuild.cpp b/devtools/create_project/msbuild.cpp index dd9917e4ee..6af9323fcd 100644 --- a/devtools/create_project/msbuild.cpp +++ b/devtools/create_project/msbuild.cpp @@ -384,7 +384,7 @@ void MSBuildProvider::createBuildProp(const BuildSetup &setup, bool isRelease, b if (isRelease) { properties << "\t\t\t<IntrinsicFunctions>true</IntrinsicFunctions>\n" "\t\t\t<WholeProgramOptimization>true</WholeProgramOptimization>\n" - "\t\t\t<PreprocessorDefinitions>WIN32;RELEASE_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n" + "\t\t\t<PreprocessorDefinitions>WIN32;DISABLE_GUI_BUILTIN_THEME;RELEASE_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n" "\t\t\t<StringPooling>true</StringPooling>\n" "\t\t\t<BufferSecurityCheck>false</BufferSecurityCheck>\n" "\t\t\t<DebugInformationFormat></DebugInformationFormat>\n" @@ -396,7 +396,7 @@ void MSBuildProvider::createBuildProp(const BuildSetup &setup, bool isRelease, b "\t\t\t<SetChecksum>true</SetChecksum>\n"; } else { properties << "\t\t\t<Optimization>Disabled</Optimization>\n" - "\t\t\t<PreprocessorDefinitions>WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n" + "\t\t\t<PreprocessorDefinitions>WIN32;DISABLE_GUI_BUILTIN_THEME;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n" "\t\t\t<MinimalRebuild>true</MinimalRebuild>\n" "\t\t\t<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\n" "\t\t\t<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n" diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp index b1d1008b60..9023548c83 100644 --- a/engines/advancedDetector.cpp +++ b/engines/advancedDetector.cpp @@ -29,7 +29,7 @@ #include "common/system.h" #include "common/textconsole.h" #include "common/translation.h" - +#include "gui/EventRecorder.h" #include "engines/advancedDetector.h" #include "engines/obsolete.h" @@ -301,6 +301,7 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) return Common::kUserCanceled; debug(2, "Running %s", gameDescriptor.description().c_str()); + initSubSystems(agdDesc); if (!createInstance(syst, engine, agdDesc)) return Common::kNoGameDataFoundError; else @@ -606,3 +607,9 @@ AdvancedMetaEngine::AdvancedMetaEngine(const void *descs, uint descItemSize, con _maxScanDepth = 1; _directoryGlobs = NULL; } + +void AdvancedMetaEngine::initSubSystems(const ADGameDescription *gameDesc) const { + if (gameDesc) { + g_eventRec.processGameDescription(gameDesc); + } +} diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h index 3eec33abe5..71d2c4a446 100644 --- a/engines/advancedDetector.h +++ b/engines/advancedDetector.h @@ -280,6 +280,9 @@ protected: return 0; } +private: + void initSubSystems(const ADGameDescription *gameDesc) const; + protected: /** * Detect games in specified directory. diff --git a/engines/cge/cge.cpp b/engines/cge/cge.cpp index 6cc0c45963..af7e91f7eb 100644 --- a/engines/cge/cge.cpp +++ b/engines/cge/cge.cpp @@ -25,7 +25,6 @@ #include "common/debug.h" #include "common/debug-channels.h" #include "common/error.h" -#include "common/EventRecorder.h" #include "common/file.h" #include "common/fs.h" #include "engines/advancedDetector.h" diff --git a/engines/dreamweb/dreamweb.cpp b/engines/dreamweb/dreamweb.cpp index c3ede46df2..08838a784a 100644 --- a/engines/dreamweb/dreamweb.cpp +++ b/engines/dreamweb/dreamweb.cpp @@ -23,7 +23,6 @@ #include "common/config-manager.h" #include "common/debug-channels.h" #include "common/events.h" -#include "common/EventRecorder.h" #include "common/file.h" #include "common/func.h" #include "common/system.h" diff --git a/engines/neverhood/console.cpp b/engines/neverhood/console.cpp index a4dd50ce4b..e676da3727 100644 --- a/engines/neverhood/console.cpp +++ b/engines/neverhood/console.cpp @@ -25,6 +25,7 @@ #include "neverhood/neverhood.h" #include "neverhood/gamemodule.h" #include "neverhood/scene.h" +#include "neverhood/sound.h" #include "neverhood/modules/module1600.h" namespace Neverhood { @@ -34,6 +35,7 @@ Console::Console(NeverhoodEngine *vm) : GUI::Debugger(), _vm(vm) { DCmd_Register("dumpvars", WRAP_METHOD(Console, Cmd_Dumpvars)); DCmd_Register("room", WRAP_METHOD(Console, Cmd_Room)); DCmd_Register("surfaces", WRAP_METHOD(Console, Cmd_Surfaces)); + DCmd_Register("playsound", WRAP_METHOD(Console, Cmd_PlaySound)); } Console::~Console() { @@ -169,4 +171,21 @@ bool Console::Cmd_Dumpvars(int argc, const char **argv) { return true; } +bool Console::Cmd_PlaySound(int argc, const char **argv) { + if (argc < 2) { + DebugPrintf("Usage: %s <sound hash>\n", argv[0]); + } else { + uint32 soundHash = strtol(argv[1], NULL, 0); + AudioResourceManSoundItem *soundItem = new AudioResourceManSoundItem(_vm, soundHash); + soundItem->setVolume(100); + soundItem->playSound(false); + while (soundItem->isPlaying()) { + _vm->_system->delayMillis(10); + } + delete soundItem; + } + + return true; +} + } // End of namespace Neverhood diff --git a/engines/neverhood/console.h b/engines/neverhood/console.h index 40c11b50e3..62d65bd693 100644 --- a/engines/neverhood/console.h +++ b/engines/neverhood/console.h @@ -41,6 +41,7 @@ private: bool Cmd_Surfaces(int argc, const char **argv); bool Cmd_Cheat(int argc, const char **argv); bool Cmd_Dumpvars(int argc, const char **argv); + bool Cmd_PlaySound(int argc, const char **argv); }; } // End of namespace Neverhood diff --git a/engines/neverhood/menumodule.cpp b/engines/neverhood/menumodule.cpp index 8814401f5e..da7abd41e1 100644 --- a/engines/neverhood/menumodule.cpp +++ b/engines/neverhood/menumodule.cpp @@ -766,9 +766,7 @@ void SavegameListBox::onClick() { mousePos.y -= _y + _rect.y1; if (mousePos.x >= 0 && mousePos.x <= _rect.x2 - _rect.x1 && mousePos.y >= 0 && mousePos.y <= _rect.y2 - _rect.y1) { - // We add 1 to the char height to ensure that the correct entry is chosen if the - // user clicks at the bottom the text entry - int newIndex = _firstVisibleItem + mousePos.y / (_fontSurface->getCharHeight() + 1); + int newIndex = _firstVisibleItem + mousePos.y / _fontSurface->getCharHeight(); if (newIndex <= _lastVisibleItem) { _currIndex = newIndex; refresh(); diff --git a/engines/neverhood/modules/module2700.cpp b/engines/neverhood/modules/module2700.cpp index 19655d128b..e9ea10bd16 100644 --- a/engines/neverhood/modules/module2700.cpp +++ b/engines/neverhood/modules/module2700.cpp @@ -83,7 +83,7 @@ static const uint32 kScene2725StaticSprites[] = { }; Module2700::Module2700(NeverhoodEngine *vm, Module *parentModule, int which) - : Module(vm, parentModule), _soundIndex(0), _raidoMusicInitialized(false) { + : Module(vm, parentModule), _soundIndex(0), _radioMusicInitialized(false) { _vm->_soundMan->addMusic(0x42212411, 0x04020210); _vm->_soundMan->startMusic(0x04020210, 24, 2); @@ -500,7 +500,7 @@ void Module2700::updateScene() { } else { switch (_sceneNum) { case 21: - if (!_raidoMusicInitialized) { + if (!_radioMusicInitialized) { _vm->_soundMan->stopMusic(0x04020210, 0, 1); _vm->gameModule()->initRadioPuzzle(); _musicFileHash = getGlobalVar(V_GOOD_RADIO_MUSIC_NAME); @@ -508,7 +508,7 @@ void Module2700::updateScene() { _vm->_soundMan->startMusic(_musicFileHash, 0, 2); _vm->_soundMan->addSound(0x42212411, 0x44014282); _vm->_soundMan->setSoundParams(0x44014282, true, 120, 360, 72, 0); - _raidoMusicInitialized = true; + _radioMusicInitialized = true; } break; } diff --git a/engines/neverhood/modules/module2700.h b/engines/neverhood/modules/module2700.h index 003666bb7f..158bb609e9 100644 --- a/engines/neverhood/modules/module2700.h +++ b/engines/neverhood/modules/module2700.h @@ -39,7 +39,7 @@ public: protected: int _sceneNum; int _soundIndex; - bool _raidoMusicInitialized; + bool _radioMusicInitialized; uint32 _scene2711StaticSprites[6]; uint32 _musicFileHash; void createScene(int sceneNum, int which); diff --git a/engines/neverhood/todo.txt b/engines/neverhood/todo.txt deleted file mode 100644 index 9d781e06ec..0000000000 --- a/engines/neverhood/todo.txt +++ /dev/null @@ -1,45 +0,0 @@ -NOTE: -------- -Some of the TODOs should be done AFTER the whole game logic is implemented -else the game disasm and reimplemtation code become even more different -(unless I decide it's ok to do it :) - -TODOs which can be done any time: ------------------------------------ -- Cleanup -- Clean up staticdata structs to look more like the ones in create_neverhood - (e.g. by using template classes etc.) - - Or use a common base class and manage all stuff in one single table and cast stuff accordingly - -TODOs which should be done only after the game logic is finished: -------------------------------------------------------------------- -- Maybe rework organization of files (e.g. put ALL Sprites into one separate file, same with Modules and Scenes) - - This would solve the problem of how to organize stuff which is used several times, and less headers would have to be included - - The move special scenes (SmackerScene) into the scenes file - -DONE: -------- -- Implement game menus -- Rework sound system (I don't like that SoundResources need to be explicitly initialized in Scene constructors) - - Should be just a handle object which initializes itself - - Play routine should fill the handle so it can be stopped/queried later - - Basically like ScummVM own sound handles -- RE and implement yet unknown music/sound stuff -- Implement clever sprite redrawing code (dirty rectangles, microtiles etc.), only redraw what's neccessary -- Rework the resource system - - The current system can be simplified a lot - - Also resource purging needs to be implemented -- Maybe merge CollisionMan with Scene (since it's so far never used independently) -- Give placeholder stuff (e.g. sub?????, _flag??? etc.) better fitting names -- Use CursorMan for the mouse cursor (instead of using it like a normal sprite) - - This whould make it neccessary to call _system->updateScreen more often else - the mouse movement would be choppy - -TODOs which are experimental: -------------------------------- -NOTE: Since they affect the whole game, they really should be only implemented once the full game logic is implemented. -These are nothing more than wild ideas for now, any might never be implemented. -- Use states instead of separate callback methods -- Try to move more stuff to neverhood.dat -- Try to use more template functions instead of manually creating functions - (Can be coupled with the above to move parameters to the dat and only use IDs) diff --git a/engines/sword25/gfx/image/renderedimage.cpp b/engines/sword25/gfx/image/renderedimage.cpp index c8a6666046..81a29c727f 100644 --- a/engines/sword25/gfx/image/renderedimage.cpp +++ b/engines/sword25/gfx/image/renderedimage.cpp @@ -41,6 +41,7 @@ #include "sword25/gfx/renderobjectmanager.h" #include "common/system.h" +#include "graphics/thumbnail.h" namespace Sword25 { @@ -509,60 +510,4 @@ void RenderedImage::checkForTransparency() { } } -/** - * Scales a passed surface, creating a new surface with the result - * @param srcImage Source image to scale - * @param scaleFactor Scale amount. Must be between 0 and 1.0 (but not zero) - * @remarks Caller is responsible for freeing the returned surface - */ -Graphics::Surface *RenderedImage::scale(const Graphics::Surface &srcImage, int xSize, int ySize) { - Graphics::Surface *s = new Graphics::Surface(); - s->create(xSize, ySize, srcImage.format); - - int *horizUsage = scaleLine(xSize, srcImage.w); - int *vertUsage = scaleLine(ySize, srcImage.h); - - // Loop to create scaled version - for (int yp = 0; yp < ySize; ++yp) { - const byte *srcP = (const byte *)srcImage.getBasePtr(0, vertUsage[yp]); - byte *destP = (byte *)s->getBasePtr(0, yp); - - for (int xp = 0; xp < xSize; ++xp) { - const byte *tempSrcP = srcP + (horizUsage[xp] * srcImage.format.bytesPerPixel); - for (int byteCtr = 0; byteCtr < srcImage.format.bytesPerPixel; ++byteCtr) { - *destP++ = *tempSrcP++; - } - } - } - - // Delete arrays and return surface - delete[] horizUsage; - delete[] vertUsage; - return s; -} - -/** - * Returns an array indicating which pixels of a source image horizontally or vertically get - * included in a scaled image - */ -int *RenderedImage::scaleLine(int size, int srcSize) { - int scale = 100 * size / srcSize; - assert(scale > 0); - int *v = new int[size]; - Common::fill(v, &v[size], 0); - - int distCtr = 0; - int *destP = v; - for (int distIndex = 0; distIndex < srcSize; ++distIndex) { - distCtr += scale; - while (distCtr >= 100) { - assert(destP < &v[size]); - *destP++ = distIndex; - distCtr -= 100; - } - } - - return v; -} - } // End of namespace Sword25 diff --git a/engines/sword25/gfx/image/renderedimage.h b/engines/sword25/gfx/image/renderedimage.h index a25b258592..116f97de26 100644 --- a/engines/sword25/gfx/image/renderedimage.h +++ b/engines/sword25/gfx/image/renderedimage.h @@ -104,8 +104,6 @@ public: return true; } - static Graphics::Surface *scale(const Graphics::Surface &srcImage, int xSize, int ySize); - void setIsTransparent(bool isTransparent) { _isTransparent = isTransparent; } virtual bool isSolid() const { return !_isTransparent; } @@ -119,7 +117,6 @@ private: Graphics::Surface *_backSurface; void checkForTransparency(); - static int *scaleLine(int size, int srcSize); }; } // End of namespace Sword25 diff --git a/engines/tsage/core.cpp b/engines/tsage/core.cpp index 4a90e23a33..131b70c4bd 100644 --- a/engines/tsage/core.cpp +++ b/engines/tsage/core.cpp @@ -3157,6 +3157,8 @@ void Visage::setVisage(int resNum, int rlbNum) { _flipHoriz = flags & 1; _data = g_resourceManager->getResource(RES_VISAGE, resNum, rlbNum); + + DEALLOCATE(indexData); } } diff --git a/engines/tsage/graphics.cpp b/engines/tsage/graphics.cpp index fb0b0b0cbb..af6e6f9b13 100644 --- a/engines/tsage/graphics.cpp +++ b/engines/tsage/graphics.cpp @@ -234,10 +234,15 @@ GfxSurface::GfxSurface(const GfxSurface &s) { } GfxSurface::~GfxSurface() { + clear(); +} + +void GfxSurface::clear() { if (_customSurface) { _customSurface->free(); delete _customSurface; - } + _customSurface = NULL; + } } /** @@ -1322,6 +1327,12 @@ void GfxManager::copyFrom(GfxSurface &src, int destX, int destY) { _surface.copyFrom(src, destX, destY); } +void GfxManager::copyFrom(GfxSurface &src, const Rect &srcBounds, const Rect &destBounds) { + _surface.setBounds(_bounds); + + _surface.copyFrom(src, srcBounds, destBounds); +} + /*--------------------------------------------------------------------------*/ diff --git a/engines/tsage/graphics.h b/engines/tsage/graphics.h index 826f2fef6f..7239a99a68 100644 --- a/engines/tsage/graphics.h +++ b/engines/tsage/graphics.h @@ -100,6 +100,7 @@ public: void unlockSurface(); void synchronize(Serializer &s); void create(int width, int height); + void clear(); void setBounds(const Rect &bounds) { _bounds = bounds; } const Rect &getBounds() const { return _bounds; } @@ -302,6 +303,7 @@ public: } void copyFrom(GfxSurface &src, Rect destBounds, Region *priorityRegion = NULL); void copyFrom(GfxSurface &src, int destX, int destY); + void copyFrom(GfxSurface &src, const Rect &srcBounds, const Rect &destBounds); GfxSurface &getSurface() { _surface.setBounds(_bounds); diff --git a/engines/tsage/ringworld2/ringworld2_logic.cpp b/engines/tsage/ringworld2/ringworld2_logic.cpp index 97042cb621..86014fb9c1 100644 --- a/engines/tsage/ringworld2/ringworld2_logic.cpp +++ b/engines/tsage/ringworld2/ringworld2_logic.cpp @@ -21,6 +21,8 @@ */ #include "common/config-manager.h" +#include "common/rect.h" +#include "tsage/graphics.h" #include "tsage/scenes.h" #include "tsage/tsage.h" #include "tsage/staticres.h" @@ -166,7 +168,7 @@ Scene *Ringworld2Game::createScene(int sceneNumber) { // Ice Maze: Large empty room return new Scene2400(); case 2425: - // Ice Maze: + // Ice Maze: The Hall of Records return new Scene2425(); case 2430: // Ice Maze: Bedroom @@ -213,7 +215,7 @@ Scene *Ringworld2Game::createScene(int sceneNumber) { case 2900: error("Missing scene %d from group 2", sceneNumber); /* Scene group #3 */ - // + // ARM Base Hanager case 3100: return new Scene3100(); case 3125: @@ -258,16 +260,22 @@ Scene *Ringworld2Game::createScene(int sceneNumber) { // Cutscene - Ship landing return new Scene3350(); case 3375: + // Outer walkway return new Scene3375(); case 3385: + // Corridor return new Scene3385(); case 3395: + // Walkway return new Scene3395(); case 3400: + // Confrontation return new Scene3400(); case 3500: + // Maze action sequencec return new Scene3500(); case 3600: + // Cutscene - walking at gunpoint return new Scene3600(); case 3700: // Cutscene - Teleport outside @@ -1268,287 +1276,202 @@ void SceneAreaObject::setDetails(int resNum, int lookLineNum, int talkLineNum, i /*****************************************************************************/ -UnkObject1200::UnkObject1200() { - _field16 = _field3A = NULL; - _field12 = _field14 = 0; - _field26 = _field28 = _field2A = _field2C = _field2E = _field30 = 0; - _field32 = _field34 = _field36 = _field38 = _field3E = _field40 = 0; +MazeUI::MazeUI() { + _mapData = NULL; + _cellsVisible.x = _cellsVisible.y = 0; + _mapCells.x = _mapCells.y = 0; + _cellSize.x = _cellSize.y = 0; + _mapOffset.x = _mapOffset.y = 0; + _resNum = _cellsResNum = 0; + _frameCount = _resCount = _mapImagePitch = _unused = 0; } -void UnkObject1200::synchronize(Serializer &s) { - SavedObject::synchronize(s); +MazeUI::~MazeUI() { + DEALLOCATE(_mapData); +} - _rect1.synchronize(s); - _rect2.synchronize(s); +void MazeUI::synchronize(Serializer &s) { + SavedObject::synchronize(s); - // FIXME: syncrhonize _field16 and _field3A + s.syncAsSint16LE(_resNum); + if (s.isLoading()) + load(_resNum); - s.syncAsSint16LE(_field12); - s.syncAsSint16LE(_field14); - s.syncAsSint16LE(_field26); - s.syncAsSint16LE(_field28); - s.syncAsSint16LE(_field2A); - s.syncAsSint16LE(_field2C); - s.syncAsSint16LE(_field2E); - s.syncAsSint16LE(_field30); - s.syncAsSint16LE(_field32); - s.syncAsSint16LE(_field34); - s.syncAsSint16LE(_field36); - s.syncAsSint16LE(_field38); - s.syncAsSint16LE(_field3E); - s.syncAsSint16LE(_field40); + s.syncAsSint16LE(_mapOffset.x); + s.syncAsSint16LE(_mapOffset.y); + s.syncAsSint16LE(_unused); } -void UnkObject1200::sub51AE9(int arg1) { - warning("STUB: UnkObject1200::sub51AE9()"); -} +void MazeUI::load(int resNum) { + clear(); + _resNum = resNum; -int UnkObject1200::sub51AF8(Common::Point pt) { - if (!_rect1.contains(pt)) - return -1; + const byte *header = g_resourceManager->getResource(RT17, resNum, 0); - int tmp1 = (pt.x - _rect1.left + _field2E) / _field2A; - int tmp2 = (pt.y - _rect1.top + _field30) / _field2C; + _cellsResNum = resNum + 1000; + _mapCells.x = READ_LE_UINT16(header + 2); + _mapCells.y = READ_LE_UINT16(header + 4); + _frameCount = 10; + _resCount = _frameCount << 3; - if ((tmp1 >= 0) && (tmp2 >= 0) && (_field26 > tmp1) && (_field28 > tmp2)) - return _field16[(((_field26 * tmp2) + tmp1)* 2)]; + Visage visage; + visage.setVisage(_cellsResNum, 1); - return -1; + GfxSurface frame = visage.getFrame(2); + _cellSize.x = frame.getBounds().width(); + _cellSize.y = frame.getBounds().height(); + + _mapData = g_resourceManager->getResource(RT17, resNum, 1); + + _mapOffset.y = _mapOffset.x = 0; + _cellsVisible.x = (_displayBounds.width() + _cellSize.x - 1) / _cellSize.x; + _cellsVisible.y = (_displayBounds.height() + _cellSize.y - 1) / _cellSize.y; + + _mapImagePitch = (_cellsVisible.x + 1) * _cellSize.x; + _mapImage.create(_mapImagePitch, _cellSize.y); + + _mapBounds = Rect(0, 0, _cellSize.x * _mapCells.x, _cellSize.y * _mapCells.y); +} + +void MazeUI::clear() { + if (!_resNum) + _resNum = 1; + + if (_mapData) + DEALLOCATE(_mapData); + _mapData = NULL; + + _mapImage.clear(); } -bool UnkObject1200::sub51AFD(Common::Point pt) { - int retval = false; +bool MazeUI::setMazePosition(const Common::Point &pt) { + bool retval = false; - _field2E = pt.x; - _field30 = pt.y; + _mapOffset = pt; - if (_field2E < _rect2.top) { - _field2E = _rect2.top; + if (_mapOffset.x < _mapBounds.top) { + _mapOffset.x = _mapBounds.top; retval = true; } - if (_field30 < _rect2.left) { - _field30 = _rect2.left; + if (_mapOffset.y < _mapBounds.left) { + _mapOffset.y = _mapBounds.left; retval = true; } - if (_field2E + _rect1.width() > _rect2.right) { - _field2E = _rect2.right - _rect1.width(); + if (_mapOffset.x + _displayBounds.width() > _mapBounds.right) { + _mapOffset.x = _mapBounds.right - _displayBounds.width(); retval = true; } - if (_field30 + _rect1.height() > _rect2.bottom) { - _field30 = _rect2.bottom - _rect1.height(); + if (_mapOffset.y + _displayBounds.height() > _mapBounds.bottom) { + _mapOffset.y = _mapBounds.bottom - _displayBounds.height(); retval = true; } return retval; } -void UnkObject1200::sub51B02() { - warning("STUB: UnkObject1200::sub51B02()"); -} +void MazeUI::draw() { + int yPos = 0; + int ySize; + Visage visage; -void UnkObject1200::sub9EDE8(Rect rect) { - _rect1 = rect; - warning("FIXME: UnkObject1200::sub9EDE8()"); -// _rect1.clip(g_globals->gfxManager()._bounds); -} + _cellsVisible.y = ((_mapOffset.y % _cellSize.y) + _displayBounds.height() + + (_cellSize.y - 1)) / _cellSize.y; -int UnkObject1200::sub9EE22(int &arg1, int &arg2) { - arg1 /= _field2A; - arg2 /= _field2C; + // Loop to handle the cell rows of the visible display area one at a time + for (int yCtr = 0; yCtr < _cellsVisible.y; ++yCtr, yPos += ySize) { + int cellY = _mapOffset.y / _cellSize.y + yCtr; + + // Loop to iterate through the horizontal visible cells to build up + // an entire cell high horizontal slice of the map + for (int xCtr = 0; xCtr < _cellsVisible.x; ++xCtr) { + int cellX = _mapOffset.x / _cellSize.x + xCtr; - if ((arg1 >= 0) && (arg2 >= 0) && (_field26 > arg1) && (_field28 > arg2)) { - return _field16[(((_field26 * arg2) + arg1) * 2)]; - } + // Get the type of content to display in the cell + int cell = getCellFromCellXY(Common::Point(cellX, cellY)) - 1; + if (cell >= 0) { + int frameNum = (cell % _frameCount) + 1; + int rlbNum = (cell % _resCount) / _frameCount + 1; + int resNum = _cellsResNum + (cell / _resCount); - return -1; -} + visage.setVisage(resNum, rlbNum); + GfxSurface frame = visage.getFrame(frameNum); -void Scene1200::sub9DAD6(int indx) { - _object1.sub9EE22(R2_GLOBALS._v56AA2, R2_GLOBALS._v56AA4); + _mapImage.copyFrom(frame, xCtr * _cellSize.x, 0); + } else { + GfxSurface emptyRect; + emptyRect.create(_cellSize.x, _cellSize.y); - switch (indx) { - case 0: - if ( ((_object1.sub51AF8(Common::Point(200, 50)) > 36) || (_object1.sub51AF8(Common::Point(200, 88)) > 36)) - && ( ((R2_GLOBALS._v56AA2 == 3) && (R2_GLOBALS._v56AA4 == 33) && (_field418 != 4)) - || ((R2_GLOBALS._v56AA2 == 13) && (R2_GLOBALS._v56AA4 == 21) && (_field418 != 2)) - || ((R2_GLOBALS._v56AA2 == 29) && (R2_GLOBALS._v56AA4 == 17) && (_field418 != 1)) - || ((R2_GLOBALS._v56AA2 == 33) && (R2_GLOBALS._v56AA4 == 41)) ) - ) { - R2_GLOBALS._player.disableControl(); - _sceneMode = 1200; - setAction(&_sequenceManager, this, 1200, &_actor1, NULL); - } else if (_object1.sub51AF8(Common::Point(200, 69)) == 36) { - switch (_field412 - 1) { - case 0: - if (R2_GLOBALS._player._visage == 3155) - _sceneMode = 15; - else - _sceneMode = 10; - break; - case 1: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 76; - else - _sceneMode = 75; - break; - case 2: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 101; - else - _sceneMode = 100; - break; - case 3: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 111; - else - _sceneMode = 110; - break; - default: - break; + _mapImage.copyFrom(emptyRect, xCtr * _cellSize.x, 0); } - R2_GLOBALS._player.disableControl(); - _field412 = 1; - signal(); } - break; - case 1: - if ( ((_object1.sub51AF8(Common::Point(120, 50)) > 36) || (_object1.sub51AF8(Common::Point(120, 88)) > 36)) - && ( ((R2_GLOBALS._v56AA2 == 7) && (R2_GLOBALS._v56AA4 == 33) && (_field418 != 4)) - || ((R2_GLOBALS._v56AA2 == 17) && (R2_GLOBALS._v56AA4 == 21) && (_field418 != 2)) - || ((R2_GLOBALS._v56AA2 == 33) && (R2_GLOBALS._v56AA4 == 17) && (_field418 != 1)) - || ((R2_GLOBALS._v56AA2 == 5) && (R2_GLOBALS._v56AA4 == 5)) ) - ) { - R2_GLOBALS._player.disableControl(); - _sceneMode = 1201; - setAction(&_sequenceManager, this, 1201, &_actor1, NULL); - } else if (_object1.sub51AF8(Common::Point(120, 69)) == 36) { - switch (_field412 - 1) { - case 0: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 56; - else - _sceneMode = 55; - break; - case 1: - if (R2_GLOBALS._player._visage == 3155) - _sceneMode = 25; - else - _sceneMode = 20; - break; - case 2: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 91; - else - _sceneMode = 90; - break; - case 3: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 121; - else - _sceneMode = 120; - break; - default: - break; - } - R2_GLOBALS._player.disableControl(); - _field412 = 2; - signal(); - } - break; - case 2: - if ( ((_object1.sub51AF8(Common::Point(140, 110)) > 36) || (_object1.sub51AF8(Common::Point(178, 110)) > 36)) - && ( ((R2_GLOBALS._v56AA2 == 17) && (R2_GLOBALS._v56AA4 == 5) && (_field418 != 3)) - || ((R2_GLOBALS._v56AA2 == 41) && (R2_GLOBALS._v56AA4 == 21)) ) - ) { - R2_GLOBALS._player.disableControl(); - _sceneMode = 1203; - setAction(&_sequenceManager, this, 1203, &_actor1, NULL); - } else if (_object1.sub51AF8(Common::Point(160, 110)) == 36) { - switch (_field412 - 1) { - case 0: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 51; - else - _sceneMode = 50; - break; - case 1: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 81; - else - _sceneMode = 80; - break; - case 2: - if (R2_GLOBALS._player._visage == 3155) - _sceneMode = 35; - else - _sceneMode = 30; - break; - case 3: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 116; - else - _sceneMode = 115; - break; - default: - break; - } - R2_GLOBALS._player.disableControl(); - _field412 = 3; - signal(); - } - break; - case 3: - if ( ((_object1.sub51AF8(Common::Point(140, 30)) > 36) || (_object1.sub51AF8(Common::Point(178, 30)) > 36)) - && ( ((R2_GLOBALS._v56AA2 == 17) && (R2_GLOBALS._v56AA4 == 9) && (_field418 != 3)) - || ((R2_GLOBALS._v56AA2 == 35) && (R2_GLOBALS._v56AA4 == 17)) ) - ) { - R2_GLOBALS._player.disableControl(); - _sceneMode = 1202; - setAction(&_sequenceManager, this, 1202, &_actor1, NULL); - } else if (_object1.sub51AF8(Common::Point(160, 30)) == 36) { - switch (_field412 - 1) { - case 0: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 61; - else - _sceneMode = 60; - break; - case 1: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 71; - else - _sceneMode = 70; - break; - case 2: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 96; - else - _sceneMode = 95; - break; - case 3: - if (R2_GLOBALS._player._visage == 3155) - _sceneMode = 45; - else - _sceneMode = 40; - break; - default: - _sceneMode = 1; - R2_GLOBALS._player.setup(3156, 4, 6); - break; + + if (yPos == 0) { + // First line of the map to be displayed - only the bottom portion of that + // first cell row may be visible + yPos = _displayBounds.top; + ySize = _cellSize.y - (_mapOffset.y % _cellSize.y); + + Rect srcBounds(_mapOffset.x % _cellSize.x, _mapOffset.y % _cellSize.y, + (_mapOffset.x % _cellSize.x) + _displayBounds.width(), _cellSize.y); + Rect destBounds(_displayBounds.left, yPos, _displayBounds.right, yPos + ySize); + + R2_GLOBALS.gfxManager().copyFrom(_mapImage, srcBounds, destBounds); + } else { + if ((yPos + _cellSize.y) < _displayBounds.bottom) { + ySize = _cellSize.y; + } else { + ySize = _displayBounds.bottom - yPos; } - R2_GLOBALS._player.disableControl(); - _field412 = 4; - signal(); + + Rect srcBounds(_mapOffset.x % _cellSize.x, 0, + (_mapOffset.x % _cellSize.x) + _displayBounds.width(), ySize); + Rect destBounds(_displayBounds.left, yPos, _displayBounds.right, yPos + ySize); + + R2_GLOBALS.gfxManager().copyFrom(_mapImage, srcBounds, destBounds); } - break; - default: - break; } } +int MazeUI::getCellFromPixelXY(const Common::Point &pt) { + if (!_displayBounds.contains(pt)) + return -1; + + int cellX = (pt.x - _displayBounds.left + _mapOffset.x) / _cellSize.x; + int cellY = (pt.y - _displayBounds.top + _mapOffset.y) / _cellSize.y; + + if ((cellX >= 0) && (cellY >= 0) && (cellX < _mapCells.x) && (cellY < _mapCells.y)) + return (int16)READ_LE_UINT16(_mapData + (_mapCells.x * cellY + cellX) * 2); + + return -1; +} + +int MazeUI::getCellFromCellXY(const Common::Point &p) { + if (p.x < 0 || p.y < 0 || p.x >= _mapCells.x || p.y >= _mapCells.y) { + return -1; + } else { + return (int16)READ_LE_UINT16(_mapData + (_mapCells.x * p.y + p.x) * 2); + } +} + +int MazeUI::pixelToCellXY(int &x, int &y) { + x /= _cellSize.x; + y /= _cellSize.y; + + if ((x >= 0) && (y >= 0) && (_mapCells.x > x) && (_mapCells.y > y)) { + return (int16)READ_LE_UINT16(_mapData + (_mapCells.x * y + x) * 2); + } + + return -1; +} + +void MazeUI::setDisplayBounds(const Rect &r) { + _displayBounds = r; + _displayBounds.clip(g_globals->gfxManager()._bounds); +} + /*--------------------------------------------------------------------------*/ void AnimationSlice::load(Common::File &f) { diff --git a/engines/tsage/ringworld2/ringworld2_logic.h b/engines/tsage/ringworld2/ringworld2_logic.h index 0b573bf7f0..576482b3eb 100644 --- a/engines/tsage/ringworld2/ringworld2_logic.h +++ b/engines/tsage/ringworld2/ringworld2_logic.h @@ -276,6 +276,49 @@ public: } }; +enum MazeDirection { MAZEDIR_NONE = 0, MAZEDIR_NORTH = 1, MAZEDIR_NORTHEAST = 2, MAZEDIR_EAST = 3, + MAZEDIR_SOUTHEAST = 4, MAZEDIR_SOUTH = 5, MAZEDIR_SOUTHWEST = 6, MAZEDIR_WEST = 7, + MAZEDIR_NORTHWEST = 8 }; + +class MazeUI: public SavedObject { +private: + void clear(); +public: + // Position on screen to show map + Rect _displayBounds; + // The dimensions (in cells) of the entire maze map + Rect _mapBounds; + + // Encoded cell map specifying the features of the maze + byte *_mapData; + // Image surface used to store a line of the map for rendering + GfxSurface _mapImage; + + Common::Point _cellsVisible; + Common::Point _mapCells; + Common::Point _cellSize; + Common::Point _mapOffset; + int _resNum; + int _cellsResNum; + int _frameCount; + int _resCount; + int _mapImagePitch; + int _unused; +public: + MazeUI(); + virtual ~MazeUI(); + void setDisplayBounds(const Rect &r); + bool setMazePosition(const Common::Point &pt); + void load(int resNum); + void draw(); + int getCellFromPixelXY(const Common::Point &pt); + int getCellFromCellXY(const Common::Point &p); + int pixelToCellXY(int &arg1, int &arg2); + + virtual Common::String getClassName() { return "MazeUI"; } + void synchronize(Serializer &s); +}; + class SceneAreaObject: public SceneArea { class Object1: public SceneActor { public: @@ -290,41 +333,6 @@ public: void setDetails(int resNum, int lookLineNum, int talkLineNum, int useLineNum); }; -class UnkObject1200 : public SavedObject { -public: - Rect _rect1; - Rect _rect2; - - int *_field16; - int *_field3A; - - int _field12; - int _field14; - int _field26; - int _field28; - int _field2A; - int _field2C; - int _field2E; - int _field30; - int _field32; - int _field34; - int _field36; - int _field38; - int _field3E; - int _field40; - - UnkObject1200(); - void synchronize(Serializer &s); - - void sub51AE9(int arg1); - int sub51AF8(Common::Point pt); - bool sub51AFD(Common::Point pt); - void sub51B02(); - void sub9EDE8(Rect rect); - int sub9EE22(int &arg1, int &arg2); - virtual Common::String getClassName() { return "UnkObject1200"; } -}; - /*--------------------------------------------------------------------------*/ class AnimationSlice { diff --git a/engines/tsage/ringworld2/ringworld2_scenes1.cpp b/engines/tsage/ringworld2/ringworld2_scenes1.cpp index af62ab6916..85a6095807 100644 --- a/engines/tsage/ringworld2/ringworld2_scenes1.cpp +++ b/engines/tsage/ringworld2/ringworld2_scenes1.cpp @@ -1099,8 +1099,6 @@ void Scene1200::Area1::proc13(int resNum, int lookLineNum, int talkLineNum, int } void Scene1200::postInit(SceneObjectList *OwnerList) { - Rect tmpRect; - loadScene(1200); SceneExt::postInit(); @@ -1133,13 +1131,12 @@ void Scene1200::postInit(SceneObjectList *OwnerList) { _actor1.postInit(); _actor1.hide(); - tmpRect.set(110, 20, 210, 120); - _object1.sub9EDE8(tmpRect); + _mazeUI.setDisplayBounds(Rect(110, 20, 210, 120)); - _object1.sub51AE9(1); - _object1.sub51AFD(Common::Point(R2_GLOBALS._v56AA2, R2_GLOBALS._v56AA4)); + _mazeUI.load(1); + _mazeUI.setMazePosition(Common::Point(R2_GLOBALS._v56AA2, R2_GLOBALS._v56AA4)); warning("int unk = set_pane_p(_paneNumber);"); - _object1.sub51B02(); + _mazeUI.draw(); warning("set_pane_p(unk);"); R2_GLOBALS._player.enableControl(); @@ -1396,8 +1393,8 @@ void Scene1200::process(Event &event) { return; if (event.eventType == EVENT_BUTTON_DOWN) { - _object1.sub9EE22(R2_GLOBALS._v56AA2, R2_GLOBALS._v56AA4); - int unk = _object1.sub51AF8(event.mousePos); + _mazeUI.pixelToCellXY(R2_GLOBALS._v56AA2, R2_GLOBALS._v56AA4); + int unk = _mazeUI.getCellFromPixelXY(event.mousePos); switch (R2_GLOBALS._events.getCursor()) { case CURSOR_ARROW: event.handled = true; @@ -1544,13 +1541,14 @@ void Scene1200::dispatch() { Rect tmpRect; Scene::dispatch(); if (_field41C != 0) { - _object1.sub51AFD(Common::Point(R2_GLOBALS._v56AA2, R2_GLOBALS._v56AA4)); - warning("int unk = set_pane_p(_paneNumber);"); - _object1.sub51B02(); + _mazeUI.setMazePosition(Common::Point(R2_GLOBALS._v56AA2, R2_GLOBALS._v56AA4)); + _mazeUI.draw(); + warning("_gfxManager.sub294AC(unk);"); warning("tmpRect.sub14DF3();"); _field41C = 0; } + _mazeUI.draw(); //**DEBUG** if (_field414 != 0) { tmpRect.set(110, 20, 210, 120); @@ -1571,9 +1569,9 @@ void Scene1200::dispatch() { default: break; } - _object1.sub51AFD(Common::Point(R2_GLOBALS._v56AA2, R2_GLOBALS._v56AA4)); + _mazeUI.setMazePosition(Common::Point(R2_GLOBALS._v56AA2, R2_GLOBALS._v56AA4)); warning("int unk = set_pane_p(_paneNumber);"); - _object1.sub51B02(); + _mazeUI.draw(); warning("_gfxManager.sub294AC(unk);"); warning("tmpRect.sub14DF3();"); @@ -1608,6 +1606,189 @@ void Scene1200::saveCharacter(int characterIndex) { SceneExt::saveCharacter(characterIndex); } +void Scene1200::sub9DAD6(int indx) { + _mazeUI.pixelToCellXY(R2_GLOBALS._v56AA2, R2_GLOBALS._v56AA4); + + switch (indx) { + case 0: + if ( ((_mazeUI.getCellFromPixelXY(Common::Point(200, 50)) > 36) || (_mazeUI.getCellFromPixelXY(Common::Point(200, 88)) > 36)) + && ( ((R2_GLOBALS._v56AA2 == 3) && (R2_GLOBALS._v56AA4 == 33) && (_field418 != 4)) + || ((R2_GLOBALS._v56AA2 == 13) && (R2_GLOBALS._v56AA4 == 21) && (_field418 != 2)) + || ((R2_GLOBALS._v56AA2 == 29) && (R2_GLOBALS._v56AA4 == 17) && (_field418 != 1)) + || ((R2_GLOBALS._v56AA2 == 33) && (R2_GLOBALS._v56AA4 == 41)) ) + ) { + R2_GLOBALS._player.disableControl(); + _sceneMode = 1200; + setAction(&_sequenceManager, this, 1200, &_actor1, NULL); + } else if (_mazeUI.getCellFromPixelXY(Common::Point(200, 69)) == 36) { + switch (_field412 - 1) { + case 0: + if (R2_GLOBALS._player._visage == 3155) + _sceneMode = 15; + else + _sceneMode = 10; + break; + case 1: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 76; + else + _sceneMode = 75; + break; + case 2: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 101; + else + _sceneMode = 100; + break; + case 3: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 111; + else + _sceneMode = 110; + break; + default: + break; + } + R2_GLOBALS._player.disableControl(); + _field412 = 1; + signal(); + } + break; + case 1: + if ( ((_mazeUI.getCellFromPixelXY(Common::Point(120, 50)) > 36) || (_mazeUI.getCellFromPixelXY(Common::Point(120, 88)) > 36)) + && ( ((R2_GLOBALS._v56AA2 == 7) && (R2_GLOBALS._v56AA4 == 33) && (_field418 != 4)) + || ((R2_GLOBALS._v56AA2 == 17) && (R2_GLOBALS._v56AA4 == 21) && (_field418 != 2)) + || ((R2_GLOBALS._v56AA2 == 33) && (R2_GLOBALS._v56AA4 == 17) && (_field418 != 1)) + || ((R2_GLOBALS._v56AA2 == 5) && (R2_GLOBALS._v56AA4 == 5)) ) + ) { + R2_GLOBALS._player.disableControl(); + _sceneMode = 1201; + setAction(&_sequenceManager, this, 1201, &_actor1, NULL); + } else if (_mazeUI.getCellFromPixelXY(Common::Point(120, 69)) == 36) { + switch (_field412 - 1) { + case 0: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 56; + else + _sceneMode = 55; + break; + case 1: + if (R2_GLOBALS._player._visage == 3155) + _sceneMode = 25; + else + _sceneMode = 20; + break; + case 2: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 91; + else + _sceneMode = 90; + break; + case 3: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 121; + else + _sceneMode = 120; + break; + default: + break; + } + R2_GLOBALS._player.disableControl(); + _field412 = 2; + signal(); + } + break; + case 2: + if ( ((_mazeUI.getCellFromPixelXY(Common::Point(140, 110)) > 36) || (_mazeUI.getCellFromPixelXY(Common::Point(178, 110)) > 36)) + && ( ((R2_GLOBALS._v56AA2 == 17) && (R2_GLOBALS._v56AA4 == 5) && (_field418 != 3)) + || ((R2_GLOBALS._v56AA2 == 41) && (R2_GLOBALS._v56AA4 == 21)) ) + ) { + R2_GLOBALS._player.disableControl(); + _sceneMode = 1203; + setAction(&_sequenceManager, this, 1203, &_actor1, NULL); + } else if (_mazeUI.getCellFromPixelXY(Common::Point(160, 110)) == 36) { + switch (_field412 - 1) { + case 0: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 51; + else + _sceneMode = 50; + break; + case 1: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 81; + else + _sceneMode = 80; + break; + case 2: + if (R2_GLOBALS._player._visage == 3155) + _sceneMode = 35; + else + _sceneMode = 30; + break; + case 3: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 116; + else + _sceneMode = 115; + break; + default: + break; + } + R2_GLOBALS._player.disableControl(); + _field412 = 3; + signal(); + } + break; + case 3: + if ( ((_mazeUI.getCellFromPixelXY(Common::Point(140, 30)) > 36) || (_mazeUI.getCellFromPixelXY(Common::Point(178, 30)) > 36)) + && ( ((R2_GLOBALS._v56AA2 == 17) && (R2_GLOBALS._v56AA4 == 9) && (_field418 != 3)) + || ((R2_GLOBALS._v56AA2 == 35) && (R2_GLOBALS._v56AA4 == 17)) ) + ) { + R2_GLOBALS._player.disableControl(); + _sceneMode = 1202; + setAction(&_sequenceManager, this, 1202, &_actor1, NULL); + } else if (_mazeUI.getCellFromPixelXY(Common::Point(160, 30)) == 36) { + switch (_field412 - 1) { + case 0: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 61; + else + _sceneMode = 60; + break; + case 1: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 71; + else + _sceneMode = 70; + break; + case 2: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 96; + else + _sceneMode = 95; + break; + case 3: + if (R2_GLOBALS._player._visage == 3155) + _sceneMode = 45; + else + _sceneMode = 40; + break; + default: + _sceneMode = 1; + R2_GLOBALS._player.setup(3156, 4, 6); + break; + } + R2_GLOBALS._player.disableControl(); + _field412 = 4; + signal(); + } + break; + default: + break; + } +} + /*-------------------------------------------------------------------------- * Scene 1337 - Card game * diff --git a/engines/tsage/ringworld2/ringworld2_scenes1.h b/engines/tsage/ringworld2/ringworld2_scenes1.h index f65a89972d..619e692f89 100644 --- a/engines/tsage/ringworld2/ringworld2_scenes1.h +++ b/engines/tsage/ringworld2/ringworld2_scenes1.h @@ -147,7 +147,7 @@ public: NamedHotspot _item1; SceneActor _actor1; Area1 _area1; - UnkObject1200 _object1; + MazeUI _mazeUI; SequenceManager _sequenceManager; int _field412; diff --git a/engines/tsage/ringworld2/ringworld2_scenes2.cpp b/engines/tsage/ringworld2/ringworld2_scenes2.cpp index 6a030e5b44..02a0c05c0c 100644 --- a/engines/tsage/ringworld2/ringworld2_scenes2.cpp +++ b/engines/tsage/ringworld2/ringworld2_scenes2.cpp @@ -630,6 +630,7 @@ void Scene2000::Exit3::changeScene() { break; } } + void Scene2000::Exit4::changeScene() { Scene2000 *scene = (Scene2000 *)R2_GLOBALS._sceneManager._scene; @@ -756,19 +757,21 @@ void Scene2000::Exit5::changeScene() { } } -void Scene2000::postInit(SceneObjectList *OwnerList) { - SceneExt::postInit(); - loadScene(2000); - +Scene2000::Scene2000(): SceneExt() { if (R2_GLOBALS._sceneManager._previousScene != -1) { R2_GLOBALS._v56605[1] = 21; R2_GLOBALS._v56605[2] = 21; } - if ((R2_GLOBALS._player._characterScene[R2_GLOBALS._player._characterIndex] != R2_GLOBALS._player._oldCharacterScene[R2_GLOBALS._player._characterIndex]) && (R2_GLOBALS._player._oldCharacterScene[R2_GLOBALS._player._characterIndex] != 2350)) { + if ((R2_GLOBALS._player._characterScene[R2_GLOBALS._player._characterIndex] + != R2_GLOBALS._player._oldCharacterScene[R2_GLOBALS._player._characterIndex]) + && (R2_GLOBALS._player._oldCharacterScene[R2_GLOBALS._player._characterIndex] != 2350)) { R2_GLOBALS._player._oldCharacterScene[R2_GLOBALS._player._characterIndex] = 0; } + _exitingFlag = false; +} +void Scene2000::postInit(SceneObjectList *OwnerList) { _exit1.setDetails(Rect(0, 100, 14, 140), EXITCURSOR_W, 2000); _exit1.setDest(Common::Point(14, 129)); _exit2.setDetails(Rect(305, 100, 320, 140), EXITCURSOR_E, 2000); @@ -917,6 +920,8 @@ void Scene2000::postInit(SceneObjectList *OwnerList) { initPlayer(); _item1.setDetails(Rect(0, 0, 320, 200), 2000, 0, -1, 23, 1, NULL); + + SceneExt::postInit(); } void Scene2000::remove() { @@ -998,6 +1003,8 @@ void Scene2000::signal() { g_globals->_sceneManager.changeScene(2350); break; default: + R2_GLOBALS._v56AAB = 0; + R2_GLOBALS._player.enableControl(); break; } } diff --git a/engines/tsage/ringworld2/ringworld2_scenes2.h b/engines/tsage/ringworld2/ringworld2_scenes2.h index 281d1da366..feceaa1537 100644 --- a/engines/tsage/ringworld2/ringworld2_scenes2.h +++ b/engines/tsage/ringworld2/ringworld2_scenes2.h @@ -80,6 +80,7 @@ public: Action1 _action1, _action2, _action3, _action4, _action5; SequenceManager _sequenceManager; + Scene2000(); virtual void postInit(SceneObjectList *OwnerList = NULL); virtual void remove(); virtual void signal(); diff --git a/engines/tsage/ringworld2/ringworld2_scenes3.cpp b/engines/tsage/ringworld2/ringworld2_scenes3.cpp index 61711d0a4f..d5e7b1b9c1 100644 --- a/engines/tsage/ringworld2/ringworld2_scenes3.cpp +++ b/engines/tsage/ringworld2/ringworld2_scenes3.cpp @@ -2885,9 +2885,10 @@ void Scene3400::signal() { } /*-------------------------------------------------------------------------- - * Scene 3500 - + * Scene 3500 - Cavern Maze * *--------------------------------------------------------------------------*/ + Scene3500::Action1::Action1() { _field1E = 0; _field20 = 0; @@ -2919,9 +2920,10 @@ void Scene3500::Action1::sub108670(int arg1) { else scene->_actor5.show(); - if (scene->_actor1._frame % 2 == 0) - scene->_actor1._frameChange = _field1E; - scene->_actor1.setFrame(scene->_actor1.changeFrame()); + if (scene->_actor1._frame % 2 == 0) { + scene->_actor1._frameChange = _field1E; + scene->_actor1.setFrame(scene->_actor1.changeFrame()); + } setActionIndex(0); } @@ -2977,219 +2979,6 @@ void Scene3500::Action1::sub108732(int arg1) { } } -Scene3500::Action2::Action2() { - _field1E = 0; -} - -void Scene3500::Action2::synchronize(Serializer &s) { - Action::synchronize(s); - - s.syncAsSint16LE(_field1E); -} - -Scene3500::Item4::Item4() { - _field34 = 0; -} - -void Scene3500::Item4::synchronize(Serializer &s) { - NamedHotspot::synchronize(s); - - s.syncAsSint16LE(_field34); -} - -Scene3500::Actor7::Actor7() { - _fieldA4 = 0; - _fieldA6 = 0; - _fieldA8 = 0; - _fieldAA = 0; - _fieldAC = 0; - _fieldAE = 0; -} - -void Scene3500::Actor7::synchronize(Serializer &s) { - SceneActor::synchronize(s); - - s.syncAsSint16LE(_fieldA4); - s.syncAsSint16LE(_fieldA6); - s.syncAsSint16LE(_fieldA8); - s.syncAsSint16LE(_fieldAA); - s.syncAsSint16LE(_fieldAC); - s.syncAsSint16LE(_fieldAE); -} - -void Scene3500::Actor7::sub109466(int arg1, int arg2, int arg3, int arg4, int arg5) { - _fieldAE = 0; - _fieldA4 = arg1; - _fieldA6 = arg2; - _fieldA8 = arg3; - _fieldAA = arg4; - _fieldAC = _fieldAA / _fieldA8; - - postInit(); - setup(10501, 3, 1); - fixPriority(255); - sub109663(arg5); -} - -void Scene3500::Actor7::sub1094ED() { - Scene3500 *scene = (Scene3500 *)R2_GLOBALS._sceneManager._scene; - - scene->_field1270 = _position.x - _fieldA4; -} - -void Scene3500::Actor7::sub109663(int arg1){ - sub109693(Common::Point(_fieldA4 + arg1, _fieldA6 - (_fieldAC * arg1))); -} - -void Scene3500::Actor7::sub109693(Common::Point Pt) { - setPosition(Pt); -} - -int Scene3500::UnkObject3500::sub1097C9(int arg1) { - return (_field2A / 2) + arg1 - (arg1 % _field2A); -} - -int Scene3500::UnkObject3500::sub1097EF(int arg1) { - return (_field2C / 2) + arg1 - (arg1 % _field2C); -} - -int Scene3500::UnkObject3500::sub109C09(Common::Point pt) { - int vx = pt.x / _field2A; - int vy = pt.y / _field2C; - - if ((vx >= 0) && (_field26 > vx) && (_field28 > vy)) { - return _field16[((_field26 * vy) + vx) * 2]; - } else - return -1; -} - -int Scene3500::UnkObject3500::sub109C5E(int &x, int &y) { - int retVal = sub51AFD(Common::Point(x, y)); - x = _field2E; - y = _field30; - - return retVal; -} - -Scene3500::Scene3500() { - _fieldAF8 = 0; - _fieldB9E = 0; - _rotation = NULL; - _field126E = 0; - _field1270 = 0; - _field1272 = 0; - _field1274 = 0; - _field1276 = 0; - _field1278 = 0; - _field127A = 0; - _field127C = 0; - _field127E = 0; - _field1280 = 0; - _field1282 = 0; - _field1284 = 0; - _field1286 = 0; -} - -void Scene3500::synchronize(Serializer &s) { - SceneExt::synchronize(s); - SYNC_POINTER(_rotation); - - s.syncAsSint16LE(_fieldAF8); - s.syncAsSint16LE(_fieldB9E); - s.syncAsSint16LE(_field126E); - s.syncAsSint16LE(_field1270); - s.syncAsSint16LE(_field1272); - s.syncAsSint16LE(_field1274); - s.syncAsSint16LE(_field1276); - s.syncAsSint16LE(_field1278); - s.syncAsSint16LE(_field127A); - s.syncAsSint16LE(_field127C); - s.syncAsSint16LE(_field127E); - s.syncAsSint16LE(_field1280); - s.syncAsSint16LE(_field1282); - s.syncAsSint16LE(_field1284); - s.syncAsSint16LE(_field1286); -} - -void Scene3500::sub107F71(int arg1) { - switch (arg1) { - case -1: - _actor7.sub1094ED(); - if (_field1270 != 0) { - _field1270--; - _actor7.sub109663(_field1270); - } - if (_action1._field24 != 0) - _field1270 = 0; - break; - case 1: - _actor7.sub1094ED(); - if (_field1270 < 16) { - ++_field1270; - _actor7.sub109663(_field1270); - } - if (_action1._field24 != 0) - _field1270 = 0; - break; - case 88: - if ((_action == 0) || (_action1._field24 == 0)) { - // The original makes a second useless check on action, skipped - _action2.sub10831F(2); - if ((_action) && ((_action2.getActionIndex() != 0) || (_action2._field1E != 2))) { - _action2.signal(); - } else { - _actor9.setAction(&_action2, &_actor9, NULL); - } - } - break; - case 96: - if ((_action) && (_action1._field24 != 0) && (_action2._field1E != 1)) { - _field1278 = 0; - _action1.sub108732(0); - } else if ((_action) && (_field1278 == 0) && (_action1._field24 != 0)) { - _field1278 = arg1; - } else if ((_action) && (_action1._field24 == 0)) { - _action1.sub108670(1); - _action1.signal(); - } else if (_action == 0) { - _action1.sub108670(1); - setAction(&_action1, &_actor1, NULL); - } - break; - case 104: - if ((_action == 0) || (_action1._field24 == 0)) { - _action2.sub10831F(-1); - if ((_action) && ((_action2.getActionIndex() != 0) || (_action2._field1E != -1))) { - _action2.signal(); - } else { - _actor9.setAction(&_action2, &_actor9, NULL); - } - } - break; - case 112: - if ((_action) && (_action1._field24 != 0) && (_action2._field1E != -1)) { - _field1278 = 0; - _action1.sub108732(0); - } else if ((_action) && (_field1278 == 0) && (_action1._field24 != 0)) { - _field1278 = arg1; - } else if ((_action) && (_action1._field24 == 0)) { - _action1.sub108670(-1); - _action1.signal(); - } else if (_action == 0) { - _action1.sub108670(-1); - setAction(&_action1, &_actor1, NULL); - } - break; - default: - _field1270 = arg1; - _actor7.sub109663(arg1); - if (_action1._field24 != 0) { - _field1270 = 0; - } - break; - } -} - void Scene3500::Action1::signal() { Scene3500 *scene = (Scene3500 *)R2_GLOBALS._sceneManager._scene; @@ -3199,13 +2988,13 @@ void Scene3500::Action1::signal() { scene->_field1286 = 0; if (scene->_field1270 != 0) { scene->_field1270 = 0; - scene->_field126E = 0; + scene->_mazeChangeAmount = 0; scene->_field1272 = 0; scene->_rotation->_idxChange = 0; } break; case 1: - if ((scene->_actor1._frame % 2) == 0) { + if ((scene->_actor1._frame % 2) == 1) { setDelay(1); return; } @@ -3216,25 +3005,28 @@ void Scene3500::Action1::signal() { setDelay(1); break; case 4: { - int si = scene->_unkObj1.sub109C09(Common::Point(scene->_field127A + 70, scene->_field127C + 46)); - int var2 = scene->_unkObj1.sub1097C9(scene->_field127A + 70) - 70; - int var4 = scene->_unkObj1.sub1097EF(scene->_field127C + 46) - 46; - int di = abs(var2 - scene->_field127A); - int var6 = abs(var4 - scene->_field127C); + int si = scene->_mazeUI.getCellFromMapXY(Common::Point(scene->_mazePosition.x + 70, scene->_mazePosition.y + 46)); + int var2 = scene->_mazeUI.cellFromX(scene->_mazePosition.x + 70) - 70; + int var4 = scene->_mazeUI.cellFromY(scene->_mazePosition.y + 46) - 46; + int di = abs(var2 - scene->_mazePosition.x); + int var6 = abs(var4 - scene->_mazePosition.y); if ((scene->_actor1._frame % 2) != 0) { scene->_actor1._frameChange = _field1E; scene->_actor1.setFrame(scene->_actor1.changeFrame()); } - int var8 = (scene->_action1._field1E * 2 + scene->_field1276); - if (var8 > 7) - var8 = 1; - else if (var8 < 1) - var8 = 7; + // Get the new direction starting on + int var8 = (scene->_action1._field1E * 2 + scene->_mazeDirection); + if (var8 > MAZEDIR_NORTHWEST) + var8 = MAZEDIR_NORTH; + else if (var8 < MAZEDIR_NORTH) + var8 = MAZEDIR_WEST; + // Check whether movement is allowed in that direction. If so, then + // movement is started again switch (var8) { - case 0: + case MAZEDIR_NORTH: if ( ((si != 2) && (si != 3) && (si != 6) && (si != 1) && (si != 23) && (si != 24) && (si != 4) && (si != 11)) || (var6 != 0)) { if ((si != 25) && (si != 26) && (si != 5) && (si != 14) && (si != 15)) @@ -3246,7 +3038,7 @@ void Scene3500::Action1::signal() { } else _field20 = 1; break; - case 2: + case MAZEDIR_EAST: if ( ((si != 12) && (si != 13) && (si != 11) && (si != 16) && (si != 26) && (si != 24) && (si != 15) && (si != 6) && (si != 31)) || (di != 0)) { if ((si != 25) && (si != 23) && (si != 14) && (si != 5) && (si != 4)) @@ -3258,7 +3050,7 @@ void Scene3500::Action1::signal() { } else _field20 = 1; break; - case 4: + case MAZEDIR_SOUTH: if ( ((si != 2) && (si != 3) && (si != 6) && (si != 1) && (si != 25) && (si != 26) && (si != 5) && (si != 16) && (si != 31)) || (var6 != 0)) { if ((si != 23) && (si != 24) && (si != 4) && (si != 14) && (si != 15)) @@ -3270,7 +3062,7 @@ void Scene3500::Action1::signal() { } else _field20 = 1; break; - case 6: + case MAZEDIR_WEST: if ( ((si != 12) && (si != 13) && (si != 11) && (si != 16) && (si != 25) && (si != 23) && (si != 14) && (si != 1) && (si != 31)) || (var6 != 0)) { if ((si != 26) && (si != 24) && (si != 15) && (si != 5) && (si != 4)) @@ -3288,14 +3080,14 @@ void Scene3500::Action1::signal() { // No break on purpose case 2: { scene->_actor8.setPosition(Common::Point(160, 73)); - scene->_actor8._moveDiff.x = 160 - scene->_field126E; + scene->_actor8._moveDiff.x = 160 - scene->_mazeChangeAmount; scene->_fieldAF8 = 160 - ((_field1E * 2) * 160); Common::Point pt(scene->_fieldAF8, 73); NpcMover *mover = new NpcMover(); scene->_actor8.addMover(mover, &pt, this); scene->_actor9.setPosition(Common::Point(160 + ((_field1E * 2) * 160), 73)); - scene->_actor9._moveDiff.x = 160 - scene->_field126E; + scene->_actor9._moveDiff.x = 160 - scene->_mazeChangeAmount; scene->_fieldB9E = 160; Common::Point pt2(scene->_fieldB9E, 73); NpcMover *mover2 = new NpcMover(); @@ -3304,8 +3096,8 @@ void Scene3500::Action1::signal() { break; case 5: scene->_actor1._frameChange = _field1E; - scene->_field1276 = scene->_actor1.changeFrame(); - scene->_actor1.setFrame(scene->_field1276); + scene->_mazeDirection = scene->_actor1.changeFrame(); + scene->_actor1.setFrame(scene->_mazeDirection); setDelay(1); break; case 6: @@ -3325,8 +3117,8 @@ void Scene3500::Action1::signal() { case 7: if ((scene->_actor1._frame % 2) == 0) { scene->_actor1._frameChange = _field1E; - scene->_field1276 = scene->_actor1.changeFrame(); - scene->_actor1.setFrame(scene->_field1276); + scene->_mazeDirection = scene->_actor1.changeFrame(); + scene->_actor1.setFrame(scene->_mazeDirection); } setDelay(1); break; @@ -3342,7 +3134,7 @@ void Scene3500::Action1::signal() { // but it's clearly a cut and paste error from case 4. // The following code allows the switch to work properly. warning("Checkme: fix for dead code"); - int var_8 = (_field1E * 2 + scene->_field1276); + int var_8 = (_field1E * 2 + scene->_mazeDirection); if (var_8 > 7) var_8 = 1; else if (var_8 < 1) @@ -3353,12 +3145,12 @@ void Scene3500::Action1::signal() { case 0: // No break on purpose case 4: - scene->_field127A = scene->_unkObj1.sub1097C9(scene->_field127A + 70) - 70; + scene->_mazePosition.x = scene->_mazeUI.cellFromX(scene->_mazePosition.x + 70) - 70; break; case 2: // No break on purpose case 6: - scene->_field127C = scene->_unkObj1.sub1097EF(scene->_field127C + 46) - 46; + scene->_mazePosition.y = scene->_mazeUI.cellFromY(scene->_mazePosition.y + 46) - 46; break; default: break; @@ -3368,7 +3160,7 @@ void Scene3500::Action1::signal() { _field24 = 0; if (_field20 == 0) { scene->_actor7.sub1094ED(); - if (scene->_field126E == scene->_field1270) + if (scene->_mazeChangeAmount == scene->_field1270) scene->_aSound1.play(276); } break; @@ -3416,12 +3208,232 @@ void Scene3500::Action1::dispatch() { Scene3500 *scene = (Scene3500 *)R2_GLOBALS._sceneManager._scene; Action::dispatch(); - if ((_actionIndex == 1) && (scene->_field126E <= 4)) { + if ((_actionIndex == 1) && (scene->_mazeChangeAmount <= 4)) { scene->_rotation->_idxChange = 0; signal(); } } +/*--------------------------------------------------------------------------*/ + +Scene3500::Action2::Action2() { + _field1E = 0; +} + +void Scene3500::Action2::synchronize(Serializer &s) { + Action::synchronize(s); + + s.syncAsSint16LE(_field1E); +} + +/*--------------------------------------------------------------------------*/ + +Scene3500::Item4::Item4() { + _field34 = 0; +} + +void Scene3500::Item4::synchronize(Serializer &s) { + NamedHotspot::synchronize(s); + + s.syncAsSint16LE(_field34); +} + +/*--------------------------------------------------------------------------*/ + +Scene3500::Actor7::Actor7() { + _fieldA4 = 0; + _fieldA6 = 0; + _fieldA8 = 0; + _fieldAA = 0; + _fieldAC = 0; + _fieldAE = 0; +} + +void Scene3500::Actor7::synchronize(Serializer &s) { + SceneActor::synchronize(s); + + s.syncAsSint16LE(_fieldA4); + s.syncAsSint16LE(_fieldA6); + s.syncAsSint16LE(_fieldA8); + s.syncAsSint16LE(_fieldAA); + s.syncAsSint16LE(_fieldAC); + s.syncAsSint16LE(_fieldAE); +} + +void Scene3500::Actor7::sub109466(int arg1, int arg2, int arg3, int arg4, int arg5) { + _fieldAE = 0; + _fieldA4 = arg1; + _fieldA6 = arg2; + _fieldA8 = arg3; + _fieldAA = arg4; + _fieldAC = _fieldAA / _fieldA8; + + postInit(); + setup(1050, 3, 1); + fixPriority(255); + sub109663(arg5); +} + +void Scene3500::Actor7::sub1094ED() { + Scene3500 *scene = (Scene3500 *)R2_GLOBALS._sceneManager._scene; + + scene->_field1270 = _position.x - _fieldA4; +} + +void Scene3500::Actor7::sub109663(int arg1){ + sub109693(Common::Point(_fieldA4 + arg1, _fieldA6 - (_fieldAC * arg1))); +} + +void Scene3500::Actor7::sub109693(Common::Point Pt) { + setPosition(Pt); +} + +/*--------------------------------------------------------------------------*/ + +int Scene3500::MazeUI3500::cellFromX(int x) { + return (_cellSize.x / 2) + x - (x % _cellSize.x); +} + +int Scene3500::MazeUI3500::cellFromY(int y) { + return (_cellSize.y / 2) + y - (y % _cellSize.y) - 1; +} + +int Scene3500::MazeUI3500::getCellFromMapXY(Common::Point pt) { + int cellX = pt.x / _cellSize.x; + int cellY = pt.y / _cellSize.y; + + if ((cellX >= 0) && (cellY >= 0) && (cellX < _mapCells.x) && (cellY < _mapCells.y)) { + return (int16)READ_LE_UINT16(_mapData + (_mapCells.x * cellY + cellX) * 2); + } else + return -1; +} + +bool Scene3500::MazeUI3500::setMazePosition2(Common::Point &p) { + bool retVal = setMazePosition(p); + p = _mapOffset; + + return retVal; +} + +Scene3500::Scene3500() { + _fieldAF8 = 0; + _fieldB9E = 0; + _rotation = NULL; + _mazeChangeAmount = 0; + _field1270 = 0; + _field1272 = 0; + _field1274 = 0; + _mazeDirection = MAZEDIR_NONE; + _field1278 = 0; + _mazePosition.x = 0; + _mazePosition.y = 0; + _field127E = 0; + _field1280 = 0; + _field1282 = 0; + _field1284 = 0; + _field1286 = 0; +} + +void Scene3500::synchronize(Serializer &s) { + SceneExt::synchronize(s); + SYNC_POINTER(_rotation); + + s.syncAsSint16LE(_fieldAF8); + s.syncAsSint16LE(_fieldB9E); + s.syncAsSint16LE(_mazeChangeAmount); + s.syncAsSint16LE(_field1270); + s.syncAsSint16LE(_field1272); + s.syncAsSint16LE(_field1274); + s.syncAsSint16LE(_mazeDirection); + s.syncAsSint16LE(_field1278); + s.syncAsSint16LE(_mazePosition.x); + s.syncAsSint16LE(_mazePosition.y); + s.syncAsSint16LE(_field127E); + s.syncAsSint16LE(_field1280); + s.syncAsSint16LE(_field1282); + s.syncAsSint16LE(_field1284); + s.syncAsSint16LE(_field1286); +} + +void Scene3500::sub107F71(int arg1) { + switch (arg1) { + case -1: + _actor7.sub1094ED(); + if (_field1270 != 0) { + _field1270--; + _actor7.sub109663(_field1270); + } + if (_action1._field24 != 0) + _field1270 = 0; + break; + case 1: + _actor7.sub1094ED(); + if (_field1270 < 16) { + ++_field1270; + _actor7.sub109663(_field1270); + } + if (_action1._field24 != 0) + _field1270 = 0; + break; + case 88: + if ((_action == 0) || (_action1._field24 == 0)) { + // The original makes a second useless check on action, skipped + _action2.sub10831F(2); + if ((_action) && ((_action2.getActionIndex() != 0) || (_action2._field1E != 2))) { + _action2.signal(); + } else { + _actor9.setAction(&_action2, &_actor9, NULL); + } + } + break; + case 96: + if ((_action) && (_action1._field24 != 0) && (_action2._field1E != 1)) { + _field1278 = 0; + _action1.sub108732(0); + } else if ((_action) && (_field1278 == 0) && (_action1._field24 != 0)) { + _field1278 = arg1; + } else if ((_action) && (_action1._field24 == 0)) { + _action1.sub108670(1); + _action1.signal(); + } else if (_action == 0) { + _action1.sub108670(1); + setAction(&_action1, &_actor1, NULL); + } + break; + case 104: + if ((_action == 0) || (_action1._field24 == 0)) { + _action2.sub10831F(-1); + if ((_action) && ((_action2.getActionIndex() != 0) || (_action2._field1E != -1))) { + _action2.signal(); + } else { + _actor9.setAction(&_action2, &_actor9, NULL); + } + } + break; + case 112: + if ((_action) && (_action1._field24 != 0) && (_action2._field1E != -1)) { + _field1278 = 0; + _action1.sub108732(0); + } else if ((_action) && (_field1278 == 0) && (_action1._field24 != 0)) { + _field1278 = arg1; + } else if ((_action) && (_action1._field24 == 0)) { + _action1.sub108670(-1); + _action1.signal(); + } else if (_action == 0) { + _action1.sub108670(-1); + setAction(&_action1, &_actor1, NULL); + } + break; + default: + _field1270 = arg1; + _actor7.sub109663(arg1); + if (_action1._field24 != 0) { + _field1270 = 0; + } + break; + } +} + void Scene3500::Action2::sub10831F(int arg1) { Scene3500 *scene = (Scene3500 *)R2_GLOBALS._sceneManager._scene; @@ -3452,17 +3464,17 @@ void Scene3500::Action2::signal() { di = scene->_fieldB9E; } - scene->_actor8._moveDiff.y = 9 - (scene->_field126E / 2); + scene->_actor8._moveDiff.y = 9 - (scene->_mazeChangeAmount / 2); Common::Point pt(si, 73 - (_field1E * 12)); NpcMover *mover = new NpcMover(); scene->_actor8.addMover(mover, &pt, NULL); - scene->_actor9._moveDiff.y = 9 - (scene->_field126E / 2); + scene->_actor9._moveDiff.y = 9 - (scene->_mazeChangeAmount / 2); Common::Point pt2(di, 73 - (_field1E * 12)); NpcMover *mover2 = new NpcMover(); scene->_actor9.addMover(mover2, &pt2, NULL); - scene->_field126E = (scene->_field126E / 2) + (scene->_field126E % 2); - setDelay(17 - scene->_field126E); + scene->_mazeChangeAmount = (scene->_mazeChangeAmount / 2) + (scene->_mazeChangeAmount % 2); + setDelay(17 - scene->_mazeChangeAmount); } break; case 1: { @@ -3477,12 +3489,12 @@ void Scene3500::Action2::signal() { scene->_actor7.sub1094ED(); - scene->_actor8._moveDiff.y = 9 - (scene->_field126E / 2); + scene->_actor8._moveDiff.y = 9 - (scene->_mazeChangeAmount / 2); Common::Point pt(si, 73); NpcMover *mover = new NpcMover(); scene->_actor8.addMover(mover, &pt, NULL); - scene->_actor9._moveDiff.y = 9 - (scene->_field126E / 2); + scene->_actor9._moveDiff.y = 9 - (scene->_mazeChangeAmount / 2); Common::Point pt2(di, 73); NpcMover *mover2 = new NpcMover(); scene->_actor9.addMover(mover2, &pt2, NULL); @@ -3559,7 +3571,6 @@ bool Scene3500::Actor7::startAction(CursorType action, Event &event) { void Scene3500::postInit(SceneObjectList *OwnerList) { byte tmpPal[768]; - Rect tmpRect; loadScene(1050); R2_GLOBALS._uiElements._active = false; @@ -3574,9 +3585,8 @@ void Scene3500::postInit(SceneObjectList *OwnerList) { _field1278 = 0; _field1272 = 1; _field1270 = 4; - _field126E = 4; - _field127A = 860; - _field127C = 891; + _mazeChangeAmount = 4; + _mazePosition = Common::Point(860, 891); _rotation = R2_GLOBALS._scenePalette.addRotation(240, 254, -1); _rotation->setDelay(0); _rotation->_idxChange = 1; @@ -3615,7 +3625,7 @@ void Scene3500::postInit(SceneObjectList *OwnerList) { _item7.setDetails(96, 3500, 12, 10, -1); _actor8.postInit(); - _actor8.setup(10501, 1, 1); + _actor8.setup(1050, 1, 1); _actor8.setPosition(Common::Point(160, 73)); _actor8.fixPriority(1); @@ -3630,8 +3640,8 @@ void Scene3500::postInit(SceneObjectList *OwnerList) { _item1.setDetails(Rect(0, 0, 320, 200), 3500, 0, -1, 2, 1, NULL); _actor1.postInit(); - _field1276 = 1; - _actor1.setup(1004, 1, _field1276); + _mazeDirection = MAZEDIR_NORTH; + _actor1.setup(1004, 1, _mazeDirection); _actor1.setPosition(Common::Point(230, 135)); _actor1.fixPriority(200); _actor1._frameChange = 1; @@ -3654,7 +3664,7 @@ void Scene3500::postInit(SceneObjectList *OwnerList) { _actor6.hide(); _actor2.postInit(); - _actor2.setup(1004, 4, _field126E + 1); + _actor2.setup(1004, 4, _mazeChangeAmount + 1); _actor2.setPosition(Common::Point(126, 137)); _actor2.fixPriority(200); @@ -3663,15 +3673,12 @@ void Scene3500::postInit(SceneObjectList *OwnerList) { _actor3.setPosition(Common::Point(126, 108)); _actor3.fixPriority(200); - tmpRect.set(160, 89, 299, 182); - _unkObj1.sub9EDE8(tmpRect); - _unkObj1.sub51AE9(2); - _unkObj1.sub51AFD(Common::Point(_field127A, _field127C)); + _mazeUI.setDisplayBounds(Rect(160, 89, 299, 182)); + _mazeUI.load(2); + _mazeUI.setMazePosition(_mazePosition); _action1._field24 = 0; - warning("gfx_set_pane_p()"); - _unkObj1.sub51B02(); - warning("gfx_set_pane_p()"); + _mazeUI.draw(); _field1286 = 1; R2_GLOBALS._player.postInit(); @@ -3682,7 +3689,6 @@ void Scene3500::postInit(SceneObjectList *OwnerList) { } void Scene3500::remove() { - _rotation->remove(); R2_GLOBALS._sound2.fadeOut2(NULL); SceneExt::remove(); } @@ -3699,64 +3705,58 @@ void Scene3500::process(Event &event) { if (event.eventType == EVENT_KEYPRESS) { switch (event.kbd.keycode) { - case Common::KEYCODE_1: - warning("FIXME: keycode = 0x4700"); + case Common::KEYCODE_KP7: R2_GLOBALS._sound2.play(338); sub107F71(16); event.handled = true; break; - case Common::KEYCODE_2: - warning("FIXME: keycode = 0x4800"); + case Common::KEYCODE_UP: + case Common::KEYCODE_KP8: R2_GLOBALS._sound2.play(14, NULL, 63); sub107F71(88); event.handled = true; break; - case Common::KEYCODE_3: - warning("FIXME: keycode = 0x4900"); + case Common::KEYCODE_KP9: if (_field1270 < 16) R2_GLOBALS._sound2.play(338); sub107F71(1); event.handled = true; break; - case Common::KEYCODE_4: - warning("FIXME: keycode = 0x4B00"); + case Common::KEYCODE_KP4: + case Common::KEYCODE_LEFT: R2_GLOBALS._sound2.play(14, NULL, 63); sub107F71(112); event.handled = true; break; - case Common::KEYCODE_5: - warning("FIXME: keycode = 0x4D00"); + case Common::KEYCODE_KP6: + case Common::KEYCODE_RIGHT: R2_GLOBALS._sound2.play(14, NULL, 63); sub107F71(96); event.handled = true; break; - case Common::KEYCODE_6: - warning("FIXME: keycode = 0x4F00"); + case Common::KEYCODE_KP1: R2_GLOBALS._sound2.play(338); sub107F71(0); event.handled = true; break; - case Common::KEYCODE_7: - warning("FIXME: keycode = 0x5000"); + case Common::KEYCODE_KP2: + case Common::KEYCODE_DOWN: R2_GLOBALS._sound2.play(14, NULL, 63); sub107F71(104); event.handled = true; break; - case Common::KEYCODE_8: - warning("FIXME: keycode = 0x5100"); + case Common::KEYCODE_KP3: if (_field1270 != 0) R2_GLOBALS._sound2.play(338); sub107F71(-1); event.handled = true; break; - case Common::KEYCODE_9: - warning("FIXME: keycode = 0x5200"); + case Common::KEYCODE_KP0: R2_GLOBALS._sound2.play(338); sub107F71(8); event.handled = true; break; - case Common::KEYCODE_0: - warning("FIXME: keycode = 0x5300"); + case Common::KEYCODE_KP_PERIOD: R2_GLOBALS._sound2.play(338); sub107F71(4); event.handled = true; @@ -3787,9 +3787,10 @@ void Scene3500::process(Event &event) { void Scene3500::dispatch() { Rect tmpRect; Scene::dispatch(); + if (((_actor1._frame % 2) == 0) && (_action1._field24 == 0)) { _actor1.setFrame(_actor1.changeFrame()); - _field1276 = _actor1._frame; + _mazeDirection = _actor1._frame; } int oldField1278; if ((_field1278 != 0) && (_action1._field24 == 0)) { @@ -3801,90 +3802,90 @@ void Scene3500::dispatch() { if (!_rotation) return; - int var_field127A = 0; - int di = 0; + int newMazeX = 0; + int newMazeY = 0; int var_4 = 0; int var_6 = 0; int var_8 = 0; int var_a = 0; int dx = 0; - int tmpVar = 0; + int cellId = 0; - if ((_field126E == 0) && (_field1282 == 0)) { + if ((_mazeChangeAmount == 0) && (_field1282 == 0)) { if (_field1284 == 2) R2_GLOBALS._sceneManager.changeScene(1000); } else { _field1282 = 0; tmpRect.set(160, 89, 299, 182); - var_field127A = _field127A; - di = _field127C; - var_4 = _unkObj1.sub1097C9(70) - 70; - var_6 = _unkObj1.sub1097EF(_field127C + 46) - 46; - var_8 = abs(var_4 - var_field127A); - var_a = abs(var_6 - di); + newMazeX = _mazePosition.x; + newMazeY = _mazePosition.y; + var_4 = _mazeUI.cellFromX(newMazeX + 70) - 70; + var_6 = _mazeUI.cellFromY(_mazePosition.y + 46) - 46; + var_8 = abs(var_4 - newMazeX); + var_a = abs(var_6 - newMazeY); dx = 0; - switch (_field1276) { - case 0: - tmpVar = _unkObj1.sub109C09(Common::Point(var_field127A + 70, 46)); - if ( ((tmpVar == 2) || (tmpVar == 3) || (tmpVar == 6) || (tmpVar == 1)) - || (((tmpVar == 25) || (tmpVar == 26) || (tmpVar == 5) || (tmpVar == 14) || (tmpVar == 15)) && (var_8 > 3)) ) { + switch (_mazeDirection) { + case MAZEDIR_NORTH: + cellId = _mazeUI.getCellFromMapXY(Common::Point(newMazeX + 70, newMazeY + 46)); + if (((cellId == 2) || (cellId == 3) || (cellId == 6) || (cellId == 1)) || + ((cellId == 25 || cellId == 26 || cellId == 5 || cellId == 14 || cellId == 15) && var_8 > 3)) { R2_GLOBALS._sound2.play(339); _rotation->_idxChange = 0; _field1270 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1272 = 0; if (_action1._field24 == 0) _actor8.hide(); } else { - var_6 = _unkObj1.sub1097EF(di + 46) - 46; - di = _field127C - _field126E; - dx = _unkObj1.sub109C09(Common::Point(var_field127A + 70, di + 46)); - if (((tmpVar == 23) || (tmpVar == 24) || (tmpVar == 4)) && (tmpVar != dx)) { - di = var_6; + var_6 = _mazeUI.cellFromY(newMazeY + 46) - 46; + newMazeY = _mazePosition.y - _mazeChangeAmount; + dx = _mazeUI.getCellFromMapXY(Common::Point(newMazeX + 70, newMazeY + 46)); + if (((cellId == 23) || (cellId == 24) || (cellId == 4)) && (cellId != dx)) { + newMazeY = var_6; R2_GLOBALS._sound2.play(339); _rotation->_idxChange = 0; _field1270 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1272 = 0; if (_action1._field24 == 0) _actor8.hide(); - } else if ((tmpVar == 11) && (tmpVar != dx)) { - di = var_6 + 3; + } else if ((cellId == 11) && (cellId != dx)) { + newMazeY = var_6 + 3; R2_GLOBALS._sound2.play(339); _rotation->_idxChange = 0; _field1270 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1272 = 0; if (_action1._field24 == 0) _actor8.hide(); } else { - var_6 = _unkObj1.sub1097EF(di + 46) - 46; - var_a = abs(var_6 - di); - tmpVar = _unkObj1.sub109C09(Common::Point(var_field127A + 70, di + 46)); - - if ( (((tmpVar == 23) || (tmpVar == 24) || (tmpVar == 4)) && (di <= var_6) && (_field127C>= var_6)) - || (((tmpVar == 25) || (tmpVar == 26) || (tmpVar == 5) || (tmpVar == 14) || (tmpVar == 15)) && (_field126E >= var_a) && (_field126E > 3) && (_action1._field24 != 0)) ) { - di = var_6; - if ((tmpVar != 25) && (tmpVar != 26) && (tmpVar != 5) && (tmpVar != 14) && (tmpVar == 15)) + var_6 = _mazeUI.cellFromY(newMazeY + 46) - 46; + var_a = abs(var_6 - newMazeY); + cellId = _mazeUI.getCellFromMapXY(Common::Point(newMazeX + 70, newMazeY + 46)); + + if ( (((cellId == 23) || (cellId == 24) || (cellId == 4)) && (newMazeY <= var_6) && (_mazePosition.y>= var_6)) + || (((cellId == 25) || (cellId == 26) || (cellId == 5) || (cellId == 14) || (cellId == 15)) && (_mazeChangeAmount >= var_a) && (_mazeChangeAmount > 3) && (_action1._field24 != 0)) ) { + newMazeY = var_6; + if ((cellId != 25) && (cellId != 26) && (cellId != 5) && (cellId != 14) && (cellId == 15)) R2_GLOBALS._sound2.play(339); _rotation->_idxChange = 0; _field1270 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1272 = 0; if (_action1._field24 == 0) _actor8.hide(); - } else if ((tmpVar == 11) && (var_6 + 3 >= di) && (_field127C >= var_6 + 3)) { + } else if ((cellId == 11) && (var_6 + 3 >= newMazeY) && (_mazePosition.y >= var_6 + 3)) { R2_GLOBALS._sound2.play(339); _rotation->_idxChange = 0; _field1270 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1272 = 0; if (_action1._field24 == 0) _actor8.hide(); - } else if (((tmpVar == 25) || (tmpVar == 26) || (tmpVar == 5) || (tmpVar == 14) || (tmpVar == 15)) && (var_8 != 0) && (var_8 <= 3)) { - var_field127A = var_4; + } else if (((cellId == 25) || (cellId == 26) || (cellId == 5) || (cellId == 14) || (cellId == 15)) && (var_8 != 0) && (var_8 <= 3)) { + newMazeX = var_4; R2_GLOBALS._sound2.play(339); } else { // Nothing @@ -3892,65 +3893,65 @@ void Scene3500::dispatch() { } } break; - case 2: - tmpVar = _unkObj1.sub109C09(Common::Point(var_field127A + 70, di + 46)); - if ( ((tmpVar == 12) || (tmpVar == 13) || (tmpVar == 11) || (tmpVar == 16) || (tmpVar == 31)) - || (((tmpVar == 25) || (tmpVar == 23) || (tmpVar == 14) || (tmpVar == 5) || (tmpVar == 4)) && (var_a > 3)) ) { + case MAZEDIR_EAST: + cellId = _mazeUI.getCellFromMapXY(Common::Point(newMazeX + 70, newMazeY + 46)); + if ( ((cellId == 12) || (cellId == 13) || (cellId == 11) || (cellId == 16) || (cellId == 31)) + || (((cellId == 25) || (cellId == 23) || (cellId == 14) || (cellId == 5) || (cellId == 4)) && (var_a > 3)) ) { R2_GLOBALS._sound2.play(339); _rotation->_idxChange = 0; _field1270 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1272 = 0; if (_action1._field24 == 0) _actor8.hide(); } else { - var_4 = _unkObj1.sub1097C9(var_field127A + 70) - 70; - var_field127A = _field127A + _field126E; - dx = _unkObj1.sub109C09(Common::Point(var_field127A + 70, di + 46)); - if (((tmpVar == 26) || (tmpVar == 24) || (tmpVar == 15)) && (tmpVar != dx)) { - var_field127A = var_4; + var_4 = _mazeUI.cellFromX(newMazeX + 70) - 70; + newMazeX = _mazePosition.x + _mazeChangeAmount; + dx = _mazeUI.getCellFromMapXY(Common::Point(newMazeX + 70, newMazeY + 46)); + if (((cellId == 26) || (cellId == 24) || (cellId == 15)) && (cellId != dx)) { + newMazeX = var_4; R2_GLOBALS._sound2.play(339); _rotation->_idxChange = 0; _field1270 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1272 = 0; if (_action1._field24 == 0) _actor8.hide(); - } else if ((tmpVar == 6) && (tmpVar != dx)) { - var_field127A = var_4 - 5; + } else if ((cellId == 6) && (cellId != dx)) { + newMazeX = var_4 - 5; R2_GLOBALS._sound2.play(339); _rotation->_idxChange = 0; _field1270 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1272 = 0; if (_action1._field24 == 0) _actor8.hide(); } else { - var_4 = _unkObj1.sub1097C9(var_field127A + 70) - 70; - var_8 = abs(var_field127A - var_4); - tmpVar = _unkObj1.sub109C09(Common::Point(var_field127A + 70, tmpVar + 46)); - if ( (((tmpVar == 26) || (tmpVar == 24) || (tmpVar == 15)) && (var_field127A >= var_4) && (_field127A <= var_4)) - || (((tmpVar == 25) || (tmpVar == 23) || (tmpVar == 14) || (tmpVar == 5) || (tmpVar == 4)) && (_field126E >= var_8) && (_field126E <= 3) && (_action1._field24 != 0)) ) { - var_field127A = var_4; - if ((tmpVar == 25) || (tmpVar == 23) || (tmpVar == 14) || (tmpVar == 5) || (tmpVar == 4)) + var_4 = _mazeUI.cellFromX(newMazeX + 70) - 70; + var_8 = abs(newMazeX - var_4); + cellId = _mazeUI.getCellFromMapXY(Common::Point(newMazeX + 70, newMazeY + 46)); + if ( (((cellId == 26) || (cellId == 24) || (cellId == 15)) && (newMazeX >= var_4) && (_mazePosition.x <= var_4)) + || (((cellId == 25) || (cellId == 23) || (cellId == 14) || (cellId == 5) || (cellId == 4)) && (_mazeChangeAmount >= var_8) && (_mazeChangeAmount <= 3) && (_action1._field24 != 0)) ) { + newMazeX = var_4; + if ((cellId == 25) || (cellId == 23) || (cellId == 14) || (cellId == 5) || (cellId == 4)) R2_GLOBALS._sound2.play(339); _rotation->_idxChange = 0; _field1270 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1272 = 0; if (_action1._field24 == 0) _actor8.hide(); - } else if ((tmpVar == 6) && (var_4 - 5 <= var_field127A) && (_field127A <= var_4 - 5)) { - var_field127A = var_4 - 5; + } else if ((cellId == 6) && (var_4 - 5 <= newMazeX) && (_mazePosition.x <= var_4 - 5)) { + newMazeX = var_4 - 5; R2_GLOBALS._sound2.play(339); _rotation->_idxChange = 0; _field1270 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1272 = 0; if (_action1._field24 == 0) _actor8.hide(); - } else if (((tmpVar == 25) || (tmpVar == 23) || (tmpVar == 14) || (tmpVar == 5) || (tmpVar == 4)) && (var_a != 0) && (var_a <= 3)) { - di = var_6; + } else if (((cellId == 25) || (cellId == 23) || (cellId == 14) || (cellId == 5) || (cellId == 4)) && (var_a != 0) && (var_a <= 3)) { + newMazeY = var_6; R2_GLOBALS._sound2.play(339); } else { // Nothing @@ -3958,84 +3959,87 @@ void Scene3500::dispatch() { } } break; - case 4: - tmpVar = _unkObj1.sub109C09(Common::Point(var_field127A + 70, di + 46)); - if ( ((tmpVar == 2) || (tmpVar == 3) || (tmpVar == 6) || (tmpVar == 1)) - || (((tmpVar == 23) || (tmpVar == 24) || (tmpVar == 4) || (tmpVar == 14) || (tmpVar == 15)) && (var_8 > 3)) ) { + case MAZEDIR_SOUTH: + cellId = _mazeUI.getCellFromMapXY(Common::Point(newMazeX + 70, newMazeY + 46)); + if ( ((cellId == 2) || (cellId == 3) || (cellId == 6) || (cellId == 1)) + || (((cellId == 23) || (cellId == 24) || (cellId == 4) || (cellId == 14) || (cellId == 15)) && (var_8 > 3)) ) { R2_GLOBALS._sound2.play(339); _rotation->_idxChange = 0; _field1270 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1272 = 0; if (_action1._field24 == 0) _actor8.hide(); } else { - var_6 = _unkObj1.sub1097EF(di + 46) - 46; - di = _field127C + _field126E; - dx = _unkObj1.sub109C09(Common::Point(var_field127A + 70, di + 46)); - if (((tmpVar == 25) || (tmpVar == 26) || (tmpVar == 5)) && (tmpVar == dx)) { + var_6 = _mazeUI.cellFromY(newMazeY + 46) - 46; + newMazeY = _mazePosition.y + _mazeChangeAmount; + dx = _mazeUI.getCellFromMapXY(Common::Point(newMazeX + 70, newMazeY + 46)); + + if (((cellId == 25) || (cellId == 26) || (cellId == 5)) && (cellId != dx)) { R2_GLOBALS._sound2.play(339); _rotation->_idxChange = 0; _field1270 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1272 = 0; if (_action1._field24 == 0) _actor8.hide(); - } else if ((tmpVar == 16) && (tmpVar == dx)) { - di = var_6 - 3; + } else if ((cellId == 16) && (cellId != dx)) { + newMazeY = var_6 - 3; R2_GLOBALS._sound2.play(339); _rotation->_idxChange = 0; _field1270 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1272 = 0; if (_action1._field24 == 0) _actor8.hide(); - } else if ((tmpVar == 31) && (tmpVar == dx)) { - di = var_6 + 4; + } else if ((cellId == 31) && (cellId != dx)) { + newMazeY = var_6 + 4; R2_GLOBALS._sound2.play(339); _rotation->_idxChange = 0; _field1270 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1272 = 0; if (_action1._field24 == 0) _actor8.hide(); } else { - var_6 = _unkObj1.sub1097EF(di + 46) - 46; - var_a = abs(di - var_6); - tmpVar = _unkObj1.sub109C09(Common::Point(var_field127A + 70, di + 46)); - if ( (((tmpVar == 25) || (tmpVar == 26) || (tmpVar == 5)) && (di >= var_6) && (_field127C <= var_6)) - || (((tmpVar == 23) || (tmpVar == 24) || (tmpVar == 4) || (tmpVar == 14) || (tmpVar == 15)) && (_field126E >= var_a) && (_field126E <= 3) && (_action1._field24 != 0)) ){ - if ((tmpVar != 23) && (tmpVar != 24) && (tmpVar != 4) && (tmpVar != 14) && (tmpVar != 15)) + var_6 = _mazeUI.cellFromY(newMazeY + 46) - 46; + var_a = abs(newMazeY - var_6); + cellId = _mazeUI.getCellFromMapXY(Common::Point(newMazeX + 70, newMazeY + 46)); + if ( (((cellId == 25) || (cellId == 26) || (cellId == 5)) && (newMazeY >= var_6) && (_mazePosition.y <= var_6)) + || (((cellId == 23) || (cellId == 24) || (cellId == 4) || (cellId == 14) || (cellId == 15)) && (_mazeChangeAmount >= var_a) && (_mazeChangeAmount <= 3) && (_action1._field24 != 0)) ){ + newMazeY = var_6; + + if ((cellId != 23) && (cellId != 24) && (cellId != 4) && (cellId != 14) && (cellId != 15)) R2_GLOBALS._sound2.play(339); _rotation->_idxChange = 0; _field1270 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1272 = 0; if (_action1._field24 == 0) _actor8.hide(); - } else if ((tmpVar == 16) && (var_6 - 3 <= di) && (_field127C <= var_6 - 3)) { - di = var_6 - 3; + } else if ((cellId == 16) && (var_6 - 3 <= newMazeY) && (_mazePosition.y <= var_6 - 3)) { + newMazeY = var_6 - 3; R2_GLOBALS._sound2.play(339); _rotation->_idxChange = 0; _field1270 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1272 = 0; if (_action1._field24 == 0) _actor8.hide(); - } else if ((tmpVar == 31) && (var_6 + 4 <= di) && (_field127C <= var_6 + 4)) { - di = var_6 + 4; + } else if ((cellId == 31) && (var_6 + 4 <= newMazeY) && (_mazePosition.y <= var_6 + 4)) { + newMazeY = var_6 + 4; _rotation->_idxChange = 0; _field1270 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1272 = 0; if (_action1._field24 == 0) _actor8.hide(); - if ((var_field127A == 660) && (_field126E + 306 <= di) && (di <= 307)) + if ((newMazeX == 660) && (_mazeChangeAmount + 306 <= newMazeY) && (newMazeY <= 307)) ++_field1284; else R2_GLOBALS._sound2.play(339); - } else if (((tmpVar == 23) || (tmpVar == 24) || (tmpVar == 4) || (tmpVar == 14) || (tmpVar == 15)) && (var_8 != 0) && (var_8 <= 3)) { - var_field127A = var_4; + } else if (((cellId == 23) || (cellId == 24) || (cellId == 4) || (cellId == 14) || (cellId == 15)) && (var_8 != 0) && (var_8 <= 3)) { + newMazeX = var_4; R2_GLOBALS._sound2.play(339); } else { // Nothing @@ -4043,65 +4047,65 @@ void Scene3500::dispatch() { } } break; - case 6: - tmpVar = _unkObj1.sub109C09(Common::Point(var_field127A + 70, di + 46)); - if ( ((tmpVar == 12) || (tmpVar == 13) || (tmpVar == 11) || (tmpVar == 16) || (tmpVar == 31)) - || (((tmpVar == 26) || (tmpVar == 24) || (tmpVar == 15) || (tmpVar == 5) || (tmpVar == 4)) && (var_a > 3)) ) { + case MAZEDIR_WEST: + cellId = _mazeUI.getCellFromMapXY(Common::Point(newMazeX + 70, newMazeY + 46)); + if ( ((cellId == 12) || (cellId == 13) || (cellId == 11) || (cellId == 16) || (cellId == 31)) + || (((cellId == 26) || (cellId == 24) || (cellId == 15) || (cellId == 5) || (cellId == 4)) && (var_a > 3)) ) { R2_GLOBALS._sound2.play(339); _rotation->_idxChange = 0; _field1270 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1272 = 0; if (_action1._field24 == 0) _actor8.hide(); } else { - var_4 = _unkObj1.sub1097C9(var_field127A + 70) - 70; - var_field127A = _field127A - _field126E; - dx = _unkObj1.sub109C09(Common::Point(var_field127A + 70, di + 46)); - if (((tmpVar == 25) || (tmpVar == 23) || (tmpVar == 14)) && (tmpVar != dx)) { - var_field127A = var_4; + var_4 = _mazeUI.cellFromX(newMazeX + 70) - 70; + newMazeX = _mazePosition.x - _mazeChangeAmount; + dx = _mazeUI.getCellFromMapXY(Common::Point(newMazeX + 70, newMazeY + 46)); + if (((cellId == 25) || (cellId == 23) || (cellId == 14)) && (cellId != dx)) { + newMazeX = var_4; R2_GLOBALS._sound2.play(339); _rotation->_idxChange = 0; _field1270 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1272 = 0; if (_action1._field24 == 0) _actor8.hide(); - } else if ((tmpVar == 1) && (tmpVar != dx)) { - var_field127A = var_4 + 5; + } else if ((cellId == 1) && (cellId != dx)) { + newMazeX = var_4 + 5; R2_GLOBALS._sound2.play(339); _rotation->_idxChange = 0; _field1270 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1272 = 0; if (_action1._field24 == 0) _actor8.hide(); } else { - var_4 = _unkObj1.sub1097C9(var_field127A + 70) - 70; - var_8 = abs(var_4 - var_field127A); - tmpVar = _unkObj1.sub109C09(Common::Point(var_field127A + 70, di + 46)); - if ( (((tmpVar == 25) || (tmpVar == 23) || (tmpVar == 14)) && (var_field127A <= var_4) && (_field127A >= var_4)) - || (((tmpVar == 26) || (tmpVar == 24) || (tmpVar == 15) || (tmpVar == 5) || (tmpVar == 4)) && (_field126E >= var_8) && (_field126E <= 3) && (_action1._field24 != 0)) ) { - var_field127A = var_4; - if ((tmpVar == 26) || (tmpVar == 24) || (tmpVar == 15) || (tmpVar == 5) || (tmpVar == 4)) + var_4 = _mazeUI.cellFromX(newMazeX + 70) - 70; + var_8 = abs(var_4 - newMazeX); + cellId = _mazeUI.getCellFromMapXY(Common::Point(newMazeX + 70, newMazeY + 46)); + if ( (((cellId == 25) || (cellId == 23) || (cellId == 14)) && (newMazeX <= var_4) && (_mazePosition.x >= var_4)) + || (((cellId == 26) || (cellId == 24) || (cellId == 15) || (cellId == 5) || (cellId == 4)) && (_mazeChangeAmount >= var_8) && (_mazeChangeAmount <= 3) && (_action1._field24 != 0)) ) { + newMazeX = var_4; + if ((cellId == 26) || (cellId == 24) || (cellId == 15) || (cellId == 5) || (cellId == 4)) R2_GLOBALS._sound2.play(339); _rotation->_idxChange = 0; _field1270 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1272 = 0; if (_action1._field24 == 0) _actor8.hide(); - } else if ((tmpVar == 1) && (var_field127A >= var_4 + 5) && (_field127A >= var_4 + 5)) { - var_field127A = var_4 + 5; + } else if ((cellId == 1) && (newMazeX >= var_4 + 5) && (_mazePosition.x >= var_4 + 5)) { + newMazeX = var_4 + 5; R2_GLOBALS._sound2.play(339); _rotation->_idxChange = 0; _field1270 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1272 = 0; if (_action1._field24 == 0) _actor8.hide(); - } else if (((tmpVar == 26) || (tmpVar == 24) || (tmpVar == 15) || (tmpVar == 5) || (tmpVar == 4)) && (var_a != 0) && (var_a <= 3)) { - di = var_6; + } else if (((cellId == 26) || (cellId == 24) || (cellId == 15) || (cellId == 5) || (cellId == 4)) && (var_a != 0) && (var_a <= 3)) { + newMazeY = var_6; R2_GLOBALS._sound2.play(339); } else { // Nothing @@ -4114,49 +4118,49 @@ void Scene3500::dispatch() { } if (_field1284 < 2) { - _field127A = var_field127A; - _field127C = di; - if (_unkObj1.sub109C5E(_field127A, _field127C) != 0) { + _mazePosition.x = newMazeX; + _mazePosition.y = newMazeY; + if (_mazeUI.setMazePosition2(_mazePosition) != 0) { _field1272 = 0; - _field126E = 0; + _mazeChangeAmount = 0; _field1270 = 0; _rotation->setDelay(0); _rotation->_idxChange = 0; } - warning("gfx_set_pane_p"); - _unkObj1.sub51B02(); + + _mazeUI.draw(); if (_field1284 != 0) ++_field1284; } } if (_field1272 == 0) { - if (_field126E != _field1270) { - if (_field126E >= _field1270) { - if (_field126E == 1) { + if (_mazeChangeAmount != _field1270) { + if (_mazeChangeAmount >= _field1270) { + if (_mazeChangeAmount == 1) { if (_action1._field24 != 0) { - if ( ((_field1276 == 1) && (var_8 == 0) && (var_a != 0) && (var_a <= 3) && ((tmpVar == 25) || (tmpVar == 26) || (tmpVar == 5) || (tmpVar == 14) || (tmpVar == 15))) - || ((_field1276 == 3) && (var_a == 0) && (var_8 != 0) && (var_8 <= 3) && ((tmpVar == 25) || (tmpVar == 23) || (tmpVar == 14) || (tmpVar == 5) || (tmpVar == 4))) - || ((_field1276 == 5) && (var_8 == 0) && (var_a != 0) && (var_a <= 3) && ((tmpVar == 23) || (tmpVar == 24) || (tmpVar == 4) || (tmpVar == 14) || (tmpVar == 15))) - || ((_field1276 == 7) && (var_a == 0) && (var_8 != 0) && (var_8 <= 3) && ((tmpVar == 26) || (tmpVar == 24) || (tmpVar == 15) || (tmpVar == 5) || (tmpVar == 4))) ){ - _field126E = 1; + if ( ((_mazeDirection == 1) && (var_8 == 0) && (var_a != 0) && (var_a <= 3) && ((cellId == 25) || (cellId == 26) || (cellId == 5) || (cellId == 14) || (cellId == 15))) + || ((_mazeDirection == 3) && (var_a == 0) && (var_8 != 0) && (var_8 <= 3) && ((cellId == 25) || (cellId == 23) || (cellId == 14) || (cellId == 5) || (cellId == 4))) + || ((_mazeDirection == 5) && (var_8 == 0) && (var_a != 0) && (var_a <= 3) && ((cellId == 23) || (cellId == 24) || (cellId == 4) || (cellId == 14) || (cellId == 15))) + || ((_mazeDirection == 7) && (var_a == 0) && (var_8 != 0) && (var_8 <= 3) && ((cellId == 26) || (cellId == 24) || (cellId == 15) || (cellId == 5) || (cellId == 4))) ){ + _mazeChangeAmount = 1; } else - _field126E--; + _mazeChangeAmount--; } else - _field126E--; + _mazeChangeAmount--; } else - _field126E--; + _mazeChangeAmount--; } else - ++_field126E; + ++_mazeChangeAmount; _field1272 = 1; } - _actor2.setFrame2(_field126E); + _actor2.setFrame2(_mazeChangeAmount + 1); } if (_field1272 == 1) { - if (_field126E == 0) + if (_mazeChangeAmount == 0) _rotation->_idxChange = 0; - else if (_field126E > 8) + else if (_mazeChangeAmount > 8) _rotation->_idxChange = 2; else _rotation->_idxChange = 1; @@ -4165,9 +4169,9 @@ void Scene3500::dispatch() { if (_field1272 != 0) _field1272--; - if (_field126E != 0) { - R2_GLOBALS._player._uiEnabled = false; - if (_field126E != _field1270) + if (_mazeChangeAmount != 0) { + R2_GLOBALS._player._uiEnabled = false; + if (_mazeChangeAmount != _field1270) _aSound1.play(276); } else { R2_GLOBALS._player._uiEnabled = true; diff --git a/engines/tsage/ringworld2/ringworld2_scenes3.h b/engines/tsage/ringworld2/ringworld2_scenes3.h index 44787b9eef..85e5674433 100644 --- a/engines/tsage/ringworld2/ringworld2_scenes3.h +++ b/engines/tsage/ringworld2/ringworld2_scenes3.h @@ -633,12 +633,12 @@ class Scene3500 : public SceneExt { // TODO: double check if nothing specific is present, then remove this class }; - class UnkObject3500 : public UnkObject1200 { + class MazeUI3500 : public MazeUI { public: - int sub1097C9(int arg1); - int sub1097EF(int arg1); - int sub109C09(Common::Point pt); - int sub109C5E(int &x, int &y); + int cellFromX(int arg1); + int cellFromY(int arg1); + int getCellFromMapXY(Common::Point pt); + bool setMazePosition2(Common::Point &p); }; public: Action1 _action1; @@ -650,6 +650,7 @@ public: Item4 _item5; Item4 _item6; Item4 _item7; + // Glyph of vessel on top of the maze ui SceneActor _actor1; SceneActor _actor2; SceneActor _actor3; @@ -660,20 +661,19 @@ public: Actor8 _actor8; Actor8 _actor9; ASoundExt _aSound1; - UnkObject3500 _unkObj1; + MazeUI3500 _mazeUI; SequenceManager _sequenceManager; int _fieldAF8; int _fieldB9E; PaletteRotation *_rotation; - int _field126E; + int _mazeChangeAmount; int _field1270; int _field1272; int _field1274; - int _field1276; + int _mazeDirection; int _field1278; - int _field127A; - int _field127C; + Common::Point _mazePosition; int _field127E; int _field1280; int _field1282; diff --git a/engines/wintermute/wintermute.cpp b/engines/wintermute/wintermute.cpp index 89a6f1b3e0..19848b002e 100644 --- a/engines/wintermute/wintermute.cpp +++ b/engines/wintermute/wintermute.cpp @@ -26,7 +26,6 @@ #include "common/debug.h" #include "common/debug-channels.h" #include "common/error.h" -#include "common/EventRecorder.h" #include "common/file.h" #include "common/fs.h" #include "common/tokenizer.h" diff --git a/graphics/cursorman.cpp b/graphics/cursorman.cpp index c818101645..6825767dfd 100644 --- a/graphics/cursorman.cpp +++ b/graphics/cursorman.cpp @@ -48,6 +48,9 @@ bool CursorManager::isVisible() { bool CursorManager::showMouse(bool visible) { if (_cursorStack.empty()) return false; + if (_locked) { + return false; + } _cursorStack.top()->_visible = visible; @@ -225,6 +228,10 @@ void CursorManager::replaceCursorPalette(const byte *colors, uint start, uint nu } } +void CursorManager::lock(bool locked) { + _locked = locked; +} + CursorManager::Cursor::Cursor(const void *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) { #ifdef USE_RGB_COLOR if (!format) diff --git a/graphics/cursorman.h b/graphics/cursorman.h index 66e8d1ba56..b4d8ad94ce 100644 --- a/graphics/cursorman.h +++ b/graphics/cursorman.h @@ -160,12 +160,15 @@ public: */ void replaceCursorPalette(const byte *colors, uint start, uint num); + void lock(bool locked); private: friend class Common::Singleton<SingletonBaseType>; // Even though this is basically the default constructor we implement it // ourselves, so it is private and thus there is no way to create this class // except from the Singleton code. - CursorManager() {} + CursorManager() { + _locked = false; + } ~CursorManager(); struct Cursor { @@ -198,6 +201,7 @@ private: }; Common::Stack<Cursor *> _cursorStack; Common::Stack<Palette *> _cursorPaletteStack; + bool _locked; }; } // End of namespace Graphics diff --git a/graphics/scaler.h b/graphics/scaler.h index 1e5b796631..54d022d202 100644 --- a/graphics/scaler.h +++ b/graphics/scaler.h @@ -89,4 +89,10 @@ extern bool createThumbnailFromScreen(Graphics::Surface *surf); */ extern bool createThumbnail(Graphics::Surface *surf, const uint8 *pixels, int w, int h, const uint8 *palette); +/** + * Downscale screenshot to thumbnale size. + * + */ +extern bool createThumbnail(Graphics::Surface &out, Graphics::Surface &in); + #endif diff --git a/graphics/scaler/thumbnail_intern.cpp b/graphics/scaler/thumbnail_intern.cpp index 88f3cc2077..8a98263eee 100644 --- a/graphics/scaler/thumbnail_intern.cpp +++ b/graphics/scaler/thumbnail_intern.cpp @@ -134,7 +134,7 @@ static bool grabScreen565(Graphics::Surface *surf) { return true; } -static bool createThumbnail(Graphics::Surface &out, Graphics::Surface &in) { +bool createThumbnail(Graphics::Surface &out, Graphics::Surface &in) { uint16 width = in.w; uint16 inHeight = in.h; @@ -206,7 +206,7 @@ static bool createThumbnail(Graphics::Surface &out, Graphics::Surface &in) { return true; } -bool createThumbnailFromScreen(Graphics::Surface* surf) { +bool createThumbnailFromScreen(Graphics::Surface *surf) { assert(surf); Graphics::Surface screen; @@ -236,3 +236,31 @@ bool createThumbnail(Graphics::Surface *surf, const uint8 *pixels, int w, int h, return createThumbnail(*surf, screen); } + +// this is somewhat awkward, but createScreenShot should logically be in graphics, +// but moving other functions in this file into that namespace breaks several engines +namespace Graphics { +bool createScreenShot(Graphics::Surface &surf) { + Graphics::PixelFormat screenFormat = g_system->getScreenFormat(); + //convert surface to 2 bytes pixel format to avoid problems with palette saving and loading + if ((screenFormat.bytesPerPixel == 1) || (screenFormat.bytesPerPixel == 2)) { + return grabScreen565(&surf); + } else { + Graphics::Surface *screen = g_system->lockScreen(); + if (!screen) { + return false; + } + surf.create(screen->w, screen->h, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)); + for (uint y = 0; y < screen->h; ++y) { + for (uint x = 0; x < screen->w; ++x) { + byte r = 0, g = 0, b = 0, a = 0; + uint32 col = READ_UINT32(screen->getBasePtr(x, y)); + screenFormat.colorToARGB(col, a, r, g, b); + ((uint32 *)surf.pixels)[y * surf.w + x] = Graphics::ARGBToColor<Graphics::ColorMasks<8888> >(a, r, g, b); + } + } + g_system->unlockScreen(); + return true; + } +} +} // End of namespace Graphics diff --git a/graphics/thumbnail.cpp b/graphics/thumbnail.cpp index ddb377306d..d04c218624 100644 --- a/graphics/thumbnail.cpp +++ b/graphics/thumbnail.cpp @@ -23,6 +23,7 @@ #include "graphics/scaler.h" #include "graphics/colormasks.h" #include "common/endian.h" +#include "common/algorithm.h" #include "common/system.h" #include "common/stream.h" #include "common/textconsole.h" @@ -143,7 +144,6 @@ Graphics::Surface *loadThumbnail(Common::SeekableReadStream &in) { assert(0); } } - return to; } @@ -216,4 +216,55 @@ bool saveThumbnail(Common::WriteStream &out, const Graphics::Surface &thumb) { return true; } + +/** + * Returns an array indicating which pixels of a source image horizontally or vertically get + * included in a scaled image + */ +int *scaleLine(int size, int srcSize) { + int scale = 100 * size / srcSize; + assert(scale > 0); + int *v = new int[size]; + Common::fill(v, &v[size], 0); + + int distCtr = 0; + int *destP = v; + for (int distIndex = 0; distIndex < srcSize; ++distIndex) { + distCtr += scale; + while (distCtr >= 100) { + assert(destP < &v[size]); + *destP++ = distIndex; + distCtr -= 100; + } + } + + return v; +} + +Graphics::Surface *scale(const Graphics::Surface &srcImage, int xSize, int ySize) { + Graphics::Surface *s = new Graphics::Surface(); + s->create(xSize, ySize, srcImage.format); + + int *horizUsage = scaleLine(xSize, srcImage.w); + int *vertUsage = scaleLine(ySize, srcImage.h); + + // Loop to create scaled version + for (int yp = 0; yp < ySize; ++yp) { + const byte *srcP = (const byte *)srcImage.getBasePtr(0, vertUsage[yp]); + byte *destP = (byte *)s->getBasePtr(0, yp); + + for (int xp = 0; xp < xSize; ++xp) { + const byte *tempSrcP = srcP + (horizUsage[xp] * srcImage.format.bytesPerPixel); + for (int byteCtr = 0; byteCtr < srcImage.format.bytesPerPixel; ++byteCtr) { + *destP++ = *tempSrcP++; + } + } + } + + // Delete arrays and return surface + delete[] horizUsage; + delete[] vertUsage; + return s; +} + } // End of namespace Graphics diff --git a/graphics/thumbnail.h b/graphics/thumbnail.h index 45a0fdbf07..c857809c91 100644 --- a/graphics/thumbnail.h +++ b/graphics/thumbnail.h @@ -64,6 +64,24 @@ bool saveThumbnail(Common::WriteStream &out); */ bool saveThumbnail(Common::WriteStream &out, const Graphics::Surface &thumb); +/** + * Grabs framebuffer into surface + * + * @param surf a surface + * @return false if a error occurred + */ +bool createScreenShot(Graphics::Surface &surf); + +/** + * Scales a passed surface, creating a new surface with the result + * @param srcImage Source image to scale + * @param xSize New surface width + * @param ySize New surface height + * @remarks Caller is responsible for freeing the returned surface + */ +Graphics::Surface *scale(const Graphics::Surface &srcImage, int xSize, int ySize); + + } // End of namespace Graphics #endif diff --git a/gui/EventRecorder.cpp b/gui/EventRecorder.cpp new file mode 100644 index 0000000000..47358a0b3d --- /dev/null +++ b/gui/EventRecorder.cpp @@ -0,0 +1,665 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#include "gui/EventRecorder.h" + +namespace Common { +DECLARE_SINGLETON(GUI::EventRecorder); +} + +#ifdef ENABLE_EVENTRECORDER + +#include "common/debug-channels.h" +#include "backends/timer/sdl/sdl-timer.h" +#include "backends/mixer/sdl/sdl-mixer.h" +#include "common/config-manager.h" +#include "common/md5.h" +#include "gui/gui-manager.h" +#include "gui/widget.h" +#include "gui/onscreendialog.h" +#include "common/random.h" +#include "common/savefile.h" +#include "common/textconsole.h" +#include "graphics/thumbnail.h" +#include "graphics/surface.h" +#include "graphics/scaler.h" + +namespace GUI { + + +const int kMaxRecordsNames = 0x64; +const int kDefaultScreenshotPeriod = 60000; +const int kDefaultBPP = 2; + +uint32 readTime(Common::ReadStream *inFile) { + uint32 d = inFile->readByte(); + if (d == 0xff) { + d = inFile->readUint32LE(); + } + + return d; +} + +void writeTime(Common::WriteStream *outFile, uint32 d) { + //Simple RLE compression + if (d >= 0xff) { + outFile->writeByte(0xff); + outFile->writeUint32LE(d); + } else { + outFile->writeByte(d); + } +} + +EventRecorder::EventRecorder() { + _timerManager = NULL; + _recordMode = kPassthrough; + _fakeMixerManager = NULL; + _initialized = false; + _needRedraw = false; + _fastPlayback = false; + DebugMan.addDebugChannel(kDebugLevelEventRec, "EventRec", "Event recorder debug level"); +} + +EventRecorder::~EventRecorder() { + if (_timerManager != NULL) { + delete _timerManager; + } +} + +void EventRecorder::deinit() { + if (!_initialized) { + return; + } + setFileHeader(); + _needRedraw = false; + _initialized = false; + _recordMode = kPassthrough; + delete _fakeMixerManager; + _fakeMixerManager = NULL; + controlPanel->close(); + delete controlPanel; + debugC(1, kDebugLevelEventRec, "playback:action=stopplayback"); + g_system->getEventManager()->getEventDispatcher()->unregisterSource(this); + _recordMode = kPassthrough; + _playbackFile->close(); + delete _playbackFile; + switchMixer(); + switchTimerManagers(); + DebugMan.disableDebugChannel("EventRec"); +} + +void EventRecorder::processMillis(uint32 &millis, bool skipRecord) { + if (!_initialized) { + return; + } + if (skipRecord) { + millis = _fakeTimer; + return; + } + if (_recordMode == kRecorderPlaybackPause) { + millis = _fakeTimer; + } + uint32 millisDelay; + Common::RecorderEvent timerEvent; + switch (_recordMode) { + case kRecorderRecord: + updateSubsystems(); + millisDelay = millis - _lastMillis; + _lastMillis = millis; + _fakeTimer += millisDelay; + controlPanel->setReplayedTime(_fakeTimer); + timerEvent.recordedtype = Common::kRecorderEventTypeTimer; + timerEvent.time = _fakeTimer; + _playbackFile->writeEvent(timerEvent); + takeScreenshot(); + _timerManager->handler(); + break; + case kRecorderPlayback: + updateSubsystems(); + if (_nextEvent.recordedtype == Common::kRecorderEventTypeTimer) { + _fakeTimer = _nextEvent.time; + _nextEvent = _playbackFile->getNextEvent(); + _timerManager->handler(); + } else { + if (_nextEvent.type == Common::EVENT_RTL) { + error("playback:action=stopplayback"); + } else { + uint32 seconds = _fakeTimer / 1000; + Common::String screenTime = Common::String::format("%.2d:%.2d:%.2d", seconds / 3600 % 24, seconds / 60 % 60, seconds % 60); + error("playback:action=error reason=\"synchronization error\" time = %s", screenTime.c_str()); + } + } + millis = _fakeTimer; + controlPanel->setReplayedTime(_fakeTimer); + break; + case kRecorderPlaybackPause: + millis = _fakeTimer; + break; + default: + break; + } +} + +bool EventRecorder::processDelayMillis() { + return _fastPlayback; +} + +void EventRecorder::checkForKeyCode(const Common::Event &event) { + if ((event.type == Common::EVENT_KEYDOWN) && (event.kbd.flags & Common::KBD_CTRL) && (event.kbd.keycode == Common::KEYCODE_p) && (!event.synthetic)) { + togglePause(); + } +} + +bool EventRecorder::pollEvent(Common::Event &ev) { + if ((_recordMode != kRecorderPlayback) || !_initialized) + return false; + + if ((_nextEvent.recordedtype == Common::kRecorderEventTypeTimer) || (_nextEvent.type == Common::EVENT_INVALID)) { + return false; + } + + switch (_nextEvent.type) { + 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: + g_system->warpMouse(_nextEvent.mouse.x, _nextEvent.mouse.y); + break; + default: + break; + } + ev = _nextEvent; + _nextEvent = _playbackFile->getNextEvent(); + return true; +} + +void EventRecorder::switchFastMode() { + if (_recordMode == kRecorderPlaybackPause) { + _fastPlayback = !_fastPlayback; + } +} + +void EventRecorder::togglePause() { + RecordMode oldState; + switch (_recordMode) { + case kRecorderPlayback: + case kRecorderRecord: + oldState = _recordMode; + _recordMode = kRecorderPlaybackPause; + controlPanel->runModal(); + _recordMode = oldState; + _initialized = true; + break; + case kRecorderPlaybackPause: + controlPanel->close(); + break; + default: + break; + } +} + +void EventRecorder::RegisterEventSource() { + g_system->getEventManager()->getEventDispatcher()->registerMapper(this, false); +} + +uint32 EventRecorder::getRandomSeed(const Common::String &name) { + uint32 result = g_system->getMillis(); + if (_recordMode == kRecorderRecord) { + _playbackFile->getHeader().randomSourceRecords[name] = result; + } else if (_recordMode == kRecorderPlayback) { + result = _playbackFile->getHeader().randomSourceRecords[name]; + } + return result; +} + +Common::String EventRecorder::generateRecordFileName(const Common::String &target) { + Common::String pattern(target+".r??"); + Common::StringArray files = g_system->getSavefileManager()->listSavefiles(pattern); + for (int i = 0; i < kMaxRecordsNames; ++i) { + Common::String recordName = Common::String::format("%s.r%02d", target.c_str(), i); + if (find(files.begin(), files.end(), recordName) != files.end()) { + continue; + } + return recordName; + } + return ""; +} + + +void EventRecorder::init(Common::String recordFileName, RecordMode mode) { + _fakeMixerManager = new NullSdlMixerManager(); + _fakeMixerManager->init(); + _fakeMixerManager->suspendAudio(); + _fakeTimer = 0; + _lastMillis = g_system->getMillis(); + _playbackFile = new Common::PlaybackFile(); + _lastScreenshotTime = 0; + _recordMode = mode; + _needcontinueGame = false; + if (ConfMan.hasKey("disable_display")) { + DebugMan.enableDebugChannel("EventRec"); + gDebugLevel = 1; + } + if (_recordMode == kRecorderPlayback) { + debugC(1, kDebugLevelEventRec, "playback:action=\"Load file\" filename=%s", recordFileName.c_str()); + } + g_system->getEventManager()->getEventDispatcher()->registerSource(this, false); + _screenshotPeriod = ConfMan.getInt("screenshot_period"); + if (_screenshotPeriod == 0) { + _screenshotPeriod = kDefaultScreenshotPeriod; + } + if (!openRecordFile(recordFileName)) { + deinit(); + error("playback:action=error reason=\"Record file loading error\""); + return; + } + if (_recordMode != kPassthrough) { + controlPanel = new GUI::OnScreenDialog(_recordMode == kRecorderRecord); + } + if (_recordMode == kRecorderPlayback) { + applyPlaybackSettings(); + _nextEvent = _playbackFile->getNextEvent(); + } + if (_recordMode == kRecorderRecord) { + getConfig(); + } + + switchMixer(); + switchTimerManagers(); + _needRedraw = true; + _initialized = true; +} + + +/** + * Opens or creates file depend of recording mode. + * + *@param id of recording or playing back game + *@return true in case of success, false in case of error + * + */ +bool EventRecorder::openRecordFile(const Common::String &fileName) { + bool result; + switch (_recordMode) { + case kRecorderRecord: + return _playbackFile->openWrite(fileName); + case kRecorderPlayback: + _recordMode = kPassthrough; + result = _playbackFile->openRead(fileName); + _recordMode = kRecorderPlayback; + return result; + default: + return false; + } + return true; +} + +bool EventRecorder::checkGameHash(const ADGameDescription *gameDesc) { + if ((gameDesc == NULL) && (_playbackFile->getHeader().hashRecords.size() != 0)) { + warning("Engine doesn't contain description table"); + return false; + } + for (const ADGameFileDescription *fileDesc = gameDesc->filesDescriptions; fileDesc->fileName; fileDesc++) { + if (_playbackFile->getHeader().hashRecords.find(fileDesc->fileName) == _playbackFile->getHeader().hashRecords.end()) { + warning("MD5 hash for file %s not found in record file", fileDesc->fileName); + debugC(1, kDebugLevelEventRec, "playback:action=\"Check game hash\" filename=%s filehash=%s storedhash=\"\" result=different", fileDesc->fileName, fileDesc->md5); + return false; + } + if (_playbackFile->getHeader().hashRecords[fileDesc->fileName] != fileDesc->md5) { + warning("Incorrect version of game file %s. Stored MD5 is %s. MD5 of loaded game is %s", fileDesc->fileName, _playbackFile->getHeader().hashRecords[fileDesc->fileName].c_str(), fileDesc->md5); + debugC(1, kDebugLevelEventRec, "playback:action=\"Check game hash\" filename=%s filehash=%s storedhash=%s result=different", fileDesc->fileName, fileDesc->md5, _playbackFile->getHeader().hashRecords[fileDesc->fileName].c_str()); + return false; + } + debugC(1, kDebugLevelEventRec, "playback:action=\"Check game hash\" filename=%s filehash=%s storedhash=%s result=equal", fileDesc->fileName, fileDesc->md5, _playbackFile->getHeader().hashRecords[fileDesc->fileName].c_str()); + } + return true; +} + +void EventRecorder::registerMixerManager(SdlMixerManager *mixerManager) { + _realMixerManager = mixerManager; +} + +void EventRecorder::switchMixer() { + if (_recordMode == kPassthrough) { + _realMixerManager->resumeAudio(); + } else { + _realMixerManager->suspendAudio(); + _fakeMixerManager->resumeAudio(); + } +} + +SdlMixerManager *EventRecorder::getMixerManager() { + if (_recordMode == kPassthrough) { + return _realMixerManager; + } else { + return _fakeMixerManager; + } +} + +void EventRecorder::getConfigFromDomain(Common::ConfigManager::Domain *domain) { + for (Common::ConfigManager::Domain::iterator entry = domain->begin(); entry!= domain->end(); ++entry) { + _playbackFile->getHeader().settingsRecords[entry->_key] = entry->_value; + } +} + +void EventRecorder::getConfig() { + getConfigFromDomain(ConfMan.getDomain(ConfMan.kApplicationDomain)); + getConfigFromDomain(ConfMan.getActiveDomain()); + _playbackFile->getHeader().settingsRecords["save_slot"] = ConfMan.get("save_slot"); +} + + +void EventRecorder::applyPlaybackSettings() { + for (Common::StringMap::iterator i = _playbackFile->getHeader().settingsRecords.begin(); i != _playbackFile->getHeader().settingsRecords.end(); ++i) { + Common::String currentValue = ConfMan.get(i->_key); + if (currentValue != i->_value) { + ConfMan.set(i->_key, i->_value, ConfMan.kTransientDomain); + debugC(1, kDebugLevelEventRec, "playback:action=\"Apply settings\" key=%s storedvalue=%s currentvalue=%s result=different", i->_key.c_str(), i->_value.c_str(), currentValue.c_str()); + } else { + debugC(1, kDebugLevelEventRec, "playback:action=\"Apply settings\" key=%s storedvalue=%s currentvalue=%s result=equal", i->_key.c_str(), i->_value.c_str(), currentValue.c_str()); + } + } + removeDifferentEntriesInDomain(ConfMan.getDomain(ConfMan.kApplicationDomain)); + removeDifferentEntriesInDomain(ConfMan.getActiveDomain()); +} + +void EventRecorder::removeDifferentEntriesInDomain(Common::ConfigManager::Domain *domain) { + for (Common::ConfigManager::Domain::iterator entry = domain->begin(); entry!= domain->end(); ++entry) { + if (_playbackFile->getHeader().settingsRecords.find(entry->_key) == _playbackFile->getHeader().settingsRecords.end()) { + debugC(1, kDebugLevelEventRec, "playback:action=\"Apply settings\" checksettings:key=%s storedvalue=%s currentvalue="" result=different", entry->_key.c_str(), entry->_value.c_str()); + domain->erase(entry->_key); + } + } +} + +DefaultTimerManager *EventRecorder::getTimerManager() { + return _timerManager; +} + +void EventRecorder::registerTimerManager(DefaultTimerManager *timerManager) { + _timerManager = timerManager; +} + +void EventRecorder::switchTimerManagers() { + delete _timerManager; + if (_recordMode == kPassthrough) { + _timerManager = new SdlTimerManager(); + } else { + _timerManager = new DefaultTimerManager(); + } +} + +void EventRecorder::updateSubsystems() { + if (_recordMode == kPassthrough) { + return; + } + RecordMode oldRecordMode = _recordMode; + _recordMode = kPassthrough; + _fakeMixerManager->update(); + _recordMode = oldRecordMode; +} + +Common::List<Common::Event> EventRecorder::mapEvent(const Common::Event &ev, Common::EventSource *source) { + if ((!_initialized) && (_recordMode != kRecorderPlaybackPause)) { + return DefaultEventMapper::mapEvent(ev, source); + } + + checkForKeyCode(ev); + Common::Event evt = ev; + evt.mouse.x = evt.mouse.x * (g_system->getOverlayWidth() / g_system->getWidth()); + evt.mouse.y = evt.mouse.y * (g_system->getOverlayHeight() / g_system->getHeight()); + switch (_recordMode) { + case kRecorderPlayback: + if (ev.synthetic != true) { + return Common::List<Common::Event>(); + } + return Common::DefaultEventMapper::mapEvent(ev, source); + break; + case kRecorderRecord: + g_gui.processEvent(evt, controlPanel); + if (((evt.type == Common::EVENT_LBUTTONDOWN) || (evt.type == Common::EVENT_LBUTTONUP) || (evt.type == Common::EVENT_MOUSEMOVE)) && controlPanel->isMouseOver()) { + return Common::List<Common::Event>(); + } else { + Common::RecorderEvent e; + memcpy(&e, &ev, sizeof(ev)); + e.recordedtype = Common::kRecorderEventTypeNormal; + e.time = _fakeTimer; + _playbackFile->writeEvent(e); + return DefaultEventMapper::mapEvent(ev, source); + } + break; + case kRecorderPlaybackPause: { + Common::Event dialogEvent; + if (controlPanel->isEditDlgVisible()) { + dialogEvent = ev; + } else { + dialogEvent = evt; + } + g_gui.processEvent(dialogEvent, controlPanel->getActiveDlg()); + if (((dialogEvent.type == Common::EVENT_LBUTTONDOWN) || (dialogEvent.type == Common::EVENT_LBUTTONUP) || (dialogEvent.type == Common::EVENT_MOUSEMOVE)) && controlPanel->isMouseOver()) { + return Common::List<Common::Event>(); + } + return Common::DefaultEventMapper::mapEvent(dialogEvent, source); + } + break; + default: + return Common::DefaultEventMapper::mapEvent(ev, source); + } +} + +void EventRecorder::setGameMd5(const ADGameDescription *gameDesc) { + for (const ADGameFileDescription *fileDesc = gameDesc->filesDescriptions; fileDesc->fileName; fileDesc++) { + if (fileDesc->md5 != NULL) { + _playbackFile->getHeader().hashRecords[fileDesc->fileName] = fileDesc->md5; + } + } +} + +void EventRecorder::processGameDescription(const ADGameDescription *desc) { + if (_recordMode == kRecorderRecord) { + setGameMd5(desc); + } + if ((_recordMode == kRecorderPlayback) && !checkGameHash(desc)) { + deinit(); + error("playback:action=error reason=\"\""); + } +} + +void EventRecorder::deleteRecord(const Common::String& fileName) { + g_system->getSavefileManager()->removeSavefile(fileName); +} + +void EventRecorder::takeScreenshot() { + if ((_fakeTimer - _lastScreenshotTime) > _screenshotPeriod) { + Graphics::Surface screen; + uint8 md5[16]; + if (grabScreenAndComputeMD5(screen, md5)) { + _lastScreenshotTime = _fakeTimer; + _playbackFile->saveScreenShot(screen, md5); + screen.free(); + } + } +} + +bool EventRecorder::grabScreenAndComputeMD5(Graphics::Surface &screen, uint8 md5[16]) { + if (!createScreenShot(screen)) { + warning("Can't save screenshot"); + return false; + } + Common::MemoryReadStream bitmapStream((const byte*)screen.pixels, screen.w * screen.h * screen.format.bytesPerPixel); + computeStreamMD5(bitmapStream, md5); + return true; +} + +Common::SeekableReadStream *EventRecorder::processSaveStream(const Common::String &fileName) { + Common::InSaveFile *saveFile; + switch (_recordMode) { + case kRecorderPlayback: + debugC(1, kDebugLevelEventRec, "playback:action=\"Process save file\" filename=%s len=%d", fileName.c_str(), _playbackFile->getHeader().saveFiles[fileName].size); + return new Common::MemoryReadStream(_playbackFile->getHeader().saveFiles[fileName].buffer, _playbackFile->getHeader().saveFiles[fileName].size); + case kRecorderRecord: + saveFile = _realSaveManager->openForLoading(fileName); + if (saveFile != NULL) { + _playbackFile->addSaveFile(fileName, saveFile); + saveFile->seek(0); + } + return saveFile; + default: + return NULL; + break; + } +} + +Common::SaveFileManager *EventRecorder::getSaveManager(Common::SaveFileManager *realSaveManager) { + _realSaveManager = realSaveManager; + if (_recordMode != kPassthrough) { + return &_fakeSaveManager; + } else { + return realSaveManager; + } +} + +void EventRecorder::preDrawOverlayGui() { + if ((_initialized) || (_needRedraw)) { + RecordMode oldMode = _recordMode; + _recordMode = kPassthrough; + g_system->showOverlay(); + g_gui.theme()->clearAll(); + g_gui.theme()->openDialog(true, GUI::ThemeEngine::kShadingNone); + controlPanel->drawDialog(); + g_gui.theme()->finishBuffering(); + g_gui.theme()->updateScreen(); + _recordMode = oldMode; + } +} + +void EventRecorder::postDrawOverlayGui() { + if ((_initialized) || (_needRedraw)) { + RecordMode oldMode = _recordMode; + _recordMode = kPassthrough; + g_system->hideOverlay(); + _recordMode = oldMode; + } +} + +Common::StringArray EventRecorder::listSaveFiles(const Common::String &pattern) { + if (_recordMode == kRecorderPlayback) { + Common::StringArray result; + for (Common::HashMap<Common::String, Common::PlaybackFile::SaveFileBuffer>::iterator i = _playbackFile->getHeader().saveFiles.begin(); i != _playbackFile->getHeader().saveFiles.end(); ++i) { + if (i->_key.matchString(pattern, false, true)) { + result.push_back(i->_key); + } + } + return result; + } else { + return _realSaveManager->listSavefiles(pattern); + } +} + +void EventRecorder::setFileHeader() { + if (_recordMode != kRecorderRecord) { + return; + } + TimeDate t; + const EnginePlugin *plugin = 0; + GameDescriptor desc = EngineMan.findGame(ConfMan.getActiveDomainName(), &plugin); + g_system->getTimeAndDate(t); + if (_author.empty()) { + setAuthor("Unknown Author"); + } + if (_name.empty()) { + g_eventRec.setName(Common::String::format("%.2d.%.2d.%.4d ", t.tm_mday, t.tm_mon, 1900 + t.tm_year) + desc.description()); + } + _playbackFile->getHeader().author = _author; + _playbackFile->getHeader().notes = _desc; + _playbackFile->getHeader().name = _name; +} + +SDL_Surface *EventRecorder::getSurface(int width, int height) { + // Create a RGB565 surface of the requested dimensions. + return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 16, 0xF800, 0x07E0, 0x001F, 0x0000); +} + +bool EventRecorder::switchMode() { + const Common::String gameId = ConfMan.get("gameid"); + const EnginePlugin *plugin = 0; + EngineMan.findGame(gameId, &plugin); + bool metaInfoSupport = (*plugin)->hasFeature(MetaEngine::kSavesSupportMetaInfo); + bool featuresSupport = metaInfoSupport && + g_engine->canSaveGameStateCurrently() && + (*plugin)->hasFeature(MetaEngine::kSupportsListSaves) && + (*plugin)->hasFeature(MetaEngine::kSupportsDeleteSave); + if (!featuresSupport) { + return false; + } + + int emptySlot = 1; + SaveStateList saveList = (*plugin)->listSaves(gameId.c_str()); + for (SaveStateList::const_iterator x = saveList.begin(); x != saveList.end(); ++x) { + int saveSlot = x->getSaveSlot(); + if (saveSlot == 0) { + continue; + } + if (emptySlot != saveSlot) { + break; + } + emptySlot++; + } + Common::String saveName; + if (emptySlot >= 0) { + saveName = Common::String::format("Save %d", emptySlot + 1); + Common::Error status = g_engine->saveGameState(emptySlot, saveName); + if (status.getCode() == Common::kNoError) { + Common::Event eventRTL; + eventRTL.type = Common::EVENT_RTL; + g_system->getEventManager()->pushEvent(eventRTL); + } + } + ConfMan.set("record_mode", "", Common::ConfigManager::kTransientDomain); + ConfMan.setInt("save_slot", emptySlot, Common::ConfigManager::kTransientDomain); + _needcontinueGame = true; + return true; +} + +bool EventRecorder::checkForContinueGame() { + bool result = _needcontinueGame; + _needcontinueGame = false; + return result; +} + +void EventRecorder::deleteTemporarySave() { + if (_temporarySlot == -1) return; + const Common::String gameId = ConfMan.get("gameid"); + const EnginePlugin *plugin = 0; + EngineMan.findGame(gameId, &plugin); + (*plugin)->removeSaveState(gameId.c_str(), _temporarySlot); + _temporarySlot = -1; +} + +} // End of namespace GUI + +#endif // ENABLE_EVENTRECORDER + diff --git a/gui/EventRecorder.h b/gui/EventRecorder.h new file mode 100644 index 0000000000..3e32d89232 --- /dev/null +++ b/gui/EventRecorder.h @@ -0,0 +1,293 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GUI_EVENTRECORDER_H +#define GUI_EVENTRECORDER_H + +#include "common/system.h" + +#include "common/events.h" +#include "common/savefile.h" +#include "common/singleton.h" + +#include "engines/advancedDetector.h" + +#ifdef ENABLE_EVENTRECORDER + +#include "common/mutex.h" +#include "common/array.h" +#include "common/memstream.h" +#include "backends/keymapper/keymapper.h" +#include "backends/mixer/sdl/sdl-mixer.h" +#include "common/hashmap.h" +#include "common/hash-str.h" +#include "backends/timer/sdl/sdl-timer.h" +#include "common/config-manager.h" +#include "common/recorderfile.h" +#include "backends/saves/recorder/recorder-saves.h" +#include "backends/mixer/nullmixer/nullsdl-mixer.h" +#include "backends/saves/default/default-saves.h" + + +#define g_eventRec (GUI::EventRecorder::instance()) + +namespace GUI { + class OnScreenDialog; +} + +namespace GUI { +class RandomSource; +class SeekableReadStream; +class WriteStream; + + +/** + * Our generic event recorder. + * + * TODO: Add more documentation. + */ +class EventRecorder : private Common::EventSource, public Common::Singleton<EventRecorder>, private Common::DefaultEventMapper { + friend class Common::Singleton<SingletonBaseType>; + EventRecorder(); + ~EventRecorder(); +public: + /** Specify operation mode of Event Recorder */ + enum RecordMode { + kPassthrough = 0, /**< kPassthrough, do nothing */ + kRecorderRecord = 1, /**< kRecorderRecord, do the recording */ + kRecorderPlayback = 2, /**< kRecorderPlayback, playback existing recording */ + kRecorderPlaybackPause = 3 /**< kRecordetPlaybackPause, interal state when user pauses the playback */ + }; + + void init(Common::String recordFileName, RecordMode mode); + void deinit(); + bool processDelayMillis(); + uint32 getRandomSeed(const Common::String &name); + void processMillis(uint32 &millis, bool skipRecord); + bool processAudio(uint32 &samples, bool paused); + void processGameDescription(const ADGameDescription *desc); + Common::SeekableReadStream *processSaveStream(const Common::String & fileName); + + /** Hooks for intercepting into GUI processing, so required events could be shoot + * or filtered out */ + void preDrawOverlayGui(); + void postDrawOverlayGui(); + + /** Set recording author + * + * @see getAuthor + */ + void setAuthor(const Common::String &author) { + _author = author; + } + + /** Set recording notes + * + * @see getNotes + */ + void setNotes(const Common::String &desc){ + _desc = desc; + } + + /** Set descriptive name of the recording + * + * @see getName + */ + void setName(const Common::String &name) { + _name = name; + } + + /** Get recording author + * + * @see getAuthor + */ + const Common::String getAuthor() { + return _author; + } + + /** Get recording notes + * + * @see setNotes + */ + const Common::String getNotes() { + return _desc; + } + + /** Get recording name + * + * @see setName + */ + const Common::String getName() { + return _name; + } + void setRedraw(bool redraw) { + _needRedraw = redraw; + } + + void registerMixerManager(SdlMixerManager *mixerManager); + void registerTimerManager(DefaultTimerManager *timerManager); + + SdlMixerManager *getMixerManager(); + DefaultTimerManager *getTimerManager(); + + void deleteRecord(const Common::String& fileName); + bool checkForContinueGame(); + + void suspendRecording() { + _savedState = _initialized; + _initialized = false; + } + + void resumeRecording() { + _initialized = _savedState; + } + + Common::StringArray listSaveFiles(const Common::String &pattern); + Common::String generateRecordFileName(const Common::String &target); + + Common::SaveFileManager *getSaveManager(Common::SaveFileManager *realSaveManager); + SDL_Surface *getSurface(int width, int height); + void RegisterEventSource(); + + /** Retrieve game screenshot and compute its checksum for comparison */ + bool grabScreenAndComputeMD5(Graphics::Surface &screen, uint8 md5[16]); + + void updateSubsystems(); + bool switchMode(); + void switchFastMode(); + +private: + virtual Common::List<Common::Event> mapEvent(const Common::Event &ev, Common::EventSource *source); + bool notifyPoll(); + bool pollEvent(Common::Event &ev); + bool _initialized; + volatile uint32 _fakeTimer; + bool _savedState; + bool _needcontinueGame; + int _temporarySlot; + Common::String _author; + Common::String _desc; + Common::String _name; + + Common::SaveFileManager *_realSaveManager; + SdlMixerManager *_realMixerManager; + DefaultTimerManager *_timerManager; + RecorderSaveFileManager _fakeSaveManager; + NullSdlMixerManager *_fakeMixerManager; + GUI::OnScreenDialog *controlPanel; + Common::RecorderEvent _nextEvent; + + void setFileHeader(); + void setGameMd5(const ADGameDescription *gameDesc); + void getConfig(); + void getConfigFromDomain(Common::ConfigManager::Domain *domain); + void removeDifferentEntriesInDomain(Common::ConfigManager::Domain *domain); + void applyPlaybackSettings(); + + void switchMixer(); + void switchTimerManagers(); + + void togglePause(); + + void takeScreenshot(); + + bool openRecordFile(const Common::String &fileName); + + bool checkGameHash(const ADGameDescription *desc); + + void checkForKeyCode(const Common::Event &event); + bool allowMapping() const { return false; } + + volatile uint32 _lastMillis; + uint32 _lastScreenshotTime; + uint32 _screenshotPeriod; + Common::PlaybackFile *_playbackFile; + + void saveScreenShot(); + void checkRecordedMD5(); + void deleteTemporarySave(); + volatile RecordMode _recordMode; + Common::String _recordFileName; + bool _fastPlayback; + bool _needRedraw; +}; + +} // End of namespace GUI + +#else + +#ifdef SDL_BACKEND +#include "backends/timer/default/default-timer.h" +#include "backends/mixer/sdl/sdl-mixer.h" +#endif + +#define g_eventRec (GUI::EventRecorder::instance()) + +namespace GUI { + +class EventRecorder : private Common::EventSource, public Common::Singleton<EventRecorder>, private Common::DefaultEventMapper { + friend class Common::Singleton<SingletonBaseType>; + + public: + EventRecorder() { +#ifdef SDL_BACKEND + _timerManager = NULL; + _realMixerManager = NULL; +#endif + } + ~EventRecorder() {} + + bool pollEvent(Common::Event &ev) { return false; } + void RegisterEventSource() {} + void deinit() {} + void suspendRecording() {} + void resumeRecording() {} + void preDrawOverlayGui() {} + void postDrawOverlayGui() {} + void processGameDescription(const ADGameDescription *desc) {} + void updateSubsystems() {} + uint32 getRandomSeed(const Common::String &name) { return g_system->getMillis(); } + Common::SaveFileManager *getSaveManager(Common::SaveFileManager *realSaveManager) { return realSaveManager; } + +#ifdef SDL_BACKEND + private: + DefaultTimerManager *_timerManager; + SdlMixerManager *_realMixerManager; + + public: + DefaultTimerManager *getTimerManager() { return _timerManager; } + void registerTimerManager(DefaultTimerManager *timerManager) { _timerManager = timerManager; } + + SdlMixerManager *getMixerManager() { return _realMixerManager; } + void registerMixerManager(SdlMixerManager *mixerManager) { _realMixerManager = mixerManager; } + + void processMillis(uint32 &millis, bool skipRecord) {} + bool processDelayMillis() { return false; } +#endif + +}; + +} // namespace GUI + +#endif // ENABLE_EVENTRECORDER + +#endif diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp index e2fa2580f5..3ce043cb39 100644 --- a/gui/ThemeEngine.cpp +++ b/gui/ThemeEngine.cpp @@ -50,6 +50,14 @@ const char * const ThemeEngine::kImageEraser = "eraser.bmp"; const char * const ThemeEngine::kImageDelbtn = "delbtn.bmp"; const char * const ThemeEngine::kImageList = "list.bmp"; const char * const ThemeEngine::kImageGrid = "grid.bmp"; +const char * const ThemeEngine::kImageStopbtn = "stopbtn.bmp"; +const char * const ThemeEngine::kImageEditbtn = "editbtn.bmp"; +const char * const ThemeEngine::kImageSwitchModebtn = "switchbtn.bmp"; +const char * const ThemeEngine::kImageFastReplaybtn = "fastreplay.bmp"; +const char * const ThemeEngine::kImageStopSmallbtn = "stopbtn_small.bmp"; +const char * const ThemeEngine::kImageEditSmallbtn = "editbtn_small.bmp"; +const char * const ThemeEngine::kImageSwitchModeSmallbtn = "switchbtn_small.bmp"; +const char * const ThemeEngine::kImageFastReplaySmallbtn = "fastreplay_small.bmp"; struct TextDrawData { const Graphics::Font *_fontPtr; @@ -465,11 +473,7 @@ void ThemeEngine::enable() { if (_enabled) return; - if (_useCursor) { - CursorMan.pushCursorPalette(_cursorPal, 0, _cursorPalSize); - CursorMan.pushCursor(_cursor, _cursorWidth, _cursorHeight, _cursorHotspotX, _cursorHotspotY, 255, true); - CursorMan.showMouse(true); - } + showCursor(); _system->showOverlay(); clearAll(); @@ -482,10 +486,8 @@ void ThemeEngine::disable() { _system->hideOverlay(); - if (_useCursor) { - CursorMan.popCursorPalette(); - CursorMan.popCursor(); - } + hideCursor(); + _enabled = false; } @@ -1787,5 +1789,20 @@ Common::String ThemeEngine::getThemeId(const Common::String &filename) { } } +void ThemeEngine::showCursor() { + if (_useCursor) { + CursorMan.pushCursorPalette(_cursorPal, 0, _cursorPalSize); + CursorMan.pushCursor(_cursor, _cursorWidth, _cursorHeight, _cursorHotspotX, _cursorHotspotY, 255, true); + CursorMan.showMouse(true); + } +} + +void ThemeEngine::hideCursor() { + if (_useCursor) { + CursorMan.popCursorPalette(); + CursorMan.popCursor(); + } +} + } // End of namespace GUI. diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h index 6e5fd291b7..160ceb3259 100644 --- a/gui/ThemeEngine.h +++ b/gui/ThemeEngine.h @@ -234,6 +234,14 @@ public: static const char *const kImageDelbtn; ///< Delete characters in the predictive dialog static const char *const kImageList; ///< List image used in save/load chooser selection static const char *const kImageGrid; ///< Grid image used in save/load chooser selection + static const char *const kImageStopbtn; ///< Stop recording button in recorder onscreen dialog + static const char *const kImageEditbtn; ///< Edit recording metadata in recorder onscreen dialog + static const char *const kImageSwitchModebtn; ///< Switch mode button in recorder onscreen dialog + static const char *const kImageFastReplaybtn; ///< Fast playback mode button in recorder onscreen dialog + static const char *const kImageStopSmallbtn; ///< Stop recording button in recorder onscreen dialog (for 320xY) + static const char *const kImageEditSmallbtn; ///< Edit recording metadata in recorder onscreen dialog (for 320xY) + static const char *const kImageSwitchModeSmallbtn; ///< Switch mode button in recorder onscreen dialog (for 320xY) + static const char *const kImageFastReplaySmallbtn; ///< Fast playback mode button in recorder onscreen dialog (for 320xY) /** * Graphics mode enumeration. @@ -275,8 +283,13 @@ public: void refresh(); void enable(); + + void showCursor(); + void hideCursor(); + void disable(); + /** * Query the set up pixel format. */ diff --git a/gui/dialog.h b/gui/dialog.h index 1773c6633e..d269a2f645 100644 --- a/gui/dialog.h +++ b/gui/dialog.h @@ -37,6 +37,8 @@ struct Event; namespace GUI { +class EventRecorder; + class Widget; // Some "common" commands sent to handleCommand() @@ -47,6 +49,7 @@ enum { class Dialog : public GuiObject { friend class GuiManager; + friend class EventRecorder; friend class Tooltip; protected: Widget *_mouseWidget; diff --git a/gui/editrecorddialog.cpp b/gui/editrecorddialog.cpp new file mode 100644 index 0000000000..a6a7a2560e --- /dev/null +++ b/gui/editrecorddialog.cpp @@ -0,0 +1,87 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "editrecorddialog.h" +#include "gui/widgets/edittext.h" +#include "common/translation.h" + + +namespace GUI { + +const Common::String EditRecordDialog::getAuthor() { + return _authorEdit->getEditString(); +} + +void EditRecordDialog::setAuthor(const Common::String &author) { + _authorEdit->setEditString(author); +} + +const Common::String EditRecordDialog::getNotes() { + return _notesEdit->getEditString(); +} + +void EditRecordDialog::setNotes(const Common::String &desc) { + _notesEdit->setEditString(desc); +} + +const Common::String EditRecordDialog::getName() { + return _nameEdit->getEditString(); +} + +void EditRecordDialog::setName(const Common::String &name) { + _nameEdit->setEditString(name); +} + +EditRecordDialog::~EditRecordDialog() { +} + +EditRecordDialog::EditRecordDialog(const Common::String author, const Common::String name, const Common::String notes) : Dialog("EditRecordDialog") { + new StaticTextWidget(this,"EditRecordDialog.AuthorLabel",_("Author:")); + new StaticTextWidget(this,"EditRecordDialog.NameLabel",_("Name:")); + new StaticTextWidget(this,"EditRecordDialog.NotesLabel",_("Notes:")); + _authorEdit = new EditTextWidget(this, "EditRecordDialog.AuthorEdit",""); + _notesEdit = new EditTextWidget(this, "EditRecordDialog.NotesEdit",""); + _nameEdit = new EditTextWidget(this, "EditRecordDialog.NameEdit",""); + _authorEdit->setEditString(author); + _notesEdit->setEditString(notes); + _nameEdit->setEditString(name); + new GUI::ButtonWidget(this, "EditRecordDialog.Cancel", _("Cancel"), 0, kCloseCmd); + new GUI::ButtonWidget(this, "EditRecordDialog.OK", _("Ok"), 0, kOKCmd); +} + +void EditRecordDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) { + switch(cmd) { + case kCloseCmd: + setResult(kCloseCmd); + close(); + break; + case kOKCmd: + setResult(kOKCmd); + close(); + break; + default: + Dialog::handleCommand(sender, cmd, data); + break; + } +} + +} diff --git a/gui/editrecorddialog.h b/gui/editrecorddialog.h new file mode 100644 index 0000000000..c8da4521ca --- /dev/null +++ b/gui/editrecorddialog.h @@ -0,0 +1,56 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GUI_EDITRECORDDIALOG_H +#define GUI_EDITRECORDDIALOG_H + +#include "gui/dialog.h" + +namespace GUI { + +class EditTextWidget; +class StaticTextWidget; + +class EditRecordDialog : public Dialog { +private: + EditTextWidget *_notesEdit; + EditTextWidget *_nameEdit; + EditTextWidget *_authorEdit; + EditRecordDialog() : Dialog("EditRecordDialog") {}; +public: + EditRecordDialog(const Common::String author, const Common::String name, const Common::String notes); + ~EditRecordDialog(); + + const Common::String getAuthor(); + const Common::String getNotes(); + const Common::String getName(); + + void setAuthor(const Common::String &author); + void setNotes(const Common::String &desc); + void setName(const Common::String &name); + + virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); +}; + +}// End of namespace GUI + +#endif diff --git a/gui/gui-manager.cpp b/gui/gui-manager.cpp index a0ef4216aa..78b40a46ce 100644 --- a/gui/gui-manager.cpp +++ b/gui/gui-manager.cpp @@ -27,6 +27,7 @@ #include "common/rect.h" #include "common/textconsole.h" #include "common/translation.h" +#include "gui/EventRecorder.h" #include "backends/keymapper/keymapper.h" @@ -253,12 +254,13 @@ Dialog *GuiManager::getTopDialog() const { void GuiManager::runLoop() { Dialog * const activeDialog = getTopDialog(); bool didSaveState = false; - int button; - uint32 time; if (activeDialog == 0) return; + // Suspend recording while GUI is shown + g_eventRec.suspendRecording(); + if (!_stateIsSaved) { saveState(); _theme->enable(); @@ -296,10 +298,10 @@ void GuiManager::runLoop() { // _theme->updateScreen(); // _system->updateScreen(); - if (lastRedraw + waitTime < _system->getMillis()) { + if (lastRedraw + waitTime < _system->getMillis(true)) { _theme->updateScreen(); _system->updateScreen(); - lastRedraw = _system->getMillis(); + lastRedraw = _system->getMillis(true); } Common::Event event; @@ -314,72 +316,21 @@ void GuiManager::runLoop() { if (activeDialog != getTopDialog() && event.type != Common::EVENT_SCREEN_CHANGED) continue; - Common::Point mouse(event.mouse.x - activeDialog->_x, event.mouse.y - activeDialog->_y); - - switch (event.type) { - case Common::EVENT_KEYDOWN: - activeDialog->handleKeyDown(event.kbd); - break; - case Common::EVENT_KEYUP: - activeDialog->handleKeyUp(event.kbd); - break; - case Common::EVENT_MOUSEMOVE: - activeDialog->handleMouseMoved(mouse.x, mouse.y, 0); - - if (mouse.x != _lastMousePosition.x || mouse.y != _lastMousePosition.y) { - _lastMousePosition.x = mouse.x; - _lastMousePosition.y = mouse.y; - _lastMousePosition.time = _system->getMillis(); - } + processEvent(event, activeDialog); + if (event.type == Common::EVENT_MOUSEMOVE) { tooltipCheck = true; - break; - // We don't distinguish between mousebuttons (for now at least) - case Common::EVENT_LBUTTONDOWN: - case Common::EVENT_RBUTTONDOWN: - button = (event.type == Common::EVENT_LBUTTONDOWN ? 1 : 2); - time = _system->getMillis(); - if (_lastClick.count && (time < _lastClick.time + kDoubleClickDelay) - && ABS(_lastClick.x - event.mouse.x) < 3 - && ABS(_lastClick.y - event.mouse.y) < 3) { - _lastClick.count++; - } else { - _lastClick.x = event.mouse.x; - _lastClick.y = event.mouse.y; - _lastClick.count = 1; - } - _lastClick.time = time; - activeDialog->handleMouseDown(mouse.x, mouse.y, button, _lastClick.count); - break; - case Common::EVENT_LBUTTONUP: - case Common::EVENT_RBUTTONUP: - button = (event.type == Common::EVENT_LBUTTONUP ? 1 : 2); - activeDialog->handleMouseUp(mouse.x, mouse.y, button, _lastClick.count); - break; - case Common::EVENT_WHEELUP: - activeDialog->handleMouseWheel(mouse.x, mouse.y, -1); - break; - case Common::EVENT_WHEELDOWN: - activeDialog->handleMouseWheel(mouse.x, mouse.y, 1); - break; - case Common::EVENT_SCREEN_CHANGED: - screenChange(); - break; - default: -#ifdef ENABLE_KEYMAPPER - activeDialog->handleOtherEvent(event); -#endif - break; } - if (lastRedraw + waitTime < _system->getMillis()) { + + if (lastRedraw + waitTime < _system->getMillis(true)) { _theme->updateScreen(); _system->updateScreen(); - lastRedraw = _system->getMillis(); + lastRedraw = _system->getMillis(true); } } - if (tooltipCheck && _lastMousePosition.time + kTooltipDelay < _system->getMillis()) { + if (tooltipCheck && _lastMousePosition.time + kTooltipDelay < _system->getMillis(true)) { Widget *wdg = activeDialog->findWidget(_lastMousePosition.x, _lastMousePosition.y); if (wdg && wdg->hasTooltip() && !(wdg->getFlags() & WIDGET_PRESSED)) { Tooltip *tooltip = new Tooltip(); @@ -409,6 +360,9 @@ void GuiManager::runLoop() { restoreState(); _useStdCursor = false; } + + // Resume recording once GUI is shown + g_eventRec.resumeRecording(); } #pragma mark - @@ -492,7 +446,7 @@ void GuiManager::setupCursor() { // very much. We could plug in a different cursor here if we like to. void GuiManager::animateCursor() { - int time = _system->getMillis(); + int time = _system->getMillis(true); if (time > _cursorAnimateTimer + kCursorAnimateDelay) { for (int i = 0; i < 15; i++) { if ((i < 6) || (i > 8)) { @@ -537,4 +491,64 @@ void GuiManager::screenChange() { _system->updateScreen(); } +void GuiManager::processEvent(const Common::Event &event, Dialog *const activeDialog) { + int button; + uint32 time; + Common::Point mouse(event.mouse.x - activeDialog->_x, event.mouse.y - activeDialog->_y); + switch (event.type) { + case Common::EVENT_KEYDOWN: + activeDialog->handleKeyDown(event.kbd); + break; + case Common::EVENT_KEYUP: + activeDialog->handleKeyUp(event.kbd); + break; + case Common::EVENT_MOUSEMOVE: + activeDialog->handleMouseMoved(mouse.x, mouse.y, 0); + + if (mouse.x != _lastMousePosition.x || mouse.y != _lastMousePosition.y) { + _lastMousePosition.x = mouse.x; + _lastMousePosition.y = mouse.y; + _lastMousePosition.time = _system->getMillis(true); + } + + break; + // We don't distinguish between mousebuttons (for now at least) + case Common::EVENT_LBUTTONDOWN: + case Common::EVENT_RBUTTONDOWN: + button = (event.type == Common::EVENT_LBUTTONDOWN ? 1 : 2); + time = _system->getMillis(true); + if (_lastClick.count && (time < _lastClick.time + kDoubleClickDelay) + && ABS(_lastClick.x - event.mouse.x) < 3 + && ABS(_lastClick.y - event.mouse.y) < 3) { + _lastClick.count++; + } else { + _lastClick.x = event.mouse.x; + _lastClick.y = event.mouse.y; + _lastClick.count = 1; + } + _lastClick.time = time; + activeDialog->handleMouseDown(mouse.x, mouse.y, button, _lastClick.count); + break; + case Common::EVENT_LBUTTONUP: + case Common::EVENT_RBUTTONUP: + button = (event.type == Common::EVENT_LBUTTONUP ? 1 : 2); + activeDialog->handleMouseUp(mouse.x, mouse.y, button, _lastClick.count); + break; + case Common::EVENT_WHEELUP: + activeDialog->handleMouseWheel(mouse.x, mouse.y, -1); + break; + case Common::EVENT_WHEELDOWN: + activeDialog->handleMouseWheel(mouse.x, mouse.y, 1); + break; + case Common::EVENT_SCREEN_CHANGED: + screenChange(); + break; + default: + #ifdef ENABLE_KEYMAPPER + activeDialog->handleOtherEvent(event); + #endif + break; + } +} + } // End of namespace GUI diff --git a/gui/gui-manager.h b/gui/gui-manager.h index 49542fd001..b52d91ba08 100644 --- a/gui/gui-manager.h +++ b/gui/gui-manager.h @@ -35,6 +35,10 @@ namespace Graphics { class Font; } +namespace Common { + struct Event; +} + namespace GUI { class Dialog; @@ -67,6 +71,8 @@ public: // until no dialogs are active anymore. void runLoop(); + void processEvent(const Common::Event &event, Dialog *const activeDialog); + bool isActive() const { return ! _dialogStack.empty(); } bool loadNewTheme(Common::String id, ThemeEngine::GraphicsMode gfx = ThemeEngine::kGfxDisabled, bool force = false); diff --git a/gui/launcher.cpp b/gui/launcher.cpp index 4e35b54db8..77d4cce794 100644 --- a/gui/launcher.cpp +++ b/gui/launcher.cpp @@ -37,6 +37,11 @@ #include "gui/message.h" #include "gui/gui-manager.h" #include "gui/options.h" +#ifdef ENABLE_EVENTRECORDER +#include "gui/onscreendialog.h" +#include "gui/recorderdialog.h" +#include "gui/EventRecorder.h" +#endif #include "gui/saveload.h" #include "gui/widgets/edittext.h" #include "gui/widgets/list.h" @@ -596,7 +601,6 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat LauncherDialog::LauncherDialog() : Dialog(0, 0, 320, 200) { _backgroundType = GUI::ThemeEngine::kDialogBackgroundMain; - const int screenW = g_system->getOverlayWidth(); const int screenH = g_system->getOverlayHeight(); @@ -779,10 +783,9 @@ void LauncherDialog::updateListing() { } void LauncherDialog::addGame() { - int modifiers = g_system->getEventManager()->getModifierState(); #ifndef DISABLE_MASS_ADD - const bool massAdd = (modifiers & Common::KBD_SHIFT) != 0; + const bool massAdd = checkModifier(Common::KBD_SHIFT); if (massAdd) { MessageDialog alert(_("Do you really want to run the mass game detector? " @@ -975,6 +978,49 @@ void LauncherDialog::editGame(int item) { } } +void LauncherDialog::loadGameButtonPressed(int item) { +#ifdef ENABLE_EVENTRECORDER + const bool shiftPressed = checkModifier(Common::KBD_SHIFT); + if (shiftPressed) { + recordGame(item); + } else { + loadGame(item); + } + updateButtons(); +#else + loadGame(item); +#endif +} + +#ifdef ENABLE_EVENTRECORDER +void LauncherDialog::recordGame(int item) { + RecorderDialog recorderDialog; + MessageDialog alert(_("Do you want to load savegame?"), + _("Yes"), _("No")); + switch(recorderDialog.runModal(_domains[item])) { + case RecorderDialog::kRecordDialogClose: + break; + case RecorderDialog::kRecordDialogPlayback: + ConfMan.setActiveDomain(_domains[item]); + close(); + ConfMan.set("record_mode", "playback", ConfigManager::kTransientDomain); + ConfMan.set("record_file_name", recorderDialog.getFileName(), ConfigManager::kTransientDomain); + break; + case RecorderDialog::kRecordDialogRecord: + ConfMan.setActiveDomain(_domains[item]); + if (alert.runModal() == GUI::kMessageOK) { + loadGame(item); + } + close(); + g_eventRec.setAuthor(recorderDialog._author); + g_eventRec.setName(recorderDialog._name); + g_eventRec.setNotes(recorderDialog._notes); + ConfMan.set("record_mode", "record", ConfigManager::kTransientDomain); + break; + } +} +#endif + void LauncherDialog::loadGame(int item) { String gameId = ConfMan.get("gameid", _domains[item]); if (gameId.empty()) @@ -1039,7 +1085,7 @@ void LauncherDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat editGame(item); break; case kLoadGameCmd: - loadGame(item); + loadGameButtonPressed(item); break; case kOptionsCmd: { GlobalOptionsDialog options; @@ -1109,20 +1155,28 @@ void LauncherDialog::updateButtons() { _loadButton->setEnabled(en); _loadButton->draw(); } + switchButtonsText(_addButton, "~A~dd Game...", "Mass Add..."); +#ifdef ENABLE_EVENTRECORDER + switchButtonsText(_loadButton, "~L~oad...", "Record..."); +#endif +} - // Update the label of the "Add" button depending on whether shift is pressed or not - int modifiers = g_system->getEventManager()->getModifierState(); - const bool massAdd = (modifiers & Common::KBD_SHIFT) != 0; +// Update the label of the button depending on whether shift is pressed or not +void LauncherDialog::switchButtonsText(ButtonWidget *button, const char *normalText, const char *shiftedText) { + const bool shiftPressed = checkModifier(Common::KBD_SHIFT); const bool lowRes = g_system->getOverlayWidth() <= 320; - const char *newAddButtonLabel = massAdd - ? (lowRes ? _c("Mass Add...", "lowres") : _("Mass Add...")) - : (lowRes ? _c("~A~dd Game...", "lowres") : _("~A~dd Game...")); + const char *newAddButtonLabel = shiftPressed + ? (lowRes ? _c(shiftedText, "lowres") : _(shiftedText)) + : (lowRes ? _c(normalText, "lowres") : _(normalText)); - if (_addButton->getLabel() != newAddButtonLabel) - _addButton->setLabel(newAddButtonLabel); + if (button->getLabel() != newAddButtonLabel) + button->setLabel(newAddButtonLabel); } + + + void LauncherDialog::reflowLayout() { #ifndef DISABLE_FANCY_THEMES if (g_gui.xmlEval()->getVar("Globals.ShowLauncherLogo") == 1 && g_gui.theme()->supportsImages()) { @@ -1186,4 +1240,9 @@ void LauncherDialog::reflowLayout() { Dialog::reflowLayout(); } +bool LauncherDialog::checkModifier(int checkedModifier) { + int modifiers = g_system->getEventManager()->getModifierState(); + return (modifiers & checkedModifier) != 0; +} + } // End of namespace GUI diff --git a/gui/launcher.h b/gui/launcher.h index fc0484350a..2ab47be98d 100644 --- a/gui/launcher.h +++ b/gui/launcher.h @@ -56,7 +56,7 @@ protected: ListWidget *_list; ButtonWidget *_addButton; Widget *_startButton; - Widget *_loadButton; + ButtonWidget *_loadButton; Widget *_editButton; Widget *_removeButton; #ifndef DISABLE_FANCY_THEMES @@ -80,6 +80,7 @@ protected: void updateListing(); void updateButtons(); + void switchButtonsText(ButtonWidget *button, const char *normalText, const char *shiftedText); void open(); void close(); @@ -100,6 +101,16 @@ protected: void editGame(int item); /** + * Facade for "Load..."/"Record..." buttons. + */ + void loadGameButtonPressed(int item); + + /** + * Handle "Record..." button. + */ + void recordGame(int item); + + /** * Handle "Load..." button. */ void loadGame(int item); @@ -111,6 +122,8 @@ protected: * @target name of target to select */ void selectTarget(const String &target); +private: + bool checkModifier(int modifier); }; } // End of namespace GUI diff --git a/gui/module.mk b/gui/module.mk index bda3c88cd5..338e43c6a4 100644 --- a/gui/module.mk +++ b/gui/module.mk @@ -7,6 +7,7 @@ MODULE_OBJS := \ debugger.o \ dialog.o \ error.o \ + EventRecorder.o \ gui-manager.o \ launcher.o \ massadd.o \ @@ -38,6 +39,13 @@ MODULE_OBJS += \ browser.o endif +ifdef ENABLE_EVENTRECORDER +MODULE_OBJS += \ + editrecorddialog.o \ + onscreendialog.o \ + recorderdialog.o +endif + ifdef USE_FLUIDSYNTH MODULE_OBJS += \ fluidsynth-dialog.o diff --git a/gui/onscreendialog.cpp b/gui/onscreendialog.cpp new file mode 100644 index 0000000000..efe8038e68 --- /dev/null +++ b/gui/onscreendialog.cpp @@ -0,0 +1,231 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/system.h" + +#include "gui/gui-manager.h" + +#include "gui/EventRecorder.h" + +#include "common/events.h" +#include "common/rect.h" +#include "common/translation.h" + +#include "graphics/cursorman.h" + +#include "gui/editrecorddialog.h" +#include "gui/ThemeEval.h" + +#include "gui/onscreendialog.h" + +namespace GUI { + +bool OnScreenDialog::isVisible() const { + return true; +} + +enum { + kStopCmd = 'STOP', + kEditCmd = 'EDIT', + kSwitchModeCmd = 'MODE', + kFastModeCmd = 'FAST' +}; + +void OnScreenDialog::reflowLayout() { + GuiObject::reflowLayout(); +} + +void OnScreenDialog::releaseFocus() { +} + +OnScreenDialog::OnScreenDialog(bool isRecord) : Dialog("OnScreenDialog") { + _x = _y = 0; + +#ifndef DISABLE_FANCY_THEMES + if (g_gui.xmlEval()->getVar("Globals.OnScreenDialog.ShowPics") == 1 && g_gui.theme()->supportsImages()) { + GUI::PicButtonWidget *btn; + btn = new PicButtonWidget(this, "OnScreenDialog.StopButton", 0, kStopCmd, 0); + btn->useThemeTransparency(true); + + if (g_system->getOverlayWidth() > 320) + btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageStopbtn)); + else + btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageStopSmallbtn)); + + if (isRecord) { + btn = new PicButtonWidget(this, "OnScreenDialog.EditButton", 0, kEditCmd, 0); + btn->useThemeTransparency(true); + + if (g_system->getOverlayWidth() > 320) + btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageEditbtn)); + else + btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageEditSmallbtn)); + } else { + btn = new PicButtonWidget(this, "OnScreenDialog.SwitchModeButton", 0, kSwitchModeCmd, 0); + btn->useThemeTransparency(true); + if (g_system->getOverlayWidth() > 320) + btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageSwitchModebtn)); + else + btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageSwitchModeSmallbtn)); + + btn = new PicButtonWidget(this, "OnScreenDialog.FastReplayButton", 0, kFastModeCmd, 0); + btn->useThemeTransparency(true); + if (g_system->getOverlayWidth() > 320) + btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageFastReplaybtn)); + else + btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageFastReplaySmallbtn)); + } + } else +#endif + { + GUI::ButtonWidget *btn; + if (g_system->getOverlayWidth() > 320) + btn = new ButtonWidget(this, "OnScreenDialog.StopButton", "[ ]", _("Stop"), kStopCmd); + else + btn = new ButtonWidget(this, "OnScreenDialog.StopButton", "[]", _("Stop"), kStopCmd); + + if (isRecord) { + btn = new ButtonWidget(this, "OnScreenDialog.EditButton", "E", _("Edit record description"), kEditCmd); + } else { + btn = new ButtonWidget(this, "OnScreenDialog.SwitchModeButton", "G", _("Switch to Game"), kSwitchModeCmd); + + btn = new ButtonWidget(this, "OnScreenDialog.FastReplayButton", ">>", _("Fast replay"), kFastModeCmd); + } + } + + + text = new GUI::StaticTextWidget(this, "OnScreenDialog.TimeLabel", "00:00:00"); + _enableDrag = false; + _mouseOver = false; + _editDlgShown = false; +} + +void OnScreenDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { + Common::Event eventRTL; + switch (cmd) { + case kStopCmd: + eventRTL.type = Common::EVENT_RTL; + g_system->getEventManager()->pushEvent(eventRTL); + close(); + break; + case kEditCmd: + dlg = new EditRecordDialog(g_eventRec.getAuthor(), g_eventRec.getName(), g_eventRec.getNotes()); + CursorMan.lock(false); + g_eventRec.setRedraw(false); + g_system->showOverlay(); + _editDlgShown = true; + dlg->runModal(); + _editDlgShown = false; + g_system->hideOverlay(); + g_eventRec.setRedraw(true); + CursorMan.lock(true); + g_eventRec.setAuthor(((EditRecordDialog *)dlg)->getAuthor()); + g_eventRec.setName(((EditRecordDialog *)dlg)->getName()); + g_eventRec.setNotes(((EditRecordDialog *)dlg)->getNotes()); + delete dlg; + break; + case kSwitchModeCmd: + if (g_eventRec.switchMode()) { + close(); + } + break; + case kFastModeCmd: + g_eventRec.switchFastMode(); + break; + } +} + +void OnScreenDialog::setReplayedTime(uint32 newTime) { + if (newTime - lastTime > 1000) { + uint32 seconds = newTime / 1000; + text->setLabel(Common::String::format("%.2d:%.2d:%.2d", seconds / 3600 % 24, seconds / 60 % 60, seconds % 60)); + lastTime = newTime; + } +} + +OnScreenDialog::~OnScreenDialog() { +} + +void OnScreenDialog::handleMouseMoved(int x, int y, int button) { + if (_enableDrag) { + _x = _x + x - _dragPoint.x; + _y = _y + y - _dragPoint.y; + } + Dialog::handleMouseMoved(x, y, button); + if (isMouseOver(x, y)) { + if (_mouseOver == false) { + g_gui.theme()->showCursor(); + CursorMan.lock(true); + } + _mouseOver = true; + } else { + if (_mouseOver == true) { + CursorMan.lock(false); + g_gui.theme()->hideCursor(); + } + _mouseOver = false; + } +} + +void OnScreenDialog::handleMouseDown(int x, int y, int button, int clickCount) { + if (isMouseOver(x, y)) { + _dragPoint.x = x; + _dragPoint.y = y; + _enableDrag = true; + } + Dialog::handleMouseDown(x, y, button, clickCount); +} + +void OnScreenDialog::handleMouseUp(int x, int y, int button, int clickCount) { + if (isMouseOver(x, y)) { + + } + _enableDrag = false; + Dialog::handleMouseUp(x, y, button, clickCount); +} + +bool OnScreenDialog::isMouseOver(int x, int y) { + return (x >= 0 && x < _w && y >= 0 && y < _h); +} + +bool OnScreenDialog::isMouseOver() { + return _mouseOver; +} + +void OnScreenDialog::close() { + CursorMan.lock(false); + Dialog::close(); +} + +Dialog *OnScreenDialog::getActiveDlg() { + if (_editDlgShown) { + return dlg; + } else { + return this; + } +} + +bool OnScreenDialog::isEditDlgVisible() { + return _editDlgShown; +} + +} diff --git a/gui/onscreendialog.h b/gui/onscreendialog.h new file mode 100644 index 0000000000..4f3839acb6 --- /dev/null +++ b/gui/onscreendialog.h @@ -0,0 +1,64 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GUI_ONSCREENDIALOG_H +#define GUI_ONSCREENDIALOG_H + +#include "gui/dialog.h" +#include "gui/widget.h" + +namespace GUI { + +class OnScreenDialog : public Dialog { +private: + uint32 lastTime; + bool _enableDrag; + bool _mouseOver; + bool _editDlgShown; + Common::Point _dragPoint; + GUI::StaticTextWidget *text; + Dialog *dlg; + bool isMouseOver(int x, int y); +public: + OnScreenDialog(bool recordingMode); + ~OnScreenDialog(); + virtual void close(); + virtual bool isVisible() const; + virtual void reflowLayout(); + + void setReplayedTime(uint32 newTime); + + virtual void handleMouseMoved(int x, int y, int button); + virtual void handleMouseDown(int x, int y, int button, int clickCount); + virtual void handleMouseUp(int x, int y, int button, int clickCount); + void handleCommand(CommandSender *sender, uint32 cmd, uint32 data); + + bool isMouseOver(); + bool isEditDlgVisible(); + Dialog *getActiveDlg(); +protected: + virtual void releaseFocus(); +}; + +} // End of namespace GUI + +#endif diff --git a/gui/recorderdialog.cpp b/gui/recorderdialog.cpp new file mode 100644 index 0000000000..55f342d4a1 --- /dev/null +++ b/gui/recorderdialog.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. + */ + +#include "common/algorithm.h" +#include "common/bufferedstream.h" +#include "common/savefile.h" +#include "common/system.h" +#include "graphics/colormasks.h" +#include "graphics/palette.h" +#include "graphics/scaler.h" +#include "graphics/thumbnail.h" +#include "common/translation.h" +#include "gui/widgets/list.h" +#include "gui/editrecorddialog.h" +#include "gui/EventRecorder.h" +#include "gui/message.h" +#include "gui/saveload.h" +#include "common/system.h" +#include "gui/ThemeEval.h" +#include "gui/gui-manager.h" +#include "recorderdialog.h" + +#define MAX_RECORDS_NAMES 0xFF + +namespace GUI { + +enum { + kRecordCmd = 'RCRD', + kPlaybackCmd = 'PBCK', + kDeleteCmd = 'DEL ', + kNextScreenshotCmd = 'NEXT', + kPrevScreenshotCmd = 'PREV', + kEditRecordCmd = 'EDIT' +}; + +RecorderDialog::RecorderDialog() : Dialog("RecorderDialog"), _list(0), _currentScreenshot(0) { + _backgroundType = ThemeEngine::kDialogBackgroundSpecial; + + new StaticTextWidget(this, "SaveLoadChooser.Title", _("Recorder or Playback Gameplay")); + + _list = new GUI::ListWidget(this, "RecorderDialog.List"); + _list->setNumberingMode(GUI::kListNumberingOff); + + _deleteButton = new GUI::ButtonWidget(this, "RecorderDialog.Delete", _("Delete"), 0, kDeleteCmd); + new GUI::ButtonWidget(this, "RecorderDialog.Cancel", _("Cancel"), 0, kCloseCmd); + new GUI::ButtonWidget(this, "RecorderDialog.Record", _("Record"), 0, kRecordCmd); + _playbackButton = new GUI::ButtonWidget(this, "RecorderDialog.Playback", _("Playback"), 0, kPlaybackCmd); + + _editButton = new GUI::ButtonWidget(this, "RecorderDialog.Edit", _("Edit"), 0, kEditRecordCmd); + + _editButton->setEnabled(false); + _deleteButton->setEnabled(false); + _playbackButton->setEnabled(false); + + _gfxWidget = new GUI::GraphicsWidget(this, 0, 0, 10, 10); + _container = new GUI::ContainerWidget(this, 0, 0, 10, 10); + if (g_gui.xmlEval()->getVar("Globals.RecorderDialog.ExtInfo.Visible") == 1) { + new GUI::ButtonWidget(this,"RecorderDialog.NextScreenShotButton", "<", 0, kPrevScreenshotCmd); + new GUI::ButtonWidget(this, "RecorderDialog.PreviousScreenShotButton", ">", 0, kNextScreenshotCmd); + _currentScreenshotText = new StaticTextWidget(this, "RecorderDialog.currentScreenshot", "0/0"); + _authorText = new StaticTextWidget(this, "RecorderDialog.Author", _("Author: ")); + _notesText = new StaticTextWidget(this, "RecorderDialog.Notes", _("Notes: ")); + } + if (_gfxWidget) + _gfxWidget->setGfx(0); +} + + +void RecorderDialog::reflowLayout() { + if (g_gui.xmlEval()->getVar("Globals.RecorderDialog.ExtInfo.Visible") == 1) { + int16 x, y; + uint16 w, h; + + if (!g_gui.xmlEval()->getWidgetData("RecorderDialog.Thumbnail", x, y, w, h)) { + error("Error when loading position data for Recorder Thumbnails"); + } + + int thumbW = kThumbnailWidth; + int thumbH = kThumbnailHeight2; + int thumbX = x + (w >> 1) - (thumbW >> 1); + int thumbY = y + kLineHeight; + + _container->resize(x, y, w, h); + _gfxWidget->resize(thumbX, thumbY, thumbW, thumbH); + + _container->setVisible(true); + _gfxWidget->setVisible(true); + updateSelection(false); + } else { + _container->setVisible(false); + _gfxWidget->setVisible(false); + } + Dialog::reflowLayout(); +} + + + +void RecorderDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { + switch(cmd) { + case kEditRecordCmd: { + if (_list->getSelected() >= 0) { + EditRecordDialog editDlg(_fileHeaders[_list->getSelected()].author, _fileHeaders[_list->getSelected()].name, _fileHeaders[_list->getSelected()].notes); + if (editDlg.runModal() != kOKCmd) { + return; + } + _playbackFile.openRead(_fileHeaders[_list->getSelected()].fileName); + _playbackFile.getHeader().author = editDlg.getAuthor(); + _playbackFile.getHeader().name = editDlg.getName(); + _playbackFile.getHeader().notes = editDlg.getNotes(); + _playbackFile.updateHeader(); + _fileHeaders[_list->getSelected()] = _playbackFile.getHeader(); + int oldselection = _list->getSelected(); + updateList(); + _list->setSelected(oldselection); + updateSelection(true); + _playbackFile.close(); + } + } + break; + case kNextScreenshotCmd: + ++_currentScreenshot; + updateScreenshot(); + break; + case kPrevScreenshotCmd: + --_currentScreenshot; + updateScreenshot(); + break; + case kDeleteCmd: + if (_list->getSelected() >= 0) { + MessageDialog alert(_("Do you really want to delete this record?"), + _("Delete"), _("Cancel")); + if (alert.runModal() == GUI::kMessageOK) { + _playbackFile.close(); + g_eventRec.deleteRecord(_fileHeaders[_list->getSelected()].fileName); + _list->setSelected(-1); + updateList(); + } + } + break; + case GUI::kListSelectionChangedCmd: + updateSelection(true); + break; + case kRecordCmd: { + TimeDate t; + Common::String gameId = ConfMan.get("gameid", _target); + const EnginePlugin *plugin = 0; + GameDescriptor desc = EngineMan.findGame(gameId, &plugin); + g_system->getTimeAndDate(t); + EditRecordDialog editDlg("Unknown Author", Common::String::format("%.2d.%.2d.%.4d ", t.tm_mday, t.tm_mon, 1900 + t.tm_year) + desc.description(), ""); + if (editDlg.runModal() != kOKCmd) { + return; + } + _author = editDlg.getAuthor(); + _name = editDlg.getName(); + _notes = editDlg.getNotes(); + _filename = g_eventRec.generateRecordFileName(_target); + setResult(kRecordDialogRecord); + close(); + } + break; + case kPlaybackCmd: + if (_list->getSelected() >= 0) { + _filename = _fileHeaders[_list->getSelected()].fileName; + setResult(kRecordDialogPlayback); + close(); + } + break; + case kCloseCmd: + setResult(kRecordDialogClose); + default: + Dialog::handleCommand(sender, cmd, data); + } + } + +void RecorderDialog::updateList() { + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Common::String pattern(_target+".r??"); + Common::StringArray files = saveFileMan->listSavefiles(pattern); + Common::PlaybackFile file; + Common::StringArray namesList; + _fileHeaders.clear(); + for (Common::StringArray::iterator i = files.begin(); i != files.end(); ++i) { + if (file.openRead(*i)) { + namesList.push_back(file.getHeader().name); + _fileHeaders.push_back(file.getHeader()); + } + file.close(); + } + _list->setList(namesList); + _list->draw(); +} + +int RecorderDialog::runModal(Common::String &target) { + _target = target; + updateList(); + return Dialog::runModal(); +} + +RecorderDialog::~RecorderDialog() { +} + +void RecorderDialog::updateSelection(bool redraw) { + if (_list->getSelected() >= 0) { + _editButton->setEnabled(true); + _deleteButton->setEnabled(true); + _playbackButton->setEnabled(true); + } + + if (g_gui.xmlEval()->getVar("Globals.RecorderDialog.ExtInfo.Visible") != 1) + return; + + _gfxWidget->setGfx(-1, -1, 0, 0, 0); + _screenShotsCount = 0; + _currentScreenshot = 0; + updateScreenShotsText(); + if (_list->getSelected() >= 0) { + _authorText->setLabel(_("Author: ") + _fileHeaders[_list->getSelected()].author); + _notesText->setLabel(_("Notes: ") + _fileHeaders[_list->getSelected()].notes); + + _firstScreenshotUpdate = true; + updateScreenshot(); + if ((_screenShotsCount) > 0) { + _currentScreenshot = 1; + } + updateScreenshot(); + } else { + _authorText->setLabel(_("Author: ")); + _notesText->setLabel(_("Notes: ")); + _screenShotsCount = -1; + _currentScreenshot = 0; + _gfxWidget->setGfx(-1, -1, 0, 0, 0); + _gfxWidget->draw(); + updateScreenShotsText(); + } +} + +void RecorderDialog::updateScreenshot() { + if (_list->getSelected() == -1) { + return; + } + if (_currentScreenshot < 1) { + _currentScreenshot = _screenShotsCount; + } + if (_currentScreenshot > _screenShotsCount) { + _currentScreenshot = 1; + } + if (_firstScreenshotUpdate) { + _playbackFile.openRead(_fileHeaders[_list->getSelected()].fileName); + _screenShotsCount = _playbackFile.getScreensCount(); + _firstScreenshotUpdate = false; + } + Graphics::Surface *srcsf = _playbackFile.getScreenShot(_currentScreenshot); + if (srcsf != NULL) { + Graphics::Surface *destsf = Graphics::scale(*srcsf, _gfxWidget->getWidth(), _gfxWidget->getHeight()); + _gfxWidget->setGfx(destsf); + updateScreenShotsText(); + delete destsf; + delete srcsf; + } else { + _gfxWidget->setGfx(-1, -1, 0, 0, 0); + } + _gfxWidget->draw(); +} + +void RecorderDialog::updateScreenShotsText() { + if (_screenShotsCount == -1) { + _currentScreenshotText->setLabel(Common::String::format("%d / ?", _currentScreenshot)); + } else { + _currentScreenshotText->setLabel(Common::String::format("%d / %d", _currentScreenshot, _screenShotsCount)); + } +} + +} // End of namespace GUI diff --git a/gui/recorderdialog.h b/gui/recorderdialog.h new file mode 100644 index 0000000000..eb690a4f38 --- /dev/null +++ b/gui/recorderdialog.h @@ -0,0 +1,81 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef GUI_RECORDER_DIALOG_H +#define GUI_RECORDER_DIALOG_H +#include "common/stream.h" +#include "common/recorderfile.h" +#include "gui/dialog.h" +namespace GUI { + +class ListWidget; +class GraphicsWidget; +class ButtonWidget; +class CommandSender; +class ContainerWidget; +class StaticTextWidget; + +class RecorderDialog : public GUI::Dialog { +private: + bool _firstScreenshotUpdate; + Common::PlaybackFile _playbackFile; + Common::String _target; + Common::String _filename; + int _currentScreenshot; + int _screenShotsCount; + Common::Array<Common::PlaybackFile::PlaybackFileHeader> _fileHeaders; + GUI::ListWidget *_list; + GUI::ContainerWidget *_container; + GUI::GraphicsWidget *_gfxWidget; + GUI::StaticTextWidget *_currentScreenshotText; + GUI::StaticTextWidget *_authorText; + GUI::StaticTextWidget *_notesText; + GUI::ButtonWidget *_editButton; + GUI::ButtonWidget *_deleteButton; + GUI::ButtonWidget *_playbackButton; + + void updateList(); + void updateScreenShotsText(); + void updateSelection(bool redraw); + void updateScreenshot(); +public: + Common::String _author; + Common::String _name; + Common::String _notes; + enum DialogResult { + kRecordDialogClose, + kRecordDialogRecord, + kRecordDialogPlayback + }; + RecorderDialog(); + ~RecorderDialog(); + + virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); + virtual void reflowLayout(); + + int runModal(Common::String &target); + const Common::String getFileName() {return _filename;} +}; + +} // End of namespace GUI + + +#endif diff --git a/gui/themes/default.inc b/gui/themes/default.inc index 6d8e6baac7..1b6ae3ec27 100644 --- a/gui/themes/default.inc +++ b/gui/themes/default.inc @@ -610,50 +610,54 @@ "/> " "</drawdata> " "</render_info> " -"<layout_info resolution='y<400'> " +"<layout_info resolution='y>399'> " "<globals> " -"<def var='Line.Height' value='12' /> " -"<def var='Font.Height' value='10' /> " -"<def var='About.OuterBorder' value='10'/> " -"<def var='Layout.Spacing' value='8'/> " +"<def var='Line.Height' value='16' /> " +"<def var='Font.Height' value='16' /> " +"<def var='About.OuterBorder' value='80'/> " +"<def var='Layout.Spacing' value='8' /> " "<def var='ShowLauncherLogo' value='0'/> " "<def var='ShowGlobalMenuLogo' value='0'/> " "<def var='ShowSearchPic' value='0'/> " "<def var='ShowChooserPics' value='0'/> " -"<def var='ShowChooserPageDisplay' value='0'/> " -"<def var='SaveLoadChooser.ExtInfo.Visible' value='0'/> " -"<def var='KeyMapper.Spacing' value='5'/> " -"<def var='KeyMapper.LabelWidth' value='80'/> " -"<def var='KeyMapper.ButtonWidth' value='60'/> " -"<def var='Tooltip.MaxWidth' value='70'/> " -"<def var='Tooltip.XDelta' value='8'/> " -"<def var='Tooltip.YDelta' value='8'/> " -"<def var='Predictive.Button.Width' value='45' /> " -"<def var='Predictive.Button.Height' value='15' /> " -"<widget name='Button' " -"size='72,16' " -"/> " -"<widget name='Slider' " -"size='85,12' " -"/> " +"<def var='ShowChooserPageDisplay' value='1'/> " +"<def var='SaveLoadChooser.ExtInfo.Visible' value='1'/> " +"<def var='RecorderDialog.ExtInfo.Visible' value='1'/> " +"<def var='OnScreenDialog.ShowPics' value='0'/> " +"<def var='KeyMapper.Spacing' value='10'/> " +"<def var='KeyMapper.LabelWidth' value='100'/> " +"<def var='KeyMapper.ButtonWidth' value='80'/> " +"<def var='Tooltip.MaxWidth' value='200'/> " +"<def var='Tooltip.XDelta' value='16'/> " +"<def var='Tooltip.YDelta' value='16'/> " +"<def var='Predictive.Button.Width' value='60' /> " "<widget name='OptionsLabel' " "size='110,Globals.Line.Height' " "textalign='right' " "/> " "<widget name='SmallLabel' " -"size='18,Globals.Line.Height' " +"size='24,Globals.Line.Height' " +"/> " +"<widget name='ShortOptionsLabel' " +"size='60,Globals.Line.Height' " +"/> " +"<widget name='Button' " +"size='108,24' " +"/> " +"<widget name='Slider' " +"size='128,18' " "/> " "<widget name='PopUp' " -"size='-1,15' " +"size='-1,19' " "/> " "<widget name='Checkbox' " -"size='-1,Globals.Line.Height' " +"size='-1,14' " "/> " "<widget name='Radiobutton' " "size='-1,Globals.Line.Height' " "/> " "<widget name='ListWidget' " -"padding='5,0,0,0' " +"padding='5,0,8,0' " "/> " "<widget name='PopUpWidget' " "padding='7,5,0,0' " @@ -665,29 +669,35 @@ "padding='7,5,5,5' " "/> " "<widget name='Scrollbar' " -"size='9,0' " +"size='15,0' " "/> " "<widget name='TabWidget.Tab' " -"size='45,16' " -"padding='0,0,2,0' " +"size='75,27' " +"padding='0,0,8,0' " "/> " "<widget name='TabWidget.Body' " -"padding='0,0,0,-8' " +"padding='0,0,0,0' " "/> " "<widget name='TabWidget.NavButton' " -"size='32,18' " -"padding='0,0,1,0' " +"size='15,18' " +"padding='0,3,4,0' " +"/> " +"<widget name='EditRecordLabel' " +"size='60,25' " +"/> " +"<widget name='EditRecord' " +"size='240,25' " "/> " "</globals> " "<dialog name='Launcher' overlays='screen'> " -"<layout type='vertical' center='true' padding='6,6,2,2'> " +"<layout type='vertical' center='true' padding='16,16,8,8'> " "<widget name='Version' " "height='Globals.Line.Height' " "textalign='center' " "/> " -"<layout type='horizontal' spacing='5' padding='0,0,0,0'> " +"<layout type='horizontal' spacing='5' padding='10,0,0,0'> " "<widget name='SearchDesc' " -"width='50' " +"width='60' " "height='Globals.Line.Height' " "textalign='right' " "/> " @@ -702,38 +712,39 @@ "<space /> " "</layout> " "<widget name='GameList'/> " -"<layout type='horizontal' padding='0,0,0,0' spacing='8'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " "<widget name='LoadGameButton' " -"height='12' " +"height='20' " "/> " "<widget name='AddGameButton' " -"height='12' " +"height='20' " "/> " "<widget name='EditGameButton' " -"height='12' " +"height='20' " "/> " "<widget name='RemoveGameButton' " -"height='12' " +"height='20' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='8'> " +"<space size='4'/> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " "<widget name='QuitButton' " -"height='12' " +"height='20' " "/> " "<widget name='AboutButton' " -"height='12' " +"height='20' " "/> " "<widget name='OptionsButton' " -"height='12' " +"height='20' " "/> " "<widget name='StartButton' " -"height='12' " +"height='20' " "/> " "</layout> " "</layout> " "</dialog> " -"<dialog name='Browser' overlays='screen' inset='8' shading='dim'> " -"<layout type='vertical' padding='8,8,0,4'> " +"<dialog name='Browser' overlays='Dialog.Launcher.GameList' shading='dim'> " +"<layout type='vertical' padding='8,8,8,8'> " "<widget name='Headline' " "height='Globals.Line.Height' " "/> " @@ -741,7 +752,7 @@ "height='Globals.Line.Height' " "/> " "<widget name='List'/> " -"<layout type='vertical' padding='0,0,8,0'> " +"<layout type='vertical' padding='0,0,16,0'> " "<widget name='Hidden' " "type='Checkbox' " "/> " @@ -760,10 +771,10 @@ "</layout> " "</layout> " "</dialog> " -"<dialog name='GlobalOptions' overlays='screen' inset='16' shading='dim'> " +"<dialog name='GlobalOptions' overlays='Dialog.Launcher.GameList' shading='dim'> " "<layout type='vertical' padding='0,0,0,0'> " "<widget name='TabWidget'/> " -"<layout type='horizontal' padding='8,8,8,8'> " +"<layout type='horizontal' padding='16,16,16,16'> " "<space/> " "<widget name='Cancel' " "type='Button' " @@ -776,7 +787,7 @@ "</dialog> " "<dialog name='GlobalOptions_Graphics' overlays='Dialog.GlobalOptions.TabWidget'> " "<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='grModePopupDesc' " "type='OptionsLabel' " "/> " @@ -784,7 +795,7 @@ "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='grRenderPopupDesc' " "type='OptionsLabel' " "/> " @@ -802,7 +813,7 @@ "</dialog> " "<dialog name='GlobalOptions_Audio' overlays='Dialog.GlobalOptions.TabWidget'> " "<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='auMidiPopupDesc' " "type='OptionsLabel' " "/> " @@ -810,7 +821,7 @@ "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='auOPLPopupDesc' " "type='OptionsLabel' " "/> " @@ -818,7 +829,7 @@ "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='auSampleRatePopupDesc' " "type='OptionsLabel' " "/> " @@ -826,7 +837,7 @@ "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='3' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " "<widget name='subToggleDesc' " "type='OptionsLabel' " "/> " @@ -840,7 +851,7 @@ "type='Radiobutton' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " "<widget name='subSubtitleSpeedDesc' " "type='OptionsLabel' " "/> " @@ -854,8 +865,9 @@ "</layout> " "</dialog> " "<dialog name='GlobalOptions_Volume' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='16,16,16,16' spacing='8'> " +"<layout type='vertical' padding='0,0,0,0' spacing='8'> " +"<layout type='horizontal' padding='0,0,0,0'> " "<widget name='vcMusicText' " "type='OptionsLabel' " "/> " @@ -866,7 +878,7 @@ "type='SmallLabel' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0'> " "<widget name='vcSfxText' " "type='OptionsLabel' " "/> " @@ -877,7 +889,7 @@ "type='SmallLabel' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0'> " "<widget name='vcSpeechText' " "type='OptionsLabel' " "/> " @@ -888,8 +900,8 @@ "type='SmallLabel' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " -"<space size='110' /> " +"</layout> " +"<layout type='vertical' padding='24,0,24,0' center='true'> " "<widget name='vcMuteCheckbox' " "type='Checkbox' " "/> " @@ -897,8 +909,8 @@ "</layout> " "</dialog> " "<dialog name='GlobalOptions_MIDI' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='16,16,16,16' spacing='6'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='vertical' padding='16,16,16,16' spacing='8'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='auPrefGmPopupDesc' " "type='OptionsLabel' " "/> " @@ -906,7 +918,7 @@ "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='mcFontButton' " "type='Button' " "/> " @@ -921,7 +933,7 @@ "<widget name='mcMixedCheckbox' " "type='Checkbox' " "/> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0'> " "<widget name='mcMidiGainText' " "type='OptionsLabel' " "/> " @@ -934,14 +946,14 @@ "/> " "</layout> " "<widget name='mcFluidSynthSettings' " -"width='150' " +"width='200' " "height='Globals.Button.Height' " "/> " "</layout> " "</dialog> " "<dialog name='GlobalOptions_MT32' overlays='Dialog.GlobalOptions.TabWidget'> " "<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='auPrefMt32PopupDesc' " "type='OptionsLabel' " "/> " @@ -959,7 +971,7 @@ "</dialog> " "<dialog name='GlobalOptions_Paths' overlays='Dialog.GlobalOptions.TabWidget'> " "<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='16'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='SaveButton' " "type='Button' " "/> " @@ -971,7 +983,7 @@ "width='Globals.Line.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='16'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='ThemeButton' " "type='Button' " "/> " @@ -983,7 +995,7 @@ "width='Globals.Line.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='16'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='ExtraButton' " "type='Button' " "/> " @@ -1007,7 +1019,7 @@ "</dialog> " "<dialog name='GlobalOptions_Misc' overlays='Dialog.GlobalOptions.TabWidget'> " "<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='16'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='ThemeButton' " "type='Button' " "/> " @@ -1015,31 +1027,25 @@ "height='Globals.Line.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='RendererPopupDesc' " -"width='80' " -"height='Globals.Line.Height' " -"textalign='right' " +"type='OptionsLabel' " "/> " "<widget name='RendererPopup' " "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='AutosavePeriodPopupDesc' " -"width='80' " -"height='Globals.Line.Height' " -"textalign='right' " +"type='OptionsLabel' " "/> " "<widget name='AutosavePeriodPopup' " "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='GuiLanguagePopupDesc' " -"width='80' " -"height='Globals.Line.Height' " -"textalign='right' " +"type='OptionsLabel' " "/> " "<widget name='GuiLanguagePopup' " "type='PopUp' " @@ -1074,10 +1080,10 @@ "</layout> " "</layout> " "</dialog> " -"<dialog name='GameOptions' overlays='screen' inset='16' shading='dim'> " +"<dialog name='GameOptions' overlays='Dialog.Launcher.GameList' shading='dim'> " "<layout type='vertical' padding='0,0,0,0' spacing='16'> " "<widget name='TabWidget'/> " -"<layout type='horizontal' padding='8,8,8,8'> " +"<layout type='horizontal' padding='16,16,16,4'> " "<space/> " "<widget name='Cancel' " "type='Button' " @@ -1089,7 +1095,7 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_Graphics' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='8,8,8,8' spacing='6'> " +"<layout type='vertical' padding='16,16,16,16' spacing='8'> " "<widget name='EnableTabCheckbox' " "type='Checkbox' " "/> " @@ -1097,7 +1103,7 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_Audio' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='8,8,8,8' spacing='6'> " +"<layout type='vertical' padding='16,16,16,16' spacing='8'> " "<widget name='EnableTabCheckbox' " "type='Checkbox' " "/> " @@ -1105,7 +1111,7 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_MIDI' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='8,8,8,8' spacing='6'> " +"<layout type='vertical' padding='16,16,16,16' spacing='8'> " "<widget name='EnableTabCheckbox' " "type='Checkbox' " "/> " @@ -1113,7 +1119,7 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_MT32' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='8,8,8,8' spacing='6'> " +"<layout type='vertical' padding='16,16,16,16' spacing='8'> " "<widget name='EnableTabCheckbox' " "type='Checkbox' " "/> " @@ -1121,7 +1127,7 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_Volume' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='8,8,8,8' spacing='6'> " +"<layout type='vertical' padding='16,16,16,16' spacing='8'> " "<widget name='EnableTabCheckbox' " "type='Checkbox' " "/> " @@ -1129,43 +1135,34 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_Game' overlays='Dialog.GameOptions.TabWidget' shading='dim'> " -"<layout type='vertical' padding='8,8,8,8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='vertical' padding='16,16,16,16'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='Id' " -"width='35' " -"height='Globals.Line.Height' " -"textalign='right' " +"type='OptionsLabel' " "/> " "<widget name='Domain' " "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='Name' " -"width='35' " -"height='Globals.Line.Height' " -"textalign='right' " +"type='OptionsLabel' " "/> " "<widget name='Desc' " "type='PopUp' " "/> " "</layout> " -"<space size='8'/> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='LangPopupDesc' " -"width='60' " -"height='Globals.Line.Height' " -"textalign='right' " +"type='OptionsLabel' " "/> " "<widget name='LangPopup' " "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='PlatformPopupDesc' " -"width='60' " -"height='Globals.Line.Height' " -"textalign='right' " +"type='OptionsLabel' " "/> " "<widget name='PlatformPopup' " "type='PopUp' " @@ -1174,8 +1171,8 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_Paths' overlays='Dialog.GameOptions.TabWidget' shading='dim'> " -"<layout type='vertical' padding='8,8,8,8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> " +"<layout type='vertical' padding='16,16,16,16'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='Savepath' " "type='Button' " "/> " @@ -1187,7 +1184,7 @@ "width='Globals.Line.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='Extrapath' " "type='Button' " "/> " @@ -1199,7 +1196,7 @@ "width='Globals.Line.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='Gamepath' " "type='Button' " "/> " @@ -1210,7 +1207,7 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_Engine' overlays='Dialog.GameOptions.TabWidget' shading='dim'> " -"<layout type='vertical' padding='8,8,8,8'> " +"<layout type='vertical' padding='16,16,16,16'> " "<widget name='customOption1Checkbox' " "type='Checkbox' " "/> " @@ -1235,55 +1232,57 @@ "</layout> " "</dialog> " "<dialog name='GlobalMenu' overlays='screen_center'> " -"<layout type='vertical' padding='2,2,4,6' center='true' spacing='6'> " +"<layout type='vertical' padding='16,16,16,16' center='true'> " "<widget name='Title' " -"width='160' " -"height='4' " +"width='210' " +"height='Globals.Line.Height' " "/> " "<widget name='Version' " -"width='160' " -"height='4' " +"width='210' " +"height='Globals.Line.Height' " "/> " -"<space size='1'/> " +"<widget name='Resume' " +"width='150' " +"height='Globals.Button.Height' " +"/> " +"<space size='10'/> " "<widget name='Load' " -"width='120' " -"height='12' " +"width='150' " +"height='Globals.Button.Height' " "/> " "<widget name='Save' " -"width='120' " -"height='12' " +"width='150' " +"height='Globals.Button.Height' " "/> " -"<space size='1'/> " +"<space size='10'/> " "<widget name='Options' " -"width='120' " -"height='12' " +"width='150' " +"height='Globals.Button.Height' " "/> " "<widget name='Help' " -"width='120' " -"height='12' " +"width='150' " +"height='Globals.Button.Height' " "/> " "<widget name='About' " -"width='120' " -"height='12' " -"/> " -"<space size='1'/> " -"<widget name='Resume' " -"width='120' " -"height='12' " +"width='150' " +"height='Globals.Button.Height' " "/> " +"<space size='10'/> " "<widget name='RTL' " -"width='120' " -"height='12' " +"width='150' " +"height='Globals.Button.Height' " "/> " "<widget name='Quit' " -"width='120' " -"height='12' " +"width='150' " +"height='Globals.Button.Height' " "/> " "</layout> " "</dialog> " "<dialog name='GlobalConfig' overlays='screen_center'> " "<layout type='vertical' padding='8,8,8,8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0'> " +"<layout type='vertical' padding='0,0,0,0' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='8'> " "<widget name='vcMusicText' " "type='OptionsLabel' " "/> " @@ -1294,7 +1293,7 @@ "type='SmallLabel' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='8'> " "<widget name='vcSfxText' " "type='OptionsLabel' " "/> " @@ -1305,7 +1304,7 @@ "type='SmallLabel' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='8'> " "<widget name='vcSpeechText' " "type='OptionsLabel' " "/> " @@ -1316,34 +1315,33 @@ "type='SmallLabel' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " -"<space size='110' /> " +"</layout> " +"<layout type='vertical' padding='24,24,24,24' center='true'> " "<widget name='vcMuteCheckbox' " "type='Checkbox' " -"width='80' " +"width='80' " "/> " "</layout> " -"<layout type='vertical' padding='0,0,0,0' spacing='1' center='true'> " +"</layout> " +"<space size='8' /> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " "<widget name='subToggleDesc' " "type='OptionsLabel' " "/> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='subToggleSpeechOnly' " "type='Radiobutton' " -"width='90' " +"width='100' " "/> " "<widget name='subToggleSubOnly' " "type='Radiobutton' " -"width='90' " +"width='100' " "/> " "<widget name='subToggleSubBoth' " "type='Radiobutton' " -"width='90' " +"width='100' " "/> " "</layout> " -"</layout> " -"<space size='2' /> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " "<widget name='subSubtitleSpeedDesc' " "type='OptionsLabel' " "/> " @@ -1354,8 +1352,8 @@ "type='SmallLabel' " "/> " "</layout> " -"<space size='16'/> " -"<layout type='horizontal' padding='0,0,0,0' spacing='4'> " +"<space size='60'/> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " "<widget name='Keys' " "type='Button' " "/> " @@ -1372,7 +1370,7 @@ "<dialog name='FluidSynthSettings' overlays='GlobalOptions' shading='dim'> " "<layout type='vertical' padding='0,0,0,0'> " "<widget name='TabWidget'/> " -"<layout type='horizontal' padding='8,8,8,8'> " +"<layout type='horizontal' padding='16,16,16,16'> " "<space/> " "<widget name='ResetSettings' " "type='Button' " @@ -1387,7 +1385,7 @@ "</layout> " "</dialog> " "<dialog name='FluidSynthSettings_Chorus' overlays='Dialog.FluidSynthSettings.TabWidget'> " -"<layout type='vertical' padding='8,8,8,8' spacing='6'> " +"<layout type='vertical' padding='16,16,16,16' spacing='8'> " "<widget name='EnableTabCheckbox' " "type='Checkbox' " "/> " @@ -1450,7 +1448,7 @@ "</layout> " "</dialog> " "<dialog name='FluidSynthSettings_Reverb' overlays='Dialog.FluidSynthSettings.TabWidget'> " -"<layout type='vertical' padding='8,8,8,8' spacing='6'> " +"<layout type='vertical' padding='16,16,16,16' spacing='8'> " "<widget name='EnableTabCheckbox' " "type='Checkbox' " "/> " @@ -1505,7 +1503,7 @@ "</layout> " "</dialog> " "<dialog name='FluidSynthSettings_Misc' overlays='Dialog.FluidSynthSettings.TabWidget'> " -"<layout type='vertical' padding='8,8,8,8' spacing='6'> " +"<layout type='vertical' padding='16,16,16,16' spacing='8'> " "<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='InterpolationText' " "type='OptionsLabel' " @@ -1517,10 +1515,25 @@ "</layout> " "</dialog> " "<dialog name='SaveLoadChooser' overlays='screen' inset='8' shading='dim'> " -"<layout type='vertical' padding='8,8,8,8' center='true'> " -"<widget name='Title' height='Globals.Line.Height'/> " +"<layout type='vertical' padding='8,8,8,32' center='true'> " +"<layout type='horizontal' padding='0,0,0,0'> " +"<widget name='Title' " +"height='Globals.Line.Height' " +"/> " +"<space/> " +"<widget name='PageDisplay' " +"width='200' " +"height='Globals.Line.Height' " +"/> " +"</layout> " +"<layout type='horizontal' padding='0,0,0,16' spacing='16'> " "<widget name='List' /> " -"<layout type='horizontal' padding='0,0,16,0'> " +"<widget name='Thumbnail' " +"width='180' " +"height='200' " +"/> " +"</layout> " +"<layout type='horizontal' padding='0,0,0,0'> " "<widget name='ListSwitch' " "height='Globals.Line.Height' " "width='Globals.Line.Height' " @@ -1533,7 +1546,7 @@ "<widget name='Delete' " "type='Button' " "/> " -"<space size='16'/> " +"<space size='32'/> " "<widget name='Cancel' " "type='Button' " "/> " @@ -1546,7 +1559,7 @@ "<dialog name='SavenameDialog' overlays='screen_center'> " "<layout type='vertical' padding='8,8,8,8'> " "<widget name='DescriptionText' " -"width='180' " +"width='320' " "height='Globals.Line.Height' " "/> " "<widget name='Description' " @@ -1556,22 +1569,139 @@ "<widget name='Cancel' " "type='Button' " "/> " +"<space size='96'/> " "<widget name='Ok' " "type='Button' " "/> " "</layout> " "</layout> " "</dialog> " -"<dialog name='ScummHelp' overlays='screen'> " -"<layout type='vertical' padding='8,8,8,8'> " +"<dialog name='RecorderDialog' overlays='screen' inset='8' shading='dim'> " +"<layout type='vertical' padding='8,8,8,32' center='true'> " "<widget name='Title' " +"height='Globals.Line.Height' " +"/> " +"<layout type='horizontal' padding='0,0,0,16' spacing='16'> " +"<widget name='List' /> " +"<layout type='vertical' padding='0,0,0,0'> " +"<widget name='Thumbnail' " "width='180' " +"height='170' " +"/> " +"<layout type='horizontal' padding='0,0,0,0'> " +"<widget name='NextScreenShotButton' " +"width='25' " +"height='25' " +"/> " +"<widget name='currentScreenshot' " +"width='125' " +"height='25' " +"textalign='center' " +"/> " +"<widget name='PreviousScreenShotButton' " +"width='25' " +"height='25' " +"/> " +"</layout> " +"<widget name='Author' height='Globals.Line.Height' /> " +"<widget name='Notes' height='Globals.Line.Height' /> " +"</layout> " +"</layout> " +"<layout type='horizontal' padding='0,0,0,0'> " +"<widget name='Delete' " +"type='Button' " +"/> " +"<space size='16'/> " +"<widget name='Cancel' " +"type='Button' " +"/> " +"<space size='16'/> " +"<widget name='Edit' " +"type='Button' " +"/> " +"<widget name='Record' " +"type='Button' " +"/> " +"<widget name='Playback' " +"type='Button' " +"/> " +"</layout> " +"</layout> " +"</dialog> " +"<dialog name='OnScreenDialog' overlays='screen_center'> " +"<layout type='horizontal' spacing='5' padding='5,3,5,3' center='true'> " +"<widget name='StopButton' " +"width='32' " +"height='32' " +"/> " +"<widget name='EditButton' " +"width='32' " +"height='32' " +"/> " +"<widget name='SwitchModeButton' " +"width='32' " +"height='32' " +"/> " +"<widget name='FastReplayButton' " +"width='32' " +"height='32' " +"/> " +"<widget name='TimeLabel' " +"width='50' " +"height='30' " +"/> " +"</layout> " +"</dialog> " +"<dialog name='EditRecordDialog' overlays='screen_center'> " +"<layout type='vertical' padding='8,8,8,8' center='true'> " +"<widget name='Title' " +"width='320' " +"height='Globals.Line.Height' " +"/> " +"<layout type='horizontal' spacing='5' padding='0,0,0,10'> " +"<widget name='AuthorLabel' " +"type='EditRecordLabel' " +"/> " +"<widget name='AuthorEdit' " +"type='EditRecord' " +"/> " +"</layout> " +"<layout type='horizontal' spacing='5' padding='0,0,0,10'> " +"<widget name='NameLabel' " +"type='EditRecordLabel' " +"/> " +"<widget name='NameEdit' " +"type='EditRecord' " +"/> " +"</layout> " +"<layout type='horizontal' spacing='5' padding='0,0,0,10'> " +"<widget name='NotesLabel' " +"type='EditRecordLabel' " +"/> " +"<widget name='NotesEdit' " +"type='EditRecord' " +"/> " +"</layout> " +"<layout type='horizontal' spacing='5' padding='0,0,0,10'> " +"<widget name='Cancel' " +"type='Button' " +"/> " +"<widget name='OK' " +"type='Button' " +"/> " +"</layout> " +"</layout> " +"</dialog> " +"<dialog name='ScummHelp' overlays='screen_center'> " +"<layout type='vertical' padding='8,8,8,8' center='true'> " +"<widget name='Title' " +"width='320' " "height='Globals.Line.Height' " "/> " "<widget name='HelpText' " -"height='140' " +"height='200' " "/> " -"<layout type='horizontal' padding='0,0,0,0'> " +"<layout type='horizontal' padding='0,0,16,0'> " "<widget name='Prev' " "type='Button' " "/> " @@ -1588,7 +1718,7 @@ "<dialog name='LoomTownsDifficultyDialog' overlays='screen_center'> " "<layout type='vertical' padding='8,8,8,8' center='true'> " "<widget name='Description1' " -"width='280' " +"width='320' " "height='Globals.Line.Height' " "/> " "<widget name='Description2' " @@ -1606,20 +1736,20 @@ "</layout> " "</dialog> " "<dialog name='MassAdd' overlays='screen_center' shading='dim'> " -"<layout type='vertical' padding='4,4,16,4' center='true'> " +"<layout type='vertical' padding='8,8,32,8' center='true'> " "<widget name='DirProgressText' " -"width='280' " +"width='480' " "height='Globals.Line.Height' " "/> " "<widget name='GameProgressText' " -"width='280' " +"width='480' " "height='Globals.Line.Height' " "/> " "<widget name='GameList' " -"width='280' " -"height='100' " +"width='480' " +"height='250' " "/> " -"<layout type='horizontal' padding='4,4,4,4'> " +"<layout type='horizontal' padding='8,8,8,8'> " "<widget name='Ok' " "type='Button' " "/> " @@ -1630,20 +1760,20 @@ "</layout> " "</dialog> " "<dialog name='KeyMapper' overlays='screen_center' shading='dim'> " -"<layout type='vertical' padding='8,8,8,8' spacing='10' center='true'> " +"<layout type='vertical' padding='8,8,32,8' spacing='10' center='true'> " "<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='PopupDesc' " "type='OptionsLabel' " "/> " "<widget name='Popup' " "type='PopUp' " -"width='150' " +"width='400' " "height='Globals.Line.Height' " "/> " "</layout> " "<widget name='KeymapArea' " -"width='300' " -"height='120' " +"width='600' " +"height='280' " "/> " "<widget name='Close' " "type='Button' " @@ -1651,142 +1781,144 @@ "</layout> " "</dialog> " "<dialog name='Predictive' overlays='screen_center'> " -"<layout type='vertical' padding='1,1,1,1' center='true'> " +"<layout type='vertical' padding='5,5,5,5' center='true'> " "<widget name='Headline' " "height='Globals.Line.Height' " -"width='150' " +"width='210' " "textalign='center' " "/> " -"<layout type='horizontal' padding='3,3,3,3'> " +"<layout type='horizontal' padding='5,5,5,5'> " "<widget name='Word' " -"width='120' " +"width='190' " "height='Globals.Button.Height' " "/> " "<widget name='Delete' " "width='20' " -"height='Globals.Predictive.Button.Height' " +"height='Globals.Button.Height' " "/> " "</layout> " +"<space size='5' /> " "<layout type='horizontal' padding='3,3,3,3'> " "<widget name='Button1' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Predictive.Button.Height' " +"height='Globals.Button.Height' " "/> " "<widget name='Button2' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Predictive.Button.Height' " +"height='Globals.Button.Height' " "/> " "<widget name='Button3' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Predictive.Button.Height' " +"height='Globals.Button.Height' " "/> " "</layout> " "<layout type='horizontal' padding='3,3,3,3'> " "<widget name='Button4' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Predictive.Button.Height' " +"height='Globals.Button.Height' " "/> " "<widget name='Button5' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Predictive.Button.Height' " +"height='Globals.Button.Height' " "/> " "<widget name='Button6' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Predictive.Button.Height' " +"height='Globals.Button.Height' " "/> " "</layout> " "<layout type='horizontal' padding='3,3,3,3'> " "<widget name='Button7' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Predictive.Button.Height' " +"height='Globals.Button.Height' " "/> " "<widget name='Button8' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Predictive.Button.Height' " +"height='Globals.Button.Height' " "/> " "<widget name='Button9' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Predictive.Button.Height' " +"height='Globals.Button.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='3,3,3,0'> " +"<layout type='horizontal' padding='3,3,3,3'> " "<widget name='Pre' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Predictive.Button.Height' " +"height='Globals.Button.Height' " "/> " "<widget name='Button0' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Predictive.Button.Height' " +"height='Globals.Button.Height' " "/> " "<widget name='Next' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Predictive.Button.Height' " +"height='Globals.Button.Height' " "/> " "</layout> " -"<space size='3' /> " -"<layout type='horizontal' padding='3,3,0,3'> " +"<space size='5' /> " +"<layout type='horizontal' padding='3,3,3,3'> " "<widget name='Add' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Predictive.Button.Height' " +"height='Globals.Button.Height' " "/> " +"<space size='22'/> " "<widget name='Cancel' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Predictive.Button.Height' " +"height='Globals.Button.Height' " "/> " "<widget name='OK' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Predictive.Button.Height' " +"height='Globals.Button.Height' " "/> " "</layout> " "</layout> " "</dialog> " "</layout_info> " -"<layout_info resolution='y>399'> " +"<layout_info resolution='y<400'> " "<globals> " -"<def var='Line.Height' value='16' /> " -"<def var='Font.Height' value='16' /> " -"<def var='About.OuterBorder' value='80'/> " -"<def var='Layout.Spacing' value='8' /> " +"<def var='Line.Height' value='12' /> " +"<def var='Font.Height' value='10' /> " +"<def var='About.OuterBorder' value='10'/> " +"<def var='Layout.Spacing' value='8'/> " "<def var='ShowLauncherLogo' value='0'/> " "<def var='ShowGlobalMenuLogo' value='0'/> " "<def var='ShowSearchPic' value='0'/> " "<def var='ShowChooserPics' value='0'/> " -"<def var='ShowChooserPageDisplay' value='1'/> " -"<def var='SaveLoadChooser.ExtInfo.Visible' value='1'/> " -"<def var='KeyMapper.Spacing' value='10'/> " -"<def var='KeyMapper.LabelWidth' value='100'/> " -"<def var='KeyMapper.ButtonWidth' value='80'/> " -"<def var='Tooltip.MaxWidth' value='200'/> " -"<def var='Tooltip.XDelta' value='16'/> " -"<def var='Tooltip.YDelta' value='16'/> " -"<def var='Predictive.Button.Width' value='60' /> " +"<def var='ShowChooserPageDisplay' value='0'/> " +"<def var='SaveLoadChooser.ExtInfo.Visible' value='0'/> " +"<def var='RecorderDialog.ExtInfo.Visible' value='0'/> " +"<def var='OnScreenDialog.ShowPics' value='0'/> " +"<def var='KeyMapper.Spacing' value='5'/> " +"<def var='KeyMapper.LabelWidth' value='80'/> " +"<def var='KeyMapper.ButtonWidth' value='60'/> " +"<def var='Tooltip.MaxWidth' value='70'/> " +"<def var='Tooltip.XDelta' value='8'/> " +"<def var='Tooltip.YDelta' value='8'/> " +"<def var='Predictive.Button.Width' value='45' /> " +"<def var='Predictive.Button.Height' value='15' /> " +"<widget name='Button' " +"size='72,16' " +"/> " +"<widget name='Slider' " +"size='85,12' " +"/> " "<widget name='OptionsLabel' " "size='110,Globals.Line.Height' " "textalign='right' " "/> " "<widget name='SmallLabel' " -"size='24,Globals.Line.Height' " -"/> " -"<widget name='ShortOptionsLabel' " -"size='60,Globals.Line.Height' " -"/> " -"<widget name='Button' " -"size='108,24' " -"/> " -"<widget name='Slider' " -"size='128,18' " +"size='18,Globals.Line.Height' " "/> " "<widget name='PopUp' " -"size='-1,19' " +"size='-1,15' " "/> " "<widget name='Checkbox' " -"size='-1,14' " +"size='-1,Globals.Line.Height' " "/> " "<widget name='Radiobutton' " "size='-1,Globals.Line.Height' " "/> " "<widget name='ListWidget' " -"padding='5,0,8,0' " +"padding='5,0,0,0' " "/> " "<widget name='PopUpWidget' " "padding='7,5,0,0' " @@ -1798,29 +1930,35 @@ "padding='7,5,5,5' " "/> " "<widget name='Scrollbar' " -"size='15,0' " +"size='9,0' " "/> " "<widget name='TabWidget.Tab' " -"size='75,27' " -"padding='0,0,8,0' " +"size='45,16' " +"padding='0,0,2,0' " "/> " "<widget name='TabWidget.Body' " -"padding='0,0,0,0' " +"padding='0,0,0,-8' " "/> " "<widget name='TabWidget.NavButton' " -"size='15,18' " -"padding='0,3,4,0' " +"size='32,18' " +"padding='0,0,1,0' " +"/> " +"<widget name='EditRecordLabel' " +"size='60,Globals.Line.Height' " +"/> " +"<widget name='EditRecord' " +"size='120,15' " "/> " "</globals> " "<dialog name='Launcher' overlays='screen'> " -"<layout type='vertical' center='true' padding='16,16,8,8'> " +"<layout type='vertical' center='true' padding='6,6,2,2'> " "<widget name='Version' " "height='Globals.Line.Height' " "textalign='center' " "/> " -"<layout type='horizontal' spacing='5' padding='10,0,0,0'> " +"<layout type='horizontal' spacing='5' padding='0,0,0,0'> " "<widget name='SearchDesc' " -"width='60' " +"width='50' " "height='Globals.Line.Height' " "textalign='right' " "/> " @@ -1835,39 +1973,38 @@ "<space /> " "</layout> " "<widget name='GameList'/> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='8'> " "<widget name='LoadGameButton' " -"height='20' " +"height='12' " "/> " "<widget name='AddGameButton' " -"height='20' " +"height='12' " "/> " "<widget name='EditGameButton' " -"height='20' " +"height='12' " "/> " "<widget name='RemoveGameButton' " -"height='20' " +"height='12' " "/> " "</layout> " -"<space size='4'/> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='8'> " "<widget name='QuitButton' " -"height='20' " +"height='12' " "/> " "<widget name='AboutButton' " -"height='20' " +"height='12' " "/> " "<widget name='OptionsButton' " -"height='20' " +"height='12' " "/> " "<widget name='StartButton' " -"height='20' " +"height='12' " "/> " "</layout> " "</layout> " "</dialog> " -"<dialog name='Browser' overlays='Dialog.Launcher.GameList' shading='dim'> " -"<layout type='vertical' padding='8,8,8,8'> " +"<dialog name='Browser' overlays='screen' inset='8' shading='dim'> " +"<layout type='vertical' padding='8,8,0,4'> " "<widget name='Headline' " "height='Globals.Line.Height' " "/> " @@ -1875,7 +2012,7 @@ "height='Globals.Line.Height' " "/> " "<widget name='List'/> " -"<layout type='vertical' padding='0,0,16,0'> " +"<layout type='vertical' padding='0,0,8,0'> " "<widget name='Hidden' " "type='Checkbox' " "/> " @@ -1894,10 +2031,10 @@ "</layout> " "</layout> " "</dialog> " -"<dialog name='GlobalOptions' overlays='Dialog.Launcher.GameList' shading='dim'> " +"<dialog name='GlobalOptions' overlays='screen' inset='16' shading='dim'> " "<layout type='vertical' padding='0,0,0,0'> " "<widget name='TabWidget'/> " -"<layout type='horizontal' padding='16,16,16,16'> " +"<layout type='horizontal' padding='8,8,8,8'> " "<space/> " "<widget name='Cancel' " "type='Button' " @@ -1910,7 +2047,7 @@ "</dialog> " "<dialog name='GlobalOptions_Graphics' overlays='Dialog.GlobalOptions.TabWidget'> " "<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='grModePopupDesc' " "type='OptionsLabel' " "/> " @@ -1918,7 +2055,7 @@ "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='grRenderPopupDesc' " "type='OptionsLabel' " "/> " @@ -1936,7 +2073,7 @@ "</dialog> " "<dialog name='GlobalOptions_Audio' overlays='Dialog.GlobalOptions.TabWidget'> " "<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='auMidiPopupDesc' " "type='OptionsLabel' " "/> " @@ -1944,7 +2081,7 @@ "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='auOPLPopupDesc' " "type='OptionsLabel' " "/> " @@ -1952,7 +2089,7 @@ "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='auSampleRatePopupDesc' " "type='OptionsLabel' " "/> " @@ -1960,7 +2097,7 @@ "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='3' center='true'> " "<widget name='subToggleDesc' " "type='OptionsLabel' " "/> " @@ -1974,7 +2111,7 @@ "type='Radiobutton' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='subSubtitleSpeedDesc' " "type='OptionsLabel' " "/> " @@ -1988,9 +2125,8 @@ "</layout> " "</dialog> " "<dialog name='GlobalOptions_Volume' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='horizontal' padding='16,16,16,16' spacing='8'> " -"<layout type='vertical' padding='0,0,0,0' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0'> " +"<layout type='vertical' padding='16,16,16,16' spacing='8'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='vcMusicText' " "type='OptionsLabel' " "/> " @@ -2001,7 +2137,7 @@ "type='SmallLabel' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='vcSfxText' " "type='OptionsLabel' " "/> " @@ -2012,7 +2148,7 @@ "type='SmallLabel' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='vcSpeechText' " "type='OptionsLabel' " "/> " @@ -2023,8 +2159,8 @@ "type='SmallLabel' " "/> " "</layout> " -"</layout> " -"<layout type='vertical' padding='24,0,24,0' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<space size='110' /> " "<widget name='vcMuteCheckbox' " "type='Checkbox' " "/> " @@ -2032,8 +2168,8 @@ "</layout> " "</dialog> " "<dialog name='GlobalOptions_MIDI' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='vertical' padding='16,16,16,16' spacing='6'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='auPrefGmPopupDesc' " "type='OptionsLabel' " "/> " @@ -2041,7 +2177,7 @@ "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> " "<widget name='mcFontButton' " "type='Button' " "/> " @@ -2056,7 +2192,7 @@ "<widget name='mcMixedCheckbox' " "type='Checkbox' " "/> " -"<layout type='horizontal' padding='0,0,0,0'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='mcMidiGainText' " "type='OptionsLabel' " "/> " @@ -2069,14 +2205,14 @@ "/> " "</layout> " "<widget name='mcFluidSynthSettings' " -"width='200' " +"width='150' " "height='Globals.Button.Height' " "/> " "</layout> " "</dialog> " "<dialog name='GlobalOptions_MT32' overlays='Dialog.GlobalOptions.TabWidget'> " "<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='auPrefMt32PopupDesc' " "type='OptionsLabel' " "/> " @@ -2094,7 +2230,7 @@ "</dialog> " "<dialog name='GlobalOptions_Paths' overlays='Dialog.GlobalOptions.TabWidget'> " "<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='16'> " "<widget name='SaveButton' " "type='Button' " "/> " @@ -2106,7 +2242,7 @@ "width='Globals.Line.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='16'> " "<widget name='ThemeButton' " "type='Button' " "/> " @@ -2118,7 +2254,7 @@ "width='Globals.Line.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='16'> " "<widget name='ExtraButton' " "type='Button' " "/> " @@ -2142,7 +2278,7 @@ "</dialog> " "<dialog name='GlobalOptions_Misc' overlays='Dialog.GlobalOptions.TabWidget'> " "<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='16'> " "<widget name='ThemeButton' " "type='Button' " "/> " @@ -2150,25 +2286,31 @@ "height='Globals.Line.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='RendererPopupDesc' " -"type='OptionsLabel' " +"width='80' " +"height='Globals.Line.Height' " +"textalign='right' " "/> " "<widget name='RendererPopup' " "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='AutosavePeriodPopupDesc' " -"type='OptionsLabel' " +"width='80' " +"height='Globals.Line.Height' " +"textalign='right' " "/> " "<widget name='AutosavePeriodPopup' " "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='GuiLanguagePopupDesc' " -"type='OptionsLabel' " +"width='80' " +"height='Globals.Line.Height' " +"textalign='right' " "/> " "<widget name='GuiLanguagePopup' " "type='PopUp' " @@ -2203,10 +2345,10 @@ "</layout> " "</layout> " "</dialog> " -"<dialog name='GameOptions' overlays='Dialog.Launcher.GameList' shading='dim'> " +"<dialog name='GameOptions' overlays='screen' inset='16' shading='dim'> " "<layout type='vertical' padding='0,0,0,0' spacing='16'> " "<widget name='TabWidget'/> " -"<layout type='horizontal' padding='16,16,16,4'> " +"<layout type='horizontal' padding='8,8,8,8'> " "<space/> " "<widget name='Cancel' " "type='Button' " @@ -2218,7 +2360,7 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_Graphics' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='16,16,16,16' spacing='8'> " +"<layout type='vertical' padding='8,8,8,8' spacing='6'> " "<widget name='EnableTabCheckbox' " "type='Checkbox' " "/> " @@ -2226,7 +2368,7 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_Audio' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='16,16,16,16' spacing='8'> " +"<layout type='vertical' padding='8,8,8,8' spacing='6'> " "<widget name='EnableTabCheckbox' " "type='Checkbox' " "/> " @@ -2234,7 +2376,7 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_MIDI' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='16,16,16,16' spacing='8'> " +"<layout type='vertical' padding='8,8,8,8' spacing='6'> " "<widget name='EnableTabCheckbox' " "type='Checkbox' " "/> " @@ -2242,7 +2384,7 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_MT32' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='16,16,16,16' spacing='8'> " +"<layout type='vertical' padding='8,8,8,8' spacing='6'> " "<widget name='EnableTabCheckbox' " "type='Checkbox' " "/> " @@ -2250,7 +2392,7 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_Volume' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='16,16,16,16' spacing='8'> " +"<layout type='vertical' padding='8,8,8,8' spacing='6'> " "<widget name='EnableTabCheckbox' " "type='Checkbox' " "/> " @@ -2258,34 +2400,43 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_Game' overlays='Dialog.GameOptions.TabWidget' shading='dim'> " -"<layout type='vertical' padding='16,16,16,16'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='vertical' padding='8,8,8,8'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='Id' " -"type='OptionsLabel' " +"width='35' " +"height='Globals.Line.Height' " +"textalign='right' " "/> " "<widget name='Domain' " "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='Name' " -"type='OptionsLabel' " +"width='35' " +"height='Globals.Line.Height' " +"textalign='right' " "/> " "<widget name='Desc' " "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<space size='8'/> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='LangPopupDesc' " -"type='OptionsLabel' " +"width='60' " +"height='Globals.Line.Height' " +"textalign='right' " "/> " "<widget name='LangPopup' " "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='PlatformPopupDesc' " -"type='OptionsLabel' " +"width='60' " +"height='Globals.Line.Height' " +"textalign='right' " "/> " "<widget name='PlatformPopup' " "type='PopUp' " @@ -2294,8 +2445,8 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_Paths' overlays='Dialog.GameOptions.TabWidget' shading='dim'> " -"<layout type='vertical' padding='16,16,16,16'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='vertical' padding='8,8,8,8'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> " "<widget name='Savepath' " "type='Button' " "/> " @@ -2307,7 +2458,7 @@ "width='Globals.Line.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> " "<widget name='Extrapath' " "type='Button' " "/> " @@ -2319,7 +2470,7 @@ "width='Globals.Line.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> " "<widget name='Gamepath' " "type='Button' " "/> " @@ -2330,7 +2481,7 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_Engine' overlays='Dialog.GameOptions.TabWidget' shading='dim'> " -"<layout type='vertical' padding='16,16,16,16'> " +"<layout type='vertical' padding='8,8,8,8'> " "<widget name='customOption1Checkbox' " "type='Checkbox' " "/> " @@ -2355,57 +2506,55 @@ "</layout> " "</dialog> " "<dialog name='GlobalMenu' overlays='screen_center'> " -"<layout type='vertical' padding='16,16,16,16' center='true'> " +"<layout type='vertical' padding='2,2,4,6' center='true' spacing='6'> " "<widget name='Title' " -"width='210' " -"height='Globals.Line.Height' " +"width='160' " +"height='4' " "/> " "<widget name='Version' " -"width='210' " -"height='Globals.Line.Height' " -"/> " -"<widget name='Resume' " -"width='150' " -"height='Globals.Button.Height' " +"width='160' " +"height='4' " "/> " -"<space size='10'/> " +"<space size='1'/> " "<widget name='Load' " -"width='150' " -"height='Globals.Button.Height' " +"width='120' " +"height='12' " "/> " "<widget name='Save' " -"width='150' " -"height='Globals.Button.Height' " +"width='120' " +"height='12' " "/> " -"<space size='10'/> " +"<space size='1'/> " "<widget name='Options' " -"width='150' " -"height='Globals.Button.Height' " +"width='120' " +"height='12' " "/> " "<widget name='Help' " -"width='150' " -"height='Globals.Button.Height' " +"width='120' " +"height='12' " "/> " "<widget name='About' " -"width='150' " -"height='Globals.Button.Height' " +"width='120' " +"height='12' " +"/> " +"<space size='1'/> " +"<widget name='Resume' " +"width='120' " +"height='12' " "/> " -"<space size='10'/> " "<widget name='RTL' " -"width='150' " -"height='Globals.Button.Height' " +"width='120' " +"height='12' " "/> " "<widget name='Quit' " -"width='150' " -"height='Globals.Button.Height' " +"width='120' " +"height='12' " "/> " "</layout> " "</dialog> " "<dialog name='GlobalConfig' overlays='screen_center'> " "<layout type='vertical' padding='8,8,8,8'> " -"<layout type='horizontal' padding='0,0,0,0'> " -"<layout type='vertical' padding='0,0,0,0' center='true'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='8'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='vcMusicText' " "type='OptionsLabel' " "/> " @@ -2416,7 +2565,7 @@ "type='SmallLabel' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='8'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='vcSfxText' " "type='OptionsLabel' " "/> " @@ -2427,7 +2576,7 @@ "type='SmallLabel' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='8'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='vcSpeechText' " "type='OptionsLabel' " "/> " @@ -2438,33 +2587,34 @@ "type='SmallLabel' " "/> " "</layout> " -"</layout> " -"<layout type='vertical' padding='24,24,24,24' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<space size='110' /> " "<widget name='vcMuteCheckbox' " "type='Checkbox' " -"width='80' " +"width='80' " "/> " "</layout> " -"</layout> " -"<space size='8' /> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " +"<layout type='vertical' padding='0,0,0,0' spacing='1' center='true'> " "<widget name='subToggleDesc' " "type='OptionsLabel' " "/> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='subToggleSpeechOnly' " "type='Radiobutton' " -"width='100' " +"width='90' " "/> " "<widget name='subToggleSubOnly' " "type='Radiobutton' " -"width='100' " +"width='90' " "/> " "<widget name='subToggleSubBoth' " "type='Radiobutton' " -"width='100' " +"width='90' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " +"</layout> " +"<space size='2' /> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='subSubtitleSpeedDesc' " "type='OptionsLabel' " "/> " @@ -2475,8 +2625,8 @@ "type='SmallLabel' " "/> " "</layout> " -"<space size='60'/> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " +"<space size='16'/> " +"<layout type='horizontal' padding='0,0,0,0' spacing='4'> " "<widget name='Keys' " "type='Button' " "/> " @@ -2493,7 +2643,7 @@ "<dialog name='FluidSynthSettings' overlays='GlobalOptions' shading='dim'> " "<layout type='vertical' padding='0,0,0,0'> " "<widget name='TabWidget'/> " -"<layout type='horizontal' padding='16,16,16,16'> " +"<layout type='horizontal' padding='8,8,8,8'> " "<space/> " "<widget name='ResetSettings' " "type='Button' " @@ -2508,7 +2658,7 @@ "</layout> " "</dialog> " "<dialog name='FluidSynthSettings_Chorus' overlays='Dialog.FluidSynthSettings.TabWidget'> " -"<layout type='vertical' padding='16,16,16,16' spacing='8'> " +"<layout type='vertical' padding='8,8,8,8' spacing='6'> " "<widget name='EnableTabCheckbox' " "type='Checkbox' " "/> " @@ -2571,7 +2721,7 @@ "</layout> " "</dialog> " "<dialog name='FluidSynthSettings_Reverb' overlays='Dialog.FluidSynthSettings.TabWidget'> " -"<layout type='vertical' padding='16,16,16,16' spacing='8'> " +"<layout type='vertical' padding='8,8,8,8' spacing='6'> " "<widget name='EnableTabCheckbox' " "type='Checkbox' " "/> " @@ -2626,7 +2776,7 @@ "</layout> " "</dialog> " "<dialog name='FluidSynthSettings_Misc' overlays='Dialog.FluidSynthSettings.TabWidget'> " -"<layout type='vertical' padding='16,16,16,16' spacing='8'> " +"<layout type='vertical' padding='8,8,8,8' spacing='6'> " "<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='InterpolationText' " "type='OptionsLabel' " @@ -2638,25 +2788,10 @@ "</layout> " "</dialog> " "<dialog name='SaveLoadChooser' overlays='screen' inset='8' shading='dim'> " -"<layout type='vertical' padding='8,8,8,32' center='true'> " -"<layout type='horizontal' padding='0,0,0,0'> " -"<widget name='Title' " -"height='Globals.Line.Height' " -"/> " -"<space/> " -"<widget name='PageDisplay' " -"width='200' " -"height='Globals.Line.Height' " -"/> " -"</layout> " -"<layout type='horizontal' padding='0,0,0,16' spacing='16'> " +"<layout type='vertical' padding='8,8,8,8' center='true'> " +"<widget name='Title' height='Globals.Line.Height'/> " "<widget name='List' /> " -"<widget name='Thumbnail' " -"width='180' " -"height='200' " -"/> " -"</layout> " -"<layout type='horizontal' padding='0,0,0,0'> " +"<layout type='horizontal' padding='0,0,16,0'> " "<widget name='ListSwitch' " "height='Globals.Line.Height' " "width='Globals.Line.Height' " @@ -2669,7 +2804,7 @@ "<widget name='Delete' " "type='Button' " "/> " -"<space size='32'/> " +"<space size='16'/> " "<widget name='Cancel' " "type='Button' " "/> " @@ -2682,7 +2817,7 @@ "<dialog name='SavenameDialog' overlays='screen_center'> " "<layout type='vertical' padding='8,8,8,8'> " "<widget name='DescriptionText' " -"width='320' " +"width='180' " "height='Globals.Line.Height' " "/> " "<widget name='Description' " @@ -2692,23 +2827,114 @@ "<widget name='Cancel' " "type='Button' " "/> " -"<space size='96'/> " "<widget name='Ok' " "type='Button' " "/> " "</layout> " "</layout> " "</dialog> " -"<dialog name='ScummHelp' overlays='screen_center'> " +"<dialog name='RecorderDialog' overlays='screen' inset='8' shading='dim'> " +"<layout type='vertical' padding='8,8,8,4' center='true'> " +"<widget name='Title' " +"height='Globals.Line.Height' " +"/> " +"<widget name='List' /> " +"<layout type='horizontal' padding='0,0,0,0' spacing='2'> " +"<widget name='Edit' " +"type='Button' " +"/> " +"<space /> " +"<widget name='Record' " +"type='Button' " +"/> " +"</layout> " +"<layout type='horizontal' padding='0,0,0,0' spacing='2'> " +"<widget name='Delete' " +"type='Button' " +"/> " +"<space /> " +"<widget name='Cancel' " +"type='Button' " +"/> " +"<widget name='Playback' " +"type='Button' " +"/> " +"</layout> " +"</layout> " +"</dialog> " +"<dialog name='OnScreenDialog' overlays='screen_center'> " +"<layout type='horizontal' spacing='5' padding='3,2,3,2' center='true'> " +"<widget name='StopButton' " +"width='16' " +"height='16' " +"/> " +"<widget name='EditButton' " +"width='16' " +"height='16' " +"/> " +"<widget name='SwitchModeButton' " +"width='16' " +"height='16' " +"/> " +"<widget name='FastReplayButton' " +"width='16' " +"height='16' " +"/> " +"<widget name='TimeLabel' " +"width='50' " +"height='16' " +"/> " +"</layout> " +"</dialog> " +"<dialog name='EditRecordDialog' overlays='screen_center'> " "<layout type='vertical' padding='8,8,8,8' center='true'> " "<widget name='Title' " -"width='320' " +"height='Globals.Line.Height' " +"/> " +"<layout type='horizontal' spacing='5' padding='0,0,0,10'> " +"<widget name='AuthorLabel' " +"type='EditRecordLabel' " +"/> " +"<widget name='AuthorEdit' " +"type='EditRecord' " +"/> " +"</layout> " +"<layout type='horizontal' spacing='5' padding='0,0,0,10'> " +"<widget name='NameLabel' " +"type='EditRecordLabel' " +"/> " +"<widget name='NameEdit' " +"type='EditRecord' " +"/> " +"</layout> " +"<layout type='horizontal' spacing='5' padding='0,0,0,10'> " +"<widget name='NotesLabel' " +"type='EditRecordLabel' " +"/> " +"<widget name='NotesEdit' " +"type='EditRecord' " +"/> " +"</layout> " +"<layout type='horizontal' spacing='5' padding='0,0,0,0'> " +"<widget name='Cancel' " +"type='Button' " +"/> " +"<widget name='OK' " +"type='Button' " +"/> " +"</layout> " +"</layout> " +"</dialog> " +"<dialog name='ScummHelp' overlays='screen'> " +"<layout type='vertical' padding='8,8,8,8'> " +"<widget name='Title' " +"width='180' " "height='Globals.Line.Height' " "/> " "<widget name='HelpText' " -"height='200' " +"height='140' " "/> " -"<layout type='horizontal' padding='0,0,16,0'> " +"<layout type='horizontal' padding='0,0,0,0'> " "<widget name='Prev' " "type='Button' " "/> " @@ -2725,7 +2951,7 @@ "<dialog name='LoomTownsDifficultyDialog' overlays='screen_center'> " "<layout type='vertical' padding='8,8,8,8' center='true'> " "<widget name='Description1' " -"width='320' " +"width='280' " "height='Globals.Line.Height' " "/> " "<widget name='Description2' " @@ -2743,20 +2969,20 @@ "</layout> " "</dialog> " "<dialog name='MassAdd' overlays='screen_center' shading='dim'> " -"<layout type='vertical' padding='8,8,32,8' center='true'> " +"<layout type='vertical' padding='4,4,16,4' center='true'> " "<widget name='DirProgressText' " -"width='480' " +"width='280' " "height='Globals.Line.Height' " "/> " "<widget name='GameProgressText' " -"width='480' " +"width='280' " "height='Globals.Line.Height' " "/> " "<widget name='GameList' " -"width='480' " -"height='250' " +"width='280' " +"height='100' " "/> " -"<layout type='horizontal' padding='8,8,8,8'> " +"<layout type='horizontal' padding='4,4,4,4'> " "<widget name='Ok' " "type='Button' " "/> " @@ -2767,20 +2993,20 @@ "</layout> " "</dialog> " "<dialog name='KeyMapper' overlays='screen_center' shading='dim'> " -"<layout type='vertical' padding='8,8,32,8' spacing='10' center='true'> " +"<layout type='vertical' padding='8,8,8,8' spacing='10' center='true'> " "<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='PopupDesc' " "type='OptionsLabel' " "/> " "<widget name='Popup' " "type='PopUp' " -"width='400' " +"width='150' " "height='Globals.Line.Height' " "/> " "</layout> " "<widget name='KeymapArea' " -"width='600' " -"height='280' " +"width='300' " +"height='120' " "/> " "<widget name='Close' " "type='Button' " @@ -2788,93 +3014,91 @@ "</layout> " "</dialog> " "<dialog name='Predictive' overlays='screen_center'> " -"<layout type='vertical' padding='5,5,5,5' center='true'> " +"<layout type='vertical' padding='1,1,1,1' center='true'> " "<widget name='Headline' " "height='Globals.Line.Height' " -"width='210' " +"width='150' " "textalign='center' " "/> " -"<layout type='horizontal' padding='5,5,5,5'> " +"<layout type='horizontal' padding='3,3,3,3'> " "<widget name='Word' " -"width='190' " +"width='120' " "height='Globals.Button.Height' " "/> " "<widget name='Delete' " "width='20' " -"height='Globals.Button.Height' " +"height='Globals.Predictive.Button.Height' " "/> " "</layout> " -"<space size='5' /> " "<layout type='horizontal' padding='3,3,3,3'> " "<widget name='Button1' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Button.Height' " +"height='Globals.Predictive.Button.Height' " "/> " "<widget name='Button2' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Button.Height' " +"height='Globals.Predictive.Button.Height' " "/> " "<widget name='Button3' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Button.Height' " +"height='Globals.Predictive.Button.Height' " "/> " "</layout> " "<layout type='horizontal' padding='3,3,3,3'> " "<widget name='Button4' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Button.Height' " +"height='Globals.Predictive.Button.Height' " "/> " "<widget name='Button5' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Button.Height' " +"height='Globals.Predictive.Button.Height' " "/> " "<widget name='Button6' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Button.Height' " +"height='Globals.Predictive.Button.Height' " "/> " "</layout> " "<layout type='horizontal' padding='3,3,3,3'> " "<widget name='Button7' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Button.Height' " +"height='Globals.Predictive.Button.Height' " "/> " "<widget name='Button8' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Button.Height' " +"height='Globals.Predictive.Button.Height' " "/> " "<widget name='Button9' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Button.Height' " +"height='Globals.Predictive.Button.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='3,3,3,3'> " +"<layout type='horizontal' padding='3,3,3,0'> " "<widget name='Pre' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Button.Height' " +"height='Globals.Predictive.Button.Height' " "/> " "<widget name='Button0' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Button.Height' " +"height='Globals.Predictive.Button.Height' " "/> " "<widget name='Next' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Button.Height' " +"height='Globals.Predictive.Button.Height' " "/> " "</layout> " -"<space size='5' /> " -"<layout type='horizontal' padding='3,3,3,3'> " +"<space size='3' /> " +"<layout type='horizontal' padding='3,3,0,3'> " "<widget name='Add' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Button.Height' " +"height='Globals.Predictive.Button.Height' " "/> " -"<space size='22'/> " "<widget name='Cancel' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Button.Height' " +"height='Globals.Predictive.Button.Height' " "/> " "<widget name='OK' " "width='Globals.Predictive.Button.Width' " -"height='Globals.Button.Height' " +"height='Globals.Predictive.Button.Height' " "/> " "</layout> " "</layout> " diff --git a/gui/themes/scummclassic.zip b/gui/themes/scummclassic.zip Binary files differindex 297ff20344..4154c6c33a 100644 --- a/gui/themes/scummclassic.zip +++ b/gui/themes/scummclassic.zip diff --git a/gui/themes/scummclassic/classic_layout.stx b/gui/themes/scummclassic/classic_layout.stx index 180e8fba74..5fd2d6f835 100644 --- a/gui/themes/scummclassic/classic_layout.stx +++ b/gui/themes/scummclassic/classic_layout.stx @@ -36,6 +36,9 @@ <def var = 'ShowChooserPageDisplay' value = '1'/> <def var = 'SaveLoadChooser.ExtInfo.Visible' value = '1'/> + <def var = 'RecorderDialog.ExtInfo.Visible' value = '1'/> + + <def var = 'OnScreenDialog.ShowPics' value = '0'/> <def var = 'KeyMapper.Spacing' value = '10'/> <def var = 'KeyMapper.LabelWidth' value = '100'/> @@ -101,6 +104,12 @@ size = '15, 18' padding = '0, 3, 4, 0' /> + <widget name = 'EditRecordLabel' + size = '60, 25' + /> + <widget name = 'EditRecord' + size = '240, 25' + /> </globals> <dialog name = 'Launcher' overlays = 'screen'> @@ -1019,6 +1028,125 @@ </layout> </dialog> + <dialog name = 'RecorderDialog' overlays = 'screen' inset = '8' shading = 'dim'> + <layout type = 'vertical' padding = '8, 8, 8, 32' center = 'true'> + <widget name = 'Title' + height = 'Globals.Line.Height' + /> + <layout type = 'horizontal' padding = '0, 0, 0, 16' spacing = '16'> + <widget name = 'List' /> + <layout type = 'vertical' padding = '0, 0, 0, 0'> + <widget name = 'Thumbnail' + width = '180' + height = '170' + /> + <layout type = 'horizontal' padding = '0, 0, 0, 0'> + <widget name = 'NextScreenShotButton' + width = '25' + height = '25' + /> + <widget name = 'currentScreenshot' + width = '125' + height = '25' + textalign = 'center' + /> + <widget name = 'PreviousScreenShotButton' + width = '25' + height = '25' + /> + </layout> + <widget name = 'Author' height = 'Globals.Line.Height' /> + <widget name = 'Notes' height = 'Globals.Line.Height' /> + </layout> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0'> + <widget name = 'Delete' + type = 'Button' + /> + <space size = '16'/> + <widget name = 'Cancel' + type = 'Button' + /> + <space size = '16'/> + <widget name = 'Edit' + type = 'Button' + /> + <widget name = 'Record' + type = 'Button' + /> + <widget name = 'Playback' + type = 'Button' + /> + </layout> + </layout> + </dialog> + + <dialog name = 'OnScreenDialog' overlays = 'screen_center'> + <layout type = 'horizontal' spacing = '5' padding = '5, 3, 5, 3' center = 'true'> + <widget name = 'StopButton' + width = '32' + height = '32' + /> + <widget name = 'EditButton' + width = '32' + height = '32' + /> + <widget name = 'SwitchModeButton' + width = '32' + height = '32' + /> + <widget name = 'FastReplayButton' + width = '32' + height = '32' + /> + <widget name = 'TimeLabel' + width = '50' + height = '30' + /> + </layout> + </dialog> + + <dialog name = 'EditRecordDialog' overlays = 'screen_center'> + <layout type = 'vertical' padding = '8, 8, 8, 8' center = 'true'> + <widget name = 'Title' + width = '320' + height = 'Globals.Line.Height' + /> + <layout type = 'horizontal' spacing = '5' padding = '0, 0, 0, 10'> + <widget name = 'AuthorLabel' + type = 'EditRecordLabel' + /> + <widget name = 'AuthorEdit' + type = 'EditRecord' + /> + </layout> + <layout type = 'horizontal' spacing = '5' padding = '0, 0, 0, 10'> + <widget name = 'NameLabel' + type = 'EditRecordLabel' + /> + <widget name = 'NameEdit' + type = 'EditRecord' + /> + </layout> + <layout type = 'horizontal' spacing = '5' padding = '0, 0, 0, 10'> + <widget name = 'NotesLabel' + type = 'EditRecordLabel' + /> + <widget name = 'NotesEdit' + type = 'EditRecord' + /> + </layout> + <layout type = 'horizontal' spacing = '5' padding = '0, 0, 0, 10'> + <widget name = 'Cancel' + type = 'Button' + /> + <widget name = 'OK' + type = 'Button' + /> + </layout> + </layout> + </dialog> + <dialog name = 'ScummHelp' overlays = 'screen_center'> <layout type = 'vertical' padding = '8, 8, 8, 8' center = 'true'> <widget name = 'Title' diff --git a/gui/themes/scummclassic/classic_layout_lowres.stx b/gui/themes/scummclassic/classic_layout_lowres.stx index 8bb03dea17..802998df3c 100644 --- a/gui/themes/scummclassic/classic_layout_lowres.stx +++ b/gui/themes/scummclassic/classic_layout_lowres.stx @@ -37,6 +37,9 @@ <def var = 'ShowChooserPageDisplay' value = '0'/> <def var = 'SaveLoadChooser.ExtInfo.Visible' value = '0'/> + <def var = 'RecorderDialog.ExtInfo.Visible' value = '0'/> + + <def var = 'OnScreenDialog.ShowPics' value = '0'/> <def var = 'KeyMapper.Spacing' value = '5'/> <def var = 'KeyMapper.LabelWidth' value = '80'/> @@ -99,6 +102,12 @@ size = '32, 18' padding = '0, 0, 1, 0' /> + <widget name = 'EditRecordLabel' + size = '60, Globals.Line.Height' + /> + <widget name = 'EditRecord' + size = '120, 15' + /> </globals> <dialog name = 'Launcher' overlays = 'screen'> @@ -1013,6 +1022,101 @@ </layout> </dialog> + <dialog name = 'RecorderDialog' overlays = 'screen' inset = '8' shading = 'dim'> + <layout type = 'vertical' padding = '8, 8, 8, 4' center = 'true'> + <widget name = 'Title' + height = 'Globals.Line.Height' + /> + <widget name = 'List' /> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '2'> + <widget name = 'Edit' + type = 'Button' + /> + <space /> + <widget name = 'Record' + type = 'Button' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '2'> + <widget name = 'Delete' + type = 'Button' + /> + <space /> + <widget name = 'Cancel' + type = 'Button' + /> + <widget name = 'Playback' + type = 'Button' + /> + </layout> + </layout> + </dialog> + + <dialog name = 'OnScreenDialog' overlays = 'screen_center'> + <layout type = 'horizontal' spacing = '5' padding = '3, 2, 3, 2' center = 'true'> + <widget name = 'StopButton' + width = '16' + height = '16' + /> + <widget name = 'EditButton' + width = '16' + height = '16' + /> + <widget name = 'SwitchModeButton' + width = '16' + height = '16' + /> + <widget name = 'FastReplayButton' + width = '16' + height = '16' + /> + <widget name = 'TimeLabel' + width = '50' + height = '16' + /> + </layout> + </dialog> + + <dialog name = 'EditRecordDialog' overlays = 'screen_center'> + <layout type = 'vertical' padding = '8, 8, 8, 8' center = 'true'> + <widget name = 'Title' + height = 'Globals.Line.Height' + /> + <layout type = 'horizontal' spacing = '5' padding = '0, 0, 0, 10'> + <widget name = 'AuthorLabel' + type = 'EditRecordLabel' + /> + <widget name = 'AuthorEdit' + type = 'EditRecord' + /> + </layout> + <layout type = 'horizontal' spacing = '5' padding = '0, 0, 0, 10'> + <widget name = 'NameLabel' + type = 'EditRecordLabel' + /> + <widget name = 'NameEdit' + type = 'EditRecord' + /> + </layout> + <layout type = 'horizontal' spacing = '5' padding = '0, 0, 0, 10'> + <widget name = 'NotesLabel' + type = 'EditRecordLabel' + /> + <widget name = 'NotesEdit' + type = 'EditRecord' + /> + </layout> + <layout type = 'horizontal' spacing = '5' padding = '0, 0, 0, 0'> + <widget name = 'Cancel' + type = 'Button' + /> + <widget name = 'OK' + type = 'Button' + /> + </layout> + </layout> + </dialog> + <dialog name = 'ScummHelp' overlays = 'screen'> <layout type = 'vertical' padding = '8, 8, 8, 8'> <widget name = 'Title' diff --git a/gui/themes/scummmodern.zip b/gui/themes/scummmodern.zip Binary files differindex dbd84992e6..0f10003e94 100644 --- a/gui/themes/scummmodern.zip +++ b/gui/themes/scummmodern.zip diff --git a/gui/themes/scummmodern/editbtn.bmp b/gui/themes/scummmodern/editbtn.bmp Binary files differnew file mode 100644 index 0000000000..49eb4035b5 --- /dev/null +++ b/gui/themes/scummmodern/editbtn.bmp diff --git a/gui/themes/scummmodern/editbtn_small.bmp b/gui/themes/scummmodern/editbtn_small.bmp Binary files differnew file mode 100644 index 0000000000..8a0357fc2e --- /dev/null +++ b/gui/themes/scummmodern/editbtn_small.bmp diff --git a/gui/themes/scummmodern/fastreplay.bmp b/gui/themes/scummmodern/fastreplay.bmp Binary files differnew file mode 100644 index 0000000000..35ad2b4444 --- /dev/null +++ b/gui/themes/scummmodern/fastreplay.bmp diff --git a/gui/themes/scummmodern/fastreplay_small.bmp b/gui/themes/scummmodern/fastreplay_small.bmp Binary files differnew file mode 100644 index 0000000000..8ef004c3bf --- /dev/null +++ b/gui/themes/scummmodern/fastreplay_small.bmp diff --git a/gui/themes/scummmodern/scummmodern_gfx.stx b/gui/themes/scummmodern/scummmodern_gfx.stx index 4d449f50ec..1b3bcea0d6 100644 --- a/gui/themes/scummmodern/scummmodern_gfx.stx +++ b/gui/themes/scummmodern/scummmodern_gfx.stx @@ -103,6 +103,14 @@ <bitmap filename = 'delbtn.bmp'/> <bitmap filename = 'list.bmp'/> <bitmap filename = 'grid.bmp'/> + <bitmap filename = 'stopbtn.bmp'/> + <bitmap filename = 'editbtn.bmp'/> + <bitmap filename = 'switchbtn.bmp'/> + <bitmap filename = 'fastreplay.bmp'/> + <bitmap filename = 'stopbtn_small.bmp'/> + <bitmap filename = 'editbtn_small.bmp'/> + <bitmap filename = 'switchbtn_small.bmp'/> + <bitmap filename = 'fastreplay_small.bmp'/> </bitmaps> <fonts> diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx index 49c13cf1b0..b760e15919 100644 --- a/gui/themes/scummmodern/scummmodern_layout.stx +++ b/gui/themes/scummmodern/scummmodern_layout.stx @@ -43,6 +43,9 @@ <def var = 'ShowChooserPageDisplay' value = '1'/> <def var = 'SaveLoadChooser.ExtInfo.Visible' value = '1'/> + <def var = 'RecorderDialog.ExtInfo.Visible' value = '1'/> + + <def var = 'OnScreenDialog.ShowPics' value = '1'/> <def var = 'KeyMapper.Spacing' value = '10'/> <def var = 'KeyMapper.LabelWidth' value = '100'/> @@ -106,6 +109,13 @@ size = '15, 18' padding = '0, 3, 4, 0' /> + + <widget name = 'EditRecordLabel' + size = '60, 25' + /> + <widget name = 'EditRecord' + size = '220, 25' + /> </globals> <dialog name = 'Launcher' overlays = 'screen'> @@ -1032,6 +1042,126 @@ </layout> </dialog> + <dialog name = 'RecorderDialog' overlays = 'screen' inset = '8' shading = 'dim'> + <layout type = 'vertical' padding = '8, 8, 8, 32' center = 'true'> + <widget name = 'Title' + height = 'Globals.Line.Height' + /> + <layout type = 'horizontal' padding = '0, 0, 0, 16' spacing = '16'> + <widget name = 'List' /> + <layout type = 'vertical' padding = '0, 0, 0, 0'> + <widget name = 'Thumbnail' + width = '180' + height = '170' + /> + <layout type = 'horizontal' padding = '0, 0, 0, 0'> + <widget name = 'NextScreenShotButton' + width = '25' + height = '25' + /> + <widget name = 'currentScreenshot' + width = '125' + height = '25' + textalign = 'center' + /> + <widget name = 'PreviousScreenShotButton' + width = '25' + height = '25' + /> + </layout> + <widget name = 'Author' height = 'Globals.Line.Height' /> + <widget name = 'Notes' height = 'Globals.Line.Height' /> + </layout> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0'> + <space/> + <widget name = 'Delete' + type = 'Button' + /> + <space size = '16'/> + <widget name = 'Cancel' + type = 'Button' + /> + <space size = '16'/> + <widget name = 'Edit' + type = 'Button' + /> + <widget name = 'Record' + type = 'Button' + /> + <widget name = 'Playback' + type = 'Button' + /> + </layout> + </layout> + </dialog> + + <dialog name = 'OnScreenDialog' overlays = 'screen_center'> + <layout type = 'horizontal' spacing = '5' padding = '5, 3, 5, 3' center = 'true'> + <widget name = 'StopButton' + width = '32' + height = '32' + /> + <widget name = 'EditButton' + width = '32' + height = '32' + /> + <widget name = 'SwitchModeButton' + width = '32' + height = '32' + /> + <widget name = 'FastReplayButton' + width = '32' + height = '32' + /> + <widget name = 'TimeLabel' + width = '50' + height = '30' + /> + </layout> + </dialog> + + <dialog name = 'EditRecordDialog' overlays = 'screen_center'> + <layout type = 'vertical' padding = '8, 8, 8, 8' center = 'true'> + <widget name = 'Title' + width = '320' + height = 'Globals.Line.Height' + /> + <layout type = 'horizontal' spacing = '5' padding = '0, 0, 0, 10'> + <widget name = 'AuthorLabel' + type = 'EditRecordLabel' + /> + <widget name = 'AuthorEdit' + type = 'EditRecord' + /> + </layout> + <layout type = 'horizontal' spacing = '5' padding = '0, 0, 0, 10'> + <widget name = 'NameLabel' + type = 'EditRecordLabel' + /> + <widget name = 'NameEdit' + type = 'EditRecord' + /> + </layout> + <layout type = 'horizontal' spacing = '5' padding = '0, 0, 0, 10'> + <widget name = 'NotesLabel' + type = 'EditRecordLabel' + /> + <widget name = 'NotesEdit' + type = 'EditRecord' + /> + </layout> + <layout type = 'horizontal' spacing = '5' padding = '0, 0, 0, 10'> + <widget name = 'Cancel' + type = 'Button' + /> + <widget name = 'OK' + type = 'Button' + /> + </layout> + </layout> + </dialog> + <dialog name = 'ScummHelp' overlays = 'screen_center'> <layout type = 'vertical' padding = '8, 8, 8, 8' center = 'true'> <widget name = 'Title' diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx index 9658402f82..cee1e4af2b 100644 --- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx +++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx @@ -35,6 +35,9 @@ <def var = 'ShowChooserPageDisplay' value = '0'/> <def var = 'SaveLoadChooser.ExtInfo.Visible' value = '0'/> + <def var = 'RecorderDialog.ExtInfo.Visible' value = '0'/> + + <def var = 'OnScreenDialog.ShowPics' value = '1'/> <def var = 'Predictive.Button.Width' value = '45' /> <def var = 'Predictive.Button.Height' value = '15' /> @@ -97,6 +100,12 @@ size = '32, 18' padding = '0, 0, 2, 0' /> + <widget name = 'EditRecordLabel' + size = '60, Globals.Line.Height' + /> + <widget name = 'EditRecord' + size = '120, 15' + /> </globals> <dialog name = 'Launcher' overlays = 'screen'> @@ -1012,6 +1021,122 @@ </layout> </dialog> + <dialog name = 'SavenameDialog' overlays = 'screen_center'> + <layout type = 'vertical' padding = '8, 8, 8, 8'> + <widget name = 'DescriptionText' + width = '320' + height = 'Globals.Line.Height' + /> + <widget name = 'Description' + height = '19' + /> + <layout type = 'horizontal' padding = '0, 0, 16, 0'> + <widget name = 'Cancel' + type = 'Button' + /> + <space size = '96'/> + <widget name = 'Ok' + type = 'Button' + /> + </layout> + </layout> + </dialog> + + <dialog name = 'RecorderDialog' overlays = 'screen' inset = '8' shading = 'dim'> + <layout type = 'vertical' padding = '8, 8, 8, 4' center = 'true'> + <widget name = 'Title' + height = 'Globals.Line.Height' + /> + <widget name = 'List' /> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '2'> + <widget name = 'Edit' + type = 'Button' + /> + <space /> + <widget name = 'Record' + type = 'Button' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '2'> + <widget name = 'Delete' + type = 'Button' + /> + <space /> + <widget name = 'Cancel' + type = 'Button' + /> + <widget name = 'Playback' + type = 'Button' + /> + </layout> + </layout> + </dialog> + + <dialog name = 'OnScreenDialog' overlays = 'screen_center'> + <layout type = 'horizontal' spacing = '5' padding = '3, 2, 3, 2' center = 'true'> + <widget name = 'StopButton' + width = '16' + height = '16' + /> + <widget name = 'EditButton' + width = '16' + height = '16' + /> + <widget name = 'SwitchModeButton' + width = '16' + height = '16' + /> + <widget name = 'FastReplayButton' + width = '16' + height = '16' + /> + <widget name = 'TimeLabel' + width = '50' + height = '16' + /> + </layout> + </dialog> + + <dialog name = 'EditRecordDialog' overlays = 'screen_center'> + <layout type = 'vertical' padding = '8, 8, 8, 8' center = 'true'> + <widget name = 'Title' + height = 'Globals.Line.Height' + /> + <layout type = 'horizontal' spacing = '5' padding = '0, 0, 0, 10'> + <widget name = 'AuthorLabel' + type = 'EditRecordLabel' + /> + <widget name = 'AuthorEdit' + type = 'EditRecord' + /> + </layout> + <layout type = 'horizontal' spacing = '5' padding = '0, 0, 0, 10'> + <widget name = 'NameLabel' + type = 'EditRecordLabel' + /> + <widget name = 'NameEdit' + type = 'EditRecord' + /> + </layout> + <layout type = 'horizontal' spacing = '5' padding = '0, 0, 0, 10'> + <widget name = 'NotesLabel' + type = 'EditRecordLabel' + /> + <widget name = 'NotesEdit' + type = 'EditRecord' + /> + </layout> + <layout type = 'horizontal' spacing = '5' padding = '0, 0, 0, 0'> + <widget name = 'Cancel' + type = 'Button' + /> + <widget name = 'OK' + type = 'Button' + /> + </layout> + </layout> + </dialog> + <dialog name = 'ScummHelp' overlays = 'screen' inset = '8'> <layout type = 'vertical' padding = '8, 8, 8, 8'> <widget name = 'Title' diff --git a/gui/themes/scummmodern/stopbtn.bmp b/gui/themes/scummmodern/stopbtn.bmp Binary files differnew file mode 100644 index 0000000000..3575956694 --- /dev/null +++ b/gui/themes/scummmodern/stopbtn.bmp diff --git a/gui/themes/scummmodern/stopbtn_small.bmp b/gui/themes/scummmodern/stopbtn_small.bmp Binary files differnew file mode 100644 index 0000000000..ffd5025279 --- /dev/null +++ b/gui/themes/scummmodern/stopbtn_small.bmp diff --git a/gui/themes/scummmodern/switchbtn.bmp b/gui/themes/scummmodern/switchbtn.bmp Binary files differnew file mode 100644 index 0000000000..6bafa4a998 --- /dev/null +++ b/gui/themes/scummmodern/switchbtn.bmp diff --git a/gui/themes/scummmodern/switchbtn_small.bmp b/gui/themes/scummmodern/switchbtn_small.bmp Binary files differnew file mode 100644 index 0000000000..929b128884 --- /dev/null +++ b/gui/themes/scummmodern/switchbtn_small.bmp |