diff options
author | Thomas Fach-Pedersen | 2014-05-16 21:58:49 +0200 |
---|---|---|
committer | Eugene Sandulenko | 2016-09-29 22:21:00 +0200 |
commit | 2e3fd7cf050e24b9f6363b1a38c6ff45847c6d6f (patch) | |
tree | 225c2535e8f6ac3dd21ccc082c9d7e0d9573380f /engines | |
parent | c48dfb70718f971a76049061a1c2f8bc2f37bb46 (diff) | |
download | scummvm-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.
Diffstat (limited to 'engines')
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 |