Compare commits

...

20 Commits

Author SHA1 Message Date
e3c256200a fix: use f.tell to get file size
yeah, it fixes continuous downloading
2024-06-07 23:59:47 +07:00
19ce16f095 chore: improve the download code 2024-06-07 18:59:55 +07:00
2477ed1a06 fix: don't merge zip files & use 7zip again
We return to cd2aabea60
2024-06-07 18:46:49 +07:00
f0c51530c4 fix: don't use AsyncPath.cwd()
async await
2024-06-05 09:49:00 +07:00
df00e6b679 chore: update url 2024-04-25 21:46:10 +07:00
07f6ce6317 fix: hdiffpatch download 2024-04-25 21:46:03 +07:00
fba2063bce chore: bump to 2.2.21 2023-11-08 16:22:48 +07:00
78df1d242a fix(installer): wrap rename in try/except
So it won't fail I guess, because we failed anyway.
2023-11-08 16:22:30 +07:00
f1eb1fe2c6 fix: voicepack detection 2023-06-20 22:35:33 +07:00
f549166e2e fix: workaround around merged parts 2023-06-20 22:11:10 +07:00
efc7ff2be9 fix: implement segments download
michos don't fucking do breaking changes in the API fuck you.
2023-06-20 18:14:44 +07:00
5e46b23752 chore: delete files first to save space 2023-05-25 13:03:05 +07:00
973ae2a5e6 fix: actually delete files in deletefiles.txt 2023-05-25 13:01:04 +07:00
aa4fe4d5ed chore: bump to 2.2.19 2023-05-25 10:42:14 +07:00
836c843b2e fix: str -> Path 2023-05-25 10:41:51 +07:00
0af4c4f2df chore: bump 2023-05-25 10:39:59 +07:00
a6600cf573 fix: wrong blueReporter path 2023-05-25 10:39:40 +07:00
305021d8b7 chore: bump to 2.2.17 2023-05-25 10:38:25 +07:00
2d5c75109a fix: add blueReporter to revert list 2023-05-25 10:29:24 +07:00
45006ef4b5 fix: telemetry.dll rename 2023-05-25 10:28:17 +07:00
7 changed files with 135 additions and 82 deletions

View File

