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 // Resource file routines for Simon1/Simon2
24 
25 
26 #include "common/archive.h"
27 #include "common/file.h"
28 #include "common/memstream.h"
29 #include "common/textconsole.h"
30 #include "common/util.h"
31 
32 #include "agos/agos.h"
33 #include "agos/intern.h"
34 
35 #include "common/zlib.h"
36 
37 namespace AGOS {
38 
39 #ifdef ENABLE_AGOS2
to16Wrapper(uint value)40 uint16 AGOSEngine_Feeble::to16Wrapper(uint value) {
41 	return TO_LE_16(value);
42 }
43 
readUint16Wrapper(const void * src)44 uint16 AGOSEngine_Feeble::readUint16Wrapper(const void *src) {
45 	return READ_LE_UINT16(src);
46 }
47 
readUint32Wrapper(const void * src)48 uint32 AGOSEngine_Feeble::readUint32Wrapper(const void *src) {
49 	return READ_LE_UINT32(src);
50 }
51 #endif
52 
to16Wrapper(uint value)53 uint16 AGOSEngine::to16Wrapper(uint value) {
54 	return TO_BE_16(value);
55 }
56 
readUint16Wrapper(const void * src)57 uint16 AGOSEngine::readUint16Wrapper(const void *src) {
58 	return READ_BE_UINT16(src);
59 }
60 
readUint32Wrapper(const void * src)61 uint32 AGOSEngine::readUint32Wrapper(const void *src) {
62 	return READ_BE_UINT32(src);
63 }
64 
decompressData(const char * srcName,byte * dst,uint32 offset,uint32 srcSize,uint32 dstSize)65 void AGOSEngine::decompressData(const char *srcName, byte *dst, uint32 offset, uint32 srcSize, uint32 dstSize) {
66 #ifdef USE_ZLIB
67 		Common::File in;
68 		in.open(srcName);
69 		if (in.isOpen() == false)
70 			error("decompressData: Can't load %s", srcName);
71 
72 		in.seek(offset, SEEK_SET);
73 		if (srcSize != dstSize) {
74 			byte *srcBuffer = (byte *)malloc(srcSize);
75 
76 			if (in.read(srcBuffer, srcSize) != srcSize)
77 				error("decompressData: Read failed");
78 
79 			unsigned long decompressedSize = dstSize;
80 			if (!Common::uncompress(dst, &decompressedSize, srcBuffer, srcSize))
81 				error("decompressData: Zlib uncompress error");
82 			free(srcBuffer);
83 		} else {
84 			if (in.read(dst, dstSize) != dstSize)
85 				error("decompressData: Read failed");
86 		}
87 		in.close();
88 #else
89 	error("Zlib support is required for Amiga and Macintosh versions");
90 #endif
91 }
92 
loadOffsets(const char * filename,int number,uint32 & file,uint32 & offset,uint32 & srcSize,uint32 & dstSize)93 void AGOSEngine::loadOffsets(const char *filename, int number, uint32 &file, uint32 &offset, uint32 &srcSize, uint32 &dstSize) {
94 	Common::File in;
95 
96 	int offsSize = (getPlatform() == Common::kPlatformAmiga) ? 16 : 12;
97 
98 	/* read offsets from index */
99 	in.open(filename);
100 	if (in.isOpen() == false) {
101 		error("loadOffsets: Can't load index file '%s'", filename);
102 	}
103 
104 	in.seek(number * offsSize, SEEK_SET);
105 	offset = in.readUint32LE();
106 	dstSize = in.readUint32LE();
107 	srcSize = in.readUint32LE();
108 	file = in.readUint32LE();
109 	in.close();
110 }
111 
allocGamePcVars(Common::SeekableReadStream * in)112 int AGOSEngine::allocGamePcVars(Common::SeekableReadStream *in) {
113 	uint32 itemArraySize, itemArrayInited, stringTableNum;
114 	uint32 version;
115 	uint32 i;
116 
117 	itemArraySize = in->readUint32BE();
118 	version = in->readUint32BE();
119 	itemArrayInited = in->readUint32BE();
120 	stringTableNum = in->readUint32BE();
121 
122 	// First two items are predefined
123 	if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2) {
124 		itemArraySize += 2;
125 		itemArrayInited = itemArraySize;
126 	} else {
127 		itemArrayInited += 2;
128 		itemArraySize += 2;
129 	}
130 
131 	if (version != 0x80)
132 		error("allocGamePcVars: Not a runtime database");
133 
134 	_itemArrayPtr = (Item **)calloc(itemArraySize, sizeof(Item *));
135 	if (_itemArrayPtr == NULL)
136 		error("allocGamePcVars: Out of memory for Item array");
137 
138 	_itemArraySize = itemArraySize;
139 	_itemArrayInited = itemArrayInited;
140 
141 	for (i = 1; i < itemArrayInited; i++) {
142 		_itemArrayPtr[i] = (Item *)allocateItem(sizeof(Item));
143 	}
144 
145 	// The rest is cleared automatically by calloc
146 	allocateStringTable(stringTableNum + 10);
147 	_stringTabNum = stringTableNum;
148 
149 	return itemArrayInited;
150 }
151 
loadGamePcFile()152 void AGOSEngine_PN::loadGamePcFile() {
153 	if (getFileName(GAME_BASEFILE) != NULL) {
154 		Common::File in;
155 		// Read dataBase
156 		if (!in.open(getFileName(GAME_BASEFILE))) {
157 			error("loadGamePcFile: Can't load database file '%s'", getFileName(GAME_BASEFILE));
158 		}
159 
160 		_dataBaseSize = in.size();
161 		_dataBase = (byte *)malloc(_dataBaseSize);
162 		if (_dataBase == NULL)
163 			error("loadGamePcFile: Out of memory for dataBase");
164 		in.read(_dataBase, _dataBaseSize);
165 
166 		if (_dataBase[31] != 0)
167 			error("Later version of system requested");
168 	}
169 
170 	if (getFileName(GAME_TEXTFILE) != NULL) {
171 		Common::File in;
172 		// Read textBase
173 		if (!in.open(getFileName(GAME_TEXTFILE))) {
174 			error("loadGamePcFile: Can't load textbase file '%s'", getFileName(GAME_TEXTFILE));
175 		}
176 
177 		_textBaseSize = in.size();
178 		_textBase = (byte *)malloc(_textBaseSize);
179 		if (_textBase == NULL)
180 			error("loadGamePcFile: Out of memory for textBase");
181 		in.read(_textBase, _textBaseSize);
182 
183 		if (_textBase[getlong(30L)] != 128)
184 			error("Unknown compression format");
185 	}
186 }
187 
loadGamePcFile()188 void AGOSEngine::loadGamePcFile() {
189 	int fileSize;
190 
191 	if (getFileName(GAME_BASEFILE) != NULL) {
192 		/* Read main gamexx file */
193 		Common::File in;
194 		if (!in.open(getFileName(GAME_BASEFILE))) {
195 			error("loadGamePcFile: Can't load gamexx file '%s'", getFileName(GAME_BASEFILE));
196 		}
197 
198 		if (getFeatures() & GF_CRUNCHED_GAMEPC) {
199 			uint srcSize = in.size();
200 			byte *srcBuf = (byte *)malloc(srcSize);
201 			in.read(srcBuf, srcSize);
202 
203 			uint dstSize = READ_BE_UINT32(srcBuf + srcSize - 4);
204 			byte *dstBuf = (byte *)malloc(dstSize);
205 			decrunchFile(srcBuf, dstBuf, srcSize);
206 			free(srcBuf);
207 
208 			Common::MemoryReadStream stream(dstBuf, dstSize);
209 			readGamePcFile(&stream);
210 			free(dstBuf);
211 		} else {
212 			readGamePcFile(&in);
213 		}
214 	}
215 
216 	if (getFileName(GAME_TBLFILE) != NULL) {
217 		/* Read list of TABLE resources */
218 		Common::File in;
219 		if (!in.open(getFileName(GAME_TBLFILE))) {
220 			error("loadGamePcFile: Can't load table resources file '%s'", getFileName(GAME_TBLFILE));
221 		}
222 
223 		fileSize = in.size();
224 
225 		_tblList = (byte *)malloc(fileSize);
226 		if (_tblList == NULL)
227 			error("loadGamePcFile: Out of memory for strip table list");
228 		in.read(_tblList, fileSize);
229 
230 		/* Remember the current state */
231 		_subroutineListOrg = _subroutineList;
232 		_tablesHeapPtrOrg = _tablesHeapPtr;
233 		_tablesHeapCurPosOrg = _tablesHeapCurPos;
234 	}
235 
236 	if (getFileName(GAME_STRFILE) != NULL) {
237 		/* Read list of TEXT resources */
238 		Common::File in;
239 		if (!in.open(getFileName(GAME_STRFILE)))
240 			error("loadGamePcFile: Can't load text resources file '%s'", getFileName(GAME_STRFILE));
241 
242 		fileSize = in.size();
243 		_strippedTxtMem = (byte *)malloc(fileSize);
244 		if (_strippedTxtMem == NULL)
245 			error("loadGamePcFile: Out of memory for strip text list");
246 		in.read(_strippedTxtMem, fileSize);
247 	}
248 
249 	if (getFileName(GAME_STATFILE) != NULL) {
250 		/* Read list of ROOM STATE resources */
251 		Common::File in;
252 		if (!in.open(getFileName(GAME_STATFILE))) {
253 			error("loadGamePcFile: Can't load state resources file '%s'", getFileName(GAME_STATFILE));
254 		}
255 
256 		_numRoomStates = in.size() / 8;
257 
258 		_roomStates = (RoomState *)calloc(_numRoomStates, sizeof(RoomState));
259 		if (_roomStates == NULL)
260 			error("loadGamePcFile: Out of memory for room state list");
261 
262 		for (uint s = 0; s < _numRoomStates; s++) {
263 			uint16 num = in.readUint16BE() - (_itemArrayInited - 2);
264 
265 			_roomStates[num].state = in.readUint16BE();
266 			_roomStates[num].classFlags = in.readUint16BE();
267 			_roomStates[num].roomExitStates = in.readUint16BE();
268 		}
269 	}
270 
271 	if (getFileName(GAME_RMSLFILE) != NULL) {
272 		/* Read list of ROOM ITEMS resources */
273 		Common::File in;
274 		if (!in.open(getFileName(GAME_RMSLFILE))) {
275 			error("loadGamePcFile: Can't load room resources file '%s'", getFileName(GAME_RMSLFILE));
276 		}
277 
278 		fileSize = in.size();
279 
280 		_roomsList = (byte *)malloc(fileSize);
281 		if (_roomsList == NULL)
282 			error("loadGamePcFile: Out of memory for room items list");
283 		in.read(_roomsList, fileSize);
284 	}
285 
286 	if (getFileName(GAME_XTBLFILE) != NULL) {
287 		/* Read list of XTABLE resources */
288 		Common::File in;
289 		if (!in.open(getFileName(GAME_XTBLFILE))) {
290 			error("loadGamePcFile: Can't load xtable resources file '%s'", getFileName(GAME_XTBLFILE));
291 		}
292 
293 		fileSize = in.size();
294 
295 		_xtblList = (byte *)malloc(fileSize);
296 		if (_xtblList == NULL)
297 			error("loadGamePcFile: Out of memory for strip xtable list");
298 		in.read(_xtblList, fileSize);
299 
300 		/* Remember the current state */
301 		_xsubroutineListOrg = _subroutineList;
302 		_xtablesHeapPtrOrg = _tablesHeapPtr;
303 		_xtablesHeapCurPosOrg = _tablesHeapCurPos;
304 	}
305 }
306 
readGamePcFile(Common::SeekableReadStream * in)307 void AGOSEngine::readGamePcFile(Common::SeekableReadStream *in) {
308 	int num_inited_objects;
309 	int i;
310 
311 	num_inited_objects = allocGamePcVars(in);
312 
313 	createPlayer();
314 	readGamePcText(in);
315 
316 	for (i = 2; i < num_inited_objects; i++) {
317 		readItemFromGamePc(in, _itemArrayPtr[i]);
318 	}
319 
320 	readSubroutineBlock(in);
321 }
322 
readGamePcText(Common::SeekableReadStream * in)323 void AGOSEngine::readGamePcText(Common::SeekableReadStream *in) {
324 	_textSize = in->readUint32BE();
325 	_textMem = (byte *)malloc(_textSize);
326 	if (_textMem == NULL)
327 		error("readGamePcText: Out of text memory");
328 
329 	in->read(_textMem, _textSize);
330 
331 	setupStringTable(_textMem, _stringTabNum);
332 }
333 
readItemFromGamePc(Common::SeekableReadStream * in,Item * item)334 void AGOSEngine::readItemFromGamePc(Common::SeekableReadStream *in, Item *item) {
335 	uint32 type;
336 
337 	if (getGameType() == GType_ELVIRA1) {
338 		item->itemName = (uint16)in->readUint32BE();
339 		item->adjective = in->readUint16BE();
340 		item->noun = in->readUint16BE();
341 		item->state = in->readUint16BE();
342 		in->readUint16BE();
343 		item->next = (uint16)fileReadItemID(in);
344 		item->child = (uint16)fileReadItemID(in);
345 		item->parent = (uint16)fileReadItemID(in);
346 		in->readUint16BE();
347 		in->readUint16BE();
348 		in->readUint16BE();
349 		item->classFlags = in->readUint16BE();
350 		item->children = NULL;
351 	} else if (getGameType() == GType_ELVIRA2) {
352 		item->itemName = (uint16)in->readUint32BE();
353 		item->adjective = in->readUint16BE();
354 		item->noun = in->readUint16BE();
355 		item->state = in->readUint16BE();
356 		item->next = (uint16)fileReadItemID(in);
357 		item->child = (uint16)fileReadItemID(in);
358 		item->parent = (uint16)fileReadItemID(in);
359 		in->readUint16BE();
360 		item->classFlags = in->readUint16BE();
361 		item->children = NULL;
362 	} else {
363 		item->adjective = in->readUint16BE();
364 		item->noun = in->readUint16BE();
365 		item->state = in->readUint16BE();
366 		item->next = (uint16)fileReadItemID(in);
367 		item->child = (uint16)fileReadItemID(in);
368 		item->parent = (uint16)fileReadItemID(in);
369 		in->readUint16BE();
370 		item->classFlags = in->readUint16BE();
371 		item->children = NULL;
372 	}
373 
374 
375 	type = in->readUint32BE();
376 	while (type) {
377 		type = in->readUint16BE();
378 		if (type != 0)
379 			readItemChildren(in, item, type);
380 	}
381 }
382 
readItemChildren(Common::SeekableReadStream * in,Item * item,uint type)383 void AGOSEngine::readItemChildren(Common::SeekableReadStream *in, Item *item, uint type) {
384 	if (type == kRoomType) {
385 		SubRoom *subRoom = (SubRoom *)allocateChildBlock(item, kRoomType, sizeof(SubRoom));
386 		subRoom->roomShort = in->readUint32BE();
387 		subRoom->roomLong = in->readUint32BE();
388 		subRoom->flags = in->readUint16BE();
389 	} else if (type == kObjectType) {
390 		SubObject *subObject = (SubObject *)allocateChildBlock(item, kObjectType, sizeof(SubObject));
391 		in->readUint32BE();
392 		in->readUint32BE();
393 		in->readUint32BE();
394 		subObject->objectName = in->readUint32BE();
395 		subObject->objectSize = in->readUint16BE();
396 		subObject->objectWeight = in->readUint16BE();
397 		subObject->objectFlags = in->readUint16BE();
398 	} else if (type == kGenExitType) {
399 		SubGenExit *genExit = (SubGenExit *)allocateChildBlock(item, kGenExitType, sizeof(SubGenExit));
400 		genExit->dest[0] = (uint16)fileReadItemID(in);
401 		genExit->dest[1] = (uint16)fileReadItemID(in);
402 		genExit->dest[2] = (uint16)fileReadItemID(in);
403 		genExit->dest[3] = (uint16)fileReadItemID(in);
404 		genExit->dest[4] = (uint16)fileReadItemID(in);
405 		genExit->dest[5] = (uint16)fileReadItemID(in);
406 		fileReadItemID(in);
407 		fileReadItemID(in);
408 		fileReadItemID(in);
409 		fileReadItemID(in);
410 		fileReadItemID(in);
411 		fileReadItemID(in);
412 	} else if (type == kContainerType) {
413 		SubContainer *container = (SubContainer *)allocateChildBlock(item, kContainerType, sizeof(SubContainer));
414 		container->volume = in->readUint16BE();
415 		container->flags = in->readUint16BE();
416 	} else if (type == kChainType) {
417 		SubChain *chain = (SubChain *)allocateChildBlock(item, kChainType, sizeof(SubChain));
418 		chain->chChained = (uint16)fileReadItemID(in);
419 	} else if (type == kUserFlagType) {
420 		setUserFlag(item, 0, in->readUint16BE());
421 		setUserFlag(item, 1, in->readUint16BE());
422 		setUserFlag(item, 2, in->readUint16BE());
423 		setUserFlag(item, 3, in->readUint16BE());
424 		setUserFlag(item, 4, in->readUint16BE());
425 		setUserFlag(item, 5, in->readUint16BE());
426 		setUserFlag(item, 6, in->readUint16BE());
427 		setUserFlag(item, 7, in->readUint16BE());
428 		SubUserFlag *subUserFlag = (SubUserFlag *)findChildOfType(item, kUserFlagType);
429 		subUserFlag->userItems[0] = (uint16)fileReadItemID(in);
430 		fileReadItemID(in);
431 		fileReadItemID(in);
432 		fileReadItemID(in);
433 	} else if (type == kInheritType) {
434 		SubInherit *inherit = (SubInherit *)allocateChildBlock(item, kInheritType, sizeof(SubInherit));
435 		inherit->inMaster = (uint16)fileReadItemID(in);
436 	} else {
437 		error("readItemChildren: invalid type %d", type);
438 	}
439 }
440 
readItemChildren(Common::SeekableReadStream * in,Item * item,uint type)441 void AGOSEngine_Elvira2::readItemChildren(Common::SeekableReadStream *in, Item *item, uint type) {
442 	if (type == kRoomType) {
443 		uint fr1 = in->readUint16BE();
444 		uint fr2 = in->readUint16BE();
445 		uint i, size;
446 		uint j, k;
447 		SubRoom *subRoom;
448 
449 		size = SubRoom_SIZE;
450 		for (i = 0, j = fr2; i != 6; i++, j >>= 2)
451 			if (j & 3)
452 				size += sizeof(subRoom->roomExit[0]);
453 
454 		subRoom = (SubRoom *)allocateChildBlock(item, kRoomType, size);
455 		subRoom->subroutine_id = fr1;
456 		subRoom->roomExitStates = fr2;
457 
458 		for (i = k = 0, j = fr2; i != 6; i++, j >>= 2)
459 			if (j & 3)
460 				subRoom->roomExit[k++] = (uint16)fileReadItemID(in);
461 	} else if (type == kObjectType) {
462 		uint32 fr = in->readUint32BE();
463 		uint i, k, size;
464 		SubObject *subObject;
465 
466 		size = SubObject_SIZE;
467 		for (i = 0; i != 16; i++)
468 			if (fr & (1 << i))
469 				size += sizeof(subObject->objectFlagValue[0]);
470 
471 		subObject = (SubObject *)allocateChildBlock(item, kObjectType, size);
472 		subObject->objectFlags = fr;
473 
474 		k = 0;
475 		if (fr & 1) {
476 			subObject->objectFlagValue[k++] = (uint16)in->readUint32BE();
477 		}
478 		for (i = 1; i != 16; i++)
479 			if (fr & (1 << i))
480 				subObject->objectFlagValue[k++] = in->readUint16BE();
481 
482 		if (getGameType() != GType_ELVIRA2)
483 			subObject->objectName = (uint16)in->readUint32BE();
484 	} else if (type == kSuperRoomType) {
485 		assert(getGameType() == GType_ELVIRA2);
486 
487 		uint i, j, k, size;
488 		uint id, x, y, z;
489 		SubSuperRoom *subSuperRoom;
490 
491 		id = in->readUint16BE();
492 		x = in->readUint16BE();
493 		y = in->readUint16BE();
494 		z = in->readUint16BE();
495 
496 		j = x * y * z;
497 		size = SubSuperRoom_SIZE;
498 		for (i = 0; i != j; i++)
499 			size += sizeof(subSuperRoom->roomExitStates[0]);
500 
501 		subSuperRoom = (SubSuperRoom *)allocateChildBlock(item, kSuperRoomType, size);
502 		subSuperRoom->subroutine_id = id;
503 		subSuperRoom->roomX = x;
504 		subSuperRoom->roomY = y;
505 		subSuperRoom->roomZ = z;
506 
507 		for (i = k = 0; i != j; i++)
508 			subSuperRoom->roomExitStates[k++] = in->readUint16BE();
509 	} else if (type == kContainerType) {
510 		SubContainer *container = (SubContainer *)allocateChildBlock(item, kContainerType, sizeof(SubContainer));
511 		container->volume = in->readUint16BE();
512 		container->flags = in->readUint16BE();
513 	} else if (type == kChainType) {
514 		SubChain *chain = (SubChain *)allocateChildBlock(item, kChainType, sizeof(SubChain));
515 		chain->chChained = (uint16)fileReadItemID(in);
516 	} else if (type == kUserFlagType) {
517 		setUserFlag(item, 0, in->readUint16BE());
518 		setUserFlag(item, 1, in->readUint16BE());
519 		setUserFlag(item, 2, in->readUint16BE());
520 		setUserFlag(item, 3, in->readUint16BE());
521 	} else if (type == kInheritType) {
522 		SubInherit *inherit = (SubInherit *)allocateChildBlock(item, kInheritType, sizeof(SubInherit));
523 		inherit->inMaster = (uint16)fileReadItemID(in);
524 	} else {
525 		error("readItemChildren: invalid type %d", type);
526 	}
527 }
528 
fileReadItemID(Common::SeekableReadStream * in)529 uint fileReadItemID(Common::SeekableReadStream *in) {
530 	uint32 val = in->readUint32BE();
531 	if (val == 0xFFFFFFFF)
532 		return 0;
533 	return val + 2;
534 }
535 
openGameFile()536 void AGOSEngine::openGameFile() {
537 	_gameFile = new Common::File();
538 	_gameFile->open(getFileName(GAME_GMEFILE));
539 
540 	if (!_gameFile->isOpen())
541 		error("openGameFile: Can't load game file '%s'", getFileName(GAME_GMEFILE));
542 
543 	uint32 size = _gameFile->readUint32LE();
544 
545 	_gameOffsetsPtr = (uint32 *)malloc(size);
546 	if (_gameOffsetsPtr == NULL)
547 		error("openGameFile: Out of memory, game offsets");
548 
549 	_gameFile->seek(0, SEEK_SET);
550 
551 	for (uint r = 0; r < size / 4; r++)
552 		_gameOffsetsPtr[r] = _gameFile->readUint32LE();
553 }
554 
readGameFile(void * dst,uint32 offs,uint32 size)555 void AGOSEngine::readGameFile(void *dst, uint32 offs, uint32 size) {
556 	_gameFile->seek(offs, SEEK_SET);
557 	if (_gameFile->read(dst, size) != size)
558 		error("readGameFile: Read failed (%d,%d)", offs, size);
559 }
560 
561 // Thanks to Stuart Caie for providing the original
562 // C conversion upon which this decruncher is based.
563 
564 #define SD_GETBIT(var) do {     \
565 	if (!bits--) {              \
566 		s -= 4;                 \
567 		if (s < src)            \
568 			return false;       \
569 		bb = READ_BE_UINT32(s); \
570 		bits = 31;              \
571 	}                           \
572 	(var) = bb & 1;             \
573 	bb >>= 1;                   \
574 }while (0)
575 
576 #define SD_GETBITS(var, nbits) do { \
577 	bc = (nbits);                   \
578 	(var) = 0;                      \
579 	while (bc--) {                   \
580 		(var) <<= 1;                \
581 		SD_GETBIT(bit);             \
582 		(var) |= bit;               \
583 	}                               \
584 }while (0)
585 
586 #define SD_TYPE_LITERAL (0)
587 #define SD_TYPE_MATCH   (1)
588 
decrunchFile(byte * src,byte * dst,uint32 size)589 bool AGOSEngine::decrunchFile(byte *src, byte *dst, uint32 size) {
590 	byte *s = src + size - 4;
591 	uint32 destlen = READ_BE_UINT32 (s);
592 	uint32 bb, x, y;
593 	byte *d = dst + destlen;
594 	byte bc, bit, bits, type;
595 
596 	// Initialize bit buffer.
597 	s -= 4;
598 	bb = x = READ_BE_UINT32 (s);
599 	bits = 0;
600 	do {
601 		x >>= 1;
602 		bits++;
603 	} while (x);
604 	bits--;
605 
606 	while (d > dst) {
607 		SD_GETBIT(x);
608 		if (x) {
609 			SD_GETBITS(x, 2);
610 			switch (x) {
611 			case 0:
612 				type = SD_TYPE_MATCH;
613 				x = 9;
614 				y = 2;
615 				break;
616 
617 			case 1:
618 				type = SD_TYPE_MATCH;
619 				x = 10;
620 				y = 3;
621 				break;
622 
623 			case 2:
624 				type = SD_TYPE_MATCH;
625 				x = 12;
626 				SD_GETBITS(y, 8);
627 				break;
628 
629 			default:
630 				type = SD_TYPE_LITERAL;
631 				x = 8;
632 				y = 8;
633 			}
634 		} else {
635 			SD_GETBIT(x);
636 			if (x) {
637 				type = SD_TYPE_MATCH;
638 				x = 8;
639 				y = 1;
640 			} else {
641 				type = SD_TYPE_LITERAL;
642 				x = 3;
643 				y = 0;
644 			}
645 		}
646 
647 		if (type == SD_TYPE_LITERAL) {
648 			SD_GETBITS(x, x);
649 			y += x;
650 			if ((int)(y + 1) > (d - dst))
651 				return false; // Overflow?
652 			do {
653 				SD_GETBITS(x, 8);
654 				*--d = x;
655 			} while (y-- > 0);
656 		} else {
657 			if ((int)(y + 1) > (d - dst))
658 				return false; // Overflow?
659 			SD_GETBITS(x, x);
660 			if ((d + x) > (dst + destlen))
661 				return false; // Offset overflow?
662 			do {
663 				d--;
664 				*d = d[x];
665 			} while (y-- > 0);
666 		}
667 	}
668 
669 	// Successful decrunch.
670 	return true;
671 }
672 
673 #undef SD_GETBIT
674 #undef SD_GETBITS
675 #undef SD_TYPE_LITERAL
676 #undef SD_TYPE_MATCH
677 
getBit(Common::Stack<uint32> & dataList,uint32 & srcVal)678 static bool getBit(Common::Stack<uint32> &dataList, uint32 &srcVal) {
679 	bool result = srcVal & 1;
680 	srcVal >>= 1;
681 	if (srcVal == 0) {
682 		srcVal = dataList.pop();
683 
684 		result = srcVal & 1;
685 		srcVal = (srcVal >> 1) | 0x80000000L;
686 	}
687 
688 	return result;
689 }
690 
copyBits(Common::Stack<uint32> & dataList,uint32 & srcVal,int numBits)691 static uint32 copyBits(Common::Stack<uint32> &dataList, uint32 &srcVal, int numBits) {
692 	uint32 destVal = 0;
693 
694 	for (int i = 0; i < numBits; ++i) {
695 		bool f = getBit(dataList, srcVal);
696 		destVal = (destVal << 1) | (f ? 1 : 0);
697 	}
698 
699 	return destVal;
700 }
701 
transferLoop(uint8 * dataOut,int & outIndex,uint32 destVal,int max)702 static void transferLoop(uint8 *dataOut, int &outIndex, uint32 destVal, int max) {
703 	assert(outIndex > max - 1);
704 	byte *pDest = dataOut + outIndex;
705 
706 	 for (int i = 0; (i <= max) && (outIndex > 0); ++i) {
707 		pDest = dataOut + --outIndex;
708 		*pDest = pDest[destVal];
709 	 }
710 }
711 
decompressPN(Common::Stack<uint32> & dataList,uint8 * & dataOut,int & dataOutSize)712 void AGOSEngine::decompressPN(Common::Stack<uint32> &dataList, uint8 *&dataOut, int &dataOutSize) {
713 	// Set up the output data area
714 	dataOutSize = dataList.pop();
715 	dataOut = new uint8[dataOutSize];
716 	int outIndex = dataOutSize;
717 
718 	// Decompression routine
719 	uint32 srcVal = dataList.pop();
720 	uint32 destVal;
721 
722 	while (outIndex > 0) {
723 		uint32 numBits = 0;
724 		int count = 0;
725 
726 		if (getBit(dataList, srcVal)) {
727 			destVal = copyBits(dataList, srcVal, 2);
728 
729 			if (destVal < 2) {
730 				count = destVal + 2;
731 				destVal = copyBits(dataList, srcVal, destVal + 9);
732 				transferLoop(dataOut, outIndex, destVal, count);
733 				continue;
734 			} else if (destVal != 3) {
735 				count = copyBits(dataList, srcVal, 8);
736 				destVal = copyBits(dataList, srcVal, 8);
737 				transferLoop(dataOut, outIndex, destVal, count);
738 				continue;
739 			} else {
740 				numBits = 8;
741 				count = 8;
742 			}
743 		} else if (getBit(dataList, srcVal)) {
744 			destVal = copyBits(dataList, srcVal, 8);
745 			transferLoop(dataOut, outIndex, destVal, 1);
746 			continue;
747 		} else {
748 			numBits = 3;
749 			count = 0;
750 		}
751 
752 		destVal = copyBits(dataList, srcVal, numBits);
753 		count += destVal;
754 
755 		// Loop through extracting specified number of bytes
756 		for (int i = 0; i <= count; ++i) {
757 			// Shift 8 bits from the source to the destination
758 			for (int bitCtr = 0; bitCtr < 8; ++bitCtr) {
759 				bool flag = getBit(dataList, srcVal);
760 				destVal = (destVal << 1) | (flag ? 1 : 0);
761 			}
762 
763 			dataOut[--outIndex] = destVal & 0xff;
764 		}
765 	}
766 }
767 
loadVGABeardFile(uint16 id)768 void AGOSEngine::loadVGABeardFile(uint16 id) {
769 	uint32 offs, size;
770 
771 	if (getFeatures() & GF_OLD_BUNDLE) {
772 		Common::File in;
773 		char filename[15];
774 		if (id == 23)
775 			id = 112;
776 		else if (id == 328)
777 			id = 119;
778 
779 		if (getPlatform() == Common::kPlatformAmiga) {
780 			if (getFeatures() & GF_TALKIE)
781 				sprintf(filename, "0%d.out", id);
782 			else
783 				sprintf(filename, "0%d.pkd", id);
784 		} else {
785 			sprintf(filename, "0%d.VGA", id);
786 		}
787 
788 		if (!in.open(filename))
789 			error("loadSimonVGAFile: Can't load %s", filename);
790 
791 		size = in.size();
792 		if (getFeatures() & GF_CRUNCHED) {
793 			byte *srcBuffer = (byte *)malloc(size);
794 			if (in.read(srcBuffer, size) != size)
795 				error("loadSimonVGAFile: Read failed");
796 			decrunchFile(srcBuffer, _vgaBufferPointers[11].vgaFile2, size);
797 			free(srcBuffer);
798 		} else {
799 			if (in.read(_vgaBufferPointers[11].vgaFile2, size) != size)
800 				error("loadSimonVGAFile: Read failed");
801 		}
802 	} else {
803 		offs = _gameOffsetsPtr[id];
804 		size = _gameOffsetsPtr[id + 1] - offs;
805 		readGameFile(_vgaBufferPointers[11].vgaFile2, offs, size);
806 	}
807 }
808 
loadVGAVideoFile(uint16 id,uint8 type,bool useError)809 void AGOSEngine::loadVGAVideoFile(uint16 id, uint8 type, bool useError) {
810 	Common::File in;
811 	char filename[15];
812 	byte *dst;
813 	uint32 file, offs, srcSize, dstSize;
814 	uint extraBuffer = 0;
815 
816 	if ((getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) &&
817 		id == 2 && type == 2) {
818 		// WORKAROUND: For the extra long strings in foreign languages
819 		// Allocate more space for text to cope with foreign languages that use
820 		// up more space than English. I hope 6400 bytes are enough. This number
821 		// is base on: 2 (lines) * 320 (screen width) * 10 (textheight) -- olki
822 		extraBuffer += 6400;
823 	}
824 
825 	if (getFeatures() & GF_ZLIBCOMP) {
826 		loadOffsets(getFileName(GAME_GFXIDXFILE), id * 3 + type, file, offs, srcSize, dstSize);
827 
828 		if (getPlatform() == Common::kPlatformAmiga)
829 			sprintf(filename, "GFX%d.VGA", file);
830 		else
831 			sprintf(filename, "graphics.vga");
832 
833 		dst = allocBlock(dstSize + extraBuffer);
834 		decompressData(filename, dst, offs, srcSize, dstSize);
835 	} else if (getFeatures() & GF_OLD_BUNDLE) {
836 		if (getPlatform() == Common::kPlatformAcorn) {
837 			sprintf(filename, "%.3d%d.DAT", id, type);
838 		} else if (getPlatform() == Common::kPlatformAmiga || getPlatform() == Common::kPlatformAtariST) {
839 			if (getFeatures() & GF_TALKIE) {
840 				sprintf(filename, "%.3d%d.out", id, type);
841 			} else if (getGameType() == GType_ELVIRA1 && getFeatures() & GF_DEMO) {
842 				if (getPlatform() == Common::kPlatformAtariST)
843 					sprintf(filename, "%.2d%d.out", id, type);
844 				else
845 					sprintf(filename, "%c%d.out", 48 + id, type);
846 			} else if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2) {
847 				sprintf(filename, "%.2d%d.pkd", id, type);
848 			} else if (getGameType() == GType_PN) {
849 				sprintf(filename, "%c%d.in", id + 48, type);
850 			} else {
851 				sprintf(filename, "%.3d%d.pkd", id, type);
852 			}
853 		} else {
854 			if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) {
855 				sprintf(filename, "%.2d%d.VGA", id, type);
856 			} else if (getGameType() == GType_PN) {
857 				sprintf(filename, "%c%d.out", id + 48, type);
858 			} else {
859 				sprintf(filename, "%.3d%d.VGA", id, type);
860 			}
861 		}
862 
863 		if (!in.open(filename)) {
864 			if (useError)
865 				error("loadVGAVideoFile: Can't load %s", filename);
866 
867 			_block = _blockEnd = NULL;
868 			return;
869 		}
870 
871 		dstSize = srcSize = in.size();
872 		if (getGameType() == GType_PN && getPlatform() == Common::kPlatformDOS && id == 17 && type == 2) {
873 			// The A2.out file isn't compressed in PC version of Personal Nightmare
874 			dst = allocBlock(dstSize + extraBuffer);
875 			if (in.read(dst, dstSize) != dstSize)
876 				error("loadVGAVideoFile: Read failed");
877 		} else if (getGameType() == GType_PN && (getFeatures() & GF_CRUNCHED)) {
878 			Common::Stack<uint32> data;
879 			byte *dataOut = 0;
880 			int dataOutSize = 0;
881 
882 			for (uint i = 0; i < srcSize / 4; ++i) {
883 				uint32 dataVal = in.readUint32BE();
884 				// Correct incorrect byte, in corrupt 72.out file, included in some PC versions.
885 				if (dataVal == 168042714)
886 					data.push(168050906);
887 				else
888 					data.push(dataVal);
889 			}
890 
891 			decompressPN(data, dataOut, dataOutSize);
892 			dst = allocBlock (dataOutSize + extraBuffer);
893 			memcpy(dst, dataOut, dataOutSize);
894 			delete[] dataOut;
895 		} else if (getFeatures() & GF_CRUNCHED) {
896 			byte *srcBuffer = (byte *)malloc(srcSize);
897 			if (in.read(srcBuffer, srcSize) != srcSize)
898 				error("loadVGAVideoFile: Read failed");
899 
900 			dstSize = READ_BE_UINT32(srcBuffer + srcSize - 4);
901 			dst = allocBlock (dstSize + extraBuffer);
902 			decrunchFile(srcBuffer, dst, srcSize);
903 			free(srcBuffer);
904 		} else {
905 			dst = allocBlock(dstSize + extraBuffer);
906 			if (in.read(dst, dstSize) != dstSize)
907 				error("loadVGAVideoFile: Read failed");
908 		}
909 	} else {
910 		id = id * 2 + (type - 1);
911 		offs = _gameOffsetsPtr[id];
912 		dstSize = _gameOffsetsPtr[id + 1] - offs;
913 
914 		if (!dstSize) {
915 			if (useError)
916 				error("loadVGAVideoFile: Can't load id %d type %d", id, type);
917 
918 			_block = _blockEnd = NULL;
919 			return;
920 		}
921 
922 		dst = allocBlock(dstSize + extraBuffer);
923 		readGameFile(dst, offs, dstSize);
924 	}
925 }
926 
927 } // End of namespace AGOS
928