from enum import Enum from io import BytesIO from os import makedirs from pathlib import Path from typing import Any, Generator, IO, List, Tuple, Union from dolreader import DolFile from fileutils import (write_ubyte, write_uint16, write_uint32) class InvalidGeckoCodeError(Exception): pass class GeckoCode(object): class Type(Enum): WRITE_8 = 0x00 WRITE_16 = 0x02 WRITE_32 = 0x04 WRITE_STR = 0x06 WRITE_SERIAL = 0x08 IF_EQ_32 = 0x20 IF_NEQ_32 = 0x22 IF_GT_32 = 0x24 IF_LT_32 = 0x26 IF_EQ_16 = 0x28 IF_NEQ_16 = 0x2A IF_GT_16 = 0x2C IF_LT_16 = 0x2E BASE_ADDR_LOAD = 0x40 BASE_ADDR_SET = 0x42 BASE_ADDR_STORE = 0x44 BASE_GET_NEXT = 0x46 PTR_ADDR_LOAD = 0x48 PTR_ADDR_SET = 0x4A PTR_ADDR_STORE = 0x4C PTR_GET_NEXT = 0x4E REPEAT_SET = 0x60 REPEAT_EXEC = 0x62 RETURN = 0x64 GOTO = 0x66 GOSUB = 0x68 GECKO_REG_SET = 0x80 GECKO_REG_LOAD = 0x82 GECKO_REG_STORE = 0x84 GECKO_REG_OPERATE_I = 0x86 GECKO_REG_OPERATE = 0x88 MEMCPY_1 = 0x8A MEMCPY_2 = 0x8C GECKO_IF_EQ_16 = 0xA0 GECKO_IF_NEQ_16 = 0xA2 GECKO_IF_GT_16 = 0xA4 GECKO_IF_LT_16 = 0xA6 COUNTER_IF_EQ_16 = 0xA8 COUNTER_IF_NEQ_16 = 0xAA COUNTER_IF_GT_16 = 0xAC COUNTER_IF_LT_16 = 0xAE ASM_EXECUTE = 0xC0 ASM_INSERT = 0xC2 ASM_INSERT_L = 0xC4 WRITE_BRANCH = 0xC6 SWITCH = 0xCC ADDR_RANGE_CHECK = 0xCE TERMINATOR = 0xE0 ENDIF = 0xE2 EXIT = 0xF0 ASM_INSERT_XOR = 0xF2 BRAINSLUG_SEARCH = 0xF6 class ArithmeticType(Enum): ADD = 0 MUL = 1 OR = 2 AND = 3 XOR = 4 SLW = 5 SRW = 6 ROL = 7 ASR = 8 FADDS = 9 FMULS = 10 @staticmethod def int_to_type(id: int) -> Type: id &= 0xFE if id == 0xF4: return GeckoCode.Type.ASM_INSERT_XOR elif id >= 0xF0: return GeckoCode.Type(id & 0xFE) else: return GeckoCode.Type(id & 0xEE) @staticmethod def type_to_int(ty: Type) -> int: return ty.value @staticmethod def is_ifblock(_type: Union[Type, "GeckoCode"]) -> bool: if isinstance(_type, GeckoCode): _type = _type.codetype return _type in { GeckoCode.Type.IF_EQ_32, GeckoCode.Type.IF_NEQ_32, GeckoCode.Type.IF_GT_32, GeckoCode.Type.IF_LT_32, GeckoCode.Type.IF_EQ_16, GeckoCode.Type.IF_NEQ_16, GeckoCode.Type.IF_GT_16, GeckoCode.Type.IF_LT_16, GeckoCode.Type.GECKO_IF_EQ_16, GeckoCode.Type.GECKO_IF_NEQ_16, GeckoCode.Type.GECKO_IF_GT_16, GeckoCode.Type.GECKO_IF_LT_16, GeckoCode.Type.COUNTER_IF_EQ_16, GeckoCode.Type.COUNTER_IF_NEQ_16, GeckoCode.Type.COUNTER_IF_GT_16, GeckoCode.Type.COUNTER_IF_LT_16, GeckoCode.Type.BRAINSLUG_SEARCH } @staticmethod def is_multiline(_type: Union[Type, "GeckoCode"]) -> bool: if isinstance(_type, GeckoCode): _type = _type.codetype return _type in { GeckoCode.Type.WRITE_STR, GeckoCode.Type.WRITE_SERIAL, GeckoCode.Type.ASM_EXECUTE, GeckoCode.Type.ASM_INSERT, GeckoCode.Type.ASM_INSERT_L, GeckoCode.Type.ASM_INSERT_XOR, GeckoCode.Type.BRAINSLUG_SEARCH } @staticmethod def can_preprocess(_type: Union[Type, "GeckoCode"]) -> bool: if isinstance(_type, GeckoCode): _type = _type.codetype return _type in { GeckoCode.Type.WRITE_8, GeckoCode.Type.WRITE_16, GeckoCode.Type.WRITE_32, GeckoCode.Type.WRITE_STR, GeckoCode.Type.WRITE_SERIAL, GeckoCode.Type.WRITE_BRANCH } @staticmethod def assertRegister(gr: int): assert 0 <= gr < 16, f"Only Gecko Registers 0-15 are allowed ({gr} is beyond range)" @staticmethod def typeof(code: "GeckoCode") -> Type: return code.codetype @staticmethod def bytes_to_geckocode(f: IO) -> "GeckoCode": metadata = f.read(4) address = 0x80000000 | (int.from_bytes( metadata, byteorder="big", signed=False) & 0x1FFFFFF) codetype = GeckoCode.int_to_type((int.from_bytes( metadata, "big", signed=False) >> 24) & 0xFE) isPointerType = (codetype & 0x10 != 0) if codetype == GeckoCode.Type.WRITE_8: info = f.read(4) value = int.from_bytes(info[3:], "big", signed=False) repeat = int.from_bytes(info[:2], "big", signed=False) return Write8(value, repeat, address, isPointerType) elif codetype == GeckoCode.Type.WRITE_16: info = f.read(4) value = int.from_bytes(info[2:], "big", signed=False) repeat = int.from_bytes(info[:2], "big", signed=False) return Write16(value, repeat, address, isPointerType) elif codetype == GeckoCode.Type.WRITE_32: info = f.read(4) value = int.from_bytes(info, "big", signed=False) return Write32(value, address, isPointerType) elif codetype == GeckoCode.Type.WRITE_STR: size = int.from_bytes(f.read(4), "big", signed=False) return WriteString(f.read(size), address, isPointerType) elif codetype == GeckoCode.Type.WRITE_SERIAL: info = f.read(12) value = int.from_bytes(info[:4], "big", signed=False) valueSize = int.from_bytes(info[4:5], "big", signed=False) >> 4 repeat = int.from_bytes(info[4:5], "big", signed=False) & 0xF addressInc = int.from_bytes(info[6:8], "big", signed=False) valueInc = int.from_bytes(info[8:], "big", signed=False) return WriteSerial(value, repeat, address, isPointerType, valueSize, addressInc, valueInc) elif codetype == GeckoCode.Type.IF_EQ_32: info = f.read(4) value = int.from_bytes(info, "big", signed=False) return IfEqual32(value, address, endif=(address & 1) == 1) elif codetype == GeckoCode.Type.IF_NEQ_32: info = f.read(4) value = int.from_bytes(info, "big", signed=False) return IfNotEqual32(value, address, endif=(address & 1) == 1) elif codetype == GeckoCode.Type.IF_GT_32: info = f.read(4) value = int.from_bytes(info, "big", signed=False) return IfGreaterThan32(value, address, endif=(address & 1) == 1) elif codetype == GeckoCode.Type.IF_LT_32: info = f.read(4) value = int.from_bytes(info, "big", signed=False) return IfLesserThan32(value, address, endif=(address & 1) == 1) elif codetype == GeckoCode.Type.IF_EQ_16: info = f.read(4) value = int.from_bytes(info, "big", signed=False) return IfEqual16(value, address, endif=(address & 1) == 1) elif codetype == GeckoCode.Type.IF_NEQ_16: info = f.read(4) value = int.from_bytes(info, "big", signed=False) return IfNotEqual16(value, address, endif=(address & 1) == 1) elif codetype == GeckoCode.Type.IF_GT_16: info = f.read(4) value = int.from_bytes(info, "big", signed=False) return IfGreaterThan16(value, address, endif=(address & 1) == 1) elif codetype == GeckoCode.Type.IF_LT_16: info = f.read(4) value = int.from_bytes(info, "big", signed=False) return IfLesserThan16(value, address, endif=(address & 1) == 1) elif codetype == GeckoCode.Type.BASE_ADDR_LOAD: info = f.read(4) value = int.from_bytes(info, "big", signed=False) flags = int.from_bytes(metadata, "big", signed=False) return BaseAddressLoad(value, flags & 0x01110000, flags & 0xF, isPointerType) elif codetype == GeckoCode.Type.BASE_ADDR_SET: info = f.read(4) value = int.from_bytes(info, "big", signed=False) flags = int.from_bytes(metadata, "big", signed=False) return BaseAddressSet(value, flags & 0x01110000, flags & 0xF, isPointerType) elif codetype == GeckoCode.Type.BASE_ADDR_STORE: info = f.read(4) value = int.from_bytes(info, "big", signed=False) flags = int.from_bytes(metadata, "big", signed=False) return BaseAddressStore(value, flags & 0x00110000, flags & 0xF, isPointerType) elif codetype == GeckoCode.Type.BASE_GET_NEXT: info = f.read(4) value = int.from_bytes(info, "big", signed=False) flags = int.from_bytes(metadata, "big", signed=False) return BaseAddressGetNext(value) elif codetype == GeckoCode.Type.PTR_ADDR_LOAD: info = f.read(4) value = int.from_bytes(info, "big", signed=False) flags = int.from_bytes(metadata, "big", signed=False) return PointerAddressLoad(value, flags & 0x01110000, flags & 0xF, isPointerType) elif codetype == GeckoCode.Type.PTR_ADDR_SET: info = f.read(4) value = int.from_bytes(info, "big", signed=False) flags = int.from_bytes(metadata, "big", signed=False) return PointerAddressSet(value, flags & 0x01110000, flags & 0xF, isPointerType) elif codetype == GeckoCode.Type.PTR_ADDR_STORE: info = f.read(4) value = int.from_bytes(info, "big", signed=False) flags = int.from_bytes(metadata, "big", signed=False) return PointerAddressStore(value, flags & 0x00110000, flags & 0xF, isPointerType) elif codetype == GeckoCode.Type.PTR_GET_NEXT: info = f.read(4) value = int.from_bytes(info, "big", signed=False) flags = int.from_bytes(metadata, "big", signed=False) return PointerAddressGetNext(value) elif codetype == GeckoCode.Type.REPEAT_SET: info = f.read(4) value = int.from_bytes(info, "big", signed=False) & 0xF repeat = int.from_bytes(metadata, "big", signed=False) & 0xFFFF return SetRepeat(repeat, value) elif codetype == GeckoCode.Type.REPEAT_EXEC: info = f.read(4) value = int.from_bytes(info, "big", signed=False) & 0xF return ExecuteRepeat(value) elif codetype == GeckoCode.Type.RETURN: info = f.read(4) value = int.from_bytes(info, "big", signed=False) & 0xF flags = (int.from_bytes(metadata, "big", signed=False) & 0x00300000) >> 20 return Return(value) elif codetype == GeckoCode.Type.GOTO: info = f.read(4) value = int.from_bytes(metadata, "big", signed=False) & 0xFFFF flags = (int.from_bytes(metadata, "big", signed=False) & 0x00300000) >> 20 return Goto(flags, value) elif codetype == GeckoCode.Type.GOSUB: info = f.read(4) value = int.from_bytes(metadata, "big", signed=False) & 0xFFFF flags = (int.from_bytes(metadata, "big", signed=False) & 0x00300000) >> 20 register = int.from_bytes(info, "big", signed=False) & 0xF return Gosub(flags, value, register) elif codetype == GeckoCode.Type.GECKO_REG_SET: info = f.read(4) value = int.from_bytes(info, "big", signed=False) flags = (int.from_bytes(metadata, "big", signed=False) & 0x00110000) >> 16 register = int.from_bytes(metadata, "big", signed=False) & 0xF return GeckoRegisterSet(value, flags, register, isPointerType) elif codetype == GeckoCode.Type.GECKO_REG_LOAD: info = f.read(4) value = int.from_bytes(info, "big", signed=False) flags = (int.from_bytes(metadata, "big", signed=False) & 0x00310000) >> 16 register = int.from_bytes(metadata, "big", signed=False) & 0xF return GeckoRegisterLoad(value, flags, register, isPointerType) elif codetype == GeckoCode.Type.GECKO_REG_STORE: info = f.read(4) value = int.from_bytes(info, "big", signed=False) flags = (int.from_bytes(metadata, "big", signed=False) & 0x00310000) >> 16 register = int.from_bytes(metadata, "big", signed=False) & 0xF repeat = (int.from_bytes(metadata, "big", signed=False) & 0xFFF0) >> 4 return GeckoRegisterStore(value, repeat, flags, register, isPointerType) elif codetype == GeckoCode.Type.GECKO_REG_OPERATE_I: info = f.read(4) value = int.from_bytes(info, "big", signed=False) flags = (int.from_bytes(metadata, "big", signed=False) & 0x00030000) >> 16 register = int.from_bytes(metadata, "big", signed=False) & 0xF opType = GeckoCode.ArithmeticType( (int.from_bytes(metadata, "big", signed=False) & 0x00F00000) >> 18) return GeckoRegisterOperateI(value, opType, flags, register) elif codetype == GeckoCode.Type.GECKO_REG_OPERATE: info = f.read(4) value = int.from_bytes(info, "big", signed=False) & 0xF flags = (int.from_bytes(metadata, "big", signed=False) & 0x00030000) >> 16 register = int.from_bytes(metadata, "big", signed=False) & 0xF opType = GeckoCode.ArithmeticType( (int.from_bytes(metadata, "big", signed=False) & 0x00F00000) >> 18) return GeckoRegisterOperate(value, opType, flags, register) elif codetype == GeckoCode.Type.MEMCPY_1: info = f.read(4) value = int.from_bytes(info, "big", signed=False) size = (int.from_bytes(metadata, "big", signed=False) & 0x00FFFF00) >> 8 register = (int.from_bytes( metadata, "big", signed=False) & 0xF0) >> 4 otherRegister = int.from_bytes(metadata, "big", signed=False) & 0xF return MemoryCopyTo(value, size, otherRegister, register, isPointerType) elif codetype == GeckoCode.Type.MEMCPY_2: info = f.read(4) value = int.from_bytes(info, "big", signed=False) size = (int.from_bytes(metadata, "big", signed=False) & 0x00FFFF00) >> 8 register = (int.from_bytes( metadata, "big", signed=False) & 0xF0) >> 4 otherRegister = int.from_bytes(metadata, "big", signed=False) & 0xF return MemoryCopyFrom(value, size, otherRegister, register, isPointerType) elif codetype == GeckoCode.Type.GECKO_IF_EQ_16: info = f.read(4) register = (int.from_bytes(info, "big", signed=False) & 0x0F000000) >> 24 otherRegister = (int.from_bytes( info, "big", signed=False) & 0xF0000000) >> 28 mask = int.from_bytes(info, "big", signed=False) & 0xFFFF return GeckoIfEqual16(address, register, otherRegister, isPointerType, (address & 1) == 1, mask) elif codetype == GeckoCode.Type.GECKO_IF_NEQ_16: info = f.read(4) register = (int.from_bytes(info, "big", signed=False) & 0x0F000000) >> 24 otherRegister = (int.from_bytes( info, "big", signed=False) & 0xF0000000) >> 28 mask = int.from_bytes(info, "big", signed=False) & 0xFFFF return GeckoIfNotEqual16(address, register, otherRegister, isPointerType, (address & 1) == 1, mask) elif codetype == GeckoCode.Type.GECKO_IF_GT_16: info = f.read(4) register = (int.from_bytes(info, "big", signed=False) & 0x0F000000) >> 24 otherRegister = (int.from_bytes( info, "big", signed=False) & 0xF0000000) >> 28 mask = int.from_bytes(info, "big", signed=False) & 0xFFFF return GeckoIfGreaterThan16(address, register, otherRegister, isPointerType, (address & 1) == 1, mask) elif codetype == GeckoCode.Type.GECKO_IF_LT_16: info = f.read(4) register = (int.from_bytes(info, "big", signed=False) & 0x0F000000) >> 24 otherRegister = (int.from_bytes( info, "big", signed=False) & 0xF0000000) >> 28 mask = int.from_bytes(info, "big", signed=False) & 0xFFFF return GeckoIfLesserThan16(address, register, otherRegister, isPointerType, (address & 1) == 1, mask) elif codetype == GeckoCode.Type.COUNTER_IF_EQ_16: info = f.read(4) counter = (int.from_bytes(metadata, "big", signed=False) & 0xFFFF0) >> 4 flags = int.from_bytes(metadata, "big", signed=False) & 9 mask = (int.from_bytes(info, "big", signed=False) & 0xFFFF0000) >> 16 value = int.from_bytes(info, "big", signed=False) & 0xFFFF return CounterIfEqual16(value, mask, flags, counter) elif codetype == GeckoCode.Type.COUNTER_IF_NEQ_16: info = f.read(4) counter = (int.from_bytes(metadata, "big", signed=False) & 0xFFFF0) >> 4 flags = int.from_bytes(metadata, "big", signed=False) & 9 mask = (int.from_bytes(info, "big", signed=False) & 0xFFFF0000) >> 16 value = int.from_bytes(info, "big", signed=False) & 0xFFFF return CounterIfNotEqual16(value, mask, flags, counter) elif codetype == GeckoCode.Type.COUNTER_IF_GT_16: info = f.read(4) counter = (int.from_bytes(metadata, "big", signed=False) & 0xFFFF0) >> 4 flags = int.from_bytes(metadata, "big", signed=False) & 9 mask = (int.from_bytes(info, "big", signed=False) & 0xFFFF0000) >> 16 value = int.from_bytes(info, "big", signed=False) & 0xFFFF return CounterIfGreaterThan16(value, mask, flags, counter) elif codetype == GeckoCode.Type.COUNTER_IF_LT_16: info = f.read(4) counter = (int.from_bytes(metadata, "big", signed=False) & 0xFFFF0) >> 4 flags = int.from_bytes(metadata, "big", signed=False) & 9 mask = (int.from_bytes(info, "big", signed=False) & 0xFFFF0000) >> 16 value = int.from_bytes(info, "big", signed=False) & 0xFFFF return CounterIfLesserThan16(value, mask, flags, counter) elif codetype == GeckoCode.Type.ASM_EXECUTE: info = f.read(4) size = int.from_bytes(info, "big", signed=False) return AsmExecute(f.read(size << 3)) elif codetype == GeckoCode.Type.ASM_INSERT: info = f.read(4) size = int.from_bytes(info, "big", signed=False) return AsmInsert(f.read(size << 3), address, isPointerType) elif codetype == GeckoCode.Type.ASM_INSERT_L: info = f.read(4) size = int.from_bytes(info, "big", signed=False) return AsmInsertLink(f.read(size << 3), address, isPointerType) elif codetype == GeckoCode.Type.WRITE_BRANCH: info = f.read(4) dest = int.from_bytes(info, "big", signed=False) return WriteBranch(dest, address, isPointerType) elif codetype == GeckoCode.Type.SWITCH: return Switch() elif codetype == GeckoCode.Type.ADDR_RANGE_CHECK: info = f.read(4) value = int.from_bytes(info, "big", signed=False) endif = int.from_bytes(metadata, "big", signed=False) & 0x1 return AddressRangeCheck(value, isPointerType, endif) elif codetype == GeckoCode.Type.TERMINATOR: info = f.read(4) value = int.from_bytes(info, "big", signed=False) return Terminator(value) elif codetype == GeckoCode.Type.ENDIF: info = f.read(4) value = int.from_bytes(info, "big", signed=False) inverse = (int.from_bytes(metadata, "big", signed=False) & 0x00F00000) >> 24 numEndifs = int.from_bytes(metadata, "big", signed=False) & 0xFF return Endif(value, inverse, numEndifs) elif codetype == GeckoCode.Type.EXIT: return Exit() elif codetype == GeckoCode.Type.ASM_INSERT_XOR: info = f.read(4) size = int.from_bytes(info, "big", signed=False) & 0x000000FF xor = int.from_bytes(info, "big", signed=False) & 0x00FFFF00 num = int.from_bytes(info, "big", signed=False) & 0xFF000000 pointer = codetype == 0xF4 return AsmInsertXOR(f.read(size << 3), address, pointer, xor, num) elif codetype == GeckoCode.Type.BRAINSLUG_SEARCH: info = f.read(4) value = int.from_bytes(info, "big", signed=False) size = int.from_bytes(metadata, "big", signed=False) & 0x000000FF return BrainslugSearch(f.read(size << 3), address, [(value & 0xFFFF0000) >> 16, value & 0xFFFF]) def __init__(self): raise InvalidGeckoCodeError( f"Cannot instantiate abstract type {self.__class__.__name__}") def __repr__(self) -> str: return f"{self.__class__.__name__}({self.__dict__})" def __str__(self) -> str: return self.__class__.__name__ def __len__(self) -> int: return 0 def __iter__(self): self._iterpos = 0 return self def __next__(self): try: self._iterpos += 1 return self[self._iterpos-1] except IndexError: raise StopIteration def __getitem__(self, index: int) -> Any: raise IndexError def __setitem__(self, index: int, value: Any): raise IndexError @property def children(self) -> List["GeckoCode"]: return [] @property def codetype(self) -> Type: return None @property def value(self) -> Union[int, bytes]: return None @value.setter def value(self, value: Union[int, bytes]): pass def add_child(self, child: "GeckoCode"): pass def remove_child(self, child: "GeckoCode"): pass def virtual_length(self) -> int: return 0 def populate_from_bytes(self, f: IO): pass def apply(self, dol: DolFile) -> bool: return False class Write8(GeckoCode): def __init__(self, value: Union[int, bytes], repeat: int = 0, address: int = 0, isPointer: bool = False): self.value = value self.address = address self.repeat = repeat self.isPointer = isPointer def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" if self.repeat > 0: return f"({intType:02X}) Write byte 0x{self.value:02X} to (0x{self.address:08X} + the {addrstr}) {self.repeat + 1} times consecutively" else: return f"({intType:02X}) Write byte 0x{self.value:02X} to 0x{self.address:08X} + the {addrstr}" def __len__(self) -> int: return 8 def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") return self.value def __setitem__(self, index: int, value: Union[int, bytes]): if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.WRITE_8 @property def value(self) -> int: return self._value & 0xFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFF def virtual_length(self) -> int: return 1 def apply(self, dol: DolFile) -> bool: addr = self.address | 0x80000000 if dol.is_mapped(addr): dol.seek(addr) counter = self.repeat while counter + 1 > 0: dol.write(self.value.to_bytes(1, "big", signed=False)) counter -= 1 return True return False class Write16(GeckoCode): def __init__(self, value: Union[int, bytes], repeat: int = 0, address: int = 0, isPointer: bool = False): self.value = value self.address = address self.repeat = repeat self.isPointer = isPointer def __len__(self) -> int: return 8 def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" if self.repeat > 0: return f"({intType:02X}) Write short 0x{self.value:04X} to (0x{self.address:08X} + the {addrstr}) {self.repeat + 1} times consecutively" else: return f"({intType:02X}) Write short 0x{self.value:04X} to 0x{self.address:08X} + the {addrstr}" def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") return self.value def __setitem__(self, index: int, value: Union[int, bytes]): if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.WRITE_16 @property def value(self) -> int: return self._value & 0xFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFF def virtual_length(self) -> int: return 1 def apply(self, dol: DolFile) -> bool: addr = self.address | 0x80000000 if dol.is_mapped(addr): dol.seek(addr) counter = self.repeat while counter + 1 > 0: dol.write(self.value.to_bytes(2, "big", signed=False)) counter -= 1 return True return False class Write32(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = False): self.value = value self.address = address self.isPointer = isPointer def __len__(self) -> int: return 8 def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" return f"({intType:02X}) Write word 0x{self.value:08X} to 0x{self.address:08X} + the {addrstr}" def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") return self.value def __setitem__(self, index: int, value: Union[int, bytes]): if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.WRITE_32 @property def value(self) -> int: return self._value & 0xFFFFFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 def apply(self, dol: DolFile) -> bool: addr = self.address | 0x80000000 if dol.is_mapped(addr): dol.seek(addr) dol.write(self.value.to_bytes(4, "big", signed=False)) return True return False class WriteString(GeckoCode): def __init__(self, value: bytes, address: int = 0, isPointer: bool = False): self.value = value self.address = address self.isPointer = isPointer def __len__(self) -> int: return 8 + len(self.value) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" return f"({intType:02X}) Write {len(self) - 8} bytes to 0x{self.address:08X} + the {addrstr}" def __getitem__(self, index: int) -> bytes: return self.value[index] def __setitem__(self, index: int, value: bytes): if isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value[index] = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.WRITE_STR @property def value(self) -> bytes: return self._value @value.setter def value(self, value: bytes): self._value = value def virtual_length(self) -> int: return ((len(self) + 7) & -0x8) >> 3 def apply(self, dol: DolFile) -> bool: addr = self.address | 0x80000000 if dol.is_mapped(addr): dol.seek(addr) dol.write(self.value) return True return False class WriteSerial(GeckoCode): def __init__(self, value: Union[int, bytes], repeat: int = 0, address: int = 0, isPointer: bool = False, valueSize: int = 2, addrInc: int = 4, valueInc: int = 0): self.value = value self.valueInc = valueInc self.valueSize = valueSize self.address = address self.addressInc = addrInc self.repeat = repeat self.isPointer = isPointer def __len__(self) -> int: return 16 def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" valueType = ("byte", "short", "word")[self.valueSize] if self.repeat > 0: mapping = f"incrementing the value by {self.valueInc} and the address by {self.addressInc} each iteration" return f"({intType:02X}) Write {valueType} 0x{self.value:08X} to (0x{self.address:08X} + the {addrstr}) {self.repeat + 1} times consecutively, {mapping}" else: return f"({intType:02X}) Write {valueType} 0x{self.value:08X} to 0x{self.address:08X} + the {addrstr})" def __getitem__(self, index: int) -> Tuple[int, int]: if index >= self.repeat: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif index < 0: index += self.repeat return (self.address + self.addressInc*index, self.value + self.valueInc*index) def __setitem__(self, index: int, value: Any): if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.WRITE_SERIAL @property def value(self) -> int: return self._value @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 2 def apply(self, dol: DolFile) -> bool: addr = self.address | 0x80000000 if dol.is_mapped(addr): for addr, value in self: dol.seek(addr) dol.write(value) return True return False class IfEqual32(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = False, endif: bool = False): self.value = value self.address = address self.endif = endif self.isPointer = isPointer self._children = [] def __len__(self) -> int: return 8 + sum([len(c) for c in self]) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" endif = "(Apply Endif) " if self.endif else "" return f"({intType:02X}) {endif}If the word at address (0x{self.address:08X} + the {addrstr}) is equal to 0x{self.value:08X}, run the encapsulated codes" def __getitem__(self, index: int) -> GeckoCode: return self._children[index] def __setitem__(self, index: int, value: GeckoCode): if not isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} as a child of {self.__class__.__name__}") self._children[index] = value @property def children(self) -> List["GeckoCode"]: return self._children @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.IF_EQ_32 @property def value(self) -> int: return self._value & 0xFFFFFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def add_child(self, child: "GeckoCode"): self._children.append(child) def remove_child(self, child: "GeckoCode"): self._children.remove(child) def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): code = GeckoCode.bytes_to_geckocode(f) while code != Terminator: self.add_child(code) code = GeckoCode.bytes_to_geckocode(f) self.add_child(code) class IfNotEqual32(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = False, endif: bool = False): self.value = value self.address = address self.endif = endif self.isPointer = isPointer self._children = [] def __len__(self) -> int: return 8 + sum([len(c) for c in self]) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" endif = "(Apply Endif) " if self.endif else "" return f"({intType:02X}) {endif}If the word at address (0x{self.address:08X} + the {addrstr}) is not equal to 0x{self.value:08X}, run the encapsulated codes" def __getitem__(self, index: int) -> GeckoCode: return self._children[index] def __setitem__(self, index: int, value: GeckoCode): if not isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} as a child of {self.__class__.__name__}") self._children[index] = value @property def children(self) -> List["GeckoCode"]: return self._children @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.IF_NEQ_32 @property def value(self) -> int: return self._value & 0xFFFFFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def add_child(self, child: "GeckoCode"): self._children.append(child) def remove_child(self, child: "GeckoCode"): self._children.remove(child) def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): code = GeckoCode.bytes_to_geckocode(f) while code != Terminator: self.add_child(code) code = GeckoCode.bytes_to_geckocode(f) self.add_child(code) class IfGreaterThan32(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = False, endif: bool = False): self.value = value self.address = address self.endif = endif self.isPointer = isPointer self._children = [] def __len__(self) -> int: return 8 + sum([len(c) for c in self]) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" endif = "(Apply Endif) " if self.endif else "" return f"({intType:02X}) {endif}If the word at address (0x{self.address:08X} + the {addrstr}) is greater than 0x{self.value:08X}, run the encapsulated codes" def __getitem__(self, index: int) -> GeckoCode: return self._children[index] def __setitem__(self, index: int, value: GeckoCode): if not isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} as a child of {self.__class__.__name__}") self._children[index] = value @property def children(self) -> List["GeckoCode"]: return self._children @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.IF_GT_32 @property def value(self) -> int: return self._value & 0xFFFFFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def add_child(self, child: "GeckoCode"): self._children.append(child) def remove_child(self, child: "GeckoCode"): self._children.remove(child) def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): code = GeckoCode.bytes_to_geckocode(f) while code != Terminator: self.add_child(code) code = GeckoCode.bytes_to_geckocode(f) self.add_child(code) class IfLesserThan32(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = False, endif: bool = False): self.value = value self.address = address self.endif = endif self.isPointer = isPointer self._children = [] def __len__(self) -> int: return 8 + sum([len(c) for c in self]) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" endif = "(Apply Endif) " if self.endif else "" return f"({intType:02X}) {endif}If the word at address (0x{self.address:08X} + the {addrstr}) is lesser than 0x{self.value:08X}, run the encapsulated codes" def __getitem__(self, index: int) -> GeckoCode: return self._children[index] def __setitem__(self, index: int, value: GeckoCode): if not isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} as a child of {self.__class__.__name__}") self._children[index] = value @property def children(self) -> List["GeckoCode"]: return self._children @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.IF_LT_32 @property def value(self) -> int: return self._value & 0xFFFFFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def add_child(self, child: "GeckoCode"): self._children.append(child) def remove_child(self, child: "GeckoCode"): self._children.remove(child) def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): code = GeckoCode.bytes_to_geckocode(f) while code != Terminator: self.add_child(code) code = GeckoCode.bytes_to_geckocode(f) self.add_child(code) class IfEqual16(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = False, endif: bool = False, mask: int = 0xFFFF): self.value = value self.address = address self.endif = endif self.mask = mask self.isPointer = isPointer self._children = [] def __len__(self) -> int: return 8 + sum([len(c) for c in self]) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" endif = "(Apply Endif) " if self.endif else "" return f"({intType:02X}) {endif}If the short at address (0x{self.address:08X} + the {addrstr}) & ~0x{self.mask:04X} is equal to 0x{self.value:04X}, run the encapsulated codes" def __getitem__(self, index: int) -> GeckoCode: return self._children[index] def __setitem__(self, index: int, value: GeckoCode): if not isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} as a child of {self.__class__.__name__}") self._children[index] = value @property def children(self) -> List["GeckoCode"]: return self._children @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.IF_EQ_16 @property def value(self) -> int: return self._value & 0xFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFF def add_child(self, child: "GeckoCode"): self._children.append(child) def remove_child(self, child: "GeckoCode"): self._children.remove(child) def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): code = GeckoCode.bytes_to_geckocode(f) while code != Terminator: self.add_child(code) code = GeckoCode.bytes_to_geckocode(f) self.add_child(code) class IfNotEqual16(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = False, endif: bool = False, mask: int = 0xFFFF): self.value = value self.address = address self.endif = endif self.mask = mask self.isPointer = isPointer self._children = [] def __len__(self) -> int: return 8 + sum([len(c) for c in self]) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" endif = "(Apply Endif) " if self.endif else "" return f"({intType:02X}) {endif}If the short at address (0x{self.address:08X} + the {addrstr}) & ~0x{self.mask:04X} is not equal to 0x{self.value:04X}, run the encapsulated codes" def __getitem__(self, index: int) -> GeckoCode: return self._children[index] def __setitem__(self, index: int, value: GeckoCode): if not isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} as a child of {self.__class__.__name__}") self._children[index] = value @property def children(self) -> List["GeckoCode"]: return self._children @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.IF_NEQ_16 @property def value(self) -> int: return self._value & 0xFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFF def add_child(self, child: "GeckoCode"): self._children.append(child) def remove_child(self, child: "GeckoCode"): self._children.remove(child) def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): code = GeckoCode.bytes_to_geckocode(f) while code != Terminator: self.add_child(code) code = GeckoCode.bytes_to_geckocode(f) self.add_child(code) class IfGreaterThan16(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = False, endif: bool = False, mask: int = 0xFFFF): self.value = value self.address = address self.endif = endif self.mask = mask self.isPointer = isPointer self._children = [] def __len__(self) -> int: return 8 + sum([len(c) for c in self]) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" endif = "(Apply Endif) " if self.endif else "" return f"({intType:02X}) {endif}If the short at address (0x{self.address:08X} + the {addrstr}) & ~0x{self.mask:04X} is greater than 0x{self.value:04X}, run the encapsulated codes" def __getitem__(self, index: int) -> GeckoCode: return self._children[index] def __setitem__(self, index: int, value: GeckoCode): if not isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} as a child of {self.__class__.__name__}") self._children[index] = value @property def children(self) -> List["GeckoCode"]: return self._children @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.IF_GT_16 @property def value(self) -> int: return self._value & 0xFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFF def add_child(self, child: "GeckoCode"): self._children.append(child) def remove_child(self, child: "GeckoCode"): self._children.remove(child) def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): code = GeckoCode.bytes_to_geckocode(f) while code != Terminator: self.add_child(code) code = GeckoCode.bytes_to_geckocode(f) self.add_child(code) class IfLesserThan16(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = False, endif: bool = False, mask: int = 0xFFFF): self.value = value self.address = address self.endif = endif self.mask = mask self.isPointer = isPointer self._children = [] def __len__(self) -> int: return 8 + sum([len(c) for c in self]) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" endif = "(Apply Endif) " if self.endif else "" return f"({intType:02X}) {endif}If the short at address (0x{self.address:08X} + the {addrstr}) & ~0x{self.mask:04X} is lesser than 0x{self.value:04X}, run the encapsulated codes" def __getitem__(self, index: int) -> GeckoCode: return self._children[index] def __setitem__(self, index: int, value: GeckoCode): if not isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} as a child of {self.__class__.__name__}") self._children[index] = value @property def children(self) -> List["GeckoCode"]: return self._children @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.IF_LT_16 @property def value(self) -> int: return self._value & 0xFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFF def add_child(self, child: "GeckoCode"): self._children.append(child) def remove_child(self, child: "GeckoCode"): self._children.remove(child) def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): code = GeckoCode.bytes_to_geckocode(f) while code != Terminator: self.add_child(code) code = GeckoCode.bytes_to_geckocode(f) self.add_child(code) class BaseAddressLoad(GeckoCode): def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: bool = False): GeckoCode.assertRegister(register) self.value = value self.flags = flags self._register = register self.isPointer = isPointer def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags if flags == 0x000: return f"({intType:02X}) Set the base address to the value at address [0x{self.value}]" elif flags == 0x001: return f"({intType:02X}) Set the base address to the value at address [gr{self._register} + 0x{self.value}]" elif flags == 0x010: return f"({intType:02X}) Set the base address to the value at address [{addrstr} + 0x{self.value}]" elif flags == 0x011: return f"({intType:02X}) Set the base address to the value at address [{addrstr} + gr{self._register} + 0x{self.value}]" elif flags == 0x100: return f"({intType:02X}) Add the value at address [0x{self.value}] to the base address" elif flags == 0x101: return f"({intType:02X}) Add the value at address [gr{self._register} + 0x{self.value}] to the base address" elif flags == 0x110: return f"({intType:02X}) Add the value at address [{addrstr} + 0x{self.value}] to the base address" elif flags == 0x111: return f"({intType:02X}) Add the value at address [{addrstr} + gr{self._register} + 0x{self.value}] to the base address" def __len__(self) -> int: return 8 def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") return self.value def __setitem__(self, index: int, value: Union[int, bytes]): if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.BASE_ADDR_LOAD @property def value(self) -> int: return self._value & 0xFFFFFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 class BaseAddressSet(GeckoCode): def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: bool = False): GeckoCode.assertRegister(register) self.value = value self.flags = flags self._register = register self.isPointer = isPointer def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags if flags == 0x000: return f"({intType:02X}) Set the base address to the value 0x{self.value}" elif flags == 0x001: return f"({intType:02X}) Set the base address to the value (gr{self._register} + 0x{self.value})" elif flags == 0x010: return f"({intType:02X}) Set the base address to the value ({addrstr} + 0x{self.value})" elif flags == 0x011: return f"({intType:02X}) Set the base address to the value ({addrstr} + gr{self._register} + 0x{self.value})" elif flags == 0x100: return f"({intType:02X}) Add the value 0x{self.value} to the base address" elif flags == 0x101: return f"({intType:02X}) Add the value (gr{self._register} + 0x{self.value}) to the base address" elif flags == 0x110: return f"({intType:02X}) Add the value ({addrstr} + 0x{self.value}) to the base address" elif flags == 0x111: return f"({intType:02X}) Add the value ({addrstr} + gr{self._register}) + 0x{self.value} to the base address" return f"({intType:02X}) Invalid flag {flags}" def __len__(self) -> int: return 8 def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") return self.value def __setitem__(self, index: int, value: Union[int, bytes]): if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.BASE_ADDR_SET @property def value(self) -> int: return self._value & 0xFFFFFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 class BaseAddressStore(GeckoCode): def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: bool = False): GeckoCode.assertRegister(register) self.value = value self.flags = flags self._register = register self.isPointer = isPointer def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags if flags == 0x000: return f"({intType:02X}) Store the base address at address [0x{self.value}]" elif flags == 0x001: return f"({intType:02X}) Store the base address at address [gr{self._register} + 0x{self.value}]" elif flags == 0x010: return f"({intType:02X}) Store the base address at address [{addrstr} + 0x{self.value}]" elif flags == 0x011: return f"({intType:02X}) Store the base address at address [{addrstr} + gr{self._register} + 0x{self.value}]" return f"({intType:02X}) Invalid flag {flags}" def __len__(self) -> int: return 8 def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") return self.value def __setitem__(self, index: int, value: Union[int, bytes]): if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.BASE_ADDR_STORE @property def value(self) -> int: return self._value & 0xFFFFFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 class BaseAddressGetNext(GeckoCode): def __init__(self, value: int): self.value = value def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) return f"({intType:02X}) Set the base address to be the next Gecko Code's address + {self.value}" def __len__(self) -> int: return 8 def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") return self.value def __setitem__(self, index: int, value: Union[int, bytes]): if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.BASE_GET_NEXT @property def value(self) -> int: return self.value & 0xFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self.value = value & 0xFFFF def virtual_length(self) -> int: return 1 class PointerAddressLoad(GeckoCode): def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: bool = False): GeckoCode.assertRegister(register) self.value = value self.flags = flags self._register = register self.isPointer = isPointer def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags if flags == 0x000: return f"({intType:02X}) Set the pointer address to the value at address [0x{self.value}]" elif flags == 0x001: return f"({intType:02X}) Set the pointer address to the value at address [gr{self._register} + 0x{self.value}]" elif flags == 0x010: return f"({intType:02X}) Set the pointer address to the value at address [{addrstr} + 0x{self.value}]" elif flags == 0x011: return f"({intType:02X}) Set the pointer address to the value at address [{addrstr} + gr{self._register} + 0x{self.value}]" elif flags == 0x100: return f"({intType:02X}) Add the value at address [0x{self.value}] to the pointer address" elif flags == 0x101: return f"({intType:02X}) Add the value at address [gr{self._register} + 0x{self.value}] to the pointer address" elif flags == 0x110: return f"({intType:02X}) Add the value at address [{addrstr} + 0x{self.value}] to the pointer address" elif flags == 0x111: return f"({intType:02X}) Add the value at address [{addrstr} + gr{self._register} + 0x{self.value}] to the pointer address" def __len__(self) -> int: return 8 def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") return self.value def __setitem__(self, index: int, value: Union[int, bytes]): if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.PTR_ADDR_LOAD @property def value(self) -> int: return self._value & 0xFFFFFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 class PointerAddressSet(GeckoCode): def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: bool = False): GeckoCode.assertRegister(register) self.value = value self.flags = flags self._register = register self.isPointer = isPointer def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags if flags == 0x000: return f"({intType:02X}) Set the pointer address to the value 0x{self.value}" elif flags == 0x001: return f"({intType:02X}) Set the pointer address to the value (gr{self._register} + 0x{self.value})" elif flags == 0x010: return f"({intType:02X}) Set the pointer address to the value ({addrstr} + 0x{self.value})" elif flags == 0x011: return f"({intType:02X}) Set the pointer address to the value ({addrstr} + gr{self._register} + 0x{self.value})" elif flags == 0x100: return f"({intType:02X}) Add the value 0x{self.value} to the pointer address" elif flags == 0x101: return f"({intType:02X}) Add the value (gr{self._register} + 0x{self.value}) to the pointer address" elif flags == 0x110: return f"({intType:02X}) Add the value ({addrstr} + 0x{self.value}) to the pointer address" elif flags == 0x111: return f"({intType:02X}) Add the value ({addrstr} + gr{self._register}) + 0x{self.value} to the pointer address" return f"({intType:02X}) Invalid flag {flags}" def __len__(self) -> int: return 8 def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") return self.value def __setitem__(self, index: int, value: Union[int, bytes]): if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.PTR_ADDR_SET @property def value(self) -> int: return self._value & 0xFFFFFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 class PointerAddressStore(GeckoCode): def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: bool = False): GeckoCode.assertRegister(register) self.value = value self.flags = flags self._register = register self.isPointer = isPointer def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags if flags == 0x000: return f"({intType:02X}) Store the pointer address at address [0x{self.value}]" elif flags == 0x001: return f"({intType:02X}) Store the pointer address at address [gr{self._register} + 0x{self.value}]" elif flags == 0x010: return f"({intType:02X}) Store the pointer address at address [{addrstr} + 0x{self.value}]" elif flags == 0x011: return f"({intType:02X}) Store the pointer address at address [{addrstr} + gr{self._register} + 0x{self.value}]" return f"({intType:02X}) Invalid flag {flags}" def __len__(self) -> int: return 8 def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") return self.value def __setitem__(self, index: int, value: Union[int, bytes]): if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.PTR_ADDR_STORE @property def value(self) -> int: return self._value & 0xFFFFFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 class PointerAddressGetNext(GeckoCode): def __init__(self, value: int): self.value = value def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) return f"({intType:02X}) Set the base address to be the next Gecko Code's address + {self.value}" def __len__(self) -> int: return 8 def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") return self.value def __setitem__(self, index: int, value: Union[int, bytes]): if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.PTR_GET_NEXT @property def value(self) -> int: return self.value & 0xFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self.value = value & 0xFFFF def virtual_length(self) -> int: return 1 class SetRepeat(GeckoCode): def __init__(self, repeat: int = 0, b: int = 0): self.repeat = repeat self.b = b def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) return f"({intType:02X}) Store next code address and number of times to repeat in b{self.b}" def __len__(self) -> int: return 8 @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.REPEAT_SET def virtual_length(self) -> int: return 1 class ExecuteRepeat(GeckoCode): def __init__(self, b: int = 0): self.b = b def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) return f"({intType:02X}) If NNNN stored in b{self.b} is > 0, it is decreased by 1 and the code handler jumps to the next code address stored in b{self.b}" def __len__(self) -> int: return 8 @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.REPEAT_EXEC def virtual_length(self) -> int: return 1 class Return(GeckoCode): def __init__(self, flags: int = 0, b: int = 0): self.b = b self.flags = flags def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) if self.flags == 0: return f"({intType:02X}) If the code execution status is true, jump to the next code address stored in b{self.b} (NNNN in bP is not touched)" elif self.flags == 1: return f"({intType:02X}) If the code execution status is false, jump to the next code address stored in b{self.b} (NNNN in bP is not touched)" elif self.flags == 2: return f"({intType:02X}) Jump to the next code address stored in b{self.b} (NNNN in bP is not touched)" def __len__(self) -> int: return 8 @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.RETURN def virtual_length(self) -> int: return 1 class Goto(GeckoCode): def __init__(self, flags: int = 0, lineOffset: int = 0): self.flags = flags self.offset = lineOffset def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) if self.flags == 0: return f"({intType:02X}) If the code execution status is true, jump to (next line of code + {self.offset} lines)" elif self.flags == 1: return f"({intType:02X}) If the code execution status is false, jump to (next line of code + {self.offset} lines)" elif self.flags == 2: return f"({intType:02X}) Jump to (next line of code + {self.offset} lines)" def __len__(self) -> int: return 8 @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.GOTO def virtual_length(self) -> int: return 1 class Gosub(GeckoCode): def __init__(self, flags: int = 0, lineOffset: int = 0, register: int = 0): self.flags = flags self.offset = lineOffset self.register = register def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) if self.flags == 0: return f"({intType:02X}) If the code execution status is true, store the next code address in b{self.register} and jump to (next line of code + {self.offset} lines)" elif self.flags == 1: return f"({intType:02X}) If the code execution status is false, store the next code address in b{self.register} and jump to (next line of code + {self.offset} lines)" elif self.flags == 2: return f"({intType:02X}) Store the next code address in b{self.register} and jump to (next line of code + {self.offset} lines)" def __len__(self) -> int: return 8 @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.GOSUB def virtual_length(self) -> int: return 1 class GeckoRegisterSet(GeckoCode): def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: bool = False): GeckoCode.assertRegister(register) self.value = value self.flags = flags self._register = register self.isPointer = isPointer def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags if flags == 0x00: return f"({intType:02X}) Set Gecko Register {self._register} to the value 0x{self.value}" elif flags == 0x01: return f"({intType:02X}) Set Gecko Register {self._register} to the value (0x{self.value} + the {addrstr})" elif flags == 0x10: return f"({intType:02X}) Add the value 0x{self.value} to Gecko Register {self._register}" elif flags == 0x11: return f"({intType:02X}) Add the value (0x{self.value} + the {addrstr}) to Gecko Register {self._register}" return f"({intType:02X}) Invalid flag {flags}" def __len__(self) -> int: return 8 def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") return self.value def __setitem__(self, index: int, value: Union[int, bytes]): if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.GECKO_REG_SET @property def value(self) -> int: return self._value & 0xFFFFFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 class GeckoRegisterLoad(GeckoCode): def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: bool = False): GeckoCode.assertRegister(register) self.value = value self.flags = flags self._register = register self.isPointer = isPointer def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags if flags == 0x00: return f"({intType:02X}) Set Gecko Register {self._register} to the byte at address 0x{self.value}" elif flags == 0x10: return f"({intType:02X}) Set Gecko Register {self._register} to the short at address 0x{self.value}" elif flags == 0x20: return f"({intType:02X}) Set Gecko Register {self._register} to the word at address 0x{self.value}" elif flags == 0x01: return f"({intType:02X}) Set Gecko Register {self._register} to the byte at address (0x{self.value} + the {addrstr})" elif flags == 0x11: return f"({intType:02X}) Set Gecko Register {self._register} to the short at address (0x{self.value} + the {addrstr})" elif flags == 0x21: return f"({intType:02X}) Set Gecko Register {self._register} to the word at address (0x{self.value} + the {addrstr})" return f"({intType:02X}) Invalid flag {flags}" def __len__(self) -> int: return 8 def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") return self.value def __setitem__(self, index: int, value: Union[int, bytes]): if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.GECKO_REG_LOAD @property def value(self) -> int: return self._value & 0xFFFFFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 class GeckoRegisterStore(GeckoCode): def __init__(self, value: int, repeat: int = 0, flags: int = 0, register: int = 0, valueSize: int = 0, isPointer: bool = False): GeckoCode.assertRegister(register) self.value = value self.valueSize = valueSize self.flags = flags self.repeat = repeat self._register = register self.isPointer = isPointer def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" valueType = ("byte", "short", "word")[self.valueSize] flags = self.flags if flags > 0x21: return f"({intType:02X}) Invalid flag {flags}" if self.repeat > 0: if flags & 0x01: return f"({intType:02X}) Store Gecko Register {self._register}'s {valueType} to [0x{self.value} + the {addrstr}] {self.repeat + 1} times consecutively" else: return f"({intType:02X}) Store Gecko Register {self._register}'s {valueType} to [0x{self.value}] {self.repeat + 1} times consecutively" else: if flags & 0x01: return f"({intType:02X}) Store Gecko Register {self._register}'s {valueType} to [0x{self.value} + the {addrstr}]" else: return f"({intType:02X}) Store Gecko Register {self._register}'s {valueType} to [0x{self.value}]" def __len__(self) -> int: return 8 def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") return self.value def __setitem__(self, index: int, value: Union[int, bytes]): if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.GECKO_REG_STORE @property def value(self) -> int: return self._value & 0xFFFFFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 class GeckoRegisterOperateI(GeckoCode): def __init__(self, value: int, opType: GeckoCode.ArithmeticType, flags: int = 0, register: int = 0): GeckoCode.assertRegister(register) self.value = value self.opType = opType self._register = register self.flags = flags def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) grAccessType = f"[Gecko Register {self._register}]" if ( self.flags & 1) != 0 else f"Gecko Register {self._register}" valueAccessType = f"[{self.value}]" if ( self.flags & 0x2) != 0 else f"{self.value}" opType = self.opType if opType == GeckoCode.ArithmeticType.ADD: return f"({intType:02X}) Add {valueAccessType} to {grAccessType}" elif opType == GeckoCode.ArithmeticType.MUL: return f"({intType:02X}) Multiply {grAccessType} by {valueAccessType}" elif opType == GeckoCode.ArithmeticType.OR: return f"({intType:02X}) OR {grAccessType} with {valueAccessType}" elif opType == GeckoCode.ArithmeticType.XOR: return f"({intType:02X}) XOR {grAccessType} with {valueAccessType}" elif opType == GeckoCode.ArithmeticType.SLW: return f"({intType:02X}) Shift {grAccessType} left by {valueAccessType} bits" elif opType == GeckoCode.ArithmeticType.SRW: return f"({intType:02X}) Shift {grAccessType} right by {valueAccessType} bits" elif opType == GeckoCode.ArithmeticType.ROL: return f"({intType:02X}) Rotate {grAccessType} left by {valueAccessType} bits" elif opType == GeckoCode.ArithmeticType.ASR: return f"({intType:02X}) Arithmetic shift {grAccessType} right by {valueAccessType} bits" elif opType == GeckoCode.ArithmeticType.FADDS: return f"({intType:02X}) Add {valueAccessType} to {grAccessType} as a float" elif opType == GeckoCode.ArithmeticType.FMULS: return f"({intType:02X}) Multiply {grAccessType} by {valueAccessType} as a float" return f"({intType:02X}) Invalid operation flag {opType}" def __len__(self) -> int: return 8 def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") return self.value def __setitem__(self, index: int, value: Union[int, bytes]): if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.GECKO_REG_OPERATE_I @property def value(self) -> int: return self._value & 0xFFFFFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 class GeckoRegisterOperate(GeckoCode): def __init__(self, otherRegister: int, opType: GeckoCode.ArithmeticType, flags: int = 0, register: int = 0): GeckoCode.assertRegister(register) GeckoCode.assertRegister(otherRegister) self.opType = opType self._register = register self._other = otherRegister self.flags = flags def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) grAccessType = f"[Gecko Register {self._register}]" if ( self.flags & 1) != 0 else f"Gecko Register {self._register}" valueAccessType = f"[Gecko Register {self._other}]" if ( self.flags & 0x2) != 0 else f"Gecko Register {self._other}" opType = self.opType if opType == GeckoCode.ArithmeticType.ADD: return f"({intType:02X}) Add {valueAccessType} to {grAccessType}" elif opType == GeckoCode.ArithmeticType.MUL: return f"({intType:02X}) Multiply {grAccessType} by {valueAccessType}" elif opType == GeckoCode.ArithmeticType.OR: return f"({intType:02X}) OR {grAccessType} with {valueAccessType}" elif opType == GeckoCode.ArithmeticType.XOR: return f"({intType:02X}) XOR {grAccessType} with {valueAccessType}" elif opType == GeckoCode.ArithmeticType.SLW: return f"({intType:02X}) Shift {grAccessType} left by {valueAccessType}" elif opType == GeckoCode.ArithmeticType.SRW: return f"({intType:02X}) Shift {grAccessType} right by {valueAccessType}" elif opType == GeckoCode.ArithmeticType.ROL: return f"({intType:02X}) Rotate {grAccessType} left by {valueAccessType}" elif opType == GeckoCode.ArithmeticType.ASR: return f"({intType:02X}) Arithmetic shift {grAccessType} right by {valueAccessType}" elif opType == GeckoCode.ArithmeticType.FADDS: return f"({intType:02X}) Add {valueAccessType} to {grAccessType} as a float" elif opType == GeckoCode.ArithmeticType.FMULS: return f"({intType:02X}) Multiply {grAccessType} by {valueAccessType} as a float" return f"({intType:02X}) Invalid operation flag {opType}" def __len__(self) -> int: return 8 def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") return self.value def __setitem__(self, index: int, value: Union[int, bytes]): if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.GECKO_REG_OPERATE @property def value(self) -> int: return self._value & 0xFFFFFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 class MemoryCopyTo(GeckoCode): def __init__(self, value: int, size: int, otherRegister: int = 0xF, register: int = 0, isPointer: bool = False): GeckoCode.assertRegister(register) GeckoCode.assertRegister(otherRegister) self.value = value self.size = size self._register = register self._other = otherRegister self.isPointer = isPointer def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" if self._other == 0xF: return f"({intType:02X}) Copy 0x{self.size:04X} bytes from [Gecko Register {self._register}] to (the {addrstr} + 0x{self.value})" else: return f"({intType:02X}) Copy 0x{self.size:04X} bytes from [Gecko Register {self._register}] to ([Gecko Register {self._other}] + 0x{self.value})" def __len__(self) -> int: return 8 def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") return self.value def __setitem__(self, index: int, value: Union[int, bytes]): if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.MEMCPY_1 @property def value(self) -> int: return self._value & 0xFFFFFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 class MemoryCopyFrom(GeckoCode): def __init__(self, value: int, size: int, otherRegister: int = 0xF, register: int = 0, isPointer: bool = False): GeckoCode.assertRegister(register) GeckoCode.assertRegister(otherRegister) self.value = value self.size = size self._register = register self._other = otherRegister self.isPointer = isPointer def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" if self._other == 0xF: return f"({intType:02X}) Copy 0x{self.size:04X} bytes from (the {addrstr} + 0x{self.value}) to [Gecko Register {self._register}]" else: return f"({intType:02X}) Copy 0x{self.size:04X} bytes from ([Gecko Register {self._other}] + 0x{self.value}) to [Gecko Register {self._register}]" def __len__(self) -> int: return 8 def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") return self.value def __setitem__(self, index: int, value: Union[int, bytes]): if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.MEMCPY_2 @property def value(self) -> int: return self._value & 0xFFFFFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 class GeckoIfEqual16(GeckoCode): def __init__(self, address: int = 0, register: int = 0, otherRegister: int = 15, isPointer: bool = False, endif: bool = False, mask: int = 0xFFFF): GeckoCode.assertRegister(register) GeckoCode.assertRegister(otherRegister) self.mask = mask self.address = address self.endif = endif self._register = register self._other = otherRegister self.isPointer = isPointer self._children = [] def __len__(self) -> int: return 8 + sum([len(c) for c in self]) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" home = f"(Gecko Register {self._register} & ~0x{self.mask:04X})" if self._register != 0xF else f"the short at address (0x{self.address:08X} + the {addrstr})" target = f"(Gecko Register {self._other} & ~0x{self.mask:04X})" if self._other != 0xF else f"the short at address (0x{self.address:08X} + the {addrstr})" endif = "(Apply Endif) " if self.endif else "" return f"({intType:02X}) {endif}If {home} is equal to {target}, run the encapsulated codes" def __getitem__(self, index: int) -> GeckoCode: return self._children[index] def __setitem__(self, index: int, value: GeckoCode): if not isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} as a child of {self.__class__.__name__}") self._children[index] = value @property def children(self) -> List["GeckoCode"]: return self._children @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.GECKO_IF_EQ_16 def add_child(self, child: "GeckoCode"): self._children.append(child) def remove_child(self, child: "GeckoCode"): self._children.remove(child) def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): code = GeckoCode.bytes_to_geckocode(f) while code != Terminator: self.add_child(code) code = GeckoCode.bytes_to_geckocode(f) self.add_child(code) class GeckoIfNotEqual16(GeckoCode): def __init__(self, address: int = 0, register: int = 0, otherRegister: int = 15, isPointer: bool = False, endif: bool = False, mask: int = 0xFFFF): GeckoCode.assertRegister(register) GeckoCode.assertRegister(otherRegister) self.mask = mask self.address = address self.endif = endif self._register = register self._other = otherRegister self.isPointer = isPointer self._children = [] def __len__(self) -> int: return 8 + sum([len(c) for c in self]) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" home = f"(Gecko Register {self._register} & ~0x{self.mask:04X})" if self._register != 0xF else f"the short at address (0x{self.address:08X} + the {addrstr})" target = f"(Gecko Register {self._other} & ~0x{self.mask:04X})" if self._other != 0xF else f"the short at address (0x{self.address:08X} + the {addrstr})" endif = "(Apply Endif) " if self.endif else "" return f"({intType:02X}) {endif}If {home} is not equal to {target}, run the encapsulated codes" def __getitem__(self, index: int) -> GeckoCode: return self._children[index] def __setitem__(self, index: int, value: GeckoCode): if not isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} as a child of {self.__class__.__name__}") self._children[index] = value @property def children(self) -> List["GeckoCode"]: return self._children @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.GECKO_IF_NEQ_16 def add_child(self, child: "GeckoCode"): self._children.append(child) def remove_child(self, child: "GeckoCode"): self._children.remove(child) def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): code = GeckoCode.bytes_to_geckocode(f) while code != Terminator: self.add_child(code) code = GeckoCode.bytes_to_geckocode(f) self.add_child(code) class GeckoIfGreaterThan16(GeckoCode): def __init__(self, address: int = 0, register: int = 0, otherRegister: int = 15, isPointer: bool = False, endif: bool = False, mask: int = 0xFFFF): GeckoCode.assertRegister(register) GeckoCode.assertRegister(otherRegister) self.mask = mask self.address = address self.endif = endif self._register = register self._other = otherRegister self.isPointer = isPointer self._children = [] def __len__(self) -> int: return 8 + sum([len(c) for c in self]) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" home = f"(Gecko Register {self._register} & ~0x{self.mask:04X})" if self._register != 0xF else f"the short at address (0x{self.address:08X} + the {addrstr})" target = f"(Gecko Register {self._other} & ~0x{self.mask:04X})" if self._other != 0xF else f"the short at address (0x{self.address:08X} + the {addrstr})" endif = "(Apply Endif) " if self.endif else "" return f"({intType:02X}) {endif}If {home} is greater than {target}, run the encapsulated codes" def __getitem__(self, index: int) -> GeckoCode: return self._children[index] def __setitem__(self, index: int, value: GeckoCode): if not isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} as a child of {self.__class__.__name__}") self._children[index] = value @property def children(self) -> List["GeckoCode"]: return self._children @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.GECKO_IF_GT_16 def add_child(self, child: "GeckoCode"): self._children.append(child) def remove_child(self, child: "GeckoCode"): self._children.remove(child) def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): code = GeckoCode.bytes_to_geckocode(f) while code != Terminator: self.add_child(code) code = GeckoCode.bytes_to_geckocode(f) self.add_child(code) class GeckoIfLesserThan16(GeckoCode): def __init__(self, address: int = 0, register: int = 0, otherRegister: int = 15, isPointer: bool = False, endif: bool = False, mask: int = 0xFFFF): GeckoCode.assertRegister(register) GeckoCode.assertRegister(otherRegister) self.mask = mask self.address = address self.endif = endif self._register = register self._other = otherRegister self.isPointer = isPointer self._children = [] def __len__(self) -> int: return 8 + sum([len(c) for c in self]) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" home = f"(Gecko Register {self._register} & ~0x{self.mask:04X})" if self._register != 0xF else f"the short at address (0x{self.address:08X} + the {addrstr})" target = f"(Gecko Register {self._other} & ~0x{self.mask:04X})" if self._other != 0xF else f"the short at address (0x{self.address:08X} + the {addrstr})" endif = "(Apply Endif) " if self.endif else "" return f"({intType:02X}) {endif}If {home} is less than {target}, run the encapsulated codes" def __getitem__(self, index: int) -> GeckoCode: return self._children[index] def __setitem__(self, index: int, value: GeckoCode): if not isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} as a child of {self.__class__.__name__}") self._children[index] = value @property def children(self) -> List["GeckoCode"]: return self._children @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.GECKO_IF_LT_16 def add_child(self, child: "GeckoCode"): self._children.append(child) def remove_child(self, child: "GeckoCode"): self._children.remove(child) def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): code = GeckoCode.bytes_to_geckocode(f) while code != Terminator: self.add_child(code) code = GeckoCode.bytes_to_geckocode(f) self.add_child(code) class GeckoIfLesserThan16(GeckoCode): def __init__(self, address: int = 0, register: int = 0, otherRegister: int = 15, isPointer: bool = False, endif: bool = False, mask: int = 0xFFFF): GeckoCode.assertRegister(register) GeckoCode.assertRegister(otherRegister) self.mask = mask self.address = address self.endif = endif self._register = register self._other = otherRegister self.isPointer = isPointer self._children = [] def __len__(self) -> int: return 8 + sum([len(c) for c in self]) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" home = f"(Gecko Register {self._register} & ~0x{self.mask:04X})" if self._register != 0xF else f"the short at address (0x{self.address:08X} + the {addrstr})" target = f"(Gecko Register {self._other} & ~0x{self.mask:04X})" if self._other != 0xF else f"the short at address (0x{self.address:08X} + the {addrstr})" endif = "(Apply Endif) " if self.endif else "" return f"({intType:02X}) {endif}If {home} is less than {target}, run the encapsulated codes" def __getitem__(self, index: int) -> GeckoCode: return self._children[index] def __setitem__(self, index: int, value: GeckoCode): if not isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} as a child of {self.__class__.__name__}") self._children[index] = value @property def children(self) -> List["GeckoCode"]: return self._children @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.GECKO_IF_LT_16 def add_child(self, child: "GeckoCode"): self._children.append(child) def remove_child(self, child: "GeckoCode"): self._children.remove(child) def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): code = GeckoCode.bytes_to_geckocode(f) while code != Terminator: self.add_child(code) code = GeckoCode.bytes_to_geckocode(f) self.add_child(code) class CounterIfEqual16(GeckoCode): def __init__(self, value: int, mask: int = 0xFFFF, flags: int = 0, counter: int = 0): self.value = value self.mask = mask self.flags = flags self.counter = counter self._children = [] def __len__(self) -> int: return 8 + sum([len(c) for c in self]) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) ty = " (Resets counter if true)" if (self.flags & 0x8) != 0 else " (Resets counter if false)" endif = "(Apply Endif) " if (self.flags & 0x1) != 0 else "" return f"({intType:02X}) {endif}If (0x{self.value:04X} & ~0x{self.mask:04X}) is equal to {self.counter}, run the encapsulated codes{ty}" def __getitem__(self, index: int) -> GeckoCode: return self._children[index] def __setitem__(self, index: int, value: GeckoCode): if not isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} as a child of {self.__class__.__name__}") self._children[index] = value @property def children(self) -> List["GeckoCode"]: return self._children @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.COUNTER_IF_EQ_16 @property def value(self) -> int: return self._value & 0xFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFF def add_child(self, child: "GeckoCode"): self._children.append(child) def remove_child(self, child: "GeckoCode"): self._children.remove(child) def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): code = GeckoCode.bytes_to_geckocode(f) while code != Terminator: self.add_child(code) code = GeckoCode.bytes_to_geckocode(f) self.add_child(code) class CounterIfNotEqual16(GeckoCode): def __init__(self, value: int, mask: int = 0xFFFF, flags: int = 0, counter: int = 0): self.value = value self.mask = mask self.flags = flags self.counter = counter self._children = [] def __len__(self) -> int: return 8 + sum([len(c) for c in self]) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) ty = " (Resets counter if true)" if (self.flags & 0x8) != 0 else " (Resets counter if false)" endif = "(Apply Endif) " if (self.flags & 0x1) != 0 else "" return f"({intType:02X}) {endif}If (0x{self.value:04X} & ~0x{self.mask:04X}) is not equal to {self.counter}, run the encapsulated codes{ty}" def __getitem__(self, index: int) -> GeckoCode: return self._children[index] def __setitem__(self, index: int, value: GeckoCode): if not isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} as a child of {self.__class__.__name__}") self._children[index] = value @property def children(self) -> List["GeckoCode"]: return self._children @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.COUNTER_IF_NEQ_16 @property def value(self) -> int: return self._value & 0xFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFF def add_child(self, child: "GeckoCode"): self._children.append(child) def remove_child(self, child: "GeckoCode"): self._children.remove(child) def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): code = GeckoCode.bytes_to_geckocode(f) while code != Terminator: self.add_child(code) code = GeckoCode.bytes_to_geckocode(f) self.add_child(code) class CounterIfGreaterThan16(GeckoCode): def __init__(self, value: int, mask: int = 0xFFFF, flags: int = 0, counter: int = 0): self.value = value self.mask = mask self.flags = flags self.counter = counter self._children = [] def __len__(self) -> int: return 8 + sum([len(c) for c in self]) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) ty = " (Resets counter if true)" if (self.flags & 0x8) != 0 else " (Resets counter if false)" endif = "(Apply Endif) " if (self.flags & 0x1) != 0 else "" return f"({intType:02X}) {endif}If (0x{self.value:04X} & ~0x{self.mask:04X}) is greater than {self.counter}, run the encapsulated codes{ty}" def __getitem__(self, index: int) -> GeckoCode: return self._children[index] def __setitem__(self, index: int, value: GeckoCode): if not isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} as a child of {self.__class__.__name__}") self._children[index] = value @property def children(self) -> List["GeckoCode"]: return self._children @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.COUNTER_IF_GT_16 @property def value(self) -> int: return self._value & 0xFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFF def add_child(self, child: "GeckoCode"): self._children.append(child) def remove_child(self, child: "GeckoCode"): self._children.remove(child) def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): code = GeckoCode.bytes_to_geckocode(f) while code != Terminator: self.add_child(code) code = GeckoCode.bytes_to_geckocode(f) self.add_child(code) class CounterIfLesserThan16(GeckoCode): def __init__(self, value: int, mask: int = 0xFFFF, flags: int = 0, counter: int = 0): self.value = value self.mask = mask self.flags = flags self.counter = counter self._children = [] def __len__(self) -> int: return 8 + sum([len(c) for c in self]) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) ty = " (Resets counter if true)" if (self.flags & 0x8) != 0 else " (Resets counter if false)" endif = "(Apply Endif) " if (self.flags & 0x1) != 0 else "" return f"({intType:02X}) {endif}If (0x{self.value:04X} & ~0x{self.mask:04X}) is less than {self.counter}, run the encapsulated codes{ty}" def __getitem__(self, index: int) -> GeckoCode: return self._children[index] def __setitem__(self, index: int, value: GeckoCode): if not isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} as a child of {self.__class__.__name__}") self._children[index] = value @property def children(self) -> List["GeckoCode"]: return self._children @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.COUNTER_IF_LT_16 @property def value(self) -> int: return self._value & 0xFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFF def add_child(self, child: "GeckoCode"): self._children.append(child) def remove_child(self, child: "GeckoCode"): self._children.remove(child) def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): code = GeckoCode.bytes_to_geckocode(f) while code != Terminator: self.add_child(code) code = GeckoCode.bytes_to_geckocode(f) self.add_child(code) class AsmExecute(GeckoCode): def __init__(self, value: bytes): self.value = value def __len__(self) -> int: return 8 + len(self.value) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) return f"({intType:02X}) Execute the designated ASM once every pass" def __getitem__(self, index: int) -> bytes: return self.value[index] def __setitem__(self, index: int, value: bytes): if isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value[index] = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.ASM_EXECUTE @property def value(self) -> bytes: return self._value @value.setter def value(self, value: bytes): self._value = value def virtual_length(self) -> int: return ((len(self) + 7) & -0x8) >> 3 class AsmInsert(GeckoCode): def __init__(self, value: bytes, address: int = 0, isPointer: bool = False): self.value = value self.address = address self.isPointer = isPointer def __len__(self) -> int: return 8 + len(self.value) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" return f"({intType:02X}) Inject (b / b) the designated ASM at 0x{self.address:08X} + the {addrstr}" def __getitem__(self, index: int) -> bytes: return self.value[index] def __setitem__(self, index: int, value: bytes): if isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value[index] = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.ASM_INSERT @property def value(self) -> bytes: return self._value @value.setter def value(self, value: bytes): self._value = value def virtual_length(self) -> int: return ((len(self) + 7) & -0x8) >> 3 class AsmInsertLink(GeckoCode): def __init__(self, value: bytes, address: int = 0, isPointer: bool = False): self.value = value self.address = address self.isPointer = isPointer def __len__(self) -> int: return 8 + len(self.value) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" return f"({intType:02X}) Inject (bl / blr) the designated ASM at 0x{self.address:08X} + the {addrstr}" def __getitem__(self, index: int) -> bytes: return self.value[index] def __setitem__(self, index: int, value: bytes): if isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value[index] = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.ASM_INSERT_L @property def value(self) -> bytes: return self._value @value.setter def value(self, value: bytes): self._value = value def virtual_length(self) -> int: return ((len(self) + 7) & -0x8) >> 3 class WriteBranch(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = False): self.value = value self.address = address self.isPointer = isPointer def __len__(self) -> int: return 8 def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" return f"({intType:02X}) Write a translated branch at (0x{self.address:08X} + the {addrstr}) to 0x{self.value:08X}" def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") return self.value def __setitem__(self, index: int, value: Union[int, bytes]): if index != 0: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.WRITE_BRANCH @property def value(self) -> int: return self._value & 0xFFFFFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 def apply(self, dol: DolFile) -> bool: addr = self.address | 0x80000000 if dol.is_mapped(addr): dol.seek(addr) dol.insert_branch(self.value, addr, lk=addr & 1) return True return False class Switch(GeckoCode): def __init__(self): pass def __len__(self) -> int: return 8 def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) return f"({intType:02X}) Toggle the code execution status when reached (True <-> False)" @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.SWITCH def virtual_length(self) -> int: return 1 class AddressRangeCheck(GeckoCode): def __init__(self, value: int, isPointer: bool = False, endif: bool = False): self.value = value self.isPointer = isPointer self.endif = endif def __len__(self) -> int: return 8 def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) | ( 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" endif = "(Apply Endif) " if self.endif else "" return f"({intType:02X}) {endif}Check if 0x{self[0]} <= {addrstr} < 0x{self[1]}" def __getitem__(self, index: int) -> int: if index not in {0, 1}: raise IndexError( f"Index [{index}] is beyond the virtual code size") return (self.value & (0xFFFF << (16 * (index ^ 1)))) << (16 * index) def __setitem__(self, index: int, value: Union[int, bytes]): if index not in {0, 1}: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") v = self.value v &= (0xFFFF << (16 * index)) v |= (value & 0xFFFF) << (16 * (index ^ 1)) self.value = v @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.ADDR_RANGE_CHECK @property def value(self) -> int: return self._value & 0xFFFFFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 class Terminator(GeckoCode): def __init__(self, value: int): self.value = value def __len__(self) -> int: return 8 def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) baStr = f" Set the base address to {self[0]}." if self[0] != 0 else "" poStr = f" Set the pointer address to {self[1]}." if self[1] != 0 else "" return f"({intType:02X}) Clear the code execution status.{baStr}{poStr}" def __getitem__(self, index: int) -> int: if index not in {0, 1}: raise IndexError( f"Index [{index}] is beyond the virtual code size") return (self.value & (0xFFFF << (16 * (index ^ 1)))) << (16 * index) def __setitem__(self, index: int, value: Union[int, bytes]): if index not in {0, 1}: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") v = self.value v &= (0xFFFF << (16 * index)) v |= (value & 0xFFFF) << (16 * (index ^ 1)) self.value = v @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.TERMINATOR @property def value(self) -> int: return self._value & 0xFFFFFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 class Endif(GeckoCode): def __init__(self, value: int, asElse: bool = False, numEndifs: int = 0): self.value = value self.asElse = asElse self.endifNum = numEndifs def __len__(self) -> int: return 8 def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) baStr = f" Set the base address to {self[0]}." if self[0] != 0 else "" poStr = f" Set the pointer address to {self[1]}." if self[1] != 0 else "" elseStr = "Inverse the code execution status (else) " if self.asElse else "" endif = "(Apply Endif) " if self.endifNum == 1 else f"(Apply {self.endifNum} Endifs) " return f"({intType:02X}) {endif}{elseStr}{baStr}{poStr}" def __getitem__(self, index: int) -> int: if index not in {0, 1}: raise IndexError( f"Index [{index}] is beyond the virtual code size") return (self.value & (0xFFFF << (16 * (index ^ 1)))) << (16 * index) def __setitem__(self, index: int, value: Union[int, bytes]): if index not in {0, 1}: raise IndexError( f"Index [{index}] is beyond the virtual code size") elif isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") v = self.value v &= (0xFFFF << (16 * index)) v |= (value & 0xFFFF) << (16 * (index ^ 1)) self.value = v @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.ENDIF @property def value(self) -> int: return self._value & 0xFFFFFFFF @value.setter def value(self, value: Union[int, bytes]): if isinstance(value, bytes): value = int.from_bytes(value, "big", signed=False) self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 class Exit(GeckoCode): def __init__(self): pass def __len__(self) -> int: return 8 def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) return f"({intType:02X}) Flag the end of the codelist, the codehandler exits" @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.EXIT def virtual_length(self) -> int: return 1 class AsmInsertXOR(GeckoCode): def __init__(self, value: bytes, address: int = 0, isPointer: bool = False, mask: int = 0, xorCount: int = 0): self.value = value self.mask = mask self.xorCount = xorCount self.address = address self.isPointer = isPointer def __len__(self) -> int: return 8 + len(self.value) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) + ( 2 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" return f"({intType:02X}) Inject (b / b) the designated ASM at (0x{self.address:08X} + the {addrstr}) if the 16-bit value at the injection point (and {self.xorCount} additional values) XOR'ed equals 0x{self.mask:04X}" def __getitem__(self, index: int) -> bytes: return self.value[index] def __setitem__(self, index: int, value: bytes): if isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} to the data of {self.__class__.__name__}") self.value[index] = value @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.ASM_INSERT_XOR @property def value(self) -> bytes: return self._value @value.setter def value(self, value: bytes): self._value = value def virtual_length(self) -> int: return ((len(self) + 7) & -0x8) >> 3 class BrainslugSearch(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0, searchRange: Tuple[int, int] = [0x8000, 0x8180]): self.value = value self.address = address self.searchRange = searchRange self._children = [] def __len__(self) -> int: return 8 + len(self.value) + sum([len(c) for c in self]) def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) return f"({intType:02X}) If the linear data search finds a match between addresses 0x{(self.searchRange[0] & 0xFFFF) << 16} and 0x{(self.searchRange[1] & 0xFFFF) << 16}, set the pointer address to the beginning of the match and run the encapsulated codes" def __getitem__(self, index: int) -> GeckoCode: return self._children[index] def __setitem__(self, index: int, value: GeckoCode): if not isinstance(value, GeckoCode): raise InvalidGeckoCodeError( f"Cannot assign {value.__class__.__name__} as a child of {self.__class__.__name__}") self._children[index] = value @property def children(self) -> List["GeckoCode"]: return self._children @property def codetype(self) -> GeckoCode.Type: return GeckoCode.Type.BRAINSLUG_SEARCH @property def value(self) -> bytes: return self._value @value.setter def value(self, value: bytes): self._value = value def add_child(self, child: "GeckoCode"): self._children.append(child) def remove_child(self, child: "GeckoCode"): self._children.remove(child) def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): code = GeckoCode.bytes_to_geckocode(f) while code != Terminator: self.add_child(code) code = GeckoCode.bytes_to_geckocode(f) self.add_child(code)