created a Option class, merged get_github_file with Track and install_mod with Game

This commit is contained in:
raphael60650 2021-07-17 22:23:58 +02:00
parent 2f171311bc
commit 2990b1a652
14 changed files with 432 additions and 446 deletions

View file

@ -22,14 +22,13 @@ class ClassApp():
from source.StateButton import StateButton from source.StateButton import StateButton
from source.patch_file import patch_file from source.patch_file import patch_file
from source.patch_bmg import patch_bmg from source.patch_bmg import patch_bmg
from source.install_mod import install_mod
from source.restart import restart from source.restart import restart
from source.patch_img_desc import patch_img_desc from source.patch_img_desc import patch_img_desc
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 patch_track
from source.patch_track import patch_track, patch_autoadd
from source.patch_image import patch_image from source.patch_image import patch_image
from source.option import load_option, change_option
from source.Option import Option
from source.CT_Config import CT_Config from source.CT_Config import CT_Config
from source.Game import Game, InvalidGamePath, InvalidFormat from source.Game import Game, InvalidGamePath, InvalidFormat

View file

@ -1,5 +1,9 @@
from . import wszst from . import wszst
from .definition import * from .definition import *
from threading import Thread
import subprocess
import shutil
import json
import glob import glob
import os import os
@ -10,6 +14,7 @@ region_id_to_name = {
"E": "USA" "E": "USA"
} }
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 !")
@ -60,8 +65,139 @@ 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
def install_mod(self): def patch_autoadd(self, auto_add_dir: str = "./file/auto-add"):
pass 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)
shutil.move(self.path + "/tmp/auto-add/", auto_add_dir)
shutil.rmtree(self.path + "/tmp/")
def install_mod(self, gui):
def func():
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
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.Progress(show=True, indeter=False, statut=self.translate("Installing mod"), max=max_step, step=0)
def replace_file(path, file, subpath="/"):
# self.Progress(statut=self.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))
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)
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.Progress(statut=self.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")
# self.Progress(statut=self.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.Progress(statut=self.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")
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)
shutil.rmtree(self.path + "/tmp/")
output_format = gui.stringvar_game_format.get()
# self.Progress(statut=self.translate("Converting to", " ", output_format), add=1)
if output_format in ["ISO", "WBFS", "CISO"]:
path_game_format: str = os.path.realpath(self.path + "/../MKWFaraphel." + output_format.lower())
subprocess.run(["./tools/wit/wit", "COPY", get_nodir(self.path), "--DEST",
get_nodir(path_game_format), f"--{output_format.lower()}", "--overwrite"],
creationflags=CREATE_NO_WINDOW, cwd=get_dir(path_game_format),
check=True, stdout=subprocess.PIPE)
shutil.rmtree(self.path)
self.path = path_game_format
# self.Progress(statut=self.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 {gui.ctconfig.version}", "--modify", "ALL"],
creationflags=CREATE_NO_WINDOW, cwd=get_dir(self.path),
check=True, stdout=subprocess.PIPE)
# messagebox.showinfo(self.translate("End"), self.translate("The mod has been installed !"))
except: pass # self.log_error()
finally: pass # self.Progress(show=False)
t = Thread(target=func)
t.setDaemon(True)
t.start()
return t
def convert_to(self, format: str = "FST"): def convert_to(self, format: str = "FST"):
""" """

34
source/Option.py Normal file
View file

@ -0,0 +1,34 @@
import json
import os
class Option:
def __init__(self):
self.language = "en"
self.format = "FST"
self.disable_download = False
self.del_track_after_conv = False
self.dont_check_for_update = False
self.dont_check_track_sha1 = False
self.process_track = 8
def load_from_json(self, option_json: dict):
for key, value in option_json.items(): # load all value in the json as class attribute
setattr(self, key, value)
def load_from_file(self, option_file: str = "./option.json"):
if os.path.exists(option_file):
with open(option_file, encoding="utf-8") as file:
file_json = json.load(file)
self.load_from_json(file_json)
def save_to_file(self, option_file: str = "./option.json"):
option_json: dict = self.__dict__ # this return all attribute of the class as a dict
with open(option_file, "w", encoding="utf-8") as file:
json.dump(option_json, file, ensure_ascii=False)
def edit(self, option, value, need_restart=False, gui=None):
if type(value) in [str, int, bool]: setattr(self, option, value)
else: setattr(self, option, value.get())
self.save_to_file()
if need_restart: gui.restart()

