diff --git a/engines/bladerunner/archive.cpp b/engines/bladerunner/archive.cpp new file mode 100644 index 000000000000..93c122f9afb3 --- /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(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 000000000000..1225c71e1846 --- /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 _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 000000000000..3ae0d4723920 --- /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 000000000000..c679b80f2918 --- /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 000000000000..280ce92ea2e0 --- /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 000000000000..9fae487b0099 --- /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 000000000000..611309f523fb --- /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 000000000000..e632ec77145e --- /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 000000000000..ea09aabf5b6b --- /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 000000000000..147d11d56ea6 --- /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 000000000000..20e8581f6117 --- /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 000000000000..1e40f3d3637c --- /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 000000000000..90f27ffbeeb0 --- /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 000000000000..66a7af8263a8 --- /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 000000000000..64153beb3547 --- /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 000000000000..fc6b68fd5ac7 --- /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 000000000000..5d2fa69ea022 --- /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 000000000000..c0d6416ad654 --- /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 000000000000..8e46b5d9f259 --- /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 000000000000..cb35285e045d --- /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 000000000000..b717964f5f46 --- /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 000000000000..c4b5974f2a79 --- /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