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
This commit is contained in:
Doug Hoskisson 2024-03-14 14:29:29 -07:00 committed by GitHub
parent fa233b2583
commit e534abeab0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 40 additions and 21 deletions

View File

@ -8,7 +8,7 @@ if __name__ == "__main__":
import ModuleUpdate import ModuleUpdate
ModuleUpdate.update() ModuleUpdate.update()
from worlds.Files import AutoPatchRegister, APPatch from worlds.Files import AutoPatchRegister, APAutoPatchInterface
class RomMeta(TypedDict): class RomMeta(TypedDict):
@ -20,7 +20,7 @@ class RomMeta(TypedDict):
def create_rom_file(patch_file: str) -> Tuple[RomMeta, str]: def create_rom_file(patch_file: str) -> Tuple[RomMeta, str]:
auto_handler = AutoPatchRegister.get_handler(patch_file) auto_handler = AutoPatchRegister.get_handler(patch_file)
if auto_handler: 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 target = os.path.splitext(patch_file)[0]+handler.result_file_ending
handler.patch(target) handler.patch(target)
return {"server": handler.server, return {"server": handler.server,

View File

@ -6,7 +6,7 @@ import zipfile
import os import os
import threading 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 import bsdiff4
@ -38,7 +38,7 @@ class AutoPatchRegister(abc.ABCMeta):
return None return None
current_patch_version: int = 5 container_version: int = 6
class InvalidDataError(Exception): class InvalidDataError(Exception):
@ -50,7 +50,7 @@ class InvalidDataError(Exception):
class APContainer: class APContainer:
"""A zipfile containing at least archipelago.json""" """A zipfile containing at least archipelago.json"""
version: int = current_patch_version version: int = container_version
compression_level: int = 9 compression_level: int = 9
compression_method: int = zipfile.ZIP_DEFLATED compression_method: int = zipfile.ZIP_DEFLATED
game: Optional[str] = None game: Optional[str] = None
@ -124,14 +124,31 @@ class APContainer:
"game": self.game, "game": self.game,
# minimum version of patch system expected for patching to be successful # minimum version of patch system expected for patching to be successful
"compatible_version": 5, "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 An `APContainer` that represents a patch file.
to be used by the `Patch.create_rom_file` function. 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" result_file_ending: str = ".sfc"
@ -140,14 +157,15 @@ class APPatch(APContainer, abc.ABC, metaclass=AutoPatchRegister):
""" create the output file with the file name `target` """ """ create the output file with the file name `target` """
class APDeltaPatch(APPatch): class APDeltaPatch(APAutoPatchInterface):
"""An APPatch that additionally has delta.bsdiff4 """An implementation of `APAutoPatchInterface` that additionally
containing a delta patch to get the desired file, often a rom.""" has delta.bsdiff4 containing a delta patch to get the desired file."""
hash: Optional[str] # base checksum of source file hash: Optional[str] # base checksum of source file
patch_file_ending: str = "" patch_file_ending: str = ""
delta: Optional[bytes] = None delta: Optional[bytes] = None
source_data: bytes source_data: bytes
procedure = None # delete this line when APPP is added
def __init__(self, *args: Any, patched_path: str = "", **kwargs: Any) -> None: def __init__(self, *args: Any, patched_path: str = "", **kwargs: Any) -> None:
self.patched_path = patched_path self.patched_path = patched_path
@ -158,6 +176,7 @@ class APDeltaPatch(APPatch):
manifest["base_checksum"] = self.hash manifest["base_checksum"] = self.hash
manifest["result_file_ending"] = self.result_file_ending manifest["result_file_ending"] = self.result_file_ending
manifest["patch_file_ending"] = self.patch_file_ending manifest["patch_file_ending"] = self.patch_file_ending
manifest["compatible_version"] = 5 # delete this line when APPP is added
return manifest return manifest
@classmethod @classmethod

View File

@ -7,7 +7,7 @@ from typing import Optional, Any
import Utils import Utils
from .Locations import AdventureLocation, LocationData from .Locations import AdventureLocation, LocationData
from settings import get_settings from settings import get_settings
from worlds.Files import APDeltaPatch, AutoPatchRegister, APContainer from worlds.Files import APPatch, AutoPatchRegister
import bsdiff4 import bsdiff4
@ -78,7 +78,7 @@ class BatNoTouchLocation:
return ret_dict return ret_dict
class AdventureDeltaPatch(APContainer, metaclass=AutoPatchRegister): class AdventureDeltaPatch(APPatch, metaclass=AutoPatchRegister):
hash = ADVENTUREHASH hash = ADVENTUREHASH
game = "Adventure" game = "Adventure"
patch_file_ending = ".apadvn" patch_file_ending = ".apadvn"

View File

@ -4,7 +4,7 @@ import zipfile
from copy import deepcopy from copy import deepcopy
from .Regions import object_id_table from .Regions import object_id_table
from Utils import __version__ from Utils import __version__
from worlds.Files import APContainer from worlds.Files import APPatch
import pkgutil import pkgutil
settings_template = yaml.load(pkgutil.get_data(__name__, "data/settings.yaml"), yaml.Loader) settings_template = yaml.load(pkgutil.get_data(__name__, "data/settings.yaml"), yaml.Loader)
@ -116,10 +116,10 @@ def generate_output(self, output_directory):
APMQ.write_contents(zf) APMQ.write_contents(zf)
class APMQFile(APContainer): class APMQFile(APPatch):
game = "Final Fantasy Mystic Quest" game = "Final Fantasy Mystic Quest"
def get_manifest(self): def get_manifest(self):
manifest = super().get_manifest() manifest = super().get_manifest()
manifest["patch_file_ending"] = ".apmq" manifest["patch_file_ending"] = ".apmq"
return manifest return manifest

View File

@ -29,14 +29,14 @@ from .TextBox import character_table, NORMAL_LINE_WIDTH, rom_safe_text
from .texture_util import ci4_rgba16patch_to_ci8, rgba16_patch from .texture_util import ci4_rgba16patch_to_ci8, rgba16_patch
from .Utils import __version__ from .Utils import __version__
from worlds.Files import APContainer from worlds.Files import APPatch
from Utils import __version__ as ap_version from Utils import __version__ as ap_version
AP_PROGRESSION = 0xD4 AP_PROGRESSION = 0xD4
AP_JUNK = 0xD5 AP_JUNK = 0xD5
class OoTContainer(APContainer): class OoTContainer(APPatch):
game: str = 'Ocarina of Time' game: str = 'Ocarina of Time'
def __init__(self, patch_data: bytes, base_path: str, output_directory: str, def __init__(self, patch_data: bytes, base_path: str, output_directory: str,

View File

@ -5,7 +5,7 @@ import zipfile
from typing_extensions import override from typing_extensions import override
import Utils import Utils
from worlds.Files import APPatch from worlds.Files import APAutoPatchInterface
from zilliandomizer.patch import Patcher from zilliandomizer.patch import Patcher
@ -14,7 +14,7 @@ from .gen_data import GenData
USHASH = 'd4bf9e7bcf9a48da53785d2ae7bc4270' USHASH = 'd4bf9e7bcf9a48da53785d2ae7bc4270'
class ZillionPatch(APPatch): class ZillionPatch(APAutoPatchInterface):
hash = USHASH hash = USHASH
game = "Zillion" game = "Zillion"
patch_file_ending = ".apzl" patch_file_ending = ".apzl"