Merge pull request #19 from Faraphel/dev

v0.7
This commit is contained in:
Faraphel 2021-06-19 15:12:35 +02:00 committed by GitHub
commit 4cafdc3d53
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 379 additions and 149 deletions

Binary file not shown.

File diff suppressed because one or more lines are too long

View file

@ -16,7 +16,7 @@ from source.definition import *
class ClassApp(): class ClassApp():
from source.__init__ import __init__ from source.__init__ import __init__
from source.translate import translate, change_language, get_language from source.translate import translate
from source.Progress import Progress from source.Progress import Progress
from source.check_update import check_update from source.check_update import check_update
from source.StateButton import StateButton from source.StateButton import StateButton
@ -28,6 +28,10 @@ class ClassApp():
from source.patch_img_desc import patch_img_desc from source.patch_img_desc import patch_img_desc
from source.patch_ct_icon import patch_ct_icon from source.patch_ct_icon import patch_ct_icon
from source.log_error import log_error from source.log_error import log_error
from source.get_github_file import get_github_file
from source.patch_track import count_track, patch_track, patch_autoadd
from source.patch_image import patch_image
from source.option import load_option, change_option
App = ClassApp() App = ClassApp()

1
option.json Normal file
View file

@ -0,0 +1 @@
{"language": "en", "format": "FST", "disable_download": false, "del_track_after_conv": false, "dont_check_for_update": false, "process_track": 8}

View file

@ -15,7 +15,7 @@ include_files = [
"./tools", "./tools",
"./source", "./source",
("./file/Track-WU8", "./file/Track-WU8"), # ("./file/Track-WU8", "./file/Track-WU8"),
("./file/cup_icon", "./file/cup_icon"), ("./file/cup_icon", "./file/cup_icon"),
("./file/img_desc", "./file/img_desc"), ("./file/img_desc", "./file/img_desc"),
("./file/video.thp", "./file/video.thp"), ("./file/video.thp", "./file/video.thp"),

View file

@ -11,27 +11,56 @@ from .translate import translate
def __init__(self): def __init__(self):
try: try:
self.language = self.get_language()
self.root = Tk() self.root = Tk()
self.load_option()
self.stringvar_language = StringVar(value=self.option["language"])
self.stringvar_game_format = StringVar(value=self.option["format"])
self.boolvar_disable_download = BooleanVar(value=self.option["disable_download"])
self.boolvar_del_track_after_conv = BooleanVar(value=self.option["del_track_after_conv"])
self.boolvar_dont_check_for_update = BooleanVar(value=self.option["dont_check_for_update"])
self.intvar_process_track = IntVar(value=self.option["process_track"])
self.root.title(self.translate("MKWFaraphel Installateur")) self.root.title(self.translate("MKWFaraphel Installateur"))
self.root.resizable(False, False) self.root.resizable(False, False)
self.root.iconbitmap(bitmap="./icon.ico") self.root.iconbitmap(bitmap="./icon.ico")
self.check_update() if not(self.boolvar_dont_check_for_update.get()): self.check_update()
self.path_mkwf = None self.path_mkwf = None
self.menu_bar = Menu(self.root)
self.root.config(menu=self.menu_bar)
self.menu_language = Menu(self.menu_bar, tearoff=0)
self.menu_bar.add_cascade(label=self.translate("Langage"), menu=self.menu_language)
self.menu_language.add_radiobutton(label="Français", variable=self.stringvar_language, value="fr", command=lambda: self.change_option("language", "fr", restart=True))
self.menu_language.add_radiobutton(label="English", variable=self.stringvar_language, value="en", command=lambda: self.change_option("language", "en", restart=True))
self.menu_format = Menu(self.menu_bar, tearoff=0)
self.menu_bar.add_cascade(label=self.translate("Format"), menu=self.menu_format)
self.menu_format.add_radiobutton(label=self.translate("FST (Dossier)"), variable=self.stringvar_game_format, value="FST", command=lambda: self.change_option("format", "FST"))
self.menu_format.add_radiobutton(label="ISO", variable=self.stringvar_game_format, value="ISO", command=lambda: self.change_option("format", "ISO"))
self.menu_format.add_radiobutton(label="CISO", variable=self.stringvar_game_format, value="CISO", command=lambda: self.change_option("format", "CISO"))
self.menu_format.add_radiobutton(label="WBFS", variable=self.stringvar_game_format, value="WBFS", command=lambda: self.change_option("format", "WBFS"))
self.menu_advanced = Menu(self.menu_bar, tearoff=0)
self.menu_bar.add_cascade(label=self.translate("Avancé"), menu=self.menu_advanced)
self.menu_advanced.add_checkbutton(label=self.translate("Désactiver les téléchargements"), variable=self.boolvar_disable_download, command=lambda: self.change_option("disable_download", self.boolvar_disable_download))
self.menu_advanced.add_checkbutton(label=self.translate("Supprimer les courses wu8 après conversion en szs"), variable=self.boolvar_del_track_after_conv, command=lambda: self.change_option("del_track_after_conv", self.boolvar_del_track_after_conv))
self.menu_advanced.add_checkbutton(label=self.translate("Ne pas vérifier les mises à jour"), variable=self.boolvar_dont_check_for_update, command=lambda: self.change_option("dont_check_for_update", self.boolvar_dont_check_for_update))
self.menu_advanced.add_separator()
self.menu_advanced.add_command(label=self.translate("Nombre de processus de conversion de course :"))
self.menu_advanced.add_radiobutton(label=self.translate("1 processus"), variable=self.intvar_process_track, value=1, command=lambda: self.change_option("process_track", 1))
self.menu_advanced.add_radiobutton(label=self.translate("2 processus"), variable=self.intvar_process_track, value=2, command=lambda: self.change_option("process_track", 2))
self.menu_advanced.add_radiobutton(label=self.translate("4 processus"), variable=self.intvar_process_track, value=4, command=lambda: self.change_option("process_track", 4))
self.menu_advanced.add_radiobutton(label=self.translate("8 processus"), variable=self.intvar_process_track, value=8, command=lambda: self.change_option("process_track", 8))
self.frame_language = Frame(self.root) self.frame_language = Frame(self.root)
self.frame_language.grid(row=1, column=1, sticky="E") self.frame_language.grid(row=1, column=1, sticky="E")
Label(self.frame_language, text=self.translate("Langage : ")).grid(row=1, column=1)
self.listbox_language = ttk.Combobox(self.frame_language, values=["fr", "en"], width=5)
self.listbox_language.set(self.language)
self.listbox_language.grid(row=1, column=2)
self.listbox_language.bind("<<ComboboxSelected>>", lambda x: self.change_language())
self.frame_game_path = LabelFrame(self.root, text=self.translate("Jeu original")) self.frame_game_path = LabelFrame(self.root, text=self.translate("Jeu original"))
self.frame_game_path.grid(row=2, column=1) self.frame_game_path.grid(row=2, column=1)
@ -67,7 +96,7 @@ def __init__(self):
"Êtes-vous sûr de vouloir l'utiliser ?")): "Êtes-vous sûr de vouloir l'utiliser ?")):
self.path_mkwf = os.path.realpath(path + "/../../") self.path_mkwf = os.path.realpath(path + "/../../")
else: return else: return
elif extension.upper() in ["ISO", "WBFS", "WIA", "CSIO"]: elif extension.upper() in ["ISO", "WBFS", "CSIO"]:
# Fiding a directory name that dosen't already exist # Fiding a directory name that dosen't already exist
directory_name, i = "MKWiiFaraphel", 1 directory_name, i = "MKWiiFaraphel", 1
while True: while True:
@ -150,10 +179,7 @@ def __init__(self):
command=self.patch_file, width=45) command=self.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("Installer le mod"), relief=RIDGE, self.button_install_mod = Button(self.frame_action, text=self.translate("Installer le mod"), relief=RIDGE,
command=self.install_mod, width=35) command=self.install_mod, width=45)
self.listbox_outputformat = ttk.Combobox(self.frame_action, values=[self.translate("Dossier"),
"ISO", "WBFS", "CISO"], width=5)
self.listbox_outputformat.set(self.translate("Dossier"))
# Le boutton d'installation du mod n'est affiché qu'après avoir préparer les fichiers # Le boutton d'installation du mod n'est affiché qu'après avoir préparer les fichiers
self.progressbar = ttk.Progressbar(self.root) self.progressbar = ttk.Progressbar(self.root)

