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