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