View file

@ -17,9 +17,11 @@ def check_update(self):
if ((float(gitversion["version"]) > float(locversion["version"])) or if ((float(gitversion["version"]) > float(locversion["version"])) or
(float(gitversion["version"]) == float(locversion["version"])) and (float(gitversion["version"]) == float(locversion["version"])) and
float(gitversion["subversion"]) > float(locversion["subversion"])): float(gitversion["subversion"]) > float(locversion["subversion"])):
if messagebox.askyesno(self.translate("Mise à jour disponible !"), self.translate("Une mise à jour est disponible, souhaitez-vous l'installer ?") + if messagebox.askyesno(
f"\n\nVersion : {locversion['version']}.{locversion['subversion']} -> {gitversion['version']}.{gitversion['subversion']}\n"+\ self.translate("Mise à jour disponible !"),
f"Changelog :\n{gitversion['changelog']}"): self.translate("Une mise à jour est disponible, souhaitez-vous l'installer ?") +
f"\n\nVersion : {locversion['version']}.{locversion['subversion']} -> {gitversion['version']}.{gitversion['subversion']}\n"
f"Changelog :\n{gitversion['changelog']}"):
if not(os.path.exists("./Updater/Updater.exe")): if not(os.path.exists("./Updater/Updater.exe")):
dl = requests.get(gitversion["updater_bin"], allow_redirects=True) dl = requests.get(gitversion["updater_bin"], allow_redirects=True)

View file

