2020-04-15 00:47:46 +12:00
|
|
|
# coding: utf-8
|
|
|
|
|
2021-12-02 08:46:10 +13:00
|
|
|
from datetime import datetime
|
2021-09-28 15:04:15 +13:00
|
|
|
from dataclasses import dataclass, field
|
2020-05-15 00:52:33 +12:00
|
|
|
from enum import Enum
|
2021-12-02 08:46:10 +13:00
|
|
|
from typing import Optional, List, Dict
|
2020-05-15 00:52:33 +12:00
|
|
|
|
2021-12-02 08:46:10 +13:00
|
|
|
|
|
|
|
@dataclass
|
2020-04-15 00:47:46 +12:00
|
|
|
class GameAsset:
|
2021-12-02 08:46:10 +13:00
|
|
|
"""
|
|
|
|
App asset data
|
|
|
|
"""
|
|
|
|
app_name: str = ''
|
|
|
|
asset_id: str = ''
|
|
|
|
build_version: str = ''
|
|
|
|
catalog_item_id: str = ''
|
|
|
|
label_name: str = ''
|
|
|
|
namespace: str = ''
|
|
|
|
metadata: Dict = field(default_factory=dict)
|
2020-04-15 00:47:46 +12:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_egs_json(cls, json):
|
|
|
|
tmp = cls()
|
|
|
|
tmp.app_name = json.get('appName', '')
|
|
|
|
tmp.asset_id = json.get('assetId', '')
|
|
|
|
tmp.build_version = json.get('buildVersion', '')
|
|
|
|
tmp.catalog_item_id = json.get('catalogItemId', '')
|
|
|
|
tmp.label_name = json.get('labelName', '')
|
|
|
|
tmp.namespace = json.get('namespace', '')
|
|
|
|
tmp.metadata = json.get('metadata', {})
|
|
|
|
return tmp
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_json(cls, json):
|
|
|
|
tmp = cls()
|
|
|
|
tmp.app_name = json.get('app_name', '')
|
|
|
|
tmp.asset_id = json.get('asset_id', '')
|
|
|
|
tmp.build_version = json.get('build_version', '')
|
|
|
|
tmp.catalog_item_id = json.get('catalog_item_id', '')
|
|
|
|
tmp.label_name = json.get('label_name', '')
|
|
|
|
tmp.namespace = json.get('namespace', '')
|
|
|
|
tmp.metadata = json.get('metadata', {})
|
|
|
|
return tmp
|
|
|
|
|
|
|
|
|
2021-12-02 08:46:10 +13:00
|
|
|
@dataclass
|
2020-04-15 00:47:46 +12:00
|
|
|
class Game:
|
2021-12-02 08:46:10 +13:00
|
|
|
"""
|
|
|
|
Combination of app asset and app metadata as stored on disk
|
|
|
|
"""
|
|
|
|
app_name: str
|
|
|
|
app_title: str
|
|
|
|
|
|
|
|
asset_infos: Dict[str, GameAsset] = field(default_factory=dict)
|
|
|
|
base_urls: List[str] = field(default_factory=list)
|
|
|
|
metadata: Dict = field(default_factory=dict)
|
|
|
|
|
|
|
|
def app_version(self, platform='Windows'):
|
|
|
|
if platform not in self.asset_infos:
|
|
|
|
return None
|
|
|
|
return self.asset_infos[platform].build_version
|
2020-04-15 00:47:46 +12:00
|
|
|
|
2020-04-18 12:11:58 +12:00
|
|
|
@property
|
|
|
|
def is_dlc(self):
|
|
|
|
return self.metadata and 'mainGameItem' in self.metadata
|
|
|
|
|
2021-10-17 06:42:47 +13:00
|
|
|
@property
|
|
|
|
def third_party_store(self):
|
|
|
|
if not self.metadata:
|
|
|
|
return None
|
|
|
|
return self.metadata.get('customAttributes', {}).get('ThirdPartyManagedApp', {}).get('value', None)
|
|
|
|
|
2021-12-07 06:10:30 +13:00
|
|
|
@property
|
|
|
|
def partner_link_type(self):
|
|
|
|
if not self.metadata:
|
|
|
|
return None
|
|
|
|
return self.metadata.get('customAttributes', {}).get('partnerLinkType', {}).get('value', None)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def partner_link_id(self):
|
|
|
|
if not self.metadata:
|
|
|
|
return None
|
|
|
|
return self.metadata.get('customAttributes', {}).get('partnerLinkId', {}).get('value', None)
|
|
|
|
|
2020-05-15 02:04:03 +12:00
|
|
|
@property
|
|
|
|
def supports_cloud_saves(self):
|
|
|
|
return self.metadata and (self.metadata.get('customAttributes', {}).get('CloudSaveFolder') is not None)
|
|
|
|
|
2021-12-03 03:24:01 +13:00
|
|
|
@property
|
|
|
|
def supports_mac_cloud_saves(self):
|
|
|
|
return self.metadata and (self.metadata.get('customAttributes', {}).get('CloudSaveFolder_MAC') is not None)
|
|
|
|
|
2023-12-14 11:15:08 +13:00
|
|
|
@property
|
|
|
|
def additional_command_line(self):
|
|
|
|
if not self.metadata:
|
|
|
|
return None
|
|
|
|
return self.metadata.get('customAttributes', {}).get('AdditionalCommandLine', {}).get('value', None)
|
|
|
|
|
2023-12-15 02:54:25 +13:00
|
|
|
@property
|
|
|
|
def is_launchable_addon(self):
|
|
|
|
if not self.metadata:
|
|
|
|
return False
|
|
|
|
return any(m['path'] == 'addons/launchable' for m in self.metadata.get('categories', []))
|
|
|
|
|
2021-12-03 02:28:21 +13:00
|
|
|
@property
|
|
|
|
def catalog_item_id(self):
|
|
|
|
if not self.metadata:
|
|
|
|
return None
|
|
|
|
return self.metadata['id']
|
|
|
|
|
|
|
|
@property
|
|
|
|
def namespace(self):
|
|
|
|
if not self.metadata:
|
|
|
|
return None
|
|
|
|
return self.metadata['namespace']
|
|
|
|
|
2020-04-15 00:47:46 +12:00
|
|
|
@classmethod
|
|
|
|
def from_json(cls, json):
|
2021-12-02 08:46:10 +13:00
|
|
|
tmp = cls(
|
|
|
|
app_name=json.get('app_name', ''),
|
|
|
|
app_title=json.get('app_title', ''),
|
|
|
|
)
|
2020-04-15 00:47:46 +12:00
|
|
|
tmp.metadata = json.get('metadata', dict())
|
2021-12-02 08:46:10 +13:00
|
|
|
if 'asset_infos' in json:
|
|
|
|
tmp.asset_infos = {k: GameAsset.from_json(v) for k, v in json['asset_infos'].items()}
|
|
|
|
else:
|
|
|
|
# Migrate old asset_info to new asset_infos
|
|
|
|
tmp.asset_infos['Windows'] = GameAsset.from_json(json.get('asset_info', dict()))
|
|
|
|
|
2020-04-15 00:47:46 +12:00
|
|
|
tmp.base_urls = json.get('base_urls', list())
|
|
|
|
return tmp
|
|
|
|
|
|
|
|
@property
|
|
|
|
def __dict__(self):
|
2021-12-02 08:46:10 +13:00
|
|
|
"""This is just here so asset_infos gets turned into a dict as well"""
|
|
|
|
assets_dictified = {k: v.__dict__ for k, v in self.asset_infos.items()}
|
|
|
|
return dict(metadata=self.metadata, asset_infos=assets_dictified, app_name=self.app_name,
|
|
|
|
app_title=self.app_title, base_urls=self.base_urls)
|
2020-04-15 00:47:46 +12:00
|
|
|
|
|
|
|
|
2021-12-02 08:46:10 +13:00
|
|
|
@dataclass
|
2020-04-15 00:47:46 +12:00
|
|
|
class InstalledGame:
|
2021-12-02 08:46:10 +13:00
|
|
|
"""
|
|
|
|
Local metadata for an installed app
|
|
|
|
"""
|
|
|
|
app_name: str
|
|
|
|
install_path: str
|
|
|
|
title: str
|
|
|
|
version: str
|
|
|
|
|
|
|
|
base_urls: List[str] = field(default_factory=list)
|
|
|
|
can_run_offline: bool = False
|
|
|
|
egl_guid: str = ''
|
|
|
|
executable: str = ''
|
|
|
|
install_size: int = 0
|
|
|
|
install_tags: List[str] = field(default_factory=list)
|
|
|
|
is_dlc: bool = False
|
|
|
|
launch_parameters: str = ''
|
|
|
|
manifest_path: str = ''
|
|
|
|
needs_verification: bool = False
|
|
|
|
platform: str = 'Windows'
|
|
|
|
prereq_info: Optional[Dict] = None
|
2023-06-18 10:39:11 +12:00
|
|
|
uninstaller: Optional[Dict] = None
|
2021-12-02 08:46:10 +13:00
|
|
|
requires_ot: bool = False
|
|
|
|
save_path: Optional[str] = None
|
2020-04-15 00:47:46 +12:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_json(cls, json):
|
2021-12-02 08:46:10 +13:00
|
|
|
tmp = cls(
|
|
|
|
app_name=json.get('app_name', ''),
|
|
|
|
install_path=json.get('install_path', ''),
|
|
|
|
title=json.get('title', ''),
|
|
|
|
version=json.get('version', ''),
|
|
|
|
)
|
2020-04-15 00:47:46 +12:00
|
|
|
|
|
|
|
tmp.base_urls = json.get('base_urls', list())
|
|
|
|
tmp.executable = json.get('executable', '')
|
|
|
|
tmp.launch_parameters = json.get('launch_parameters', '')
|
|
|
|
tmp.prereq_info = json.get('prereq_info', None)
|
2023-06-18 10:39:11 +12:00
|
|
|
tmp.uninstaller = json.get('uninstaller', None)
|
2020-04-18 09:54:49 +12:00
|
|
|
|
2020-04-18 12:11:58 +12:00
|
|
|
tmp.can_run_offline = json.get('can_run_offline', False)
|
2020-04-18 09:54:49 +12:00
|
|
|
tmp.requires_ot = json.get('requires_ot', False)
|
2020-04-18 12:11:58 +12:00
|
|
|
tmp.is_dlc = json.get('is_dlc', False)
|
2020-05-15 00:52:33 +12:00
|
|
|
tmp.save_path = json.get('save_path', None)
|
2021-12-02 08:46:10 +13:00
|
|
|
tmp.manifest_path = json.get('manifest_path', '')
|
2020-05-30 10:05:03 +12:00
|
|
|
tmp.needs_verification = json.get('needs_verification', False) is True
|
2021-12-02 08:46:10 +13:00
|
|
|
tmp.platform = json.get('platform', 'Windows')
|
2020-05-29 09:05:36 +12:00
|
|
|
tmp.install_size = json.get('install_size', 0)
|
2020-05-29 09:10:15 +12:00
|
|
|
tmp.egl_guid = json.get('egl_guid', '')
|
2020-12-18 02:49:56 +13:00
|
|
|
tmp.install_tags = json.get('install_tags', [])
|
2020-04-15 00:47:46 +12:00
|
|
|
return tmp
|
2020-05-15 00:52:33 +12:00
|
|
|
|
|
|
|
|
2021-12-02 08:46:10 +13:00
|
|
|
@dataclass
|
2020-05-15 00:52:33 +12:00
|
|
|
class SaveGameFile:
|
2021-12-02 08:46:10 +13:00
|
|
|
"""
|
|
|
|
Metadata for a cloud save manifest
|
|
|
|
"""
|
|
|
|
app_name: str
|
|
|
|
filename: str
|
|
|
|
manifest_name: str
|
|
|
|
datetime: Optional[datetime] = None
|
2020-05-15 00:52:33 +12:00
|
|
|
|
|
|
|
|
|
|
|
class SaveGameStatus(Enum):
|
2020-05-16 22:50:28 +12:00
|
|
|
LOCAL_NEWER = 0
|
|
|
|
REMOTE_NEWER = 1
|
|
|
|
SAME_AGE = 2
|
|
|
|
NO_SAVE = 3
|
|
|
|
|
2020-05-20 04:19:15 +12:00
|
|
|
|
|
|
|
class VerifyResult(Enum):
|
|
|
|
HASH_MATCH = 0
|
|
|
|
HASH_MISMATCH = 1
|
|
|
|
FILE_MISSING = 2
|
2020-05-24 02:55:18 +12:00
|
|
|
OTHER_ERROR = 3
|
2021-09-28 15:04:15 +13:00
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class LaunchParameters:
|
2021-12-02 08:46:10 +13:00
|
|
|
"""
|
|
|
|
Parameters for launching a game
|
|
|
|
"""
|
2021-09-28 15:04:15 +13:00
|
|
|
# game-supplied parameters
|
|
|
|
game_parameters: list = field(default_factory=list)
|
|
|
|
game_executable: str = ''
|
|
|
|
game_directory: str = ''
|
|
|
|
# EGL parameters (auth, ovt, etc.)
|
|
|
|
egl_parameters: list = field(default_factory=list)
|
|
|
|
# command line before executable (WINE, gamemode, etc.)
|
|
|
|
launch_command: list = field(default_factory=list)
|
|
|
|
# working directory for launched process
|
|
|
|
working_directory: str = ''
|
|
|
|
# user and environment supplied options
|
|
|
|
user_parameters: list = field(default_factory=list)
|
|
|
|
environment: dict = field(default_factory=dict)
|
2022-01-10 02:21:11 +13:00
|
|
|
pre_launch_command: str = ''
|
|
|
|
pre_launch_wait: bool = False
|