From 9a71eb1a6d819aac7aca2391a488942faf3e6f8f Mon Sep 17 00:00:00 2001 From: Cameron Cawley Date: Sat, 4 Jun 2022 18:32:59 +0100 Subject: [PATCH] VIDEO: Avoid 64-bit math in Smacker bitstreams --- common/bitstream.h | 65 +++++++++++++++++++++++-------------------- video/smk_decoder.cpp | 30 ++++++++++---------- video/smk_decoder.h | 9 ++++-- 3 files changed, 57 insertions(+), 47 deletions(-) diff --git a/common/bitstream.h b/common/bitstream.h index d8d3c12a751..f1b1f5400cd 100644 --- a/common/bitstream.h +++ b/common/bitstream.h @@ -51,13 +51,13 @@ namespace Common { * for valueBits, isLE and isMSB2LSB, reads 32-bit little-endian values * from the data stream and hands out the bits in the order of LSB to MSB. */ -template +template class BitStreamImpl { private: STREAM *_stream; //!< The input stream. DisposeAfterUse::Flag _disposeAfterUse; //!< Whether to delete the stream on destruction. - uint64 _bitContainer; //!< The currently available bits. + CONTAINER _bitContainer; //!< The currently available bits. uint8 _bitsLeft; //!< Number of bits currently left in the bit container. uint32 _size; //!< Total bit stream size (in bits). uint32 _pos; //!< Current bit stream position (in bits). @@ -88,7 +88,7 @@ private: FORCEINLINE void fillContainer(size_t min) { while (_bitsLeft < min) { - uint64 data; + CONTAINER data; if (_pos + _bitsLeft + valueBits <= _size) { data = readData(); } else { @@ -102,7 +102,7 @@ private: // Move the data value to the right position in the bit container if (MSB2LSB) - _bitContainer |= data << (64 - valueBits - _bitsLeft); + _bitContainer |= data << ((sizeof(_bitContainer) * 8) - valueBits - _bitsLeft); else _bitContainer |= data << _bitsLeft; @@ -111,11 +111,11 @@ private: } /** Get @p n bits from the bit container. */ - FORCEINLINE static uint32 getNBits(uint64 value, size_t n) { + FORCEINLINE static uint32 getNBits(CONTAINER value, size_t n) { if (n == 0) return 0; - const size_t toShift = 64 - n; + const size_t toShift = (sizeof(value) * 8) - n; if (MSB2LSB) return value >> toShift; @@ -281,6 +281,11 @@ public: /** Skip the specified number of bits. */ void skip(uint32 n) { + if (n >= _bitsLeft) { + n -= _bitsLeft; + skipBits(_bitsLeft); + } + while (n > 32) { fillContainer(32); skipBits(32); @@ -300,12 +305,12 @@ public: } /** Return the stream position in bits. */ - uint64 pos() const { + uint32 pos() const { return _pos; } /** Return the stream size in bits. */ - uint64 size() const { + uint32 size() const { return _size; } @@ -360,11 +365,11 @@ public: return false; } - int64 pos() const { + uint32 pos() const { return _pos; } - int64 size() const { + uint32 size() const { return _size; } @@ -469,52 +474,52 @@ public: */ /** 8-bit data, MSB to LSB. */ -typedef BitStreamImpl BitStream8MSB; +typedef BitStreamImpl BitStream8MSB; /** 8-bit data, LSB to MSB. */ -typedef BitStreamImpl BitStream8LSB; +typedef BitStreamImpl BitStream8LSB; /** 16-bit little-endian data, MSB to LSB. */ -typedef BitStreamImpl BitStream16LEMSB; +typedef BitStreamImpl BitStream16LEMSB; /** 16-bit little-endian data, LSB to MSB. */ -typedef BitStreamImpl BitStream16LELSB; +typedef BitStreamImpl BitStream16LELSB; /** 16-bit big-endian data, MSB to LSB. */ -typedef BitStreamImpl BitStream16BEMSB; +typedef BitStreamImpl BitStream16BEMSB; /** 16-bit big-endian data, LSB to MSB. */ -typedef BitStreamImpl BitStream16BELSB; +typedef BitStreamImpl BitStream16BELSB; /** 32-bit little-endian data, MSB to LSB. */ -typedef BitStreamImpl BitStream32LEMSB; +typedef BitStreamImpl BitStream32LEMSB; /** 32-bit little-endian data, LSB to MSB. */ -typedef BitStreamImpl BitStream32LELSB; +typedef BitStreamImpl BitStream32LELSB; /** 32-bit big-endian data, MSB to LSB. */ -typedef BitStreamImpl BitStream32BEMSB; +typedef BitStreamImpl BitStream32BEMSB; /** 32-bit big-endian data, LSB to MSB. */ -typedef BitStreamImpl BitStream32BELSB; +typedef BitStreamImpl BitStream32BELSB; /** 8-bit data, MSB to LSB. */ -typedef BitStreamImpl BitStreamMemory8MSB; +typedef BitStreamImpl BitStreamMemory8MSB; /** 8-bit data, LSB to MSB. */ -typedef BitStreamImpl BitStreamMemory8LSB; +typedef BitStreamImpl BitStreamMemory8LSB; /** 16-bit little-endian data, MSB to LSB. */ -typedef BitStreamImpl BitStreamMemory16LEMSB; +typedef BitStreamImpl BitStreamMemory16LEMSB; /** 16-bit little-endian data, LSB to MSB. */ -typedef BitStreamImpl BitStreamMemory16LELSB; +typedef BitStreamImpl BitStreamMemory16LELSB; /** 16-bit big-endian data, MSB to LSB. */ -typedef BitStreamImpl BitStreamMemory16BEMSB; +typedef BitStreamImpl BitStreamMemory16BEMSB; /** 16-bit big-endian data, LSB to MSB. */ -typedef BitStreamImpl BitStreamMemory16BELSB; +typedef BitStreamImpl BitStreamMemory16BELSB; /** 32-bit little-endian data, MSB to LSB. */ -typedef BitStreamImpl BitStreamMemory32LEMSB; +typedef BitStreamImpl BitStreamMemory32LEMSB; /** 32-bit little-endian data, LSB to MSB. */ -typedef BitStreamImpl BitStreamMemory32LELSB; +typedef BitStreamImpl BitStreamMemory32LELSB; /** 32-bit big-endian data, MSB to LSB. */ -typedef BitStreamImpl BitStreamMemory32BEMSB; +typedef BitStreamImpl BitStreamMemory32BEMSB; /** 32-bit big-endian data, LSB to MSB. */ -typedef BitStreamImpl BitStreamMemory32BELSB; +typedef BitStreamImpl BitStreamMemory32BELSB; /** @} */ diff --git a/video/smk_decoder.cpp b/video/smk_decoder.cpp index e918f23d118..5f6f096d2d5 100644 --- a/video/smk_decoder.cpp +++ b/video/smk_decoder.cpp @@ -53,9 +53,9 @@ enum SmkBlockTypes { class SmallHuffmanTree { public: - SmallHuffmanTree(Common::BitStreamMemory8LSB &bs); + SmallHuffmanTree(SmackerBitStream &bs); - uint16 getCode(Common::BitStreamMemory8LSB &bs); + uint16 getCode(SmackerBitStream &bs); private: enum { SMK_NODE = 0x8000 @@ -69,11 +69,11 @@ private: uint16 _prefixtree[256]; byte _prefixlength[256]; - Common::BitStreamMemory8LSB &_bs; + SmackerBitStream &_bs; bool _empty; }; -SmallHuffmanTree::SmallHuffmanTree(Common::BitStreamMemory8LSB &bs) +SmallHuffmanTree::SmallHuffmanTree(SmackerBitStream &bs) : _treeSize(0), _bs(bs), _empty(false) { if (!_bs.getBit()) { _empty = true; @@ -122,7 +122,7 @@ uint16 SmallHuffmanTree::decodeTree(uint32 prefix, int length) { return r1+r2+1; } -uint16 SmallHuffmanTree::getCode(Common::BitStreamMemory8LSB &bs) { +uint16 SmallHuffmanTree::getCode(SmackerBitStream &bs) { if (_empty) return 0; @@ -149,11 +149,11 @@ uint16 SmallHuffmanTree::getCode(Common::BitStreamMemory8LSB &bs) { class BigHuffmanTree { public: - BigHuffmanTree(Common::BitStreamMemory8LSB &bs, int allocSize); + BigHuffmanTree(SmackerBitStream &bs, int allocSize); ~BigHuffmanTree(); void reset(); - uint32 getCode(Common::BitStreamMemory8LSB &bs); + uint32 getCode(SmackerBitStream &bs); private: enum { SMK_NODE = 0x80000000 @@ -169,13 +169,13 @@ private: byte _prefixlength[256]; /* Used during construction */ - Common::BitStreamMemory8LSB &_bs; + SmackerBitStream &_bs; uint32 _markers[3]; SmallHuffmanTree *_loBytes; SmallHuffmanTree *_hiBytes; }; -BigHuffmanTree::BigHuffmanTree(Common::BitStreamMemory8LSB &bs, int allocSize) +BigHuffmanTree::BigHuffmanTree(SmackerBitStream &bs, int allocSize) : _bs(bs) { uint32 bit = _bs.getBit(); if (!bit) { @@ -265,7 +265,7 @@ uint32 BigHuffmanTree::decodeTree(uint32 prefix, int length) { return r1+r2+1; } -uint32 BigHuffmanTree::getCode(Common::BitStreamMemory8LSB &bs) { +uint32 BigHuffmanTree::getCode(SmackerBitStream &bs) { // Peeking data out of bounds is well-defined and returns 0 bits. // This is for convenience when using speed-up techniques reading // more bits than actually available. @@ -413,7 +413,7 @@ bool SmackerDecoder::loadStream(Common::SeekableReadStream *stream) { byte *huffmanTrees = (byte *) malloc(_header.treesSize); _fileStream->read(huffmanTrees, _header.treesSize); - Common::BitStreamMemory8LSB bs(new Common::BitStreamMemoryStream(huffmanTrees, _header.treesSize, DisposeAfterUse::YES), DisposeAfterUse::YES); + SmackerBitStream bs(new Common::BitStreamMemoryStream(huffmanTrees, _header.treesSize, DisposeAfterUse::YES), DisposeAfterUse::YES); videoTrack->readTrees(bs, _header.mMapSize, _header.mClrSize, _header.fullSize, _header.typeSize); _firstFrameStart = _fileStream->pos(); @@ -539,7 +539,7 @@ void SmackerDecoder::readNextPacket() { _fileStream->read(frameData, frameDataSize); - Common::BitStreamMemory8LSB bs(new Common::BitStreamMemoryStream(frameData, frameDataSize + 1, DisposeAfterUse::YES), DisposeAfterUse::YES); + SmackerBitStream bs(new Common::BitStreamMemoryStream(frameData, frameDataSize + 1, DisposeAfterUse::YES), DisposeAfterUse::YES); videoTrack->decodeFrame(bs); _fileStream->seek(startPos + frameSize); @@ -624,14 +624,14 @@ Graphics::PixelFormat SmackerDecoder::SmackerVideoTrack::getPixelFormat() const return _surface->format; } -void SmackerDecoder::SmackerVideoTrack::readTrees(Common::BitStreamMemory8LSB &bs, uint32 mMapSize, uint32 mClrSize, uint32 fullSize, uint32 typeSize) { +void SmackerDecoder::SmackerVideoTrack::readTrees(SmackerBitStream &bs, uint32 mMapSize, uint32 mClrSize, uint32 fullSize, uint32 typeSize) { _MMapTree = new BigHuffmanTree(bs, mMapSize); _MClrTree = new BigHuffmanTree(bs, mClrSize); _FullTree = new BigHuffmanTree(bs, fullSize); _TypeTree = new BigHuffmanTree(bs, typeSize); } -void SmackerDecoder::SmackerVideoTrack::decodeFrame(Common::BitStreamMemory8LSB &bs) { +void SmackerDecoder::SmackerVideoTrack::decodeFrame(SmackerBitStream &bs) { _MMapTree->reset(); _MClrTree->reset(); _FullTree->reset(); @@ -853,7 +853,7 @@ Audio::AudioStream *SmackerDecoder::SmackerAudioTrack::getAudioStream() const { } void SmackerDecoder::SmackerAudioTrack::queueCompressedBuffer(byte *buffer, uint32 bufferSize, uint32 unpackedSize) { - Common::BitStreamMemory8LSB audioBS(new Common::BitStreamMemoryStream(buffer, bufferSize), DisposeAfterUse::YES); + SmackerBitStream audioBS(new Common::BitStreamMemoryStream(buffer, bufferSize), DisposeAfterUse::YES); bool dataPresent = audioBS.getBit(); if (!dataPresent) diff --git a/video/smk_decoder.h b/video/smk_decoder.h index 3b1f910b5e8..c913daebab8 100644 --- a/video/smk_decoder.h +++ b/video/smk_decoder.h @@ -43,6 +43,11 @@ namespace Video { class BigHuffmanTree; +// Because the maximum number of bits read from a bitstream is 16, and the data is 8-bit, the container only +// needs to hold up to 23 bits at any given time. As such, we use a bitstream with a 32-bit container to +// avoid the overhead of 64-bit maths on systems that don't support it natively. +typedef Common::BitStreamImpl SmackerBitStream; + /** * Decoder for Smacker v2/v4 videos. * @@ -100,9 +105,9 @@ protected: const byte *getPalette() const { _dirtyPalette = false; return _palette; } bool hasDirtyPalette() const { return _dirtyPalette; } - void readTrees(Common::BitStreamMemory8LSB &bs, uint32 mMapSize, uint32 mClrSize, uint32 fullSize, uint32 typeSize); + void readTrees(SmackerBitStream &bs, uint32 mMapSize, uint32 mClrSize, uint32 fullSize, uint32 typeSize); void increaseCurFrame() { _curFrame++; } - void decodeFrame(Common::BitStreamMemory8LSB &bs); + void decodeFrame(SmackerBitStream &bs); void unpackPalette(Common::SeekableReadStream *stream); Common::Rational getFrameRate() const { return _frameRate; }