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 
24 
25 #include "common/file.h"
26 #include "common/textconsole.h"
27 
28 #include "gui/message.h"
29 
30 #include "agos/agos.h"
31 #include "agos/intern.h"
32 
33 namespace AGOS {
34 
uncompressText(byte * ptr)35 void AGOSEngine::uncompressText(byte *ptr) {
36 	byte a;
37 	while (1) {
38 		if (_awaitTwoByteToken != 0)
39 			a = _awaitTwoByteToken;
40 		else
41 			a = *ptr++;
42 		if (a == 0)
43 			return;
44 		ptr = uncompressToken(a, ptr);
45 		if (ptr == 0)
46 			return;
47 	}
48 }
49 
uncompressToken(byte a,byte * ptr)50 byte *AGOSEngine::uncompressToken(byte a, byte *ptr) {
51 	byte *ptr1 = 0;
52 	byte *ptr2 = 0;
53 	byte b;
54 	int count1 = 0;
55 
56 	if (a == 0xFF || a == 0xFE || a == 0xFD) {
57 		if (a == 0xFF)
58 			ptr2 = _twoByteTokenStrings;
59 		if (a == 0xFE)
60 			ptr2 = _secondTwoByteTokenStrings;
61 		if (a == 0xFD)
62 			ptr2 = _thirdTwoByteTokenStrings;
63 		_awaitTwoByteToken = a;
64 		b = a;
65 		a = *ptr++;
66 		if (a == 0)		/* Need to return such that next byte   */
67 			return 0;	/* is used as two byte token		*/
68 
69 		_awaitTwoByteToken = 0;
70 		ptr1 = _twoByteTokens;
71 		while (*ptr1 != a) {
72 			ptr1++;
73 			count1++;
74 			if (*ptr1 == 0)	{	/* If was not a two byte token  */
75 				count1 = 0;	/* then was a byte token.	*/
76 				ptr1 = _byteTokens;
77 				while (*ptr1 != a) {
78 					ptr1++;
79 					count1++;
80 				}
81 				ptr1 = _byteTokenStrings;		/* Find it */
82 				while (count1--)	{
83 					while (*ptr1++)
84 						;
85 				}
86 				ptr1 = uncompressToken(b, ptr1);	/* Try this one as a two byte token */
87 				uncompressText(ptr1);			/* Uncompress rest of this token    */
88 				return ptr;
89 			}
90 		}
91 		while (count1--) {
92 			while (*ptr2++)
93 				;
94 		}
95 		uncompressText(ptr2);
96 	} else {
97 		ptr1 = _byteTokens;
98 		while (*ptr1 != a) {
99 			ptr1++;
100 			count1++;
101 			if (*ptr1 == 0) {
102 				_textBuffer[_textCount++] = a;	/* Not a byte token */
103 				return ptr;			/* must be real character */
104 			}
105 		}
106 		ptr1 = _byteTokenStrings;
107 		while (count1--)	{		/* Is a byte token so count */
108 			while (*ptr1++)		/* to start of token */
109 				;
110 		}
111 		uncompressText(ptr1);			/* and do it */
112 	}
113 	return ptr;
114 }
115 
getStringPtrByID(uint16 stringId,bool upperCase)116 const byte *AGOSEngine::getStringPtrByID(uint16 stringId, bool upperCase) {
117 	const byte *stringPtr;
118 	byte *dst;
119 
120 	_freeStringSlot ^= 1;
121 	dst = _stringReturnBuffer[_freeStringSlot];
122 
123 	if (getGameType() == GType_ELVIRA1 && getPlatform() == Common::kPlatformAtariST) {
124 		byte *ptr = _stringTabPtr[stringId];
125 		_textCount = 0;
126 		_awaitTwoByteToken = 0;
127 		uncompressText(ptr);
128 		_textBuffer[_textCount] = 0;
129 		Common::strlcpy((char *)dst, (const char *)_textBuffer, 180);
130 	} else {
131 		if (stringId < 0x8000) {
132 			stringPtr = _stringTabPtr[stringId];
133 		} else {
134 			stringPtr = getLocalStringByID(stringId);
135 		}
136 		Common::strlcpy((char *)dst, (const char *)stringPtr, 180);
137 	}
138 
139 	// WORKAROUND bug #1538873: The French version of Simon 1 and the
140 	// Polish version of Simon 2 used excess spaces, at the end of many
141 	// messages, so we strip off those excess spaces.
142 	if ((getGameType() == GType_SIMON1 && _language == Common::FR_FRA) ||
143 		(getGameType() == GType_SIMON2 && _language == Common::PL_POL)) {
144 		uint16 len = strlen((const char *)dst) - 1;
145 
146 		while (len && dst[len] == 32) {
147 			dst[len] = 0;
148 			len--;
149 		}
150 
151 	}
152 
153 	if (upperCase && *dst) {
154 		if (Common::isLower(*dst))
155 			*dst = toupper(*dst);
156 	}
157 
158 	return dst;
159 }
160 
getLocalStringByID(uint16 stringId)161 const byte *AGOSEngine::getLocalStringByID(uint16 stringId) {
162 	if (stringId < _stringIdLocalMin || stringId >= _stringIdLocalMax) {
163 		loadTextIntoMem(stringId);
164 	}
165 	return _localStringtable[stringId - _stringIdLocalMin];
166 }
167 
getTextLocation(uint a)168 TextLocation *AGOSEngine::getTextLocation(uint a) {
169 	switch (a) {
170 	case 1:
171 		return &_textLocation1;
172 	case 2:
173 		return &_textLocation2;
174 	case 101:
175 		return &_textLocation3;
176 	case 102:
177 		return &_textLocation4;
178 	default:
179 		error("getTextLocation: Invalid text location %d", a);
180 	}
181 	return NULL;	// for compilers that don't support NORETURN
182 }
183 
allocateStringTable(int num)184 void AGOSEngine::allocateStringTable(int num) {
185 	_stringTabPtr = (byte **)calloc(num, sizeof(byte *));
186 	_stringTabPos = 0;
187 	_stringTabSize = num;
188 }
189 
setupStringTable(byte * mem,int num)190 void AGOSEngine::setupStringTable(byte *mem, int num) {
191 	int i = 0;
192 
193 	if (getGameType() == GType_ELVIRA1 && getPlatform() == Common::kPlatformAtariST) {
194 		int ct1;
195 
196 		_twoByteTokens = mem;
197 		while (*mem++) {
198 			i++;
199 		}
200 		_twoByteTokenStrings = mem;
201 		ct1 = i;
202 		while (*mem++) {
203 			while (*mem++)
204 				;
205 			i--;
206 			if ((i == 0) && (ct1 != 0)) {
207 				_secondTwoByteTokenStrings = mem;
208 				i = ct1;
209 				ct1 = 0;
210 			}
211 			if (i == 0)
212 				_thirdTwoByteTokenStrings = mem;
213 		}
214 		_byteTokens = mem;
215 		while (*mem++)
216 			;
217 		_byteTokenStrings = mem;
218 		while (*mem++) {
219 			while (*mem++)
220 				;
221 		}
222 		i = 0;
223 l1:		_stringTabPtr[i++] = mem;
224 		num--;
225 		if (!num) {
226 			_stringTabPos = i;
227 			return;
228 		}
229 		while (*mem++)
230 			;
231 		goto l1;
232 	} else {
233 		for (;;) {
234 			_stringTabPtr[i++] = mem;
235 			if (--num == 0)
236 				break;
237 			for (; *mem; mem++)
238 				;
239 			mem++;
240 		}
241 
242 		_stringTabPos = i;
243 	}
244 }
245 
setupLocalStringTable(byte * mem,int num)246 void AGOSEngine::setupLocalStringTable(byte *mem, int num) {
247 	int i = 0;
248 	for (;;) {
249 		_localStringtable[i++] = mem;
250 		if (--num == 0)
251 			break;
252 		for (; *mem; mem++)
253 			;
254 		mem++;
255 	}
256 }
257 
loadTextFile(const char * filename,byte * dst)258 uint AGOSEngine::loadTextFile(const char *filename, byte *dst) {
259 	if (getFeatures() & GF_OLD_BUNDLE)
260 		return loadTextFile_simon1(filename, dst);
261 	else
262 		return loadTextFile_gme(filename, dst);
263 }
264 
loadTextFile_simon1(const char * filename,byte * dst)265 uint AGOSEngine::loadTextFile_simon1(const char *filename, byte *dst) {
266 	Common::File fo;
267 	fo.open(filename);
268 	uint32 size;
269 
270 	if (fo.isOpen() == false)
271 		error("loadTextFile: Can't open '%s'", filename);
272 
273 	size = fo.size();
274 
275 	if (fo.read(dst, size) != size)
276 		error("loadTextFile: fread failed");
277 	fo.close();
278 
279 	return size;
280 }
281 
loadTextFile_gme(const char * filename,byte * dst)282 uint AGOSEngine::loadTextFile_gme(const char *filename, byte *dst) {
283 	uint res;
284 	uint32 offs;
285 	uint32 size;
286 
287 	res = atoi(filename + 4) + _textIndexBase - 1;
288 	offs = _gameOffsetsPtr[res];
289 	size = _gameOffsetsPtr[res + 1] - offs;
290 
291 	readGameFile(dst, offs, size);
292 
293 	return size;
294 }
295 
loadTextIntoMem(uint16 stringId)296 void AGOSEngine::loadTextIntoMem(uint16 stringId) {
297 	byte *p;
298 	uint16 baseMin = 0x8000, baseMax, size;
299 
300 	_tablesHeapPtr = _tablesheapPtrNew;
301 	_tablesHeapCurPos = _tablesHeapCurPosNew;
302 
303 	p = _strippedTxtMem;
304 
305 	// get filename
306 	while (*p) {
307 		Common::String filename;
308 		while (*p)
309 			filename += *p++;
310 		p++;
311 
312 		if (getPlatform() == Common::kPlatformAcorn) {
313 			filename += ".DAT";
314 		}
315 
316 		baseMax = (p[0] * 256) | p[1];
317 		p += 2;
318 
319 		if (stringId < baseMax) {
320 			_stringIdLocalMin = baseMin;
321 			_stringIdLocalMax = baseMax;
322 
323 			_localStringtable = (byte **)_tablesHeapPtr;
324 
325 			size = (baseMax - baseMin + 1) * sizeof(byte *);
326 			_tablesHeapPtr += size;
327 			_tablesHeapCurPos += size;
328 
329 			size = loadTextFile(filename.c_str(), _tablesHeapPtr);
330 
331 			setupLocalStringTable(_tablesHeapPtr, baseMax - baseMin + 1);
332 
333 			_tablesHeapPtr += size;
334 			_tablesHeapCurPos += size;
335 
336 			if (_tablesHeapCurPos > _tablesHeapSize) {
337 				error("loadTextIntoMem: Out of table memory");
338 			}
339 			return;
340 		}
341 
342 		baseMin = baseMax;
343 	}
344 
345 	error("loadTextIntoMem: didn't find %d", stringId);
346 }
347 
348 static const byte polish_charWidth[226] = {
349 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
350 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
351 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
352 	0, 0, 6, 2, 8, 7, 6,10, 8, 2,
353 	4, 4, 7, 6, 3, 4, 2, 3, 6, 4,
354 	6, 6, 7, 6, 6, 6, 6, 6, 2, 8,
355 	6, 9, 7, 6, 6, 8, 7, 8, 8, 7,
356 	6, 9, 8, 2, 6, 7, 6,10, 8, 9,
357 	7, 9, 7, 7, 8, 8, 8,12, 8, 8,
358 	7, 6, 7, 6, 4, 7, 7, 7, 7, 6,
359 	7, 7, 4, 7, 6, 2, 3, 6, 2,10,
360 	6, 7, 7, 7, 5, 6, 4, 6, 6,10,
361 	6, 6, 6, 0, 0, 0, 0, 0, 8, 6,
362 	7, 7, 7, 7, 7, 6, 7, 7, 7, 4,
363 	4, 3, 8, 8, 7, 0, 0, 7, 7, 7,
364 	6, 6, 6, 9, 8, 0, 0, 0, 0, 0,
365 	7, 3, 7, 6, 6, 8, 0, 0, 6, 0,
366 	0, 0, 0, 2, 0, 0, 0, 0, 0, 0,
367 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
368 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
369 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
370 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
371 	0, 0, 0, 0, 0, 7
372 };
373 
374 static const byte charWidth[226] = {
375 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
376 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
377 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
378 	0, 0, 6, 2, 4, 8, 6,10, 9, 2,
379 	4, 4, 6, 6, 3, 4, 2, 3, 6, 4,
380 	6, 6, 7, 6, 6, 6, 6, 6, 2, 3,
381 	7, 7, 7, 6,11, 8, 7, 8, 8, 7,
382 	6, 9, 8, 2, 6, 7, 6,10, 8, 9,
383 	7, 9, 7, 7, 8, 8, 8,12, 8, 8,
384 	7, 3, 3, 3, 6, 8, 3, 7, 7, 6,
385 	7, 7, 4, 7, 6, 2, 3, 6, 2,10,
386 	6, 7, 7, 7, 5, 6, 4, 7, 7,10,
387 	6, 6, 6, 0, 0, 0, 0, 0, 8, 6,
388 	7, 7, 7, 7, 7, 6, 7, 7, 7, 4,
389 	4, 3, 8, 8, 7, 0, 0, 7, 7, 7,
390 	6, 6, 6, 9, 8, 0, 0, 0, 0, 0,
391 	7, 3, 7, 6, 6, 8, 0, 6, 0, 0,
392 	0, 0, 0, 2, 0, 0, 0, 0, 0, 0,
393 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
394 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
395 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
396 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
397 	0, 0, 0, 0, 0, 7
398 };
399 
getPixelLength(const char * string,uint16 maxWidth,uint16 & pixels)400 const char *AGOSEngine::getPixelLength(const char *string, uint16 maxWidth, uint16 &pixels) {
401 	pixels = 0;
402 
403 	while (*string != 0) {
404 		byte chr = *string;
405 		uint8 len = (_language == Common::PL_POL) ? polish_charWidth[chr] : charWidth[chr];
406 		if ((pixels + len) > maxWidth)
407 			break;
408 		pixels += len;
409 		string++;
410 	}
411 
412 	return string;
413 }
414 
printTextOf(uint a,uint x,uint y)415 bool AGOSEngine::printTextOf(uint a, uint x, uint y) {
416 	const byte *stringPtr;
417 	uint16 pixels, w;
418 
419 	if (getGameType() == GType_SIMON2) {
420 		if (getBitFlag(79)) {
421 			Subroutine *sub;
422 			_variableArray[84] = a;
423 			sub = getSubroutineByID(5003);
424 			if (sub != NULL)
425 				startSubroutineEx(sub);
426 			return true;
427 		}
428 	}
429 
430 	if (a >= _numTextBoxes)
431 		return false;
432 
433 
434 	stringPtr = getStringPtrByID(_shortText[a]);
435 	if (getGameType() == GType_FF) {
436 		getPixelLength((const char *)stringPtr, 400, pixels);
437 		w = pixels + 1;
438 		x -= w / 2;
439 		printScreenText(6, 0, (const char *)stringPtr, x, y, w);
440 	} else {
441 		showActionString(stringPtr);
442 	}
443 
444 	return true;
445 }
446 
printNameOf(Item * item,uint x,uint y)447 bool AGOSEngine::printNameOf(Item *item, uint x, uint y) {
448 	SubObject *subObject;
449 	const byte *stringPtr;
450 	uint16 pixels, w;
451 
452 	if (item == 0 || item == _dummyItem2 || item == _dummyItem3)
453 		return false;
454 
455 	subObject = (SubObject *)findChildOfType(item, kObjectType);
456 	if (subObject == NULL)
457 		return false;
458 
459 	stringPtr = getStringPtrByID(subObject->objectName);
460 	if (getGameType() == GType_FF) {
461 		getPixelLength((const char *)stringPtr, 400, pixels);
462 		w = pixels + 1;
463 		x -= w / 2;
464 		printScreenText(6, 0, (const char *)stringPtr, x, y, w);
465 	} else {
466 		showActionString(stringPtr);
467 	}
468 
469 	return true;
470 }
471 
printScreenText(uint vgaSpriteId,uint color,const char * string,int16 x,int16 y,int16 width)472 void AGOSEngine::printScreenText(uint vgaSpriteId, uint color, const char *string, int16 x, int16 y, int16 width) {
473 	char convertedString[320];
474 	char *convertedString2 = convertedString;
475 	int16 height, talkDelay;
476 	int stringLength = strlen(string);
477 	int padding, lettersPerRow, lettersPerRowJustified;
478 	const int textHeight = 10;
479 
480 	height = textHeight;
481 	lettersPerRow = width / 6;
482 	lettersPerRowJustified = stringLength / (stringLength / lettersPerRow + 1) + 1;
483 
484 	talkDelay = (stringLength + 3) / 3;
485 	if (getGameType() == GType_SIMON1 && (getFeatures() & GF_TALKIE)) {
486 		if (_variableArray[141] == 0)
487 			_variableArray[141] = 9;
488 		_variableArray[85] = _variableArray[141] * talkDelay;
489 
490 		if (_language == Common::HE_ISR)
491 			_variableArray[85] += talkDelay * 2;
492 	} else {
493 		if (_variableArray[86] == 0)
494 			talkDelay /= 2;
495 		if (_variableArray[86] == 2)
496 			talkDelay *= 2;
497 		_variableArray[85] = talkDelay * 5;
498 	}
499 
500 	assert(stringLength > 0);
501 
502 	while (stringLength > 0) {
503 		int pos = 0;
504 		if (stringLength > lettersPerRow) {
505 			int removeLastWord = 0;
506 			if (lettersPerRow > lettersPerRowJustified) {
507 				pos = lettersPerRowJustified;
508 				while (string[pos] != ' ')
509 					pos++;
510 				if (pos > lettersPerRow)
511 					removeLastWord = 1;
512 			}
513 			if (lettersPerRow <= lettersPerRowJustified || removeLastWord) {
514 				pos = lettersPerRow;
515 				while (string[pos] != ' ' && pos > 0)
516 					pos--;
517 			}
518 			height += textHeight;
519 			y -= textHeight;
520 		} else
521 			pos = stringLength;
522 		padding = ((lettersPerRow - pos) % 2) ? (lettersPerRow - pos) / 2 + 1 : (lettersPerRow - pos) / 2;
523 		while (padding--)
524 			*convertedString2++ = ' ';
525 		stringLength -= pos;
526 		while (pos--)
527 			*convertedString2++ = *string++;
528 		*convertedString2++ = '\n';
529 		string++; // skip space
530 		stringLength--; // skip space
531 	}
532 	*(convertedString2 - 1) = '\0';
533 
534 	if (getGameType() == GType_SIMON1)
535 		stopAnimate(vgaSpriteId + 199);
536 	else
537 		stopAnimateSimon2(2, vgaSpriteId);
538 
539 	if (getPlatform() == Common::kPlatformAmiga) {
540 		color = color * 3 + 1;
541 		renderStringAmiga(vgaSpriteId, color, width, height, convertedString);
542 	} else {
543 		color = color * 3 + 192;
544 		renderString(vgaSpriteId, color, width, height, convertedString);
545 	}
546 
547 	uint16 windowNum = (!getBitFlag(133)) ? 3 : 4;
548 	if (getGameType() == GType_SIMON1 && (getFeatures() & GF_DEMO))
549 		windowNum = 4;
550 
551 	x /= 8;
552 	if (y < 2)
553 		y = 2;
554 
555 	if (getGameType() == GType_SIMON1) {
556 		uint16 id = 199 + vgaSpriteId;
557 		animate(windowNum, id / 100, id, x, y, 12);
558 	} else {
559 		animate(windowNum, 2, vgaSpriteId, x, y, 12);
560 	}
561 }
562 
563 #ifdef ENABLE_AGOS2
564 // Swampy Adventures specific
printInfoText(const char * itemText)565 void AGOSEngine_PuzzlePack::printInfoText(const char *itemText) {
566 	const char *itemName = NULL;
567 	int flag = (_mouse.y / 32) * 20 + (_mouse.x / 32) + 1300;
568 
569 	switch (_variableArray[999]) {
570 		case 80:
571 			if (_variableArray[flag]) {
572 				if (_variableArray[flag] == 201)
573 					itemName = " Bridge: ";
574 				if (_variableArray[flag] == 231 || _variableArray[flag] == 241)
575 					itemName = " Log: ";
576 				if (_variableArray[flag] == 281)
577 					itemName = " Rubble: ";
578 				if (_variableArray[flag] == 291)
579 					itemName = " Boulder: ";
580 				if (_variableArray[flag] == 311)
581 					itemName = " Key: ";
582 				if (_variableArray[flag] == 312)
583 					itemName = " Spanner: ";
584 				if (_variableArray[flag] == 321)
585 					itemName = " Gate: ";
586 				if (_variableArray[flag] == 331)
587 					itemName = " Crate: ";
588 			} else {
589 				flag -= 300;
590 				if (_variableArray[flag] == 2)
591 					itemName = " Water: ";
592 				if (_variableArray[flag] == 5)
593 					itemName = " Exit: ";
594 				if ((_variableArray[flag] == 5) && (_variableArray[81] == 10))
595 					itemName = " Gem: ";
596 				if (_variableArray[flag] == 236 || _variableArray[flag] == 246)
597 					itemName = " Floating Log: ";
598 				if (_variableArray[flag] == 400)
599 					itemName = " Valve: ";
600 			}
601 			break;
602 
603 		case 81:
604 			if (_variableArray[flag]) {
605 				if (_variableArray[flag] == 281)
606 					itemName = " Cracked Block: ";
607 				if (_variableArray[flag] == 291)
608 					itemName = " Boulder: ";
609 				if (_variableArray[flag] == 331)
610 					itemName = " Block: ";
611 				if (_variableArray[flag] == 341)
612 					itemName = " Switch: ";
613 				if (_variableArray[flag] == 343)
614 					itemName = " Button: ";
615 				if ((_variableArray[flag] > 430) && (_variableArray[flag] < 480))
616 					itemName = " Mosaic Block: ";
617 			} else {
618 				flag -= 300;
619 				if (_variableArray[flag] == 5)
620 					itemName = " Exit: ";
621 				if ((_variableArray[flag] == 5) && (_variableArray[82] == 10))
622 					itemName = " Gem: ";
623 			}
624 			break;
625 
626 		case 82:
627 			if (_variableArray[flag]) {
628 				if (_variableArray[flag] == 201 || _variableArray[flag] == 211)
629 					itemName = " Unstable Track: ";
630 				if (_variableArray[flag] == 281)
631 					itemName = " Rubble Pile: ";
632 				if (_variableArray[flag] == 291)
633 					itemName = " Boulder: ";
634 				if (_variableArray[flag] == 331)
635 					itemName = " Crate: ";
636 				if (_variableArray[flag] == 401 || _variableArray[flag] == 405)
637 					itemName = " Cart: ";
638 			} else {
639 				flag -= 300;
640 				if (_variableArray[flag] == 4)
641 					itemName = " Hole: ";
642 				if (_variableArray[flag] == 5)
643 					itemName = " Exit: ";
644 				if ((_variableArray[flag] == 5) && (_variableArray[83] == 10))
645 					itemName = " Gem: ";
646 				if ((_variableArray[flag] > 5) && (_variableArray[flag] < 10))
647 					itemName = " Buffer Track: ";
648 				if ((_variableArray[flag] > 9) && (_variableArray[flag] < 40))
649 					itemName = " Track: ";
650 				if (_variableArray[flag] == 300)
651 					itemName = " Boulder: ";
652 			}
653 			break;
654 
655 		case 83:
656 			if (_variableArray[flag]) {
657 				if (_variableArray[flag] == 201)
658 					itemName = " Broken Floor: ";
659 				if (_variableArray[flag] == 231 || _variableArray[flag] == 241)
660 					itemName = " Barrel: ";
661 				if (_variableArray[flag] == 281)
662 					itemName = " Cracked Rock: ";
663 				if (_variableArray[flag] == 291)
664 					itemName = " Spacehopper: ";
665 				if (_variableArray[flag] == 311)
666 					itemName = " Key: ";
667 				if (_variableArray[flag] == 321)
668 					itemName = " Trapdoor: ";
669 				if (_variableArray[flag] == 324)
670 					itemName = " Trapdoor: ";
671 				if (_variableArray[flag] == 331)
672 					itemName = " Crate: ";
673 			} else {
674 				flag -= 300;
675 				if (_variableArray[flag] == 4)
676 					itemName = " Hole: ";
677 				if (_variableArray[flag] == 239 || _variableArray[flag] == 249)
678 					itemName = " Barrel: ";
679 			}
680 			break;
681 
682 		case 84:
683 			if (_variableArray[flag]) {
684 				if (_variableArray[flag] == 201)
685 					itemName = " Floating Platform: ";
686 				if (_variableArray[flag] == 231)
687 					itemName = " Cauldron: ";
688 				if (_variableArray[flag] == 281)
689 					itemName = " Cracked Block: ";
690 				if (_variableArray[flag] == 311 || _variableArray[flag] == 312)
691 					itemName = " Key: ";
692 				if (_variableArray[flag] == 321 || _variableArray[flag] == 361 || _variableArray[flag] == 371)
693 					itemName = " Gate: ";
694 				if (_variableArray[flag] == 331)
695 					itemName = " Chest: ";
696 				if (_variableArray[flag] == 332)
697 					itemName = " Jewel: ";
698 				if (_variableArray[flag] == 351 || _variableArray[flag] == 352)
699 					itemName = " Babies: ";
700 			} else {
701 				flag -= 300;
702 				if (_variableArray[flag] == 6)
703 					itemName = " Slime: ";
704 				if (_variableArray[flag] == 334)
705 					itemName = " Chest: ";
706 			}
707 			break;
708 
709 		default:
710 			break;
711 	}
712 
713 	if (itemName != NULL) {
714 		Common::String buf = Common::String::format("%s\n%s", itemName, itemText);
715 		GUI::TimedMessageDialog dialog(buf, 1500);
716 		dialog.runModal();
717 	}
718 }
719 
720 // The Feeble Files specific
printScreenText(uint vgaSpriteId,uint color,const char * string,int16 x,int16 y,int16 width)721 void AGOSEngine_Feeble::printScreenText(uint vgaSpriteId, uint color, const char *string, int16 x, int16 y, int16 width) {
722 	char convertedString[320];
723 	char *convertedString2 = convertedString;
724 	const char *string2 = string;
725 	int16 height, talkDelay;
726 	int stringLength = strlen(string);
727 	const int textHeight = 15;
728 
729 	height = textHeight;
730 
731 	talkDelay = (stringLength + 3) / 3;
732 		if (_variableArray[86] == 0)
733 			talkDelay /= 2;
734 		if (_variableArray[86] == 2)
735 			talkDelay *= 2;
736 		_variableArray[85] = talkDelay * 5;
737 
738 	assert(stringLength > 0);
739 
740 	uint16 b, pixels, spaces;
741 
742 	while (1) {
743 		string2 = getPixelLength(string, width, pixels);
744 		if (*string2 == 0) {
745 			spaces = (width - pixels) / 12;
746 			if (spaces != 0)
747 				spaces--;
748 			while (spaces) {
749 					*convertedString2++ = ' ';
750 					spaces--;
751 			}
752 			strcpy(convertedString2, string);
753 			break;
754 		}
755 		while (*string2 != ' ') {
756 			byte chr = *string2;
757 			pixels -= (_language == Common::PL_POL) ? polish_charWidth[chr] : charWidth[chr];
758 			string2--;
759 		}
760 		spaces = (width - pixels) / 12;
761 		if (spaces != 0)
762 			spaces--;
763 		while (spaces) {
764 				*convertedString2++ = ' ';
765 				spaces--;
766 		}
767 		b = string2 - string;
768 		strncpy(convertedString2, string, b);
769 		convertedString2 += b;
770 		*convertedString2++ = '\n';
771 		height += textHeight;
772 		y -= textHeight;
773 		if (y < 2)
774 			y = 2;
775 		string = string2;
776 	}
777 
778 	stopAnimateSimon2(2, vgaSpriteId);
779 
780 	renderString(1, color, width, height, convertedString);
781 
782 	animate(4, 2, vgaSpriteId, x, y, 12);
783 }
784 
printInteractText(uint16 num,const char * string)785 void AGOSEngine_Feeble::printInteractText(uint16 num, const char *string) {
786 	char convertedString[320];
787 	char *convertedString2 = convertedString;
788 	const char *string2 = string;
789 	uint16 height = 15;
790 	uint16 w = 0xFFFF;
791 	uint16 b, pixels, x;
792 
793 	// It doesn't really matter what 'w' is to begin with, as long as it's
794 	// something that cannot be generated by getPixelLength(). The original
795 	// used 620, which was a potential problem.
796 
797 	while (1) {
798 		string2 = getPixelLength(string, 620, pixels);
799 		if (*string2 == 0x00) {
800 			if (w == 0xFFFF)
801 				w = pixels;
802 			Common::strlcpy(convertedString2, string, 320);
803 			break;
804 		}
805 		while (*string2 != ' ') {
806 			byte chr = *string2;
807 			pixels -= (_language == Common::PL_POL) ? polish_charWidth[chr] : charWidth[chr];
808 			string2--;
809 		}
810 		if (w == 0xFFFF)
811 			w = pixels;
812 		b = string2 - string;
813 		strncpy(convertedString2, string, b);
814 		convertedString2 += b;
815 		*convertedString2++ = '\n';
816 		height += 15;
817 		string = string2;
818 	}
819 
820 	// ScrollX
821 	x = _variableArray[251];
822 	x += 20;
823 
824 	if (num == 1)
825 		_interactY = 385;
826 
827 	// Returned values for box definition
828 	_variableArray[51] = x;
829 	_variableArray[52] = _interactY;
830 	_variableArray[53] = w;
831 	_variableArray[54] = height;
832 
833 	stopAnimateSimon2(2, num + 6);
834 	renderString(num, 0, w, height, convertedString);
835 	animate(4, 2, num + 6, x, _interactY, 12);
836 
837 	_interactY += height;
838 }
839 
sendInteractText(uint16 num,const char * fmt,...)840 void AGOSEngine_Feeble::sendInteractText(uint16 num, const char *fmt, ...) {
841 	va_list arglist;
842 
843 	va_start(arglist, fmt);
844 	Common::String string = Common::String::vformat(fmt, arglist);
845 	va_end(arglist);
846 
847 	printInteractText(num, string.c_str());
848 }
849 #endif
850 
851 // Waxworks specific
getBoxSize()852 uint16 AGOSEngine_Waxworks::getBoxSize() {
853 	int x;
854 	switch (_boxLineCount) {
855 	case 1: x = _lineCounts[0];
856 		if (x <= 26)
857 			return 1;
858 		if (x <= 64)
859 			if (checkFit(_linePtrs[0], 32, 2))
860 				return 2;
861 		if (x <= 111)
862 			if (checkFit(_linePtrs[0], 37, 3))
863 				return 3;
864 		if (x <= 168)
865 			if (checkFit(_linePtrs[0], 42, 4))
866 				return 4;
867 		if (x <= 240)
868 			if (checkFit(_linePtrs[0], 48, 5))
869 				return 5;
870 		return 6;
871 	case 2: if (_lineCounts[0] <= 32) {
872 			if (_lineCounts[1] <= 32)
873 				return 2;
874 			if (_lineCounts[1] <= 74)
875 				if (checkFit(_linePtrs[1], 37, 2))
876 					return 3;
877 			if (_lineCounts[1] <= 126)
878 				if (checkFit(_linePtrs[1], 42, 3))
879 					return 4;
880 			if (_lineCounts[1] <= 172)
881 				if (checkFit(_linePtrs[1], 48, 4))
882 					return 5;
883 			return 6;
884 		}
885 		if ((_lineCounts[0] <= 74) && (checkFit(_linePtrs[0], 37, 2))) {
886 			if (_lineCounts[1] <= 37)
887 				return 3;
888 			if (_lineCounts[1] <= 84)
889 				if (checkFit(_linePtrs[1], 42, 2))
890 					return 4;
891 			if (_lineCounts[1] <= 144)
892 				if (checkFit(_linePtrs[1], 48, 3))
893 					return 5;
894 			return 6;
895 		}
896 		if ((_lineCounts[0] <= 126) && (checkFit(_linePtrs[0], 42, 3))) {
897 			if (_lineCounts[1] <= 42)
898 				return 4;
899 			if (_lineCounts[1] <= 84)
900 				if (checkFit(_linePtrs[1], 48, 2))
901 					return 5;
902 			return 6;
903 		}
904 		if ((_lineCounts[0] <= 192) && (checkFit(_linePtrs[0], 48, 4))) {
905 			if (_lineCounts[1] <= 48)
906 				return 5;
907 			return 6;
908 		}
909 		return 6;
910 	case 3: if (_lineCounts[0] <= 37) {
911 			if (_lineCounts[1] <= 37) {
912 				if (_lineCounts[2] <= 37)
913 						return 3;
914 				if (_lineCounts[2] <= 84)
915 					if (checkFit(_linePtrs[2], 42, 2))
916 						return 4;
917 				if (_lineCounts[2] <= 144)
918 					if (checkFit(_linePtrs[2], 48, 3))
919 						return 5;
920 				return 6;
921 			}
922 			if ((_lineCounts[1] <= 84) && (checkFit(_linePtrs[1], 42, 2))) {
923 				if (_lineCounts[2] <= 42)
924 					return 4;
925 				if (_lineCounts[2] <= 96)
926 					if (checkFit(_linePtrs[2], 48, 2))
927 						return 5;
928 				return 6;
929 			}
930 			if ((_lineCounts[1] <= 144) && (checkFit(_linePtrs[1], 48, 3))) {
931 				if (_lineCounts[2] <= 48)
932 					return 5;
933 				return 6;
934 			}
935 			return 6;
936 		}
937 		if ((_lineCounts[0] <= 84) && (checkFit(_linePtrs[0], 42, 2))) {
938 			if (_lineCounts[1] <= 42) {
939 				if (_lineCounts[2] <= 42)
940 					return 4;
941 				if (_lineCounts[2] <= 96)
942 					if (checkFit(_linePtrs[2], 48, 2))
943 						return 5;
944 				return 6;
945 			}
946 			if ((_lineCounts[1] <= 96) && (checkFit(_linePtrs[1], 48, 2))) {
947 				if (_lineCounts[2] <= 48)
948 					return 5;
949 				return 6;
950 			}
951 			return 6;
952 		}
953 		if ((_lineCounts[0] <= 96) && (checkFit(_linePtrs[0], 48, 3))) {
954 			if (_lineCounts[1] <= 48) {
955 				if (_lineCounts[2] <= 48)
956 					return 5;
957 			}
958 			return 6;
959 		}
960 		return 6;
961 	case 4: if (_lineCounts[0] <= 42) {
962 			if (_lineCounts[1] <= 42) {
963 				if (_lineCounts[2] <= 42) {
964 					if (_lineCounts[3] <= 42)
965 						return 4;
966 					if (_lineCounts[3] <= 96)
967 						if (checkFit(_linePtrs[3], 48, 2))
968 							return 5;
969 					return 6;
970 				}
971 				if ((_lineCounts[2] <= 96) && (checkFit(_linePtrs[2], 48, 2)))
972 					if (_lineCounts[3] <= 48)
973 						return 5;
974 				return 6;
975 			}
976 			if ((_lineCounts[1] <= 96) && (checkFit(_linePtrs[1], 48, 2)))
977 					if ((_lineCounts[2] <= 48) && (_lineCounts[3] <= 48))
978 						return 5;
979 			return 6;
980 		}
981 		if ((_lineCounts[0] <= 96) && (checkFit(_linePtrs[0], 48, 2)))
982 			if ((_lineCounts[1] <= 48) && (_lineCounts[2] <= 48) && (_lineCounts[3] <= 48))
983 				return 5;
984 		return 6;
985 	case 5: if ((_lineCounts[0] > 48) || (_lineCounts[1] > 48) || (_lineCounts[2] > 48)
986 			|| (_lineCounts[3] > 48) || (_lineCounts[4] > 48))
987 			return 6;
988 		else
989 			return 5;
990 	default:
991 		return 6;
992 	}
993 }
994 
995 
checkFit(char * ptr,int width,int lines)996 uint16 AGOSEngine_Waxworks::checkFit(char *ptr, int width, int lines) {
997 	int countw = 0;
998 	int countl = 0;
999 	char *x = NULL;
1000 	while (*ptr) {
1001 		if (*ptr == '\n')
1002 			return 1;
1003 		if (countw == width) {
1004 			countl++;
1005 			countw = 0;
1006 			ptr = x;
1007 		}
1008 		if (*ptr == ' ') {
1009 			x = ptr;
1010 			x++;
1011 		}
1012 		countw++;
1013 		if (countl == lines)
1014 			return 0;
1015 		ptr++;
1016 	}
1017 
1018 	return 1;
1019 }
1020 
boxTextMessage(const char * x)1021 void AGOSEngine_Waxworks::boxTextMessage(const char *x) {
1022 	sprintf(_boxBufferPtr, "%s\n", x);
1023 	_lineCounts[_boxLineCount] += strlen(x);
1024 	_boxBufferPtr += strlen(x) + 1;
1025 	_boxLineCount++;
1026 	_linePtrs[_boxLineCount] = _boxBufferPtr;
1027 	_boxCR = 1;
1028 }
1029 
boxTextMsg(const char * x)1030 void AGOSEngine_Waxworks::boxTextMsg(const char *x) {
1031 	sprintf(_boxBufferPtr, "%s", x);
1032 	_lineCounts[_boxLineCount] += strlen(x);
1033 	_boxBufferPtr += strlen(x);
1034 	_boxCR = 0;
1035 }
1036 
printBox()1037 void AGOSEngine_Waxworks::printBox() {
1038 	uint16 BoxSize;
1039 
1040 	*_boxBufferPtr = 0;
1041 	_linePtrs[0] = _boxBuffer;
1042 	if (_boxCR == 0)
1043 		_boxLineCount++;
1044 	stopAnimate(105);
1045 	BoxSize = getBoxSize();
1046 	_variableArray[53] = BoxSize;
1047 	animate(3, 1, 100, 0, 0, 0);
1048 	changeWindow(5);
1049 
1050 	switch (BoxSize) {
1051 	case 1: _textWindow->x = 10;
1052 		_textWindow->y = 163;
1053 		_textWindow->width = 20;
1054 		_textWindow->height = 1;
1055 		_textWindow->textMaxLength = 26;
1056 		break;
1057 	case 2: _textWindow->x = 8;
1058 		_textWindow->y = 160;
1059 		_textWindow->width = 24;
1060 		_textWindow->height = 2;
1061 		_textWindow->textMaxLength = 32;
1062 		break;
1063 	case 3: _textWindow->x = 6;
1064 		_textWindow->y = 156;
1065 		_textWindow->width = 28;
1066 		_textWindow->height = 3;
1067 		_textWindow->textMaxLength = 37;
1068 		break;
1069 	case 4: _textWindow->x = 4;
1070 		_textWindow->y = 153;
1071 		_textWindow->width = 32;
1072 		_textWindow->height = 4;
1073 		_textWindow->textMaxLength = 42;
1074 		break;
1075 	case 5: _textWindow->x = 2;
1076 		_textWindow->y = 150;
1077 		_textWindow->width = 36;
1078 		_textWindow->height = 5;
1079 		_textWindow->textMaxLength = 48;
1080 		break;
1081 	default:_textWindow->x = 1;
1082 		_textWindow->y = 147;
1083 		_textWindow->width = 38;
1084 		_textWindow->height = 6;
1085 		_textWindow->textMaxLength = 50;
1086 		break;
1087 	}
1088 	_textWindow->textColumn = 0;
1089 	_textWindow->textRow = 0;
1090 	_textWindow->textColumnOffset = 0;
1091 	_textWindow->textLength = 0;
1092 	justifyStart();
1093 	waitForSync(99);
1094 	_boxBufferPtr = _boxBuffer;
1095 	while (*_boxBufferPtr)
1096 		justifyOutPut(*_boxBufferPtr++);
1097 	_boxLineCount = 0;
1098 	_boxBufferPtr = _boxBuffer;
1099 	_lineCounts[0] = 0;
1100 	_lineCounts[1] = 0;
1101 	_lineCounts[2] = 0;
1102 	_lineCounts[3] = 0;
1103 	_lineCounts[4] = 0;
1104 	_lineCounts[5] = 0;
1105 	changeWindow(0);
1106 }
1107 
1108 } // End of namespace AGOS
1109