@ -1,4 +1,5 @@
import json import json
from .definition import *
def create_lecode_config(self): def create_lecode_config(self):
@ -13,26 +14,19 @@ def create_lecode_config(self):
return "" return ""
def get_ctfile_text(track, race=False): def get_ctfile_text(track, race=False):
track_name = track["name"].replace("_", "")
if "prefix" in track: prefix = f"{track['prefix']} "
else: prefix = ""
if "suffix" in track: suffix = f" ({track['suffix']})"
else: suffix = ""
if race: if race:
return f' T {track["music"]}; ' + \ return f' T {track["music"]}; ' + \
f'{track["special"]}; ' + \ f'{track["special"]}; ' + \
f'{"0x01" if track["new"] else "0x00"}; ' + \ f'{"0x01" if track["new"] else "0x00"}; ' + \
f'"-"; ' + \ f'"-"; ' + \
f'"{get_star_text(track)}{prefix}{track_name}{suffix}\\n{track["author"]}"; ' + \ f'"{get_star_text(track)}{get_trackctname(track=track)}\\n{track["author"]}"; ' + \
f'"-"\n' f'"-"\n'
else: else:
return f' T {track["music"]}; ' + \ return f' T {track["music"]}; ' + \
f'{track["special"]}; ' + \ f'{track["special"]}; ' + \
f'{"0x01" if track["new"] else "0x00"}; ' + \ f'{"0x01" if track["new"] else "0x00"}; ' + \
f'"{prefix}{track["name"]}{suffix}"; ' + \ f'"{get_trackctname(track=track)}"; ' + \
f'"{get_star_text(track)}{prefix}{track_name}{suffix}"; ' + \ f'"{get_star_text(track)}{get_trackctname(track=track)}"; ' + \
f'"-"\n' f'"-"\n'
with open("./ct_config.json", encoding="utf-8") as f: with open("./ct_config.json", encoding="utf-8") as f:

View file

@ -1,12 +1,29 @@
CREATE_NO_WINDOW = 0x08000000
VERSION = "0.8.1"
get_filename = lambda file: ".".join(file.split(".")[:-1]) get_filename = lambda file: ".".join(file.split(".")[:-1])
get_nodir = lambda file: file.replace("\\", "/").split("/")[-1] get_nodir = lambda file: file.replace("\\", "/").split("/")[-1]
get_dir = lambda file: "/".join(file.replace("\\", "/").split("/")[:-1]) get_dir = lambda file: "/".join(file.replace("\\", "/").split("/")[:-1])
get_extension = lambda file: file.split(".")[-1] get_extension = lambda file: file.split(".")[-1]
get_track_wu8 = lambda track: f"./file/Track-WU8/{track}.wu8"
get_track_szs = lambda track: f"./file/Track/{track}.szs"
def get_trackname(name=None, prefix=None, suffix=None, track=None):
if track:
name = track["name"]
if "prefix" in track: prefix = track["prefix"]
if "suffix" in track: suffix = track["suffix"]
if prefix: name = prefix + " " + name
if suffix: name = name + " (" + suffix + ")"
return name
def get_trackctname(name=None, prefix=None, suffix=None, track=None):
return get_trackname(name=name, prefix=prefix, suffix=suffix, track=track).replace("_", "")
CREATE_NO_WINDOW = 0x08000000
VERSION = "0.8.1"
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

29
source/get_github_file.py Normal file
View file

@ -0,0 +1,29 @@
import requests
import os
root = "https://raw.githubusercontent.com/Faraphel/MKWF-Install/master/"
def get_github_file(self, file):
try:
returncode = 0
if self.boolvar_disable_download.get(): return 2
dl = requests.get(root+file, allow_redirects=True, stream=True)
if os.path.exists(file):
if int(dl.headers['Content-Length']) == os.path.getsize(file): return 1
else: returncode = 3
if dl.status_code == 200: # if page is found
with open(file, "wb") as file:
chunk_size = 4096
for i, chunk in enumerate(dl.iter_content(chunk_size=chunk_size)):
file.write(chunk)
file.flush()
return returncode
else:
print(f"error {dl.status_code} {file}")
return -1
except:
self.log_error()
return -1

View file

@ -22,8 +22,7 @@ def install_mod(self):
def count_rf(path): def count_rf(path):
nonlocal max_step nonlocal max_step
max_step += 1 max_step += 1
extension = get_extension(path) if get_extension(path) == "szs":
if extension == "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
@ -106,7 +105,7 @@ def install_mod(self):
shutil.rmtree(self.path_mkwf + "/tmp/") shutil.rmtree(self.path_mkwf + "/tmp/")
outputformat = self.listbox_outputformat.get() outputformat = self.stringvar_game_format.get()
self.Progress(statut=self.translate("Conversion en")+f" {outputformat}", add=1) self.Progress(statut=self.translate("Conversion en")+f" {outputformat}", add=1)
if outputformat in ["ISO", "WBFS", "CISO"]: if outputformat in ["ISO", "WBFS", "CISO"]:

View file

@ -1,6 +1,7 @@
import traceback import traceback
from tkinter import messagebox from tkinter import messagebox
def log_error(self): def log_error(self):
error = traceback.format_exc() error = traceback.format_exc()
with open("./error.log", "a") as f: f.write(f"---\n{error}\n") with open("./error.log", "a") as f: f.write(f"---\n{error}\n")

24
source/option.py Normal file
View file

@ -0,0 +1,24 @@
import json
import os
default_option = {
"language": "en",
"format": "FST",
"disable_download": False,
"del_track_after_conv": False,
"dont_check_for_update": False,
"process_track": 8
}
def change_option(self, option, value, restart=False):
if type(value) in [str, int, bool]: self.option[option] = value
else: self.option[option] = value.get()
with open("./option.json", "w", encoding="utf-8") as f: json.dump(self.option, f, ensure_ascii=False)
if restart: self.restart()
def load_option(self):
if not(os.path.exists("./option.json")):
with open("./option.json", "w", encoding="utf-8") as f: json.dump(default_option, f, ensure_ascii=False)
with open("./option.json", encoding="utf-8") as f: self.option = json.load(f)

