3434 lines
No EOL
124 KiB
Python
3434 lines
No EOL
124 KiB
Python
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) |