From a67e9e16bdbb5acbb2d1eb52a87c3eb2d128c44d Mon Sep 17 00:00:00 2001 From: Thomas Fach-Pedersen Date: Tue, 20 May 2014 11:30:16 +0200 Subject: BLADERUNNER: Rebuild VQADecoder on top of Video::VideoDecoder --- engines/bladerunner/aud_decoder.cpp | 167 ++++++++++ engines/bladerunner/aud_decoder.h | 50 +++ engines/bladerunner/bladerunner.cpp | 15 +- engines/bladerunner/module.mk | 4 +- engines/bladerunner/outtake.cpp | 40 ++- engines/bladerunner/outtake.h | 6 +- engines/bladerunner/vqa_decoder.cpp | 614 +++++++++++++++++++----------------- engines/bladerunner/vqa_decoder.h | 150 ++++++--- engines/bladerunner/vqa_player.cpp | 25 +- engines/bladerunner/vqa_player.h | 6 +- 10 files changed, 723 insertions(+), 354 deletions(-) create mode 100644 engines/bladerunner/aud_decoder.cpp create mode 100644 engines/bladerunner/aud_decoder.h (limited to 'engines') diff --git a/engines/bladerunner/aud_decoder.cpp b/engines/bladerunner/aud_decoder.cpp new file mode 100644 index 0000000000..d11a90e196 --- /dev/null +++ b/engines/bladerunner/aud_decoder.cpp @@ -0,0 +1,167 @@ +/* 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 "bladerunner/aud_decoder.h" + +#include "common/util.h" + +namespace BladeRunner { + +static const +int16 imaIndexTable[8] = { -1, -1, -1, -1, 2, 4, 6, 8 }; + +static const +uint16 imaStepTable[712] = +{ + 0x0000,0x0001,0x0003,0x0004,0x0007,0x0008,0x000a,0x000b, + 0x0001,0x0003,0x0005,0x0007,0x0009,0x000b,0x000d,0x000f, + 0x0001,0x0003,0x0005,0x0007,0x000a,0x000c,0x000e,0x0010, + 0x0001,0x0003,0x0006,0x0008,0x000b,0x000d,0x0010,0x0012, + 0x0001,0x0003,0x0006,0x0008,0x000c,0x000e,0x0011,0x0013, + 0x0001,0x0004,0x0007,0x000a,0x000d,0x0010,0x0013,0x0016, + 0x0001,0x0004,0x0007,0x000a,0x000e,0x0011,0x0014,0x0017, + 0x0001,0x0004,0x0008,0x000b,0x000f,0x0012,0x0016,0x0019, + 0x0002,0x0006,0x000a,0x000e,0x0012,0x0016,0x001a,0x001e, + 0x0002,0x0006,0x000a,0x000e,0x0013,0x0017,0x001b,0x001f, + 0x0002,0x0006,0x000b,0x000f,0x0015,0x0019,0x001e,0x0022, + 0x0002,0x0007,0x000c,0x0011,0x0017,0x001c,0x0021,0x0026, + 0x0002,0x0007,0x000d,0x0012,0x0019,0x001e,0x0024,0x0029, + 0x0003,0x0009,0x000f,0x0015,0x001c,0x0022,0x0028,0x002e, + 0x0003,0x000a,0x0011,0x0018,0x001f,0x0026,0x002d,0x0034, + 0x0003,0x000a,0x0012,0x0019,0x0022,0x0029,0x0031,0x0038, + 0x0004,0x000c,0x0015,0x001d,0x0026,0x002e,0x0037,0x003f, + 0x0004,0x000d,0x0016,0x001f,0x0029,0x0032,0x003b,0x0044, + 0x0005,0x000f,0x0019,0x0023,0x002e,0x0038,0x0042,0x004c, + 0x0005,0x0010,0x001b,0x0026,0x0032,0x003d,0x0048,0x0053, + 0x0006,0x0012,0x001f,0x002b,0x0038,0x0044,0x0051,0x005d, + 0x0006,0x0013,0x0021,0x002e,0x003d,0x004a,0x0058,0x0065, + 0x0007,0x0016,0x0025,0x0034,0x0043,0x0052,0x0061,0x0070, + 0x0008,0x0018,0x0029,0x0039,0x004a,0x005a,0x006b,0x007b, + 0x0009,0x001b,0x002d,0x003f,0x0052,0x0064,0x0076,0x0088, + 0x000a,0x001e,0x0032,0x0046,0x005a,0x006e,0x0082,0x0096, + 0x000b,0x0021,0x0037,0x004d,0x0063,0x0079,0x008f,0x00a5, + 0x000c,0x0024,0x003c,0x0054,0x006d,0x0085,0x009d,0x00b5, + 0x000d,0x0027,0x0042,0x005c,0x0078,0x0092,0x00ad,0x00c7, + 0x000e,0x002b,0x0049,0x0066,0x0084,0x00a1,0x00bf,0x00dc, + 0x0010,0x0030,0x0051,0x0071,0x0092,0x00b2,0x00d3,0x00f3, + 0x0011,0x0034,0x0058,0x007b,0x00a0,0x00c3,0x00e7,0x010a, + 0x0013,0x003a,0x0061,0x0088,0x00b0,0x00d7,0x00fe,0x0125, + 0x0015,0x0040,0x006b,0x0096,0x00c2,0x00ed,0x0118,0x0143, + 0x0017,0x0046,0x0076,0x00a5,0x00d5,0x0104,0x0134,0x0163, + 0x001a,0x004e,0x0082,0x00b6,0x00eb,0x011f,0x0153,0x0187, + 0x001c,0x0055,0x008f,0x00c8,0x0102,0x013b,0x0175,0x01ae, + 0x001f,0x005e,0x009d,0x00dc,0x011c,0x015b,0x019a,0x01d9, + 0x0022,0x0067,0x00ad,0x00f2,0x0139,0x017e,0x01c4,0x0209, + 0x0026,0x0072,0x00bf,0x010b,0x0159,0x01a5,0x01f2,0x023e, + 0x002a,0x007e,0x00d2,0x0126,0x017b,0x01cf,0x0223,0x0277, + 0x002e,0x008a,0x00e7,0x0143,0x01a1,0x01fd,0x025a,0x02b6, + 0x0033,0x0099,0x00ff,0x0165,0x01cb,0x0231,0x0297,0x02fd, + 0x0038,0x00a8,0x0118,0x0188,0x01f9,0x0269,0x02d9,0x0349, + 0x003d,0x00b8,0x0134,0x01af,0x022b,0x02a6,0x0322,0x039d, + 0x0044,0x00cc,0x0154,0x01dc,0x0264,0x02ec,0x0374,0x03fc, + 0x004a,0x00df,0x0175,0x020a,0x02a0,0x0335,0x03cb,0x0460, + 0x0052,0x00f6,0x019b,0x023f,0x02e4,0x0388,0x042d,0x04d1, + 0x005a,0x010f,0x01c4,0x0279,0x032e,0x03e3,0x0498,0x054d, + 0x0063,0x012a,0x01f1,0x02b8,0x037f,0x0446,0x050d,0x05d4, + 0x006d,0x0148,0x0223,0x02fe,0x03d9,0x04b4,0x058f,0x066a, + 0x0078,0x0168,0x0259,0x0349,0x043b,0x052b,0x061c,0x070c, + 0x0084,0x018d,0x0296,0x039f,0x04a8,0x05b1,0x06ba,0x07c3, + 0x0091,0x01b4,0x02d8,0x03fb,0x051f,0x0642,0x0766,0x0889, + 0x00a0,0x01e0,0x0321,0x0461,0x05a2,0x06e2,0x0823,0x0963, + 0x00b0,0x0210,0x0371,0x04d1,0x0633,0x0793,0x08f4,0x0a54, + 0x00c2,0x0246,0x03ca,0x054e,0x06d2,0x0856,0x09da,0x0b5e, + 0x00d5,0x027f,0x042a,0x05d4,0x0780,0x092a,0x0ad5,0x0c7f, + 0x00ea,0x02bf,0x0495,0x066a,0x0840,0x0a15,0x0beb,0x0dc0, + 0x0102,0x0306,0x050b,0x070f,0x0914,0x0b18,0x0d1d,0x0f21, + 0x011c,0x0354,0x058c,0x07c4,0x09fc,0x0c34,0x0e6c,0x10a4, + 0x0138,0x03a8,0x0619,0x0889,0x0afb,0x0d6b,0x0fdc,0x124c, + 0x0157,0x0406,0x06b5,0x0964,0x0c14,0x0ec3,0x1172,0x1421, + 0x017a,0x046e,0x0762,0x0a56,0x0d4a,0x103e,0x1332,0x1626, + 0x019f,0x04de,0x081e,0x0b5d,0x0e9e,0x11dd,0x151d,0x185c, + 0x01c9,0x055c,0x08ef,0x0c82,0x1015,0x13a8,0x173b,0x1ace, + 0x01f7,0x05e5,0x09d4,0x0dc2,0x11b1,0x159f,0x198e,0x1d7c, + 0x0229,0x067c,0x0acf,0x0f22,0x1375,0x17c8,0x1c1b,0x206e, + 0x0260,0x0721,0x0be3,0x10a4,0x1567,0x1a28,0x1eea,0x23ab, + 0x029d,0x07d8,0x0d14,0x124f,0x178b,0x1cc6,0x2202,0x273d, + 0x02e0,0x08a1,0x0e63,0x1424,0x19e6,0x1fa7,0x2569,0x2b2a, + 0x032a,0x097f,0x0fd4,0x1629,0x1c7e,0x22d3,0x2928,0x2f7d, + 0x037b,0x0a72,0x1169,0x1860,0x1f57,0x264e,0x2d45,0x343c, + 0x03d4,0x0b7d,0x1326,0x1acf,0x2279,0x2a22,0x31cb,0x3974, + 0x0436,0x0ca3,0x1511,0x1d7e,0x25ec,0x2e59,0x36c7,0x3f34, + 0x04a2,0x0de7,0x172c,0x2071,0x29b7,0x32fc,0x3c41,0x4586, + 0x0519,0x0f4b,0x197e,0x23b0,0x2de3,0x3815,0x4248,0x4c7a, + 0x059b,0x10d2,0x1c0a,0x2741,0x327a,0x3db1,0x48e9,0x5420, + 0x062b,0x1281,0x1ed8,0x2b2e,0x3786,0x43dc,0x5033,0x5c89, + 0x06c9,0x145b,0x21ee,0x2f80,0x3d14,0x4aa6,0x5839,0x65cb, + 0x0777,0x1665,0x2553,0x3441,0x4330,0x521e,0x610c,0x6ffa, + 0x0836,0x18a2,0x290f,0x397b,0x49e8,0x5a54,0x6ac1,0x7b2d, + 0x0908,0x1b19,0x2d2a,0x3f3b,0x514c,0x635d,0x756e,0x877f, + 0x09ef,0x1dce,0x31ae,0x458d,0x596d,0x6d4c,0x812c,0x950b, + 0x0aee,0x20ca,0x36a6,0x4c82,0x625f,0x783b,0x8e17,0xa3f3, + 0x0c05,0x2410,0x3c1c,0x5427,0x6c34,0x843f,0x9c4b,0xb456, + 0x0d39,0x27ac,0x4220,0x5c93,0x7707,0x917a,0xabee,0xc661, + 0x0e8c,0x2ba4,0x48bd,0x65d5,0x82ee,0xa006,0xbd1f,0xda37, + 0x0fff,0x2ffe,0x4ffe,0x6ffd,0x8ffe,0xaffd,0xcffd,0xeffc +}; + +void ADPCMWestwoodDecoder::decode(uint8 *in, size_t size, int16 *out) +{ + uint8 *end = in + size; + + int16 stepIndex = _stepIndex; + int32 predictor = _predictor; + + while (in != end) + { + uint16 bl = *in++; + + for (int n = 0; n != 2; ++n) + { + uint8 nibble = (bl >> (4 * n)) & 0x0f; + uint8 code = nibble & 0x07; + uint8 sign = nibble & 0x08; + + int diff = imaStepTable[(stepIndex << 3) | code]; + + // Westwood's IMA ADPCM differs from the below standard implementation + // in the LSB in a couple of places. + //int diff = imaStepTable_std[stepIndex] * code / 4 + imaStepTable_std[stepIndex] / 8; + + if (sign) + predictor -= diff; + else + predictor += diff; + + predictor = CLIP(predictor, -32768, 32767); + + *out++ = (int16)predictor; + + stepIndex = imaIndexTable[code] + stepIndex; + stepIndex = CLIP(stepIndex, 0, 88); + } + } + + _stepIndex = stepIndex; + _predictor = predictor; +} + +}; diff --git a/engines/bladerunner/aud_decoder.h b/engines/bladerunner/aud_decoder.h new file mode 100644 index 0000000000..dbf7ca1dfa --- /dev/null +++ b/engines/bladerunner/aud_decoder.h @@ -0,0 +1,50 @@ +/* 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 BLADERUNNER_AUD_DECODER_H +#define BLADERUNNER_AUD_DECODER_H + +#include "common/types.h" + +namespace BladeRunner { + +class ADPCMWestwoodDecoder { + int16 _stepIndex; + int32 _predictor; + +public: + ADPCMWestwoodDecoder() + : _stepIndex(0), _predictor(0) + {} + + void setParameters(int16 stepIndex, int32 predictor) + { + _stepIndex = stepIndex; + _predictor = predictor; + } + + void decode(uint8 *in, size_t size, int16 *out); +}; + +}; + +#endif diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp index bcf8c090a8..e914f1c032 100644 --- a/engines/bladerunner/bladerunner.cpp +++ b/engines/bladerunner/bladerunner.cpp @@ -58,13 +58,12 @@ Common::Error BladeRunnerEngine::run() { startup(); - if (!warnUserAboutUnsupportedGame()) - return Common::kNoError; + if (warnUserAboutUnsupportedGame()) { + init2(); - init2(); - - /* TODO: Check for save games and enter KIA */ - gameLoop(); + /* TODO: Check for save games and enter KIA */ + gameLoop(); + } shutdown(); @@ -169,6 +168,10 @@ void BladeRunnerEngine::gameTick() { // TODO: Only run if not in Kia, script, nor AI _settings->openNewScene(); + outtakePlay(28, true); + outtakePlay(41, true); + outtakePlay( 0, false); + // TODO: Autosave // TODO: Kia // TODO: Spinner diff --git a/engines/bladerunner/module.mk b/engines/bladerunner/module.mk index 44e681abf4..23113a9a60 100644 --- a/engines/bladerunner/module.mk +++ b/engines/bladerunner/module.mk @@ -2,6 +2,7 @@ MODULE := engines/bladerunner MODULE_OBJS = \ archive.o \ + aud_decoder.o \ bladerunner.o \ chapters.o \ decompress_lcw.o \ @@ -11,8 +12,7 @@ MODULE_OBJS = \ image.o \ outtake.o \ settings.o \ - vqa_decoder.o \ - vqa_player.o + vqa_decoder.o # This module can be built as a plugin ifeq ($(ENABLE_BLADERUNNER), DYNAMIC_PLUGIN) diff --git a/engines/bladerunner/outtake.cpp b/engines/bladerunner/outtake.cpp index 01ceefad75..6ebede5663 100644 --- a/engines/bladerunner/outtake.cpp +++ b/engines/bladerunner/outtake.cpp @@ -23,8 +23,10 @@ #include "bladerunner/outtake.h" #include "bladerunner/bladerunner.h" -#include "bladerunner/vqa_player.h" +#include "bladerunner/vqa_decoder.h" +#include "common/debug.h" +#include "common/events.h" #include "common/system.h" namespace BladeRunner { @@ -41,27 +43,33 @@ void OuttakePlayer::play(const Common::String &name, bool noLocalization, int co else resName = name + "_E.VQA"; - _vqaPlayer = new VQAPlayer(_vm, &_vm->_surface1); - _vqaPlayer->open(resName); + Common::SeekableReadStream *s = _vm->getResourceStream(resName); - for (;;) { - _vm->handleEvents(); - if (_vm->shouldQuit()) - break; + _vqaDecoder = new VQADecoder(); + _vqaDecoder->loadStream(s); - int r = _vqaPlayer->update(); - if (r == -3) - break; + _vqaDecoder->start(); - if (r >= 0) { - _vm->_system->copyRectToScreen(_vm->_surface1.getPixels(), _vm->_surface1.pitch, 0, 0, _vm->_surface1.w, _vm->_surface1.h); + uint32 last = _vm->_system->getMillis(); + + while (!_vqaDecoder->endOfVideo() && !_vm->shouldQuit()) { + if (_vqaDecoder->needsUpdate()) { + uint32 now = _vm->_system->getMillis(); + debug("delta: %d", now - last); + last = now; + + const Graphics::Surface *surface = _vqaDecoder->decodeNextFrame(); + _vm->_system->copyRectToScreen((const byte *)surface->getBasePtr(0, 0), surface->pitch, 0, 0, 640, 480); _vm->_system->updateScreen(); - _vm->_system->delayMillis(10); } - } - delete _vqaPlayer; - _vqaPlayer = nullptr; + Common::Event event; + while (_vm->_system->getEventManager()->pollEvent(event)) + if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP) + return; + + _vm->_system->delayMillis(10); + } } }; // End of namespace BladeRunner diff --git a/engines/bladerunner/outtake.h b/engines/bladerunner/outtake.h index 4e97b1b516..9182b878c0 100644 --- a/engines/bladerunner/outtake.h +++ b/engines/bladerunner/outtake.h @@ -30,20 +30,20 @@ namespace BladeRunner { class BladeRunnerEngine; -class VQAPlayer; +class VQADecoder; class OuttakePlayer { BladeRunnerEngine *_vm; bool _isVQAOpen; - VQAPlayer *_vqaPlayer; + VQADecoder *_vqaDecoder; Graphics::Surface *_surface; public: OuttakePlayer(BladeRunnerEngine *vm) : _vm(vm), _isVQAOpen(false), - _vqaPlayer(nullptr) + _vqaDecoder(nullptr) {} void play(const Common::String &name, bool noLocalization, int container); diff --git a/engines/bladerunner/vqa_decoder.cpp b/engines/bladerunner/vqa_decoder.cpp index f4676d39d6..bd898bc171 100644 --- a/engines/bladerunner/vqa_decoder.cpp +++ b/engines/bladerunner/vqa_decoder.cpp @@ -22,9 +22,12 @@ #include "bladerunner/vqa_decoder.h" +#include "bladerunner/bladerunner.h" #include "bladerunner/decompress_lcw.h" #include "bladerunner/decompress_lzo.h" +#include "audio/decoders/raw.h" + #include "common/array.h" #include "common/util.h" @@ -63,23 +66,14 @@ namespace BladeRunner { #define kWVQA 0x57565141 #define kZBUF 0x5A425546 -VQADecoder::VQADecoder() - : _s(nullptr), - _frame(0), - _zbuf(0), - _codebook(0), - _cbfz(0), - _vptr(0), - _hasView(false), - _audioFrame(0), - _maxVIEWChunkSize(0), - _maxZBUFChunkSize(0), - _maxAESCChunkSize(0) -{ -} +int32 remain(Common::SeekableReadStream *s) { + int32 pos = s->pos(); + if (pos == -1) return -1; -VQADecoder::~VQADecoder() -{ + int32 size = s->size(); + if (size == -1) return -1; + + return size - pos; } struct IFFChunkHeader @@ -92,23 +86,6 @@ struct IFFChunkHeader uint32 size; }; -static inline uint32 roundup(uint32 v) -{ - return (v + 1) & ~1u; -} - -const char *strTag(uint32 tag); - -int32 remain(Common::SeekableReadStream *s) { - int32 pos = s->pos(); - if (pos == -1) return -1; - - int32 size = s->size(); - if (size == -1) return -1; - - return size - pos; -} - static bool readIFFChunkHeader(Common::SeekableReadStream *s, IFFChunkHeader *ts) { @@ -118,12 +95,14 @@ bool readIFFChunkHeader(Common::SeekableReadStream *s, IFFChunkHeader *ts) ts->id = s->readUint32BE(); ts->size = s->readUint32BE(); - // if (ts->size != roundup(ts->size)) - // debug("%s: %d", strTag(ts->id), ts->size); - return true; } +static inline uint32 roundup(uint32 v) +{ + return (v + 1) & ~1u; +} + const char *strTag(uint32 tag) { static char s[5]; @@ -137,8 +116,21 @@ const char *strTag(uint32 tag) return s; } -bool VQADecoder::open(Common::SeekableReadStream *s) +VQADecoder::VQADecoder() + : _s(nullptr), + _maxVIEWChunkSize(0), + _maxZBUFChunkSize(0), + _maxAESCChunkSize(0) { +} + +VQADecoder::~VQADecoder() { +} + +bool VQADecoder::loadStream(Common::SeekableReadStream *s) { + close(); + _s = s; + IFFChunkHeader chd; uint32 type; bool rc; @@ -152,81 +144,124 @@ bool VQADecoder::open(Common::SeekableReadStream *s) if (type != kWVQA) return false; - _s = s; do { if (!readIFFChunkHeader(_s, &chd)) return false; - debug("\t%s : %x", strTag(chd.id), chd.size); - rc = false; switch (chd.id) { - case kCINF: rc = readCINF(chd.size); break; - case kCLIP: rc = readCLIP(chd.size); break; - case kFINF: rc = readFINF(chd.size); break; - case kLINF: rc = readLINF(chd.size); break; - case kLNIN: rc = readLNIN(chd.size); break; - case kMFCI: rc = readMFCI(chd.size); break; - case kMSCI: rc = readMSCI(chd.size); break; - case kVQHD: rc = readVQHD(chd.size); break; + case kCINF: rc = readCINF(s, chd.size); break; + case kCLIP: rc = readCLIP(s, chd.size); break; + case kFINF: rc = readFINF(s, chd.size); break; + case kLINF: rc = readLINF(s, chd.size); break; + case kLNIN: rc = readLNIN(s, chd.size); break; + case kMFCI: rc = readMFCI(s, chd.size); break; + case kMSCI: rc = readMSCI(s, chd.size); break; + case kVQHD: rc = readVQHD(s, chd.size); break; default: debug("Unhandled chunk '%s'", strTag(chd.id)); - _s->skip(roundup(chd.size)); + s->skip(roundup(chd.size)); rc = true; } - if (!rc) - { + if (!rc) { debug("failed to handle chunk %s", strTag(chd.id)); - return -1; + return false; } - } while (chd.id != kFINF); + _videoTrack = new VQAVideoTrack(this); + addTrack(_videoTrack); + + _audioTrack = new VQAAudioTrack(this); + addTrack(_audioTrack); + + /* for (int i = 0; i != _loopInfo.loopCount; ++i) { debug("LOOP %2d: %4d %4d %s", i, _loopInfo.loops[i].begin, _loopInfo.loops[i].end, _loopInfo.loops[i].name.c_str()); } + */ return true; } -bool VQADecoder::readVQHD(uint32 size) +void VQADecoder::readNextPacket() { + IFFChunkHeader chd; + + if (remain(_s) < 8) { + warning("remain: %d", remain(_s)); + assert(remain(_s) < 8); + } + + do { + if (!readIFFChunkHeader(_s, &chd)) { + warning("Error reading chunk header"); + return; + } + + bool rc = false; + switch (chd.id) { + // Video track + case kAESC: rc = _videoTrack->readAESC(_s, chd.size); break; + case kLITE: rc = _videoTrack->readLITE(_s, chd.size); break; + case kVIEW: rc = _videoTrack->readVIEW(_s, chd.size); break; + case kVQFL: rc = _videoTrack->readVQFL(_s, chd.size); break; + case kVQFR: rc = _videoTrack->readVQFR(_s, chd.size); break; + case kZBUF: rc = _videoTrack->readZBUF(_s, chd.size); break; + // Sound track + case kSN2J: rc = _audioTrack->readSN2J(_s, chd.size); break; + case kSND2: rc = _audioTrack->readSND2(_s, chd.size); break; + + default: + _s->skip(roundup(chd.size)); + rc = false; + } + + if (!rc) { + warning("Error handling chunk %s", strTag(chd.id)); + return; + } + } while (chd.id != kVQFR); +} + +bool VQADecoder::readVQHD(Common::SeekableReadStream *s, uint32 size) { if (size != 42) return false; - _header.version = _s->readUint16LE(); - _header.flags = _s->readUint16LE(); - _header.numFrames = _s->readUint16LE(); - _header.width = _s->readUint16LE(); - _header.height = _s->readUint16LE(); - _header.blockW = _s->readByte(); - _header.blockH = _s->readByte(); - _header.frameRate = _s->readByte(); - _header.cbParts = _s->readByte(); - _header.colors = _s->readUint16LE(); - _header.maxBlocks = _s->readUint16LE(); - _header.offset_x = _s->readUint16LE(); - _header.offset_y = _s->readUint16LE(); - _header.maxVPTRSize = _s->readUint16LE(); - _header.freq = _s->readUint16LE(); - _header.channels = _s->readByte(); - _header.bits = _s->readByte(); - _header.unk3 = _s->readUint32LE(); - _header.unk4 = _s->readUint16LE(); - _header.maxCBFZSize = _s->readUint32LE(); - _header.unk5 = _s->readUint32LE(); - - if (_header.offset_x || _header.offset_y) + _header.version = s->readUint16LE(); + _header.flags = s->readUint16LE(); + _header.numFrames = s->readUint16LE(); + _header.width = s->readUint16LE(); + _header.height = s->readUint16LE(); + _header.blockW = s->readByte(); + _header.blockH = s->readByte(); + _header.frameRate = s->readByte(); + _header.cbParts = s->readByte(); + _header.colors = s->readUint16LE(); + _header.maxBlocks = s->readUint16LE(); + _header.offsetX = s->readUint16LE(); + _header.offsetY = s->readUint16LE(); + _header.maxVPTRSize = s->readUint16LE(); + _header.freq = s->readUint16LE(); + _header.channels = s->readByte(); + _header.bits = s->readByte(); + _header.unk3 = s->readUint32LE(); + _header.unk4 = s->readUint16LE(); + _header.maxCBFZSize = s->readUint32LE(); + _header.unk5 = s->readUint32LE(); + + if (_header.offsetX || _header.offsetY) { - debug("_header.offset_x, _header.offset_y: %d %d", _header.offset_x, _header.offset_y); + debug("_header.offsetX, _header.offsetY: %d %d", _header.offsetX, _header.offsetY); } // if (_header.unk3 || _header.unk4 != 4 || _header.unk5 || _header.flags != 0x0014) + if (false) { debug("_header.version %d", _header.version); debug("_header.flags %04x", _header.flags); @@ -239,8 +274,8 @@ bool VQADecoder::readVQHD(uint32 size) debug("_header.cbParts %d", _header.cbParts); debug("_header.colors %d", _header.colors); debug("_header.maxBlocks %d", _header.maxBlocks); - debug("_header.offsetX %d", _header.offset_x); - debug("_header.offsetY %d", _header.offset_y); + debug("_header.offsetX %d", _header.offsetX); + debug("_header.offsetY %d", _header.offsetY); debug("_header.maxVPTRSize %d", _header.maxVPTRSize); debug("_header.freq %d", _header.freq); debug("_header.channels %d", _header.channels); @@ -251,12 +286,39 @@ bool VQADecoder::readVQHD(uint32 size) debug("_header.unk5 %d", _header.unk5); } - // exit(-1); + return true; +} + +bool VQADecoder::VQAVideoTrack::readVQFR(Common::SeekableReadStream *s, uint32 size) +{ + IFFChunkHeader chd; + + while (size >= 8) + { + if (!readIFFChunkHeader(s, &chd)) + return false; + size -= roundup(chd.size) + 8; + + bool rc = false; + switch (chd.id) + { + case kCBFZ: rc = readCBFZ(s, chd.size); break; + case kVPTR: rc = readVPTR(s, chd.size); break; + default: + s->skip(roundup(chd.size)); + } + + if (!rc) + { + debug("VQFR: error handling chunk %s", strTag(chd.id)); + return false; + } + } return true; } -bool VQADecoder::readMSCI(uint32 size) +bool VQADecoder::readMSCI(Common::SeekableReadStream *s, uint32 size) { IFFChunkHeader chd; readIFFChunkHeader(_s, &chd); @@ -265,8 +327,8 @@ bool VQADecoder::readMSCI(uint32 size) return false; uint32 count, unk0; - count = _s->readUint32LE(); - unk0 = _s->readUint32LE(); + count = s->readUint32LE(); + unk0 = s->readUint32LE(); assert(unk0 == 0); readIFFChunkHeader(_s, &chd); @@ -276,8 +338,8 @@ bool VQADecoder::readMSCI(uint32 size) for (uint32 i = 0; i < count; ++i) { uint32 tag, size; - tag = _s->readUint32BE(); - size = _s->readUint32LE(); + tag = s->readUint32BE(); + size = s->readUint32LE(); switch (tag) { @@ -287,7 +349,6 @@ bool VQADecoder::readMSCI(uint32 size) break; case kZBUF: _maxZBUFChunkSize = size; - _zbufChunk = new uint8[roundup(_maxZBUFChunkSize)]; debug("max ZBUF size: %08x", _maxZBUFChunkSize); break; case kAESC: @@ -299,14 +360,14 @@ bool VQADecoder::readMSCI(uint32 size) } uint32 zero; - zero = _s->readUint32LE(); assert(zero == 0); - zero = _s->readUint32LE(); assert(zero == 0); + zero = s->readUint32LE(); assert(zero == 0); + zero = s->readUint32LE(); assert(zero == 0); } return true; } -bool VQADecoder::readLINF(uint32 size) +bool VQADecoder::readLINF(Common::SeekableReadStream *s, uint32 size) { IFFChunkHeader chd; readIFFChunkHeader(_s, &chd); @@ -314,8 +375,8 @@ bool VQADecoder::readLINF(uint32 size) if (chd.id != kLINH || chd.size != 6) return false; - _loopInfo.loopCount = _s->readUint16LE(); - _loopInfo.flags = _s->readUint32LE(); + _loopInfo.loopCount = s->readUint16LE(); + _loopInfo.flags = s->readUint32LE(); if ((_loopInfo.flags & 3) == 0) return false; @@ -327,8 +388,8 @@ bool VQADecoder::readLINF(uint32 size) _loopInfo.loops = new Loop[_loopInfo.loopCount]; for (int i = 0; i != _loopInfo.loopCount; ++i) { - _loopInfo.loops[i].begin = _s->readUint16LE(); - _loopInfo.loops[i].end = _s->readUint16LE(); + _loopInfo.loops[i].begin = s->readUint16LE(); + _loopInfo.loops[i].end = s->readUint16LE(); // debug("Loop %d: %04x %04x", i, _loopInfo.loops[i].begin, _loopInfo.loops[i].end); } @@ -336,7 +397,7 @@ bool VQADecoder::readLINF(uint32 size) return true; } -bool VQADecoder::readCINF(uint32 size) +bool VQADecoder::readCINF(Common::SeekableReadStream *s, uint32 size) { IFFChunkHeader chd; @@ -344,8 +405,8 @@ bool VQADecoder::readCINF(uint32 size) if (chd.id != kCINH || chd.size != 8u) return false; - _clipInfo.clipCount = _s->readUint16LE(); - _s->skip(6); + _clipInfo.clipCount = s->readUint16LE(); + s->skip(6); readIFFChunkHeader(_s, &chd); if (chd.id != kCIND || chd.size != 6u * _clipInfo.clipCount) @@ -355,15 +416,14 @@ bool VQADecoder::readCINF(uint32 size) { uint16 a; uint32 b; - a = _s->readUint16LE(); - b = _s->readUint32LE(); - debug("%4d %08x", a, b); + a = s->readUint16LE(); + b = s->readUint32LE(); } return true; } -bool VQADecoder::readFINF(uint32 size) +bool VQADecoder::readFINF(Common::SeekableReadStream *s, uint32 size) { if (size != 4u * _header.numFrames) return false; @@ -371,7 +431,7 @@ bool VQADecoder::readFINF(uint32 size) _frameInfo = new uint32[_header.numFrames]; for (uint32 i = 0; i != _header.numFrames; ++i) - _frameInfo[i] = _s->readUint32LE(); + _frameInfo[i] = s->readUint32LE(); if (false) { uint32 last = 0; @@ -386,7 +446,7 @@ bool VQADecoder::readFINF(uint32 size) return true; } -bool VQADecoder::readLNIN(uint32 size) +bool VQADecoder::readLNIN(Common::SeekableReadStream *s, uint32 size) { IFFChunkHeader chd; @@ -396,11 +456,11 @@ bool VQADecoder::readLNIN(uint32 size) uint16 loopNamesCount, loopUnk1, loopUnk2, loopUnk3, loopUnk4; - loopNamesCount = _s->readUint16LE(); - loopUnk1 = _s->readUint16LE(); - loopUnk2 = _s->readUint16LE(); - loopUnk3 = _s->readUint16LE(); - loopUnk4 = _s->readUint16LE(); + loopNamesCount = s->readUint16LE(); + loopUnk1 = s->readUint16LE(); + loopUnk2 = s->readUint16LE(); + loopUnk3 = s->readUint16LE(); + loopUnk4 = s->readUint16LE(); if (loopNamesCount != _loopInfo.loopCount) return false; @@ -411,7 +471,7 @@ bool VQADecoder::readLNIN(uint32 size) uint32 *loopNameOffsets = (uint32*)alloca(loopNamesCount * sizeof(uint32)); for (int i = 0; i != loopNamesCount; ++i) { - loopNameOffsets[i] = _s->readUint32LE(); + loopNameOffsets[i] = s->readUint32LE(); } readIFFChunkHeader(_s, &chd); @@ -419,7 +479,7 @@ bool VQADecoder::readLNIN(uint32 size) return false; char *names = (char*)alloca(roundup(chd.size)); - _s->read(names, roundup(chd.size)); + s->read(names, roundup(chd.size)); for (int i = 0; i != loopNamesCount; ++i) { char *begin = names + loopNameOffsets[i]; @@ -431,157 +491,113 @@ bool VQADecoder::readLNIN(uint32 size) return true; } -bool VQADecoder::readCLIP(uint32 size) -{ - _s->skip(roundup(size)); +bool VQADecoder::readCLIP(Common::SeekableReadStream *s, uint32 size) { + s->skip(roundup(size)); return true; } -bool VQADecoder::readMFCI(uint32 size) -{ - _s->skip(roundup(size)); +bool VQADecoder::readMFCI(Common::SeekableReadStream *s, uint32 size) { + s->skip(roundup(size)); return true; } -bool VQADecoder::readFrame() -{ - IFFChunkHeader chd; - - if (!_s) - return false; - - _hasView = false; +VQADecoder::VQAVideoTrack::VQAVideoTrack(VQADecoder *vqaDecoder) { + VQADecoder::Header *header = &vqaDecoder->_header; - if (remain(_s) < 8) { - debug("remain: %d", remain(_s)); - return false; - } + _surface = nullptr; + _hasNewFrame = false; - do { - if (!readIFFChunkHeader(_s, &chd)) { - debug("Error reading chunk header"); - return false; - } + _numFrames = header->numFrames; + _width = header->width; + _height = header->height; + _blockW = header->blockW; + _blockH = header->blockH; + _frameRate = header->frameRate; + _maxBlocks = header->maxBlocks; + _offsetX = header->offsetX; + _offsetY = header->offsetY; - // debug("%s ", strTag(chd.id)); + _maxVPTRSize = header->maxVPTRSize; + _maxCBFZSize = header->maxCBFZSize; + _maxZBUFChunkSize = vqaDecoder->_maxZBUFChunkSize; - bool rc = false; - switch (chd.id) - { - case kAESC: rc = readAESC(chd.size); break; - case kLITE: rc = readLITE(chd.size); break; - case kSN2J: rc = readSN2J(chd.size); break; - case kSND2: rc = readSND2(chd.size); break; - case kVIEW: rc = readVIEW(chd.size); break; - case kVQFL: rc = readVQFL(chd.size); break; - case kVQFR: rc = readVQFR(chd.size); break; - case kZBUF: rc = readZBUF(chd.size); break; - default: - _s->skip(roundup(chd.size)); - rc = true; - } + _codebookSize = 0; + _codebook = nullptr; + _cbfz = nullptr; + _zbufChunk = nullptr; - if (!rc) - { - debug("Error handling chunk %s", strTag(chd.id)); - return false; - } - } while (chd.id != kVQFR); - - return true; -} - - -bool VQADecoder::readSN2J(uint32 size) -{ - if (size != 6) - return false; + _vptrSize = 0; + _vptr = nullptr; - uint16 step_index; - uint32 predictor; + _curFrame = -1; - step_index = _s->readUint16LE(); - predictor = _s->readUint32LE(); - // ima_adpcm_ws_decoder.set_parameters(step_index >> 5, predictor); + _zbufChunk = new uint8[roundup(_maxZBUFChunkSize)]; - return true; + _surface = new Graphics::Surface(); + _surface->create(_width, _height, RGB555); } -bool VQADecoder::readSND2(uint32 size) -{ - if (size != 735) - { - debug("audio frame size: %d", size); - return false; - } - - if (!_audioFrame) - _audioFrame = new int16[2 * size]; - memset(_audioFrame, 0, 4 * size); - - uint8 *inFrame = new uint8[roundup(size)]; - _s->read(inFrame, roundup(size)); +VQADecoder::VQAVideoTrack::~VQAVideoTrack() { + delete[] _codebook; + delete[] _cbfz; + delete[] _zbufChunk; + delete[] _vptr; - // ima_adpcm_ws_decoder.decode(inFrame, size, _audioFrame); + delete _surface; +} - delete[] inFrame; +uint16 VQADecoder::VQAVideoTrack::getWidth() const { + return _width; +} - return true; +uint16 VQADecoder::VQAVideoTrack::getHeight() const { + return _height; } -bool VQADecoder::readVQFR(uint32 size) -{ - IFFChunkHeader chd; +Graphics::PixelFormat VQADecoder::VQAVideoTrack::getPixelFormat() const { + return _surface->format; +} - while (size >= 8) - { - if (!readIFFChunkHeader(_s, &chd)) - return false; - size -= roundup(chd.size) + 8; +int VQADecoder::VQAVideoTrack::getCurFrame() const { + return _curFrame; +} - // debug("(%s) ", strTag(chd.id)); fflush(0); +int VQADecoder::VQAVideoTrack::getFrameCount() const { + return _numFrames; +} - bool rc = false; - switch (chd.id) - { - case kCBFZ: rc = readCBFZ(chd.size); break; - case kVPTR: rc = readVPTR(chd.size); break; - default: - _s->skip(roundup(chd.size)); - } +Common::Rational VQADecoder::VQAVideoTrack::getFrameRate() const { + return _frameRate; +} - if (!rc) - { - debug("VQFR: error handling chunk %s", strTag(chd.id)); - return false; - } +const Graphics::Surface *VQADecoder::VQAVideoTrack::decodeNextFrame() { + debug("_curFrame: %d", _curFrame); + if (_hasNewFrame) { + decodeFrame((uint16*)_surface->getPixels()); + _curFrame++; + _hasNewFrame = false; } - - return true; + return _surface; } -bool VQADecoder::readVQFL(uint32 size) -{ +bool VQADecoder::VQAVideoTrack::readVQFL(Common::SeekableReadStream *s, uint32 size) { IFFChunkHeader chd; - while (size >= 8) - { - if (!readIFFChunkHeader(_s, &chd)) + while (size >= 8) { + if (!readIFFChunkHeader(s, &chd)) return false; size -= roundup(chd.size) + 8; bool rc = false; - switch (chd.id) - { - case kCBFZ: rc = readCBFZ(chd.size); break; + switch (chd.id) { + case kCBFZ: rc = readCBFZ(s, chd.size); break; default: - _s->skip(roundup(chd.size)); + s->skip(roundup(chd.size)); } - if (!rc) - { - debug("VQFL: error handling chunk %s", strTag(chd.id)); + if (!rc) { + warning("VQFL: error handling chunk %s", strTag(chd.id)); return false; } } @@ -589,29 +605,27 @@ bool VQADecoder::readVQFL(uint32 size) return true; } -bool VQADecoder::readCBFZ(uint32 size) -{ - if (size > _header.maxCBFZSize) - { - debug("%d > %d", size, _header.maxCBFZSize); +bool VQADecoder::VQAVideoTrack::readCBFZ(Common::SeekableReadStream *s, uint32 size) { + if (size > _maxCBFZSize) { + debug("%d > %d", size, _maxCBFZSize); return false; } - if (!_codebook) - { - _codebookSize = 2 * _header.maxBlocks * _header.blockW * _header.blockH; + if (!_codebook) { + _codebookSize = 2 * _maxBlocks * _blockW * _blockH; _codebook = new uint8[_codebookSize]; } if (!_cbfz) - _cbfz = new uint8[roundup(_header.maxCBFZSize)]; + _cbfz = new uint8[roundup(_maxCBFZSize)]; - _s->read(_cbfz, roundup(size)); + s->read(_cbfz, roundup(size)); decompress_lcw(_cbfz, size, _codebook, _codebookSize); return true; } +#if 0 static int decodeZBUF_partial(uint8 *src, uint16 *curZBUF, uint32 srcLen) { @@ -655,52 +669,58 @@ int decodeZBUF_partial(uint8 *src, uint16 *curZBUF, uint32 srcLen) } return dstSize - dstRemain; } +#endif -bool VQADecoder::readZBUF(uint32 size) +bool VQADecoder::VQAVideoTrack::readZBUF(Common::SeekableReadStream *s, uint32 size) { + s->skip(roundup(size)); + return true; +#if 0 if (size > _maxZBUFChunkSize) { debug("VQA ERROR: ZBUF chunk size: %08x > %08x", size, _maxZBUFChunkSize); - _s->skip(roundup(size)); + s->skip(roundup(size)); return false; } uint32 width, height, complete, unk0; - width = _s->readUint32LE(); - height = _s->readUint32LE(); - complete = _s->readUint32LE(); - unk0 = _s->readUint32LE(); + width = s->readUint32LE(); + height = s->readUint32LE(); + complete = s->readUint32LE(); + unk0 = s->readUint32LE(); uint32 remain = size - 16; - if (width != _header.width || height != _header.height) + if (width != width || height != height) { debug("%d, %d, %d, %d", width, height, complete, unk0); - _s->skip(roundup(remain)); + s->skip(roundup(remain)); return false; } + s->read(_zbufChunk, roundup(remain)); + if (!_zbuf) { if (!complete) { - _s->skip(roundup(remain)); + s->skip(roundup(remain)); return false; } _zbuf = new uint16[width * height]; } - _s->read(_zbufChunk, roundup(remain)); - if (complete) { size_t zbufOutSize; decompress_lzo1x(_zbufChunk, remain, (uint8*)_zbuf, &zbufOutSize); } else { decodeZBUF_partial(_zbufChunk, _zbuf, remain); } +#endif return true; } -bool VQADecoder::getZBUF(uint16 *zbuf) +#if 0 +bool VQADecoder::VQAVideoTrack::getZBUF(uint16 *zbuf) { if (!_zbuf) return false; @@ -708,50 +728,51 @@ bool VQADecoder::getZBUF(uint16 *zbuf) memcpy(zbuf, _zbuf, 2 * _header.width * _header.height); return true; } +#endif -bool VQADecoder::readVIEW(uint32 size) +bool VQADecoder::VQAVideoTrack::readVIEW(Common::SeekableReadStream *s, uint32 size) { if (size != 56) return false; - _s->skip(size); - // _hasView = true; - + s->skip(size); return true; } -bool VQADecoder::readAESC(uint32 size) +bool VQADecoder::VQAVideoTrack::readAESC(Common::SeekableReadStream *s, uint32 size) { - _s->skip(roundup(size)); + s->skip(roundup(size)); return true; } -bool VQADecoder::readLITE(uint32 size) +bool VQADecoder::VQAVideoTrack::readLITE(Common::SeekableReadStream *s, uint32 size) { - _s->skip(roundup(size)); + s->skip(roundup(size)); return true; } -bool VQADecoder::readVPTR(uint32 size) +bool VQADecoder::VQAVideoTrack::readVPTR(Common::SeekableReadStream *s, uint32 size) { - if (size > _header.maxVPTRSize) + if (size > _maxVPTRSize) return false; if (!_vptr) - _vptr = new uint8[roundup(_header.maxVPTRSize)]; + _vptr = new uint8[roundup(_maxVPTRSize)]; _vptrSize = size; - _s->read(_vptr, roundup(size)); + s->read(_vptr, roundup(size)); + + _hasNewFrame = true; return true; } -void VQADecoder::VPTRWriteBlock(uint16 *frame, unsigned int dstBlock, unsigned int srcBlock, int count, bool alpha) const +void VQADecoder::VQAVideoTrack::VPTRWriteBlock(uint16 *frame, unsigned int dstBlock, unsigned int srcBlock, int count, bool alpha) { - uint16 frame_width = _header.width; + uint16 frame_width = _width; uint32 frame_stride = 640; - uint16 block_width = _header.blockW; - uint16 block_height = _header.blockH; + uint16 block_width = _blockW; + uint16 block_height = _blockH; const uint8 *const block_src = &_codebook[2 * srcBlock * block_width * block_height]; @@ -760,8 +781,8 @@ void VQADecoder::VPTRWriteBlock(uint16 *frame, unsigned int dstBlock, unsigned i do { - uint32 frame_x = dstBlock % blocks_per_line * block_width + _header.offset_x / 2; - uint32 frame_y = dstBlock / blocks_per_line * block_height + _header.offset_y; + uint32 frame_x = dstBlock % blocks_per_line * block_width + _offsetX / 2; + uint32 frame_y = dstBlock / blocks_per_line * block_height + _offsetY; uint32 dst_offset = frame_x + frame_y * frame_stride; @@ -789,23 +810,11 @@ void VQADecoder::VPTRWriteBlock(uint16 *frame, unsigned int dstBlock, unsigned i while (--count); } -bool VQADecoder::seekToFrame(int frame) -{ - if (frame < 0 || frame >= _header.numFrames) - return false; - - _s->seek(2 * (_frameInfo[frame] & 0x0fffffff), SEEK_SET); - return true; -} - -bool VQADecoder::decodeFrame(uint16 *frame) +bool VQADecoder::VQAVideoTrack::decodeFrame(uint16 *frame) { if (!_codebook || !_vptr) return false; - if (!_frame) - _frame = new uint16[_header.width * _header.height]; - uint8 *src = _vptr; uint8 *end = _vptr + _vptrSize; @@ -828,20 +837,20 @@ bool VQADecoder::decodeFrame(uint16 *frame) count = 2 * (((command >> 8) & 0x1f) + 1); srcBlock = command & 0x00ff; - VPTRWriteBlock(_frame, dstBlock, srcBlock, count); + VPTRWriteBlock(frame, dstBlock, srcBlock, count); dstBlock += count; break; case 2: count = 2 * (((command >> 8) & 0x1f) + 1); srcBlock = command & 0x00ff; - VPTRWriteBlock(_frame, dstBlock, srcBlock, 1); + VPTRWriteBlock(frame, dstBlock, srcBlock, 1); ++dstBlock; for (int i = 0; i < count; ++i) { srcBlock = *src++; - VPTRWriteBlock(_frame, dstBlock, srcBlock, 1); + VPTRWriteBlock(frame, dstBlock, srcBlock, 1); ++dstBlock; } break; @@ -850,7 +859,7 @@ bool VQADecoder::decodeFrame(uint16 *frame) count = 1; srcBlock = command & 0x1fff; - VPTRWriteBlock(_frame, dstBlock, srcBlock, count, prefix == 4); + VPTRWriteBlock(frame, dstBlock, srcBlock, count, prefix == 4); ++dstBlock; break; case 5: @@ -858,7 +867,7 @@ bool VQADecoder::decodeFrame(uint16 *frame) count = *src++; srcBlock = command & 0x1fff; - VPTRWriteBlock(_frame, dstBlock, srcBlock, count, prefix == 6); + VPTRWriteBlock(frame, dstBlock, srcBlock, count, prefix == 6); dstBlock += count; break; default: @@ -866,14 +875,51 @@ bool VQADecoder::decodeFrame(uint16 *frame) } } - memcpy(frame, _frame, 2 * 640 * 480); + return true; +} + +VQADecoder::VQAAudioTrack::VQAAudioTrack(VQADecoder *vqaDecoder) { + _audioStream = Audio::makeQueuingAudioStream(vqaDecoder->_header.freq, false); +} + +VQADecoder::VQAAudioTrack::~VQAAudioTrack() { + delete _audioStream; +} + +Audio::AudioStream *VQADecoder::VQAAudioTrack::getAudioStream() const { + return _audioStream; +} + +bool VQADecoder::VQAAudioTrack::readSND2(Common::SeekableReadStream *s, uint32 size) +{ + if (size != 735) { + error("audio frame size: %d", size); + return false; + } + + s->read(_compressedAudioFrame, roundup(size)); + + int16 *audioFrame = (int16*)malloc(4 * size); + memset(audioFrame, 0, 4 * size); + + _adpcmDecoder.decode(_compressedAudioFrame, size, audioFrame); + + _audioStream->queueBuffer((byte*)audioFrame, 4 * size, DisposeAfterUse::YES, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN); return true; } -int16 *VQADecoder::getAudioFrame() +bool VQADecoder::VQAAudioTrack::readSN2J(Common::SeekableReadStream *s, uint32 size) { - return _audioFrame; + if (size != 6) + return false; + + uint16 stepIndex = s->readUint16LE(); + uint32 predictor = s->readUint32LE(); + + _adpcmDecoder.setParameters(stepIndex >> 5, predictor); + + return true; } }; // End of namespace BladeRunner diff --git a/engines/bladerunner/vqa_decoder.h b/engines/bladerunner/vqa_decoder.h index ab1f9efe4d..a2237cc1fa 100644 --- a/engines/bladerunner/vqa_decoder.h +++ b/engines/bladerunner/vqa_decoder.h @@ -23,15 +23,33 @@ #ifndef BLADERUNNER_VQA_DECODER_H #define BLADERUNNER_VQA_DECODER_H +#include "bladerunner/aud_decoder.h" + +#include "audio/audiostream.h" + #include "common/debug.h" #include "common/str.h" #include "common/stream.h" #include "common/types.h" +#include "graphics/surface.h" + +#include "video/video_decoder.h" + namespace BladeRunner { -class VQADecoder +class VQADecoder : public Video::VideoDecoder { +public: + VQADecoder(); + ~VQADecoder(); + + bool loadStream(Common::SeekableReadStream *s); + +protected: + void readNextPacket(); + +private: struct Header { uint16 version; // 0x00 @@ -45,8 +63,8 @@ class VQADecoder uint8 cbParts; // 0x0D uint16 colors; // 0x0E uint16 maxBlocks; // 0x10 - uint16 offset_x; // 0x12 - uint16 offset_y; // 0x14 + uint16 offsetX; // 0x12 + uint16 offsetY; // 0x14 uint16 maxVPTRSize; // 0x16 uint16 freq; // 0x18 uint8 channels; // 0x1A @@ -85,60 +103,109 @@ class VQADecoder uint16 clipCount; }; + class VQAVideoTrack; + class VQAAudioTrack; + Common::SeekableReadStream *_s; Header _header; LoopInfo _loopInfo; ClipInfo _clipInfo; - uint16 *_frame; - uint16 *_zbuf; - - size_t _codebookSize; - uint8 *_codebook; - uint8 *_cbfz; - - size_t _vptrSize; - uint8 *_vptr; - uint32 *_frameInfo; uint32 _maxVIEWChunkSize; uint32 _maxZBUFChunkSize; uint32 _maxAESCChunkSize; - uint8 *_zbufChunk; - bool _hasView; + VQAVideoTrack *_videoTrack; + VQAAudioTrack *_audioTrack; + + // bool _hasView; // view_t view; - // ima_adpcm_ws_decoder_t ima_adpcm_ws_decoder; - int16 *_audioFrame; - - bool readVQHD(uint32 size); - bool readMSCI(uint32 size); - bool readMFCI(uint32 size); - bool readLINF(uint32 size); - bool readCINF(uint32 size); - bool readFINF(uint32 size); - bool readLNIN(uint32 size); - bool readCLIP(uint32 size); - - bool readSN2J(uint32 size); - bool readSND2(uint32 size); - bool readVQFR(uint32 size); - bool readVPTR(uint32 size); - bool readVQFL(uint32 size); - bool readCBFZ(uint32 size); - bool readZBUF(uint32 size); - bool readVIEW(uint32 size); - bool readAESC(uint32 size); - bool readLITE(uint32 size); + bool readVQHD(Common::SeekableReadStream *s, uint32 size); + bool readMSCI(Common::SeekableReadStream *s, uint32 size); + bool readMFCI(Common::SeekableReadStream *s, uint32 size); + bool readLINF(Common::SeekableReadStream *s, uint32 size); + bool readCINF(Common::SeekableReadStream *s, uint32 size); + bool readFINF(Common::SeekableReadStream *s, uint32 size); + bool readLNIN(Common::SeekableReadStream *s, uint32 size); + bool readCLIP(Common::SeekableReadStream *s, uint32 size); + + class VQAVideoTrack : public FixedRateVideoTrack { + public: + VQAVideoTrack(VQADecoder *vqaDecoder); + ~VQAVideoTrack(); + + uint16 getWidth() const; + uint16 getHeight() const; + Graphics::PixelFormat getPixelFormat() const; + int getCurFrame() const; + int getFrameCount() const; + const Graphics::Surface *decodeNextFrame(); + + bool readVQFR(Common::SeekableReadStream *s, uint32 size); + bool readVPTR(Common::SeekableReadStream *s, uint32 size); + bool readVQFL(Common::SeekableReadStream *s, uint32 size); + bool readCBFZ(Common::SeekableReadStream *s, uint32 size); + bool readZBUF(Common::SeekableReadStream *s, uint32 size); + bool readVIEW(Common::SeekableReadStream *s, uint32 size); + bool readAESC(Common::SeekableReadStream *s, uint32 size); + bool readLITE(Common::SeekableReadStream *s, uint32 size); + + protected: + Common::Rational getFrameRate() const; + + private: + Graphics::Surface *_surface; + bool _hasNewFrame; + + uint16 _numFrames; + uint16 _width, _height; + uint8 _blockW, _blockH; + uint8 _frameRate; + uint16 _maxBlocks; + uint16 _offsetX, _offsetY; + + uint16 _maxVPTRSize; + uint32 _maxCBFZSize; + uint32 _maxZBUFChunkSize; + + size_t _codebookSize; + uint8 *_codebook; + uint8 *_cbfz; + uint8 *_zbufChunk; + + size_t _vptrSize; + uint8 *_vptr; + + int _curFrame; + + + void VPTRWriteBlock(uint16 *frame, unsigned int dstBlock, unsigned int srcBlock, int count, bool alpha = false); + bool decodeFrame(uint16 *frame); + }; -public: - VQADecoder(); - ~VQADecoder(); + class VQAAudioTrack : public AudioTrack { + public: + VQAAudioTrack(VQADecoder *vqaDecoder); + ~VQAAudioTrack(); + + bool readSND2(Common::SeekableReadStream *s, uint32 size); + bool readSN2J(Common::SeekableReadStream *s, uint32 size); + + protected: + Audio::AudioStream *getAudioStream() const; + + private: + Audio::QueuingAudioStream *_audioStream; + + ADPCMWestwoodDecoder _adpcmDecoder; + uint8 _compressedAudioFrame[735]; + }; - bool open(Common::SeekableReadStream *s); +/* bool readFrame(); int getFrameTime() { return 1000 / _header.frameRate; } @@ -154,6 +221,7 @@ public: bool getZBUF(uint16 *zbuf); friend class VQAPlayer; +*/ }; }; // End of namespace BladeRunner diff --git a/engines/bladerunner/vqa_player.cpp b/engines/bladerunner/vqa_player.cpp index 8b614813a7..f05e14ba44 100644 --- a/engines/bladerunner/vqa_player.cpp +++ b/engines/bladerunner/vqa_player.cpp @@ -24,6 +24,8 @@ #include "bladerunner/bladerunner.h" +#include "audio/decoders/raw.h" + #include "common/system.h" namespace BladeRunner { @@ -39,6 +41,8 @@ bool VQAPlayer::open(const Common::String &name) { return false; } + _audioStream = Audio::makeQueuingAudioStream(_decoder._header.freq, _decoder._header.channels == 2); + return true; } @@ -78,12 +82,31 @@ int VQAPlayer::update() { else ++_curFrame; - if (_curFrame >= _decoder._header.numFrames) + if (_curFrame >= _decoder._header.numFrames) { + if (_audioStream) + _audioStream->finish(); return -3; + } _decoder.readFrame(); _decoder.decodeFrame((uint16*)_surface->getPixels()); + if (_audioStream) { + int16 *audioFrame = (int16*)malloc(2940); + memcpy(audioFrame, _decoder.getAudioFrame(), 2940); + _audioStream->queueBuffer((byte*)audioFrame, 2940, DisposeAfterUse::YES, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN); + + if (!_audioStarted) + _vm->_mixer->playStream( + Audio::Mixer::kPlainSoundType, + &_soundHandle, + _audioStream, + -1 + ); + + _audioStarted = true; + } + return _curFrame; } diff --git a/engines/bladerunner/vqa_player.h b/engines/bladerunner/vqa_player.h index 2d7d6822b3..7e7fab5b5f 100644 --- a/engines/bladerunner/vqa_player.h +++ b/engines/bladerunner/vqa_player.h @@ -26,6 +26,7 @@ #include "bladerunner/vqa_decoder.h" #include "audio/audiostream.h" +#include "audio/mixer.h" #include "graphics/surface.h" @@ -46,6 +47,8 @@ class VQAPlayer { int _loopDefault; uint32 _nextFrameTime; + bool _audioStarted; + Audio::SoundHandle _soundHandle; public: @@ -58,7 +61,8 @@ public: _curLoop(-1), _loopSpecial(-1), _loopDefault(-1), - _nextFrameTime(0) + _nextFrameTime(0), + _audioStarted(false) {} bool open(const Common::String &name); -- cgit v1.2.3