View file

@ -88,7 +88,7 @@ trackname_color = {
def patch_bmg(self, gamefile): # gamefile est le fichier .szs trouvé dans le /files/Scene/UI/ du jeu def patch_bmg(self, gamefile): # gamefile est le fichier .szs trouvé dans le /files/Scene/UI/ du jeu
try: try:
bmglang = gamefile[-len("E.txt"):-len(".txt")] # Langue du fichier bmglang = gamefile[-len("E.txt"):-len(".txt")] # Langue du fichier
self.Progress(statut=self.translate("Patch des textes " + bmglang), add=1) self.Progress(statut=self.translate("Patch des textes ") + bmglang, add=1)
subprocess.run(["./tools/szs/wszst", "EXTRACT", get_nodir(gamefile), "-d", get_nodir(gamefile) + ".d", subprocess.run(["./tools/szs/wszst", "EXTRACT", get_nodir(gamefile), "-d", get_nodir(gamefile) + ".d",
"--overwrite"], creationflags=CREATE_NO_WINDOW, cwd=get_dir(gamefile)) "--overwrite"], creationflags=CREATE_NO_WINDOW, cwd=get_dir(gamefile))
@ -96,8 +96,7 @@ def patch_bmg(self, gamefile): # gamefile est le fichier .szs trouvé dans le /
# Common.bmg # Common.bmg
bmgtracks = subprocess.run(["./tools/szs/wbmgt", "CAT", get_nodir(gamefile) + ".d/message/Common.bmg"], bmgtracks = subprocess.run(["./tools/szs/wbmgt", "CAT", get_nodir(gamefile) + ".d/message/Common.bmg"],
creationflags=CREATE_NO_WINDOW, cwd=get_dir(gamefile), creationflags=CREATE_NO_WINDOW, cwd=get_dir(gamefile),
check=True, stdout=subprocess.PIPE).stdout check=True, stdout=subprocess.PIPE).stdout.decode()
bmgtracks = bmgtracks.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)]
@ -116,10 +115,10 @@ def patch_bmg(self, gamefile): # gamefile est le fichier .szs trouvé dans le /
if "T" in bmgtrack[:bmgtrack.find("=")]: if "T" in bmgtrack[:bmgtrack.find("=")]:
sTid = bmgtrack.find("T") sTid = bmgtrack.find("T")
Tid = bmgtrack[sTid:sTid + 3] Tid = bmgtrack[sTid:sTid + 3]
if Tid[1] in "1234": prefix = "Wii " # Si la course est original à la wii if Tid[1] in "1234": prefix = "Wii " # Si la course est original à la wii
Tid = hex(bmgID_track_move[Tid])[2:] Tid = hex(bmgID_track_move[Tid])[2:]
else: # Arena else: # Arena
sTid = bmgtrack.find("U") + 1 sTid = bmgtrack.find("U") + 1
Tid = bmgtrack[sTid:sTid + 2] Tid = bmgtrack[sTid:sTid + 2]
Tid = hex((int(Tid[0]) - 1) * 5 + (int(Tid[1]) - 1) + 0x7020)[2:] Tid = hex((int(Tid[0]) - 1) * 5 + (int(Tid[1]) - 1) + 0x7020)[2:]

View file

