From 384d447032f0674a81a90d9e1e48485f39c3dd49 Mon Sep 17 00:00:00 2001 From: UsernamesAreNotMyThing <92757918+UsernamesAreNotMyThing@users.noreply.github.com> Date: Sat, 25 Jan 2025 18:15:03 -0500 Subject: [PATCH 1/7] Add save import functionality --- util.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/util.py b/util.py index 6b1c0d6c..61ae8204 100644 --- a/util.py +++ b/util.py @@ -970,3 +970,44 @@ def set_game_drive(enabled: bool) -> None: protonmain.g_session.compat_config.add('gamedrive') else: protonmain.g_session.compat_config.discard('gamedrive') + +def import_saves_folder(from_appid: int, relative_path: str) -> None: + """Creates a symlink to a folder in another game's prefix. + You can use this for games that have functionality that depends on having save data for another game, which does NOT store its save data in its steamapps/common folder. + This will only work for Steam games. + + from_appid: Steam App ID of the game you're trying to get the saves folder from + relative_path: Path to reach the target folder that has the save data, starting from .../pfx/drive_c/users/steamuser + """ + from pathlib import Path + paths = [] + # Locate the prefix for from_appid + # The location of Steam's main folder can be found in the STEAM_BASE_FOLDER environment variable + # The libraryfolders.vdf file contains the paths for all your Steam library folders, including those on external drives or SD cards. + with open(f'{os.environ["STEAM_BASE_FOLDER"]}/steamapps/libraryfolders.vdf') as f: + for i in f.readlines(): + if '"path"' in i: + paths.append(i[11:-1]) + # Find which of the library folders the prefix is in + for i in paths: + if Path(f'{i}/steamapps/compatdata/{from_appid}').exists(): + # The prefix location for from_appid + prefix = f'{i}/steamapps/compatdata/{from_appid}/pfx/drive_c' + break + # If it wasn't found, do nothing. + else: + return + # Create the symlink + # STEAM_COMPAT_DATA_PATH contains the location of the prefix for the game you're trying to play. + goal = Path(f'{os.environ["STEAM_COMPAT_DATA_PATH"]}/pfx/drive_c/users/steamuser/{relative_path}') + # The goal is where we intend to create the symlink + if not goal.exists(): + # Create the necessary parent folders if needed + goal.parent.mkdir(parents=True, exist_ok=True) + goal.symlink_to(f'{prefix}/users/steamuser/{relative_path}', True) + elif not goal.is_symlink(): + return + elif not goal.readlink().exists(): + # In the case the prefix was moved to another drive, remove and re-create the symlink to point to the correct location + goal.unlink() + goal.symlink_to(f'{prefix}/users/steamuser/{relative_path}', True) From 45dccddc64537cd1da93d8f1a1f331588aa49af2 Mon Sep 17 00:00:00 2001 From: UsernamesAreNotMyThing <92757918+UsernamesAreNotMyThing@users.noreply.github.com> Date: Tue, 28 Jan 2025 14:06:33 -0500 Subject: [PATCH 2/7] Add save detection fix for FF7 Rebirth --- gamefixes-steam/2909400.py | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 gamefixes-steam/2909400.py diff --git a/gamefixes-steam/2909400.py b/gamefixes-steam/2909400.py new file mode 100644 index 00000000..3c0e8e5c --- /dev/null +++ b/gamefixes-steam/2909400.py @@ -0,0 +1,8 @@ +"""Final Fantasy VII Rebirth""" + +from protonfixes import util + + +def main() -> None: + """Grants bonus items to players with save data for Final Fantasy VII Remake Intergrade""" + util.import_saves_folder(1462040, 'Documents/My Games/FINAL FANTASY VII REMAKE') From c10eeeab5b9c876db292cda6e5a82964b99fdceb Mon Sep 17 00:00:00 2001 From: UsernamesAreNotMyThing <92757918+UsernamesAreNotMyThing@users.noreply.github.com> Date: Wed, 29 Jan 2025 10:52:22 -0500 Subject: [PATCH 3/7] Refinements to import_saves_folder --- util.py | 63 +++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/util.py b/util.py index 61ae8204..fc9b4123 100644 --- a/util.py +++ b/util.py @@ -971,43 +971,66 @@ def set_game_drive(enabled: bool) -> None: else: protonmain.g_session.compat_config.discard('gamedrive') -def import_saves_folder(from_appid: int, relative_path: str) -> None: - """Creates a symlink to a folder in another game's prefix. - You can use this for games that have functionality that depends on having save data for another game, which does NOT store its save data in its steamapps/common folder. +def import_saves_folder(from_appid: int, relative_path: str) -> bool: + """Creates a symlink in the prefix for the game you're trying to play, to a folder from another game's prefix (it needs to be in a Steam library folder). + You can use this for games that have functionality that depends on having save data for another game or demo, + which does NOT store its save data in its steamapps/common folder. This will only work for Steam games. - + from_appid: Steam App ID of the game you're trying to get the saves folder from - relative_path: Path to reach the target folder that has the save data, starting from .../pfx/drive_c/users/steamuser + You can find this number in the URL for the game's store page, or through looking up the game in SteamDB. + relative_path: Path to reach the target folder that has the save data, starting from the .../pfx/drive_c/users/steamuser folder + For example for the "My Documents/(Game name)" folder, just use "My Documents/(Game name)". """ from pathlib import Path paths = [] + # Valid Steam app IDs always end with a zero. + if from_appid <= 0 or from_appid % 10 != 0: + log.warn(f'Invalid ID used for import_saves_folder ({from_appid}).') + return False # Locate the prefix for from_appid # The location of Steam's main folder can be found in the STEAM_BASE_FOLDER environment variable - # The libraryfolders.vdf file contains the paths for all your Steam library folders, including those on external drives or SD cards. - with open(f'{os.environ["STEAM_BASE_FOLDER"]}/steamapps/libraryfolders.vdf') as f: - for i in f.readlines(): - if '"path"' in i: - paths.append(i[11:-1]) + # The libraryfolders.vdf file contains the paths for all your Steam library folders that are known to the Steam client, including those on external drives or SD cards. + try: + with open(f'{os.environ["STEAM_BASE_FOLDER"]}/steamapps/libraryfolders.vdf') as f: + for i in f.readlines(): + # Looking for lines of the format + # \t\t"path"\t\t"(the path)" + if '"path"' in i: + paths.append(i[11:-1]) + except FileNotFoundError: + log.info("Could not find Steam's libraryfolders.vdf file.") + return False # Find which of the library folders the prefix is in for i in paths: if Path(f'{i}/steamapps/compatdata/{from_appid}').exists(): - # The prefix location for from_appid - prefix = f'{i}/steamapps/compatdata/{from_appid}/pfx/drive_c' + # The user's folder in the prefix location for from_appid + prefix = f'{i}/steamapps/compatdata/{from_appid}/pfx/drive_c/users/steamuser/{relative_location}' + if not Path(prefix).exists(): + # Better to just abort than create a broken symlink + log.info(f'Skipping save data folder import due to location `{relative_location}` not existing in the prefix for {from_appid}.') + return False break - # If it wasn't found, do nothing. + # If one wasn't found, do nothing. else: - return + log.info(f'Skipping save data folder import, due to not finding a prefix for game {from_appid} to get the folder from.') + return False # Create the symlink - # STEAM_COMPAT_DATA_PATH contains the location of the prefix for the game you're trying to play. - goal = Path(f'{os.environ["STEAM_COMPAT_DATA_PATH"]}/pfx/drive_c/users/steamuser/{relative_path}') + goal = Path(f'{protonprefix()}/drive_c/users/steamuser/{relative_path}') # The goal is where we intend to create the symlink if not goal.exists(): # Create the necessary parent folders if needed goal.parent.mkdir(parents=True, exist_ok=True) - goal.symlink_to(f'{prefix}/users/steamuser/{relative_path}', True) + goal.symlink_to(prefix, True) + log.info(f'Created a symlink at `{goal}` to go to `{prefix}`') + return True elif not goal.is_symlink(): - return + return False elif not goal.readlink().exists(): - # In the case the prefix was moved to another drive, remove and re-create the symlink to point to the correct location + # In the case the prefix for the other game was moved to another location, remove and re-create the symlink to point to the correct location goal.unlink() - goal.symlink_to(f'{prefix}/users/steamuser/{relative_path}', True) + goal.symlink_to(prefix, True) + log.info(f'Replaced symlink at `{goal}` to go to `{prefix}`') + return True + else: + return False From dc4fd0623667546c1e2c7ca2ec70f7106ba1d961 Mon Sep 17 00:00:00 2001 From: UsernamesAreNotMyThing <92757918+UsernamesAreNotMyThing@users.noreply.github.com> Date: Wed, 29 Jan 2025 18:55:04 -0500 Subject: [PATCH 4/7] Add save detection fix for HZD Remastered --- gamefixes-steam/2561580.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gamefixes-steam/2561580.py b/gamefixes-steam/2561580.py index 19eb91a8..2b5a975f 100644 --- a/gamefixes-steam/2561580.py +++ b/gamefixes-steam/2561580.py @@ -1,4 +1,4 @@ -"""Game fix for Horizon Zero Dawn Remastered""" +"""Horizon Zero Dawn Remastered""" from sys import argv from os import environ @@ -6,6 +6,8 @@ def main() -> None: - """Won't connect to internet without using `-showlinkingqr` or `SteamDeck=1` options.""" + """Won't connect to internet to login to PSN without using `-showlinkingqr` or `SteamDeck=1` options.""" if environ.get('SteamDeck', '0') == '0' and '-showlinkingqr' not in argv: util.append_argument('-showlinkingqr') + # this allows the game to detect saves from the original Complete Edition + util.import_saves_folder(1151640, 'My Documents/Horizon Zero Dawn') From 22e14be0a996b98851cef207cb6f945fed5ab5b9 Mon Sep 17 00:00:00 2001 From: UsernamesAreNotMyThing <92757918+UsernamesAreNotMyThing@users.noreply.github.com> Date: Thu, 6 Feb 2025 17:45:00 -0500 Subject: [PATCH 5/7] Fix unbound name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `relative_location` → `relative_path` --- util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util.py b/util.py index fc9b4123..0ae0622a 100644 --- a/util.py +++ b/util.py @@ -1005,10 +1005,10 @@ def import_saves_folder(from_appid: int, relative_path: str) -> bool: for i in paths: if Path(f'{i}/steamapps/compatdata/{from_appid}').exists(): # The user's folder in the prefix location for from_appid - prefix = f'{i}/steamapps/compatdata/{from_appid}/pfx/drive_c/users/steamuser/{relative_location}' + prefix = f'{i}/steamapps/compatdata/{from_appid}/pfx/drive_c/users/steamuser/{relative_path}' if not Path(prefix).exists(): # Better to just abort than create a broken symlink - log.info(f'Skipping save data folder import due to location `{relative_location}` not existing in the prefix for {from_appid}.') + log.info(f'Skipping save data folder import due to location `{relative_path}` not existing in the prefix for {from_appid}.') return False break # If one wasn't found, do nothing. From c3a47b9b677483101825ddb00a3b7e73e348b774 Mon Sep 17 00:00:00 2001 From: UsernamesAreNotMyThing <92757918+UsernamesAreNotMyThing@users.noreply.github.com> Date: Thu, 6 Feb 2025 18:07:41 -0500 Subject: [PATCH 6/7] Remove `-showlinkingqr` fix for HZD Remastered The game was updated to remove the PSN requirement. https://store.steampowered.com/news/app/2561580/view/497186807117513479 --- gamefixes-steam/2561580.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/gamefixes-steam/2561580.py b/gamefixes-steam/2561580.py index 2b5a975f..97be42b9 100644 --- a/gamefixes-steam/2561580.py +++ b/gamefixes-steam/2561580.py @@ -6,8 +6,5 @@ def main() -> None: - """Won't connect to internet to login to PSN without using `-showlinkingqr` or `SteamDeck=1` options.""" - if environ.get('SteamDeck', '0') == '0' and '-showlinkingqr' not in argv: - util.append_argument('-showlinkingqr') - # this allows the game to detect saves from the original Complete Edition + """Game allows playing saves from the original Complete Edition, but by default it can't find them.""" util.import_saves_folder(1151640, 'My Documents/Horizon Zero Dawn') From 283df32d33ef813877fd0a6a171f265e6d158b01 Mon Sep 17 00:00:00 2001 From: UsernamesAreNotMyThing <92757918+UsernamesAreNotMyThing@users.noreply.github.com> Date: Thu, 6 Feb 2025 18:35:21 -0500 Subject: [PATCH 7/7] Update 2561580.py Removed some no longer needed imports. --- gamefixes-steam/2561580.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/gamefixes-steam/2561580.py b/gamefixes-steam/2561580.py index 97be42b9..919aa1e6 100644 --- a/gamefixes-steam/2561580.py +++ b/gamefixes-steam/2561580.py @@ -1,7 +1,5 @@ """Horizon Zero Dawn Remastered""" -from sys import argv -from os import environ from protonfixes import util