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 * Low level graphics interface.
22 */
23
24 #include "tinsel/graphics.h"
25 #include "tinsel/handle.h" // LockMem()
26 #include "tinsel/object.h"
27 #include "tinsel/palette.h"
28 #include "tinsel/scene.h"
29 #include "tinsel/tinsel.h"
30 #include "tinsel/scn.h"
31
32 #include "common/textconsole.h"
33
34 namespace Tinsel {
35
36 //----------------- LOCAL DEFINES --------------------
37
38 // Defines used in graphic drawing
39 #define CHARPTR_OFFSET 16
40 #define CHAR_WIDTH 4
41 #define CHAR_HEIGHT 4
42
43 extern uint8 g_transPalette[MAX_COLORS];
44
45 //----------------- SUPPORT FUNCTIONS ---------------------
46
47 /**
48 * PSX Block list unwinder.
49 * Chunk type 0x0003 (CHUNK_CHARPTR) in PSX version of DW 1 & 2 is compressed (original code
50 * calls the compression PJCRLE), thus we need to decompress it before passing data to drawing functions
51 */
psxPJCRLEUnwinder(uint16 imageWidth,uint16 imageHeight,uint8 * srcIdx)52 uint8* psxPJCRLEUnwinder(uint16 imageWidth, uint16 imageHeight, uint8 *srcIdx) {
53 uint32 remainingBlocks = 0;
54
55 uint16 compressionType = 0; // Kind of decompression to apply
56 uint16 controlBits = 0; // Control bits used to calculate the number of decompressed indexes
57 uint16 baseIndex = 0; // Base index to be repeated / incremented.
58
59 uint16 controlData;
60
61 uint8* dstIdx = NULL;
62 uint8* destinationBuffer = NULL;
63
64 if (!imageWidth || !imageHeight)
65 return NULL;
66
67 // Calculate needed index numbers, align width and height not next multiple of four
68 imageWidth = (imageWidth % 4) ? ((imageWidth / 4) + 1) * 4 : imageWidth;
69 imageHeight = (imageHeight % 4) ? ((imageHeight / 4) + 1) * 4 : imageHeight;
70 destinationBuffer = (uint8 *)malloc((imageWidth * imageHeight) / 8);
71 dstIdx = destinationBuffer;
72 remainingBlocks = (imageWidth * imageHeight) / 16;
73
74 while (remainingBlocks) { // Repeat until all blocks are decompressed
75 if (!controlBits) {
76 controlData = READ_16(srcIdx);
77 srcIdx += 2;
78
79 // If bit 15 of controlData is enabled, compression data is type 1.
80 // else if controlData has bit 16 enabled, compression type is 2,
81 // else there is no compression.
82 if (controlData & 0x4000)
83 compressionType = 2;
84 else if (controlData & 0x8000)
85 compressionType = 1;
86 else
87 compressionType = 0;
88
89 // Fetch control bits from controlData
90 controlBits = controlData & 0x3FFF;
91
92 // If there is compression, we need to fetch an index
93 // to be treated as "base" for compression.
94 if (compressionType != 0) {
95 controlData = READ_16(srcIdx);
96 srcIdx += 2;
97 baseIndex = controlData;
98 }
99 }
100
101 uint32 decremTiles; // Number of blocks that will be decompressed
102 if (remainingBlocks < controlBits) {
103 controlBits = controlBits - remainingBlocks;
104 decremTiles = remainingBlocks;
105 } else {
106 decremTiles = controlBits;
107 controlBits = 0;
108 }
109
110 // Decrement remaining blocks
111 remainingBlocks -= decremTiles;
112
113 // Manage different compressions
114 switch (compressionType) {
115 case 0: // No compression, plain copy of indexes
116 while (decremTiles) {
117 WRITE_LE_UINT16(dstIdx, READ_16(srcIdx));
118 srcIdx += 2;
119 dstIdx += 2;
120 decremTiles--;
121 }
122 break;
123 case 1: // Compression type 1, repeat a base index
124 while (decremTiles) {
125 WRITE_LE_UINT16(dstIdx, baseIndex);
126 dstIdx += 2;
127 decremTiles--;
128 }
129 break;
130 case 2: // Compression type 2, increment a base index
131 while (decremTiles) {
132 WRITE_LE_UINT16(dstIdx, baseIndex);
133 baseIndex++;
134 dstIdx += 2;
135 decremTiles--;
136 }
137 break;
138 }
139 }
140
141 // End.
142 return destinationBuffer;
143 }
144
145 /**
146 * Straight rendering of uncompressed data
147 */
t0WrtNonZero(DRAWOBJECT * pObj,uint8 * srcP,uint8 * destP,bool applyClipping)148 static void t0WrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool applyClipping) {
149 int yClip = 0;
150
151 if (applyClipping) {
152 // Adjust the height down to skip any bottom clipping
153 pObj->height -= pObj->botClip;
154 yClip = pObj->topClip;
155 }
156
157 // Vertical loop
158 for (int y = 0; y < pObj->height; ++y) {
159 // Get the start of the next line output
160 uint8 *tempDest = destP;
161
162 int leftClip = applyClipping ? pObj->leftClip : 0;
163 int rightClip = applyClipping ? pObj->rightClip : 0;
164
165 // Horizontal loop
166 for (int x = 0; x < pObj->width; ) {
167 uint32 numBytes = READ_LE_UINT32(srcP);
168 srcP += sizeof(uint32);
169 bool repeatFlag = (numBytes & 0x80000000L) != 0;
170 numBytes &= 0x7fffffff;
171
172 uint clipAmount = MIN((int)numBytes & 0xff, leftClip);
173 leftClip -= clipAmount;
174 x += clipAmount;
175
176 if (repeatFlag) {
177 // Repeat of a given color
178 uint8 color = (numBytes >> 8) & 0xff;
179 int runLength = (numBytes & 0xff) - clipAmount;
180
181 int rptLength = MAX(MIN(runLength, pObj->width - rightClip - x), 0);
182 if (yClip == 0) {
183 if (color != 0)
184 memset(tempDest, color, rptLength);
185 tempDest += rptLength;
186 }
187
188 x += runLength;
189 } else {
190 // Copy a specified sequence length of pixels
191 srcP += clipAmount;
192
193 int runLength = numBytes - clipAmount;
194 int rptLength = MAX(MIN(runLength, pObj->width - rightClip - x), 0);
195 if (yClip == 0) {
196 memmove(tempDest, srcP, rptLength);
197 tempDest += rptLength;
198 }
199
200 int overflow = (numBytes % 4) == 0 ? 0 : 4 - (numBytes % 4);
201 x += runLength;
202 srcP += runLength + overflow;
203 }
204 }
205
206 // Move to next line
207 if (yClip > 0)
208 --yClip;
209 else
210 destP += SCREEN_WIDTH;
211 }
212 }
213
214 /**
215 * Straight rendering with transparency support, Mac variant
216 */
MacDrawTiles(DRAWOBJECT * pObj,uint8 * srcP,uint8 * destP,bool applyClipping)217 static void MacDrawTiles(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool applyClipping) {
218 int yClip = 0;
219
220 if (applyClipping) {
221 // Adjust the height down to skip any bottom clipping
222 pObj->height -= pObj->botClip;
223 yClip = pObj->topClip;
224 }
225
226 // Simple RLE-like scheme: the two first bytes of each data chunk determine
227 // if bytes should be repeated or copied.
228 // Example: 10 00 00 20 will repeat byte 0x0 0x10 times, and will copy 0x20
229 // bytes from the input stream afterwards
230
231 // Vertical loop
232 for (int y = 0; y < pObj->height; ++y) {
233 // Get the start of the next line output
234 uint8 *tempDest = destP;
235
236 int leftClip = applyClipping ? pObj->leftClip : 0;
237 int rightClip = applyClipping ? pObj->rightClip : 0;
238
239 // Horizontal loop
240 for (int x = 0; x < pObj->width; ) {
241 byte repeatBytes = *srcP++;
242
243 if (repeatBytes) {
244 uint clipAmount = MIN<int>(repeatBytes, leftClip);
245 leftClip -= clipAmount;
246 x += clipAmount;
247
248 // Repeat of a given color
249 byte color = *srcP++;
250 int runLength = repeatBytes - clipAmount;
251 int rptLength = MAX(MIN(runLength, pObj->width - rightClip - x), 0);
252 if (yClip == 0) {
253 if (color != 0)
254 memset(tempDest, color, rptLength);
255 tempDest += rptLength;
256 }
257
258 x += runLength;
259 } else {
260 // Copy a specified sequence length of pixels
261 byte copyBytes = *srcP++;
262
263 uint clipAmount = MIN<int>(copyBytes, leftClip);
264 leftClip -= clipAmount;
265 x += clipAmount;
266 srcP += clipAmount;
267
268 int runLength = copyBytes - clipAmount;
269 int rptLength = MAX(MIN(runLength, pObj->width - rightClip - x), 0);
270 if (yClip == 0) {
271 memmove(tempDest, srcP, rptLength);
272 tempDest += rptLength;
273 }
274
275 int overflow = (copyBytes & 1);
276 x += runLength;
277 srcP += runLength + overflow;
278 }
279 } // horizontal loop
280
281 // Move to next line
282 if (yClip > 0)
283 --yClip;
284 else
285 destP += SCREEN_WIDTH;
286 } // vertical loop
287 }
288
289
290 /**
291 * Straight rendering with transparency support, PSX variant supporting also 4-BIT clut data
292 */
PsxDrawTiles(DRAWOBJECT * pObj,uint8 * srcP,uint8 * destP,bool applyClipping,bool fourBitClut,uint32 psxSkipBytes,byte * psxMapperTable,bool transparency)293 static void PsxDrawTiles(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool applyClipping, bool fourBitClut, uint32 psxSkipBytes, byte *psxMapperTable, bool transparency) {
294 // Set up the offset between destination blocks
295 int rightClip = applyClipping ? pObj->rightClip : 0;
296 Common::Rect boxBounds;
297
298 if (applyClipping) {
299 // Adjust the height down to skip any bottom clipping
300 pObj->height -= pObj->botClip;
301
302 // Make adjustment for the top clipping row
303 srcP += sizeof(uint16) * ((pObj->width + 3) >> 2) * (pObj->topClip >> 2);
304 pObj->height -= pObj->topClip;
305 pObj->topClip %= 4;
306 }
307
308 // Vertical loop
309 while (pObj->height > 0) {
310 // Get the start of the next line output
311 uint8 *tempDest = destP;
312
313 // Get the line width, and figure out which row range within the 4 row high blocks
314 // will be displayed if clipping is to be taken into account
315 int width = pObj->width;
316
317 if (!applyClipping) {
318 // No clipping, so so set box bounding area for drawing full 4x4 pixel blocks
319 boxBounds.top = 0;
320 boxBounds.bottom = 3;
321 boxBounds.left = 0;
322 } else {
323 // Handle any possible clipping at the top of the char block.
324 // We already handled topClip partially at the beginning of this function.
325 // Hence the only non-zero values it can assume at this point are 1,2,3,
326 // and that only during the very first iteration (i.e. when the top char
327 // block is drawn only partially). In particular, we set topClip to zero,
328 // as all following blocks are not to be top clipped.
329 boxBounds.top = pObj->topClip;
330 pObj->topClip = 0;
331
332 boxBounds.bottom = MIN(boxBounds.top + pObj->height - 1, 3);
333
334 // Handle any possible clipping at the start of the line
335 boxBounds.left = pObj->leftClip;
336 if (boxBounds.left >= 4) {
337 srcP += sizeof(uint16) * (boxBounds.left >> 2);
338 width -= boxBounds.left & 0xfffc;
339 boxBounds.left %= 4;
340 }
341
342 width -= boxBounds.left;
343 }
344
345 // Horizontal loop
346 while (width > rightClip) {
347 boxBounds.right = MIN(boxBounds.left + width - rightClip - 1, 3);
348 assert(boxBounds.bottom >= boxBounds.top);
349 assert(boxBounds.right >= boxBounds.left);
350
351 int16 indexVal = READ_LE_UINT16(srcP);
352 srcP += sizeof(uint16);
353
354 // Draw a 4x4 block based on the opcode as in index into the block list
355 // In case we have a 4-bit CLUT image, blocks are 2x4 bytes, then expanded into 4x4
356 const uint8 *p;
357 if (fourBitClut)
358 p = (uint8 *)pObj->charBase + psxSkipBytes + (indexVal << 3);
359 else
360 p = (uint8 *)pObj->charBase + psxSkipBytes + (indexVal << 4);
361
362 p += boxBounds.top * (fourBitClut ? sizeof(uint16) : sizeof(uint32));
363 for (int yp = boxBounds.top; yp <= boxBounds.bottom; ++yp, p += (fourBitClut ? sizeof(uint16) : sizeof(uint32))) {
364 if (!fourBitClut) {
365 if (!transparency)
366 Common::copy(p + boxBounds.left, p + boxBounds.right + 1, tempDest + (SCREEN_WIDTH * (yp - boxBounds.top)));
367 else
368 for (int xp = boxBounds.left; xp <= boxBounds.right; ++xp) {
369 if (*(p + xp))
370 *(tempDest + SCREEN_WIDTH * (yp - boxBounds.top) + (xp - boxBounds.left)) = *(p + xp);
371 }
372 } else {
373 for (int xp = boxBounds.left; xp <= boxBounds.right; ++xp) {
374 // Extract pixel value from byte
375 byte pixValue = (*(p + (xp / 2)) & ((xp % 2) ? 0xf0 : 0x0f)) >> ((xp % 2) ? 4 : 0);
376 if (pixValue || !transparency)
377 *(tempDest + SCREEN_WIDTH * (yp - boxBounds.top) + (xp - boxBounds.left)) = psxMapperTable[pixValue];
378 }
379 }
380 }
381
382 tempDest += boxBounds.right - boxBounds.left + 1;
383 width -= 3 - boxBounds.left + 1;
384
385 // None of the remaining horizontal blocks should be left clipped
386 boxBounds.left = 0;
387 }
388
389 // If there is any width remaining, there must be a right edge clipping
390 if (width >= 0)
391 srcP += sizeof(uint16) * ((width + 3) >> 2);
392
393 // Move to next line line
394 pObj->height -= boxBounds.bottom - boxBounds.top + 1;
395 destP += (boxBounds.bottom - boxBounds.top + 1) * SCREEN_WIDTH;
396 }
397 }
398
399 /**
400 * Straight rendering with transparency support
401 */
WrtNonZero(DRAWOBJECT * pObj,uint8 * srcP,uint8 * destP,bool applyClipping)402 static void WrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool applyClipping) {
403 // Set up the offset between destination blocks
404 int rightClip = applyClipping ? pObj->rightClip : 0;
405 Common::Rect boxBounds;
406
407 if (applyClipping) {
408 // Adjust the height down to skip any bottom clipping
409 pObj->height -= pObj->botClip;
410
411 // Make adjustment for the top clipping row
412 srcP += sizeof(uint16) * ((pObj->width + 3) >> 2) * (pObj->topClip >> 2);
413 pObj->height -= pObj->topClip;
414 pObj->topClip %= 4;
415 }
416
417 // Vertical loop
418 while (pObj->height > 0) {
419 // Get the start of the next line output
420 uint8 *tempDest = destP;
421
422 // Get the line width, and figure out which row range within the 4 row high blocks
423 // will be displayed if clipping is to be taken into account
424 int width = pObj->width;
425
426 if (!applyClipping) {
427 // No clipping, so so set box bounding area for drawing full 4x4 pixel blocks
428 boxBounds.top = 0;
429 boxBounds.bottom = 3;
430 boxBounds.left = 0;
431 } else {
432 // Handle any possible clipping at the top of the char block.
433 // We already handled topClip partially at the beginning of this function.
434 // Hence the only non-zero values it can assume at this point are 1,2,3,
435 // and that only during the very first iteration (i.e. when the top char
436 // block is drawn only partially). In particular, we set topClip to zero,
437 // as all following blocks are not to be top clipped.
438 boxBounds.top = pObj->topClip;
439 pObj->topClip = 0;
440
441 boxBounds.bottom = MIN(boxBounds.top + pObj->height - 1, 3);
442
443 // Handle any possible clipping at the start of the line
444 boxBounds.left = pObj->leftClip;
445 if (boxBounds.left >= 4) {
446 srcP += sizeof(uint16) * (boxBounds.left >> 2);
447 width -= boxBounds.left & 0xfffc;
448 boxBounds.left %= 4;
449 }
450
451 width -= boxBounds.left;
452 }
453
454 // Horizontal loop
455 while (width > rightClip) {
456 boxBounds.right = MIN(boxBounds.left + width - rightClip - 1, 3);
457 assert(boxBounds.bottom >= boxBounds.top);
458 assert(boxBounds.right >= boxBounds.left);
459
460 int16 indexVal = READ_LE_UINT16(srcP);
461 srcP += sizeof(uint16);
462
463 if (indexVal >= 0) {
464 // Draw a 4x4 block based on the opcode as in index into the block list
465 const uint8 *p = (uint8 *)pObj->charBase + (indexVal << 4);
466 p += boxBounds.top * sizeof(uint32);
467 for (int yp = boxBounds.top; yp <= boxBounds.bottom; ++yp, p += sizeof(uint32)) {
468 Common::copy(p + boxBounds.left, p + boxBounds.right + 1, tempDest + (SCREEN_WIDTH * (yp - boxBounds.top)));
469 }
470
471 } else {
472 // Draw a 4x4 block with transparency support
473 indexVal &= 0x7fff;
474
475 // If index is zero, then skip drawing the block completely
476 if (indexVal > 0) {
477 // Use the index along with the object's translation offset
478 const uint8 *p = (uint8 *)pObj->charBase + ((pObj->transOffset + indexVal) << 4);
479
480 // Loop through each pixel - only draw a pixel if it's non-zero
481 p += boxBounds.top * sizeof(uint32);
482 for (int yp = boxBounds.top; yp <= boxBounds.bottom; ++yp) {
483 p += boxBounds.left;
484 for (int xp = boxBounds.left; xp <= boxBounds.right; ++xp, ++p) {
485 if (*p)
486 *(tempDest + SCREEN_WIDTH * (yp - boxBounds.top) + (xp - boxBounds.left)) = *p;
487 }
488 p += 3 - boxBounds.right;
489 }
490 }
491 }
492
493 tempDest += boxBounds.right - boxBounds.left + 1;
494 width -= 3 - boxBounds.left + 1;
495
496 // None of the remaining horizontal blocks should be left clipped
497 boxBounds.left = 0;
498 }
499
500 // If there is any width remaining, there must be a right edge clipping
501 if (width >= 0)
502 srcP += sizeof(uint16) * ((width + 3) >> 2);
503
504 // Move to next line line
505 pObj->height -= boxBounds.bottom - boxBounds.top + 1;
506 destP += (boxBounds.bottom - boxBounds.top + 1) * SCREEN_WIDTH;
507 }
508 }
509
510 /**
511 * Tinsel 2 Straight rendering with transparency support
512 */
t2WrtNonZero(DRAWOBJECT * pObj,uint8 * srcP,uint8 * destP,bool applyClipping,bool horizFlipped)513 static void t2WrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool applyClipping, bool horizFlipped) {
514 // Setup for correct clipping of object edges
515 int yClip = applyClipping ? pObj->topClip : 0;
516 if (applyClipping)
517 pObj->height -= pObj->botClip;
518 int numBytes;
519 int clipAmount;
520
521 // WORKAROUND: One of the mortician frames has several corrupt bytes in the Russian version
522 if ((pObj->hBits == 2517583660UL) && (_vm->getLanguage() == Common::RU_RUS)) {
523 uint8 correctBytes[5] = {0xA3, 0x00, 0x89, 0xC0, 0xA6};
524 Common::copy(&correctBytes[0], &correctBytes[5], srcP);
525 }
526
527 for (int y = 0; y < pObj->height; ++y) {
528 // Get the position to start writing out from
529 uint8 *tempP = !horizFlipped ? destP :
530 destP + (pObj->width - pObj->leftClip - pObj->rightClip) - 1;
531 int leftClip = applyClipping ? pObj->leftClip : 0;
532 int rightClip = applyClipping ? pObj->rightClip : 0;
533 if (horizFlipped)
534 SWAP(leftClip, rightClip);
535
536 int x = 0;
537 while (x < pObj->width) {
538 // Get the next opcode
539 numBytes = *srcP++;
540 if (numBytes & 0x80) {
541 // Run length following
542 numBytes &= 0x7f;
543 clipAmount = MIN(numBytes, leftClip);
544 leftClip -= clipAmount;
545 x+= clipAmount;
546
547 int runLength = numBytes - clipAmount;
548 uint8 color = *srcP++;
549
550 if ((yClip == 0) && (runLength > 0) && (color != 0)) {
551 runLength = MIN(runLength, pObj->width - rightClip - x);
552
553 if (runLength > 0) {
554 // Non-transparent run length
555 color += pObj->constant;
556 if (horizFlipped)
557 Common::fill(tempP - runLength + 1, tempP + 1, color);
558 else
559 Common::fill(tempP, tempP + runLength, color);
560 }
561 }
562
563 if (horizFlipped)
564 tempP -= runLength;
565 else
566 tempP += runLength;
567
568 x += numBytes - clipAmount;
569
570 } else {
571 // Dump a length of pixels
572 clipAmount = MIN(numBytes, leftClip);
573 leftClip -= clipAmount;
574 srcP += clipAmount;
575 int runLength = numBytes - clipAmount;
576 x += numBytes - runLength;
577
578 for (int xp = 0; xp < runLength; ++xp) {
579 if ((yClip > 0) || (x >= (pObj->width - rightClip)))
580 ++srcP;
581 else if (horizFlipped)
582 *tempP-- = pObj->constant + *srcP++;
583 else
584 *tempP++ = pObj->constant + *srcP++;
585 ++x;
586 }
587 }
588 }
589 assert(x == pObj->width);
590
591 if (yClip > 0)
592 --yClip;
593 else
594 destP += SCREEN_WIDTH;
595 }
596 }
597
598 /**
599 * Fill the destination area with a constant color
600 */
WrtConst(DRAWOBJECT * pObj,uint8 * destP,bool applyClipping)601 static void WrtConst(DRAWOBJECT *pObj, uint8 *destP, bool applyClipping) {
602 if (applyClipping) {
603 pObj->height -= pObj->topClip + pObj->botClip;
604 pObj->width -= pObj->leftClip + pObj->rightClip;
605
606 if (pObj->width <= 0)
607 return;
608 }
609
610 // Loop through any remaining lines
611 while (pObj->height > 0) {
612 Common::fill(destP, destP + pObj->width, pObj->constant);
613
614 --pObj->height;
615 destP += SCREEN_WIDTH;
616 }
617 }
618
619 /**
620 * Translates the destination surface within the object's bounds using the transparency
621 * lookup table from transpal.cpp (the contents of which have been moved into palette.cpp)
622 */
WrtTrans(DRAWOBJECT * pObj,uint8 * destP,bool applyClipping)623 static void WrtTrans(DRAWOBJECT *pObj, uint8 *destP, bool applyClipping) {
624 if (applyClipping) {
625 pObj->height -= pObj->topClip + pObj->botClip;
626 pObj->width -= pObj->leftClip + pObj->rightClip;
627
628 if (pObj->width <= 0)
629 return;
630 }
631
632 // Set up the offset between destination lines
633 int lineOffset = SCREEN_WIDTH - pObj->width;
634
635 // Loop through any remaining lines
636 while (pObj->height > 0) {
637 for (int i = 0; i < pObj->width; ++i, ++destP)
638 *destP = g_transPalette[*destP];
639
640 --pObj->height;
641 destP += lineOffset;
642 }
643 }
644
645 /**
646 * Copies an uncompressed block of data straight to the screen
647 */
WrtAll(DRAWOBJECT * pObj,uint8 * srcP,uint8 * destP,bool applyClipping)648 static void WrtAll(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool applyClipping) {
649 int objWidth = pObj->width;
650
651 if (applyClipping) {
652 srcP += (pObj->topClip * pObj->width) + pObj->leftClip;
653
654 pObj->height -= pObj->topClip + pObj->botClip;
655 pObj->width -= pObj->leftClip + pObj->rightClip;
656
657 if (pObj->width <= 0)
658 return;
659 }
660
661 for (int y = 0; y < pObj->height; ++y) {
662 Common::copy(srcP, srcP + pObj->width, destP);
663 srcP += objWidth;
664 destP += SCREEN_WIDTH;
665 }
666 }
667
668 /**
669 * Renders a packed data stream with a variable sized palette
670 */
PackedWrtNonZero(DRAWOBJECT * pObj,uint8 * srcP,uint8 * destP,bool applyClipping,bool horizFlipped,int packingType)671 static void PackedWrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP,
672 bool applyClipping, bool horizFlipped, int packingType) {
673 uint8 numColors = 0;
674 uint8 *colorTable = NULL;
675 int topClip = 0;
676 int xOffset = 0;
677 int numBytes, color;
678 int v;
679
680 if (_vm->getLanguage() == Common::RU_RUS) {
681 // WORKAROUND: One of the mortician frames has several corrupt bytes in the Russian version
682 if (pObj->hBits == 2517583393UL) {
683 uint8 correctBytes[5] = {0x00, 0x00, 0x17, 0x01, 0x00};
684 Common::copy(&correctBytes[0], &correctBytes[5], srcP + 267);
685 }
686 // WORKAROUND: One of Dibbler's frames in the end sequence has corrupt bytes in the Russian version
687 if (pObj->hBits == 33651742) {
688 uint8 correctBytes[40] = {
689 0x06, 0xc0, 0xd6, 0xc1, 0x09, 0xce, 0x0d, 0x24, 0x02, 0x12, 0x01, 0x00, 0x00, 0x23, 0x21, 0x32,
690 0x12, 0x00, 0x00, 0x20, 0x01, 0x11, 0x32, 0x12, 0x01, 0x00, 0x00, 0x1b, 0x02, 0x11, 0x34, 0x11,
691 0x00, 0x00, 0x18, 0x01, 0x11, 0x35, 0x21, 0x01
692 };
693 Common::copy(&correctBytes[0], &correctBytes[40], srcP);
694 }
695 }
696
697 if (applyClipping) {
698 pObj->height -= pObj->botClip;
699 topClip = pObj->topClip;
700 }
701
702 if (packingType == 3) {
703 // Variable colors
704 numColors = *srcP++;
705 colorTable = srcP;
706 srcP += numColors;
707 }
708
709 for (int y = 0; y < pObj->height; ++y) {
710 // Get the position to start writing out from
711 uint8 *tempP = !horizFlipped ? destP :
712 destP + (pObj->width - pObj->leftClip - pObj->rightClip) - 1;
713 int leftClip = applyClipping ? pObj->leftClip : 0;
714 int rightClip = applyClipping ? pObj->rightClip : 0;
715 if (horizFlipped)
716 SWAP(leftClip, rightClip);
717 bool eolFlag = false;
718
719 // Get offset for first pixels in next line
720 xOffset = *srcP++;
721
722 int x = 0;
723 while (x < pObj->width) {
724 // Get next run size and color to use
725 for (;;) {
726 if (xOffset > 0) {
727 x += xOffset;
728
729 // Reduce offset amount by any remaining left clipping
730 v = MIN(xOffset, leftClip);
731 xOffset -= v;
732 leftClip -= v;
733
734 if (horizFlipped) tempP -= xOffset; else tempP += xOffset;
735 xOffset = 0;
736 }
737
738 v = *srcP++;
739 numBytes = v & 0xf; // No. bytes 1-15
740 if (packingType == 3)
741 color = colorTable[v >> 4];
742 else
743 color = pObj->baseCol + (v >> 4);
744
745 if (numBytes != 0)
746 break;
747
748 numBytes = *srcP++;
749 if (numBytes >= 16)
750 break;
751
752 xOffset = numBytes + v;
753 if (xOffset == 0) {
754 // End of line encountered
755 eolFlag = true;
756 break;
757 }
758 }
759
760 if (eolFlag)
761 break;
762
763 // Apply clipping on byte sequence
764 v = MIN(numBytes, leftClip);
765 leftClip -= v;
766 numBytes -= v;
767 x += v;
768
769 while (numBytes-- > 0) {
770 if ((topClip == 0) && (x < (pObj->width - rightClip))) {
771 *tempP = color;
772 if (horizFlipped) --tempP; else ++tempP;
773 }
774 ++x;
775 }
776 }
777 assert(x <= pObj->width);
778
779 if (!eolFlag) {
780 // Assert that the next bytes signal a line end
781 uint8 d = *srcP++;
782 assert((d & 0xf) == 0);
783 d = *srcP++;
784 assert(d == 0);
785 }
786
787 if (topClip > 0)
788 --topClip;
789 else
790 destP += SCREEN_WIDTH;
791 }
792 }
793
794 //----------------- MAIN FUNCTIONS ---------------------
795
796 /**
797 * Clears both the screen surface buffer and screen to the specified value
798 */
ClearScreen()799 void ClearScreen() {
800 byte blackColorIndex = (!TinselV1Mac) ? 0 : 255;
801 void *pDest = _vm->screen().getPixels();
802 memset(pDest, blackColorIndex, SCREEN_WIDTH * SCREEN_HEIGHT);
803 g_system->fillScreen(blackColorIndex);
804 g_system->updateScreen();
805 }
806
807 /**
808 * Updates the screen surface within the following rectangle
809 */
UpdateScreenRect(const Common::Rect & pClip)810 void UpdateScreenRect(const Common::Rect &pClip) {
811 int yOffset = TinselV2 ? (g_system->getHeight() - SCREEN_HEIGHT) / 2 : 0;
812 byte *pSrc = (byte *)_vm->screen().getBasePtr(pClip.left, pClip.top);
813 g_system->copyRectToScreen(pSrc, _vm->screen().pitch, pClip.left, pClip.top + yOffset,
814 pClip.width(), pClip.height());
815 }
816
817 /**
818 * Draws the specified object onto the screen surface buffer
819 */
DrawObject(DRAWOBJECT * pObj)820 void DrawObject(DRAWOBJECT *pObj) {
821 uint8 *srcPtr = NULL;
822 uint8 *destPtr;
823 byte psxMapperTable[16];
824
825 bool psxFourBitClut = false; // Used by Tinsel PSX, true if an image using a 4bit CLUT is rendered
826 bool psxRLEindex = false; // Used by Tinsel PSX, true if an image is using PJCRLE compressed indexes
827 uint32 psxSkipBytes = 0; // Used by Tinsel PSX, number of bytes to skip before counting indexes for image tiles
828
829 if ((pObj->width <= 0) || (pObj->height <= 0))
830 // Empty image, so return immediately
831 return;
832
833 // If writing constant data, don't bother locking the data pointer and reading src details
834 if ((pObj->flags & DMA_CONST) == 0) {
835 if (TinselV2) {
836 srcPtr = (byte *)LockMem(pObj->hBits);
837 pObj->charBase = NULL;
838 pObj->transOffset = 0;
839 } else {
840 byte *p = (byte *)LockMem(pObj->hBits & HANDLEMASK);
841
842 srcPtr = p + (pObj->hBits & OFFSETMASK);
843 pObj->charBase = (char *)p + READ_LE_UINT32(p + 0x10);
844 pObj->transOffset = READ_LE_UINT32(p + 0x14);
845
846 // Decompress block indexes for Discworld PSX
847 if (TinselV1PSX) {
848 uint8 paletteType = *(srcPtr); // if 0x88 we are using an 8bit palette type, if 0x44 we are using a 4 bit PSX CLUT
849 uint8 indexType = *(srcPtr + 1); // if 0xCC indexes for this image are compressed with PCJRLE, if 0xDD indexes are not compressed
850
851 switch (paletteType) {
852 case 0x88: // Normal 8-bit palette
853 psxFourBitClut = false;
854 psxSkipBytes = 0;
855 switch (indexType) {
856 case 0xDD: // Normal uncompressed indexes
857 psxRLEindex = false;
858 srcPtr += sizeof(uint16); // Get to the beginning of index data
859 break;
860 case 0xCC: // PJCRLE compressed indexes
861 psxRLEindex = true;
862 srcPtr = psxPJCRLEUnwinder(pObj->width, pObj->height, srcPtr + sizeof(uint16));
863 break;
864 default:
865 error("Unknown PSX index type 0x%.2X", indexType);
866 break;
867 }
868 break;
869 case 0x44: // PSX 4-bit CLUT
870 psxPaletteMapper(pObj->pPal, srcPtr + sizeof(uint16), psxMapperTable);
871
872 psxFourBitClut = true;
873 psxSkipBytes = READ_LE_UINT32(p + sizeof(uint32) * 5) << 4; // Fetch number of bytes we have to skip
874 switch (indexType) {
875 case 0xDD: // Normal uncompressed indexes
876 psxRLEindex = false;
877 srcPtr += sizeof(uint16) * 17; // Skip image type and clut, and get to beginning of index data
878 break;
879 case 0xCC: // PJCRLE compressed indexes
880 psxRLEindex = true;
881 srcPtr = psxPJCRLEUnwinder(pObj->width, pObj->height, srcPtr + sizeof(uint16) * 17);
882 break;
883 default:
884 error("Unknown PSX index type 0x%.2X", indexType);
885 break;
886 }
887 break;
888 default:
889 error("Unknown PSX palette type 0x%.2X", paletteType);
890 break;
891 }
892 }
893 }
894
895 }
896
897 // Get destination starting point
898 destPtr = (byte *)_vm->screen().getBasePtr(pObj->xPos, pObj->yPos);
899
900 // Handle various draw types
901 uint8 typeId = pObj->flags & 0xff;
902 int packType = pObj->flags >> 14; // TinselV2
903
904 if (TinselV2 && packType != 0) {
905 // Color packing for TinselV2
906
907 if (packType == 1)
908 pObj->baseCol = 0xF0; // 16 from 240
909 else if (packType == 2)
910 pObj->baseCol = 0xE0; // 16 from 224
911 // 3 = variable color
912
913 PackedWrtNonZero(pObj, srcPtr, destPtr, (pObj->flags & DMA_CLIP) != 0,
914 (pObj->flags & DMA_FLIPH), packType);
915 } else {
916 switch (typeId) {
917 case 0x01: // all versions, draw sprite without clipping
918 case 0x41: // all versions, draw sprite with clipping
919 case 0x02: // TinselV2, draw sprite without clipping
920 case 0x11: // TinselV2, draw sprite without clipping, flipped horizontally
921 case 0x42: // TinselV2, draw sprite with clipping
922 case 0x51: // TinselV2, draw sprite with clipping, flipped horizontally
923 case 0x81: // TinselV2, draw sprite with clipping
924 case 0xC1: // TinselV2, draw sprite with clipping
925 assert(TinselV2 || (typeId == 0x01 || typeId == 0x41));
926
927 if (TinselV2)
928 t2WrtNonZero(pObj, srcPtr, destPtr, typeId >= 0x40, (typeId & 0x10) != 0);
929 else if (TinselV1PSX)
930 PsxDrawTiles(pObj, srcPtr, destPtr, typeId == 0x41, psxFourBitClut, psxSkipBytes, psxMapperTable, true);
931 else if (TinselV1Mac)
932 MacDrawTiles(pObj, srcPtr, destPtr, typeId == 0x41);
933 else if (TinselV1)
934 WrtNonZero(pObj, srcPtr, destPtr, typeId == 0x41);
935 else if (TinselV0)
936 t0WrtNonZero(pObj, srcPtr, destPtr, typeId == 0x41);
937 break;
938 case 0x08: // draw background without clipping
939 case 0x48: // draw background with clipping
940 if (TinselV2 || TinselV1Mac || TinselV0)
941 WrtAll(pObj, srcPtr, destPtr, typeId == 0x48);
942 else if (TinselV1PSX)
943 PsxDrawTiles(pObj, srcPtr, destPtr, typeId == 0x48, psxFourBitClut, psxSkipBytes, psxMapperTable, false);
944 else if (TinselV1)
945 WrtNonZero(pObj, srcPtr, destPtr, typeId == 0x48);
946 break;
947 case 0x04: // fill with constant color without clipping
948 case 0x44: // fill with constant color with clipping
949 WrtConst(pObj, destPtr, typeId == 0x44);
950 break;
951 case 0x84: // draw transparent surface without clipping
952 case 0xC4: // draw transparent surface with clipping
953 WrtTrans(pObj, destPtr, typeId == 0xC4);
954 break;
955 default:
956 error("Unknown drawing type %d", typeId);
957 }
958 }
959
960 // If we were using Discworld PSX, free the memory allocated
961 // for decompressed block indexes.
962 if (TinselV1PSX && psxRLEindex)
963 free(srcPtr);
964 }
965
966 } // End of namespace Tinsel
967