[Scummvm-git-logs] scummvm master -> 824ecc0aad325c54f34c8fb7f64cf4df71c53090

peterkohaut peterkohaut at users.noreply.github.com
Sat Nov 24 08:39:45 CET 2018


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
824ecc0aad BLADERUNNER: Preliminary saving & loading support


Commit: 824ecc0aad325c54f34c8fb7f64cf4df71c53090
    https://github.com/scummvm/scummvm/commit/824ecc0aad325c54f34c8fb7f64cf4df71c53090
Author: Peter Kohaut (peter.kohaut at gmail.com)
Date: 2018-11-24T08:39:03+01:00

Commit Message:
BLADERUNNER: Preliminary saving & loading support

Saving and loading is accessible via ScummVM dialogs.
No in-game UI support yet.

It is possible to load saves from original game via debugger console.
ScummVM saves have additional header and are incompatibile with original
game.

Changed paths:
    engines/bladerunner/actor.cpp
    engines/bladerunner/actor.h
    engines/bladerunner/actor_clues.cpp
    engines/bladerunner/actor_clues.h
    engines/bladerunner/bladerunner.cpp
    engines/bladerunner/bladerunner.h
    engines/bladerunner/crimes_database.cpp
    engines/bladerunner/debugger.cpp
    engines/bladerunner/debugger.h
    engines/bladerunner/detection.cpp
    engines/bladerunner/detection_tables.h
    engines/bladerunner/dialogue_menu.cpp
    engines/bladerunner/dialogue_menu.h
    engines/bladerunner/game_flags.cpp
    engines/bladerunner/game_flags.h
    engines/bladerunner/item.cpp
    engines/bladerunner/items.cpp
    engines/bladerunner/items.h
    engines/bladerunner/outtake.cpp
    engines/bladerunner/overlays.cpp
    engines/bladerunner/overlays.h
    engines/bladerunner/savefile.cpp
    engines/bladerunner/savefile.h
    engines/bladerunner/scene.cpp
    engines/bladerunner/scene_objects.cpp
    engines/bladerunner/script/ai_script.cpp
    engines/bladerunner/script/police_maze.cpp
    engines/bladerunner/script/scene/rc01.cpp
    engines/bladerunner/script/script.cpp
    engines/bladerunner/set.cpp
    engines/bladerunner/settings.cpp
    engines/bladerunner/settings.h
    engines/bladerunner/slice_renderer.cpp
    engines/bladerunner/suspects_database.h
    engines/bladerunner/ui/elevator.cpp
    engines/bladerunner/ui/esper.cpp
    engines/bladerunner/ui/kia.cpp
    engines/bladerunner/ui/kia.h
    engines/bladerunner/ui/kia_section_clues.cpp
    engines/bladerunner/ui/kia_section_clues.h
    engines/bladerunner/ui/kia_section_crimes.cpp
    engines/bladerunner/ui/kia_section_crimes.h
    engines/bladerunner/ui/kia_section_suspects.cpp
    engines/bladerunner/ui/kia_section_suspects.h
    engines/bladerunner/ui/scores.cpp
    engines/bladerunner/ui/spinner.cpp
    engines/bladerunner/ui/vk.cpp
    engines/bladerunner/vqa_player.cpp
    engines/bladerunner/vqa_player.h


