mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-21 01:08:25 +00:00
335ba979a2
Fixes bug #3539031 - "LURE: Crash at Copy Protection Screen". This reverts the previous fix which only worked for QWERTY keyboards and made the issue worse for AZERTY keyboards. It now uses the ASCII code instead of the keycode for the sanity check.
1417 lines
43 KiB
C++
1417 lines
43 KiB
C++
/* 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 "lure/decode.h"
|
|
#include "lure/events.h"
|
|
#include "lure/game.h"
|
|
#include "lure/lure.h"
|
|
#include "lure/room.h"
|
|
#include "lure/screen.h"
|
|
#include "lure/sound.h"
|
|
#include "lure/strings.h"
|
|
#include "lure/surface.h"
|
|
#include "common/endian.h"
|
|
|
|
namespace Lure {
|
|
|
|
// These variables hold resources commonly used by the Surfaces, and must be initialized and freed
|
|
// by the static Surface methods initialize and deinitailse
|
|
|
|
static MemoryBlock *int_font = NULL;
|
|
static MemoryBlock *int_dialog_frame = NULL;
|
|
static uint8 fontSize[256];
|
|
static int numFontChars;
|
|
|
|
static const byte char8A[8] = {0x40, 0x20, 0x00, 0x90, 0x90, 0x90, 0x68, 0x00}; // accented `u
|
|
static const byte char8D[8] = {0x80, 0x40, 0x00, 0xc0, 0x40, 0x40, 0x60, 0x00}; // accented `i
|
|
static const byte char95[8] = {0x40, 0x20, 0x00, 0x60, 0x90, 0x90, 0x60, 0x00}; // accented `o
|
|
|
|
void Surface::initialize() {
|
|
Disk &disk = Disk::getReference();
|
|
int_font = disk.getEntry(FONT_RESOURCE_ID);
|
|
int_dialog_frame = disk.getEntry(DIALOG_RESOURCE_ID);
|
|
|
|
if (LureEngine::getReference().getLanguage() == Common::IT_ITA) {
|
|
Common::copy(&char8A[0], &char8A[8], int_font->data() + (0x8A - 32) * 8);
|
|
Common::copy(&char8D[0], &char8D[8], int_font->data() + (0x8D - 32) * 8);
|
|
Common::copy(&char95[0], &char95[8], int_font->data() + (0x95 - 32) * 8);
|
|
}
|
|
|
|
numFontChars = int_font->size() / 8;
|
|
if (numFontChars > 256)
|
|
error("Font data exceeded maximum allowable size");
|
|
|
|
// Calculate the size of each font character
|
|
for (int ctr = 0; ctr < numFontChars; ++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::deinitialize() {
|
|
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;
|
|
}
|
|
|
|
// textX / textY
|
|
// Returns the offset into a dialog for writing text
|
|
|
|
uint16 Surface::textX() { return LureEngine::getReference().isEGA() ? 10 : 12; }
|
|
|
|
uint16 Surface::textY() { return LureEngine::getReference().isEGA() ? 8 : 12; }
|
|
|
|
// getDialogBounds
|
|
// Returns a suggested size for a dialog given a number of horizontal characters and rows
|
|
|
|
void Surface::getDialogBounds(Common::Point &size, int charWidth, int numLines, bool squashedLines) {
|
|
size.x = Surface::textX() * 2 + FONT_WIDTH * charWidth;
|
|
size.y = Surface::textY() * 2 + (squashedLines ? (FONT_HEIGHT - 1) : FONT_HEIGHT) * numLines;
|
|
}
|
|
|
|
// egaCreateDialog
|
|
// Forms a dialog encompassing the entire surface
|
|
|
|
void Surface::egaCreateDialog(bool blackFlag) {
|
|
byte lineColors1[3] = {6, 0, 9};
|
|
byte lineColors2[3] = {7, 0, 12};
|
|
|
|
// Surface contents
|
|
data().setBytes(blackFlag ? 0 : EGA_DIALOG_BG_COLOR, 0, data().size());
|
|
|
|
// Top/bottom lines
|
|
for (int y = 2; y >= 0; --y) {
|
|
data().setBytes(lineColors1[y], y * width(), width());
|
|
data().setBytes(lineColors2[y], (height() - y - 1) * width(), width());
|
|
|
|
for (int p = y + 1; p < height() - y; ++p) {
|
|
byte *line = data().data() + p * width();
|
|
*(line + y) = lineColors2[y];
|
|
*(line + width() - y - 1) = lineColors1[y];
|
|
}
|
|
}
|
|
}
|
|
|
|
// vgaCreateDialog
|
|
// 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;
|
|
}
|
|
|
|
#define VGA_DIALOG_EDGE_WIDTH 9
|
|
|
|
void Surface::vgaCreateDialog(bool blackFlag) {
|
|
byte *pSrc = int_dialog_frame->data();
|
|
byte *pDest = _data->data();
|
|
uint16 xCenter = _width - VGA_DIALOG_EDGE_WIDTH * 2;
|
|
uint16 yCenter = _height - VGA_DIALOG_EDGE_WIDTH * 2;
|
|
int y;
|
|
|
|
// Dialog top
|
|
for (y = 0; y < 9; ++y) {
|
|
copyLine(pSrc, pDest, VGA_DIALOG_EDGE_WIDTH - 2, xCenter + 2, VGA_DIALOG_EDGE_WIDTH);
|
|
pSrc += (VGA_DIALOG_EDGE_WIDTH - 2) + 1 + VGA_DIALOG_EDGE_WIDTH;
|
|
pDest += _width;
|
|
}
|
|
|
|
// Dialog sides - note that the same source data gets used for all side lines
|
|
for (y = 0; y < yCenter; ++y) {
|
|
copyLine(pSrc, pDest, VGA_DIALOG_EDGE_WIDTH, xCenter, VGA_DIALOG_EDGE_WIDTH);
|
|
pDest += _width;
|
|
}
|
|
pSrc += VGA_DIALOG_EDGE_WIDTH * 2 + 1;
|
|
|
|
// Dialog bottom
|
|
for (y = 0; y < 9; ++y) {
|
|
copyLine(pSrc, pDest, VGA_DIALOG_EDGE_WIDTH, xCenter + 1, VGA_DIALOG_EDGE_WIDTH - 1);
|
|
pSrc += VGA_DIALOG_EDGE_WIDTH + 1 + (VGA_DIALOG_EDGE_WIDTH - 1);
|
|
pDest += _width;
|
|
}
|
|
|
|
// Final processing - if black flag set, clear dialog inside area
|
|
if (blackFlag) {
|
|
Common::Rect r = Common::Rect(VGA_DIALOG_EDGE_WIDTH, VGA_DIALOG_EDGE_WIDTH,
|
|
_width - VGA_DIALOG_EDGE_WIDTH, _height-VGA_DIALOG_EDGE_WIDTH);
|
|
fillRect(r, 0);
|
|
}
|
|
}
|
|
|
|
void Surface::loadScreen(uint16 resourceId) {
|
|
MemoryBlock *rawData = Disk::getReference().getEntry(resourceId);
|
|
loadScreen(rawData);
|
|
delete rawData;
|
|
}
|
|
|
|
void Surface::loadScreen(MemoryBlock *rawData) {
|
|
PictureDecoder decoder;
|
|
uint16 v = READ_BE_UINT16(rawData->data());
|
|
bool is5Bit = (v & 0xfffe) == 0x140;
|
|
MemoryBlock *tmpScreen;
|
|
|
|
if (is5Bit)
|
|
// 5-bit decompression
|
|
tmpScreen = decoder.egaDecode(rawData, FULL_SCREEN_HEIGHT * FULL_SCREEN_WIDTH + 1);
|
|
else
|
|
// VGA decompression
|
|
tmpScreen = decoder.vgaDecode(rawData, FULL_SCREEN_HEIGHT * FULL_SCREEN_WIDTH + 1);
|
|
|
|
empty();
|
|
_data->copyFrom(tmpScreen, 0, MENUBAR_Y_SIZE * FULL_SCREEN_WIDTH,
|
|
(FULL_SCREEN_HEIGHT - MENUBAR_Y_SIZE) * FULL_SCREEN_WIDTH);
|
|
delete tmpScreen;
|
|
}
|
|
|
|
int Surface::writeChar(uint16 x, uint16 y, uint8 ascii, bool transparent, int color) {
|
|
byte *const addr = _data->data() + (y * _width) + x;
|
|
if (color == DEFAULT_TEXT_COLOR)
|
|
color = LureEngine::getReference().isEGA() ? EGA_DIALOG_TEXT_COLOR : VGA_DIALOG_TEXT_COLOR;
|
|
|
|
if ((ascii < 32) || (ascii >= 32 + numFontChars))
|
|
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 = color;
|
|
if (x1+1 > charWidth) charWidth = x1 + 1;
|
|
}
|
|
else if (!transparent) *pDest = 0;
|
|
v = (v << 1) & 0xff;
|
|
}
|
|
}
|
|
|
|
return charWidth;
|
|
}
|
|
|
|
void Surface::writeString(uint16 x, uint16 y, Common::String line, bool transparent,
|
|
int color, bool varLength) {
|
|
writeSubstring(x, y, line, line.size(), transparent, color, varLength);
|
|
}
|
|
|
|
void Surface::writeSubstring(uint16 x, uint16 y, Common::String line, int len,
|
|
bool transparent, int color, bool varLength) {
|
|
|
|
const char *sPtr = line.c_str();
|
|
if (color == DEFAULT_TEXT_COLOR)
|
|
color = LureEngine::getReference().isEGA() ? EGA_DIALOG_TEXT_COLOR : VGA_DIALOG_TEXT_COLOR;
|
|
|
|
for (int index = 0; (index < len) && (*sPtr != '\0'); ++index, ++sPtr) {
|
|
int charSize = varLength ? fontSize[(uint8)*sPtr - 32] + 2 : FONT_WIDTH;
|
|
if (x + charSize >= width())
|
|
// Passed the right hand edge of the surface
|
|
break;
|
|
|
|
// Write next character
|
|
writeChar(x, y, (uint8) *sPtr, transparent, color);
|
|
|
|
// Move to after the character in preparation for the next character
|
|
x += charSize;
|
|
}
|
|
}
|
|
|
|
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
|
|
Common::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 Common::Rect &srcBounds,
|
|
uint16 destX, uint16 destY, int transparentColor) {
|
|
int numBytes = srcBounds.right - srcBounds.left + 1;
|
|
if (destX + numBytes > dest->width())
|
|
numBytes = dest->width() - destX;
|
|
if (numBytes <= 0) return;
|
|
|
|
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;
|
|
|
|
if (transparentColor == -1) {
|
|
// No trnnsparent color, 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;
|
|
|
|
int bytesCtr = numBytes;
|
|
while (bytesCtr-- > 0) {
|
|
if (*pSrc != (uint8) transparentColor)
|
|
*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 color
|
|
|
|
void Surface::fillRect(const Common::Rect &r, uint8 color) {
|
|
for (int yp = r.top; yp <= r.bottom; ++yp) {
|
|
byte *const addr = _data->data() + (yp * _width) + r.left;
|
|
memset(addr, color, r.width());
|
|
}
|
|
}
|
|
|
|
void Surface::createDialog(bool blackFlag) {
|
|
if (LureEngine::getReference().isEGA())
|
|
egaCreateDialog(blackFlag);
|
|
else
|
|
vgaCreateDialog(blackFlag);
|
|
}
|
|
|
|
void Surface::copyToScreen(uint16 x, uint16 y) {
|
|
OSystem &system = *g_system;
|
|
system.copyRectToScreen(_data->data(), _width, x, y, _width, _height);
|
|
system.updateScreen();
|
|
}
|
|
|
|
void Surface::centerOnScreen() {
|
|
OSystem &system = *g_system;
|
|
|
|
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) {
|
|
uint8 charIndex = (uint8)*s++ - 32;
|
|
assert(charIndex < numFontChars);
|
|
result += fontSize[charIndex] + 2;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void Surface::wordWrap(char *text, uint16 width, char **&lines, uint8 &numLines) {
|
|
debugC(ERROR_INTERMEDIATE, kLureDebugStrings, "wordWrap(text=%s, width=%d", text, width);
|
|
numLines = 1;
|
|
uint16 lineWidth = 0;
|
|
char *s;
|
|
bool newLine;
|
|
|
|
s = text;
|
|
|
|
// 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;
|
|
}
|
|
|
|
debugC(ERROR_DETAILED, kLureDebugStrings, "word scanning: start=%xh, after=%xh, newLine=%d",
|
|
(uint32)(wordStart - text), (uint32)((wordEnd == NULL) ? -1 : wordEnd - text), newLine ? 1 : 0);
|
|
|
|
if (wordEnd) {
|
|
if (*wordEnd != '\0') --wordEnd;
|
|
} else {
|
|
wordEnd = strchr(wordStart, '\0') - 1;
|
|
}
|
|
|
|
int wordBytes = (int) (wordEnd - s + 1);
|
|
uint16 wordSize = (wordBytes == 0) ? 0 : textWidth(s, wordBytes);
|
|
if (gDebugLevel >= ERROR_DETAILED) {
|
|
char wordBuffer[MAX_DESC_SIZE];
|
|
strncpy(wordBuffer, wordStart, wordBytes);
|
|
wordBuffer[wordBytes] = '\0';
|
|
debugC(ERROR_DETAILED, kLureDebugStrings, "word='%s', size=%d", wordBuffer, wordSize);
|
|
}
|
|
|
|
if (lineWidth + wordSize > width) {
|
|
// Break word onto next line
|
|
*(wordStart - 1) = '\0';
|
|
++numLines;
|
|
lineWidth = 0;
|
|
wordEnd = wordStart - 1;
|
|
} else if (newLine) {
|
|
// Break on newline
|
|
++numLines;
|
|
*++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
|
|
lines = (char **) Memory::alloc(sizeof(char *) * numLines);
|
|
lines[0] = text;
|
|
debugC(ERROR_DETAILED, kLureDebugStrings, "wordWrap lines[0]='%s'", lines[0]);
|
|
for (int ctr = 1; ctr < numLines; ++ctr) {
|
|
lines[ctr] = strchr(lines[ctr-1], 0) + 1;
|
|
debugC(ERROR_DETAILED, kLureDebugStrings, "wordWrap lines[%d]='%s'", ctr, lines[ctr]);
|
|
}
|
|
|
|
debugC(ERROR_INTERMEDIATE, kLureDebugStrings, "wordWrap end - numLines=%d", numLines);
|
|
}
|
|
|
|
Surface *Surface::newDialog(uint16 width, uint8 numLines, const char **lines, bool varLength,
|
|
int color, bool squashedLines) {
|
|
Common::Point size;
|
|
Surface::getDialogBounds(size, 0, numLines, squashedLines);
|
|
|
|
Surface *s = new Surface(width, size.y);
|
|
s->createDialog();
|
|
|
|
uint16 yP = Surface::textY();
|
|
for (uint8 ctr = 0; ctr < numLines; ++ctr) {
|
|
s->writeString(Surface::textX(), yP, lines[ctr], true, color, varLength);
|
|
yP += squashedLines ? FONT_HEIGHT - 1 : FONT_HEIGHT;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
Surface *Surface::newDialog(uint16 width, const char *line, int color) {
|
|
char **lines;
|
|
char *lineCopy = strdup(line);
|
|
uint8 numLines;
|
|
wordWrap(lineCopy, width - (Surface::textX() * 2), lines, numLines);
|
|
|
|
// Create the dialog
|
|
Surface *result = newDialog(width, numLines, const_cast<const char **>(lines), true, color);
|
|
|
|
// 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);
|
|
}
|
|
|
|
bool Surface::getString(Common::String &line, int maxSize, bool isNumeric, bool varLength, int16 x, int16 y) {
|
|
OSystem &system = *g_system;
|
|
LureEngine &engine = LureEngine::getReference();
|
|
Mouse &mouse = Mouse::getReference();
|
|
Events &events = Events::getReference();
|
|
Screen &screen = Screen::getReference();
|
|
uint8 bgColor = *(screen.screen().data().data() + (y * FULL_SCREEN_WIDTH) + x);
|
|
Common::String newLine(line);
|
|
bool abortFlag = false;
|
|
bool refreshFlag = false;
|
|
|
|
bool vKbdFlag = g_system->hasFeature(OSystem::kFeatureVirtualKeyboard);
|
|
if (!vKbdFlag)
|
|
mouse.cursorOff();
|
|
else
|
|
g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
|
|
|
|
|
|
// Insert a cursor character at the end of the string
|
|
newLine.insertChar('_', newLine.size());
|
|
|
|
while (!abortFlag) {
|
|
// Display the string
|
|
screen.screen().writeString(x, y, newLine, true, DEFAULT_TEXT_COLOR, varLength);
|
|
screen.update();
|
|
int stringSize = textWidth(newLine.c_str());
|
|
|
|
// Loop until the input string changes
|
|
refreshFlag = false;
|
|
while (!refreshFlag && !abortFlag) {
|
|
abortFlag = engine.shouldQuit();
|
|
if (abortFlag) break;
|
|
|
|
while (events.pollEvent()) {
|
|
if (events.type() == Common::EVENT_KEYDOWN) {
|
|
char ch = events.event().kbd.ascii;
|
|
uint16 keycode = events.event().kbd.keycode;
|
|
|
|
if ((keycode == Common::KEYCODE_RETURN) || (keycode == Common::KEYCODE_KP_ENTER)) {
|
|
// Return character
|
|
screen.screen().fillRect(
|
|
Common::Rect(x, y, x + maxSize - 1, y + FONT_HEIGHT), bgColor);
|
|
screen.update();
|
|
newLine.deleteLastChar();
|
|
line = newLine;
|
|
if (!vKbdFlag)
|
|
mouse.cursorOn();
|
|
return true;
|
|
}
|
|
else if (keycode == Common::KEYCODE_ESCAPE) {
|
|
// Escape character
|
|
screen.screen().fillRect(
|
|
Common::Rect(x, y, x + maxSize - 1, y + FONT_HEIGHT), bgColor);
|
|
screen.update();
|
|
abortFlag = true;
|
|
} else if (keycode == Common::KEYCODE_BACKSPACE) {
|
|
// Delete the last character
|
|
if (newLine.size() == 1) continue;
|
|
|
|
screen.screen().fillRect(
|
|
Common::Rect(x, y, x + maxSize - 1, y + FONT_HEIGHT), bgColor);
|
|
newLine.deleteChar(newLine.size() - 2);
|
|
refreshFlag = true;
|
|
|
|
} else if ((ch >= ' ') && (stringSize + 8 < maxSize)) {
|
|
if (((ch >= '0') && (ch <= '9')) || !isNumeric) {
|
|
screen.screen().fillRect(
|
|
Common::Rect(x, y, x + maxSize - 1, y + FONT_HEIGHT), bgColor);
|
|
newLine.insertChar(ch, newLine.size() - 1);
|
|
refreshFlag = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
system.updateScreen();
|
|
system.delayMillis(10);
|
|
}
|
|
}
|
|
|
|
if (!vKbdFlag)
|
|
mouse.cursorOn();
|
|
else
|
|
g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
void Dialog::show(const char *text) {
|
|
debugC(ERROR_BASIC, kLureDebugStrings, "Dialog::show text=%s", text);
|
|
Screen &screen = Screen::getReference();
|
|
Mouse &mouse = Mouse::getReference();
|
|
Room &room = Room::getReference();
|
|
mouse.cursorOff();
|
|
|
|
room.update();
|
|
debugC(ERROR_DETAILED, kLureDebugStrings, "Dialog::show creating dialog");
|
|
Surface *s = Surface::newDialog(INFO_DIALOG_WIDTH, text);
|
|
debugC(ERROR_DETAILED, kLureDebugStrings, "Dialog::show created dialog");
|
|
s->copyToScreen(INFO_DIALOG_X, INFO_DIALOG_Y);
|
|
debugC(ERROR_DETAILED, kLureDebugStrings, "Dialog::show copied to screen");
|
|
|
|
// Wait for a keypress or mouse button
|
|
Events::getReference().waitForPress();
|
|
|
|
screen.update();
|
|
mouse.cursorOn();
|
|
|
|
delete s;
|
|
}
|
|
|
|
void Dialog::show(uint16 stringId, const char *hotspotName, const char *characterName) {
|
|
debugC(ERROR_BASIC, kLureDebugStrings, "Hotspot::showMessage stringId=%xh hotspot=%s, character=%s",
|
|
stringId, hotspotName, characterName);
|
|
char buffer[MAX_DESC_SIZE];
|
|
StringData &sl = StringData::getReference();
|
|
|
|
sl.getString(stringId, buffer, hotspotName, characterName);
|
|
show(buffer);
|
|
}
|
|
|
|
void Dialog::show(uint16 stringId) {
|
|
show(stringId, NULL, NULL);
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
const uint16 spanish_pre_e1_type_tl[] = {0x8000, 4, 0x4000, 5, 0x2000, 6, 0xc000, 7, 0, 0};
|
|
const uint16 spanish_others_tl[] = {0x8000, 0, 0x4000, 1, 0x2000, 2, 0xc000, 3, 0, 0};
|
|
|
|
const uint16 german_pre_k_type[] = {106, 0};
|
|
const uint16 german_pre_k_type_tl[] = {0x8000, 0, 0xc000, 0, 0x4000, 1, 0xa000, 1, 0x2000, 2, 0, 0};
|
|
const uint16 german_pre_d[] = {128, 0};
|
|
const uint16 german_pre_d_tl[] = {0x8000, 6, 0x4000, 4, 0xa000, 4, 0x2000, 5, 0xc000, 6, 0, 0};
|
|
const uint16 german_pre_d_type[] = {158, 236, 161, 266, 280, 287, 286, 294, 264, 0};
|
|
const uint16 german_pre_d_type_tl[] = {0x8000, 3, 0x4000, 4, 0xa000, 4, 0x2000, 5, 0xc000, 6, 0, 0};
|
|
const uint16 german_pre_e_type[] = {160, 0};
|
|
const uint16 german_pre_e_type_tl[] = {0x8000, 7, 0xc000, 7, 0x4000, 8, 0xa000, 8, 0x2000, 9, 0, 0};
|
|
|
|
struct GermanLanguageArticle {
|
|
const uint16 *messageList;
|
|
const uint16 *translations;
|
|
};
|
|
|
|
const GermanLanguageArticle germanArticles[] = {
|
|
{&german_pre_k_type[0], &german_pre_k_type_tl[0]},
|
|
{&german_pre_d[0], &german_pre_d_tl[0]},
|
|
{&german_pre_d_type[0], &german_pre_d_type_tl[0]},
|
|
{&german_pre_e_type[0], &german_pre_e_type_tl[0]}
|
|
};
|
|
|
|
|
|
int TalkDialog::getArticle(uint16 msgId, uint16 objId) {
|
|
Common::Language language = LureEngine::getReference().getLanguage();
|
|
int id = objId & 0xe000;
|
|
|
|
if (language == Common::DE_DEU) {
|
|
// Special handling for German language
|
|
|
|
for (int sectionIndex = 0; sectionIndex < 4; ++sectionIndex) {
|
|
// Scan through the list of messages for this section
|
|
bool msgFound = false;
|
|
for (const uint16 *msgPtr = germanArticles[sectionIndex].messageList; *msgPtr != 0; ++msgPtr) {
|
|
msgFound = *msgPtr == msgId;
|
|
if (msgFound) break;
|
|
}
|
|
|
|
if (msgFound) {
|
|
// Scan against possible bit combinations
|
|
for (const uint16 *p = germanArticles[sectionIndex].translations; *p != 0; p += 2) {
|
|
if (*p == id)
|
|
// Return the article index to use
|
|
return *++p + 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
} else if (language == Common::ES_ESP) {
|
|
// Special handling for Spanish langugae
|
|
const uint16 *tlData = (msgId == 158) ? spanish_pre_e1_type_tl : spanish_others_tl;
|
|
|
|
// Scan through the list of article bitflag mappings
|
|
for (const uint16 *p = tlData; *p != 0; p += 2) {
|
|
if (*p == id)
|
|
// Return the article index to use
|
|
return *++p + 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
return (id >> 13) + 1;
|
|
}
|
|
|
|
void TalkDialog::vgaTalkDialog(Surface *s) {
|
|
Resources &res = Resources::getReference();
|
|
|
|
// Draw the dialog
|
|
byte *pSrc = res.getTalkDialogData().data();
|
|
byte *pDest = s->data().data();
|
|
int xPos, yPos;
|
|
|
|
// Handle the dialog top
|
|
for (yPos = 0; yPos < TALK_DIALOG_EDGE_SIZE; ++yPos) {
|
|
*pDest++ = *pSrc++;
|
|
*pDest++ = *pSrc++;
|
|
|
|
for (xPos = 0; xPos < TALK_DIALOG_WIDTH - TALK_DIALOG_EDGE_SIZE - 2; ++xPos)
|
|
*pDest++ = *pSrc;
|
|
++pSrc;
|
|
|
|
for (xPos = 0; xPos < TALK_DIALOG_EDGE_SIZE; ++xPos)
|
|
*pDest++ = *pSrc++;
|
|
}
|
|
|
|
// Handle the middle section
|
|
for (yPos = 0; yPos < _surface->height() - TALK_DIALOG_EDGE_SIZE * 2; ++yPos) {
|
|
byte *pSrcTemp = pSrc;
|
|
|
|
// Left edge
|
|
for (xPos = 0; xPos < TALK_DIALOG_EDGE_SIZE; ++xPos)
|
|
*pDest++ = *pSrcTemp++;
|
|
|
|
// Middle section
|
|
for (xPos = 0; xPos < _surface->width() - TALK_DIALOG_EDGE_SIZE * 2; ++xPos)
|
|
*pDest++ = *pSrcTemp;
|
|
++pSrcTemp;
|
|
|
|
// Right edge
|
|
for (xPos = 0; xPos < TALK_DIALOG_EDGE_SIZE; ++xPos)
|
|
*pDest++ = *pSrcTemp++;
|
|
}
|
|
|
|
// Bottom section
|
|
pSrc += TALK_DIALOG_EDGE_SIZE * 2 + 1;
|
|
for (yPos = 0; yPos < TALK_DIALOG_EDGE_SIZE; ++yPos) {
|
|
for (xPos = 0; xPos < TALK_DIALOG_EDGE_SIZE; ++xPos)
|
|
*pDest++ = *pSrc++;
|
|
|
|
for (xPos = 0; xPos < TALK_DIALOG_WIDTH - TALK_DIALOG_EDGE_SIZE - 2; ++xPos)
|
|
*pDest++ = *pSrc;
|
|
++pSrc;
|
|
|
|
*pDest++ = *pSrc++;
|
|
*pDest++ = *pSrc++;
|
|
}
|
|
}
|
|
|
|
TalkDialog::TalkDialog(uint16 characterId, uint16 destCharacterId, uint16 activeItemId, uint16 descId) {
|
|
debugC(ERROR_DETAILED, kLureDebugAnimations, "TalkDialog(chars=%xh/%xh, item=%d, str=%d",
|
|
characterId, destCharacterId, activeItemId, descId);
|
|
StringData &strings = StringData::getReference();
|
|
Resources &res = Resources::getReference();
|
|
char srcCharName[MAX_DESC_SIZE];
|
|
char destCharName[MAX_DESC_SIZE];
|
|
char itemName[MAX_DESC_SIZE];
|
|
int characterArticle = 0, hotspotArticle = 0;
|
|
bool isEGA = LureEngine::getReference().isEGA();
|
|
|
|
|
|
_characterId = characterId;
|
|
_destCharacterId = destCharacterId;
|
|
_activeItemId = activeItemId;
|
|
_descId = descId;
|
|
|
|
HotspotData *talkingChar = res.getHotspot(characterId);
|
|
HotspotData *destCharacter = (destCharacterId == 0) ? NULL :
|
|
res.getHotspot(destCharacterId);
|
|
HotspotData *itemHotspot = (activeItemId == 0) ? NULL :
|
|
res.getHotspot(activeItemId);
|
|
assert(talkingChar);
|
|
|
|
strings.getString(talkingChar->nameId & 0x1fff, srcCharName);
|
|
|
|
strcpy(destCharName, "");
|
|
if (destCharacter != NULL) {
|
|
strings.getString(destCharacter->nameId, destCharName);
|
|
characterArticle = getArticle(descId, destCharacter->nameId);
|
|
}
|
|
strcpy(itemName, "");
|
|
if (itemHotspot != NULL) {
|
|
strings.getString(itemHotspot->nameId & 0x1fff, itemName);
|
|
hotspotArticle = getArticle(descId, itemHotspot->nameId);
|
|
}
|
|
|
|
strings.getString(descId, _desc, itemName, destCharName, hotspotArticle, characterArticle);
|
|
|
|
// Apply word wrapping to figure out the needed size of the dialog
|
|
Surface::wordWrap(_desc, TALK_DIALOG_WIDTH - (TALK_DIALOG_EDGE_SIZE + 3) * 2,
|
|
_lines, _numLines);
|
|
_endLine = 0; _endIndex = 0;
|
|
|
|
debugC(ERROR_DETAILED, kLureDebugAnimations, "Creating talk dialog for %d lines", _numLines);
|
|
|
|
_surface = new Surface(TALK_DIALOG_WIDTH,
|
|
(_numLines + 1) * FONT_HEIGHT + TALK_DIALOG_EDGE_SIZE * 4);
|
|
|
|
if (isEGA)
|
|
_surface->createDialog();
|
|
else
|
|
vgaTalkDialog(_surface);
|
|
|
|
_wordCountdown = 0;
|
|
|
|
// Write out the character name
|
|
uint16 charWidth = Surface::textWidth(srcCharName);
|
|
byte white = LureEngine::getReference().isEGA() ? EGA_DIALOG_WHITE_COLOR : VGA_DIALOG_WHITE_COLOR;
|
|
_surface->writeString((TALK_DIALOG_WIDTH - charWidth) / 2, TALK_DIALOG_EDGE_SIZE + 2,
|
|
srcCharName, true, white);
|
|
debugC(ERROR_DETAILED, kLureDebugAnimations, "TalkDialog end");
|
|
}
|
|
|
|
TalkDialog::~TalkDialog() {
|
|
Memory::dealloc(_lines);
|
|
delete _surface;
|
|
}
|
|
|
|
void TalkDialog::copyTo(Surface *dest, uint16 x, uint16 y) {
|
|
if (_endLine < _numLines) {
|
|
if (_wordCountdown > 0) {
|
|
// Handle delay between words
|
|
--_wordCountdown;
|
|
|
|
} else {
|
|
// Set a delay before the next word is displayed
|
|
Game &game = Game::getReference();
|
|
_wordCountdown = game.fastTextFlag() ? 0 : 1;
|
|
|
|
// Scan forward to find the next word break
|
|
char ch = '\0';
|
|
bool wordFlag = false;
|
|
|
|
while (!wordFlag) {
|
|
ch = _lines[_endLine][++_endIndex];
|
|
wordFlag = (ch == ' ') || (ch == '\0');
|
|
}
|
|
|
|
// Write out the completed portion of the current line
|
|
_surface->writeSubstring(TALK_DIALOG_EDGE_SIZE + 2,
|
|
TALK_DIALOG_EDGE_SIZE + 4 + (_endLine + 1) * FONT_HEIGHT,
|
|
_lines[_endLine], _endIndex, true);
|
|
|
|
// If at end of line, move to next line for next time
|
|
if (ch == '\0') {
|
|
++_endLine;
|
|
_endIndex = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
_surface->copyTo(dest, x, y);
|
|
}
|
|
|
|
void TalkDialog::saveToStream(Common::WriteStream *stream) {
|
|
stream->writeUint16LE(_characterId);
|
|
stream->writeUint16LE(_destCharacterId);
|
|
stream->writeUint16LE(_activeItemId);
|
|
stream->writeUint16LE(_descId);
|
|
stream->writeSint16LE(_endLine);
|
|
stream->writeSint16LE(_endIndex);
|
|
stream->writeSint16LE(_wordCountdown);
|
|
|
|
}
|
|
|
|
TalkDialog *TalkDialog::loadFromStream(Common::ReadStream *stream) {
|
|
uint16 characterId = stream->readUint16LE();
|
|
if (characterId == 0)
|
|
return NULL;
|
|
|
|
uint16 destCharacterId = stream->readUint16LE();
|
|
uint16 activeItemId = stream->readUint16LE();
|
|
uint16 descId = stream->readUint16LE();
|
|
|
|
TalkDialog *dialog = new TalkDialog(characterId, destCharacterId, activeItemId, descId);
|
|
dialog->_endLine = stream->readSint16LE();
|
|
dialog->_endIndex = stream->readSint16LE();
|
|
dialog->_wordCountdown = stream->readSint16LE();
|
|
return dialog;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
#define SR_SEPARATOR_Y 21
|
|
#define SR_SEPARATOR_X 5
|
|
#define SR_SEPARATOR_HEIGHT 5
|
|
#define SR_SAVEGAME_NAMES_Y (SR_SEPARATOR_Y + SR_SEPARATOR_HEIGHT + 1)
|
|
|
|
|
|
void SaveRestoreDialog::toggleHightlight(int xs, int xe, int ys, int ye) {
|
|
Screen &screen = Screen::getReference();
|
|
byte *addr = screen.screen().data().data() + FULL_SCREEN_WIDTH * ys + xs;
|
|
const byte colorList[4] = {EGA_DIALOG_TEXT_COLOR, EGA_DIALOG_WHITE_COLOR,
|
|
VGA_DIALOG_TEXT_COLOR, VGA_DIALOG_WHITE_COLOR};
|
|
const byte *colors = LureEngine::getReference().isEGA() ? &colorList[0] : &colorList[2];
|
|
|
|
for (int y = 0; y < ye - ys + 1; ++y, addr += FULL_SCREEN_WIDTH) {
|
|
for (int x = 0; x < xe - xs + 1; ++x) {
|
|
if (addr[x] == colors[0]) addr[x] = colors[1];
|
|
else if (addr[x] == colors[1]) addr[x] = colors[0];
|
|
}
|
|
}
|
|
|
|
screen.update();
|
|
}
|
|
|
|
bool SaveRestoreDialog::show(bool saveDialog) {
|
|
OSystem &system = *g_system;
|
|
Screen &screen = Screen::getReference();
|
|
Mouse &mouse = Mouse::getReference();
|
|
Events &events = Events::getReference();
|
|
Resources &res = Resources::getReference();
|
|
LureEngine &engine = LureEngine::getReference();
|
|
int selectedLine = -1;
|
|
int index;
|
|
|
|
// Figure out a list of present savegames
|
|
Common::String **saveNames = (Common::String **)Memory::alloc(sizeof(Common::String *) * MAX_SAVEGAME_SLOTS);
|
|
int numSaves = 0;
|
|
while ((numSaves < MAX_SAVEGAME_SLOTS) &&
|
|
((saveNames[numSaves] = engine.detectSave(numSaves + 1)) != NULL))
|
|
++numSaves;
|
|
|
|
// For the save dialog, if all the slots have not been used up, create a
|
|
// blank entry for a new savegame
|
|
if (saveDialog && (numSaves < MAX_SAVEGAME_SLOTS))
|
|
saveNames[numSaves++] = new Common::String();
|
|
|
|
// For the restore dialog, if there are no savegames, return immediately
|
|
if (!saveDialog && (numSaves == 0)) {
|
|
Memory::dealloc(saveNames);
|
|
return false;
|
|
}
|
|
|
|
Surface *s = new Surface(INFO_DIALOG_WIDTH, SR_SAVEGAME_NAMES_Y +
|
|
numSaves * FONT_HEIGHT + FONT_HEIGHT + 2);
|
|
|
|
// Create the outer dialog and dividing line
|
|
s->createDialog();
|
|
byte *pDest = s->data().data() + (s->width() * SR_SEPARATOR_Y) + SR_SEPARATOR_X;
|
|
uint8 rowColors[5] = {*(pDest-2), *(pDest-1), *(pDest-1), *(pDest-2), *(pDest+1)};
|
|
for (int y = 0; y < SR_SEPARATOR_HEIGHT; ++y, pDest += s->width())
|
|
memset(pDest, rowColors[y], s->width() - 12);
|
|
|
|
// Create title line
|
|
Common::String title(res.stringList().getString(
|
|
saveDialog ? S_SAVE_GAME : S_RESTORE_GAME));
|
|
s->writeString((s->width() - s->textWidth(title.c_str())) / 2, FONT_HEIGHT+2, title, true);
|
|
|
|
// Write out any existing save names
|
|
for (index = 0; index < numSaves; ++index)
|
|
s->writeString(Surface::textX(), SR_SAVEGAME_NAMES_Y + (index * 8), saveNames[index]->c_str(), true);
|
|
|
|
// Display the dialog
|
|
s->copyTo(&screen.screen(), SAVE_DIALOG_X, SAVE_DIALOG_Y);
|
|
screen.update();
|
|
mouse.pushCursorNum(CURSOR_ARROW);
|
|
Sound.pause();
|
|
|
|
bool abortFlag = false;
|
|
bool doneFlag = false;
|
|
while (!abortFlag && !doneFlag) {
|
|
// Provide highlighting of lines to select a save slot
|
|
while (!abortFlag && !(mouse.lButton() && (selectedLine != -1))
|
|
&& !mouse.rButton() && !mouse.mButton()) {
|
|
abortFlag = engine.shouldQuit();
|
|
if (abortFlag) break;
|
|
|
|
while (events.pollEvent()) {
|
|
if ((events.type() == Common::EVENT_KEYDOWN) &&
|
|
(events.event().kbd.keycode == Common::KEYCODE_ESCAPE)) {
|
|
abortFlag = true;
|
|
break;
|
|
}
|
|
if (events.type() == Common::EVENT_MOUSEMOVE ||
|
|
events.type() == Common::EVENT_WHEELUP || events.type() == Common::EVENT_WHEELDOWN) {
|
|
// Mouse movement
|
|
int lineNum = 0;
|
|
|
|
if (events.type() == Common::EVENT_MOUSEMOVE) {
|
|
if ((mouse.x() < (SAVE_DIALOG_X + Surface::textX())) ||
|
|
(mouse.x() >= (SAVE_DIALOG_X + s->width() - Surface::textX())) ||
|
|
(mouse.y() < SAVE_DIALOG_Y + SR_SAVEGAME_NAMES_Y) ||
|
|
(mouse.y() >= SAVE_DIALOG_Y + SR_SAVEGAME_NAMES_Y + numSaves * FONT_HEIGHT))
|
|
// Outside displayed lines
|
|
lineNum = -1;
|
|
else
|
|
lineNum = (mouse.y() - (SAVE_DIALOG_Y + SR_SAVEGAME_NAMES_Y)) / FONT_HEIGHT;
|
|
} else if (events.type() == Common::EVENT_WHEELUP) {
|
|
if (selectedLine > 0) {
|
|
lineNum = selectedLine - 1;
|
|
}
|
|
} else if (events.type() == Common::EVENT_WHEELDOWN) {
|
|
if (selectedLine < numSaves - 1) {
|
|
lineNum = selectedLine + 1;
|
|
}
|
|
}
|
|
|
|
if (lineNum != selectedLine) {
|
|
if (selectedLine != -1)
|
|
// Deselect previously selected line
|
|
toggleHightlight(SAVE_DIALOG_X + Surface::textX(),
|
|
SAVE_DIALOG_X + s->width() - Surface::textX(),
|
|
SAVE_DIALOG_Y + SR_SAVEGAME_NAMES_Y + selectedLine * FONT_HEIGHT,
|
|
SAVE_DIALOG_Y + SR_SAVEGAME_NAMES_Y + (selectedLine + 1) * FONT_HEIGHT - 1);
|
|
|
|
// Highlight new line
|
|
selectedLine = lineNum;
|
|
if (selectedLine != -1)
|
|
toggleHightlight(SAVE_DIALOG_X + Surface::textX(),
|
|
SAVE_DIALOG_X + s->width() - Surface::textX(),
|
|
SAVE_DIALOG_Y + SR_SAVEGAME_NAMES_Y + selectedLine * FONT_HEIGHT,
|
|
SAVE_DIALOG_Y + SR_SAVEGAME_NAMES_Y + (selectedLine + 1) * FONT_HEIGHT - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
system.updateScreen();
|
|
system.delayMillis(10);
|
|
}
|
|
|
|
// Deselect selected row
|
|
if (selectedLine != -1)
|
|
toggleHightlight(SAVE_DIALOG_X + Surface::textX(),
|
|
SAVE_DIALOG_X + s->width() - Surface::textX(),
|
|
SAVE_DIALOG_Y + SR_SAVEGAME_NAMES_Y + selectedLine * FONT_HEIGHT,
|
|
SAVE_DIALOG_Y + SR_SAVEGAME_NAMES_Y + (selectedLine + 1) * FONT_HEIGHT - 1);
|
|
|
|
if (mouse.lButton() || mouse.rButton() || mouse.mButton()) {
|
|
abortFlag = mouse.rButton();
|
|
mouse.waitForRelease();
|
|
}
|
|
if (abortFlag) break;
|
|
|
|
// If in save mode, allow the entry of a new savename
|
|
if (saveDialog) {
|
|
if (!screen.screen().getString(*saveNames[selectedLine],
|
|
INFO_DIALOG_WIDTH - (Surface::textX() * 2),
|
|
false, true, SAVE_DIALOG_X + Surface::textX(),
|
|
SAVE_DIALOG_Y + SR_SAVEGAME_NAMES_Y + selectedLine * FONT_HEIGHT)) {
|
|
// Aborted out of name selection, so restore old name and
|
|
// go back to slot selection
|
|
screen.screen().writeString(
|
|
SAVE_DIALOG_X + Surface::textX(),
|
|
SAVE_DIALOG_Y + SR_SAVEGAME_NAMES_Y + selectedLine * FONT_HEIGHT,
|
|
saveNames[selectedLine]->c_str(), true);
|
|
selectedLine = -1;
|
|
continue;
|
|
}
|
|
}
|
|
doneFlag = true;
|
|
}
|
|
|
|
delete s;
|
|
Sound.resume();
|
|
|
|
int errorFlag = 0;
|
|
if (doneFlag) {
|
|
// Handle save or restore
|
|
if (saveDialog) {
|
|
doneFlag = engine.saveGame(selectedLine + 1, *saveNames[selectedLine]);
|
|
if (!doneFlag)
|
|
errorFlag = 1;
|
|
} else {
|
|
doneFlag = engine.loadGame(selectedLine + 1);
|
|
if (!doneFlag)
|
|
errorFlag = 2;
|
|
}
|
|
}
|
|
|
|
mouse.popCursor();
|
|
|
|
// Free savegame caption list
|
|
for (index = 0; index < numSaves; ++index) delete saveNames[index];
|
|
Memory::dealloc(saveNames);
|
|
|
|
if (errorFlag != 0) {
|
|
Room::getReference().update();
|
|
screen.update();
|
|
|
|
if (errorFlag == 1)
|
|
Dialog::show("Error occurred saving the game");
|
|
else if (errorFlag == 2)
|
|
Dialog::show("Error occurred loading the savegame");
|
|
}
|
|
|
|
return doneFlag;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
struct RestartRecordPos {
|
|
int16 x, y;
|
|
};
|
|
|
|
struct RestartRecord {
|
|
Common::Language Language;
|
|
int16 width, height;
|
|
RestartRecordPos BtnRestart;
|
|
RestartRecordPos BtnRestore;
|
|
};
|
|
|
|
static const RestartRecord buttonBounds[] = {
|
|
{ Common::EN_ANY, 48, 14, { 118, 152 }, { 168, 152 } },
|
|
{ Common::DE_DEU, 48, 14, { 106, 152 }, { 168, 152 } },
|
|
{ Common::UNK_LANG, 48, 14, { 112, 152 }, { 168, 152 } }
|
|
};
|
|
|
|
|
|
bool RestartRestoreDialog::show() {
|
|
Resources &res = Resources::getReference();
|
|
Events &events = Events::getReference();
|
|
Mouse &mouse = Mouse::getReference();
|
|
Screen &screen = Screen::getReference();
|
|
LureEngine &engine = LureEngine::getReference();
|
|
|
|
Sound.killSounds();
|
|
Sound.musicInterface_Play(60, 0);
|
|
mouse.setCursorNum(CURSOR_ARROW);
|
|
|
|
// See if there are any savegames that can be restored
|
|
Common::String *firstSave = engine.detectSave(1);
|
|
bool restartFlag = (firstSave == NULL);
|
|
int highlightedButton = -1;
|
|
|
|
if (!restartFlag) {
|
|
delete firstSave;
|
|
|
|
// Get the correct button bounds record to use
|
|
const RestartRecord *btnRecord = &buttonBounds[0];
|
|
while ((btnRecord->Language != engine.getLanguage()) &&
|
|
(btnRecord->Language != Common::UNK_LANG))
|
|
++btnRecord;
|
|
|
|
// Fade out the screen
|
|
screen.paletteFadeOut(RES_PALETTE_ENTRIES);
|
|
|
|
// Get the palette that will be used, and first fade out the prior screen
|
|
Palette p(RESTART_RESOURCE_ID - 1);
|
|
|
|
// Turn on the mouse
|
|
mouse.cursorOn();
|
|
|
|
// Load the restore/restart screen image
|
|
Surface *s = Surface::getScreen(RESTART_RESOURCE_ID);
|
|
s->copyTo(&screen.screen(), 0, MENUBAR_Y_SIZE);
|
|
delete s;
|
|
|
|
res.activeHotspots().clear();
|
|
Hotspot *btnHotspot = new Hotspot();
|
|
|
|
// Restart button
|
|
btnHotspot->setSize(btnRecord->width, btnRecord->height);
|
|
btnHotspot->setPosition(btnRecord->BtnRestart.x, btnRecord->BtnRestart.y);
|
|
btnHotspot->setAnimation(0x184B);
|
|
btnHotspot->copyTo(&screen.screen());
|
|
|
|
// Restore button
|
|
btnHotspot->setFrameNumber(1);
|
|
btnHotspot->setPosition(btnRecord->BtnRestore.x, btnRecord->BtnRestore.y);
|
|
btnHotspot->copyTo(&screen.screen());
|
|
|
|
screen.update();
|
|
screen.paletteFadeIn(&p);
|
|
|
|
// Event loop for making selection
|
|
bool buttonPressed = false;
|
|
|
|
while (!engine.shouldQuit()) {
|
|
// Handle events
|
|
while (events.pollEvent()) {
|
|
if ((events.type() == Common::EVENT_LBUTTONDOWN) && (highlightedButton != -1)) {
|
|
mouse.waitForRelease();
|
|
buttonPressed = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (buttonPressed)
|
|
break;
|
|
|
|
// Check if the pointer is over either button
|
|
int currentButton = -1;
|
|
if ((mouse.y() >= btnRecord->BtnRestart.y) &&
|
|
(mouse.y() < btnRecord->BtnRestart.y + btnRecord->height)) {
|
|
// Check whether the Restart or Restore button is highlighted
|
|
if ((mouse.x() >= btnRecord->BtnRestart.x) &&
|
|
(mouse.x() < btnRecord->BtnRestart.x + btnRecord->width))
|
|
currentButton = 0;
|
|
else if ((mouse.x() >= btnRecord->BtnRestore.x) &&
|
|
(mouse.x() < btnRecord->BtnRestore.x + btnRecord->width))
|
|
currentButton = 1;
|
|
}
|
|
|
|
// Take care of highlighting as the selected button changes
|
|
if (currentButton != highlightedButton) {
|
|
highlightedButton = currentButton;
|
|
|
|
// Restart button
|
|
btnHotspot->setFrameNumber((highlightedButton == 0) ? 2 : 0);
|
|
btnHotspot->setPosition(btnRecord->BtnRestart.x, btnRecord->BtnRestart.y);
|
|
btnHotspot->copyTo(&screen.screen());
|
|
|
|
// Restore button
|
|
btnHotspot->setFrameNumber((highlightedButton == 1) ? 3 : 1);
|
|
btnHotspot->setPosition(btnRecord->BtnRestore.x, btnRecord->BtnRestore.y);
|
|
btnHotspot->copyTo(&screen.screen());
|
|
}
|
|
|
|
|
|
screen.update();
|
|
g_system->delayMillis(10);
|
|
}
|
|
|
|
restartFlag = highlightedButton == 0;
|
|
delete btnHotspot;
|
|
}
|
|
|
|
Sound.killSounds();
|
|
|
|
if (!restartFlag && !engine.shouldQuit()) {
|
|
// Need to show Restore game dialog
|
|
if (!SaveRestoreDialog::show(false))
|
|
// User cancelled, so fall back on Restart
|
|
restartFlag = true;
|
|
}
|
|
|
|
return restartFlag;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
struct ItemDesc {
|
|
Common::Language language;
|
|
int16 x, y;
|
|
uint16 width, height;
|
|
uint16 animId;
|
|
uint8 startColor;
|
|
};
|
|
|
|
#define PROT_SPR_HEADER 0x1830
|
|
#define WORDING_HEADER 0x1839
|
|
#define NUMBER_HEADER 0x1842
|
|
|
|
static const ItemDesc copyProtectElements[] = {
|
|
{Common::UNK_LANG, 104, 96, 32, 48, PROT_SPR_HEADER, 0},
|
|
{Common::UNK_LANG, 179, 96, 32, 48, PROT_SPR_HEADER, 0},
|
|
|
|
{Common::EN_ANY, 57, 40, 208, 40, WORDING_HEADER, 32},
|
|
{Common::FR_FRA, 57, 40, 208, 40, WORDING_HEADER, 32},
|
|
{Common::DE_DEU, 39, 30, 240, 53, WORDING_HEADER, 32},
|
|
{Common::NL_NLD, 57, 40, 208, 40, WORDING_HEADER, 32},
|
|
{Common::ES_ESP, 57, 40, 208, 40, WORDING_HEADER, 32},
|
|
{Common::IT_ITA, 57, 40, 208, 40, WORDING_HEADER, 32},
|
|
|
|
{Common::UNK_LANG, 138, 168, 16, 8, NUMBER_HEADER, 32},
|
|
{Common::UNK_LANG, 145, 168, 16, 8, NUMBER_HEADER, 32},
|
|
{Common::UNK_LANG, 164, 168, 16, 8, NUMBER_HEADER, 32},
|
|
{Common::UNK_LANG, 171, 168, 16, 8, NUMBER_HEADER, 32},
|
|
{Common::UNK_LANG, 0, 0, 0, 0, 0, 0}
|
|
};
|
|
|
|
int pageNumbers[20] = {
|
|
4, 10, 16, 22, 5, 11, 17, 23, 6, 12, 18, 7, 13, 19, 8, 14, 20, 9, 15, 21};
|
|
|
|
CopyProtectionDialog::CopyProtectionDialog() {
|
|
// Get objects for the screen
|
|
LureEngine &engine = LureEngine::getReference();
|
|
|
|
const ItemDesc *ptr = ©ProtectElements[0];
|
|
while ((ptr->width != 0) || (ptr->height != 0)) {
|
|
if ((ptr->language == Common::UNK_LANG) || (ptr->language == engine.getLanguage())) {
|
|
Hotspot *h = new Hotspot();
|
|
h->setPosition(ptr->x, ptr->y);
|
|
h->setSize(ptr->width, ptr->height);
|
|
h->setColorOffset(ptr->startColor);
|
|
h->setAnimation(ptr->animId);
|
|
|
|
_hotspots.push_back(HotspotsList::value_type(h));
|
|
}
|
|
|
|
++ptr;
|
|
}
|
|
}
|
|
|
|
bool CopyProtectionDialog::show() {
|
|
Screen &screen = Screen::getReference();
|
|
Events &events = Events::getReference();
|
|
LureEngine &engine = LureEngine::getReference();
|
|
|
|
screen.setPaletteEmpty();
|
|
Palette p(COPY_PROTECTION_RESOURCE_ID - 1);
|
|
|
|
for (int tryCounter = 0; tryCounter < 3; ++tryCounter) {
|
|
// Copy the base screen to the output screen
|
|
Surface *s = Surface::getScreen(COPY_PROTECTION_RESOURCE_ID);
|
|
s->copyTo(&screen.screen(), 0, MENUBAR_Y_SIZE);
|
|
delete s;
|
|
|
|
// Get needed hotspots
|
|
HotspotsList::iterator hotspot0 = _hotspots.begin();
|
|
HotspotsList::iterator hotspot1 = _hotspots.begin();
|
|
for (int i = 0; i < 1; i++)
|
|
++hotspot1;
|
|
HotspotsList::iterator hotspot2 = _hotspots.begin();
|
|
for (int i = 0; i < 2; i++)
|
|
++hotspot2;
|
|
HotspotsList::iterator hotspot3 = _hotspots.begin();
|
|
for (int i = 0; i < 3; i++)
|
|
++hotspot3;
|
|
HotspotsList::iterator hotspot4 = _hotspots.begin();
|
|
for (int i = 0; i < 4; i++)
|
|
++hotspot4;
|
|
HotspotsList::iterator hotspot5 = _hotspots.begin();
|
|
for (int i = 0; i < 5; i++)
|
|
++hotspot5;
|
|
HotspotsList::iterator hotspot6 = _hotspots.begin();
|
|
for (int i = 0; i < 6; i++)
|
|
++hotspot6;
|
|
|
|
// Add wording header and display screen
|
|
(*hotspot2)->setFrameNumber(1);
|
|
(*hotspot2)->copyTo(&screen.screen());
|
|
screen.update();
|
|
screen.setPalette(&p);
|
|
|
|
// Cycle through displaying different characters until a key or mouse button is pressed
|
|
do {
|
|
chooseCharacters();
|
|
} while (!events.interruptableDelay(100));
|
|
|
|
// Change title text to selection
|
|
(*hotspot2)->setFrameNumber(0);
|
|
(*hotspot2)->copyTo(&screen.screen());
|
|
screen.update();
|
|
|
|
// Clear any prior try
|
|
_charIndex = 0;
|
|
|
|
while (!engine.shouldQuit()) {
|
|
while (events.pollEvent() && (_charIndex < 4)) {
|
|
if (events.type() == Common::EVENT_KEYDOWN) {
|
|
if ((events.event().kbd.keycode == Common::KEYCODE_BACKSPACE) && (_charIndex > 0)) {
|
|
// Remove the last number typed
|
|
--_charIndex;
|
|
HotspotsList::iterator tmpHotspot = _hotspots.begin();
|
|
for (int i = 0; i < _charIndex + 3; i++)
|
|
++tmpHotspot;
|
|
(*tmpHotspot)->setFrameNumber(10); // Blank space
|
|
(*tmpHotspot)->copyTo(&screen.screen());
|
|
|
|
screen.update();
|
|
} else if ((events.event().kbd.ascii >= '0') &&
|
|
(events.event().kbd.ascii <= '9')) {
|
|
HotspotsList::iterator tmpHotspot = _hotspots.begin();
|
|
for (int i = 0; i < _charIndex + 3; i++)
|
|
++tmpHotspot;
|
|
// Number pressed
|
|
(*tmpHotspot)->setFrameNumber(events.event().kbd.ascii - '0');
|
|
(*tmpHotspot)->copyTo(&screen.screen());
|
|
|
|
++_charIndex;
|
|
}
|
|
|
|
screen.update();
|
|
}
|
|
}
|
|
|
|
g_system->delayMillis(10);
|
|
if (_charIndex == 4)
|
|
break;
|
|
}
|
|
|
|
if (engine.shouldQuit())
|
|
return false;
|
|
|
|
// At this point, two page numbers have been entered - validate them
|
|
int page1 = ((*hotspot3)->frameNumber() * 10) + (*hotspot4)->frameNumber();
|
|
int page2 = ((*hotspot5)->frameNumber() * 10) + (*hotspot6)->frameNumber();
|
|
|
|
if ((page1 == pageNumbers[(*hotspot0)->frameNumber()]) &&
|
|
(page2 == pageNumbers[(*hotspot1)->frameNumber()]))
|
|
return true;
|
|
}
|
|
|
|
// Copy protection failed
|
|
return false;
|
|
}
|
|
|
|
void CopyProtectionDialog::chooseCharacters() {
|
|
Screen &screen = Screen::getReference();
|
|
Common::RandomSource &rnd = LureEngine::getReference().rnd();
|
|
int char1 = rnd.getRandomNumber(19);
|
|
int char2 = rnd.getRandomNumber(19);
|
|
|
|
HotspotsList::iterator curHotspot = _hotspots.begin();
|
|
(*curHotspot)->setFrameNumber(char1);
|
|
(*curHotspot)->copyTo(&screen.screen());
|
|
++curHotspot;
|
|
(*curHotspot)->setFrameNumber(char2);
|
|
(*curHotspot)->copyTo(&screen.screen());
|
|
|
|
screen.update();
|
|
}
|
|
|
|
} // End of namespace Lure
|