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/stream.h"
25 #include "common/textconsole.h"
26 
27 #include "audio/audiostream.h"
28 #include "audio/decoders/raw.h"
29 
30 #include "access/access.h"
31 #include "access/video/movie_decoder.h"
32 
33 // for Test-Code
34 #include "common/system.h"
35 #include "common/events.h"
36 #include "common/keyboard.h"
37 #include "engines/engine.h"
38 #include "engines/util.h"
39 #include "graphics/palette.h"
40 #include "graphics/pixelformat.h"
41 #include "graphics/surface.h"
42 
43 namespace Access {
44 
AccessVIDMovieDecoder()45 AccessVIDMovieDecoder::AccessVIDMovieDecoder()
46 	: _stream(0), _videoTrack(0), _audioTrack(0) {
47 	_streamSeekOffset = 0;
48 	_streamVideoIndex = 0;
49 	_streamAudioIndex = 0;
50 }
51 
~AccessVIDMovieDecoder()52 AccessVIDMovieDecoder::~AccessVIDMovieDecoder() {
53 	close();
54 }
55 
loadStream(Common::SeekableReadStream * stream)56 bool AccessVIDMovieDecoder::loadStream(Common::SeekableReadStream *stream) {
57 	uint32 videoCodecTag   = 0;
58 	uint32 videoHeight     = 0;
59 	uint32 videoWidth      = 0;
60 	uint16 regularDelay    = 0;
61 	uint32 audioSampleRate = 0;
62 
63 	close();
64 
65 	_stream = stream;
66 	_streamSeekOffset = 15; // offset of first chunk
67 	_streamVideoIndex = 0;
68 	_streamAudioIndex = 0;
69 
70 	// read header
71 	//  ID [dword] "VID"
72 	//  ?? [byte]
73 	//  ?? [word]
74 	//  width [word]
75 	//  height [word]
76 	//  regular delay between frames (60 per second) [word]
77 	//  ?? [word]
78 
79 	videoCodecTag = _stream->readUint32BE();
80 	if (videoCodecTag != MKTAG('V','I','D',0x00)) {
81 		warning("AccessVIDMoviePlay: bad codec tag, not a video file?");
82 		close();
83 		return false;
84 	}
85 	_stream->skip(3);
86 	videoWidth = _stream->readUint16LE();
87 	videoHeight = _stream->readUint16LE();
88 	regularDelay = _stream->readUint16LE();
89 	_stream->skip(2);
90 
91 	if (!regularDelay) {
92 		warning("AccessVIDMoviePlay: delay between frames is zero?");
93 		close();
94 		return false;
95 	}
96 
97 	// create video track
98 	_videoTrack = new StreamVideoTrack(videoWidth, videoHeight, regularDelay);
99 	addTrack(_videoTrack);
100 
101 	//warning("width %d, height %d", videoWidth, videoHeight);
102 
103 	// Look through the first few packets
104 	static const int maxPacketCheckCount = 10;
105 
106 	for (int i = 0; i < maxPacketCheckCount; i++) {
107 		byte chunkId = _stream->readByte();
108 
109 		// Bail out if done
110 		if (_stream->eos())
111 			break;
112 
113 		// Bail also in case end of file chunk was found
114 		if (chunkId == kVIDMovieChunkId_EndOfFile)
115 			break;
116 
117 		uint32 chunkStartOffset = _stream->pos();
118 		//warning("data chunk at %x", chunkStartOffset);
119 
120 		switch (chunkId) {
121 		case kVIDMovieChunkId_FullFrame:
122 		case kVIDMovieChunkId_FullFrameCompressed:
123 		case kVIDMovieChunkId_PartialFrameCompressed:
124 		case kVIDMovieChunkId_FullFrameCompressedFill: {
125 			if (!_videoTrack->skipOverFrame(_stream, chunkId)) {
126 				close();
127 				return false;
128 			}
129 			break;
130 		}
131 
132 		case kVIDMovieChunkId_Palette: {
133 			if (!_videoTrack->skipOverPalette(_stream)) {
134 				close();
135 				return false;
136 			}
137 			break;
138 		}
139 
140 		case kVIDMovieChunkId_AudioFirstChunk:
141 		case kVIDMovieChunkId_Audio: {
142 			// sync [word]
143 			// sampling rate [byte]
144 			// size of audio data [word]
145 			// sample data [] (mono, 8-bit, unsigned)
146 			//
147 			// Only first chunk has sync + sampling rate
148 			if (chunkId == kVIDMovieChunkId_AudioFirstChunk) {
149 				byte soundblasterRate;
150 
151 				_stream->skip(2); // skip over sync
152 				soundblasterRate = _stream->readByte();
153 				audioSampleRate = 1000000 / (256 - soundblasterRate);
154 
155 				_audioTrack = new StreamAudioTrack(audioSampleRate, getSoundType());
156 				addTrack(_audioTrack);
157 
158 				_stream->seek(chunkStartOffset); // seek back
159 			}
160 
161 			if (!_audioTrack) {
162 				warning("AccessVIDMoviePlay: regular audio chunk, before audio chunk w/ header");
163 				close();
164 				return false;
165 			}
166 			if (!_audioTrack->skipOverAudio(_stream, chunkId)) {
167 				close();
168 				return false;
169 			}
170 			break;
171 		}
172 
173 		default:
174 			warning("AccessVIDMoviePlay: Unknown chunk-id '%x' inside VID movie", chunkId);
175 			close();
176 			return false;
177 		}
178 
179 		// Remember this chunk inside our cache
180 		IndexCacheEntry indexCacheEntry;
181 
182 		indexCacheEntry.chunkId = chunkId;
183 		indexCacheEntry.offset  = chunkStartOffset;
184 
185 		_indexCacheTable.push_back(indexCacheEntry);
186 
187 		// Got an audio chunk now? -> exit b/c we are done
188 		if (audioSampleRate)
189 			break;
190 	}
191 
192 	// Remember offset of latest not-indexed-yet chunk
193 	_streamSeekOffset = _stream->pos();
194 
195 	// If sample rate was found, create an audio track
196 	if (audioSampleRate) {
197 		_audioTrack = new StreamAudioTrack(audioSampleRate, getSoundType());
198 		addTrack(_audioTrack);
199 	}
200 
201 	// Rewind back to the beginning right to the first chunk
202 	_stream->seek(15);
203 
204 	return true;
205 }
206 
close()207 void AccessVIDMovieDecoder::close() {
208 	Video::VideoDecoder::close();
209 
210 	delete _stream; _stream = 0;
211 	_videoTrack = 0;
212 
213 	_indexCacheTable.clear();
214 }
215 
216 // We try to at least decode 1 frame
217 // and also try to get at least 0.5 seconds of audio queued up
readNextPacket()218 void AccessVIDMovieDecoder::readNextPacket() {
219 	uint32 currentMovieTime = getTime();
220 	uint32 wantedAudioQueued  = currentMovieTime + 500; // always try to be 0.500 seconds in front of movie time
221 
222 	uint32 streamIndex = 0;
223 	IndexCacheEntry indexEntry;
224 	bool currentlySeeking = false;
225 
226 	bool videoDone     = false;
227 	bool audioDone     = false;
228 
229 	// Seek to smallest stream offset
230 	if ((_streamVideoIndex <= _streamAudioIndex) || (!_audioTrack)) {
231 		streamIndex = _streamVideoIndex;
232 	} else {
233 		streamIndex = _streamAudioIndex;
234 	}
235 
236 	if (_audioTrack) {
237 		if (wantedAudioQueued <= _audioTrack->getTotalAudioQueued()) {
238 			// already got enough audio queued up
239 			audioDone = true;
240 		}
241 	} else {
242 		// no audio track, audio is always done
243 		audioDone = true;
244 	}
245 
246 	while (1) {
247 		// Check, if stream-index is already cached
248 		if (streamIndex < _indexCacheTable.size()) {
249 			indexEntry.chunkId = _indexCacheTable[streamIndex].chunkId;
250 			indexEntry.offset  = _indexCacheTable[streamIndex].offset;
251 			currentlySeeking = false;
252 
253 		} else {
254 			// read from file
255 			_stream->seek(_streamSeekOffset);
256 			indexEntry.chunkId = _stream->readByte();
257 			indexEntry.offset  = _stream->pos();
258 			currentlySeeking = true;
259 
260 			// and store that as well
261 			_indexCacheTable.push_back(indexEntry);
262 		}
263 
264 		// end of stream -> exit
265 		if (_stream->eos())
266 			break;
267 
268 		// end of file chunk -> exit
269 		if (indexEntry.chunkId == kVIDMovieChunkId_EndOfFile)
270 			break;
271 
272 //		warning("chunk %x", indexEntry.chunkId);
273 
274 		switch (indexEntry.chunkId) {
275 		case kVIDMovieChunkId_FullFrame:
276 		case kVIDMovieChunkId_FullFrameCompressed:
277 		case kVIDMovieChunkId_PartialFrameCompressed:
278 		case kVIDMovieChunkId_FullFrameCompressedFill: {
279 			if ((_streamVideoIndex <= streamIndex) && (!videoDone)) {
280 				// We are at an index, that is still relevant for video decoding
281 				// and we are not done with video yet
282 				if (!currentlySeeking) {
283 					// seek to stream position in case we used the cache
284 					_stream->seek(indexEntry.offset);
285 				}
286 				//warning("video decode chunk %x at %lx", indexEntry.chunkId, _stream->pos());
287 				_videoTrack->decodeFrame(_stream, indexEntry.chunkId);
288 				videoDone = true;
289 				_streamVideoIndex = streamIndex + 1;
290 			} else {
291 				if (currentlySeeking) {
292 					// currently seeking, so we have to skip the frame bytes manually
293 					_videoTrack->skipOverFrame(_stream, indexEntry.chunkId);
294 				}
295 			}
296 			break;
297 		}
298 
299 		case kVIDMovieChunkId_Palette: {
300 			if ((_streamVideoIndex <= streamIndex) && (!videoDone)) {
301 				// We are at an index, that is still relevant for video decoding
302 				// and we are not done with video yet
303 				if (!currentlySeeking) {
304 					// seek to stream position in case we used the cache
305 					_stream->seek(indexEntry.offset);
306 				}
307 				_videoTrack->decodePalette(_stream);
308 				_streamVideoIndex = streamIndex + 1;
309 			} else {
310 				if (currentlySeeking) {
311 					// currently seeking, so we have to skip the frame bytes manually
312 					_videoTrack->skipOverPalette(_stream);
313 				}
314 			}
315 			break;
316 		}
317 
318 		case kVIDMovieChunkId_AudioFirstChunk:
319 		case kVIDMovieChunkId_Audio: {
320 			if ((_streamAudioIndex <= streamIndex) && (!audioDone)) {
321 				// We are at an index that is still relevant for audio decoding
322 				if (!currentlySeeking) {
323 					// seek to stream position in case we used the cache
324 					_stream->seek(indexEntry.offset);
325 				}
326 				_audioTrack->queueAudio(_stream, indexEntry.chunkId);
327 				_streamAudioIndex = streamIndex + 1;
328 
329 				if (wantedAudioQueued <= _audioTrack->getTotalAudioQueued()) {
330 					// Got enough audio
331 					audioDone = true;
332 				}
333 			} else {
334 				if (!_audioTrack) {
335 					error("AccessVIDMoviePlay: audio chunks found without audio track active");
336 				}
337 				if (currentlySeeking) {
338 					// currently seeking, so we have to skip the audio bytes manually
339 					_audioTrack->skipOverAudio(_stream, indexEntry.chunkId);
340 				}
341 			}
342 			break;
343 		}
344 
345 		default:
346 			error("AccessVIDMoviePlay: Unknown chunk-id '%x' inside VID movie", indexEntry.chunkId);
347 		}
348 
349 		if (currentlySeeking) {
350 			// remember currently stream offset in case we are seeking
351 			_streamSeekOffset = _stream->pos();
352 		}
353 
354 		// go to next index
355 		streamIndex++;
356 
357 		if ((videoDone) && (audioDone)) {
358 			return;
359 		}
360 	}
361 
362 	if (!videoDone) {
363 		// no more video frames? set end of video track
364 		_videoTrack->setEndOfTrack();
365 	}
366 }
367 
StreamVideoTrack(uint32 width,uint32 height,uint16 regularFrameDelay)368 AccessVIDMovieDecoder::StreamVideoTrack::StreamVideoTrack(uint32 width, uint32 height, uint16 regularFrameDelay) {
369 	_width = width;
370 	_height = height;
371 	_regularFrameDelay = regularFrameDelay;
372 	_curFrame = -1;
373 	_nextFrameStartTime = 0;
374 	_endOfTrack = false;
375 	_dirtyPalette = false;
376 
377 	memset(&_palette, 0, sizeof(_palette));
378 
379 	_surface = new Graphics::Surface();
380 	_surface->create(_width, _height, Graphics::PixelFormat::createFormatCLUT8());
381 }
382 
~StreamVideoTrack()383 AccessVIDMovieDecoder::StreamVideoTrack::~StreamVideoTrack() {
384 	delete _surface;
385 }
386 
endOfTrack() const387 bool AccessVIDMovieDecoder::StreamVideoTrack::endOfTrack() const {
388 	return _endOfTrack;
389 }
390 
getPixelFormat() const391 Graphics::PixelFormat AccessVIDMovieDecoder::StreamVideoTrack::getPixelFormat() const {
392 	return _surface->format;
393 }
394 
decodeFrame(Common::SeekableReadStream * stream,byte chunkId)395 void AccessVIDMovieDecoder::StreamVideoTrack::decodeFrame(Common::SeekableReadStream *stream, byte chunkId) {
396 	byte *framePixelsPtr = (byte *)_surface->getPixels();
397 	byte *pixelsPtr = framePixelsPtr;
398 	byte rleByte = 0;
399 	uint16 additionalDelay = 0;
400 	int32 expectedPixels = 0;
401 
402 	switch (chunkId) {
403 	case kVIDMovieChunkId_FullFrame: {
404 		// Full frame is:
405 		//  data [width * height]
406 		additionalDelay = stream->readUint16LE();
407 		stream->read(framePixelsPtr, _width * _height);
408 		break;
409 	}
410 
411 	case kVIDMovieChunkId_FullFrameCompressed:
412 	case kVIDMovieChunkId_PartialFrameCompressed: {
413 		// Skip manually over compressed data
414 		// Full frame compressed is:
415 		//  additional delay [word]
416 		//  REPEAT:
417 		//   RLE [byte]
418 		//   RLE upper bit set: skip over RLE & 0x7F pixels
419 		//   RLE upper bit not set: draw RLE amount of pixels (those pixels follow right after RLE byte)
420 		//
421 		// Partial frame compressed is:
422 		//  sync [word]
423 		//  horizontal start position [word]
424 		//  REPEAT:
425 		//   see full frame compressed
426 		uint16 horizontalStartPosition = 0;
427 
428 		additionalDelay = stream->readUint16LE();
429 
430 		if (chunkId == kVIDMovieChunkId_PartialFrameCompressed) {
431 			horizontalStartPosition = stream->readUint16LE();
432 			if (horizontalStartPosition >= _height) {
433 				error("AccessVIDMoviePlay: starting position larger than height during partial frame compressed, data corrupt?");
434 				return;
435 			}
436 		}
437 
438 		expectedPixels = _width * (_height - horizontalStartPosition);
439 
440 		// adjust frame destination pointer
441 		pixelsPtr += (horizontalStartPosition * _width);
442 
443 		while (expectedPixels >= 0) {
444 			rleByte = stream->readByte();
445 			if (!rleByte) // NUL means end of stream
446 				break;
447 
448 			if (rleByte & 0x80) {
449 				rleByte = rleByte & 0x7F;
450 				expectedPixels -= rleByte;
451 			} else {
452 				// skip over pixels
453 				expectedPixels -= rleByte;
454 				stream->read(pixelsPtr, rleByte); // read pixel data into frame
455 			}
456 			pixelsPtr += rleByte;
457 		}
458 		// expectedPixels may be positive here in case stream got terminated with a NUL
459 		if (expectedPixels < 0) {
460 			error("AccessVIDMoviePlay: pixel count mismatch during full/partial frame compressed, data corrupt?");
461 		}
462 		break;
463 	}
464 
465 	case kVIDMovieChunkId_FullFrameCompressedFill: {
466 		// Full frame compressed fill is:
467 		//  additional delay [word]
468 		//  REPEAT:
469 		//   RLE [byte]
470 		//   RLE upper bit set: draw RLE amount (& 0x7F) of pixels with specified color (color byte follows after RLE byte)
471 		//   RLE upper bit not set: draw RLE amount of pixels (those pixels follow right after RLE byte)
472 		additionalDelay = stream->readUint16LE();
473 		expectedPixels = _width * _height;
474 
475 		while (expectedPixels > 0) {
476 			rleByte = stream->readByte();
477 
478 			if (rleByte & 0x80) {
479 				rleByte = rleByte & 0x7F;
480 				expectedPixels -= rleByte;
481 
482 				byte fillColor = stream->readByte();
483 				memset(pixelsPtr, fillColor, rleByte);
484 			} else {
485 				// skip over pixels
486 				expectedPixels -= rleByte;
487 				stream->read(pixelsPtr, rleByte); // read pixel data into frame
488 			}
489 			pixelsPtr += rleByte;
490 		}
491 		if (expectedPixels < 0) {
492 			error("AccessVIDMoviePlay: pixel count mismatch during full frame compressed fill, data corrupt?");
493 		}
494 		break;
495 	}
496 	default:
497 		assert(0);
498 		break;
499 	}
500 
501 	_curFrame++;
502 
503 	// TODO: not sure, if additionalDelay is supposed to affect the follow-up frame or the current frame
504 	// the videos, that I found, don't have it set
505 	uint32 currentFrameStartTime = getNextFrameStartTime();
506 	uint32 nextFrameStartTime = (_regularFrameDelay * _curFrame) * 1000 / 60;
507 	if (additionalDelay) {
508 		nextFrameStartTime += additionalDelay * 1000 / 60;
509 	}
510 	assert(currentFrameStartTime <= nextFrameStartTime);
511 	setNextFrameStartTime(nextFrameStartTime);
512 }
513 
skipOverFrame(Common::SeekableReadStream * stream,byte chunkId)514 bool AccessVIDMovieDecoder::StreamVideoTrack::skipOverFrame(Common::SeekableReadStream *stream, byte chunkId) {
515 	byte rleByte = 0;
516 	int32 expectedPixels = 0;
517 
518 	switch (chunkId) {
519 	case kVIDMovieChunkId_FullFrame: {
520 		// Full frame is:
521 		//  additional delay [word]
522 		//  data [width * height]
523 		stream->skip(2);
524 		stream->skip(_width * _height);
525 		break;
526 	}
527 
528 	case kVIDMovieChunkId_FullFrameCompressed:
529 	case kVIDMovieChunkId_PartialFrameCompressed: {
530 		// Skip manually over compressed data
531 		// Full frame compressed is:
532 		//  additional delay [word]
533 		//  REPEAT:
534 		//   RLE [byte]
535 		//   RLE upper bit set: skip over RLE & 0x7F pixels
536 		//   RLE upper bit not set: draw RLE amount of pixels (those pixels follow right after RLE byte)
537 		//
538 		// Partial frame compressed is:
539 		//  sync [word]
540 		//  horizontal start position [word]
541 		//  REPEAT:
542 		//   see full frame compressed
543 		uint16 horizontalStartPosition = 0;
544 
545 		stream->skip(2);
546 
547 		if (chunkId == kVIDMovieChunkId_PartialFrameCompressed) {
548 			horizontalStartPosition = stream->readUint16LE();
549 			if (horizontalStartPosition >= _height) {
550 				warning("AccessVIDMoviePlay: starting position larger than height during partial frame compressed, data corrupt?");
551 				return false;
552 			}
553 		}
554 
555 		expectedPixels = _width * (_height - horizontalStartPosition);
556 
557 		while (expectedPixels >= 0) {
558 			rleByte = stream->readByte();
559 			if (!rleByte) // NUL means end of stream
560 				break;
561 
562 			if (rleByte & 0x80) {
563 				expectedPixels -= rleByte & 0x7F;
564 			} else {
565 				// skip over pixels
566 				expectedPixels -= rleByte;
567 				stream->skip(rleByte); // skip over pixel data
568 			}
569 		}
570 		// expectedPixels may be positive here in case stream got terminated with a NUL
571 		if (expectedPixels < 0) {
572 			warning("AccessVIDMoviePlay: pixel count mismatch during full/partial frame compressed, data corrupt?");
573 			return false;
574 		}
575 		break;
576 	}
577 
578 	case kVIDMovieChunkId_FullFrameCompressedFill: {
579 		// Full frame compressed fill is:
580 		//  additional delay [word]
581 		//  REPEAT:
582 		//   RLE [byte]
583 		//   RLE upper bit set: draw RLE amount (& 0x7F) of pixels with specified color (color byte follows after RLE byte)
584 		//   RLE upper bit not set: draw RLE amount of pixels (those pixels follow right after RLE byte)
585 		stream->skip(2);
586 		expectedPixels = _width * _height;
587 
588 		while (expectedPixels > 0) {
589 			rleByte = stream->readByte();
590 
591 			if (rleByte & 0x80) {
592 				expectedPixels -= rleByte & 0x7F;
593 				stream->skip(1);
594 			} else {
595 				// skip over pixels
596 				expectedPixels -= rleByte;
597 				stream->skip(rleByte); // skip over pixel data
598 			}
599 		}
600 		if (expectedPixels < 0) {
601 			warning("AccessVIDMoviePlay: pixel count mismatch during full frame compressed fill, data corrupt?");
602 			return false;
603 		}
604 		break;
605 	}
606 	default:
607 		assert(0);
608 		break;
609 	}
610 	return true;
611 }
612 
skipOverPalette(Common::SeekableReadStream * stream)613 bool AccessVIDMovieDecoder::StreamVideoTrack::skipOverPalette(Common::SeekableReadStream *stream) {
614 	stream->skip(0x300); // 3 bytes per color, 256 colors
615 	return true;
616 }
617 
decodePalette(Common::SeekableReadStream * stream)618 void AccessVIDMovieDecoder::StreamVideoTrack::decodePalette(Common::SeekableReadStream *stream) {
619 	byte red, green, blue;
620 	assert(stream);
621 
622 	// VID files use a 6-bit palette and not a 8-bit one, we change it to 8-bit
623 	for (uint16 curColor = 0; curColor < 256; curColor++) {
624 		red = stream->readByte() & 0x3F;
625 		green = stream->readByte() & 0x3F;
626 		blue = stream->readByte() & 0x3F;
627 		_palette[curColor * 3] = (red << 2) | (red >> 4);
628 		_palette[curColor * 3 + 1] = (green << 2) | (green >> 4);
629 		_palette[curColor * 3 + 2] = (blue << 2) | (blue >> 4);
630 	}
631 
632 	_dirtyPalette = true;
633 }
634 
getPalette() const635 const byte *AccessVIDMovieDecoder::StreamVideoTrack::getPalette() const {
636 	_dirtyPalette = false;
637 	return _palette;
638 }
639 
hasDirtyPalette() const640 bool AccessVIDMovieDecoder::StreamVideoTrack::hasDirtyPalette() const {
641 	return _dirtyPalette;
642 }
643 
StreamAudioTrack(uint32 sampleRate,Audio::Mixer::SoundType soundType)644 AccessVIDMovieDecoder::StreamAudioTrack::StreamAudioTrack(uint32 sampleRate, Audio::Mixer::SoundType soundType) :
645 		AudioTrack(soundType) {
646 	_totalAudioQueued = 0; // currently 0 milliseconds queued
647 
648 	_sampleRate  = sampleRate;
649 	_stereo = false; // always mono
650 
651 	_audioStream = Audio::makeQueuingAudioStream(sampleRate, _stereo);
652 }
653 
~StreamAudioTrack()654 AccessVIDMovieDecoder::StreamAudioTrack::~StreamAudioTrack() {
655 	delete _audioStream;
656 }
657 
queueAudio(Common::SeekableReadStream * stream,byte chunkId)658 void AccessVIDMovieDecoder::StreamAudioTrack::queueAudio(Common::SeekableReadStream *stream, byte chunkId) {
659 	Common::SeekableReadStream *rawAudioStream = 0;
660 	Audio::RewindableAudioStream *audioStream = 0;
661 	uint32 audioLengthMSecs = 0;
662 
663 	if (chunkId == kVIDMovieChunkId_AudioFirstChunk) {
664 		stream->skip(3); // skip over additional delay + sample rate
665 	}
666 
667 	uint32 audioSize = stream->readUint16LE();
668 
669 	// Read the specified chunk into memory
670 	rawAudioStream = stream->readStream(audioSize);
671 	audioLengthMSecs = audioSize * 1000 / _sampleRate; // 1 byte == 1 8-bit sample
672 
673 	audioStream = Audio::makeRawStream(rawAudioStream, _sampleRate, Audio::FLAG_UNSIGNED | Audio::FLAG_LITTLE_ENDIAN, DisposeAfterUse::YES);
674 	if (audioStream) {
675 		_totalAudioQueued += audioLengthMSecs;
676 		_audioStream->queueAudioStream(audioStream, DisposeAfterUse::YES);
677 	} else {
678 		// in case there was an error
679 		delete rawAudioStream;
680 	}
681 }
682 
skipOverAudio(Common::SeekableReadStream * stream,byte chunkId)683 bool AccessVIDMovieDecoder::StreamAudioTrack::skipOverAudio(Common::SeekableReadStream *stream, byte chunkId) {
684 	if (chunkId == kVIDMovieChunkId_AudioFirstChunk) {
685 		stream->skip(3); // skip over additional delay + sample rate
686 	}
687 	uint32 audioSize = stream->readUint16LE();
688 	stream->skip(audioSize);
689 	return true;
690 }
691 
getAudioStream() const692 Audio::AudioStream *AccessVIDMovieDecoder::StreamAudioTrack::getAudioStream() const {
693 	return _audioStream;
694 }
695 
playMovie(const Common::String & filename,const Common::Point & pos)696 bool AccessEngine::playMovie(const Common::String &filename, const Common::Point &pos) {
697 	AccessVIDMovieDecoder *videoDecoder = new AccessVIDMovieDecoder();
698 
699 	Common::Point framePos(pos.x, pos.y);
700 
701 	if (!videoDecoder->loadFile(filename)) {
702 		warning("AccessVIDMoviePlay: could not open '%s'", filename.c_str());
703 		return false;
704 	}
705 
706 	bool skipVideo = false;
707 
708 	_events->clearEvents();
709 	videoDecoder->start();
710 
711 	while (!shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) {
712 		if (videoDecoder->needsUpdate()) {
713 			const Graphics::Surface *frame = videoDecoder->decodeNextFrame();
714 
715 			if (frame) {
716 				_screen->blitFrom(*frame);
717 
718 				if (videoDecoder->hasDirtyPalette()) {
719 					const byte *palette = videoDecoder->getPalette();
720 					g_system->getPaletteManager()->setPalette(palette, 0, 256);
721 				}
722 
723 				_screen->update();
724 			}
725 		}
726 
727 		_events->pollEventsAndWait();
728 
729 		Common::KeyState keyState;
730 		if (_events->getKey(keyState)) {
731 			if (keyState.keycode == Common::KEYCODE_ESCAPE)
732 				skipVideo = true;
733 		}
734 	}
735 
736 	videoDecoder->close();
737 	delete videoDecoder;
738 
739 	return !skipVideo;
740 }
741 
742 } // End of namespace Access
743