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 #ifdef ENABLE_HE
24 
25 #include "common/system.h"
26 #include "common/memstream.h"
27 #include "audio/audiostream.h"
28 #include "audio/mixer.h"
29 #include "audio/decoders/raw.h"
30 #include "graphics/palette.h"
31 #include "scumm/scumm.h"
32 #include "scumm/util.h"
33 #include "scumm/he/intern_he.h"
34 #include "scumm/he/cup_player_he.h"
35 
36 namespace Scumm {
37 
CUP_Player(OSystem * sys,ScummEngine_vCUPhe * vm,Audio::Mixer * mixer)38 CUP_Player::CUP_Player(OSystem *sys, ScummEngine_vCUPhe *vm, Audio::Mixer *mixer)
39 	: _vm(vm), _mixer(mixer), _system(sys) {
40 }
41 
open(const char * filename)42 bool CUP_Player::open(const char *filename) {
43 	bool opened = false;
44 	debug(1, "opening '%s'", filename);
45 	if (_fileStream.open(filename)) {
46 		uint32 tag = _fileStream.readUint32BE();
47 		_fileStream.readUint32BE();
48 		if (tag == MKTAG('B','E','A','N')) {
49 			_playbackRate = kDefaultPlaybackRate;
50 			_width = kDefaultVideoWidth;
51 			_height = kDefaultVideoHeight;
52 
53 			memset(_paletteData, 0, sizeof(_paletteData));
54 			_paletteChanged = false;
55 			_offscreenBuffer = 0;
56 
57 			_inLzssBufData = 0;
58 			_inLzssBufSize = 0;
59 			_outLzssBufData = 0;
60 			_outLzssBufSize = 0;
61 
62 			_dataSize = 0;
63 
64 			_sfxCount = 0;
65 			_sfxBuffer = 0;
66 			for (int i = 0; i < kSfxChannels; ++i) {
67 				_sfxChannels[i].sfxNum = -1;
68 			}
69 			memset(_sfxQueue, 0, sizeof(_sfxQueue));
70 			_sfxQueuePos = 0;
71 			_lastSfxChannel = -1;
72 
73 			_offscreenBuffer = (uint8 *)malloc(_width * _height);
74 			memset(_offscreenBuffer, 0, _width * _height);
75 
76 			opened = true;
77 		}
78 	}
79 	return opened;
80 }
81 
close()82 void CUP_Player::close() {
83 	_fileStream.close();
84 	free(_offscreenBuffer);
85 	_offscreenBuffer = 0;
86 	free(_inLzssBufData);
87 	_inLzssBufData = 0;
88 	free(_outLzssBufData);
89 	_outLzssBufData = 0;
90 	free(_sfxBuffer);
91 	_sfxBuffer = 0;
92 }
93 
play()94 void CUP_Player::play() {
95 	while (parseNextHeaderTag(_fileStream)) { }
96 
97 	if (_fileStream.eos() || _fileStream.err())
98 		return;
99 
100 	debug(1, "rate %d width %d height %d", _playbackRate, _width, _height);
101 
102 	int ticks = _system->getMillis();
103 	while (_dataSize != 0 && !_vm->shouldQuit()) {
104 		while (parseNextBlockTag(_fileStream)) { }
105 		if (_fileStream.eos() || _fileStream.err())
106 			return;
107 
108 		int diff = _system->getMillis() - ticks;
109 		if (diff >= 0 && diff <= _playbackRate) {
110 			_system->delayMillis(_playbackRate - diff);
111 		} else {
112 			_system->delayMillis(1);
113 		}
114 		updateSfx();
115 		updateScreen();
116 		_vm->parseEvents();
117 		ticks = _system->getMillis();
118 	}
119 }
120 
copyRectToScreen(const Common::Rect & r)121 void CUP_Player::copyRectToScreen(const Common::Rect &r) {
122 	const uint8 *src = _offscreenBuffer + r.top * _width + r.left;
123 	_system->copyRectToScreen(src, _width, r.left, r.top, r.width() + 1, r.height() + 1);
124 }
125 
updateScreen()126 void CUP_Player::updateScreen() {
127 	if (_paletteChanged) {
128 		_system->getPaletteManager()->setPalette(_paletteData, 0, 256);
129 		_paletteChanged = false;
130 	}
131 	_system->updateScreen();
132 }
133 
updateSfx()134 void CUP_Player::updateSfx() {
135 	int lastSfxChannel = _lastSfxChannel;
136 	for (int i = 0; i < _sfxQueuePos; ++i) {
137 		const CUP_Sfx *sfx = &_sfxQueue[i];
138 		if (sfx->num == -1) {
139 			debug(1, "Stopping sound channel %d", _lastSfxChannel);
140 			if (_lastSfxChannel != -1) {
141 				_mixer->stopHandle(_sfxChannels[_lastSfxChannel].handle);
142 			}
143 			continue;
144 		}
145 		if ((sfx->flags & kSfxFlagRestart) != 0) {
146 			for (int ch = 0; ch < kSfxChannels; ++ch) {
147 				if (_mixer->isSoundHandleActive(_sfxChannels[ch].handle) && _sfxChannels[ch].sfxNum == sfx->num) {
148 					_mixer->stopHandle(_sfxChannels[ch].handle);
149 					break;
150 				}
151 			}
152 		}
153 		CUP_SfxChannel *sfxChannel = 0;
154 		for (int ch = 0; ch < kSfxChannels; ++ch) {
155 			if (!_mixer->isSoundHandleActive(_sfxChannels[ch].handle)) {
156 				lastSfxChannel = ch;
157 				sfxChannel = &_sfxChannels[ch];
158 				sfxChannel->sfxNum = sfx->num;
159 				sfxChannel->flags = sfx->flags;
160 				break;
161 			}
162 		}
163 		if (sfxChannel) {
164 			debug(1, "Start sound %d channel %d flags 0x%X", sfx->num, lastSfxChannel, sfx->flags);
165 			int sfxIndex = sfxChannel->sfxNum - 1;
166 			assert(sfxIndex >= 0 && sfxIndex < _sfxCount);
167 			uint32 offset = READ_LE_UINT32(_sfxBuffer + sfxIndex * 4) - 8;
168 			uint8 *soundData = _sfxBuffer + offset;
169 			if (READ_BE_UINT32(soundData) == MKTAG('D','A','T','A')) {
170 				uint32 soundSize = READ_BE_UINT32(soundData + 4);
171 				_mixer->playStream(Audio::Mixer::kSFXSoundType, &sfxChannel->handle,
172 							Audio::makeLoopingAudioStream(
173 								Audio::makeRawStream(soundData + 8, soundSize - 8,
174 										11025, Audio::FLAG_UNSIGNED, DisposeAfterUse::NO),
175 								(sfx->flags & kSfxFlagLoop) ? 0 : 1
176 							)
177 						);
178 			}
179 		} else {
180 			warning("Unable to find a free channel to play sound %d", sfx->num);
181 		}
182 	}
183 	_lastSfxChannel = lastSfxChannel;
184 	_sfxQueuePos = 0;
185 }
186 
waitForSfxChannel(int channel)187 void CUP_Player::waitForSfxChannel(int channel) {
188 	assert(channel >= 0 && channel < kSfxChannels);
189 	CUP_SfxChannel *sfxChannel = &_sfxChannels[channel];
190 	debug(1, "waitForSfxChannel %d", channel);
191 	if ((sfxChannel->flags & kSfxFlagLoop) == 0) {
192 		while (_mixer->isSoundHandleActive(sfxChannel->handle) && !_vm->shouldQuit()) {
193 			_vm->parseEvents();
194 			_system->delayMillis(10);
195 		}
196 	}
197 }
198 
parseNextHeaderTag(Common::SeekableReadStream & dataStream)199 bool CUP_Player::parseNextHeaderTag(Common::SeekableReadStream &dataStream) {
200 	uint32 tag = dataStream.readUint32BE();
201 	uint32 size = dataStream.readUint32BE() - 8;
202 
203 	if (dataStream.eos())
204 		return false;
205 
206 	uint32 next = dataStream.pos() + size;
207 	debug(1, "New header tag %s %d dataSize %d", tag2str(tag), size, _dataSize);
208 	switch (tag) {
209 	case MKTAG('H','E','A','D'):
210 		handleHEAD(dataStream, size);
211 		break;
212 	case MKTAG('S','F','X','B'):
213 		handleSFXB(dataStream, size);
214 		break;
215 	case MKTAG('R','G','B','S'):
216 		handleRGBS(dataStream, size);
217 		break;
218 	case MKTAG('D','A','T','A'):
219 		_dataSize = size;
220 		return false;
221 	case MKTAG('G','F','X','B'):
222 		// this is never triggered
223 	default:
224 		warning("Unhandled tag %s", tag2str(tag));
225 		break;
226 	}
227 	dataStream.seek(next);
228 	return true;
229 }
230 
parseNextBlockTag(Common::SeekableReadStream & dataStream)231 bool CUP_Player::parseNextBlockTag(Common::SeekableReadStream &dataStream) {
232 	uint32 tag = dataStream.readUint32BE();
233 	uint32 size = dataStream.readUint32BE() - 8;
234 	uint32 next = dataStream.pos() + size;
235 	debug(1, "New block tag %s %d dataSize %d", tag2str(tag), size, _dataSize);
236 	switch (tag) {
237 	case MKTAG('F','R','A','M'):
238 		handleFRAM(dataStream, size);
239 		break;
240 	case MKTAG('L','Z','S','S'):
241 		if (handleLZSS(dataStream, size) && _outLzssBufSize != 0) {
242 			Common::MemoryReadStream memoryStream(_outLzssBufData, _outLzssBufSize);
243 			parseNextBlockTag(memoryStream);
244 		}
245 		break;
246 	case MKTAG('R','A','T','E'):
247 		handleRATE(dataStream, size);
248 		break;
249 	case MKTAG('R','G','B','S'):
250 		handleRGBS(dataStream, size);
251 		break;
252 	case MKTAG('S','N','D','E'):
253 		handleSNDE(dataStream, size);
254 		break;
255 	case MKTAG('T','O','I','L'):
256 		handleTOIL(dataStream, size);
257 		break;
258 	case MKTAG('S','R','L','E'):
259 		handleSRLE(dataStream, size);
260 		break;
261 	case MKTAG('B','L','O','K'):
262 		_dataSize -= size + 8;
263 		return false;
264 	case MKTAG('W','R','L','E'):
265 		// this is never triggered
266 	default:
267 		warning("Unhandled tag %s", tag2str(tag));
268 		break;
269 	}
270 	dataStream.seek(next);
271 	return true;
272 }
273 
handleHEAD(Common::SeekableReadStream & dataStream,uint32 dataSize)274 void CUP_Player::handleHEAD(Common::SeekableReadStream &dataStream, uint32 dataSize) {
275 	_playbackRate = dataStream.readUint16LE();
276 	_width = dataStream.readUint16LE();
277 	_height = dataStream.readUint16LE();
278 }
279 
handleSFXB(Common::SeekableReadStream & dataStream,uint32 dataSize)280 void CUP_Player::handleSFXB(Common::SeekableReadStream &dataStream, uint32 dataSize) {
281 	if (dataSize > 16) { // WRAP and OFFS chunks
282 		uint32 tag = dataStream.readUint32BE();
283 		uint32 size = dataStream.readUint32BE();
284 		if (tag == MKTAG('W','R','A','P')) {
285 			tag = dataStream.readUint32BE();
286 			size = dataStream.readUint32BE();
287 			if (tag == MKTAG('O','F','F','S')) {
288 				_sfxCount = (size - 8) / 4;
289 				_sfxBuffer = (uint8 *)malloc(dataSize - 16);
290 				if (_sfxBuffer) {
291 					dataStream.read(_sfxBuffer, dataSize - 16);
292 				}
293 			}
294 		}
295 	}
296 }
297 
handleRGBS(Common::SeekableReadStream & dataStream,uint32 dataSize)298 void CUP_Player::handleRGBS(Common::SeekableReadStream &dataStream, uint32 dataSize) {
299 	dataStream.read(_paletteData, 256 * 3);
300 	_paletteChanged = true;
301 }
302 
decodeTRLE(uint8 * dst,int dstPitch,Common::Rect & dstRect,Common::SeekableReadStream & dataStream)303 static void decodeTRLE(uint8 *dst, int dstPitch, Common::Rect &dstRect, Common::SeekableReadStream &dataStream) {
304 	dst += dstRect.top * dstPitch + dstRect.left;
305 	int h = dstRect.bottom - dstRect.top + 1;
306 	int w = dstRect.right - dstRect.left + 1;
307 	while (h--) {
308 		int lineSize = dataStream.readUint16LE();
309 		int nextLineOffset = dataStream.pos() + lineSize;
310 		uint8 *dstNextLine = dst + dstPitch;
311 		if (lineSize != 0) {
312 			uint8 *dstEnd = dst + w;
313 			while (dst < dstEnd) {
314 				int code = dataStream.readByte();
315 				if (code & 1) { // skip
316 					code >>= 1;
317 					dst += code;
318 				} else if (code & 2) { // set
319 					code = (code >> 2) + 1;
320 					const int sz = MIN<int>(code, dstEnd - dst);
321 					memset(dst, dataStream.readByte(), sz);
322 					dst += sz;
323 				} else { // copy
324 					code = (code >> 2) + 1;
325 					const int sz = MIN<int>(code, dstEnd - dst);
326 					dataStream.read(dst, sz);
327 					dst += sz;
328 				}
329 			}
330 		}
331 		dataStream.seek(nextLineOffset);
332 		dst = dstNextLine;
333 	}
334 }
335 
336 
handleFRAM(Common::SeekableReadStream & dataStream,uint32 dataSize)337 void CUP_Player::handleFRAM(Common::SeekableReadStream &dataStream, uint32 dataSize) {
338 	const uint8 flags = dataStream.readByte();
339 	int type = 256;
340 	if (flags & 1) {
341 		type = dataStream.readByte();
342 	}
343 	Common::Rect r;
344 	if (flags & 2) {
345 		r.left   = dataStream.readUint16LE();
346 		r.top    = dataStream.readUint16LE();
347 		r.right  = dataStream.readUint16LE();
348 		r.bottom = dataStream.readUint16LE();
349 	}
350 	if (flags & 0x80) {
351 		if (type == 256) {
352 			decodeTRLE(_offscreenBuffer, _width, r, dataStream);
353 			copyRectToScreen(r);
354 		} else {
355 			warning("Unhandled FRAM type %d", type); // this is never triggered
356 		}
357 	}
358 }
359 
decodeSRLE(uint8 * dst,const uint8 * colorMap,Common::SeekableReadStream & dataStream,int unpackedSize)360 static void decodeSRLE(uint8 *dst, const uint8 *colorMap, Common::SeekableReadStream &dataStream, int unpackedSize) {
361 	while (unpackedSize > 0) {
362 		int size, code = dataStream.readByte();
363 		if ((code & 1) == 0) {
364 			if ((code & 2) == 0) {
365 				size = (code >> 2) + 1;
366 				dst += size;
367 				unpackedSize -= size;
368 			} else {
369 				if ((code & 4) == 0) {
370 					*dst++ = colorMap[code >> 3];
371 					--unpackedSize;
372 				} else {
373 					code >>= 3;
374 					if (code == 0) {
375 						size = 1 + dataStream.readByte();
376 					} else {
377 						size = code;
378 					}
379 					memset(dst, dataStream.readByte(), MIN(unpackedSize, size));
380 					dst += size;
381 					unpackedSize -= size;
382 				}
383 			}
384 		} else {
385 			code >>= 1;
386 			if (code == 0) {
387 				code = 1 + dataStream.readUint16LE();
388 			}
389 			dst += code;
390 			unpackedSize -= code;
391 		}
392 	}
393 }
394 
handleSRLE(Common::SeekableReadStream & dataStream,uint32 dataSize)395 void CUP_Player::handleSRLE(Common::SeekableReadStream &dataStream, uint32 dataSize) {
396 	Common::Rect r;
397 	r.left   = dataStream.readUint16LE();
398 	r.top    = dataStream.readUint16LE();
399 	r.right  = dataStream.readUint16LE();
400 	r.bottom = dataStream.readUint16LE();
401 	uint8 colorMap[32];
402 	dataStream.read(colorMap, 32);
403 	int unpackedSize = dataStream.readUint32LE();
404 	decodeSRLE(_offscreenBuffer, colorMap, dataStream, unpackedSize);
405 	copyRectToScreen(r);
406 }
407 
decodeLZSS(uint8 * dst,const uint8 * src1,const uint8 * src2,const uint8 * src3)408 static void decodeLZSS(uint8 *dst, const uint8 *src1, const uint8 *src2, const uint8 *src3) {
409 	uint8 wnd[4096];
410 	int index = 1;
411 
412 	memset(wnd, 0, sizeof(wnd));
413 
414 	while (1) {
415 		int code = *src1++;
416 		for (int b = 0; b < 8; ++b) {
417 			if (code & (1 << b)) {
418 				*dst++ = wnd[index] = *src2++;
419 				++index;
420 				index &= 0xFFF;
421 			} else {
422 				int cmd = READ_LE_UINT16(src3); src3 += 2;
423 				int count = (cmd >> 0xC) + 2;
424 				int offs = cmd & 0xFFF;
425 				if (offs == 0) {
426 					return;
427 				}
428 				while (count--) {
429 					*dst++ = wnd[index] = wnd[offs];
430 					++index;
431 					index &= 0xFFF;
432 					++offs;
433 					offs &= 0xFFF;
434 				}
435 			}
436 		}
437 	}
438 }
439 
handleLZSS(Common::SeekableReadStream & dataStream,uint32 dataSize)440 bool CUP_Player::handleLZSS(Common::SeekableReadStream &dataStream, uint32 dataSize) {
441 	uint32 tag = dataStream.readUint32BE();
442 	uint32 size = dataStream.readUint32BE();
443 	if (tag == MKTAG('L','Z','H','D')) {
444 		uint32 compressionType = dataStream.readUint32LE();
445 		uint32 compressionSize = dataStream.readUint32LE();
446 		tag = dataStream.readUint32BE();
447 		size = dataStream.readUint32BE();
448 		if (tag == MKTAG('D','A','T','A') && compressionType == 0x2000) {
449 			if (_inLzssBufSize < size - 16) {
450 				free(_inLzssBufData);
451 				_inLzssBufSize = size - 16;
452 				_inLzssBufData = (uint8 *)malloc(_inLzssBufSize);
453 			}
454 			if (_outLzssBufSize < compressionSize) {
455 				free(_outLzssBufData);
456 				_outLzssBufSize = compressionSize;
457 				_outLzssBufData = (uint8 *)malloc(_outLzssBufSize);
458 			}
459 			if (_inLzssBufData && _outLzssBufData) {
460 				uint32 offset1 = dataStream.readUint32LE() - 8;
461 				uint32 offset2 = dataStream.readUint32LE() - 8;
462 				dataStream.read(_inLzssBufData, size - 16);
463 				decodeLZSS(_outLzssBufData, _inLzssBufData, _inLzssBufData + offset1, _inLzssBufData + offset2);
464 				return true;
465 			}
466 		}
467 	}
468 	return false;
469 }
470 
handleRATE(Common::SeekableReadStream & dataStream,uint32 dataSize)471 void CUP_Player::handleRATE(Common::SeekableReadStream &dataStream, uint32 dataSize) {
472 	const int rate = dataStream.readSint16LE();
473 	_playbackRate = CLIP(rate, 1, 4000);
474 }
475 
handleSNDE(Common::SeekableReadStream & dataStream,uint32 dataSize)476 void CUP_Player::handleSNDE(Common::SeekableReadStream &dataStream, uint32 dataSize) {
477 	assert(_sfxQueuePos < kSfxQueueSize);
478 	CUP_Sfx *sfx = &_sfxQueue[_sfxQueuePos];
479 	sfx->flags = dataStream.readUint32LE();
480 	sfx->num = dataStream.readUint16LE();
481 	dataStream.skip(2);
482 	uint16 loop = dataStream.readUint16LE();
483 	assert((loop & 0x8000) != 0); // this is never triggered
484 	++_sfxQueuePos;
485 }
486 
handleTOIL(Common::SeekableReadStream & dataStream,uint32 dataSize)487 void CUP_Player::handleTOIL(Common::SeekableReadStream &dataStream, uint32 dataSize) {
488 	int codesCount = dataStream.readUint16LE();
489 	while (codesCount != 0) {
490 		int codeSize = dataStream.readByte();
491 		if (codeSize == 0) {
492 			codeSize = dataStream.readUint16LE();
493 		}
494 		int code = dataStream.readByte();
495 		if (code == 0) {
496 			code = dataStream.readUint16LE();
497 		}
498 		switch (code) {
499 		case 1:
500 			for (int i = 0; i < kSfxChannels; ++i) {
501 				waitForSfxChannel(i);
502 			}
503 			_vm->quitGame();
504 			break;
505 		case 7: {
506 				int channelSync = dataStream.readUint32LE();
507 				waitForSfxChannel(channelSync);
508 			}
509 			break;
510 		case 2: // display copyright/information messagebox
511 		case 3: // no-op in the original
512 		case 4: // restart playback
513 		case 5: // disable normal screen update
514 		case 6: // enable double buffer rendering
515 			// these are never triggered
516 		default:
517 			warning("Unhandled TOIL code=%d", code);
518 			break;
519 		}
520 		--codesCount;
521 	}
522 }
523 
524 } // End of namespace Scumm
525 
526 #endif // ENABLE_HE
527