[Scummvm-git-logs] scummvm master -> 6352de724adc52cafd814e58c7c9df36827c5d83
lephilousophe
noreply at scummvm.org
Sat Jan 8 21:25:15 UTC 2022
This automated email contains information about 4 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
c6383c472e VIDEO: Rework HNM decoder to make it handle different versions of format
97b474e78a VIDEO: Fix uint usage
3f01f8301c VIDEO: Fix audio stereo decoding.
6352de724a VIDEO: Implement UBB2 (aka HNM5) decoding
Commit: c6383c472e6bc74f1f252900043586264773e9cc
https://github.com/scummvm/scummvm/commit/c6383c472e6bc74f1f252900043586264773e9cc
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2022-01-08T22:24:14+01:00
Commit Message:
VIDEO: Rework HNM decoder to make it handle different versions of format
For now nothing new: HNM4(A) format is supported and HNM5 has a
placeholder.
Changed paths:
video/hnm_decoder.cpp
video/hnm_decoder.h
diff --git a/video/hnm_decoder.cpp b/video/hnm_decoder.cpp
index 6e06cdc8e5e..0fa42d7ce65 100644
--- a/video/hnm_decoder.cpp
+++ b/video/hnm_decoder.cpp
@@ -53,7 +53,8 @@ bool HNMDecoder::loadStream(Common::SeekableReadStream *stream) {
uint32 tag = stream->readUint32BE();
/* For now, only HNM4 and UBB2, HNM6 in the future */
- if (tag != MKTAG('H', 'N', 'M', '4') && tag != MKTAG('U', 'B', 'B', '2')) {
+ if (tag != MKTAG('H', 'N', 'M', '4') &&
+ tag != MKTAG('U', 'B', 'B', '2')) {
close();
return false;
}
@@ -81,19 +82,32 @@ bool HNMDecoder::loadStream(Common::SeekableReadStream *stream) {
frameCount = 0;
}
- _videoTrack = new HNM4VideoTrack(width, height, frameSize, frameCount, _regularFrameDelay,
- _initialPalette);
- addTrack(_videoTrack);
- if (tag == MKTAG('H', 'N', 'M', '4') && soundFormat == 2 && soundBits != 0) {
- // HNM4 is Mono 22050Hz
- _audioTrack = new DPCMAudioTrack(soundFormat, soundBits, 22050, false, getSoundType());
- addTrack(_audioTrack);
- } else if (tag == MKTAG('U', 'B', 'B', '2') && soundFormat == 2 && soundBits == 0) {
- // UBB2 is Stereo 22050Hz
- _audioTrack = new DPCMAudioTrack(soundFormat, 16, 22050, true, getSoundType());
- addTrack(_audioTrack);
+ _videoTrack = nullptr;
+ _audioTrack = nullptr;
+ if (tag == MKTAG('H', 'N', 'M', '4')) {
+ _videoTrack = new HNM4VideoTrack(width, height, frameSize, frameCount, _regularFrameDelay,
+ _initialPalette);
+ if (soundFormat == 2 && soundBits != 0) {
+ // HNM4 is Mono 22050Hz
+ _audioTrack = new DPCMAudioTrack(soundFormat, soundBits, 22050, false, getSoundType());
+ }
+ } else if (tag == MKTAG('U', 'B', 'B', '2')) {
+ _videoTrack = new HNM5VideoTrack(width, height, frameSize, frameCount, _regularFrameDelay,
+ _initialPalette);
+ if (soundFormat == 2 && soundBits == 0) {
+ // UBB2 is Stereo 22050Hz
+ _audioTrack = new DPCMAudioTrack(soundFormat, 16, 22050, true, getSoundType());
+ }
} else {
- _audioTrack = nullptr;
+ // We should never be here
+ close();
+ return false;
+ }
+ if (_videoTrack) {
+ addTrack(_videoTrack);
+ }
+ if (_audioTrack) {
+ addTrack(_audioTrack);
}
_stream = stream;
@@ -132,23 +146,7 @@ void HNMDecoder::readNextPacket() {
uint16 chunkType = _stream->readUint16BE();
uint16 flags = _stream->readUint16LE();
- if (chunkType == MKTAG16('P', 'L')) {
- _videoTrack->decodePalette(_stream, chunkSize - 8);
- } else if (chunkType == MKTAG16('I', 'Z')) {
- _stream->skip(4);
- _videoTrack->decodeIntraframe(_stream, chunkSize - 8 - 4);
- _videoTrack->presentFrame(flags);
- } else if (chunkType == MKTAG16('I', 'U')) {
- if ((flags & 1) == 1) {
- _videoTrack->decodeInterframeA(_stream, chunkSize - 8);
- } else {
- _videoTrack->decodeInterframe(_stream, chunkSize - 8);
- }
- _videoTrack->presentFrame(flags);
- } else if (chunkType == MKTAG16('I', 'V')) {
- _videoTrack->decodeInterframeIV(_stream, chunkSize - 8);
- _videoTrack->presentFrame(flags);
- } else if (chunkType == MKTAG16('S', 'D')) {
+ if (chunkType == MKTAG16('S', 'D')) {
if (_audioTrack) {
Audio::Timestamp duration = _audioTrack->decodeSound(_stream, chunkSize - 8);
_videoTrack->setFrameDelay(duration.msecs());
@@ -157,20 +155,33 @@ void HNMDecoder::readNextPacket() {
_stream->skip(chunkSize - 8);
}
} else {
- error("Got %d chunk: size %d", chunkType, chunkSize);
+ _videoTrack->decodeChunk(_stream, chunkSize - 8, chunkType, flags);
}
superchunkRemaining -= chunkSize;
}
}
-HNMDecoder::HNM4VideoTrack::HNM4VideoTrack(uint32 width, uint32 height, uint32 frameSize,
- uint32 frameCount, uint32 regularFrameDelay, const byte *initialPalette) :
- _frameCount(frameCount), _regularFrameDelay(regularFrameDelay), _nextFrameStartTime(0) {
-
+HNMDecoder::HNMVideoTrack::HNMVideoTrack(uint32 frameCount, uint32 regularFrameDelay) :
+ _frameCount(frameCount), _curFrame(-1),
+ _regularFrameDelay(regularFrameDelay), _nextFrameStartTime(0) {
restart();
+}
+
+void HNMDecoder::HNMVideoTrack::setFrameDelay(uint32 frameDelay) {
+ if (_nextFrameDelay == uint32(-1)) {
+ _nextFrameDelay = frameDelay;
+ } else if (_nextNextFrameDelay == uint32(-1)) {
+ _nextNextFrameDelay = frameDelay;
+ } else {
+ _nextNextFrameDelay += frameDelay;
+ }
+}
+
+HNMDecoder::HNM45VideoTrack::HNM45VideoTrack(uint32 width, uint32 height, uint32 frameSize,
+ uint32 frameCount, uint32 regularFrameDelay, const byte *initialPalette) :
+ HNMVideoTrack(frameCount, regularFrameDelay) {
- _curFrame = -1;
// Get the currently loaded palette for undefined colors
if (initialPalette) {
memcpy(_palette, initialPalette, 256 * 3);
@@ -183,37 +194,23 @@ HNMDecoder::HNM4VideoTrack::HNM4VideoTrack(uint32 width, uint32 height, uint32 f
error("Invalid frameSize: expected %d, got %d", width * height, frameSize);
}
- _frameBufferF = new byte[frameSize]();
_frameBufferC = new byte[frameSize]();
_frameBufferP = new byte[frameSize]();
- // We will use _frameBufferF/C/P as the surface pixels, just init there with nullptr to avoid unintended usage of surface
+ // We will use _frameBufferC/P as the surface pixels, just init there with nullptr to avoid unintended usage of surface
const Graphics::PixelFormat &f = Graphics::PixelFormat::createFormatCLUT8();
_surface.init(width, height, width * f.bytesPerPixel, nullptr, f);
}
-HNMDecoder::HNM4VideoTrack::~HNM4VideoTrack() {
+HNMDecoder::HNM45VideoTrack::~HNM45VideoTrack() {
// Don't free _surface as we didn't used create() but init()
- delete[] _frameBufferF;
- _frameBufferF = nullptr;
delete[] _frameBufferC;
_frameBufferC = nullptr;
delete[] _frameBufferP;
_frameBufferP = nullptr;
}
-void HNMDecoder::HNM4VideoTrack::setFrameDelay(uint32 frameDelay) {
- if (_nextFrameDelay == uint32(-1)) {
- _nextFrameDelay = frameDelay;
- } else if (_nextNextFrameDelay == uint32(-1)) {
- _nextNextFrameDelay = frameDelay;
- } else {
- _nextNextFrameDelay += frameDelay;
- }
-}
-
-
-void HNMDecoder::HNM4VideoTrack::decodePalette(Common::SeekableReadStream *stream, uint32 size) {
+void HNMDecoder::HNM45VideoTrack::decodePalette(Common::SeekableReadStream *stream, uint32 size) {
while (true) {
if (size < 2) {
break;
@@ -254,7 +251,41 @@ void HNMDecoder::HNM4VideoTrack::decodePalette(Common::SeekableReadStream *strea
}
}
-void HNMDecoder::HNM4VideoTrack::decodeInterframe(Common::SeekableReadStream *stream, uint32 size) {
+HNMDecoder::HNM4VideoTrack::HNM4VideoTrack(uint32 width, uint32 height, uint32 frameSize,
+ uint32 frameCount, uint32 regularFrameDelay, const byte *initialPalette) :
+ HNM45VideoTrack(width, height, frameSize, frameCount, regularFrameDelay, initialPalette) {
+
+ _frameBufferF = new byte[frameSize]();
+}
+
+HNMDecoder::HNM4VideoTrack::~HNM4VideoTrack() {
+ // Don't free _surface as we didn't used create() but init()
+ delete[] _frameBufferF;
+ _frameBufferF = nullptr;
+}
+
+void HNMDecoder::HNM4VideoTrack::decodeChunk(Common::SeekableReadStream *stream, uint32 size,
+ uint16 chunkType, uint16 flags) {
+ if (chunkType == MKTAG16('P', 'L')) {
+ decodePalette(stream, size);
+ } else if (chunkType == MKTAG16('I', 'Z')) {
+ stream->skip(4);
+ decodeIntraframe(stream, size - 4);
+ presentFrame(flags);
+ } else if (chunkType == MKTAG16('I', 'U')) {
+ if ((flags & 1) == 1) {
+ decodeInterframeA(stream, size);
+ } else {
+ decodeInterframe(stream, size);
+ }
+ presentFrame(flags);
+ } else {
+ error("HNM4: Got %d chunk: size %d", chunkType, size);
+ }
+}
+
+void HNMDecoder::HNM4VideoTrack::decodeInterframe(
+ Common::SeekableReadStream *stream, uint32 size) {
SWAP(_frameBufferC, _frameBufferP);
uint16 width = _surface.w;
@@ -353,8 +384,9 @@ void HNMDecoder::HNM4VideoTrack::decodeInterframe(Common::SeekableReadStream *st
shft1 = 0;
shft2 = 1;
}
- if (swap)
+ if (swap) {
SWAP(shft1, shft2);
+ }
int src_inc = backward ? -2 : 2;
@@ -373,7 +405,8 @@ void HNMDecoder::HNM4VideoTrack::decodeInterframe(Common::SeekableReadStream *st
}
}
-void HNMDecoder::HNM4VideoTrack::decodeInterframeA(Common::SeekableReadStream *stream, uint32 size) {
+void HNMDecoder::HNM4VideoTrack::decodeInterframeA(
+ Common::SeekableReadStream *stream, uint32 size) {
SWAP(_frameBufferC, _frameBufferP);
uint16 width = _surface.w;
@@ -465,16 +498,6 @@ void HNMDecoder::HNM4VideoTrack::decodeInterframeA(Common::SeekableReadStream *s
}
}
-void HNMDecoder::HNM4VideoTrack::decodeInterframeIV(Common::SeekableReadStream *stream, uint32 size) {
- SWAP(_frameBufferC, _frameBufferP);
-
- // TODO: Implement this
-
- if (size > 0) {
- stream->skip(size);
- }
-}
-
void HNMDecoder::HNM4VideoTrack::decodeIntraframe(Common::SeekableReadStream *stream, uint32 size) {
Image::HLZDecoder::decodeFrameInPlace(*stream, size, _frameBufferC);
memcpy(_frameBufferP, _frameBufferC, (uint)_surface.w * (uint)_surface.h);
@@ -500,11 +523,15 @@ void HNMDecoder::HNM4VideoTrack::presentFrame(uint16 flags) {
uint32 p4 = *input++;
#ifndef SCUMM_LITTLE_ENDIAN
- *line0++ = ((p4 & 0xFF00) >> 8) | ((p4 & 0xFF000000) >> 16) | ((p0 & 0xFF00) << 8) | (p0 & 0xFF000000);
- *line1++ = ((p0 & 0xFF0000) << 8) | ((p0 & 0xFF) << 16) | ((p4 & 0xFF0000) >> 8) | (p4 & 0xFF);
+ *line0++ = ((p4 & 0xFF00) >> 8) | ((p4 & 0xFF000000) >> 16) |
+ ((p0 & 0xFF00) << 8) | (p0 & 0xFF000000);
+ *line1++ = ((p0 & 0xFF0000) << 8) | ((p0 & 0xFF) << 16) |
+ ((p4 & 0xFF0000) >> 8) | (p4 & 0xFF);
#else
- *line0++ = (p0 & 0xFF) | ((p0 & 0xFF0000) >> 8) | ((p4 & 0xFF) << 16) | ((p4 & 0xFF0000) << 8);
- *line1++ = ((p0 & 0xFF00) >> 8) | ((p0 & 0xFF000000) >> 16) | ((p4 & 0xFF00) << 8) | (p4 & 0xFF000000);
+ *line0++ = (p0 & 0xFF) | ((p0 & 0xFF0000) >> 8) |
+ ((p4 & 0xFF) << 16) | ((p4 & 0xFF0000) << 8);
+ *line1++ = ((p0 & 0xFF00) >> 8) | ((p0 & 0xFF000000) >> 16) |
+ ((p4 & 0xFF00) << 8) | (p4 & 0xFF000000);
#endif
}
line0 += width / 4;
@@ -522,6 +549,27 @@ void HNMDecoder::HNM4VideoTrack::presentFrame(uint16 flags) {
_nextNextFrameDelay = uint32(-1);
}
+void HNMDecoder::HNM5VideoTrack::decodeChunk(Common::SeekableReadStream *stream, uint32 size,
+ uint16 chunkType, uint16 flags) {
+ if (chunkType == MKTAG16('P', 'L')) {
+ decodePalette(stream, size);
+ } else if (chunkType == MKTAG16('I', 'V')) {
+ decodeFrame(stream, size);
+ } else {
+ error("HNM5: Got %d chunk: size %d", chunkType, size);
+ }
+}
+
+void HNMDecoder::HNM5VideoTrack::decodeFrame(Common::SeekableReadStream *stream, uint32 size) {
+ SWAP(_frameBufferC, _frameBufferP);
+
+ // TODO: Implement this
+
+ if (size > 0) {
+ stream->skip(size);
+ }
+}
+
HNMDecoder::DPCMAudioTrack::DPCMAudioTrack(uint16 format, uint16 bits, uint sampleRate, bool stereo,
Audio::Mixer::SoundType soundType) : AudioTrack(soundType), _audioStream(nullptr),
_gotLUT(false), _lastSample(0), _sampleRate(sampleRate), _stereo(stereo) {
@@ -539,8 +587,8 @@ HNMDecoder::DPCMAudioTrack::~DPCMAudioTrack() {
delete _audioStream;
}
-Audio::Timestamp HNMDecoder::DPCMAudioTrack::decodeSound(Common::SeekableReadStream *stream,
- uint32 size) {
+Audio::Timestamp HNMDecoder::DPCMAudioTrack::decodeSound(
+ Common::SeekableReadStream *stream, uint32 size) {
if (!_gotLUT) {
if (size < 256 * sizeof(*_lut)) {
error("Invalid first sound chunk");
diff --git a/video/hnm_decoder.h b/video/hnm_decoder.h
index 11796e06305..ddf360a14b0 100644
--- a/video/hnm_decoder.h
+++ b/video/hnm_decoder.h
@@ -52,38 +52,23 @@ public:
void setRegularFrameDelay(uint32 regularFrameDelay) { _regularFrameDelay = regularFrameDelay; }
private:
- class HNM4VideoTrack : public VideoTrack {
+ class HNMVideoTrack : public VideoTrack {
public:
- HNM4VideoTrack(uint32 width, uint32 height, uint32 frameSize, uint32 frameCount,
- uint32 regularFrameDelay, const byte *initialPalette = nullptr);
- ~HNM4VideoTrack() override;
+ HNMVideoTrack(uint32 frameCount, uint32 regularFrameDelay);
// When _frameCount is 0, it means we are looping
bool endOfTrack() const override { return (_frameCount == 0) ? false : VideoTrack::endOfTrack(); }
- uint16 getWidth() const override { return _surface.w; }
- uint16 getHeight() const override { return _surface.h; }
- Graphics::PixelFormat getPixelFormat() const override { return _surface.format; }
int getCurFrame() const override { return _curFrame; }
int getFrameCount() const override { return _frameCount; }
uint32 getNextFrameStartTime() const override { return _nextFrameStartTime; }
- const Graphics::Surface *decodeNextFrame() override { return &_surface; }
- const byte *getPalette() const override { _dirtyPalette = false; return _palette; }
- bool hasDirtyPalette() const override { return _dirtyPalette; }
-
- /** Decode a video chunk. */
- void decodePalette(Common::SeekableReadStream *stream, uint32 size);
- void decodeInterframe(Common::SeekableReadStream *stream, uint32 size);
- void decodeInterframeA(Common::SeekableReadStream *stream, uint32 size);
- void decodeInterframeIV(Common::SeekableReadStream *stream, uint32 size);
- void decodeIntraframe(Common::SeekableReadStream *stream, uint32 size);
- void presentFrame(uint16 flags);
void restart() { _nextFrameDelay = uint32(-1); _nextNextFrameDelay = uint32(-1); }
void setFrameDelay(uint32 frameDelay);
- private:
- Graphics::Surface _surface;
+ virtual void decodeChunk(Common::SeekableReadStream *stream, uint32 size,
+ uint16 chunkType, uint16 flags) = 0;
+ protected:
uint32 _regularFrameDelay;
uint32 _nextFrameDelay;
uint32 _nextNextFrameDelay;
@@ -91,15 +76,69 @@ private:
uint32 _frameCount;
int _curFrame;
+ };
+
+ class HNM45VideoTrack : public HNMVideoTrack {
+ public:
+ // When _frameCount is 0, it means we are looping
+ uint16 getWidth() const override { return _surface.w; }
+ uint16 getHeight() const override { return _surface.h; }
+ Graphics::PixelFormat getPixelFormat() const override { return _surface.format; }
+ const Graphics::Surface *decodeNextFrame() override { return &_surface; }
+ const byte *getPalette() const override { _dirtyPalette = false; return _palette; }
+ bool hasDirtyPalette() const override { return _dirtyPalette; }
+
+ protected:
+ HNM45VideoTrack(uint32 width, uint32 height, uint32 frameSize, uint32 frameCount,
+ uint32 regularFrameDelay, const byte *initialPalette = nullptr);
+ ~HNM45VideoTrack() override;
+
+ /** Decode a video chunk. */
+ void decodePalette(Common::SeekableReadStream *stream, uint32 size);
+
+ Graphics::Surface _surface;
byte _palette[256 * 3];
mutable bool _dirtyPalette;
- byte *_frameBufferF;
byte *_frameBufferC;
byte *_frameBufferP;
};
+ class HNM4VideoTrack : public HNM45VideoTrack {
+ public:
+ HNM4VideoTrack(uint32 width, uint32 height, uint32 frameSize, uint32 frameCount,
+ uint32 regularFrameDelay, const byte *initialPalette = nullptr);
+ ~HNM4VideoTrack() override;
+
+ /** Decode a video chunk. */
+ void decodeChunk(Common::SeekableReadStream *stream, uint32 size,
+ uint16 chunkType, uint16 flags) override;
+
+ protected:
+ /* Really decode */
+ void decodeInterframe(Common::SeekableReadStream *stream, uint32 size);
+ void decodeInterframeA(Common::SeekableReadStream *stream, uint32 size);
+ void decodeIntraframe(Common::SeekableReadStream *stream, uint32 size);
+ void presentFrame(uint16 flags);
+
+ byte *_frameBufferF;
+ };
+
+ class HNM5VideoTrack : public HNM45VideoTrack {
+ public:
+ HNM5VideoTrack(uint32 width, uint32 height, uint32 frameSize, uint32 frameCount,
+ uint32 regularFrameDelay, const byte *initialPalette = nullptr) :
+ HNM45VideoTrack(width, height, frameSize, frameCount, regularFrameDelay, initialPalette) {}
+ /** Decode a video chunk. */
+ void decodeChunk(Common::SeekableReadStream *stream, uint32 size,
+ uint16 chunkType, uint16 flags) override;
+
+ protected:
+ /** Really decode */
+ void decodeFrame(Common::SeekableReadStream *stream, uint32 size);
+ };
+
class DPCMAudioTrack : public AudioTrack {
public:
DPCMAudioTrack(uint16 format, uint16 bits, uint sampleRate, bool stereo,
@@ -123,7 +162,7 @@ private:
uint32 _regularFrameDelay;
// These two pointer are owned by VideoDecoder
- HNM4VideoTrack *_videoTrack;
+ HNMVideoTrack *_videoTrack;
DPCMAudioTrack *_audioTrack;
Common::SeekableReadStream *_stream;
Commit: 97b474e78a2608b5fe74d5790c63270fa26cf085
https://github.com/scummvm/scummvm/commit/97b474e78a2608b5fe74d5790c63270fa26cf085
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2022-01-08T22:24:14+01:00
Commit Message:
VIDEO: Fix uint usage
unisgned int is guaranteed to be at least 16 bits not more
Changed paths:
video/hnm_decoder.cpp
diff --git a/video/hnm_decoder.cpp b/video/hnm_decoder.cpp
index 0fa42d7ce65..9e73349ec68 100644
--- a/video/hnm_decoder.cpp
+++ b/video/hnm_decoder.cpp
@@ -291,7 +291,7 @@ void HNMDecoder::HNM4VideoTrack::decodeInterframe(
uint16 width = _surface.w;
bool eop = false;
- uint currentPos = 0;
+ uint32 currentPos = 0;
while (!eop) {
if (size < 1) {
@@ -412,7 +412,7 @@ void HNMDecoder::HNM4VideoTrack::decodeInterframeA(
uint16 width = _surface.w;
bool eop = false;
- uint currentPos = 0;
+ uint32 currentPos = 0;
while (!eop) {
if (size < 1) {
@@ -500,7 +500,7 @@ void HNMDecoder::HNM4VideoTrack::decodeInterframeA(
void HNMDecoder::HNM4VideoTrack::decodeIntraframe(Common::SeekableReadStream *stream, uint32 size) {
Image::HLZDecoder::decodeFrameInPlace(*stream, size, _frameBufferC);
- memcpy(_frameBufferP, _frameBufferC, (uint)_surface.w * (uint)_surface.h);
+ memcpy(_frameBufferP, _frameBufferC, (uint32)_surface.w * (uint32)_surface.h);
}
void HNMDecoder::HNM4VideoTrack::presentFrame(uint16 flags) {
Commit: 3f01f8301cd7d96dda8af92daac03d14f7344d46
https://github.com/scummvm/scummvm/commit/3f01f8301cd7d96dda8af92daac03d14f7344d46
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2022-01-08T22:24:14+01:00
Commit Message:
VIDEO: Fix audio stereo decoding.
The differential PCM is applied for each channel independently
Changed paths:
video/hnm_decoder.cpp
video/hnm_decoder.h
diff --git a/video/hnm_decoder.cpp b/video/hnm_decoder.cpp
index 9e73349ec68..8c675c573b5 100644
--- a/video/hnm_decoder.cpp
+++ b/video/hnm_decoder.cpp
@@ -572,7 +572,7 @@ void HNMDecoder::HNM5VideoTrack::decodeFrame(Common::SeekableReadStream *stream,
HNMDecoder::DPCMAudioTrack::DPCMAudioTrack(uint16 format, uint16 bits, uint sampleRate, bool stereo,
Audio::Mixer::SoundType soundType) : AudioTrack(soundType), _audioStream(nullptr),
- _gotLUT(false), _lastSample(0), _sampleRate(sampleRate), _stereo(stereo) {
+ _gotLUT(false), _lastSampleL(0), _lastSampleR(0), _sampleRate(sampleRate), _stereo(stereo) {
if (bits != 16) {
error("Unsupported audio bits");
}
@@ -603,28 +603,45 @@ Audio::Timestamp HNMDecoder::DPCMAudioTrack::decodeSound(
_gotLUT = true;
}
- if (size > 0) {
- uint16 *out = (uint16 *)malloc(size * sizeof(*out));
- uint16 *p = out;
- uint16 sample = _lastSample;
- for (uint32 i = 0; i < size; i++, p++) {
- byte deltaId = stream->readByte();
- sample += _lut[deltaId];
- *p = sample;
- }
- _lastSample = sample;
-
- byte flags = Audio::FLAG_16BITS;
+ if (size == 0) {
+ return Audio::Timestamp(0, 0, _sampleRate);
+ }
- if (_audioStream->isStereo())
- flags |= Audio::FLAG_STEREO;
+ uint16 *out = (uint16 *)malloc(size * sizeof(*out));
+ uint16 *p = out;
+ byte flags = Audio::FLAG_16BITS;
#ifdef SCUMM_LITTLE_ENDIAN
- flags |= Audio::FLAG_LITTLE_ENDIAN;
+ flags |= Audio::FLAG_LITTLE_ENDIAN;
#endif
+ if (_audioStream->isStereo()) {
+ uint16 sampleL = _lastSampleL;
+ uint16 sampleR = _lastSampleR;
+ byte deltaId;
+ for (uint32 i = 0; i < size / 2; i++, p += 2) {
+ deltaId = stream->readByte();
+ sampleL += _lut[deltaId];
+ deltaId = stream->readByte();
+ sampleR += _lut[deltaId];
+ p[0] = sampleL;
+ p[1] = sampleR;
+ }
+ _lastSampleL = sampleL;
+ _lastSampleR = sampleR;
- _audioStream->queueBuffer((byte *)out, size * sizeof(*out), DisposeAfterUse::YES, flags);
+ flags |= Audio::FLAG_STEREO;
+ } else {
+ uint16 sample = _lastSampleL;
+ byte deltaId;
+ for (uint32 i = 0; i < size; i++, p++) {
+ deltaId = stream->readByte();
+ sample += _lut[deltaId];
+ *p = sample;
+ }
+ _lastSampleL = sample;
}
+
+ _audioStream->queueBuffer((byte *)out, size * sizeof(*out), DisposeAfterUse::YES, flags);
return Audio::Timestamp(0, _audioStream->isStereo() ? size / 2 : size, _sampleRate);
}
diff --git a/video/hnm_decoder.h b/video/hnm_decoder.h
index ddf360a14b0..e5a084404bd 100644
--- a/video/hnm_decoder.h
+++ b/video/hnm_decoder.h
@@ -152,7 +152,8 @@ private:
Audio::QueuingAudioStream *_audioStream;
bool _gotLUT;
uint16 _lut[256];
- uint16 _lastSample;
+ uint16 _lastSampleL;
+ uint16 _lastSampleR;
uint _sampleRate;
bool _stereo;
};
Commit: 6352de724adc52cafd814e58c7c9df36827c5d83
https://github.com/scummvm/scummvm/commit/6352de724adc52cafd814e58c7c9df36827c5d83
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2022-01-08T22:24:14+01:00
Commit Message:
VIDEO: Implement UBB2 (aka HNM5) decoding
Changed paths:
video/hnm_decoder.cpp
video/hnm_decoder.h
diff --git a/video/hnm_decoder.cpp b/video/hnm_decoder.cpp
index 8c675c573b5..f2edae6a6d1 100644
--- a/video/hnm_decoder.cpp
+++ b/video/hnm_decoder.cpp
@@ -560,14 +560,367 @@ void HNMDecoder::HNM5VideoTrack::decodeChunk(Common::SeekableReadStream *stream,
}
}
+static inline byte *HNM5_getSourcePtr(Common::SeekableReadStream *stream, uint32 &size,
+ byte *previous, byte *current, int16 pitch, byte currentMode) {
+ int32 offset;
+ byte offb;
+
+#define HNM5_DECODE_OFFSET_CST(src, constant_off) \
+ offset = constant_off + stream->readUint16LE(); \
+ size -= 2; \
+ return (src + offset)
+#define HNM5_DECODE_OFFSET(src, xbits, xbase, ybase) \
+ offb = stream->readByte(); \
+ size -= 1; \
+ offset = ((offb >> xbits) + ybase) * pitch + \
+ (offb & ((1 << xbits) - 1)) + xbase; \
+ return (src + offset)
+
+ switch (currentMode) {
+ case 2:
+ HNM5_DECODE_OFFSET_CST(previous, -32768);
+ case 3:
+ HNM5_DECODE_OFFSET_CST(previous, -32768);
+ case 4:
+ HNM5_DECODE_OFFSET_CST(previous, -32768);
+ case 5:
+ HNM5_DECODE_OFFSET(previous, 4, -8, -8);
+ case 6:
+ HNM5_DECODE_OFFSET(previous, 4, -8, -8);
+ case 7:
+ HNM5_DECODE_OFFSET(previous, 4, -8, -8);
+ case 8:
+ HNM5_DECODE_OFFSET(previous, 4, -2, -8);
+ case 9:
+ HNM5_DECODE_OFFSET(previous, 4, -14, -8);
+ case 10:
+ HNM5_DECODE_OFFSET(previous, 4, -8, -2);
+ case 11:
+ HNM5_DECODE_OFFSET(previous, 4, -8, -14);
+ case 12:
+ HNM5_DECODE_OFFSET(previous, 4, -2, -2);
+ case 13:
+ HNM5_DECODE_OFFSET(previous, 4, -14, -2);
+ case 14:
+ HNM5_DECODE_OFFSET(previous, 4, -2, -14);
+ case 15:
+ HNM5_DECODE_OFFSET(previous, 4, -14, -14);
+ case 16:
+ HNM5_DECODE_OFFSET(previous, 4, -2, -8);
+ case 17:
+ HNM5_DECODE_OFFSET(previous, 4, -14, -8);
+ case 18:
+ HNM5_DECODE_OFFSET(previous, 4, -8, -2);
+ case 19:
+ HNM5_DECODE_OFFSET(previous, 4, -8, -14);
+ case 20:
+ HNM5_DECODE_OFFSET(previous, 4, -2, -2);
+ case 21:
+ HNM5_DECODE_OFFSET(previous, 4, -14, -2);
+ case 22:
+ HNM5_DECODE_OFFSET(previous, 4, -2, -14);
+ case 23:
+ HNM5_DECODE_OFFSET(previous, 4, -14, -14);
+ case 24:
+ HNM5_DECODE_OFFSET(previous, 4, -2, -8);
+ case 25:
+ HNM5_DECODE_OFFSET(previous, 4, -14, -8);
+ case 26:
+ HNM5_DECODE_OFFSET(previous, 4, -8, -2);
+ case 27:
+ HNM5_DECODE_OFFSET(previous, 4, -8, -14);
+ case 28:
+ HNM5_DECODE_OFFSET(previous, 4, -2, -2);
+ case 29:
+ HNM5_DECODE_OFFSET(previous, 4, -14, -2);
+ case 30:
+ HNM5_DECODE_OFFSET(previous, 4, -2, -14);
+ case 31:
+ HNM5_DECODE_OFFSET(previous, 4, -14, -14);
+ case 32:
+ HNM5_DECODE_OFFSET_CST(current, -65536);
+ case 33:
+ HNM5_DECODE_OFFSET(current, 5, -16, -8);
+ case 34:
+ HNM5_DECODE_OFFSET(current, 4, -8, -16);
+ case 35:
+ HNM5_DECODE_OFFSET(current, 4, -24, -16);
+ case 36:
+ HNM5_DECODE_OFFSET(current, 4, 8, -16);
+ case 37:
+ HNM5_DECODE_OFFSET(current, 3, -4, -32);
+ case 38:
+ HNM5_DECODE_OFFSET(current, 3, -12, -32);
+ case 39:
+ HNM5_DECODE_OFFSET(current, 3, 4, -32);
+ case 40:
+ HNM5_DECODE_OFFSET_CST(current, -65536);
+ case 41:
+ HNM5_DECODE_OFFSET(current, 5, -16, -8);
+ case 42:
+ HNM5_DECODE_OFFSET(current, 4, -8, -16);
+ case 43:
+ HNM5_DECODE_OFFSET(current, 4, -24, -16);
+ case 44:
+ HNM5_DECODE_OFFSET(current, 4, 8, -16);
+ case 45:
+ HNM5_DECODE_OFFSET(current, 3, -4, -32);
+ case 46:
+ HNM5_DECODE_OFFSET(current, 3, -12, -32);
+ case 47:
+ HNM5_DECODE_OFFSET(current, 3, 4, -32);
+ default:
+ error("BUG: Invalid offset mode");
+ }
+
+#undef HNM5_DECODE_OFFSET_CST
+#undef HNM5_DECODE_OFFSET
+}
+
+static inline void HNM5_copy(byte *dst, byte *src, int16 pitch,
+ byte copyMode, byte width, byte height) {
+ switch (copyMode) {
+ case 0:
+ // Copy
+ for (byte row = 0; row < height; row++) {
+ memcpy(&dst[row * pitch],
+ &src[row * pitch], width);
+ }
+ break;
+ case 1:
+ // Horizontal reverse
+ for (byte row = 0; row < height; row++) {
+ byte *dp = &dst[row * pitch];
+ byte *sp = &src[row * pitch];
+ for (byte col = 0; col < width; col++, dp++, sp--) {
+ *dp = *sp;
+ }
+ }
+ break;
+ case 2:
+ // Vertical reverse
+ for (byte row = 0; row < height; row++) {
+ memcpy(&dst[ row * pitch],
+ &src[-row * pitch], width);
+ }
+ break;
+ case 3:
+ // Horiz-Vert reverse
+ for (byte row = 0; row < height; row++) {
+ byte *dp = &dst[ row * pitch];
+ byte *sp = &src[-row * pitch];
+ for (byte col = 0; col < width; col++, dp++, sp--) {
+ *dp = *sp;
+ }
+ }
+ break;
+ case 4:
+ // Swap
+ for (byte row = 0; row < height; row++) {
+ byte *dp = &dst[row * pitch];
+ byte *sp = &src[row * 1];
+ for (byte col = 0; col < width; col++, dp++, sp += pitch) {
+ *dp = *sp;
+ }
+ }
+ break;
+ case 5:
+ // Swap Horiz-Reverse
+ for (byte row = 0; row < height; row++) {
+ byte *dp = &dst[row * pitch];
+ byte *sp = &src[row * 1];
+ for (byte col = 0; col < width; col++, dp++, sp -= pitch) {
+ *dp = *sp;
+ }
+ }
+ break;
+ case 6:
+ // Swap Vert-Reverse
+ for (byte row = 0; row < height; row++) {
+ byte *dp = &dst[ row * pitch];
+ byte *sp = &src[-row * 1];
+ for (byte col = 0; col < width; col++, dp++, sp += pitch) {
+ *dp = *sp;
+ }
+ }
+ break;
+ case 7:
+ // Swap Vert-Reverse
+ for (byte row = 0; row < height; row++) {
+ byte *dp = &dst[ row * pitch];
+ byte *sp = &src[-row * 1];
+ for (byte col = 0; col < width; col++, dp++, sp -= pitch) {
+ *dp = *sp;
+ }
+ }
+ break;
+ default:
+ error("BUG: Invalid copy mode");
+ return;
+ }
+}
+
+static const byte HNM5_WIDTHS[3][32] = {
+ {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 44, 48, 52, 56
+ }, /* 2 */
+ {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44
+ }, /* 3 */
+ {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 30, 32, 34, 36, 38
+ }, /* 4 */
+};
+
void HNMDecoder::HNM5VideoTrack::decodeFrame(Common::SeekableReadStream *stream, uint32 size) {
SWAP(_frameBufferC, _frameBufferP);
- // TODO: Implement this
+ uint16 pitch = _surface.pitch;
+ bool eop = false;
+
+ byte height = (byte)-1;
+ byte currentMode = (byte)-1;
+ uint32 currentPos = 0;
+
+ while (!eop) {
+ if (size < 1) {
+ warning("Not enough data in chunk for frame block");
+ break;
+ }
+ byte opcode = stream->readByte();
+ size -= 1;
+
+ if (opcode == 0x20) {
+ assert(height != (byte)-1);
+ if (size < 1) {
+ error("Not enough data for opcode 0x20");
+ }
+ uint width = stream->readByte();
+ size -= 1;
+ width++;
+ for (byte row = 0; row < height; row++) {
+ memcpy(&_frameBufferC[currentPos + row * pitch], &_frameBufferP[currentPos + row * pitch], width);
+ }
+ currentPos += width;
+ } else if (opcode == 0x60) {
+ // Maximal pixels height is 4
+ assert(height != (byte)-1 && height <= 4);
+ if (size < height) {
+ error("Not enough data for opcode 0x60");
+ }
+ assert(stream->read(_workingPixels, height) == height);
+ size -= height;
+ for (byte row = 0; row < height; row++) {
+ _frameBufferC[currentPos + row * pitch] = _workingPixels[row];
+ }
+ currentPos += 1;
+ } else if (opcode == 0xA0) {
+ assert(height != (byte)-1);
+ if (size < 1) {
+ error("Not enough data for opcode 0x20");
+ }
+ uint width = stream->readByte();
+ size -= 1;
+ width += 2;
+
+ if (size < height * width) {
+ error("Not enough data for opcode 0xA0");
+ }
+ assert(stream->read(_workingPixels, height * width) == height * width);
+ size -= height * width;
+
+ for (byte row = 0; row < height; row++) {
+ for (uint col = 0; col < width; col++) {
+ _frameBufferC[currentPos + row * pitch + col] = _workingPixels[height * col + row];
+ }
+ }
+ currentPos += width;
+ } else if (opcode == 0xE0) {
+ if (size < 1) {
+ error("Not enough data for opcode 0xE0");
+ }
+ byte subop = stream->readByte();
+ size -= 1;
+
+ if (subop == 0x00) {
+ assert(height != (byte)-1);
+ if (size < 2) {
+ error("Not enough data for opcode 0xE0 0x00");
+ }
+ uint width = stream->readByte();
+ byte px = stream->readByte();
+ size -= 2;
+
+ width += 1;
+
+ for (byte row = 0; row < height; row++) {
+ memset(&_frameBufferC[currentPos + row * pitch], px, width);
+ }
+ currentPos += width;
+ } else if (subop == 0x01) {
+ if (height != (byte)-1) {
+ currentPos += (height - 1) * pitch;
+ }
+
+ eop = true;
+ } else {
+ // Reconfigure decoder at line start
+ assert((currentPos % pitch) == 0);
+ assert(subop < 48);
+
+ if (height != (byte)-1) {
+ currentPos += (height - 1) * pitch;
+ }
+
+ currentMode = subop;
+
+ if (( 8 <= subop && subop <= 15) ||
+ (32 <= subop && subop <= 39) ||
+ (subop == 2) || (subop == 5)) {
+ height = 2;
+ } else if ((16 <= subop && subop <= 23) ||
+ (40 <= subop && subop <= 47) ||
+ (subop == 3) || (subop == 6)) {
+ height = 3;
+ } else if ((24 <= subop && subop <= 31) ||
+ (subop == 4) || (subop == 7)) {
+ height = 4;
+ }
+
+ }
+ } else {
+ assert(height != (byte)-1);
+ assert(2 <= height && height <= 4);
+ byte index = opcode & 0x1f;
+ byte copyMode = (opcode >> 5) & 0x7;
+ byte width = HNM5_WIDTHS[height - 2][index];
+
+ // HNM5_getSourcePtr can consume 1 byte but the stream can not end like this so check for maximum
+ if (size < 2) {
+ error("Not enough data for opcode 0x%02X", opcode);
+ }
+ byte *src = HNM5_getSourcePtr(stream, size, _frameBufferP, _frameBufferC, pitch, currentMode);
+
+ HNM5_copy(_frameBufferC + currentPos, src + currentPos, pitch, copyMode, width, height);
+ currentPos += width;
+ }
+ }
if (size > 0) {
stream->skip(size);
}
+
+ _surface.setPixels(_frameBufferC);
+
+ // Frame done
+ _curFrame++;
+ _nextFrameStartTime += _nextFrameDelay != uint32(-1) ? _nextFrameDelay : _regularFrameDelay;
+ _nextFrameDelay = _nextNextFrameDelay;
+ _nextNextFrameDelay = uint32(-1);
+
}
HNMDecoder::DPCMAudioTrack::DPCMAudioTrack(uint16 format, uint16 bits, uint sampleRate, bool stereo,
diff --git a/video/hnm_decoder.h b/video/hnm_decoder.h
index e5a084404bd..2796b4d9264 100644
--- a/video/hnm_decoder.h
+++ b/video/hnm_decoder.h
@@ -137,6 +137,9 @@ private:
protected:
/** Really decode */
void decodeFrame(Common::SeekableReadStream *stream, uint32 size);
+
+ // Some space to speed things out
+ byte _workingPixels[257 * 4];
};
class DPCMAudioTrack : public AudioTrack {
More information about the Scummvm-git-logs
mailing list