mirror of
https://github.com/Faraphel/Atlas-Install.git
synced 2025-07-03 03:08:29 +02:00
created a Option class, merged get_github_file with Track and install_mod with Game
This commit is contained in:
parent
2f171311bc
commit
2990b1a652
14 changed files with 432 additions and 446 deletions
7
main.pyw
7
main.pyw
|
@ -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
|
||||||
|
|
||||||
|
|
140
source/Game.py
140
source/Game.py
|
@ -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
34
source/Option.py
Normal 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()
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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}",
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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"))
|
|
@ -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
|
|
|
@ -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:
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
Loading…
Reference in a new issue