[Scummvm-git-logs] scummvm master -> c4baf30f9bd88148a961ba592d8c5120486175e5
sev-
sev at scummvm.org
Sun Mar 14 18:16:28 UTC 2021
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:
1947eae043 AUDIO: Add support for Xan DPCM (Crusader: No Regret)
017a801c96 IMAGE: Add support for Xan codec videos (Crusader, Wing Commander)
a20b8781e1 VIDEO: Add support for Xan codec videos (Crusader, Wing Commander)
c4baf30f9b ULTIMA8: Add Crusader: No Regret movies
Commit: 1947eae043bf71a60e132f956913452257dd068e
https://github.com/scummvm/scummvm/commit/1947eae043bf71a60e132f956913452257dd068e
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-03-14T19:16:24+01:00
Commit Message:
AUDIO: Add support for Xan DPCM (Crusader: No Regret)
Changed paths:
A audio/decoders/xan_dpcm.cpp
A audio/decoders/xan_dpcm.h
audio/decoders/wave_types.h
audio/module.mk
diff --git a/audio/decoders/wave_types.h b/audio/decoders/wave_types.h
index 0b64fab6d0..c600b02bc1 100644
--- a/audio/decoders/wave_types.h
+++ b/audio/decoders/wave_types.h
@@ -34,7 +34,8 @@ enum WaveCompressionType {
kWaveFormatMP3 = 0x0055,
kWaveFormatDK3 = 0x0062, // rogue format number
kWaveFormatMSIMAADPCM2 = 0x0069,
- kWaveFormatWMAv2 = 0x0161
+ kWaveFormatWMAv2 = 0x0161,
+ kWaveFormatXanDPCM = 0x594a // 'JY', Crusader: No Regret videos
};
} // End of namespace Audio
diff --git a/audio/decoders/xan_dpcm.cpp b/audio/decoders/xan_dpcm.cpp
new file mode 100644
index 0000000000..0c2f221ba6
--- /dev/null
+++ b/audio/decoders/xan_dpcm.cpp
@@ -0,0 +1,105 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/stream.h"
+#include "common/util.h"
+#include "common/textconsole.h"
+
+#include "audio/audiostream.h"
+#include "audio/decoders/xan_dpcm.h"
+
+
+namespace Audio {
+
+/**
+ * Implements the Xan DPCM decoder used in Crusader: No Regret and Wing
+ * Commander IV movies. Implementation based on the description on the
+ * MultiMedia Wiki:
+ * https://wiki.multimedia.cx/index.php/Xan_DPCM
+ */
+class Xan_DPCMStream : public Audio::AudioStream {
+public:
+ Xan_DPCMStream(int rate, int channels, Common::SeekableReadStream *data) : _rate(rate), _channels(channels), _data(data) {
+ assert(channels == 1 || channels == 2);
+ _pred[0] = data->readSint16LE();
+ if (channels == 2)
+ _pred[1] = data->readSint16LE();
+ _shift[0] = 4;
+ _shift[1] = 4;
+ };
+
+ virtual int readBuffer(int16 *buffer, const int numSamples) override {
+ int i = 0;
+ for (; i < numSamples; i++) {
+ int32 *pshift = ((_channels == 2 && (i % 2)) ? _shift + 1 : _shift);
+ int32 *ppred = ((_channels == 2 && (i % 2)) ? _pred + 1 : _pred);
+ const uint8 b = _data->readByte();
+ const int diff = static_cast<int8>(b & 0xFC) * 256;
+ if ((b & 3) == 3)
+ *pshift += 1;
+ else
+ *pshift -= (2 * (b & 3));
+ if (*pshift < 0)
+ *pshift = 0;
+ if (*pshift > 15) {
+ warning("Xan DPCM shift should not go over 15, corrupt data?");
+ *pshift = 15;
+ }
+ *ppred += (diff >> *pshift);
+ *ppred = CLIP(*ppred, -32768, 32767);
+ *buffer = *ppred;
+ buffer++;
+ if (_data->eos())
+ break;
+ }
+ return i;
+ }
+
+ virtual bool isStereo() const override {
+ return _channels == 2;
+ }
+
+ virtual int getRate() const override {
+ return _rate;
+ }
+
+ virtual bool endOfData() const override {
+ return _data->eos();
+ }
+
+private:
+ int _channels;
+ int _rate;
+ Common::SeekableReadStream *_data;
+ int32 _pred[2];
+ int32 _shift[2];
+};
+
+XanDPCMStream::XanDPCMStream(int rate, int channels) :
+ Audio::StatelessPacketizedAudioStream(rate, channels) {
+}
+
+AudioStream *XanDPCMStream::makeStream(Common::SeekableReadStream *data) {
+ return new Xan_DPCMStream(getRate(), getChannels(), data);
+}
+
+} // End of namespace Audio
diff --git a/audio/decoders/xan_dpcm.h b/audio/decoders/xan_dpcm.h
new file mode 100644
index 0000000000..8fd939c6fc
--- /dev/null
+++ b/audio/decoders/xan_dpcm.h
@@ -0,0 +1,66 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/**
+ * @file
+ * Sound decoder used in engines:
+ * - ultima8 (Crusader: No Regret)
+ */
+
+#ifndef AUDIO_XAN_DPCM_H
+#define AUDIO_XAN_DPCM_H
+
+#include "common/scummsys.h"
+#include "common/types.h"
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace Audio {
+
+class StatelessPacketizedAudioStream;
+
+/**
+ * Implements the Xan DPCM decoder used in Crusader: No Regret and Wing
+ * Commander IV movies. Implementation based on the description on the
+ * MultiMedia Wiki:
+ * https://wiki.multimedia.cx/index.php/Xan_DPCM
+ */
+class XanDPCMStream : public StatelessPacketizedAudioStream {
+public:
+ /**
+ * Create a Xan DPCM stream
+ * @param rate sampling rate (samples per second)
+ * @param channels number of channels to decode
+ * @return a new XanDPCMStream, or NULL, if an error occurred
+ */
+ XanDPCMStream(int rate, int channels);
+
+protected:
+ virtual AudioStream *makeStream(Common::SeekableReadStream *data) override;
+};
+
+
+} // End of namespace Audio
+
+#endif
diff --git a/audio/module.mk b/audio/module.mk
index 4456f87a3b..4b795bc786 100644
--- a/audio/module.mk
+++ b/audio/module.mk
@@ -35,6 +35,7 @@ MODULE_OBJS := \
decoders/wave.o \
decoders/wma.o \
decoders/xa.o \
+ decoders/xan_dpcm.o \
mods/infogrames.o \
mods/maxtrax.o \
mods/mod_xm_s3m.o \
Commit: 017a801c96a650b18bf607a7d65ad292dd6445b3
https://github.com/scummvm/scummvm/commit/017a801c96a650b18bf607a7d65ad292dd6445b3
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-03-14T19:16:24+01:00
Commit Message:
IMAGE: Add support for Xan codec videos (Crusader, Wing Commander)
Changed paths:
A image/codecs/xan.cpp
A image/codecs/xan.h
image/codecs/codec.cpp
image/module.mk
diff --git a/image/codecs/codec.cpp b/image/codecs/codec.cpp
index 8716a01d4e..4e161428d2 100644
--- a/image/codecs/codec.cpp
+++ b/image/codecs/codec.cpp
@@ -43,6 +43,7 @@
#include "image/codecs/smc.h"
#include "image/codecs/svq1.h"
#include "image/codecs/truemotion1.h"
+#include "image/codecs/xan.h"
#include "common/endian.h"
#include "common/textconsole.h"
@@ -224,6 +225,8 @@ Codec *createBitmapCodec(uint32 tag, uint32 streamTag, int width, int height, in
return new Indeo4Decoder(width, height, bitsPerPixel);
case MKTAG('I', 'V', '5', '0'):
return new Indeo5Decoder(width, height, bitsPerPixel);
+ case MKTAG('X', 'x', 'a', 'n'):
+ return new XanDecoder(width, height, bitsPerPixel);
#ifdef IMAGE_CODECS_TRUEMOTION1_H
case MKTAG('D','U','C','K'):
case MKTAG('d','u','c','k'):
diff --git a/image/codecs/xan.cpp b/image/codecs/xan.cpp
new file mode 100644
index 0000000000..d46c3b9197
--- /dev/null
+++ b/image/codecs/xan.cpp
@@ -0,0 +1,502 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * Xan image decoder. (fourcc Xxan)
+ *
+ * Used by Crusader: No Regret AVI files
+ *
+ * This code was created based on the multimedia wiki:
+ * https://wiki.multimedia.cx/index.php/Origin_Xan_Codec
+ * and ffmpeg's libavcodec/xxan.c.
+ * The ffmpeg code is LGPL2 licensed and Copyright (C) 2011
+ * Konstantin Shishkov based on work by Mike Melanson.
+ *
+ * A similar format is used in Wing Commander III (although not in an AVI
+ * container) and IV.
+ */
+
+#include "image/codecs/xan.h"
+
+#include "common/stream.h"
+#include "common/bitstream.h"
+#include "common/memstream.h"
+#include "common/util.h"
+#include "common/textconsole.h"
+#include "common/system.h"
+#include "common/debug.h"
+#include "common/rect.h"
+#include "graphics/surface.h"
+#include "graphics/yuv_to_rgb.h"
+
+namespace Image {
+
+static const int SCRATCH_SPARE = 256;
+
+XanDecoder::XanDecoder(int width, int height, int bitsPerPixel) : Codec(),
+ _width(width), _height(height), _wc4Mode(false) {
+ assert(bitsPerPixel == 16);
+ if (bitsPerPixel != 16)
+ error("XanDecoder: BPP must be 16 not %d", bitsPerPixel);
+ if (width % 2)
+ error("XanDecoder: width must be even, not %d", width);
+ _surface.create(_width, _height, getPixelFormat());
+ _scratchbuf = new uint8[_width * _height + SCRATCH_SPARE];
+ _lumabuf = new uint8[_width * _height];
+ _ybuf = new uint8[_width * _height];
+ _ubuf = new uint8[_width * _height / 2];
+ _vbuf = new uint8[_width * _height / 2];
+ memset(_scratchbuf, 0, _width * _height + SCRATCH_SPARE);
+ memset(_lumabuf, 0, _width * _height);
+ memset(_ybuf, 0, _width * _height);
+ memset(_ubuf, 127, _width * _height / 2);
+ memset(_vbuf, 127, _width * _height / 2);
+}
+
+XanDecoder::~XanDecoder() {
+ _surface.free();
+ delete [] _scratchbuf;
+ delete [] _lumabuf;
+ delete [] _ybuf;
+ delete [] _ubuf;
+ delete [] _vbuf;
+}
+
+const Graphics::Surface *XanDecoder::decodeFrame(Common::SeekableReadStream &stream) {
+ uint32 frametype = stream.readUint32LE();
+ if (frametype > 1) {
+ error("Xan frametype should be 0 or 1, got %d", frametype);
+ }
+
+ if (frametype == 0) {
+ decodeFrameType0(stream);
+ } else {
+ decodeFrameType1(stream);
+ }
+
+ return &_surface;
+}
+
+// An unoptimized version of the one from libavutil, but works fine
+static void _memcpy_backptr(uint8 *dst, int back, int cnt) {
+ if (cnt <= 0 || back <= 0)
+ return;
+
+ const uint8 *src = &dst[-back];
+ if (back == 1) {
+ uint8 val = *src;
+ memset(dst, val, cnt);
+ } else {
+ int blocklen = back;
+ while (cnt > blocklen) {
+ memcpy(dst, src, blocklen);
+ dst += blocklen;
+ cnt -= blocklen;
+ blocklen <<= 1;
+ }
+ memcpy(dst, src, cnt);
+ }
+}
+
+// Unpack using the WC3 algorithm
+static int _unpack(Common::SeekableReadStream &stream, uint8 *dest, const int dest_len) {
+ const uint8 *orig_dest = dest;
+ const uint8 *dest_end = dest + dest_len;
+
+ memset(dest, 0, dest_len);
+
+ while (dest < dest_end) {
+ if (stream.eos())
+ return -1;
+
+ const uint8 opcode = stream.readByte();
+
+ if (opcode < 0xe0) {
+ int readsize, copysize, back;
+ if ((opcode & 0x80) == 0) {
+ readsize = opcode & 3;
+ back = ((opcode & 0x60) << 3) + stream.readByte() + 1;
+ copysize = ((opcode & 0x1c) >> 2) + 3;
+ } else if ((opcode & 0x40) == 0) {
+ const uint8 b = stream.readByte();
+ readsize = b >> 6;
+ back = ((b & 0x3f) << 8) + stream.readByte() + 1;
+ copysize = (opcode & 0x3f) + 4;
+ } else {
+ readsize = opcode & 3;
+ back = ((opcode & 0x10) << 12) + stream.readUint16BE() + 1;
+ copysize = ((opcode & 0x0c) << 6) + stream.readByte() + 5;
+ if (readsize + copysize > dest_end - dest)
+ break;
+ }
+ if (dest + readsize + copysize > dest_end ||
+ dest - orig_dest + readsize < back)
+ return -1;
+ stream.read(dest, readsize);
+ dest += readsize;
+ _memcpy_backptr(dest, back, copysize);
+ dest += copysize;
+ } else {
+ bool finish = (opcode >= 0xfc);
+
+ int readsize = finish ? opcode & 3 : ((opcode & 0x1f) * 4) + 4;
+ if (dest + readsize > dest_end)
+ return -1;
+ stream.read(dest, readsize);
+ dest += readsize;
+ if (finish)
+ break;
+ }
+ }
+
+ return dest - orig_dest;
+}
+
+
+bool XanDecoder::decodeChroma(Common::SeekableReadStream &stream, const int chroma_off) {
+ if (!chroma_off)
+ return 0;
+
+ if (chroma_off + 4 >= stream.size() - stream.pos()) {
+ warning("Invalid chroma block position");
+ return false;
+ }
+
+ stream.seek(chroma_off + 4);
+
+ const int mode = stream.readUint16LE();
+ const int table_start = stream.pos();
+ // 2 bytes per table entry
+ const int table_size = stream.readUint16LE() * 2;
+
+ if (mode > 1) {
+ warning("Unexpected chroma mode %d", mode);
+ return false;
+ }
+
+ if (table_size >= stream.size() - stream.pos()) {
+ warning("Invalid chroma block offset");
+ return false;
+ }
+
+ stream.skip(table_size);
+ const int dec_size = _unpack(stream, _scratchbuf, _width * _height);
+ if (dec_size < 0) {
+ warning("Chroma unpacking failed");
+ return false;
+ }
+
+ const int pitch = _width / 2;
+
+ uint8 *U = _ubuf;
+ uint8 *V = _vbuf;
+ const uint8 *src = _scratchbuf;
+ const uint8 *src_end = src + dec_size;
+ if (mode) {
+ // YUV420 frame
+ for (int y = 0; y < _height / 2; y++) {
+ for (int x = 0; x < pitch; x++) {
+ if (src >= src_end)
+ return true;
+ int toff = *src++ * 2;
+ if (toff) {
+ if (toff > table_size)
+ return false;
+ const int pos = stream.pos();
+ stream.seek(table_start + toff);
+ uint8 uval, vval;
+ if (_wc4Mode) {
+ uint16 val = stream.readUint16LE();
+ uval = (val >> 3) & 0xF8;
+ vval = (val >> 8) & 0xF8;
+ } else {
+ uval = stream.readByte();
+ vval = stream.readByte();
+ }
+ uval = uval | uval >> 5;
+ vval = vval | vval >> 5;
+ stream.seek(pos);
+ U[x] = uval;
+ V[x] = vval;
+ }
+ }
+ U += pitch;
+ V += pitch;
+ }
+ if (_height % 1) {
+ memcpy(U, U - pitch, pitch);
+ memcpy(V, V - pitch, pitch);
+ }
+ } else {
+ // YUV410 frame - expand out U and V components
+ uint8 *U2 = U + pitch;
+ uint8 *V2 = V + pitch;
+
+ for (int y = 0; y < _height / 4; y++) {
+ for (int x = 0; x < pitch; x += 2) {
+ if (src >= src_end)
+ return true;
+ int toff = *src++ * 2;
+ if (toff) {
+ if (toff > table_size)
+ return false;
+ const int pos = stream.pos();
+ stream.seek(table_start + toff);
+ uint8 uval, vval;
+ if (_wc4Mode) {
+ uint16 val = stream.readUint16LE();
+ uval = (val >> 3) & 0xF8;
+ vval = (val >> 8) & 0xF8;
+ } else {
+ uval = stream.readByte();
+ vval = stream.readByte();
+ }
+ uval = uval | uval >> 5;
+ vval = vval | vval >> 5;
+ stream.seek(pos);
+ U[x] = U[x + 1] = U2[x] = U2[x + 1] = uval;
+ V[x] = V[x + 1] = V2[x] = V2[x + 1] = vval;
+ }
+ }
+ U += pitch * 2;
+ V += pitch * 2;
+ U2 += pitch * 2;
+ V2 += pitch * 2;
+ }
+ if (_height % 4) {
+ int lines = ((_height + 1) / 2) - (_height / 4) * 2;
+
+ memcpy(U, U - lines * pitch, lines * pitch);
+ memcpy(V, V - lines * pitch, lines * pitch);
+ }
+ }
+
+ return true;
+}
+
+void XanDecoder::decodeFrameType0(Common::SeekableReadStream &stream) {
+ const uint32 chroma_offset = stream.readUint32LE();
+ const uint32 refines_offset = stream.readUint32LE();
+ const uint32 luma_offset = stream.pos();
+ const uint32 nbytes = static_cast<uint32>(stream.size());
+
+ if (chroma_offset > nbytes || refines_offset > nbytes) {
+ error("invalid frame type 0 offsets");
+ }
+
+ if (!decodeChroma(stream, chroma_offset)) {
+ warning("xxan chrome decode failed frame type 0");
+ return;
+ }
+
+ stream.seek(luma_offset);
+ decompressLuma(stream);
+
+ //
+ // Expand out the decompressed luma data. For type 0 frames:
+ // * luma vals are 5-bit diffs, where
+ // * top row values are diffs on the last value
+ // * and the remaining rows are deltas on the value above.
+ // * every second pixel in x is linearly interpolated from its horizontal neighbours.
+ // * output values are clipped to 6 bits.
+ // * a set of refinements values can adjust luma of interpolated pixels
+ //
+ const uint8 *lumadecomp = _scratchbuf;
+ uint8 *lumarow = _lumabuf;
+ int last = *lumadecomp++;
+ lumarow[0] = last * 2;
+ int x;
+ // The top row uses only the left value for prediction
+ for (x = 1; x < _surface.w - 1; x += 2) {
+ int cur = (last + *lumadecomp++) & 0x1F;
+ lumarow[x] = last + cur;
+ lumarow[x + 1] = cur * 2;
+ last = cur;
+ }
+ lumarow[x] = last * 2;
+ uint8 const *last_lumarow = lumarow;
+ lumarow += _surface.w;
+
+ // The remaining rows
+ for (int y = 1; y < _surface.h; y++) {
+ last = ((last_lumarow[0] / 2) + *lumadecomp++) & 0x1F;
+ lumarow[0] = last * 2;
+ for (x = 1; x < _surface.w - 1; x += 2) {
+ int cur = ((last_lumarow[x + 1] / 2) + *lumadecomp++) & 0x1F;
+ lumarow[x] = last + cur;
+ lumarow[x + 1] = cur * 2;
+ last = cur;
+ }
+ lumarow[x] = last * 2;
+ last_lumarow = lumarow;
+ lumarow += _surface.w;
+ }
+
+ if (refines_offset) {
+ stream.seek(refines_offset + 8);
+
+ int dec_size = _unpack(stream, _scratchbuf, _width * _height / 2);
+ if (dec_size < 0) {
+ warning("luma refine unpacking failed!");
+ dec_size = 0;
+ } else {
+ dec_size = MIN(dec_size, _width * _height / 2 - 1);
+ }
+
+ for (int i = 0; i < dec_size; i++)
+ _lumabuf[i * 2 + 1] = (_lumabuf[i * 2 + 1] + _scratchbuf[i] * 2) & 0x3F;
+ }
+
+ convertYUVtoRGBSurface();
+}
+
+void XanDecoder::decodeFrameType1(Common::SeekableReadStream &stream) {
+ const uint32 chroma_offset = stream.readUint32LE();
+ const uint32 refines_offset = stream.readUint32LE();
+ const uint32 refine2_offset = stream.readUint32LE();
+ const uint32 luma_offset = stream.pos();
+ const uint32 nbytes = static_cast<uint32>(stream.size());
+
+ if (chroma_offset > nbytes || refines_offset > nbytes || refine2_offset > nbytes) {
+ error("invalid frame type 1 offset");
+ }
+
+ if (!decodeChroma(stream, chroma_offset)) {
+ warning("xxan chrome decode failed frame type 1");
+ return;
+ }
+
+ stream.seek(luma_offset);
+ decompressLuma(stream);
+
+ //
+ // Expand out the decompressed luma data. For type 1 frames:
+ // * luma vals are 5-bit diffs on the previous frame's values
+ // * every second pixel in x is linearly interpolated from its horizontal neighbours.
+ // * output values are clipped to 6 bits.
+ // * a set of refinements values can adjust luma of interpolated pixels
+ //
+ const uint8 *lumadecomp = _scratchbuf;
+ uint8 *lumarow = _lumabuf;
+ for (int y = 0; y < _surface.h; y++) {
+ int x;
+ int last = (lumarow[0] + (*lumadecomp++ * 2)) & 0x3F;
+ lumarow[0] = last;
+ for (x = 1; x < _surface.w - 1; x += 2) {
+ int cur = (lumarow[x + 1] + (*lumadecomp++ * 2)) & 0x3F;
+ lumarow[x] = (last + cur) / 2;
+ lumarow[x + 1] = cur;
+ last = cur;
+ }
+ lumarow[x] = last;
+ lumarow += _surface.w;
+ }
+
+ if (refines_offset) {
+ stream.seek(refines_offset + 8);
+
+ int dec_size = _unpack(stream, _scratchbuf, _width * _height / 2);
+
+ if (dec_size < 0) {
+ warning("luma refine unpacking failed!");
+ dec_size = 0;
+ } else {
+ dec_size = MIN(dec_size, _width * _height / 2 - 1);
+ }
+
+ int dec2_size = 0;
+ uint8 *scratch2 = _scratchbuf + _width * _height / 2;
+ if (refine2_offset) {
+ stream.seek(refine2_offset + 8);
+ dec2_size = _unpack(stream, scratch2, _width * _height / 2);
+ if (dec2_size < 0) {
+ warning("luma refine2 unpacking failed!");
+ dec2_size = 0;
+ } else {
+ dec2_size = MIN(dec_size, _width * _height / 2 - 1);
+ }
+ }
+
+ for (int i = 0; i < dec_size; i++) {
+ int adjust = _scratchbuf[i] * 2;
+ if (dec2_size)
+ adjust += scratch2[i] * 2;
+ _lumabuf[i * 2 + 1] = (_lumabuf[i * 2 + 1] + adjust) & 0x3F;
+ }
+ }
+
+ convertYUVtoRGBSurface();
+}
+
+void XanDecoder::decompressLuma(Common::SeekableReadStream &stream) {
+ const int32 startpos = stream.pos();
+ const int nsymbols = stream.readByte();
+ const int eofsymbol = stream.readByte();
+
+ const int root = nsymbols + eofsymbol;
+ const uint8 *lumaend = _scratchbuf + _surface.w * _surface.h;
+
+ stream.skip(nsymbols * 2);
+
+ uint8 *luma = _scratchbuf;
+ int node = root;
+ int bits = stream.readByte();
+ int mask = 0x80;
+ while (!stream.eos()) {
+ const int bit = ((bits & mask) ? 1 : 0);
+ mask >>= 1;
+
+ const int32 nextbitspos = stream.pos();
+ stream.seek(startpos + node * 2 + bit - eofsymbol * 2);
+ node = stream.readByte();
+ stream.seek(nextbitspos);
+
+ if (node == eofsymbol)
+ break;
+ if (node < eofsymbol) {
+ *luma++ = node;
+ if (luma >= lumaend)
+ break;
+ node = root;
+ }
+ if (!mask) {
+ if (stream.eos())
+ break;
+ bits = stream.readByte();
+ mask = 0x80;
+ }
+ }
+}
+
+void XanDecoder::convertYUVtoRGBSurface() {
+ // Expand luma from 6-bit to 8-bit.
+ for (int i = 0; i < _width * _height; i++)
+ _ybuf[i] = _lumabuf[i] << 2 | _lumabuf[i] >> 4;
+
+ YUVToRGBMan.convert420(&_surface, Graphics::YUVToRGBManager::kScaleFull,
+ _ybuf, _ubuf, _vbuf, _width, (_height / 2) * 2, _width, _width / 2);
+}
+
+Graphics::PixelFormat XanDecoder::getPixelFormat() const {
+ return Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24);
+}
+
+} // End of namespace Image
diff --git a/image/codecs/xan.h b/image/codecs/xan.h
new file mode 100644
index 0000000000..f935ea094f
--- /dev/null
+++ b/image/codecs/xan.h
@@ -0,0 +1,89 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef IMAGE_CODECS_XAN_H
+#define IMAGE_CODECS_XAN_H
+
+#include "image/codecs/codec.h"
+
+namespace Image {
+
+/**
+ * Xan image decoder. (fourcc Xxan)
+ *
+ * Used by Crusader: No Regret AVI files
+ *
+ * This code was created based on the multimedia wiki:
+ * https://wiki.multimedia.cx/index.php/Origin_Xan_Codec
+ * and ffmpeg's libavcodec/xxan.c.
+ * The ffmpeg code is LGPL2 licensed and Copyright (C) 2011
+ * Konstantin Shishkov based on work by Mike Melanson.
+ *
+ * A similar format is used in Wing Commander III (although not in an AVI
+ * container) and IV.
+ */
+class XanDecoder : public Codec {
+public:
+ XanDecoder (int width, int height, int bitsPerPixel);
+ ~XanDecoder();
+
+ const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
+ Graphics::PixelFormat getPixelFormat() const override;
+
+private:
+ void decodeFrameType0(Common::SeekableReadStream &stream);
+ void decodeFrameType1(Common::SeekableReadStream &stream);
+
+ /** Decompress the huffman table for base luma data */
+ void decompressLuma(Common::SeekableReadStream &stream);
+
+ bool decodeChroma(Common::SeekableReadStream &stream, int chroma_off);
+
+ /** convert the internally expanded YUV to the output RGBA surface */
+ void convertYUVtoRGBSurface();
+
+ /** A buffer to hold the final frame in RGBA */
+ Graphics::Surface _surface;
+
+ /** Dest surface width and height */
+ int _width, _height;
+
+ /** If true, decode chroma vals in Wing Commander 4 style (false = No Regret style) */
+ bool _wc4Mode;
+
+ /** A buffer to hold scratch data. Either chroma data in progress, or
+ * decompressed delta luma values (5-bit). Interpretation depends on frame type. */
+ uint8 *_scratchbuf;
+ /** A buffer to hold expanded/interpolated absolute luma values (6-bit) from the values in _scratchbuf.
+ * These still need to be multiplied out to make 8-bit values. */
+ uint8 *_lumabuf;
+ /** a buffer for uncompressed and multiplied out "y" values (of yuv) */
+ uint8 *_ybuf;
+ /** a buffer for uncompressed "u" values (of yuv) */
+ uint8 *_ubuf;
+ /** a buffer for uncompressed "v" values (of yuv) */
+ uint8 *_vbuf;
+};
+
+} // End of namespace Image
+
+#endif
diff --git a/image/module.mk b/image/module.mk
index 2100f3093c..23424e5ced 100644
--- a/image/module.mk
+++ b/image/module.mk
@@ -26,6 +26,7 @@ MODULE_OBJS := \
codecs/smc.o \
codecs/svq1.o \
codecs/truemotion1.o \
+ codecs/xan.o \
codecs/indeo/indeo.o \
codecs/indeo/indeo_dsp.o \
codecs/indeo/mem.o \
Commit: a20b8781e1fb6f7531f763d771ff7120d4737045
https://github.com/scummvm/scummvm/commit/a20b8781e1fb6f7531f763d771ff7120d4737045
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-03-14T19:16:24+01:00
Commit Message:
VIDEO: Add support for Xan codec videos (Crusader, Wing Commander)
Changed paths:
video/avi_decoder.cpp
video/avi_decoder.h
diff --git a/video/avi_decoder.cpp b/video/avi_decoder.cpp
index 309f0bc122..0266f8bbe5 100644
--- a/video/avi_decoder.cpp
+++ b/video/avi_decoder.cpp
@@ -34,6 +34,7 @@
#include "audio/decoders/adpcm.h"
#include "audio/decoders/mp3.h"
#include "audio/decoders/raw.h"
+#include "audio/decoders/xan_dpcm.h"
// Video Codecs
#include "image/codecs/codec.h"
@@ -70,6 +71,11 @@ namespace Video {
#define ID_PRMI MKTAG('P','R','M','I')
#define ID_STRN MKTAG('s','t','r','n')
#define ID_INDX MKTAG('i','n','d','x')
+#define ID_INDX2 MKTAG('I','N','D','X')
+#define ID__PC_ MKTAG('_','P','C','_')
+#define ID_PAL8 MKTAG('P','A','L','8')
+#define ID_BEST MKTAG('B','E','S','T')
+#define ID_SHOT MKTAG('S','H','O','T')
// Stream Types
enum {
@@ -212,9 +218,16 @@ bool AVIDecoder::parseNextChunk() {
case ID_DISP: // Metadata, should be safe to ignore
case ID_DMLH: // OpenDML extension, contains an extra total frames field, safe to ignore
case ID_INDX: // OpenDML extension, contains another type of index
+ case ID__PC_: // block in Origin Systems Xxan videos
+ case ID_SHOT: // block in Origin Systems Xxan videos
+ case ID_BEST: // block in Origin Systems Xxan videos
+ case ID_INDX2: // block in Origin Systems Xxan videos
skipChunk(size);
break;
- case ID_STRN: // Metadata, safe to ignore
+ case ID_PAL8: // PAL8 block in Origin Systems Xxan videos
+ readPalette8(size);
+ break;
+ case ID_STRN:
readStreamName(size);
break;
case ID_IDX1:
@@ -385,6 +398,28 @@ void AVIDecoder::readStreamName(uint32 size) {
}
}
+void AVIDecoder::readPalette8(uint32 size) {
+ if (size < 768) {
+ warning("AVI palette8 is too small (%d, expected >= 768)", size);
+ skipChunk(size);
+ return;
+ }
+
+ // Should also be able to load 768 byte palette8 entries here.
+ // Not supported at the moment.
+ if (!_lastAddedTrack || size != 1024) {
+ skipChunk(size);
+ } else {
+ AVIVideoTrack *vidTrack = dynamic_cast<AVIVideoTrack *>(_lastAddedTrack);
+ if (vidTrack) {
+ vidTrack->loadPaletteFromChunkRaw(_fileStream, 0, 256);
+ } else {
+ skipChunk(size);
+ warning("unexpected palette8 on a non-video track");
+ }
+ }
+}
+
bool AVIDecoder::loadStream(Common::SeekableReadStream *stream) {
close();
@@ -943,6 +978,19 @@ Graphics::PixelFormat AVIDecoder::AVIVideoTrack::getPixelFormat() const {
return Graphics::PixelFormat();
}
+void AVIDecoder::AVIVideoTrack::loadPaletteFromChunkRaw(Common::SeekableReadStream *chunk, int firstEntry, int numEntries) {
+ assert(chunk);
+ assert(firstEntry >= 0);
+ assert(numEntries > 0);
+ for (uint16 i = firstEntry; i < numEntries + firstEntry; i++) {
+ _palette[i * 3] = chunk->readByte();
+ _palette[i * 3 + 1] = chunk->readByte();
+ _palette[i * 3 + 2] = chunk->readByte();
+ chunk->readByte(); // Flags that don't serve us any purpose
+ }
+ _dirtyPalette = true;
+}
+
void AVIDecoder::AVIVideoTrack::loadPaletteFromChunk(Common::SeekableReadStream *chunk) {
assert(chunk);
byte firstEntry = chunk->readByte();
@@ -953,17 +1001,12 @@ void AVIDecoder::AVIVideoTrack::loadPaletteFromChunk(Common::SeekableReadStream
if (numEntries == 0)
numEntries = 256;
- for (uint16 i = firstEntry; i < numEntries + firstEntry; i++) {
- _palette[i * 3] = chunk->readByte();
- _palette[i * 3 + 1] = chunk->readByte();
- _palette[i * 3 + 2] = chunk->readByte();
- chunk->readByte(); // Flags that don't serve us any purpose
- }
+ loadPaletteFromChunkRaw(chunk, firstEntry, numEntries);
delete chunk;
- _dirtyPalette = true;
}
+
void AVIDecoder::AVIVideoTrack::useInitialPalette() {
_dirtyPalette = false;
@@ -1112,6 +1155,9 @@ void AVIDecoder::AVIAudioTrack::createAudioStream() {
_packetStream = Audio::makePacketizedRawStream(_wvInfo.samplesPerSec, flags);
break;
}
+ case Audio::kWaveFormatXanDPCM:
+ _packetStream = new Audio::XanDPCMStream(_wvInfo.samplesPerSec, _wvInfo.channels);
+ break;
case Audio::kWaveFormatMSADPCM:
_packetStream = Audio::makePacketizedADPCMStream(Audio::kADPCMMS, _wvInfo.samplesPerSec, _wvInfo.channels, _wvInfo.blockAlign);
break;
diff --git a/video/avi_decoder.h b/video/avi_decoder.h
index e0fb0094f9..6ec0d285b5 100644
--- a/video/avi_decoder.h
+++ b/video/avi_decoder.h
@@ -221,6 +221,7 @@ protected:
bool hasDirtyPalette() const;
void setCurFrame(int frame) { _curFrame = frame; }
void loadPaletteFromChunk(Common::SeekableReadStream *chunk);
+ void loadPaletteFromChunkRaw(Common::SeekableReadStream *chunk, int firstEntry, int numEntries);
void useInitialPalette();
bool canDither() const;
void setDither(const byte *palette);
@@ -338,6 +339,7 @@ protected:
void handleList(uint32 listSize);
void handleStreamHeader(uint32 size);
void readStreamName(uint32 size);
+ void readPalette8(uint32 size);
uint16 getStreamType(uint32 tag) const { return tag & 0xFFFF; }
static byte getStreamIndex(uint32 tag);
void checkTruemotion1();
Commit: c4baf30f9bd88148a961ba592d8c5120486175e5
https://github.com/scummvm/scummvm/commit/c4baf30f9bd88148a961ba592d8c5120486175e5
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-03-14T19:16:24+01:00
Commit Message:
ULTIMA8: Add Crusader: No Regret movies
Changed paths:
engines/ultima/ultima8/games/remorse_game.cpp
engines/ultima/ultima8/games/u8_game.cpp
engines/ultima/ultima8/graphics/avi_player.cpp
engines/ultima/ultima8/graphics/avi_player.h
engines/ultima/ultima8/gumps/movie_gump.cpp
engines/ultima/ultima8/gumps/movie_gump.h
engines/ultima/ultima8/gumps/weasel_gump.cpp
engines/ultima/ultima8/misc/debugger.cpp
diff --git a/engines/ultima/ultima8/games/remorse_game.cpp b/engines/ultima/ultima8/games/remorse_game.cpp
index ec95035d35..5a454fd3f1 100644
--- a/engines/ultima/ultima8/games/remorse_game.cpp
+++ b/engines/ultima/ultima8/games/remorse_game.cpp
@@ -148,7 +148,7 @@ bool RemorseGame::startInitialUsecode(int saveSlot) {
}
-static ProcId playMovie(const char *movieID, bool fade) {
+static ProcId playMovie(const char *movieID, bool fade, bool noScale) {
const Std::string filename = Std::string::format("flics/%s.avi", movieID);
FileSystem *filesys = FileSystem::get_instance();
Common::SeekableReadStream *rs = filesys->ReadFile(filename);
@@ -157,20 +157,21 @@ static ProcId playMovie(const char *movieID, bool fade) {
return 0;
}
// TODO: Add support for subtitles (.txt file). The format is very simple.
- return MovieGump::U8MovieViewer(rs, fade);
+ return MovieGump::U8MovieViewer(rs, fade, false, noScale);
}
ProcId RemorseGame::playIntroMovie(bool fade) {
- return playMovie("T01", fade);
+ const char *name = (GAME_IS_REMORSE ? "T01" : "origin");
+ return playMovie(name, fade, true);
}
ProcId RemorseGame::playIntroMovie2(bool fade) {
- return playMovie("T02", fade);
+ const char *name = (GAME_IS_REMORSE ? "T02" : "ANIM01");
+ return playMovie(name, fade, false);
}
-
ProcId RemorseGame::playEndgameMovie(bool fade) {
- return playMovie("O01", fade);
+ return playMovie("O01", fade, false);
}
void RemorseGame::playCredits() {
@@ -178,44 +179,6 @@ void RemorseGame::playCredits() {
}
void RemorseGame::writeSaveInfo(Common::WriteStream *ws) {
-#if 0
- MainActor *av = getMainActor();
- int32 x, y, z;
-
- const Std::string &avname = av->getName();
- uint8 namelength = static_cast<uint8>(avname.size());
- ws->writeByte(namelength);
- for (unsigned int i = 0; i < namelength; ++i)
- ws->writeByte(static_cast<uint8>(avname[i]));
-
- av->getLocation(x, y, z);
- ws->writeUint16LE(av->getMapNum());
- ws->writeUint32LE(static_cast<uint32>(x));
- ws->writeUint32LE(static_cast<uint32>(y));
- ws->writeUint32LE(static_cast<uint32>(z));
-
- ws->writeUint16LE(av->getStr());
- ws->writeUint16LE(av->getInt());
- ws->writeUint16LE(av->getDex());
- ws->writeUint16LE(av->getHP());
- ws->writeUint16LE(av->getMaxHP());
- ws->writeUint16LE(av->getMana());
- ws->writeUint16LE(av->getMaxMana());
- ws->writeUint16LE(av->getArmourClass());
- ws->writeUint16LE(av->getTotalWeight());
-
- for (unsigned int i = 1; i <= 6; i++) {
- const uint16 objid = av->getEquip(i);
- const Item *item = getItem(objid);
- if (item) {
- ws->writeUint32LE(item->getShape());
- ws->writeUint32LE(item->getFrame());
- } else {
- ws->writeUint32LE(0);
- ws->writeUint32LE(0);
- }
- }
-#endif
}
} // End of namespace Ultima8
diff --git a/engines/ultima/ultima8/games/u8_game.cpp b/engines/ultima/ultima8/games/u8_game.cpp
index 467b604298..a8878748c5 100644
--- a/engines/ultima/ultima8/games/u8_game.cpp
+++ b/engines/ultima/ultima8/games/u8_game.cpp
@@ -174,7 +174,7 @@ ProcId U8Game::playIntroMovie(bool fade) {
return 0;
}
- return MovieGump::U8MovieViewer(skf, fade, true);
+ return MovieGump::U8MovieViewer(skf, fade, true, true);
}
ProcId U8Game::playEndgameMovie(bool fade) {
@@ -186,7 +186,7 @@ ProcId U8Game::playEndgameMovie(bool fade) {
return 0;
}
- return MovieGump::U8MovieViewer(skf, fade);
+ return MovieGump::U8MovieViewer(skf, fade, false, true);
}
void U8Game::playCredits() {
diff --git a/engines/ultima/ultima8/graphics/avi_player.cpp b/engines/ultima/ultima8/graphics/avi_player.cpp
index ae5e958715..8469dad12f 100644
--- a/engines/ultima/ultima8/graphics/avi_player.cpp
+++ b/engines/ultima/ultima8/graphics/avi_player.cpp
@@ -28,14 +28,14 @@
namespace Ultima {
namespace Ultima8 {
-AVIPlayer::AVIPlayer(Common::SeekableReadStream *rs, int width, int height, const byte *overridePal)
+AVIPlayer::AVIPlayer(Common::SeekableReadStream *rs, int width, int height, const byte *overridePal, bool noScale)
: MoviePlayer(), _playing(false), _width(width), _height(height),
_doubleSize(false), _pausedMusic(false), _overridePal(overridePal) {
_decoder = new Video::AVIDecoder();
_decoder->loadStream(rs);
uint32 vidWidth = _decoder->getWidth();
uint32 vidHeight = _decoder->getHeight();
- if (vidWidth <= _width / 2 && vidHeight <= _height / 2) {
+ if (vidWidth <= _width / 2 && vidHeight <= _height / 2 && !noScale) {
_doubleSize = true;
vidHeight *= 2;
vidWidth *= 2;
@@ -95,14 +95,18 @@ void AVIPlayer::paint(RenderSurface *surf, int /*lerp*/) {
_currentFrame.setPalette(pal, 0, 256);
}
if (_doubleSize) {
+ // TODO: Add support for multiple bytes per pixel
assert(_currentFrame.w == frame->w * 2 && _currentFrame.h == frame->h * 2);
+ const int bpp = frame->format.bytesPerPixel;
for (int y = 0; y < frame->h; y++) {
const uint8 *srcPixel = static_cast<const uint8 *>(frame->getPixels()) + frame->pitch * y;
uint8 *dstPixels = static_cast<uint8 *>(_currentFrame.getPixels()) + _currentFrame.pitch * y * 2;
for (int x = 0; x < frame->w; x++) {
- dstPixels[x * 2] = *srcPixel;
- dstPixels[x * 2 + 1] = *srcPixel;
- srcPixel++;
+ for (int i = 0; i < bpp; i++) {
+ dstPixels[x * 2 * bpp + i] = *srcPixel;
+ dstPixels[x * 2 * bpp + i + bpp] = *srcPixel;
+ srcPixel++;
+ }
}
}
} else {
diff --git a/engines/ultima/ultima8/graphics/avi_player.h b/engines/ultima/ultima8/graphics/avi_player.h
index fe5eb638d4..4d5e663947 100644
--- a/engines/ultima/ultima8/graphics/avi_player.h
+++ b/engines/ultima/ultima8/graphics/avi_player.h
@@ -35,7 +35,13 @@ namespace Ultima8 {
class AVIPlayer : public MoviePlayer {
public:
- AVIPlayer(Common::SeekableReadStream *rs, int width, int height, const byte *overridePal);
+ //!
+ //! Create a new player for the given stream at the given size. Playback is
+ //! automatically scaled up using the line-skip style from Crusader, unless
+ //! noScale flag is set.
+ //! If overridePal is set, use that as the palette instead of the AVI's one.
+ //!
+ AVIPlayer(Common::SeekableReadStream *rs, int width, int height, const byte *overridePal, bool noScale);
~AVIPlayer();
void run() override;
diff --git a/engines/ultima/ultima8/gumps/movie_gump.cpp b/engines/ultima/ultima8/gumps/movie_gump.cpp
index f7b95c40bc..c77705fc61 100644
--- a/engines/ultima/ultima8/gumps/movie_gump.cpp
+++ b/engines/ultima/ultima8/gumps/movie_gump.cpp
@@ -46,13 +46,13 @@ MovieGump::MovieGump() : ModalGump(), _player(nullptr) {
}
MovieGump::MovieGump(int width, int height, Common::SeekableReadStream *rs,
- bool introMusicHack, const byte *overridePal,
+ bool introMusicHack, bool noScale, const byte *overridePal,
uint32 flags, int32 layer)
: ModalGump(50, 50, width, height, 0, flags, layer) {
uint32 stream_id = rs->readUint32BE();
rs->seek(-4, SEEK_CUR);
if (stream_id == 0x52494646) {// 'RIFF' - crusader AVIs
- _player = new AVIPlayer(rs, width, height, overridePal);
+ _player = new AVIPlayer(rs, width, height, overridePal, noScale);
} else {
_player = new SKFPlayer(rs, width, height, introMusicHack);
}
@@ -116,12 +116,12 @@ bool MovieGump::OnKeyDown(int key, int mod) {
}
//static
-ProcId MovieGump::U8MovieViewer(Common::SeekableReadStream *rs, bool fade, bool introMusicHack) {
+ProcId MovieGump::U8MovieViewer(Common::SeekableReadStream *rs, bool fade, bool introMusicHack, bool noScale) {
ModalGump *gump;
if (GAME_IS_U8)
- gump = new MovieGump(320, 200, rs, introMusicHack);
+ gump = new MovieGump(320, 200, rs, introMusicHack, noScale);
else
- gump = new MovieGump(640, 480, rs, introMusicHack);
+ gump = new MovieGump(640, 480, rs, introMusicHack, noScale);
if (fade) {
FadeToModalProcess *p = new FadeToModalProcess(gump);
@@ -203,7 +203,7 @@ uint32 MovieGump::I_playMovieOverlay(const uint8 *args,
Common::SeekableReadStream *rs = _tryLoadCruMovie(name);
if (rs) {
- Gump *gump = new MovieGump(x, y, rs, false, pal->_palette);
+ Gump *gump = new MovieGump(x, y, rs, false, false, pal->_palette);
gump->InitGump(nullptr, true);
gump->setRelativePosition(CENTER);
}
@@ -221,7 +221,7 @@ uint32 MovieGump::I_playMovieCutscene(const uint8 *args, unsigned int /*argsize*
if (item) {
Common::SeekableReadStream *rs = _tryLoadCruMovie(name);
if (rs) {
- Gump *gump = new MovieGump(x * 3, y * 3, rs, false);
+ Gump *gump = new MovieGump(x * 3, y * 3, rs, false, false);
gump->InitGump(nullptr, true);
gump->setRelativePosition(CENTER);
}
@@ -241,7 +241,7 @@ uint32 MovieGump::I_playMovieCutsceneAlt(const uint8 *args, unsigned int /*argsi
if (item) {
Common::SeekableReadStream *rs = _tryLoadCruMovie(name);
if (rs) {
- Gump *gump = new MovieGump(x * 3, y * 3, rs, false);
+ Gump *gump = new MovieGump(x * 3, y * 3, rs, false, false);
gump->InitGump(nullptr, true);
gump->setRelativePosition(CENTER);
}
diff --git a/engines/ultima/ultima8/gumps/movie_gump.h b/engines/ultima/ultima8/gumps/movie_gump.h
index fc90558a63..ee00741710 100644
--- a/engines/ultima/ultima8/gumps/movie_gump.h
+++ b/engines/ultima/ultima8/gumps/movie_gump.h
@@ -39,7 +39,8 @@ public:
MovieGump();
MovieGump(int width, int height, Common::SeekableReadStream *rs,
- bool introMusicHack = false, const byte *overridePal = nullptr,
+ bool introMusicHack = false, bool noScale = false,
+ const byte *overridePal = nullptr,
uint32 flags = FLAG_PREVENT_SAVE, int32 layer = LAYER_MODAL);
~MovieGump() override;
@@ -54,7 +55,7 @@ public:
bool OnKeyDown(int key, int mod) override;
- static ProcId U8MovieViewer(Common::SeekableReadStream *rs, bool fade, bool introMusicHack = false);
+ static ProcId U8MovieViewer(Common::SeekableReadStream *rs, bool fade, bool introMusicHack, bool noScale);
bool loadData(Common::ReadStream *rs);
void saveData(Common::WriteStream *ws) override;
diff --git a/engines/ultima/ultima8/gumps/weasel_gump.cpp b/engines/ultima/ultima8/gumps/weasel_gump.cpp
index f105b6d873..deeec08df3 100644
--- a/engines/ultima/ultima8/gumps/weasel_gump.cpp
+++ b/engines/ultima/ultima8/gumps/weasel_gump.cpp
@@ -206,7 +206,7 @@ Gump *WeaselGump::playMovie(const Std::string &filename) {
warning("Couldn't load flic %s", filename.c_str());
return nullptr;
}
- Gump *gump = new MovieGump(600, 450, rs, false);
+ Gump *gump = new MovieGump(600, 450, rs, false, false);
gump->InitGump(this, true);
gump->setRelativePosition(CENTER);
gump->CreateNotifier();
diff --git a/engines/ultima/ultima8/misc/debugger.cpp b/engines/ultima/ultima8/misc/debugger.cpp
index a66656859e..d64dd48cc7 100644
--- a/engines/ultima/ultima8/misc/debugger.cpp
+++ b/engines/ultima/ultima8/misc/debugger.cpp
@@ -1759,7 +1759,7 @@ bool Debugger::cmdPlayMovie(int argc, const char **argv) {
return true;
}
- MovieGump::U8MovieViewer(skf, false);
+ MovieGump::U8MovieViewer(skf, false, false, true);
return false;
}
More information about the Scummvm-git-logs
mailing list