[Scummvm-git-logs] scummvm master -> 5e9985bd6e4d136ec20e5e3be7589d34051227f1

peterkohaut peterkohaut at users.noreply.github.com
Sat Mar 24 17:31:32 CET 2018


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

Summary:
2a646f8cc2 BLADERUNNER: Save game methods
5e9985bd6e BLADERUNNER: Load game methods


Commit: 2a646f8cc287a98f90e561ce8072c2af4322fd22
    https://github.com/scummvm/scummvm/commit/2a646f8cc287a98f90e561ce8072c2af4322fd22
Author: Thomas Fach-Pedersen (thomas at fach-pedersen.net)
Date: 2018-03-24T17:07:06+01:00

Commit Message:
BLADERUNNER: Save game methods

Changed paths:
  A engines/bladerunner/police_maze.cpp
  A engines/bladerunner/police_maze.h
  A engines/bladerunner/police_maze_track.cpp
  A engines/bladerunner/police_maze_track.h
  A engines/bladerunner/savefile.cpp
  A engines/bladerunner/savefile.h
    engines/bladerunner/actor.cpp
    engines/bladerunner/actor.h
    engines/bladerunner/actor_clues.cpp
    engines/bladerunner/actor_clues.h
    engines/bladerunner/actor_combat.cpp
    engines/bladerunner/actor_combat.h
    engines/bladerunner/actor_dialogue_queue.cpp
    engines/bladerunner/actor_dialogue_queue.h
    engines/bladerunner/actor_walk.cpp
    engines/bladerunner/actor_walk.h
    engines/bladerunner/ambient_sounds.cpp
    engines/bladerunner/ambient_sounds.h
    engines/bladerunner/bladerunner.cpp
    engines/bladerunner/bladerunner.h
    engines/bladerunner/boundingbox.cpp
    engines/bladerunner/boundingbox.h
    engines/bladerunner/combat.cpp
    engines/bladerunner/combat.h
    engines/bladerunner/crimes_database.cpp
    engines/bladerunner/crimes_database.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/item.h
    engines/bladerunner/items.cpp
    engines/bladerunner/items.h
    engines/bladerunner/module.mk
    engines/bladerunner/movement_track.cpp
    engines/bladerunner/movement_track.h
    engines/bladerunner/obstacles.cpp
    engines/bladerunner/obstacles.h
    engines/bladerunner/overlays.cpp
    engines/bladerunner/overlays.h
    engines/bladerunner/regions.cpp
    engines/bladerunner/regions.h
    engines/bladerunner/scene.cpp
    engines/bladerunner/scene.h
    engines/bladerunner/scene_objects.cpp
    engines/bladerunner/scene_objects.h
    engines/bladerunner/script/ai_script.cpp
    engines/bladerunner/script/ai_script.h
    engines/bladerunner/set.cpp
    engines/bladerunner/set.h
    engines/bladerunner/settings.cpp
    engines/bladerunner/settings.h
    engines/bladerunner/ui/spinner.cpp
    engines/bladerunner/ui/spinner.h
    engines/bladerunner/waypoints.cpp
    engines/bladerunner/waypoints.h


diff --git a/engines/bladerunner/actor.cpp b/engines/bladerunner/actor.cpp
index b45e6e2..aae1085 100644
--- a/engines/bladerunner/actor.cpp
+++ b/engines/bladerunner/actor.cpp
@@ -32,6 +32,7 @@
 #include "bladerunner/items.h"
 #include "bladerunner/mouse.h"
 #include "bladerunner/movement_track.h"
+#include "bladerunner/savefile.h"
 #include "bladerunner/scene.h"
 #include "bladerunner/scene_objects.h"
 #include "bladerunner/script/scene_script.h"
@@ -1225,4 +1226,80 @@ bool Actor::walkToNearestPoint(const Vector3 &destination, float distance) {
 	return false;
 }
 
+void Actor::save(SaveFile &f) {
+	f.write(_id);
+	f.write(_setId);
+	f.write(_position);
+	f.write(_facing);
+	f.write(_targetFacing);
+	f.write(0); // TODO: _timer4RemainDefault
+
+	f.write(_honesty);
+	f.write(_intelligence);
+	f.write(_stability);
+	f.write(_combatAggressiveness);
+	f.write(_goalNumber);
+
+	f.write(_currentHP);
+	f.write(_maxHP);
+
+	f.write(_movementTrackPaused);
+	f.write(_movementTrackNextWaypointId);
+	f.write(_movementTrackNextDelay);
+	f.write(_movementTrackNextAngle);
+	f.write(_movementTrackNextRunning);
+
+	f.write(0); // TODO: _clueType
+	f.write(_isMoving);
+	f.write(_isTarget);
+	f.write(_inCombat);
+	f.write(_isInvisible);
+	f.write(_isRetired);
+	f.write(_isImmuneToObstacles);
+
+	f.write(_animationMode);
+	f.write(_fps);
+	f.write(_frameMs);
+	f.write(_animationId);
+	f.write(_animationFrame);
+
+	f.write(_movementTrackWalkingToWaypointId);
+	f.write(_movementTrackDelayOnNextWaypoint);
+
+	f.write(_screenRectangle);
+	f.write(_retiredWidth);
+	f.write(_retiredHeight);
+	f.write(_damageAnimIfMoving);
+	f.write(0); // TODO: _actorFieldU6
+	f.write(0); // TODO: _actorFieldU7
+	f.write(_scale);
+
+	for (int i = 0; i < 7; ++i) {
+		f.write(_timersLeft[i]);
+	}
+
+	uint32 now = _vm->getTotalPlayTime(); // TODO: should be last lock time
+	for (int i = 0; i < 7; ++i) {
+		f.write(_timersLast[i] - now);
+	}
+
+	int actorCount = _vm->_gameInfo->getActorCount();
+	for (int i = 0; i != actorCount; ++i) {
+		f.write(_friendlinessToOther[i]);
+	}
+
+	_clues->save(f);
+
+	_movementTrack->save(f);
+
+	_walkInfo->save(f);
+
+	_bbox->save(f);
+
+	_combatInfo->save(f);
+	f.write(_animationModeCombatIdle);
+	f.write(_animationModeCombatWalk);
+	f.write(_animationModeCombatRun);
+}
+
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/actor.h b/engines/bladerunner/actor.h
index e381f68..55ffc4f 100644
--- a/engines/bladerunner/actor.h
+++ b/engines/bladerunner/actor.h
@@ -36,6 +36,7 @@ class ActorWalk;
 class BladeRunnerEngine;
 class BoundingBox;
 class MovementTrack;