diff --git a/engines/bladerunner/actor.cpp b/engines/bladerunner/actor.cpp
index 4899ea9..26a7a80 100644
--- a/engines/bladerunner/actor.cpp
+++ b/engines/bladerunner/actor.cpp
@@ -51,7 +51,8 @@ Actor::Actor(BladeRunnerEngine *vm, int actorId) {
 
 	_walkInfo      = new ActorWalk(vm);
 	_movementTrack = new MovementTrack();
-	_clues         = new ActorClues(vm, (actorId && actorId != 99) ? 2 : 4);
+	_cluesLimit    = (actorId == 0 || actorId == 99) ? 4 : 2;
+	_clues         = new ActorClues(vm, _cluesLimit);
 	_combatInfo    = new ActorCombat(vm);
 
 	_friendlinessToOther.resize(_vm->_gameInfo->getActorCount());
@@ -1105,6 +1106,10 @@ void Actor::addClueToDatabase(int clueId, int weight, bool clueAcquired, bool un
 	_clues->add(_id, clueId, weight, clueAcquired, unknownFlag, fromActorId);
 }
 
+bool Actor::canAcquireClue(int clueId) const {
+	return _clues->exists(clueId);
+}
+
 void Actor::acquireClue(int clueId, bool unknownFlag, int fromActorId) {
 	bool hasAlready = hasClue(clueId);
 	_clues->acquire(clueId, unknownFlag, fromActorId);
@@ -1124,7 +1129,7 @@ bool Actor::hasClue(int clueId) const {
 void Actor::copyClues(int actorId) {
 	Actor *otherActor = _vm->_actors[actorId];
 	for (int i = 0; i < (int)_vm->_gameInfo->getClueCount(); i++) {
-		if (hasClue(i) && !_clues->isPrivate(i) && !otherActor->hasClue(i)) {
+		if (hasClue(i) && !_clues->isPrivate(i) && otherActor->canAcquireClue(i) && !otherActor->hasClue(i)) {
 			int fromActorId = _id;
 			if (_id == BladeRunnerEngine::kActorVoiceOver) {
 				fromActorId = _clues->getFromActorId(i);
@@ -1258,8 +1263,9 @@ void Actor::save(SaveFileWriteStream &f) {
 
 	f.writeInt(_honesty);
 	f.writeInt(_intelligence);
-	f.writeInt(_stability);
 	f.writeInt(_combatAggressiveness);
+	f.writeInt(_stability);
+
 	f.writeInt(_goalNumber);
 
 	f.writeInt(_currentHP);
@@ -1271,7 +1277,8 @@ void Actor::save(SaveFileWriteStream &f) {
 	f.writeInt(_movementTrackNextAngle);
 	f.writeBool(_movementTrackNextRunning);
 
-	f.writeInt(0); // TODO: _clueType
+	f.writeInt(_cluesLimit);
+
 	f.writeBool(_isMoving);
 	f.writeBool(_isTarget);
 	f.writeBool(_inCombat);
@@ -1302,7 +1309,7 @@ void Actor::save(SaveFileWriteStream &f) {
 
 	uint32 now = _vm->getTotalPlayTime(); // TODO: should be last lock time
 	for (int i = 0; i < 7; ++i) {
-		f.writeInt(_timersLast[i] - now);
+		f.writeInt(now - _timersLast[i]);
 	}
 
 	int actorCount = _vm->_gameInfo->getActorCount();
@@ -1316,9 +1323,10 @@ void Actor::save(SaveFileWriteStream &f) {
 
 	_walkInfo->save(f);
 
-	f.writeBoundingBox(_bbox);
+	f.writeBoundingBox(_bbox, false);
 
 	_combatInfo->save(f);
+
 	f.writeInt(_animationModeCombatIdle);
 	f.writeInt(_animationModeCombatWalk);
 	f.writeInt(_animationModeCombatRun);
@@ -1334,8 +1342,9 @@ void Actor::load(SaveFileReadStream &f) {
 
 	_honesty = f.readInt();
 	_intelligence = f.readInt();
-	_stability = f.readInt();
 	_combatAggressiveness = f.readInt();
+	_stability = f.readInt();
+
 	_goalNumber = f.readInt();
 
 	_currentHP = f.readInt();
@@ -1347,7 +1356,8 @@ void Actor::load(SaveFileReadStream &f) {
 	_movementTrackNextAngle = f.readInt();
 	_movementTrackNextRunning = f.readBool();
 
-	f.skip(4); // TODO: _clueType
+	_cluesLimit = f.readInt();
+
 	_isMoving = f.readBool();
 	_isTarget = f.readBool();
 	_inCombat = f.readBool();
@@ -1378,7 +1388,7 @@ void Actor::load(SaveFileReadStream &f) {
 
 	uint32 now = _vm->getTotalPlayTime(); // TODO: should be last lock time
 	for (int i = 0; i < 7; ++i) {
-		_timersLast[i] = f.readInt() + now;
+		_timersLast[i] = now - f.readInt();
 	}
 
 	int actorCount = _vm->_gameInfo->getActorCount();
@@ -1392,7 +1402,7 @@ void Actor::load(SaveFileReadStream &f) {
 
 	_walkInfo->load(f);
 
-	_bbox = f.readBoundingBox();
+	_bbox = f.readBoundingBox(false);
 
 	_combatInfo->load(f);
 
diff --git a/engines/bladerunner/actor.h b/engines/bladerunner/actor.h
index 6540c05..896f3a2 100644
--- a/engines/bladerunner/actor.h
+++ b/engines/bladerunner/actor.h
@@ -70,6 +70,7 @@ private:
 	int     _targetFacing;
 	int     _walkboxId;
 
+	int     _cluesLimit;
 	int     _timer4RemainDefault;
 
 	// Flags
@@ -242,9 +243,10 @@ public:
 	bool isSpeeching();
 
 	void addClueToDatabase(int clueId, int unknown, bool clueAcquired, bool unknownFlag, int fromActorId);
+	bool canAcquireClue(int clueId) const;
 	void acquireClue(int clueId, bool unknownFlag, int fromActorId);
 	void loseClue(int clueId);
-	bool hasClue(int clueId)  const;
+	bool hasClue(int clueId) const;
 	void copyClues(int actorId);
 	void acquireCluesByRelations();
 
diff --git a/engines/bladerunner/actor_clues.cpp b/engines/bladerunner/actor_clues.cpp
index 70d3ad5..07df81e 100644
--- a/engines/bladerunner/actor_clues.cpp
+++ b/engines/bladerunner/actor_clues.cpp
@@ -33,11 +33,11 @@
 
 namespace BladeRunner {
 
-ActorClues::ActorClues(BladeRunnerEngine *vm, int cluesType) {
+ActorClues::ActorClues(BladeRunnerEngine *vm, int cluesLimit) {
 	_vm = vm;
 	_count = 0;
 	_maxCount = 0;
-	switch (cluesType) {
+	switch (cluesLimit) {
 	case 4:
 		_maxCount = _vm->_gameInfo->getClueCount();
 		break;
@@ -83,11 +83,11 @@ bool ActorClues::isAcquired(int clueId) const {
 	if (clueIndex == -1) {
 		return false;
 	}
-#if BLADERUNNER_DEBUG_GAME
-	return true;
-#else
+// #if BLADERUNNER_DEBUG_GAME
+// 	return true;
+// #else
 	return _clues[clueIndex].flags & 0x01;
-#endif
+// #endif
 }
 
 int ActorClues::getWeight(int clueId) const {
@@ -346,6 +346,10 @@ void ActorClues::add(int actorId, int clueId, int weight, bool acquired, bool un
 	++_count;
 }
 
+bool ActorClues::exists(int clueId) const {
+	return findClueIndex(clueId) != -1;
+}
+
 void ActorClues::remove(int index) {
 	if (_vm->_crimesDatabase) {
 		debug("Actor removed clue: \"%s\"", _vm->_crimesDatabase->getClueText(_clues[index].clueId));
@@ -367,7 +371,7 @@ void ActorClues::remove(int index) {
 void ActorClues::save(SaveFileWriteStream &f) {
 	f.writeInt(_count);
 	f.writeInt(_maxCount);
-	for (int i = 0; i < _count; ++i) {
+	for (int i = 0; i < _maxCount; ++i) {
 		Clue &c = _clues[i];
 		f.writeInt(c.clueId);
 		f.writeInt(c.weight);
@@ -387,7 +391,7 @@ void ActorClues::load(SaveFileReadStream &f) {
 	_maxCount = f.readInt();
 	_clues.clear();
 	_clues.resize(_maxCount);
-	for (int i = 0; i < _count; ++i) {
+	for (int i = 0; i < _maxCount; ++i) {
 		Clue &c = _clues[i];
 		c.clueId = f.readInt();
 		c.weight = f.readInt();
@@ -402,8 +406,4 @@ void ActorClues::load(SaveFileReadStream &f) {
 	}
 }
 
-bool ActorClues::exists(int clueId) const {
-	return findClueIndex(clueId) != -1;
-}
-
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/actor_clues.h b/engines/bladerunner/actor_clues.h
index 7669032..88af222 100644
--- a/engines/bladerunner/actor_clues.h
+++ b/engines/bladerunner/actor_clues.h
@@ -60,9 +60,10 @@ public:
 	};
 
 public:
-	ActorClues(BladeRunnerEngine *_vm, int cluesType);
+	ActorClues(BladeRunnerEngine *_vm, int cluesLimit);
 
 	void add(int actorId, int clueId, int unknown, bool acquired, bool unknownFlag, int fromActorId);
+	bool exists(int clueId) const;
 
 	void acquire(int clueId, bool flag2, int fromActorId);
 	void lose(int clueId);
@@ -95,7 +96,6 @@ public:
 	void load(SaveFileReadStream &f);
 
 private:
-	bool exists(int clueId) const;
 	int findClueIndex(int clueId) const;
 	void remove(int clueIndex);
 };
diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp
index 362e0c4..1c08b79 100644
--- a/engines/bladerunner/bladerunner.cpp
+++ b/engines/bladerunner/bladerunner.cpp
@@ -203,7 +203,80 @@ BladeRunnerEngine::~BladeRunnerEngine() {
 }
 
 bool BladeRunnerEngine::hasFeature(EngineFeature f) const {
-	return f == kSupportsRTL;
+	return
+		f == kSupportsRTL ||
+		f == kSupportsLoadingDuringRuntime ||
+		f == kSupportsSavingDuringRuntime;
+}
+
+bool BladeRunnerEngine::canLoadGameStateCurrently() {
+	return
+		playerHasControl() &&
+		!_sceneScript->isInsideScript() &&
+		!_aiScripts->isInsideScript() &&
+		!_kia->isOpen() &&
+		!_spinner->isOpen() &&
+		!_vk->isOpen() &&
+		!_elevator->isOpen();
+}
+
+Common::Error BladeRunnerEngine::loadGameState(int slot) {
+	const Common::String saveName = Common::String::format("%s.%03d", _targetName.c_str(), slot);
+
+	Common::InSaveFile *saveFile = getSaveFileManager()->openForLoading(saveName);
+	if (saveFile == nullptr || saveFile->err()) {
+		delete saveFile;
+		return Common::kReadingFailed;
+	}
+
+	BladeRunner::SaveFileHeader header;
+	if (!BladeRunner::SaveFile::readHeader(*saveFile, header)) {
+		error("Invalid savegame");
+	}
+
+	loadGame(*saveFile);
+
+	delete saveFile;
+
+	return Common::kNoError;
+}
+
+bool BladeRunnerEngine::canSaveGameStateCurrently() {
+	return
+		playerHasControl() &&
+		!_sceneScript->isInsideScript() &&
+		!_aiScripts->isInsideScript() &&
+		!_kia->isOpen() &&
+		!_spinner->isOpen() &&
+		!_vk->isOpen() &&
+		!_elevator->isOpen();
+}
+
+Common::Error BladeRunnerEngine::saveGameState(int slot, const Common::String &desc) {
+	const Common::String saveName = Common::String::format("%s.%03d", _targetName.c_str(), slot);
+
+	Common::OutSaveFile *saveFile = g_system->getSavefileManager()->openForSaving(saveName);
+	if (saveFile == nullptr || saveFile->err()) {
+		delete saveFile;
+		return Common::kReadingFailed;
+	}
+
+	byte *thumbnail = new byte[SaveFile::kThumbnailSize];
+	generateThumbnail(thumbnail);
+
+	BladeRunner::SaveFileHeader header;
+	header._name = desc;
+	BladeRunner::SaveFile::writeHeader(*saveFile, header);
+
+	saveGame(*saveFile, thumbnail);
+
+	saveFile->finalize();
+
+	delete[] thumbnail;
+
+	delete saveFile;
+
+	return Common::kNoError;
 }
 
 Common::Error BladeRunnerEngine::run() {
@@ -315,8 +388,8 @@ bool BladeRunnerEngine::startup(bool hasSavegames) {
 	// Seed rand
 
 	// TODO: Sine and cosine lookup tables for intervals of 1.0, 4.0, and 12.0
-	_cosTable1024 = new Common::CosineTable(1024); // 10-bits = 1024 points for 2*PI;	
-	_sinTable1024 = new Common::SineTable(1024);	
+	_cosTable1024 = new Common::CosineTable(1024); // 10-bits = 1024 points for 2*PI;
+	_sinTable1024 = new Common::SineTable(1024);
 
 	_view = new View();
 
@@ -1640,22 +1713,16 @@ void BladeRunnerEngine::playerGainsControl() {
 	}
 }
 
-bool BladeRunnerEngine::saveGame(const Common::String &filename, byte *thumbnail) {
-	warning("BladeRunnerEngine::saveGame not finished");
-
+bool BladeRunnerEngine::saveGame(Common::WriteStream &stream, const void *thumbnail) {
 	if (!playerHasControl() || _sceneScript->isInsideScript() || _aiScripts->isInsideScript()) {
 		return false;
 	}
 
-	Common::OutSaveFile *commonSaveFile = getSaveFileManager()->openForSaving(filename, false);
-	if (commonSaveFile->err()) {
-		return false;
-	}
-
-	SaveFileWriteStream s;
+	Common::MemoryWriteStreamDynamic memoryStream(DisposeAfterUse::Flag::YES);
+	SaveFileWriteStream s(memoryStream);
 
-	s.padBytes(9600); // TODO: thumbnail
-	s.writeFloat(-1.0f);
+	s.write(thumbnail, SaveFile::kThumbnailSize);
+	s.writeFloat(1.0f);
 	_settings->save(s);
 	_scene->save(s);
 	_scene->_exits->save(s);
@@ -1691,49 +1758,43 @@ bool BladeRunnerEngine::saveGame(const Common::String &filename, byte *thumbnail
 		s.writeInt(nextAnimation);
 	}
 	_actors[kActorVoiceOver]->save(s);
-
 	_policeMaze->save(s);
 	_crimesDatabase->save(s);
 
 	s.finalize();
-	assert(0 && "ok");
 
-	commonSaveFile->writeUint32LE(s.size() + 4);
-	commonSaveFile->write(s.getData(), s.size());
+	stream.writeUint32LE(memoryStream.size() + 4);
+	stream.write(memoryStream.getData(), memoryStream.size());
+	stream.flush();
 
-	return !commonSaveFile->err();
+	return true;
 }
 
-void BladeRunnerEngine::loadGame(const Common::String &filename, byte *thumbnail) {
-	warning("BladeRunnerEngine::loadGame not finished");
-
+bool BladeRunnerEngine::loadGame(Common::SeekableReadStream &stream) {
 	if (!playerHasControl() || _sceneScript->isInsideScript() || _aiScripts->isInsideScript()) {
-		return;
-	}
-
-	Common::InSaveFile *commonSaveFile = getSaveFileManager()->openForLoading(filename);
-	if (commonSaveFile->err()) {
-		return;
+		return false;
 	}
 
-	void *buf = malloc(commonSaveFile->size());
-	int dataSize = commonSaveFile->read(buf, commonSaveFile->size());
-
-	SaveFileReadStream s((const byte*)buf, dataSize);
+	SaveFileReadStream s(stream);
 
 	_ambientSounds->removeAllNonLoopingSounds(true);
 	_ambientSounds->removeAllLoopingSounds(1);
 	_music->stop(2);
 	_audioSpeech->stopSpeech();
 	_actorDialogueQueue->flush(true, false);
+	_screenEffects->_entries.clear();
 
 	int size = s.readInt();
 
-	if (size != dataSize) {
-		return;
+	if (size != s.size() - s.pos() + 4) {
+		_gameIsLoading = false;
+		return false;
 	}
 
-	s.skip(9600); // thumbnail
+	_gameIsLoading = true;
+	_settings->setLoadingGame();
+	s.skip(SaveFile::kThumbnailSize); // skip the thumbnail
+	s.skip(4);// always float 1.0, but never used
 	_settings->load(s);
 	_scene->load(s);
 	_scene->_exits->load(s);
@@ -1757,7 +1818,6 @@ void BladeRunnerEngine::loadGame(const Common::String &filename, byte *thumbnail
 	_obstacles->load(s);
 	_actorDialogueQueue->load(s);
 	_waypoints->load(s);
-
 	for (uint i = 0; i != _gameInfo->getActorCount(); ++i) {
 		_actors[i]->load(s);
 
@@ -1768,23 +1828,71 @@ void BladeRunnerEngine::loadGame(const Common::String &filename, byte *thumbnail
 		_aiScripts->setAnimationState(i, animationState, animationFrame, animationStateNext, nextAnimation);
 	}
 	_actors[kActorVoiceOver]->load(s);
-
 	_policeMaze->load(s);
 	_crimesDatabase->load(s);
 
+	_gameIsLoading = false;
+
 	_settings->setNewSetAndScene(_settings->getSet(), _settings->getScene());
 	_settings->setChapter(_settings->getChapter());
+
+	return true;
+}
+
+void BladeRunnerEngine::newGame(int difficulty) {
+	_settings->reset();
+	_combat->reset();
+
+	for (uint i = 0; i < _gameInfo->getActorCount(); ++i) {
+		_actors[i]->setup(i);
+	}
+	_actors[kActorVoiceOver]->setup(99);
+
+	for (uint i = 0; i < _gameInfo->getSuspectCount(); ++i) {
+		_suspectsDatabase->get(i)->reset();
+	}
+
+	_gameFlags->clear();
+
+	_gameInfo->getGlobalVarCount();
+
+	for (uint i = 0; i < _gameInfo->getGlobalVarCount(); ++i) {
+		_gameVars[i] = 0;
+	}
+
+	_items->reset();
+	_scores->reset();
+	_kia->reset();
+	_dialogueMenu->clear();
+	_scene->_exits->enable();
+
+	if (difficulty >= 0 && difficulty < 3) {
+		_settings->setDifficulty(difficulty);
+	}
+
+	_settings->setStartingGame();
 }
 
 void BladeRunnerEngine::ISez(const Common::String &str) {
 	debug("\t%s", str.c_str());
 }
 
-void BladeRunnerEngine::blitToScreen(const Graphics::Surface &src) {
+void BladeRunnerEngine::blitToScreen(const Graphics::Surface &src) const {
 	_system->copyRectToScreen(src.getPixels(), src.pitch, 0, 0, src.w, src.h);
 	_system->updateScreen();
 }
 
+void BladeRunnerEngine::generateThumbnail(void *thumbnail) const {
+	uint16 *dstPixels = (uint16*)thumbnail;
+
+	for (int y = 0; y < 480; y += 8) {
+		for (int x = 0; x < 640; x += 8) {
+			*dstPixels = *(const uint16 *)_surfaceFront.getBasePtr(x, y);
+			++dstPixels;
+		}
+	}
+}
+
 GUI::Debugger *BladeRunnerEngine::getDebugger() {
 	return _debugger;
 }
diff --git a/engines/bladerunner/bladerunner.h b/engines/bladerunner/bladerunner.h
index 8d45d6a..817c306 100644
--- a/engines/bladerunner/bladerunner.h
+++ b/engines/bladerunner/bladerunner.h
@@ -24,6 +24,7 @@
 #define BLADERUNNER_BLADERUNNER_H
 
 #include "bladerunner/archive.h"
+#include "bladerunner/savefile.h"
 
 #include "common/array.h"
 #include "common/cosinetables.h"
@@ -37,7 +38,7 @@
 
 //TODO: remove these when game is playable
 #define BLADERUNNER_DEBUG_CONSOLE 0
-#define BLADERUNNER_DEBUG_GAME 0
+#define BLADERUNNER_DEBUG_GAME 1
 
 namespace Common {
 struct Event;
@@ -224,7 +225,11 @@ public:
 	BladeRunnerEngine(OSystem *syst, const ADGameDescription *desc);
 	~BladeRunnerEngine();
 
-	bool hasFeature(EngineFeature f) const;
+	bool hasFeature(EngineFeature f) const override;
+	bool canLoadGameStateCurrently() override;
+	Common::Error loadGameState(int slot) override;
+	bool canSaveGameStateCurrently() override;
+	Common::Error saveGameState(int slot, const Common::String &desc) override;
 
 	Common::Error run();
 
@@ -271,14 +276,15 @@ public:
 	void playerLosesControl();
 	void playerGainsControl();
 
-	bool saveGame(const Common::String &filename, byte *thumbnail);
-	void loadGame(const Common::String &filename, byte *thumbnail);
-	void newGame();
+	bool saveGame(Common::WriteStream &stream, const void *thumbnail);
+	bool loadGame(Common::SeekableReadStream &stream);
+	void newGame(int difficulty);
 	void autoSaveGame();
 
 	void ISez(const Common::String &str);
 
-	void blitToScreen(const Graphics::Surface &src);
+	void blitToScreen(const Graphics::Surface &src) const;
+	void generateThumbnail(void *thumbnail) const;
 
 	GUI::Debugger *getDebugger();
 };
diff --git a/engines/bladerunner/crimes_database.cpp b/engines/bladerunner/crimes_database.cpp
index e9faa22..91def98 100644
--- a/engines/bladerunner/crimes_database.cpp
+++ b/engines/bladerunner/crimes_database.cpp
@@ -73,14 +73,14 @@ const char *CrimesDatabase::getClueText(int clueId) const {
 
 void CrimesDatabase::save(SaveFileWriteStream &f) {
 	for (int i = 0; i < _crimeCount; ++i) {
-		uint8 c = _crimes[i];
-		f.writeByte(c);
+		int8 c = _crimes[i];
+		f.writeSByte(c);
 	}
 }
 
 void CrimesDatabase::load(SaveFileReadStream &f) {
 	for (int i = 0; i < _crimeCount; ++i) {
-		_crimes[i] = f.readByte();
+		_crimes[i] = f.readSByte();
 	}
 }
 
diff --git a/engines/bladerunner/debugger.cpp b/engines/bladerunner/debugger.cpp
index 0355f9d..6f7e5d1 100644
--- a/engines/bladerunner/debugger.cpp
+++ b/engines/bladerunner/debugger.cpp
@@ -71,6 +71,8 @@ Debugger::Debugger(BladeRunnerEngine *vm) : GUI::Debugger() {
 	registerCmd("say", WRAP_METHOD(Debugger, cmdSay));
 	registerCmd("scene", WRAP_METHOD(Debugger, cmdScene));
 	registerCmd("var", WRAP_METHOD(Debugger, cmdVariable));
+	registerCmd("load", WRAP_METHOD(Debugger, cmdLoad));
+	registerCmd("save", WRAP_METHOD(Debugger, cmdSave));
 }
 
 Debugger::~Debugger() {
@@ -358,6 +360,59 @@ bool Debugger::cmdVariable(int argc, const char **argv) {
 	return true;
 }
 
+bool Debugger::cmdLoad(int argc, const char **argv) {
+	if (argc != 2) {
+		debugPrintf("Loads a save game from original format.\n");
+		debugPrintf("Usage: %s <file path>\n", argv[0]);
+		return true;
+	}
+
+	Common::FSNode fs(argv[1]);
+
+	if (!fs.isReadable()) {
+		debugPrintf("Warning: File %s does not exist or is not readable\n", argv[1]);
+		return true;
+	}
+
+	Common::SeekableReadStream *saveFile = fs.createReadStream();
+
+	_vm->loadGame(*saveFile);
+
+	delete saveFile;
+
+	return false;
+}
+
+bool Debugger::cmdSave(int argc, const char **argv) {
+	if (argc != 2) {
+		debugPrintf("Saves game to original format.\n");
+		debugPrintf("Usage: %s <file path>\n", argv[0]);
+		return true;
+	}
+
+	Common::FSNode fs(argv[1]);
+
+	if (fs.exists() && !fs.isWritable()) {
+		debugPrintf("Warning: File %s is not writable\n", argv[1]);
+		return true;
+	}
+
+	Common::WriteStream *saveFile = fs.createWriteStream();
+
+	uint16 *thumbnail = new uint16[SaveFile::kThumbnailSize];
+	_vm->generateThumbnail(thumbnail);
+
+	_vm->saveGame(*saveFile, thumbnail);
+
+	saveFile->finalize();
+
+	delete[] thumbnail;
+
+	delete saveFile;
+
+	return true;
+}
+
 void Debugger::drawBBox(Vector3 start, Vector3 end, View *view, Graphics::Surface *surface, int color) {
 	Vector3 bfl = view->calculateScreenPosition(Vector3(start.x, start.y, start.z));
 	Vector3 bfr = view->calculateScreenPosition(Vector3(start.x, end.y, start.z));
diff --git a/engines/bladerunner/debugger.h b/engines/bladerunner/debugger.h
index 64e312a..fef545e 100644
--- a/engines/bladerunner/debugger.h
+++ b/engines/bladerunner/debugger.h
@@ -59,6 +59,8 @@ public:
 	bool cmdSay(int argc, const char **argv);
 	bool cmdScene(int argc, const char **argv);
 	bool cmdVariable(int argc, const char **argv);
+	bool cmdLoad(int argc, const char **argv);
+	bool cmdSave(int argc, const char **argv);
 
 	void drawBBox(Vector3 start, Vector3 end, View *view, Graphics::Surface *surface, int color);
 	void drawSceneObjects();
diff --git a/engines/bladerunner/detection.cpp b/engines/bladerunner/detection.cpp
index 934eea7..8cd4288 100644
--- a/engines/bladerunner/detection.cpp
+++ b/engines/bladerunner/detection.cpp
@@ -20,12 +20,17 @@
  *
  */
 
-#include "base/plugins.h"
-
-#include "engines/advancedDetector.h"
 
 #include "bladerunner/bladerunner.h"
 #include "bladerunner/detection_tables.h"
+#include "bladerunner/savefile.h"
+
+#include "common/config-manager.h"
+#include "common/system.h"
+#include "common/savefile.h"
+#include "common/serializer.h"
+
+#include "engines/advancedDetector.h"
 
 namespace BladeRunner {
 
@@ -38,19 +43,32 @@ static const PlainGameDescriptor bladeRunnerGames[] = {
 
 class BladeRunnerMetaEngine : public AdvancedMetaEngine {
 public:
-	BladeRunnerMetaEngine() : AdvancedMetaEngine(BladeRunner::gameDescriptions, sizeof(BladeRunner::gameDescriptions[0]), BladeRunner::bladeRunnerGames) {
-	}
+	BladeRunnerMetaEngine();
 
-	virtual const char *getName() const {
-		return "Blade Runner Engine";
-	}
+	const char *getName() const override;
+	const char *getOriginalCopyright() const override;
+	bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
+	bool hasFeature(MetaEngineFeature f) const override;
+	SaveStateList listSaves(const char *target) const override;
+	int getMaximumSaveSlot() const override;
+	void removeSaveState(const char *target, int slot) const override;
+	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
+};
 
-	virtual const char *getOriginalCopyright() const {
-		return "Blade Runner (C) Westwood Studios.";
-	}
+BladeRunnerMetaEngine::BladeRunnerMetaEngine()
+	: AdvancedMetaEngine(
+		BladeRunner::gameDescriptions,
+		sizeof(BladeRunner::gameDescriptions[0]),
+		BladeRunner::bladeRunnerGames) {}
 
-	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
-};
+
+const char *BladeRunnerMetaEngine::getName() const {
+	return "Blade Runner Engine";
+}
+
+const char *BladeRunnerMetaEngine::getOriginalCopyright() const {
+	return "Blade Runner (C) 1997 Westwood Studios";
+}
 
 bool BladeRunnerMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
 	*engine = new BladeRunner::BladeRunnerEngine(syst, desc);
@@ -58,6 +76,71 @@ bool BladeRunnerMetaEngine::createInstance(OSystem *syst, Engine **engine, const
 	return true;
 }
 
+bool BladeRunnerMetaEngine::hasFeature(MetaEngineFeature f) const {
+	return
+		f == kSupportsListSaves ||
+		f == kSupportsLoadingDuringStartup ||
+		f == kSupportsDeleteSave ||
+		f == kSavesSupportMetaInfo ||
+		f == kSavesSupportThumbnail ||
+		f == kSimpleSavesNames;
+}
+
+SaveStateList BladeRunnerMetaEngine::listSaves(const char *target) const {
+	Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+	Common::StringArray files = saveFileMan->listSavefiles(Common::String::format("%s.###", target));
+
+	SaveStateList saveList;
+	for (Common::StringArray::const_iterator fileName = files.begin(); fileName != files.end(); ++fileName) {
+		Common::InSaveFile *saveFile = saveFileMan->openForLoading(*fileName);
+		if (saveFile == nullptr || saveFile->err()) {
+			warning("Cannot open save file '%s'", fileName->c_str());
+			continue;
+		}
+
+		BladeRunner::SaveFileHeader header;
+		BladeRunner::SaveFile::readHeader(*saveFile, header);
+
+		int slotNum = atoi(fileName->c_str() + fileName->size() - 3);
+		saveList.push_back(SaveStateDescriptor(slotNum, header._name));
+	}
+
+	// Sort saves based on slot number.
+	Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
+	return saveList;
+}
+
+int BladeRunnerMetaEngine::getMaximumSaveSlot() const {
+	return 999;
+}
+
+void BladeRunnerMetaEngine::removeSaveState(const char *target, int slot) const {
+	Common::String filename = Common::String::format("%s.%03d", target, slot);
+	g_system->getSavefileManager()->removeSavefile(filename);
+}
+
+SaveStateDescriptor BladeRunnerMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+	Common::String filename = Common::String::format("%s.%03d", target, slot);
+	Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(filename);
+
+	if (saveFile == nullptr || saveFile->err()) {
+		return SaveStateDescriptor();
+	}
+
+	BladeRunner::SaveFileHeader header;
+	if (!BladeRunner::SaveFile::readHeader(*saveFile, header, false)) {
+		delete saveFile;
+		return SaveStateDescriptor();
+	}
+	delete saveFile;
+
+	SaveStateDescriptor desc(slot, header._name);
+	desc.setThumbnail(header._thumbnail);
+	desc.setSaveDate(header._year, header._month, header._day);
+	desc.setSaveTime(header._hour, header._minute);
+	return desc;
+}
+
 #if PLUGIN_ENABLED_DYNAMIC(BLADERUNNER)
 	REGISTER_PLUGIN_DYNAMIC(BLADERUNNER, PLUGIN_TYPE_ENGINE, BladeRunnerMetaEngine);
 #else
diff --git a/engines/bladerunner/detection_tables.h b/engines/bladerunner/detection_tables.h
index 3c509f4..5d1a851 100644
--- a/engines/bladerunner/detection_tables.h
+++ b/engines/bladerunner/detection_tables.h
@@ -23,6 +23,8 @@
 #ifndef BLADERUNNER_DETECTION_TABLES_H
 #define BLADERUNNER_DETECTION_TABLES_H
 
+#include "engines/advancedDetector.h"
+
 namespace BladeRunner {
 
 static const ADGameDescription gameDescriptions[] = {
diff --git a/engines/bladerunner/dialogue_menu.cpp b/engines/bladerunner/dialogue_menu.cpp
index d9a7c62..4f82326 100644
--- a/engines/bladerunner/dialogue_menu.cpp
+++ b/engines/bladerunner/dialogue_menu.cpp
@@ -183,7 +183,7 @@ int DialogueMenu::queryInput() {
 				}
 
 				_vm->gameTick();
-			} while (_waitingForInput);
+			} while (_vm->_gameIsRunning && _waitingForInput);
 		} else if (agenda == kPlayerAgendaErratic) {
 			int tries = 0;
 			bool searching = true;
diff --git a/engines/bladerunner/dialogue_menu.h b/engines/bladerunner/dialogue_menu.h
index 6dfd3ef..4029fe4 100644
--- a/engines/bladerunner/dialogue_menu.h
+++ b/engines/bladerunner/dialogue_menu.h
@@ -81,6 +81,8 @@ public:
 	DialogueMenu(BladeRunnerEngine *vm);
 	~DialogueMenu();
 
+	void clear();
+
 	bool loadText(const Common::String &name);
 
 	bool show();
@@ -106,7 +108,6 @@ private:
 	int  getAnswerIndex(int answer) const;
 	const char *getText(int id) const;
 	void calculatePosition(int unusedX = 0, int unusedY = 0);
-	void clear();
 	void reset();
 
 	static void darkenRect(Graphics::Surface &s, int x1, int y1, int x2, int y2);
diff --git a/engines/bladerunner/game_flags.cpp b/engines/bladerunner/game_flags.cpp
index 6fdcb89..e2ae3c3 100644
--- a/engines/bladerunner/game_flags.cpp
+++ b/engines/bladerunner/game_flags.cpp
@@ -36,14 +36,19 @@ GameFlags::~GameFlags() {
 	delete[] _flags;
 }
 
+void GameFlags::clear() {
+	for (int i = 0; i <= _flagCount; ++i) {
+		reset(i);
+	}
+}
+
 void GameFlags::setFlagCount(int count) {
 	assert(count > 0);
 
 	_flagCount = count;
 	_flags = new uint32[count / 32 + 1]();
 
-	for (int i = 0; i <= _flagCount; ++i)
-		reset(i);
+	clear();
 }
 
 void GameFlags::set(int flag) {
diff --git a/engines/bladerunner/game_flags.h b/engines/bladerunner/game_flags.h
index 837537f..3b0d93f 100644
--- a/engines/bladerunner/game_flags.h
+++ b/engines/bladerunner/game_flags.h
@@ -38,6 +38,7 @@ public:
 	GameFlags();
 	~GameFlags();
 
+	void clear();
 	void setFlagCount(int count);
 
 	void set(int flag);
diff --git a/engines/bladerunner/item.cpp b/engines/bladerunner/item.cpp
index 2f82fc0..751978b 100644
--- a/engines/bladerunner/item.cpp
+++ b/engines/bladerunner/item.cpp
@@ -177,7 +177,7 @@ bool Item::isUnderMouse(int mouseX, int mouseY) const {
 void Item::save(SaveFileWriteStream &f) {
 	f.writeInt(_setId);
 	f.writeInt(_itemId);
-	f.writeBoundingBox(_boundingBox);
+	f.writeBoundingBox(_boundingBox, false);
 	f.writeRect(_screenRectangle);
 	f.writeInt(_animationId);
 	f.writeVector3(_position);
@@ -199,7 +199,7 @@ void Item::save(SaveFileWriteStream &f) {
 void Item::load(SaveFileReadStream &f) {
 	_setId = f.readInt();
 	_itemId = f.readInt();
-	_boundingBox = f.readBoundingBox();
+	_boundingBox = f.readBoundingBox(false);
 	_screenRectangle = f.readRect();
 	_animationId = f.readInt();
 	_position = f.readVector3();
diff --git a/engines/bladerunner/items.cpp b/engines/bladerunner/items.cpp
index a2cb9da..3259451 100644
--- a/engines/bladerunner/items.cpp
+++ b/engines/bladerunner/items.cpp
@@ -35,6 +35,10 @@ Items::Items(BladeRunnerEngine *vm) {
 }
 
 Items::~Items() {
+	reset();
+}
+
+void Items::reset() {
 	for (int i = _items.size() - 1; i >= 0; i--) {
 		delete _items.remove_at(i);
 	}
diff --git a/engines/bladerunner/items.h b/engines/bladerunner/items.h
index ce29a77..a2c7720 100644
--- a/engines/bladerunner/items.h
+++ b/engines/bladerunner/items.h
@@ -42,6 +42,8 @@ public:
 	Items(BladeRunnerEngine *vm);
 	~Items();
 
+	void reset();
+
 	void getXYZ(int itemId, float *x, float *y, float *z) const;
 	void setXYZ(int itemId, Vector3 position);
 	void getWidthHeight(int itemId, int *width, int *height) const;
diff --git a/engines/bladerunner/outtake.cpp b/engines/bladerunner/outtake.cpp
index 6d76162..187f46c 100644
--- a/engines/bladerunner/outtake.cpp
+++ b/engines/bladerunner/outtake.cpp
@@ -44,9 +44,9 @@ void OuttakePlayer::play(const Common::String &name, bool noLocalization, int co
 
 	resName = resName + ".VQA";
 
-	VQAPlayer vqa_player(_vm, &_vm->_surfaceFront);
+	VQAPlayer vqa_player(_vm, &_vm->_surfaceFront, resName);
 
-	vqa_player.open(resName);
+	vqa_player.open();
 
 	_vm->_mixer->stopAll();
 	while (!_vm->shouldQuit()) {
diff --git a/engines/bladerunner/overlays.cpp b/engines/bladerunner/overlays.cpp
index 65ba83f..5bbac87 100644
--- a/engines/bladerunner/overlays.cpp
+++ b/engines/bladerunner/overlays.cpp
@@ -69,14 +69,15 @@ int Overlays::play(const Common::String &name, int loopId, bool loopForever, boo
 		_videos[index].loaded = true;
 		_videos[index].name = name;
 		_videos[index].hash = hash;
-		_videos[index].vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceFront);
+		_videos[index].loopId = loopId;
+		_videos[index].loopForever = loopForever;
+		_videos[index].vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceFront, Common::String::format("%s.VQA", name.c_str()));
 
 		// repeat forever
 		_videos[index].vqaPlayer->setBeginAndEndFrame(0, 0, -1, kLoopSetModeJustStart, nullptr, nullptr);
 	}
 
-	Common::String resourceName = Common::String::format("%s.VQA", name.c_str());
-	_videos[index].vqaPlayer->open(resourceName);
+	_videos[index].vqaPlayer->open();
 	_videos[index].vqaPlayer->setLoop(
 		loopId,
 		loopForever ? -1 : 0,
@@ -86,6 +87,29 @@ int Overlays::play(const Common::String &name, int loopId, bool loopForever, boo
 	return index;
 }
 
+void Overlays::resume(bool isLoadingGame) {
+
+	for (int i = 0; i < kOverlayVideos; ++i) {
+		if (_videos[i].loaded && isLoadingGame) {
+			_videos[i].vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceFront, Common::String::format("%s.VQA", _videos[i].name.c_str()));
+			if (!_videos[i].vqaPlayer) {
+				resetSingle(i);
+				continue;
+			}
+
+			_videos[i].vqaPlayer->open();
+			_videos[i].vqaPlayer->setLoop(
+				_videos[i].loopId,
+				_videos[i].loopForever ? -1 : 0,
+				kLoopSetModeImmediate,
+				nullptr, nullptr);
+
+			_videos[i].vqaPlayer->seekToFrame(_videos[i].frame);
+			_videos[i].vqaPlayer->update(true);
+		}
+	}
+}
+
 void Overlays::remove(const Common::String &name) {
 	int index = findByHash(MIXArchive::getHash(name));
 	if (index >= 0) {
@@ -104,8 +128,8 @@ void Overlays::removeAll() {
 void Overlays::tick() {
 	for (int i = 0; i < kOverlayVideos; ++i) {
 		if (_videos[i].loaded) {
-			int frame = _videos[i].vqaPlayer->update(true);
-			if (frame < 0) {
+			_videos[i].frame = _videos[i].vqaPlayer->update(true);
+			if (_videos[i].frame < 0) {
 				resetSingle(i);
 			}
 		}
@@ -138,7 +162,7 @@ void Overlays::resetSingle(int i) {
 	}
 	_videos[i].loaded = false;
 	_videos[i].hash = 0;
-	_videos[i].field2 = -1;
+	_videos[i].frame = -1;
 	_videos[i].name.clear();
 }
 
@@ -155,9 +179,9 @@ void Overlays::save(SaveFileWriteStream &f) {
 		f.writeInt(0); // vqaPlayer pointer
 		f.writeStringSz(ov.name, 13);
 		f.writeSint32LE(ov.hash);
-		f.writeInt(ov.field0);
-		f.writeInt(ov.field1);
-		f.writeInt(ov.field2);
+		f.writeInt(ov.loopId);
+		f.writeBool(ov.loopForever);
+		f.writeInt(ov.frame);
 	}
 }
 
@@ -171,9 +195,9 @@ void Overlays::load(SaveFileReadStream &f) {
 		ov.vqaPlayer = nullptr;
 		ov.name = f.readStringSz(13);
 		ov.hash = f.readSint32LE();
-		ov.field0 = f.readInt();
-		ov.field1 = f.readInt();
-		ov.field2 = f.readInt();
+		ov.loopId = f.readInt();
+		ov.loopForever = f.readBool();
+		ov.frame = f.readInt();
 	}
 }
 
diff --git a/engines/bladerunner/overlays.h b/engines/bladerunner/overlays.h
index 405acbc..e250db9 100644
--- a/engines/bladerunner/overlays.h
+++ b/engines/bladerunner/overlays.h
@@ -45,9 +45,9 @@ class Overlays {
 		VQAPlayer      *vqaPlayer;
 		Common::String  name;
 		int32           hash;
-		int             field0;
-		int             field1;
-		int             field2;
+		int             loopId;
+		bool            loopForever;
+		int             frame;
 	};
 
 	BladeRunnerEngine *_vm;
@@ -59,6 +59,7 @@ public:
 	~Overlays();
 
 	int play(const Common::String &name, int loopId, bool loopForever, bool startNow, int a6);
+	void resume(bool isLoadingGame);
 	void remove(const Common::String &name);
 	void removeAll();
 	void tick();
diff --git a/engines/bladerunner/savefile.cpp b/engines/bladerunner/savefile.cpp
index 3528a6b..b0fb1e7 100644
--- a/engines/bladerunner/savefile.cpp
+++ b/engines/bladerunner/savefile.cpp
@@ -27,13 +27,76 @@
 
 #include "common/rect.h"
 #include "common/savefile.h"
+#include "common/system.h"
+
+#include "graphics/thumbnail.h"
 
 namespace BladeRunner {
 
-SaveFileWriteStream::SaveFileWriteStream()
-	: MemoryWriteStreamDynamic(DisposeAfterUse::YES) {
+bool SaveFile::readHeader(Common::SeekableReadStream &in, SaveFileHeader &header, bool skipThumbnail) {
+	SaveFileReadStream s(in);
+
+	if (s.readUint32BE() != kTag) {
+		warning("No header found in save file");
+		return false;
+	}
+
+	header._version = s.readByte();
+	if (header._version != kVersion) {
+		warning("Unsupported version of save file %u, supported is %u", header._version, kVersion);
+		return false;
+	}
+
+	header._name = s.readStringSz(kNameLength);
+
+	header._year   = s.readUint16LE();
+	header._month  = s.readUint16LE();
+	header._day    = s.readUint16LE();
+	header._hour   = s.readUint16LE();
+	header._minute = s.readUint16LE();
+
+	header._thumbnail = nullptr;
+
+	if (!skipThumbnail) {
+		header._thumbnail = new Graphics::Surface();
+
+		int32 pos = s.pos();
+
+		s.skip(4); //skip size;
+
+		void *thumbnailData = new byte[kThumbnailSize];
+		s.read(thumbnailData, kThumbnailSize);
+
+		// TODO: cleanup - remove magic constants
+		header._thumbnail->init(80, 60, 160, thumbnailData, Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0));
+
+		s.seek(pos);
+	}
+
+	return true;
+}
+
+bool SaveFile::writeHeader(Common::WriteStream &out, SaveFileHeader &header) {
+	SaveFileWriteStream s(out);
+
+	s.writeUint32BE(kTag);
+	s.writeByte(kVersion);
+
+	s.writeStringSz(header._name, kNameLength);
+
+	TimeDate td;
+	g_system->getTimeAndDate(td);
+	s.writeUint16LE(td.tm_year + 1900);
+	s.writeUint16LE(td.tm_mon + 1);
+	s.writeUint16LE(td.tm_mday);
+	s.writeUint16LE(td.tm_hour);
+	s.writeUint16LE(td.tm_min);
+
+	return true;
 }
 
+SaveFileWriteStream::SaveFileWriteStream(Common::WriteStream &s) : _s(s) {}
+
 void SaveFileWriteStream::debug(char *p) {
 	write(p, strlen(p) + 1);
 }
@@ -48,7 +111,7 @@ void SaveFileWriteStream::writeInt(int v) {
 	writeUint32LE(v);
 }
 
-void SaveFileWriteStream::writeFloat(int v) {
+void SaveFileWriteStream::writeFloat(float v) {
 	writeFloatLE(v);
 }
 
@@ -80,7 +143,7 @@ void SaveFileWriteStream::writeRect(const Common::Rect &v) {
 	writeUint32LE(v.bottom);
 }
 
-void SaveFileWriteStream::writeBoundingBox(const BoundingBox &v) {
+void SaveFileWriteStream::writeBoundingBox(const BoundingBox &v, bool serialized) {
 	float x0, y0, z0, x1, y1, z1;
 
 	v.getXYZ(&x0, &y0, &z0, &x1, &y1, &z1);
@@ -92,14 +155,13 @@ void SaveFileWriteStream::writeBoundingBox(const BoundingBox &v) {
 	writeFloatLE(z1);
 
 	// Bounding boxes have a lot of extra data that's never actually used
-	for (int i = 0; i != 96; ++i) {
+	int count = serialized ? 96 : 64;
+	for (int i = 0; i < count; ++i) {
 		writeFloatLE(0.0f);
 	}
 }
 
-SaveFileReadStream::SaveFileReadStream(const byte *dataPtr, uint32 dataSize)
-	: MemoryReadStream(dataPtr, dataSize, DisposeAfterUse::YES) {
-}
+SaveFileReadStream::SaveFileReadStream(Common::SeekableReadStream &s) : _s(s) {}
 
 int SaveFileReadStream::readInt() {
 	return readUint32LE();
@@ -114,10 +176,10 @@ bool SaveFileReadStream::readBool() {
 }
 
 Common::String SaveFileReadStream::readStringSz(int sz) {
-	char *buf = (char *)malloc(sz);
+	char *buf = new char[sz];
 	read(buf, sz);
 	Common::String result = buf;
-	free(buf);
+	delete[] buf;
 	return result;
 }
 
@@ -145,18 +207,22 @@ Common::Rect SaveFileReadStream::readRect() {
 	return result;
 }
 
-BoundingBox SaveFileReadStream::readBoundingBox() {
+BoundingBox SaveFileReadStream::readBoundingBox(bool serialized) {
 	float x0, y0, z0, x1, y1, z1;
 
 	x0 = readFloatLE();
 	y0 = readFloatLE();
 	z0 = readFloatLE();
+
 	x1 = readFloatLE();
 	y1 = readFloatLE();
 	z1 = readFloatLE();
 
-	// Bounding boxes have a lot of extra data that's never actually used
-	skip(384);
+	// Bounding boxes have a lot of extra data that's never actually used, and there two formats for storing bounding boxes.
+	int count = serialized ? 96 : 64;
+	for (int i = 0; i < count; ++i) {
+		readFloatLE();
+	}
 
 	return BoundingBox(x0, y0, z0, x1, y1, z1);
 }
diff --git a/engines/bladerunner/savefile.h b/engines/bladerunner/savefile.h
index 4dfdb20..bcd1619 100644
--- a/engines/bladerunner/savefile.h
+++ b/engines/bladerunner/savefile.h
@@ -26,6 +26,8 @@
 #include "common/memstream.h"
 #include "common/types.h"
 
+#include "graphics/surface.h"
+
 namespace Common {
 class OutSaveFile;
 class String;
@@ -38,27 +40,67 @@ class Vector2;
 class Vector3;
 class BoundingBox;
 
-class SaveFileWriteStream : public Common::MemoryWriteStreamDynamic {
+struct SaveFileHeader {
+	uint8              _version;
+	Common::String     _name;
+	int                _year;
+	int                _month;
+	int                _day;
+	int                _hour;
+	int                _minute;
+	Graphics::Surface *_thumbnail;
+};
+
+class SaveFile {
+private:
+	static const uint32 kTag = MKTAG('B', 'R', 'S', 'V');
+	static const uint32 kVersion = 1;
+	static const uint32 kNameLength = 32;
+
+public:
+	static const uint32 kThumbnailSize = 9600; // 80x60x16bpp
+
+	static bool readHeader(Common::SeekableReadStream &in, SaveFileHeader &header, bool skipThumbnail = true);
+	static bool writeHeader(Common::WriteStream &out, SaveFileHeader &header);
+};
+
+class SaveFileWriteStream : public Common::WriteStream {
+private:
+	Common::WriteStream &_s;
+
 public:
-	SaveFileWriteStream();
+	SaveFileWriteStream(Common::WriteStream &s);
+
+	uint32 write(const void *dataPtr, uint32 dataSize) override { return _s.write(dataPtr, dataSize); }
+	bool flush() override { return _s.flush(); }
+	int32 pos() const override { return _s.pos(); }
 
 	void debug(char *p);
 
 	void padBytes(int count);
 
 	void writeInt(int v);
-	void writeFloat(int v);
+	void writeFloat(float v);
 	void writeBool(bool v);
 	void writeStringSz(const Common::String &s, int sz);
 	void writeVector2(const Vector2 &v);
 	void writeVector3(const Vector3 &v);
 	void writeRect(const Common::Rect &v);
-	void writeBoundingBox(const BoundingBox &v);
+	void writeBoundingBox(const BoundingBox &v, bool serialized);
 };
 
-class SaveFileReadStream : public Common::MemoryReadStream {
+class SaveFileReadStream : public Common::SeekableReadStream {
+private:
+	Common::SeekableReadStream &_s;
+
 public:
-	SaveFileReadStream(const byte *dataPtr, uint32 dataSize);
+	SaveFileReadStream(Common::SeekableReadStream &s);
+
+	bool eos() const override { return _s.eos(); }
+	uint32 read(void *dataPtr, uint32 dataSize) override { return _s.read(dataPtr, dataSize); }
+	int32 pos() const override { return _s.pos(); }
+	int32 size() const override { return _s.size(); }
+	bool seek(int32 offset, int whence = SEEK_SET) override { return _s.seek(offset, whence); }
 
 	int readInt();
 	float readFloat();
@@ -67,7 +109,7 @@ public:
 	Vector2 readVector2();
 	Vector3 readVector3();
 	Common::Rect readRect();
-	BoundingBox readBoundingBox();
+	BoundingBox readBoundingBox(bool serialized);
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/scene.cpp b/engines/bladerunner/scene.cpp
index fe8dbc7..09764c9 100644
--- a/engines/bladerunner/scene.cpp
+++ b/engines/bladerunner/scene.cpp
@@ -87,7 +87,7 @@ bool Scene::open(int setId, int sceneId, bool isLoadingGame) {
 	const Common::String sceneName = _vm->_gameInfo->getSceneName(_sceneId);
 
 	if (isLoadingGame) {
-		// TODO: _vm->overlays->resume()
+		_vm->_overlays->resume(true);
 	} else {
 		_regions->clear();
 		_exits->clear();
@@ -113,7 +113,7 @@ bool Scene::open(int setId, int sceneId, bool isLoadingGame) {
 		delete _vqaPlayer;
 	}
 
-	_vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack);
+	_vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack, vqaName);
 
 	if (!_vm->_sceneScript->open(sceneName)) {
 		return false;
@@ -138,7 +138,7 @@ bool Scene::open(int setId, int sceneId, bool isLoadingGame) {
 		return true;
 	}
 
-	if (!_vqaPlayer->open(vqaName)) {
+	if (!_vqaPlayer->open()) {
 		return false;
 	}
 
@@ -253,7 +253,9 @@ void Scene::resume(bool isLoadingGame) {
 
 	int targetFrame = _frame;
 
-	if (!isLoadingGame) {
+	if (isLoadingGame) {
+		_vqaPlayer->open();
+	} else {
 		_vm->_zbuffer->disable();
 	}
 
diff --git a/engines/bladerunner/scene_objects.cpp b/engines/bladerunner/scene_objects.cpp
index 30802a8..51538cd 100644
--- a/engines/bladerunner/scene_objects.cpp
+++ b/engines/bladerunner/scene_objects.cpp
@@ -331,7 +331,7 @@ void SceneObjects::save(SaveFileWriteStream &f) {
 	for (int i = 0; i < kSceneObjectCount; ++i) {
 		f.writeInt(_sceneObjects[i].id);
 		f.writeInt(_sceneObjects[i].type);
-		f.writeBoundingBox(_sceneObjects[i].boundingBox);
+		f.writeBoundingBox(_sceneObjects[i].boundingBox, true);
 		f.writeRect(_sceneObjects[i].screenRectangle);
 		f.writeFloat(_sceneObjects[i].distanceToCamera);
 		f.writeBool(_sceneObjects[i].isPresent);
@@ -352,7 +352,8 @@ void SceneObjects::load(SaveFileReadStream &f) {
 	for (int i = 0; i < kSceneObjectCount; ++i) {
 		_sceneObjects[i].id = f.readInt();
 		_sceneObjects[i].type = (SceneObjectType)f.readInt();
-		_sceneObjects[i].boundingBox = f.readBoundingBox();
+		_sceneObjects[i].boundingBox = f.readBoundingBox(true);
+		debug("screenRectangle[%i]: %08x", i, f.pos());
 		_sceneObjects[i].screenRectangle = f.readRect();
 		_sceneObjects[i].distanceToCamera = f.readFloat();
 		_sceneObjects[i].isPresent = f.readBool();
diff --git a/engines/bladerunner/script/ai_script.cpp b/engines/bladerunner/script/ai_script.cpp
index b328824..6addc43 100644
--- a/engines/bladerunner/script/ai_script.cpp
+++ b/engines/bladerunner/script/ai_script.cpp
@@ -364,7 +364,6 @@ void AIScripts::queryAnimationState(int actor, int *animationState, int *animati
 
 	_inScriptCounter++;
 	if (_AIScripts[actor]) {
-		_AIScripts[actor]->FledCombat();
 		_AIScripts[actor]->QueryAnimationState(animationState, animationFrame, animationStateNext, animationNext);
 	}
 	_inScriptCounter--;
diff --git a/engines/bladerunner/script/police_maze.cpp b/engines/bladerunner/script/police_maze.cpp
index a85fa07..284b55d 100644
--- a/engines/bladerunner/script/police_maze.cpp
+++ b/engines/bladerunner/script/police_maze.cpp
@@ -175,7 +175,7 @@ void PoliceMazeTargetTrack::clear(bool isLoadingGame) {
 void PoliceMazeTargetTrack::add(int trackId, float startX, float startY, float startZ, float endX, float endY, float endZ, int steps, const int *instructions, bool isActive) {
 	_data = (const int *)instructions;
 
-	if (true /* !GameIsLoading */) { // TODO: FIXME
+	if (!_vm->_gameIsLoading) {
 		_itemId = trackId;
 		_pointCount = steps;
 		_dataIndex = 0;
diff --git a/engines/bladerunner/script/scene/rc01.cpp b/engines/bladerunner/script/scene/rc01.cpp
index db89807..b217d03 100644
--- a/engines/bladerunner/script/scene/rc01.cpp
+++ b/engines/bladerunner/script/scene/rc01.cpp
@@ -50,7 +50,7 @@ void SceneScriptRC01::InitializeScene() {
 #if BLADERUNNER_DEBUG_GAME
 	//TODO: not part of game, remove
 	Game_Flag_Set(kFlagIntroPlayed); // force skip intro
-	 Game_Flag_Set(kFlagRC02toRC01); // no landing
+	Game_Flag_Set(kFlagRC02toRC01); // no landing
 	// Game_Flag_Set(kFlagRC01PoliceDone);
 	// Game_Flag_Set(kFlagKIAPrivacyAddon);
 	// Game_Flag_Set(kFlagZubenRetired);
@@ -74,7 +74,7 @@ void SceneScriptRC01::InitializeScene() {
 	// Global_Variable_Set(kVariableChapter, 2);
 	// Chapter_Enter(2, kSetRC03, kSceneRC03);
 
-	Set_Enter(14, 73);
+	// Set_Enter(14, 73);
 
 #endif
 
diff --git a/engines/bladerunner/script/script.cpp b/engines/bladerunner/script/script.cpp
index 92fbc2b..9d9243a 100644
--- a/engines/bladerunner/script/script.cpp
+++ b/engines/bladerunner/script/script.cpp
@@ -589,7 +589,7 @@ void ScriptBase::Loop_Actor_Travel_Stairs(int actorId, int stepCount, bool up, i
 				break;
 			}
 		}
-	} while (true);
+	} while (_vm->_gameIsRunning);
 	actor->setImmunityToObstacles(immunityToObstacles);
 
 	actor->setAtXYZ(Vector3(actor->getX(), targetY, actor->getZ()), actor->getFacing(), true, false, false);
@@ -632,7 +632,7 @@ void ScriptBase::Loop_Actor_Travel_Ladder(int actorId, int stepCount, bool up, i
 				break;
 			}
 		}
-	} while (true);
+	} while (_vm->_gameIsRunning);
 	actor->setImmunityToObstacles(immunityToObstacles);
 
 	actor->setAtXYZ(Vector3(actor->getX(), targetY, actor->getZ()), actor->getFacing(), true, false, false);
@@ -730,7 +730,7 @@ int ScriptBase::Animation_Skip_To_Frame() {
 void ScriptBase::Delay(int miliseconds) {
 	Player_Loses_Control();
 	int endTime = _vm->getTotalPlayTime() + miliseconds;
-	while ((int)_vm->getTotalPlayTime() < endTime) {
+	while (_vm->_gameIsRunning && (int)_vm->getTotalPlayTime() < endTime) {
 		_vm->gameTick();
 	}
 	Player_Gains_Control();
diff --git a/engines/bladerunner/set.cpp b/engines/bladerunner/set.cpp
index ac026a4..8111d0b 100644
--- a/engines/bladerunner/set.cpp
+++ b/engines/bladerunner/set.cpp
@@ -340,7 +340,7 @@ void Set::save(SaveFileWriteStream &f) {
 
 	for (int i = 0; i != _objectCount; ++i) {
 		f.writeStringSz(_objects[i].name, 20);
-		f.writeBoundingBox(_objects[i].bbox);
+		f.writeBoundingBox(_objects[i].bbox, true);
 		f.writeBool(_objects[i].isObstacle);
 		f.writeBool(_objects[i].isClickable);
 		f.writeBool(_objects[i].isHotMouse);
@@ -375,7 +375,7 @@ void Set::load(SaveFileReadStream &f) {
 
 	for (int i = 0; i != _objectCount; ++i) {
 		_objects[i].name = f.readStringSz(20);
-		_objects[i].bbox = f.readBoundingBox();
+		_objects[i].bbox = f.readBoundingBox(true);
 		_objects[i].isObstacle = f.readBool();
 		_objects[i].isClickable = f.readBool();
 		_objects[i].isHotMouse = f.readBool();
diff --git a/engines/bladerunner/settings.cpp b/engines/bladerunner/settings.cpp
index 071adf6..6c3e5a7 100644
--- a/engines/bladerunner/settings.cpp
+++ b/engines/bladerunner/settings.cpp
@@ -73,6 +73,13 @@ Settings::Settings(BladeRunnerEngine *vm) {
 	_learyMode = false;
 }
 
+void Settings::reset() {
+	_ammoType = 0;
+	_ammoAmounts[0] = 1;
+	_ammoAmounts[1] = 0;
+	_ammoAmounts[2] = 0;
+}
+
 bool Settings::openNewScene() {
 	if (_newSet == -1) {
 		assert(_newScene == -1);
@@ -108,8 +115,9 @@ bool Settings::openNewScene() {
 			return false;
 		}
 		_chapter = newChapter;
-		if (_startingGame)
+		if (_startingGame) {
 			_startingGame = false;
+		}
 	}
 
 	if (!_vm->_scene->open(newSet, newScene, _loadingGame)) {
@@ -184,6 +192,10 @@ int Settings::getDifficulty() const {
 	return _difficulty;
 }
 
+void Settings::setDifficulty(int difficulty) {
+	_difficulty = difficulty;
+}
+
 int Settings::getPlayerAgenda() const {
 	return _playerAgenda;
 }
diff --git a/engines/bladerunner/settings.h b/engines/bladerunner/settings.h
index 413786e..3c7048f 100644
--- a/engines/bladerunner/settings.h
+++ b/engines/bladerunner/settings.h
@@ -70,6 +70,8 @@ class Settings {
 public:
 	Settings(BladeRunnerEngine *vm);
 
+	void reset();
+
 	void setGamma(float gamma) {
 		_gamma = gamma;
 	}
@@ -109,16 +111,16 @@ public:
 		_newChapter = newChapter;
 	}
 
-	void setLoadingGame(bool loadingGame) {
-		_loadingGame = loadingGame;
+	void setLoadingGame() {
+		_loadingGame = true;
 	}
 
-	bool getLoadingGame() const {
+	bool isLoadingGame() const {
 		return _loadingGame;
 	}
 
-	void setStartingGame(bool startingGame) {
-		_startingGame = startingGame;
+	void setStartingGame() {
+		_startingGame = true;
 	}
 
 	bool openNewScene();
@@ -130,6 +132,7 @@ public:
 	void decreaseAmmo();
 
 	int getDifficulty() const;
+	void setDifficulty(int difficulty);
 
 	int getPlayerAgenda() const;
 	void setPlayerAgenda(int agenda);
diff --git a/engines/bladerunner/slice_renderer.cpp b/engines/bladerunner/slice_renderer.cpp
index 932e02b..5cf818c 100644
--- a/engines/bladerunner/slice_renderer.cpp
+++ b/engines/bladerunner/slice_renderer.cpp
@@ -207,15 +207,13 @@ void SliceRenderer::calculateBoundingRect() {
 	 * Calculate min and max X
 	 */
 
-	/* TODO, there is something off with X scaling when Y is high like in rc02, on sides, x seems to be ofsetted a bit more than it should. Start/top vector is doing that  */
-
 	Matrix3x2 facingRotation = calculateFacingRotationMatrix();
 
 	Matrix3x2 mProjection(_view->_viewportPosition.z / bottom.z,  0.0f, 0.0f,
 	                                                       0.0f, 25.5f, 0.0f);
 
 	Matrix3x2 mOffset(1.0f, 0.0f, _framePos.x,
-                      0.0f, 1.0f, _framePos.y);
+	                  0.0f, 1.0f, _framePos.y);
 
 	Matrix3x2 mScale(_frameScale.x,          0.0f, 0.0f,
 	                          0.0f, _frameScale.y, 0.0f);
@@ -281,7 +279,6 @@ void SliceRenderer::loadFrame(int animation, int frame) {
 }
 
 struct SliceLineIterator {
-	// int _sliceMatrix[2][3];
 	Matrix3x2 _sliceMatrix;
 	int _startY;
 	int _endY;
diff --git a/engines/bladerunner/suspects_database.h b/engines/bladerunner/suspects_database.h
index c4ed089..3eff7a7 100644
--- a/engines/bladerunner/suspects_database.h
+++ b/engines/bladerunner/suspects_database.h
@@ -96,7 +96,6 @@ public:
 	int getPhotoShapeId(int photoId) const;
 	int getPhotoNotUsed(int photoId) const;
 
-private:
 	void reset();
 };
 
diff --git a/engines/bladerunner/ui/elevator.cpp b/engines/bladerunner/ui/elevator.cpp
index 7a6ab3c..c743236 100644
--- a/engines/bladerunner/ui/elevator.cpp
+++ b/engines/bladerunner/ui/elevator.cpp
@@ -66,8 +66,8 @@ int Elevator::activate(int elevatorId) {
 		return 0;
 	}
 
-	_vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack);
-	if (!_vqaPlayer->open(vqaName)) {
+	_vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack, vqaName);
+	if (!_vqaPlayer->open()) {
 		return 0;
 	}
 
@@ -161,7 +161,7 @@ int Elevator::activate(int elevatorId) {
 	_buttonClicked = -1;
 	do {
 		_vm->gameTick();
-	} while (_buttonClicked == -1);
+	} while (_vm->_gameIsRunning && _buttonClicked == -1);
 
 	_imagePicker->deactivate();
 
diff --git a/engines/bladerunner/ui/esper.cpp b/engines/bladerunner/ui/esper.cpp
index 338ee14..6803ce5 100644
--- a/engines/bladerunner/ui/esper.cpp
+++ b/engines/bladerunner/ui/esper.cpp
@@ -107,8 +107,8 @@ void ESPER::open(Graphics::Surface *surface) {
 
 	_shapesPhotos.resize(10);
 
-	_vqaPlayerMain = new VQAPlayer(_vm, &_vm->_surfaceBack);
-	if (!_vqaPlayerMain->open("ESPER.VQA")) {
+	_vqaPlayerMain = new VQAPlayer(_vm, &_vm->_surfaceBack, "ESPER.VQA");
+	if (!_vqaPlayerMain->open()) {
 		return;
 	}
 	_vqaPlayerMain->setLoop(2, -1, kLoopSetModeJustStart, nullptr, nullptr);
@@ -534,7 +534,7 @@ void ESPER::wait(int timeout) {
 	if (!_isWaiting) {
 		_isWaiting = true;
 		uint timeEnd = timeout + _vm->getTotalPlayTime();
-		while (_vm->getTotalPlayTime() < timeEnd) {
+		while (_vm->_gameIsRunning && _vm->getTotalPlayTime() < timeEnd) {
 			_vm->gameTick();
 		}
 		_isWaiting = false;
@@ -866,8 +866,8 @@ void ESPER::drawPhotoZoomOut(Graphics::Surface &surface) {
 
 void ESPER::drawVideoZooming(Graphics::Surface &surface) {
 	if (_vqaPlayerPhoto == nullptr) {
-		_vqaPlayerPhoto = new VQAPlayer(_vm, &_surfaceViewport);
-		if (!_vqaPlayerPhoto->open(Common::String(_regions[_regionSelected].name) + ".VQA")) {
+		_vqaPlayerPhoto = new VQAPlayer(_vm, &_surfaceViewport, Common::String(_regions[_regionSelected].name) + ".VQA");
+		if (!_vqaPlayerPhoto->open()) {
 			setStatePhoto(kEsperPhotoStateShow);
 			_vm->_mouse->enable();
 
diff --git a/engines/bladerunner/ui/kia.cpp b/engines/bladerunner/ui/kia.cpp
index 756f1bd..4b95607 100644
--- a/engines/bladerunner/ui/kia.cpp
+++ b/engines/bladerunner/ui/kia.cpp
@@ -88,6 +88,8 @@ KIA::KIA(BladeRunnerEngine *vm) {
 
 	_pogoPos = 0;
 
+	_thumbnail = nullptr;
+
 	_buttons = new UIImagePicker(_vm, 22);
 
 	_crimesSection     = new KIASectionCrimes(_vm, _vm->_playerActor->_clues);
@@ -124,6 +126,18 @@ KIA::~KIA() {
 	delete _script;
 }
 
+void KIA::reset() {
+	_lastSectionIdKIA = kKIASectionCrimes;
+	_lastSectionIdOptions = kKIASectionSettings;
+	_playerVqaFrame = 0;
+	_playerVisualizerState = 0;
+	_playerSliceModelAngle = 0.0f;
+
+	_crimesSection->reset();
+	_suspectsSection->reset();
+	_cluesSection->reset();
+}
+
 void KIA::openLastOpened() {
 	open(_lastSectionIdKIA);
 }
@@ -169,8 +183,8 @@ void KIA::open(KIASections sectionId) {
 			_mainVqaPlayer = nullptr;
 		}
 
-		_mainVqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack);
-		_mainVqaPlayer->open(name);
+		_mainVqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack, name);
+		_mainVqaPlayer->open();
 	}
 
 	if (_transitionId) {
@@ -621,6 +635,9 @@ void KIA::loopEnded(void *callbackData, int frame, int loopId) {
 }
 
 void KIA::init() {
+	_thumbnail = new byte[SaveFile::kThumbnailSize];
+	_vm->generateThumbnail(_thumbnail);
+
 	if (!_vm->openArchive("MODE.MIX")) {
 		return;
 	}
@@ -640,8 +657,8 @@ void KIA::init() {
 	_vm->_mouse->setCursor(0);
 	if (_playerVqaPlayer == nullptr) {
 
-		_playerVqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceFront);
-		_playerVqaPlayer->open("kiaover.vqa");
+		_playerVqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceFront, "kiaover.vqa");
+		_playerVqaPlayer->open();
 		_playerVqaPlayer->setLoop(0, -1, kLoopSetModeJustStart, nullptr, nullptr);
 	}
 	_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(501), 70, 0, 0, 50, 0);
@@ -650,6 +667,9 @@ void KIA::init() {
 }
 
 void KIA::unload() {
+	delete[] _thumbnail;
+	_thumbnail = nullptr;
+
 	if (!isOpen()) {
 		return;
 	}
@@ -684,7 +704,7 @@ void KIA::unload() {
 
 	// TODO: Unfreeze game time
 
-	if (!_vm->_settings->getLoadingGame() && _vm->_gameIsRunning) {
+	if (!_vm->_settings->isLoadingGame() && _vm->_gameIsRunning) {
 		_vm->_scene->resume();
 	}
 }
diff --git a/engines/bladerunner/ui/kia.h b/engines/bladerunner/ui/kia.h
index 1c2dc19..0612234 100644
--- a/engines/bladerunner/ui/kia.h
+++ b/engines/bladerunner/ui/kia.h
@@ -117,6 +117,8 @@ class KIA {
 
 	int                   _pogoPos;
 
+	byte                 *_thumbnail;
+
 public:
 	KIALog     *_log;
 	KIAScript  *_script;
@@ -126,6 +128,8 @@ public:
 	KIA(BladeRunnerEngine *vm);
 	~KIA();
 
+	void reset();
+
 	void openLastOpened();
 	void open(KIASections sectionId);
 	bool isOpen() const;
diff --git a/engines/bladerunner/ui/kia_section_clues.cpp b/engines/bladerunner/ui/kia_section_clues.cpp
index e003e9b..7485d8e 100644
--- a/engines/bladerunner/ui/kia_section_clues.cpp
+++ b/engines/bladerunner/ui/kia_section_clues.cpp
@@ -80,6 +80,18 @@ KIASectionClues::~KIASectionClues() {
 	delete _uiContainer;
 }
 
+void KIASectionClues::reset() {
+	_debugIntangible = false;
+	_debugNop = 0;
+
+	_mouseX = 0;
+	_mouseY = 0;
+
+	for (int i = 0; i < _filterCount; ++i) {
+		_filters[i] = true;
+	}
+}
+
 void KIASectionClues::open() {
 	_isOpen = true;
 
diff --git a/engines/bladerunner/ui/kia_section_clues.h b/engines/bladerunner/ui/kia_section_clues.h
index 3f6a13d..afd8b67 100644
--- a/engines/bladerunner/ui/kia_section_clues.h
+++ b/engines/bladerunner/ui/kia_section_clues.h
@@ -67,6 +67,8 @@ public:
 	KIASectionClues(BladeRunnerEngine *vm, ActorClues *clues);
 	~KIASectionClues();
 
+	void reset();
+
 	void open();
 	void close();
 
diff --git a/engines/bladerunner/ui/kia_section_crimes.cpp b/engines/bladerunner/ui/kia_section_crimes.cpp
index 96075fb..ff6352b 100644
--- a/engines/bladerunner/ui/kia_section_crimes.cpp
+++ b/engines/bladerunner/ui/kia_section_crimes.cpp
@@ -41,7 +41,6 @@
 #include "bladerunner/ui/ui_image_picker.h"
 #include "bladerunner/ui/ui_scroll_box.h"
 
-
 #include "graphics/surface.h"
 
 namespace BladeRunner {
@@ -71,6 +70,7 @@ KIASectionCrimes::KIASectionCrimes(BladeRunnerEngine *vm, ActorClues *clues) : K
 
 	_suspectSelected = -1;
 	_suspectPhotoShapeId = -1;
+	_suspectPhotoNotUsed = -1;
 	_suspectPhotoShape = nullptr;
 	_suspectsFoundCount = 0;
 	_suspectsFound.resize(_vm->_gameInfo->getSuspectCount());
@@ -87,6 +87,18 @@ KIASectionCrimes::~KIASectionCrimes() {
 	delete _uiContainer;
 }
 
+void KIASectionCrimes::reset() {
+	_acquiredClueCount = 0;
+    _crimesFoundCount = 0;
+    _suspectsFoundCount = 0;
+    _mouseX = 0;
+    _mouseY = 0;
+    _suspectSelected = -1;
+    _crimeSelected = -1;
+    _suspectPhotoShapeId = -1;
+    _suspectPhotoNotUsed = -1;
+}
+
 void KIASectionCrimes::open() {
 	_scheduledSwitch = false;
 
@@ -277,7 +289,7 @@ void KIASectionCrimes::populateAcquiredClues() {
 			++_acquiredClueCount;
 		}
 	}
-	// sort clues by name, is it necessary
+	// sort clues by name, is it necessary?
 }
 
 void KIASectionCrimes::populateCrimes() {
@@ -391,18 +403,20 @@ void KIASectionCrimes::updateSuspectPhoto() {
 	SuspectDatabaseEntry *suspect = _vm->_suspectsDatabase->get(_suspectSelected);
 
 	_suspectPhotoShapeId = -1;
+	_suspectPhotoNotUsed = -1;
 	int photoCluesCount = suspect->getPhotoCount();
 	if (photoCluesCount > 0) {
 		for (int i = 0 ; i < photoCluesCount; i++) {
-			//TODO: weird stuff going on here... it's using index instead id, also some field is used but its always -1
+			//TODO: weird stuff going on here... original game is using internal clue index instead id
 			if (_clues->isAcquired(suspect->getPhotoClueId(i))) {
 				_suspectPhotoShapeId = suspect->getPhotoShapeId(i);
+				_suspectPhotoNotUsed = suspect->getPhotoNotUsed(i);
 				break;
 			}
 		}
 	}
 
-	if (_suspectPhotoShapeId == -1) {
+	if (_suspectPhotoShapeId == -1 && _suspectPhotoNotUsed == -1) {
 		if (suspect->getSex()) {
 			_suspectPhotoShapeId = 14;
 		} else {
diff --git a/engines/bladerunner/ui/kia_section_crimes.h b/engines/bladerunner/ui/kia_section_crimes.h
index 23983b8..bd2a2f2 100644
--- a/engines/bladerunner/ui/kia_section_crimes.h
+++ b/engines/bladerunner/ui/kia_section_crimes.h
@@ -69,6 +69,7 @@ class KIASectionCrimes : public KIASectionBase {
 	int   _mouseY;
 
 	int    _suspectPhotoShapeId;
+	int    _suspectPhotoNotUsed;
 	Shape *_suspectPhotoShape;
 
 public:
@@ -78,6 +79,8 @@ public:
 	KIASectionCrimes(BladeRunnerEngine *vm, ActorClues *clues);
 	~KIASectionCrimes();
 
+	void reset();
+
 	void open();
 	void close();
 
diff --git a/engines/bladerunner/ui/kia_section_suspects.cpp b/engines/bladerunner/ui/kia_section_suspects.cpp
index 54e33cb..460f744 100644
--- a/engines/bladerunner/ui/kia_section_suspects.cpp
+++ b/engines/bladerunner/ui/kia_section_suspects.cpp
@@ -86,6 +86,7 @@ KIASectionSuspects::KIASectionSuspects(BladeRunnerEngine *vm, ActorClues *clues)
 
 	_suspectSelected = -1;
 	_suspectPhotoShapeId = -1;
+	_suspectPhotoNotUsed = -1;
 	_suspectPhotoShape = nullptr;
 	_suspectsFoundCount = 0;
 	_suspectsFound.resize(_vm->_gameInfo->getSuspectCount());
@@ -108,6 +109,22 @@ KIASectionSuspects::~KIASectionSuspects() {
 	delete _uiContainer;
 }
 
+void KIASectionSuspects::reset() {
+	_acquiredClueCount = 0;
+	_suspectsFoundCount = 0;
+	_mouseX = 0;
+	_mouseY = 0;
+	_suspectSelected = -1;
+	_crimeSelected = -1;
+	_suspectPhotoShapeId = -1;
+	_suspectPhotoNotUsed = -1;
+	_whereaboutsFilter  = true;
+	_MOFilter = true;
+	_replicantFilter = true;
+	_nonReplicantFilter = true;
+	_othersFilter = true;
+}
+
 void KIASectionSuspects::open() {
 	_scheduledSwitch = false;
 
@@ -169,7 +186,6 @@ void KIASectionSuspects::draw(Graphics::Surface &surface) {
 
 	_uiContainer->draw(surface);
 
-
 	_vm->_mainFont->drawColor(_vm->_textKIA->getText(0),  surface, 300, 162, 0x77DF);
 	_vm->_mainFont->drawColor(_vm->_textKIA->getText(46), surface, 142, 248, 0x77DF);
 	_vm->_mainFont->drawColor(_vm->_textKIA->getText(47), surface, 142, 308, 0x77DF);
@@ -479,18 +495,20 @@ void KIASectionSuspects::updateSuspectPhoto() {
 	SuspectDatabaseEntry *suspect = _vm->_suspectsDatabase->get(_suspectSelected);
 
 	_suspectPhotoShapeId = -1;
+	_suspectPhotoNotUsed = -1;
 	int photoCluesCount = suspect->getPhotoCount();
 	if (photoCluesCount > 0) {
 		for (int i = 0 ; i < photoCluesCount; i++) {
-			//TODO: weird stuff going on here... it's using index instead id, also some field is used but its always -1
+			//TODO: weird stuff going on here... original game is using internal clue index instead id
 			if (_clues->isAcquired(suspect->getPhotoClueId(i))) {
 				_suspectPhotoShapeId = suspect->getPhotoShapeId(i);
+				_suspectPhotoNotUsed = suspect->getPhotoNotUsed(i);
 				break;
 			}
 		}
 	}
 
-	if (_suspectPhotoShapeId == -1) {
+	if (_suspectPhotoShapeId == -1 && _suspectPhotoNotUsed == -1) {
 		if (suspect->getSex()) {
 			_suspectPhotoShapeId = 14;
 		} else {
diff --git a/engines/bladerunner/ui/kia_section_suspects.h b/engines/bladerunner/ui/kia_section_suspects.h
index 0cc957f..22a3acc 100644
--- a/engines/bladerunner/ui/kia_section_suspects.h
+++ b/engines/bladerunner/ui/kia_section_suspects.h
@@ -78,6 +78,7 @@ class KIASectionSuspects : public KIASectionBase {
 	int   _mouseY;
 
 	int    _suspectPhotoShapeId;
+	int    _suspectPhotoNotUsed;
 	Shape *_suspectPhotoShape;
 
 public:
@@ -87,6 +88,8 @@ public:
 	KIASectionSuspects(BladeRunnerEngine *vm, ActorClues *clues);
 	~KIASectionSuspects();
 
+	void reset();
+
 	void open();
 	void close();
 
diff --git a/engines/bladerunner/ui/scores.cpp b/engines/bladerunner/ui/scores.cpp
index c4a7df7..8fc3378 100644
--- a/engines/bladerunner/ui/scores.cpp
+++ b/engines/bladerunner/ui/scores.cpp
@@ -50,9 +50,9 @@ void Scores::open() {
 		return;
 	}
 
-	_vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack);
+	_vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack, "SCORE.VQA");
 
-	if (!_vqaPlayer->open("SCORE.VQA")) {
+	if (!_vqaPlayer->open()) {
 		return;
 	}
 
diff --git a/engines/bladerunner/ui/spinner.cpp b/engines/bladerunner/ui/spinner.cpp
index 9e491f7..32809e8 100644
--- a/engines/bladerunner/ui/spinner.cpp
+++ b/engines/bladerunner/ui/spinner.cpp
@@ -74,14 +74,14 @@ int Spinner::chooseDestination(int loopId, bool immediately) {
 	} else {
 		_vm->playerLosesControl();
 		_vm->_scene->loopStartSpecial(kSceneLoopModeSpinner, loopId, immediately);
-		while (!_isOpen) {
+		while (_vm->_gameIsRunning && !_isOpen) {
 			_vm->gameTick();
 		}
 		_vm->playerGainsControl();
 	}
 
-	_vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack);
-	if (!_vqaPlayer->open("SPINNER.VQA")) {
+	_vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack, "SPINNER.VQA");
+	if (!_vqaPlayer->open()) {
 		return 0;
 	}
 
@@ -159,7 +159,7 @@ int Spinner::chooseDestination(int loopId, bool immediately) {
 	_selectedDestination = -1;
 	do {
 		_vm->gameTick();
-	} while (_selectedDestination == -1);
+	} while (_vm->_gameIsRunning && _selectedDestination == -1);
 
 	_imagePicker->deactivate();
 
diff --git a/engines/bladerunner/ui/vk.cpp b/engines/bladerunner/ui/vk.cpp
index ec5013c..ae776b7 100644
--- a/engines/bladerunner/ui/vk.cpp
+++ b/engines/bladerunner/ui/vk.cpp
@@ -87,8 +87,8 @@ void VK::open(int actorId, int calibrationRatio) {
 		_shapes[i]->open("VK.SHP", i);
 	}
 
-	_vqaPlayerMain = new VQAPlayer(_vm, &_vm->_surfaceBack);
-	if (!_vqaPlayerMain->open("VK.VQA")) {
+	_vqaPlayerMain = new VQAPlayer(_vm, &_vm->_surfaceBack, "VK.VQA");
+	if (!_vqaPlayerMain->open()) {
 		return;
 	}
 
@@ -114,8 +114,8 @@ void VK::open(int actorId, int calibrationRatio) {
 	}
 
 	_surfaceEye.create(172, 116, createRGB555());
-	_vqaPlayerEye = new VQAPlayer(_vm, &_surfaceEye);
-	if (!_vqaPlayerEye->open(eyeVqa)) {
+	_vqaPlayerEye = new VQAPlayer(_vm, &_surfaceEye, eyeVqa);
+	if (!_vqaPlayerEye->open()) {
 		return;
 	}
 	if (!_vqaPlayerEye->setLoop(0, -1, kLoopSetModeEnqueue, nullptr, nullptr)) {
diff --git a/engines/bladerunner/vqa_player.cpp b/engines/bladerunner/vqa_player.cpp
index 5f25a63..68a4e94 100644
--- a/engines/bladerunner/vqa_player.cpp
+++ b/engines/bladerunner/vqa_player.cpp
@@ -30,8 +30,8 @@
 
 namespace BladeRunner {
 
-bool VQAPlayer::open(const Common::String &name) {
-	_s = _vm->getResourceStream(name);
+bool VQAPlayer::open() {
+	_s = _vm->getResourceStream(_name);
 	if (!_s) {
 		return false;
 	}
diff --git a/engines/bladerunner/vqa_player.h b/engines/bladerunner/vqa_player.h
index b2c6ff2..c4dd182 100644
--- a/engines/bladerunner/vqa_player.h
+++ b/engines/bladerunner/vqa_player.h
@@ -49,6 +49,7 @@ class VQAPlayer {
 	friend class Debugger;
 
 	BladeRunnerEngine           *_vm;
+	Common::String               _name;
 	Common::SeekableReadStream  *_s;
 	VQADecoder                   _decoder;
 	Audio::QueuingAudioStream   *_audioStream;
@@ -77,8 +78,9 @@ class VQAPlayer {
 
 public:
 
-	VQAPlayer(BladeRunnerEngine *vm, Graphics::Surface *surface)
+	VQAPlayer(BladeRunnerEngine *vm, Graphics::Surface *surface, const Common::String &name)
 		: _vm(vm),
+		  _name(name),
 		  _s(nullptr),
 		  _surface(surface),
 		  _decoder(),
@@ -103,7 +105,7 @@ public:
 		close();
 	}
 
-	bool open(const Common::String &name);
+	bool open();
 	void close();
 
 	int  update(bool forceDraw = false, bool advanceFrame = true, Graphics::Surface *customSurface = nullptr);





More information about the Scummvm-git-logs mailing list