added in_thread decorator, wszst module is now always used to edit file, ctconfig is now property of Game class and no more Gui

This commit is contained in:
raphael60650 2021-07-18 22:17:03 +02:00
parent 014e4fe259
commit 01ac79f623
7 changed files with 271 additions and 252 deletions

View file

@ -79,8 +79,7 @@ class CT_Config:
"%LE-FLAGS=1\n" "%LE-FLAGS=1\n"
"%WIIMM-CUP=1\n" "%WIIMM-CUP=1\n"
"N N$SWAP | N$F_WII\n\n") "N N$SWAP | N$F_WII\n\n")
ctfile.write(header); ctfile.write(header); rctfile.write(header)
rctfile.write(header)
# generate cup for undefined track # generate cup for undefined track
unordered_cups = [] unordered_cups = []

View file

@ -1,4 +1,4 @@
from source.Track import Track from .Track import Track
class Cup: class Cup:

View file

@ -1,16 +1,20 @@
from tkinter import messagebox from tkinter import messagebox
from threading import Thread
from PIL import Image from PIL import Image
import subprocess
import shutil import shutil
import glob import glob
import json import json
import os import os
from .CT_Config import CT_Config
from .definition import * from .definition import *
from . import wszst from . import wszst
class RomAlreadyPatched(Exception):
def __init__(self):
super().__init__("ROM Already patched !")
class InvalidGamePath(Exception): class InvalidGamePath(Exception):
def __init__(self): def __init__(self):
super().__init__("This path is not valid !") super().__init__("This path is not valid !")
@ -37,14 +41,20 @@ class CantConvertTrack(Exception):
class Game: class Game:
def __init__(self, path: str, region_ID: str = "P", game_ID: str = "RMCP01", gui=None): def __init__(self, path: str = "", region_ID: str = "P", game_ID: str = "RMCP01", gui=None):
if not os.path.exists(path): raise InvalidGamePath() if not os.path.exists(path) and path: raise InvalidGamePath()
self.extension = get_extension(path).upper() self.extension = None
self.path = path self.path = path
self.set_path(path)
self.region = region_id_to_name[region_ID] self.region = region_id_to_name[region_ID]
self.region_ID = region_ID self.region_ID = region_ID
self.game_ID = game_ID self.game_ID = game_ID
self.gui = gui self.gui = gui
self.ctconfig = CT_Config()
def set_path(self, path):
self.extension = get_extension(path).upper()
self.path = path
def convert_to(self, format: str = "FST"): def convert_to(self, format: str = "FST"):
""" """
@ -53,19 +63,12 @@ class Game:
""" """
if format in ["ISO", "WBFS", "CISO"]: if format in ["ISO", "WBFS", "CISO"]:
path_game_format: str = os.path.realpath(self.path + "/../MKWFaraphel." + format.lower()) path_game_format: str = os.path.realpath(self.path + "/../MKWFaraphel." + format.lower())
subprocess.run(["./tools/wit/wit", "COPY", get_nodir(self.path), "--DEST", wszst.wit_copy(self.path, path_game_format, format)
get_nodir(path_game_format), f"--{format.lower()}", "--overwrite"],
creationflags=CREATE_NO_WINDOW, cwd=get_dir(path_game_format),
check=True, stdout=subprocess.PIPE)
shutil.rmtree(self.path) shutil.rmtree(self.path)
self.path = path_game_format self.path = path_game_format
self.gui.progress(statut=self.gui.translate("Changing game's ID"), add=1) self.gui.progress(statut=self.gui.translate("Changing game's ID"), add=1)
subprocess.run(["./tools/wit/wit", "EDIT", get_nodir(self.path), "--id", wszst.edit(self.path, region_ID=self.region_ID, name=f"Mario Kart Wii Faraphel {self.ctconfig.version}")
f"RMC{self.region_ID}60", "--name",
f"Mario Kart Wii Faraphel {self.gui.ctconfig.version}", "--modify", "ALL"],
creationflags=CREATE_NO_WINDOW, cwd=get_dir(self.path),
check=True, stdout=subprocess.PIPE)
def extract(self): def extract(self):
if self.extension == "DOL": if self.extension == "DOL":
@ -79,7 +82,7 @@ class Game:
if not (os.path.exists(path_dir)): break if not (os.path.exists(path_dir)): break
directory_name, i = f"MKWiiFaraphel ({i})", i + 1 directory_name, i = f"MKWiiFaraphel ({i})", i + 1
wszst.extract(self.path, path_dir) wszst.wit_extract(self.path, path_dir)
self.path = path_dir self.path = path_dir
if os.path.exists(self.path + "/DATA"): self.path += "/DATA" if os.path.exists(self.path + "/DATA"): self.path += "/DATA"
@ -89,7 +92,7 @@ class Game:
raise InvalidFormat() raise InvalidFormat()
if glob.glob(self.path + "/files/rel/lecode-???.bin"): # if a LECODE file is already here if glob.glob(self.path + "/files/rel/lecode-???.bin"): # if a LECODE file is already here
raise Warning("ROM Already patched") # warning already patched raise RomAlreadyPatched() # warning already patched
with open(self.path + "/setup.txt") as f: with open(self.path + "/setup.txt") as f:
setup = f.read() setup = f.read()
@ -99,133 +102,118 @@ class Game:
self.region_ID = self.game_ID[3] self.region_ID = self.game_ID[3]
self.region = region_id_to_name[self.region_ID] if self.region_ID in region_id_to_name else self.region self.region = region_id_to_name[self.region_ID] if self.region_ID in region_id_to_name else self.region
@in_thread
def install_mod(self): def install_mod(self):
def func(): try:
try: with open("./fs.json") as f:
with open("./fs.json") as f: fs = json.load(f)
fs = json.load(f)
# This part is used to estimate the max_step # This part is used to estimate the max_step
extracted_file = [] extracted_file = []
max_step, step = 1, 0 max_step, step = 1, 0
def count_rf(path): def count_rf(path):
nonlocal max_step nonlocal max_step
max_step += 1 max_step += 1
if get_extension(path) == "szs": if get_extension(path) == "szs":
if not (os.path.realpath(path) in extracted_file): if not (os.path.realpath(path) in extracted_file):
extracted_file.append(os.path.realpath(path)) extracted_file.append(os.path.realpath(path))
max_step += 1 max_step += 1
for fp in fs: for fp in fs:
for f in glob.glob(self.path + "/files/" + fp, recursive=True): for f in glob.glob(self.path + "/files/" + fp, recursive=True):
if type(fs[fp]) == str: if type(fs[fp]) == str:
count_rf(path=f) count_rf(path=f)
elif type(fs[fp]) == dict: elif type(fs[fp]) == dict:
for nf in fs[fp]: for nf in fs[fp]:
if type(fs[fp][nf]) == str: if type(fs[fp][nf]) == str:
count_rf(path=f) count_rf(path=f)
elif type(fs[fp][nf]) == list: elif type(fs[fp][nf]) == list:
for ffp in fs[fp][nf]: count_rf(path=f) for ffp in fs[fp][nf]: count_rf(path=f)
### ###
extracted_file = [] extracted_file = []
max_step += 4 # PATCH main.dol and PATCH lecode.bin, converting, changing ID max_step += 4 # PATCH main.dol and PATCH lecode.bin, converting, changing ID
self.gui.progress(show=True, indeter=False, statut=self.gui.translate("Installing mod"), max=max_step, self.gui.progress(show=True, indeter=False, statut=self.gui.translate("Installing mod"), max=max_step,
step=0) step=0)
def replace_file(path, file, subpath="/"): def replace_file(path, file, subpath="/"):
self.gui.progress(statut=self.gui.translate("Editing", "\n", get_nodir(path)), add=1) self.gui.progress(statut=self.gui.translate("Editing", "\n", get_nodir(path)), add=1)
extension = get_extension(path) extension = get_extension(path)
if extension == "szs": if extension == "szs":
if not (os.path.realpath(path) in extracted_file): if not (os.path.realpath(path) in extracted_file):
subprocess.run( wszst.szs_extract(path, get_nodir(path))
["./tools/szs/wszst", "EXTRACT", get_nodir(path), "-d", get_nodir(path) + ".d", extracted_file.append(os.path.realpath(path))
"--overwrite"], creationflags=CREATE_NO_WINDOW, cwd=get_dir(path),
check=True, stdout=subprocess.PIPE)
extracted_file.append(os.path.realpath(path))
szs_extract_path = path + ".d" szs_extract_path = path + ".d"
if os.path.exists(szs_extract_path + subpath): if os.path.exists(szs_extract_path + subpath):
if subpath[-1] == "/": if subpath[-1] == "/":
filecopy(f"./file/{file}", szs_extract_path + subpath + file) filecopy(f"./file/{file}", szs_extract_path + subpath + file)
else: else:
filecopy(f"./file/{file}", szs_extract_path + subpath) filecopy(f"./file/{file}", szs_extract_path + subpath)
elif path[-1] == "/": elif path[-1] == "/":
filecopy(f"./file/{file}", path + file) filecopy(f"./file/{file}", path + file)
else: else:
filecopy(f"./file/{file}", path) filecopy(f"./file/{file}", path)
for fp in fs: for fp in fs:
for f in glob.glob(self.path + "/files/" + fp, recursive=True): for f in glob.glob(self.path + "/files/" + fp, recursive=True):
if type(fs[fp]) == str: if type(fs[fp]) == str:
replace_file(path=f, file=fs[fp]) replace_file(path=f, file=fs[fp])
elif type(fs[fp]) == dict: elif type(fs[fp]) == dict:
for nf in fs[fp]: for nf in fs[fp]:
if type(fs[fp][nf]) == str: if type(fs[fp][nf]) == str:
replace_file(path=f, subpath=nf, file=fs[fp][nf]) replace_file(path=f, subpath=nf, file=fs[fp][nf])
elif type(fs[fp][nf]) == list: elif type(fs[fp][nf]) == list:
for ffp in fs[fp][nf]: replace_file(path=f, subpath=nf, file=ffp) for ffp in fs[fp][nf]: replace_file(path=f, subpath=nf, file=ffp)
for file in extracted_file: for file in extracted_file:
self.gui.progress(statut=self.gui.translate("Recompilating", "\n", get_nodir(file)), add=1) self.gui.progress(statut=self.gui.translate("Recompilating", "\n", get_nodir(file)), add=1)
subprocess.run(["./tools/szs/wszst", "CREATE", get_nodir(file) + ".d", "-d", get_nodir(file), wszst.create(file)
"--overwrite"], creationflags=CREATE_NO_WINDOW, cwd=get_dir(file), if os.path.exists(file + ".d"):
check=True, stdout=subprocess.PIPE) shutil.rmtree(file + ".d")
if os.path.exists(file + ".d"): shutil.rmtree(file + ".d")
self.gui.progress(statut=self.gui.translate("Patch main.dol"), add=1) self.gui.progress(statut=self.gui.translate("Patch main.dol"), add=1)
subprocess.run(["./tools/szs/wstrt", "patch", get_nodir(self.path) + "/sys/main.dol", "--clean-dol", wszst.str_patch(self.path)
"--add-lecode"], creationflags=CREATE_NO_WINDOW, cwd=get_dir(self.path),
check=True, stdout=subprocess.PIPE)
self.gui.progress(statut=self.gui.translate("Patch lecode.bin"), add=1) self.gui.progress(statut=self.gui.translate("Patch lecode.bin"), add=1)
shutil.copytree("./file/Track/", self.path + "/files/Race/Course/", dirs_exist_ok=True) 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/") if not (os.path.exists(self.path + "/tmp/")): os.makedirs(self.path + "/tmp/")
filecopy("./file/CTFILE.txt", self.path + "/tmp/CTFILE.txt") filecopy("./file/CTFILE.txt", self.path + "/tmp/CTFILE.txt")
filecopy("./file/lpar-default.txt", self.path + "/tmp/lpar-default.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") filecopy(f"./file/lecode-{self.region}.bin", self.path + f"/tmp/lecode-{self.region}.bin")
subprocess.run( wszst.lec_patch(
["./tools/szs/wlect", "patch", f"./tmp/lecode-{self.region}.bin", "-od", self.path,
f"./files/rel/lecode-{self.region}.bin", "--track-dir", "./files/Race/Course/", lecode_file=f"./tmp/lecode-{self.region}.bin",
"--move-tracks", "./files/Race/Course/", "--le-define", "./tmp/CTFILE.txt", "--lpar", dest_lecode_file=f"./files/rel/lecode-{self.region}.bin",
"./tmp/lpar-default.txt", "--overwrite"], )
creationflags=CREATE_NO_WINDOW, cwd=self.path, check=True, stdout=subprocess.PIPE)
shutil.rmtree(self.path + "/tmp/") shutil.rmtree(self.path + "/tmp/")
output_format = self.gui.stringvar_game_format.get() output_format = self.gui.stringvar_game_format.get()
self.gui.progress(statut=self.gui.translate("Converting to", " ", output_format), add=1) self.gui.progress(statut=self.gui.translate("Converting to", " ", output_format), add=1)
self.convert_to(output_format) self.convert_to(output_format)
messagebox.showinfo(self.gui.translate("End"), self.gui.translate("The mod has been installed !")) messagebox.showinfo(self.gui.translate("End"), self.gui.translate("The mod has been installed !"))
except: except:
self.gui.log_error() self.gui.log_error()
finally: finally:
self.gui.progress(show=False) self.gui.progress(show=False)
t = Thread(target=func)
t.setDaemon(True)
t.start()
return t
def patch_autoadd(self, auto_add_dir: str = "./file/auto-add"): def patch_autoadd(self, auto_add_dir: str = "./file/auto-add"):
if os.path.exists(auto_add_dir): shutil.rmtree(auto_add_dir) if os.path.exists(auto_add_dir): shutil.rmtree(auto_add_dir)
if not os.path.exists(self.path + "/tmp/"): os.makedirs(self.path + "/tmp/") if not os.path.exists(self.path + "/tmp/"): os.makedirs(self.path + "/tmp/")
subprocess.run(["./tools/szs/wszst", "AUTOADD", get_nodir(self.path) + "/files/Race/Course/", wszst.autoadd(self.path, get_nodir(self.path) + "/tmp/auto-add/")
"--DEST", get_nodir(self.path) + "/tmp/auto-add/"],
creationflags=CREATE_NO_WINDOW, cwd=get_dir(self.path),
check=True, stdout=subprocess.PIPE)
shutil.move(self.path + "/tmp/auto-add/", auto_add_dir) shutil.move(self.path + "/tmp/auto-add/", auto_add_dir)
shutil.rmtree(self.path + "/tmp/") shutil.rmtree(self.path + "/tmp/")
def patch_bmg(self, gamefile: str): # gamefile est le fichier .szs trouvé dans le /files/Scene/UI/ du jeu def patch_bmg(self, gamefile: str): # gamefile est le fichier .szs trouvé dans le /files/Scene/UI/ du jeu
NINTENDO_CWF_REPLACE = "Wiimmfi" NINTENDO_CWF_REPLACE = "Wiimmfi"
MAINMENU_REPLACE = f"MKWFaraphel {self.gui.ctconfig.version}" MAINMENU_REPLACE = f"MKWFaraphel {self.ctconfig.version}"
menu_replacement = { menu_replacement = {
"CWF de Nintendo": NINTENDO_CWF_REPLACE, "CWF de Nintendo": NINTENDO_CWF_REPLACE,
"Wi-Fi Nintendo": NINTENDO_CWF_REPLACE, "Wi-Fi Nintendo": NINTENDO_CWF_REPLACE,
@ -245,18 +233,11 @@ class Game:
bmglang = gamefile[-len("E.txt"):-len(".txt")] # Langue du fichier bmglang = gamefile[-len("E.txt"):-len(".txt")] # Langue du fichier
self.gui.progress(statut=self.gui.translate("Patching text", " ", bmglang), add=1) self.gui.progress(statut=self.gui.translate("Patching text", " ", bmglang), add=1)
subprocess.run(["./tools/szs/wszst", "EXTRACT", get_nodir(gamefile), "-d", get_nodir(gamefile) + ".d", wszst.szs_extract(gamefile, get_nodir(gamefile))
"--overwrite"], creationflags=CREATE_NO_WINDOW, cwd=get_dir(gamefile))
# Menu.bmg bmgmenu = wszst.bmg_cat(gamefile, ".d/message/Menu.bmg") # Menu.bmg
bmgmenu = subprocess.run(["./tools/szs/wbmgt", "CAT", get_nodir(gamefile) + ".d/message/Menu.bmg"], bmgtracks = wszst.bmg_cat(gamefile, ".d/message/Common.bmg") # Common.bmg
creationflags=CREATE_NO_WINDOW, cwd=get_dir(gamefile),
check=True, stdout=subprocess.PIPE).stdout.decode()
# Common.bmg
bmgtracks = subprocess.run(["./tools/szs/wbmgt", "CAT", get_nodir(gamefile) + ".d/message/Common.bmg"],
creationflags=CREATE_NO_WINDOW, cwd=get_dir(gamefile),
check=True, stdout=subprocess.PIPE).stdout.decode()
trackheader = "#--- standard track names" trackheader = "#--- standard track names"
trackend = "2328" trackend = "2328"
bmgtracks = bmgtracks[bmgtracks.find(trackheader) + len(trackheader):bmgtracks.find(trackend)] bmgtracks = bmgtracks[bmgtracks.find(trackheader) + len(trackheader):bmgtracks.find(trackend)]
@ -290,14 +271,10 @@ class Game:
if not (os.path.exists("./file/tmp/")): os.makedirs("./file/tmp/") if not (os.path.exists("./file/tmp/")): os.makedirs("./file/tmp/")
filecopy(gamefile + ".d/message/Common.bmg", "./file/tmp/Common.bmg") filecopy(gamefile + ".d/message/Common.bmg", "./file/tmp/Common.bmg")
bmgcommon = subprocess.run( bmgcommon = wszst.ctc_patch_bmg(ctfile="./file/CTFILE.txt",
["tools/szs/wctct", "bmg", "--le-code", "--long", "./file/CTFILE.txt", "--patch-bmg", bmgs=["./file/tmp/Common.bmg", "./file/ExtraCommon.txt"])
"OVERWRITE=./file/tmp/Common.bmg", "--patch-bmg", "OVERWRITE=./file/ExtraCommon.txt"], rbmgcommon = wszst.ctc_patch_bmg(ctfile="./file/RCTFILE.txt",
creationflags=CREATE_NO_WINDOW, check=True, stdout=subprocess.PIPE).stdout.decode() bmgs=["./file/tmp/Common.bmg", "./file/ExtraCommon.txt"])
rbmgcommon = subprocess.run(
["tools/szs/wctct", "bmg", "--le-code", "--long", "./file/RCTFILE.txt", "--patch-bmg",
"OVERWRITE=./file/tmp/Common.bmg", "--patch-bmg", "OVERWRITE=./file/ExtraCommon.txt"],
creationflags=CREATE_NO_WINDOW, check=True, stdout=subprocess.PIPE).stdout.decode()
shutil.rmtree(gamefile + ".d") shutil.rmtree(gamefile + ".d")
os.remove("./file/tmp/Common.bmg") os.remove("./file/tmp/Common.bmg")
@ -308,59 +285,52 @@ class Game:
for text, colored_text in replacement_list.items(): bmgtext = bmgtext.replace(text, colored_text) for text, colored_text in replacement_list.items(): bmgtext = bmgtext.replace(text, colored_text)
with open(file, "w", encoding="utf-8") as f: with open(file, "w", encoding="utf-8") as f:
f.write(bmgtext) f.write(bmgtext)
subprocess.run(["./tools/szs/wbmgt", "ENCODE", get_nodir(file), "--overwrite"], wszst.bmg_encode(file)
creationflags=CREATE_NO_WINDOW, cwd=get_dir(file))
os.remove(file) os.remove(file)
finalise(f"./file/Menu_{bmglang}.txt", bmgmenu, menu_replacement) finalise(f"./file/Menu_{bmglang}.txt", bmgmenu, menu_replacement)
finalise(f"./file/Common_{bmglang}.txt", bmgcommon) finalise(f"./file/Common_{bmglang}.txt", bmgcommon)
finalise(f"./file/Common_R{bmglang}.txt", rbmgcommon) finalise(f"./file/Common_R{bmglang}.txt", rbmgcommon)
@in_thread
def patch_file(self): def patch_file(self):
def func(): try:
try: if not (os.path.exists("./file/Track-WU8/")): os.makedirs("./file/Track-WU8/")
if not (os.path.exists("./file/Track-WU8/")): os.makedirs("./file/Track-WU8/") with open("./convert_file.json") as f:
with open("./convert_file.json") as f: fc = json.load(f)
fc = json.load(f) max_step = len(fc["img"]) + len(self.ctconfig.all_tracks) + 3 + len("EGFIS")
max_step = len(fc["img"]) + len(self.gui.ctconfig.all_tracks) + 3 + len("EGFIS")
self.gui.progress(show=True, indeter=False, statut=self.gui.translate("Converting files"), max=max_step, self.gui.progress(show=True, indeter=False, statut=self.gui.translate("Converting files"),
step=0) max=max_step, step=0)
self.gui.progress(statut=self.gui.translate("Configurating LE-CODE"), add=1) self.gui.progress(statut=self.gui.translate("Configurating LE-CODE"), add=1)
self.gui.ctconfig.create_ctfile() self.ctconfig.create_ctfile()
self.gui.progress(statut=self.gui.translate("Creating ct_icon.png"), add=1) self.gui.progress(statut=self.gui.translate("Creating ct_icon.png"), add=1)
ct_icon = self.gui.ctconfig.get_cticon() ct_icon = self.ctconfig.get_cticon()
ct_icon.save("./file/ct_icons.tpl.png") ct_icon.save("./file/ct_icons.tpl.png")
self.gui.progress(statut=self.gui.translate("Creating descriptive images"), add=1) self.gui.progress(statut=self.gui.translate("Creating descriptive images"), add=1)
self.patch_img_desc() self.patch_img_desc()
self.patch_image(fc) self.patch_image(fc)
for file in glob.glob(self.path + "/files/Scene/UI/MenuSingle_?.szs"): self.patch_bmg(file) for file in glob.glob(self.path + "/files/Scene/UI/MenuSingle_?.szs"): self.patch_bmg(file)
# MenuSingle could be any other file, Common and Menu are all the same in all other files. # MenuSingle could be any other file, Common and Menu are all the same in all other files.
self.patch_autoadd() self.patch_autoadd()
if self.patch_tracks() != 0: return if self.patch_tracks() != 0: return
self.gui.button_install_mod.grid(row=2, column=1, columnspan=2, sticky="NEWS") self.gui.button_install_mod.grid(row=2, column=1, columnspan=2, sticky="NEWS")
self.gui.button_install_mod.config( self.gui.button_install_mod.config(
text=self.gui.translate("Install mod", " (v", self.gui.ctconfig.version, ")")) text=self.gui.translate("Install mod", " (v", self.ctconfig.version, ")"))
except: except:
self.gui.log_error() self.gui.log_error()
finally: finally:
self.gui.progress(show=False) self.gui.progress(show=False)
t = Thread(target=func)
t.setDaemon(True)
t.start()
return t
def patch_image(self, fc): def patch_image(self, fc):
for i, file in enumerate(fc["img"]): for i, file in enumerate(fc["img"]):
self.gui.progress(statut=self.gui.translate("Converting images") + f"\n({i + 1}/{len(fc['img'])}) {file}", self.gui.progress(statut=self.gui.translate("Converting images") + f"\n({i + 1}/{len(fc['img'])}) {file}",
add=1) add=1)
subprocess.run(["./tools/szs/wimgt", "ENCODE", "./file/" + file, "-x", fc["img"][file], "--overwrite"], wszst.img_encode("./file/" + file, fc["img"][file])
creationflags=CREATE_NO_WINDOW, check=True, stdout=subprocess.PIPE)
def patch_img_desc(self, img_desc_path: str = "./file/img_desc", dest_dir: str = "./file"): def patch_img_desc(self, img_desc_path: str = "./file/img_desc", dest_dir: str = "./file"):
il = Image.open(img_desc_path + "/illustration.png") il = Image.open(img_desc_path + "/illustration.png")
@ -390,7 +360,7 @@ class Game:
def add_process(track): def add_process(track):
nonlocal error_count, error_max, process_list nonlocal error_count, error_max, process_list
track_file = track.get_track_name() track_file = track.get_track_name()
total_track = len(self.gui.ctconfig.all_tracks) total_track = len(self.ctconfig.all_tracks)
process_list[track_file] = None # Used for showing track in progress even if there's no process process_list[track_file] = None # Used for showing track in progress even if there's no process
self.gui.progress(statut=self.gui.translate("Converting tracks", f"\n({i + 1}/{total_track})\n", self.gui.progress(statut=self.gui.translate("Converting tracks", f"\n({i + 1}/{total_track})\n",
@ -407,15 +377,14 @@ class Game:
if download_returncode == -1: # can't download if download_returncode == -1: # can't download
error_count += 1 error_count += 1
if error_count > error_max: # Too much track wasn't correctly converted if error_count > error_max: # Too much track wasn't correctly converted
"""messagebox.showerror( messagebox.showerror(
gui.translate("Error"), self.gui.translate("Error"),
gui.translate("Too much tracks had a download issue.")) self.gui.translate("Too much tracks had a download issue."))
return -1"""
raise TooMuchDownloadFailed() raise TooMuchDownloadFailed()
else: else:
"""messagebox.showwarning(gui.translate("Warning"), messagebox.showwarning(self.gui.translate("Warning"),
gui.translate("Can't download this track !", self.gui.translate("Can't download this track !",
f" ({error_count} / {error_max})"))""" f" ({error_count} / {error_max})"))
elif download_returncode == 2: elif download_returncode == 2:
break # if download is disabled, do not check sha1 break # if download is disabled, do not check sha1
@ -424,22 +393,21 @@ class Game:
if not track.check_sha1(): # Check si le sha1 du fichier est le bon if not track.check_sha1(): # Check si le sha1 du fichier est le bon
error_count += 1 error_count += 1
if error_count > error_max: # Too much track wasn't correctly converted if error_count > error_max: # Too much track wasn't correctly converted
"""messagebox.showerror( messagebox.showerror(
gui.translate("Error"), self.gui.translate("Error"),
gui.translate("Too much tracks had an issue during sha1 check."))""" self.gui.translate("Too much tracks had an issue during sha1 check."))
raise TooMuchSha1CheckFailed() raise TooMuchSha1CheckFailed()
continue continue
break break
if not ( if not (os.path.exists(track.file_szs)) or download_returncode == 3:
os.path.exists( # returncode 3 is track has been updated
track.file_szs)) or download_returncode == 3: # returncode 3 is track has been updated
if os.path.exists(track.file_wu8): if os.path.exists(track.file_wu8):
process_list[track_file] = track.convert_wu8_to_szs() process_list[track_file] = track.convert_wu8_to_szs()
else: else:
"""messagebox.showerror(gui.translate("Error"), messagebox.showerror(self.gui.translate("Error"),
gui.translate("Can't convert track.\nEnable track download and retry."))""" self.gui.translate("Can't convert track.\nEnable track download and retry."))
raise CantConvertTrack() raise CantConvertTrack()
elif self.gui.boolvar_del_track_after_conv.get(): elif self.gui.boolvar_del_track_after_conv.get():
os.remove(track.file_wu8) os.remove(track.file_wu8)
@ -459,28 +427,28 @@ class Game:
os.remove(track.file_szs) os.remove(track.file_szs)
error_count += 1 error_count += 1
if error_count > error_max: # Too much track wasn't correctly converted if error_count > error_max: # Too much track wasn't correctly converted
"""messagebox.showerror( messagebox.showerror(
gui.translate("Error"), self.gui.translate("Error"),
gui.translate("Too much track had a conversion issue."))""" self.gui.translate("Too much track had a conversion issue."))
raise CantConvertTrack raise CantConvertTrack()
else: # if the error max hasn't been reach else: # if the error max hasn't been reach
"""messagebox.showwarning( messagebox.showwarning(
gui.translate("Warning"), self.gui.translate("Warning"),
gui.translate("The track", " ", track.file_wu8, self.gui.translate("The track", " ", track.file_wu8,
"do not have been properly converted.", "do not have been properly converted.",
f" ({error_count} / {error_max})"))""" f" ({error_count} / {error_max})"))
else: else:
if self.gui.boolvar_del_track_after_conv.get(): os.remove(track.file_wu8) if self.gui.boolvar_del_track_after_conv.get(): os.remove(track.file_wu8)
else: else:
process_list.pop(track_file) process_list.pop(track_file)
if not (any(process_list.values())): return 1 # si il n'y a plus de processus if not (any(process_list.values())): return 1 # if there is no more process
if len(process_list): if len(process_list):
return 1 return 1
else: else:
return 0 return 0
for i, track in enumerate(self.gui.ctconfig.all_tracks): for i, track in enumerate(self.ctconfig.all_tracks):
while True: while True:
if len(process_list) < max_process: if len(process_list) < max_process:
returncode = add_process(track) returncode = add_process(track)