+class SaveFile;
 class View;
 
 class Actor {
@@ -256,6 +257,9 @@ private:
 	bool walkFindU2(Vector3 *newDestination, float targetWidth, int destinationOffset, float targetSize, const Vector3 &startPosition, const Vector3 &targetPosition);
 	bool walkToNearestPoint(const Vector3 &destination, float distance);
 	//bool walkFindU3(int actorId, Vector3 from, int distance, Vector3 *out);
+
+public:
+	void save(SaveFile &f);
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/actor_clues.cpp b/engines/bladerunner/actor_clues.cpp
index 39fbc77..89db6fe 100644
--- a/engines/bladerunner/actor_clues.cpp
+++ b/engines/bladerunner/actor_clues.cpp
@@ -25,6 +25,7 @@
 #include "bladerunner/bladerunner.h"
 #include "bladerunner/game_info.h"
 #include "bladerunner/crimes_database.h"
+#include "bladerunner/savefile.h"
 
 #include "common/debug.h"
 
@@ -216,6 +217,24 @@ void ActorClues::remove(int index) {
 	_clues[index].field8 = 0;
 }
 
+void ActorClues::save(SaveFile &f) {
+	f.write(_count);
+	f.write(_maxCount);
+	for (int i = 0; i < _count; ++i) {
+		Clue &c = _clues[i];
+		f.write(c.clueId);
+		f.write(c.weight);
+		f.write(c.fromActorId);
+		f.write(c.field3);
+		f.write(c.field4);
+		f.write(c.field5);
+		f.write(c.field6);
+		f.write(c.field7);
+		f.write(c.field8);
+		f.write(c.flags);
+	}
+}
+
 bool ActorClues::exists(int clueId) const {
 	return findClueIndex(clueId) != -1;
 }
diff --git a/engines/bladerunner/actor_clues.h b/engines/bladerunner/actor_clues.h
index b2b39d8..03ccff2 100644
--- a/engines/bladerunner/actor_clues.h
+++ b/engines/bladerunner/actor_clues.h
@@ -28,6 +28,7 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
+class SaveFile;
 
 class ActorClues {
 	struct Clue {
@@ -74,7 +75,7 @@ public:
 
 	void removeAll();
 
-	//savegame
+	void save(SaveFile &f);
 	//loadgame
 
 private:
diff --git a/engines/bladerunner/actor_combat.cpp b/engines/bladerunner/actor_combat.cpp
index aa8e55b..7a60d6d 100644
--- a/engines/bladerunner/actor_combat.cpp
+++ b/engines/bladerunner/actor_combat.cpp
@@ -29,6 +29,7 @@
 #include "bladerunner/game_constants.h"
 #include "bladerunner/game_info.h"
 #include "bladerunner/movement_track.h"
+#include "bladerunner/savefile.h"
 #include "bladerunner/scene.h"
 #include "bladerunner/scene_objects.h"
 #include "bladerunner/script/ai_script.h"
@@ -49,6 +50,31 @@ void ActorCombat::setup() {
 	reset();
 }
 
+void ActorCombat::save(SaveFile &f) {
+	// TODO
+	f.write(0); // _actorId
+	f.write(0); // _combatOn
+	f.write(0); // _field2
+	f.write(0); // _field3
+	f.write(0); // _otherActorId
+	f.write(0); // _field5
+	f.write(0); // _field6
+	f.write(0); // _field7
+	f.write(0); // _field8
+	f.write(0); // _field9
+	f.write(0); // _field10
+	f.write(0); // _field11
+	f.write(0); // _field12
+	f.write(0); // _actorHp
+	f.write(0); // _field14
+	f.write(0); // _field15
+	f.write(0); // _actorPosition
+	f.write(0); // _otherActorPosition
+	f.write(0); // _availableCoversCount
+	f.write(0); // _availableFleeWaypointsCount
+	f.write(0); // _field24
+}
+
 void ActorCombat::combatOn(int actorId, int initialState, bool rangedAttackFlag, int enemyId, int waypointType, int fleeRatio, int coverRatio, int actionRatio, int damage, int range, bool unstoppable) {
 	_actorId = actorId;
 	_state = initialState;
diff --git a/engines/bladerunner/actor_combat.h b/engines/bladerunner/actor_combat.h
index 42ef7de..a621c3d 100644
--- a/engines/bladerunner/actor_combat.h
+++ b/engines/bladerunner/actor_combat.h
@@ -28,6 +28,7 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
+class SaveFile;
 
 class ActorCombat {
 	BladeRunnerEngine *_vm;
@@ -64,6 +65,8 @@ public:
 	void combatOff();
 
 	void tick();
+	
+	void save(SaveFile &f);
 
 	void hitAttempt();
 
diff --git a/engines/bladerunner/actor_dialogue_queue.cpp b/engines/bladerunner/actor_dialogue_queue.cpp
index dbf5598..5f0ad60 100644
--- a/engines/bladerunner/actor_dialogue_queue.cpp
+++ b/engines/bladerunner/actor_dialogue_queue.cpp
@@ -26,6 +26,7 @@
 
 #include "bladerunner/actor.h"
 #include "bladerunner/audio_speech.h"
+#include "bladerunner/savefile.h"
 #include "bladerunner/scene.h"
 
 #include "bladerunner/script/scene_script.h"
@@ -159,6 +160,30 @@ void ActorDialogueQueue::tick() {
 	}
 }
 
+void ActorDialogueQueue::save(SaveFile &f) {
+	int count = (int)_entries.size();
+	f.write(count);
+	for (int i = 0; i < count; ++i) {
+		Entry &e = _entries[i];
+		f.write(e.isNotPause);
+		f.write(e.isPause);
+		f.write(e.actorId);
+		f.write(e.sentenceId);
+		f.write(e.animationMode);
+		f.write(e.delay);
+	}
+	f.padBytes((25 - count) * 24);
+
+	f.write(_isNotPause);
+	f.write(_actorId);
+	f.write(_sentenceId);
+	f.write(_animationMode);
+	f.write(_animationModePrevious);
+	f.write(_isPause);
+	f.write(_delay);
+	// f.write(_timeLast);
+}
+
 void ActorDialogueQueue::clear() {
 	_entries.clear();
 	_isNotPause = false;
@@ -170,4 +195,5 @@ void ActorDialogueQueue::clear() {
 	_delay = 0;
 	_timeLast = 0;
 }
+
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/actor_dialogue_queue.h b/engines/bladerunner/actor_dialogue_queue.h
index 841ca8a..e26b6cf 100644
--- a/engines/bladerunner/actor_dialogue_queue.h
+++ b/engines/bladerunner/actor_dialogue_queue.h
@@ -22,11 +22,13 @@
 
 #ifndef BLADERUNNER_ACTOR_DIALOGUE_QUEUE_H
 #define BLADERUNNER_ACTOR_DIALOGUE_QUEUE_H
+
 #include "common/array.h"
 
 namespace BladeRunner {
 
 class BladeRunnerEngine;
+class SaveFile;
 
 class ActorDialogueQueue {
 	struct Entry {
@@ -61,6 +63,8 @@ public:
 	void flush(int a1, bool callScript);
 	void tick();
 
+	void save(SaveFile &f);
+
 private:
 	void clear();
 };
diff --git a/engines/bladerunner/actor_walk.cpp b/engines/bladerunner/actor_walk.cpp
index 16009aa..d376e36 100644
--- a/engines/bladerunner/actor_walk.cpp
+++ b/engines/bladerunner/actor_walk.cpp
@@ -28,6 +28,7 @@
 #include "bladerunner/game_constants.h"
 #include "bladerunner/game_info.h"
 #include "bladerunner/obstacles.h"
+#include "bladerunner/savefile.h"
 #include "bladerunner/scene.h"
 #include "bladerunner/scene_objects.h"
 #include "bladerunner/set.h"
@@ -238,6 +239,27 @@ void ActorWalk::run(int actorId) {
 	_vm->_actors[actorId]->changeAnimationMode(animationMode, false);
 }
 
+void ActorWalk::save(SaveFile &f) {
+	f.write(_walking);
+	f.write(_running);
+	f.write(_destination);
+	// _originalDestination is not saved
+	f.write(_current);
+	f.write(_next);
+	f.write(_facing);
+
+	assert(_nearActors.size() <= 20);
+	for (Common::HashMap<int, bool>::const_iterator it = _nearActors.begin(); it != _nearActors.end(); ++it) {
+		f.write(it->_key);
+		f.write(it->_value);
+	}
+	f.padBytes(8 * (20 - _nearActors.size()));
+	f.write((int)_nearActors.size());
+
+	f.write(0); // _notUsed
+	f.write(_status);
+}
+
 bool ActorWalk::isXYZEmpty(float x, float y, float z, int actorId) const {
 	if (_vm->_scene->_set->findWalkbox(x, z) == -1) {
 		return true;
diff --git a/engines/bladerunner/actor_walk.h b/engines/bladerunner/actor_walk.h
index f4caa65..2654214 100644
--- a/engines/bladerunner/actor_walk.h
+++ b/engines/bladerunner/actor_walk.h
@@ -29,6 +29,7 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
+class SaveFile;
 
 class ActorWalk {
 	BladeRunnerEngine *_vm;
@@ -60,6 +61,7 @@ public:
 	void stop(int actorId, bool immediately, int combatAnimationMode, int animationMode);
 	void run(int actorId);
 
+	void save(SaveFile &f);
 private:
 	int nextOnPath(int actorId, const Vector3 &from, const Vector3 &to, Vector3 &next) const;
 
diff --git a/engines/bladerunner/ambient_sounds.cpp b/engines/bladerunner/ambient_sounds.cpp
index 64a85bc..6477f27 100644
--- a/engines/bladerunner/ambient_sounds.cpp
+++ b/engines/bladerunner/ambient_sounds.cpp
@@ -25,6 +25,7 @@
 #include "bladerunner/audio_player.h"
 #include "bladerunner/bladerunner.h"
 #include "bladerunner/game_info.h"
+#include "bladerunner/savefile.h"
 
 #include "common/debug.h"
 #include "common/system.h"
@@ -362,4 +363,40 @@ void AmbientSounds::removeLoopingSoundByIndex(int index, int delay) {
 	track.pan = 0;
 }
 
+void AmbientSounds::save(SaveFile &f) {
+	f.write(false); // TODO: _isDisabled
+
+	for (int i = 0; i != kNonLoopingSounds; ++i) {
+		// 73 bytes per non-looping sound
+		NonLoopingSound &s = _nonLoopingSounds[i];
+		f.write(s.isActive);
+		f.write(s.name, 13);
+		f.write(s.hash);
+		f.write(s.audioPlayerTrack);
+		f.write(s.timeMin);
+		f.write(s.timeMax);
+		f.write(s.nextPlayTime);
+		f.write(s.volumeMin);
+		f.write(s.volumeMax);
+		f.write(s.volume);
+		f.write(s.panStartMin);
+		f.write(s.panStartMax);
+		f.write(s.panEndMin);
+		f.write(s.panEndMax);
+		f.write(s.priority);
+		f.padBytes(4); // field_45
+	}
+
+	for (int i = 0; i != kLoopingSounds; ++i) {
+		// 33 bytes per looping sound
+		LoopingSound &s = _loopingSounds[i];
+		f.write(s.isActive);
+		f.write(s.name, 13);
+		f.write(s.hash);
+		f.write(s.audioPlayerTrack);
+		f.write(s.volume);
+		f.write(s.pan);
+	}
+}
+
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/ambient_sounds.h b/engines/bladerunner/ambient_sounds.h
index e06726b..6e14c56 100644
--- a/engines/bladerunner/ambient_sounds.h
+++ b/engines/bladerunner/ambient_sounds.h
@@ -28,6 +28,7 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
+class SaveFile;
 
 class AmbientSounds {
 	static const int kNonLoopingSounds = 25;
@@ -103,6 +104,8 @@ public:
 	int getVolume() const;
 	void playSample();
 
+	void save(SaveFile &f);
+
 private:
 	int findAvailableNonLoopingTrack() const;
 	int findNonLoopingTrackByHash(int32 hash) const;
diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp
index 0fd7663..805461c 100644
--- a/engines/bladerunner/bladerunner.cpp
+++ b/engines/bladerunner/bladerunner.cpp
@@ -45,7 +45,9 @@
 #include "bladerunner/outtake.h"
 #include "bladerunner/obstacles.h"
 #include "bladerunner/overlays.h"
+#include "bladerunner/police_maze.h"
 #include "bladerunner/regions.h"
+#include "bladerunner/savefile.h"
 #include "bladerunner/scene.h"
 #include "bladerunner/scene_objects.h"
 #include "bladerunner/screen_effects.h"
@@ -74,6 +76,7 @@
 #include "common/array.h"
 #include "common/error.h"
 #include "common/events.h"
+#include "common/savefile.h"
 #include "common/system.h"
 
 #include "engines/util.h"
@@ -290,7 +293,7 @@ bool BladeRunnerEngine::startup(bool hasSavegames) {
 
 	// TODO: Flee waypoints
 
-	_gameVars = new int[_gameInfo->getGlobalVarCount()];
+	_gameVars = new int[_gameInfo->getGlobalVarCount()]();
 
 	// TODO: Actor AI DLL init
 
@@ -352,7 +355,9 @@ bool BladeRunnerEngine::startup(bool hasSavegames) {
 
 	// TODO: Set actor ids (redundant?)
 
-	// TODO: Police Maze
+	_policeMaze = new PoliceMaze(this);
+	if (!_policeMaze->init())
+		return false;
 
 	_textActorNames = new TextResource(this);
 	if (!_textActorNames->open("ACTORS"))
@@ -1580,6 +1585,91 @@ void BladeRunnerEngine::playerGainsControl() {
 	}
 }
 
+bool BladeRunnerEngine::saveGame(const Common::String &filename, byte *thumbnail) {
+	warning("BladeRunnerEngine::saveGame not finished");
+
+	if (!playerHasControl() || _sceneScript->isInsideScript() || _aiScripts->isInsideScript()) {
+		return false;
+	}
+
+	Common::OutSaveFile *commonSaveFile = getSaveFileManager()->openForSaving(filename, false);
+	if (commonSaveFile->err()) {
+		return false;
+	}
+
+	SaveFile s(commonSaveFile);
+
+	s.padBytes(9600); // TODO: thumbnail
+	s.write(-1.0f);
+
+	_settings->save(s);
+	// s.debug(" - SCENE - ");
+	_scene->save(s);
+	// s.debug(" - EXIST - ");
+	_scene->_exits->save(s);
+	// s.debug(" - REGIONS - ");
+	_scene->_regions->save(s);
+	// s.debug(" - SET - ");
+	_scene->_set->save(s);
+
+	// s.debug(" - GAMEVARS - ");
+	for (uint i = 0; i != _gameInfo->getGlobalVarCount(); ++i) {
+		s.write(_gameVars[i]);
+	}
+
+	// TODO
+	// _music->save(s);
+	// s.debug(" - MUSIC - ");
+	s.padBytes(0x56);
+
+	// _audioPlayer->save(s) // zero func
+	// _audioSpeech->save(s) // zero func
+
+	// s.debug(" - COMBAT - ");
+	_combat->save(s);
+	// s.debug(" - GAMEFLAGS - ");
+	_gameFlags->save(s);
+	// s.debug(" - ITEMS - ");
+	_items->save(s);
+	// s.debug(" - SCENEOBJECTS - ");
+	_sceneObjects->save(s);
+	// s.debug(" - AMBIENTSOUNDS - ");
+	_ambientSounds->save(s);
+	// s.debug(" - OVERLAYS - ");
+	_overlays->save(s);
+	// s.debug(" - SPINNER - ");
+	_spinner->save(s);
+
+	// TODO
+	// _scores->save(s);
+	s.padBytes(0x28);
+
+	_dialogueMenu->save(s);
+	_obstacles->save(s);
+	_actorDialogueQueue->save(s);
+	_waypoints->save(s);
+
+	for (uint i = 0; i != _gameInfo->getActorCount(); ++i) {
+		_actors[i]->save(s);
+
+		int animationState, animationFrame, a3, a4;
+		_aiScripts->queryAnimationState(i, &animationState, &animationFrame, &a3, &a4);
+		s.write(animationState);
+		s.write(animationFrame);
+		s.write(a3);
+		s.write(a4);
+	}
+	_actors[kActorVoiceOver]->save(s);
+
+	_policeMaze->save(s);
+	_crimesDatabase->save(s);
+
+	s.finalize();
+	assert(0 && "ok");
+
+	return !commonSaveFile->err();
+}
+
 void BladeRunnerEngine::ISez(const char *str) {
 	debug("\t%s", str);
 }
diff --git a/engines/bladerunner/bladerunner.h b/engines/bladerunner/bladerunner.h
index 2c3b7c6..7561db2 100644
--- a/engines/bladerunner/bladerunner.h
+++ b/engines/bladerunner/bladerunner.h
@@ -260,6 +260,11 @@ public:
 	void playerLosesControl();
 	void playerGainsControl();
 
+	bool saveGame(const Common::String &filename, byte *thumbnail);
+	void loadGame();
+	void newGame();
+	void autoSaveGame();
+
 	void ISez(const char *str);
 
 	void blitToScreen(const Graphics::Surface &src);
diff --git a/engines/bladerunner/boundingbox.cpp b/engines/bladerunner/boundingbox.cpp
index a1c79a1..aea2dc0 100644
--- a/engines/bladerunner/boundingbox.cpp
+++ b/engines/bladerunner/boundingbox.cpp
@@ -22,6 +22,8 @@
 
 #include "bladerunner/boundingbox.h"
 
+#include "bladerunner/savefile.h"
+
 namespace BladeRunner {
 
 BoundingBox::BoundingBox(float x0, float y0, float z0, float x1, float y1, float z1) {
@@ -82,4 +84,16 @@ float BoundingBox::getZ1() const {
 	return _vertices[1].z;
 }
 
+void BoundingBox::save(SaveFile &f) {
+	f.write(_vertices[0].x);
+	f.write(_vertices[0].y);
+	f.write(_vertices[0].z);
+	f.write(_vertices[1].x);
+	f.write(_vertices[1].y);
+	f.write(_vertices[1].z);
+
+	// Bounding boxes have a lot of extra data that's never actually used
+	f.padBytes(8*8*4);
+}
+
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/boundingbox.h b/engines/bladerunner/boundingbox.h
index 11922cb..0397a86 100644
--- a/engines/bladerunner/boundingbox.h
+++ b/engines/bladerunner/boundingbox.h
@@ -27,6 +27,8 @@
 
 namespace BladeRunner {
 
+class SaveFile;
+
 class BoundingBox {
 	Vector3 _vertices[2];
 
@@ -43,6 +45,8 @@ public:
 
 	float getZ0() const;
 	float getZ1() const;
+
+	void save(SaveFile &f);
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/combat.cpp b/engines/bladerunner/combat.cpp
index ef7a589..eefcebc 100644
--- a/engines/bladerunner/combat.cpp
+++ b/engines/bladerunner/combat.cpp
@@ -28,6 +28,7 @@
 #include "bladerunner/game_constants.h"
 #include "bladerunner/game_info.h"
 #include "bladerunner/movement_track.h"
+#include "bladerunner/savefile.h"
 #include "bladerunner/scene_objects.h"
 #include "bladerunner/settings.h"
 
@@ -204,4 +205,15 @@ int Combat::findCoverWaypoint(int waypointType, int actorId, int enemyId) const
 	return result;
 }
 
+void Combat::save(SaveFile &f) {
+	f.write(_active);
+	f.write(_enabled);
+	for (int i = 0; i != 9; ++i) {
+		f.write(_hitSoundId[i]);
+	}
+	for (int i = 0; i != 9; ++i) {
+		f.write(_missSoundId[i]);
+	}
+}
+
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/combat.h b/engines/bladerunner/combat.h
index ab289cc..670f580 100644
--- a/engines/bladerunner/combat.h
+++ b/engines/bladerunner/combat.h
@@ -30,6 +30,8 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
+class SaveFile;
+class Vector3;
 
 class Combat {
 	static const int kSoundCount = 9;
@@ -38,10 +40,10 @@ class Combat {
 
 	bool _active;
 	bool _enabled;
-	int _hitSoundId[kSoundCount];
-	int _missSoundId[kSoundCount];
-//	int _random1;
-//	int _random2;
+	int  _hitSoundId[kSoundCount];
+	int  _missSoundId[kSoundCount];
+	// int  _random1;
+	// int  _random2;
 
 public:
 	int _ammoDamage[3];
@@ -87,6 +89,8 @@ public:
 
 	int findFleeWaypoint(int setId, int enemyId, const Vector3& position) const;
 	int findCoverWaypoint(int waypointType, int actorId, int enemyId) const;
+
+	void save(SaveFile &f);
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/crimes_database.cpp b/engines/bladerunner/crimes_database.cpp
index febe408..131c871 100644
--- a/engines/bladerunner/crimes_database.cpp
+++ b/engines/bladerunner/crimes_database.cpp
@@ -24,6 +24,7 @@
 
 #include "bladerunner/bladerunner.h"
 
+#include "bladerunner/savefile.h"
 #include "bladerunner/text_resource.h"
 
 namespace BladeRunner {
@@ -70,4 +71,11 @@ const char *CrimesDatabase::getClueText(int clueId) const {
 	return _cluesText->getText(clueId);
 }
 
+void CrimesDatabase::save(SaveFile &f) {
+	for (int i = 0; i < _crimeCount; ++i) {
+		uint8 c = _crimes[i];
+		f.write(c);
+	}
+}
+
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/crimes_database.h b/engines/bladerunner/crimes_database.h
index 40e46cb..1374d52 100644
--- a/engines/bladerunner/crimes_database.h
+++ b/engines/bladerunner/crimes_database.h
@@ -28,6 +28,7 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
+class SaveFile;
 class TextResource;
 
 class CrimesDatabase {
@@ -47,6 +48,8 @@ public:
 	int getAssetType(int clueId) const;
 
 	const char *getClueText(int clueId) const;
+
+	void save(SaveFile &f);
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/dialogue_menu.cpp b/engines/bladerunner/dialogue_menu.cpp
index 685c845..7b7223f 100644
--- a/engines/bladerunner/dialogue_menu.cpp
+++ b/engines/bladerunner/dialogue_menu.cpp
@@ -25,6 +25,7 @@
 #include "bladerunner/bladerunner.h"
 #include "bladerunner/font.h"
 #include "bladerunner/mouse.h"
+#include "bladerunner/savefile.h"
 #include "bladerunner/settings.h"
 #include "bladerunner/shape.h"
 #include "bladerunner/text_resource.h"
@@ -364,6 +365,30 @@ bool DialogueMenu::waitingForInput() const {
 	return _waitingForInput;
 }
 
+void DialogueMenu::save(SaveFile &f) {
+	f.write(_isVisible);
+	f.write(_waitingForInput);
+	f.write(_selectedItemIndex);
+	f.write(_listSize);
+
+	f.write(_neverRepeatListSize);
+	for (int i = 0; i < 100; ++i) {
+		f.write(_neverRepeatValues[i]);
+	}
+	for (int i = 0; i < 100; ++i) {
+		f.write(_neverRepeatWasSelected[i]);
+	}
+	for (int i = 0; i < 10; ++i) {
+		f.write(_items[i].text, 50);
+		f.write(_items[i].answerValue);
+		f.write(_items[i].colorIntensity);
+		f.write(_items[i].priorityPolite);
+		f.write(_items[i].priorityNormal);
+		f.write(_items[i].prioritySurly);
+		f.write(_items[i].isDone);
+	}
+}
+
 void DialogueMenu::clear() {
 	_isVisible = false;
 	_waitingForInput = false;
diff --git a/engines/bladerunner/dialogue_menu.h b/engines/bladerunner/dialogue_menu.h
index 63e23d8..b06cf9d 100644
--- a/engines/bladerunner/dialogue_menu.h
+++ b/engines/bladerunner/dialogue_menu.h
@@ -33,6 +33,7 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
+class SaveFile;
 class TextResource;
 
 class DialogueMenu {
@@ -102,6 +103,10 @@ private:
 	const char *getText(int id) const;
 	void calculatePosition(int unusedX = 0, int unusedY = 0);
 
+public:
+	void save(SaveFile &f);
+
+private:
 	void clear();
 	void reset();
 
diff --git a/engines/bladerunner/game_flags.cpp b/engines/bladerunner/game_flags.cpp
index 81fe6a0..1e0b583 100644
--- a/engines/bladerunner/game_flags.cpp
+++ b/engines/bladerunner/game_flags.cpp
@@ -22,6 +22,8 @@
 
 #include "bladerunner/game_flags.h"
 
+#include "bladerunner/savefile.h"
+
 #include "common/debug.h"
 
 namespace BladeRunner {
@@ -38,7 +40,7 @@ void GameFlags::setFlagCount(int count) {
 	assert(count > 0);
 
 	_flagCount = count;
-	_flags = new uint32[count / 32 + 1];
+	_flags = new uint32[count / 32 + 1]();
 
 	for (int i = 0; i <= _flagCount; ++i)
 		reset(i);
@@ -71,4 +73,10 @@ bool GameFlags::query(int flag) const {
 	return !!(_flags[flag / 32] & (1 << (flag % 32)));
 }
 
+void GameFlags::save(SaveFile &f) {
+	for (int i = 0; i != _flagCount / 32 + 1; ++i) {
+		f.write(_flags[i]);
+	}
+}
+
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/game_flags.h b/engines/bladerunner/game_flags.h
index 83cbdbc..b409e85 100644
--- a/engines/bladerunner/game_flags.h
+++ b/engines/bladerunner/game_flags.h
@@ -27,6 +27,8 @@
 
 namespace BladeRunner {
 
+class SaveFile;
+
 class GameFlags {
 	uint32 *_flags;
 	int     _flagCount;
@@ -40,6 +42,8 @@ public:
 	void set(int flag);
 	void reset(int flag);
 	bool query(int flag) const;
+
+	void save(SaveFile &f);
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/item.cpp b/engines/bladerunner/item.cpp
index 7fca4d1..bf09efb 100644
--- a/engines/bladerunner/item.cpp
+++ b/engines/bladerunner/item.cpp
@@ -24,6 +24,7 @@
 
 #include "bladerunner/bladerunner.h"
 
+#include "bladerunner/savefile.h"
 #include "bladerunner/slice_renderer.h"
 #include "bladerunner/zbuffer.h"
 
@@ -173,4 +174,26 @@ bool Item::isUnderMouse(int mouseX, int mouseY) const {
 	    && mouseY <= _screenRectangle.bottom + 10;
 }
 
+void Item::save(SaveFile &f) {
+	f.write(_setId);
+	f.write(_itemId);
+	_boundingBox.save(f);
+	f.write(_screenRectangle);
+	f.write(_animationId);
+	f.write(_position);
+	f.write(_facing);
+	f.write(_angle);
+	f.write(_width);
+	f.write(_height);
+	f.write(_screenX);
+	f.write(_screenY);
+	f.write(_depth);
+	f.write(_isTarget);
+	f.write(_isSpinning);
+	f.write(_facingChange);
+	f.write(0.0f); // _viewAngle
+	f.write(_isVisible);
+	f.write(_isPoliceMazeEnemy);
+}
+
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/item.h b/engines/bladerunner/item.h
index 3beca15..bf07a11 100644
--- a/engines/bladerunner/item.h
+++ b/engines/bladerunner/item.h
@@ -32,6 +32,7 @@ namespace BladeRunner {
 
 class BladeRunnerEngine;
 class Items;
+class SaveFile;
 
 class Item {
 	friend class Items;
@@ -84,6 +85,8 @@ public:
 	void setup(int itemId, int setId, int animationId, Vector3 position, int facing, int height, int width, bool isTargetFlag, bool isVisible, bool isPoliceMazeEnemy);
 
 	bool isUnderMouse(int mouseX, int mouseY) const;
+
+	void save(SaveFile &f);
 };
 
 }
diff --git a/engines/bladerunner/items.cpp b/engines/bladerunner/items.cpp
index 0348efc..7fe769cb9 100644
--- a/engines/bladerunner/items.cpp
+++ b/engines/bladerunner/items.cpp
@@ -23,6 +23,7 @@
 #include "bladerunner/items.h"
 
 #include "bladerunner/game_constants.h"
+#include "bladerunner/savefile.h"
 #include "bladerunner/scene.h"
 #include "bladerunner/scene_objects.h"
 #include "bladerunner/zbuffer.h"
@@ -242,4 +243,19 @@ int Items::findItem(int itemId) const {
 	return -1;
 }
 
+void Items::save(SaveFile &f) {
+	int size = (int)_items.size();
+
+	f.write(size);
+	int i;
+	for (i = 0; i != size; ++i) {
+		_items[i]->save(f);
+	}
+
+	// Always write out 100 items
+	for (; i != 100; ++i) {
+		f.padBytes(0x174); // bbox + rect + 18 float fields
+	}
+}
+
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/items.h b/engines/bladerunner/items.h
index 6bbc99c..cc9af73 100644
--- a/engines/bladerunner/items.h
+++ b/engines/bladerunner/items.h
@@ -30,6 +30,8 @@
 
 namespace BladeRunner {
 
+class SaveFile;
+
 class Items {
 	BladeRunnerEngine *_vm;
 
@@ -64,6 +66,7 @@ public:
 
 	void spinInWorld(int itemId);
 
+	void save(SaveFile &f);
 private:
 	int findItem(int itemId) const;
 };
diff --git a/engines/bladerunner/module.mk b/engines/bladerunner/module.mk
index bf90922..f4bf926 100644
--- a/engines/bladerunner/module.mk
+++ b/engines/bladerunner/module.mk
@@ -40,7 +40,10 @@ MODULE_OBJS = \
 	obstacles.o \
 	outtake.o \
 	overlays.o \
+	police_maze.o \
+	police_maze_track.o \
 	regions.o \
+	savefile.o \
 	scene.o \
 	scene_objects.o \
 	screen_effects.o \
diff --git a/engines/bladerunner/movement_track.cpp b/engines/bladerunner/movement_track.cpp
index 47eb56a..5a6407e 100644
--- a/engines/bladerunner/movement_track.cpp
+++ b/engines/bladerunner/movement_track.cpp
@@ -22,6 +22,8 @@
 
 #include "bladerunner/movement_track.h"
 
+#include "bladerunner/savefile.h"
+
 namespace BladeRunner {
 
 MovementTrack::MovementTrack() {
@@ -107,4 +109,18 @@ bool MovementTrack::next(int *waypointId, int *delay, int *angle, bool *run) {
 	}
 }
 
+void MovementTrack::save(SaveFile &f) {
+	f.write(_currentIndex);
+	f.write(_lastIndex);
+	f.write(_hasNext);
+	f.write(_paused);
+	for (int i = 0; i < 100; ++i) {
+		Entry &e = _entries[i];
+		f.write(e.waypointId);
+		f.write(e.delay);
+		f.write(e.angle);
+		f.write(e.run);
+	}
+}
+
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/movement_track.h b/engines/bladerunner/movement_track.h
index cba9b69..2d59fd9 100644
--- a/engines/bladerunner/movement_track.h
+++ b/engines/bladerunner/movement_track.h
@@ -29,6 +29,7 @@ namespace BladeRunner {
 
 class BladeRunnerEngine;
 class BoundingBox;
+class SaveFile;
 
 class MovementTrack {
 	static const int kSize = 100;
@@ -59,8 +60,7 @@ public:
 	bool hasNext() const;
 	bool next(int *waypointId, int *delay, int *angle, bool *run);
 
-	//int saveGame();
-
+	void save(SaveFile &f);
 private:
 	void reset();
 };
diff --git a/engines/bladerunner/obstacles.cpp b/engines/bladerunner/obstacles.cpp
index 8061e78..603d514 100644
--- a/engines/bladerunner/obstacles.cpp
+++ b/engines/bladerunner/obstacles.cpp
@@ -24,11 +24,15 @@
 
 #include "bladerunner/bladerunner.h"
 
+#include "bladerunner/savefile.h"
+
 namespace BladeRunner {
 
 Obstacles::Obstacles(BladeRunnerEngine *vm) {
 	_vm = vm;
-	_vertices       = new Vector2[150];
+	_polygons       = new Polygon[kPolygonCount];
+	_polygonsBackup = new Polygon[kPolygonCount];
+	_vertices       = new Vector2[kVertexCount];
 	clear();
 }
 
@@ -40,7 +44,7 @@ void Obstacles::clear() {
 	for (int i = 0; i < kPolygonCount; i++) {
 		_polygons[i].isPresent = false;
 		_polygons[i].verticeCount = 0;
-		for (int j = 0; j < kVertexCount; j++) {
+		for (int j = 0; j < kPolygonVertexCount; j++) {
 			_polygons[i].vertices[j].x = 0.0f;
 			_polygons[i].vertices[j].y = 0.0f;
 		}
@@ -64,5 +68,29 @@ void Obstacles::backup() {
 
 void Obstacles::restore() {}
 
+void Obstacles::save(SaveFile &f) {
+	f.write(_backup);
+	f.write(_count);
+	for (int i = 0; i < _count; ++i) {
+		Polygon &p = _polygonsBackup[i];
+		f.write(p.isPresent);
+		f.write(p.verticeCount);
+		f.write(p.left);
+		f.write(p.bottom);
+		f.write(p.right);
+		f.write(p.top);
+		for (int j = 0; j < kPolygonVertexCount; ++j) {
+			f.write(p.vertices[j]);
+		}
+		for (int j = 0; j < kPolygonVertexCount; ++j) {
+			f.write(p.vertexType[j]);
+		}
+	}
+	for (int i = 0; i < kVertexCount; ++i) {
+		f.write(_vertices[i]);
+	}
+	f.write(_verticeCount);
+}
+
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/obstacles.h b/engines/bladerunner/obstacles.h
index c2c84c3..fc06fe6 100644
--- a/engines/bladerunner/obstacles.h
+++ b/engines/bladerunner/obstacles.h
@@ -28,10 +28,12 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
+class SaveFile;
 
 class Obstacles {
-	static const int kPolygonCount = 50;
-	static const int kVertexCount = 160;
+	static const int kVertexCount        = 150;
+	static const int kPolygonCount       =  50;
+	static const int kPolygonVertexCount = 160;
 
 	struct Polygon {
 		bool    isPresent;
@@ -40,14 +42,14 @@ class Obstacles {
 		float   bottom;
 		float   right;
 		float   top;
-		Vector2 vertices[kVertexCount];
-		int     vertexType[kVertexCount];
+		Vector2 vertices[kPolygonVertexCount];
+		int     vertexType[kPolygonVertexCount];
 	};
 
 	BladeRunnerEngine *_vm;
 
-	Polygon  _polygons[kPolygonCount];
-	Polygon  _polygonsBackup[kPolygonCount];
+	Polygon *_polygons;
+	Polygon *_polygonsBackup;
 	Vector2 *_vertices;
 	int      _verticeCount;
 	int      _count;
@@ -62,6 +64,7 @@ public:
 	bool find(const Vector3 &from, const Vector3 &to, Vector3 *next) const;
 	void backup();
 	void restore();
+	void save(SaveFile &f);
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/overlays.cpp b/engines/bladerunner/overlays.cpp
index b5cb130..0f011ad 100644
--- a/engines/bladerunner/overlays.cpp
+++ b/engines/bladerunner/overlays.cpp
@@ -25,6 +25,7 @@
 #include "bladerunner/bladerunner.h"
 
 #include "bladerunner/archive.h"
+#include "bladerunner/savefile.h"
 #include "bladerunner/vqa_player.h"
 
 #include "graphics/surface.h"
@@ -56,6 +57,8 @@ Overlays::~Overlays() {
 }
 
 int Overlays::play(const Common::String &name, int loopId, bool loopForever, bool startNow, int a6) {
+	assert(name.size() <= 12);
+
 	int id = mix_id(name);
 	int index = findById(id);
 	if (index < 0) {
@@ -63,12 +66,13 @@ int Overlays::play(const Common::String &name, int loopId, bool loopForever, boo
 		if (index < 0) {
 			return index;
 		}
+		_videos[index].loaded = true;
+		_videos[index].name = name;
 		_videos[index].id = id;
 		_videos[index].vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceFront);
 
 		// 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());
@@ -136,10 +140,26 @@ void Overlays::resetSingle(int i) {
 	_videos[i].loaded = false;
 	_videos[i].id = 0;
 	_videos[i].field2 = -1;
+	_videos[i].name.clear();
 }
 
 void Overlays::reset() {
 	_videos.clear();
 }
 
+void Overlays::save(SaveFile &f) {
+	for (int i = 0; i < kOverlayVideos; ++i) {
+		// 37 bytes per overlay
+		Video &ov = _videos[i];
+
+		f.write(ov.loaded);
+		f.write(nullptr);
+		f.write(ov.name, 13);
+		f.write(ov.id);
+		f.write(ov.field0);
+		f.write(ov.field1);
+		f.write(ov.field2);
+	}
+}
+
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/overlays.h b/engines/bladerunner/overlays.h
index 38edf74..fc8dfa11 100644
--- a/engines/bladerunner/overlays.h
+++ b/engines/bladerunner/overlays.h
@@ -33,20 +33,20 @@ struct Surface;
 namespace BladeRunner {
 
 class BladeRunnerEngine;
+class SaveFile;
 class VQAPlayer;
 
-
 class Overlays {
 	static const int kOverlayVideos = 5;
 
 	struct Video {
-		bool       loaded;
-		VQAPlayer *vqaPlayer;
-		// char       name[13];
-		int32      id;
-		int        field0;
-		int        field1;
-		int        field2;
+		bool            loaded;
+		VQAPlayer      *vqaPlayer;
+		Common::String  name;
+		int32           id;
+		int             field0;
+		int             field1;
+		int             field2;
 	};
 
 	BladeRunnerEngine *_vm;
@@ -62,6 +62,8 @@ public:
 	void removeAll();
 	void tick();
 
+	void save(SaveFile &f);
+
 private:
 	int findById(int32 id) const;
 	int findEmpty() const;
diff --git a/engines/bladerunner/police_maze.cpp b/engines/bladerunner/police_maze.cpp
new file mode 100644
index 0000000..223171d
--- /dev/null
+++ b/engines/bladerunner/police_maze.cpp
@@ -0,0 +1,64 @@
+/* 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/police_maze.h"
+
+#include "bladerunner/bladerunner.h"
+
+#include "bladerunner/police_maze_track.h"
+#include "bladerunner/savefile.h"
+
+namespace BladeRunner {
+
+PoliceMaze::PoliceMaze(BladeRunnerEngine *vm) : _vm(vm) {
+	reset();
+}
+
+PoliceMaze::~PoliceMaze() {
+	reset();
+}
+
+bool PoliceMaze::init() {
+	return true;
+}
+
+void PoliceMaze::save(SaveFile &f) {
+	f.write(_tracksCount);
+	f.write(_a2);
+	f.write(_a3);
+	for (int i = 0; i < 64; ++i) {
+		_tracks[i]->save(f);
+	}
+}
+
+void PoliceMaze::reset() {
+	_tracksCount = 0;
+	_a2 = 0;
+	_a3 = 0;
+	for (int i = 0; i < 64; ++i) {
+		_tracks[i] = nullptr;
+	}
+	_a4 = 0;
+	_a5 = 0;
+}
+
+} // End of namespace BladeRunner
diff --git a/engines/bladerunner/police_maze.h b/engines/bladerunner/police_maze.h
new file mode 100644
index 0000000..4bd06bb
--- /dev/null
+++ b/engines/bladerunner/police_maze.h
@@ -0,0 +1,54 @@
+/* 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_POLICE_MAZE_H
+#define BLADERUNNER_POLICE_MAZE_H
+
+namespace BladeRunner {
+
+class BladeRunnerEngine;
+class PoliceMazeTrack;
+class SaveFile;
+
+class PoliceMaze {
+	BladeRunnerEngine *_vm;
+
+	PoliceMazeTrack *_tracks[64];
+	int              _tracksCount;
+	int              _a2;
+	int              _a3;
+	int              _a4;
+	int              _a5;
+
+public:
+	PoliceMaze(BladeRunnerEngine *vm);
+	~PoliceMaze();
+
+	bool init();
+
+	void save(SaveFile &f);
+	void reset();
+};
+
+} // End of namespace BladeRunner
+
+#endif
diff --git a/engines/bladerunner/police_maze_track.cpp b/engines/bladerunner/police_maze_track.cpp
new file mode 100644
index 0000000..988f904
--- /dev/null
+++ b/engines/bladerunner/police_maze_track.cpp
@@ -0,0 +1,80 @@
+/* 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/police_maze_track.h"
+
+#include "bladerunner/bladerunner.h"
+
+#include "bladerunner/savefile.h"
+
+namespace BladeRunner {
+
+PoliceMazeTrack::PoliceMazeTrack(BladeRunnerEngine *vm) : _vm(vm) {
+	reset();
+}
+
+PoliceMazeTrack::~PoliceMazeTrack() {
+	reset();
+}
+
+void PoliceMazeTrack::save(SaveFile &f) {
+	f.write(_isPresent);
+	f.write(_itemId);
+	f.write(_count);
+	f.write(_dataIndex);
+	f.write(_a6);
+	f.write(_a7);
+	f.write(_pointIndex);
+	f.write(_a9);
+	f.write(_rotating);
+	f.write(_maxAngle);
+	f.write(_angleChange);
+	f.write(_a13);
+
+	for (int i = 0; i < 100; ++i) {
+		f.write(_points[i]);
+	}
+
+	f.write(_a4);
+	f.write(_a5);
+ }
+
+void PoliceMazeTrack::reset() {
+	_isPresent   = false;
+	_itemId      = -1;
+	_count       =  0;
+	_data        =  0;
+	_dataIndex   =  0;
+	_a4          =  0;
+	_a5          =  0;
+	_time        =  0;
+	_a6          =  0;
+	_a7          =  0;
+	_pointIndex  =  0;
+	_a9          =  0;
+	_rotating    =  0;
+	_maxAngle    =  0;
+	_angleChange =  0;
+	_a13         =  1;
+}
+
+} // End of namespace BladeRunner
diff --git a/engines/bladerunner/police_maze_track.h b/engines/bladerunner/police_maze_track.h
new file mode 100644
index 0000000..9de7a1b
--- /dev/null
+++ b/engines/bladerunner/police_maze_track.h
@@ -0,0 +1,65 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BLADERUNNER_POLICE_MAZE_TRACK_H
+#define BLADERUNNER_POLICE_MAZE_TRACK_H
+
+#include "bladerunner/vector.h"
+
+namespace BladeRunner {
+
+class BladeRunnerEngine;
+class SaveFile;
+
+class PoliceMazeTrack {
+	BladeRunnerEngine *_vm;
+
+	int     _time;
+	bool    _isPresent;
+	int     _itemId;
+	int     _count;
+	Vector2 _points[100];
+	int     _data;
+	int     _dataIndex;
+	int     _a4;
+	int     _a5;
+	int     _a6;
+	int     _a7;
+	int     _pointIndex;
+	int     _a9;
+	int     _rotating;
+	int     _maxAngle;
+	int     _angleChange;
+	int     _a13;
+
+public:
+	PoliceMazeTrack(BladeRunnerEngine *vm);
+	~PoliceMazeTrack();
+
+	void save(SaveFile &f);
+
+	void reset();
+};
+
+} // End of namespace BladeRunner
+
+#endif
diff --git a/engines/bladerunner/regions.cpp b/engines/bladerunner/regions.cpp
index 80dabf2..51b2bae 100644
--- a/engines/bladerunner/regions.cpp
+++ b/engines/bladerunner/regions.cpp
@@ -22,6 +22,8 @@
 
 #include "bladerunner/regions.h"
 
+#include "bladerunner/savefile.h"
+
 namespace BladeRunner {
 
 Regions::Regions() {
@@ -99,4 +101,13 @@ void Regions::enable() {
 	_enabled = true;
 }
 
+void Regions::save(SaveFile &f) {
+	f.write(_enabled);
+	for (int i = 0; i != 10; ++i) {
+		f.write(_regions[i].rectangle);
+		f.write(_regions[i].type);
+		f.write(_regions[i].present);
+	}
+}
+
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/regions.h b/engines/bladerunner/regions.h
index 9868f46..ed4dcba 100644
--- a/engines/bladerunner/regions.h
+++ b/engines/bladerunner/regions.h
@@ -30,6 +30,8 @@
 
 namespace BladeRunner {
 
+class SaveFile;
+
 class Regions {
 	friend class Debugger;
 
@@ -54,6 +56,8 @@ public:
 
 	void setEnabled(bool enabled);
 	void enable();
+
+	void save(SaveFile &f);
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/savefile.cpp b/engines/bladerunner/savefile.cpp
new file mode 100644
index 0000000..ff1ff75
--- /dev/null
+++ b/engines/bladerunner/savefile.cpp
@@ -0,0 +1,120 @@
+/* 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/savefile.h"
+
+#include "bladerunner/boundingbox.h"
+#include "bladerunner/vector.h"
+
+#include "common/rect.h"
+#include "common/savefile.h"
+
+namespace BladeRunner {
+
+SaveFile::SaveFile(Common::OutSaveFile *saveFile)
+	: _saveFile(saveFile),
+	  _stream(DisposeAfterUse::YES)
+{
+}
+
+void SaveFile::finalize() {
+	_saveFile->writeUint32LE(_stream.size() + 4);
+	_saveFile->write(_stream.getData(), _stream.size());
+	_saveFile->finalize();
+}
+
+void SaveFile::padBytes(int count) {
+	for (int i = 0; i < count; ++i) {
+		_stream.writeByte(0);
+	}
+}
+
+void SaveFile::write(bool v) {
+	_stream.writeUint32LE(v);
+}
+
+void SaveFile::write(int v) {
+	_stream.writeUint32LE(v);
+}
+
+void SaveFile::write(uint32 v) {
+	_stream.writeUint32LE(v);
+}
+
+void SaveFile::write(byte v) {
+	_stream.writeByte(v);
+}
+
+void SaveFile::write(float v) {
+	_stream.writeFloatLE(v);
+}
+
+void SaveFile::debug(char *p) {
+	_stream.write(p, strlen(p) + 1);
+}
+
+void SaveFile::write(char *p, int sz) {
+	_stream.write(p, sz);
+}
+
+void SaveFile::write(Common::String &s, int sz) {
+	assert(s.size() < (uint)sz);
+	_stream.write(s.begin(), s.size());
+	padBytes((uint)sz - s.size());
+}
+
+void SaveFile::write(const Vector2 &v) {
+	_stream.writeFloatLE(v.x);
+	_stream.writeFloatLE(v.y);
+}
+
+void SaveFile::write(const Vector3 &v) {
+	_stream.writeFloatLE(v.x);
+	_stream.writeFloatLE(v.y);
+	_stream.writeFloatLE(v.z);
+}
+
+void SaveFile::write(const Common::Rect &v) {
+	_stream.writeUint32LE(v.left);
+	_stream.writeUint32LE(v.top);
+	_stream.writeUint32LE(v.right);
+	_stream.writeUint32LE(v.bottom);
+}
+
+void SaveFile::write(const BoundingBox &v) {
+	float x0, y0, z0, x1, y1, z1;
+
+	v.getXYZ(&x0, &y0, &z0, &x1, &y1, &z1);
+	_stream.writeFloatLE(x0);
+	_stream.writeFloatLE(y0);
+	_stream.writeFloatLE(z0);
+	_stream.writeFloatLE(x1);
+	_stream.writeFloatLE(y1);
+	_stream.writeFloatLE(z1);
+
+	// Bounding boxes have a lot of extra data that's never actually used
+	for (int i = 0; i != 96; ++i) {
+		_stream.writeFloatLE(0.0f);
+	}
+}
+
+} // End of namespace BladeRunner
diff --git a/engines/bladerunner/savefile.h b/engines/bladerunner/savefile.h
new file mode 100644
index 0000000..8c5dc0a
--- /dev/null
+++ b/engines/bladerunner/savefile.h
@@ -0,0 +1,67 @@
+/* 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_SAVEFILE_H
+#define BLADERUNNER_SAVEFILE_H
+
+#include "common/memstream.h"
+#include "common/types.h"
+
+namespace Common {
+	class OutSaveFile;
+	class String;
+	struct Rect;
+}
+
+namespace BladeRunner {
+
+class Vector2;
+class Vector3;
+class BoundingBox;
+
+class SaveFile {
+	Common::OutSaveFile              *_saveFile;
+	Common::MemoryWriteStreamDynamic  _stream;
+public:
+	SaveFile(Common::OutSaveFile *saveFile);
+
+	// bool err();
+	void finalize();
+
+	void padBytes(int count);
+	void write(bool v);
+	void write(int v);
+	void write(uint32 v);
+	void write(byte  v);
+	void write(float v);
+	void debug(char *p);
+	void write(char *p, int sz);
+	void write(Common::String &s, int sz);
+	void write(const Vector2 &v);
+	void write(const Vector3 &v);
+	void write(const Common::Rect &v);
+	void write(const BoundingBox &v);
+};
+
+} // End of namespace BladeRunner
+
+#endif
diff --git a/engines/bladerunner/scene.cpp b/engines/bladerunner/scene.cpp
index 84d0dd4..d12c85e 100644
--- a/engines/bladerunner/scene.cpp
+++ b/engines/bladerunner/scene.cpp
@@ -30,6 +30,7 @@
 #include "bladerunner/items.h"
 #include "bladerunner/overlays.h"
 #include "bladerunner/regions.h"
+#include "bladerunner/savefile.h"
 #include "bladerunner/scene_objects.h"
 #include "bladerunner/screen_effects.h"
 #include "bladerunner/set.h"
@@ -412,4 +413,21 @@ void Scene::loopEnded(int frame, int loopId) {
 void Scene::loopEndedStatic(void *data, int frame, int loopId) {
 	((Scene *)data)->loopEnded(frame, loopId);
 }
+
+void Scene::save(SaveFile &f) {
+	f.write(_setId);
+	f.write(_sceneId);
+	f.write(_defaultLoop);
+	f.write(_defaultLoopSet);
+	f.write(_defaultLoopPreloadedSet);
+	f.write(_specialLoopMode);
+	f.write(_specialLoop);
+	f.write(_nextSetId);
+	f.write(_nextSceneId);
+	f.write(_frame);
+	f.write(_actorStartPosition);
+	f.write(_actorStartFacing);
+	f.write(_playerWalkedIn);
+}
+
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/scene.h b/engines/bladerunner/scene.h
index 91cd2ed..b1963e8 100644
--- a/engines/bladerunner/scene.h
+++ b/engines/bladerunner/scene.h
@@ -30,6 +30,7 @@ namespace BladeRunner {
 class BladeRunnerEngine;
 class BoundingBox;
 class Regions;
+class SaveFile;
 class Set;
 class VQAPlayer;
 
@@ -89,6 +90,8 @@ public:
 	void objectSetIsTarget(int objectId, bool isTarget, bool sceneLoaded);
 	const char *objectGetName(int objectId);
 
+	void save(SaveFile &f);
+
 private:
 	void loopEnded(int frame, int loopId);
 	static void loopEndedStatic(void *data, int frame, int loopId);
diff --git a/engines/bladerunner/scene_objects.cpp b/engines/bladerunner/scene_objects.cpp
index 148cde8..f68dccc 100644
--- a/engines/bladerunner/scene_objects.cpp
+++ b/engines/bladerunner/scene_objects.cpp
@@ -25,6 +25,7 @@
 #include "bladerunner/bladerunner.h"
 
 #include "bladerunner/obstacles.h"
+#include "bladerunner/savefile.h"
 #include "bladerunner/view.h"
 
 namespace BladeRunner {
@@ -327,4 +328,25 @@ void SceneObjects::updateObstacles() {
 	_vm->_obstacles->backup();
 }
 
+void SceneObjects::save(SaveFile &f) {
+	f.write(_count);
+	for (int i = 0; i < kSceneObjectCount; ++i) {
+		f.write(_sceneObjects[i].id);
+		f.write(_sceneObjects[i].type);
+		f.write(_sceneObjects[i].boundingBox);
+		f.write(_sceneObjects[i].screenRectangle);
+		f.write(_sceneObjects[i].distanceToCamera);
+		f.write(_sceneObjects[i].isPresent);
+		f.write(_sceneObjects[i].isClickable);
+		f.write(_sceneObjects[i].isObstacle);
+		f.write(_sceneObjects[i].unknown1);
+		f.write(_sceneObjects[i].isTarget);
+		f.write(_sceneObjects[i].isMoving);
+		f.write(_sceneObjects[i].isRetired);
+	}
+	for (int i = 0; i < kSceneObjectCount; ++i) {
+		f.write(_sceneObjectsSortedByDistance[i]);
+	}
+}
+
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/scene_objects.h b/engines/bladerunner/scene_objects.h
index de31eae..2f154db 100644
--- a/engines/bladerunner/scene_objects.h
+++ b/engines/bladerunner/scene_objects.h
@@ -30,6 +30,7 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
+class SaveFile;
 class View;
 
 enum SceneObjectType {
@@ -86,7 +87,7 @@ public:
 	void setIsTarget(int sceneObjectId, bool isTarget);
 	void updateObstacles();
 
-
+	void save(SaveFile &f);
 private:
 	int findById(int sceneObjectId) const;
 	bool addSceneObject(int sceneObjectId, SceneObjectType sceneObjectType, BoundingBox *boundingBox, Common::Rect *screenRectangle, bool isClickable, bool isObstacle, uint8 unknown1, bool isTarget, bool isMoving, bool isRetired);
diff --git a/engines/bladerunner/script/ai_script.cpp b/engines/bladerunner/script/ai_script.cpp
index c960ff1..257e323 100644
--- a/engines/bladerunner/script/ai_script.cpp
+++ b/engines/bladerunner/script/ai_script.cpp
@@ -345,4 +345,31 @@ void AIScripts::fledCombat(int actor) {
 	_inScriptCounter--;
 }
 
+void AIScripts::setAnimationState(int actor, int animationState, int animationFrame, int animationStateNext, int animationNext) {
+	if (actor >= _actorCount) {
+		return;
+	}
+
+	_inScriptCounter++;
+	if (_AIScripts[actor]) {
+		_AIScripts[actor]->SetAnimationState(animationState, animationFrame, animationStateNext, animationNext);
+	}
+	_inScriptCounter--;
+}
+
+
+void AIScripts::queryAnimationState(int actor, int *animationState, int *animationFrame, int *animationStateNext, int *nextAnimation) {
+		if (actor >= _actorCount) {
+		return;
+	}
+
+	_inScriptCounter++;
+	if (_AIScripts[actor]) {
+		_AIScripts[actor]->FledCombat();
+		_AIScripts[actor]->QueryAnimationState(animationState, animationFrame, animationStateNext, nextAnimation);
+	}
+	_inScriptCounter--;
+}
+
+
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/script/ai_script.h b/engines/bladerunner/script/ai_script.h
index cfd3fe5..5e782d0 100644
--- a/engines/bladerunner/script/ai_script.h
+++ b/engines/bladerunner/script/ai_script.h
@@ -566,6 +566,8 @@ public:
 	bool reachedMovementTrackWaypoint(int actor, int waypointId);
 	void updateAnimation(int actor, int *animation, int *frame);
 	void changeAnimationMode(int actor, int mode);
+	void queryAnimationState(int actor, int *animationState, int *animationFrame, int *animationStateNext, int *nextAnimation);
+	void setAnimationState(int actor, int animationState, int animationFrame, int animationStateNext, int animationNext);
 	void fledCombat(int actor);
 
 	bool isInsideScript() const { return _inScriptCounter > 0; }
diff --git a/engines/bladerunner/set.cpp b/engines/bladerunner/set.cpp
index fe10c18..6a83294 100644
--- a/engines/bladerunner/set.cpp
+++ b/engines/bladerunner/set.cpp
@@ -25,6 +25,7 @@
 #include "bladerunner/bladerunner.h"
 #include "bladerunner/game_constants.h"
 #include "bladerunner/lights.h"
+#include "bladerunner/savefile.h"
 #include "bladerunner/scene_objects.h"
 #include "bladerunner/set_effects.h"
 #include "bladerunner/slice_renderer.h"
@@ -82,6 +83,7 @@ bool Set::open(const Common::String &name) {
 		_objects[i].isObstacle = s->readByte();
 		_objects[i].isClickable = s->readByte();
 		_objects[i].isHotMouse = 0;
+		_objects[i].unknown1 = 0;
 		_objects[i].isTarget = 0;
 		s->skip(4);
 
@@ -327,4 +329,40 @@ int Set::getWalkboxSoundRunLeft(int walkboxId) const {
 int Set::getWalkboxSoundRunRight(int walkboxId) const {
 	return getWalkboxSoundWalkRight(walkboxId);
 }
+
+void Set::save(SaveFile &f) {
+	f.write(_loaded);
+	f.write(_objectCount);
+	f.write(_walkboxCount);
+
+	for (int i = 0; i != _objectCount; ++i) {
+		f.write(_objects[i].name, 20);
+		f.write(_objects[i].bbox);
+		f.write(_objects[i].isObstacle);
+		f.write(_objects[i].isClickable);
+		f.write(_objects[i].isHotMouse);
+		f.write(_objects[i].unknown1);
+		f.write(_objects[i].isTarget);
+	}
+
+	for (int i = 0; i != _walkboxCount; ++i) {
+		f.write(_walkboxes[i].name, 20);
+		f.write(_walkboxes[i].altitude);
+		f.write(_walkboxes[i].vertexCount);
+		for (int j = 0; j != 8; ++j) {
+			f.write(_walkboxes[i].vertices[j]);
+
+			// In BLADE.EXE vertices are a vec5
+			f.write(0);
+			f.write(0);
+		}
+	}
+
+	for (int i = 0; i != 85; ++i) {
+		f.write(_walkboxStepSound[i]);
+	}
+
+	f.write(_footstepSoundOverride);
+}
+
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/set.h b/engines/bladerunner/set.h
index a6e7e9f..7989ef1 100644
--- a/engines/bladerunner/set.h
+++ b/engines/bladerunner/set.h
@@ -33,6 +33,7 @@ namespace BladeRunner {
 class BladeRunnerEngine;
 
 class VQADecoder;
+class SaveFile;
 class SetEffects;
 class SceneObjects;
 
@@ -94,11 +95,14 @@ public:
 	void setWalkboxStepSound(int walkboxId, int soundId);
 	void setFoodstepSoundOverride(int soundId);
 	void resetFoodstepSoundOverride();
+
 	int getWalkboxSoundWalkLeft(int walkboxId) const;
 	int getWalkboxSoundWalkRight(int walkboxId) const;
 	int getWalkboxSoundRunLeft(int walkboxId) const;
 	int getWalkboxSoundRunRight(int walkboxId) const;
 
+	void save(SaveFile &f);
+
 private:
 	static bool isXZInWalkbox(float x, float z, const Walkbox &walkbox);
 };
diff --git a/engines/bladerunner/settings.cpp b/engines/bladerunner/settings.cpp
index a5540fc..e4db0b8 100644
--- a/engines/bladerunner/settings.cpp
+++ b/engines/bladerunner/settings.cpp
@@ -26,6 +26,7 @@
 #include "bladerunner/bladerunner.h"
 #include "bladerunner/chapters.h"
 #include "bladerunner/music.h"
+#include "bladerunner/savefile.h"
 #include "bladerunner/scene.h"
 
 #include "common/debug.h"
@@ -39,14 +40,23 @@ Settings::Settings(BladeRunnerEngine *vm) {
 	_playerAgenda = 1;
 
 	_chapter = 1;
+	_scene = -1;
+	_set = -1;
+	_unk0 = 0;
 	_gamma = 1.0f;
 
+	_ammoType = 0;
+	_ammoAmounts[0] = 1;
+	_ammoAmounts[1] = 0;
+	_ammoAmounts[2] = 0;
+
+	// The remaining fields are not reset in BLADE.EXE
 	_chapterChanged = false;
 	_newChapter = -1;
 	_newScene = -1;
 	_newSet = -1;
 
-	_startingGame = true;
+	_startingGame = false;
 	_loadingGame = false;
 
 	_fullHDFrames = true;
@@ -83,8 +93,9 @@ bool Settings::openNewScene() {
 		_vm->_scene->close(!_loadingGame && !_startingGame);
 	}
 	if (_chapterChanged) {
-		if (_vm->_chapters->hasOpenResources())
+		if (_vm->_chapters->hasOpenResources()) {
 			_vm->_chapters->closeResources();
+		}
 
 		int newChapter = _newChapter;
 		_chapterChanged = false;
@@ -103,6 +114,9 @@ bool Settings::openNewScene() {
 		return false;
 	}
 
+	_set = newSet;
+	_scene = newScene;
+
 	if (!_loadingGame && currentSet != newSet) {
 		// TODO: Reset actors for new set
 	}
@@ -171,4 +185,17 @@ void Settings::setLearyMode(bool learyMode) {
 	_learyMode = learyMode;
 }
 
+void Settings::save(SaveFile &f) {
+	f.write(_scene);
+	f.write(_set);
+	f.write(_chapter);
+	f.write(_playerAgenda);
+	f.write(_unk0);
+	f.write(_difficulty);
+	f.write(_ammoType);
+	for (int i = 0; i != 3; ++i) {
+		f.write(_ammoAmounts[i]);
+	}
+}
+
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/settings.h b/engines/bladerunner/settings.h
index bf2d2d3..bc1c255 100644
--- a/engines/bladerunner/settings.h
+++ b/engines/bladerunner/settings.h
@@ -26,6 +26,7 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
+class SaveFile;
 
 enum PlayerAgenda {
 	kPlayerAgendaPolite = 0,
@@ -39,6 +40,9 @@ class Settings {
 	BladeRunnerEngine *_vm;
 
 	int   _chapter;
+	int   _scene;
+	int   _set;
+	int   _unk0;
 	float _gamma;
 
 	bool  _chapterChanged;
@@ -49,6 +53,8 @@ class Settings {
 	bool  _startingGame;
 	bool  _loadingGame;
 
+	int   _unk1;
+
 	int   _fullHDFrames;
 	int   _mst3k;
 
@@ -117,6 +123,8 @@ public:
 
 	bool getLearyMode() const;
 	void setLearyMode(bool learyMode);
+
+	void save(SaveFile &f);
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/ui/spinner.cpp b/engines/bladerunner/ui/spinner.cpp
index 0740715..1f8f197 100644
--- a/engines/bladerunner/ui/spinner.cpp
+++ b/engines/bladerunner/ui/spinner.cpp
@@ -25,6 +25,7 @@
 #include "bladerunner/bladerunner.h"
 #include "bladerunner/game_constants.h"
 #include "bladerunner/mouse.h"
+#include "bladerunner/savefile.h"
 #include "bladerunner/scene.h"
 #include "bladerunner/shape.h"
 #include "bladerunner/text_resource.h"
@@ -262,6 +263,14 @@ void Spinner::resume() {
 	_vqaPlayer->setLoop(1, -1, kLoopSetModeJustStart, nullptr, nullptr);
 }
 
+void Spinner::save(SaveFile &f) {
+	assert(!_isOpen);
+
+	for (int i = 0; i != kSpinnerDestinations; ++i) {
+		f.write(_isDestinationSelectable[i]);
+	}
+}
+
 const Spinner::Destination *Spinner::getDestinationsFar() {
 	static const Destination destinations[] = {
 		{  0, Common::Rect(220, 227, 246, 262) },
diff --git a/engines/bladerunner/ui/spinner.h b/engines/bladerunner/ui/spinner.h
index b1785a5..24f66ed 100644
--- a/engines/bladerunner/ui/spinner.h
+++ b/engines/bladerunner/ui/spinner.h
@@ -29,9 +29,10 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
+class SaveFile;
 class Shape;
-class VQAPlayer;
 class UIImagePicker;
+class VQAPlayer;
 
 class Spinner {
 	static const int kSpinnerDestinations = 10;
@@ -70,6 +71,8 @@ public:
 	void reset();
 	void resume();
 
+	void save(SaveFile &f);
+
 private:
 	static void mouseUpCallback(int, void *);
 	static const Destination *getDestinationsFar();
diff --git a/engines/bladerunner/waypoints.cpp b/engines/bladerunner/waypoints.cpp
index 4d80841..0783af8 100644
--- a/engines/bladerunner/waypoints.cpp
+++ b/engines/bladerunner/waypoints.cpp
@@ -22,6 +22,8 @@
 
 #include "bladerunner/waypoints.h"
 
+#include "bladerunner/savefile.h"
+
 namespace BladeRunner {
 
 Waypoints::Waypoints(BladeRunnerEngine *vm, int count) {
@@ -89,4 +91,14 @@ float Waypoints::getZ(int waypointId) const {
 	return _waypoints[waypointId].position.z;
 }
 
+void Waypoints::save(SaveFile &f) {
+	f.write(_count);
+	for (int i = 0; i < _count; ++i) {
+		Waypoint &w = _waypoints[i];
+		f.write(w.setId);
+		f.write(w.position);
+		f.write(w.present);
+	}
+}
+
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/waypoints.h b/engines/bladerunner/waypoints.h
index 826f8f5..9159301 100644
--- a/engines/bladerunner/waypoints.h
+++ b/engines/bladerunner/waypoints.h
@@ -30,6 +30,8 @@
 
 namespace BladeRunner {
 
+class SaveFile;
+
 class Waypoints {
 	friend class Debugger;
 
@@ -55,6 +57,8 @@ public:
 
 	bool set(int waypointId, int setId, Vector3 position);
 	bool reset(int waypointId);
+
+	void save(SaveFile &f);
 };
 
 } // End of namespace BladeRunner


Commit: 5e9985bd6e4d136ec20e5e3be7589d34051227f1
    https://github.com/scummvm/scummvm/commit/5e9985bd6e4d136ec20e5e3be7589d34051227f1
Author: Peter Kohaut (peter.kohaut at gmail.com)
Date: 2018-03-24T17:28:34+01:00

Commit Message:
BLADERUNNER: Load game methods

save methods update
replaced few char* with Common::String
changed few pointers to references

Changed paths:
  R engines/bladerunner/police_maze.cpp
  R engines/bladerunner/police_maze.h
  R engines/bladerunner/police_maze_track.cpp
  R engines/bladerunner/police_maze_track.h
    engines/bladerunner/actor.cpp
    engines/bladerunner/actor.h
    engines/bladerunner/actor_clues.cpp
    engines/bladerunner/actor_clues.h
    engines/bladerunner/actor_combat.cpp
    engines/bladerunner/actor_combat.h
    engines/bladerunner/actor_dialogue_queue.cpp
    engines/bladerunner/actor_dialogue_queue.h
    engines/bladerunner/actor_walk.cpp
    engines/bladerunner/actor_walk.h
    engines/bladerunner/ambient_sounds.cpp
    engines/bladerunner/ambient_sounds.h
    engines/bladerunner/archive.cpp
    engines/bladerunner/archive.h
    engines/bladerunner/audio_player.cpp
    engines/bladerunner/audio_speech.cpp
    engines/bladerunner/audio_speech.h
    engines/bladerunner/bladerunner.cpp
    engines/bladerunner/bladerunner.h
    engines/bladerunner/boundingbox.cpp
    engines/bladerunner/boundingbox.h
    engines/bladerunner/combat.cpp
    engines/bladerunner/combat.h
    engines/bladerunner/crimes_database.cpp
    engines/bladerunner/crimes_database.h
    engines/bladerunner/debugger.cpp
    engines/bladerunner/dialogue_menu.cpp
    engines/bladerunner/dialogue_menu.h
    engines/bladerunner/fog.cpp
    engines/bladerunner/fog.h
    engines/bladerunner/game_flags.cpp
    engines/bladerunner/game_flags.h
    engines/bladerunner/game_info.cpp
    engines/bladerunner/game_info.h
    engines/bladerunner/item.cpp
    engines/bladerunner/item.h
    engines/bladerunner/items.cpp
    engines/bladerunner/items.h
    engines/bladerunner/light.cpp
    engines/bladerunner/light.h
    engines/bladerunner/module.mk
    engines/bladerunner/movement_track.cpp
    engines/bladerunner/movement_track.h
    engines/bladerunner/music.cpp
    engines/bladerunner/music.h
    engines/bladerunner/obstacles.cpp
    engines/bladerunner/obstacles.h
    engines/bladerunner/overlays.cpp
    engines/bladerunner/overlays.h
    engines/bladerunner/regions.cpp
    engines/bladerunner/regions.h
    engines/bladerunner/savefile.cpp
    engines/bladerunner/savefile.h
    engines/bladerunner/scene.cpp
    engines/bladerunner/scene.h
    engines/bladerunner/scene_objects.cpp
    engines/bladerunner/scene_objects.h
    engines/bladerunner/script/ai_script.cpp
    engines/bladerunner/script/ai_script.h
    engines/bladerunner/script/police_maze.cpp
    engines/bladerunner/script/police_maze.h
    engines/bladerunner/script/script.cpp
    engines/bladerunner/script/script.h
    engines/bladerunner/set.cpp
    engines/bladerunner/set.h
    engines/bladerunner/set_effects.cpp
    engines/bladerunner/set_effects.h
    engines/bladerunner/settings.cpp
    engines/bladerunner/settings.h
    engines/bladerunner/text_resource.cpp
    engines/bladerunner/ui/elevator.cpp
    engines/bladerunner/ui/kia.cpp
    engines/bladerunner/ui/kia_log.cpp
    engines/bladerunner/ui/kia_log.h
    engines/bladerunner/ui/spinner.cpp
    engines/bladerunner/ui/spinner.h
    engines/bladerunner/ui/ui_scroll_box.cpp
    engines/bladerunner/waypoints.cpp
    engines/bladerunner/waypoints.h


diff --git a/engines/bladerunner/actor.cpp b/engines/bladerunner/actor.cpp
index aae1085..998791d 100644
--- a/engines/bladerunner/actor.cpp
+++ b/engines/bladerunner/actor.cpp
@@ -52,7 +52,6 @@ Actor::Actor(BladeRunnerEngine *vm, int actorId) {
 	_walkInfo      = new ActorWalk(vm);
 	_movementTrack = new MovementTrack();
 	_clues         = new ActorClues(vm, (actorId && actorId != 99) ? 2 : 4);
-	_bbox          = new BoundingBox();
 	_combatInfo    = new ActorCombat(vm);
 
 	_friendlinessToOther.resize(_vm->_gameInfo->getActorCount());
@@ -65,7 +64,6 @@ Actor::Actor(BladeRunnerEngine *vm, int actorId) {
 
 Actor::~Actor() {
 	delete _combatInfo;
-	delete _bbox;
 	delete _clues;
 	delete _movementTrack;
 	delete _walkInfo;
@@ -355,7 +353,7 @@ void Actor::setAtXYZ(const Vector3 &position, int facing, bool snapFacing, bool
 	_vm->_sceneObjects->remove(_id + kSceneObjectOffsetActors);
 
 	if (_vm->_scene->getSetId() == _setId) {
-		_vm->_sceneObjects->addActor(_id + kSceneObjectOffsetActors, _bbox, &_screenRectangle, true, moving, _isTarget, retired);
+		_vm->_sceneObjects->addActor(_id + kSceneObjectOffsetActors, _bbox, _screenRectangle, true, moving, _isTarget, retired);
 	}
 }
 
@@ -469,7 +467,7 @@ bool Actor::loopWalkToItem(int itemId, int destinationOffset, int interruptible,
 	return loopWalk(itemPosition, destinationOffset, interruptible, runFlag, _position, width, 24.0f, a5, isRunningFlag, false);
 }
 
-bool Actor::loopWalkToSceneObject(const char *objectName, int destinationOffset, bool interruptible, bool runFlag, bool a5, bool *isRunningFlag) {
+bool Actor::loopWalkToSceneObject(const Common::String &objectName, int destinationOffset, bool interruptible, bool runFlag, bool a5, bool *isRunningFlag) {
 	int sceneObject = _vm->_scene->_set->findObject(objectName);
 	if (sceneObject < 0) {
 		return true;
@@ -760,21 +758,21 @@ void Actor::setFacing(int facing, bool halfOrSet) {
 
 void Actor::setBoundingBox(const Vector3 &position, bool retired) {
 	if (retired) {
-		_bbox->setXYZ(position.x - (_retiredWidth / 2.0f),
-		              position.y,
-		              position.z - (_retiredWidth / 2.0f),
+		_bbox.setXYZ(position.x - (_retiredWidth / 2.0f),
+		             position.y,
+		             position.z - (_retiredWidth / 2.0f),
 
-		              position.x + (_retiredWidth / 2.0f),
-		              position.y + _retiredHeight,
-		              position.z + (_retiredWidth / 2.0f));
+		             position.x + (_retiredWidth / 2.0f),
+		             position.y + _retiredHeight,
+		             position.z + (_retiredWidth / 2.0f));
 	} else {
-		_bbox->setXYZ(position.x - 12.0f,
-		              position.y + 6.0f,
-		              position.z - 12.0f,
+		_bbox.setXYZ(position.x - 12.0f,
+		             position.y + 6.0f,
+		             position.z - 12.0f,
 
-		              position.x + 12.0f,
-		              position.y + 72.0f,
-		              position.z + 12.0f);
+		             position.x + 12.0f,
+		             position.y + 72.0f,
+		             position.z + 12.0f);
 	}
 }
 
@@ -820,7 +818,7 @@ void Actor::faceActor(int otherActorId, bool animate) {
 	faceXYZ(otherActor->_position, animate);
 }
 
-void Actor::faceObject(const char *objectName, bool animate) {
+void Actor::faceObject(const Common::String &objectName, bool animate) {
 	int objectId = _vm->_scene->findObject(objectName);
 	if (objectId == -1) {
 		return;
@@ -1066,13 +1064,10 @@ int Actor::getGoal() const {
 }
 
 void Actor::speechPlay(int sentenceId, bool voiceOver) {
-	char name[13];
-	sprintf(name, "%02d-%04d%s.AUD", _id, sentenceId, _vm->_languageCode);
-	int balance;
+	Common::String name = Common::String::format( "%02d-%04d%s.AUD", _id, sentenceId, _vm->_languageCode.c_str());
 
-	if (voiceOver || _id == BladeRunnerEngine::kActorVoiceOver) {
-		balance = 0;
-	} else {
+	int balance = 0;
+	if (!voiceOver && _id != BladeRunnerEngine::kActorVoiceOver) {
 		// Vector3 pos = _vm->_view->_frameViewMatrix * _position;
 		int screenX = 320; //, screenY = 0;
 		//TODO: transform to screen space using fov;
@@ -1226,66 +1221,66 @@ bool Actor::walkToNearestPoint(const Vector3 &destination, float distance) {
 	return false;
 }
 
-void Actor::save(SaveFile &f) {
-	f.write(_id);
-	f.write(_setId);
-	f.write(_position);
-	f.write(_facing);
-	f.write(_targetFacing);
-	f.write(0); // TODO: _timer4RemainDefault
-
-	f.write(_honesty);
-	f.write(_intelligence);
-	f.write(_stability);
-	f.write(_combatAggressiveness);
-	f.write(_goalNumber);
-
-	f.write(_currentHP);
-	f.write(_maxHP);
-
-	f.write(_movementTrackPaused);
-	f.write(_movementTrackNextWaypointId);
-	f.write(_movementTrackNextDelay);
-	f.write(_movementTrackNextAngle);
-	f.write(_movementTrackNextRunning);
-
-	f.write(0); // TODO: _clueType
-	f.write(_isMoving);
-	f.write(_isTarget);
-	f.write(_inCombat);
-	f.write(_isInvisible);
-	f.write(_isRetired);
-	f.write(_isImmuneToObstacles);
-
-	f.write(_animationMode);
-	f.write(_fps);
-	f.write(_frameMs);
-	f.write(_animationId);
-	f.write(_animationFrame);
-
-	f.write(_movementTrackWalkingToWaypointId);
-	f.write(_movementTrackDelayOnNextWaypoint);
-
-	f.write(_screenRectangle);
-	f.write(_retiredWidth);
-	f.write(_retiredHeight);
-	f.write(_damageAnimIfMoving);
-	f.write(0); // TODO: _actorFieldU6
-	f.write(0); // TODO: _actorFieldU7
-	f.write(_scale);
+void Actor::save(SaveFileWriteStream &f) {
+	f.writeInt(_id);
+	f.writeInt(_setId);
+	f.writeVector3(_position);
+	f.writeInt(_facing);
+	f.writeInt(_targetFacing);
+	f.writeInt(0); // TODO: _timer4RemainDefault
+
+	f.writeInt(_honesty);
+	f.writeInt(_intelligence);
+	f.writeInt(_stability);
+	f.writeInt(_combatAggressiveness);
+	f.writeInt(_goalNumber);
+
+	f.writeInt(_currentHP);
+	f.writeInt(_maxHP);
+
+	f.writeBool(_movementTrackPaused);
+	f.writeInt(_movementTrackNextWaypointId);
+	f.writeInt(_movementTrackNextDelay);
+	f.writeInt(_movementTrackNextAngle);
+	f.writeBool(_movementTrackNextRunning);
+
+	f.writeInt(0); // TODO: _clueType
+	f.writeBool(_isMoving);
+	f.writeBool(_isTarget);
+	f.writeBool(_inCombat);
+	f.writeBool(_isInvisible);
+	f.writeBool(_isRetired);
+	f.writeBool(_isImmuneToObstacles);
+
+	f.writeInt(_animationMode);
+	f.writeInt(_fps);
+	f.writeInt(_frameMs);
+	f.writeInt(_animationId);
+	f.writeInt(_animationFrame);
+
+	f.writeInt(_movementTrackWalkingToWaypointId);
+	f.writeInt(_movementTrackDelayOnNextWaypoint);
+
+	f.writeRect(_screenRectangle);
+	f.writeInt(_retiredWidth);
+	f.writeInt(_retiredHeight);
+	f.writeInt(_damageAnimIfMoving);
+	f.writeInt(0); // TODO: _actorFieldU6
+	f.writeInt(0); // TODO: _actorFieldU7
+	f.writeFloat(_scale);
 
 	for (int i = 0; i < 7; ++i) {
-		f.write(_timersLeft[i]);
+		f.writeInt(_timersLeft[i]);
 	}
 
 	uint32 now = _vm->getTotalPlayTime(); // TODO: should be last lock time
 	for (int i = 0; i < 7; ++i) {
-		f.write(_timersLast[i] - now);
+		f.writeInt(_timersLast[i] - now);
 	}
 
 	int actorCount = _vm->_gameInfo->getActorCount();
 	for (int i = 0; i != actorCount; ++i) {
-		f.write(_friendlinessToOther[i]);
+		f.writeInt(_friendlinessToOther[i]);
 	}
 
 	_clues->save(f);
@@ -1294,12 +1289,89 @@ void Actor::save(SaveFile &f) {
 
 	_walkInfo->save(f);
 
-	_bbox->save(f);
+	f.writeBoundingBox(_bbox);
 
 	_combatInfo->save(f);
-	f.write(_animationModeCombatIdle);
-	f.write(_animationModeCombatWalk);
-	f.write(_animationModeCombatRun);
+	f.writeInt(_animationModeCombatIdle);
+	f.writeInt(_animationModeCombatWalk);
+	f.writeInt(_animationModeCombatRun);
+}
+
+void Actor::load(SaveFileReadStream &f) {
+	_id = f.readInt();
+	_setId = f.readInt();
+	_position = f.readVector3();
+	_facing = f.readInt();
+	_targetFacing = f.readInt();
+	f.skip(4); // TODO: _timer4RemainDefault
+
+	_honesty = f.readInt();
+	_intelligence = f.readInt();
+	_stability = f.readInt();
+	_combatAggressiveness = f.readInt();
+	_goalNumber = f.readInt();
+
+	_currentHP = f.readInt();
+	_maxHP = f.readInt();
+
+	_movementTrackPaused = f.readBool();
+	_movementTrackNextWaypointId = f.readInt();
+	_movementTrackNextDelay = f.readInt();
+	_movementTrackNextAngle = f.readInt();
+	_movementTrackNextRunning = f.readBool();
+
+	f.skip(4); // TODO: _clueType
+	_isMoving = f.readBool();
+	_isTarget = f.readBool();
+	_inCombat = f.readBool();
+	_isInvisible = f.readBool();
+	_isRetired = f.readBool();
+	_isImmuneToObstacles = f.readBool();
+
+	_animationMode = f.readInt();
+	_fps = f.readInt();
+	_frameMs = f.readInt();
+	_animationId = f.readInt();
+	_animationFrame = f.readInt();
+
+	_movementTrackWalkingToWaypointId = f.readInt();
+	_movementTrackDelayOnNextWaypoint = f.readInt();
+
+	_screenRectangle = f.readRect();
+	_retiredWidth = f.readInt();
+	_retiredHeight = f.readInt();
+	_damageAnimIfMoving = f.readInt();
+	f.skip(4); // TODO: _actorFieldU6
+	f.skip(4); // TODO: _actorFieldU7
+	_scale = f.readFloat();
+
+	for (int i = 0; i < 7; ++i) {
+		_timersLeft[i] = f.readInt();
+	}
+
+	uint32 now = _vm->getTotalPlayTime(); // TODO: should be last lock time
+	for (int i = 0; i < 7; ++i) {
+		_timersLast[i] = f.readInt() + now;
+	}
+
+	int actorCount = _vm->_gameInfo->getActorCount();
+	for (int i = 0; i != actorCount; ++i) {
+		_friendlinessToOther[i] = f.readInt();
+	}
+
+	_clues->load(f);
+
+	_movementTrack->load(f);
+
+	_walkInfo->load(f);
+
+	_bbox = f.readBoundingBox();
+
+	_combatInfo->load(f);
+
+	_animationModeCombatIdle = f.readInt();
+	_animationModeCombatWalk = f.readInt();
+	_animationModeCombatRun = f.readInt();
 }
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/actor.h b/engines/bladerunner/actor.h
index 55ffc4f..7498d2b 100644
--- a/engines/bladerunner/actor.h
+++ b/engines/bladerunner/actor.h
@@ -23,6 +23,7 @@
 #ifndef BLADERUNNER_ACTOR_H
 #define BLADERUNNER_ACTOR_H
 
+#include "bladerunner/boundingbox.h"
 #include "bladerunner/vector.h"
 
 #include "common/array.h"
@@ -36,14 +37,15 @@ class ActorWalk;
 class BladeRunnerEngine;
 class BoundingBox;
 class MovementTrack;
-class SaveFile;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 class View;
 
 class Actor {
 	BladeRunnerEngine *_vm;
 
 public:
-	BoundingBox   *_bbox;
+	BoundingBox    _bbox;
 	Common::Rect   _screenRectangle;
 	MovementTrack *_movementTrack;
 	ActorWalk     *_walkInfo;
@@ -147,7 +149,7 @@ public:
 	bool walkTo(bool runFlag, const Vector3 &destination, bool a3);
 	bool loopWalkToActor(int otherActorId, int destinationOffset, int interruptible, bool runFlag, bool a5, bool *isRunningFlag);
 	bool loopWalkToItem(int itemId, int destinationOffset, int interruptible, bool runFlag, bool a5, bool *isRunningFlag);
-	bool loopWalkToSceneObject(const char *objectName, int destinationOffset, bool interruptible, bool runFlag, bool a5, bool *isRunningFlag);
+	bool loopWalkToSceneObject(const Common::String &objectName, int destinationOffset, bool interruptible, bool runFlag, bool a5, bool *isRunningFlag);
 	bool loopWalkToWaypoint(int waypointId, int destinationOffset, int interruptible, bool runFlag, bool a5, bool *isRunningFlag);
 	bool loopWalkToXYZ(const Vector3 &destination, int destinationOffset, bool interruptible, bool runFlag, bool a5, bool *isRunningFlag);
 	bool asyncWalkToWaypoint(int waypointId, int destinationOffset, bool runFlag, bool a5);
@@ -160,8 +162,8 @@ public:
 
 	int getSetId()  const;
 	void setSetId(int setId);
-	BoundingBox *getBoundingBox() const { return _bbox; }
-	Common::Rect *getScreenRectangle() { return &_screenRectangle; }
+	const BoundingBox &getBoundingBox() const { return _bbox; }
+	const Common::Rect &getScreenRectangle() { return _screenRectangle; }
 	int getWalkbox() const { return _walkboxId; }
 
 	bool isRetired() const { return _isRetired; }
@@ -179,7 +181,7 @@ public:
 	void stopWalking(bool value);
 
 	void faceActor(int otherActorId, bool animate);
-	void faceObject(const char *objectName, bool animate);
+	void faceObject(const Common::String &objectName, bool animate);
 	void faceItem(int itemId, bool animate);
 	void faceWaypoint(int waypointId, bool animate);
 	void faceXYZ(float x, float y, float z, bool animate);
@@ -247,6 +249,9 @@ public:
 
 	bool isObstacleBetween(const Vector3 &target);
 
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
+
 	static int findTargetUnderMouse(BladeRunnerEngine *vm, int mouseX, int mouseY);
 
 private:
@@ -257,9 +262,6 @@ private:
 	bool walkFindU2(Vector3 *newDestination, float targetWidth, int destinationOffset, float targetSize, const Vector3 &startPosition, const Vector3 &targetPosition);
 	bool walkToNearestPoint(const Vector3 &destination, float distance);
 	//bool walkFindU3(int actorId, Vector3 from, int distance, Vector3 *out);
-
-public:
-	void save(SaveFile &f);
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/actor_clues.cpp b/engines/bladerunner/actor_clues.cpp
index 89db6fe..0c25ffc 100644
--- a/engines/bladerunner/actor_clues.cpp
+++ b/engines/bladerunner/actor_clues.cpp
@@ -217,21 +217,41 @@ void ActorClues::remove(int index) {
 	_clues[index].field8 = 0;
 }
 
-void ActorClues::save(SaveFile &f) {
-	f.write(_count);
-	f.write(_maxCount);
+void ActorClues::save(SaveFileWriteStream &f) {
+	f.writeInt(_count);
+	f.writeInt(_maxCount);
 	for (int i = 0; i < _count; ++i) {
 		Clue &c = _clues[i];
-		f.write(c.clueId);
-		f.write(c.weight);
-		f.write(c.fromActorId);
-		f.write(c.field3);
-		f.write(c.field4);
-		f.write(c.field5);
-		f.write(c.field6);
-		f.write(c.field7);
-		f.write(c.field8);
-		f.write(c.flags);
+		f.writeInt(c.clueId);
+		f.writeInt(c.weight);
+		f.writeInt(c.fromActorId);
+		f.writeInt(c.field3);
+		f.writeInt(c.field4);
+		f.writeInt(c.field5);
+		f.writeInt(c.field6);
+		f.writeInt(c.field7);
+		f.writeInt(c.field8);
+		f.writeByte(c.flags);
+	}
+}
+
+void ActorClues::load(SaveFileReadStream &f) {
+	_count = f.readInt();
+	_maxCount = f.readInt();
+	_clues.clear();
+	_clues.resize(_maxCount);
+	for (int i = 0; i < _count; ++i) {
+		Clue &c = _clues[i];
+		c.clueId = f.readInt();
+		c.weight = f.readInt();
+		c.fromActorId = f.readInt();
+		c.field3 = f.readInt();
+		c.field4 = f.readInt();
+		c.field5 = f.readInt();
+		c.field6 = f.readInt();
+		c.field7 = f.readInt();
+		c.field8 = f.readInt();
+		c.flags = f.readByte();
 	}
 }
 
diff --git a/engines/bladerunner/actor_clues.h b/engines/bladerunner/actor_clues.h
index 03ccff2..79181cc 100644
--- a/engines/bladerunner/actor_clues.h
+++ b/engines/bladerunner/actor_clues.h
@@ -28,7 +28,8 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
-class SaveFile;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 
 class ActorClues {
 	struct Clue {
@@ -41,7 +42,7 @@ class ActorClues {
 		int field6;
 		int field7;
 		int field8;
-		unsigned char flags;
+		byte flags;
 	};
 
 	BladeRunnerEngine *_vm;
@@ -75,8 +76,8 @@ public:
 
 	void removeAll();
 
-	void save(SaveFile &f);
-	//loadgame
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
 
 private:
 	bool exists(int clueId) const;
diff --git a/engines/bladerunner/actor_combat.cpp b/engines/bladerunner/actor_combat.cpp
index 7a60d6d..4bcce5d 100644
--- a/engines/bladerunner/actor_combat.cpp
+++ b/engines/bladerunner/actor_combat.cpp
@@ -50,31 +50,6 @@ void ActorCombat::setup() {
 	reset();
 }
 
-void ActorCombat::save(SaveFile &f) {
-	// TODO
-	f.write(0); // _actorId
-	f.write(0); // _combatOn
-	f.write(0); // _field2
-	f.write(0); // _field3
-	f.write(0); // _otherActorId
-	f.write(0); // _field5
-	f.write(0); // _field6
-	f.write(0); // _field7
-	f.write(0); // _field8
-	f.write(0); // _field9
-	f.write(0); // _field10
-	f.write(0); // _field11
-	f.write(0); // _field12
-	f.write(0); // _actorHp
-	f.write(0); // _field14
-	f.write(0); // _field15
-	f.write(0); // _actorPosition
-	f.write(0); // _otherActorPosition
-	f.write(0); // _availableCoversCount
-	f.write(0); // _availableFleeWaypointsCount
-	f.write(0); // _field24
-}
-
 void ActorCombat::combatOn(int actorId, int initialState, bool rangedAttackFlag, int enemyId, int waypointType, int fleeRatio, int coverRatio, int actionRatio, int damage, int range, bool unstoppable) {
 	_actorId = actorId;
 	_state = initialState;
@@ -316,11 +291,59 @@ void ActorCombat::hitAttempt() {
 	}
 }
 
+void ActorCombat::save(SaveFileWriteStream &f) {
+	f.writeInt(_actorId);
+	f.writeBool(_active);
+	f.writeInt(_state);
+	f.writeBool(_rangedAttack);
+	f.writeInt(_enemyId);
+	f.writeInt(_waypointType);
+	f.writeInt(_damage);
+	f.writeInt(_fleeRatio);
+	f.writeInt(_coverRatio);
+	f.writeInt(_actionRatio);
+	f.writeInt(_fleeRatioConst);
+	f.writeInt(_coverRatioConst);
+	f.writeInt(_actionRatioConst);
+	f.writeInt(_range);
+	f.writeInt(_unstoppable);
+	f.writeInt(_actorHp);
+	f.writeInt(_fleeingTowards);
+	f.writeVector3(_actorPosition);
+	f.writeVector3(_enemyPosition);
+	f.writeInt(_coversWaypointCount);
+	f.writeInt(_fleeWaypointsCount);
+}
+
+void ActorCombat::load(SaveFileReadStream &f) {
+	_actorId = f.readInt();
+	_active = f.readBool();
+	_state = f.readInt();
+	_rangedAttack = f.readBool();
+	_enemyId = f.readInt();
+	_waypointType = f.readInt();
+	_damage = f.readInt();
+	_fleeRatio = f.readInt();
+	_coverRatio = f.readInt();
+	_actionRatio = f.readInt();
+	_fleeRatioConst = f.readInt();
+	_coverRatioConst = f.readInt();
+	_actionRatioConst = f.readInt();
+	_range = f.readInt();
+	_unstoppable = f.readInt();
+	_actorHp = f.readInt();
+	_fleeingTowards = f.readInt();
+	_actorPosition = f.readVector3();
+	_enemyPosition = f.readVector3();
+	_coversWaypointCount = f.readInt();
+	_fleeWaypointsCount = f.readInt();
+}
+
 void ActorCombat::reset() {
 	_active              = false;
 	_actorId             = -1;
 	_state               = -1;
-	_rangedAttack        = -1;
+	_rangedAttack        = false;
 	_enemyId             = -1;
 	_waypointType        = -1;
 	_damage              = 0;
diff --git a/engines/bladerunner/actor_combat.h b/engines/bladerunner/actor_combat.h
index a621c3d..2f65f19 100644
--- a/engines/bladerunner/actor_combat.h
+++ b/engines/bladerunner/actor_combat.h
@@ -28,7 +28,8 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
-class SaveFile;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 
 class ActorCombat {
 	BladeRunnerEngine *_vm;
@@ -36,7 +37,7 @@ class ActorCombat {
 	int _actorId;
 	bool _active;
 	int _state;
-	int _rangedAttack;
+	bool _rangedAttack;
 	int _enemyId;
 	int _waypointType;
 	int _damage;
@@ -66,10 +67,11 @@ public:
 
 	void tick();
 	
-	void save(SaveFile &f);
-
 	void hitAttempt();
 
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
+
 private:
 	void reset();
 
diff --git a/engines/bladerunner/actor_dialogue_queue.cpp b/engines/bladerunner/actor_dialogue_queue.cpp
index 5f0ad60..48fcf8a 100644
--- a/engines/bladerunner/actor_dialogue_queue.cpp
+++ b/engines/bladerunner/actor_dialogue_queue.cpp
@@ -54,7 +54,7 @@ void ActorDialogueQueue::add(int actorId, int sentenceId, int animationMode) {
 	if (actorId == 0 || actorId == BladeRunnerEngine::kActorVoiceOver) {
 		animationMode = -1;
 	}
-	if (_entries.size() < 25) {
+	if (_entries.size() < kMaxEntries) {
 		Entry entry;
 		entry.isNotPause = true;
 		entry.isPause = false;
@@ -68,7 +68,7 @@ void ActorDialogueQueue::add(int actorId, int sentenceId, int animationMode) {
 }
 
 void ActorDialogueQueue::addPause(int delay) {
-	if (_entries.size() < 25) {
+	if (_entries.size() < kMaxEntries) {
 		Entry entry;
 		entry.isNotPause = false;
 		entry.isPause = true;
@@ -160,30 +160,58 @@ void ActorDialogueQueue::tick() {
 	}
 }
 
-void ActorDialogueQueue::save(SaveFile &f) {
+void ActorDialogueQueue::save(SaveFileWriteStream &f) {
 	int count = (int)_entries.size();
-	f.write(count);
+	f.writeInt(count);
 	for (int i = 0; i < count; ++i) {
 		Entry &e = _entries[i];
-		f.write(e.isNotPause);
-		f.write(e.isPause);
-		f.write(e.actorId);
-		f.write(e.sentenceId);
-		f.write(e.animationMode);
-		f.write(e.delay);
+		f.writeBool(e.isNotPause);
+		f.writeBool(e.isPause);
+		f.writeInt(e.actorId);
+		f.writeInt(e.sentenceId);
+		f.writeInt(e.animationMode);
+		f.writeInt(e.delay);
 	}
-	f.padBytes((25 - count) * 24);
-
-	f.write(_isNotPause);
-	f.write(_actorId);
-	f.write(_sentenceId);
-	f.write(_animationMode);
-	f.write(_animationModePrevious);
-	f.write(_isPause);
-	f.write(_delay);
+	f.padBytes((kMaxEntries - count) * 24);
+
+	f.writeBool(_isNotPause);
+	f.writeInt(_actorId);
+	f.writeInt(_sentenceId);
+	f.writeInt(_animationMode);
+	f.writeInt(_animationModePrevious);
+	f.writeBool(_isPause);
+	f.writeInt(_delay);
 	// f.write(_timeLast);
 }
 
+void ActorDialogueQueue::load(SaveFileReadStream &f) {
+	_entries.clear();
+	int count = f.readInt();
+	assert(count <= kMaxEntries);
+	_entries.resize(count);
+	for (int i = 0; i < count; ++i) {
+		Entry &e = _entries[i];
+		e.isNotPause = f.readBool();
+		e.isPause = f.readBool();
+		e.actorId = f.readInt();
+		e.sentenceId = f.readInt();
+		e.animationMode = f.readInt();
+		e.delay = f.readInt();
+	}
+
+	f.skip((kMaxEntries - count) * 24);
+
+	_isNotPause = f.readBool();
+	_actorId = f.readInt();
+	_sentenceId = f.readInt();
+	_animationMode = f.readInt();
+	_animationModePrevious = f.readInt();
+	_isPause = f.readBool();
+	_delay = f.readInt();
+
+	_timeLast = 0;
+}
+
 void ActorDialogueQueue::clear() {
 	_entries.clear();
 	_isNotPause = false;
diff --git a/engines/bladerunner/actor_dialogue_queue.h b/engines/bladerunner/actor_dialogue_queue.h
index e26b6cf..832bcc9 100644
--- a/engines/bladerunner/actor_dialogue_queue.h
+++ b/engines/bladerunner/actor_dialogue_queue.h
@@ -28,9 +28,12 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
-class SaveFile;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 
 class ActorDialogueQueue {
+	static const int kMaxEntries = 25;
+
 	struct Entry {
 		bool isNotPause;
 		bool isPause;
@@ -63,7 +66,8 @@ public:
 	void flush(int a1, bool callScript);
 	void tick();
 
-	void save(SaveFile &f);
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
 
 private:
 	void clear();
diff --git a/engines/bladerunner/actor_walk.cpp b/engines/bladerunner/actor_walk.cpp
index d376e36..b458733 100644
--- a/engines/bladerunner/actor_walk.cpp
+++ b/engines/bladerunner/actor_walk.cpp
@@ -239,25 +239,51 @@ void ActorWalk::run(int actorId) {
 	_vm->_actors[actorId]->changeAnimationMode(animationMode, false);
 }
 
-void ActorWalk::save(SaveFile &f) {
-	f.write(_walking);
-	f.write(_running);
-	f.write(_destination);
+void ActorWalk::save(SaveFileWriteStream &f) {
+	f.writeInt(_walking);
+	f.writeInt(_running);
+	f.writeVector3(_destination);
 	// _originalDestination is not saved
-	f.write(_current);
-	f.write(_next);
-	f.write(_facing);
+	f.writeVector3(_current);
+	f.writeVector3(_next);
+	f.writeInt(_facing);
 
 	assert(_nearActors.size() <= 20);
 	for (Common::HashMap<int, bool>::const_iterator it = _nearActors.begin(); it != _nearActors.end(); ++it) {
-		f.write(it->_key);
-		f.write(it->_value);
+		f.writeInt(it->_key);
+		f.writeBool(it->_value);
 	}
 	f.padBytes(8 * (20 - _nearActors.size()));
-	f.write((int)_nearActors.size());
+	f.writeInt(_nearActors.size());
 
-	f.write(0); // _notUsed
-	f.write(_status);
+	f.writeInt(0); // _notUsed
+	f.writeInt(_status);
+}
+
+void ActorWalk::load(SaveFileReadStream &f) {
+	_walking = f.readInt();
+	_running = f.readInt();
+	_destination = f.readVector3();
+	// _originalDestination is not saved
+	_current = f.readVector3();
+	_next = f.readVector3();
+	_facing = f.readInt();
+
+	int actorId[20];
+	bool isNear[20];
+
+	for (int i = 0; i < 20; ++i) {
+		actorId[i] = f.readInt();
+		isNear[i] = f.readBool();
+	}
+
+	int count = f.readInt();
+	for (int i = 0; i < count; ++i) {
+		_nearActors.setVal(actorId[i], isNear[i]);
+	}
+
+	f.skip(4); // _notUsed
+	_status = f.readInt();
 }
 
 bool ActorWalk::isXYZEmpty(float x, float y, float z, int actorId) const {
diff --git a/engines/bladerunner/actor_walk.h b/engines/bladerunner/actor_walk.h
index 2654214..45298e2 100644
--- a/engines/bladerunner/actor_walk.h
+++ b/engines/bladerunner/actor_walk.h
@@ -29,7 +29,8 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
-class SaveFile;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 
 class ActorWalk {
 	BladeRunnerEngine *_vm;
@@ -61,7 +62,9 @@ public:
 	void stop(int actorId, bool immediately, int combatAnimationMode, int animationMode);
 	void run(int actorId);
 
-	void save(SaveFile &f);
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
+
 private:
 	int nextOnPath(int actorId, const Vector3 &from, const Vector3 &to, Vector3 &next) const;
 
diff --git a/engines/bladerunner/ambient_sounds.cpp b/engines/bladerunner/ambient_sounds.cpp
index 6477f27..81b6174 100644
--- a/engines/bladerunner/ambient_sounds.cpp
+++ b/engines/bladerunner/ambient_sounds.cpp
@@ -69,25 +69,23 @@ void AmbientSounds::addSound(
 	int panStartMin, int panStartMax,
 	int panEndMin, int panEndMax,
 	int priority, int unk) {
-	const char *name = _vm->_gameInfo->getSfxTrack(sfxId);
 
 	sort(volumeMin, volumeMax);
 	sort(panStartMin, panStartMax);
 	sort(panEndMin, panEndMax);
 
 	addSoundByName(
-				 name,
-				 timeMin, timeMax,
-				 volumeMin, volumeMax,
-				 panStartMin, panStartMax,
-				 panEndMin, panEndMax,
-				 priority, unk
+				_vm->_gameInfo->getSfxTrack(sfxId),
+				timeMin, timeMax,
+				volumeMin, volumeMax,
+				panStartMin, panStartMax,
+				panEndMin, panEndMax,
+				priority, unk
 				);
 }
 
 void AmbientSounds::removeNonLoopingSound(int sfxId, bool stopPlaying) {
-	const char *name = _vm->_gameInfo->getSfxTrack(sfxId);
-	int32 hash = mix_id(name);
+	int32 hash = MIXArchive::getHash(_vm->_gameInfo->getSfxTrack(sfxId));
 	int index = findNonLoopingTrackByHash(hash);
 	if (index >= 0) {
 		removeNonLoopingSoundByIndex(index, stopPlaying);
@@ -105,8 +103,7 @@ void AmbientSounds::addSpeech(int actorId, int sentenceId, int timeMin, int time
 	sort(panStartMin, panStartMax);
 	sort(panEndMin, panEndMax);
 
-	char name[13];
-	sprintf(name, "%02d-%04d%s.AUD", actorId, sentenceId, _vm->_languageCode);
+	Common::String name = Common::String::format( "%02d-%04d%s.AUD", actorId, sentenceId, _vm->_languageCode.c_str());
 	addSoundByName(name,
 					timeMin, timeMax,
 					volumeMin, volumeMax,
@@ -116,15 +113,12 @@ void AmbientSounds::addSpeech(int actorId, int sentenceId, int timeMin, int time
 }
 
 void AmbientSounds::playSound(int sfxId, int volume, int panStart, int panEnd, int priority) {
-	const char *name = _vm->_gameInfo->getSfxTrack(sfxId);
-
-	_vm->_audioPlayer->playAud(name, volume * _ambientVolume / 100, panStart, panEnd, priority, kAudioPlayerOverrideVolume);
+	_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(sfxId), volume * _ambientVolume / 100, panStart, panEnd, priority, kAudioPlayerOverrideVolume);
 }
 
 void AmbientSounds::addLoopingSound(int sfxId, int volume, int pan, int delay) {
-	const char *name = _vm->_gameInfo->getSfxTrack(sfxId);
-
-	int32 hash = mix_id(name);
+	const Common::String &name = _vm->_gameInfo->getSfxTrack(sfxId);
+	int32 hash = MIXArchive::getHash(name);
 
 	if (findLoopingTrackByHash(hash) >= 0) {
 		return;
@@ -137,8 +131,7 @@ void AmbientSounds::addLoopingSound(int sfxId, int volume, int pan, int delay) {
 	LoopingSound &track = _loopingSounds[i];
 
 	track.isActive = true;
-	strncpy(track.name, name, sizeof(track.name));
-	track.name[sizeof(track.name) - 1] = 0;
+	track.name = name;
 	track.hash = hash;
 	track.pan = pan;
 	track.volume = volume;
@@ -162,8 +155,7 @@ void AmbientSounds::addLoopingSound(int sfxId, int volume, int pan, int delay) {
 }
 
 void AmbientSounds::adjustLoopingSound(int sfxId, int volume, int pan, int delay) {
-	const char *name = _vm->_gameInfo->getSfxTrack(sfxId);
-	int32 hash = mix_id(name);
+	int32 hash = MIXArchive::getHash(_vm->_gameInfo->getSfxTrack(sfxId));
 	int index = findLoopingTrackByHash(hash);
 
 	if (index >= 0 && _loopingSounds[index].audioPlayerTrack != -1 && _vm->_audioPlayer->isActive(_loopingSounds[index].audioPlayerTrack)) {
@@ -179,8 +171,7 @@ void AmbientSounds::adjustLoopingSound(int sfxId, int volume, int pan, int delay
 }
 
 void AmbientSounds::removeLoopingSound(int sfxId, int delay) {
-	const char *name = _vm->_gameInfo->getSfxTrack(sfxId);
-	int32 hash = mix_id(name);
+	int32 hash = MIXArchive::getHash(_vm->_gameInfo->getSfxTrack(sfxId));
 	int index = findLoopingTrackByHash(hash);
 	if (index >= 0) {
 		removeLoopingSoundByIndex(index, delay);
@@ -298,15 +289,12 @@ int AmbientSounds::findLoopingTrackByHash(int32 hash) const {
 }
 
 void AmbientSounds::addSoundByName(
-	const char *name,
+	const Common::String &name,
 	int timeMin, int timeMax,
 	int volumeMin, int volumeMax,
 	int panStartMin, int panStartMax,
 	int panEndMin, int panEndMax,
 	int priority, int unk) {
-	if (strlen(name) > 12) {
-		error("AmbientSounds::addSoundByName: Overlong name '%s'", name);
-	}
 
 	int i = findAvailableNonLoopingTrack();
 	if (i < 0) {
@@ -318,9 +306,8 @@ void AmbientSounds::addSoundByName(
 	uint32 now = _vm->getTotalPlayTime();
 
 	track.isActive = true;
-	strncpy(track.name, name, sizeof(track.name));
-	track.name[sizeof(track.name) - 1] = 0;
-	track.hash = mix_id(name);
+	track.name = name;
+	track.hash = MIXArchive::getHash(name);
 	track.timeMin = 1000 * timeMin;
 	track.timeMax = 1000 * timeMax;
 	track.nextPlayTime = now + _vm->_rnd.getRandomNumberRng(track.timeMin, track.timeMax);
@@ -356,46 +343,82 @@ void AmbientSounds::removeLoopingSoundByIndex(int index, int delay) {
 		}
 	}
 	track.isActive = false;
-	track.name[0] = 0;
+	track.name.clear();
 	track.hash = 0;
 	track.audioPlayerTrack = -1;
 	track.volume = 0;
 	track.pan = 0;
 }
 
-void AmbientSounds::save(SaveFile &f) {
-	f.write(false); // TODO: _isDisabled
+void AmbientSounds::save(SaveFileWriteStream &f) {
+	f.writeBool(false); // TODO: _isDisabled
 
 	for (int i = 0; i != kNonLoopingSounds; ++i) {
 		// 73 bytes per non-looping sound
 		NonLoopingSound &s = _nonLoopingSounds[i];
-		f.write(s.isActive);
-		f.write(s.name, 13);
-		f.write(s.hash);
-		f.write(s.audioPlayerTrack);
-		f.write(s.timeMin);
-		f.write(s.timeMax);
-		f.write(s.nextPlayTime);
-		f.write(s.volumeMin);
-		f.write(s.volumeMax);
-		f.write(s.volume);
-		f.write(s.panStartMin);
-		f.write(s.panStartMax);
-		f.write(s.panEndMin);
-		f.write(s.panEndMax);
-		f.write(s.priority);
+		f.writeBool(s.isActive);
+		f.writeStringSz(s.name, 13);
+		f.writeSint32LE(s.hash);
+		f.writeInt(s.audioPlayerTrack);
+		f.writeInt(s.timeMin);
+		f.writeInt(s.timeMax);
+		f.writeUint32LE(s.nextPlayTime);
+		f.writeInt(s.volumeMin);
+		f.writeInt(s.volumeMax);
+		f.writeInt(s.volume);
+		f.writeInt(s.panStartMin);
+		f.writeInt(s.panStartMax);
+		f.writeInt(s.panEndMin);
+		f.writeInt(s.panEndMax);
+		f.writeInt(s.priority);
 		f.padBytes(4); // field_45
 	}
 
 	for (int i = 0; i != kLoopingSounds; ++i) {
 		// 33 bytes per looping sound
 		LoopingSound &s = _loopingSounds[i];
-		f.write(s.isActive);
-		f.write(s.name, 13);
-		f.write(s.hash);
-		f.write(s.audioPlayerTrack);
-		f.write(s.volume);
-		f.write(s.pan);
+		f.writeBool(s.isActive);
+		f.writeStringSz(s.name, 13);
+		f.writeSint32LE(s.hash);
+		f.writeInt(s.audioPlayerTrack);
+		f.writeInt(s.volume);
+		f.writeInt(s.pan);
+	}
+}
+
+void AmbientSounds::load(SaveFileReadStream &f) {
+	f.skip(4); // TODO: _isDisabled
+
+	for (int i = 0; i != kNonLoopingSounds; ++i) {
+		// 73 bytes per non-looping sound
+		NonLoopingSound &s = _nonLoopingSounds[i];
+		s.isActive = f.readBool();
+		s.name = f.readStringSz(13);
+		s.hash = f.readSint32LE();
+		s.audioPlayerTrack = f.readInt();
+		s.timeMin = f.readInt();
+		s.timeMax = f.readInt();
+		s.nextPlayTime = f.readUint32LE();
+		s.volumeMin = f.readInt();
+		s.volumeMax = f.readInt();
+		s.volume = f.readInt();
+		s.panStartMin = f.readInt();
+		s.panStartMax = f.readInt();
+		s.panEndMin = f.readInt();
+		s.panEndMax = f.readInt();
+		s.priority = f.readInt();
+		f.skip(4); // field_45
+	}
+
+	for (int i = 0; i != kLoopingSounds; ++i) {
+		// 33 bytes per looping sound
+		LoopingSound &s = _loopingSounds[i];
+		s.isActive = f.readBool();
+		s.name = f.readStringSz(13);
+		s.hash = f.readSint32LE();
+		s.audioPlayerTrack = f.readInt();
+		s.volume = f.readInt();
+		s.pan = f.readInt();
 	}
 }
 
diff --git a/engines/bladerunner/ambient_sounds.h b/engines/bladerunner/ambient_sounds.h
index 6e14c56..de08965 100644
--- a/engines/bladerunner/ambient_sounds.h
+++ b/engines/bladerunner/ambient_sounds.h
@@ -25,40 +25,43 @@
 
 #include "audio/audiostream.h"
 
+#include "common/str.h"
+
 namespace BladeRunner {
 
 class BladeRunnerEngine;
-class SaveFile;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 
 class AmbientSounds {
 	static const int kNonLoopingSounds = 25;
 	static const int kLoopingSounds = 3;
 
 	struct NonLoopingSound {
-		bool   isActive;
-		char   name[13];
-		int32  hash;
-		int32  audioPlayerTrack;
-		int32  timeMin;
-		int32  timeMax;
-		uint32 nextPlayTime;
-		int32  volumeMin;
-		int32  volumeMax;
-		int32  volume;
-		int32  panStartMin;
-		int32  panStartMax;
-		int32  panEndMin;
-		int32  panEndMax;
-		int32  priority;
+		bool           isActive;
+		Common::String name;
+		int32          hash;
+		int            audioPlayerTrack;
+		int            timeMin;
+		int            timeMax;
+		uint32         nextPlayTime;
+		int            volumeMin;
+		int            volumeMax;
+		int            volume;
+		int            panStartMin;
+		int            panStartMax;
+		int            panEndMin;
+		int            panEndMax;
+		int            priority;
 	};
 
 	struct LoopingSound {
-		bool  isActive;
-		char  name[13];
-		int32 hash;
-		int   audioPlayerTrack;
-		int32 volume;
-		int   pan;
+		bool           isActive;
+		Common::String name;
+		int32          hash;
+		int            audioPlayerTrack;
+		int            volume;
+		int            pan;
 	};
 
 	BladeRunnerEngine *_vm;
@@ -104,7 +107,8 @@ public:
 	int getVolume() const;
 	void playSample();
 
-	void save(SaveFile &f);
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
 
 private:
 	int findAvailableNonLoopingTrack() const;
@@ -122,7 +126,7 @@ private:
 	// playVolumeAdjustSound
 
 	void addSoundByName(
-		const char *name,
+		const Common::String &name,
 		int timeMin, int timeMax,
 		int volumeMin, int volumeMax,
 		int panStartMin, int panStartMax,
diff --git a/engines/bladerunner/archive.cpp b/engines/bladerunner/archive.cpp
index 8088e1a..c116eeb 100644
--- a/engines/bladerunner/archive.cpp
+++ b/engines/bladerunner/archive.cpp
@@ -51,7 +51,7 @@ bool MIXArchive::open(const Common::String &filename) {
 
 	_entries.resize(_entryCount);
 	for (uint16 i = 0; i != _entryCount; ++i) {
-		_entries[i].id     = _fd.readSint32LE();
+		_entries[i].hash   = _fd.readUint32LE();
 		_entries[i].offset = _fd.readUint32LE();
 		_entries[i].length = _fd.readUint32LE();
 
@@ -61,7 +61,7 @@ bool MIXArchive::open(const Common::String &filename) {
 
 		// Verify that the entries are sorted by id. Note that id is signed.
 		if (i > 0) {
-			assert(_entries[i].id > _entries[i - 1].id);
+			assert(_entries[i].hash > _entries[i - 1].hash);
 		}
 	}
 
@@ -86,7 +86,7 @@ bool MIXArchive::isOpen() const {
 
 #define ROL(n) ((n << 1) | ((n >> 31) & 1))
 
-int32 mix_id(const Common::String &name) {
+int32 MIXArchive::getHash(const Common::String &name) {
 	char buffer[12] = { 0 };
 
 	for (uint i = 0; i != name.size() && i < 12u; ++i) {
@@ -103,11 +103,10 @@ int32 mix_id(const Common::String &name) {
 		id = ROL(id) + t;
 	}
 
-	return reinterpret_cast<int32&>(id);
+	return id;
 }
 
-static
-int32 tlk_id(const Common::String &name) {
+static uint32 tlk_id(const Common::String &name) {
 	char buffer[12] = { 0 };
 
 	for (uint i = 0; i != name.size() && i < 12u; ++i)
@@ -124,15 +123,15 @@ int32 tlk_id(const Common::String &name) {
 	return 10000 * actor_id + speech_id;
 }
 
-uint32 MIXArchive::indexForId(int32 id) const {
+uint32 MIXArchive::indexForHash(int32 hash) const {
 	uint32 lo = 0, hi = _entryCount;
 
 	while (lo < hi) {
 		uint32 mid = lo + (hi - lo) / 2;
 
-		if (id > _entries[mid].id) {
+		if (hash > _entries[mid].hash) {
 			lo = mid + 1;
-		} else if (id < _entries[mid].id) {
+		} else if (hash < _entries[mid].hash) {
 			hi = mid;
 		} else {
 			return mid;
@@ -142,14 +141,15 @@ uint32 MIXArchive::indexForId(int32 id) const {
 }
 
 Common::SeekableReadStream *MIXArchive::createReadStreamForMember(const Common::String &name) {
-	int32 id;
+	int32 hash;
 
-	if (_isTLK)
-		id = tlk_id(name);
-	else
-		id = mix_id(name);
+	if (_isTLK) {
+		hash = tlk_id(name);
+	} else {
+		hash = MIXArchive::getHash(name);
+	}
 
-	uint32 i = indexForId(id);
+	uint32 i = indexForHash(hash);
 
 	if (i == _entryCount) {
 		return nullptr;
diff --git a/engines/bladerunner/archive.h b/engines/bladerunner/archive.h
index 7bec41f..9f7c679 100644
--- a/engines/bladerunner/archive.h
+++ b/engines/bladerunner/archive.h
@@ -34,6 +34,8 @@ public:
 	MIXArchive();
 	~MIXArchive();
 
+	static int32 getHash(const Common::String &name);
+
 	bool open(const Common::String &filename);
 	void close();
 	bool isOpen() const;
@@ -50,17 +52,16 @@ private:
 	uint32 _size;
 
 	struct ArchiveEntry {
-		int32  id;
+		int32  hash;
 		uint32 offset;
 		uint32 length;
 	};
 
 	Common::Array<ArchiveEntry> _entries;
 
-	uint32 indexForId(int32 id) const;
+	uint32 indexForHash(int32 hash) const;
 };
 
-int32 mix_id(const Common::String &name);
 
 } // End of namespace BladeRunner
 
diff --git a/engines/bladerunner/audio_player.cpp b/engines/bladerunner/audio_player.cpp
index e1ef026..5186888 100644
--- a/engines/bladerunner/audio_player.cpp
+++ b/engines/bladerunner/audio_player.cpp
@@ -253,7 +253,7 @@ int AudioPlayer::playAud(const Common::String &name, int volume, int panFrom, in
 	}
 
 	/* Load audio resource and store in cache. Playback will happen directly from there. */
-	int32 hash = mix_id(name);
+	int32 hash = MIXArchive::getHash(name);
 	if (!_cache->findByHash(hash)) {
 		Common::SeekableReadStream *r = _vm->getResourceStream(name);
 		if (!r) {
diff --git a/engines/bladerunner/audio_speech.cpp b/engines/bladerunner/audio_speech.cpp
index 69883cb..87f7373 100644
--- a/engines/bladerunner/audio_speech.cpp
+++ b/engines/bladerunner/audio_speech.cpp
@@ -58,12 +58,12 @@ AudioSpeech::~AudioSpeech() {
 	delete[] _data;
 }
 
-bool AudioSpeech::playSpeech(const char *name, int pan) {
+bool AudioSpeech::playSpeech(const Common::String &name, int pan) {
 	// debug("AudioSpeech::playSpeech(\"%s\")", name);
 	Common::ScopedPtr<Common::SeekableReadStream> r(_vm->getResourceStream(name));
 
 	if (!r) {
-		warning("AudioSpeech::playSpeech: AUD resource \"%s\" not found", name);
+		warning("AudioSpeech::playSpeech: AUD resource \"%s\" not found", name.c_str());
 		return false;
 	}
 
@@ -78,7 +78,7 @@ bool AudioSpeech::playSpeech(const char *name, int pan) {
 
 	r->read(_data, r->size());
 	if (r->err()) {
-		warning("AudioSpeech::playSpeech: Error reading resource \"%s\"", name);
+		warning("AudioSpeech::playSpeech: Error reading resource \"%s\"", name.c_str());
 		return false;
 	}
 
@@ -117,7 +117,7 @@ bool AudioSpeech::isPlaying() const {
 
 bool AudioSpeech::playSpeechLine(int actorId, int sentenceId, int volume, int a4, int priority) {
 	int balance = _vm->_actors[actorId]->soundBalance();
-	Common::String name = Common::String::format("%02d-%04d%s.AUD", actorId, sentenceId,  _vm->_languageCode);
+	Common::String name = Common::String::format("%02d-%04d%s.AUD", actorId, sentenceId, _vm->_languageCode.c_str());
 	return _vm->_audioPlayer->playAud(name, _speechVolume * volume / 100, balance, balance, priority, kAudioPlayerOverrideVolume);
 }
 
diff --git a/engines/bladerunner/audio_speech.h b/engines/bladerunner/audio_speech.h
index 010499f..5406f1d 100644
--- a/engines/bladerunner/audio_speech.h
+++ b/engines/bladerunner/audio_speech.h
@@ -23,6 +23,7 @@
 #ifndef BLADERUNNER_AUDIO_SPEECH_H
 #define BLADERUNNER_AUDIO_SPEECH_H
 
+#include "common/str.h"
 #include "common/types.h"
 
 namespace BladeRunner {
@@ -44,7 +45,7 @@ public:
 	AudioSpeech(BladeRunnerEngine *vm);
 	~AudioSpeech();
 
-	bool playSpeech(const char *name, int balance = 0);
+	bool playSpeech(const Common::String &name, int balance = 0);
 	void stopSpeech();
 	bool isPlaying() const;
 
diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp
index 805461c..f481acc 100644
--- a/engines/bladerunner/bladerunner.cpp
+++ b/engines/bladerunner/bladerunner.cpp
@@ -45,7 +45,6 @@
 #include "bladerunner/outtake.h"
 #include "bladerunner/obstacles.h"
 #include "bladerunner/overlays.h"
-#include "bladerunner/police_maze.h"
 #include "bladerunner/regions.h"
 #include "bladerunner/savefile.h"
 #include "bladerunner/scene.h"
@@ -356,8 +355,6 @@ bool BladeRunnerEngine::startup(bool hasSavegames) {
 	// TODO: Set actor ids (redundant?)
 
 	_policeMaze = new PoliceMaze(this);
-	if (!_policeMaze->init())
-		return false;
 
 	_textActorNames = new TextResource(this);
 	if (!_textActorNames->open("ACTORS"))
@@ -827,8 +824,7 @@ void BladeRunnerEngine::gameTick() {
 			// TODO: Process AUD
 
 			if (_walkSoundId >= 0) {
-				const char *name = _gameInfo->getSfxTrack(_walkSoundId);
-				_audioPlayer->playAud(name, _walkSoundVolume, _walkSoundBalance, _walkSoundBalance, 50, 0);
+				_audioPlayer->playAud(_gameInfo->getSfxTrack(_walkSoundId), _walkSoundVolume, _walkSoundBalance, _walkSoundBalance, 50, 0);
 				_walkSoundId = -1;
 			}
 
@@ -1196,8 +1192,8 @@ void BladeRunnerEngine::handleMouseClickRegion(int regionId, int x, int y, bool
 }
 
 void BladeRunnerEngine::handleMouseClick3DObject(int objectId, bool buttonDown, bool isClickable, bool isTarget) {
-	const char *objectName = _scene->objectGetName(objectId);
-	debug("Clicked on object %s", objectName);
+	const Common::String &objectName = _scene->objectGetName(objectId);
+	debug("Clicked on object %s", objectName.c_str());
 
 	if (_isWalkingInterruptible && objectId != _walkingToObjectId) {
 		_isWalkingInterruptible = false;
@@ -1230,7 +1226,7 @@ void BladeRunnerEngine::handleMouseClick3DObject(int objectId, bool buttonDown,
 			_walkingToActorId  = -1;
 
 			_isInsideScriptObject = true;
-			_sceneScript->clickedOn3DObject(objectName, false);
+			_sceneScript->clickedOn3DObject(objectName.c_str(), false);
 			_isInsideScriptObject = false;
 		}
 	} else {
@@ -1246,7 +1242,7 @@ void BladeRunnerEngine::handleMouseClick3DObject(int objectId, bool buttonDown,
 		//TODO mouse::randomize(Mouse);
 
 		_isInsideScriptObject = true;
-		_sceneScript->clickedOn3DObject(objectName, true);
+		_sceneScript->clickedOn3DObject(objectName.c_str(), true);
 		_isInsideScriptObject = false;
 	}
 }
@@ -1597,53 +1593,29 @@ bool BladeRunnerEngine::saveGame(const Common::String &filename, byte *thumbnail
 		return false;
 	}
 
-	SaveFile s(commonSaveFile);
+	SaveFileWriteStream s;
 
 	s.padBytes(9600); // TODO: thumbnail
-	s.write(-1.0f);
-
+	s.writeFloat(-1.0f);
 	_settings->save(s);
-	// s.debug(" - SCENE - ");
 	_scene->save(s);
-	// s.debug(" - EXIST - ");
 	_scene->_exits->save(s);
-	// s.debug(" - REGIONS - ");
 	_scene->_regions->save(s);
-	// s.debug(" - SET - ");
 	_scene->_set->save(s);
-
-	// s.debug(" - GAMEVARS - ");
 	for (uint i = 0; i != _gameInfo->getGlobalVarCount(); ++i) {
-		s.write(_gameVars[i]);
+		s.writeInt(_gameVars[i]);
 	}
-
-	// TODO
-	// _music->save(s);
-	// s.debug(" - MUSIC - ");
-	s.padBytes(0x56);
-
+	_music->save(s);
 	// _audioPlayer->save(s) // zero func
 	// _audioSpeech->save(s) // zero func
-
-	// s.debug(" - COMBAT - ");
 	_combat->save(s);
-	// s.debug(" - GAMEFLAGS - ");
 	_gameFlags->save(s);
-	// s.debug(" - ITEMS - ");
 	_items->save(s);
-	// s.debug(" - SCENEOBJECTS - ");
 	_sceneObjects->save(s);
-	// s.debug(" - AMBIENTSOUNDS - ");
 	_ambientSounds->save(s);
-	// s.debug(" - OVERLAYS - ");
 	_overlays->save(s);
-	// s.debug(" - SPINNER - ");
 	_spinner->save(s);
-
-	// TODO
-	// _scores->save(s);
-	s.padBytes(0x28);
-
+	s.padBytes(0x28); // TODO: _scores->save(s);
 	_dialogueMenu->save(s);
 	_obstacles->save(s);
 	_actorDialogueQueue->save(s);
@@ -1652,12 +1624,12 @@ bool BladeRunnerEngine::saveGame(const Common::String &filename, byte *thumbnail
 	for (uint i = 0; i != _gameInfo->getActorCount(); ++i) {
 		_actors[i]->save(s);
 
-		int animationState, animationFrame, a3, a4;
-		_aiScripts->queryAnimationState(i, &animationState, &animationFrame, &a3, &a4);
-		s.write(animationState);
-		s.write(animationFrame);
-		s.write(a3);
-		s.write(a4);
+		int animationState, animationFrame, animationStateNext, nextAnimation;
+		_aiScripts->queryAnimationState(i, &animationState, &animationFrame, &animationStateNext, &nextAnimation);
+		s.writeInt(animationState);
+		s.writeInt(animationFrame);
+		s.writeInt(animationStateNext);
+		s.writeInt(nextAnimation);
 	}
 	_actors[kActorVoiceOver]->save(s);
 
@@ -1667,11 +1639,86 @@ bool BladeRunnerEngine::saveGame(const Common::String &filename, byte *thumbnail
 	s.finalize();
 	assert(0 && "ok");
 
+	commonSaveFile->writeUint32LE(s.size() + 4);
+	commonSaveFile->write(s.getData(), s.size());
+
 	return !commonSaveFile->err();
 }
 
-void BladeRunnerEngine::ISez(const char *str) {
-	debug("\t%s", str);
+void BladeRunnerEngine::loadGame(const Common::String &filename, byte *thumbnail) {
+	warning("BladeRunnerEngine::loadGame not finished");
+
+	if (!playerHasControl() || _sceneScript->isInsideScript() || _aiScripts->isInsideScript()) {
+		return;
+	}
+
+	Common::InSaveFile *commonSaveFile = getSaveFileManager()->openForLoading(filename);
+	if (commonSaveFile->err()) {
+		return;
+	}
+
+	void *buf = malloc(commonSaveFile->size());
+	int dataSize = commonSaveFile->read(buf, commonSaveFile->size());
+
+	SaveFileReadStream s((const byte*)buf, dataSize);
+
+	_ambientSounds->removeAllNonLoopingSounds(true);
+	_ambientSounds->removeAllLoopingSounds(1);
+	_music->stop(2);
+	_audioSpeech->stopSpeech();
+	_actorDialogueQueue->flush(true, false);
+
+	int size = s.readInt();
+
+	if (size != dataSize) {
+		return;
+	}
+
+	s.skip(9600); // thumbnail
+	_settings->load(s);
+	_scene->load(s);
+	_scene->_exits->load(s);
+	_scene->_regions->load(s);
+	_scene->_set->load(s);
+	for (uint i = 0; i != _gameInfo->getGlobalVarCount(); ++i) {
+		_gameVars[i] = s.readInt();
+	}
+	_music->load(s);
+	// _audioPlayer->load(s) // zero func
+	// _audioSpeech->load(s) // zero func
+	_combat->load(s);
+	_gameFlags->load(s);
+	_items->load(s);
+	_sceneObjects->load(s);
+	_ambientSounds->load(s);
+	_overlays->load(s);
+	_spinner->load(s);
+	s.skip(0x28); // TODO: _scores->load(s);
+	_dialogueMenu->load(s);
+	_obstacles->load(s);
+	_actorDialogueQueue->load(s);
+	_waypoints->load(s);
+
+	for (uint i = 0; i != _gameInfo->getActorCount(); ++i) {
+		_actors[i]->load(s);
+
+		int animationState = s.readInt();
+		int animationFrame = s.readInt();
+		int animationStateNext = s.readInt();
+		int nextAnimation = s.readInt();
+		_aiScripts->setAnimationState(i, animationState, animationFrame, animationStateNext, nextAnimation);
+	}
+	_actors[kActorVoiceOver]->load(s);
+
+	_policeMaze->load(s);
+	_crimesDatabase->load(s);
+
+	_settings->setNewSetAndScene(_settings->getSet(), _settings->getScene());
+	_settings->setChapter(_settings->getChapter());
+}
+
+void BladeRunnerEngine::ISez(const Common::String &str) {
+	debug("\t%s", str.c_str());
 }
 
 void BladeRunnerEngine::blitToScreen(const Graphics::Surface &src) {
diff --git a/engines/bladerunner/bladerunner.h b/engines/bladerunner/bladerunner.h
index 7561db2..32747cc 100644
--- a/engines/bladerunner/bladerunner.h
+++ b/engines/bladerunner/bladerunner.h
@@ -104,10 +104,10 @@ public:
 	static const int kActorCount = 100;
 	static const int kActorVoiceOver = kActorCount - 1;
 
-	bool       _gameIsRunning;
-	bool       _windowIsActive;
-	int        _playerLosesControlCounter;
-	const char *_languageCode;
+	bool           _gameIsRunning;
+	bool           _windowIsActive;
+	int            _playerLosesControlCounter;
+	Common::String _languageCode;
 
 	ActorDialogueQueue *_actorDialogueQueue;
 	ScreenEffects      *_screenEffects;
@@ -261,11 +261,11 @@ public:
 	void playerGainsControl();
 
 	bool saveGame(const Common::String &filename, byte *thumbnail);
-	void loadGame();
+	void loadGame(const Common::String &filename, byte *thumbnail);
 	void newGame();
 	void autoSaveGame();
 
-	void ISez(const char *str);
+	void ISez(const Common::String &str);
 
 	void blitToScreen(const Graphics::Surface &src);
 
diff --git a/engines/bladerunner/boundingbox.cpp b/engines/bladerunner/boundingbox.cpp
index aea2dc0..40b7d42 100644
--- a/engines/bladerunner/boundingbox.cpp
+++ b/engines/bladerunner/boundingbox.cpp
@@ -84,16 +84,4 @@ float BoundingBox::getZ1() const {
 	return _vertices[1].z;
 }
 
-void BoundingBox::save(SaveFile &f) {
-	f.write(_vertices[0].x);
-	f.write(_vertices[0].y);
-	f.write(_vertices[0].z);
-	f.write(_vertices[1].x);
-	f.write(_vertices[1].y);
-	f.write(_vertices[1].z);
-
-	// Bounding boxes have a lot of extra data that's never actually used
-	f.padBytes(8*8*4);
-}
-
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/boundingbox.h b/engines/bladerunner/boundingbox.h
index 0397a86..0206f0b 100644
--- a/engines/bladerunner/boundingbox.h
+++ b/engines/bladerunner/boundingbox.h
@@ -27,7 +27,7 @@
 
 namespace BladeRunner {
 
-class SaveFile;
+class SaveFileWriteStream;
 
 class BoundingBox {
 	Vector3 _vertices[2];
@@ -45,8 +45,6 @@ public:
 
 	float getZ0() const;
 	float getZ1() const;
-
-	void save(SaveFile &f);
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/combat.cpp b/engines/bladerunner/combat.cpp
index eefcebc..e1dc6b1 100644
--- a/engines/bladerunner/combat.cpp
+++ b/engines/bladerunner/combat.cpp
@@ -127,8 +127,8 @@ void Combat::shoot(int actorId, Vector3 &to, int screenX) {
 	distanceFromCenter = 100 *  -------------------------------
 	                                 abs(right - left) / 2
 	*/
-	Common::Rect *rect = actor->getScreenRectangle();
-	int distanceFromCenter = CLIP(100 * (screenX - abs((rect->right + rect->left) / 2)) / abs((rect->right - rect->left) / 2), 0, 100);
+	const Common::Rect &rect = actor->getScreenRectangle();
+	int distanceFromCenter = CLIP(100 * (screenX - abs((rect.right + rect.left) / 2)) / abs((rect.right - rect.left) / 2), 0, 100);
 
 	int damage = (100 - distanceFromCenter) * _ammoDamage[_vm->_settings->getAmmoType()] / 100;
 
@@ -205,14 +205,25 @@ int Combat::findCoverWaypoint(int waypointType, int actorId, int enemyId) const
 	return result;
 }
 
-void Combat::save(SaveFile &f) {
-	f.write(_active);
-	f.write(_enabled);
-	for (int i = 0; i != 9; ++i) {
-		f.write(_hitSoundId[i]);
+void Combat::save(SaveFileWriteStream &f) {
+	f.writeBool(_active);
+	f.writeBool(_enabled);
+	for (int i = 0; i != kSoundCount; ++i) {
+		f.writeInt(_hitSoundId[i]);
 	}
-	for (int i = 0; i != 9; ++i) {
-		f.write(_missSoundId[i]);
+	for (int i = 0; i != kSoundCount; ++i) {
+		f.writeInt(_missSoundId[i]);
+	}
+}
+
+void Combat::load(SaveFileReadStream &f) {
+	_active = f.readBool();
+	_enabled = f.readBool();
+	for (int i = 0; i != kSoundCount; ++i) {
+		_hitSoundId[i] = f.readInt();
+	}
+	for (int i = 0; i != kSoundCount; ++i) {
+		_missSoundId[i] = f.readInt();
 	}
 }
 
diff --git a/engines/bladerunner/combat.h b/engines/bladerunner/combat.h
index 670f580..e0038d6 100644
--- a/engines/bladerunner/combat.h
+++ b/engines/bladerunner/combat.h
@@ -30,7 +30,8 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
-class SaveFile;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 class Vector3;
 
 class Combat {
@@ -90,7 +91,8 @@ public:
 	int findFleeWaypoint(int setId, int enemyId, const Vector3& position) const;
 	int findCoverWaypoint(int waypointType, int actorId, int enemyId) const;
 
-	void save(SaveFile &f);
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/crimes_database.cpp b/engines/bladerunner/crimes_database.cpp
index 131c871..e9faa22 100644
--- a/engines/bladerunner/crimes_database.cpp
+++ b/engines/bladerunner/crimes_database.cpp
@@ -29,7 +29,7 @@
 
 namespace BladeRunner {
 
-CrimesDatabase::CrimesDatabase(BladeRunnerEngine *vm, const char *cluesResource, int crimeCount) {
+CrimesDatabase::CrimesDatabase(BladeRunnerEngine *vm, const Common::String &cluesResource, int crimeCount) {
 	_crimeCount = crimeCount;
 
 	_crimes.resize(_crimeCount);
@@ -71,10 +71,16 @@ const char *CrimesDatabase::getClueText(int clueId) const {
 	return _cluesText->getText(clueId);
 }
 
-void CrimesDatabase::save(SaveFile &f) {
+void CrimesDatabase::save(SaveFileWriteStream &f) {
 	for (int i = 0; i < _crimeCount; ++i) {
 		uint8 c = _crimes[i];
-		f.write(c);
+		f.writeByte(c);
+	}
+}
+
+void CrimesDatabase::load(SaveFileReadStream &f) {
+	for (int i = 0; i < _crimeCount; ++i) {
+		_crimes[i] = f.readByte();
 	}
 }
 
diff --git a/engines/bladerunner/crimes_database.h b/engines/bladerunner/crimes_database.h
index 1374d52..9bb83f1 100644
--- a/engines/bladerunner/crimes_database.h
+++ b/engines/bladerunner/crimes_database.h
@@ -28,7 +28,8 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
-class SaveFile;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 class TextResource;
 
 class CrimesDatabase {
@@ -38,7 +39,7 @@ class CrimesDatabase {
 	TextResource       *_cluesText;
 
 public:
-	CrimesDatabase(BladeRunnerEngine *vm, const char *cluesResource, int crimeCount);
+	CrimesDatabase(BladeRunnerEngine *vm, const Common::String &cluesResource, int crimeCount);
 	~CrimesDatabase();
 
 	void setCrime(int clueId, int crimeId);
@@ -49,7 +50,8 @@ public:
 
 	const char *getClueText(int clueId) const;
 
-	void save(SaveFile &f);
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/debugger.cpp b/engines/bladerunner/debugger.cpp
index 2c9b7e6..87ab5d2 100644
--- a/engines/bladerunner/debugger.cpp
+++ b/engines/bladerunner/debugger.cpp
@@ -386,9 +386,9 @@ void Debugger::drawSceneObjects() {
 		for (int i = 0; i < count; i++) {
 			SceneObjects::SceneObject *sceneObject = &_vm->_sceneObjects->_sceneObjects[_vm->_sceneObjects->_sceneObjectsSortedByDistance[i]];
 
-			const BoundingBox *bbox = sceneObject->boundingBox;
+			const BoundingBox &bbox = sceneObject->boundingBox;
 			Vector3 a, b;
-			bbox->getXYZ(&a.x, &a.y, &a.z, &b.x, &b.y, &b.z);
+			bbox.getXYZ(&a.x, &a.y, &a.z, &b.x, &b.y, &b.z);
 			Vector3 pos = _vm->_view->calculateScreenPosition(0.5 * (a + b));
 			int color;
 
@@ -398,7 +398,7 @@ void Debugger::drawSceneObjects() {
 			case kSceneObjectTypeActor:
 				color = 0x7C00; // 11111 00000 00000;
 				drawBBox(a, b, _vm->_view, &_vm->_surfaceFront, color);
-				_vm->_surfaceFront.frameRect(*sceneObject->screenRectangle, color);
+				_vm->_surfaceFront.frameRect(sceneObject->screenRectangle, color);
 				_vm->_mainFont->drawColor(_vm->_textActorNames->getText(sceneObject->id - kSceneObjectOffsetActors), _vm->_surfaceFront, pos.x, pos.y, color);
 				break;
 			case kSceneObjectTypeItem:
@@ -406,7 +406,7 @@ void Debugger::drawSceneObjects() {
 				char itemText[40];
 				drawBBox(a, b, _vm->_view, &_vm->_surfaceFront, color);
 				sprintf(itemText, "item %i", sceneObject->id - kSceneObjectOffsetItems);
-				_vm->_surfaceFront.frameRect(*sceneObject->screenRectangle, color);
+				_vm->_surfaceFront.frameRect(sceneObject->screenRectangle, color);
 				_vm->_mainFont->drawColor(itemText, _vm->_surfaceFront, pos.x, pos.y, color);
 				break;
 			case kSceneObjectTypeObject:
@@ -417,7 +417,7 @@ void Debugger::drawSceneObjects() {
 					color = 0x03E0; // 00000 11111 00000;
 				}
 				drawBBox(a, b, _vm->_view, &_vm->_surfaceFront, color);
-				_vm->_surfaceFront.frameRect(*sceneObject->screenRectangle, color);
+				_vm->_surfaceFront.frameRect(sceneObject->screenRectangle, color);
 				_vm->_mainFont->drawColor(_vm->_scene->objectGetName(sceneObject->id - kSceneObjectOffsetObjects), _vm->_surfaceFront, pos.x, pos.y, color);
 				break;
 			}
diff --git a/engines/bladerunner/dialogue_menu.cpp b/engines/bladerunner/dialogue_menu.cpp
index 7b7223f..744183a 100644
--- a/engines/bladerunner/dialogue_menu.cpp
+++ b/engines/bladerunner/dialogue_menu.cpp
@@ -365,27 +365,51 @@ bool DialogueMenu::waitingForInput() const {
 	return _waitingForInput;
 }
 
-void DialogueMenu::save(SaveFile &f) {
-	f.write(_isVisible);
-	f.write(_waitingForInput);
-	f.write(_selectedItemIndex);
-	f.write(_listSize);
+void DialogueMenu::save(SaveFileWriteStream &f) {
+	f.writeBool(_isVisible);
+	f.writeBool(_waitingForInput);
+	f.writeInt(_selectedItemIndex);
+	f.writeInt(_listSize);
 
-	f.write(_neverRepeatListSize);
+	f.writeInt(_neverRepeatListSize);
 	for (int i = 0; i < 100; ++i) {
-		f.write(_neverRepeatValues[i]);
+		f.writeInt(_neverRepeatValues[i]);
 	}
 	for (int i = 0; i < 100; ++i) {
-		f.write(_neverRepeatWasSelected[i]);
+		f.writeBool(_neverRepeatWasSelected[i]);
 	}
 	for (int i = 0; i < 10; ++i) {
-		f.write(_items[i].text, 50);
-		f.write(_items[i].answerValue);
-		f.write(_items[i].colorIntensity);
-		f.write(_items[i].priorityPolite);
-		f.write(_items[i].priorityNormal);
-		f.write(_items[i].prioritySurly);
-		f.write(_items[i].isDone);
+		f.writeStringSz(_items[i].text, 50);
+		f.writeInt(_items[i].answerValue);
+		f.writeInt(_items[i].colorIntensity);
+		f.writeInt(_items[i].priorityPolite);
+		f.writeInt(_items[i].priorityNormal);
+		f.writeInt(_items[i].prioritySurly);
+		f.writeInt(_items[i].isDone);
+	}
+}
+
+void DialogueMenu::load(SaveFileReadStream &f) {
+	_isVisible = f.readBool();
+	_waitingForInput = f.readBool();
+	_selectedItemIndex = f.readInt();
+	_listSize = f.readInt();
+
+	_neverRepeatListSize = f.readInt();
+	for (int i = 0; i < 100; ++i) {
+		_neverRepeatValues[i] = f.readInt();
+	}
+	for (int i = 0; i < 100; ++i) {
+		_neverRepeatWasSelected[i] = f.readBool();
+	}
+	for (int i = 0; i < 10; ++i) {
+		_items[i].text = f.readStringSz(50);
+		_items[i].answerValue = f.readInt();
+		_items[i].colorIntensity = f.readInt();
+		_items[i].priorityPolite = f.readInt();
+		_items[i].priorityNormal = f.readInt();
+		_items[i].prioritySurly = f.readInt();
+		_items[i].isDone = f.readInt();
 	}
 }
 
diff --git a/engines/bladerunner/dialogue_menu.h b/engines/bladerunner/dialogue_menu.h
index b06cf9d..6dfd3ef 100644
--- a/engines/bladerunner/dialogue_menu.h
+++ b/engines/bladerunner/dialogue_menu.h
@@ -33,7 +33,8 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
-class SaveFile;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 class TextResource;
 
 class DialogueMenu {
@@ -97,16 +98,14 @@ public:
 	void mouseUp();
 	bool waitingForInput() const;
 
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
+
 private:
 	bool showAt(int x, int y);
 	int  getAnswerIndex(int answer) const;
 	const char *getText(int id) const;
 	void calculatePosition(int unusedX = 0, int unusedY = 0);
-
-public:
-	void save(SaveFile &f);
-
-private:
 	void clear();
 	void reset();
 
diff --git a/engines/bladerunner/fog.cpp b/engines/bladerunner/fog.cpp
index fff27b0..c732b59 100644
--- a/engines/bladerunner/fog.cpp
+++ b/engines/bladerunner/fog.cpp
@@ -27,7 +27,6 @@
 namespace BladeRunner {
 
 Fog::Fog() {
-	_name[0]            = 0;
 	_frameCount         = 0;
 	_animatedParameters = 0;
 	_fogDensity         = 0.0f;
@@ -55,7 +54,9 @@ Fog::~Fog() {
 
 int Fog::readCommon(Common::ReadStream *stream) {
 	int offset = stream->readUint32LE();
-	stream->read(_name, 20);
+	char buf[20];
+	stream->read(buf, sizeof(buf));
+	_name = buf;
 	_fogColor.r = stream->readFloatLE();
 	_fogColor.g = stream->readFloatLE();
 	_fogColor.b = stream->readFloatLE();
diff --git a/engines/bladerunner/fog.h b/engines/bladerunner/fog.h
index 95ac550..e952d24 100644
--- a/engines/bladerunner/fog.h
+++ b/engines/bladerunner/fog.h
@@ -38,7 +38,8 @@ class Fog {
 	friend class SetEffects;
 
 protected:
-	char       _name[20];
+	Common::String _name;
+
 	int        _frameCount;
 	int        _animatedParameters;
 	Matrix4x3  _matrix;
diff --git a/engines/bladerunner/game_flags.cpp b/engines/bladerunner/game_flags.cpp
index 1e0b583..6fdcb89 100644
--- a/engines/bladerunner/game_flags.cpp
+++ b/engines/bladerunner/game_flags.cpp
@@ -73,9 +73,15 @@ bool GameFlags::query(int flag) const {
 	return !!(_flags[flag / 32] & (1 << (flag % 32)));
 }
 
-void GameFlags::save(SaveFile &f) {
+void GameFlags::save(SaveFileWriteStream &f) {
 	for (int i = 0; i != _flagCount / 32 + 1; ++i) {
-		f.write(_flags[i]);
+		f.writeUint32LE(_flags[i]);
+	}
+}
+
+void GameFlags::load(SaveFileReadStream &f) {
+	for (int i = 0; i != _flagCount / 32 + 1; ++i) {
+		_flags[i] = f.readUint32LE();
 	}
 }
 
diff --git a/engines/bladerunner/game_flags.h b/engines/bladerunner/game_flags.h
index b409e85..837537f 100644
--- a/engines/bladerunner/game_flags.h
+++ b/engines/bladerunner/game_flags.h
@@ -27,7 +27,8 @@
 
 namespace BladeRunner {
 
-class SaveFile;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 
 class GameFlags {
 	uint32 *_flags;
@@ -43,7 +44,8 @@ public:
 	void reset(int flag);
 	bool query(int flag) const;
 
-	void save(SaveFile &f);
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/game_info.cpp b/engines/bladerunner/game_info.cpp
index db664c0..9dc261b 100644
--- a/engines/bladerunner/game_info.cpp
+++ b/engines/bladerunner/game_info.cpp
@@ -31,10 +31,6 @@ namespace BladeRunner {
 
 GameInfo::GameInfo(BladeRunnerEngine *vm) {
 	_vm = vm;
-	_sceneNames         = nullptr;
-	_sfxTracks          = nullptr;
-	_musicTracks        = nullptr;
-	_outtakes           = nullptr;
 	_actorCount         = 0;
 	_playerId           = 0;
 	_flagCount          = 0;
@@ -53,18 +49,12 @@ GameInfo::GameInfo(BladeRunnerEngine *vm) {
 	_fleeWaypointCount  = 0;
 }
 
-GameInfo::~GameInfo() {
-	delete[] _sceneNames;
-	delete[] _sfxTracks;
-	delete[] _musicTracks;
-	delete[] _outtakes;
-}
-
 bool GameInfo::open(const Common::String &name) {
 	Common::SeekableReadStream *s = _vm->getResourceStream(name);
 
-	if (!s)
+	if (!s) {
 		return false;
+	}
 
 	uint32 unk;
 	_actorCount           = s->readUint32LE();   /* 00 */
@@ -88,25 +78,33 @@ bool GameInfo::open(const Common::String &name) {
 
 	(void)unk;
 
-	_sceneNames = new char[_sceneNamesCount][5];
-	for (uint32 i = 0; i != _sceneNamesCount; ++i)
-		s->read(_sceneNames[i], 5);
+	char buf[9];
+
+	_sceneNames.resize(_sceneNamesCount);
+	for (uint32 i = 0; i != _sceneNamesCount; ++i) {
+		s->read(buf, 5);
+		_sceneNames[i] = buf;
+	}
 
-	_sfxTracks = new char[_sfxTrackCount][13];
+	_sfxTracks.resize(_sfxTrackCount);
 	for (uint32 i = 0; i != _sfxTrackCount; ++i) {
-		s->read(_sfxTracks[i], 9);
-		strcat(_sfxTracks[i], ".AUD");
+		s->read(buf, 9);
+		_sfxTracks[i] = buf;
+		_sfxTracks[i] += ".AUD";
 	}
 
-	_musicTracks = new char[_musicTrackCount][13];
+	_musicTracks.resize(_musicTrackCount);
 	for (uint32 i = 0; i != _musicTrackCount; ++i) {
-		s->read(_musicTracks[i], 9);
-		strcat(_musicTracks[i], ".AUD");
+		s->read(buf, 9);
+		_musicTracks[i] = buf;
+		_musicTracks[i] += ".AUD";
 	}
 
-	_outtakes = new char[_outtakeCount][13];
-	for (uint32 i = 0; i != _outtakeCount; ++i)
-		s->read(_outtakes[i], 9);
+	_outtakes.resize(_outtakeCount);
+	for (uint32 i = 0; i != _outtakeCount; ++i) {
+		s->read(buf, 9);
+		_outtakes[i] = buf;
+	}
 
 #if BLADERUNNER_DEBUG_CONSOLE
 	debug("\nScene names\n----------------");
@@ -135,34 +133,38 @@ bool GameInfo::open(const Common::String &name) {
 	return !err;
 }
 
-const char *GameInfo::getSceneName(int i) const {
+const Common::String &GameInfo::getSceneName(int i) const {
 	if (i < 0 || i >= (int)_sceneNamesCount) {
 		warning("GameInfo::getSceneName: unknown id \"%i\"", i);
-		return nullptr;
+		static Common::String str("UNKNOWN_SCENE");
+		return str;
 	}
 	return _sceneNames[i];
 }
 
-const char *GameInfo::getSfxTrack(int i) const {
+const Common::String &GameInfo::getSfxTrack(int i) const {
 	if (i < 0 || i >= (int)_sfxTrackCount) {
 		warning("GameInfo::getSfxTrack: unknown id \"%i\"", i);
-		return nullptr;
+		static Common::String str("UNKNOWN_SFX_TRACK");
+		return str;
 	}
 	return _sfxTracks[i];
 }
 
-const char *GameInfo::getMusicTrack(int i) const {
+const Common::String &GameInfo::getMusicTrack(int i) const {
 	if (i < 0 || i >= (int)_musicTrackCount) {
 		warning("GameInfo::getMusicTrack: unknown id \"%i\"", i);
-		return nullptr;
+		static Common::String str("UNKNOWN_MUSIC_TRACK");
+		return str;
 	}
 	return _musicTracks[i];
 }
 
-const char *GameInfo::getOuttake(int i) const {
+const Common::String &GameInfo::getOuttake(int i) const {
 	if (i < 0 || i >= (int)_outtakeCount) {
 		warning("GameInfo::getOuttake: unknown id \"%i\"", i);
-		return nullptr;
+		static Common::String str("UNKNOWN_OUTTAKE");
+		return str;
 	}
 	return _outtakes[i];
 }
diff --git a/engines/bladerunner/game_info.h b/engines/bladerunner/game_info.h
index bc7fc1e..857efa9 100644
--- a/engines/bladerunner/game_info.h
+++ b/engines/bladerunner/game_info.h
@@ -23,6 +23,7 @@
 #ifndef BLADERUNNER_GAME_INFO_H
 #define BLADERUNNER_GAME_INFO_H
 
+#include "common/array.h"
 #include "common/str.h"
 
 namespace BladeRunner {
@@ -49,14 +50,13 @@ class GameInfo {
 	uint32 _coverWaypointCount;
 	uint32 _fleeWaypointCount;
 
-	char (*_sceneNames)[5];
-	char (*_sfxTracks)[13];
-	char (*_musicTracks)[13];
-	char (*_outtakes)[13];
+	Common::Array<Common::String> _sceneNames;
+	Common::Array<Common::String> _sfxTracks;
+	Common::Array<Common::String> _musicTracks;
+	Common::Array<Common::String> _outtakes;
 
 public:
 	GameInfo(BladeRunnerEngine *vm);
-	~GameInfo();
 
 	bool open(const Common::String &name);
 
@@ -77,10 +77,10 @@ public:
 	uint32 getCoverWaypointCount() const   { return _coverWaypointCount; }
 	uint32 getFleeWaypointCount() const    { return _fleeWaypointCount; }
 
-	const char *getSceneName(int i) const;
-	const char *getSfxTrack(int i) const;
-	const char *getMusicTrack(int i) const;
-	const char *getOuttake(int i) const;
+	const Common::String &getSceneName(int i) const;
+	const Common::String &getSfxTrack(int i) const;
+	const Common::String &getMusicTrack(int i) const;
+	const Common::String &getOuttake(int i) const;
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/item.cpp b/engines/bladerunner/item.cpp
index bf09efb..9aa50e3 100644
--- a/engines/bladerunner/item.cpp
+++ b/engines/bladerunner/item.cpp
@@ -174,26 +174,48 @@ bool Item::isUnderMouse(int mouseX, int mouseY) const {
 	    && mouseY <= _screenRectangle.bottom + 10;
 }
 
-void Item::save(SaveFile &f) {
-	f.write(_setId);
-	f.write(_itemId);
-	_boundingBox.save(f);
-	f.write(_screenRectangle);
-	f.write(_animationId);
-	f.write(_position);
-	f.write(_facing);
-	f.write(_angle);
-	f.write(_width);
-	f.write(_height);
-	f.write(_screenX);
-	f.write(_screenY);
-	f.write(_depth);
-	f.write(_isTarget);
-	f.write(_isSpinning);
-	f.write(_facingChange);
-	f.write(0.0f); // _viewAngle
-	f.write(_isVisible);
-	f.write(_isPoliceMazeEnemy);
+void Item::save(SaveFileWriteStream &f) {
+	f.writeInt(_setId);
+	f.writeInt(_itemId);
+	f.writeBoundingBox(_boundingBox);
+	f.writeRect(_screenRectangle);
+	f.writeInt(_animationId);
+	f.writeVector3(_position);
+	f.writeInt(_facing);
+	f.writeFloat(_angle);
+	f.writeInt(_width);
+	f.writeInt(_height);
+	f.writeInt(_screenX);
+	f.writeInt(_screenY);
+	f.writeFloat(_depth);
+	f.writeBool(_isTarget);
+	f.writeBool(_isSpinning);
+	f.writeInt(_facingChange);
+	f.writeFloat(0.0f); // _viewAngle
+	f.writeBool(_isVisible);
+	f.writeBool(_isPoliceMazeEnemy);
+}
+
+void Item::load(SaveFileReadStream &f) {
+	_setId = f.readInt();
+	_itemId = f.readInt();
+	_boundingBox = f.readBoundingBox();
+	_screenRectangle = f.readRect();
+	_animationId = f.readInt();
+	_position = f.readVector3();
+	_facing  = f.readInt();
+	_angle = f.readFloat();
+	_width = f.readInt();
+	_height = f.readInt();
+	_screenX = f.readInt();
+	_screenY = f.readInt();
+	_depth = f.readFloat();
+	_isTarget = f.readBool();
+	_isSpinning = f.readBool();
+	_facingChange = f.readInt();
+	f.skip(4);
+	_isVisible = f.readBool();
+	_isPoliceMazeEnemy = f.readBool();
 }
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/item.h b/engines/bladerunner/item.h
index bf07a11..9a28d9b 100644
--- a/engines/bladerunner/item.h
+++ b/engines/bladerunner/item.h
@@ -32,7 +32,8 @@ namespace BladeRunner {
 
 class BladeRunnerEngine;
 class Items;
-class SaveFile;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 
 class Item {
 	friend class Items;
@@ -66,8 +67,8 @@ public:
 	void setXYZ(Vector3 position);
 	void getWidthHeight(int *width, int *height) const;
 
-	BoundingBox *getBoundingBox() { return &_boundingBox; }
-	Common::Rect *getScreenRectangle() { return &_screenRectangle; }
+	const BoundingBox &getBoundingBox() { return _boundingBox; }
+	const Common::Rect &getScreenRectangle() { return _screenRectangle; }
 	int getFacing() const { return _facing; }
 	void setFacing(int facing) { _facing = facing; }
 
@@ -86,7 +87,8 @@ public:
 
 	bool isUnderMouse(int mouseX, int mouseY) const;
 
-	void save(SaveFile &f);
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
 };
 
 }
diff --git a/engines/bladerunner/items.cpp b/engines/bladerunner/items.cpp
index 7fe769cb9..caae3ae 100644
--- a/engines/bladerunner/items.cpp
+++ b/engines/bladerunner/items.cpp
@@ -89,7 +89,7 @@ bool Items::addToWorld(int itemId, int animationId, int setId, Vector3 position,
 	item->setup(itemId, setId, animationId, position, facing, height, width, isTargetFlag, isVisible, isPoliceMazeEnemy);
 
 	if (addToSetFlag && setId == _vm->_scene->getSetId()) {
-		return _vm->_sceneObjects->addItem(itemId + kSceneObjectOffsetItems, &item->_boundingBox, &item->_screenRectangle, isTargetFlag, isVisible);
+		return _vm->_sceneObjects->addItem(itemId + kSceneObjectOffsetItems, item->_boundingBox, item->_screenRectangle, isTargetFlag, isVisible);
 	}
 	return true;
 }
@@ -102,7 +102,7 @@ bool Items::addToSet(int setId) {
 	for (int i = 0; i < itemCount; i++) {
 		Item *item = _items[i];
 		if (item->_setId == setId) {
-			_vm->_sceneObjects->addItem(item->_itemId + kSceneObjectOffsetItems, &item->_boundingBox, &item->_screenRectangle, item->isTarget(), item->_isVisible);
+			_vm->_sceneObjects->addItem(item->_itemId + kSceneObjectOffsetItems, item->_boundingBox, item->_screenRectangle, item->isTarget(), item->_isVisible);
 		}
 	}
 	return true;
@@ -184,19 +184,19 @@ void Items::setIsObstacle(int itemId, bool val) {
 	_vm->_sceneObjects->setIsClickable(itemId + kSceneObjectOffsetItems, val);
 }
 
-BoundingBox *Items::getBoundingBox(int itemId) {
+const BoundingBox &Items::getBoundingBox(int itemId) {
 	int itemIndex = findItem(itemId);
-	if (itemIndex == -1) {
-		return nullptr;
-	}
+	// if (itemIndex == -1) {
+	// 	return nullptr;
+	// }
 	return _items[itemIndex]->getBoundingBox();
 }
 
-Common::Rect *Items::getScreenRectangle(int itemId) {
+const Common::Rect &Items::getScreenRectangle(int itemId) {
 	int itemIndex = findItem(itemId);
-	if (itemIndex == -1) {
-		return nullptr;
-	}
+	// if (itemIndex == -1) {
+	// 	return nullptr;
+	// }
 	return _items[itemIndex]->getScreenRectangle();
 }
 
@@ -243,10 +243,10 @@ int Items::findItem(int itemId) const {
 	return -1;
 }
 
-void Items::save(SaveFile &f) {
+void Items::save(SaveFileWriteStream &f) {
 	int size = (int)_items.size();
 
-	f.write(size);
+	f.writeInt(size);
 	int i;
 	for (i = 0; i != size; ++i) {
 		_items[i]->save(f);
@@ -258,4 +258,24 @@ void Items::save(SaveFile &f) {
 	}
 }
 
+void Items::load(SaveFileReadStream &f) {
+	for (int i = _items.size() - 1; i >= 0; i--) {
+		delete _items.remove_at(i);
+	}
+	_items.resize(f.readInt());
+
+	int size = (int)_items.size();
+
+	int i;
+	for (i = 0; i != size; ++i) {
+		_items[i] = new Item(_vm);
+		_items[i]->load(f);
+	}
+
+	// Always read out 100 items
+	for (; i != 100; ++i) {
+		f.skip(0x174); // bbox + rect + 18 float fields
+	}
+}
+
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/items.h b/engines/bladerunner/items.h
index cc9af73..e397105 100644
--- a/engines/bladerunner/items.h
+++ b/engines/bladerunner/items.h
@@ -30,7 +30,8 @@
 
 namespace BladeRunner {
 
-class SaveFile;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 
 class Items {
 	BladeRunnerEngine *_vm;
@@ -59,14 +60,16 @@ public:
 	bool isVisible(int itemId) const;
 	int findTargetUnderMouse(int mouseX, int mouseY) const;
 
-	BoundingBox *getBoundingBox(int itemId);
-	Common::Rect *getScreenRectangle(int itemId);
+	const BoundingBox &getBoundingBox(int itemId);
+	const Common::Rect &getScreenRectangle(int itemId);
 	int getFacing(int itemId) const;
 	void setFacing(int itemId, int facing);
 
 	void spinInWorld(int itemId);
 
-	void save(SaveFile &f);
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
+
 private:
 	int findItem(int itemId) const;
 };
diff --git a/engines/bladerunner/light.cpp b/engines/bladerunner/light.cpp
index 6159582..2231e04 100644
--- a/engines/bladerunner/light.cpp
+++ b/engines/bladerunner/light.cpp
@@ -69,7 +69,9 @@ void Light::read(Common::ReadStream *stream, int frameCount, int frame, int anim
 	int size = stream->readUint32LE();
 	size = size - 32;
 
-	stream->read(_name, 20);
+	char buf[20];
+	stream->read(buf, sizeof(buf));
+	_name = buf;
 
 	_animatedParameters = stream->readUint32LE();
 
diff --git a/engines/bladerunner/light.h b/engines/bladerunner/light.h
index 1ef9f30..31e40e0 100644
--- a/engines/bladerunner/light.h
+++ b/engines/bladerunner/light.h
@@ -42,7 +42,8 @@ class Light {
 	friend class SliceRenderer;
 
 protected:
-	char      _name[20];
+	Common::String _name;
+
 	int       _frameCount;
 	int       _animated;
 	int       _animatedParameters;
diff --git a/engines/bladerunner/module.mk b/engines/bladerunner/module.mk
index f4bf926..5fb39b8 100644
--- a/engines/bladerunner/module.mk
+++ b/engines/bladerunner/module.mk
@@ -40,8 +40,6 @@ MODULE_OBJS = \
 	obstacles.o \
 	outtake.o \
 	overlays.o \
-	police_maze.o \
-	police_maze_track.o \
 	regions.o \
 	savefile.o \
 	scene.o \
diff --git a/engines/bladerunner/movement_track.cpp b/engines/bladerunner/movement_track.cpp
index 5a6407e..322d92e 100644
--- a/engines/bladerunner/movement_track.cpp
+++ b/engines/bladerunner/movement_track.cpp
@@ -109,17 +109,31 @@ bool MovementTrack::next(int *waypointId, int *delay, int *angle, bool *run) {
 	}
 }
 
-void MovementTrack::save(SaveFile &f) {
-	f.write(_currentIndex);
-	f.write(_lastIndex);
-	f.write(_hasNext);
-	f.write(_paused);
-	for (int i = 0; i < 100; ++i) {
+void MovementTrack::save(SaveFileWriteStream &f) {
+	f.writeInt(_currentIndex);
+	f.writeInt(_lastIndex);
+	f.writeBool(_hasNext);
+	f.writeBool(_paused);
+	for (int i = 0; i < kSize; ++i) {
 		Entry &e = _entries[i];
-		f.write(e.waypointId);
-		f.write(e.delay);
-		f.write(e.angle);
-		f.write(e.run);
+		f.writeInt(e.waypointId);
+		f.writeInt(e.delay);
+		f.writeInt(e.angle);
+		f.writeBool(e.run);
+	}
+}
+
+void MovementTrack::load(SaveFileReadStream &f) {
+	_currentIndex = f.readInt();
+	_lastIndex = f.readInt();
+	_hasNext = f.readBool();
+	_paused = f.readBool();
+	for (int i = 0; i < kSize; ++i) {
+		Entry &e = _entries[i];
+		e.waypointId = f.readInt();
+		e.delay = f.readInt();
+		e.angle = f.readInt();
+		e.run = f.readBool();
 	}
 }
 
diff --git a/engines/bladerunner/movement_track.h b/engines/bladerunner/movement_track.h
index 2d59fd9..2eab4cd 100644
--- a/engines/bladerunner/movement_track.h
+++ b/engines/bladerunner/movement_track.h
@@ -29,7 +29,8 @@ namespace BladeRunner {
 
 class BladeRunnerEngine;
 class BoundingBox;
-class SaveFile;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 
 class MovementTrack {
 	static const int kSize = 100;
@@ -60,7 +61,9 @@ public:
 	bool hasNext() const;
 	bool next(int *waypointId, int *delay, int *angle, bool *run);
 
-	void save(SaveFile &f);
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
+
 private:
 	void reset();
 };
diff --git a/engines/bladerunner/music.cpp b/engines/bladerunner/music.cpp
index 818d412..05412dd 100644
--- a/engines/bladerunner/music.cpp
+++ b/engines/bladerunner/music.cpp
@@ -26,6 +26,7 @@
 #include "bladerunner/aud_stream.h"
 #include "bladerunner/bladerunner.h"
 #include "bladerunner/game_info.h"
+#include "bladerunner/savefile.h"
 
 #include "common/timer.h"
 
@@ -167,6 +168,68 @@ void Music::playSample() {
 	}
 }
 
+void Music::save(SaveFileWriteStream &f) {
+	f.writeBool(_isNextPresent);
+	f.writeBool(_isPlaying);
+	f.writeBool(_isPaused);
+	f.writeStringSz(_current.name, 13);
+	f.writeInt(_current.volume);
+	f.writeInt(_current.pan);
+	f.writeInt(_current.timeFadeIn);
+	f.writeInt(_current.timePlay);
+	f.writeInt(_current.loop);
+	f.writeInt(_current.timeFadeOut);
+	f.writeStringSz(_next.name, 13);
+	f.writeInt(_next.volume);
+	f.writeInt(_next.pan);
+	f.writeInt(_next.timeFadeIn);
+	f.writeInt(_next.timePlay);
+	f.writeInt(_next.loop);
+	f.writeInt(_next.timeFadeOut);
+}
+
+void Music::load(SaveFileReadStream &f) {
+	_isNextPresent = f.readBool();
+	_isPlaying = f.readBool();
+	_isPaused = f.readBool();
+	_current.name = f.readStringSz(13);
+	_current.volume = f.readInt();
+	_current.pan = f.readInt();
+	_current.timeFadeIn = f.readInt();
+	_current.timePlay = f.readInt();
+	_current.loop = f.readInt();
+	_current.timeFadeOut = f.readInt();
+	_next.name = f.readStringSz(13);
+	_next.volume = f.readInt();
+	_next.pan = f.readInt();
+	_next.timeFadeIn = f.readInt();
+	_next.timePlay = f.readInt();
+	_next.loop = f.readInt();
+	_next.timeFadeOut = f.readInt();
+
+	stop(2);
+	if (_isPlaying) {
+		if (_channel == -1) {
+			play(_current.name,
+				_current.volume,
+				_current.pan,
+				_current.timeFadeIn,
+				_current.timePlay,
+				_current.loop,
+				_current.timeFadeOut);
+		} else {
+			_isNextPresent = true;
+			_next.name = _current.name;
+			_next.volume = _current.volume;
+			_next.pan = _current.pan;
+			_next.timeFadeIn = _current.timeFadeIn;
+			_next.timePlay = _current.timePlay;
+			_next.loop = _current.loop;
+			_next.timeFadeOut = _current.timeFadeOut;
+		}
+	}
+}
+
 void Music::adjustVolume(int volume, int delay) {
 	if (_channel >= 0) {
 		_vm->_audioMixer->adjustVolume(_channel, volume, delay);
diff --git a/engines/bladerunner/music.h b/engines/bladerunner/music.h
index de19942..b4dc284 100644
--- a/engines/bladerunner/music.h
+++ b/engines/bladerunner/music.h
@@ -30,6 +30,8 @@ namespace BladeRunner {
 
 class AudStream;
 class BladeRunnerEngine;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 
 class Music {
 	struct Track {
@@ -47,9 +49,9 @@ class Music {
 	Common::Mutex _mutex;
 	int           _musicVolume;
 	int           _channel;
-	int           _isNextPresent;
-	int           _isPlaying;
-	int           _isPaused;
+	bool          _isNextPresent;
+	bool          _isPlaying;
+	bool          _isPaused;
 	Track         _current;
 	Track         _next;
 	byte         *_data;
@@ -68,6 +70,9 @@ public:
 	int getVolume();
 	void playSample();
 
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
+
 private:
 	void adjustVolume(int volume, int delay);
 	void adjustPan(int pan, int delay);
diff --git a/engines/bladerunner/obstacles.cpp b/engines/bladerunner/obstacles.cpp
index 603d514..ada41df 100644
--- a/engines/bladerunner/obstacles.cpp
+++ b/engines/bladerunner/obstacles.cpp
@@ -68,28 +68,64 @@ void Obstacles::backup() {
 
 void Obstacles::restore() {}
 
-void Obstacles::save(SaveFile &f) {
-	f.write(_backup);
-	f.write(_count);
+void Obstacles::save(SaveFileWriteStream &f) {
+	f.writeBool(_backup);
+	f.writeInt(_count);
 	for (int i = 0; i < _count; ++i) {
 		Polygon &p = _polygonsBackup[i];
-		f.write(p.isPresent);
-		f.write(p.verticeCount);
-		f.write(p.left);
-		f.write(p.bottom);
-		f.write(p.right);
-		f.write(p.top);
+		f.writeBool(p.isPresent);
+		f.writeInt(p.verticeCount);
+		f.writeFloat(p.left);
+		f.writeFloat(p.bottom);
+		f.writeFloat(p.right);
+		f.writeFloat(p.top);
 		for (int j = 0; j < kPolygonVertexCount; ++j) {
-			f.write(p.vertices[j]);
+			f.writeVector2(p.vertices[j]);
 		}
 		for (int j = 0; j < kPolygonVertexCount; ++j) {
-			f.write(p.vertexType[j]);
+			f.writeInt(p.vertexType[j]);
 		}
 	}
 	for (int i = 0; i < kVertexCount; ++i) {
-		f.write(_vertices[i]);
+		f.writeVector2(_vertices[i]);
 	}
-	f.write(_verticeCount);
+	f.writeInt(_verticeCount);
+}
+
+void Obstacles::load(SaveFileReadStream &f) {
+	for (int i = 0; i < kPolygonCount; ++i) {
+		_polygons[i].isPresent = false;
+		_polygons[i].verticeCount = 0;
+		_polygonsBackup[i].isPresent = false;
+		_polygonsBackup[i].verticeCount = 0;
+	}
+
+	_backup = f.readBool();
+	_count = f.readInt();
+	for (int i = 0; i < _count; ++i) {
+		Polygon &p = _polygonsBackup[i];
+		p.isPresent = f.readBool();
+		p.verticeCount = f.readInt();
+		p.left = f.readFloat();
+		p.bottom = f.readFloat();
+		p.right = f.readFloat();
+		p.top = f.readFloat();
+		for (int j = 0; j < kPolygonVertexCount; ++j) {
+			p.vertices[j] = f.readVector2();
+		}
+		for (int j = 0; j < kPolygonVertexCount; ++j) {
+			p.vertexType[j] = f.readInt();
+		}
+	}
+
+	for (int i = 0; i < kPolygonCount; ++i) {
+		_polygons[i] = _polygonsBackup[i];
+	}
+
+	for (int i = 0; i < kVertexCount; ++i) {
+		_vertices[i] = f.readVector2();
+	}
+	_verticeCount = f.readInt();
 }
 
 
diff --git a/engines/bladerunner/obstacles.h b/engines/bladerunner/obstacles.h
index fc06fe6..b50c51e 100644
--- a/engines/bladerunner/obstacles.h
+++ b/engines/bladerunner/obstacles.h
@@ -28,7 +28,8 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
-class SaveFile;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 
 class Obstacles {
 	static const int kVertexCount        = 150;
@@ -64,7 +65,9 @@ public:
 	bool find(const Vector3 &from, const Vector3 &to, Vector3 *next) const;
 	void backup();
 	void restore();
-	void save(SaveFile &f);
+
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/overlays.cpp b/engines/bladerunner/overlays.cpp
index 0f011ad..65ba83f 100644
--- a/engines/bladerunner/overlays.cpp
+++ b/engines/bladerunner/overlays.cpp
@@ -59,8 +59,8 @@ Overlays::~Overlays() {
 int Overlays::play(const Common::String &name, int loopId, bool loopForever, bool startNow, int a6) {
 	assert(name.size() <= 12);
 
-	int id = mix_id(name);
-	int index = findById(id);
+	int32 hash = MIXArchive::getHash(name);
+	int index = findByHash(hash);
 	if (index < 0) {
 		index = findEmpty();
 		if (index < 0) {
@@ -68,7 +68,7 @@ int Overlays::play(const Common::String &name, int loopId, bool loopForever, boo
 		}
 		_videos[index].loaded = true;
 		_videos[index].name = name;
-		_videos[index].id = id;
+		_videos[index].hash = hash;
 		_videos[index].vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceFront);
 
 		// repeat forever
@@ -87,8 +87,7 @@ int Overlays::play(const Common::String &name, int loopId, bool loopForever, boo
 }
 
 void Overlays::remove(const Common::String &name) {
-	int id = mix_id(name);
-	int index = findById(id);
+	int index = findByHash(MIXArchive::getHash(name));
 	if (index >= 0) {
 		resetSingle(index);
 	}
@@ -113,9 +112,9 @@ void Overlays::tick() {
 	}
 }
 
-int Overlays::findById(int32 id) const {
+int Overlays::findByHash(int32 hash) const {
 	for (int i = 0; i < kOverlayVideos; ++i) {
-		if (_videos[i].loaded && _videos[i].id == id) {
+		if (_videos[i].loaded && _videos[i].hash == hash) {
 			return i;
 		}
 	}
@@ -138,7 +137,7 @@ void Overlays::resetSingle(int i) {
 		_videos[i].vqaPlayer = nullptr;
 	}
 	_videos[i].loaded = false;
-	_videos[i].id = 0;
+	_videos[i].hash = 0;
 	_videos[i].field2 = -1;
 	_videos[i].name.clear();
 }
@@ -147,18 +146,34 @@ void Overlays::reset() {
 	_videos.clear();
 }
 
-void Overlays::save(SaveFile &f) {
+void Overlays::save(SaveFileWriteStream &f) {
 	for (int i = 0; i < kOverlayVideos; ++i) {
 		// 37 bytes per overlay
 		Video &ov = _videos[i];
 
-		f.write(ov.loaded);
-		f.write(nullptr);
-		f.write(ov.name, 13);
-		f.write(ov.id);
-		f.write(ov.field0);
-		f.write(ov.field1);
-		f.write(ov.field2);
+		f.writeBool(ov.loaded);
+		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);
+	}
+}
+
+void Overlays::load(SaveFileReadStream &f) {
+	for (int i = 0; i < kOverlayVideos; ++i) {
+		// 37 bytes per overlay
+		Video &ov = _videos[i];
+
+		ov.loaded = f.readBool();
+		f.skip(4); // vqaPlayer pointer
+		ov.vqaPlayer = nullptr;
+		ov.name = f.readStringSz(13);
+		ov.hash = f.readSint32LE();
+		ov.field0 = f.readInt();
+		ov.field1 = f.readInt();
+		ov.field2 = f.readInt();
 	}
 }
 
diff --git a/engines/bladerunner/overlays.h b/engines/bladerunner/overlays.h
index fc8dfa11..405acbc 100644
--- a/engines/bladerunner/overlays.h
+++ b/engines/bladerunner/overlays.h
@@ -33,7 +33,8 @@ struct Surface;
 namespace BladeRunner {
 
 class BladeRunnerEngine;
-class SaveFile;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 class VQAPlayer;
 
 class Overlays {
@@ -43,7 +44,7 @@ class Overlays {
 		bool            loaded;
 		VQAPlayer      *vqaPlayer;
 		Common::String  name;
-		int32           id;
+		int32           hash;
 		int             field0;
 		int             field1;
 		int             field2;
@@ -62,10 +63,11 @@ public:
 	void removeAll();
 	void tick();
 
-	void save(SaveFile &f);
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
 
 private:
-	int findById(int32 id) const;
+	int findByHash(int32 hash) const;
 	int findEmpty() const;
 
 	void resetSingle(int i);
diff --git a/engines/bladerunner/police_maze.cpp b/engines/bladerunner/police_maze.cpp
deleted file mode 100644
index 223171d..0000000
--- a/engines/bladerunner/police_maze.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/* 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/police_maze.h"
-
-#include "bladerunner/bladerunner.h"
-
-#include "bladerunner/police_maze_track.h"
-#include "bladerunner/savefile.h"
-
-namespace BladeRunner {
-
-PoliceMaze::PoliceMaze(BladeRunnerEngine *vm) : _vm(vm) {
-	reset();
-}
-
-PoliceMaze::~PoliceMaze() {
-	reset();
-}
-
-bool PoliceMaze::init() {
-	return true;
-}
-
-void PoliceMaze::save(SaveFile &f) {
-	f.write(_tracksCount);
-	f.write(_a2);
-	f.write(_a3);
-	for (int i = 0; i < 64; ++i) {
-		_tracks[i]->save(f);
-	}
-}
-
-void PoliceMaze::reset() {
-	_tracksCount = 0;
-	_a2 = 0;
-	_a3 = 0;
-	for (int i = 0; i < 64; ++i) {
-		_tracks[i] = nullptr;
-	}
-	_a4 = 0;
-	_a5 = 0;
-}
-
-} // End of namespace BladeRunner
diff --git a/engines/bladerunner/police_maze.h b/engines/bladerunner/police_maze.h
deleted file mode 100644
index 4bd06bb..0000000
--- a/engines/bladerunner/police_maze.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* 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_POLICE_MAZE_H
-#define BLADERUNNER_POLICE_MAZE_H
-
-namespace BladeRunner {
-
-class BladeRunnerEngine;
-class PoliceMazeTrack;
-class SaveFile;
-
-class PoliceMaze {
-	BladeRunnerEngine *_vm;
-
-	PoliceMazeTrack *_tracks[64];
-	int              _tracksCount;
-	int              _a2;
-	int              _a3;
-	int              _a4;
-	int              _a5;
-
-public:
-	PoliceMaze(BladeRunnerEngine *vm);
-	~PoliceMaze();
-
-	bool init();
-
-	void save(SaveFile &f);
-	void reset();
-};
-
-} // End of namespace BladeRunner
-
-#endif
diff --git a/engines/bladerunner/police_maze_track.cpp b/engines/bladerunner/police_maze_track.cpp
deleted file mode 100644
index 988f904..0000000
--- a/engines/bladerunner/police_maze_track.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/* 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/police_maze_track.h"
-
-#include "bladerunner/bladerunner.h"
-
-#include "bladerunner/savefile.h"
-
-namespace BladeRunner {
-
-PoliceMazeTrack::PoliceMazeTrack(BladeRunnerEngine *vm) : _vm(vm) {
-	reset();
-}
-
-PoliceMazeTrack::~PoliceMazeTrack() {
-	reset();
-}
-
-void PoliceMazeTrack::save(SaveFile &f) {
-	f.write(_isPresent);
-	f.write(_itemId);
-	f.write(_count);
-	f.write(_dataIndex);
-	f.write(_a6);
-	f.write(_a7);
-	f.write(_pointIndex);
-	f.write(_a9);
-	f.write(_rotating);
-	f.write(_maxAngle);
-	f.write(_angleChange);
-	f.write(_a13);
-
-	for (int i = 0; i < 100; ++i) {
-		f.write(_points[i]);
-	}
-
-	f.write(_a4);
-	f.write(_a5);
- }
-
-void PoliceMazeTrack::reset() {
-	_isPresent   = false;
-	_itemId      = -1;
-	_count       =  0;
-	_data        =  0;
-	_dataIndex   =  0;
-	_a4          =  0;
-	_a5          =  0;
-	_time        =  0;
-	_a6          =  0;
-	_a7          =  0;
-	_pointIndex  =  0;
-	_a9          =  0;
-	_rotating    =  0;
-	_maxAngle    =  0;
-	_angleChange =  0;
-	_a13         =  1;
-}
-
-} // End of namespace BladeRunner
diff --git a/engines/bladerunner/police_maze_track.h b/engines/bladerunner/police_maze_track.h
deleted file mode 100644
index 9de7a1b..0000000
--- a/engines/bladerunner/police_maze_track.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/* 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_POLICE_MAZE_TRACK_H
-#define BLADERUNNER_POLICE_MAZE_TRACK_H
-
-#include "bladerunner/vector.h"
-
-namespace BladeRunner {
-
-class BladeRunnerEngine;
-class SaveFile;
-
-class PoliceMazeTrack {
-	BladeRunnerEngine *_vm;
-
-	int     _time;
-	bool    _isPresent;
-	int     _itemId;
-	int     _count;
-	Vector2 _points[100];
-	int     _data;
-	int     _dataIndex;
-	int     _a4;
-	int     _a5;
-	int     _a6;
-	int     _a7;
-	int     _pointIndex;
-	int     _a9;
-	int     _rotating;
-	int     _maxAngle;
-	int     _angleChange;
-	int     _a13;
-
-public:
-	PoliceMazeTrack(BladeRunnerEngine *vm);
-	~PoliceMazeTrack();
-
-	void save(SaveFile &f);
-
-	void reset();
-};
-
-} // End of namespace BladeRunner
-
-#endif
diff --git a/engines/bladerunner/regions.cpp b/engines/bladerunner/regions.cpp
index 51b2bae..f741862 100644
--- a/engines/bladerunner/regions.cpp
+++ b/engines/bladerunner/regions.cpp
@@ -101,12 +101,21 @@ void Regions::enable() {
 	_enabled = true;
 }
 
-void Regions::save(SaveFile &f) {
-	f.write(_enabled);
+void Regions::save(SaveFileWriteStream &f) {
+	f.writeBool(_enabled);
 	for (int i = 0; i != 10; ++i) {
-		f.write(_regions[i].rectangle);
-		f.write(_regions[i].type);
-		f.write(_regions[i].present);
+		f.writeRect(_regions[i].rectangle);
+		f.writeInt(_regions[i].type);
+		f.writeInt(_regions[i].present);
+	}
+}
+
+void Regions::load(SaveFileReadStream &f) {
+	_enabled = f.readBool();
+	for (int i = 0; i != 10; ++i) {
+		_regions[i].rectangle = f.readRect();
+		_regions[i].type = f.readInt();
+		_regions[i].present = f.readInt();
 	}
 }
 
diff --git a/engines/bladerunner/regions.h b/engines/bladerunner/regions.h
index ed4dcba..aae3627 100644
--- a/engines/bladerunner/regions.h
+++ b/engines/bladerunner/regions.h
@@ -30,7 +30,8 @@
 
 namespace BladeRunner {
 
-class SaveFile;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 
 class Regions {
 	friend class Debugger;
@@ -57,7 +58,8 @@ public:
 	void setEnabled(bool enabled);
 	void enable();
 
-	void save(SaveFile &f);
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/savefile.cpp b/engines/bladerunner/savefile.cpp
index ff1ff75..3528a6b 100644
--- a/engines/bladerunner/savefile.cpp
+++ b/engines/bladerunner/savefile.cpp
@@ -30,91 +30,137 @@
 
 namespace BladeRunner {
 
-SaveFile::SaveFile(Common::OutSaveFile *saveFile)
-	: _saveFile(saveFile),
-	  _stream(DisposeAfterUse::YES)
-{
+SaveFileWriteStream::SaveFileWriteStream()
+	: MemoryWriteStreamDynamic(DisposeAfterUse::YES) {
 }
 
-void SaveFile::finalize() {
-	_saveFile->writeUint32LE(_stream.size() + 4);
-	_saveFile->write(_stream.getData(), _stream.size());
-	_saveFile->finalize();
+void SaveFileWriteStream::debug(char *p) {
+	write(p, strlen(p) + 1);
 }
 
-void SaveFile::padBytes(int count) {
+void SaveFileWriteStream::padBytes(int count) {
 	for (int i = 0; i < count; ++i) {
-		_stream.writeByte(0);
+		writeByte(0);
 	}
 }
 
-void SaveFile::write(bool v) {
-	_stream.writeUint32LE(v);
+void SaveFileWriteStream::writeInt(int v) {
+	writeUint32LE(v);
 }
 
-void SaveFile::write(int v) {
-	_stream.writeUint32LE(v);
+void SaveFileWriteStream::writeFloat(int v) {
+	writeFloatLE(v);
 }
 
-void SaveFile::write(uint32 v) {
-	_stream.writeUint32LE(v);
+void SaveFileWriteStream::writeBool(bool v) {
+	writeUint32LE(v);
 }
 
-void SaveFile::write(byte v) {
-	_stream.writeByte(v);
+void SaveFileWriteStream::writeStringSz(const Common::String &s, int sz) {
+	assert(s.size() < (uint)sz);
+	write(s.begin(), s.size());
+	padBytes((uint)sz - s.size());
 }
 
-void SaveFile::write(float v) {
-	_stream.writeFloatLE(v);
+void SaveFileWriteStream::writeVector2(const Vector2 &v) {
+	writeFloatLE(v.x);
+	writeFloatLE(v.y);
 }
 
-void SaveFile::debug(char *p) {
-	_stream.write(p, strlen(p) + 1);
+void SaveFileWriteStream::writeVector3(const Vector3 &v) {
+	writeFloatLE(v.x);
+	writeFloatLE(v.y);
+	writeFloatLE(v.z);
 }
 
-void SaveFile::write(char *p, int sz) {
-	_stream.write(p, sz);
+void SaveFileWriteStream::writeRect(const Common::Rect &v) {
+	writeUint32LE(v.left);
+	writeUint32LE(v.top);
+	writeUint32LE(v.right);
+	writeUint32LE(v.bottom);
 }
 
-void SaveFile::write(Common::String &s, int sz) {
-	assert(s.size() < (uint)sz);
-	_stream.write(s.begin(), s.size());
-	padBytes((uint)sz - s.size());
+void SaveFileWriteStream::writeBoundingBox(const BoundingBox &v) {
+	float x0, y0, z0, x1, y1, z1;
+
+	v.getXYZ(&x0, &y0, &z0, &x1, &y1, &z1);
+	writeFloatLE(x0);
+	writeFloatLE(y0);
+	writeFloatLE(z0);
+	writeFloatLE(x1);
+	writeFloatLE(y1);
+	writeFloatLE(z1);
+
+	// Bounding boxes have a lot of extra data that's never actually used
+	for (int i = 0; i != 96; ++i) {
+		writeFloatLE(0.0f);
+	}
 }
 
-void SaveFile::write(const Vector2 &v) {
-	_stream.writeFloatLE(v.x);
-	_stream.writeFloatLE(v.y);
+SaveFileReadStream::SaveFileReadStream(const byte *dataPtr, uint32 dataSize)
+	: MemoryReadStream(dataPtr, dataSize, DisposeAfterUse::YES) {
 }
 
-void SaveFile::write(const Vector3 &v) {
-	_stream.writeFloatLE(v.x);
-	_stream.writeFloatLE(v.y);
-	_stream.writeFloatLE(v.z);
+int SaveFileReadStream::readInt() {
+	return readUint32LE();
 }
 
-void SaveFile::write(const Common::Rect &v) {
-	_stream.writeUint32LE(v.left);
-	_stream.writeUint32LE(v.top);
-	_stream.writeUint32LE(v.right);
-	_stream.writeUint32LE(v.bottom);
+float SaveFileReadStream::readFloat() {
+	return readFloatLE();
 }
 
-void SaveFile::write(const BoundingBox &v) {
+bool SaveFileReadStream::readBool() {
+	return readUint32LE();
+}
+
+Common::String SaveFileReadStream::readStringSz(int sz) {
+	char *buf = (char *)malloc(sz);
+	read(buf, sz);
+	Common::String result = buf;
+	free(buf);
+	return result;
+}
+
+Vector2 SaveFileReadStream::readVector2() {
+	Vector2 result;
+	result.x = readFloatLE();
+	result.y = readFloatLE();
+	return result;
+}
+
+Vector3 SaveFileReadStream::readVector3() {
+	Vector3 result;
+	result.x = readFloatLE();
+	result.y = readFloatLE();
+	result.z = readFloatLE();
+	return result;
+}
+
+Common::Rect SaveFileReadStream::readRect() {
+	Common::Rect result;
+	result.left = readUint32LE();
+	result.top = readUint32LE();
+	result.right = readUint32LE();
+	result.bottom = readUint32LE();
+	return result;
+}
+
+BoundingBox SaveFileReadStream::readBoundingBox() {
 	float x0, y0, z0, x1, y1, z1;
 
-	v.getXYZ(&x0, &y0, &z0, &x1, &y1, &z1);
-	_stream.writeFloatLE(x0);
-	_stream.writeFloatLE(y0);
-	_stream.writeFloatLE(z0);
-	_stream.writeFloatLE(x1);
-	_stream.writeFloatLE(y1);
-	_stream.writeFloatLE(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
-	for (int i = 0; i != 96; ++i) {
-		_stream.writeFloatLE(0.0f);
-	}
+	skip(384);
+
+	return BoundingBox(x0, y0, z0, x1, y1, z1);
 }
 
+
+
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/savefile.h b/engines/bladerunner/savefile.h
index 8c5dc0a..4dfdb20 100644
--- a/engines/bladerunner/savefile.h
+++ b/engines/bladerunner/savefile.h
@@ -27,9 +27,9 @@
 #include "common/types.h"
 
 namespace Common {
-	class OutSaveFile;
-	class String;
-	struct Rect;
+class OutSaveFile;
+class String;
+struct Rect;
 }
 
 namespace BladeRunner {
@@ -38,28 +38,36 @@ class Vector2;
 class Vector3;
 class BoundingBox;
 
-class SaveFile {
-	Common::OutSaveFile              *_saveFile;
-	Common::MemoryWriteStreamDynamic  _stream;
+class SaveFileWriteStream : public Common::MemoryWriteStreamDynamic {
 public:
-	SaveFile(Common::OutSaveFile *saveFile);
+	SaveFileWriteStream();
 
-	// bool err();
-	void finalize();
+	void debug(char *p);
 
 	void padBytes(int count);
-	void write(bool v);
-	void write(int v);
-	void write(uint32 v);
-	void write(byte  v);
-	void write(float v);
-	void debug(char *p);
-	void write(char *p, int sz);
-	void write(Common::String &s, int sz);
-	void write(const Vector2 &v);
-	void write(const Vector3 &v);
-	void write(const Common::Rect &v);
-	void write(const BoundingBox &v);
+
+	void writeInt(int v);
+	void writeFloat(int 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);
+};
+
+class SaveFileReadStream : public Common::MemoryReadStream {
+public:
+	SaveFileReadStream(const byte *dataPtr, uint32 dataSize);
+
+	int readInt();
+	float readFloat();
+	bool readBool();
+	Common::String readStringSz(int sz);
+	Vector2 readVector2();
+	Vector3 readVector3();
+	Common::Rect readRect();
+	BoundingBox readBoundingBox();
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/scene.cpp b/engines/bladerunner/scene.cpp
index d12c85e..fe8dbc7 100644
--- a/engines/bladerunner/scene.cpp
+++ b/engines/bladerunner/scene.cpp
@@ -331,7 +331,7 @@ void Scene::loopStartSpecial(int specialLoopMode, int loopId, bool immediately)
 	}
 }
 
-int Scene::findObject(const char *objectName) {
+int Scene::findObject(const Common::String &objectName) {
 	return _set->findObject(objectName);
 }
 
@@ -377,7 +377,7 @@ void Scene::objectSetIsTarget(int objectId, bool isTarget, bool sceneLoaded) {
 	}
 }
 
-const char *Scene::objectGetName(int objectId) {
+const Common::String &Scene::objectGetName(int objectId) {
 	return _set->objectGetName(objectId);
 }
 
@@ -414,20 +414,36 @@ void Scene::loopEndedStatic(void *data, int frame, int loopId) {
 	((Scene *)data)->loopEnded(frame, loopId);
 }
 
-void Scene::save(SaveFile &f) {
-	f.write(_setId);
-	f.write(_sceneId);
-	f.write(_defaultLoop);
-	f.write(_defaultLoopSet);
-	f.write(_defaultLoopPreloadedSet);
-	f.write(_specialLoopMode);
-	f.write(_specialLoop);
-	f.write(_nextSetId);
-	f.write(_nextSceneId);
-	f.write(_frame);
-	f.write(_actorStartPosition);
-	f.write(_actorStartFacing);
-	f.write(_playerWalkedIn);
+void Scene::save(SaveFileWriteStream &f) {
+	f.writeInt(_setId);
+	f.writeInt(_sceneId);
+	f.writeInt(_defaultLoop);
+	f.writeBool(_defaultLoopSet);
+	f.writeBool(_defaultLoopPreloadedSet);
+	f.writeInt(_specialLoopMode);
+	f.writeInt(_specialLoop);
+	f.writeInt(_nextSetId);
+	f.writeInt(_nextSceneId);
+	f.writeInt(_frame);
+	f.writeVector3(_actorStartPosition);
+	f.writeInt(_actorStartFacing);
+	f.writeBool(_playerWalkedIn);
+}
+
+void Scene::load(SaveFileReadStream &f) {
+	_setId = f.readInt();
+	_sceneId = f.readInt();
+	_defaultLoop = f.readInt();
+	_defaultLoopSet = f.readBool();
+	_defaultLoopPreloadedSet = f.readBool();
+	_specialLoopMode = f.readInt();
+	_specialLoop = f.readInt();
+	_nextSetId = f.readInt();
+	_nextSceneId = f.readInt();
+	_frame = f.readInt();
+	_actorStartPosition = f.readVector3();
+	_actorStartFacing = f.readInt();
+	_playerWalkedIn = f.readBool();
 }
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/scene.h b/engines/bladerunner/scene.h
index b1963e8..0403cd3 100644
--- a/engines/bladerunner/scene.h
+++ b/engines/bladerunner/scene.h
@@ -25,12 +25,15 @@
 
 #include "bladerunner/vector.h"
 
+#include "common/str.h"
+
 namespace BladeRunner {
 
 class BladeRunnerEngine;
 class BoundingBox;
 class Regions;
-class SaveFile;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 class Set;
 class VQAPlayer;
 
@@ -81,16 +84,17 @@ public:
 
 	bool didPlayerWalkIn() { bool r = _playerWalkedIn; _playerWalkedIn = false; return r; }
 
-	int findObject(const char *objectName);
+	int findObject(const Common::String &objectName);
 	bool objectSetHotMouse(int objectId);
 	bool objectGetBoundingBox(int objectId, BoundingBox *boundingBox);
 	void objectSetIsClickable(int objectId, bool isClickable, bool sceneLoaded);
 	void objectSetIsObstacle(int objectId, bool isObstacle, bool sceneLoaded, bool updateWalkpath);
 	void objectSetIsObstacleAll(bool isObstacle, bool sceneLoaded);
 	void objectSetIsTarget(int objectId, bool isTarget, bool sceneLoaded);
-	const char *objectGetName(int objectId);
+	const Common::String &objectGetName(int objectId);
 
-	void save(SaveFile &f);
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
 
 private:
 	void loopEnded(int frame, int loopId);
diff --git a/engines/bladerunner/scene_objects.cpp b/engines/bladerunner/scene_objects.cpp
index f68dccc..6857416 100644
--- a/engines/bladerunner/scene_objects.cpp
+++ b/engines/bladerunner/scene_objects.cpp
@@ -65,16 +65,16 @@ void SceneObjects::clear() {
 	_count = 0;
 }
 
-bool SceneObjects::addActor(int sceneObjectId, BoundingBox *boundingBox, Common::Rect *screenRectangle, bool isClickable, bool isMoving, bool isTarget, bool isRetired) {
+bool SceneObjects::addActor(int sceneObjectId, const BoundingBox &boundingBox, const Common::Rect &screenRectangle, bool isClickable, bool isMoving, bool isTarget, bool isRetired) {
 	return addSceneObject(sceneObjectId, kSceneObjectTypeActor, boundingBox, screenRectangle, isClickable, false, 0, isTarget, isMoving, isRetired);
 }
 
-bool SceneObjects::addObject(int sceneObjectId, BoundingBox *boundingBox, bool isClickable, bool isObstacle, uint8 unknown1, bool isTarget) {
+bool SceneObjects::addObject(int sceneObjectId, const BoundingBox &boundingBox, bool isClickable, bool isObstacle, uint8 unknown1, bool isTarget) {
 	Common::Rect rect(-1, -1, -1, -1);
-	return addSceneObject(sceneObjectId, kSceneObjectTypeObject, boundingBox, &rect, isClickable, isObstacle, unknown1, isTarget, false, false);
+	return addSceneObject(sceneObjectId, kSceneObjectTypeObject, boundingBox, rect, isClickable, isObstacle, unknown1, isTarget, false, false);
 }
 
-bool SceneObjects::addItem(int sceneObjectId, BoundingBox *boundingBox, Common::Rect *screenRectangle, bool isTarget, bool isObstacle) {
+bool SceneObjects::addItem(int sceneObjectId, const BoundingBox &boundingBox, const Common::Rect &screenRectangle, bool isTarget, bool isObstacle) {
 	return addSceneObject(sceneObjectId, kSceneObjectTypeItem, boundingBox, screenRectangle, isObstacle, 0, 0, isTarget, 0, 0);
 }
 
@@ -111,7 +111,7 @@ int SceneObjects::findByXYZ(bool *isClickable, bool *isObstacle, bool *isTarget,
 		if ((findClickables && sceneObject->isClickable) ||
 			(findObstacles  && sceneObject->isObstacle) ||
 			(findTargets    && sceneObject->isTarget)) {
-			BoundingBox boundingBox = *sceneObject->boundingBox;
+			BoundingBox boundingBox = sceneObject->boundingBox;
 
 			if (sceneObject->type == kSceneObjectTypeActor) {
 				boundingBox.expand(-4.0, 0.0, -4.0, 4.0, 0.0, 4.0);
@@ -156,7 +156,7 @@ bool SceneObjects::existsOnXZ(int exceptSceneObjectId, float x, float z, bool mo
 
 			if (isObstacle && sceneObject->id != exceptSceneObjectId) {
 				float x1, y1, z1, x2, y2, z2;
-				sceneObject->boundingBox->getXYZ(&x1, &y1, &z1, &x2, &y2, &z2);
+				sceneObject->boundingBox.getXYZ(&x1, &y1, &z1, &x2, &y2, &z2);
 				if (z1 <= zMax && z2 >= zMin && x1 <= xMax && x2 >= xMin) {
 					return true;
 				}
@@ -177,7 +177,7 @@ int SceneObjects::findById(int sceneObjectId) const {
 	return -1;
 }
 
-bool SceneObjects::addSceneObject(int sceneObjectId, SceneObjectType sceneObjectType, BoundingBox *boundingBox, Common::Rect *screenRectangle, bool isClickable, bool isObstacle, uint8 unknown1, bool isTarget, bool isMoving, bool isRetired) {
+bool SceneObjects::addSceneObject(int sceneObjectId, SceneObjectType sceneObjectType, const BoundingBox &boundingBox, const Common::Rect &screenRectangle, bool isClickable, bool isObstacle, uint8 unknown1, bool isTarget, bool isMoving, bool isRetired) {
 	int index = findEmpty();
 	if (index == -1) {
 		return false;
@@ -195,7 +195,7 @@ bool SceneObjects::addSceneObject(int sceneObjectId, SceneObjectType sceneObject
 	_sceneObjects[index].isMoving        = isMoving;
 	_sceneObjects[index].isRetired       = isRetired;
 
-	float centerZ = (_sceneObjects[index].boundingBox->getZ0() + _sceneObjects[index].boundingBox->getZ1()) / 2.0f;
+	float centerZ = (_sceneObjects[index].boundingBox.getZ0() + _sceneObjects[index].boundingBox.getZ1()) / 2.0f;
 
 	float distanceToCamera = fabs(-centerZ - _view->_cameraPosition.y); // y<->z is intentional, not a bug
 	_sceneObjects[index].distanceToCamera = distanceToCamera;
@@ -247,7 +247,7 @@ bool SceneObjects::isBetween(float sourceX, float sourceZ, float targetX, float
 	}
 
 	float objectX1, objectY1, objectZ1, objectX2, objectY2, objectZ2;
-	_sceneObjects[i].boundingBox->getXYZ(&objectX1, &objectY1, &objectZ1, &objectX2, &objectY2, &objectZ2);
+	_sceneObjects[i].boundingBox.getXYZ(&objectX1, &objectY1, &objectZ1, &objectX2, &objectY2, &objectZ2);
 
 	Vector2 intersection;
 	return lineIntersection(Vector2(sourceX, sourceZ), Vector2(targetX, targetZ), Vector2(objectX1, objectZ1), Vector2(objectX2, objectZ1), &intersection)
@@ -265,7 +265,7 @@ bool SceneObjects::isObstacleBetween(const Vector3 &source, const Vector3 &targe
 		}
 
 		float objectX1, objectY1, objectZ1, objectX2, objectY2, objectZ2;
-		sceneObject->boundingBox->getXYZ(&objectX1, &objectY1, &objectZ1, &objectX2, &objectY2, &objectZ2);
+		sceneObject->boundingBox.getXYZ(&objectX1, &objectY1, &objectZ1, &objectX2, &objectY2, &objectZ2);
 
 		if (84.0f <= objectY1 - source.y || 72.0f >= objectY2 - source.y) {
 			continue;
@@ -321,31 +321,52 @@ void SceneObjects::updateObstacles() {
 		const SceneObject *sceneObject = &_sceneObjects[index];
 		if (sceneObject->isObstacle) {
 			float x0, y0, z0, x1, y1, z1;
-			sceneObject->boundingBox->getXYZ(&x0, &y0, &z0, &x1, &y1, &z1);
+			sceneObject->boundingBox.getXYZ(&x0, &y0, &z0, &x1, &y1, &z1);
 			_vm->_obstacles->add(x0, z0, x1, z1);
 		}
 	}
 	_vm->_obstacles->backup();
 }
 
-void SceneObjects::save(SaveFile &f) {
-	f.write(_count);
+void SceneObjects::save(SaveFileWriteStream &f) {
+	f.writeInt(_count);
 	for (int i = 0; i < kSceneObjectCount; ++i) {
-		f.write(_sceneObjects[i].id);
-		f.write(_sceneObjects[i].type);
-		f.write(_sceneObjects[i].boundingBox);
-		f.write(_sceneObjects[i].screenRectangle);
-		f.write(_sceneObjects[i].distanceToCamera);
-		f.write(_sceneObjects[i].isPresent);
-		f.write(_sceneObjects[i].isClickable);
-		f.write(_sceneObjects[i].isObstacle);
-		f.write(_sceneObjects[i].unknown1);
-		f.write(_sceneObjects[i].isTarget);
-		f.write(_sceneObjects[i].isMoving);
-		f.write(_sceneObjects[i].isRetired);
+		f.writeInt(_sceneObjects[i].id);
+		f.writeInt(_sceneObjects[i].type);
+		f.writeBoundingBox(_sceneObjects[i].boundingBox);
+		f.writeRect(_sceneObjects[i].screenRectangle);
+		f.writeFloat(_sceneObjects[i].distanceToCamera);
+		f.writeBool(_sceneObjects[i].isPresent);
+		f.writeBool(_sceneObjects[i].isClickable);
+		f.writeBool(_sceneObjects[i].isObstacle);
+		f.writeInt(_sceneObjects[i].unknown1);
+		f.writeBool(_sceneObjects[i].isTarget);
+		f.writeBool(_sceneObjects[i].isMoving);
+		f.writeBool(_sceneObjects[i].isRetired);
 	}
 	for (int i = 0; i < kSceneObjectCount; ++i) {
-		f.write(_sceneObjectsSortedByDistance[i]);
+		f.writeInt(_sceneObjectsSortedByDistance[i]);
+	}
+}
+
+void SceneObjects::load(SaveFileReadStream &f) {
+	_count = f.readInt();
+	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].screenRectangle = f.readRect();
+		_sceneObjects[i].distanceToCamera = f.readFloat();
+		_sceneObjects[i].isPresent = f.readBool();
+		_sceneObjects[i].isClickable = f.readBool();
+		_sceneObjects[i].isObstacle = f.readBool();
+		_sceneObjects[i].unknown1 = f.readInt();
+		_sceneObjects[i].isTarget = f.readBool();
+		_sceneObjects[i].isMoving = f.readBool();
+		_sceneObjects[i].isRetired = f.readBool();
+	}
+	for (int i = 0; i < kSceneObjectCount; ++i) {
+		_sceneObjectsSortedByDistance[i] = f.readInt();
 	}
 }
 
diff --git a/engines/bladerunner/scene_objects.h b/engines/bladerunner/scene_objects.h
index 2f154db..a6d5520 100644
--- a/engines/bladerunner/scene_objects.h
+++ b/engines/bladerunner/scene_objects.h
@@ -30,7 +30,8 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
-class SaveFile;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 class View;
 
 enum SceneObjectType {
@@ -46,18 +47,18 @@ class SceneObjects {
 	static const int kSceneObjectCount = 115;
 
 	struct SceneObject {
-		int                 id;
-		SceneObjectType     type;
-		const BoundingBox  *boundingBox;
-		const Common::Rect *screenRectangle;
-		float               distanceToCamera;
-		bool                isPresent;
-		bool                isClickable;
-		bool                isObstacle;
-		int                 unknown1;
-		bool                isTarget;
-		bool                isMoving;
-		bool                isRetired;
+		int             id;
+		SceneObjectType type;
+		BoundingBox     boundingBox;
+		Common::Rect    screenRectangle;
+		float           distanceToCamera;
+		bool            isPresent;
+		bool            isClickable;
+		bool            isObstacle;
+		int             unknown1;
+		bool            isTarget;
+		bool            isMoving;
+		bool            isRetired;
 	};
 
 	BladeRunnerEngine *_vm;
@@ -71,9 +72,9 @@ public:
 	SceneObjects(BladeRunnerEngine *vm, View *view);
 	~SceneObjects();
 
-	bool addActor(int sceneObjectId, BoundingBox *boundingBox, Common::Rect *screenRectangle, bool isClickable, bool isMoving, bool isTarget, bool isRetired);
-	bool addObject(int sceneObjectId, BoundingBox *boundingBox, bool isClickable, bool isObstacle, uint8 unknown1, bool isTarget);
-	bool addItem(int sceneObjectId, BoundingBox *boundingBox, Common::Rect *screenRectangle, bool isTarget, bool isObstacle);
+	bool addActor(int sceneObjectId, const BoundingBox &boundingBox, const Common::Rect &screenRectangle, bool isClickable, bool isMoving, bool isTarget, bool isRetired);
+	bool addObject(int sceneObjectId, const BoundingBox &boundingBox, bool isClickable, bool isObstacle, uint8 unknown1, bool isTarget);
+	bool addItem(int sceneObjectId, const BoundingBox &boundingBox, const Common::Rect &screenRectangle, bool isTarget, bool isObstacle);
 	bool remove(int sceneObjectId);
 	void clear();
 	int findByXYZ(bool *isClickable, bool *isObstacle, bool *isTarget, Vector3 &position, bool findClickables, bool findObstacles, bool findTargets) const;
@@ -87,10 +88,12 @@ public:
 	void setIsTarget(int sceneObjectId, bool isTarget);
 	void updateObstacles();
 
-	void save(SaveFile &f);
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
+
 private:
 	int findById(int sceneObjectId) const;
-	bool addSceneObject(int sceneObjectId, SceneObjectType sceneObjectType, BoundingBox *boundingBox, Common::Rect *screenRectangle, bool isClickable, bool isObstacle, uint8 unknown1, bool isTarget, bool isMoving, bool isRetired);
+	bool addSceneObject(int sceneObjectId, SceneObjectType sceneObjectType, const BoundingBox &boundingBox, const Common::Rect &screenRectangle, bool isClickable, bool isObstacle, uint8 unknown1, bool isTarget, bool isMoving, bool isRetired);
 	int findEmpty() const;
 };
 
diff --git a/engines/bladerunner/script/ai_script.cpp b/engines/bladerunner/script/ai_script.cpp
index 257e323..b328824 100644
--- a/engines/bladerunner/script/ai_script.cpp
+++ b/engines/bladerunner/script/ai_script.cpp
@@ -22,9 +22,8 @@
 
 #include "bladerunner/script/ai_script.h"
 
-#include "bladerunner/bladerunner.h"
-
 #include "bladerunner/actor.h"
+#include "bladerunner/bladerunner.h"
 
 namespace BladeRunner {
 
@@ -358,7 +357,7 @@ void AIScripts::setAnimationState(int actor, int animationState, int animationFr
 }
 
 
-void AIScripts::queryAnimationState(int actor, int *animationState, int *animationFrame, int *animationStateNext, int *nextAnimation) {
+void AIScripts::queryAnimationState(int actor, int *animationState, int *animationFrame, int *animationStateNext, int *animationNext) {
 		if (actor >= _actorCount) {
 		return;
 	}
@@ -366,7 +365,7 @@ void AIScripts::queryAnimationState(int actor, int *animationState, int *animati
 	_inScriptCounter++;
 	if (_AIScripts[actor]) {
 		_AIScripts[actor]->FledCombat();
-		_AIScripts[actor]->QueryAnimationState(animationState, animationFrame, animationStateNext, nextAnimation);
+		_AIScripts[actor]->QueryAnimationState(animationState, animationFrame, animationStateNext, animationNext);
 	}
 	_inScriptCounter--;
 }
diff --git a/engines/bladerunner/script/ai_script.h b/engines/bladerunner/script/ai_script.h
index 5e782d0..03789ae 100644
--- a/engines/bladerunner/script/ai_script.h
+++ b/engines/bladerunner/script/ai_script.h
@@ -61,7 +61,7 @@ public:
 	virtual bool GoalChanged(int currentGoalNumber, int newGoalNumber) = 0;
 	virtual bool UpdateAnimation(int *animation, int *frame) = 0;
 	virtual bool ChangeAnimationMode(int mode) = 0;
-	virtual void QueryAnimationState(int *animationState, int *animationFrame, int *animationStateNext, int *nextAnimation) = 0;
+	virtual void QueryAnimationState(int *animationState, int *animationFrame, int *animationStateNext, int *animationNext) = 0;
 	virtual void SetAnimationState(int animationState, int animationFrame, int animationStateNext, int animationNext) = 0;
 	virtual bool ReachedMovementTrackWaypoint(int waypointId) = 0;
 	virtual void FledCombat() = 0;
@@ -566,7 +566,7 @@ public:
 	bool reachedMovementTrackWaypoint(int actor, int waypointId);
 	void updateAnimation(int actor, int *animation, int *frame);
 	void changeAnimationMode(int actor, int mode);
-	void queryAnimationState(int actor, int *animationState, int *animationFrame, int *animationStateNext, int *nextAnimation);
+	void queryAnimationState(int actor, int *animationState, int *animationFrame, int *animationStateNext, int *animationNext);
 	void setAnimationState(int actor, int animationState, int animationFrame, int animationStateNext, int animationNext);
 	void fledCombat(int actor);
 
diff --git a/engines/bladerunner/script/police_maze.cpp b/engines/bladerunner/script/police_maze.cpp
index 3aa3ebb..68bb84a 100644
--- a/engines/bladerunner/script/police_maze.cpp
+++ b/engines/bladerunner/script/police_maze.cpp
@@ -24,6 +24,7 @@
 #include "bladerunner/game_constants.h"
 #include "bladerunner/items.h"
 #include "bladerunner/mouse.h"
+#include "bladerunner/savefile.h"
 #include "bladerunner/scene.h"
 #include "bladerunner/scene_objects.h"
 #include "bladerunner/script/police_maze.h"
@@ -122,6 +123,24 @@ void PoliceMaze::tick() {
 	}
 }
 
+void PoliceMaze::save(SaveFileWriteStream &f) {
+	f.writeBool(_isPaused);
+	f.writeBool(_isActive);
+	f.writeBool(_isEnding);
+	for (int i = 0; i < kNumMazeTracks; ++i) {
+		_tracks[i]->save(f);
+	}
+}
+
+void PoliceMaze::load(SaveFileReadStream &f) {
+	_isPaused = f.readBool();
+	_isActive = f.readBool();
+	_isEnding = f.readBool();
+	for (int i = 0; i < kNumMazeTracks; ++i) {
+		_tracks[i]->load(f);
+	}
+}
+
 PoliceMazeTargetTrack::PoliceMazeTargetTrack(BladeRunnerEngine *vm) : ScriptBase(vm) {
 	reset();
 }
@@ -139,7 +158,7 @@ void PoliceMazeTargetTrack::reset() {
 	_timeLeftUpdate = 0;
 	_timeLeftWait   = 0;
 	_time           = 0;
-	_isWaiting     = false;
+	_isWaiting      = false;
 	_isMoving       = false;
 	_pointIndex     = 0;
 	_pointTarget    = 0;
@@ -528,8 +547,8 @@ bool PoliceMazeTargetTrack::tick() {
 
 void PoliceMazeTargetTrack::readdObject(int itemId) {
 	if (_vm->_sceneObjects->remove(itemId + kSceneObjectOffsetItems)) {
-		BoundingBox *boundingBox = _vm->_items->getBoundingBox(itemId);
-		Common::Rect *screenRect = _vm->_items->getScreenRectangle(itemId);
+		const BoundingBox &boundingBox = _vm->_items->getBoundingBox(itemId);
+		const Common::Rect &screenRect = _vm->_items->getScreenRectangle(itemId);
 		bool targetable = _vm->_items->isTarget(itemId);
 		bool obstacle = _vm->_items->isVisible(itemId);
 
@@ -537,5 +556,48 @@ void PoliceMazeTargetTrack::readdObject(int itemId) {
 	}
 }
 
+void PoliceMazeTargetTrack::save(SaveFileWriteStream &f) {
+	f.writeBool(_isPresent);
+	f.writeInt(_itemId);
+	f.writeInt(_pointCount);
+	f.writeInt(_dataIndex);
+	f.writeBool(_isWaiting);
+	f.writeBool(_isMoving);
+	f.writeInt(_pointIndex);
+	f.writeInt(_pointTarget);
+	f.writeBool(_isRotating);
+	f.writeInt(_angleTarget);
+	f.writeInt(_angleDelta);
+	f.writeBool(_isPaused);
+
+	for (int i = 0; i < kNumTrackPoints; ++i) {
+		f.writeVector3(_points[i]);
+	}
+
+	f.writeInt(_timeLeftUpdate);
+	f.writeInt(_timeLeftWait);
+}
+
+void PoliceMazeTargetTrack::load(SaveFileReadStream &f) {
+	_isPresent = f.readBool();
+	_itemId = f.readInt();
+	_pointCount = f.readInt();
+	_dataIndex = f.readInt();
+	_isWaiting = f.readBool();
+	_isMoving = f.readBool();
+	_pointIndex = f.readInt();
+	_pointTarget = f.readInt();
+	_isRotating = f.readBool();
+	_angleTarget = f.readInt();
+	_angleDelta = f.readInt();
+	_isPaused = f.readBool();
+
+	for (int i = 0; i < kNumTrackPoints; ++i) {
+		_points[i] = f.readVector3();
+	}
+
+	_timeLeftUpdate = f.readInt();
+	_timeLeftWait = f.readInt();
+}
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/script/police_maze.h b/engines/bladerunner/script/police_maze.h
index e53d68e..725fbd4 100644
--- a/engines/bladerunner/script/police_maze.h
+++ b/engines/bladerunner/script/police_maze.h
@@ -34,25 +34,27 @@ enum {
 };
 
 class BladeRunnerEngine;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 
 class PoliceMazeTargetTrack : ScriptBase {
-	uint32  _time;
-	bool    _isPresent;
-	int     _itemId;
-	int     _pointCount;
-	Vector3 _points[kNumTrackPoints];
+	uint32     _time;
+	bool       _isPresent;
+	int        _itemId;
+	int        _pointCount;
+	Vector3    _points[kNumTrackPoints];
 	const int *_data;
-	int     _dataIndex;
-	int32   _timeLeftUpdate;
-	int32   _timeLeftWait;
-	bool    _isWaiting;
-	int     _isMoving;
-	int     _pointIndex;
-	int     _pointTarget;
-	bool    _isRotating;
-	int     _angleTarget;
-	int     _angleDelta;
-	bool    _isPaused;
+	int        _dataIndex;
+	int32      _timeLeftUpdate;
+	int32      _timeLeftWait;
+	bool       _isWaiting;
+	int        _isMoving;
+	int        _pointIndex;
+	int        _pointTarget;
+	bool       _isRotating;
+	int        _angleTarget;
+	int        _angleDelta;
+	bool       _isPaused;
 
 public:
 	PoliceMazeTargetTrack(BladeRunnerEngine *vm);
@@ -70,6 +72,9 @@ public:
 	void setTime(uint32 t) { _time = t; }
 
 	void readdObject(int itemId);
+
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
 };
 
 class PoliceMaze : ScriptBase {
@@ -91,6 +96,9 @@ public:
 	void clear(bool isLoadingGame);
 	void setPauseState(bool state);
 	void activate();
+
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/script/script.cpp b/engines/bladerunner/script/script.cpp
index 6c7d8f5..6d981fe 100644
--- a/engines/bladerunner/script/script.cpp
+++ b/engines/bladerunner/script/script.cpp
@@ -832,8 +832,7 @@ int ScriptBase::Random_Query(int min, int max) {
 }
 
 void ScriptBase::Sound_Play(int id, int volume, int panFrom, int panTo, int priority) {
-	const char *name = _vm->_gameInfo->getSfxTrack(id);
-	_vm->_audioPlayer->playAud(name, volume, panFrom, panTo, priority);
+	_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(id), volume, panFrom, panTo, priority);
 }
 
 void ScriptBase::Sound_Play_Speech_Line(int actorId, int sentenceId, int volume, int a4, int priority) {
@@ -899,8 +898,7 @@ void ScriptBase::Footstep_Sound_Override_Off() {
 }
 
 bool ScriptBase::Music_Play(int musicId, int volume, int pan, int timeFadeIn, int timePlay, int loop, int timeFadeOut) {
-	const char *musicName = _vm->_gameInfo->getMusicTrack(musicId);
-	return _vm->_music->play(musicName, volume, pan, timeFadeIn, timePlay, loop, timeFadeOut);
+	return _vm->_music->play(_vm->_gameInfo->getMusicTrack(musicId), volume, pan, timeFadeIn, timePlay, loop, timeFadeOut);
 }
 
 void ScriptBase::Music_Adjust(int volume, int pan, int delay) {
diff --git a/engines/bladerunner/script/script.h b/engines/bladerunner/script/script.h
index 5e1e160..09c372d 100644
--- a/engines/bladerunner/script/script.h
+++ b/engines/bladerunner/script/script.h
@@ -23,11 +23,11 @@
 #ifndef BLADERUNNER_SCRIPT_H
 #define BLADERUNNER_SCRIPT_H
 
-#include "common/str.h"
-
 #include "bladerunner/bladerunner.h"
 #include "bladerunner/game_constants.h"
 
+#include "common/str.h"
+
 namespace BladeRunner {
 
 class BladeRunnerEngine;
diff --git a/engines/bladerunner/set.cpp b/engines/bladerunner/set.cpp
index 6a83294..ac026a4 100644
--- a/engines/bladerunner/set.cpp
+++ b/engines/bladerunner/set.cpp
@@ -68,8 +68,10 @@ bool Set::open(const Common::String &name) {
 	_objectCount = s->readUint32LE();
 	assert(_objectCount <= 85);
 
+	char buf[20];
 	for (int i = 0; i < _objectCount; ++i) {
-		s->read(_objects[i].name, 20);
+		s->read(buf, sizeof(buf));
+		_objects[i].name = buf;
 
 		float x0, y0, z0, x1, y1, z1;
 		x0 = s->readFloatLE();
@@ -96,7 +98,9 @@ bool Set::open(const Common::String &name) {
 	for (int i = 0; i < _walkboxCount; ++i) {
 		float x, z;
 
-		s->read(_walkboxes[i].name, 20);
+		s->read(buf, sizeof(buf));
+		_walkboxes[i].name = buf;
+
 		_walkboxes[i].altitude = s->readFloatLE();
 		_walkboxes[i].vertexCount = s->readUint32LE();
 
@@ -131,7 +135,7 @@ bool Set::open(const Common::String &name) {
 
 void Set::addObjectsToScene(SceneObjects *sceneObjects) const {
 	for (int i = 0; i < _objectCount; i++) {
-		sceneObjects->addObject(i + kSceneObjectOffsetObjects, &_objects[i].bbox, _objects[i].isClickable, _objects[i].isObstacle, _objects[i].unknown1, _objects[i].isTarget);
+		sceneObjects->addObject(i + kSceneObjectOffsetObjects, _objects[i].bbox, _objects[i].isClickable, _objects[i].isObstacle, _objects[i].unknown1, _objects[i].isTarget);
 	}
 }
 
@@ -209,15 +213,14 @@ int Set::findWalkbox(float x, float z) const {
 	return result;
 }
 
-int Set::findObject(const char *objectName) const {
-	int i;
-	for (i = 0; i < (int)_objectCount; i++) {
-		if (scumm_stricmp(objectName, _objects[i].name) == 0) {
+int Set::findObject(const Common::String &objectName) const {
+	for (int i = 0; i < _objectCount; ++i) {
+		if (objectName.compareToIgnoreCase(_objects[i].name) == 0) {
 			return i;
 		}
 	}
 
-	debug("Set::findObject didn't find \"%s\"", objectName);
+	debug("Set::findObject didn't find \"%s\"", objectName.c_str());
 
 	return -1;
 }
@@ -258,7 +261,7 @@ void Set::objectSetIsTarget(int objectId, bool isTarget) {
 	_objects[objectId].isTarget = isTarget;
 }
 
-const char *Set::objectGetName(int objectId) const {
+const Common::String &Set::objectGetName(int objectId) const {
 	return _objects[objectId].name;
 }
 
@@ -330,39 +333,73 @@ int Set::getWalkboxSoundRunRight(int walkboxId) const {
 	return getWalkboxSoundWalkRight(walkboxId);
 }
 
-void Set::save(SaveFile &f) {
-	f.write(_loaded);
-	f.write(_objectCount);
-	f.write(_walkboxCount);
+void Set::save(SaveFileWriteStream &f) {
+	f.writeBool(_loaded);
+	f.writeInt(_objectCount);
+	f.writeInt(_walkboxCount);
+
+	for (int i = 0; i != _objectCount; ++i) {
+		f.writeStringSz(_objects[i].name, 20);
+		f.writeBoundingBox(_objects[i].bbox);
+		f.writeBool(_objects[i].isObstacle);
+		f.writeBool(_objects[i].isClickable);
+		f.writeBool(_objects[i].isHotMouse);
+		f.writeInt(_objects[i].unknown1);
+		f.writeBool(_objects[i].isTarget);
+	}
+
+	for (int i = 0; i != _walkboxCount; ++i) {
+		f.writeStringSz(_walkboxes[i].name, 20);
+		f.writeFloat(_walkboxes[i].altitude);
+		f.writeInt(_walkboxes[i].vertexCount);
+		for (int j = 0; j != 8; ++j) {
+			f.writeVector3(_walkboxes[i].vertices[j]);
+
+			// In BLADE.EXE vertices are a vec5
+			f.writeInt(0);
+			f.writeInt(0);
+		}
+	}
+
+	for (int i = 0; i != 85; ++i) {
+		f.writeInt(_walkboxStepSound[i]);
+	}
+
+	f.writeInt(_footstepSoundOverride);
+}
+
+void Set::load(SaveFileReadStream &f) {
+	_loaded = f.readBool();
+	_objectCount = f.readInt();
+	_walkboxCount = f.readInt();
 
 	for (int i = 0; i != _objectCount; ++i) {
-		f.write(_objects[i].name, 20);
-		f.write(_objects[i].bbox);
-		f.write(_objects[i].isObstacle);
-		f.write(_objects[i].isClickable);
-		f.write(_objects[i].isHotMouse);
-		f.write(_objects[i].unknown1);
-		f.write(_objects[i].isTarget);
+		_objects[i].name = f.readStringSz(20);
+		_objects[i].bbox = f.readBoundingBox();
+		_objects[i].isObstacle = f.readBool();
+		_objects[i].isClickable = f.readBool();
+		_objects[i].isHotMouse = f.readBool();
+		_objects[i].unknown1 = f.readInt();
+		_objects[i].isTarget = f.readBool();
 	}
 
 	for (int i = 0; i != _walkboxCount; ++i) {
-		f.write(_walkboxes[i].name, 20);
-		f.write(_walkboxes[i].altitude);
-		f.write(_walkboxes[i].vertexCount);
+		_walkboxes[i].name = f.readStringSz(20);
+		_walkboxes[i].altitude = f.readFloat();
+		_walkboxes[i].vertexCount = f.readInt();
 		for (int j = 0; j != 8; ++j) {
-			f.write(_walkboxes[i].vertices[j]);
+			_walkboxes[i].vertices[j] = f.readVector3();
 
 			// In BLADE.EXE vertices are a vec5
-			f.write(0);
-			f.write(0);
+			f.skip(8);
 		}
 	}
 
 	for (int i = 0; i != 85; ++i) {
-		f.write(_walkboxStepSound[i]);
+		_walkboxStepSound[i] = f.readInt();
 	}
 
-	f.write(_footstepSoundOverride);
+	_footstepSoundOverride = f.readInt();
 }
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/set.h b/engines/bladerunner/set.h
index 7989ef1..c6c1081 100644
--- a/engines/bladerunner/set.h
+++ b/engines/bladerunner/set.h
@@ -32,29 +32,30 @@ namespace BladeRunner {
 
 class BladeRunnerEngine;
 
-class VQADecoder;
-class SaveFile;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 class SetEffects;
 class SceneObjects;
+class VQADecoder;
 
 class Set {
 	friend class Debugger;
 
 	struct Object {
-		char        name[20];
-		BoundingBox bbox;
-		uint8       isObstacle;
-		uint8       isClickable;
-		uint8       isHotMouse;
-		uint8       isTarget;
-		uint8       unknown1;
+		Common::String name;
+		BoundingBox    bbox;
+		uint8          isObstacle;
+		uint8          isClickable;
+		uint8          isHotMouse;
+		uint8          isTarget;
+		uint8          unknown1;
 	};
 
 	struct Walkbox {
-		char    name[20];
-		float   altitude;
-		int     vertexCount;
-		Vector3 vertices[8];
+		Common::String name;
+		float          altitude;
+		int            vertexCount;
+		Vector3        vertices[8];
 	};
 
 	BladeRunnerEngine *_vm;
@@ -83,14 +84,14 @@ public:
 	float getAltitudeAtXZ(float x, float z, bool *inWalkbox) const;
 
 	int findWalkbox(float x, float z) const;
-	int findObject(const char *objectName) const;
+	int findObject(const Common::String &objectName) const;
 
 	bool objectSetHotMouse(int objectId) const;
 	bool objectGetBoundingBox(int objectId, BoundingBox *boundingBox) const;
 	void objectSetIsClickable(int objectId, bool isClickable);
 	void objectSetIsObstacle(int objectId, bool isObstacle);
 	void objectSetIsTarget(int objectId, bool isTarget);
-	const char *objectGetName(int objectId) const;
+	const Common::String &objectGetName(int objectId) const;
 
 	void setWalkboxStepSound(int walkboxId, int soundId);
 	void setFoodstepSoundOverride(int soundId);
@@ -101,7 +102,8 @@ public:
 	int getWalkboxSoundRunLeft(int walkboxId) const;
 	int getWalkboxSoundRunRight(int walkboxId) const;
 
-	void save(SaveFile &f);
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
 
 private:
 	static bool isXZInWalkbox(float x, float z, const Walkbox &walkbox);
diff --git a/engines/bladerunner/set_effects.cpp b/engines/bladerunner/set_effects.cpp
index 5c7b248..3d46e41 100644
--- a/engines/bladerunner/set_effects.cpp
+++ b/engines/bladerunner/set_effects.cpp
@@ -104,7 +104,7 @@ void SetEffects::setFadeDensity(float density) {
 	_fadeDensity = density;
 }
 
-void SetEffects::setFogColor(const char *fogName, float r, float g, float b) {
+void SetEffects::setFogColor(const Common::String &fogName, float r, float g, float b) {
 	Fog *fog = findFog(fogName);
 	if (fog == nullptr) {
 		return;
@@ -115,7 +115,7 @@ void SetEffects::setFogColor(const char *fogName, float r, float g, float b) {
 	fog->_fogColor.b = b;
 }
 
-void SetEffects::setFogDensity(const char *fogName, float density) {
+void SetEffects::setFogDensity(const Common::String &fogName, float density) {
 	Fog *fog = findFog(fogName);
 	if (fog == nullptr) {
 		return;
@@ -151,7 +151,7 @@ void SetEffects::calculateColor(Vector3 viewPosition, Vector3 position, float *o
 	outColor->b = outColor->b * (1.0f - _fadeDensity) + _fadeColor.b * _fadeDensity;
 }
 
-Fog *SetEffects::findFog(const char *fogName) const {
+Fog *SetEffects::findFog(const Common::String &fogName) const {
 	if (!_fogs) {
 		return nullptr;
 	}
@@ -159,7 +159,7 @@ Fog *SetEffects::findFog(const char *fogName) const {
 	Fog *fog = _fogs;
 
 	while (fog != nullptr) {
-		if (strcmp(fogName, fog->_name) == 0) {
+		if (fogName.compareTo(fog->_name) == 0) {
 			break;
 		}
 		fog = fog->_next;
diff --git a/engines/bladerunner/set_effects.h b/engines/bladerunner/set_effects.h
index 81d7b93..6bd139c 100644
--- a/engines/bladerunner/set_effects.h
+++ b/engines/bladerunner/set_effects.h
@@ -53,13 +53,13 @@ public:
 
 	void setFadeColor(float r, float g, float b);
 	void setFadeDensity(float density);
-	void setFogColor(const char *fogName, float r, float g, float b);
-	void setFogDensity(const char *fogName, float density);
+	void setFogColor(const Common::String &fogName, float r, float g, float b);
+	void setFogDensity(const Common::String &fogName, float density);
 
 	void calculateColor(Vector3 viewPosition, Vector3 position, float *outCoeficient, Color *outColor) const;
 
 private:
-	Fog *findFog(const char *fogName) const;
+	Fog *findFog(const Common::String &fogName) const;
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/settings.cpp b/engines/bladerunner/settings.cpp
index e4db0b8..071adf6 100644
--- a/engines/bladerunner/settings.cpp
+++ b/engines/bladerunner/settings.cpp
@@ -22,9 +22,12 @@
 
 #include "bladerunner/settings.h"
 
+#include "bladerunner/actor.h"
 #include "bladerunner/ambient_sounds.h"
 #include "bladerunner/bladerunner.h"
 #include "bladerunner/chapters.h"
+#include "bladerunner/game_constants.h"
+#include "bladerunner/game_info.h"
 #include "bladerunner/music.h"
 #include "bladerunner/savefile.h"
 #include "bladerunner/scene.h"
@@ -118,7 +121,19 @@ bool Settings::openNewScene() {
 	_scene = newScene;
 
 	if (!_loadingGame && currentSet != newSet) {
-		// TODO: Reset actors for new set
+		for (int i = 0; i < (int)_vm->_gameInfo->getActorCount(); ++i) {
+			Actor *actor = _vm->_actors[i];
+			if (i != kActorMcCoy && actor->getSetId() == currentSet) {
+				if (!actor->isRetired()) {
+					actor->stopWalking(false);
+					actor->movementTrackWaypointReached();
+				}
+				if (actor->inCombat()) {
+					actor->setSetId(kSetFreeSlotG);
+					actor->combatModeOff();
+				}
+			}
+		}
 	}
 
 	_loadingGame = false;
@@ -185,16 +200,29 @@ void Settings::setLearyMode(bool learyMode) {
 	_learyMode = learyMode;
 }
 
-void Settings::save(SaveFile &f) {
-	f.write(_scene);
-	f.write(_set);
-	f.write(_chapter);
-	f.write(_playerAgenda);
-	f.write(_unk0);
-	f.write(_difficulty);
-	f.write(_ammoType);
+void Settings::save(SaveFileWriteStream &f) {
+	f.writeInt(_scene);
+	f.writeInt(_set);
+	f.writeInt(_chapter);
+	f.writeInt(_playerAgenda);
+	f.writeInt(_unk0);
+	f.writeInt(_difficulty);
+	f.writeInt(_ammoType);
+	for (int i = 0; i != 3; ++i) {
+		f.writeInt(_ammoAmounts[i]);
+	}
+}
+
+void Settings::load(SaveFileReadStream &f) {
+	_scene = f.readInt();
+	_set = f.readInt();
+	_chapter = f.readInt();
+	_playerAgenda = f.readInt();
+	_unk0 = f.readInt();
+	_difficulty = f.readInt();
+	_ammoType = f.readInt();
 	for (int i = 0; i != 3; ++i) {
-		f.write(_ammoAmounts[i]);
+		_ammoAmounts[i] = f.readInt();
 	}
 }
 
diff --git a/engines/bladerunner/settings.h b/engines/bladerunner/settings.h
index bc1c255..413786e 100644
--- a/engines/bladerunner/settings.h
+++ b/engines/bladerunner/settings.h
@@ -26,7 +26,8 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
-class SaveFile;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 
 enum PlayerAgenda {
 	kPlayerAgendaPolite = 0,
@@ -53,7 +54,7 @@ class Settings {
 	bool  _startingGame;
 	bool  _loadingGame;
 
-	int   _unk1;
+	// int   _unk1;
 
 	int   _fullHDFrames;
 	int   _mst3k;
@@ -91,6 +92,18 @@ public:
 		return _newSet;
 	}
 
+	int getScene() const {
+		return _scene;
+	}
+
+	int getSet() const {
+		return _set;
+	}
+
+	int getChapter() const {
+		return _chapter;
+	}
+
 	void setChapter(int newChapter) {
 		_chapterChanged = true;
 		_newChapter = newChapter;
@@ -124,7 +137,8 @@ public:
 	bool getLearyMode() const;
 	void setLearyMode(bool learyMode);
 
-	void save(SaveFile &f);
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/text_resource.cpp b/engines/bladerunner/text_resource.cpp
index 2a4840c..8f54f8a 100644
--- a/engines/bladerunner/text_resource.cpp
+++ b/engines/bladerunner/text_resource.cpp
@@ -46,11 +46,11 @@ TextResource::~TextResource() {
 bool TextResource::open(const Common::String &name) {
 	assert(name.size() <= 8);
 
-	char resName[13];
-	sprintf(resName, "%s.TR%s", name.c_str(), _vm->_languageCode);
+	Common::String resName = Common::String::format("%s.TR%s", name.c_str(), _vm->_languageCode.c_str());
 	Common::ScopedPtr<Common::SeekableReadStream> s(_vm->getResourceStream(resName));
-	if (!s)
+	if (!s) {
 		return false;
+	}
 
 	_count = s->readUint32LE();
 
diff --git a/engines/bladerunner/ui/elevator.cpp b/engines/bladerunner/ui/elevator.cpp
index 3f668ef..7a6ab3c 100644
--- a/engines/bladerunner/ui/elevator.cpp
+++ b/engines/bladerunner/ui/elevator.cpp
@@ -315,8 +315,7 @@ void Elevator::mouseOutCallback(int, void *self) {
 
 void Elevator::mouseDownCallback(int, void *self) {
 	Elevator *elevator = ((Elevator *)self);
-	const char *name = elevator->_vm->_gameInfo->getSfxTrack(515);
-	elevator->_vm->_audioPlayer->playAud(name, 100, 0, 0, 50, 0);
+	elevator->_vm->_audioPlayer->playAud(elevator->_vm->_gameInfo->getSfxTrack(515), 100, 0, 0, 50, 0);
 }
 
 void Elevator::mouseUpCallback(int buttonId, void *self) {
diff --git a/engines/bladerunner/ui/kia.cpp b/engines/bladerunner/ui/kia.cpp
index 86fdc92..756f1bd 100644
--- a/engines/bladerunner/ui/kia.cpp
+++ b/engines/bladerunner/ui/kia.cpp
@@ -350,7 +350,7 @@ void KIA::tick() {
 			_shapes->get(47)->draw(_vm->_surfaceFront, 182, 446);
 		}
 	}
-	_vm->_mainFont->drawColor("1.00", _vm->_surfaceFront, 438, 471, 0x1CE7);
+	_vm->_mainFont->drawColor("1.00", _vm->_surfaceFront, 438, 471, 0x1CE7); // TODO: 1.01 for DVD version
 	if (!_transitionId) {
 		_buttons->drawTooltip(_vm->_surfaceFront, mouse.x, mouse.y);
 	}
diff --git a/engines/bladerunner/ui/kia_log.cpp b/engines/bladerunner/ui/kia_log.cpp
index 51b922a..7f75f2e 100644
--- a/engines/bladerunner/ui/kia_log.cpp
+++ b/engines/bladerunner/ui/kia_log.cpp
@@ -55,7 +55,7 @@ void KIALog::add(int type, int dataSize, const void *data) {
 	_entries[_currentIndex].dataSize = dataSize;
 
 	if (dataSize > 0) {
-		char *dataCopy = new char[dataSize];
+		unsigned char *dataCopy = new unsigned char[dataSize];
 		memcpy(dataCopy, data, dataSize);
 		_entries[_currentIndex].data = dataCopy;
 	} else {
diff --git a/engines/bladerunner/ui/kia_log.h b/engines/bladerunner/ui/kia_log.h
index 4a89492..99c834e 100644
--- a/engines/bladerunner/ui/kia_log.h
+++ b/engines/bladerunner/ui/kia_log.h
@@ -33,7 +33,7 @@ class KIALog {
 	struct Entry {
 		int type;
 		int dataSize;
-		const char *data;
+		const unsigned char *data;
 	};
 
 	BladeRunnerEngine *_vm;
diff --git a/engines/bladerunner/ui/spinner.cpp b/engines/bladerunner/ui/spinner.cpp
index 1f8f197..9e491f7 100644
--- a/engines/bladerunner/ui/spinner.cpp
+++ b/engines/bladerunner/ui/spinner.cpp
@@ -263,11 +263,17 @@ void Spinner::resume() {
 	_vqaPlayer->setLoop(1, -1, kLoopSetModeJustStart, nullptr, nullptr);
 }
 
-void Spinner::save(SaveFile &f) {
+void Spinner::save(SaveFileWriteStream &f) {
 	assert(!_isOpen);
 
 	for (int i = 0; i != kSpinnerDestinations; ++i) {
-		f.write(_isDestinationSelectable[i]);
+		f.writeBool(_isDestinationSelectable[i]);
+	}
+}
+
+void Spinner::load(SaveFileReadStream &f) {
+	for (int i = 0; i != kSpinnerDestinations; ++i) {
+		_isDestinationSelectable[i] = f.readBool();
 	}
 }
 
diff --git a/engines/bladerunner/ui/spinner.h b/engines/bladerunner/ui/spinner.h
index 24f66ed..d2d666e 100644
--- a/engines/bladerunner/ui/spinner.h
+++ b/engines/bladerunner/ui/spinner.h
@@ -29,7 +29,8 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
-class SaveFile;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 class Shape;
 class UIImagePicker;
 class VQAPlayer;
@@ -71,7 +72,8 @@ public:
 	void reset();
 	void resume();
 
-	void save(SaveFile &f);
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
 
 private:
 	static void mouseUpCallback(int, void *);
diff --git a/engines/bladerunner/ui/ui_scroll_box.cpp b/engines/bladerunner/ui/ui_scroll_box.cpp
index 6bd4dab..8621039 100644
--- a/engines/bladerunner/ui/ui_scroll_box.cpp
+++ b/engines/bladerunner/ui/ui_scroll_box.cpp
@@ -481,7 +481,7 @@ void UIScrollBox::draw(Graphics::Surface &surface) {
 			}
 
 			if (_center) {
-				x = (_rect.width() - _vm->_mainFont->getTextWidth(_lines[i]->text)) / 2;
+				x = _rect.left + (_rect.width() - _vm->_mainFont->getTextWidth(_lines[i]->text)) / 2;
 			}
 
 			_vm->_mainFont->drawColor(_lines[i]->text, surface, x, y, color);
diff --git a/engines/bladerunner/waypoints.cpp b/engines/bladerunner/waypoints.cpp
index 0783af8..bf2f09b 100644
--- a/engines/bladerunner/waypoints.cpp
+++ b/engines/bladerunner/waypoints.cpp
@@ -91,13 +91,23 @@ float Waypoints::getZ(int waypointId) const {
 	return _waypoints[waypointId].position.z;
 }
 
-void Waypoints::save(SaveFile &f) {
-	f.write(_count);
+void Waypoints::save(SaveFileWriteStream &f) {
+	f.writeInt(_count);
 	for (int i = 0; i < _count; ++i) {
 		Waypoint &w = _waypoints[i];
-		f.write(w.setId);
-		f.write(w.position);
-		f.write(w.present);
+		f.writeInt(w.setId);
+		f.writeVector3(w.position);
+		f.writeInt(w.present);
+	}
+}
+
+void Waypoints::load(SaveFileReadStream &f) {
+	_count = f.readInt();
+	for (int i = 0; i < _count; ++i) {
+		Waypoint &w = _waypoints[i];
+		w.setId = f.readInt();
+		w.position = f.readVector3();
+		w.present = f.readInt();
 	}
 }
 
diff --git a/engines/bladerunner/waypoints.h b/engines/bladerunner/waypoints.h
index 9159301..6bbe8d3 100644
--- a/engines/bladerunner/waypoints.h
+++ b/engines/bladerunner/waypoints.h
@@ -30,7 +30,8 @@
 
 namespace BladeRunner {
 
-class SaveFile;
+class SaveFileReadStream;
+class SaveFileWriteStream;
 
 class Waypoints {
 	friend class Debugger;
@@ -58,7 +59,8 @@ public:
 	bool set(int waypointId, int setId, Vector3 position);
 	bool reset(int waypointId);
 
-	void save(SaveFile &f);
+	void save(SaveFileWriteStream &f);
+	void load(SaveFileReadStream &f);
 };
 
 } // End of namespace BladeRunner





More information about the Scummvm-git-logs mailing list