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
6 changed files with 40 additions and 21 deletions

View File

@@ -6,7 +6,7 @@ import zipfile
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 @@ class AutoPatchRegister(abc.ABCMeta):
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 @@ class APContainer:
"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 @@ class APPatch(APContainer, abc.ABC, metaclass=AutoPatchRegister):
""" 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 @@ class APDeltaPatch(APPatch):
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