@ -1,113 +1,34 @@
from tkinter import messagebox
from threading import Thread from threading import Thread
import subprocess
import shutil
import json import json
import glob import glob
import os import os
from .definition import *
def patch_file(self): def patch_file(self):
def func(): def func():
try: try:
if os.path.exists("./file/Track-WU8/"): if not(os.path.exists("./file/Track-WU8/")): os.makedirs("./file/Track-WU8/")
total_track = len(os.listdir("./file/Track-WU8/")) with open("./convert_file.json") as f: fc = json.load(f)
else: tracks, total_track = self.count_track()
total_track = 0
with open("./convert_file.json") as f:
fc = json.load(f)
max_step = len(fc["img"]) + total_track + 3 + len("EGFIS") max_step = len(fc["img"]) + total_track + 3 + len("EGFIS")
self.Progress(show=True, indeter=False, statut=self.translate("Conversion des fichiers"), max=max_step, step=0)
self.Progress(show=True, indeter=False, statut=self.translate("Conversion des fichiers"), max=max_step, step=0)
self.Progress(statut=self.translate("Configuration de LE-CODE"), add=1) self.Progress(statut=self.translate("Configuration de LE-CODE"), add=1)
self.create_lecode_config() self.create_lecode_config()
self.Progress(statut=self.translate("Création de ct_icon.png"), add=1) self.Progress(statut=self.translate("Création de ct_icon.png"), add=1)
self.patch_ct_icon() self.patch_ct_icon()
self.Progress(statut=self.translate("Création des images descriptives"), add=1) self.Progress(statut=self.translate("Création des images descriptives"), add=1)
self.patch_img_desc() self.patch_img_desc()
self.patch_image(fc)
for file in glob.glob(self.path_mkwf+"/files/Scene/UI/MenuSingle_?.szs"): self.patch_bmg(file)
self.patch_autoadd()
if self.patch_track(tracks, total_track) != 0: return
for i, file in enumerate(fc["img"]): self.button_install_mod.grid(row=2, column=1, columnspan=2, sticky="NEWS")
self.Progress(statut=self.translate("Conversion des images")+f"\n({i + 1}/{len(fc['img'])}) {file}", add=1)
subprocess.run(["./tools/szs/wimgt", "ENCODE", "./file/" + file, "-x", fc["img"][file], "--overwrite"],
creationflags=CREATE_NO_WINDOW, check=True, stdout=subprocess.PIPE)
for file in glob.glob(self.path_mkwf+"/files/Scene/UI/MenuSingle_?.szs"):
self.patch_bmg(file)
if os.path.exists("./file/auto-add"): shutil.rmtree("./file/auto-add")
if not(os.path.exists(self.path_mkwf + "/tmp/")): os.makedirs(self.path_mkwf + "/tmp/")
subprocess.run(["./tools/szs/wszst", "AUTOADD", get_nodir(self.path_mkwf) + "/files/Race/Course/",
"--DEST", get_nodir(self.path_mkwf) + "/tmp/auto-add/"],
creationflags=CREATE_NO_WINDOW, cwd=get_dir(self.path_mkwf),
check=True, stdout=subprocess.PIPE)
shutil.move(self.path_mkwf + "/tmp/auto-add/", "./file/auto-add/")
shutil.rmtree(self.path_mkwf + "/tmp/")
max_process = 8
process_list = {}
error_count, error_max = 0, 3
for i, file in enumerate(os.listdir("./file/Track-WU8/")):
while True:
if len(process_list) < max_process:
process_list[file] = None
self.Progress(statut=self.translate("Conversion des courses")+f"\n({i + 1}/{total_track})\n" +
"\n".join(process_list.keys()), add=1)
track_szs_file = f"./file/Track/{get_filename(file)}.szs"
if os.path.exists(track_szs_file):
if os.path.getsize(track_szs_file) < 1000: # File under this size are corrupted
os.remove(track_szs_file)
if not(os.path.exists(track_szs_file)):
process_list[file] = subprocess.Popen([
"./tools/szs/wszst", "NORMALIZE", "./file/Track-WU8/" + file, "--DEST",
"./file/Track/%N.szs", "--szs", "--overwrite", "--autoadd-path",
"./file/auto-add/"], creationflags=CREATE_NO_WINDOW, stderr=subprocess.PIPE)
break
else:
for process in process_list:
if process_list[process] is not None:
returncode = process_list[process].poll()
if returncode is None: pass # if the process is still running
else: # process ended
stderr = process_list[process].stderr.read()
if b"wszst: ERROR" in stderr: # Error occured
process_list.pop(process)
os.remove(f"./file/Track/{get_filename(process)}.szs")
error_count += 1
if error_count > error_max: # Too much track wasn't correctly converted
messagebox.showerror(
self.translate("Erreur"),
self.translate("Trop de course ont eu une erreur de conversion."))
return
else: # if the error max hasn't been reach
messagebox.showwarning(
self.translate("Attention"),
self.translate("La course ") +
process +
self.translate(" n'a pas été correctement converti. (") +
str(error_count) + "/"+str(error_max)+")")
break
else:
process_list.pop(process)
break
else:
process_list.pop(process)
break
self.button_install_mod.grid(row=2, column=1, sticky="NEWS")
self.listbox_outputformat.grid(row=2, column=2, sticky="NEWS")
except: self.log_error() except: self.log_error()
finally: self.Progress(show=False) finally: self.Progress(show=False)
t = Thread(target=func) t = Thread(target=func)
t.setDaemon(True) t.setDaemon(True)
t.start() t.start()

9
source/patch_image.py Normal file
View file

@ -0,0 +1,9 @@
from .definition import *
import subprocess
def patch_image(self, fc):
for i, file in enumerate(fc["img"]):
self.Progress(statut=self.translate("Conversion des images") + f"\n({i + 1}/{len(fc['img'])}) {file}", add=1)
subprocess.run(["./tools/szs/wimgt", "ENCODE", "./file/" + file, "-x", fc["img"][file], "--overwrite"],
creationflags=CREATE_NO_WINDOW, check=True, stdout=subprocess.PIPE)

127
source/patch_track.py Normal file
View file

