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 #2780: 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 			alignTableMem();
336 
337 			if (_tablesHeapCurPos > _tablesHeapSize) {
338 				error("loadTextIntoMem: Out of table memory");
339 			}
340 			return;
341 		}
342 
343 		baseMin = baseMax;
344 	}
345 
346 	error("loadTextIntoMem: didn't find %d", stringId);
347 }
348 
349 static const byte polish_charWidth[226] = {
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, 0, 0, 0, 0, 0, 0, 0, 0,
353 	0, 0, 6, 2, 8, 7, 6,10, 8, 2,
354 	4, 4, 7, 6, 3, 4, 2, 3, 6, 4,
355 	6, 6, 7, 6, 6, 6, 6, 6, 2, 8,
356 	6, 9, 7, 6, 6, 8, 7, 8, 8, 7,
357 	6, 9, 8, 2, 6, 7, 6,10, 8, 9,
358 	7, 9, 7, 7, 8, 8, 8,12, 8, 8,
359 	7, 6, 7, 6, 4, 7, 7, 7, 7, 6,
360 	7, 7, 4, 7, 6, 2, 3, 6, 2,10,
361 	6, 7, 7, 7, 5, 6, 4, 6, 6,10,
362 	6, 6, 6, 0, 0, 0, 0, 0, 8, 6,
363 	7, 7, 7, 7, 7, 6, 7, 7, 7, 4,
364 	4, 3, 8, 8, 7, 0, 0, 7, 7, 7,
365 	6, 6, 6, 9, 8, 0, 0, 0, 0, 0,
366 	7, 3, 7, 6, 6, 8, 0, 0, 6, 0,
367 	0, 0, 0, 2, 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, 0, 0, 0, 0, 0,
372 	0, 0, 0, 0, 0, 7
373 };
374 
375 static const byte charWidth[226] = {
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, 0, 0, 0, 0, 0, 0, 0, 0,
379 	0, 0, 6, 2, 4, 8, 6,10, 9, 2,
380 	4, 4, 6, 6, 3, 4, 2, 3, 6, 4,
381 	6, 6, 7, 6, 6, 6, 6, 6, 2, 3,
382 	7, 7, 7, 6,11, 8, 7, 8, 8, 7,
383 	6, 9, 8, 2, 6, 7, 6,10, 8, 9,
384 	7, 9, 7, 7, 8, 8, 8,12, 8, 8,
385 	7, 3, 3, 3, 6, 8, 3, 7, 7, 6,
386 	7, 7, 4, 7, 6, 2, 3, 6, 2,10,
387 	6, 7, 7, 7, 5, 6, 4, 7, 7,10,
388 	6, 6, 6, 0, 0, 0, 0, 0, 8, 6,
389 	7, 7, 7, 7, 7, 6, 7, 7, 7, 4,
390 	4, 3, 8, 8, 7, 0, 0, 7, 7, 7,
391 	6, 6, 6, 9, 8, 0, 0, 0, 0, 0,
392 	7, 3, 7, 6, 6, 8, 0, 6, 0, 0,
393 	0, 0, 0, 2, 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, 0, 0, 0, 0, 0,
398 	0, 0, 0, 0, 0, 7
399 };
400 
getPixelLength(const char * string,uint16 maxWidth,uint16 & pixels)401 const char *AGOSEngine::getPixelLength(const char *string, uint16 maxWidth, uint16 &pixels) {
402 	pixels = 0;
403 
404 	while (*string != 0) {
405 		byte chr = *string;
406 		uint8 len = (_language == Common::PL_POL) ? polish_charWidth[chr] : charWidth[chr];
407 		if ((pixels + len) > maxWidth)
408 			break;
409 		pixels += len;
410 		string++;
411 	}
412 
413 	return string;
414 }
415 
printTextOf(uint a,uint x,uint y)416 bool AGOSEngine::printTextOf(uint a, uint x, uint y) {
417 	const byte *stringPtr;
418 	uint16 pixels, w;
419 
420 	if (getGameType() == GType_SIMON2) {
421 		if (getBitFlag(79)) {
422 			Subroutine *sub;
423 			_variableArray[84] = a;
424 			sub = getSubroutineByID(5003);
425 			if (sub != NULL)
426 				startSubroutineEx(sub);
427 			return true;
428 		}
429 	}
430 
431 	if (a >= _numTextBoxes)
432 		return false;
433 
434 
435 	stringPtr = getStringPtrByID(_shortText[a]);
436 	if (getGameType() == GType_FF) {
437 		getPixelLength((const char *)stringPtr, 400, pixels);
438 		w = pixels + 1;
439 		x -= w / 2;
440 		printScreenText(6, 0, (const char *)stringPtr, x, y, w);
441 	} else {
442 		showActionString(stringPtr);
443 	}
444 
445 	return true;
446 }
447 
printNameOf(Item * item,uint x,uint y)448 bool AGOSEngine::printNameOf(Item *item, uint x, uint y) {
449 	SubObject *subObject;
450 	const byte *stringPtr;
451 	uint16 pixels, w;
452 
453 	if (item == 0 || item == _dummyItem2 || item == _dummyItem3)
454 		return false;
455 
456 	subObject = (SubObject *)findChildOfType(item, kObjectType);
457 	if (subObject == NULL)
458 		return false;
459 
460 	stringPtr = getStringPtrByID(subObject->objectName);
461 	if (getGameType() == GType_FF) {
462 		getPixelLength((const char *)stringPtr, 400, pixels);
463 		w = pixels + 1;
464 		x -= w / 2;
465 		printScreenText(6, 0, (const char *)stringPtr, x, y, w);
466 	} else {
467 		showActionString(stringPtr);
468 	}
469 
470 	return true;
471 }
472 
printScreenText(uint vgaSpriteId,uint color,const char * string,int16 x,int16 y,int16 width)473 void AGOSEngine::printScreenText(uint vgaSpriteId, uint color, const char *string, int16 x, int16 y, int16 width) {
474 	char convertedString[320];
475 	char *convertedString2 = convertedString;
476 	int16 height, talkDelay;
477 	int stringLength = strlen(string);
478 	int padding, lettersPerRow, lettersPerRowJustified;
479 	const int textHeight = 10;
480 
481 	height = textHeight;
482 	lettersPerRow = width / 6;
483 	lettersPerRowJustified = stringLength / (stringLength / lettersPerRow + 1) + 1;
484 
485 	talkDelay = (stringLength + 3) / 3;
486 	if (getGameType() == GType_SIMON1 && (getFeatures() & GF_TALKIE)) {
487 		if (_variableArray[141] == 0)
488 			_variableArray[141] = 9;
489 		_variableArray[85] = _variableArray[141] * talkDelay;
490 
491 		if (_language == Common::HE_ISR)
492 			_variableArray[85] += talkDelay * 2;
493 	} else {
494 		if (_variableArray[86] == 0)
495 			talkDelay /= 2;
496 		if (_variableArray[86] == 2)
497 			talkDelay *= 2;
498 		_variableArray[85] = talkDelay * 5;
499 	}
500 
501 	assert(stringLength > 0);
502 
503 	while (stringLength > 0) {
504 		int pos = 0;
505 		if (stringLength > lettersPerRow) {
506 			int removeLastWord = 0;
507 			if (lettersPerRow > lettersPerRowJustified) {
508 				pos = lettersPerRowJustified;
509 				while (string[pos] != ' ')
510 					pos++;
511 				if (pos > lettersPerRow)
512 					removeLastWord = 1;
513 			}
514 			if (lettersPerRow <= lettersPerRowJustified || removeLastWord) {
515 				pos = lettersPerRow;
516 				while (string[pos] != ' ' && pos > 0)
517 					pos--;
518 			}
519 			height += textHeight;
520 			y -= textHeight;
521 		} else
522 			pos = stringLength;
523 		padding = ((lettersPerRow - pos) % 2) ? (lettersPerRow - pos) / 2 + 1 : (lettersPerRow - pos) / 2;
524 		while (padding--)
525 			*convertedString2++ = ' ';
526 		stringLength -= pos;
527 		while (pos--)
528 			*convertedString2++ = *string++;
529 		*convertedString2++ = '\n';
530 		string++; // skip space
531 		stringLength--; // skip space
532 	}
533 	*(convertedString2 - 1) = '\0';
534 
535 	if (getGameType() == GType_SIMON1)
536 		stopAnimate(vgaSpriteId + 199);
537 	else
538 		stopAnimateSimon2(2, vgaSpriteId);
539 
540 	if (getPlatform() == Common::kPlatformAmiga) {
541 		color = color * 3 + 1;
542 		renderStringAmiga(vgaSpriteId, color, width, height, convertedString);
543 	} else {
544 		color = color * 3 + 192;
545 		renderString(vgaSpriteId, color, width, height, convertedString);
546 	}
547 
548 	uint16 windowNum = (!getBitFlag(133)) ? 3 : 4;
549 	if (getGameType() == GType_SIMON1 && (getFeatures() & GF_DEMO))
550 		windowNum = 4;
551 
552 	x /= 8;
553 	if (y < 2)
554 		y = 2;
555 
556 	if (getGameType() == GType_SIMON1) {
557 		uint16 id = 199 + vgaSpriteId;
558 		animate(windowNum, id / 100, id, x, y, 12);
559 	} else {
560 		animate(windowNum, 2, vgaSpriteId, x, y, 12);
561 	}
562 }
563 
564 #ifdef ENABLE_AGOS2
565 // Swampy Adventures specific
printInfoText(const char * itemText)566 void AGOSEngine_PuzzlePack::printInfoText(const char *itemText) {
567 	const char *itemName = NULL;
568 	int flag = (_mouse.y / 32) * 20 + (_mouse.x / 32) + 1300;
569 
570 	switch (_variableArray[999]) {
571 		case 80:
572 			if (_variableArray[flag]) {
573 				if (_variableArray[flag] == 201)
574 					itemName = " Bridge: ";
575 				if (_variableArray[flag] == 231 || _variableArray[flag] == 241)
576 					itemName = " Log: ";
577 				if (_variableArray[flag] == 281)
578 					itemName = " Rubble: ";
579 				if (_variableArray[flag] == 291)
580 					itemName = " Boulder: ";
581 				if (_variableArray[flag] == 311)
582 					itemName = " Key: ";
583 				if (_variableArray[flag] == 312)
584 					itemName = " Spanner: ";
585 				if (_variableArray[flag] == 321)
586 					itemName = " Gate: ";
587 				if (_variableArray[flag] == 331)
588 					itemName = " Crate: ";
589 			} else {
590 				flag -= 300;
591 				if (_variableArray[flag] == 2)
592 					itemName = " Water: ";
593 				if (_variableArray[flag] == 5)
594 					itemName = " Exit: ";
595 				if ((_variableArray[flag] == 5) && (_variableArray[81] == 10))
596 					itemName = " Gem: ";
597 				if (_variableArray[flag] == 236 || _variableArray[flag] == 246)
598 					itemName = " Floating Log: ";
599 				if (_variableArray[flag] == 400)
600 					itemName = " Valve: ";
601 			}
602 			break;
603 
604 		case 81:
605 			if (_variableArray[flag]) {
606 				if (_variableArray[flag] == 281)
607 					itemName = " Cracked Block: ";
608 				if (_variableArray[flag] == 291)
609 					itemName = " Boulder: ";
610 				if (_variableArray[flag] == 331)
611 					itemName = " Block: ";
612 				if (_variableArray[flag] == 341)
613 					itemName = " Switch: ";
614 				if (_variableArray[flag] == 343)
615 					itemName = " Button: ";
616 				if ((_variableArray[flag] > 430) && (_variableArray[flag] < 480))
617 					itemName = " Mosaic Block: ";
618 			} else {
619 				flag -= 300;
620 				if (_variableArray[flag] == 5)
621 					itemName = " Exit: ";
622 				if ((_variableArray[flag] == 5) && (_variableArray[82] == 10))
623 					itemName = " Gem: ";
624 			}
625 			break;
626 
627 		case 82:
628 			if (_variableArray[flag]) {
629 				if (_variableArray[flag] == 201 || _variableArray[flag] == 211)
630 					itemName = " Unstable Track: ";
631 				if (_variableArray[flag] == 281)
632 					itemName = " Rubble Pile: ";
633 				if (_variableArray[flag] == 291)
634 					itemName = " Boulder: ";
635 				if (_variableArray[flag] == 331)
636 					itemName = " Crate: ";
637 				if (_variableArray[flag] == 401 || _variableArray[flag] == 405)
638 					itemName = " Cart: ";
639 			} else {
640 				flag -= 300;
641 				if (_variableArray[flag] == 4)
642 					itemName = " Hole: ";
643 				if (_variableArray[flag] == 5)
644 					itemName = " Exit: ";
645 				if ((_variableArray[flag] == 5) && (_variableArray[83] == 10))
646 					itemName = " Gem: ";
647 				if ((_variableArray[flag] > 5) && (_variableArray[flag] < 10))
648 					itemName = " Buffer Track: ";
649 				if ((_variableArray[flag] > 9) && (_variableArray[flag] < 40))
650 					itemName = " Track: ";
651 				if (_variableArray[flag] == 300)
652 					itemName = " Boulder: ";
653 			}
654 			break;
655 
656 		case 83:
657 			if (_variableArray[flag]) {
658 				if (_variableArray[flag] == 201)
659 					itemName = " Broken Floor: ";
660 				if (_variableArray[flag] == 231 || _variableArray[flag] == 241)
661 					itemName = " Barrel: ";
662 				if (_variableArray[flag] == 281)
663 					itemName = " Cracked Rock: ";
664 				if (_variableArray[flag] == 291)
665 					itemName = " Spacehopper: ";
666 				if (_variableArray[flag] == 311)
667 					itemName = " Key: ";
668 				if (_variableArray[flag] == 321)
669 					itemName = " Trapdoor: ";
670 				if (_variableArray[flag] == 324)
671 					itemName = " Trapdoor: ";
672 				if (_variableArray[flag] == 331)
673 					itemName = " Crate: ";
674 			} else {
675 				flag -= 300;
676 				if (_variableArray[flag] == 4)
677 					itemName = " Hole: ";
678 				if (_variableArray[flag] == 239 || _variableArray[flag] == 249)
679 					itemName = " Barrel: ";
680 			}
681 			break;
682 
683 		case 84:
684 			if (_variableArray[flag]) {
685 				if (_variableArray[flag] == 201)
686 					itemName = " Floating Platform: ";
687 				if (_variableArray[flag] == 231)
688 					itemName = " Cauldron: ";
689 				if (_variableArray[flag] == 281)
690 					itemName = " Cracked Block: ";
691 				if (_variableArray[flag] == 311 || _variableArray[flag] == 312)
692 					itemName = " Key: ";
693 				if (_variableArray[flag] == 321 || _variableArray[flag] == 361 || _variableArray[flag] == 371)
694 					itemName = " Gate: ";
695 				if (_variableArray[flag] == 331)
696 					itemName = " Chest: ";
697 				if (_variableArray[flag] == 332)
698 					itemName = " Jewel: ";
699 				if (_variableArray[flag] == 351 || _variableArray[flag] == 352)
700 					itemName = " Babies: ";
701 			} else {
702 				flag -= 300;
703 				if (_variableArray[flag] == 6)
704 					itemName = " Slime: ";
705 				if (_variableArray[flag] == 334)
706 					itemName = " Chest: ";
707 			}
708 			break;
709 
710 		default:
711 			break;
712 	}
713 
714 	if (itemName != NULL) {
715 		Common::String buf = Common::String::format("%s\n%s", itemName, itemText);
716 		GUI::TimedMessageDialog dialog(buf, 1500);
717 		dialog.runModal();
718 	}
719 }
720 
721 // The Feeble Files specific
printScreenText(uint vgaSpriteId,uint color,const char * string,int16 x,int16 y,int16 width)722 void AGOSEngine_Feeble::printScreenText(uint vgaSpriteId, uint color, const char *string, int16 x, int16 y, int16 width) {
723 	char convertedString[320];
724 	char *convertedString2 = convertedString;
725 	const char *string2 = string;
726 	int16 height, talkDelay;
727 	int stringLength = strlen(string);
728 	const int textHeight = 15;
729 
730 	height = textHeight;
731 
732 	talkDelay = (stringLength + 3) / 3;
733 		if (_variableArray[86] == 0)
734 			talkDelay /= 2;
735 		if (_variableArray[86] == 2)
736 			talkDelay *= 2;
737 		_variableArray[85] = talkDelay * 5;
738 
739 	assert(stringLength > 0);
740 
741 	uint16 b, pixels, spaces;
742 
743 	while (1) {
744 		string2 = getPixelLength(string, width, pixels);
745 		if (*string2 == 0) {
746 			spaces = (width - pixels) / 12;
747 			if (spaces != 0)
748 				spaces--;
749 			while (spaces) {
750 					*convertedString2++ = ' ';
751 					spaces--;
752 			}
753 			strcpy(convertedString2, string);
754 			break;
755 		}
756 		while (*string2 != ' ') {
757 			byte chr = *string2;
758 			pixels -= (_language == Common::PL_POL) ? polish_charWidth[chr] : charWidth[chr];
759 			string2--;
760 		}
761 		spaces = (width - pixels) / 12;
762 		if (spaces != 0)
763 			spaces--;
764 		while (spaces) {
765 				*convertedString2++ = ' ';
766 				spaces--;
767 		}
768 		b = string2 - string;
769 		strncpy(convertedString2, string, b);
770 		convertedString2 += b;
771 		*convertedString2++ = '\n';
772 		height += textHeight;
773 		y -= textHeight;
774 		if (y < 2)
775 			y = 2;
776 		string = string2;
777 	}
778 
779 	stopAnimateSimon2(2, vgaSpriteId);
780 
781 	renderString(1, color, width, height, convertedString);
782 
783 	animate(4, 2, vgaSpriteId, x, y, 12);
784 }
785 
printInteractText(uint16 num,const char * string)786 void AGOSEngine_Feeble::printInteractText(uint16 num, const char *string) {
787 	char convertedString[320];
788 	char *convertedString2 = convertedString;
789 	const char *string2 = string;
790 	uint16 height = 15;
791 	uint16 w = 0xFFFF;
792 	uint16 b, pixels, x;
793 
794 	// It doesn't really matter what 'w' is to begin with, as long as it's
795 	// something that cannot be generated by getPixelLength(). The original
796 	// used 620, which was a potential problem.
797 
798 	while (1) {
799 		string2 = getPixelLength(string, 620, pixels);
800 		if (*string2 == 0x00) {
801 			if (w == 0xFFFF)
802 				w = pixels;
803 			Common::strlcpy(convertedString2, string, 320);
804 			break;
805 		}
806 		while (*string2 != ' ') {
807 			byte chr = *string2;
808 			pixels -= (_language == Common::PL_POL) ? polish_charWidth[chr] : charWidth[chr];
809 			string2--;
810 		}
811 		if (w == 0xFFFF)
812 			w = pixels;
813 		b = string2 - string;
814 		strncpy(convertedString2, string, b);
815 		convertedString2 += b;
816 		*convertedString2++ = '\n';
817 		height += 15;
818 		string = string2;
819 	}
820 
821 	// ScrollX
822 	x = _variableArray[251];
823 	x += 20;
824 
825 	if (num == 1)
826 		_interactY = 385;
827 
828 	// Returned values for box definition
829 	_variableArray[51] = x;
830 	_variableArray[52] = _interactY;
831 	_variableArray[53] = w;
832 	_variableArray[54] = height;
833 
834 	stopAnimateSimon2(2, num + 6);
835 	renderString(num, 0, w, height, convertedString);
836 	animate(4, 2, num + 6, x, _interactY, 12);
837 
838 	_interactY += height;
839 }
840 
sendInteractText(uint16 num,const char * fmt,...)841 void AGOSEngine_Feeble::sendInteractText(uint16 num, const char *fmt, ...) {
842 	va_list arglist;
843 
844 	va_start(arglist, fmt);
845 	Common::String string = Common::String::vformat(fmt, arglist);
846 	va_end(arglist);
847 
848 	printInteractText(num, string.c_str());
849 }
850 #endif
851 
852 // Waxworks specific
getBoxSize()853 uint16 AGOSEngine_Waxworks::getBoxSize() {
854 	int x;
855 	switch (_boxLineCount) {
856 	case 1: x = _lineCounts[0];
857 		if (x <= 26)
858 			return 1;
859 		if (x <= 64)
860 			if (checkFit(_linePtrs[0], 32, 2))
861 				return 2;
862 		if (x <= 111)
863 			if (checkFit(_linePtrs[0], 37, 3))
864 				return 3;
865 		if (x <= 168)
866 			if (checkFit(_linePtrs[0], 42, 4))
867 				return 4;
868 		if (x <= 240)
869 			if (checkFit(_linePtrs[0], 48, 5))
870 				return 5;
871 		return 6;
872 	case 2: if (_lineCounts[0] <= 32) {
873 			if (_lineCounts[1] <= 32)
874 				return 2;
875 			if (_lineCounts[1] <= 74)
876 				if (checkFit(_linePtrs[1], 37, 2))
877 					return 3;
878 			if (_lineCounts[1] <= 126)
879 				if (checkFit(_linePtrs[1], 42, 3))
880 					return 4;
881 			if (_lineCounts[1] <= 172)
882 				if (checkFit(_linePtrs[1], 48, 4))
883 					return 5;
884 			return 6;
885 		}
886 		if ((_lineCounts[0] <= 74) && (checkFit(_linePtrs[0], 37, 2))) {
887 			if (_lineCounts[1] <= 37)
888 				return 3;
889 			if (_lineCounts[1] <= 84)
890 				if (checkFit(_linePtrs[1], 42, 2))
891 					return 4;
892 			if (_lineCounts[1] <= 144)
893 				if (checkFit(_linePtrs[1], 48, 3))
894 					return 5;
895 			return 6;
896 		}
897 		if ((_lineCounts[0] <= 126) && (checkFit(_linePtrs[0], 42, 3))) {
898 			if (_lineCounts[1] <= 42)
899 				return 4;
900 			if (_lineCounts[1] <= 84)
901 				if (checkFit(_linePtrs[1], 48, 2))
902 					return 5;
903 			return 6;
904 		}
905 		if ((_lineCounts[0] <= 192) && (checkFit(_linePtrs[0], 48, 4))) {
906 			if (_lineCounts[1] <= 48)
907 				return 5;
908 			return 6;
909 		}
910 		return 6;
911 	case 3: if (_lineCounts[0] <= 37) {
912 			if (_lineCounts[1] <= 37) {
913 				if (_lineCounts[2] <= 37)
914 						return 3;
915 				if (_lineCounts[2] <= 84)
916 					if (checkFit(_linePtrs[2], 42, 2))
917 						return 4;
918 				if (_lineCounts[2] <= 144)
919 					if (checkFit(_linePtrs[2], 48, 3))
920 						return 5;
921 				return 6;
922 			}
923 			if ((_lineCounts[1] <= 84) && (checkFit(_linePtrs[1], 42, 2))) {
924 				if (_lineCounts[2] <= 42)
925 					return 4;
926 				if (_lineCounts[2] <= 96)
927 					if (checkFit(_linePtrs[2], 48, 2))
928 						return 5;
929 				return 6;
930 			}
931 			if ((_lineCounts[1] <= 144) && (checkFit(_linePtrs[1], 48, 3))) {
932 				if (_lineCounts[2] <= 48)
933 					return 5;
934 				return 6;
935 			}
936 			return 6;
937 		}
938 		if ((_lineCounts[0] <= 84) && (checkFit(_linePtrs[0], 42, 2))) {
939 			if (_lineCounts[1] <= 42) {
940 				if (_lineCounts[2] <= 42)
941 					return 4;
942 				if (_lineCounts[2] <= 96)
943 					if (checkFit(_linePtrs[2], 48, 2))
944 						return 5;
945 				return 6;
946 			}
947 			if ((_lineCounts[1] <= 96) && (checkFit(_linePtrs[1], 48, 2))) {
948 				if (_lineCounts[2] <= 48)
949 					return 5;
950 				return 6;
951 			}
952 			return 6;
953 		}
954 		if ((_lineCounts[0] <= 96) && (checkFit(_linePtrs[0], 48, 3))) {
955 			if (_lineCounts[1] <= 48) {
956 				if (_lineCounts[2] <= 48)
957 					return 5;
958 			}
959 			return 6;
960 		}
961 		return 6;
962 	case 4: if (_lineCounts[0] <= 42) {
963 			if (_lineCounts[1] <= 42) {
964 				if (_lineCounts[2] <= 42) {
965 					if (_lineCounts[3] <= 42)
966 						return 4;
967 					if (_lineCounts[3] <= 96)
968 						if (checkFit(_linePtrs[3], 48, 2))
969 							return 5;
970 					return 6;
971 				}
972 				if ((_lineCounts[2] <= 96) && (checkFit(_linePtrs[2], 48, 2)))
973 					if (_lineCounts[3] <= 48)
974 						return 5;
975 				return 6;
976 			}
977 			if ((_lineCounts[1] <= 96) && (checkFit(_linePtrs[1], 48, 2)))
978 					if ((_lineCounts[2] <= 48) && (_lineCounts[3] <= 48))
979 						return 5;
980 			return 6;
981 		}
982 		if ((_lineCounts[0] <= 96) && (checkFit(_linePtrs[0], 48, 2)))
983 			if ((_lineCounts[1] <= 48) && (_lineCounts[2] <= 48) && (_lineCounts[3] <= 48))
984 				return 5;
985 		return 6;
986 	case 5: if ((_lineCounts[0] > 48) || (_lineCounts[1] > 48) || (_lineCounts[2] > 48)
987 			|| (_lineCounts[3] > 48) || (_lineCounts[4] > 48))
988 			return 6;
989 		else
990 			return 5;
991 	default:
992 		return 6;
993 	}
994 }
995 
996 
checkFit(char * ptr,int width,int lines)997 uint16 AGOSEngine_Waxworks::checkFit(char *ptr, int width, int lines) {
998 	int countw = 0;
999 	int countl = 0;
1000 	char *x = NULL;
1001 	while (*ptr) {
1002 		if (*ptr == '\n')
1003 			return 1;
1004 		if (countw == width) {
1005 			countl++;
1006 			countw = 0;
1007 			ptr = x;
1008 		}
1009 		if (*ptr == ' ') {
1010 			x = ptr;
1011 			x++;
1012 		}
1013 		countw++;
1014 		if (countl == lines)
1015 			return 0;
1016 		ptr++;
1017 	}
1018 
1019 	return 1;
1020 }
1021 
boxTextMessage(const char * x)1022 void AGOSEngine_Waxworks::boxTextMessage(const char *x) {
1023 	sprintf(_boxBufferPtr, "%s\n", x);
1024 	_lineCounts[_boxLineCount] += strlen(x);
1025 	_boxBufferPtr += strlen(x) + 1;
1026 	_boxLineCount++;
1027 	_linePtrs[_boxLineCount] = _boxBufferPtr;
1028 	_boxCR = 1;
1029 }
1030 
boxTextMsg(const char * x)1031 void AGOSEngine_Waxworks::boxTextMsg(const char *x) {
1032 	sprintf(_boxBufferPtr, "%s", x);
1033 	_lineCounts[_boxLineCount] += strlen(x);
1034 	_boxBufferPtr += strlen(x);
1035 	_boxCR = 0;
1036 }
1037 
printBox()1038 void AGOSEngine_Waxworks::printBox() {
1039 	uint16 BoxSize;
1040 
1041 	*_boxBufferPtr = 0;
1042 	_linePtrs[0] = _boxBuffer;
1043 	if (_boxCR == 0)
1044 		_boxLineCount++;
1045 	stopAnimate(105);
1046 	BoxSize = getBoxSize();
1047 	_variableArray[53] = BoxSize;
1048 	animate(3, 1, 100, 0, 0, 0);
1049 	changeWindow(5);
1050 
1051 	switch (BoxSize) {
1052 	case 1: _textWindow->x = 10;
1053 		_textWindow->y = 163;
1054 		_textWindow->width = 20;
1055 		_textWindow->height = 1;
1056 		_textWindow->textMaxLength = 26;
1057 		break;
1058 	case 2: _textWindow->x = 8;
1059 		_textWindow->y = 160;
1060 		_textWindow->width = 24;
1061 		_textWindow->height = 2;
1062 		_textWindow->textMaxLength = 32;
1063 		break;
1064 	case 3: _textWindow->x = 6;
1065 		_textWindow->y = 156;
1066 		_textWindow->width = 28;
1067 		_textWindow->height = 3;
1068 		_textWindow->textMaxLength = 37;
1069 		break;
1070 	case 4: _textWindow->x = 4;
1071 		_textWindow->y = 153;
1072 		_textWindow->width = 32;
1073 		_textWindow->height = 4;
1074 		_textWindow->textMaxLength = 42;
1075 		break;
1076 	case 5: _textWindow->x = 2;
1077 		_textWindow->y = 150;
1078 		_textWindow->width = 36;
1079 		_textWindow->height = 5;
1080 		_textWindow->textMaxLength = 48;
1081 		break;
1082 	default:_textWindow->x = 1;
1083 		_textWindow->y = 147;
1084 		_textWindow->width = 38;
1085 		_textWindow->height = 6;
1086 		_textWindow->textMaxLength = 50;
1087 		break;
1088 	}
1089 	_textWindow->textColumn = 0;
1090 	_textWindow->textRow = 0;
1091 	_textWindow->textColumnOffset = 0;
1092 	_textWindow->textLength = 0;
1093 	justifyStart();
1094 	waitForSync(99);
1095 	_boxBufferPtr = _boxBuffer;
1096 	while (*_boxBufferPtr)
1097 		justifyOutPut(*_boxBufferPtr++);
1098 	_boxLineCount = 0;
1099 	_boxBufferPtr = _boxBuffer;
1100 	_lineCounts[0] = 0;
1101 	_lineCounts[1] = 0;
1102 	_lineCounts[2] = 0;
1103 	_lineCounts[3] = 0;
1104 	_lineCounts[4] = 0;
1105 	_lineCounts[5] = 0;
1106 	changeWindow(0);
1107 }
1108 
1109 } // End of namespace AGOS
1110