diff options
53 files changed, 840 insertions, 416 deletions
@@ -3,7 +3,17 @@ For a more comprehensive changelog of the latest experimental code, see: 1.5.0 (????-??-??) New Games: + - Added support for Blue Force. + - Added support for Darby the Dragon. + - Added support for Dreamweb. + - Added support for Gregory and the Hot Air Balloon. + - Added support for Magic Tales: Baba Yaga and the Magic Geese. + - Added support for Magic Tales: Imo and the King. + - Added support for Magic Tales: Liam Finds a Story. + - Added support for Magic Tales: The Little Samurai. + - Added support for Sleeping Cub's Test of Courage. - Added support for Soltys. + - Added support for The Princess and the Crab. General: - Updated MT-32 emulation code to latest munt project snapshot. The emulation diff --git a/audio/decoders/quicktime.cpp b/audio/decoders/quicktime.cpp index 48e76a94da..99c1527a71 100644 --- a/audio/decoders/quicktime.cpp +++ b/audio/decoders/quicktime.cpp @@ -449,7 +449,7 @@ void QuickTimeAudioDecoder::QuickTimeAudioTrack::skipSamples(const Timestamp &le } void QuickTimeAudioDecoder::QuickTimeAudioTrack::findEdit(const Timestamp &position) { - for (_curEdit = 0; _curEdit < _parentTrack->editCount && position < Timestamp(0, _parentTrack->editList[_curEdit].timeOffset, _decoder->_timeScale); _curEdit++) + for (_curEdit = 0; _curEdit < _parentTrack->editCount - 1 && position > Timestamp(0, _parentTrack->editList[_curEdit].timeOffset, _decoder->_timeScale); _curEdit++) ; enterNewEdit(position); diff --git a/backends/graphics/opengl/opengl-graphics.cpp b/backends/graphics/opengl/opengl-graphics.cpp index 45804b5d6e..cd820ae3b2 100644 --- a/backends/graphics/opengl/opengl-graphics.cpp +++ b/backends/graphics/opengl/opengl-graphics.cpp @@ -70,6 +70,11 @@ OpenGLGraphicsManager::~OpenGLGraphicsManager() { free(_gamePalette); free(_cursorPalette); + _screenData.free(); + _overlayData.free(); + _cursorData.free(); + _osdSurface.free(); + delete _gameTexture; delete _overlayTexture; delete _cursorTexture; diff --git a/base/main.cpp b/base/main.cpp index c657488758..25e1b881cc 100644 --- a/base/main.cpp +++ b/base/main.cpp @@ -55,6 +55,12 @@ #include "audio/mididrv.h" #include "audio/musicplugin.h" /* for music manager */ +#include "graphics/cursorman.h" +#include "graphics/fontman.h" +#ifdef USE_FREETYPE2 +#include "graphics/fonts/ttf.h" +#endif + #include "backends/keymapper/keymapper.h" #if defined(_WIN32_WCE) @@ -493,10 +499,18 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) { PluginManager::destroy(); GUI::GuiManager::destroy(); Common::ConfigManager::destroy(); + Common::DebugManager::destroy(); + Common::EventRecorder::destroy(); Common::SearchManager::destroy(); #ifdef USE_TRANSLATION Common::TranslationManager::destroy(); #endif + MusicManager::destroy(); + Graphics::CursorManager::destroy(); + Graphics::FontManager::destroy(); +#ifdef USE_FREETYPE2 + Graphics::shutdownTTF(); +#endif return 0; } diff --git a/common/unzip.cpp b/common/unzip.cpp index 8cfcd605fa..ab659343a2 100644 --- a/common/unzip.cpp +++ b/common/unzip.cpp @@ -1463,22 +1463,16 @@ bool ZipArchive::hasFile(const String &name) const { } int ZipArchive::listMembers(ArchiveMemberList &list) const { - int matches = 0; - int err = unzGoToFirstFile(_zipFile); + int members = 0; - while (err == UNZ_OK) { - char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; - if (unzGetCurrentFileInfo(_zipFile, NULL, - szCurrentFileName, sizeof(szCurrentFileName)-1, - NULL, 0, NULL, 0) == UNZ_OK) { - list.push_back(ArchiveMemberList::value_type(new GenericArchiveMember(szCurrentFileName, this))); - matches++; - } - - err = unzGoToNextFile(_zipFile); + const unz_s *const archive = (const unz_s *)_zipFile; + for (ZipHash::const_iterator i = archive->_hash.begin(), end = archive->_hash.end(); + i != end; ++i) { + list.push_back(ArchiveMemberList::value_type(new GenericArchiveMember(i->_key, this))); + ++members; } - return matches; + return members; } const ArchiveMemberPtr ZipArchive::getMember(const String &name) const { @@ -840,7 +840,7 @@ Optional Libraries: --with-sdl-prefix=DIR Prefix where the sdl-config script is installed (optional) - --with-freetype-prefix=DIR Prefix where the freetype-config script is + --with-freetype2-prefix=DIR Prefix where the freetype-config script is installed (optional) --with-nasm-prefix=DIR Prefix where nasm executable is installed (optional) @@ -1403,6 +1403,10 @@ ps3) echo "Please set PS3DEV in your environment. export PS3DEV=<path to ps3 toolchain>" exit 1 fi + if test -z "$PSL1GHT"; then + echo "Please set PSL1GHT in your environment. export PSL1GHT=<path to psl1ght>" + exit 1 + fi ;; psp) if test -z "$PSPDEV"; then @@ -2068,8 +2072,8 @@ case $_host_os in _sdlpath="$PS3DEV/portlibs/ppu:$PS3DEV/portlibs/ppu/bin" DEFINES="$DEFINES -DPLAYSTATION3" - CXXFLAGS="$CXXFLAGS -mcpu=cell -mminimal-toc -I$PS3DEV/psl1ght/ppu/include -I$PS3DEV/portlibs/ppu/include" - LDFLAGS="$LDFLAGS -L$PS3DEV/psl1ght/ppu/lib -L$PS3DEV/portlibs/ppu/lib" + CXXFLAGS="$CXXFLAGS -mcpu=cell -mminimal-toc -I$PSL1GHT/ppu/include -I$PS3DEV/portlibs/ppu/include" + LDFLAGS="$LDFLAGS -L$PSL1GHT/ppu/lib -L$PS3DEV/portlibs/ppu/lib" add_line_to_config_mk 'PLAYSTATION3 = 1' add_line_to_config_h "#define PREFIX \"${prefix}\"" ;; diff --git a/devtools/create_project/create_project.cpp b/devtools/create_project/create_project.cpp index 293cc0b2de..df220f0934 100644 --- a/devtools/create_project/create_project.cpp +++ b/devtools/create_project/create_project.cpp @@ -654,14 +654,14 @@ void displayHelp(const char *exe) { "\n" "Engines settings:\n" " --list-engines list all available engines and their default state\n" - " --enable-engine enable building of the engine with the name \"engine\"\n" - " --disable-engine disable building of the engine with the name \"engine\"\n" + " --enable-engine=<name> enable building of the engine with the name \"name\"\n" + " --disable-engine=<name> disable building of the engine with the name \"name\"\n" " --enable-all-engines enable building of all engines\n" " --disable-all-engines disable building of all engines\n" "\n" "Optional features settings:\n" - " --enable-name enable inclusion of the feature \"name\"\n" - " --disable-name disable inclusion of the feature \"name\"\n" + " --enable-<name> enable inclusion of the feature \"name\"\n" + " --disable-<name> disable inclusion of the feature \"name\"\n" "\n" " There are the following features available:\n" "\n"; diff --git a/devtools/scumm-md5.txt b/devtools/scumm-md5.txt index 01d806a484..7d2ea94f10 100644 --- a/devtools/scumm-md5.txt +++ b/devtools/scumm-md5.txt @@ -590,6 +590,7 @@ FreddisFunShop Freddi Fish's One-Stop Fun Shop catalog Humongous Interactive Catalog 11e6e244078ff09b0f3832e35420e0a7 -1 en Windows - Demo - khalek, sev 037385a953789190298494d92b89b3d0 -1 en Windows HE 72 Demo - khalek, sev + f7635a0e2ab82c9a0f9ace5f232a488f -1 en Windows HE 72 Demo - Kirben a56e8d9d4281c53c3f63c9bd22a59e21 10978342 en All HE CUP Preview George Kormendi 74da3494fbe1a7d20213b0afe0954755 10841544 fr All HE CUP Preview - George Kormendi 4c4820518e16e1a0e3616a3b021a04f3 10927456 de All HE CUP Preview - Kirben diff --git a/engines/agi/saveload.cpp b/engines/agi/saveload.cpp index 8e524c8d9a..d58e55a6b9 100644 --- a/engines/agi/saveload.cpp +++ b/engines/agi/saveload.cpp @@ -831,6 +831,9 @@ int AgiEngine::scummVMSaveLoadDialog(bool isSave) { delete dialog; + if (slot < 0) + return true; + if (isSave) return doSave(slot, desc); else diff --git a/engines/configure.engines b/engines/configure.engines index 93c349378d..91e5e82506 100644 --- a/engines/configure.engines +++ b/engines/configure.engines @@ -7,11 +7,11 @@ add_engine agos "AGOS" yes "agos2" add_engine agos2 "AGOS 2 games" yes add_engine cge "CGE" yes add_engine cine "Cinematique evo 1" yes -add_engine composer "Magic Composer" no +add_engine composer "Magic Composer" yes add_engine cruise "Cinematique evo 2" yes add_engine draci "Dragon History" yes add_engine drascula "Drascula: The Vampire Strikes Back" yes -add_engine dreamweb "Dreamweb" no +add_engine dreamweb "Dreamweb" yes add_engine gob "Gobli*ns" yes add_engine groovie "Groovie" yes "groovie2" add_engine groovie2 "Groovie 2 games" no diff --git a/engines/cruise/cruise_main.cpp b/engines/cruise/cruise_main.cpp index 6e2847d6d7..911041c1a4 100644 --- a/engines/cruise/cruise_main.cpp +++ b/engines/cruise/cruise_main.cpp @@ -1799,32 +1799,60 @@ void CruiseEngine::mainLoop() { // Handle frame delay uint32 currentTick = g_system->getMillis(); - if (!bFastMode) { - // Delay for the specified amount of time, but still respond to events - bool skipEvents = false; + // Delay for the specified amount of time, but still respond to events + bool skipEvents = false; - do { - g_system->updateScreen(); + do { + if (userEnabled && !userWait && !autoTrack) { + if (currentActiveMenu == -1) { + static int16 oldMouseX = -1; + static int16 oldMouseY = -1; - g_system->delayMillis(10); - currentTick = g_system->getMillis(); + getMouseStatus(&main10, &mouseX, &mouseButton, &mouseY); - if (!skipEvents) - skipEvents = manageEvents(); + if (mouseX != oldMouseX || mouseY != oldMouseY) { + int objectType; + int newCursor1; + int newCursor2; - if (playerDontAskQuit) - break; + oldMouseX = mouseX; + oldMouseY = mouseY; - _vm->getDebugger()->onFrame(); - } while (currentTick < lastTick + _gameSpeed); - } else { - manageEvents(); + objectType = findObject(mouseX, mouseY, &newCursor1, &newCursor2); - if (currentTick >= (lastTickDebug + 10)) { - lastTickDebug = currentTick; - _vm->getDebugger()->onFrame(); + if (objectType == 9) { + changeCursor(CURSOR_EXIT); + } else if (objectType != -1) { + changeCursor(CURSOR_MAGNIFYING_GLASS); + } else { + changeCursor(CURSOR_WALK); + } + } + } else { + changeCursor(CURSOR_NORMAL); + } + } else { + changeCursor(CURSOR_NORMAL); } - } + + g_system->updateScreen(); + + if (!skipEvents || bFastMode) + skipEvents = manageEvents(); + + if (bFastMode) { + if (currentTick >= (lastTickDebug + 10)) + lastTickDebug = currentTick; + } else { + g_system->delayMillis(10); + currentTick = g_system->getMillis(); + } + + if (playerDontAskQuit) + break; + + _vm->getDebugger()->onFrame(); + } while (currentTick < lastTick + _gameSpeed && !bFastMode); if (playerDontAskQuit) break; @@ -1844,6 +1872,14 @@ void CruiseEngine::mainLoop() { // readKeyboard(); bool isUserWait = userWait != 0; + // WORKAROUND: This prevents hotspots responding during + // delays i.e. Menu opening if you click fast on another + // hotspot after trying to open a locked door, which + // occurred with the original interpreter. + if (userDelay) { + currentMouseButton = 0; + } + playerDontAskQuit = processInput(); if (playerDontAskQuit) break; @@ -1855,7 +1891,6 @@ void CruiseEngine::mainLoop() { if (userDelay && !userWait) { userDelay--; - continue; } if (isUserWait & !userWait) { @@ -1918,38 +1953,6 @@ void CruiseEngine::mainLoop() { mainDraw(userWait); flipScreen(); - if (userEnabled && !userWait && !autoTrack) { - if (currentActiveMenu == -1) { - static int16 oldMouseX = -1; - static int16 oldMouseY = -1; - - getMouseStatus(&main10, &mouseX, &mouseButton, &mouseY); - - if (mouseX != oldMouseX || mouseY != oldMouseY) { - int objectType; - int newCursor1; - int newCursor2; - - oldMouseX = mouseX; - oldMouseY = mouseY; - - objectType = findObject(mouseX, mouseY, &newCursor1, &newCursor2); - - if (objectType == 9) { - changeCursor(CURSOR_EXIT); - } else if (objectType != -1) { - changeCursor(CURSOR_MAGNIFYING_GLASS); - } else { - changeCursor(CURSOR_WALK); - } - } - } else { - changeCursor(CURSOR_NORMAL); - } - } else { - changeCursor(CURSOR_NORMAL); - } - if (userWait == 1) { // Waiting for press - original wait loop has been integrated into the // main event loop diff --git a/engines/dreamweb/detection_tables.h b/engines/dreamweb/detection_tables.h index 8ca24d1724..d54b2402c8 100644 --- a/engines/dreamweb/detection_tables.h +++ b/engines/dreamweb/detection_tables.h @@ -45,7 +45,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::EN_ANY, Common::kPlatformPC, - ADGF_UNSTABLE, + ADGF_TESTING, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -62,7 +62,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::EN_ANY, Common::kPlatformPC, - ADGF_CD | ADGF_UNSTABLE, + ADGF_CD | ADGF_TESTING, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -96,7 +96,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::FR_FRA, Common::kPlatformPC, - ADGF_CD | ADGF_UNSTABLE, + ADGF_CD | ADGF_TESTING, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -113,7 +113,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::DE_DEU, Common::kPlatformPC, - ADGF_UNSTABLE, + ADGF_TESTING, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -130,7 +130,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::DE_DEU, Common::kPlatformPC, - ADGF_CD | ADGF_UNSTABLE, + ADGF_CD | ADGF_TESTING, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -147,7 +147,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::ES_ESP, Common::kPlatformPC, - ADGF_UNSTABLE, + ADGF_TESTING, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -164,7 +164,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::ES_ESP, Common::kPlatformPC, - ADGF_CD | ADGF_UNSTABLE, + ADGF_CD | ADGF_TESTING, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -181,7 +181,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::IT_ITA, Common::kPlatformPC, - ADGF_UNSTABLE, + ADGF_TESTING, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, diff --git a/engines/dreamweb/dreamweb.cpp b/engines/dreamweb/dreamweb.cpp index 2e9c7bb2b8..299dd74b53 100644 --- a/engines/dreamweb/dreamweb.cpp +++ b/engines/dreamweb/dreamweb.cpp @@ -422,25 +422,6 @@ void DreamWebEngine::keyPressed(uint16 ascii) { DreamWeb::g_keyBuffer[in] = ascii; } -void DreamWebEngine::mouseCall(uint16 *x, uint16 *y, uint16 *state) { - processEvents(); - Common::Point pos = _eventMan->getMousePos(); - if (pos.x > 298) - pos.x = 298; - if (pos.x < 15) - pos.x = 15; - if (pos.y < 15) - pos.y = 15; - if (pos.y > 184) - pos.y = 184; - *x = pos.x; - *y = pos.y; - - unsigned newState = _eventMan->getButtonState(); - *state = (newState == _oldMouseState? 0 : newState); - _oldMouseState = newState; -} - void DreamWebEngine::getPalette(uint8 *data, uint start, uint count) { _system->getPaletteManager()->grabPalette(data, start, count); while (count--) @@ -541,6 +522,7 @@ uint8 DreamWebEngine::modifyChar(uint8 c) const { return c; } case Common::FR_FRA: + case Common::IT_ITA: switch(c) { case 133: return 'Z' + 1; diff --git a/engines/dreamweb/module.mk b/engines/dreamweb/module.mk index 6bc4f8728e..3e367c6fdf 100644 --- a/engines/dreamweb/module.mk +++ b/engines/dreamweb/module.mk @@ -7,6 +7,7 @@ MODULE_OBJS := \ dreamweb.o \ keypad.o \ monitor.o \ + mouse.o \ newplace.o \ object.o \ pathfind.o \ diff --git a/engines/dreamweb/mouse.cpp b/engines/dreamweb/mouse.cpp new file mode 100644 index 0000000000..77d907611d --- /dev/null +++ b/engines/dreamweb/mouse.cpp @@ -0,0 +1,171 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/events.h" +#include "dreamweb/dreamweb.h" + +namespace DreamWeb { + +void DreamWebEngine::mouseCall(uint16 *x, uint16 *y, uint16 *state) { + processEvents(); + Common::Point pos = _eventMan->getMousePos(); + *x = CLIP<int16>(pos.x, 15, 298); + *y = CLIP<int16>(pos.y, 15, 184); + + unsigned newState = _eventMan->getButtonState(); + *state = (newState == _oldMouseState ? 0 : newState); + _oldMouseState = newState; +} + +void DreamWebEngine::readMouse() { + _oldButton = _mouseButton; + _mouseButton = readMouseState(); +} + +uint16 DreamWebEngine::readMouseState() { + _oldX = _mouseX; + _oldY = _mouseY; + uint16 x, y, state; + mouseCall(&x, &y, &state); + _mouseX = x; + _mouseY = y; + return state; +} + +void DreamWebEngine::dumpPointer() { + dumpBlink(); + multiDump(_delHereX, _delHereY, _delXS, _delYS); + if ((_oldPointerX != _delHereX) || (_oldPointerY != _delHereY)) + multiDump(_oldPointerX, _oldPointerY, _pointerXS, _pointerYS); +} + +void DreamWebEngine::showPointer() { + showBlink(); + uint16 x = _mouseX; + _oldPointerX = _mouseX; + uint16 y = _mouseY; + _oldPointerY = _mouseY; + if (_pickUp == 1) { + const GraphicsFile *frames; + if (_objectType != kExObjectType) + frames = &_freeFrames; + else + frames = &_exFrames; + const Frame *frame = &frames->_frames[(3 * _itemFrame + 1)]; + + uint8 width = MAX<uint8>(frame->width, 12); + uint8 height = MAX<uint8>(frame->height, 12); + _pointerXS = width; + _pointerYS = height; + uint16 xMin = (x >= width / 2) ? x - width / 2 : 0; + uint16 yMin = (y >= height / 2) ? y - height / 2 : 0; + _oldPointerX = xMin; + _oldPointerY = yMin; + multiGet(_pointerBack, xMin, yMin, width, height); + showFrame(*frames, x, y, 3 * _itemFrame + 1, 128); + showFrame(_icons1, x, y, 3, 128); + } else { + const Frame *frame = &_icons1._frames[_pointerFrame + 20]; + uint8 width = MAX<uint8>(frame->width, 12); + uint8 height = MAX<uint8>(frame->height, 12); + _pointerXS = width; + _pointerYS = height; + multiGet(_pointerBack, x, y, width, height); + showFrame(_icons1, x, y, _pointerFrame + 20, 0); + } +} + +void DreamWebEngine::delPointer() { + if (_oldPointerX == 0xffff) + return; + _delHereX = _oldPointerX; + _delHereY = _oldPointerY; + _delXS = _pointerXS; + _delYS = _pointerYS; + multiPut(_pointerBack, _delHereX, _delHereY, _pointerXS, _pointerYS); +} + +void DreamWebEngine::animPointer() { + if (_pointerMode == 2) { + _pointerFrame = 0; + if ((_realLocation == 14) && (_commandType == 211)) + _pointerFrame = 5; + return; + } else if (_pointerMode == 3) { + if (_pointerSpeed != 0) { + --_pointerSpeed; + } else { + _pointerSpeed = 5; + ++_pointerCount; + if (_pointerCount == 16) + _pointerCount = 0; + } + _pointerFrame = (_pointerCount <= 8) ? 1 : 2; + return; + } + if (_vars._watchingTime != 0) { + _pointerFrame = 11; + return; + } + _pointerFrame = 0; + if (_inMapArea == 0) + return; + if (_pointerFirstPath == 0) + return; + uint8 flag, flagEx; + getFlagUnderP(&flag, &flagEx); + if (flag < 2) + return; + if (flag >= 128) + return; + if (flag & 4) { + _pointerFrame = 3; + return; + } + if (flag & 16) { + _pointerFrame = 4; + return; + } + if (flag & 2) { + _pointerFrame = 5; + return; + } + if (flag & 8) { + _pointerFrame = 6; + return; + } + _pointerFrame = 8; +} + +void DreamWebEngine::checkCoords(const RectWithCallback *rectWithCallbacks) { + if (_newLocation != 0xff) + return; + const RectWithCallback *r; + for (r = rectWithCallbacks; r->_xMin != 0xffff; ++r) { + if (r->contains(_mouseX, _mouseY)) { + (this->*(r->_callback))(); + return; + } + } +} + +} // End of namespace DreamWeb diff --git a/engines/dreamweb/stubs.cpp b/engines/dreamweb/stubs.cpp index 5a53b82510..750dafe7b4 100644 --- a/engines/dreamweb/stubs.cpp +++ b/engines/dreamweb/stubs.cpp @@ -876,22 +876,6 @@ void DreamWebEngine::hangOnCurs(uint16 frameCount) { } } -void DreamWebEngine::readMouse() { - _oldButton = _mouseButton; - uint16 state = readMouseState(); - _mouseButton = state; -} - -uint16 DreamWebEngine::readMouseState() { - _oldX = _mouseX; - _oldY = _mouseY; - uint16 x, y, state; - mouseCall(&x, &y, &state); - _mouseX = x; - _mouseY = y; - return state; -} - void DreamWebEngine::dumpTextLine() { if (_newTextLine != 1) return; @@ -1513,16 +1497,6 @@ void DreamWebEngine::obName(uint8 command, uint8 commandType) { } } -void DreamWebEngine::delPointer() { - if (_oldPointerX == 0xffff) - return; - _delHereX = _oldPointerX; - _delHereY = _oldPointerY; - _delXS = _pointerXS; - _delYS = _pointerYS; - multiPut(_pointerBack, _delHereX, _delHereY, _pointerXS, _pointerYS); -} - void DreamWebEngine::showBlink() { if (_manIsOffScreen == 1) return; @@ -1554,110 +1528,6 @@ void DreamWebEngine::dumpBlink() { multiDump(44, 32, 16, 12); } -void DreamWebEngine::dumpPointer() { - dumpBlink(); - multiDump(_delHereX, _delHereY, _delXS, _delYS); - if ((_oldPointerX != _delHereX) || (_oldPointerY != _delHereY)) - multiDump(_oldPointerX, _oldPointerY, _pointerXS, _pointerYS); -} - -void DreamWebEngine::showPointer() { - showBlink(); - uint16 x = _mouseX; - _oldPointerX = _mouseX; - uint16 y = _mouseY; - _oldPointerY = _mouseY; - if (_pickUp == 1) { - const GraphicsFile *frames; - if (_objectType != kExObjectType) - frames = &_freeFrames; - else - frames = &_exFrames; - const Frame *frame = &frames->_frames[(3 * _itemFrame + 1)]; - - uint8 width = frame->width; - uint8 height = frame->height; - if (width < 12) - width = 12; - if (height < 12) - height = 12; - _pointerXS = width; - _pointerYS = height; - uint16 xMin = (x >= width / 2) ? x - width / 2 : 0; - uint16 yMin = (y >= height / 2) ? y - height / 2 : 0; - _oldPointerX = xMin; - _oldPointerY = yMin; - multiGet(_pointerBack, xMin, yMin, width, height); - showFrame(*frames, x, y, 3 * _itemFrame + 1, 128); - showFrame(_icons1, x, y, 3, 128); - } else { - const Frame *frame = &_icons1._frames[_pointerFrame + 20]; - uint8 width = frame->width; - uint8 height = frame->height; - if (width < 12) - width = 12; - if (height < 12) - height = 12; - _pointerXS = width; - _pointerYS = height; - multiGet(_pointerBack, x, y, width, height); - showFrame(_icons1, x, y, _pointerFrame + 20, 0); - } -} - -void DreamWebEngine::animPointer() { - - if (_pointerMode == 2) { - _pointerFrame = 0; - if ((_realLocation == 14) && (_commandType == 211)) - _pointerFrame = 5; - return; - } else if (_pointerMode == 3) { - if (_pointerSpeed != 0) { - --_pointerSpeed; - } else { - _pointerSpeed = 5; - ++_pointerCount; - if (_pointerCount == 16) - _pointerCount = 0; - } - _pointerFrame = (_pointerCount <= 8) ? 1 : 2; - return; - } - if (_vars._watchingTime != 0) { - _pointerFrame = 11; - return; - } - _pointerFrame = 0; - if (_inMapArea == 0) - return; - if (_pointerFirstPath == 0) - return; - uint8 flag, flagEx; - getFlagUnderP(&flag, &flagEx); - if (flag < 2) - return; - if (flag >= 128) - return; - if (flag & 4) { - _pointerFrame = 3; - return; - } - if (flag & 16) { - _pointerFrame = 4; - return; - } - if (flag & 2) { - _pointerFrame = 5; - return; - } - if (flag & 8) { - _pointerFrame = 6; - return; - } - _pointerFrame = 8; -} - void DreamWebEngine::printMessage(uint16 x, uint16 y, uint8 index, uint8 maxWidth, bool centered) { const uint8 *string = (const uint8 *)_commandText.getString(index); printDirect(string, x, y, maxWidth, centered); @@ -3183,16 +3053,4 @@ void DreamWebEngine::purgeAnItem() { } } -void DreamWebEngine::checkCoords(const RectWithCallback *rectWithCallbacks) { - if (_newLocation != 0xff) - return; - const RectWithCallback *r; - for (r = rectWithCallbacks; r->_xMin != 0xffff; ++r) { - if (r->contains(_mouseX, _mouseY)) { - (this->*(r->_callback))(); - return; - } - } -} - } // End of namespace DreamWeb diff --git a/engines/kyra/screen.cpp b/engines/kyra/screen.cpp index d3b4d6f943..711fe15348 100644 --- a/engines/kyra/screen.cpp +++ b/engines/kyra/screen.cpp @@ -95,8 +95,23 @@ bool Screen::init() { _use16ColorMode = _vm->gameFlags().use16ColorMode; _isAmiga = (_vm->gameFlags().platform == Common::kPlatformAmiga); - if (ConfMan.hasKey("render_mode")) - _renderMode = Common::parseRenderMode(ConfMan.get("render_mode")); + // We only check the "render_mode" setting for both Eye of the Beholder + // games here, since all the other games do not support the render_mode + // setting or handle it differently, like Kyra 1 PC-98. This avoids + // graphics glitches and crashes in other games, when the user sets his + // global render_mode setting to EGA for example. + // TODO/FIXME: It would be nice not to hardcode this. But there is no + // trivial/non annoying way to do mode checks in an easy fashion right + // now. + // In a more general sense, we might want to think about a way to only + // pass valid config values, as in values which the engine can work with, + // to the engines. We already limit the selection via our GUIO flags in + // the game specific settings, but this is not enough due to global + // settings allowing everything. + if (_vm->game() == GI_EOB1 || _vm->game() == GI_EOB2) { + if (ConfMan.hasKey("render_mode")) + _renderMode = Common::parseRenderMode(ConfMan.get("render_mode")); + } // CGA and EGA modes use additional pages to do the CGA/EGA specific graphics conversions. if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderEGA) { diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h index d741bb801f..ff78d4f18b 100644 --- a/engines/sci/detection_tables.h +++ b/engines/sci/detection_tables.h @@ -3445,6 +3445,19 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Space Quest 4 1.000 - French DOS Floppy (supplied by misterhands in bug report #3515247) + {"sq4", "", { + + {"resource.map", 0, "1fd6f356f6a59ad2057686ce6573caeb", 6159}, + {"resource.000", 0, "8000a55aebc50a68b7cce07a8c33758c", 205287}, + {"resource.001", 0, "99a6df6d366b3f061271ff3450ac0d32", 1269850}, + {"resource.002", 0, "a6a8d7a24dbb7a266a26b084e7275e89", 1242668}, + {"resource.003", 0, "482a99c8103b4bcb5706e5969d1c1193", 1323083}, + {"resource.004", 0, "b2cca3afcf2e013b8ce86b64155af766", 1254353}, + {"resource.005", 0, "9e520577e035547c4b5149a6d12ef85b", 1098814}, + AD_LISTEND}, + Common::FR_FRA, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Space Quest 4 1.000 - English DOS Floppy (from abevi, bug report #2612718) {"sq4", "", { {"resource.map", 0, "8f08b97ca093f370c56d99715b015554", 6153}, diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp index cad95b1c18..8a932232f8 100644 --- a/engines/sci/engine/features.cpp +++ b/engines/sci/engine/features.cpp @@ -45,6 +45,7 @@ GameFeatures::GameFeatures(SegManager *segMan, Kernel *kernel) : _segMan(segMan) _usesCdTrack = Common::File::exists("cdaudio.map"); if (!ConfMan.getBool("use_cdaudio")) _usesCdTrack = false; + _forceDOSTracks = false; } reg_t GameFeatures::getDetectionAddr(const Common::String &objName, Selector slc, int methodNum) { @@ -642,7 +643,7 @@ MoveCountType GameFeatures::detectMoveCountType() { } bool GameFeatures::useAltWinGMSound() { - if (g_sci && g_sci->getPlatform() == Common::kPlatformWindows && g_sci->isCD()) { + if (g_sci && g_sci->getPlatform() == Common::kPlatformWindows && g_sci->isCD() && !_forceDOSTracks) { SciGameId id = g_sci->getGameId(); return (id == GID_ECOQUEST || id == GID_JONES || diff --git a/engines/sci/engine/features.h b/engines/sci/engine/features.h index 4592c5be9c..f6bb0b5759 100644 --- a/engines/sci/engine/features.h +++ b/engines/sci/engine/features.h @@ -117,6 +117,12 @@ public: */ bool useAltWinGMSound(); + /** + * Forces DOS soundtracks in Windows CD versions when the user hasn't + * selected a MIDI output device + */ + void forceDOSTracks() { _forceDOSTracks = true; } + private: reg_t getDetectionAddr(const Common::String &objName, Selector slc, int methodNum = -1); @@ -137,6 +143,7 @@ private: MoveCountType _moveCountType; bool _usesCdTrack; + bool _forceDOSTracks; SegManager *_segMan; Kernel *_kernel; diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp index 71f4598afc..14ffa69f91 100644 --- a/engines/sci/graphics/cursor.cpp +++ b/engines/sci/graphics/cursor.cpp @@ -148,11 +148,13 @@ void GfxCursor::kernelSetShape(GuiResourceId resourceId) { colorMapping[1] = _screen->getColorWhite(); // White is also hardcoded colorMapping[2] = SCI_CURSOR_SCI0_TRANSPARENCYCOLOR; colorMapping[3] = _palette->matchColor(170, 170, 170); // Grey - // Special case for the magnifier cursor in LB1 (bug #3487092). - // No other SCI0 game has a cursor resource of 1, so this is handled - // specifically for LB1. + // TODO: Figure out if the grey color is hardcoded + // HACK for the magnifier cursor in LB1, fixes its color (bug #3487092) if (g_sci->getGameId() == GID_LAURABOW && resourceId == 1) colorMapping[3] = _screen->getColorWhite(); + // HACK for Longbow cursors, fixes the shade of grey they're using (bug #3489101) + if (g_sci->getGameId() == GID_LONGBOW) + colorMapping[3] = _palette->matchColor(223, 223, 223); // Light Grey // Seek to actual data resourceData += 4; diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp index 09cab75c39..918b045cb9 100644 --- a/engines/sci/sound/music.cpp +++ b/engines/sci/sound/music.cpp @@ -88,6 +88,12 @@ void SciMusic::init() { uint32 dev = MidiDriver::detectDevice(deviceFlags); _musicType = MidiDriver::getMusicType(dev); + if (g_sci->_features->useAltWinGMSound() && _musicType != MT_GM) { + warning("A Windows CD version with an alternate MIDI soundtrack has been chosen, " + "but no MIDI music device has been selected. Reverting to the DOS soundtrack"); + g_sci->_features->forceDOSTracks(); + } + switch (_musicType) { case MT_ADLIB: // FIXME: There's no Amiga sound option, so we hook it up to AdLib diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp index b47982af00..2da0abb5df 100644 --- a/engines/scumm/detection.cpp +++ b/engines/scumm/detection.cpp @@ -589,11 +589,11 @@ static void detectGames(const Common::FSList &fslist, Common::List<DetectorResul file.c_str(), md5str.c_str(), filesize); // Sanity check: We *should* have found a matching gameid / variant at this point. - // If not, then there's a bug in our data tables... - assert(dr.game.gameid != 0); - - // Add it to the list of detected games - results.push_back(dr); + // If not, we may have #ifdef'ed the entry out in our detection_tables.h because we + // don't have the required stuff compiled in, or there's a bug in our data tables... + if (dr.game.gameid != 0) + // Add it to the list of detected games + results.push_back(dr); } } diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h index c25c875616..3957c7c42d 100644 --- a/engines/scumm/scumm-md5.h +++ b/engines/scumm/scumm-md5.h @@ -1,5 +1,5 @@ /* - This file was generated by the md5table tool on Mon Apr 9 12:23:48 2012 + This file was generated by the md5table tool on Tue Apr 24 03:50:58 2012 DO NOT EDIT MANUALLY! */ @@ -621,6 +621,7 @@ static const MD5Table md5table[] = { { "f40a7f495f59188ca57a9d1d50301bb6", "puttputt", "HE 60", "Demo", -1, Common::EN_ANY, Common::kPlatformMacintosh }, { "f5228b0cc1c19e6ea8268ba2eeb61f60", "freddi", "HE 73", "Demo", -1, Common::FR_FRA, Common::kPlatformWindows }, { "f73883f13b5a302749a5bad31d909780", "tentacle", "", "CD", -1, Common::DE_DEU, Common::kPlatformMacintosh }, + { "f7635a0e2ab82c9a0f9ace5f232a488f", "catalog", "HE 72", "Demo", -1, Common::EN_ANY, Common::kPlatformWindows }, { "f7711f9264d4d43c2a1518ec7c10a607", "pajama3", "", "", 79382, Common::EN_USA, Common::kPlatformUnknown }, { "f79e60c17cca601e411f1f75e8ee9b5a", "spyfox2", "", "", 51286, Common::UNK_LANG, Common::kPlatformUnknown }, { "f8be685007a8b425ba2a455da732f59f", "pajama2", "HE 99", "", -1, Common::FR_FRA, Common::kPlatformMacintosh }, diff --git a/engines/sword25/script/luabindhelper.cpp b/engines/sword25/script/luabindhelper.cpp index 8fbbe7e272..6900305f5c 100644 --- a/engines/sword25/script/luabindhelper.cpp +++ b/engines/sword25/script/luabindhelper.cpp @@ -412,7 +412,7 @@ Common::String LuaBindhelper::stackDump(lua_State *L) { oss += "------------------- Stack Dump -------------------\n"; while (i) { - oss += i + ": " + getLuaValueInfo(L, i) + "\n"; + oss += Common::String::format("%d: ", i) + getLuaValueInfo(L, i) + "\n"; i--; } diff --git a/engines/tinsel/detection_tables.h b/engines/tinsel/detection_tables.h index be44b1c462..b6b19f6ee7 100644 --- a/engines/tinsel/detection_tables.h +++ b/engines/tinsel/detection_tables.h @@ -397,7 +397,7 @@ static const TinselGameDescription gameDescriptions[] = { { // multilanguage PSX demo { "dw", - "CD demo", + "CD Demo", { {"french.txt", 0, "e7020d35f58d0d187052ac406d86cc87", 273914}, {"german.txt", 0, "52f0a01e0ff0d340b02a36fd5109d705", 263942}, diff --git a/engines/tinsel/music.cpp b/engines/tinsel/music.cpp index 781a378f13..fa5334a033 100644 --- a/engines/tinsel/music.cpp +++ b/engines/tinsel/music.cpp @@ -131,11 +131,6 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) { g_currentMidi = dwFileOffset; g_currentLoop = bLoop; - // Tinsel V1 PSX uses a different music format, so i - // disable it here. - // TODO: Maybe this should be moved to a better place... - if (TinselV1PSX) return false; - if (_vm->_config->_musicVolume != 0) { bool mute = false; if (ConfMan.hasKey("mute")) @@ -231,14 +226,14 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) { _vm->_midiMusic->send(0x7F07B0 | 13); } - _vm->_midiMusic->playXMIDI(g_midiBuffer.pDat, dwSeqLen, bLoop); + _vm->_midiMusic->playMIDI(dwSeqLen, bLoop); // Store the length //dwLastSeqLen = dwSeqLen; } else { // dwFileOffset == dwLastMidiIndex _vm->_midiMusic->stop(); - _vm->_midiMusic->playXMIDI(g_midiBuffer.pDat, dwSeqLen, bLoop); + _vm->_midiMusic->playMIDI(dwSeqLen, bLoop); } return true; @@ -314,8 +309,7 @@ void OpenMidiFiles() { Common::File midiStream; // Demo version has no midi file - // Also, Discworld PSX uses still unsupported psx SEQ format for music... - if ((_vm->getFeatures() & GF_DEMO) || (TinselVersion == TINSEL_V2) || TinselV1PSX) + if ((_vm->getFeatures() & GF_DEMO) || (TinselVersion == TINSEL_V2)) return; if (g_midiBuffer.pDat) @@ -412,7 +406,7 @@ void MidiMusicPlayer::send(uint32 b) { } } -void MidiMusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) { +void MidiMusicPlayer::playMIDI(uint32 size, bool loop) { Common::StackLock lock(_mutex); if (_isPlaying) @@ -420,6 +414,13 @@ void MidiMusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) { stop(); + if (TinselV1PSX) + playSEQ(size, loop); + else + playXMIDI(size, loop); +} + +void MidiMusicPlayer::playXMIDI(uint32 size, bool loop) { // It seems like not all music (the main menu music, for instance) set // all the instruments explicitly. That means the music will sound // different, depending on which music played before it. This appears @@ -433,7 +434,78 @@ void MidiMusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) { // Load XMID resource data MidiParser *parser = MidiParser::createParser_XMIDI(); - if (parser->loadMusic(midiData, size)) { + if (parser->loadMusic(g_midiBuffer.pDat, size)) { + parser->setTrack(0); + parser->setMidiDriver(this); + parser->setTimerRate(getBaseTempo()); + parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1); + parser->property(MidiParser::mpSendSustainOffOnNotesOff, 1); + + _parser = parser; + + _isLooping = loop; + _isPlaying = true; + } else { + delete parser; + } +} + +void MidiMusicPlayer::playSEQ(uint32 size, bool loop) { + // MIDI.DAT holds the file names in DW1 PSX + Common::String baseName((char *)g_midiBuffer.pDat, size); + Common::String seqName = baseName + ".SEQ"; + + // TODO: Load the instrument bank (<baseName>.VB and <baseName>.VH) + + Common::File seqFile; + if (!seqFile.open(seqName)) + error("Failed to open SEQ file '%s'", seqName.c_str()); + + if (seqFile.readUint32LE() != MKTAG('S', 'E', 'Q', 'p')) + error("Failed to find SEQp tag"); + + // Make sure we don't have a SEP file (with multiple SEQ's inside) + if (seqFile.readUint32BE() != 1) + error("Can only play SEQ files, not SEP"); + + uint16 ppqn = seqFile.readUint16BE(); + uint32 tempo = seqFile.readUint16BE() << 8; + tempo |= seqFile.readByte(); + /* uint16 beat = */ seqFile.readUint16BE(); + + // SEQ is directly based on SMF and we'll use that to our advantage here + // and convert to SMF and then use the SMF MidiParser. + + // Calculate the SMF size we'll need + uint32 dataSize = seqFile.size() - 15; + uint32 actualSize = dataSize + 7 + 22; + + // Resize the buffer if necessary + if (g_midiBuffer.size < actualSize) { + g_midiBuffer.pDat = (byte *)realloc(g_midiBuffer.pDat, actualSize); + assert(g_midiBuffer.pDat); + } + + // Now construct the header + WRITE_BE_UINT32(g_midiBuffer.pDat, MKTAG('M', 'T', 'h', 'd')); + WRITE_BE_UINT32(g_midiBuffer.pDat + 4, 6); // header size + WRITE_BE_UINT16(g_midiBuffer.pDat + 8, 0); // type 0 + WRITE_BE_UINT16(g_midiBuffer.pDat + 10, 1); // one track + WRITE_BE_UINT16(g_midiBuffer.pDat + 12, ppqn); + WRITE_BE_UINT32(g_midiBuffer.pDat + 14, MKTAG('M', 'T', 'r', 'k')); + WRITE_BE_UINT32(g_midiBuffer.pDat + 18, dataSize + 7); // SEQ data size + tempo change event size + + // Add in a fake tempo change event + WRITE_BE_UINT32(g_midiBuffer.pDat + 22, 0x00FF5103); // no delta, meta event, tempo change, param size = 3 + WRITE_BE_UINT16(g_midiBuffer.pDat + 26, tempo >> 8); + g_midiBuffer.pDat[28] = tempo & 0xFF; + + // Now copy in the rest of the events + seqFile.read(g_midiBuffer.pDat + 29, dataSize); + seqFile.close(); + + MidiParser *parser = MidiParser::createParser_SMF(); + if (parser->loadMusic(g_midiBuffer.pDat, actualSize)) { parser->setTrack(0); parser->setMidiDriver(this); parser->setTimerRate(getBaseTempo()); diff --git a/engines/tinsel/music.h b/engines/tinsel/music.h index d43fed268d..121bf3d79b 100644 --- a/engines/tinsel/music.h +++ b/engines/tinsel/music.h @@ -64,7 +64,7 @@ public: virtual void setVolume(int volume); - void playXMIDI(byte *midiData, uint32 size, bool loop); + void playMIDI(uint32 size, bool loop); // void stop(); void pause(); @@ -76,6 +76,10 @@ public: // The original sets the "sequence timing" to 109 Hz, whatever that // means. The default is 120. uint32 getBaseTempo() { return _driver ? (109 * _driver->getBaseTempo()) / 120 : 0; } + +private: + void playXMIDI(uint32 size, bool loop); + void playSEQ(uint32 size, bool loop); }; class PCMMusicPlayer : public Audio::AudioStream { diff --git a/engines/tinsel/tinlib.cpp b/engines/tinsel/tinlib.cpp index c652abca25..cd65a4ec32 100644 --- a/engines/tinsel/tinlib.cpp +++ b/engines/tinsel/tinlib.cpp @@ -1626,7 +1626,7 @@ static void Play(CORO_PARAM, SCNHANDLE hFilm, int x, int y, bool bComplete, int */ static void PlayMidi(CORO_PARAM, SCNHANDLE hMidi, int loop, bool complete) { // FIXME: This is a workaround for the FIXME below - if (GetMidiVolume() == 0 || TinselV1PSX) + if (GetMidiVolume() == 0) return; CORO_BEGIN_CONTEXT; diff --git a/graphics/decoders/bmp.h b/graphics/decoders/bmp.h index 6360aa81c9..8a37538ee1 100644 --- a/graphics/decoders/bmp.h +++ b/graphics/decoders/bmp.h @@ -51,7 +51,7 @@ public: void destroy(); virtual bool loadStream(Common::SeekableReadStream &stream); virtual const Surface *getSurface() const { return _surface; } - virtual const byte *getPalette() { return _palette; } + const byte *getPalette() const { return _palette; } private: Surface *_surface; diff --git a/graphics/decoders/pict.cpp b/graphics/decoders/pict.cpp index 957342084e..bdb733a87d 100644 --- a/graphics/decoders/pict.cpp +++ b/graphics/decoders/pict.cpp @@ -361,14 +361,14 @@ void PICTDecoder::unpackBitsRect(Common::SeekableReadStream &stream, bool hasPal memcpy(_outputSurface->pixels, buffer, _outputSurface->w * _outputSurface->h); break; case 2: - // Convert from 16-bit to whatever surface we need + // We have a 16-bit surface _outputSurface->create(width, height, PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)); for (uint16 y = 0; y < _outputSurface->h; y++) for (uint16 x = 0; x < _outputSurface->w; x++) WRITE_UINT16(_outputSurface->getBasePtr(x, y), READ_UINT16(buffer + (y * _outputSurface->w + x) * 2)); break; case 3: - // Convert from 24-bit (planar!) to whatever surface we need + // We have a planar 24-bit surface _outputSurface->create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)); for (uint16 y = 0; y < _outputSurface->h; y++) { for (uint16 x = 0; x < _outputSurface->w; x++) { @@ -380,15 +380,18 @@ void PICTDecoder::unpackBitsRect(Common::SeekableReadStream &stream, bool hasPal } break; case 4: - // Convert from 32-bit (planar!) to whatever surface we need + // We have a planar 32-bit surface + // Note that we ignore the alpha channel since it seems to not be correct + // Mac OS X does not ignore it, but then displays it incorrectly. Photoshop + // does ignore it and displays it correctly. _outputSurface->create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)); for (uint16 y = 0; y < _outputSurface->h; y++) { for (uint16 x = 0; x < _outputSurface->w; x++) { - byte r = *(buffer + y * _outputSurface->w * 4 + x); - byte g = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w + x); - byte b = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 2 + x); - byte a = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 3 + x); - *((uint32 *)_outputSurface->getBasePtr(x, y)) = _outputSurface->format.ARGBToColor(r, g, b, a); + byte a = 0xFF; + byte r = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w + x); + byte g = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 2 + x); + byte b = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 3 + x); + *((uint32 *)_outputSurface->getBasePtr(x, y)) = _outputSurface->format.ARGBToColor(a, r, g, b); } } break; @@ -541,10 +544,14 @@ void PICTDecoder::decodeCompressedQuickTime(Common::SeekableReadStream &stream) // Now we've reached the image descriptor, so read the relevant data from that uint32 idStart = stream.pos(); uint32 idSize = stream.readUint32BE(); - stream.skip(40); // miscellaneous stuff + uint32 codec = stream.readUint32BE(); + stream.skip(36); // miscellaneous stuff uint32 jpegSize = stream.readUint32BE(); stream.skip(idSize - (stream.pos() - idStart)); // more useless stuff + if (codec != MKTAG('j', 'p', 'e', 'g')) + error("Unhandled CompressedQuickTime format '%s'", tag2str(codec)); + Common::SeekableSubReadStream jpegStream(&stream, stream.pos(), stream.pos() + jpegSize); JPEGDecoder jpeg; diff --git a/graphics/fontman.cpp b/graphics/fontman.cpp index 8d967d595f..99dd3d664f 100644 --- a/graphics/fontman.cpp +++ b/graphics/fontman.cpp @@ -47,6 +47,13 @@ FontManager::FontManager() { } FontManager::~FontManager() { + for (uint i = 0; i < _ownedFonts.size(); ++i) { + const Font *font = _ownedFonts[i]; + if (font == g_sysfont || font == g_sysfont_big || font == g_consolefont) + continue; + delete font; + } + delete g_sysfont; g_sysfont = 0; delete g_sysfont_big; @@ -90,6 +97,8 @@ bool FontManager::assignFontToName(const Common::String &name, const Font *font) Common::String lowercaseName = name; lowercaseName.toLowercase(); _fontMap[lowercaseName] = font; + if (Common::find(_ownedFonts.begin(), _ownedFonts.end(), font) == _ownedFonts.end()) + _ownedFonts.push_back(font); return true; } @@ -116,8 +125,35 @@ bool FontManager::setFont(FontUsage usage, const BdfFont *font) { void FontManager::removeFontName(const Common::String &name) { Common::String lowercaseName = name; lowercaseName.toLowercase(); + if (!_fontMap.contains(lowercaseName)) + return; + + const Font *font = _fontMap[lowercaseName]; _fontMap.erase(lowercaseName); + // Check if we still have a copy of this font in the map. + bool stillHasFont = false; + for (Common::HashMap<Common::String, const Font *>::iterator i = _fontMap.begin(); i != _fontMap.end(); ++i) { + if (i->_value != font) + continue; + stillHasFont = true; + break; + } + + if (!stillHasFont) { + // We don't have a copy of the font, so remove it from our list and delete it. + stillHasFont = true; + for (uint i = 0; i < _ownedFonts.size(); ++i) { + if (_ownedFonts[i] != font) + continue; + stillHasFont = false; + _ownedFonts.remove_at(i); + break; + } + assert(!stillHasFont); + delete font; + } + // In case the current localized font is removed, we fall back to the // default font again. if (_localizedFontName == lowercaseName) diff --git a/graphics/fontman.h b/graphics/fontman.h index 42f7d856fa..b06ddea860 100644 --- a/graphics/fontman.h +++ b/graphics/fontman.h @@ -60,7 +60,9 @@ public: const Font *getFontByName(const Common::String &name) const; /** - * Associates a font object with an 'name' + * Associates a font object with an 'name'. + * The FontManager takes ownership of the provided font object + * and will delete it when necesssary. * * @param name the name of the font * @param font the font object @@ -111,6 +113,7 @@ private: ~FontManager(); Common::HashMap<Common::String, const Font *> _fontMap; + Common::Array<const Font *> _ownedFonts; Common::String _localizedFontName; }; diff --git a/graphics/fonts/bdf.h b/graphics/fonts/bdf.h index 5b615cc043..b0166a2095 100644 --- a/graphics/fonts/bdf.h +++ b/graphics/fonts/bdf.h @@ -77,7 +77,7 @@ private: #define DEFINE_FONT(n) \ const BdfFont *n = 0; \ void create_##n() { \ - n = new BdfFont(desc, DisposeAfterUse::YES); \ + n = new BdfFont(desc, DisposeAfterUse::NO); \ } #define FORWARD_DECLARE_FONT(n) \ diff --git a/graphics/fonts/ttf.cpp b/graphics/fonts/ttf.cpp index 7505f7913e..96241e923c 100644 --- a/graphics/fonts/ttf.cpp +++ b/graphics/fonts/ttf.cpp @@ -43,10 +43,6 @@ namespace Graphics { namespace { -inline int ftFloor26_6(FT_Pos x) { - return x / 64; -} - inline int ftCeil26_6(FT_Pos x) { return (x + 63) / 64; } @@ -70,6 +66,10 @@ private: bool _initialized; }; +void shutdownTTF() { + TTFLibrary::destroy(); +} + #define g_ttf ::Graphics::TTFLibrary::instance() TTFLibrary::TTFLibrary() : _library(), _initialized(false) { diff --git a/graphics/fonts/ttf.h b/graphics/fonts/ttf.h index 7222d6e112..ec7dbe04ef 100644 --- a/graphics/fonts/ttf.h +++ b/graphics/fonts/ttf.h @@ -34,6 +34,8 @@ namespace Graphics { class Font; Font *loadTTFFont(Common::SeekableReadStream &stream, int size, bool monochrome = false, const uint32 *mapping = 0); +void shutdownTTF(); + } // End of namespace Graphics #endif diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp index fdd7750af9..be0a5db601 100644 --- a/gui/ThemeEngine.cpp +++ b/gui/ThemeEngine.cpp @@ -175,6 +175,7 @@ static const DrawDataInfo kDrawDataDefaults[] = { {kDDButtonIdle, "button_idle", true, kDDWidgetBackgroundSlider}, {kDDButtonHover, "button_hover", false, kDDButtonIdle}, {kDDButtonDisabled, "button_disabled", true, kDDNone}, + {kDDButtonPressed, "button_pressed", false, kDDButtonIdle}, {kDDSliderFull, "slider_full", false, kDDNone}, {kDDSliderHover, "slider_hover", false, kDDNone}, @@ -877,6 +878,8 @@ void ThemeEngine::drawButton(const Common::Rect &r, const Common::String &str, W dd = kDDButtonHover; else if (state == kStateDisabled) dd = kDDButtonDisabled; + else if (state == kStatePressed) + dd = kDDButtonPressed; queueDD(dd, r, 0, hints & WIDGET_CLEARBG); queueDDText(getTextData(dd), getTextColor(dd), r, str, false, true, _widgets[dd]->_textAlignH, _widgets[dd]->_textAlignV); @@ -1125,6 +1128,7 @@ void ThemeEngine::drawText(const Common::Rect &r, const Common::String &str, Wid break; case kStateEnabled: + case kStatePressed: colorId = kTextColorNormal; break; } @@ -1145,6 +1149,7 @@ void ThemeEngine::drawText(const Common::Rect &r, const Common::String &str, Wid break; case kStateEnabled: + case kStatePressed: colorId = kTextColorAlternative; break; } diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h index 0495a85c88..acded085f5 100644 --- a/gui/ThemeEngine.h +++ b/gui/ThemeEngine.h @@ -35,7 +35,7 @@ #include "graphics/pixelformat.h" -#define SCUMMVM_THEME_VERSION_STR "SCUMMVM_STX0.8.11" +#define SCUMMVM_THEME_VERSION_STR "SCUMMVM_STX0.8.12" class OSystem; @@ -81,6 +81,7 @@ enum DrawData { kDDButtonIdle, kDDButtonHover, kDDButtonDisabled, + kDDButtonPressed, kDDSliderFull, kDDSliderHover, @@ -178,7 +179,8 @@ public: enum State { kStateDisabled, ///< Indicates that the widget is disabled, that does NOT include that it is invisible kStateEnabled, ///< Indicates that the widget is enabled - kStateHighlight ///< Indicates that the widget is highlighted by the user + kStateHighlight, ///< Indicates that the widget is highlighted by the user + kStatePressed ///< Indicates that the widget is pressed, currently works for buttons }; typedef State WidgetStateInfo; diff --git a/gui/dialog.cpp b/gui/dialog.cpp index 2201e83ca5..ffca15bbc8 100644 --- a/gui/dialog.cpp +++ b/gui/dialog.cpp @@ -42,7 +42,7 @@ namespace GUI { Dialog::Dialog(int x, int y, int w, int h) : GuiObject(x, y, w, h), - _mouseWidget(0), _focusedWidget(0), _dragWidget(0), _visible(false), + _mouseWidget(0), _focusedWidget(0), _dragWidget(0), _tickleWidget(0), _visible(false), _backgroundType(GUI::ThemeEngine::kDialogBackgroundDefault) { // Some dialogs like LauncherDialog use internally a fixed size, even though // their widgets rely on the layout to be initialized correctly by the theme. @@ -54,7 +54,7 @@ Dialog::Dialog(int x, int y, int w, int h) Dialog::Dialog(const Common::String &name) : GuiObject(name), - _mouseWidget(0), _focusedWidget(0), _dragWidget(0), _visible(false), + _mouseWidget(0), _focusedWidget(0), _dragWidget(0), _tickleWidget(0), _visible(false), _backgroundType(GUI::ThemeEngine::kDialogBackgroundDefault) { // It may happen that we have 3x scaler in launcher (960xY) and then 640x480 @@ -117,6 +117,12 @@ void Dialog::reflowLayout() { GuiObject::reflowLayout(); } +void Dialog::lostFocus() { + if (_tickleWidget) { + _tickleWidget->lostFocus(); + } +} + void Dialog::setFocusWidget(Widget *widget) { // The focus will change. Tell the old focused widget (if any) // that it lost the focus. @@ -308,6 +314,9 @@ void Dialog::handleTickle() { // Focused widget receives tickle notifications if (_focusedWidget && _focusedWidget->getFlags() & WIDGET_WANT_TICKLE) _focusedWidget->handleTickle(); + + if (_tickleWidget && _tickleWidget->getFlags() & WIDGET_WANT_TICKLE) + _tickleWidget->handleTickle(); } void Dialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { diff --git a/gui/dialog.h b/gui/dialog.h index f5a5f94a68..1773c6633e 100644 --- a/gui/dialog.h +++ b/gui/dialog.h @@ -52,6 +52,7 @@ protected: Widget *_mouseWidget; Widget *_focusedWidget; Widget *_dragWidget; + Widget *_tickleWidget; bool _visible; ThemeEngine::DialogBackground _backgroundType; @@ -71,7 +72,13 @@ public: void setFocusWidget(Widget *widget); Widget *getFocusWidget() { return _focusedWidget; } + void setTickleWidget(Widget *widget) { _tickleWidget = widget; } + void unSetTickleWidget() { _tickleWidget = NULL; } + Widget *getTickleWidget() { return _tickleWidget; } + virtual void reflowLayout(); + virtual void lostFocus(); + virtual void receivedFocus() {} protected: virtual void open(); diff --git a/gui/gui-manager.cpp b/gui/gui-manager.cpp index ffecd928bc..abd781e1a3 100644 --- a/gui/gui-manager.cpp +++ b/gui/gui-manager.cpp @@ -381,7 +381,7 @@ void GuiManager::runLoop() { if (tooltipCheck && _lastMousePosition.time + kTooltipDelay < _system->getMillis()) { Widget *wdg = activeDialog->findWidget(_lastMousePosition.x, _lastMousePosition.y); - if (wdg && wdg->getTooltip()) { + if (wdg && wdg->getTooltip() && !(wdg->getFlags() & WIDGET_PRESSED)) { Tooltip *tooltip = new Tooltip(); tooltip->setup(activeDialog, wdg, _lastMousePosition.x, _lastMousePosition.y); tooltip->runModal(); @@ -441,6 +441,11 @@ void GuiManager::restoreState() { } void GuiManager::openDialog(Dialog *dialog) { + dialog->receivedFocus(); + + if (!_dialogStack.empty()) + getTopDialog()->lostFocus(); + _dialogStack.push(dialog); if (_redrawStatus != kRedrawFull) _redrawStatus = kRedrawOpenDialog; @@ -458,7 +463,11 @@ void GuiManager::closeTopDialog() { return; // Remove the dialog from the stack - _dialogStack.pop(); + _dialogStack.pop()->lostFocus(); + + if (!_dialogStack.empty()) + getTopDialog()->receivedFocus(); + if (_redrawStatus != kRedrawFull) _redrawStatus = kRedrawCloseDialog; diff --git a/gui/launcher.cpp b/gui/launcher.cpp index a86a98f819..c8ed3126c4 100644 --- a/gui/launcher.cpp +++ b/gui/launcher.cpp @@ -663,7 +663,6 @@ LauncherDialog::LauncherDialog() _list->setEditable(false); _list->setNumberingMode(kListNumberingOff); - // Populate the list updateListing(); diff --git a/gui/predictivedialog.cpp b/gui/predictivedialog.cpp index 9cd18b81ba..b827d49416 100644 --- a/gui/predictivedialog.cpp +++ b/gui/predictivedialog.cpp @@ -69,31 +69,33 @@ enum { PredictiveDialog::PredictiveDialog() : Dialog("Predictive") { new StaticTextWidget(this, "Predictive.Headline", "Enter Text"); - new ButtonWidget(this, "Predictive.Cancel" , _("Cancel"), 0, kCancelCmd); - new ButtonWidget(this, "Predictive.OK" , _("Ok") , 0, kOkCmd); - new ButtonWidget(this, "Predictive.Button1", "1 `-.&" , 0, kBut1Cmd); - new ButtonWidget(this, "Predictive.Button2", "2 abc" , 0, kBut2Cmd); - new ButtonWidget(this, "Predictive.Button3", "3 def" , 0, kBut3Cmd); - new ButtonWidget(this, "Predictive.Button4", "4 ghi" , 0, kBut4Cmd); - new ButtonWidget(this, "Predictive.Button5", "5 jkl" , 0, kBut5Cmd); - new ButtonWidget(this, "Predictive.Button6", "6 mno" , 0, kBut6Cmd); - new ButtonWidget(this, "Predictive.Button7", "7 pqrs" , 0, kBut7Cmd); - new ButtonWidget(this, "Predictive.Button8", "8 tuv" , 0, kBut8Cmd); - new ButtonWidget(this, "Predictive.Button9", "9 wxyz" , 0, kBut9Cmd); - new ButtonWidget(this, "Predictive.Button0", "0" , 0, kBut0Cmd); - // I18N: You must leave "#" as is, only word 'next' is translatable - new ButtonWidget(this, "Predictive.Next" , _("# next"), 0, kNextCmd); - _addBtn = new ButtonWidget(this, "Predictive.Add", _("add"), 0, kAddCmd); - _addBtn->setEnabled(false); - -#ifndef DISABLE_FANCY_THEMES - _delbtn = new PicButtonWidget(this, "Predictive.Delete", _("Delete char"), kDelCmd); - ((PicButtonWidget *)_delbtn)->useThemeTransparency(true); - ((PicButtonWidget *)_delbtn)->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageDelbtn)); -#endif - _delbtn = new ButtonWidget(this, "Predictive.Delete" , _("<"), 0, kDelCmd); - // I18N: Pre means 'Predictive', leave '*' as is - _modebutton = new ButtonWidget(this, "Predictive.Pre", _("* Pre"), 0, kModeCmd); + _btns = (ButtonWidget **)calloc(1, sizeof(ButtonWidget *) * 16); + + _btns[kCancelAct] = new ButtonWidget(this, "Predictive.Cancel", _("Cancel") , 0, kCancelCmd); + _btns[kOkAct] = new ButtonWidget(this, "Predictive.OK", _("Ok") , 0, kOkCmd); + _btns[kBtn1Act] = new ButtonWidget(this, "Predictive.Button1", "1 `-.&" , 0, kBut1Cmd); + _btns[kBtn2Act] = new ButtonWidget(this, "Predictive.Button2", "2 abc" , 0, kBut2Cmd); + _btns[kBtn3Act] = new ButtonWidget(this, "Predictive.Button3", "3 def" , 0, kBut3Cmd); + _btns[kBtn4Act] = new ButtonWidget(this, "Predictive.Button4", "4 ghi" , 0, kBut4Cmd); + _btns[kBtn5Act] = new ButtonWidget(this, "Predictive.Button5", "5 jkl" , 0, kBut5Cmd); + _btns[kBtn6Act] = new ButtonWidget(this, "Predictive.Button6", "6 mno" , 0, kBut6Cmd); + _btns[kBtn7Act] = new ButtonWidget(this, "Predictive.Button7", "7 pqrs" , 0, kBut7Cmd); + _btns[kBtn8Act] = new ButtonWidget(this, "Predictive.Button8", "8 tuv" , 0, kBut8Cmd); + _btns[kBtn9Act] = new ButtonWidget(this, "Predictive.Button9", "9 wxyz" , 0, kBut9Cmd); + _btns[kBtn0Act] = new ButtonWidget(this, "Predictive.Button0", "0" , 0, kBut0Cmd); + // I18N: You must leave "#" as is, only word 'next' is translatable + _btns[kNextAct] = new ButtonWidget(this, "Predictive.Next", _("# next") , 0, kNextCmd); + _btns[kAddAct] = new ButtonWidget(this, "Predictive.Add", _("add") , 0, kAddCmd); + _btns[kAddAct]->setEnabled(false); + + #ifndef DISABLE_FANCY_THEMES + _btns[kDelAct] = new PicButtonWidget(this, "Predictive.Delete", _("Delete char"), kDelCmd); + ((PicButtonWidget *)_btns[kDelAct])->useThemeTransparency(true); + ((PicButtonWidget *)_btns[kDelAct])->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageDelbtn)); + #endif + _btns[kDelAct] = new ButtonWidget(this, "Predictive.Delete" , _("<") , 0, kDelCmd); + // I18N: Pre means 'Predictive', leave '*' as is + _btns[kModeAct] = new ButtonWidget(this, "Predictive.Pre", _("* Pre"), 0, kModeCmd); _edittext = new EditTextWidget(this, "Predictive.Word", _search, 0, 0, 0); _userDictHasChanged = false; @@ -164,6 +166,8 @@ PredictiveDialog::~PredictiveDialog() { free(_userDict.dictLine); free(_predictiveDict.dictLine); free(_unitedDict.dictLine); + + free(_btns); } void PredictiveDialog::saveUserDictToFile() { @@ -182,11 +186,23 @@ void PredictiveDialog::saveUserDictToFile() { } } +void PredictiveDialog::handleKeyUp(Common::KeyState state) { + if (_currBtn != kNoAct && !_needRefresh) { + _btns[_currBtn]->startAnimatePressedState(); + processBtnActive(_currBtn); + } +} + void PredictiveDialog::handleKeyDown(Common::KeyState state) { - ButtonId act = kNoAct; + _currBtn = kNoAct; + _needRefresh = false; if (getFocusWidget() == _edittext) { - setFocusWidget(_addBtn); + setFocusWidget(_btns[kAddAct]); + } + + if (_lastbutton == kNoAct) { + _lastbutton = kBtn5Act; } switch (state.keycode) { @@ -197,96 +213,126 @@ void PredictiveDialog::handleKeyDown(Common::KeyState state) { case Common::KEYCODE_LEFT: _navigationwithkeys = true; if (_lastbutton == kBtn1Act || _lastbutton == kBtn4Act || _lastbutton == kBtn7Act) - act = ButtonId(_lastbutton + 2); + _currBtn = ButtonId(_lastbutton + 2); + else if (_lastbutton == kDelAct) + _currBtn = kBtn1Act; + else if (_lastbutton == kModeAct) + _currBtn = kNextAct; else if (_lastbutton == kNextAct) - act = kBtn0Act; - else if (_lastbutton == kDelAct) - act = kDelAct; + _currBtn = kBtn0Act; + else if (_lastbutton == kAddAct) + _currBtn = kOkAct; else if (_lastbutton == kCancelAct) - act = kOkAct; - else if (_lastbutton == kModeAct) - act = kAddAct; + _currBtn = kAddAct; else - act = ButtonId(_lastbutton - 1); - _lastbutton = act; - //needRefresh = true; + _currBtn = ButtonId(_lastbutton - 1); + + + if (_mode != kModeAbc && _lastbutton == kCancelAct) + _currBtn = kOkAct; + + _needRefresh = true; break; case Common::KEYCODE_RIGHT: _navigationwithkeys = true; - if (_lastbutton == kBtn3Act || _lastbutton == kBtn6Act || _lastbutton == kBtn9Act) - act = ButtonId(_lastbutton - 2); + if (_lastbutton == kBtn3Act || _lastbutton == kBtn6Act || _lastbutton == kBtn9Act || _lastbutton == kOkAct) + _currBtn = ButtonId(_lastbutton - 2); + else if (_lastbutton == kDelAct) + _currBtn = kBtn3Act; + else if (_lastbutton == kBtn0Act) + _currBtn = kNextAct; + else if (_lastbutton == kNextAct) + _currBtn = kModeAct; else if (_lastbutton == kAddAct) - act = kModeAct; - else if (_lastbutton == kDelAct) - act = kDelAct; + _currBtn = kCancelAct; else if (_lastbutton == kOkAct) - act = kCancelAct; - else if (_lastbutton == kBtn0Act) - act = kNextAct; + _currBtn = kAddAct; else - act = ButtonId(_lastbutton + 1); - _lastbutton = act; - //needRefresh = true; + _currBtn = ButtonId(_lastbutton + 1); + + if (_mode != kModeAbc && _lastbutton == kOkAct) + _currBtn = kCancelAct; + _needRefresh = true; break; case Common::KEYCODE_UP: _navigationwithkeys = true; if (_lastbutton <= kBtn3Act) - act = kDelAct; - else if (_lastbutton == kNextAct || _lastbutton == kAddAct) - act = ButtonId(_lastbutton - 2); + _currBtn = kDelAct; else if (_lastbutton == kDelAct) - act = kOkAct; - else if (_lastbutton == kModeAct) - act = kBtn9Act; + _currBtn = kOkAct; + else if (_lastbutton == kModeAct) + _currBtn = kBtn7Act; else if (_lastbutton == kBtn0Act) - act = kBtn7Act; + _currBtn = kBtn8Act; + else if (_lastbutton == kNextAct) + _currBtn = kBtn9Act; + else if (_lastbutton == kAddAct) + _currBtn = kModeAct; + else if (_lastbutton == kCancelAct) + _currBtn = kBtn0Act; + else if (_lastbutton == kOkAct) + _currBtn = kNextAct; else - act = ButtonId(_lastbutton - 3); - _lastbutton = act; - //needRefresh = true; + _currBtn = ButtonId(_lastbutton - 3); + _needRefresh = true; break; case Common::KEYCODE_DOWN: _navigationwithkeys = true; - if (_lastbutton == kBtn7Act) - act = kBtn0Act; - else if (_lastbutton == kBtn8Act || _lastbutton == kBtn9Act) - act = ButtonId(_lastbutton + 2); - else if (_lastbutton == kDelAct) - act = kBtn1Act; - else if (_lastbutton == kCancelAct || _lastbutton == kOkAct) - act = kDelAct; - else if (_lastbutton == kModeAct || _lastbutton == kBtn0Act) - act = ButtonId(_lastbutton - 2); + if (_lastbutton == kDelAct) + _currBtn = kBtn3Act; + else if (_lastbutton == kBtn7Act) + _currBtn = kModeAct; + else if (_lastbutton == kBtn8Act) + _currBtn = kBtn0Act; + else if (_lastbutton == kBtn9Act) + _currBtn = kNextAct; + else if (_lastbutton == kModeAct) + _currBtn = kAddAct; + else if (_lastbutton == kBtn0Act) + _currBtn = kCancelAct; + else if (_lastbutton == kNextAct) + _currBtn = kOkAct; + else if (_lastbutton == kAddAct || _lastbutton == kCancelAct || _lastbutton == kOkAct) + _currBtn = kDelAct; else - act = ButtonId(_lastbutton + 3); - _lastbutton = act; - //needRefresh = true; + _currBtn = ButtonId(_lastbutton + 3); + + if (_mode != kModeAbc && _lastbutton == kModeAct) + _currBtn = kCancelAct; + + _needRefresh = true; break; case Common::KEYCODE_KP_ENTER: + case Common::KEYCODE_RETURN: + if (state.flags & Common::KBD_CTRL) { + _currBtn = kOkAct; + break; + } if (_navigationwithkeys) { // when the user has utilized arrow key navigation, - // interpret enter as 'click' on the act button - act = _lastbutton; + // interpret enter as 'click' on the _currBtn button + _currBtn = _lastbutton; + _needRefresh = false; } else { // else it is a shortcut for 'Ok' - act = kOkAct; + _currBtn = kOkAct; } break; case Common::KEYCODE_KP_PLUS: - act = kAddAct; + _currBtn = kAddAct; break; case Common::KEYCODE_BACKSPACE: case Common::KEYCODE_KP_MINUS: - act = kDelAct; + _currBtn = kDelAct; break; case Common::KEYCODE_KP_DIVIDE: - act = kNextAct; + _currBtn = kNextAct; break; case Common::KEYCODE_KP_MULTIPLY: - act = kModeAct; + _currBtn = kModeAct; break; case Common::KEYCODE_KP0: - act = kBtn0Act; + _currBtn = kBtn0Act; break; case Common::KEYCODE_KP1: case Common::KEYCODE_KP2: @@ -297,78 +343,93 @@ void PredictiveDialog::handleKeyDown(Common::KeyState state) { case Common::KEYCODE_KP7: case Common::KEYCODE_KP8: case Common::KEYCODE_KP9: - act = ButtonId(state.keycode - Common::KEYCODE_KP1); + _currBtn = ButtonId(state.keycode - Common::KEYCODE_KP1); break; default: Dialog::handleKeyDown(state); } - if (act != kNoAct) { - processBtnActive(act); + if (_lastbutton != _currBtn) + _btns[_lastbutton]->stopAnimatePressedState(); + + if (_currBtn != kNoAct && !_needRefresh) + _btns[_currBtn]->setPressedState(); + else + updateHighLightedButton(_currBtn); +} + +void PredictiveDialog::updateHighLightedButton(ButtonId act) { + if (_currBtn != kNoAct) { + _btns[_lastbutton]->setHighLighted(false); + _lastbutton = act; + _btns[_lastbutton]->setHighLighted(true); } } void PredictiveDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { - ButtonId act = kNoAct; + _currBtn = kNoAct; _navigationwithkeys = false; + if (_lastbutton != kNoAct) + _btns[_lastbutton]->setHighLighted(false); + switch (cmd) { case kDelCmd: - act = kDelAct; + _currBtn = kDelAct; break; case kNextCmd: - act = kNextAct; + _currBtn = kNextAct; break; case kAddCmd: - act = kAddAct; + _currBtn = kAddAct; break; case kModeCmd: - act = kModeAct; + _currBtn = kModeAct; break; case kBut1Cmd: - act = kBtn1Act; + _currBtn = kBtn1Act; break; case kBut2Cmd: - act = kBtn2Act; + _currBtn = kBtn2Act; break; case kBut3Cmd: - act = kBtn3Act; + _currBtn = kBtn3Act; break; case kBut4Cmd: - act = kBtn4Act; + _currBtn = kBtn4Act; break; case kBut5Cmd: - act = kBtn5Act; + _currBtn = kBtn5Act; break; case kBut6Cmd: - act = kBtn6Act; + _currBtn = kBtn6Act; break; case kBut7Cmd: - act = kBtn7Act; + _currBtn = kBtn7Act; break; case kBut8Cmd: - act = kBtn8Act; + _currBtn = kBtn8Act; break; case kBut9Cmd: - act = kBtn9Act; + _currBtn = kBtn9Act; break; case kBut0Cmd: - act = kBtn0Act; + _currBtn = kBtn0Act; break; case kCancelCmd: saveUserDictToFile(); close(); return; case kOkCmd: - act = kOkAct; + _currBtn = kOkAct; break; default: Dialog::handleCommand(sender, cmd, data); } - if (act != kNoAct) { - processBtnActive(act); + if (_currBtn != kNoAct) { + processBtnActive(_currBtn); } } @@ -500,18 +561,18 @@ void PredictiveDialog::processBtnActive(ButtonId button) { bringWordtoTop(_unitedDict.dictActLine, _wordNumber); } else if (button == kModeAct) { // Mode _mode++; - _addBtn->setEnabled(false); + _btns[kAddAct]->setEnabled(false); if (_mode > kModeAbc) { _mode = kModePre; // I18N: Pre means 'Predictive', leave '*' as is - _modebutton->setLabel("* Pre"); + _btns[kModeAct]->setLabel("* Pre"); } else if (_mode == kModeNum) { // I18N: 'Num' means Numbers - _modebutton->setLabel("* Num"); + _btns[kModeAct]->setLabel("* Num"); } else { // I18N: 'Abc' means Latin alphabet input - _modebutton->setLabel("* Abc"); - _addBtn->setEnabled(true); + _btns[kModeAct]->setLabel("* Abc"); + _btns[kAddAct]->setEnabled(true); } // truncate current input at mode change @@ -532,18 +593,23 @@ void PredictiveDialog::processBtnActive(ButtonId button) { if (button == kOkAct) close(); + + if (button == kCancelAct) { + saveUserDictToFile(); + close(); + } } void PredictiveDialog::handleTickle() { - // TODO/FIXME: This code does not seem to make any sense. It is only - // triggered when _lastTime is zero and sets _lastTime to zero again - // under some condition. This should really be a nop. Probably this - // code intends to check "_lastTime" instead of "!_lastTime". - if (!_lastTime) { + if (_lastTime) { if ((_curTime - _lastTime) > kRepeatDelay) { _lastTime = 0; } } + + if (getTickleWidget()) { + getTickleWidget()->handleTickle(); + } } void PredictiveDialog::mergeDicts() { @@ -664,7 +730,7 @@ bool PredictiveDialog::matchWord() { // The entries in the dictionary consist of a code, a space, and then // a space-separated list of words matching this code. - // To exactly match a code, we therefore match the code plus the trailing + // To ex_currBtnly match a code, we therefore match the code plus the trailing // space in the dictionary. Common::String code = _currentCode + " "; diff --git a/gui/predictivedialog.h b/gui/predictivedialog.h index 32de36d5f2..0e3d2967c0 100644 --- a/gui/predictivedialog.h +++ b/gui/predictivedialog.h @@ -68,6 +68,7 @@ public: ~PredictiveDialog(); virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); + virtual void handleKeyUp(Common::KeyState state); virtual void handleKeyDown(Common::KeyState state); virtual void handleTickle(); @@ -98,6 +99,8 @@ private: void saveUserDictToFile(); void mergeDicts(); + + void updateHighLightedButton(ButtonId active); private: Dict _unitedDict; Dict _predictiveDict; @@ -118,6 +121,7 @@ private: uint32 _curTime, _lastTime; ButtonId _lastPressBtn; + ButtonId _currBtn; char _temp[kMaxWordLen + 1]; int _repeatcount[kMaxWordLen]; @@ -128,11 +132,10 @@ private: Common::String _search; bool _navigationwithkeys; + bool _needRefresh; private: EditTextWidget *_edittext; - ButtonWidget *_modebutton; - ButtonWidget *_delbtn; - ButtonWidget *_addBtn; + ButtonWidget **_btns; }; } // namespace GUI diff --git a/gui/themes/default.inc b/gui/themes/default.inc index 542f776dbd..5ee9b9202a 100644 --- a/gui/themes/default.inc +++ b/gui/themes/default.inc @@ -460,6 +460,17 @@ "bevel='2' " "/> " "</drawdata> " +"<drawdata id='button_pressed' cache='false'> " +"<text font='text_button' " +"text_color='color_alternative_inverted' " +"vertical_align='center' " +"horizontal_align='center' " +"/> " +"<drawstep func='square' " +"fill='foreground' " +"fg_color='green' " +"/> " +"</drawdata> " "<drawdata id='button_idle' cache='false'> " "<text font='text_button' " "text_color='color_button' " diff --git a/gui/themes/scummclassic.zip b/gui/themes/scummclassic.zip Binary files differindex eb5ac2d9f7..ec1728b778 100644 --- a/gui/themes/scummclassic.zip +++ b/gui/themes/scummclassic.zip diff --git a/gui/themes/scummclassic/THEMERC b/gui/themes/scummclassic/THEMERC index b808aee32f..7cbed97e40 100644 --- a/gui/themes/scummclassic/THEMERC +++ b/gui/themes/scummclassic/THEMERC @@ -1 +1 @@ -[SCUMMVM_STX0.8.11:ScummVM Classic Theme:No Author] +[SCUMMVM_STX0.8.12:ScummVM Classic Theme:No Author] diff --git a/gui/themes/scummclassic/classic_gfx.stx b/gui/themes/scummclassic/classic_gfx.stx index f07499ce79..49ecea2e11 100644 --- a/gui/themes/scummclassic/classic_gfx.stx +++ b/gui/themes/scummclassic/classic_gfx.stx @@ -541,6 +541,19 @@ /> </drawdata> + <!-- Pressed button --> + <drawdata id = 'button_pressed' cache = 'false'> + <text font = 'text_button' + text_color = 'color_alternative_inverted' + vertical_align = 'center' + horizontal_align = 'center' + /> + <drawstep func = 'square' + fill = 'foreground' + fg_color = 'green' + /> + </drawdata> + <drawdata id = 'button_idle' cache = 'false'> <text font = 'text_button' text_color = 'color_button' diff --git a/gui/themes/scummmodern.zip b/gui/themes/scummmodern.zip Binary files differindex 94f7cd8f6e..deae315e30 100644 --- a/gui/themes/scummmodern.zip +++ b/gui/themes/scummmodern.zip diff --git a/gui/themes/scummmodern/THEMERC b/gui/themes/scummmodern/THEMERC index e6c441d543..326993e98d 100644 --- a/gui/themes/scummmodern/THEMERC +++ b/gui/themes/scummmodern/THEMERC @@ -1 +1 @@ -[SCUMMVM_STX0.8.11:ScummVM Modern Theme:No Author] +[SCUMMVM_STX0.8.12:ScummVM Modern Theme:No Author] diff --git a/gui/themes/scummmodern/scummmodern_gfx.stx b/gui/themes/scummmodern/scummmodern_gfx.stx index 403f636fd0..970d78a5ae 100644 --- a/gui/themes/scummmodern/scummmodern_gfx.stx +++ b/gui/themes/scummmodern/scummmodern_gfx.stx @@ -740,6 +740,27 @@ /> </drawdata> + <!-- Pressed button --> + <drawdata id = 'button_pressed' cache = 'false'> + <text font = 'text_button' + text_color = 'color_button' + vertical_align = 'center' + horizontal_align = 'center' + /> + <drawstep func = 'roundedsq' + radius = '5' + stroke = '1' + fill = 'foreground' + shadow = '0' + factor = '0' + fg_color = '120, 40, 16' + gradient_start = '255, 0, 0' + gradient_end = '255, 0, 0' + bevel = '1' + bevel_color = 'black' + /> + </drawdata> + <!-- Idle button --> <drawdata id = 'button_idle' cache = 'false'> <text font = 'text_button' diff --git a/gui/widget.cpp b/gui/widget.cpp index 0e2fd248b1..6ae4e5cee5 100644 --- a/gui/widget.cpp +++ b/gui/widget.cpp @@ -30,6 +30,8 @@ #include "gui/ThemeEval.h" +#include "gui/dialog.h" + namespace GUI { Widget::Widget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip) @@ -77,6 +79,8 @@ void Widget::updateState(int oldFlags, int newFlags) { _state = ThemeEngine::kStateEnabled; if (newFlags & WIDGET_HILITED) _state = ThemeEngine::kStateHighlight; + if (newFlags & WIDGET_PRESSED) + _state = ThemeEngine::kStatePressed; } else { _state = ThemeEngine::kStateDisabled; } @@ -272,27 +276,33 @@ void StaticTextWidget::drawWidget() { ButtonWidget::ButtonWidget(GuiObject *boss, int x, int y, int w, int h, const Common::String &label, const char *tooltip, uint32 cmd, uint8 hotkey) : StaticTextWidget(boss, x, y, w, h, cleanupHotkey(label), Graphics::kTextAlignCenter, tooltip), CommandSender(boss), - _cmd(cmd), _hotkey(hotkey) { + _cmd(cmd), _hotkey(hotkey), _lastTime(0) { if (hotkey == 0) _hotkey = parseHotkey(label); - setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG); + setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG | WIDGET_WANT_TICKLE); _type = kButtonWidget; } ButtonWidget::ButtonWidget(GuiObject *boss, const Common::String &name, const Common::String &label, const char *tooltip, uint32 cmd, uint8 hotkey) : StaticTextWidget(boss, name, cleanupHotkey(label), tooltip), CommandSender(boss), - _cmd(cmd) { + _cmd(cmd), _lastTime(0) { if (hotkey == 0) _hotkey = parseHotkey(label); - setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG); + setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG | WIDGET_WANT_TICKLE); _type = kButtonWidget; } void ButtonWidget::handleMouseUp(int x, int y, int button, int clickCount) { - if (isEnabled() && x >= 0 && x < _w && y >= 0 && y < _h) + if (isEnabled() && x >= 0 && x < _w && y >= 0 && y < _h) { sendCommand(_cmd, 0); + startAnimatePressedState(); + } +} + +void ButtonWidget::handleMouseDown(int x, int y, int button, int clickCount) { + setPressedState(); } void ButtonWidget::drawWidget() { @@ -324,6 +334,44 @@ ButtonWidget *addClearButton(GuiObject *boss, const Common::String &name, uint32 return button; } +void ButtonWidget::setHighLighted(bool enable) { + (enable) ? setFlags(WIDGET_HILITED) : clearFlags(WIDGET_HILITED); + draw(); +} + +void ButtonWidget::handleTickle() { + if (_lastTime) { + uint32 curTime = g_system->getMillis(); + if (curTime - _lastTime > kPressedButtonTime) { + stopAnimatePressedState(); + } + } +} + +void ButtonWidget::setPressedState() { + wantTickle(true); + setFlags(WIDGET_PRESSED); + draw(); +} + +void ButtonWidget::stopAnimatePressedState() { + wantTickle(false); + _lastTime = 0; + clearFlags(WIDGET_PRESSED); + draw(); +} + +void ButtonWidget::startAnimatePressedState() { + _lastTime = g_system->getMillis(); +} + +void ButtonWidget::wantTickle(bool tickled) { + if (tickled) + ((GUI::Dialog *)_boss)->setTickleWidget(this); + else + ((GUI::Dialog *)_boss)->unSetTickleWidget(); +} + #pragma mark - PicButtonWidget::PicButtonWidget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip, uint32 cmd, uint8 hotkey) diff --git a/gui/widget.h b/gui/widget.h index 789fc09231..6a6c67ced9 100644 --- a/gui/widget.h +++ b/gui/widget.h @@ -38,6 +38,7 @@ enum { WIDGET_INVISIBLE = 1 << 1, WIDGET_HILITED = 1 << 2, WIDGET_BORDER = 1 << 3, + WIDGET_PRESSED = 1 << 4, //WIDGET_INV_BORDER = 1 << 4, WIDGET_CLEARBG = 1 << 5, WIDGET_WANT_TICKLE = 1 << 7, @@ -73,6 +74,10 @@ enum { kCaretBlinkTime = 300 }; +enum { + kPressedButtonTime = 200 +}; + /* Widget */ class Widget : public GuiObject { friend class Dialog; @@ -189,11 +194,22 @@ public: void setLabel(const Common::String &label); void handleMouseUp(int x, int y, int button, int clickCount); + void handleMouseDown(int x, int y, int button, int clickCount); void handleMouseEntered(int button) { setFlags(WIDGET_HILITED); draw(); } - void handleMouseLeft(int button) { clearFlags(WIDGET_HILITED); draw(); } + void handleMouseLeft(int button) { clearFlags(WIDGET_HILITED | WIDGET_PRESSED); draw(); } + void handleTickle(); + + void setHighLighted(bool enable); + void setPressedState(); + void startAnimatePressedState(); + void stopAnimatePressedState(); + void lostFocusWidget() { stopAnimatePressedState(); } protected: void drawWidget(); + void wantTickle(bool tickled); +private: + uint32 _lastTime; }; /* PicButtonWidget */ |