View file

@ -1,10 +1,10 @@
from tkinter import filedialog, ttk from tkinter import filedialog, ttk
from tkinter import * from tkinter import *
import zipfile import subprocess
import traceback import traceback
import requests import requests
import zipfile
from source.CT_Config import CT_Config
from source.Option import Option from source.Option import Option
from source.Game import * from source.Game import *
@ -24,8 +24,8 @@ class Gui:
self.option = Option() self.option = Option()
self.option.load_from_file("./option.json") self.option.load_from_file("./option.json")
self.ctconfig = CT_Config() self.game = Game(gui=self)
self.ctconfig.load_ctconfig_file("./ct_config.json") self.game.ctconfig.load_ctconfig_file("./ct_config.json")
self.is_dev_version = False # Is this installer version a dev ? self.is_dev_version = False # Is this installer version a dev ?
self.stringvar_language = StringVar(value=self.option.language) self.stringvar_language = StringVar(value=self.option.language)
@ -70,7 +70,7 @@ class Gui:
self.menu_marktrackversion = Menu(self.menu_trackselection, tearoff=0) self.menu_marktrackversion = Menu(self.menu_trackselection, tearoff=0)
self.menu_trackselection.add_cascade(label=self.translate("Mark all tracks from version"), menu=self.menu_marktrackversion) self.menu_trackselection.add_cascade(label=self.translate("Mark all tracks from version"), menu=self.menu_marktrackversion)
self.menu_marktrackversion.add_radiobutton(label=self.translate("None"), variable=self.stringvar_mark_track_from_version, value="None") self.menu_marktrackversion.add_radiobutton(label=self.translate("None"), variable=self.stringvar_mark_track_from_version, value="None")
for version in self.ctconfig.all_version: for version in self.game.ctconfig.all_version:
self.menu_marktrackversion.add_radiobutton(label=f"v{version}", variable=self.stringvar_mark_track_from_version, value=version) self.menu_marktrackversion.add_radiobutton(label=f"v{version}", variable=self.stringvar_mark_track_from_version, value=version)
self.menu_advanced = Menu(self.menu_bar, tearoff=0) self.menu_advanced = Menu(self.menu_bar, tearoff=0)
@ -109,53 +109,48 @@ class Gui:
self.frame_game_path_action.grid(row=2, column=1, columnspan=2, sticky="NEWS") self.frame_game_path_action.grid(row=2, column=1, columnspan=2, sticky="NEWS")
self.frame_game_path_action.columnconfigure(1, weight=1) self.frame_game_path_action.columnconfigure(1, weight=1)
@in_thread
def use_path(): def use_path():
def func(): self.frame_action.grid_forget()
self.frame_action.grid_forget() try:
try: self.game.set_path(entry_game_path.get())
self.game = Game(path=entry_game_path.get(), gui=self) self.progress(show=True, indeter=True, statut=self.translate("Extracting the game..."))
self.progress(show=True, indeter=True, statut=self.translate("Extracting the game...")) self.game.extract()
self.game.extract() self.frame_action.grid(row=3, column=1, sticky="NEWS")
self.frame_action.grid(row=3, column=1, sticky="NEWS") except RomAlreadyPatched:
except InvalidGamePath: messagebox.showerror(self.translate("Error"), self.translate("This game is already modded"))
messagebox.showerror(self.translate("Error"), self.translate("The file path in invalid")) except InvalidGamePath:
except InvalidFormat: messagebox.showerror(self.translate("Error"), self.translate("The file path in invalid"))
messagebox.showerror(self.translate("Error"), self.translate("This game's format is invalid")) except InvalidFormat:
except: messagebox.showerror(self.translate("Error"), self.translate("This game's format is invalid"))
self.log_error() except:
finally: self.log_error()
self.progress(show=False) finally:
self.progress(show=False)
t = Thread(target=func)
t.setDaemon(True)
t.start()
return t
self.button_game_extract = Button(self.frame_game_path_action, text=self.translate("Extract file"), self.button_game_extract = Button(self.frame_game_path_action, text=self.translate("Extract file"),
relief=RIDGE, command=use_path) relief=RIDGE, command=use_path)
self.button_game_extract.grid(row=1, column=1, sticky="NEWS") self.button_game_extract.grid(row=1, column=1, sticky="NEWS")
@in_thread
def do_everything(): def do_everything():
def func(): use_path().join()
use_path().join() self.game.patch_file().join()
self.game.patch_file(self).join() self.game.install_mod().join()
self.game.install_mod(self).join()
if messagebox.askyesno(self.translate("Experimental functionality"), if messagebox.askyesno(self.translate("Experimental functionality"),
self.translate("This will extract the selected ROM, prepare files and install mod. " self.translate("This will extract the selected ROM, prepare files and install mod. "
"Do you wish to continue ?")): "Do you wish to continue ?")):
t = Thread(target=func) do_everything()
t.setDaemon(True)
t.start()
self.button_do_everything = Button(self.frame_game_path_action, text=self.translate("Do everything"), relief=RIDGE, command=do_everything) self.button_do_everything = Button(self.frame_game_path_action, text=self.translate("Do everything"), relief=RIDGE, command=do_everything)
self.button_do_everything.grid(row=1, column=2, sticky="NEWS") self.button_do_everything.grid(row=1, column=2, sticky="NEWS")
self.frame_action = LabelFrame(self.root, text=self.translate("Action")) self.frame_action = LabelFrame(self.root, text=self.translate("Action"))
self.button_prepare_file = Button(self.frame_action, text=self.translate("Prepare files"), relief=RIDGE, command=lambda: self.game.patch_file(self), width=45) self.button_prepare_file = Button(self.frame_action, text=self.translate("Prepare files"), relief=RIDGE, command=lambda: self.game.patch_file(), width=45)
self.button_prepare_file.grid(row=1, column=1, columnspan=2, sticky="NEWS") self.button_prepare_file.grid(row=1, column=1, columnspan=2, sticky="NEWS")
self.button_install_mod = Button(self.frame_action, text=self.translate("Install mod"), relief=RIDGE, command=lambda: self.game.install_mod(self), width=45) self.button_install_mod = Button(self.frame_action, text=self.translate("Install mod"), relief=RIDGE, command=lambda: self.game.install_mod(), width=45)
# Install mod button will only appear after prepare file step # Install mod button will only appear after prepare file step
self.progressbar = ttk.Progressbar(self.root) self.progressbar = ttk.Progressbar(self.root)

