[Scummvm-git-logs] scummvm master -> 1880b37100511988b292d673ee32630e031f6dcb

bluegr noreply at scummvm.org
Thu Dec 30 11:28:13 UTC 2021


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

Summary:
1880b37100 BURIED: Add metadata to saved games - FR #12889


Commit: 1880b37100511988b292d673ee32630e031f6dcb
    https://github.com/scummvm/scummvm/commit/1880b37100511988b292d673ee32630e031f6dcb
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2021-12-30T13:27:49+02:00

Commit Message:
BURIED: Add metadata to saved games - FR #12889

- Saved games now contain all metadata, including thumbnails, creation
date and play time
- Savesd games are now named buried.###, and they are compatible among
all versions
- Saved games are now sorted by slot, like in other engines, and are no
longer sorted alphabetically as before
- The engine checks on startup for saves using the original format and
converts them to the new format, thus it's possible to import saved
games from the original game
- The currently selected item is now stored in the saved game, thus
crashes that were related to loading a game with fewer items than the
currently selected item no longer occur

Changed paths:
    engines/buried/buried.cpp
    engines/buried/buried.h
    engines/buried/frame_window.h
    engines/buried/global_flags.h
    engines/buried/inventory_window.cpp
    engines/buried/inventory_window.h
    engines/buried/metaengine.cpp
    engines/buried/saveload.cpp


diff --git a/engines/buried/buried.cpp b/engines/buried/buried.cpp
index f4411088d8d..c7bfd749dc7 100644
--- a/engines/buried/buried.cpp
+++ b/engines/buried/buried.cpp
@@ -132,6 +132,8 @@ Common::Error BuriedEngine::run() {
 	_mainWindow = new FrameWindow(this);
 	_mainWindow->showWindow(Window::kWindowShow);
 
+	checkForOriginalSavedGames();
+
 	if (isDemo()) {
 		((FrameWindow *)_mainWindow)->showTitleSequence();
 		((FrameWindow *)_mainWindow)->showMainMenu();
diff --git a/engines/buried/buried.h b/engines/buried/buried.h
index 7216c79fb26..b02a98e631b 100644
--- a/engines/buried/buried.h
+++ b/engines/buried/buried.h
@@ -143,11 +143,11 @@ public:
 	// Save/Load
 	bool canLoadGameStateCurrently();
 	bool canSaveGameStateCurrently();
-	Common::Error loadGameState(int slot);
-	Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave);
-	static Common::StringArray listSaveFiles();
-	bool loadState(Common::SeekableReadStream *saveFile, Location &location, GlobalFlags &flags, Common::Array<int> &inventoryItems);
-	bool saveState(Common::WriteStream *saveFile, Location &location, GlobalFlags &flags, Common::Array<int> &inventoryItems);
+	Common::String getSaveStateName(int slot) const override {
+		return Common::String::format("buried.%03d", slot);
+	}
+	Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
+	Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override;
 	void handleSaveDialog();
 	void handleRestoreDialog();
 
@@ -184,6 +184,10 @@ private:
 	// Saves
 	bool syncLocation(Common::Serializer &s, Location &location);
 	bool syncGlobalFlags(Common::Serializer &s, GlobalFlags &flags);
+	Common::Error syncSaveData(Common::Serializer &ser);
+	Common::Error syncSaveData(Common::Serializer &ser, Location &location, GlobalFlags &flags, Common::Array<int> &inventoryItems);
+	void checkForOriginalSavedGames();
+	void convertSavedGame(Common::String oldFile, Common::String newFile);
 };
 
 // Macro for creating a version field
diff --git a/engines/buried/frame_window.h b/engines/buried/frame_window.h
index 91190f09df5..a2ad6110b7f 100644
--- a/engines/buried/frame_window.h
+++ b/engines/buried/frame_window.h
@@ -48,7 +48,6 @@ public:
 	bool showCompletionScene(GlobalFlags &globalFlags);
 	bool showCredits();
 	bool showOverview();
-	bool notifyUserOfFrameCycling();
 	bool setTimerPause(bool pause);
 
 	bool onEraseBackground();