View file

@ -1,11 +1,14 @@
from .definition import * from .definition import *
import source.wszst import source.wszst
import requests
import os
class Track: class Track:
def __init__(self, name: str = "_", file_wu8: str = None, file_szs: str = None, prefix: str = None, suffix: str = None, def __init__(self, name: str = "_", file_wu8: str = None, file_szs: str = None, prefix: str = None, suffix: str = None,
author="Nintendo", special="T11", music="T11", new=True, sha1: str = None, since_version: str = None, author="Nintendo", special="T11", music="T11", new=True, sha1: str = None, since_version: str = None,
score: int = 0, warning: int = 0, note: str = "", *args, **kwargs): score: int = 0, warning: int = 0, note: str = "", track_wu8_dir: str = "./file/Track-WU8/",
track_szs_dir: str = "./file/Track/", *args, **kwargs):
self.name = name # Track name self.name = name # Track name
self.prefix = prefix # Prefix, often used for game or original console like Wii U, DS, ... self.prefix = prefix # Prefix, often used for game or original console like Wii U, DS, ...
@ -19,8 +22,8 @@ class Track:
self.score = score # Track score between 1 and 3 stars self.score = score # Track score between 1 and 3 stars
self.warning = warning # Track bug level (1 = minor, 2 = major) self.warning = warning # Track bug level (1 = minor, 2 = major)
self.note = note # Note about the track self.note = note # Note about the track
self.file_wu8 = f"./file/Track-WU8/{self.get_track_name()}.wu8" self.file_wu8 = f"{track_wu8_dir}/{self.get_track_name()}.wu8"
self.file_szs = f"./file/Track/{self.get_track_name()}.szs" self.file_szs = f"{track_szs_dir}/{self.get_track_name()}.szs"
def __repr__(self): def __repr__(self):
return f"{self.get_track_name()} sha1={self.sha1} score={self.score}" return f"{self.get_track_name()} sha1={self.sha1} score={self.score}"
@ -32,8 +35,8 @@ class Track:
name = (prefix + self.name + suffix) name = (prefix + self.name + suffix)
return name return name
def load_from_json(self, track: dict): def load_from_json(self, track_json: dict):
for key, value in track.items(): # load all value in the json as class attribute for key, value in track_json.items(): # load all value in the json as class attribute
setattr(self, key, value) setattr(self, key, value)
def get_track_formatted_name(self, highlight_track_from_version: str = None): def get_track_formatted_name(self, highlight_track_from_version: str = None):
@ -67,7 +70,24 @@ class Track:
def convert_wu8_to_szs(self): def convert_wu8_to_szs(self):
return source.wszst.normalize(src_file=self.file_wu8, use_popen=True) return source.wszst.normalize(src_file=self.file_wu8, use_popen=True)
def download_wu8(self): pass def download_wu8(self):
returncode = 0
dl = requests.get(get_github_content_root(self) + self.file_wu8, allow_redirects=True, stream=True)
if os.path.exists(self.file_wu8):
if int(dl.headers['Content-Length']) == os.path.getsize(self.file_wu8): return 1
else: returncode = 3
if dl.status_code == 200: # if page is found
with open(self.file_wu8, "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} {self.file_wu8}")
return -1
def check_sha1(self): def check_sha1(self):
if source.wszst.sha1(self.file_wu8) == self.sha1: return 0 if source.wszst.sha1(self.file_wu8) == self.sha1: return 0

View file

