void SmackerDecoder::readNextPacket() { SmackerVideoTrack *videoTrack = (SmackerVideoTrack *)getTrack(0); if (videoTrack->endOfTrack()) return; videoTrack->increaseCurFrame(); uint i; uint32 chunkSize = 0; uint32 dataSizeUnpacked = 0; uint32 startPos = _fileStream->pos(); // Check if we got a frame with palette data, and // call back the virtual setPalette function to set // the current palette if (_frameTypes[videoTrack->getCurFrame()] & 1) videoTrack->unpackPalette(_fileStream); // Load audio tracks for (i = 0; i < 7; ++i) { if (!(_frameTypes[videoTrack->getCurFrame()] & (2 << i))) continue; chunkSize = _fileStream->readUint32LE(); chunkSize -= 4; // subtract the first 4 bytes (chunk size) if (_header.audioInfo[i].compression == kCompressionNone) { dataSizeUnpacked = chunkSize; } else { dataSizeUnpacked = _fileStream->readUint32LE(); chunkSize -= 4; // subtract the next 4 bytes (unpacked data size) } handleAudioTrack(i, chunkSize, dataSizeUnpacked); } uint32 frameSize = _frameSizes[videoTrack->getCurFrame()] & ~3; // uint32 remainder = _frameSizes[videoTrack->getCurFrame()] & 3; if (_fileStream->pos() - startPos > frameSize) error("Smacker actual frame size exceeds recorded frame size"); uint32 frameDataSize = frameSize - (_fileStream->pos() - startPos); byte *frameData = (byte *)malloc(frameDataSize + 1); // Padding to keep the BigHuffmanTrees from reading past the data end frameData[frameDataSize] = 0x00; _fileStream->read(frameData, frameDataSize); Common::BitStream8LSB bs(new Common::MemoryReadStream(frameData, frameDataSize + 1, DisposeAfterUse::YES), true); videoTrack->decodeFrame(bs); _fileStream->seek(startPos + frameSize); }
void NeverhoodSmackerDecoder::forceSeekToFrame(uint frame) { if (!isVideoLoaded()) return; if (frame >= getFrameCount()) error("Can't force Smacker seek to invalid frame %d", frame); if (_header.audioInfo[0].hasAudio) error("Can't force Smacker frame seek with audio"); if (!rewind()) error("Failed to rewind"); SmackerVideoTrack *videoTrack = (SmackerVideoTrack *)getTrack(0); uint32 offset = 0; for (uint32 i = 0; i < frame; i++) { videoTrack->increaseCurFrame(); offset += _frameSizes[i] & ~3; } _fileStream->seek(offset, SEEK_CUR); }
bool SmackerDecoder::loadStream(Common::SeekableReadStream *stream) { close(); _fileStream = stream; // Read in the Smacker header _header.signature = _fileStream->readUint32BE(); if (_header.signature != MKTAG('S', 'M', 'K', '2') && _header.signature != MKTAG('S', 'M', 'K', '4')) return false; uint32 width = _fileStream->readUint32LE(); uint32 height = _fileStream->readUint32LE(); uint32 frameCount = _fileStream->readUint32LE(); int32 frameDelay = _fileStream->readSint32LE(); // frame rate contains 2 digits after the comma, so 1497 is actually 14.97 fps Common::Rational frameRate; if (frameDelay > 0) frameRate = Common::Rational(1000, frameDelay); else if (frameDelay < 0) frameRate = Common::Rational(100000, -frameDelay); else frameRate = 1000; // Flags are determined by which bit is set, which can be one of the following: // 0 - set to 1 if file contains a ring frame. // 1 - set to 1 if file is Y-interlaced // 2 - set to 1 if file is Y-doubled // If bits 1 or 2 are set, the frame should be scaled to twice its height // before it is displayed. _header.flags = _fileStream->readUint32LE(); SmackerVideoTrack *videoTrack = createVideoTrack(width, height, frameCount, frameRate, _header.flags, _header.signature); addTrack(videoTrack); // TODO: should we do any extra processing for Smacker files with ring frames? // TODO: should we do any extra processing for Y-doubled videos? Are they the // same as Y-interlaced videos? uint32 i; for (i = 0; i < 7; ++i) _header.audioSize[i] = _fileStream->readUint32LE(); _header.treesSize = _fileStream->readUint32LE(); _header.mMapSize = _fileStream->readUint32LE(); _header.mClrSize = _fileStream->readUint32LE(); _header.fullSize = _fileStream->readUint32LE(); _header.typeSize = _fileStream->readUint32LE(); for (i = 0; i < 7; ++i) { // AudioRate - Frequency and format information for each sound track, up to 7 audio tracks. // The 32 constituent bits have the following meaning: // * bit 31 - indicates Huffman + DPCM compression // * bit 30 - indicates that audio data is present for this track // * bit 29 - 1 = 16-bit audio; 0 = 8-bit audio // * bit 28 - 1 = stereo audio; 0 = mono audio // * bit 27 - indicates Bink RDFT compression // * bit 26 - indicates Bink DCT compression // * bits 25-24 - unused // * bits 23-0 - audio sample rate uint32 audioInfo = _fileStream->readUint32LE(); _header.audioInfo[i].hasAudio = audioInfo & 0x40000000; _header.audioInfo[i].is16Bits = audioInfo & 0x20000000; _header.audioInfo[i].isStereo = audioInfo & 0x10000000; _header.audioInfo[i].sampleRate = audioInfo & 0xFFFFFF; if (audioInfo & 0x8000000) _header.audioInfo[i].compression = kCompressionRDFT; else if (audioInfo & 0x4000000) _header.audioInfo[i].compression = kCompressionDCT; else if (audioInfo & 0x80000000) _header.audioInfo[i].compression = kCompressionDPCM; else _header.audioInfo[i].compression = kCompressionNone; if (_header.audioInfo[i].hasAudio) { if (_header.audioInfo[i].compression == kCompressionRDFT || _header.audioInfo[i].compression == kCompressionDCT) warning("Unhandled Smacker v2 audio compression"); if (i == 0) addTrack(new SmackerAudioTrack(_header.audioInfo[i], _soundType)); } } _header.dummy = _fileStream->readUint32LE(); _frameSizes = new uint32[frameCount]; for (i = 0; i < frameCount; ++i) _frameSizes[i] = _fileStream->readUint32LE(); _frameTypes = new byte[frameCount]; for (i = 0; i < frameCount; ++i) _frameTypes[i] = _fileStream->readByte(); byte *huffmanTrees = (byte *) malloc(_header.treesSize); _fileStream->read(huffmanTrees, _header.treesSize); Common::BitStream8LSB bs(new Common::MemoryReadStream(huffmanTrees, _header.treesSize, DisposeAfterUse::YES), true); videoTrack->readTrees(bs, _header.mMapSize, _header.mClrSize, _header.fullSize, _header.typeSize); _firstFrameStart = _fileStream->pos(); return true; }