diff --git a/engines/buried/global_flags.h b/engines/buried/global_flags.h
index 2632551987c..5126e7b4a84 100644
--- a/engines/buried/global_flags.h
+++ b/engines/buried/global_flags.h
@@ -93,7 +93,8 @@ struct GlobalFlags {
 	byte myMCStingerChannelID;          // 54
 	byte faStingerID;                   // 55
 	byte faStingerChannelID;            // 56
-	byte unused0[3];                    // 57-59
+	uint16 curItem;                     // ScummVM enhancement, originally unused bytes 57-58
+	byte unused0;                       // 59
 	uint32 cgMWCatapultData;            // 60-63
 	uint32 cgMWCatapultOffset;          // 64-67
 	byte cgTSTriedDoor;                 // 68
diff --git a/engines/buried/inventory_window.cpp b/engines/buried/inventory_window.cpp
index ef23a3df8d1..0fcf6c5d30f 100644
--- a/engines/buried/inventory_window.cpp
+++ b/engines/buried/inventory_window.cpp
@@ -68,7 +68,7 @@ InventoryWindow::InventoryWindow(BuriedEngine *vm, Window *parent) : Window(vm,
 		_itemArray.push_back(kItemBioChipJump);
 	}
 
-	_curItem = 0;
+	setCurItem(0);
 
 	_infoWindow = nullptr;
 	_letterViewWindow = nullptr;
@@ -142,7 +142,8 @@ bool InventoryWindow::rebuildPreBuffer() {
 
 	if (!_itemArray.empty()) {
 		// Draw the icon for the current item
-		Graphics::Surface *icon = _vm->_gfx->getBitmap(IDB_PICON_BITMAP_BASE + _itemArray[_curItem]);
+		const uint16 curItem = getCurItem();
+		Graphics::Surface *icon = _vm->_gfx->getBitmap(IDB_PICON_BITMAP_BASE + _itemArray[curItem]);
 		_vm->_gfx->crossBlit(_background, 17, 8, icon->w, icon->h, icon, 0, 0);
 		icon->free();
 		delete icon;
@@ -160,7 +161,7 @@ bool InventoryWindow::addItem(int itemID) {
 	// Find the new position, and set the current selection to that
 	for (int i = 0; i < (int)_itemArray.size(); i++) {
 		if (_itemArray[i] == itemID) {
-			_curItem = i;
+			setCurItem(i);
 			break;
 		}
 	}
@@ -220,8 +221,10 @@ bool InventoryWindow::removeItem(int itemID) {
 	if (!found)
 		return false;
 
-	if (_curItem >= (int)_itemArray.size())
-		_curItem--;
+	const uint16 curItem = getCurItem();
+	if (curItem >= (int)_itemArray.size()) {
+		setCurItem(curItem - 1);
+	}
 
 	rebuildPreBuffer();
 	invalidateWindow(false);
@@ -300,8 +303,9 @@ void InventoryWindow::onPaint() {
 
 	// Draw inventory item names
 	uint32 textColor = _vm->_gfx->getColor(212, 109, 0);
+	const uint16 curItem = getCurItem();
 	for (int i = -2; i < 3; i++) {
-		if ((i + _curItem) >= 0 && (i + _curItem) < (int)_itemArray.size()) {
+		if ((i + curItem) >= 0 && (i + curItem) < (int)_itemArray.size()) {
 			Common::Rect textRect = Common::Rect(120, (i + 2) * 13 + 8, 254, (i + 3) * 13 + 8);
 
 			if (_vm->getLanguage() == Common::JA_JPN) {
@@ -311,7 +315,7 @@ void InventoryWindow::onPaint() {
 			}
 
 			textRect.translate(absoluteRect.left, absoluteRect.top);
-			Common::String text = _vm->getString(IDES_ITEM_TITLE_BASE + _itemArray[_curItem + i]);
+			Common::String text = _vm->getString(IDES_ITEM_TITLE_BASE + _itemArray[curItem + i]);
 			_vm->_gfx->renderText(_vm->_gfx->getScreen(), _textFont, text, textRect.left, textRect.top, textRect.width(), textRect.height(), textColor, _fontHeight);
 		}
 	}
@@ -332,7 +336,7 @@ void InventoryWindow::onLButtonDown(const Common::Point &point, uint flags) {
 
 	bool redraw = false;
 	if (up.contains(point)) {
-		if (_curItem > 0) {
+		if (getCurItem() > 0) {
 			_upSelected = true;
 			redraw = true;
 			_scrollTimer = setTimer(250);
@@ -340,7 +344,7 @@ void InventoryWindow::onLButtonDown(const Common::Point &point, uint flags) {
 	}
 
 	if (down.contains(point)) {
-		if (_curItem < ((int)_itemArray.size() - 1)) {
+		if (getCurItem() < ((int)_itemArray.size() - 1)) {
 			_downSelected = true;
 			redraw = true;
 			_scrollTimer = setTimer(250);
@@ -360,7 +364,8 @@ void InventoryWindow::onLButtonDown(const Common::Point &point, uint flags) {
 	}
 
 	if (picon.contains(point) && !_itemArray.empty() && !_infoWindow) {
-		int itemID = _itemArray[_curItem];
+		const uint16 curItem = getCurItem();
+		int itemID = _itemArray[curItem];
 
 		switch (itemID) {
 		case kItemBioChipAI:
@@ -407,7 +412,7 @@ void InventoryWindow::onLButtonDown(const Common::Point &point, uint flags) {
 				return;
 			}
 
-			InventoryElement staticItemData = getItemStaticData(_itemArray[_curItem]);
+			InventoryElement staticItemData = getItemStaticData(_itemArray[curItem]);
 
 			if (staticItemData.firstDragID < 0)
 				return;
@@ -466,13 +471,16 @@ void InventoryWindow::onLButtonUp(const Common::Point &point, uint flags) {
 		inventoryText[i] = Common::Rect(120, i * 13 + 8, 254, (i + 1) * 13 + 8);
 
 	bool redraw = _upSelected || _downSelected || _magSelected;
+	uint16 curItem = getCurItem();
 
 	if (up.contains(point) && _upSelected) {
-		if (_curItem > 0)
-			_curItem--;
+		if (curItem > 0) {
+			curItem--;
+			setCurItem(curItem);
+		}
 
 		if (_infoWindow)
-			_infoWindow->changeCurrentItem(_itemArray[_curItem]);
+			_infoWindow->changeCurrentItem(_itemArray[curItem]);
 
 		if (_scrollTimer != 0) {
 			killTimer(_scrollTimer);
@@ -481,11 +489,13 @@ void InventoryWindow::onLButtonUp(const Common::Point &point, uint flags) {
 	}
 
 	if (down.contains(point) && _downSelected) {
-		if (_curItem < ((int)_itemArray.size() - 1))
-			_curItem++;
+		if (curItem < ((int)_itemArray.size() - 1)) {
+			curItem++;
+			setCurItem(curItem);
+		}
 
 		if (_infoWindow)
-			_infoWindow->changeCurrentItem(_itemArray[_curItem]);
+			_infoWindow->changeCurrentItem(_itemArray[curItem]);
 
 		if (_scrollTimer != 0) {
 			killTimer(_scrollTimer);
@@ -499,7 +509,7 @@ void InventoryWindow::onLButtonUp(const Common::Point &point, uint flags) {
 		if (_infoWindow) {
 			destroyInfoWindow();
 		} else {
-			_infoWindow = new InventoryInfoWindow(_vm, ((GameUIWindow *)_parent)->_sceneViewWindow, _itemArray[_curItem]);
+			_infoWindow = new InventoryInfoWindow(_vm, ((GameUIWindow *)_parent)->_sceneViewWindow, _itemArray[curItem]);
 			((GameUIWindow *)_parent)->_sceneViewWindow->infoWindowDisplayed(true);
 			_infoWindow->setWindowPos(kWindowPosTop, 0, 0, 0, 0, kWindowPosShowWindow | kWindowPosNoMove | kWindowPosNoSize);
 			_magSelected = true;
@@ -508,13 +518,15 @@ void InventoryWindow::onLButtonUp(const Common::Point &point, uint flags) {
 	}
 
 	if (_textSelected >= 0) {
+		const uint16 curItem = getCurItem();
+
 		for (int i = 0; i < 5; i++) {
-			if (inventoryText[i].contains(point) && (_curItem + i - 2) >= 0 && (_curItem + i - 2) < (int)_itemArray.size() && i == _textSelected) {
-				_curItem += i - 2;
+			if (inventoryText[i].contains(point) && (curItem + i - 2) >= 0 && (curItem + i - 2) < (int)_itemArray.size() && i == _textSelected) {
+				setCurItem(curItem + i - 2);
 				redraw = true;
 
 				if (_infoWindow)
-					_infoWindow->changeCurrentItem(_itemArray[_curItem]);
+					_infoWindow->changeCurrentItem(_itemArray[curItem]);
 			}
 		}
 	}
@@ -717,15 +729,16 @@ bool InventoryWindow::onSetCursor(uint message) {
 }
 
 void InventoryWindow::onTimer(uint timer) {
+	const uint16 curItem = getCurItem();
 	if (_upSelected) {
-		if (_curItem > 0) {
-			_curItem--;
+		if (curItem > 0) {
+			setCurItem(curItem - 1);
 			rebuildPreBuffer();
 			invalidateWindow(false);
 		}
 	} else if (_downSelected) {
-		if (_curItem < (int)_itemArray.size() - 1) {
-			_curItem++;
+		if (curItem < (int)_itemArray.size() - 1) {
+			setCurItem(curItem + 1);
 			rebuildPreBuffer();
 			invalidateWindow(false);
 		}
@@ -773,6 +786,22 @@ bool InventoryWindow::destroyInfoWindow() {
 void InventoryWindow::setItemArray(const Common::Array<int> &array) {
 	_itemArray = array;
 	Common::sort(_itemArray.begin(), _itemArray.end());
+
+	// Sanity check
+	uint16 curItem = getCurItem();
+	if (curItem >= _itemArray.size()) {
+		warning("Invalid current item, resetting it to the first one");
+		setCurItem(0);
+	}
 }
 
+void InventoryWindow::setCurItem(uint16 itemId) {
+	GlobalFlags &globalFlags = ((GameUIWindow *)_parent)->_sceneViewWindow->getGlobalFlags();
+	globalFlags.curItem = itemId;
+}
+
+uint16 InventoryWindow::getCurItem() const {
+	GlobalFlags &globalFlags = ((GameUIWindow *)_parent)->_sceneViewWindow->getGlobalFlags();
+	return globalFlags.curItem;
+}
 } // End of namespace Buried
diff --git a/engines/buried/inventory_window.h b/engines/buried/inventory_window.h
index 74ee13eb61f..be7d551aefb 100644
--- a/engines/buried/inventory_window.h
+++ b/engines/buried/inventory_window.h
@@ -52,7 +52,6 @@ public:
 	bool removeItem(int itemID);
 
 	bool startDraggingNewItem(int itemID, const Common::Point &pointStart);
-	int getCurrentItemID() { return _itemArray[_curItem]; }
 	bool isItemInInventory(int itemID);
 	InventoryElement getItemStaticData(int itemID);
 	int getItemCount() { return _itemArray.size(); }
@@ -75,11 +74,13 @@ public:
 	void onTimer(uint timer);
 
 private:
+	void setCurItem(uint16 itemId);
+	uint16 getCurItem() const;
+
 	Graphics::Font *_textFont;
 	int _fontHeight;
 	Graphics::Surface *_background;
 	Common::Array<int> _itemArray;
-	int _curItem;
 
 	bool _magSelected;
 	bool _upSelected;
diff --git a/engines/buried/metaengine.cpp b/engines/buried/metaengine.cpp
index b94320c55ac..a7bb1676a61 100644
--- a/engines/buried/metaengine.cpp
+++ b/engines/buried/metaengine.cpp
@@ -80,49 +80,24 @@ public:
 
 	bool hasFeature(MetaEngineFeature f) const override;
 	Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
-	SaveStateList listSaves(const char *target) const override;
 	int getMaximumSaveSlot() const override { return 999; }
-	void removeSaveState(const char *target, int slot) const override;
 	Common::String getSavegameFile(int saveGameIdx, const char *target) const override {
-		if (!target)
-			target = getEngineId();
-		if (saveGameIdx == kSavegameFilePattern)
-			return Common::String::format("buried-*.sav");
-		else
-			return Common::String::format("buried-%s.sav", target);
+		// We set a standard target because saves are compatible among all versions
+		return AdvancedMetaEngine::getSavegameFile(saveGameIdx, "buried");
 	}
 };
 
 bool BuriedMetaEngine::hasFeature(MetaEngineFeature f) const {
 	return
-		(f == kSupportsListSaves)
-		|| (f == kSupportsLoadingDuringStartup)
-		|| (f == kSupportsDeleteSave);
-}
-
-SaveStateList BuriedMetaEngine::listSaves(const char *target) const {
-	// The original had no pattern, so the user must rename theirs
-	// Note that we ignore the target because saves are compatible between
-	// all versions
-	Common::StringArray fileNames = Buried::BuriedEngine::listSaveFiles();
-
-	SaveStateList saveList;
-	for (uint32 i = 0; i < fileNames.size(); i++) {
-		// Isolate the description from the file name
-		Common::String desc = fileNames[i].c_str() + 7;
-		for (int j = 0; j < 4; j++)
-			desc.deleteLastChar();
-
-		saveList.push_back(SaveStateDescriptor(this, i, desc));
-	}
-
-	return saveList;
-}
-
-void BuriedMetaEngine::removeSaveState(const char *target, int slot) const {
-	// See listSaves() for info on the pattern
-	const Common::StringArray &fileNames = Buried::BuriedEngine::listSaveFiles();
-	g_system->getSavefileManager()->removeSavefile(fileNames[slot].c_str());
+		f == kSupportsListSaves ||
+		f == kSupportsLoadingDuringStartup ||
+		f == kSupportsDeleteSave ||
+		f == kSavesSupportMetaInfo ||
+		f == kSavesSupportThumbnail ||
+		f == kSavesSupportCreationDate ||
+		f == kSavesSupportPlayTime ||
+		f == kSimpleSavesNames ||
+		f == kSavesUseExtendedFormat;
 }
 
 Common::Error BuriedMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
diff --git a/engines/buried/saveload.cpp b/engines/buried/saveload.cpp
index 41fd3249544..9f732a5c26d 100644
--- a/engines/buried/saveload.cpp
+++ b/engines/buried/saveload.cpp
@@ -25,6 +25,7 @@
 #include "common/scummsys.h"
 #include "common/config-manager.h"
 #include "common/error.h"
+#include "common/file.h"
 #include "common/savefile.h"
 #include "common/serializer.h"
 #include "common/system.h"
@@ -32,21 +33,19 @@
 #include "gui/message.h"
 #include "gui/saveload.h"
 
+#include "buried/biochip_right.h"
 #include "buried/buried.h"
 #include "buried/frame_window.h"
 #include "buried/gameui.h"
 #include "buried/global_flags.h"
+#include "buried/graphics.h"
 #include "buried/inventory_window.h"
 #include "buried/navdata.h"
 #include "buried/scene_view.h"
 
 namespace Buried {
 
-Common::StringArray BuriedEngine::listSaveFiles() {
-	Common::StringArray fileNames = g_system->getSavefileManager()->listSavefiles("buried-*.sav");
-	Common::sort(fileNames.begin(), fileNames.end());
-	return fileNames;
-}
+#define SAVEGAME_CURRENT_VERSION 1
 
 bool BuriedEngine::canLoadGameStateCurrently() {
 	return !isDemo() && _mainWindow && !_yielding;
@@ -56,141 +55,218 @@ bool BuriedEngine::canSaveGameStateCurrently() {
 	return !isDemo() && _mainWindow && !_yielding && ((FrameWindow *)_mainWindow)->isGameInProgress();
 }
 
-Common::Error BuriedEngine::loadGameState(int slot) {
-	Common::StringArray fileNames = listSaveFiles();
-	Common::InSaveFile *loadFile = _saveFileMan->openForLoading(fileNames[slot]);
-	if (!loadFile)
-		return Common::kUnknownError;
+void BuriedEngine::checkForOriginalSavedGames() {
+	Common::StringArray fileNames = _saveFileMan->listSavefiles("buried-*.sav");
+	Common::StringArray newFileNames = _saveFileMan->listSavefiles("buried.###");
+	Common::sort(newFileNames.begin(), newFileNames.end());
+	if (fileNames.size() == 0)
+		return;
+
+	GUI::MessageDialog dialog(
+		_("ScummVM found that you have saved games that should be converted from the original saved game format.\n"
+		  "The original saved game format is no longer supported directly, so you will not be able to load your games if you don't convert them.\n\n"
+		  "Press OK to convert them now, otherwise you will be asked again the next time you start the game.\n"),
+		_("OK"), _("Cancel"));
+
+	int choice = dialog.runModal();
+	if (choice != GUI::kMessageOK)
+		return;
+
+	// Convert every save slot we find with the original naming scheme
+	for (Common::StringArray::const_iterator file = fileNames.begin(); file != fileNames.end(); ++file) {
+		int slotNum = 1;
+		if (newFileNames.size() > 0) {
+			Common::String lastFile = newFileNames.back();
+			const char *slotStr = lastFile.c_str() + lastFile.size() - 3;
+			slotNum = atoi(slotStr) + 1;
+		}
+
+		Common::String newFile = getMetaEngine()->getSavegameFile(slotNum);
+		convertSavedGame(*file, newFile);
+		newFileNames.push_back(newFile);
+	}
+}
 
+enum {
+	kSavedGameHeaderSize = 9,
+	kSavedGameHeaderSizeAlt = 7
+};
+
+void BuriedEngine::convertSavedGame(Common::String oldFile, Common::String newFile) {
+	static const byte s_savedGameHeader[kSavedGameHeaderSize] = {'B', 'I', 'T', 'M', 'P', 'C', 0, 5, 0};
 	Location location;
 	GlobalFlags flags;
 	Common::Array<int> inventoryItems;
-	if (!loadState(loadFile, location, flags, inventoryItems)) {
-		delete loadFile;
-		return Common::kUnknownError;
-	}
+	byte header[9];
 
-	// Done with the file
-	delete loadFile;
+	debug("Converting %s to %s", oldFile.c_str(), newFile.c_str());
 
-	if (isTrial() && location.timeZone != 4) {
-		// Display a message preventing the user from loading a non-apartment
-		// saved game in the trial version
-		GUI::MessageDialog dialog("ERROR: The location in this saved game is not included in this version of Buried in Time");
-		dialog.runModal();
+	// Read original/old saved game
+	// Isolate the description from the file name
+	Common::String desc = oldFile.c_str() + 7;
+	for (int j = 0; j < 4; j++)
+		desc.deleteLastChar();
 
-		// Don't return an error. It's an "error" that we can't load,
-		// but we're still in a valid state. The message above will
-		// be displayed instead of the usual GUI load error.
-		return Common::kNoError;
-	}
+	Common::InSaveFile *inFile = _saveFileMan->openForLoading(oldFile);
+	inFile->read(header, kSavedGameHeaderSize);
 
-	((FrameWindow *)_mainWindow)->loadFromState(location, flags, inventoryItems);
-	return Common::kNoError;
-}
+	// Only compare the first 6 bytes
+	// Win95 version of the game output garbage as the last two bytes
+	if (inFile->eos() || memcmp(header, s_savedGameHeader, kSavedGameHeaderSizeAlt) != 0) {
+		delete inFile;
+		warning("Saved game %s is using an unsupported format, skipping", oldFile.c_str());
+		return;
+	}
+	
+	// Set necessary properties from the old save
+	Common::Serializer inS(inFile, nullptr);
+	Common::Error res = syncSaveData(inS, location, flags, inventoryItems);
+	delete inFile;
+	if (res.getCode() != Common::kNoError) {
+		warning("Error reading data from saved game %s, skipping", oldFile.c_str());
+		return;
+	}
 
-static bool isValidSaveFileChar(char c) {
-	// Limit it to letters, digits, and a few other characters that should be safe
-	return Common::isAlnum(c) || c == ' ' || c == '_' || c == '+' || c == '-' || c == '.';
-}
+	flags.curItem = 0;	// did not persist in the original format, so set it here
 
-static bool isValidSaveFileName(const Common::String &desc) {
-	for (uint32 i = 0; i < desc.size(); i++)
-		if (!isValidSaveFileChar(desc[i]))
-			return false;
+	// Write the new saved format
+	Common::OutSaveFile *outFile = _saveFileMan->openForSaving(newFile);
+	if (!outFile) {
+		warning("Error creating new save file %s", newFile.c_str());
+		return;
+	}
 
-	return true;
-}
+	const byte version = SAVEGAME_CURRENT_VERSION;
+	Common::Serializer outS(nullptr, outFile);
+	outS.setVersion(version);
+	outFile->writeByte(version);
 
-Common::Error BuriedEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
-	if (!isValidSaveFileName(desc))
-		return Common::Error(Common::kCreatingFileFailed, _("Invalid save file name"));
+	if (syncSaveData(outS, location, flags, inventoryItems).getCode() == Common::kNoError) {
+		getMetaEngine()->appendExtendedSave(outFile, getTotalPlayTime() / 1000, desc, false);
 
-	Common::String output = Common::String::format("buried-%s.sav", desc.c_str());
-	Common::OutSaveFile *saveFile = _saveFileMan->openForSaving(output, false);
-	if (!saveFile)
-		return Common::kUnknownError;
+		outFile->finalize();
+		delete outFile;
 
-	GameUIWindow *gameUI = (GameUIWindow *)((FrameWindow *)_mainWindow)->getMainChildWindow();
+		// Delete the old saved game
+		_saveFileMan->removeSavefile(oldFile);
+	} else {
+		delete outFile;
 
-	Location location;
-	gameUI->_sceneViewWindow->getCurrentSceneLocation(location);
-	GlobalFlags &flags = gameUI->_sceneViewWindow->getGlobalFlags();
-	Common::Array<int> &inventoryItems = gameUI->_inventoryWindow->getItemArray();
+		warning("Error writing data to saved game %s, skipping", newFile.c_str());
 
-	if (!saveState(saveFile, location, flags, inventoryItems)) {
-		delete saveFile;
-		return Common::kUnknownError;
+		// The newly created saved game is corrupted, delete it
+		_saveFileMan->removeSavefile(newFile);
 	}
-
-	delete saveFile;
-	return Common::kNoError;
 }
 
-enum {
-	kSavedGameHeaderSize = 9,
-	kSavedGameHeaderSizeAlt = 7
-};
-
-static const byte s_savedGameHeader[kSavedGameHeaderSize] = { 'B', 'I', 'T', 'M', 'P', 'C', 0, 5, 0 };
-
-bool BuriedEngine::loadState(Common::SeekableReadStream *saveFile, Location &location, GlobalFlags &flags, Common::Array<int> &inventoryItems) {
-	byte header[9];
-	saveFile->read(header, kSavedGameHeaderSize);
-
-	// Only compare the first 6 bytes
-	// Win95 version of the game output garbage as the last two bytes
-	if (saveFile->eos() || memcmp(header, s_savedGameHeader, kSavedGameHeaderSizeAlt) != 0)
-		return false;
+Common::Error BuriedEngine::loadGameStream(Common::SeekableReadStream *stream) {
+	const byte version = stream->readByte();
+	if (version > SAVEGAME_CURRENT_VERSION) {
+		GUI::MessageDialog dialog(_s("Saved game was created with a newer version of ScummVM. Unable to load."));
+		dialog.runModal();
+		return Common::kUnknownError;
+	}
 
-	Common::Serializer s(saveFile, nullptr);
+	Common::Serializer ser(stream, nullptr);
+	ser.setVersion(version);
 
-	if (!syncLocation(s, location))
-		return false;
+	return syncSaveData(ser);
+}
 
-	if (saveFile->eos())
-		return false;
+Common::Error BuriedEngine::saveGameStream(Common::WriteStream *stream, bool isAutosave) {
+	const byte version = SAVEGAME_CURRENT_VERSION;
+	Common::Serializer ser(nullptr, stream);
+	ser.setVersion(version);
+	stream->writeByte(version);
 
-	if (!syncGlobalFlags(s, flags))
-		return false;
+	GameUIWindow *gameUI = (GameUIWindow *)((FrameWindow *)_mainWindow)->getMainChildWindow();
+	gameUI->_bioChipRightWindow->destroyBioChipViewWindow();	// to capture a game screenshot
+	_gfx->updateScreen();
 
-	if (saveFile->eos())
-		return false;
+	return syncSaveData(ser);
+}
 
-	uint16 itemCount = saveFile->readUint16LE();
+Common::Error BuriedEngine::syncSaveData(Common::Serializer &ser) {
+	Common::Error result;
+
+	if (ser.isLoading()) {
+		Location location;
+		GlobalFlags flags;
+		Common::Array<int> inventoryItems;
+
+		result = syncSaveData(ser, location, flags, inventoryItems);
+
+		if (isTrial() && location.timeZone != 4) {
+			// Display a message preventing the user from loading a non-apartment
+			// saved game in the trial version
+			GUI::MessageDialog dialog("ERROR: The location in this saved game is not included in this version of Buried in Time");
+			dialog.runModal();
+		} else {
+			((FrameWindow *)_mainWindow)->loadFromState(location, flags, inventoryItems);
+		}
+	} else {
+		Location location;
+		GameUIWindow *gameUI = (GameUIWindow *)((FrameWindow *)_mainWindow)->getMainChildWindow();
+		gameUI->_sceneViewWindow->getCurrentSceneLocation(location);
+		GlobalFlags &flags = gameUI->_sceneViewWindow->getGlobalFlags();
+		Common::Array<int> &inventoryItems = gameUI->_inventoryWindow->getItemArray();
+
+		result = syncSaveData(ser, location, flags, inventoryItems);
+	}
 
-	if (saveFile->eos())
-		return false;
+	return result;
+}
 
-	inventoryItems.clear();
-	for (uint16 i = 0; i < itemCount; i++)
-		inventoryItems.push_back(saveFile->readUint16LE());
+Common::Error BuriedEngine::syncSaveData(Common::Serializer &ser, Location &location, GlobalFlags &flags, Common::Array<int> &inventoryItems) {
+	if (!syncLocation(ser, location)) {
+		warning("Error while synchronizing location data");
+		return Common::kUnknownError;
+	}
 
-	return !saveFile->eos();
-}
+	if (!syncGlobalFlags(ser, flags)) {
+		warning("Error while synchronizing global flag data");
+		return Common::kUnknownError;
+	}
 
-bool BuriedEngine::saveState(Common::WriteStream *saveFile, Location &location, GlobalFlags &flags, Common::Array<int> &inventoryItems) {
-	saveFile->write(s_savedGameHeader, kSavedGameHeaderSize);
+	if (ser.err()) {
+		warning("Error while synchronizing data");
+		return Common::kUnknownError;
+	}
 
-	Common::Serializer s(nullptr, saveFile);
+	uint16 itemCount = inventoryItems.size();
+	ser.syncAsUint16LE(itemCount);
 
-	if (!syncLocation(s, location))
-		return false;
+	if (ser.isLoading()) {
+		inventoryItems.clear();
+		inventoryItems.reserve(itemCount);
+	}
 
-	if (!syncGlobalFlags(s, flags))
-		return false;
+	for (uint16 i = 0; i < itemCount; i++) {
+		uint16 itemId = 0;
 
-	saveFile->writeUint16LE(inventoryItems.size());
+		if (ser.isLoading()) {
+			ser.syncAsUint16LE(itemId);
+			inventoryItems.push_back(itemId);
+		} else {
+			itemId = inventoryItems[i];
+			ser.syncAsUint16LE(itemId);
+		}
+	}
 
-	for (uint16 i = 0; i < inventoryItems.size(); i++)
-		saveFile->writeUint16LE(inventoryItems[i]);
+	if (ser.isSaving()) {
+		// Fill in remaining items with all zeroes
+		uint16 fillItems = 50 - itemCount;
+		uint16 filler = 0;
+		while (fillItems--)
+			ser.syncAsUint16LE(filler);
+	}
 
-	// Fill in remaining items with all zeroes
-	uint16 fillItems = 50 - inventoryItems.size();
-	while (fillItems--)
-		saveFile->writeUint16LE(0);
+	if (ser.err()) {
+		warning("Error while synchronizing inventory data");
+		return Common::kUnknownError;
+	}
 
-	return true;
+	return Common::kNoError;
 }
 
 // Since we can't take the address of a uint16 or uint32 from
@@ -288,7 +364,8 @@ bool BuriedEngine::syncGlobalFlags(Common::Serializer &s, GlobalFlags &flags) {
 	s.syncAsByte(flags.myMCStingerChannelID);
 	s.syncAsByte(flags.faStingerID);
 	s.syncAsByte(flags.faStingerChannelID);
-	s.syncBytes(flags.unused0, sizeof(flags.unused0));
+	SYNC_FLAG_UINT16(curItem);
+	s.syncAsByte(flags.unused0);
 	SYNC_FLAG_UINT32(cgMWCatapultData);
 	SYNC_FLAG_UINT32(cgMWCatapultOffset);
 	s.syncAsByte(flags.cgTSTriedDoor);




More information about the Scummvm-git-logs mailing list