@ -9,24 +9,25 @@ from .definition import *
from .check_update import check_update from .check_update import check_update
from .translate import translate from .translate import translate
from .CT_Config import * from .CT_Config import *
from .Option import *
from .Game import * from .Game import *
def __init__(self): def __init__(self):
try:
self.root = Tk() self.root = Tk()
self.load_option() self.option = Option()
self.option.load_from_file("./option.json")
self.ctconfig = CT_Config() self.ctconfig = CT_Config()
self.ctconfig.load_ctconfig_file("./ct_config.json") self.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)
self.stringvar_game_format = StringVar(value=self.option["format"]) self.stringvar_game_format = StringVar(value=self.option.format)
self.boolvar_disable_download = BooleanVar(value=self.option["disable_download"]) 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_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.boolvar_dont_check_for_update = BooleanVar(value=self.option.dont_check_for_update)
self.boolvar_dont_check_track_sha1 = BooleanVar(value=self.option["dont_check_track_sha1"]) self.boolvar_dont_check_track_sha1 = BooleanVar(value=self.option.dont_check_track_sha1)
self.intvar_process_track = IntVar(value=self.option["process_track"]) self.intvar_process_track = IntVar(value=self.option.process_track)
self.boolvar_use_1star_track = BooleanVar(value=True) self.boolvar_use_1star_track = BooleanVar(value=True)
self.boolvar_use_2star_track = BooleanVar(value=True) self.boolvar_use_2star_track = BooleanVar(value=True)
self.boolvar_use_3star_track = BooleanVar(value=True) self.boolvar_use_3star_track = BooleanVar(value=True)
@ -43,15 +44,15 @@ def __init__(self):
self.menu_language = Menu(self.menu_bar, tearoff=0) self.menu_language = Menu(self.menu_bar, tearoff=0)
self.menu_bar.add_cascade(label=self.translate("Language"), menu=self.menu_language) self.menu_bar.add_cascade(label=self.translate("Language"), 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="Français", variable=self.stringvar_language, value="fr", command=lambda: self.option.edit("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_language.add_radiobutton(label="English", variable=self.stringvar_language, value="en", command=lambda: self.option.edit("language", "en", restart=True))
self.menu_format = Menu(self.menu_bar, tearoff=0) self.menu_format = Menu(self.menu_bar, tearoff=0)
self.menu_bar.add_cascade(label=self.translate("Format"), menu=self.menu_format) self.menu_bar.add_cascade(label=self.translate("Format"), menu=self.menu_format)
self.menu_format.add_radiobutton(label=self.translate("FST (Directory)"), variable=self.stringvar_game_format, value="FST", command=lambda: self.change_option("format", "FST")) self.menu_format.add_radiobutton(label=self.translate("FST (Directory)"), variable=self.stringvar_game_format, value="FST", command=lambda: self.option.edit("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="ISO", variable=self.stringvar_game_format, value="ISO", command=lambda: self.option.edit("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="CISO", variable=self.stringvar_game_format, value="CISO", command=lambda: self.option.edit("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_format.add_radiobutton(label="WBFS", variable=self.stringvar_game_format, value="WBFS", command=lambda: self.option.edit("format", "WBFS"))
self.menu_trackselection = Menu(self.menu_bar, tearoff=0) self.menu_trackselection = Menu(self.menu_bar, tearoff=0)
self.menu_bar.add_cascade(label=self.translate("Track selection"), menu=self.menu_trackselection) self.menu_bar.add_cascade(label=self.translate("Track selection"), menu=self.menu_trackselection)
@ -67,17 +68,17 @@ def __init__(self):
self.menu_advanced = Menu(self.menu_bar, tearoff=0) self.menu_advanced = Menu(self.menu_bar, tearoff=0)
self.menu_bar.add_cascade(label=self.translate("Advanced"), menu=self.menu_advanced) self.menu_bar.add_cascade(label=self.translate("Advanced"), menu=self.menu_advanced)
self.menu_advanced.add_checkbutton(label=self.translate("Disable downloads"), variable=self.boolvar_disable_download, command=lambda: self.change_option("disable_download", self.boolvar_disable_download)) self.menu_advanced.add_checkbutton(label=self.translate("Disable downloads"), variable=self.boolvar_disable_download, command=lambda: self.option.edit("disable_download", self.boolvar_disable_download))
self.menu_advanced.add_checkbutton(label=self.translate("Delete track after wu8 to szs conversion"), 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("Delete track after wu8 to szs conversion"), variable=self.boolvar_del_track_after_conv, command=lambda: self.option.edit("del_track_after_conv", self.boolvar_del_track_after_conv))
self.menu_advanced.add_checkbutton(label=self.translate("Don't check for update"), 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_checkbutton(label=self.translate("Don't check for update"), variable=self.boolvar_dont_check_for_update, command=lambda: self.option.edit("dont_check_for_update", self.boolvar_dont_check_for_update))
self.menu_advanced.add_checkbutton(label=self.translate("Don't check track's sha1"), variable=self.boolvar_dont_check_track_sha1, command=lambda: self.change_option("dont_check_track_sha1",self.boolvar_dont_check_track_sha1)) self.menu_advanced.add_checkbutton(label=self.translate("Don't check track's sha1"), variable=self.boolvar_dont_check_track_sha1, command=lambda: self.option.edit("dont_check_track_sha1",self.boolvar_dont_check_track_sha1))
self.menu_advanced.add_separator() self.menu_advanced.add_separator()
self.menu_advanced.add_command(label=self.translate("Number of track conversion process", " :")) self.menu_advanced.add_command(label=self.translate("Number of track conversion process", " :"))
self.menu_advanced.add_radiobutton(label=self.translate("1 ", "process"), variable=self.intvar_process_track, value=1, command=lambda: self.change_option("process_track", 1)) self.menu_advanced.add_radiobutton(label=self.translate("1 ", "process"), variable=self.intvar_process_track, value=1, command=lambda: self.option.edit("process_track", 1))
self.menu_advanced.add_radiobutton(label=self.translate("2 ", "process"), variable=self.intvar_process_track, value=2, command=lambda: self.change_option("process_track", 2)) self.menu_advanced.add_radiobutton(label=self.translate("2 ", "process"), variable=self.intvar_process_track, value=2, command=lambda: self.option.edit("process_track", 2))
self.menu_advanced.add_radiobutton(label=self.translate("4 ", "process"), variable=self.intvar_process_track, value=4, command=lambda: self.change_option("process_track", 4)) self.menu_advanced.add_radiobutton(label=self.translate("4 ", "process"), variable=self.intvar_process_track, value=4, command=lambda: self.option.edit("process_track", 4))
self.menu_advanced.add_radiobutton(label=self.translate("8 ", "process"), variable=self.intvar_process_track, value=8, command=lambda: self.change_option("process_track", 8)) self.menu_advanced.add_radiobutton(label=self.translate("8 ", "process"), variable=self.intvar_process_track, value=8, command=lambda: self.option.edit("process_track", 8))
self.frame_language = Frame(self.root) self.frame_language = Frame(self.root)
@ -132,7 +133,7 @@ def __init__(self):
def func(): def func():
use_path().join() use_path().join()
self.patch_file().join() self.patch_file().join()
self.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. "
@ -141,22 +142,16 @@ def __init__(self):
t.setDaemon(True) t.setDaemon(True)
t.start() t.start()
self.button_do_everything = Button(self.frame_game_path_action, text=self.translate("Do everything"), self.button_do_everything = Button(self.frame_game_path_action, text=self.translate("Do everything"), relief=RIDGE, command=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, self.button_prepare_file = Button(self.frame_action, text=self.translate("Prepare files"), relief=RIDGE, 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("Install mod"), relief=RIDGE, self.button_install_mod = Button(self.frame_action, text=self.translate("Install mod"), relief=RIDGE, command=lambda: self.game.install_mod(self), width=45)
command=self.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)
self.progresslabel = Label(self.root) self.progresslabel = Label(self.root)
except:
self.log_error()

View file

@ -46,6 +46,6 @@ def check_update(self):
except requests.ConnectionError: except requests.ConnectionError:
messagebox.showwarning(self.translate("Warning"), messagebox.showwarning(self.translate("Warning"),
self.translate("Can't connect to internet. Download will be disabled.")) self.translate("Can't connect to internet. Download will be disabled."))
self.option["disable_download"] = True self.option.disable_download = True
except: self.log_error() except: self.log_error()

View file

@ -23,7 +23,7 @@ bmgID_track_move = {
"T61": 0x701b, "T62": 0x701f, "T63": 0x7017, "T64": 0x7012, "T61": 0x701b, "T62": 0x701f, "T63": 0x7017, "T64": 0x7012,
"T71": 0x7015, "T72": 0x701e, "T73": 0x701d, "T74": 0x7011, "T71": 0x7015, "T72": 0x701e, "T73": 0x701d, "T74": 0x7011,
"T81": 0x7018, "T82": 0x7016, "T83": 0x7013, "T84": 0x701c, "T81": 0x7018, "T82": 0x7016, "T83": 0x7013, "T84": 0x701c,
} }
trackname_color = { trackname_color = {
"MSRDS": "\\\\c{green}MSRDS\\\\c{off}", "MSRDS": "\\\\c{green}MSRDS\\\\c{off}",
"CTR": "\\\\c{YOR4}CTR\\\\c{off}", "CTR": "\\\\c{YOR4}CTR\\\\c{off}",

View file

@ -1,31 +0,0 @@
import requests
import os
from .definition import *
def get_github_file(self, file):
try:
returncode = 0
if self.boolvar_disable_download.get(): return 2
dl = requests.get(get_github_content_root(self)+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
# TODO: if version > github version, do not search in master branch but dev branch

View file

@ -1,137 +0,0 @@
from tkinter import messagebox
from threading import Thread
import subprocess
import shutil
import json
import glob
import os
from .definition import *
def install_mod(self):
def func():
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
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.game.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.Progress(show=True, indeter=False, statut=self.translate("Installing mod"), max=max_step, step=0)
def replace_file(path, file, subpath="/"):
self.Progress(statut=self.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))
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)
for fp in fs:
for f in glob.glob(self.game.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.Progress(statut=self.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")
self.Progress(statut=self.translate("Patch main.dol"), add=1)
subprocess.run(["./tools/szs/wstrt", "patch", get_nodir(self.game.path) + "/sys/main.dol", "--clean-dol",
"--add-lecode"], creationflags=CREATE_NO_WINDOW, cwd=get_dir(self.game.path),
check=True, stdout=subprocess.PIPE)
self.Progress(statut=self.translate("Patch lecode.bin"), add=1)
shutil.copytree("./file/Track/", self.game.path+"/files/Race/Course/", dirs_exist_ok=True)
if not(os.path.exists(self.game.path+"/tmp/")): os.makedirs(self.game.path+"/tmp/")
filecopy("./file/CTFILE.txt", self.game.path+"/tmp/CTFILE.txt")
filecopy("./file/lpar-default.txt", self.game.path + "/tmp/lpar-default.txt")
filecopy(f"./file/lecode-{self.game.region}.bin", self.game.path + f"/tmp/lecode-{self.game.region}.bin")
subprocess.run(
["./tools/szs/wlect", "patch", f"./tmp/lecode-{self.game.region}.bin", "-od",
f"./files/rel/lecode-{self.game.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.game.path, check=True, stdout=subprocess.PIPE)
shutil.rmtree(self.game.path + "/tmp/")
outputformat = self.stringvar_game_format.get()
self.Progress(statut=self.translate("Converting to", " ", outputformat), add=1)
if outputformat in ["ISO", "WBFS", "CISO"]:
path_game_format: str = os.path.realpath(self.game.path + "/../MKWFaraphel." + outputformat.lower())
subprocess.run(["./tools/wit/wit", "COPY", get_nodir(self.game.path), "--DEST",
get_nodir(path_game_format), f"--{outputformat.lower()}", "--overwrite"],
creationflags=CREATE_NO_WINDOW, cwd=get_dir(path_game_format),
check=True, stdout=subprocess.PIPE)
shutil.rmtree(self.game.path)
self.game.path = path_game_format
self.Progress(statut=self.translate("Changing game's ID"), add=1)
subprocess.run(["./tools/wit/wit", "EDIT", get_nodir(self.game.path), "--id",
f"RMC{self.game.region_ID}60", "--name",
f"Mario Kart Wii Faraphel {self.ctconfig.version}", "--modify", "ALL"],
creationflags=CREATE_NO_WINDOW, cwd=get_dir(self.game.path),
check=True, stdout=subprocess.PIPE)
messagebox.showinfo(self.translate("End"), self.translate("The mod has been installed !"))
except: self.log_error()
finally: self.Progress(show=False)
t = Thread(target=func)
t.setDaemon(True)
t.start()
return t
# TODO: use wszst module instead of subprocess

View file

@ -1,8 +1,9 @@
import traceback import traceback
from tkinter import messagebox
def log_error(self): def log_error(func):
try: func()
except Exception:
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")
messagebox.showerror(self.translate("Error"), self.translate("An error occured", " :", "\n", error, "\n\n")) # messagebox.showerror(self.translate("Error"), self.translate("An error occured", " :", "\n", error, "\n\n"))

View file

@ -1,32 +0,0 @@
import json
import os
default_option = {
"language": "en",
"format": "FST",
"disable_download": False,
"del_track_after_conv": False,
"dont_check_for_update": False,
"dont_check_track_sha1": False,
"process_track": 8
}
with open("./translation.json", encoding="utf-8") as f:
translation_dict = json.load(f)
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)
for option_key, option_value in default_option.items():
if not(option_key in self.option):
self.option[option_key] = option_value

View file

@ -1,21 +1,7 @@
from .definition import *
from tkinter import messagebox from tkinter import messagebox
import subprocess
import shutil
import os import os
def patch_autoadd(self):
if os.path.exists("./file/auto-add"): shutil.rmtree("./file/auto-add")
if not os.path.exists(self.game.path + "/tmp/"): os.makedirs(self.game.path + "/tmp/")
subprocess.run(["./tools/szs/wszst", "AUTOADD", get_nodir(self.game.path) + "/files/Race/Course/",
"--DEST", get_nodir(self.game.path) + "/tmp/auto-add/"],
creationflags=CREATE_NO_WINDOW, cwd=get_dir(self.game.path),
check=True, stdout=subprocess.PIPE)
shutil.move(self.game.path + "/tmp/auto-add/", "./file/auto-add/")
shutil.rmtree(self.game.path + "/tmp/")
def patch_track(self): def patch_track(self):
max_process = self.intvar_process_track.get() max_process = self.intvar_process_track.get()
process_list = {} process_list = {}
@ -35,8 +21,9 @@ def patch_track(self):
if os.path.getsize(_track) < 1000: # File under this size are corrupted if os.path.getsize(_track) < 1000: # File under this size are corrupted
os.remove(_track) os.remove(_track)
if not self.boolvar_disable_download.get():
while True: while True:
download_returncode = self.get_github_file(track.file_wu8) download_returncode = track.download_wu8()
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
@ -94,7 +81,7 @@ def patch_track(self):
else: # if the error max hasn't been reach else: # if the error max hasn't been reach
messagebox.showwarning( messagebox.showwarning(
self.translate("Warning"), self.translate("Warning"),
self.translate("The track", " ", get_track_wu8(track_file), self.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:

View file

@ -1,4 +1,7 @@
from .option import translation_dict import json
with open("./translation.json", encoding="utf-8") as f:
translation_dict = json.load(f)
def translate(self, *texts, lang=None): def translate(self, *texts, lang=None):

View file

@ -32,6 +32,11 @@ def normalize(src_file: str, dest_dir: str = "./file/Track/", dest_name: str = "
def extract(file: str, dest_dir: str): def 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
:return: ?
"""
subprocess.run(["./tools/wit/wit", "EXTRACT", get_nodir(file), "--DEST", dest_dir], subprocess.run(["./tools/wit/wit", "EXTRACT", get_nodir(file), "--DEST", dest_dir],
creationflags=CREATE_NO_WINDOW, cwd=get_dir(file)) creationflags=CREATE_NO_WINDOW, cwd=get_dir(file))
@ -51,3 +56,9 @@ def wlect_patch(file: str,
ctfile_path: str = "./tmp/CTFILE.txt", ctfile_path: str = "./tmp/CTFILE.txt",
lpar_path: str = "./tmp/lpar-default.txt"): lpar_path: str = "./tmp/lpar-default.txt"):
pass pass
def edit(): pass
def autoadd(): pass