[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