View file

@ -1,3 +1,5 @@
from threading import Thread
CREATE_NO_WINDOW = 0x08000000 CREATE_NO_WINDOW = 0x08000000
GITHUB_REPOSITORY = "Faraphel/MKWF-Install" GITHUB_REPOSITORY = "Faraphel/MKWF-Install"
GITHUB_MASTER_BRANCH = f"https://raw.githubusercontent.com/{GITHUB_REPOSITORY}/master/" GITHUB_MASTER_BRANCH = f"https://raw.githubusercontent.com/{GITHUB_REPOSITORY}/master/"
@ -94,7 +96,18 @@ region_id_to_name = {
"E": "USA" "E": "USA"
} }
def filecopy(src, dst): def filecopy(src, dst):
with open(src, "rb") as f1: with open(src, "rb") as f1:
with open(dst, "wb") as f2: with open(dst, "wb") as f2:
f2.write(f1.read()) # could be buffered f2.write(f1.read()) # could be buffered
def in_thread(func):
def wrapped_func(*args, **kwargs):
thread = Thread(target=func, args=args, kwargs=kwargs)
thread.setDaemon(True)
thread.start()
return thread
return wrapped_func

View file

@ -31,7 +31,7 @@ def normalize(src_file: str, dest_dir: str = "./file/Track/", dest_name: str = "
autoadd_path], creationflags=CREATE_NO_WINDOW, stderr=subprocess.PIPE) autoadd_path], creationflags=CREATE_NO_WINDOW, stderr=subprocess.PIPE)
def extract(file: str, dest_dir: str): def wit_extract(file: str, dest_dir: str):
""" """
:param file: game's file to extract (can be WBFS, ISO, CISO) :param file: game's file to extract (can be WBFS, ISO, CISO)
:param dest_dir: where to extract the game :param dest_dir: where to extract the game
@ -42,35 +42,79 @@ def extract(file: str, dest_dir: str):
def create(file: str): def create(file: str):
pass """
: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 wstrt_patch(file: str): def str_patch(file: str):
pass subprocess.run(["./tools/szs/wstrt", "patch", get_nodir(file) + "/sys/main.dol", "--clean-dol",
"--add-lecode"], creationflags=CREATE_NO_WINDOW, cwd=get_dir(file),
check=True, stdout=subprocess.PIPE)
def wlect_patch(file: str, def lec_patch(file: str,
lecode_file: str = f"./files/rel/lecode-PAL.bin", lecode_file: str = f"./tmp/lecode-PAL.bin",
game_track_path: str = "./files/Race/Course/", dest_lecode_file: str = f"./files/rel/lecode-PAL.bin",
move_track_path: str = "./files/Race/Course/", game_track_path: str = "./files/Race/Course/",
ctfile_path: str = "./tmp/CTFILE.txt", move_track_path: str = "./files/Race/Course/",
lpar_path: str = "./tmp/lpar-default.txt"): ctfile_path: str = "./tmp/CTFILE.txt",
pass lpar_path: str = "./tmp/lpar-default.txt"):
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)
def edit(): pass def edit(file, region_ID: str = "P", name: str = "Mario Kart Wii"):
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(): pass def autoadd(file: str, dest_dir: str):
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_decode(): pass def bmg_encode(file: str):
subprocess.run(["./tools/szs/wbmgt", "ENCODE", get_nodir(file), "--overwrite"],
creationflags=CREATE_NO_WINDOW, cwd=get_dir(file))
def bmg_encode(): pass def bmg_cat(path: str, subfile: str = ".d/message/Common.bmg"):
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 wctct_bmg(): pass def wit_copy(src_path, dst_path, format: str = "ISO"):
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),
check=True, stdout=subprocess.PIPE)
def wit_copy(): pass def ctc_patch_bmg(bmgs: list, ctfile: str = "./file/CTFILE.txt"):
bmg_cmd = []
for bmg in bmgs: bmg_cmd.extend(["--patch-bmg", f"OVERWRITE={bmg}"])
return subprocess.run(
["tools/szs/wctct", "bmg", "--le-code", "--long", ctfile, *bmg_cmd],
creationflags=CREATE_NO_WINDOW, check=True, stdout=subprocess.PIPE).stdout.decode()
def img_encode(src_file, 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):
subprocess.run(["./tools/szs/wszst", "EXTRACT", get_nodir(file), "--DEST", dest_dir+".d"],
creationflags=CREATE_NO_WINDOW, cwd=get_dir(file))

View file

@ -19,7 +19,7 @@
"This directory will be overwritten if you install the mod !\nAre you sure you want to use it ?": "Ce dossier sera écrasé si vous installer le mod !\nÊtes-vous sûr de vouloir l'utiliser ?", "This directory will be overwritten if you install the mod !\nAre you sure you want to use it ?": "Ce dossier sera écrasé si vous installer le mod !\nÊtes-vous sûr de vouloir l'utiliser ?",
"Extracting the game...": "Extraction du jeu...", "Extracting the game...": "Extraction du jeu...",
"This file type is not supported": "Le type de fichier n'est pas reconnu", "This file type is not supported": "Le type de fichier n'est pas reconnu",
"This game is already modded, it is not recommended to use it to install the mod": "Cette ROM est déjà moddé, il est déconseillé de l'utiliser pour installer le mod", "This game is already modded": "Cette ROM est déjà moddé",
"Can't find game region.\nPAL region will be used.": "Impossible de trouver la région de votre jeu.\nla région PAL sera utilisé par défaut.", "Can't find game region.\nPAL region will be used.": "Impossible de trouver la région de votre jeu.\nla région PAL sera utilisé par défaut.",
"Extract file": "Extraire le fichier", "Extract file": "Extraire le fichier",
"Experimental functionality": "Fonctionnalité expérimentale", "Experimental functionality": "Fonctionnalité expérimentale",