feat(hsr): implement voicepack update
This commit is contained in:
parent
4c851b999a
commit
9f9a63c9b0
@ -83,12 +83,6 @@ class GameABC(ABC):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_voiceover_update(self, language: str):
|
|
||||||
"""
|
|
||||||
Get the voiceover update
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_channel(self):
|
def get_channel(self):
|
||||||
"""
|
"""
|
||||||
Get the game channel
|
Get the game channel
|
||||||
|
@ -339,7 +339,40 @@ class UpdateCommand(Command):
|
|||||||
f"<error>Couldn't apply update: {e} ({e.__context__})</error>"
|
f"<error>Couldn't apply update: {e} ({e.__context__})</error>"
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
progress.finish("<comment>Update applied.</comment>")
|
progress.finish("<comment>Update applied for base game.</comment>")
|
||||||
|
# Get installed voicepacks
|
||||||
|
installed_voicepacks = State.game.get_installed_voicepacks()
|
||||||
|
# Voicepack update
|
||||||
|
for remote_voicepack in update_diff.voice_packs:
|
||||||
|
if remote_voicepack.language not in installed_voicepacks:
|
||||||
|
continue
|
||||||
|
# Voicepack is installed, update it
|
||||||
|
archive_file = State.game._cache.joinpath(remote_voicepack.name)
|
||||||
|
try:
|
||||||
|
download_result = utils.download(
|
||||||
|
update_diff.path, archive_file, file_len=update_diff.size
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
self.line_error(f"<error>Couldn't download update: {e}</error>")
|
||||||
|
return
|
||||||
|
if not download_result:
|
||||||
|
self.line_error("<error>Download failed.</error>")
|
||||||
|
return
|
||||||
|
self.line("Download completed.")
|
||||||
|
progress = utils.ProgressIndicator(self)
|
||||||
|
progress.start("Applying update package...")
|
||||||
|
try:
|
||||||
|
State.game.apply_update_archive(
|
||||||
|
archive_file=archive_file, auto_repair=auto_repair
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
progress.finish(
|
||||||
|
f"<error>Couldn't apply update: {e} ({e.__context__})</error>"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
progress.finish(
|
||||||
|
f"<comment>Update applied for language {remote_voicepack.language}.</comment>"
|
||||||
|
)
|
||||||
self.line("Setting version config... ")
|
self.line("Setting version config... ")
|
||||||
self.set_version_config()
|
self.set_version_config()
|
||||||
self.line(
|
self.line(
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from configparser import ConfigParser
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from io import IOBase
|
from io import IOBase
|
||||||
from os import PathLike
|
from os import PathLike
|
||||||
@ -5,6 +6,7 @@ from pathlib import Path
|
|||||||
from vollerei.abc.launcher.game import GameABC
|
from vollerei.abc.launcher.game import GameABC
|
||||||
from vollerei.common import ConfigFile, functions
|
from vollerei.common import ConfigFile, functions
|
||||||
from vollerei.common.api import resource
|
from vollerei.common.api import resource
|
||||||
|
from vollerei.common.enums import VoicePackLanguage
|
||||||
from vollerei.exceptions.game import (
|
from vollerei.exceptions.game import (
|
||||||
GameAlreadyUpdatedError,
|
GameAlreadyUpdatedError,
|
||||||
GameNotInstalledError,
|
GameNotInstalledError,
|
||||||
@ -111,6 +113,42 @@ class Game(GameABC):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def get_channel(self) -> GameChannel:
|
||||||
|
"""
|
||||||
|
Gets the current game channel.
|
||||||
|
|
||||||
|
Only works for Star Rail version 1.0.5, other versions will return the
|
||||||
|
overridden channel or GameChannel.Overseas if no channel is overridden.
|
||||||
|
|
||||||
|
This is not needed for game patching, since the patcher will automatically
|
||||||
|
detect the channel.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
GameChannel: The current game channel.
|
||||||
|
"""
|
||||||
|
version = self._version_override or self.get_version()
|
||||||
|
if version == (1, 0, 5):
|
||||||
|
for channel, v in MD5SUMS["1.0.5"].values():
|
||||||
|
for file, md5sum in v.values():
|
||||||
|
if (
|
||||||
|
md5(self._path.joinpath(file).read_bytes()).hexdigest()
|
||||||
|
!= md5sum
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
match channel:
|
||||||
|
case "cn":
|
||||||
|
return GameChannel.China
|
||||||
|
case "os":
|
||||||
|
return GameChannel.Overseas
|
||||||
|
else:
|
||||||
|
# if self._path.joinpath("StarRail_Data").is_dir():
|
||||||
|
# return GameChannel.Overseas
|
||||||
|
# elif self._path.joinpath("StarRail_Data").exists():
|
||||||
|
# return GameChannel.China
|
||||||
|
# No reliable method there, so we'll just return the overridden channel or
|
||||||
|
# fallback to overseas.
|
||||||
|
return self._channel_override or GameChannel.Overseas
|
||||||
|
|
||||||
def get_version_config(self) -> tuple[int, int, int]:
|
def get_version_config(self) -> tuple[int, int, int]:
|
||||||
"""
|
"""
|
||||||
Gets the current installed game version from config.ini.
|
Gets the current installed game version from config.ini.
|
||||||
@ -147,11 +185,24 @@ class Game(GameABC):
|
|||||||
This method is meant to keep compatibility with the official launcher only.
|
This method is meant to keep compatibility with the official launcher only.
|
||||||
"""
|
"""
|
||||||
cfg_file = self._path.joinpath("config.ini")
|
cfg_file = self._path.joinpath("config.ini")
|
||||||
if not cfg_file.exists():
|
if cfg_file.exists():
|
||||||
raise FileNotFoundError("config.ini not found.")
|
cfg = ConfigFile(cfg_file)
|
||||||
cfg = ConfigFile(cfg_file)
|
cfg.set("General", "game_version", self.get_version_str())
|
||||||
cfg.set("General", "game_version", self.get_version_str())
|
cfg.save()
|
||||||
cfg.save()
|
else:
|
||||||
|
cfg = ConfigParser()
|
||||||
|
cfg.read_dict(
|
||||||
|
{
|
||||||
|
"General": {
|
||||||
|
"channel": 1,
|
||||||
|
"cps": "hoyoverse_PC",
|
||||||
|
"game_version": self.get_version_str(),
|
||||||
|
"sub_channel": 1,
|
||||||
|
"plugin_2_version": "0.0.1",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
cfg.write(cfg_file.open("w"))
|
||||||
|
|
||||||
def get_version(self) -> tuple[int, int, int]:
|
def get_version(self) -> tuple[int, int, int]:
|
||||||
"""
|
"""
|
||||||
@ -232,41 +283,27 @@ class Game(GameABC):
|
|||||||
"""
|
"""
|
||||||
return ".".join(str(i) for i in self.get_version())
|
return ".".join(str(i) for i in self.get_version())
|
||||||
|
|
||||||
def get_channel(self) -> GameChannel:
|
def get_installed_voicepacks(self) -> list[VoicePackLanguage]:
|
||||||
"""
|
"""
|
||||||
Gets the current game channel.
|
Gets the installed voicepacks.
|
||||||
|
|
||||||
Only works for Star Rail version 1.0.5, other versions will return the
|
|
||||||
overridden channel or GameChannel.Overseas if no channel is overridden.
|
|
||||||
|
|
||||||
This is not needed for game patching, since the patcher will automatically
|
|
||||||
detect the channel.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
GameChannel: The current game channel.
|
list[VoicePackLanguage]: A list of installed voicepacks.
|
||||||
"""
|
"""
|
||||||
version = self._version_override or self.get_version()
|
if not self.is_installed():
|
||||||
if version == (1, 0, 5):
|
raise GameNotInstalledError("Game is not installed.")
|
||||||
for channel, v in MD5SUMS["1.0.5"].values():
|
voicepacks = []
|
||||||
for file, md5sum in v.values():
|
for child in (
|
||||||
if (
|
self.data_folder()
|
||||||
md5(self._path.joinpath(file).read_bytes()).hexdigest()
|
.joinpath("Persistent/Audio/AudioPackage/Windows/")
|
||||||
!= md5sum
|
.iterdir()
|
||||||
):
|
):
|
||||||
continue
|
if child.is_dir():
|
||||||
match channel:
|
try:
|
||||||
case "cn":
|
voicepacks.append(VoicePackLanguage(child.name))
|
||||||
return GameChannel.China
|
except ValueError:
|
||||||
case "os":
|
pass
|
||||||
return GameChannel.Overseas
|
return voicepacks
|
||||||
else:
|
|
||||||
# if self._path.joinpath("StarRail_Data").is_dir():
|
|
||||||
# return GameChannel.Overseas
|
|
||||||
# elif self._path.joinpath("StarRail_Data").exists():
|
|
||||||
# return GameChannel.China
|
|
||||||
# No reliable method there, so we'll just return the overridden channel or
|
|
||||||
# fallback to overseas.
|
|
||||||
return self._channel_override or GameChannel.Overseas
|
|
||||||
|
|
||||||
def get_remote_game(self, pre_download: bool = False) -> resource.Game:
|
def get_remote_game(self, pre_download: bool = False) -> resource.Game:
|
||||||
"""
|
"""
|
||||||
@ -393,7 +430,20 @@ class Game(GameABC):
|
|||||||
update_info = self.get_update()
|
update_info = self.get_update()
|
||||||
if not update_info or update_info.version == self.get_version_str():
|
if not update_info or update_info.version == self.get_version_str():
|
||||||
raise GameAlreadyUpdatedError("Game is already updated.")
|
raise GameAlreadyUpdatedError("Game is already updated.")
|
||||||
|
# Base game update
|
||||||
archive_file = self._cache.joinpath(update_info.name)
|
archive_file = self._cache.joinpath(update_info.name)
|
||||||
download(update_info.path, archive_file)
|
download(update_info.path, archive_file)
|
||||||
self.apply_update_archive(archive_file=archive_file, auto_repair=auto_repair)
|
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:
|
||||||
|
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)
|
||||||
|
self.apply_update_archive(
|
||||||
|
archive_file=archive_file, auto_repair=auto_repair
|
||||||
|
)
|
||||||
self.set_version_config()
|
self.set_version_config()
|
||||||
|
Loading…
Reference in New Issue
Block a user