2022-01-28 13:08:53 +00:00
|
|
|
#!/usr/bin/python3
|
|
|
|
|
|
|
|
import argparse
|
2022-02-18 04:11:55 +00:00
|
|
|
import asyncio
|
|
|
|
|
2022-02-15 19:55:19 +00:00
|
|
|
import appdirs
|
2022-01-28 13:08:53 +00:00
|
|
|
from pathlib import Path
|
2022-02-16 15:18:56 +00:00
|
|
|
from worthless.launcher import Launcher
|
|
|
|
from worthless.installer import Installer
|
2022-02-16 19:43:21 +00:00
|
|
|
from worthless.patcher import Patcher
|
2022-02-16 15:18:56 +00:00
|
|
|
import worthless.constants as constants
|
2022-01-28 13:08:53 +00:00
|
|
|
|
|
|
|
|
2022-02-15 17:49:33 +00:00
|
|
|
class UI:
|
2022-02-16 19:43:21 +00:00
|
|
|
def __init__(self, gamedir: str, noconfirm: bool, tempdir: str | Path = None) -> None:
|
2022-02-15 17:49:33 +00:00
|
|
|
self._noconfirm = noconfirm
|
|
|
|
self._gamedir = gamedir
|
2022-02-16 15:18:56 +00:00
|
|
|
self._launcher = Launcher(gamedir)
|
2022-02-16 19:43:21 +00:00
|
|
|
self._installer = Installer(gamedir, data_dir=tempdir)
|
2022-02-18 13:09:03 +00:00
|
|
|
self._patcher = Patcher(gamedir, data_dir=tempdir)
|
2022-02-16 19:43:21 +00:00
|
|
|
|
2022-02-18 04:11:55 +00:00
|
|
|
def _ask(self, question):
|
|
|
|
if self._noconfirm:
|
|
|
|
# Fake dialog
|
|
|
|
print(question + " (y/n): y")
|
|
|
|
return True
|
2022-02-16 19:43:21 +00:00
|
|
|
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'
|
2022-01-28 13:08:53 +00:00
|
|
|
|
2022-02-19 11:57:45 +00:00
|
|
|
def override_game_version(self, version: str):
|
|
|
|
self._installer._version = version
|
|
|
|
|
2022-02-16 15:18:56 +00:00
|
|
|
def get_game_version(self):
|
|
|
|
print(self._installer.get_game_version())
|
|
|
|
|
2022-02-16 19:43:21 +00:00
|
|
|
def _update_from_archive(self, filepath):
|
|
|
|
print("Reverting patches if patched...")
|
|
|
|
self._patcher.revert_patch(True)
|
2022-02-17 15:02:08 +00:00
|
|
|
print("Updating game from archive (this may takes some time)...")
|
2022-02-16 19:43:21 +00:00
|
|
|
self._installer.update_game(filepath)
|
|
|
|
|
2022-02-19 11:57:45 +00:00
|
|
|
def _install_from_archive(self, filepath, force_reinstall):
|
2022-02-18 01:45:05 +00:00
|
|
|
print("Installing game from archive (this may takes some time)...")
|
2022-02-19 11:57:45 +00:00
|
|
|
self._installer.install_game(filepath, force_reinstall)
|
2022-02-18 01:45:05 +00:00
|
|
|
|
2022-02-16 19:43:21 +00:00
|
|
|
def _apply_voiceover_from_archive(self, filepath):
|
2022-02-17 15:02:08 +00:00
|
|
|
print("Applying voiceover from archive (this may takes some time)...")
|
2022-02-16 19:43:21 +00:00
|
|
|
self._installer.apply_voiceover(filepath)
|
|
|
|
|
|
|
|
def install_voiceover_from_file(self, filepath):
|
2022-02-17 15:02:08 +00:00
|
|
|
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"))
|
2022-02-16 19:43:21 +00:00
|
|
|
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.")
|
|
|
|
|
2022-02-18 13:09:03 +00:00
|
|
|
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
|
|
|
|
print("Patching game...")
|
2022-02-18 13:10:27 +00:00
|
|
|
asyncio.run(self._patcher.download_patch())
|
2022-02-18 13:09:03 +00:00
|
|
|
self._patcher.apply_patch(login_fix)
|
|
|
|
print("Game patched.")
|
|
|
|
print("Please refrain from sharing this project to public especially official channels, thank you.")
|
|
|
|
|
2022-02-16 19:43:21 +00:00
|
|
|
def install_from_file(self, filepath):
|
|
|
|
gamever = self._installer.get_game_version()
|
2022-02-18 01:45:05 +00:00
|
|
|
print("Archive game version: " + self._installer.get_game_archive_version(filepath))
|
2022-02-16 19:43:21 +00:00
|
|
|
if gamever:
|
|
|
|
print("Current game installation detected. ({})".format(self._installer.get_game_version()))
|
2022-02-17 15:02:08 +00:00
|
|
|
if not self._ask("Do you want to update the game? ({})".format(filepath)):
|
2022-02-16 19:43:21 +00:00
|
|
|
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
|
2022-02-19 11:57:45 +00:00
|
|
|
self._install_from_archive(filepath, False)
|
2022-02-18 01:45:05 +00:00
|
|
|
print("Game installed successfully.")
|
2022-02-16 19:43:21 +00:00
|
|
|
|
2022-02-19 11:57:45 +00:00
|
|
|
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))
|
|
|
|
|
2022-02-18 04:11:55 +00:00
|
|
|
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
|
2022-02-19 11:57:45 +00:00
|
|
|
self.download_game()
|
2022-02-18 04:11:55 +00:00
|
|
|
print("Installing game...")
|
2022-02-19 11:57:45 +00:00
|
|
|
self._install_from_archive(self._installer.temp_path.joinpath(res_info.game.latest.name), forced)
|
2022-02-18 04:11:55 +00:00
|
|
|
|
|
|
|
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
|
2022-02-15 17:49:33 +00:00
|
|
|
|
|
|
|
def update_game(self):
|
2022-02-18 04:11:55 +00:00
|
|
|
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)
|
2022-02-15 17:49:33 +00:00
|
|
|
|
|
|
|
def interactive_ui(self):
|
|
|
|
raise NotImplementedError()
|
2022-01-28 13:08:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
def main():
|
2022-02-15 19:55:19 +00:00
|
|
|
default_dirs = appdirs.AppDirs(constants.APP_NAME, constants.APP_AUTHOR)
|
2022-02-15 17:49:33 +00:00
|
|
|
parser = argparse.ArgumentParser(prog="worthless", description="A worthless launcher written in Python.")
|
2022-02-15 19:55:19 +00:00
|
|
|
parser.add_argument("-D", "--dir", action="store", type=Path, default=Path.cwd(),
|
2022-01-28 13:08:53 +00:00
|
|
|
help="Specify the game directory (default current working directory)")
|
2022-02-15 19:55:19 +00:00
|
|
|
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,
|
2022-02-16 19:43:21 +00:00
|
|
|
default_dirs.user_cache_dir))
|
2022-02-15 17:49:33 +00:00
|
|
|
parser.add_argument("-S", "--install", action="store_true",
|
2022-02-16 15:18:56 +00:00
|
|
|
help="Install/update the game (if not already installed, else do nothing)")
|
2022-02-16 19:43:21 +00:00
|
|
|
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, \
|
2022-02-15 17:49:33 +00:00
|
|
|
else update from archive)")
|
|
|
|
parser.add_argument("-Sp", "--patch", action="store_true",
|
|
|
|
help="Patch the game (if not already patched, else do nothing)")
|
2022-02-18 13:09:03 +00:00
|
|
|
parser.add_argument("--login-fix", action="store_true",
|
|
|
|
help="Patch the game to fix login issues (if not already patched, else do nothing)")
|
2022-02-19 11:57:45 +00:00
|
|
|
parser.add_argument("-Sy", "--update", action="store", type=str, default="",
|
2022-02-15 17:49:33 +00:00
|
|
|
help="Update the game and specified voiceover pack only (or install if not found)")
|
2022-02-19 11:57:45 +00:00
|
|
|
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")
|
2022-02-18 04:11:55 +00:00
|
|
|
parser.add_argument("-Sv", "--update-voiceover", action="store", type=str,
|
2022-02-16 15:18:56 +00:00
|
|
|
help="Update the voiceover pack only (or install if not found)")
|
2022-02-15 19:55:19 +00:00
|
|
|
parser.add_argument("-Syu", "--update-all", action="store_true",
|
2022-02-15 17:49:33 +00:00
|
|
|
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)")
|
2022-02-16 15:18:56 +00:00
|
|
|
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")
|
2022-02-19 11:57:45 +00:00
|
|
|
parser.add_argument("--from-ver", action="store", help="Override the detected game version", type=str, default=None)
|
2022-01-28 13:08:53 +00:00
|
|
|
parser.add_argument("--noconfirm", action="store_true",
|
2022-02-15 17:49:33 +00:00
|
|
|
help="Do not ask any for confirmation. (Ignored in interactive mode)")
|
2022-01-28 13:08:53 +00:00
|
|
|
args = parser.parse_args()
|
2022-02-15 17:49:33 +00:00
|
|
|
interactive_mode = not args.install and not args.install_from_file and not args.patch and not args.update and not \
|
2022-02-16 19:43:21 +00:00
|
|
|
args.remove and not args.remove_patch and not args.remove_voiceover and not args.get_game_version and not \
|
2022-02-19 11:57:45 +00:00
|
|
|
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
|
2022-02-16 19:43:21 +00:00
|
|
|
if args.temporary_dir:
|
|
|
|
args.temporary_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
|
|
ui = UI(args.dir, args.noconfirm, args.temporary_dir)
|
2022-02-15 17:49:33 +00:00
|
|
|
|
2022-01-28 13:08:53 +00:00
|
|
|
if args.install and args.update:
|
|
|
|
raise ValueError("Cannot specify both --install and --update arguments.")
|
|
|
|
|
2022-02-16 19:43:21 +00:00
|
|
|
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.")
|
|
|
|
|
2022-02-16 15:18:56 +00:00
|
|
|
if args.get_game_version:
|
|
|
|
ui.get_game_version()
|
|
|
|
|
2022-02-19 11:57:45 +00:00
|
|
|
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)
|
|
|
|
|
2022-01-28 13:08:53 +00:00
|
|
|
if args.install:
|
2022-02-15 17:49:33 +00:00
|
|
|
ui.install_game()
|
2022-01-28 13:08:53 +00:00
|
|
|
|
|
|
|
if args.update:
|
2022-02-18 04:11:55 +00:00
|
|
|
ui.update_game_voiceover(args.update)
|
|
|
|
|
|
|
|
if args.update_voiceover:
|
|
|
|
ui.update_voiceover(args.update_voiceover)
|
2022-02-16 19:43:21 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
2022-02-18 13:09:03 +00:00
|
|
|
if args.patch:
|
|
|
|
ui.patch_game(args.login_fix)
|
|
|
|
|
2022-02-16 19:43:21 +00:00
|
|
|
if args.remove_patch:
|
|
|
|
ui.revert_patch()
|
2022-01-28 13:08:53 +00:00
|
|
|
|
2022-02-15 17:49:33 +00:00
|
|
|
if interactive_mode:
|
|
|
|
ui.interactive_ui()
|
2022-01-28 13:08:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|