[Scummvm-git-logs] scummvm master -> 18f2ab00cdbb90a02983f3d9850fb7a19784e0c4

dreammaster noreply at scummvm.org
Fri Apr 1 05:16:20 UTC 2022


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

Summary:
e37ab7b725 AGS: Implemented "localuserconf" option in cmdline and global config
8d8b786079 AGS: Don't mkdirs in the platform driver, return FSLocation instead
ccba1ca389 AGS: Code rewrite: split MakeGameDataDir() to make things bit clear
1d92ed73d9 AGS: Code rewrite: split SetSaveGameDirectory to make things easier
18f2ab00cd AGS: Fixed and simplified MakeSaveGameDir() to match recent changes


Commit: e37ab7b7253b7f6b87c6a3bb56bac7d619b6f97d
    https://github.com/scummvm/scummvm/commit/e37ab7b7253b7f6b87c6a3bb56bac7d619b6f97d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-31T19:36:12-07:00

Commit Message:
AGS: Implemented "localuserconf" option in cmdline and global config

>From upstream 109a9d985f498ed31308e2e97e1b97fa306c87c0

Changed paths:
    engines/ags/engine/ac/file.cpp
    engines/ags/engine/ac/game_setup.cpp
    engines/ags/engine/ac/game_setup.h
    engines/ags/engine/main/engine.cpp
    engines/ags/engine/main/main.cpp


