1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "lure/decode.h"
24 #include "lure/events.h"
25 #include "lure/game.h"
26 #include "lure/lure.h"
27 #include "lure/room.h"
28 #include "lure/screen.h"
29 #include "lure/sound.h"
30 #include "lure/strings.h"
31 #include "lure/surface.h"
32 #include "common/endian.h"
33 #include "common/config-manager.h"
34 #include "common/text-to-speech.h"
35
36 namespace Lure {
37
38 // These variables hold resources commonly used by the Surfaces, and must be initialized and freed
39 // by the static Surface methods initialize and deinitailse
40
41 static MemoryBlock *int_font = NULL;
42 static MemoryBlock *int_dialog_frame = NULL;
43 static uint8 fontSize[256];
44 static int numFontChars;
45
46 static const byte char8A[8] = {0x40, 0x20, 0x00, 0x90, 0x90, 0x90, 0x68, 0x00}; // accented `u
47 static const byte char8D[8] = {0x80, 0x40, 0x00, 0xc0, 0x40, 0x40, 0x60, 0x00}; // accented `i
48 static const byte char95[8] = {0x40, 0x20, 0x00, 0x60, 0x90, 0x90, 0x60, 0x00}; // accented `o
49
initialize()50 void Surface::initialize() {
51 Disk &disk = Disk::getReference();
52 int_font = disk.getEntry(FONT_RESOURCE_ID);
53 int_dialog_frame = disk.getEntry(DIALOG_RESOURCE_ID);
54
55 if (LureEngine::getReference().getLanguage() == Common::IT_ITA) {
56 Common::copy(&char8A[0], &char8A[8], int_font->data() + (0x8A - 32) * 8);
57 Common::copy(&char8D[0], &char8D[8], int_font->data() + (0x8D - 32) * 8);
58 Common::copy(&char95[0], &char95[8], int_font->data() + (0x95 - 32) * 8);
59 }
60
61 numFontChars = int_font->size() / 8;
62 if (numFontChars > 256)
63 error("Font data exceeded maximum allowable size");
64
65 // Calculate the size of each font character
66 for (int ctr = 0; ctr < numFontChars; ++ctr) {
67 byte *pChar = int_font->data() + (ctr * 8);
68 fontSize[ctr] = 0;
69
70 for (int yp = 0; yp < FONT_HEIGHT; ++yp) {
71 byte v = *pChar++;
72
73 for (int xp = 0; xp < FONT_WIDTH; ++xp) {
74 if ((v & 0x80) && (xp > fontSize[ctr]))
75 fontSize[ctr] = xp;
76 v = (v << 1) & 0xff;
77 }
78 }
79
80 // If character is empty, like for a space, give a default size
81 if (fontSize[ctr] == 0) fontSize[ctr] = 2;
82 }
83 }
84
deinitialize()85 void Surface::deinitialize() {
86 delete int_font;
87 delete int_dialog_frame;
88 }
89
90 /*--------------------------------------------------------------------------*/
91
Surface(MemoryBlock * src,uint16 wdth,uint16 hght)92 Surface::Surface(MemoryBlock *src, uint16 wdth, uint16 hght): _data(src),
93 _width(wdth), _height(hght) {
94 if ((uint32) (wdth * hght) != src->size())
95 error("Surface dimensions do not match size of passed data");
96 }
97
Surface(uint16 wdth,uint16 hght)98 Surface::Surface(uint16 wdth, uint16 hght): _data(Memory::allocate(wdth*hght)),
99 _width(wdth), _height(hght) {
100 }
101
~Surface()102 Surface::~Surface() {
103 delete _data;
104 }
105
106 // textX / textY
107 // Returns the offset into a dialog for writing text
108
textX()109 uint16 Surface::textX() { return LureEngine::getReference().isEGA() ? 10 : 12; }
110
textY()111 uint16 Surface::textY() { return LureEngine::getReference().isEGA() ? 8 : 12; }
112
113 // getDialogBounds
114 // Returns a suggested size for a dialog given a number of horizontal characters and rows
115
getDialogBounds(Common::Point & size,int charWidth,int numLines,bool squashedLines)116 void Surface::getDialogBounds(Common::Point &size, int charWidth, int numLines, bool squashedLines) {
117 size.x = Surface::textX() * 2 + FONT_WIDTH * charWidth;
118 size.y = Surface::textY() * 2 + (squashedLines ? (FONT_HEIGHT - 1) : FONT_HEIGHT) * numLines;
119 }
120
121 // egaCreateDialog
122 // Forms a dialog encompassing the entire surface
123
egaCreateDialog(bool blackFlag)124 void Surface::egaCreateDialog(bool blackFlag) {
125 byte lineColors1[3] = {6, 0, 9};
126 byte lineColors2[3] = {7, 0, 12};
127
128 // Surface contents
129 data().setBytes(blackFlag ? 0 : EGA_DIALOG_BG_COLOR, 0, data().size());
130
131 // Top/bottom lines
132 for (int y = 2; y >= 0; --y) {
133 data().setBytes(lineColors1[y], y * width(), width());
134 data().setBytes(lineColors2[y], (height() - y - 1) * width(), width());
135
136 for (int p = y + 1; p < height() - y; ++p) {
137 byte *line = data().data() + p * width();
138 *(line + y) = lineColors2[y];
139 *(line + width() - y - 1) = lineColors1[y];
140 }
141 }
142 }
143
144 // vgaCreateDialog
145 // Forms a dialog encompassing the entire surface
146
copyLine(byte * pSrc,byte * pDest,uint16 leftSide,uint16 center,uint16 rightSide)147 void copyLine(byte *pSrc, byte *pDest, uint16 leftSide, uint16 center, uint16 rightSide) {
148 // Left area
149 memcpy(pDest, pSrc, leftSide);
150 pSrc += leftSide; pDest += leftSide;
151 // Center area
152 memset(pDest, *pSrc, center);
153 ++pSrc; pDest += center;
154 // Right side
155 memcpy(pDest, pSrc, rightSide);
156 pSrc += rightSide; pDest += rightSide;
157 }
158
159 #define VGA_DIALOG_EDGE_WIDTH 9
160
vgaCreateDialog(bool blackFlag)161 void Surface::vgaCreateDialog(bool blackFlag) {
162 byte *pSrc = int_dialog_frame->data();
163 byte *pDest = _data->data();
164 uint16 xCenter = _width - VGA_DIALOG_EDGE_WIDTH * 2;
165 uint16 yCenter = _height - VGA_DIALOG_EDGE_WIDTH * 2;
166 int y;
167
168 // Dialog top
169 for (y = 0; y < 9; ++y) {
170 copyLine(pSrc, pDest, VGA_DIALOG_EDGE_WIDTH - 2, xCenter + 2, VGA_DIALOG_EDGE_WIDTH);
171 pSrc += (VGA_DIALOG_EDGE_WIDTH - 2) + 1 + VGA_DIALOG_EDGE_WIDTH;
172 pDest += _width;
173 }
174
175 // Dialog sides - note that the same source data gets used for all side lines
176 for (y = 0; y < yCenter; ++y) {
177 copyLine(pSrc, pDest, VGA_DIALOG_EDGE_WIDTH, xCenter, VGA_DIALOG_EDGE_WIDTH);
178 pDest += _width;
179 }
180 pSrc += VGA_DIALOG_EDGE_WIDTH * 2 + 1;
181
182 // Dialog bottom
183 for (y = 0; y < 9; ++y) {
184 copyLine(pSrc, pDest, VGA_DIALOG_EDGE_WIDTH, xCenter + 1, VGA_DIALOG_EDGE_WIDTH - 1);
185 pSrc += VGA_DIALOG_EDGE_WIDTH + 1 + (VGA_DIALOG_EDGE_WIDTH - 1);
186 pDest += _width;
187 }
188
189 // Final processing - if black flag set, clear dialog inside area
190 if (blackFlag) {
191 Common::Rect r = Common::Rect(VGA_DIALOG_EDGE_WIDTH, VGA_DIALOG_EDGE_WIDTH,
192 _width - VGA_DIALOG_EDGE_WIDTH, _height-VGA_DIALOG_EDGE_WIDTH);
193 fillRect(r, 0);
194 }
195 }
196
loadScreen(uint16 resourceId)197 void Surface::loadScreen(uint16 resourceId) {
198 MemoryBlock *rawData = Disk::getReference().getEntry(resourceId);
199 loadScreen(rawData);
200 delete rawData;
201 }
202
loadScreen(MemoryBlock * rawData)203 void Surface::loadScreen(MemoryBlock *rawData) {
204 PictureDecoder decoder;
205 uint16 v = READ_BE_UINT16(rawData->data());
206 bool is5Bit = (v & 0xfffe) == 0x140;
207 MemoryBlock *tmpScreen;
208
209 if (is5Bit)
210 // 5-bit decompression
211 tmpScreen = decoder.egaDecode(rawData, FULL_SCREEN_HEIGHT * FULL_SCREEN_WIDTH + 1);
212 else
213 // VGA decompression
214 tmpScreen = decoder.vgaDecode(rawData, FULL_SCREEN_HEIGHT * FULL_SCREEN_WIDTH + 1);
215
216 empty();
217 _data->copyFrom(tmpScreen, 0, MENUBAR_Y_SIZE * FULL_SCREEN_WIDTH,
218 (FULL_SCREEN_HEIGHT - MENUBAR_Y_SIZE) * FULL_SCREEN_WIDTH);
219 delete tmpScreen;
220 }
221
writeChar(uint16 x,uint16 y,uint8 ascii,bool transparent,int color)222 int Surface::writeChar(uint16 x, uint16 y, uint8 ascii, bool transparent, int color) {
223 byte *const addr = _data->data() + (y * _width) + x;
224 if (color == DEFAULT_TEXT_COLOR)
225 color = LureEngine::getReference().isEGA() ? EGA_DIALOG_TEXT_COLOR : VGA_DIALOG_TEXT_COLOR;
226
227 if ((ascii < 32) || (ascii >= 32 + numFontChars))
228 error("Invalid ascii character passed for display '%d'", ascii);
229
230 uint8 v;
231 byte *pFont = int_font->data() + ((ascii - 32) * 8);
232 byte *pDest;
233 uint8 charWidth = 0;
234
235 for (int y1 = 0; y1 < 8; ++y1) {
236 v = *pFont++;
237 pDest = addr + (y1 * _width);
238
239 for (int x1 = 0; x1 < 8; ++x1, ++pDest) {
240 if (v & 0x80) {
241 *pDest = color;
242 if (x1+1 > charWidth) charWidth = x1 + 1;
243 }
244 else if (!transparent) *pDest = 0;
245 v = (v << 1) & 0xff;
246 }
247 }
248
249 return charWidth;
250 }
251
writeString(uint16 x,uint16 y,Common::String line,bool transparent,int color,bool varLength)252 void Surface::writeString(uint16 x, uint16 y, Common::String line, bool transparent,
253 int color, bool varLength) {
254 writeSubstring(x, y, line, line.size(), transparent, color, varLength);
255 }
256
writeSubstring(uint16 x,uint16 y,Common::String line,int len,bool transparent,int color,bool varLength)257 void Surface::writeSubstring(uint16 x, uint16 y, Common::String line, int len,
258 bool transparent, int color, bool varLength) {
259
260 const char *sPtr = line.c_str();
261 if (color == DEFAULT_TEXT_COLOR)
262 color = LureEngine::getReference().isEGA() ? EGA_DIALOG_TEXT_COLOR : VGA_DIALOG_TEXT_COLOR;
263
264 for (int index = 0; (index < len) && (*sPtr != '\0'); ++index, ++sPtr) {
265 int charSize = varLength ? fontSize[(uint8)*sPtr - 32] + 2 : FONT_WIDTH;
266 if (x + charSize >= width())
267 // Passed the right hand edge of the surface
268 break;
269
270 // Write next character
271 writeChar(x, y, (uint8) *sPtr, transparent, color);
272
273 // Move to after the character in preparation for the next character
274 x += charSize;
275 }
276 }
277
transparentCopyTo(Surface * dest)278 void Surface::transparentCopyTo(Surface *dest) {
279 if (dest->width() != _width)
280 error("Incompatible surface sizes for transparent copy");
281
282 byte *pSrc = _data->data();
283 byte *pDest = dest->data().data();
284 uint16 numBytes = MIN(_height,dest->height()) * FULL_SCREEN_WIDTH;
285
286 while (numBytes-- > 0) {
287 if (*pSrc) *pDest = *pSrc;
288
289 ++pSrc;
290 ++pDest;
291 }
292 }
293
copyTo(Surface * dest)294 void Surface::copyTo(Surface *dest) {
295 copyTo(dest, 0, 0);
296 }
297
copyTo(Surface * dest,uint16 x,uint16 y)298 void Surface::copyTo(Surface *dest, uint16 x, uint16 y) {
299 if ((x == 0) && (dest->width() == _width)) {
300 // Use fast data transfer
301 uint32 dataSize = dest->data().size() - (y * _width);
302 if (dataSize > _data->size()) dataSize = _data->size();
303 dest->data().copyFrom(_data, 0, y * _width, dataSize);
304 } else {
305 // Use slower transfer
306 Common::Rect rect;
307 rect.left = 0; rect.top = 0;
308 rect.right = _width-1; rect.bottom = _height-1;
309 copyTo(dest, rect, x, y);
310 }
311 }
312
copyTo(Surface * dest,const Common::Rect & srcBounds,uint16 destX,uint16 destY,int transparentColor)313 void Surface::copyTo(Surface *dest, const Common::Rect &srcBounds,
314 uint16 destX, uint16 destY, int transparentColor) {
315 int numBytes = srcBounds.right - srcBounds.left + 1;
316 if (destX + numBytes > dest->width())
317 numBytes = dest->width() - destX;
318 if (numBytes <= 0) return;
319
320 for (uint16 y=0; y<=(srcBounds.bottom-srcBounds.top); ++y) {
321 const uint32 srcPos = (srcBounds.top + y) * _width + srcBounds.left;
322 const uint32 destPos = (destY+y) * dest->width() + destX;
323
324 if (transparentColor == -1) {
325 // No trnnsparent color, so copy all the bytes of the line
326 dest->data().copyFrom(_data, srcPos, destPos, numBytes);
327 } else {
328 byte *pSrc = _data->data() + srcPos;
329 byte *pDest = dest->data().data() + destPos;
330
331 int bytesCtr = numBytes;
332 while (bytesCtr-- > 0) {
333 if (*pSrc != (uint8) transparentColor)
334 *pDest = *pSrc;
335 ++pSrc;
336 ++pDest;
337 }
338 }
339 }
340 }
341
copyFrom(MemoryBlock * src,uint32 destOffset)342 void Surface::copyFrom(MemoryBlock *src, uint32 destOffset) {
343 uint32 size = _data->size() - destOffset;
344 if (src->size() > size) size = src->size();
345 _data->copyFrom(src, 0, destOffset, size);
346 }
347
348 // fillRect
349 // Fills a rectangular area with a color
350
fillRect(const Common::Rect & r,uint8 color)351 void Surface::fillRect(const Common::Rect &r, uint8 color) {
352 for (int yp = r.top; yp <= r.bottom; ++yp) {
353 byte *const addr = _data->data() + (yp * _width) + r.left;
354 memset(addr, color, r.width());
355 }
356 }
357
createDialog(bool blackFlag)358 void Surface::createDialog(bool blackFlag) {
359 if (LureEngine::getReference().isEGA())
360 egaCreateDialog(blackFlag);
361 else
362 vgaCreateDialog(blackFlag);
363 }
364
copyToScreen(uint16 x,uint16 y)365 void Surface::copyToScreen(uint16 x, uint16 y) {
366 OSystem &system = *g_system;
367 system.copyRectToScreen(_data->data(), _width, x, y, _width, _height);
368 system.updateScreen();
369 }
370
centerOnScreen()371 void Surface::centerOnScreen() {
372 OSystem &system = *g_system;
373
374 system.copyRectToScreen(_data->data(), _width,
375 (FULL_SCREEN_WIDTH - _width) / 2, (FULL_SCREEN_HEIGHT - _height) / 2,
376 _width, _height);
377 system.updateScreen();
378 }
379
textWidth(const char * s,int numChars)380 uint16 Surface::textWidth(const char *s, int numChars) {
381 uint16 result = 0;
382 if (numChars == 0) numChars = strlen(s);
383
384 while (numChars-- > 0) {
385 uint8 charIndex = (uint8)*s++ - 32;
386 assert(charIndex < numFontChars);
387 result += fontSize[charIndex] + 2;
388 }
389
390 return result;
391 }
392
wordWrap(char * text,uint16 width,char ** & lines,uint8 & numLines)393 void Surface::wordWrap(char *text, uint16 width, char **&lines, uint8 &numLines) {
394 debugC(ERROR_INTERMEDIATE, kLureDebugStrings, "wordWrap(text=%s, width=%d", text, width);
395 numLines = 1;
396 uint16 lineWidth = 0;
397 char *s;
398 bool newLine;
399
400 s = text;
401
402 // Scan through the text and insert NULLs to break the line into allowable widths
403
404 while (*s != '\0') {
405 char *wordStart = s;
406 while (*wordStart == ' ') ++wordStart;
407 char *wordEnd = strchr(wordStart, ' ');
408 char *wordEnd2 = strchr(wordStart, '\n');
409 if ((!wordEnd) || ((wordEnd2) && (wordEnd2 < wordEnd))) {
410 wordEnd = wordEnd2;
411 newLine = (wordEnd2 != NULL);
412 } else {
413 newLine = false;
414 }
415
416 debugC(ERROR_DETAILED, kLureDebugStrings, "word scanning: start=%xh, after=%xh, newLine=%d",
417 (uint32)(wordStart - text), (uint32)((wordEnd == NULL) ? -1 : wordEnd - text), newLine ? 1 : 0);
418
419 if (wordEnd) {
420 if (*wordEnd != '\0') --wordEnd;
421 } else {
422 wordEnd = strchr(wordStart, '\0') - 1;
423 }
424
425 int wordBytes = (int)(wordEnd - s + 1);
426 uint16 wordSize = (wordBytes == 0) ? 0 : textWidth(s, wordBytes);
427 if (gDebugLevel >= ERROR_DETAILED) {
428 char wordBuffer[MAX_DESC_SIZE];
429 strncpy(wordBuffer, wordStart, wordBytes);
430 wordBuffer[wordBytes] = '\0';
431 debugC(ERROR_DETAILED, kLureDebugStrings, "word='%s', size=%d", wordBuffer, wordSize);
432 }
433
434 if (lineWidth + wordSize > width) {
435 // Break word onto next line
436 *(wordStart - 1) = '\0';
437 ++numLines;
438 lineWidth = 0;
439 wordEnd = wordStart - 1;
440 } else if (newLine) {
441 // Break on newline
442 ++numLines;
443 *++wordEnd = '\0';
444 lineWidth = 0;
445 } else {
446 // Add word's length to total for line
447 lineWidth += wordSize;
448 }
449
450 s = wordEnd+1;
451 }
452
453 // Set up a list for the start of each line
454 lines = (char **) Memory::alloc(sizeof(char *) * numLines);
455 lines[0] = text;
456 debugC(ERROR_DETAILED, kLureDebugStrings, "wordWrap lines[0]='%s'", lines[0]);
457 for (int ctr = 1; ctr < numLines; ++ctr) {
458 lines[ctr] = strchr(lines[ctr-1], 0) + 1;
459 debugC(ERROR_DETAILED, kLureDebugStrings, "wordWrap lines[%d]='%s'", ctr, lines[ctr]);
460 }
461
462 debugC(ERROR_INTERMEDIATE, kLureDebugStrings, "wordWrap end - numLines=%d", numLines);
463 }
464
newDialog(uint16 width,uint8 numLines,const char ** lines,bool varLength,int color,bool squashedLines)465 Surface *Surface::newDialog(uint16 width, uint8 numLines, const char **lines, bool varLength,
466 int color, bool squashedLines) {
467 Common::Point size;
468 Surface::getDialogBounds(size, 0, numLines, squashedLines);
469
470 Surface *s = new Surface(width, size.y);
471 s->createDialog();
472 Common::String text;
473
474 uint16 yP = Surface::textY();
475 for (uint8 ctr = 0; ctr < numLines; ++ctr) {
476 text += lines[ctr];
477 s->writeString(Surface::textX(), yP, lines[ctr], true, color, varLength);
478 yP += squashedLines ? FONT_HEIGHT - 1 : FONT_HEIGHT;
479 }
480
481 if (ConfMan.getBool("tts_narrator")) {
482 Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
483 if (ttsMan != nullptr) {
484 ttsMan->stop();
485 ttsMan->say(text.c_str());
486 }
487 }
488
489 return s;
490 }
491
newDialog(uint16 width,const char * line,int color)492 Surface *Surface::newDialog(uint16 width, const char *line, int color) {
493 char **lines;
494 Common::String lineCopy(line);
495 uint8 numLines;
496 wordWrap(lineCopy.begin(), width - (Surface::textX() * 2), lines, numLines);
497
498 // Create the dialog
499 Surface *result = newDialog(width, numLines, const_cast<const char **>(lines), true, color);
500
501 // Deallocate used resources
502 free(lines);
503
504 return result;
505 }
506
getScreen(uint16 resourceId)507 Surface *Surface::getScreen(uint16 resourceId) {
508 MemoryBlock *block = Disk::getReference().getEntry(resourceId);
509 PictureDecoder d;
510 MemoryBlock *decodedData = d.decode(block);
511 delete block;
512 return new Surface(decodedData, FULL_SCREEN_WIDTH, decodedData->size() / FULL_SCREEN_WIDTH);
513 }
514
getString(Common::String & line,int maxSize,bool isNumeric,bool varLength,int16 x,int16 y)515 bool Surface::getString(Common::String &line, int maxSize, bool isNumeric, bool varLength, int16 x, int16 y) {
516 OSystem &system = *g_system;
517 LureEngine &engine = LureEngine::getReference();
518 Mouse &mouse = Mouse::getReference();
519 Events &events = Events::getReference();
520 Screen &screen = Screen::getReference();
521 uint8 bgColor = *(screen.screen().data().data() + (y * FULL_SCREEN_WIDTH) + x);
522 Common::String newLine(line);
523 bool abortFlag = false;
524 bool refreshFlag = false;
525
526 bool vKbdFlag = g_system->hasFeature(OSystem::kFeatureVirtualKeyboard);
527 if (!vKbdFlag)
528 mouse.cursorOff();
529 else
530 g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
531
532
533 // Insert a cursor character at the end of the string
534 newLine.insertChar('_', newLine.size());
535
536 while (!abortFlag) {
537 // Display the string
538 screen.screen().writeString(x, y, newLine, true, DEFAULT_TEXT_COLOR, varLength);
539 screen.update();
540 int stringSize = textWidth(newLine.c_str());
541
542 // Loop until the input string changes
543 refreshFlag = false;
544 while (!refreshFlag && !abortFlag) {
545 abortFlag = engine.shouldQuit();
546 if (abortFlag) break;
547
548 while (events.pollEvent()) {
549 if (events.type() == Common::EVENT_KEYDOWN) {
550 char ch = events.event().kbd.ascii;
551 uint16 keycode = events.event().kbd.keycode;
552
553 if ((keycode == Common::KEYCODE_RETURN) || (keycode == Common::KEYCODE_KP_ENTER)) {
554 // Return character
555 screen.screen().fillRect(
556 Common::Rect(x, y, x + maxSize - 1, y + FONT_HEIGHT), bgColor);
557 screen.update();
558 newLine.deleteLastChar();
559 line = newLine;
560 if (!vKbdFlag)
561 mouse.cursorOn();
562 return true;
563 }
564 else if (keycode == Common::KEYCODE_ESCAPE) {
565 // Escape character
566 screen.screen().fillRect(
567 Common::Rect(x, y, x + maxSize - 1, y + FONT_HEIGHT), bgColor);
568 screen.update();
569 abortFlag = true;
570 } else if (keycode == Common::KEYCODE_BACKSPACE) {
571 // Delete the last character
572 if (newLine.size() == 1) continue;
573
574 screen.screen().fillRect(
575 Common::Rect(x, y, x + maxSize - 1, y + FONT_HEIGHT), bgColor);
576 newLine.deleteChar(newLine.size() - 2);
577 refreshFlag = true;
578
579 } else if ((ch >= ' ') && (stringSize + 8 < maxSize)) {
580 if (((ch >= '0') && (ch <= '9')) || !isNumeric) {
581 screen.screen().fillRect(
582 Common::Rect(x, y, x + maxSize - 1, y + FONT_HEIGHT), bgColor);
583 newLine.insertChar(ch, newLine.size() - 1);
584 refreshFlag = true;
585 }
586 }
587 }
588 }
589
590 system.updateScreen();
591 system.delayMillis(10);
592 }
593 }
594
595 if (!vKbdFlag)
596 mouse.cursorOn();
597 else
598 g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
599
600 return false;
601 }
602
603
604 /*--------------------------------------------------------------------------*/
605
show(const char * text)606 void Dialog::show(const char *text) {
607 debugC(ERROR_BASIC, kLureDebugStrings, "Dialog::show text=%s", text);
608 Screen &screen = Screen::getReference();
609 Mouse &mouse = Mouse::getReference();
610 Room &room = Room::getReference();
611 mouse.cursorOff();
612
613 room.update();
614 debugC(ERROR_DETAILED, kLureDebugStrings, "Dialog::show creating dialog");
615 Surface *s = Surface::newDialog(INFO_DIALOG_WIDTH, text);
616 debugC(ERROR_DETAILED, kLureDebugStrings, "Dialog::show created dialog");
617 s->copyToScreen(INFO_DIALOG_X, INFO_DIALOG_Y);
618 debugC(ERROR_DETAILED, kLureDebugStrings, "Dialog::show copied to screen");
619
620 // Wait for a keypress or mouse button
621 Events::getReference().waitForPress();
622
623 screen.update();
624 mouse.cursorOn();
625
626 delete s;
627 }
628
show(uint16 stringId,const char * hotspotName,const char * characterName)629 void Dialog::show(uint16 stringId, const char *hotspotName, const char *characterName) {
630 debugC(ERROR_BASIC, kLureDebugStrings, "Hotspot::showMessage stringId=%xh hotspot=%s, character=%s",
631 stringId, hotspotName, characterName);
632 char buffer[MAX_DESC_SIZE];
633 StringData &sl = StringData::getReference();
634
635 sl.getString(stringId, buffer, hotspotName, characterName);
636 show(buffer);
637 }
638
show(uint16 stringId)639 void Dialog::show(uint16 stringId) {
640 show(stringId, NULL, NULL);
641 }
642
643 /*--------------------------------------------------------------------------*/
644
645 const uint16 spanish_pre_e1_type_tl[] = {0x8000, 4, 0x4000, 5, 0x2000, 6, 0xc000, 7, 0, 0};
646 const uint16 spanish_others_tl[] = {0x8000, 0, 0x4000, 1, 0x2000, 2, 0xc000, 3, 0, 0};
647
648 const uint16 german_pre_k_type[] = {106, 0};
649 const uint16 german_pre_k_type_tl[] = {0x8000, 0, 0xc000, 0, 0x4000, 1, 0xa000, 1, 0x2000, 2, 0, 0};
650 const uint16 german_pre_d[] = {128, 0};
651 const uint16 german_pre_d_tl[] = {0x8000, 6, 0x4000, 4, 0xa000, 4, 0x2000, 5, 0xc000, 6, 0, 0};
652 const uint16 german_pre_d_type[] = {158, 236, 161, 266, 280, 287, 286, 294, 264, 0};
653 const uint16 german_pre_d_type_tl[] = {0x8000, 3, 0x4000, 4, 0xa000, 4, 0x2000, 5, 0xc000, 6, 0, 0};
654 const uint16 german_pre_e_type[] = {160, 0};
655 const uint16 german_pre_e_type_tl[] = {0x8000, 7, 0xc000, 7, 0x4000, 8, 0xa000, 8, 0x2000, 9, 0, 0};
656
657 struct GermanLanguageArticle {
658 const uint16 *messageList;
659 const uint16 *translations;
660 };
661
662 const GermanLanguageArticle germanArticles[] = {
663 {&german_pre_k_type[0], &german_pre_k_type_tl[0]},
664 {&german_pre_d[0], &german_pre_d_tl[0]},
665 {&german_pre_d_type[0], &german_pre_d_type_tl[0]},
666 {&german_pre_e_type[0], &german_pre_e_type_tl[0]}
667 };
668
669
getArticle(uint16 msgId,uint16 objId)670 int TalkDialog::getArticle(uint16 msgId, uint16 objId) {
671 Common::Language language = LureEngine::getReference().getLanguage();
672 int id = objId & 0xe000;
673
674 if (language == Common::DE_DEU) {
675 // Special handling for German language
676
677 for (int sectionIndex = 0; sectionIndex < 4; ++sectionIndex) {
678 // Scan through the list of messages for this section
679 bool msgFound = false;
680 for (const uint16 *msgPtr = germanArticles[sectionIndex].messageList; *msgPtr != 0; ++msgPtr) {
681 msgFound = *msgPtr == msgId;
682 if (msgFound) break;
683 }
684
685 if (msgFound) {
686 // Scan against possible bit combinations
687 for (const uint16 *p = germanArticles[sectionIndex].translations; *p != 0; p += 2) {
688 if (*p == id)
689 // Return the article index to use
690 return *++p + 1;
691 }
692
693 return 0;
694 }
695 }
696
697
698 return 0;
699
700 } else if (language == Common::ES_ESP) {
701 // Special handling for Spanish langugae
702 const uint16 *tlData = (msgId == 158) ? spanish_pre_e1_type_tl : spanish_others_tl;
703
704 // Scan through the list of article bitflag mappings
705 for (const uint16 *p = tlData; *p != 0; p += 2) {
706 if (*p == id)
707 // Return the article index to use
708 return *++p + 1;
709 }
710
711 return 0;
712 }
713
714 return (id >> 13) + 1;
715 }
716
vgaTalkDialog(Surface * s)717 void TalkDialog::vgaTalkDialog(Surface *s) {
718 Resources &res = Resources::getReference();
719
720 // Draw the dialog
721 byte *pSrc = res.getTalkDialogData().data();
722 byte *pDest = s->data().data();
723 int xPos, yPos;
724
725 // Handle the dialog top
726 for (yPos = 0; yPos < TALK_DIALOG_EDGE_SIZE; ++yPos) {
727 *pDest++ = *pSrc++;
728 *pDest++ = *pSrc++;
729
730 for (xPos = 0; xPos < TALK_DIALOG_WIDTH - TALK_DIALOG_EDGE_SIZE - 2; ++xPos)
731 *pDest++ = *pSrc;
732 ++pSrc;
733
734 for (xPos = 0; xPos < TALK_DIALOG_EDGE_SIZE; ++xPos)
735 *pDest++ = *pSrc++;
736 }
737
738 // Handle the middle section
739 for (yPos = 0; yPos < _surface->height() - TALK_DIALOG_EDGE_SIZE * 2; ++yPos) {
740 byte *pSrcTemp = pSrc;
741
742 // Left edge
743 for (xPos = 0; xPos < TALK_DIALOG_EDGE_SIZE; ++xPos)
744 *pDest++ = *pSrcTemp++;
745
746 // Middle section
747 for (xPos = 0; xPos < _surface->width() - TALK_DIALOG_EDGE_SIZE * 2; ++xPos)
748 *pDest++ = *pSrcTemp;
749 ++pSrcTemp;
750
751 // Right edge
752 for (xPos = 0; xPos < TALK_DIALOG_EDGE_SIZE; ++xPos)
753 *pDest++ = *pSrcTemp++;
754 }
755
756 // Bottom section
757 pSrc += TALK_DIALOG_EDGE_SIZE * 2 + 1;
758 for (yPos = 0; yPos < TALK_DIALOG_EDGE_SIZE; ++yPos) {
759 for (xPos = 0; xPos < TALK_DIALOG_EDGE_SIZE; ++xPos)
760 *pDest++ = *pSrc++;
761
762 for (xPos = 0; xPos < TALK_DIALOG_WIDTH - TALK_DIALOG_EDGE_SIZE - 2; ++xPos)
763 *pDest++ = *pSrc;
764 ++pSrc;
765
766 *pDest++ = *pSrc++;
767 *pDest++ = *pSrc++;
768 }
769 }
770
TalkDialog(uint16 characterId,uint16 destCharacterId,uint16 activeItemId,uint16 descId)771 TalkDialog::TalkDialog(uint16 characterId, uint16 destCharacterId, uint16 activeItemId, uint16 descId) {
772 debugC(ERROR_DETAILED, kLureDebugAnimations, "TalkDialog(chars=%xh/%xh, item=%d, str=%d",
773 characterId, destCharacterId, activeItemId, descId);
774 StringData &strings = StringData::getReference();
775 Resources &res = Resources::getReference();
776 char srcCharName[MAX_DESC_SIZE];
777 char destCharName[MAX_DESC_SIZE];
778 char itemName[MAX_DESC_SIZE];
779 int characterArticle = 0, hotspotArticle = 0;
780 bool isEGA = LureEngine::getReference().isEGA();
781
782
783 _characterId = characterId;
784 _destCharacterId = destCharacterId;
785 _activeItemId = activeItemId;
786 _descId = descId;
787
788 HotspotData *talkingChar = res.getHotspot(characterId);
789 HotspotData *destCharacter = (destCharacterId == 0) ? NULL :
790 res.getHotspot(destCharacterId);
791 HotspotData *itemHotspot = (activeItemId == 0) ? NULL :
792 res.getHotspot(activeItemId);
793 assert(talkingChar);
794
795 strings.getString(talkingChar->nameId & 0x1fff, srcCharName);
796
797 strcpy(destCharName, "");
798 if (destCharacter != NULL) {
799 strings.getString(destCharacter->nameId, destCharName);
800 characterArticle = getArticle(descId, destCharacter->nameId);
801 }
802 strcpy(itemName, "");
803 if (itemHotspot != NULL) {
804 strings.getString(itemHotspot->nameId & 0x1fff, itemName);
805 hotspotArticle = getArticle(descId, itemHotspot->nameId);
806 }
807
808 strings.getString(descId, _desc, itemName, destCharName, hotspotArticle, characterArticle);
809
810 // Apply word wrapping to figure out the needed size of the dialog
811 Surface::wordWrap(_desc, TALK_DIALOG_WIDTH - (TALK_DIALOG_EDGE_SIZE + 3) * 2,
812 _lines, _numLines);
813 _endLine = 0; _endIndex = 0;
814
815 debugC(ERROR_DETAILED, kLureDebugAnimations, "Creating talk dialog for %d lines", _numLines);
816
817 _surface = new Surface(TALK_DIALOG_WIDTH,
818 (_numLines + 1) * FONT_HEIGHT + TALK_DIALOG_EDGE_SIZE * 4);
819
820 if (isEGA)
821 _surface->createDialog();
822 else
823 vgaTalkDialog(_surface);
824
825 _wordCountdown = 0;
826
827 // Write out the character name
828 uint16 charWidth = Surface::textWidth(srcCharName);
829 byte white = LureEngine::getReference().isEGA() ? EGA_DIALOG_WHITE_COLOR : VGA_DIALOG_WHITE_COLOR;
830 _surface->writeString((TALK_DIALOG_WIDTH - charWidth) / 2, TALK_DIALOG_EDGE_SIZE + 2,
831 srcCharName, true, white);
832 debugC(ERROR_DETAILED, kLureDebugAnimations, "TalkDialog end");
833 }
834
~TalkDialog()835 TalkDialog::~TalkDialog() {
836 Memory::dealloc(_lines);
837 delete _surface;
838 }
839
copyTo(Surface * dest,uint16 x,uint16 y)840 void TalkDialog::copyTo(Surface *dest, uint16 x, uint16 y) {
841 if (_endLine < _numLines) {
842 if (_wordCountdown > 0) {
843 // Handle delay between words
844 --_wordCountdown;
845
846 } else {
847 // Set a delay before the next word is displayed
848 Game &game = Game::getReference();
849 _wordCountdown = game.fastTextFlag() ? 0 : 1;
850
851 // Scan forward to find the next word break
852 char ch = '\0';
853 bool wordFlag = false;
854
855 while (!wordFlag) {
856 ch = _lines[_endLine][++_endIndex];
857 wordFlag = (ch == ' ') || (ch == '\0');
858 }
859
860 // Write out the completed portion of the current line
861 _surface->writeSubstring(TALK_DIALOG_EDGE_SIZE + 2,
862 TALK_DIALOG_EDGE_SIZE + 4 + (_endLine + 1) * FONT_HEIGHT,
863 _lines[_endLine], _endIndex, true);
864
865 // If at end of line, move to next line for next time
866 if (ch == '\0') {
867 ++_endLine;
868 _endIndex = -1;
869 }
870 }
871 }
872
873 _surface->copyTo(dest, x, y);
874 }
875
saveToStream(Common::WriteStream * stream)876 void TalkDialog::saveToStream(Common::WriteStream *stream) {
877 stream->writeUint16LE(_characterId);
878 stream->writeUint16LE(_destCharacterId);
879 stream->writeUint16LE(_activeItemId);
880 stream->writeUint16LE(_descId);
881 stream->writeSint16LE(_endLine);
882 stream->writeSint16LE(_endIndex);
883 stream->writeSint16LE(_wordCountdown);
884
885 }
886
loadFromStream(Common::ReadStream * stream)887 TalkDialog *TalkDialog::loadFromStream(Common::ReadStream *stream) {
888 uint16 characterId = stream->readUint16LE();
889 if (characterId == 0)
890 return NULL;
891
892 uint16 destCharacterId = stream->readUint16LE();
893 uint16 activeItemId = stream->readUint16LE();
894 uint16 descId = stream->readUint16LE();
895
896 TalkDialog *dialog = new TalkDialog(characterId, destCharacterId, activeItemId, descId);
897 dialog->_endLine = stream->readSint16LE();
898 dialog->_endIndex = stream->readSint16LE();
899 dialog->_wordCountdown = stream->readSint16LE();
900 return dialog;
901 }
902
903 /*--------------------------------------------------------------------------*/
904
905 #define SR_SEPARATOR_Y 21
906 #define SR_SEPARATOR_X 5
907 #define SR_SEPARATOR_HEIGHT 5
908 #define SR_SAVEGAME_NAMES_Y (SR_SEPARATOR_Y + SR_SEPARATOR_HEIGHT + 1)
909
910
toggleHightlight(int xs,int xe,int ys,int ye)911 void SaveRestoreDialog::toggleHightlight(int xs, int xe, int ys, int ye) {
912 Screen &screen = Screen::getReference();
913 byte *addr = screen.screen().data().data() + FULL_SCREEN_WIDTH * ys + xs;
914 const byte colorList[4] = {EGA_DIALOG_TEXT_COLOR, EGA_DIALOG_WHITE_COLOR,
915 VGA_DIALOG_TEXT_COLOR, VGA_DIALOG_WHITE_COLOR};
916 const byte *colors = LureEngine::getReference().isEGA() ? &colorList[0] : &colorList[2];
917
918 for (int y = 0; y < ye - ys + 1; ++y, addr += FULL_SCREEN_WIDTH) {
919 for (int x = 0; x < xe - xs + 1; ++x) {
920 if (addr[x] == colors[0]) addr[x] = colors[1];
921 else if (addr[x] == colors[1]) addr[x] = colors[0];
922 }
923 }
924
925 screen.update();
926 }
927
show(bool saveDialog)928 bool SaveRestoreDialog::show(bool saveDialog) {
929 OSystem &system = *g_system;
930 Screen &screen = Screen::getReference();
931 Mouse &mouse = Mouse::getReference();
932 Events &events = Events::getReference();
933 Resources &res = Resources::getReference();
934 LureEngine &engine = LureEngine::getReference();
935 int selectedLine = -1;
936 int index;
937
938 // Figure out a list of present savegames
939 Common::String **saveNames = (Common::String **)Memory::alloc(sizeof(Common::String *) * MAX_SAVEGAME_SLOTS);
940 int numSaves = 0;
941 while ((numSaves < MAX_SAVEGAME_SLOTS) &&
942 ((saveNames[numSaves] = engine.detectSave(numSaves + 1)) != NULL))
943 ++numSaves;
944
945 // For the save dialog, if all the slots have not been used up, create a
946 // blank entry for a new savegame
947 if (saveDialog && (numSaves < MAX_SAVEGAME_SLOTS))
948 saveNames[numSaves++] = new Common::String();
949
950 // For the restore dialog, if there are no savegames, return immediately
951 if (!saveDialog && (numSaves == 0)) {
952 Memory::dealloc(saveNames);
953 return false;
954 }
955
956 Surface *s = new Surface(INFO_DIALOG_WIDTH, SR_SAVEGAME_NAMES_Y +
957 numSaves * FONT_HEIGHT + FONT_HEIGHT + 2);
958
959 // Create the outer dialog and dividing line
960 s->createDialog();
961 byte *pDest = s->data().data() + (s->width() * SR_SEPARATOR_Y) + SR_SEPARATOR_X;
962 uint8 rowColors[5] = {*(pDest-2), *(pDest-1), *(pDest-1), *(pDest-2), *(pDest+1)};
963 for (int y = 0; y < SR_SEPARATOR_HEIGHT; ++y, pDest += s->width())
964 memset(pDest, rowColors[y], s->width() - 12);
965
966 // Create title line
967 Common::String title(res.stringList().getString(
968 saveDialog ? S_SAVE_GAME : S_RESTORE_GAME));
969 s->writeString((s->width() - s->textWidth(title.c_str())) / 2, FONT_HEIGHT+2, title, true);
970
971 // Write out any existing save names
972 for (index = 0; index < numSaves; ++index)
973 s->writeString(Surface::textX(), SR_SAVEGAME_NAMES_Y + (index * 8), saveNames[index]->c_str(), true);
974
975 // Display the dialog
976 s->copyTo(&screen.screen(), SAVE_DIALOG_X, SAVE_DIALOG_Y);
977 screen.update();
978 mouse.pushCursorNum(CURSOR_ARROW);
979 Sound.pause();
980
981 bool abortFlag = false;
982 bool doneFlag = false;
983 while (!abortFlag && !doneFlag) {
984 // Provide highlighting of lines to select a save slot
985 while (!abortFlag && !(mouse.lButton() && (selectedLine != -1))
986 && !mouse.rButton() && !mouse.mButton()) {
987 abortFlag = engine.shouldQuit();
988 if (abortFlag) break;
989
990 while (events.pollEvent()) {
991 if ((events.type() == Common::EVENT_KEYDOWN) &&
992 (events.event().kbd.keycode == Common::KEYCODE_ESCAPE)) {
993 abortFlag = true;
994 break;
995 }
996 if (events.type() == Common::EVENT_MOUSEMOVE ||
997 events.type() == Common::EVENT_WHEELUP || events.type() == Common::EVENT_WHEELDOWN) {
998 // Mouse movement
999 int lineNum = 0;
1000
1001 if (events.type() == Common::EVENT_MOUSEMOVE) {
1002 if ((mouse.x() < (SAVE_DIALOG_X + Surface::textX())) ||
1003 (mouse.x() >= (SAVE_DIALOG_X + s->width() - Surface::textX())) ||
1004 (mouse.y() < SAVE_DIALOG_Y + SR_SAVEGAME_NAMES_Y) ||
1005 (mouse.y() >= SAVE_DIALOG_Y + SR_SAVEGAME_NAMES_Y + numSaves * FONT_HEIGHT))
1006 // Outside displayed lines
1007 lineNum = -1;
1008 else
1009 lineNum = (mouse.y() - (SAVE_DIALOG_Y + SR_SAVEGAME_NAMES_Y)) / FONT_HEIGHT;
1010 } else if (events.type() == Common::EVENT_WHEELUP) {
1011 if (selectedLine > 0) {
1012 lineNum = selectedLine - 1;
1013 }
1014 } else if (events.type() == Common::EVENT_WHEELDOWN) {
1015 if (selectedLine < numSaves - 1) {
1016 lineNum = selectedLine + 1;
1017 }
1018 }
1019
1020 if (lineNum != selectedLine) {
1021 if (selectedLine != -1)
1022 // Deselect previously selected line
1023 toggleHightlight(SAVE_DIALOG_X + Surface::textX(),
1024 SAVE_DIALOG_X + s->width() - Surface::textX(),
1025 SAVE_DIALOG_Y + SR_SAVEGAME_NAMES_Y + selectedLine * FONT_HEIGHT,
1026 SAVE_DIALOG_Y + SR_SAVEGAME_NAMES_Y + (selectedLine + 1) * FONT_HEIGHT - 1);
1027
1028 // Highlight new line
1029 selectedLine = lineNum;
1030 if (selectedLine != -1)
1031 toggleHightlight(SAVE_DIALOG_X + Surface::textX(),
1032 SAVE_DIALOG_X + s->width() - Surface::textX(),
1033 SAVE_DIALOG_Y + SR_SAVEGAME_NAMES_Y + selectedLine * FONT_HEIGHT,
1034 SAVE_DIALOG_Y + SR_SAVEGAME_NAMES_Y + (selectedLine + 1) * FONT_HEIGHT - 1);
1035 }
1036 }
1037 }
1038
1039 system.updateScreen();
1040 system.delayMillis(10);
1041 }
1042
1043 // Deselect selected row
1044 if (selectedLine != -1)
1045 toggleHightlight(SAVE_DIALOG_X + Surface::textX(),
1046 SAVE_DIALOG_X + s->width() - Surface::textX(),
1047 SAVE_DIALOG_Y + SR_SAVEGAME_NAMES_Y + selectedLine * FONT_HEIGHT,
1048 SAVE_DIALOG_Y + SR_SAVEGAME_NAMES_Y + (selectedLine + 1) * FONT_HEIGHT - 1);
1049
1050 if (mouse.lButton() || mouse.rButton() || mouse.mButton()) {
1051 abortFlag = mouse.rButton();
1052 mouse.waitForRelease();
1053 }
1054 if (abortFlag) break;
1055
1056 // If in save mode, allow the entry of a new savename
1057 if (saveDialog) {
1058 if (!screen.screen().getString(*saveNames[selectedLine],
1059 INFO_DIALOG_WIDTH - (Surface::textX() * 2),
1060 false, true, SAVE_DIALOG_X + Surface::textX(),
1061 SAVE_DIALOG_Y + SR_SAVEGAME_NAMES_Y + selectedLine * FONT_HEIGHT)) {
1062 // Aborted out of name selection, so restore old name and
1063 // go back to slot selection
1064 screen.screen().writeString(
1065 SAVE_DIALOG_X + Surface::textX(),
1066 SAVE_DIALOG_Y + SR_SAVEGAME_NAMES_Y + selectedLine * FONT_HEIGHT,
1067 saveNames[selectedLine]->c_str(), true);
1068 selectedLine = -1;
1069 continue;
1070 }
1071 }
1072 doneFlag = true;
1073 }
1074
1075 delete s;
1076 Sound.resume();
1077
1078 int errorFlag = 0;
1079 if (doneFlag) {
1080 // Handle save or restore
1081 if (saveDialog) {
1082 doneFlag = engine.saveGame(selectedLine + 1, *saveNames[selectedLine]);
1083 if (!doneFlag)
1084 errorFlag = 1;
1085 } else {
1086 doneFlag = engine.loadGame(selectedLine + 1);
1087 if (!doneFlag)
1088 errorFlag = 2;
1089 }
1090 }
1091
1092 mouse.popCursor();
1093
1094 // Free savegame caption list
1095 for (index = 0; index < numSaves; ++index) delete saveNames[index];
1096 Memory::dealloc(saveNames);
1097
1098 if (errorFlag != 0) {
1099 Room::getReference().update();
1100 screen.update();
1101
1102 if (errorFlag == 1)
1103 Dialog::show("Error occurred saving the game");
1104 else if (errorFlag == 2)
1105 Dialog::show("Error occurred loading the savegame");
1106 }
1107
1108 return doneFlag;
1109 }
1110
1111 /*--------------------------------------------------------------------------*/
1112
1113 struct RestartRecordPos {
1114 int16 x, y;
1115 };
1116
1117 struct RestartRecord {
1118 Common::Language Language;
1119 int16 width, height;
1120 RestartRecordPos BtnRestart;
1121 RestartRecordPos BtnRestore;
1122 };
1123
1124 static const RestartRecord buttonBounds[] = {
1125 { Common::EN_ANY, 48, 14, { 118, 152 }, { 168, 152 } },
1126 { Common::DE_DEU, 48, 14, { 106, 152 }, { 168, 152 } },
1127 { Common::UNK_LANG, 48, 14, { 112, 152 }, { 168, 152 } }
1128 };
1129
1130
show()1131 bool RestartRestoreDialog::show() {
1132 Resources &res = Resources::getReference();
1133 Events &events = Events::getReference();
1134 Mouse &mouse = Mouse::getReference();
1135 Screen &screen = Screen::getReference();
1136 LureEngine &engine = LureEngine::getReference();
1137
1138 Sound.killSounds();
1139 Sound.musicInterface_Play(188, true);
1140 mouse.setCursorNum(CURSOR_ARROW);
1141
1142 // See if there are any savegames that can be restored
1143 Common::String *firstSave = engine.detectSave(1);
1144 bool restartFlag = (firstSave == NULL);
1145 int highlightedButton = -1;
1146
1147 if (!restartFlag) {
1148 delete firstSave;
1149
1150 // Get the correct button bounds record to use
1151 const RestartRecord *btnRecord = &buttonBounds[0];
1152 while ((btnRecord->Language != engine.getLanguage()) &&
1153 (btnRecord->Language != Common::UNK_LANG))
1154 ++btnRecord;
1155
1156 // Fade out the screen
1157 screen.paletteFadeOut(RES_PALETTE_ENTRIES);
1158
1159 // Get the palette that will be used, and first fade out the prior screen
1160 Palette p(RESTART_RESOURCE_ID - 1);
1161
1162 // Turn on the mouse
1163 mouse.cursorOn();
1164
1165 // Load the restore/restart screen image
1166 Surface *s = Surface::getScreen(RESTART_RESOURCE_ID);
1167 s->copyTo(&screen.screen(), 0, MENUBAR_Y_SIZE);
1168 delete s;
1169
1170 res.activeHotspots().clear();
1171 Hotspot *btnHotspot = new Hotspot();
1172
1173 // Restart button
1174 btnHotspot->setSize(btnRecord->width, btnRecord->height);
1175 btnHotspot->setPosition(btnRecord->BtnRestart.x, btnRecord->BtnRestart.y);
1176 btnHotspot->setAnimation(0x184B);
1177 btnHotspot->copyTo(&screen.screen());
1178
1179 // Restore button
1180 btnHotspot->setFrameNumber(1);
1181 btnHotspot->setPosition(btnRecord->BtnRestore.x, btnRecord->BtnRestore.y);
1182 btnHotspot->copyTo(&screen.screen());
1183
1184 screen.update();
1185 screen.paletteFadeIn(&p);
1186
1187 // Event loop for making selection
1188 bool buttonPressed = false;
1189
1190 while (!engine.shouldQuit()) {
1191 // Handle events
1192 while (events.pollEvent()) {
1193 if ((events.type() == Common::EVENT_LBUTTONDOWN) && (highlightedButton != -1)) {
1194 mouse.waitForRelease();
1195 buttonPressed = true;
1196 break;
1197 }
1198 }
1199
1200 if (buttonPressed)
1201 break;
1202
1203 // Check if the pointer is over either button
1204 int currentButton = -1;
1205 if ((mouse.y() >= btnRecord->BtnRestart.y) &&
1206 (mouse.y() < btnRecord->BtnRestart.y + btnRecord->height)) {
1207 // Check whether the Restart or Restore button is highlighted
1208 if ((mouse.x() >= btnRecord->BtnRestart.x) &&
1209 (mouse.x() < btnRecord->BtnRestart.x + btnRecord->width))
1210 currentButton = 0;
1211 else if ((mouse.x() >= btnRecord->BtnRestore.x) &&
1212 (mouse.x() < btnRecord->BtnRestore.x + btnRecord->width))
1213 currentButton = 1;
1214 }
1215
1216 // Take care of highlighting as the selected button changes
1217 if (currentButton != highlightedButton) {
1218 highlightedButton = currentButton;
1219
1220 // Restart button
1221 btnHotspot->setFrameNumber((highlightedButton == 0) ? 2 : 0);
1222 btnHotspot->setPosition(btnRecord->BtnRestart.x, btnRecord->BtnRestart.y);
1223 btnHotspot->copyTo(&screen.screen());
1224
1225 // Restore button
1226 btnHotspot->setFrameNumber((highlightedButton == 1) ? 3 : 1);
1227 btnHotspot->setPosition(btnRecord->BtnRestore.x, btnRecord->BtnRestore.y);
1228 btnHotspot->copyTo(&screen.screen());
1229 }
1230
1231
1232 screen.update();
1233 g_system->delayMillis(10);
1234 }
1235
1236 restartFlag = highlightedButton == 0;
1237 delete btnHotspot;
1238 }
1239
1240 Sound.killSounds();
1241
1242 if (!restartFlag && !engine.shouldQuit()) {
1243 // Need to show Restore game dialog
1244 if (!SaveRestoreDialog::show(false))
1245 // User cancelled, so fall back on Restart
1246 restartFlag = true;
1247 }
1248
1249 return restartFlag;
1250 }
1251
1252 /*--------------------------------------------------------------------------*/
1253
1254 struct ItemDesc {
1255 Common::Language language;
1256 int16 x, y;
1257 uint16 width, height;
1258 uint16 animId;
1259 uint8 startColor;
1260 };
1261
1262 #define PROT_SPR_HEADER 0x1830
1263 #define WORDING_HEADER 0x1839
1264 #define NUMBER_HEADER 0x1842
1265
1266 static const ItemDesc copyProtectElements[] = {
1267 {Common::UNK_LANG, 104, 96, 32, 48, PROT_SPR_HEADER, 0},
1268 {Common::UNK_LANG, 179, 96, 32, 48, PROT_SPR_HEADER, 0},
1269
1270 {Common::EN_ANY, 57, 40, 208, 40, WORDING_HEADER, 32},
1271 {Common::FR_FRA, 57, 40, 208, 40, WORDING_HEADER, 32},
1272 {Common::DE_DEU, 39, 30, 240, 53, WORDING_HEADER, 32},
1273 {Common::NL_NLD, 57, 40, 208, 40, WORDING_HEADER, 32},
1274 {Common::ES_ESP, 57, 40, 208, 40, WORDING_HEADER, 32},
1275 {Common::IT_ITA, 57, 40, 208, 40, WORDING_HEADER, 32},
1276 {Common::RU_RUS, 57, 40, 208, 40, WORDING_HEADER, 32},
1277
1278 {Common::UNK_LANG, 138, 168, 16, 8, NUMBER_HEADER, 32},
1279 {Common::UNK_LANG, 145, 168, 16, 8, NUMBER_HEADER, 32},
1280 {Common::UNK_LANG, 164, 168, 16, 8, NUMBER_HEADER, 32},
1281 {Common::UNK_LANG, 171, 168, 16, 8, NUMBER_HEADER, 32},
1282 {Common::UNK_LANG, 0, 0, 0, 0, 0, 0}
1283 };
1284
1285 int pageNumbers[20] = {
1286 4, 10, 16, 22, 5, 11, 17, 23, 6, 12, 18, 7, 13, 19, 8, 14, 20, 9, 15, 21};
1287
CopyProtectionDialog()1288 CopyProtectionDialog::CopyProtectionDialog() {
1289 // Get objects for the screen
1290 LureEngine &engine = LureEngine::getReference();
1291
1292 const ItemDesc *ptr = ©ProtectElements[0];
1293 while ((ptr->width != 0) || (ptr->height != 0)) {
1294 if ((ptr->language == Common::UNK_LANG) || (ptr->language == engine.getLanguage())) {
1295 Hotspot *h = new Hotspot();
1296 h->setPosition(ptr->x, ptr->y);
1297 h->setSize(ptr->width, ptr->height);
1298 h->setColorOffset(ptr->startColor);
1299 h->setAnimation(ptr->animId);
1300
1301 _hotspots.push_back(HotspotsList::value_type(h));
1302 }
1303
1304 ++ptr;
1305 }
1306 }
1307
show()1308 bool CopyProtectionDialog::show() {
1309 Screen &screen = Screen::getReference();
1310 Events &events = Events::getReference();
1311 LureEngine &engine = LureEngine::getReference();
1312
1313 screen.setPaletteEmpty();
1314 Palette p(COPY_PROTECTION_RESOURCE_ID - 1);
1315
1316 for (int tryCounter = 0; tryCounter < 3; ++tryCounter) {
1317 // Copy the base screen to the output screen
1318 Surface *s = Surface::getScreen(COPY_PROTECTION_RESOURCE_ID);
1319 s->copyTo(&screen.screen(), 0, MENUBAR_Y_SIZE);
1320 delete s;
1321
1322 // Get needed hotspots
1323 HotspotsList::iterator hotspot0 = _hotspots.begin();
1324 HotspotsList::iterator hotspot1 = _hotspots.begin();
1325 for (int i = 0; i < 1; i++)
1326 ++hotspot1;
1327 HotspotsList::iterator hotspot2 = _hotspots.begin();
1328 for (int i = 0; i < 2; i++)
1329 ++hotspot2;
1330 HotspotsList::iterator hotspot3 = _hotspots.begin();
1331 for (int i = 0; i < 3; i++)
1332 ++hotspot3;
1333 HotspotsList::iterator hotspot4 = _hotspots.begin();
1334 for (int i = 0; i < 4; i++)
1335 ++hotspot4;
1336 HotspotsList::iterator hotspot5 = _hotspots.begin();
1337 for (int i = 0; i < 5; i++)
1338 ++hotspot5;
1339 HotspotsList::iterator hotspot6 = _hotspots.begin();
1340 for (int i = 0; i < 6; i++)
1341 ++hotspot6;
1342
1343 // Add wording header and display screen
1344 (*hotspot2)->setFrameNumber(1);
1345 (*hotspot2)->copyTo(&screen.screen());
1346 screen.update();
1347 screen.setPalette(&p);
1348
1349 // Cycle through displaying different characters until a key or mouse button is pressed
1350 do {
1351 chooseCharacters();
1352 } while (!events.interruptableDelay(100));
1353
1354 // Change title text to selection
1355 (*hotspot2)->setFrameNumber(0);
1356 (*hotspot2)->copyTo(&screen.screen());
1357 screen.update();
1358
1359 // Clear any prior try
1360 _charIndex = 0;
1361
1362 while (!engine.shouldQuit()) {
1363 while (events.pollEvent() && (_charIndex < 4)) {
1364 if (events.type() == Common::EVENT_KEYDOWN) {
1365 if ((events.event().kbd.keycode == Common::KEYCODE_BACKSPACE) && (_charIndex > 0)) {
1366 // Remove the last number typed
1367 --_charIndex;
1368 HotspotsList::iterator tmpHotspot = _hotspots.begin();
1369 for (int i = 0; i < _charIndex + 3; i++)
1370 ++tmpHotspot;
1371 (*tmpHotspot)->setFrameNumber(10); // Blank space
1372 (*tmpHotspot)->copyTo(&screen.screen());
1373
1374 screen.update();
1375 } else if ((events.event().kbd.ascii >= '0') &&
1376 (events.event().kbd.ascii <= '9')) {
1377 HotspotsList::iterator tmpHotspot = _hotspots.begin();
1378 for (int i = 0; i < _charIndex + 3; i++)
1379 ++tmpHotspot;
1380 // Number pressed
1381 (*tmpHotspot)->setFrameNumber(events.event().kbd.ascii - '0');
1382 (*tmpHotspot)->copyTo(&screen.screen());
1383
1384 ++_charIndex;
1385 }
1386
1387 screen.update();
1388 }
1389 }
1390
1391 g_system->delayMillis(10);
1392 if (_charIndex == 4)
1393 break;
1394 }
1395
1396 if (engine.shouldQuit())
1397 return false;
1398
1399 // At this point, two page numbers have been entered - validate them
1400 int page1 = ((*hotspot3)->frameNumber() * 10) + (*hotspot4)->frameNumber();
1401 int page2 = ((*hotspot5)->frameNumber() * 10) + (*hotspot6)->frameNumber();
1402
1403 if ((page1 == pageNumbers[(*hotspot0)->frameNumber()]) &&
1404 (page2 == pageNumbers[(*hotspot1)->frameNumber()]))
1405 return true;
1406 }
1407
1408 // Copy protection failed
1409 return false;
1410 }
1411
chooseCharacters()1412 void CopyProtectionDialog::chooseCharacters() {
1413 Screen &screen = Screen::getReference();
1414 Common::RandomSource &rnd = LureEngine::getReference().rnd();
1415 int char1 = rnd.getRandomNumber(19);
1416 int char2 = rnd.getRandomNumber(19);
1417
1418 HotspotsList::iterator curHotspot = _hotspots.begin();
1419 (*curHotspot)->setFrameNumber(char1);
1420 (*curHotspot)->copyTo(&screen.screen());
1421 ++curHotspot;
1422 (*curHotspot)->setFrameNumber(char2);
1423 (*curHotspot)->copyTo(&screen.screen());
1424
1425 screen.update();
1426 }
1427
AudioInitIcon()1428 AudioInitIcon::AudioInitIcon() : _visible(false) {
1429 if (LureEngine::getReference().isEGA()) {
1430 // The icon is not shown on EGA
1431 _iconSurface = 0;
1432 } else {
1433 // Load icon
1434 _iconSurface = new Surface(Disk::getReference().getEntry(AUDIO_INIT_ICON_RESOURCE_ID), 14, 14);
1435
1436 Screen &screen = Screen::getReference();
1437
1438 // Add the colors needed for displaying the icon to the current palette
1439 Palette combinedPalette;
1440 Palette defaultPalette(GAME_PALETTE_RESOURCE_ID);
1441 combinedPalette.palette()->copyFrom(screen.getPalette().palette(), 0, 0, 4 * 0xF8);
1442 combinedPalette.palette()->copyFrom(defaultPalette.palette(), 4 * 0xF8, 4 * 0xF8, 4 * 6);
1443 screen.setPalette(&combinedPalette);
1444 }
1445 }
1446
~AudioInitIcon()1447 AudioInitIcon::~AudioInitIcon() {
1448 if (_iconSurface)
1449 delete _iconSurface;
1450 }
1451
show()1452 void AudioInitIcon::show() {
1453 if (!LureEngine::getReference().isEGA()) {
1454 Screen &screen = Screen::getReference();
1455
1456 _iconSurface->copyTo(&screen.screen(), 0, 185);
1457 screen.update();
1458 _visible = true;
1459 }
1460 }
1461
hide()1462 void AudioInitIcon::hide() {
1463 if (!LureEngine::getReference().isEGA()) {
1464 Screen &screen = Screen::getReference();
1465
1466 screen.screen().fillRect(Common::Rect(0, 185, 14, 199), 0);
1467 screen.update();
1468 _visible = false;
1469 }
1470 }
1471
toggleVisibility()1472 void AudioInitIcon::toggleVisibility() {
1473 if (_visible) {
1474 hide();
1475 } else {
1476 show();
1477 }
1478 }
1479
1480 } // End of namespace Lure
1481