diff --git a/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonLanguageMenu.d/message/Common.bmg.json b/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonLanguageMenu.d/message/Common.bmg.json index f241942..cc38b58 100644 --- a/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonLanguageMenu.d/message/Common.bmg.json +++ b/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonLanguageMenu.d/message/Common.bmg.json @@ -1,5 +1,5 @@ { - "mode": "edit", + "source": "game", "operation": { "bmg-decode": {}, "bmgtxt-edit": { diff --git a/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/button/blyt/cup_icon_64x64_common.brlyt.json b/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/button/blyt/cup_icon_64x64_common.brlyt.json index 23a93ab..654927b 100644 --- a/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/button/blyt/cup_icon_64x64_common.brlyt.json +++ b/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/button/blyt/cup_icon_64x64_common.brlyt.json @@ -1,3 +1,3 @@ { - "mode": "overwrite" + "mode": "replace" } \ No newline at end of file diff --git a/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/button/ctrl/Back.brctr.json b/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/button/ctrl/Back.brctr.json index 23a93ab..654927b 100644 --- a/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/button/ctrl/Back.brctr.json +++ b/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/button/ctrl/Back.brctr.json @@ -1,3 +1,3 @@ { - "mode": "overwrite" + "mode": "replace" } \ No newline at end of file diff --git a/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/button/ctrl/CupSelectCup.brctr.json b/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/button/ctrl/CupSelectCup.brctr.json index 23a93ab..654927b 100644 --- a/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/button/ctrl/CupSelectCup.brctr.json +++ b/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/button/ctrl/CupSelectCup.brctr.json @@ -1,3 +1,3 @@ { - "mode": "overwrite" + "mode": "replace" } \ No newline at end of file diff --git a/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/control/blyt/cup_icon_64x64_common.brlyt.json b/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/control/blyt/cup_icon_64x64_common.brlyt.json index 23a93ab..654927b 100644 --- a/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/control/blyt/cup_icon_64x64_common.brlyt.json +++ b/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/control/blyt/cup_icon_64x64_common.brlyt.json @@ -1,3 +1,3 @@ { - "mode": "overwrite" + "mode": "replace" } \ No newline at end of file diff --git a/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/control/ctrl/CourseSelectCup.brctr.json b/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/control/ctrl/CourseSelectCup.brctr.json index 23a93ab..654927b 100644 --- a/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/control/ctrl/CourseSelectCup.brctr.json +++ b/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/control/ctrl/CourseSelectCup.brctr.json @@ -1,3 +1,3 @@ { - "mode": "overwrite" + "mode": "replace" } \ No newline at end of file diff --git a/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/demo/blyt/course_name.brlyt.json b/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/demo/blyt/course_name.brlyt.json index 23a93ab..654927b 100644 --- a/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/demo/blyt/course_name.brlyt.json +++ b/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/demo/blyt/course_name.brlyt.json @@ -1,3 +1,3 @@ { - "mode": "overwrite" + "mode": "replace" } \ No newline at end of file diff --git a/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/demo/timg/tt_hatena_64x64.tpl.png.json b/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/demo/timg/tt_hatena_64x64.tpl.png.json index 8b06523..d44f5db 100644 --- a/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/demo/timg/tt_hatena_64x64.tpl.png.json +++ b/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/CommonMenu.d/demo/timg/tt_hatena_64x64.tpl.png.json @@ -1,5 +1,5 @@ { - "mode": "overwrite", + "mode": "replace", "operation": { "img-encode": {"encoding": "TPL.RGB5A3"} } diff --git a/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/MultiplayerLanguage.d/message/Common.bmg.json b/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/MultiplayerLanguage.d/message/Common.bmg.json index 786f72c..25a0266 100644 --- a/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/MultiplayerLanguage.d/message/Common.bmg.json +++ b/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/MultiplayerLanguage.d/message/Common.bmg.json @@ -1,5 +1,5 @@ { - "mode": "edit", + "source": "game", "operation": { "bmg-decode": {}, "bmgtxt-edit": { diff --git a/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/RaceLanguage.d/message/Common.bmg.json b/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/RaceLanguage.d/message/Common.bmg.json index d67a114..9a3507f 100644 --- a/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/RaceLanguage.d/message/Common.bmg.json +++ b/Pack/MKWFaraphel/essentials/_PREPATCH/files/Scene/UI/RaceLanguage.d/message/Common.bmg.json @@ -1,5 +1,5 @@ { - "mode": "edit", + "source": "game", "operation": { "bmg-decode": {}, "bmgtxt-edit": { diff --git a/Pack/MKWFaraphel/essentials/_PREPATCH/files/rel/StaticR.rel.json b/Pack/MKWFaraphel/essentials/_PREPATCH/files/rel/StaticR.rel.json index c169c96..577c7f9 100644 --- a/Pack/MKWFaraphel/essentials/_PREPATCH/files/rel/StaticR.rel.json +++ b/Pack/MKWFaraphel/essentials/_PREPATCH/files/rel/StaticR.rel.json @@ -1,5 +1,5 @@ { - "mode": "edit", + "source": "game", "operation": { "str-edit": { "region": "5500" diff --git a/Pack/MKWFaraphel/essentials/_PREPATCH/files/thp/video.thp.json b/Pack/MKWFaraphel/essentials/_PREPATCH/files/thp/video.thp.json index 2ca454a..7814605 100644 --- a/Pack/MKWFaraphel/essentials/_PREPATCH/files/thp/video.thp.json +++ b/Pack/MKWFaraphel/essentials/_PREPATCH/files/thp/video.thp.json @@ -1,4 +1,4 @@ { - "mode": "match-copy", + "mode": "match", "match_regex": "*/*.thp" } \ No newline at end of file diff --git a/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/de.json b/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/de.json index f9db81e..bbad907 100644 --- a/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/de.json +++ b/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/de.json @@ -1,5 +1,5 @@ { - "mode": "overwrite", + "mode": "replace", "operation": { "img-edit": { "layers": [ diff --git a/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/en.json b/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/en.json index a72821c..e9545e0 100644 --- a/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/en.json +++ b/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/en.json @@ -1,5 +1,5 @@ { - "mode": "overwrite", + "mode": "replace", "operation": { "img-edit": { "layers": [ diff --git a/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/es.json b/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/es.json index cb35000..aceec7a 100644 --- a/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/es.json +++ b/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/es.json @@ -1,5 +1,5 @@ { - "mode": "overwrite", + "mode": "replace", "operation": { "img-edit": { "layers": [ diff --git a/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/fr.json b/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/fr.json index e07990f..8b82d52 100644 --- a/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/fr.json +++ b/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/fr.json @@ -1,5 +1,5 @@ { - "mode": "overwrite", + "mode": "replace", "operation": { "img-edit": { "layers": [ diff --git a/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/ge.json b/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/ge.json index 1c4aad8..154db5b 100644 --- a/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/ge.json +++ b/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/ge.json @@ -1,5 +1,5 @@ { - "mode": "overwrite", + "mode": "replace", "operation": { "img-edit": { "layers": [ diff --git a/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/it.json b/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/it.json index 35105a8..5449aa5 100644 --- a/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/it.json +++ b/Pack/MKWFaraphel/specific/_PATCH/files/Boot/Strap/it.json @@ -1,5 +1,5 @@ { - "mode": "overwrite", + "mode": "replace", "operation": { "img-edit": { "layers": [ diff --git a/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/CommonLanguageMenu.d/message/Menu.bmg.json b/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/CommonLanguageMenu.d/message/Menu.bmg.json index 0e2a801..d4f61f9 100644 --- a/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/CommonLanguageMenu.d/message/Menu.bmg.json +++ b/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/CommonLanguageMenu.d/message/Menu.bmg.json @@ -1,5 +1,5 @@ { - "mode": "edit", + "source": "game", "operation": { "bmg-decode": {}, diff --git a/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/en.json b/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/en.json index 1f5b07e..a0742e0 100644 --- a/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/en.json +++ b/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/en.json @@ -1,5 +1,5 @@ { - "mode": "edit", + "source": "game", "operation": { "bmg-decode": {}, "bmgtxt-edit": { diff --git a/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/es.json b/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/es.json index 5674c6c..47548aa 100644 --- a/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/es.json +++ b/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/es.json @@ -1,5 +1,5 @@ { - "mode": "edit", + "source": "game", "operation": { "bmg-decode": {}, "bmgtxt-edit": { diff --git a/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/fr.json b/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/fr.json index 2122fd6..eaa2b55 100644 --- a/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/fr.json +++ b/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/fr.json @@ -1,5 +1,5 @@ { - "mode": "edit", + "source": "game", "operation": { "bmg-decode": {}, "bmgtxt-edit": { diff --git a/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/ge.json b/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/ge.json index b6e8dda..6ef5682 100644 --- a/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/ge.json +++ b/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/ge.json @@ -1,5 +1,5 @@ { - "mode": "edit", + "source": "game", "operation": { "bmg-decode": {}, "bmgtxt-edit": { diff --git a/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/it.json b/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/it.json index e9cbcb9..81d277f 100644 --- a/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/it.json +++ b/Pack/MKWFaraphel/specific/_PATCH/files/Scene/UI/it.json @@ -1,5 +1,5 @@ { - "mode": "edit", + "source": "game", "operation": { "bmg-decode": {}, "bmgtxt-edit": { diff --git a/Pack/MKWFaraphel/szstest/_PATCH/files/Race/Course/track.szs b/Pack/MKWFaraphel/szstest/_PATCH/files/Race/Course/track.szs deleted file mode 100644 index e69de29..0000000 diff --git a/Pack/MKWFaraphel/szstest/_PATCH/files/Race/Course/track.szs.json b/Pack/MKWFaraphel/szstest/_PATCH/files/Race/Course/track.szs.json deleted file mode 100644 index 63b7bf6..0000000 --- a/Pack/MKWFaraphel/szstest/_PATCH/files/Race/Course/track.szs.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "mode": "match-edit", - "match_regex": "*.szs", - - "operation": { - "szs-edit": { - "scale": {"x": 2, "y": 2, "z": 2}, - "speed": 1.5, - "laps": 9 - } - } -} \ No newline at end of file diff --git a/source/mkw/Patch/PatchDirectory.py b/source/mkw/Patch/PatchDirectory.py index 802e9dd..6a07353 100644 --- a/source/mkw/Patch/PatchDirectory.py +++ b/source/mkw/Patch/PatchDirectory.py @@ -26,12 +26,10 @@ class PatchDirectory(PatchObject): """ patch a subdirectory of the game with the PatchDirectory """ - yield {"description": f"Patching {self}"} + yield {"description": f"Patching {game_subpath}"} - if self.patch.mod_config.safe_eval( - self.configuration["if"], - env={"extracted_game": extracted_game} - ) is not True: return + # check if the directory should be patched + if not self.is_enabled(extracted_game): return match self.configuration["mode"]: # if the mode is copy, then simply patch the subfile into the game with the same path @@ -59,4 +57,4 @@ class PatchDirectory(PatchObject): # else raise an error case _: - raise InvalidPatchMode(self.configuration["mode"]) + raise InvalidPatchMode(self, self.configuration["mode"]) diff --git a/source/mkw/Patch/PatchFile.py b/source/mkw/Patch/PatchFile.py index 85db9ee..d2dc524 100644 --- a/source/mkw/Patch/PatchFile.py +++ b/source/mkw/Patch/PatchFile.py @@ -2,7 +2,7 @@ from io import BytesIO from pathlib import Path from typing import Generator, IO, TYPE_CHECKING -from source.mkw.Patch import PathOutsidePatch, InvalidPatchMode +from source.mkw.Patch import PathOutsidePatch, InvalidPatchMode, InvalidSourceMode from source.mkw.Patch.PatchOperation import AbstractPatchOperation from source.mkw.Patch.PatchObject import PatchObject from source.wt.szs import SZSPath @@ -16,46 +16,27 @@ class PatchFile(PatchObject): Represent a file from a patch """ - def get_source_path(self, game_subpath: Path): + def get_source_path(self, game_subpath: Path) -> Path: """ Return the path of the file that the patch is applied on. - If the configuration mode is set to "edit", then return the path of the file inside the Game + If the configuration mode is set to "game", then return the path of the file inside the Game Else, return the path of the file inside the Patch """ - match self.configuration["mode"]: - case "edit": - return game_subpath - case _: - return self.full_path + match self.configuration["source"]: + case "patch": return self.full_path + case "game": return game_subpath + case _: raise InvalidSourceMode(self, self.configuration["course"]) - def install(self, extracted_game: "ExtractedGame", game_subpath: Path) -> Generator[dict, None, None]: + def get_patched_file(self, game_subpath: Path) -> (str, BytesIO): """ - patch a subfile of the game with the PatchFile + Return the name and the content of the patched file + :param game_subpath: path to the game subfile + :return: the name and the content of the patched file """ - yield {"description": f"Patching {self}"} - - # check if the file should be patched considering the "if" configuration - if self.patch.mod_config.safe_eval( - self.configuration["if"], - env={"extracted_game": extracted_game} - ) is not True: return - - # check if the path to the game_subpath is inside a szs, and if yes extract it - for szs_subpath in filter(lambda path: path.suffix == ".d", - game_subpath.relative_to(extracted_game.path).parents): - szs_path = extracted_game.path / szs_subpath - - # if the archive is already extracted, ignore - if not szs_path.exists(): - # if the szs file in the game exists, extract it - if szs_path.with_suffix(".szs").exists(): - SZSPath(szs_path.with_suffix(".szs")).extract_all(szs_path) - - # apply operation on the file patch_source: Path = self.get_source_path(game_subpath) patch_name: str = game_subpath.name patch_content: BytesIO = BytesIO(open(patch_source, "rb").read()) - # the file is converted into a BytesIO because if the mode is "edit", + # the file is converted into a BytesIO because if the source is "game", # the file is overwritten and if the content is untouched, the file will only be lost for operation_name, operation in self.configuration.get("operation", {}).items(): @@ -65,42 +46,90 @@ class PatchFile(PatchObject): self.patch, patch_name, patch_content ) - def write_patch(destination: Path, patch_content: IO) -> None: - """ - Write a patch content to the destination. Automatically create the directory and seek to the start. - :param destination: file where the content will be written - :param patch_content: content of the file to write - """ - destination.parent.mkdir(parents=True, exist_ok=True) - with open(destination, "wb") as file: - patch_content.seek(0) - file.write(patch_content.read()) + return patch_name, patch_content + + @staticmethod + def write_patch(destination: Path, patch_content: IO) -> None: + """ + Write a patch content to the destination. Automatically create the directory and seek to the start. + :param destination: file where the content will be written + :param patch_content: content of the file to write + """ + destination.parent.mkdir(parents=True, exist_ok=True) + with open(destination, "wb") as file: + patch_content.seek(0) + file.write(patch_content.read()) + + @staticmethod + def check_szs(extracted_game: "ExtractedGame", game_subpath: Path) -> None: + """ + Check if game path is inside a szs archive. If yes, extract it. + :param extracted_game: the extracted game object + :param game_subpath: path to the game file that is being patched + """ + for szs_subpath in filter(lambda path: path.suffix == ".d", + game_subpath.relative_to(extracted_game.path).parents): + + szs_path = extracted_game.path / szs_subpath + + # if the archive is not already extracted and the szs file in the game exists, extract it + if not szs_path.exists() and szs_path.with_suffix(".szs").exists(): + SZSPath(szs_path.with_suffix(".szs")).extract_all(szs_path) + + def install(self, extracted_game: "ExtractedGame", game_subpath: Path) -> Generator[dict, None, None]: + """ + patch a subfile of the game with the PatchFile + """ + yield {"description": f"Patching {game_subpath}"} + + # check if the file should be patched + if not self.is_enabled(extracted_game): return + + # check if it is patching a szs archive. If yes, extract it. + self.check_szs(extracted_game, game_subpath) + + # apply operation on the file match self.configuration["mode"]: # if the mode is copy, replace the subfile in the game by the PatchFile - case "copy" | "edit": - write_patch(game_subpath.parent / patch_name, patch_content) + case "copy": + patch_name, patch_content = self.get_patched_file(game_subpath) + self.write_patch(game_subpath.parent / patch_name, patch_content) + patch_content.close() - # if the mode is overwrite, only write if the file existed before - case "overwrite": + # if the mode is replace, only write if the file existed before + case "replace": + patch_name, patch_content = self.get_patched_file(game_subpath) if (game_subpath.parent / patch_name).exists(): - write_patch(game_subpath.parent / patch_name, patch_content) + self.write_patch(game_subpath.parent / patch_name, patch_content) + patch_content.close() # if the mode is match, replace all the subfiles that match match_regex by the PatchFile case "match": + patch_content: BytesIO = BytesIO() + + # if the source is the patch, then directly calculate the patch content + # so that it is not recalculated for every file with no reason + if self.configuration["source"] == "patch": + _, patch_content = self.get_patched_file(game_subpath) + for game_subfile in game_subpath.parent.glob(self.configuration["match_regex"]): # disallow patching files outside of the game if not game_subfile.relative_to(extracted_game.path): raise PathOutsidePatch(game_subfile, extracted_game.path) - # patch the game with the subpatch - write_patch(game_subfile, patch_content) + yield {"description": f"Patching {game_subfile}"} + + # if the source is the game, then recalculate the content for every game subfile + if self.configuration["source"] == "game": + _, patch_content = self.get_patched_file(game_subfile) + + # patch the game with the patch content + self.write_patch(game_subfile, patch_content) + + patch_content.close() # ignore if mode is "ignore", useful if the file is used as a resource for an operation case "ignore": pass - # else raise an error - case _: - raise InvalidPatchMode(self.configuration["mode"]) - - patch_content.close() + case _: raise InvalidPatchMode(self, self.configuration["mode"]) diff --git a/source/mkw/Patch/PatchObject.py b/source/mkw/Patch/PatchObject.py index 0d5f489..6ade202 100644 --- a/source/mkw/Patch/PatchObject.py +++ b/source/mkw/Patch/PatchObject.py @@ -34,8 +34,9 @@ class PatchObject(ABC): # default configuration self._configuration = { - "mode": "copy", - "if": "True", + "mode": "copy", # mode on how should the file be patched + "source": "patch", # source of the file data that will be modified + "if": "True", # condition for the patch to be applied } configuration_path = self.full_path.with_suffix(self.full_path.suffix + ".json") @@ -68,3 +69,12 @@ class PatchObject(ABC): install the PatchObject into the game yield the step of the process """ + ... + + def is_enabled(self, extracted_game: "ExtractedGame") -> bool: + """ + Return if the patch object is actually enabled + :param extracted_game: the extracted game object + :return: should the patch be applied ? + """ + return self.patch.mod_config.safe_eval(self.configuration["if"], env={"extracted_game": extracted_game}) is True diff --git a/source/mkw/Patch/__init__.py b/source/mkw/Patch/__init__.py index 380a691..8b60381 100644 --- a/source/mkw/Patch/__init__.py +++ b/source/mkw/Patch/__init__.py @@ -3,13 +3,19 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: from source.mkw.Patch import Patch + from source.mkw.Patch.PatchObject import PatchObject 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__(f'Error : path "{forbidden_path}" outside of allowed range {allowed_range}') class InvalidPatchMode(Exception): - def __init__(self, mode: str): - super().__init__(f"Error : mode \"{mode}\" is not implemented") + def __init__(self, patch: "PatchObject", mode: str): + super().__init__(f'Error : 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}")')