mirror of
https://github.com/Faraphel/Atlas-Install.git
synced 2025-07-02 10:48:29 +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",
|
||||
"TRACK_FILTER": "Track Filters",
|
||||
"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):
|
||||
try: return func(*args, **kwargs)
|
||||
except:
|
||||
except Exception:
|
||||
exc = traceback.format_exc()
|
||||
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")
|
||||
|
|
|
@ -24,12 +24,12 @@ from source.wt.wit import Extension
|
|||
|
||||
class SourceGameError(Exception):
|
||||
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):
|
||||
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):
|
||||
|
@ -183,7 +183,7 @@ class Menu(tkinter.Menu):
|
|||
self.root = master.root
|
||||
|
||||
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)
|
||||
|
||||
|
@ -192,7 +192,7 @@ class Menu(tkinter.Menu):
|
|||
super().__init__(master, tearoff=False)
|
||||
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"])
|
||||
|
||||
|
@ -214,12 +214,12 @@ class Menu(tkinter.Menu):
|
|||
super().__init__(master, tearoff=False)
|
||||
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.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=_("ReadTheDocs"), command=lambda: webbrowser.open(readthedocs_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=_("READTHEDOCS"), command=lambda: webbrowser.open(readthedocs_url))
|
||||
|
||||
def set_installation_state(self, state: InstallerState) -> bool:
|
||||
"""
|
||||
|
@ -248,7 +248,7 @@ class Menu(tkinter.Menu):
|
|||
# Select game frame
|
||||
class SourceGame(ttk.LabelFrame):
|
||||
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.columnconfigure(1, weight=1)
|
||||
|
@ -311,7 +311,7 @@ class SourceGame(ttk.LabelFrame):
|
|||
# Select game destination frame
|
||||
class DestinationGame(ttk.LabelFrame):
|
||||
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.columnconfigure(1, weight=1)
|
||||
|
@ -379,7 +379,7 @@ class DestinationGame(ttk.LabelFrame):
|
|||
# Install button
|
||||
class ButtonInstall(ttk.Button):
|
||||
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
|
||||
|
||||
@threaded
|
||||
|
@ -431,7 +431,7 @@ class ButtonInstall(ttk.Button):
|
|||
|
||||
messagebox.showinfo(
|
||||
_("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:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import tkinter
|
||||
from abc import abstractmethod, ABC
|
||||
from typing import Type, TYPE_CHECKING
|
||||
from source.translation import translate as _
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source.mkw.ModConfig import ModConfig
|
||||
|
@ -8,7 +9,7 @@ if TYPE_CHECKING:
|
|||
|
||||
class InvalidPreviewWindowName(Exception):
|
||||
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):
|
||||
|
|
|
@ -8,6 +8,7 @@ from source.mkw.Patch.Patch import Patch
|
|||
from source.progress import Progress
|
||||
from source.wt import szs, lec, wit
|
||||
from source.wt.wstrt import StrPath
|
||||
from source.translation import translate as _
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source.mkw.Game import Game
|
||||
|
@ -15,7 +16,7 @@ if TYPE_CHECKING:
|
|||
|
||||
class PathOutsideMod(Exception):
|
||||
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:
|
||||
|
@ -33,7 +34,7 @@ class ExtractedGame:
|
|||
Extract all the autoadd files from the game to destination_path
|
||||
: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)
|
||||
|
||||
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.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"):
|
||||
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)
|
||||
else: track_file.unlink()
|
||||
|
||||
|
@ -56,7 +60,7 @@ class ExtractedGame:
|
|||
:mystuff_path: path to the MyStuff directory
|
||||
: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_rootfiles: dict[str, Path] = {}
|
||||
|
@ -73,7 +77,7 @@ class ExtractedGame:
|
|||
Install multiple 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:
|
||||
yield from self.install_mystuff(mystuff_path)
|
||||
|
@ -83,7 +87,7 @@ class ExtractedGame:
|
|||
Prepare special files for the patch
|
||||
: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()
|
||||
mod_config.get_full_cticon().save(ct_icons, format="PNG")
|
||||
ct_icons.seek(0)
|
||||
|
@ -100,11 +104,11 @@ class ExtractedGame:
|
|||
"""
|
||||
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 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)
|
||||
shutil.rmtree(str(extracted_szs.resolve()))
|
||||
|
@ -118,7 +122,7 @@ class ExtractedGame:
|
|||
:param cache_directory: Path to the cache
|
||||
:param mod_config: mod configuration
|
||||
"""
|
||||
yield Progress(description="Patching LECODE.bin")
|
||||
yield Progress(description=_("PATCHING", " LECODE.bin"))
|
||||
cache_directory = Path(cache_directory)
|
||||
cttracks_directory = Path(cttracks_directory)
|
||||
ogtracks_directory = Path(ogtracks_directory)
|
||||
|
@ -159,7 +163,7 @@ class ExtractedGame:
|
|||
Used before the lecode patch is applied
|
||||
: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/")
|
||||
|
||||
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
|
||||
: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/")
|
||||
|
||||
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
|
||||
: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
|
||||
|
||||
destination_file = self.path.with_suffix(self.path.suffix + output_type.value)
|
||||
|
@ -193,7 +197,7 @@ class ExtractedGame:
|
|||
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)
|
||||
|
||||
return converted_game
|
||||
|
|
|
@ -6,18 +6,19 @@ from source.mkw.ModConfig import ModConfig
|
|||
from source.option import Option
|
||||
from source.progress import Progress
|
||||
from source.wt.wit import WITPath, Region, Extension
|
||||
from source.translation import translate as _
|
||||
|
||||
|
||||
class NotMKWGameError(Exception):
|
||||
def __init__(self, path: "Path | str"):
|
||||
path = Path(path)
|
||||
super().__init__(f'Not a Mario Kart Wii game : "{path.name}"')
|
||||
super().__init__(_("NOT_MKW_GAME", ' : "', path.name, '"'))
|
||||
|
||||
|
||||
class NotVanillaError(Exception):
|
||||
def __init__(self, path: "Path | str"):
|
||||
path = Path(path)
|
||||
super().__init__(f'This game is already modded : "{path.name}"')
|
||||
super().__init__(_("GAME_ALREADY_MODDED", ' : "', path.name, '"'))
|
||||
|
||||
|
||||
class Game:
|
||||
|
@ -46,15 +47,15 @@ class Game:
|
|||
gen = self.wit_path.progress_extract_all(dest)
|
||||
|
||||
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)
|
||||
except StopIteration as e: return e.value
|
||||
|
||||
else:
|
||||
for gen_data in gen:
|
||||
yield Progress(
|
||||
description=f'Extracting - {gen_data["percentage"]}% - (estimated time remaining: '
|
||||
f'{gen_data["estimation"] if gen_data["estimation"] is not None else "-:--"})',
|
||||
description=_("EXTRACTING", " - ", gen_data["percentage"], "% - (", "ESTIMATED_TIME_REMAINING", ": "
|
||||
f'{gen_data["estimation"] if gen_data["estimation"] is not None else "-:--"})'),
|
||||
max_step=100,
|
||||
set_step=gen_data["percentage"],
|
||||
determinate=True
|
||||
|
@ -64,7 +65,7 @@ class Game:
|
|||
return e.value
|
||||
|
||||
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(
|
||||
name=mod_config.name,
|
||||
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)
|
||||
|
||||
# extract the game
|
||||
yield Progress(title="Extraction", set_part=1)
|
||||
yield Progress(title=_("EXTRACTION"), set_part=1)
|
||||
yield from self.extract(extracted_game.path)
|
||||
|
||||
# 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"])
|
||||
if mystuff_data is not None: yield from extracted_game.install_multiple_mystuff(mystuff_data["paths"])
|
||||
|
||||
# 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_original_tracks(cache_ogtracks_directory)
|
||||
yield from mod_config.normalize_all_tracks(
|
||||
|
@ -139,7 +140,7 @@ class Game:
|
|||
yield from extracted_game.prepare_special_file(mod_config)
|
||||
|
||||
# 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 Progress(title="LE-CODE", set_part=5)
|
||||
|
@ -150,11 +151,11 @@ class Game:
|
|||
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.recreate_all_szs()
|
||||
|
||||
# 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)
|
||||
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):
|
||||
def __init__(self, color_data: any):
|
||||
super().__init__(f'Can\'t find color "{color_data}"')
|
||||
super().__init__(_("CANNOT_FIND_COLOR", ' "', color_data, '"'))
|
||||
|
||||
|
||||
class MKWColor:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import shutil
|
||||
from pathlib import Path
|
||||
from typing import Generator, Callable, Iterator, Iterable, TYPE_CHECKING
|
||||
|
||||
import json
|
||||
from PIL import Image
|
||||
|
||||
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.ModSettings import AbstractModSettings
|
||||
from source.mkw.Track import CustomTrack, DefaultTrack, Arena
|
||||
import json
|
||||
|
||||
from source.mkw.OriginalTrack import OriginalTrack
|
||||
from source.progress import Progress
|
||||
from source.safe_eval import safe_eval, multiple_safe_eval
|
||||
from source.wt.szs import SZSPath
|
||||
from source.translation import translate as _
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source import TemplateMultipleSafeEval, TemplateSafeEval, Env
|
||||
|
@ -414,7 +413,7 @@ class ModConfig:
|
|||
:param autoadd_path: autoadd directory
|
||||
:param destination_path: destination where the files are converted
|
||||
"""
|
||||
yield Progress(description="Normalizing track...")
|
||||
yield Progress(description=_("NORMALIZING_TRACKS"))
|
||||
destination_path = Path(destination_path)
|
||||
original_tracks_path = Path(original_tracks_path)
|
||||
destination_path.mkdir(parents=True, exist_ok=True)
|
||||
|
@ -429,7 +428,7 @@ class ModConfig:
|
|||
nonlocal normalize_threads
|
||||
|
||||
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))
|
||||
|
||||
|
|
|
@ -2,10 +2,12 @@ import tkinter
|
|||
from tkinter import ttk
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
from source.translation import translate as _
|
||||
|
||||
|
||||
class InvalidSettingsType(Exception):
|
||||
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):
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
from source.translation import translate as _
|
||||
|
||||
|
||||
class OriginalTrackNotFound(Exception):
|
||||
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:
|
||||
|
|
|
@ -2,6 +2,8 @@ from pathlib import Path
|
|||
from typing import Generator, IO, TYPE_CHECKING
|
||||
|
||||
from source.progress import Progress
|
||||
from source.translation import translate as _
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source.mkw.ModConfig import ModConfig
|
||||
|
@ -27,7 +29,7 @@ class Patch:
|
|||
:param extracted_game: the extracted game
|
||||
"""
|
||||
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.
|
||||
# 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.PatchObject import PatchObject
|
||||
from source.progress import Progress
|
||||
from source.translation import translate as _
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source.mkw.ExtractedGame import ExtractedGame
|
||||
|
@ -27,7 +28,7 @@ class PatchDirectory(PatchObject):
|
|||
"""
|
||||
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
|
||||
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.progress import Progress
|
||||
from source.wt.szs import SZSPath
|
||||
from source.translation import translate
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source.mkw.ExtractedGame import ExtractedGame
|
||||
|
@ -81,7 +82,8 @@ class PatchFile(PatchObject):
|
|||
"""
|
||||
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
|
||||
if not self.is_enabled(extracted_game): return
|
||||
|
@ -119,7 +121,7 @@ class PatchFile(PatchObject):
|
|||
if not game_subfile.relative_to(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 self.configuration["source"] == "game":
|
||||
|
|
|
@ -3,6 +3,7 @@ from typing import IO, TYPE_CHECKING
|
|||
from abc import ABC, abstractmethod
|
||||
|
||||
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
|
||||
from source.translation import translate as _
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source.mkw.Patch import Patch
|
||||
|
@ -10,7 +11,7 @@ if TYPE_CHECKING:
|
|||
|
||||
class InvalidBmgLayerMode(Exception):
|
||||
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):
|
||||
|
|
|
@ -4,6 +4,7 @@ from abc import abstractmethod, ABC
|
|||
from PIL import Image
|
||||
|
||||
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
|
||||
from source.translation import translate as _
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source.mkw.Patch import Patch
|
||||
|
@ -11,7 +12,7 @@ if TYPE_CHECKING:
|
|||
|
||||
class InvalidImageLayerType(Exception):
|
||||
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):
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from typing import IO, Type, TYPE_CHECKING
|
||||
|
||||
from source.translation import translate as _
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source.mkw.Patch import Patch
|
||||
|
||||
|
||||
class InvalidPatchOperation(Exception):
|
||||
def __init__(self, operation: str):
|
||||
super().__init__(f"Error : operation \"{operation}\" is not implemented")
|
||||
super().__init__(_("OPERATION", ' "', operation, '" ', "IS_NOT_IMPLEMENTED"))
|
||||
|
||||
|
||||
class AbstractPatchOperation(ABC):
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
from source.translation import translate as _
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source.mkw.Patch import Patch
|
||||
|
@ -8,14 +9,16 @@ if TYPE_CHECKING:
|
|||
|
||||
class PathOutsidePatch(Exception):
|
||||
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):
|
||||
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):
|
||||
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 source.mkw import Slot, Tag, ModConfig
|
||||
from source.translation import translate as _
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source import TemplateMultipleSafeEval
|
||||
|
@ -9,7 +10,7 @@ if TYPE_CHECKING:
|
|||
|
||||
class TrackForbiddenCustomAttribute(Exception):
|
||||
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):
|
||||
|
|
|
@ -2,14 +2,16 @@ from typing import TYPE_CHECKING
|
|||
|
||||
from source.mkw import Slot, Tag
|
||||
from source.mkw.Track.RealArenaTrack import RealArenaTrack
|
||||
from source.translation import translate as _
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source import TemplateMultipleSafeEval
|
||||
from source.mkw.ModConfig import ModConfig
|
||||
|
||||
|
||||
class ArenaForbiddenCustomAttribute(Exception):
|
||||
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):
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
from typing import TYPE_CHECKING
|
||||
|
||||
from source.mkw.Track.AbstractTrack import AbstractTrack
|
||||
from source.mkw.Track.RealArenaTrack import RealArenaTrack
|
||||
|
||||
ModConfig: any
|
||||
if TYPE_CHECKING:
|
||||
from source.mkw.ModConfig import ModConfig
|
||||
|
||||
|
||||
class CustomTrack(RealArenaTrack, AbstractTrack):
|
||||
|
|
|
@ -2,6 +2,7 @@ from typing import TYPE_CHECKING
|
|||
|
||||
if TYPE_CHECKING:
|
||||
from source import TemplateMultipleSafeEval
|
||||
from source.mkw import Tag
|
||||
from source.mkw.ModConfig import ModConfig
|
||||
|
||||
|
||||
|
@ -11,7 +12,7 @@ class RealArenaTrack:
|
|||
(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:
|
||||
"""
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import re
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from source.translation import translate as _
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source import TemplateSafeEval
|
||||
|
||||
|
@ -9,7 +12,7 @@ MACRO_START, MACRO_END = "##", "##"
|
|||
|
||||
class NotImplementedMacro(Exception):
|
||||
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:
|
||||
|
|
|
@ -4,6 +4,8 @@ from typing import TYPE_CHECKING, Iterable, Callable
|
|||
|
||||
from source.safe_eval.macros import replace_macro
|
||||
from source.safe_eval.safe_function import get_all_safe_functions
|
||||
from source.translation import translate as _
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
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
|
||||
stmt: ast.stmt = ast.parse(template).body[0]
|
||||
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
|
||||
for node in ast.walk(stmt):
|
||||
|
@ -69,20 +71,20 @@ def safe_eval(template: "TemplateSafeEval", env: "Env" = None, macros: dict[str,
|
|||
case ast.Attribute:
|
||||
# ban all magical function, disabling the __class__.__bases__[0] ... tricks
|
||||
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
|
||||
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
|
||||
case ast.Name:
|
||||
# ban modification to environment, but allow custom variable to be changed
|
||||
if isinstance(node.ctx, ast.Store):
|
||||
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:
|
||||
raise SafeEvalException(f'Can\'t set value of argument : "{node.id}"')
|
||||
raise SafeEvalException(_("CANNOT_SET_ARGUMENT", ' : "', node.id, '"'))
|
||||
|
||||
# when calling any function
|
||||
case ast.Call:
|
||||
|
@ -91,10 +93,10 @@ def safe_eval(template: "TemplateSafeEval", env: "Env" = None, macros: dict[str,
|
|||
if isinstance(callnode, ast.Attribute):
|
||||
for attrnode in ast.walk(callnode.value):
|
||||
if isinstance(attrnode, ast.Name):
|
||||
if attrnode.id in globals_ | locals_:
|
||||
raise SafeEvalException(f'Calling this function is not allowed : "{callnode.attr}"')
|
||||
if attrnode.id in args:
|
||||
raise SafeEvalException(f'Calling this function is not allowed : "{callnode.attr}"')
|
||||
if attrnode.id in globals_ | locals_ or attrnode.id in args:
|
||||
raise SafeEvalException(
|
||||
_("CALLING_FUNCTION_NOT_ALLOWED", ' : "', callnode.attr, '"')
|
||||
)
|
||||
|
||||
# when assigning a value with ":="
|
||||
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.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
|
||||
stmt.value = ast.Lambda(
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
from typing import Callable, Generator, TYPE_CHECKING
|
||||
|
||||
from source.translation import translate as _
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source import TemplateSafeEval, Env
|
||||
from source import TemplateSafeEval
|
||||
|
||||
|
||||
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
|
||||
"""
|
||||
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)
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -3,6 +3,8 @@ from pathlib import Path
|
|||
import os
|
||||
from typing import Callable
|
||||
|
||||
from source.translation import translate as _
|
||||
|
||||
|
||||
class WTError(Exception):
|
||||
def __init__(self, tools_path: Path | str, return_code: int):
|
||||
|
@ -14,14 +16,14 @@ class WTError(Exception):
|
|||
creationflags=subprocess.CREATE_NO_WINDOW,
|
||||
).stdout.decode()
|
||||
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):
|
||||
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/")
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from source.wt import *
|
||||
from source.wt import _run, _run_dict
|
||||
from source.translation import translate as _
|
||||
|
||||
|
||||
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
|
||||
"""
|
||||
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
|
||||
|
||||
|
||||
|
@ -191,7 +192,7 @@ class SZSSubPath:
|
|||
:param dest: destination 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)
|
||||
if dest.is_dir(): dest /= self.basename()
|
||||
|
|
Loading…
Reference in a new issue