From 59106d8ed9fce57cf2143d1c75b601213d77be66 Mon Sep 17 00:00:00 2001 From: Faraphel Date: Sun, 31 Jul 2022 23:05:54 +0200 Subject: [PATCH] module filtering subclasses have been simplified to made the code easier to read with less directory inside other useless directory and comment have been added --- source/gui/mod_settings.py | 1 - source/gui/preview/__init__.py | 20 +++--- source/mkw/ModConfig.py | 8 ++- .../ModSettings/{TypeSettings => }/Choices.py | 5 +- .../ModSettings/{TypeSettings => }/String.py | 8 +-- .../mkw/ModSettings/TypeSettings/__init__.py | 31 ---------- source/mkw/ModSettings/__init__.py | 48 +++++++++++--- source/mkw/Patch/PatchFile.py | 4 +- .../{Operation => }/BmgDecoder.py | 4 +- .../{Operation => }/BmgEncoder.py | 4 +- .../Layer => BmgTxtEditor}/CTFileLayer.py | 3 +- .../FormatOriginalTrackLayer.py | 3 +- .../Layer => BmgTxtEditor}/IDLayer.py | 2 +- .../Layer => BmgTxtEditor}/PatchLayer.py | 3 +- .../Layer => BmgTxtEditor}/RegexLayer.py | 2 +- .../PatchOperation/BmgTxtEditor/__init__.py | 62 +++++++++++++++++++ .../{Operation => }/ImageDecoder.py | 4 +- .../Layer => ImageEditor}/ColorLayer.py | 4 +- .../Layer => ImageEditor}/ImageLayer.py | 3 +- .../Layer => ImageEditor}/TextLayer.py | 2 +- .../Layer => ImageEditor}/__init__.py | 47 +++++++++++++- .../{Operation => }/ImageEncoder.py | 4 +- .../Operation/BmgTxtEditor/Layer/__init__.py | 17 ----- .../Operation/BmgTxtEditor/__init__.py | 45 -------------- .../Operation/ImageEditor/__init__.py | 47 -------------- .../PatchOperation/Operation/__init__.py | 22 ------- .../PatchOperation/{Operation => }/Rename.py | 4 +- .../PatchOperation/{Operation => }/Special.py | 4 +- .../{Operation => }/StrEditor.py | 4 +- source/mkw/Patch/PatchOperation/__init__.py | 31 ++++++++-- 30 files changed, 223 insertions(+), 223 deletions(-) rename source/mkw/ModSettings/{TypeSettings => }/Choices.py (89%) rename source/mkw/ModSettings/{TypeSettings => }/String.py (83%) delete mode 100644 source/mkw/ModSettings/TypeSettings/__init__.py rename source/mkw/Patch/PatchOperation/{Operation => }/BmgDecoder.py (85%) rename source/mkw/Patch/PatchOperation/{Operation => }/BmgEncoder.py (86%) rename source/mkw/Patch/PatchOperation/{Operation/BmgTxtEditor/Layer => BmgTxtEditor}/CTFileLayer.py (87%) rename source/mkw/Patch/PatchOperation/{Operation/BmgTxtEditor/Layer => BmgTxtEditor}/FormatOriginalTrackLayer.py (96%) rename source/mkw/Patch/PatchOperation/{Operation/BmgTxtEditor/Layer => BmgTxtEditor}/IDLayer.py (88%) rename source/mkw/Patch/PatchOperation/{Operation/BmgTxtEditor/Layer => BmgTxtEditor}/PatchLayer.py (82%) rename source/mkw/Patch/PatchOperation/{Operation/BmgTxtEditor/Layer => BmgTxtEditor}/RegexLayer.py (93%) create mode 100644 source/mkw/Patch/PatchOperation/BmgTxtEditor/__init__.py rename source/mkw/Patch/PatchOperation/{Operation => }/ImageDecoder.py (85%) rename source/mkw/Patch/PatchOperation/{Operation/ImageEditor/Layer => ImageEditor}/ColorLayer.py (88%) rename source/mkw/Patch/PatchOperation/{Operation/ImageEditor/Layer => ImageEditor}/ImageLayer.py (94%) rename source/mkw/Patch/PatchOperation/{Operation/ImageEditor/Layer => ImageEditor}/TextLayer.py (95%) rename source/mkw/Patch/PatchOperation/{Operation/ImageEditor/Layer => ImageEditor}/__init__.py (56%) rename source/mkw/Patch/PatchOperation/{Operation => }/ImageEncoder.py (89%) delete mode 100644 source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/Layer/__init__.py delete mode 100644 source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/__init__.py delete mode 100644 source/mkw/Patch/PatchOperation/Operation/ImageEditor/__init__.py delete mode 100644 source/mkw/Patch/PatchOperation/Operation/__init__.py rename source/mkw/Patch/PatchOperation/{Operation => }/Rename.py (73%) rename source/mkw/Patch/PatchOperation/{Operation => }/Special.py (80%) rename source/mkw/Patch/PatchOperation/{Operation => }/StrEditor.py (91%) diff --git a/source/gui/mod_settings.py b/source/gui/mod_settings.py index 7606aa4..1ba16b0 100644 --- a/source/gui/mod_settings.py +++ b/source/gui/mod_settings.py @@ -1,7 +1,6 @@ import tkinter from tkinter import ttk from source.translation import translate as _ -from source.gui.preview import track_formatting ModConfig: any diff --git a/source/gui/preview/__init__.py b/source/gui/preview/__init__.py index 33ad0d9..a7fea3e 100644 --- a/source/gui/preview/__init__.py +++ b/source/gui/preview/__init__.py @@ -23,16 +23,16 @@ class AbstractPreviewWindow(tkinter.Toplevel, ABC): super().__init__() ... - -def get_preview_window_class(name: str) -> Type[AbstractPreviewWindow]: - """ - Return the windows class object from its name - :param name: name of the window class - :return: the window class object - """ - for window_class in filter(lambda cls: cls.name == name, AbstractPreviewWindow.__subclasses__()): - return window_class - raise InvalidPreviewWindowName(name) + @classmethod + def get(cls, name: str) -> Type["AbstractPreviewWindow"]: + """ + Return the windows class object from its name + :param name: name of the window class + :return: the window class object + """ + for subclass in filter(lambda subclass: subclass.name == name, cls.__subclasses__()): + return subclass + raise InvalidPreviewWindowName(name) from source.gui.preview import track_formatting, track_selecting diff --git a/source/mkw/ModConfig.py b/source/mkw/ModConfig.py index 104d3f8..d1ac664 100644 --- a/source/mkw/ModConfig.py +++ b/source/mkw/ModConfig.py @@ -8,7 +8,7 @@ from source import threaded from source.mkw import Tag from source.mkw.Cup import Cup from source.mkw.MKWColor import bmg_color_text -from source.mkw.ModSettings import ModSettings +from source.mkw.ModSettings import AbstractModSettings from source.mkw.Track import Track import json @@ -65,8 +65,10 @@ class ModConfig: self.macros: dict = macros if macros is not None else {} self.messages: dict = messages if messages is not None else {} - self.global_settings: dict = ModSettings(global_settings) - self.specific_settings: dict = ModSettings(specific_settings if specific_settings is not None else {}) + self.global_settings: dict = AbstractModSettings.get(global_settings) + self.specific_settings: dict = AbstractModSettings.get( + specific_settings if specific_settings is not None else {} + ) self.name: str = name self.nickname: str = nickname if nickname is not None else name diff --git a/source/mkw/ModSettings/TypeSettings/Choices.py b/source/mkw/ModSettings/Choices.py similarity index 89% rename from source/mkw/ModSettings/TypeSettings/Choices.py rename to source/mkw/ModSettings/Choices.py index 652e5c2..3416989 100644 --- a/source/mkw/ModSettings/TypeSettings/Choices.py +++ b/source/mkw/ModSettings/Choices.py @@ -1,9 +1,10 @@ import tkinter from tkinter import ttk -from source.mkw.ModSettings.TypeSettings import AbstractTypeSettings + +from source.mkw.ModSettings import AbstractModSettings -class Choices(AbstractTypeSettings): +class Choices(AbstractModSettings): """ This setting type allow you to input a string text. You can optionally add a "preview" to allow the user to use a window to select the value. diff --git a/source/mkw/ModSettings/TypeSettings/String.py b/source/mkw/ModSettings/String.py similarity index 83% rename from source/mkw/ModSettings/TypeSettings/String.py rename to source/mkw/ModSettings/String.py index b0e165d..bf6a4e9 100644 --- a/source/mkw/ModSettings/TypeSettings/String.py +++ b/source/mkw/ModSettings/String.py @@ -1,11 +1,11 @@ import tkinter from tkinter import ttk -from source.mkw.ModSettings.TypeSettings import AbstractTypeSettings -from source.gui.preview import get_preview_window_class +from source.mkw.ModSettings import AbstractModSettings +from source.gui.preview import AbstractPreviewWindow -class String(AbstractTypeSettings): +class String(AbstractModSettings): """ This setting type allow you to input a string text. You can optionally add a "preview" to allow the user to use a window to select the value. @@ -30,7 +30,7 @@ class String(AbstractTypeSettings): if self.preview is not None: button = ttk.Button( master, text="...", width=3, - command=lambda: get_preview_window_class( + command=lambda: AbstractPreviewWindow.get( self.preview )(master.master.master.master.mod_config, value_variable) ) diff --git a/source/mkw/ModSettings/TypeSettings/__init__.py b/source/mkw/ModSettings/TypeSettings/__init__.py deleted file mode 100644 index 37fc06c..0000000 --- a/source/mkw/ModSettings/TypeSettings/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -import tkinter -from tkinter import ttk -from abc import ABC, abstractmethod - - -class AbstractTypeSettings(ABC): - type: str # type name of the settings - value: str # value for the settings - enabled: bool # is the settings enabled - - @abstractmethod - def __init__(self, value: str = None, preview: str = None, enabled: bool = False): - ... - - @abstractmethod - def tkinter_show(self, master: ttk.LabelFrame, checkbox) -> None: - """ - Show the option inside a tkinter widget - :master: master widget - :checkbox: checkbox inside the labelframe allowing to enable or disable the setting - """ - master.grid_rowconfigure(1, weight=1) - master.grid_columnconfigure(1, weight=1) - - enabled_variable = tkinter.BooleanVar(master, value=self.enabled) - enabled_variable.trace_add("write", lambda *_: setattr(self, "enabled", enabled_variable.get())) - checkbox.configure(variable=enabled_variable) - ... - - -from source.mkw.ModSettings.TypeSettings import String, Choices diff --git a/source/mkw/ModSettings/__init__.py b/source/mkw/ModSettings/__init__.py index 269089c..fe7263a 100644 --- a/source/mkw/ModSettings/__init__.py +++ b/source/mkw/ModSettings/__init__.py @@ -1,4 +1,6 @@ -from source.mkw.ModSettings.TypeSettings import AbstractTypeSettings +import tkinter +from tkinter import ttk +from abc import ABC, abstractmethod class InvalidSettingsType(Exception): @@ -6,21 +8,51 @@ class InvalidSettingsType(Exception): super().__init__(f"Error : Type of mod settings '{settings_type}' not found.") -class ModSettings: - def __new__(cls, settings_dict: dict) -> dict[str, AbstractTypeSettings]: +class AbstractModSettings(ABC): + """ + Base class for every different type of ModSettings + """ + + type: str # type name of the settings + value: str # value for the settings + enabled: bool # is the settings enabled + + @abstractmethod + def __init__(self, value: str = None, preview: str = None, enabled: bool = False): + ... + + @abstractmethod + def tkinter_show(self, master: ttk.LabelFrame, checkbox) -> None: + """ + Show the option inside a tkinter widget + :master: master widget + :checkbox: checkbox inside the labelframe allowing to enable or disable the setting + """ + master.grid_rowconfigure(1, weight=1) + master.grid_columnconfigure(1, weight=1) + + enabled_variable = tkinter.BooleanVar(master, value=self.enabled) + enabled_variable.trace_add("write", lambda *_: setattr(self, "enabled", enabled_variable.get())) + checkbox.configure(variable=enabled_variable) + ... + + @classmethod + def get(cls, settings_dict: dict) -> dict[str, "AbstractModSettings"]: """ Load all the settings in mod_settings_dict - :param settings_dict: dictionnary containing all the settings defined for the mod + :param settings_dict: dictionary containing all the settings defined for the mod """ - settings: dict[str, AbstractTypeSettings] = {} + settings: dict[str, AbstractModSettings] = {} for settings_name, settings_data in settings_dict.items(): - for subclass in filter( - lambda subclass: subclass.type == settings_data["type"], AbstractTypeSettings.__subclasses__() - ): + for subclass in filter(lambda subclass: subclass.type == settings_data["type"], cls.__subclasses__()): settings_data.pop("type") settings[settings_name] = subclass(**settings_data) break else: raise InvalidSettingsType(settings_name) return settings + + +# these import load the different ModSettings, and so get_mod_settings will be able to fetch them with __subclasses__ +from source.mkw.ModSettings import Choices, String diff --git a/source/mkw/Patch/PatchFile.py b/source/mkw/Patch/PatchFile.py index cd967d6..6723d19 100644 --- a/source/mkw/Patch/PatchFile.py +++ b/source/mkw/Patch/PatchFile.py @@ -2,7 +2,7 @@ from io import BytesIO from typing import Generator, IO from source.mkw.Patch import * -from source.mkw.Patch.PatchOperation import PatchOperation +from source.mkw.Patch.PatchOperation import AbstractPatchOperation from source.mkw.Patch.PatchObject import PatchObject from source.wt.szs import SZSPath @@ -55,7 +55,7 @@ class PatchFile(PatchObject): for operation_name, operation in self.configuration.get("operation", {}).items(): # process every operation and get the new patch_path (if the name is changed) # and the new content of the patch - patch_name, patch_content = PatchOperation(operation_name)(**operation).patch( + patch_name, patch_content = AbstractPatchOperation.get(operation_name)(**operation).patch( self.patch, patch_name, patch_content ) diff --git a/source/mkw/Patch/PatchOperation/Operation/BmgDecoder.py b/source/mkw/Patch/PatchOperation/BmgDecoder.py similarity index 85% rename from source/mkw/Patch/PatchOperation/Operation/BmgDecoder.py rename to source/mkw/Patch/PatchOperation/BmgDecoder.py index 0de772d..a48993d 100644 --- a/source/mkw/Patch/PatchOperation/Operation/BmgDecoder.py +++ b/source/mkw/Patch/PatchOperation/BmgDecoder.py @@ -1,14 +1,14 @@ from io import BytesIO from typing import IO -from source.mkw.Patch.PatchOperation.Operation import * +from source.mkw.Patch.PatchOperation import AbstractPatchOperation from source.wt import bmg Patch: any -class BmgDecoder(AbstractOperation): +class BmgDecoder(AbstractPatchOperation): """ decode a bmg file to a txt file """ diff --git a/source/mkw/Patch/PatchOperation/Operation/BmgEncoder.py b/source/mkw/Patch/PatchOperation/BmgEncoder.py similarity index 86% rename from source/mkw/Patch/PatchOperation/Operation/BmgEncoder.py rename to source/mkw/Patch/PatchOperation/BmgEncoder.py index 4c85392..817523f 100644 --- a/source/mkw/Patch/PatchOperation/Operation/BmgEncoder.py +++ b/source/mkw/Patch/PatchOperation/BmgEncoder.py @@ -1,13 +1,13 @@ from io import BytesIO from typing import IO -from source.mkw.Patch.PatchOperation.Operation import * +from source.mkw.Patch.PatchOperation import AbstractPatchOperation from source.wt import bmg Patch: any -class BmgEncoder(AbstractOperation): +class BmgEncoder(AbstractPatchOperation): """ encode a bmg file to a txt file """ diff --git a/source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/Layer/CTFileLayer.py b/source/mkw/Patch/PatchOperation/BmgTxtEditor/CTFileLayer.py similarity index 87% rename from source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/Layer/CTFileLayer.py rename to source/mkw/Patch/PatchOperation/BmgTxtEditor/CTFileLayer.py index 004cbb4..fc85bad 100644 --- a/source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/Layer/CTFileLayer.py +++ b/source/mkw/Patch/PatchOperation/BmgTxtEditor/CTFileLayer.py @@ -1,6 +1,7 @@ -from source.mkw.Patch.PatchOperation.Operation.BmgTxtEditor.Layer import * +from source.mkw.Patch.PatchOperation.BmgTxtEditor import AbstractLayer from source.wt import ctc + Patch: any diff --git a/source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/Layer/FormatOriginalTrackLayer.py b/source/mkw/Patch/PatchOperation/BmgTxtEditor/FormatOriginalTrackLayer.py similarity index 96% rename from source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/Layer/FormatOriginalTrackLayer.py rename to source/mkw/Patch/PatchOperation/BmgTxtEditor/FormatOriginalTrackLayer.py index 2406886..4b6b528 100644 --- a/source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/Layer/FormatOriginalTrackLayer.py +++ b/source/mkw/Patch/PatchOperation/BmgTxtEditor/FormatOriginalTrackLayer.py @@ -1,9 +1,10 @@ import re -from source.mkw.Patch.PatchOperation.Operation.BmgTxtEditor.Layer import * +from source.mkw.Patch.PatchOperation.BmgTxtEditor import AbstractLayer from source.mkw.Track import Track from source.wt import bmg + Patch: any diff --git a/source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/Layer/IDLayer.py b/source/mkw/Patch/PatchOperation/BmgTxtEditor/IDLayer.py similarity index 88% rename from source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/Layer/IDLayer.py rename to source/mkw/Patch/PatchOperation/BmgTxtEditor/IDLayer.py index 41cdd36..9981338 100644 --- a/source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/Layer/IDLayer.py +++ b/source/mkw/Patch/PatchOperation/BmgTxtEditor/IDLayer.py @@ -1,4 +1,4 @@ -from source.mkw.Patch.PatchOperation.Operation.BmgTxtEditor.Layer import * +from source.mkw.Patch.PatchOperation.BmgTxtEditor import AbstractLayer Patch: any diff --git a/source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/Layer/PatchLayer.py b/source/mkw/Patch/PatchOperation/BmgTxtEditor/PatchLayer.py similarity index 82% rename from source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/Layer/PatchLayer.py rename to source/mkw/Patch/PatchOperation/BmgTxtEditor/PatchLayer.py index 291a101..44d765e 100644 --- a/source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/Layer/PatchLayer.py +++ b/source/mkw/Patch/PatchOperation/BmgTxtEditor/PatchLayer.py @@ -1,6 +1,7 @@ -from source.mkw.Patch.PatchOperation.Operation.BmgTxtEditor.Layer import * +from source.mkw.Patch.PatchOperation.BmgTxtEditor import AbstractLayer from source.wt import bmg + Patch: any diff --git a/source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/Layer/RegexLayer.py b/source/mkw/Patch/PatchOperation/BmgTxtEditor/RegexLayer.py similarity index 93% rename from source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/Layer/RegexLayer.py rename to source/mkw/Patch/PatchOperation/BmgTxtEditor/RegexLayer.py index d57f897..33ec038 100644 --- a/source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/Layer/RegexLayer.py +++ b/source/mkw/Patch/PatchOperation/BmgTxtEditor/RegexLayer.py @@ -1,6 +1,6 @@ import re -from source.mkw.Patch.PatchOperation.Operation.BmgTxtEditor.Layer import * +from source.mkw.Patch.PatchOperation.BmgTxtEditor import AbstractLayer Patch: any diff --git a/source/mkw/Patch/PatchOperation/BmgTxtEditor/__init__.py b/source/mkw/Patch/PatchOperation/BmgTxtEditor/__init__.py new file mode 100644 index 0000000..646d535 --- /dev/null +++ b/source/mkw/Patch/PatchOperation/BmgTxtEditor/__init__.py @@ -0,0 +1,62 @@ +from io import BytesIO +from typing import IO +from abc import ABC, abstractmethod + +from source.mkw.Patch import * +from source.mkw.Patch.PatchOperation import AbstractPatchOperation + + +Layer: any +Patch: any + + +class AbstractLayer(ABC): + """ + Represent a Layer for a bmgtxt patch + """ + + @abstractmethod + def patch_bmg(self, patch: "Patch", decoded_content: str) -> str: + """ + Patch a bmg with the actual layer. Return the new bmg content. + """ + + @classmethod + def get(cls, layer: dict) -> "Layer": + """ + return the correct type of layer corresponding to the layer mode + :param layer: the layer to load + """ + for subclass in filter(lambda subclass: subclass.mode == layer["mode"], cls.__subclasses__()): + layer.pop("mode") + return subclass(**layer) + raise InvalidBmgLayerMode(layer["mode"]) + + +class BmgTxtEditor(AbstractPatchOperation): + """ + edit a decoded bmg + """ + + type = "bmgtxt-edit" + + def __init__(self, layers: list[dict]): + """ + :param layers: layers + """ + self.layers = layers + + def patch(self, patch: "Patch", file_name: str, file_content: IO) -> (str, IO): + decoded_content: str = file_content.read().decode("utf-8") + + for layer in self.layers: + decoded_content = AbstractLayer.get(layer).patch_bmg(patch, decoded_content) + + patch_content: IO = BytesIO(decoded_content.encode("utf-8")) + return file_name, patch_content + + +# Load the subclasses so that get_layer can filter them. +from source.mkw.Patch.PatchOperation.BmgTxtEditor import ( + CTFileLayer, FormatOriginalTrackLayer, IDLayer, PatchLayer, RegexLayer +) \ No newline at end of file diff --git a/source/mkw/Patch/PatchOperation/Operation/ImageDecoder.py b/source/mkw/Patch/PatchOperation/ImageDecoder.py similarity index 85% rename from source/mkw/Patch/PatchOperation/Operation/ImageDecoder.py rename to source/mkw/Patch/PatchOperation/ImageDecoder.py index 986c6e9..bf08018 100644 --- a/source/mkw/Patch/PatchOperation/Operation/ImageDecoder.py +++ b/source/mkw/Patch/PatchOperation/ImageDecoder.py @@ -1,14 +1,14 @@ from io import BytesIO from typing import IO -from source.mkw.Patch.PatchOperation.Operation import * +from source.mkw.Patch.PatchOperation import AbstractPatchOperation from source.wt import img Patch: any -class ImageDecoder(AbstractOperation): +class ImageDecoder(AbstractPatchOperation): """ decode a game image to a image file """ diff --git a/source/mkw/Patch/PatchOperation/Operation/ImageEditor/Layer/ColorLayer.py b/source/mkw/Patch/PatchOperation/ImageEditor/ColorLayer.py similarity index 88% rename from source/mkw/Patch/PatchOperation/Operation/ImageEditor/Layer/ColorLayer.py rename to source/mkw/Patch/PatchOperation/ImageEditor/ColorLayer.py index 37d28f0..cad70b1 100644 --- a/source/mkw/Patch/PatchOperation/Operation/ImageEditor/Layer/ColorLayer.py +++ b/source/mkw/Patch/PatchOperation/ImageEditor/ColorLayer.py @@ -1,6 +1,6 @@ from PIL import ImageDraw, Image -from source.mkw.Patch.PatchOperation.Operation.ImageEditor.Layer import * +from source.mkw.Patch.PatchOperation.ImageEditor import AbstractLayer Patch: any @@ -24,4 +24,4 @@ class ColorLayer(AbstractLayer): draw = ImageDraw.Draw(image) draw.rectangle(self.get_bbox(image), fill=self.color) - return image \ No newline at end of file + return image diff --git a/source/mkw/Patch/PatchOperation/Operation/ImageEditor/Layer/ImageLayer.py b/source/mkw/Patch/PatchOperation/ImageEditor/ImageLayer.py similarity index 94% rename from source/mkw/Patch/PatchOperation/Operation/ImageEditor/Layer/ImageLayer.py rename to source/mkw/Patch/PatchOperation/ImageEditor/ImageLayer.py index 8902c48..dcdd61b 100644 --- a/source/mkw/Patch/PatchOperation/Operation/ImageEditor/Layer/ImageLayer.py +++ b/source/mkw/Patch/PatchOperation/ImageEditor/ImageLayer.py @@ -1,7 +1,7 @@ from PIL import Image from source.mkw.Patch import * -from source.mkw.Patch.PatchOperation.Operation.ImageEditor.Layer import * +from source.mkw.Patch.PatchOperation.ImageEditor import AbstractLayer class ImageLayer(AbstractLayer): @@ -36,3 +36,4 @@ class ImageLayer(AbstractLayer): ) return image + diff --git a/source/mkw/Patch/PatchOperation/Operation/ImageEditor/Layer/TextLayer.py b/source/mkw/Patch/PatchOperation/ImageEditor/TextLayer.py similarity index 95% rename from source/mkw/Patch/PatchOperation/Operation/ImageEditor/Layer/TextLayer.py rename to source/mkw/Patch/PatchOperation/ImageEditor/TextLayer.py index ac8f2e8..d9794f7 100644 --- a/source/mkw/Patch/PatchOperation/Operation/ImageEditor/Layer/TextLayer.py +++ b/source/mkw/Patch/PatchOperation/ImageEditor/TextLayer.py @@ -1,7 +1,7 @@ from PIL import ImageFont, ImageDraw, Image from source.mkw.Patch import * -from source.mkw.Patch.PatchOperation.Operation.ImageEditor.Layer import * +from source.mkw.Patch.PatchOperation.ImageEditor import AbstractLayer class TextLayer(AbstractLayer): diff --git a/source/mkw/Patch/PatchOperation/Operation/ImageEditor/Layer/__init__.py b/source/mkw/Patch/PatchOperation/ImageEditor/__init__.py similarity index 56% rename from source/mkw/Patch/PatchOperation/Operation/ImageEditor/Layer/__init__.py rename to source/mkw/Patch/PatchOperation/ImageEditor/__init__.py index cb9fb7c..a007ada 100644 --- a/source/mkw/Patch/PatchOperation/Operation/ImageEditor/Layer/__init__.py +++ b/source/mkw/Patch/PatchOperation/ImageEditor/__init__.py @@ -1,8 +1,13 @@ +from io import BytesIO +from typing import IO from abc import abstractmethod, ABC - from PIL import Image +from source.mkw.Patch import * +from source.mkw.Patch.PatchOperation import AbstractPatchOperation + +Layer: any Patch: any @@ -58,6 +63,42 @@ class AbstractLayer(ABC): Patch an image with the actual layer. Return the new image. """ + @classmethod + def get(cls, layer: dict) -> "Layer": + """ + return the correct type of layer corresponding to the layer mode + :param layer: the layer to load + """ + for subclass in filter(lambda subclass: subclass.type == layer["type"], cls.__subclasses__()): + layer.pop("type") + return subclass(**layer) + raise InvalidImageLayerType(layer["type"]) -from source.mkw.Patch.PatchOperation.Operation.ImageEditor.Layer import ColorLayer, ImageLayer, TextLayer -__all__ = ["AbstractLayer", "ColorLayer", "ImageLayer", "TextLayer"] + +class ImageGenerator(AbstractPatchOperation): + """ + generate a new image based on a file and apply a generator on it + """ + + type = "img-edit" + + def __init__(self, layers: list[dict]): + self.layers: list["Layer"] = [AbstractLayer.get(layer) for layer in layers] + + def patch(self, patch: "Patch", file_name: str, file_content: IO) -> (str, IO): + image = Image.open(file_content).convert("RGBA") + + for layer in self.layers: + image = layer.patch_image(patch, image) + + patch_content = BytesIO() + image.save(patch_content, format="PNG") + patch_content.seek(0) + + return file_name, patch_content + + +# Load the class so that __subclasses__ can find them +from source.mkw.Patch.PatchOperation.ImageEditor import ( + ColorLayer, ImageLayer, TextLayer +) diff --git a/source/mkw/Patch/PatchOperation/Operation/ImageEncoder.py b/source/mkw/Patch/PatchOperation/ImageEncoder.py similarity index 89% rename from source/mkw/Patch/PatchOperation/Operation/ImageEncoder.py rename to source/mkw/Patch/PatchOperation/ImageEncoder.py index 5012ff0..7cb1db6 100644 --- a/source/mkw/Patch/PatchOperation/Operation/ImageEncoder.py +++ b/source/mkw/Patch/PatchOperation/ImageEncoder.py @@ -1,14 +1,14 @@ from io import BytesIO from typing import IO -from source.mkw.Patch.PatchOperation.Operation import * +from source.mkw.Patch.PatchOperation import AbstractPatchOperation from source.wt import img Patch: any -class ImageEncoder(AbstractOperation): +class ImageEncoder(AbstractPatchOperation): """ encode an image to a game image file """ diff --git a/source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/Layer/__init__.py b/source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/Layer/__init__.py deleted file mode 100644 index 6557f67..0000000 --- a/source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/Layer/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -from abc import ABC, abstractmethod - - -Patch: any - - -class AbstractLayer(ABC): - @abstractmethod - def patch_bmg(self, patch: "Patch", decoded_content: str) -> str: - """ - Patch a bmg with the actual layer. Return the new bmg content. - """ - - -from source.mkw.Patch.PatchOperation.Operation.BmgTxtEditor.Layer import IDLayer, RegexLayer, CTFileLayer, PatchLayer, \ - FormatOriginalTrackLayer -__all__ = ["AbstractLayer", "IDLayer", "RegexLayer", "CTFileLayer", "PatchLayer", "FormatOriginalTrackLayer"] diff --git a/source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/__init__.py b/source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/__init__.py deleted file mode 100644 index 6d50bd9..0000000 --- a/source/mkw/Patch/PatchOperation/Operation/BmgTxtEditor/__init__.py +++ /dev/null @@ -1,45 +0,0 @@ -from io import BytesIO -from typing import IO - -from source.mkw.Patch import * -from source.mkw.Patch.PatchOperation.Operation import * -from source.mkw.Patch.PatchOperation.Operation.BmgTxtEditor.Layer import * - - -class BmgTxtEditor(AbstractOperation): - """ - edit a decoded bmg - """ - - type = "bmgtxt-edit" - - def __init__(self, layers: list[dict]): - """ - :param layers: layers - """ - self.layers = layers - - def patch(self, patch: "Patch", file_name: str, file_content: IO) -> (str, IO): - decoded_content: str = file_content.read().decode("utf-8") - - for layer in self.layers: - decoded_content = self.Layer(layer).patch_bmg(patch, decoded_content) - - patch_content: IO = BytesIO(decoded_content.encode("utf-8")) - return file_name, patch_content - - class Layer: - """ - represent a layer for a bmg-edit - """ - - def __new__(cls, layer: dict) -> "Layer": - """ - return the correct type of layer corresponding to the layer mode - :param layer: the layer to load - """ - for subclass in filter(lambda subclass: subclass.mode == layer["mode"], - AbstractLayer.__subclasses__()): - layer.pop("mode") - return subclass(**layer) - raise InvalidBmgLayerMode(layer["mode"]) diff --git a/source/mkw/Patch/PatchOperation/Operation/ImageEditor/__init__.py b/source/mkw/Patch/PatchOperation/Operation/ImageEditor/__init__.py deleted file mode 100644 index 7147507..0000000 --- a/source/mkw/Patch/PatchOperation/Operation/ImageEditor/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -from io import BytesIO -from typing import IO - -from PIL import Image - -from source.mkw.Patch import * -from source.mkw.Patch.PatchOperation.Operation import * -from source.mkw.Patch.PatchOperation.Operation.ImageEditor.Layer import * - - -class ImageGenerator(AbstractOperation): - """ - generate a new image based on a file and apply a generator on it - """ - - type = "img-edit" - - def __init__(self, layers: list[dict]): - self.layers: list["Layer"] = [self.Layer(layer) for layer in layers] - - def patch(self, patch: "Patch", file_name: str, file_content: IO) -> (str, IO): - image = Image.open(file_content).convert("RGBA") - - for layer in self.layers: - image = layer.patch_image(patch, image) - - patch_content = BytesIO() - image.save(patch_content, format="PNG") - patch_content.seek(0) - - return file_name, patch_content - - class Layer: - """ - represent a layer for an image generator - """ - - def __new__(cls, layer: dict) -> "Layer": - """ - return the correct type of layer corresponding to the layer mode - :param layer: the layer to load - """ - for subclass in filter(lambda subclass: subclass.type == layer["type"], - AbstractLayer.__subclasses__()): - layer.pop("type") - return subclass(**layer) - raise InvalidImageLayerType(layer["type"]) diff --git a/source/mkw/Patch/PatchOperation/Operation/__init__.py b/source/mkw/Patch/PatchOperation/Operation/__init__.py deleted file mode 100644 index c2cdf44..0000000 --- a/source/mkw/Patch/PatchOperation/Operation/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -from abc import ABC, abstractmethod -from typing import IO - -Patch: any -Layer: any - - -class AbstractOperation(ABC): - - mode: str # name of the operation - - @abstractmethod - def patch(self, patch: "Patch", file_name: str, file_content: IO) -> (str, IO): - """ - patch a file and return the new file_path (if changed) and the new content of the file - """ - - -from source.mkw.Patch.PatchOperation.Operation import ImageDecoder, ImageEncoder, Rename, Special, StrEditor, \ - BmgTxtEditor, ImageEditor, BmgEncoder, BmgDecoder -__all__ = ["AbstractOperation", "ImageDecoder", "ImageEncoder", "Rename", - "Special", "StrEditor", "BmgTxtEditor", "ImageEditor", "BmgEncoder", "BmgDecoder"] diff --git a/source/mkw/Patch/PatchOperation/Operation/Rename.py b/source/mkw/Patch/PatchOperation/Rename.py similarity index 73% rename from source/mkw/Patch/PatchOperation/Operation/Rename.py rename to source/mkw/Patch/PatchOperation/Rename.py index d9751ad..3961fd7 100644 --- a/source/mkw/Patch/PatchOperation/Operation/Rename.py +++ b/source/mkw/Patch/PatchOperation/Rename.py @@ -1,12 +1,12 @@ from typing import IO -from source.mkw.Patch.PatchOperation.Operation import * +from source.mkw.Patch.PatchOperation import AbstractPatchOperation Patch: any -class Rename(AbstractOperation): +class Rename(AbstractPatchOperation): """ Rename the output file """ diff --git a/source/mkw/Patch/PatchOperation/Operation/Special.py b/source/mkw/Patch/PatchOperation/Special.py similarity index 80% rename from source/mkw/Patch/PatchOperation/Operation/Special.py rename to source/mkw/Patch/PatchOperation/Special.py index c9eeae5..ed19359 100644 --- a/source/mkw/Patch/PatchOperation/Operation/Special.py +++ b/source/mkw/Patch/PatchOperation/Special.py @@ -1,12 +1,12 @@ from typing import IO -from source.mkw.Patch.PatchOperation.Operation import * +from source.mkw.Patch.PatchOperation import AbstractPatchOperation Patch: any -class Special(AbstractOperation): +class Special(AbstractPatchOperation): """ use a file defined as special in the patch to replace the current file content """ diff --git a/source/mkw/Patch/PatchOperation/Operation/StrEditor.py b/source/mkw/Patch/PatchOperation/StrEditor.py similarity index 91% rename from source/mkw/Patch/PatchOperation/Operation/StrEditor.py rename to source/mkw/Patch/PatchOperation/StrEditor.py index c7edfb8..a86e414 100644 --- a/source/mkw/Patch/PatchOperation/Operation/StrEditor.py +++ b/source/mkw/Patch/PatchOperation/StrEditor.py @@ -1,12 +1,12 @@ from io import BytesIO from typing import IO -from source.mkw.Patch.PatchOperation.Operation import * +from source.mkw.Patch.PatchOperation import AbstractPatchOperation from source.mkw.Patch import * from source.wt import wstrt -class StrEditor(AbstractOperation): +class StrEditor(AbstractPatchOperation): """ patch the main.dol file """ diff --git a/source/mkw/Patch/PatchOperation/__init__.py b/source/mkw/Patch/PatchOperation/__init__.py index 4d31827..dd6859c 100644 --- a/source/mkw/Patch/PatchOperation/__init__.py +++ b/source/mkw/Patch/PatchOperation/__init__.py @@ -1,18 +1,39 @@ from source.mkw.Patch import * -from source.mkw.Patch.PatchOperation.Operation import * +from abc import ABC, abstractmethod +from typing import IO, Type + +Patch: any +Layer: any -class PatchOperation: +class AbstractPatchOperation(ABC): """ - Represent an operation that can be applied onto a patch to modify it before installing + An AbstractPatchOperation representing any PatchOperation that can be used on one of the game files """ - def __new__(cls, name) -> "Operation": + mode: str # name of the operation + + @abstractmethod + def patch(self, patch: "Patch", file_name: str, file_content: IO) -> (str, IO): + """ + patch a file and return the new file_path (if changed) and the new content of the file + """ + + @classmethod + def get(cls, name: str) -> Type["AbstractPatchOperation"]: """ Return an operation from its name + :name: name of the operation :return: an Operation from its name """ - for subclass in filter(lambda subclass: subclass.type == name, AbstractOperation.__subclasses__()): + for subclass in filter(lambda subclass: subclass.type == name, cls.__subclasses__()): return subclass raise InvalidPatchOperation(name) + + +# load all the subclass of AbstractPatchOperation to that __subclasses__ can filter them +from source.mkw.Patch.PatchOperation import ( + ImageDecoder, ImageEncoder, Rename, Special, StrEditor, + BmgTxtEditor, ImageEditor, BmgEncoder, BmgDecoder +)