@ -10,9 +10,9 @@ README = (HERE / "README.md").read_text()
setup( setup(
name='worthless', name='worthless',
version='2.2.16', version='2.2.22',
packages=['worthless', 'worthless.classes', 'worthless.classes.launcher', 'worthless.classes.installer'], packages=['worthless', 'worthless.classes', 'worthless.classes.launcher', 'worthless.classes.installer'],
url='https://git.froggi.es/tretrauit/worthless-launcher', url='https://git.tretrauit.me/tretrauit/worthless-launcher',
license='MIT License', license='MIT License',
author='tretrauit', author='tretrauit',
author_email='tretrauit@gmail.org', author_email='tretrauit@gmail.org',

View File

@ -4,4 +4,4 @@ Launcher = launcher.Launcher
Installer = installer.Installer Installer = installer.Installer
__version__ = "2.2.16" __version__ = "2.2.22"

0
worthless/__main__.py Executable file → Normal file
View File

View File

@ -15,7 +15,10 @@ class Latest:
self.raw = raw self.raw = raw
def get_name(self): def get_name(self):
return self.path.split("/")[-1] name = self.path.split("/")[-1]
if name == "":
name = self.segments[0]["path"].split("/")[-1][:-4]
return name
@staticmethod @staticmethod
def from_dict(data): def from_dict(data):

3
worthless/cli.py Executable file → Normal file
View File

@ -169,7 +169,8 @@ class UI:
return return
await self.download_game() await self.download_game()
print("Game archive:", game.latest.get_name()) print("Game archive:", game.latest.get_name())
await self._install_from_archive(self._installer.temp_path.joinpath(game.latest.get_name()), forced) # I'm too lazy to properly fix things so here we are :D
await self._install_from_archive(self._installer.temp_path.joinpath(game.latest.segments[0]["path"].split("/")[-1]), forced)
async def install_voiceover(self, languages: str): async def install_voiceover(self, languages: str):
res_info = await self._launcher.get_resource_info() res_info = await self._launcher.get_resource_info()

View File

@ -9,7 +9,6 @@ from configparser import ConfigParser
from pathlib import Path from pathlib import Path
import aiohttp import aiohttp
from aiopath import AsyncPath
from worthless import constants from worthless import constants
from worthless.launcher import Launcher from worthless.launcher import Launcher
@ -25,29 +24,26 @@ async def _download_file(file_url: str, file_name: str, file_path: Path | str, f
:return: :return:
""" """
headers = {} headers = {}
file_path = AsyncPath(file_path).joinpath(file_name) file_path = Path(file_path).joinpath(file_name)
if overwrite: if overwrite:
await file_path.unlink(missing_ok=True) file_path.unlink(missing_ok=True)
if await file_path.exists(): if file_path.exists():
cur_len = (await file_path.stat()).st_size with file_path.open("rb") as f:
headers |= { cur_len = f.tell()
headers |= {
"Range": f"bytes={cur_len}-{file_len if file_len else ''}" "Range": f"bytes={cur_len}-{file_len if file_len else ''}"
} }
else: else:
await file_path.touch() file_path.touch()
print(f"Downloading {file_url} to {file_path}...") print(f"Downloading {file_url} to {file_path}...")
async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=60*60, sock_read=240)) as session: async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=60*60, sock_read=240)) as session:
rsp = await session.get(file_url, headers=headers, timeout=None) rsp = await session.get(file_url, headers=headers, timeout=None)
if rsp.status == 416: if rsp.status == 416:
return return
rsp.raise_for_status() rsp.raise_for_status()
while True: with file_path.open("ab") as f:
chunk = await rsp.content.read(chunks) async for data in rsp.content.iter_chunked(chunks):
await asyncio.sleep(0) f.write(data)
if not chunk:
break
async with file_path.open("ab") as f:
await f.write(chunk)
def calculate_md5(file_to_calculate): def calculate_md5(file_to_calculate):
@ -79,19 +75,28 @@ class HDiffPatch:
@staticmethod @staticmethod
def _get_platform_arch(): def _get_platform_arch():
processor = platform.machine()
match platform.system(): match platform.system():
case "Windows": case "Windows":
match platform.architecture()[0]: match processor:
case "32bit": case "i386":
return "windows32" return "windows32"
case "64bit": case "x86_64":
return "windows64" return "windows64"
case "arm":
return "windows_arm32"
case "arm64":
return "windows_arm64"
case "Linux": case "Linux":
match platform.architecture()[0]: match processor:
case "32bit": case "i386":
return "linux32" return "linux32"
case "64bit": case "x86_64":
return "linux64" return "linux64"
case "arm":
return "linux_arm32"
case "arm64":
return "linux_arm64"
case "Darwin": case "Darwin":
return "macos" return "macos"
@ -141,10 +146,13 @@ class HDiffPatch:
rsp = await session.get("https://api.github.com/repos/{}/{}/releases/latest".format(owner, repo), rsp = await session.get("https://api.github.com/repos/{}/{}/releases/latest".format(owner, repo),
params={"Headers": "Accept: application/vnd.github.v3+json"}) params={"Headers": "Accept: application/vnd.github.v3+json"})
rsp.raise_for_status() rsp.raise_for_status()
archive_processor = self._get_platform_arch()
for asset in (await rsp.json())["assets"]: for asset in (await rsp.json())["assets"]:
if asset["name"].endswith(".zip") and "linux" not in asset["name"] and "windows" not in asset["name"] \ if not asset["name"].endswith(".zip"):
and "macos" not in asset["name"] and "android" not in asset["name"]: continue
return asset if archive_processor not in asset["name"]:
continue
return asset
async def get_latest_release_url(self): async def get_latest_release_url(self):
asset = await self._get_latest_release_info() asset = await self._get_latest_release_info()
@ -167,17 +175,17 @@ class HDiffPatch:
class Installer: class Installer:
def __init__(self, gamedir: str | Path | AsyncPath = AsyncPath.cwd(), def __init__(self, gamedir: str | Path = Path.cwd(),
overseas: bool = True, data_dir: str | Path | AsyncPath = None): overseas: bool = True, data_dir: str | Path = None):
if isinstance(gamedir, str | Path): if isinstance(gamedir, str | Path):
gamedir = AsyncPath(gamedir) gamedir = Path(gamedir)
self._gamedir = gamedir self._gamedir = gamedir
if not data_dir: if not data_dir:
self._appdirs = constants.APPDIRS self._appdirs = constants.APPDIRS
self.temp_path = AsyncPath(self._appdirs.user_cache_dir).joinpath("Installer") self.temp_path = Path(self._appdirs.user_cache_dir).joinpath("Installer")
else: else:
if isinstance(data_dir, str | AsyncPath): if isinstance(data_dir, str | Path):
data_dir = AsyncPath(data_dir) data_dir = Path(data_dir)
self.temp_path = data_dir.joinpath("Temp/Installer/") self.temp_path = data_dir.joinpath("Temp/Installer/")
Path(self.temp_path).mkdir(parents=True, exist_ok=True) Path(self.temp_path).mkdir(parents=True, exist_ok=True)
config_file = self._gamedir.joinpath("config.ini") config_file = self._gamedir.joinpath("config.ini")
@ -201,13 +209,13 @@ class Installer:
chunks=self._download_chunk) chunks=self._download_chunk)
async def read_version_from_config(self): async def read_version_from_config(self):
if not await self._config_file.exists(): if not self._config_file.exists():
raise FileNotFoundError(f"Config file {self._config_file} not found") raise FileNotFoundError(f"Config file {self._config_file} not found")
cfg = ConfigParser() cfg = ConfigParser()
await asyncio.to_thread(cfg.read, str(self._config_file)) await asyncio.to_thread(cfg.read, str(self._config_file))
return cfg.get("General", "game_version") return cfg.get("General", "game_version")
async def read_version_from_game_file(self, globalgamemanagers: AsyncPath | Path | bytes) -> str: async def read_version_from_game_file(self, globalgamemanagers: Path | Path | bytes) -> str:
""" """
Reads the version from the globalgamemanagers file. (Data/globalgamemanagers) Reads the version from the globalgamemanagers file. (Data/globalgamemanagers)
@ -216,9 +224,9 @@ class Installer:
:return: Game version (ex 1.0.0) :return: Game version (ex 1.0.0)
""" """
if isinstance(globalgamemanagers, Path | AsyncPath): if isinstance(globalgamemanagers, Path | Path):
globalgamemanagers = AsyncPath(globalgamemanagers) globalgamemanagers = Path(globalgamemanagers)
data = await globalgamemanagers.read_text("ascii", errors="ignore") data = globalgamemanagers.read_text("ascii", errors="ignore")
else: else:
data = globalgamemanagers.decode("ascii", errors="ignore") data = globalgamemanagers.decode("ascii", errors="ignore")
result = self._game_version_re.search(data) result = self._game_version_re.search(data)
@ -258,7 +266,7 @@ class Installer:
return lang return lang
@staticmethod @staticmethod
async def get_voiceover_archive_language(voiceover_archive: str | Path | AsyncPath) -> str: async def get_voiceover_archive_language(voiceover_archive: str | Path | Path) -> str:
if isinstance(voiceover_archive, str | Path): if isinstance(voiceover_archive, str | Path):
voiceover_archive = Path(voiceover_archive).resolve() voiceover_archive = Path(voiceover_archive).resolve()
if not voiceover_archive.exists(): if not voiceover_archive.exists():
@ -293,7 +301,7 @@ class Installer:
else: else:
return "YuanShen_Data/" return "YuanShen_Data/"
def get_game_data_path(self) -> AsyncPath: def get_game_data_path(self) -> Path:
return self._gamedir.joinpath(self.get_game_data_name()) return self._gamedir.joinpath(self.get_game_data_name())
async def get_game_archive_version(self, game_archive: str | Path): async def get_game_archive_version(self, game_archive: str | Path):
@ -307,7 +315,7 @@ class Installer:
async def get_game_version(self) -> str | None: async def get_game_version(self) -> str | None:
globalgamemanagers = self.get_game_data_path().joinpath("./globalgamemanagers") globalgamemanagers = self.get_game_data_path().joinpath("./globalgamemanagers")
if not await globalgamemanagers.exists(): if not globalgamemanagers.exists():
try: try:
return await self.read_version_from_config() return await self.read_version_from_config()
except FileNotFoundError: except FileNotFoundError:
@ -321,13 +329,13 @@ class Installer:
:return: List of installed voiceovers :return: List of installed voiceovers
""" """
voiceovers = [] voiceovers = []
async for file in self.get_game_data_path()\ for file in self.get_game_data_path()\
.joinpath("StreamingAssets/AudioAssets/").iterdir(): .joinpath("StreamingAssets/AudioAssets/").iterdir():
if await file.is_dir(): if file.is_dir():
voiceovers.append(file.name) voiceovers.append(file.name)
return voiceovers return voiceovers
async def _update(self, game_archive: str | Path | AsyncPath): async def _update(self, game_archive: str | Path | Path):
archive = zipfile.ZipFile(game_archive, 'r') archive = zipfile.ZipFile(game_archive, 'r')
if not self._hdiffpatch.get_hpatchz_executable(): if not self._hdiffpatch.get_hpatchz_executable():
@ -343,6 +351,22 @@ class Installer:
except ValueError: except ValueError:
pass pass
try:
deletefiles = archive.read("deletefiles.txt").decode().split("\r\n")
for file in deletefiles:
current_game_file = Path(self._gamedir).joinpath(file)
if current_game_file == Path(self._gamedir):
# Don't delete the game folder
print("Game folder detected, not deleting:", current_game_file)
continue
if not current_game_file.relative_to(Path(self._gamedir)):
print("Not deleting (not relative to game):", current_game_file)
continue
print("Deleting", file)
current_game_file.unlink(missing_ok=True)
except Exception as e:
print(f"Error while reading deletefiles.txt: {e}")
# hdiffpatch implementation # hdiffpatch implementation
try: try:
hdifffiles = [] hdifffiles = []
@ -354,7 +378,7 @@ class Installer:
count = 0 count = 0
for file in hdifffiles: for file in hdifffiles:
current_game_file = self._gamedir.joinpath(file) current_game_file = self._gamedir.joinpath(file)
if not await current_game_file.exists(): if not current_game_file.exists():
print("File", file, "not found") print("File", file, "not found")
# Not patching since we don't have the file # Not patching since we don't have the file
continue continue
@ -371,18 +395,21 @@ class Installer:
print("Failed to extract diff file", diff_file) print("Failed to extract diff file", diff_file)
return return
old_suffix = old_file.suffix old_suffix = old_file.suffix
old_file = await old_file.rename(old_file.with_suffix(".bak")) old_file = old_file.rename(old_file.with_suffix(".bak"))
proc = await self._hdiffpatch.patch_file(old_file, old_file.with_suffix(old_suffix), proc = await self._hdiffpatch.patch_file(old_file, old_file.with_suffix(old_suffix),
patch_path, wait=True) patch_path, wait=True)
patch_path.unlink() patch_path.unlink()
if proc.returncode == 0: if proc.returncode == 0:
await old_file.unlink() old_file.unlink()
return return
# Let the game download the file. # Let the game download the file.
print("Failed to patch {}, reverting and let the in-game updater do the job...".format( print("Failed to patch {}, reverting and let the in-game updater do the job...".format(
old_file.with_suffix(old_suffix)) old_file.with_suffix(old_suffix))
) )
await old_file.rename(old_file.with_suffix(old_suffix)) try:
old_file.rename(old_file.with_suffix(old_suffix))
except Exception:
pass
files.remove(patch_file) files.remove(patch_file)
# Limit to 8 process running so it doesn't hang the PC. # Limit to 8 process running so it doesn't hang the PC.
@ -403,24 +430,11 @@ class Installer:
except Exception as e: except Exception as e:
print(f"Error while reading hdifffiles.txt: {e}") print(f"Error while reading hdifffiles.txt: {e}")
try:
deletefiles = archive.read("deletefiles.txt").decode().split("\n")
for file in deletefiles:
current_game_file = Path(self._gamedir.joinpath(file))
if current_game_file == Path(self._gamedir):
continue
if not current_game_file.is_file():
continue
print("Deleting ", file)
current_game_file.unlink(missing_ok=True)
except Exception as e:
print(f"Error while reading deletefiles.txt: {e}")
await asyncio.to_thread(archive.extractall, self._gamedir, members=files) await asyncio.to_thread(archive.extractall, self._gamedir, members=files)
archive.close() archive.close()
async def update_game(self, game_archive: str | Path | AsyncPath): async def update_game(self, game_archive: str | Path | Path):
if not await self.get_game_data_path().exists(): if not self.get_game_data_path().exists():
raise FileNotFoundError(f"Game not found in {self._gamedir}") raise FileNotFoundError(f"Game not found in {self._gamedir}")
if isinstance(game_archive, str | Path): if isinstance(game_archive, str | Path):
game_archive = Path(game_archive).resolve() game_archive = Path(game_archive).resolve()
@ -440,10 +454,28 @@ class Installer:
async def download_full_game(self, pre_download=False): async def download_full_game(self, pre_download=False):
game = await self._get_game(pre_download) game = await self._get_game(pre_download)
archive_name = game.latest.path.split("/")[-1] # Segment download
await self._download_file(game.latest.path, archive_name, game.latest.size) # base_archive = None
if calculate_md5(self.temp_path.joinpath(archive_name)) != game.latest.md5: for _, segment in enumerate(game.latest.segments):
raise RuntimeError("mismatch md5 for downloaded game archive") archive_name = segment["path"].split("/")[-1]
if self.temp_path.joinpath(archive_name + ".downloaded").exists() and self.temp_path.joinpath(archive_name).exists():
print(f"calculating md5 for downloaded game archive: {archive_name}")
md5 = calculate_md5(self.temp_path.joinpath(archive_name))
if md5 != segment["md5"]:
print(f"mismatch md5 for downloaded game archive: {archive_name} ({md5}; expected {segment['md5']}), continuing anyway...")
continue
await self._download_file(segment["path"], archive_name)
print(f"calculating md5 for downloaded game archive: {archive_name}")
md5 = calculate_md5(self.temp_path.joinpath(archive_name))
if md5 != segment["md5"]:
print(f"mismatch md5 for downloaded game archive: {archive_name} ({md5}; expected {segment['md5']}), continuing anyway...")
# if i != 0:
# with open(self.temp_path.joinpath(base_archive), 'ab') as f:
# with open(self.temp_path.joinpath(archive_name), 'rb') as f2:
# shutil.copyfileobj(f2, f)
# self.temp_path.joinpath(archive_name).unlink()
self.temp_path.joinpath(archive_name + ".downloaded").touch()
async def download_full_voiceover(self, language: str, pre_download=False): async def download_full_voiceover(self, language: str, pre_download=False):
game = await self._get_game(pre_download) game = await self._get_game(pre_download)
@ -455,36 +487,46 @@ class Installer:
async def uninstall_game(self): async def uninstall_game(self):
await asyncio.to_thread(shutil.rmtree, self._gamedir, ignore_errors=True) await asyncio.to_thread(shutil.rmtree, self._gamedir, ignore_errors=True)
async def _extract_game_file(self, archive: str | Path | AsyncPath): async def _extract_game_file(self, archive: str | Path):
if isinstance(archive, str | AsyncPath): if isinstance(archive, str | Path):
archive = Path(archive).resolve() archive = Path(archive).resolve()
if not archive.exists(): if not archive.exists():
raise FileNotFoundError(f"'{archive}' not found") raise FileNotFoundError(f"'{archive}' not found")
with zipfile.ZipFile(archive, 'r') as f: game_dir_str = str(self._gamedir)
await asyncio.to_thread(f.extractall, path=self._gamedir) if not game_dir_str.endswith("/"):
game_dir_str += "/"
print(f'-o"{game_dir_str}"')
proc = await asyncio.create_subprocess_shell(f'7z x {str(archive)} -o"{game_dir_str}"')
await proc.wait()
if proc.returncode != 0:
raise RuntimeError("Extracting failed")
# with zipfile.ZipFile(archive, 'r') as f:
# await asyncio.to_thread(f.extractall, path=self._gamedir)
async def apply_voiceover(self, voiceover_archive: str | Path): async def apply_voiceover(self, voiceover_archive: str | Path):
# Since Voiceover packages are unclear about diff package or full package # Since Voiceover packages are unclear about diff package or full package
# we will try to extract the voiceover package and apply it to the game # we will try to extract the voiceover package and apply it to the game
# making this function universal for both cases # making this function universal for both cases
if not await self.get_game_data_path().exists(): if not self.get_game_data_path().exists():
raise FileNotFoundError(f"Game not found in {self._gamedir}") raise FileNotFoundError(f"Game not found in {self._gamedir}")
if isinstance(voiceover_archive, str | Path): if isinstance(voiceover_archive, str | Path):
voiceover_archive = Path(voiceover_archive).resolve() voiceover_archive = Path(voiceover_archive).resolve()
await self._update(voiceover_archive) await self._update(voiceover_archive)
# await self._extract_game_file(voiceover_archive) # await self._extract_game_file(voiceover_archive)
async def install_game(self, game_archive: str | Path | AsyncPath, force_reinstall: bool = False): async def install_game(self, game_archive: str | Path | Path, force_reinstall: bool = False):
"""Installs the game to the current directory """Installs the game to the current directory
If `force_reinstall` is True, the game will be uninstalled then reinstalled. If `force_reinstall` is True, the game will be uninstalled then reinstalled.
""" """
if await self.get_game_data_path().exists(): if self.get_game_data_path().exists() \
and all([self._gamedir.joinpath(x).exists() for x in ["UnityPlayer.dll", "pkg_version"]]) \
and any([self._gamedir.joinpath(x).exists() for x in ["GenshinImpact.exe", "YuanShen.exe"]]):
if not force_reinstall: if not force_reinstall:
raise ValueError(f"Game is already installed in {self._gamedir}") raise ValueError(f"Game is already installed in {self._gamedir}")
await self.uninstall_game() await self.uninstall_game()
await self._gamedir.mkdir(parents=True, exist_ok=True) self._gamedir.mkdir(parents=True, exist_ok=True)
await self._extract_game_file(game_archive) await self._extract_game_file(game_archive)
self._version = await self.get_game_version() self._version = await self.get_game_version()
self.set_version_config() self.set_version_config()
@ -556,8 +598,8 @@ class Installer:
if v.version == from_version: if v.version == from_version:
return v return v
async def verify_from_pkg_version(self, pkg_version: AsyncPath, ignore_mismatch=False): async def verify_from_pkg_version(self, pkg_version: Path, ignore_mismatch=False):
contents = await pkg_version.read_text() contents = pkg_version.read_text()
async def verify_file(file_to_verify, md5): async def verify_file(file_to_verify, md5):
print("Verifying file:", file_to_verify) print("Verifying file:", file_to_verify)
@ -593,7 +635,7 @@ class Installer:
return None if not failed_files else failed_files return None if not failed_files else failed_files
async def verify_game(self, pkg_version: str | Path | AsyncPath = None, ignore_mismatch=False): async def verify_game(self, pkg_version: str | Path | Path = None, ignore_mismatch=False):
if pkg_version is None: if pkg_version is None:
pkg_version = self._gamedir.joinpath("pkg_version") pkg_version = self._gamedir.joinpath("pkg_version")
return await self.verify_from_pkg_version(pkg_version, ignore_mismatch) return await self.verify_from_pkg_version(pkg_version, ignore_mismatch)

