mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-24 05:01:43 +00:00
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.
This commit is contained in:
parent
6b1a3983f3
commit
c6383c472e
@ -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");
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user