From 5ef1f76b10681a1ec5d0e35793183575250f1ada Mon Sep 17 00:00:00 2001 From: Doug Hoskisson Date: Thu, 14 Mar 2024 14:29:29 -0700 Subject: [PATCH] Core: add layer for patches that don't use `Patch.py` (#2889) * Core: add layer for patches that don't use `Patch.py` * bump container version * APAutoPatchInterface name * mystic quest change * OoT and Adventure changes * missed name in docstring * container version compatibility --- Patch.py | 4 ++-- worlds/Files.py | 39 +++++++++++++++++++++++++++++---------- worlds/adventure/Rom.py | 4 ++-- worlds/ffmq/Output.py | 6 +++--- worlds/oot/Patches.py | 4 ++-- worlds/zillion/patch.py | 4 ++-- 6 files changed, 40 insertions(+), 21 deletions(-) diff --git a/Patch.py b/Patch.py index 091545700059..9b49876bb72d 100644 --- a/Patch.py +++ b/Patch.py @@ -8,7 +8,7 @@ import ModuleUpdate ModuleUpdate.update() -from worlds.Files import AutoPatchRegister, APPatch +from worlds.Files import AutoPatchRegister, APAutoPatchInterface class RomMeta(TypedDict): @@ -20,7 +20,7 @@ class RomMeta(TypedDict): def create_rom_file(patch_file: str) -> Tuple[RomMeta, str]: auto_handler = AutoPatchRegister.get_handler(patch_file) if auto_handler: - handler: APPatch = auto_handler(patch_file) + handler: APAutoPatchInterface = auto_handler(patch_file) target = os.path.splitext(patch_file)[0]+handler.result_file_ending handler.patch(target) return {"server": handler.server, diff --git a/worlds/Files.py b/worlds/Files.py index f46b9cba7a7c..b2ecb9afb880 100644 --- a/worlds/Files.py +++ b/worlds/Files.py @@ -6,7 +6,7 @@ import os import threading -from typing import ClassVar, Dict, Tuple, Any, Optional, Union, BinaryIO +from typing import ClassVar, Dict, List, Literal, Tuple, Any, Optional, Union, BinaryIO import bsdiff4 @@ -38,7 +38,7 @@ def get_handler(file: str) -> Optional[AutoPatchRegister]: return None -current_patch_version: int = 5 +container_version: int = 6 class InvalidDataError(Exception): @@ -50,7 +50,7 @@ class InvalidDataError(Exception): class APContainer: """A zipfile containing at least archipelago.json""" - version: int = current_patch_version + version: int = container_version compression_level: int = 9 compression_method: int = zipfile.ZIP_DEFLATED game: Optional[str] = None @@ -124,14 +124,31 @@ def get_manifest(self) -> Dict[str, Any]: "game": self.game, # minimum version of patch system expected for patching to be successful "compatible_version": 5, - "version": current_patch_version, + "version": container_version, } -class APPatch(APContainer, abc.ABC, metaclass=AutoPatchRegister): +class APPatch(APContainer): """ - An abstract `APContainer` that defines the requirements for an object - to be used by the `Patch.create_rom_file` function. + An `APContainer` that represents a patch file. + It includes the `procedure` key in the manifest to indicate that it is a patch. + + Your implementation should inherit from this if your output file + represents a patch file, but will not be applied with AP's `Patch.py` + """ + procedure: Union[Literal["custom"], List[Tuple[str, List[Any]]]] = "custom" + + def get_manifest(self) -> Dict[str, Any]: + manifest = super(APPatch, self).get_manifest() + manifest["procedure"] = self.procedure + manifest["compatible_version"] = 6 + return manifest + + +class APAutoPatchInterface(APPatch, abc.ABC, metaclass=AutoPatchRegister): + """ + An abstract `APPatch` that defines the requirements for a patch + to be applied with AP's `Patch.py` """ result_file_ending: str = ".sfc" @@ -140,14 +157,15 @@ def patch(self, target: str) -> None: """ create the output file with the file name `target` """ -class APDeltaPatch(APPatch): - """An APPatch that additionally has delta.bsdiff4 - containing a delta patch to get the desired file, often a rom.""" +class APDeltaPatch(APAutoPatchInterface): + """An implementation of `APAutoPatchInterface` that additionally + has delta.bsdiff4 containing a delta patch to get the desired file.""" hash: Optional[str] # base checksum of source file patch_file_ending: str = "" delta: Optional[bytes] = None source_data: bytes + procedure = None # delete this line when APPP is added def __init__(self, *args: Any, patched_path: str = "", **kwargs: Any) -> None: self.patched_path = patched_path @@ -158,6 +176,7 @@ def get_manifest(self) -> Dict[str, Any]: manifest["base_checksum"] = self.hash manifest["result_file_ending"] = self.result_file_ending manifest["patch_file_ending"] = self.patch_file_ending + manifest["compatible_version"] = 5 # delete this line when APPP is added return manifest @classmethod diff --git a/worlds/adventure/Rom.py b/worlds/adventure/Rom.py index 9f1ca3fe5eba..ca64e569716a 100644 --- a/worlds/adventure/Rom.py +++ b/worlds/adventure/Rom.py @@ -7,7 +7,7 @@ import Utils from .Locations import AdventureLocation, LocationData from settings import get_settings -from worlds.Files import APDeltaPatch, AutoPatchRegister, APContainer +from worlds.Files import APPatch, AutoPatchRegister import bsdiff4 @@ -78,7 +78,7 @@ def get_dict(self): return ret_dict -class AdventureDeltaPatch(APContainer, metaclass=AutoPatchRegister): +class AdventureDeltaPatch(APPatch, metaclass=AutoPatchRegister): hash = ADVENTUREHASH game = "Adventure" patch_file_ending = ".apadvn" diff --git a/worlds/ffmq/Output.py b/worlds/ffmq/Output.py index 9daee9f643a9..1b17aaa98f28 100644 --- a/worlds/ffmq/Output.py +++ b/worlds/ffmq/Output.py @@ -4,7 +4,7 @@ from copy import deepcopy from .Regions import object_id_table from Utils import __version__ -from worlds.Files import APContainer +from worlds.Files import APPatch import pkgutil settings_template = yaml.load(pkgutil.get_data(__name__, "data/settings.yaml"), yaml.Loader) @@ -116,10 +116,10 @@ def tf(option): APMQ.write_contents(zf) -class APMQFile(APContainer): +class APMQFile(APPatch): game = "Final Fantasy Mystic Quest" def get_manifest(self): manifest = super().get_manifest() manifest["patch_file_ending"] = ".apmq" - return manifest \ No newline at end of file + return manifest diff --git a/worlds/oot/Patches.py b/worlds/oot/Patches.py index 0f1d3f4dcb8c..2219d7bb95a8 100644 --- a/worlds/oot/Patches.py +++ b/worlds/oot/Patches.py @@ -29,14 +29,14 @@ from .texture_util import ci4_rgba16patch_to_ci8, rgba16_patch from .Utils import __version__ -from worlds.Files import APContainer +from worlds.Files import APPatch from Utils import __version__ as ap_version AP_PROGRESSION = 0xD4 AP_JUNK = 0xD5 -class OoTContainer(APContainer): +class OoTContainer(APPatch): game: str = 'Ocarina of Time' def __init__(self, patch_data: bytes, base_path: str, output_directory: str, diff --git a/worlds/zillion/patch.py b/worlds/zillion/patch.py index dcbb85bcfc89..6bc6d04dd663 100644 --- a/worlds/zillion/patch.py +++ b/worlds/zillion/patch.py @@ -5,7 +5,7 @@ from typing_extensions import override import Utils -from worlds.Files import APPatch +from worlds.Files import APAutoPatchInterface from zilliandomizer.patch import Patcher @@ -14,7 +14,7 @@ USHASH = 'd4bf9e7bcf9a48da53785d2ae7bc4270' -class ZillionPatch(APPatch): +class ZillionPatch(APAutoPatchInterface): hash = USHASH game = "Zillion" patch_file_ending = ".apzl"