aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/bladerunner/bladerunner.cpp10
-rw-r--r--engines/bladerunner/bladerunner.h2
-rw-r--r--engines/bladerunner/module.mk1
-rw-r--r--engines/bladerunner/overlays.cpp146
-rw-r--r--engines/bladerunner/overlays.h74
-rw-r--r--engines/bladerunner/script/scene/ct01.cpp1
-rw-r--r--engines/bladerunner/script/script.cpp7
-rw-r--r--engines/bladerunner/vqa_decoder.cpp115
-rw-r--r--engines/bladerunner/vqa_decoder.h41
-rw-r--r--engines/bladerunner/vqa_player.cpp75
-rw-r--r--engines/bladerunner/vqa_player.h3
11 files changed, 374 insertions, 101 deletions
diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp
index d138360fab..121ad8128e 100644
--- a/engines/bladerunner/bladerunner.cpp
+++ b/engines/bladerunner/bladerunner.cpp
@@ -44,6 +44,7 @@
#include "bladerunner/mouse.h"
#include "bladerunner/outtake.h"
#include "bladerunner/obstacles.h"
+#include "bladerunner/overlays.h"
#include "bladerunner/regions.h"
#include "bladerunner/scene.h"
#include "bladerunner/scene_objects.h"
@@ -239,7 +240,8 @@ bool BladeRunnerEngine::startup(bool hasSavegames) {
if (!openArchive("SPCHSFX.TLK"))
return false;
- // TODO: Video overlays
+ _overlays = new Overlays(this);
+ _overlays->init();
_zbuffer = new ZBuffer();
_zbuffer->init(640, 480);
@@ -437,7 +439,8 @@ void BladeRunnerEngine::shutdown() {
delete _ambientSounds;
- // TODO: Delete overlays
+ delete _overlays;
+ _overlays = nullptr;
delete _audioSpeech;
@@ -646,10 +649,11 @@ void BladeRunnerEngine::gameTick() {
}
(void)backgroundChanged;
blit(_surfaceInterface, _surfaceGame);
+
// TODO: remove zbuffer draw
// _surfaceGame.copyRectToSurface(_zbuffer->getData(), 1280, 0, 0, 640, 480);
- // TODO: Render overlays
+ _overlays->tick();
if (!inDialogueMenu) {
actorsUpdate();
diff --git a/engines/bladerunner/bladerunner.h b/engines/bladerunner/bladerunner.h
index 7118dcce83..066937ca38 100644
--- a/engines/bladerunner/bladerunner.h
+++ b/engines/bladerunner/bladerunner.h
@@ -69,6 +69,7 @@ class Items;
class Lights;
class Mouse;
class Obstacles;
+class Overlays;
class Scene;
class SceneObjects;
class SceneScript;
@@ -112,6 +113,7 @@ public:
Font *_mainFont;
Mouse *_mouse;
Obstacles *_obstacles;
+ Overlays *_overlays;
Scene *_scene;
SceneObjects *_sceneObjects;
SceneScript *_sceneScript;
diff --git a/engines/bladerunner/module.mk b/engines/bladerunner/module.mk
index eeaff45794..711ddd3346 100644
--- a/engines/bladerunner/module.mk
+++ b/engines/bladerunner/module.mk
@@ -39,6 +39,7 @@ MODULE_OBJS = \
movement_track.o \
obstacles.o \
outtake.o \
+ overlays.o \
regions.o \
scene.o \
scene_objects.o \
diff --git a/engines/bladerunner/overlays.cpp b/engines/bladerunner/overlays.cpp
new file mode 100644
index 0000000000..946cbe266d
--- /dev/null
+++ b/engines/bladerunner/overlays.cpp
@@ -0,0 +1,146 @@
+/* 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/overlays.h"
+
+#include "bladerunner/bladerunner.h"
+
+#include "bladerunner/archive.h"
+#include "bladerunner/vqa_player.h"
+
+#include "graphics/surface.h"
+
+namespace BladeRunner {
+
+Overlays::Overlays(BladeRunnerEngine *vm)
+ : _vm(vm)
+{
+}
+
+bool Overlays::init() {
+ reset();
+ _videos.resize(kOverlayVideos);
+
+ for (int i = 0; i < kOverlayVideos; ++i) {
+ _videos[i].vqaPlayer = nullptr;
+ resetSingle(i);
+ }
+
+ return true;
+}
+
+Overlays::~Overlays() {
+ for (int i = 0; i < kOverlayVideos; ++i) {
+ resetSingle(i);
+ }
+ _videos.clear();
+ reset();
+}
+
+int Overlays::play(const Common::String &name, int loopId, int loopForever, int a5, int a6) {
+ int id = mix_id(name);
+ int index = findById(id);
+ if (index < 0) {
+ index = findEmpty();
+ if (index < 0) {
+ return index;
+ }
+ _videos[index].id = id;
+ _videos[index].vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceGame);
+
+ // repeat forever
+ _videos[index].vqaPlayer->setBeginAndEndFrame(0, 0, -1, kLoopSetModeJustStart, nullptr, nullptr);
+ _videos[index].loaded = true;
+ }
+
+ Common::String resourceName = Common::String::format("%s.VQA", name.c_str());
+ _videos[index].vqaPlayer->open(resourceName);
+ _videos[index].vqaPlayer->setLoop(
+ loopId,
+ loopForever ? -1 : 0,
+ a5 ? kLoopSetModeImmediate : kLoopSetModeEnqueue,
+ nullptr, nullptr);
+
+ return index;
+}
+
+void Overlays::remove(const Common::String &name) {
+ int id = mix_id(name);
+ int index = findById(id);
+ if (index >= 0) {
+ resetSingle(index);
+ }
+}
+
+void Overlays::tick() {
+ for (int i = 0; i < kOverlayVideos; ++i) {
+ if (_videos[i].loaded) {
+ int frame = _videos[i].vqaPlayer->update(true);
+ if (frame < 0) {
+ resetSingle(i);
+ }
+ }
+ }
+}
+
+int Overlays::findById(int id) const {
+ for (int i = 0; i < kOverlayVideos; ++i) {
+ if (_videos[i].loaded && _videos[i].id == id) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int Overlays::findEmpty() const {
+ for (int i = 0; i < kOverlayVideos; ++i) {
+ if (!_videos[i].loaded) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void Overlays::resetSingle(int i) {
+ assert(i >= 0 && i < (int)_videos.size());
+ if (_videos[i].vqaPlayer) {
+ delete _videos[i].vqaPlayer;
+ _videos[i].vqaPlayer = nullptr;
+ }
+ _videos[i].loaded = false;
+ _videos[i].id = 0;
+ _videos[i].field2 = -1;
+}
+
+void Overlays::resetAll() {
+ for (int i = 0; i < kOverlayVideos; ++i) {
+ if (_videos[i].loaded) {
+ resetSingle(i);
+ }
+ }
+}
+
+void Overlays::reset() {
+ _videos.clear();
+}
+
+} // End of namespace BladeRunner
diff --git a/engines/bladerunner/overlays.h b/engines/bladerunner/overlays.h
new file mode 100644
index 0000000000..edeb0d120c
--- /dev/null
+++ b/engines/bladerunner/overlays.h
@@ -0,0 +1,74 @@
+/* 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_OVERLAYS_H
+#define BLADERUNNER_OVERLAYS_H
+
+#include "common/array.h"
+#include "common/str.h"
+
+namespace Graphics {
+ struct Surface;
+};
+
+namespace BladeRunner {
+
+class BladeRunnerEngine;
+class VQAPlayer;
+
+struct OverlayVideo {
+ bool loaded;
+ VQAPlayer *vqaPlayer;
+ // char name[13];
+ int32 id;
+ int field0;
+ int field1;
+ int field2;
+};
+
+class Overlays {
+ static const int kOverlayVideos = 5;
+
+ BladeRunnerEngine *_vm;
+ Common::Array<OverlayVideo> _videos;
+
+public:
+ Overlays(BladeRunnerEngine *vm);
+ bool init();
+ ~Overlays();
+
+ int play(const Common::String &name, int a3, int a4, int a5, int a6);
+ void remove(const Common::String &name);
+ void tick();
+
+private:
+ int findById(int32 id) const;
+ int findEmpty() const;
+
+ void resetSingle(int i);
+ void resetAll();
+ void reset();
+};
+
+} // End of namespace BladeRunner
+
+#endif
diff --git a/engines/bladerunner/script/scene/ct01.cpp b/engines/bladerunner/script/scene/ct01.cpp
index 5a2c62cfbb..21e6fc7600 100644
--- a/engines/bladerunner/script/scene/ct01.cpp
+++ b/engines/bladerunner/script/scene/ct01.cpp
@@ -366,7 +366,6 @@ void SceneScriptCT01::SceneFrameAdvanced(int frame) {
} else {
Ambient_Sounds_Play_Sound(66, Random_Query(33, 50), 0, 0, 0);
}
-
}
}
}
diff --git a/engines/bladerunner/script/script.cpp b/engines/bladerunner/script/script.cpp
index 72f4e50a53..25d78e4991 100644
--- a/engines/bladerunner/script/script.cpp
+++ b/engines/bladerunner/script/script.cpp
@@ -38,6 +38,7 @@
#include "bladerunner/items.h"
#include "bladerunner/item_pickup.h"
#include "bladerunner/movement_track.h"
+#include "bladerunner/overlays.h"
#include "bladerunner/regions.h"
#include "bladerunner/set.h"
#include "bladerunner/settings.h"
@@ -818,13 +819,11 @@ bool ScriptBase::Music_Is_Playing() {
}
void ScriptBase::Overlay_Play(const char *overlay, int a2, int a3, int a4, int a5) {
- //TODO
- warning("Overlay_Play(%s, %d, %d, %d, %d)", overlay, a2, a3, a4, a5);
+ _vm->_overlays->play(overlay, a2, a3, a4, a5);
}
void ScriptBase::Overlay_Remove(const char *overlay) {
- //TODO
- warning("Overlay_Remove(%s)", overlay);
+ _vm->_overlays->remove(overlay);
}
void ScriptBase::Scene_Loop_Set_Default(int loopId) {
diff --git a/engines/bladerunner/vqa_decoder.cpp b/engines/bladerunner/vqa_decoder.cpp
index 06cba12592..275ec18a4d 100644
--- a/engines/bladerunner/vqa_decoder.cpp
+++ b/engines/bladerunner/vqa_decoder.cpp
@@ -128,6 +128,9 @@ VQADecoder::VQADecoder(Graphics::Surface *surface) :
}
VQADecoder::~VQADecoder() {
+ for (uint i = 0; i < _codebooks.size(); ++i) {
+ delete[] _codebooks[i].data;
+ }
delete _audioTrack;
delete _videoTrack;
delete[] _frameInfo;
@@ -191,8 +194,9 @@ bool VQADecoder::loadStream(Common::SeekableReadStream *s) {
return true;
}
-void VQADecoder::decodeVideoFrame() {
- _videoTrack->decodeVideoFrame();
+void VQADecoder::decodeVideoFrame(int frame, bool forceDraw) {
+ _decodingFrame = frame;
+ _videoTrack->decodeVideoFrame(forceDraw);
}
void VQADecoder::decodeZBuffer(ZBuffer *zbuffer) {
@@ -215,7 +219,7 @@ void VQADecoder::decodeLights(Lights *lights) {
_videoTrack->decodeLights(lights);
}
-void VQADecoder::readPacket(int skipFlags) {
+void VQADecoder::readPacket(uint readFlags) {
IFFChunkHeader chd;
if (remain(_s) < 8) {
@@ -232,15 +236,15 @@ void VQADecoder::readPacket(int skipFlags) {
bool rc = false;
// Video track
switch (chd.id) {
- case kAESC: rc = skipFlags & 1 ? _s->skip(roundup(chd.size)) : _videoTrack->readAESC(_s, chd.size); break;
- case kLITE: rc = skipFlags & 1 ? _s->skip(roundup(chd.size)) : _videoTrack->readLITE(_s, chd.size); break;
- case kVIEW: rc = skipFlags & 1 ? _s->skip(roundup(chd.size)) : _videoTrack->readVIEW(_s, chd.size); break;
- case kVQFL: rc = skipFlags & 1 ? _s->skip(roundup(chd.size)) : _videoTrack->readVQFL(_s, chd.size); break;
- case kVQFR: rc = skipFlags & 1 ? _s->skip(roundup(chd.size)) : _videoTrack->readVQFR(_s, chd.size); break;
- case kZBUF: rc = skipFlags & 1 ? _s->skip(roundup(chd.size)) : _videoTrack->readZBUF(_s, chd.size); break;
+ case kAESC: rc = ((readFlags & kVQAReadCustom) == 0) ? _s->skip(roundup(chd.size)) : _videoTrack->readAESC(_s, chd.size); break;
+ case kLITE: rc = ((readFlags & kVQAReadCustom) == 0) ? _s->skip(roundup(chd.size)) : _videoTrack->readLITE(_s, chd.size); break;
+ case kVIEW: rc = ((readFlags & kVQAReadCustom) == 0) ? _s->skip(roundup(chd.size)) : _videoTrack->readVIEW(_s, chd.size); break;
+ case kVQFL: rc = ((readFlags & kVQAReadVideo ) == 0) ? _s->skip(roundup(chd.size)) : _videoTrack->readVQFL(_s, chd.size, readFlags); break;
+ case kVQFR: rc = ((readFlags & kVQAReadVideo ) == 0) ? _s->skip(roundup(chd.size)) : _videoTrack->readVQFR(_s, chd.size, readFlags); break;
+ case kZBUF: rc = ((readFlags & kVQAReadCustom) == 0) ? _s->skip(roundup(chd.size)) : _videoTrack->readZBUF(_s, chd.size); break;
// Sound track
- case kSN2J: rc = skipFlags & 2 ? _s->skip(roundup(chd.size)) : _audioTrack->readSN2J(_s, chd.size); break;
- case kSND2: rc = skipFlags & 2 ? _s->skip(roundup(chd.size)) : _audioTrack->readSND2(_s, chd.size); break;
+ case kSN2J: rc = ((readFlags & kVQAReadAudio) == 0) ? _s->skip(roundup(chd.size)) : _audioTrack->readSN2J(_s, chd.size); break;
+ case kSND2: rc = ((readFlags & kVQAReadAudio) == 0) ? _s->skip(roundup(chd.size)) : _audioTrack->readSND2(_s, chd.size); break;
default:
rc = false;
_s->skip(roundup(chd.size));
@@ -253,14 +257,16 @@ void VQADecoder::readPacket(int skipFlags) {
} while (chd.id != kVQFR);
}
-void VQADecoder::readFrame(int frame, int skipFlags) {
+void VQADecoder::readFrame(int frame, uint readFlags) {
if (frame < 0 || frame >= numFrames()) {
error("frame %d out of bounds, frame count is %d", frame, numFrames());
}
uint32 frameOffset = 2 * (_frameInfo[frame] & 0x0FFFFFFF);
_s->seek(frameOffset);
- readPacket(skipFlags);
+
+ _readingFrame = frame;
+ readPacket(readFlags);
}
bool VQADecoder::readVQHD(Common::SeekableReadStream *s, uint32 size) {
@@ -289,10 +295,6 @@ bool VQADecoder::readVQHD(Common::SeekableReadStream *s, uint32 size) {
_header.maxCBFZSize = s->readUint32LE();
_header.unk5 = s->readUint32LE();
- if (_header.offsetX || _header.offsetY) {
- 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);
@@ -330,7 +332,7 @@ bool VQADecoder::readVQHD(Common::SeekableReadStream *s, uint32 size) {
return true;
}
-bool VQADecoder::VQAVideoTrack::readVQFR(Common::SeekableReadStream *s, uint32 size) {
+bool VQADecoder::VQAVideoTrack::readVQFR(Common::SeekableReadStream *s, uint32 size, uint readFlags) {
IFFChunkHeader chd;
while (size >= 8) {
@@ -340,8 +342,8 @@ bool VQADecoder::VQAVideoTrack::readVQFR(Common::SeekableReadStream *s, uint32 s
bool rc = false;
switch (chd.id) {
- case kCBFZ: rc = readCBFZ(s, chd.size); break;
- case kVPTR: rc = readVPTR(s, chd.size); break;
+ case kCBFZ: rc = ((readFlags & kVQAReadCodebook ) == 0) ? s->skip(roundup(chd.size)) : readCBFZ(s, chd.size); break;
+ case kVPTR: rc = ((readFlags & kVQAReadVectorPointerTable) == 0) ? s->skip(roundup(chd.size)) : readVPTR(s, chd.size); break;
default:
s->skip(roundup(chd.size));
}
@@ -426,6 +428,22 @@ bool VQADecoder::readLINF(Common::SeekableReadStream *s, uint32 size) {
return true;
}
+VQADecoder::CodebookInfo &VQADecoder::codebookInfoForFrame(int frame) {
+ assert(frame < numFrames());
+ assert(!_codebooks.empty());
+
+ CodebookInfo *ci = nullptr;
+ uint count = _codebooks.size();
+ for (uint i = 0; i != count; ++i) {
+ if (frame >= _codebooks[count - i - 1].frame) {
+ return _codebooks[count - i - 1];
+ }
+ }
+
+ assert(ci && "No codebook found");
+ return _codebooks[0];
+}
+
bool VQADecoder::readCINF(Common::SeekableReadStream *s, uint32 size) {
IFFChunkHeader chd;
@@ -433,17 +451,23 @@ bool VQADecoder::readCINF(Common::SeekableReadStream *s, uint32 size) {
if (chd.id != kCINH || chd.size != 8u)
return false;
- _clipInfo.clipCount = s->readUint16LE();
+ uint16 codebookCount = s->readUint16LE();
+ _codebooks.resize(codebookCount);
+
s->skip(6);
readIFFChunkHeader(_s, &chd);
- if (chd.id != kCIND || chd.size != 6u * _clipInfo.clipCount)
+ if (chd.id != kCIND || chd.size != 6u * codebookCount)
return false;
- for (int i = 0; i != _clipInfo.clipCount; ++i) {
- uint16 a = s->readUint16LE();
- uint32 b = s->readUint32LE();
- debug("VQADecoder::readCINF() i: %d a: 0x%04x b: 0x%08x", i, a, b);
+ for (int i = 0; i != codebookCount; ++i) {
+ _codebooks[i].frame = s->readUint16LE();
+ _codebooks[i].size = s->readUint32LE();
+ _codebooks[i].data = nullptr;
+
+ // debug("Codebook %2d: %4d %8d", i, _codebooks[i].frame, _codebooks[i].size);
+
+ assert(_codebooks[i].frame < numFrames());
}
return true;
@@ -543,11 +567,11 @@ bool VQADecoder::readMFCI(Common::SeekableReadStream *s, uint32 size) {
}
VQADecoder::VQAVideoTrack::VQAVideoTrack(VQADecoder *vqaDecoder, Graphics::Surface *surface) {
- VQADecoder::Header *header = &vqaDecoder->_header;
-
+ _vqaDecoder = vqaDecoder;
_surface = surface;
_hasNewFrame = false;
+ VQADecoder::Header *header = &vqaDecoder->_header;
_numFrames = header->numFrames;
_width = header->width;
_height = header->height;
@@ -562,7 +586,6 @@ VQADecoder::VQAVideoTrack::VQAVideoTrack(VQADecoder *vqaDecoder, Graphics::Surfa
_maxCBFZSize = header->maxCBFZSize;
_maxZBUFChunkSize = vqaDecoder->_maxZBUFChunkSize;
- _codebookSize = 0;
_codebook = nullptr;
_cbfz = nullptr;
_zbufChunk = nullptr;
@@ -580,7 +603,6 @@ VQADecoder::VQAVideoTrack::VQAVideoTrack(VQADecoder *vqaDecoder, Graphics::Surfa
}
VQADecoder::VQAVideoTrack::~VQAVideoTrack() {
- delete[] _codebook;
delete[] _cbfz;
delete[] _zbufChunk;
delete[] _vpointer;
@@ -598,10 +620,6 @@ uint16 VQADecoder::VQAVideoTrack::getHeight() const {
return _height;
}
-int VQADecoder::VQAVideoTrack::getCurFrame() const {
- return _curFrame;
-}
-
int VQADecoder::VQAVideoTrack::getFrameCount() const {
return _numFrames;
}
@@ -610,15 +628,14 @@ Common::Rational VQADecoder::VQAVideoTrack::getFrameRate() const {
return _frameRate;
}
-void VQADecoder::VQAVideoTrack::decodeVideoFrame() {
- if (_hasNewFrame) {
+void VQADecoder::VQAVideoTrack::decodeVideoFrame(bool forceDraw) {
+ if (_hasNewFrame || forceDraw) {
decodeFrame((uint16*)_surface->getPixels());
- _curFrame++;
_hasNewFrame = false;
}
}
-bool VQADecoder::VQAVideoTrack::readVQFL(Common::SeekableReadStream *s, uint32 size) {
+bool VQADecoder::VQAVideoTrack::readVQFL(Common::SeekableReadStream *s, uint32 size, uint readFlags) {
IFFChunkHeader chd;
while (size >= 8) {
@@ -648,17 +665,22 @@ bool VQADecoder::VQAVideoTrack::readCBFZ(Common::SeekableReadStream *s, uint32 s
return false;
}
- if (!_codebook) {
- _codebookSize = 2 * _maxBlocks * _blockW * _blockH;
- _codebook = new uint8[_codebookSize];
+ CodebookInfo &codebookInfo = _vqaDecoder->codebookInfoForFrame(_vqaDecoder->_readingFrame);
+ if (codebookInfo.data) {
+ s->skip(roundup(size));
+ return true;
}
+
+ uint32 codebookSize = 2 * _maxBlocks * _blockW * _blockH;
+ codebookInfo.data = new uint8[codebookSize];
+
if (!_cbfz) {
_cbfz = new uint8[roundup(_maxCBFZSize)];
}
s->read(_cbfz, roundup(size));
- decompress_lcw(_cbfz, size, _codebook, _codebookSize);
+ decompress_lcw(_cbfz, size, codebookInfo.data, codebookSize);
return true;
}
@@ -790,7 +812,7 @@ void VQADecoder::VQAVideoTrack::VPTRWriteBlock(uint16 *frame, unsigned int dstBl
int blocks_per_line = frame_width / block_width;
do {
- uint32 frame_x = dstBlock % blocks_per_line * block_width + _offsetX / 2;
+ uint32 frame_x = dstBlock % blocks_per_line * block_width + _offsetX;
uint32 frame_y = dstBlock / blocks_per_line * block_height + _offsetY;
uint32 dst_offset = frame_x + frame_y * frame_stride;
@@ -817,6 +839,13 @@ void VQADecoder::VQAVideoTrack::VPTRWriteBlock(uint16 *frame, unsigned int dstBl
}
bool VQADecoder::VQAVideoTrack::decodeFrame(uint16 *frame) {
+ VQADecoder::CodebookInfo &codebookInfo = _vqaDecoder->codebookInfoForFrame(_vqaDecoder->_decodingFrame);
+
+ if (!codebookInfo.data) {
+ _vqaDecoder->readFrame(codebookInfo.frame, kVQAReadCodebook);
+ }
+
+ _codebook = codebookInfo.data;
if (!_codebook || !_vpointer)
return false;
diff --git a/engines/bladerunner/vqa_decoder.h b/engines/bladerunner/vqa_decoder.h
index 8ef2ddd9c4..c76a8b336a 100644
--- a/engines/bladerunner/vqa_decoder.h
+++ b/engines/bladerunner/vqa_decoder.h
@@ -43,6 +43,15 @@ class Lights;
class View;
class ZBuffer;
+enum VQADecoderSkipFlags {
+ kVQAReadCodebook = 1,
+ kVQAReadVectorPointerTable = 2,
+ kVQAReadCustom = 4,
+ kVQAReadVideo = kVQAReadCodebook|kVQAReadVectorPointerTable|kVQAReadCustom,
+ kVQAReadAudio = 8,
+ kVQAReadAll = kVQAReadVideo|kVQAReadAudio
+};
+
class VQADecoder {
public:
VQADecoder(Graphics::Surface *surface);
@@ -50,9 +59,9 @@ public:
bool loadStream(Common::SeekableReadStream *s);
- void readFrame(int frame, int skipFlags);
+ void readFrame(int frame, uint readFlags = kVQAReadAll);
- void decodeVideoFrame();
+ void decodeVideoFrame(int frame, bool forceDraw = false);
void decodeZBuffer(ZBuffer *zbuffer);
Audio::SeekableAudioStream *decodeAudioFrame();
void decodeView(View *view);
@@ -120,8 +129,10 @@ private:
}
};
- struct ClipInfo {
- uint16 clipCount;
+ struct CodebookInfo {
+ uint16 frame;
+ uint32 size;
+ uint8 *data;
};
class VQAVideoTrack;
@@ -131,8 +142,11 @@ private:
Graphics::Surface *_surface;
Header _header;
+ int _readingFrame;
+ int _decodingFrame;
LoopInfo _loopInfo;
- ClipInfo _clipInfo;
+
+ Common::Array<CodebookInfo> _codebooks;
uint32 *_frameInfo;
@@ -143,7 +157,7 @@ private:
VQAVideoTrack *_videoTrack;
VQAAudioTrack *_audioTrack;
- void readPacket(int skipFlags);
+ void readPacket(uint readFlags);
bool readVQHD(Common::SeekableReadStream *s, uint32 size);
bool readMSCI(Common::SeekableReadStream *s, uint32 size);
@@ -154,6 +168,8 @@ private:
bool readLNIN(Common::SeekableReadStream *s, uint32 size);
bool readCLIP(Common::SeekableReadStream *s, uint32 size);
+ VQADecoder::CodebookInfo &VQADecoder::codebookInfoForFrame(int frame);
+
class VQAVideoTrack {
public:
VQAVideoTrack(VQADecoder *vqaDecoder, Graphics::Surface *surface);
@@ -162,18 +178,17 @@ private:
uint16 getWidth() const;
uint16 getHeight() const;
- int getCurFrame() const;
int getFrameCount() const;
- void decodeVideoFrame();
+ void decodeVideoFrame(bool forceDraw);
void decodeZBuffer(ZBuffer *zbuffer);
void decodeView(View *view);
void decodeAESC(AESC *aesc);
void decodeLights(Lights *lights);
- bool readVQFR(Common::SeekableReadStream *s, uint32 size);
+ bool readVQFR(Common::SeekableReadStream *s, uint32 size, uint readFlags);
bool readVPTR(Common::SeekableReadStream *s, uint32 size);
- bool readVQFL(Common::SeekableReadStream *s, uint32 size);
+ bool readVQFL(Common::SeekableReadStream *s, uint32 size, uint readFlags);
bool readCBFZ(Common::SeekableReadStream *s, uint32 size);
bool readZBUF(Common::SeekableReadStream *s, uint32 size);
bool readVIEW(Common::SeekableReadStream *s, uint32 size);
@@ -186,6 +201,7 @@ private:
bool useAudioSync() const { return false; }
private:
+ VQADecoder *_vqaDecoder;
Graphics::Surface *_surface;
bool _hasNewFrame;
@@ -200,7 +216,6 @@ private:
uint32 _maxCBFZSize;
uint32 _maxZBUFChunkSize;
- uint32 _codebookSize;
uint8 *_codebook;
uint8 *_cbfz;
bool _zbufChunkComplete;
@@ -215,9 +230,9 @@ private:
uint8 *_viewData;
uint32 _viewDataSize;
uint8 *_lightsData;
- uint32 _lightsDataSize;
+ uint32 _lightsDataSize;
uint8 *_aescData;
- uint32 _aescDataSize;
+ uint32 _aescDataSize;
void VPTRWriteBlock(uint16 *frame, unsigned int dstBlock, unsigned int srcBlock, int count, bool alpha = false);
bool decodeFrame(uint16 *frame);
diff --git a/engines/bladerunner/vqa_player.cpp b/engines/bladerunner/vqa_player.cpp
index a340f798d1..51766d6e28 100644
--- a/engines/bladerunner/vqa_player.cpp
+++ b/engines/bladerunner/vqa_player.cpp
@@ -49,6 +49,7 @@ bool VQAPlayer::open(const Common::String &name) {
_repeatsCount = 0;
_loop = -1;
+ _frame = -1;
_frameBegin = -1;
_frameEnd = _decoder.numFrames() - 1;
_frameEndQueued = -1;
@@ -70,8 +71,9 @@ void VQAPlayer::close() {
_s = nullptr;
}
-int VQAPlayer::update() {
+int VQAPlayer::update(bool forceDraw) {
uint32 now = 60 * _vm->_system->getMillis();
+ int result = -1;
if (_frameNext < 0) {
_frameNext = _frameBegin;
@@ -101,47 +103,48 @@ int VQAPlayer::update() {
}
}
- return -1;
- }
-
- if (_frameNext > _frameEnd) {
- return -3;
- }
-
- if (now < _frameNextTime) {
- return -1;
- }
-
- int frame = _frameNext;
- _decoder.readFrame(_frameNext, 0x2);
- _decoder.decodeVideoFrame();
-
- int audioPreloadFrames = 14;
-
- if (_hasAudio) {
- if (!_audioStarted) {
- for (int i = 0; i < audioPreloadFrames; i++) {
- if (_frameNext + i < _frameEnd) {
- _decoder.readFrame(_frameNext + i, 0x1);
- queueAudioFrame(_decoder.decodeAudioFrame());
+ result = -1;
+ } else if (_frameNext > _frameEnd) {
+ result = -3;
+ } else if (now < _frameNextTime) {
+ result = -1;
+ } else {
+ _frame = _frameNext;
+ _decoder.readFrame(_frameNext, kVQAReadVideo);
+ _decoder.decodeVideoFrame(_frameNext);
+
+ int audioPreloadFrames = 14;
+
+ if (_hasAudio) {
+ if (!_audioStarted) {
+ for (int i = 0; i < audioPreloadFrames; i++) {
+ if (_frameNext + i < _frameEnd) {
+ _decoder.readFrame(_frameNext + i, kVQAReadAudio);
+ queueAudioFrame(_decoder.decodeAudioFrame());
+ }
}
+ _vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, _audioStream);
+ _audioStarted = true;
}
- _vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, _audioStream);
- _audioStarted = true;
+ if (_frameNext + audioPreloadFrames < _frameEnd) {
+ _decoder.readFrame(_frameNext + audioPreloadFrames, kVQAReadAudio);
+ queueAudioFrame(_decoder.decodeAudioFrame());
+ }
+ }
+ if (_frameNextTime == 0) {
+ _frameNextTime = now + 60000 / 15;
}
- if (_frameNext + audioPreloadFrames < _frameEnd) {
- _decoder.readFrame(_frameNext + audioPreloadFrames, 0x1);
- queueAudioFrame(_decoder.decodeAudioFrame());
+ else {
+ _frameNextTime += 60000 / 15;
}
+ _frameNext++;
+ result = _frame;
}
- if (_frameNextTime == 0) {
- _frameNextTime = now + 60000 / 15;
- } else {
- _frameNextTime += 60000 / 15;
+ if (result < 0 && forceDraw && _frame != -1) {
+ _decoder.decodeVideoFrame(_frame, true);
+ result = _frame;
}
-
- _frameNext++;
- return frame;
+ return result;
}
void VQAPlayer::updateZBuffer(ZBuffer *zbuffer) {
diff --git a/engines/bladerunner/vqa_player.h b/engines/bladerunner/vqa_player.h
index dde5201932..1be47bd87e 100644
--- a/engines/bladerunner/vqa_player.h
+++ b/engines/bladerunner/vqa_player.h
@@ -52,6 +52,7 @@ class VQAPlayer {
const uint16 *_zBuffer;
Audio::QueuingAudioStream *_audioStream;
+ int _frame;
int _frameNext;
int _frameBegin;
int _frameEnd;
@@ -102,7 +103,7 @@ public:
bool open(const Common::String &name);
void close();
- int update();
+ int update(bool forceDraw = false);
void updateZBuffer(ZBuffer *zbuffer);
void updateView(View *view);
void updateAESC(AESC *aesc);