mirror of
https://github.com/Faraphel/Atlas-Install.git
synced 2025-07-02 02:38:30 +02:00
readded a better track selection filter (advanced menu), fixed an issue with tracks always renormalizing, added a default sort in ct_config, added a Common class to reunite all component with more readability and less weird code, splited the Gui into a directory
This commit is contained in:
parent
85e36c461c
commit
b3d5af63ed
10 changed files with 463 additions and 177 deletions
|
@ -1804,6 +1804,7 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"default_sort": "name",
|
||||
"tracks_list":[
|
||||
{
|
||||
"name":"4IT Clown's Road",
|
||||
|
|
6
main.pyw
6
main.pyw
|
@ -1,4 +1,4 @@
|
|||
from source.Gui import Gui
|
||||
from source.Common import Common
|
||||
|
||||
gui = Gui()
|
||||
gui.root.mainloop()
|
||||
common = Common()
|
||||
common.mainloop()
|
||||
|
|
|
@ -9,10 +9,10 @@ from source.Track import Track, get_trackdata_from_json
|
|||
|
||||
class CT_Config:
|
||||
def __init__(self, version: str = None, name: str = None, nickname: str = None,
|
||||
game_variant: str = "01", gui=None, region: int = None, cheat_region: int = None,
|
||||
game_variant: str = "01", region: int = None, cheat_region: int = None,
|
||||
tags_color: dict = None, prefix_list: list = None, suffix_list: list = None,
|
||||
tag_retro: str = "Retro", default_track: Track = None, pack_path: str = "",
|
||||
file_process: dict = None, file_structure: dict = None):
|
||||
file_process: dict = None, file_structure: dict = None, default_sort: str = "name"):
|
||||
|
||||
self.version = version
|
||||
self.name = name
|
||||
|
@ -23,7 +23,6 @@ class CT_Config:
|
|||
|
||||
self.ordered_cups = []
|
||||
self.unordered_tracks = []
|
||||
self.gui = gui
|
||||
|
||||
self.tags_color = tags_color if tags_color else {}
|
||||
self.prefix_list = prefix_list if tags_color else []
|
||||
|
@ -32,10 +31,15 @@ class CT_Config:
|
|||
self.default_track = default_track
|
||||
|
||||
self.pack_path = pack_path
|
||||
self.sort_track_attr = default_sort
|
||||
|
||||
self.file_process = file_process
|
||||
self.file_structure = file_structure
|
||||
|
||||
self.filter_track_selection = lambda track: True
|
||||
self.filter_track_highlight = lambda track: False
|
||||
self.filter_track_random_new = lambda track: getattr(track, "new", False)
|
||||
|
||||
def add_ordered_cup(self, cup: Cup) -> None:
|
||||
"""
|
||||
add a cup to the config
|
||||
|
@ -53,9 +57,11 @@ class CT_Config:
|
|||
def unordered_tracks_to_cup(self):
|
||||
track_in_cup: int = 4
|
||||
|
||||
for cup_id, track_id in enumerate(range(0, len(self.unordered_tracks), track_in_cup), start=1):
|
||||
track_selection = list(filter(self.filter_track_selection, self.unordered_tracks))
|
||||
|
||||
for cup_id, track_id in enumerate(range(0, len(track_selection), track_in_cup), start=1):
|
||||
cup = Cup(id=cup_id, name=f"CT{cup_id}")
|
||||
for index, track in enumerate(self.unordered_tracks[track_id:track_id + track_in_cup]):
|
||||
for index, track in enumerate(track_selection[track_id:track_id + track_in_cup]):
|
||||
cup.tracks[index] = track
|
||||
yield cup
|
||||
|
||||
|
@ -77,8 +83,13 @@ class CT_Config:
|
|||
ctfile.write(header); rctfile.write(header)
|
||||
|
||||
# all cups
|
||||
kwargs = {
|
||||
"filter_highlight": self.filter_track_highlight,
|
||||
"filter_random_new": self.filter_track_random_new,
|
||||
"ct_config": self
|
||||
}
|
||||
|
||||
for cup in self.get_all_cups():
|
||||
kwargs = {"highlight_version": highlight_version, "ct_config": self}
|
||||
ctfile.write(cup.get_ctfile(race=False, **kwargs))
|
||||
rctfile.write(cup.get_ctfile(race=True, **kwargs))
|
||||
|
||||
|
@ -183,8 +194,9 @@ class CT_Config:
|
|||
self.version = ctconfig_json.get("version")
|
||||
|
||||
if "name" in ctconfig_json: self.name = ctconfig_json["name"]
|
||||
self.nickname = ctconfig_json["nickname"] if "nickname" in ctconfig_json else self.name
|
||||
if "game_variant" in ctconfig_json: self.game_variant = ctconfig_json["game_variant"]
|
||||
if "default_sort" in ctconfig_json: self.default_sort = ctconfig_json["default_sort"]
|
||||
self.nickname = ctconfig_json["nickname"] if "nickname" in ctconfig_json else self.name
|
||||
|
||||
for param in ["region", "cheat_region", "tags_color", "prefix_list", "suffix_list", "tag_retro"]:
|
||||
setattr(self, param, ctconfig_json.get(param))
|
||||
|
|
21
source/Common.py
Normal file
21
source/Common.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
from source.CT_Config import CT_Config
|
||||
from source.Option import Option
|
||||
from source.Game import Game
|
||||
from source.Gui.Main import Main
|
||||
from source.Gui.TrackSelection import TrackSelection
|
||||
|
||||
class Common:
|
||||
def __init__(self):
|
||||
"""
|
||||
Common allow to store multiple object that need each other and still make the code readable enough without
|
||||
having to access an object with some obscure way
|
||||
"""
|
||||
|
||||
self.option = Option().load_from_file("./option.json")
|
||||
self.ct_config = CT_Config()
|
||||
self.game = Game(common=self)
|
||||
|
||||
self.gui_main = Main(common=self)
|
||||
|
||||
def show_gui_track_configuration(self): TrackSelection(common=self)
|
||||
def mainloop(self): self.gui_main.mainloop()
|
175
source/Game.py
175
source/Game.py
|
@ -4,53 +4,19 @@ import shutil
|
|||
import glob
|
||||
import json
|
||||
|
||||
from source.CT_Config import CT_Config
|
||||
from source.definition import *
|
||||
from source.wszst import *
|
||||
from source.Error import *
|
||||
|
||||
|
||||
class NoGui:
|
||||
"""
|
||||
'fake' gui if no gui are used for compatibility.
|
||||
"""
|
||||
|
||||
class NoButton:
|
||||
def grid(self, *args, **kwargs): pass
|
||||
|
||||
def config(self, *args, **kwargs): pass
|
||||
|
||||
class NoVariable:
|
||||
def __init__(self, value=None):
|
||||
self.value = None
|
||||
|
||||
def set(self, value):
|
||||
self.value = value
|
||||
|
||||
def get(self):
|
||||
return self.value
|
||||
|
||||
def progress(*args, **kwargs): print(args, kwargs)
|
||||
|
||||
def translate(*args, **kwargs): return ""
|
||||
|
||||
def log_error(*args, **kwargs): print(args, kwargs)
|
||||
|
||||
is_dev_version = False
|
||||
button_install_mod = NoButton()
|
||||
stringvar_game_format = NoVariable()
|
||||
intvar_process_track = NoVariable()
|
||||
boolvar_dont_check_track_sha1 = NoVariable()
|
||||
|
||||
|
||||
class Game:
|
||||
def __init__(self, path: str = "", region_ID: str = "P", game_ID: str = "RMCP01", gui=None):
|
||||
def __init__(self, common, path: str = "", region_ID: str = "P", game_ID: str = "RMCP01"):
|
||||
"""
|
||||
Class about the game code and its treatment.
|
||||
:param path: path of the game file / directory
|
||||
:param region_ID: game's region id (P for PAL, K for KOR, ...)
|
||||
:param game_ID: game's id (RMCP01 for PAL, ...)
|
||||
:param gui: gui class used by the program
|
||||
:param common: common class to access all other element
|
||||
"""
|
||||
if not os.path.exists(path) and path: raise InvalidGamePath()
|
||||
self.extension = None
|
||||
|
@ -59,8 +25,7 @@ class Game:
|
|||
self.region = region_id_to_name[region_ID]
|
||||
self.region_ID = region_ID
|
||||
self.game_ID = game_ID
|
||||
self.gui = gui if gui else NoGui
|
||||
self.ctconfig = CT_Config(gui=gui)
|
||||
self.common = common
|
||||
|
||||
def set_path(self, path: str) -> None:
|
||||
"""
|
||||
|
@ -76,17 +41,17 @@ class Game:
|
|||
:param format: game format (ISO, WBFS, ...)
|
||||
"""
|
||||
if format in ["ISO", "WBFS", "CISO"]:
|
||||
path_game_format: str = os.path.realpath(self.path + f"/../{self.ctconfig.nickname} v{self.ctconfig.version}." + format.lower())
|
||||
path_game_format: str = os.path.realpath(self.path + f"/../{self.common.ct_config.nickname} v{self.common.ct_config.version}." + format.lower())
|
||||
wit.copy(src_path=self.path, dst_path=path_game_format, format=format)
|
||||
shutil.rmtree(self.path)
|
||||
self.path = path_game_format
|
||||
|
||||
self.gui.progress(statut=self.gui.translate("Changing game's ID"), add=1)
|
||||
self.common.gui_main.progress(statut=self.common.gui_main.translate("Changing game's ID"), add=1)
|
||||
wit.edit(
|
||||
file=self.path,
|
||||
region_ID=self.region_ID,
|
||||
game_variant=self.ctconfig.game_variant,
|
||||
name=f"{self.ctconfig.name} {self.ctconfig.version}"
|
||||
game_variant=self.common.ct_config.game_variant,
|
||||
name=f"{self.common.ct_config.name} {self.common.ct_config.version}"
|
||||
)
|
||||
|
||||
def extract(self) -> None:
|
||||
|
@ -100,7 +65,7 @@ class Game:
|
|||
# Fiding a directory name that doesn't already exist
|
||||
path_dir = get_next_available_dir(
|
||||
parent_dir=self.path + f"/../",
|
||||
dir_name=f"{self.ctconfig.nickname} v{self.ctconfig.version}"
|
||||
dir_name=f"{self.common.ct_config.nickname} v{self.common.ct_config.version}"
|
||||
)
|
||||
|
||||
wit.extract(file=self.path, dst_dir=path_dir)
|
||||
|
@ -128,7 +93,7 @@ class Game:
|
|||
count all the step patching subfile will take (for the progress bar)
|
||||
:return: number of step estimated
|
||||
"""
|
||||
with open(f"{self.ctconfig.pack_path}/file_structure.json") as f:
|
||||
with open(f"{self.common.ct_config.pack_path}/file_structure.json") as f:
|
||||
fs = json.load(f)
|
||||
|
||||
# This part is used to estimate the max_step
|
||||
|
@ -160,11 +125,11 @@ class Game:
|
|||
"""
|
||||
patch subfile as indicated in the file_structure.json file (for file structure)
|
||||
"""
|
||||
with open(f"{self.ctconfig.pack_path}/file_structure.json") as f:
|
||||
with open(f"{self.common.ct_config.pack_path}/file_structure.json") as f:
|
||||
fs = json.load(f)
|
||||
|
||||
extracted_file = []
|
||||
self.gui.progress(show=True, indeter=False, statut=self.gui.translate("Modifying subfile..."), add=1)
|
||||
self.common.gui_main.progress(show=True, indeter=False, statut=self.common.gui_main.translate("Modifying subfile..."), add=1)
|
||||
|
||||
def replace_file(path, file, subpath="/") -> None:
|
||||
"""
|
||||
|
@ -173,10 +138,10 @@ class Game:
|
|||
:param file: file to replace
|
||||
:param subpath: directory between .szs file and file inside to replace
|
||||
"""
|
||||
self.gui.progress(statut=self.gui.translate("Editing", "\n", get_nodir(path)), add=1)
|
||||
self.common.gui_main.progress(statut=self.common.gui_main.translate("Editing", "\n", get_nodir(path)), add=1)
|
||||
extension = get_extension(path)
|
||||
|
||||
source_file = f"{self.ctconfig.pack_path}/file/{file}"
|
||||
source_file = f"{self.common.ct_config.pack_path}/file/{file}"
|
||||
dest_file = path
|
||||
|
||||
if extension == "szs":
|
||||
|
@ -205,7 +170,7 @@ class Game:
|
|||
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)
|
||||
self.common.gui_main.progress(statut=self.common.gui_main.translate("Recompilating", "\n", get_nodir(file)), add=1)
|
||||
szs.create(file=file)
|
||||
shutil.rmtree(file + ".d", ignore_errors=True)
|
||||
|
||||
|
@ -213,9 +178,9 @@ class Game:
|
|||
"""
|
||||
copy MyStuff directory into the game *before* patching the game
|
||||
"""
|
||||
self.gui.progress(show=True, indeter=False, statut=self.gui.translate("Copying MyStuff..."), add=1)
|
||||
self.common.gui_main.progress(show=True, indeter=False, statut=self.common.gui_main.translate("Copying MyStuff..."), add=1)
|
||||
|
||||
mystuff_folder = self.gui.stringvar_mystuff_folder.get()
|
||||
mystuff_folder = self.common.gui_main.stringvar_mystuff_folder.get()
|
||||
if mystuff_folder and mystuff_folder != "None":
|
||||
|
||||
# replace game's file by files with the same name in the MyStuff root
|
||||
|
@ -241,27 +206,27 @@ class Game:
|
|||
"""
|
||||
patch the main.dol file to allow the addition of LECODE.bin file
|
||||
"""
|
||||
self.gui.progress(statut=self.gui.translate("Patch main.dol"), add=1)
|
||||
self.common.gui_main.progress(statut=self.common.gui_main.translate("Patch main.dol"), add=1)
|
||||
|
||||
region_id = self.ctconfig.region if self.gui.is_using_official_config() else self.ctconfig.cheat_region
|
||||
region_id = self.common.ct_config.region if self.common.gui_main.is_using_official_config() else self.common.ct_config.cheat_region
|
||||
wstrt.patch(path=self.path, region_id=region_id)
|
||||
|
||||
def install_patch_lecode(self) -> None:
|
||||
"""
|
||||
configure and add the LECODE.bin file to the mod
|
||||
"""
|
||||
self.gui.progress(statut=self.gui.translate("Patch lecode.bin"), add=1)
|
||||
self.common.gui_main.progress(statut=self.common.gui_main.translate("Patch lecode.bin"), add=1)
|
||||
|
||||
lpar_path = self.ctconfig.file_process["placement"].get("lpar_dir")
|
||||
lpar_path = self.common.ct_config.file_process["placement"].get("lpar_dir")
|
||||
if not lpar_path: f""
|
||||
lpar_path = (
|
||||
f"{self.ctconfig.pack_path}/file/{lpar_path}/"
|
||||
f"lpar-{'debug' if self.gui.boolvar_use_debug_mode.get() else 'normal'}.txt"
|
||||
f"{self.common.ct_config.pack_path}/file/{lpar_path}/"
|
||||
f"lpar-{'debug' if self.common.gui_main.boolvar_use_debug_mode.get() else 'normal'}.txt"
|
||||
)
|
||||
|
||||
lecode_file = self.ctconfig.file_process["placement"].get("lecode_bin_dir")
|
||||
lecode_file = self.common.ct_config.file_process["placement"].get("lecode_bin_dir")
|
||||
if not lecode_file: lecode_file = ""
|
||||
lecode_file = f"{self.ctconfig.pack_path}/file/{lecode_file}/lecode-{self.region}.bin"
|
||||
lecode_file = f"{self.common.ct_config.pack_path}/file/{lecode_file}/lecode-{self.region}.bin"
|
||||
|
||||
lec.patch(
|
||||
lecode_file=lecode_file,
|
||||
|
@ -277,8 +242,8 @@ class Game:
|
|||
"""
|
||||
convert the rom to the selected game format
|
||||
"""
|
||||
output_format = self.gui.stringvar_game_format.get()
|
||||
self.gui.progress(statut=self.gui.translate("Converting to", " ", output_format), add=1)
|
||||
output_format = self.common.gui_main.stringvar_game_format.get()
|
||||
self.common.gui_main.progress(statut=self.common.gui_main.translate("Converting to", " ", output_format), add=1)
|
||||
self.convert_to(output_format)
|
||||
|
||||
def install_mod(self) -> None:
|
||||
|
@ -289,22 +254,22 @@ class Game:
|
|||
max_step = 5 + self.count_patch_subfile_operation()
|
||||
# PATCH main.dol and PATCH lecode.bin, converting, changing ID, copying MyStuff Folder
|
||||
|
||||
self.gui.progress(statut=self.gui.translate("Installing mod..."), max=max_step, step=0)
|
||||
self.common.gui_main.progress(statut=self.common.gui_main.translate("Installing mod..."), max=max_step, step=0)
|
||||
self.install_copy_mystuff()
|
||||
self.install_patch_subfile()
|
||||
self.install_patch_maindol()
|
||||
self.install_patch_lecode()
|
||||
self.install_convert_rom()
|
||||
|
||||
messagebox.showinfo(self.gui.translate("End"), self.gui.translate("The mod have been installed !"))
|
||||
messagebox.showinfo(self.common.gui_main.translate("End"), self.common.gui_main.translate("The mod have been installed !"))
|
||||
|
||||
except Exception as e:
|
||||
self.gui.log_error()
|
||||
self.common.gui_main.log_error()
|
||||
raise e
|
||||
|
||||
finally:
|
||||
self.gui.progress(show=False)
|
||||
self.gui.quit()
|
||||
self.common.gui_main.progress(show=False)
|
||||
self.common.gui_main.quit()
|
||||
|
||||
def patch_autoadd(self, auto_add_dir: str = "./file/auto-add") -> None:
|
||||
"""
|
||||
|
@ -337,15 +302,15 @@ class Game:
|
|||
track_id = bmgtrack[start_track_id:start_track_id + 3]
|
||||
if track_id[1] in "1234": # if the track is a original track from the wii
|
||||
prefix = "Wii"
|
||||
if prefix in self.ctconfig.tags_color:
|
||||
prefix = "\\\\c{" + self.ctconfig.tags_color[prefix] + "}" + prefix + "\\\\c{off}"
|
||||
if prefix in self.common.ct_config.tags_color:
|
||||
prefix = "\\\\c{" + self.common.ct_config.tags_color[prefix] + "}" + prefix + "\\\\c{off}"
|
||||
prefix += " "
|
||||
|
||||
elif track_id[1] in "5678": # if the track is a retro track from the original game
|
||||
prefix, *track_name = track_name.split(" ")
|
||||
track_name = " ".join(track_name)
|
||||
if prefix in self.ctconfig.tags_color:
|
||||
prefix = "\\\\c{" + self.ctconfig.tags_color[prefix] + "}" + prefix + "\\\\c{off}"
|
||||
if prefix in self.common.ct_config.tags_color:
|
||||
prefix = "\\\\c{" + self.common.ct_config.tags_color[prefix] + "}" + prefix + "\\\\c{off}"
|
||||
prefix += " "
|
||||
|
||||
track_id = hex(bmgID_track_move[track_id])[2:]
|
||||
|
@ -364,15 +329,15 @@ class Game:
|
|||
"""
|
||||
|
||||
bmg_replacement = {
|
||||
"MOD_NAME": self.ctconfig.name,
|
||||
"MOD_NICKNAME": self.ctconfig.nickname,
|
||||
"MOD_VERSION": self.ctconfig.version,
|
||||
"MOD_CUSTOMIZED": "" if self.gui.is_using_official_config() else "(custom)",
|
||||
"MOD_NAME": self.common.ct_config.name,
|
||||
"MOD_NICKNAME": self.common.ct_config.nickname,
|
||||
"MOD_VERSION": self.common.ct_config.version,
|
||||
"MOD_CUSTOMIZED": "" if self.common.gui_main.is_using_official_config() else "(custom)",
|
||||
"ONLINE_SERVICE": "Wiimmfi",
|
||||
}
|
||||
|
||||
bmglang = gamefile[-len("E.txt"):-len(".txt")] # Langue du fichier
|
||||
self.gui.progress(statut=self.gui.translate("Patching text", " ", bmglang), add=1)
|
||||
self.common.gui_main.progress(statut=self.common.gui_main.translate("Patching text", " ", bmglang), add=1)
|
||||
|
||||
szs.extract(file=gamefile)
|
||||
|
||||
|
@ -401,7 +366,7 @@ class Game:
|
|||
:param bmg_language: language of the bmg file
|
||||
:return: the replaced bmg file
|
||||
"""
|
||||
with open(f"{self.ctconfig.pack_path}/file_process.json", encoding="utf8") as fp_file:
|
||||
with open(f"{self.common.ct_config.pack_path}/file_process.json", encoding="utf8") as fp_file:
|
||||
file_process = json.load(fp_file)
|
||||
|
||||
for bmg_process in file_process["bmg"]:
|
||||
|
@ -440,8 +405,8 @@ class Game:
|
|||
bmg.encode(file)
|
||||
os.remove(file)
|
||||
|
||||
bmg_dir = self.ctconfig.file_process["placement"].get("bmg_patch_dir")
|
||||
bmg_dir = f"{self.ctconfig.pack_path}/file/{bmg_dir if bmg_dir else ''}"
|
||||
bmg_dir = self.common.ct_config.file_process["placement"].get("bmg_patch_dir")
|
||||
bmg_dir = f"{self.common.ct_config.pack_path}/file/{bmg_dir if bmg_dir else ''}"
|
||||
os.makedirs(get_dir(bmg_dir), exist_ok=True)
|
||||
|
||||
save_bmg(f"{bmg_dir}/Menu_{bmglang}.txt", process_bmg_replacement(bmgmenu, bmglang))
|
||||
|
@ -457,18 +422,18 @@ class Game:
|
|||
Prepare all files to install the mod (track, bmg text, descriptive image, ...)
|
||||
"""
|
||||
try:
|
||||
os.makedirs(f"{self.ctconfig.pack_path}/file/Track-WU8/", exist_ok=True)
|
||||
os.makedirs(f"{self.common.ct_config.pack_path}/file/Track-WU8/", exist_ok=True)
|
||||
|
||||
max_step = len(self.ctconfig.file_process["img_encode"]) + \
|
||||
len(self.ctconfig.all_tracks) + \
|
||||
max_step = len(self.common.ct_config.file_process["img_encode"]) + \
|
||||
len(self.common.ct_config.all_tracks) + \
|
||||
3 + \
|
||||
len("EGFIS")
|
||||
|
||||
self.gui.progress(show=True, indeter=False, statut=self.gui.translate("Converting files"),
|
||||
self.common.gui_main.progress(show=True, indeter=False, statut=self.common.gui_main.translate("Converting files"),
|
||||
max=max_step, step=0)
|
||||
self.gui.progress(statut=self.gui.translate("Configurating LE-CODE"), add=1)
|
||||
self.ctconfig.create_ctfile(
|
||||
highlight_version=self.gui.stringvar_mark_track_from_version.get(),
|
||||
self.common.gui_main.progress(statut=self.common.gui_main.translate("Configurating LE-CODE"), add=1)
|
||||
self.common.ct_config.create_ctfile(
|
||||
highlight_version=self.common.gui_main.stringvar_mark_track_from_version.get(),
|
||||
)
|
||||
|
||||
self.generate_cticons()
|
||||
|
@ -479,37 +444,37 @@ class Game:
|
|||
self.patch_tracks()
|
||||
|
||||
except Exception as e:
|
||||
self.gui.log_error()
|
||||
self.common.gui_main.log_error()
|
||||
raise e
|
||||
|
||||
finally:
|
||||
self.gui.progress(show=False)
|
||||
self.common.gui_main.progress(show=False)
|
||||
|
||||
def generate_cticons(self):
|
||||
file = self.ctconfig.file_process["placement"].get("ct_icons")
|
||||
file = self.common.ct_config.file_process["placement"].get("ct_icons")
|
||||
if not file: file = "ct_icons.tpl.png"
|
||||
file = f"{self.ctconfig.pack_path}/file/{file}"
|
||||
file = f"{self.common.ct_config.pack_path}/file/{file}"
|
||||
|
||||
os.makedirs(get_dir(file), exist_ok=True)
|
||||
self.ctconfig.get_cticon().save(file)
|
||||
self.common.ct_config.get_cticon().save(file)
|
||||
|
||||
def patch_image(self) -> None:
|
||||
"""
|
||||
Convert .png image into the format wrote in convert_file
|
||||
"""
|
||||
|
||||
image_amount = len(self.ctconfig.file_process["img_encode"])
|
||||
image_amount = len(self.common.ct_config.file_process["img_encode"])
|
||||
|
||||
for i, (file, data) in enumerate(self.ctconfig.file_process["img_encode"].items()):
|
||||
self.gui.progress(
|
||||
statut=self.gui.translate("Converting images") + f"\n({i + 1}/{image_amount}) {file}",
|
||||
for i, (file, data) in enumerate(self.common.ct_config.file_process["img_encode"].items()):
|
||||
self.common.gui_main.progress(
|
||||
statut=self.common.gui_main.translate("Converting images") + f"\n({i + 1}/{image_amount}) {file}",
|
||||
add=1
|
||||
)
|
||||
|
||||
img.encode(
|
||||
file=f"{self.ctconfig.pack_path}/file/{file}",
|
||||
file=f"{self.common.ct_config.pack_path}/file/{file}",
|
||||
format=data["format"],
|
||||
dest_file=f"{self.ctconfig.pack_path}/file/{data['dest']}" if "dest" in data else None
|
||||
dest_file=f"{self.common.ct_config.pack_path}/file/{data['dest']}" if "dest" in data else None
|
||||
)
|
||||
|
||||
def generate_image(self, generator: dict) -> Image.Image:
|
||||
|
@ -546,7 +511,7 @@ class Game:
|
|||
tuple(layer["color"]) if "color" in layer else 0
|
||||
)
|
||||
if layer["type"] == "image":
|
||||
layer_image = Image.open(f'{self.ctconfig.pack_path}/file/{layer["path"]}')
|
||||
layer_image = Image.open(f'{self.common.ct_config.pack_path}/file/{layer["path"]}')
|
||||
layer_image = layer_image.resize(get_layer_size(layer)).convert("RGBA")
|
||||
image.paste(
|
||||
layer_image,
|
||||
|
@ -555,7 +520,7 @@ class Game:
|
|||
)
|
||||
if layer["type"] == "text":
|
||||
font = ImageFont.truetype(
|
||||
font=f'{self.ctconfig.pack_path}/file/{layer["font"]}' if "font" in layer else None,
|
||||
font=f'{self.common.ct_config.pack_path}/file/{layer["font"]}' if "font" in layer else None,
|
||||
size=int(layer["text_size"] * generator["height"]) if "text_size" in layer else 10,
|
||||
)
|
||||
draw.text(
|
||||
|
@ -568,8 +533,8 @@ class Game:
|
|||
return image
|
||||
|
||||
def generate_all_image(self) -> None:
|
||||
for file, generator in self.ctconfig.file_process["img_generator"].items():
|
||||
file = f"{self.ctconfig.pack_path}/file/{file}"
|
||||
for file, generator in self.common.ct_config.file_process["img_generator"].items():
|
||||
file = f"{self.common.ct_config.pack_path}/file/{file}"
|
||||
os.makedirs(get_dir(file), exist_ok=True)
|
||||
self.generate_image(generator).save(file)
|
||||
|
||||
|
@ -577,7 +542,7 @@ class Game:
|
|||
"""
|
||||
Download track's wu8 file and convert them to szs
|
||||
"""
|
||||
max_process = self.gui.intvar_process_track.get()
|
||||
max_process = self.common.gui_main.intvar_process_track.get()
|
||||
thread_list = {}
|
||||
error_count, error_max = 0, 3
|
||||
|
||||
|
@ -607,16 +572,16 @@ class Game:
|
|||
|
||||
return bool(thread_list)
|
||||
|
||||
total_track = self.ctconfig.get_tracks_count()
|
||||
self.gui.progress(max=total_track, indeter=False, show=True)
|
||||
total_track = self.common.ct_config.get_tracks_count()
|
||||
self.common.gui_main.progress(max=total_track, indeter=False, show=True)
|
||||
|
||||
for i, track in enumerate(self.ctconfig.get_tracks()):
|
||||
for i, track in enumerate(self.common.ct_config.get_tracks()):
|
||||
while error_count <= error_max:
|
||||
if len(thread_list) < max_process:
|
||||
thread_list[track.sha1] = Thread(target=add_process, args=[track])
|
||||
thread_list[track.sha1].setDaemon(True)
|
||||
thread_list[track.sha1].start()
|
||||
self.gui.progress(statut=self.gui.translate("Converting tracks", f"\n({i + 1}/{total_track})\n",
|
||||
self.common.gui_main.progress(statut=self.common.gui_main.translate("Converting tracks", f"\n({i + 1}/{total_track})\n",
|
||||
"\n".join(thread_list.keys())), add=1)
|
||||
break
|
||||
clean_process()
|
||||
|
|
0
source/Gui/CheatManager.py
Normal file
0
source/Gui/CheatManager.py
Normal file
|
@ -8,9 +8,7 @@ import zipfile
|
|||
import glob
|
||||
import json
|
||||
|
||||
from source.Game import Game, RomAlreadyPatched, InvalidGamePath, InvalidFormat
|
||||
from source.Option import Option
|
||||
|
||||
from source.Error import *
|
||||
from source.definition import *
|
||||
|
||||
|
||||
|
@ -18,8 +16,8 @@ with open("./translation.json", encoding="utf-8") as f:
|
|||
translation_dict = json.load(f)
|
||||
|
||||
|
||||
class Gui:
|
||||
def __init__(self) -> None:
|
||||
class Main:
|
||||
def __init__(self, common) -> None:
|
||||
"""
|
||||
Initialize program Gui
|
||||
"""
|
||||
|
@ -27,9 +25,7 @@ class Gui:
|
|||
self.root.resizable(False, False)
|
||||
self.root.iconbitmap(bitmap="./icon.ico")
|
||||
|
||||
self.option = Option().load_from_file("./option.json")
|
||||
self.game = Game(gui=self)
|
||||
|
||||
self.common = common
|
||||
self.menu_bar = None
|
||||
|
||||
self.available_packs = self.get_available_packs()
|
||||
|
@ -42,10 +38,10 @@ class Gui:
|
|||
|
||||
self.is_dev_version = False # Is this installer version a dev ?
|
||||
self.stringvar_ctconfig = StringVar(value=self.available_packs[0])
|
||||
self.stringvar_language = StringVar(value=self.option.language)
|
||||
self.stringvar_game_format = StringVar(value=self.option.format)
|
||||
self.boolvar_dont_check_for_update = BooleanVar(value=self.option.dont_check_for_update)
|
||||
self.intvar_process_track = IntVar(value=self.option.process_track)
|
||||
self.stringvar_language = StringVar(value=self.common.option.language)
|
||||
self.stringvar_game_format = StringVar(value=self.common.option.format)
|
||||
self.boolvar_dont_check_for_update = BooleanVar(value=self.common.option.dont_check_for_update)
|
||||
self.intvar_process_track = IntVar(value=self.common.option.process_track)
|
||||
|
||||
self.root.title(self.translate("MKWFaraphel Installer"))
|
||||
|
||||
|
@ -104,9 +100,9 @@ class Gui:
|
|||
game_path = entry_game_path.get()
|
||||
if not os.path.exists(game_path): raise InvalidGamePath
|
||||
|
||||
self.game.set_path(game_path)
|
||||
self.common.game.set_path(game_path)
|
||||
self.progress(show=True, indeter=True, statut=self.translate("Extracting the game..."))
|
||||
self.game.extract()
|
||||
self.common.game.extract()
|
||||
|
||||
except RomAlreadyPatched:
|
||||
messagebox.showerror(self.translate("Error"), self.translate("This game is already modded"))
|
||||
|
@ -126,8 +122,8 @@ class Gui:
|
|||
@in_thread
|
||||
def do_everything():
|
||||
use_path()
|
||||
self.game.patch_file()
|
||||
self.game.install_mod()
|
||||
self.common.game.patch_file()
|
||||
self.common.game.install_mod()
|
||||
|
||||
self.button_do_everything = Button(
|
||||
self.frame_game_path_action,
|
||||
|
@ -152,13 +148,13 @@ class Gui:
|
|||
label="Français",
|
||||
variable=self.stringvar_language,
|
||||
value="fr",
|
||||
command=lambda: self.option.edit("language", "fr", need_restart=True)
|
||||
command=lambda: self.common.option.edit("language", "fr", need_restart=True)
|
||||
)
|
||||
self.menu_language.add_radiobutton(
|
||||
label="English",
|
||||
variable=self.stringvar_language,
|
||||
value="en",
|
||||
command=lambda: self.option.edit("language", "en", need_restart=True)
|
||||
command=lambda: self.common.option.edit("language", "en", need_restart=True)
|
||||
)
|
||||
|
||||
# OUTPUT FORMAT MENU
|
||||
|
@ -168,25 +164,25 @@ class Gui:
|
|||
label=self.translate("FST (Directory)"),
|
||||
variable=self.stringvar_game_format,
|
||||
value="FST", command=lambda:
|
||||
self.option.edit("format", "FST")
|
||||
self.common.option.edit("format", "FST")
|
||||
)
|
||||
self.menu_format.add_radiobutton(
|
||||
label="ISO",
|
||||
variable=self.stringvar_game_format,
|
||||
value="ISO",
|
||||
command=lambda: self.option.edit("format", "ISO")
|
||||
command=lambda: self.common.option.edit("format", "ISO")
|
||||
)
|
||||
self.menu_format.add_radiobutton(
|
||||
label="CISO",
|
||||
variable=self.stringvar_game_format,
|
||||
value="CISO",
|
||||
command=lambda: self.option.edit("format", "CISO")
|
||||
command=lambda: self.common.option.edit("format", "CISO")
|
||||
)
|
||||
self.menu_format.add_radiobutton(
|
||||
label="WBFS",
|
||||
variable=self.stringvar_game_format,
|
||||
value="WBFS",
|
||||
command=lambda: self.option.edit("format", "WBFS")
|
||||
command=lambda: self.common.option.edit("format", "WBFS")
|
||||
)
|
||||
|
||||
# ADVANCED MENU
|
||||
|
@ -196,7 +192,7 @@ class Gui:
|
|||
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(
|
||||
command=lambda: self.common.option.edit(
|
||||
"dont_check_for_update",
|
||||
self.boolvar_dont_check_for_update
|
||||
)
|
||||
|
@ -215,29 +211,36 @@ class Gui:
|
|||
self.menu_conv_process.add_radiobutton(
|
||||
label=self.translate("1 ", "process"),
|
||||
variable=self.intvar_process_track, value=1,
|
||||
command=lambda: self.option.edit("process_track", 1)
|
||||
command=lambda: self.common.option.edit("process_track", 1)
|
||||
)
|
||||
self.menu_conv_process.add_radiobutton(
|
||||
label=self.translate("2 ", "process"),
|
||||
variable=self.intvar_process_track, value=2,
|
||||
command=lambda: self.option.edit("process_track", 2)
|
||||
command=lambda: self.common.option.edit("process_track", 2)
|
||||
)
|
||||
self.menu_conv_process.add_radiobutton(
|
||||
label=self.translate("4 ", "process"),
|
||||
variable=self.intvar_process_track, value=4,
|
||||
command=lambda: self.option.edit("process_track", 4)
|
||||
command=lambda: self.common.option.edit("process_track", 4)
|
||||
)
|
||||
self.menu_conv_process.add_radiobutton(
|
||||
label=self.translate("8 ", "process"),
|
||||
variable=self.intvar_process_track, value=8,
|
||||
command=lambda: self.option.edit("process_track", 8)
|
||||
command=lambda: self.common.option.edit("process_track", 8)
|
||||
)
|
||||
|
||||
## GAME PARAMETER
|
||||
self.menu_advanced.add_separator()
|
||||
|
||||
self.menu_advanced.add_checkbutton(label=self.translate("Use debug mode"),
|
||||
variable=self.boolvar_use_debug_mode)
|
||||
self.menu_advanced.add_command(
|
||||
label=self.translate("Change track configuration"),
|
||||
command=self.common.show_gui_track_configuration
|
||||
)
|
||||
|
||||
self.menu_advanced.add_checkbutton(
|
||||
label=self.translate("Use debug mode"),
|
||||
variable=self.boolvar_use_debug_mode
|
||||
)
|
||||
|
||||
self.menu_mystuff = Menu(self.menu_advanced, tearoff=0)
|
||||
self.menu_advanced.add_cascade(label=self.translate("MyStuff"), menu=self.menu_mystuff)
|
||||
|
@ -270,7 +273,7 @@ class Gui:
|
|||
self.menu_help.add_command(label="Discord", command=lambda: webbrowser.open(DISCORD_URL))
|
||||
|
||||
def reload_ctconfig(self) -> None:
|
||||
self.game.ctconfig.load_ctconfig_file(
|
||||
self.common.ct_config.load_ctconfig_file(
|
||||
ctconfig_file=self.get_ctconfig_path_pack(self.stringvar_ctconfig.get())
|
||||
)
|
||||
|
||||
|
@ -326,7 +329,7 @@ class Gui:
|
|||
except requests.ConnectionError:
|
||||
messagebox.showwarning(self.translate("Warning"),
|
||||
self.translate("Can't connect to internet. Download will be disabled."))
|
||||
self.option.disable_download = True
|
||||
self.common.option.disable_download = True
|
||||
|
||||
except:
|
||||
self.log_error()
|
||||
|
@ -339,10 +342,10 @@ class Gui:
|
|||
with open("./error.log", "a") as f:
|
||||
f.write(
|
||||
f"---\n"
|
||||
f"For game version : {self.game.ctconfig.version}\n"
|
||||
f"For game version : {self.common.ct_config.version}\n"
|
||||
f"./file/ directory : {os.listdir('./file/')}\n"
|
||||
f"ctconfig directory : {os.listdir(self.game.ctconfig.pack_path)}\n"
|
||||
f"GAME/files/ information : {self.game.path, self.game.region}\n"
|
||||
f"ctconfig directory : {os.listdir(self.common.ct_config.pack_path)}\n"
|
||||
f"GAME/files/ information : {self.common.game.path, self.common.game.region}\n"
|
||||
f"{error}\n"
|
||||
)
|
||||
messagebox.showerror(
|
||||
|
@ -404,7 +407,7 @@ class Gui:
|
|||
:param gamelang: force a destination language to convert track
|
||||
:return: translated text
|
||||
"""
|
||||
lang = gamelang_to_lang.get(gamelang, self.stringvar_language.get())
|
||||
lang = gamelang_to_lang.get(gamelang, self.common.option.language)
|
||||
if lang not in translation_dict: return "".join(texts) # if no translation language is found
|
||||
|
||||
_lang_trad = translation_dict[lang]
|
||||
|
@ -434,3 +437,6 @@ class Gui:
|
|||
self.root.quit()
|
||||
self.root.destroy()
|
||||
sys.exit()
|
||||
|
||||
def mainloop(self) -> None:
|
||||
self.root.mainloop()
|
3
source/Gui/SelectPack.py
Normal file
3
source/Gui/SelectPack.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
class SelectPack:
|
||||
def __init__(self):
|
||||
pass
|
283
source/Gui/TrackSelection.py
Normal file
283
source/Gui/TrackSelection.py
Normal file
|
@ -0,0 +1,283 @@
|
|||
from tkinter import *
|
||||
from tkinter import ttk
|
||||
|
||||
|
||||
class Orderbox(Listbox):
|
||||
def order_up(self, *args, **kwargs):
|
||||
self.order_change(delta_start=1)
|
||||
|
||||
def order_down(self, *args, **kwargs):
|
||||
self.order_change(delta_end=1)
|
||||
|
||||
def order_change(self, delta_start: int = 0, delta_end: int = 0):
|
||||
selection = self.curselection()
|
||||
if len(selection) < 1: return
|
||||
index = selection[0]
|
||||
|
||||
values = self.get(index - delta_start, index + delta_end)
|
||||
if len(values) < 2: return
|
||||
|
||||
self.delete(index - delta_start, index + delta_end)
|
||||
self.insert(index - delta_start, *reversed(values))
|
||||
|
||||
self.selection_set(index - delta_start + delta_end)
|
||||
|
||||
|
||||
class TrackSelection:
|
||||
def __init__(self, common):
|
||||
self.common = common
|
||||
|
||||
self.root = Toplevel(self.common.gui_main.root)
|
||||
self.root.title("Track selection")
|
||||
self.root.iconbitmap("./icon.ico")
|
||||
self.root.resizable(False, False)
|
||||
self.root.grab_set()
|
||||
|
||||
self.text_is_equal_to = "is equal to"
|
||||
self.text_is_in = "is in"
|
||||
self.text_is_between = "is between"
|
||||
self.text_contains = "contains"
|
||||
|
||||
self.text_and = "and"
|
||||
self.text_nand = "nand"
|
||||
self.text_or = "or"
|
||||
self.text_nor = "nor"
|
||||
self.text_xor = "xor"
|
||||
self.text_xnor = "xnor"
|
||||
self.condition_link_end = "end"
|
||||
|
||||
track_filter_row_start = 10
|
||||
self.condition_links = {
|
||||
self.text_and: lambda a, b: lambda track: a(track) and b(track),
|
||||
self.text_nand: lambda a, b: lambda track: not (a(track) and b(track)),
|
||||
self.text_or: lambda a, b: lambda track: a(track) or b(track),
|
||||
self.text_nor: lambda a, b: lambda track: not (a(track) or b(track)),
|
||||
self.text_xor: lambda a, b: lambda track: a(track) != b(track),
|
||||
self.text_xnor: lambda a, b: lambda track: a(track) == b(track),
|
||||
self.condition_link_end: -1
|
||||
}
|
||||
|
||||
def del_frame_track_filter(frames_filter: list, index: int = 0):
|
||||
for elem in frames_filter[index:]: # remove all track filter after this one
|
||||
elem["frame"].destroy()
|
||||
del frames_filter[index:]
|
||||
|
||||
def add_frame_track_filter(root: Frame, frames_filter: list, index: int = 0):
|
||||
frame = Frame(root)
|
||||
frame.grid(row=index + track_filter_row_start, column=1, sticky="NEWS")
|
||||
Label(frame, text="If track's").grid(row=1, column=1)
|
||||
track_property = ttk.Combobox(frame, values=list(self.common.ct_config.get_all_track_possibilities().keys()))
|
||||
track_property.current(0)
|
||||
track_property.grid(row=1, column=2)
|
||||
|
||||
frame_equal = Frame(frame)
|
||||
entry_equal = Entry(frame_equal, width=20)
|
||||
entry_equal.grid(row=1, column=1)
|
||||
entry_equal.insert(END, "value")
|
||||
|
||||
frame_in = Frame(frame)
|
||||
entry_in = Entry(frame_in, width=30)
|
||||
entry_in.grid(row=1, column=1)
|
||||
entry_in.insert(END, "value1, value2, ...")
|
||||
|
||||
frame_between = Frame(frame)
|
||||
entry_start = Entry(frame_between, width=10)
|
||||
entry_start.grid(row=1, column=1)
|
||||
entry_start.insert(END, "value1")
|
||||
Label(frame_between, text="and").grid(row=1, column=2)
|
||||
entry_end = Entry(frame_between, width=10)
|
||||
entry_end.insert(END, "value2")
|
||||
entry_end.grid(row=1, column=3)
|
||||
|
||||
frame_contains = Frame(frame)
|
||||
entry_contains = Entry(frame_contains, width=20)
|
||||
entry_contains.grid(row=1, column=1)
|
||||
entry_contains.insert(END, "value")
|
||||
|
||||
condition_frames = {
|
||||
self.text_is_equal_to: frame_equal,
|
||||
self.text_is_in: frame_in,
|
||||
self.text_is_between: frame_between,
|
||||
self.text_contains: frame_contains,
|
||||
}
|
||||
|
||||
def change_condition_type(event: Event = None):
|
||||
condition = combobox_condition_type.get()
|
||||
for frame in condition_frames.values(): frame.grid_forget()
|
||||
condition_frames[condition].grid(row=1, column=10)
|
||||
|
||||
combobox_condition_type = ttk.Combobox(frame, values=list(condition_frames.keys()), width=10)
|
||||
combobox_condition_type.current(0)
|
||||
combobox_condition_type.bind("<<ComboboxSelected>>", change_condition_type)
|
||||
change_condition_type()
|
||||
combobox_condition_type.grid(row=1, column=3)
|
||||
|
||||
def change_condition_link(event: Event = None):
|
||||
link = next_condition_link.get()
|
||||
|
||||
if link == self.condition_link_end:
|
||||
del_frame_track_filter(frames_filter, index=index + 1)
|
||||
|
||||
else:
|
||||
if frames_filter[-1]["frame"] == frame: # if this is the last filter available
|
||||
add_frame_track_filter(root=root, frames_filter=frames_filter, index=index + 1)
|
||||
|
||||
next_condition_link = ttk.Combobox(frame, values=list(self.condition_links.keys()), width=10)
|
||||
next_condition_link.bind("<<ComboboxSelected>>", change_condition_link)
|
||||
next_condition_link.set(self.condition_link_end)
|
||||
next_condition_link.grid(row=1, column=100)
|
||||
|
||||
frames_filter.append({
|
||||
"frame": frame,
|
||||
"track_property": track_property,
|
||||
"condition_type": combobox_condition_type,
|
||||
|
||||
"value_equal": entry_equal,
|
||||
"value_in": entry_in,
|
||||
"value_between_start": entry_start,
|
||||
"value_between_end": entry_end,
|
||||
"value_contains": entry_contains,
|
||||
|
||||
"next_condition_link": next_condition_link
|
||||
})
|
||||
|
||||
def get_change_enable_track_filter_func(root: [Frame, LabelFrame], frames_filter: list, variable_enable: BooleanVar):
|
||||
def change_enable_track_filter(event: Event = None):
|
||||
if variable_enable.get(): add_frame_track_filter(root=root, frames_filter=frames_filter)
|
||||
else: del_frame_track_filter(frames_filter=frames_filter)
|
||||
|
||||
return change_enable_track_filter
|
||||
|
||||
self.track_sort = LabelFrame(self.root, text="Sort Track")
|
||||
self.track_sort.grid(row=1, column=1, sticky="NEWS")
|
||||
|
||||
Label(self.track_sort, text="Sort track by : ").grid(row=1, column=1)
|
||||
self.combobox_track_sort = ttk.Combobox(
|
||||
self.track_sort,
|
||||
values=list(self.common.ct_config.get_all_track_possibilities().keys())
|
||||
)
|
||||
self.combobox_track_sort.grid(row=1, column=2, sticky="NEWS")
|
||||
self.combobox_track_sort.insert(END, self.common.ct_config.sort_track_attr)
|
||||
|
||||
self.track_filter = LabelFrame(self.root, text="Filter Track")
|
||||
self.track_filter.grid(row=2, column=1, sticky="NEWS")
|
||||
|
||||
self.variable_enable_track_filter = BooleanVar(value=False)
|
||||
self.frames_track_filter = []
|
||||
self.checkbutton_track_filter = ttk.Checkbutton(
|
||||
self.track_filter,
|
||||
text="Enable track filter",
|
||||
variable=self.variable_enable_track_filter,
|
||||
command=get_change_enable_track_filter_func(
|
||||
self.track_filter,
|
||||
self.frames_track_filter,
|
||||
self.variable_enable_track_filter
|
||||
)
|
||||
)
|
||||
self.checkbutton_track_filter.grid(row=1, column=1)
|
||||
|
||||
self.track_highlight = LabelFrame(self.root, text="Highlight Track")
|
||||
self.track_highlight.grid(row=3, column=1, sticky="NEWS")
|
||||
|
||||
self.variable_enable_track_highlight = BooleanVar(value=False)
|
||||
self.frames_track_highlight = []
|
||||
self.checkbutton_track_highlight = ttk.Checkbutton(
|
||||
self.track_highlight,
|
||||
text="Enable track highlight",
|
||||
variable=self.variable_enable_track_highlight,
|
||||
command=get_change_enable_track_filter_func(
|
||||
self.track_highlight,
|
||||
self.frames_track_highlight,
|
||||
self.variable_enable_track_highlight
|
||||
)
|
||||
)
|
||||
self.checkbutton_track_highlight.grid(row=1, column=1)
|
||||
|
||||
self.track_random_new = LabelFrame(self.root, text="Overwrite random cup new")
|
||||
self.track_random_new.grid(row=4, column=1, sticky="NEWS")
|
||||
|
||||
self.variable_enable_track_random_new = BooleanVar(value=False)
|
||||
self.frames_track_random_new = []
|
||||
self.checkbutton_track_random_new = ttk.Checkbutton(
|
||||
self.track_random_new,
|
||||
text="Enable overwriting random \"new\" track",
|
||||
variable=self.variable_enable_track_random_new,
|
||||
command=get_change_enable_track_filter_func(
|
||||
self.track_random_new,
|
||||
self.frames_track_random_new,
|
||||
self.variable_enable_track_random_new
|
||||
)
|
||||
)
|
||||
self.checkbutton_track_random_new.grid(row=1, column=1)
|
||||
|
||||
Button(
|
||||
self.root,
|
||||
text="Save configuration",
|
||||
relief=RIDGE,
|
||||
command=self.save_configuration
|
||||
).grid(row=100, column=1, sticky="E")
|
||||
|
||||
def save_configuration(self):
|
||||
self.common.ct_config.sort_track_attr = self.combobox_track_sort.get()
|
||||
self.common.ct_config.filter_track_selection = self.get_filter(
|
||||
self.variable_enable_track_filter,
|
||||
self.frames_track_filter
|
||||
)
|
||||
self.common.ct_config.filter_track_highlight = self.get_filter(
|
||||
self.variable_enable_track_highlight,
|
||||
self.frames_track_highlight
|
||||
)
|
||||
self.common.ct_config.filter_track_random_new = self.get_filter(
|
||||
self.variable_enable_track_random_new,
|
||||
self.frames_track_random_new
|
||||
)
|
||||
|
||||
def get_filter(self, condition_enabled: BooleanVar, frames_filter: list):
|
||||
s = lambda x: str(x).strip()
|
||||
|
||||
filter_condition = lambda track: True
|
||||
if not condition_enabled.get(): return filter_condition
|
||||
next_condition_link_func = lambda a, b: lambda track: a(track) and b(track)
|
||||
|
||||
for frame_filter in frames_filter:
|
||||
track_property = frame_filter["track_property"].get()
|
||||
|
||||
value_equal = frame_filter["value_equal"].get()
|
||||
value_in = frame_filter["value_in"].get()
|
||||
value_contains = frame_filter["value_contains"].get()
|
||||
value_between_start = frame_filter["value_between_start"].get()
|
||||
value_between_end = frame_filter["value_between_end"].get()
|
||||
|
||||
def _is_between_func_wrapper(property):
|
||||
def _is_between_func(track):
|
||||
from_ = s(value_between_start)
|
||||
to = s(value_between_end)
|
||||
prop = s(getattr(track, property, None))
|
||||
|
||||
if from_.isnumeric() and prop.isnumeric() and to.isnumeric(): return int(from_) <= int(prop) <= int(to)
|
||||
else: return from_ <= prop <= to
|
||||
|
||||
return _is_between_func
|
||||
|
||||
track_conditions_filter = {
|
||||
self.text_is_equal_to: lambda property: lambda track:
|
||||
s(getattr(track, property, None)) == s(value_equal),
|
||||
self.text_is_in: lambda property: lambda track:
|
||||
s(getattr(track, property, None)) in [s(v) for v in value_in.split(",")],
|
||||
self.text_is_between:
|
||||
_is_between_func_wrapper,
|
||||
self.text_contains: lambda property: lambda track:
|
||||
s(value_contains) in s(getattr(track, property, None))
|
||||
}
|
||||
|
||||
track_condition_type = frame_filter["condition_type"].get()
|
||||
track_condition_filter = track_conditions_filter[track_condition_type]
|
||||
|
||||
filter_condition = next_condition_link_func(
|
||||
filter_condition,
|
||||
track_condition_filter(track_property)
|
||||
)
|
||||
next_condition_link = frame_filter["next_condition_link"].get()
|
||||
next_condition_link_func = self.condition_links[next_condition_link]
|
||||
|
||||
return filter_condition
|
|
@ -35,10 +35,6 @@ class Track:
|
|||
"""
|
||||
Track class
|
||||
:param name: track name
|
||||
:param file_wu8: path to its wu8 file
|
||||
:param file_szs: path to its szs file
|
||||
:param prefix: track prefix (often original console or game)
|
||||
:param suffix: track suffix (often for variation like Boost or Night)
|
||||
:param author: track creator(s)
|
||||
:param special: track special slot
|
||||
:param music: track music slot
|
||||
|
@ -47,8 +43,6 @@ class Track:
|
|||
:param since_version: since when version did the track got added to the mod
|
||||
:param score: what it the score of the track
|
||||
:param warning: what is the warn level of the track (0 = none, 1 = minor bug, 2 = major bug)
|
||||
:param track_wu8_dir: where is stored the track wu8
|
||||
:param track_szs_dir: where is stored the track szs
|
||||
:param track_version: version of the track
|
||||
:param tags: a list of tags that correspond to the track
|
||||
|
||||
|
@ -66,7 +60,6 @@ class Track:
|
|||
self.warning = warning # Track bug level (1 = minor, 2 = major)
|
||||
self.version = version
|
||||
self.tags = tags if tags else []
|
||||
self.refresh_filename()
|
||||
|
||||
self._is_in_group = is_in_group
|
||||
|
||||
|
@ -82,30 +75,34 @@ class Track:
|
|||
check if track wu8 sha1 is correct
|
||||
:return: 0 if yes, -1 if no
|
||||
"""
|
||||
return check_file_sha1(self._wu8_file, self.sha1)
|
||||
return check_file_sha1(self.get_wu8_file(), self.sha1)
|
||||
|
||||
def check_szs_sha1(self) -> int:
|
||||
"""
|
||||
check if track szs sha1 is correct
|
||||
:return: 0 if yes, -1 if no
|
||||
"""
|
||||
return check_file_sha1(self._szs_file, self.sha1)
|
||||
return check_file_sha1(self.get_szs_file(), self.sha1)
|
||||
|
||||
def get_wu8_file(self): return f"{self._wu8_dir}/{self.sha1}.wu8"
|
||||
def get_szs_file(self): return f"{self._szs_dir}/{self.sha1}.szs"
|
||||
|
||||
def convert_wu8_to_szs(self) -> None:
|
||||
"""
|
||||
convert track to szs
|
||||
"""
|
||||
file_wu8 = f"{self._wu8_dir}/{self.sha1}.wu8"
|
||||
file_szs = f"{self._szs_dir}/{self.sha1}.szs"
|
||||
|
||||
if os.path.exists(file_szs) and os.path.getsize(file_szs) < 1000:
|
||||
os.remove(file_szs) # File under this size are corrupted
|
||||
szs_file = self.get_szs_file()
|
||||
wu8_file = self.get_wu8_file()
|
||||
|
||||
if os.path.exists(szs_file) and os.path.getsize(szs_file) < 1000:
|
||||
os.remove(szs_file) # File under this size are corrupted
|
||||
|
||||
if not self.check_szs_sha1(): # if sha1 of track's szs is incorrect or track's szs does not exist
|
||||
if os.path.exists(file_wu8):
|
||||
if os.path.exists(wu8_file):
|
||||
szs.normalize(
|
||||
src_file=file_wu8,
|
||||
dest_file=file_szs
|
||||
src_file=wu8_file,
|
||||
dest_file=szs_file
|
||||
)
|
||||
else:
|
||||
raise MissingTrackWU8()
|
||||
|
@ -116,14 +113,17 @@ class Track:
|
|||
"""
|
||||
return self.author if type(self.author) == str else ", ".join(self.author)
|
||||
|
||||
def get_ctfile(self, race=False, *args, **kwargs) -> str:
|
||||
def get_ctfile(self, race=False, filter_random_new=None, *args, **kwargs) -> str:
|
||||
"""
|
||||
get ctfile text to create CTFILE.txt and RCTFILE.txt
|
||||
:param filter_random_new: function to decide if the track should be used by the "random new" option
|
||||
:param race: is it a text used for Race_*.szs ?
|
||||
:return: ctfile definition for the track
|
||||
"""
|
||||
track_type = "T"
|
||||
track_flag = 0x00 if self.tag_retro in self.tags else 0x01
|
||||
if filter_random_new: track_flag = 0x01 if filter_random_new(self) else 0x00
|
||||
|
||||
if self._is_in_group:
|
||||
track_type = "H"
|
||||
track_flag |= 0x04
|
||||
|
@ -152,13 +152,15 @@ class Track:
|
|||
if tag in tag_list: return tag
|
||||
return ""
|
||||
|
||||
def get_track_formatted_name(self, highlight_version: str = None, *args, **kwargs) -> str:
|
||||
def get_track_formatted_name(self, filter_highlight=None, *args, **kwargs) -> str:
|
||||
"""
|
||||
get the track name with score, color, ...
|
||||
:param ct_config: ct_config for tags configuration
|
||||
:param highlight_version: if a specific version need to be highlighted.
|
||||
:param filter_highlight: filter function to decide if the track should be filtered.
|
||||
:return: the name of the track with colored prefix, suffix
|
||||
"""
|
||||
if not filter_highlight: filter_highlight = lambda track: False
|
||||
|
||||
hl_prefix = "" # highlight
|
||||
hl_suffix = ""
|
||||
prefix = self.select_tag(self.prefix_list) # tag prefix
|
||||
|
@ -174,8 +176,7 @@ class Track:
|
|||
if 0 < self.warning <= 3:
|
||||
star_prefix = warning_color[self.warning]
|
||||
|
||||
if self.since_version == highlight_version:
|
||||
hl_prefix, hl_suffix = "\\\\c{blue1}", "\\\\c{off}"
|
||||
if filter_highlight(self): hl_prefix, hl_suffix = "\\\\c{blue1}", "\\\\c{off}"
|
||||
|
||||
if prefix: prefix = "\\\\c{" + self.tags_color[prefix] + "}" + prefix + "\\\\c{off} "
|
||||
if suffix: suffix = " (\\\\c{" + self.tags_color[suffix] + "}" + suffix + "\\\\c{off})"
|
||||
|
@ -190,10 +191,6 @@ class Track:
|
|||
"""
|
||||
return f"{self.select_tag(ct_config.prefix_list)}{self.name}{self.select_tag(ct_config.suffix_list)}"
|
||||
|
||||
def refresh_filename(self):
|
||||
self._wu8_file = f"{self._wu8_dir}/{self.sha1}.wu8"
|
||||
self._szs_file = f"{self._szs_dir}/{self.sha1}.szs"
|
||||
|
||||
def load_from_json(self, track_json: dict):
|
||||
"""
|
||||
load the track from a dictionary
|
||||
|
@ -202,8 +199,6 @@ class Track:
|
|||
for key, value in track_json.items(): # load all value in the json as class attribute
|
||||
setattr(self, key, value)
|
||||
|
||||
self.refresh_filename()
|
||||
|
||||
return self
|
||||
|
||||
def create_from_track_file(self, track_file: str) -> None:
|
||||
|
|
Loading…
Reference in a new issue