From a5fc17f0b190953b2f7d65da9b1de92e4a953fd6 Mon Sep 17 00:00:00 2001
From: Alexander Tkachev
Date: Tue, 26 Jul 2016 20:07:45 +0600
Subject: WAGE: Add saveGameState() sketch

Copy-pasted from asvitkine/wage-engine (on java). I've managed filling
in some values, the others are 0 for now.
---
 engines/wage/saveload.cpp | 192 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 192 insertions(+)
 create mode 100644 engines/wage/saveload.cpp

(limited to 'engines/wage/saveload.cpp')

diff --git a/engines/wage/saveload.cpp b/engines/wage/saveload.cpp
new file mode 100644
index 0000000000..ea3ae34048
--- /dev/null
+++ b/engines/wage/saveload.cpp
@@ -0,0 +1,192 @@
+/* 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 "common/file.h"
+#include "common/debug.h"
+#include "common/debug-channels.h"
+#include "common/config-manager.h"
+#include "common/savefile.h"
+#include "common/system.h"
+#include "common/textconsole.h"
+#include "common/translation.h"
+
+#include "gui/saveload.h"
+
+#include "graphics/thumbnail.h"
+#include "graphics/surface.h"
+
+#include "wage/wage.h"
+#include "wage/world.h"
+#include "wage/entities.h"
+
+#define SAVEGAME_CURRENT_VERSION 1
+
+//
+// Version 0 (ScummVM):  first ScummVM version
+//
+
+namespace Wage {
+
+static const uint32 AGIflag = MKTAG('W', 'A', 'G', 'E');
+
+int WageEngine::saveGame(const Common::String &fileName, const Common::String &descriptionString) {
+	Common::OutSaveFile *out;
+	int result = 0;
+
+	debug(9, "WageEngine::saveGame(%s, %s)", fileName.c_str(), descriptionString.c_str());
+	if (!(out = _saveFileMan->openForSaving(fileName))) {
+		warning("Can't create file '%s', game not saved", fileName.c_str());
+		return -1;
+	} else {
+		debug(9, "Successfully opened %s for writing", fileName.c_str());
+	}
+
+	out->writeUint32BE(AGIflag);
+
+	// Write description of saved game, limited to WAGE_SAVEDGAME_DESCRIPTION_LEN characters + terminating NUL
+	const int WAGE_SAVEDGAME_DESCRIPTION_LEN = 127;
+	char description[WAGE_SAVEDGAME_DESCRIPTION_LEN + 1];
+
+	memset(description, 0, sizeof(description));
+	strncpy(description, descriptionString.c_str(), WAGE_SAVEDGAME_DESCRIPTION_LEN);
+	assert(WAGE_SAVEDGAME_DESCRIPTION_LEN + 1 == 128); // safety
+	out->write(description, 128);
+
+	out->writeByte(SAVEGAME_CURRENT_VERSION);
+	debug(9, "Writing save game version (%d)", SAVEGAME_CURRENT_VERSION);
+
+	// Thumbnail
+	Graphics::saveThumbnail(*out);
+
+	// Counters
+	out->writeSint16LE(_world->_scenes.size()); //numScenes
+	out->writeSint16LE(_world->_chrs.size()); //numChars
+	out->writeSint16LE(_world->_objs.size()); //numObjs
+
+	// Hex Offsets
+	out->writeSint32LE(0); //state.getChrsHexOffset()
+	out->writeSint32LE(0); //state.getObjsHexOffset()
+
+	// Unique 8-byte World Signature
+	out->writeSint32LE(0); //8-byte ints? seriously?
+
+	// More Counters
+	out->writeSint32LE(_world->_player->_context._visits); //visitNum
+	out->writeSint32LE(0); //state.getLoopNum()
+	out->writeSint32LE(_world->_player->_context._kills); //killNum
+
+	// Hex offset to player character
+	out->writeSint32LE(0); //state.getPlayerHexOffset()
+
+	// character in this scene?
+	out->writeSint32LE(0); //state.getPresCharHexOffset()
+
+	// Hex offset to current scene
+	out->writeSint32LE(0); //state.getCurSceneHexOffset()
+
+	// wearing a helmet?
+	out->writeSint32LE(0); //hex offset for <_world->_player->_armor[Chr::ChrArmorType::HEAD_ARMOR]> //state.getHelmetIndex()
+
+	// holding a shield?
+	out->writeSint32LE(0); //hex offset for <_world->_player->_armor[Chr::ChrArmorType::SHIELD_ARMOR]> //state.getShieldIndex()
+
+	// wearing chest armor?
+	out->writeSint32LE(0); //hex offset for <_world->_player->_armor[Chr::ChrArmorType::BODY_ARMOR]> //state.getChestArmIndex()
+
+	// wearing spiritual armor?
+	out->writeSint32LE(0); //hex offset for <_world->_player->_armor[Chr::ChrArmorType::MAGIC_ARMOR]> //state.getSprtArmIndex()
+
+	// TODO:
+	out->writeSint16LE(0xffff);	// ???? - always FFFF
+	out->writeSint16LE(0xffff);	// ???? - always FFFF
+	out->writeSint16LE(0xffff);	// ???? - always FFFF
+	out->writeSint16LE(0xffff);	// ???? - always FFFF
+
+	// did a character just escape?
+	out->writeSint32LE(0); //state.getRunCharHexOffset()
+
+	// players experience points
+	out->writeSint32LE(_world->_player->_context._experience);
+
+	out->writeSint16LE(0); //state.getAim()
+	out->writeSint16LE(0); //state.getOpponentAim()
+
+	// TODO:
+	out->writeSint16LE(0x0000);	// always 0
+	out->writeSint16LE(0x0000);	// always 0
+	out->writeSint16LE(0x0000);	// always 0
+
+	// Base character stats
+	out->writeByte(_world->_player->_physicalStrength); //state.getBasePhysStr()
+	out->writeByte(_world->_player->_physicalHp); //state.getBasePhysHp()
+	out->writeByte(_world->_player->_naturalArmor); //state.getBasePhysArm()
+	out->writeByte(_world->_player->_physicalAccuracy); //state.getBasePhysAcc()
+	out->writeByte(_world->_player->_spiritualStength); //state.getBaseSprtStr()
+	out->writeByte(_world->_player->_spiritialHp); //state.getBaseSprtHp()
+	out->writeByte(_world->_player->_resistanceToMagic); //state.getBaseSprtArm()
+	out->writeByte(_world->_player->_spiritualAccuracy); //state.getBaseSprtAcc()
+	out->writeByte(_world->_player->_runningSpeed); //state.getBaseRunSpeed()
+
+	// TODO:
+	out->writeByte(0x0A);		// ???? - always seems to be 0x0A
+
+	/*
+	// write user vars
+	for (short var : state.getUserVars())
+		out->->writeSint16LE(var);
+
+	// write updated info for all scenes
+	out.write(state.getSceneData());
+
+	// write updated info for all characters
+	out.write(state.getChrData());
+
+	// write updated info for all objects
+	out.write(state.getObjData());
+	*/
+
+	out->finalize();
+	if (out->err()) {
+		warning("Can't write file '%s'. (Disk full?)", fileName.c_str());
+		result = -1;
+	} else
+		debug(9, "Saved game %s in file %s", descriptionString.c_str(), fileName.c_str());
+
+	delete out;
+	return result;
+}
+
+Common::String WageEngine::getSavegameFilename(int16 slotId) const {
+	Common::String saveLoadSlot = _targetName;
+	saveLoadSlot += Common::String::format(".%.3d", slotId);
+	return saveLoadSlot;
+}
+
+Common::Error WageEngine::saveGameState(int slot, const Common::String &description) {
+	Common::String saveLoadSlot = getSavegameFilename(slot);
+	if (saveGame(saveLoadSlot, description) == 0)
+		return Common::kNoError;
+	else
+		return Common::kUnknownError;
+}
+
+} // End of namespace Agi
-- 
cgit v1.2.3


From fe1f5352aaccd0253b22e5add14ff395e3370953 Mon Sep 17 00:00:00 2001
From: Alexander Tkachev
Date: Wed, 27 Jul 2016 12:36:41 +0600
Subject: WAGE: Refine WageEngine::saveGame()

More values are saved now, yet some are still not found.
---
 engines/wage/saveload.cpp | 185 +++++++++++++++++++++++++++++++++++++---------
 1 file changed, 151 insertions(+), 34 deletions(-)

(limited to 'engines/wage/saveload.cpp')

diff --git a/engines/wage/saveload.cpp b/engines/wage/saveload.cpp
index ea3ae34048..71321628a8 100644
--- a/engines/wage/saveload.cpp
+++ b/engines/wage/saveload.cpp
@@ -18,6 +18,31 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
  */
 
 #include "common/file.h"
