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); }
const Graphics::Surface *SmackerDecoder::decodeNextFrame() { uint i; uint32 chunkSize = 0; uint32 dataSizeUnpacked = 0; uint32 startPos = _fileStream->pos(); _curFrame++; // Check if we got a frame with palette data, and // call back the virtual setPalette function to set // the current palette if (_frameTypes[_curFrame] & 1) { unpackPalette(); _dirtyPalette = true; } // Load audio tracks for (i = 0; i < 7; ++i) { if (!(_frameTypes[_curFrame] & (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[_curFrame] & ~3; // uint32 remainder = _frameSizes[_curFrame] & 3; if (_fileStream->pos() - startPos > frameSize) error("Smacker actual frame size exceeds recorded frame size"); uint32 frameDataSize = frameSize - (_fileStream->pos() - startPos); _frameData = (byte *)malloc(frameDataSize); _fileStream->read(_frameData, frameDataSize); BitStream bs(_frameData, frameDataSize); _MMapTree->reset(); _MClrTree->reset(); _FullTree->reset(); _TypeTree->reset(); // Height needs to be doubled if we have flags (Y-interlaced or Y-doubled) uint doubleY = _header.flags ? 2 : 1; uint bw = getWidth() / 4; uint bh = getHeight() / doubleY / 4; uint stride = getWidth(); uint block = 0, blocks = bw*bh; byte *out; uint type, run, j, mode; uint32 p1, p2, clr, map; byte hi, lo; while (block < blocks) { type = _TypeTree->getCode(bs); run = getBlockRun((type >> 2) & 0x3f); switch (type & 3) { case SMK_BLOCK_MONO: while (run-- && block < blocks) { clr = _MClrTree->getCode(bs); map = _MMapTree->getCode(bs); out = (byte *)_surface->pixels + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4; hi = clr >> 8; lo = clr & 0xff; for (i = 0; i < 4; i++) { for (j = 0; j < doubleY; j++) { out[0] = (map & 1) ? hi : lo; out[1] = (map & 2) ? hi : lo; out[2] = (map & 4) ? hi : lo; out[3] = (map & 8) ? hi : lo; out += stride; } map >>= 4; } ++block; } break; case SMK_BLOCK_FULL: // Smacker v2 has one mode, Smacker v4 has three if (_header.signature == MKTAG('S','M','K','2')) { mode = 0; } else { // 00 - mode 0 // 10 - mode 1 // 01 - mode 2 mode = 0; if (bs.getBit()) { mode = 1; } else if (bs.getBit()) { mode = 2; } } while (run-- && block < blocks) { out = (byte *)_surface->pixels + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4; switch (mode) { case 0: for (i = 0; i < 4; ++i) { p1 = _FullTree->getCode(bs); p2 = _FullTree->getCode(bs); for (j = 0; j < doubleY; ++j) { out[2] = p1 & 0xff; out[3] = p1 >> 8; out[0] = p2 & 0xff; out[1] = p2 >> 8; out += stride; } } break; case 1: p1 = _FullTree->getCode(bs); out[0] = out[1] = p1 & 0xFF; out[2] = out[3] = p1 >> 8; out += stride; out[0] = out[1] = p1 & 0xFF; out[2] = out[3] = p1 >> 8; out += stride; p2 = _FullTree->getCode(bs); out[0] = out[1] = p2 & 0xFF; out[2] = out[3] = p2 >> 8; out += stride; out[0] = out[1] = p2 & 0xFF; out[2] = out[3] = p2 >> 8; out += stride; break; case 2: for (i = 0; i < 2; i++) { // We first get p2 and then p1 // Check ffmpeg thread "[PATCH] Smacker video decoder bug fix" // http://article.gmane.org/gmane.comp.video.ffmpeg.devel/78768 p2 = _FullTree->getCode(bs); p1 = _FullTree->getCode(bs); for (j = 0; j < doubleY; ++j) { out[0] = p1 & 0xff; out[1] = p1 >> 8; out[2] = p2 & 0xff; out[3] = p2 >> 8; out += stride; } for (j = 0; j < doubleY; ++j) { out[0] = p1 & 0xff; out[1] = p1 >> 8; out[2] = p2 & 0xff; out[3] = p2 >> 8; out += stride; } } break; } ++block; } break; case SMK_BLOCK_SKIP: while (run-- && block < blocks) block++; break; case SMK_BLOCK_FILL: uint32 col; mode = type >> 8; while (run-- && block < blocks) { out = (byte *)_surface->pixels + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4; col = mode * 0x01010101; for (i = 0; i < 4 * doubleY; ++i) { out[0] = out[1] = out[2] = out[3] = col; out += stride; } ++block; } break; } } _fileStream->seek(startPos + frameSize); free(_frameData); if (_curFrame == 0) _startTime = g_system->getMillis(); return _surface; }