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 #include "cruise/cruise_main.h"
24 #include "common/endian.h"
25 #include "common/memstream.h"
26 #include "common/textconsole.h"
27
28 namespace Cruise {
29
30 enum fileTypeEnum {
31 type_UNK,
32 type_SPL,
33 type_SET,
34 type_FNT
35 };
36
37 int loadSingleFile;
38
39 /**
40 * Takes care of decoding a compressed graphic
41 */
decodeGfxUnified(dataFileEntry * pCurrentFileEntry,int16 format)42 void decodeGfxUnified(dataFileEntry *pCurrentFileEntry, int16 format) {
43 uint8 *dataPtr = pCurrentFileEntry->subData.ptr;
44 int spriteSize;
45
46 // Unified how to get spriteSize
47 switch (format) {
48 case 1:
49 case 4:
50 spriteSize = pCurrentFileEntry->height * pCurrentFileEntry->width;
51 break;
52 case 5:
53 spriteSize = pCurrentFileEntry->height * pCurrentFileEntry->widthInColumn;
54 break;
55
56 default:
57 error("Unknown gfx format %d", format);
58 }
59
60 uint8 *buffer = (uint8 *)MemAlloc(spriteSize);
61
62 // Perform format specific decoding
63 switch (format) {
64 case 1:
65 case 4: {
66 int x = 0;
67 while (x < spriteSize) {
68 uint8 c;
69 uint16 p0;
70 // Format 4
71 uint16 p1 = 0, p2 = 0, p3 = 0;
72
73 p0 = (dataPtr[0] << 8) | dataPtr[1];
74
75 // Format 4
76 if (format == 4) {
77 p1 = (dataPtr[2] << 8) | dataPtr[3];
78 p2 = (dataPtr[4] << 8) | dataPtr[5];
79 p3 = (dataPtr[6] << 8) | dataPtr[7];
80 }
81
82 /* decode planes */
83 for (c = 0; c < 16; c++) {
84 // Format 4
85 if (format == 4) {
86 buffer[x + c] = ((p0 >> 15) & 1) | ((p1 >> 14) & 2) | ((p2 >> 13) & 4) | ((p3 >> 12) & 8);
87 } else {
88 buffer[x + c] = ((p0 >> 15) & 1);
89 }
90
91 p0 <<= 1;
92
93 // Format 4
94 if (format == 4) {
95 p1 <<= 1;
96 p2 <<= 1;
97 p3 <<= 1;
98 }
99 }
100
101 x += 16;
102
103 dataPtr += (2 * format);
104 }
105
106 break;
107 }
108 case 5: {
109 uint8 *destP = buffer;
110 int range = pCurrentFileEntry->height * pCurrentFileEntry->width;
111
112 for (int line = 0; line < pCurrentFileEntry->height; line++) {
113 uint8 p0, p1, p2, p3, p4;
114
115 for (int x = 0; x < pCurrentFileEntry->widthInColumn; x++) {
116 int bit = 7 - (x % 8);
117 int col = x / 8;
118
119 p0 = (dataPtr[line*pCurrentFileEntry->width + col + range * 0] >> bit) & 1;
120 p1 = (dataPtr[line*pCurrentFileEntry->width + col + range * 1] >> bit) & 1;
121 p2 = (dataPtr[line*pCurrentFileEntry->width + col + range * 2] >> bit) & 1;
122 p3 = (dataPtr[line*pCurrentFileEntry->width + col + range * 3] >> bit) & 1;
123 p4 = (dataPtr[line*pCurrentFileEntry->width + col + range * 4] >> bit) & 1;
124
125 *destP++ = p0 | (p1 << 1) | (p2 << 2) | (p3 << 3) | (p4 << 4);
126 }
127 }
128
129 break;
130 }
131 default:
132 break;
133 }
134
135 MemFree(pCurrentFileEntry->subData.ptr);
136 pCurrentFileEntry->subData.ptr = buffer;
137 }
138
updateResFileEntry(int height,int width,int size,int entryNumber,int resType)139 int updateResFileEntry(int height, int width, int size, int entryNumber, int resType) {
140 int div = 0;
141
142 resetFileEntry(entryNumber);
143
144 filesDatabase[entryNumber].subData.compression = 0;
145
146 int maskSize = size;
147
148 if (resType == 4) {
149 div = maskSize / 4;
150 } else if (resType == 5) {
151 width = (width * 8) / 5;
152 maskSize = MAX(size, height * width);
153 }
154
155 filesDatabase[entryNumber].subData.ptr = (uint8 *)mallocAndZero(maskSize + div);
156
157 if (!filesDatabase[entryNumber].subData.ptr)
158 return (-2);
159
160 filesDatabase[entryNumber].widthInColumn = width;
161 filesDatabase[entryNumber].subData.ptrMask = (uint8 *) mallocAndZero(maskSize);
162 filesDatabase[entryNumber].width = width / 8;
163 filesDatabase[entryNumber].resType = resType;
164 filesDatabase[entryNumber].height = height;
165 filesDatabase[entryNumber].subData.index = -1;
166
167 return entryNumber;
168 }
169
createResFileEntry(int width,int height,int size,int resType)170 int createResFileEntry(int width, int height, int size, int resType) {
171 error("Executing untested createResFileEntry");
172 return 0; // for compilers that don't support NORETURN
173
174 #if 0
175 int entryNumber;
176 int div = 0;
177
178 int i = 0;
179 for (; i < NUM_FILE_ENTRIES; i++) {
180 if (!filesDatabase[i].subData.ptr)
181 break;
182 }
183
184 if (i >= NUM_FILE_ENTRIES) {
185 return (-19);
186 }
187
188 entryNumber = i;
189
190 filesDatabase[entryNumber].subData.compression = 0;
191
192 if (resType == 4) {
193 div = size / 4;
194 } else if (resType == 5) {
195 width = (width * 8) / 5;
196 }
197
198 filesDatabase[entryNumber].subData.ptr = (uint8 *) mallocAndZero(size + div);
199
200 if (!filesDatabase[entryNumber].subData.ptr) {
201 return (-2);
202 }
203
204 filesDatabase[entryNumber].widthInColumn = width;
205 filesDatabase[entryNumber].subData.ptrMask = filesDatabase[entryNumber].subData.ptr + size;
206 filesDatabase[entryNumber].width = width / 8;
207 filesDatabase[entryNumber].resType = resType;
208 filesDatabase[entryNumber].height = height;
209 filesDatabase[entryNumber].subData.index = -1;
210
211 return entryNumber;
212 #endif
213 }
214
getFileType(const char * name)215 fileTypeEnum getFileType(const char *name) {
216 char extentionBuffer[16];
217
218 fileTypeEnum newFileType = type_UNK;
219
220 getFileExtention(name, extentionBuffer);
221
222 if (!strcmp(extentionBuffer, ".SPL")) {
223 newFileType = type_SPL;
224 } else if (!strcmp(extentionBuffer, ".SET")) {
225 newFileType = type_SET;
226 } else if (!strcmp(extentionBuffer, ".FNT")) {
227 newFileType = type_FNT;
228 }
229
230 assert(newFileType != type_UNK);
231
232 return newFileType;
233 }
234
getNumMaxEntiresInSet(uint8 * ptr)235 int getNumMaxEntiresInSet(uint8 *ptr) {
236 uint16 numEntries = READ_BE_UINT16(ptr + 4);
237 return numEntries;
238 }
239
loadFile(const char * name,int idx,int destIdx)240 int loadFile(const char* name, int idx, int destIdx) {
241 uint8 *ptr = NULL;
242 fileTypeEnum fileType;
243
244 fileType = getFileType(name);
245
246 loadFileSub1(&ptr, name, NULL);
247
248 switch (fileType) {
249 case type_SET: {
250
251 int numMaxEntriesInSet = getNumMaxEntiresInSet(ptr);
252
253 if (destIdx > numMaxEntriesInSet) {
254 MemFree(ptr);
255 return 0; // exit if limit is reached
256 }
257 int res = loadSetEntry(name, ptr, destIdx, idx);
258 MemFree(ptr);
259
260 return res;
261 }
262 case type_FNT: {
263 int res = loadFNTSub(ptr, idx);
264 MemFree(ptr);
265
266 return res;
267 }
268 case type_SPL: {
269 // Sound file
270 loadSPLSub(ptr, idx);
271 break;
272 }
273 default:
274 error("Unknown fileType in loadFile");
275 }
276
277 MemFree(ptr);
278
279 return -1;
280 }
281
loadFileRange(const char * name,int startIdx,int currentEntryIdx,int numIdx)282 int loadFileRange(const char *name, int startIdx, int currentEntryIdx, int numIdx) {
283 uint8 *ptr = NULL;
284 fileTypeEnum fileType;
285
286 fileType = getFileType(name);
287
288 loadFileSub1(&ptr, name, NULL);
289
290 switch (fileType) {
291 case type_SET: {
292 int numMaxEntriesInSet = getNumMaxEntiresInSet(ptr);
293
294 for (int i = 0; i < numIdx; i++) {
295 if ((startIdx + i) > numMaxEntriesInSet) {
296 MemFree(ptr);
297 return 0; // exit if limit is reached
298 }
299 loadSetEntry(name, ptr, startIdx + i, currentEntryIdx + i);
300 }
301
302 break;
303 }
304 case type_FNT: {
305 loadFNTSub(ptr, startIdx);
306 break;
307 }
308 case type_SPL: {
309 // Sound file
310 loadSPLSub(ptr, startIdx);
311 break;
312 }
313 default:
314 error("Unknown fileType in loadFileRange");
315 }
316
317 MemFree(ptr);
318
319 return 0;
320 }
321
loadFullBundle(const char * name,int startIdx)322 int loadFullBundle(const char *name, int startIdx) {
323 uint8 *ptr = NULL;
324 fileTypeEnum fileType;
325
326 fileType = getFileType(name);
327
328 loadFileSub1(&ptr, name, NULL);
329
330 if (ptr == NULL)
331 return 0;
332
333 switch (fileType) {
334 case type_SET: {
335 // Sprite set
336 int numMaxEntriesInSet = getNumMaxEntiresInSet(ptr); // get maximum number of sprites/animations in SET file
337
338 for (int i = 0; i < numMaxEntriesInSet; i++) {
339 loadSetEntry(name, ptr, i, startIdx + i);
340 }
341
342 break;
343 }
344 case type_FNT: {
345 // Font file
346 loadFNTSub(ptr, startIdx);
347 break;
348 }
349 case type_SPL: {
350 // Sound file
351 loadSPLSub(ptr, startIdx);
352 break;
353 }
354 default:
355 error("Unknown fileType in loadFullBundle");
356 }
357
358 MemFree(ptr);
359
360 return 0;
361 }
362
loadFNTSub(uint8 * ptr,int destIdx)363 int loadFNTSub(uint8 *ptr, int destIdx) {
364 uint8 *ptr2 = ptr + 4;
365 loadFileVar1 = READ_BE_UINT32(ptr2);
366
367 int fileIndex;
368 if (destIdx == -1)
369 fileIndex = createResFileEntry(loadFileVar1, 1, loadFileVar1, 1);
370 else
371 fileIndex = updateResFileEntry(loadFileVar1, 1, loadFileVar1, destIdx, 1);
372
373 if (fileIndex < 0)
374 error("Unable to load FNT resource");
375
376 uint8 *destPtr = filesDatabase[fileIndex].subData.ptr;
377
378 if (destPtr != NULL) {
379 memcpy(destPtr, ptr2, loadFileVar1);
380
381 destPtr = filesDatabase[fileIndex].subData.ptr;
382
383 bigEndianLongToNative((int32 *) destPtr);
384 bigEndianLongToNative((int32 *)(destPtr + 4));
385 flipGen(destPtr + 8, 6);
386
387 uint8 *currentPtr = destPtr + 14;
388
389 for (int i = 0; i < (int16)READ_UINT16(destPtr + 8); i++) {
390 bigEndianLongToNative((int32 *) currentPtr);
391 currentPtr += 4;
392
393 flipGen(currentPtr, 8);
394 currentPtr += 8;
395 }
396 }
397
398 return 1;
399 }
400
loadSPLSub(uint8 * ptr,int destIdx)401 int loadSPLSub(uint8 *ptr, int destIdx) {
402 int fileIndex;
403
404 if (destIdx == -1)
405 fileIndex = createResFileEntry(loadFileVar1, 1, loadFileVar1, 1);
406 else
407 fileIndex = updateResFileEntry(loadFileVar1, 1, loadFileVar1, destIdx, 1);
408
409 if (fileIndex < 0)
410 error("Unable to load SPL resource");
411
412 uint8* destPtr = filesDatabase[fileIndex].subData.ptr;
413 memcpy(destPtr, ptr, loadFileVar1);
414
415 return 1;
416 }
417
418
loadSetEntry(const char * name,uint8 * ptr,int currentEntryIdx,int currentDestEntry)419 int loadSetEntry(const char *name, uint8 *ptr, int currentEntryIdx, int currentDestEntry) {
420 uint8 *ptr3;
421 int offset;
422 int sec = 0;
423 uint16 numIdx;
424
425 if (!strcmp((char *)ptr, "SEC"))
426 sec = 1;
427
428 numIdx = READ_BE_UINT16(ptr + 4);
429 ptr3 = ptr + 6;
430 offset = currentEntryIdx * 16;
431
432 int resourceSize;
433 int fileIndex;
434 setHeaderEntry localBuffer;
435
436 Common::MemoryReadStream s4(ptr + offset + 6, 16);
437
438 localBuffer.offset = s4.readUint32BE();
439 localBuffer.width = s4.readUint16BE();
440 localBuffer.height = s4.readUint16BE();
441 localBuffer.type = s4.readUint16BE();
442 localBuffer.transparency = s4.readUint16BE() & 0x1F;
443 localBuffer.hotspotY = s4.readUint16BE();
444 localBuffer.hotspotX = s4.readUint16BE();
445
446 if (sec == 1)
447 // Type 1: Width - (1*2) , Type 5: Width - (5*2)
448 localBuffer.width -= localBuffer.type * 2;
449
450 resourceSize = localBuffer.width * localBuffer.height;
451
452 if (!sec && (localBuffer.type == 5))
453 // Type 5: Width - (2*5)
454 localBuffer.width -= 10;
455
456 if (currentDestEntry == -1)
457 fileIndex = createResFileEntry(localBuffer.width, localBuffer.height, resourceSize, localBuffer.type);
458 else
459 fileIndex = updateResFileEntry(localBuffer.height, localBuffer.width, resourceSize, currentDestEntry, localBuffer.type);
460
461 if (fileIndex < 0)
462 return -1; // TODO: buffer is not freed
463
464 if (!sec && (localBuffer.type == 5)) {
465 // There are sometimes sprites with a reduced width than what their pixels provide.
466 // The original handled this here by copy parts of each line - for ScummVM, we're
467 // simply setting the width in bytes and letting the decoder do the rest
468 filesDatabase[fileIndex].width += 2;
469 }
470
471 uint8 *ptr5 = ptr3 + localBuffer.offset + numIdx * 16;
472 memcpy(filesDatabase[fileIndex].subData.ptr, ptr5, resourceSize);
473
474 switch (localBuffer.type) {
475 case 0: // polygon
476 filesDatabase[fileIndex].subData.resourceType = OBJ_TYPE_POLY;
477 filesDatabase[fileIndex].subData.index = currentEntryIdx;
478 break;
479
480 case 1:
481 filesDatabase[fileIndex].width = filesDatabase[fileIndex].widthInColumn * 8;
482 filesDatabase[fileIndex].subData.resourceType = OBJ_TYPE_BGMASK;
483 decodeGfxUnified(&filesDatabase[fileIndex], localBuffer.type);
484 filesDatabase[fileIndex].subData.index = currentEntryIdx;
485 filesDatabase[fileIndex].subData.transparency = 0;
486 break;
487
488 case 4:
489 filesDatabase[fileIndex].width = filesDatabase[fileIndex].widthInColumn * 2;
490 filesDatabase[fileIndex].subData.resourceType = OBJ_TYPE_SPRITE;
491 decodeGfxUnified(&filesDatabase[fileIndex], localBuffer.type);
492 filesDatabase[fileIndex].subData.index = currentEntryIdx;
493 filesDatabase[fileIndex].subData.transparency = localBuffer.transparency % 0x10;
494 break;
495
496 case 5:
497 filesDatabase[fileIndex].subData.resourceType = OBJ_TYPE_SPRITE;
498 decodeGfxUnified(&filesDatabase[fileIndex], localBuffer.type);
499 filesDatabase[fileIndex].width = filesDatabase[fileIndex].widthInColumn;
500 filesDatabase[fileIndex].subData.index = currentEntryIdx;
501 filesDatabase[fileIndex].subData.transparency = localBuffer.transparency;
502 break;
503
504 case 8:
505 filesDatabase[fileIndex].subData.resourceType = OBJ_TYPE_SPRITE;
506 filesDatabase[fileIndex].width = filesDatabase[fileIndex].widthInColumn;
507 filesDatabase[fileIndex].subData.index = currentEntryIdx;
508 filesDatabase[fileIndex].subData.transparency = localBuffer.transparency;
509 break;
510
511 default:
512 warning("Unsupported gfx loading type: %d", localBuffer.type);
513 break;
514 }
515
516 if (name != filesDatabase[fileIndex].subData.name)
517 Common::strlcpy(filesDatabase[fileIndex].subData.name, name, sizeof(filesDatabase[fileIndex].subData.name));
518
519 // create the mask
520 switch (localBuffer.type) {
521 case 1:
522 case 4:
523 case 5:
524 case 8:
525 memset(filesDatabase[fileIndex].subData.ptrMask, 0, filesDatabase[fileIndex].width / 8 * filesDatabase[fileIndex].height);
526
527 for (int maskY = 0; maskY < filesDatabase[fileIndex].height; maskY++) {
528 for (int maskX = 0; maskX < filesDatabase[fileIndex].width; maskX++) {
529 if (*(filesDatabase[fileIndex].subData.ptr + filesDatabase[fileIndex].width * maskY + maskX) != filesDatabase[fileIndex].subData.transparency) {
530 *(filesDatabase[fileIndex].subData.ptrMask + filesDatabase[fileIndex].width / 8 * maskY + maskX / 8) |= 0x80 >> (maskX & 7);
531 }
532 }
533 }
534 break;
535
536 default:
537 break;
538 }
539
540 // TODO: free
541
542 return 1;
543 }
544
545 } // End of namespace Cruise
546