worthless-launcher/worthless/installer.py

141 lines
6.0 KiB
Python
Raw Normal View History

import re
import appdirs
import zipfile
from pathlib import Path
from configparser import ConfigParser
from worthless import constants
from worthless.launcher import Launcher
def read_version_from_game_file(globalgamemanagers: Path | bytes):
if isinstance(globalgamemanagers, Path):
with globalgamemanagers.open("rb") as f:
data = f.read().decode("ascii", errors="ignore")
else:
data = globalgamemanagers.decode("ascii", errors="ignore")
result = re.search(r"([1-9]+\.[0-9]+\.[0-9]+)_[\d]+_[\d]+", data)
if not result:
raise ValueError("Could not find version in game file")
return result.group(1)
class Installer:
def _read_version_from_config(self):
if not self._config_file.exists():
raise FileNotFoundError(f"Config file {self._config_file} not found")
cfg = ConfigParser()
cfg.read(str(self._config_file))
return cfg.get("General", "game_version")
def get_game_data_name(self):
if self._overseas:
return "GenshinImpact_Data/"
else:
return "YuanShen_Data/"
def get_game_data_path(self):
return self._gamedir.joinpath(self.get_game_data_name())
# https://gitlab.com/KRypt0n_/an-anime-game-launcher/-/blob/main/src/ts/Game.ts#L26
def get_game_version(self):
if self._config_file.exists():
return self._read_version_from_config()
else:
globalgamemanagers = self.get_game_data_path().joinpath("./globalgamemanagers")
if not globalgamemanagers.exists():
return
return read_version_from_game_file(globalgamemanagers)
def __init__(self, gamedir: str | Path = Path.cwd(), overseas: bool = True, data_dir: str | Path = None):
if isinstance(gamedir, str):
gamedir = Path(gamedir)
self._gamedir = gamedir
if not data_dir:
self._appdirs = appdirs.AppDirs(constants.APP_NAME, constants.APP_AUTHOR)
self._temp_path = Path(self._appdirs.user_cache_dir).joinpath("Installer")
else:
if not isinstance(data_dir, Path):
data_dir = Path(data_dir)
self._temp_path = data_dir.joinpath("Temp/Installer/")
config_file = self._gamedir.joinpath("config.ini")
self._config_file = config_file.resolve()
self._version = None
self._overseas = overseas
self._launcher = Launcher(self._gamedir, overseas=self._overseas)
self._version = self.get_game_version()
def get_archive_version(self, game_archive: str | Path):
if not game_archive.exists():
raise FileNotFoundError(f"Game archive {game_archive} not found")
archive = zipfile.ZipFile(game_archive, 'r')
return read_version_from_game_file(archive.read(self.get_game_data_name() + "globalgamemanagers"))
def apply_voiceover(self, voiceover_archive: str | Path):
if not self.get_game_data_path().exists():
raise FileNotFoundError(f"Game not found in {self._gamedir}")
if isinstance(voiceover_archive, str):
game_archive = Path(voiceover_archive).resolve()
if not voiceover_archive.exists():
raise FileNotFoundError(f"Voiceover archive {voiceover_archive} not found")
archive = zipfile.ZipFile(voiceover_archive, 'r')
archive.extractall(self._gamedir)
archive.close()
def update_game(self, game_archive: str | Path):
if not self.get_game_data_path().exists():
raise FileNotFoundError(f"Game not found in {self._gamedir}")
if isinstance(game_archive, str):
game_archive = Path(game_archive).resolve()
if not game_archive.exists():
raise FileNotFoundError(f"Update archive {game_archive} not found")
archive = zipfile.ZipFile(game_archive, 'r')
deletefiles = archive.read("deletefiles.txt").decode().split("\n")
for file in deletefiles:
current_game_file = self._gamedir.joinpath(file)
if not current_game_file.exists():
continue
if current_game_file.is_file():
current_game_file.unlink(missing_ok=True)
archive.extractall(self._gamedir)
archive.close()
self._gamedir.joinpath("deletefiles.txt").unlink(missing_ok=True)
self._gamedir.joinpath("hdifffiles.txt").unlink(missing_ok=True)
async def install_game(self, force_reinstall: bool = False):
"""Installs the game to the current directory
If `from_version` is not specified, it will be taken from the game version.
If `to_version` is not specified, it will be taken from the game version.
"""
raise NotImplementedError("Not implemented yet")
# if not force:
# if self._temp_path.exists():
# raise FileExistsError(f"Directory {self._temp_path} already exists")
# self._temp_path.mkdir(parents=True, exist_ok=True)
# self._launcher.set_temp_path(self._temp_path)
# await self._launcher.download_game_diff_archive(from_version, to_version)
# await self._launcher.extract_game_diff_archive()
# await self._launcher.install_game_diff_archive()
# self._launcher.set_temp_path(None)
# self._temp_path.rmdir()
async def get_game_diff_archive(self, from_version: str = None):
"""Gets a diff archive from `from_version` to the latest one
If from_version is not specified, it will be taken from the game version.
"""
if not from_version:
if self._version:
from_version = self._version
else:
from_version = self._version = self.get_game_version()
if not from_version:
raise ValueError("No game version found")
game_resource = await self._launcher.get_resource_info()
if not game_resource:
raise ValueError("Could not fetch game resource")
for v in game_resource.game.diffs:
if v.version == from_version:
return v