mirror of
https://github.com/Faraphel/Atlas-Install.git
synced 2025-07-02 02:38:30 +02:00
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
This commit is contained in:
parent
547e9108da
commit
59106d8ed9
30 changed files with 223 additions and 223 deletions
|
@ -1,7 +1,6 @@
|
||||||
import tkinter
|
import tkinter
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
from source.translation import translate as _
|
from source.translation import translate as _
|
||||||
from source.gui.preview import track_formatting
|
|
||||||
|
|
||||||
|
|
||||||
ModConfig: any
|
ModConfig: any
|
||||||
|
|
|
@ -23,16 +23,16 @@ class AbstractPreviewWindow(tkinter.Toplevel, ABC):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
def get_preview_window_class(name: str) -> Type[AbstractPreviewWindow]:
|
def get(cls, name: str) -> Type["AbstractPreviewWindow"]:
|
||||||
"""
|
"""
|
||||||
Return the windows class object from its name
|
Return the windows class object from its name
|
||||||
:param name: name of the window class
|
:param name: name of the window class
|
||||||
:return: the window class object
|
:return: the window class object
|
||||||
"""
|
"""
|
||||||
for window_class in filter(lambda cls: cls.name == name, AbstractPreviewWindow.__subclasses__()):
|
for subclass in filter(lambda subclass: subclass.name == name, cls.__subclasses__()):
|
||||||
return window_class
|
return subclass
|
||||||
raise InvalidPreviewWindowName(name)
|
raise InvalidPreviewWindowName(name)
|
||||||
|
|
||||||
|
|
||||||
from source.gui.preview import track_formatting, track_selecting
|
from source.gui.preview import track_formatting, track_selecting
|
||||||
|
|
|
@ -8,7 +8,7 @@ from source import threaded
|
||||||
from source.mkw import Tag
|
from source.mkw import Tag
|
||||||
from source.mkw.Cup import Cup
|
from source.mkw.Cup import Cup
|
||||||
from source.mkw.MKWColor import bmg_color_text
|
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
|
from source.mkw.Track import Track
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
@ -65,8 +65,10 @@ class ModConfig:
|
||||||
self.macros: dict = macros if macros is not None else {}
|
self.macros: dict = macros if macros is not None else {}
|
||||||
self.messages: dict = messages if messages is not None else {}
|
self.messages: dict = messages if messages is not None else {}
|
||||||
|
|
||||||
self.global_settings: dict = ModSettings(global_settings)
|
self.global_settings: dict = AbstractModSettings.get(global_settings)
|
||||||
self.specific_settings: dict = ModSettings(specific_settings if specific_settings is not None else {})
|
self.specific_settings: dict = AbstractModSettings.get(
|
||||||
|
specific_settings if specific_settings is not None else {}
|
||||||
|
)
|
||||||
|
|
||||||
self.name: str = name
|
self.name: str = name
|
||||||
self.nickname: str = nickname if nickname is not None else name
|
self.nickname: str = nickname if nickname is not None else name
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import tkinter
|
import tkinter
|
||||||
from tkinter import ttk
|
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.
|
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.
|
You can optionally add a "preview" to allow the user to use a window to select the value.
|
|
@ -1,11 +1,11 @@
|
||||||
import tkinter
|
import tkinter
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
|
|
||||||
from source.mkw.ModSettings.TypeSettings import AbstractTypeSettings
|
from source.mkw.ModSettings import AbstractModSettings
|
||||||
from source.gui.preview import get_preview_window_class
|
from source.gui.preview import AbstractPreviewWindow
|
||||||
|
|
||||||
|
|
||||||
class String(AbstractTypeSettings):
|
class String(AbstractModSettings):
|
||||||
"""
|
"""
|
||||||
This setting type allow you to input a string text.
|
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.
|
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:
|
if self.preview is not None:
|
||||||
button = ttk.Button(
|
button = ttk.Button(
|
||||||
master, text="...", width=3,
|
master, text="...", width=3,
|
||||||
command=lambda: get_preview_window_class(
|
command=lambda: AbstractPreviewWindow.get(
|
||||||
self.preview
|
self.preview
|
||||||
)(master.master.master.master.mod_config, value_variable)
|
)(master.master.master.master.mod_config, value_variable)
|
||||||
)
|
)
|
|
@ -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
|
|
|
@ -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):
|
class InvalidSettingsType(Exception):
|
||||||
|
@ -6,21 +8,51 @@ class InvalidSettingsType(Exception):
|
||||||
super().__init__(f"Error : Type of mod settings '{settings_type}' not found.")
|
super().__init__(f"Error : Type of mod settings '{settings_type}' not found.")
|
||||||
|
|
||||||
|
|
||||||
class ModSettings:
|
class AbstractModSettings(ABC):
|
||||||
def __new__(cls, settings_dict: dict) -> dict[str, AbstractTypeSettings]:
|
"""
|
||||||
|
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
|
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 settings_name, settings_data in settings_dict.items():
|
||||||
for subclass in filter(
|
for subclass in filter(lambda subclass: subclass.type == settings_data["type"], cls.__subclasses__()):
|
||||||
lambda subclass: subclass.type == settings_data["type"], AbstractTypeSettings.__subclasses__()
|
|
||||||
):
|
|
||||||
settings_data.pop("type")
|
settings_data.pop("type")
|
||||||
settings[settings_name] = subclass(**settings_data)
|
settings[settings_name] = subclass(**settings_data)
|
||||||
break
|
break
|
||||||
else: raise InvalidSettingsType(settings_name)
|
else: raise InvalidSettingsType(settings_name)
|
||||||
|
|
||||||
return settings
|
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
|
||||||
|
|
|
@ -2,7 +2,7 @@ from io import BytesIO
|
||||||
from typing import Generator, IO
|
from typing import Generator, IO
|
||||||
|
|
||||||
from source.mkw.Patch import *
|
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.mkw.Patch.PatchObject import PatchObject
|
||||||
from source.wt.szs import SZSPath
|
from source.wt.szs import SZSPath
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ class PatchFile(PatchObject):
|
||||||
for operation_name, operation in self.configuration.get("operation", {}).items():
|
for operation_name, operation in self.configuration.get("operation", {}).items():
|
||||||
# process every operation and get the new patch_path (if the name is changed)
|
# process every operation and get the new patch_path (if the name is changed)
|
||||||
# and the new content of the patch
|
# 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
|
self.patch, patch_name, patch_content
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from typing import IO
|
from typing import IO
|
||||||
|
|
||||||
from source.mkw.Patch.PatchOperation.Operation import *
|
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
|
||||||
from source.wt import bmg
|
from source.wt import bmg
|
||||||
|
|
||||||
|
|
||||||
Patch: any
|
Patch: any
|
||||||
|
|
||||||
|
|
||||||
class BmgDecoder(AbstractOperation):
|
class BmgDecoder(AbstractPatchOperation):
|
||||||
"""
|
"""
|
||||||
decode a bmg file to a txt file
|
decode a bmg file to a txt file
|
||||||
"""
|
"""
|
|
@ -1,13 +1,13 @@
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from typing import IO
|
from typing import IO
|
||||||
|
|
||||||
from source.mkw.Patch.PatchOperation.Operation import *
|
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
|
||||||
from source.wt import bmg
|
from source.wt import bmg
|
||||||
|
|
||||||
Patch: any
|
Patch: any
|
||||||
|
|
||||||
|
|
||||||
class BmgEncoder(AbstractOperation):
|
class BmgEncoder(AbstractPatchOperation):
|
||||||
"""
|
"""
|
||||||
encode a bmg file to a txt file
|
encode a bmg file to a txt file
|
||||||
"""
|
"""
|
|
@ -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
|
from source.wt import ctc
|
||||||
|
|
||||||
|
|
||||||
Patch: any
|
Patch: any
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import re
|
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.mkw.Track import Track
|
||||||
from source.wt import bmg
|
from source.wt import bmg
|
||||||
|
|
||||||
|
|
||||||
Patch: any
|
Patch: any
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from source.mkw.Patch.PatchOperation.Operation.BmgTxtEditor.Layer import *
|
from source.mkw.Patch.PatchOperation.BmgTxtEditor import AbstractLayer
|
||||||
|
|
||||||
|
|
||||||
Patch: any
|
Patch: any
|
|
@ -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
|
from source.wt import bmg
|
||||||
|
|
||||||
|
|
||||||
Patch: any
|
Patch: any
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from source.mkw.Patch.PatchOperation.Operation.BmgTxtEditor.Layer import *
|
from source.mkw.Patch.PatchOperation.BmgTxtEditor import AbstractLayer
|
||||||
|
|
||||||
|
|
||||||
Patch: any
|
Patch: any
|
62
source/mkw/Patch/PatchOperation/BmgTxtEditor/__init__.py
Normal file
62
source/mkw/Patch/PatchOperation/BmgTxtEditor/__init__.py
Normal file
|
@ -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
|
||||||
|
)
|
|
@ -1,14 +1,14 @@
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from typing import IO
|
from typing import IO
|
||||||
|
|
||||||
from source.mkw.Patch.PatchOperation.Operation import *
|
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
|
||||||
from source.wt import img
|
from source.wt import img
|
||||||
|
|
||||||
|
|
||||||
Patch: any
|
Patch: any
|
||||||
|
|
||||||
|
|
||||||
class ImageDecoder(AbstractOperation):
|
class ImageDecoder(AbstractPatchOperation):
|
||||||
"""
|
"""
|
||||||
decode a game image to a image file
|
decode a game image to a image file
|
||||||
"""
|
"""
|
|
@ -1,6 +1,6 @@
|
||||||
from PIL import ImageDraw, Image
|
from PIL import ImageDraw, Image
|
||||||
|
|
||||||
from source.mkw.Patch.PatchOperation.Operation.ImageEditor.Layer import *
|
from source.mkw.Patch.PatchOperation.ImageEditor import AbstractLayer
|
||||||
|
|
||||||
|
|
||||||
Patch: any
|
Patch: any
|
||||||
|
@ -24,4 +24,4 @@ class ColorLayer(AbstractLayer):
|
||||||
draw = ImageDraw.Draw(image)
|
draw = ImageDraw.Draw(image)
|
||||||
draw.rectangle(self.get_bbox(image), fill=self.color)
|
draw.rectangle(self.get_bbox(image), fill=self.color)
|
||||||
|
|
||||||
return image
|
return image
|
|
@ -1,7 +1,7 @@
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from source.mkw.Patch import *
|
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):
|
class ImageLayer(AbstractLayer):
|
||||||
|
@ -36,3 +36,4 @@ class ImageLayer(AbstractLayer):
|
||||||
)
|
)
|
||||||
|
|
||||||
return image
|
return image
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from PIL import ImageFont, ImageDraw, Image
|
from PIL import ImageFont, ImageDraw, Image
|
||||||
|
|
||||||
from source.mkw.Patch import *
|
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):
|
class TextLayer(AbstractLayer):
|
|
@ -1,8 +1,13 @@
|
||||||
|
from io import BytesIO
|
||||||
|
from typing import IO
|
||||||
from abc import abstractmethod, ABC
|
from abc import abstractmethod, ABC
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
|
from source.mkw.Patch import *
|
||||||
|
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
|
||||||
|
|
||||||
|
|
||||||
|
Layer: any
|
||||||
Patch: any
|
Patch: any
|
||||||
|
|
||||||
|
|
||||||
|
@ -58,6 +63,42 @@ class AbstractLayer(ABC):
|
||||||
Patch an image with the actual layer. Return the new image.
|
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
|
||||||
|
)
|
|
@ -1,14 +1,14 @@
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from typing import IO
|
from typing import IO
|
||||||
|
|
||||||
from source.mkw.Patch.PatchOperation.Operation import *
|
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
|
||||||
from source.wt import img
|
from source.wt import img
|
||||||
|
|
||||||
|
|
||||||
Patch: any
|
Patch: any
|
||||||
|
|
||||||
|
|
||||||
class ImageEncoder(AbstractOperation):
|
class ImageEncoder(AbstractPatchOperation):
|
||||||
"""
|
"""
|
||||||
encode an image to a game image file
|
encode an image to a game image file
|
||||||
"""
|
"""
|
|
@ -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"]
|
|
|
@ -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"])
|
|
|
@ -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"])
|
|
|
@ -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"]
|
|
|
@ -1,12 +1,12 @@
|
||||||
from typing import IO
|
from typing import IO
|
||||||
|
|
||||||
from source.mkw.Patch.PatchOperation.Operation import *
|
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
|
||||||
|
|
||||||
|
|
||||||
Patch: any
|
Patch: any
|
||||||
|
|
||||||
|
|
||||||
class Rename(AbstractOperation):
|
class Rename(AbstractPatchOperation):
|
||||||
"""
|
"""
|
||||||
Rename the output file
|
Rename the output file
|
||||||
"""
|
"""
|
|
@ -1,12 +1,12 @@
|
||||||
from typing import IO
|
from typing import IO
|
||||||
|
|
||||||
from source.mkw.Patch.PatchOperation.Operation import *
|
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
|
||||||
|
|
||||||
|
|
||||||
Patch: any
|
Patch: any
|
||||||
|
|
||||||
|
|
||||||
class Special(AbstractOperation):
|
class Special(AbstractPatchOperation):
|
||||||
"""
|
"""
|
||||||
use a file defined as special in the patch to replace the current file content
|
use a file defined as special in the patch to replace the current file content
|
||||||
"""
|
"""
|
|
@ -1,12 +1,12 @@
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from typing import IO
|
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.mkw.Patch import *
|
||||||
from source.wt import wstrt
|
from source.wt import wstrt
|
||||||
|
|
||||||
|
|
||||||
class StrEditor(AbstractOperation):
|
class StrEditor(AbstractPatchOperation):
|
||||||
"""
|
"""
|
||||||
patch the main.dol file
|
patch the main.dol file
|
||||||
"""
|
"""
|
|
@ -1,18 +1,39 @@
|
||||||
from source.mkw.Patch import *
|
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
|
Return an operation from its name
|
||||||
|
:name: name of the operation
|
||||||
:return: an Operation from its name
|
: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
|
return subclass
|
||||||
raise InvalidPatchOperation(name)
|
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
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in a new issue