Compare commits

..

No commits in common. "ca03718bf11880e5400e8da5b63a3cd16eb3b08a" and "80f1ea87d7760355699ee8eb6657cf8dd66bfc25" have entirely different histories.

5 changed files with 50 additions and 144 deletions

View File

@ -10,7 +10,6 @@ class GameABC(ABC):
""" """
path: Path path: Path
cache: Path
version_override: tuple[int, int, int] | None version_override: tuple[int, int, int] | None
channel_override: Any channel_override: Any
@ -84,6 +83,12 @@ 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

View File

@ -25,7 +25,7 @@ default_options = [
option("patch-type", "p", description="Patch type", flag=False), option("patch-type", "p", description="Patch type", flag=False),
option("temporary-path", "t", description="Temporary path", flag=False), option("temporary-path", "t", description="Temporary path", flag=False),
option("silent", "s", description="Silent mode"), option("silent", "s", description="Silent mode"),
option("noconfirm", "y", description="Do not ask for confirmation (yes to all)"), option("noconfirm", "y", description="Do not ask for confirmation"),
] ]
@ -62,16 +62,6 @@ def callback(
utils.silent_message = silent utils.silent_message = silent
if noconfirm: if noconfirm:
utils.no_confirm = noconfirm utils.no_confirm = noconfirm
def confirm(
question: str, default: bool = False, true_answer_regex: str = r"(?i)^y"
):
command.line(
f"<question>{question} (yes/no)</question> [<comment>{'yes' if default else 'no'}</comment>] y"
)
return True
command.confirm = confirm
command.add_style("warn", fg="yellow") command.add_style("warn", fg="yellow")
@ -126,13 +116,8 @@ class PatchInstallCommand(Command):
self.line( self.line(
"You need to <warn>run the game using Jadeite</warn> to use the patch." "You need to <warn>run the game using Jadeite</warn> to use the patch."
) )
self.line(f'E.g: <question>{exe_path} "{State.game.path}"</question>')
print()
self.line( self.line(
"To activate the experimental patching method, set the environment variable BREAK_CATHACK=1" f'E.g: <question>I_WANT_A_BAN=1 {exe_path} "{State.game.path}"</question>'
)
self.line(
"Read more about it here: https://codeberg.org/mkrsym1/jadeite/issues/37"
) )
print() print()
self.line( self.line(
@ -317,7 +302,7 @@ class UpdateCommand(Command):
self.line("<error>Update aborted.</error>") self.line("<error>Update aborted.</error>")
return return
self.line("Downloading update package...") self.line("Downloading update package...")
out_path = State.game.cache.joinpath(update_diff.name) out_path = State.game._cache.joinpath(update_diff.name)
try: try:
download_result = utils.download( download_result = utils.download(
update_diff.path, out_path, file_len=update_diff.size update_diff.path, out_path, file_len=update_diff.size
@ -339,40 +324,7 @@ 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 for base game.</comment>") progress.finish("<comment>Update applied.</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(

View File

@ -47,7 +47,6 @@ class ProgressIndicator:
interval=interval, values=values interval=interval, values=values
) )
self.thread = Thread(target=self.auto_advance) self.thread = Thread(target=self.auto_advance)
self.thread.daemon = True
def start(self, message: str): def start(self, message: str):
""" """

View File

@ -67,7 +67,7 @@ def apply_update_archive(
# Patch function # Patch function
def extract_and_patch(file, patch_file): def extract_and_patch(file, patch_file):
patchpath = game.cache.joinpath(patch_file) patchpath = game._cache.joinpath(patch_file)
# Delete old patch file if exists # Delete old patch file if exists
patchpath.unlink(missing_ok=True) patchpath.unlink(missing_ok=True)
# Extract patch file # Extract patch file

View File

@ -1,4 +1,3 @@
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
@ -6,7 +5,6 @@ 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,
@ -34,8 +32,8 @@ class Game(GameABC):
if not cache_path: if not cache_path:
cache_path = paths.cache_path cache_path = paths.cache_path
cache_path = Path(cache_path) cache_path = Path(cache_path)
self.cache: Path = cache_path.joinpath("game/hsr/") self._cache: Path = cache_path.joinpath("game/hsr/")
self.cache.mkdir(parents=True, exist_ok=True) self._cache.mkdir(parents=True, exist_ok=True)
self._version_override: tuple[int, int, int] | None = None self._version_override: tuple[int, int, int] | None = None
self._channel_override: GameChannel | None = None self._channel_override: GameChannel | None = None
@ -113,42 +111,6 @@ 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.
@ -185,24 +147,11 @@ 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 cfg_file.exists(): if not cfg_file.exists():
cfg = ConfigFile(cfg_file) raise FileNotFoundError("config.ini not found.")
cfg.set("General", "game_version", self.get_version_str()) cfg = ConfigFile(cfg_file)
cfg.save() cfg.set("General", "game_version", self.get_version_str())
else: cfg.save()
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]:
""" """
@ -283,27 +232,41 @@ 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_installed_voicepacks(self) -> list[VoicePackLanguage]: def get_channel(self) -> GameChannel:
""" """
Gets the installed voicepacks. 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: Returns:
list[VoicePackLanguage]: A list of installed voicepacks. GameChannel: The current game channel.
""" """
if not self.is_installed(): version = self._version_override or self.get_version()
raise GameNotInstalledError("Game is not installed.") if version == (1, 0, 5):
voicepacks = [] for channel, v in MD5SUMS["1.0.5"].values():
for child in ( for file, md5sum in v.values():
self.data_folder() if (
.joinpath("Persistent/Audio/AudioPackage/Windows/") md5(self._path.joinpath(file).read_bytes()).hexdigest()
.iterdir() != md5sum
): ):
if child.is_dir(): continue
try: match channel:
voicepacks.append(VoicePackLanguage(child.name)) case "cn":
except ValueError: return GameChannel.China
pass case "os":
return voicepacks 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_remote_game(self, pre_download: bool = False) -> resource.Game: def get_remote_game(self, pre_download: bool = False) -> resource.Game:
""" """
@ -430,20 +393,7 @@ 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()