@@ -48,6 +73,30 @@ namespace Wage {
 
 static const uint32 AGIflag = MKTAG('W', 'A', 'G', 'E');
 
+//TODO: make sure these are calculated right: (we add flag, description, etc)
+#define VARS_INDEX 0x005E
+#define SCENES_INDEX 0x0232
+
+#define SCENE_SIZE 0x0010
+#define CHR_SIZE 0x0016
+#define OBJ_SIZE 0x0010
+
+#define GET_HEX_OFFSET(ptr, baseOffset, entrySize) ((ptr) == nullptr ? -1 : ((baseOffset) + (entrySize) * (ptr)->_index))
+#define GET_HEX_CHR_OFFSET(ptr) GET_HEX_OFFSET((ptr), chrsHexOffset, CHR_SIZE)
+#define GET_HEX_OBJ_OFFSET(ptr) GET_HEX_OFFSET((ptr), objsHexOffset, OBJ_SIZE)
+#define GET_HEX_SCENE_OFFSET(ptr) ((ptr) == nullptr ? -1 : (SCENES_INDEX + getSceneIndex(_world->_player->_currentScene) * SCENE_SIZE))
+
+int WageEngine::getSceneIndex(Scene *scene) const {
+	assert(scene);
+	Common::Array<Scene *> &orderedScenes = _world->_orderedScenes;
+	for (uint32 i = 0; i < orderedScenes.size(); ++i) {
+		if (orderedScenes[i] == scene) return i;
+	}
+
+	warning("Scene's index not found");
+	return -1;
+}
+
 int WageEngine::saveGame(const Common::String &fileName, const Common::String &descriptionString) {
 	Common::OutSaveFile *out;
 	int result = 0;
@@ -83,37 +132,42 @@ int WageEngine::saveGame(const Common::String &fileName, const Common::String &d
 	out->writeSint16LE(_world->_objs.size()); //numObjs
 
 	// Hex Offsets
-	out->writeSint32LE(0); //state.getChrsHexOffset()
-	out->writeSint32LE(0); //state.getObjsHexOffset()
+	int chrsHexOffset = SCENES_INDEX + _world->_scenes.size() * SCENE_SIZE; //chrs start after scenes
+	int objsHexOffset = chrsHexOffset + _world->_chrs.size() * CHR_SIZE; //objs start after chrs
+	out->writeSint32LE(chrsHexOffset);
+	out->writeSint32LE(objsHexOffset);
 
 	// Unique 8-byte World Signature
-	out->writeSint32LE(0); //8-byte ints? seriously?
+	out->writeSint32LE(0); //TODO: 8-byte ints? seriously?
+
+	Chr *player = _world->_player;
+	Context &playerContext = player->_context;
 
 	// More Counters
-	out->writeSint32LE(_world->_player->_context._visits); //visitNum
-	out->writeSint32LE(0); //state.getLoopNum()
-	out->writeSint32LE(_world->_player->_context._kills); //killNum
+	out->writeSint32LE(playerContext._visits); //visitNum
+	out->writeSint32LE(0); //TODO: state.getLoopNum()
+	out->writeSint32LE(playerContext._kills); //killNum
 
 	// Hex offset to player character
-	out->writeSint32LE(0); //state.getPlayerHexOffset()
+	out->writeSint32LE(GET_HEX_CHR_OFFSET(player)); //getPlayerHexOffset() == getHexOffsetForChr(player)
 
 	// character in this scene?
-	out->writeSint32LE(0); //state.getPresCharHexOffset()
+	out->writeSint32LE(GET_HEX_CHR_OFFSET(_monster)); //getPresCharHexOffset() == getHexOffsetForChr(monster)
 
 	// Hex offset to current scene
-	out->writeSint32LE(0); //state.getCurSceneHexOffset()
+	out->writeSint32LE(GET_HEX_SCENE_OFFSET(player->_currentScene)); //getCurSceneHexOffset()
 
 	// wearing a helmet?
-	out->writeSint32LE(0); //hex offset for <_world->_player->_armor[Chr::ChrArmorType::HEAD_ARMOR]> //state.getHelmetIndex()
+	out->writeSint32LE(GET_HEX_OBJ_OFFSET(player->_armor[Chr::ChrArmorType::HEAD_ARMOR])); //helmetIndex
 
 	// holding a shield?
-	out->writeSint32LE(0); //hex offset for <_world->_player->_armor[Chr::ChrArmorType::SHIELD_ARMOR]> //state.getShieldIndex()
+	out->writeSint32LE(GET_HEX_OBJ_OFFSET(player->_armor[Chr::ChrArmorType::SHIELD_ARMOR])); //shieldIndex
 
 	// wearing chest armor?
-	out->writeSint32LE(0); //hex offset for <_world->_player->_armor[Chr::ChrArmorType::BODY_ARMOR]> //state.getChestArmIndex()
+	out->writeSint32LE(GET_HEX_OBJ_OFFSET(player->_armor[Chr::ChrArmorType::BODY_ARMOR])); //chestArmIndex
 
 	// wearing spiritual armor?
-	out->writeSint32LE(0); //hex offset for <_world->_player->_armor[Chr::ChrArmorType::MAGIC_ARMOR]> //state.getSprtArmIndex()
+	out->writeSint32LE(GET_HEX_OBJ_OFFSET(player->_armor[Chr::ChrArmorType::MAGIC_ARMOR])); //sprtArmIndex
 
 	// TODO:
 	out->writeSint16LE(0xffff);	// ???? - always FFFF
@@ -122,13 +176,13 @@ int WageEngine::saveGame(const Common::String &fileName, const Common::String &d
 	out->writeSint16LE(0xffff);	// ???? - always FFFF
 
 	// did a character just escape?
-	out->writeSint32LE(0); //state.getRunCharHexOffset()
+	out->writeSint32LE(GET_HEX_CHR_OFFSET(_running)); //getRunCharHexOffset() == getHexOffsetForChr(running)
 
 	// players experience points
-	out->writeSint32LE(_world->_player->_context._experience);
+	out->writeSint32LE(playerContext._experience);
 
-	out->writeSint16LE(0); //state.getAim()
-	out->writeSint16LE(0); //state.getOpponentAim()
+	out->writeSint16LE(0); //TODO: state.getAim()
+	out->writeSint16LE(0); //TODO: state.getOpponentAim()
 
 	// TODO:
 	out->writeSint16LE(0x0000);	// always 0
@@ -136,33 +190,96 @@ int WageEngine::saveGame(const Common::String &fileName, const Common::String &d
 	out->writeSint16LE(0x0000);	// always 0
 
 	// Base character stats
-	out->writeByte(_world->_player->_physicalStrength); //state.getBasePhysStr()
-	out->writeByte(_world->_player->_physicalHp); //state.getBasePhysHp()
-	out->writeByte(_world->_player->_naturalArmor); //state.getBasePhysArm()
-	out->writeByte(_world->_player->_physicalAccuracy); //state.getBasePhysAcc()
-	out->writeByte(_world->_player->_spiritualStength); //state.getBaseSprtStr()
-	out->writeByte(_world->_player->_spiritialHp); //state.getBaseSprtHp()
-	out->writeByte(_world->_player->_resistanceToMagic); //state.getBaseSprtArm()
-	out->writeByte(_world->_player->_spiritualAccuracy); //state.getBaseSprtAcc()
-	out->writeByte(_world->_player->_runningSpeed); //state.getBaseRunSpeed()
+	// TODO: are these *base* btw? looks like we don't want to save *current* stats
+	out->writeByte(player->_physicalStrength); //state.getBasePhysStr()
+	out->writeByte(player->_physicalHp); //state.getBasePhysHp()
+	out->writeByte(player->_naturalArmor); //state.getBasePhysArm()
+	out->writeByte(player->_physicalAccuracy); //state.getBasePhysAcc()
+	out->writeByte(player->_spiritualStength); //state.getBaseSprtStr()
+	out->writeByte(player->_spiritialHp); //state.getBaseSprtHp()
+	out->writeByte(player->_resistanceToMagic); //state.getBaseSprtArm()
+	out->writeByte(player->_spiritualAccuracy); //state.getBaseSprtAcc()
+	out->writeByte(player->_runningSpeed); //state.getBaseRunSpeed()
 
 	// TODO:
 	out->writeByte(0x0A);		// ???? - always seems to be 0x0A
 
-	/*
 	// write user vars
-	for (short var : state.getUserVars())
-		out->->writeSint16LE(var);
-
+	for (uint32 i = 0; i < 26 * 9; ++i)
+		out->writeSint16LE(playerContext._userVariables[i]);
+	
 	// write updated info for all scenes
-	out.write(state.getSceneData());
+	Common::Array<Scene *> &orderedScenes = _world->_orderedScenes;
+	for (uint32 i = 0; i < orderedScenes.size(); ++i) {
+		Scene *scene = orderedScenes[i];
+		if (scene != _world->_storageScene) {
+			out->writeSint16LE(0); //TODO: scene.resourceId
+			out->writeSint16LE(scene->_worldY);
+			out->writeSint16LE(scene->_worldX);
+			out->writeByte(scene->_blocked[NORTH] ? 0x01 : 0x00);
+			out->writeByte(scene->_blocked[SOUTH] ? 0x01 : 0x00);
+			out->writeByte(scene->_blocked[EAST] ? 0x01 : 0x00);
+			out->writeByte(scene->_blocked[WEST] ? 0x01 : 0x00);
+			out->writeSint16LE(scene->_soundFrequency);
+			out->writeByte(scene->_soundType);
+			// the following two bytes are currently unknown
+			out->writeByte(0);
+			out->writeByte(0);
+			out->writeByte(scene->_visited ? 0x01 : 0x00);
+		}
+	}
 
 	// write updated info for all characters
-	out.write(state.getChrData());
+	Common::Array<Chr *> &orderedChrs = _world->_orderedChrs;
+	for (uint32 i = 0; i < orderedChrs.size(); ++i) {
+		Chr *chr = orderedChrs[i];
+		out->writeSint16LE(0); //TODO: chr.getResourceID()
+		out->writeSint16LE(0); //TODO: chr->_currentScene.getResourceID()
+		//TODO: here we want to write *current* stats
+		out->writeByte(chr->_physicalStrength);
+		out->writeByte(chr->_physicalHp);
+		out->writeByte(chr->_naturalArmor);
+		out->writeByte(chr->_physicalAccuracy);
+		out->writeByte(chr->_spiritualStength);
+		out->writeByte(chr->_spiritialHp);
+		out->writeByte(chr->_resistanceToMagic);
+		out->writeByte(chr->_spiritualAccuracy);
+		out->writeByte(chr->_runningSpeed);
+		out->writeByte(chr->_rejectsOffers);
+		out->writeByte(chr->_followsOpponent);
+		// bytes 16-20 are unknown
+		out->writeByte(0);
+		out->writeByte(0);
+		out->writeByte(0);
+		out->writeByte(0);
+		out->writeByte(0);
+		out->writeByte(chr->_weaponDamage1);
+		out->writeByte(chr->_weaponDamage2);
+	}
 
 	// write updated info for all objects
-	out.write(state.getObjData());
-	*/
+	Common::Array<Obj *> &orderedObjs = _world->_orderedObjs;
+	for (uint32 i = 0; i < orderedObjs.size(); ++i) {
+		Obj *obj = orderedObjs[i];
+		Scene *location = obj->_currentScene;
+		Chr *owner = obj->_currentOwner;
+
+		out->writeSint16LE(0); //TODO: obj.getResourceID()
+		out->writeSint16LE(0); //TODO: location == nullptr ? 0 : location.getResourceID());
+		out->writeSint16LE(0); //TODO: owner == nullptr ? 0 : owner.getResourceID());
+
+		// bytes 7-9 are unknown (always = 0)
+		out->writeByte(0);
+		out->writeByte(0);
+		out->writeByte(0);
+
+		out->writeByte(obj->_accuracy);
+		out->writeByte(obj->_value);
+		out->writeByte(obj->_type);
+		out->writeByte(obj->_damage);
+		out->writeByte(obj->_attackType);
+		out->writeSint16LE(obj->_numberOfUses);
+	}
 
 	out->finalize();
 	if (out->err()) {
-- 
cgit v1.2.3


From 5d804f379ce77f0dd5eb43cfa5c2d18aa2de3585 Mon Sep 17 00:00:00 2001
From: Alexander Tkachev
Date: Wed, 27 Jul 2016 17:27:18 +0600
Subject: WAGE: Refine saveGame()

Base/Current stats fix + loopNum, aim, opponentAim saving.
---
 engines/wage/saveload.cpp | 45 ++++++++++++++++++++++-----------------------
 1 file changed, 22 insertions(+), 23 deletions(-)

(limited to 'engines/wage/saveload.cpp')

diff --git a/engines/wage/saveload.cpp b/engines/wage/saveload.cpp
index 71321628a8..2c4a23f2d3 100644
--- a/engines/wage/saveload.cpp
+++ b/engines/wage/saveload.cpp
@@ -145,7 +145,7 @@ int WageEngine::saveGame(const Common::String &fileName, const Common::String &d
 
 	// More Counters
 	out->writeSint32LE(playerContext._visits); //visitNum
-	out->writeSint32LE(0); //TODO: state.getLoopNum()
+	out->writeSint32LE(_loopCount); //loopNum
 	out->writeSint32LE(playerContext._kills); //killNum
 
 	// Hex offset to player character
@@ -181,8 +181,8 @@ int WageEngine::saveGame(const Common::String &fileName, const Common::String &d
 	// players experience points
 	out->writeSint32LE(playerContext._experience);
 
-	out->writeSint16LE(0); //TODO: state.getAim()
-	out->writeSint16LE(0); //TODO: state.getOpponentAim()
+	out->writeSint16LE(_aim); //aim
+	out->writeSint16LE(_opponentAim); //opponentAim
 
 	// TODO:
 	out->writeSint16LE(0x0000);	// always 0
@@ -190,16 +190,15 @@ int WageEngine::saveGame(const Common::String &fileName, const Common::String &d
 	out->writeSint16LE(0x0000);	// always 0
 
 	// Base character stats
-	// TODO: are these *base* btw? looks like we don't want to save *current* stats
-	out->writeByte(player->_physicalStrength); //state.getBasePhysStr()
-	out->writeByte(player->_physicalHp); //state.getBasePhysHp()
-	out->writeByte(player->_naturalArmor); //state.getBasePhysArm()
-	out->writeByte(player->_physicalAccuracy); //state.getBasePhysAcc()
-	out->writeByte(player->_spiritualStength); //state.getBaseSprtStr()
-	out->writeByte(player->_spiritialHp); //state.getBaseSprtHp()
-	out->writeByte(player->_resistanceToMagic); //state.getBaseSprtArm()
-	out->writeByte(player->_spiritualAccuracy); //state.getBaseSprtAcc()
-	out->writeByte(player->_runningSpeed); //state.getBaseRunSpeed()
+	out->writeByte(playerContext._statVariables[PHYS_STR_BAS]); //state.getBasePhysStr()
+	out->writeByte(playerContext._statVariables[PHYS_HIT_BAS]); //state.getBasePhysHp()
+	out->writeByte(playerContext._statVariables[PHYS_ARM_BAS]); //state.getBasePhysArm()
+	out->writeByte(playerContext._statVariables[PHYS_ACC_BAS]); //state.getBasePhysAcc()
+	out->writeByte(playerContext._statVariables[SPIR_STR_BAS]); //state.getBaseSprtStr()
+	out->writeByte(playerContext._statVariables[SPIR_HIT_BAS]); //state.getBaseSprtHp()
+	out->writeByte(playerContext._statVariables[SPIR_ARM_BAS]); //state.getBaseSprtArm()
+	out->writeByte(playerContext._statVariables[SPIR_ACC_BAS]); //state.getBaseSprtAcc()
+	out->writeByte(playerContext._statVariables[PHYS_SPE_BAS]); //state.getBaseRunSpeed()
 
 	// TODO:
 	out->writeByte(0x0A);		// ???? - always seems to be 0x0A
@@ -235,16 +234,16 @@ int WageEngine::saveGame(const Common::String &fileName, const Common::String &d
 		Chr *chr = orderedChrs[i];
 		out->writeSint16LE(0); //TODO: chr.getResourceID()
 		out->writeSint16LE(0); //TODO: chr->_currentScene.getResourceID()
-		//TODO: here we want to write *current* stats
-		out->writeByte(chr->_physicalStrength);
-		out->writeByte(chr->_physicalHp);
-		out->writeByte(chr->_naturalArmor);
-		out->writeByte(chr->_physicalAccuracy);
-		out->writeByte(chr->_spiritualStength);
-		out->writeByte(chr->_spiritialHp);
-		out->writeByte(chr->_resistanceToMagic);
-		out->writeByte(chr->_spiritualAccuracy);
-		out->writeByte(chr->_runningSpeed);
+		Context &chrContext = chr->_context;
+		out->writeByte(chrContext._statVariables[PHYS_STR_CUR]);
+		out->writeByte(chrContext._statVariables[PHYS_HIT_CUR]);
+		out->writeByte(chrContext._statVariables[PHYS_ARM_CUR]);
+		out->writeByte(chrContext._statVariables[PHYS_ACC_CUR]);
+		out->writeByte(chrContext._statVariables[SPIR_STR_CUR]);
+		out->writeByte(chrContext._statVariables[SPIR_HIT_CUR]);
+		out->writeByte(chrContext._statVariables[SPIR_ARM_CUR]);
+		out->writeByte(chrContext._statVariables[SPIR_ACC_CUR]);
+		out->writeByte(chrContext._statVariables[PHYS_SPE_CUR]);
 		out->writeByte(chr->_rejectsOffers);
 		out->writeByte(chr->_followsOpponent);
 		// bytes 16-20 are unknown
-- 
cgit v1.2.3


From 6b21b1f89365bd77494940ec918212069238cc70 Mon Sep 17 00:00:00 2001
From: Alexander Tkachev
Date: Wed, 27 Jul 2016 17:38:43 +0600
Subject: WAGE: Add World's _signature

---
 engines/wage/saveload.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'engines/wage/saveload.cpp')

diff --git a/engines/wage/saveload.cpp b/engines/wage/saveload.cpp
index 2c4a23f2d3..60852551b7 100644
--- a/engines/wage/saveload.cpp
+++ b/engines/wage/saveload.cpp
@@ -138,7 +138,7 @@ int WageEngine::saveGame(const Common::String &fileName, const Common::String &d
 	out->writeSint32LE(objsHexOffset);
 
 	// Unique 8-byte World Signature
-	out->writeSint32LE(0); //TODO: 8-byte ints? seriously?
+	out->writeSint32LE(_world->_signature); //8-byte ints? seriously? (uses 4 bytes in java code too)
 
 	Chr *player = _world->_player;
 	Context &playerContext = player->_context;
-- 
cgit v1.2.3


From 35883517994efbcef9b5eb429b3888235745dea8 Mon Sep 17 00:00:00 2001
From: Alexander Tkachev
Date: Wed, 27 Jul 2016 17:51:00 +0600
Subject: WAGE: Refine saveGame() once more

_resourceId is added to entities, so saveGame() can access these ids and
save them.
---
 engines/wage/saveload.cpp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

(limited to 'engines/wage/saveload.cpp')

diff --git a/engines/wage/saveload.cpp b/engines/wage/saveload.cpp
index 60852551b7..a204757543 100644
--- a/engines/wage/saveload.cpp
+++ b/engines/wage/saveload.cpp
@@ -212,7 +212,7 @@ int WageEngine::saveGame(const Common::String &fileName, const Common::String &d
 	for (uint32 i = 0; i < orderedScenes.size(); ++i) {
 		Scene *scene = orderedScenes[i];
 		if (scene != _world->_storageScene) {
-			out->writeSint16LE(0); //TODO: scene.resourceId
+			out->writeSint16LE(scene->_resourceId);
 			out->writeSint16LE(scene->_worldY);
 			out->writeSint16LE(scene->_worldX);
 			out->writeByte(scene->_blocked[NORTH] ? 0x01 : 0x00);
@@ -232,8 +232,8 @@ int WageEngine::saveGame(const Common::String &fileName, const Common::String &d
 	Common::Array<Chr *> &orderedChrs = _world->_orderedChrs;
 	for (uint32 i = 0; i < orderedChrs.size(); ++i) {
 		Chr *chr = orderedChrs[i];
-		out->writeSint16LE(0); //TODO: chr.getResourceID()
-		out->writeSint16LE(0); //TODO: chr->_currentScene.getResourceID()
+		out->writeSint16LE(chr->_resourceId);
+		out->writeSint16LE(chr->_currentScene->_resourceId);
 		Context &chrContext = chr->_context;
 		out->writeByte(chrContext._statVariables[PHYS_STR_CUR]);
 		out->writeByte(chrContext._statVariables[PHYS_HIT_CUR]);
@@ -263,9 +263,9 @@ int WageEngine::saveGame(const Common::String &fileName, const Common::String &d
 		Scene *location = obj->_currentScene;
 		Chr *owner = obj->_currentOwner;
 
-		out->writeSint16LE(0); //TODO: obj.getResourceID()
-		out->writeSint16LE(0); //TODO: location == nullptr ? 0 : location.getResourceID());
-		out->writeSint16LE(0); //TODO: owner == nullptr ? 0 : owner.getResourceID());
+		out->writeSint16LE(obj->_resourceId);
+		out->writeSint16LE(location == nullptr ? 0 : location->_resourceId);
+		out->writeSint16LE(owner == nullptr ? 0 : owner->_resourceId);
 
 		// bytes 7-9 are unknown (always = 0)
 		out->writeByte(0);
-- 
cgit v1.2.3


From a4f02c73837ef46f620872d8297cdc6cbf5119dc Mon Sep 17 00:00:00 2001
From: Alexander Tkachev
Date: Wed, 27 Jul 2016 18:06:39 +0600
Subject: WAGE: Move some code in saveGame()

Now flags, version, description and thumbnail are added in the end of
the file, thus making saves compatible with original ones.
---
 engines/wage/saveload.cpp | 49 +++++++++++++++++++++++++++++------------------
 1 file changed, 30 insertions(+), 19 deletions(-)

(limited to 'engines/wage/saveload.cpp')

diff --git a/engines/wage/saveload.cpp b/engines/wage/saveload.cpp
index a204757543..bc9a443aa0 100644
--- a/engines/wage/saveload.cpp
+++ b/engines/wage/saveload.cpp
@@ -66,12 +66,16 @@
 #define SAVEGAME_CURRENT_VERSION 1
 
 //
-// Version 0 (ScummVM):  first ScummVM version
+// Original saves format is supported.
+// ScummVM adds flags, description and thumbnail
+// in the end of the file (shouldn't make saves incompatible).
+//
+// Version 0 (original/ScummVM):  first ScummVM version
 //
 
 namespace Wage {
 
-static const uint32 AGIflag = MKTAG('W', 'A', 'G', 'E');
+static const uint32 WAGEflag = MKTAG('W', 'A', 'G', 'E');
 
 //TODO: make sure these are calculated right: (we add flag, description, etc)
 #define VARS_INDEX 0x005E
@@ -109,23 +113,6 @@ int WageEngine::saveGame(const Common::String &fileName, const Common::String &d
 		debug(9, "Successfully opened %s for writing", fileName.c_str());
 	}
 
-	out->writeUint32BE(AGIflag);
-
-	// Write description of saved game, limited to WAGE_SAVEDGAME_DESCRIPTION_LEN characters + terminating NUL
-	const int WAGE_SAVEDGAME_DESCRIPTION_LEN = 127;
-	char description[WAGE_SAVEDGAME_DESCRIPTION_LEN + 1];
-
-	memset(description, 0, sizeof(description));
-	strncpy(description, descriptionString.c_str(), WAGE_SAVEDGAME_DESCRIPTION_LEN);
-	assert(WAGE_SAVEDGAME_DESCRIPTION_LEN + 1 == 128); // safety
-	out->write(description, 128);
-
-	out->writeByte(SAVEGAME_CURRENT_VERSION);
-	debug(9, "Writing save game version (%d)", SAVEGAME_CURRENT_VERSION);
-
-	// Thumbnail
-	Graphics::saveThumbnail(*out);
-
 	// Counters
 	out->writeSint16LE(_world->_scenes.size()); //numScenes
 	out->writeSint16LE(_world->_chrs.size()); //numChars
@@ -280,6 +267,30 @@ int WageEngine::saveGame(const Common::String &fileName, const Common::String &d
 		out->writeSint16LE(obj->_numberOfUses);
 	}
 
+	// the following is appended by ScummVM
+	out->writeUint32BE(WAGEflag);
+
+	// Write description of saved game, limited to WAGE_SAVEDGAME_DESCRIPTION_LEN characters + terminating NUL
+	const int WAGE_SAVEDGAME_DESCRIPTION_LEN = 127;
+	char description[WAGE_SAVEDGAME_DESCRIPTION_LEN + 1];
+
+	memset(description, 0, sizeof(description));
+	strncpy(description, descriptionString.c_str(), WAGE_SAVEDGAME_DESCRIPTION_LEN);
+	assert(WAGE_SAVEDGAME_DESCRIPTION_LEN + 1 == 128); // safety
+	out->write(description, 128);
+
+	out->writeByte(SAVEGAME_CURRENT_VERSION);
+	debug(9, "Writing save game version (%d)", SAVEGAME_CURRENT_VERSION);
+
+	// Thumbnail
+	Graphics::saveThumbnail(*out);
+
+	// this one to make checking easier:
+	// it couldn't be added to the beginning
+	// and we won't be able to find it in the middle,
+	// so these would be the last 4 bytes of the file
+	out->writeUint32BE(WAGEflag);
+
 	out->finalize();
 	if (out->err()) {
 		warning("Can't write file '%s'. (Disk full?)", fileName.c_str());
-- 
cgit v1.2.3


From 18476dc752efd9888222b2b61d30a88648486fbd Mon Sep 17 00:00:00 2001
From: Alexander Tkachev
Date: Wed, 27 Jul 2016 21:38:10 +0600
Subject: WAGE: Add loadGame() sketch

---
 engines/wage/saveload.cpp | 184 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 184 insertions(+)

(limited to 'engines/wage/saveload.cpp')

diff --git a/engines/wage/saveload.cpp b/engines/wage/saveload.cpp
index bc9a443aa0..4b38e5be6e 100644
--- a/engines/wage/saveload.cpp
+++ b/engines/wage/saveload.cpp
@@ -302,6 +302,190 @@ int WageEngine::saveGame(const Common::String &fileName, const Common::String &d
 	return result;
 }
 
+int WageEngine::loadGame(int slotId) {
+	Common::InSaveFile *data;
+	Common::String fileName = getSavegameFilename(slotId);
+
+	debug(9, "WageEngine::loadGame(%d)", slotId);
+	if (!(data = _saveFileMan->openForLoading(fileName))) {
+		warning("Can't open file '%s', game not loaded", fileName.c_str());
+		return -1;
+	} else {
+		debug(9, "Successfully opened %s for reading", fileName.c_str());
+	}
+
+	// Counters
+	int numScenes = data->readSint16LE();
+	int numChars = data->readSint16LE();
+	int numObjs = data->readSint16LE();
+
+	// Hex Offsets
+	int chrsHexOffset = data->readSint32LE();
+	int objsHexOffset = data->readSint32LE();
+
+	// Unique 8-byte World Signature
+	_world->_signature = data->readSint32LE();
+
+	//Chr *player = _world->_player;
+	//Context &playerContext = player->_context;
+
+	// More Counters
+	int visitNum = data->readSint32LE(); //visitNum @ playerContext._visits
+	_loopCount = data->readSint32LE(); //loopNum
+	int killNum = data->readSint32LE(); //killNum @ playerContext._kills
+
+	// Hex offset to player character
+	int playerOffset = data->readSint32LE();
+
+	// character in this scene?
+	int presCharOffset = data->readSint32LE();
+
+	// Hex offset to current scene
+	int currentSceneOffset = data->readSint32LE();
+
+	// wearing a helmet?
+	int helmetOffset = data->readSint32LE(); //helmetIndex @ player->_armor[Chr::ChrArmorType::HEAD_ARMOR]
+
+	// holding a shield?
+	int shieldOffset = data->readSint32LE(); //shieldIndex @ player->_armor[Chr::ChrArmorType::SHIELD_ARMOR]
+
+	// wearing chest armor?
+	int armorOffset = data->readSint32LE(); //chestArmIndex @ player->_armor[Chr::ChrArmorType::BODY_ARMOR]
+
+	// wearing spiritual armor?
+	int spiritualArmorOffset = data->readSint32LE(); //sprtArmIndex @ player->_armor[Chr::ChrArmorType::MAGIC_ARMOR]
+
+	data->readSint16LE();	// FFFF
+	data->readSint16LE();	// FFFF
+	data->readSint16LE();	// FFFF
+	data->readSint16LE();	// FFFF
+
+	int runCharOffset = data->readSint32LE();
+
+	// players experience points
+	int exp = data->readSint32LE(); // @ playerContext._experience
+
+	_aim = data->readSint16LE(); //aim
+	_opponentAim = data->readSint16LE(); //opponentAim
+
+	data->readSint16LE(); // 0000
+	data->readSint16LE(); // 0000
+	data->readSint16LE(); // 0000
+
+	// Base character stats
+	int basePhysStr = data->readByte(); // @ playerContext._statVariables[PHYS_STR_BAS]
+	int basePhysHp = data->readByte(); // @ playerContext._statVariables[PHYS_HIT_BAS]
+	int basePhysArm = data->readByte(); // @ playerContext._statVariables[PHYS_ARM_BAS]
+	int basePhysAcc = data->readByte(); // @ playerContext._statVariables[PHYS_ACC_BAS]
+	int baseSprtStr = data->readByte(); // @ playerContext._statVariables[SPIR_STR_BAS]
+	int baseSprtHp = data->readByte(); // @ playerContext._statVariables[SPIR_HIT_BAS]
+	int baseSprtArm = data->readByte(); // @ playerContext._statVariables[SPIR_ARM_BAS]
+	int baseSprtAcc = data->readByte(); // @ playerContext._statVariables[SPIR_ACC_BAS]
+	int baseRunSpeed = data->readByte(); // @ playerContext._statVariables[PHYS_SPE_BAS]
+
+	data->readByte(); // 0x0A?
+
+	// write user vars
+	for (uint32 i = 0; i < 26 * 9; ++i)
+		data->readSint16LE(); // @ playerContext._userVariables[i]
+
+	// write updated info for all scenes	
+	for (uint32 i = 0; i < numScenes; ++i) {		
+		data->readSint16LE(); // @ scene->_resourceId
+		data->readSint16LE(); // @ scene->_worldY
+		data->readSint16LE(); // @ scene->_worldX
+		data->readByte(); // @ scene->_blocked[NORTH]
+		data->readByte(); // @ scene->_blocked[SOUTH]
+		data->readByte(); // @ scene->_blocked[EAST]
+		data->readByte(); // @ scene->_blocked[WEST]
+		data->readSint16LE(); // @ scene->_soundFrequency
+		data->readByte(); // @ scene->_soundType
+		// the following two bytes are currently unknown
+		data->readByte();
+		data->readByte();
+		data->readByte(); // @ scene->_visited
+	}
+
+	// write updated info for all characters
+	Common::Array<Chr *> &orderedChrs = _world->_orderedChrs;
+	for (uint32 i = 0; i < orderedChrs.size(); ++i) {
+		int resourceId = data->readSint16LE();
+		int sceneResourceId = data->readSint16LE();
+
+		int strength = data->readByte(); // @ chrContext._statVariables[PHYS_STR_CUR]
+		int hp = data->readByte(); // @ chrContext._statVariables[PHYS_HIT_CUR]
+		int armor = data->readByte(); // @ chrContext._statVariables[PHYS_ARM_CUR]
+		int accuracy = data->readByte(); // @ chrContext._statVariables[PHYS_ACC_CUR]
+		int spirStrength = data->readByte(); // @ chrContext._statVariables[SPIR_STR_CUR]
+		int spirHp = data->readByte(); // @ chrContext._statVariables[SPIR_HIT_CUR]
+		int spirArmor = data->readByte(); // @ chrContext._statVariables[SPIR_ARM_CUR]
+		int spirAccuracy = data->readByte(); // @ chrContext._statVariables[SPIR_ACC_CUR]
+		int speed = data->readByte(); // @ chrContext._statVariables[PHYS_SPE_CUR]
+		int rejectsOffers = data->readByte(); // @ chr->_rejectsOffers
+		int followsOpponent = data->readByte(); // @ chr->_followsOpponent
+
+		// bytes 16-20 are unknown
+		data->readByte();
+		data->readByte();
+		data->readByte();
+		data->readByte();
+		data->readByte();
+
+		data->readByte(); // @ chr->_weaponDamage1
+		data->readByte(); // @ chr->_weaponDamage2
+	}
+
+	// write updated info for all objects
+	for (uint32 i = 0; i < numObjs; ++i) {
+		int resourceId = data->readSint16LE();
+		int locationResourceId = data->readSint16LE();
+		int ownerResourceId = data->readSint16LE();
+
+		// bytes 7-9 are unknown (always = 0)
+		data->readByte();
+		data->readByte();
+		data->readByte();
+
+		data->readByte(); // @ obj->_accuracy
+		data->readByte(); // @ obj->_value
+		data->readByte(); // @ obj->_type
+		data->readByte(); // @ obj->_damage
+		data->readByte(); // @ obj->_attackType
+		data->readSint16LE(); // @ obj->_numberOfUses
+	}
+
+	// the following is appended by ScummVM
+	if (data->pos() < data->size()) {
+		int scummvmWageFlag = data->readUint32BE();
+
+		if (scummvmWageFlag != WAGEflag) {
+			warning("Extra bytes after original save's information found, but that's not ScummVM's");
+			delete data;
+			return 0;
+		}
+
+		// Write description of saved game, limited to WAGE_SAVEDGAME_DESCRIPTION_LEN characters + terminating NUL
+		const int WAGE_SAVEDGAME_DESCRIPTION_LEN = 127;
+		char description[WAGE_SAVEDGAME_DESCRIPTION_LEN + 1];
+		data->read(description, 128);
+		if (description[WAGE_SAVEDGAME_DESCRIPTION_LEN] != 0) {
+			warning("Description's last byte is not '\0'");
+			description[WAGE_SAVEDGAME_DESCRIPTION_LEN] = 0;
+		}
+
+		int version = data->readByte();
+		if (version != SAVEGAME_CURRENT_VERSION) {
+			warning("Reading version %d while current is %d", version, SAVEGAME_CURRENT_VERSION);
+		}
+
+		// Thumbnail
+		Graphics::loadThumbnail(*data);
+	}
+
+	delete data;
+	return 0;
+}
+
 Common::String WageEngine::getSavegameFilename(int16 slotId) const {
 	Common::String saveLoadSlot = _targetName;
 	saveLoadSlot += Common::String::format(".%.3d", slotId);
-- 
cgit v1.2.3


From a854217bd67c0e9ac8df552064dbfcaf6fd83024 Mon Sep 17 00:00:00 2001
From: Alexander Tkachev
Date: Thu, 28 Jul 2016 15:21:14 +0600
Subject: WAGE: Refine loadGame()

It now actually does the loading.
---
 engines/wage/saveload.cpp | 399 ++++++++++++++++++++++++++++++++++++----------
 1 file changed, 314 insertions(+), 85 deletions(-)

(limited to 'engines/wage/saveload.cpp')

diff --git a/engines/wage/saveload.cpp b/engines/wage/saveload.cpp
index 4b38e5be6e..654812a6b1 100644
--- a/engines/wage/saveload.cpp
+++ b/engines/wage/saveload.cpp
@@ -88,19 +88,86 @@ static const uint32 WAGEflag = MKTAG('W', 'A', 'G', 'E');
 #define GET_HEX_OFFSET(ptr, baseOffset, entrySize) ((ptr) == nullptr ? -1 : ((baseOffset) + (entrySize) * (ptr)->_index))
 #define GET_HEX_CHR_OFFSET(ptr) GET_HEX_OFFSET((ptr), chrsHexOffset, CHR_SIZE)
 #define GET_HEX_OBJ_OFFSET(ptr) GET_HEX_OFFSET((ptr), objsHexOffset, OBJ_SIZE)
-#define GET_HEX_SCENE_OFFSET(ptr) ((ptr) == nullptr ? -1 : (SCENES_INDEX + getSceneIndex(_world->_player->_currentScene) * SCENE_SIZE))
+#define GET_HEX_SCENE_OFFSET(ptr) ((ptr) == nullptr ? -1 : \
+	((ptr) == _world->_storageScene ? 0 : (SCENES_INDEX + getSceneIndex(_world->_player->_currentScene) * SCENE_SIZE)))
 
 int WageEngine::getSceneIndex(Scene *scene) const {
 	assert(scene);
 	Common::Array<Scene *> &orderedScenes = _world->_orderedScenes;
 	for (uint32 i = 0; i < orderedScenes.size(); ++i) {
-		if (orderedScenes[i] == scene) return i;
+		if (orderedScenes[i] == scene) return i-1;
 	}
 
 	warning("Scene's index not found");
 	return -1;
 }
 
+Obj *WageEngine::getObjByOffset(int offset, int objBaseOffset) const {
+	int objIndex = -1;
+
+	if (offset != 0xFFFF) {
+		objIndex = (offset - objBaseOffset) / CHR_SIZE;
+	}
+
+	if (objIndex >= 0 && objIndex < _world->_orderedObjs.size()) {
+		return _world->_orderedObjs[objIndex];
+	}
+
+	return nullptr;
+}
+
+Chr *WageEngine::getChrById(int resId) const {
+	Common::Array<Chr *> &orderedChrs = _world->_orderedChrs;
+	for (uint32 i = 0; i < orderedChrs.size(); ++i) {
+		if (orderedChrs[i]->_resourceId == resId)
+			return orderedChrs[i];
+	}
+
+	return nullptr;
+}
+
+Chr *WageEngine::getChrByOffset(int offset, int chrBaseOffset) const {
+	int chrIndex = -1;
+
+	if (offset != 0xFFFF) {
+		chrIndex = (offset - chrBaseOffset) / CHR_SIZE;
+	}
+
+	if (chrIndex >= 0 && chrIndex < _world->_orderedChrs.size()) {
+		return _world->_orderedChrs[chrIndex];
+	}
+
+	return nullptr;
+}
+
+Scene *WageEngine::getSceneById(int resId) const {
+	Common::Array<Scene *> &orderedScenes = _world->_orderedScenes;
+	for (uint32 i = 0; i < orderedScenes.size(); ++i) {
+		if (orderedScenes[i]->_resourceId == resId)
+			return orderedScenes[i];
+	}
+
+	return nullptr;
+}
+
+Scene *WageEngine::getSceneByOffset(int offset) const {
+	int sceneIndex = -1;
+
+	if (offset != 0xFFFF) {
+		if (offset == 0)
+			sceneIndex = 0;
+		else
+			sceneIndex = 1 + (offset - SCENES_INDEX) / SCENE_SIZE;
+	}
+
+	if (sceneIndex >= 0 && sceneIndex < _world->_orderedScenes.size()) {
+		if (sceneIndex == 0) return _world->_storageScene;
+		return _world->_orderedScenes[sceneIndex];
+	}
+
+	return nullptr;
+}
+
 int WageEngine::saveGame(const Common::String &fileName, const Common::String &descriptionString) {
 	Common::OutSaveFile *out;
 	int result = 0;
@@ -324,15 +391,18 @@ int WageEngine::loadGame(int slotId) {
 	int objsHexOffset = data->readSint32LE();
 
 	// Unique 8-byte World Signature
-	_world->_signature = data->readSint32LE();
-
-	//Chr *player = _world->_player;
-	//Context &playerContext = player->_context;
+	int signature = data->readSint32LE();
+	if (_world->_signature != signature) {
+		warning("This saved game is for a different world, please select another one");
+		warning("World signature = %d, save signature = %d", _world->_signature, signature);
+		delete data;
+		return -1;
+	}
 
 	// More Counters
-	int visitNum = data->readSint32LE(); //visitNum @ playerContext._visits
-	_loopCount = data->readSint32LE(); //loopNum
-	int killNum = data->readSint32LE(); //killNum @ playerContext._kills
+	int visitNum = data->readSint32LE(); //visitNum
+	int loopNum = data->readSint32LE(); //loopNum
+	int killNum = data->readSint32LE(); //killNum
 
 	// Hex offset to player character
 	int playerOffset = data->readSint32LE();
@@ -343,17 +413,41 @@ int WageEngine::loadGame(int slotId) {
 	// Hex offset to current scene
 	int currentSceneOffset = data->readSint32LE();
 
+	// find player and current scene
+	Chr *player = getChrByOffset(playerOffset, chrsHexOffset);
+	if (player == nullptr) {
+		warning("Invalid Character!  Aborting load.");
+		delete data;
+		return -1;
+	}
+
+	Scene *currentScene = getSceneByOffset(currentSceneOffset);
+	if (currentScene == nullptr) {
+		warning("Invalid Scene!  Aborting load.");
+		delete data;
+		return -1;
+	}
+
+	// set player character
+	_world->_player = player;
+
+	// set current scene
+	player->_currentScene = currentScene;
+
+	// clear the players inventory list
+	player->_inventory.clear();
+
 	// wearing a helmet?
-	int helmetOffset = data->readSint32LE(); //helmetIndex @ player->_armor[Chr::ChrArmorType::HEAD_ARMOR]
+	int helmetOffset = data->readSint32LE(); //helmetIndex
 
 	// holding a shield?
-	int shieldOffset = data->readSint32LE(); //shieldIndex @ player->_armor[Chr::ChrArmorType::SHIELD_ARMOR]
+	int shieldOffset = data->readSint32LE(); //shieldIndex
 
 	// wearing chest armor?
-	int armorOffset = data->readSint32LE(); //chestArmIndex @ player->_armor[Chr::ChrArmorType::BODY_ARMOR]
+	int armorOffset = data->readSint32LE(); //chestArmIndex
 
 	// wearing spiritual armor?
-	int spiritualArmorOffset = data->readSint32LE(); //sprtArmIndex @ player->_armor[Chr::ChrArmorType::MAGIC_ARMOR]
+	int spiritualArmorOffset = data->readSint32LE(); //sprtArmIndex
 
 	data->readSint16LE();	// FFFF
 	data->readSint16LE();	// FFFF
@@ -365,64 +459,118 @@ int WageEngine::loadGame(int slotId) {
 	// players experience points
 	int exp = data->readSint32LE(); // @ playerContext._experience
 
-	_aim = data->readSint16LE(); //aim
-	_opponentAim = data->readSint16LE(); //opponentAim
+	int aim = data->readSint16LE(); //aim
+	int opponentAim = data->readSint16LE(); //opponentAim
 
 	data->readSint16LE(); // 0000
 	data->readSint16LE(); // 0000
 	data->readSint16LE(); // 0000
 
 	// Base character stats
-	int basePhysStr = data->readByte(); // @ playerContext._statVariables[PHYS_STR_BAS]
-	int basePhysHp = data->readByte(); // @ playerContext._statVariables[PHYS_HIT_BAS]
-	int basePhysArm = data->readByte(); // @ playerContext._statVariables[PHYS_ARM_BAS]
-	int basePhysAcc = data->readByte(); // @ playerContext._statVariables[PHYS_ACC_BAS]
-	int baseSprtStr = data->readByte(); // @ playerContext._statVariables[SPIR_STR_BAS]
-	int baseSprtHp = data->readByte(); // @ playerContext._statVariables[SPIR_HIT_BAS]
-	int baseSprtArm = data->readByte(); // @ playerContext._statVariables[SPIR_ARM_BAS]
-	int baseSprtAcc = data->readByte(); // @ playerContext._statVariables[SPIR_ACC_BAS]
-	int baseRunSpeed = data->readByte(); // @ playerContext._statVariables[PHYS_SPE_BAS]
+	int basePhysStr = data->readByte();
+	int basePhysHp = data->readByte();
+	int basePhysArm = data->readByte();
+	int basePhysAcc = data->readByte();
+	int baseSprtStr = data->readByte();
+	int baseSprtHp = data->readByte();
+	int baseSprtArm = data->readByte();
+	int baseSprtAcc = data->readByte();
+	int baseRunSpeed = data->readByte();
+
+	// set player stats
+	Context &playerContext = player->_context;
+	// I'm setting player fields also, because those are used as base values in Chr::resetState()
+	playerContext._statVariables[PHYS_STR_BAS] = player->_physicalStrength = basePhysStr;
+	playerContext._statVariables[PHYS_HIT_BAS] = player->_physicalHp = basePhysHp;
+	playerContext._statVariables[PHYS_ARM_BAS] = player->_naturalArmor = basePhysArm;
+	playerContext._statVariables[PHYS_ACC_BAS] = player->_physicalAccuracy = basePhysAcc;
+	playerContext._statVariables[SPIR_STR_BAS] = player->_spiritualStength = baseSprtStr;
+	playerContext._statVariables[SPIR_HIT_BAS] = player->_spiritialHp = baseSprtHp;
+	playerContext._statVariables[SPIR_ARM_BAS] = player->_resistanceToMagic = baseSprtArm;
+	playerContext._statVariables[SPIR_ACC_BAS] = player->_spiritualAccuracy = baseSprtAcc;
+	playerContext._statVariables[PHYS_SPE_BAS] = player->_runningSpeed = baseRunSpeed;
+
+	// set visit#
+	playerContext._visits = visitNum;
+
+	// set monsters killed
+	playerContext._kills = killNum;
+
+	// set experience
+	playerContext._experience = exp;
+
+	// if a character is present, move it to this scene
+	// TODO: This is done in the engine object, would it be cleaner
+	// to move it here?
+	// well, it's actually down there now, now sure if that's "cleaner"
+	// when it's up there or down there
+
+	// if a character just ran away, let our engine know
+	// TODO: The current engine doesn't have a case for this, we
+	// should update it
+	// yep, I don't see such code anywhere in java, so not added it here
 
 	data->readByte(); // 0x0A?
 
-	// write user vars
-	for (uint32 i = 0; i < 26 * 9; ++i)
-		data->readSint16LE(); // @ playerContext._userVariables[i]
-
-	// write updated info for all scenes	
-	for (uint32 i = 0; i < numScenes; ++i) {		
-		data->readSint16LE(); // @ scene->_resourceId
-		data->readSint16LE(); // @ scene->_worldY
-		data->readSint16LE(); // @ scene->_worldX
-		data->readByte(); // @ scene->_blocked[NORTH]
-		data->readByte(); // @ scene->_blocked[SOUTH]
-		data->readByte(); // @ scene->_blocked[EAST]
-		data->readByte(); // @ scene->_blocked[WEST]
-		data->readSint16LE(); // @ scene->_soundFrequency
-		data->readByte(); // @ scene->_soundType
-		// the following two bytes are currently unknown
-		data->readByte();
-		data->readByte();
-		data->readByte(); // @ scene->_visited
+	// set all user variables
+	for (uint32 i = 0; i < 26 * 9; ++i) {
+		playerContext._userVariables[i] = data->readSint16LE();	
 	}
 
-	// write updated info for all characters
+	// update all scene stats
+	Common::Array<Scene *> &orderedScenes = _world->_orderedScenes;
+	if (numScenes != orderedScenes.size()) {
+		warning("scenes number in file (%d) differs from the one in world (%d)", numScenes, orderedScenes.size());
+	}
+	for (uint32 i = 0; i < orderedScenes.size(); ++i) {
+		Scene *scene = orderedScenes[i];
+		if (scene == _world->_storageScene) {
+			scene->_chrs.clear();
+			scene->_objs.clear();
+		} else {
+			int id = data->readSint16LE();
+
+			if (scene->_resourceId != id) {
+				warning("loadGame(): updating scenes: expected %d but got %d", scene->_resourceId, id);
+				data->skip(14); //2,2,1,1,1,1,2,1,1,1,1 down there
+				continue;
+			}
+
+			scene->_worldY = data->readSint16LE();
+			scene->_worldX = data->readSint16LE();
+			scene->_blocked[NORTH] = data->readByte() != 0;
+			scene->_blocked[SOUTH] = data->readByte() != 0;
+			scene->_blocked[EAST] = data->readByte() != 0;
+			scene->_blocked[WEST] = data->readByte() != 0;
+			scene->_soundFrequency = data->readSint16LE();
+			scene->_soundType = data->readByte();
+			// the following two bytes are currently unknown
+			data->readByte();
+			data->readByte();
+			scene->_visited = data->readByte() != 0;
+		}
+	}
+
+	// update all char locations and stats
 	Common::Array<Chr *> &orderedChrs = _world->_orderedChrs;
+	if (numChars != orderedChrs.size()) {
+		warning("characters number in file (%d) differs from the one in world (%d)", numChars, orderedChrs.size());
+	}
 	for (uint32 i = 0; i < orderedChrs.size(); ++i) {
 		int resourceId = data->readSint16LE();
 		int sceneResourceId = data->readSint16LE();
 
-		int strength = data->readByte(); // @ chrContext._statVariables[PHYS_STR_CUR]
-		int hp = data->readByte(); // @ chrContext._statVariables[PHYS_HIT_CUR]
-		int armor = data->readByte(); // @ chrContext._statVariables[PHYS_ARM_CUR]
-		int accuracy = data->readByte(); // @ chrContext._statVariables[PHYS_ACC_CUR]
-		int spirStrength = data->readByte(); // @ chrContext._statVariables[SPIR_STR_CUR]
-		int spirHp = data->readByte(); // @ chrContext._statVariables[SPIR_HIT_CUR]
-		int spirArmor = data->readByte(); // @ chrContext._statVariables[SPIR_ARM_CUR]
-		int spirAccuracy = data->readByte(); // @ chrContext._statVariables[SPIR_ACC_CUR]
-		int speed = data->readByte(); // @ chrContext._statVariables[PHYS_SPE_CUR]
-		int rejectsOffers = data->readByte(); // @ chr->_rejectsOffers
-		int followsOpponent = data->readByte(); // @ chr->_followsOpponent
+		int strength = data->readByte();
+		int hp = data->readByte();
+		int armor = data->readByte();
+		int accuracy = data->readByte();
+		int spirStrength = data->readByte();
+		int spirHp = data->readByte();
+		int spirArmor = data->readByte();
+		int spirAccuracy = data->readByte();
+		int speed = data->readByte();
+		int rejectsOffers = data->readByte();
+		int followsOpponent = data->readByte();
 
 		// bytes 16-20 are unknown
 		data->readByte();
@@ -431,12 +579,38 @@ int WageEngine::loadGame(int slotId) {
 		data->readByte();
 		data->readByte();
 
-		data->readByte(); // @ chr->_weaponDamage1
-		data->readByte(); // @ chr->_weaponDamage2
+		int weaponDamage1 = data->readByte();
+		int weaponDamage2 = data->readByte();
+
+		Chr *chr = orderedChrs[i];
+		if (chr->_resourceId != resourceId) {
+			warning("loadGame(): updating chrs: expected %d but got %d", chr->_resourceId, resourceId);
+			continue;
+		}
+
+		chr->_currentScene = getSceneById(sceneResourceId);
+		Context &chrContext = chr->_context;
+		chrContext._statVariables[PHYS_STR_CUR] = strength;
+		chrContext._statVariables[PHYS_HIT_CUR] = hp;
+		chrContext._statVariables[PHYS_ARM_CUR] = armor;
+		chrContext._statVariables[PHYS_ACC_CUR] = accuracy;
+		chrContext._statVariables[SPIR_STR_CUR] = spirStrength;
+		chrContext._statVariables[SPIR_HIT_CUR] = spirHp;
+		chrContext._statVariables[SPIR_ARM_CUR] = spirArmor;
+		chrContext._statVariables[SPIR_ACC_CUR] = spirAccuracy;
+		chrContext._statVariables[PHYS_SPE_CUR] = speed;
+		chr->_rejectsOffers = rejectsOffers;
+		chr->_followsOpponent = followsOpponent;
+		chr->_weaponDamage1 = weaponDamage1;
+		chr->_weaponDamage2 = weaponDamage2;
 	}
 
-	// write updated info for all objects
-	for (uint32 i = 0; i < numObjs; ++i) {
+	// update all object locations and stats
+	Common::Array<Obj *> &orderedObjs = _world->_orderedObjs;
+	if (numObjs != orderedObjs.size()) {
+		warning("objects number in file (%d) differs from the one in world (%d)", numObjs, orderedObjs.size());
+	}
+	for (uint32 i = 0; i < orderedObjs.size(); ++i) {
 		int resourceId = data->readSint16LE();
 		int locationResourceId = data->readSint16LE();
 		int ownerResourceId = data->readSint16LE();
@@ -446,42 +620,97 @@ int WageEngine::loadGame(int slotId) {
 		data->readByte();
 		data->readByte();
 
-		data->readByte(); // @ obj->_accuracy
-		data->readByte(); // @ obj->_value
-		data->readByte(); // @ obj->_type
-		data->readByte(); // @ obj->_damage
-		data->readByte(); // @ obj->_attackType
-		data->readSint16LE(); // @ obj->_numberOfUses
-	}
+		int accuracy = data->readByte();
+		int value = data->readByte();
+		int type = data->readByte();
+		int damage = data->readByte();
+		int attackType= data->readByte();
+		int numberOfUses = data->readSint16LE();
 
-	// the following is appended by ScummVM
-	if (data->pos() < data->size()) {
-		int scummvmWageFlag = data->readUint32BE();
+		Obj *obj = orderedObjs[i];
+		if (obj->_resourceId != resourceId) {
+			warning("loadGame(): updating objs: expected %d but got %d", obj->_resourceId, resourceId);
+			continue;
+		}
+
+		if (ownerResourceId != 0) {
+			obj->setCurrentOwner(getChrById(ownerResourceId));
+			if (obj->_currentOwner == nullptr)
+				warning("loadGame(): updating objs: owner not found - char with id %d", ownerResourceId);
+		} else {
+			obj->setCurrentScene(getSceneById(locationResourceId));
+			if (obj->_currentScene == nullptr)
+				warning("loadGame(): updating objs: scene with id %d not found", ownerResourceId);
+		}
+
+		obj->_accuracy = accuracy;
+		obj->_value = value;
+		obj->_type = type;
+		obj->_damage = damage;
+		obj->_attackType = attackType;
+		obj->_numberOfUses = numberOfUses;
+	}
 
-		if (scummvmWageFlag != WAGEflag) {
-			warning("Extra bytes after original save's information found, but that's not ScummVM's");
-			delete data;
-			return 0;
+	// update inventories and scene contents
+	for (uint32 i = 0; i < orderedObjs.size(); ++i) {
+		Obj *obj = orderedObjs[i];
+		Chr *chr = obj->_currentOwner;
+		if (chr != nullptr) {
+			chr->_inventory.push_back(obj);
+		} else {
+			Scene *scene = obj->_currentScene;
+			scene->_objs.push_back(obj);
 		}
+	}
 
-		// Write description of saved game, limited to WAGE_SAVEDGAME_DESCRIPTION_LEN characters + terminating NUL
-		const int WAGE_SAVEDGAME_DESCRIPTION_LEN = 127;
-		char description[WAGE_SAVEDGAME_DESCRIPTION_LEN + 1];
-		data->read(description, 128);
-		if (description[WAGE_SAVEDGAME_DESCRIPTION_LEN] != 0) {
-			warning("Description's last byte is not '\0'");
-			description[WAGE_SAVEDGAME_DESCRIPTION_LEN] = 0;
+	// update scene chrs
+	for (uint32 i = 0; i < orderedChrs.size(); ++i) {
+		Chr *chr = orderedChrs[i];
+		Scene *scene = chr->_currentScene;
+		scene->_chrs.push_back(chr);
+		if (chr != player) {
+			wearObjs(chr);
 		}
+	}
 
-		int version = data->readByte();
-		if (version != SAVEGAME_CURRENT_VERSION) {
-			warning("Reading version %d while current is %d", version, SAVEGAME_CURRENT_VERSION);
+	// move all worn helmets, shields, chest armors and spiritual
+	// armors to player
+	for (int type = 0; type < Chr::ChrArmorType::NUMBER_OF_ARMOR_TYPES; ++type) {
+		Obj *armor;
+
+		if (type == Chr::ChrArmorType::HEAD_ARMOR)
+			armor = getObjByOffset(helmetOffset, objsHexOffset);
+		else if (type == Chr::ChrArmorType::SHIELD_ARMOR)
+			armor = getObjByOffset(shieldOffset, objsHexOffset);
+		else if (type == Chr::ChrArmorType::BODY_ARMOR)
+			armor = getObjByOffset(armorOffset, objsHexOffset);
+		else
+			armor = getObjByOffset(spiritualArmorOffset, objsHexOffset);
+
+		if (armor != nullptr) {
+			_world->move(armor, player);
+			player->_armor[type] = armor;
 		}
+	}
+
+	//TODO: make sure that armor in the inventory gets put on if we are wearing it
+
+	_loopCount = loopNum;
 
-		// Thumbnail
-		Graphics::loadThumbnail(*data);
+	// let the engine know if there is a npc in the current scene
+	if (presCharOffset != 0xffff) {
+		_monster = getChrByOffset(presCharOffset, chrsHexOffset);
 	}
 
+	// java engine calls clearOutput(); here
+	// processTurn("look", NULL); called in Wage right after this loadGame()
+
+	// TODO: as you may see, aim, opponentAim or runCharOffset are not used anywhere
+	// I'm fixing the first two, as those are clearly not even mentioned anywhere
+	// the runCharOffset is mentioned up there as "not implemented case"
+	_aim = aim;
+	_opponentAim = opponentAim;
+
 	delete data;
 	return 0;
 }
-- 
cgit v1.2.3


From faed7c79730f13d98f7b68794293891404013b58 Mon Sep 17 00:00:00 2001
From: Alexander Tkachev
Date: Fri, 29 Jul 2016 11:35:36 +0600
Subject: WAGE: Fix Chr::ChrArmorType unknown identifier

It should've been `Chr` instead.
---
 engines/wage/saveload.cpp | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

(limited to 'engines/wage/saveload.cpp')

diff --git a/engines/wage/saveload.cpp b/engines/wage/saveload.cpp
index 654812a6b1..4889add4f0 100644
--- a/engines/wage/saveload.cpp
+++ b/engines/wage/saveload.cpp
@@ -212,16 +212,16 @@ int WageEngine::saveGame(const Common::String &fileName, const Common::String &d
 	out->writeSint32LE(GET_HEX_SCENE_OFFSET(player->_currentScene)); //getCurSceneHexOffset()
 
 	// wearing a helmet?
-	out->writeSint32LE(GET_HEX_OBJ_OFFSET(player->_armor[Chr::ChrArmorType::HEAD_ARMOR])); //helmetIndex
+	out->writeSint32LE(GET_HEX_OBJ_OFFSET(player->_armor[Chr::HEAD_ARMOR])); //helmetIndex
 
 	// holding a shield?
-	out->writeSint32LE(GET_HEX_OBJ_OFFSET(player->_armor[Chr::ChrArmorType::SHIELD_ARMOR])); //shieldIndex
+	out->writeSint32LE(GET_HEX_OBJ_OFFSET(player->_armor[Chr::SHIELD_ARMOR])); //shieldIndex
 
 	// wearing chest armor?
-	out->writeSint32LE(GET_HEX_OBJ_OFFSET(player->_armor[Chr::ChrArmorType::BODY_ARMOR])); //chestArmIndex
+	out->writeSint32LE(GET_HEX_OBJ_OFFSET(player->_armor[Chr::BODY_ARMOR])); //chestArmIndex
 
 	// wearing spiritual armor?
-	out->writeSint32LE(GET_HEX_OBJ_OFFSET(player->_armor[Chr::ChrArmorType::MAGIC_ARMOR])); //sprtArmIndex
+	out->writeSint32LE(GET_HEX_OBJ_OFFSET(player->_armor[Chr::MAGIC_ARMOR])); //sprtArmIndex
 
 	// TODO:
 	out->writeSint16LE(0xffff);	// ???? - always FFFF
@@ -675,14 +675,14 @@ int WageEngine::loadGame(int slotId) {
 
 	// move all worn helmets, shields, chest armors and spiritual
 	// armors to player
-	for (int type = 0; type < Chr::ChrArmorType::NUMBER_OF_ARMOR_TYPES; ++type) {
+	for (int type = 0; type < Chr::NUMBER_OF_ARMOR_TYPES; ++type) {
 		Obj *armor;
 
-		if (type == Chr::ChrArmorType::HEAD_ARMOR)
+		if (type == Chr::HEAD_ARMOR)
 			armor = getObjByOffset(helmetOffset, objsHexOffset);
-		else if (type == Chr::ChrArmorType::SHIELD_ARMOR)
+		else if (type == Chr::SHIELD_ARMOR)
 			armor = getObjByOffset(shieldOffset, objsHexOffset);
-		else if (type == Chr::ChrArmorType::BODY_ARMOR)
+		else if (type == Chr::BODY_ARMOR)
 			armor = getObjByOffset(armorOffset, objsHexOffset);
 		else
 			armor = getObjByOffset(spiritualArmorOffset, objsHexOffset);
-- 
cgit v1.2.3


From f50d9feee1e04a8ca9271b21859bda48172b7ff5 Mon Sep 17 00:00:00 2001
From: Alexander Tkachev
Date: Fri, 29 Jul 2016 17:48:23 +0600
Subject: WAGE: Make menu items show Save/Load dialog

Default ScummVM Save/Load dialog shows up on click.
---
 engines/wage/saveload.cpp | 38 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

(limited to 'engines/wage/saveload.cpp')

diff --git a/engines/wage/saveload.cpp b/engines/wage/saveload.cpp
index 4889add4f0..78e8d389d3 100644
--- a/engines/wage/saveload.cpp
+++ b/engines/wage/saveload.cpp
@@ -721,6 +721,13 @@ Common::String WageEngine::getSavegameFilename(int16 slotId) const {
 	return saveLoadSlot;
 }
 
+Common::Error WageEngine::loadGameState(int slot) {	
+	if (loadGame(slot) == 0)
+		return Common::kNoError;
+	else
+		return Common::kUnknownError;
+}
+
 Common::Error WageEngine::saveGameState(int slot, const Common::String &description) {
 	Common::String saveLoadSlot = getSavegameFilename(slot);
 	if (saveGame(saveLoadSlot, description) == 0)
@@ -729,4 +736,35 @@ Common::Error WageEngine::saveGameState(int slot, const Common::String &descript
 		return Common::kUnknownError;
 }
 
+bool WageEngine::scummVMSaveLoadDialog(bool isSave) {
+	if (!isSave) {
+		// do loading
+		GUI::SaveLoadChooser dialog = GUI::SaveLoadChooser(_("Load game:"), _("Load"), false);
+		int slot = dialog.runModalWithCurrentTarget();
+
+		if (slot < 0)
+			return true;
+
+		return loadGameState(slot).getCode() == Common::kNoError;
+	}
+
+	// do saving
+	GUI::SaveLoadChooser dialog = GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
+	int slot = dialog.runModalWithCurrentTarget();
+	Common::String desc = dialog.getResultString();
+
+	if (desc.empty()) {
+		// create our own description for the saved game, the user didnt enter it
+		desc = dialog.createDefaultSaveDescription(slot);
+	}
+
+	if (desc.size() > 28)
+		desc = Common::String(desc.c_str(), 28);
+
+	if (slot < 0)
+		return true;
+
+	return saveGameState(slot, desc).getCode() == Common::kNoError;
+}
+
 } // End of namespace Agi
-- 
cgit v1.2.3


From ef631c9e75f09e3a2a54701c1d6bf0e2dce5b71e Mon Sep 17 00:00:00 2001
From: Alexander Tkachev
Date: Thu, 4 Aug 2016 15:15:45 +0600
Subject: WAGE: Update saves format

Offset is added in the end of the file, so ScummVM would know where to
look for description, version, thumbnail information.
---
 engines/wage/saveload.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

(limited to 'engines/wage/saveload.cpp')

diff --git a/engines/wage/saveload.cpp b/engines/wage/saveload.cpp
index 78e8d389d3..c3b20bdf2f 100644
--- a/engines/wage/saveload.cpp
+++ b/engines/wage/saveload.cpp
@@ -335,6 +335,10 @@ int WageEngine::saveGame(const Common::String &fileName, const Common::String &d
 	}
 
 	// the following is appended by ScummVM
+	int32 appendixOffset = out->pos();
+	if (appendixOffset < 0) {
+		warning("OutSaveFile::pos() failed");
+	}
 	out->writeUint32BE(WAGEflag);
 
 	// Write description of saved game, limited to WAGE_SAVEDGAME_DESCRIPTION_LEN characters + terminating NUL
@@ -352,6 +356,8 @@ int WageEngine::saveGame(const Common::String &fileName, const Common::String &d
 	// Thumbnail
 	Graphics::saveThumbnail(*out);
 
+	out->writeUint32BE(appendixOffset);
+
 	// this one to make checking easier:
 	// it couldn't be added to the beginning
 	// and we won't be able to find it in the middle,
-- 
cgit v1.2.3


From 78f1d707c3284365e158eb0598cdc1ef9b1c0ab6 Mon Sep 17 00:00:00 2001
From: Eugene Sandulenko
Date: Sat, 20 Aug 2016 23:54:07 +0200
Subject: WAGE: Fix warnings

---
 engines/wage/saveload.cpp | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

(limited to 'engines/wage/saveload.cpp')

diff --git a/engines/wage/saveload.cpp b/engines/wage/saveload.cpp
index c3b20bdf2f..da10ad41c1 100644
--- a/engines/wage/saveload.cpp
+++ b/engines/wage/saveload.cpp
@@ -109,7 +109,7 @@ Obj *WageEngine::getObjByOffset(int offset, int objBaseOffset) const {
 		objIndex = (offset - objBaseOffset) / CHR_SIZE;
 	}
 
-	if (objIndex >= 0 && objIndex < _world->_orderedObjs.size()) {
+	if (objIndex >= 0 && (uint)objIndex < _world->_orderedObjs.size()) {
 		return _world->_orderedObjs[objIndex];
 	}
 
@@ -133,7 +133,7 @@ Chr *WageEngine::getChrByOffset(int offset, int chrBaseOffset) const {
 		chrIndex = (offset - chrBaseOffset) / CHR_SIZE;
 	}
 
-	if (chrIndex >= 0 && chrIndex < _world->_orderedChrs.size()) {
+	if (chrIndex >= 0 && (uint)chrIndex < _world->_orderedChrs.size()) {
 		return _world->_orderedChrs[chrIndex];
 	}
 
@@ -160,7 +160,7 @@ Scene *WageEngine::getSceneByOffset(int offset) const {
 			sceneIndex = 1 + (offset - SCENES_INDEX) / SCENE_SIZE;
 	}
 
-	if (sceneIndex >= 0 && sceneIndex < _world->_orderedScenes.size()) {
+	if (sceneIndex >= 0 && (uint)sceneIndex < _world->_orderedScenes.size()) {
 		if (sceneIndex == 0) return _world->_storageScene;
 		return _world->_orderedScenes[sceneIndex];
 	}
@@ -224,10 +224,10 @@ int WageEngine::saveGame(const Common::String &fileName, const Common::String &d
 	out->writeSint32LE(GET_HEX_OBJ_OFFSET(player->_armor[Chr::MAGIC_ARMOR])); //sprtArmIndex
 
 	// TODO:
-	out->writeSint16LE(0xffff);	// ???? - always FFFF
-	out->writeSint16LE(0xffff);	// ???? - always FFFF
-	out->writeSint16LE(0xffff);	// ???? - always FFFF
-	out->writeSint16LE(0xffff);	// ???? - always FFFF
+	out->writeUint16LE(0xffff);	// ???? - always FFFF
+	out->writeUint16LE(0xffff);	// ???? - always FFFF
+	out->writeUint16LE(0xffff);	// ???? - always FFFF
+	out->writeUint16LE(0xffff);	// ???? - always FFFF
 
 	// did a character just escape?
 	out->writeSint32LE(GET_HEX_CHR_OFFSET(_running)); //getRunCharHexOffset() == getHexOffsetForChr(running)
@@ -260,7 +260,7 @@ int WageEngine::saveGame(const Common::String &fileName, const Common::String &d
 	// write user vars
 	for (uint32 i = 0; i < 26 * 9; ++i)
 		out->writeSint16LE(playerContext._userVariables[i]);
-	
+
 	// write updated info for all scenes
 	Common::Array<Scene *> &orderedScenes = _world->_orderedScenes;
 	for (uint32 i = 0; i < orderedScenes.size(); ++i) {
@@ -520,12 +520,12 @@ int WageEngine::loadGame(int slotId) {
 
 	// set all user variables
 	for (uint32 i = 0; i < 26 * 9; ++i) {
-		playerContext._userVariables[i] = data->readSint16LE();	
+		playerContext._userVariables[i] = data->readSint16LE();
 	}
 
 	// update all scene stats
 	Common::Array<Scene *> &orderedScenes = _world->_orderedScenes;
-	if (numScenes != orderedScenes.size()) {
+	if ((uint)numScenes != orderedScenes.size()) {
 		warning("scenes number in file (%d) differs from the one in world (%d)", numScenes, orderedScenes.size());
 	}
 	for (uint32 i = 0; i < orderedScenes.size(); ++i) {
@@ -559,7 +559,7 @@ int WageEngine::loadGame(int slotId) {
 
 	// update all char locations and stats
 	Common::Array<Chr *> &orderedChrs = _world->_orderedChrs;
-	if (numChars != orderedChrs.size()) {
+	if ((uint)numChars != orderedChrs.size()) {
 		warning("characters number in file (%d) differs from the one in world (%d)", numChars, orderedChrs.size());
 	}
 	for (uint32 i = 0; i < orderedChrs.size(); ++i) {
@@ -613,7 +613,7 @@ int WageEngine::loadGame(int slotId) {
 
 	// update all object locations and stats
 	Common::Array<Obj *> &orderedObjs = _world->_orderedObjs;
-	if (numObjs != orderedObjs.size()) {
+	if ((uint)numObjs != orderedObjs.size()) {
 		warning("objects number in file (%d) differs from the one in world (%d)", numObjs, orderedObjs.size());
 	}
 	for (uint32 i = 0; i < orderedObjs.size(); ++i) {
@@ -727,7 +727,7 @@ Common::String WageEngine::getSavegameFilename(int16 slotId) const {
 	return saveLoadSlot;
 }
 
-Common::Error WageEngine::loadGameState(int slot) {	
+Common::Error WageEngine::loadGameState(int slot) {
 	if (loadGame(slot) == 0)
 		return Common::kNoError;
 	else
-- 
cgit v1.2.3