diff options
Diffstat (limited to 'engines/sci/graphics/ports.cpp')
-rw-r--r-- | engines/sci/graphics/ports.cpp | 530 |
1 files changed, 530 insertions, 0 deletions
diff --git a/engines/sci/graphics/ports.cpp b/engines/sci/graphics/ports.cpp new file mode 100644 index 0000000000..2b4a9c9afa --- /dev/null +++ b/engines/sci/graphics/ports.cpp @@ -0,0 +1,530 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/util.h" + +#include "sci/sci.h" +#include "sci/engine/state.h" +#include "sci/graphics/screen.h" +#include "sci/graphics/paint16.h" +#include "sci/graphics/animate.h" +#include "sci/graphics/text.h" +#include "sci/graphics/ports.h" + +namespace Sci { + +// window styles +enum { + SCI_WINDOWMGR_STYLE_TRANSPARENT = (1 << 0), + SCI_WINDOWMGR_STYLE_NOFRAME = (1 << 1), + SCI_WINDOWMGR_STYLE_TITLE = (1 << 2), + SCI_WINDOWMGR_STYLE_TOPMOST = (1 << 3), + SCI_WINDOWMGR_STYLE_USER = (1 << 7) +}; + +GfxPorts::GfxPorts(SegManager *segMan, Screen *screen) + : _segMan(segMan), _screen(screen) { +} + +GfxPorts::~GfxPorts() { + // TODO: Clear _windowList and delete all stuff in it? + delete _mainPort; + delete _menuPort; +} + +void GfxPorts::init(SciGui *gui, GfxPaint16 *paint16, Text *text, Common::String gameId) { + int16 offTop = 10; + + _gui = gui; + _paint16 = paint16; + _text = text; + + // _mainPort is not known to windowmanager, that's okay according to sierra sci + // its not even used currently in our engine + _mainPort = new Port(0); + setPort(_mainPort); + openPort(_mainPort); + + // _menuPort has actually hardcoded id 0xFFFF. Its not meant to be known to windowmanager according to sierra sci + _menuPort = new Port(0xFFFF); + openPort(_menuPort); + _text->SetFont(0); + _menuPort->rect = Common::Rect(0, 0, _screen->getWidth(), _screen->getHeight()); + _menuBarRect = Common::Rect(0, 0, _screen->getWidth(), 9); + _menuRect = Common::Rect(0, 0, _screen->getWidth(), 10); + _menuLine = Common::Rect(0, 9, _screen->getWidth(), 10); + + _wmgrPort = new Port(1); + _windowsById.resize(2); + _windowsById[0] = _wmgrPort; // wmgrPort is supposed to be accessible via id 0 + _windowsById[1] = _wmgrPort; // but wmgrPort may not actually have id 0, so we assign id 1 (as well) + // Background: sierra sci replies with the offset of curPort on kGetPort calls. If we reply with 0 there most games + // will work, but some scripts seem to check for 0 and initialize the variable again in that case + // resulting in problems. + + // Jones, Slater and Hoyle 3 were called with parameter -Nw 0 0 200 320. + // This actually meant not skipping the first 10 pixellines in windowMgrPort + if (gameId == "jones" || gameId == "slater" || gameId == "hoyle3") + offTop = 0; + + openPort(_wmgrPort); + setPort(_wmgrPort); + setOrigin(0, offTop); + _wmgrPort->rect.bottom = _screen->getHeight() - offTop; + _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); + + priorityBandsMemoryActive = false; +} + +void GfxPorts::kernelSetActive(uint16 portId) { + switch (portId) { + case 0: + setPort(_wmgrPort); + break; + case 0xFFFF: + setPort(_menuPort); + break; + default: + setPort(getPortById(portId)); + }; +} + +Common::Rect GfxPorts::kernelGetPicWindow(int16 &picTop, int16 &picLeft) { + picTop = _picWind->top; + picLeft = _picWind->left; + return _picWind->rect; +} + +void GfxPorts::kernelSetPicWindow(Common::Rect rect, int16 picTop, int16 picLeft, bool initPriorityBandsFlag) { + _picWind->rect = rect; + _picWind->top = picTop; + _picWind->left = picLeft; + // TODO: fixme before joining + //if (initPriorityBandsFlag) + // initPriorityBands(); +} + +reg_t GfxPorts::kernelGetActive() { + return make_reg(0, getPort()->id); +} + +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); + else + wnd = newWindow(dims, NULL, title, style, priority, false); + wnd->penClr = colorPen; + wnd->backClr = colorBack; + drawWindow(wnd); + + return make_reg(0, wnd->id); +} + +void GfxPorts::kernelDisposeWindow(uint16 windowId, bool reanimate) { + Window *wnd = (Window *)getPortById(windowId); + disposeWindow(wnd, reanimate); +} + +int16 GfxPorts::isFrontWindow(Window *pWnd) { + return _windowList.back() == pWnd; +} + +void GfxPorts::beginUpdate(Window *wnd) { + Port *oldPort = setPort(_wmgrPort); + PortList::iterator it = _windowList.reverse_begin(); + const PortList::iterator end = Common::find(_windowList.begin(), _windowList.end(), wnd); + while (it != end) { + // FIXME: We also store Port objects in the window list. + // We should add a check that we really only pass windows here... + updateWindow((Window *)*it); + --it; + } + setPort(oldPort); +} + +void GfxPorts::endUpdate(Window *wnd) { + Port *oldPort = setPort(_wmgrPort); + const PortList::iterator end = _windowList.end(); + PortList::iterator it = Common::find(_windowList.begin(), end, wnd); + + // wnd has to be in _windowList + assert(it != end); + + while (++it != end) { + // FIXME: We also store Port objects in the window list. + // We should add a check that we really only pass windows here... + updateWindow((Window *)*it); + } + + setPort(oldPort); +} + +Window *GfxPorts::newWindow(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]) { + ++id; + } + if (id == _windowsById.size()) + _windowsById.push_back(0); + assert(0 < id && id < 0xFFFF); + + Window *pwnd = new Window(id); + Common::Rect r; + + if (!pwnd) { + warning("Can't open window!"); + return 0; + } + + _windowsById[id] = pwnd; + if (style & SCI_WINDOWMGR_STYLE_TOPMOST) + _windowList.push_front(pwnd); + else + _windowList.push_back(pwnd); + openPort(pwnd); + r = dims; + pwnd->rect = dims; + if (restoreRect) + pwnd->restoreRect = *restoreRect; + + pwnd->wndStyle = style; + pwnd->hSaved1 = pwnd->hSaved2 = NULL_REG; + pwnd->bDrawn = false; + if ((style & SCI_WINDOWMGR_STYLE_TRANSPARENT) == 0) + pwnd->saveScreenMask = (priority == -1 ? SCI_SCREEN_MASK_VISUAL : SCI_SCREEN_MASK_VISUAL | SCI_SCREEN_MASK_PRIORITY); + + if (title && (style & SCI_WINDOWMGR_STYLE_TITLE)) { + pwnd->title = title; + } + + r = dims; + if ((style != SCI_WINDOWMGR_STYLE_USER) && !(style & SCI_WINDOWMGR_STYLE_NOFRAME)) { + r.grow(1); + if (style & SCI_WINDOWMGR_STYLE_TITLE) { + r.top -= 10; + r.bottom++; + } + } + + // FIXME: it seems as if shadows may result in the window getting moved one upwards + // so that the shadow is visible (lsl5) + + pwnd->dims = r; + const Common::Rect *wmprect = &_wmgrPort->rect; + int16 oldtop = pwnd->dims.top; + int16 oldleft = pwnd->dims.left; + if (wmprect->top > pwnd->dims.top) + pwnd->dims.moveTo(pwnd->dims.left, wmprect->top); + + if (wmprect->bottom < pwnd->dims.bottom) + pwnd->dims.moveTo(pwnd->dims.left, wmprect->bottom - pwnd->dims.bottom + pwnd->dims.top); + + if (wmprect->right < pwnd->dims.right) + pwnd->dims.moveTo(wmprect->right + pwnd->dims.left - pwnd->dims.right, pwnd->dims.top); + + if (wmprect->left > pwnd->dims.left) + pwnd->dims.moveTo(wmprect->left, pwnd->dims.top); + + pwnd->rect.moveTo(pwnd->rect.left + pwnd->dims.left - oldleft, pwnd->rect.top + pwnd->dims.top - oldtop); + if (restoreRect == 0) + pwnd->restoreRect = pwnd->dims; + + if (!(pwnd->wndStyle & (SCI_WINDOWMGR_STYLE_USER | SCI_WINDOWMGR_STYLE_NOFRAME))) { + // The shadow is drawn slightly outside the window. + // Enlarge restoreRect to cover that. + pwnd->restoreRect.bottom++; + pwnd->restoreRect.right++; + } + + if (draw) + drawWindow(pwnd); + setPort((Port *)pwnd); + setOrigin(pwnd->rect.left, pwnd->rect.top + _wmgrPort->top); + pwnd->rect.moveTo(0, 0); + return pwnd; +} + +void GfxPorts::drawWindow(Window *pWnd) { + if (pWnd->bDrawn) + return; + Common::Rect r; + int16 wndStyle = pWnd->wndStyle; + + pWnd->bDrawn = true; + Port *oldport = setPort(_wmgrPort); + penColor(0); + if ((wndStyle & SCI_WINDOWMGR_STYLE_TRANSPARENT) == 0) { + pWnd->hSaved1 = _paint16->bitsSave(pWnd->restoreRect, SCI_SCREEN_MASK_VISUAL); + if (pWnd->saveScreenMask & SCI_SCREEN_MASK_PRIORITY) { + pWnd->hSaved2 = _paint16->bitsSave(pWnd->restoreRect, SCI_SCREEN_MASK_PRIORITY); + if ((wndStyle & SCI_WINDOWMGR_STYLE_USER) == 0) + _paint16->fillRect(pWnd->restoreRect, SCI_SCREEN_MASK_PRIORITY, 0, 15); + } + } + + // drawing frame,shadow and title + if (!(wndStyle & SCI_WINDOWMGR_STYLE_USER)) { + r = pWnd->dims; + if (!(wndStyle & SCI_WINDOWMGR_STYLE_NOFRAME)) { + r.translate(1, 1); + _paint16->frameRect(r);// shadow + r.translate(-1, -1); + _paint16->frameRect(r);// window frame + + if (wndStyle & SCI_WINDOWMGR_STYLE_TITLE) { + _paint16->frameRect(r); + r.grow(-1); + if (getSciVersion() <= SCI_VERSION_0_LATE) + _paint16->fillRect(r, SCI_SCREEN_MASK_VISUAL, 8); // grey titlebar for SCI0 + else + _paint16->fillRect(r, SCI_SCREEN_MASK_VISUAL, 0); // black titlebar for SCI01+ + if (!pWnd->title.empty()) { + int16 oldcolor = getPort()->penClr; + penColor(255); + _text->Box(pWnd->title.c_str(), 1, r, SCI_TEXT_ALIGNMENT_CENTER, 0); + penColor(oldcolor); + } + + r = pWnd->dims; + r.top += 9; + } + + r.grow(-1); + } + + if (!(wndStyle & SCI_WINDOWMGR_STYLE_TRANSPARENT)) + _paint16->fillRect(r, SCI_SCREEN_MASK_VISUAL, pWnd->backClr); + + _paint16->bitsShow(pWnd->restoreRect); + } + setPort(oldport); +} + +void GfxPorts::disposeWindow(Window *pWnd, bool reanimate) { + setPort(_wmgrPort); + _paint16->bitsRestore(pWnd->hSaved1); + _paint16->bitsRestore(pWnd->hSaved2); + if (!reanimate) + _paint16->bitsShow(pWnd->restoreRect); + else + _gui->graphRedrawBox(pWnd->restoreRect); + _windowList.remove(pWnd); + setPort(_windowList.back()); + _windowsById[pWnd->id] = 0; + delete pWnd; +} + +void GfxPorts::updateWindow(Window *wnd) { + reg_t handle; + + if (wnd->saveScreenMask && wnd->bDrawn) { + handle = _paint16->bitsSave(wnd->restoreRect, SCI_SCREEN_MASK_VISUAL); + _paint16->bitsRestore(wnd->hSaved1); + wnd->hSaved1 = handle; + if (wnd->saveScreenMask & SCI_SCREEN_MASK_PRIORITY) { + handle = _paint16->bitsSave(wnd->restoreRect, SCI_SCREEN_MASK_PRIORITY); + _paint16->bitsRestore(wnd->hSaved2); + wnd->hSaved2 = handle; + } + } +} + +Port *GfxPorts::getPortById(uint16 id) { + if (id > _windowsById.size()) + error("getPortById() received invalid id"); + return _windowsById[id]; +} + + + +Port *GfxPorts::setPort(Port *newPort) { + Port *oldPort = _curPort; + _curPort = newPort; + return oldPort; +} + +Port *GfxPorts::getPort() { + return _curPort; +} + +void GfxPorts::setOrigin(int16 left, int16 top) { + _curPort->left = left; + _curPort->top = top; +} + +void GfxPorts::moveTo(int16 left, int16 top) { + _curPort->curTop = top; + _curPort->curLeft = left; +} + +void GfxPorts::move(int16 left, int16 top) { + _curPort->curTop += top; + _curPort->curLeft += left; +} + +void GfxPorts::openPort(Port *port) { + port->fontId = 0; + port->fontHeight = 8; + + Port *tmp = _curPort; + _curPort = port; + _text->SetFont(port->fontId); + _curPort = tmp; + + port->top = 0; + port->left = 0; + port->greyedOutput = false; + port->penClr = 0; + port->backClr = 255; + port->penMode = 0; + port->rect = _bounds; +} + +void GfxPorts::penColor(int16 color) { + _curPort->penClr = color; +} + +void GfxPorts::backColor(int16 color) { + _curPort->backClr = color; +} + +void GfxPorts::penMode(int16 mode) { + _curPort->penMode = mode; +} + +void GfxPorts::textGreyedOutput(bool state) { + _curPort->greyedOutput = state; +} + +int16 GfxPorts::getPointSize() { + return _curPort->fontHeight; +} + +void GfxPorts::offsetRect(Common::Rect &r) { + r.top += _curPort->top; + r.bottom += _curPort->top; + r.left += _curPort->left; + r.right += _curPort->left; +} + +void GfxPorts::offsetLine(Common::Point &start, Common::Point &end) { + start.x += _curPort->left; + start.y += _curPort->top; + end.x += _curPort->left; + end.y += _curPort->top; +} + +void GfxPorts::priorityBandsInit(int16 bandCount, int16 top, int16 bottom) { + int16 y; + int32 bandSize; + + // This code is for 320x200 games only + if (_screen->getHeight() != 200) + return; + + if (bandCount != -1) + _priorityBandCount = bandCount; + + _priorityTop = top; + _priorityBottom = bottom; + + // Do NOT modify this algo or optimize it anyhow, sierra sci used int32 for calculating the + // priority bands and by using double or anything rounding WILL destroy the result + bandSize = ((_priorityBottom - _priorityTop) * 2000) / _priorityBandCount; + + memset(_priorityBands, 0, sizeof(byte) * _priorityTop); + for (y = _priorityTop; y < _priorityBottom; y++) + _priorityBands[y] = 1 + (((y - _priorityTop) * 2000) / bandSize); + if (_priorityBandCount == 15) { + // When having 15 priority bands, we actually replace band 15 with band 14, cause the original sci interpreter also + // does it that way as well + y = _priorityBottom; + while (_priorityBands[--y] == _priorityBandCount) + _priorityBands[y]--; + } + // 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; +} + +void GfxPorts::priorityBandsInit(byte *data) { + int i = 0, inx; + byte priority = 0; + + for (inx = 0; inx < 14; inx++) { + priority = *data++; + while (i < priority) + _priorityBands[i++] = inx; + } + while (i < 200) + _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); + data += 2; + } + priorityBandsMemoryActive = true; +} + +void GfxPorts::priorityBandsRecall() { + if (priorityBandsMemoryActive) { + priorityBandsInit((byte *)&priorityBandsMemory); + priorityBandsMemoryActive = false; + } +} + +byte GfxPorts::coordinateToPriority(int16 y) { + if (y < _priorityTop) + return _priorityBands[_priorityTop]; + if (y > _priorityBottom) + return _priorityBands[_priorityBottom]; + return _priorityBands[y]; +} + +int16 GfxPorts::priorityToCoordinate(byte priority) { + int16 y; + if (priority <= _priorityBandCount) { + for (y = 0; y <= _priorityBottom; y++) + if (_priorityBands[y] == priority) + return y; + } + return _priorityBottom; +} + +} // End of namespace Sci |