mirror of
https://github.com/derrod/legendary.git
synced 2024-12-22 01:45:28 +00:00
[cli/core/models] Add basic support for DLCs
This commit is contained in:
parent
0031e5908a
commit
662f6e7bd0
|
@ -151,12 +151,14 @@ def main():
|
|||
if not core.login():
|
||||
logger.error('Login failed, cannot continue!')
|
||||
exit(1)
|
||||
logger.info('Getting game list...')
|
||||
games = core.get_game_list()
|
||||
logger.info('Getting game list... (this may take a while)')
|
||||
games, dlc_list = core.get_game_and_dlc_list()
|
||||
|
||||
print('\nAvailable games:')
|
||||
for game in sorted(games, key=lambda x: x.app_title):
|
||||
print(f' * {game.app_title} (App name: {game.app_name}, version: {game.app_version})')
|
||||
for dlc in sorted(dlc_list[game.asset_info.catalog_item_id], key=lambda d: d.app_title):
|
||||
print(f' + {dlc.app_title} (App name: {dlc.app_name}, version: {dlc.app_version})')
|
||||
|
||||
print(f'\nTotal: {len(games)}')
|
||||
|
||||
|
@ -185,6 +187,10 @@ def main():
|
|||
logger.error(f'Game {app_name} is not currently installed!')
|
||||
exit(1)
|
||||
|
||||
if core.is_dlc(app_name):
|
||||
logger.error(f'{app_name} is DLC; please launch the base game instead!')
|
||||
exit(1)
|
||||
|
||||
if not args.offline and not core.is_offline_game(app_name):
|
||||
logger.info('Logging in...')
|
||||
if not core.login():
|
||||
|
@ -233,9 +239,23 @@ def main():
|
|||
logger.fatal(f'Could not find "{target_app}" in list of available games, did you type the name correctly?')
|
||||
exit(1)
|
||||
|
||||
if game.is_dlc:
|
||||
logger.info('Install candidate is DLC')
|
||||
app_name = game.metadata['mainGameItem']['releaseInfo'][0]['appId']
|
||||
base_game = core.get_game(app_name)
|
||||
# check if base_game is actually installed
|
||||
if not core.get_installed_game(app_name):
|
||||
# download mode doesn't care about whether or not something's installed
|
||||
if args.install or args.update:
|
||||
logger.fatal(f'Base game "{app_name}" is not installed!')
|
||||
exit(1)
|
||||
else:
|
||||
base_game = None
|
||||
|
||||
# todo use status queue to print progress from CLI
|
||||
dlm, analysis, igame = core.prepare_download(game=game, base_path=args.base_path, force=args.force,
|
||||
max_shm=args.shared_memory, max_workers=args.max_workers,
|
||||
dlm, analysis, igame = core.prepare_download(game=game, base_game=base_game, base_path=args.base_path,
|
||||
force=args.force, max_shm=args.shared_memory,
|
||||
max_workers=args.max_workers,
|
||||
disable_patching=args.disable_patching,
|
||||
override_manifest=args.override_manifest,
|
||||
override_base_url=args.override_base_url)
|
||||
|
@ -284,6 +304,15 @@ def main():
|
|||
end_t = time.time()
|
||||
if args.install or args.update:
|
||||
postinstall = core.install_game(igame)
|
||||
|
||||
dlcs = core.get_dlc_for_game(game.app_name)
|
||||
if dlcs:
|
||||
print('The following DLCs are available for this game:')
|
||||
for dlc in dlcs:
|
||||
print(f' - {dlc.app_title} (App name: {dlc.app_name}, version: {dlc.app_version})')
|
||||
print('Installing DLCs works the same as the main game, just use the DLC app name instead.')
|
||||
print('Automatic installation of DLC is currently not supported.')
|
||||
|
||||
if postinstall:
|
||||
logger.info('This game lists the following prequisites to be installed:')
|
||||
logger.info(f'{postinstall["name"]}: {" ".join((postinstall["path"], postinstall["args"]))}')
|
||||
|
@ -307,10 +336,21 @@ def main():
|
|||
igame = core.get_installed_game(target_app)
|
||||
if not igame:
|
||||
logger.error(f'Game {target_app} not installed, cannot uninstall!')
|
||||
if igame.is_dlc:
|
||||
logger.error('Uninstalling DLC is not supported.')
|
||||
exit(1)
|
||||
|
||||
try:
|
||||
logger.info(f'Removing "{igame.title}" from "{igame.install_path}"...')
|
||||
core.uninstall_game(igame)
|
||||
|
||||
dlcs = core.get_dlc_for_game(igame.app_name)
|
||||
for dlc in dlcs:
|
||||
idlc = core.get_installed_game(dlc.app_name)
|
||||
if core.is_installed(dlc.app_name):
|
||||
logger.info(f'Uninstalling DLC "{dlc.app_name}"...')
|
||||
core.uninstall_game(idlc, delete_files=False)
|
||||
|
||||
logger.info('Game has been uninstalled.')
|
||||
except Exception as e:
|
||||
logger.warning(f'Removing game failed: {e!r}, please remove {igame.install_path} manually.')
|
||||
|
|
|
@ -8,10 +8,11 @@ import shlex
|
|||
import shutil
|
||||
|
||||
from base64 import b64decode
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
from random import choice as randchoice
|
||||
from requests.exceptions import HTTPError
|
||||
from typing import List
|
||||
from typing import List, Dict
|
||||
|
||||
from legendary.api.egs import EPCAPI
|
||||
from legendary.downloader.manager import DLManager
|
||||
|
@ -141,7 +142,11 @@ class LegendaryCore:
|
|||
return self.lgd.get_game_meta(app_name)
|
||||
|
||||
def get_game_list(self, update_assets=True) -> List[Game]:
|
||||
return self.get_game_and_dlc_list(update_assets=update_assets)[0]
|
||||
|
||||
def get_game_and_dlc_list(self, update_assets=True) -> (List[Game], Dict[str, Game]):
|
||||
_ret = []
|
||||
_dlc = defaultdict(list)
|
||||
|
||||
for ga in self.get_assets(update_assets=update_assets):
|
||||
if ga.namespace == 'ue': # skip UE demo content
|
||||
|
@ -156,12 +161,24 @@ class LegendaryCore:
|
|||
game = Game(app_name=ga.app_name, app_version=ga.build_version,
|
||||
app_title=eg_meta['title'], asset_info=ga, metadata=eg_meta)
|
||||
self.lgd.set_game_meta(game.app_name, game)
|
||||
_ret.append(game)
|
||||
|
||||
return _ret
|
||||
if game.is_dlc:
|
||||
_dlc[game.metadata['mainGameItem']['id']].append(game)
|
||||
else:
|
||||
_ret.append(game)
|
||||
|
||||
return _ret, _dlc
|
||||
|
||||
def get_dlc_for_game(self, app_name):
|
||||
game = self.get_game(app_name)
|
||||
_, dlcs = self.get_game_and_dlc_list(update_assets=False)
|
||||
return dlcs[game.asset_info.catalog_item_id]
|
||||
|
||||
def get_installed_list(self) -> List[InstalledGame]:
|
||||
return self.lgd.get_installed_list()
|
||||
return [g for g in self.lgd.get_installed_list() if not g.is_dlc]
|
||||
|
||||
def get_installed_dlc_list(self) -> List[InstalledGame]:
|
||||
return [g for g in self.lgd.get_installed_list() if g.is_dlc]
|
||||
|
||||
def get_installed_game(self, app_name) -> InstalledGame:
|
||||
return self.lgd.get_installed_game(app_name)
|
||||
|
@ -254,6 +271,12 @@ class LegendaryCore:
|
|||
def is_installed(self, app_name: str) -> bool:
|
||||
return self.lgd.get_installed_game(app_name) is not None
|
||||
|
||||
def is_dlc(self, app_name: str) -> bool:
|
||||
meta = self.lgd.get_game_meta(app_name)
|
||||
if not meta:
|
||||
raise ValueError('Game unknown!')
|
||||
return meta.is_dlc
|
||||
|
||||
@staticmethod
|
||||
def load_manfiest(data: bytes) -> Manifest:
|
||||
if data[0:1] == b'{':
|
||||
|
@ -261,7 +284,7 @@ class LegendaryCore:
|
|||
else:
|
||||
return Manifest.read_all(data)
|
||||
|
||||
def prepare_download(self, game: Game, base_path: str = '',
|
||||
def prepare_download(self, game: Game, base_game: Game = None, base_path: str = '',
|
||||
max_shm: int = 0, max_workers: int = 0, force: bool = False,
|
||||
disable_patching: bool = False, override_manifest: str = '',
|
||||
override_base_url: str = '') -> (DLManager, AnalysisResult, ManifestMeta):
|
||||
|
@ -323,10 +346,17 @@ class LegendaryCore:
|
|||
if not base_path:
|
||||
base_path = self.get_default_install_dir()
|
||||
|
||||
install_path = os.path.join(
|
||||
base_path,
|
||||
game.metadata.get('customAttributes', {}).get('FolderName', {}).get('value', game.app_name)
|
||||
)
|
||||
if game.is_dlc:
|
||||
install_path = os.path.join(
|
||||
base_path,
|
||||
base_game.metadata.get('customAttributes', {}).get('FolderName', {}).get('value', game.app_name)
|
||||
)
|
||||
else:
|
||||
install_path = os.path.join(
|
||||
base_path,
|
||||
game.metadata.get('customAttributes', {}).get('FolderName', {}).get('value', game.app_name)
|
||||
)
|
||||
|
||||
if not os.path.exists(install_path):
|
||||
os.makedirs(install_path)
|
||||
|
||||
|
@ -362,7 +392,8 @@ class LegendaryCore:
|
|||
prereq_info=prereq, manifest_path=override_manifest, base_urls=base_urls,
|
||||
install_path=install_path, executable=new_manifest.meta.launch_exe,
|
||||
launch_parameters=new_manifest.meta.launch_command,
|
||||
can_run_offline=offline == 'true', requires_ot=ot == 'true')
|
||||
can_run_offline=offline == 'true', requires_ot=ot == 'true',
|
||||
is_dlc=base_game is not None)
|
||||
|
||||
return dlm, anlres, igame
|
||||
|
||||
|
|
|
@ -47,6 +47,10 @@ class Game:
|
|||
self.app_title = app_title
|
||||
self.base_urls = [] # base urls for download, only really used when cached manifest is current
|
||||
|
||||
@property
|
||||
def is_dlc(self):
|
||||
return self.metadata and 'mainGameItem' in self.metadata
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
tmp = cls()
|
||||
|
@ -69,7 +73,7 @@ class Game:
|
|||
class InstalledGame:
|
||||
def __init__(self, app_name='', title='', version='', manifest_path='', base_urls=None,
|
||||
install_path='', executable='', launch_parameters='', prereq_info=None,
|
||||
can_run_offline=False, requires_ot=False):
|
||||
can_run_offline=False, requires_ot=False, is_dlc=False):
|
||||
self.app_name = app_name
|
||||
self.title = title
|
||||
self.version = version
|
||||
|
@ -82,6 +86,7 @@ class InstalledGame:
|
|||
self.prereq_info = prereq_info
|
||||
self.can_run_offline = can_run_offline
|
||||
self.requires_ot = requires_ot
|
||||
self.is_dlc = is_dlc
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
|
@ -97,6 +102,7 @@ class InstalledGame:
|
|||
tmp.launch_parameters = json.get('launch_parameters', '')
|
||||
tmp.prereq_info = json.get('prereq_info', None)
|
||||
|
||||
tmp.can_run_offline = json.get('can_run_offline', True)
|
||||
tmp.can_run_offline = json.get('can_run_offline', False)
|
||||
tmp.requires_ot = json.get('requires_ot', False)
|
||||
tmp.is_dlc = json.get('is_dlc', False)
|
||||
return tmp
|
||||
|
|
Loading…
Reference in a new issue