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 "groovie/vdx.h"
24 #include "groovie/graphics.h"
25 #include "groovie/groovie.h"
26 #include "groovie/lzss.h"
27 
28 #include "common/debug.h"
29 #include "common/debug-channels.h"
30 #include "common/textconsole.h"
31 #include "audio/audiostream.h"
32 #include "audio/mixer.h"
33 #include "audio/decoders/raw.h"
34 #include "graphics/palette.h"
35 
36 #define TILE_SIZE 4			// Size of each tile on the image: only ever seen 4 so far
37 #define VDX_IDENT 0x9267	// 37479
38 
39 namespace Groovie {
40 
VDXPlayer(GroovieEngine * vm)41 VDXPlayer::VDXPlayer(GroovieEngine *vm) :
42 	VideoPlayer(vm), _origX(0), _origY(0), _flagOnePrev(false),
43 	_fg(&_vm->_graphicsMan->_foreground), _bg(&_vm->_graphicsMan->_background) {
44 }
45 
~VDXPlayer()46 VDXPlayer::~VDXPlayer() {
47 	//delete _audioStream;
48 }
49 
resetFlags()50 void VDXPlayer::resetFlags() {
51 	_flagOnePrev = false;
52 }
53 
setOrigin(int16 x,int16 y)54 void VDXPlayer::setOrigin(int16 x, int16 y) {
55 	_origX = x;
56 	_origY = y;
57 }
58 
loadInternal()59 uint16 VDXPlayer::loadInternal() {
60 	if (DebugMan.isDebugChannelEnabled(kDebugVideo)) {
61 		int8 i;
62 		debugN(1, "Groovie::VDX: New VDX: bitflags are ");
63 		for (i = 15; i >= 0; i--) {
64 			debugN(1, "%d", _flags & (1 << i)? 1 : 0);
65 			if (i % 4 == 0) {
66 				debugN(1, " ");
67 			}
68 		}
69 		debug(1, " <- 0 ");
70 	}
71 	// Flags:
72 	// - 1 Puzzle piece? Skip palette, don't redraw full screen, draw still to b/ack buffer
73 	// - 2 Transparent color is 0xFF
74 	// - 5 Skip still chunks
75 	// - 7
76 	// - 8 Just show the first frame
77 	// - 9 Start a palette fade in
78 	_flagZero =		((_flags & (1 << 0)) != 0);
79 	_flagOne =		((_flags & (1 << 1)) != 0);
80 	_flag2Byte =	(_flags & (1 << 2)) ? 0xFF : 0x00;
81 	_flagThree =	((_flags & (1 << 3)) != 0);
82 	_flagFour =		((_flags & (1 << 4)) != 0);
83 	_flagFive =		((_flags & (1 << 5)) != 0);
84 	_flagSix =		((_flags & (1 << 6)) != 0);
85 	_flagSeven =	((_flags & (1 << 7)) != 0);
86 	_flagEight =	((_flags & (1 << 8)) != 0);
87 	_flagNine =		((_flags & (1 << 9)) != 0);
88 
89 	// Enable highspeed if we're not obeying fps, and not marked as special
90 	// This will be disabled in chunk audio if we're actually an audio vdx
91 	if (_vm->_modeSpeed == kGroovieSpeedFast && ((_flags & (1 << 15)) == 0))
92 		setOverrideSpeed(true);
93 
94 	if (_flagOnePrev && !_flagOne && !_flagEight) {
95 		_flagSeven = true;
96 	}
97 
98 	// Save _flagOne for the next video
99 	_flagOnePrev = _flagOne;
100 
101 	//_flagTransparent =	_flagOne;
102 	_flagFirstFrame =	_flagEight;
103 	//_flagSkipPalette =	_flagSeven;
104 	_flagSkipPalette =	false;
105 	//_flagSkipStill =	_flagFive || _flagSeven;
106 	//_flagUpdateStill =	_flagNine || _flagSix;
107 
108 	// Begin reading the file
109 	debugC(1, kDebugVideo, "Groovie::VDX: Playing video");
110 
111 	if (_file->readUint16LE() != VDX_IDENT) {
112 		error("Groovie::VDX: This does not appear to be a 7th guest VDX file");
113 		return 0;
114 	} else {
115 		debugC(5, kDebugVideo, "Groovie::VDX: VDX file identified correctly");
116 	}
117 
118 	uint16 tmp;
119 
120 	// Skip unknown data: 6 bytes, ref Martine
121 	tmp = _file->readUint16LE();
122 	debugC(2, kDebugVideo | kDebugUnknown, "Groovie::VDX: Martine1 = 0x%04X", tmp);
123 	tmp = _file->readUint16LE();
124 	debugC(2, kDebugVideo | kDebugUnknown, "Groovie::VDX: Martine2 = 0x%04X", tmp);
125 	tmp = _file->readUint16LE();
126 	debugC(2, kDebugVideo | kDebugUnknown, "Groovie::VDX: Martine3 (FPS?) = %d", tmp);
127 
128 	return tmp;
129 }
130 
playFrameInternal()131 bool VDXPlayer::playFrameInternal() {
132 	byte currRes = 0x80;
133 	Common::ReadStream *vdxData = 0;
134 	while (currRes == 0x80) {
135 		currRes = _file->readByte();
136 
137 		// Skip unknown data: 1 byte, ref Edward
138 		byte tmp = _file->readByte();
139 
140 		uint32 compSize = _file->readUint32LE();
141 		uint8 lengthmask = _file->readByte();
142 		uint8 lengthbits = _file->readByte();
143 
144 		if (_file->eos())
145 			break;
146 
147 		debugC(5, kDebugVideo | kDebugUnknown, "Groovie::VDX: Edward = 0x%04X", tmp);
148 
149 		// Read the chunk data and decompress if needed
150 		if (compSize)
151 			vdxData = _file->readStream(compSize);
152 
153 		if (lengthmask && lengthbits) {
154 			Common::ReadStream *decompData = new LzssReadStream(vdxData, lengthmask, lengthbits);
155 			delete vdxData;
156 			vdxData = decompData;
157 		}
158 
159 		// Use the current chunk
160 		switch (currRes) {
161 			case 0x00:
162 				debugC(6, kDebugVideo, "Groovie::VDX: Replay frame");
163 				break;
164 			case 0x20:
165 				debugC(5, kDebugVideo, "Groovie::VDX: Still frame");
166 				getStill(vdxData);
167 				break;
168 			case 0x25:
169 				debugC(5, kDebugVideo, "Groovie::VDX: Animation frame");
170 				getDelta(vdxData);
171 				break;
172 			case 0x80:
173 				debugC(5, kDebugVideo, "Groovie::VDX: Sound resource");
174 				chunkSound(vdxData);
175 				break;
176 			default:
177 				error("Groovie::VDX: Invalid resource type: %d", currRes);
178 		}
179 		delete vdxData;
180 		vdxData = 0;
181 	}
182 
183 	// Wait until the current frame can be shown
184 
185 	if (!DebugMan.isDebugChannelEnabled(kDebugFast)) {
186 		waitFrame();
187 	}
188 	// TODO: Move it to a better place
189 	// Update the screen
190 	if (currRes == 0x25) {
191 		//if (_flagSeven) {
192 			//_vm->_graphicsMan->mergeFgAndBg();
193 		//}
194 		_vm->_graphicsMan->updateScreen(_bg);
195 	}
196 
197 	// Report the end of the video if we reached the end of the file or if we
198 	// just wanted to play one frame.
199 	if (_file->eos() || _flagFirstFrame) {
200 		_origX = _origY = 0;
201 		return 1;
202 	} else {
203 		return 0;
204 	}
205 }
206 
207 static const uint16 vdxBlockMapLookup[] = {
208 0xc800, 0xec80, 0xfec8, 0xffec, 0xfffe, 0x3100, 0x7310, 0xf731, 0xff73, 0xfff7, 0x6c80, 0x36c8, 0x136c, 0x6310, 0xc631, 0x8c63,
209 0xf000, 0xff00, 0xfff0, 0x1111, 0x3333, 0x7777, 0x6666, 0xcccc, 0x0ff0, 0x00ff, 0xffcc, 0x0076, 0xff33, 0x0ee6, 0xccff, 0x6770,
210 0x33ff, 0x6ee0, 0x4800, 0x2480, 0x1248, 0x0024, 0x0012, 0x2100, 0x4210, 0x8421, 0x0042, 0x0084, 0xf888, 0x0044, 0x0032, 0x111f,
211 0x22e0, 0x4c00, 0x888f, 0x4470, 0x2300, 0xf111, 0x0e22, 0x00c4, 0xf33f, 0xfccf, 0xff99, 0x99ff, 0x4444, 0x2222, 0xccee, 0x7733,
212 0x00f8, 0x00f1, 0x00bb, 0x0cdd, 0x0f0f, 0x0f88, 0x13f1, 0x19b3, 0x1f80, 0x226f, 0x27ec, 0x3077, 0x3267, 0x37e4, 0x38e3, 0x3f90,
213 0x44cf, 0x4cd9, 0x4c99, 0x5555, 0x603f, 0x6077, 0x6237, 0x64c9, 0x64cd, 0x6cd9, 0x70ef, 0x0f00, 0x00f0, 0x0000, 0x4444, 0x2222
214 };
215 
getDelta(Common::ReadStream * in)216 void VDXPlayer::getDelta(Common::ReadStream *in) {
217 	uint16 k, l;
218 
219 	// Get the size of the local palette
220 	uint16 palSize = in->readUint16LE();
221 
222 	// Load the palette if it isn't empty
223 	if (palSize) {
224 		uint16 palBitField[16];
225 
226 		// Load the bit field
227 		for (l = 0; l < 16; l++) {
228 			palBitField[l] = in->readUint16LE();
229 		}
230 
231 		// Load the actual palette
232 		for (l = 0; l < 16; l++) {
233 			int flag = 1 << 15;
234 			for (uint16 j = 0; j < 16; j++) {
235 				int palIndex = (l * 16) + j;
236 
237 				if (flag & palBitField[l]) {
238 					for (k = 0; k < 3; k++) {
239 						_palBuf[(palIndex * 3) + k] = in->readByte();
240 					}
241 				}
242 				flag = flag >> 1;
243 			}
244 		}
245 
246 		// Apply the palette
247 		if (!_flagSeven) {
248 		//if (!_flagSix && !_flagSeven) {
249 			setPalette(_palBuf);
250 		}
251 	}
252 
253 	uint8 currOpCode = in->readByte();
254 	uint8 param1, param2, param3;
255 
256 	uint16 currentLine = 0;
257 	uint32 offset = 0;
258 	while (!in->eos()) {
259 		byte colors[16];
260 		if (currOpCode < 0x60) {
261 			param1 = in->readByte();
262 			param2 = in->readByte();
263 			expandColorMap(colors, vdxBlockMapLookup[currOpCode], param1, param2);
264 			decodeBlockDelta(offset, colors, 640);
265 			offset += TILE_SIZE;
266 		} else if (currOpCode > 0x7f) {
267 			param1 = in->readByte();
268 			param2 = in->readByte();
269 			param3 = in->readByte();
270 			expandColorMap(colors, (param1 << 8) + currOpCode, param2, param3);
271 			decodeBlockDelta(offset, colors, 640);
272 			offset += TILE_SIZE;
273 		} else switch (currOpCode) {
274 			case 0x60: /* Fill tile with the 16 colors given as parameters */
275 				for (l = 0; l < 16; l++) {
276 					colors[l] = in->readByte();
277 				}
278 				decodeBlockDelta(offset, colors, 640);
279 				offset += TILE_SIZE;
280 				break;
281 			case 0x61: /* Skip to the end of this line, next block is start of next */
282 				/* Note this is used at the end of EVERY line */
283 				currentLine++;
284 				offset = currentLine * TILE_SIZE * 640;
285 				break;
286 			case 0x62:
287 			case 0x63:
288 			case 0x64:
289 			case 0x65:
290 			case 0x66:
291 			case 0x67:
292 			case 0x68:
293 			case 0x69:
294 			case 0x6a:
295 			case 0x6b: /* Skip next param1 blocks (within line) */
296 				offset += (currOpCode - 0x62) * TILE_SIZE;
297 				break;
298 			case 0x6c:
299 			case 0x6d:
300 			case 0x6e:
301 			case 0x6f:
302 			case 0x70:
303 			case 0x71:
304 			case 0x72:
305 			case 0x73:
306 			case 0x74:
307 			case 0x75: /* Next param1 blocks are filled with color param2 */
308 				param1 = currOpCode - 0x6b;
309 				param2 = in->readByte();
310 				for (l = 0; l < 16; l++) {
311 					colors[l] = param2;
312 				}
313 				for (k = 0; k < param1; k++) {
314 					decodeBlockDelta(offset, colors, 640);
315 					offset += TILE_SIZE;
316 				}
317 				break;
318 			case 0x76:
319 			case 0x77:
320 			case 0x78:
321 			case 0x79:
322 			case 0x7a:
323 			case 0x7b:
324 			case 0x7c:
325 			case 0x7d:
326 			case 0x7e:
327 			case 0x7f: /* Next bytes contain colors to fill the next param1 blocks in the current line*/
328 				param1 = currOpCode - 0x75;
329 				for (k = 0; k < param1; k++) {
330 					param2 = in->readByte();
331 					for (l = 0; l < 16; l++) {
332 						colors[l] = param2;
333 					}
334 					decodeBlockDelta(offset, colors, 640);
335 					offset += TILE_SIZE;
336 				}
337 				break;
338 			default:
339 				error("Groovie::VDX: Broken somehow");
340 		}
341 		currOpCode = in->readByte();
342 	}
343 }
344 
getStill(Common::ReadStream * in)345 void VDXPlayer::getStill(Common::ReadStream *in) {
346 	uint16 numXTiles = in->readUint16LE();
347 	debugC(5, kDebugVideo, "Groovie::VDX: numXTiles=%d", numXTiles);
348 	uint16 numYTiles = in->readUint16LE();
349 	debugC(5, kDebugVideo, "Groovie::VDX: numYTiles=%d", numYTiles);
350 
351 	// It's skipped in the original:
352 	uint16 colorDepth = in->readUint16LE();
353 	debugC(5, kDebugVideo, "Groovie::VDX: colorDepth=%d", colorDepth);
354 
355 	uint16 imageWidth = TILE_SIZE * numXTiles;
356 
357 	uint8 mask = 0;
358 	byte *buf;
359 	if (_flagOne) {
360 		// Paint to the foreground
361 		buf = (byte *)_fg->getPixels();
362 		if (_flag2Byte) {
363 			mask = 0xff;
364 		} else {
365 			mask = 0;
366 		}
367 
368 		// TODO: Verify this is the right procedure. Couldn't find it on the
369 		// disassembly, but it's required to work properly
370 		_flagFirstFrame = true;
371 	} else {
372 		// Paint to the background
373 		buf = (byte *)_bg->getPixels();
374 	}
375 
376 	// Read the palette
377 	in->read(_palBuf, 3 * 256);
378 
379 	if (_flagSeven) {
380 		_flagFive = true;
381 	}
382 
383 	// Skip the frame when flag 5 is set, unless flag 1 is set
384 	if (!_flagFive || _flagOne) {
385 
386 		byte colors[16];
387 		for (uint16 j = 0; j < numYTiles; j++) {
388 			byte *currentTile = buf + j * TILE_SIZE * imageWidth;
389 			for (uint16 i = numXTiles; i; i--) {
390 				uint8 color1 = in->readByte();
391 				uint8 color0 = in->readByte();
392 				uint16 colorMap = in->readUint16LE();
393 				expandColorMap(colors, colorMap, color1, color0);
394 				decodeBlockStill(currentTile, colors, 640, mask);
395 
396 				currentTile += TILE_SIZE;
397 			}
398 		}
399 
400 		// Apply the palette
401 		if (_flagNine) {
402 			// Flag 9 starts a fade in
403 			fadeIn(_palBuf);
404 		} else {
405 			if (!_flagOne && !_flagSeven) {
406 				// Actually apply the palette
407 				setPalette(_palBuf);
408 			}
409 		}
410 
411 		if (!_flagOne) {
412 			_vm->_graphicsMan->updateScreen(_bg);
413 		}
414 		/*
415 		if (_flagSix) {
416 			if (_flagOne) {
417 				_vm->_graphicsMan->updateScreen(_fg);
418 			} else {
419 				_vm->_graphicsMan->updateScreen(_bg);
420 			}
421 			_flagSix = 0;
422 		}
423 		*/
424 	} else {
425 		// Skip the remaining data
426 		debugC(10, kDebugVideo, "Groovie::VDX: Skipping still frame");
427 		while (!in->eos()) {
428 			in->readByte();
429 		}
430 	}
431 }
432 
expandColorMap(byte * out,uint16 colorMap,uint8 color1,uint8 color0)433 void VDXPlayer::expandColorMap(byte *out, uint16 colorMap, uint8 color1, uint8 color0) {
434 	// It's a bit faster to start from the end
435 	out += 16;
436 	for (int i = 16; i; i--) {
437 		// Set the corresponding color
438 		// The following is an optimized version of:
439 		// *--out = (colorMap & 1) ? color1 : color0;
440 		uint8 selector = -(colorMap & 1);
441 		*--out = (selector & color1) | (~selector & color0);
442 
443 		// Update the flag map to test the next color
444 		colorMap >>= 1;
445 	}
446 }
447 
decodeBlockStill(byte * buf,byte * colors,uint16 imageWidth,uint8 mask)448 void VDXPlayer::decodeBlockStill(byte *buf, byte *colors, uint16 imageWidth, uint8 mask) {
449 	assert(TILE_SIZE == 4);
450 
451 	for (int y = TILE_SIZE; y; y--) {
452 		if (_flagOne) {
453 			// TODO: optimize with bit logic?
454 			for (int x = 0; x < TILE_SIZE; x++) {
455 				// 0xff pixels don't modify the buffer
456 				if (*colors != 0xff) {
457 					// Write the color
458 					*buf = *colors | mask;
459 					// Note: if the mask is 0, it paints the image
460 					// else, it paints the image's mask using 0xff
461 				}
462 
463 				// Point to the next color
464 				colors++;
465 
466 				// Point to the next pixel
467 				buf++;
468 			}
469 
470 			// Point to the start of the next line
471 			buf += imageWidth - TILE_SIZE;
472 		} else {
473 			*((uint32 *)buf) = *((uint32 *)colors);
474 			colors += 4;
475 
476 			// Point to the start of the next line
477 			buf += imageWidth;
478 		}
479 	}
480 }
481 
decodeBlockDelta(uint32 offset,byte * colors,uint16 imageWidth)482 void VDXPlayer::decodeBlockDelta(uint32 offset, byte *colors, uint16 imageWidth) {
483 	assert(TILE_SIZE == 4);
484 
485 	byte *dest;
486 	// TODO: Verify just the else block is required
487 	//if (_flagOne) {
488 		// Paint to the foreground
489 		//dest = (byte *)_fg->getPixels() + offset;
490 	//} else {
491 		dest = (byte *)_bg->getPixels() + offset;
492 	//}
493 
494 	// Move the pointers to the beginning of the current block
495 	int32 blockOff = _origX + _origY * imageWidth;
496 	dest += blockOff;
497 	byte *fgBuf = 0;
498 	if (_flagSeven) {
499 		fgBuf = (byte *)_fg->getPixels() + offset + blockOff;
500 		//byte *bgBuf = (byte *)_bg->getPixels() + offset + blockOff;
501 	}
502 
503 	for (int y = TILE_SIZE; y; y--) {
504 		if (_flagSeven) {
505 			// Paint mask
506 			for (int x = 0; x < TILE_SIZE; x++) {
507 				// TODO: this can probably be optimized with bit logic
508 				if (fgBuf[x] != 0xff) {
509 					if (*colors == 0xff) {
510 						dest[x] = fgBuf[x];
511 					} else {
512 						dest[x] = *colors;
513 					}
514 				}
515 				colors++;
516 			}
517 			fgBuf += imageWidth;
518 		} else {
519 			// Paint directly
520 			*((uint32 *)dest) = *((uint32 *)colors);
521 			colors += 4;
522 		}
523 
524 		// Move to the next line
525 		dest += imageWidth;
526 	}
527 }
528 
chunkSound(Common::ReadStream * in)529 void VDXPlayer::chunkSound(Common::ReadStream *in) {
530 	if (getOverrideSpeed())
531 		setOverrideSpeed(false);
532 
533 	if (!_audioStream) {
534 		_audioStream = Audio::makeQueuingAudioStream(22050, false);
535 		Audio::SoundHandle sound_handle;
536 		g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &sound_handle, _audioStream);
537 	}
538 
539 	byte *data = (byte *)malloc(60000);
540 	int chunksize = in->read(data, 60000);
541 	if (!DebugMan.isDebugChannelEnabled(kDebugFast)) {
542 		_audioStream->queueBuffer(data, chunksize, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED);
543 	}
544 }
545 
fadeIn(uint8 * targetpal)546 void VDXPlayer::fadeIn(uint8 *targetpal) {
547 	// Don't do anything if we're asked to skip palette changes
548 	if (_flagSkipPalette)
549 		return;
550 
551 	// TODO: Is it required? If so, move to an appropiate place
552 	// Copy the foreground to the background
553 	memcpy((byte *)_vm->_graphicsMan->_foreground.getPixels(), (byte *)_vm->_graphicsMan->_background.getPixels(), 640 * 320);
554 
555 	// Start a fadein
556 	_vm->_graphicsMan->fadeIn(targetpal);
557 
558 	// Show the background
559 	_vm->_graphicsMan->updateScreen(_bg);
560 }
561 
setPalette(uint8 * palette)562 void VDXPlayer::setPalette(uint8 *palette) {
563 	if (_flagSkipPalette)
564 		return;
565 
566 	debugC(7, kDebugVideo, "Groovie::VDX: Setting palette");
567 	_syst->getPaletteManager()->setPalette(palette, 0, 256);
568 }
569 
570 } // End of Groovie namespace
571