Speech bubbles can point down right.
[scummvm-innocent.git] / graphics / video / coktelvideo / coktelvideo.cpp
blob39aeca07bdb44372ec482167587547ebdcc3f28c
1 /* ScummVM - Graphic Adventure Engine
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.
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.
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.
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.
21 * $URL$
22 * $Id$
26 #include "common/endian.h"
27 #include "common/system.h"
29 #include "graphics/dither.h"
30 #include "graphics/video/coktelvideo/coktelvideo.h"
31 #include "graphics/video/coktelvideo/indeo3.h"
33 namespace Graphics {
35 Imd::Imd() {
36 clear(false);
39 Imd::~Imd() {
40 clear();
43 uint32 Imd::getFeatures() const {
44 return _features;
47 uint16 Imd::getFlags() const {
48 return _flags;
51 int16 Imd::getX() const {
52 return _x;
55 int16 Imd::getY() const {
56 return _y;
59 int16 Imd::getWidth() const {
60 return _width;
63 int16 Imd::getHeight() const {
64 return _height;
67 uint16 Imd::getFramesCount() const {
68 return _framesCount;
71 uint16 Imd::getCurrentFrame() const {
72 return _curFrame;
75 int16 Imd::getFrameRate() const {
76 if (!_hasSound)
77 return _frameRate;
79 return 1000 / (_soundSliceLength >> 16);
82 uint32 Imd::getSyncLag() const {
83 return _skipFrames;
86 const byte *Imd::getPalette() const {
87 return _palette;
90 bool Imd::getFrameCoords(int16 frame,
91 int16 &x, int16 &y, int16 &width, int16 &height) {
93 return false;
96 bool Imd::hasExtraData(const char *fileName) const {
97 return false;
100 Common::MemoryReadStream *Imd::getExtraData(const char *fileName) {
101 return 0;
104 bool Imd::loadCoordinates() {
105 // Standard coordinates
106 if (_version >= 3) {
107 _stdX = _stream->readUint16LE();
108 if (_stdX > 1) {
109 warning("IMD: More than one standard coordinate quad found (%d)", _stdX);
110 return false;
112 if (_stdX != 0) {
113 _stdX = _stream->readSint16LE();
114 _stdY = _stream->readSint16LE();
115 _stdWidth = _stream->readSint16LE();
116 _stdHeight = _stream->readSint16LE();
117 _features |= kFeaturesStdCoords;
118 } else
119 _stdX = -1;
120 } else
121 _stdX = -1;
123 return true;
126 bool Imd::loadFrameTableOffsets(uint32 &framesPosPos, uint32 &framesCoordsPos) {
127 framesPosPos = 0;
128 framesCoordsPos = 0;
130 // Frame positions
131 if (_version >= 4) {
132 framesPosPos = _stream->readUint32LE();
133 if (framesPosPos != 0) {
134 _framesPos = new uint32[_framesCount];
135 assert(_framesPos);
136 _features |= kFeaturesFramesPos;
140 // Frame coordinates
141 if (_features & kFeaturesFrameCoords)
142 framesCoordsPos = _stream->readUint32LE();
144 return true;
147 bool Imd::assessVideoProperties() {
148 // Sizes of the frame data and extra video buffer
149 if (_features & kFeaturesDataSize) {
150 _frameDataSize = _stream->readUint16LE();
151 if (_frameDataSize == 0) {
152 _frameDataSize = _stream->readUint32LE();
153 _vidBufferSize = _stream->readUint32LE();
154 } else
155 _vidBufferSize = _stream->readUint16LE();
156 } else {
157 _frameDataSize = _width * _height + 500;
158 if (!(_flags & 0x100) || (_flags & 0x1000))
159 _vidBufferSize = _frameDataSize;
162 // Allocating working memory
163 _frameData = new byte[_frameDataSize + 500];
164 assert(_frameData);
165 memset(_frameData, 0, _frameDataSize + 500);
166 _vidBuffer = new byte[_vidBufferSize + 500];
167 assert(_vidBuffer);
168 memset(_vidBuffer, 0, _vidBufferSize + 500);
170 return true;
173 bool Imd::assessAudioProperties() {
174 if (_features & kFeaturesSound) {
175 _soundFreq = _stream->readSint16LE();
176 _soundSliceSize = _stream->readSint16LE();
177 _soundSlicesCount = _stream->readSint16LE();
179 if (_soundFreq < 0)
180 _soundFreq = -_soundFreq;
182 if (_soundSlicesCount < 0)
183 _soundSlicesCount = -_soundSlicesCount - 1;
185 if (_soundSlicesCount > 40) {
186 warning("Imd::load(): More than 40 sound slices found (%d)", _soundSlicesCount);
187 return false;
190 _soundSliceLength = (uint32) (((double) (1000 << 16)) /
191 ((double) _soundFreq / (double) _soundSliceSize));
192 _frameLength = _soundSliceLength >> 16;
194 _soundStage = 1;
195 _hasSound = true;
197 _audioStream = Audio::makeAppendableAudioStream(_soundFreq, 0);
198 } else
199 _frameLength = 1000 / _frameRate;
201 return true;
204 bool Imd::loadFrameTables(uint32 framesPosPos, uint32 framesCoordsPos) {
205 // Positions table
206 if (_framesPos) {
207 _stream->seek(framesPosPos, SEEK_SET);
208 for (int i = 0; i < _framesCount; i++)
209 _framesPos[i] = _stream->readUint32LE();
212 // Coordinates table
213 if (_features & kFeaturesFrameCoords) {
214 _stream->seek(framesCoordsPos, SEEK_SET);
215 _frameCoords = new Coord[_framesCount];
216 assert(_frameCoords);
217 for (int i = 0; i < _framesCount; i++) {
218 _frameCoords[i].left = _stream->readSint16LE();
219 _frameCoords[i].top = _stream->readSint16LE();
220 _frameCoords[i].right = _stream->readSint16LE();
221 _frameCoords[i].bottom = _stream->readSint16LE();
225 return true;
228 bool Imd::load(Common::SeekableReadStream &stream) {
229 unload();
231 _stream = &stream;
233 uint16 handle;
235 handle = _stream->readUint16LE();
236 _version = _stream->readByte();
238 // Version checking
239 if ((handle != 0) || (_version < 2)) {
240 warning("Imd::load(): Version incorrect (%d,%X)", handle, _version);
241 unload();
242 return false;
245 // Rest header
246 _features = _stream->readByte();
247 _framesCount = _stream->readUint16LE();
248 _x = _stream->readSint16LE();
249 _y = _stream->readSint16LE();
250 _width = _stream->readSint16LE();
251 _height = _stream->readSint16LE();
252 _flags = _stream->readUint16LE();
253 _firstFramePos = _stream->readUint16LE();
255 // IMDs always have video
256 _features |= kFeaturesVideo;
257 // IMDs always have palettes
258 _features |= kFeaturesPalette;
260 // Palette
261 _stream->read((byte *) _palette, 768);
263 if (!loadCoordinates()) {
264 unload();
265 return false;
268 uint32 framesPosPos, frameCoordsPos;
269 if (!loadFrameTableOffsets(framesPosPos, frameCoordsPos)) {
270 unload();
271 return false;
274 if (!assessAudioProperties()) {
275 unload();
276 return false;
279 if (!assessVideoProperties()) {
280 unload();
281 return false;
284 if (!loadFrameTables(framesPosPos, frameCoordsPos)) {
285 unload();
286 return false;
289 // Seek to the first frame
290 _stream->seek(_firstFramePos, SEEK_SET);
292 return true;
295 void Imd::unload() {
296 clear();
299 void Imd::setFrameRate(int16 frameRate) {
300 if (frameRate == 0)
301 frameRate = 1;
303 _frameRate = frameRate;
304 _frameLength = 1000 / _frameRate;
307 void Imd::setXY(int16 x, int16 y) {
308 // Adjusting the standard coordinates
309 if (_stdX != -1) {
310 if (x >= 0)
311 _stdX = _stdX - _x + x;
312 if (y >= 0)
313 _stdY = _stdY - _y + y;
316 // Going through the coordinate table as well
317 if (_frameCoords) {
318 for (int i = 0; i < _framesCount; i++) {
319 if (_frameCoords[i].left != -1) {
320 if (x >= 0) {
321 _frameCoords[i].left = _frameCoords[i].left - _x + x;
322 _frameCoords[i].right = _frameCoords[i].right - _x + x;
324 if (y >= 0) {
325 _frameCoords[i].top = _frameCoords[i].top - _y + y;
326 _frameCoords[i].bottom = _frameCoords[i].bottom - _y + y;
332 if (x >= 0)
333 _x = x;
334 if (y >= 0)
335 _y = y;
338 void Imd::setVideoMemory(byte *vidMem, uint16 width, uint16 height) {
339 deleteVidMem();
341 _hasOwnVidMem = false;
342 _vidMem = vidMem;
343 _vidMemWidth = width;
344 _vidMemHeight = height;
347 void Imd::setVideoMemory() {
348 deleteVidMem();
350 if ((_width > 0) && (_height > 0)) {
351 setXY(0, 0);
352 _hasOwnVidMem = true;
353 _vidMem = new byte[_width * _height];
354 _vidMemWidth = _width;
355 _vidMemHeight = _height;
357 memset(_vidMem, 0, _width * _height);
361 void Imd::setDoubleMode(bool doubleMode) {
364 void Imd::enableSound(Audio::Mixer &mixer) {
365 // Only possible on the first frame
366 if (_curFrame > 0)
367 return;
369 _mixer = &mixer;
370 _soundEnabled = true;
373 void Imd::disableSound() {
374 if (_audioStream) {
376 if (_soundStage == 2) {
377 _audioStream->finish();
378 _mixer->stopHandle(_audioHandle);
379 } else
380 delete _audioStream;
382 _audioStream = 0;
383 _soundStage = 0;
385 _soundEnabled = false;
386 _mixer = 0;
389 bool Imd::isSoundPlaying() const {
390 if (_audioStream && _mixer->isSoundHandleActive(_audioHandle))
391 return true;
393 return false;
396 void Imd::seekFrame(int32 frame, int16 whence, bool restart) {
397 if (!_stream)
398 // Nothing to do
399 return;
401 // Find the frame to which to seek
402 if (whence == SEEK_CUR)
403 frame += _curFrame;
404 else if (whence == SEEK_END)
405 frame = _framesCount - frame - 1;
406 else if (whence != SEEK_SET)
407 return;
409 if ((frame < 0) || (frame >= _framesCount) || (frame == _curFrame))
410 // Nothing to do
411 return;
413 // Try every possible way to find a file offset to that frame
414 uint32 framePos = 0;
415 if (frame == 0) {
416 framePos = _firstFramePos;
417 } else if (frame == 1) {
418 framePos = _firstFramePos;
419 _stream->seek(framePos, SEEK_SET);
420 framePos += _stream->readUint16LE() + 4;
421 } else if (_framesPos) {
422 framePos = _framesPos[frame];
423 } else if (restart && (_soundStage == 0)) {
424 for (int i = ((frame > _curFrame) ? _curFrame : 0); i <= frame; i++)
425 processFrame(i);
426 return;
427 } else
428 error("Imd::seekFrame(): Frame %d is not directly accessible", frame);
430 // Seek
431 _stream->seek(framePos);
432 _curFrame = frame;
435 CoktelVideo::State Imd::nextFrame() {
436 return processFrame(_curFrame);
439 void Imd::waitEndFrame() {
440 if (_soundEnabled && _hasSound) {;
441 if (_soundStage != 2)
442 return;
444 if (_skipFrames == 0) {
445 int32 waitTime = (int16) (((_curFrame * _soundSliceLength) -
446 (_mixer->getSoundElapsedTime(_audioHandle) << 16)) >> 16);
448 if (waitTime < 0) {
449 _skipFrames = -waitTime / (_soundSliceLength >> 16);
450 warning("Video A/V sync broken, skipping %d frame(s)", _skipFrames + 1);
451 } else if (waitTime > 0)
452 g_system->delayMillis(waitTime);
454 } else
455 _skipFrames--;
456 } else
457 g_system->delayMillis(_frameLength);
460 void Imd::copyCurrentFrame(byte *dest,
461 uint16 left, uint16 top, uint16 width, uint16 height,
462 uint16 x, uint16 y, uint16 pitch, int16 transp) {
464 if (!_vidMem)
465 return;
467 if (((left + width) > _width) || ((top + height) > _height))
468 return;
470 dest += pitch * y;
471 byte *vidMem = _vidMem + _width * top;
473 if (transp < 0) {
474 // No transparency
475 if ((x > 0) || (left > 0) || (pitch != _width) || (width != _width)) {
476 // Copy row-by-row
477 for (int i = 0; i < height; i++) {
478 byte *d = dest + x;
479 byte *s = vidMem + left;
481 memcpy(d, s, width);
483 dest += pitch;
484 vidMem += _width;
486 } else
487 // Dimensions fit, copy everything at once
488 memcpy(dest, vidMem, width * height);
490 return;
493 for (int i = 0; i < height; i++) {
494 byte *d = dest + x;
495 byte *s = vidMem + left;
497 for (int j = 0; j < width; j++) {
498 if (*s != transp)
499 *d = *s;
501 s++;
502 d++;
505 dest += pitch;
506 vidMem += _width;
511 void Imd::deleteVidMem(bool del) {
512 if (del) {
513 if (_hasOwnVidMem)
514 delete[] _vidMem;
517 _hasOwnVidMem = false;
518 _vidMem = 0;
519 _vidMemWidth = 0;
520 _vidMemHeight = 0;
523 void Imd::clear(bool del) {
524 if (del) {
525 delete[] _framesPos;
526 delete[] _frameCoords;
527 delete[] _frameData;
528 delete[] _vidBuffer;
530 disableSound();
533 _stream = 0;
535 _version = 0;
536 _features = 0;
537 _flags = 0;
539 _x = _y = _width = _height = 0;
540 _stdX = _stdY = _stdWidth = _stdHeight = 0;
542 _framesCount = _curFrame = 0;
544 _framesPos = 0;
545 _firstFramePos = 0;
546 _frameCoords = 0;
548 _frameDataSize = _vidBufferSize = 0;
549 _frameData = _vidBuffer = 0;
550 _frameDataLen = 0;
552 memset(_palette, 0, 768);
554 deleteVidMem(del);
556 _hasSound = false;
557 _soundEnabled = false;
558 _soundStage = 0;
559 _skipFrames = 0;
561 _soundFlags = 0;
562 _soundFreq = 0;
563 _soundSliceSize = 0;
564 _soundSlicesCount = 0;
565 _soundSliceLength = 0;
566 _audioStream = 0;
568 _frameRate = 12;
569 _frameLength = 0;
570 _lastFrameTime = 0;
573 void Imd::nextSoundSlice(bool hasNextCmd) {
574 if (hasNextCmd || !_soundEnabled) {
575 _stream->seek(_soundSliceSize, SEEK_CUR);
576 return;
579 byte *soundBuf = new byte[_soundSliceSize];
581 _stream->read(soundBuf, _soundSliceSize);
582 unsignedToSigned(soundBuf, _soundSliceSize);
584 _audioStream->queueBuffer(soundBuf, _soundSliceSize);
587 bool Imd::initialSoundSlice(bool hasNextCmd) {
588 int dataLength = _soundSliceSize * _soundSlicesCount;
590 if (hasNextCmd || !_soundEnabled) {
591 _stream->seek(dataLength, SEEK_CUR);
592 return false;
595 byte *soundBuf = new byte[dataLength];
597 _stream->read(soundBuf, dataLength);
598 unsignedToSigned(soundBuf, dataLength);
600 _audioStream->queueBuffer(soundBuf, dataLength);
602 return _soundStage == 1;
605 void Imd::emptySoundSlice(bool hasNextCmd) {
606 if (hasNextCmd || !_soundEnabled)
607 return;
609 byte *soundBuf = new byte[_soundSliceSize];
611 memset(soundBuf, 0, _soundSliceSize);
613 _audioStream->queueBuffer(soundBuf, _soundSliceSize);
616 void Imd::videoData(uint32 size, State &state) {
617 _stream->read(_frameData, size);
618 _frameDataLen = size;
620 if (_vidMemWidth <= state.right) {
621 state.left = 0;
622 state.right -= state.left;
624 if (_vidMemWidth <= state.right)
625 state.right = _vidMemWidth - 1;
626 if (_vidMemHeight <= state.bottom) {
627 state.top = 0;
628 state.bottom -= state.top;
630 if (_vidMemHeight <= state.bottom)
631 state.bottom = _vidMemHeight -1;
633 state.flags |= renderFrame(state.left, state.top, state.right, state.bottom);
634 state.flags |= _frameData[0];
637 void Imd::calcFrameCoords(uint16 frame, State &state) {
638 if (_stdX != -1) {
639 state.left = _stdX;
640 state.top = _stdY;
641 state.right = _stdWidth + state.left - 1;
642 state.bottom = _stdHeight + state.top - 1;
643 state.flags |= kStateStdCoords;
645 if (_frameCoords &&
646 (_frameCoords[frame].left != -1)) {
647 state.left = _frameCoords[frame].left;
648 state.top = _frameCoords[frame].top;
649 state.right = _frameCoords[frame].right;
650 state.bottom = _frameCoords[frame].bottom;
651 state.flags |= kStateFrameCoords;
655 CoktelVideo::State Imd::processFrame(uint16 frame) {
656 State state;
657 uint32 cmd = 0;
658 bool hasNextCmd = false;
659 bool startSound = false;
661 if (!_stream || (frame >= _framesCount)) {
662 state.flags = kStateBreak;
663 return state;
666 if (frame != _curFrame) {
667 state.flags |= kStateSeeked;
668 seekFrame(frame);
671 if (!_vidMem)
672 setVideoMemory();
674 state.left = _x;
675 state.top = _y;
676 state.right = _width + state.left - 1;
677 state.bottom = _height + state.top - 1;
679 do {
680 if (frame != 0)
681 calcFrameCoords(frame, state);
683 cmd = _stream->readUint16LE();
685 if ((cmd & kCommandBreakMask) == kCommandBreak) {
686 // Flow control
688 if (cmd == kCommandBreak) {
689 _stream->seek(2, SEEK_CUR);
690 cmd = _stream->readUint16LE();
693 // Break
694 if (cmd == kCommandBreakSkip0) {
695 state.flags = kStateBreak;
696 continue;
697 } else if (cmd == kCommandBreakSkip16) {
698 cmd = _stream->readUint16LE();
699 _stream->seek(cmd, SEEK_CUR);
700 state.flags = kStateBreak;
701 continue;
702 } else if (cmd == kCommandBreakSkip32) {
703 cmd = _stream->readUint32LE();
704 _stream->seek(cmd, SEEK_CUR);
705 state.flags = kStateBreak;
706 continue;
710 // Audio
711 if (_soundStage != 0) {
712 if (cmd == kCommandNextSound) {
714 nextSoundSlice(hasNextCmd);
715 cmd = _stream->readUint16LE();
717 } else if (cmd == kCommandStartSound) {
719 startSound = initialSoundSlice(hasNextCmd);
720 cmd = _stream->readUint16LE();
722 } else
723 emptySoundSlice(hasNextCmd);
726 // Set palette
727 if (cmd == kCommandPalette) {
728 _stream->seek(2, SEEK_CUR);
729 state.flags |= kStatePalette;
731 _stream->read(_palette, 768);
732 cmd = _stream->readUint16LE();
735 hasNextCmd = false;
737 if (cmd == kCommandJump) {
738 // Jump to frame
740 frame = _stream->readSint16LE();
741 if (_framesPos) {
742 _curFrame = frame;
743 _stream->seek(_framesPos[frame], SEEK_SET);
745 hasNextCmd = true;
746 state.flags |= kStateJump;
749 } else if (cmd == kCommandVideoData) {
750 uint32 size = _stream->readUint32LE() + 2;
752 videoData(size, state);
754 state.flags |= 1;
756 } else if (cmd != 0) {
757 uint32 size = cmd + 2;
759 videoData(size, state);
761 } else
762 state.flags |= kStateNoVideoData;
764 } while (hasNextCmd);
766 if (startSound && _soundEnabled) {
767 _mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_audioHandle, _audioStream);
768 _skipFrames = 0;
769 _soundStage = 2;
772 _curFrame++;
773 if ((_curFrame == _framesCount) && (_soundStage == 2)) {
774 _audioStream->finish();
775 _mixer->stopHandle(_audioHandle);
776 _audioStream = 0;
777 _soundStage = 0;
780 _lastFrameTime = g_system->getMillis();
781 return state;
784 // A whole, completely filled block
785 void Imd::renderBlockWhole(byte *dest, const byte *src, int16 width, int16 height,
786 int16 destWidth, int16 destHeight) {
788 int16 w = MIN(width, destWidth);
789 int16 h = MIN(height, destHeight);
791 for (int i = 0; i < h; i++) {
792 memcpy(dest, src, w);
794 src += width;
795 dest += destWidth;
799 // A quarter-wide whole, completely filled block
800 void Imd::renderBlockWhole4X(byte *dest, const byte *src, int16 width, int16 height,
801 int16 destWidth, int16 destHeight) {
803 for (int i = 0; i < height; i++) {
804 byte *destBak = dest;
806 for (int j = 0; j < width; j += 4, dest += 4, src++)
807 memset(dest, *src, 4);
809 dest = destBak + destWidth;
813 // A half-high whole, completely filled block
814 void Imd::renderBlockWhole2Y(byte *dest, const byte *src, int16 width, int16 height,
815 int16 destWidth, int16 destHeight) {
817 while (height > 1) {
818 memcpy(dest , src, width);
819 memcpy(dest + destWidth, src, width);
821 height -= 2;
822 dest += 2 * destWidth;
823 src += width;
826 if (height == 1)
827 memcpy(dest, src, width);
830 // A sparse block
831 void Imd::renderBlockSparse(byte *dest, const byte *src, int16 width, int16 height,
832 int16 destWidth, int16 destHeight) {
834 for (int i = 0; i < height; i++) {
835 byte *destBak = dest;
836 uint16 pixWritten = 0;
838 while (pixWritten < width) {
839 uint16 pixCount = *src++;
841 if (pixCount & 0x80) { // Data
842 pixCount = MIN((pixCount & 0x7F) + 1, width - pixWritten);
843 memcpy(dest, src, pixCount);
845 pixWritten += pixCount;
846 dest += pixCount;
847 src += pixCount;
848 } else { // "Hole"
849 pixWritten += pixCount + 1;
850 dest += pixCount + 1;
855 dest = destBak + destWidth;
859 // A half-high sparse block
860 void Imd::renderBlockSparse2Y(byte *dest, const byte *src, int16 width, int16 height,
861 int16 destWidth, int16 destHeight) {
863 for (int i = 0; i < height; i += 2) {
864 byte *destBak = dest;
865 uint16 pixWritten = 0;
867 while (pixWritten < width) {
868 uint16 pixCount = *src++;
870 if (pixCount & 0x80) { // Data
871 pixCount = MIN((pixCount & 0x7F) + 1, width - pixWritten);
872 memcpy(dest , src, pixCount);
873 memcpy(dest + destWidth, src, pixCount);
875 pixWritten += pixCount;
876 dest += pixCount;
877 src += pixCount;
878 } else { // "Hole"
879 pixWritten += pixCount + 1;
880 dest += pixCount + 1;
885 dest = destBak + destWidth;
889 uint32 Imd::renderFrame(int16 left, int16 top, int16 right, int16 bottom) {
890 if (!_frameData || !_vidMem || (_width <= 0) || (_height <= 0))
891 return 0;
893 uint32 retVal = 0;
895 int16 width = right - left + 1;
896 int16 height = bottom - top + 1;
897 int16 sW = _vidMemWidth;
898 int16 sH = _vidMemHeight;
900 byte *dataPtr = _frameData;
901 byte *imdVidMem = _vidMem + sW * top + left;
902 byte *srcPtr;
904 uint8 type = *dataPtr++;
906 if (type & 0x10) { // Palette data
907 // One byte index
908 int index = *dataPtr++;
909 // 16 entries with each 3 bytes (RGB)
910 memcpy(_palette + index * 3, dataPtr, MIN((255 - index) * 3, 48));
912 retVal = kStatePalette;
913 dataPtr += 48;
914 type ^= 0x10;
917 srcPtr = dataPtr;
919 if (type & 0x80) {
920 // Frame data is compressed
922 srcPtr = _vidBuffer;
923 type &= 0x7F;
924 if ((type == 2) && (width == sW)) {
925 // Directly uncompress onto the video surface
926 deLZ77(imdVidMem, dataPtr);
927 return retVal;
928 } else
929 deLZ77(srcPtr, dataPtr);
932 // Evaluate the block type
933 if (type == 0x01)
934 renderBlockSparse (imdVidMem, srcPtr, width, height, sW, sH);
935 else if (type == 0x02)
936 renderBlockWhole (imdVidMem, srcPtr, width, height, sW, sH);
937 else if (type == 0x42)
938 renderBlockWhole4X (imdVidMem, srcPtr, width, height, sW, sH);
939 else if ((type & 0x0F) == 0x02)
940 renderBlockWhole2Y (imdVidMem, srcPtr, width, height, sW, sH);
941 else
942 renderBlockSparse2Y(imdVidMem, srcPtr, width, height, sW, sH);
944 return retVal;
947 void Imd::deLZ77(byte *dest, byte *src) {
948 int i;
949 byte buf[4370];
950 uint16 chunkLength;
951 uint32 frameLength;
952 uint16 bufPos1;
953 uint16 bufPos2;
954 uint16 tmp;
955 uint8 chunkBitField;
956 uint8 chunkCount;
957 bool mode;
959 frameLength = READ_LE_UINT32(src);
960 src += 4;
962 if ((READ_LE_UINT16(src) == 0x1234) && (READ_LE_UINT16(src + 2) == 0x5678)) {
963 src += 4;
964 bufPos1 = 273;
965 mode = 1; // 123Ch (cmp al, 12h)
966 } else {
967 bufPos1 = 4078;
968 mode = 0; // 275h (jnz +2)
971 memset(buf, 32, bufPos1);
972 chunkCount = 1;
973 chunkBitField = 0;
975 while (frameLength > 0) {
976 chunkCount--;
977 if (chunkCount == 0) {
978 tmp = *src++;
979 chunkCount = 8;
980 chunkBitField = tmp;
982 if (chunkBitField % 2) {
983 chunkBitField >>= 1;
984 buf[bufPos1] = *src;
985 *dest++ = *src++;
986 bufPos1 = (bufPos1 + 1) % 4096;
987 frameLength--;
988 continue;
990 chunkBitField >>= 1;
992 tmp = READ_LE_UINT16(src);
993 src += 2;
994 chunkLength = ((tmp & 0xF00) >> 8) + 3;
996 if ((mode && ((chunkLength & 0xFF) == 0x12)) ||
997 (!mode && (chunkLength == 0)))
998 chunkLength = *src++ + 0x12;
1000 bufPos2 = (tmp & 0xFF) + ((tmp >> 4) & 0x0F00);
1001 if (((tmp + chunkLength) >= 4096) ||
1002 ((chunkLength + bufPos1) >= 4096)) {
1004 for (i = 0; i < chunkLength; i++, dest++) {
1005 *dest = buf[bufPos2];
1006 buf[bufPos1] = buf[bufPos2];
1007 bufPos1 = (bufPos1 + 1) % 4096;
1008 bufPos2 = (bufPos2 + 1) % 4096;
1011 } else if (((tmp + chunkLength) < bufPos1) ||
1012 ((chunkLength + bufPos1) < bufPos2)) {
1014 memcpy(dest, buf + bufPos2, chunkLength);
1015 memmove(buf + bufPos1, buf + bufPos2, chunkLength);
1017 dest += chunkLength;
1018 bufPos1 += chunkLength;
1019 bufPos2 += chunkLength;
1021 } else {
1023 for (i = 0; i < chunkLength; i++, dest++, bufPos1++, bufPos2++) {
1024 *dest = buf[bufPos2];
1025 buf[bufPos1] = buf[bufPos2];
1029 frameLength -= chunkLength;
1034 inline void Imd::unsignedToSigned(byte *buffer, int length) {
1035 while (length-- > 0) *buffer++ ^= 0x80;
1039 Vmd::ExtraData::ExtraData() {
1040 memset(name, 0, 16);
1042 offset = 0;
1043 size = 0;
1044 realSize = 0;
1048 Vmd::Part::Part() {
1049 type = kPartTypeSeparator;
1050 field_1 = 0;
1051 field_E = 0;
1052 size = 0;
1053 left = 0;
1054 top = 0;
1055 right = 0;
1056 bottom = 0;
1057 id = 0;
1058 flags = 0;
1062 Vmd::Frame::Frame() {
1063 parts = 0;
1064 offset = 0;
1067 Vmd::Frame::~Frame() {
1068 delete[] parts;
1072 const uint16 Vmd::_tableDPCM[128] = {
1073 0x0000, 0x0008, 0x0010, 0x0020, 0x0030, 0x0040, 0x0050, 0x0060, 0x0070, 0x0080,
1074 0x0090, 0x00A0, 0x00B0, 0x00C0, 0x00D0, 0x00E0, 0x00F0, 0x0100, 0x0110, 0x0120,
1075 0x0130, 0x0140, 0x0150, 0x0160, 0x0170, 0x0180, 0x0190, 0x01A0, 0x01B0, 0x01C0,
1076 0x01D0, 0x01E0, 0x01F0, 0x0200, 0x0208, 0x0210, 0x0218, 0x0220, 0x0228, 0x0230,
1077 0x0238, 0x0240, 0x0248, 0x0250, 0x0258, 0x0260, 0x0268, 0x0270, 0x0278, 0x0280,
1078 0x0288, 0x0290, 0x0298, 0x02A0, 0x02A8, 0x02B0, 0x02B8, 0x02C0, 0x02C8, 0x02D0,
1079 0x02D8, 0x02E0, 0x02E8, 0x02F0, 0x02F8, 0x0300, 0x0308, 0x0310, 0x0318, 0x0320,
1080 0x0328, 0x0330, 0x0338, 0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370,
1081 0x0378, 0x0380, 0x0388, 0x0390, 0x0398, 0x03A0, 0x03A8, 0x03B0, 0x03B8, 0x03C0,
1082 0x03C8, 0x03D0, 0x03D8, 0x03E0, 0x03E8, 0x03F0, 0x03F8, 0x0400, 0x0440, 0x0480,
1083 0x04C0, 0x0500, 0x0540, 0x0580, 0x05C0, 0x0600, 0x0640, 0x0680, 0x06C0, 0x0700,
1084 0x0740, 0x0780, 0x07C0, 0x0800, 0x0900, 0x0A00, 0x0B00, 0x0C00, 0x0D00, 0x0E00,
1085 0x0F00, 0x1000, 0x1400, 0x1800, 0x1C00, 0x2000, 0x3000, 0x4000
1088 const int32 Vmd::_tableADPCM[] = {
1089 7, 8, 9, 10, 11, 12, 13, 14,
1090 16, 17, 19, 21, 23, 25, 28, 31,
1091 34, 37, 41, 45, 50, 55, 60, 66,
1092 73, 80, 88, 97, 107, 118, 130, 143,
1093 157, 173, 190, 209, 230, 253, 279, 307,
1094 337, 371, 408, 449, 494, 544, 598, 658,
1095 724, 796, 876, 963, 1060, 1166, 1282, 1411,
1096 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
1097 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
1098 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
1099 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
1100 32767, 0
1103 const int32 Vmd::_tableADPCMStep[] = {
1104 -1, -1, -1, -1, 2, 4, 6, 8,
1105 -1, -1, -1, -1, 2, 4, 6, 8
1108 Vmd::Vmd(Graphics::PaletteLUT *palLUT) : _palLUT(palLUT) {
1109 clear(false);
1112 Vmd::~Vmd() {
1113 clear();
1116 bool Vmd::assessVideoProperties() {
1117 if (_bytesPerPixel > 1)
1118 _features |= kFeaturesFullColor;
1119 else
1120 _features |= kFeaturesPalette;
1122 if ((_version & 2) && !(_version & 8)) {
1123 _externalCodec = true;
1124 _frameDataSize = _vidBufferSize = 0;
1125 } else
1126 _externalCodec = false;
1128 if (_externalCodec) {
1129 if (_videoCodec == MKID_BE('iv32')) {
1130 _features &= ~kFeaturesPalette;
1131 _features |= kFeaturesFullColor;
1132 _codecIndeo3 = new Indeo3(_width, _height, _palLUT);
1133 } else {
1134 char *fourcc = (char *) &_videoCodec;
1136 warning("Vmd::assessVideoProperties(): Unknow video codec FourCC \'%c%c%c%c\'",
1137 fourcc[3], fourcc[2], fourcc[1], fourcc[0]);
1138 return false;
1142 _preScaleX = 1;
1143 _postScaleX = 1;
1145 if (_externalCodec)
1146 _blitMode = 0;
1147 else if (_bytesPerPixel == 1)
1148 _blitMode = 0;
1149 else if ((_bytesPerPixel == 2) || (_bytesPerPixel == 3)) {
1150 int n = (_flags & 0x80) ? 2 : 3;
1152 _blitMode = n - 1;
1154 if (_bytesPerPixel == 2) {
1155 _preScaleX = n;
1156 _postScaleX = 1;
1157 } else if (_bytesPerPixel == 3) {
1158 _preScaleX = 1;
1159 _postScaleX = n;
1162 _bytesPerPixel = n;
1165 _scaleExternalX = 1;
1166 if (!_externalCodec && !(_flags & 0x1000))
1167 _scaleExternalX = _bytesPerPixel;
1169 if (_hasVideo) {
1170 if ((_frameDataSize == 0) || (_frameDataSize > 1048576))
1171 _frameDataSize = _width * _height + 1000;
1172 if ((_vidBufferSize == 0) || (_vidBufferSize > 1048576))
1173 _vidBufferSize = _frameDataSize;
1175 _frameData = new byte[_frameDataSize];
1176 assert(_frameData);
1177 memset(_frameData, 0, _frameDataSize);
1179 _vidBuffer = new byte[_vidBufferSize];
1180 assert(_vidBuffer);
1181 memset(_vidBuffer, 0, _vidBufferSize);
1183 if (_blitMode > 0) {
1184 _vidMemBuffer = new byte[_bytesPerPixel * (_width * _height + 1000)];
1185 memset(_vidMemBuffer, 0, _bytesPerPixel * (_width * _height + 1000));
1189 if (_externalCodec && _codecIndeo3)
1190 _features |= kFeaturesSupportsDouble;
1192 return true;
1195 bool Vmd::assessAudioProperties() {
1196 bool supportedFormat = true;
1198 _features |= kFeaturesSound;
1200 _soundStereo = (_soundFlags & 0x8000) ? 1 : ((_soundFlags & 0x200) ? 2 : 0);
1202 if (_soundSliceSize < 0) {
1203 _soundBytesPerSample = 2;
1204 _soundSliceSize = -_soundSliceSize;
1206 if (_soundFlags & 0x10) {
1207 _audioFormat = kAudioFormat16bitADPCM;
1208 _soundHeaderSize = 3;
1209 _soundDataSize = _soundSliceSize >> 1;
1211 if (_soundStereo > 0)
1212 supportedFormat = false;
1214 } else {
1215 _audioFormat = kAudioFormat16bitDPCM;
1216 _soundHeaderSize = 1;
1217 _soundDataSize = _soundSliceSize;
1219 if (_soundStereo == 1) {
1220 supportedFormat = false;
1221 } else if (_soundStereo == 2) {
1222 _soundDataSize = 2 * _soundDataSize + 2;
1223 _soundHeaderSize = 4;
1227 } else {
1228 _soundBytesPerSample = 1;
1229 _audioFormat = kAudioFormat8bitDirect;
1230 _soundHeaderSize = 0;
1231 _soundDataSize = _soundSliceSize;
1233 if (_soundStereo > 0)
1234 supportedFormat = false;
1237 if (!supportedFormat) {
1238 warning("Vmd::assessAudioProperties(): Unsupported audio format: %d bits, encoding %d, stereo %d",
1239 _soundBytesPerSample * 8, _audioFormat, _soundStereo);
1240 return false;
1243 _soundSliceLength = (uint32) (((double) (1000 << 16)) /
1244 ((double) _soundFreq / (double) _soundSliceSize));
1245 _frameLength = _soundSliceLength >> 16;
1247 _soundStage = 1;
1249 uint32 flags = 0;
1251 flags |= (_soundBytesPerSample == 2) ? Audio::Mixer::FLAG_16BITS : 0;
1252 flags |= (_soundStereo > 0) ? Audio::Mixer::FLAG_STEREO : 0;
1254 _audioStream = Audio::makeAppendableAudioStream(_soundFreq, flags);
1256 return true;
1259 void Vmd::readFrameTable(int &numExtraData) {
1260 numExtraData = 0;
1262 _stream->seek(_frameInfoOffset);
1263 _frames = new Frame[_framesCount];
1264 for (uint16 i = 0; i < _framesCount; i++) {
1265 _frames[i].parts = new Part[_partsPerFrame];
1266 _stream->skip(2); // Unknown
1267 _frames[i].offset = _stream->readUint32LE();
1270 for (uint16 i = 0; i < _framesCount; i++) {
1271 bool separator = false;
1273 for (uint16 j = 0; j < _partsPerFrame; j++) {
1275 _frames[i].parts[j].type = (PartType) _stream->readByte();
1276 _frames[i].parts[j].field_1 = _stream->readByte();
1277 _frames[i].parts[j].size = _stream->readUint32LE();
1279 if (_frames[i].parts[j].type == kPartTypeAudio) {
1281 _frames[i].parts[j].flags = _stream->readByte();
1282 _stream->skip(9); // Unknown
1284 } else if (_frames[i].parts[j].type == kPartTypeVideo) {
1286 _frames[i].parts[j].left = _stream->readUint16LE();
1287 _frames[i].parts[j].top = _stream->readUint16LE();
1288 _frames[i].parts[j].right = _stream->readUint16LE();
1289 _frames[i].parts[j].bottom = _stream->readUint16LE();
1290 _frames[i].parts[j].field_E = _stream->readByte();
1291 _frames[i].parts[j].flags = _stream->readByte();
1293 } else if (_frames[i].parts[j].type == kPartTypeSpeech) {
1294 _frames[i].parts[j].id = _stream->readUint16LE();
1295 // Speech text file name
1296 _stream->skip(8);
1297 } else if (_frames[i].parts[j].type == kPartTypeExtraData) {
1298 if (!separator)
1299 numExtraData++;
1300 _stream->skip(10);
1301 } else if (_frames[i].parts[j].type == kPartTypeSeparator) {
1302 separator = true;
1303 _stream->skip(10);
1304 } else {
1305 // Unknow type
1306 _stream->skip(10);
1313 void Vmd::readExtraData() {
1314 uint32 ssize = _stream->size();
1315 for (uint16 i = 0; i < _framesCount; i++) {
1316 _stream->seek(_frames[i].offset);
1318 for (uint16 j = 0; j < _partsPerFrame; j++) {
1319 if (_frames[i].parts[j].type == kPartTypeSeparator)
1320 break;
1322 if (_frames[i].parts[j].type == kPartTypeExtraData) {
1323 ExtraData data;
1325 data.offset = _stream->pos() + 20;
1326 data.size = _frames[i].parts[j].size;
1327 data.realSize = _stream->readUint32LE();
1329 _stream->read(data.name, 16);
1330 data.name[15] = '\0';
1332 _stream->skip(_frames[i].parts[j].size - 20);
1334 if ((((uint32) data.realSize) >= ssize) || (data.name[0] == 0))
1335 continue;
1337 _extraData.push_back(data);
1339 } else
1340 _stream->skip(_frames[i].parts[j].size);
1345 bool Vmd::load(Common::SeekableReadStream &stream) {
1346 unload();
1348 _stream = &stream;
1350 uint16 headerLength;
1351 uint16 handle;
1353 headerLength = _stream->readUint16LE();
1354 handle = _stream->readUint16LE();
1355 _version = _stream->readUint16LE();
1357 bool readPalette;
1359 // Version checking
1360 if (headerLength == 50) {
1361 // Newer version, used in Addy 5 upwards
1362 warning("Vmd::load(): TODO: Addy 5 videos");
1363 readPalette = false;
1364 } else if (headerLength == 814) {
1365 // Old version
1366 readPalette = true;
1367 } else {
1368 warning("Vmd::load(): Version incorrect (%d, %d, %d)", headerLength, handle, _version);
1369 unload();
1370 return false;
1373 _framesCount = _stream->readUint16LE();
1375 _x = _stream->readSint16LE();
1376 _y = _stream->readSint16LE();
1377 _width = _stream->readSint16LE();
1378 _height = _stream->readSint16LE();
1380 if ((_width != 0) && (_height != 0)) {
1382 _hasVideo = true;
1383 _features |= kFeaturesVideo;
1385 } else
1386 _hasVideo = false;
1388 _bytesPerPixel = 1;
1389 if (_version & 4)
1390 _bytesPerPixel = handle + 1;
1392 if (_bytesPerPixel > 3) {
1393 warning("Vmd::load(): Requested %d bytes per pixel (%d, %d, %d)", _bytesPerPixel, headerLength, handle, _version);
1394 unload();
1395 return false;
1398 _flags = _stream->readUint16LE();
1400 _partsPerFrame = _stream->readUint16LE();
1401 _firstFramePos = _stream->readUint32LE();
1403 _videoCodec = _stream->readUint32BE();
1405 if (readPalette)
1406 _stream->read((byte *) _palette, 768);
1408 _frameDataSize = _stream->readUint32LE();
1409 _vidBufferSize = _stream->readUint32LE();
1411 _doubleMode = false;
1413 if (_hasVideo) {
1414 if (!assessVideoProperties()) {
1415 unload();
1416 return false;
1420 _soundFreq = _stream->readSint16LE();
1421 _soundSliceSize = _stream->readSint16LE();
1422 _soundSlicesCount = _stream->readSint16LE();
1423 _soundFlags = _stream->readUint16LE();
1425 _hasSound = (_soundFreq != 0);
1427 if (_hasSound) {
1428 if (!assessAudioProperties()) {
1429 unload();
1430 return false;
1432 } else
1433 _frameLength = 1000 / _frameRate;
1435 _frameInfoOffset = _stream->readUint32LE();
1437 int numExtraData;
1438 readFrameTable(numExtraData);
1440 _stream->seek(_firstFramePos);
1442 if (numExtraData == 0)
1443 return true;
1445 _extraData.reserve(numExtraData);
1446 readExtraData();
1448 _stream->seek(_firstFramePos);
1449 return true;
1452 void Vmd::unload() {
1453 clear();
1456 int16 Vmd::getWidth() const {
1457 return preScaleX(_width);
1460 void Vmd::setXY(int16 x, int16 y) {
1462 x *= _scaleExternalX;
1464 for (int i = 0; i < _framesCount; i++) {
1465 for (int j = 0; j < _partsPerFrame; j++) {
1467 if (_frames[i].parts[j].type == kPartTypeVideo) {
1468 if (x >= 0) {
1469 _frames[i].parts[j].left = _frames[i].parts[j].left - _x + x;
1470 _frames[i].parts[j].right = _frames[i].parts[j].right - _x + x;
1472 if (y >= 0) {
1473 _frames[i].parts[j].top = _frames[i].parts[j].top - _y + y;
1474 _frames[i].parts[j].bottom = _frames[i].parts[j].bottom - _y + y;
1481 if (x >= 0)
1482 _x = x;
1483 if (y >= 0)
1484 _y = y;
1487 void Vmd::setDoubleMode(bool doubleMode) {
1488 if (_doubleMode == doubleMode)
1489 return;
1491 if (_vidBuffer) {
1492 delete[] _vidBuffer;
1494 if (doubleMode)
1495 _vidBufferSize *= 4;
1496 else
1497 _vidBufferSize /= 4;
1499 _vidBuffer = new byte[_vidBufferSize];
1500 assert(_vidBuffer);
1501 memset(_vidBuffer, 0, _vidBufferSize);
1505 if (_codecIndeo3) {
1506 delete _codecIndeo3;
1508 _codecIndeo3 = new Indeo3(_width * (doubleMode ? 2 : 1),
1509 _height * (doubleMode ? 2 : 1), _palLUT);
1512 _doubleMode = doubleMode;
1515 void Vmd::seekFrame(int32 frame, int16 whence, bool restart) {
1516 if (!_stream)
1517 // Nothing to do
1518 return;
1520 // Find the frame to which to seek
1521 if (whence == SEEK_CUR)
1522 frame += _curFrame;
1523 else if (whence == SEEK_END)
1524 frame = _framesCount - frame - 1;
1525 else if (whence != SEEK_SET)
1526 return;
1528 if ((frame < 0) || (frame >= _framesCount))
1529 // Nothing to do
1530 return;
1532 // Restart sound
1533 if (_hasSound && (frame == 0) && (_soundStage == 0) && !_audioStream) {
1534 _soundStage = 1;
1535 _audioStream = Audio::makeAppendableAudioStream(_soundFreq,
1536 (_soundBytesPerSample == 2) ? Audio::Mixer::FLAG_16BITS : 0);
1539 // Seek
1540 _stream->seek(_frames[frame].offset);
1541 _curFrame = frame;
1544 CoktelVideo::State Vmd::nextFrame() {
1545 State state;
1547 state = processFrame(_curFrame);
1548 _curFrame++;
1549 return state;
1552 void Vmd::clear(bool del) {
1553 Imd::clear(del);
1555 if (del) {
1556 delete _codecIndeo3;
1557 delete[] _frames;
1558 delete[] _vidMemBuffer;
1561 _hasVideo = true;
1562 _videoCodec = 0;
1564 _codecIndeo3 = 0;
1566 _partsPerFrame = 0;
1567 _frames = 0;
1569 _extraData.clear();
1571 _soundBytesPerSample = 1;
1572 _soundStereo = 0;
1573 _soundHeaderSize = 0;
1574 _soundDataSize = 0;
1575 _audioFormat = kAudioFormat8bitDirect;
1577 _externalCodec = false;
1578 _doubleMode = false;
1579 _blitMode = 0;
1580 _bytesPerPixel = 1;
1581 _preScaleX = 1;
1582 _postScaleX = 1;
1583 _scaleExternalX = 1;
1584 _vidMemBuffer = 0;
1587 CoktelVideo::State Vmd::processFrame(uint16 frame) {
1588 State state;
1589 bool startSound = false;
1591 seekFrame(frame);
1593 state.flags |= kStateNoVideoData;
1594 state.left = 0x7FFF;
1595 state.top = 0x7FFF;
1596 state.right = 0;
1597 state.bottom = 0;
1599 if (!_vidMem)
1600 setVideoMemory();
1602 for (uint16 i = 0; (i < _partsPerFrame) && (frame < _framesCount); i++) {
1603 uint32 pos = _stream->pos();
1605 Part &part = _frames[frame].parts[i];
1607 if (part.type == kPartTypeAudio) {
1608 // Next sound slice data
1609 if (part.flags == 1) {
1611 if (_soundEnabled) {
1612 filledSoundSlice(part.size);
1614 if (_soundStage == 1)
1615 startSound = true;
1617 } else
1618 _stream->skip(part.size);
1620 // Initial sound data (all slices)
1621 } else if (part.flags == 2) {
1623 if (_soundEnabled) {
1624 uint32 mask = _stream->readUint32LE();
1625 filledSoundSlices(part.size - 4, mask);
1627 if (_soundStage == 1)
1628 startSound = true;
1630 } else
1631 _stream->skip(part.size);
1633 // Empty sound slice
1634 } else if (part.flags == 3) {
1636 if (_soundEnabled) {
1637 emptySoundSlice(_soundDataSize * _soundBytesPerSample);
1639 if (_soundStage == 1)
1640 startSound = true;
1643 _stream->skip(part.size);
1644 } else if (part.flags == 4) {
1645 warning("Vmd::processFrame(): TODO: Addy 5 sound type 4 (%d)", part.size);
1646 disableSound();
1647 _stream->skip(part.size);
1648 } else {
1649 warning("Vmd::processFrame(): Unknown sound type %d", part.flags);
1650 _stream->skip(part.size);
1653 _stream->seek(pos + part.size);
1655 } else if ((part.type == kPartTypeVideo) && !_hasVideo) {
1656 warning("Vmd::processFrame(): Header claims there's no video, but video found (%d)", part.size);
1657 _stream->skip(part.size);
1658 } else if ((part.type == kPartTypeVideo) && _hasVideo) {
1659 state.flags &= ~kStateNoVideoData;
1661 uint32 size = part.size;
1663 // New palette
1664 if (part.flags & 2) {
1665 uint8 index = _stream->readByte();
1666 uint8 count = _stream->readByte();
1668 _stream->read(_palette + index * 3, (count + 1) * 3);
1669 _stream->skip((255 - count) * 3);
1671 state.flags |= kStatePalette;
1673 size -= (768 + 2);
1676 _stream->read(_frameData, size);
1677 _frameDataLen = size;
1679 int16 l = part.left, t = part.top, r = part.right, b = part.bottom;
1680 if (renderFrame(l, t, r, b)) {
1681 if (!_externalCodec) {
1682 l = preScaleX(l);
1683 r = preScaleX(r);
1685 // Rendering succeeded, merging areas
1686 state.left = MIN(state.left, l);
1687 state.top = MIN(state.top, t);
1688 state.right = MAX(state.right, r);
1689 state.bottom = MAX(state.bottom, b);
1692 } else if (part.type == kPartTypeSeparator) {
1693 } else if (part.type == kPartTypeExtraData) {
1694 _stream->skip(part.size);
1695 } else if (part.type == kPartType4) {
1696 // Unknown
1697 _stream->skip(part.size);
1698 } else if (part.type == kPartTypeSpeech) {
1699 state.flags |= kStateSpeech;
1700 state.speechId = part.id;
1701 // Always triggers when speech starts
1702 _stream->skip(part.size);
1703 } else {
1704 warning("Vmd::processFrame(): Unknown frame part type %d, size %d (%d of %d)", part.type, part.size, i + 1, _partsPerFrame);
1708 if (startSound && _soundEnabled) {
1709 _mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_audioHandle, _audioStream);
1710 _skipFrames = 0;
1711 _soundStage = 2;
1714 if ((_curFrame == (_framesCount - 1)) && (_soundStage == 2)) {
1715 _audioStream->finish();
1716 _mixer->stopHandle(_audioHandle);
1717 _audioStream = 0;
1718 _soundStage = 0;
1721 // If these are still 0x7FFF, no video data has been processed
1722 if ((state.left == 0x7FFF) || (state.top == 0x7FFF))
1723 state.left = state.top = state.right = state.bottom = 0;
1725 _lastFrameTime = g_system->getMillis();
1726 return state;
1729 void Vmd::deRLE(byte *&destPtr, const byte *&srcPtr, int16 len) {
1730 srcPtr++;
1732 if (len & 1)
1733 *destPtr++ = *srcPtr++;
1735 len >>= 1;
1737 while (len > 0) {
1738 uint8 tmp = *srcPtr++;
1739 if (tmp & 0x80) { // Verbatim copy
1740 tmp &= 0x7F;
1742 memcpy(destPtr, srcPtr, tmp * 2);
1743 destPtr += tmp * 2;
1744 srcPtr += tmp * 2;
1745 } else { // 2 bytes tmp times
1746 for (int i = 0; i < tmp; i++) {
1747 *destPtr++ = srcPtr[0];
1748 *destPtr++ = srcPtr[1];
1750 srcPtr += 2;
1752 len -= tmp;
1756 // A run-length-encoded sparse block
1757 void Vmd::renderBlockRLE(byte *dest, const byte *src, int16 width, int16 height,
1758 int16 destWidth, int16 destHeight) {
1760 for (int i = 0; i < height; i++) {
1761 byte *destBak = dest;
1762 uint16 pixWritten = 0;
1764 while (pixWritten < width) {
1765 uint16 pixCount = *src++;
1767 if (pixCount & 0x80) {
1768 pixCount = (pixCount & 0x7F) + 1;
1770 if (*src != 0xFF) { // Normal copy
1771 memcpy(dest, src, pixCount);
1772 dest += pixCount;
1773 src += pixCount;
1774 } else
1775 deRLE(dest, src, pixCount);
1777 pixWritten += pixCount;
1778 } else { // "Hole"
1779 dest += pixCount + 1;
1780 pixWritten += pixCount + 1;
1785 dest = destBak + destWidth;
1790 uint32 Vmd::renderFrame(int16 &left, int16 &top, int16 &right, int16 &bottom) {
1791 if (!_frameData || !_vidMem || (_width <= 0) || (_height <= 0))
1792 return 0;
1794 int16 width = right - left + 1;
1795 int16 height = bottom - top + 1;
1796 int16 sW = _vidMemWidth;
1797 int16 sH = _vidMemHeight;
1798 uint32 dataLen = _frameDataLen;
1800 byte *dataPtr = _frameData;
1801 byte *imdVidMem = _vidMem + sW * top + left;
1802 byte *srcPtr;
1804 if ((left < 0) || (top < 0) || (right < 0) || (bottom < 0))
1805 return 1;
1806 if ((width <= 0) || (height <= 0))
1807 return 1;
1809 uint8 type;
1810 byte *dest = imdVidMem;
1812 if (Indeo3::isIndeo3(dataPtr, dataLen)) {
1813 if (!_codecIndeo3)
1814 return 0;
1816 if (!_codecIndeo3->decompressFrame(dataPtr, dataLen, _vidBuffer,
1817 width * (_doubleMode ? 2 : 1), height * (_doubleMode ? 2 : 1)))
1818 return 0;
1820 type = 2;
1821 srcPtr = _vidBuffer;
1822 width = _width * (_doubleMode ? 2 : 1);
1823 height = _height * (_doubleMode ? 2 : 1);
1824 right = left + width - 1;
1825 bottom = top + height - 1;
1827 } else {
1829 if (_externalCodec) {
1830 warning("Unknown external codec");
1831 return 0;
1834 type = *dataPtr++;
1835 srcPtr = dataPtr;
1837 if (_blitMode > 0) {
1838 dest = _vidMemBuffer + postScaleX(_width) * (top - _y) + postScaleX((left - _x));
1839 imdVidMem = _vidMem + _vidMemWidth * top + preScaleX(left);
1840 sW = postScaleX(_width);
1841 sH = _height;
1844 if (type & 0x80) {
1845 // Frame data is compressed
1847 srcPtr = _vidBuffer;
1848 type &= 0x7F;
1849 if ((type == 2) && (postScaleX(width) == sW)) {
1850 // Directly uncompress onto the video surface
1851 deLZ77(dest, dataPtr);
1852 blit(imdVidMem, dest, width, height);
1853 return 1;
1854 } else
1855 deLZ77(srcPtr, dataPtr);
1860 width = postScaleX(width);
1862 // Evaluate the block type
1863 if (type == 0x01)
1864 renderBlockSparse (dest, srcPtr, width, height, sW, sH);
1865 else if (type == 0x02)
1866 renderBlockWhole (dest, srcPtr, width, height, sW, sH);
1867 else if (type == 0x03)
1868 renderBlockRLE (dest, srcPtr, width, height, sW, sH);
1869 else if (type == 0x42)
1870 renderBlockWhole4X (dest, srcPtr, width, height, sW, sH);
1871 else if ((type & 0x0F) == 0x02)
1872 renderBlockWhole2Y (dest, srcPtr, width, height, sW, sH);
1873 else
1874 renderBlockSparse2Y(dest, srcPtr, width, height, sW, sH);
1877 dest = _vidMemBuffer + postScaleX(_width) * (top - _y) + postScaleX(left - _x);
1878 blit(imdVidMem, dest, width, height);
1880 return 1;
1883 inline int32 Vmd::preScaleX(int32 x) const {
1884 return x / _preScaleX;
1887 inline int32 Vmd::postScaleX(int32 x) const {
1888 return x * _postScaleX;
1891 void Vmd::blit(byte *dest, byte *src, int16 width, int16 height) {
1892 if (_blitMode == 0)
1893 return;
1895 if (_blitMode == 1)
1896 blit16(dest, src, preScaleX(_width), preScaleX(width), height);
1897 else if (_blitMode == 2)
1898 blit24(dest, src, preScaleX(_width), preScaleX(width), height);
1901 void Vmd::blit16(byte *dest, byte *src, int16 srcPitch, int16 width, int16 height) {
1902 assert(_palLUT);
1904 Graphics::SierraLight *dither =
1905 new Graphics::SierraLight(width, _palLUT);
1907 for (int i = 0; i < height; i++) {
1908 byte *d = dest;
1909 byte *s = src;
1911 for (int j = 0; j < width; j++, s += 2) {
1912 uint16 data = READ_LE_UINT16(s);
1913 byte r = ((data & 0x7C00) >> 10);
1914 byte g = ((data & 0x03E0) >> 5);
1915 byte b = ((data & 0x001F) >> 0);
1916 byte dY, dU, dV;
1918 Graphics::PaletteLUT::RGB2YUV(r << 3, g << 3, b << 3, dY, dU, dV);
1920 byte p = dither->dither(dY, dU, dV, j);
1922 if ((dY == 0) || ((r == 0) && (g == 0) && (b == 0)))
1923 *d++ = 0;
1924 else
1925 *d++ = p;
1928 dither->nextLine();
1929 dest += _vidMemWidth;
1930 src += 2 * srcPitch;
1933 delete dither;
1936 void Vmd::blit24(byte *dest, byte *src, int16 srcPitch, int16 width, int16 height) {
1937 assert(_palLUT);
1939 Graphics::SierraLight *dither =
1940 new Graphics::SierraLight(width, _palLUT);
1942 for (int i = 0; i < height; i++) {
1943 byte *d = dest;
1944 byte *s = src;
1946 for (int j = 0; j < width; j++, s += 3) {
1947 byte r = s[2];
1948 byte g = s[1];
1949 byte b = s[0];
1950 byte dY, dU, dV;
1952 Graphics::PaletteLUT::RGB2YUV(r, g, b, dY, dU, dV);
1954 byte p = dither->dither(dY, dU, dV, j);
1956 if ((dY == 0) || ((r == 0) && (g == 0) && (b == 0)))
1957 *d++ = 0;
1958 else
1959 *d++ = p;
1962 dither->nextLine();
1963 dest += _vidMemWidth;
1964 src += 3 * srcPitch;
1967 delete dither;
1970 byte *Vmd::deDPCM(const byte *data, uint32 &size, int32 init[2]) {
1971 if (!data || (size == 0))
1972 return 0;
1974 int channels = (_soundStereo > 0) ? 2 : 1;
1976 uint32 inSize = size;
1977 uint32 outSize = size + channels;
1979 int16 *out = new int16[outSize];
1980 byte *sound = (byte *) out;
1982 int channel = 0;
1984 for (int i = 0; i < channels; i++) {
1985 *out++ = TO_BE_16(init[channel]);
1987 channel = (channel + 1) % channels;
1990 while (inSize-- > 0) {
1991 if (*data & 0x80)
1992 init[channel] -= _tableDPCM[*data++ & 0x7F];
1993 else
1994 init[channel] += _tableDPCM[*data++];
1996 init[channel] = CLIP<int32>(init[channel], -32768, 32767);
1997 *out++ = TO_BE_16(init[channel]);
1999 channel = (channel + 1) % channels;
2002 size = outSize * 2;
2003 return sound;
2006 // Yet another IMA ADPCM variant
2007 byte *Vmd::deADPCM(const byte *data, uint32 &size, int32 init, int32 index) {
2008 if (!data || (size == 0))
2009 return 0;
2011 uint32 outSize = size * 2;
2013 int16 *out = new int16[outSize];
2014 byte *sound = (byte *) out;
2016 index = CLIP<int32>(index, 0, 88);
2018 int32 predictor = _tableADPCM[index];
2020 uint32 dataByte = 0;
2021 bool newByte = true;
2023 size *= 2;
2024 while (size -- > 0) {
2025 byte code = 0;
2027 if (newByte) {
2028 dataByte = *data++;
2029 code = (dataByte >> 4) & 0xF;
2030 } else
2031 code = dataByte & 0xF;
2033 newByte = !newByte;
2035 index += _tableADPCMStep[code];
2036 index = CLIP<int32>(index, 0, 88);
2038 int32 value = predictor / 8;
2040 if (code & 4)
2041 value += predictor;
2042 if (code & 2)
2043 value += predictor / 2;
2044 if (code & 1)
2045 value += predictor / 4;
2047 if (code & 8)
2048 init -= value;
2049 else
2050 init += value;
2052 init = CLIP<int32>(init, -32768, 32767);
2054 predictor = _tableADPCM[index];
2056 *out++ = TO_BE_16(init);
2059 size = outSize * 2;
2060 return sound;
2063 byte *Vmd::soundEmpty(uint32 &size) {
2064 if (!_audioStream)
2065 return 0;
2067 byte *soundBuf = new byte[size];
2068 memset(soundBuf, 0, size);
2070 return soundBuf;
2073 byte *Vmd::sound8bitDirect(uint32 &size) {
2074 if (!_audioStream) {
2075 _stream->skip(size);
2076 return 0;
2079 byte *soundBuf = new byte[size];
2080 _stream->read(soundBuf, size);
2081 unsignedToSigned(soundBuf, size);
2083 return soundBuf;
2086 byte *Vmd::sound16bitDPCM(uint32 &size) {
2087 if (!_audioStream) {
2088 _stream->skip(size);
2089 return 0;
2092 int32 init[2];
2094 init[0] = _stream->readSint16LE();
2095 size -= 2;
2097 if (_soundStereo > 0) {
2098 init[1] = _stream->readSint16LE();
2099 size -= 2;
2102 byte *data = new byte[size];
2103 byte *sound = 0;
2105 if (_stream->read(data, size) == size)
2106 sound = deDPCM(data, size, init);
2108 delete[] data;
2110 return sound;
2113 byte *Vmd::sound16bitADPCM(uint32 &size) {
2114 if (!_audioStream) {
2115 _stream->skip(size);
2116 return 0;
2119 int32 init = _stream->readSint16LE();
2120 size -= 2;
2122 int32 index = _stream->readByte();
2123 size--;
2125 byte *data = new byte[size];
2126 byte *sound = 0;
2128 if (_stream->read(data, size) == size)
2129 sound = deADPCM(data, size, init, index);
2131 delete[] data;
2133 return sound;
2136 void Vmd::emptySoundSlice(uint32 size) {
2137 byte *sound = soundEmpty(size);
2139 if (sound)
2140 _audioStream->queueBuffer(sound, size);
2143 void Vmd::filledSoundSlice(uint32 size) {
2144 byte *sound = 0;
2145 if (_audioFormat == kAudioFormat8bitDirect)
2146 sound = sound8bitDirect(size);
2147 else if (_audioFormat == kAudioFormat16bitDPCM)
2148 sound = sound16bitDPCM(size);
2149 else if (_audioFormat == kAudioFormat16bitADPCM)
2150 sound = sound16bitADPCM(size);
2152 if (sound)
2153 _audioStream->queueBuffer(sound, size);
2156 uint8 Vmd::evaluateMask(uint32 mask, bool *fillInfo, uint8 &max) {
2157 max = MIN<int>(_soundSlicesCount - 1, 31);
2159 uint8 n = 0;
2160 for (int i = 0; i < max; i++) {
2162 if (!(mask & 1)) {
2163 n++;
2164 *fillInfo++ = true;
2165 } else
2166 *fillInfo++ = false;
2168 mask >>= 1;
2171 return n;
2174 void Vmd::filledSoundSlices(uint32 size, uint32 mask) {
2175 bool fillInfo[32];
2177 uint8 max;
2178 uint8 n = evaluateMask(mask, fillInfo, max);
2180 int32 extraSize;
2182 extraSize = size - n * _soundDataSize;
2184 if (_soundSlicesCount > 32)
2185 extraSize -= (_soundSlicesCount - 32) * _soundDataSize;
2187 if (n > 0)
2188 extraSize /= n;
2190 for (uint8 i = 0; i < max; i++)
2191 if (fillInfo[i])
2192 filledSoundSlice(_soundDataSize + extraSize);
2193 else
2194 emptySoundSlice(_soundDataSize * _soundBytesPerSample);
2196 if (_soundSlicesCount > 32)
2197 filledSoundSlice((_soundSlicesCount - 32) * _soundDataSize + _soundHeaderSize);
2200 bool Vmd::getPartCoords(int16 frame, PartType type,
2201 int16 &x, int16 &y, int16 &width, int16 &height) {
2203 if (frame >= _framesCount)
2204 return false;
2206 Frame &f = _frames[frame];
2208 // Look for a part matching the requested type, stopping at a separator
2209 Part *part = 0;
2210 for (int i = 0; i < _partsPerFrame; i++) {
2211 Part &p = f.parts[i];
2213 if ((p.type == kPartTypeSeparator) || (p.type == type)) {
2214 part = &p;
2215 break;
2219 if (!part)
2220 return false;
2222 x = part->left;
2223 y = part->top;
2224 width = part->right - part->left + 1;
2225 height = part->bottom - part->top + 1;
2227 return true;
2230 bool Vmd::getFrameCoords(int16 frame,
2231 int16 &x, int16 &y, int16 &width, int16 &height) {
2233 return getPartCoords(frame, kPartTypeVideo, x, y, width, height);
2236 bool Vmd::hasExtraData(const char *fileName) const {
2237 for (uint i = 0; i < _extraData.size(); i++)
2238 if (!scumm_stricmp(_extraData[i].name, fileName))
2239 return true;
2241 return false;
2244 Common::MemoryReadStream *Vmd::getExtraData(const char *fileName) {
2245 uint i = 0;
2247 for (i = 0; i < _extraData.size(); i++)
2248 if (!scumm_stricmp(_extraData[i].name, fileName))
2249 break;
2251 if (i >= _extraData.size())
2252 return 0;
2254 if ((_extraData[i].size - 20) != _extraData[i].realSize) {
2255 warning("Vmd::getExtraData(): Sizes for \"%s\" differ! (%d, %d)",
2256 fileName, (_extraData[i].size - 20), _extraData[i].realSize);
2257 return 0;
2260 if (!_stream->seek(_extraData[i].offset)) {
2261 warning("Vmd::getExtraData(): Can't seek to offset %d to (file \"%s\")",
2262 _extraData[i].offset, fileName);
2263 return 0;
2266 byte *data = (byte *) malloc(_extraData[i].realSize);
2267 if (_stream->read(data, _extraData[i].realSize) != _extraData[i].realSize) {
2268 free(data);
2269 warning("Vmd::getExtraData(): Couldn't read %d bytes (file \"%s\")",
2270 _extraData[i].realSize, fileName);
2273 Common::MemoryReadStream *stream =
2274 new Common::MemoryReadStream(data, _extraData[i].realSize, true);
2276 return stream;
2279 } // End of namespace Graphics