From de5421cfdf32d2c84fa14029ddaa2de0fe185395 Mon Sep 17 00:00:00 2001 From: Faraphel Date: Tue, 6 Sep 2022 09:56:14 +0200 Subject: [PATCH] cli now support arguments (removed the old input method) and raise error if one of them is invalid. --- assets/language/en.json | 2 + assets/language/fr.json | 2 + source/interface/__init__.py | 39 ++++++++++++++++++ source/interface/cli/install.py | 71 ++++++++++++++++++++------------- 4 files changed, 86 insertions(+), 28 deletions(-) diff --git a/assets/language/en.json b/assets/language/en.json index 0599927..12e3f71 100644 --- a/assets/language/en.json +++ b/assets/language/en.json @@ -74,6 +74,8 @@ "ERROR_SEE_LOGS": "See error.log for more information", "ERROR_INVALID_SOURCE_GAME": "Invalid source game : \"%s\"", "ERROR_INVALID_GAME_DESTINATION": "Invalid game destination : \"%s\"", + "ERROR_INVALID_MOD": "Invalid mod : \"%s\"", + "ERROR_INVALID_OUTPUT_TYPE": "Invalid output type : \"%s\"", "ERROR_MYSTUFF_PROFILE_ALREADY_EXIST": "The MyStuff profile \"%s\" already exist !", "ERROR_MYSTUFF_PROFILE_FORBIDDEN_NAME": "The MyStuff profile name \"%s\" is forbidden !", "ERROR_NOT_MKW_GAME": "This game is not a Mario Kart Wii game : \"%s\"", diff --git a/assets/language/fr.json b/assets/language/fr.json index 40469cb..b38ecf9 100644 --- a/assets/language/fr.json +++ b/assets/language/fr.json @@ -74,6 +74,8 @@ "ERROR_SEE_LOGS": "Voir le fichier error.log pour plus d'information", "ERROR_INVALID_SOURCE_GAME": "Jeu source invalide : \"%s\"", "ERROR_INVALID_GAME_DESTINATION": "Destination de jeu invalide : \"%s\"", + "ERROR_INVALID_MOD": "Mod invalide : \"%s\"", + "ERROR_INVALID_OUTPUT_TYPE": "type de sortie invalide : \"%s\"", "ERROR_MYSTUFF_PROFILE_ALREADY_EXIST": "Le profil MyStuff \"%s\" existe déjà !", "ERROR_MYSTUFF_PROFILE_FORBIDDEN_NAME": "Le nom de profil MyStuff \"%s\" est interdit !", "ERROR_NOT_MKW_GAME": "Ce jeu n'est pas un jeu Mario Kart Wii : \"%s\"", diff --git a/source/interface/__init__.py b/source/interface/__init__.py index 8e5a20e..5f32bec 100644 --- a/source/interface/__init__.py +++ b/source/interface/__init__.py @@ -1,3 +1,42 @@ +import os +from pathlib import Path +from typing import TYPE_CHECKING + +from source import system +from source.translation import translate_external, translate as _ + +if TYPE_CHECKING: + from source.mkw.ModConfig import ModConfig + + +def is_valid_source_path(path: Path): + """ + :param path: path to the source path + :return: is the source path valid + """ + return path.exists() and str(path) != "." + + +def is_valid_destination_path(path: Path): + """ + :param path: path to the destination path + :return: is the destination path valid + """ + return path.exists() and str(path) != "." + + +def is_user_root(): + """ + :return: does the user have root permission (linux only) + """ + return system != "lin64" or os.getuid() == 0 + + +def are_permissions_enabled(): + """ + :return: does the installer have writing and execution permissions + """ + return os.access("./", os.W_OK | os.X_OK) def get_finished_installation_message(mod_config: "ModConfig") -> str: diff --git a/source/interface/cli/install.py b/source/interface/cli/install.py index 6b9e6d4..0e4f688 100644 --- a/source/interface/cli/install.py +++ b/source/interface/cli/install.py @@ -1,41 +1,44 @@ import argparse +import sys from pathlib import Path +from source.interface import is_valid_source_path, is_valid_destination_path, get_finished_installation_message from source.mkw.Game import Game from source.mkw.ModConfig import ModConfig -from source.translation import translate as _ from source.mkw.collection.Extension import Extension +from source.translation import translate as _ def cli(options, argparser: argparse.ArgumentParser): - argparser.add_argument("-m", "--mod", help="name of the mod to install") - argparser.add_argument("-s", "--source", help="path to the original game") - argparser.add_argument("-d", "--dest", help="destination directory of the patched game") - argparser.add_argument("-ot", "--output_type", help="format of the patched game") + argparser.add_argument("-m", "--mod", required=True, help="name of the mod to install") + argparser.add_argument("-s", "--source", required=True, help="path to the original game") + argparser.add_argument("-d", "--dest", required=True, help="destination directory of the patched game") + argparser.add_argument("-ot", "--output_type", required=True, help="format of the patched game") args = argparser.parse_args() - packs = [] - for pack in Path("./Pack/").iterdir(): - packs.append(pack) + mod_config_path = Path(f"./Pack/{args.mod}/mod_config.json") + if not mod_config_path.exists(): + print(_("ERROR_INVALID_MOD") % args.mod, file=sys.stderr) + return + mod_config = ModConfig.from_file(mod_config_path) - mod_name = args.mod - choices = [pack.name for pack in packs] - while mod_name is None or mod_name not in choices: mod_name = input(_("TEXT_INPUT_MOD_NAME") % choices) - mod_config = ModConfig.from_file(Path(f"./Pack/{mod_name}/mod_config.json")) + game_path = Path(args.source) + if not is_valid_source_path(game_path): + print(_("ERROR_INVALID_SOURCE_GAME") % game_path, file=sys.stderr) + return + game = Game(args.source) - source_path = args.source - if source_path is None: source_path = input(_("TEXT_INPUT_SOURCE_PATH")) - game = Game(source_path) + destination_path = Path(args.dest) + if not is_valid_destination_path(destination_path): + print(_("ERROR_INVALID_GAME_DESTINATION") % destination_path, file=sys.stderr) + return - destination_directory = args.dest - if destination_directory is None: destination_directory = input(_("TEXT_INPUT_DESTINATION_DIRECTORY")) - destination_path = Path(destination_directory) - - output_name = args.output_type - choices = [extension.name for extension in Extension] - if output_name is None or mod_name not in choices: output_name = input(_("TEXT_INPUT_OUPUT_TYPE") % choices) - output_type = Extension[output_name] + try: output_type = Extension[args.output_type] + except KeyError: + print(_("ERROR_INVALID_OUTPUT_TYPE") % args.output_type, file=sys.stderr) + return + # installation and progress bar progressbar_max: int = 40 title: str = "" @@ -44,6 +47,7 @@ def cli(options, argparser: argparse.ArgumentParser): max_part: int = 0 current_step: int = 0 max_step: int = 0 + determinate: bool = False for step in game.install_mod( dest=destination_path, @@ -59,14 +63,25 @@ def cli(options, argparser: argparse.ArgumentParser): if step.max_step is not None: max_step = step.max_step if step.set_step is not None: current_step = step.set_step if step.step is not None: current_step += step.step + if step.determinate is not None: determinate = step.determinate progressbar_step: int = current_step * progressbar_max // max_step if max_step > 0 else 0 - print("\033[H\033[J", end="") - print(title, f"({current_part} / {max_part})") - print(description) print( - f"{round((current_step / max_step if max_step > 0 else 0) * 100, 2)}% " - f"[{'#' * progressbar_step}{' ' * (progressbar_max - progressbar_step)}]" + "\033[H\033[J", "\n", # clear the shell + title, f"({current_part} / {max_part})", "\n", # print the title and the actual part + description, "\n", # print the description + *(( + f"{round((current_step / max_step if max_step > 0 else 0) * 100, 2)}% " + f"[{'#' * progressbar_step}{' ' * (progressbar_max - progressbar_step)}]" + ) if determinate else ()), # if determinate, show a progress bar + + sep="" ) + print( + "\033[H\033[J", + get_finished_installation_message(mod_config), + + sep="\n" + )