diff options
Diffstat (limited to 'engines/sci/graphics/ports.cpp')
-rw-r--r-- | engines/sci/graphics/ports.cpp | 181 |
1 files changed, 138 insertions, 43 deletions
diff --git a/engines/sci/graphics/ports.cpp b/engines/sci/graphics/ports.cpp index cdb6fe4ae1..2e9128cda6 100644 --- a/engines/sci/graphics/ports.cpp +++ b/engines/sci/graphics/ports.cpp @@ -27,7 +27,9 @@ #include "sci/sci.h" #include "sci/engine/features.h" +#include "sci/engine/kernel.h" #include "sci/engine/state.h" +#include "sci/engine/selector.h" #include "sci/graphics/screen.h" #include "sci/graphics/paint16.h" #include "sci/graphics/animate.h" @@ -50,15 +52,17 @@ GfxPorts::GfxPorts(SegManager *segMan, GfxScreen *screen) } GfxPorts::~GfxPorts() { - // TODO: Clear _windowList and delete all stuff in it? + // reset frees all windows but _picWind + reset(); + freeWindow(_picWind); + delete _wmgrPort; delete _menuPort; } -void GfxPorts::init(bool usesOldGfxFunctions, SciGui *gui, GfxPaint16 *paint16, GfxText16 *text16) { +void GfxPorts::init(bool usesOldGfxFunctions, GfxPaint16 *paint16, GfxText16 *text16) { int16 offTop = 10; _usesOldGfxFunctions = usesOldGfxFunctions; - _gui = gui; _paint16 = paint16; _text16 = text16; @@ -85,37 +89,90 @@ void GfxPorts::init(bool usesOldGfxFunctions, SciGui *gui, GfxPaint16 *paint16, else _styleUser = SCI_WINDOWMGR_STYLE_USER | SCI_WINDOWMGR_STYLE_TRANSPARENT; - // Jones, Slater and Hoyle 3 were called with parameter -Nw 0 0 200 320. - // Mother Goose (SCI1) uses -Nw 0 0 159 262. The game will later use SetPort so we don't need to set the other fields. + // Jones, Slater, Hoyle 3&4 and Crazy Nicks Laura Bow/Kings Quest were + // called with parameter -Nw 0 0 200 320. + // Mother Goose (SCI1) uses -Nw 0 0 159 262. The game will later use + // SetPort so we don't need to set the other fields. // This actually meant not skipping the first 10 pixellines in windowMgrPort - Common::String gameId = g_sci->getGameID(); - if (gameId == "jones" || gameId == "slater" || gameId == "hoyle3" || (gameId == "mothergoose" && getSciVersion() == SCI_VERSION_1_EARLY)) + switch (g_sci->getGameId()) { + case GID_JONES: + case GID_SLATER: + case GID_HOYLE3: + case GID_HOYLE4: + case GID_CNICK_LAURABOW: + case GID_CNICK_KQ: offTop = 0; + break; + case GID_MOTHERGOOSE: + // TODO: if mother goose EGA also uses offTop we can simply remove this check altogether + switch (getSciVersion()) { + case SCI_VERSION_1_EARLY: + case SCI_VERSION_1_1: + offTop = 0; + break; + default: + break; + } + break; + case GID_FAIRYTALES: + // Mixed-Up Fairy Tales (& its demo) uses -w 26 0 200 320. If we don't + // also do this we will get not-fully-removed windows everywhere. + offTop = 26; + break; + default: + offTop = 10; + break; + } openPort(_wmgrPort); setPort(_wmgrPort); // SCI0 games till kq4 (.502 - not including) did not adjust against _wmgrPort in kNewWindow // We leave _wmgrPort top at 0, so the adjustment wont get done - if (!g_sci->_features->usesOldGfxFunctions()) + if (!g_sci->_features->usesOldGfxFunctions()) { setOrigin(0, offTop); - _wmgrPort->rect.bottom = _screen->getHeight() - offTop; + _wmgrPort->rect.bottom = _screen->getHeight() - offTop; + } else { + _wmgrPort->rect.bottom = _screen->getHeight(); + } _wmgrPort->rect.right = _screen->getWidth(); _wmgrPort->rect.moveTo(0, 0); _wmgrPort->curTop = 0; _wmgrPort->curLeft = 0; _windowList.push_front(_wmgrPort); - _picWind = newWindow(Common::Rect(0, offTop, _screen->getWidth(), _screen->getHeight()), 0, 0, SCI_WINDOWMGR_STYLE_TRANSPARENT | SCI_WINDOWMGR_STYLE_NOFRAME, 0, true); + _picWind = addWindow(Common::Rect(0, offTop, _screen->getWidth(), _screen->getHeight()), 0, 0, SCI_WINDOWMGR_STYLE_TRANSPARENT | SCI_WINDOWMGR_STYLE_NOFRAME, 0, true); // For SCI0 games till kq4 (.502 - not including) we set _picWind top to offTop instead // Because of the menu/status bar if (g_sci->_features->usesOldGfxFunctions()) _picWind->top = offTop; - priorityBandsMemoryActive = false; - kernelInitPriorityBands(); } +// Removes any windows from windowList +// is used when restoring/restarting the game +// Sierra SCI actually saved the whole windowList, it seems we don't need to do this at all +// but in some games there are still windows active when restoring. Leaving those windows open +// would create all sorts of issues, that's why we remove them +void GfxPorts::reset() { + PortList::iterator it = _windowList.begin(); + const PortList::iterator end = _windowList.end(); + + setPort(_picWind); + + while (it != end) { + Port *pPort = *it; + if (pPort->id > 2) { + // found a window beyond _picWind + freeWindow((Window *)pPort); + } + it++; + } + _windowList.clear(); + _windowList.push_front(_wmgrPort); + _windowList.push_back(_picWind); +} + void GfxPorts::kernelSetActive(uint16 portId) { switch (portId) { case 0: @@ -124,8 +181,13 @@ void GfxPorts::kernelSetActive(uint16 portId) { case 0xFFFF: setPort(_menuPort); break; - default: - setPort(getPortById(portId)); + default: { + Port *newPort = getPortById(portId); + if (newPort) + setPort(newPort); + else + error("GfxPorts::kernelSetActive was requested to set invalid port id %d", portId); + } }; } @@ -150,10 +212,10 @@ reg_t GfxPorts::kernelGetActive() { reg_t GfxPorts::kernelNewWindow(Common::Rect dims, Common::Rect restoreRect, uint16 style, int16 priority, int16 colorPen, int16 colorBack, const char *title) { Window *wnd = NULL; - if (restoreRect.top != 0 && restoreRect.left != 0 && restoreRect.height() != 0 && restoreRect.width() != 0) - wnd = newWindow(dims, &restoreRect, title, style, priority, false); + if (restoreRect.bottom != 0 && restoreRect.right != 0) + wnd = addWindow(dims, &restoreRect, title, style, priority, false); else - wnd = newWindow(dims, NULL, title, style, priority, false); + wnd = addWindow(dims, NULL, title, style, priority, false); wnd->penClr = colorPen; wnd->backClr = colorBack; drawWindow(wnd); @@ -163,7 +225,36 @@ reg_t GfxPorts::kernelNewWindow(Common::Rect dims, Common::Rect restoreRect, uin void GfxPorts::kernelDisposeWindow(uint16 windowId, bool reanimate) { Window *wnd = (Window *)getPortById(windowId); - disposeWindow(wnd, reanimate); + if (wnd) + removeWindow(wnd, reanimate); + else + error("GfxPorts::kernelDisposeWindow: Request to dispose invalid port id %d", windowId); + + if ((g_sci->getGameId() == GID_HOYLE4) && (!g_sci->isDemo())) { + // WORKAROUND: hoyle 4 has a broken User::handleEvent implementation + // first of all iconbar is always set and always gets called with + // events checking if event got claimed got removed inside that code + // and it will call handleEvent on gameObj afterwards. Iconbar windows + // are handled inside iconbar as well including disposing + // e.g. iconOK::doit, script 14) and claimed isn't even set. gameObj + // handleEvent calling will result in coordinate adjust with a now + // invalid port. + // We fix this by adjusting the port variable to be global + // again when hoyle4 is disposing windows. + // This worked because sierra sci leaves old port data, so the pointer + // was still valid for a short period of time + // TODO: maybe this could get implemented as script patch somehow + // although this could get quite tricky to implement (script 996) + // IconBar::handleEvent (script 937) + // maybe inside export 8 of script 0, which is called by iconOK + // and iconReplay + // or inside GameControls::hide (script 978) which is called to + // actually remove the window + reg_t eventObject = _segMan->findObjectByName("uEvt"); + if (!eventObject.isNull()) { + writeSelectorValue(_segMan, eventObject, SELECTOR(port), 0); + } + } } int16 GfxPorts::isFrontWindow(Window *pWnd) { @@ -200,7 +291,7 @@ void GfxPorts::endUpdate(Window *wnd) { setPort(oldPort); } -Window *GfxPorts::newWindow(const Common::Rect &dims, const Common::Rect *restoreRect, const char *title, uint16 style, int16 priority, bool draw) { +Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restoreRect, const char *title, uint16 style, int16 priority, bool draw) { // Find an unused window/port id uint id = 1; while (id < _windowsById.size() && _windowsById[id]) { @@ -214,7 +305,7 @@ Window *GfxPorts::newWindow(const Common::Rect &dims, const Common::Rect *restor Common::Rect r; if (!pwnd) { - warning("Can't open window!"); + error("Can't open window!"); return 0; } @@ -227,8 +318,10 @@ Window *GfxPorts::newWindow(const Common::Rect &dims, const Common::Rect *restor r = dims; if (r.width() > _screen->getWidth()) { - // We get invalid dimensions at least at the end of sq3 (script bug!) - warning("fixing too large window, given left&right was %d, %d", dims.left, dims.right); + // We get invalid dimensions at least at the end of sq3 (script bug!). + // Same happens very often in lsl5, sierra sci didnt fix it but it looked awful. + // Also happens frequently in the demo of GK1. + warning("Fixing too large window, left: %d, right: %d", dims.left, dims.right); r.left = 0; r.right = _screen->getWidth() - 1; if ((style != _styleUser) && !(style & SCI_WINDOWMGR_STYLE_NOFRAME)) @@ -343,12 +436,12 @@ void GfxPorts::drawWindow(Window *pWnd) { if (!(wndStyle & SCI_WINDOWMGR_STYLE_TRANSPARENT)) _paint16->fillRect(r, GFX_SCREEN_MASK_VISUAL, pWnd->backClr); - _paint16->bitsShow(pWnd->restoreRect); + _paint16->bitsShow(pWnd->dims); } setPort(oldport); } -void GfxPorts::disposeWindow(Window *pWnd, bool reanimate) { +void GfxPorts::removeWindow(Window *pWnd, bool reanimate) { setPort(_wmgrPort); _paint16->bitsRestore(pWnd->hSaved1); _paint16->bitsRestore(pWnd->hSaved2); @@ -358,6 +451,15 @@ void GfxPorts::disposeWindow(Window *pWnd, bool reanimate) { _paint16->kernelGraphRedrawBox(pWnd->restoreRect); _windowList.remove(pWnd); setPort(_windowList.back()); + _windowsById[pWnd->id] = NULL; + delete pWnd; +} + +void GfxPorts::freeWindow(Window *pWnd) { + if (!pWnd->hSaved1.isNull()) + _segMan->freeHunkEntry(pWnd->hSaved1); + if (!pWnd->hSaved2.isNull()) + _segMan->freeHunkEntry(pWnd->hSaved1); _windowsById[pWnd->id] = 0; delete pWnd; } @@ -378,13 +480,9 @@ void GfxPorts::updateWindow(Window *wnd) { } Port *GfxPorts::getPortById(uint16 id) { - if (id > _windowsById.size()) - error("getPortById() received invalid id"); - return _windowsById[id]; + return (id < _windowsById.size()) ? _windowsById[id] : NULL; } - - Port *GfxPorts::setPort(Port *newPort) { Port *oldPort = _curPort; _curPort = newPort; @@ -496,6 +594,11 @@ void GfxPorts::priorityBandsInit(int16 bandCount, int16 top, int16 bottom) { // We fill space that is left over with the highest band (hardcoded 200 limit, because this algo isnt meant to be used on hires) for (y = _priorityBottom; y < 200; y++) _priorityBands[y] = _priorityBandCount; + + // adjust, if bottom is 200 (one over the actual screen range) - we could otherwise go possible out of bounds + // sierra sci also adjust accordingly + if (_priorityBottom == 200) + _priorityBottom--; } void GfxPorts::priorityBandsInit(byte *data) { @@ -511,22 +614,14 @@ void GfxPorts::priorityBandsInit(byte *data) { _priorityBands[i++] = inx; } -// Gets used by picture class to remember priority bands data from sci1.1 pictures that need to get applied when -// transitioning to that picture -void GfxPorts::priorityBandsRemember(byte *data) { - int bandNo; - for (bandNo = 0; bandNo < 14; bandNo++) { - priorityBandsMemory[bandNo] = READ_LE_UINT16(data); +// Gets used to read priority bands data from sci1.1 pictures +void GfxPorts::priorityBandsInitSci11(byte *data) { + byte priorityBands[14]; + for (int bandNo = 0; bandNo < 14; bandNo++) { + priorityBands[bandNo] = READ_LE_UINT16(data); data += 2; } - priorityBandsMemoryActive = true; -} - -void GfxPorts::priorityBandsRecall() { - if (priorityBandsMemoryActive) { - priorityBandsInit((byte *)&priorityBandsMemory); - priorityBandsMemoryActive = false; - } + priorityBandsInit(priorityBands); } void GfxPorts::kernelInitPriorityBands() { |