@ -0,0 +1,127 @@
from .definition import *
from tkinter import messagebox
import subprocess
import shutil
import json
import os
def count_track(self):
tracks = []
with open("./ct_config.json", encoding="utf-8") as f:
ctconfig = json.load(f)
for cup in ctconfig["cup"].values():
if not (cup["locked"]): tracks.extend(cup["courses"].values())
tracks.extend(ctconfig["tracks_list"])
tracks = [dict(t) for t in {tuple(d.items()) for d in tracks}]
total_track = len(tracks)
return tracks, total_track
def patch_autoadd(self):
if os.path.exists("./file/auto-add"): shutil.rmtree("./file/auto-add")
if not os.path.exists(self.path_mkwf + "/tmp/"): os.makedirs(self.path_mkwf + "/tmp/")
subprocess.run(["./tools/szs/wszst", "AUTOADD", get_nodir(self.path_mkwf) + "/files/Race/Course/",
"--DEST", get_nodir(self.path_mkwf) + "/tmp/auto-add/"],
creationflags=CREATE_NO_WINDOW, cwd=get_dir(self.path_mkwf),
check=True, stdout=subprocess.PIPE)
shutil.move(self.path_mkwf + "/tmp/auto-add/", "./file/auto-add/")
shutil.rmtree(self.path_mkwf + "/tmp/")
def patch_track(self, tracks, total_track="?"):
max_process = self.intvar_process_track.get()
process_list = {}
error_count, error_max = 0, 3
def add_process(track_file):
nonlocal error_count, error_max, process_list
process_list[track_file] = None # Used for
self.Progress(statut=self.translate("Conversion des courses") + f"\n({i + 1}/{total_track})\n" +
"\n".join(process_list.keys()), add=1)
for _track in [get_track_szs(track_file), get_track_wu8(track_file)]:
if os.path.exists(_track):
if os.path.getsize(_track) < 1000: # File under this size are corrupted
os.remove(_track)
download_returncode = self.get_github_file(get_track_wu8(track_file))
if download_returncode == -1: # can't download
error_count += 1
if error_count > error_max: # Too much track wasn't correctly converted
messagebox.showerror(
self.translate("Erreur"),
self.translate("Trop de course ont eu une erreur du téléchargement."))
return -1
else:
messagebox.showwarning(self.translate("Attention"),
self.translate("Impossible de télécharger cette course ! (") +
str(error_count) + "/" + str(error_max) + ")")
if not (os.path.exists(
get_track_szs(track_file))) or download_returncode == 3: # returncode 3 is track has been updated
if os.path.exists(get_track_wu8(track_file)):
process_list[track_file] = subprocess.Popen([
"./tools/szs/wszst", "NORMALIZE", get_track_wu8(track_file), "--DEST",
"./file/Track/%N.szs", "--szs", "--overwrite", "--autoadd-path",
"./file/auto-add/"], creationflags=CREATE_NO_WINDOW, stderr=subprocess.PIPE)
else:
messagebox.showerror(self.translate("Erreur"),
self.translate("Impossible de convertir la course.\n"
"Réactiver le téléchargement des courses et réessayer."))
return -1
elif self.boolvar_del_track_after_conv.get(): os.remove(get_track_wu8(track_file))
return 0
def clean_process():
nonlocal error_count, error_max, process_list
for track_file, process in process_list.copy().items():
if process is not None:
if process.poll() is None:
print("still running...")
pass # if the process is still running
else: # process ended
process_list.pop(track_file)
stderr = process.stderr.read()
if b"wszst: ERROR" in stderr: # Error occured
os.remove(get_track_szs(track_file))
error_count += 1
if error_count > error_max: # Too much track wasn't correctly converted
messagebox.showerror(
self.translate("Erreur"),
self.translate("Trop de course ont eu une erreur de conversion."))
return -1
else: # if the error max hasn't been reach
messagebox.showwarning(
self.translate("Attention"),
self.translate("La course ") +
get_track_wu8(track_file) +
self.translate(" n'a pas été correctement converti. (") +
str(error_count) + "/" + str(error_max) + ")")
else:
if self.boolvar_del_track_after_conv.get(): os.remove(get_track_wu8(track_file))
else:
process_list.pop(track_file)
if not(any(process_list.values())): return 1 # si il n'y a plus de processus
if len(process_list): return 1
else: return 0
for i, track in enumerate(tracks):
track_file = get_trackname(track=track)
while True:
if len(process_list) < max_process:
returncode = add_process(track_file)
if returncode == 0: break
elif returncode == -1: return -1 # if error occur, stop function
elif clean_process() == -1: return -1
while True:
returncode = clean_process()
if returncode == 1: break # End the process if all process ended
elif returncode == 0: pass
else: return -1
return 0

View file

@ -1,7 +1,10 @@
import subprocess
import sys import sys
import os import os
from .definition import *
def restart(self): def restart(self):
os.execl(sys.executable, f'"{sys.executable}"', *sys.argv) subprocess.Popen([sys.executable] + sys.argv, creationflags=CREATE_NO_WINDOW, cwd=os.getcwd())
sys.exit() sys.exit()

View file

@ -2,7 +2,7 @@ import json
def translate(self, text, lang = None): def translate(self, text, lang = None):
if lang == None: lang = self.language if lang == None: lang = self.stringvar_language.get()
elif lang == "E": lang = "en" elif lang == "E": lang = "en"
elif lang == "G": lang = "ge" elif lang == "G": lang = "ge"
elif lang == "I": lang = "it" elif lang == "I": lang = "it"
@ -13,18 +13,8 @@ def translate(self, text, lang = None):
if lang in translation: if lang in translation:
_lang_trad = translation[lang] _lang_trad = translation[lang]
if text in _lang_trad: return _lang_trad[text] if text in _lang_trad: return _lang_trad[text]
else:
print(f"no translation for : \"{text}\"")
return text return text
def change_language(self):
with open("./translation.json", encoding="utf-8") as f: translation = json.load(f)
translation["selected"] = self.listbox_language.get()
with open("./translation.json", "w", encoding="utf-8") as f: json.dump(translation, f)
self.restart()
def get_language(self):
with open("./translation.json", encoding="utf-8") as f: translation = json.load(f)
if "selected" in translation: return translation["selected"]
else: return "fr"

View file

