diff options
47 files changed, 937 insertions, 212 deletions
diff --git a/devtools/create_titanic/create_titanic_dat.cpp b/devtools/create_titanic/create_titanic_dat.cpp index 3d5e4476c9..b9d21d7ca8 100644 --- a/devtools/create_titanic/create_titanic_dat.cpp +++ b/devtools/create_titanic/create_titanic_dat.cpp @@ -56,7 +56,7 @@ * ASCIIZ - name of the resource */ -#define VERSION_NUMBER 2 +#define VERSION_NUMBER 3 #define HEADER_SIZE 0x1380 Common::File inputFile, outputFile; @@ -615,7 +615,7 @@ static const BedheadEntry OFF_RESTING_D_WRONG[1] = { { "Any", "Any", "Any", "ClosedWrong", 59, 70 } }; -static const char *const STRINGS_EN[141] = { +static const char *const STRINGS_EN[151] = { "", "You are standing outside the Pellerator.", "I'm sorry, you cannot enter this pellerator at present as a bot is in the way.", @@ -761,10 +761,20 @@ static const char *const STRINGS_EN[141] = { "Saved Chevron: ", "Current location: ", "A hot", - "A cold" + "A cold", + "Load the game.", + "Save the game.", + "Empty", + "Quit the game.", + "Are you sure you want to quit?", + "Change the volume settings", + "Master volume", + "Music volume", + "Parrot volume", + "Speech volume" }; -static const char *const STRINGS_DE[186] = { +static const char *const STRINGS_DE[197] = { "", "Sie befinden sich vor dem Pellerator.", "Wir bedauern, da ein Bot den Weg versperrt, ist Ihnen der " @@ -848,7 +858,6 @@ static const char *const STRINGS_DE[186] = { "Leider ist es nicht m\xF6""glich den T\xFC""r-Bot von diesem Ort aus herbeizurufen.", "Leider ist es nicht m\xF6""glich den Klingel-Bot von diesem Ort aus herbeizurufen.", "Es ist niemand hier mit dem du sprechen k\xF6""nntest", - "Spricht mit ", "Im Gespr\xE4""ch mit ", "der T\xFC""r-Bot", "der Empfangs-Bot", @@ -891,7 +900,7 @@ static const char *const STRINGS_DE[186] = { "Rufe Pellerator", "Gehe zum Grund des Brunnens", "Gehe zur Oberfl\xE4""che des Brunnens", - "Gehe zu deiner Kabine" + "Gehe zu deiner Kabine", "Gehe zur Bar", "Gehe zum Promenadendeck", "Gehe zum Baumgarten", @@ -900,11 +909,11 @@ static const char *const STRINGS_DE[186] = { "Die Papagei-Lobby", "Das Zimmer des Erschaffers", "Die Br\xFC""cke", - "Der Kielraum" + "Der Kielraum", "Das Skulpturenzimmer", "Der Baumgarten", "Der Grund des Brunnens", - "Das Promenadendeck" + "Das Promenadendeck", "Das First-Class-Restaurant", "Titanias Zimmer", "Die Bar", @@ -928,6 +937,16 @@ static const char *const STRINGS_DE[186] = { "Derzeitige Position: ", "Eine hei\xDF""e", "Eine kalte", + "Laden Sie das Spiel.", + "Speichern Sie das Spiel.", + "Leer", + "Beenden Sie das Spiel.", + "Sind Sie sicher, da\xDF"" Sie das Spiel verlassen m\XF6""chten?", + "\xC4""ndern der Lautst\xE4""rkeeinstellungen", + "Grundlautst\xE4""rke", + "Musiklautst\xE4""rke", + "Papageienlautst\xE4""rke", + "Sprachlautst\xE4""rke", "Sommer", "Herbst", @@ -1550,8 +1569,8 @@ void writeData() { writeStringArray("TEXT/ITEM_NAMES", ITEM_NAMES, 46); writeStringArray("TEXT/ITEM_IDS", ITEM_IDS, 40); writeStringArray("TEXT/ROOM_NAMES", ROOM_NAMES, 34); - writeStringArray("TEXT/STRINGS", STRINGS_EN, 141); - writeStringArray("TEXT/STRINGS/DE", STRINGS_DE, 186); + writeStringArray("TEXT/STRINGS", STRINGS_EN, 151); + writeStringArray("TEXT/STRINGS/DE", STRINGS_DE, 197); const int TEXT_PHRASES[3] = { 0x61D3C8, 0x618340, 0x61B1E0 }; const int TEXT_REPLACEMENTS1[3] = { 0x61D9B0, 0x61C788, 0x61B7C8 }; const int TEXT_REPLACEMENTS2[3] = { 0x61DD20, 0x61CAF8, 0x61BB38 }; diff --git a/dists/engine-data/titanic.dat b/dists/engine-data/titanic.dat Binary files differindex 2a9d60122b..2d5ea73117 100644 --- a/dists/engine-data/titanic.dat +++ b/dists/engine-data/titanic.dat diff --git a/engines/bladerunner/aesc.cpp b/engines/bladerunner/aesc.cpp new file mode 100644 index 0000000000..d653d1df35 --- /dev/null +++ b/engines/bladerunner/aesc.cpp @@ -0,0 +1,139 @@ +/* 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/aesc.h" + +#include "common/stream.h" + +namespace BladeRunner { + +AESC::AESC(BladeRunnerEngine *vm, int size) : _vm(vm) { + _dataSize = size; + _data = new uint8[size]; + _entries.reserve(8); +} + +AESC::~AESC() { + delete[] _data; +} + +void AESC::readVqa(Common::SeekableReadStream *stream) { + uint8* dataPtr = _data; + int dataSize = _dataSize; + + int entriesCount = stream->readUint32LE(); + + if (entriesCount == 0) { + return; + } + + entriesCount = MIN(entriesCount, 7); + _entries.resize(entriesCount); + + for (Common::Array<Entry>::iterator entry = _entries.begin(); entry != _entries.end(); entry++) { + stream->read(&entry->palette, sizeof(Color256) * 16); + + entry->x = stream->readUint16LE(); + entry->y = stream->readUint16LE(); + entry->width = stream->readUint16LE(); + entry->height = stream->readUint16LE(); + entry->z = stream->readUint16LE(); + + int entryDataSize = stream->readUint16LE(); + + int pixelCount = entry->width * entry->height; + + if (pixelCount > dataSize) { // to big to fit + entry->width = 0; + entry->height = 0; + entry->data = _data; + continue; + // there is a issue in the game code, because it's not skipping data of entry in this case + } + + int pos = stream->pos(); + dataSize -= pixelCount; + entry->data = dataPtr; + do { + uint8 count = stream->readByte(); + if (count & 0x80) { // repeat same data + uint8 colors = stream->readByte(); + for (uint8 j = 0; j < (count & 0x7F) + 1; j++) { + *(dataPtr++) = colors >> 4; // upper 4 bit + *(dataPtr++) = colors & 0xF; // lower 4 bit + pixelCount -= 2; + } + } else { // copy data + for (uint8 j = 0; j < count + 1; j++) { + uint8 colors = stream->readByte(); + *(dataPtr++) = colors >> 4; // upper 4 bit + *(dataPtr++) = colors & 0xF; // lower 4 bit + pixelCount -= 2; + } + } + } while (pixelCount > 0); + stream->seek(pos + entryDataSize, SEEK_SET); + } +} + +//TODO: +//bool AESC::isAffectingArea(int x, int y, int width, int height, int z) { +// int xx = x >> 1; +// int yy = y >> 1; +// if (_entries.empty()) { +// return false; +// } +// +// for(int i = 0; i < _entries.size(); i++) { +// Entry &entry = _entries[i]; +// if (entry.z < z) { +// if (entry.width < (width >> 1) + xx) { +// if (entry.width + entry.x > xx) { +// if (entry.height < (height >> 1) + yy) { +// if(entry.height + entry.y > yy) { +// return true; +// } +// } +// } +// } +// } +// } +// return false; +//} + +void AESC::getColor(Color256 *outColor, uint16 x, uint16 y, uint16 z) { + Color256 color = { 0, 0, 0 }; + for (Common::Array<Entry>::iterator entry = _entries.begin(); entry != _entries.end(); entry++) { + uint16 x1 = (x / 2) - entry->x; + uint16 y1 = (y / 2) - entry->y; + if ( x1 < entry->width && y1 < entry->height && z > entry->z) { + int colorIndex = entry->data[y1 * entry->width + x1]; + Color256 entryColor = entry->palette[colorIndex]; + color.r += entryColor.r; + color.g += entryColor.g; + color.b += entryColor.b; + } + } + *outColor = color; +} + +} // End of namespace BladeRunner diff --git a/engines/bladerunner/aesc.h b/engines/bladerunner/aesc.h new file mode 100644 index 0000000000..d3f926b190 --- /dev/null +++ b/engines/bladerunner/aesc.h @@ -0,0 +1,68 @@ +/* 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_AESC_H +#define BLADERUNNER_AESC_H + +#include "bladerunner/color.h" + +#include "common/array.h" + +namespace Common { +class ReadStream; +} + +namespace BladeRunner { +class BladeRunnerEngine; + +class AESC { +public: + struct Entry + { + Color256 palette[16]; + uint16 x; + uint16 y; + uint16 width; + uint16 height; + uint16 z; + uint8 *data; + }; + + BladeRunnerEngine *_vm; + + Common::Array<Entry> _entries; + uint8 *_data; + int _dataSize; + +public: + AESC(BladeRunnerEngine *vm, int size); + ~AESC(); + + void readVqa(Common::SeekableReadStream *stream); + void getColor(Color256 *outColor, uint16 x, uint16 y, uint16 z); + + //TODO + //bool isAffectingArea(int x, int y, int width, int height, int unk); +}; +} // End of namespace BladeRunner + +#endif diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp index 7fe3f9ed2e..121ad8128e 100644 --- a/engines/bladerunner/bladerunner.cpp +++ b/engines/bladerunner/bladerunner.cpp @@ -24,6 +24,7 @@ #include "bladerunner/actor.h" #include "bladerunner/adq.h" +#include "bladerunner/aesc.h" #include "bladerunner/ambient_sounds.h" #include "bladerunner/audio_mixer.h" #include "bladerunner/audio_player.h" @@ -43,6 +44,7 @@ #include "bladerunner/mouse.h" #include "bladerunner/outtake.h" #include "bladerunner/obstacles.h" +#include "bladerunner/overlays.h" #include "bladerunner/regions.h" #include "bladerunner/scene.h" #include "bladerunner/scene_objects.h" @@ -69,7 +71,6 @@ #include "graphics/pixelformat.h" - namespace BladeRunner { BladeRunnerEngine::BladeRunnerEngine(OSystem *syst) @@ -79,10 +80,13 @@ BladeRunnerEngine::BladeRunnerEngine(OSystem *syst) _gameIsRunning = true; _playerLosesControlCounter = 0; + //TODO(peterkohaut): move these to init + _crimesDatabase = nullptr; _sceneScript = new SceneScript(this); _settings = new Settings(this); _lights = new Lights(this); + _aesc = new AESC(this, 0x8000); _combat = new Combat(this); _adq = new ADQ(this); _obstacles = new Obstacles(this); @@ -122,6 +126,7 @@ BladeRunnerEngine::~BladeRunnerEngine() { delete _obstacles; delete _adq; delete _combat; + delete _aesc; delete _lights; delete _settings; delete _sceneScript; @@ -235,7 +240,8 @@ bool BladeRunnerEngine::startup(bool hasSavegames) { if (!openArchive("SPCHSFX.TLK")) return false; - // TODO: Video overlays + _overlays = new Overlays(this); + _overlays->init(); _zbuffer = new ZBuffer(); _zbuffer->init(640, 480); @@ -325,14 +331,17 @@ bool BladeRunnerEngine::startup(bool hasSavegames) { // TODO: Support cdframes r = _sliceAnimations->openHDFrames(); - if (!r) + if (!r) { return false; + } r = _sliceAnimations->openCoreAnim(); - if (!r) + if (!r) { return false; + } _sliceRenderer = new SliceRenderer(this); + _sliceRenderer->setAESC(_aesc); _crimesDatabase = new CrimesDatabase(this, "CLUES", _gameInfo->getClueCount()); @@ -430,7 +439,8 @@ void BladeRunnerEngine::shutdown() { delete _ambientSounds; - // TODO: Delete overlays + delete _overlays; + _overlays = nullptr; delete _audioSpeech; @@ -639,10 +649,11 @@ void BladeRunnerEngine::gameTick() { } (void)backgroundChanged; blit(_surfaceInterface, _surfaceGame); + // TODO: remove zbuffer draw // _surfaceGame.copyRectToSurface(_zbuffer->getData(), 1280, 0, 0, 640, 480); - // TODO: Render overlays + _overlays->tick(); if (!inDialogueMenu) { actorsUpdate(); @@ -703,14 +714,14 @@ void BladeRunnerEngine::gameTick() { switch (sceneObject->_sceneObjectType) { case SceneObjectTypeActor: color = 0b111110000000000; - drawBBox(a, b, _view, &_surface2, color); - _mainFont->drawColor(_textActorNames->getText(sceneObject->_sceneObjectId - SCENE_OBJECTS_ACTORS_OFFSET), _surface2, pos.x, pos.y, color); + drawBBox(a, b, _view, &_surfaceGame, color); + _mainFont->drawColor(_textActorNames->getText(sceneObject->_sceneObjectId - SCENE_OBJECTS_ACTORS_OFFSET), _surfaceGame, pos.x, pos.y, color); break; case SceneObjectTypeItem: char itemText[40]; - drawBBox(a, b, _view, &_surface2, color); + drawBBox(a, b, _view, &_surfaceGame, color); sprintf(itemText, "item %i", sceneObject->_sceneObjectId - SCENE_OBJECTS_ITEMS_OFFSET); - _mainFont->drawColor(itemText, _surface2, pos.x, pos.y, color); + _mainFont->drawColor(itemText, _surfaceGame, pos.x, pos.y, color); break; case SceneObjectTypeObject: color = 0b011110111101111; @@ -719,11 +730,11 @@ void BladeRunnerEngine::gameTick() { if (sceneObject->_isClickable) { color = 0b000001111100000; } - drawBBox(a, b, _view, &_surface2, color); - _mainFont->drawColor(_scene->objectGetName(sceneObject->_sceneObjectId - SCENE_OBJECTS_OBJECTS_OFFSET), _surface2, pos.x, pos.y, color); + drawBBox(a, b, _view, &_surfaceGame, color); + _mainFont->drawColor(_scene->objectGetName(sceneObject->_sceneObjectId - SCENE_OBJECTS_OBJECTS_OFFSET), _surfaceGame, pos.x, pos.y, color); break; } - _surface2.frameRect(sceneObject->_screenRectangle, color); + _surfaceGame.frameRect(sceneObject->_screenRectangle, color); } } @@ -731,16 +742,15 @@ void BladeRunnerEngine::gameTick() { for (int i = 0; i < 10; i++) { Region *region = &_scene->_regions->_regions[i]; if (!region->_present) continue; - _surface2.frameRect(region->_rectangle, 0b000000000011111); + _surfaceGame.frameRect(region->_rectangle, 0b000000000011111); } for (int i = 0; i < 10; i++) { Region *region = &_scene->_exits->_regions[i]; if (!region->_present) continue; - _surface2.frameRect(region->_rectangle, 0b111111111111111); + _surfaceGame.frameRect(region->_rectangle, 0b111111111111111); } - //draw walkboxes for (int i = 0; i < _scene->_set->_walkboxCount; i++) { Walkbox *walkbox = &_scene->_set->_walkboxes[i]; @@ -748,9 +758,9 @@ void BladeRunnerEngine::gameTick() { for (int j = 0; j < walkbox->_vertexCount; j++) { Vector3 start = _view->calculateScreenPosition(walkbox->_vertices[j]); Vector3 end = _view->calculateScreenPosition(walkbox->_vertices[(j + 1) % walkbox->_vertexCount]); - _surface2.drawLine(start.x, start.y, end.x, end.y, 0b111111111100000); + _surfaceGame.drawLine(start.x, start.y, end.x, end.y, 0b111111111100000); Vector3 pos = _view->calculateScreenPosition(0.5 * (start + end)); - _mainFont->drawColor(walkbox->_name, _surface2, pos.x, pos.y, 0b111111111100000); + _mainFont->drawColor(walkbox->_name, _surfaceGame, pos.x, pos.y, 0b111111111100000); } } @@ -776,12 +786,12 @@ void BladeRunnerEngine::gameTick() { int colorB = (light->_color.b * 31.0f); int color = (colorR << 10) + (colorG << 5) + colorB; - drawBBox(posOrigin - size, posOrigin + size, _view, &_surface2, color); + drawBBox(posOrigin - size, posOrigin + size, _view, &_surfaceGame, color); Vector3 posOriginT = _view->calculateScreenPosition(posOrigin); Vector3 posTargetT = _view->calculateScreenPosition(posTarget); - _surface2.drawLine(posOriginT.x, posOriginT.y, posTargetT.x, posTargetT.y, color); - _mainFont->drawColor(light->_name, _surface2, posOriginT.x, posOriginT.y, color); + _surfaceGame.drawLine(posOriginT.x, posOriginT.y, posTargetT.x, posTargetT.y, color); + _mainFont->drawColor(light->_name, _surfaceGame, posOriginT.x, posOriginT.y, color); } //draw waypoints @@ -792,11 +802,34 @@ void BladeRunnerEngine::gameTick() { Vector3 pos = waypoint->_position; Vector3 size = Vector3(5.0f, 5.0f, 5.0f); int color = 0b111111111111111; - drawBBox(pos - size, pos + size, _view, &_surface2, color); + drawBBox(pos - size, pos + size, _view, &_surfaceGame, color); Vector3 spos = _view->calculateScreenPosition(pos); char waypointText[40]; sprintf(waypointText, "waypoint %i", i); - _mainFont->drawColor(waypointText, _surface2, spos.x, spos.y, color); + _mainFont->drawColor(waypointText, _surfaceGame, spos.x, spos.y, color); + } +#endif +#if 0 + //draw aesc + for (uint i = 0; i < _aesc->_entries.size(); i++) { + AESC::Entry &entry = _aesc->_entries[i]; + int j = 0; + for (int y = 0; y < entry.height; y++) { + for (int x = 0; x < entry.width; x++) { + Common::Rect r((entry.x + x) * 2, (entry.y + y) * 2, (entry.x + x) * 2 + 2, (entry.y + y) * 2 + 2); + + int ec = entry.data[j++]; + Color256 color = entry.palette[ec]; + int bladeToScummVmConstant = 256 / 16; + + Graphics::PixelFormat _pixelFormat = createRGB555(); + int color555 = _pixelFormat.RGBToColor( + CLIP(color.r * bladeToScummVmConstant, 0, 255), + CLIP(color.g * bladeToScummVmConstant, 0, 255), + CLIP(color.b * bladeToScummVmConstant, 0, 255)); + _surfaceGame.fillRect(r, color555); + } + } } #endif diff --git a/engines/bladerunner/bladerunner.h b/engines/bladerunner/bladerunner.h index 5164f353dd..066937ca38 100644 --- a/engines/bladerunner/bladerunner.h +++ b/engines/bladerunner/bladerunner.h @@ -50,6 +50,7 @@ enum AnimationModes { class Actor; class ADQ; +class AESC; class AIScripts; class AmbientSounds; class AudioMixer; @@ -68,6 +69,7 @@ class Items; class Lights; class Mouse; class Obstacles; +class Overlays; class Scene; class SceneObjects; class SceneScript; @@ -92,6 +94,7 @@ public: int _playerLosesControlCounter; ADQ *_adq; + AESC *_aesc; AIScripts *_aiScripts; AmbientSounds *_ambientSounds; AudioMixer *_audioMixer; @@ -110,6 +113,7 @@ public: Font *_mainFont; Mouse *_mouse; Obstacles *_obstacles; + Overlays *_overlays; Scene *_scene; SceneObjects *_sceneObjects; SceneScript *_sceneScript; diff --git a/engines/bladerunner/module.mk b/engines/bladerunner/module.mk index 61a95352bf..711ddd3346 100644 --- a/engines/bladerunner/module.mk +++ b/engines/bladerunner/module.mk @@ -2,6 +2,7 @@ MODULE := engines/bladerunner MODULE_OBJS = \ adq.o \ + aesc.o \ actor.o \ actor_clues.o \ actor_combat.o \ @@ -38,6 +39,7 @@ MODULE_OBJS = \ movement_track.o \ obstacles.o \ outtake.o \ + overlays.o \ regions.o \ scene.o \ scene_objects.o \ diff --git a/engines/bladerunner/overlays.cpp b/engines/bladerunner/overlays.cpp new file mode 100644 index 0000000000..946cbe266d --- /dev/null +++ b/engines/bladerunner/overlays.cpp @@ -0,0 +1,146 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "bladerunner/overlays.h" + +#include "bladerunner/bladerunner.h" + +#include "bladerunner/archive.h" +#include "bladerunner/vqa_player.h" + +#include "graphics/surface.h" + +namespace BladeRunner { + +Overlays::Overlays(BladeRunnerEngine *vm) + : _vm(vm) +{ +} + +bool Overlays::init() { + reset(); + _videos.resize(kOverlayVideos); + + for (int i = 0; i < kOverlayVideos; ++i) { + _videos[i].vqaPlayer = nullptr; + resetSingle(i); + } + + return true; +} + +Overlays::~Overlays() { + for (int i = 0; i < kOverlayVideos; ++i) { + resetSingle(i); + } + _videos.clear(); + reset(); +} + +int Overlays::play(const Common::String &name, int loopId, int loopForever, int a5, int a6) { + int id = mix_id(name); + int index = findById(id); + if (index < 0) { + index = findEmpty(); + if (index < 0) { + return index; + } + _videos[index].id = id; + _videos[index].vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceGame); + + // repeat forever + _videos[index].vqaPlayer->setBeginAndEndFrame(0, 0, -1, kLoopSetModeJustStart, nullptr, nullptr); + _videos[index].loaded = true; + } + + Common::String resourceName = Common::String::format("%s.VQA", name.c_str()); + _videos[index].vqaPlayer->open(resourceName); + _videos[index].vqaPlayer->setLoop( + loopId, + loopForever ? -1 : 0, + a5 ? kLoopSetModeImmediate : kLoopSetModeEnqueue, + nullptr, nullptr); + + return index; +} + +void Overlays::remove(const Common::String &name) { + int id = mix_id(name); + int index = findById(id); + if (index >= 0) { + resetSingle(index); + } +} + +void Overlays::tick() { + for (int i = 0; i < kOverlayVideos; ++i) { + if (_videos[i].loaded) { + int frame = _videos[i].vqaPlayer->update(true); + if (frame < 0) { + resetSingle(i); + } + } + } +} + +int Overlays::findById(int id) const { + for (int i = 0; i < kOverlayVideos; ++i) { + if (_videos[i].loaded && _videos[i].id == id) { + return i; + } + } + return -1; +} + +int Overlays::findEmpty() const { + for (int i = 0; i < kOverlayVideos; ++i) { + if (!_videos[i].loaded) { + return i; + } + } + return -1; +} + +void Overlays::resetSingle(int i) { + assert(i >= 0 && i < (int)_videos.size()); + if (_videos[i].vqaPlayer) { + delete _videos[i].vqaPlayer; + _videos[i].vqaPlayer = nullptr; + } + _videos[i].loaded = false; + _videos[i].id = 0; + _videos[i].field2 = -1; +} + +void Overlays::resetAll() { + for (int i = 0; i < kOverlayVideos; ++i) { + if (_videos[i].loaded) { + resetSingle(i); + } + } +} + +void Overlays::reset() { + _videos.clear(); +} + +} // End of namespace BladeRunner diff --git a/engines/bladerunner/overlays.h b/engines/bladerunner/overlays.h new file mode 100644 index 0000000000..9be515e036 --- /dev/null +++ b/engines/bladerunner/overlays.h @@ -0,0 +1,74 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef BLADERUNNER_OVERLAYS_H +#define BLADERUNNER_OVERLAYS_H + +#include "common/array.h" +#include "common/str.h" + +namespace Graphics { + struct Surface; +} + +namespace BladeRunner { + +class BladeRunnerEngine; +class VQAPlayer; + +struct OverlayVideo { + bool loaded; + VQAPlayer *vqaPlayer; + // char name[13]; + int32 id; + int field0; + int field1; + int field2; +}; + +class Overlays { + static const int kOverlayVideos = 5; + + BladeRunnerEngine *_vm; + Common::Array<OverlayVideo> _videos; + +public: + Overlays(BladeRunnerEngine *vm); + bool init(); + ~Overlays(); + + int play(const Common::String &name, int a3, int a4, int a5, int a6); + void remove(const Common::String &name); + void tick(); + +private: + int findById(int32 id) const; + int findEmpty() const; + + void resetSingle(int i); + void resetAll(); + void reset(); +}; + +} // End of namespace BladeRunner + +#endif diff --git a/engines/bladerunner/scene.cpp b/engines/bladerunner/scene.cpp index 54bc97abe4..de3d89291b 100644 --- a/engines/bladerunner/scene.cpp +++ b/engines/bladerunner/scene.cpp @@ -210,6 +210,7 @@ int Scene::advanceFrame() { blit(_vm->_surfaceInterface, _vm->_surfaceGame); _vqaPlayer->updateZBuffer(_vm->_zbuffer); _vqaPlayer->updateView(_vm->_view); + _vqaPlayer->updateAESC(_vm->_aesc); _vqaPlayer->updateLights(_vm->_lights); } if (_specialLoopMode && _specialLoopMode != kSceneLoopMode2 && _specialLoopMode != kSceneLoopModeSpinner) { diff --git a/engines/bladerunner/script/scene/ct01.cpp b/engines/bladerunner/script/scene/ct01.cpp index 5a2c62cfbb..21e6fc7600 100644 --- a/engines/bladerunner/script/scene/ct01.cpp +++ b/engines/bladerunner/script/scene/ct01.cpp @@ -366,7 +366,6 @@ void SceneScriptCT01::SceneFrameAdvanced(int frame) { } else { Ambient_Sounds_Play_Sound(66, Random_Query(33, 50), 0, 0, 0); } - } } } diff --git a/engines/bladerunner/script/script.cpp b/engines/bladerunner/script/script.cpp index 72f4e50a53..25d78e4991 100644 --- a/engines/bladerunner/script/script.cpp +++ b/engines/bladerunner/script/script.cpp @@ -38,6 +38,7 @@ #include "bladerunner/items.h" #include "bladerunner/item_pickup.h" #include "bladerunner/movement_track.h" +#include "bladerunner/overlays.h" #include "bladerunner/regions.h" #include "bladerunner/set.h" #include "bladerunner/settings.h" @@ -818,13 +819,11 @@ bool ScriptBase::Music_Is_Playing() { } void ScriptBase::Overlay_Play(const char *overlay, int a2, int a3, int a4, int a5) { - //TODO - warning("Overlay_Play(%s, %d, %d, %d, %d)", overlay, a2, a3, a4, a5); + _vm->_overlays->play(overlay, a2, a3, a4, a5); } void ScriptBase::Overlay_Remove(const char *overlay) { - //TODO - warning("Overlay_Remove(%s)", overlay); + _vm->_overlays->remove(overlay); } void ScriptBase::Scene_Loop_Set_Default(int loopId) { diff --git a/engines/bladerunner/slice_renderer.cpp b/engines/bladerunner/slice_renderer.cpp index e0845eeba3..34c910742c 100644 --- a/engines/bladerunner/slice_renderer.cpp +++ b/engines/bladerunner/slice_renderer.cpp @@ -22,6 +22,7 @@ #include "bladerunner/slice_renderer.h" +#include "bladerunner/aesc.h" #include "bladerunner/bladerunner.h" #include "bladerunner/lights.h" #include "bladerunner/set_effects.h" @@ -46,6 +47,10 @@ SliceRenderer::SliceRenderer(BladeRunnerEngine *vm) { SliceRenderer::~SliceRenderer() { } +void SliceRenderer::setAESC(AESC *aesc) { + _aesc = aesc; +} + void SliceRenderer::setView(const View &view) { _view = view; } @@ -349,6 +354,8 @@ void SliceRenderer::drawInWorld(int animationId, int animationFrame, Vector3 pos _modelMatrix ); + Vector3 cameraPosition(_view._cameraPosition.x, _view._cameraPosition.z, _view._cameraPosition.y); // not a bug + SliceRendererLights sliceRendererLights = SliceRendererLights(_lights); _lights->setupFrame(_view._frame); @@ -364,7 +371,7 @@ void SliceRenderer::drawInWorld(int animationId, int animationFrame, Vector3 pos float setEffectsColorCoeficient; Color setEffectColor; _setEffects->calculateColor( - _view._cameraPosition, + cameraPosition, Vector3(_position.x, _position.y, _position.z + _frameBottomZ + sliceLine * _frameSliceHeight), &setEffectsColorCoeficient, &setEffectColor); @@ -401,7 +408,7 @@ void SliceRenderer::drawInWorld(int animationId, int animationFrame, Vector3 pos if (sliceLineIterator._currentY & 1) { _setEffects->calculateColor( - _view._cameraPosition, + cameraPosition, Vector3(_position.x, _position.y, _position.z + _frameBottomZ + sliceLine * _frameSliceHeight), &setEffectsColorCoeficient, &setEffectColor); @@ -416,7 +423,7 @@ void SliceRenderer::drawInWorld(int animationId, int animationFrame, Vector3 pos _setEffectColor.b = setEffectColor.b * 31.0f * 65536.0f; if (frameY >= 0 && frameY < 480) { - drawSlice((int)sliceLine, true, frameLinePtr, zBufferLinePtr); + drawSlice((int)sliceLine, true, frameLinePtr, zBufferLinePtr, frameY); } sliceLineIterator.advance(); @@ -480,7 +487,7 @@ void SliceRenderer::drawOnScreen(int animationId, int animationFrame, int screen while (currentSlice < _frameSliceCount) { if (currentY >= 0 && currentY < 480) { memset(lineZbuffer, 0xFF, 640 * 2); - drawSlice(currentSlice, false, frameLinePtr, lineZbuffer); + drawSlice(currentSlice, false, frameLinePtr, lineZbuffer, currentY); currentSlice += sliceStep; currentY--; frameLinePtr -= 640; @@ -488,7 +495,7 @@ void SliceRenderer::drawOnScreen(int animationId, int animationFrame, int screen } } -void SliceRenderer::drawSlice(int slice, bool advanced, uint16 *frameLinePtr, uint16 *zbufLinePtr) { +void SliceRenderer::drawSlice(int slice, bool advanced, uint16 *frameLinePtr, uint16 *zbufLinePtr, int y) { if (slice < 0 || (uint32)slice >= _frameSliceCount) return; @@ -523,14 +530,15 @@ void SliceRenderer::drawSlice(int slice, bool advanced, uint16 *frameLinePtr, ui if (vertexZ >= 0 && vertexZ < 65536) { int color555 = palette.color555[p[2]]; if (advanced) { - Color256 color = palette.color[p[2]]; + Color256 aescColor = { 0, 0, 0 }; + _aesc->getColor(&aescColor, vertexX, y, vertexZ); - color.r = (int)(_setEffectColor.r + _lightsColor.r * color.r) >> 16; - color.g = (int)(_setEffectColor.g + _lightsColor.g * color.g) >> 16; - color.b = (int)(_setEffectColor.b + _lightsColor.b * color.b) >> 16; + Color256 color = palette.color[p[2]]; + color.r = ((int)(_setEffectColor.r + _lightsColor.r * color.r) >> 16) + aescColor.r; + color.g = ((int)(_setEffectColor.g + _lightsColor.g * color.g) >> 16) + aescColor.g; + color.b = ((int)(_setEffectColor.b + _lightsColor.b * color.b) >> 16) + aescColor.b; int bladeToScummVmConstant = 256 / 32; - color555 = _pixelFormat.RGBToColor(CLIP(color.r * bladeToScummVmConstant, 0, 255), CLIP(color.g * bladeToScummVmConstant, 0, 255), CLIP(color.b * bladeToScummVmConstant, 0, 255)); } for (int x = previousVertexX; x != vertexX; ++x) { diff --git a/engines/bladerunner/slice_renderer.h b/engines/bladerunner/slice_renderer.h index 1a876de543..fbdcdf3617 100644 --- a/engines/bladerunner/slice_renderer.h +++ b/engines/bladerunner/slice_renderer.h @@ -38,6 +38,7 @@ class MemoryReadStream; namespace BladeRunner { +class AESC; class BladeRunnerEngine; class Lights; class SetEffects; @@ -51,6 +52,7 @@ class SliceRenderer { float _facing; float _scale; + AESC *_aesc; View _view; Lights *_lights; SetEffects *_setEffects; @@ -87,12 +89,13 @@ class SliceRenderer { Graphics::PixelFormat _pixelFormat; Matrix3x2 calculateFacingRotationMatrix(); - void drawSlice(int slice, bool advanced, uint16 *frameLinePtr, uint16 *zbufLinePtr); + void drawSlice(int slice, bool advanced, uint16 *frameLinePtr, uint16 *zbufLinePtr, int y); public: SliceRenderer(BladeRunnerEngine *vm); ~SliceRenderer(); + void setAESC(AESC *aesc); void setView(const View &view); void setLights(Lights *lights); void setSetEffects(SetEffects *setEffects); diff --git a/engines/bladerunner/view.cpp b/engines/bladerunner/view.cpp index ed5cef9e3d..11cd99c9fc 100644 --- a/engines/bladerunner/view.cpp +++ b/engines/bladerunner/view.cpp @@ -27,7 +27,7 @@ namespace BladeRunner { -bool View::read(Common::ReadStream *stream) { +bool View::readVqa(Common::ReadStream *stream) { _frame = stream->readUint32LE(); float d[12]; diff --git a/engines/bladerunner/view.h b/engines/bladerunner/view.h index eb68aa9eb6..9d53d0852f 100644 --- a/engines/bladerunner/view.h +++ b/engines/bladerunner/view.h @@ -44,7 +44,7 @@ public: float _viewportHalfHeight; float _viewportDistance; - bool read(Common::ReadStream *stream); + bool readVqa(Common::ReadStream *stream); Vector3 calculateScreenPosition(Vector3 worldPosition); private: diff --git a/engines/bladerunner/vqa_decoder.cpp b/engines/bladerunner/vqa_decoder.cpp index 377251714b..53751afcd6 100644 --- a/engines/bladerunner/vqa_decoder.cpp +++ b/engines/bladerunner/vqa_decoder.cpp @@ -22,6 +22,7 @@ #include "bladerunner/vqa_decoder.h" +#include "bladerunner/aesc.h" #include "bladerunner/bladerunner.h" #include "bladerunner/decompress_lcw.h" #include "bladerunner/decompress_lzo.h" @@ -127,6 +128,9 @@ VQADecoder::VQADecoder(Graphics::Surface *surface) : } VQADecoder::~VQADecoder() { + for (uint i = 0; i < _codebooks.size(); ++i) { + delete[] _codebooks[i].data; + } delete _audioTrack; delete _videoTrack; delete[] _frameInfo; @@ -190,8 +194,9 @@ bool VQADecoder::loadStream(Common::SeekableReadStream *s) { return true; } -void VQADecoder::decodeVideoFrame() { - _videoTrack->decodeVideoFrame(); +void VQADecoder::decodeVideoFrame(int frame, bool forceDraw) { + _decodingFrame = frame; + _videoTrack->decodeVideoFrame(forceDraw); } void VQADecoder::decodeZBuffer(ZBuffer *zbuffer) { @@ -206,11 +211,15 @@ void VQADecoder::decodeView(View *view) { _videoTrack->decodeView(view); } +void VQADecoder::decodeAESC(AESC *aesc) { + _videoTrack->decodeAESC(aesc); +} + void VQADecoder::decodeLights(Lights *lights) { _videoTrack->decodeLights(lights); } -void VQADecoder::readPacket(int skipFlags) { +void VQADecoder::readPacket(uint readFlags) { IFFChunkHeader chd; if (remain(_s) < 8) { @@ -227,15 +236,15 @@ void VQADecoder::readPacket(int skipFlags) { bool rc = false; // Video track switch (chd.id) { - case kAESC: rc = skipFlags & 1 ? _s->skip(roundup(chd.size)) : _videoTrack->readAESC(_s, chd.size); break; - case kLITE: rc = skipFlags & 1 ? _s->skip(roundup(chd.size)) : _videoTrack->readLITE(_s, chd.size); break; - case kVIEW: rc = skipFlags & 1 ? _s->skip(roundup(chd.size)) : _videoTrack->readVIEW(_s, chd.size); break; - case kVQFL: rc = skipFlags & 1 ? _s->skip(roundup(chd.size)) : _videoTrack->readVQFL(_s, chd.size); break; - case kVQFR: rc = skipFlags & 1 ? _s->skip(roundup(chd.size)) : _videoTrack->readVQFR(_s, chd.size); break; - case kZBUF: rc = skipFlags & 1 ? _s->skip(roundup(chd.size)) : _videoTrack->readZBUF(_s, chd.size); break; + case kAESC: rc = ((readFlags & kVQAReadCustom) == 0) ? _s->skip(roundup(chd.size)) : _videoTrack->readAESC(_s, chd.size); break; + case kLITE: rc = ((readFlags & kVQAReadCustom) == 0) ? _s->skip(roundup(chd.size)) : _videoTrack->readLITE(_s, chd.size); break; + case kVIEW: rc = ((readFlags & kVQAReadCustom) == 0) ? _s->skip(roundup(chd.size)) : _videoTrack->readVIEW(_s, chd.size); break; + case kVQFL: rc = ((readFlags & kVQAReadVideo ) == 0) ? _s->skip(roundup(chd.size)) : _videoTrack->readVQFL(_s, chd.size, readFlags); break; + case kVQFR: rc = ((readFlags & kVQAReadVideo ) == 0) ? _s->skip(roundup(chd.size)) : _videoTrack->readVQFR(_s, chd.size, readFlags); break; + case kZBUF: rc = ((readFlags & kVQAReadCustom) == 0) ? _s->skip(roundup(chd.size)) : _videoTrack->readZBUF(_s, chd.size); break; // Sound track - case kSN2J: rc = skipFlags & 2 ? _s->skip(roundup(chd.size)) : _audioTrack->readSN2J(_s, chd.size); break; - case kSND2: rc = skipFlags & 2 ? _s->skip(roundup(chd.size)) : _audioTrack->readSND2(_s, chd.size); break; + case kSN2J: rc = ((readFlags & kVQAReadAudio) == 0) ? _s->skip(roundup(chd.size)) : _audioTrack->readSN2J(_s, chd.size); break; + case kSND2: rc = ((readFlags & kVQAReadAudio) == 0) ? _s->skip(roundup(chd.size)) : _audioTrack->readSND2(_s, chd.size); break; default: rc = false; _s->skip(roundup(chd.size)); @@ -248,14 +257,16 @@ void VQADecoder::readPacket(int skipFlags) { } while (chd.id != kVQFR); } -void VQADecoder::readFrame(int frame, int skipFlags) { +void VQADecoder::readFrame(int frame, uint readFlags) { if (frame < 0 || frame >= numFrames()) { error("frame %d out of bounds, frame count is %d", frame, numFrames()); } uint32 frameOffset = 2 * (_frameInfo[frame] & 0x0FFFFFFF); _s->seek(frameOffset); - readPacket(skipFlags); + + _readingFrame = frame; + readPacket(readFlags); } bool VQADecoder::readVQHD(Common::SeekableReadStream *s, uint32 size) { @@ -284,10 +295,6 @@ bool VQADecoder::readVQHD(Common::SeekableReadStream *s, uint32 size) { _header.maxCBFZSize = s->readUint32LE(); _header.unk5 = s->readUint32LE(); - if (_header.offsetX || _header.offsetY) { - debug("_header.offsetX, _header.offsetY: %d %d", _header.offsetX, _header.offsetY); - } - // if (_header.unk3 || _header.unk4 != 4 || _header.unk5 || _header.flags != 0x0014) if (false) { debug("_header.version %d", _header.version); @@ -325,7 +332,7 @@ bool VQADecoder::readVQHD(Common::SeekableReadStream *s, uint32 size) { return true; } -bool VQADecoder::VQAVideoTrack::readVQFR(Common::SeekableReadStream *s, uint32 size) { +bool VQADecoder::VQAVideoTrack::readVQFR(Common::SeekableReadStream *s, uint32 size, uint readFlags) { IFFChunkHeader chd; while (size >= 8) { @@ -335,8 +342,8 @@ bool VQADecoder::VQAVideoTrack::readVQFR(Common::SeekableReadStream *s, uint32 s bool rc = false; switch (chd.id) { - case kCBFZ: rc = readCBFZ(s, chd.size); break; - case kVPTR: rc = readVPTR(s, chd.size); break; + case kCBFZ: rc = ((readFlags & kVQAReadCodebook ) == 0) ? s->skip(roundup(chd.size)) : readCBFZ(s, chd.size); break; + case kVPTR: rc = ((readFlags & kVQAReadVectorPointerTable) == 0) ? s->skip(roundup(chd.size)) : readVPTR(s, chd.size); break; default: s->skip(roundup(chd.size)); } @@ -421,6 +428,22 @@ bool VQADecoder::readLINF(Common::SeekableReadStream *s, uint32 size) { return true; } +VQADecoder::CodebookInfo &VQADecoder::codebookInfoForFrame(int frame) { + assert(frame < numFrames()); + assert(!_codebooks.empty()); + + CodebookInfo *ci = nullptr; + uint count = _codebooks.size(); + for (uint i = 0; i != count; ++i) { + if (frame >= _codebooks[count - i - 1].frame) { + return _codebooks[count - i - 1]; + } + } + + assert(ci && "No codebook found"); + return _codebooks[0]; +} + bool VQADecoder::readCINF(Common::SeekableReadStream *s, uint32 size) { IFFChunkHeader chd; @@ -428,17 +451,23 @@ bool VQADecoder::readCINF(Common::SeekableReadStream *s, uint32 size) { if (chd.id != kCINH || chd.size != 8u) return false; - _clipInfo.clipCount = s->readUint16LE(); + uint16 codebookCount = s->readUint16LE(); + _codebooks.resize(codebookCount); + s->skip(6); readIFFChunkHeader(_s, &chd); - if (chd.id != kCIND || chd.size != 6u * _clipInfo.clipCount) + if (chd.id != kCIND || chd.size != 6u * codebookCount) return false; - for (int i = 0; i != _clipInfo.clipCount; ++i) { - uint16 a = s->readUint16LE(); - uint32 b = s->readUint32LE(); - debug("VQADecoder::readCINF() i: %d a: 0x%04x b: 0x%08x", i, a, b); + for (int i = 0; i != codebookCount; ++i) { + _codebooks[i].frame = s->readUint16LE(); + _codebooks[i].size = s->readUint32LE(); + _codebooks[i].data = nullptr; + + // debug("Codebook %2d: %4d %8d", i, _codebooks[i].frame, _codebooks[i].size); + + assert(_codebooks[i].frame < numFrames()); } return true; @@ -538,11 +567,11 @@ bool VQADecoder::readMFCI(Common::SeekableReadStream *s, uint32 size) { } VQADecoder::VQAVideoTrack::VQAVideoTrack(VQADecoder *vqaDecoder, Graphics::Surface *surface) { - VQADecoder::Header *header = &vqaDecoder->_header; - + _vqaDecoder = vqaDecoder; _surface = surface; _hasNewFrame = false; + VQADecoder::Header *header = &vqaDecoder->_header; _numFrames = header->numFrames; _width = header->width; _height = header->height; @@ -557,7 +586,6 @@ VQADecoder::VQAVideoTrack::VQAVideoTrack(VQADecoder *vqaDecoder, Graphics::Surfa _maxCBFZSize = header->maxCBFZSize; _maxZBUFChunkSize = vqaDecoder->_maxZBUFChunkSize; - _codebookSize = 0; _codebook = nullptr; _cbfz = nullptr; _zbufChunk = nullptr; @@ -570,19 +598,18 @@ VQADecoder::VQAVideoTrack::VQAVideoTrack(VQADecoder *vqaDecoder, Graphics::Surfa _zbufChunk = new uint8[roundup(_maxZBUFChunkSize)]; _viewData = nullptr; + _aescData = nullptr; _lightsData = nullptr; } VQADecoder::VQAVideoTrack::~VQAVideoTrack() { - delete[] _codebook; delete[] _cbfz; delete[] _zbufChunk; delete[] _vpointer; - if (_viewData) - delete[] _viewData; - if (_lightsData) - delete[] _lightsData; + delete[] _viewData; + delete[] _aescData; + delete[] _lightsData; } uint16 VQADecoder::VQAVideoTrack::getWidth() const { @@ -593,10 +620,6 @@ uint16 VQADecoder::VQAVideoTrack::getHeight() const { return _height; } -int VQADecoder::VQAVideoTrack::getCurFrame() const { - return _curFrame; -} - int VQADecoder::VQAVideoTrack::getFrameCount() const { return _numFrames; } @@ -605,15 +628,14 @@ Common::Rational VQADecoder::VQAVideoTrack::getFrameRate() const { return _frameRate; } -void VQADecoder::VQAVideoTrack::decodeVideoFrame() { - if (_hasNewFrame) { +void VQADecoder::VQAVideoTrack::decodeVideoFrame(bool forceDraw) { + if (_hasNewFrame || forceDraw) { decodeFrame((uint16*)_surface->getPixels()); - _curFrame++; _hasNewFrame = false; } } -bool VQADecoder::VQAVideoTrack::readVQFL(Common::SeekableReadStream *s, uint32 size) { +bool VQADecoder::VQAVideoTrack::readVQFL(Common::SeekableReadStream *s, uint32 size, uint readFlags) { IFFChunkHeader chd; while (size >= 8) { @@ -643,16 +665,22 @@ bool VQADecoder::VQAVideoTrack::readCBFZ(Common::SeekableReadStream *s, uint32 s return false; } - if (!_codebook) { - _codebookSize = 2 * _maxBlocks * _blockW * _blockH; - _codebook = new uint8[_codebookSize]; + CodebookInfo &codebookInfo = _vqaDecoder->codebookInfoForFrame(_vqaDecoder->_readingFrame); + if (codebookInfo.data) { + s->skip(roundup(size)); + return true; } - if (!_cbfz) + + uint32 codebookSize = 2 * _maxBlocks * _blockW * _blockH; + codebookInfo.data = new uint8[codebookSize]; + + if (!_cbfz) { _cbfz = new uint8[roundup(_maxCBFZSize)]; + } s->read(_cbfz, roundup(size)); - decompress_lcw(_cbfz, size, _codebook, _codebookSize); + decompress_lcw(_cbfz, size, codebookInfo.data, codebookSize); return true; } @@ -671,22 +699,23 @@ bool VQADecoder::VQAVideoTrack::readZBUF(Common::SeekableReadStream *s, uint32 s } void VQADecoder::VQAVideoTrack::decodeZBuffer(ZBuffer *zbuffer) { - if (_maxZBUFChunkSize == 0) + if (_maxZBUFChunkSize == 0) { return; + } zbuffer->decodeData(_zbufChunk, _zbufChunkSize); } bool VQADecoder::VQAVideoTrack::readVIEW(Common::SeekableReadStream *s, uint32 size) { - if (size != 56) + if (size != 56) { return false; + } if (_viewData) { delete[] _viewData; - _viewData = nullptr; } - _viewDataSize = size; + _viewDataSize = roundup(size); _viewData = new uint8[_viewDataSize]; s->read(_viewData, _viewDataSize); @@ -694,30 +723,47 @@ bool VQADecoder::VQAVideoTrack::readVIEW(Common::SeekableReadStream *s, uint32 s } void VQADecoder::VQAVideoTrack::decodeView(View *view) { - if (!view || !_viewData) + if (!view || !_viewData) { return; + } Common::MemoryReadStream s(_viewData, _viewDataSize); - view->read(&s); + view->readVqa(&s); delete[] _viewData; _viewData = nullptr; } bool VQADecoder::VQAVideoTrack::readAESC(Common::SeekableReadStream *s, uint32 size) { - debug("VQADecoder::readAESC(%d)", size); + if (_aescData) { + delete[] _aescData; + } + + _aescDataSize = roundup(size); + _aescData = new uint8[_aescDataSize]; + s->read(_aescData, _aescDataSize); - s->skip(roundup(size)); return true; } +void VQADecoder::VQAVideoTrack::decodeAESC(AESC *aesc) { + if (!aesc || !_aescData) { + return; + } + + Common::MemoryReadStream s(_aescData, _aescDataSize); + aesc->readVqa(&s); + + delete[] _aescData; + _aescData = nullptr; +} + bool VQADecoder::VQAVideoTrack::readLITE(Common::SeekableReadStream *s, uint32 size) { if (_lightsData) { delete[] _lightsData; - _lightsData = nullptr; } - _lightsDataSize = size; + _lightsDataSize = roundup(size); _lightsData = new uint8[_lightsDataSize]; s->read(_lightsData, _lightsDataSize); @@ -726,8 +772,9 @@ bool VQADecoder::VQAVideoTrack::readLITE(Common::SeekableReadStream *s, uint32 s void VQADecoder::VQAVideoTrack::decodeLights(Lights *lights) { - if (!lights || !_lightsData) + if (!lights || !_lightsData) { return; + } Common::MemoryReadStream s(_lightsData, _lightsDataSize); lights->readVqa(&s); @@ -741,8 +788,9 @@ bool VQADecoder::VQAVideoTrack::readVPTR(Common::SeekableReadStream *s, uint32 s if (size > _maxVPTRSize) return false; - if (!_vpointer) + if (!_vpointer) { _vpointer = new uint8[roundup(_maxVPTRSize)]; + } _vpointerSize = size; s->read(_vpointer, roundup(size)); @@ -764,7 +812,7 @@ void VQADecoder::VQAVideoTrack::VPTRWriteBlock(uint16 *frame, unsigned int dstBl int blocks_per_line = frame_width / block_width; do { - uint32 frame_x = dstBlock % blocks_per_line * block_width + _offsetX / 2; + uint32 frame_x = dstBlock % blocks_per_line * block_width + _offsetX; uint32 frame_y = dstBlock / blocks_per_line * block_height + _offsetY; uint32 dst_offset = frame_x + frame_y * frame_stride; @@ -791,6 +839,13 @@ void VQADecoder::VQAVideoTrack::VPTRWriteBlock(uint16 *frame, unsigned int dstBl } bool VQADecoder::VQAVideoTrack::decodeFrame(uint16 *frame) { + CodebookInfo &codebookInfo = _vqaDecoder->codebookInfoForFrame(_vqaDecoder->_decodingFrame); + + if (!codebookInfo.data) { + _vqaDecoder->readFrame(codebookInfo.frame, kVQAReadCodebook); + } + + _codebook = codebookInfo.data; if (!_codebook || !_vpointer) return false; diff --git a/engines/bladerunner/vqa_decoder.h b/engines/bladerunner/vqa_decoder.h index d7086d1b9f..4e070647a6 100644 --- a/engines/bladerunner/vqa_decoder.h +++ b/engines/bladerunner/vqa_decoder.h @@ -35,6 +35,7 @@ #include "graphics/surface.h" #include "video/video_decoder.h" +#include "aesc.h" namespace BladeRunner { @@ -42,6 +43,15 @@ class Lights; class View; class ZBuffer; +enum VQADecoderSkipFlags { + kVQAReadCodebook = 1, + kVQAReadVectorPointerTable = 2, + kVQAReadCustom = 4, + kVQAReadVideo = kVQAReadCodebook|kVQAReadVectorPointerTable|kVQAReadCustom, + kVQAReadAudio = 8, + kVQAReadAll = kVQAReadVideo|kVQAReadAudio +}; + class VQADecoder { public: VQADecoder(Graphics::Surface *surface); @@ -49,12 +59,13 @@ public: bool loadStream(Common::SeekableReadStream *s); - void readFrame(int frame, int skipFlags); + void readFrame(int frame, uint readFlags = kVQAReadAll); - void decodeVideoFrame(); + void decodeVideoFrame(int frame, bool forceDraw = false); void decodeZBuffer(ZBuffer *zbuffer); Audio::SeekableAudioStream *decodeAudioFrame(); void decodeView(View *view); + void decodeAESC(AESC *aesc); void decodeLights(Lights *lights); uint16 numFrames() const { return _header.numFrames; } @@ -118,8 +129,10 @@ private: } }; - struct ClipInfo { - uint16 clipCount; + struct CodebookInfo { + uint16 frame; + uint32 size; + uint8 *data; }; class VQAVideoTrack; @@ -129,8 +142,11 @@ private: Graphics::Surface *_surface; Header _header; + int _readingFrame; + int _decodingFrame; LoopInfo _loopInfo; - ClipInfo _clipInfo; + + Common::Array<CodebookInfo> _codebooks; uint32 *_frameInfo; @@ -141,7 +157,7 @@ private: VQAVideoTrack *_videoTrack; VQAAudioTrack *_audioTrack; - void readPacket(int skipFlags); + void readPacket(uint readFlags); bool readVQHD(Common::SeekableReadStream *s, uint32 size); bool readMSCI(Common::SeekableReadStream *s, uint32 size); @@ -152,6 +168,8 @@ private: bool readLNIN(Common::SeekableReadStream *s, uint32 size); bool readCLIP(Common::SeekableReadStream *s, uint32 size); + CodebookInfo &codebookInfoForFrame(int frame); + class VQAVideoTrack { public: VQAVideoTrack(VQADecoder *vqaDecoder, Graphics::Surface *surface); @@ -159,16 +177,18 @@ private: uint16 getWidth() const; uint16 getHeight() const; - int getCurFrame() const; + int getFrameCount() const; - void decodeVideoFrame(); + + void decodeVideoFrame(bool forceDraw); void decodeZBuffer(ZBuffer *zbuffer); void decodeView(View *view); + void decodeAESC(AESC *aesc); void decodeLights(Lights *lights); - bool readVQFR(Common::SeekableReadStream *s, uint32 size); + bool readVQFR(Common::SeekableReadStream *s, uint32 size, uint readFlags); bool readVPTR(Common::SeekableReadStream *s, uint32 size); - bool readVQFL(Common::SeekableReadStream *s, uint32 size); + bool readVQFL(Common::SeekableReadStream *s, uint32 size, uint readFlags); bool readCBFZ(Common::SeekableReadStream *s, uint32 size); bool readZBUF(Common::SeekableReadStream *s, uint32 size); bool readVIEW(Common::SeekableReadStream *s, uint32 size); @@ -181,6 +201,7 @@ private: bool useAudioSync() const { return false; } private: + VQADecoder *_vqaDecoder; Graphics::Surface *_surface; bool _hasNewFrame; @@ -195,7 +216,6 @@ private: uint32 _maxCBFZSize; uint32 _maxZBUFChunkSize; - uint32 _codebookSize; uint8 *_codebook; uint8 *_cbfz; bool _zbufChunkComplete; @@ -208,9 +228,11 @@ private: int _curFrame; uint8 *_viewData; - uint32 _viewDataSize; + uint32 _viewDataSize; uint8 *_lightsData; - uint32 _lightsDataSize; + uint32 _lightsDataSize; + uint8 *_aescData; + uint32 _aescDataSize; void VPTRWriteBlock(uint16 *frame, unsigned int dstBlock, unsigned int srcBlock, int count, bool alpha = false); bool decodeFrame(uint16 *frame); diff --git a/engines/bladerunner/vqa_player.cpp b/engines/bladerunner/vqa_player.cpp index 12baa26d76..51766d6e28 100644 --- a/engines/bladerunner/vqa_player.cpp +++ b/engines/bladerunner/vqa_player.cpp @@ -49,6 +49,7 @@ bool VQAPlayer::open(const Common::String &name) { _repeatsCount = 0; _loop = -1; + _frame = -1; _frameBegin = -1; _frameEnd = _decoder.numFrames() - 1; _frameEndQueued = -1; @@ -70,8 +71,9 @@ void VQAPlayer::close() { _s = nullptr; } -int VQAPlayer::update() { +int VQAPlayer::update(bool forceDraw) { uint32 now = 60 * _vm->_system->getMillis(); + int result = -1; if (_frameNext < 0) { _frameNext = _frameBegin; @@ -101,47 +103,48 @@ int VQAPlayer::update() { } } - return -1; - } - - if (_frameNext > _frameEnd) { - return -3; - } - - if (now < _frameNextTime) { - return -1; - } - - int frame = _frameNext; - _decoder.readFrame(_frameNext, 0x2); - _decoder.decodeVideoFrame(); - - int audioPreloadFrames = 14; - - if (_hasAudio) { - if (!_audioStarted) { - for (int i = 0; i < audioPreloadFrames; i++) { - if (_frameNext + i < _frameEnd) { - _decoder.readFrame(_frameNext + i, 0x1); - queueAudioFrame(_decoder.decodeAudioFrame()); + result = -1; + } else if (_frameNext > _frameEnd) { + result = -3; + } else if (now < _frameNextTime) { + result = -1; + } else { + _frame = _frameNext; + _decoder.readFrame(_frameNext, kVQAReadVideo); + _decoder.decodeVideoFrame(_frameNext); + + int audioPreloadFrames = 14; + + if (_hasAudio) { + if (!_audioStarted) { + for (int i = 0; i < audioPreloadFrames; i++) { + if (_frameNext + i < _frameEnd) { + _decoder.readFrame(_frameNext + i, kVQAReadAudio); + queueAudioFrame(_decoder.decodeAudioFrame()); + } } + _vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, _audioStream); + _audioStarted = true; } - _vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, _audioStream); - _audioStarted = true; + if (_frameNext + audioPreloadFrames < _frameEnd) { + _decoder.readFrame(_frameNext + audioPreloadFrames, kVQAReadAudio); + queueAudioFrame(_decoder.decodeAudioFrame()); + } + } + if (_frameNextTime == 0) { + _frameNextTime = now + 60000 / 15; } - if (_frameNext + audioPreloadFrames < _frameEnd) { - _decoder.readFrame(_frameNext + audioPreloadFrames, 0x1); - queueAudioFrame(_decoder.decodeAudioFrame()); + else { + _frameNextTime += 60000 / 15; } + _frameNext++; + result = _frame; } - if (_frameNextTime == 0) { - _frameNextTime = now + 60000 / 15; - } else { - _frameNextTime += 60000 / 15; + if (result < 0 && forceDraw && _frame != -1) { + _decoder.decodeVideoFrame(_frame, true); + result = _frame; } - - _frameNext++; - return frame; + return result; } void VQAPlayer::updateZBuffer(ZBuffer *zbuffer) { @@ -152,6 +155,10 @@ void VQAPlayer::updateView(View *view) { _decoder.decodeView(view); } +void VQAPlayer::updateAESC(AESC *aesc) { + _decoder.decodeAESC(aesc); +} + void VQAPlayer::updateLights(Lights *lights) { _decoder.decodeLights(lights); } diff --git a/engines/bladerunner/vqa_player.h b/engines/bladerunner/vqa_player.h index b4365670e8..1be47bd87e 100644 --- a/engines/bladerunner/vqa_player.h +++ b/engines/bladerunner/vqa_player.h @@ -52,6 +52,7 @@ class VQAPlayer { const uint16 *_zBuffer; Audio::QueuingAudioStream *_audioStream; + int _frame; int _frameNext; int _frameBegin; int _frameEnd; @@ -102,9 +103,10 @@ public: bool open(const Common::String &name); void close(); - int update(); + int update(bool forceDraw = false); void updateZBuffer(ZBuffer *zbuffer); void updateView(View *view); + void updateAESC(AESC *aesc); void updateLights(Lights *lights); bool setBeginAndEndFrame(int begin, int end, int repeatsCount, int loopSetMode, void(*callback)(void *, int, int), void *callbackData); diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h index 91ce6785d7..d4e4a4f3c4 100644 --- a/engines/sci/detection_tables.h +++ b/engines/sci/detection_tables.h @@ -843,9 +843,8 @@ static const struct ADGameDescription SciGameDescriptions[] = { GUIO_NOLAUNCHLOAD, \ GUIO_NOASPECT, \ GAMEOPTION_HQ_VIDEO) -#define GUIO_GK2 GUIO8(GUIO_NOSUBTITLES, \ - GUIO_NOSFX, \ - GUIO_NOSPEECHVOLUME, \ +#define GUIO_GK2 GUIO7(GUIO_NOSUBTITLES, \ + GUIO_LINKSPEECHTOSFX, \ GUIO_NOMIDI, \ GUIO_NOASPECT, \ GAMEOPTION_ORIGINAL_SAVELOAD, \ diff --git a/engines/sci/engine/features.h b/engines/sci/engine/features.h index 41a4b6014b..92cc45a162 100644 --- a/engines/sci/engine/features.h +++ b/engines/sci/engine/features.h @@ -110,6 +110,17 @@ public: } } + inline bool gameScriptsControlMasterVolume() const { + switch (g_sci->getGameId()) { + case GID_LSL7: + case GID_PHANTASMAGORIA2: + case GID_TORIN: + return true; + default: + return false; + } + } + inline bool hasSci3Audio() const { return getSciVersion() == SCI_VERSION_3 || g_sci->getGameId() == GID_GK2; } diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp index 9fd5dc00e6..ddaec1e971 100644 --- a/engines/sci/resource_audio.cpp +++ b/engines/sci/resource_audio.cpp @@ -482,13 +482,17 @@ int ResourceManager::readAudioMapSCI11(IntMapResourceSource *map) { // and points to garbage in the RESOURCE.AUD. The affected audio36 // assets seem to be able to load successfully from one of the later // CDs, so just ignore the map on this disc - if (g_sci->getGameId() == GID_PQSWAT && map->_volumeNumber == 1 && map->_mapNumber == 405) { + if (g_sci->getGameId() == GID_PQSWAT && + g_sci->getLanguage() == Common::EN_ANY && + map->_volumeNumber == 1 && + map->_mapNumber == 405) { continue; } - // At least version 1.00 of GK2 has multiple invalid audio36 map - // entries on CD 6 + // At least version 1.00 of the US release of GK2 has multiple + // invalid audio36 map entries on CD 6 if (g_sci->getGameId() == GID_GK2 && + g_sci->getLanguage() == Common::EN_ANY && map->_volumeNumber == 6 && offset + syncSize >= srcSize) { @@ -496,6 +500,27 @@ int ResourceManager::readAudioMapSCI11(IntMapResourceSource *map) { continue; } + // Map 2020 on CD 1 of the German release of GK2 is invalid. This + // content does not appear to ever be used by the game (it does not + // even exist in the US release), and there is a correct copy of it + // on CD 6, so just ignore the bad copy on CD 1 + if (g_sci->getGameId() == GID_GK2 && + g_sci->getLanguage() == Common::DE_DEU && + map->_volumeNumber == 1 && + map->_mapNumber == 2020) { + continue; + } + + // Map 800 and 4176 contain content that was cut from the game. The + // French version of the game includes map files from the US + // release, but the audio resources are French so the maps don't + // match. Since the content was never used, just ignore these maps + // everywhere + if (g_sci->getGameId() == GID_PHANTASMAGORIA2 && + (map->_mapNumber == 800 || map->_mapNumber == 4176)) { + continue; + } + addResource(id, src, offset + syncSize, 0, map->getLocationName()); } } diff --git a/engines/sci/sound/audio32.cpp b/engines/sci/sound/audio32.cpp index 7622305adb..c3f9eb4098 100644 --- a/engines/sci/sound/audio32.cpp +++ b/engines/sci/sound/audio32.cpp @@ -181,10 +181,18 @@ Audio32::Audio32(ResourceManager *resMan) : } _useModifiedAttenuation = g_sci->_features->usesModifiedAudioAttenuation(); - // The mixer stream type is given as `kSFXSoundType` so that audio from - // Audio32 will be mixed at the same standard volume as the video players - // (which must use `kSFXSoundType` as well). - _mixer->playStream(Audio::Mixer::kSFXSoundType, &_handle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + + // In games where scripts premultiply master audio volumes into the volumes + // of the individual audio channels sent to the mixer, Audio32 needs to use + // the kPlainSoundType so that the master SFX volume is not applied twice. + // Otherwise, we simply pass along master volume changes to the ScummVM + // mixer for the kSFXSoundType and allow it to control master volume. + // (The volume of the kSFXSoundType in the mixer still needs to be updated + // for games that control master volumes themselves so that videos will play + // at the same volume as the rest of the game.) + const Audio::Mixer::SoundType soundType = g_sci->_features->gameScriptsControlMasterVolume() ? Audio::Mixer::kPlainSoundType : Audio::Mixer::kSFXSoundType; + + _mixer->playStream(soundType, &_handle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); } Audio32::~Audio32() { diff --git a/engines/titanic/carry/eye.cpp b/engines/titanic/carry/eye.cpp index 148d2ea154..9bad0e480c 100644 --- a/engines/titanic/carry/eye.cpp +++ b/engines/titanic/carry/eye.cpp @@ -22,9 +22,11 @@ #include "titanic/carry/eye.h" #include "titanic/game/head_slot.h" -#include "titanic/pet_control/pet_control.h" -#include "titanic/game/transport/lift.h" +#include "titanic/game/light.h" #include "titanic/game/television.h" +#include "titanic/game/transport/lift.h" +#include "titanic/pet_control/pet_control.h" + namespace Titanic { @@ -109,7 +111,8 @@ bool CEye::ActMsg(CActMsg *msg) { playSound("z#47.wav"); CActMsg actMsg("Eye Removed"); - actMsg.execute("1stClassState"); + actMsg.execute("1stClassState", CLight::_type, + MSGFLAG_CLASS_DEF | MSGFLAG_SCAN); } else { _eyeFlag = false; diff --git a/engines/titanic/debugger.cpp b/engines/titanic/debugger.cpp index 15d098a99a..a8b580b636 100644 --- a/engines/titanic/debugger.cpp +++ b/engines/titanic/debugger.cpp @@ -44,6 +44,7 @@ Debugger::Debugger(TitanicEngine *vm) : GUI::Debugger(), _vm(vm) { registerCmd("movie", WRAP_METHOD(Debugger, cmdMovie)); registerCmd("sound", WRAP_METHOD(Debugger, cmdSound)); registerCmd("cheat", WRAP_METHOD(Debugger, cmdCheat)); + registerCmd("frame", WRAP_METHOD(Debugger, cmdFrame)); } int Debugger::strToInt(const char *s) { @@ -346,4 +347,22 @@ bool Debugger::cmdCheat(int argc, const char **argv) { return false; } +bool Debugger::cmdFrame(int argc, const char **argv) { + if (argc == 3) { + CGameObject *obj = dynamic_cast<CGameObject *>( + g_vm->_window->_project->findByName(argv[1])); + + if (obj) { + obj->loadFrame(strToInt(argv[2])); + return false; + } else { + debugPrintf("Object not found\n"); + return true; + } + } else { + debugPrintf("frame <object> <frame number>"); + return true; + } +} + } // End of namespace Titanic diff --git a/engines/titanic/debugger.h b/engines/titanic/debugger.h index 3e53feec2a..cd9da2b2ab 100644 --- a/engines/titanic/debugger.h +++ b/engines/titanic/debugger.h @@ -115,6 +115,11 @@ private: * Change to the cheat room */ bool cmdCheat(int argc, const char **argv); + + /** + * Set the movie frame for a given object + */ + bool cmdFrame(int argc, const char **argv); protected: TitanicEngine *_vm; public: diff --git a/engines/titanic/npcs/maitre_d.cpp b/engines/titanic/npcs/maitre_d.cpp index e944af81c1..a45f48523a 100644 --- a/engines/titanic/npcs/maitre_d.cpp +++ b/engines/titanic/npcs/maitre_d.cpp @@ -78,6 +78,11 @@ void CMaitreD::load(SimpleFile *file) { _timerId = file->readNumber(); CTrueTalkNPC::load(file); + + // WORKAROUND: The back view of the MaitreD from close to the table is dodgy + // in the original. And unneeded anyway, since he's also part of the background + if (_name == "MaitreLoop03") + _visible = false; } bool CMaitreD::RestaurantMusicChanged(CRestaurantMusicChanged *msg) { diff --git a/engines/titanic/pet_control/pet_frame.cpp b/engines/titanic/pet_control/pet_frame.cpp index 9088cfe126..738116ed8f 100644 --- a/engines/titanic/pet_control/pet_frame.cpp +++ b/engines/titanic/pet_control/pet_frame.cpp @@ -143,8 +143,11 @@ bool CPetFrame::setPetControl(CPetControl *petControl) { void CPetFrame::setArea(PetArea newArea) { resetArea(); - if ((uint)newArea < _petAreas.size()) - _modeButtons[_petAreas[newArea]].setMode(MODE_SELECTED); + + for (uint idx = 0; idx < _modeButtons.size(); ++idx) { + if (_petAreas[idx] == newArea) + _modeButtons[idx].setMode(MODE_SELECTED); + } } void CPetFrame::resetArea() { diff --git a/engines/titanic/pet_control/pet_load.cpp b/engines/titanic/pet_control/pet_load.cpp index 1d81435bed..6c24c575c9 100644 --- a/engines/titanic/pet_control/pet_load.cpp +++ b/engines/titanic/pet_control/pet_load.cpp @@ -52,7 +52,7 @@ bool CPetLoad::MouseButtonUpMsg(const Point &pt) { } void CPetLoad::getTooltip(CTextControl *text) { - text->setText("Load the game."); + text->setText(LOAD_THE_GAME); } void CPetLoad::execute() { diff --git a/engines/titanic/pet_control/pet_load_save.cpp b/engines/titanic/pet_control/pet_load_save.cpp index aa438c06d3..d918478fb1 100644 --- a/engines/titanic/pet_control/pet_load_save.cpp +++ b/engines/titanic/pet_control/pet_load_save.cpp @@ -117,7 +117,7 @@ Rect CPetLoadSave::getSlotBounds(int index) { void CPetLoadSave::resetSlots() { for (int idx = 0; idx < SAVEGAME_SLOTS_COUNT; ++idx) { - _slotNames[idx].setText("Empty"); + _slotNames[idx].setText(EMPTY); _slotInUse[idx] = false; // Try and open up the savegame for access diff --git a/engines/titanic/pet_control/pet_quit.cpp b/engines/titanic/pet_control/pet_quit.cpp index 0d94474f99..b180bf8dc8 100644 --- a/engines/titanic/pet_control/pet_quit.cpp +++ b/engines/titanic/pet_control/pet_quit.cpp @@ -53,7 +53,7 @@ bool CPetQuit::reset() { setName("PetExit", pet); uint col = getPetSection()->getColor(0); - _text.setText("Are you sure you want to quit?"); + _text.setText(SURE_YOU_WANT_TO_QUIT); _text.setLineColor(0, col); _btnYes.reset("PetQuitOut", pet, MODE_UNSELECTED); @@ -84,7 +84,7 @@ bool CPetQuit::MouseButtonUpMsg(const Point &pt) { } void CPetQuit::getTooltip(CTextControl *text) { - text->setText("Quit the game."); + text->setText(QUIT_THE_GAME); } } // End of namespace Titanic diff --git a/engines/titanic/pet_control/pet_save.cpp b/engines/titanic/pet_control/pet_save.cpp index 00dbfa6b4a..16fe092b24 100644 --- a/engines/titanic/pet_control/pet_save.cpp +++ b/engines/titanic/pet_control/pet_save.cpp @@ -65,7 +65,7 @@ void CPetSave::highlightCurrent(const Point &pt) { } void CPetSave::getTooltip(CTextControl *text) { - text->setText("Save the game."); + text->setText(SAVE_THE_GAME); } void CPetSave::highlightSave(int index) { diff --git a/engines/titanic/pet_control/pet_sound.cpp b/engines/titanic/pet_control/pet_sound.cpp index 1ba557ab14..085ee649b2 100644 --- a/engines/titanic/pet_control/pet_sound.cpp +++ b/engines/titanic/pet_control/pet_sound.cpp @@ -65,25 +65,25 @@ bool CPetSound::setup(CPetControl *petControl, CPetGlyphs *owner) { _textMasterVolume.setBounds(rect); _textMasterVolume.resize(3); _textMasterVolume.setHasBorder(false); - _textMasterVolume.setText("Master volume"); + _textMasterVolume.setText(MASTER_VOLUME); rect.translate(0, 20); _textMusicVolume.setBounds(rect); _textMusicVolume.resize(3); _textMusicVolume.setHasBorder(false); - _textMusicVolume.setText("Music volume"); + _textMusicVolume.setText(MUSIC_VOLUME); rect.translate(0, 20); _textParrotVolume.setBounds(rect); _textParrotVolume.resize(3); _textParrotVolume.setHasBorder(false); - _textParrotVolume.setText("Parrot volume"); + _textParrotVolume.setText(PARROT_VOLUME); rect.translate(0, 20); _textSpeechVolume.setBounds(rect); _textSpeechVolume.resize(3); _textSpeechVolume.setHasBorder(false); - _textSpeechVolume.setText("Speech volume"); + _textSpeechVolume.setText(SPEECH_VOLUME); return true; } @@ -276,7 +276,7 @@ bool CPetSound::MouseButtonUpMsg(const Point &pt) { } void CPetSound::getTooltip(CTextControl *text) { - text->setText("Change the volume settings."); + text->setText(CHANGE_VOLUME_SETTINGS); } } // End of namespace Titanic diff --git a/engines/titanic/star_control/star_camera.h b/engines/titanic/star_control/star_camera.h index 6cf0183f0a..b22abe6eff 100644 --- a/engines/titanic/star_control/star_camera.h +++ b/engines/titanic/star_control/star_camera.h @@ -31,7 +31,7 @@ namespace Titanic { class CCameraMover; class CErrorCode; -class CNavigationInfo; +struct CNavigationInfo; class FPoint; class SimpleFile; diff --git a/engines/titanic/support/files_manager.cpp b/engines/titanic/support/files_manager.cpp index eebd56c1fa..fbfb7e0f61 100644 --- a/engines/titanic/support/files_manager.cpp +++ b/engines/titanic/support/files_manager.cpp @@ -45,11 +45,16 @@ bool CFilesManager::loadResourceIndex() { uint headerId = _datFile.readUint32BE(); _version = _datFile.readUint16LE(); - if (headerId != MKTAG('S', 'V', 'T', 'N') || _version < 1) { + if (headerId != MKTAG('S', 'V', 'T', 'N')) { g_vm->GUIError("titanic.dat has invalid contents"); return false; } + if (_version != 3) { + g_vm->GUIError("titanic.dat is out of date"); + return false; + } + // Read in entries uint offset, size, flags; char c; diff --git a/engines/titanic/support/strings.h b/engines/titanic/support/strings.h index 65d6550e1e..b7a775cc99 100644 --- a/engines/titanic/support/strings.h +++ b/engines/titanic/support/strings.h @@ -170,6 +170,16 @@ enum StringId { CURRENT_LOCATION, A_HOT, A_COLD, + LOAD_THE_GAME, + SAVE_THE_GAME, + EMPTY, + QUIT_THE_GAME, + SURE_YOU_WANT_TO_QUIT, + CHANGE_VOLUME_SETTINGS, + MASTER_VOLUME, + MUSIC_VOLUME, + PARROT_VOLUME, + SPEECH_VOLUME, // German version only DE_SUMMER, diff --git a/engines/titanic/titanic.cpp b/engines/titanic/titanic.cpp index de28e073a9..67bdf82fa0 100644 --- a/engines/titanic/titanic.cpp +++ b/engines/titanic/titanic.cpp @@ -191,7 +191,7 @@ bool TitanicEngine::canLoadGameStateCurrently() { return false; if (screenMan && screenMan->_inputHandler->isLocked()) return false; - if (!gameManager->isntTransitioning()) + if (!gameManager || !gameManager->isntTransitioning()) return false; CProjectItem *project = gameManager->_project; @@ -210,6 +210,8 @@ bool TitanicEngine::canLoadGameStateCurrently() { bool TitanicEngine::canSaveGameStateCurrently() { CGameManager *gameManager = _window->_gameManager; + if (!gameManager) + return false; return gameManager->_gameState._petActive && canLoadGameStateCurrently(); diff --git a/engines/wage/detection_tables.h b/engines/wage/detection_tables.h index b21169b35f..d163e382ca 100644 --- a/engines/wage/detection_tables.h +++ b/engines/wage/detection_tables.h @@ -112,9 +112,9 @@ static const ADGameDescription gameDescriptions[] = { FANGAME("Mountain of Mayhem", "634211b004371635d191ae0687035501", 750003), // Original file name "Mountain of Mayhem †" // No way to pass through the first screen FANGAME("Nightcrawler Ned", "8423fc015c395bd6be54a7ea69170d99", 366542), - // Crash on startup + // No player in the world FANGAMEN("Parrot Talk", "PARROT TALK V1", "c38c090be8c078d931905c93bc0689f5", 118936), - // Crash on startup + // No player in the world FANGAMEN("Parrot Talk", "PARROT TALKV2", "5ec1df9e2d6d0dcf1a040a95500d9551", 118884), FANGAME("Pavilion", "a980e60a291c0a7b949474177affa134", 231687), FANGAMEN("Pencils", "Pencils.99", "09dbcdbefe20536c2db1b1a4fb4e5ed3", 408551), diff --git a/engines/wage/script.cpp b/engines/wage/script.cpp index 3405c8bf47..9f8093c50c 100644 --- a/engines/wage/script.cpp +++ b/engines/wage/script.cpp @@ -50,6 +50,8 @@ #include "wage/script.h" #include "wage/world.h" +#include "common/config-manager.h" +#include "common/file.h" #include "common/stream.h" namespace Wage { @@ -74,8 +76,7 @@ Common::String Script::Operand::toString() { } } -Script::Script(Common::SeekableReadStream *data) : _data(data) { - _engine = NULL; +Script::Script(Common::SeekableReadStream *data, int num, WageEngine *engine) : _data(data), _engine(engine) { _world = NULL; _loopCount = 0; @@ -85,6 +86,29 @@ Script::Script(Common::SeekableReadStream *data) : _data(data) { _handled = false; convertToText(); + + if (ConfMan.getBool("dump_scripts")) { + Common::DumpFile out; + Common::String name; + + if (num == -1) + name = Common::String::format("./dumps/%s-global.txt", _engine->getTargetName()); + else + name = Common::String::format("./dumps/%s-%d.txt", _engine->getTargetName(), num); + + if (!out.open(name)) { + warning("Can not open dump file %s", name.c_str()); + return; + } + + for (uint i = 0; i < _scriptText.size(); i++) { + out.write(_scriptText[i]->line.c_str(), strlen(_scriptText[i]->line.c_str())); + out.writeByte('\n'); + } + + out.flush(); + out.close(); + } } Script::~Script() { @@ -149,12 +173,11 @@ Common::String Script::preprocessInputText(Common::String inputText) { return inputText; } -bool Script::execute(World *world, int loopCount, Common::String *inputText, Designed *inputClick, WageEngine *engine) { +bool Script::execute(World *world, int loopCount, Common::String *inputText, Designed *inputClick) { _world = world; _loopCount = loopCount; _inputText = inputText; _inputClick = inputClick; - _engine = engine; _handled = false; Common::String input; @@ -165,7 +188,7 @@ bool Script::execute(World *world, int loopCount, Common::String *inputText, Des } _data->seek(12); - while (_data->pos() < _data->size()) { + while (_data->pos() < _data->size() && !_engine->_shouldQuit) { printLine(_data->pos()); byte command = _data->readByte(); @@ -232,7 +255,7 @@ bool Script::execute(World *world, int loopCount, Common::String *inputText, Des if (_world->_globalScript != this) { debug(1, "Executing global script..."); - bool globalHandled = _world->_globalScript->execute(_world, _loopCount, &input, _inputClick, _engine); + bool globalHandled = _world->_globalScript->execute(_world, _loopCount, &input, _inputClick); if (globalHandled) _handled = true; } else if (!input.empty()) { diff --git a/engines/wage/script.h b/engines/wage/script.h index e796195b7f..df7ec7e4d6 100644 --- a/engines/wage/script.h +++ b/engines/wage/script.h @@ -52,7 +52,7 @@ namespace Wage { class Script { public: - Script(Common::SeekableReadStream *data); + Script(Common::SeekableReadStream *data, int num, WageEngine *engine); ~Script(); private: @@ -130,7 +130,7 @@ private: public: void print(); void printLine(int offset); - bool execute(World *world, int loopCount, Common::String *inputText, Designed *inputClick, WageEngine *engine); + bool execute(World *world, int loopCount, Common::String *inputText, Designed *inputClick); private: Common::String preprocessInputText(Common::String inputText); diff --git a/engines/wage/sound.cpp b/engines/wage/sound.cpp index fd6adaea19..9f5ed3497c 100644 --- a/engines/wage/sound.cpp +++ b/engines/wage/sound.cpp @@ -49,6 +49,7 @@ #include "audio/mixer.h" #include "audio/decoders/raw.h" #include "common/stream.h" +#include "common/system.h" #include "wage/wage.h" #include "wage/entities.h" @@ -91,8 +92,26 @@ void WageEngine::playSound(Common::String soundName) { Audio::AudioStream *stream = Audio::makeRawStream(s->_data, s->_size, 11000, Audio::FLAG_UNSIGNED); - _mixer->playStream(Audio::Mixer::kPlainSoundType, &s->_handle, stream, + _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, stream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO); + + while (_mixer->isSoundHandleActive(_soundHandle) && !_shouldQuit) { + Common::Event event; + + _eventMan->pollEvent(event); + + switch (event.type) { + case Common::EVENT_QUIT: + if (saveDialog()) + _shouldQuit = true; + break; + default: + break; + } + + _system->updateScreen(); + _system->delayMillis(10); + } } void WageEngine::updateSoundTimerForScene(Scene *scene, bool firstTime) { diff --git a/engines/wage/sound.h b/engines/wage/sound.h index 101b5a0db6..ada5a3c0cb 100644 --- a/engines/wage/sound.h +++ b/engines/wage/sound.h @@ -48,8 +48,6 @@ #ifndef WAGE_SOUND_H #define WAGE_SOUND_H -#include "audio/mixer.h" - namespace Wage { class Sound { @@ -60,8 +58,6 @@ public: Common::String _name; uint _size; byte *_data; - - Audio::SoundHandle _handle; }; } // End of namespace Wage diff --git a/engines/wage/wage.cpp b/engines/wage/wage.cpp index 7f9fb073f5..c5f3673255 100644 --- a/engines/wage/wage.cpp +++ b/engines/wage/wage.cpp @@ -123,6 +123,8 @@ Common::Error WageEngine::run() { if (!_world->loadWorld(_resManager)) return Common::kNoGameDataFoundError; + _shouldQuit = false; + _gui = new Gui(this); _temporarilyHidden = true; @@ -140,8 +142,6 @@ Common::Error WageEngine::run() { processTurn(&input, NULL); _temporarilyHidden = false; - _shouldQuit = false; - while (!_shouldQuit) { _debugger->onFrame(); @@ -431,7 +431,7 @@ void WageEngine::processTurnInternal(Common::String *textInput, Designed *clickI bool monsterWasNull = (_monster == NULL); Script *script = playerScene->_script != NULL ? playerScene->_script : _world->_globalScript; - bool handled = script->execute(_world, _loopCount++, textInput, clickInput, this); + bool handled = script->execute(_world, _loopCount++, textInput, clickInput); playerScene = _world->_player->_currentScene; @@ -445,6 +445,10 @@ void WageEngine::processTurnInternal(Common::String *textInput, Designed *clickI regen(); Common::String input("look"); processTurnInternal(&input, NULL); + + if (_shouldQuit) + return; + redrawScene(); _temporarilyHidden = false; } else if (_loopCount == 1) { diff --git a/engines/wage/wage.h b/engines/wage/wage.h index 4c93399ad7..cb4e8a3520 100644 --- a/engines/wage/wage.h +++ b/engines/wage/wage.h @@ -49,6 +49,7 @@ #define WAGE_WAGE_H #include "engines/engine.h" +#include "audio/mixer.h" #include "common/debug.h" #include "common/endian.h" #include "common/rect.h" @@ -126,6 +127,8 @@ public: void processTurn(Common::String *textInput, Designed *clickInput); void regen(); + const char *getTargetName() { return _targetName.c_str(); } + private: bool loadWorld(Common::MacResManager *resMan); void performInitialSetup(); @@ -191,6 +194,8 @@ public: bool _isGameOver; bool _commandWasQuick; + bool _shouldQuit; + Common::String _inputText; void playSound(Common::String soundName); @@ -233,7 +238,7 @@ private: Common::MacResManager *_resManager; - bool _shouldQuit; + Audio::SoundHandle _soundHandle; }; // Example console class diff --git a/engines/wage/world.cpp b/engines/wage/world.cpp index c0c802ff8d..fa8cfd57a1 100644 --- a/engines/wage/world.cpp +++ b/engines/wage/world.cpp @@ -132,7 +132,7 @@ bool World::loadWorld(Common::MacResManager *resMan) { // Load global script res = resMan->getResource(MKTAG('G','C','O','D'), resArray[0]); - _globalScript = new Script(res); + _globalScript = new Script(res, -1, _engine); // TODO: read creator @@ -209,7 +209,7 @@ bool World::loadWorld(Common::MacResManager *resMan) { res = resMan->getResource(MKTAG('A','C','O','D'), *iter); if (res != NULL) - scene->_script = new Script(res); + scene->_script = new Script(res, *iter, _engine); res = resMan->getResource(MKTAG('A','T','X','T'), *iter); if (res != NULL) { @@ -267,7 +267,7 @@ bool World::loadWorld(Common::MacResManager *resMan) { if (_chrs.empty()) { error("loadWorld: and I have no characters"); } - _player = _chrs[0]; + _player = _orderedChrs[0]; } diff --git a/graphics/macgui/mactext.cpp b/graphics/macgui/mactext.cpp index e2b945d28f..69e2dce932 100644 --- a/graphics/macgui/mactext.cpp +++ b/graphics/macgui/mactext.cpp @@ -244,6 +244,7 @@ void MacText::reallocSurface() { if (_surface->w < _textMaxWidth || _surface->h < _textMaxHeight) { // realloc surface and copy old content ManagedSurface *n = new ManagedSurface(_textMaxWidth, _textMaxHeight); + n->clear(_bgcolor); n->blitFrom(*_surface, Common::Point(0, 0)); delete _surface; @@ -416,7 +417,8 @@ void MacText::clearText() { _textLines.clear(); _str.clear(); - _surface->clear(_bgcolor); + if (_surface) + _surface->clear(_bgcolor); recalcDims(); } |