mirror of
https://github.com/derrod/legendary.git
synced 2024-12-22 17:55:27 +00:00
[cli/core/models] Add support for importing already installed games
Fixes #10 though will need further improvement.
This commit is contained in:
parent
0d6bcf5950
commit
38f5bbd934
|
@ -389,6 +389,12 @@ class LegendaryCLI:
|
||||||
subprocess.Popen(params, cwd=cwd, env=env)
|
subprocess.Popen(params, cwd=cwd, env=env)
|
||||||
|
|
||||||
def install_game(self, args):
|
def install_game(self, args):
|
||||||
|
if self.core.is_installed(args.app_name):
|
||||||
|
igame = self.core.get_installed_game(args.app_name)
|
||||||
|
if igame.needs_verification and not args.repair_mode:
|
||||||
|
logger.info('Game needs to be verified before updating, switching to repair mode...')
|
||||||
|
args.repair_mode = True
|
||||||
|
|
||||||
repair_file = None
|
repair_file = None
|
||||||
if args.subparser_name == 'download':
|
if args.subparser_name == 'download':
|
||||||
logger.info('Setting --no-install flag since "download" command was used')
|
logger.info('Setting --no-install flag since "download" command was used')
|
||||||
|
@ -472,6 +478,11 @@ class LegendaryCLI:
|
||||||
if not analysis.dl_size:
|
if not analysis.dl_size:
|
||||||
logger.info('Download size is 0, the game is either already up to date or has not changed. Exiting...')
|
logger.info('Download size is 0, the game is either already up to date or has not changed. Exiting...')
|
||||||
if args.repair_mode and os.path.exists(repair_file):
|
if args.repair_mode and os.path.exists(repair_file):
|
||||||
|
igame = self.core.get_installed_game(game.app_name)
|
||||||
|
if igame.needs_verification:
|
||||||
|
igame.needs_verification = False
|
||||||
|
self.core.install_game(igame)
|
||||||
|
|
||||||
logger.debug('Removing repair file.')
|
logger.debug('Removing repair file.')
|
||||||
os.remove(repair_file)
|
os.remove(repair_file)
|
||||||
exit(0)
|
exit(0)
|
||||||
|
@ -557,6 +568,11 @@ class LegendaryCLI:
|
||||||
logger.info(f'To download saves for this game run "legendary sync-saves {args.app_name}"')
|
logger.info(f'To download saves for this game run "legendary sync-saves {args.app_name}"')
|
||||||
|
|
||||||
if args.repair_mode and os.path.exists(repair_file):
|
if args.repair_mode and os.path.exists(repair_file):
|
||||||
|
igame = self.core.get_installed_game(game.app_name)
|
||||||
|
if igame.needs_verification:
|
||||||
|
igame.needs_verification = False
|
||||||
|
self.core.install_game(igame)
|
||||||
|
|
||||||
logger.debug('Removing repair file.')
|
logger.debug('Removing repair file.')
|
||||||
os.remove(repair_file)
|
os.remove(repair_file)
|
||||||
|
|
||||||
|
@ -670,6 +686,46 @@ class LegendaryCLI:
|
||||||
if print_command:
|
if print_command:
|
||||||
logger.info(f'Run "legendary repair {args.app_name}" to repair your game installation.')
|
logger.info(f'Run "legendary repair {args.app_name}" to repair your game installation.')
|
||||||
|
|
||||||
|
def import_game(self, args):
|
||||||
|
if not os.path.exists(args.app_path):
|
||||||
|
logger.error(f'Specified path "{args.app_path}" does not exist!')
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
if self.core.is_installed(args.app_name):
|
||||||
|
logger.error('Game is already installed!')
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
if not self.core.login():
|
||||||
|
logger.error('Log in failed!')
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
# do some basic checks
|
||||||
|
game = self.core.get_game(args.app_name, update_meta=True)
|
||||||
|
if not game:
|
||||||
|
logger.fatal(f'Did not find game "{args.app_name}" on account.')
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
# todo: if there is an Epic Games Launcher manifest in the install path use that instead
|
||||||
|
# get everything needed for import from core, then run additional checks.
|
||||||
|
manifest, igame = self.core.import_game(game, args.app_path)
|
||||||
|
# check if most files at least exist or if user might have specified the wrong directory
|
||||||
|
total = len(manifest.file_manifest_list.elements)
|
||||||
|
found = sum(os.path.exists(os.path.join(args.app_path, f.filename))
|
||||||
|
for f in manifest.file_manifest_list.elements)
|
||||||
|
if found != total:
|
||||||
|
ratio = found / total
|
||||||
|
if ratio < 0.95:
|
||||||
|
logger.fatal(f'{total-found}/{total} files are missing, cannot import.')
|
||||||
|
exit(1)
|
||||||
|
logger.warning('Some files are missing from the game installation, this may be due to newer updates.')
|
||||||
|
else:
|
||||||
|
logger.info('Game install appears to be complete.')
|
||||||
|
|
||||||
|
self.core.install_game(igame)
|
||||||
|
logger.info(f'NOTE: The game installation will have to be verified before it can be updated with legendary. '
|
||||||
|
f'Run "legendary repair {args.app_name}" to do so.')
|
||||||
|
logger.info('Game has been imported.')
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description=f'Legendary v{__version__} - "{__codename__}"')
|
parser = argparse.ArgumentParser(description=f'Legendary v{__version__} - "{__codename__}"')
|
||||||
|
@ -697,6 +753,7 @@ def main():
|
||||||
download_saves_parser = subparsers.add_parser('download-saves', help='Download all cloud saves')
|
download_saves_parser = subparsers.add_parser('download-saves', help='Download all cloud saves')
|
||||||
sync_saves_parser = subparsers.add_parser('sync-saves', help='Sync cloud saves')
|
sync_saves_parser = subparsers.add_parser('sync-saves', help='Sync cloud saves')
|
||||||
verify_parser = subparsers.add_parser('verify-game', help='Verify a game\'s local files')
|
verify_parser = subparsers.add_parser('verify-game', help='Verify a game\'s local files')
|
||||||
|
import_parser = subparsers.add_parser('import-game', help='Import an already installed game')
|
||||||
|
|
||||||
install_parser.add_argument('app_name', help='Name of the app', metavar='<App Name>')
|
install_parser.add_argument('app_name', help='Name of the app', metavar='<App Name>')
|
||||||
uninstall_parser.add_argument('app_name', help='Name of the app', metavar='<App Name>')
|
uninstall_parser.add_argument('app_name', help='Name of the app', metavar='<App Name>')
|
||||||
|
@ -709,7 +766,10 @@ def main():
|
||||||
help='Name of the app (optional)')
|
help='Name of the app (optional)')
|
||||||
sync_saves_parser.add_argument('app_name', nargs='?', metavar='<App Name>', default='',
|
sync_saves_parser.add_argument('app_name', nargs='?', metavar='<App Name>', default='',
|
||||||
help='Name of the app (optional)')
|
help='Name of the app (optional)')
|
||||||
verify_parser.add_argument('app_name', help='Name of the app (optional)', metavar='<App Name>')
|
verify_parser.add_argument('app_name', help='Name of the app', metavar='<App Name>')
|
||||||
|
import_parser.add_argument('app_name', help='Name of the app', metavar='<App Name>')
|
||||||
|
import_parser.add_argument('app_path', help='Path where the game is installed',
|
||||||
|
metavar='<Installation directory>')
|
||||||
|
|
||||||
# importing only works on Windows right now
|
# importing only works on Windows right now
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
|
@ -835,7 +895,7 @@ def main():
|
||||||
if args.subparser_name not in ('auth', 'list-games', 'list-installed', 'list-files',
|
if args.subparser_name not in ('auth', 'list-games', 'list-installed', 'list-files',
|
||||||
'launch', 'download', 'uninstall', 'install', 'update',
|
'launch', 'download', 'uninstall', 'install', 'update',
|
||||||
'repair', 'list-saves', 'download-saves', 'sync-saves',
|
'repair', 'list-saves', 'download-saves', 'sync-saves',
|
||||||
'verify-game'):
|
'verify-game', 'import-game'):
|
||||||
print(parser.format_help())
|
print(parser.format_help())
|
||||||
|
|
||||||
# Print the main help *and* the help for all of the subcommands. Thanks stackoverflow!
|
# Print the main help *and* the help for all of the subcommands. Thanks stackoverflow!
|
||||||
|
@ -883,6 +943,8 @@ def main():
|
||||||
cli.sync_saves(args)
|
cli.sync_saves(args)
|
||||||
elif args.subparser_name == 'verify-game':
|
elif args.subparser_name == 'verify-game':
|
||||||
cli.verify_game(args)
|
cli.verify_game(args)
|
||||||
|
elif args.subparser_name == 'import-game':
|
||||||
|
cli.import_game(args)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
logger.info('Command was aborted via KeyboardInterrupt, cleaning up...')
|
logger.info('Command was aborted via KeyboardInterrupt, cleaning up...')
|
||||||
|
|
||||||
|
|
|
@ -754,6 +754,36 @@ class LegendaryCore:
|
||||||
igame.prereq_info['installed'] = True
|
igame.prereq_info['installed'] = True
|
||||||
self.lgd.set_installed_game(app_name, igame)
|
self.lgd.set_installed_game(app_name, igame)
|
||||||
|
|
||||||
|
def import_game(self, game: Game, app_path: str) -> (Manifest, InstalledGame):
|
||||||
|
self.log.info(f'Downloading latest manifest for "{game.app_name}"')
|
||||||
|
manifest_data, base_urls = self.get_cdn_manifest(game)
|
||||||
|
if not game.base_urls:
|
||||||
|
game.base_urls = base_urls
|
||||||
|
self.lgd.set_game_meta(game.app_name, game)
|
||||||
|
|
||||||
|
# parse and save manifest to disk for verification step of import
|
||||||
|
new_manifest = self.load_manfiest(manifest_data)
|
||||||
|
self.lgd.save_manifest(game.app_name, manifest_data)
|
||||||
|
self.lgd.save_manifest(game.app_name, manifest_data,
|
||||||
|
version=new_manifest.meta.build_version)
|
||||||
|
|
||||||
|
prereq = None
|
||||||
|
if new_manifest.meta.prereq_ids:
|
||||||
|
prereq = dict(ids=new_manifest.meta.prereq_ids, name=new_manifest.meta.prereq_name,
|
||||||
|
path=new_manifest.meta.prereq_path, args=new_manifest.meta.prereq_args)
|
||||||
|
|
||||||
|
offline = game.metadata.get('customAttributes', {}).get('CanRunOffline', {}).get('value', 'true')
|
||||||
|
ot = game.metadata.get('customAttributes', {}).get('OwnershipToken', {}).get('value', 'false')
|
||||||
|
igame = InstalledGame(app_name=game.app_name, title=game.app_title, prereq_info=prereq, base_urls=base_urls,
|
||||||
|
install_path=app_path, version=new_manifest.meta.build_version, is_dlc=game.is_dlc,
|
||||||
|
executable=new_manifest.meta.launch_exe, can_run_offline=offline == 'true',
|
||||||
|
launch_parameters=new_manifest.meta.launch_command, requires_ot=ot == 'true',
|
||||||
|
needs_verification=True)
|
||||||
|
|
||||||
|
return new_manifest, igame
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def exit(self):
|
def exit(self):
|
||||||
"""
|
"""
|
||||||
Do cleanup, config saving, and exit.
|
Do cleanup, config saving, and exit.
|
||||||
|
|
|
@ -78,7 +78,8 @@ class Game:
|
||||||
class InstalledGame:
|
class InstalledGame:
|
||||||
def __init__(self, app_name='', title='', version='', manifest_path='', base_urls=None,
|
def __init__(self, app_name='', title='', version='', manifest_path='', base_urls=None,
|
||||||
install_path='', executable='', launch_parameters='', prereq_info=None,
|
install_path='', executable='', launch_parameters='', prereq_info=None,
|
||||||
can_run_offline=False, requires_ot=False, is_dlc=False, save_path=None):
|
can_run_offline=False, requires_ot=False, is_dlc=False, save_path=None,
|
||||||
|
needs_verification=False):
|
||||||
self.app_name = app_name
|
self.app_name = app_name
|
||||||
self.title = title
|
self.title = title
|
||||||
self.version = version
|
self.version = version
|
||||||
|
@ -93,6 +94,7 @@ class InstalledGame:
|
||||||
self.requires_ot = requires_ot
|
self.requires_ot = requires_ot
|
||||||
self.is_dlc = is_dlc
|
self.is_dlc = is_dlc
|
||||||
self.save_path = save_path
|
self.save_path = save_path
|
||||||
|
self.needs_verification = needs_verification
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_json(cls, json):
|
def from_json(cls, json):
|
||||||
|
@ -112,6 +114,7 @@ class InstalledGame:
|
||||||
tmp.requires_ot = json.get('requires_ot', False)
|
tmp.requires_ot = json.get('requires_ot', False)
|
||||||
tmp.is_dlc = json.get('is_dlc', False)
|
tmp.is_dlc = json.get('is_dlc', False)
|
||||||
tmp.save_path = json.get('save_path', None)
|
tmp.save_path = json.get('save_path', None)
|
||||||
|
tmp.needs_verification = json.get('needs_verification', None)
|
||||||
return tmp
|
return tmp
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue