ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

smush_decoder.cpp

Go to the documentation of this file.
00001 /* ResidualVM - A 3D game interpreter
00002  *
00003  * ResidualVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "common/endian.h"
00024 #include "common/events.h"
00025 #include "common/file.h"
00026 #include "common/rational.h"
00027 #include "common/system.h"
00028 #include "common/timer.h"
00029 #include "common/memstream.h"
00030 
00031 #include "audio/audiostream.h"
00032 #include "audio/mixer.h"
00033 #include "audio/decoders/raw.h"
00034 
00035 #include "engines/grim/debug.h"
00036 
00037 #include "engines/grim/movie/codecs/codec48.h"
00038 #include "engines/grim/movie/codecs/blocky8.h"
00039 #include "engines/grim/movie/codecs/blocky16.h"
00040 #include "engines/grim/movie/codecs/smush_decoder.h"
00041 #include "engines/grim/movie/codecs/vima.h"
00042 
00043 namespace Grim {
00044 
00045 #define ANNO_HEADER "MakeAnim animation type 'Bl16' parameters: "
00046 #define BUFFER_SIZE 16385
00047 #define SMUSH_SPEED 66667
00048 
00049 bool SmushDecoder::_demo = false;
00050 
00051 static uint16 smushDestTable[5786];
00052 
00053 SmushDecoder::SmushDecoder() {
00054     _file = nullptr;
00055 
00056     _videoLooping = false;
00057     _startPos = 0;
00058     _frames = nullptr;
00059 
00060     _videoTrack = nullptr;
00061     _audioTrack = nullptr;
00062     _videoPause = false;
00063 }
00064 
00065 SmushDecoder::~SmushDecoder() {
00066     delete _videoTrack;
00067     delete _audioTrack;
00068     delete[] _frames;
00069 }
00070 
00071 void SmushDecoder::init() {
00072     _videoTrack->init();
00073     _audioTrack->init();
00074 }
00075 
00076 void SmushDecoder::initFrames() {
00077     delete[] _frames;
00078     _frames = new Frame[_videoTrack->getFrameCount()];
00079 
00080     int seekPos = _file->pos();
00081     int curFrame = -1;
00082     _file->seek(_startPos, SEEK_SET);
00083     while (curFrame < _videoTrack->getFrameCount() - 1) {
00084         Frame &frame = _frames[++curFrame];
00085         frame.frame = curFrame;
00086         frame.pos = _file->pos();
00087         frame.keyframe = false;
00088 
00089         uint32 tag = _file->readUint32BE();
00090         uint32 size;
00091         if (tag == MKTAG('A', 'N', 'N', 'O')) {
00092             size = _file->readUint32BE();
00093             _file->seek(size, SEEK_CUR);
00094             tag = _file->readUint32BE();
00095         }
00096         assert(tag == MKTAG('F', 'R', 'M', 'E'));
00097         size = _file->readUint32BE();
00098 
00099         while (size > 0) {
00100             uint32 subType = _file->readUint32BE();
00101             uint32 subSize = _file->readUint32BE();
00102             int32  subPos  = _file->pos();
00103 
00104             if (subType == MKTAG('B', 'l', '1', '6')) {
00105                 _file->seek(18, SEEK_CUR);
00106                 if (_file->readByte() == 0) {
00107                     frame.keyframe = true;
00108                 }
00109             }
00110 
00111             size -= subSize + 8 + (subSize & 1);
00112             _file->seek(subPos + subSize + (subSize & 1), SEEK_SET);
00113         }
00114 
00115         _file->seek(size, SEEK_CUR);
00116     }
00117 
00118     _file->seek(seekPos, SEEK_SET);
00119 }
00120 
00121 void SmushDecoder::close() {
00122     VideoDecoder::close();
00123     _audioTrack = nullptr;
00124     _videoTrack = nullptr;
00125     _videoLooping = false;
00126     _startPos = 0;
00127     delete[] _frames;
00128     _frames = nullptr;
00129     if (_file) {
00130         delete _file;
00131         _file = nullptr;
00132     }
00133 }
00134 
00135 
00136 bool SmushDecoder::readHeader() {
00137     if (!_file) {
00138         return false;
00139     }
00140 
00141     uint32 mainTag = _file->readUint32BE();
00142     uint32 pos = _file->pos();
00143     uint32 expectedTag = 0;
00144     uint32 size = _file->readUint32BE(); // file-size
00145 
00146     // Verify that we have the correct combination of headers.
00147     if (mainTag == MKTAG('A', 'N', 'I', 'M')) { // Demo
00148         expectedTag = MKTAG('A', 'H', 'D', 'R');
00149     } else if (mainTag == MKTAG('S', 'A', 'N', 'M')) { // Retail
00150         expectedTag = MKTAG('S', 'H', 'D', 'R');
00151     } else {
00152         error("Invalid SMUSH-header");
00153     }
00154 
00155     uint32 tag = _file->readUint32BE();
00156     size = _file->readUint32BE();
00157     pos = _file->pos();
00158 
00159     assert(tag == expectedTag);
00160 
00161     if (tag == MKTAG('A', 'H', 'D', 'R')) { // Demo
00162         uint32 version = _file->readUint16LE();
00163         uint16 nbFrames = _file->readUint16LE();
00164         _file->readUint16BE(); // unknown
00165 
00166         int width = -1;
00167         int height = -1;
00168         _videoLooping = false;
00169         _startPos = 0;
00170 
00171         _videoTrack = new SmushVideoTrack(width, height, SMUSH_SPEED, nbFrames, false);
00172         _videoTrack->_x = -1;
00173         _videoTrack->_y = -1;
00174         addTrack(_videoTrack);
00175         _file->read(_videoTrack->getPal(), 0x300);
00176 
00177         int audioRate = 11025;
00178         if (version == 2) {
00179 
00180             _file->readUint32LE(); // framerate
00181             _file->readUint32LE();
00182             audioRate = _file->readUint32LE();
00183         }
00184 
00185         _file->readUint32BE();
00186         _file->readUint32BE();
00187         _audioTrack = new SmushAudioTrack(getSoundType(), false, audioRate, 2);
00188         addTrack(_audioTrack);
00189         return true;
00190 
00191     } else if (tag == MKTAG('S', 'H', 'D', 'R')) { // Retail
00192         _file->readUint16LE();
00193         uint16 nbFrames = _file->readUint32LE();
00194         _file->readUint16LE();
00195         int width = _file->readUint16LE();
00196         int height = _file->readUint16LE();
00197         _file->readUint16LE();
00198         int frameRate = _file->readUint32LE();
00199 
00200         int16 flags = _file->readUint16LE();
00201         // Output information for checking out the flags
00202         if (Debug::isChannelEnabled(Debug::Movie | Debug::Info)) {
00203             warning("SMUSH Flags:");
00204             for (int i = 0; i < 16; i++) {
00205                 warning(" %d", (flags & (1 << i)) != 0);
00206             }
00207         }
00208 
00209         _file->seek(pos + size + (size & 1), SEEK_SET);
00210 
00211         _videoLooping = true;
00212         // If the video is NOT looping, setLooping will set the speed to the proper value
00213         _videoTrack = new SmushVideoTrack(width, height, frameRate, nbFrames, true);
00214         addTrack(_videoTrack);
00215         return handleFramesHeader();
00216     }
00217     return false;
00218 }
00219 
00220 bool SmushDecoder::handleFramesHeader() {
00221     uint32 tag;
00222     int32 size;
00223     int pos = 0;
00224     int freq = 0;
00225     int channels = 0;
00226 
00227     tag = _file->readUint32BE();
00228     if (tag != MKTAG('F', 'L', 'H', 'D')) {
00229         return false;
00230     }
00231     size = _file->readUint32BE();
00232     byte *f_header = new byte[size];
00233     _file->read(f_header, size);
00234 
00235     do {
00236         if (READ_BE_UINT32(f_header + pos) == MKTAG('B', 'l', '1', '6')) {
00237             pos += READ_BE_UINT32(f_header + pos + 4) + 8;
00238         } else if (READ_BE_UINT32(f_header + pos) == MKTAG('W', 'a', 'v', 'e')) {
00239             freq = READ_LE_UINT32(f_header + pos + 8);
00240             channels = READ_LE_UINT32(f_header + pos + 12);
00241             pos += 20;
00242         } else {
00243             error("SmushDecoder::handleFramesHeader() unknown tag");
00244         }
00245     } while (pos < size);
00246     delete[] f_header;
00247 
00248     _audioTrack = new SmushAudioTrack(getSoundType(), true, freq, channels);
00249     addTrack(_audioTrack);
00250     return true;
00251 }
00252 
00253 bool SmushDecoder::loadStream(Common::SeekableReadStream *stream) {
00254     close();
00255 
00256     _file = stream;
00257 
00258     // Load the video
00259     if (!readHeader()) {
00260         warning("Failure loading SMUSH-file");
00261         return false;
00262     }
00263 
00264     _startPos = _file->pos();
00265 
00266     init();
00267     return true;
00268 }
00269 
00270 const Graphics::Surface *SmushDecoder::decodeNextFrame() {
00271     handleFrame();
00272 
00273     // We might be interested in getting the last frame even after the video ends:
00274     if (endOfVideo()) {
00275         return _videoTrack->decodeNextFrame();
00276     }
00277     return VideoDecoder::decodeNextFrame();
00278 }
00279 
00280 void SmushDecoder::setLooping(bool l) {
00281     _videoLooping = l;
00282 
00283     if (!_videoLooping) {
00284         _videoTrack->setMsPerFrame(SMUSH_SPEED);
00285     }
00286 }
00287 
00288 void SmushDecoder::handleFrame() {
00289     uint32 tag;
00290     int32 size;
00291 
00292     if (isPaused()) {
00293         return;
00294     }
00295 
00296     if (_videoTrack->endOfTrack()) { // Looping is handled outside, by rewinding the video.
00297         _audioTrack->stop(); // HACK: Avoids the movie playing past the last frame
00298         //  pauseVideo(true);
00299         return;
00300     }
00301 
00302     tag = _file->readUint32BE();
00303     size = _file->readUint32BE();
00304     if (tag == MKTAG('A', 'N', 'N', 'O')) {
00305         char *anno;
00306         byte *data;
00307 
00308         data = new byte[size];
00309         _file->read(data, size);
00310         anno = (char *)data;
00311         if (strncmp(anno, ANNO_HEADER, sizeof(ANNO_HEADER) - 1) == 0) {
00312             //char *annoData = anno + sizeof(ANNO_HEADER);
00313 
00314             // Examples:
00315             //  Water streaming around boat from Manny's balcony
00316             //  MakeAnim animation type 'Bl16' parameters: 10000;12000;100;1;0;0;0;0;25;0;
00317             //  Water in front of the Blue Casket
00318             //  MakeAnim animation type 'Bl16' parameters: 20000;25000;100;1;0;0;0;0;25;0;
00319             //  Scrimshaw exterior:
00320             //  MakeAnim animation type 'Bl16' parameters: 6000;8000;100;0;0;0;0;0;2;0;
00321             //  Lola engine room (loops a limited number of times?):
00322             //  MakeAnim animation type 'Bl16' parameters: 6000;8000;90;1;0;0;0;0;2;0;
00323             Debug::debug(Debug::Movie, "Announcement data: %s\n", anno);
00324             // It looks like the announcement data is actually for setting some of the
00325             // header parameters, not for any looping purpose
00326         } else {
00327             Debug::debug(Debug::Movie, "Announcement header not understood: %s\n", anno);
00328         }
00329         delete[] anno;
00330         tag = _file->readUint32BE();
00331         size = _file->readUint32BE();
00332     }
00333 
00334     assert(tag == MKTAG('F', 'R', 'M', 'E'));
00335     handleFRME(_file, size);
00336 
00337     _videoTrack->finishFrame();
00338 }
00339 
00340 void SmushDecoder::handleFRME(Common::SeekableReadStream *stream, uint32 size) {
00341     int blockSize = size;
00342 
00343     byte *block = new byte[size];
00344     stream->read(block, size);
00345 
00346     Common::MemoryReadStream *memStream = new Common::MemoryReadStream(block, size, DisposeAfterUse::NO);
00347     while (size > 0) {
00348         uint32 subType = memStream->readUint32BE();
00349         uint32 subSize = memStream->readUint32BE();
00350         uint32 subPos = memStream->pos();
00351 
00352         switch (subType) {
00353             // Retail only:
00354         case MKTAG('B', 'l', '1', '6'):
00355             _videoTrack->handleBlocky16(memStream, subSize);
00356             break;
00357         case MKTAG('W', 'a', 'v', 'e'):
00358             _audioTrack->handleVIMA(memStream, blockSize);
00359             break;
00360             // Demo only:
00361         case MKTAG('F', 'O', 'B', 'J'):
00362             _videoTrack->handleFrameObject(memStream, subSize);
00363             break;
00364         case MKTAG('I', 'A', 'C', 'T'):
00365             _audioTrack->handleIACT(memStream, subSize);
00366             break;
00367         case MKTAG('X', 'P', 'A', 'L'):
00368             _videoTrack->handleDeltaPalette(memStream, subSize);
00369             break;
00370         default:
00371             Debug::error(Debug::Movie, "SmushDecoder::handleFrame() unknown tag");
00372         }
00373         size -= subSize + 8 + (subSize & 1);
00374         memStream->seek(subPos + subSize + (subSize & 1), SEEK_SET);
00375     }
00376     delete memStream;
00377     delete[] block;
00378 }
00379 
00380 bool SmushDecoder::rewind() {
00381     return seekToFrame(0);
00382 }
00383 
00384 bool SmushDecoder::seekIntern(const Audio::Timestamp &time) {
00385     int32 wantedFrame = (uint32)((time.msecs() / 1000.0f) * _videoTrack->getFrameRate().toDouble());
00386     if (wantedFrame != 0) {
00387         Debug::debug(Debug::Movie, "Seek to time: %d, frame: %d", time.msecs(), wantedFrame);
00388         Debug::debug(Debug::Movie, "Current frame: %d", _videoTrack->getCurFrame());
00389     }
00390 
00391     if (wantedFrame > _videoTrack->getFrameCount()) {
00392         return false;
00393     }
00394 
00395     if (!_frames) {
00396         initFrames();
00397     }
00398 
00399     // Track down the keyframe
00400     int keyframe = 0;
00401     for (int i = wantedFrame; i >= 0; --i) {
00402         if (_frames[i].keyframe) {
00403             keyframe = i;
00404             break;
00405         }
00406     }
00407     _videoTrack->setFrameStart(keyframe);
00408 
00409     // VIMA frames are 50 frames ahead of time, so we have to make sure we have 50 frames
00410     // of audio before the wantedFrame. Here we use 51 to have a bit of safe margin
00411     if (wantedFrame - keyframe < 51) {
00412         keyframe = wantedFrame - 51;
00413     }
00414     if (keyframe < 0) {
00415         keyframe = 0;
00416     }
00417 
00418     _file->seek(_frames[keyframe].pos, SEEK_SET);
00419     _videoTrack->setCurFrame(keyframe - 1);
00420 
00421     while (_videoTrack->getCurFrame() < wantedFrame - 1) {
00422         decodeNextFrame();
00423     }
00424 
00425     // As said, VIMA is 50 frames ahead of time. Every frame it pushes 1470 samples, and 50 * 1470 = 73500.
00426     // The first frame, instead of 1470, it pushes 73500 samples to have this 50-frames-time.
00427     // So if we have used frame 0 as keyframe we can remove safely time * rate samples, and we will
00428     // still have the 50 frames margin. If we have used a later frame as keyframe we don't have the 73500
00429     // samples pushed the first frame, so we have to be careful not to remove too much data,
00430     // otherwise the audio will start at a later point. (72030 == 73500 - 1470)
00431     int offset = (keyframe == 0 ? 0 : 72030);
00432 
00433     // Skip decoded audio between the keyframe and the target frame
00434     Audio::Timestamp delay = 0;
00435     if (_videoTrack->getCurFrame() > 0) {
00436         delay = _videoTrack->getFrameTime(_videoTrack->getCurFrame());
00437     }
00438     if (keyframe > 0) {
00439         delay = delay - _videoTrack->getFrameTime(keyframe);
00440     }
00441 
00442     int32 sampleCount = (delay.msecs() / 1000.f) * _audioTrack->getRate() - offset;
00443     _audioTrack->skipSamples(sampleCount);
00444 
00445     VideoDecoder::seekIntern(time);
00446     return true;
00447 }
00448 
00449 SmushDecoder::SmushVideoTrack::SmushVideoTrack(int width, int height, int fps, int numFrames, bool is16Bit) {
00450     // Set color-format statically here for SMUSH (5650), to allow for differing
00451     // PixelFormat in engine and renderer (and conversion from Surface there)
00452     // Which means 16 bpp, 565, shift of 11, 5, 0, 0 for RGBA
00453     _format = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
00454     if (!is16Bit) { // Demo
00455         _codec48 = new Codec48Decoder();
00456         _blocky8 = new Blocky8();
00457         _blocky16 = nullptr;
00458     } else {
00459         _codec48 = nullptr;
00460         _blocky8 = nullptr;
00461         _blocky16 = new Blocky16();
00462         _blocky16->init(width, height);
00463     }
00464     _width = width;
00465     _height = height;
00466     _nbframes = numFrames;
00467     _is16Bit = is16Bit;
00468     _x = 0;
00469     _y = 0;
00470     setMsPerFrame(fps);
00471     _curFrame = 0;
00472     for (int i = 0; i < 0x300; i++) {
00473         _pal[i] = 0;
00474         _deltaPal[i] = 0;
00475     }
00476     _frameStart = 0;
00477 }
00478 
00479 SmushDecoder::SmushVideoTrack::~SmushVideoTrack() {
00480     delete _codec48;
00481     delete _blocky8;
00482     delete _blocky16;
00483     _surface.free();
00484 }
00485 
00486 void SmushDecoder::SmushVideoTrack::init() {
00487     _curFrame = -1;
00488     _frameStart = -1;
00489     if (_is16Bit) { // Retail only
00490         _surface.create(_width, _height, _format);
00491     }
00492 }
00493 
00494 void SmushDecoder::SmushVideoTrack::finishFrame() {
00495     if (!_is16Bit) {
00496         convertDemoFrame();
00497     }
00498     _curFrame++;
00499 }
00500 
00501 void SmushDecoder::SmushVideoTrack::setFrameStart(int frame) {
00502     _frameStart = frame - 1;
00503 }
00504 
00505 void SmushDecoder::SmushVideoTrack::convertDemoFrame() {
00506     Graphics::Surface conversion;
00507     conversion.create(0, 0, _format); // Avoid issues with copyFrom, by creating an empty surface.
00508     conversion.copyFrom(_surface);
00509 
00510     uint16 *d = (uint16 *)_surface.getPixels();
00511     for (int l = 0; l < _width * _height; l++) {
00512         int index = ((byte *)conversion.getPixels())[l];
00513         d[l] = ((_pal[(index * 3) + 0] & 0xF8) << 8) | ((_pal[(index * 3) + 1] & 0xFC) << 3) | (_pal[(index * 3) + 2] >> 3);
00514     }
00515     conversion.free();
00516 }
00517 
00518 void SmushDecoder::SmushVideoTrack::handleBlocky16(Common::SeekableReadStream *stream, uint32 size) {
00519     if (_curFrame < _frameStart) {
00520         return;
00521     }
00522 
00523     assert(_is16Bit);
00524     byte *ptr = new byte[size];
00525     stream->read(ptr, size);
00526 
00527     _blocky16->decode((byte *)_surface.getPixels(), ptr);
00528     delete[] ptr;
00529 }
00530 
00531 void SmushDecoder::SmushVideoTrack::handleFrameObject(Common::SeekableReadStream *stream, uint32 size) {
00532     if (_curFrame < _frameStart) {
00533         return;
00534     }
00535 
00536     assert(!_is16Bit);
00537     assert(size >= 14);
00538     byte codec = stream->readByte();
00539     assert(codec == 47 || codec == 48);
00540     /* byte codecParam = */ stream->readByte();
00541     _x = stream->readSint16LE();
00542     _y = stream->readSint16LE();
00543     uint16 width = stream->readUint16LE();
00544     uint16 height = stream->readUint16LE();
00545     if (width != _width || height != _height) {
00546         _width = width;
00547         _height = height;
00548         _surface.create(_width, _height, _format);
00549         _codec48->init(_width, _height);
00550         _blocky8->init(_width, _height);
00551     }
00552     stream->readUint16LE();
00553     stream->readUint16LE();
00554 
00555     size -= 14;
00556     byte *ptr = new byte[size];
00557     stream->read(ptr, size);
00558 
00559     if (codec == 47) {
00560         _blocky8->decode((byte *)_surface.getPixels(), ptr);
00561     } else if (codec == 48) {
00562         _codec48->decode((byte *)_surface.getPixels(), ptr);
00563     }
00564     delete[] ptr;
00565 }
00566 
00567 static byte delta_color(byte org_color, int16 delta_color) {
00568     int t = (org_color * 129 + delta_color) / 128;
00569     return CLIP(t, 0, 255);
00570 }
00571 
00572 void SmushDecoder::SmushVideoTrack::handleDeltaPalette(Common::SeekableReadStream *stream, int32 size) {
00573     if (size == 0x300 * 3 + 4) {
00574         stream->seek(4, SEEK_CUR);
00575         for (int i = 0; i < 0x300; i++) {
00576             _deltaPal[i] = stream->readUint16LE();
00577         }
00578         stream->read(_pal, 0x300);
00579     } else if (size == 6) {
00580         for (int i = 0; i < 0x300; i++) {
00581             _pal[i] = delta_color(_pal[i], _deltaPal[i]);
00582         }
00583     } else {
00584         error("SmushDecoder::handleDeltaPalette() Wrong size for DeltaPalette");
00585     }
00586 }
00587 
00588 Graphics::Surface *SmushDecoder::SmushVideoTrack::decodeNextFrame() {
00589     return &_surface;
00590 }
00591 
00592 void SmushDecoder::SmushVideoTrack::setMsPerFrame(int ms) {
00593     _frameRate = Common::Rational(1000000, ms);
00594 }
00595 SmushDecoder::SmushAudioTrack::SmushAudioTrack(Audio::Mixer::SoundType soundType, bool isVima, int freq, int channels) :
00596     AudioTrack(soundType) {
00597     _isVima = isVima;
00598     _channels = channels;
00599     _freq = freq;
00600     _queueStream = Audio::makeQueuingAudioStream(_freq, (_channels == 2));
00601     _IACTpos = 0;
00602 }
00603 
00604 SmushDecoder::SmushAudioTrack::~SmushAudioTrack() {
00605     delete _queueStream;
00606 }
00607 
00608 void SmushDecoder::SmushAudioTrack::init() {
00609     _IACTpos = 0;
00610 
00611     if (_isVima) {
00612         vimaInit(smushDestTable);
00613     }
00614 }
00615 
00616 void SmushDecoder::SmushAudioTrack::handleVIMA(Common::SeekableReadStream *stream, uint32 size) {
00617     int decompressedSize = stream->readUint32BE();
00618     if (decompressedSize < 0) {
00619         stream->readUint32BE();
00620         decompressedSize = stream->readUint32BE();
00621     }
00622 
00623     byte *src = new byte[size];
00624     stream->read(src, size);
00625 
00626     // this will be deleted using free() by the stream, so allocate it using malloc().
00627     int16 *dst = (int16 *)malloc(decompressedSize * _channels * 2);
00628     decompressVima(src, dst, decompressedSize * _channels * 2, smushDestTable);
00629 
00630     int flags = Audio::FLAG_16BITS;
00631     if (_channels == 2) {
00632         flags |= Audio::FLAG_STEREO;
00633     }
00634 
00635     if (!_queueStream) {
00636         _queueStream = Audio::makeQueuingAudioStream(_freq, (_channels == 2));
00637     }
00638     _queueStream->queueBuffer((byte *)dst, decompressedSize * _channels * 2, DisposeAfterUse::YES, flags);
00639     delete[] src;
00640 }
00641 
00642 void SmushDecoder::SmushAudioTrack::handleIACT(Common::SeekableReadStream *stream, int32 size) {
00643     byte *src = new byte[size];
00644     stream->read(src, size);
00645 
00646     int32 bsize = size - 18;
00647     const byte *d_src = src + 18;
00648 
00649     while (bsize > 0) {
00650         if (_IACTpos >= 2) {
00651             int32 len = READ_BE_UINT16(_IACToutput) + 2;
00652             len -= _IACTpos;
00653             if (len > bsize) {
00654                 memcpy(_IACToutput + _IACTpos, d_src, bsize);
00655                 _IACTpos += bsize;
00656                 bsize = 0;
00657             } else {
00658                 // this will be deleted using free() by the stream, so allocate it using malloc().
00659                 byte *output_data = (byte *)malloc(4096);
00660                 memcpy(_IACToutput + _IACTpos, d_src, len);
00661                 byte *dst = output_data;
00662                 byte *d_src2 = _IACToutput;
00663                 d_src2 += 2;
00664                 int32 count = 1024;
00665                 byte variable1 = *d_src2++;
00666                 byte variable2 = variable1 / 16;
00667                 variable1 &= 0x0f;
00668                 do {
00669                     byte value;
00670                     value = *(d_src2++);
00671                     if (value == 0x80) {
00672                         *dst++ = *d_src2++;
00673                         *dst++ = *d_src2++;
00674                     } else {
00675                         int16 val = (int8)value << variable2;
00676                         *dst++ = val >> 8;
00677                         *dst++ = (byte)(val);
00678                     }
00679                     value = *(d_src2++);
00680                     if (value == 0x80) {
00681                         *dst++ = *d_src2++;
00682                         *dst++ = *d_src2++;
00683                     } else {
00684                         int16 val = (int8)value << variable1;
00685                         *dst++ = val >> 8;
00686                         *dst++ = (byte)(val);
00687                     }
00688                 } while (--count);
00689 
00690                 if (!_queueStream) {
00691                     _queueStream = Audio::makeQueuingAudioStream(22050, true);
00692                 }
00693                 _queueStream->queueBuffer(output_data, 0x1000, DisposeAfterUse::YES, Audio::FLAG_STEREO | Audio::FLAG_16BITS);
00694 
00695                 bsize -= len;
00696                 d_src += len;
00697                 _IACTpos = 0;
00698             }
00699         } else {
00700             if (bsize > 1 && _IACTpos == 0) {
00701                 *(_IACToutput + 0) = *d_src++;
00702                 _IACTpos = 1;
00703                 bsize--;
00704             }
00705             *(_IACToutput + _IACTpos) = *d_src++;
00706             _IACTpos++;
00707             bsize--;
00708         }
00709     }
00710     delete[] src;
00711 }
00712 
00713 bool SmushDecoder::SmushAudioTrack::seek(const Audio::Timestamp &time) {
00714     return true;
00715 }
00716 
00717 void SmushDecoder::SmushAudioTrack::skipSamples(int sampleCount) {
00718     if (sampleCount <= 0)
00719         return;
00720 
00721     if (_queueStream->isStereo())
00722         sampleCount *= 2;
00723 
00724     int16 *tempBuffer = new int16[sampleCount];
00725     _queueStream->readBuffer(tempBuffer, sampleCount);
00726     delete[] tempBuffer;
00727 }
00728 
00729 
00730 } // end of namespace Grim


Generated on Sat Sep 14 2019 05:01:16 for ResidualVM by doxygen 1.7.1
curved edge   curved edge