mirror of
https://github.com/Faraphel/Atlas-Install.git
synced 2025-07-03 03:08:29 +02:00
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:
parent
014e4fe259
commit
01ac79f623
7 changed files with 271 additions and 252 deletions
|
@ -79,8 +79,7 @@ class CT_Config:
|
|||
"%LE-FLAGS=1\n"
|
||||
"%WIIMM-CUP=1\n"
|
||||
"N N$SWAP | N$F_WII\n\n")
|
||||
ctfile.write(header);
|
||||
rctfile.write(header)
|
||||
ctfile.write(header); rctfile.write(header)
|
||||
|
||||
# generate cup for undefined track
|
||||
unordered_cups = []
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from source.Track import Track
|
||||
from .Track import Track
|
||||
|
||||
|
||||
class Cup:
|
||||
|
|
362
source/Game.py
362
source/Game.py
|
@ -1,16 +1,20 @@
|
|||
from tkinter import messagebox
|
||||
from threading import Thread
|
||||
from PIL import Image
|
||||
import subprocess
|
||||
import shutil
|
||||
import glob
|
||||
import json
|
||||
import os
|
||||
|
||||
from .CT_Config import CT_Config
|
||||
from .definition import *
|
||||
from . import wszst
|
||||
|
||||
|
||||
class RomAlreadyPatched(Exception):
|
||||
def __init__(self):
|
||||
super().__init__("ROM Already patched !")
|
||||
|
||||
|
||||
class InvalidGamePath(Exception):
|
||||
def __init__(self):
|
||||
super().__init__("This path is not valid !")
|
||||
|
@ -37,14 +41,20 @@ class CantConvertTrack(Exception):
|
|||
|
||||
|
||||
class Game:
|
||||
def __init__(self, path: str, region_ID: str = "P", game_ID: str = "RMCP01", gui=None):
|
||||
if not os.path.exists(path): raise InvalidGamePath()
|
||||
self.extension = get_extension(path).upper()
|
||||
def __init__(self, path: str = "", region_ID: str = "P", game_ID: str = "RMCP01", gui=None):
|
||||
if not os.path.exists(path) and path: raise InvalidGamePath()
|
||||
self.extension = None
|
||||
self.path = path
|
||||
self.set_path(path)
|
||||
self.region = region_id_to_name[region_ID]
|
||||
self.region_ID = region_ID
|
||||
self.game_ID = game_ID
|
||||
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"):
|
||||
"""
|
||||
|
@ -53,19 +63,12 @@ class Game:
|
|||
"""
|
||||
if format in ["ISO", "WBFS", "CISO"]:
|
||||
path_game_format: str = os.path.realpath(self.path + "/../MKWFaraphel." + format.lower())
|
||||
subprocess.run(["./tools/wit/wit", "COPY", get_nodir(self.path), "--DEST",
|
||||
get_nodir(path_game_format), f"--{format.lower()}", "--overwrite"],
|
||||
creationflags=CREATE_NO_WINDOW, cwd=get_dir(path_game_format),
|
||||
check=True, stdout=subprocess.PIPE)
|
||||
wszst.wit_copy(self.path, path_game_format, format)
|
||||
shutil.rmtree(self.path)
|
||||
self.path = path_game_format
|
||||
|
||||
self.gui.progress(statut=self.gui.translate("Changing game's ID"), add=1)
|
||||
subprocess.run(["./tools/wit/wit", "EDIT", get_nodir(self.path), "--id",
|
||||
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)
|
||||
wszst.edit(self.path, region_ID=self.region_ID, name=f"Mario Kart Wii Faraphel {self.ctconfig.version}")
|
||||
|
||||
def extract(self):
|
||||
if self.extension == "DOL":
|
||||
|
@ -79,7 +82,7 @@ class Game:
|
|||
if not (os.path.exists(path_dir)): break
|
||||
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
|
||||
if os.path.exists(self.path + "/DATA"): self.path += "/DATA"
|
||||
|
@ -89,7 +92,7 @@ class Game:
|
|||
raise InvalidFormat()
|
||||
|
||||
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:
|
||||
setup = f.read()
|
||||
|
@ -99,133 +102,118 @@ class Game:
|
|||
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
|
||||
|
||||
@in_thread
|
||||
def install_mod(self):
|
||||
def func():
|
||||
try:
|
||||
with open("./fs.json") as f:
|
||||
fs = json.load(f)
|
||||
try:
|
||||
with open("./fs.json") as f:
|
||||
fs = json.load(f)
|
||||
|
||||
# This part is used to estimate the max_step
|
||||
extracted_file = []
|
||||
max_step, step = 1, 0
|
||||
# This part is used to estimate the max_step
|
||||
extracted_file = []
|
||||
max_step, step = 1, 0
|
||||
|
||||
def count_rf(path):
|
||||
nonlocal max_step
|
||||
max_step += 1
|
||||
if get_extension(path) == "szs":
|
||||
if not (os.path.realpath(path) in extracted_file):
|
||||
extracted_file.append(os.path.realpath(path))
|
||||
max_step += 1
|
||||
def count_rf(path):
|
||||
nonlocal max_step
|
||||
max_step += 1
|
||||
if get_extension(path) == "szs":
|
||||
if not (os.path.realpath(path) in extracted_file):
|
||||
extracted_file.append(os.path.realpath(path))
|
||||
max_step += 1
|
||||
|
||||
for fp in fs:
|
||||
for f in glob.glob(self.path + "/files/" + fp, recursive=True):
|
||||
if type(fs[fp]) == str:
|
||||
count_rf(path=f)
|
||||
elif type(fs[fp]) == dict:
|
||||
for nf in fs[fp]:
|
||||
if type(fs[fp][nf]) == str:
|
||||
count_rf(path=f)
|
||||
elif type(fs[fp][nf]) == list:
|
||||
for ffp in fs[fp][nf]: count_rf(path=f)
|
||||
###
|
||||
extracted_file = []
|
||||
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,
|
||||
step=0)
|
||||
for fp in fs:
|
||||
for f in glob.glob(self.path + "/files/" + fp, recursive=True):
|
||||
if type(fs[fp]) == str:
|
||||
count_rf(path=f)
|
||||
elif type(fs[fp]) == dict:
|
||||
for nf in fs[fp]:
|
||||
if type(fs[fp][nf]) == str:
|
||||
count_rf(path=f)
|
||||
elif type(fs[fp][nf]) == list:
|
||||
for ffp in fs[fp][nf]: count_rf(path=f)
|
||||
###
|
||||
extracted_file = []
|
||||
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,
|
||||
step=0)
|
||||
|
||||
def replace_file(path, file, subpath="/"):
|
||||
self.gui.progress(statut=self.gui.translate("Editing", "\n", get_nodir(path)), add=1)
|
||||
extension = get_extension(path)
|
||||
def replace_file(path, file, subpath="/"):
|
||||
self.gui.progress(statut=self.gui.translate("Editing", "\n", get_nodir(path)), add=1)
|
||||
extension = get_extension(path)
|
||||
|
||||
if extension == "szs":
|
||||
if not (os.path.realpath(path) in extracted_file):
|
||||
subprocess.run(
|
||||
["./tools/szs/wszst", "EXTRACT", get_nodir(path), "-d", get_nodir(path) + ".d",
|
||||
"--overwrite"], creationflags=CREATE_NO_WINDOW, cwd=get_dir(path),
|
||||
check=True, stdout=subprocess.PIPE)
|
||||
extracted_file.append(os.path.realpath(path))
|
||||
if extension == "szs":
|
||||
if not (os.path.realpath(path) in extracted_file):
|
||||
wszst.szs_extract(path, get_nodir(path))
|
||||
extracted_file.append(os.path.realpath(path))
|
||||
|
||||
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)
|
||||
else:
|
||||
filecopy(f"./file/{file}", szs_extract_path + subpath)
|
||||
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)
|
||||
else:
|
||||
filecopy(f"./file/{file}", szs_extract_path + subpath)
|
||||
|
||||
elif path[-1] == "/":
|
||||
filecopy(f"./file/{file}", path + file)
|
||||
else:
|
||||
filecopy(f"./file/{file}", path)
|
||||
elif path[-1] == "/":
|
||||
filecopy(f"./file/{file}", path + file)
|
||||
else:
|
||||
filecopy(f"./file/{file}", path)
|
||||
|
||||
for fp in fs:
|
||||
for f in glob.glob(self.path + "/files/" + fp, recursive=True):
|
||||
if type(fs[fp]) == str:
|
||||
replace_file(path=f, file=fs[fp])
|
||||
elif type(fs[fp]) == dict:
|
||||
for nf in fs[fp]:
|
||||
if type(fs[fp][nf]) == str:
|
||||
replace_file(path=f, subpath=nf, file=fs[fp][nf])
|
||||
elif type(fs[fp][nf]) == list:
|
||||
for ffp in fs[fp][nf]: replace_file(path=f, subpath=nf, file=ffp)
|
||||
for fp in fs:
|
||||
for f in glob.glob(self.path + "/files/" + fp, recursive=True):
|
||||
if type(fs[fp]) == str:
|
||||
replace_file(path=f, file=fs[fp])
|
||||
elif type(fs[fp]) == dict:
|
||||
for nf in fs[fp]:
|
||||
if type(fs[fp][nf]) == str:
|
||||
replace_file(path=f, subpath=nf, file=fs[fp][nf])
|
||||
elif type(fs[fp][nf]) == list:
|
||||
for ffp in fs[fp][nf]: replace_file(path=f, subpath=nf, file=ffp)
|
||||
|
||||
for file in extracted_file:
|
||||
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),
|
||||
"--overwrite"], creationflags=CREATE_NO_WINDOW, cwd=get_dir(file),
|
||||
check=True, stdout=subprocess.PIPE)
|
||||
if os.path.exists(file + ".d"): shutil.rmtree(file + ".d")
|
||||
for file in extracted_file:
|
||||
self.gui.progress(statut=self.gui.translate("Recompilating", "\n", get_nodir(file)), add=1)
|
||||
wszst.create(file)
|
||||
if os.path.exists(file + ".d"):
|
||||
shutil.rmtree(file + ".d")
|
||||
|
||||
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",
|
||||
"--add-lecode"], creationflags=CREATE_NO_WINDOW, cwd=get_dir(self.path),
|
||||
check=True, stdout=subprocess.PIPE)
|
||||
self.gui.progress(statut=self.gui.translate("Patch main.dol"), add=1)
|
||||
wszst.str_patch(self.path)
|
||||
|
||||
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)
|
||||
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.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")
|
||||
|
||||
subprocess.run(
|
||||
["./tools/szs/wlect", "patch", f"./tmp/lecode-{self.region}.bin", "-od",
|
||||
f"./files/rel/lecode-{self.region}.bin", "--track-dir", "./files/Race/Course/",
|
||||
"--move-tracks", "./files/Race/Course/", "--le-define", "./tmp/CTFILE.txt", "--lpar",
|
||||
"./tmp/lpar-default.txt", "--overwrite"],
|
||||
creationflags=CREATE_NO_WINDOW, cwd=self.path, check=True, stdout=subprocess.PIPE)
|
||||
wszst.lec_patch(
|
||||
self.path,
|
||||
lecode_file=f"./tmp/lecode-{self.region}.bin",
|
||||
dest_lecode_file=f"./files/rel/lecode-{self.region}.bin",
|
||||
)
|
||||
|
||||
shutil.rmtree(self.path + "/tmp/")
|
||||
shutil.rmtree(self.path + "/tmp/")
|
||||
|
||||
output_format = self.gui.stringvar_game_format.get()
|
||||
self.gui.progress(statut=self.gui.translate("Converting to", " ", output_format), add=1)
|
||||
self.convert_to(output_format)
|
||||
output_format = self.gui.stringvar_game_format.get()
|
||||
self.gui.progress(statut=self.gui.translate("Converting to", " ", output_format), add=1)
|
||||
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:
|
||||
self.gui.log_error()
|
||||
finally:
|
||||
self.gui.progress(show=False)
|
||||
|
||||
t = Thread(target=func)
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
return t
|
||||
except:
|
||||
self.gui.log_error()
|
||||
finally:
|
||||
self.gui.progress(show=False)
|
||||
|
||||
def patch_autoadd(self, auto_add_dir: str = "./file/auto-add"):
|
||||
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/")
|
||||
subprocess.run(["./tools/szs/wszst", "AUTOADD", get_nodir(self.path) + "/files/Race/Course/",
|
||||
"--DEST", get_nodir(self.path) + "/tmp/auto-add/"],
|
||||
creationflags=CREATE_NO_WINDOW, cwd=get_dir(self.path),
|
||||
check=True, stdout=subprocess.PIPE)
|
||||
wszst.autoadd(self.path, get_nodir(self.path) + "/tmp/auto-add/")
|
||||
shutil.move(self.path + "/tmp/auto-add/", auto_add_dir)
|
||||
shutil.rmtree(self.path + "/tmp/")
|
||||
|
||||
def patch_bmg(self, gamefile: str): # gamefile est le fichier .szs trouvé dans le /files/Scene/UI/ du jeu
|
||||
NINTENDO_CWF_REPLACE = "Wiimmfi"
|
||||
MAINMENU_REPLACE = f"MKWFaraphel {self.gui.ctconfig.version}"
|
||||
MAINMENU_REPLACE = f"MKWFaraphel {self.ctconfig.version}"
|
||||
menu_replacement = {
|
||||
"CWF de 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
|
||||
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",
|
||||
"--overwrite"], creationflags=CREATE_NO_WINDOW, cwd=get_dir(gamefile))
|
||||
wszst.szs_extract(gamefile, get_nodir(gamefile))
|
||||
|
||||
# Menu.bmg
|
||||
bmgmenu = subprocess.run(["./tools/szs/wbmgt", "CAT", get_nodir(gamefile) + ".d/message/Menu.bmg"],
|
||||
creationflags=CREATE_NO_WINDOW, cwd=get_dir(gamefile),
|
||||
check=True, stdout=subprocess.PIPE).stdout.decode()
|
||||
bmgmenu = wszst.bmg_cat(gamefile, ".d/message/Menu.bmg") # Menu.bmg
|
||||
bmgtracks = wszst.bmg_cat(gamefile, ".d/message/Common.bmg") # Common.bmg
|
||||
|
||||
# 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"
|
||||
trackend = "2328"
|
||||
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/")
|
||||
|
||||
filecopy(gamefile + ".d/message/Common.bmg", "./file/tmp/Common.bmg")
|
||||
bmgcommon = subprocess.run(
|
||||
["tools/szs/wctct", "bmg", "--le-code", "--long", "./file/CTFILE.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()
|
||||
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()
|
||||
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",
|
||||
bmgs=["./file/tmp/Common.bmg", "./file/ExtraCommon.txt"])
|
||||
|
||||
shutil.rmtree(gamefile + ".d")
|
||||
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)
|
||||
with open(file, "w", encoding="utf-8") as f:
|
||||
f.write(bmgtext)
|
||||
subprocess.run(["./tools/szs/wbmgt", "ENCODE", get_nodir(file), "--overwrite"],
|
||||
creationflags=CREATE_NO_WINDOW, cwd=get_dir(file))
|
||||
wszst.bmg_encode(file)
|
||||
os.remove(file)
|
||||
|
||||
finalise(f"./file/Menu_{bmglang}.txt", bmgmenu, menu_replacement)
|
||||
finalise(f"./file/Common_{bmglang}.txt", bmgcommon)
|
||||
finalise(f"./file/Common_R{bmglang}.txt", rbmgcommon)
|
||||
|
||||
@in_thread
|
||||
def patch_file(self):
|
||||
def func():
|
||||
try:
|
||||
if not (os.path.exists("./file/Track-WU8/")): os.makedirs("./file/Track-WU8/")
|
||||
with open("./convert_file.json") as f:
|
||||
fc = json.load(f)
|
||||
max_step = len(fc["img"]) + len(self.gui.ctconfig.all_tracks) + 3 + len("EGFIS")
|
||||
try:
|
||||
if not (os.path.exists("./file/Track-WU8/")): os.makedirs("./file/Track-WU8/")
|
||||
with open("./convert_file.json") as f:
|
||||
fc = json.load(f)
|
||||
max_step = len(fc["img"]) + len(self.ctconfig.all_tracks) + 3 + len("EGFIS")
|
||||
|
||||
self.gui.progress(show=True, indeter=False, statut=self.gui.translate("Converting files"), max=max_step,
|
||||
step=0)
|
||||
self.gui.progress(statut=self.gui.translate("Configurating LE-CODE"), add=1)
|
||||
self.gui.ctconfig.create_ctfile()
|
||||
self.gui.progress(show=True, indeter=False, statut=self.gui.translate("Converting files"),
|
||||
max=max_step, step=0)
|
||||
self.gui.progress(statut=self.gui.translate("Configurating LE-CODE"), add=1)
|
||||
self.ctconfig.create_ctfile()
|
||||
|
||||
self.gui.progress(statut=self.gui.translate("Creating ct_icon.png"), add=1)
|
||||
ct_icon = self.gui.ctconfig.get_cticon()
|
||||
ct_icon.save("./file/ct_icons.tpl.png")
|
||||
self.gui.progress(statut=self.gui.translate("Creating ct_icon.png"), add=1)
|
||||
ct_icon = self.ctconfig.get_cticon()
|
||||
ct_icon.save("./file/ct_icons.tpl.png")
|
||||
|
||||
self.gui.progress(statut=self.gui.translate("Creating descriptive images"), add=1)
|
||||
self.patch_img_desc()
|
||||
self.patch_image(fc)
|
||||
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.
|
||||
self.patch_autoadd()
|
||||
if self.patch_tracks() != 0: return
|
||||
self.gui.progress(statut=self.gui.translate("Creating descriptive images"), add=1)
|
||||
self.patch_img_desc()
|
||||
self.patch_image(fc)
|
||||
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.
|
||||
self.patch_autoadd()
|
||||
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.config(
|
||||
text=self.gui.translate("Install mod", " (v", self.gui.ctconfig.version, ")"))
|
||||
self.gui.button_install_mod.grid(row=2, column=1, columnspan=2, sticky="NEWS")
|
||||
self.gui.button_install_mod.config(
|
||||
text=self.gui.translate("Install mod", " (v", self.ctconfig.version, ")"))
|
||||
|
||||
except:
|
||||
self.gui.log_error()
|
||||
finally:
|
||||
self.gui.progress(show=False)
|
||||
|
||||
t = Thread(target=func)
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
return t
|
||||
except:
|
||||
self.gui.log_error()
|
||||
finally:
|
||||
self.gui.progress(show=False)
|
||||
|
||||
def patch_image(self, fc):
|
||||
for i, file in enumerate(fc["img"]):
|
||||
self.gui.progress(statut=self.gui.translate("Converting 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)
|
||||
wszst.img_encode("./file/" + file, fc["img"][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")
|
||||
|
@ -390,7 +360,7 @@ class Game:
|
|||
def add_process(track):
|
||||
nonlocal error_count, error_max, process_list
|
||||
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
|
||||
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
|
||||
error_count += 1
|
||||
if error_count > error_max: # Too much track wasn't correctly converted
|
||||
"""messagebox.showerror(
|
||||
gui.translate("Error"),
|
||||
gui.translate("Too much tracks had a download issue."))
|
||||
return -1"""
|
||||
messagebox.showerror(
|
||||
self.gui.translate("Error"),
|
||||
self.gui.translate("Too much tracks had a download issue."))
|
||||
raise TooMuchDownloadFailed()
|
||||
else:
|
||||
"""messagebox.showwarning(gui.translate("Warning"),
|
||||
gui.translate("Can't download this track !",
|
||||
f" ({error_count} / {error_max})"))"""
|
||||
messagebox.showwarning(self.gui.translate("Warning"),
|
||||
self.gui.translate("Can't download this track !",
|
||||
f" ({error_count} / {error_max})"))
|
||||
elif download_returncode == 2:
|
||||
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
|
||||
error_count += 1
|
||||
if error_count > error_max: # Too much track wasn't correctly converted
|
||||
"""messagebox.showerror(
|
||||
gui.translate("Error"),
|
||||
gui.translate("Too much tracks had an issue during sha1 check."))"""
|
||||
messagebox.showerror(
|
||||
self.gui.translate("Error"),
|
||||
self.gui.translate("Too much tracks had an issue during sha1 check."))
|
||||
raise TooMuchSha1CheckFailed()
|
||||
continue
|
||||
|
||||
break
|
||||
|
||||
if not (
|
||||
os.path.exists(
|
||||
track.file_szs)) or download_returncode == 3: # returncode 3 is track has been updated
|
||||
if not (os.path.exists(track.file_szs)) or download_returncode == 3:
|
||||
# returncode 3 is track has been updated
|
||||
if os.path.exists(track.file_wu8):
|
||||
process_list[track_file] = track.convert_wu8_to_szs()
|
||||
else:
|
||||
"""messagebox.showerror(gui.translate("Error"),
|
||||
gui.translate("Can't convert track.\nEnable track download and retry."))"""
|
||||
messagebox.showerror(self.gui.translate("Error"),
|
||||
self.gui.translate("Can't convert track.\nEnable track download and retry."))
|
||||
raise CantConvertTrack()
|
||||
elif self.gui.boolvar_del_track_after_conv.get():
|
||||
os.remove(track.file_wu8)
|
||||
|
@ -459,28 +427,28 @@ class Game:
|
|||
os.remove(track.file_szs)
|
||||
error_count += 1
|
||||
if error_count > error_max: # Too much track wasn't correctly converted
|
||||
"""messagebox.showerror(
|
||||
gui.translate("Error"),
|
||||
gui.translate("Too much track had a conversion issue."))"""
|
||||
raise CantConvertTrack
|
||||
messagebox.showerror(
|
||||
self.gui.translate("Error"),
|
||||
self.gui.translate("Too much track had a conversion issue."))
|
||||
raise CantConvertTrack()
|
||||
else: # if the error max hasn't been reach
|
||||
"""messagebox.showwarning(
|
||||
gui.translate("Warning"),
|
||||
gui.translate("The track", " ", track.file_wu8,
|
||||
"do not have been properly converted.",
|
||||
f" ({error_count} / {error_max})"))"""
|
||||
messagebox.showwarning(
|
||||
self.gui.translate("Warning"),
|
||||
self.gui.translate("The track", " ", track.file_wu8,
|
||||
"do not have been properly converted.",
|
||||
f" ({error_count} / {error_max})"))
|
||||
else:
|
||||
if self.gui.boolvar_del_track_after_conv.get(): os.remove(track.file_wu8)
|
||||
else:
|
||||
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):
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
for i, track in enumerate(self.gui.ctconfig.all_tracks):
|
||||
for i, track in enumerate(self.ctconfig.all_tracks):
|
||||
while True:
|
||||
if len(process_list) < max_process:
|
||||
returncode = add_process(track)
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
from tkinter import filedialog, ttk
|
||||
from tkinter import *
|
||||
import zipfile
|
||||
import subprocess
|
||||
import traceback
|
||||
import requests
|
||||
import zipfile
|
||||
|
||||
from source.CT_Config import CT_Config
|
||||
from source.Option import Option
|
||||
from source.Game import *
|
||||
|
||||
|
@ -24,8 +24,8 @@ class Gui:
|
|||
|
||||
self.option = Option()
|
||||
self.option.load_from_file("./option.json")
|
||||
self.ctconfig = CT_Config()
|
||||
self.ctconfig.load_ctconfig_file("./ct_config.json")
|
||||
self.game = Game(gui=self)
|
||||
self.game.ctconfig.load_ctconfig_file("./ct_config.json")
|
||||
|
||||
self.is_dev_version = False # Is this installer version a dev ?
|
||||
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_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")
|
||||
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_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.columnconfigure(1, weight=1)
|
||||
|
||||
@in_thread
|
||||
def use_path():
|
||||
def func():
|
||||
self.frame_action.grid_forget()
|
||||
try:
|
||||
self.game = Game(path=entry_game_path.get(), gui=self)
|
||||
self.progress(show=True, indeter=True, statut=self.translate("Extracting the game..."))
|
||||
self.game.extract()
|
||||
self.frame_action.grid(row=3, column=1, sticky="NEWS")
|
||||
except InvalidGamePath:
|
||||
messagebox.showerror(self.translate("Error"), self.translate("The file path in invalid"))
|
||||
except InvalidFormat:
|
||||
messagebox.showerror(self.translate("Error"), self.translate("This game's format is invalid"))
|
||||
except:
|
||||
self.log_error()
|
||||
finally:
|
||||
self.progress(show=False)
|
||||
|
||||
t = Thread(target=func)
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
return t
|
||||
self.frame_action.grid_forget()
|
||||
try:
|
||||
self.game.set_path(entry_game_path.get())
|
||||
self.progress(show=True, indeter=True, statut=self.translate("Extracting the game..."))
|
||||
self.game.extract()
|
||||
self.frame_action.grid(row=3, column=1, sticky="NEWS")
|
||||
except RomAlreadyPatched:
|
||||
messagebox.showerror(self.translate("Error"), self.translate("This game is already modded"))
|
||||
except InvalidGamePath:
|
||||
messagebox.showerror(self.translate("Error"), self.translate("The file path in invalid"))
|
||||
except InvalidFormat:
|
||||
messagebox.showerror(self.translate("Error"), self.translate("This game's format is invalid"))
|
||||
except:
|
||||
self.log_error()
|
||||
finally:
|
||||
self.progress(show=False)
|
||||
|
||||
self.button_game_extract = Button(self.frame_game_path_action, text=self.translate("Extract file"),
|
||||
relief=RIDGE, command=use_path)
|
||||
self.button_game_extract.grid(row=1, column=1, sticky="NEWS")
|
||||
|
||||
@in_thread
|
||||
def do_everything():
|
||||
def func():
|
||||
use_path().join()
|
||||
self.game.patch_file(self).join()
|
||||
self.game.install_mod(self).join()
|
||||
use_path().join()
|
||||
self.game.patch_file().join()
|
||||
self.game.install_mod().join()
|
||||
|
||||
if messagebox.askyesno(self.translate("Experimental functionality"),
|
||||
self.translate("This will extract the selected ROM, prepare files and install mod. "
|
||||
"Do you wish to continue ?")):
|
||||
t = Thread(target=func)
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
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.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_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
|
||||
|
||||
self.progressbar = ttk.Progressbar(self.root)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from threading import Thread
|
||||
|
||||
CREATE_NO_WINDOW = 0x08000000
|
||||
GITHUB_REPOSITORY = "Faraphel/MKWF-Install"
|
||||
GITHUB_MASTER_BRANCH = f"https://raw.githubusercontent.com/{GITHUB_REPOSITORY}/master/"
|
||||
|
@ -94,7 +96,18 @@ region_id_to_name = {
|
|||
"E": "USA"
|
||||
}
|
||||
|
||||
|
||||
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):
|
||||
thread = Thread(target=func, args=args, kwargs=kwargs)
|
||||
thread.setDaemon(True)
|
||||
thread.start()
|
||||
return thread
|
||||
|
||||
return wrapped_func
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
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 dest_dir: where to extract the game
|
||||
|
@ -42,35 +42,79 @@ def extract(file: str, dest_dir: 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):
|
||||
pass
|
||||
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),
|
||||
check=True, stdout=subprocess.PIPE)
|
||||
|
||||
|
||||
def wlect_patch(file: str,
|
||||
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"):
|
||||
pass
|
||||
def lec_patch(file: 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"):
|
||||
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))
|
|
@ -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 ?",
|
||||
"Extracting the game...": "Extraction du jeu...",
|
||||
"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.",
|
||||
"Extract file": "Extraire le fichier",
|
||||
"Experimental functionality": "Fonctionnalité expérimentale",
|
||||
|
|
Loading…
Reference in a new issue