diff --git a/source/CT_Config.py b/source/CT_Config.py index 287d2a0..b87376e 100644 --- a/source/CT_Config.py +++ b/source/CT_Config.py @@ -7,9 +7,9 @@ from .Cup import Cup from .Track import Track -def get_cup_icon(id, font_path: str = "./file/SuperMario256.ttf", cup_icon_dir: str = "./file/cup_icon"): +def get_cup_icon(id, font_path: str = "./file/SuperMario256.ttf", cup_icon_dir: str = "./file/cup_icon") -> Image: """ - :param id: + :param id: id of the cup :param cup_icon_dir: directory to cup icon :param font_path: path to the font used to generate icon :return: cup icon @@ -37,8 +37,9 @@ class CT_Config: self.all_version = {version} self.gui = gui - def add_ordered_cup(self, cup: Cup): + def add_ordered_cup(self, cup: Cup) -> None: """ + add a cup to the config :param cup: a Cup object to add as an ordered cup :return: ? """ @@ -47,8 +48,9 @@ class CT_Config: self.all_version.add(track.since_version) self.all_tracks.append(track) - def add_unordered_track(self, track: Track): + def add_unordered_track(self, track: Track) -> None: """ + add a single track to the config :param track: a Track object to add as an unordered tracks :return: ? """ @@ -56,9 +58,10 @@ class CT_Config: self.all_version.add(track.since_version) self.all_tracks.append(track) - def create_ctfile(self, directory="./file/", highlight_version=None): + def create_ctfile(self, directory: str = "./file/", highlight_version: str = None) -> None: """ - :param highlight_version: + create a ctfile configuration in a directory + :param highlight_version: highlight a specific version in light blue :param directory: create CTFILE.txt and RCTFILE.txt in this directory :return: None """ @@ -94,7 +97,7 @@ class CT_Config: ctfile.write(cup.get_ctfile_cup(race=False, highlight_version=highlight_version)) rctfile.write(cup.get_ctfile_cup(race=True, highlight_version=highlight_version)) - def get_cticon(self): + def get_cticon(self) -> Image: """ get all cup icon into a single image :return: ct_icon image @@ -114,19 +117,19 @@ class CT_Config: return ct_icon - def load_ctconfig_file(self, ctconfig_file: str = "./ct_config.json"): + def load_ctconfig_file(self, ctconfig_file: str = "./ct_config.json") -> None: """ + load a ctconfig from a json file :param ctconfig_file: path to the ctconfig file - :return: ? """ with open(ctconfig_file, encoding="utf-8") as f: ctconfig_json = json.load(f) self.load_ctconfig_json(ctconfig_json) - def load_ctconfig_json(self, ctconfig_json: dict): + def load_ctconfig_json(self, ctconfig_json: dict) -> None: """ + load ctconfig from a dictionnary :param ctconfig_json: json of the ctconfig to load - :return: ? """ self.ordered_cups = [] self.unordered_tracks = [] @@ -152,7 +155,7 @@ class CT_Config: self.all_version.add(track.since_version) self.all_version = sorted(self.all_version) - def search_tracks(self, values_list=False, not_value=False, only_unordered_track=False, **kwargs): + def search_tracks(self, values_list=False, not_value=False, only_unordered_track=False, **kwargs) -> list: """ :param only_unordered_track: only search in unordered track :param values_list: search track with a value list instead of a single value diff --git a/source/Cup.py b/source/Cup.py index 1bedb88..df14da6 100644 --- a/source/Cup.py +++ b/source/Cup.py @@ -6,20 +6,33 @@ class Cup: track1: Track = None, track2: Track = None, track3: Track = None, - track4: Track = None, locked: bool = False, + track4: Track = None, + locked: bool = False, *args, **kwargs): + """ + class of a cup + :param name: name of the cup + :param track1: first track + :param track2: second track + :param track3: third track + :param track4: fourth track + :param locked: is the track locked (used to load ctconfig in CT_Config) + :param args: other args that I could add in the future + :param kwargs: other kwargs that I could add in the future + """ self.name = name + self.locked = locked self.tracks = [ track1 if track1 else Track(), track2 if track2 else Track(), track3 if track3 else Track(), track4 if track4 else Track() ] - self.locked = locked - def get_ctfile_cup(self, *args, **kwargs): + def get_ctfile_cup(self, *args, **kwargs) -> str: """ + get the ctfile definition for the cup :param race: is it a text used for Race_*.szs ? :return: ctfile definition for the cup """ @@ -28,7 +41,11 @@ class Cup: ctfile_cup += track.get_ctfile(*args, **kwargs) return ctfile_cup - def load_from_json(self, cup: dict): + def load_from_json(self, cup: dict) -> None: + """ + load the cup from a dictionnary + :param cup: dictionnary cup + """ for key, value in cup.items(): # load all value in the json as class attribute if key != "tracks": setattr(self, key, value) diff --git a/source/Game.py b/source/Game.py index 0293422..a1afe0e 100644 --- a/source/Game.py +++ b/source/Game.py @@ -1,4 +1,4 @@ -from tkinter import messagebox, StringVar, BooleanVar, IntVar +from tkinter import messagebox from PIL import Image import shutil import glob @@ -7,6 +7,7 @@ import os from .CT_Config import CT_Config from .definition import * +from .Gui import NoGui from . import wszst @@ -40,23 +41,6 @@ class CantConvertTrack(Exception): super().__init__("Can't convert track, check if download are enabled.") -class NoGui: - """ - 'fake' gui if no gui are used for compatibility. - """ - def progression(self, *args, **kwargs): print(args, kwargs) - def translate(self, *args, **kwargs): return "" - def log_error(self, *args, **kwargs): print(args, kwargs) - - is_dev_version = False - - stringvar_game_format = StringVar() - boolvar_disable_download = BooleanVar() - intvar_process_track = IntVar() - boolvar_dont_check_track_sha1 = BooleanVar() - boolvar_del_track_after_conv = BooleanVar() - - class Game: def __init__(self, path: str = "", region_ID: str = "P", game_ID: str = "RMCP01", gui=None): """ @@ -170,7 +154,13 @@ class Game: self.gui.progress(show=True, indeter=False, statut=self.gui.translate("Installing mod"), max=max_step, step=0) - def replace_file(path, file, subpath="/"): + def replace_file(path, file, subpath="/") -> None: + """ + Replace subfile in the .szs file + :param path: path to the .szs file + :param file: file to replace + :param subpath: directory between .szs file and file inside to replace + """ self.gui.progress(statut=self.gui.translate("Editing", "\n", get_nodir(path)), add=1) extension = get_extension(path) @@ -182,14 +172,14 @@ class Game: szs_extract_path = path + ".d" if os.path.exists(szs_extract_path + subpath): if subpath[-1] == "/": - filecopy(f"./file/{file}", szs_extract_path + subpath + file) + shutil.copyfile(f"./file/{file}", szs_extract_path + subpath + file) else: - filecopy(f"./file/{file}", szs_extract_path + subpath) + shutil.copyfile(f"./file/{file}", szs_extract_path + subpath) elif path[-1] == "/": - filecopy(f"./file/{file}", path + file) + shutil.copyfile(f"./file/{file}", path + file) else: - filecopy(f"./file/{file}", path) + shutil.copyfile(f"./file/{file}", path) for fp in fs: for f in glob.glob(self.path + "/files/" + fp, recursive=True): @@ -215,9 +205,9 @@ class Game: shutil.copytree("./file/Track/", self.path + "/files/Race/Course/", dirs_exist_ok=True) if not (os.path.exists(self.path + "/tmp/")): os.makedirs(self.path + "/tmp/") - filecopy("./file/CTFILE.txt", self.path + "/tmp/CTFILE.txt") - filecopy("./file/lpar-default.txt", self.path + "/tmp/lpar-default.txt") - filecopy(f"./file/lecode-{self.region}.bin", self.path + f"/tmp/lecode-{self.region}.bin") + shutil.copyfile("./file/CTFILE.txt", self.path + "/tmp/CTFILE.txt") + shutil.copyfile("./file/lpar-default.txt", self.path + "/tmp/lpar-default.txt") + shutil.copyfile(f"./file/lecode-{self.region}.bin", self.path + f"/tmp/lecode-{self.region}.bin") wszst.lec_patch( self.path, @@ -315,7 +305,7 @@ class Game: if not os.path.exists("./file/tmp/"): os.makedirs("./file/tmp/") - filecopy(gamefile + ".d/message/Common.bmg", "./file/tmp/Common.bmg") + shutil.copyfile(gamefile + ".d/message/Common.bmg", "./file/tmp/Common.bmg") bmgcommon = wszst.ctc_patch_bmg(ctfile="./file/CTFILE.txt", bmgs=["./file/tmp/Common.bmg", "./file/ExtraCommon.txt"]) rbmgcommon = wszst.ctc_patch_bmg(ctfile="./file/RCTFILE.txt", @@ -374,11 +364,10 @@ class Game: finally: self.gui.progress(show=False) - def patch_image(self, fc) -> None: + def patch_image(self, fc: dict) -> None: """ Convert .png image into the format wrote in convert_file - :param fc: - :return: + :param fc: file convert, a dictionnary indicating which format a file need to be converted """ for i, file in enumerate(fc["img"]): self.gui.progress(statut=self.gui.translate("Converting images") + f"\n({i + 1}/{len(fc['img'])}) {file}", @@ -390,7 +379,6 @@ class Game: patch descriptive image used when the game boot :param img_desc_path: directory where original part of the image are stored :param dest_dir: directory where patched image will be saved - :return: """ il = Image.open(img_desc_path + "/illustration.png") il_16_9 = il.resize((832, 456)) diff --git a/source/Gui.py b/source/Gui.py index 659c680..ba55911 100644 --- a/source/Gui.py +++ b/source/Gui.py @@ -18,6 +18,28 @@ def restart(): exit() +class NoGui: + """ + 'fake' gui if no gui are used for compatibility. + """ + class NoButton: + def grid(self, *args, **kwargs): pass + def config(self, *args, **kwargs): pass + + def progress(*args, **kwargs): print(args, kwargs) + def translate(*args, **kwargs): return "" + def log_error(*args, **kwargs): print(args, kwargs) + def restart(self): restart() + + is_dev_version = False + button_install_mod = NoButton() + stringvar_game_format = StringVar() + boolvar_disable_download = BooleanVar() + intvar_process_track = IntVar() + boolvar_dont_check_track_sha1 = BooleanVar() + boolvar_del_track_after_conv = BooleanVar() + + class Gui: def __init__(self): """ diff --git a/source/Option.py b/source/Option.py index 867ddf8..eb5abf8 100644 --- a/source/Option.py +++ b/source/Option.py @@ -4,6 +4,9 @@ import os class Option: def __init__(self): + """ + class for Option + """ self.language = "en" self.format = "FST" self.disable_download = False @@ -12,7 +15,14 @@ class Option: self.dont_check_track_sha1 = False self.process_track = 8 - def edit(self, option, value, need_restart=False, gui=None): + def edit(self, option: str, value: any, need_restart: bool = False, gui=None) -> None: + """ + Change the value of a parameter + :param option: the name of the option to change + :param value: the new value for the option + :param need_restart: do this value need a restart ? + :param gui: the gui object to restart + """ if type(value) in [str, int, bool]: setattr(self, option, value) else: @@ -20,17 +30,29 @@ class Option: self.save_to_file() if need_restart: gui.restart() - def load_from_file(self, option_file: str = "./option.json"): + def load_from_file(self, option_file: str = "./option.json") -> None: + """ + Load all options from a json file + :param option_file: the file where to load option + """ if os.path.exists(option_file): with open(option_file, encoding="utf-8") as file: file_json = json.load(file) self.load_from_json(file_json) - def load_from_json(self, option_json: dict): + def load_from_json(self, option_json: dict) -> None: + """ + Load all options from a dictionnary + :param option_json: the dictionnary to load + """ for key, value in option_json.items(): # load all value in the json as class attribute setattr(self, key, value) - def save_to_file(self, option_file: str = "./option.json"): + def save_to_file(self, option_file: str = "./option.json") -> None: + """ + Save all options to a file + :param option_file: the file where to save option + """ option_json: dict = self.__dict__ # this return all attribute of the class as a dict with open(option_file, "w", encoding="utf-8") as file: json.dump(option_json, file, ensure_ascii=False) diff --git a/source/Track.py b/source/Track.py index ef0fa38..02f35df 100644 --- a/source/Track.py +++ b/source/Track.py @@ -65,12 +65,11 @@ class Track: if wszst.sha1(self.file_wu8) == self.sha1: return 0 else: return -1 - def convert_wu8_to_szs(self) -> str: + def convert_wu8_to_szs(self) -> None: """ convert track to szs - :return: path to szs track """ - return wszst.normalize(src_file=self.file_wu8) + wszst.normalize(src_file=self.file_wu8) def download_wu8(self, github_content_root: str) -> int: """ diff --git a/source/definition.py b/source/definition.py index 24dd5ad..b50fa1a 100644 --- a/source/definition.py +++ b/source/definition.py @@ -95,14 +95,19 @@ region_id_to_name = { } -def filecopy(src, dst): - with open(src, "rb") as f1: - with open(dst, "wb") as f2: - f2.write(f1.read()) # could be buffered - - def in_thread(func): - def wrapped_func(*args, **kwargs): + """ + instead of calling a function, this will start it in a thread + :param func: function to thread + :return: threaded function + """ + def wrapped_func(*args, **kwargs) -> Thread: + """ + function that will be returned instead of the function, will call it in a thread + :param args: args of the original function + :param kwargs: kwargs of the original function + :return: thread object to the function + """ thread = Thread(target=func, args=args, kwargs=kwargs) thread.setDaemon(True) thread.start() diff --git a/source/wszst.py b/source/wszst.py index 7020809..4d9b35a 100644 --- a/source/wszst.py +++ b/source/wszst.py @@ -2,9 +2,9 @@ from .definition import * import subprocess -def sha1(file, autoadd_path: str = "./file/auto-add/"): +def sha1(file, autoadd_path: str = "./file/auto-add/") -> str: """ - :param autoadd_path: + :param autoadd_path: directory where is autoadd directory :param file: track file to check sha1 :return: track's sha1 """ @@ -14,86 +14,126 @@ def sha1(file, autoadd_path: str = "./file/auto-add/"): def normalize(src_file: str, dest_dir: str = "./file/Track/", dest_name: str = "%N.szs", - output_format: str = "szs", autoadd_path: str = "./file/auto-add/"): + output_format: str = "szs", autoadd_path: str = "./file/auto-add/") -> None: """ - :param use_popen: True if you want to use Popen to convert + convert a track into an another format :param src_file: source file :param dest_dir: destination directory :param dest_name: destination filename (%N mean same name as src_file) :param output_format: format of the destination track :param autoadd_path: path of the auto-add directory - :return: 0 """ subprocess.run(["./tools/szs/wszst", "NORMALIZE", src_file, "--DEST", dest_dir+dest_name, "--"+output_format, "--overwrite", "--autoadd-path", autoadd_path], creationflags=CREATE_NO_WINDOW, stderr=subprocess.PIPE) -def wit_extract(file: str, dest_dir: str): +def wit_extract(file: str, dest_dir: str) -> None: """ + extract the game into a directory :param file: game's file to extract (can be WBFS, ISO, CISO) :param dest_dir: where to extract the game - :return: ? """ subprocess.run(["./tools/wit/wit", "EXTRACT", get_nodir(file), "--DEST", dest_dir], creationflags=CREATE_NO_WINDOW, cwd=get_dir(file)) -def create(file: str): +def create(file: str) -> None: """ + convert a directory into a szs file :param file: create a .szs file from the directory {file}.d - :return: """ subprocess.run(["./tools/szs/wszst", "CREATE", get_nodir(file) + ".d", "-d", get_nodir(file), "--overwrite"], creationflags=CREATE_NO_WINDOW, cwd=get_dir(file), check=True, stdout=subprocess.PIPE) -def str_patch(file: str): - subprocess.run(["./tools/szs/wstrt", "patch", get_nodir(file) + "/sys/main.dol", "--clean-dol", - "--add-lecode"], creationflags=CREATE_NO_WINDOW, cwd=get_dir(file), +def str_patch(path: str) -> None: + """ + Patch the main.dol file + :param path: path to the game + """ + subprocess.run(["./tools/szs/wstrt", "patch", get_nodir(path) + "/sys/main.dol", "--clean-dol", + "--add-lecode"], creationflags=CREATE_NO_WINDOW, cwd=get_dir(path), check=True, stdout=subprocess.PIPE) -def lec_patch(file: str, +def lec_patch(path: str, lecode_file: str = f"./tmp/lecode-PAL.bin", dest_lecode_file: str = f"./files/rel/lecode-PAL.bin", game_track_path: str = "./files/Race/Course/", move_track_path: str = "./files/Race/Course/", ctfile_path: str = "./tmp/CTFILE.txt", - lpar_path: str = "./tmp/lpar-default.txt"): + lpar_path: str = "./tmp/lpar-default.txt") -> None: + """ + Patch the file with a lecode file (this is the adding track part) + :param path: path to the game file + :param lecode_file: path to the lecode file + :param dest_lecode_file: destination of the lecode file + :param game_track_path: subpath to the track directory + :param move_track_path: where are stored the track to move + :param ctfile_path: where is the ctfile (track and cup definition) + :param lpar_path: where is the lpar_path (game modification like speed, speedometer, ...) + """ subprocess.run( ["./tools/szs/wlect", "patch", lecode_file, "-od", dest_lecode_file, "--track-dir", game_track_path, "--move-tracks", move_track_path, "--le-define", ctfile_path, "--lpar", - lpar_path, "--overwrite"], creationflags=CREATE_NO_WINDOW, cwd=file, check=True, stdout=subprocess.PIPE) + lpar_path, "--overwrite"], creationflags=CREATE_NO_WINDOW, cwd=path, check=True, stdout=subprocess.PIPE) -def edit(file, region_ID: str = "P", name: str = "Mario Kart Wii"): +def edit(file: str, region_ID: str = "P", name: str = "Mario Kart Wii") -> None: + """ + Edit game property like region or name + :param file: game's file + :param region_ID: new region_ID + :param name: new name + """ subprocess.run(["./tools/wit/wit", "EDIT", get_nodir(file), "--id", f"RMC{region_ID}60", "--name", name, "--modify", "ALL"], creationflags=CREATE_NO_WINDOW, cwd=get_dir(file), check=True, stdout=subprocess.PIPE) -def autoadd(file: str, dest_dir: str): +def autoadd(file: str, dest_dir: str) -> None: + """ + Create an auto_add directory from a game file + :param file: the game's path + :param dest_dir: directory where to store autoadd file + """ subprocess.run(["./tools/szs/wszst", "AUTOADD", get_nodir(file) + "/files/Race/Course/", "--DEST", dest_dir], creationflags=CREATE_NO_WINDOW, cwd=get_dir(file), check=True, stdout=subprocess.PIPE) -def bmg_encode(file: str): +def bmg_encode(file: str) -> None: + """ + Encode a txt file into a bmg file + :param file: txt file to convert + """ subprocess.run(["./tools/szs/wbmgt", "ENCODE", get_nodir(file), "--overwrite"], creationflags=CREATE_NO_WINDOW, cwd=get_dir(file)) -def bmg_cat(path: str, subfile: str = ".d/message/Common.bmg"): +def bmg_cat(path: str, subfile: str = ".d/message/Common.bmg") -> str: + """ + read a bmg file + :param path: path to a szs file + :param subfile: path to a subdirectory + :return: bmg definition + """ return subprocess.run(["./tools/szs/wbmgt", "CAT", get_nodir(path) + subfile], creationflags=CREATE_NO_WINDOW, cwd=get_dir(path), check=True, stdout=subprocess.PIPE).stdout.decode() -def wit_copy(src_path, dst_path, format: str = "ISO"): +def wit_copy(src_path, dst_path, format: str = "ISO") -> None: + """ + Copy the game into an another format + :param src_path: original game path + :param dst_path: new game path + :param format: format for the new game + """ subprocess.run(["./tools/wit/wit", "COPY", get_nodir(src_path), "--DEST", get_nodir(dst_path), f"--{format.lower()}", "--overwrite"], creationflags=CREATE_NO_WINDOW, cwd=get_dir(dst_path), @@ -101,6 +141,12 @@ def wit_copy(src_path, dst_path, format: str = "ISO"): def ctc_patch_bmg(bmgs: list, ctfile: str = "./file/CTFILE.txt"): + """ + Patch a bmg file with a ctfile with OVERWRITE option + :param bmgs: all bmg files + :param ctfile: the ctfile path + :return: combined bmg + """ bmg_cmd = [] for bmg in bmgs: bmg_cmd.extend(["--patch-bmg", f"OVERWRITE={bmg}"]) return subprocess.run( @@ -108,11 +154,21 @@ def ctc_patch_bmg(bmgs: list, ctfile: str = "./file/CTFILE.txt"): creationflags=CREATE_NO_WINDOW, check=True, stdout=subprocess.PIPE).stdout.decode() -def img_encode(src_file, format): +def img_encode(src_file: str, format: str) -> None: + """ + Encode an .png image into a new format + :param src_file: .png image + :param format: new image format + """ subprocess.run(["./tools/szs/wimgt", "ENCODE", src_file, "-x", format, "--overwrite"], creationflags=CREATE_NO_WINDOW, check=True, stdout=subprocess.PIPE) -def szs_extract(file, dest_dir): +def szs_extract(file: str, dest_dir: str) -> None: + """ + Extract an szs in a directory + :param file: .szs file + :param dest_dir: directory where to extract the file + """ subprocess.run(["./tools/szs/wszst", "EXTRACT", get_nodir(file), "--DEST", dest_dir+".d"], creationflags=CREATE_NO_WINDOW, cwd=get_dir(file)) \ No newline at end of file