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