mirror of
https://github.com/Faraphel/Atlas-Install.git
synced 2025-07-04 11:48:22 +02:00
the code is now fully translatable
This commit is contained in:
parent
65e7fb7118
commit
ec94ddece8
26 changed files with 214 additions and 82 deletions
|
@ -5,6 +5,96 @@
|
||||||
"LANGUAGE_SELECTION": "Language",
|
"LANGUAGE_SELECTION": "Language",
|
||||||
"TRACK_FILTER": "Track Filters",
|
"TRACK_FILTER": "Track Filters",
|
||||||
"ADVANCED_CONFIGURATION": "Advanced",
|
"ADVANCED_CONFIGURATION": "Advanced",
|
||||||
"HELP": "Help"
|
"HELP": "Help",
|
||||||
|
"INVALID_SOURCE_PATH": "Invalid path for source game",
|
||||||
|
"DISCORD": "Discord",
|
||||||
|
"GITHUB WIKI": "Github Wiki",
|
||||||
|
"READTHEDOCS": "ReadTheDocs",
|
||||||
|
"ORIGINAL_GAME_FILE": "Original Game File",
|
||||||
|
"SELECT_SOURCE_GAME": "Select the source game",
|
||||||
|
"WII GAMES": "Wii games",
|
||||||
|
"ERROR": "Error",
|
||||||
|
"ERROR_INVALID_SOURCE_GAME": "The source game path is invalid",
|
||||||
|
"GAME_DIRECTORY_DESTINATION": "Game Directory Destination",
|
||||||
|
"SELECT_DESTINATION_GAME": "Select the destination game",
|
||||||
|
"WARNING": "Warning",
|
||||||
|
"WARNING_DESTINATION_GAME_NOT_WRITABLE": "The destination game path is not writable",
|
||||||
|
"INSTALL": "Install",
|
||||||
|
"ERROR_INVALID_DESTINATION_GAME": "The destination game path is invalid",
|
||||||
|
"WARNING_LOW_SPACE_CONTINUE": "The space left on the drive is low. Continue ?",
|
||||||
|
"INSTALLATION_COMPLETED": "Installation Completed",
|
||||||
|
"INSTALLATION_FINISHED_WITH_SUCCESS": "The installation ended successfully !",
|
||||||
|
"OPEN_MYSTUFF_SETTINGS": "Open MyStuff settings",
|
||||||
|
"THREADS_USAGE": "Threads usage",
|
||||||
|
"USE": "Use",
|
||||||
|
"THREADS": "threads",
|
||||||
|
"MESSAGE_FROM_MOD_AUTHOR": "Message from the author",
|
||||||
|
"NO_MESSAGE_FROM_AUTHOR": "< The author didn't left any message >",
|
||||||
|
"GLOBAL_MOD_SETTINGS": "Global mod settings",
|
||||||
|
"SPECIFIC_MOD_SETTINGS": "Specific mod settings",
|
||||||
|
"CONFIGURE_MYSTUFF_PATCH": "Configure the MyStuff patchs",
|
||||||
|
"DISABLED": "Disabled",
|
||||||
|
"NEW_PROFILE": "New Profile",
|
||||||
|
"DELETE_PROFILE": "Delete Profile",
|
||||||
|
"ADD_MYSTUFF": "Add MyStuff patch",
|
||||||
|
"REMOVE_MYSTUFF": "Remove MyStuff patch",
|
||||||
|
"MYSTUFF_PROFILE_ALREADY_EXIST": "This MyStuff profile already exist !",
|
||||||
|
"MYSTUFF_PROFILE_FORBIDDEN_NAME": "This MyStuff profile name can't be used !",
|
||||||
|
"SELECT_MYSTUFF": "Select MyStuff patch",
|
||||||
|
"TYPE_PREVIEW_WINDOW": "Type of preview window",
|
||||||
|
"NOT_FOUND": "not found",
|
||||||
|
"TYPE_MOD_SETTINGS": "Type of mod settings",
|
||||||
|
"BMG_LAYER_MODE": "bmg layer mode",
|
||||||
|
"IS_NOT_IMPLEMENTED": "is not implemented",
|
||||||
|
"IMAGE_LAYER_TYPE": "image layer type",
|
||||||
|
"OPERATION": "Operation",
|
||||||
|
"PATH": "Path",
|
||||||
|
"MODE": "Mode",
|
||||||
|
"SOURCE": "Source",
|
||||||
|
"OUTSIDE_ALLOWED_RANGE": "outside of allowed range",
|
||||||
|
"IN_PATCH": "in patch",
|
||||||
|
"INSTALLING_PATCH": "Installing the patch",
|
||||||
|
"PATCHING": "Patching",
|
||||||
|
"FORBIDDEN_TRACK_ATTRIBUTE": "Forbidden track attribute",
|
||||||
|
"FORBIDDEN_ARENA_ATTRIBUTE": "Forbidden arena attribute",
|
||||||
|
"EXTRACTING_AUTOADD_FILES": "Extracting autoadd files...",
|
||||||
|
"EXTRACTING_ORIGINAL_TRACKS": "Extracting original tracks...",
|
||||||
|
"INSTALLING_MYSTUFF": "Installing MyStuff",
|
||||||
|
"INSTALLING_ALL_MYSTUFF_PATCHS": "Installing all the mystuff patchs",
|
||||||
|
"PREPARING": "Preparing",
|
||||||
|
"SPECIAL_FILE": "special file",
|
||||||
|
"REPACKING": "Repacking",
|
||||||
|
"ALL_ARCHIVES": "all archives",
|
||||||
|
"INSTALLING_ALL": "Installing all",
|
||||||
|
"CONVERTING_GAME_TO": "Converting game to",
|
||||||
|
"DELETING_EXTRACTED_GAME": "Deleting the extracted game...",
|
||||||
|
"NOT_MKW_GAME": "Not a Mario Kart Wii game",
|
||||||
|
"GAME_ALREADY_MODDED": "This game is already modded",
|
||||||
|
"COPYING_GAME": "Copying Game...",
|
||||||
|
"EXTRACTING": "Extracting",
|
||||||
|
"ESTIMATED_TIME_REMAINING": "estimated time remaining",
|
||||||
|
"CHANGING_GAME_METADATA": "Changing game metadata...",
|
||||||
|
"EXTRACTION": "Extraction",
|
||||||
|
"MYSTUFF": "MyStuff",
|
||||||
|
"PREPARING_FILES": "Preparing files",
|
||||||
|
"PRE-PATCHING": "Pre-Patching",
|
||||||
|
"CONVERTING_TO_GAME_FILE": "Converting to game file",
|
||||||
|
"CANNOT_FIND_COLOR": "Can't find color",
|
||||||
|
"NORMALIZING_TRACKS": "Normalizing tracks",
|
||||||
|
"CANNOT_FIND_ORIGINAL_TRACK": "Can't find original track",
|
||||||
|
"INVALID_MACRO": "Invalid macro",
|
||||||
|
"INVALID_AST_TYPE": "Invalid ast type",
|
||||||
|
"MAGIC_ATTRIBUTE_ARE_FORBIDDEN": "Magic attribute are forbidden",
|
||||||
|
"CANNOT_SET_ATTRIBUTE": "Can't set value of attribute",
|
||||||
|
"CANNOT_SET_ENVIRONMENT": "Can't set value of environment",
|
||||||
|
"CANNOT_SET_ARGUMENT": "Can't set value of argument",
|
||||||
|
"CALLING_FUNCTION_NOT_ALLOWED": "Calling this function is not allowed",
|
||||||
|
"FORBIDDEN_SYNTAX": "Forbidden syntax",
|
||||||
|
"MAGIC_METHOD_FORBIDDEN": "Magic method are not allowed",
|
||||||
|
"CANNOT_GET_ERROR_MESSAGE": "Can't get the error message",
|
||||||
|
"RAISED": "raised",
|
||||||
|
"CANNOT_FIND_TOOL": "Can't find tool",
|
||||||
|
"IN_TOOLS_DIRECTORY": "in the tools directory.",
|
||||||
|
"CANNOT_EXTRACT_A_DIRECTORY": "Can't extract a directory"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,7 +13,7 @@ def better_gui_error(func: Callable) -> Callable:
|
||||||
|
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
try: return func(*args, **kwargs)
|
try: return func(*args, **kwargs)
|
||||||
except:
|
except Exception:
|
||||||
exc = traceback.format_exc()
|
exc = traceback.format_exc()
|
||||||
with Path("error.log").open("a", encoding="utf8") as log_file:
|
with Path("error.log").open("a", encoding="utf8") as log_file:
|
||||||
log_file.write(f"{'#' * 20}\n{time.strftime('%Y/%M/%d %H:%m:%S')}\n\n{exc}\n\n")
|
log_file.write(f"{'#' * 20}\n{time.strftime('%Y/%M/%d %H:%m:%S')}\n\n{exc}\n\n")
|
||||||
|
|
|
@ -24,12 +24,12 @@ from source.wt.wit import Extension
|
||||||
|
|
||||||
class SourceGameError(Exception):
|
class SourceGameError(Exception):
|
||||||
def __init__(self, path: Path | str):
|
def __init__(self, path: Path | str):
|
||||||
super().__init__(f"Invalid path for source game : {path}")
|
super().__init__(_(f"ERROR_INVALID_SOURCE_GAME", " : ", path))
|
||||||
|
|
||||||
|
|
||||||
class DestinationGameError(Exception):
|
class DestinationGameError(Exception):
|
||||||
def __init__(self, path: Path | str):
|
def __init__(self, path: Path | str):
|
||||||
super().__init__(f"Invalid path for destination game : {path}")
|
super().__init__(_("ERROR_INVALID_DESTINATION_GAME", " : ", path))
|
||||||
|
|
||||||
|
|
||||||
class InstallerState(enum.Enum):
|
class InstallerState(enum.Enum):
|
||||||
|
@ -183,7 +183,7 @@ class Menu(tkinter.Menu):
|
||||||
self.root = master.root
|
self.root = master.root
|
||||||
|
|
||||||
master.add_cascade(label=_("ADVANCED_CONFIGURATION"), menu=self)
|
master.add_cascade(label=_("ADVANCED_CONFIGURATION"), menu=self)
|
||||||
self.add_command(label=_("OPEN_MYSTUFF_WINDOW"), command= mystuff.Window)
|
self.add_command(label=_("OPEN_MYSTUFF_SETTINGS"), command= mystuff.Window)
|
||||||
|
|
||||||
self.threads_used = self.ThreadsUsed(self)
|
self.threads_used = self.ThreadsUsed(self)
|
||||||
|
|
||||||
|
@ -192,7 +192,7 @@ class Menu(tkinter.Menu):
|
||||||
super().__init__(master, tearoff=False)
|
super().__init__(master, tearoff=False)
|
||||||
self.root = master.root
|
self.root = master.root
|
||||||
|
|
||||||
master.add_cascade(label=_("THREADS_USED"), menu=self)
|
master.add_cascade(label=_("THREADS_USAGE"), menu=self)
|
||||||
|
|
||||||
self.variable = tkinter.IntVar(value=self.root.options["threads"])
|
self.variable = tkinter.IntVar(value=self.root.options["threads"])
|
||||||
|
|
||||||
|
@ -214,12 +214,12 @@ class Menu(tkinter.Menu):
|
||||||
super().__init__(master, tearoff=False)
|
super().__init__(master, tearoff=False)
|
||||||
self.root = master.root
|
self.root = master.root
|
||||||
|
|
||||||
master.add_cascade(label="Help", menu=self)
|
master.add_cascade(label=_("HELP"), menu=self)
|
||||||
self.menu_id = self.master.index(tkinter.END)
|
self.menu_id = self.master.index(tkinter.END)
|
||||||
|
|
||||||
self.add_command(label="Discord", command=lambda: webbrowser.open(discord_url))
|
self.add_command(label=_("DISCORD"), command=lambda: webbrowser.open(discord_url))
|
||||||
self.add_command(label="Github Wiki", command=lambda: webbrowser.open(github_wiki_url))
|
self.add_command(label=_("GITHUB WIKI"), command=lambda: webbrowser.open(github_wiki_url))
|
||||||
self.add_command(label=_("ReadTheDocs"), command=lambda: webbrowser.open(readthedocs_url))
|
self.add_command(label=_("READTHEDOCS"), command=lambda: webbrowser.open(readthedocs_url))
|
||||||
|
|
||||||
def set_installation_state(self, state: InstallerState) -> bool:
|
def set_installation_state(self, state: InstallerState) -> bool:
|
||||||
"""
|
"""
|
||||||
|
@ -248,7 +248,7 @@ class Menu(tkinter.Menu):
|
||||||
# Select game frame
|
# Select game frame
|
||||||
class SourceGame(ttk.LabelFrame):
|
class SourceGame(ttk.LabelFrame):
|
||||||
def __init__(self, master: tkinter.Tk):
|
def __init__(self, master: tkinter.Tk):
|
||||||
super().__init__(master, text="Original Game File")
|
super().__init__(master, text=_("ORIGINAL_GAME_FILE"))
|
||||||
self.root = master.root
|
self.root = master.root
|
||||||
|
|
||||||
self.columnconfigure(1, weight=1)
|
self.columnconfigure(1, weight=1)
|
||||||
|
@ -311,7 +311,7 @@ class SourceGame(ttk.LabelFrame):
|
||||||
# Select game destination frame
|
# Select game destination frame
|
||||||
class DestinationGame(ttk.LabelFrame):
|
class DestinationGame(ttk.LabelFrame):
|
||||||
def __init__(self, master: tkinter.Tk):
|
def __init__(self, master: tkinter.Tk):
|
||||||
super().__init__(master, text="Game Directory Destination")
|
super().__init__(master, text=_("GAME_DIRECTORY_DESTINATION"))
|
||||||
self.root = master.root
|
self.root = master.root
|
||||||
|
|
||||||
self.columnconfigure(1, weight=1)
|
self.columnconfigure(1, weight=1)
|
||||||
|
@ -379,7 +379,7 @@ class DestinationGame(ttk.LabelFrame):
|
||||||
# Install button
|
# Install button
|
||||||
class ButtonInstall(ttk.Button):
|
class ButtonInstall(ttk.Button):
|
||||||
def __init__(self, master: tkinter.Tk):
|
def __init__(self, master: tkinter.Tk):
|
||||||
super().__init__(master, text="Install", command=self.install)
|
super().__init__(master, text=_("INSTALL"), command=self.install)
|
||||||
self.root = master.root
|
self.root = master.root
|
||||||
|
|
||||||
@threaded
|
@threaded
|
||||||
|
@ -431,7 +431,7 @@ class ButtonInstall(ttk.Button):
|
||||||
|
|
||||||
messagebox.showinfo(
|
messagebox.showinfo(
|
||||||
_("INSTALLATION_COMPLETED"),
|
_("INSTALLATION_COMPLETED"),
|
||||||
f"{_('INSTALLATION_FINISHED_WITH_SUCCESS')}\n{_('MESSAGE_FROM_MOD_AUTHOR')}:\n\n{message}"
|
f"{_('INSTALLATION_FINISHED_WITH_SUCCESS')}\n{_('MESSAGE_FROM_MOD_AUTHOR')} :\n\n{message}"
|
||||||
)
|
)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import tkinter
|
import tkinter
|
||||||
from abc import abstractmethod, ABC
|
from abc import abstractmethod, ABC
|
||||||
from typing import Type, TYPE_CHECKING
|
from typing import Type, TYPE_CHECKING
|
||||||
|
from source.translation import translate as _
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from source.mkw.ModConfig import ModConfig
|
from source.mkw.ModConfig import ModConfig
|
||||||
|
@ -8,7 +9,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
class InvalidPreviewWindowName(Exception):
|
class InvalidPreviewWindowName(Exception):
|
||||||
def __init__(self, name: str):
|
def __init__(self, name: str):
|
||||||
super().__init__(f"Error : Type of preview window '{name}' not found.")
|
super().__init__(_("TYPE_PREVIEW_WINDOW", " '", name, "' ", "NOT_FOUND"))
|
||||||
|
|
||||||
|
|
||||||
class AbstractPreviewWindow(tkinter.Toplevel, ABC):
|
class AbstractPreviewWindow(tkinter.Toplevel, ABC):
|
||||||
|
|
|
@ -8,6 +8,7 @@ from source.mkw.Patch.Patch import Patch
|
||||||
from source.progress import Progress
|
from source.progress import Progress
|
||||||
from source.wt import szs, lec, wit
|
from source.wt import szs, lec, wit
|
||||||
from source.wt.wstrt import StrPath
|
from source.wt.wstrt import StrPath
|
||||||
|
from source.translation import translate as _
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from source.mkw.Game import Game
|
from source.mkw.Game import Game
|
||||||
|
@ -15,7 +16,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
class PathOutsideMod(Exception):
|
class PathOutsideMod(Exception):
|
||||||
def __init__(self, forbidden_path: Path, allowed_range: Path):
|
def __init__(self, forbidden_path: Path, allowed_range: Path):
|
||||||
super().__init__(f"Error : path {forbidden_path} outside of allowed range {allowed_range}")
|
super().__init__(_("PATH", ' "', forbidden_path, '" ', "OUTSIDE_ALLOWED_RANGE", ' "', allowed_range, '" '))
|
||||||
|
|
||||||
|
|
||||||
class ExtractedGame:
|
class ExtractedGame:
|
||||||
|
@ -33,7 +34,7 @@ class ExtractedGame:
|
||||||
Extract all the autoadd files from the game to destination_path
|
Extract all the autoadd files from the game to destination_path
|
||||||
:param destination_path: directory where the autoadd files will be extracted
|
:param destination_path: directory where the autoadd files will be extracted
|
||||||
"""
|
"""
|
||||||
yield Progress(description="Extracting autoadd files...", determinate=False)
|
yield Progress(description=_("EXTRACTING_AUTOADD_FILES"), determinate=False)
|
||||||
szs.autoadd(self.path / "files/Race/Course/", destination_path)
|
szs.autoadd(self.path / "files/Race/Course/", destination_path)
|
||||||
|
|
||||||
def extract_original_tracks(self, destination_path: "Path | str") -> Generator[Progress, None, None]:
|
def extract_original_tracks(self, destination_path: "Path | str") -> Generator[Progress, None, None]:
|
||||||
|
@ -43,9 +44,12 @@ class ExtractedGame:
|
||||||
"""
|
"""
|
||||||
destination_path = Path(destination_path)
|
destination_path = Path(destination_path)
|
||||||
destination_path.mkdir(parents=True, exist_ok=True)
|
destination_path.mkdir(parents=True, exist_ok=True)
|
||||||
yield Progress(description="Extracting original tracks...", determinate=False)
|
yield Progress(description=_("EXTRACTING_ORIGINAL_TRACKS"), determinate=False)
|
||||||
for track_file in (self.path / "files/Race/Course/").glob("*.szs"):
|
for track_file in (self.path / "files/Race/Course/").glob("*.szs"):
|
||||||
yield Progress(description=f"Extracting original tracks ({track_file.name})...", determinate=False)
|
|
||||||
|
yield Progress(description=_("EXTRACTING_ORIGINAL_TRACKS", " (", track_file.name, ") ..."),
|
||||||
|
determinate=False)
|
||||||
|
|
||||||
if not (destination_path / track_file.name).exists(): track_file.rename(destination_path / track_file.name)
|
if not (destination_path / track_file.name).exists(): track_file.rename(destination_path / track_file.name)
|
||||||
else: track_file.unlink()
|
else: track_file.unlink()
|
||||||
|
|
||||||
|
@ -56,7 +60,7 @@ class ExtractedGame:
|
||||||
:mystuff_path: path to the MyStuff directory
|
:mystuff_path: path to the MyStuff directory
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
yield Progress(description=f"Installing MyStuff '{mystuff_path}'...", determinate=False)
|
yield Progress(description=_("INSTALLING_MYSTUFF", ' "', mystuff_path, '" ...'), determinate=False)
|
||||||
mystuff_path = Path(mystuff_path)
|
mystuff_path = Path(mystuff_path)
|
||||||
|
|
||||||
mystuff_rootfiles: dict[str, Path] = {}
|
mystuff_rootfiles: dict[str, Path] = {}
|
||||||
|
@ -73,7 +77,7 @@ class ExtractedGame:
|
||||||
Install multiple mystuff patch
|
Install multiple mystuff patch
|
||||||
:param mystuff_paths: paths to all the mystuff patch
|
:param mystuff_paths: paths to all the mystuff patch
|
||||||
"""
|
"""
|
||||||
yield Progress(description="Installing all the mystuff patchs")
|
yield Progress(description=_("INSTALLING_ALL_MYSTUFF_PATCHS"))
|
||||||
|
|
||||||
for mystuff_path in mystuff_paths:
|
for mystuff_path in mystuff_paths:
|
||||||
yield from self.install_mystuff(mystuff_path)
|
yield from self.install_mystuff(mystuff_path)
|
||||||
|
@ -83,7 +87,7 @@ class ExtractedGame:
|
||||||
Prepare special files for the patch
|
Prepare special files for the patch
|
||||||
:return: the special files dict
|
:return: the special files dict
|
||||||
"""
|
"""
|
||||||
yield Progress(description="Preparing ct_icon special file...", determinate=False)
|
yield Progress(description=_("PREPARING", " ct_icon ", "SPECIAL_FILE", "..."), determinate=False)
|
||||||
ct_icons = BytesIO()
|
ct_icons = BytesIO()
|
||||||
mod_config.get_full_cticon().save(ct_icons, format="PNG")
|
mod_config.get_full_cticon().save(ct_icons, format="PNG")
|
||||||
ct_icons.seek(0)
|
ct_icons.seek(0)
|
||||||
|
@ -100,11 +104,11 @@ class ExtractedGame:
|
||||||
"""
|
"""
|
||||||
Repack all the .d directory into .szs files.
|
Repack all the .d directory into .szs files.
|
||||||
"""
|
"""
|
||||||
yield Progress(description=f"Repacking all szs", determinate=False)
|
yield Progress(description=_("REPACKING", " ", "ALL_ARCHIVES"), determinate=False)
|
||||||
|
|
||||||
for extracted_szs in filter(lambda path: path.is_dir(), self.path.rglob("*.d")):
|
for extracted_szs in filter(lambda path: path.is_dir(), self.path.rglob("*.d")):
|
||||||
# for every directory that end with a .d in the extracted game, recreate the szs
|
# for every directory that end with a .d in the extracted game, recreate the szs
|
||||||
yield Progress(description=f"Repacking {extracted_szs} to szs", determinate=False)
|
yield Progress(description=_("REPACKING", ' "', extracted_szs, '"'), determinate=False)
|
||||||
|
|
||||||
szs.create(extracted_szs, extracted_szs.with_suffix(".szs"), overwrite=True)
|
szs.create(extracted_szs, extracted_szs.with_suffix(".szs"), overwrite=True)
|
||||||
shutil.rmtree(str(extracted_szs.resolve()))
|
shutil.rmtree(str(extracted_szs.resolve()))
|
||||||
|
@ -118,7 +122,7 @@ class ExtractedGame:
|
||||||
:param cache_directory: Path to the cache
|
:param cache_directory: Path to the cache
|
||||||
:param mod_config: mod configuration
|
:param mod_config: mod configuration
|
||||||
"""
|
"""
|
||||||
yield Progress(description="Patching LECODE.bin")
|
yield Progress(description=_("PATCHING", " LECODE.bin"))
|
||||||
cache_directory = Path(cache_directory)
|
cache_directory = Path(cache_directory)
|
||||||
cttracks_directory = Path(cttracks_directory)
|
cttracks_directory = Path(cttracks_directory)
|
||||||
ogtracks_directory = Path(ogtracks_directory)
|
ogtracks_directory = Path(ogtracks_directory)
|
||||||
|
@ -159,7 +163,7 @@ class ExtractedGame:
|
||||||
Used before the lecode patch is applied
|
Used before the lecode patch is applied
|
||||||
:param mod_config: the mod to install
|
:param mod_config: the mod to install
|
||||||
"""
|
"""
|
||||||
yield Progress(description="Installing all Pre-Patch...", determinate=False)
|
yield Progress(description=_("INSTALLING_ALL", " Pre-Patchs..."), determinate=False)
|
||||||
yield from self._install_all_patch(mod_config, "_PREPATCH/")
|
yield from self._install_all_patch(mod_config, "_PREPATCH/")
|
||||||
|
|
||||||
def install_all_patch(self, mod_config: ModConfig) -> Generator[Progress, None, None]:
|
def install_all_patch(self, mod_config: ModConfig) -> Generator[Progress, None, None]:
|
||||||
|
@ -168,7 +172,7 @@ class ExtractedGame:
|
||||||
Used after the lecode patch is applied
|
Used after the lecode patch is applied
|
||||||
:param mod_config: the mod to install
|
:param mod_config: the mod to install
|
||||||
"""
|
"""
|
||||||
yield Progress(description="Installing all Patch...", determinate=False)
|
yield Progress(description=_("INSTALLING_ALL", " Patchs..."), determinate=False)
|
||||||
yield from self._install_all_patch(mod_config, "_PATCH/")
|
yield from self._install_all_patch(mod_config, "_PATCH/")
|
||||||
|
|
||||||
def convert_to(self, output_type: wit.Extension) -> Generator[Progress, None, wit.WITPath | None]:
|
def convert_to(self, output_type: wit.Extension) -> Generator[Progress, None, wit.WITPath | None]:
|
||||||
|
@ -177,7 +181,7 @@ class ExtractedGame:
|
||||||
:param output_type: path to the destination of the game
|
:param output_type: path to the destination of the game
|
||||||
:output_type: format of the destination game
|
:output_type: format of the destination game
|
||||||
"""
|
"""
|
||||||
yield Progress(description=f"Converting game to {output_type}", determinate=False)
|
yield Progress(description=_("CONVERTING_GAME_TO", " ", output_type.name), determinate=False)
|
||||||
if output_type == wit.Extension.FST: return
|
if output_type == wit.Extension.FST: return
|
||||||
|
|
||||||
destination_file = self.path.with_suffix(self.path.suffix + output_type.value)
|
destination_file = self.path.with_suffix(self.path.suffix + output_type.value)
|
||||||
|
@ -193,7 +197,7 @@ class ExtractedGame:
|
||||||
destination_file=destination_file,
|
destination_file=destination_file,
|
||||||
)
|
)
|
||||||
|
|
||||||
yield Progress(description="Deleting the extracted game...", determinate=False)
|
yield Progress(description=_("DELETING_EXTRACTED_GAME"), determinate=False)
|
||||||
shutil.rmtree(self.path)
|
shutil.rmtree(self.path)
|
||||||
|
|
||||||
return converted_game
|
return converted_game
|
||||||
|
|
|
@ -6,18 +6,19 @@ from source.mkw.ModConfig import ModConfig
|
||||||
from source.option import Option
|
from source.option import Option
|
||||||
from source.progress import Progress
|
from source.progress import Progress
|
||||||
from source.wt.wit import WITPath, Region, Extension
|
from source.wt.wit import WITPath, Region, Extension
|
||||||
|
from source.translation import translate as _
|
||||||
|
|
||||||
|
|
||||||
class NotMKWGameError(Exception):
|
class NotMKWGameError(Exception):
|
||||||
def __init__(self, path: "Path | str"):
|
def __init__(self, path: "Path | str"):
|
||||||
path = Path(path)
|
path = Path(path)
|
||||||
super().__init__(f'Not a Mario Kart Wii game : "{path.name}"')
|
super().__init__(_("NOT_MKW_GAME", ' : "', path.name, '"'))
|
||||||
|
|
||||||
|
|
||||||
class NotVanillaError(Exception):
|
class NotVanillaError(Exception):
|
||||||
def __init__(self, path: "Path | str"):
|
def __init__(self, path: "Path | str"):
|
||||||
path = Path(path)
|
path = Path(path)
|
||||||
super().__init__(f'This game is already modded : "{path.name}"')
|
super().__init__(_("GAME_ALREADY_MODDED", ' : "', path.name, '"'))
|
||||||
|
|
||||||
|
|
||||||
class Game:
|
class Game:
|
||||||
|
@ -46,15 +47,15 @@ class Game:
|
||||||
gen = self.wit_path.progress_extract_all(dest)
|
gen = self.wit_path.progress_extract_all(dest)
|
||||||
|
|
||||||
if self.wit_path.extension == Extension.FST:
|
if self.wit_path.extension == Extension.FST:
|
||||||
for _ in gen: yield Progress(description="Copying Game...", determinate=False)
|
for __ in gen: yield Progress(description=_("COPYING_GAME"), determinate=False)
|
||||||
try: next(gen)
|
try: next(gen)
|
||||||
except StopIteration as e: return e.value
|
except StopIteration as e: return e.value
|
||||||
|
|
||||||
else:
|
else:
|
||||||
for gen_data in gen:
|
for gen_data in gen:
|
||||||
yield Progress(
|
yield Progress(
|
||||||
description=f'Extracting - {gen_data["percentage"]}% - (estimated time remaining: '
|
description=_("EXTRACTING", " - ", gen_data["percentage"], "% - (", "ESTIMATED_TIME_REMAINING", ": "
|
||||||
f'{gen_data["estimation"] if gen_data["estimation"] is not None else "-:--"})',
|
f'{gen_data["estimation"] if gen_data["estimation"] is not None else "-:--"})'),
|
||||||
max_step=100,
|
max_step=100,
|
||||||
set_step=gen_data["percentage"],
|
set_step=gen_data["percentage"],
|
||||||
determinate=True
|
determinate=True
|
||||||
|
@ -64,7 +65,7 @@ class Game:
|
||||||
return e.value
|
return e.value
|
||||||
|
|
||||||
def edit(self, mod_config: ModConfig) -> Generator[Progress, None, None]:
|
def edit(self, mod_config: ModConfig) -> Generator[Progress, None, None]:
|
||||||
yield Progress(description="Changing game metadata...", determinate=False)
|
yield Progress(description=_("CHANGING_GAME_METADATA"), determinate=False)
|
||||||
self.wit_path.edit(
|
self.wit_path.edit(
|
||||||
name=mod_config.name,
|
name=mod_config.name,
|
||||||
game_id=self.wit_path.id[:4] + mod_config.variant
|
game_id=self.wit_path.id[:4] + mod_config.variant
|
||||||
|
@ -117,16 +118,16 @@ class Game:
|
||||||
if not self.is_vanilla(): raise NotVanillaError(self.wit_path.path)
|
if not self.is_vanilla(): raise NotVanillaError(self.wit_path.path)
|
||||||
|
|
||||||
# extract the game
|
# extract the game
|
||||||
yield Progress(title="Extraction", set_part=1)
|
yield Progress(title=_("EXTRACTION"), set_part=1)
|
||||||
yield from self.extract(extracted_game.path)
|
yield from self.extract(extracted_game.path)
|
||||||
|
|
||||||
# install mystuff
|
# install mystuff
|
||||||
yield Progress(title="MyStuff", set_part=2)
|
yield Progress(title=_("MYSTUFF"), set_part=2)
|
||||||
mystuff_data = options["mystuff_packs"].get(options["mystuff_pack_selected"])
|
mystuff_data = options["mystuff_packs"].get(options["mystuff_pack_selected"])
|
||||||
if mystuff_data is not None: yield from extracted_game.install_multiple_mystuff(mystuff_data["paths"])
|
if mystuff_data is not None: yield from extracted_game.install_multiple_mystuff(mystuff_data["paths"])
|
||||||
|
|
||||||
# prepare the cache
|
# prepare the cache
|
||||||
yield Progress(title="Preparing files", set_part=3)
|
yield Progress(title=_("PREPARING_FILES"), set_part=3)
|
||||||
yield from extracted_game.extract_autoadd(cache_autoadd_directory)
|
yield from extracted_game.extract_autoadd(cache_autoadd_directory)
|
||||||
yield from extracted_game.extract_original_tracks(cache_ogtracks_directory)
|
yield from extracted_game.extract_original_tracks(cache_ogtracks_directory)
|
||||||
yield from mod_config.normalize_all_tracks(
|
yield from mod_config.normalize_all_tracks(
|
||||||
|
@ -139,7 +140,7 @@ class Game:
|
||||||
yield from extracted_game.prepare_special_file(mod_config)
|
yield from extracted_game.prepare_special_file(mod_config)
|
||||||
|
|
||||||
# prepatch the game
|
# prepatch the game
|
||||||
yield Progress(title="Pre-Patching", set_part=4)
|
yield Progress(title=_("PRE-PATCHING"), set_part=4)
|
||||||
yield from extracted_game.install_all_prepatch(mod_config)
|
yield from extracted_game.install_all_prepatch(mod_config)
|
||||||
|
|
||||||
yield Progress(title="LE-CODE", set_part=5)
|
yield Progress(title="LE-CODE", set_part=5)
|
||||||
|
@ -150,11 +151,11 @@ class Game:
|
||||||
cache_ogtracks_directory,
|
cache_ogtracks_directory,
|
||||||
)
|
)
|
||||||
|
|
||||||
yield Progress(title="Patching", set_part=6)
|
yield Progress(title=_("PATCHING"), set_part=6)
|
||||||
yield from extracted_game.install_all_patch(mod_config)
|
yield from extracted_game.install_all_patch(mod_config)
|
||||||
yield from extracted_game.recreate_all_szs()
|
yield from extracted_game.recreate_all_szs()
|
||||||
|
|
||||||
# convert the extracted game into a file
|
# convert the extracted game into a file
|
||||||
yield Progress(title="Converting to game file", set_part=7)
|
yield Progress(title=_("CONVERTING_TO_GAME_FILE"), set_part=7)
|
||||||
converted_game: WITPath = yield from extracted_game.convert_to(output_type)
|
converted_game: WITPath = yield from extracted_game.convert_to(output_type)
|
||||||
if converted_game is not None: yield from Game(converted_game.path).edit(mod_config)
|
if converted_game is not None: yield from Game(converted_game.path).edit(mod_config)
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
|
from source.translation import translate as _
|
||||||
|
|
||||||
|
|
||||||
class ColorNotFound(Exception):
|
class ColorNotFound(Exception):
|
||||||
def __init__(self, color_data: any):
|
def __init__(self, color_data: any):
|
||||||
super().__init__(f'Can\'t find color "{color_data}"')
|
super().__init__(_("CANNOT_FIND_COLOR", ' "', color_data, '"'))
|
||||||
|
|
||||||
|
|
||||||
class MKWColor:
|
class MKWColor:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import shutil
|
import shutil
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Generator, Callable, Iterator, Iterable, TYPE_CHECKING
|
from typing import Generator, Callable, Iterator, Iterable, TYPE_CHECKING
|
||||||
|
import json
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from source import threaded
|
from source import threaded
|
||||||
|
@ -10,12 +10,11 @@ from source.mkw.Cup import Cup
|
||||||
from source.mkw.MKWColor import bmg_color_text, bmg_color_raw
|
from source.mkw.MKWColor import bmg_color_text, bmg_color_raw
|
||||||
from source.mkw.ModSettings import AbstractModSettings
|
from source.mkw.ModSettings import AbstractModSettings
|
||||||
from source.mkw.Track import CustomTrack, DefaultTrack, Arena
|
from source.mkw.Track import CustomTrack, DefaultTrack, Arena
|
||||||
import json
|
|
||||||
|
|
||||||
from source.mkw.OriginalTrack import OriginalTrack
|
from source.mkw.OriginalTrack import OriginalTrack
|
||||||
from source.progress import Progress
|
from source.progress import Progress
|
||||||
from source.safe_eval import safe_eval, multiple_safe_eval
|
from source.safe_eval import safe_eval, multiple_safe_eval
|
||||||
from source.wt.szs import SZSPath
|
from source.wt.szs import SZSPath
|
||||||
|
from source.translation import translate as _
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from source import TemplateMultipleSafeEval, TemplateSafeEval, Env
|
from source import TemplateMultipleSafeEval, TemplateSafeEval, Env
|
||||||
|
@ -414,7 +413,7 @@ class ModConfig:
|
||||||
:param autoadd_path: autoadd directory
|
:param autoadd_path: autoadd directory
|
||||||
:param destination_path: destination where the files are converted
|
:param destination_path: destination where the files are converted
|
||||||
"""
|
"""
|
||||||
yield Progress(description="Normalizing track...")
|
yield Progress(description=_("NORMALIZING_TRACKS"))
|
||||||
destination_path = Path(destination_path)
|
destination_path = Path(destination_path)
|
||||||
original_tracks_path = Path(original_tracks_path)
|
original_tracks_path = Path(original_tracks_path)
|
||||||
destination_path.mkdir(parents=True, exist_ok=True)
|
destination_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
@ -429,7 +428,7 @@ class ModConfig:
|
||||||
nonlocal normalize_threads
|
nonlocal normalize_threads
|
||||||
|
|
||||||
yield Progress(
|
yield Progress(
|
||||||
description=f"Normalizing tracks :\n" + "\n".join(thread['name'] for thread in normalize_threads)
|
description=_("NORMALIZING_TRACKS", " :\n" + "\n".join(thread['name'] for thread in normalize_threads))
|
||||||
)
|
)
|
||||||
normalize_threads = list(filter(lambda thread: thread["thread"].is_alive(), normalize_threads))
|
normalize_threads = list(filter(lambda thread: thread["thread"].is_alive(), normalize_threads))
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,12 @@ import tkinter
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
from source.translation import translate as _
|
||||||
|
|
||||||
|
|
||||||
class InvalidSettingsType(Exception):
|
class InvalidSettingsType(Exception):
|
||||||
def __init__(self, settings_type: str):
|
def __init__(self, settings_type: str):
|
||||||
super().__init__(f"Error : Type of mod settings '{settings_type}' not found.")
|
super().__init__(_("TYPE_MOD_SETTINGS", " '", settings_type, "' ", "NOT_FOUND"))
|
||||||
|
|
||||||
|
|
||||||
class AbstractModSettings(ABC):
|
class AbstractModSettings(ABC):
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
|
from source.translation import translate as _
|
||||||
|
|
||||||
|
|
||||||
class OriginalTrackNotFound(Exception):
|
class OriginalTrackNotFound(Exception):
|
||||||
def __init__(self, track_data: any):
|
def __init__(self, track_data: any):
|
||||||
super().__init__(f'Can\'t find original track "{track_data}"')
|
super().__init__(_("CANNOT_FIND_ORIGINAL_TRACK", ' "', track_data, '" '))
|
||||||
|
|
||||||
|
|
||||||
class OriginalTrack:
|
class OriginalTrack:
|
||||||
|
|
|
@ -2,6 +2,8 @@ from pathlib import Path
|
||||||
from typing import Generator, IO, TYPE_CHECKING
|
from typing import Generator, IO, TYPE_CHECKING
|
||||||
|
|
||||||
from source.progress import Progress
|
from source.progress import Progress
|
||||||
|
from source.translation import translate as _
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from source.mkw.ModConfig import ModConfig
|
from source.mkw.ModConfig import ModConfig
|
||||||
|
@ -27,7 +29,7 @@ class Patch:
|
||||||
:param extracted_game: the extracted game
|
:param extracted_game: the extracted game
|
||||||
"""
|
"""
|
||||||
from source.mkw.Patch.PatchDirectory import PatchDirectory
|
from source.mkw.Patch.PatchDirectory import PatchDirectory
|
||||||
yield Progress(description=f"Installing the patch", determinate=False)
|
yield Progress(description=_("INSTALLING_PATCH"), determinate=False)
|
||||||
|
|
||||||
# take all the files in the root directory, and patch them into the game.
|
# take all the files in the root directory, and patch them into the game.
|
||||||
# Patch is not directly applied to the root to avoid custom configuration
|
# Patch is not directly applied to the root to avoid custom configuration
|
||||||
|
|
|
@ -4,6 +4,7 @@ from typing import Generator, TYPE_CHECKING
|
||||||
from source.mkw.Patch import PathOutsidePatch, InvalidPatchMode
|
from source.mkw.Patch import PathOutsidePatch, InvalidPatchMode
|
||||||
from source.mkw.Patch.PatchObject import PatchObject
|
from source.mkw.Patch.PatchObject import PatchObject
|
||||||
from source.progress import Progress
|
from source.progress import Progress
|
||||||
|
from source.translation import translate as _
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from source.mkw.ExtractedGame import ExtractedGame
|
from source.mkw.ExtractedGame import ExtractedGame
|
||||||
|
@ -27,7 +28,7 @@ class PatchDirectory(PatchObject):
|
||||||
"""
|
"""
|
||||||
patch a subdirectory of the game with the PatchDirectory
|
patch a subdirectory of the game with the PatchDirectory
|
||||||
"""
|
"""
|
||||||
yield Progress(description=f"Patching {game_subpath}")
|
yield Progress(description=_("PATCHING", " ", game_subpath))
|
||||||
|
|
||||||
# check if the directory should be patched
|
# check if the directory should be patched
|
||||||
if not self.is_enabled(extracted_game): return
|
if not self.is_enabled(extracted_game): return
|
||||||
|
|
|
@ -7,6 +7,7 @@ from source.mkw.Patch.PatchOperation import AbstractPatchOperation
|
||||||
from source.mkw.Patch.PatchObject import PatchObject
|
from source.mkw.Patch.PatchObject import PatchObject
|
||||||
from source.progress import Progress
|
from source.progress import Progress
|
||||||
from source.wt.szs import SZSPath
|
from source.wt.szs import SZSPath
|
||||||
|
from source.translation import translate
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from source.mkw.ExtractedGame import ExtractedGame
|
from source.mkw.ExtractedGame import ExtractedGame
|
||||||
|
@ -81,7 +82,8 @@ class PatchFile(PatchObject):
|
||||||
"""
|
"""
|
||||||
patch a subfile of the game with the PatchFile
|
patch a subfile of the game with the PatchFile
|
||||||
"""
|
"""
|
||||||
yield Progress(description=f"Patching {game_subpath}")
|
yield Progress(description=translate("PATCHING", " ", game_subpath))
|
||||||
|
# translate is not renamed "_" here because it is used to drop useless value in unpacking
|
||||||
|
|
||||||
# check if the file should be patched
|
# check if the file should be patched
|
||||||
if not self.is_enabled(extracted_game): return
|
if not self.is_enabled(extracted_game): return
|
||||||
|
@ -119,7 +121,7 @@ class PatchFile(PatchObject):
|
||||||
if not game_subfile.relative_to(extracted_game.path):
|
if not game_subfile.relative_to(extracted_game.path):
|
||||||
raise PathOutsidePatch(game_subfile, extracted_game.path)
|
raise PathOutsidePatch(game_subfile, extracted_game.path)
|
||||||
|
|
||||||
yield Progress(description=f"Patching {game_subfile}")
|
yield Progress(description=translate("PATCHING", " ", game_subfile))
|
||||||
|
|
||||||
# if the source is the game, then recalculate the content for every game subfile
|
# if the source is the game, then recalculate the content for every game subfile
|
||||||
if self.configuration["source"] == "game":
|
if self.configuration["source"] == "game":
|
||||||
|
|
|
@ -3,6 +3,7 @@ from typing import IO, TYPE_CHECKING
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
|
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
|
||||||
|
from source.translation import translate as _
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from source.mkw.Patch import Patch
|
from source.mkw.Patch import Patch
|
||||||
|
@ -10,7 +11,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
class InvalidBmgLayerMode(Exception):
|
class InvalidBmgLayerMode(Exception):
|
||||||
def __init__(self, layer_mode: str):
|
def __init__(self, layer_mode: str):
|
||||||
super().__init__(f"Error : bmg layer mode \"{layer_mode}\" is not implemented")
|
super().__init__(_("BMG_LAYER_MODE", ' "', layer_mode, '" ', "IS_NOT_IMPLEMENTED"))
|
||||||
|
|
||||||
|
|
||||||
class AbstractLayer(ABC):
|
class AbstractLayer(ABC):
|
||||||
|
|
|
@ -4,6 +4,7 @@ from abc import abstractmethod, ABC
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
|
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
|
||||||
|
from source.translation import translate as _
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from source.mkw.Patch import Patch
|
from source.mkw.Patch import Patch
|
||||||
|
@ -11,7 +12,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
class InvalidImageLayerType(Exception):
|
class InvalidImageLayerType(Exception):
|
||||||
def __init__(self, layer_type: str):
|
def __init__(self, layer_type: str):
|
||||||
super().__init__(f"Error : image layer type \"{layer_type}\" is not implemented")
|
super().__init__(_("IMAGE_LAYER_TYPE", ' "', layer_type, '" ', "IS_NOT_IMPLEMENTED"))
|
||||||
|
|
||||||
|
|
||||||
class AbstractLayer(ABC):
|
class AbstractLayer(ABC):
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from typing import IO, Type, TYPE_CHECKING
|
from typing import IO, Type, TYPE_CHECKING
|
||||||
|
|
||||||
|
from source.translation import translate as _
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from source.mkw.Patch import Patch
|
from source.mkw.Patch import Patch
|
||||||
|
|
||||||
|
|
||||||
class InvalidPatchOperation(Exception):
|
class InvalidPatchOperation(Exception):
|
||||||
def __init__(self, operation: str):
|
def __init__(self, operation: str):
|
||||||
super().__init__(f"Error : operation \"{operation}\" is not implemented")
|
super().__init__(_("OPERATION", ' "', operation, '" ', "IS_NOT_IMPLEMENTED"))
|
||||||
|
|
||||||
|
|
||||||
class AbstractPatchOperation(ABC):
|
class AbstractPatchOperation(ABC):
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
from source.translation import translate as _
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from source.mkw.Patch import Patch
|
from source.mkw.Patch import Patch
|
||||||
|
@ -8,14 +9,16 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
class PathOutsidePatch(Exception):
|
class PathOutsidePatch(Exception):
|
||||||
def __init__(self, forbidden_path: Path, allowed_range: Path):
|
def __init__(self, forbidden_path: Path, allowed_range: Path):
|
||||||
super().__init__(f'Error : path "{forbidden_path}" outside of allowed range {allowed_range}')
|
super().__init__(_("PATH", ' "', forbidden_path, '" ', "OUTSIDE_ALLOWED_RANGE", ' "', {allowed_range}, '" '))
|
||||||
|
|
||||||
|
|
||||||
class InvalidPatchMode(Exception):
|
class InvalidPatchMode(Exception):
|
||||||
def __init__(self, patch: "PatchObject", mode: str):
|
def __init__(self, patch: "PatchObject", mode: str):
|
||||||
super().__init__(f'Error : mode "{mode}" is not implemented (in patch : "{patch.full_path}")')
|
super().__init__(_("MODE", ' "', mode, '" ', "IS_NOT_IMPLEMENTED",
|
||||||
|
"(", "IN_PATCH", ' : "', patch.full_path, '")'))
|
||||||
|
|
||||||
|
|
||||||
class InvalidSourceMode(Exception):
|
class InvalidSourceMode(Exception):
|
||||||
def __init__(self, patch: "PatchObject", source: str):
|
def __init__(self, patch: "PatchObject", source: str):
|
||||||
super().__init__(f'Error : source "{source}" is not implemented (in patch : "{patch.full_path}")')
|
super().__init__(_("SOURCE", ' "', source, '" ', "IS_NOT_IMPLEMENTED",
|
||||||
|
"(", "IN_PATCH", ' : "', patch.full_path, '")'))
|
||||||
|
|
|
@ -2,6 +2,7 @@ from abc import ABC, abstractmethod
|
||||||
from typing import Generator, TYPE_CHECKING
|
from typing import Generator, TYPE_CHECKING
|
||||||
|
|
||||||
from source.mkw import Slot, Tag, ModConfig
|
from source.mkw import Slot, Tag, ModConfig
|
||||||
|
from source.translation import translate as _
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from source import TemplateMultipleSafeEval
|
from source import TemplateMultipleSafeEval
|
||||||
|
@ -9,7 +10,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
class TrackForbiddenCustomAttribute(Exception):
|
class TrackForbiddenCustomAttribute(Exception):
|
||||||
def __init__(self, attribute_name: str):
|
def __init__(self, attribute_name: str):
|
||||||
super().__init__(f"Forbidden track attribute : {attribute_name!r}")
|
super().__init__(_("FORBIDDEN_TRACK_ATTRIBUTE", " : ", repr(attribute_name)))
|
||||||
|
|
||||||
|
|
||||||
class AbstractTrack(ABC):
|
class AbstractTrack(ABC):
|
||||||
|
|
|
@ -2,14 +2,16 @@ from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from source.mkw import Slot, Tag
|
from source.mkw import Slot, Tag
|
||||||
from source.mkw.Track.RealArenaTrack import RealArenaTrack
|
from source.mkw.Track.RealArenaTrack import RealArenaTrack
|
||||||
|
from source.translation import translate as _
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from source import TemplateMultipleSafeEval
|
from source import TemplateMultipleSafeEval
|
||||||
|
from source.mkw.ModConfig import ModConfig
|
||||||
|
|
||||||
|
|
||||||
class ArenaForbiddenCustomAttribute(Exception):
|
class ArenaForbiddenCustomAttribute(Exception):
|
||||||
def __init__(self, attribute_name: str):
|
def __init__(self, attribute_name: str):
|
||||||
super().__init__(f"Forbidden arena attribute : {attribute_name!r}")
|
super().__init__(_("FORBIDDEN_ARENA_ATTRIBUTE", " : ", repr(attribute_name)))
|
||||||
|
|
||||||
|
|
||||||
class Arena(RealArenaTrack):
|
class Arena(RealArenaTrack):
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from source.mkw.Track.AbstractTrack import AbstractTrack
|
from source.mkw.Track.AbstractTrack import AbstractTrack
|
||||||
from source.mkw.Track.RealArenaTrack import RealArenaTrack
|
from source.mkw.Track.RealArenaTrack import RealArenaTrack
|
||||||
|
|
||||||
ModConfig: any
|
if TYPE_CHECKING:
|
||||||
|
from source.mkw.ModConfig import ModConfig
|
||||||
|
|
||||||
|
|
||||||
class CustomTrack(RealArenaTrack, AbstractTrack):
|
class CustomTrack(RealArenaTrack, AbstractTrack):
|
||||||
|
|
|
@ -2,6 +2,7 @@ from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from source import TemplateMultipleSafeEval
|
from source import TemplateMultipleSafeEval
|
||||||
|
from source.mkw import Tag
|
||||||
from source.mkw.ModConfig import ModConfig
|
from source.mkw.ModConfig import ModConfig
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,7 +12,7 @@ class RealArenaTrack:
|
||||||
(For example, DefaultTrack is not considered a real track class)
|
(For example, DefaultTrack is not considered a real track class)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
tags: list
|
tags: list["Tag"]
|
||||||
|
|
||||||
def get_tag_template(self, mod_config: "ModConfig", template_name: str, default: any = None) -> any:
|
def get_tag_template(self, mod_config: "ModConfig", template_name: str, default: any = None) -> any:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import re
|
import re
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from source.translation import translate as _
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from source import TemplateSafeEval
|
from source import TemplateSafeEval
|
||||||
|
|
||||||
|
@ -9,7 +12,7 @@ MACRO_START, MACRO_END = "##", "##"
|
||||||
|
|
||||||
class NotImplementedMacro(Exception):
|
class NotImplementedMacro(Exception):
|
||||||
def __init__(self, macro: str):
|
def __init__(self, macro: str):
|
||||||
super().__init__(f"Invalid macro while parsing macros:\n{macro}")
|
super().__init__(_("INVALID_MACRO", ' : "', macro, '"'))
|
||||||
|
|
||||||
|
|
||||||
def replace_macro(template: str, macros: dict[str, "TemplateSafeEval"]) -> str:
|
def replace_macro(template: str, macros: dict[str, "TemplateSafeEval"]) -> str:
|
||||||
|
|
|
@ -4,6 +4,8 @@ from typing import TYPE_CHECKING, Iterable, Callable
|
||||||
|
|
||||||
from source.safe_eval.macros import replace_macro
|
from source.safe_eval.macros import replace_macro
|
||||||
from source.safe_eval.safe_function import get_all_safe_functions
|
from source.safe_eval.safe_function import get_all_safe_functions
|
||||||
|
from source.translation import translate as _
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from source import TemplateSafeEval, Env
|
from source import TemplateSafeEval, Env
|
||||||
|
@ -59,7 +61,7 @@ def safe_eval(template: "TemplateSafeEval", env: "Env" = None, macros: dict[str,
|
||||||
# convert the template to an ast expression
|
# convert the template to an ast expression
|
||||||
stmt: ast.stmt = ast.parse(template).body[0]
|
stmt: ast.stmt = ast.parse(template).body[0]
|
||||||
if not isinstance(stmt, ast.Expr):
|
if not isinstance(stmt, ast.Expr):
|
||||||
raise SafeEvalException(f'Invalid ast type : "{type(stmt).__name__}"')
|
raise SafeEvalException(_("INVALID_AST_TYPE", ' : "', type(stmt).__name__, '"'))
|
||||||
|
|
||||||
# check every node for disabled expression
|
# check every node for disabled expression
|
||||||
for node in ast.walk(stmt):
|
for node in ast.walk(stmt):
|
||||||
|
@ -69,20 +71,20 @@ def safe_eval(template: "TemplateSafeEval", env: "Env" = None, macros: dict[str,
|
||||||
case ast.Attribute:
|
case ast.Attribute:
|
||||||
# ban all magical function, disabling the __class__.__bases__[0] ... tricks
|
# ban all magical function, disabling the __class__.__bases__[0] ... tricks
|
||||||
if "__" in node.attr:
|
if "__" in node.attr:
|
||||||
raise SafeEvalException(f'Magic attribute are forbidden : "{node.attr}"')
|
raise SafeEvalException(_("MAGIC_ATTRIBUTE_ARE_FORBIDDEN", ' : "', node.attr, '"'))
|
||||||
|
|
||||||
# ban modification to environment
|
# ban modification to environment
|
||||||
if isinstance(node.ctx, ast.Store):
|
if isinstance(node.ctx, ast.Store):
|
||||||
raise SafeEvalException(f'Can\'t set value of attribute : "{node.attr}"')
|
raise SafeEvalException(_("CANNOT_SET_ATTRIBUTE", ' : "', node.attr, '"'))
|
||||||
|
|
||||||
# when accessing any variable
|
# when accessing any variable
|
||||||
case ast.Name:
|
case ast.Name:
|
||||||
# ban modification to environment, but allow custom variable to be changed
|
# ban modification to environment, but allow custom variable to be changed
|
||||||
if isinstance(node.ctx, ast.Store):
|
if isinstance(node.ctx, ast.Store):
|
||||||
if node.id in globals_ | locals_:
|
if node.id in globals_ | locals_:
|
||||||
raise SafeEvalException(f'Can\'t set value of environment : "{node.id}"')
|
raise SafeEvalException(_("CANNOT_SET_ENVIRONMENT", ' : "', node.id, '"'))
|
||||||
elif node.id in args:
|
elif node.id in args:
|
||||||
raise SafeEvalException(f'Can\'t set value of argument : "{node.id}"')
|
raise SafeEvalException(_("CANNOT_SET_ARGUMENT", ' : "', node.id, '"'))
|
||||||
|
|
||||||
# when calling any function
|
# when calling any function
|
||||||
case ast.Call:
|
case ast.Call:
|
||||||
|
@ -91,10 +93,10 @@ def safe_eval(template: "TemplateSafeEval", env: "Env" = None, macros: dict[str,
|
||||||
if isinstance(callnode, ast.Attribute):
|
if isinstance(callnode, ast.Attribute):
|
||||||
for attrnode in ast.walk(callnode.value):
|
for attrnode in ast.walk(callnode.value):
|
||||||
if isinstance(attrnode, ast.Name):
|
if isinstance(attrnode, ast.Name):
|
||||||
if attrnode.id in globals_ | locals_:
|
if attrnode.id in globals_ | locals_ or attrnode.id in args:
|
||||||
raise SafeEvalException(f'Calling this function is not allowed : "{callnode.attr}"')
|
raise SafeEvalException(
|
||||||
if attrnode.id in args:
|
_("CALLING_FUNCTION_NOT_ALLOWED", ' : "', callnode.attr, '"')
|
||||||
raise SafeEvalException(f'Calling this function is not allowed : "{callnode.attr}"')
|
)
|
||||||
|
|
||||||
# when assigning a value with ":="
|
# when assigning a value with ":="
|
||||||
case ast.NamedExpr:
|
case ast.NamedExpr:
|
||||||
|
@ -115,7 +117,7 @@ def safe_eval(template: "TemplateSafeEval", env: "Env" = None, macros: dict[str,
|
||||||
ast.ClassDef | # Declaring class could maybe allow for dangerous calls
|
ast.ClassDef | # Declaring class could maybe allow for dangerous calls
|
||||||
ast.AsyncFor | ast.AsyncWith | ast.AsyncFunctionDef | ast.Await # Just in case
|
ast.AsyncFor | ast.AsyncWith | ast.AsyncFunctionDef | ast.Await # Just in case
|
||||||
):
|
):
|
||||||
raise SafeEvalException(f'Forbidden syntax : "{type(node).__name__}"')
|
raise SafeEvalException(_("FORBIDDEN_SYNTAX", ' : "', type(node).__name__, '"'))
|
||||||
|
|
||||||
# embed the whole expression into a lambda expression
|
# embed the whole expression into a lambda expression
|
||||||
stmt.value = ast.Lambda(
|
stmt.value = ast.Lambda(
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
from typing import Callable, Generator, TYPE_CHECKING
|
from typing import Callable, Generator, TYPE_CHECKING
|
||||||
|
|
||||||
|
from source.translation import translate as _
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from source import TemplateSafeEval, Env
|
from source import TemplateSafeEval
|
||||||
|
|
||||||
|
|
||||||
def get_all_safe_functions() -> Generator[list[Callable], None, None]:
|
def get_all_safe_functions() -> Generator[list[Callable], None, None]:
|
||||||
|
@ -33,7 +35,7 @@ class safe_function:
|
||||||
"""
|
"""
|
||||||
Same as normal getattr, but magic attribute are banned
|
Same as normal getattr, but magic attribute are banned
|
||||||
"""
|
"""
|
||||||
if "__" in name: raise Exception(f'Magic method are not allowed : "{name}"')
|
if "__" in name: raise Exception(_("MAGIC_METHOD_FORBIDDEN", ' : "', name, '"'))
|
||||||
return getattr(obj, name, default)
|
return getattr(obj, name, default)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -3,6 +3,8 @@ from pathlib import Path
|
||||||
import os
|
import os
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
|
from source.translation import translate as _
|
||||||
|
|
||||||
|
|
||||||
class WTError(Exception):
|
class WTError(Exception):
|
||||||
def __init__(self, tools_path: Path | str, return_code: int):
|
def __init__(self, tools_path: Path | str, return_code: int):
|
||||||
|
@ -14,14 +16,14 @@ class WTError(Exception):
|
||||||
creationflags=subprocess.CREATE_NO_WINDOW,
|
creationflags=subprocess.CREATE_NO_WINDOW,
|
||||||
).stdout.decode()
|
).stdout.decode()
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
error = "- Can't get the error message -"
|
error = _("- ", "CANNOT_GET_ERROR_MESSAGE", " -")
|
||||||
|
|
||||||
super().__init__(f"{tools_path} raised {return_code} :\n{error}\n")
|
super().__init__(_(tools_path, " ", "RAISED", " ", return_code, ":\n", error, "\n"))
|
||||||
|
|
||||||
|
|
||||||
class MissingWTError(Exception):
|
class MissingWTError(Exception):
|
||||||
def __init__(self, tool_name: str):
|
def __init__(self, tool_name: str):
|
||||||
super().__init__(f"Can't find tools \"{tool_name}\" in the tools directory.")
|
super().__init__(_("CANNOT_FIND_TOOL", ' "', tool_name, '" ', "IN_TOOLS_DIRECTORY"))
|
||||||
|
|
||||||
|
|
||||||
tools_dir = Path("./tools/")
|
tools_dir = Path("./tools/")
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from source.wt import *
|
from source.wt import *
|
||||||
from source.wt import _run, _run_dict
|
from source.translation import translate as _
|
||||||
|
|
||||||
|
|
||||||
tools_path = tools_szs_dir / "wszst"
|
tools_path = tools_szs_dir / "wszst"
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ def autoadd(course_directory: Path | str, destination_path: Path | str) -> Path:
|
||||||
:return: directory where the autoadd files were extracted
|
:return: directory where the autoadd files were extracted
|
||||||
"""
|
"""
|
||||||
destination_path = Path(destination_path)
|
destination_path = Path(destination_path)
|
||||||
_run(tools_path, "AUTOADD", course_directory, "-D", destination_path)
|
_tools_run("AUTOADD", course_directory, "-D", destination_path)
|
||||||
return destination_path
|
return destination_path
|
||||||
|
|
||||||
|
|
||||||
|
@ -191,7 +192,7 @@ class SZSSubPath:
|
||||||
:param dest: destination path
|
:param dest: destination path
|
||||||
:return: the extracted file path
|
:return: the extracted file path
|
||||||
"""
|
"""
|
||||||
if self.is_dir(): raise ValueError("Can't extract a directory")
|
if self.is_dir(): raise ValueError(_("CANNOT_EXTRACT_A_DIRECTORY"))
|
||||||
|
|
||||||
dest: Path = Path(dest)
|
dest: Path = Path(dest)
|
||||||
if dest.is_dir(): dest /= self.basename()
|
if dest.is_dir(): dest /= self.basename()
|
||||||
|
|
Loading…
Reference in a new issue