@ -1 +1,85 @@
{"en": {"La course ": "The track ", " n'a pas été correctement converti. (": "hasn't been correctly converted. (", "Trop de course ont eu une erreur de conversion.": "Too much track had a conversion issue.", "Une erreur est survenue :": "An error occured :", "Cette action va extraire / utiliser la ROM s\u00e9lectionn\u00e9, pr\u00e9parer les fichiers et installer le mod. Voulez-vous continuer ?": "This will extract the selected ROM, prepare files and install mod. Do you wish to continue ?", "Fonctionnalit\u00e9 exp\u00e9rimentale": "Experimental functionality", "Tout faire": "Do everything", "Cr\u00e9ation des images descriptives": "Creation of descriptive images", "Cr\u00e9ation de ct_icon.png": "Creating ct_icon.png", "Patch des textes ": "Patching text ", "Al\u00e9atoire: Toutes les pistes": "Random: All tracks", "Al\u00e9atoire: Pistes Originales": "Random: Original tracks", "Al\u00e9atoire: Custom Tracks": "Random: Custom Tracks", "Al\u00e9atoire: Pistes Nouvelles": "Random: New tracks", "MKWFaraphel Installateur": "MKWFaraphel Installer", "Jeu Wii": "Wii game", "Jeu original": "Original game", "Erreur": "Error", "Le chemin de fichier est invalide": "The file path in invalid", "Attention": "Warning", "Ce dossier sera \u00e9cras\u00e9 si vous installer le mod !\n\u00cates-vous s\u00fbr de vouloir l'utiliser ?": "This directory will be overwritten if you install the mod !\nAre you sure you want to use it ?", "Extraction du jeu...": "Extracting the game...", "Le type de fichier n'est pas reconnu": "This file type is not supported", "Cette ROM est d\u00e9j\u00e0 modd\u00e9, il est d\u00e9conseill\u00e9 de l'utiliser pour installer le mod": "This game is already modded, it is not recommended to use it to install the mod", "Extraire le fichier": "Extract file", "Preparer les fichiers": "Prepare files", "Action": "Action", "Installer le mod": "Install mod", "Dossier": "Directory", "Langage : ": "Language : ", "Mise \u00e0 jour disponible !": "Update available !", "Une mise \u00e0 jour est disponible, souhaitez-vous l'installer ?": "An update is available, do you want to install it ?", "T\u00e9l\u00e9chargement de Updater en cours...": "Downloading the Updater...", "fin du t\u00e9l\u00e9chargement, d\u00e9but de l'extraction...": "end of the download, extracting...", "fin de l'extraction": "finished extracting", "lancement de l'application...": "starting application...", "Modification de": "Modifying", "Recompilation de": "Recompilating", "Conversion en": "Converting to", "Changement de l'ID du jeu": "editing game's ID", "Fin": "End", "L'installation est termin\u00e9 !": "The mod has been installed !", "Conversion des fichiers": "Converting files", "Conversion des images": "Converting images", "Conversion des textes": "Converting texts", "Conversion des courses": "Converting races", "Configuration de LE-CODE": "Configurating LE-CODE"}, "ge": {"Al\u00e9atoire: Toutes les pistes": "Zuf\u00e4llig: Alle Spuren", "Al\u00e9atoire: Pistes Originales": "Zuf\u00e4llig: Original-Spuren", "Al\u00e9atoire: Custom Tracks": "Zuf\u00e4llig: Custom Tracks", "Al\u00e9atoire: Pistes Nouvelles": "Zuf\u00e4llig: Neue Spuren"}, "it": {"Al\u00e9atoire: Toutes les pistes": "Casuale: Tutte le tracce", "Al\u00e9atoire: Pistes Originales": "Casuale: Tracce originali", "Al\u00e9atoire: Custom Tracks": "Casuale: Custom Tracks", "Al\u00e9atoire: Pistes Nouvelles": "Casuale: Nuovi brani"}, "sp": {"Al\u00e9atoire: Toutes les pistes": "Aleatorio: Todas las pistas", "Al\u00e9atoire: Pistes Originales": "Aleatorio: Pistas originales", "Al\u00e9atoire: Custom Tracks": "Aleatorio: Custom Tracks", "Al\u00e9atoire: Pistes Nouvelles": "Aleatorio: Pistas nuevas"}, "selected": "fr"} {
"en": {
"Patch main.dol": "Patch main.dol",
"Patch lecode.bin": "Patch lecode.bin",
"Installation du mod": "Installing mod",
"Impossible de convertir la course.\nRéactiver le téléchargement des courses et réessayer.": "Impossible to convert track.\nEnable track download and retry.",
"Langage": "Language",
"Format": "Format",
"FST (Dossier)": "FST (Directory)",
"Avancé": "Advanced",
"Désactiver les téléchargements": "Disable downloads",
"Supprimer les courses wu8 après conversion en szs": "Delete wu8 track after conversion to szs",
"Ne pas vérifier les mises à jour": "Don't check for update",
"Nombre de processus de conversion de course :": "Number of process for track conversion :",
"1 processus": "1 process",
"2 processus": "2 process",
"4 processus": "4 process",
"8 processus": "8 process",
"La course ": "The track ",
" n'a pas été correctement converti. (": "hasn't been correctly converted. (",
"Trop de course ont eu une erreur de conversion.": "Too much track had a conversion issue.",
"Une erreur est survenue :": "An error occured :",
"Cette action va extraire / utiliser la ROM sélectionné, préparer les fichiers et installer le mod. Voulez-vous continuer ?": "This will extract the selected ROM, prepare files and install mod. Do you wish to continue ?",
"Fonctionnalité expérimentale": "Experimental functionality",
"Tout faire": "Do everything",
"Création des images descriptives": "Creation of descriptive images",
"Création de ct_icon.png": "Creating ct_icon.png",
"Patch des textes ": "Patching text ",
"Aléatoire: Toutes les pistes": "Random: All tracks",
"Aléatoire: Pistes Originales": "Random: Original tracks",
"Aléatoire: Custom Tracks": "Random: Custom Tracks",
"Aléatoire: Pistes Nouvelles": "Random: New tracks",
"MKWFaraphel Installateur": "MKWFaraphel Installer",
"Jeu Wii": "Wii game",
"Jeu original": "Original game",
"Erreur": "Error",
"Le chemin de fichier est invalide": "The file path in invalid",
"Attention": "Warning",
"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 ?",
"Extraction du jeu...": "Extracting the game...",
"Le type de fichier n'est pas reconnu": "This file type is not supported",
"Cette ROM est déjà moddé, il est déconseillé de l'utiliser pour installer le mod": "This game is already modded, it is not recommended to use it to install the mod",
"Extraire le fichier": "Extract file",
"Preparer les fichiers": "Prepare files",
"Action": "Action",
"Installer le mod": "Install mod",
"Dossier": "Directory",
"Langage : ": "Language : ",
"Mise à jour disponible !": "Update available !",
"Une mise à jour est disponible, souhaitez-vous l'installer ?": "An update is available, do you want to install it ?",
"Téléchargement de Updater en cours...": "Downloading the Updater...",
"fin du téléchargement, début de l'extraction...": "end of the download, extracting...",
"fin de l'extraction": "finished extracting",
"lancement de l'application...": "starting application...",
"Modification de": "Modifying",
"Recompilation de": "Recompilating",
"Conversion en": "Converting to",
"Changement de l'ID du jeu": "editing game's ID",
"Fin": "End",
"L'installation est terminé !": "The mod has been installed !",
"Conversion des fichiers": "Converting files",
"Conversion des images": "Converting images",
"Conversion des textes": "Converting texts",
"Conversion des courses": "Converting races",
"Configuration de LE-CODE": "Configurating LE-CODE"
},
"ge": {
"Aléatoire: Toutes les pistes": "Zufällig: Alle Spuren",
"Aléatoire: Pistes Originales": "Zufällig: Original-Spuren",
"Aléatoire: Custom Tracks": "Zufällig: Custom Tracks",
"Aléatoire: Pistes Nouvelles": "Zufällig: Neue Spuren"
},
"it": {
"Aléatoire: Toutes les pistes": "Casuale: Tutte le tracce",
"Aléatoire: Pistes Originales": "Casuale: Tracce originali",
"Aléatoire: Custom Tracks": "Casuale: Custom Tracks",
"Aléatoire: Pistes Nouvelles": "Casuale: Nuovi brani"
},
"sp": {
"Aléatoire: Toutes les pistes": "Aleatorio: Todas las pistas",
"Aléatoire: Pistes Originales": "Aleatorio: Pistas originales",
"Aléatoire: Custom Tracks": "Aleatorio: Custom Tracks",
"Aléatoire: Pistes Nouvelles": "Aleatorio: Pistas nuevas"
}
}