View File

@ -28,7 +28,7 @@ except ImportError:
class Patcher: class Patcher:
def __init__(self, gamedir: Path | AsyncPath | str = AsyncPath.cwd(), data_dir: str | Path | AsyncPath = None, def __init__(self, gamedir: Path | AsyncPath | str = Path.cwd(), data_dir: str | Path | AsyncPath = None,
patch_url: str = None, overseas=True, patch_provider="Krock"): patch_url: str = None, overseas=True, patch_provider="Krock"):
if isinstance(gamedir, str | Path): if isinstance(gamedir, str | Path):
gamedir = AsyncPath(gamedir) gamedir = AsyncPath(gamedir)
@ -237,13 +237,19 @@ class Patcher:
disable_files = [ disable_files = [
self._installer.get_game_data_name() + "upload_crash.exe", self._installer.get_game_data_name() + "upload_crash.exe",
self._installer.get_game_data_name() + "Plugins/crashreport.exe", self._installer.get_game_data_name() + "Plugins/crashreport.exe",
self._installer.get_game_data_name() + "Plugins/blueReporter.exe", self._installer.get_game_data_name() + "blueReporter.exe",
] ]
for file in disable_files: for file in disable_files:
file_path = Path(self._gamedir.joinpath(file)).resolve() file_path = Path(self._gamedir.joinpath(file)).resolve()
if file_path.exists(): if file_path.exists():
await AsyncPath(file_path).rename(str(file_path) + ".bak") await AsyncPath(file_path).rename(str(file_path) + ".bak")
# Delete old Telemetry.dll on Linux (cAsE sEnsItIvE)
if platform.system() == "Linux":
telemetry_path = Path(self._installer.get_game_data_name()).joinpath("Plugins/Telemetry.dll")
if telemetry_path.exists() and Path(self._installer.get_game_data_name()).joinpath("Plugins/telemetry.dll").exists():
await telemetry_path.unlink()
patch_jobs.append(disable_crashreporters()) patch_jobs.append(disable_crashreporters())
await asyncio.gather(*patch_jobs) await asyncio.gather(*patch_jobs)
@ -291,6 +297,7 @@ class Patcher:
self._installer.get_game_data_name() + "upload_crash.exe", self._installer.get_game_data_name() + "upload_crash.exe",
self._installer.get_game_data_name() + "Plugins/crashreport.exe", self._installer.get_game_data_name() + "Plugins/crashreport.exe",
self._installer.get_game_data_name() + "Plugins/xlua.dll", self._installer.get_game_data_name() + "Plugins/xlua.dll",
self._installer.get_game_data_name() + "blueReporter.exe",
] ]
revert_job = [] revert_job = []
for file in revert_files: for file in revert_files: