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 "image/cel_3do.h"
24 
25 #include "common/file.h"
26 #include "common/stream.h"
27 #include "common/substream.h"
28 #include "common/textconsole.h"
29 #include "graphics/pixelformat.h"
30 #include "graphics/surface.h"
31 #include "image/bmp.h"
32 
33 namespace Image {
34 
35 enum CCBFlags {
36 	kCCBPacked = 1 << 9,
37 	kCCBNoPre0 = 1 << 22
38 };
39 
Cel3DODecoder()40 Cel3DODecoder::Cel3DODecoder() {
41 	_surface = 0;
42 	_palette = 0;
43 	_paletteColorCount = 0;
44 }
45 
~Cel3DODecoder()46 Cel3DODecoder::~Cel3DODecoder() {
47 	destroy();
48 }
49 
destroy()50 void Cel3DODecoder::destroy() {
51 	_surface = 0;
52 
53 	delete[] _palette;
54 	_palette = 0;
55 
56 	_paletteColorCount = 0;
57 }
58 
loadStream(Common::SeekableReadStream & stream)59 bool Cel3DODecoder::loadStream(Common::SeekableReadStream &stream) {
60 	destroy();
61 
62 	// This is not a full implementaion of CEL support,
63 	// just what is currently needed for following games:
64 	// * Plumbers don't wear ties
65 	// TODO: support paletted
66 
67 	if (stream.readUint32BE() != MKTAG('C', 'C', 'B', ' '))
68 		return false;
69 
70 	if (stream.readUint32BE() != 0x50) // block size
71 		return false;
72 
73 	if (stream.readUint32BE() != 0) // CCB version
74 		return false;
75 
76 	uint32 flags = stream.readUint32BE();
77 
78 	stream.skip(0x30);
79 	uint32 pre0 = stream.readUint32BE();
80 	/* pre1 = */ stream.readUint32BE();
81 	uint32 width = stream.readUint32BE();
82 	uint32 height = stream.readUint32BE();
83 
84 	while (!stream.eos()) {
85 		if (stream.readUint32BE() == MKTAG('P', 'D', 'A', 'T'))
86 			break;
87 		stream.skip(stream.readUint32BE() - 8);
88 	}
89 
90 	if (stream.eos())
91 		return false;
92 
93 	if (width == 0 || height == 0)
94 		return false;
95 
96 	/* pdat_size = */ stream.readUint32BE();
97 
98 	Graphics::PixelFormat format(2, 5, 5, 5, 1, 10, 5, 0, 15);
99 	Graphics::Surface *surface = new Graphics::Surface();
100 	surface->create(width, height, format);
101 
102 	uint16 *dst = (uint16 *)surface->getBasePtr(0, 0);
103 
104 	if(!(flags & kCCBNoPre0)) {
105 		pre0 = stream.readUint32BE();
106 		if(!(flags & kCCBPacked)) {
107 			/* pre1 = */ stream.readUint32BE();
108 		}
109 	}
110 
111 	// Only RGB555 is supported
112 	if ((pre0 & 0x17) != 0x16)
113 		return false;
114 
115 	if(!(flags & kCCBPacked)) {
116 		// RAW
117 		// TODO: this can be optimized, especially on BE systems, but do we care?
118 		for (uint xy = 0; xy < width * height; xy++)
119 			*dst++ = stream.readUint16BE();
120 	} else {
121 		// RLE
122 		for (uint y = 0; y < height; y++) {
123 			int linecomprem = (stream.readUint16BE() + 2) * 4 - 2;
124 			int linerem = width;
125 			bool stopLine = false;
126 			while (linerem > 0 && linecomprem > 0 && !stopLine) {
127 				byte lead = stream.readByte();
128 				linecomprem--;
129 				switch (lead >> 6) {
130 				case 0: // end of the line
131 					stopLine = true;
132 					break;
133 				case 1: // copy
134 					for (uint i = 0; i <= (lead & 0x3f) && linerem > 0 && linecomprem > 0;
135 					     i++, linerem--, linecomprem -= 2)
136 						*dst++ = stream.readUint16BE();
137 					break;
138 				case 2: // black
139 					for (uint i = 0; i <= (lead & 0x3f) && linerem > 0; i++, linerem--)
140 						*dst++ = 0;
141 					break;
142 				case 3: { // RLE multiply
143 					uint16 rleval = stream.readUint16BE();
144 					linecomprem -= 2;
145 					for (uint i = 0; i <= (lead & 0x3f) && linerem > 0; i++, linerem--)
146 						*dst++ = rleval;
147 					break;
148 				}
149 				}
150 			}
151 			if (linecomprem > 0)
152 				stream.skip(linecomprem);
153 			if (linerem > 0) {
154 				memset(dst, 0, 2 * linerem);
155 				dst += linerem;
156 			}
157 		}
158 	}
159 
160 	_surface = surface;
161 
162 	return true;
163 }
164 
165 } // End of namespace Image
166