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