APProcedurePatch: hotfix changing class variables to instance variables (#2996)

* change class variables to instance variables

* Update worlds/Files.py

Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com>

* Update worlds/Files.py

Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com>

* move required_extensions to tuple

* fix missing tuple ellipsis

* fix classvar mixup

* rename tokens to _tokens. use hasattr

* type hint cleanup

* Update Files.py

* check using isinstance instead

---------

Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com>
This commit is contained in:
Silvris 2024-03-20 17:45:32 -05:00 committed by GitHub
parent 12864f7b24
commit f4b7c28a33
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 20 additions and 14 deletions

View File

@ -7,7 +7,7 @@ from enum import IntEnum
import os import os
import threading import threading
from typing import ClassVar, Dict, List, Literal, Tuple, Any, Optional, Union, BinaryIO, overload from typing import ClassVar, Dict, List, Literal, Tuple, Any, Optional, Union, BinaryIO, overload, Sequence
import bsdiff4 import bsdiff4
@ -41,7 +41,7 @@ class AutoPatchRegister(abc.ABCMeta):
class AutoPatchExtensionRegister(abc.ABCMeta): class AutoPatchExtensionRegister(abc.ABCMeta):
extension_types: ClassVar[Dict[str, AutoPatchExtensionRegister]] = {} extension_types: ClassVar[Dict[str, AutoPatchExtensionRegister]] = {}
required_extensions: List[str] = [] required_extensions: Tuple[str, ...] = ()
def __new__(mcs, name: str, bases: Tuple[type, ...], dct: Dict[str, Any]) -> AutoPatchExtensionRegister: def __new__(mcs, name: str, bases: Tuple[type, ...], dct: Dict[str, Any]) -> AutoPatchExtensionRegister:
# construct class # construct class
@ -51,7 +51,9 @@ class AutoPatchExtensionRegister(abc.ABCMeta):
return new_class return new_class
@staticmethod @staticmethod
def get_handler(game: str) -> Union[AutoPatchExtensionRegister, List[AutoPatchExtensionRegister]]: def get_handler(game: Optional[str]) -> Union[AutoPatchExtensionRegister, List[AutoPatchExtensionRegister]]:
if not game:
return APPatchExtension
handler = AutoPatchExtensionRegister.extension_types.get(game, APPatchExtension) handler = AutoPatchExtensionRegister.extension_types.get(game, APPatchExtension)
if handler.required_extensions: if handler.required_extensions:
handlers = [handler] handlers = [handler]
@ -191,7 +193,7 @@ class APProcedurePatch(APAutoPatchInterface):
hash: Optional[str] # base checksum of source file hash: Optional[str] # base checksum of source file
source_data: bytes source_data: bytes
patch_file_ending: str = "" patch_file_ending: str = ""
files: Dict[str, bytes] = {} files: Dict[str, bytes]
@classmethod @classmethod
def get_source_data(cls) -> bytes: def get_source_data(cls) -> bytes:
@ -206,6 +208,7 @@ class APProcedurePatch(APAutoPatchInterface):
def __init__(self, *args: Any, **kwargs: Any): def __init__(self, *args: Any, **kwargs: Any):
super(APProcedurePatch, self).__init__(*args, **kwargs) super(APProcedurePatch, self).__init__(*args, **kwargs)
self.files = {}
def get_manifest(self) -> Dict[str, Any]: def get_manifest(self) -> Dict[str, Any]:
manifest = super(APProcedurePatch, self).get_manifest() manifest = super(APProcedurePatch, self).get_manifest()
@ -277,7 +280,7 @@ class APDeltaPatch(APProcedurePatch):
super(APDeltaPatch, self).__init__(*args, **kwargs) super(APDeltaPatch, self).__init__(*args, **kwargs)
self.patched_path = patched_path self.patched_path = patched_path
def write_contents(self, opened_zipfile: zipfile.ZipFile): def write_contents(self, opened_zipfile: zipfile.ZipFile) -> None:
self.write_file("delta.bsdiff4", self.write_file("delta.bsdiff4",
bsdiff4.diff(self.get_source_data_with_cache(), open(self.patched_path, "rb").read())) bsdiff4.diff(self.get_source_data_with_cache(), open(self.patched_path, "rb").read()))
super(APDeltaPatch, self).write_contents(opened_zipfile) super(APDeltaPatch, self).write_contents(opened_zipfile)
@ -296,12 +299,12 @@ class APTokenMixin:
""" """
A class that defines functions for generating a token binary, for use in patches. A class that defines functions for generating a token binary, for use in patches.
""" """
tokens: List[ _tokens: Sequence[
Tuple[APTokenTypes, int, Union[ Tuple[APTokenTypes, int, Union[
bytes, # WRITE bytes, # WRITE
Tuple[int, int], # COPY, RLE Tuple[int, int], # COPY, RLE
int # AND_8, OR_8, XOR_8 int # AND_8, OR_8, XOR_8
]]] = [] ]]] = ()
def get_token_binary(self) -> bytes: def get_token_binary(self) -> bytes:
""" """
@ -309,8 +312,8 @@ class APTokenMixin:
:return: A bytes object representing the token data. :return: A bytes object representing the token data.
""" """
data = bytearray() data = bytearray()
data.extend(len(self.tokens).to_bytes(4, "little")) data.extend(len(self._tokens).to_bytes(4, "little"))
for token_type, offset, args in self.tokens: for token_type, offset, args in self._tokens:
data.append(token_type) data.append(token_type)
data.extend(offset.to_bytes(4, "little")) data.extend(offset.to_bytes(4, "little"))
if token_type in [APTokenTypes.AND_8, APTokenTypes.OR_8, APTokenTypes.XOR_8]: if token_type in [APTokenTypes.AND_8, APTokenTypes.OR_8, APTokenTypes.XOR_8]:
@ -351,11 +354,14 @@ class APTokenMixin:
data: bytes) -> None: data: bytes) -> None:
... ...
def write_token(self, token_type: APTokenTypes, offset: int, data: Union[bytes, Tuple[int, int], int]): def write_token(self, token_type: APTokenTypes, offset: int, data: Union[bytes, Tuple[int, int], int]) -> None:
""" """
Stores a token to be used by patching. Stores a token to be used by patching.
""" """
self.tokens.append((token_type, offset, data)) if not isinstance(self._tokens, list):
assert len(self._tokens) == 0, f"{type(self)}._tokens was tampered with."
self._tokens = []
self._tokens.append((token_type, offset, data))
class APPatchExtension(metaclass=AutoPatchExtensionRegister): class APPatchExtension(metaclass=AutoPatchExtensionRegister):
@ -371,10 +377,10 @@ class APPatchExtension(metaclass=AutoPatchExtensionRegister):
Patch extension functions must return the changed bytes. Patch extension functions must return the changed bytes.
""" """
game: str game: str
required_extensions: List[str] = [] required_extensions: ClassVar[Tuple[str, ...]] = ()
@staticmethod @staticmethod
def apply_bsdiff4(caller: APProcedurePatch, rom: bytes, patch: str): def apply_bsdiff4(caller: APProcedurePatch, rom: bytes, patch: str) -> bytes:
"""Applies the given bsdiff4 from the patch onto the current file.""" """Applies the given bsdiff4 from the patch onto the current file."""
return bsdiff4.patch(rom, caller.get_file(patch)) return bsdiff4.patch(rom, caller.get_file(patch))
@ -411,7 +417,7 @@ class APPatchExtension(metaclass=AutoPatchExtensionRegister):
return bytes(rom_data) return bytes(rom_data)
@staticmethod @staticmethod
def calc_snes_crc(caller: APProcedurePatch, rom: bytes): def calc_snes_crc(caller: APProcedurePatch, rom: bytes) -> bytes:
"""Calculates and applies a valid CRC for the SNES rom header.""" """Calculates and applies a valid CRC for the SNES rom header."""
rom_data = bytearray(rom) rom_data = bytearray(rom)
if len(rom) < 0x8000: if len(rom) < 0x8000: