aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Fach-Pedersen2014-05-16 21:58:49 +0200
committerEugene Sandulenko2016-09-29 22:21:00 +0200
commit2e3fd7cf050e24b9f6363b1a38c6ff45847c6d6f (patch)
tree225c2535e8f6ac3dd21ccc082c9d7e0d9573380f
parentc48dfb70718f971a76049061a1c2f8bc2f37bb46 (diff)
downloadscummvm-rg350-2e3fd7cf050e24b9f6363b1a38c6ff45847c6d6f.tar.gz
scummvm-rg350-2e3fd7cf050e24b9f6363b1a38c6ff45847c6d6f.tar.bz2
scummvm-rg350-2e3fd7cf050e24b9f6363b1a38c6ff45847c6d6f.zip
BLADERUNNER: Start port to scummvm engine
Just shows the splash for now. VQA decoder partially ported but without audio and some minor special features.
-rw-r--r--engines/bladerunner/archive.cpp158
-rw-r--r--engines/bladerunner/archive.h65
-rw-r--r--engines/bladerunner/bladerunner.cpp324
-rw-r--r--engines/bladerunner/bladerunner.h91
-rw-r--r--engines/bladerunner/chapters.cpp59
-rw-r--r--engines/bladerunner/chapters.h62
-rw-r--r--engines/bladerunner/configure.engine3
-rw-r--r--engines/bladerunner/decompress_lcw.cpp162
-rw-r--r--engines/bladerunner/decompress_lcw.h35
-rw-r--r--engines/bladerunner/decompress_lzo.cpp143
-rw-r--r--engines/bladerunner/decompress_lzo.h34
-rw-r--r--engines/bladerunner/detection.cpp66
-rw-r--r--engines/bladerunner/detection_tables.h47
-rw-r--r--engines/bladerunner/gameinfo.cpp112
-rw-r--r--engines/bladerunner/gameinfo.h82
-rw-r--r--engines/bladerunner/image.cpp78
-rw-r--r--engines/bladerunner/image.h47
-rw-r--r--engines/bladerunner/module.mk21
-rw-r--r--engines/bladerunner/settings.cpp99
-rw-r--r--engines/bladerunner/settings.h94
-rw-r--r--engines/bladerunner/vqa_decoder.cpp933
-rw-r--r--engines/bladerunner/vqa_decoder.h165
22 files changed, 2880 insertions, 0 deletions
diff --git a/engines/bladerunner/archive.cpp b/engines/bladerunner/archive.cpp
new file mode 100644
index 0000000000..93c122f9af
--- /dev/null
+++ b/engines/bladerunner/archive.cpp
@@ -0,0 +1,158 @@
+/* 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/archive.h"
+
+#include "common/debug.h"
+
+namespace BladeRunner {
+
+MIXArchive::MIXArchive() {
+}
+
+MIXArchive::~MIXArchive() {
+ if (_fd.isOpen())
+ debug("~MIXArchive: fd not closed: %s", _fd.getName());
+}
+
+bool MIXArchive::open(const Common::String &filename) {
+ if (!_fd.open(filename)) {
+ debug("MIXArchive::open(): Could not open %s", filename.c_str());
+ return false;
+ }
+
+ _isTLK = filename.hasSuffix(".TLK");
+
+ _entry_count = _fd.readUint16LE();
+ _size = _fd.readUint32LE();
+
+ _entries.resize(_entry_count);
+ for (uint16 i = 0; i != _entry_count; ++i) {
+ _entries[i].id = _fd.readSint32LE();
+ _entries[i].offset = _fd.readUint32LE();
+ _entries[i].length = _fd.readUint32LE();
+
+ if (false)
+ debug("%08x %-12d %-12d", _entries[i].id, _entries[i].offset, _entries[i].length);
+
+ // Verify that the entries are sorted by id. Note that id is signed.
+ if (i > 0)
+ assert(_entries[i].id > _entries[i - 1].id);
+ }
+
+ if (_fd.err()) {
+ error("MIXArchive::open(): Error reading entries in %s", filename.c_str());
+ _fd.close();
+ return false;
+ }
+
+ debug("MIXArchive::open: Opened archive %s", filename.c_str());
+
+ return true;
+}
+
+void MIXArchive::close() {
+ return _fd.close();
+}
+
+bool MIXArchive::isOpen() const {
+ return _fd.isOpen();
+}
+
+#define ROL(n) ((n << 1) | ((n >> 31) & 1))
+
+static
+int32 mix_id(const Common::String &name) {
+ char buffer[12] = { 0 };
+
+ for (uint i = 0; i != name.size() && i < 12u; ++i)
+ buffer[i] = (char)toupper(name[i]);
+
+ uint32 id = 0;
+ for (int i = 0; i < 12 && buffer[i]; i += 4)
+ {
+ uint32 t = (uint32)buffer[i + 3] << 24
+ | (uint32)buffer[i + 2] << 16
+ | (uint32)buffer[i + 1] << 8
+ | (uint32)buffer[i + 0];
+
+ id = ROL(id) + t;
+ }
+
+ return reinterpret_cast<int32&>(id);
+}
+
+static
+int32 tlk_id(const Common::String &name) {
+ char buffer[12] = { 0 };
+
+ for (uint i = 0; i != name.size() && i < 12u; ++i)
+ buffer[i] = (char)toupper(name[i]);
+
+ int actor_id = 10 * (name[0] - '0') +
+ (name[1] - '0');
+
+ int speech_id = 1000 * (name[3] - '0') +
+ 100 * (name[4] - '0') +
+ 10 * (name[5] - '0') +
+ (name[6] - '0');
+
+ return 10000 * actor_id + speech_id;
+}
+
+uint32 MIXArchive::indexForId(int32 id) const {
+ uint32 lo = 0, hi = _entry_count;
+
+ while (lo < hi)
+ {
+ uint32 mid = lo + (hi - lo) / 2;
+
+ if (id > _entries[mid].id)
+ lo = mid + 1;
+ else if (id < _entries[mid].id)
+ hi = mid;
+ else
+ return mid;
+ }
+ return _entry_count;
+}
+
+Common::SeekableReadStream *MIXArchive::createReadStreamForMember(const Common::String &name) {
+ int32 id;
+
+ if (_isTLK)
+ id = tlk_id(name);
+ else
+ id = mix_id(name);
+
+ uint32 i = indexForId(id);
+
+ if (i == _entry_count)
+ return NULL;
+
+ uint32 start = _entries[i].offset + 6 + 12 * _entry_count;
+ uint32 end = _entries[i].length + start;
+
+ return new Common::SafeSeekableSubReadStream(&_fd, start, end, DisposeAfterUse::NO);
+}
+
+} // End of namespace BladeRunner
diff --git a/engines/bladerunner/archive.h b/engines/bladerunner/archive.h
new file mode 100644
index 0000000000..1225c71e18
--- /dev/null
+++ b/engines/bladerunner/archive.h
@@ -0,0 +1,65 @@
+/* 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_ARCHIVE_H
+#define BLADERUNNER_ARCHIVE_H
+
+#include "common/array.h"
+#include "common/file.h"
+#include "common/substream.h"
+
+namespace BladeRunner {
+
+class MIXArchive {
+public:
+ MIXArchive();
+ ~MIXArchive();
+
+ bool open(const Common::String &filename);
+ void close();
+ bool isOpen() const;
+
+ Common::String getName() { return _fd.getName(); }
+
+ Common::SeekableReadStream *createReadStreamForMember(const Common::String &name);
+
+private:
+ Common::File _fd;
+ bool _isTLK;
+
+ uint16 _entry_count;
+ uint32 _size;
+
+ struct ArchiveEntry {
+ int32 id;
+ uint32 offset;
+ uint32 length;
+ };
+
+ Common::Array<ArchiveEntry> _entries;
+
+ uint32 indexForId(int32 id) const;
+};
+
+} // End of namespace BladeRunner
+
+#endif
diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp
new file mode 100644
index 0000000000..3ae0d47239
--- /dev/null
+++ b/engines/bladerunner/bladerunner.cpp
@@ -0,0 +1,324 @@
+/* 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/bladerunner.h"
+
+#include "bladerunner/chapters.h"
+#include "bladerunner/gameinfo.h"
+#include "bladerunner/image.h"
+#include "bladerunner/settings.h"
+#include "bladerunner/vqa_decoder.h"
+
+#include "common/error.h"
+#include "common/events.h"
+#include "common/system.h"
+
+#include "engines/util.h"
+
+#include "graphics/pixelformat.h"
+
+namespace BladeRunner {
+
+BladeRunnerEngine::BladeRunnerEngine(OSystem *syst) : Engine(syst) {
+ _gameInfo = nullptr;
+
+ _windowIsActive = true;
+ _gameIsRunning = true;
+
+ _chapters = nullptr;
+ _settings = new Settings(this);
+}
+
+bool BladeRunnerEngine::hasFeature(EngineFeature f) const {
+ return f == kSupportsRTL;
+}
+
+Common::Error BladeRunnerEngine::run() {
+ initGraphics(640, 480, true, &RGB555);
+
+ startup();
+
+ if (!warnUserAboutUnsupportedGame())
+ return Common::kNoError;
+
+ init2();
+
+ /* TODO: Check for save games and enter KIA */
+ gameLoop();
+
+ shutdown();
+
+ return Common::kNoError;
+}
+
+bool BladeRunnerEngine::startup() {
+ bool r;
+
+ _surface1.create(640, 480, RGB555);
+
+ r = openArchive("STARTUP.MIX");
+ if (!r)
+ return false;
+
+ loadSplash();
+
+ _gameInfo = new GameInfo(this);
+ if (!_gameInfo)
+ return false;
+
+ r = _gameInfo->open("GAMEINFO.DAT");
+ if (!r)
+ return false;
+
+ _chapters = new Chapters(this);
+ if (!_chapters)
+ return false;
+
+ r = openArchive("MUSIC.MIX");
+ if (!r)
+ return false;
+
+ r = openArchive("SFX.MIX");
+ if (!r)
+ return false;
+
+ r = openArchive("SPCHSFX.TLK");
+ if (!r)
+ return false;
+
+ initActors();
+
+ return true;
+}
+
+void BladeRunnerEngine::initActors() {
+ // TODO: Init actors...
+
+ _settings->setChapter(1);
+ _settings->setNewSetAndScene(_gameInfo->getInitialSetId(), _gameInfo->getInitialSceneId());
+}
+
+void BladeRunnerEngine::shutdown() {
+ if (_chapters) {
+ if (_chapters->hasOpenResources())
+ _chapters->closeResources();
+ delete _chapters;
+ _chapters = 0;
+ }
+
+ if (isArchiveOpen("MUSIC.MIX"))
+ closeArchive("MUSIC.MIX");
+
+ if (isArchiveOpen("SFX.MIX"))
+ closeArchive("SFX.MIX");
+
+ if (isArchiveOpen("SPCHSFX.TLK"))
+ closeArchive("SPCHSFX.TLK");
+
+ if (isArchiveOpen("STARTUP.MIX"))
+ closeArchive("STARTUP.MIX");
+}
+
+void BladeRunnerEngine::loadSplash() {
+ Image img(this);
+ if (!img.open("SPLASH.IMG"))
+ return;
+
+ img.copyToSurface(&_surface1);
+
+ _system->copyRectToScreen(_surface1.getPixels(), _surface1.pitch, 0, 0, _surface1.w, _surface1.h);
+ _system->updateScreen();
+}
+
+bool BladeRunnerEngine::init2() {
+ return true;
+}
+
+void BladeRunnerEngine::gameLoop() {
+ _gameIsRunning = true;
+ do {
+ /* TODO: check player death */
+ gameTick();
+ } while (_gameIsRunning && !shouldQuit());
+}
+
+void BladeRunnerEngine::gameTick() {
+ handleEvents();
+
+ if (_gameIsRunning && _windowIsActive) {
+ // TODO: Only run if not in Kia, script, nor AI
+ _settings->openNewScene();
+
+ // TODO: Autosave
+ // TODO: Kia
+ // TODO: Spinner
+ // TODO: Esper
+ // TODO: VK
+ // TODO: Elevators
+ // TODO: Scores
+ // TODO: Call Script_Player_Walked_In if applicable
+ // TODO: Gun range announcements
+ // TODO: ZBUF repair dirty rects
+ // TODO: Tick Ambient Audio (in Replicant)
+
+ // TODO: Advance frame (in Replicant)
+ // TODO: Render overlays (mostly in Replicant)
+ // TODO: Tick Actor AI and Timers (timers in Replicant)
+
+ if (_settings->getNewScene() == -1 /* || in_script_counter || in_ai */) {
+
+ // TODO: Tick and draw all actors in current set (drawing works in Replicant)
+ // TODO: Draw items (drawing works in Replicant)
+ // TODO: Draw item pickup (understood, drawing works in Replicant)
+ // TODO: Draw dialogue menu
+ // TODO: Draw mouse (understood)
+ // TODO: Process AUD (audio in Replicant)
+ // TODO: Footstep sound
+
+ _system->updateScreen();
+ }
+ }
+}
+
+void BladeRunnerEngine::handleEvents() {
+ Common::Event event;
+ Common::EventManager *eventMan = _system->getEventManager();
+ while (eventMan->pollEvent(event)) {
+ }
+}
+
+void BladeRunnerEngine::playOuttake(int id, bool no_localization) {
+ Common::String name = _gameInfo->getOuttake(id);
+
+ if (no_localization)
+ name += ".VQA";
+ else
+ name += "_E.VQA";
+
+ Common::SeekableReadStream *s = getResourceStream(name);
+ if (!s)
+ return;
+
+ VQADecoder vqa_decoder(s);
+
+ bool b = vqa_decoder.read_header();
+ if (!b) return;
+
+ uint32 frame_count = 0;
+ uint32 start_time = 0;
+ uint32 next_frame_time = 0;
+
+ for (;;)
+ {
+ handleEvents();
+ // TODO: Handle skips
+ if (shouldQuit())
+ break;
+
+ uint32 cur_time = next_frame_time + 1;
+
+ if (next_frame_time <= cur_time)
+ {
+ int frame_number = vqa_decoder.read_frame();
+ debug("frame_number: %d", frame_number);
+
+ if (frame_number < 0)
+ break;
+
+ b = vqa_decoder.decode_frame((uint16*)_surface1.getPixels());
+
+ _system->copyRectToScreen(_surface1.getPixels(), _surface1.pitch, 0, 0, _surface1.w, _surface1.h);
+ _system->updateScreen();
+
+ ++frame_count;
+
+ if (!next_frame_time)
+ next_frame_time = cur_time;
+ next_frame_time = next_frame_time + (60 * 1000) / 15;
+ }
+ }
+}
+
+bool BladeRunnerEngine::openArchive(const Common::String &name) {
+ uint i;
+
+ // If archive is already open, return true
+ for (i = 0; i != kArchiveCount; ++i) {
+ if (_archives[i].isOpen() && _archives[i].getName() == name)
+ return true;
+ }
+
+ // Find first available slot
+ for (i = 0; i != kArchiveCount; ++i) {
+ if (!_archives[i].isOpen())
+ break;
+ }
+ if (i == kArchiveCount) {
+ /* TODO: BLADE.EXE retires the least recently used
+ * archive when it runs out of slots. */
+
+ error("openArchive: No more archive slots");
+ return false;
+ }
+
+ _archives[i].open(name);
+ return _archives[i].isOpen();
+}
+
+bool BladeRunnerEngine::closeArchive(const Common::String &name) {
+ for (uint i = 0; i != 10; ++i) {
+ if (_archives[i].isOpen() &&_archives[i].getName() == name) {
+ _archives[i].close();
+ return true;
+ }
+ }
+
+ debug("closeArchive: Archive %s not open.", name.c_str());
+ return false;
+}
+
+bool BladeRunnerEngine::isArchiveOpen(const Common::String &name) {
+ for (uint i = 0; i != 10; ++i) {
+ if (_archives[i].isOpen() &&_archives[i].getName() == name)
+ return true;
+ }
+
+ return false;
+}
+
+Common::SeekableReadStream *BladeRunnerEngine::getResourceStream(const Common::String &name) {
+ for (uint i = 0; i != 10; ++i) {
+ if (!_archives[i].isOpen())
+ continue;
+
+ if (false)
+ debug("getResource: Searching archive %s for %s.", _archives[i].getName().c_str(), name.c_str());
+ Common::SeekableReadStream *stream = _archives[i].createReadStreamForMember(name);
+ if (stream)
+ return stream;
+ }
+
+ debug("getResource: Resource %s not found.", name.c_str());
+ return 0;
+}
+
+} // End of namespace BladeRunner
diff --git a/engines/bladerunner/bladerunner.h b/engines/bladerunner/bladerunner.h
new file mode 100644
index 0000000000..c679b80f29
--- /dev/null
+++ b/engines/bladerunner/bladerunner.h
@@ -0,0 +1,91 @@
+/* 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_BLADERUNNER_H
+#define BLADERUNNER_BLADERUNNER_H
+
+#include "bladerunner/archive.h"
+
+#include "common/array.h"
+#include "common/stream.h"
+#include "common/types.h"
+
+#include "engines/engine.h"
+
+#include "graphics/surface.h"
+
+namespace BladeRunner {
+
+const Graphics::PixelFormat RGB555(2, 5, 5, 5, 0, 10, 5, 0, 0);
+
+class Chapters;
+class Settings;
+class GameInfo;
+
+class BladeRunnerEngine : public Engine {
+ GameInfo *_gameInfo;
+
+public:
+ bool _gameIsRunning;
+ bool _windowIsActive;
+
+ Chapters *_chapters;
+ Settings *_settings;
+
+ int in_script_counter;
+
+private:
+ static const int kArchiveCount = 10;
+ MIXArchive _archives[kArchiveCount];
+
+ Graphics::Surface _surface1;
+
+public:
+ BladeRunnerEngine(OSystem *syst);
+
+ bool hasFeature(EngineFeature f) const;
+
+ Common::Error BladeRunnerEngine::run();
+
+ bool startup();
+ void initActors();
+ void shutdown();
+
+ void loadSplash();
+ bool init2();
+
+ void gameLoop();
+ void gameTick();
+ void handleEvents();
+
+ void playOuttake(int id, bool no_localization);
+
+ bool openArchive(const Common::String &name);
+ bool closeArchive(const Common::String &name);
+ bool isArchiveOpen(const Common::String &name);
+
+ Common::SeekableReadStream *getResourceStream(const Common::String &name);
+};
+
+} // End of namespace Blade Runner
+
+#endif
diff --git a/engines/bladerunner/chapters.cpp b/engines/bladerunner/chapters.cpp
new file mode 100644
index 0000000000..280ce92ea2
--- /dev/null
+++ b/engines/bladerunner/chapters.cpp
@@ -0,0 +1,59 @@
+/* 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/chapters.h"
+
+#include "bladerunner/bladerunner.h"
+
+namespace BladeRunner {
+
+bool Chapters::enterChapter(int chapter) {
+ int id = _resourceIds[chapter];
+
+ if (!_vm->openArchive("A.TLK"))
+ return false;
+
+ if (!_vm->openArchive(Common::String::format("VQA%d.MIX", MAX(id, 3))))
+ return false;
+
+ if (!_vm->openArchive(Common::String::format("%d.TLK", MAX(id, 3))))
+ return false;
+
+ if (!_vm->openArchive(Common::String::format("OUTTAKE%d.MIX", id)))
+ return false;
+
+ _chapter = chapter;
+ _hasOpenResources = true;
+ return true;
+}
+
+void Chapters::closeResources() {
+ int id = _resourceIds[_chapter];
+
+ _vm->closeArchive("A.TLK");
+ _vm->closeArchive(Common::String::format("VQA%d.MIX", MAX(id, 3)));
+ _vm->closeArchive(Common::String::format("%d.TLK", MAX(id, 3)));
+ _vm->closeArchive(Common::String::format("OUTTAKE%d.MIX", id));
+ _hasOpenResources = false;
+}
+
+} // End of namespace BladeRunner
diff --git a/engines/bladerunner/chapters.h b/engines/bladerunner/chapters.h
new file mode 100644
index 0000000000..9fae487b00
--- /dev/null
+++ b/engines/bladerunner/chapters.h
@@ -0,0 +1,62 @@
+/* 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_CHAPTERS_H
+#define BLADERUNNER_CHAPTERS_H
+
+namespace BladeRunner {
+
+class BladeRunnerEngine;
+
+class Chapters {
+ BladeRunnerEngine *_vm;
+
+ int _chapter;
+ int _resourceIds[6];
+ bool _hasOpenResources;
+
+public:
+ Chapters(BladeRunnerEngine *vm)
+ : _vm(vm), _chapter(0)
+ {
+ _chapter = 0;
+
+ _resourceIds[0] = 1;
+ _resourceIds[1] = 1;
+ _resourceIds[2] = 2;
+ _resourceIds[3] = 2;
+ _resourceIds[4] = 3;
+ _resourceIds[5] = 4;
+
+ _hasOpenResources = false;
+ }
+
+ bool enterChapter(int chapter);
+ void closeResources();
+
+ bool hasOpenResources() { return _hasOpenResources; }
+ int currentResouceId() { return _chapter ? _resourceIds[_chapter] : -1; }
+};
+
+}; // End of namespace BladeRunner
+
+#endif
diff --git a/engines/bladerunner/configure.engine b/engines/bladerunner/configure.engine
new file mode 100644
index 0000000000..611309f523
--- /dev/null
+++ b/engines/bladerunner/configure.engine
@@ -0,0 +1,3 @@
+# This file is included from the main "configure" script
+# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
+add_engine bladerunner "Blade Runner" no "" "" "16bit"
diff --git a/engines/bladerunner/decompress_lcw.cpp b/engines/bladerunner/decompress_lcw.cpp
new file mode 100644
index 0000000000..e632ec7714
--- /dev/null
+++ b/engines/bladerunner/decompress_lcw.cpp
@@ -0,0 +1,162 @@
+/* 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/decompress_lcw.h"
+
+#include "common/util.h"
+
+namespace BladeRunner {
+
+uint32 decompress_lcw(uint8 *inBuf, uint32 inLen, uint8 *outBuf, uint32 outLen) {
+ int version = 1;
+ int count, i, color, pos, relpos, out_remain;
+
+ uint8 *src = inBuf;
+ uint8 *dst = outBuf;
+ uint8 *outEnd = dst + outLen;
+
+ if (src[0] == 0)
+ {
+ version = 2;
+ ++src;
+ }
+
+ while (src[0] != 0x80 && src < inBuf + inLen && dst < outEnd)
+ {
+ out_remain = (int)(outEnd - dst);
+
+ if (src[0] == 0xff) // 0b11111111
+ {
+ count = src[1] | (src[2] << 8);
+ pos = src[3] | (src[4] << 8);
+ src += 5;
+ count = MIN(count, out_remain);
+
+ if (version == 1)
+ {
+ for (i = 0; i < count; i++)
+ dst[i] = outBuf[i + pos];
+ }
+ else
+ {
+ for (i = 0; i < count; i++)
+ dst[i] = *(dst + i - pos);
+ }
+ }
+ else if (src[0] == 0xfe) // 0b11111110
+ {
+ count = src[1] | (src[2] << 8);
+ color = src[3];
+ src += 4;
+ count = MIN(count, out_remain);
+
+ memset(dst, color, count);
+ }
+ else if (src[0] >= 0xc0) // 0b11??????
+ {
+ count = (src[0] & 0x3f) + 3;
+ pos = src[1] | (src[2] << 8);
+ src += 3;
+ count = MIN(count, out_remain);
+
+ if (version == 1)
+ {
+ for (i = 0; i < count; i++)
+ dst[i] = outBuf[i + pos];
+ }
+ else
+ {
+ for (i = 0; i < count; i++)
+ dst[i] = *(dst + i - pos);
+ }
+ }
+ else if (src[0] >= 0x80) // 0b10??????
+ {
+ count = src[0] & 0x3f;
+ ++src;
+ count = MIN(count, out_remain);
+
+ memcpy(dst, src, count);
+ src += count;
+ }
+ else // 0b0???????
+ {
+ count = ((src[0] & 0x70) >> 4) + 3;
+ relpos = ((src[0] & 0x0f) << 8) | src[1];
+ src += 2;
+ count = MIN(count, out_remain);
+
+ for (i = 0; i < count; i++)
+ {
+ dst[i] = *(dst + i - relpos);
+ }
+ }
+
+ dst += count;
+ }
+
+ return uint32(dst - outBuf);
+}
+
+uint32 decompress_lcw_output_size(uint8 *inBuf, uint32 inLen) {
+ int count;
+ uint8 *src = inBuf;
+ uint32 outsize = 0;
+
+ if (src[0] == 0)
+ ++src;
+
+ while (src[0] != 0x80 && src < inBuf + inLen)
+ {
+ if (src[0] == 0xff) // 0b11111111
+ {
+ count = src[1] | (src[2] << 8);
+ src += 5;
+ }
+ else if (src[0] == 0xfe) // 0b11111110
+ {
+ count = src[1] | (src[2] << 8);
+ src += 4;
+ }
+ else if (src[0] >= 0xc0) // 0b11??????
+ {
+ count = (src[0] & 0x3f) + 3;
+ src += 3;
+ }
+ else if (src[0] & 0x80) // 0b10??????
+ {
+ count = src[0] & 0x3f;
+ src += count + 1;
+ }
+ else // 0b0???????
+ {
+ count = ((src[0] & 0x70) >> 4) + 3;
+ src += 2;
+ }
+
+ outsize += count;
+ }
+
+ return outsize;
+}
+
+}; // End of namespace BladeRunner
diff --git a/engines/bladerunner/decompress_lcw.h b/engines/bladerunner/decompress_lcw.h
new file mode 100644
index 0000000000..ea09aabf5b
--- /dev/null
+++ b/engines/bladerunner/decompress_lcw.h
@@ -0,0 +1,35 @@
+/* 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_DECOMPRESS_LCW_H
+#define BLADERUNNER_DECOMPRESS_LCW_H
+
+#include "common/types.h"
+
+namespace BladeRunner {
+
+uint32 decompress_lcw(uint8 *inBuf, uint32 inLen, uint8 *outBuf, uint32 outLen);
+uint32 decompress_lcw_output_size(void *inBuf, uint32 inLen);
+
+}; // End of namespace BladeRunner
+
+#endif
diff --git a/engines/bladerunner/decompress_lzo.cpp b/engines/bladerunner/decompress_lzo.cpp
new file mode 100644
index 0000000000..147d11d56e
--- /dev/null
+++ b/engines/bladerunner/decompress_lzo.cpp
@@ -0,0 +1,143 @@
+/* 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/decompress_lzo.h"
+
+namespace BladeRunner {
+
+static inline
+uint32 decode_count(const uint8 **pp) {
+ uint32 v = 0;
+ for (;!**pp;(*pp)++)
+ v += 255;
+
+ v += **pp;
+ (*pp)++;
+
+ return v;
+}
+
+static inline
+void copy(uint8 **dst, const uint8 **src, int count) {
+ assert(count > 0);
+
+ uint8 *d = *dst;
+ const uint8 *s = *src;
+
+ *dst += count;
+ *src += count;
+
+ do { *d++ = *s++; } while (--count);
+}
+
+int decompress_lzo1x(const uint8 *in, size_t inLen, uint8 *out, size_t *outLen) {
+ uint32 t;
+ uint8 *op;
+ const uint8 *ip, *m_pos;
+ const uint8 * const ip_end = in + inLen;
+
+ *outLen = 0;
+
+ op = out;
+ ip = in;
+
+ if (*ip > 17) {
+ t = *ip++ - 17;
+ if (t < 4)
+ goto match_next;
+ copy(&op, &ip, t);
+ goto first_literal_run;
+ }
+
+ for (;;) {
+ t = *ip++;
+ if (t >= 16)
+ goto match;
+
+ if (t == 0)
+ t = 15 + decode_count(&ip);
+ copy(&op, &ip, t + 3);
+
+first_literal_run:
+ t = *ip++;
+ if (t >= 16)
+ goto match;
+ m_pos = op - 0x0801 - (t >> 2) - (*ip++ << 2);
+ copy(&op, &m_pos, 3);
+ goto match_done;
+
+ for (;;) {
+match:
+ if (t >= 64)
+ {
+ m_pos = op - 1 - ((t >> 2) & 7) - (*ip++ << 3);
+ t = (t >> 5) - 1;
+ goto copy_match;
+ }
+ else if (t >= 32)
+ {
+ t &= 31;
+ if (t == 0)
+ t = 31 + decode_count(&ip);
+ m_pos = op - 1 - (ip[0] >> 2) - (ip[1] << 6);
+ ip += 2;
+ }
+ else if (t >= 16)
+ {
+ m_pos = op - ((t & 8) << 11);
+ t &= 7;
+ if (t == 0)
+ t = 7 + decode_count(&ip);
+ m_pos -= (ip[0] >> 2) + (ip[1] << 6);
+ ip += 2;
+ if (m_pos == op)
+ goto eof_found;
+ m_pos -= 0x4000;
+ }
+ else
+ {
+ m_pos = op - 1 - (t >> 2) - (*ip++ << 2);
+ copy(&op, &m_pos, 2);
+ goto match_done;
+ }
+
+copy_match:
+ copy(&op, &m_pos, t + 2);
+
+match_done:
+ t = ip[-2] & 3;
+ if (t == 0)
+ break;
+
+match_next:
+ assert(t > 0 && t <= 3);
+ copy(&op, &ip, t);
+ t = *ip++;
+ }
+ }
+
+eof_found:
+ *outLen = op - out;
+ return ip != ip_end;
+}
+
+}; // End of namespace BladeRunner
diff --git a/engines/bladerunner/decompress_lzo.h b/engines/bladerunner/decompress_lzo.h
new file mode 100644
index 0000000000..20e8581f61
--- /dev/null
+++ b/engines/bladerunner/decompress_lzo.h
@@ -0,0 +1,34 @@
+/* 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_DECOMPRESS_LZO_H
+#define BLADERUNNER_DECOMPRESS_LZO_H
+
+#include "common/types.h"
+
+namespace BladeRunner {
+
+int decompress_lzo1x(const uint8 *in, size_t inLen, uint8 *out, size_t *outLen);
+
+}; // End of namespace BladeRunner
+
+#endif
diff --git a/engines/bladerunner/detection.cpp b/engines/bladerunner/detection.cpp
new file mode 100644
index 0000000000..1e40f3d363
--- /dev/null
+++ b/engines/bladerunner/detection.cpp
@@ -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.
+ *
+ */
+
+#include "base/plugins.h"
+
+#include "engines/advancedDetector.h"
+
+#include "bladerunner/bladerunner.h"
+#include "bladerunner/detection_tables.h"
+
+namespace BladeRunner {
+
+static const PlainGameDescriptor bladeRunnerGames[] = {
+ {"bladerunner", "Blade Runner"},
+ {0, 0}
+};
+
+} // End of namespace BladeRunner
+
+class BladeRunnerMetaEngine : public AdvancedMetaEngine {
+public:
+ BladeRunnerMetaEngine() : AdvancedMetaEngine(BladeRunner::gameDescriptions, sizeof(BladeRunner::gameDescriptions[0]), BladeRunner::bladeRunnerGames) {
+ }
+
+ virtual const char *getName() const {
+ return "Blade Runner Engine";
+ }
+
+ virtual const char *getOriginalCopyright() const {
+ return "Blade Runner (C) Westwood Studios.";
+ }
+
+ virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
+};
+
+bool BladeRunnerMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const
+{
+ *engine = new BladeRunner::BladeRunnerEngine(syst);
+
+ return true;
+}
+
+#if PLUGIN_ENABLED_DYNAMIC(BLADERUNNER)
+ REGISTER_PLUGIN_DYNAMIC(BLADERUNNER, PLUGIN_TYPE_ENGINE, BladeRunnerMetaEngine);
+#else
+ REGISTER_PLUGIN_STATIC(BLADERUNNER, PLUGIN_TYPE_ENGINE, BladeRunnerMetaEngine);
+#endif
diff --git a/engines/bladerunner/detection_tables.h b/engines/bladerunner/detection_tables.h
new file mode 100644
index 0000000000..90f27ffbee
--- /dev/null
+++ b/engines/bladerunner/detection_tables.h
@@ -0,0 +1,47 @@
+/* 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_DETECTION_TABLES_H
+#define BLADERUNNER_DETECTION_TABLES_H
+
+namespace BladeRunner {
+
+static const ADGameDescription gameDescriptions[] = {
+ // BladeRunner
+ {
+ "bladerunner",
+ 0,
+ {
+ {"STARTUP.MIX", 0, "5643b53306ca7764cf1ec7b79c9630a3", 2312374},
+ AD_LISTEND
+ },
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO0()
+ },
+ AD_TABLE_END_MARKER
+};
+
+} // End of namespace Hopkins
+
+#endif
diff --git a/engines/bladerunner/gameinfo.cpp b/engines/bladerunner/gameinfo.cpp
new file mode 100644
index 0000000000..66a7af8263
--- /dev/null
+++ b/engines/bladerunner/gameinfo.cpp
@@ -0,0 +1,112 @@
+/* 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/gameinfo.h"
+
+#include "bladerunner/bladerunner.h"
+
+#include "common/debug.h"
+#include "common/substream.h"
+
+namespace BladeRunner {
+
+GameInfo::GameInfo(BladeRunnerEngine *vm)
+ : _vm(vm)
+{
+ _set_names = nullptr;
+ _sfx_tracks = nullptr;
+ _music_tracks = nullptr;
+ _outtakes = nullptr;
+}
+
+GameInfo::~GameInfo() {
+ delete[] _set_names;
+ delete[] _sfx_tracks;
+ delete[] _music_tracks;
+ delete[] _outtakes;
+}
+
+bool GameInfo::open(const Common::String &name) {
+ Common::SeekableReadStream *s = _vm->getResourceStream(name);
+
+ if (!s)
+ return false;
+
+ uint32 unk;
+ _actor_count = s->readUint32LE(); /* 00 */
+ unk = s->readUint32LE(); /* 01 */
+ _flag_count = s->readUint32LE(); /* 02 */
+ unk = s->readUint32LE(); /* 03 */
+ _global_var_count = s->readUint32LE(); /* 04 */
+ _set_names_count = s->readUint32LE(); /* 05 */
+ _initial_scene_id = s->readUint32LE(); /* 06 */
+ unk = s->readUint32LE(); /* 07 */
+ _initial_set_id = s->readUint32LE(); /* 08 */
+ unk = s->readUint32LE(); /* 09 */
+ _waypoint_count = s->readUint32LE(); /* 10 */
+ _sfx_track_count = s->readUint32LE(); /* 11 */
+ _music_track_count = s->readUint32LE(); /* 12 */
+ _outtake_count = s->readUint32LE(); /* 13 */
+ unk = s->readUint32LE(); /* 14 */
+ unk = s->readUint32LE(); /* 15 */
+ _cover_waypoint_count = s->readUint32LE(); /* 16 */
+ _flee_waypoint_count = s->readUint32LE(); /* 17 */
+
+ (void)unk;
+
+ _set_names = new char[_set_names_count][5];
+ for (uint32 i = 0; i != _set_names_count; ++i)
+ s->read(_set_names[i], 5);
+
+ _sfx_tracks = new char[_sfx_track_count][13];
+ for (uint32 i = 0; i != _sfx_track_count; ++i)
+ {
+ s->read(_sfx_tracks[i], 9);
+ strcat(_sfx_tracks[i], ".AUD");
+ }
+
+ _music_tracks = new char[_music_track_count][13];
+ for (uint32 i = 0; i != _music_track_count; ++i)
+ {
+ s->read(_music_tracks[i], 9);
+ strcat(_music_tracks[i], ".AUD");
+ }
+
+ _outtakes = new char[_outtake_count][13];
+ for (uint32 i = 0; i != _outtake_count; ++i)
+ s->read(_outtakes[i], 9);
+
+ if (false) {
+ for (uint32 i = 0; i != _set_names_count; ++i)
+ debug("%3d: %s", i, _set_names[i]);
+ for (uint32 i = 0; i != _sfx_track_count; ++i)
+ debug("%3d: %s", i, _sfx_tracks[i]);
+ for (uint32 i = 0; i != _music_track_count; ++i)
+ debug("%s", _music_tracks[i]);
+ for (uint32 i = 0; i != _outtake_count; ++i)
+ debug("%2d: %s.VQA", i, _outtakes[i]);
+ }
+
+ return !s->err();
+}
+
+} // End of namespace Blade Runner
diff --git a/engines/bladerunner/gameinfo.h b/engines/bladerunner/gameinfo.h
new file mode 100644
index 0000000000..64153beb35
--- /dev/null
+++ b/engines/bladerunner/gameinfo.h
@@ -0,0 +1,82 @@
+/* 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_GAMEINFO_H
+#define BLADERUNNER_GAMEINFO_H
+
+#include "common/str.h"
+
+#include "common/str.h"
+
+namespace BladeRunner {
+
+class BladeRunnerEngine;
+
+class GameInfo {
+ BladeRunnerEngine *_vm;
+
+ uint32 _actor_count;
+ uint32 _flag_count;
+ uint32 _global_var_count;
+ uint32 _set_names_count;
+ uint32 _initial_scene_id;
+ uint32 _initial_set_id;
+ uint32 _waypoint_count;
+ uint32 _sfx_track_count;
+ uint32 _music_track_count;
+ uint32 _outtake_count;
+ uint32 _cover_waypoint_count;
+ uint32 _flee_waypoint_count;
+
+ char (*_set_names)[5];
+ char (*_sfx_tracks)[13];
+ char (*_music_tracks)[13];
+ char (*_outtakes)[13];
+
+public:
+ GameInfo(BladeRunnerEngine *vm);
+ ~GameInfo();
+
+ bool open(const Common::String &name);
+
+ uint32 getActorCount() { return _actor_count; }
+ uint32 getFlagCount() { return _flag_count; }
+ uint32 getGlobalVarCount() { return _global_var_count; }
+ uint32 getSetNamesCount() { return _set_names_count; }
+ uint32 getInitialSceneId() { return _initial_scene_id; }
+ uint32 getInitialSetId() { return _initial_set_id; }
+ uint32 getWaypointCount() { return _waypoint_count; }
+ uint32 getSfxTrackCount() { return _sfx_track_count; }
+ uint32 getMusicTrackCount() { return _music_track_count; }
+ uint32 getOuttakeCount() { return _outtake_count; }
+ uint32 getCoverWaypointCount() { return _cover_waypoint_count; }
+ uint32 getFleeWaypointCount() { return _flee_waypoint_count; }
+
+ char *getSetName(int i) { return _set_names[i]; }
+ char *getSfxTrack(int i) { return _sfx_tracks[i]; }
+ char *getMusicTrack(int i) { return _music_tracks[i]; }
+ char *getOuttake(int i) { return _outtakes[i]; }
+};
+
+} // End of namespace Blade Runner
+
+#endif
diff --git a/engines/bladerunner/image.cpp b/engines/bladerunner/image.cpp
new file mode 100644
index 0000000000..fc6b68fd5a
--- /dev/null
+++ b/engines/bladerunner/image.cpp
@@ -0,0 +1,78 @@
+/* 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/image.h"
+
+#include "bladerunner/bladerunner.h"
+#include "bladerunner/decompress_lcw.h"
+
+#include "common/rect.h"
+
+namespace BladeRunner {
+
+Image::Image(BladeRunnerEngine *vm)
+ : _vm(vm)
+{
+}
+
+bool Image::open(const Common::String &name) {
+ Common::SeekableReadStream *stream = _vm->getResourceStream(name);
+ if (!stream) {
+ debug("Image::open failed to open '%s'\n", name.c_str());
+ return false;
+ }
+
+ char tag[4] = { 0 };
+ stream->read(tag, 3);
+ uint32 width = stream->readUint32LE();
+ uint32 height = stream->readUint32LE();
+
+ // Enforce a reasonable limit
+ assert(width < 8000 && height < 8000);
+
+ uint32 bufSize = stream->size();
+ uint8 *buf = new uint8[bufSize];
+ stream->read(buf, bufSize);
+
+ uint32 dataSize = 2 * width * height;
+ void *data = malloc(dataSize);
+ assert(data);
+
+ if (strcmp(tag, "LZO") == 0) {
+ debug("LZO");
+ } else if (strcmp(tag, "LCW") == 0) {
+ decompress_lcw(buf, bufSize, (uint8*)data, dataSize);
+ }
+
+ const Graphics::PixelFormat pixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
+ _surface.init(width, height, 2*width, data, pixelFormat);
+
+ delete[] buf;
+
+ return true;
+}
+
+void Image::copyToSurface(Graphics::Surface *dst) const {
+ dst->copyRectToSurface(_surface, 0, 0, Common::Rect(_surface.w, _surface.h));
+}
+
+}; // End of namespace BladeRunner
diff --git a/engines/bladerunner/image.h b/engines/bladerunner/image.h
new file mode 100644
index 0000000000..5d2fa69ea0
--- /dev/null
+++ b/engines/bladerunner/image.h
@@ -0,0 +1,47 @@
+/* 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_IMAGE_H
+#define BLADERUNNER_IMAGE_H
+
+#include "common/debug.h"
+#include "common/substream.h"
+
+#include "graphics/surface.h"
+
+namespace BladeRunner {
+
+class BladeRunnerEngine;
+
+class Image {
+ BladeRunnerEngine *_vm;
+ Graphics::Surface _surface;
+public:
+ Image(BladeRunnerEngine *vm);
+
+ bool open(const Common::String &name);
+ void copyToSurface(Graphics::Surface *surface) const;
+};
+
+}; // End of namespace BladeRunner
+
+#endif
diff --git a/engines/bladerunner/module.mk b/engines/bladerunner/module.mk
new file mode 100644
index 0000000000..c0d6416ad6
--- /dev/null
+++ b/engines/bladerunner/module.mk
@@ -0,0 +1,21 @@
+MODULE := engines/bladerunner
+
+MODULE_OBJS = \
+ archive.o \
+ bladerunner.o \
+ chapters.o \
+ decompress_lcw.o \
+ decompress_lzo.o \
+ detection.o \
+ gameinfo.o \
+ image.o \
+ settings.o \
+ vqa_decoder.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_BLADERUNNER), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/bladerunner/settings.cpp b/engines/bladerunner/settings.cpp
new file mode 100644
index 0000000000..8e46b5d9f2
--- /dev/null
+++ b/engines/bladerunner/settings.cpp
@@ -0,0 +1,99 @@
+/* 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/settings.h"
+
+#include "bladerunner/bladerunner.h"
+#include "bladerunner/chapters.h"
+
+namespace BladeRunner {
+
+Settings::Settings(BladeRunnerEngine *vm)
+ : _vm(vm)
+{
+ _chapter = 1;
+ _gamma = 1.0f;
+
+ _chapterChanged;
+ _newChapter;
+ _newScene;
+ _newSet;
+
+ _startingGame;
+ _loadingGame;
+
+ _fullHDFrames;
+ _mst3k;
+
+}
+
+bool Settings::openNewScene() {
+ if (_newSet == -1) {
+ assert(_newScene == -1);
+ return true;
+ }
+ assert(_newScene != -1);
+
+ if (_startingGame) {
+ // Stop ambient audio and music
+ }
+
+ // int currentSet = _vm->scene()->getSet();
+ // int newSet = _newSet;
+ // int newScene = _newScene;
+
+ _newSet = -1;
+ _newScene = -1;
+
+ if (_chapterChanged) {
+ if (_vm->_chapters->hasOpenResources())
+ _vm->_chapters->closeResources();
+
+ int newChapter = _newChapter;
+ _chapterChanged = false;
+ _newChapter = 0;
+ if (!_vm->_chapters->enterChapter(newChapter))
+ {
+ _vm->_gameIsRunning = false;
+ return false;
+ }
+ _chapter = newChapter;
+ if (_startingGame)
+ _startingGame = false;
+ }
+
+ // if (!_vm->scene()->open(newSet, newScene, _loadingGame))
+ // {
+ // _vm->_gameIsRunning = false;
+ // return false;
+ // }
+
+ // if (!_loadingGame && currentSet != newSet) {
+ // // TODO: Reset actors for new set
+
+ // }
+
+ _loadingGame = false;
+ return true;
+}
+
+} // End of namespace Blade Runner
diff --git a/engines/bladerunner/settings.h b/engines/bladerunner/settings.h
new file mode 100644
index 0000000000..cb35285e04
--- /dev/null
+++ b/engines/bladerunner/settings.h
@@ -0,0 +1,94 @@
+/* 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_SETTINGS_H
+#define BLADERUNNER_SETTINGS_H
+
+namespace BladeRunner {
+
+class BladeRunnerEngine;
+
+class Settings {
+ BladeRunnerEngine *_vm;
+
+ int _chapter;
+ float _gamma;
+
+ bool _chapterChanged;
+ int _newChapter;
+ int _newScene;
+ int _newSet;
+
+ bool _startingGame;
+ bool _loadingGame;
+
+ int _fullHDFrames;
+ int _mst3k;
+
+public:
+ Settings(BladeRunnerEngine *vm);
+
+ void setGamma(float gamma) {
+ _gamma = gamma;
+ }
+
+ void setNewSetAndScene(int set, int scene) {
+ _newSet = set;
+ _newScene = scene;
+ }
+
+ void clearNewSetAndScene() {
+ _newSet = -1;
+ _newScene = -1;
+ }
+
+ int getNewScene() {
+ return _newScene;
+ }
+
+ int getNewSet() {
+ return _newSet;
+ }
+
+ void setChapter(int newChapter) {
+ _chapterChanged = true;
+ _newChapter = newChapter;
+ }
+
+ void setLoadingGame(bool loadingGame) {
+ _loadingGame = loadingGame;
+ }
+
+ bool getLoadingGame() {
+ return _loadingGame;
+ }
+
+ void setStartingGame(bool startingGame) {
+ _startingGame = startingGame;
+ }
+
+ bool openNewScene();
+};
+
+} // End of namespace Blade Runner
+
+#endif
diff --git a/engines/bladerunner/vqa_decoder.cpp b/engines/bladerunner/vqa_decoder.cpp
new file mode 100644
index 0000000000..b717964f5f
--- /dev/null
+++ b/engines/bladerunner/vqa_decoder.cpp
@@ -0,0 +1,933 @@
+/* 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_VQA_DECODER_H
+#define BLADERUNNER_VQA_DECODER_H
+
+#include "bladerunner/vqa_decoder.h"
+
+#include "bladerunner/decompress_lcw.h"
+#include "bladerunner/decompress_lzo.h"
+
+#include "common/array.h"
+#include "common/util.h"
+
+namespace BladeRunner {
+
+#define kAESC 0x41455343
+#define kCBFZ 0x4342465A
+#define kCIND 0x43494E44
+#define kCINF 0x43494E46
+#define kCINH 0x43494E48
+#define kCLIP 0x434C4950
+#define kFINF 0x46494E46
+#define kFORM 0x464f524d
+#define kLIND 0x4C494E44
+#define kLINF 0x4C494E46
+#define kLINH 0x4C494E48
+#define kLITE 0x4C495445
+#define kLNID 0x4C4E4944
+#define kLNIH 0x4C4E4948
+#define kLNIN 0x4C4E494E
+#define kLNIO 0x4C4E494F
+#define kMFCD 0x4D464344
+#define kMFCH 0x4D464348
+#define kMFCI 0x4D464349
+#define kMFCT 0x4D464354
+#define kMSCH 0x4D534348
+#define kMSCI 0x4D534349
+#define kMSCT 0x4D534354
+#define kSN2J 0x534e324a
+#define kSND2 0x534e4432
+#define kVIEW 0x56494557
+#define kVPTR 0x56505452
+#define kVQFL 0x5651464C
+#define kVQFR 0x56514652
+#define kVQHD 0x56514844
+#define kWVQA 0x57565141
+#define kZBUF 0x5A425546
+
+VQADecoder::VQADecoder(Common::SeekableReadStream *r)
+ : r(r),
+ frame(0),
+ zbuf(0),
+ codebook(0),
+ cbfz(0),
+ vptr(0),
+ cur_frame(-1),
+ cur_loop(-1),
+ loop_special(-1),
+ loop_default(-1),
+ has_view(false),
+ audio_frame(0),
+ max_view_chunk_size(0),
+ max_zbuf_chunk_size(0),
+ max_aesc_chunk_size(0)
+{
+ // debug("Opening VQA: '%s'\n", r->get_name());
+}
+
+VQADecoder::~VQADecoder()
+{
+}
+
+struct iff_chunk_header_s
+{
+ iff_chunk_header_s()
+ : id(0), size(0)
+ {}
+
+ uint32 id;
+ uint32 size;
+};
+
+static inline uint32 roundup(uint32 v)
+{
+ return (v + 1) & ~1u;
+}
+
+const char *str_tag(uint32 tag);
+
+int32 stream_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 read_iff_chunk_header(Common::SeekableReadStream *r, iff_chunk_header_s *ts)
+{
+ if (stream_remain(r) < 8)
+ return false;
+
+ ts->id = r->readUint32BE();
+ ts->size = r->readUint32BE();
+
+ // if (ts->size != roundup(ts->size))
+ // debug("%s: %d\n", str_tag(ts->id), ts->size);
+
+ return true;
+}
+
+const char *str_tag(uint32 tag)
+{
+ static char s[5];
+
+ sprintf(s, "%c%c%c%c",
+ (tag >> 24) & 0xff,
+ (tag >> 16) & 0xff,
+ (tag >> 8) & 0xff,
+ (tag >> 0) & 0xff);
+
+ return s;
+}
+
+bool VQADecoder::read_header()
+{
+ iff_chunk_header_s chd;
+ uint32 type;
+ bool rc;
+
+ read_iff_chunk_header(r, &chd);
+ if (chd.id != kFORM || !chd.size)
+ return false;
+
+ type = r->readUint32BE();
+
+ if (type != kWVQA)
+ return false;
+
+ do {
+ if (!read_iff_chunk_header(r, &chd))
+ return false;
+
+ debug("\t%s : %x\n", str_tag(chd.id), chd.size);
+
+ rc = false;
+ switch (chd.id)
+ {
+ case kCINF: rc = read_cinf(chd.size); break;
+ case kCLIP: rc = read_clip(chd.size); break;
+ case kFINF: rc = read_finf(chd.size); break;
+ case kLINF: rc = read_linf(chd.size); break;
+ case kLNIN: rc = read_lnin(chd.size); break;
+ case kMFCI: rc = read_mfci(chd.size); break;
+ case kMSCI: rc = read_msci(chd.size); break;
+ case kVQHD: rc = read_vqhd(chd.size); break;
+ default:
+ debug("Unhandled chunk '%s'\n", str_tag(chd.id));
+ r->skip(roundup(chd.size));
+ rc = true;
+ }
+
+ if (!rc)
+ {
+ debug("failed to handle chunk %s\n", str_tag(chd.id));
+ return false;
+ }
+
+ } while (chd.id != kFINF);
+
+ for (int i = 0; i != loop_info.loop_count; ++i) {
+ debug("LOOP %2d: %4d %4d %s\n", i,
+ loop_info.loops[i].begin,
+ loop_info.loops[i].end,
+ loop_info.loops[i].name.c_str());
+ }
+
+ return true;
+}
+
+bool VQADecoder::read_vqhd(uint32 size)
+{
+ if (size != 42)
+ return false;
+
+ header.version = r->readUint16LE();
+ header.flags = r->readUint16LE();
+ header.numFrames = r->readUint16LE();
+ header.width = r->readUint16LE();
+ header.height = r->readUint16LE();
+ header.blockW = r->readByte();
+ header.blockH = r->readByte();
+ header.frameRate = r->readByte();
+ header.cbParts = r->readByte();
+ header.colors = r->readUint16LE();
+ header.maxBlocks = r->readUint16LE();
+ header.offset_x = r->readUint16LE();
+ header.offset_y = r->readUint16LE();
+ header.maxVPTRSize = r->readUint16LE();
+ header.freq = r->readUint16LE();
+ header.channels = r->readByte();
+ header.bits = r->readByte();
+ header.unk3 = r->readUint32LE();
+ header.unk4 = r->readUint16LE();
+ header.maxCBFZSize = r->readUint32LE();
+ header.unk5 = r->readUint32LE();
+
+ if (header.offset_x || header.offset_y)
+ {
+ debug("header.offset_x, header.offset_y: %d %d\n", header.offset_x, header.offset_y);
+ }
+
+ // if (header.unk3 || header.unk4 != 4 || header.unk5 || header.flags != 0x0014)
+ {
+ debug("header.version %d\n", header.version);
+ debug("header.flags %04x\n", header.flags);
+ debug("header.numFrames %d\n", header.numFrames);
+ debug("header.width %d\n", header.width);
+ debug("header.height %d\n", header.height);
+ debug("header.blockW %d\n", header.blockW);
+ debug("header.blockH %d\n", header.blockH);
+ debug("header.frameRate %d\n", header.frameRate);
+ debug("header.cbParts %d\n", header.cbParts);
+ debug("header.colors %d\n", header.colors);
+ debug("header.maxBlocks %d\n", header.maxBlocks);
+ debug("header.offsetX %d\n", header.offset_x);
+ debug("header.offsetY %d\n", header.offset_y);
+ debug("header.maxVPTRSize %d\n", header.maxVPTRSize);
+ debug("header.freq %d\n", header.freq);
+ debug("header.channels %d\n", header.channels);
+ debug("header.bits %d\n", header.bits);
+ debug("header.unk3 %d\n", header.unk3);
+ debug("header.unk4 %d\n", header.unk4);
+ debug("header.maxCBFZSize %d\n", header.maxCBFZSize);
+ debug("header.unk5 %d\n", header.unk5);
+ }
+
+ // exit(-1);
+
+ return true;
+}
+
+bool VQADecoder::read_msci(uint32 size)
+{
+ iff_chunk_header_s chd;
+ read_iff_chunk_header(r, &chd);
+
+ if (chd.id != kMSCH)
+ return false;
+
+ uint32 count, unk0;
+ count = r->readUint32LE();
+ unk0 = r->readUint32LE();
+ assert(unk0 == 0);
+
+ read_iff_chunk_header(r, &chd);
+ if (chd.id != kMSCT || chd.size != count * 0x10)
+ return false;
+
+ for (uint32 i = 0; i < count; ++i)
+ {
+ uint32 tag, size;
+ tag = r->readUint32BE();
+ size = r->readUint32LE();
+
+ switch (tag)
+ {
+ case kVIEW:
+ max_view_chunk_size = size;
+ debug("max VIEW size: %08x\n", max_view_chunk_size);
+ break;
+ case kZBUF:
+ max_zbuf_chunk_size = size;
+ zbuf_chunk = new uint8[roundup(max_zbuf_chunk_size)];
+ debug("max ZBUF size: %08x\n", max_zbuf_chunk_size);
+ break;
+ case kAESC:
+ max_aesc_chunk_size = size;
+ debug("max AESC size: %08x\n", max_aesc_chunk_size);
+ break;
+ default:
+ debug("Unknown tag in MSCT: %s\n", str_tag(tag));
+ }
+
+ uint32 zero;
+ zero = r->readUint32LE(); assert(zero == 0);
+ zero = r->readUint32LE(); assert(zero == 0);
+ }
+
+ return true;
+}
+
+bool VQADecoder::read_linf(uint32 size)
+{
+ iff_chunk_header_s chd;
+ read_iff_chunk_header(r, &chd);
+
+ if (chd.id != kLINH || chd.size != 6)
+ return false;
+
+ loop_info.loop_count = r->readUint16LE();
+ loop_info.flags = r->readUint32LE();
+
+ if ((loop_info.flags & 3) == 0)
+ return false;
+
+ read_iff_chunk_header(r, &chd);
+ if (chd.id != kLIND || chd.size != 4u * loop_info.loop_count)
+ return false;
+
+ loop_info.loops = new Loop[loop_info.loop_count];
+ for (int i = 0; i != loop_info.loop_count; ++i)
+ {
+ loop_info.loops[i].begin = r->readUint16LE();
+ loop_info.loops[i].end = r->readUint16LE();
+
+ // debug("Loop %d: %04x %04x\n", i, loop_info.loops[i].begin, loop_info.loops[i].end);
+ }
+
+ return true;
+}
+
+bool VQADecoder::read_cinf(uint32 size)
+{
+ iff_chunk_header_s chd;
+
+ read_iff_chunk_header(r, &chd);
+ if (chd.id != kCINH || chd.size != 8u)
+ return false;
+
+ clip_info.clip_count = r->readUint16LE();
+ r->skip(6);
+
+ read_iff_chunk_header(r, &chd);
+ if (chd.id != kCIND || chd.size != 6u * clip_info.clip_count)
+ return false;
+
+ for (int i = 0; i != clip_info.clip_count; ++i)
+ {
+ uint16 a;
+ uint32 b;
+ a = r->readUint16LE();
+ b = r->readUint32LE();
+ debug("%4d %08x\n", a, b);
+ }
+
+ return true;
+}
+
+bool VQADecoder::read_finf(uint32 size)
+{
+ if (size != 4u * header.numFrames)
+ return false;
+
+ frame_info = new uint32[header.numFrames];
+
+ for (uint32 i = 0; i != header.numFrames; ++i)
+ frame_info[i] = r->readUint32LE();
+
+ if (false) {
+ uint32 last = 0;
+ for (uint32 i = 0; i != header.numFrames; ++i)
+ {
+ uint32 diff = frame_info[i] - last;
+ debug("frame_info[%4d] = 0x%08x - %08x\n", i, frame_info[i], diff);
+ last = frame_info[i];
+ }
+ }
+
+ return true;
+}
+
+bool VQADecoder::read_lnin(uint32 size)
+{
+ iff_chunk_header_s chd;
+
+ read_iff_chunk_header(r, &chd);
+ if (chd.id != kLNIH || chd.size != 10)
+ return false;
+
+ uint16 loop_names_count, loop_unk_1, loop_unk_2, loop_unk_3, loop_unk_4;
+
+ loop_names_count = r->readUint16LE();
+ loop_unk_1 = r->readUint16LE();
+ loop_unk_2 = r->readUint16LE();
+ loop_unk_3 = r->readUint16LE();
+ loop_unk_4 = r->readUint16LE();
+
+ if (loop_names_count != loop_info.loop_count)
+ return false;
+
+ read_iff_chunk_header(r, &chd);
+ if (chd.id != kLNIO || chd.size != 4u * loop_names_count)
+ return false;
+
+ uint32 *loop_name_offsets = (uint32*)alloca(loop_names_count * sizeof(uint32));
+ for (int i = 0; i != loop_names_count; ++i) {
+ loop_name_offsets[i] = r->readUint32LE();
+ }
+
+ read_iff_chunk_header(r, &chd);
+ if (chd.id != kLNID)
+ return false;
+
+ char *names = (char*)alloca(roundup(chd.size));
+ r->read(names, roundup(chd.size));
+
+ for (int i = 0; i != loop_names_count; ++i) {
+ char *begin = names + loop_name_offsets[i];
+ size_t len = ((i == loop_names_count) ? chd.size : loop_name_offsets[i+1]) - loop_name_offsets[i];
+
+ loop_info.loops[i].name = Common::String(begin, len);
+ }
+
+ return true;
+}
+
+bool VQADecoder::read_clip(uint32 size)
+{
+ r->skip(roundup(size));
+ return true;
+}
+
+bool VQADecoder::read_mfci(uint32 size)
+{
+ r->skip(roundup(size));
+ return true;
+}
+
+int VQADecoder::read_frame()
+{
+ // debug("VQADecoder::read_frame(): %d, %d, %d, %d\n", loop_default, loop_special, cur_loop, cur_frame);
+
+ if (loop_info.loop_count)
+ {
+ if (loop_special >= 0)
+ {
+ cur_loop = loop_special;
+ loop_special = -1;
+
+ cur_frame = loop_info.loops[cur_loop].begin;
+ seek_to_frame(cur_frame);
+ }
+ else if (cur_loop == -1 && loop_default >= 0)
+ {
+ cur_loop = loop_default;
+ cur_frame = loop_info.loops[cur_loop].begin;
+ seek_to_frame(cur_frame);
+ }
+ else if (cur_loop >= -1 && cur_frame == loop_info.loops[cur_loop].end)
+ {
+ if (loop_default == -1)
+ return -1;
+
+ cur_loop = loop_default;
+ cur_frame = loop_info.loops[cur_loop].begin;
+ seek_to_frame(cur_frame);
+ }
+ else
+ ++cur_frame;
+ }
+ else
+ ++cur_frame;
+
+ if (cur_frame >= header.numFrames)
+ return -1;
+
+ iff_chunk_header_s chd;
+
+ has_view = false;
+
+ if (stream_remain(r) < 8) {
+ debug("remain: %d\n", stream_remain(r));
+ return -1;
+ }
+
+ do {
+ if (!read_iff_chunk_header(r, &chd)) {
+ debug("Error reading chunk header\n");
+ return -1;
+ }
+
+ // debug("%s ", str_tag(chd.id));
+
+ bool rc = false;
+ switch (chd.id)
+ {
+ case kAESC: rc = read_aesc(chd.size); break;
+ case kLITE: rc = read_lite(chd.size); break;
+ case kSN2J: rc = read_sn2j(chd.size); break;
+ case kSND2: rc = read_snd2(chd.size); break;
+ case kVIEW: rc = read_view(chd.size); break;
+ case kVQFL: rc = read_vqfl(chd.size); break;
+ case kVQFR: rc = read_vqfr(chd.size); break;
+ case kZBUF: rc = read_zbuf(chd.size); break;
+ default:
+ r->skip(roundup(chd.size));
+ rc = true;
+ }
+
+ if (!rc)
+ {
+ debug("Error handling chunk %s\n", str_tag(chd.id));
+ return -1;
+ }
+ } while (chd.id != kVQFR);
+
+ return cur_frame;
+}
+
+
+bool VQADecoder::read_sn2j(uint32 size)
+{
+ if (size != 6)
+ return false;
+
+ uint16 step_index;
+ uint32 predictor;
+
+ step_index = r->readUint16LE();
+ predictor = r->readUint32LE();
+
+ // ima_adpcm_ws_decoder.set_parameters(step_index >> 5, predictor);
+
+ return true;
+}
+
+bool VQADecoder::read_snd2(uint32 size)
+{
+ if (size != 735)
+ {
+ debug("audio frame size: %d\n", size);
+ return false;
+ }
+
+ if (!audio_frame)
+ audio_frame = new int16[2 * size];
+ memset(audio_frame, 0, 4 * size);
+
+ uint8 *in_frame = new uint8[roundup(size)];
+ r->read(in_frame, roundup(size));
+
+ // ima_adpcm_ws_decoder.decode(in_frame, size, audio_frame);
+
+ delete[] in_frame;
+
+ return true;
+}
+
+bool VQADecoder::read_vqfr(uint32 size)
+{
+ iff_chunk_header_s chd;
+
+ while (size >= 8)
+ {
+ if (!read_iff_chunk_header(r, &chd))
+ return false;
+ size -= roundup(chd.size) + 8;
+
+ // debug("(%s) ", str_tag(chd.id)); fflush(0);
+
+ bool rc = false;
+ switch (chd.id)
+ {
+ case kCBFZ: rc = read_cbfz(chd.size); break;
+ case kVPTR: rc = read_vptr(chd.size); break;
+ default:
+ r->skip(roundup(chd.size));
+ }
+
+ if (!rc)
+ {
+ debug("VQFR: error handling chunk %s\n", str_tag(chd.id));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool VQADecoder::read_vqfl(uint32 size)
+{
+ iff_chunk_header_s chd;
+
+ while (size >= 8)
+ {
+ if (!read_iff_chunk_header(r, &chd))
+ return false;
+ size -= roundup(chd.size) + 8;
+
+ bool rc = false;
+ switch (chd.id)
+ {
+ case kCBFZ: rc = read_cbfz(chd.size); break;
+ default:
+ r->skip(roundup(chd.size));
+ }
+
+ if (!rc)
+ {
+ debug("VQFL: error handling chunk %s\n", str_tag(chd.id));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool VQADecoder::read_cbfz(uint32 size)
+{
+ if (size > header.maxCBFZSize)
+ {
+ debug("%d > %d\n", size, header.maxCBFZSize);
+ return false;
+ }
+
+ if (!codebook)
+ {
+ codebookSize = 2 * header.maxBlocks * header.blockW * header.blockH;
+ codebook = new uint8[codebookSize];
+ }
+ if (!cbfz)
+ cbfz = new uint8[roundup(header.maxCBFZSize)];
+
+ r->read(cbfz, roundup(size));
+
+ decompress_lcw(cbfz, size, codebook, codebookSize);
+
+ return true;
+}
+
+static
+int decodeZBUF_partial(uint8 *src, uint16 *cur_zbuf, uint32 src_len)
+{
+ uint32 dst_size = 640 * 480; // This is taken from global variables?
+ uint32 dst_remain = dst_size;
+
+ uint16 *curz_p = cur_zbuf;
+ uint16 *in_p = (uint16*)src;
+
+ while (dst_remain && (in_p - (uint16*)src) < (ptrdiff_t)src_len)
+ {
+ uint32 count = FROM_LE_16(*in_p++);
+
+ if (count & 0x8000)
+ {
+ count = MIN(count & 0x7fff, dst_remain);
+ dst_remain -= count;
+
+ while (count--)
+ {
+ uint16 value = FROM_LE_16(*in_p++);
+ if (value)
+ *curz_p = value;
+ ++curz_p;
+ }
+ }
+ else
+ {
+ count = MIN(count, dst_remain);
+ dst_remain -= count;
+ uint16 value = FROM_LE_16(*in_p++);
+
+ if (!value)
+ curz_p += count;
+ else
+ {
+ while (count--)
+ *curz_p++ = value;
+ }
+ }
+ }
+ return dst_size - dst_remain;
+}
+
+bool VQADecoder::read_zbuf(uint32 size)
+{
+ if (size > max_zbuf_chunk_size) {
+ debug("VQA ERROR: ZBUF chunk size: %08x > %08x\n", size, max_zbuf_chunk_size);
+ r->skip(roundup(size));
+ return false;
+ }
+
+ uint32 width, height, complete, unk0;
+ width = r->readUint32LE();
+ height = r->readUint32LE();
+ complete = r->readUint32LE();
+ unk0 = r->readUint32LE();
+
+ uint32 remain = size - 16;
+
+ if (width != header.width || height != header.height)
+ {
+ debug("%d, %d, %d, %d\n", width, height, complete, unk0);
+ r->skip(roundup(remain));
+ return false;
+ }
+
+ if (!zbuf)
+ {
+ if (!complete) {
+ r->skip(roundup(remain));
+ return false;
+ }
+ zbuf = new uint16[width * height];
+ }
+
+ r->read(zbuf_chunk, roundup(remain));
+
+ if (complete) {
+ size_t zbuf_out_size;
+ decompress_lzo1x(zbuf_chunk, remain, (uint8*)zbuf, &zbuf_out_size);
+ } else {
+ decodeZBUF_partial(zbuf_chunk, zbuf, remain);
+ }
+
+ return true;
+}
+
+bool VQADecoder::get_zbuf(uint16 *a_zbuf)
+{
+ if (!zbuf)
+ return false;
+
+ memcpy(a_zbuf, zbuf, 2 * header.width * header.height);
+ return true;
+}
+
+bool VQADecoder::read_view(uint32 size)
+{
+ if (size != 56)
+ return false;
+
+ r->skip(size);
+ // has_view = true;
+
+ return true;
+}
+
+bool VQADecoder::read_aesc(uint32 size)
+{
+ r->skip(roundup(size));
+ return true;
+}
+
+bool VQADecoder::read_lite(uint32 size)
+{
+ r->skip(roundup(size));
+ return true;
+}
+
+bool VQADecoder::read_vptr(uint32 size)
+{
+ if (size > header.maxVPTRSize)
+ return false;
+
+ if (!vptr)
+ vptr = new uint8[roundup(header.maxVPTRSize)];
+
+ vptrSize = size;
+ r->read(vptr, roundup(size));
+
+ return true;
+}
+
+void VQADecoder::vptr_write_block(uint16 *frame, unsigned int dst_block, unsigned int src_block, int count, bool alpha) const
+{
+ uint16 frame_width = header.width;
+ uint32 frame_stride = 640;
+ uint16 block_width = header.blockW;
+ uint16 block_height = header.blockH;
+
+ const uint8 *const block_src =
+ &codebook[2 * src_block * block_width * block_height];
+
+ int blocks_per_line = frame_width / block_width;
+
+ do
+ {
+ uint32 frame_x = dst_block % blocks_per_line * block_width + header.offset_x / 2;
+ uint32 frame_y = dst_block / blocks_per_line * block_height + header.offset_y;
+
+ uint32 dst_offset = frame_x + frame_y * frame_stride;
+
+ const uint8 *__restrict src = block_src;
+ uint16 *__restrict dst = frame + dst_offset;
+
+ unsigned int block_y;
+ for (block_y = 0; block_y != block_height; ++block_y)
+ {
+ unsigned int block_x;
+ for (block_x = 0; block_x != block_width; ++block_x)
+ {
+ uint16 rgb555 = src[0] | (src[1] << 8);
+ src += 2;
+
+ if (!(alpha && (rgb555 & 0x8000)))
+ *dst = rgb555;
+ ++dst;
+ }
+ dst += frame_stride - block_width;
+ }
+
+ ++dst_block;
+ }
+ while (--count);
+}
+
+void VQADecoder::set_loop_special(int loop, bool wait)
+{
+ loop_special = loop;
+ if (!wait)
+ cur_loop = -1;
+}
+
+void VQADecoder::set_loop_default(int loop)
+{
+ loop_default = loop;
+}
+
+bool VQADecoder::seek_to_frame(int frame)
+{
+ if (frame < 0 || frame >= header.numFrames)
+ return false;
+
+ r->seek(2 * (frame_info[frame] & 0x0fffffff), SEEK_SET);
+ return true;
+}
+
+bool VQADecoder::decode_frame(uint16 *a_frame)
+{
+ if (!codebook || !vptr)
+ return false;
+
+ if (!frame)
+ frame = new uint16[header.width * header.height];
+
+ uint8 *src = vptr;
+ uint8 *end = vptr + vptrSize;
+
+ uint16 count, src_block, dst_block = 0;
+ (void)src_block;
+
+ while (end - src >= 2)
+ {
+ uint16 command = src[0] | (src[1] << 8);
+ uint8 prefix = command >> 13;
+ src += 2;
+
+ switch (prefix)
+ {
+ case 0:
+ count = command & 0x1fff;
+ dst_block += count;
+ break;
+ case 1:
+ count = 2 * (((command >> 8) & 0x1f) + 1);
+ src_block = command & 0x00ff;
+
+ vptr_write_block(frame, dst_block, src_block, count);
+ dst_block += count;
+ break;
+ case 2:
+ count = 2 * (((command >> 8) & 0x1f) + 1);
+ src_block = command & 0x00ff;
+
+ vptr_write_block(frame, dst_block, src_block, 1);
+ ++dst_block;
+
+ for (int i = 0; i < count; ++i)
+ {
+ src_block = *src++;
+ vptr_write_block(frame, dst_block, src_block, 1);
+ ++dst_block;
+ }
+ break;
+ case 3:
+ case 4:
+ count = 1;
+ src_block = command & 0x1fff;
+
+ vptr_write_block(frame, dst_block, src_block, count, prefix == 4);
+ ++dst_block;
+ break;
+ case 5:
+ case 6:
+ count = *src++;
+ src_block = command & 0x1fff;
+
+ vptr_write_block(frame, dst_block, src_block, count, prefix == 6);
+ dst_block += count;
+ break;
+ default:
+ debug("Undefined case %d\n", command >> 13);
+ }
+ }
+
+ memcpy(a_frame, frame, 2 * 640 * 480);
+
+ return true;
+}
+
+int16 *VQADecoder::get_audio_frame()
+{
+ return audio_frame;
+}
+
+}; // End of namespace BladeRunner
+
+#endif
diff --git a/engines/bladerunner/vqa_decoder.h b/engines/bladerunner/vqa_decoder.h
new file mode 100644
index 0000000000..c4b5974f2a
--- /dev/null
+++ b/engines/bladerunner/vqa_decoder.h
@@ -0,0 +1,165 @@
+/* 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_VQA_H
+#define BLADERUNNER_VQA_H
+
+#include "common/debug.h"
+#include "common/str.h"
+#include "common/stream.h"
+#include "common/types.h"
+
+namespace BladeRunner {
+class VQADecoder
+{
+ struct Header
+ {
+ uint16 version; // 0x00
+ uint16 flags; // 0x02
+ uint16 numFrames; // 0x04
+ uint16 width; // 0x06
+ uint16 height; // 0x08
+ uint8 blockW; // 0x0A
+ uint8 blockH; // 0x0B
+ uint8 frameRate; // 0x0C
+ uint8 cbParts; // 0x0D
+ uint16 colors; // 0x0E
+ uint16 maxBlocks; // 0x10
+ uint16 offset_x; // 0x12
+ uint16 offset_y; // 0x14
+ uint16 maxVPTRSize; // 0x16
+ uint16 freq; // 0x18
+ uint8 channels; // 0x1A
+ uint8 bits; // 0x1B
+ uint32 unk3; // 0x1C
+ uint16 unk4; // 0x20
+ uint32 maxCBFZSize; // 0x22
+ uint32 unk5; // 0x26
+ // 0x2A
+ };
+
+ struct Loop {
+ uint16 begin;
+ uint16 end;
+ Common::String name;
+
+ Loop() :
+ begin(0),
+ end(0)
+ {}
+ };
+
+ struct LoopInfo
+ {
+ uint16 loop_count;
+ uint32 flags;
+ Loop *loops;
+
+ LoopInfo()
+ : loop_count(0)
+ {}
+ };
+
+ struct ClipInfo
+ {
+ uint16 clip_count;
+ };
+
+ Common::SeekableReadStream *r;
+
+ Header header;
+ LoopInfo loop_info;
+ ClipInfo clip_info;
+
+ uint16 *frame;
+ uint16 *zbuf;
+
+ size_t codebookSize;
+ uint8 *codebook;
+ uint8 *cbfz;
+
+ size_t vptrSize;
+ uint8 *vptr;
+
+ uint32 *frame_info;
+
+ int cur_frame;
+
+ int cur_loop;
+ int loop_special;
+ int loop_default;
+
+ uint32 max_view_chunk_size;
+ uint32 max_zbuf_chunk_size;
+ uint32 max_aesc_chunk_size;
+ uint8 *zbuf_chunk;
+
+ bool has_view;
+ // view_t view;
+
+ // ima_adpcm_ws_decoder_t ima_adpcm_ws_decoder;
+ int16 *audio_frame;
+
+ bool read_vqhd(uint32 size);
+ bool read_msci(uint32 size);
+ bool read_mfci(uint32 size);
+ bool read_linf(uint32 size);
+ bool read_cinf(uint32 size);
+ bool read_finf(uint32 size);
+ bool read_lnin(uint32 size);
+ bool read_clip(uint32 size);
+
+ bool read_sn2j(uint32 size);
+ bool read_snd2(uint32 size);
+ bool read_vqfr(uint32 size);
+ bool read_vptr(uint32 size);
+ bool read_vqfl(uint32 size);
+ bool read_cbfz(uint32 size);
+ bool read_zbuf(uint32 size);
+ bool read_view(uint32 size);
+ bool read_aesc(uint32 size);
+ bool read_lite(uint32 size);
+
+public:
+ VQADecoder(Common::SeekableReadStream *r);
+ ~VQADecoder();
+
+ bool read_header();
+ int read_frame();
+
+ void vptr_write_block(uint16 *frame, unsigned int dst_block, unsigned int src_block, int count, bool alpha = false) const;
+
+ void set_loop_special(int loop, bool wait);
+ void set_loop_default(int loop);
+
+ bool seek_to_frame(int frame);
+ bool decode_frame(uint16 *frame);
+
+ int16 *get_audio_frame();
+
+ // bool get_view(view_t *view);
+ bool get_zbuf(uint16 *zbuf);
+};
+
+}; // End of namespace BladeRunner
+
+#endif