aboutsummaryrefslogtreecommitdiff
path: root/engines/lure/surface.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/lure/surface.cpp')
-rw-r--r--engines/lure/surface.cpp456
1 files changed, 456 insertions, 0 deletions
diff --git a/engines/lure/surface.cpp b/engines/lure/surface.cpp
new file mode 100644
index 0000000000..e254cfe501
--- /dev/null
+++ b/engines/lure/surface.cpp
@@ -0,0 +1,456 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 "lure/surface.h"
+#include "lure/decode.h"
+#include "lure/system.h"
+#include "lure/events.h"
+#include "lure/screen.h"
+#include "lure/room.h"
+#include "lure/strings.h"
+
+namespace Lure {
+
+// These variables hold resources commonly used by the Surfaces, and must be initialised and freed
+// by the static Surface methods initialise and deinitailse
+
+static MemoryBlock *int_font = NULL;
+static MemoryBlock *int_dialog_frame = NULL;
+static uint8 fontSize[NUM_CHARS_IN_FONT];
+
+void Surface::initialise() {
+ int_font = Disk::getReference().getEntry(FONT_RESOURCE_ID);
+ int_dialog_frame = Disk::getReference().getEntry(DIALOG_RESOURCE_ID);
+
+ // Calculate the size of each font character
+ for (int ctr = 0; ctr < NUM_CHARS_IN_FONT; ++ctr) {
+ byte *pChar = int_font->data() + (ctr * 8);
+ fontSize[ctr] = 0;
+
+ for (int yp = 0; yp < FONT_HEIGHT; ++yp)
+ {
+ byte v = *pChar++;
+
+ for (int xp = 0; xp < FONT_WIDTH; ++xp) {
+ if ((v & 0x80) && (xp > fontSize[ctr]))
+ fontSize[ctr] = xp;
+ v = (v << 1) & 0xff;
+ }
+ }
+
+ // If character is empty, like for a space, give a default size
+ if (fontSize[ctr] == 0) fontSize[ctr] = 2;
+ }
+}
+
+void Surface::deinitialise() {
+ delete int_font;
+ delete int_dialog_frame;
+}
+
+/*--------------------------------------------------------------------------*/
+
+Surface::Surface(MemoryBlock *src, uint16 wdth, uint16 hght): _data(src),
+ _width(wdth), _height(hght) {
+ if ((uint32) (wdth * hght) != src->size())
+ error("Surface dimensions do not match size of passed data");
+}
+
+Surface::Surface(uint16 wdth, uint16 hght): _data(Memory::allocate(wdth*hght)),
+ _width(wdth), _height(hght) {
+}
+
+Surface::~Surface() {
+ delete _data;
+}
+
+void Surface::loadScreen(uint16 resourceId) {
+ MemoryBlock *rawData = Disk::getReference().getEntry(resourceId);
+ PictureDecoder decoder;
+ MemoryBlock *tmpScreen = decoder.decode(rawData, FULL_SCREEN_HEIGHT * FULL_SCREEN_WIDTH);
+ delete rawData;
+ empty();
+ copyFrom(tmpScreen, MENUBAR_Y_SIZE * FULL_SCREEN_WIDTH);
+
+ delete tmpScreen;
+}
+
+void Surface::writeChar(uint16 x, uint16 y, uint8 ascii, bool transparent, uint8 colour) {
+ byte *const addr = _data->data() + (y * _width) + x;
+
+ if ((ascii < 32) || (ascii >= 32 + NUM_CHARS_IN_FONT))
+ error("Invalid ascii character passed for display '%d'", ascii);
+
+ uint8 v;
+ byte *pFont = int_font->data() + ((ascii - 32) * 8);
+ byte *pDest;
+ uint8 charWidth = 0;
+
+ for (int y1 = 0; y1 < 8; ++y1) {
+ v = *pFont++;
+ pDest = addr + (y1 * _width);
+
+ for (int x1 = 0; x1 < 8; ++x1, ++pDest) {
+ if (v & 0x80) {
+ *pDest = colour;
+ if (x1+1 > charWidth) charWidth = x1 + 1;
+ }
+ else if (!transparent) *pDest = 0;
+ v = (v << 1) & 0xff;
+ }
+ }
+}
+
+void Surface::writeString(uint16 x, uint16 y, Common::String line, bool transparent,
+ uint8 colour, bool varLength) {
+ const char *sPtr = line.c_str();
+
+ while (*sPtr) {
+ writeChar(x, y, (uint8) *sPtr, transparent, colour);
+
+ // Move to after the character in preparation for the next character
+ if (!varLength) x += FONT_WIDTH;
+ else x += fontSize[*sPtr - ' '] + 2;
+
+ ++sPtr; // Move to next character
+ }
+}
+
+void Surface::transparentCopyTo(Surface *dest) {
+ if (dest->width() != _width)
+ error("Incompatible surface sizes for transparent copy");
+
+ byte *pSrc = _data->data();
+ byte *pDest = dest->data().data();
+ uint16 numBytes = MIN(_height,dest->height()) * FULL_SCREEN_WIDTH;
+
+ while (numBytes-- > 0) {
+ if (*pSrc) *pDest = *pSrc;
+
+ ++pSrc;
+ ++pDest;
+ }
+}
+
+void Surface::copyTo(Surface *dest)
+{
+ copyTo(dest, 0, 0);
+}
+
+void Surface::copyTo(Surface *dest, uint16 x, uint16 y)
+{
+ if ((x == 0) && (dest->width() == _width)) {
+ // Use fast data transfer
+ uint32 dataSize = dest->data().size() - (y * _width);
+ if (dataSize > _data->size()) dataSize = _data->size();
+ dest->data().copyFrom(_data, 0, y * _width, dataSize);
+ } else {
+ // Use slower transfer
+ Rect rect;
+ rect.left = 0; rect.top = 0;
+ rect.right = _width-1; rect.bottom = _height-1;
+ copyTo(dest, rect, x, y);
+ }
+}
+
+void Surface::copyTo(Surface *dest, const Rect &srcBounds,
+ uint16 destX, uint16 destY, int transparentColour) {
+ for (uint16 y=0; y<=(srcBounds.bottom-srcBounds.top); ++y) {
+ const uint32 srcPos = (srcBounds.top + y) * _width + srcBounds.left;
+ const uint32 destPos = (destY+y) * dest->width() + destX;
+
+ uint16 numBytes = srcBounds.right-srcBounds.left+1;
+ if (transparentColour == -1) {
+ // No trnnsparent colour, so copy all the bytes of the line
+ dest->data().copyFrom(_data, srcPos, destPos, numBytes);
+ } else {
+ byte *pSrc = _data->data() + srcPos;
+ byte *pDest = dest->data().data() + destPos;
+
+ while (numBytes-- > 0) {
+ if (*pSrc != (uint8) transparentColour)
+ *pDest = *pSrc;
+ ++pSrc;
+ ++pDest;
+ }
+ }
+ }
+}
+
+void Surface::copyFrom(MemoryBlock *src, uint32 destOffset) {
+ uint32 size = _data->size() - destOffset;
+ if (src->size() > size) size = src->size();
+ _data->copyFrom(src, 0, destOffset, size);
+}
+
+// fillRect
+// Fills a rectangular area with a colour
+
+void Surface::fillRect(const Rect &r, uint8 colour) {
+ for (int yp = r.top; yp <= r.bottom; ++yp) {
+ byte *const addr = _data->data() + (yp * _width) + r.left;
+ memset(addr, colour, r.width());
+ }
+}
+
+// createDialog
+// Forms a dialog encompassing the entire surface
+
+void copyLine(byte *pSrc, byte *pDest, uint16 leftSide, uint16 center, uint16 rightSide) {
+ // Left area
+ memcpy(pDest, pSrc, leftSide);
+ pSrc += leftSide; pDest += leftSide;
+ // Center area
+ memset(pDest, *pSrc, center);
+ ++pSrc; pDest += center;
+ // Right side
+ memcpy(pDest, pSrc, rightSide);
+ pSrc += rightSide; pDest += rightSide;
+}
+
+void Surface::createDialog(bool blackFlag) {
+ if ((_width < 20) || (_height < 20)) return;
+
+ byte *pSrc = int_dialog_frame->data();
+ byte *pDest = _data->data();
+ uint16 xCenter = _width - DIALOG_EDGE_SIZE * 2;
+ uint16 yCenter = _height - DIALOG_EDGE_SIZE * 2;
+
+ // Dialog top
+ for (int y = 0; y < 9; ++y) {
+ copyLine(pSrc, pDest, DIALOG_EDGE_SIZE - 2, xCenter + 2, DIALOG_EDGE_SIZE);
+ pSrc += (DIALOG_EDGE_SIZE - 2) + 1 + DIALOG_EDGE_SIZE;
+ pDest += _width;
+ }
+
+ // Dialog sides - note that the same source data gets used for all side lines
+ for (int y = 0; y < yCenter; ++y) {
+ copyLine(pSrc, pDest, DIALOG_EDGE_SIZE, xCenter, DIALOG_EDGE_SIZE);
+ pDest += _width;
+ }
+ pSrc += DIALOG_EDGE_SIZE * 2 + 1;
+
+ // Dialog bottom
+ for (int y = 0; y < 9; ++y) {
+ copyLine(pSrc, pDest, DIALOG_EDGE_SIZE, xCenter + 1, DIALOG_EDGE_SIZE - 1);
+ pSrc += DIALOG_EDGE_SIZE + 1 + (DIALOG_EDGE_SIZE - 1);
+ pDest += _width;
+ }
+
+ // Final processing - if black flag set, clear dialog inside area
+ if (blackFlag) {
+ Rect r = Rect(DIALOG_EDGE_SIZE, DIALOG_EDGE_SIZE,
+ _width - DIALOG_EDGE_SIZE, _height-DIALOG_EDGE_SIZE);
+ fillRect(r, 0);
+ }
+}
+
+void Surface::copyToScreen(uint16 x, uint16 y) {
+ OSystem &system = System::getReference();
+ system.copyRectToScreen(_data->data(), _width, x, y, _width, _height);
+ system.updateScreen();
+}
+
+void Surface::centerOnScreen() {
+ OSystem &system = System::getReference();
+
+ system.copyRectToScreen(_data->data(), _width,
+ (FULL_SCREEN_WIDTH - _width) / 2, (FULL_SCREEN_HEIGHT - _height) / 2,
+ _width, _height);
+ system.updateScreen();
+}
+
+uint16 Surface::textWidth(const char *s, int numChars) {
+ uint16 result = 0;
+ if (numChars == 0) numChars = strlen(s);
+
+ while (numChars-- > 0) result += fontSize[*s++ - ' '] + 2;
+ return result;
+}
+
+Surface *Surface::newDialog(uint16 width, uint8 numLines, char **lines, bool varLength, uint8 colour) {
+ Surface *s = new Surface(width, (DIALOG_EDGE_SIZE + 3) * 2 +
+ numLines * (FONT_HEIGHT - 1));
+ s->createDialog();
+
+ for (uint8 ctr = 0; ctr < numLines; ++ctr)
+ s->writeString(DIALOG_EDGE_SIZE + 3, DIALOG_EDGE_SIZE + 3 +
+ (ctr * (FONT_HEIGHT - 1)), lines[ctr], true, colour, varLength);
+ return s;
+}
+
+Surface *Surface::newDialog(uint16 width, const char *line, uint8 colour) {
+ uint8 numLines = 1;
+ uint16 lineWidth = 0;
+ char *s, *lineCopy;
+ bool newLine;
+
+ s = lineCopy = strdup(line);
+
+ // Scan through the text and insert NULLs to break the line into allowable widths
+
+ while (*s != '\0') {
+ char *wordStart = s;
+ while (*wordStart == ' ') ++wordStart;
+ char *wordEnd = strchr(wordStart, ' ');
+ char *wordEnd2 = strchr(wordStart, '\n');
+ if ((!wordEnd) || ((wordEnd2) && (wordEnd2 < wordEnd))) {
+ wordEnd = wordEnd2;
+ newLine = (wordEnd2 != NULL);
+ } else {
+ newLine = false;
+ }
+
+ if (wordEnd) --wordEnd; // move back one to end of word
+ else wordEnd = strchr(s, '\0') - 1;
+
+ uint16 wordSize = textWidth(s, (int) (wordEnd - s + 1));
+
+ if (lineWidth + wordSize > width - (DIALOG_EDGE_SIZE + 3) * 2) {
+ // Break word onto next line
+ *(wordStart - 1) = '\0';
+ ++numLines;
+ lineWidth = textWidth(wordStart, (int) (wordEnd - wordStart + 1));
+ } else if (newLine) {
+ // Break on newline
+ ++numLines;
+ ++wordEnd;
+ *wordEnd = '\0';
+ lineWidth = 0;
+ } else {
+ // Add word's length to total for line
+ lineWidth += wordSize;
+ }
+
+ s = wordEnd+1;
+ }
+
+ // Set up a list for the start of each line
+ char **lines = (char **) Memory::alloc(sizeof(char *) * numLines);
+ lines[0] = lineCopy;
+ for (int ctr = 1; ctr < numLines; ++ctr)
+ lines[ctr] = strchr(lines[ctr-1], 0) + 1;
+
+ // Create the dialog
+ Surface *result = newDialog(width, numLines, lines, true, colour);
+
+ // Deallocate used resources
+ free(lines);
+ free(lineCopy);
+
+ return result;
+}
+
+Surface *Surface::getScreen(uint16 resourceId) {
+ MemoryBlock *block = Disk::getReference().getEntry(resourceId);
+ PictureDecoder d;
+ MemoryBlock *decodedData = d.decode(block);
+ delete block;
+ return new Surface(decodedData, FULL_SCREEN_WIDTH, decodedData->size() / FULL_SCREEN_WIDTH);
+}
+
+/*--------------------------------------------------------------------------*/
+
+void Dialog::show(const char *text) {
+ Screen &screen = Screen::getReference();
+ Mouse &mouse = Mouse::getReference();
+ mouse.cursorOff();
+
+ Surface *s = Surface::newDialog(INFO_DIALOG_WIDTH, text);
+ s->copyToScreen(INFO_DIALOG_X, INFO_DIALOG_Y);
+
+ // Wait for a keypress or mouse button
+ Events::getReference().waitForPress();
+
+ screen.update();
+ mouse.cursorOn();
+}
+
+void Dialog::show(uint16 stringId) {
+ char buffer[MAX_DESC_SIZE];
+ Room &r = Room::getReference();
+ StringData &sl = StringData::getReference();
+
+ Action action = r.getCurrentAction();
+
+ const char *actionName = (action == NONE) ? NULL : actionList[action];
+ char hotspotName[MAX_HOTSPOT_NAME_SIZE];
+ if (r.hotspotId() == 0)
+ strcpy(hotspotName, "");
+ else
+ sl.getString(r.hotspot().nameId, hotspotName, NULL, NULL);
+
+ sl.getString(stringId, buffer, hotspotName, actionName);
+ show(buffer);
+}
+
+void Dialog::showMessage(uint16 messageId, uint16 characterId) {
+ MemoryBlock *data = Resources::getReference().messagesData();
+ uint16 *v = (uint16 *) data->data();
+ uint16 v2, idVal;
+ messageId &= 0x7fff;
+
+ // Skip through header to find table for given character
+ while (READ_LE_UINT16(v) != characterId) v += 2;
+
+ // Scan through secondary list
+ ++v;
+ v = (uint16 *) (data->data() + READ_LE_UINT16(v));
+ v2 = 0;
+ while ((idVal = READ_LE_UINT16(v)) != 0xffff) {
+ ++v;
+ if (READ_LE_UINT16(v) == messageId) break;
+ ++v;
+ }
+ // default response if a specific response not found
+ if (idVal == 0xffff) idVal = 0x8c4;
+
+ if (idVal == 0x76) {
+ /*
+ call sub_154 ; (64E7)
+ mov ax,word ptr ds:[5813h] ; (273F:5813=1BA3h)
+ mov [bx+ANIM_SEGMENT],ax
+ mov ax,word ptr ds:[5817h] ; (273F:5817=0ED8Eh)
+ mov [bx+ANIM_FRAME],ax
+ retn
+*/
+ } else if (idVal == 0x120) {
+ /*
+ call sub_154 ; (64E7)
+ mov ax,word ptr ds:[5813h] ; (273F:5813=1BA3h)
+ mov [bx+ANIM_SEGMENT],ax
+ mov ax,word ptr ds:[5817h] ; (273F:5817=0ED8Eh)
+ shl ax,1
+ mov [bx+ANIM_FRAME],ax
+*/
+ } else if (idVal >= 0x8000) {
+ // Handle string display
+ idVal &= 0x7fff;
+ Dialog::show(idVal);
+
+ } else if (idVal != 0) {
+ /* still to be decoded */
+
+ }
+}
+
+} // end of namespace Lure