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 "common/scummsys.h"
24 #include "common/archive.h"
25 #include "common/memstream.h"
26 #include "common/textconsole.h"
27 #include "xeen/xeen.h"
28 #include "xeen/screen.h"
29 #include "xeen/sprites.h"
30 
31 #include "graphics/palette.h"
32 
33 namespace Xeen {
34 
35 #define SCENE_CLIP_LEFT 8
36 #define SCENE_CLIP_RIGHT 223
37 
38 int SpriteResource::_clippedBottom;
39 
SpriteResource()40 SpriteResource::SpriteResource() {
41 	_filesize = 0;
42 	_data = nullptr;
43 	_scaledWidth = _scaledHeight = 0;
44 }
45 
SpriteResource(const Common::String & filename)46 SpriteResource::SpriteResource(const Common::String &filename) {
47 	_data = nullptr;
48 	_scaledWidth = _scaledHeight = 0;
49 	load(filename);
50 }
51 
SpriteResource(const Common::String & filename,int ccMode)52 SpriteResource::SpriteResource(const Common::String &filename, int ccMode) {
53 	_data = nullptr;
54 	_scaledWidth = _scaledHeight = 0;
55 	load(filename, ccMode);
56 }
57 
~SpriteResource()58 SpriteResource::~SpriteResource() {
59 	clear();
60 }
61 
operator =(const SpriteResource & src)62 SpriteResource &SpriteResource::operator=(const SpriteResource &src) {
63 	delete[] _data;
64 	_index.clear();
65 
66 	_filesize = src._filesize;
67 	_data = new byte[_filesize];
68 	Common::copy(src._data, src._data + _filesize, _data);
69 
70 	_index.resize(src._index.size());
71 	for (uint i = 0; i < src._index.size(); ++i)
72 		_index[i] = src._index[i];
73 
74 	return *this;
75 }
76 
load(const Common::String & filename)77 void SpriteResource::load(const Common::String &filename) {
78 	_filename = filename;
79 	File f(filename);
80 	load(f);
81 }
82 
load(const Common::String & filename,int ccMode)83 void SpriteResource::load(const Common::String &filename, int ccMode) {
84 	_filename = filename;
85 	File f(filename, ccMode);
86 	load(f);
87 }
88 
load(Common::SeekableReadStream & f)89 void SpriteResource::load(Common::SeekableReadStream &f) {
90 	// Read in a copy of the file
91 	_filesize = f.size();
92 	delete[] _data;
93 	_data = new byte[_filesize];
94 	f.read(_data, _filesize);
95 
96 	// Read in the index
97 	f.seek(0);
98 	int count = f.readUint16LE();
99 	_index.resize(count);
100 
101 	for (int i = 0; i < count; ++i) {
102 		_index[i]._offset1 = f.readUint16LE();
103 		_index[i]._offset2 = f.readUint16LE();
104 	}
105 }
106 
clear()107 void SpriteResource::clear() {
108 	delete[] _data;
109 	_data = nullptr;
110 	_filesize = 0;
111 	_index.clear();
112 }
113 
draw(XSurface & dest,int frame,const Common::Point & destPos,uint flags,int scale)114 void SpriteResource::draw(XSurface &dest, int frame, const Common::Point &destPos,
115 		uint flags, int scale) {
116 	draw(dest, frame, destPos, Common::Rect(0, 0, dest.w, dest.h), flags, scale);
117 }
118 
draw(Window & dest,int frame,const Common::Point & destPos,uint flags,int scale)119 void SpriteResource::draw(Window &dest, int frame, const Common::Point &destPos,
120 		uint flags, int scale) {
121 	draw(dest, frame, destPos, dest.getBounds(), flags, scale);
122 }
123 
draw(int windowIndex,int frame,const Common::Point & destPos,uint flags,int scale)124 void SpriteResource::draw(int windowIndex, int frame, const Common::Point &destPos,
125 		uint flags, int scale) {
126 	Window &win = (*g_vm->_windows)[windowIndex];
127 	draw(win, frame, destPos, flags, scale);
128 }
129 
draw(XSurface & dest,int frame,const Common::Point & destPos,const Common::Rect & bounds,uint flags,int scale)130 void SpriteResource::draw(XSurface &dest, int frame, const Common::Point &destPos,
131 		const Common::Rect &bounds, uint flags, int scale) {
132 	Common::Rect r = bounds;
133 	if (flags & SPRFLAG_BOTTOM_CLIPPED)
134 		r.clip(SCREEN_WIDTH, _clippedBottom);
135 
136 	// Create drawer to handle the rendering
137 	SpriteDrawer *drawer;
138 	switch (flags & SPRFLAG_MODE_MASK) {
139 	case SPRFLAG_DRAWER1:
140 		drawer = new SpriteDrawer1(_data, _filesize, flags & 0x1F);
141 		break;
142 	case SPRFLAG_DRAWER2:
143 		error("TODO: Sprite drawer mode 2");
144 	case SPRFLAG_DRAWER3:
145 		drawer = new SpriteDrawer3(_data, _filesize, flags & 0x1F);
146 		break;
147 	case SPRFLAG_DRAWER5:
148 		drawer = new SpriteDrawer5(_data, _filesize, flags & 0x1F);
149 		break;
150 	case SPRFLAG_DRAWER6:
151 		drawer = new SpriteDrawer6(_data, _filesize, flags & 0x1F);
152 		break;
153 	default:
154 		drawer = new SpriteDrawer(_data, _filesize);
155 		break;
156 	}
157 
158 	// Sprites can consist of separate background & foreground
159 	drawer->draw(dest, _index[frame]._offset1, destPos, r, flags, scale);
160 	if (_index[frame]._offset2)
161 		drawer->draw(dest, _index[frame]._offset2, destPos, r, flags, scale);
162 
163 	delete drawer;
164 }
165 
draw(XSurface & dest,int frame)166 void SpriteResource::draw(XSurface &dest, int frame) {
167 	draw(dest, frame, Common::Point());
168 }
169 
draw(int windowIndex,int frame)170 void SpriteResource::draw(int windowIndex, int frame) {
171 	draw((*g_vm->_windows)[windowIndex], frame, Common::Point());
172 }
173 
getFrameSize(int frame) const174 Common::Point SpriteResource::getFrameSize(int frame) const {
175 	Common::MemoryReadStream f(_data, _filesize);
176 	Common::Point frameSize;
177 
178 	for (int idx = 0; idx < (_index[frame]._offset2 ? 2 : 1); ++idx) {
179 		f.seek((idx == 0) ? _index[frame]._offset1 : _index[frame]._offset2);
180 		int xOffset = f.readUint16LE();
181 		int width = f.readUint16LE();
182 		int yOffset = f.readUint16LE();
183 		int height = f.readUint16LE();
184 
185 		frameSize.x = MAX((int)frameSize.x, xOffset + width);
186 		frameSize.y = MAX((int)frameSize.y, yOffset + height);
187 	}
188 
189 	return frameSize;
190 }
191 
192 /*------------------------------------------------------------------------*/
193 
draw(XSurface & dest,uint16 offset,const Common::Point & pt,const Common::Rect & clipRect,uint flags,int scale)194 void SpriteDrawer::draw(XSurface &dest, uint16 offset, const Common::Point &pt,
195 		const Common::Rect &clipRect, uint flags, int scale) {
196 	static const uint SCALE_TABLE[] = {
197 		0xFFFF, 0xFFEF, 0xEFEF, 0xEFEE, 0xEEEE, 0xEEAE, 0xAEAE, 0xAEAA,
198 		0xAAAA, 0xAA8A, 0x8A8A, 0x8A88, 0x8888, 0x8880, 0x8080, 0x8000
199 	};
200 	static const int PATTERN_STEPS[] = { 0, 1, 1, 1, 2, 2, 3, 3, 0, -1, -1, -1, -2, -2, -3, -3 };
201 
202 	assert((scale & SCALE_MASK) < 16);
203 	uint16 scaleMask = SCALE_TABLE[scale & SCALE_MASK];
204 	uint16 scaleMaskX = scaleMask, scaleMaskY = scaleMask;
205 	bool flipped = (flags & SPRFLAG_HORIZ_FLIPPED) != 0;
206 	int xInc = flipped ? -1 : 1;
207 	bool enlarge = (scale & SCALE_ENLARGE) != 0;
208 
209 	// Get cell header
210 	Common::MemoryReadStream f(_data, _filesize);
211 	f.seek(offset);
212 	int xOffset = f.readUint16LE();
213 	int width = f.readUint16LE();
214 	int yOffset = f.readUint16LE();
215 	int height = f.readUint16LE();
216 
217 	// Figure out drawing x, y
218 	Common::Point destPos;
219 	destPos.x = pt.x + getScaledVal(xOffset, scaleMaskX);
220 	destPos.x += (width - getScaledVal(width, scaleMaskX)) / 2;
221 
222 	destPos.y = pt.y + getScaledVal(yOffset, scaleMaskY);
223 
224 	// If the flags allow the dest surface to be resized, ensure dest surface is big enough
225 	Common::Rect bounds = clipRect;
226 	if (flags & SPRFLAG_RESIZE) {
227 		if (dest.w < (xOffset + width) || dest.h < (yOffset + height))
228 			dest.create(xOffset + width, yOffset + height);
229 		bounds = Common::Rect(0, 0, dest.w, dest.h);
230 	}
231 	if (flags & SPRFLAG_SCENE_CLIPPED) {
232 		bounds.clip(Common::Rect(8, 8, 223, 141));
233 	}
234 
235 	uint16 scaleMaskXCopy = scaleMaskX;
236 	Common::Rect drawBounds;
237 	drawBounds.left = SCREEN_WIDTH;
238 	drawBounds.top = SCREEN_HEIGHT;
239 	drawBounds.right = drawBounds.bottom = 0;
240 
241 	// Main loop
242 	for (int yCtr = height; yCtr > 0; --yCtr) {
243 		// The number of bytes in this scan line
244 		int lineLength = f.readByte();
245 
246 		if (lineLength == 0) {
247 			// Skip the specified number of scan lines
248 			int numLines = f.readByte();
249 			destPos.y += getScaledVal(numLines + 1, scaleMaskY);
250 			yCtr -= numLines;
251 			continue;
252 		}
253 
254 		// Roll the scale mask
255 		uint bit = (scaleMaskY >> 15) & 1;
256 		scaleMaskY = ((scaleMaskY & 0x7fff) << 1) + bit;
257 
258 		if (!bit) {
259 			// Not a line to be drawn due to scaling down
260 			f.skip(lineLength);
261 		} else if (destPos.y < bounds.top || destPos.y >= bounds.bottom) {
262 			// Skip over the bytes of the line
263 			f.skip(lineLength);
264 			destPos.y++;
265 		} else {
266 			scaleMaskX = scaleMaskXCopy;
267 			xOffset = f.readByte();
268 
269 			// Initialize the array to hold the temporary data for the line. We do this to make it simpler
270 			// to handle both deciding which pixels to draw in a scaled image, as well as when images
271 			// have been horizontally flipped. Note that we allocate an extra line for before and after our
272 			// work line, just in case the sprite is screwed up and overruns the line
273 			int tempLine[SCREEN_WIDTH * 3];
274 			Common::fill(&tempLine[SCREEN_WIDTH], &tempLine[SCREEN_WIDTH * 3], -1);
275 			int *lineP = flipped ? &tempLine[SCREEN_WIDTH + width - 1 - xOffset] : &tempLine[SCREEN_WIDTH + xOffset];
276 
277 			// Build up the line
278 			int byteCount, opr1, opr2;
279 			int32 pos;
280 			for (byteCount = 1; byteCount < lineLength; ) {
281 				// The next byte is an opcode that determines what operators are to follow and how to interpret them.
282 				int opcode = f.readByte(); ++byteCount;
283 
284 				// Decode the opcode
285 				int len = opcode & 0x1F;
286 				int cmd = (opcode & 0xE0) >> 5;
287 
288 				switch (cmd) {
289 				case 0:   // The following len + 1 bytes are stored as indexes into the color table.
290 				case 1:   // The following len + 33 bytes are stored as indexes into the color table.
291 					for (int i = 0; i < opcode + 1; ++i, ++byteCount) {
292 						byte b = f.readByte();
293 						*lineP = b;
294 						lineP += xInc;
295 					}
296 					break;
297 
298 				case 2:   // The following byte is an index into the color table, draw it len + 3 times.
299 					opr1 = f.readByte(); ++byteCount;
300 					for (int i = 0; i < len + 3; ++i) {
301 						*lineP = opr1;
302 						lineP += xInc;
303 					}
304 					break;
305 
306 				case 3:   // Stream copy command.
307 					opr1 = f.readUint16LE(); byteCount += 2;
308 					pos = f.pos();
309 					f.seek(-opr1, SEEK_CUR);
310 
311 					for (int i = 0; i < len + 4; ++i) {
312 						*lineP = f.readByte();
313 						lineP += xInc;
314 					}
315 
316 					f.seek(pos, SEEK_SET);
317 					break;
318 
319 				case 4:   // The following two bytes are indexes into the color table, draw the pair len + 2 times.
320 					opr1 = f.readByte(); ++byteCount;
321 					opr2 = f.readByte(); ++byteCount;
322 					for (int i = 0; i < len + 2; ++i) {
323 						*lineP = opr1;
324 						lineP += xInc;
325 						*lineP = opr2;
326 						lineP += xInc;
327 					}
328 					break;
329 
330 				case 5:   // Skip len + 1 pixels
331 					lineP += (len + 1) * xInc;
332 					break;
333 
334 				case 6:   // Pattern command.
335 				case 7:
336 					// The pattern command has a different opcode format
337 					len = opcode & 0x07;
338 					cmd = (opcode >> 2) & 0x0E;
339 
340 					opr1 = f.readByte(); ++byteCount;
341 					for (int i = 0; i < len + 3; ++i) {
342 						*lineP = opr1;
343 						lineP += xInc;
344 						opr1 += PATTERN_STEPS[cmd + (i % 2)];
345 					}
346 					break;
347 
348 				default:
349 					break;
350 				}
351 			}
352 			assert(byteCount == lineLength);
353 
354 			drawBounds.top = MIN(drawBounds.top, destPos.y);
355 			drawBounds.bottom = MAX((int)drawBounds.bottom, destPos.y + 1);
356 
357 			// Handle drawing out the line
358 			byte *destP = (byte *)dest.getBasePtr(destPos.x, destPos.y);
359 			int16 xp = destPos.x;
360 			lineP = &tempLine[SCREEN_WIDTH];
361 
362 			for (int xCtr = 0; xCtr < width; ++xCtr, ++lineP) {
363 				bit = (scaleMaskX >> 15) & 1;
364 				scaleMaskX = ((scaleMaskX & 0x7fff) << 1) + bit;
365 
366 				if (bit) {
367 					// Check whether there's a pixel to write, and we're within the allowable bounds. Note that for
368 					// the SPRFLAG_SCENE_CLIPPED or when enlarging, we also have an extra horizontal bounds check
369 					if (*lineP != -1 && xp >= bounds.left && xp < bounds.right) {
370 						drawBounds.left = MIN(drawBounds.left, xp);
371 						drawBounds.right = MAX((int)drawBounds.right, xp + 1);
372 						drawPixel(destP, (byte)*lineP);
373 						if (enlarge) {
374 							drawPixel(destP + SCREEN_WIDTH, (byte)*lineP);
375 							drawPixel(destP + 1, (byte)*lineP);
376 							drawPixel(destP + 1 + SCREEN_WIDTH, (byte)*lineP);
377 						}
378 					}
379 
380 					++xp;
381 					++destP;
382 					if (enlarge) {
383 						++destP;
384 						++xp;
385 					}
386 				}
387 			}
388 
389 			++destPos.y;
390 			if (enlarge)
391 				++destPos.y;
392 		}
393 	}
394 
395 	if (drawBounds.isValidRect()) {
396 		drawBounds.clip(Common::Rect(0, 0, dest.w, dest.h));
397 		if (!drawBounds.isEmpty())
398 			dest.addDirtyRect(drawBounds);
399 	}
400 }
401 
getScaledVal(int xy,uint16 & scaleMask)402 uint SpriteDrawer::getScaledVal(int xy, uint16 &scaleMask) {
403 	if (!xy)
404 		return 0;
405 
406 	uint result = 0;
407 	for (int idx = 0; idx < xy; ++idx) {
408 		uint bit = (scaleMask >> 15) & 1;
409 		scaleMask = ((scaleMask & 0x7fff) << 1) + bit;
410 		result += bit;
411 	}
412 
413 	return result;
414 }
415 
drawPixel(byte * dest,byte pixel)416 void SpriteDrawer::drawPixel(byte *dest, byte pixel) {
417 	*dest = pixel;
418 }
419 
420 /*------------------------------------------------------------------------*/
421 
422 const byte DRAWER1_OFFSET[24] = {
423 	0x30, 0xC0, 0xB0, 0x10, 0x41, 0x20, 0x40, 0x21, 0x48, 0x46, 0x43, 0x40,
424 	0xD0, 0xD3, 0xD6, 0xD8, 0x01, 0x04, 0x07, 0x0A, 0xEA, 0xEE, 0xF2, 0xF6
425 };
426 
427 const byte DRAWER1_MASK[24] = {
428 	0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x0F, 0x07, 0x07, 0x07, 0x07,
429 	0x07, 0x07, 0x07, 0x07, 0x0F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x07, 0x07
430 };
431 
SpriteDrawer1(byte * data,size_t filesize,int index)432 SpriteDrawer1::SpriteDrawer1(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) {
433 	_offset = DRAWER1_OFFSET[index];
434 	_mask = DRAWER1_MASK[index];
435 }
436 
drawPixel(byte * dest,byte pixel)437 void SpriteDrawer1::drawPixel(byte *dest, byte pixel) {
438 	*dest = (pixel & _mask) + _offset;
439 }
440 
441 /*------------------------------------------------------------------------*/
442 
443 const uint16 DRAWER3_MASK[4] = { 1, 3, 7, 15 };
444 const uint16 DRAWER3_OFFSET[4] = { 1, 2, 4, 8 };
445 
SpriteDrawer3(byte * data,size_t filesize,int index)446 SpriteDrawer3::SpriteDrawer3(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) {
447 	_offset = DRAWER3_OFFSET[index];
448 	_mask = DRAWER3_MASK[index];
449 
450 	g_system->getPaletteManager()->grabPalette(_palette, 0, PALETTE_COUNT);
451 	_hasPalette = false;
452 	for (byte *pal = _palette; pal < _palette + PALETTE_SIZE && !_hasPalette; ++pal)
453 		_hasPalette = *pal != 0;
454 }
455 
drawPixel(byte * dest,byte pixel)456 void SpriteDrawer3::drawPixel(byte *dest, byte pixel) {
457 	// WORKAROUND: This is slightly different then the original:
458 	// 1) The original has bunches of black pixels appearing. This does index increments to avoid such pixels
459 	// 2) It also prevents any pixels being drawn in the single initial frame until the palette is set
460 	if (_hasPalette) {
461 		byte level = (pixel & _mask) - _offset + (*dest & 0xf);
462 
463 		if (level >= 0x80) {
464 			*dest &= 0xf0;
465 		} else if (level <= 0xf) {
466 			*dest = (*dest & 0xf0) | level;
467 		} else {
468 			*dest |= 0xf;
469 		}
470 
471 		//
472 		while (*dest < 0xff && !_palette[*dest * 3] && !_palette[*dest * 3 + 1] && !_palette[*dest * 3 + 2])
473 			++*dest;
474 	}
475 }
476 
477 /*------------------------------------------------------------------------*/
478 
479 const byte DRAWER4_THRESHOLD[4] = { 4, 7, 10, 13 };
480 
SpriteDrawer4(byte * data,size_t filesize,int index)481 SpriteDrawer4::SpriteDrawer4(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) {
482 	_threshold = DRAWER4_THRESHOLD[index];
483 }
484 
drawPixel(byte * dest,byte pixel)485 void SpriteDrawer4::drawPixel(byte *dest, byte pixel) {
486 	if ((pixel & 0xf) >= _threshold)
487 		*dest = pixel;
488 }
489 
490 /*------------------------------------------------------------------------*/
491 
492 const uint16 DRAWER5_THRESHOLD[4] = { 0x3333, 0x6666, 0x999A, 0xCCCD };
493 
SpriteDrawer5(byte * data,size_t filesize,int index)494 SpriteDrawer5::SpriteDrawer5(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) {
495 	_threshold = DRAWER5_THRESHOLD[index];
496 	_random1 = g_vm->getRandomNumber(0xffff);
497 	_random2 = g_vm->getRandomNumber(0xffff);
498 }
499 
drawPixel(byte * dest,byte pixel)500 void SpriteDrawer5::drawPixel(byte *dest, byte pixel) {
501 	bool flag = (_random1 & 0x8000) != 0;
502 	_random1 = (int)((uint16)_random1 << 1) - _random2 - (flag ? 1 : 0);
503 
504 	rcr(_random2, flag);
505 	rcr(_random2, flag);
506 	_random2 ^= _random1;
507 
508 	if (_random2 > _threshold)
509 		*dest = pixel;
510 }
511 
rcr(uint16 & val,bool & cf)512 void SpriteDrawer5::rcr(uint16 &val, bool &cf) {
513 	bool newCf = (val & 1);
514 	val = (val >> 1) | (cf ? 0x8000 : 0);
515 	cf = newCf;
516 }
517 
518 /*------------------------------------------------------------------------*/
519 
520 const byte DRAWER6_MASK[16] = { 1, 2, 4, 8, 1, 3, 7, 15, 8, 12, 14, 15, 1, 2, 1, 2 };
521 
SpriteDrawer6(byte * data,size_t filesize,int index)522 SpriteDrawer6::SpriteDrawer6(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) {
523 	_mask = DRAWER6_MASK[index];
524 }
525 
drawPixel(byte * dest,byte pixel)526 void SpriteDrawer6::drawPixel(byte *dest, byte pixel) {
527 	*dest = pixel ^ _mask;
528 }
529 
530 } // End of namespace Xeen
531