mirror of
https://github.com/derrod/legendary.git
synced 2024-12-22 17:55:27 +00:00
[cli/core/models/utils] macOS cloud save support
This commit is contained in:
parent
4eaa608370
commit
1dfc5aabe7
|
@ -447,7 +447,7 @@ class LegendaryCLI:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
game = self.core.get_game(igame.app_name)
|
game = self.core.get_game(igame.app_name)
|
||||||
if not game or not game.supports_cloud_saves:
|
if not game or not (game.supports_cloud_saves or game.supports_mac_cloud_saves):
|
||||||
if igame.app_name in latest_save:
|
if igame.app_name in latest_save:
|
||||||
# this should never happen unless cloud save support was removed from a game
|
# this should never happen unless cloud save support was removed from a game
|
||||||
logger.warning(f'{igame.app_name} has remote save(s) but does not support cloud saves?!')
|
logger.warning(f'{igame.app_name} has remote save(s) but does not support cloud saves?!')
|
||||||
|
@ -462,7 +462,7 @@ class LegendaryCLI:
|
||||||
|
|
||||||
# if there is no saved save path, try to get one
|
# if there is no saved save path, try to get one
|
||||||
if not igame.save_path:
|
if not igame.save_path:
|
||||||
save_path = self.core.get_save_path(igame.app_name)
|
save_path = self.core.get_save_path(igame.app_name, platform=igame.platform)
|
||||||
|
|
||||||
# ask user if path is correct if computing for the first time
|
# ask user if path is correct if computing for the first time
|
||||||
logger.info(f'Computed save path: "{save_path}"')
|
logger.info(f'Computed save path: "{save_path}"')
|
||||||
|
@ -902,7 +902,7 @@ class LegendaryCLI:
|
||||||
end_t = time.time()
|
end_t = time.time()
|
||||||
if not args.no_install:
|
if not args.no_install:
|
||||||
# Allow setting savegame directory at install time so sync-saves will work immediately
|
# Allow setting savegame directory at install time so sync-saves will work immediately
|
||||||
if game.supports_cloud_saves and args.save_path:
|
if (game.supports_cloud_saves or game.supports_mac_cloud_saves) and args.save_path:
|
||||||
igame.save_path = args.save_path
|
igame.save_path = args.save_path
|
||||||
|
|
||||||
postinstall = self.core.install_game(igame)
|
postinstall = self.core.install_game(igame)
|
||||||
|
@ -930,7 +930,7 @@ class LegendaryCLI:
|
||||||
self.install_game(args)
|
self.install_game(args)
|
||||||
args.yes, args.app_name = _yes, _app_name
|
args.yes, args.app_name = _yes, _app_name
|
||||||
|
|
||||||
if game.supports_cloud_saves and not game.is_dlc:
|
if (game.supports_cloud_saves or game.supports_mac_cloud_saves) and not game.is_dlc:
|
||||||
# todo option to automatically download saves after the installation
|
# todo option to automatically download saves after the installation
|
||||||
# args does not have the required attributes for sync_saves in here,
|
# args does not have the required attributes for sync_saves in here,
|
||||||
# not sure how to solve that elegantly.
|
# not sure how to solve that elegantly.
|
||||||
|
@ -1399,13 +1399,20 @@ class LegendaryCLI:
|
||||||
game.app_version(args.platform)))
|
game.app_version(args.platform)))
|
||||||
all_versions = {k: v.build_version for k,v in game.asset_infos.items()}
|
all_versions = {k: v.build_version for k,v in game.asset_infos.items()}
|
||||||
game_infos.append(InfoItem('All versions', 'platform_versions', all_versions, all_versions))
|
game_infos.append(InfoItem('All versions', 'platform_versions', all_versions, all_versions))
|
||||||
|
# Cloud save support for Mac and Windows
|
||||||
game_infos.append(InfoItem('Cloud saves supported', 'cloud_saves_supported',
|
game_infos.append(InfoItem('Cloud saves supported', 'cloud_saves_supported',
|
||||||
game.supports_cloud_saves, game.supports_cloud_saves))
|
game.supports_cloud_saves or game.supports_mac_cloud_saves,
|
||||||
|
game.supports_cloud_saves or game.supports_mac_cloud_saves))
|
||||||
|
cs_dir = None
|
||||||
if game.supports_cloud_saves:
|
if game.supports_cloud_saves:
|
||||||
cs_dir = game.metadata['customAttributes']['CloudSaveFolder']['value']
|
cs_dir = game.metadata['customAttributes']['CloudSaveFolder']['value']
|
||||||
else:
|
game_infos.append(InfoItem('Cloud save folder (Windows)', 'cloud_save_folder', cs_dir, cs_dir))
|
||||||
|
|
||||||
cs_dir = None
|
cs_dir = None
|
||||||
game_infos.append(InfoItem('Cloud save folder', 'cloud_save_folder', cs_dir, cs_dir))
|
if game.supports_mac_cloud_saves:
|
||||||
|
cs_dir = game.metadata['customAttributes']['CloudSaveFolder_MAC']['value']
|
||||||
|
game_infos.append(InfoItem('Cloud save folder (Mac)', 'cloud_save_folder_mac', cs_dir, cs_dir))
|
||||||
|
|
||||||
game_infos.append(InfoItem('Is DLC', 'is_dlc', game.is_dlc, game.is_dlc))
|
game_infos.append(InfoItem('Is DLC', 'is_dlc', game.is_dlc, game.is_dlc))
|
||||||
# Find custom launch options, if available
|
# Find custom launch options, if available
|
||||||
launch_options = []
|
launch_options = []
|
||||||
|
|
|
@ -14,6 +14,7 @@ from multiprocessing import Queue
|
||||||
from platform import system
|
from platform import system
|
||||||
from requests import session
|
from requests import session
|
||||||
from requests.exceptions import HTTPError
|
from requests.exceptions import HTTPError
|
||||||
|
from sys import platform as sys_platform
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from urllib.parse import urlencode, parse_qsl
|
from urllib.parse import urlencode, parse_qsl
|
||||||
|
|
||||||
|
@ -673,9 +674,14 @@ class LegendaryCore:
|
||||||
|
|
||||||
return _saves
|
return _saves
|
||||||
|
|
||||||
def get_save_path(self, app_name):
|
def get_save_path(self, app_name, platform='Windows'):
|
||||||
game = self.lgd.get_game_meta(app_name)
|
game = self.lgd.get_game_meta(app_name)
|
||||||
|
|
||||||
|
if platform == 'Mac':
|
||||||
|
save_path = game.metadata['customAttributes'].get('CloudSaveFolder_MAC', {}).get('value')
|
||||||
|
else:
|
||||||
save_path = game.metadata['customAttributes'].get('CloudSaveFolder', {}).get('value')
|
save_path = game.metadata['customAttributes'].get('CloudSaveFolder', {}).get('value')
|
||||||
|
|
||||||
if not save_path:
|
if not save_path:
|
||||||
raise ValueError('Game does not support cloud saves')
|
raise ValueError('Game does not support cloud saves')
|
||||||
|
|
||||||
|
@ -689,13 +695,19 @@ class LegendaryCore:
|
||||||
'{epicid}': self.lgd.userdata['account_id']
|
'{epicid}': self.lgd.userdata['account_id']
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.name == 'nt':
|
if sys_platform == 'win32':
|
||||||
path_vars.update({
|
path_vars.update({
|
||||||
'{appdata}': os.path.expandvars('%LOCALAPPDATA%'),
|
'{appdata}': os.path.expandvars('%LOCALAPPDATA%'),
|
||||||
'{userdir}': os.path.expandvars('%userprofile%/documents'),
|
'{userdir}': os.path.expandvars('%userprofile%/documents'),
|
||||||
# '{userprofile}': os.path.expandvars('%userprofile%'), # possibly wrong
|
'{userprofile}': os.path.expandvars('%userprofile%'),
|
||||||
'{usersavedgames}': os.path.expandvars('%userprofile%/Saved Games')
|
'{usersavedgames}': os.path.expandvars('%userprofile%/Saved Games')
|
||||||
})
|
})
|
||||||
|
elif sys_platform == 'darwin' and platform == 'Mac':
|
||||||
|
path_vars.update({
|
||||||
|
'{appdata}': os.path.expandvars('~/Library/Application Support'),
|
||||||
|
'{userdir}': os.path.expandvars('~/Documents'),
|
||||||
|
'{userlibrary}': os.path.expandvars('~/Library')
|
||||||
|
})
|
||||||
else:
|
else:
|
||||||
# attempt to get WINE prefix from config
|
# attempt to get WINE prefix from config
|
||||||
wine_pfx = self.lgd.config.get(app_name, 'wine_prefix', fallback=None)
|
wine_pfx = self.lgd.config.get(app_name, 'wine_prefix', fallback=None)
|
||||||
|
@ -724,8 +736,8 @@ class LegendaryCore:
|
||||||
# these paths should always use a forward slash
|
# these paths should always use a forward slash
|
||||||
new_save_path = [path_vars.get(p.lower(), p) for p in save_path.split('/')]
|
new_save_path = [path_vars.get(p.lower(), p) for p in save_path.split('/')]
|
||||||
absolute_path = os.path.realpath(os.path.join(*new_save_path))
|
absolute_path = os.path.realpath(os.path.join(*new_save_path))
|
||||||
# attempt to resolve as much as possible on case-insensitive file-systems
|
# attempt to resolve as much as possible on case-sensitive file-systems
|
||||||
if os.name != 'nt':
|
if os.name != 'nt' and platform != 'Mac':
|
||||||
absolute_path = case_insensitive_path_search(absolute_path)
|
absolute_path = case_insensitive_path_search(absolute_path)
|
||||||
|
|
||||||
return absolute_path
|
return absolute_path
|
||||||
|
@ -765,6 +777,7 @@ class LegendaryCore:
|
||||||
game = self.lgd.get_game_meta(app_name)
|
game = self.lgd.get_game_meta(app_name)
|
||||||
custom_attr = game.metadata['customAttributes']
|
custom_attr = game.metadata['customAttributes']
|
||||||
save_path = custom_attr.get('CloudSaveFolder', {}).get('value')
|
save_path = custom_attr.get('CloudSaveFolder', {}).get('value')
|
||||||
|
save_path_mac = custom_attr.get('CloudSaveFolder_MAC', {}).get('value')
|
||||||
|
|
||||||
include_f = exclude_f = None
|
include_f = exclude_f = None
|
||||||
if not disable_filtering:
|
if not disable_filtering:
|
||||||
|
@ -774,12 +787,12 @@ class LegendaryCore:
|
||||||
if (_exclude := custom_attr.get('CloudExcludeList', {}).get('value', None)) is not None:
|
if (_exclude := custom_attr.get('CloudExcludeList', {}).get('value', None)) is not None:
|
||||||
exclude_f = _exclude.split(',')
|
exclude_f = _exclude.split(',')
|
||||||
|
|
||||||
if not save_path:
|
if not save_path and not save_path_mac:
|
||||||
raise ValueError('Game does not support cloud saves')
|
raise ValueError('Game does not support cloud saves')
|
||||||
|
|
||||||
sgh = SaveGameHelper()
|
sgh = SaveGameHelper()
|
||||||
files = sgh.package_savegame(save_dir, app_name, self.egs.user.get('account_id'),
|
files = sgh.package_savegame(save_dir, app_name, self.egs.user.get('account_id'),
|
||||||
save_path, include_f, exclude_f, local_dt)
|
save_path, save_path_mac, include_f, exclude_f, local_dt)
|
||||||
|
|
||||||
if not files:
|
if not files:
|
||||||
self.log.info('No files to upload. If you believe this is incorrect run command with "--disable-filters"')
|
self.log.info('No files to upload. If you believe this is incorrect run command with "--disable-filters"')
|
||||||
|
|
|
@ -75,6 +75,10 @@ class Game:
|
||||||
def supports_cloud_saves(self):
|
def supports_cloud_saves(self):
|
||||||
return self.metadata and (self.metadata.get('customAttributes', {}).get('CloudSaveFolder') is not None)
|
return self.metadata and (self.metadata.get('customAttributes', {}).get('CloudSaveFolder') is not None)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supports_mac_cloud_saves(self):
|
||||||
|
return self.metadata and (self.metadata.get('customAttributes', {}).get('CloudSaveFolder_MAC') is not None)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def catalog_item_id(self):
|
def catalog_item_id(self):
|
||||||
if not self.metadata:
|
if not self.metadata:
|
||||||
|
|
|
@ -51,8 +51,8 @@ class SaveGameHelper:
|
||||||
_tmp_file.seek(0)
|
_tmp_file.seek(0)
|
||||||
return ci
|
return ci
|
||||||
|
|
||||||
def package_savegame(self, input_folder: str, app_name: str = '',
|
def package_savegame(self, input_folder: str, app_name: str = '', epic_id: str = '',
|
||||||
epic_id: str = '', cloud_folder: str = '',
|
cloud_folder: str = '', cloud_folder_mac: str = '',
|
||||||
include_filter: list = None,
|
include_filter: list = None,
|
||||||
exclude_filter: list = None,
|
exclude_filter: list = None,
|
||||||
manifest_dt: datetime = None):
|
manifest_dt: datetime = None):
|
||||||
|
@ -61,6 +61,7 @@ class SaveGameHelper:
|
||||||
:param app_name: App name for savegame being stored
|
:param app_name: App name for savegame being stored
|
||||||
:param epic_id: Epic account ID
|
:param epic_id: Epic account ID
|
||||||
:param cloud_folder: Folder the savegame resides in (based on game metadata)
|
:param cloud_folder: Folder the savegame resides in (based on game metadata)
|
||||||
|
:param cloud_folder_mac: Folder the macOS savegame resides in (based on game metadata)
|
||||||
:param include_filter: list of patterns for files to include (excludes all others)
|
:param include_filter: list of patterns for files to include (excludes all others)
|
||||||
:param exclude_filter: list of patterns for files to exclude (includes all others)
|
:param exclude_filter: list of patterns for files to exclude (includes all others)
|
||||||
:param manifest_dt: datetime for the manifest name (optional)
|
:param manifest_dt: datetime for the manifest name (optional)
|
||||||
|
@ -77,6 +78,8 @@ class SaveGameHelper:
|
||||||
manifest_dt = datetime.utcnow()
|
manifest_dt = datetime.utcnow()
|
||||||
m.meta.build_version = manifest_dt.strftime('%Y.%m.%d-%H.%M.%S')
|
m.meta.build_version = manifest_dt.strftime('%Y.%m.%d-%H.%M.%S')
|
||||||
m.custom_fields['CloudSaveFolder'] = cloud_folder
|
m.custom_fields['CloudSaveFolder'] = cloud_folder
|
||||||
|
if cloud_folder_mac:
|
||||||
|
m.custom_fields['CloudSaveFolder_MAC'] = cloud_folder_mac
|
||||||
|
|
||||||
self.log.info(f'Packing savegame for "{app_name}", input folder: {input_folder}')
|
self.log.info(f'Packing savegame for "{app_name}", input folder: {input_folder}')
|
||||||
files = []
|
files = []
|
||||||
|
|
Loading…
Reference in a new issue