diff --git a/engines/ags/engine/ac/file.cpp b/engines/ags/engine/ac/file.cpp
index da20e76f51e..dfdecafb1dc 100644
--- a/engines/ags/engine/ac/file.cpp
+++ b/engines/ags/engine/ac/file.cpp
@@ -269,6 +269,8 @@ FSLocation GetGameUserConfigDir() {
 		return FSLocation(_GP(usetup).user_conf_dir);
 	else if (Path::IsRelativePath(dir)) // relative dir is resolved relative to the game data dir
 		return FSLocation(_GP(ResPaths).DataDir, dir);
+	else if (_GP(usetup).local_user_conf) // directive to use game dir location
+		return FSLocation(_GP(ResPaths).DataDir);
 	// For absolute dir, we assume it's a special directory prepared for AGS engine
 	// and therefore amend it with a game own subdir
 	return FSLocation(dir, _GP(game).saveGameFolderName);
diff --git a/engines/ags/engine/ac/game_setup.cpp b/engines/ags/engine/ac/game_setup.cpp
index ff6c7a52439..948cce1936c 100644
--- a/engines/ags/engine/ac/game_setup.cpp
+++ b/engines/ags/engine/ac/game_setup.cpp
@@ -24,6 +24,7 @@
 namespace AGS3 {
 
 GameSetup::GameSetup() {
+	local_user_conf = false;
 	audio_backend = 1;
 	no_speech_pack = false;
 	textheight = 0;
diff --git a/engines/ags/engine/ac/game_setup.h b/engines/ags/engine/ac/game_setup.h
index d165bd62f71..1b7bc4a2dfc 100644
--- a/engines/ags/engine/ac/game_setup.h
+++ b/engines/ags/engine/ac/game_setup.h
@@ -68,6 +68,7 @@ struct GameSetup {
 	String opt_voice_dir; // optional custom install voice-over dir path
 	//
 	String conf_path; // a read-only config path (if set the regular config is ignored)
+	bool   local_user_conf; // search for user config in the game directory
 	String user_conf_dir; // directory to read and write user config in
 	String user_data_dir; // directory to write savedgames and user files to
 	String shared_data_dir; // directory to write shared game files to
diff --git a/engines/ags/engine/main/engine.cpp b/engines/ags/engine/main/engine.cpp
index 39ac3eb7b41..384daf60476 100644
--- a/engines/ags/engine/main/engine.cpp
+++ b/engines/ags/engine/main/engine.cpp
@@ -964,6 +964,15 @@ void engine_read_config(ConfigTree &cfg) {
 		}
 	}
 
+	// Handle directive to search for the user config inside the game directory;
+	// this option may come either from command line or default/global config.
+	_GP(usetup).local_user_conf |= INIreadint(cfg, "misc", "localuserconf", 0) != 0;
+	if (_GP(usetup).local_user_conf) { // Test if the file is writeable, if it is then both engine and setup
+	  // applications may actually use it fully as a user config, otherwise
+	  // fallback to default behavior.
+		_GP(usetup).local_user_conf = File::TestWriteFile(def_cfg_file);
+	}
+
 	// Read user configuration file
 	String user_cfg_file = find_user_cfg_file();
 	if (Path::ComparePaths(user_cfg_file, def_cfg_file) != 0 &&
diff --git a/engines/ags/engine/main/main.cpp b/engines/ags/engine/main/main.cpp
index c4e19aade28..1b3894c9778 100644
--- a/engines/ags/engine/main/main.cpp
+++ b/engines/ags/engine/main/main.cpp
@@ -211,6 +211,8 @@ int main_process_cmdline(ConfigTree &cfg, int argc, const char *argv[]) {
 			ee++;
 		} else if (ags_stricmp(arg, "--conf") == 0 && (argc > ee + 1)) {
 			_GP(usetup).conf_path = argv[++ee];
+		} else if (ags_stricmp(arg, "--localuserconf") == 0) {
+			_GP(usetup).local_user_conf = true;
 		} else if (ags_stricmp(arg, "--localuserconf") == 0) {
 			_GP(usetup).user_conf_dir = ".";
 		} else if ((ags_stricmp(arg, "--user-conf-dir") == 0) && (argc > ee + 1)) {


Commit: 8d8b7860796f9beca1375dbdf27a2dd1437aab28
    https://github.com/scummvm/scummvm/commit/8d8b7860796f9beca1375dbdf27a2dd1437aab28
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-31T20:04:43-07:00

Commit Message:
AGS: Don't mkdirs in the platform driver, return FSLocation instead

>From upstream 13312e9a1d3a427f49ccfe4e78f7d0736123a809

Changed paths:
    engines/ags/engine/ac/file.cpp
    engines/ags/engine/ac/game.cpp
    engines/ags/engine/ac/path_helper.h
    engines/ags/engine/debugging/debug.cpp
    engines/ags/engine/platform/base/ags_platform_driver.h
    engines/ags/engine/platform/scummvm/scummvm_platform_driver.cpp


diff --git a/engines/ags/engine/ac/file.cpp b/engines/ags/engine/ac/file.cpp
index dfdecafb1dc..9bee383f4ee 100644
--- a/engines/ags/engine/ac/file.cpp
+++ b/engines/ags/engine/ac/file.cpp
@@ -250,6 +250,12 @@ String PathFromInstallDir(const String &path) {
 	return path;
 }
 
+FSLocation PathFromInstallDir(const FSLocation &fsloc) {
+	if (is_relative_filename(fsloc.FullDir.GetCStr()))
+		return FSLocation(_GP(ResPaths).DataDir).Rebase(fsloc.FullDir);
+	return fsloc;
+}
+
 String PreparePathForWriting(const FSLocation &fsloc, const String &filename) {
 	if (Directory::CreateAllDirectories(fsloc.BaseDir, fsloc.SubDir))
 		return Path::ConcatPaths(fsloc.FullDir, filename);
@@ -257,45 +263,41 @@ String PreparePathForWriting(const FSLocation &fsloc, const String &filename) {
 }
 
 FSLocation GetGlobalUserConfigDir() {
-	String dir = _G(platform)->GetUserGlobalConfigDirectory();
-	if (Path::IsRelativePath(dir)) // relative dir is resolved relative to the game data dir
-		return FSLocation(_GP(ResPaths).DataDir, dir);
-	return FSLocation(dir);
+	FSLocation dir = _G(platform)->GetUserGlobalConfigDirectory();
+	if (is_relative_filename(dir.FullDir.GetCStr())) // relative dir is resolved relative to the game data dir
+		return FSLocation(_GP(ResPaths).DataDir).Rebase(dir.FullDir);
+	return dir;
 }
 
 FSLocation GetGameUserConfigDir() {
-	String dir = _G(platform)->GetUserConfigDirectory();
-	if (!_GP(usetup).user_conf_dir.IsEmpty()) // directive to use custom userconf location
-		return FSLocation(_GP(usetup).user_conf_dir);
-	else if (Path::IsRelativePath(dir)) // relative dir is resolved relative to the game data dir
-		return FSLocation(_GP(ResPaths).DataDir, dir);
+	FSLocation dir = _G(platform)->GetUserConfigDirectory();
+	if (is_relative_filename(dir.FullDir.GetCStr())) // relative dir is resolved relative to the game data dir
+		return FSLocation(_GP(ResPaths).DataDir).Rebase(dir.FullDir);
 	else if (_GP(usetup).local_user_conf) // directive to use game dir location
 		return FSLocation(_GP(ResPaths).DataDir);
 	// For absolute dir, we assume it's a special directory prepared for AGS engine
-	// and therefore amend it with a game own subdir
-	return FSLocation(dir, _GP(game).saveGameFolderName);
+	// and therefore append a game's own subdir
+	return dir.Concat(_GP(game).saveGameFolderName);
 }
 
 // A helper function that deduces a data directory either using default system location,
 // or user option from config. In case of a default location a path is appended with
 // game's "save folder" name, which is meant to separate files from different games.
-static FSLocation MakeGameDataDir(const String &default_dir, const String &user_option) {
-	if (user_option.IsEmpty()) {
-		String dir = default_dir;
-		if (Path::IsRelativePath(dir)) // relative dir is resolved relative to the game data dir
-			return FSLocation(_GP(ResPaths).DataDir, dir);
+static FSLocation MakeGameDataDir(const FSLocation &def_dir, const String &user_dir) {
+	if (user_dir.IsEmpty()) {
+		if (is_relative_filename(def_dir.FullDir.GetCStr())) // relative dir is resolved relative to the game data dir
+			return FSLocation(_GP(ResPaths).DataDir).Rebase(def_dir.FullDir);
 		// For absolute dir, we assume it's a special directory prepared for AGS engine
-		// and therefore amend it with a game own subdir
-		return FSLocation(dir, _GP(game).saveGameFolderName);
+		// and therefore amend it with a game's own subdir
+		return def_dir.Concat(_GP(game).saveGameFolderName);
 	}
 	// If this location is set up by user config, then use it as is (resolving relative path if necessary)
-	String dir = user_option;
-	if (Path::IsSameOrSubDir(_GP(ResPaths).DataDir, dir)) // check if it's inside game dir
-		return FSLocation(_GP(ResPaths).DataDir, Path::MakeRelativePath(_GP(ResPaths).DataDir, dir));
-	dir = Path::MakeAbsolutePath(dir);
-	return FSLocation(dir);
+	if (Path::IsSameOrSubDir(_GP(ResPaths).DataDir, user_dir)) // check if it's inside game dir
+		return FSLocation(_GP(ResPaths).DataDir, Path::MakeRelativePath(_GP(ResPaths).DataDir, user_dir));
+	return FSLocation(Path::MakeAbsolutePath(user_dir));
 }
 
+
 FSLocation GetGameAppDataDir() {
 	return MakeGameDataDir(_G(platform)->GetAllUsersDataDirectory(), _GP(usetup).shared_data_dir);
 }
@@ -422,6 +424,10 @@ bool ResolveWritePathAndCreateDirs(const String &sc_path, ResolvedPath &rp) {
 	return true;
 }
 
+bool CreateFSDirs(const FSLocation &fs) {
+	return Directory::CreateAllDirectories(fs.BaseDir, fs.FullDir);
+}
+
 //
 // AGS custom PACKFILE callbacks, that use our own Stream object
 //
diff --git a/engines/ags/engine/ac/game.cpp b/engines/ags/engine/ac/game.cpp
index c603df1dc7f..abd2164bae0 100644
--- a/engines/ags/engine/ac/game.cpp
+++ b/engines/ags/engine/ac/game.cpp
@@ -237,46 +237,46 @@ String get_save_game_path(int slotNum) {
 
 #if !AGS_PLATFORM_SCUMMVM
 // Convert a path possibly containing path tags into acceptable save path
+// NOTE that the game script may issue an order to change the save directory to
+// a dir of a new name. While we let this work, we also try to keep these
+// inside same parent location, would that be a common system directory,
+// or a custom one set by a player in config.
 bool MakeSaveGameDir(const String &newFolder, FSLocation &fsloc) {
-	fsloc = FSLocation();
+	rp = ResolvedPath();
 	// don't allow absolute paths
-	if (!Path::IsRelativePath(newFolder))
+	if (!is_relative_filename(newFolder))
 		return false;
 
-	String base_dir;
-	String sub_dir;
+	FSLocation fsdir;
+	String newSaveGameDir = FixSlashAfterToken(newFolder);
 
-	if (newFolder.CompareLeft(UserSavedgamesRootToken) == 0) {
-		// IMPORTANT: for compatibility reasons we support both cases:
-		// when token is followed by the path separator and when it is not, in which case it's assumed.
-		if (saveGameParent.IsEmpty()) {
-			base_dir = PathFromInstallDir(platform->GetUserSavedgamesDirectory());
-			sub_dir = newFolder.Mid(UserSavedgamesRootToken.GetLength());
+	if (newSaveGameDir.CompareLeft(UserSavedgamesRootToken, UserSavedgamesRootToken.GetLength()) == 0) {
+		if (saveGameParent.IsEmpty()) { // Set this up inside a standard AGS save dir
+			fsdir = PathFromInstallDir(platform->GetUserSavedgamesDirectory());
+			fsdir = fsdir.Concat(newSaveGameDir.Mid(UserSavedgamesRootToken.GetLength()));
 		} else {
 			// If there is a custom save parent directory, then replace
-			// not only root token, but also first subdirectory after the token
-			base_dir = saveGameParent;
-			sub_dir = Path::ConcatPaths(".", newFolder.Mid(UserSavedgamesRootToken.GetLength()));
-			sub_dir.ClipSection('/', 0, 1); // TODO: Path helper function for this?
+			// not only root token, but also first subdirectory
+			newSaveGameDir.ClipSection('/', 0, 1); // TODO: Path helper function for this?
+			fsdir = FSLocation(saveGameParent).Concat(newSaveGameDir);
 		}
-		fsloc = FSLocation(base_dir, sub_dir);
 	} else {
 		// Convert the path relative to installation folder into path relative to the
 		// safe save path with default name
-		if (saveGameParent.IsEmpty()) {
-			base_dir = PathFromInstallDir(platform->GetUserSavedgamesDirectory());
-			sub_dir = Path::ConcatPaths(game.saveGameFolderName, newFolder);
+		if (saveGameParent.IsEmpty()) { // Set this up inside a standard AGS save dir
+			fsdir = PathFromInstallDir(platform->GetUserSavedgamesDirectory());
+			fsdir = fsdir.Concat(Path::ConcatPaths(game.saveGameFolderName, newFolder));
 		} else {
-			base_dir = saveGameParent;
-			sub_dir = newFolder;
+			fsdir = FSLocation(saveGameParent).Concat(newFolder);
 		}
-		fsloc = FSLocation(base_dir, sub_dir);
 		// For games made in the safe-path-aware versions of AGS, report a warning
 		if (game.options[OPT_SAFEFILEPATHS]) {
 			debug_script_warn("Attempt to explicitly set savegame location relative to the game installation directory ('%s') denied;\nPath will be remapped to the user documents directory: '%s'",
-				newFolder.GetCStr(), fsloc.FullDir.GetCStr());
+				newFolder.GetCStr(), fsdir.FullDir.GetCStr());
 		}
 	}
+	rp.BaseDir = fsdir.BaseDir;
+	rp.FullPath = fsdir.FullDir;
 	return true;
 }
 #endif
diff --git a/engines/ags/engine/ac/path_helper.h b/engines/ags/engine/ac/path_helper.h
index 399372b81f1..265651a0f4e 100644
--- a/engines/ags/engine/ac/path_helper.h
+++ b/engines/ags/engine/ac/path_helper.h
@@ -47,9 +47,6 @@ extern const char *DefaultConfigFileName;
 // Subsitutes illegal characters with '_'. This function uses illegal chars array
 // specific to current platform.
 void FixupFilename(char *filename);
-// Tests the input path, if it's an absolute path then returns it unchanged;
-// if it's a relative path then resolves it into absolute, using install dir as a base.
-String PathFromInstallDir(const String &path);
 
 // FSLocation describes a file system location defined by two parts:
 // a secure path that engine does not own, and sub-path that it owns.
@@ -66,7 +63,22 @@ struct FSLocation {
 		: BaseDir(base), SubDir(subdir),
 		FullDir(AGS::Shared::Path::ConcatPaths(base, subdir)) {
 	}
+	inline bool IsValid() const {
+		return !FullDir.IsEmpty();
+	}
+	// Concats the given path to the existing full dir
+	inline FSLocation Concat(const String &path) const {
+		return FSLocation(BaseDir, AGS::Shared::Path::ConcatPaths(FullDir, path));
+	}
+	// Sets full path as a relative to the existing base dir
+	inline FSLocation Rebase(const String &path) const {
+		return FSLocation(BaseDir, AGS::Shared::Path::ConcatPaths(BaseDir, path));
+	}
 };
+// Tests the input path, if it's an absolute path then returns it unchanged;
+// if it's a relative path then resolves it into absolute, using install dir as a base.
+String PathFromInstallDir(const String &path);
+FSLocation PathFromInstallDir(const FSLocation &fsloc);
 // Makes sure that given system location is available, makes directories if have to (and if it's allowed to)
 // Returns full file path on success, empty string on failure.
 String PreparePathForWriting(const FSLocation &fsloc, const String &filename);
@@ -106,6 +118,8 @@ bool ResolveScriptPath(const String &sc_path, bool read_only, ResolvedPath &rp);
 // Returns 'true' on success, and 'false' if either path is impossible to resolve,
 // forbidden for writing, or if failed to create any subdirectories.
 bool ResolveWritePathAndCreateDirs(const String &sc_path, ResolvedPath &rp);
+// Creates all necessary subdirectories inside the safe parent location.
+bool CreateFSDirs(const FSLocation &fs);
 
 } // namespace AGS3
 
diff --git a/engines/ags/engine/debugging/debug.cpp b/engines/ags/engine/debugging/debug.cpp
index 16fe3c1569c..8080be0e4cc 100644
--- a/engines/ags/engine/debugging/debug.cpp
+++ b/engines/ags/engine/debugging/debug.cpp
@@ -90,7 +90,12 @@ PDebugOutput create_log_output(const String &name, const String &path = "", LogF
 		return _GP(DbgMgr).RegisterOutput(OutputSystemID, AGSPlatformDriver::GetDriver(), kDbgMsg_None);
 	} else if (name.CompareNoCase(OutputFileID) == 0) {
 		_GP(DebugLogFile).reset(new LogFile());
-		String logfile_path = !path.IsEmpty() ? path : Path::ConcatPaths(_G(platform)->GetAppOutputDirectory(), "ags.log");
+		String logfile_path = path;
+		if (logfile_path.IsEmpty()) {
+			FSLocation fs = _G(platform)->GetAppOutputDirectory();
+			CreateFSDirs(fs);
+			logfile_path = Path::ConcatPaths(fs.FullDir, "ags.log");
+		}
 		if (!_GP(DebugLogFile)->OpenFile(logfile_path, open_mode))
 			return nullptr;
 		Debug::Printf(kDbgMsg_Info, "Logging to %s", logfile_path.GetCStr());
diff --git a/engines/ags/engine/platform/base/ags_platform_driver.h b/engines/ags/engine/platform/base/ags_platform_driver.h
index 3347bf52e59..415486b48fb 100644
--- a/engines/ags/engine/platform/base/ags_platform_driver.h
+++ b/engines/ags/engine/platform/base/ags_platform_driver.h
@@ -30,6 +30,7 @@
 
 #include "ags/lib/std/vector.h"
 #include "ags/engine/ac/date_time.h"
+#include "ags/engine/ac/path_helper.h"
 #include "ags/shared/debugging/output_handler.h"
 #include "ags/shared/util/ini_util.h"
 #include "ags/lib/allegro/error.h"
@@ -86,24 +87,24 @@ struct AGSPlatformDriver
 	virtual void AttachToParentConsole();
 	virtual int  GetLastSystemError();
 	// Get root directory for storing per-game shared data
-	virtual const char *GetAllUsersDataDirectory() {
-		return ".";
+	virtual FSLocation GetAllUsersDataDirectory() {
+		return FSLocation(".");
 	}
 	// Get root directory for storing per-game saved games
-	virtual const char *GetUserSavedgamesDirectory() {
-		return ".";
+	virtual FSLocation GetUserSavedgamesDirectory() {
+		return FSLocation(".");
 	}
 	// Get root directory for storing per-game user configuration files
-	virtual const char *GetUserConfigDirectory() {
-		return ".";
+	virtual FSLocation GetUserConfigDirectory() {
+		return FSLocation(".");
 	}
 	// Get directory for storing all-games user configuration files
-	virtual const char *GetUserGlobalConfigDirectory() {
-		return ".";
+	virtual FSLocation GetUserGlobalConfigDirectory() {
+		return FSLocation(".");
 	}
 	// Get default directory for program output (logs)
-	virtual const char *GetAppOutputDirectory() {
-		return ".";
+	virtual FSLocation GetAppOutputDirectory() {
+		return FSLocation(".");
 	}
 	// Returns array of characters illegal to use in file names
 	virtual const char *GetIllegalFileChars() {
diff --git a/engines/ags/engine/platform/scummvm/scummvm_platform_driver.cpp b/engines/ags/engine/platform/scummvm/scummvm_platform_driver.cpp
index e083bfd2963..c7dfa5afc16 100644
--- a/engines/ags/engine/platform/scummvm/scummvm_platform_driver.cpp
+++ b/engines/ags/engine/platform/scummvm/scummvm_platform_driver.cpp
@@ -40,11 +40,11 @@ struct ScummVMPlatformDriver : AGSPlatformDriver {
 
 	int  CDPlayerCommand(int cmdd, int datt) override;
 	void DisplayAlert(const char *, ...) override;
-	const char *GetAllUsersDataDirectory() override;
-	const char *GetUserSavedgamesDirectory() override;
-	const char *GetUserConfigDirectory() override;
-	const char *GetUserGlobalConfigDirectory() override;
-	const char *GetAppOutputDirectory() override;
+	FSLocation GetAllUsersDataDirectory() override;
+	FSLocation GetUserSavedgamesDirectory() override;
+	FSLocation GetUserConfigDirectory() override;
+	FSLocation GetUserGlobalConfigDirectory() override;
+	FSLocation GetAppOutputDirectory() override;
 	unsigned long GetDiskFreeSpaceMB() override;
 	const char *GetNoMouseErrorString() override;
 	const char *GetAllegroFailUserHint() override;
@@ -77,24 +77,24 @@ void ScummVMPlatformDriver::DisplayAlert(const char *text, ...) {
 		::AGS::g_vm->GUIError(msg);
 }
 
-const char *ScummVMPlatformDriver::GetAllUsersDataDirectory() {
-	return "";
+FSLocation ScummVMPlatformDriver::GetAllUsersDataDirectory() {
+	return FSLocation(".");
 }
 
-const char *ScummVMPlatformDriver::GetUserSavedgamesDirectory() {
-	return "";
+FSLocation ScummVMPlatformDriver::GetUserSavedgamesDirectory() {
+	return FSLocation(".");
 }
 
-const char *ScummVMPlatformDriver::GetUserConfigDirectory() {
+FSLocation ScummVMPlatformDriver::GetUserConfigDirectory() {
 	return GetUserSavedgamesDirectory();
 }
 
-const char *ScummVMPlatformDriver::GetUserGlobalConfigDirectory() {
+FSLocation ScummVMPlatformDriver::GetUserGlobalConfigDirectory() {
 	return GetUserSavedgamesDirectory();
 }
 
-const char *ScummVMPlatformDriver::GetAppOutputDirectory() {
-	return "";
+FSLocation ScummVMPlatformDriver::GetAppOutputDirectory() {
+	return FSLocation(".");
 }
 
 unsigned long ScummVMPlatformDriver::GetDiskFreeSpaceMB() {


Commit: ccba1ca38934fb4575bd94159328569ee05b1a35
    https://github.com/scummvm/scummvm/commit/ccba1ca38934fb4575bd94159328569ee05b1a35
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-31T20:29:04-07:00

Commit Message:
AGS: Code rewrite: split MakeGameDataDir() to make things bit clear

>From upstream 403e680373732d984c97845d20f32dd53f5c23f1

Changed paths:
    engines/ags/engine/ac/file.cpp
    engines/ags/shared/util/string.h


diff --git a/engines/ags/engine/ac/file.cpp b/engines/ags/engine/ac/file.cpp
index 9bee383f4ee..62e6ab5f815 100644
--- a/engines/ags/engine/ac/file.cpp
+++ b/engines/ags/engine/ac/file.cpp
@@ -251,7 +251,7 @@ String PathFromInstallDir(const String &path) {
 }
 
 FSLocation PathFromInstallDir(const FSLocation &fsloc) {
-	if (is_relative_filename(fsloc.FullDir.GetCStr()))
+	if (is_relative_filename(fsloc.FullDir))
 		return FSLocation(_GP(ResPaths).DataDir).Rebase(fsloc.FullDir);
 	return fsloc;
 }
@@ -264,14 +264,14 @@ String PreparePathForWriting(const FSLocation &fsloc, const String &filename) {
 
 FSLocation GetGlobalUserConfigDir() {
 	FSLocation dir = _G(platform)->GetUserGlobalConfigDirectory();
-	if (is_relative_filename(dir.FullDir.GetCStr())) // relative dir is resolved relative to the game data dir
+	if (is_relative_filename(dir.FullDir)) // relative dir is resolved relative to the game data dir
 		return FSLocation(_GP(ResPaths).DataDir).Rebase(dir.FullDir);
 	return dir;
 }
 
 FSLocation GetGameUserConfigDir() {
 	FSLocation dir = _G(platform)->GetUserConfigDirectory();
-	if (is_relative_filename(dir.FullDir.GetCStr())) // relative dir is resolved relative to the game data dir
+	if (is_relative_filename(dir.FullDir)) // relative dir is resolved relative to the game data dir
 		return FSLocation(_GP(ResPaths).DataDir).Rebase(dir.FullDir);
 	else if (_GP(usetup).local_user_conf) // directive to use game dir location
 		return FSLocation(_GP(ResPaths).DataDir);
@@ -280,30 +280,35 @@ FSLocation GetGameUserConfigDir() {
 	return dir.Concat(_GP(game).saveGameFolderName);
 }
 
-// A helper function that deduces a data directory either using default system location,
-// or user option from config. In case of a default location a path is appended with
-// game's "save folder" name, which is meant to separate files from different games.
-static FSLocation MakeGameDataDir(const FSLocation &def_dir, const String &user_dir) {
-	if (user_dir.IsEmpty()) {
-		if (is_relative_filename(def_dir.FullDir.GetCStr())) // relative dir is resolved relative to the game data dir
-			return FSLocation(_GP(ResPaths).DataDir).Rebase(def_dir.FullDir);
-		// For absolute dir, we assume it's a special directory prepared for AGS engine
-		// and therefore amend it with a game's own subdir
-		return def_dir.Concat(_GP(game).saveGameFolderName);
-	}
-	// If this location is set up by user config, then use it as is (resolving relative path if necessary)
-	if (Path::IsSameOrSubDir(_GP(ResPaths).DataDir, user_dir)) // check if it's inside game dir
-		return FSLocation(_GP(ResPaths).DataDir, Path::MakeRelativePath(_GP(ResPaths).DataDir, user_dir));
-	return FSLocation(Path::MakeAbsolutePath(user_dir));
+// Constructs data dir using rules for default system location
+inline FSLocation MakeDefaultDataDir(const FSLocation &def_dir) {
+	// Relative dir is resolved relative to the game data dir
+	if (is_relative_filename(def_dir.FullDir))
+		return FSLocation(_GP(ResPaths).DataDir).Rebase(def_dir.FullDir);
+	// For absolute dir, we assume it's a special directory prepared for AGS engine
+	// and therefore amend it with a game's own subdir (to separate files from different games)
+	return def_dir.Concat(_GP(game).saveGameFolderName);
 }
 
+// Constructs data dir using rules for the user-specified location
+inline FSLocation MakeUserDataDir(const String &user_dir) {
+	// If user-set location is inside game dir, then form a relative path
+	if (is_relative_filename(user_dir))
+		return FSLocation(_GP(ResPaths).DataDir).Rebase(user_dir);
+	// Otherwise treat it as an absolute path
+	return FSLocation(Path::MakeAbsolutePath(user_dir));
+}
 
 FSLocation GetGameAppDataDir() {
-	return MakeGameDataDir(_G(platform)->GetAllUsersDataDirectory(), _GP(usetup).shared_data_dir);
+	if (_GP(usetup).shared_data_dir.IsEmpty())
+		return MakeDefaultDataDir(_G(platform)->GetAllUsersDataDirectory());
+	return MakeUserDataDir(_GP(usetup).shared_data_dir);
 }
 
 FSLocation GetGameUserDataDir() {
-	return MakeGameDataDir(_G(platform)->GetUserSavedgamesDirectory(), _GP(usetup).user_data_dir);
+	if (_GP(usetup).user_data_dir.IsEmpty())
+		return MakeDefaultDataDir(_G(platform)->GetUserSavedgamesDirectory());
+	return MakeUserDataDir(_GP(usetup).user_data_dir);
 }
 
 bool ResolveScriptPath(const String &orig_sc_path, bool read_only, ResolvedPath &rp) {
diff --git a/engines/ags/shared/util/string.h b/engines/ags/shared/util/string.h
index a969aae7097..c216506f08c 100644
--- a/engines/ags/shared/util/string.h
+++ b/engines/ags/shared/util/string.h
@@ -423,6 +423,9 @@ public:
 	operator bool() const {
 		return !IsEmpty();
 	}
+	operator const char *() const {
+		return GetCStr();
+	}
 
 private:
 	// Creates new empty string with buffer enough to fit given length


Commit: 1d92ed73d94fc76c3dea26dadba83489cf7d43f6
    https://github.com/scummvm/scummvm/commit/1d92ed73d94fc76c3dea26dadba83489cf7d43f6
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-31T21:34:33-07:00

Commit Message:
AGS: Code rewrite: split SetSaveGameDirectory to make things easier

>From upstream a53c06090a268b8c6dd6aa30369a30db2d3513a7

Changed paths:
    engines/ags/engine/ac/game.cpp
    engines/ags/engine/ac/game.h
    engines/ags/engine/main/engine.cpp


diff --git a/engines/ags/engine/ac/game.cpp b/engines/ags/engine/ac/game.cpp
index abd2164bae0..a311d7cff46 100644
--- a/engines/ags/engine/ac/game.cpp
+++ b/engines/ags/engine/ac/game.cpp
@@ -220,11 +220,9 @@ void set_save_game_suffix(const String &suffix) {
 	_G(saveGameSuffix) = suffix;
 }
 
-#if !AGS_PLATFORM_SCUMMVM
 String get_save_game_filename(int slotNum) {
-	return String::FromFormat("agssave.%03d%s", slotNum, _G(saveGameSuffix).GetCStr());
+	return String(g_engine->getSaveStateName(slotNum).c_str());
 }
-#endif
 
 String get_save_game_path(int slotNum) {
 #if AGS_PLATFORM_SCUMMVM
@@ -241,33 +239,32 @@ String get_save_game_path(int slotNum) {
 // a dir of a new name. While we let this work, we also try to keep these
 // inside same parent location, would that be a common system directory,
 // or a custom one set by a player in config.
-bool MakeSaveGameDir(const String &newFolder, FSLocation &fsloc) {
-	rp = ResolvedPath();
+static bool MakeSaveGameDir(const String &newFolder, FSLocation &fsdir) {
+	fsdir = FSLocation();
 	// don't allow absolute paths
 	if (!is_relative_filename(newFolder))
 		return false;
 
-	FSLocation fsdir;
 	String newSaveGameDir = FixSlashAfterToken(newFolder);
 
 	if (newSaveGameDir.CompareLeft(UserSavedgamesRootToken, UserSavedgamesRootToken.GetLength()) == 0) {
-		if (saveGameParent.IsEmpty()) { // Set this up inside a standard AGS save dir
+		if (_G(saveGameParent).IsEmpty()) { // Set this up inside a standard AGS save dir
 			fsdir = PathFromInstallDir(platform->GetUserSavedgamesDirectory());
 			fsdir = fsdir.Concat(newSaveGameDir.Mid(UserSavedgamesRootToken.GetLength()));
 		} else {
 			// If there is a custom save parent directory, then replace
 			// not only root token, but also first subdirectory
 			newSaveGameDir.ClipSection('/', 0, 1); // TODO: Path helper function for this?
-			fsdir = FSLocation(saveGameParent).Concat(newSaveGameDir);
+			fsdir = FSLocation(_G(saveGameParent)).Concat(newSaveGameDir);
 		}
 	} else {
 		// Convert the path relative to installation folder into path relative to the
 		// safe save path with default name
-		if (saveGameParent.IsEmpty()) { // Set this up inside a standard AGS save dir
+		if (_G(saveGameParent).IsEmpty()) { // Set this up inside a standard AGS save dir
 			fsdir = PathFromInstallDir(platform->GetUserSavedgamesDirectory());
 			fsdir = fsdir.Concat(Path::ConcatPaths(game.saveGameFolderName, newFolder));
 		} else {
-			fsdir = FSLocation(saveGameParent).Concat(newFolder);
+			fsdir = FSLocation(_G(saveGameParent)).Concat(newFolder);
 		}
 		// For games made in the safe-path-aware versions of AGS, report a warning
 		if (game.options[OPT_SAFEFILEPATHS]) {
@@ -275,47 +272,24 @@ bool MakeSaveGameDir(const String &newFolder, FSLocation &fsloc) {
 				newFolder.GetCStr(), fsdir.FullDir.GetCStr());
 		}
 	}
-	rp.BaseDir = fsdir.BaseDir;
-	rp.FullPath = fsdir.FullDir;
+
 	return true;
 }
 #endif
 
-bool SetCustomSaveParent(const String &path) {
-	if (SetSaveGameDirectoryPath(path, true)) {
-		_G(saveGameParent) = path;
-		return true;
-	}
-	return false;
-}
-
-bool SetSaveGameDirectoryPath(const String &newFolder, bool explicit_path) {
-#if AGS_PLATFORM_SCUMMVM
-	return false;
-#else
-	String newFolder = new_dir.IsEmpty() ? "." : new_dir;
-	String newSaveGameDir;
-	if (explicit_path) {
-		newSaveGameDir = PathFromInstallDir(newFolder);
-		if (!Directory::CreateDirectory(newSaveGameDir))
-			return false;
-	} else {
-		FSLocation fsloc;
-		if (!MakeSaveGameDir(newFolder, fsloc))
-			return false;
-		if (!Directory::CreateAllDirectories(fsloc.BaseDir, fsloc.SubDir)) {
-			debug_script_warn("SetSaveGameDirectory: failed to create all subdirectories: %s", fsloc.FullDir.GetCStr());
-			return false;
-		}
-		newSaveGameDir = fsloc.FullDir;
+// Tries to assign a new save directory, and copies the restart point if available
+static bool SetSaveGameDirectory(const FSLocation &fsdir) {
+	if (!Directory::CreateAllDirectories(fsdir.BaseDir, fsdir.FullDir)) {
+		debug_script_warn("SetSaveGameDirectory: failed to create all subdirectories: %s", fsdir.FullDir.GetCStr());
+		return false;
 	}
+	String newSaveGameDir = fsdir.FullDir;
 
-	String newFolderTempFile = Path::ConcatPaths(newSaveGameDir, "agstmp.tmp");
-	if (!File::TestCreateFile(newFolderTempFile))
+	if (!File::TestCreateFile(Path::ConcatPaths(newSaveGameDir, "agstmp.tmp")))
 		return false;
 
 	// copy the Restart Game file, if applicable
-	String restartGamePath = Path::ConcatPaths(saveGameDirectory, get_save_game_filename(RESTART_POINT_SAVE_GAME_NUMBER));
+	String restartGamePath = Path::ConcatPaths(_G(saveGameDirectory), get_save_game_filename(RESTART_POINT_SAVE_GAME_NUMBER));
 	Stream *restartGameFile = File::OpenFileRead(restartGamePath);
 	if (restartGameFile != nullptr) {
 		long fileSize = restartGameFile->GetLength();
@@ -330,15 +304,34 @@ bool SetSaveGameDirectoryPath(const String &newFolder, bool explicit_path) {
 		free(mbuffer);
 	}
 
-	saveGameDirectory = newSaveGameDir;
+	_G(saveGameDirectory) = newSaveGameDir;
 	return true;
-#endif
 }
 
-int Game_SetSaveGameDirectory(const String &newFolder) {
-	return SetSaveGameDirectoryPath(newFolder, false) ? 1 : 0;
+void SetDefaultSaveDirectory() {
+	// If user set a custom save dir, also keep it as a "save root", in case
+	// the game script uses Game.SetSaveGameDirectory().
+	if (!_GP(usetup).user_data_dir.IsEmpty())
+		_G(saveGameParent) = _GP(usetup).user_data_dir;
+	// Request a default save location, and assign it as a save dir
+	FSLocation fsdir = GetGameUserDataDir();
+	SetSaveGameDirectory(fsdir);
+}
+
+int Game_SetSaveGameDirectory(const char *newFolder) {
+#if AGS_PLATFORM_SCUMMVM
+	return 1;
+#else
+	// First resolve the script path (it may contain tokens)
+	FSLocation fsdir;
+	if (!MakeSaveGameDir(newFolder, fsdir))
+		return 0;
+	// If resolved successfully, try to assign the new dir
+	return SetSaveGameDirectory(fsdir) ? 1 : 0;
+#endif
 }
 
+
 const char *Game_GetSaveSlotDescription(int slnum) {
 	String description;
 	if (read_savedgame_description(get_save_game_path(slnum), description)) {
diff --git a/engines/ags/engine/ac/game.h b/engines/ags/engine/ac/game.h
index 3fa76848152..6f50abc49b0 100644
--- a/engines/ags/engine/ac/game.h
+++ b/engines/ags/engine/ac/game.h
@@ -90,13 +90,10 @@ int Game_GetMODPattern();
 //=============================================================================
 int Game_GetDialogCount();
 
-// Defines a custom save parent directory, which will replace $MYDOCS$/GameName
-// when a new save directory is set from the script
-bool SetCustomSaveParent(const Shared::String &path);
-// If explicit_path flag is false, the actual path will be constructed
-// as a relative to system's user saves directory
-bool SetSaveGameDirectoryPath(const Shared::String &newFolder, bool explicit_path = false);
-int Game_SetSaveGameDirectory(const Shared::String &newFolder);
+// Sets a default save directory, based on platform driver settings and user config
+void SetDefaultSaveDirectory();
+// Sets a new save directory within the save parent; copies "restart" slot if available
+int Game_SetSaveGameDirectory(const char *newFolder);
 const char *Game_GetSaveSlotDescription(int slnum);
 
 const char *Game_GetGlobalStrings(int index);
diff --git a/engines/ags/engine/main/engine.cpp b/engines/ags/engine/main/engine.cpp
index 384daf60476..3266515a786 100644
--- a/engines/ags/engine/main/engine.cpp
+++ b/engines/ags/engine/main/engine.cpp
@@ -411,19 +411,8 @@ void engine_init_user_directories() {
 	if (!_GP(usetup).shared_data_dir.IsEmpty())
 		Debug::Printf(kDbgMsg_Info, "Shared data directory: %s", _GP(usetup).shared_data_dir.GetCStr());
 
-	// if end-user specified custom save path, use it
-	bool res = false;
-	if (!_GP(usetup).user_data_dir.IsEmpty()) {
-		res = SetCustomSaveParent(_GP(usetup).user_data_dir);
-		if (!res) {
-			Debug::Printf(kDbgMsg_Warn, "WARNING: custom user save path failed, using default system paths");
-			res = false;
-		}
-	}
-	// if there is no custom path, or if custom path failed, use default system path
-	if (!res) {
-		SetSaveGameDirectoryPath(Path::ConcatPaths(UserSavedgamesRootToken, _GP(game).saveGameFolderName));
-	}
+	// Initialize default save directory early, for we'll need it to set restart point
+	SetDefaultSaveDirectory();
 }
 
 #if AGS_PLATFORM_OS_ANDROID


Commit: 18f2ab00cdbb90a02983f3d9850fb7a19784e0c4
    https://github.com/scummvm/scummvm/commit/18f2ab00cdbb90a02983f3d9850fb7a19784e0c4
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-31T21:43:00-07:00

Commit Message:
AGS: Fixed and simplified MakeSaveGameDir() to match recent changes

>From upstream 133843b5c1eef7b8614532be9b62d6a8809069db

Changed paths:
    engines/ags/engine/ac/game.cpp
    engines/ags/globals.h


diff --git a/engines/ags/engine/ac/game.cpp b/engines/ags/engine/ac/game.cpp
index a311d7cff46..144a2756ab7 100644
--- a/engines/ags/engine/ac/game.cpp
+++ b/engines/ags/engine/ac/game.cpp
@@ -239,40 +239,22 @@ String get_save_game_path(int slotNum) {
 // a dir of a new name. While we let this work, we also try to keep these
 // inside same parent location, would that be a common system directory,
 // or a custom one set by a player in config.
-static bool MakeSaveGameDir(const String &newFolder, FSLocation &fsdir) {
+static bool MakeSaveGameDir(const String &new_dir, FSLocation &fsdir) {
 	fsdir = FSLocation();
 	// don't allow absolute paths
-	if (!is_relative_filename(newFolder))
+	if (!is_relative_filename(new_dir))
 		return false;
 
-	String newSaveGameDir = FixSlashAfterToken(newFolder);
-
-	if (newSaveGameDir.CompareLeft(UserSavedgamesRootToken, UserSavedgamesRootToken.GetLength()) == 0) {
-		if (_G(saveGameParent).IsEmpty()) { // Set this up inside a standard AGS save dir
-			fsdir = PathFromInstallDir(platform->GetUserSavedgamesDirectory());
-			fsdir = fsdir.Concat(newSaveGameDir.Mid(UserSavedgamesRootToken.GetLength()));
-		} else {
-			// If there is a custom save parent directory, then replace
-			// not only root token, but also first subdirectory
-			newSaveGameDir.ClipSection('/', 0, 1); // TODO: Path helper function for this?
-			fsdir = FSLocation(_G(saveGameParent)).Concat(newSaveGameDir);
-		}
-	} else {
-		// Convert the path relative to installation folder into path relative to the
-		// safe save path with default name
-		if (_G(saveGameParent).IsEmpty()) { // Set this up inside a standard AGS save dir
-			fsdir = PathFromInstallDir(platform->GetUserSavedgamesDirectory());
-			fsdir = fsdir.Concat(Path::ConcatPaths(game.saveGameFolderName, newFolder));
-		} else {
-			fsdir = FSLocation(_G(saveGameParent)).Concat(newFolder);
-		}
-		// For games made in the safe-path-aware versions of AGS, report a warning
-		if (game.options[OPT_SAFEFILEPATHS]) {
-			debug_script_warn("Attempt to explicitly set savegame location relative to the game installation directory ('%s') denied;\nPath will be remapped to the user documents directory: '%s'",
-				newFolder.GetCStr(), fsdir.FullDir.GetCStr());
-		}
+	String fixed_newdir = FixSlashAfterToken(new_dir);
+	if (fixed_newdir.CompareLeft(UserSavedgamesRootToken, UserSavedgamesRootToken.GetLength()) == 0) {
+		fixed_newdir.ClipLeft(UserSavedgamesRootToken.GetLength());
+	} else if (game.options[OPT_SAFEFILEPATHS] > 0) { // For games made in the safe-path-aware versions of AGS, report a warning
+		debug_script_warn("Attempt to explicitly set savegame location relative to the game installation directory ('%s') denied;\nPath will be remapped to the user documents directory: '%s'",
+			fixed_newdir.GetCStr(), fsdir.FullDir.GetCStr());
 	}
 
+	// Resolve the new dir relative to the user data parent dir
+	fsdir = GetGameUserDataDir().Concat(fixed_newdir);
 	return true;
 }
 #endif
@@ -309,10 +291,6 @@ static bool SetSaveGameDirectory(const FSLocation &fsdir) {
 }
 
 void SetDefaultSaveDirectory() {
-	// If user set a custom save dir, also keep it as a "save root", in case
-	// the game script uses Game.SetSaveGameDirectory().
-	if (!_GP(usetup).user_data_dir.IsEmpty())
-		_G(saveGameParent) = _GP(usetup).user_data_dir;
 	// Request a default save location, and assign it as a save dir
 	FSLocation fsdir = GetGameUserDataDir();
 	SetSaveGameDirectory(fsdir);
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index e8ccca6985f..5830cc6b5f7 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -754,7 +754,6 @@ public:
 	MoveList *_mls = nullptr;
 	GameSetup *_usetup;
 	AGS::Shared::String _saveGameDirectory;
-	AGS::Shared::String _saveGameParent;
 	AGS::Shared::String _saveGameSuffix;
 	bool _want_exit = false;
 	bool _abort_engine = false;




More information about the Scummvm-git-logs mailing list