View file

@ -1,7 +1,7 @@
{ {
"version": "0.6", "version": "0.7",
"subversion": "1", "subversion": "0",
"changelog": "- JAP, USA and KOR version are now supported by the installer !", "changelog": "- \"CNR Outer Space Chase\" avait le préfix \"CTR\", rendant la course inutilisable\n- \"SADX Twinkle Circuit\" avaient le préfix \"SHR\", rendant les courses inutilisables\n- Si une course n'est pas trouvée, elle sera téléchargée depuis le github -> les mises à jour seront bien plus légère car vous ne devrez pas retélécharger toutes les courses. Elles seront également retéléchargées si leur taille ne correspond pas à leur version github\n- le code patch_file.py a été divisé en plusieurs fichier pour simplifier le code\n- Update.exe a été recompilé avec la nouvelle version de cx_freeze pour corriger le problème des dossiers non-ASCII\n- Correction de traduction oubliée\n- Ajout d'une barre de menus, où a été déplacé le langage, le format de sortie et de nouvelles options avancé\n\n- \"CNR Outer Space Chase\" had \"CTR\" prefix, making the map unusable\n- \"SADX Twinkle Circuit\" all had \"SHR\" prefix, making the tracks unusable\n- If a track is not found, the program will download it from the github -> update could be much lighter because you won't have to redownload all tracks. They will also be redownloaded if their size doesn't match with their github one\n- Splitted patch_file.py into multiple file to make the code more readable\n- Recompiled Update.exe with new cx_freeze version to fix issue with non-ASCII directory\n- Fixed missing translation\n- Added a menu bar, moved it language, output format and added new advanced option",
"download_bin": "https://github.com/Faraphel/MKWF-Install/releases/download/0.6.1/MKWF.v0.6.1.zip", "download_bin": "https://github.com/Faraphel/MKWF-Install/releases/download/0.7/MKWF.v0.7.zip",
"updater_bin": "https://github.com/Faraphel/MKWF-Install/raw/master/Updater/Updater.zip" "updater_bin": "https://github.com/Faraphel/MKWF-Install/raw/master/Updater/Updater.zip"
} }