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 "sherlock/image_file.h"
24 #include "sherlock/screen.h"
25 #include "sherlock/sherlock.h"
26 #include "common/debug.h"
27 #include "common/memstream.h"
28 
29 namespace Sherlock {
30 
31 SherlockEngine *ImageFile::_vm;
32 
setVm(SherlockEngine * vm)33 void ImageFile::setVm(SherlockEngine *vm) {
34 	_vm = vm;
35 }
36 
ImageFile()37 ImageFile::ImageFile() {
38 }
39 
ImageFile(const Common::String & name,bool skipPal,bool animImages)40 ImageFile::ImageFile(const Common::String &name, bool skipPal, bool animImages) {
41 	Common::SeekableReadStream *stream = _vm->_res->load(name);
42 
43 	Common::fill(&_palette[0], &_palette[PALETTE_SIZE], 0);
44 	load(*stream, skipPal, animImages);
45 
46 	delete stream;
47 }
48 
ImageFile(Common::SeekableReadStream & stream,bool skipPal)49 ImageFile::ImageFile(Common::SeekableReadStream &stream, bool skipPal) {
50 	Common::fill(&_palette[0], &_palette[PALETTE_SIZE], 0);
51 	load(stream, skipPal, false);
52 }
53 
~ImageFile()54 ImageFile::~ImageFile() {
55 	for (uint idx = 0; idx < size(); ++idx)
56 		(*this)[idx]._frame.free();
57 }
58 
load(Common::SeekableReadStream & stream,bool skipPalette,bool animImages)59 void ImageFile::load(Common::SeekableReadStream &stream, bool skipPalette, bool animImages) {
60 	loadPalette(stream);
61 
62 	int streamSize = stream.size();
63 	while (stream.pos() < streamSize) {
64 		ImageFrame frame;
65 		frame._width = stream.readUint16LE() + 1;
66 		frame._height = stream.readUint16LE() + 1;
67 		frame._paletteBase = stream.readByte();
68 
69 		if (animImages) {
70 			// Animation cutscene image files use a 16-bit x offset
71 			frame._offset.x = stream.readUint16LE();
72 			frame._rleEncoded = (frame._offset.x & 0xff) == 1;
73 			frame._offset.y = stream.readByte();
74 		} else {
75 			// Standard image files have a separate byte for the RLE flag, and an 8-bit X offset
76 			frame._rleEncoded = stream.readByte() == 1;
77 			frame._offset.x = stream.readByte();
78 			frame._offset.y = stream.readByte();
79 		}
80 
81 		frame._rleEncoded = !skipPalette && frame._rleEncoded;
82 
83 		if (frame._paletteBase) {
84 			// Nibble packed frame data
85 			frame._size = (frame._width * frame._height) / 2;
86 		} else if (frame._rleEncoded) {
87 			// This size includes the header size, which we subtract
88 			frame._size = stream.readUint16LE() - 11;
89 			frame._rleMarker = stream.readByte();
90 		} else {
91 			// Uncompressed data
92 			frame._size = frame._width * frame._height;
93 		}
94 
95 		// Load data for frame and decompress it
96 		byte *data1 = new byte[frame._size + 4];
97 		stream.read(data1, frame._size);
98 		Common::fill(data1 + frame._size, data1 + frame._size + 4, 0);
99 		frame.decompressFrame(data1, IS_ROSE_TATTOO);
100 		delete[] data1;
101 
102 		push_back(frame);
103 	}
104 }
105 
loadPalette(Common::SeekableReadStream & stream)106 void ImageFile::loadPalette(Common::SeekableReadStream &stream) {
107 	// Check for palette
108 	uint16 width        = stream.readUint16LE() + 1;
109 	uint16 height       = stream.readUint16LE() + 1;
110 	byte   paletteBase  = stream.readByte();
111 	byte   rleEncoded   = stream.readByte();
112 	byte   offsetX      = stream.readByte();
113 	byte   offsetY      = stream.readByte();
114 	uint32 palSignature = 0;
115 
116 	if ((width == 390) && (height == 2) && (!paletteBase) && (!rleEncoded) && (!offsetX) && (!offsetY)) {
117 		// We check for these specific values
118 		// We can't do "width * height", because at least the first German+Spanish menu bar is 60 x 13
119 		// which is 780, which is the size of the palette. We obviously don't want to detect it as palette.
120 
121 		// As another security measure, we also check for the signature text
122 		palSignature = stream.readUint32BE();
123 		if (palSignature != MKTAG('V', 'G', 'A', ' ')) {
124 			// signature mismatch, rewind
125 			stream.seek(-12, SEEK_CUR);
126 			return;
127 		}
128 		// Found palette, so read it in
129 		stream.seek(8, SEEK_CUR); // Skip over the rest of the signature text "VGA palette"
130 		for (int idx = 0; idx < PALETTE_SIZE; ++idx)
131 			_palette[idx] = VGA_COLOR_TRANS(stream.readByte());
132 	} else {
133 		// Not a palette, so rewind to start of frame data for normal frame processing
134 		stream.seek(-8, SEEK_CUR);
135 	}
136 }
137 
decompressFrame(const byte * src,bool isRoseTattoo)138 void ImageFrame::decompressFrame(const byte *src, bool isRoseTattoo) {
139 	_frame.create(_width, _height, Graphics::PixelFormat::createFormatCLUT8());
140 	byte *dest = (byte *)_frame.getPixels();
141 	Common::fill(dest, dest + _width * _height, 0xff);
142 
143 	if (_paletteBase) {
144 		// Nibble-packed
145 		for (uint idx = 0; idx < _size; ++idx, ++src) {
146 			*dest++ = *src & 0xF;
147 			*dest++ = (*src >> 4);
148 		}
149 	} else if (_rleEncoded && isRoseTattoo) {
150 		// Rose Tattoo run length encoding doesn't use the RLE marker byte
151 		for (int yp = 0; yp < _height; ++yp) {
152 			int xSize = _width;
153 			while (xSize > 0) {
154 				// Skip a given number of pixels
155 				byte skip = *src++;
156 				dest += skip;
157 				xSize -= skip;
158 				if (!xSize)
159 					break;
160 
161 				// Get a run length, and copy the following number of pixels
162 				int rleCount = *src++;
163 				xSize -= rleCount;
164 				while (rleCount-- > 0)
165 					*dest++ = *src++;
166 			}
167 			assert(xSize == 0);
168 		}
169 	} else if (_rleEncoded) {
170 		// RLE encoded
171 		int frameSize = _width * _height;
172 		while (frameSize > 0) {
173 			if (*src == _rleMarker) {
174 				byte rleColor = src[1];
175 				byte rleCount = MIN((int)src[2], frameSize);
176 				src += 3;
177 				frameSize -= rleCount;
178 				while (rleCount--)
179 					*dest++ = rleColor;
180 			} else {
181 				*dest++ = *src++;
182 				--frameSize;
183 			}
184 		}
185 	} else {
186 		// Uncompressed frame
187 		Common::copy(src, src + _width * _height, dest);
188 	}
189 }
190 
191 /*----------------------------------------------------------------*/
192 
sDrawXSize(int scaleVal) const193 int ImageFrame::sDrawXSize(int scaleVal) const {
194 	int width = _width;
195 	int scale = scaleVal == 0 ? 1 : scaleVal;
196 
197 	if (scaleVal >= SCALE_THRESHOLD)
198 		--width;
199 
200 	int result = width * SCALE_THRESHOLD / scale;
201 	if (scaleVal >= SCALE_THRESHOLD)
202 		++result;
203 
204 	return result;
205 }
206 
sDrawYSize(int scaleVal) const207 int ImageFrame::sDrawYSize(int scaleVal) const {
208 	int height = _height;
209 	int scale = scaleVal == 0 ? 1 : scaleVal;
210 
211 	if (scaleVal >= SCALE_THRESHOLD)
212 		--height;
213 
214 	int result = height * SCALE_THRESHOLD / scale;
215 	if (scaleVal >= SCALE_THRESHOLD)
216 		++result;
217 
218 	return result;
219 }
220 
sDrawXOffset(int scaleVal) const221 int ImageFrame::sDrawXOffset(int scaleVal) const {
222 	if (scaleVal == SCALE_THRESHOLD)
223 		return _offset.x;
224 
225 	int width = _offset.x;
226 	int scale = scaleVal == 0 ? 1 : scaleVal;
227 
228 	if (scaleVal >= SCALE_THRESHOLD)
229 		--width;
230 
231 	int result = width * SCALE_THRESHOLD / scale;
232 	if (scaleVal > SCALE_THRESHOLD)
233 		++result;
234 
235 	return result;
236 }
237 
sDrawYOffset(int scaleVal) const238 int ImageFrame::sDrawYOffset(int scaleVal) const {
239 	if (scaleVal == SCALE_THRESHOLD)
240 		return _offset.y;
241 
242 	int height = _offset.y;
243 	int scale = scaleVal == 0 ? 1 : scaleVal;
244 
245 	if (scaleVal >= SCALE_THRESHOLD)
246 		--height;
247 
248 	int result = height * SCALE_THRESHOLD / scale;
249 	if (scaleVal > SCALE_THRESHOLD)
250 		++result;
251 
252 	return result;
253 }
254 
255 // *******************************************************
256 
257 /*----------------------------------------------------------------*/
258 
259 SherlockEngine *ImageFile3DO::_vm;
260 
setVm(SherlockEngine * vm)261 void ImageFile3DO::setVm(SherlockEngine *vm) {
262 	_vm = vm;
263 }
264 
ImageFile3DO(const Common::String & name,ImageFile3DOType imageFile3DOType)265 ImageFile3DO::ImageFile3DO(const Common::String &name, ImageFile3DOType imageFile3DOType) {
266 #if 0
267 	Common::File *dataStream = new Common::File();
268 
269 	if (!dataStream->open(name)) {
270 		error("unable to open %s\n", name.c_str());
271 	}
272 #endif
273 	Common::SeekableReadStream *dataStream = _vm->_res->load(name);
274 
275 	switch(imageFile3DOType) {
276 	case kImageFile3DOType_Animation:
277 		loadAnimationFile(*dataStream);
278 		break;
279 	case kImageFile3DOType_Cel:
280 	case kImageFile3DOType_CelAnimation:
281 		load3DOCelFile(*dataStream);
282 		break;
283 	case kImageFile3DOType_RoomFormat:
284 		load3DOCelRoomData(*dataStream);
285 		break;
286 	case kImageFile3DOType_Font:
287 		loadFont(*dataStream);
288 		break;
289 	default:
290 		error("unknown Imagefile-3DO-Type");
291 		break;
292 	}
293 
294 	delete dataStream;
295 }
296 
ImageFile3DO(Common::SeekableReadStream & stream,bool isRoomData)297 ImageFile3DO::ImageFile3DO(Common::SeekableReadStream &stream, bool isRoomData) {
298 	if (!isRoomData) {
299 		load(stream, isRoomData);
300 	} else {
301 		load3DOCelRoomData(stream);
302 	}
303 }
304 
load(Common::SeekableReadStream & stream,bool isRoomData)305 void ImageFile3DO::load(Common::SeekableReadStream &stream, bool isRoomData) {
306 	uint32 headerId = 0;
307 
308 	if (isRoomData) {
309 		load3DOCelRoomData(stream);
310 		return;
311 	}
312 
313 	headerId = stream.readUint32BE();
314 	assert(!stream.eos());
315 
316 	// Seek back to the start
317 	stream.seek(-4, SEEK_CUR);
318 
319 	// Identify type of file
320 	switch (headerId) {
321 	case MKTAG('C', 'C', 'B', ' '):
322 	case MKTAG('A', 'N', 'I', 'M'):
323 	case MKTAG('O', 'F', 'S', 'T'): // 3DOSplash.cel
324 		// 3DO .cel (title1a.cel, etc.) or animation file (walk.anim)
325 		load3DOCelFile(stream);
326 		break;
327 
328 	default:
329 		// Sherlock animation file (.3da files)
330 		loadAnimationFile(stream);
331 		break;
332 	}
333 }
334 
335 // 3DO uses RGB555, we use RGB565 internally so that more platforms are able to run us
convertPixel(uint16 pixel3DO)336 inline uint16 ImageFile3DO::convertPixel(uint16 pixel3DO) {
337 	byte red   = (pixel3DO >> 10) & 0x1F;
338 	byte green = (pixel3DO >> 5) & 0x1F;
339 	byte blue  = pixel3DO & 0x1F;
340 
341 	return ((red << 11) | (green << 6) | (blue));
342 }
343 
loadAnimationFile(Common::SeekableReadStream & stream)344 void ImageFile3DO::loadAnimationFile(Common::SeekableReadStream &stream) {
345 	uint32 streamLeft = stream.size() - stream.pos();
346 	uint32 celDataSize = 0;
347 
348 	while (streamLeft > 0) {
349 		ImageFrame frame;
350 
351 		// We expect a basic header of 8 bytes
352 		if (streamLeft < 8)
353 			error("load3DOAnimationFile: expected animation header, not enough bytes");
354 
355 		celDataSize = stream.readUint16BE();
356 
357 		frame._width = stream.readUint16BE() + 1; // 2 bytes BE width
358 		frame._height = stream.readByte() + 1; // 1 byte BE height
359 		frame._paletteBase = 0;
360 
361 		frame._rleEncoded = true; // always compressed
362 		if (frame._width & 0x8000) {
363 			frame._width &= 0x7FFF;
364 			celDataSize += 0x10000;
365 		}
366 
367 		frame._offset.x = stream.readUint16BE();
368 		frame._offset.y = stream.readByte();
369 		frame._size = 0;
370 		// Got header
371 		streamLeft -= 8;
372 
373 		// cel data follows
374 		if (streamLeft < celDataSize)
375 			error("load3DOAnimationFile: expected cel data, not enough bytes");
376 
377 		//
378 		// Load data for frame and decompress it
379 		byte *data_ = new byte[celDataSize];
380 		stream.read(data_, celDataSize);
381 		streamLeft -= celDataSize;
382 
383 		// always 16 bits per pixel (RGB555)
384 		decompress3DOCelFrame(frame, data_, celDataSize, 16, NULL);
385 
386 		delete[] data_;
387 
388 		push_back(frame);
389 	}
390 }
391 
392 static byte imagefile3DO_cel_bitsPerPixelLookupTable[8] = {
393 	0, 1, 2, 4, 6, 8, 16, 0
394 };
395 
396 // Reads a 3DO .cel/.anim file
load3DOCelFile(Common::SeekableReadStream & stream)397 void ImageFile3DO::load3DOCelFile(Common::SeekableReadStream &stream) {
398 	int32  streamSize = stream.size();
399 	int32  chunkStartPos = 0;
400 	uint32 chunkTag = 0;
401 	uint32 chunkSize = 0;
402 	byte  *chunkDataPtr = NULL;
403 
404 	// ANIM chunk (animation header for animation files)
405 	bool   animFound = false;
406 	uint32 animVersion = 0;
407 	uint32 animType = 0;
408 	uint32 animFrameCount = 1; // we expect 1 frame without an ANIM header
409 	// CCB chunk (cel control block)
410 	bool   ccbFound = false;
411 	uint32 ccbVersion = 0;
412 	uint32 ccbFlags = 0;
413 	bool   ccbFlags_compressed = false;
414 	uint16 ccbPPMP0 = 0;
415 	uint16 ccbPPMP1 = 0;
416 	uint32 ccbPRE0 = 0;
417 	uint16 ccbPRE0_height = 0;
418 	byte   ccbPRE0_bitsPerPixel = 0;
419 	uint32 ccbPRE1 = 0;
420 	uint16 ccbPRE1_width = 0;
421 	uint32 ccbWidth = 0;
422 	uint32 ccbHeight = 0;
423 	// pixel lookup table
424 	bool   plutFound = false;
425 	uint32 plutCount = 0;
426 	ImageFile3DOPixelLookupTable plutRGBlookupTable;
427 
428 	memset(&plutRGBlookupTable, 0, sizeof(plutRGBlookupTable));
429 
430 	while (!stream.err() && (stream.pos() < streamSize)) {
431 		chunkStartPos = stream.pos();
432 		chunkTag = stream.readUint32BE();
433 		chunkSize = stream.readUint32BE();
434 
435 		if (stream.eos() || stream.err())
436 			break;
437 
438 		if (chunkSize < 8)
439 			error("load3DOCelFile: Invalid chunk size");
440 
441 		uint32 dataSize = chunkSize - 8;
442 
443 		switch (chunkTag) {
444 		case MKTAG('A', 'N', 'I', 'M'):
445 			// animation header
446 			assert(dataSize >= 24);
447 
448 			if (animFound)
449 				error("load3DOCelFile: multiple ANIM chunks not supported");
450 
451 			animFound   = true;
452 			animVersion = stream.readUint32BE();
453 			animType = stream.readUint32BE();
454 			animFrameCount = stream.readUint32BE();
455 			// UINT32 - framerate (0x2000 in walk.anim???)
456 			// UINT32 - starting frame (0 for walk.anim)
457 			// UINT32 - number of loops (0 for walk.anim)
458 
459 			if (animVersion != 0)
460 				error("load3DOCelFile: Unsupported animation file version");
461 			if (animType != 1)
462 				error("load3DOCelFile: Only single CCB animation files are supported");
463 			break;
464 
465 		case MKTAG('C', 'C', 'B', ' '):
466 			// CEL control block
467 			assert(dataSize >= 72);
468 
469 			if (ccbFound)
470 				error("load3DOCelFile: multiple CCB chunks not supported");
471 
472 			ccbFound   = true;
473 			ccbVersion = stream.readUint32BE();
474 			ccbFlags   = stream.readUint32BE();
475 			stream.skip(3 * 4); // skip over 3 pointer fields, which are used in memory only by 3DO hardware
476 			stream.skip(8 * 4); // skip over 8 offset fields
477 			ccbPPMP0   = stream.readUint16BE();
478 			ccbPPMP1   = stream.readUint16BE();
479 			ccbPRE0    = stream.readUint32BE();
480 			ccbPRE1    = stream.readUint32BE();
481 			ccbWidth   = stream.readUint32BE();
482 			ccbHeight  = stream.readUint32BE();
483 
484 			if (ccbVersion != 0)
485 				error("load3DOCelFile: Unsupported CCB version");
486 
487 			if (ccbFlags & 0x200) // bit 9
488 				ccbFlags_compressed = true;
489 
490 			// bit 5 of ccbFlags defines how RGB-black (0, 0, 0) will get treated
491 			// = false -> RGB-black is treated as transparent
492 			// = true  -> RGB-black is treated as actual black
493 			// atm we are always treating it as transparent
494 			// it seems this bit is not set for any data of Sherlock Holmes
495 
496 			// PRE0 first 3 bits define how many bits per encoded pixel are used
497 			ccbPRE0_bitsPerPixel = imagefile3DO_cel_bitsPerPixelLookupTable[ccbPRE0 & 0x07];
498 			if (!ccbPRE0_bitsPerPixel)
499 				error("load3DOCelFile: Invalid CCB PRE0 bits per pixel");
500 
501 			ccbPRE0_height = ((ccbPRE0 >> 6) & 0x03FF) + 1;
502 			ccbPRE1_width  = (ccbPRE1 & 0x03FF) + 1;
503 			assert(ccbPRE0_height == ccbHeight);
504 			assert(ccbPRE1_width == ccbWidth);
505 			break;
506 
507 		case MKTAG('P', 'L', 'U', 'T'):
508 			// pixel lookup table
509 			// optional, not required for at least 16-bit pixel data
510 			assert(dataSize >= 6);
511 
512 			if (!ccbFound)
513 				error("load3DOCelFile: PLUT chunk found without CCB chunk");
514 			if (plutFound)
515 				error("load3DOCelFile: multiple PLUT chunks currently not supported");
516 
517 			plutFound = true;
518 			plutCount = stream.readUint32BE();
519 			// table follows, each entry is 16bit RGB555
520 			assert(dataSize >= 4 + (plutCount * 2)); // security check
521 			assert(plutCount <= 256); // security check
522 
523 			assert(plutCount <= 32); // PLUT should never contain more than 32 entries
524 
525 			for (uint32 plutColorNr = 0; plutColorNr < plutCount; plutColorNr++) {
526 				plutRGBlookupTable.pixelColor[plutColorNr] = stream.readUint16BE();
527 			}
528 
529 			if (ccbPRE0_bitsPerPixel == 8) {
530 				// In case we are getting 8-bits per pixel, we calculate the shades accordingly
531 				// I'm not 100% sure if the calculation is correct. It's difficult to find information
532 				// on this topic.
533 				// The map uses this type of cel
534 				assert(plutCount == 32); // And we expect 32 entries inside PLUT chunk
535 
536 				uint16 plutColorRGB = 0;
537 				for (uint32 plutColorNr = 0; plutColorNr < plutCount; plutColorNr++) {
538 					plutColorRGB = plutRGBlookupTable.pixelColor[plutColorNr];
539 
540 					// Extract RGB values
541 					byte plutColorRed   = (plutColorRGB >> 10) & 0x1F;
542 					byte plutColorGreen = (plutColorRGB >> 5) & 0x1F;
543 					byte plutColorBlue  = plutColorRGB & 0x1F;
544 
545 					byte shadeMultiplier = 2;
546 					for (uint32 plutShadeNr = 1; plutShadeNr < 8; plutShadeNr++) {
547 						uint16 shadedColorRGB;
548 						byte   shadedColorRed   = (plutColorRed * shadeMultiplier) >> 3;
549 						byte   shadedColorGreen = (plutColorGreen * shadeMultiplier) >> 3;
550 						byte   shadedColorBlue  = (plutColorBlue * shadeMultiplier) >> 3;
551 
552 						shadedColorRed = CLIP<byte>(shadedColorRed, 0, 0x1F);
553 						shadedColorGreen = CLIP<byte>(shadedColorGreen, 0, 0x1F);
554 						shadedColorBlue = CLIP<byte>(shadedColorBlue, 0, 0x1F);
555 						shadedColorRGB = (shadedColorRed << 10) | (shadedColorGreen << 5) | shadedColorBlue;
556 
557 						plutRGBlookupTable.pixelColor[plutColorNr + (plutShadeNr << 5)] = shadedColorRGB;
558 						shadeMultiplier++;
559 					}
560 				}
561 			}
562 			break;
563 
564 		case MKTAG('X', 'T', 'R', 'A'):
565 			// Unknown contents, occurs right before PDAT
566 			break;
567 
568 		case MKTAG('P', 'D', 'A', 'T'): {
569 			// pixel data for one frame
570 			// may be compressed or uncompressed pixels
571 
572 			if (ccbPRE0_bitsPerPixel != 16) {
573 				// We require a pixel lookup table in case bits-per-pixel is lower than 16
574 				if (!plutFound)
575 					error("load3DOCelFile: bits per pixel < 16, but no pixel lookup table was found");
576 			} else {
577 				// But we don't like it in case bits-per-pixel is 16 and we find one
578 				if (plutFound)
579 					error("load3DOCelFile: bits per pixel == 16, but pixel lookup table was found as well");
580 			}
581 			// read data into memory
582 			chunkDataPtr = new byte[dataSize];
583 
584 			stream.read(chunkDataPtr, dataSize);
585 
586 			// Set up frame
587 			ImageFrame imageFrame;
588 
589 			imageFrame._width = ccbWidth;
590 			imageFrame._height = ccbHeight;
591 			imageFrame._paletteBase = 0;
592 			imageFrame._offset.x = 0;
593 			imageFrame._offset.y = 0;
594 			imageFrame._rleEncoded = ccbFlags_compressed;
595 			imageFrame._size = 0;
596 
597 			// Decompress/copy this frame
598 			if (!plutFound) {
599 				decompress3DOCelFrame(imageFrame, chunkDataPtr, dataSize, ccbPRE0_bitsPerPixel, NULL);
600 			} else {
601 				decompress3DOCelFrame(imageFrame, chunkDataPtr, dataSize, ccbPRE0_bitsPerPixel, &plutRGBlookupTable);
602 			}
603 
604 			delete[] chunkDataPtr;
605 
606 			push_back(imageFrame);
607 			break;
608 		}
609 
610 		case MKTAG('O', 'F', 'S', 'T'): // 3DOSplash.cel
611 			// unknown contents
612 			break;
613 
614 		default:
615 			error("Unsupported '%s' chunk in 3DO cel file", tag2str(chunkTag));
616 		}
617 
618 		// Seek to end of chunk
619 		stream.seek(chunkStartPos + chunkSize);
620 	}
621 
622 	// Warning below being used to silence unused variable warnings for now
623 	warning("TODO: Remove %d %d %d", animFrameCount, ccbPPMP0, ccbPPMP1);
624 }
625 
626 // Reads 3DO .cel data (room file format)
load3DOCelRoomData(Common::SeekableReadStream & stream)627 void ImageFile3DO::load3DOCelRoomData(Common::SeekableReadStream &stream) {
628 	uint32 streamLeft = stream.size() - stream.pos();
629 	uint16 roomDataHeader_size = 0;
630 	byte   roomDataHeader_offsetX = 0;
631 	byte   roomDataHeader_offsetY = 0;
632 
633 	// CCB chunk (cel control block)
634 	uint32 ccbFlags = 0;
635 	bool   ccbFlags_compressed = false;
636 	uint16 ccbPPMP0 = 0;
637 	uint16 ccbPPMP1 = 0;
638 	uint32 ccbPRE0 = 0;
639 	uint16 ccbPRE0_height = 0;
640 	byte   ccbPRE0_bitsPerPixel = 0;
641 	uint32 ccbPRE1 = 0;
642 	uint16 ccbPRE1_width = 0;
643 	uint32 ccbWidth = 0;
644 	uint32 ccbHeight = 0;
645 	// cel data
646 	uint32 celDataSize = 0;
647 
648 	while (streamLeft > 0) {
649 		// We expect at least 8 bytes basic header
650 		if (streamLeft < 8)
651 			error("load3DOCelRoomData: expected room data header, not enough bytes");
652 
653 		// 3DO sherlock holmes room data header
654 		stream.skip(4); // Possibly UINT16 width, UINT16 height?!?!
655 		roomDataHeader_size = stream.readUint16BE();
656 		roomDataHeader_offsetX = stream.readByte();
657 		roomDataHeader_offsetY = stream.readByte();
658 		streamLeft -= 8;
659 
660 		// We expect the header size specified in the basic header to be at least a raw CCB
661 		if (roomDataHeader_size < 68)
662 			error("load3DOCelRoomData: header size is too small");
663 		// Check, that enough bytes for CCB are available
664 		if (streamLeft < 68)
665 			error("load3DOCelRoomData: expected raw cel control block, not enough bytes");
666 
667 		// 3DO raw cel control block
668 		ccbFlags   = stream.readUint32BE();
669 		stream.skip(3 * 4); // skip over 3 pointer fields, which are used in memory only by 3DO hardware
670 		stream.skip(8 * 4); // skip over 8 offset fields
671 		ccbPPMP0   = stream.readUint16BE();
672 		ccbPPMP1   = stream.readUint16BE();
673 		ccbPRE0    = stream.readUint32BE();
674 		ccbPRE1    = stream.readUint32BE();
675 		ccbWidth   = stream.readUint32BE();
676 		ccbHeight  = stream.readUint32BE();
677 
678 		if (ccbFlags & 0x200) // bit 9
679 			ccbFlags_compressed = true;
680 
681 		// PRE0 first 3 bits define how many bits per encoded pixel are used
682 		ccbPRE0_bitsPerPixel = imagefile3DO_cel_bitsPerPixelLookupTable[ccbPRE0 & 0x07];
683 		if (!ccbPRE0_bitsPerPixel)
684 			error("load3DOCelRoomData: Invalid CCB PRE0 bits per pixel");
685 
686 		ccbPRE0_height = ((ccbPRE0 >> 6) & 0x03FF) + 1;
687 		ccbPRE1_width  = (ccbPRE1 & 0x03FF) + 1;
688 		assert(ccbPRE0_height == ccbHeight);
689 		assert(ccbPRE1_width == ccbWidth);
690 
691 		if (ccbPRE0_bitsPerPixel != 16) {
692 			// We currently support 16-bits per pixel in here
693 			error("load3DOCelRoomData: bits per pixel < 16?!?!?");
694 		}
695 		// Got the raw CCB
696 		streamLeft -= 68;
697 
698 		// cel data follows
699 		// size field does not include the 8 byte header
700 		celDataSize = roomDataHeader_size - 68;
701 
702 		if (streamLeft < celDataSize)
703 			error("load3DOCelRoomData: expected cel data, not enough bytes");
704 
705 		// read data into memory
706 		byte *celDataPtr = new byte[celDataSize];
707 
708 		stream.read(celDataPtr, celDataSize);
709 		streamLeft -= celDataSize;
710 
711 		// Set up frame
712 		{
713 			ImageFrame imageFrame;
714 
715 			imageFrame._width = ccbWidth;
716 			imageFrame._height = ccbHeight;
717 			imageFrame._paletteBase = 0;
718 			imageFrame._offset.x = roomDataHeader_offsetX;
719 			imageFrame._offset.y = roomDataHeader_offsetY;
720 			imageFrame._rleEncoded = ccbFlags_compressed;
721 			imageFrame._size = 0;
722 
723 			// Decompress/copy this frame
724 			decompress3DOCelFrame(imageFrame, celDataPtr, celDataSize, ccbPRE0_bitsPerPixel, NULL);
725 
726 			delete[] celDataPtr;
727 
728 			push_back(imageFrame);
729 		}
730 	}
731 
732 	// Suppress compiler warning
733 	warning("ccbPPMP0 = %d, ccbPPMP1 = %d", ccbPPMP0, ccbPPMP1);
734 }
735 
736 static uint16 imagefile3DO_cel_bitsMask[17] = {
737 	0,
738 	0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF,
739 	0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF
740 };
741 
742 // gets [bitCount] bits from dataPtr, going from MSB to LSB
celGetBits(const byte * & dataPtr,byte bitCount,byte & dataBitsLeft)743 inline uint16 ImageFile3DO::celGetBits(const byte *&dataPtr, byte bitCount, byte &dataBitsLeft) {
744 	byte   resultBitsLeft = bitCount;
745 	uint16 result = 0;
746 	byte   currentByte = *dataPtr;
747 
748 	// Get bits of current byte
749 	while (resultBitsLeft) {
750 		if (resultBitsLeft < dataBitsLeft) {
751 			// we need less than we have left
752 			result |= (currentByte >> (dataBitsLeft - resultBitsLeft)) & imagefile3DO_cel_bitsMask[resultBitsLeft];
753 			dataBitsLeft -= resultBitsLeft;
754 			resultBitsLeft = 0;
755 
756 		} else {
757 			// we need as much as we have left or more
758 			resultBitsLeft -= dataBitsLeft;
759 			result |= (currentByte & imagefile3DO_cel_bitsMask[dataBitsLeft]) << resultBitsLeft;
760 
761 			// Go to next byte
762 			dataPtr++;
763 			dataBitsLeft = 8;
764 			if (resultBitsLeft) {
765 				currentByte = *dataPtr;
766 			}
767 		}
768 	}
769 	return result;
770 }
771 
772 // decompress/copy 3DO cel data
decompress3DOCelFrame(ImageFrame & frame,const byte * dataPtr,uint32 dataSize,byte bitsPerPixel,ImageFile3DOPixelLookupTable * pixelLookupTable)773 void ImageFile3DO::decompress3DOCelFrame(ImageFrame &frame, const byte *dataPtr, uint32 dataSize, byte bitsPerPixel, ImageFile3DOPixelLookupTable *pixelLookupTable) {
774 	frame._frame.create(frame._width, frame._height, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
775 	uint16 *dest = (uint16 *)frame._frame.getPixels();
776 	Common::fill(dest, dest + frame._width * frame._height, 0);
777 
778 	int frameHeightLeft = frame._height;
779 	int frameWidthLeft = frame._width;
780 	uint16 pixelCount = 0;
781 	uint16 pixel = 0;
782 
783 	const  byte *srcLineStart = dataPtr;
784 	const  byte *srcLineData = dataPtr;
785 	byte   srcLineDataBitsLeft = 0;
786 	uint16 lineDWordSize = 0;
787 	uint16 lineByteSize = 0;
788 
789 	if (bitsPerPixel == 16) {
790 		// Must not use pixel lookup table on 16-bits-per-pixel data
791 		assert(!pixelLookupTable);
792 	}
793 
794 	if (frame._rleEncoded) {
795 		// compressed
796 		byte   compressionType = 0;
797 		byte   compressionPixels = 0;
798 
799 		while (frameHeightLeft > 0) {
800 			frameWidthLeft = frame._width;
801 
802 			if (bitsPerPixel >= 8) {
803 				lineDWordSize = READ_BE_UINT16(srcLineStart);
804 				srcLineData = srcLineStart + 2;
805 			} else {
806 				lineDWordSize = *srcLineStart;
807 				srcLineData = srcLineStart + 1;
808 			}
809 			srcLineDataBitsLeft = 8;
810 
811 			lineDWordSize += 2;
812 			lineByteSize = lineDWordSize * 4; // calculate compressed data size in bytes for current line
813 
814 			// debug
815 			//warning("offset %d: decoding line, size %d, bytesize %d", srcSeeker - src, dwordSize, lineByteSize);
816 
817 			while (frameWidthLeft > 0) {
818 				// get 2 bits -> compressionType
819 				// get 6 bits -> pixel count (0 = 1 pixel)
820 				compressionType = celGetBits(srcLineData, 2, srcLineDataBitsLeft);
821 				// 6 bits == length (0 = 1 pixel)
822 				compressionPixels = celGetBits(srcLineData, 6, srcLineDataBitsLeft) + 1;
823 
824 				if (!compressionType) // end of line
825 					break;
826 
827 				switch(compressionType) {
828 				case 1: // simple copy
829 					for (pixelCount = 0; pixelCount < compressionPixels; pixelCount++) {
830 						pixel = celGetBits(srcLineData, bitsPerPixel, srcLineDataBitsLeft);
831 						if (pixelLookupTable) {
832 							pixel = pixelLookupTable->pixelColor[pixel];
833 						}
834 						*dest++ = convertPixel(pixel);
835 					}
836 					break;
837 				case 2: // transparent
838 					for (pixelCount = 0; pixelCount < compressionPixels; pixelCount++) {
839 						*dest++ = 0;
840 					}
841 					break;
842 				case 3: // duplicate pixels
843 					pixel = celGetBits(srcLineData, bitsPerPixel, srcLineDataBitsLeft);
844 					if (pixelLookupTable) {
845 						pixel = pixelLookupTable->pixelColor[pixel];
846 					}
847 					pixel = convertPixel(pixel);
848 					for (pixelCount = 0; pixelCount < compressionPixels; pixelCount++) {
849 						*dest++ = pixel;
850 					}
851 					break;
852 				default:
853 					break;
854 				}
855 				frameWidthLeft -= compressionPixels;
856 			}
857 
858 			assert(frameWidthLeft >= 0);
859 
860 			if (frameWidthLeft > 0) {
861 				// still pixels left? skip them
862 				dest += frameWidthLeft;
863 			}
864 
865 			frameHeightLeft--;
866 
867 			// Seek to next line start
868 			srcLineStart += lineByteSize;
869 		}
870 	} else {
871 		// uncompressed
872 		srcLineDataBitsLeft = 8;
873 		lineDWordSize = ((frame._width * bitsPerPixel) + 31) >> 5;
874 		lineByteSize = lineDWordSize * 4;
875 		uint32 totalExpectedSize = lineByteSize * frame._height;
876 
877 		assert(totalExpectedSize <= dataSize); // security check
878 
879 		while (frameHeightLeft > 0) {
880 			srcLineData = srcLineStart;
881 			frameWidthLeft = frame._width;
882 
883 			while (frameWidthLeft > 0) {
884 				pixel = celGetBits(srcLineData, bitsPerPixel, srcLineDataBitsLeft);
885 				if (pixelLookupTable) {
886 					pixel = pixelLookupTable->pixelColor[pixel];
887 				}
888 				*dest++ = convertPixel(pixel);
889 
890 				frameWidthLeft--;
891 			}
892 			frameHeightLeft--;
893 
894 			// Seek to next line start
895 			srcLineStart += lineByteSize;
896 		}
897 	}
898 }
899 
900 // Reads Sherlock Holmes 3DO font file
loadFont(Common::SeekableReadStream & stream)901 void ImageFile3DO::loadFont(Common::SeekableReadStream &stream) {
902 	uint32 streamSize = stream.size();
903 	uint32 header_offsetWidthTable = 0;
904 	uint32 header_offsetBitsTable = 0;
905 	uint32 header_fontHeight = 0;
906 	uint32 header_bytesPerLine = 0;
907 	uint32 header_maxChar = 0;
908 	uint32 header_charCount = 0;
909 
910 	byte  *widthTablePtr = NULL;
911 	uint32 bitsTableSize = 0;
912 	byte  *bitsTablePtr = NULL;
913 
914 	stream.skip(2); // Unknown bytes
915 	stream.skip(2); // Unknown bytes (0x000E)
916 	header_offsetWidthTable = stream.readUint32BE();
917 	header_offsetBitsTable = stream.readUint32BE();
918 	stream.skip(4); // Unknown bytes (0x00000004)
919 	header_fontHeight = stream.readUint32BE();
920 	header_bytesPerLine = stream.readUint32BE();
921 	header_maxChar = stream.readUint32BE();
922 
923 	assert(header_maxChar <= 255);
924 	header_charCount = header_maxChar + 1;
925 
926 	// Allocate memory for width table
927 	widthTablePtr = new byte[header_charCount];
928 
929 	stream.seek(header_offsetWidthTable);
930 	stream.read(widthTablePtr, header_charCount);
931 
932 	// Allocate memory for the bits
933 	assert(header_offsetBitsTable < streamSize); // Security check
934 	bitsTableSize = streamSize - header_offsetBitsTable;
935 	bitsTablePtr  = new byte[bitsTableSize];
936 	stream.read(bitsTablePtr, bitsTableSize);
937 
938 	// Now extract all characters
939 	uint16      curChar = 0;
940 	const byte *curBitsLinePtr = bitsTablePtr;
941 	const byte *curBitsPtr = NULL;
942 	byte        curBitsLeft = 0;
943 	uint32      curCharHeightLeft = 0;
944 	uint32      curCharWidthLeft = 0;
945 	byte        curBits = 0;
946 	byte        curBitsReversed = 0;
947 	byte        curPosX = 0;
948 
949 	assert(bitsTableSize >= (header_maxChar * header_fontHeight * header_bytesPerLine)); // Security
950 
951 	// first frame needs to be "!" (33 decimal)
952 	// our font code is subtracting 33 from the actual character code
953 	curBitsLinePtr += (33 * (header_fontHeight * header_bytesPerLine));
954 
955 	for (curChar = 33; curChar < header_charCount; curChar++) {
956 		// create frame
957 		ImageFrame imageFrame;
958 
959 		imageFrame._width = widthTablePtr[curChar];
960 		imageFrame._height = header_fontHeight;
961 		imageFrame._paletteBase = 0;
962 		imageFrame._offset.x = 0;
963 		imageFrame._offset.y = 0;
964 		imageFrame._rleEncoded = false;
965 		imageFrame._size = 0;
966 
967 		// Extract pixels
968 		imageFrame._frame.create(imageFrame._width, imageFrame._height, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
969 		uint16 *dest = (uint16 *)imageFrame._frame.getPixels();
970 		Common::fill(dest, dest + imageFrame._width * imageFrame._height, 0);
971 
972 		curCharHeightLeft = header_fontHeight;
973 		while (curCharHeightLeft) {
974 			curCharWidthLeft = widthTablePtr[curChar];
975 			curBitsPtr  = curBitsLinePtr;
976 			curBitsLeft = 8;
977 			curPosX     = 0;
978 
979 			while (curCharWidthLeft) {
980 				if (!(curPosX & 1)) {
981 					curBits = *curBitsPtr >> 4;
982 				} else {
983 					curBits = *curBitsPtr & 0x0F;
984 					curBitsPtr++;
985 				}
986 				// doing this properly is complicated
987 				// the 3DO has built-in anti-aliasing
988 				// this here at least results in somewhat readable text
989 				// TODO: make it better
990 				if (curBits) {
991 					curBitsReversed = (curBits >> 3) | ((curBits & 0x04) >> 1) | ((curBits & 0x02) << 1) | ((curBits & 0x01) << 3);
992 					curBits = 20 - curBits;
993 				}
994 
995 				byte curIntensity = curBits;
996 				*dest = (curIntensity << 11) | (curIntensity << 6) | curIntensity;
997 				dest++;
998 
999 				curCharWidthLeft--;
1000 				curPosX++;
1001 			}
1002 
1003 			curCharHeightLeft--;
1004 			curBitsLinePtr += header_bytesPerLine;
1005 		}
1006 
1007 		push_back(imageFrame);
1008 	}
1009 
1010 	// Warning below being used to silence unused variable warnings for now
1011 	warning("TODO: Remove %d %d", curBitsLeft, curBitsReversed);
1012 
1013 	delete[] bitsTablePtr;
1014 	delete[] widthTablePtr;
1015 }
1016 
1017 /*----------------------------------------------------------------*/
1018 
StreamingImageFile()1019 StreamingImageFile::StreamingImageFile() {
1020 	_frameNumber = 0;
1021 	_stream = nullptr;
1022 	_flags = 0;
1023 	_scaleVal = 0;
1024 	_zPlacement = 0;
1025 	_compressed = false;
1026 	_active = false;
1027 }
1028 
~StreamingImageFile()1029 StreamingImageFile::~StreamingImageFile() {
1030 	close();
1031 }
1032 
load(Common::SeekableReadStream * stream,bool compressed)1033 void StreamingImageFile::load(Common::SeekableReadStream *stream, bool compressed) {
1034 	_stream = stream;
1035 	_compressed = compressed;
1036 	_frameNumber = -1;
1037 	_active = true;
1038 }
1039 
close()1040 void StreamingImageFile::close() {
1041 	delete _stream;
1042 	_stream = nullptr;
1043 	_frameNumber = -1;
1044 	_active = false;
1045 	_imageFrame._frame.free();
1046 }
1047 
getNextFrame()1048 bool StreamingImageFile::getNextFrame() {
1049 	// Don't proceed if we're already at the end of the stream
1050 	assert(_stream);
1051 	if (_stream->pos() >= _stream->size()) {
1052 		_active = false;
1053 		return false;
1054 	}
1055 
1056 	// Increment frame number
1057 	++_frameNumber;
1058 
1059 	// If necessary, decompress the next frame
1060 	Common::SeekableReadStream *frameStream = _stream;
1061 	if (_compressed) {
1062 		uint32 inSize = _stream->readUint32LE();
1063 		Resources::decompressLZ(*_stream, _buffer, STREAMING_BUFFER_SIZE, inSize);
1064 		frameStream = new Common::MemoryReadStream(_buffer, 11, DisposeAfterUse::NO);
1065 	}
1066 
1067 	// Load the data for the frame
1068 	_imageFrame._width = frameStream->readUint16LE() + 1;
1069 	_imageFrame._height = frameStream->readUint16LE() + 1;
1070 	_imageFrame._paletteBase = frameStream->readByte();
1071 	_imageFrame._rleEncoded = frameStream->readByte() == 1;
1072 	_imageFrame._offset.x = frameStream->readByte();
1073 	_imageFrame._offset.y = frameStream->readByte();
1074 	_imageFrame._size = frameStream->readUint16LE() - 11;
1075 	_imageFrame._rleMarker = frameStream->readByte();
1076 
1077 	// Free the previous frame
1078 	_imageFrame._frame.free();
1079 
1080 	// Decode the frame
1081 	if (_compressed) {
1082 		delete frameStream;
1083 		_imageFrame.decompressFrame(_buffer + 11, true);
1084 	} else {
1085 		byte *data = new byte[_imageFrame._size];
1086 		_stream->read(data, _imageFrame._size);
1087 		_imageFrame.decompressFrame(_buffer + 11, true);
1088 		delete[] data;
1089 	}
1090 
1091 	return true;
1092 }
1093 
1094 } // End of namespace Sherlock
1095