diff --git a/vollerei/cli/hsr.py b/vollerei/cli/hsr.py index c651ee2..fc5f052 100644 --- a/vollerei/cli/hsr.py +++ b/vollerei/cli/hsr.py @@ -1,8 +1,10 @@ +import traceback from cleo.commands.command import Command from cleo.helpers import option, argument from copy import deepcopy +from pathlib import PurePath from platform import system -from vollerei.hsr.launcher.enums import GameChannel +from vollerei.common.enums import GameChannel from vollerei.cli import utils from vollerei.exceptions.game import GameError from vollerei.hsr import Game, Patcher @@ -434,11 +436,12 @@ class UpdateCommand(Command): update_diff = State.game.get_update(pre_download=pre_download) game_info = State.game.get_remote_game(pre_download=pre_download) except Exception as e: + print(traceback.format_exc()) progress.finish( f"Update checking failed with following error: {e} ({e.__context__})" ) return - if update_diff is None: + if update_diff is None or isinstance(game_info.major, str | None): progress.finish("Game is already updated.") return progress.finish("Update available.") @@ -446,16 +449,17 @@ class UpdateCommand(Command): f"The current version is: {State.game.get_version_str()}" ) self.line( - f"The latest version is: {game_info.latest.version}" + f"The latest version is: {game_info.major.version}" ) if not self.confirm("Do you want to update the game?"): self.line("Update aborted.") return self.line("Downloading update package...") - out_path = State.game.cache.joinpath(update_diff.name) + update_game_url = update_diff.game_pkgs[0].url + out_path = State.game.cache.joinpath(PurePath(update_game_url).name) try: download_result = utils.download( - update_diff.path, out_path, file_len=update_diff.size + update_game_url, out_path, file_len=update_diff.game_pkgs[0].size ) except Exception as e: self.line_error(f"Couldn't download update: {e}") @@ -478,14 +482,14 @@ class UpdateCommand(Command): # Get installed voicepacks installed_voicepacks = State.game.get_installed_voicepacks() # Voicepack update - for remote_voicepack in update_diff.voice_packs: + for remote_voicepack in update_diff.audio_pkgs: if remote_voicepack.language not in installed_voicepacks: continue # Voicepack is installed, update it - archive_file = State.game.cache.joinpath(remote_voicepack.name) + archive_file = State.game.cache.joinpath(PurePath(remote_voicepack.url).name) try: download_result = utils.download( - remote_voicepack.path, archive_file, file_len=update_diff.size + remote_voicepack.url, archive_file, file_len=remote_voicepack.size ) except Exception as e: self.line_error(f"Couldn't download update: {e}") diff --git a/vollerei/common/api/__init__.py b/vollerei/common/api/__init__.py index a5c2823..b556abb 100644 --- a/vollerei/common/api/__init__.py +++ b/vollerei/common/api/__init__.py @@ -1,4 +1,35 @@ -from vollerei.common.api.resource import Resource +import requests +from vollerei.common.api import resource +from vollerei.common.enums import GameChannel +from vollerei.constants import LAUNCHER_API -__all__ = ["Resource"] +__all__ = ["GamePackage"] + + +def get_game_packages( + channel: GameChannel = GameChannel.Overseas, +) -> list[resource.GameInfo]: + """ + Get game packages information from the launcher API. + + Default channel is overseas. + + Args: + channel: Game channel to get the resource information from. + + Returns: + Resource: Game resource information. + """ + resource_path: dict = None + match channel: + case GameChannel.Overseas: + resource_path = LAUNCHER_API.OS + case GameChannel.China: + resource_path = LAUNCHER_API.CN + return resource.from_dict( + requests.get( + resource_path["url"] + LAUNCHER_API.RESOURCE_PATH, + params=resource_path["params"], + ).json()["data"] + ) diff --git a/vollerei/common/api/resource.py b/vollerei/common/api/resource.py index e323eda..1a3e357 100644 --- a/vollerei/common/api/resource.py +++ b/vollerei/common/api/resource.py @@ -1,409 +1,134 @@ -""" -Class wrapper for API endpoint /resource -""" - from vollerei.common.enums import VoicePackLanguage -class Segment: - """ - A segment of the game archive. - - Attributes: - path (str): Segment download path. - md5 (str): Segment md5 checksum. - package_size (int | None): Segment package size. - """ - - path: str - md5: str - # str -> int and checked if int is 0 then None - package_size: int | None - - def __init__(self, path: str, md5: str, package_size: int | None) -> None: - self.path = path - self.md5 = md5 - self.package_size = package_size - - @staticmethod - def from_dict(data: dict) -> "Segment": - return Segment( - data["path"], - data["md5"], - ( - int(data["package_size"]) - if data["package_size"] and data["package_size"] != "0" - else None - ), - ) - - -class VoicePack: - """ - Voice pack information - - `name` maybe converted from `path` if the server returns empty string. - - Attributes: - language (VoicePackLanguage): Language of the voice pack. - name (str): Voice pack archive name. - path (str): Voice pack download path. - size (int): Voice pack size. - md5 (str): Voice pack md5 checksum. - package_size (int): Voice pack package size. - """ - - language: VoicePackLanguage - name: str - path: str - # str -> int - size: int - md5: str - # str -> int - package_size: int - - def __init__( - self, - language: VoicePackLanguage, - name: str, - path: str, - size: int, - md5: str, - package_size: int, - ) -> None: - self.language = language - self.name = name - self.path = path - self.size = size - self.md5 = md5 - self.package_size = package_size - - @staticmethod - def from_dict(data: dict) -> "VoicePack": - return VoicePack( - VoicePackLanguage.from_remote_str(data["language"]), - data["name"], - data["path"], - int(data["size"]), - data["md5"], - int(data["package_size"]), - ) - - -class Diff: - """ - Game resource diff from a version to latest information - - Attributes: - TODO - """ - - name: str - version: str - path: str - # str -> int - size: int - md5: str - is_recommended_update: bool - voice_packs: list[VoicePack] - # str -> int - package_size: int - - def __init__( - self, - name: str, - version: str, - path: str, - size: int, - md5: str, - is_recommended_update: bool, - voice_packs: list[VoicePack], - package_size: int, - ) -> None: - self.name = name - self.version = version - self.path = path - self.size = size - self.md5 = md5 - self.is_recommended_update = is_recommended_update - self.voice_packs = voice_packs - self.package_size = package_size - - @staticmethod - def from_dict(data: dict) -> "Diff": - return Diff( - data["name"], - data["version"], - data["path"], - int(data["size"]), - data["md5"], - data["is_recommended_update"], - [VoicePack.from_dict(i) for i in data["voice_packs"]], - int(data["package_size"]), - ) - - -class Latest: - """ - Latest game resource information - - `name` maybe converted from `path` if the server returns empty string, - and if `path` is empty too then it'll convert the name from the first - segment of `segments` list. - - `path` maybe None if the server returns empty string, in that case - you'll have to download the game using `segments` list and merge them. - - `voice_packs` will be empty for Star Rail, they force you to download - in-game instead. - - `decompressed_path` is useful for repairing game files by only having - to re-download the corrupted files. - - `segments` is a list of game archive segments, you'll have to download - them and merge them together to get the full game archive. Not available - on Star Rail. - - Attributes: - name (str): Game archive name. - version (str): Game version in the archive. - path (str | None): Game archive download path. - size (int): Game archive size in bytes. - md5 (str): Game archive MD5 checksum. - entry (str): Game entry file (e.g. GenshinImpact.exe). - voice_packs (list[VoicePack]): Game voice packs. - decompressed_path (str | None): Game archive decompressed path. - segments (list[Segment]): Game archive segments. - package_size (int): Game archive package size in bytes. - """ - - name: str - version: str - path: str | None - # str -> int - size: int - md5: str - entry: str - voice_packs: list[VoicePack] - # str but checked for empty string - decompressed_path: str | None - segments: list[Segment] - # str -> int - package_size: int - - def __init__( - self, - name: str, - version: str, - path: str, - size: int, - md5: str, - entry: str, - voice_packs: list[VoicePack], - decompressed_path: str | None, - segments: list[Segment], - package_size: int, - ) -> None: - self.name = name - self.version = version - self.path = path - self.size = size - self.md5 = md5 - self.entry = entry - self.voice_packs = voice_packs - self.decompressed_path = decompressed_path - self.segments = segments - self.package_size = package_size - - @staticmethod - def from_dict(data: dict) -> "Latest": - if data["name"] == "": - if data["path"] == "": - data["name"] = data["segments"][0]["path"].split("/")[-1] - else: - data["name"] = data["path"].split("/")[-1] - return Latest( - data["name"], - data["version"], - data["path"] if data["path"] != "" else None, - int(data["size"]), - data["md5"], - data["entry"], - [VoicePack.from_dict(i) for i in data["voice_packs"]], - data["decompressed_path"] if data["decompressed_path"] != "" else None, - [Segment.from_dict(i) for i in data["segments"]], - int(data["package_size"]), - ) - - class Game: - latest: Latest - diffs: list[Diff] - - def __init__(self, latest: Latest, diffs: list[Diff]) -> None: - self.latest = latest - self.diffs = diffs + def __init__(self, id: str, biz: str): + self.id = id + self.biz = biz @staticmethod def from_dict(data: dict) -> "Game": - return Game( - Latest.from_dict(data["latest"]), [Diff.from_dict(i) for i in data["diffs"]] - ) + return Game(id=data["id"], biz=data["biz"]) -class Plugin: - name: str - # str but checked for empty string - version: str | None - path: str - # str -> int - size: int - md5: str - # str but checked for empty string - entry: str | None - # str -> int - package_size: int - - def __init__( - self, - name: str, - version: str | None, - path: str, - size: int, - md5: str, - entry: str | None, - package_size: int, - ) -> None: - self.name = name - self.version = version - self.path = path +class GamePackage: + def __init__(self, url: str, md5: str, size: int, decompressed_size: int): + self.url = url + self.md5 = md5 self.size = size - self.md5 = md5 - self.entry = entry - self.package_size = package_size + self.decompressed_size = decompressed_size @staticmethod - def from_dict(data: dict) -> "Plugin": - return Plugin( - data["name"], - data["version"] if data["version"] != "" else None, - data["path"], - int(data["size"]), - data["md5"], - data["entry"] if data["entry"] != "" else None, - int(data["package_size"]), + def from_dict(data: dict) -> "GamePackage": + return GamePackage( + url=data["url"], + md5=data["md5"], + size=int(data["size"]), + decompressed_size=int(data["decompressed_size"]), ) -class LauncherPlugin: - plugins: list[Plugin] - # str -> int - version: int - - def __init__(self, plugins: list[Plugin], version: int) -> None: - self.plugins = plugins - self.version = version - - @staticmethod - def from_dict(data: dict) -> "LauncherPlugin": - return LauncherPlugin( - [Plugin.from_dict(i) for i in data["plugins"]], int(data["version"]) - ) - - -class DeprecatedPackage: - name: str - md5: str - - def __init__(self, name: str, md5: str) -> None: - self.name = name - self.md5 = md5 - - @staticmethod - def from_dict(data: dict) -> "DeprecatedPackage": - return DeprecatedPackage(data["name"], data["md5"]) - - -class DeprecatedFile: - path: str - # str but checked for empty string - md5: str | None - - def __init__(self, path: str, md5: str | None) -> None: - self.path = path - self.md5 = md5 - - @staticmethod - def from_dict(data: dict) -> "DeprecatedFile": - return DeprecatedFile(data["path"], data["md5"] if data["md5"] != "" else None) - - -class Resource: - """ - Data class for /resource endpoint - - I'm still unclear about `force_update` and `sdk` attributes, so I'll - leave them as None for now. - - Attributes: - game (Game): Game resource information. - plugin (LauncherPlugin): Launcher plugin information. - web_url (str): Game official launcher web URL. - force_update (None): Not used by official launcher I guess? - pre_download_game (Game | None): Pre-download game resource information. - deprecated_packages (list[DeprecatedPackage]): Deprecated game packages. - sdk (None): Maybe for Bilibili version of Genshin? - deprecated_files (list[DeprecatedFile]): Deprecated game files. - """ - - # I'm generous enough to convert the string into int - # for you guys, wtf Mihoyo? - game: Game - # ?? Mihoyo for plugin["plugins"] which is a list of Plugin objects - plugin: LauncherPlugin - web_url: str - # ?? Mihoyo - force_update: None - # Will be a Game object if a pre-download is available. - pre_download_game: Game | None - deprecated_packages: list[DeprecatedPackage] - # Maybe a SDK for Bilibili version in Genshin? - sdk: None - deprecated_files: list[DeprecatedFile] - +class AudioPackage: def __init__( self, - game: Game, - plugin: Plugin, - web_url: str, - force_update: None, - pre_download_game: Game | None, - deprecated_packages: list[DeprecatedPackage], - sdk: None, - deprecated_files: list[DeprecatedFile], - ) -> None: - self.game = game - self.plugin = plugin - self.web_url = web_url - self.force_update = force_update - self.pre_download_game = pre_download_game - self.deprecated_packages = deprecated_packages - self.sdk = sdk - self.deprecated_files = deprecated_files + language: VoicePackLanguage, + url: str, + md5: str, + size: int, + decompressed_size: int, + ): + self.language = language + self.url = url + self.md5 = md5 + self.size = size + self.decompressed_size = decompressed_size @staticmethod - def from_dict(json: dict) -> "Resource": - return Resource( - Game.from_dict(json["game"]), - LauncherPlugin.from_dict(json["plugin"]), - json["web_url"], - json["force_update"], - ( - Game.from_dict(json["pre_download_game"]) - if json["pre_download_game"] - else None - ), - [DeprecatedPackage.from_dict(x) for x in json["deprecated_packages"]], - json["sdk"], - [DeprecatedFile.from_dict(x) for x in json["deprecated_files"]], + def from_dict(data: dict) -> "AudioPackage": + return AudioPackage( + language=VoicePackLanguage.from_remote_str(data["language"]), + url=data["url"], + md5=data["md5"], + size=int(data["size"]), + decompressed_size=int(data["decompressed_size"]), ) + + +class Major: + def __init__( + self, + version: str, + game_pkgs: list[GamePackage], + audio_pkgs: list[AudioPackage], + res_list_url: str, + ): + self.version = version + self.game_pkgs = game_pkgs + self.audio_pkgs = audio_pkgs + self.res_list_url = res_list_url + + @staticmethod + def from_dict(data: dict) -> "Major": + return Major( + version=data["version"], + game_pkgs=[GamePackage(**x) for x in data["game_pkgs"]], + audio_pkgs=[AudioPackage(**x) for x in data["audio_pkgs"]], + res_list_url=data["res_list_url"], + ) + + +# Currently patch has the same fields as major +Patch = Major + + +class Main: + def __init__(self, major: Major, patches: list[Patch]): + self.major = major + self.patches = patches + + @staticmethod + def from_dict(data: dict) -> "Main": + return Main( + major=Major.from_dict(data["major"]), + patches=[Patch.from_dict(x) for x in data["patches"]], + ) + + +class PreDownload: + def __init__(self, major: Major | str | None, patches: list[Patch]): + self.major = major + self.patches = patches + + @staticmethod + def from_dict(data: dict) -> "PreDownload": + return PreDownload( + major=( + data["major"] + if isinstance(data["major"], str | None) + else Major.from_dict(data["major"]) + ), + patches=[Patch.from_dict(x) for x in data["patches"]], + ) + + +# Why miHoYo uses the same name "game_packages" for this big field and smol field +class GameInfo: + def __init__(self, game: Game, main: Main, pre_download: PreDownload): + self.game = game + self.main = main + self.pre_download = pre_download + + @staticmethod + def from_dict(data: dict) -> "GameInfo": + return GameInfo( + game=Game.from_dict(data["game"]), + main=Main.from_dict(data["main"]), + pre_download=PreDownload.from_dict(data["pre_download"]), + ) + + +def from_dict(data: dict) -> list[GameInfo]: + game_pkgs = [] + for pkg in data["game_packages"]: + game_pkgs.append(GameInfo.from_dict(pkg)) + return game_pkgs diff --git a/vollerei/common/enums.py b/vollerei/common/enums.py index 2ae981c..9f8e24a 100644 --- a/vollerei/common/enums.py +++ b/vollerei/common/enums.py @@ -1,6 +1,11 @@ from enum import Enum +class GameChannel(Enum): + Overseas = 0 + China = 1 + + class VoicePackLanguage(Enum): Japanese = "ja-jp" Chinese = "zh-cn" diff --git a/vollerei/constants.py b/vollerei/constants.py index 43a5de1..598a0a3 100644 --- a/vollerei/constants.py +++ b/vollerei/constants.py @@ -1,3 +1,21 @@ +class LAUNCHER_API: + """Launcher API constants.""" + + RESOURCE_PATH: str = "hyp/hyp-connect/api/getGamePackages" + OS: dict = { + "url": "https://sg-hyp-api.hoyoverse.com/", + "params": { + "launcher_id": "VYTpXlbWo8", + }, + } + CN: dict = { + "url": "https://hyp-api.mihoyo.com/", + "params": { + "launcher_id": "jGHBHlcOq1", + }, + } + + TELEMETRY_HOSTS = [ # Global "log-upload-os.hoyoverse.com", diff --git a/vollerei/hi3/constants.py b/vollerei/hi3/constants.py deleted file mode 100644 index 54fceae..0000000 --- a/vollerei/hi3/constants.py +++ /dev/null @@ -1,24 +0,0 @@ -class LAUNCHER_API: - """Launcher API constants.""" - - RESOURCE_PATH: str = "mdk/launcher/api/resource" - OS: dict = { - "url": "https://hkrpg-launcher-static.hoyoverse.com/hkrpg_global/", - "params": { - "channel_id": 1, - "key": "vplOVX8Vn7cwG8yb", - "launcher_id": 35, - }, - } - ASIA: dict = {} - CN: dict = { - "url": "https://api-launcher.mihoyo.com/hkrpg_cn/mdk/launcher/api/resource", - "params": { - "channel_id": 1, - "key": "6KcVuOkbcqjJomjZ", - "launcher_id": 33, - }, - } - - -LATEST_VERSION = (7, 2, 0) diff --git a/vollerei/hi3/launcher/enums.py b/vollerei/hi3/launcher/enums.py deleted file mode 100644 index fe1f2e0..0000000 --- a/vollerei/hi3/launcher/enums.py +++ /dev/null @@ -1,9 +0,0 @@ -from enum import Enum - - -class GameChannel(Enum): - Global = 0 - Asia = 1 - Taiwan = 2 - Korea = 3 - China = 4 diff --git a/vollerei/hsr/constants.py b/vollerei/hsr/constants.py index 6617df9..3977b25 100644 --- a/vollerei/hsr/constants.py +++ b/vollerei/hsr/constants.py @@ -1,26 +1,4 @@ -class LAUNCHER_API: - """Launcher API constants.""" - - RESOURCE_PATH: str = "mdk/launcher/api/resource" - OS: dict = { - "url": "https://hkrpg-launcher-static.hoyoverse.com/hkrpg_global/", - "params": { - "channel_id": 1, - "key": "vplOVX8Vn7cwG8yb", - "launcher_id": 35, - }, - } - CN: dict = { - "url": "https://api-launcher.mihoyo.com/hkrpg_cn/mdk/launcher/api/resource", - "params": { - "channel_id": 1, - "key": "6KcVuOkbcqjJomjZ", - "launcher_id": 33, - }, - } - - -LATEST_VERSION = (1, 6, 0) +LATEST_VERSION = (2, 5, 0) MD5SUMS = { "1.0.5": { "cn": { diff --git a/vollerei/hsr/launcher/api.py b/vollerei/hsr/launcher/api.py index 69598f9..f7864f9 100644 --- a/vollerei/hsr/launcher/api.py +++ b/vollerei/hsr/launcher/api.py @@ -1,13 +1,10 @@ -import requests - -from vollerei.common.api import Resource -from vollerei.hsr.constants import LAUNCHER_API -from vollerei.hsr.launcher.enums import GameChannel +from vollerei.common.api import get_game_packages, resource +from vollerei.common.enums import GameChannel -def get_resource(channel: GameChannel = GameChannel.Overseas) -> Resource: +def get_game_package(channel: GameChannel = GameChannel.Overseas) -> resource.GameInfo: """ - Get game resource information from the launcher API. + Get game package information from the launcher API. Default channel is overseas. @@ -17,15 +14,7 @@ def get_resource(channel: GameChannel = GameChannel.Overseas) -> Resource: Returns: Resource: Game resource information. """ - resource_path: dict = None - match channel: - case GameChannel.Overseas: - resource_path = LAUNCHER_API.OS - case GameChannel.China: - resource_path = LAUNCHER_API.CN - return Resource.from_dict( - requests.get( - resource_path["url"] + LAUNCHER_API.RESOURCE_PATH, - params=resource_path["params"], - ).json()["data"] - ) + game_packages = get_game_packages(channel=channel) + for package in game_packages: + if "hkrpg" in package.game.biz: + return package diff --git a/vollerei/hsr/launcher/enums.py b/vollerei/hsr/launcher/enums.py deleted file mode 100644 index 4726217..0000000 --- a/vollerei/hsr/launcher/enums.py +++ /dev/null @@ -1,6 +0,0 @@ -from enum import Enum - - -class GameChannel(Enum): - Overseas = 0 - China = 1 diff --git a/vollerei/hsr/launcher/game.py b/vollerei/hsr/launcher/game.py index 6c2669c..57be18f 100644 --- a/vollerei/hsr/launcher/game.py +++ b/vollerei/hsr/launcher/game.py @@ -3,12 +3,12 @@ from configparser import ConfigParser from hashlib import md5 from io import IOBase from os import PathLike -from pathlib import Path +from pathlib import Path, PurePath from shutil import move, copyfile from vollerei.abc.launcher.game import GameABC from vollerei.common import ConfigFile, functions from vollerei.common.api import resource -from vollerei.common.enums import VoicePackLanguage +from vollerei.common.enums import VoicePackLanguage, GameChannel from vollerei.exceptions.game import ( GameAlreadyUpdatedError, GameNotInstalledError, @@ -16,7 +16,6 @@ from vollerei.exceptions.game import ( ScatteredFilesNotAvailableError, ) from vollerei.hsr.constants import MD5SUMS -from vollerei.hsr.launcher.enums import GameChannel from vollerei.hsr.launcher import api from vollerei import paths from vollerei.utils import download @@ -189,7 +188,7 @@ class Game(GameABC): cfg_file = self._path.joinpath("config.ini") if cfg_file.exists(): cfg = ConfigFile(cfg_file) - cfg.set("General", "game_version", self.get_version_str()) + cfg.set("general", "game_version", self.get_version_str()) cfg.save() else: cfg = ConfigParser() @@ -307,7 +306,7 @@ class Game(GameABC): pass return voicepacks - def get_remote_game(self, pre_download: bool = False) -> resource.Game: + def get_remote_game(self, pre_download: bool = False) -> resource.Main | resource.PreDownload: """ Gets the current game information from remote. @@ -316,17 +315,17 @@ class Game(GameABC): Defaults to False. Returns: - A `Game` object that contains the game information. + A `Main` or `PreDownload` object that contains the game information. """ channel = self._channel_override or self.get_channel() if pre_download: - game = api.get_resource(channel=channel).pre_download_game + game = api.get_game_package(channel=channel).pre_download if not game: raise PreDownloadNotAvailable("Pre-download version is not available.") return game - return api.get_resource(channel=channel).game + return api.get_game_package(channel=channel).main - def get_update(self, pre_download: bool = False) -> resource.Diff | None: + def get_update(self, pre_download: bool = False) -> resource.Patch | None: """ Gets the current game update. @@ -335,7 +334,7 @@ class Game(GameABC): Defaults to False. Returns: - A `Diff` object that contains the update information or + A `Patch` object that contains the update information or `None` if the game is not installed or already up-to-date. """ if not self.is_installed(): @@ -345,9 +344,9 @@ class Game(GameABC): if self._version_override else self.get_version_str() ) - for diff in self.get_remote_game(pre_download=pre_download).diffs: - if diff.version == version: - return diff + for patch in self.get_remote_game(pre_download=pre_download).patches: + if patch.version == version: + return patch return None def _repair_file(self, file: PathLike, game: resource.Game) -> None: @@ -486,10 +485,10 @@ class Game(GameABC): functions.apply_update_archive(self, archive_file, auto_repair=auto_repair) def install_update( - self, update_info: resource.Diff = None, auto_repair: bool = True + self, update_info: resource.Patch = None, auto_repair: bool = True ): """ - Installs an update from a `Diff` object. + Installs an update from a `Patch` object. You may want to download the update manually and pass it to `apply_update_archive()` instead for better control, and after that @@ -506,19 +505,20 @@ class Game(GameABC): update_info = self.get_update() if not update_info or update_info.version == self.get_version_str(): raise GameAlreadyUpdatedError("Game is already updated.") + update_url = update_info.game_pkgs[0].url # Base game update - archive_file = self.cache.joinpath(update_info.name) - download(update_info.path, archive_file) + archive_file = self.cache.joinpath(PurePath(update_url).name) + download(update_url, archive_file) self.apply_update_archive(archive_file=archive_file, auto_repair=auto_repair) # Get installed voicepacks installed_voicepacks = self.get_installed_voicepacks() # Voicepack update - for remote_voicepack in update_info.voice_packs: + for remote_voicepack in update_info.audio_pkgs: if remote_voicepack.language not in installed_voicepacks: continue # Voicepack is installed, update it - archive_file = self.cache.joinpath(remote_voicepack.name) - download(remote_voicepack.path, archive_file) + archive_file = self.cache.joinpath(PurePath(remote_voicepack.url).name) + download(remote_voicepack.url, archive_file) self.apply_update_archive( archive_file=archive_file, auto_repair=auto_repair ) diff --git a/vollerei/utils/hdiffpatch/__init__.py b/vollerei/utils/hdiffpatch/__init__.py index ba66684..07216ed 100644 --- a/vollerei/utils/hdiffpatch/__init__.py +++ b/vollerei/utils/hdiffpatch/__init__.py @@ -34,6 +34,8 @@ class HDiffPatch: return "windows32" case "x86_64": return "windows64" + case "AMD64": + return "windows64" case "arm": return "windows_arm32" case "arm64":