tretrauit
aaf728445d
-Sp/--patch is now required to do block telemetry before patching. Still preparing for hdiffpatch (will be coming at 1.10) Ay yo hosty support coming soon xD
338 lines
16 KiB
Python
Executable File
338 lines
16 KiB
Python
Executable File
#!/usr/bin/python3
|
|
|
|
import argparse
|
|
import asyncio
|
|
|
|
import appdirs
|
|
from pathlib import Path
|
|
from worthless.launcher import Launcher
|
|
from worthless.installer import Installer
|
|
from worthless.patcher import Patcher
|
|
import worthless.constants as constants
|
|
|
|
|
|
class UI:
|
|
def __init__(self, gamedir: str, noconfirm: bool, tempdir: str | Path = None) -> None:
|
|
self._noconfirm = noconfirm
|
|
self._gamedir = gamedir
|
|
self._launcher = Launcher(gamedir)
|
|
self._installer = Installer(gamedir, data_dir=tempdir)
|
|
self._patcher = Patcher(gamedir, data_dir=tempdir)
|
|
|
|
def _ask(self, question):
|
|
if self._noconfirm:
|
|
# Fake dialog
|
|
print(question + " (y/n): y")
|
|
return True
|
|
answer = ""
|
|
while answer.lower() not in ['y', 'n']:
|
|
if answer != "":
|
|
print("Invalid choice, please try again.")
|
|
answer = input(question + " (y/n): ")
|
|
return answer.lower() == 'y'
|
|
|
|
def override_game_version(self, version: str):
|
|
self._installer._version = version
|
|
|
|
def get_game_version(self):
|
|
print(self._installer.get_game_version())
|
|
|
|
def block_telemetry(self):
|
|
print("Checking for available telemetry to block...")
|
|
try:
|
|
asyncio.run(self._patcher.block_telemetry())
|
|
except ValueError:
|
|
print("No telemetry to block.")
|
|
else:
|
|
print("Telemetry blocked.")
|
|
|
|
def check_telemetry(self):
|
|
block_status = asyncio.run(self._patcher.is_telemetry_blocked())
|
|
if not block_status:
|
|
print("Telemetry is blocked.")
|
|
else:
|
|
print("Telemetry is not blocked, you need to block these hosts below.")
|
|
for block in block_status:
|
|
print(block)
|
|
|
|
def _update_from_archive(self, filepath):
|
|
print("Reverting patches if patched...")
|
|
self._patcher.revert_patch(True)
|
|
print("Updating game from archive (this may takes some time)...")
|
|
self._installer.update_game(filepath)
|
|
|
|
def _install_from_archive(self, filepath, force_reinstall):
|
|
print("Installing game from archive (this may takes some time)...")
|
|
self._installer.install_game(filepath, force_reinstall)
|
|
|
|
def _apply_voiceover_from_archive(self, filepath):
|
|
print("Applying voiceover from archive (this may takes some time)...")
|
|
self._installer.apply_voiceover(filepath)
|
|
|
|
def install_voiceover_from_file(self, filepath):
|
|
print("Archive voiceover language: {} ({})".format(
|
|
self._installer.get_voiceover_archive_language(filepath),
|
|
"Full archive" if self._installer.get_voiceover_archive_type(filepath) else "Update archive"))
|
|
if not self._ask("Do you want to apply this voiceover pack? ({})".format(filepath)):
|
|
print("Aborting apply process.")
|
|
return
|
|
self._apply_voiceover_from_archive(filepath)
|
|
print("Voiceover applied successfully.")
|
|
|
|
def revert_patch(self):
|
|
print("Reverting patches...")
|
|
self._patcher.revert_patch(True)
|
|
print("Patches reverted.")
|
|
|
|
def patch_game(self, login_fix: bool = False):
|
|
print("NOTE: Hereby you are violating the game's Terms of Service!")
|
|
print("Do not patch the game if you don't know what you are doing!")
|
|
if not self._ask("Do you want to patch the game? (This will overwrite your game files!)"):
|
|
print("Aborting patch process.")
|
|
return
|
|
self.block_telemetry()
|
|
print("Updating patches...")
|
|
asyncio.run(self._patcher.download_patch())
|
|
print("Patching game...")
|
|
self._patcher.apply_patch(login_fix)
|
|
print("Game patched.")
|
|
print("Please refrain from sharing this project to public, thank you.")
|
|
|
|
def install_from_file(self, filepath):
|
|
gamever = self._installer.get_game_version()
|
|
print("Archive game version: " + self._installer.get_game_archive_version(filepath))
|
|
if gamever:
|
|
print("Current game installation detected. ({})".format(self._installer.get_game_version()))
|
|
if not self._ask("Do you want to update the game? ({})".format(filepath)):
|
|
print("Aborting update process.")
|
|
return
|
|
self._update_from_archive(filepath)
|
|
print("Game updated successfully.")
|
|
else:
|
|
print("No game installation detected.")
|
|
if not self._ask("Do you want to install the game? ({})".format(filepath)):
|
|
print("Aborting installation process.")
|
|
return
|
|
self._install_from_archive(filepath, False)
|
|
print("Game installed successfully.")
|
|
|
|
def download_patch(self):
|
|
print("Downloading patches...")
|
|
asyncio.run(self._patcher.download_patch())
|
|
|
|
def download_game(self):
|
|
print("Downloading full game (This will take a long time)...")
|
|
asyncio.run(self._installer.download_full_game())
|
|
|
|
def download_game_update(self):
|
|
print("Downloading game update (This will take a long time)...")
|
|
asyncio.run(self._installer.download_game_update())
|
|
|
|
def download_voiceover(self, languages: str):
|
|
res_info = asyncio.run(self._launcher.get_resource_info())
|
|
for lng in languages.split(" "):
|
|
for vo in res_info.game.latest.voice_packs:
|
|
if not self._installer.voiceover_lang_translate(lng) == vo.language:
|
|
continue
|
|
print("Downloading voiceover pack for {} (This will take a long time)...".format(lng))
|
|
asyncio.run(self._installer.download_full_voiceover(lng))
|
|
|
|
def download_voiceover_update(self, languages: str):
|
|
res_info = asyncio.run(self._launcher.get_resource_info())
|
|
for lng in languages.split(" "):
|
|
for vo in res_info.game.latest.voice_packs:
|
|
if not self._installer.voiceover_lang_translate(lng) == vo.language:
|
|
continue
|
|
print("Downloading voiceover update pack for {} (This will take a long time)...".format(lng))
|
|
asyncio.run(self._installer.download_voiceover_update(lng))
|
|
|
|
def install_game(self, forced: bool = False):
|
|
res_info = asyncio.run(self._launcher.get_resource_info())
|
|
print("Latest game version: {}".format(res_info.game.latest.version))
|
|
if not self._ask("Do you want to install the game?"):
|
|
print("Aborting game installation process.")
|
|
return
|
|
self.download_game()
|
|
print("Installing game...")
|
|
self._install_from_archive(self._installer.temp_path.joinpath(res_info.game.latest.name), forced)
|
|
|
|
def install_voiceover(self, languages: str):
|
|
res_info = asyncio.run(self._launcher.get_resource_info())
|
|
print("Latest game version: {}".format(res_info.game.latest.version))
|
|
for lng in languages.split(" "):
|
|
for vo in res_info.game.latest.voice_packs:
|
|
if not self._installer.voiceover_lang_translate(lng) == vo.language:
|
|
continue
|
|
if not self._ask("Do you want to install this voiceover pack? ({})".format(lng)):
|
|
print("Aborting voiceover installation process.")
|
|
return
|
|
print("Downloading voiceover pack (This will take a long time)...")
|
|
asyncio.run(self._installer.download_full_voiceover(lng))
|
|
print("Installing voiceover pack...")
|
|
self._apply_voiceover_from_archive(self._installer.temp_path.joinpath(res_info.game.latest.name))
|
|
break
|
|
|
|
def update_game(self):
|
|
game_ver = self._installer.get_game_version()
|
|
if not game_ver:
|
|
self.install_game()
|
|
return
|
|
print("Current game installation detected. ({})".format(game_ver))
|
|
diff_archive = asyncio.run(self._installer.get_game_diff_archive())
|
|
res_info = asyncio.run(self._launcher.get_resource_info())
|
|
if not diff_archive:
|
|
print("No game updates available.")
|
|
return
|
|
print("Latest game version: {}".format(res_info.game.latest.version))
|
|
if not self._ask("Do you want to update the game?"):
|
|
print("Aborting game update process.")
|
|
return
|
|
print("Downloading game update (This will take a long time)...")
|
|
asyncio.run(self._installer.download_game_update())
|
|
print("Installing game update...")
|
|
self.install_from_file(self._installer.temp_path.joinpath(res_info.game.latest.name))
|
|
|
|
def update_voiceover(self, languages: str):
|
|
game_ver = self._installer.get_game_version()
|
|
if not game_ver:
|
|
self.install_voiceover(languages)
|
|
return
|
|
print("Current game installation detected. ({})".format(game_ver))
|
|
for lng in languages.split(" "):
|
|
diff_archive = asyncio.run(self._installer.get_voiceover_diff_archive(lng))
|
|
# res_info = asyncio.run(self._launcher.get_resource_info())
|
|
if not diff_archive:
|
|
print("No voiceover updates available for {}.".format(lng))
|
|
continue
|
|
if not self._ask("Do you want to update this voiceover? ({})".format(lng)):
|
|
print("Aborting this voiceover language update process.")
|
|
continue
|
|
print("Downloading voiceover update (This may takes some time)...")
|
|
asyncio.run(self._installer.download_voiceover_update(lng))
|
|
print("Installing voiceover update for {}...".format(lng))
|
|
self._apply_voiceover_from_archive(self._installer.temp_path.joinpath(diff_archive.name))
|
|
|
|
def update_game_voiceover(self, languages: str):
|
|
self.update_game()
|
|
self.update_voiceover(languages)
|
|
|
|
def interactive_ui(self):
|
|
raise NotImplementedError()
|
|
|
|
|
|
def main():
|
|
default_dirs = appdirs.AppDirs(constants.APP_NAME, constants.APP_AUTHOR)
|
|
parser = argparse.ArgumentParser(prog="worthless", description="A worthless launcher written in Python.")
|
|
parser.add_argument("-D", "--dir", action="store", type=Path, default=Path.cwd(),
|
|
help="Specify the game directory (default current working directory)")
|
|
parser.add_argument("-W", "--temporary-dir", action="store", type=Path, default=None,
|
|
help="Specify the temporary directory (default {} and {})".format(default_dirs.user_data_dir,
|
|
default_dirs.user_cache_dir))
|
|
parser.add_argument("-S", "--install", action="store_true",
|
|
help="Install/update the game (if not already installed, else do nothing)")
|
|
parser.add_argument("-U", "--install-from-file", action="store", type=Path, default=None,
|
|
help="Install the game from an archive (if not already installed, \
|
|
else update from archive)")
|
|
parser.add_argument("-Uv", "--install-voiceover-from-file", action="store", type=Path, default=None,
|
|
help="Install the voiceover from an archive (if not already installed, \
|
|
else update from archive)")
|
|
parser.add_argument("-Sp", "--patch", action="store_true",
|
|
help="Patch the game (if not already patched, else do nothing)")
|
|
parser.add_argument("--login-fix", action="store_true",
|
|
help="Patch the game to fix login issues (if not already patched, else do nothing)")
|
|
parser.add_argument("-Sy", "--update", action="store", type=str, default="",
|
|
help="Update the game and specified voiceover pack only (or install if not found)")
|
|
parser.add_argument("-Sw", "--download-game", action="store_true",
|
|
help="Download the full game to the temporary directory")
|
|
parser.add_argument("-Swv", "--download-voiceover", action="store", type=str,
|
|
help="Download the full voiceover to the temporary directory")
|
|
parser.add_argument("-Syw", "--download-game-update", action="store", type=str, default="",
|
|
help="Download the game and the voiceover update to the temporary directory")
|
|
parser.add_argument("-Sywv", "--download-voiceover-update", action="store", type=str,
|
|
help="Download the voiceover update to the temporary directory")
|
|
parser.add_argument("-Sv", "--update-voiceover", action="store", type=str,
|
|
help="Update the voiceover pack only (or install if not found)")
|
|
parser.add_argument("-Syu", "--update-all", action="store_true",
|
|
help="Update the game and all installed voiceover packs (or install if not found)")
|
|
parser.add_argument("-Rs", "--remove", action="store_true", help="Remove the game (if installed)")
|
|
parser.add_argument("-Rp", "--remove-patch", action="store_true", help="Revert the game patch (if patched)")
|
|
parser.add_argument("-Rv", "--remove-voiceover", action="store_true", help="Remove a Voiceover pack (if installed)")
|
|
parser.add_argument("--get-game-version", action="store_true", help="Get the current game version")
|
|
parser.add_argument("--no-overseas", action="store_true", help="Don't use overseas server")
|
|
parser.add_argument("--check-telemetry", action="store_true", help="Check for the telemetry information")
|
|
parser.add_argument("--from-ver", action="store", help="Override the detected game version", type=str, default=None)
|
|
parser.add_argument("--noconfirm", action="store_true",
|
|
help="Do not ask any for confirmation. (Ignored in interactive mode)")
|
|
args = parser.parse_args()
|
|
interactive_mode = not args.install and not args.install_from_file and not args.patch and not args.update and not \
|
|
args.remove and not args.remove_patch and not args.remove_voiceover and not args.get_game_version and not \
|
|
args.install_voiceover_from_file and not args.update_voiceover and not args.download_game and not \
|
|
args.download_voiceover and not args.download_game_update and not args.download_voiceover_update and not \
|
|
args.install_voiceover_from_file and not args.update_all and not args.login_fix and not args.check_telemetry\
|
|
and not args.from_ver
|
|
if args.temporary_dir:
|
|
args.temporary_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
ui = UI(args.dir, args.noconfirm, args.temporary_dir)
|
|
|
|
if args.install and args.update:
|
|
raise ValueError("Cannot specify both --install and --update arguments.")
|
|
|
|
if args.install_from_file and args.update:
|
|
raise ValueError("Cannot specify both --install-from-file and --update arguments.")
|
|
|
|
if args.install_voiceover_from_file and args.update:
|
|
raise ValueError("Cannot specify both --install-voiceover-from-file and --update arguments.")
|
|
|
|
if args.install_from_file and args.install:
|
|
raise ValueError("Cannot specify both --install-from-file and --install arguments.")
|
|
|
|
if args.from_ver:
|
|
ui.override_game_version(args.from_ver)
|
|
|
|
if args.get_game_version:
|
|
ui.get_game_version()
|
|
|
|
if args.check_telemetry:
|
|
ui.check_telemetry()
|
|
|
|
if args.download_game:
|
|
ui.download_game()
|
|
|
|
if args.download_voiceover:
|
|
ui.download_voiceover(args.download_voiceover)
|
|
|
|
if args.download_game_update:
|
|
ui.download_game_update()
|
|
|
|
if args.download_voiceover_update:
|
|
ui.download_voiceover_update(args.download_voiceover_update)
|
|
|
|
if args.install:
|
|
ui.install_game()
|
|
|
|
if args.update:
|
|
ui.update_game_voiceover(args.update)
|
|
|
|
if args.update_voiceover:
|
|
ui.update_voiceover(args.update_voiceover)
|
|
|
|
if args.install_from_file:
|
|
ui.install_from_file(args.install_from_file)
|
|
|
|
if args.install_voiceover_from_file:
|
|
ui.install_voiceover_from_file(args.install_voiceover_from_file)
|
|
|
|
if args.patch:
|
|
ui.patch_game(args.login_fix)
|
|
|
|
if args.remove_patch:
|
|
ui.revert_patch()
|
|
|
|
if interactive_mode:
|
|
ui.interactive_ui()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|