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
safeReadByte(const uint8 * & src,const uint8 * end)809 uint8 safeReadByte(const uint8 *&src, const uint8 *end) {
810 if (src < end)
811 return (*src++);
812 error("decodePak98(): invalid data");
813 return 0;
814 }
815
safeReadWord(const uint8 * & src,const uint8 * end)816 uint16 safeReadWord(const uint8 *&src, const uint8 *end) {
817 uint8 lo = safeReadByte(src, end);
818 return (safeReadByte(src, end) << 8) | lo;
819 }
820
821 #define S_NEXTBYTE safeReadByte(src, endSrc)
822 #define S_NEXTWORD safeReadWord(src, endSrc)
823
decodePak98(uint8 * dst,uint32 outSize,const uint8 * src,uint32 inSize)824 void decodePak98(uint8 *dst, uint32 outSize, const uint8 *src, uint32 inSize) {
825 const uint8 *end = dst + outSize;
826 const uint8 *endSrc = src + inSize;
827 uint8 state = 0x80;
828 uint8 flg = S_NEXTBYTE;
829
830 for (uint32 srcBytesLeft = inSize - 1; srcBytesLeft; ) {
831 if (state & flg) {
832 if (dst < end)
833 *dst++ = S_NEXTBYTE;
834 --srcBytesLeft;
835 } else {
836 srcBytesLeft -= 2;
837 uint16 cmd2 = S_NEXTWORD;
838 uint8 cmd3 = cmd2 & 0x0F;
839 cmd2 >>= 4;
840
841 if (cmd2 == 0) {
842 uint16 count = cmd3 + 4;
843 --srcBytesLeft;
844 if (cmd3 == 0x0F) {
845 count = S_NEXTWORD;
846 srcBytesLeft -= 2;
847 } else if (cmd3 == 0x0E) {
848 count = 18 + (S_NEXTBYTE);
849 --srcBytesLeft;
850 }
851
852 uint8 destVal = S_NEXTBYTE;
853 while (count-- && dst < end)
854 *dst++ = destVal;
855
856 } else if (cmd2 == 1) {
857 uint16 count = cmd3 + 3;
858 if (cmd3 == 0x0F) {
859 count = S_NEXTWORD;
860 srcBytesLeft -= 2;
861 } else if (cmd3 == 0x0E) {
862 count = 17 + (S_NEXTBYTE);
863 --srcBytesLeft;
864 }
865
866 dst += count;
867
868 } else if (cmd2 == 2) {
869 uint16 count = cmd3 + 16;
870 if (cmd3 == 0x0F) {
871 count = S_NEXTWORD;
872 srcBytesLeft -= 2;
873 } else if (cmd3 == 0x0E) {
874 count = 30 + (S_NEXTBYTE);
875 --srcBytesLeft;
876 }
877
878 srcBytesLeft -= count;
879 while (count-- && dst < end)
880 *dst++ = S_NEXTBYTE;
881
882 } else {
883 uint16 count = cmd3 + 3;
884 if (cmd3 == 0x0F) {
885 count = 18 + (S_NEXTBYTE);
886 --srcBytesLeft;
887 }
888
889 const uint8 *src2 = dst - cmd2;
890 while (count-- && dst < end)
891 *dst++ = *src2++;
892 }
893 }
894
895 if (!(state >>= 1)) {
896 state = 0x80;
897 if (srcBytesLeft) {
898 flg = S_NEXTBYTE;
899 srcBytesLeft--;
900 }
901 }
902 }
903 }
904
905 #undef S_NEXTBYTE
906 #undef S_NEXTWORD
907
loadVGAVideoFile(uint16 id,uint8 type,bool useError)908 void AGOSEngine::loadVGAVideoFile(uint16 id, uint8 type, bool useError) {
909 Common::File in;
910 char filename[15];
911 byte *dst;
912 uint32 file, offs, srcSize, dstSize;
913 uint extraBuffer = 0;
914
915 if ((getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) &&
916 id == 2 && type == 2) {
917 // WORKAROUND: For the extra long strings in foreign languages
918 // Allocate more space for text to cope with foreign languages that use
919 // up more space than English. I hope 6400 bytes are enough. This number
920 // is base on: 2 (lines) * 320 (screen width) * 10 (textheight) -- olki
921 extraBuffer += 6400;
922 }
923
924 if (getFeatures() & GF_ZLIBCOMP) {
925 loadOffsets(getFileName(GAME_GFXIDXFILE), id * 3 + type, file, offs, srcSize, dstSize);
926
927 if (getPlatform() == Common::kPlatformAmiga)
928 sprintf(filename, "GFX%d.VGA", file);
929 else
930 sprintf(filename, "graphics.vga");
931
932 dst = allocBlock(dstSize + extraBuffer);
933 decompressData(filename, dst, offs, srcSize, dstSize);
934 } else if (getFeatures() & GF_OLD_BUNDLE) {
935 if (getPlatform() == Common::kPlatformAcorn) {
936 sprintf(filename, "%.3d%d.DAT", id, type);
937 } else if (getPlatform() == Common::kPlatformAmiga || getPlatform() == Common::kPlatformAtariST) {
938 if (getFeatures() & GF_TALKIE) {
939 sprintf(filename, "%.3d%d.out", id, type);
940 } else if (getGameType() == GType_ELVIRA1 && getFeatures() & GF_DEMO) {
941 if (getPlatform() == Common::kPlatformAtariST)
942 sprintf(filename, "%.2d%d.out", id, type);
943 else
944 sprintf(filename, "%c%d.out", 48 + id, type);
945 } else if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2) {
946 sprintf(filename, "%.2d%d.pkd", id, type);
947 } else if (getGameType() == GType_PN) {
948 sprintf(filename, "%c%d.in", id + 48, type);
949 } else {
950 sprintf(filename, "%.3d%d.pkd", id, type);
951 }
952 } else {
953 if (getGameType() == GType_ELVIRA1 && getPlatform() == Common::kPlatformPC98) {
954 sprintf(filename, "%.2d.GR2", id);
955 } else if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) {
956 sprintf(filename, "%.2d%d.VGA", id, type);
957 } else if (getGameType() == GType_PN) {
958 sprintf(filename, "%c%d.out", id + 48, type);
959 } else {
960 sprintf(filename, "%.3d%d.VGA", id, type);
961 }
962 }
963
964 if (!in.open(filename)) {
965 if (useError)
966 error("loadVGAVideoFile: Can't load %s", filename);
967
968 _block = _blockEnd = NULL;
969 return;
970 }
971
972 dstSize = srcSize = in.size();
973 if (getGameType() == GType_PN && getPlatform() == Common::kPlatformDOS && id == 17 && type == 2) {
974 // The A2.out file isn't compressed in PC version of Personal Nightmare
975 dst = allocBlock(dstSize + extraBuffer);
976 if (in.read(dst, dstSize) != dstSize)
977 error("loadVGAVideoFile: Read failed");
978 } else if (getGameType() == GType_PN && (getFeatures() & GF_CRUNCHED)) {
979 Common::Stack<uint32> data;
980 byte *dataOut = 0;
981 int dataOutSize = 0;
982
983 for (uint i = 0; i < srcSize / 4; ++i) {
984 uint32 dataVal = in.readUint32BE();
985 // Correct incorrect byte, in corrupt 72.out file, included in some PC versions.
986 if (dataVal == 168042714)
987 data.push(168050906);
988 else
989 data.push(dataVal);
990 }
991
992 decompressPN(data, dataOut, dataOutSize);
993 dst = allocBlock (dataOutSize + extraBuffer);
994 memcpy(dst, dataOut, dataOutSize);
995 delete[] dataOut;
996 } else if (getFeatures() & GF_CRUNCHED) {
997 byte *srcBuffer = (byte *)malloc(srcSize);
998 if (in.read(srcBuffer, srcSize) != srcSize)
999 error("loadVGAVideoFile: Read failed");
1000
1001 dstSize = READ_BE_UINT32(srcBuffer + srcSize - 4);
1002 dst = allocBlock (dstSize + extraBuffer);
1003 decrunchFile(srcBuffer, dst, srcSize);
1004 free(srcBuffer);
1005 } else if (getPlatform() == Common::kPlatformPC98) {
1006 bool compressed = (in.readUint16LE() == 1);
1007 srcSize = in.readUint32LE();
1008 if (type == 1) {
1009 if (compressed)
1010 srcSize = in.readUint32LE() + 2;
1011 in.seek(srcSize, SEEK_CUR);
1012 compressed = (in.readUint16LE() == 1);
1013 srcSize = in.readUint32LE();
1014 }
1015
1016 if (compressed) {
1017 dstSize = srcSize;
1018 srcSize = in.readUint32LE();
1019 uint16 fill = in.readUint16LE();
1020 dst = allocBlock(dstSize);
1021
1022 Common::fill<uint16*, uint16>((uint16*)dst, (uint16*)(dst + (dstSize & ~1)), TO_LE_16(fill));
1023 if (dstSize & 1)
1024 *(dst + dstSize - 1) = fill & 0xff;
1025
1026 if (srcSize) {
1027 uint8 *srcBuffer = new uint8[srcSize];
1028 if (in.read(srcBuffer, srcSize) != srcSize)
1029 error("loadVGAVideoFile: Read failed");
1030 decodePak98(dst, dstSize, srcBuffer, srcSize);
1031 delete[] srcBuffer;
1032 }
1033 } else {
1034 dstSize = srcSize;
1035 dst = allocBlock(dstSize + extraBuffer);
1036 if (in.read(dst, dstSize) != dstSize)
1037 error("loadVGAVideoFile: Read failed");
1038 }
1039 } else {
1040 dst = allocBlock(dstSize + extraBuffer);
1041 if (in.read(dst, dstSize) != dstSize)
1042 error("loadVGAVideoFile: Read failed");
1043 }
1044 } else {
1045 id = id * 2 + (type - 1);
1046 offs = _gameOffsetsPtr[id];
1047 dstSize = _gameOffsetsPtr[id + 1] - offs;
1048
1049 if (!dstSize) {
1050 if (useError)
1051 error("loadVGAVideoFile: Can't load id %d type %d", id, type);
1052
1053 _block = _blockEnd = NULL;
1054 return;
1055 }
1056
1057 dst = allocBlock(dstSize + extraBuffer);
1058 readGameFile(dst, offs, dstSize);
1059 }
1060 }
1061
createPak98FileStream(const char * filename)1062 Common::SeekableReadStream *AGOSEngine::createPak98FileStream(const char *filename) {
1063 Common::File in;
1064 if (!in.open(filename))
1065 return 0;
1066
1067 /*uint16 cmpType = */in.readUint16LE();
1068 uint32 outSize = in.readUint32LE();
1069 uint32 inSize = in.readUint32LE();
1070 uint16 fill = in.readUint16LE();
1071
1072 uint8 *decBuffer = (uint8*)malloc(outSize);
1073 Common::fill<uint16*, uint16>((uint16*)decBuffer, (uint16*)(decBuffer + (outSize & ~1)), TO_LE_16(fill));
1074 if (outSize & 1)
1075 *(decBuffer + outSize - 1) = fill & 0xff;
1076
1077 if (inSize) {
1078 uint8 *tempBuffer = new uint8[inSize];
1079 in.read(tempBuffer, inSize);
1080 decodePak98(decBuffer, outSize, tempBuffer, inSize);
1081 delete[] tempBuffer;
1082 }
1083
1084 return new Common::MemoryReadStream(decBuffer, outSize, DisposeAfterUse::YES);
1085 }
1086
convertPC98Image(VC10_state & state)1087 void AGOSEngine::convertPC98Image(VC10_state &state) {
1088 if (state.flags & (kDFCompressedFlip | kDFCompressed)) {
1089 const byte *src = state.srcPtr;
1090 uint32 outSize = READ_LE_UINT32(src + 2);
1091 assert(outSize >= (uint32)((state.width << 3) * state.height));
1092 uint32 inSize = READ_LE_UINT32(src + 6);
1093 uint16 fill = READ_LE_UINT16(src + 10);
1094 delete[] _pak98Buf;
1095 byte *decBuffer = new uint8[outSize];
1096 Common::fill<uint16*, uint16>((uint16*)decBuffer, (uint16*)(decBuffer + (outSize & ~1)), TO_LE_16(fill));
1097 if (outSize & 1)
1098 *(decBuffer + outSize - 1) = fill & 0xff;
1099 if (inSize)
1100 decodePak98(decBuffer, outSize, src + 12, inSize);
1101 _pak98Buf = state.srcPtr = decBuffer;
1102 _paletteModNext = 0;
1103 }
1104
1105 // The PC-98 images are in a planar format, but slightly different from the Amiga format. It does
1106 // not make much sense to set the GF_PLANAR flag, since the Amiga code can't be used anyway.
1107 free(_planarBuf);
1108 uint16 planeLW = state.width << 1;
1109 uint16 planePitch = planeLW * 3;
1110
1111 _planarBuf = (byte*)malloc((state.width << 3) * state.height);
1112
1113 const byte *src[4];
1114 memset(src, 0, sizeof(src));
1115 for (int i = 0; i < 4; ++i)
1116 src[i] = state.srcPtr + i * planeLW;
1117 byte *dst = _planarBuf;
1118
1119 for (int y = 0; y < state.height; ++y) {
1120 for (int x = 0; x < planeLW; ++x) {
1121 for (int i = 0; i <= 6; i += 2) {
1122 byte col = 0;
1123 for (int ii = 0; ii < 4; ++ii) {
1124 col |= ((*src[ii] >> (7 - i)) & 1) << (ii + 4);
1125 col |= ((*src[ii] >> (6 - i)) & 1) << ii;
1126 }
1127 *dst++ = col;
1128 }
1129 for (int ii = 0; ii < 4; ++ii)
1130 ++src[ii];
1131 }
1132 for (int ii = 0; ii < 4; ++ii)
1133 src[ii] += planePitch;
1134 }
1135
1136 state.srcPtr = _planarBuf;
1137 if (state.flags & kDFCompressedFlip)
1138 state.flags |= kDFFlip;
1139 state.flags &= ~(kDFCompressedFlip | kDFCompressed);
1140 }
1141
1142 } // End of namespace AGOS
1143