From 59aa638191940e096885d0fdd243c7806c039688 Mon Sep 17 00:00:00 2001 From: JoshuaMK Date: Tue, 20 Apr 2021 01:10:30 -0500 Subject: [PATCH 01/17] Create framework and implementation of write types --- kernel.py | 445 +++++++++++++++++++++++++----------------------------- 1 file changed, 208 insertions(+), 237 deletions(-) diff --git a/kernel.py b/kernel.py index 9e0f8e7..be8ab8d 100644 --- a/kernel.py +++ b/kernel.py @@ -3,8 +3,10 @@ import re import sys import time +from enum import Enum from io import BytesIO from pathlib import Path +from typing import Generator, IO, List, Optional, Union import tools from dolreader import DolFile, SectionCountFullError, UnmappedAddressError @@ -17,162 +19,94 @@ print(IE) sys.exit(1) + def timer(func): @functools.wraps(func) def wrapper(*args, **kwargs): start = time.perf_counter() value = func(*args, **kwargs) end = time.perf_counter() - print(tools.color_text(f"\n :: Completed in {(end - start):0.4f} seconds!\n", defaultColor=tools.TGREENLIT)) + print(tools.color_text( + f"\n :: Completed in {(end - start):0.4f} seconds!\n", defaultColor=tools.TGREENLIT)) return value return wrapper -class InvalidGeckoCodeError(Exception): pass -class GCT(object): - def __init__(self, f): - self.codeList = BytesIO(f.read()) - self.rawLineCount = tools.stream_size(self.codeList) >> 3 - self.lineCount = self.rawLineCount - 2 - f.seek(0) - @property - def size(self): - return len(self.codeList.getbuffer()) +class GCT(object): - @staticmethod - def determine_codelength(codetype, info: bytes) -> int: - if codetype.startswith(b"\x06"): - bytelength = int.from_bytes(info, byteorder="big", signed=False) - padding = get_alignment(bytelength, 8) - return 0x8 + bytelength + padding + def __init__(self): + self.codelist: List[GeckoCode] = [] - elif (codetype.startswith(b"\x08") or codetype.startswith(b"\x09") - or codetype.startswith(b"\x18") or codetype.startswith(b"\x18")): - return 0x16 + def __len__(self): + return self.byteSize - elif (codetype.startswith(b"\xC0") or codetype.startswith(b"\xC2") or codetype.startswith(b"\xC4") - or codetype.startswith(b"\xC3") or codetype.startswith(b"\xC5") or codetype.startswith(b"\xD2") - or codetype.startswith(b"\xD4") or codetype.startswith(b"\xD3") or codetype.startswith(b"\xD5")): - return 0x8 + (int.from_bytes(info, byteorder="big", signed=False) << 3) + @classmethod + def from_bytes(cls, f: IO): + gct = cls() + gct.bytes_to_codelist(f) + return gct - elif (codetype.startswith(b"\xF2") or codetype.startswith(b"\xF3") - or codetype.startswith(b"\xF4") or codetype.startswith(b"\xF5")): - return 0x8 + (int.from_bytes(info[:2], byteorder="big", signed=False) << 3) + @property + def byteSize(self): + return sum([len(c) for c in self.codelist]) - elif codetype.startswith(b"\xF6"): - return 0x8 + (int.from_bytes(info[:4], byteorder="big", signed=False) << 3) + @property + def groupSize(self): + return (self.byteSize >> 3) - else: - return 0x8 + @property + def virtualSize(self): + return len(self.codelist) + + def add_geckocode(self, code: GeckoCode): + self.codelist.append(code) + + def remove_geckocode(self, code: GeckoCode): + self.codelist.remove(code) + + def bytes_to_codelist(self, f: IO): + while metadata := f.read(4): + info = self._rawData.read(4) + address = 0x80000000 | (int.from_bytes( + metadata, byteorder="big", signed=False) & 0x1FFFFFF) + codetype = (int.from_bytes( + metadata, "big", signed=False) >> 24) & 0xFF + isPointerType = (codetype & 0x10 != 0) + + if ((codetype & 0xEF) <= 0x0F): + self.add_geckocode( + GeckoCode(GeckoCode.Type.WRITE, info, address, isPointerType)) + elif ((codetype & 0xEF) <= 0x2F): + ifBlock = GeckoCode(GeckoCode.Type.IF, info, + address, isPointerType) + ifBlock.populate_from_bytes(f) + self.add_geckocode( + GeckoCode(GeckoCode.Type.IF, info, address, isPointerType)) + elif ((codetype & 0xEF) <= 0xC5): + self.add_geckocode( + GeckoCode(GeckoCode.Type.ASM, info, address, isPointerType)) + elif ((codetype & 0xEF) <= 0xC7): + self.add_geckocode( + GeckoCode(GeckoCode.Type.BRANCH, info, address, isPointerType)) + elif codetype == 0xF0: + break - def optimize_codelist(self, dolFile: DolFile): - codelist = b"\x00\xD0\xC0\xDE"*2 - skipcodes = 0 + def to_bytes(self) -> bytes: + rawCodelist = b"\x00\xD0\xC0\xDE"*2 + for code in self.codelist: + if code._type == GeckoCode.Type.WRITE_8: + metadata = (code.address & 0x17FFFFF).to_bytes(4, "big", signed=False) + rawCodelist += metadata + code.info + rawCodelist += code.value + + def apply_to_dol(self, dol: DolFile): + for code in self.codelist: + if code.is_preprocess_allowed(): + code.apply(dol) + self.remove_geckocode(code) - self.codeList.seek(8) - while codetype := self.codeList.read(4): - info = self.codeList.read(4) - address = 0x80000000 | (int.from_bytes(codetype, byteorder="big", signed=False) & 0x1FFFFFF) - try: - if skipcodes <= 0: - if (codetype.startswith(b"\x00") or codetype.startswith(b"\x01") - or codetype.startswith(b"\x10") or codetype.startswith(b"\x11")): - dolFile.seek(address) - - counter = int.from_bytes(info[:-2], byteorder="big", signed=False) - value = info[2:] - - while counter + 1 > 0: - dolFile.write(value[1:]) - counter -= 1 - continue - - elif (codetype.startswith(b"\x02") or codetype.startswith(b"\x03") - or codetype.startswith(b"\x12") or codetype.startswith(b"\x13")): - dolFile.seek(address) - - counter = int.from_bytes(info[:-2], byteorder="big", signed=False) - value = info[2:] - - while counter + 1 > 0: - dolFile.write(value) - counter -= 1 - continue - - elif (codetype.startswith(b"\x04") or codetype.startswith(b"\x05") - or codetype.startswith(b"\x14") or codetype.startswith(b"\x15")): - dolFile.seek(address) - dolFile.write(info) - continue - - elif (codetype.startswith(b"\x06") or codetype.startswith(b"\x07") - or codetype.startswith(b"\x16") or codetype.startswith(b"\x17")): - dolFile.seek(address) - - arraylength = int.from_bytes(info, byteorder="big", signed=False) - padding = get_alignment(arraylength, 8) - - dolFile.write(self.codeList.read(arraylength)) - - self.codeList.seek(padding, 1) - continue - - elif (codetype.startswith(b"\x08") or codetype.startswith(b"\x09") - or codetype.startswith(b"\x18") or codetype.startswith(b"\x19")): - dolFile.seek(address) - - value = int.from_bytes(info, byteorder="big", signed=False) - data = read_uint16(self.codeList) - size = (data & 0x3000) >> 12 - counter = data & 0xFFF - address_increment = read_uint16(self.codeList) - value_increment = read_uint32(self.codeList) - - while counter + 1 > 0: - if size == 0: - write_ubyte(dolFile, value & 0xFF) - dolFile.seek(-1, 1) - elif size == 1: - write_uint16(dolFile, value & 0xFFFF) - dolFile.seek(-2, 1) - elif size == 2: - write_uint32(dolFile, value) - dolFile.seek(-4, 1) - else: - raise ValueError("Size type {} does not match 08 codetype specs".format(size)) - - dolFile.seek(address_increment, 1) - value += value_increment - counter -= 1 - if value > 0xFFFFFFFF: - value -= 0x100000000 - continue - - elif (codetype.startswith(b"\xC6") or codetype.startswith(b"\xC7") - or codetype.startswith(b"\xC6") or codetype.startswith(b"\xC7")): - dolFile.insert_branch(int.from_bytes(info, byteorder="big", signed=False), address, lk=address&1) - continue - - if codetype.hex().startswith("2") or codetype.hex().startswith("3"): - skipcodes += 1 - - elif codetype.startswith(b"\xE0"): - skipcodes -= 1 - - elif codetype.startswith(b"\xF0"): - codelist += b"\xF0\x00\x00\x00\x00\x00\x00\x00" - break - - self.codeList.seek(-8, 1) - codelist += self.codeList.read(GCT.determine_codelength(codetype, info)) - - except (RuntimeError, UnmappedAddressError): - self.codeList.seek(-8, 1) - codelist += self.codeList.read(GCT.determine_codelength(codetype, info)) - - self.codeList = BytesIO(codelist) class CodeHandler(object): @@ -180,6 +114,13 @@ class Types: MINI = "MINI" FULL = "FULL" + WiiVIHook = b"\x7C\xE3\x3B\x78\x38\x87\x00\x34\x38\xA7\x00\x38\x38\xC7\x00\x4C" + GCNVIHook = b"\x7C\x03\x00\x34\x38\x83\x00\x20\x54\x85\x08\x3C\x7C\x7F\x2A\x14\xA0\x03\x00\x00\x7C\x7D\x2A\x14\x20\xA4\x00\x3F\xB0\x03\x00\x00" + WiiGXDrawHook = b"\x3C\xA0\xCC\x01\x38\x00\x00\x61\x3C\x80\x45\x00\x98\x05\x80\x00" + GCNGXDrawHook = b"\x38\x00\x00\x61\x3C\xA0\xCC\x01\x3C\x80\x45\x00\x98\x05\x80\x00" + WiiPADHook = b"\x3A\xB5\x00\x01\x3A\x73\x00\x0C\x2C\x15\x00\x04\x3B\x18\x00\x0C" + GCNPADHook = b"\x3A\xB5\x00\x01\x2C\x15\x00\x04\x3B\x18\x00\x0C\x3B\xFF\x00\x0C" + def __init__(self, f): self._rawData = BytesIO(f.read()) @@ -189,22 +130,15 @@ def __init__(self, f): self._rawData.seek(0xFE) codelistLower = self._rawData.read(2).hex() - self.codeListPointer = int(codelistUpper[2:] + codelistLower[2:], 16) + self._rawDataPointer = int(codelistUpper[2:] + codelistLower[2:], 16) self.handlerLength = tools.stream_size(self._rawData) self.initAddress = 0x80001800 self.startAddress = 0x800018A8 - self.wiiVIHook = b"\x7C\xE3\x3B\x78\x38\x87\x00\x34\x38\xA7\x00\x38\x38\xC7\x00\x4C" - self.gcnVIHook = b"\x7C\x03\x00\x34\x38\x83\x00\x20\x54\x85\x08\x3C\x7C\x7F\x2A\x14\xA0\x03\x00\x00\x7C\x7D\x2A\x14\x20\xA4\x00\x3F\xB0\x03\x00\x00" - self.wiiGXDrawHook = b"\x3C\xA0\xCC\x01\x38\x00\x00\x61\x3C\x80\x45\x00\x98\x05\x80\x00" - self.gcnGXDrawHook = b"\x38\x00\x00\x61\x3C\xA0\xCC\x01\x3C\x80\x45\x00\x98\x05\x80\x00" - self.wiiPADHook = b"\x3A\xB5\x00\x01\x3A\x73\x00\x0C\x2C\x15\x00\x04\x3B\x18\x00\x0C" - self.gcnPADHook = b"\x3A\xB5\x00\x01\x2C\x15\x00\x04\x3B\x18\x00\x0C\x3B\xFF\x00\x0C" - self.allocation = None self.hookAddress = None self.hookType = None - self.geckoCodes = None + self.gct = GCT() self.includeAll = False self.optimizeList = False @@ -215,7 +149,7 @@ def __init__(self, f): f.seek(0) - def init_gct(self, gctFile: Path, tmpdir: Path=None): + def init_gct(self, gctFile: Path, tmpdir: Path = None): if tmpdir is not None: _tmpGct = tmpdir / "gct.bin" else: @@ -223,12 +157,13 @@ def init_gct(self, gctFile: Path, tmpdir: Path=None): if gctFile.suffix.lower() == ".txt": with _tmpGct.open("wb+") as temp: - temp.write(bytes.fromhex("00D0C0DE"*2 + self.parse_input(gctFile) + "F000000000000000")) + temp.write(bytes.fromhex("00D0C0DE"*2 + + self.parse_input(gctFile) + "F000000000000000")) temp.seek(0) - self.geckoCodes = GCT(temp) + self.gct = GCT(temp) elif gctFile.suffix.lower() == ".gct": with gctFile.open("rb") as gct: - self.geckoCodes = GCT(gct) + self.gct = GCT(gct) elif gctFile.suffix == "": with _tmpGct.open("wb+") as temp: temp.write(b"\x00\xD0\xC0\xDE"*2) @@ -241,13 +176,15 @@ def init_gct(self, gctFile: Path, tmpdir: Path=None): with file.open("rb") as gct: temp.write(gct.read()[8:-8]) else: - print(tools.color_text(f" :: HINT: {file} is not a .txt or .gct file", defaultColor=tools.TYELLOWLIT)) - + print(tools.color_text( + f" :: HINT: {file} is not a .txt or .gct file", defaultColor=tools.TYELLOWLIT)) + temp.write(b"\xF0\x00\x00\x00\x00\x00\x00\x00") temp.seek(0) - self.geckoCodes = GCT(temp) + self.gct = GCT(temp) else: - raise NotImplementedError(f"Parsing file type `{gctFile.suffix}' as a GCT is unsupported") + raise NotImplementedError( + f"Parsing file type `{gctFile.suffix}' as a GCT is unsupported") def parse_input(self, geckoText: Path) -> str: with geckoText.open("rb") as gecko: @@ -255,7 +192,7 @@ def parse_input(self, geckoText: Path) -> str: encodeType = result["encoding"] with geckoText.open("r", encoding=encodeType) as gecko: - geckoCodes = "" + gct = "" state = None for line in gecko.readlines(): @@ -267,24 +204,27 @@ def parse_input(self, geckoText: Path) -> str: state = "Dolphin" else: state = "OcarinaM" - + try: if state == "OcarinaM": if self.includeAll: - geckoLine = re.findall(r"[A-F0-9]{8}[\t\f ][A-F0-9]{8}", line, re.IGNORECASE)[0] + geckoLine = re.findall( + r"[A-F0-9]{8}[\t\f ][A-F0-9]{8}", line, re.IGNORECASE)[0] else: - geckoLine = re.findall(r"(?:\*\s*)([A-F0-9]{8}[\t\f ][A-F0-9]{8})", line, re.IGNORECASE)[0] + geckoLine = re.findall( + r"(?:\*\s*)([A-F0-9]{8}[\t\f ][A-F0-9]{8})", line, re.IGNORECASE)[0] else: - geckoLine = re.findall(r"(? int: + def encrypt_key(key: int) -> int: b1 = key & 0xFF b2 = (key >> 8) & 0xFF b3 = (key >> 16) & 0xFF @@ -295,13 +235,13 @@ def encrypt_key(key: int) -> int: return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4 def encrypt_codes(self, key: int): - self.geckoCodes.codeList.seek(0) + self.gct._rawData.seek(0) i = 0 while True: try: - packet = read_uint32(self.geckoCodes.codeList) - self.geckoCodes.codeList.seek(-4, 1) - write_uint32(self.geckoCodes.codeList, (packet^key) & 0xFFFFFFFF) + packet = read_uint32(self.gct._rawData) + self.gct._rawData.seek(-4, 1) + write_uint32(self.gct._rawData, (packet ^ key) & 0xFFFFFFFF) key += (i << 3) & 0xFFFFFFFF if key > 0xFFFFFFFF: key -= 0x100000000 @@ -315,7 +255,7 @@ def find_variable_data(self, variable) -> int: while sample := self._rawData.read(4): if sample == variable: return self._rawData.tell() - 4 - + return None def set_hook_instruction(self, dolFile: DolFile, address: int, varOffset: int, lk=0): @@ -326,24 +266,29 @@ def set_hook_instruction(self, dolFile: DolFile, address: int, varOffset: int, l if ((((ppc >> 24) & 0xFF) > 0x47 and ((ppc >> 24) & 0xFF) < 0x4C) or (((ppc >> 24) & 0xFF) > 0x3F and ((ppc >> 24) & 0xFF) < 0x44)): to, conditional = dolFile.extract_branch_addr(address) if conditional: - raise NotImplementedError("Hooking to a conditional non spr branch is unsupported") - write_uint32(self._rawData, (to - (self.initAddress + varOffset)) & 0x3FFFFFD | 0x48000000 | lk) + raise NotImplementedError( + "Hooking to a conditional non spr branch is unsupported") + write_uint32(self._rawData, (to - (self.initAddress + + varOffset)) & 0x3FFFFFD | 0x48000000 | lk) else: write_uint32(self._rawData, ppc) - + def set_variables(self, dolFile: DolFile): varOffset = self.find_variable_data(b"\x00\xDE\xDE\xDE") if varOffset is None: - raise RuntimeError(tools.color_text("Variable codehandler data not found\n", defaultColor=tools.TREDLIT)) + raise RuntimeError(tools.color_text( + "Variable codehandler data not found\n", defaultColor=tools.TREDLIT)) self.set_hook_instruction(dolFile, self.hookAddress, varOffset, 0) self._rawData.seek(varOffset + 4) - write_uint32(self._rawData, ((self.hookAddress + 4) - (self.initAddress + (varOffset + 4))) & 0x3FFFFFD | 0x48000000 | 0) + write_uint32(self._rawData, ((self.hookAddress + 4) - + (self.initAddress + (varOffset + 4))) & 0x3FFFFFD | 0x48000000 | 0) + class KernelLoader(object): - def __init__(self, f, cli: tools.CommandLineParser=None): + def __init__(self, f, cli: tools.CommandLineParser = None): self._rawData = BytesIO(f.read()) self._initDataList = None self._gpModDataList = None @@ -363,19 +308,20 @@ def error(self, msg: str): print(msg) sys.exit(1) - def set_variables(self, entryPoint: list, baseOffset: int=0): + def set_variables(self, entryPoint: list, baseOffset: int = 0): self._rawData.seek(0) if self._gpModDataList is None: return - + while sample := self._rawData.read(2): if sample == b"GH": self._rawData.seek(-2, 1) write_uint16(self._rawData, self._gpModDataList[0]) elif sample == b"GL": self._rawData.seek(-2, 1) - write_uint16(self._rawData, baseOffset + self._gpModDataList[1]) + write_uint16(self._rawData, baseOffset + + self._gpModDataList[1]) elif sample == b"IH": self._rawData.seek(-2, 1) write_uint16(self._rawData, entryPoint[0]) @@ -387,50 +333,57 @@ def set_variables(self, entryPoint: list, baseOffset: int=0): write_uint16(self._rawData, self._gpKeyAddrList[0]) elif sample == b"KL": self._rawData.seek(-2, 1) - write_uint16(self._rawData, baseOffset + self._gpKeyAddrList[1]) + write_uint16(self._rawData, baseOffset + + self._gpKeyAddrList[1]) def complete_data(self, codeHandler: CodeHandler, initpoint: list): - _upperAddr, _lowerAddr = ((self.initAddress >> 16) & 0xFFFF, self.initAddress & 0xFFFF) + _upperAddr, _lowerAddr = ( + (self.initAddress >> 16) & 0xFFFF, self.initAddress & 0xFFFF) _key = random.randrange(0x100000000) self._rawData.seek(0) while sample := self._rawData.read(4): - if sample == b"HEAP": #Found keyword "HEAP". Goes with the resize of the heap + if sample == b"HEAP": # Found keyword "HEAP". Goes with the resize of the heap self._rawData.seek(-4, 1) gpModInfoOffset = self._rawData.tell() - gpModUpperAddr = _upperAddr + 1 if (_lowerAddr + gpModInfoOffset) > 0x7FFF else _upperAddr #Absolute addressing - + gpModUpperAddr = _upperAddr + \ + 1 if ( + _lowerAddr + gpModInfoOffset) > 0x7FFF else _upperAddr # Absolute addressing + if codeHandler.allocation == None: - codeHandler.allocation = (codeHandler.handlerLength + codeHandler.geckoCodes.size + 7) & -8 - + codeHandler.allocation = ( + codeHandler.handlerLength + codeHandler.gct.byteSize + 7) & -8 + write_uint32(self._rawData, codeHandler.allocation) - - elif sample == b"LSIZ": #Found keyword "LSIZ". Goes with the size of the loader + + elif sample == b"LSIZ": # Found keyword "LSIZ". Goes with the size of the loader self._rawData.seek(-4, 1) write_uint32(self._rawData, len(self._rawData.getbuffer())) - - elif sample == b"HSIZ": #Found keyword "HSIZ". Goes with the size of the codehandler + + elif sample == b"HSIZ": # Found keyword "HSIZ". Goes with the size of the codehandler self._rawData.seek(-4, 1) write_sint32(self._rawData, codeHandler.handlerLength) - - elif sample == b"CSIZ": #Found keyword "CSIZ". Goes with the size of the codes + + elif sample == b"CSIZ": # Found keyword "CSIZ". Goes with the size of the codes self._rawData.seek(-4, 1) - write_sint32(self._rawData, codeHandler.geckoCodes.size) - - elif sample == b"HOOK": #Found keyword "HOOK". Goes with the codehandler hook + write_sint32(self._rawData, codeHandler.gct.byteSize) + + elif sample == b"HOOK": # Found keyword "HOOK". Goes with the codehandler hook self._rawData.seek(-4, 1) write_uint32(self._rawData, codeHandler.hookAddress) - elif sample == b"CRPT": #Found keyword "CRPT". Boolean of the encryption + elif sample == b"CRPT": # Found keyword "CRPT". Boolean of the encryption self._rawData.seek(-4, 1) write_bool(self._rawData, self.encrypt, 4) - elif sample == b"CYPT": #Found keyword "CYPT". Encryption Key + elif sample == b"CYPT": # Found keyword "CYPT". Encryption Key self._rawData.seek(-4, 1) gpKeyOffset = self._rawData.tell() - gpKeyUpperAddr = _upperAddr + 1 if (_lowerAddr + gpKeyOffset) > 0x7FFF else _upperAddr #Absolute addressing + gpKeyUpperAddr = _upperAddr + \ + 1 if ( + _lowerAddr + gpKeyOffset) > 0x7FFF else _upperAddr # Absolute addressing write_uint32(self._rawData, CodeHandler.encrypt_key(_key)) @@ -441,15 +394,17 @@ def complete_data(self, codeHandler: CodeHandler, initpoint: list): self._gpKeyAddrList = (gpKeyUpperAddr, gpKeyOffset) self.set_variables(initpoint, _lowerAddr) - + if self.encrypt: codeHandler.encrypt_codes(_key) def patch_arena(self, codeHandler: CodeHandler, dolFile: DolFile) -> tuple: - self.complete_data(codeHandler, [(dolFile.entryPoint >> 16) & 0xFFFF, dolFile.entryPoint & 0xFFFF]) + self.complete_data( + codeHandler, [(dolFile.entryPoint >> 16) & 0xFFFF, dolFile.entryPoint & 0xFFFF]) self._rawData.seek(0, 2) - self._rawData.write(codeHandler._rawData.getvalue() + codeHandler.geckoCodes.codeList.getvalue()) + self._rawData.write(codeHandler._rawData.getvalue() + + codeHandler.gct._rawData.getvalue()) self._rawData.seek(0) _kernelData = self._rawData.getvalue() @@ -460,32 +415,36 @@ def patch_arena(self, codeHandler: CodeHandler, dolFile: DolFile) -> tuple: try: dolFile.append_data_sections([(_kernelData, self.initAddress)]) except SectionCountFullError: - self.error(tools.color_text("There are no unused sections left for GeckoLoader to use!\n", defaultColor=tools.TREDLIT)) + self.error(tools.color_text( + "There are no unused sections left for GeckoLoader to use!\n", defaultColor=tools.TREDLIT)) dolFile.entryPoint = self.initAddress return True, None def patch_legacy(self, codeHandler: CodeHandler, dolFile: DolFile) -> tuple: codeHandler._rawData.seek(0) - codeHandler.geckoCodes.codeList.seek(0) - - _handlerData = codeHandler._rawData.getvalue() + codeHandler.geckoCodes.codeList.getvalue() + codeHandler.gct._rawData.seek(0) + + _handlerData = codeHandler._rawData.getvalue() + codeHandler.gct._rawData.getvalue() try: - dolFile.append_text_sections([(_handlerData, codeHandler.initAddress)]) + dolFile.append_text_sections( + [(_handlerData, codeHandler.initAddress)]) except SectionCountFullError: try: - dolFile.append_data_sections([(_handlerData, codeHandler.initAddress)]) + dolFile.append_data_sections( + [(_handlerData, codeHandler.initAddress)]) except SectionCountFullError: - self.error(tools.color_text("There are no unused sections left for GeckoLoader to use!\n", defaultColor=tools.TREDLIT)) + self.error(tools.color_text( + "There are no unused sections left for GeckoLoader to use!\n", defaultColor=tools.TREDLIT)) return True, None def protect_game(self, codeHandler: CodeHandler): - _oldpos = codeHandler.geckoCodes.codeList.tell() + _oldpos = codeHandler.gct._rawData.tell() protectdata = (b"\xC0\x00\x00\x00\x00\x00\x00\x17", - b"\x7C\x08\x02\xA6\x94\x21\xFF\x70", + b"\x7C\x08\x02\xA6\x94\x21\xFF\x70", b"\x90\x01\x00\x08\xBC\x61\x00\x0C", b"\x48\x00\x00\x0D\x00\xD0\xC0\xDE", b"\x00\xD0\xDE\xAD\x7F\xE8\x02\xA6", @@ -509,13 +468,13 @@ def protect_game(self, codeHandler: CodeHandler): b"\x38\x21\x00\x90\x7C\x08\x03\xA6", b"\x4E\x80\x00\x20\x00\x00\x00\x00") - codeHandler.geckoCodes.codeList.seek(-8, 2) + codeHandler.gct._rawData.seek(-8, 2) for line in protectdata: - codeHandler.geckoCodes.codeList.write(line) + codeHandler.gct._rawData.write(line) - codeHandler.geckoCodes.codeList.write(b"\xF0\x00\x00\x00\x00\x00\x00\x00") - codeHandler.geckoCodes.codeList.seek(_oldpos) + codeHandler.gct._rawData.write(b"\xF0\x00\x00\x00\x00\x00\x00\x00") + codeHandler.gct._rawData.seek(_oldpos) @timer def build(self, gctFile: Path, dolFile: DolFile, codeHandler: CodeHandler, tmpdir: Path, dump: Path): @@ -525,9 +484,10 @@ def build(self, gctFile: Path, dolFile: DolFile, codeHandler: CodeHandler, tmpdi codeHandler.init_gct(gctFile, tmpdir) - if codeHandler.geckoCodes is None: - self.error(tools.color_text("Valid codelist not found. Please provide a .txt/.gct file, or a folder of .txt/.gct files\n", defaultColor=tools.TREDLIT)) - + if codeHandler.gct is None: + self.error(tools.color_text( + "Valid codelist not found. Please provide a .txt/.gct file, or a folder of .txt/.gct files\n", defaultColor=tools.TREDLIT)) + if self.protect: self.protect_game(codeHandler) @@ -536,19 +496,21 @@ def build(self, gctFile: Path, dolFile: DolFile, codeHandler: CodeHandler, tmpdi if self.initAddress: try: dolFile.resolve_address(self.initAddress) - self.error(tools.color_text(f"Init address specified for GeckoLoader (0x{self.initAddress:X}) clobbers existing dol sections", defaultColor=tools.TREDLIT)) + self.error(tools.color_text( + f"Init address specified for GeckoLoader (0x{self.initAddress:X}) clobbers existing dol sections", defaultColor=tools.TREDLIT)) except UnmappedAddressError: pass else: - self.initAddress = dolFile.seek_nearest_unmapped(dolFile.bssAddress, len(self._rawData.getbuffer()) + codeHandler.handlerLength + codeHandler.geckoCodes.size) + self.initAddress = dolFile.seek_nearest_unmapped(dolFile.bssAddress, len( + self._rawData.getbuffer()) + codeHandler.handlerLength + codeHandler.gct.byteSize) self._rawData.seek(0) if codeHandler.optimizeList: - codeHandler.geckoCodes.optimize_codelist(dolFile) + codeHandler.gct.optimize_codelist(dolFile) """Is codelist optimized away?""" - if codeHandler.geckoCodes.codeList.getvalue() == b"\x00\xD0\xC0\xDE\x00\xD0\xC0\xDE\xF0\x00\x00\x00\x00\x00\x00\x00": + if codeHandler.gct._rawData.getvalue() == b"\x00\xD0\xC0\xDE\x00\xD0\xC0\xDE\xF0\x00\x00\x00\x00\x00\x00\x00": with dump.open("wb") as final: dolFile.save(final) @@ -557,35 +519,39 @@ def build(self, gctFile: Path, dolFile: DolFile, codeHandler: CodeHandler, tmpdi dolFile.print_info() print("-"*64) if self.verbosity >= 1: - print(tools.color_text("\n :: All codes have been successfully pre patched", defaultColor=tools.TGREENLIT)) + print(tools.color_text( + "\n :: All codes have been successfully pre patched", defaultColor=tools.TGREENLIT)) return hooked = determine_codehook(dolFile, codeHandler, False) if hooked: _status, _msg = self.patch_arena(codeHandler, dolFile) else: - self.error(tools.color_text("Failed to find a hook address. Try using option --codehook to use your own address\n", defaultColor=tools.TREDLIT)) + self.error(tools.color_text( + "Failed to find a hook address. Try using option --codehook to use your own address\n", defaultColor=tools.TREDLIT)) if _status is False: - self.error(tools.color_text(_msg + "\n", defaultColor=tools.TREDLIT)) - elif codeHandler.allocation < codeHandler.geckoCodes.size: - self.error(tools.color_text("Allocated codespace was smaller than the given codelist\n", defaultColor=tools.TYELLOW)) + self.error(tools.color_text( + _msg + "\n", defaultColor=tools.TREDLIT)) + elif codeHandler.allocation < codeHandler.gct.byteSize: + self.error(tools.color_text( + "Allocated codespace was smaller than the given codelist\n", defaultColor=tools.TYELLOW)) with dump.open("wb") as final: dolFile.save(final) if self.quiet: return - + if self.verbosity >= 3: dolFile.print_info() print("-"*64) - + if self.verbosity >= 2: print("") info = [f" :: Start of game modified to address 0x{self.initAddress:X}", f" :: Game function `__start()' located at address 0x{_oldStart:X}", - f" :: Allocation is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}", + f" :: Allocation is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.gct.byteSize:X}", f" :: Codehandler hooked at 0x{codeHandler.hookAddress:X}", f" :: Codehandler is of type `{codeHandler.type}'", f" :: Of the {DolFile.maxTextSections} text sections in this DOL file, {len(dolFile.textSections)} are now being used", @@ -593,20 +559,21 @@ def build(self, gctFile: Path, dolFile: DolFile, codeHandler: CodeHandler, tmpdi for bit in info: print(tools.color_text(bit, defaultColor=tools.TGREENLIT)) - + elif self.verbosity >= 1: print("") info = [f" :: GeckoLoader set at address 0x{self.initAddress:X}", - f" :: Legacy size is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}", + f" :: Legacy size is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.gct.byteSize:X}", f" :: Codehandler is of type `{codeHandler.type}'"] for bit in info: print(tools.color_text(bit, defaultColor=tools.TGREENLIT)) + def determine_codehook(dolFile: DolFile, codeHandler: CodeHandler, hook=False) -> bool: if codeHandler.hookAddress is None: if not assert_code_hook(dolFile, codeHandler): return False - + if hook: codeHandler.set_variables(dolFile) insert_code_hook(dolFile, codeHandler, codeHandler.hookAddress) @@ -620,25 +587,27 @@ def assert_code_hook(dolFile: DolFile, codeHandler: CodeHandler) -> bool: sample = dolFile.read(section["size"]) if codeHandler.hookType == "VI": - result = sample.find(codeHandler.gcnVIHook) + result = sample.find(codeHandler.GCNVIHook) elif codeHandler.hookType == "GX": - result = sample.find(codeHandler.gcnGXDrawHook) + result = sample.find(codeHandler.GCNGXDrawHook) elif codeHandler.hookType == "PAD": - result = sample.find(codeHandler.gcnPADHook) + result = sample.find(codeHandler.GCNPADHook) else: - raise NotImplementedError(tools.color_text(f"Unsupported hook type specified ({codeHandler.hookType})", defaultColor=tools.TREDLIT)) + raise NotImplementedError(tools.color_text( + f"Unsupported hook type specified ({codeHandler.hookType})", defaultColor=tools.TREDLIT)) if result >= 0: dolFile.seek(section["address"] + result) else: if codeHandler.hookType == "VI": - result = sample.find(codeHandler.wiiVIHook) + result = sample.find(codeHandler.WiiVIHook) elif codeHandler.hookType == "GX": - result = sample.find(codeHandler.wiiGXDrawHook) + result = sample.find(codeHandler.WiiGXDrawHook) elif codeHandler.hookType == "PAD": - result = sample.find(codeHandler.wiiPADHook) + result = sample.find(codeHandler.WiiPADHook) else: - raise NotImplementedError(tools.color_text(f"Unsupported hook type specified ({codeHandler.hookType})", defaultColor=tools.TREDLIT)) + raise NotImplementedError(tools.color_text( + f"Unsupported hook type specified ({codeHandler.hookType})", defaultColor=tools.TREDLIT)) if result >= 0: dolFile.seek(section["address"] + result) @@ -654,12 +623,14 @@ def assert_code_hook(dolFile: DolFile, codeHandler: CodeHandler) -> bool: return True return False + def insert_code_hook(dolFile: DolFile, codeHandler: CodeHandler, address: int): dolFile.seek(address) ppc = read_uint32(dolFile) if ((ppc >> 24) & 0xFF) > 0x3F and ((ppc >> 24) & 0xFF) < 0x48: - raise NotImplementedError(tools.color_text("Hooking the codehandler to a conditional non spr branch is unsupported", defaultColor=tools.TREDLIT)) + raise NotImplementedError(tools.color_text( + "Hooking the codehandler to a conditional non spr branch is unsupported", defaultColor=tools.TREDLIT)) dolFile.seek(-4, 1) dolFile.insert_branch(codeHandler.startAddress, address, lk=0) From fc424f81321595e5bb150ae3f949e7dc8a4a5a4f Mon Sep 17 00:00:00 2001 From: JoshuaMK Date: Tue, 20 Apr 2021 01:11:05 -0500 Subject: [PATCH 02/17] Create framework and implementation of write types --- geckocode.py | 597 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 597 insertions(+) create mode 100644 geckocode.py diff --git a/geckocode.py b/geckocode.py new file mode 100644 index 0000000..5b7d762 --- /dev/null +++ b/geckocode.py @@ -0,0 +1,597 @@ +from enum import Enum +from io import BytesIO +from pathlib import Path +from typing import Any, 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 + 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_I = 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 + TERMINATE = 0xE0 + ENDIF = 0xE2 + EXIT = 0xF0 + ASM_INSERT_XOR = 0xF2 + BRAINSLUG_SEARCH = 0xF6 + + @staticmethod + def int_to_type(id: int) -> Type: + return GeckoCode.Type(id & 0xEE) + + @staticmethod + def is_ifblock(_type: Type) -> bool: + 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) -> bool: + 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 is_preprocess_allowed(_type) -> bool: + 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 + } + + def __init__(self): + raise InvalidGeckoCodeError( + f"Cannot instantiate abstract type {self.__class__.__name__}") + + def __len__(self): + return 0 + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + 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 = 0x80000000, isPointer: bool = False): + self.value = value + self.address = address + self.repeat = repeat + self.isPointer = isPointer + + def __len__(self): + return 8 + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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): + if dol.is_mapped(self.address): + dol.seek(self.address) + 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 = 0x80000000, isPointer: bool = False): + self.value = value + self.address = address + self.repeat = repeat + self.isPointer = isPointer + + def __len__(self): + return 8 + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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): + if dol.is_mapped(self.address): + dol.seek(self.address) + 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 = 0x80000000, isPointer: bool = False): + self.value = value + self.address = address + self.isPointer = isPointer + + def __len__(self): + return 8 + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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): + if dol.is_mapped(self.address): + dol.seek(self.address) + dol.write(self.value.to_bytes(4, "big", signed=False)) + return True + return False + +class WriteString(GeckoCode): + def __init__(self, value: Union[int, bytes], address: int = 0x80000000, isPointer: bool = False): + self.value = value + self.address = address + self.isPointer = isPointer + + def __len__(self): + return 8 + len(self.value) + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + def __getitem__(self, index: int) -> bytes: + return self.value[index] + + def __setitem__(self, index: int, value: Union[int, 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: Union[int, bytes]): + if isinstance(value, int): + value = (value & 0xFFFFFFFF).to_bytes(4, "big", signed=False) + self._value = value + + def virtual_length(self) -> int: + return ((len(self) + 7) & -0x8) >> 3 + + def apply(self, dol: DolFile) -> bool: + if dol.is_mapped(self.address): + dol.seek(self.address) + dol.write(self.value) + return True + return False + +class WriteSerial(GeckoCode): + def __init__(self, value: Union[int, bytes], repeat: int = 0, address: int = 0x80000000, 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): + return 16 + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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 + 1 + + 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, int): + value = (value & 0xFFFFFFFF).to_bytes(4, "big", signed=False) + self._value = value + + def virtual_length(self) -> int: + return 2 + + def apply(self, dol: DolFile) -> bool: + if dol.is_mapped(self.address): + for addr, value in self: + dol.seek(addr) + dol.write(value) + return True + return False + + """ + try: + if codetype.hex().startswith("2") or codetype.hex().startswith("3"): + skipcodes += 1 + + elif codetype.startswith(b"\xE0"): + skipcodes -= 1 + + elif codetype.startswith(b"\xF0"): + codelist += b"\xF0\x00\x00\x00\x00\x00\x00\x00" + break + + self._rawData.seek(-8, 1) + codelist += self._rawData.read( + GCT.determine_codelength(codetype, info)) + + except (RuntimeError, UnmappedAddressError): + self._rawData.seek(-8, 1) + codelist += self._rawData.read( + GCT.determine_codelength(codetype, info)) + """ + + """ --------------- """ + + """ + def add_child(self, child: "GeckoCode"): + if self.is_ifblock(): + raise InvalidGeckoCodeError( + "Non IF type code can't contain children") + self._children.append(child) + + def remove_child(self, child: "GeckoCode"): + if self.is_ifblock(): + raise InvalidGeckoCodeError( + "Non IF type code can't contain children") + self._children.remove(child) + + def virtual_length(self) -> int: + if self.is_multiline(): + return (len(self) >> 3) + 1 + elif self.is_ifblock(): + return len(self.children) + 1 + else: + return 1 + + def populate_from_bytes(self, f: IO): + while metadata := f.read(4): + info = self._rawData.read(4) + address = 0x80000000 | (int.from_bytes( + metadata, byteorder="big", signed=False) & 0x1FFFFFF) + codetype = (int.from_bytes( + metadata, "big", signed=False) >> 24) & 0xFF + isPointerType = (codetype & 0x10 != 0) + + if (codetype & 0xEF) <= 0x0F: + self.add_child(GeckoCode(GeckoCode.Type.WRITE, + info, address, isPointerType)) + elif (codetype & 0xEF) <= 0x2F: + ifBlock = GeckoCode(GeckoCode.Type.IF, info, + address, isPointerType) + ifBlock.populate_from_bytes(f) + self.add_child(GeckoCode(GeckoCode.Type.IF, + info, address, isPointerType)) + elif (codetype & 0xEF) <= 0xC5: + self.add_child(GeckoCode(GeckoCode.Type.ASM, + info, address, isPointerType)) + elif (codetype & 0xEF) <= 0xC7: + self.add_child(GeckoCode(GeckoCode.Type.BRANCH, + info, address, isPointerType)) + elif (codetype & 0xEF) in {0xE0, 0xE2}: + break + + def apply(self, dol: DolFile, preprocess: bool = True): + if not self.is_preprocess_allowed(): + return + + if dol.is_mapped(self.address): + dol.seek(self.address) + if self._type in {GeckoCode.Type.WRITE_8, GeckoCode.Type.WRITE_16}: + counter = int.from_bytes( + self.info, byteorder="big", signed=False) + while counter + 1 > 0: + dol.write(self.data) + counter -= 1 + elif self._type in {GeckoCode.Type.WRITE_32, GeckoCode.Type.WRITE_STR}: + dol.write(self.data) + elif self._type == GeckoCode.Type.WRITE_SERIAL: + value = int.from_bytes( + self.info[:4], byteorder="big", signed=False) + _data = int.from_bytes( + self.info[4:6], byteorder="big", signed=False) + size = (_data & 0x3000) >> 12 + counter = _data & 0xFFF + addressIncrement = int.from_bytes( + self.info[6:8], byteorder="big", signed=False) + valueIncrement = int.from_bytes( + self.info[8:12], byteorder="big", signed=False) + while counter + 1 > 0: + if size == 0: + write_ubyte(dol, value & 0xFF) + dol.seek(-1, 1) + elif size == 1: + write_uint16(dol, value & 0xFFFF) + dol.seek(-2, 1) + elif size == 2: + write_uint32(dol, value) + dol.seek(-4, 1) + else: + raise ValueError( + "Size type {} does not match 08 codetype specs".format(size)) + + dol.seek(addressIncrement, 1) + value += valueIncrement + counter -= 1 + if value > 0xFFFFFFFF: + value -= 0x100000000 + elif self._type == GeckoCode.Type.WRITE_BRANCH: + dol.insert_branch(int.from_bytes( + self.info, byteorder="big", signed=False), self.address, lk=(self.address & 1) == 1) + """ From 7588875b3a9e8c83114d8531d64cc93da8c0b553 Mon Sep 17 00:00:00 2001 From: JoshuaMK Date: Fri, 30 Apr 2021 02:31:57 -0500 Subject: [PATCH 03/17] Add __repr__ method, misc updates --- geckocode.py | 166 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 139 insertions(+), 27 deletions(-) diff --git a/geckocode.py b/geckocode.py index 5b7d762..4123fe4 100644 --- a/geckocode.py +++ b/geckocode.py @@ -1,7 +1,7 @@ from enum import Enum from io import BytesIO from pathlib import Path -from typing import Any, IO, List, Tuple, Union +from typing import Any, Generator, IO, List, Tuple, Union from dolreader import DolFile from fileutils import (write_ubyte, write_uint16, write_uint32) @@ -114,10 +114,52 @@ def is_preprocess_allowed(_type) -> bool: GeckoCode.Type.WRITE_BRANCH } + @staticmethod + def bytes_to_codelist(f: IO) -> Generator["GeckoCode"]: + while 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) & 0xFF) + 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) + def __init__(self): raise InvalidGeckoCodeError( f"Cannot instantiate abstract type {self.__class__.__name__}") + def __repr__(self) -> str: + return self.__class__.__name__ + def __len__(self): return 0 @@ -176,6 +218,12 @@ def __init__(self, value: Union[int, bytes], repeat: int = 0, address: int = 0x8 self.repeat = repeat self.isPointer = isPointer + def __repr__(self) -> str: + if self.repeat > 0: + return f"(00) Write byte 0x{self.value:2X} to 0x{self.address:8X} {self.repeat + 1} times consecutively" + else: + return f"(00) Write byte 0x{self.value:2X} to 0x{self.address:8X}" + def __len__(self): return 8 @@ -232,6 +280,7 @@ def apply(self, dol: DolFile): return True return False + class Write16(GeckoCode): def __init__(self, value: Union[int, bytes], repeat: int = 0, address: int = 0x80000000, isPointer: bool = False): self.value = value @@ -242,6 +291,12 @@ def __init__(self, value: Union[int, bytes], repeat: int = 0, address: int = 0x8 def __len__(self): return 8 + def __repr__(self) -> str: + if self.repeat > 0: + return f"(02) Write short 0x{self.value:4X} to 0x{self.address:8X} {self.repeat + 1} times consecutively" + else: + return f"(02) Write short 0x{self.value:4X} to 0x{self.address:8X}" + def __iter__(self): self._iterpos = 0 return self @@ -295,6 +350,7 @@ def apply(self, dol: DolFile): return True return False + class Write32(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0x80000000, isPointer: bool = False): self.value = value @@ -304,6 +360,9 @@ def __init__(self, value: Union[int, bytes], address: int = 0x80000000, isPointe def __len__(self): return 8 + def __repr__(self) -> str: + return f"(04) Write word 0x{self.value:8X} to 0x{self.address:8X}" + def __iter__(self): self._iterpos = 0 return self @@ -354,6 +413,7 @@ def apply(self, dol: DolFile): return True return False + class WriteString(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0x80000000, isPointer: bool = False): self.value = value @@ -363,6 +423,9 @@ def __init__(self, value: Union[int, bytes], address: int = 0x80000000, isPointe def __len__(self): return 8 + len(self.value) + def __repr__(self) -> str: + return f"(06) Write {len(self) - 8} bytes to 0x{self.address:8X}" + def __iter__(self): self._iterpos = 0 return self @@ -405,7 +468,8 @@ def apply(self, dol: DolFile) -> bool: dol.write(self.value) return True return False - + + class WriteSerial(GeckoCode): def __init__(self, value: Union[int, bytes], repeat: int = 0, address: int = 0x80000000, isPointer: bool = False, valueSize: int = 2, addrInc: int = 4, valueInc: int = 0): @@ -420,6 +484,14 @@ def __init__(self, value: Union[int, bytes], repeat: int = 0, address: int = 0x8 def __len__(self): return 16 + def __repr__(self) -> str: + 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"(08) Write {valueType} 0x{self.value:8X} to 0x{self.address:8X} {self.repeat + 1} times consecutively, {mapping}" + else: + return f"(08) Write {valueType} 0x{self.value:8X} to 0x{self.address:8X}" + def __iter__(self): self._iterpos = 0 return self @@ -475,6 +547,70 @@ def apply(self, dol: DolFile) -> bool: return True return False + +class IfEqual32(GeckoCode): + def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False): + self.value = value + self.address = address + self.endif = endif + self._children = [] + + def __len__(self): + return sum([len(c) for c in self]) + + def __repr__(self) -> str: + return f"(20) If the word at address 0x{self.address:8X} is equal to 0x{self.value:08X}, run the encapsulated codes" + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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): + pass + """ try: if codetype.hex().startswith("2") or codetype.hex().startswith("3"): @@ -521,31 +657,7 @@ def virtual_length(self) -> int: return 1 def populate_from_bytes(self, f: IO): - while metadata := f.read(4): - info = self._rawData.read(4) - address = 0x80000000 | (int.from_bytes( - metadata, byteorder="big", signed=False) & 0x1FFFFFF) - codetype = (int.from_bytes( - metadata, "big", signed=False) >> 24) & 0xFF - isPointerType = (codetype & 0x10 != 0) - - if (codetype & 0xEF) <= 0x0F: - self.add_child(GeckoCode(GeckoCode.Type.WRITE, - info, address, isPointerType)) - elif (codetype & 0xEF) <= 0x2F: - ifBlock = GeckoCode(GeckoCode.Type.IF, info, - address, isPointerType) - ifBlock.populate_from_bytes(f) - self.add_child(GeckoCode(GeckoCode.Type.IF, - info, address, isPointerType)) - elif (codetype & 0xEF) <= 0xC5: - self.add_child(GeckoCode(GeckoCode.Type.ASM, - info, address, isPointerType)) - elif (codetype & 0xEF) <= 0xC7: - self.add_child(GeckoCode(GeckoCode.Type.BRANCH, - info, address, isPointerType)) - elif (codetype & 0xEF) in {0xE0, 0xE2}: - break + def apply(self, dol: DolFile, preprocess: bool = True): if not self.is_preprocess_allowed(): From 16ad088efca934d7588b21cd424fcec3feef63a8 Mon Sep 17 00:00:00 2001 From: JoshuaMK Date: Fri, 30 Apr 2021 12:43:08 -0500 Subject: [PATCH 04/17] Provide if types --- geckocode.py | 493 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 488 insertions(+), 5 deletions(-) diff --git a/geckocode.py b/geckocode.py index 4123fe4..9ecd657 100644 --- a/geckocode.py +++ b/geckocode.py @@ -415,7 +415,7 @@ def apply(self, dol: DolFile): class WriteString(GeckoCode): - def __init__(self, value: Union[int, bytes], address: int = 0x80000000, isPointer: bool = False): + def __init__(self, value: bytes, address: int = 0x80000000, isPointer: bool = False): self.value = value self.address = address self.isPointer = isPointer @@ -439,7 +439,7 @@ def __next__(self): def __getitem__(self, index: int) -> bytes: return self.value[index] - def __setitem__(self, index: int, value: Union[int, bytes]): + 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__}") @@ -454,9 +454,7 @@ def value(self) -> bytes: return self._value @value.setter - def value(self, value: Union[int, bytes]): - if isinstance(value, int): - value = (value & 0xFFFFFFFF).to_bytes(4, "big", signed=False) + def value(self, value: bytes): self._value = value def virtual_length(self) -> int: @@ -611,6 +609,491 @@ def virtual_length(self) -> int: def populate_from_bytes(self, f: IO): pass + def apply(self, dol: DolFile) -> bool: + for code in self: + code.apply(dol) + return True + +class IfNotEqual32(GeckoCode): + def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False): + self.value = value + self.address = address + self.endif = endif + self._children = [] + + def __len__(self): + return sum([len(c) for c in self]) + + def __repr__(self) -> str: + return f"(22) If the word at address 0x{self.address:8X} is not equal to 0x{self.value:08X}, run the encapsulated codes" + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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): + pass + + def apply(self, dol: DolFile) -> bool: + for code in self: + code.apply(dol) + return True + +class IfGreaterThan32(GeckoCode): + def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False): + self.value = value + self.address = address + self.endif = endif + self._children = [] + + def __len__(self): + return sum([len(c) for c in self]) + + def __repr__(self) -> str: + return f"(24) If the word at address 0x{self.address:8X} is greater than 0x{self.value:08X}, run the encapsulated codes" + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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): + pass + + def apply(self, dol: DolFile) -> bool: + for code in self: + code.apply(dol) + return True + +class IfLesserThan32(GeckoCode): + def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False): + self.value = value + self.address = address + self.endif = endif + self._children = [] + + def __len__(self): + return sum([len(c) for c in self]) + + def __repr__(self) -> str: + return f"(26) If the word at address 0x{self.address:8X} is lesser than 0x{self.value:08X}, run the encapsulated codes" + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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): + pass + + def apply(self, dol: DolFile) -> bool: + for code in self: + code.apply(dol) + return True + +class IfEqual16(GeckoCode): + def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False, mask: int = 0xFFFF): + self.value = value + self.address = address + self.endif = endif + self.mask = mask + self._children = [] + + def __len__(self): + return sum([len(c) for c in self]) + + def __repr__(self) -> str: + return f"(28) If the short at address 0x{self.address:8X} is equal to (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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 & 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): + pass + + def apply(self, dol: DolFile) -> bool: + for code in self: + code.apply(dol) + return True + +class IfNotEqual16(GeckoCode): + def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False, mask: int = 0xFFFF): + self.value = value + self.address = address + self.endif = endif + self.mask = mask + self._children = [] + + def __len__(self): + return sum([len(c) for c in self]) + + def __repr__(self) -> str: + return f"(2A) If the short at address 0x{self.address:8X} is not equal to (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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 & 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): + pass + + def apply(self, dol: DolFile) -> bool: + for code in self: + code.apply(dol) + return True + +class IfGreaterThan16(GeckoCode): + def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False, mask: int = 0xFFFF): + self.value = value + self.address = address + self.endif = endif + self.mask = mask + self._children = [] + + def __len__(self): + return sum([len(c) for c in self]) + + def __repr__(self) -> str: + return f"(2C) If the short at address 0x{self.address:8X} is greater than (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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 & 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): + pass + + def apply(self, dol: DolFile) -> bool: + for code in self: + code.apply(dol) + return True + +class IfLesserThan16(GeckoCode): + def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False, mask: int = 0xFFFF): + self.value = value + self.address = address + self.endif = endif + self.mask = mask + self._children = [] + + def __len__(self): + return sum([len(c) for c in self]) + + def __repr__(self) -> str: + return f"(2E) If the short at address 0x{self.address:8X} is lesser than (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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 & 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): + pass + + def apply(self, dol: DolFile) -> bool: + for code in self: + code.apply(dol) + return True + """ try: if codetype.hex().startswith("2") or codetype.hex().startswith("3"): From 6d0820b121cc4a17724832b2858a8e05e7269e14 Mon Sep 17 00:00:00 2001 From: JoshuaMK Date: Fri, 30 Apr 2021 12:45:34 -0500 Subject: [PATCH 05/17] Add if types to static converter --- geckocode.py | 102 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 65 insertions(+), 37 deletions(-) diff --git a/geckocode.py b/geckocode.py index 9ecd657..0601b8b 100644 --- a/geckocode.py +++ b/geckocode.py @@ -115,43 +115,71 @@ def is_preprocess_allowed(_type) -> bool: } @staticmethod - def bytes_to_codelist(f: IO) -> Generator["GeckoCode"]: - while 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) & 0xFF) - 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) + def bytes_to_geckocode(f: IO) -> Generator["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) & 0xFF) + 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 IfEqual32(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 IfEqual32(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 IfEqual32(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 IfEqual32(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 IfEqual32(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 IfEqual32(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 IfEqual32(value, address, endif=(address & 1) == 1) def __init__(self): raise InvalidGeckoCodeError( From c49569c447c6cae424895b683958cd445ca8150a Mon Sep 17 00:00:00 2001 From: JoshuaMK Date: Sat, 1 May 2021 00:50:20 -0500 Subject: [PATCH 06/17] Add ba/po access types --- geckocode.py | 533 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 533 insertions(+) diff --git a/geckocode.py b/geckocode.py index 0601b8b..16aaeea 100644 --- a/geckocode.py +++ b/geckocode.py @@ -29,6 +29,7 @@ class Type(Enum): 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 @@ -1122,6 +1123,538 @@ def apply(self, dol: DolFile) -> bool: code.apply(dol) return True +class BaseAddressLoad(GeckoCode): + def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, isPointer: bool = False): + self.value = value + self.flags = flags + self.register = register + self.isPointer = isPointer + + def __repr__(self) -> str: + addrstr = "pointer address" if self.isPointer else "base address" + flags = self.flags + if flags == 0b000: + return f"(40) Set the base address to the value at address [0x{self.value}]" + if flags == 0b001: + return f"(40) Set the base address to the value at address [gr{self.register} + 0x{self.value}]" + if flags == 0b010: + return f"(40) Set the base address to the value at address [{addrstr} + 0x{self.value}]" + if flags == 0b011: + return f"(40) Set the base address to the value at address [{addrstr} + gr{self.register} + 0x{self.value}]" + if flags == 0b100: + return f"(40) Add the value at address [0x{self.value}] to the base address" + if flags == 0b101: + return f"(40) Add the value at address [gr{self.register} + 0x{self.value}] to the base address" + if flags == 0b110: + return f"(40) Add the value at address [{addrstr} + 0x{self.value}] to the base address" + if flags == 0b111: + return f"(40) Add the value at address [{addrstr} + gr{self.register} + 0x{self.value}] to the base address" + + def __len__(self): + return 8 + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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 = 0x80000000, flags: int = 0, register: int = 0, isPointer: bool = False): + self.value = value + self.flags = flags + self.register = register + self.isPointer = isPointer + + def __repr__(self) -> str: + addrstr = "pointer address" if self.isPointer else "base address" + flags = self.flags + if flags == 0b000: + return f"(42) Set the base address to the value 0x{self.value}" + elif flags == 0b001: + return f"(42) Set the base address to the value (gr{self.register} + 0x{self.value})" + elif flags == 0b010: + return f"(42) Set the base address to the value ({addrstr} + 0x{self.value})" + elif flags == 0b011: + return f"(42) Set the base address to the value ({addrstr} + gr{self.register} + 0x{self.value})" + elif flags == 0b100: + return f"(42) Add the value 0x{self.value} to the base address" + elif flags == 0b101: + return f"(42) Add the value (gr{self.register} + 0x{self.value}) to the base address" + elif flags == 0b110: + return f"(42) Add the value ({addrstr} + 0x{self.value}) to the base address" + elif flags == 0b111: + return f"(42) Add the value ({addrstr} + gr{self.register}) + 0x{self.value} to the base address" + return f"(42) Invalid flag {flags}" + + def __len__(self): + return 8 + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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 = 0x80000000, flags: int = 0, register: int = 0, isPointer: bool = False): + self.value = value + self.flags = flags + self.register = register + self.isPointer = isPointer + + def __repr__(self) -> str: + addrstr = "pointer address" if self.isPointer else "base address" + flags = self.flags + if flags == 0b000: + return f"(44) Store the base address at address [0x{self.value}]" + elif flags == 0b001: + return f"(44) Store the base address at address [gr{self.register} + 0x{self.value}]" + elif flags == 0b010: + return f"(44) Store the base address at address [{addrstr} + 0x{self.value}]" + elif flags == 0b011: + return f"(44) Store the base address at address [{addrstr} + gr{self.register} + 0x{self.value}]" + return f"(44) Invalid flag {flags}" + + def __len__(self): + return 8 + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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 = 0x80000000): + self.value = value + + def __repr__(self) -> str: + return f"(46) Set the base address to be the next Gecko Code's address + {self.value}" + + def __len__(self): + return 8 + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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 = 0x80000000, flags: int = 0, register: int = 0, isPointer: bool = False): + self.value = value + self.flags = flags + self.register = register + self.isPointer = isPointer + + def __repr__(self) -> str: + addrstr = "pointer address" if self.isPointer else "base address" + flags = self.flags + if flags == 0b000: + return f"(48) Set the pointer address to the value at address [0x{self.value}]" + if flags == 0b001: + return f"(48) Set the pointer address to the value at address [gr{self.register} + 0x{self.value}]" + if flags == 0b010: + return f"(48) Set the pointer address to the value at address [{addrstr} + 0x{self.value}]" + if flags == 0b011: + return f"(48) Set the pointer address to the value at address [{addrstr} + gr{self.register} + 0x{self.value}]" + if flags == 0b100: + return f"(48) Add the value at address [0x{self.value}] to the pointer address" + if flags == 0b101: + return f"(48) Add the value at address [gr{self.register} + 0x{self.value}] to the pointer address" + if flags == 0b110: + return f"(48) Add the value at address [{addrstr} + 0x{self.value}] to the pointer address" + if flags == 0b111: + return f"(48) Add the value at address [{addrstr} + gr{self.register} + 0x{self.value}] to the pointer address" + + def __len__(self): + return 8 + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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 = 0x80000000, flags: int = 0, register: int = 0, isPointer: bool = False): + self.value = value + self.flags = flags + self.register = register + self.isPointer = isPointer + + def __repr__(self) -> str: + addrstr = "pointer address" if self.isPointer else "base address" + flags = self.flags + if flags == 0b000: + return f"(4A) Set the pointer address to the value 0x{self.value}" + elif flags == 0b001: + return f"(4A) Set the pointer address to the value (gr{self.register} + 0x{self.value})" + elif flags == 0b010: + return f"(4A) Set the pointer address to the value ({addrstr} + 0x{self.value})" + elif flags == 0b011: + return f"(4A) Set the pointer address to the value ({addrstr} + gr{self.register} + 0x{self.value})" + elif flags == 0b100: + return f"(4A) Add the value 0x{self.value} to the pointer address" + elif flags == 0b101: + return f"(4A) Add the value (gr{self.register} + 0x{self.value}) to the pointer address" + elif flags == 0b110: + return f"(4A) Add the value ({addrstr} + 0x{self.value}) to the pointer address" + elif flags == 0b111: + return f"(4A) Add the value ({addrstr} + gr{self.register}) + 0x{self.value} to the pointer address" + return f"(4A) Invalid flag {flags}" + + def __len__(self): + return 8 + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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 = 0x80000000, flags: int = 0, register: int = 0, isPointer: bool = False): + self.value = value + self.flags = flags + self.register = register + self.isPointer = isPointer + + def __repr__(self) -> str: + addrstr = "pointer address" if self.isPointer else "base address" + flags = self.flags + if flags == 0b000: + return f"(4C) Store the pointer address at address [0x{self.value}]" + elif flags == 0b001: + return f"(4C) Store the pointer address at address [gr{self.register} + 0x{self.value}]" + elif flags == 0b010: + return f"(4C) Store the pointer address at address [{addrstr} + 0x{self.value}]" + elif flags == 0b011: + return f"(4C) Store the pointer address at address [{addrstr} + gr{self.register} + 0x{self.value}]" + return f"(4C) Invalid flag {flags}" + + def __len__(self): + return 8 + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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 = 0x80000000): + self.value = value + + def __repr__(self) -> str: + return f"(4E) Set the base address to be the next Gecko Code's address + {self.value}" + + def __len__(self): + return 8 + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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 + """ try: if codetype.hex().startswith("2") or codetype.hex().startswith("3"): From a9605a0981a831c028fbecfaf0f51655ad173ca0 Mon Sep 17 00:00:00 2001 From: JoshuaMK Date: Sat, 1 May 2021 01:02:36 -0500 Subject: [PATCH 07/17] Add 2 flow execution codetypes --- geckocode.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/geckocode.py b/geckocode.py index 16aaeea..ce5b256 100644 --- a/geckocode.py +++ b/geckocode.py @@ -643,6 +643,7 @@ def apply(self, dol: DolFile) -> bool: code.apply(dol) return True + class IfNotEqual32(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False): self.value = value @@ -711,6 +712,7 @@ def apply(self, dol: DolFile) -> bool: code.apply(dol) return True + class IfGreaterThan32(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False): self.value = value @@ -779,6 +781,7 @@ def apply(self, dol: DolFile) -> bool: code.apply(dol) return True + class IfLesserThan32(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False): self.value = value @@ -847,6 +850,7 @@ def apply(self, dol: DolFile) -> bool: code.apply(dol) return True + class IfEqual16(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False, mask: int = 0xFFFF): self.value = value @@ -916,6 +920,7 @@ def apply(self, dol: DolFile) -> bool: code.apply(dol) return True + class IfNotEqual16(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False, mask: int = 0xFFFF): self.value = value @@ -985,6 +990,7 @@ def apply(self, dol: DolFile) -> bool: code.apply(dol) return True + class IfGreaterThan16(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False, mask: int = 0xFFFF): self.value = value @@ -1054,6 +1060,7 @@ def apply(self, dol: DolFile) -> bool: code.apply(dol) return True + class IfLesserThan16(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False, mask: int = 0xFFFF): self.value = value @@ -1123,6 +1130,7 @@ def apply(self, dol: DolFile) -> bool: code.apply(dol) return True + class BaseAddressLoad(GeckoCode): def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, isPointer: bool = False): self.value = value @@ -1196,6 +1204,7 @@ def value(self, value: Union[int, bytes]): def virtual_length(self) -> int: return 1 + class BaseAddressSet(GeckoCode): def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, isPointer: bool = False): self.value = value @@ -1270,6 +1279,7 @@ def value(self, value: Union[int, bytes]): def virtual_length(self) -> int: return 1 + class BaseAddressStore(GeckoCode): def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, isPointer: bool = False): self.value = value @@ -1336,6 +1346,7 @@ def value(self, value: Union[int, bytes]): def virtual_length(self) -> int: return 1 + class BaseAddressGetNext(GeckoCode): def __init__(self, value: int = 0x80000000): self.value = value @@ -1389,6 +1400,7 @@ def value(self, value: Union[int, bytes]): def virtual_length(self) -> int: return 1 + class PointerAddressLoad(GeckoCode): def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, isPointer: bool = False): self.value = value @@ -1462,6 +1474,7 @@ def value(self, value: Union[int, bytes]): def virtual_length(self) -> int: return 1 + class PointerAddressSet(GeckoCode): def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, isPointer: bool = False): self.value = value @@ -1536,6 +1549,7 @@ def value(self, value: Union[int, bytes]): def virtual_length(self) -> int: return 1 + class PointerAddressStore(GeckoCode): def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, isPointer: bool = False): self.value = value @@ -1602,6 +1616,7 @@ def value(self, value: Union[int, bytes]): def virtual_length(self) -> int: return 1 + class PointerAddressGetNext(GeckoCode): def __init__(self, value: int = 0x80000000): self.value = value @@ -1655,6 +1670,43 @@ def value(self, value: Union[int, bytes]): 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 __repr__(self) -> str: + return f"(60) Store next code address and number of times to repeat in b{self.b}" + + def __len__(self): + 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 __repr__(self) -> str: + return f"(62) 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): + return 8 + + @property + def codetype(self) -> GeckoCode.Type: + return GeckoCode.Type.REPEAT_EXEC + + def virtual_length(self) -> int: + return 1 + """ try: if codetype.hex().startswith("2") or codetype.hex().startswith("3"): From 839837cd343779a4b53861e558249a3ad5e44c48 Mon Sep 17 00:00:00 2001 From: JoshuaMK Date: Sat, 1 May 2021 03:24:50 -0500 Subject: [PATCH 08/17] Change magic methods --- geckocode.py | 49 ++++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/geckocode.py b/geckocode.py index ce5b256..dba9bcf 100644 --- a/geckocode.py +++ b/geckocode.py @@ -187,6 +187,9 @@ def __init__(self): 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): @@ -247,7 +250,7 @@ def __init__(self, value: Union[int, bytes], repeat: int = 0, address: int = 0x8 self.repeat = repeat self.isPointer = isPointer - def __repr__(self) -> str: + def __str__(self) -> str: if self.repeat > 0: return f"(00) Write byte 0x{self.value:2X} to 0x{self.address:8X} {self.repeat + 1} times consecutively" else: @@ -320,7 +323,7 @@ def __init__(self, value: Union[int, bytes], repeat: int = 0, address: int = 0x8 def __len__(self): return 8 - def __repr__(self) -> str: + def __str__(self) -> str: if self.repeat > 0: return f"(02) Write short 0x{self.value:4X} to 0x{self.address:8X} {self.repeat + 1} times consecutively" else: @@ -389,7 +392,7 @@ def __init__(self, value: Union[int, bytes], address: int = 0x80000000, isPointe def __len__(self): return 8 - def __repr__(self) -> str: + def __str__(self) -> str: return f"(04) Write word 0x{self.value:8X} to 0x{self.address:8X}" def __iter__(self): @@ -452,7 +455,7 @@ def __init__(self, value: bytes, address: int = 0x80000000, isPointer: bool = Fa def __len__(self): return 8 + len(self.value) - def __repr__(self) -> str: + def __str__(self) -> str: return f"(06) Write {len(self) - 8} bytes to 0x{self.address:8X}" def __iter__(self): @@ -511,7 +514,7 @@ def __init__(self, value: Union[int, bytes], repeat: int = 0, address: int = 0x8 def __len__(self): return 16 - def __repr__(self) -> str: + def __str__(self) -> str: 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" @@ -585,7 +588,7 @@ def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: b def __len__(self): return sum([len(c) for c in self]) - def __repr__(self) -> str: + def __str__(self) -> str: return f"(20) If the word at address 0x{self.address:8X} is equal to 0x{self.value:08X}, run the encapsulated codes" def __iter__(self): @@ -654,7 +657,7 @@ def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: b def __len__(self): return sum([len(c) for c in self]) - def __repr__(self) -> str: + def __str__(self) -> str: return f"(22) If the word at address 0x{self.address:8X} is not equal to 0x{self.value:08X}, run the encapsulated codes" def __iter__(self): @@ -723,7 +726,7 @@ def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: b def __len__(self): return sum([len(c) for c in self]) - def __repr__(self) -> str: + def __str__(self) -> str: return f"(24) If the word at address 0x{self.address:8X} is greater than 0x{self.value:08X}, run the encapsulated codes" def __iter__(self): @@ -792,7 +795,7 @@ def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: b def __len__(self): return sum([len(c) for c in self]) - def __repr__(self) -> str: + def __str__(self) -> str: return f"(26) If the word at address 0x{self.address:8X} is lesser than 0x{self.value:08X}, run the encapsulated codes" def __iter__(self): @@ -862,7 +865,7 @@ def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: b def __len__(self): return sum([len(c) for c in self]) - def __repr__(self) -> str: + def __str__(self) -> str: return f"(28) If the short at address 0x{self.address:8X} is equal to (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" def __iter__(self): @@ -932,7 +935,7 @@ def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: b def __len__(self): return sum([len(c) for c in self]) - def __repr__(self) -> str: + def __str__(self) -> str: return f"(2A) If the short at address 0x{self.address:8X} is not equal to (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" def __iter__(self): @@ -1002,7 +1005,7 @@ def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: b def __len__(self): return sum([len(c) for c in self]) - def __repr__(self) -> str: + def __str__(self) -> str: return f"(2C) If the short at address 0x{self.address:8X} is greater than (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" def __iter__(self): @@ -1072,7 +1075,7 @@ def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: b def __len__(self): return sum([len(c) for c in self]) - def __repr__(self) -> str: + def __str__(self) -> str: return f"(2E) If the short at address 0x{self.address:8X} is lesser than (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" def __iter__(self): @@ -1138,7 +1141,7 @@ def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, i self.register = register self.isPointer = isPointer - def __repr__(self) -> str: + def __str__(self) -> str: addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags if flags == 0b000: @@ -1212,7 +1215,7 @@ def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, i self.register = register self.isPointer = isPointer - def __repr__(self) -> str: + def __str__(self) -> str: addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags if flags == 0b000: @@ -1287,7 +1290,7 @@ def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, i self.register = register self.isPointer = isPointer - def __repr__(self) -> str: + def __str__(self) -> str: addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags if flags == 0b000: @@ -1351,7 +1354,7 @@ class BaseAddressGetNext(GeckoCode): def __init__(self, value: int = 0x80000000): self.value = value - def __repr__(self) -> str: + def __str__(self) -> str: return f"(46) Set the base address to be the next Gecko Code's address + {self.value}" def __len__(self): @@ -1408,7 +1411,7 @@ def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, i self.register = register self.isPointer = isPointer - def __repr__(self) -> str: + def __str__(self) -> str: addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags if flags == 0b000: @@ -1482,7 +1485,7 @@ def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, i self.register = register self.isPointer = isPointer - def __repr__(self) -> str: + def __str__(self) -> str: addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags if flags == 0b000: @@ -1557,7 +1560,7 @@ def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, i self.register = register self.isPointer = isPointer - def __repr__(self) -> str: + def __str__(self) -> str: addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags if flags == 0b000: @@ -1621,7 +1624,7 @@ class PointerAddressGetNext(GeckoCode): def __init__(self, value: int = 0x80000000): self.value = value - def __repr__(self) -> str: + def __str__(self) -> str: return f"(4E) Set the base address to be the next Gecko Code's address + {self.value}" def __len__(self): @@ -1676,7 +1679,7 @@ def __init__(self, repeat: int = 0, b: int = 0): self.repeat = repeat self.b = b - def __repr__(self) -> str: + def __str__(self) -> str: return f"(60) Store next code address and number of times to repeat in b{self.b}" def __len__(self): @@ -1694,7 +1697,7 @@ class ExecuteRepeat(GeckoCode): def __init__(self, b: int = 0): self.b = b - def __repr__(self) -> str: + def __str__(self) -> str: return f"(62) 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): From d74042c33bd318b9df33e84d61076025592daee1 Mon Sep 17 00:00:00 2001 From: JoshuaMK Date: Sun, 2 May 2021 03:44:21 -0500 Subject: [PATCH 09/17] Add ba/po types to static method --- geckocode.py | 150 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 95 insertions(+), 55 deletions(-) diff --git a/geckocode.py b/geckocode.py index dba9bcf..70af0ff 100644 --- a/geckocode.py +++ b/geckocode.py @@ -156,31 +156,71 @@ def bytes_to_geckocode(f: IO) -> Generator["GeckoCode"]: elif codetype == GeckoCode.Type.IF_NEQ_32: info = f.read(4) value = int.from_bytes(info, "big", signed=False) - return IfEqual32(value, address, endif=(address & 1) == 1) + 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 IfEqual32(value, address, endif=(address & 1) == 1) + 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 IfEqual32(value, address, endif=(address & 1) == 1) + 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 IfEqual32(value, address, endif=(address & 1) == 1) + 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 IfEqual32(value, address, endif=(address & 1) == 1) + 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 IfEqual32(value, address, endif=(address & 1) == 1) + 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 IfEqual32(value, address, endif=(address & 1) == 1) + 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) def __init__(self): raise InvalidGeckoCodeError( @@ -1135,7 +1175,7 @@ def apply(self, dol: DolFile) -> bool: class BaseAddressLoad(GeckoCode): - def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, isPointer: bool = False): + def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: bool = False): self.value = value self.flags = flags self.register = register @@ -1144,21 +1184,21 @@ def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, i def __str__(self) -> str: addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags - if flags == 0b000: + if flags == 0x000: return f"(40) Set the base address to the value at address [0x{self.value}]" - if flags == 0b001: + if flags == 0x001: return f"(40) Set the base address to the value at address [gr{self.register} + 0x{self.value}]" - if flags == 0b010: + if flags == 0x010: return f"(40) Set the base address to the value at address [{addrstr} + 0x{self.value}]" - if flags == 0b011: + if flags == 0x011: return f"(40) Set the base address to the value at address [{addrstr} + gr{self.register} + 0x{self.value}]" - if flags == 0b100: + if flags == 0x100: return f"(40) Add the value at address [0x{self.value}] to the base address" - if flags == 0b101: + if flags == 0x101: return f"(40) Add the value at address [gr{self.register} + 0x{self.value}] to the base address" - if flags == 0b110: + if flags == 0x110: return f"(40) Add the value at address [{addrstr} + 0x{self.value}] to the base address" - if flags == 0b111: + if flags == 0x111: return f"(40) Add the value at address [{addrstr} + gr{self.register} + 0x{self.value}] to the base address" def __len__(self): @@ -1209,7 +1249,7 @@ def virtual_length(self) -> int: class BaseAddressSet(GeckoCode): - def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, isPointer: bool = False): + def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: bool = False): self.value = value self.flags = flags self.register = register @@ -1218,21 +1258,21 @@ def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, i def __str__(self) -> str: addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags - if flags == 0b000: + if flags == 0x000: return f"(42) Set the base address to the value 0x{self.value}" - elif flags == 0b001: + elif flags == 0x001: return f"(42) Set the base address to the value (gr{self.register} + 0x{self.value})" - elif flags == 0b010: + elif flags == 0x010: return f"(42) Set the base address to the value ({addrstr} + 0x{self.value})" - elif flags == 0b011: + elif flags == 0x011: return f"(42) Set the base address to the value ({addrstr} + gr{self.register} + 0x{self.value})" - elif flags == 0b100: + elif flags == 0x100: return f"(42) Add the value 0x{self.value} to the base address" - elif flags == 0b101: + elif flags == 0x101: return f"(42) Add the value (gr{self.register} + 0x{self.value}) to the base address" - elif flags == 0b110: + elif flags == 0x110: return f"(42) Add the value ({addrstr} + 0x{self.value}) to the base address" - elif flags == 0b111: + elif flags == 0x111: return f"(42) Add the value ({addrstr} + gr{self.register}) + 0x{self.value} to the base address" return f"(42) Invalid flag {flags}" @@ -1284,7 +1324,7 @@ def virtual_length(self) -> int: class BaseAddressStore(GeckoCode): - def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, isPointer: bool = False): + def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: bool = False): self.value = value self.flags = flags self.register = register @@ -1293,13 +1333,13 @@ def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, i def __str__(self) -> str: addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags - if flags == 0b000: + if flags == 0x000: return f"(44) Store the base address at address [0x{self.value}]" - elif flags == 0b001: + elif flags == 0x001: return f"(44) Store the base address at address [gr{self.register} + 0x{self.value}]" - elif flags == 0b010: + elif flags == 0x010: return f"(44) Store the base address at address [{addrstr} + 0x{self.value}]" - elif flags == 0b011: + elif flags == 0x011: return f"(44) Store the base address at address [{addrstr} + gr{self.register} + 0x{self.value}]" return f"(44) Invalid flag {flags}" @@ -1351,7 +1391,7 @@ def virtual_length(self) -> int: class BaseAddressGetNext(GeckoCode): - def __init__(self, value: int = 0x80000000): + def __init__(self, value: int): self.value = value def __str__(self) -> str: @@ -1405,7 +1445,7 @@ def virtual_length(self) -> int: class PointerAddressLoad(GeckoCode): - def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, isPointer: bool = False): + def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: bool = False): self.value = value self.flags = flags self.register = register @@ -1414,21 +1454,21 @@ def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, i def __str__(self) -> str: addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags - if flags == 0b000: + if flags == 0x000: return f"(48) Set the pointer address to the value at address [0x{self.value}]" - if flags == 0b001: + if flags == 0x001: return f"(48) Set the pointer address to the value at address [gr{self.register} + 0x{self.value}]" - if flags == 0b010: + if flags == 0x010: return f"(48) Set the pointer address to the value at address [{addrstr} + 0x{self.value}]" - if flags == 0b011: + if flags == 0x011: return f"(48) Set the pointer address to the value at address [{addrstr} + gr{self.register} + 0x{self.value}]" - if flags == 0b100: + if flags == 0x100: return f"(48) Add the value at address [0x{self.value}] to the pointer address" - if flags == 0b101: + if flags == 0x101: return f"(48) Add the value at address [gr{self.register} + 0x{self.value}] to the pointer address" - if flags == 0b110: + if flags == 0x110: return f"(48) Add the value at address [{addrstr} + 0x{self.value}] to the pointer address" - if flags == 0b111: + if flags == 0x111: return f"(48) Add the value at address [{addrstr} + gr{self.register} + 0x{self.value}] to the pointer address" def __len__(self): @@ -1479,7 +1519,7 @@ def virtual_length(self) -> int: class PointerAddressSet(GeckoCode): - def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, isPointer: bool = False): + def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: bool = False): self.value = value self.flags = flags self.register = register @@ -1488,21 +1528,21 @@ def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, i def __str__(self) -> str: addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags - if flags == 0b000: + if flags == 0x000: return f"(4A) Set the pointer address to the value 0x{self.value}" - elif flags == 0b001: + elif flags == 0x001: return f"(4A) Set the pointer address to the value (gr{self.register} + 0x{self.value})" - elif flags == 0b010: + elif flags == 0x010: return f"(4A) Set the pointer address to the value ({addrstr} + 0x{self.value})" - elif flags == 0b011: + elif flags == 0x011: return f"(4A) Set the pointer address to the value ({addrstr} + gr{self.register} + 0x{self.value})" - elif flags == 0b100: + elif flags == 0x100: return f"(4A) Add the value 0x{self.value} to the pointer address" - elif flags == 0b101: + elif flags == 0x101: return f"(4A) Add the value (gr{self.register} + 0x{self.value}) to the pointer address" - elif flags == 0b110: + elif flags == 0x110: return f"(4A) Add the value ({addrstr} + 0x{self.value}) to the pointer address" - elif flags == 0b111: + elif flags == 0x111: return f"(4A) Add the value ({addrstr} + gr{self.register}) + 0x{self.value} to the pointer address" return f"(4A) Invalid flag {flags}" @@ -1554,7 +1594,7 @@ def virtual_length(self) -> int: class PointerAddressStore(GeckoCode): - def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, isPointer: bool = False): + def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: bool = False): self.value = value self.flags = flags self.register = register @@ -1563,13 +1603,13 @@ def __init__(self, value: int = 0x80000000, flags: int = 0, register: int = 0, i def __str__(self) -> str: addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags - if flags == 0b000: + if flags == 0x000: return f"(4C) Store the pointer address at address [0x{self.value}]" - elif flags == 0b001: + elif flags == 0x001: return f"(4C) Store the pointer address at address [gr{self.register} + 0x{self.value}]" - elif flags == 0b010: + elif flags == 0x010: return f"(4C) Store the pointer address at address [{addrstr} + 0x{self.value}]" - elif flags == 0b011: + elif flags == 0x011: return f"(4C) Store the pointer address at address [{addrstr} + gr{self.register} + 0x{self.value}]" return f"(4C) Invalid flag {flags}" @@ -1621,7 +1661,7 @@ def virtual_length(self) -> int: class PointerAddressGetNext(GeckoCode): - def __init__(self, value: int = 0x80000000): + def __init__(self, value: int): self.value = value def __str__(self) -> str: From 0b50c5345bc563cab5ba572bf4e0243cb06aaf50 Mon Sep 17 00:00:00 2001 From: JoshuaMK Date: Thu, 6 May 2021 22:37:35 -0500 Subject: [PATCH 10/17] Add more codetypes --- geckocode.py | 527 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 433 insertions(+), 94 deletions(-) diff --git a/geckocode.py b/geckocode.py index 70af0ff..9d620c5 100644 --- a/geckocode.py +++ b/geckocode.py @@ -71,7 +71,14 @@ def int_to_type(id: int) -> Type: return GeckoCode.Type(id & 0xEE) @staticmethod - def is_ifblock(_type: Type) -> bool: + 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, @@ -93,7 +100,10 @@ def is_ifblock(_type: Type) -> bool: } @staticmethod - def is_multiline(_type) -> bool: + 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, @@ -105,7 +115,10 @@ def is_multiline(_type) -> bool: } @staticmethod - def is_preprocess_allowed(_type) -> bool: + def is_preprocess_allowed(_type: Union[Type, "GeckoCode"]) -> bool: + if isinstance(_type, GeckoCode): + _type = _type.codetype + return _type in { GeckoCode.Type.WRITE_8, GeckoCode.Type.WRITE_16, @@ -115,6 +128,10 @@ def is_preprocess_allowed(_type) -> bool: GeckoCode.Type.WRITE_BRANCH } + @staticmethod + def typeof(code: "GeckoCode") -> Type: + return code.codetype + @staticmethod def bytes_to_geckocode(f: IO) -> Generator["GeckoCode"]: metadata = f.read(4) @@ -284,17 +301,19 @@ def apply(self, dol: DolFile) -> bool: class Write8(GeckoCode): - def __init__(self, value: Union[int, bytes], repeat: int = 0, address: int = 0x80000000, isPointer: bool = False): + 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"(00) Write byte 0x{self.value:2X} to 0x{self.address:8X} {self.repeat + 1} times consecutively" + return f"({intType:X}) Write byte 0x{self.value:2X} to (0x{self.address:8X} + the {addrstr}) {self.repeat + 1} times consecutively" else: - return f"(00) Write byte 0x{self.value:2X} to 0x{self.address:8X}" + return f"({intType:X}) Write byte 0x{self.value:2X} to 0x{self.address:8X} + the {addrstr}" def __len__(self): return 8 @@ -354,7 +373,7 @@ def apply(self, dol: DolFile): class Write16(GeckoCode): - def __init__(self, value: Union[int, bytes], repeat: int = 0, address: int = 0x80000000, isPointer: bool = False): + 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 @@ -364,10 +383,12 @@ def __len__(self): 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"(02) Write short 0x{self.value:4X} to 0x{self.address:8X} {self.repeat + 1} times consecutively" + return f"({intType:X}) Write short 0x{self.value:4X} to (0x{self.address:8X} + the {addrstr}) {self.repeat + 1} times consecutively" else: - return f"(02) Write short 0x{self.value:4X} to 0x{self.address:8X}" + return f"({intType:X}) Write short 0x{self.value:4X} to 0x{self.address:8X} + the {addrstr}" def __iter__(self): self._iterpos = 0 @@ -424,7 +445,7 @@ def apply(self, dol: DolFile): class Write32(GeckoCode): - def __init__(self, value: Union[int, bytes], address: int = 0x80000000, isPointer: bool = False): + def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = False): self.value = value self.address = address self.isPointer = isPointer @@ -433,7 +454,9 @@ def __len__(self): return 8 def __str__(self) -> str: - return f"(04) Write word 0x{self.value:8X} to 0x{self.address:8X}" + 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:X}) Write word 0x{self.value:8X} to 0x{self.address:8X} + the {addrstr}" def __iter__(self): self._iterpos = 0 @@ -487,7 +510,7 @@ def apply(self, dol: DolFile): class WriteString(GeckoCode): - def __init__(self, value: bytes, address: int = 0x80000000, isPointer: bool = False): + def __init__(self, value: bytes, address: int = 0, isPointer: bool = False): self.value = value self.address = address self.isPointer = isPointer @@ -496,7 +519,9 @@ def __len__(self): return 8 + len(self.value) def __str__(self) -> str: - return f"(06) Write {len(self) - 8} bytes to 0x{self.address:8X}" + 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:X}) Write {len(self) - 8} bytes to 0x{self.address:8X} + the {addrstr}" def __iter__(self): self._iterpos = 0 @@ -541,7 +566,7 @@ def apply(self, dol: DolFile) -> bool: class WriteSerial(GeckoCode): - def __init__(self, value: Union[int, bytes], repeat: int = 0, address: int = 0x80000000, isPointer: bool = False, + 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 @@ -555,12 +580,14 @@ def __len__(self): 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"(08) Write {valueType} 0x{self.value:8X} to 0x{self.address:8X} {self.repeat + 1} times consecutively, {mapping}" + return f"({intType:X}) Write {valueType} 0x{self.value:8X} to (0x{self.address:8X} + the {addrstr}) {self.repeat + 1} times consecutively, {mapping}" else: - return f"(08) Write {valueType} 0x{self.value:8X} to 0x{self.address:8X}" + return f"({intType:X}) Write {valueType} 0x{self.value:8X} to 0x{self.address:8X} + the {addrstr})" def __iter__(self): self._iterpos = 0 @@ -619,17 +646,21 @@ def apply(self, dol: DolFile) -> bool: class IfEqual32(GeckoCode): - def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False): + 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): return sum([len(c) for c in self]) def __str__(self) -> str: - return f"(20) If the word at address 0x{self.address:8X} is equal to 0x{self.value:08X}, run the encapsulated codes" + 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:X}) If the word at address (0x{self.address:8X} + the {addrstr}) is equal to 0x{self.value:08X}, run the encapsulated codes" def __iter__(self): self._iterpos = 0 @@ -688,17 +719,21 @@ def apply(self, dol: DolFile) -> bool: class IfNotEqual32(GeckoCode): - def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False): + 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): return sum([len(c) for c in self]) def __str__(self) -> str: - return f"(22) If the word at address 0x{self.address:8X} is not equal to 0x{self.value:08X}, run the encapsulated codes" + 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:X}) If the word at address (0x{self.address:8X} + the {addrstr}) is not equal to 0x{self.value:08X}, run the encapsulated codes" def __iter__(self): self._iterpos = 0 @@ -757,17 +792,21 @@ def apply(self, dol: DolFile) -> bool: class IfGreaterThan32(GeckoCode): - def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False): + 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): return sum([len(c) for c in self]) def __str__(self) -> str: - return f"(24) If the word at address 0x{self.address:8X} is greater than 0x{self.value:08X}, run the encapsulated codes" + 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:X}) If the word at address (0x{self.address:8X} + the {addrstr}) is greater than 0x{self.value:08X}, run the encapsulated codes" def __iter__(self): self._iterpos = 0 @@ -826,17 +865,21 @@ def apply(self, dol: DolFile) -> bool: class IfLesserThan32(GeckoCode): - def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False): + 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): return sum([len(c) for c in self]) def __str__(self) -> str: - return f"(26) If the word at address 0x{self.address:8X} is lesser than 0x{self.value:08X}, run the encapsulated codes" + 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:X}) If the word at address (0x{self.address:8X} + the {addrstr}) is lesser than 0x{self.value:08X}, run the encapsulated codes" def __iter__(self): self._iterpos = 0 @@ -895,18 +938,22 @@ def apply(self, dol: DolFile) -> bool: class IfEqual16(GeckoCode): - def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False, mask: int = 0xFFFF): + 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): return sum([len(c) for c in self]) def __str__(self) -> str: - return f"(28) If the short at address 0x{self.address:8X} is equal to (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" + 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:X}) If the short at address (0x{self.address:8X} + the {addrstr}) is equal to (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" def __iter__(self): self._iterpos = 0 @@ -965,18 +1012,22 @@ def apply(self, dol: DolFile) -> bool: class IfNotEqual16(GeckoCode): - def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False, mask: int = 0xFFFF): + 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): return sum([len(c) for c in self]) def __str__(self) -> str: - return f"(2A) If the short at address 0x{self.address:8X} is not equal to (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" + 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:X}) If the short at address (0x{self.address:8X} + the {addrstr}) is not equal to (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" def __iter__(self): self._iterpos = 0 @@ -1035,18 +1086,22 @@ def apply(self, dol: DolFile) -> bool: class IfGreaterThan16(GeckoCode): - def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False, mask: int = 0xFFFF): + 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): return sum([len(c) for c in self]) def __str__(self) -> str: - return f"(2C) If the short at address 0x{self.address:8X} is greater than (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" + 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:X}) If the short at address (0x{self.address:8X} + the {addrstr}) is greater than (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" def __iter__(self): self._iterpos = 0 @@ -1105,18 +1160,22 @@ def apply(self, dol: DolFile) -> bool: class IfLesserThan16(GeckoCode): - def __init__(self, value: Union[int, bytes], address: int = 0x80000000, endif: bool = False, mask: int = 0xFFFF): + 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): return sum([len(c) for c in self]) def __str__(self) -> str: - return f"(2E) If the short at address 0x{self.address:8X} is lesser than (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" + 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:X}) If the short at address (0x{self.address:8X} + the {addrstr}) is lesser than (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" def __iter__(self): self._iterpos = 0 @@ -1182,24 +1241,25 @@ def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: boo 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"(40) Set the base address to the value at address [0x{self.value}]" - if flags == 0x001: - return f"(40) Set the base address to the value at address [gr{self.register} + 0x{self.value}]" - if flags == 0x010: - return f"(40) Set the base address to the value at address [{addrstr} + 0x{self.value}]" - if flags == 0x011: - return f"(40) Set the base address to the value at address [{addrstr} + gr{self.register} + 0x{self.value}]" - if flags == 0x100: - return f"(40) Add the value at address [0x{self.value}] to the base address" - if flags == 0x101: - return f"(40) Add the value at address [gr{self.register} + 0x{self.value}] to the base address" - if flags == 0x110: - return f"(40) Add the value at address [{addrstr} + 0x{self.value}] to the base address" - if flags == 0x111: - return f"(40) Add the value at address [{addrstr} + gr{self.register} + 0x{self.value}] to the base address" + return f"({intType:X}) Set the base address to the value at address [0x{self.value}]" + elif flags == 0x001: + return f"({intType:X}) Set the base address to the value at address [gr{self.register} + 0x{self.value}]" + elif flags == 0x010: + return f"({intType:X}) Set the base address to the value at address [{addrstr} + 0x{self.value}]" + elif flags == 0x011: + return f"({intType:X}) Set the base address to the value at address [{addrstr} + gr{self.register} + 0x{self.value}]" + elif flags == 0x100: + return f"({intType:X}) Add the value at address [0x{self.value}] to the base address" + elif flags == 0x101: + return f"({intType:X}) Add the value at address [gr{self.register} + 0x{self.value}] to the base address" + elif flags == 0x110: + return f"({intType:X}) Add the value at address [{addrstr} + 0x{self.value}] to the base address" + elif flags == 0x111: + return f"({intType:X}) Add the value at address [{addrstr} + gr{self.register} + 0x{self.value}] to the base address" def __len__(self): return 8 @@ -1256,25 +1316,26 @@ def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: boo 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"(42) Set the base address to the value 0x{self.value}" + return f"({intType:X}) Set the base address to the value 0x{self.value}" elif flags == 0x001: - return f"(42) Set the base address to the value (gr{self.register} + 0x{self.value})" + return f"({intType:X}) Set the base address to the value (gr{self.register} + 0x{self.value})" elif flags == 0x010: - return f"(42) Set the base address to the value ({addrstr} + 0x{self.value})" + return f"({intType:X}) Set the base address to the value ({addrstr} + 0x{self.value})" elif flags == 0x011: - return f"(42) Set the base address to the value ({addrstr} + gr{self.register} + 0x{self.value})" + return f"({intType:X}) Set the base address to the value ({addrstr} + gr{self.register} + 0x{self.value})" elif flags == 0x100: - return f"(42) Add the value 0x{self.value} to the base address" + return f"({intType:X}) Add the value 0x{self.value} to the base address" elif flags == 0x101: - return f"(42) Add the value (gr{self.register} + 0x{self.value}) to the base address" + return f"({intType:X}) Add the value (gr{self.register} + 0x{self.value}) to the base address" elif flags == 0x110: - return f"(42) Add the value ({addrstr} + 0x{self.value}) to the base address" + return f"({intType:X}) Add the value ({addrstr} + 0x{self.value}) to the base address" elif flags == 0x111: - return f"(42) Add the value ({addrstr} + gr{self.register}) + 0x{self.value} to the base address" - return f"(42) Invalid flag {flags}" + return f"({intType:X}) Add the value ({addrstr} + gr{self.register}) + 0x{self.value} to the base address" + return f"({intType:X}) Invalid flag {flags}" def __len__(self): return 8 @@ -1331,17 +1392,18 @@ def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: boo 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"(44) Store the base address at address [0x{self.value}]" + return f"({intType:X}) Store the base address at address [0x{self.value}]" elif flags == 0x001: - return f"(44) Store the base address at address [gr{self.register} + 0x{self.value}]" + return f"({intType:X}) Store the base address at address [gr{self.register} + 0x{self.value}]" elif flags == 0x010: - return f"(44) Store the base address at address [{addrstr} + 0x{self.value}]" + return f"({intType:X}) Store the base address at address [{addrstr} + 0x{self.value}]" elif flags == 0x011: - return f"(44) Store the base address at address [{addrstr} + gr{self.register} + 0x{self.value}]" - return f"(44) Invalid flag {flags}" + return f"({intType:X}) Store the base address at address [{addrstr} + gr{self.register} + 0x{self.value}]" + return f"({intType:X}) Invalid flag {flags}" def __len__(self): return 8 @@ -1395,7 +1457,8 @@ def __init__(self, value: int): self.value = value def __str__(self) -> str: - return f"(46) Set the base address to be the next Gecko Code's address + {self.value}" + intType = GeckoCode.type_to_int(self.codetype) + return f"({intType:X}) Set the base address to be the next Gecko Code's address + {self.value}" def __len__(self): return 8 @@ -1452,24 +1515,25 @@ def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: boo 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"(48) Set the pointer address to the value at address [0x{self.value}]" - if flags == 0x001: - return f"(48) Set the pointer address to the value at address [gr{self.register} + 0x{self.value}]" - if flags == 0x010: - return f"(48) Set the pointer address to the value at address [{addrstr} + 0x{self.value}]" - if flags == 0x011: - return f"(48) Set the pointer address to the value at address [{addrstr} + gr{self.register} + 0x{self.value}]" - if flags == 0x100: - return f"(48) Add the value at address [0x{self.value}] to the pointer address" - if flags == 0x101: - return f"(48) Add the value at address [gr{self.register} + 0x{self.value}] to the pointer address" - if flags == 0x110: - return f"(48) Add the value at address [{addrstr} + 0x{self.value}] to the pointer address" - if flags == 0x111: - return f"(48) Add the value at address [{addrstr} + gr{self.register} + 0x{self.value}] to the pointer address" + return f"({intType:X}) Set the pointer address to the value at address [0x{self.value}]" + elif flags == 0x001: + return f"({intType:X}) Set the pointer address to the value at address [gr{self.register} + 0x{self.value}]" + elif flags == 0x010: + return f"({intType:X}) Set the pointer address to the value at address [{addrstr} + 0x{self.value}]" + elif flags == 0x011: + return f"({intType:X}) Set the pointer address to the value at address [{addrstr} + gr{self.register} + 0x{self.value}]" + elif flags == 0x100: + return f"({intType:X}) Add the value at address [0x{self.value}] to the pointer address" + elif flags == 0x101: + return f"({intType:X}) Add the value at address [gr{self.register} + 0x{self.value}] to the pointer address" + elif flags == 0x110: + return f"({intType:X}) Add the value at address [{addrstr} + 0x{self.value}] to the pointer address" + elif flags == 0x111: + return f"({intType:X}) Add the value at address [{addrstr} + gr{self.register} + 0x{self.value}] to the pointer address" def __len__(self): return 8 @@ -1526,25 +1590,26 @@ def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: boo 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"(4A) Set the pointer address to the value 0x{self.value}" + return f"({intType:X}) Set the pointer address to the value 0x{self.value}" elif flags == 0x001: - return f"(4A) Set the pointer address to the value (gr{self.register} + 0x{self.value})" + return f"({intType:X}) Set the pointer address to the value (gr{self.register} + 0x{self.value})" elif flags == 0x010: - return f"(4A) Set the pointer address to the value ({addrstr} + 0x{self.value})" + return f"({intType:X}) Set the pointer address to the value ({addrstr} + 0x{self.value})" elif flags == 0x011: - return f"(4A) Set the pointer address to the value ({addrstr} + gr{self.register} + 0x{self.value})" + return f"({intType:X}) Set the pointer address to the value ({addrstr} + gr{self.register} + 0x{self.value})" elif flags == 0x100: - return f"(4A) Add the value 0x{self.value} to the pointer address" + return f"({intType:X}) Add the value 0x{self.value} to the pointer address" elif flags == 0x101: - return f"(4A) Add the value (gr{self.register} + 0x{self.value}) to the pointer address" + return f"({intType:X}) Add the value (gr{self.register} + 0x{self.value}) to the pointer address" elif flags == 0x110: - return f"(4A) Add the value ({addrstr} + 0x{self.value}) to the pointer address" + return f"({intType:X}) Add the value ({addrstr} + 0x{self.value}) to the pointer address" elif flags == 0x111: - return f"(4A) Add the value ({addrstr} + gr{self.register}) + 0x{self.value} to the pointer address" - return f"(4A) Invalid flag {flags}" + return f"({intType:X}) Add the value ({addrstr} + gr{self.register}) + 0x{self.value} to the pointer address" + return f"({intType:X}) Invalid flag {flags}" def __len__(self): return 8 @@ -1601,17 +1666,18 @@ def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: boo 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"(4C) Store the pointer address at address [0x{self.value}]" + return f"({intType:X}) Store the pointer address at address [0x{self.value}]" elif flags == 0x001: - return f"(4C) Store the pointer address at address [gr{self.register} + 0x{self.value}]" + return f"({intType:X}) Store the pointer address at address [gr{self.register} + 0x{self.value}]" elif flags == 0x010: - return f"(4C) Store the pointer address at address [{addrstr} + 0x{self.value}]" + return f"({intType:X}) Store the pointer address at address [{addrstr} + 0x{self.value}]" elif flags == 0x011: - return f"(4C) Store the pointer address at address [{addrstr} + gr{self.register} + 0x{self.value}]" - return f"(4C) Invalid flag {flags}" + return f"({intType:X}) Store the pointer address at address [{addrstr} + gr{self.register} + 0x{self.value}]" + return f"({intType:X}) Invalid flag {flags}" def __len__(self): return 8 @@ -1665,7 +1731,8 @@ def __init__(self, value: int): self.value = value def __str__(self) -> str: - return f"(4E) Set the base address to be the next Gecko Code's address + {self.value}" + intType = GeckoCode.type_to_int(self.codetype) + return f"({intType:X}) Set the base address to be the next Gecko Code's address + {self.value}" def __len__(self): return 8 @@ -1720,7 +1787,8 @@ def __init__(self, repeat: int = 0, b: int = 0): self.b = b def __str__(self) -> str: - return f"(60) Store next code address and number of times to repeat in b{self.b}" + intType = GeckoCode.type_to_int(self.codetype) + return f"({intType:X}) Store next code address and number of times to repeat in b{self.b}" def __len__(self): return 8 @@ -1738,7 +1806,8 @@ def __init__(self, b: int = 0): self.b = b def __str__(self) -> str: - return f"(62) 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}" + intType = GeckoCode.type_to_int(self.codetype) + return f"({intType:X}) 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): return 8 @@ -1750,6 +1819,276 @@ def codetype(self) -> GeckoCode.Type: 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:X}) 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:X}) 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:X}) Jump to the next code address stored in b{self.b} (NNNN in bP is not touched)" + + def __len__(self): + 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:X}) If the code execution status is true, jump to (next line of code + {self.offset} lines)" + elif self.flags == 1: + return f"({intType:X}) If the code execution status is false, jump to (next line of code + {self.offset} lines)" + elif self.flags == 2: + return f"({intType:X}) Jump to (next line of code + {self.offset} lines)" + + def __len__(self): + return 8 + + @property + def codetype(self) -> GeckoCode.Type: + return GeckoCode.Type.GOTO + + def virtual_length(self) -> int: + return 1 + + +class GeckoRegisterSet(GeckoCode): + def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: bool = False): + 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:X}) Set Gecko Register {self.register} to the value 0x{self.value}" + elif flags == 0x01: + return f"({intType:X}) Set Gecko Register {self.register} to the value (0x{self.value} + the {addrstr})" + elif flags == 0x10: + return f"({intType:X}) Add the value 0x{self.value} to Gecko Register {self.register}" + elif flags == 0x11: + return f"({intType:X}) Add the value (0x{self.value} + the {addrstr}) to Gecko Register {self.register}" + return f"({intType:X}) Invalid flag {flags}" + + def __len__(self): + return 8 + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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): + 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:X}) Set Gecko Register {self.register} to the byte at address 0x{self.value}" + elif flags == 0x10: + return f"({intType:X}) Set Gecko Register {self.register} to the short at address 0x{self.value}" + elif flags == 0x20: + return f"({intType:X}) Set Gecko Register {self.register} to the word at address 0x{self.value}" + elif flags == 0x01: + return f"({intType:X}) Set Gecko Register {self.register} to the byte at address (0x{self.value} + the {addrstr})" + elif flags == 0x11: + return f"({intType:X}) Set Gecko Register {self.register} to the short at address (0x{self.value} + the {addrstr})" + elif flags == 0x21: + return f"({intType:X}) Set Gecko Register {self.register} to the word at address (0x{self.value} + the {addrstr})" + return f"({intType:X}) Invalid flag {flags}" + + def __len__(self): + return 8 + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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): + 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:X}) Invalid flag {flags}" + + if self.repeat > 0: + if flags & 0x01: + return f"({intType:X}) Store Gecko Register {self.register}'s {valueType} to [0x{self.value} + the {addrstr}] {self.repeat + 1} times consecutively" + else: + return f"({intType:X}) Store Gecko Register {self.register}'s {valueType} to [0x{self.value}] {self.repeat + 1} times consecutively" + else: + if flags & 0x01: + return f"({intType:X}) Store Gecko Register {self.register}'s {valueType} to [0x{self.value} + the {addrstr}]" + else: + return f"({intType:X}) Store Gecko Register {self.register}'s {valueType} to [0x{self.value}]" + + def __len__(self): + return 8 + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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 + + + + """ try: if codetype.hex().startswith("2") or codetype.hex().startswith("3"): From c9af54e84cac756f9991567db942abace820f7f9 Mon Sep 17 00:00:00 2001 From: JoshuaMK Date: Fri, 7 May 2021 02:11:33 -0500 Subject: [PATCH 11/17] Add rest of Gecko Register types --- geckocode.py | 253 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 226 insertions(+), 27 deletions(-) diff --git a/geckocode.py b/geckocode.py index 9d620c5..1e125a1 100644 --- a/geckocode.py +++ b/geckocode.py @@ -43,7 +43,7 @@ class Type(Enum): GECKO_REG_LOAD = 0x82 GECKO_REG_STORE = 0x84 GECKO_REG_OPERATE_I = 0x86 - GECKO_REG_OPERATE_I = 0x88 + GECKO_REG_OPERATE = 0x88 MEMCPY_1 = 0x8A MEMCPY_2 = 0x8C GECKO_IF_EQ_16 = 0xA0 @@ -66,6 +66,19 @@ class Type(Enum): 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: return GeckoCode.Type(id & 0xEE) @@ -103,7 +116,7 @@ def is_ifblock(_type: Union[Type, "GeckoCode"]) -> bool: 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, @@ -118,7 +131,7 @@ def is_multiline(_type: Union[Type, "GeckoCode"]) -> bool: def is_preprocess_allowed(_type: Union[Type, "GeckoCode"]) -> bool: if isinstance(_type, GeckoCode): _type = _type.codetype - + return _type in { GeckoCode.Type.WRITE_8, GeckoCode.Type.WRITE_16, @@ -308,7 +321,8 @@ def __init__(self, value: Union[int, bytes], repeat: int = 0, address: int = 0, self.isPointer = isPointer def __str__(self) -> str: - intType = GeckoCode.type_to_int(self.codetype) | (0x10 if self.isPointer else 0) + 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:X}) Write byte 0x{self.value:2X} to (0x{self.address:8X} + the {addrstr}) {self.repeat + 1} times consecutively" @@ -383,7 +397,8 @@ def __len__(self): return 8 def __str__(self) -> str: - intType = GeckoCode.type_to_int(self.codetype) | (0x10 if self.isPointer else 0) + 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:X}) Write short 0x{self.value:4X} to (0x{self.address:8X} + the {addrstr}) {self.repeat + 1} times consecutively" @@ -454,7 +469,8 @@ def __len__(self): return 8 def __str__(self) -> str: - intType = GeckoCode.type_to_int(self.codetype) | (0x10 if self.isPointer else 0) + 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:X}) Write word 0x{self.value:8X} to 0x{self.address:8X} + the {addrstr}" @@ -519,7 +535,8 @@ def __len__(self): return 8 + len(self.value) def __str__(self) -> str: - intType = GeckoCode.type_to_int(self.codetype) | (0x10 if self.isPointer else 0) + 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:X}) Write {len(self) - 8} bytes to 0x{self.address:8X} + the {addrstr}" @@ -580,7 +597,8 @@ def __len__(self): return 16 def __str__(self) -> str: - intType = GeckoCode.type_to_int(self.codetype) | (0x10 if self.isPointer else 0) + 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: @@ -658,7 +676,8 @@ def __len__(self): return sum([len(c) for c in self]) def __str__(self) -> str: - intType = GeckoCode.type_to_int(self.codetype) | (0x10 if self.isPointer else 0) + 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:X}) If the word at address (0x{self.address:8X} + the {addrstr}) is equal to 0x{self.value:08X}, run the encapsulated codes" @@ -731,7 +750,8 @@ def __len__(self): return sum([len(c) for c in self]) def __str__(self) -> str: - intType = GeckoCode.type_to_int(self.codetype) | (0x10 if self.isPointer else 0) + 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:X}) If the word at address (0x{self.address:8X} + the {addrstr}) is not equal to 0x{self.value:08X}, run the encapsulated codes" @@ -804,7 +824,8 @@ def __len__(self): return sum([len(c) for c in self]) def __str__(self) -> str: - intType = GeckoCode.type_to_int(self.codetype) | (0x10 if self.isPointer else 0) + 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:X}) If the word at address (0x{self.address:8X} + the {addrstr}) is greater than 0x{self.value:08X}, run the encapsulated codes" @@ -877,7 +898,8 @@ def __len__(self): return sum([len(c) for c in self]) def __str__(self) -> str: - intType = GeckoCode.type_to_int(self.codetype) | (0x10 if self.isPointer else 0) + 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:X}) If the word at address (0x{self.address:8X} + the {addrstr}) is lesser than 0x{self.value:08X}, run the encapsulated codes" @@ -951,7 +973,8 @@ def __len__(self): return sum([len(c) for c in self]) def __str__(self) -> str: - intType = GeckoCode.type_to_int(self.codetype) | (0x10 if self.isPointer else 0) + 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:X}) If the short at address (0x{self.address:8X} + the {addrstr}) is equal to (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" @@ -1025,7 +1048,8 @@ def __len__(self): return sum([len(c) for c in self]) def __str__(self) -> str: - intType = GeckoCode.type_to_int(self.codetype) | (0x10 if self.isPointer else 0) + 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:X}) If the short at address (0x{self.address:8X} + the {addrstr}) is not equal to (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" @@ -1099,7 +1123,8 @@ def __len__(self): return sum([len(c) for c in self]) def __str__(self) -> str: - intType = GeckoCode.type_to_int(self.codetype) | (0x10 if self.isPointer else 0) + 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:X}) If the short at address (0x{self.address:8X} + the {addrstr}) is greater than (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" @@ -1173,7 +1198,8 @@ def __len__(self): return sum([len(c) for c in self]) def __str__(self) -> str: - intType = GeckoCode.type_to_int(self.codetype) | (0x10 if self.isPointer else 0) + 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:X}) If the short at address (0x{self.address:8X} + the {addrstr}) is lesser than (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" @@ -1241,7 +1267,8 @@ def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: boo self.isPointer = isPointer def __str__(self) -> str: - intType = GeckoCode.type_to_int(self.codetype) | (0x10 if self.isPointer else 0) + 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: @@ -1316,7 +1343,8 @@ def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: boo self.isPointer = isPointer def __str__(self) -> str: - intType = GeckoCode.type_to_int(self.codetype) | (0x10 if self.isPointer else 0) + 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: @@ -1392,7 +1420,8 @@ def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: boo self.isPointer = isPointer def __str__(self) -> str: - intType = GeckoCode.type_to_int(self.codetype) | (0x10 if self.isPointer else 0) + 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: @@ -1515,7 +1544,8 @@ def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: boo self.isPointer = isPointer def __str__(self) -> str: - intType = GeckoCode.type_to_int(self.codetype) | (0x10 if self.isPointer else 0) + 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: @@ -1590,7 +1620,8 @@ def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: boo self.isPointer = isPointer def __str__(self) -> str: - intType = GeckoCode.type_to_int(self.codetype) | (0x10 if self.isPointer else 0) + 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: @@ -1666,7 +1697,8 @@ def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: boo self.isPointer = isPointer def __str__(self) -> str: - intType = GeckoCode.type_to_int(self.codetype) | (0x10 if self.isPointer else 0) + 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: @@ -1878,7 +1910,8 @@ def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: boo self.isPointer = isPointer def __str__(self) -> str: - intType = GeckoCode.type_to_int(self.codetype) | (0x10 if self.isPointer else 0) + 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: @@ -1946,7 +1979,8 @@ def __init__(self, value: int, flags: int = 0, register: int = 0, isPointer: boo self.isPointer = isPointer def __str__(self) -> str: - intType = GeckoCode.type_to_int(self.codetype) | (0x10 if self.isPointer else 0) + 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: @@ -2021,10 +2055,11 @@ def __init__(self, value: int, repeat: int = 0, flags: int = 0, self.isPointer = isPointer def __str__(self) -> str: - intType = GeckoCode.type_to_int(self.codetype) | (0x10 if self.isPointer else 0) + 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:X}) Invalid flag {flags}" @@ -2071,7 +2106,7 @@ def __setitem__(self, index: int, value: Union[int, bytes]): @property def codetype(self) -> GeckoCode.Type: - return GeckoCode.Type.PTR_ADDR_STORE + return GeckoCode.Type.GECKO_REG_STORE @property def value(self) -> int: @@ -2087,6 +2122,170 @@ def virtual_length(self) -> int: return 1 +class GeckoRegisterOperateI(GeckoCode): + def __init__(self, value: int, opType: GeckoCode.ArithmeticType, flags: int = 0, register: int = 0): + 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:X}) Add {valueAccessType} to {grAccessType}" + elif opType == GeckoCode.ArithmeticType.MUL: + return f"({intType:X}) Multiply {grAccessType} by {valueAccessType}" + elif opType == GeckoCode.ArithmeticType.OR: + return f"({intType:X}) OR {grAccessType} with {valueAccessType}" + elif opType == GeckoCode.ArithmeticType.XOR: + return f"({intType:X}) XOR {grAccessType} with {valueAccessType}" + elif opType == GeckoCode.ArithmeticType.SLW: + return f"({intType:X}) Shift {grAccessType} left by {valueAccessType} bits" + elif opType == GeckoCode.ArithmeticType.SRW: + return f"({intType:X}) Shift {grAccessType} right by {valueAccessType} bits" + elif opType == GeckoCode.ArithmeticType.ROL: + return f"({intType:X}) Rotate {grAccessType} left by {valueAccessType} bits" + elif opType == GeckoCode.ArithmeticType.ASR: + return f"({intType:X}) Arithmetic shift {grAccessType} right by {valueAccessType} bits" + elif opType == GeckoCode.ArithmeticType.FADDS: + return f"({intType:X}) Add {valueAccessType} to {grAccessType} as a float" + elif opType == GeckoCode.ArithmeticType.FMULS: + return f"({intType:X}) Multiply {grAccessType} by {valueAccessType} as a float" + return f"({intType:X}) Invalid operation flag {opType}" + + def __len__(self): + return 8 + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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): + 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:X}) Add {valueAccessType} to {grAccessType}" + elif opType == GeckoCode.ArithmeticType.MUL: + return f"({intType:X}) Multiply {grAccessType} by {valueAccessType}" + elif opType == GeckoCode.ArithmeticType.OR: + return f"({intType:X}) OR {grAccessType} with {valueAccessType}" + elif opType == GeckoCode.ArithmeticType.XOR: + return f"({intType:X}) XOR {grAccessType} with {valueAccessType}" + elif opType == GeckoCode.ArithmeticType.SLW: + return f"({intType:X}) Shift {grAccessType} left by {valueAccessType}" + elif opType == GeckoCode.ArithmeticType.SRW: + return f"({intType:X}) Shift {grAccessType} right by {valueAccessType}" + elif opType == GeckoCode.ArithmeticType.ROL: + return f"({intType:X}) Rotate {grAccessType} left by {valueAccessType}" + elif opType == GeckoCode.ArithmeticType.ASR: + return f"({intType:X}) Arithmetic shift {grAccessType} right by {valueAccessType}" + elif opType == GeckoCode.ArithmeticType.FADDS: + return f"({intType:X}) Add {valueAccessType} to {grAccessType} as a float" + elif opType == GeckoCode.ArithmeticType.FMULS: + return f"({intType:X}) Multiply {grAccessType} by {valueAccessType} as a float" + return f"({intType:X}) Invalid operation flag {opType}" + + def __len__(self): + return 8 + + def __iter__(self): + self._iterpos = 0 + return self + + def __next__(self): + try: + return self[self._iterpos] + except IndexError: + raise StopIteration + + 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 """ From 66cdcb468e37d507d6cb372d484f42f415864157 Mon Sep 17 00:00:00 2001 From: JoshuaMK Date: Tue, 11 May 2021 23:28:36 -0500 Subject: [PATCH 12/17] Finish adding codetypes, further develop code parser --- geckocode.py | 1952 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 1424 insertions(+), 528 deletions(-) diff --git a/geckocode.py b/geckocode.py index 1e125a1..fe92d19 100644 --- a/geckocode.py +++ b/geckocode.py @@ -128,7 +128,7 @@ def is_multiline(_type: Union[Type, "GeckoCode"]) -> bool: } @staticmethod - def is_preprocess_allowed(_type: Union[Type, "GeckoCode"]) -> bool: + def can_preprocess(_type: Union[Type, "GeckoCode"]) -> bool: if isinstance(_type, GeckoCode): _type = _type.codetype @@ -141,12 +141,16 @@ def is_preprocess_allowed(_type: Union[Type, "GeckoCode"]) -> bool: 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) -> Generator["GeckoCode"]: + def bytes_to_geckocode(f: IO) -> "GeckoCode": metadata = f.read(4) address = 0x80000000 | (int.from_bytes( metadata, byteorder="big", signed=False) & 0x1FFFFFF) @@ -251,6 +255,32 @@ def bytes_to_geckocode(f: IO) -> Generator["GeckoCode"]: 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) >> 18 + 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) >> 18 + 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) >> 18 + register = int.from_bytes(info, "big", signed=False) & 0xF + return Gosub(flags, value, register) + def __init__(self): raise InvalidGeckoCodeError( @@ -262,7 +292,7 @@ def __repr__(self) -> str: def __str__(self) -> str: return self.__class__.__name__ - def __len__(self): + def __len__(self) -> int: return 0 def __iter__(self): @@ -271,7 +301,8 @@ def __iter__(self): def __next__(self): try: - return self[self._iterpos] + self._iterpos += 1 + return self[self._iterpos-1] except IndexError: raise StopIteration @@ -325,23 +356,13 @@ def __str__(self) -> str: 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" if self.repeat > 0: - return f"({intType:X}) Write byte 0x{self.value:2X} to (0x{self.address:8X} + the {addrstr}) {self.repeat + 1} times consecutively" + 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:X}) Write byte 0x{self.value:2X} to 0x{self.address:8X} + the {addrstr}" + return f"({intType:02X}) Write byte 0x{self.value:02X} to 0x{self.address:08X} + the {addrstr}" - def __len__(self): + def __len__(self) -> int: return 8 - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration - def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( @@ -375,9 +396,10 @@ def value(self, value: Union[int, bytes]): def virtual_length(self) -> int: return 1 - def apply(self, dol: DolFile): - if dol.is_mapped(self.address): - dol.seek(self.address) + 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)) @@ -393,7 +415,7 @@ def __init__(self, value: Union[int, bytes], repeat: int = 0, address: int = 0, self.repeat = repeat self.isPointer = isPointer - def __len__(self): + def __len__(self) -> int: return 8 def __str__(self) -> str: @@ -401,19 +423,9 @@ def __str__(self) -> str: 0x10 if self.isPointer else 0) addrstr = "pointer address" if self.isPointer else "base address" if self.repeat > 0: - return f"({intType:X}) Write short 0x{self.value:4X} to (0x{self.address:8X} + the {addrstr}) {self.repeat + 1} times consecutively" + 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:X}) Write short 0x{self.value:4X} to 0x{self.address:8X} + the {addrstr}" - - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration + 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: @@ -448,9 +460,10 @@ def value(self, value: Union[int, bytes]): def virtual_length(self) -> int: return 1 - def apply(self, dol: DolFile): - if dol.is_mapped(self.address): - dol.seek(self.address) + 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)) @@ -465,24 +478,14 @@ def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = self.address = address self.isPointer = isPointer - def __len__(self): + 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:X}) Write word 0x{self.value:8X} to 0x{self.address:8X} + the {addrstr}" - - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration + 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: @@ -517,9 +520,10 @@ def value(self, value: Union[int, bytes]): def virtual_length(self) -> int: return 1 - def apply(self, dol: DolFile): - if dol.is_mapped(self.address): - dol.seek(self.address) + 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 @@ -531,24 +535,14 @@ def __init__(self, value: bytes, address: int = 0, isPointer: bool = False): self.address = address self.isPointer = isPointer - def __len__(self): + 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:X}) Write {len(self) - 8} bytes to 0x{self.address:8X} + the {addrstr}" - - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration + 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] @@ -575,8 +569,9 @@ def virtual_length(self) -> int: return ((len(self) + 7) & -0x8) >> 3 def apply(self, dol: DolFile) -> bool: - if dol.is_mapped(self.address): - dol.seek(self.address) + addr = self.address | 0x80000000 + if dol.is_mapped(addr): + dol.seek(addr) dol.write(self.value) return True return False @@ -593,7 +588,7 @@ def __init__(self, value: Union[int, bytes], repeat: int = 0, address: int = 0, self.repeat = repeat self.isPointer = isPointer - def __len__(self): + def __len__(self) -> int: return 16 def __str__(self) -> str: @@ -603,26 +598,16 @@ def __str__(self) -> str: 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:X}) Write {valueType} 0x{self.value:8X} to (0x{self.address:8X} + the {addrstr}) {self.repeat + 1} times consecutively, {mapping}" + 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:X}) Write {valueType} 0x{self.value:8X} to 0x{self.address:8X} + the {addrstr})" - - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration + 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 + 1 + index += self.repeat return (self.address + self.addressInc*index, self.value + self.valueInc*index) @@ -647,15 +632,16 @@ def value(self) -> int: @value.setter def value(self, value: Union[int, bytes]): - if isinstance(value, int): - value = (value & 0xFFFFFFFF).to_bytes(4, "big", signed=False) - self._value = value + 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: - if dol.is_mapped(self.address): + addr = self.address | 0x80000000 + if dol.is_mapped(addr): for addr, value in self: dol.seek(addr) dol.write(value) @@ -672,24 +658,15 @@ def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = self.isPointer = isPointer self._children = [] - def __len__(self): - return sum([len(c) for c in self]) + 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" - return f"({intType:X}) If the word at address (0x{self.address:8X} + the {addrstr}) is equal to 0x{self.value:08X}, run the encapsulated codes" - - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration + 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] @@ -711,13 +688,13 @@ def codetype(self) -> GeckoCode.Type: @property def value(self) -> int: - return self.value & 0xFFFFFFFF + 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 + self._value = value & 0xFFFFFFFF def add_child(self, child: "GeckoCode"): self._children.append(child) @@ -731,11 +708,6 @@ def virtual_length(self) -> int: def populate_from_bytes(self, f: IO): pass - def apply(self, dol: DolFile) -> bool: - for code in self: - code.apply(dol) - return True - class IfNotEqual32(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = False, @@ -746,24 +718,15 @@ def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = self.isPointer = isPointer self._children = [] - def __len__(self): - return sum([len(c) for c in self]) + 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" - return f"({intType:X}) If the word at address (0x{self.address:8X} + the {addrstr}) is not equal to 0x{self.value:08X}, run the encapsulated codes" - - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration + 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] @@ -785,13 +748,13 @@ def codetype(self) -> GeckoCode.Type: @property def value(self) -> int: - return self.value & 0xFFFFFFFF + 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 + self._value = value & 0xFFFFFFFF def add_child(self, child: "GeckoCode"): self._children.append(child) @@ -805,11 +768,6 @@ def virtual_length(self) -> int: def populate_from_bytes(self, f: IO): pass - def apply(self, dol: DolFile) -> bool: - for code in self: - code.apply(dol) - return True - class IfGreaterThan32(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = False, @@ -820,24 +778,15 @@ def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = self.isPointer = isPointer self._children = [] - def __len__(self): - return sum([len(c) for c in self]) + 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" - return f"({intType:X}) If the word at address (0x{self.address:8X} + the {addrstr}) is greater than 0x{self.value:08X}, run the encapsulated codes" - - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration + 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] @@ -859,13 +808,13 @@ def codetype(self) -> GeckoCode.Type: @property def value(self) -> int: - return self.value & 0xFFFFFFFF + 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 + self._value = value & 0xFFFFFFFF def add_child(self, child: "GeckoCode"): self._children.append(child) @@ -879,11 +828,6 @@ def virtual_length(self) -> int: def populate_from_bytes(self, f: IO): pass - def apply(self, dol: DolFile) -> bool: - for code in self: - code.apply(dol) - return True - class IfLesserThan32(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = False, @@ -894,24 +838,15 @@ def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = self.isPointer = isPointer self._children = [] - def __len__(self): - return sum([len(c) for c in self]) + 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" - return f"({intType:X}) If the word at address (0x{self.address:8X} + the {addrstr}) is lesser than 0x{self.value:08X}, run the encapsulated codes" - - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration + 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] @@ -933,13 +868,13 @@ def codetype(self) -> GeckoCode.Type: @property def value(self) -> int: - return self.value & 0xFFFFFFFF + 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 + self._value = value & 0xFFFFFFFF def add_child(self, child: "GeckoCode"): self._children.append(child) @@ -953,11 +888,6 @@ def virtual_length(self) -> int: def populate_from_bytes(self, f: IO): pass - def apply(self, dol: DolFile) -> bool: - for code in self: - code.apply(dol) - return True - class IfEqual16(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = False, @@ -969,24 +899,15 @@ def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = self.isPointer = isPointer self._children = [] - def __len__(self): - return sum([len(c) for c in self]) + 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" - return f"({intType:X}) If the short at address (0x{self.address:8X} + the {addrstr}) is equal to (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" - - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration + 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] @@ -1008,13 +929,13 @@ def codetype(self) -> GeckoCode.Type: @property def value(self) -> int: - return self.value & 0xFFFFFFFF + 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 & 0xFFFFFFFF + self._value = value & 0xFFFF def add_child(self, child: "GeckoCode"): self._children.append(child) @@ -1028,11 +949,6 @@ def virtual_length(self) -> int: def populate_from_bytes(self, f: IO): pass - def apply(self, dol: DolFile) -> bool: - for code in self: - code.apply(dol) - return True - class IfNotEqual16(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = False, @@ -1044,24 +960,15 @@ def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = self.isPointer = isPointer self._children = [] - def __len__(self): - return sum([len(c) for c in self]) + 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" - return f"({intType:X}) If the short at address (0x{self.address:8X} + the {addrstr}) is not equal to (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" - - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration + 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] @@ -1083,13 +990,13 @@ def codetype(self) -> GeckoCode.Type: @property def value(self) -> int: - return self.value & 0xFFFFFFFF + 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 & 0xFFFFFFFF + self._value = value & 0xFFFF def add_child(self, child: "GeckoCode"): self._children.append(child) @@ -1103,11 +1010,6 @@ def virtual_length(self) -> int: def populate_from_bytes(self, f: IO): pass - def apply(self, dol: DolFile) -> bool: - for code in self: - code.apply(dol) - return True - class IfGreaterThan16(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = False, @@ -1119,24 +1021,15 @@ def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = self.isPointer = isPointer self._children = [] - def __len__(self): - return sum([len(c) for c in self]) + 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" - return f"({intType:X}) If the short at address (0x{self.address:8X} + the {addrstr}) is greater than (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" - - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration + 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] @@ -1158,13 +1051,13 @@ def codetype(self) -> GeckoCode.Type: @property def value(self) -> int: - return self.value & 0xFFFFFFFF + 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 & 0xFFFFFFFF + self._value = value & 0xFFFF def add_child(self, child: "GeckoCode"): self._children.append(child) @@ -1178,11 +1071,6 @@ def virtual_length(self) -> int: def populate_from_bytes(self, f: IO): pass - def apply(self, dol: DolFile) -> bool: - for code in self: - code.apply(dol) - return True - class IfLesserThan16(GeckoCode): def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = False, @@ -1194,24 +1082,15 @@ def __init__(self, value: Union[int, bytes], address: int = 0, isPointer: bool = self.isPointer = isPointer self._children = [] - def __len__(self): - return sum([len(c) for c in self]) + 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" - return f"({intType:X}) If the short at address (0x{self.address:8X} + the {addrstr}) is lesser than (0x{self.value:04X} & 0x{self.mask:04X}), run the encapsulated codes" - - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration + 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] @@ -1233,13 +1112,13 @@ def codetype(self) -> GeckoCode.Type: @property def value(self) -> int: - return self.value & 0xFFFFFFFF + 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 & 0xFFFFFFFF + self._value = value & 0xFFFF def add_child(self, child: "GeckoCode"): self._children.append(child) @@ -1253,17 +1132,14 @@ def virtual_length(self) -> int: def populate_from_bytes(self, f: IO): pass - def apply(self, dol: DolFile) -> bool: - for code in self: - code.apply(dol) - return True - 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._register = register self.isPointer = isPointer def __str__(self) -> str: @@ -1272,35 +1148,25 @@ def __str__(self) -> str: addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags if flags == 0x000: - return f"({intType:X}) Set the base address to the value at address [0x{self.value}]" + return f"({intType:02X}) Set the base address to the value at address [0x{self.value}]" elif flags == 0x001: - return f"({intType:X}) Set the base address to the value at address [gr{self.register} + 0x{self.value}]" + 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:X}) Set the base address to the value at address [{addrstr} + 0x{self.value}]" + return f"({intType:02X}) Set the base address to the value at address [{addrstr} + 0x{self.value}]" elif flags == 0x011: - return f"({intType:X}) Set the base address to the value at address [{addrstr} + gr{self.register} + 0x{self.value}]" + 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:X}) Add the value at address [0x{self.value}] to the base address" + return f"({intType:02X}) Add the value at address [0x{self.value}] to the base address" elif flags == 0x101: - return f"({intType:X}) Add the value at address [gr{self.register} + 0x{self.value}] to the base address" + 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:X}) Add the value at address [{addrstr} + 0x{self.value}] to the base address" + return f"({intType:02X}) Add the value at address [{addrstr} + 0x{self.value}] to the base address" elif flags == 0x111: - return f"({intType:X}) Add the value at address [{addrstr} + gr{self.register} + 0x{self.value}] to the base address" + return f"({intType:02X}) Add the value at address [{addrstr} + gr{self._register} + 0x{self.value}] to the base address" - def __len__(self): + def __len__(self) -> int: return 8 - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration - def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( @@ -1323,13 +1189,13 @@ def codetype(self) -> GeckoCode.Type: @property def value(self) -> int: - return self.value & 0xFFFFFFFF + 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 + self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 @@ -1337,9 +1203,11 @@ def virtual_length(self) -> int: 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._register = register self.isPointer = isPointer def __str__(self) -> str: @@ -1348,36 +1216,26 @@ def __str__(self) -> str: addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags if flags == 0x000: - return f"({intType:X}) Set the base address to the value 0x{self.value}" + return f"({intType:02X}) Set the base address to the value 0x{self.value}" elif flags == 0x001: - return f"({intType:X}) Set the base address to the value (gr{self.register} + 0x{self.value})" + return f"({intType:02X}) Set the base address to the value (gr{self._register} + 0x{self.value})" elif flags == 0x010: - return f"({intType:X}) Set the base address to the value ({addrstr} + 0x{self.value})" + return f"({intType:02X}) Set the base address to the value ({addrstr} + 0x{self.value})" elif flags == 0x011: - return f"({intType:X}) Set the base address to the value ({addrstr} + gr{self.register} + 0x{self.value})" + return f"({intType:02X}) Set the base address to the value ({addrstr} + gr{self._register} + 0x{self.value})" elif flags == 0x100: - return f"({intType:X}) Add the value 0x{self.value} to the base address" + return f"({intType:02X}) Add the value 0x{self.value} to the base address" elif flags == 0x101: - return f"({intType:X}) Add the value (gr{self.register} + 0x{self.value}) to the base address" + return f"({intType:02X}) Add the value (gr{self._register} + 0x{self.value}) to the base address" elif flags == 0x110: - return f"({intType:X}) Add the value ({addrstr} + 0x{self.value}) to the base address" + return f"({intType:02X}) Add the value ({addrstr} + 0x{self.value}) to the base address" elif flags == 0x111: - return f"({intType:X}) Add the value ({addrstr} + gr{self.register}) + 0x{self.value} to the base address" - return f"({intType:X}) Invalid flag {flags}" + 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): + def __len__(self) -> int: return 8 - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration - def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( @@ -1400,13 +1258,13 @@ def codetype(self) -> GeckoCode.Type: @property def value(self) -> int: - return self.value & 0xFFFFFFFF + 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 + self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 @@ -1414,9 +1272,11 @@ def virtual_length(self) -> int: 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._register = register self.isPointer = isPointer def __str__(self) -> str: @@ -1425,28 +1285,18 @@ def __str__(self) -> str: addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags if flags == 0x000: - return f"({intType:X}) Store the base address at address [0x{self.value}]" + return f"({intType:02X}) Store the base address at address [0x{self.value}]" elif flags == 0x001: - return f"({intType:X}) Store the base address at address [gr{self.register} + 0x{self.value}]" + return f"({intType:02X}) Store the base address at address [gr{self._register} + 0x{self.value}]" elif flags == 0x010: - return f"({intType:X}) Store the base address at address [{addrstr} + 0x{self.value}]" + return f"({intType:02X}) Store the base address at address [{addrstr} + 0x{self.value}]" elif flags == 0x011: - return f"({intType:X}) Store the base address at address [{addrstr} + gr{self.register} + 0x{self.value}]" - return f"({intType:X}) Invalid flag {flags}" + 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): + def __len__(self) -> int: return 8 - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration - def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( @@ -1469,13 +1319,13 @@ def codetype(self) -> GeckoCode.Type: @property def value(self) -> int: - return self.value & 0xFFFFFFFF + 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 + self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 @@ -1487,21 +1337,11 @@ def __init__(self, value: int): def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) - return f"({intType:X}) Set the base address to be the next Gecko Code's address + {self.value}" + return f"({intType:02X}) Set the base address to be the next Gecko Code's address + {self.value}" - def __len__(self): + def __len__(self) -> int: return 8 - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration - def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( @@ -1538,9 +1378,11 @@ def virtual_length(self) -> int: 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._register = register self.isPointer = isPointer def __str__(self) -> str: @@ -1549,35 +1391,25 @@ def __str__(self) -> str: addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags if flags == 0x000: - return f"({intType:X}) Set the pointer address to the value at address [0x{self.value}]" + return f"({intType:02X}) Set the pointer address to the value at address [0x{self.value}]" elif flags == 0x001: - return f"({intType:X}) Set the pointer address to the value at address [gr{self.register} + 0x{self.value}]" + 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:X}) Set the pointer address to the value at address [{addrstr} + 0x{self.value}]" + return f"({intType:02X}) Set the pointer address to the value at address [{addrstr} + 0x{self.value}]" elif flags == 0x011: - return f"({intType:X}) Set the pointer address to the value at address [{addrstr} + gr{self.register} + 0x{self.value}]" + 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:X}) Add the value at address [0x{self.value}] to the pointer address" + return f"({intType:02X}) Add the value at address [0x{self.value}] to the pointer address" elif flags == 0x101: - return f"({intType:X}) Add the value at address [gr{self.register} + 0x{self.value}] to the pointer address" + 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:X}) Add the value at address [{addrstr} + 0x{self.value}] to the pointer address" + return f"({intType:02X}) Add the value at address [{addrstr} + 0x{self.value}] to the pointer address" elif flags == 0x111: - return f"({intType:X}) Add the value at address [{addrstr} + gr{self.register} + 0x{self.value}] to the pointer address" + return f"({intType:02X}) Add the value at address [{addrstr} + gr{self._register} + 0x{self.value}] to the pointer address" - def __len__(self): + def __len__(self) -> int: return 8 - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration - def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( @@ -1600,13 +1432,13 @@ def codetype(self) -> GeckoCode.Type: @property def value(self) -> int: - return self.value & 0xFFFFFFFF + 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 + self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 @@ -1614,9 +1446,11 @@ def virtual_length(self) -> int: 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._register = register self.isPointer = isPointer def __str__(self) -> str: @@ -1625,36 +1459,26 @@ def __str__(self) -> str: addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags if flags == 0x000: - return f"({intType:X}) Set the pointer address to the value 0x{self.value}" + return f"({intType:02X}) Set the pointer address to the value 0x{self.value}" elif flags == 0x001: - return f"({intType:X}) Set the pointer address to the value (gr{self.register} + 0x{self.value})" + return f"({intType:02X}) Set the pointer address to the value (gr{self._register} + 0x{self.value})" elif flags == 0x010: - return f"({intType:X}) Set the pointer address to the value ({addrstr} + 0x{self.value})" + return f"({intType:02X}) Set the pointer address to the value ({addrstr} + 0x{self.value})" elif flags == 0x011: - return f"({intType:X}) Set the pointer address to the value ({addrstr} + gr{self.register} + 0x{self.value})" + return f"({intType:02X}) Set the pointer address to the value ({addrstr} + gr{self._register} + 0x{self.value})" elif flags == 0x100: - return f"({intType:X}) Add the value 0x{self.value} to the pointer address" + return f"({intType:02X}) Add the value 0x{self.value} to the pointer address" elif flags == 0x101: - return f"({intType:X}) Add the value (gr{self.register} + 0x{self.value}) to the pointer address" + return f"({intType:02X}) Add the value (gr{self._register} + 0x{self.value}) to the pointer address" elif flags == 0x110: - return f"({intType:X}) Add the value ({addrstr} + 0x{self.value}) to the pointer address" + return f"({intType:02X}) Add the value ({addrstr} + 0x{self.value}) to the pointer address" elif flags == 0x111: - return f"({intType:X}) Add the value ({addrstr} + gr{self.register}) + 0x{self.value} to the pointer address" - return f"({intType:X}) Invalid flag {flags}" + 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): + def __len__(self) -> int: return 8 - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration - def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( @@ -1677,13 +1501,13 @@ def codetype(self) -> GeckoCode.Type: @property def value(self) -> int: - return self.value & 0xFFFFFFFF + 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 + self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 @@ -1691,9 +1515,11 @@ def virtual_length(self) -> int: 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._register = register self.isPointer = isPointer def __str__(self) -> str: @@ -1702,33 +1528,23 @@ def __str__(self) -> str: addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags if flags == 0x000: - return f"({intType:X}) Store the pointer address at address [0x{self.value}]" + return f"({intType:02X}) Store the pointer address at address [0x{self.value}]" elif flags == 0x001: - return f"({intType:X}) Store the pointer address at address [gr{self.register} + 0x{self.value}]" + return f"({intType:02X}) Store the pointer address at address [gr{self._register} + 0x{self.value}]" elif flags == 0x010: - return f"({intType:X}) Store the pointer address at address [{addrstr} + 0x{self.value}]" + return f"({intType:02X}) Store the pointer address at address [{addrstr} + 0x{self.value}]" elif flags == 0x011: - return f"({intType:X}) Store the pointer address at address [{addrstr} + gr{self.register} + 0x{self.value}]" - return f"({intType:X}) Invalid flag {flags}" + 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): + def __len__(self) -> int: return 8 - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration - - def __getitem__(self, index: int) -> int: - if index != 0: - raise IndexError( - f"Index [{index}] is beyond the virtual code size") - return self.value + 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: @@ -1746,13 +1562,13 @@ def codetype(self) -> GeckoCode.Type: @property def value(self) -> int: - return self.value & 0xFFFFFFFF + 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 + self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 @@ -1764,21 +1580,11 @@ def __init__(self, value: int): def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) - return f"({intType:X}) Set the base address to be the next Gecko Code's address + {self.value}" + return f"({intType:02X}) Set the base address to be the next Gecko Code's address + {self.value}" - def __len__(self): + def __len__(self) -> int: return 8 - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration - def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( @@ -1820,9 +1626,9 @@ def __init__(self, repeat: int = 0, b: int = 0): def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) - return f"({intType:X}) Store next code address and number of times to repeat in b{self.b}" + return f"({intType:02X}) Store next code address and number of times to repeat in b{self.b}" - def __len__(self): + def __len__(self) -> int: return 8 @property @@ -1839,9 +1645,9 @@ def __init__(self, b: int = 0): def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) - return f"({intType:X}) 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}" + 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): + def __len__(self) -> int: return 8 @property @@ -1860,13 +1666,13 @@ def __init__(self, flags: int = 0, b: int = 0): def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) if self.flags == 0: - return f"({intType:X}) If the code execution status is true, jump to the next code address stored in b{self.b} (NNNN in bP is not touched)" + 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:X}) If the code execution status is false, jump to the next code address stored in b{self.b} (NNNN in bP is not touched)" + 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:X}) Jump to the next code address stored in b{self.b} (NNNN in bP is not touched)" + return f"({intType:02X}) Jump to the next code address stored in b{self.b} (NNNN in bP is not touched)" - def __len__(self): + def __len__(self) -> int: return 8 @property @@ -1885,13 +1691,13 @@ def __init__(self, flags: int = 0, lineOffset: int = 0): def __str__(self) -> str: intType = GeckoCode.type_to_int(self.codetype) if self.flags == 0: - return f"({intType:X}) If the code execution status is true, jump to (next line of code + {self.offset} lines)" + 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:X}) If the code execution status is false, jump to (next line of code + {self.offset} lines)" + 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:X}) Jump to (next line of code + {self.offset} lines)" + return f"({intType:02X}) Jump to (next line of code + {self.offset} lines)" - def __len__(self): + def __len__(self) -> int: return 8 @property @@ -1902,11 +1708,39 @@ 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._register = register self.isPointer = isPointer def __str__(self) -> str: @@ -1915,28 +1749,18 @@ def __str__(self) -> str: addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags if flags == 0x00: - return f"({intType:X}) Set Gecko Register {self.register} to the value 0x{self.value}" + return f"({intType:02X}) Set Gecko Register {self._register} to the value 0x{self.value}" elif flags == 0x01: - return f"({intType:X}) Set Gecko Register {self.register} to the value (0x{self.value} + the {addrstr})" + return f"({intType:02X}) Set Gecko Register {self._register} to the value (0x{self.value} + the {addrstr})" elif flags == 0x10: - return f"({intType:X}) Add the value 0x{self.value} to Gecko Register {self.register}" + return f"({intType:02X}) Add the value 0x{self.value} to Gecko Register {self._register}" elif flags == 0x11: - return f"({intType:X}) Add the value (0x{self.value} + the {addrstr}) to Gecko Register {self.register}" - return f"({intType:X}) Invalid flag {flags}" + 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): + def __len__(self) -> int: return 8 - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration - def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( @@ -1959,13 +1783,13 @@ def codetype(self) -> GeckoCode.Type: @property def value(self) -> int: - return self.value & 0xFFFFFFFF + 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 + self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 @@ -1973,9 +1797,11 @@ def virtual_length(self) -> int: 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._register = register self.isPointer = isPointer def __str__(self) -> str: @@ -1984,32 +1810,22 @@ def __str__(self) -> str: addrstr = "pointer address" if self.isPointer else "base address" flags = self.flags if flags == 0x00: - return f"({intType:X}) Set Gecko Register {self.register} to the byte at address 0x{self.value}" + return f"({intType:02X}) Set Gecko Register {self._register} to the byte at address 0x{self.value}" elif flags == 0x10: - return f"({intType:X}) Set Gecko Register {self.register} to the short at address 0x{self.value}" + return f"({intType:02X}) Set Gecko Register {self._register} to the short at address 0x{self.value}" elif flags == 0x20: - return f"({intType:X}) Set Gecko Register {self.register} to the word at address 0x{self.value}" + return f"({intType:02X}) Set Gecko Register {self._register} to the word at address 0x{self.value}" elif flags == 0x01: - return f"({intType:X}) Set Gecko Register {self.register} to the byte at address (0x{self.value} + the {addrstr})" + 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:X}) Set Gecko Register {self.register} to the short at address (0x{self.value} + the {addrstr})" + 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:X}) Set Gecko Register {self.register} to the word at address (0x{self.value} + the {addrstr})" - return f"({intType:X}) Invalid flag {flags}" + 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): + def __len__(self) -> int: return 8 - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration - def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( @@ -2032,13 +1848,13 @@ def codetype(self) -> GeckoCode.Type: @property def value(self) -> int: - return self.value & 0xFFFFFFFF + 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 + self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 @@ -2047,11 +1863,13 @@ def virtual_length(self) -> int: 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._register = register self.isPointer = isPointer def __str__(self) -> str: @@ -2062,32 +1880,22 @@ def __str__(self) -> str: flags = self.flags if flags > 0x21: - return f"({intType:X}) Invalid flag {flags}" + return f"({intType:02X}) Invalid flag {flags}" if self.repeat > 0: if flags & 0x01: - return f"({intType:X}) Store Gecko Register {self.register}'s {valueType} to [0x{self.value} + the {addrstr}] {self.repeat + 1} times consecutively" + 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:X}) Store Gecko Register {self.register}'s {valueType} to [0x{self.value}] {self.repeat + 1} times consecutively" + 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:X}) Store Gecko Register {self.register}'s {valueType} to [0x{self.value} + the {addrstr}]" + return f"({intType:02X}) Store Gecko Register {self._register}'s {valueType} to [0x{self.value} + the {addrstr}]" else: - return f"({intType:X}) Store Gecko Register {self.register}'s {valueType} to [0x{self.value}]" + return f"({intType:02X}) Store Gecko Register {self._register}'s {valueType} to [0x{self.value}]" - def __len__(self): + def __len__(self) -> int: return 8 - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration - def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( @@ -2110,13 +1918,13 @@ def codetype(self) -> GeckoCode.Type: @property def value(self) -> int: - return self.value & 0xFFFFFFFF + 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 + self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 @@ -2124,53 +1932,45 @@ def virtual_length(self) -> int: 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._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}" + 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:X}) Add {valueAccessType} to {grAccessType}" + return f"({intType:02X}) Add {valueAccessType} to {grAccessType}" elif opType == GeckoCode.ArithmeticType.MUL: - return f"({intType:X}) Multiply {grAccessType} by {valueAccessType}" + return f"({intType:02X}) Multiply {grAccessType} by {valueAccessType}" elif opType == GeckoCode.ArithmeticType.OR: - return f"({intType:X}) OR {grAccessType} with {valueAccessType}" + return f"({intType:02X}) OR {grAccessType} with {valueAccessType}" elif opType == GeckoCode.ArithmeticType.XOR: - return f"({intType:X}) XOR {grAccessType} with {valueAccessType}" + return f"({intType:02X}) XOR {grAccessType} with {valueAccessType}" elif opType == GeckoCode.ArithmeticType.SLW: - return f"({intType:X}) Shift {grAccessType} left by {valueAccessType} bits" + return f"({intType:02X}) Shift {grAccessType} left by {valueAccessType} bits" elif opType == GeckoCode.ArithmeticType.SRW: - return f"({intType:X}) Shift {grAccessType} right by {valueAccessType} bits" + return f"({intType:02X}) Shift {grAccessType} right by {valueAccessType} bits" elif opType == GeckoCode.ArithmeticType.ROL: - return f"({intType:X}) Rotate {grAccessType} left by {valueAccessType} bits" + return f"({intType:02X}) Rotate {grAccessType} left by {valueAccessType} bits" elif opType == GeckoCode.ArithmeticType.ASR: - return f"({intType:X}) Arithmetic shift {grAccessType} right by {valueAccessType} bits" + return f"({intType:02X}) Arithmetic shift {grAccessType} right by {valueAccessType} bits" elif opType == GeckoCode.ArithmeticType.FADDS: - return f"({intType:X}) Add {valueAccessType} to {grAccessType} as a float" + return f"({intType:02X}) Add {valueAccessType} to {grAccessType} as a float" elif opType == GeckoCode.ArithmeticType.FMULS: - return f"({intType:X}) Multiply {grAccessType} by {valueAccessType} as a float" - return f"({intType:X}) Invalid operation flag {opType}" + return f"({intType:02X}) Multiply {grAccessType} by {valueAccessType} as a float" + return f"({intType:02X}) Invalid operation flag {opType}" - def __len__(self): + def __len__(self) -> int: return 8 - def __iter__(self): - self._iterpos = 0 - return self - - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration - def __getitem__(self, index: int) -> int: if index != 0: raise IndexError( @@ -2193,13 +1993,13 @@ def codetype(self) -> GeckoCode.Type: @property def value(self) -> int: - return self.value & 0xFFFFFFFF + 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 + self._value = value & 0xFFFFFFFF def virtual_length(self) -> int: return 1 @@ -2207,52 +2007,104 @@ def virtual_length(self) -> int: 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._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}" + 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:X}) Add {valueAccessType} to {grAccessType}" + return f"({intType:02X}) Add {valueAccessType} to {grAccessType}" elif opType == GeckoCode.ArithmeticType.MUL: - return f"({intType:X}) Multiply {grAccessType} by {valueAccessType}" + return f"({intType:02X}) Multiply {grAccessType} by {valueAccessType}" elif opType == GeckoCode.ArithmeticType.OR: - return f"({intType:X}) OR {grAccessType} with {valueAccessType}" + return f"({intType:02X}) OR {grAccessType} with {valueAccessType}" elif opType == GeckoCode.ArithmeticType.XOR: - return f"({intType:X}) XOR {grAccessType} with {valueAccessType}" + return f"({intType:02X}) XOR {grAccessType} with {valueAccessType}" elif opType == GeckoCode.ArithmeticType.SLW: - return f"({intType:X}) Shift {grAccessType} left by {valueAccessType}" + return f"({intType:02X}) Shift {grAccessType} left by {valueAccessType}" elif opType == GeckoCode.ArithmeticType.SRW: - return f"({intType:X}) Shift {grAccessType} right by {valueAccessType}" + return f"({intType:02X}) Shift {grAccessType} right by {valueAccessType}" elif opType == GeckoCode.ArithmeticType.ROL: - return f"({intType:X}) Rotate {grAccessType} left by {valueAccessType}" + return f"({intType:02X}) Rotate {grAccessType} left by {valueAccessType}" elif opType == GeckoCode.ArithmeticType.ASR: - return f"({intType:X}) Arithmetic shift {grAccessType} right by {valueAccessType}" + return f"({intType:02X}) Arithmetic shift {grAccessType} right by {valueAccessType}" elif opType == GeckoCode.ArithmeticType.FADDS: - return f"({intType:X}) Add {valueAccessType} to {grAccessType} as a float" + return f"({intType:02X}) Add {valueAccessType} to {grAccessType} as a float" elif opType == GeckoCode.ArithmeticType.FMULS: - return f"({intType:X}) Multiply {grAccessType} by {valueAccessType} as a float" - return f"({intType:X}) Invalid operation flag {opType}" + return f"({intType:02X}) Multiply {grAccessType} by {valueAccessType} as a float" + return f"({intType:02X}) Invalid operation flag {opType}" - def __len__(self): + def __len__(self) -> int: return 8 - def __iter__(self): - self._iterpos = 0 - return self + def __getitem__(self, index: int) -> int: + if index != 0: + raise IndexError( + f"Index [{index}] is beyond the virtual code size") + return self.value - def __next__(self): - try: - return self[self._iterpos] - except IndexError: - raise StopIteration + 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: @@ -2272,22 +2124,1066 @@ def __setitem__(self, index: int, value: Union[int, bytes]): @property def codetype(self) -> GeckoCode.Type: - return GeckoCode.Type.GECKO_REG_OPERATE + 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 + 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 + 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): + pass + + +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): + pass + + +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): + pass + + +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): + pass + + +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): + pass + + +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): + pass + + +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): + pass + + +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): + pass + + +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): + pass + + +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, isPointer: bool = False, numEndifs: int = 0): + 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.TERMINATE + + @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): + 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) + ( + 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): + pass + """ try: if codetype.hex().startswith("2") or codetype.hex().startswith("3"): @@ -2337,7 +3233,7 @@ def populate_from_bytes(self, f: IO): def apply(self, dol: DolFile, preprocess: bool = True): - if not self.is_preprocess_allowed(): + if not self.can_preprocess(): return if dol.is_mapped(self.address): From fee326a5d8044f4689983785f5d7e449dc8352b5 Mon Sep 17 00:00:00 2001 From: JoshuaMK Date: Wed, 12 May 2021 00:36:02 -0500 Subject: [PATCH 13/17] Add Gecko Register types --- geckocode.py | 67 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/geckocode.py b/geckocode.py index fe92d19..a4b4a4b 100644 --- a/geckocode.py +++ b/geckocode.py @@ -267,20 +267,79 @@ def bytes_to_geckocode(f: IO) -> "GeckoCode": 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) >> 18 + 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) >> 18 + 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) >> 18 + 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) def __init__(self): raise InvalidGeckoCodeError( From 34487fc102b6ec91ac4d5a85643bd8102a788136 Mon Sep 17 00:00:00 2001 From: JoshuaMK Date: Mon, 17 May 2021 04:00:38 -0500 Subject: [PATCH 14/17] Finish generator poc --- geckocode.py | 343 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 218 insertions(+), 125 deletions(-) diff --git a/geckocode.py b/geckocode.py index a4b4a4b..175f94c 100644 --- a/geckocode.py +++ b/geckocode.py @@ -1,5 +1,6 @@ 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 @@ -60,7 +61,7 @@ class Type(Enum): WRITE_BRANCH = 0xC6 SWITCH = 0xCC ADDR_RANGE_CHECK = 0xCE - TERMINATE = 0xE0 + TERMINATOR = 0xE0 ENDIF = 0xE2 EXIT = 0xF0 ASM_INSERT_XOR = 0xF2 @@ -81,7 +82,13 @@ class ArithmeticType(Enum): @staticmethod def int_to_type(id: int) -> Type: - return GeckoCode.Type(id & 0xEE) + 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: @@ -155,7 +162,7 @@ def bytes_to_geckocode(f: IO) -> "GeckoCode": address = 0x80000000 | (int.from_bytes( metadata, byteorder="big", signed=False) & 0x1FFFFFF) codetype = GeckoCode.int_to_type((int.from_bytes( - metadata, "big", signed=False) >> 24) & 0xFF) + metadata, "big", signed=False) >> 24) & 0xFE) isPointerType = (codetype & 0x10 != 0) if codetype == GeckoCode.Type.WRITE_8: @@ -328,18 +335,127 @@ def bytes_to_geckocode(f: IO) -> "GeckoCode": 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 + 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 + 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( @@ -765,7 +881,11 @@ def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): - pass + 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): @@ -825,7 +945,11 @@ def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): - pass + 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): @@ -885,7 +1009,11 @@ def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): - pass + 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): @@ -945,7 +1073,11 @@ def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): - pass + 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): @@ -1006,7 +1138,11 @@ def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): - pass + 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): @@ -1067,7 +1203,11 @@ def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): - pass + 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): @@ -1128,7 +1268,11 @@ def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): - pass + 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): @@ -1189,7 +1333,11 @@ def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): - pass + 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): @@ -2312,7 +2460,11 @@ def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): - pass + 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): @@ -2369,7 +2521,11 @@ def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): - pass + 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): @@ -2426,7 +2582,11 @@ def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): - pass + 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): @@ -2483,7 +2643,11 @@ def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): - pass + 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): @@ -2540,7 +2704,11 @@ def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): - pass + 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): @@ -2600,7 +2768,11 @@ def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): - pass + 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): @@ -2660,7 +2832,11 @@ def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): - pass + 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): @@ -2720,7 +2896,11 @@ def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): - pass + 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): @@ -2780,7 +2960,11 @@ def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): - pass + 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): @@ -3029,7 +3213,7 @@ def virtual_length(self) -> int: class Terminator(GeckoCode): - def __init__(self, value: int, isPointer: bool = False, numEndifs: int = 0): + def __init__(self, value: int): self.value = value def __len__(self) -> int: @@ -3062,7 +3246,7 @@ def __setitem__(self, index: int, value: Union[int, bytes]): @property def codetype(self) -> GeckoCode.Type: - return GeckoCode.Type.TERMINATE + return GeckoCode.Type.TERMINATOR @property def value(self) -> int: @@ -3152,8 +3336,10 @@ def virtual_length(self) -> int: class AsmInsertXOR(GeckoCode): - def __init__(self, value: bytes, address: int = 0, isPointer: bool = False): + 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 @@ -3241,101 +3427,8 @@ def virtual_length(self) -> int: return len(self.children) + 1 def populate_from_bytes(self, f: IO): - pass - - """ - try: - if codetype.hex().startswith("2") or codetype.hex().startswith("3"): - skipcodes += 1 - - elif codetype.startswith(b"\xE0"): - skipcodes -= 1 - - elif codetype.startswith(b"\xF0"): - codelist += b"\xF0\x00\x00\x00\x00\x00\x00\x00" - break - - self._rawData.seek(-8, 1) - codelist += self._rawData.read( - GCT.determine_codelength(codetype, info)) - - except (RuntimeError, UnmappedAddressError): - self._rawData.seek(-8, 1) - codelist += self._rawData.read( - GCT.determine_codelength(codetype, info)) - """ - - """ --------------- """ - - """ - def add_child(self, child: "GeckoCode"): - if self.is_ifblock(): - raise InvalidGeckoCodeError( - "Non IF type code can't contain children") - self._children.append(child) - - def remove_child(self, child: "GeckoCode"): - if self.is_ifblock(): - raise InvalidGeckoCodeError( - "Non IF type code can't contain children") - self._children.remove(child) - - def virtual_length(self) -> int: - if self.is_multiline(): - return (len(self) >> 3) + 1 - elif self.is_ifblock(): - return len(self.children) + 1 - else: - return 1 - - def populate_from_bytes(self, f: IO): - - - def apply(self, dol: DolFile, preprocess: bool = True): - if not self.can_preprocess(): - return - - if dol.is_mapped(self.address): - dol.seek(self.address) - if self._type in {GeckoCode.Type.WRITE_8, GeckoCode.Type.WRITE_16}: - counter = int.from_bytes( - self.info, byteorder="big", signed=False) - while counter + 1 > 0: - dol.write(self.data) - counter -= 1 - elif self._type in {GeckoCode.Type.WRITE_32, GeckoCode.Type.WRITE_STR}: - dol.write(self.data) - elif self._type == GeckoCode.Type.WRITE_SERIAL: - value = int.from_bytes( - self.info[:4], byteorder="big", signed=False) - _data = int.from_bytes( - self.info[4:6], byteorder="big", signed=False) - size = (_data & 0x3000) >> 12 - counter = _data & 0xFFF - addressIncrement = int.from_bytes( - self.info[6:8], byteorder="big", signed=False) - valueIncrement = int.from_bytes( - self.info[8:12], byteorder="big", signed=False) - while counter + 1 > 0: - if size == 0: - write_ubyte(dol, value & 0xFF) - dol.seek(-1, 1) - elif size == 1: - write_uint16(dol, value & 0xFFFF) - dol.seek(-2, 1) - elif size == 2: - write_uint32(dol, value) - dol.seek(-4, 1) - else: - raise ValueError( - "Size type {} does not match 08 codetype specs".format(size)) - - dol.seek(addressIncrement, 1) - value += valueIncrement - counter -= 1 - if value > 0xFFFFFFFF: - value -= 0x100000000 - elif self._type == GeckoCode.Type.WRITE_BRANCH: - dol.insert_branch(int.from_bytes( - self.info, byteorder="big", signed=False), self.address, lk=(self.address & 1) == 1) - """ + code = GeckoCode.bytes_to_geckocode(f) + while code != Terminator: + self.add_child(code) + code = GeckoCode.bytes_to_geckocode(f) + self.add_child(code) \ No newline at end of file From 7e0ec0b73519fea91877171c767b192599bfa41f Mon Sep 17 00:00:00 2001 From: JoshuaMK <60854312+JoshuaMKW@users.noreply.github.com> Date: Wed, 9 Mar 2022 00:51:35 -0600 Subject: [PATCH 15/17] upload current work --- GeckoLoader.py | 46 +- build.py | 28 + dolreader.py | 397 ----- gct_d.txt | 174 ++ gct_o.txt | 122 ++ geckocode.py | 3434 --------------------------------------- geckoloader/__init__.py | 2 + kernel.py | 581 +++---- kernel/codehandler.hxx | 20 + kernel/globals.hxx | 97 ++ kernel/kernel.cpp | 73 + kernel/kernel.hxx | 10 + kernel/memory.hxx | 161 ++ kernel/ticket.cpp | 52 + kernel/ticket.hxx | 28 + kernel/types.h | 33 + loader.cpp | 496 +++--- setup.py | 51 +- tools.py | 22 + 19 files changed, 1394 insertions(+), 4433 deletions(-) create mode 100644 build.py delete mode 100644 dolreader.py create mode 100644 gct_d.txt create mode 100644 gct_o.txt delete mode 100644 geckocode.py create mode 100644 geckoloader/__init__.py create mode 100644 kernel/codehandler.hxx create mode 100644 kernel/globals.hxx create mode 100644 kernel/kernel.cpp create mode 100644 kernel/kernel.hxx create mode 100644 kernel/memory.hxx create mode 100644 kernel/ticket.cpp create mode 100644 kernel/ticket.hxx create mode 100644 kernel/types.h diff --git a/GeckoLoader.py b/GeckoLoader.py index 891d745..44eb43f 100644 --- a/GeckoLoader.py +++ b/GeckoLoader.py @@ -1,6 +1,8 @@ # Written by JoshuaMK 2020 +from argparse import Namespace import atexit +from enum import Enum import logging import os import pickle as cPickle @@ -13,11 +15,12 @@ from distutils.version import LooseVersion from io import StringIO from pathlib import Path +from typing import Any, Dict from PyQt5 import QtCore, QtGui, QtWidgets from children_ui import PrefWindow, SettingsWindow -from dolreader import DolFile +from dolreader.dol import DolFile from fileutils import get_program_folder, resource_path from kernel import CodeHandler, KernelLoader from main_ui import MainWindow @@ -164,7 +167,7 @@ def print_splash(self): for line in logo: print(color_text( - line, [('║', TREDLIT), ('╔╚╝╗═', TRED)], TGREENLIT)) + line, textToColor={'║': TREDLIT, '╔': TRED, '╚': TRED, '╝': TRED, '╗': TRED, '═': TRED}, defaultColor=TGREENLIT)) def check_updates(self): repoChecker = Updater('JoshuaMKW', 'GeckoLoader') @@ -193,16 +196,16 @@ def check_updates(self): print('') - def _validate_args(self, args) -> dict: + def _validate_args(self, args: Namespace) -> Dict[str, Any]: dolFile = Path(args.dolfile).resolve() codeList = Path(args.codelist).resolve() if args.dest: dest = Path(args.dest).resolve() if dest.suffix == "": - dest = dest / args.dolfile.name + dest = dest / dolFile.name else: - dest = Path.cwd() / "geckoloader-build" / args.dolfile.name + dest = Path.cwd() / "geckoloader-build" / dolFile.name if args.alloc: try: @@ -226,9 +229,9 @@ def _validate_args(self, args) -> dict: else: _codehook = None - if args.handler == CodeHandler.Types.MINI: + if args.handler == KernelLoader.HandlerType.MINI: codeHandlerFile = Path('bin/codehandler-mini.bin') - elif args.handler == CodeHandler.Types.FULL: + elif args.handler == KernelLoader.HandlerType.FULL: codeHandlerFile = Path('bin/codehandler.bin') else: self.error(color_text( @@ -266,22 +269,27 @@ def _exec(self, args, tmpdir): with resource_path(context["codehandler"]).open("rb") as handler: codeHandler = CodeHandler(handler) - codeHandler.allocation = context["allocation"] - codeHandler.hookAddress = context["hookaddress"] - codeHandler.hookType = context["hooktype"] - codeHandler.includeAll = context["includeall"] - codeHandler.optimizeList = context["optimize"] with resource_path("bin/geckoloader.bin").open("rb") as kernelfile: - geckoKernel = KernelLoader(kernelfile, cli) - geckoKernel.initAddress = context["initaddress"] + geckoKernel = KernelLoader(kernelfile, + hookType=context["hooktype"], + hookAddress=context["hookaddress"], + initAddress=context["initaddress"], + allocation=context["allocation"], + includeAllCodes=context["includeall"], + optimizeCodes=context["optimize"], + protectCodes=context["protect"], + encryptCodes=context["encrypt"], + cli=cli) geckoKernel.verbosity = context["verbosity"] - geckoKernel.quiet = context["quiet"] - geckoKernel.encrypt = context["encrypt"] - geckoKernel.protect = context["protect"] + if context["quiet"] == True: + geckoKernel.silence() + else: + geckoKernel.desilence() if not context["destination"].parent.exists(): - context["destination"].parent.mkdir(parents=True, exist_ok=True) + context["destination"].parent.mkdir( + parents=True, exist_ok=True) geckoKernel.build(context["codepath"], dolFile, codeHandler, TMPDIR, context["destination"]) @@ -292,7 +300,7 @@ def _exec(self, args, tmpdir): class GUI(object): - class Dialogs: + class Dialogs(Enum): LOAD_DEST = 0 LOAD_GCT = 1 LOAD_FOLDER = 2 diff --git a/build.py b/build.py new file mode 100644 index 0000000..3bb5859 --- /dev/null +++ b/build.py @@ -0,0 +1,28 @@ +import os +import sys +from cx_Freeze import setup, Executable + +include_files = ["bin/"] +excludes = ["tkinter"] + +options = { + "build_exe": { + "optimize": 4, + "excludes": excludes, + "include_files": include_files + } +} + +executables = [ + Executable("GeckoLoader.py"), +] + +setup(name="GeckoLoader", + version="7.1.0", + description="DOL Patcher for extending the codespace of Wii/GC games", + executables=[Executable( + "GeckoLoader.py", icon=os.path.join("bin", "icon.ico"))], + author="JoshuaMK", + author_email="joshuamkw2002@gmail.com", + options=options + ) diff --git a/dolreader.py b/dolreader.py deleted file mode 100644 index 8249bee..0000000 --- a/dolreader.py +++ /dev/null @@ -1,397 +0,0 @@ -from io import BytesIO - -import tools -from fileutils import align_byte_size, read_uint32, write_uint32 - -class UnmappedAddressError(Exception): pass -class SectionCountFullError(Exception): pass -class AddressOutOfRangeError(Exception): pass - -class DolFile(object): - - class SectionType: - Text = 0 - Data = 1 - - maxTextSections = 7 - maxDataSections = 11 - offsetInfoLoc = 0 - addressInfoLoc = 0x48 - sizeInfoLoc = 0x90 - bssInfoLoc = 0xD8 - entryInfoLoc = 0xE0 - - def __init__(self, f=None): - - self.textSections = [] - self.dataSections = [] - - self.bssAddress = 0 - self.bssSize = 0 - self.entryPoint = 0x80003000 - - if f is None: return - - # Read text and data section addresses and sizes - for i in range(DolFile.maxTextSections + DolFile.maxDataSections): - f.seek(DolFile.offsetInfoLoc + (i << 2)) - offset = read_uint32(f) - f.seek(DolFile.addressInfoLoc + (i << 2)) - address = read_uint32(f) - f.seek(DolFile.sizeInfoLoc + (i << 2)) - size = read_uint32(f) - - if offset >= 0x100: - f.seek(offset) - data = BytesIO(f.read(size)) - if i < DolFile.maxTextSections: - self.textSections.append({"offset": offset, "address": address, "size": size, "data": data, "type": DolFile.SectionType.Text}) - else: - self.dataSections.append({"offset": offset, "address": address, "size": size, "data": data, "type": DolFile.SectionType.Data}) - - f.seek(DolFile.bssInfoLoc) - self.bssAddress = read_uint32(f) - self.bssSize = read_uint32(f) - - f.seek(DolFile.entryInfoLoc) - self.entryPoint = read_uint32(f) - - self._currLogicAddr = self.first_section["address"] - self.seek(self._currLogicAddr) - f.seek(0) - - def __repr__(self) -> str: - return f"repr={vars(self)}" - - def __str__(self) -> str: - return f"Nintendo DOL executable {self.__repr__()}" - - def resolve_address(self, gcAddr: int) -> tuple: - """ Returns the data of the section that houses the given address\n - UnmappedAddressError is raised when the address is unmapped """ - - for section in self.sections: - if section["address"] <= gcAddr < (section["address"] + section["size"]): - return section - - raise UnmappedAddressError(f"Unmapped address: 0x{gcAddr:X}") - - def seek_nearest_unmapped(self, gcAddr: int, buffer=0) -> int: - '''Returns the nearest unmapped address (greater) if the given address is already taken by data''' - - for section in self.sections: - if section["address"] > (gcAddr + buffer) or (section["address"] + section["size"]) < gcAddr: - continue - gcAddr = section["address"] + section["size"] - - try: - self.resolve_address(gcAddr) - except UnmappedAddressError: - break - return gcAddr - - @property - def sections(self) -> tuple: - """ Generator that yields each section's data """ - - for i in self.textSections: - yield i - for i in self.dataSections: - yield i - - @property - def last_section(self) -> tuple: - """ Returns the last section in the dol file as sorted by internal offset """ - - largestOffset = 0 - indexToTarget = 0 - targetType = DolFile.SectionType.Text - - for i, section in enumerate(self.textSections): - if section["offset"] > largestOffset: - largestOffset = section["offset"] - indexToTarget = i - targetType = DolFile.SectionType.Text - for i, section in enumerate(self.dataSections): - if section["offset"] > largestOffset: - largestOffset = section["offset"] - indexToTarget = i - targetType = DolFile.SectionType.Data - - if targetType == DolFile.SectionType.Text: - return self.textSections[indexToTarget] - else: - return self.dataSections[indexToTarget] - - @property - def first_section(self) -> tuple: - """ Returns the first section in the dol file as sorted by internal offset """ - - smallestOffset = 0xFFFFFFFF - indexToTarget = 0 - targetType = DolFile.SectionType.Text - - for i, section in enumerate(self.textSections): - if section["offset"] < smallestOffset: - smallestOffset = section["offset"] - indexToTarget = i - targetType = DolFile.SectionType.Text - for i, section in enumerate(self.dataSections): - if section["offset"] < smallestOffset: - smallestOffset = section["offset"] - indexToTarget = i - targetType = DolFile.SectionType.Data - - if targetType == DolFile.SectionType.Text: - return self.textSections[indexToTarget] - else: - return self.dataSections[indexToTarget] - - # Unsupported: Reading an entire dol file - # Assumption: A read should not go beyond the current section - def read(self, _size: int) -> bytes: - section = self.resolve_address(self._currLogicAddr) - if self._currLogicAddr + _size > (section["address"] + section["size"]): - raise UnmappedAddressError("Read goes over current section") - - self._currLogicAddr += _size - return section["data"].read(_size) - - # Assumption: A write should not go beyond the current section - def write(self, _data: bytes): - section = self.resolve_address(self._currLogicAddr) - if self._currLogicAddr + len(_data) > (section["address"] + section["size"]): - raise UnmappedAddressError("Write goes over current section") - - section["data"].write(_data) - self._currLogicAddr += len(_data) - - def seek(self, where: int, whence: int = 0): - if whence == 0: - section = self.resolve_address(where) - section["data"].seek(where - section["address"]) - - self._currLogicAddr = where - elif whence == 1: - section = self.resolve_address(self._currLogicAddr + where) - section["data"].seek((self._currLogicAddr + where) - section["address"]) - - self._currLogicAddr += where - else: - raise NotImplementedError(f"Unsupported whence type '{whence}'") - - def tell(self) -> int: - return self._currLogicAddr - - def save(self, f): - f.seek(0) - f.write(b"\x00" * self.size) - - for i, section in enumerate(self.sections): - if section["type"] == DolFile.SectionType.Data: - entry = i + (DolFile.maxTextSections - len(self.textSections)) - else: - entry = i - - f.seek(DolFile.offsetInfoLoc + (entry << 2)) - write_uint32(f, section["offset"]) #offset in file - f.seek(DolFile.addressInfoLoc + (entry << 2)) - write_uint32(f, section["address"]) #game address - f.seek(DolFile.sizeInfoLoc + (entry << 2)) - write_uint32(f, section["size"]) #size in file - - f.seek(section["offset"]) - f.write(section["data"].getbuffer()) - - f.seek(DolFile.bssInfoLoc) - write_uint32(f, self.bssAddress) - write_uint32(f, self.bssSize) - - f.seek(DolFile.entryInfoLoc) - write_uint32(f, self.entryPoint) - align_byte_size(f, 256) - - @property - def size(self) -> int: - try: - section = self.last_section - return (section["offset"] + section["size"] + 255) & -256 - except IndexError: - return 0x100 - - def get_section_size(self, index: int, section: SectionType) -> int: - """ Return the current size of the specified section\n - section: DolFile.SectionType """ - - if section == DolFile.SectionType.Text: - return self.textSections[index]["size"] - else: - return self.dataSections[index]["size"] - - - def append_text_sections(self, sectionsList: list): - """ Follows the list format: [tuple(Data, GameAddress or None), tuple(Data... """ - - for i, dataSet in enumerate(sectionsList): - if len(self.textSections) >= DolFile.maxTextSections: - raise SectionCountFullError(f"Exceeded max text section limit of {DolFile.maxTextSections}") - - finalSection = self.last_section - lastSection = self.textSections[len(self.textSections) - 1] - data, address = dataSet - - if not hasattr(data, "getbuffer"): - if hasattr(data, "read"): - data.seek(0) - data = BytesIO(data.read()) - else: - data = BytesIO(data) - - offset = finalSection["offset"] + finalSection["size"] - - if i < len(sectionsList) - 1: - size = (len(data.getbuffer()) + 31) & -32 - else: - size = (len(data.getbuffer()) + 255) & -256 - - if address is None: - address = self.seek_nearest_unmapped(lastSection["address"] + lastSection["size"], size) - - if address < 0x80000000 or address >= 0x81200000: - raise AddressOutOfRangeError(f"Address '{address:08X}' of text section {i} is beyond scope (0x80000000 <-> 0x81200000)") - - self.textSections.append({"offset": offset, "address": address, "size": size, "data": data, "type": DolFile.SectionType.Text}) - - def append_data_sections(self, sectionsList: list): - """ Follows the list format: [tuple(Data, GameAddress or None), tuple(Data... """ - - for i, dataSet in enumerate(sectionsList): - if len(self.dataSections) >= DolFile.maxDataSections: - raise SectionCountFullError(f"Exceeded max data section limit of {DolFile.maxDataSections}") - - finalSection = self.last_section - lastSection = self.dataSections[len(self.dataSections) - 1] - data, address = dataSet - - if not hasattr(data, "getbuffer"): - if hasattr(data, "read"): - data.seek(0) - data = BytesIO(data.read()) - else: - data = BytesIO(data) - - offset = finalSection["offset"] + finalSection["size"] - - if i < len(sectionsList) - 1: - size = (len(data.getbuffer()) + 31) & -32 - else: - size = (len(data.getbuffer()) + 255) & -256 - - if address is None: - address = self.seek_nearest_unmapped(lastSection["address"] + lastSection["size"], size) - - if address < 0x80000000 or address >= 0x81200000: - raise AddressOutOfRangeError(f"Address '{address:08X}' of data section {i} is beyond scope (0x80000000 <-> 0x81200000)") - - self.dataSections.append({"offset": offset, "address": address, "size": size, "data": data, "type": DolFile.SectionType.Data}) - - def insert_branch(self, to: int, _from: int, lk=0): - """ Insert a branch instruction at _from\n - to: address to branch to\n - _from: address to branch from\n - lk: 0 | 1, is branch linking? """ - - _from &= 0xFFFFFFFC - to &= 0xFFFFFFFC - self.seek(_from) - write_uint32(self, (to - _from) & 0x3FFFFFD | 0x48000000 | lk) - - def extract_branch_addr(self, bAddr: int) -> tuple: - """ Returns the destination of the given branch, - and if the branch is conditional """ - - self.seek(bAddr) - - ppc = read_uint32(self) - conditional = (ppc >> 24) & 0xFF < 0x48 - - if conditional is True: - if (ppc & 0x8000): - offset = (ppc & 0xFFFD) - 0x10000 - else: - offset = ppc & 0xFFFD - else: - if (ppc & 0x2000000): - offset = (ppc & 0x3FFFFFD) - 0x4000000 - else: - offset = ppc & 0x3FFFFFD - - return (bAddr + offset, conditional) - - def read_string(self, addr: int = None, maxlen: int = 0, encoding: str = "utf-8") -> str: - """ Reads a null terminated string from the specified address """ - - if addr != None: - self.seek(addr) - - length = 0 - string = "" - while (char := self.read(1)) != b"\x00": - try: - string += char.decode(encoding) - length += 1 - except UnicodeDecodeError: - print(f"{char} at pos {length}, (address 0x{addr + length:08X}) is not a valid utf-8 character") - return "" - if length > (maxlen-1) and maxlen != 0: - return string - - return string - - def print_info(self): - print("") - print("|-- DOL INFO --|".center(20, " ")) - print("") - - for i, (offset, addr, size, _, _) in enumerate(self.textSections): - header = f"| Text section {i} |" - info = [ "-"*len(header) + "\n" + header + "\n" + "-"*len(header), - "File Offset:".ljust(16, " ") + f"0x{offset:X}", - "Virtual addr:".ljust(16, " ") + f"0x{addr:X}", - "Size:".ljust(16, " ") + f"0x{size:X}" ] - - print("\n".join(info) + "\n") - - for i, (offset, addr, size, _, _) in enumerate(self.dataSections): - header = f"| Data section {i} |" - info = [ "-"*len(header) + "\n" + header + "\n" + "-"*len(header), - "File Offset:".ljust(16, " ") + f"0x{offset:X}", - "Virtual addr:".ljust(16, " ") + f"0x{addr:X}", - "Size:".ljust(16, " ") + f"0x{size:X}" ] - - print("\n".join(info) + "\n") - - header = "| BSS section |" - info = [ "-"*len(header) + "\n" + header + "\n" + "-"*len(header), - "Virtual addr:".ljust(16, " ") + f"0x{self.bssAddress:X}", - "Size:".ljust(16, " ") + f"0x{self.bssSize:X}", - "End:".ljust(16, " ") + f"0x{self.bssAddress+self.bssSize:X}" ] - - print("\n".join(info) + "\n") - - header = "| Miscellaneous Info |" - info = [ "-"*len(header) + "\n" + header + "\n" + "-"*len(header), - "Text sections:".ljust(16, " ") + f"0x{len(self.textSections):X}", - "Data sections:".ljust(16, " ") + f"0x{len(self.dataSections):X}", - "File length:".ljust(16, " ") + f"0x{self.size:X}" ] - - print("\n".join(info) + "\n") - -if __name__ == "__main__": - # Example usage (Reading global string "mario" from Super Mario Sunshine (NTSC-U)) - - with open("Start.dol", "rb") as f: - dol = DolFile(f) - - name = dol.read_string(addr=0x804165A0) - print(name) diff --git a/gct_d.txt b/gct_d.txt new file mode 100644 index 0000000..8f86924 --- /dev/null +++ b/gct_d.txt @@ -0,0 +1,174 @@ +[Gecko] +$On Airstrip From File Select [JoshuaMK] +02164e32 00000001 +*Set to Delfino Plaza +$Skip FMVs +042B5E8C 38600001 +042B5EF4 38600001 +* +$Light Adjuster [JoshuaMK] +C227C6AC 0000001A +7C0802A6 90010004 +9421FFC0 BF610008 +3C608041 8063E108 +83C304FC 4800000D +43000000 00000000 +7FE802A6 839E0018 +837E001C 73600008 +887F0004 4182000C +68600001 981F0004 +2C030000 41820070 +73800001 41820030 +73800400 41820018 +887D5E45 3463FFFF +4180001C 987D5E45 +48000014 C03F0000 +C05D5E0C EC420828 +D05D5E0C 73800002 +41820034 73800400 +4182001C 887D5E45 +280300FF 41820020 +38630001 987D5E45 +48000014 C03F0000 +C05D5E0C EC42082A +D05D5E0C BB610008 +38210040 80010004 +7C0803A6 00000000 +0427C054 60000000 +0427C05C 60000000 +0427C6D4 60000000 +04280154 60000000 +* +* +$Level Select +042A6788 3BC00009 +* +$Boot Area [JoshuaMK] +042A65E0 38600001 +042A65E4 38000005 +* +$LongJump [JoshuaMK] +0417A830 5400077B +04297A60 5400077B +C224954C 00000005 +3CC08041 80C6E108 +7C061800 38A0004D +40820014 89430400 +2C0A0000 41820008 +38A000F6 00000000 +C224C398 00000006 +3C808041 8084E108 +7C04F800 40820018 +809F007C 70840800 +4182000C 881F0400 +7C630378 2C030000 +60000000 00000000 +C224C3FC 00000005 +3C808041 8084E108 +7C04F800 40820014 +881F0400 7C000034 +5400D97E 7C630038 +2C030001 00000000 +C22543B8 00000017 +3FC08041 83DEE108 +7C1E1800 7C7E1B78 +408200A0 7C0802A6 +90010004 9421FFE0 +BFA10008 7C9D2378 +48000009 40A00000 +7FE802A6 806304FC +80630018 70600010 +41A2004C 807E007C +70600800 40820040 +2C1D0882 41820018 +3C1DFE00 2C000881 +4182000C 2C000880 +40820024 C03E00B0 +C01F0000 FF810040 +419C0014 3FA00200 +63BD0880 38600001 +48000008 38600000 +987E0400 7FA4EB78 +BBA10008 38210020 +80010004 7C0803A6 +7FC3F378 7C9F2378 +60000000 00000000 +C225453C 00000018 +7C0802A6 90010004 +9421FFC0 BFA10008 +D3E10018 D3C10020 +D3A10028 FFE00890 +FFC01090 FFA01890 +48000011 3EC00000 +40000000 42900000 +7FE802A6 3D408041 +814AE108 7C0AF000 +4082004C 895E0400 +2C0A0000 41820040 +C03E00B0 C01F0004 +EC210032 C01F0008 +FF810040 409D0008 +FC200090 7FC3F378 +3C008025 60005734 +7C0903A6 4E800421 +C3DE00B0 C01F0000 +EFFF0032 FC20F890 +FC40F090 C3E10018 +C3C10020 C3A10028 +BBA10008 38210040 +80010004 7C0803A6 +C002F0C4 00000000 +C22565D8 00000005 +3D608041 816BE108 +7C0BF800 40820010 +897F0400 556B3830 +7C005B78 54000631 +60000000 00000000 +*v4 +$70 shines stuff [people] +C2299614 00000023 +83ED9FA0 3BC00002 +7FE3FB78 3C800001 +608403AE 3D808029 +618C4A24 7D8903A6 +4E800421 2C030001 +41820034 3BC00009 +7FE3FB78 3C800004 +3D808029 618C4D4C +7D8903A6 4E800421 +3D808029 618C967C +7D8903A6 2C030046 +4D800420 7FE3FB78 +38800001 3CA00001 +60A50384 3D808029 +618C48E4 7D8903A6 +4E800421 7FE3FB78 +38800001 3CA00001 +60A50385 3D808029 +618C48E4 7D8903A6 +4E800421 7FE3FB78 +38800001 3CA00001 +60A50386 3D808029 +618C48E4 7D8903A6 +4E800421 7FE3FB78 +38800001 3CA00001 +60A50387 3D808029 +618C48E4 7D8903A6 +4E800421 7FE3FB78 +38800001 3CA00001 +60A5038F 3D808029 +618C48E4 7D8903A6 +4E800421 7FC3F378 +3D808029 618C9728 +7D8903A6 4E800420 +60000000 00000000 +* +* +* +* +* +[Gecko_Enabled] +$Level Select +[BreakPoints] +803433ac nb +8021dbc4 nb diff --git a/gct_o.txt b/gct_o.txt new file mode 100644 index 0000000..7f73ec6 --- /dev/null +++ b/gct_o.txt @@ -0,0 +1,122 @@ +Boot Stage +* 042A65E0 38600001 +* 042A65E4 38000005 + +On Delfino From Death [NTSC-U] +* 022997A6 00000001 +* 022997AE 00000005 +* 022997BE 00000001 +* 022997C6 00000005 + +No Light Circle +* 0427C6AC 48000560 + +Always Remove Boot Out +060049CC 00000014 +BF859999 9999999A +00000000 40800000 +40400000 00000000 +C22995BC 00000023 +9421FFE0 93A10014 +7C7D1B78 806300AC +93C10018 7C9E2378 +3C808029 9361000C +28030000 609B8BB0 +93410008 93810010 +418200BC 281E0004 +408200B4 889D0064 +2804000B 408200A8 +3C808040 6084D0A8 +83441060 809A007C +28041302 40820090 +80630118 886302E9 +28030001 40820014 +A07D004C 60630002 +B07D004C 48000070 +3C608041 389BFF4C +8383D0A8 3C640001 +4CC63182 7C6903A6 +4E800421 809A0080 +2C830000 386020D0 +7C632079 3C600C40 +4E823342 60630201 +41940008 48000008 +38640000 907A007C +3C60802B 6063B6F8 +7C6903A6 4E800421 +3C608003 6063293C +7C6903A6 7F83E378 +4E800421 7F6903A6 +7FA3EB78 7FC4F378 +4E800421 83C10018 +83A10014 83810010 +8361000C 83410008 +38210020 00000000 +C2004A64 0000002C +7C0802A6 90010004 +9421FFF0 3C808040 +6085E108 80850070 +3CC08000 C80649CC +3CC08000 8084025C +C04649D4 C0240024 +FC01002A FC001000 +4E811B82 4094003C +80A50000 80A5007C +28051302 40820078 +80A30008 90A40018 +80A30004 90A40014 +80630000 90640010 +80010014 38210010 +7C0803A6 4E800020 +3C603F80 90640024 +90640028 9064002C +906401A4 906401A8 +906401AC 38600000 +90640034 3C60801B +60630738 4CC63182 +7C6903A6 7C832378 +4E800421 80010014 +38210010 7C0803A6 +4E800020 3C608000 +C06349D8 3C608000 +C0240014 C0440028 +C90349CC C084002C +C0A401A4 FC000018 +C0C401A8 C0E401AC +3C608000 D0040024 +EC21182A C0040034 +C06349DC FC42402A +FC84402A D0240014 +FC201018 FCA5402A +D0240028 FC202018 +FCC6402A FCE7402A +D024002C FC202818 +EC00182A D02401A4 +FC203018 FC403818 +D02401A8 D04401AC +D0040034 80010014 +38210010 7C0803A6 +4E800020 00000000 +C21BD664 00000006 +9421FFF0 7C0802A6 +90010008 7FC4F378 +3C008000 60004A64 +7C0903A6 4E800421 +80010008 7C0803A6 +38210010 00000000 +041BD668 48000568 +062999DC 00000038 +41820030 4080000C +2C000005 40820028 +2C00000D 40800020 +806D9F28 8063007C +2C031302 4082000C +2C00000B 41820008 +637B0003 576007FF +C22999D8 00000002 +881A0064 2C00000A +60000000 00000000 +042413E0 60000000 +04297BD8 60000000 +04297BE8 60848200 +0429A598 4800000C \ No newline at end of file diff --git a/geckocode.py b/geckocode.py deleted file mode 100644 index 175f94c..0000000 --- a/geckocode.py +++ /dev/null @@ -1,3434 +0,0 @@ -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) \ No newline at end of file diff --git a/geckoloader/__init__.py b/geckoloader/__init__.py new file mode 100644 index 0000000..07b5c38 --- /dev/null +++ b/geckoloader/__init__.py @@ -0,0 +1,2 @@ +__version__ = "v7.1.0" +__author__ = "JoshuaMK" \ No newline at end of file diff --git a/kernel.py b/kernel.py index be8ab8d..5d622e0 100644 --- a/kernel.py +++ b/kernel.py @@ -1,17 +1,20 @@ import functools import random import re +import string import sys import time from enum import Enum -from io import BytesIO +from io import BytesIO, StringIO from pathlib import Path -from typing import Generator, IO, List, Optional, Union +from typing import BinaryIO, Dict, Generator, IO, Iterable, List, Optional, Set, TextIO, Tuple, Union import tools -from dolreader import DolFile, SectionCountFullError, UnmappedAddressError +from dolreader.dol import DolFile, SectionCountFullError, UnmappedAddressError from fileutils import (get_alignment, read_uint16, read_uint32, write_bool, write_sint32, write_ubyte, write_uint16, write_uint32) +from geckolibs.gct import GeckoCodeTable +from geckolibs.geckocode import AsmExecute, GeckoCode try: import chardet @@ -32,82 +35,6 @@ def wrapper(*args, **kwargs): return wrapper - - -class GCT(object): - - def __init__(self): - self.codelist: List[GeckoCode] = [] - - def __len__(self): - return self.byteSize - - @classmethod - def from_bytes(cls, f: IO): - gct = cls() - gct.bytes_to_codelist(f) - return gct - - @property - def byteSize(self): - return sum([len(c) for c in self.codelist]) - - @property - def groupSize(self): - return (self.byteSize >> 3) - - @property - def virtualSize(self): - return len(self.codelist) - - def add_geckocode(self, code: GeckoCode): - self.codelist.append(code) - - def remove_geckocode(self, code: GeckoCode): - self.codelist.remove(code) - - def bytes_to_codelist(self, f: IO): - while metadata := f.read(4): - info = self._rawData.read(4) - address = 0x80000000 | (int.from_bytes( - metadata, byteorder="big", signed=False) & 0x1FFFFFF) - codetype = (int.from_bytes( - metadata, "big", signed=False) >> 24) & 0xFF - isPointerType = (codetype & 0x10 != 0) - - if ((codetype & 0xEF) <= 0x0F): - self.add_geckocode( - GeckoCode(GeckoCode.Type.WRITE, info, address, isPointerType)) - elif ((codetype & 0xEF) <= 0x2F): - ifBlock = GeckoCode(GeckoCode.Type.IF, info, - address, isPointerType) - ifBlock.populate_from_bytes(f) - self.add_geckocode( - GeckoCode(GeckoCode.Type.IF, info, address, isPointerType)) - elif ((codetype & 0xEF) <= 0xC5): - self.add_geckocode( - GeckoCode(GeckoCode.Type.ASM, info, address, isPointerType)) - elif ((codetype & 0xEF) <= 0xC7): - self.add_geckocode( - GeckoCode(GeckoCode.Type.BRANCH, info, address, isPointerType)) - elif codetype == 0xF0: - break - - def to_bytes(self) -> bytes: - rawCodelist = b"\x00\xD0\xC0\xDE"*2 - for code in self.codelist: - if code._type == GeckoCode.Type.WRITE_8: - metadata = (code.address & 0x17FFFFF).to_bytes(4, "big", signed=False) - rawCodelist += metadata + code.info - rawCodelist += code.value - - def apply_to_dol(self, dol: DolFile): - for code in self.codelist: - if code.is_preprocess_allowed(): - code.apply(dol) - self.remove_geckocode(code) - - class CodeHandler(object): class Types: @@ -122,7 +49,10 @@ class Types: GCNPADHook = b"\x3A\xB5\x00\x01\x2C\x15\x00\x04\x3B\x18\x00\x0C\x3B\xFF\x00\x0C" def __init__(self, f): + self.baseAddress = int.from_bytes(f.read(4), "big", signed=False) + self. self._rawData = BytesIO(f.read()) + self.gct = GeckoCodeTable() """Get codelist pointer""" self._rawData.seek(0xFA) @@ -132,43 +62,40 @@ def __init__(self, f): self._rawDataPointer = int(codelistUpper[2:] + codelistLower[2:], 16) self.handlerLength = tools.stream_size(self._rawData) - self.initAddress = 0x80001800 - self.startAddress = 0x800018A8 self.allocation = None self.hookAddress = None self.hookType = None - self.gct = GCT() self.includeAll = False self.optimizeList = False if self.handlerLength < 0x900: - self.type = CodeHandler.Types.MINI + self.type = KernelLoader.HandlerType.MINI else: - self.type = CodeHandler.Types.FULL + self.type = KernelLoader.HandlerType.FULL f.seek(0) - def init_gct(self, gctFile: Path, tmpdir: Path = None): + def init_gct(self, gctPath: Path, tmpdir: Path = None): if tmpdir is not None: _tmpGct = tmpdir / "gct.bin" else: _tmpGct = Path("gct.bin") - if gctFile.suffix.lower() == ".txt": + if gctPath.suffix.lower() == ".txt": with _tmpGct.open("wb+") as temp: temp.write(bytes.fromhex("00D0C0DE"*2 + - self.parse_input(gctFile) + "F000000000000000")) + self.parse_input(gctPath) + "F000000000000000")) temp.seek(0) self.gct = GCT(temp) - elif gctFile.suffix.lower() == ".gct": - with gctFile.open("rb") as gct: + elif gctPath.suffix.lower() == ".gct": + with gctPath.open("rb") as gct: self.gct = GCT(gct) - elif gctFile.suffix == "": + elif gctPath.suffix == "": with _tmpGct.open("wb+") as temp: temp.write(b"\x00\xD0\xC0\xDE"*2) - for file in gctFile.iterdir(): + for file in gctPath.iterdir(): if file.is_file(): if file.suffix.lower() == ".txt": temp.write(bytes.fromhex(self.parse_input(file))) @@ -184,7 +111,7 @@ def init_gct(self, gctFile: Path, tmpdir: Path = None): self.gct = GCT(temp) else: raise NotImplementedError( - f"Parsing file type `{gctFile.suffix}' as a GCT is unsupported") + f"Parsing file type `{gctPath.suffix}' as a GCT is unsupported") def parse_input(self, geckoText: Path) -> str: with geckoText.open("rb") as gecko: @@ -223,6 +150,103 @@ def parse_input(self, geckoText: Path) -> str: return gct + +class KernelLoader(object): + class DataCryptor(object): + @staticmethod + def encrypt_key(key: int) -> int: + b1 = key & 0xFF + b2 = (key >> 8) & 0xFF + b3 = (key >> 16) & 0xFF + b4 = (key >> 24) & 0xFF + b3 ^= b4 + b2 ^= b3 + b1 ^= b2 + return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4 + + @staticmethod + def decrypt_key(key: int) -> int: + b1 = (key >> 24) & 0xFF + b2 = (key >> 16) & 0xFF + b3 = (key >> 8) & 0xFF + b4 = key & 0xFF + b1 ^= b2 + b2 ^= b3 + b3 ^= b4 + return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1 + + @staticmethod + def encrypt_data(data: bytes, key: int) -> bytes: + stream = BytesIO(data) + i = 0 + try: + while packet := read_uint32(stream): + packet = read_uint32(stream) + stream.seek(-4, 1) + write_uint32(stream, (packet ^ key) & 0xFFFFFFFF) + key += (i << 3) & 0xFFFFFFFF + if key > 0xFFFFFFFF: + key -= 0x100000000 + i += 1 + except Exception: + pass + return stream.getvalue() + + def __init__(self, key: int): + self.key = key + + @property + def encryptedKey(self) -> int: + return self.encrypt_key(self.key) + + def encrypt_data(self, data: bytes) -> bytes: + stream = BytesIO(data) + i = 0 + try: + while packet := read_uint32(stream): + packet = read_uint32(stream) + stream.seek(-4, 1) + write_uint32(stream, (packet ^ key) & 0xFFFFFFFF) + key += (i << 3) & 0xFFFFFFFF + if key > 0xFFFFFFFF: + key -= 0x100000000 + i += 1 + except Exception: + pass + return stream.getvalue() + + class HandlerType(Enum): + MINI = "MINI" + FULL = "FULL" + + GeckoProtector = AsmExecute( + b" \ + \x7C\x08\x02\xA6\x94\x21\xFF\x70 \ + \x90\x01\x00\x08\xBC\x61\x00\x0C \ + \x48\x00\x00\x0D\x00\xD0\xC0\xDE \ + \x00\xD0\xDE\xAD\x7F\xE8\x02\xA6 \ + \x3B\xDF\x00\x08\x3C\x60\x80\x00 \ + \x38\x80\x11\x00\x38\xA0\x00\x00 \ + \x60\x63\x1E\xF8\x7C\x89\x03\xA6 \ + \x38\x80\x00\x00\x7D\x03\x22\x14 \ + \x54\xE9\x06\x3E\x89\x08\x00\x08 \ + \x7D\x3F\x48\xAE\x38\xE7\x00\x01 \ + \x7C\x08\x48\x40\x41\x82\x00\x0C \ + \x60\xA7\x00\x00\x48\x00\x00\x04 \ + \x54\xE8\x06\x3E\x28\x08\x00\x03 \ + \x41\x81\x00\x10\x38\x84\x00\x01 \ + \x42\x00\xFF\xCC\x48\x00\x00\x2C \ + \x38\xA0\x00\x08\x7C\x84\x1A\x14 \ + \x7C\xA9\x03\xA6\x38\x60\x00\x00 \ + \x38\x84\xFF\xFF\x54\x66\x07\xBE \ + \x7C\xDE\x30\xAE\x38\x63\x00\x01 \ + \x9C\xC4\x00\x01\x42\x00\xFF\xF0 \ + \xB8\x61\x00\x0C\x80\x01\x00\x08 \ + \x38\x21\x00\x90\x7C\x08\x03\xA6 \ + \x4E\x80\x00\x20\x00\x00\x00\x00 \ + " + ) + @staticmethod def encrypt_key(key: int) -> int: b1 = key & 0xFF @@ -234,21 +258,50 @@ def encrypt_key(key: int) -> int: b1 ^= b2 return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4 - def encrypt_codes(self, key: int): - self.gct._rawData.seek(0) - i = 0 - while True: + @staticmethod + def decrypt_key(key: int) -> int: + b1 = (key >> 24) & 0xFF + b2 = (key >> 16) & 0xFF + b3 = (key >> 8) & 0xFF + b4 = key & 0xFF + b1 ^= b2 + b2 ^= b3 + b3 ^= b4 + return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1 + + @staticmethod + def encrypt_gct(gct: GeckoCodeTable, key: int) -> bytes: + with BytesIO(gct.as_bytes()) as data: + i = 0 + try: + while packet := read_uint32(data): + packet = read_uint32(data) + data.seek(-4, 1) + write_uint32(data, (packet ^ key) & 0xFFFFFFFF) + key += (i << 3) & 0xFFFFFFFF + if key > 0xFFFFFFFF: + key -= 0x100000000 + i += 1 + except Exception: + pass + + @staticmethod + def decrypt_gct(gct: GeckoCodeTable, key: int) -> bytes: + with BytesIO(gct.as_bytes()) as data: + i = 0 try: - packet = read_uint32(self.gct._rawData) - self.gct._rawData.seek(-4, 1) - write_uint32(self.gct._rawData, (packet ^ key) & 0xFFFFFFFF) - key += (i << 3) & 0xFFFFFFFF - if key > 0xFFFFFFFF: - key -= 0x100000000 - i += 1 - except: - break + while packet := read_uint32(data): + packet = read_uint32(data) + data.seek(-4, 1) + write_uint32(data, (packet ^ key) & 0xFFFFFFFF) + key += (i << 3) & 0xFFFFFFFF + if key > 0xFFFFFFFF: + key -= 0x100000000 + i += 1 + except Exception: + pass + @staticmethod def find_variable_data(self, variable) -> int: self._rawData.seek(0) @@ -258,13 +311,14 @@ def find_variable_data(self, variable) -> int: return None - def set_hook_instruction(self, dolFile: DolFile, address: int, varOffset: int, lk=0): + @staticmethod + def set_hook_instruction(self, dol: DolFile, address: int, varOffset: int, lk=0): self._rawData.seek(varOffset) - dolFile.seek(address) - ppc = read_uint32(dolFile) + dol.seek(address) + ppc = read_uint32(dol) if ((((ppc >> 24) & 0xFF) > 0x47 and ((ppc >> 24) & 0xFF) < 0x4C) or (((ppc >> 24) & 0xFF) > 0x3F and ((ppc >> 24) & 0xFF) < 0x44)): - to, conditional = dolFile.extract_branch_addr(address) + to, conditional = dol.extract_branch_addr(address) if conditional: raise NotImplementedError( "Hooking to a conditional non spr branch is unsupported") @@ -273,134 +327,118 @@ def set_hook_instruction(self, dolFile: DolFile, address: int, varOffset: int, l else: write_uint32(self._rawData, ppc) - def set_variables(self, dolFile: DolFile): + @staticmethod + def set_variables(self, dol: DolFile): varOffset = self.find_variable_data(b"\x00\xDE\xDE\xDE") if varOffset is None: raise RuntimeError(tools.color_text( "Variable codehandler data not found\n", defaultColor=tools.TREDLIT)) - self.set_hook_instruction(dolFile, self.hookAddress, varOffset, 0) + self.set_hook_instruction(dol, self.hookAddress, varOffset, 0) self._rawData.seek(varOffset + 4) write_uint32(self._rawData, ((self.hookAddress + 4) - (self.initAddress + (varOffset + 4))) & 0x3FFFFFD | 0x48000000 | 0) + def __init__(self, f: BinaryIO, hookType: CodeHandler.Hook, hookAddress: int, initAddress: int, + allocation: Optional[int] = None, includeAllCodes: bool = False, optimizeCodes: bool = False, + protectGame: bool = False, encryptCodes: bool = False, cli: Optional[tools.CommandLineParser] = None): + self.hookHandler = HookHandler(hookType) + self.hookAddress = hookAddress + self.initAddress = initAddress + self.allocation = allocation + self.includeAll = includeAllCodes + self.optimize = optimizeCodes + self.protect = protectGame + self.encrypt = encryptCodes -class KernelLoader(object): - - def __init__(self, f, cli: tools.CommandLineParser = None): self._rawData = BytesIO(f.read()) self._initDataList = None - self._gpModDataList = None - self._gpDiscDataList = None - self._gpKeyAddrList = None + self._gpModDataList: Tuple[int, int] = None + self._gpDiscDataList: Tuple[int, int] = None + self._gpKeyAddrList: Tuple[int, int] = None + self._cli = cli - self.initAddress = None - self.protect = False - self.verbosity = 0 - self.quiet = False - self.encrypt = False + self._verbosity = 0 + self._quiet = False - def error(self, msg: str): + @property + def verbosity(self) -> int: + return self._verbosity + + @verbosity.setter + def verbosity(self, level: int): + self._verbosity = max(min(level, 0), 3) + + def silence(self): + self._quiet = True + + def desilence(self): + self._quiet = False + + def error(self, msg: str, buffer: TextIO = sys.stderr): if self._cli is not None: self._cli.error(msg) else: - print(msg) + print(msg, file=buffer) sys.exit(1) - def set_variables(self, entryPoint: list, baseOffset: int = 0): + def apply_reloc(self, key: bytes, value: int): + keylen = len(key) self._rawData.seek(0) - - if self._gpModDataList is None: - return - - while sample := self._rawData.read(2): - if sample == b"GH": - self._rawData.seek(-2, 1) - write_uint16(self._rawData, self._gpModDataList[0]) - elif sample == b"GL": - self._rawData.seek(-2, 1) - write_uint16(self._rawData, baseOffset + - self._gpModDataList[1]) - elif sample == b"IH": - self._rawData.seek(-2, 1) - write_uint16(self._rawData, entryPoint[0]) - elif sample == b"IL": - self._rawData.seek(-2, 1) - write_uint16(self._rawData, entryPoint[1]) - elif sample == b"KH": - self._rawData.seek(-2, 1) - write_uint16(self._rawData, self._gpKeyAddrList[0]) - elif sample == b"KL": - self._rawData.seek(-2, 1) - write_uint16(self._rawData, baseOffset + - self._gpKeyAddrList[1]) - - def complete_data(self, codeHandler: CodeHandler, initpoint: list): + while (offset := self._rawData.getvalue().find(key)) >= 0: + self._rawData.seek(offset) + if keylen == 1: + _bytes = (value & 0xFF).to_bytes(1, "big", signed=False) + elif keylen == 2: + _bytes = (value & 0xFFFF).to_bytes(2, "big", signed=False) + elif keylen == 4: + _bytes = (value & 0xFFFFFFFF).to_bytes(4, "big", signed=False) + self._rawData.write(_bytes) + + def do_data_relocs(self, entryAddress: int, modAddress: int, encryptKeyAddress: int, baseOffset: int = 0): + self.apply_reloc(b"GH", ((modAddress >> 16) & 0xFFFF)) + self.apply_reloc(b"GL", (modAddress & 0xFFFF) + baseOffset) + self.apply_reloc(b"IH", (entryAddress >> 16) & 0xFFFF) + self.apply_reloc(b"IL", entryAddress & 0xFFFF) + self.apply_reloc(b"GH", ((encryptKeyAddress >> 16) & 0xFFFF)) + self.apply_reloc(b"GL", (encryptKeyAddress & 0xFFFF) + baseOffset) + + def complete_data(self, codeHandler: CodeHandler, hookAddress: int, initAddress: int): _upperAddr, _lowerAddr = ( (self.initAddress >> 16) & 0xFFFF, self.initAddress & 0xFFFF) _key = random.randrange(0x100000000) self._rawData.seek(0) - while sample := self._rawData.read(4): - if sample == b"HEAP": # Found keyword "HEAP". Goes with the resize of the heap - self._rawData.seek(-4, 1) - - gpModInfoOffset = self._rawData.tell() - gpModUpperAddr = _upperAddr + \ - 1 if ( - _lowerAddr + gpModInfoOffset) > 0x7FFF else _upperAddr # Absolute addressing - - if codeHandler.allocation == None: - codeHandler.allocation = ( - codeHandler.handlerLength + codeHandler.gct.byteSize + 7) & -8 - - write_uint32(self._rawData, codeHandler.allocation) - - elif sample == b"LSIZ": # Found keyword "LSIZ". Goes with the size of the loader - self._rawData.seek(-4, 1) - write_uint32(self._rawData, len(self._rawData.getbuffer())) - - elif sample == b"HSIZ": # Found keyword "HSIZ". Goes with the size of the codehandler - self._rawData.seek(-4, 1) - write_sint32(self._rawData, codeHandler.handlerLength) - - elif sample == b"CSIZ": # Found keyword "CSIZ". Goes with the size of the codes - self._rawData.seek(-4, 1) - write_sint32(self._rawData, codeHandler.gct.byteSize) - - elif sample == b"HOOK": # Found keyword "HOOK". Goes with the codehandler hook - self._rawData.seek(-4, 1) - write_uint32(self._rawData, codeHandler.hookAddress) - - elif sample == b"CRPT": # Found keyword "CRPT". Boolean of the encryption - self._rawData.seek(-4, 1) - write_bool(self._rawData, self.encrypt, 4) - - elif sample == b"CYPT": # Found keyword "CYPT". Encryption Key - self._rawData.seek(-4, 1) - - gpKeyOffset = self._rawData.tell() - gpKeyUpperAddr = _upperAddr + \ - 1 if ( - _lowerAddr + gpKeyOffset) > 0x7FFF else _upperAddr # Absolute addressing - - write_uint32(self._rawData, CodeHandler.encrypt_key(_key)) + self.apply_reloc(b"HEAP", len(codeHandler) + len(codeHandler.gct)) + self.apply_reloc(b"LSIZ", len(self._rawData.getbuffer())) + self.apply_reloc(b"HSIZ", len(codeHandler)) + self.apply_reloc(b"CSIZ", len(codeHandler.gct)) + self.apply_reloc(b"HOOK", self.hookAddress) + self.apply_reloc(b"CRPT", int(self.encrypt)) + self.apply_reloc(b"CYPT", KernelLoader.encrypt_key(_key)) + + gpModInfoOffset = (self._rawData.getvalue().find(b"HEAP") << 16) + gpModUpperAddr = _upperAddr + \ + 1 if ( + _lowerAddr + gpModInfoOffset) > 0x7FFF else _upperAddr # Absolute addressing + gpKeyOffset = self._rawData.getvalue().find(b"CYPT") + gpKeyUpperAddr = _upperAddr + \ + 1 if ( + _lowerAddr + gpKeyOffset) > 0x7FFF else _upperAddr # Absolute addressing if _lowerAddr + gpModInfoOffset > 0xFFFF: _lowerAddr -= 0x10000 - self._gpModDataList = (gpModUpperAddr, gpModInfoOffset) - self._gpKeyAddrList = (gpKeyUpperAddr, gpKeyOffset) - - self.set_variables(initpoint, _lowerAddr) + self.do_data_relocs(initAddress, ((gpModUpperAddr << 16) | gpModInfoOffset) & 0xFFFFFFFF, (( + gpKeyUpperAddr << 16) | gpKeyOffset) & 0xFFFFFFFF, _lowerAddr) if self.encrypt: codeHandler.encrypt_codes(_key) - def patch_arena(self, codeHandler: CodeHandler, dolFile: DolFile) -> tuple: + def patch_arena(self, codeHandler: CodeHandler, dol: DolFile) -> tuple: self.complete_data( - codeHandler, [(dolFile.entryPoint >> 16) & 0xFFFF, dolFile.entryPoint & 0xFFFF]) + codeHandler, [(dol.entryPoint >> 16) & 0xFFFF, dol.entryPoint & 0xFFFF]) self._rawData.seek(0, 2) self._rawData.write(codeHandler._rawData.getvalue() + @@ -410,29 +448,29 @@ def patch_arena(self, codeHandler: CodeHandler, dolFile: DolFile) -> tuple: _kernelData = self._rawData.getvalue() try: - dolFile.append_text_sections([(_kernelData, self.initAddress)]) + dol.append_text_sections([(_kernelData, self.initAddress)]) except SectionCountFullError: try: - dolFile.append_data_sections([(_kernelData, self.initAddress)]) + dol.append_data_sections([(_kernelData, self.initAddress)]) except SectionCountFullError: self.error(tools.color_text( "There are no unused sections left for GeckoLoader to use!\n", defaultColor=tools.TREDLIT)) - dolFile.entryPoint = self.initAddress + dol.entryPoint = self.initAddress return True, None - def patch_legacy(self, codeHandler: CodeHandler, dolFile: DolFile) -> tuple: + def patch_legacy(self, codeHandler: CodeHandler, dol: DolFile) -> tuple: codeHandler._rawData.seek(0) codeHandler.gct._rawData.seek(0) _handlerData = codeHandler._rawData.getvalue() + codeHandler.gct._rawData.getvalue() try: - dolFile.append_text_sections( + dol.append_text_sections( [(_handlerData, codeHandler.initAddress)]) except SectionCountFullError: try: - dolFile.append_data_sections( + dol.append_data_sections( [(_handlerData, codeHandler.initAddress)]) except SectionCountFullError: self.error(tools.color_text( @@ -441,48 +479,14 @@ def patch_legacy(self, codeHandler: CodeHandler, dolFile: DolFile) -> tuple: return True, None def protect_game(self, codeHandler: CodeHandler): - _oldpos = codeHandler.gct._rawData.tell() - - protectdata = (b"\xC0\x00\x00\x00\x00\x00\x00\x17", - b"\x7C\x08\x02\xA6\x94\x21\xFF\x70", - b"\x90\x01\x00\x08\xBC\x61\x00\x0C", - b"\x48\x00\x00\x0D\x00\xD0\xC0\xDE", - b"\x00\xD0\xDE\xAD\x7F\xE8\x02\xA6", - b"\x3B\xDF\x00\x08\x3C\x60\x80\x00", - b"\x38\x80\x11\x00\x38\xA0\x00\x00", - b"\x60\x63\x1E\xF8\x7C\x89\x03\xA6", - b"\x38\x80\x00\x00\x7D\x03\x22\x14", - b"\x54\xE9\x06\x3E\x89\x08\x00\x08", - b"\x7D\x3F\x48\xAE\x38\xE7\x00\x01", - b"\x7C\x08\x48\x40\x41\x82\x00\x0C", - b"\x60\xA7\x00\x00\x48\x00\x00\x04", - b"\x54\xE8\x06\x3E\x28\x08\x00\x03", - b"\x41\x81\x00\x10\x38\x84\x00\x01", - b"\x42\x00\xFF\xCC\x48\x00\x00\x2C", - b"\x38\xA0\x00\x08\x7C\x84\x1A\x14", - b"\x7C\xA9\x03\xA6\x38\x60\x00\x00", - b"\x38\x84\xFF\xFF\x54\x66\x07\xBE", - b"\x7C\xDE\x30\xAE\x38\x63\x00\x01", - b"\x9C\xC4\x00\x01\x42\x00\xFF\xF0", - b"\xB8\x61\x00\x0C\x80\x01\x00\x08", - b"\x38\x21\x00\x90\x7C\x08\x03\xA6", - b"\x4E\x80\x00\x20\x00\x00\x00\x00") - - codeHandler.gct._rawData.seek(-8, 2) - - for line in protectdata: - codeHandler.gct._rawData.write(line) - - codeHandler.gct._rawData.write(b"\xF0\x00\x00\x00\x00\x00\x00\x00") - codeHandler.gct._rawData.seek(_oldpos) + codeHandler.gct.add_child(KernelLoader.GeckoProtector) @timer - def build(self, gctFile: Path, dolFile: DolFile, codeHandler: CodeHandler, tmpdir: Path, dump: Path): - _oldStart = dolFile.entryPoint + def build(self, gctPath: Path, dol: DolFile, codeHandler: CodeHandler, tmpdir: Path, dump: Path): + _oldStart = dol.entryPoint - """Initialize our codes""" - - codeHandler.init_gct(gctFile, tmpdir) + # Initialize our codes + codeHandler.gct = self.load_gct_from(gctPath) if codeHandler.gct is None: self.error(tools.color_text( @@ -491,41 +495,41 @@ def build(self, gctFile: Path, dolFile: DolFile, codeHandler: CodeHandler, tmpdi if self.protect: self.protect_game(codeHandler) - """Get entrypoint (or BSS midpoint) for insert""" + # Get entrypoint (or BSS midpoint) for insert if self.initAddress: try: - dolFile.resolve_address(self.initAddress) + dol.resolve_address(self.initAddress) self.error(tools.color_text( f"Init address specified for GeckoLoader (0x{self.initAddress:X}) clobbers existing dol sections", defaultColor=tools.TREDLIT)) except UnmappedAddressError: pass else: - self.initAddress = dolFile.seek_nearest_unmapped(dolFile.bssAddress, len( - self._rawData.getbuffer()) + codeHandler.handlerLength + codeHandler.gct.byteSize) + self.initAddress = dol.seek_nearest_unmapped(dol.bssAddress, len( + self._rawData.getbuffer()) + len(codeHandler) + len(codeHandler.gct)) self._rawData.seek(0) if codeHandler.optimizeList: - codeHandler.gct.optimize_codelist(dolFile) + codeHandler.gct.optimize_codelist(dol) - """Is codelist optimized away?""" + # Is codelist optimized away? if codeHandler.gct._rawData.getvalue() == b"\x00\xD0\xC0\xDE\x00\xD0\xC0\xDE\xF0\x00\x00\x00\x00\x00\x00\x00": with dump.open("wb") as final: - dolFile.save(final) + dol.save(final) if not self.quiet: if self.verbosity >= 3: - dolFile.print_info() + dol.print_info() print("-"*64) if self.verbosity >= 1: print(tools.color_text( "\n :: All codes have been successfully pre patched", defaultColor=tools.TGREENLIT)) return - hooked = determine_codehook(dolFile, codeHandler, False) + hooked = determine_codehook(dol, codeHandler, False) if hooked: - _status, _msg = self.patch_arena(codeHandler, dolFile) + _status, _msg = self.patch_arena(codeHandler, dol) else: self.error(tools.color_text( "Failed to find a hook address. Try using option --codehook to use your own address\n", defaultColor=tools.TREDLIT)) @@ -538,13 +542,13 @@ def build(self, gctFile: Path, dolFile: DolFile, codeHandler: CodeHandler, tmpdi "Allocated codespace was smaller than the given codelist\n", defaultColor=tools.TYELLOW)) with dump.open("wb") as final: - dolFile.save(final) + dol.save(final) if self.quiet: return if self.verbosity >= 3: - dolFile.print_info() + dol.print_info() print("-"*64) if self.verbosity >= 2: @@ -554,8 +558,8 @@ def build(self, gctFile: Path, dolFile: DolFile, codeHandler: CodeHandler, tmpdi f" :: Allocation is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.gct.byteSize:X}", f" :: Codehandler hooked at 0x{codeHandler.hookAddress:X}", f" :: Codehandler is of type `{codeHandler.type}'", - f" :: Of the {DolFile.maxTextSections} text sections in this DOL file, {len(dolFile.textSections)} are now being used", - f" :: Of the {DolFile.maxDataSections} text sections in this DOL file, {len(dolFile.dataSections)} are now being used"] + f" :: Of the {DolFile.maxTextSections} text sections in this DOL file, {len(dol.textSections)} are now being used", + f" :: Of the {DolFile.maxDataSections} text sections in this DOL file, {len(dol.dataSections)} are now being used"] for bit in info: print(tools.color_text(bit, defaultColor=tools.TGREENLIT)) @@ -568,23 +572,42 @@ def build(self, gctFile: Path, dolFile: DolFile, codeHandler: CodeHandler, tmpdi for bit in info: print(tools.color_text(bit, defaultColor=tools.TGREENLIT)) + def load_gct_from(self, gctPath: Path) -> GeckoCodeTable: + def load_data(gctFile: Path) -> GeckoCodeTable: + if gctFile.suffix.lower() == ".txt": + return GeckoCodeTable.from_text(gctFile.read_text()) + elif gctFile.suffix.lower() == ".gct": + return GeckoCodeTable.from_bytes(gctFile.read_bytes()) + else: + print(tools.color_text( + f" :: HINT: {file} is not a .txt or .gct file, and will be ignored", defaultColor=tools.TYELLOWLIT)) + + gct = GeckoCodeTable() + if gctPath.is_dir(): + for file in gctPath.iterdir(): + gct += load_data(file) + elif gctPath.is_file(): + gct += load_data(gctPath) + + return gct + -def determine_codehook(dolFile: DolFile, codeHandler: CodeHandler, hook=False) -> bool: +def determine_codehook(dol: DolFile, codeHandler: CodeHandler, hook=False) -> bool: if codeHandler.hookAddress is None: - if not assert_code_hook(dolFile, codeHandler): + if not assert_code_hook(dol, codeHandler): return False if hook: - codeHandler.set_variables(dolFile) - insert_code_hook(dolFile, codeHandler, codeHandler.hookAddress) + codeHandler.set_variables(dol) + insert_code_hook(dol, codeHandler, codeHandler.hookAddress) return True -def assert_code_hook(dolFile: DolFile, codeHandler: CodeHandler) -> bool: - for section in dolFile.textSections: - dolFile.seek(section["address"]) - sample = dolFile.read(section["size"]) +def assert_code_hook(dol: DolFile, codeHandler: CodeHandler) -> bool: + for section in dol.textSections: + dol.seek(section["address"]) + sample = dol.read(section["size"]) if codeHandler.hookType == "VI": result = sample.find(codeHandler.GCNVIHook) @@ -597,7 +620,7 @@ def assert_code_hook(dolFile: DolFile, codeHandler: CodeHandler) -> bool: f"Unsupported hook type specified ({codeHandler.hookType})", defaultColor=tools.TREDLIT)) if result >= 0: - dolFile.seek(section["address"] + result) + dol.seek(section["address"] + result) else: if codeHandler.hookType == "VI": result = sample.find(codeHandler.WiiVIHook) @@ -610,27 +633,27 @@ def assert_code_hook(dolFile: DolFile, codeHandler: CodeHandler) -> bool: f"Unsupported hook type specified ({codeHandler.hookType})", defaultColor=tools.TREDLIT)) if result >= 0: - dolFile.seek(section["address"] + result) + dol.seek(section["address"] + result) else: continue - while (sample := read_uint32(dolFile)) != 0x4E800020: + while (sample := read_uint32(dol)) != 0x4E800020: pass - dolFile.seek(-4, 1) - codeHandler.hookAddress = dolFile.tell() + dol.seek(-4, 1) + codeHandler.hookAddress = dol.tell() return True return False -def insert_code_hook(dolFile: DolFile, codeHandler: CodeHandler, address: int): - dolFile.seek(address) - ppc = read_uint32(dolFile) +def insert_code_hook(dol: DolFile, codeHandler: CodeHandler, address: int): + dol.seek(address) + ppc = read_uint32(dol) if ((ppc >> 24) & 0xFF) > 0x3F and ((ppc >> 24) & 0xFF) < 0x48: raise NotImplementedError(tools.color_text( "Hooking the codehandler to a conditional non spr branch is unsupported", defaultColor=tools.TREDLIT)) - dolFile.seek(-4, 1) - dolFile.insert_branch(codeHandler.startAddress, address, lk=0) + dol.seek(-4, 1) + dol.insert_branch(codeHandler.startAddress, address, lk=0) diff --git a/kernel/codehandler.hxx b/kernel/codehandler.hxx new file mode 100644 index 0000000..0e36119 --- /dev/null +++ b/kernel/codehandler.hxx @@ -0,0 +1,20 @@ +#pragma once + +#include "types.h" + +struct CodeHandler { + u8 mDiscID; + u16 mGameCode; + u8 mRegionCode; + u16 mMakerCode; + u32 mRegArea[38]; + u32 mHandler[]; +}; + +struct CodeHandlerBinary { + s32 mExitInstrOfs; + s32 mGeckoCodeOfs; + s32 mRegListOfs; + s32 _padding1; + CodeHandler mCodeHandler; +}; \ No newline at end of file diff --git a/kernel/globals.hxx b/kernel/globals.hxx new file mode 100644 index 0000000..5a44eff --- /dev/null +++ b/kernel/globals.hxx @@ -0,0 +1,97 @@ +#pragma once + +#include "types.h" + +extern OSGlobals gGlobals; + +class OSGlobals { + +public: + enum class TVMODE { NTSC, PAL, DEBUG, DEBUGPAL, MPAL, PAL60 }; + + struct MetaData { + + const u8 mDiscID; // 0x0000 + const u16 mGameCode; // 0x0001 + const u8 mRegionCode; // 0x0003 + const u16 mMakerCode; // 0x0004 + const u8 mDiscNumber; // 0x0006 + const u8 mDiscVersion; // 0x0007 + const u8 mAudioStreaming; // 0x0008 + const u8 mStreamBufferSize; // 0x0009 + const u8 _00[12]; // 0x000A + const u32 mWiiMagic; // 0x0018 + const u32 mGCNMagic; // 0x001C + const u32 mNinBootCode; // 0x0020 + const u32 mAppVersion; // 0x0024 + const u32 mPhysicalRAMSize; // 0x0028 + const u32 mBoardModel; // 0x002C + u8 *mOSArenaLo; // 0x0030 + u8 *mOSArenaHi; // 0x0034 + u32 *mFstStart; // 0x0038 + u32 mFstSize; // 0x003C + u32 mDebuggerPresent; // 0x0040 + const u32 mDebuggerExceptionMask; // 0x0044 + void *mExceptionHookDest; // 0x0048 + const u32 mExceptionReturn; // 0x004C + u32 _01[0x10 / 4]; // 0x0050 + u32 mDebuggerHook[0x24 / 4]; // 0x0060 + u32 _02[0x3C / 4]; // 0x0084 + u32 mCurrentOSContext; // 0x00C0 + u32 mPreviousOSMask; // 0x00C4 + u32 mCurrentOSMask; // 0x00C8 + OSGlobals::TVMODE mTVMode; // 0x00CC + u32 mARAMSize; // 0x00D0 + void *mCurOSContextLogical; // 0x00D4 + void *mDefaultOSThreadLogical; // 0x00D8 + u32 *mThreadQueueHead; // 0x00DC + u32 *mThreadQueueTail; // 0x00E0 + u32 *mCurrentOSThread; // 0x00E4 + u32 mDebuggerSize; // 0x00E8 + u32 *mDebuggerMonitorLoc; // 0x00EC + u32 mSimulatedMemSize; // 0x00F0 + u8 *mBi2HeaderLoc; // 0x00F4 + u32 mBusClockSpeed; // 0x00F8 + u32 mCPUClockSpeed; // 0x00FC + u32 _04[0x3010 / 4]; // 0x0100 + u8 *mWiiHeap; // 0x3110 + }; + + static MetaData sMetaData; + + enum class CONSOLETYPE { Gamecube, Wii, Unknown }; + + inline u32 getGameID() { + return ((u32)sMetaData.mDiscID << 24) | ((u32)sMetaData.mGameCode << 8) | + ((u32)sMetaData.mRegionCode); + } + inline u16 getMakerID() { return sMetaData.mMakerCode; } + inline u8 getDiscNumber() { return sMetaData.mDiscNumber; } + inline u8 getDiscVersion() { return sMetaData.mDiscVersion; } + + CONSOLETYPE detectHomeConsole() { + if (sMetaData.mGCNMagic) { + return CONSOLETYPE::Gamecube; + } else if (sMetaData.mWiiMagic) { + return CONSOLETYPE::Wii; + } + + return CONSOLETYPE::Unknown; + } + + void allocHeap(u32 alloc) { + if (sMetaData.mBi2HeaderLoc < sMetaData.mOSArenaHi) { + sMetaData.mOSArenaHi = sMetaData.mBi2HeaderLoc - alloc; + if (detectHomeConsole() == OSGlobals::CONSOLETYPE::Wii) { + sMetaData.mWiiHeap = sMetaData.mBi2HeaderLoc - alloc; + } + } else { + if (detectHomeConsole() == OSGlobals::CONSOLETYPE::Wii) { + sMetaData.mOSArenaHi = sMetaData.mWiiHeap - alloc; + sMetaData.mWiiHeap -= alloc; + } else { + sMetaData.mOSArenaHi -= alloc; + } + } + } +}; \ No newline at end of file diff --git a/kernel/kernel.cpp b/kernel/kernel.cpp new file mode 100644 index 0000000..6ad573e --- /dev/null +++ b/kernel/kernel.cpp @@ -0,0 +1,73 @@ +#include "types.h" +#include "globals.hxx" +#include "kernel.hxx" +#include "memory.hxx" + +OSGlobals gGlobals; + +void GeckoLoaderKernel::runTicket(Ticket &ticket) { + ticket.exec(); +} + + +Memory::Crypt gpCryptor = {0x43595054}; + +static void initMods() +{ + sDisc.setHeap(gpModInfo.allocsize); /*Reallocate the internal heap*/ + + /*Change codelist pointer to the new address in the allocation*/ + CodeList *codelistPointer = (CodeList *)((u32)&gpModInfo + sizeof(Info) + 0xFC); + codelistPointer->mUpperBase = (((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) >> 16) & 0xFFFF; + codelistPointer->mLowerOffset = ((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) & 0xFFFF; + + /*Copy codelist to the new allocation*/ + if (gpModInfo.crypted) + { + Memory::memcpy(sDisc.sMetaData.mOSArenaHi, (u8 *)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize); + gpCryptor.xorCrypt((u32 *)(sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize), (u32 *)((u8 *)&gpModInfo + sizeof(Info) + gpModInfo.handlerSize + 4), gpModInfo.codeSize >> 2); + } + else + { + Memory::memcpy(sDisc.sMetaData.mOSArenaHi, (u8 *)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize + gpModInfo.codeSize); + } + + /*Get codehandler hook resources*/ + auto fillInField = Memory::Search::single((u32 *)sDisc.sMetaData.mOSArenaHi, (u32 *)(sDisc.sMetaData.mOSArenaHi + 0x600), 0x00DEDEDE); + auto returnAddress = extractBranchAddr((u32 *)gpModInfo.codehandlerHook); + auto ppc = *gpModInfo.codehandlerHook; + + /*Write hook branch*/ + Memory::Direct::branch((void *)gpModInfo.codehandlerHook, (void *)((u32)sDisc.sMetaData.mOSArenaHi + 0xA8), false); //entryhook + + /*Temporary nop*/ + *fillInField = 0x60000000; + + /*Flush the cache so that the instructions update*/ + Memory::Cache::flushRange((u8 *)sDisc.sMetaData.mOSArenaHi, gpModInfo.handlerSize + gpModInfo.codeSize); + + /*Call the codehandler*/ + call((void *)((u32)(sDisc.sMetaData.mOSArenaHi) + 0xA8))(); + + /*Write original instruction or translate offset data if a branch*/ + if (((ppc >> 24) & 0xFF) > 0x47 && ((ppc >> 24) & 0xFF) < 0x4C) + { + Memory::Direct::branch((void *)fillInField, (void *)returnAddress, ppc & 1); + } + else + { + Memory::Direct::write(fillInField, ppc); + } + + /*Write branch back to the hook address + 4*/ + Memory::Direct::branch((void *)&fillInField[1], (void *)(&gpModInfo.codehandlerHook[1]), false); //return +} + +int main() +{ + if (sDisc.detectHomeConsole() != DiscHeader::CONSOLETYPE::Unknown) + { + initMods(); + } + __start(); +} \ No newline at end of file diff --git a/kernel/kernel.hxx b/kernel/kernel.hxx new file mode 100644 index 0000000..0645c2f --- /dev/null +++ b/kernel/kernel.hxx @@ -0,0 +1,10 @@ +#pragma once + +#include "types.h" +#include "ticket.hxx" + +class GeckoLoaderKernel { + void execHandler(void (*codehandler)()); +public: + void runTicket(Ticket &ticket); +}; \ No newline at end of file diff --git a/kernel/memory.hxx b/kernel/memory.hxx new file mode 100644 index 0000000..bbd1117 --- /dev/null +++ b/kernel/memory.hxx @@ -0,0 +1,161 @@ +#pragma once + +#include "types.h" + +#define dcbst(_val) asm volatile("dcbst 0, %0" : : "r"(_val)) +#define dcbf(_val) asm volatile("dcbf 0, %0" : : "r"(_val)) +#define icbi(_val) asm volatile("icbi 0, %0" : : "r"(_val)) + +namespace Memory { + +static void memcpy(void *dst, void *src, size_t size) { + u8 *castDst = static_cast(dst); + u8 *castSrc = static_cast(src); + for (s32 i = 0; i < size; ++i) { + *castDst++ = *castSrc++; + } +} + +namespace Cache { + +inline void flushAddr(void *addr) { + dcbf(addr); + icbi(addr); +} + +inline void flushRange(u8 *addr, s32 size) { + size += 31 + (((u32)addr & 31) > 0); + + for (u32 i = 0; i < (size >> 5); ++i) { + flushAddr((void *)(addr + (i << 5))); + } +} + +inline void storeAddr(void *addr) { + dcbst(addr); + icbi(addr); +} + +inline void storeRange(u8 *addr, s32 size) { + size += 31 + (((u32)addr & 31) > 0); + + for (u32 i = 0; i < (size >> 5); ++i) { + storeAddr((void *)(addr + (i << 5))); + } +} + +} // namespace Cache + +namespace Direct { + +template inline void write(T *addr, T value) { + *addr = value; + Cache::flushAddr(addr); +} + +/*This constructs a branch instruction. &TO = ((TO - FROM) & MAX_OFFSET) | + * BRANCH_TYPE | !!isLink*/ +inline void writeBranch(void *addr, void *to, bool lk) { + Direct::write((u32 *)(addr), ((((u32)(to) - (u32)(addr)) & 0x3ffffff) | + 0x48000000 | lk)); +} + +/*Get the target address of the branch at bAddr*/ +inline u32 getBranch(u32 *bAddr) { + s32 offset; + if (*bAddr & 0x2000000) + offset = (*bAddr & 0x3FFFFFD) - 0x4000000; + else + offset = *bAddr & 0x3FFFFFD; + return (u32)bAddr + offset; +} + +} // namespace Direct + +namespace Search { + +template +T *array(T *start, T *end, size_t matchLen, const T *matchData) { + u32 index = 0; + + /*Loop through the games RAM, make sure we don't find our own hook data by + * accident*/ + for (u32 i = 0; &start[i] < end; ++i) { + /*If the data matches, increase the index counter and continue search, + else set index to 0 and continue searching*/ + if (start[i] == matchData[index]) + ++index; + else + index = 0; + + /*If the data has matched the whole array, return the address of the match*/ + if (index >= matchLen) + return &start[i]; + } + return nullptr; +} + +template T *single(T *start, T *end, T match) { + for (u32 i = 0; &start[i] < end; ++i) { + if (start[i] == match) { + return &start[i]; + } + } + return nullptr; +} + +/*Call this after viHook, finds the address of the first instance +of targetVal, and hooks it to the pointer hookTo*/ +static inline void hookFunction(u32 *start, u32 targetVal, void *hookTo, + bool lk) { + Direct::branch(Search::single(start, start + 0x500, targetVal), hookTo, + lk); +} + +} // namespace Search + +class Crypt { + +private: + u32 key; + + u32 getKey() { + u32 b1 = (this->key >> 24) & 0xFF; + u32 b2 = (this->key >> 16) & 0xFF; + u32 b3 = (this->key >> 8) & 0xFF; + u32 b4 = this->key & 0xFF; + b1 ^= b2; + b2 ^= b3; + b3 ^= b4; + return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1; + } + + void setKey(u32 key) { + u32 b1 = key & 0xFF; + u32 b2 = (key >> 8) & 0xFF; + u32 b3 = (key >> 16) & 0xFF; + u32 b4 = (key >> 24) & 0xFF; + b3 ^= b4; + b2 ^= b3; + b1 ^= b2; + this->key = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4; + } + +public: + Crypt(u32 key) { this->key = key; } + + inline void xorCrypt(u32 *dest, u32 *buffer, u32 size) { + auto key = this->getKey(); + + for (u32 i = 0; i < size; ++i) { + dest[i] = buffer[i] ^ key; + key += i << 3; + } + } +}; + +constexpr u32 mem_start = 0x80000000; +constexpr u32 mem_end = 0x81800000; +constexpr size_t mem_size = mem_end - mem_start; + +} // namespace Memory \ No newline at end of file diff --git a/kernel/ticket.cpp b/kernel/ticket.cpp new file mode 100644 index 0000000..5a8b6f1 --- /dev/null +++ b/kernel/ticket.cpp @@ -0,0 +1,52 @@ +#include "ticket.hxx" +#include "globals.hxx" + +bool Ticket::exec() { + gGlobals.allocHeap((mSizeInfo.getPacketSize() + 31) & -32); /*Reallocate the internal heap*/ + + /*Change codelist pointer to the new address in the allocation*/ + CodeList *codelistPointer = (CodeList *)((u32)&gpModInfo + sizeof(Info) + 0xFC); + codelistPointer->mUpperBase = (((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) >> 16) & 0xFFFF; + codelistPointer->mLowerOffset = ((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) & 0xFFFF; + + /*Copy codelist to the new allocation*/ + if (gpModInfo.crypted) + { + Memory::memcpy(sDisc.sMetaData.mOSArenaHi, (u8 *)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize); + gpCryptor.xorCrypt((u32 *)(sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize), (u32 *)((u8 *)&gpModInfo + sizeof(Info) + gpModInfo.handlerSize + 4), gpModInfo.codeSize >> 2); + } + else + { + Memory::memcpy(sDisc.sMetaData.mOSArenaHi, (u8 *)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize + gpModInfo.codeSize); + } + + /*Get codehandler hook resources*/ + auto fillInField = Memory::Search::single((u32 *)sDisc.sMetaData.mOSArenaHi, (u32 *)(sDisc.sMetaData.mOSArenaHi + 0x600), 0x00DEDEDE); + auto returnAddress = extractBranchAddr((u32 *)gpModInfo.codehandlerHook); + auto ppc = *gpModInfo.codehandlerHook; + + /*Write hook branch*/ + Memory::Direct::writeBranch(gpModInfo.codehandlerHook, (u8 *)sDisc.sMetaData.mOSArenaHi + 0xA8, false); //entryhook + + /*Temporary nop*/ + *fillInField = 0x60000000; + + /*Flush the cache so that the instructions update*/ + Memory::Cache::flushRange((u8 *)sDisc.sMetaData.mOSArenaHi, gpModInfo.handlerSize + gpModInfo.codeSize); + + /*Call the codehandler*/ + call((void *)((u32)(sDisc.sMetaData.mOSArenaHi) + 0xA8))(); + + /*Write original instruction or translate offset data if a branch*/ + if (((ppc >> 24) & 0xFF) > 0x47 && ((ppc >> 24) & 0xFF) < 0x4C) + { + Memory::Direct::branch((void *)fillInField, (void *)returnAddress, ppc & 1); + } + else + { + Memory::Direct::write(fillInField, ppc); + } + + /*Write branch back to the hook address + 4*/ + Memory::Direct::branch((void *)&fillInField[1], (void *)(&gpModInfo.codehandlerHook[1]), false); //return +} \ No newline at end of file diff --git a/kernel/ticket.hxx b/kernel/ticket.hxx new file mode 100644 index 0000000..b72ccd5 --- /dev/null +++ b/kernel/ticket.hxx @@ -0,0 +1,28 @@ +#pragma once + +#include "types.h" + +struct SizeInfo { + size_t mLoaderSize; + size_t mHandlerSize; + size_t mCodeSize; + + size_t getPacketSize() const { + return mLoaderSize + mHandlerSize + mCodeSize; + } +}; + +struct Ticket { + enum class Type { + MEMORY, + DISC + }; + + SizeInfo mSizeInfo; + void *mBinary; + + bool exec(); + void *getLoaderPtr(); + void *getHandlerPtr(); + void *getGeckoPtr(); +}; \ No newline at end of file diff --git a/kernel/types.h b/kernel/types.h new file mode 100644 index 0000000..5eb9762 --- /dev/null +++ b/kernel/types.h @@ -0,0 +1,33 @@ +#pragma once + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned long u32; +typedef unsigned long long u64; +typedef char s8; +typedef short s16; +typedef long s32; +typedef long long s64; +typedef float f32; +typedef double f64; +typedef volatile s8 vs8; +typedef volatile s16 vs16; +typedef volatile s32 vs32; +typedef volatile s64 vs64; +typedef volatile u8 vu8; +typedef volatile u16 vu16; +typedef volatile u32 vu32; +typedef volatile u64 vu64; +typedef volatile f32 vf32; +typedef volatile f64 vf64; + +typedef unsigned long size_t; + +#ifdef __cplusplus +#define NULL nullptr +#else +#define NULL ((void*)0) +#define bool int +#define true 1 +#define false 0 +#endif \ No newline at end of file diff --git a/loader.cpp b/loader.cpp index 7885817..e468773 100644 --- a/loader.cpp +++ b/loader.cpp @@ -1,21 +1,6 @@ -//#include -//#include - -//using std::array; -//using std::memcpy; -//using std::memcmp; -//using std::memset; -//using std::string; - -#define dcbst(_val) asm volatile("dcbst 0, %0" \ - : \ - : "r"(_val)) -#define dcbf(_val) asm volatile("dcbf 0, %0" \ - : \ - : "r"(_val)) -#define icbi(_val) asm volatile("icbi 0, %0" \ - : \ - : "r"(_val)) +#define dcbst(_val) asm volatile("dcbst 0, %0" : : "r"(_val)) +#define dcbf(_val) asm volatile("dcbf 0, %0" : : "r"(_val)) +#define icbi(_val) asm volatile("icbi 0, %0" : : "r"(_val)) #define call(addr) ((void (*)(...))addr) #define CODEHANDLER 0x800018A8 @@ -34,18 +19,14 @@ using f64 = double; __attribute__((noreturn)) int main(); -struct CodeList -{ - +struct CodeList { u16 mBaseASM; u16 mUpperBase; u16 mOffsetASM; u16 mLowerOffset; }; -struct Info -{ - +struct Info { const u32 allocsize; const u32 loaderSize; const u32 handlerSize; @@ -54,135 +35,104 @@ struct Info const u32 crypted; }; -class DiscHeader -{ - +class DiscHeader { public: - enum class TVMODE - { - NTSC, - PAL, - DEBUG, - DEBUGPAL, - MPAL, - PAL60 - }; - - struct MetaData - { - - const u8 mDiscID; //0x0000 - const u16 mGameCode; //0x0001 - const u8 mRegionCode; //0x0003 - const u16 mMakerCode; //0x0004 - const u8 mDiscNumber; //0x0006 - const u8 mDiscVersion; //0x0007 - const u8 mAudioStreaming; //0x0008 - const u8 mStreamBufferSize; //0x0009 - const u8 _00[12]; //0x000A - const u32 mWiiMagic; //0x0018 - const u32 mGCNMagic; //0x001C - const u32 mNinBootCode; //0x0020 - const u32 mAppVersion; //0x0024 - const u32 mPhysicalRAMSize; //0x0028 - const u32 mBoardModel; //0x002C - u8 *mOSArenaLo; //0x0030 - u8 *mOSArenaHi; //0x0034 - u32 *mFstStart; //0x0038 - u32 mFstSize; //0x003C - u32 mDebuggerPresent; //0x0040 - const u32 mDebuggerExceptionMask; //0x0044 - void *mExceptionHookDest; //0x0048 - const u32 mExceptionReturn; //0x004C - u32 _01[0x10 / 4]; //0x0050 - u32 mDebuggerHook[0x24 / 4]; //0x0060 - u32 _02[0x3C / 4]; //0x0084 - u32 mCurrentOSContext; //0x00C0 - u32 mPreviousOSMask; //0x00C4 - u32 mCurrentOSMask; //0x00C8 - DiscHeader::TVMODE mTVMode; //0x00CC - u32 mARAMSize; //0x00D0 - void *mCurOSContextLogical; //0x00D4 - void *mDefaultOSThreadLogical; //0x00D8 - u32 *mThreadQueueHead; //0x00DC - u32 *mThreadQueueTail; //0x00E0 - u32 *mCurrentOSThread; //0x00E4 - u32 mDebuggerSize; //0x00E8 - u32 *mDebuggerMonitorLoc; //0x00EC - u32 mSimulatedMemSize; //0x00F0 - u8 *mBi2HeaderLoc; //0x00F4 - u32 mBusClockSpeed; //0x00F8 - u32 mCPUClockSpeed; //0x00FC - u32 _04[0x3010 / 4]; //0x0100 - u8 *mWiiHeap; //0x3110 + enum class TVMODE { NTSC, PAL, DEBUG, DEBUGPAL, MPAL, PAL60 }; + + struct MetaData { + + const u8 mDiscID; // 0x0000 + const u16 mGameCode; // 0x0001 + const u8 mRegionCode; // 0x0003 + const u16 mMakerCode; // 0x0004 + const u8 mDiscNumber; // 0x0006 + const u8 mDiscVersion; // 0x0007 + const u8 mAudioStreaming; // 0x0008 + const u8 mStreamBufferSize; // 0x0009 + const u8 _00[12]; // 0x000A + const u32 mWiiMagic; // 0x0018 + const u32 mGCNMagic; // 0x001C + const u32 mNinBootCode; // 0x0020 + const u32 mAppVersion; // 0x0024 + const u32 mPhysicalRAMSize; // 0x0028 + const u32 mBoardModel; // 0x002C + u8 *mOSArenaLo; // 0x0030 + u8 *mOSArenaHi; // 0x0034 + u32 *mFstStart; // 0x0038 + u32 mFstSize; // 0x003C + u32 mDebuggerPresent; // 0x0040 + const u32 mDebuggerExceptionMask; // 0x0044 + void *mExceptionHookDest; // 0x0048 + const u32 mExceptionReturn; // 0x004C + u32 _01[0x10 / 4]; // 0x0050 + u32 mDebuggerHook[0x24 / 4]; // 0x0060 + u32 _02[0x3C / 4]; // 0x0084 + u32 mCurrentOSContext; // 0x00C0 + u32 mPreviousOSMask; // 0x00C4 + u32 mCurrentOSMask; // 0x00C8 + DiscHeader::TVMODE mTVMode; // 0x00CC + u32 mARAMSize; // 0x00D0 + void *mCurOSContextLogical; // 0x00D4 + void *mDefaultOSThreadLogical; // 0x00D8 + u32 *mThreadQueueHead; // 0x00DC + u32 *mThreadQueueTail; // 0x00E0 + u32 *mCurrentOSThread; // 0x00E4 + u32 mDebuggerSize; // 0x00E8 + u32 *mDebuggerMonitorLoc; // 0x00EC + u32 mSimulatedMemSize; // 0x00F0 + u8 *mBi2HeaderLoc; // 0x00F4 + u32 mBusClockSpeed; // 0x00F8 + u32 mCPUClockSpeed; // 0x00FC + u32 _04[0x3010 / 4]; // 0x0100 + u8 *mWiiHeap; // 0x3110 }; static MetaData sMetaData; - enum class CONSOLETYPE - { - Gamecube, - Wii, - Unknown - }; + enum class CONSOLETYPE { Gamecube, Wii, Unknown }; - inline u32 getGameID() { return ((u32)sMetaData.mDiscID << 24) | ((u32)sMetaData.mGameCode << 8) | ((u32)sMetaData.mRegionCode); } + inline u32 getGameID() { + return ((u32)sMetaData.mDiscID << 24) | ((u32)sMetaData.mGameCode << 8) | + ((u32)sMetaData.mRegionCode); + } inline u16 getMakerID() { return sMetaData.mMakerCode; } inline u8 getDiscNumber() { return sMetaData.mDiscNumber; } inline u8 getDiscVersion() { return sMetaData.mDiscVersion; } - CONSOLETYPE detectHomeConsole() - { - if (sMetaData.mGCNMagic) - { + CONSOLETYPE detectHomeConsole() { + if (sMetaData.mGCNMagic) { return CONSOLETYPE::Gamecube; - } - else if (sMetaData.mWiiMagic) - { + } else if (sMetaData.mWiiMagic) { return CONSOLETYPE::Wii; } return CONSOLETYPE::Unknown; } - inline void setHeap(u32 alloc) - { - if (sMetaData.mBi2HeaderLoc < sMetaData.mOSArenaHi) - { + inline void setHeap(u32 alloc) { + if (sMetaData.mBi2HeaderLoc < sMetaData.mOSArenaHi) { sMetaData.mOSArenaHi = sMetaData.mBi2HeaderLoc - alloc; - if (this->detectHomeConsole() == DiscHeader::CONSOLETYPE::Wii) - { + if (this->detectHomeConsole() == DiscHeader::CONSOLETYPE::Wii) { sMetaData.mWiiHeap = sMetaData.mBi2HeaderLoc - alloc; } - } - else - { - if (this->detectHomeConsole() == DiscHeader::CONSOLETYPE::Wii) - { + } else { + if (this->detectHomeConsole() == DiscHeader::CONSOLETYPE::Wii) { sMetaData.mOSArenaHi = sMetaData.mWiiHeap - alloc; sMetaData.mWiiHeap -= alloc; - } - else - { + } else { sMetaData.mOSArenaHi -= alloc; } - } } }; static DiscHeader sDisc; Info gpModInfo = { - 0x48454150, - 0x4C53495A, - 0x4853495A, - 0x4353495A, - (const u32 *)0x484F4F4B, + 0x48454150, 0x4C53495A, 0x4853495A, 0x4353495A, (const u32 *)0x484F4F4B, 0x43525054, }; -inline u32 extractBranchAddr(u32 *bAddr) -{ +inline u32 extractBranchAddr(u32 *bAddr) { s32 offset; if (*bAddr & 0x2000000) offset = (*bAddr & 0x3FFFFFD) - 0x4000000; @@ -191,232 +141,216 @@ inline u32 extractBranchAddr(u32 *bAddr) return (u32)bAddr + offset; } -namespace Memory -{ +namespace Memory { - static void memcpy(u8 *to, u8 *from, s32 size) - { - for (s32 i = 0; i < size; ++i) - { - *to++ = *from++; - } +static void memcpy(u8 *to, u8 *from, s32 size) { + for (s32 i = 0; i < size; ++i) { + *to++ = *from++; } +} - namespace Cache - { +namespace Cache { - static inline void flushAddr(void *addr) - { - dcbf(addr); - icbi(addr); - } +static inline void flushAddr(void *addr) { + dcbf(addr); + icbi(addr); +} - static void flushRange(u8 *addr, s32 size) - { - size += 31 + (((u32)addr & 31) > 0); +static void flushRange(u8 *addr, s32 size) { + size += 31 + (((u32)addr & 31) > 0); - for (u32 i = 0; i < (size >> 5); ++i) - { - flushAddr((void *)(addr + (i << 5))); - } - } + for (u32 i = 0; i < (size >> 5); ++i) { + flushAddr((void *)(addr + (i << 5))); + } +} - static void storeAddr(void *addr) - { - dcbst(addr); - icbi(addr); - } +static void storeAddr(void *addr) { + dcbst(addr); + icbi(addr); +} - static void storeRange(u8 *addr, s32 size) - { - size += 31 + (((u32)addr & 31) > 0); +static void storeRange(u8 *addr, s32 size) { + size += 31 + (((u32)addr & 31) > 0); - for (u32 i = 0; i < (size >> 5); ++i) - { - storeAddr((void *)(addr + (i << 5))); - } - } + for (u32 i = 0; i < (size >> 5); ++i) { + storeAddr((void *)(addr + (i << 5))); + } +} - } // namespace Cache +} // namespace Cache - namespace Direct - { +namespace Direct { - template - static inline void write(T *addr, T value) - { - *addr = value; - Cache::flushAddr(addr); - } +template static inline void write(T *addr, T value) { + *addr = value; + Cache::flushAddr(addr); +} - /*This constructs a branch instruction. &TO = ((TO - FROM) & MAX_OFFSET) | BRANCH_TYPE | !!isLink*/ - static inline void branch(void *addr, void *to, bool lk) - { - Direct::write((u32 *)(addr), ((((u32)(to) - (u32)(addr)) & 0x3ffffff) | 0x48000000 | lk)); - } +/*This constructs a branch instruction. &TO = ((TO - FROM) & MAX_OFFSET) | + * BRANCH_TYPE | !!isLink*/ +static inline void branch(void *addr, void *to, bool lk) { + Direct::write((u32 *)(addr), ((((u32)(to) - (u32)(addr)) & 0x3ffffff) | + 0x48000000 | lk)); +} - } // namespace Direct +} // namespace Direct - namespace Search - { +namespace Search { - static u32 *array(u32 *start, u32 *end, u32 arrayLength, const u32 *hookData) - { - u32 index = 0; +static u32 *array(u32 *start, u32 *end, u32 arrayLength, const u32 *hookData) { + u32 index = 0; - /*Loop through the games RAM, make sure we don't find our own hook data by accident*/ - for (u32 i = 0; &start[i] < end; ++i) - { - /*If the data matches, increase the index counter and continue search, - else set index to 0 and continue searching*/ - if (start[i] == hookData[index]) - ++index; - else - index = 0; + /*Loop through the games RAM, make sure we don't find our own hook data by + * accident*/ + for (u32 i = 0; &start[i] < end; ++i) { + /*If the data matches, increase the index counter and continue search, + else set index to 0 and continue searching*/ + if (start[i] == hookData[index]) + ++index; + else + index = 0; - /*If the data has matched the whole array, return the address of the match*/ - if (index >= (arrayLength) && (&start[i] < (u32 *)&gpModInfo || &start[i] > (u32 *)&gpModInfo + sizeof(Info))) - return &start[i]; - } - return nullptr; - } + /*If the data has matched the whole array, return the address of the match*/ + if (index >= (arrayLength) && + (&start[i] < (u32 *)&gpModInfo || + &start[i] > (u32 *)&gpModInfo + sizeof(Info))) + return &start[i]; + } + return nullptr; +} - template - static T *single(T *start, T *end, T match) - { - for (u32 i = 0; &start[i] < end; ++i) - { - if (start[i] == match) - { - return &start[i]; - } - } - return nullptr; +template static T *single(T *start, T *end, T match) { + for (u32 i = 0; &start[i] < end; ++i) { + if (start[i] == match) { + return &start[i]; } + } + return nullptr; +} - /*Call this after viHook, finds the address of the first instance - of targetVal, and hooks it to the pointer hookTo*/ - static inline void hookFunction(u32 *start, u32 targetVal, void *hookTo, bool lk) - { - Direct::branch(Search::single(start, start + 0x500, targetVal), hookTo, lk); - } +/*Call this after viHook, finds the address of the first instance +of targetVal, and hooks it to the pointer hookTo*/ +static inline void hookFunction(u32 *start, u32 targetVal, void *hookTo, + bool lk) { + Direct::branch(Search::single(start, start + 0x500, targetVal), hookTo, + lk); +} - } // namespace Search +} // namespace Search - class Crypt - { +class Crypt { - private: - u32 key; +private: + u32 key; - u32 getKey() - { - u32 b1 = (this->key >> 24) & 0xFF; - u32 b2 = (this->key >> 16) & 0xFF; - u32 b3 = (this->key >> 8) & 0xFF; - u32 b4 = this->key & 0xFF; - b1 ^= b2; - b2 ^= b3; - b3 ^= b4; - return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1; - } + u32 getKey() { + u32 b1 = (this->key >> 24) & 0xFF; + u32 b2 = (this->key >> 16) & 0xFF; + u32 b3 = (this->key >> 8) & 0xFF; + u32 b4 = this->key & 0xFF; + b1 ^= b2; + b2 ^= b3; + b3 ^= b4; + return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1; + } - void setKey(u32 key) - { - u32 b1 = key & 0xFF; - u32 b2 = (key >> 8) & 0xFF; - u32 b3 = (key >> 16) & 0xFF; - u32 b4 = (key >> 24) & 0xFF; - b3 ^= b4; - b2 ^= b3; - b1 ^= b2; - this->key = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4; - } + void setKey(u32 key) { + u32 b1 = key & 0xFF; + u32 b2 = (key >> 8) & 0xFF; + u32 b3 = (key >> 16) & 0xFF; + u32 b4 = (key >> 24) & 0xFF; + b3 ^= b4; + b2 ^= b3; + b1 ^= b2; + this->key = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4; + } - public: - Crypt(u32 key) - { - this->key = key; - } +public: + Crypt(u32 key) { this->key = key; } - inline void xorCrypt(u32 *dest, u32 *buffer, u32 size) - { - auto key = this->getKey(); + inline void xorCrypt(u32 *dest, u32 *buffer, u32 size) { + auto key = this->getKey(); - for (u32 i = 0; i < size; ++i) - { - dest[i] = buffer[i] ^ key; - key += i << 3; - } + for (u32 i = 0; i < size; ++i) { + dest[i] = buffer[i] ^ key; + key += i << 3; } - }; + } +}; - enum class Space : u32 - { - Start = 0x80000000, - End = 0x81800000, - Size = 0x1800000 - }; +enum class Space : u32 { + Start = 0x80000000, + End = 0x81800000, + Size = 0x1800000 +}; } // namespace Memory Memory::Crypt gpCryptor = {0x43595054}; -static void initMods() -{ +static void initMods() { sDisc.setHeap(gpModInfo.allocsize); /*Reallocate the internal heap*/ /*Change codelist pointer to the new address in the allocation*/ - CodeList *codelistPointer = (CodeList *)((u32)&gpModInfo + sizeof(Info) + 0xFC); - codelistPointer->mUpperBase = (((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) >> 16) & 0xFFFF; - codelistPointer->mLowerOffset = ((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) & 0xFFFF; + CodeList *codelistPointer = + (CodeList *)((u32)&gpModInfo + sizeof(Info) + 0xFC); + codelistPointer->mUpperBase = + (((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) >> 16) & + 0xFFFF; + codelistPointer->mLowerOffset = + ((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) & 0xFFFF; /*Copy codelist to the new allocation*/ - if (gpModInfo.crypted) - { - Memory::memcpy(sDisc.sMetaData.mOSArenaHi, (u8 *)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize); - gpCryptor.xorCrypt((u32 *)(sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize), (u32 *)((u8 *)&gpModInfo + sizeof(Info) + gpModInfo.handlerSize + 4), gpModInfo.codeSize >> 2); - } - else - { - Memory::memcpy(sDisc.sMetaData.mOSArenaHi, (u8 *)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize + gpModInfo.codeSize); + if (gpModInfo.crypted) { + Memory::memcpy(sDisc.sMetaData.mOSArenaHi, + (u8 *)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize); + gpCryptor.xorCrypt( + (u32 *)(sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize), + (u32 *)((u8 *)&gpModInfo + sizeof(Info) + gpModInfo.handlerSize + 4), + gpModInfo.codeSize >> 2); + } else { + Memory::memcpy(sDisc.sMetaData.mOSArenaHi, + (u8 *)&gpModInfo + sizeof(Info) + 4, + gpModInfo.handlerSize + gpModInfo.codeSize); } /*Get codehandler hook resources*/ - auto fillInField = Memory::Search::single((u32 *)sDisc.sMetaData.mOSArenaHi, (u32 *)(sDisc.sMetaData.mOSArenaHi + 0x600), 0x00DEDEDE); + auto fillInField = Memory::Search::single( + (u32 *)sDisc.sMetaData.mOSArenaHi, + (u32 *)(sDisc.sMetaData.mOSArenaHi + 0x600), 0x00DEDEDE); auto returnAddress = extractBranchAddr((u32 *)gpModInfo.codehandlerHook); auto ppc = *gpModInfo.codehandlerHook; /*Write hook branch*/ - Memory::Direct::branch((void *)gpModInfo.codehandlerHook, (void *)((u32)sDisc.sMetaData.mOSArenaHi + 0xA8), false); //entryhook + Memory::Direct::branch((void *)gpModInfo.codehandlerHook, + (void *)((u32)sDisc.sMetaData.mOSArenaHi + 0xA8), + false); // entryhook /*Temporary nop*/ *fillInField = 0x60000000; /*Flush the cache so that the instructions update*/ - Memory::Cache::flushRange((u8 *)sDisc.sMetaData.mOSArenaHi, gpModInfo.handlerSize + gpModInfo.codeSize); + Memory::Cache::flushRange((u8 *)sDisc.sMetaData.mOSArenaHi, + gpModInfo.handlerSize + gpModInfo.codeSize); /*Call the codehandler*/ call((void *)((u32)(sDisc.sMetaData.mOSArenaHi) + 0xA8))(); /*Write original instruction or translate offset data if a branch*/ - if (((ppc >> 24) & 0xFF) > 0x47 && ((ppc >> 24) & 0xFF) < 0x4C) - { + if (((ppc >> 24) & 0xFF) > 0x47 && ((ppc >> 24) & 0xFF) < 0x4C) { Memory::Direct::branch((void *)fillInField, (void *)returnAddress, ppc & 1); - } - else - { + } else { Memory::Direct::write(fillInField, ppc); } /*Write branch back to the hook address + 4*/ - Memory::Direct::branch((void *)&fillInField[1], (void *)(&gpModInfo.codehandlerHook[1]), false); //return + Memory::Direct::branch((void *)&fillInField[1], + (void *)(&gpModInfo.codehandlerHook[1]), + false); // return } -int main() -{ - if (sDisc.detectHomeConsole() != DiscHeader::CONSOLETYPE::Unknown) - { +int main() { + if (sDisc.detectHomeConsole() != DiscHeader::CONSOLETYPE::Unknown) { initMods(); } __start(); diff --git a/setup.py b/setup.py index 33c9fa1..f0341ba 100644 --- a/setup.py +++ b/setup.py @@ -1,27 +1,32 @@ -import os -import sys -from cx_Freeze import setup, Executable +import geckoloader +import setuptools -include_files = [ "bin/" ] -excludes = [ "tkinter" ] +with open("README.md", "r", encoding="utf-8") as fh: + longDescription = fh.read() -options = { - "build_exe": { - "optimize": 4, - "excludes": excludes, - "include_files": include_files - } -} +with open("requirements.txt", "r", encoding="utf-8") as r: + requires = [x for x in r] -executables = [ - Executable("GeckoLoader.py"), -] +setuptools.setup( + name='geckoloader', + version=geckoloader.__version__, + description='Simple python library for extracting and rebuilding ISOs', + long_description=longDescription, + long_description_content_type="text/markdown", + url='https://github.com/JoshuaMKW/GeckoLoader', + author='JoshuaMK', + author_email='joshuamkw2002@gmail.com', + license='GNU General Public License v3.0', + packages=setuptools.find_packages(), + include_package_data=True, + install_requires=requires, -setup(name = "GeckoLoader", - version = "7.1.0", - description = "DOL Patcher for extending the codespace of Wii/GC games", - executables = [Executable("GeckoLoader.py", icon=os.path.join("bin", "icon.ico"))], - author = "JoshuaMK", - author_email = "joshuamkw2002@gmail.com", - options = options - ) \ No newline at end of file + classifiers=[ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'Programming Language :: Python :: 3.8', + 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', + 'Operating System :: OS Independent' + ], + python_requires='>=3.8', +) \ No newline at end of file diff --git a/tools.py b/tools.py index ba4f3b1..993a281 100644 --- a/tools.py +++ b/tools.py @@ -3,6 +3,7 @@ import os from io import IOBase from argparse import ArgumentParser +from typing import Dict, Optional, Set, Tuple try: import colorama @@ -54,6 +55,7 @@ def align_byte_size(obj, alignment: int, fillchar="00"): else: raise NotImplementedError(f"Aligning the size of class {type(obj)} is unsupported") +""" def color_text(text: str, textToColor: list=[("", None)], defaultColor: str=None) -> str: currentColor = None formattedText = "" @@ -92,6 +94,26 @@ def color_text(text: str, textToColor: list=[("", None)], defaultColor: str=None formattedText += char return formattedText + TRESET +""" + +def color_text(text: str, textToColor: Optional[Dict[str, str]] = None, defaultColor: Optional[str] = "") -> str: + currentColor = None + formattedText = "" + + if textToColor is None: + return f"{defaultColor}{text}{TRESET}" + + for char in text: + if char in textToColor: + newColor = textToColor[char] + if currentColor != newColor: + formattedText += TRESET + formattedText += newColor + currentColor = newColor + formattedText += char + + return formattedText + class CommandLineParser(ArgumentParser): From dfaab6593958a1304983ec8cf0c75d6422d52a6a Mon Sep 17 00:00:00 2001 From: JoshuaMK <60854312+JoshuaMKW@users.noreply.github.com> Date: Sun, 20 Mar 2022 03:45:52 -0500 Subject: [PATCH 16/17] Make README good --- README.md | 74 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 61 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index cc0db00..421b32d 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,87 @@ -# GeckoLoader -**GeckoLoader is a command line tool, providing an easy way to have near unlimited code space, allowing thousands of lines of gecko code for every Wii/GCN game.** +![GeckoLoaderLogo](https://user-images.githubusercontent.com/60854312/159154005-b666fa45-67a6-4c6b-8a61-b986676e6083.png) -**Windows** +--- + +### GeckoLoader is a command line tool, providing an easy way to have near unlimited code space, allowing thousands of lines of gecko code for every Wii/GCN game. + +## Windows ![Imgur](https://i.imgur.com/3NiQ4T4.png) -**Linux** +## Linux ![Imgur](https://i.imgur.com/6cDiMW7.png) -**Codes** +--- + +## Installation + +Run the installer and choose to install `GeckoLoader` + +## Usage + +### Codes `GeckoLoader` supports 2 methods: 1. GCT files (Raw codelist) 2. Textual Codelist (Ocarina Manager or Dolphin Format txt files) -`GeckoLoader` also supports the ability to use a folder filled with GCT files and/or Textual codelists as input for multi codelist patching +`GeckoLoader` also supports the ability to use a folder filled with GCT files and/or Textual codelists as input for multi codelist patching. -**DOL File** +### DOL -`GeckoLoader` needs a valid `dol` file to patch with your codes. Simply supply the path to your `dol` file in either the GUI or the CLI +`GeckoLoader` needs a valid `dol` file to patch with your codes. Simply supply the path to your `dol` file in either the GUI or the CLI. `GeckoLoader` also supports patching the same `dol` file multiple times until the file becomes filled with section data. -**Steps to compile GeckoLoader** - -Run the installer and choose to install `GeckoLoader` +### Compilation Then you can do either: + 1. In command prompt, input `GeckoLoader -h` for help on syntax and options 2. Run the command `GeckoLoader ` filling in the variables as needed Or: + 1. Fill out the relevant data in the GUI - 2. Click the RUN button + 2. Click the "RUN" button + +Your new patched `dol` file will be in the folder `./geckoloader-build` by default. + +## Common Issues + +### The allocation was invalid + +This means the manual allocation amount you've passed into GeckoLoader is not a hexidecimal number. + +### The codehandler hook address was beyond bounds + +This means the manual hook address you've passed into GeckoLoader is beyond valid range. Values from 0x80000000 to 0x817FFFFF (inclusive) are allowed. + +### The codehandler hook address was invalid + +This means the manual hook address you've passed into GeckoLoader is not a hexidecimal number. + +### There are no unused sections left for GeckoLoader to use + +This means you've used up all of the remaining text/data sections, and GeckoLoader can't make space for itself. Try using a fresh DOL if this has been patched many times. Otherwise you're out of luck. + +### Init address specified for GeckoLoader (x) clobbers existing dol sections + +This means the address you specified manually for GeckoLoader to initialize at is about to mutilate your game and thus, try a safer address instead. + +### Failed to find a hook address + +This means it couldn't find a known location to hook to. This can be resolved by trying different hooktypes, or manually specifying a hook address. + +### Allocated codespace was smaller than the given codelist + +This means you manually specified an allocation size that happens to be smaller than the codelist's minimum space requirements. Give it some more room to work with or let it automatically decide the allocation amount. + +## Credits + +- Wiimm - Original idea and implementation this is based from +- Riidefi - Taught me C/C++, which this uses -Your new patched `dol` file will be in the folder `./geckoloader-build` by default +**Make sure to give proper credit to myself (JoshuaMK) and this project when you utilize it in a mod! It's greatly appreciated.** From f9e618416fe0c0b3d1fd6052e4797c0f84f7b633 Mon Sep 17 00:00:00 2001 From: JoshuaMK <60854312+JoshuaMKW@users.noreply.github.com> Date: Mon, 21 Mar 2022 02:38:33 -0500 Subject: [PATCH 17/17] Refactor progress --- .qt_for_python/lupdate/main_ui.qt.ts | 309 +++++++ build.py | 52 +- geckoloader/__main__.py | 51 ++ children_ui.py => geckoloader/children_ui.py | 156 ++-- geckoloader/cli.py | 280 ++++++ fileutils.py => geckoloader/fileutils.py | 48 +- GeckoLoader.py => geckoloader/gui.py | 519 +++-------- kernel.py => geckoloader/kernel.py | 280 ++---- geckoloader/main_ui.py | 847 ++++++++++++++++++ tools.py => geckoloader/tools.py | 5 +- .../versioncheck.py | 0 kernel/codehandler.hxx | 20 - kernel/globals.hxx | 97 -- kernel/kernel.cpp | 73 -- kernel/kernel.hxx | 10 - kernel/memory.hxx | 161 ---- kernel/ticket.cpp | 52 -- kernel/ticket.hxx | 28 - kernel/types.h | 33 - loader.cpp | 14 +- main_ui.py | 762 ---------------- requirements.txt | 5 + 22 files changed, 1850 insertions(+), 1952 deletions(-) create mode 100644 .qt_for_python/lupdate/main_ui.qt.ts create mode 100644 geckoloader/__main__.py rename children_ui.py => geckoloader/children_ui.py (54%) create mode 100644 geckoloader/cli.py rename fileutils.py => geckoloader/fileutils.py (76%) rename GeckoLoader.py => geckoloader/gui.py (53%) rename kernel.py => geckoloader/kernel.py (73%) create mode 100644 geckoloader/main_ui.py rename tools.py => geckoloader/tools.py (98%) rename versioncheck.py => geckoloader/versioncheck.py (100%) delete mode 100644 kernel/codehandler.hxx delete mode 100644 kernel/globals.hxx delete mode 100644 kernel/kernel.cpp delete mode 100644 kernel/kernel.hxx delete mode 100644 kernel/memory.hxx delete mode 100644 kernel/ticket.cpp delete mode 100644 kernel/ticket.hxx delete mode 100644 kernel/types.h delete mode 100644 main_ui.py create mode 100644 requirements.txt diff --git a/.qt_for_python/lupdate/main_ui.qt.ts b/.qt_for_python/lupdate/main_ui.qt.ts new file mode 100644 index 0000000..5b17fa6 --- /dev/null +++ b/.qt_for_python/lupdate/main_ui.qt.ts @@ -0,0 +1,309 @@ + + + + Dialog + + + FULL + + + + + MINI + + + + + VI + + + + + GX + + + + + PAD + + + + + ACTIVE + + + + + ALL + + + + + MainWindow + + + &File + + + + + &Edit + + + + + &Help + + + + + &Open Session... + + + + + Open a session + + + + + Ctrl+O + + + + + &Close Session... + + + + + Close the current session + + + + + Ctrl+Shift+C + + + + + &Save Session + + + + + Save the current session + + + + + Ctrl+S + + + + + &Save Session As... + + + + + Save the current session to the specified location + + + + + Ctrl+Shift+S + + + + + Undo + + + + + Undo the last action + + + + + Ctrl+Z + + + + + Redo + + + + + Redo the last action + + + + + Ctrl+Shift+Z + + + + + Cut + + + + + Cuts the selected text and places it on the clipboard + + + + + Ctrl+X + + + + + Copy + + + + + Copies the selected text and places it on the clipboard + + + + + Ctrl+C + + + + + Paste + + + + + Paste the contents of the clipboard + + + + + Ctrl+V + + + + + Delete + + + + + Deletes the selected text + + + + + Select All + + + + + Select all of the text + + + + + Ctrl+A + + + + + &Preferences... + + + + + Open the application preferences dialog + + + + + About &GeckoLoader... + + + + + About &Qt... + + + + + &Check Update + + + + + Files + + + + + Open DOL + + + + + Open Codes + + + + + Open Folder + + + + + Destination + + + + + Options + + + + + Allocation + + + + + AUTO + + + + + Codehandler + + + + + Code Hook + + + + + Include Codes + + + + + Advanced Settings + + + + + RUN + + + + diff --git a/build.py b/build.py index 3bb5859..6aa7bfd 100644 --- a/build.py +++ b/build.py @@ -1,28 +1,32 @@ -import os -import sys -from cx_Freeze import setup, Executable +import geckoloader +import setuptools -include_files = ["bin/"] -excludes = ["tkinter"] +with open("README.md", "r", encoding="utf-8") as fh: + longDescription = fh.read() -options = { - "build_exe": { - "optimize": 4, - "excludes": excludes, - "include_files": include_files - } -} +with open("requirements.txt", "r", encoding="utf-8") as r: + requires = [x for x in r] -executables = [ - Executable("GeckoLoader.py"), -] +setuptools.setup( + name='GeckoLoader', + version=geckoloader.__version__, + description='DOL Patcher for extending the codespace of Wii/GC games', + long_description=longDescription, + long_description_content_type="text/markdown", + url='https://github.com/JoshuaMKW/geckoloader', + author='JoshuaMK', + author_email='joshuamkw2002@gmail.com', + license='GNU General Public License v3.0', + packages=setuptools.find_packages(), + include_package_data=True, + install_requires=requires, -setup(name="GeckoLoader", - version="7.1.0", - description="DOL Patcher for extending the codespace of Wii/GC games", - executables=[Executable( - "GeckoLoader.py", icon=os.path.join("bin", "icon.ico"))], - author="JoshuaMK", - author_email="joshuamkw2002@gmail.com", - options=options - ) + classifiers=[ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'Programming Language :: Python :: 3.8', + 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', + 'Operating System :: OS Independent' + ], + python_requires='>=3.8', +) \ No newline at end of file diff --git a/geckoloader/__main__.py b/geckoloader/__main__.py new file mode 100644 index 0000000..d938de9 --- /dev/null +++ b/geckoloader/__main__.py @@ -0,0 +1,51 @@ +# Written by JoshuaMK 2020 + +from argparse import Namespace +import atexit +from enum import Enum +import logging +import os +import pickle as cPickle +import re +import shutil +import signal +import sys +import tempfile +from contextlib import redirect_stderr, redirect_stdout +from distutils.version import LooseVersion +from io import StringIO +from pathlib import Path +from typing import Any, Dict + +from PyQt5 import QtCore, QtGui, QtWidgets +from geckoloader.gui import GeckoLoaderGUI + +from pyfile.children_ui import PrefWindow, SettingsWindow +from dolreader.dol import DolFile +from fileutils import get_program_folder, resource_path +from kernel import CodeHandler, KernelLoader +from main_ui import MainWindow +from tools import CommandLineParser, color_text +from versioncheck import Updater +from geckoloader import __version__ +from geckoloader.cli import GeckoLoaderCli + + + + +if __name__ == "__main__": + cli = GeckoLoaderCli('GeckoLoader', __version__, + description='Dol editing tool for allocating extended codespace') + + if len(sys.argv) == 1: + cli.print_splash() + app = GeckoLoaderGUI(cli) + signal.signal(signal.SIGINT, signal.SIG_DFL) + app.run() + elif '--checkupdate' in sys.argv: + cli.check_updates() + elif '--splash' in sys.argv: + cli.print_splash() + else: + args = cli.parse_args() + cli._exec(args, TMPDIR) diff --git a/children_ui.py b/geckoloader/children_ui.py similarity index 54% rename from children_ui.py rename to geckoloader/children_ui.py index fae7d00..224b849 100644 --- a/children_ui.py +++ b/geckoloader/children_ui.py @@ -1,13 +1,18 @@ import sys -from PyQt5 import QtCore, QtGui, QtWidgets +from PySide6.QtCore import QMetaObject, QSize, Qt +from PySide6.QtGui import QFont, QIcon, QPixmap +from PySide6.QtWidgets import (QApplication, QCheckBox, QComboBox, QDialog, + QDialogButtonBox, QGridLayout, QLabel, + QLineEdit, QSizePolicy) -from fileutils import resource_path +from geckoloader.fileutils import resource_path -class PrefWindow(QtWidgets.QDialog): +class PrefWindow(QDialog): def __init__(self): - super().__init__(None, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint) + super().__init__(None, Qt.WindowSystemMenuHint | + Qt.WindowTitleHint | Qt.WindowCloseButtonHint) self.setup_ui() @@ -15,36 +20,38 @@ def setup_ui(self): self.setObjectName("Dialog") self.setFixedSize(300, 120) self.setModal(True) - icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap(str(resource_path("bin/icon.ico"))), QtGui.QIcon.Normal, QtGui.QIcon.Off) + icon = QIcon() + icon.addPixmap(QPixmap( + str(resource_path("bin/icon.ico"))), QIcon.Normal, QIcon.Off) self.setWindowIcon(icon) - #Buttonbox - self.buttonBox = QtWidgets.QDialogButtonBox(self) + # Buttonbox + self.buttonBox = QDialogButtonBox(self) self.buttonBox.setFixedSize(280, 30) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + self.buttonBox.setOrientation(Qt.Horizontal) + self.buttonBox.setStandardButtons( + QDialogButtonBox.Cancel | QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") - self.formLayoutWidget = QtWidgets.QGridLayout(self) + self.formLayoutWidget = QGridLayout(self) self.formLayoutWidget.setObjectName("formLayoutWidget") - self.comboBoxLayout = QtWidgets.QGridLayout() + self.comboBoxLayout = QGridLayout() self.comboBoxLayout.setVerticalSpacing(10) self.comboBoxLayout.setObjectName("comboBoxLayout") - #qtstyle box - self.qtstyleSelect = QtWidgets.QComboBox() + # qtstyle box + self.qtstyleSelect = QComboBox() self.qtstyleSelect.setObjectName("qtstyleSelect") self.comboBoxLayout.addWidget(self.qtstyleSelect, 1, 1, 1, 1) - #qtstyle label - self.qtstyleLabel = QtWidgets.QLabel() + # qtstyle label + self.qtstyleLabel = QLabel() self.qtstyleLabel.setObjectName("qtstyleLabel") self.comboBoxLayout.addWidget(self.qtstyleLabel, 1, 0, 1, 1) - #qtdark theme - self.qtdarkButton = QtWidgets.QCheckBox() + # qtdark theme + self.qtdarkButton = QCheckBox() self.qtdarkButton.setObjectName("formalnaming") self.qtdarkButton.setText("Dark Theme") self.comboBoxLayout.addWidget(self.qtdarkButton, 2, 0, 1, 1) @@ -56,128 +63,137 @@ def setup_ui(self): self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) - QtCore.QMetaObject.connectSlotsByName(self) + QMetaObject.connectSlotsByName(self) def retranslateUi(self): - self.setWindowTitle(QtWidgets.QApplication.translate("Dialog", "Preferences", None)) - self.qtstyleLabel.setText(QtWidgets.QApplication.translate("Dialog", "GUI Style:", None)) + self.setWindowTitle(QApplication.translate( + "Dialog", "Preferences", None)) + self.qtstyleLabel.setText( + QApplication.translate("Dialog", "GUI Style:", None)) -class SettingsWindow(QtWidgets.QDialog): + +class SettingsWindow(QDialog): def __init__(self): - super().__init__(None, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint) + super().__init__(None, Qt.WindowSystemMenuHint | + Qt.WindowTitleHint | Qt.WindowCloseButtonHint) self.setup_ui() def setup_ui(self): self.setObjectName("Dialog") - + if sys.platform == "win32": self.setFixedSize(300, 240) else: self.setFixedSize(370, 240) - + self.setModal(True) - icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap(str(resource_path("bin/icon.ico"))), QtGui.QIcon.Normal, QtGui.QIcon.Off) + icon = QIcon() + icon.addPixmap(QPixmap( + str(resource_path("bin/icon.ico"))), QIcon.Normal, QIcon.Off) self.setWindowIcon(icon) - #Buttonbox - self.buttonBox = QtWidgets.QDialogButtonBox(self) + # Buttonbox + self.buttonBox = QDialogButtonBox(self) if sys.platform == "win32": self.buttonBox.setFixedSize(280, 30) else: self.buttonBox.setFixedSize(350, 30) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Ok) + self.buttonBox.setOrientation(Qt.Horizontal) + self.buttonBox.setStandardButtons(QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") - self.formLayoutWidget = QtWidgets.QGridLayout(self) + self.formLayoutWidget = QGridLayout(self) self.formLayoutWidget.setObjectName("formLayoutWidget") - self.comboBoxLayout = QtWidgets.QGridLayout() + self.comboBoxLayout = QGridLayout() self.comboBoxLayout.setVerticalSpacing(10) self.comboBoxLayout.setObjectName("comboBoxLayout") - #protect codes - self.protectCodes = QtWidgets.QCheckBox() + # protect codes + self.protectCodes = QCheckBox() self.protectCodes.setObjectName("protectCodes") self.protectCodes.setText("Protect Game (Prevent user codes)") self.comboBoxLayout.addWidget(self.protectCodes, 1, 0, 1, 1) - #encrypt codes - self.encryptCodes = QtWidgets.QCheckBox() + # encrypt codes + self.encryptCodes = QCheckBox() self.encryptCodes.setObjectName("encryptCodes") self.encryptCodes.setText("Encrypt codes") self.comboBoxLayout.addWidget(self.encryptCodes, 2, 0, 1, 1) - #optimize codes - self.optimizeCodes = QtWidgets.QCheckBox() + # optimize codes + self.optimizeCodes = QCheckBox() self.optimizeCodes.setObjectName("optimizeCodes") self.optimizeCodes.setText("Optimize codes") self.optimizeCodes.setChecked(True) self.comboBoxLayout.addWidget(self.optimizeCodes, 3, 0, 1, 1) - #Codehook Address Label - self.codehookLabel = QtWidgets.QLabel() + # Codehook Address Label + self.codehookLabel = QLabel() self.codehookLabel.setObjectName("codehookLabel") self.comboBoxLayout.addWidget(self.codehookLabel, 4, 0, 1, 1) - #Codehook Address Textbox - self.codehookLineEdit = QtWidgets.QLineEdit() + # Codehook Address Textbox + self.codehookLineEdit = QLineEdit() self.codehookLineEdit.setEnabled(False) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.codehookLineEdit.sizePolicy().hasHeightForWidth()) + sizePolicy.setHeightForWidth( + self.codehookLineEdit.sizePolicy().hasHeightForWidth()) self.codehookLineEdit.setSizePolicy(sizePolicy) - self.codehookLineEdit.setMinimumSize(QtCore.QSize(79, 23)) - self.codehookLineEdit.setMaximumSize(QtCore.QSize(79, 23)) - font = QtGui.QFont() + self.codehookLineEdit.setMinimumSize(QSize(79, 23)) + self.codehookLineEdit.setMaximumSize(QSize(79, 23)) + font = QFont() font.setFamily("Consolas") font.setPointSize(12) font.setWeight(42) self.codehookLineEdit.setFont(font) self.codehookLineEdit.setText("") self.codehookLineEdit.setMaxLength(8) - self.codehookLineEdit.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter) + self.codehookLineEdit.setAlignment( + Qt.AlignLeading | Qt.AlignCenter | Qt.AlignVCenter) self.codehookLineEdit.setObjectName("codehookLineEdit") self.comboBoxLayout.addWidget(self.codehookLineEdit, 4, 1, 1, 1) - #kernelHook Address Label - self.kernelHookLabel = QtWidgets.QLabel() + # kernelHook Address Label + self.kernelHookLabel = QLabel() self.kernelHookLabel.setObjectName("kernelHookLabel") self.comboBoxLayout.addWidget(self.kernelHookLabel, 5, 0, 1, 1) - #kernelHook Address Textbox - self.kernelHookLineEdit = QtWidgets.QLineEdit() + # kernelHook Address Textbox + self.kernelHookLineEdit = QLineEdit() self.kernelHookLineEdit.setEnabled(False) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.kernelHookLineEdit.sizePolicy().hasHeightForWidth()) + sizePolicy.setHeightForWidth( + self.kernelHookLineEdit.sizePolicy().hasHeightForWidth()) self.kernelHookLineEdit.setSizePolicy(sizePolicy) - self.kernelHookLineEdit.setMinimumSize(QtCore.QSize(79, 23)) - self.kernelHookLineEdit.setMaximumSize(QtCore.QSize(79, 23)) - font = QtGui.QFont() + self.kernelHookLineEdit.setMinimumSize(QSize(79, 23)) + self.kernelHookLineEdit.setMaximumSize(QSize(79, 23)) + font = QFont() font.setFamily("Consolas") font.setPointSize(12) font.setWeight(42) self.kernelHookLineEdit.setFont(font) self.kernelHookLineEdit.setText("") self.kernelHookLineEdit.setMaxLength(8) - self.kernelHookLineEdit.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter) + self.kernelHookLineEdit.setAlignment( + Qt.AlignLeading | Qt.AlignCenter | Qt.AlignVCenter) self.kernelHookLineEdit.setObjectName("kernelHookLineEdit") self.comboBoxLayout.addWidget(self.kernelHookLineEdit, 5, 1, 1, 1) - #verbosity label - self.verbosityLabel = QtWidgets.QLabel() + # verbosity label + self.verbosityLabel = QLabel() self.verbosityLabel.setObjectName("verbosityLabel") self.comboBoxLayout.addWidget(self.verbosityLabel, 6, 0, 1, 1) - #verbosity box - self.verbositySelect = QtWidgets.QComboBox() + # verbosity box + self.verbositySelect = QComboBox() self.verbositySelect.addItems(["1", "2", "3", "0"]) self.verbositySelect.setObjectName("verbositySelect") self.comboBoxLayout.addWidget(self.verbositySelect, 6, 1, 1, 1) @@ -189,7 +205,7 @@ def setup_ui(self): self.retranslateUi() self.buttonBox.accepted.connect(self.accept) - QtCore.QMetaObject.connectSlotsByName(self) + QMetaObject.connectSlotsByName(self) def set_edit_fields(self): self.protectCodes.setEnabled(True) @@ -200,7 +216,11 @@ def set_edit_fields(self): self.verbositySelect.setEnabled(True) def retranslateUi(self): - self.setWindowTitle(QtWidgets.QApplication.translate("Dialog", "Advanced Settings", None)) - self.codehookLabel.setText(QtWidgets.QApplication.translate("Dialog", "Codehook Address:", None)) - self.kernelHookLabel.setText(QtWidgets.QApplication.translate("Dialog", "Kernel Init Address:", None)) - self.verbosityLabel.setText(QtWidgets.QApplication.translate("Dialog", "Verbosity:", None)) + self.setWindowTitle(QApplication.translate( + "Dialog", "Advanced Settings", None)) + self.codehookLabel.setText(QApplication.translate( + "Dialog", "Codehook Address:", None)) + self.kernelHookLabel.setText(QApplication.translate( + "Dialog", "Kernel Init Address:", None)) + self.verbosityLabel.setText( + QApplication.translate("Dialog", "Verbosity:", None)) diff --git a/geckoloader/cli.py b/geckoloader/cli.py new file mode 100644 index 0000000..54e2a7e --- /dev/null +++ b/geckoloader/cli.py @@ -0,0 +1,280 @@ +# Written by JoshuaMK 2020 + +from argparse import Namespace +import atexit +import shutil +import tempfile +from distutils.version import LooseVersion +from pathlib import Path +from typing import Any, Dict +from dolreader.dol import DolFile +from fileutils import resource_path +from kernel import CodeHandler, KernelLoader +from tools import CommandLineParser, color_text +from versioncheck import Updater + +from geckoloader import __version__ + +try: + import colorama + from colorama import Fore, Style + colorama.init() + TRESET = Style.RESET_ALL + TGREEN = Fore.GREEN + TGREENLIT = Style.BRIGHT + Fore.GREEN + TYELLOW = Fore.YELLOW + TYELLOWLIT = Style.BRIGHT + Fore.YELLOW + TRED = Fore.RED + TREDLIT = Style.BRIGHT + Fore.RED +except ImportError: + TRESET = '' + TGREEN = '' + TGREENLIT = '' + TYELLOW = '' + TYELLOWLIT = '' + TRED = '' + TREDLIT = '' + +TMPDIR = Path(tempfile.mkdtemp(prefix="GeckoLoader-")) + +@atexit.register +def clean_tmp_resources(): + tmpfolder = TMPDIR.parent + for entry in tmpfolder.iterdir(): + if entry.name.startswith("GeckoLoader-"): + shutil.rmtree(entry, ignore_errors=True) + + +class GeckoLoaderCli(CommandLineParser): + def __init__(self, name, version: str = None, description: str = ''): + super().__init__(prog=(f"{name} {version}"), + description=description, allow_abbrev=False) + self.__version__ = version + self.__doc__ = description + + self.add_argument('dolfile', help='DOL file') + self.add_argument('codelist', help='Folder or Gecko GCT|TXT file') + self.add_argument('-a', '--alloc', + help='Define the size of the code allocation in hex, only applies when using the ARENA space', + metavar='SIZE') + self.add_argument('-i', '--init', + help='Define where GeckoLoader is initialized in hex', + metavar='ADDRESS') + self.add_argument('-tc', '--txtcodes', + help='''["ACTIVE", "ALL"] What codes get parsed when a txt file is used. + "ALL" makes all codes get parsed, + "ACTIVE" makes only activated codes get parsed. + "ACTIVE" is the default''', + default='ACTIVE', + metavar='TYPE') + self.add_argument('--handler', + help='''["MINI", "FULL"] Which codehandler gets used. "MINI" uses a smaller codehandler + which only supports (0x, 2x, Cx, and E0 types) and supports up to + 600 lines of gecko codes when using the legacy codespace. + "FULL" is the standard codehandler, supporting up to 350 lines of code + in the legacy codespace. "FULL" is the default''', + default='FULL', + choices=['MINI', 'FULL'], + metavar='TYPE') + self.add_argument('--hooktype', + help='''["VI", "GX", "PAD"] The type of hook used for the RAM search. "VI" or "GX" are recommended, + although "PAD" can work just as well. "VI" is the default''', + default='VI', + choices=['VI', 'GX', 'PAD'], + metavar='HOOK') + self.add_argument('--hookaddress', + help='Choose where the codehandler hooks to in hex, overrides auto hooks', + metavar='ADDRESS') + self.add_argument('-o', '--optimize', + help='''Optimizes the codelist by directly patching qualifying + ram writes into the dol file, and removing them from the codelist''', + action='store_true') + self.add_argument('-p', '--protect', + help='''Targets and nullifies the standard codehandler provided by loaders and Dolphin Emulator, + only applies when the ARENA is used''', + action='store_true') + self.add_argument('--dest', + help='Target path to put the modified DOL, can be a folder or file', + metavar='PATH') + self.add_argument('--checkupdate', + help='''Checks to see if a new update exists on the GitHub Repository releases page, + this option overrides all other commands.''', + action='store_true') + self.add_argument('--splash', + help='''Print the splash screen, this option overrides + all other commands excluding --checkupdate''', + action='store_true') + self.add_argument('--encrypt', + help='Encrypts the codelist on compile time, helping to slow the snoopers', + action='store_true') + self.add_argument('-q', '--quiet', + help='Print nothing to the console', + action='store_true') + self.add_argument('-v', '--verbose', + help='Print extra info to the console', + default=0, + action='count') + + def __str__(self) -> str: + return self.__doc__ + + def print_splash(self): + helpMessage = 'Try option -h for more info on this program'.center( + 64, ' ') + version = self.__version__.rjust(9, ' ') + + logo = [' ', + ' ╔═══════════════════════════════════════════════════════════╗ ', + ' ║ ║ ', + ' ║ ┌───┐┌───┐┌───┐┌┐┌─┐┌───┐┌┐ ┌───┐┌───┐┌───┐┌───┐┌───┐ ║ ', + ' ║ │┌─┐││┌──┘│┌─┐││││┌┘│┌─┐│││ │┌─┐││┌─┐│└┐┌┐││┌──┘│┌─┐│ ║ ', + ' ║ ││ └┘│└──┐││ └┘│└┘┘ ││ ││││ ││ ││││ ││ │││││└──┐│└─┘│ ║ ', + ' ║ ││┌─┐│┌──┘││ ┌┐│┌┐│ ││ ││││ ┌┐││ │││└─┘│ │││││┌──┘│┌┐┌┘ ║ ', + ' ║ │└┴─││└──┐│└─┘││││└┐│└─┘││└─┘││└─┘││┌─┐│┌┘└┘││└──┐│││└┐ ║ ', + ' ║ └───┘└───┘└───┘└┘└─┘└───┘└───┘└───┘└┘ └┘└───┘└───┘└┘└─┘ ║ ', + ' ║ ║ ', + ' ║ ┌┐┌───┐┌───┐┌┐ ┌┐┌┐ ┌┐┌───┐┌─┐┌─┐┌┐┌─┐ ║ ', + ' ║ │││┌─┐││┌─┐│││ ││││ │││┌─┐││ └┘ ││││┌┘ ║ ', + ' ║ ││││ │││└──┐│└─┘│││ ││││ │││┌┐┌┐││└┘┘ ║ ', + ' ║ ┌──┐┌┐││││ ││└──┐││┌─┐│││ │││└─┘││││││││┌┐│ ┌──┐ ║ ', + ' ║ └──┘│└┘││└─┘││└─┘│││ │││└─┘││┌─┐││││││││││└┐└──┘ ║ ', + ' ║ └──┘└───┘└───┘└┘ └┘└───┘└┘ └┘└┘└┘└┘└┘└─┘ ║ ', + f' ║ {version} ║ ', + ' ╚═══════════════════════════════════════════════════════════╝ ', + ' ', + ' GeckoLoader is a cli tool for allowing extended ', + ' gecko code space in all Wii and GC games. ', + ' ', + f'{helpMessage}', + ' '] + + for line in logo: + print(color_text( + line, textToColor={'║': TREDLIT, '╔': TRED, '╚': TRED, '╝': TRED, '╗': TRED, '═': TRED}, defaultColor=TGREENLIT)) + + def check_updates(self): + repoChecker = Updater('JoshuaMKW', 'GeckoLoader') + + tag, status = repoChecker.get_newest_version() + + if status is False: + self.error(color_text(tag + '\n', defaultColor=TREDLIT), + print_usage=False) + + print('') + + if LooseVersion(tag) > LooseVersion(self.__version__): + print(color_text( + f' :: A new update is live at {repoChecker.gitReleases.format(repoChecker.owner, repoChecker.repo)}', defaultColor=TYELLOWLIT)) + print(color_text( + f' :: Current version is "{self.__version__}", Most recent version is "{tag}"', defaultColor=TYELLOWLIT)) + elif LooseVersion(tag) < LooseVersion(self.__version__): + print(color_text(' :: No update available', defaultColor=TGREENLIT)) + print(color_text( + f' :: Current version is "{self.__version__}(dev)", Most recent version is "{tag}(release)"', defaultColor=TGREENLIT)) + else: + print(color_text(' :: No update available', defaultColor=TGREENLIT)) + print(color_text( + f' :: Current version is "{self.__version__}(release)", Most recent version is "{tag}(release)"', defaultColor=TGREENLIT)) + + print('') + + def _validate_args(self, args: Namespace) -> Dict[str, Any]: + dolFile = Path(args.dolfile).resolve() + codeList = Path(args.codelist).resolve() + + if args.dest: + dest = Path(args.dest).resolve() + if dest.suffix == "": + dest = dest / dolFile.name + else: + dest = Path.cwd() / "geckoloader-build" / dolFile.name + + if args.alloc: + try: + _allocation = int(args.alloc, 16) + except ValueError: + self.error(color_text( + 'The allocation was invalid\n', defaultColor=TREDLIT)) + else: + _allocation = None + + if args.hookaddress: + try: + _codehook = int(args.hookaddress, 16) + except ValueError: + self.error(color_text( + 'The codehandler hook address was invalid\n', defaultColor=TREDLIT)) + if 0x80000000 > _codehook >= 0x81800000: + self.error(color_text( + 'The codehandler hook address was beyond bounds\n', defaultColor=TREDLIT)) + else: + _codehook = None + + if args.handler == KernelLoader.HandlerType.MINI: + codeHandlerFile = Path('bin/codehandler-mini.bin') + elif args.handler == KernelLoader.HandlerType.FULL: + codeHandlerFile = Path('bin/codehandler.bin') + else: + self.error(color_text( + f'Codehandler type {args.handler} is invalid\n', defaultColor=TREDLIT)) + + if not dolFile.is_file(): + self.error(color_text( + f'File "{dolFile}" does not exist\n', defaultColor=TREDLIT)) + + if not codeList.exists(): + self.error(color_text( + f'File/folder "{codeList}" does not exist\n', defaultColor=TREDLIT)) + + return {"dol": dolFile, + "codepath": codeList, + "codehandler": codeHandlerFile, + "destination": dest, + "allocation": _allocation, + "hookaddress": _codehook, + "hooktype": args.hooktype, + "initaddress": None if args.init is None else int(args.init, 16), + "includeall": args.txtcodes.lower() == "all", + "optimize": args.optimize, + "protect": args.protect, + "encrypt": args.encrypt, + "verbosity": args.verbose, + "quiet": args.quiet} + + def _exec(self, args): + context = self._validate_args(args) + + try: + with context["dol"].open("rb") as dol: + dolFile = DolFile(dol) + + with resource_path(context["codehandler"]).open("rb") as handler: + codeHandler = CodeHandler(handler) + + with resource_path("bin/geckoloader.bin").open("rb") as kernelfile: + geckoKernel = KernelLoader(kernelfile, + hookType=context["hooktype"], + hookAddress=context["hookaddress"], + initAddress=context["initaddress"], + allocation=context["allocation"], + includeAllCodes=context["includeall"], + optimizeCodes=context["optimize"], + protectCodes=context["protect"], + encryptCodes=context["encrypt"], + cli=self) + geckoKernel.verbosity = context["verbosity"] + if context["quiet"] == True: + geckoKernel.silence() + else: + geckoKernel.desilence() + + if not context["destination"].parent.exists(): + context["destination"].parent.mkdir( + parents=True, exist_ok=True) + + geckoKernel.build(context["codepath"], dolFile, + codeHandler, TMPDIR, context["destination"]) + + except FileNotFoundError as e: + self.error(color_text(e, defaultColor=TREDLIT)) diff --git a/fileutils.py b/geckoloader/fileutils.py similarity index 76% rename from fileutils.py rename to geckoloader/fileutils.py index ddd29fc..b495354 100644 --- a/fileutils.py +++ b/geckoloader/fileutils.py @@ -1,23 +1,28 @@ import struct -import sys -from os import getenv from pathlib import Path -from tools import align_byte_size, get_alignment - -def resource_path(relative_path: str = "") -> Path: +def resource_path(relPath: str = "") -> Path: """ Get absolute path to resource, works for dev and for cx_freeze """ - if getattr(sys, "frozen", False): - # The application is frozen - base_path = Path(sys.executable).parent + import sys + + if hasattr(sys, "_MEIPASS"): + return getattr(sys, "_MEIPASS", Path(__file__).parent) / relPath else: - base_path = Path(__file__).parent - - return base_path / relative_path + if getattr(sys, "frozen", False): + # The application is frozen + base_path = Path(sys.executable).parent + else: + base_path = Path(__file__).parent + + return base_path / relPath + def get_program_folder(folder: str = "") -> Path: """ Get path to appdata """ + from os import getenv + import sys + if sys.platform == "win32": datapath = Path(getenv("APPDATA")) / folder elif sys.platform == "darwin": @@ -32,56 +37,75 @@ def get_program_folder(folder: str = "") -> Path: raise NotImplementedError(f"{sys.platform} OS is unsupported") return datapath + def read_sbyte(f): return struct.unpack("b", f.read(1))[0] + def write_sbyte(f, val): f.write(struct.pack("b", val)) + def read_sint16(f): return struct.unpack(">h", f.read(2))[0] + def write_sint16(f, val): f.write(struct.pack(">h", val)) + def read_sint32(f): return struct.unpack(">i", f.read(4))[0] + def write_sint32(f, val): f.write(struct.pack(">i", val)) + def read_ubyte(f): return struct.unpack("B", f.read(1))[0] + def write_ubyte(f, val): f.write(struct.pack("B", val)) + def read_uint16(f): return struct.unpack(">H", f.read(2))[0] + def write_uint16(f, val): f.write(struct.pack(">H", val)) + def read_uint32(f): return struct.unpack(">I", f.read(4))[0] + def write_uint32(f, val): f.write(struct.pack(">I", val)) + def read_float(f): return struct.unpack(">f", f.read(4))[0] + def write_float(f, val): f.write(struct.pack(">f", val)) + def read_double(f): return struct.unpack(">d", f.read(4))[0] + def write_double(f, val): f.write(struct.pack(">d", val)) + def read_bool(f, vSize=1): return struct.unpack("B", f.read(vSize))[0] > 0 + def write_bool(f, val, vSize=1): - f.write(b'\x00'*(vSize-1) + b'\x01') if val is True else f.write(b'\x00' * vSize) + f.write(b'\x00'*(vSize-1) + + b'\x01') if val is True else f.write(b'\x00' * vSize) diff --git a/GeckoLoader.py b/geckoloader/gui.py similarity index 53% rename from GeckoLoader.py rename to geckoloader/gui.py index 44eb43f..ff682e4 100644 --- a/GeckoLoader.py +++ b/geckoloader/gui.py @@ -1,306 +1,31 @@ -# Written by JoshuaMK 2020 -from argparse import Namespace -import atexit -from enum import Enum import logging import os import pickle as cPickle import re -import shutil -import signal import sys -import tempfile from contextlib import redirect_stderr, redirect_stdout -from distutils.version import LooseVersion +from enum import Enum, IntEnum from io import StringIO from pathlib import Path -from typing import Any, Dict - -from PyQt5 import QtCore, QtGui, QtWidgets - -from children_ui import PrefWindow, SettingsWindow -from dolreader.dol import DolFile -from fileutils import get_program_folder, resource_path -from kernel import CodeHandler, KernelLoader -from main_ui import MainWindow -from tools import CommandLineParser, color_text -from versioncheck import Updater - -try: - import colorama - from colorama import Fore, Style - colorama.init() - TRESET = Style.RESET_ALL - TGREEN = Fore.GREEN - TGREENLIT = Style.BRIGHT + Fore.GREEN - TYELLOW = Fore.YELLOW - TYELLOWLIT = Style.BRIGHT + Fore.YELLOW - TRED = Fore.RED - TREDLIT = Style.BRIGHT + Fore.RED - -except ImportError: - TRESET = '' - TGREEN = '' - TGREENLIT = '' - TYELLOW = '' - TYELLOWLIT = '' - TRED = '' - TREDLIT = '' - -__version__ = "v7.1.0" - -TMPDIR = Path(tempfile.mkdtemp(prefix="GeckoLoader-")) - - -@atexit.register -def clean_tmp_resources(): - tmpfolder = TMPDIR.parent - for entry in tmpfolder.iterdir(): - if entry.name.startswith("GeckoLoader-"): - shutil.rmtree(entry, ignore_errors=True) - - -class GeckoLoaderCli(CommandLineParser): - - def __init__(self, name, version=None, description=''): - super().__init__(prog=(f"{name} {version}"), - description=description, allow_abbrev=False) - self.__version__ = version - self.__doc__ = description - - self.add_argument('dolfile', help='DOL file') - self.add_argument('codelist', help='Folder or Gecko GCT|TXT file') - self.add_argument('-a', '--alloc', - help='Define the size of the code allocation in hex, only applies when using the ARENA space', - metavar='SIZE') - self.add_argument('-i', '--init', - help='Define where GeckoLoader is initialized in hex', - metavar='ADDRESS') - self.add_argument('-tc', '--txtcodes', - help='''["ACTIVE", "ALL"] What codes get parsed when a txt file is used. - "ALL" makes all codes get parsed, - "ACTIVE" makes only activated codes get parsed. - "ACTIVE" is the default''', - default='ACTIVE', - metavar='TYPE') - self.add_argument('--handler', - help='''["MINI", "FULL"] Which codehandler gets used. "MINI" uses a smaller codehandler - which only supports (0x, 2x, Cx, and E0 types) and supports up to - 600 lines of gecko codes when using the legacy codespace. - "FULL" is the standard codehandler, supporting up to 350 lines of code - in the legacy codespace. "FULL" is the default''', - default='FULL', - choices=['MINI', 'FULL'], - metavar='TYPE') - self.add_argument('--hooktype', - help='''["VI", "GX", "PAD"] The type of hook used for the RAM search. "VI" or "GX" are recommended, - although "PAD" can work just as well. "VI" is the default''', - default='VI', - choices=['VI', 'GX', 'PAD'], - metavar='HOOK') - self.add_argument('--hookaddress', - help='Choose where the codehandler hooks to in hex, overrides auto hooks', - metavar='ADDRESS') - self.add_argument('-o', '--optimize', - help='''Optimizes the codelist by directly patching qualifying - ram writes into the dol file, and removing them from the codelist''', - action='store_true') - self.add_argument('-p', '--protect', - help='''Targets and nullifies the standard codehandler provided by loaders and Dolphin Emulator, - only applies when the ARENA is used''', - action='store_true') - self.add_argument('--dest', - help='Target path to put the modified DOL, can be a folder or file', - metavar='PATH') - self.add_argument('--checkupdate', - help='''Checks to see if a new update exists on the GitHub Repository releases page, - this option overrides all other commands.''', - action='store_true') - self.add_argument('--splash', - help='''Print the splash screen, this option overrides - all other commands excluding --checkupdate''', - action='store_true') - self.add_argument('--encrypt', - help='Encrypts the codelist on compile time, helping to slow the snoopers', - action='store_true') - self.add_argument('-q', '--quiet', - help='Print nothing to the console', - action='store_true') - self.add_argument('-v', '--verbose', - help='Print extra info to the console', - default=0, - action='count') - - def __str__(self) -> str: - return self.__doc__ - - def print_splash(self): - helpMessage = 'Try option -h for more info on this program'.center( - 64, ' ') - version = self.__version__.rjust(9, ' ') - - logo = [' ', - ' ╔═══════════════════════════════════════════════════════════╗ ', - ' ║ ║ ', - ' ║ ┌───┐┌───┐┌───┐┌┐┌─┐┌───┐┌┐ ┌───┐┌───┐┌───┐┌───┐┌───┐ ║ ', - ' ║ │┌─┐││┌──┘│┌─┐││││┌┘│┌─┐│││ │┌─┐││┌─┐│└┐┌┐││┌──┘│┌─┐│ ║ ', - ' ║ ││ └┘│└──┐││ └┘│└┘┘ ││ ││││ ││ ││││ ││ │││││└──┐│└─┘│ ║ ', - ' ║ ││┌─┐│┌──┘││ ┌┐│┌┐│ ││ ││││ ┌┐││ │││└─┘│ │││││┌──┘│┌┐┌┘ ║ ', - ' ║ │└┴─││└──┐│└─┘││││└┐│└─┘││└─┘││└─┘││┌─┐│┌┘└┘││└──┐│││└┐ ║ ', - ' ║ └───┘└───┘└───┘└┘└─┘└───┘└───┘└───┘└┘ └┘└───┘└───┘└┘└─┘ ║ ', - ' ║ ║ ', - ' ║ ┌┐┌───┐┌───┐┌┐ ┌┐┌┐ ┌┐┌───┐┌─┐┌─┐┌┐┌─┐ ║ ', - ' ║ │││┌─┐││┌─┐│││ ││││ │││┌─┐││ └┘ ││││┌┘ ║ ', - ' ║ ││││ │││└──┐│└─┘│││ ││││ │││┌┐┌┐││└┘┘ ║ ', - ' ║ ┌──┐┌┐││││ ││└──┐││┌─┐│││ │││└─┘││││││││┌┐│ ┌──┐ ║ ', - ' ║ └──┘│└┘││└─┘││└─┘│││ │││└─┘││┌─┐││││││││││└┐└──┘ ║ ', - ' ║ └──┘└───┘└───┘└┘ └┘└───┘└┘ └┘└┘└┘└┘└┘└─┘ ║ ', - f' ║ {version} ║ ', - ' ╚═══════════════════════════════════════════════════════════╝ ', - ' ', - ' GeckoLoader is a cli tool for allowing extended ', - ' gecko code space in all Wii and GC games. ', - ' ', - f'{helpMessage}', - ' '] - - for line in logo: - print(color_text( - line, textToColor={'║': TREDLIT, '╔': TRED, '╚': TRED, '╝': TRED, '╗': TRED, '═': TRED}, defaultColor=TGREENLIT)) - - def check_updates(self): - repoChecker = Updater('JoshuaMKW', 'GeckoLoader') - - tag, status = repoChecker.get_newest_version() - - if status is False: - self.error(color_text(tag + '\n', defaultColor=TREDLIT), - print_usage=False) - - print('') - - if LooseVersion(tag) > LooseVersion(self.__version__): - print(color_text( - f' :: A new update is live at {repoChecker.gitReleases.format(repoChecker.owner, repoChecker.repo)}', defaultColor=TYELLOWLIT)) - print(color_text( - f' :: Current version is "{self.__version__}", Most recent version is "{tag}"', defaultColor=TYELLOWLIT)) - elif LooseVersion(tag) < LooseVersion(self.__version__): - print(color_text(' :: No update available', defaultColor=TGREENLIT)) - print(color_text( - f' :: Current version is "{self.__version__}(dev)", Most recent version is "{tag}(release)"', defaultColor=TGREENLIT)) - else: - print(color_text(' :: No update available', defaultColor=TGREENLIT)) - print(color_text( - f' :: Current version is "{self.__version__}(release)", Most recent version is "{tag}(release)"', defaultColor=TGREENLIT)) - - print('') - - def _validate_args(self, args: Namespace) -> Dict[str, Any]: - dolFile = Path(args.dolfile).resolve() - codeList = Path(args.codelist).resolve() - - if args.dest: - dest = Path(args.dest).resolve() - if dest.suffix == "": - dest = dest / dolFile.name - else: - dest = Path.cwd() / "geckoloader-build" / dolFile.name - - if args.alloc: - try: - _allocation = int(args.alloc, 16) - except ValueError: - self.error(color_text( - 'The allocation was invalid\n', defaultColor=TREDLIT)) - else: - _allocation = None - - if args.hookaddress: - if 0x80000000 > int(args.hookaddress, 16) >= 0x81800000: - self.error(color_text( - 'The codehandler hook address was beyond bounds\n', defaultColor=TREDLIT)) - else: - try: - _codehook = int(args.hookaddress, 16) - except ValueError: - self.error(color_text( - 'The codehandler hook address was invalid\n', defaultColor=TREDLIT)) - else: - _codehook = None - - if args.handler == KernelLoader.HandlerType.MINI: - codeHandlerFile = Path('bin/codehandler-mini.bin') - elif args.handler == KernelLoader.HandlerType.FULL: - codeHandlerFile = Path('bin/codehandler.bin') - else: - self.error(color_text( - f'Codehandler type {args.handler} is invalid\n', defaultColor=TREDLIT)) - - if not dolFile.is_file(): - self.error(color_text( - f'File "{dolFile}" does not exist\n', defaultColor=TREDLIT)) - - if not codeList.exists(): - self.error(color_text( - f'File/folder "{codeList}" does not exist\n', defaultColor=TREDLIT)) - - return {"dol": dolFile, - "codepath": codeList, - "codehandler": codeHandlerFile, - "destination": dest, - "allocation": _allocation, - "hookaddress": _codehook, - "hooktype": args.hooktype, - "initaddress": None if args.init is None else int(args.init, 16), - "includeall": args.txtcodes.lower() == "all", - "optimize": args.optimize, - "protect": args.protect, - "encrypt": args.encrypt, - "verbosity": args.verbose, - "quiet": args.quiet} - - def _exec(self, args, tmpdir): - context = self._validate_args(args) - - try: - with context["dol"].open("rb") as dol: - dolFile = DolFile(dol) - - with resource_path(context["codehandler"]).open("rb") as handler: - codeHandler = CodeHandler(handler) - - with resource_path("bin/geckoloader.bin").open("rb") as kernelfile: - geckoKernel = KernelLoader(kernelfile, - hookType=context["hooktype"], - hookAddress=context["hookaddress"], - initAddress=context["initaddress"], - allocation=context["allocation"], - includeAllCodes=context["includeall"], - optimizeCodes=context["optimize"], - protectCodes=context["protect"], - encryptCodes=context["encrypt"], - cli=cli) - geckoKernel.verbosity = context["verbosity"] - if context["quiet"] == True: - geckoKernel.silence() - else: - geckoKernel.desilence() +from typing import Tuple - if not context["destination"].parent.exists(): - context["destination"].parent.mkdir( - parents=True, exist_ok=True) +from PySide6.QtCore import QRegularExpression, Qt +from PySide6.QtGui import QIcon, QPixmap, QRegularExpressionValidator +from PySide6.QtWidgets import (QApplication, QErrorMessage, QFileDialog, + QMessageBox, QStyleFactory, QTextEdit) - geckoKernel.build(context["codepath"], dolFile, - codeHandler, TMPDIR, context["destination"]) +from geckoloader import __version__ +from geckoloader.children_ui import PrefWindow, SettingsWindow +from geckoloader.cli import GeckoLoaderCli +from geckoloader.fileutils import get_program_folder, resource_path +from geckoloader.gui import GeckoLoaderGUI +from geckoloader.main_ui import MainWindow - except FileNotFoundError as e: - self.error(color_text(e, defaultColor=TREDLIT)) +class GeckoLoaderGUI(object): -class GUI(object): - - class Dialogs(Enum): + class Dialogs(IntEnum): LOAD_DEST = 0 LOAD_GCT = 1 LOAD_FOLDER = 2 @@ -325,11 +50,11 @@ def __init__(self, cli: GeckoLoaderCli): self.log = logging.getLogger(f"GeckoLoader {self.version}") - if not get_program_folder("GeckoLoader").exists(): - get_program_folder("GeckoLoader").mkdir() + pFolder = get_program_folder("GeckoLoader") + if not pFolder.exists(): + pFolder.mkdir() - hdlr = logging.FileHandler( - get_program_folder("GeckoLoader") / "error.log") + hdlr = logging.FileHandler(pFolder / "error.log") formatter = logging.Formatter( "\n%(levelname)s (%(asctime)s): %(message)s") hdlr.setFormatter(formatter) @@ -337,7 +62,7 @@ def __init__(self, cli: GeckoLoaderCli): def show_dialog(self, dialog_type=None): if dialog_type == "aboutqt": - QtWidgets.QMessageBox.aboutQt(self.app.activeWindow()) + QMessageBox.aboutQt(self.app.activeWindow()) elif dialog_type == "aboutGeckoLoader": desc = "".join(["GeckoLoader is a cross platform tool designed to give ", "the user the most efficient codespace usage possible.\n\n ", @@ -350,7 +75,7 @@ def show_dialog(self, dialog_type=None): "JoshuaMK \n\n", "All rights reserved."]) - QtWidgets.QMessageBox.about( + QMessageBox.about( self.app.activeWindow(), "About GeckoLoader", desc) elif dialog_type == "Preferences": self.uiprefs.show() @@ -361,12 +86,12 @@ def show_dialog(self, dialog_type=None): def version(self) -> str: return self.cli.__version__ - def _open_dol(self) -> tuple: + def _open_dol(self) -> Tuple[bool, str]: if self.dolPath is None: # Just start in the home directory - fname = str(QtWidgets.QFileDialog.getOpenFileName(self.ui, "Open DOL", str(Path.home()), + fname = str(QFileDialog.getOpenFileName(self.ui, "Open DOL", str(Path.home()), "Nintendo DOL Executable (*.dol);;All files (*)")[0]) else: # Start in the last directory used by the user - fname = str(QtWidgets.QFileDialog.getOpenFileName(self.ui, "Open DOL", str(self.dolPath.parent), + fname = str(QFileDialog.getOpenFileName(self.ui, "Open DOL", str(self.dolPath.parent), "Nintendo DOL Executable (*.dol);;All files (*)")[0]) if fname == "" or fname is None: # Make sure we have something to open @@ -380,21 +105,21 @@ def _open_dol(self) -> tuple: else: return False, "The file does not exist!" - def _load_codes(self, isFolder: bool = False) -> tuple: + def _load_codes(self, isFolder: bool = False) -> Tuple[bool, str]: if not isFolder: if self.codePath[0] is None: # Just start in the home directory - fname = str(QtWidgets.QFileDialog.getOpenFileName(self.ui, "Open Codelist", str(Path.home()), + fname = str(QFileDialog.getOpenFileName(self.ui, "Open Codelist", str(Path.home()), "Gecko Code Table (*.gct);;Gecko Codelist (*.txt);;All files (*)")[0]) else: # Start in the last directory used by the user - fname = str(QtWidgets.QFileDialog.getOpenFileName(self.ui, "Open Codelist", str(self.codePath[0].parent), + fname = str(QFileDialog.getOpenFileName(self.ui, "Open Codelist", str(self.codePath[0].parent), "Gecko Code Table (*.gct);;Gecko Codelist (*.txt);;All files (*)")[0]) else: if self.codePath[0] is None: # Just start in the home directory - fname = str(QtWidgets.QFileDialog.getExistingDirectory(self.ui, "Open Codelist", str(Path.home()), - QtWidgets.QFileDialog.ShowDirsOnly)) + fname = str(QFileDialog.getExistingDirectory(self.ui, "Open Codelist", str(Path.home()), + QFileDialog.ShowDirsOnly)) else: # Start in the last directory used by the user - fname = str(QtWidgets.QFileDialog.getExistingDirectory(self.ui, "Open Codelist", str(self.codePath[0].parent), - QtWidgets.QFileDialog.ShowDirsOnly)) + fname = str(QFileDialog.getExistingDirectory(self.ui, "Open Codelist", str(self.codePath[0].parent), + QFileDialog.ShowDirsOnly)) if fname == "" or fname is None: # Make sure we have something to open return False, None @@ -412,10 +137,10 @@ def _load_codes(self, isFolder: bool = False) -> tuple: def _open_dest(self) -> tuple: if self.dolPath is None: # Just start in the home directory - fname = str(QtWidgets.QFileDialog.getOpenFileName(self.ui, "Open DOL", str(Path.home()), + fname = str(QFileDialog.getOpenFileName(self.ui, "Open DOL", str(Path.home()), "Nintendo DOL Executable (*.dol);;All files (*)")[0]) else: # Start in the last directory used by the user - fname = str(QtWidgets.QFileDialog.getOpenFileName(self.ui, "Open DOL", str(self.dolPath.parent), + fname = str(QFileDialog.getOpenFileName(self.ui, "Open DOL", str(self.dolPath.parent), "Nintendo DOL Executable (*.dol);;All files (*)")[0]) if fname == "" or fname is None: # Make sure we have something to open @@ -428,10 +153,10 @@ def _open_dest(self) -> tuple: def _load_session(self) -> tuple: if self.sessionPath is None: - fname = str(QtWidgets.QFileDialog.getOpenFileName(self.ui, "Open Session", str(Path.home()), + fname = str(QFileDialog.getOpenFileName(self.ui, "Open Session", str(Path.home()), "GeckoLoader Session (*.gprf);;All files (*)")[0]) else: # Start in the last directory used by the user - fname = str(QtWidgets.QFileDialog.getOpenFileName(self.ui, "Open Session", str(self.sessionPath.parent), + fname = str(QFileDialog.getOpenFileName(self.ui, "Open Session", str(self.sessionPath.parent), "GeckoLoader Session (*.gprf);;All files (*)")[0]) if fname == "" or fname is None: # Make sure we have something to open @@ -472,10 +197,10 @@ def _load_session(self) -> tuple: def _save_session(self, saveAs=False): if saveAs or self.sessionPath is None or self.sessionPath == "": if self.sessionPath is None: # Just start in the home directory - fname = str(QtWidgets.QFileDialog.getSaveFileName(self.ui, "Save Session", str(Path.home()), + fname = str(QFileDialog.getSaveFileName(self.ui, "Save Session", str(Path.home()), "GeckoLoader Session (*.gprf);;All files (*)")[0]) else: # Start in the last directory used by the user - fname = str(QtWidgets.QFileDialog.getSaveFileName(self.ui, "Save Session", str(self.dolPath.parent), + fname = str(QFileDialog.getSaveFileName(self.ui, "Save Session", str(self.dolPath.parent), "GeckoLoader Session (*.gprf);;All files (*)")[0]) if fname == "" or fname is None: # Make sure we have something to open @@ -513,19 +238,19 @@ def _save_session(self, saveAs=False): def file_dialog_exec(self, event: Dialogs): try: - if event == GUI.Dialogs.LOAD_DOL: + if event == GeckoLoaderGUI.Dialogs.LOAD_DOL: status, msg = self._open_dol() - elif event == GUI.Dialogs.LOAD_GCT: + elif event == GeckoLoaderGUI.Dialogs.LOAD_GCT: status, msg = self._load_codes(False) - elif event == GUI.Dialogs.LOAD_FOLDER: + elif event == GeckoLoaderGUI.Dialogs.LOAD_FOLDER: status, msg = self._load_codes(True) - elif event == GUI.Dialogs.LOAD_DEST: + elif event == GeckoLoaderGUI.Dialogs.LOAD_DEST: status, msg = self._open_dest() - elif event == GUI.Dialogs.LOAD_SESSION: + elif event == GeckoLoaderGUI.Dialogs.LOAD_SESSION: status, msg = self._load_session() - elif event == GUI.Dialogs.SAVE_SESSION: + elif event == GeckoLoaderGUI.Dialogs.SAVE_SESSION: status, msg = self._save_session() - elif event == GUI.Dialogs.SAVE_SESSION_AS: + elif event == GeckoLoaderGUI.Dialogs.SAVE_SESSION_AS: status, msg = self._save_session(True) else: return @@ -534,12 +259,12 @@ def file_dialog_exec(self, event: Dialogs): return if status is False and msg is not None: - reply = QtWidgets.QErrorMessage(self) + reply = QErrorMessage(self) reply.setWindowTitle("I/O Failure") reply.setText(msg) reply.setInformativeText("Please try again.") - reply.setIcon(QtWidgets.QMessageBox.Warning) - reply.setStandardButtons(QtWidgets.QMessageBox.Ok) + reply.setIcon(QMessageBox.Warning) + reply.setStandardButtons(QMessageBox.Ok) reply.exec_() else: self.ui.set_edit_fields() @@ -576,7 +301,7 @@ def load_prefs(self): self.log.exception(e) # Use defaults for prefs else: # Input validation - if (p.get("qtstyle") in list(QtWidgets.QStyleFactory.keys()) or + if (p.get("qtstyle") in list(QStyleFactory.keys()) or p.get("qtstyle") == "Default"): self.prefs["qtstyle"] = p.get("qtstyle") @@ -590,7 +315,7 @@ def load_prefs(self): else: setCIndex(self.uiprefs.qtstyleSelect.findText( self.prefs.get("qtstyle"), - flags=QtCore.Qt.MatchFixedString)) + flags=Qt.MatchFixedString)) self.uiprefs.qtdarkButton.setChecked( self.prefs.get("darktheme")) @@ -626,7 +351,7 @@ def load_qtstyle(self, style, first_style_load=False): if first_style_load: setCIndex = self.uiprefs.qtstyleSelect.setCurrentIndex setCIndex(self.uiprefs.qtstyleSelect.findText(style, - flags=QtCore.Qt.MatchFixedString)) + flags=Qt.MatchFixedString)) def update_theme(self): if self.uiprefs.qtdarkButton.isChecked(): @@ -650,47 +375,30 @@ def display_update(self): else: _status = True - icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap( - str(resource_path(Path("bin/icon.ico")))), QtGui.QIcon.Normal, QtGui.QIcon.Off) + icon = QIcon() + icon.addPixmap(QPixmap( + str(resource_path(Path("bin/icon.ico")))), QIcon.Normal, QIcon.Off) if _status is False: - reply = QtWidgets.QErrorMessage() + reply = QErrorMessage() reply.setWindowIcon(icon) reply.setWindowTitle("Response Error") reply.setText(self._remove_ansi(_errpipe.getvalue())) reply.setInformativeText( "Make sure you have an internet connection") - reply.setIcon(QtWidgets.QMessageBox.Warning) - reply.setStandardButtons(QtWidgets.QMessageBox.Ok) + reply.setIcon(QMessageBox.Warning) + reply.setStandardButtons(QMessageBox.Ok) reply.exec_() else: - reply = QtWidgets.QMessageBox() + reply = QMessageBox() reply.setWindowIcon(icon) reply.setWindowTitle("Update Info") reply.setText(self._remove_ansi(_outpipe.getvalue()).strip( "\n") + "\n\nYou can find all GeckoLoader releases at:\nhttps://github.com/JoshuaMKW/GeckoLoader/releases") - reply.setIcon(QtWidgets.QMessageBox.Information) - reply.setStandardButtons(QtWidgets.QMessageBox.Ok) + reply.setIcon(QMessageBox.Information) + reply.setStandardButtons(QMessageBox.Ok) reply.exec_() - @staticmethod - def _enforce_mask(textbox: QtWidgets.QTextEdit, mask: int, _or: int = 0): - textbox.setText(textbox.text().strip()) - if len(textbox.text()) > 0: - _depth = len(hex(mask)[2:]) - _address = int(textbox.text(), 16) << ( - (_depth - len(textbox.text())) << 2) - - aligned = hex(((_address & mask) | _or) >> ( - (_depth - len(textbox.text())) << 2))[2:].upper() - textbox.setText(aligned) - - @staticmethod - def _remove_ansi(msg: str) -> str: - ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])') - return ansi_escape.sub('', msg) - def connect_signals(self): self.ui.actionPreferences.triggered.connect( lambda: self.show_dialog("Preferences")) @@ -702,21 +410,21 @@ def connect_signals(self): lambda: self.display_update()) self.ui.actionOpen.triggered.connect( - lambda: self.file_dialog_exec(GUI.Dialogs.LOAD_SESSION)) + lambda: self.file_dialog_exec(GeckoLoaderGUI.Dialogs.LOAD_SESSION)) self.ui.actionClose.triggered.connect(lambda: self.close_session()) self.ui.actionSave_As.triggered.connect( - lambda: self.file_dialog_exec(GUI.Dialogs.SAVE_SESSION_AS)) + lambda: self.file_dialog_exec(GeckoLoaderGUI.Dialogs.SAVE_SESSION_AS)) self.ui.actionSave.triggered.connect( - lambda: self.file_dialog_exec(GUI.Dialogs.SAVE_SESSION)) + lambda: self.file_dialog_exec(GeckoLoaderGUI.Dialogs.SAVE_SESSION)) self.ui.dolButton.clicked.connect( - lambda: self.file_dialog_exec(GUI.Dialogs.LOAD_DOL)) + lambda: self.file_dialog_exec(GeckoLoaderGUI.Dialogs.LOAD_DOL)) self.ui.gctFileButton.clicked.connect( - lambda: self.file_dialog_exec(GUI.Dialogs.LOAD_GCT)) + lambda: self.file_dialog_exec(GeckoLoaderGUI.Dialogs.LOAD_GCT)) self.ui.gctFolderButton.clicked.connect( - lambda: self.file_dialog_exec(GUI.Dialogs.LOAD_FOLDER)) + lambda: self.file_dialog_exec(GeckoLoaderGUI.Dialogs.LOAD_FOLDER)) self.ui.destButton.clicked.connect( - lambda: self.file_dialog_exec(GUI.Dialogs.LOAD_DEST)) + lambda: self.file_dialog_exec(GeckoLoaderGUI.Dialogs.LOAD_DEST)) self.ui.dolTextBox.textChanged.connect( lambda: self.ui.set_edit_fields()) @@ -744,6 +452,39 @@ def connect_signals(self): self.uiexSettings.kernelHookLineEdit.textChanged.connect(lambda: self._enforce_mask( self.uiexSettings.kernelHookLineEdit, 0x817FFFFC, 0x80000000)) + def run(self): + if sys.platform != "win32": + datapath = Path.home() / ".GeckoLoader" + else: + datapath = Path(os.getenv("APPDATA")) / "GeckoLoader" + + if not datapath.is_dir(): + datapath.mkdir() + + self.app = QApplication(sys.argv) + self.default_qtstyle = self.app.style().objectName() + self.ui = MainWindow(self.version) + self.uiprefs = PrefWindow() + self.uiexSettings = SettingsWindow() + + self.uiprefs.qtstyleSelect.addItem("Default") + + styleKeys = list(QStyleFactory.keys()) + self.uiprefs.qtstyleSelect.addItems(styleKeys) + + self.load_prefs() + self.load_qtstyle(self.prefs.get("qtstyle"), True) + + regex = QRegularExpression("[0-9A-Fa-f]*") + validator = QRegularExpressionValidator(regex) + self.ui.allocLineEdit.setValidator(validator) + self.uiexSettings.codehookLineEdit.setValidator(validator) + self.uiexSettings.kernelHookLineEdit.setValidator(validator) + + self.connect_signals() + self.ui.show() + sys.exit(self.app.exec_()) + def _exec_api(self): if sys.platform == "win32": self.ui.responses.appendPlainText( @@ -840,7 +581,7 @@ def _exec_api(self): with redirect_stdout(_outpipe), redirect_stderr(_errpipe): try: - self.cli._exec(args, tmpdir=TMPDIR) + self.cli._exec(args) except (SystemExit, Exception): _status = False else: @@ -854,53 +595,19 @@ def _exec_api(self): _msg += line.lstrip() + "\n" self.ui.responses.appendPlainText(_msg.strip() + "\n") - def run(self): - if sys.platform != "win32": - datapath = Path.home() / ".GeckoLoader" - else: - datapath = Path(os.getenv("APPDATA")) / "GeckoLoader" - - if not datapath.is_dir(): - datapath.mkdir() - - self.app = QtWidgets.QApplication(sys.argv) - self.default_qtstyle = self.app.style().objectName() - self.ui = MainWindow(self.version) - self.uiprefs = PrefWindow() - self.uiexSettings = SettingsWindow() - - self.uiprefs.qtstyleSelect.addItem("Default") - - styleKeys = list(QtWidgets.QStyleFactory.keys()) - self.uiprefs.qtstyleSelect.addItems(styleKeys) - - self.load_prefs() - self.load_qtstyle(self.prefs.get("qtstyle"), True) - - regex = QtCore.QRegExp("[0-9A-Fa-f]*") - validator = QtGui.QRegExpValidator(regex) - self.ui.allocLineEdit.setValidator(validator) - self.uiexSettings.codehookLineEdit.setValidator(validator) - self.uiexSettings.kernelHookLineEdit.setValidator(validator) - - self.connect_signals() - self.ui.show() - sys.exit(self.app.exec_()) + @staticmethod + def _enforce_mask(textbox: QTextEdit, mask: int, _or: int = 0): + textbox.setText(textbox.text().strip()) + if len(textbox.text()) > 0: + _depth = len(hex(mask)[2:]) + _address = int(textbox.text(), 16) << ( + (_depth - len(textbox.text())) << 2) + aligned = hex(((_address & mask) | _or) >> ( + (_depth - len(textbox.text())) << 2))[2:].upper() + textbox.setText(aligned) -if __name__ == "__main__": - cli = GeckoLoaderCli('GeckoLoader', __version__, - description='Dol editing tool for allocating extended codespace') - - if len(sys.argv) == 1: - cli.print_splash() - app = GUI(cli) - signal.signal(signal.SIGINT, signal.SIG_DFL) - app.run() - elif '--checkupdate' in sys.argv: - cli.check_updates() - elif '--splash' in sys.argv: - cli.print_splash() - else: - args = cli.parse_args() - cli._exec(args, TMPDIR) + @staticmethod + def _remove_ansi(msg: str) -> str: + ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])') + return ansi_escape.sub('', msg) diff --git a/kernel.py b/geckoloader/kernel.py similarity index 73% rename from kernel.py rename to geckoloader/kernel.py index 5d622e0..5688988 100644 --- a/kernel.py +++ b/geckoloader/kernel.py @@ -35,9 +35,19 @@ def wrapper(*args, **kwargs): return wrapper -class CodeHandler(object): +def create_branch(to: int, _from: int, lk: bool = False) -> int: + """ Create a branch instruction at `_from`\n + to: address to branch to\n + _from: address to branch from\n + lk: is branch linking? """ + + _from &= 0xFFFFFFFC + to &= 0xFFFFFFFC + return (to - _from) & 0x3FFFFFD | 0x48000000 | (1 if lk else 0) + - class Types: +class CodeHandler(object): + class Types(Enum): MINI = "MINI" FULL = "FULL" @@ -48,13 +58,12 @@ class Types: WiiPADHook = b"\x3A\xB5\x00\x01\x3A\x73\x00\x0C\x2C\x15\x00\x04\x3B\x18\x00\x0C" GCNPADHook = b"\x3A\xB5\x00\x01\x2C\x15\x00\x04\x3B\x18\x00\x0C\x3B\xFF\x00\x0C" - def __init__(self, f): + def __init__(self, f: BinaryIO): self.baseAddress = int.from_bytes(f.read(4), "big", signed=False) - self. self._rawData = BytesIO(f.read()) - self.gct = GeckoCodeTable() + self.gct: GeckoCodeTable = None - """Get codelist pointer""" + # Get codelist pointer self._rawData.seek(0xFA) codelistUpper = self._rawData.read(2).hex() self._rawData.seek(0xFE) @@ -69,91 +78,81 @@ def __init__(self, f): self.includeAll = False self.optimizeList = False - if self.handlerLength < 0x900: - self.type = KernelLoader.HandlerType.MINI - else: - self.type = KernelLoader.HandlerType.FULL + self.type = KernelLoader.HandlerType.MINI if self.handlerLength < 0x900 else KernelLoader.HandlerType.FULL f.seek(0) - def init_gct(self, gctPath: Path, tmpdir: Path = None): - if tmpdir is not None: - _tmpGct = tmpdir / "gct.bin" - else: - _tmpGct = Path("gct.bin") - + def init_gct(self, gctPath: Path): if gctPath.suffix.lower() == ".txt": - with _tmpGct.open("wb+") as temp: - temp.write(bytes.fromhex("00D0C0DE"*2 + - self.parse_input(gctPath) + "F000000000000000")) - temp.seek(0) - self.gct = GCT(temp) + self.gct = GeckoCodeTable.from_text(gctPath.read_text()) elif gctPath.suffix.lower() == ".gct": - with gctPath.open("rb") as gct: - self.gct = GCT(gct) + self.gct = GeckoCodeTable.from_bytes(gctPath.read_bytes()) elif gctPath.suffix == "": - with _tmpGct.open("wb+") as temp: - temp.write(b"\x00\xD0\xC0\xDE"*2) - - for file in gctPath.iterdir(): - if file.is_file(): - if file.suffix.lower() == ".txt": - temp.write(bytes.fromhex(self.parse_input(file))) - elif file.suffix.lower() == ".gct": - with file.open("rb") as gct: - temp.write(gct.read()[8:-8]) - else: - print(tools.color_text( - f" :: HINT: {file} is not a .txt or .gct file", defaultColor=tools.TYELLOWLIT)) - - temp.write(b"\xF0\x00\x00\x00\x00\x00\x00\x00") - temp.seek(0) - self.gct = GCT(temp) + gct = GeckoCodeTable() + for file in gctPath.iterdir(): + if not file.is_file(): + continue + + if file.suffix.lower() == ".txt": + nextGCT = GeckoCodeTable.from_text(gctPath.read_text()) + if gct.gameID == "GECK01": + gct.gameID = nextGCT.gameID + gct.gameName = nextGCT.gameName + gct += nextGCT + elif file.suffix.lower() == ".gct": + nextGCT = GeckoCodeTable.from_bytes(gctPath.read_bytes()) + gct += nextGCT + else: + print(tools.color_text( + f" :: HINT: {file} is not a .txt or .gct file", defaultColor=tools.TYELLOWLIT)) + self.gct = gct else: raise NotImplementedError( f"Parsing file type `{gctPath.suffix}' as a GCT is unsupported") - def parse_input(self, geckoText: Path) -> str: - with geckoText.open("rb") as gecko: - result = chardet.detect(gecko.read()) - encodeType = result["encoding"] + def set_variables(self, dol: DolFile): + varOffset = self.__find_variable_data(b"\x00\xDE\xDE\xDE") + if varOffset is None: + raise RuntimeError(tools.color_text( + "Variable codehandler data not found\n", defaultColor=tools.TREDLIT)) + + self.__set_hook_instruction(dol, self.hookAddress, varOffset, 0) - with geckoText.open("r", encoding=encodeType) as gecko: - gct = "" - state = None + self._rawData.seek(varOffset + 4) + write_uint32(self._rawData, create_branch(self.hookAddress + 4, + self.baseAddress + (varOffset + 4), False)) - for line in gecko.readlines(): - if line in ("", "\n"): - continue + def __find_variable_data(self, variable) -> int: + self._rawData.seek(0) - if state is None: - if line.startswith("$") or line.startswith("["): - state = "Dolphin" - else: - state = "OcarinaM" - - try: - if state == "OcarinaM": - if self.includeAll: - geckoLine = re.findall( - r"[A-F0-9]{8}[\t\f ][A-F0-9]{8}", line, re.IGNORECASE)[0] - else: - geckoLine = re.findall( - r"(?:\*\s*)([A-F0-9]{8}[\t\f ][A-F0-9]{8})", line, re.IGNORECASE)[0] - else: - geckoLine = re.findall( - r"(?> 24) & 0xFF) > 0x47 and ((ppc >> 24) & 0xFF) < 0x4C) or (((ppc >> 24) & 0xFF) > 0x3F and ((ppc >> 24) & 0xFF) < 0x44)): + to, conditional = dol.extract_branch_addr(hookAddress) + if conditional: + raise NotImplementedError( + "Hooking to a conditional non spr branch is unsupported") + dol.insert_branch + write_uint32( + self._rawData, + create_branch(to, self.baseAddress + returnOffset, lk) + ) + else: + write_uint32(self._rawData, ppc) class KernelLoader(object): class DataCryptor(object): - @staticmethod + @ staticmethod def encrypt_key(key: int) -> int: b1 = key & 0xFF b2 = (key >> 8) & 0xFF @@ -164,7 +163,7 @@ def encrypt_key(key: int) -> int: b1 ^= b2 return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4 - @staticmethod + @ staticmethod def decrypt_key(key: int) -> int: b1 = (key >> 24) & 0xFF b2 = (key >> 16) & 0xFF @@ -175,41 +174,25 @@ def decrypt_key(key: int) -> int: b3 ^= b4 return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1 - @staticmethod - def encrypt_data(data: bytes, key: int) -> bytes: - stream = BytesIO(data) - i = 0 - try: - while packet := read_uint32(stream): - packet = read_uint32(stream) - stream.seek(-4, 1) - write_uint32(stream, (packet ^ key) & 0xFFFFFFFF) - key += (i << 3) & 0xFFFFFFFF - if key > 0xFFFFFFFF: - key -= 0x100000000 - i += 1 - except Exception: - pass - return stream.getvalue() - def __init__(self, key: int): self.key = key - @property + @ property def encryptedKey(self) -> int: return self.encrypt_key(self.key) - def encrypt_data(self, data: bytes) -> bytes: + def xorcrypt_data(self, data: bytes) -> bytes: stream = BytesIO(data) + streamLength = len(stream.getbuffer()) i = 0 try: - while packet := read_uint32(stream): + while (stream.tell() < streamLength): packet = read_uint32(stream) stream.seek(-4, 1) - write_uint32(stream, (packet ^ key) & 0xFFFFFFFF) - key += (i << 3) & 0xFFFFFFFF - if key > 0xFFFFFFFF: - key -= 0x100000000 + write_uint32(stream, (packet ^ self.key) & 0xFFFFFFFF) + self.key += (i << 3) & 0xFFFFFFFF + if self.key > 0xFFFFFFFF: + self.key -= 0x100000000 i += 1 except Exception: pass @@ -247,99 +230,6 @@ class HandlerType(Enum): " ) - @staticmethod - def encrypt_key(key: int) -> int: - b1 = key & 0xFF - b2 = (key >> 8) & 0xFF - b3 = (key >> 16) & 0xFF - b4 = (key >> 24) & 0xFF - b3 ^= b4 - b2 ^= b3 - b1 ^= b2 - return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4 - - @staticmethod - def decrypt_key(key: int) -> int: - b1 = (key >> 24) & 0xFF - b2 = (key >> 16) & 0xFF - b3 = (key >> 8) & 0xFF - b4 = key & 0xFF - b1 ^= b2 - b2 ^= b3 - b3 ^= b4 - return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1 - - @staticmethod - def encrypt_gct(gct: GeckoCodeTable, key: int) -> bytes: - with BytesIO(gct.as_bytes()) as data: - i = 0 - try: - while packet := read_uint32(data): - packet = read_uint32(data) - data.seek(-4, 1) - write_uint32(data, (packet ^ key) & 0xFFFFFFFF) - key += (i << 3) & 0xFFFFFFFF - if key > 0xFFFFFFFF: - key -= 0x100000000 - i += 1 - except Exception: - pass - - @staticmethod - def decrypt_gct(gct: GeckoCodeTable, key: int) -> bytes: - with BytesIO(gct.as_bytes()) as data: - i = 0 - try: - while packet := read_uint32(data): - packet = read_uint32(data) - data.seek(-4, 1) - write_uint32(data, (packet ^ key) & 0xFFFFFFFF) - key += (i << 3) & 0xFFFFFFFF - if key > 0xFFFFFFFF: - key -= 0x100000000 - i += 1 - except Exception: - pass - - @staticmethod - def find_variable_data(self, variable) -> int: - self._rawData.seek(0) - - while sample := self._rawData.read(4): - if sample == variable: - return self._rawData.tell() - 4 - - return None - - @staticmethod - def set_hook_instruction(self, dol: DolFile, address: int, varOffset: int, lk=0): - self._rawData.seek(varOffset) - dol.seek(address) - ppc = read_uint32(dol) - - if ((((ppc >> 24) & 0xFF) > 0x47 and ((ppc >> 24) & 0xFF) < 0x4C) or (((ppc >> 24) & 0xFF) > 0x3F and ((ppc >> 24) & 0xFF) < 0x44)): - to, conditional = dol.extract_branch_addr(address) - if conditional: - raise NotImplementedError( - "Hooking to a conditional non spr branch is unsupported") - write_uint32(self._rawData, (to - (self.initAddress + - varOffset)) & 0x3FFFFFD | 0x48000000 | lk) - else: - write_uint32(self._rawData, ppc) - - @staticmethod - def set_variables(self, dol: DolFile): - varOffset = self.find_variable_data(b"\x00\xDE\xDE\xDE") - if varOffset is None: - raise RuntimeError(tools.color_text( - "Variable codehandler data not found\n", defaultColor=tools.TREDLIT)) - - self.set_hook_instruction(dol, self.hookAddress, varOffset, 0) - - self._rawData.seek(varOffset + 4) - write_uint32(self._rawData, ((self.hookAddress + 4) - - (self.initAddress + (varOffset + 4))) & 0x3FFFFFD | 0x48000000 | 0) - def __init__(self, f: BinaryIO, hookType: CodeHandler.Hook, hookAddress: int, initAddress: int, allocation: Optional[int] = None, includeAllCodes: bool = False, optimizeCodes: bool = False, protectGame: bool = False, encryptCodes: bool = False, cli: Optional[tools.CommandLineParser] = None): @@ -362,11 +252,11 @@ def __init__(self, f: BinaryIO, hookType: CodeHandler.Hook, hookAddress: int, in self._verbosity = 0 self._quiet = False - @property + @ property def verbosity(self) -> int: return self._verbosity - @verbosity.setter + @ verbosity.setter def verbosity(self, level: int): self._verbosity = max(min(level, 0), 3) @@ -404,7 +294,7 @@ def do_data_relocs(self, entryAddress: int, modAddress: int, encryptKeyAddress: self.apply_reloc(b"GH", ((encryptKeyAddress >> 16) & 0xFFFF)) self.apply_reloc(b"GL", (encryptKeyAddress & 0xFFFF) + baseOffset) - def complete_data(self, codeHandler: CodeHandler, hookAddress: int, initAddress: int): + def complete_data(self, codeHandler: CodeHandler, initAddress: int): _upperAddr, _lowerAddr = ( (self.initAddress >> 16) & 0xFFFF, self.initAddress & 0xFFFF) _key = random.randrange(0x100000000) @@ -416,7 +306,7 @@ def complete_data(self, codeHandler: CodeHandler, hookAddress: int, initAddress: self.apply_reloc(b"CSIZ", len(codeHandler.gct)) self.apply_reloc(b"HOOK", self.hookAddress) self.apply_reloc(b"CRPT", int(self.encrypt)) - self.apply_reloc(b"CYPT", KernelLoader.encrypt_key(_key)) + self.apply_reloc(b"CYPT", KernelLoader.DataCryptor.encrypt_key(_key)) gpModInfoOffset = (self._rawData.getvalue().find(b"HEAP") << 16) gpModUpperAddr = _upperAddr + \ @@ -481,7 +371,7 @@ def patch_legacy(self, codeHandler: CodeHandler, dol: DolFile) -> tuple: def protect_game(self, codeHandler: CodeHandler): codeHandler.gct.add_child(KernelLoader.GeckoProtector) - @timer + @ timer def build(self, gctPath: Path, dol: DolFile, codeHandler: CodeHandler, tmpdir: Path, dump: Path): _oldStart = dol.entryPoint diff --git a/geckoloader/main_ui.py b/geckoloader/main_ui.py new file mode 100644 index 0000000..3da07e5 --- /dev/null +++ b/geckoloader/main_ui.py @@ -0,0 +1,847 @@ +from PySide6.QtGui import QIcon, QPalette, QFont, QCloseEvent, QPixmap, QColor, QFontMetricsF, QAction +from PySide6.QtCore import Qt, QSize, QRect, QMetaObject +from PySide6.QtWidgets import (QApplication, QComboBox, QWidget, + QGridLayout, QLabel, QMessageBox, QPushButton, QFrame, QPlainTextEdit, QMenuBar, QMenu, QStatusBar, QMainWindow, + QLineEdit, QSizePolicy) + +from geckoloader.fileutils import resource_path + + +class MainWindow(QMainWindow): + def __init__(self, version: str): + super().__init__() + + self._job_active = False + self.apiRevision = version + self.setup_ui() + + self.LightTheme = self.palette() + + self.DarkTheme = QPalette() + self.DarkTheme.setColor(QPalette.Window, QColor(53, 53, 53)) + self.DarkTheme.setColor(QPalette.WindowText, Qt.white) + self.DarkTheme.setColor(QPalette.Base, QColor(25, 25, 25)) + self.DarkTheme.setColor(QPalette.AlternateBase, QColor(53, 53, 53)) + self.DarkTheme.setColor(QPalette.ToolTipBase, Qt.black) + self.DarkTheme.setColor(QPalette.ToolTipText, Qt.white) + self.DarkTheme.setColor(QPalette.Text, Qt.white) + self.DarkTheme.setColor(QPalette.Button, QColor(53, 53, 53)) + self.DarkTheme.setColor(QPalette.ButtonText, Qt.white) + self.DarkTheme.setColor(QPalette.BrightText, Qt.red) + self.DarkTheme.setColor(QPalette.Link, QColor(42, 130, 218)) + self.DarkTheme.setColor(QPalette.Highlight, QColor(42, 130, 218)) + self.DarkTheme.setColor(QPalette.HighlightedText, Qt.black) + + def set_job_activity(self, active: bool): + self._job_active = active + + def close_event(self, event: QCloseEvent): + if self._job_active: + reply = QMessageBox(self) + reply.setWindowTitle("Active job") + reply.setText("GeckoLoader is busy!") + reply.setInformativeText("Exiting is disabled") + reply.setIcon(QMessageBox.Warning) + reply.setStandardButtons(QMessageBox.Ok) + reply.setDefaultButton(QMessageBox.Ok) + reply.exec_() + event.ignore() + else: + event.accept() + + def setup_ui(self): + self.setObjectName("MainWindow") + self.setWindowModality(Qt.NonModal) + self.setEnabled(True) + self.setFixedSize(550, 680) + font = QFont() + font.setFamily("Helvetica") + font.setPointSize(10) + font.setWeight(42) + self.setFont(font) + icon = QIcon() + icon.addPixmap(QPixmap(str(resource_path("bin/icon.ico"))), + QIcon.Normal, QIcon.Off) + self.setWindowIcon(icon) + + # Top level widget + self.centerWidget = QWidget(self) + self.centerWidget.setObjectName("centerWidget") + + self.gridLayout = QGridLayout(self.centerWidget) + self.gridLayout.setVerticalSpacing(0) + self.gridLayout.setObjectName("gridLayout") + + # Layout for file paths and open boxes + self.filesLayout = QGridLayout() + self.filesLayout.setHorizontalSpacing(0) + self.filesLayout.setObjectName("filesLayout") + + self.dolLayout = QGridLayout() + self.dolLayout.setHorizontalSpacing(0) + self.dolLayout.setObjectName("dolLayout") + + # Layout for folder path + self.gctLayout = QGridLayout() + self.gctLayout.setHorizontalSpacing(0) + self.gctLayout.setVerticalSpacing(5) + self.gctLayout.setObjectName("gctLayout") + + self.destLayout = QGridLayout() + self.dolLayout.setHorizontalSpacing(0) + self.dolLayout.setObjectName("dolLayout") + + # Files label + self.filesLabel = QLabel(self.centerWidget) + self.filesLabel.setEnabled(False) + sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.filesLabel.sizePolicy().hasHeightForWidth()) + self.filesLabel.setSizePolicy(sizePolicy) + self.filesLabel.setMinimumSize(QSize(80, 30)) + self.filesLabel.setMaximumSize(QSize(16777215, 30)) + font = QFont("Helvetica") + font.setPointSize(21) + font.setWeight(82) + font.setBold(True) + self.filesLabel.setFont(font) + self.filesLabel.setTextFormat(Qt.PlainText) + self.filesLabel.setAlignment(Qt.AlignCenter | Qt.AlignVCenter) + self.filesLabel.setObjectName("filesLabel") + + # Dol button to open file + self.dolButton = QPushButton(self.centerWidget) + self.dolButton.setMinimumSize(QSize(100, 26)) + self.dolButton.setMaximumSize(QSize(100, 26)) + font = QFont("Helvetica") + font.setPointSize(11) + self.dolButton.setFont(font) + self.dolButton.setCheckable(False) + self.dolButton.setChecked(False) + self.dolButton.setAutoDefault(True) + self.dolButton.setDefault(False) + self.dolButton.setFlat(False) + self.dolButton.setObjectName("dolButton") + self.dolLayout.addWidget(self.dolButton, 1, 0, 1, 1) + + # Dol path textbox + self.dolTextBox = QLineEdit(self.centerWidget) + self.dolTextBox.setEnabled(False) + self.dolTextBox.setMinimumSize(QSize(200, 24)) + self.dolTextBox.setMaximumSize(QSize(16777215, 24)) + font = QFont() + font.setFamily("Consolas") + font.setPointSize(10) + font.setWeight(42) + self.dolTextBox.setFont(font) + self.dolTextBox.setText("") + self.dolTextBox.setMaxLength(255) + self.dolTextBox.setFrame(True) + self.dolTextBox.setAlignment( + Qt.AlignLeading | Qt.AlignCenter | Qt.AlignVCenter) + self.dolTextBox.setObjectName("dolTextBox") + self.dolLayout.addWidget(self.dolTextBox, 1, 1, 1, 1) + + # horizontal separater codes + self.horiSepFiles = QFrame(self.centerWidget) + self.horiSepFiles.setMinimumSize(QSize(474, 30)) + self.horiSepFiles.setContentsMargins(20, 0, 20, 0) + self.horiSepFiles.setFrameShape(QFrame.HLine) + self.horiSepFiles.setFrameShadow(QFrame.Sunken) + self.horiSepFiles.setObjectName("horiSepFiles") + + # gctFile button to open file + self.gctFileButton = QPushButton(self.centerWidget) + self.gctFileButton.setMinimumSize(QSize(100, 26)) + self.gctFileButton.setMaximumSize(QSize(100, 26)) + font = QFont("Helvetica") + font.setPointSize(10) + self.gctFileButton.setFont(font) + self.gctFileButton.setCheckable(False) + self.gctFileButton.setChecked(False) + self.gctFileButton.setAutoDefault(True) + self.gctFileButton.setDefault(False) + self.gctFileButton.setFlat(False) + self.gctFileButton.setObjectName("gctFileButton") + self.gctLayout.addWidget(self.gctFileButton, 0, 0, 1, 1) + + # gctFile path textbox + self.gctFileTextBox = QLineEdit(self.centerWidget) + self.gctFileTextBox.setEnabled(False) + self.gctFileTextBox.setMinimumSize(QSize(200, 24)) + self.gctFileTextBox.setMaximumSize(QSize(16777215, 24)) + font = QFont() + font.setFamily("Consolas") + font.setPointSize(10) + font.setWeight(42) + self.gctFileTextBox.setFont(font) + self.gctFileTextBox.setText("") + self.gctFileTextBox.setMaxLength(255) + self.gctFileTextBox.setFrame(True) + self.gctFileTextBox.setAlignment( + Qt.AlignLeading | Qt.AlignCenter | Qt.AlignVCenter) + self.gctFileTextBox.setObjectName("gctFileTextBox") + self.gctLayout.addWidget(self.gctFileTextBox, 0, 1, 1, 1) + + # --or-- Label + self.orFolderLabel = QLabel(self.centerWidget) + self.orFolderLabel.setEnabled(False) + self.orFolderLabel.setMinimumSize(QSize(80, 8)) + self.orFolderLabel.setMaximumSize(QSize(16777215, 8)) + font = QFont("Helvetica") + font.setPointSize(8) + font.setWeight(82) + font.setBold(True) + self.orFolderLabel.setFont(font) + self.orFolderLabel.setTextFormat(Qt.PlainText) + self.orFolderLabel.setAlignment(Qt.AlignCenter | Qt.AlignVCenter) + self.orFolderLabel.setObjectName("orFolderLabel") + self.gctLayout.addWidget(self.orFolderLabel, 1, 0, 1, 2) + + # gctFolder button to open file + self.gctFolderButton = QPushButton(self.centerWidget) + self.gctFolderButton.setMinimumSize(QSize(100, 26)) + self.gctFolderButton.setMaximumSize(QSize(100, 26)) + font = QFont("Helvetica") + font.setPointSize(10) + self.gctFolderButton.setFont(font) + self.gctFolderButton.setCheckable(False) + self.gctFolderButton.setChecked(False) + self.gctFolderButton.setAutoDefault(True) + self.gctFolderButton.setDefault(False) + self.gctFolderButton.setFlat(False) + self.gctFolderButton.setObjectName("gctFolderButton") + self.gctLayout.addWidget(self.gctFolderButton, 2, 0, 1, 1) + + # gctFolder path textbox + self.gctFolderTextBox = QLineEdit(self.centerWidget) + self.gctFolderTextBox.setEnabled(False) + self.gctFolderTextBox.setMinimumSize(QSize(200, 24)) + self.gctFolderTextBox.setMaximumSize(QSize(16777215, 24)) + font = QFont() + font.setFamily("Consolas") + font.setPointSize(10) + font.setWeight(42) + self.gctFolderTextBox.setFont(font) + self.gctFolderTextBox.setText("") + self.gctFolderTextBox.setMaxLength(255) + self.gctFolderTextBox.setFrame(True) + self.gctFolderTextBox.setAlignment( + Qt.AlignLeading | Qt.AlignCenter | Qt.AlignVCenter) + self.gctFolderTextBox.setObjectName("gctFolderTextBox") + self.gctLayout.addWidget(self.gctFolderTextBox, 2, 1, 1, 1) + + # horizontal separater dest + self.horiSepDest = QFrame(self.centerWidget) + self.horiSepDest.setMinimumSize(QSize(474, 30)) + self.horiSepDest.setContentsMargins(20, 0, 20, 0) + self.horiSepDest.setFrameShape(QFrame.HLine) + self.horiSepDest.setFrameShadow(QFrame.Sunken) + self.horiSepDest.setObjectName("horiSepDest") + + # Dest button to open file + self.destButton = QPushButton(self.centerWidget) + self.destButton.setMinimumSize(QSize(100, 26)) + self.destButton.setMaximumSize(QSize(100, 26)) + font = QFont("Helvetica") + font.setPointSize(11) + self.destButton.setFont(font) + self.destButton.setCheckable(False) + self.destButton.setChecked(False) + self.destButton.setAutoDefault(True) + self.destButton.setDefault(False) + self.destButton.setFlat(False) + self.destButton.setObjectName("destButton") + self.destLayout.addWidget(self.destButton, 0, 0, 1, 1) + + # Dest path textbox + self.destTextBox = QLineEdit(self.centerWidget) + self.destTextBox.setEnabled(False) + self.destTextBox.setMinimumSize(QSize(200, 24)) + self.destTextBox.setMaximumSize(QSize(16777215, 24)) + font = QFont() + font.setFamily("Consolas") + font.setPointSize(10) + font.setWeight(42) + self.destTextBox.setFont(font) + self.destTextBox.setText("") + self.destTextBox.setMaxLength(255) + self.destTextBox.setFrame(True) + self.destTextBox.setAlignment( + Qt.AlignLeading | Qt.AlignCenter | Qt.AlignVCenter) + self.destTextBox.setObjectName("destTextBox") + self.destLayout.addWidget(self.destTextBox, 0, 1, 1, 1) + + self.filesLayout.addLayout(self.dolLayout, 0, 0, 1, 1) + self.filesLayout.addWidget(self.horiSepFiles, 1, 0, 1, 1) + self.filesLayout.addLayout(self.gctLayout, 2, 0, 1, 1) + self.filesLayout.addWidget(self.horiSepDest, 3, 0, 1, 1) + self.filesLayout.addLayout(self.destLayout, 4, 0, 1, 1) + + # Options Layout + self.optionsLayout = QGridLayout() + self.optionsLayout.setHorizontalSpacing(20) + self.optionsLayout.setObjectName("optionsLayout") + + # Options Label + self.optionsLabel = QLabel(self.centerWidget) + self.optionsLabel.setEnabled(False) + sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.optionsLabel.sizePolicy().hasHeightForWidth()) + self.optionsLabel.setSizePolicy(sizePolicy) + self.optionsLabel.setMinimumSize(QSize(79, 23)) + self.optionsLabel.setMaximumSize(QSize(16777215, 23)) + font = QFont("Helvetica") + font.setPointSize(18) + font.setWeight(82) + font.setBold(True) + self.optionsLabel.setFont(font) + self.optionsLabel.setTextFormat(Qt.PlainText) + self.optionsLabel.setAlignment(Qt.AlignCenter | Qt.AlignVCenter) + self.optionsLabel.setObjectName("optionsLabel") + self.optionsLayout.addWidget(self.optionsLabel, 0, 0, 1, 4) + + # Allocation Label + self.allocLabel = QLabel(self.centerWidget) + self.allocLabel.setEnabled(False) + sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.allocLabel.sizePolicy().hasHeightForWidth()) + self.allocLabel.setSizePolicy(sizePolicy) + self.allocLabel.setMinimumSize(QSize(79, 23)) + self.allocLabel.setMaximumSize(QSize(16777215, 23)) + self.allocLabel.setTextFormat(Qt.PlainText) + self.allocLabel.setAlignment(Qt.AlignCenter | Qt.AlignVCenter) + self.allocLabel.setObjectName("allocLabel") + self.optionsLayout.addWidget(self.allocLabel, 1, 0, 1, 1) + + # Allocation Textbox + self.allocLineEdit = QLineEdit(self.centerWidget) + self.allocLineEdit.setEnabled(False) + sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.allocLineEdit.sizePolicy().hasHeightForWidth()) + self.allocLineEdit.setSizePolicy(sizePolicy) + self.allocLineEdit.setMinimumSize(QSize(79, 23)) + self.allocLineEdit.setMaximumSize(QSize(79, 23)) + font = QFont() + font.setFamily("Consolas") + font.setPointSize(12) + font.setWeight(42) + self.allocLineEdit.setFont(font) + self.allocLineEdit.setText("") + self.allocLineEdit.setMaxLength(6) + self.allocLineEdit.setAlignment(Qt.AlignCenter | Qt.AlignVCenter) + self.allocLineEdit.setObjectName("allocLineEdit") + self.optionsLayout.addWidget(self.allocLineEdit, 2, 0, 1, 1) + + # handlerType label + self.handlerTypeLabel = QLabel(self.centerWidget) + self.handlerTypeLabel.setEnabled(False) + sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.handlerTypeLabel.sizePolicy().hasHeightForWidth()) + self.handlerTypeLabel.setSizePolicy(sizePolicy) + self.handlerTypeLabel.setMinimumSize(QSize(79, 23)) + self.handlerTypeLabel.setMaximumSize(QSize(16777215, 23)) + self.handlerTypeLabel.setTextFormat(Qt.PlainText) + self.handlerTypeLabel.setAlignment(Qt.AlignCenter | Qt.AlignVCenter) + self.handlerTypeLabel.setObjectName("handlerTypeLabel") + self.optionsLayout.addWidget(self.handlerTypeLabel, 1, 1, 1, 1) + + # handlerType selection + self.handlerTypeSelect = QComboBox(self.centerWidget) + sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.allocLabel.sizePolicy().hasHeightForWidth()) + self.handlerTypeSelect.setSizePolicy(sizePolicy) + self.handlerTypeSelect.setMinimumSize(QSize(79, 23)) + self.handlerTypeSelect.setMaximumSize(QSize(79, 23)) + self.handlerTypeSelect.setObjectName("handlerTypeSelect") + self.handlerTypeSelect.addItems(["FULL", "MINI"]) + self.optionsLayout.addWidget(self.handlerTypeSelect, 2, 1, 1, 1) + + # hookType label + self.hookTypeLabel = QLabel(self.centerWidget) + self.hookTypeLabel.setEnabled(False) + sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.hookTypeLabel.sizePolicy().hasHeightForWidth()) + self.hookTypeLabel.setSizePolicy(sizePolicy) + self.hookTypeLabel.setMinimumSize(QSize(79, 23)) + self.hookTypeLabel.setMaximumSize(QSize(16777215, 23)) + self.hookTypeLabel.setTextFormat(Qt.PlainText) + self.hookTypeLabel.setAlignment(Qt.AlignCenter | Qt.AlignVCenter) + self.hookTypeLabel.setObjectName("hookTypeLabel") + self.optionsLayout.addWidget(self.hookTypeLabel, 1, 2, 1, 1) + + # hookType selection + self.hookTypeSelect = QComboBox(self.centerWidget) + sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.allocLabel.sizePolicy().hasHeightForWidth()) + self.hookTypeSelect.setSizePolicy(sizePolicy) + self.hookTypeSelect.setMinimumSize(QSize(79, 23)) + self.hookTypeSelect.setMaximumSize(QSize(79, 23)) + self.hookTypeSelect.setObjectName("hookTypeSelect") + self.hookTypeSelect.addItems(["VI", "GX", "PAD"]) + self.optionsLayout.addWidget(self.hookTypeSelect, 2, 2, 1, 1) + + # txtCodesInclude label + self.txtCodesIncludeLabel = QLabel(self.centerWidget) + self.txtCodesIncludeLabel.setEnabled(False) + sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.txtCodesIncludeLabel.sizePolicy().hasHeightForWidth()) + self.txtCodesIncludeLabel.setSizePolicy(sizePolicy) + self.txtCodesIncludeLabel.setMinimumSize(QSize(79, 23)) + self.txtCodesIncludeLabel.setMaximumSize(QSize(16777215, 23)) + self.txtCodesIncludeLabel.setTextFormat(Qt.PlainText) + self.txtCodesIncludeLabel.setAlignment( + Qt.AlignCenter | Qt.AlignVCenter) + self.txtCodesIncludeLabel.setObjectName("txtCodesIncludeLabel") + self.optionsLayout.addWidget(self.txtCodesIncludeLabel, 1, 3, 1, 1) + + # txtCodesInclude selection + self.txtCodesIncludeSelect = QComboBox(self.centerWidget) + sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.allocLabel.sizePolicy().hasHeightForWidth()) + self.txtCodesIncludeSelect.setSizePolicy(sizePolicy) + self.txtCodesIncludeSelect.setMinimumSize(QSize(79, 23)) + self.txtCodesIncludeSelect.setMaximumSize(QSize(79, 23)) + self.txtCodesIncludeSelect.setObjectName("txtCodesIncludeSelect") + self.txtCodesIncludeSelect.addItems(["ACTIVE", "ALL"]) + self.optionsLayout.addWidget(self.txtCodesIncludeSelect, 2, 3, 1, 1) + + # horizontal separater options + self.horiSepOptions = QFrame(self.centerWidget) + self.horiSepOptions.setMinimumSize(QSize(300, 30)) + self.horiSepOptions.setContentsMargins(20, 0, 20, 0) + self.horiSepOptions.setFrameShape(QFrame.HLine) + self.horiSepOptions.setFrameShadow(QFrame.Sunken) + self.horiSepOptions.setObjectName("horiSepOptions") + self.optionsLayout.addWidget(self.horiSepOptions, 3, 0, 1, 4) + + # Advanced options button + self.exOptionsButton = QPushButton(self.centerWidget) + font = QFont("Helvetica") + font.setPointSize(13) + self.exOptionsButton.setFont(font) + self.exOptionsButton.setCheckable(False) + self.exOptionsButton.setChecked(False) + self.exOptionsButton.setAutoDefault(True) + self.exOptionsButton.setDefault(False) + self.exOptionsButton.setFlat(False) + self.exOptionsButton.setDisabled(True) + self.exOptionsButton.setObjectName("exOptionsButton") + self.optionsLayout.addWidget(self.exOptionsButton, 4, 0, 1, 4) + + # horizontal separater 1 + self.horiSepA = QFrame(self.centerWidget) + self.horiSepA.setMinimumSize(QSize(470, 30)) + self.horiSepA.setFrameShape(QFrame.HLine) + self.horiSepA.setFrameShadow(QFrame.Sunken) + self.horiSepA.setObjectName("horiSepA") + + # horizontal separater 2 + self.horiSepB = QFrame(self.centerWidget) + self.horiSepB.setMinimumSize(QSize(470, 30)) + self.horiSepB.setFrameShape(QFrame.HLine) + self.horiSepB.setFrameShadow(QFrame.Sunken) + self.horiSepB.setObjectName("horiSepB") + + # response panel + self.responses = QPlainTextEdit(self.centerWidget) + self.responses.setEnabled(True) + sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.responses.sizePolicy().hasHeightForWidth()) + self.responses.setSizePolicy(sizePolicy) + self.responses.setMinimumSize(QSize(474, 180)) + self.responses.setMaximumSize(QSize(16777215, 180)) + font = QFont() + font.setFamily("Consolas") + font.setPointSize(8) + font.setWeight(42) + fontMetrics = QFontMetricsF(font) + spaceWidth = fontMetrics.width(' ') + self.responses.setFont(font) + self.responses.setPlainText("") + self.responses.setTabStopDistance(spaceWidth * 4) + self.responses.setReadOnly(True) + self.responses.setObjectName("responses") + + # Compile button + self.compileButton = QPushButton(self.centerWidget) + font = QFont("Helvetica") + font.setPointSize(34) + self.compileButton.setFont(font) + self.compileButton.setCheckable(False) + self.compileButton.setChecked(False) + self.compileButton.setAutoDefault(True) + self.compileButton.setDefault(False) + self.compileButton.setFlat(False) + self.compileButton.setDisabled(True) + self.compileButton.setObjectName("compileButton") + + self.gridLayout.addWidget(self.filesLabel, 0, 0, 1, 1) + self.gridLayout.addLayout(self.filesLayout, 1, 0, 1, 1) + self.gridLayout.addWidget(self.horiSepA, 2, 0, 1, 1) + self.gridLayout.addLayout(self.optionsLayout, 3, 0, 1, 1) + self.gridLayout.addWidget(self.horiSepB, 4, 0, 1, 1) + self.gridLayout.addWidget(self.responses, 5, 0, 1, 1) + self.gridLayout.addWidget(self.compileButton, 6, 0, 1, 1) + + self.setCentralWidget(self.centerWidget) + + # Toolbar + self.menubar = QMenuBar(self) + self.menubar.setGeometry(QRect(0, 0, 470, 22)) + self.menubar.setObjectName("menubar") + + self.menuFile = QMenu(self.menubar) + font = QFont() + font.setFamily("Helvetica") + self.menuFile.setFont(font) + self.menuFile.setObjectName("menuFile") + + self.menuEdit = QMenu(self.menubar) + font = QFont() + font.setFamily("Helvetica") + self.menuEdit.setFont(font) + self.menuEdit.setObjectName("menuEdit") + + self.menuHelp = QMenu(self.menubar) + font = QFont() + font.setFamily("Helvetica") + self.menuHelp.setFont(font) + self.menuHelp.setObjectName("menuHelp") + + self.setMenuBar(self.menubar) + + self.actionOpen = QAction(self) + font = QFont() + font.setFamily("Helvetica") + self.actionOpen.setFont(font) + self.actionOpen.setObjectName("actionOpen") + + self.actionClose = QAction(self) + font = QFont() + font.setFamily("Helvetica") + self.actionClose.setFont(font) + self.actionClose.setObjectName("actionClose") + + self.actionSave = QAction(self) + self.actionSave.setEnabled(False) + font = QFont() + font.setFamily("Helvetica") + self.actionSave.setFont(font) + self.actionSave.setObjectName("actionSave") + + self.actionSave_As = QAction(self) + self.actionSave_As.setEnabled(False) + font = QFont() + font.setFamily("Helvetica") + self.actionSave_As.setFont(font) + self.actionSave_As.setObjectName("actionSave_As") + + self.actionUndo = QAction(self) + font = QFont() + font.setFamily("Helvetica") + self.actionUndo.setFont(font) + self.actionUndo.setMenuRole(QAction.TextHeuristicRole) + self.actionUndo.setObjectName("actionUndo") + self.actionRedo = QAction(self) + font = QFont() + font.setFamily("Helvetica") + self.actionRedo.setFont(font) + self.actionRedo.setObjectName("actionRedo") + self.actionCut = QAction(self) + font = QFont() + font.setFamily("Helvetica") + self.actionCut.setFont(font) + self.actionCut.setObjectName("actionCut") + self.actionCopy = QAction(self) + font = QFont() + font.setFamily("Helvetica") + self.actionCopy.setFont(font) + self.actionCopy.setObjectName("actionCopy") + self.actionPaste = QAction(self) + font = QFont() + font.setFamily("Helvetica") + self.actionPaste.setFont(font) + self.actionPaste.setObjectName("actionPaste") + self.actionDelete = QAction(self) + font = QFont() + font.setFamily("Helvetica") + self.actionDelete.setFont(font) + self.actionDelete.setObjectName("actionDelete") + self.actionSelect_All = QAction(self) + font = QFont() + font.setFamily("Helvetica") + self.actionSelect_All.setFont(font) + self.actionSelect_All.setObjectName("actionSelect_All") + self.actionPreferences = QAction(self) + font = QFont() + font.setFamily("Helvetica") + self.actionPreferences.setFont(font) + self.actionPreferences.setMenuRole(QAction.PreferencesRole) + self.actionPreferences.setObjectName("actionPreferences") + + self.actionAbout_GeckoLoader = QAction(self) + font = QFont() + font.setFamily("Helvetica") + self.actionAbout_GeckoLoader.setFont(font) + self.actionAbout_GeckoLoader.setMenuRole(QAction.AboutRole) + self.actionAbout_GeckoLoader.setObjectName("actionAbout_GeckoLoader") + + self.actionAbout_Qt = QAction(self) + self.actionAbout_Qt.setStatusTip("") + font = QFont() + font.setFamily("Helvetica") + self.actionAbout_Qt.setFont(font) + self.actionAbout_Qt.setMenuRole(QAction.AboutQtRole) + self.actionAbout_Qt.setObjectName("actionAbout_Qt") + + self.actionCheck_Update = QAction(self) + self.actionCheck_Update.setStatusTip("") + font = QFont() + font.setFamily("Helvetica") + self.actionCheck_Update.setFont(font) + self.actionCheck_Update.setObjectName("actionCheck_Update") + + self.menuFile.addAction(self.actionOpen) + self.menuFile.addAction(self.actionClose) + self.menuFile.addSeparator() + self.menuFile.addAction(self.actionSave) + self.menuFile.addAction(self.actionSave_As) + + self.menuEdit.addAction(self.actionPreferences) + + self.menuHelp.addAction(self.actionAbout_GeckoLoader) + self.menuHelp.addAction(self.actionAbout_Qt) + self.menuHelp.addAction(self.actionCheck_Update) + + self.menubar.addAction(self.menuFile.menuAction()) + self.menubar.addAction(self.menuEdit.menuAction()) + self.menubar.addAction(self.menuHelp.menuAction()) + + # Statusbar + self.statusbar = QStatusBar(self) + self.statusbar.setObjectName("statusbar") + self.setStatusBar(self.statusbar) + + self.retranslate_ui() + self.set_edit_fields() + + QMetaObject.connectSlotsByName(self) + + def set_edit_fields(self): + self.filesLabel.setEnabled(True) + self.dolTextBox.setEnabled(True) + self.destTextBox.setEnabled(True) + self.optionsLabel.setEnabled(True) + self.allocLabel.setEnabled(True) + self.allocLineEdit.setEnabled(True) + self.handlerTypeLabel.setEnabled(True) + self.handlerTypeSelect.setEnabled(True) + self.hookTypeLabel.setEnabled(True) + self.hookTypeSelect.setEnabled(True) + self.txtCodesIncludeLabel.setEnabled(True) + self.txtCodesIncludeSelect.setEnabled(True) + self.exOptionsButton.setEnabled(True) + self.actionSave.setEnabled(True) + self.actionSave_As.setEnabled(True) + + self._lstrip_textboxes() + + if self.gctFileTextBox.text() != "": + self.gctFileTextBox.setEnabled(True) + self.gctFolderTextBox.setDisabled(True) + elif self.gctFolderTextBox.text() != "": + self.gctFileTextBox.setDisabled(True) + self.gctFolderTextBox.setEnabled(True) + else: + self.gctFileTextBox.setEnabled(True) + self.gctFolderTextBox.setEnabled(True) + + if self.dolTextBox.text().lower().endswith(".dol") and len(self.dolTextBox.text()) > 4: + self.compileButton.setEnabled( + self.gctFileTextBox.text() != "" or self.gctFolderTextBox.text() != "") + else: + self.compileButton.setDisabled(True) + + def retranslate_ui(self): + self.setWindowTitle(QApplication.translate( + "MainWindow", f"GeckoLoader {self.apiRevision} - untitled", None)) + self.menuFile.setTitle( + QApplication.translate("MainWindow", "&File", None)) + self.menuEdit.setTitle( + QApplication.translate("MainWindow", "&Edit", None)) + self.menuHelp.setTitle( + QApplication.translate("MainWindow", "&Help", None)) + self.actionOpen.setText(QApplication.translate( + "MainWindow", "&Open Session...", None)) + self.actionOpen.setStatusTip(QApplication.translate( + "MainWindow", "Open a session", None)) + self.actionOpen.setShortcut( + QApplication.translate("MainWindow", "Ctrl+O", None)) + self.actionClose.setText(QApplication.translate( + "MainWindow", "&Close Session...", None)) + self.actionClose.setStatusTip(QApplication.translate( + "MainWindow", "Close the current session", None)) + self.actionClose.setShortcut(QApplication.translate( + "MainWindow", "Ctrl+Shift+C", None)) + self.actionSave.setText(QApplication.translate( + "MainWindow", "&Save Session", None)) + self.actionSave.setStatusTip(QApplication.translate( + "MainWindow", "Save the current session", None)) + self.actionSave.setShortcut( + QApplication.translate("MainWindow", "Ctrl+S", None)) + self.actionSave_As.setText(QApplication.translate( + "MainWindow", "&Save Session As...", None)) + self.actionSave_As.setStatusTip(QApplication.translate( + "MainWindow", "Save the current session to the specified location", None)) + self.actionSave_As.setShortcut( + QApplication.translate("MainWindow", "Ctrl+Shift+S", None)) + self.actionUndo.setText( + QApplication.translate("MainWindow", "Undo", None)) + self.actionUndo.setStatusTip(QApplication.translate( + "MainWindow", "Undo the last action", None)) + self.actionUndo.setShortcut( + QApplication.translate("MainWindow", "Ctrl+Z", None)) + self.actionRedo.setText( + QApplication.translate("MainWindow", "Redo", None)) + self.actionRedo.setStatusTip(QApplication.translate( + "MainWindow", "Redo the last action", None)) + self.actionRedo.setShortcut(QApplication.translate( + "MainWindow", "Ctrl+Shift+Z", None)) + self.actionCut.setText( + QApplication.translate("MainWindow", "Cut", None)) + self.actionCut.setStatusTip(QApplication.translate( + "MainWindow", "Cuts the selected text and places it on the clipboard", None)) + self.actionCut.setShortcut( + QApplication.translate("MainWindow", "Ctrl+X", None)) + self.actionCopy.setText( + QApplication.translate("MainWindow", "Copy", None)) + self.actionCopy.setStatusTip(QApplication.translate( + "MainWindow", "Copies the selected text and places it on the clipboard", None)) + self.actionCopy.setShortcut( + QApplication.translate("MainWindow", "Ctrl+C", None)) + self.actionPaste.setText( + QApplication.translate("MainWindow", "Paste", None)) + self.actionPaste.setStatusTip(QApplication.translate( + "MainWindow", "Paste the contents of the clipboard", None)) + self.actionPaste.setShortcut( + QApplication.translate("MainWindow", "Ctrl+V", None)) + self.actionDelete.setText( + QApplication.translate("MainWindow", "Delete", None)) + self.actionDelete.setStatusTip(QApplication.translate( + "MainWindow", "Deletes the selected text", None)) + self.actionSelect_All.setText( + QApplication.translate("MainWindow", "Select All", None)) + self.actionSelect_All.setStatusTip(QApplication.translate( + "MainWindow", "Select all of the text", None)) + self.actionSelect_All.setShortcut( + QApplication.translate("MainWindow", "Ctrl+A", None)) + self.actionPreferences.setText(QApplication.translate( + "MainWindow", "&Preferences...", None)) + self.actionPreferences.setStatusTip(QApplication.translate( + "MainWindow", "Open the application preferences dialog", None)) + self.actionAbout_GeckoLoader.setText(QApplication.translate( + "MainWindow", "About &GeckoLoader...", None)) + self.actionAbout_Qt.setText(QApplication.translate( + "MainWindow", "About &Qt...", None)) + self.actionCheck_Update.setText( + QApplication.translate("MainWindow", "&Check Update", None)) + + self.filesLabel.setText( + QApplication.translate("MainWindow", "Files", None)) + + self.dolButton.setText(QApplication.translate( + "MainWindow", "Open DOL", None)) + self.gctFileButton.setText(QApplication.translate( + "MainWindow", "Open Codes", None)) + self.orFolderLabel.setText(QApplication.translate( + "MainWindow", "-"*40 + "OR" + "-"*40, None)) + self.gctFolderButton.setText( + QApplication.translate("MainWindow", "Open Folder", None)) + self.destButton.setText(QApplication.translate( + "MainWindow", "Destination", None)) + + self.optionsLabel.setText( + QApplication.translate("MainWindow", "Options", None)) + + self.allocLabel.setText(QApplication.translate( + "MainWindow", "Allocation", None)) + self.allocLineEdit.setPlaceholderText( + QApplication.translate("MainWindow", "AUTO", None)) + + self.handlerTypeLabel.setText( + QApplication.translate("MainWindow", "Codehandler", None)) + self.handlerTypeSelect.setItemText( + 0, QApplication.translate("Dialog", "FULL", None)) + self.handlerTypeSelect.setItemText( + 1, QApplication.translate("Dialog", "MINI", None)) + + self.hookTypeLabel.setText( + QApplication.translate("MainWindow", "Code Hook", None)) + self.hookTypeSelect.setItemText( + 0, QApplication.translate("Dialog", "VI", None)) + self.hookTypeSelect.setItemText( + 1, QApplication.translate("Dialog", "GX", None)) + self.hookTypeSelect.setItemText( + 2, QApplication.translate("Dialog", "PAD", None)) + + self.txtCodesIncludeLabel.setText( + QApplication.translate("MainWindow", "Include Codes", None)) + self.txtCodesIncludeSelect.setItemText( + 0, QApplication.translate("Dialog", "ACTIVE", None)) + self.txtCodesIncludeSelect.setItemText( + 1, QApplication.translate("Dialog", "ALL", None)) + + self.exOptionsButton.setText(QApplication.translate( + "MainWindow", "Advanced Settings", None)) + + self.compileButton.setText( + QApplication.translate("MainWindow", "RUN", None)) + + def _lstrip_textboxes(self): + attributes = [item for item in vars(self) if not item.startswith('__')] + + for item in attributes: + item = getattr(self, item) + if isinstance(item, QLineEdit): + strlength = len(item.text()) + cursorPos = item.cursorPosition() + item.setText(item.text().lstrip()) + item.setCursorPosition( + cursorPos - (strlength - len(item.text()))) + elif isinstance(item, QPlainTextEdit): + sliderPos = item.verticalScrollBar().sliderPosition() + item.setPlainText(item.toPlainText().lstrip()) + item.verticalScrollBar().setSliderPosition(sliderPos) diff --git a/tools.py b/geckoloader/tools.py similarity index 98% rename from tools.py rename to geckoloader/tools.py index 993a281..acd16c0 100644 --- a/tools.py +++ b/geckoloader/tools.py @@ -1,9 +1,7 @@ -import struct import sys -import os from io import IOBase from argparse import ArgumentParser -from typing import Dict, Optional, Set, Tuple +from typing import Dict, Optional try: import colorama @@ -116,7 +114,6 @@ def color_text(text: str, textToColor: Optional[Dict[str, str]] = None, defaultC class CommandLineParser(ArgumentParser): - def error(self, message: str, prefix: str=None, print_usage=True, exit=True): if print_usage: self.print_usage(sys.stderr) diff --git a/versioncheck.py b/geckoloader/versioncheck.py similarity index 100% rename from versioncheck.py rename to geckoloader/versioncheck.py diff --git a/kernel/codehandler.hxx b/kernel/codehandler.hxx deleted file mode 100644 index 0e36119..0000000 --- a/kernel/codehandler.hxx +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "types.h" - -struct CodeHandler { - u8 mDiscID; - u16 mGameCode; - u8 mRegionCode; - u16 mMakerCode; - u32 mRegArea[38]; - u32 mHandler[]; -}; - -struct CodeHandlerBinary { - s32 mExitInstrOfs; - s32 mGeckoCodeOfs; - s32 mRegListOfs; - s32 _padding1; - CodeHandler mCodeHandler; -}; \ No newline at end of file diff --git a/kernel/globals.hxx b/kernel/globals.hxx deleted file mode 100644 index 5a44eff..0000000 --- a/kernel/globals.hxx +++ /dev/null @@ -1,97 +0,0 @@ -#pragma once - -#include "types.h" - -extern OSGlobals gGlobals; - -class OSGlobals { - -public: - enum class TVMODE { NTSC, PAL, DEBUG, DEBUGPAL, MPAL, PAL60 }; - - struct MetaData { - - const u8 mDiscID; // 0x0000 - const u16 mGameCode; // 0x0001 - const u8 mRegionCode; // 0x0003 - const u16 mMakerCode; // 0x0004 - const u8 mDiscNumber; // 0x0006 - const u8 mDiscVersion; // 0x0007 - const u8 mAudioStreaming; // 0x0008 - const u8 mStreamBufferSize; // 0x0009 - const u8 _00[12]; // 0x000A - const u32 mWiiMagic; // 0x0018 - const u32 mGCNMagic; // 0x001C - const u32 mNinBootCode; // 0x0020 - const u32 mAppVersion; // 0x0024 - const u32 mPhysicalRAMSize; // 0x0028 - const u32 mBoardModel; // 0x002C - u8 *mOSArenaLo; // 0x0030 - u8 *mOSArenaHi; // 0x0034 - u32 *mFstStart; // 0x0038 - u32 mFstSize; // 0x003C - u32 mDebuggerPresent; // 0x0040 - const u32 mDebuggerExceptionMask; // 0x0044 - void *mExceptionHookDest; // 0x0048 - const u32 mExceptionReturn; // 0x004C - u32 _01[0x10 / 4]; // 0x0050 - u32 mDebuggerHook[0x24 / 4]; // 0x0060 - u32 _02[0x3C / 4]; // 0x0084 - u32 mCurrentOSContext; // 0x00C0 - u32 mPreviousOSMask; // 0x00C4 - u32 mCurrentOSMask; // 0x00C8 - OSGlobals::TVMODE mTVMode; // 0x00CC - u32 mARAMSize; // 0x00D0 - void *mCurOSContextLogical; // 0x00D4 - void *mDefaultOSThreadLogical; // 0x00D8 - u32 *mThreadQueueHead; // 0x00DC - u32 *mThreadQueueTail; // 0x00E0 - u32 *mCurrentOSThread; // 0x00E4 - u32 mDebuggerSize; // 0x00E8 - u32 *mDebuggerMonitorLoc; // 0x00EC - u32 mSimulatedMemSize; // 0x00F0 - u8 *mBi2HeaderLoc; // 0x00F4 - u32 mBusClockSpeed; // 0x00F8 - u32 mCPUClockSpeed; // 0x00FC - u32 _04[0x3010 / 4]; // 0x0100 - u8 *mWiiHeap; // 0x3110 - }; - - static MetaData sMetaData; - - enum class CONSOLETYPE { Gamecube, Wii, Unknown }; - - inline u32 getGameID() { - return ((u32)sMetaData.mDiscID << 24) | ((u32)sMetaData.mGameCode << 8) | - ((u32)sMetaData.mRegionCode); - } - inline u16 getMakerID() { return sMetaData.mMakerCode; } - inline u8 getDiscNumber() { return sMetaData.mDiscNumber; } - inline u8 getDiscVersion() { return sMetaData.mDiscVersion; } - - CONSOLETYPE detectHomeConsole() { - if (sMetaData.mGCNMagic) { - return CONSOLETYPE::Gamecube; - } else if (sMetaData.mWiiMagic) { - return CONSOLETYPE::Wii; - } - - return CONSOLETYPE::Unknown; - } - - void allocHeap(u32 alloc) { - if (sMetaData.mBi2HeaderLoc < sMetaData.mOSArenaHi) { - sMetaData.mOSArenaHi = sMetaData.mBi2HeaderLoc - alloc; - if (detectHomeConsole() == OSGlobals::CONSOLETYPE::Wii) { - sMetaData.mWiiHeap = sMetaData.mBi2HeaderLoc - alloc; - } - } else { - if (detectHomeConsole() == OSGlobals::CONSOLETYPE::Wii) { - sMetaData.mOSArenaHi = sMetaData.mWiiHeap - alloc; - sMetaData.mWiiHeap -= alloc; - } else { - sMetaData.mOSArenaHi -= alloc; - } - } - } -}; \ No newline at end of file diff --git a/kernel/kernel.cpp b/kernel/kernel.cpp deleted file mode 100644 index 6ad573e..0000000 --- a/kernel/kernel.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "types.h" -#include "globals.hxx" -#include "kernel.hxx" -#include "memory.hxx" - -OSGlobals gGlobals; - -void GeckoLoaderKernel::runTicket(Ticket &ticket) { - ticket.exec(); -} - - -Memory::Crypt gpCryptor = {0x43595054}; - -static void initMods() -{ - sDisc.setHeap(gpModInfo.allocsize); /*Reallocate the internal heap*/ - - /*Change codelist pointer to the new address in the allocation*/ - CodeList *codelistPointer = (CodeList *)((u32)&gpModInfo + sizeof(Info) + 0xFC); - codelistPointer->mUpperBase = (((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) >> 16) & 0xFFFF; - codelistPointer->mLowerOffset = ((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) & 0xFFFF; - - /*Copy codelist to the new allocation*/ - if (gpModInfo.crypted) - { - Memory::memcpy(sDisc.sMetaData.mOSArenaHi, (u8 *)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize); - gpCryptor.xorCrypt((u32 *)(sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize), (u32 *)((u8 *)&gpModInfo + sizeof(Info) + gpModInfo.handlerSize + 4), gpModInfo.codeSize >> 2); - } - else - { - Memory::memcpy(sDisc.sMetaData.mOSArenaHi, (u8 *)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize + gpModInfo.codeSize); - } - - /*Get codehandler hook resources*/ - auto fillInField = Memory::Search::single((u32 *)sDisc.sMetaData.mOSArenaHi, (u32 *)(sDisc.sMetaData.mOSArenaHi + 0x600), 0x00DEDEDE); - auto returnAddress = extractBranchAddr((u32 *)gpModInfo.codehandlerHook); - auto ppc = *gpModInfo.codehandlerHook; - - /*Write hook branch*/ - Memory::Direct::branch((void *)gpModInfo.codehandlerHook, (void *)((u32)sDisc.sMetaData.mOSArenaHi + 0xA8), false); //entryhook - - /*Temporary nop*/ - *fillInField = 0x60000000; - - /*Flush the cache so that the instructions update*/ - Memory::Cache::flushRange((u8 *)sDisc.sMetaData.mOSArenaHi, gpModInfo.handlerSize + gpModInfo.codeSize); - - /*Call the codehandler*/ - call((void *)((u32)(sDisc.sMetaData.mOSArenaHi) + 0xA8))(); - - /*Write original instruction or translate offset data if a branch*/ - if (((ppc >> 24) & 0xFF) > 0x47 && ((ppc >> 24) & 0xFF) < 0x4C) - { - Memory::Direct::branch((void *)fillInField, (void *)returnAddress, ppc & 1); - } - else - { - Memory::Direct::write(fillInField, ppc); - } - - /*Write branch back to the hook address + 4*/ - Memory::Direct::branch((void *)&fillInField[1], (void *)(&gpModInfo.codehandlerHook[1]), false); //return -} - -int main() -{ - if (sDisc.detectHomeConsole() != DiscHeader::CONSOLETYPE::Unknown) - { - initMods(); - } - __start(); -} \ No newline at end of file diff --git a/kernel/kernel.hxx b/kernel/kernel.hxx deleted file mode 100644 index 0645c2f..0000000 --- a/kernel/kernel.hxx +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include "types.h" -#include "ticket.hxx" - -class GeckoLoaderKernel { - void execHandler(void (*codehandler)()); -public: - void runTicket(Ticket &ticket); -}; \ No newline at end of file diff --git a/kernel/memory.hxx b/kernel/memory.hxx deleted file mode 100644 index bbd1117..0000000 --- a/kernel/memory.hxx +++ /dev/null @@ -1,161 +0,0 @@ -#pragma once - -#include "types.h" - -#define dcbst(_val) asm volatile("dcbst 0, %0" : : "r"(_val)) -#define dcbf(_val) asm volatile("dcbf 0, %0" : : "r"(_val)) -#define icbi(_val) asm volatile("icbi 0, %0" : : "r"(_val)) - -namespace Memory { - -static void memcpy(void *dst, void *src, size_t size) { - u8 *castDst = static_cast(dst); - u8 *castSrc = static_cast(src); - for (s32 i = 0; i < size; ++i) { - *castDst++ = *castSrc++; - } -} - -namespace Cache { - -inline void flushAddr(void *addr) { - dcbf(addr); - icbi(addr); -} - -inline void flushRange(u8 *addr, s32 size) { - size += 31 + (((u32)addr & 31) > 0); - - for (u32 i = 0; i < (size >> 5); ++i) { - flushAddr((void *)(addr + (i << 5))); - } -} - -inline void storeAddr(void *addr) { - dcbst(addr); - icbi(addr); -} - -inline void storeRange(u8 *addr, s32 size) { - size += 31 + (((u32)addr & 31) > 0); - - for (u32 i = 0; i < (size >> 5); ++i) { - storeAddr((void *)(addr + (i << 5))); - } -} - -} // namespace Cache - -namespace Direct { - -template inline void write(T *addr, T value) { - *addr = value; - Cache::flushAddr(addr); -} - -/*This constructs a branch instruction. &TO = ((TO - FROM) & MAX_OFFSET) | - * BRANCH_TYPE | !!isLink*/ -inline void writeBranch(void *addr, void *to, bool lk) { - Direct::write((u32 *)(addr), ((((u32)(to) - (u32)(addr)) & 0x3ffffff) | - 0x48000000 | lk)); -} - -/*Get the target address of the branch at bAddr*/ -inline u32 getBranch(u32 *bAddr) { - s32 offset; - if (*bAddr & 0x2000000) - offset = (*bAddr & 0x3FFFFFD) - 0x4000000; - else - offset = *bAddr & 0x3FFFFFD; - return (u32)bAddr + offset; -} - -} // namespace Direct - -namespace Search { - -template -T *array(T *start, T *end, size_t matchLen, const T *matchData) { - u32 index = 0; - - /*Loop through the games RAM, make sure we don't find our own hook data by - * accident*/ - for (u32 i = 0; &start[i] < end; ++i) { - /*If the data matches, increase the index counter and continue search, - else set index to 0 and continue searching*/ - if (start[i] == matchData[index]) - ++index; - else - index = 0; - - /*If the data has matched the whole array, return the address of the match*/ - if (index >= matchLen) - return &start[i]; - } - return nullptr; -} - -template T *single(T *start, T *end, T match) { - for (u32 i = 0; &start[i] < end; ++i) { - if (start[i] == match) { - return &start[i]; - } - } - return nullptr; -} - -/*Call this after viHook, finds the address of the first instance -of targetVal, and hooks it to the pointer hookTo*/ -static inline void hookFunction(u32 *start, u32 targetVal, void *hookTo, - bool lk) { - Direct::branch(Search::single(start, start + 0x500, targetVal), hookTo, - lk); -} - -} // namespace Search - -class Crypt { - -private: - u32 key; - - u32 getKey() { - u32 b1 = (this->key >> 24) & 0xFF; - u32 b2 = (this->key >> 16) & 0xFF; - u32 b3 = (this->key >> 8) & 0xFF; - u32 b4 = this->key & 0xFF; - b1 ^= b2; - b2 ^= b3; - b3 ^= b4; - return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1; - } - - void setKey(u32 key) { - u32 b1 = key & 0xFF; - u32 b2 = (key >> 8) & 0xFF; - u32 b3 = (key >> 16) & 0xFF; - u32 b4 = (key >> 24) & 0xFF; - b3 ^= b4; - b2 ^= b3; - b1 ^= b2; - this->key = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4; - } - -public: - Crypt(u32 key) { this->key = key; } - - inline void xorCrypt(u32 *dest, u32 *buffer, u32 size) { - auto key = this->getKey(); - - for (u32 i = 0; i < size; ++i) { - dest[i] = buffer[i] ^ key; - key += i << 3; - } - } -}; - -constexpr u32 mem_start = 0x80000000; -constexpr u32 mem_end = 0x81800000; -constexpr size_t mem_size = mem_end - mem_start; - -} // namespace Memory \ No newline at end of file diff --git a/kernel/ticket.cpp b/kernel/ticket.cpp deleted file mode 100644 index 5a8b6f1..0000000 --- a/kernel/ticket.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "ticket.hxx" -#include "globals.hxx" - -bool Ticket::exec() { - gGlobals.allocHeap((mSizeInfo.getPacketSize() + 31) & -32); /*Reallocate the internal heap*/ - - /*Change codelist pointer to the new address in the allocation*/ - CodeList *codelistPointer = (CodeList *)((u32)&gpModInfo + sizeof(Info) + 0xFC); - codelistPointer->mUpperBase = (((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) >> 16) & 0xFFFF; - codelistPointer->mLowerOffset = ((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) & 0xFFFF; - - /*Copy codelist to the new allocation*/ - if (gpModInfo.crypted) - { - Memory::memcpy(sDisc.sMetaData.mOSArenaHi, (u8 *)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize); - gpCryptor.xorCrypt((u32 *)(sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize), (u32 *)((u8 *)&gpModInfo + sizeof(Info) + gpModInfo.handlerSize + 4), gpModInfo.codeSize >> 2); - } - else - { - Memory::memcpy(sDisc.sMetaData.mOSArenaHi, (u8 *)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize + gpModInfo.codeSize); - } - - /*Get codehandler hook resources*/ - auto fillInField = Memory::Search::single((u32 *)sDisc.sMetaData.mOSArenaHi, (u32 *)(sDisc.sMetaData.mOSArenaHi + 0x600), 0x00DEDEDE); - auto returnAddress = extractBranchAddr((u32 *)gpModInfo.codehandlerHook); - auto ppc = *gpModInfo.codehandlerHook; - - /*Write hook branch*/ - Memory::Direct::writeBranch(gpModInfo.codehandlerHook, (u8 *)sDisc.sMetaData.mOSArenaHi + 0xA8, false); //entryhook - - /*Temporary nop*/ - *fillInField = 0x60000000; - - /*Flush the cache so that the instructions update*/ - Memory::Cache::flushRange((u8 *)sDisc.sMetaData.mOSArenaHi, gpModInfo.handlerSize + gpModInfo.codeSize); - - /*Call the codehandler*/ - call((void *)((u32)(sDisc.sMetaData.mOSArenaHi) + 0xA8))(); - - /*Write original instruction or translate offset data if a branch*/ - if (((ppc >> 24) & 0xFF) > 0x47 && ((ppc >> 24) & 0xFF) < 0x4C) - { - Memory::Direct::branch((void *)fillInField, (void *)returnAddress, ppc & 1); - } - else - { - Memory::Direct::write(fillInField, ppc); - } - - /*Write branch back to the hook address + 4*/ - Memory::Direct::branch((void *)&fillInField[1], (void *)(&gpModInfo.codehandlerHook[1]), false); //return -} \ No newline at end of file diff --git a/kernel/ticket.hxx b/kernel/ticket.hxx deleted file mode 100644 index b72ccd5..0000000 --- a/kernel/ticket.hxx +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "types.h" - -struct SizeInfo { - size_t mLoaderSize; - size_t mHandlerSize; - size_t mCodeSize; - - size_t getPacketSize() const { - return mLoaderSize + mHandlerSize + mCodeSize; - } -}; - -struct Ticket { - enum class Type { - MEMORY, - DISC - }; - - SizeInfo mSizeInfo; - void *mBinary; - - bool exec(); - void *getLoaderPtr(); - void *getHandlerPtr(); - void *getGeckoPtr(); -}; \ No newline at end of file diff --git a/kernel/types.h b/kernel/types.h deleted file mode 100644 index 5eb9762..0000000 --- a/kernel/types.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned long u32; -typedef unsigned long long u64; -typedef char s8; -typedef short s16; -typedef long s32; -typedef long long s64; -typedef float f32; -typedef double f64; -typedef volatile s8 vs8; -typedef volatile s16 vs16; -typedef volatile s32 vs32; -typedef volatile s64 vs64; -typedef volatile u8 vu8; -typedef volatile u16 vu16; -typedef volatile u32 vu32; -typedef volatile u64 vu64; -typedef volatile f32 vf32; -typedef volatile f64 vf64; - -typedef unsigned long size_t; - -#ifdef __cplusplus -#define NULL nullptr -#else -#define NULL ((void*)0) -#define bool int -#define true 1 -#define false 0 -#endif \ No newline at end of file diff --git a/loader.cpp b/loader.cpp index e468773..3a94845 100644 --- a/loader.cpp +++ b/loader.cpp @@ -179,7 +179,7 @@ static void storeRange(u8 *addr, s32 size) { } // namespace Cache -namespace Direct { +namespace Instr { template static inline void write(T *addr, T value) { *addr = value; @@ -189,7 +189,7 @@ template static inline void write(T *addr, T value) { /*This constructs a branch instruction. &TO = ((TO - FROM) & MAX_OFFSET) | * BRANCH_TYPE | !!isLink*/ static inline void branch(void *addr, void *to, bool lk) { - Direct::write((u32 *)(addr), ((((u32)(to) - (u32)(addr)) & 0x3ffffff) | + Instr::write((u32 *)(addr), ((((u32)(to) - (u32)(addr)) & 0x3ffffff) | 0x48000000 | lk)); } @@ -232,7 +232,7 @@ template static T *single(T *start, T *end, T match) { of targetVal, and hooks it to the pointer hookTo*/ static inline void hookFunction(u32 *start, u32 targetVal, void *hookTo, bool lk) { - Direct::branch(Search::single(start, start + 0x500, targetVal), hookTo, + Instr::branch(Search::single(start, start + 0x500, targetVal), hookTo, lk); } @@ -322,7 +322,7 @@ static void initMods() { auto ppc = *gpModInfo.codehandlerHook; /*Write hook branch*/ - Memory::Direct::branch((void *)gpModInfo.codehandlerHook, + Memory::Instr::branch((void *)gpModInfo.codehandlerHook, (void *)((u32)sDisc.sMetaData.mOSArenaHi + 0xA8), false); // entryhook @@ -338,13 +338,13 @@ static void initMods() { /*Write original instruction or translate offset data if a branch*/ if (((ppc >> 24) & 0xFF) > 0x47 && ((ppc >> 24) & 0xFF) < 0x4C) { - Memory::Direct::branch((void *)fillInField, (void *)returnAddress, ppc & 1); + Memory::Instr::branch((void *)fillInField, (void *)returnAddress, ppc & 1); } else { - Memory::Direct::write(fillInField, ppc); + Memory::Instr::write(fillInField, ppc); } /*Write branch back to the hook address + 4*/ - Memory::Direct::branch((void *)&fillInField[1], + Memory::Instr::branch((void *)&fillInField[1], (void *)(&gpModInfo.codehandlerHook[1]), false); // return } diff --git a/main_ui.py b/main_ui.py deleted file mode 100644 index d8d6150..0000000 --- a/main_ui.py +++ /dev/null @@ -1,762 +0,0 @@ -from PyQt5 import QtCore, QtGui, QtWidgets - -from children_ui import PrefWindow -from fileutils import resource_path - -class MainWindow(QtWidgets.QMainWindow): - def __init__(self, version: str): - super().__init__() - - self._job_active = False - self.apiRevision = version - self.setup_ui() - - self.LightTheme = self.palette() - - self.DarkTheme = QtGui.QPalette() - self.DarkTheme.setColor(QtGui.QPalette.Window, QtGui.QColor(53, 53, 53)) - self.DarkTheme.setColor(QtGui.QPalette.WindowText, QtCore.Qt.white) - self.DarkTheme.setColor(QtGui.QPalette.Base, QtGui.QColor(25, 25, 25)) - self.DarkTheme.setColor(QtGui.QPalette.AlternateBase, QtGui.QColor(53, 53, 53)) - self.DarkTheme.setColor(QtGui.QPalette.ToolTipBase, QtCore.Qt.black) - self.DarkTheme.setColor(QtGui.QPalette.ToolTipText, QtCore.Qt.white) - self.DarkTheme.setColor(QtGui.QPalette.Text, QtCore.Qt.white) - self.DarkTheme.setColor(QtGui.QPalette.Button, QtGui.QColor(53, 53, 53)) - self.DarkTheme.setColor(QtGui.QPalette.ButtonText, QtCore.Qt.white) - self.DarkTheme.setColor(QtGui.QPalette.BrightText, QtCore.Qt.red) - self.DarkTheme.setColor(QtGui.QPalette.Link, QtGui.QColor(42, 130, 218)) - self.DarkTheme.setColor(QtGui.QPalette.Highlight, QtGui.QColor(42, 130, 218)) - self.DarkTheme.setColor(QtGui.QPalette.HighlightedText, QtCore.Qt.black) - - def set_job_activity(self, active: bool): - self._job_active = active - - def close_event(self, event: QtGui.QCloseEvent): - if self._job_active: - reply = QtWidgets.QMessageBox(self) - reply.setWindowTitle("Active job") - reply.setText("GeckoLoader is busy!") - reply.setInformativeText("Exiting is disabled") - reply.setIcon(QtWidgets.QMessageBox.Warning) - reply.setStandardButtons(QtWidgets.QMessageBox.Ok) - reply.setDefaultButton(QtWidgets.QMessageBox.Ok) - reply.exec_() - event.ignore() - else: - event.accept() - - def setup_ui(self): - self.setObjectName("MainWindow") - self.setWindowModality(QtCore.Qt.NonModal) - self.setEnabled(True) - self.setFixedSize(550, 680) - font = QtGui.QFont() - font.setFamily("Helvetica") - font.setPointSize(10) - font.setWeight(42) - self.setFont(font) - icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap(str(resource_path("bin/icon.ico"))), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.setWindowIcon(icon) - - #Top level widget - self.centerWidget = QtWidgets.QWidget(self) - self.centerWidget.setObjectName("centerWidget") - - self.gridLayout = QtWidgets.QGridLayout(self.centerWidget) - self.gridLayout.setVerticalSpacing(0) - self.gridLayout.setObjectName("gridLayout") - - #Layout for file paths and open boxes - self.filesLayout = QtWidgets.QGridLayout() - self.filesLayout.setHorizontalSpacing(0) - self.filesLayout.setObjectName("filesLayout") - - self.dolLayout = QtWidgets.QGridLayout() - self.dolLayout.setHorizontalSpacing(0) - self.dolLayout.setObjectName("dolLayout") - - #Layout for folder path - self.gctLayout = QtWidgets.QGridLayout() - self.gctLayout.setHorizontalSpacing(0) - self.gctLayout.setVerticalSpacing(5) - self.gctLayout.setObjectName("gctLayout") - - self.destLayout = QtWidgets.QGridLayout() - self.dolLayout.setHorizontalSpacing(0) - self.dolLayout.setObjectName("dolLayout") - - #Files label - self.filesLabel = QtWidgets.QLabel(self.centerWidget) - self.filesLabel.setEnabled(False) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.filesLabel.sizePolicy().hasHeightForWidth()) - self.filesLabel.setSizePolicy(sizePolicy) - self.filesLabel.setMinimumSize(QtCore.QSize(80, 30)) - self.filesLabel.setMaximumSize(QtCore.QSize(16777215, 30)) - font = QtGui.QFont("Helvetica") - font.setPointSize(21) - font.setWeight(82) - font.setBold(True) - self.filesLabel.setFont(font) - self.filesLabel.setTextFormat(QtCore.Qt.PlainText) - self.filesLabel.setAlignment(QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter) - self.filesLabel.setObjectName("filesLabel") - - #Dol button to open file - self.dolButton = QtWidgets.QPushButton(self.centerWidget) - self.dolButton.setMinimumSize(QtCore.QSize(100, 26)) - self.dolButton.setMaximumSize(QtCore.QSize(100, 26)) - font = QtGui.QFont("Helvetica") - font.setPointSize(11) - self.dolButton.setFont(font) - self.dolButton.setCheckable(False) - self.dolButton.setChecked(False) - self.dolButton.setAutoDefault(True) - self.dolButton.setDefault(False) - self.dolButton.setFlat(False) - self.dolButton.setObjectName("dolButton") - self.dolLayout.addWidget(self.dolButton, 1, 0, 1, 1) - - #Dol path textbox - self.dolTextBox = QtWidgets.QLineEdit(self.centerWidget) - self.dolTextBox.setEnabled(False) - self.dolTextBox.setMinimumSize(QtCore.QSize(200, 24)) - self.dolTextBox.setMaximumSize(QtCore.QSize(16777215, 24)) - font = QtGui.QFont() - font.setFamily("Consolas") - font.setPointSize(10) - font.setWeight(42) - self.dolTextBox.setFont(font) - self.dolTextBox.setText("") - self.dolTextBox.setMaxLength(255) - self.dolTextBox.setFrame(True) - self.dolTextBox.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter) - self.dolTextBox.setObjectName("dolTextBox") - self.dolLayout.addWidget(self.dolTextBox, 1, 1, 1, 1) - - #horizontal separater codes - self.horiSepFiles = QtWidgets.QFrame(self.centerWidget) - self.horiSepFiles.setMinimumSize(QtCore.QSize(474, 30)) - self.horiSepFiles.setContentsMargins(20, 0, 20, 0) - self.horiSepFiles.setFrameShape(QtWidgets.QFrame.HLine) - self.horiSepFiles.setFrameShadow(QtWidgets.QFrame.Sunken) - self.horiSepFiles.setObjectName("horiSepFiles") - - #gctFile button to open file - self.gctFileButton = QtWidgets.QPushButton(self.centerWidget) - self.gctFileButton.setMinimumSize(QtCore.QSize(100, 26)) - self.gctFileButton.setMaximumSize(QtCore.QSize(100, 26)) - font = QtGui.QFont("Helvetica") - font.setPointSize(10) - self.gctFileButton.setFont(font) - self.gctFileButton.setCheckable(False) - self.gctFileButton.setChecked(False) - self.gctFileButton.setAutoDefault(True) - self.gctFileButton.setDefault(False) - self.gctFileButton.setFlat(False) - self.gctFileButton.setObjectName("gctFileButton") - self.gctLayout.addWidget(self.gctFileButton, 0, 0, 1, 1) - - #gctFile path textbox - self.gctFileTextBox = QtWidgets.QLineEdit(self.centerWidget) - self.gctFileTextBox.setEnabled(False) - self.gctFileTextBox.setMinimumSize(QtCore.QSize(200, 24)) - self.gctFileTextBox.setMaximumSize(QtCore.QSize(16777215, 24)) - font = QtGui.QFont() - font.setFamily("Consolas") - font.setPointSize(10) - font.setWeight(42) - self.gctFileTextBox.setFont(font) - self.gctFileTextBox.setText("") - self.gctFileTextBox.setMaxLength(255) - self.gctFileTextBox.setFrame(True) - self.gctFileTextBox.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter) - self.gctFileTextBox.setObjectName("gctFileTextBox") - self.gctLayout.addWidget(self.gctFileTextBox, 0, 1, 1, 1) - - #--or-- Label - self.orFolderLabel = QtWidgets.QLabel(self.centerWidget) - self.orFolderLabel.setEnabled(False) - self.orFolderLabel.setMinimumSize(QtCore.QSize(80, 8)) - self.orFolderLabel.setMaximumSize(QtCore.QSize(16777215, 8)) - font = QtGui.QFont("Helvetica") - font.setPointSize(8) - font.setWeight(82) - font.setBold(True) - self.orFolderLabel.setFont(font) - self.orFolderLabel.setTextFormat(QtCore.Qt.PlainText) - self.orFolderLabel.setAlignment(QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter) - self.orFolderLabel.setObjectName("orFolderLabel") - self.gctLayout.addWidget(self.orFolderLabel, 1, 0, 1, 2) - - #gctFolder button to open file - self.gctFolderButton = QtWidgets.QPushButton(self.centerWidget) - self.gctFolderButton.setMinimumSize(QtCore.QSize(100, 26)) - self.gctFolderButton.setMaximumSize(QtCore.QSize(100, 26)) - font = QtGui.QFont("Helvetica") - font.setPointSize(10) - self.gctFolderButton.setFont(font) - self.gctFolderButton.setCheckable(False) - self.gctFolderButton.setChecked(False) - self.gctFolderButton.setAutoDefault(True) - self.gctFolderButton.setDefault(False) - self.gctFolderButton.setFlat(False) - self.gctFolderButton.setObjectName("gctFolderButton") - self.gctLayout.addWidget(self.gctFolderButton, 2, 0, 1, 1) - - #gctFolder path textbox - self.gctFolderTextBox = QtWidgets.QLineEdit(self.centerWidget) - self.gctFolderTextBox.setEnabled(False) - self.gctFolderTextBox.setMinimumSize(QtCore.QSize(200, 24)) - self.gctFolderTextBox.setMaximumSize(QtCore.QSize(16777215, 24)) - font = QtGui.QFont() - font.setFamily("Consolas") - font.setPointSize(10) - font.setWeight(42) - self.gctFolderTextBox.setFont(font) - self.gctFolderTextBox.setText("") - self.gctFolderTextBox.setMaxLength(255) - self.gctFolderTextBox.setFrame(True) - self.gctFolderTextBox.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter) - self.gctFolderTextBox.setObjectName("gctFolderTextBox") - self.gctLayout.addWidget(self.gctFolderTextBox, 2, 1, 1, 1) - - #horizontal separater dest - self.horiSepDest = QtWidgets.QFrame(self.centerWidget) - self.horiSepDest.setMinimumSize(QtCore.QSize(474, 30)) - self.horiSepDest.setContentsMargins(20, 0, 20, 0) - self.horiSepDest.setFrameShape(QtWidgets.QFrame.HLine) - self.horiSepDest.setFrameShadow(QtWidgets.QFrame.Sunken) - self.horiSepDest.setObjectName("horiSepDest") - - #Dest button to open file - self.destButton = QtWidgets.QPushButton(self.centerWidget) - self.destButton.setMinimumSize(QtCore.QSize(100, 26)) - self.destButton.setMaximumSize(QtCore.QSize(100, 26)) - font = QtGui.QFont("Helvetica") - font.setPointSize(11) - self.destButton.setFont(font) - self.destButton.setCheckable(False) - self.destButton.setChecked(False) - self.destButton.setAutoDefault(True) - self.destButton.setDefault(False) - self.destButton.setFlat(False) - self.destButton.setObjectName("destButton") - self.destLayout.addWidget(self.destButton, 0, 0, 1, 1) - - #Dest path textbox - self.destTextBox = QtWidgets.QLineEdit(self.centerWidget) - self.destTextBox.setEnabled(False) - self.destTextBox.setMinimumSize(QtCore.QSize(200, 24)) - self.destTextBox.setMaximumSize(QtCore.QSize(16777215, 24)) - font = QtGui.QFont() - font.setFamily("Consolas") - font.setPointSize(10) - font.setWeight(42) - self.destTextBox.setFont(font) - self.destTextBox.setText("") - self.destTextBox.setMaxLength(255) - self.destTextBox.setFrame(True) - self.destTextBox.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter) - self.destTextBox.setObjectName("destTextBox") - self.destLayout.addWidget(self.destTextBox, 0, 1, 1, 1) - - self.filesLayout.addLayout(self.dolLayout, 0, 0, 1, 1) - self.filesLayout.addWidget(self.horiSepFiles, 1, 0, 1, 1) - self.filesLayout.addLayout(self.gctLayout, 2, 0, 1, 1) - self.filesLayout.addWidget(self.horiSepDest, 3, 0, 1, 1) - self.filesLayout.addLayout(self.destLayout, 4, 0, 1, 1) - - #Options Layout - self.optionsLayout = QtWidgets.QGridLayout() - self.optionsLayout.setHorizontalSpacing(20) - self.optionsLayout.setObjectName("optionsLayout") - - #Options Label - self.optionsLabel = QtWidgets.QLabel(self.centerWidget) - self.optionsLabel.setEnabled(False) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.optionsLabel.sizePolicy().hasHeightForWidth()) - self.optionsLabel.setSizePolicy(sizePolicy) - self.optionsLabel.setMinimumSize(QtCore.QSize(79, 23)) - self.optionsLabel.setMaximumSize(QtCore.QSize(16777215, 23)) - font = QtGui.QFont("Helvetica") - font.setPointSize(18) - font.setWeight(82) - font.setBold(True) - self.optionsLabel.setFont(font) - self.optionsLabel.setTextFormat(QtCore.Qt.PlainText) - self.optionsLabel.setAlignment(QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter) - self.optionsLabel.setObjectName("optionsLabel") - self.optionsLayout.addWidget(self.optionsLabel, 0, 0, 1, 4) - - #Allocation Label - self.allocLabel = QtWidgets.QLabel(self.centerWidget) - self.allocLabel.setEnabled(False) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.allocLabel.sizePolicy().hasHeightForWidth()) - self.allocLabel.setSizePolicy(sizePolicy) - self.allocLabel.setMinimumSize(QtCore.QSize(79, 23)) - self.allocLabel.setMaximumSize(QtCore.QSize(16777215, 23)) - self.allocLabel.setTextFormat(QtCore.Qt.PlainText) - self.allocLabel.setAlignment(QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter) - self.allocLabel.setObjectName("allocLabel") - self.optionsLayout.addWidget(self.allocLabel, 1, 0, 1, 1) - - #Allocation Textbox - self.allocLineEdit = QtWidgets.QLineEdit(self.centerWidget) - self.allocLineEdit.setEnabled(False) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.allocLineEdit.sizePolicy().hasHeightForWidth()) - self.allocLineEdit.setSizePolicy(sizePolicy) - self.allocLineEdit.setMinimumSize(QtCore.QSize(79, 23)) - self.allocLineEdit.setMaximumSize(QtCore.QSize(79, 23)) - font = QtGui.QFont() - font.setFamily("Consolas") - font.setPointSize(12) - font.setWeight(42) - self.allocLineEdit.setFont(font) - self.allocLineEdit.setText("") - self.allocLineEdit.setMaxLength(6) - self.allocLineEdit.setAlignment(QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter) - self.allocLineEdit.setObjectName("allocLineEdit") - self.optionsLayout.addWidget(self.allocLineEdit, 2, 0, 1, 1) - - #handlerType label - self.handlerTypeLabel = QtWidgets.QLabel(self.centerWidget) - self.handlerTypeLabel.setEnabled(False) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.handlerTypeLabel.sizePolicy().hasHeightForWidth()) - self.handlerTypeLabel.setSizePolicy(sizePolicy) - self.handlerTypeLabel.setMinimumSize(QtCore.QSize(79, 23)) - self.handlerTypeLabel.setMaximumSize(QtCore.QSize(16777215, 23)) - self.handlerTypeLabel.setTextFormat(QtCore.Qt.PlainText) - self.handlerTypeLabel.setAlignment(QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter) - self.handlerTypeLabel.setObjectName("handlerTypeLabel") - self.optionsLayout.addWidget(self.handlerTypeLabel, 1, 1, 1, 1) - - #handlerType selection - self.handlerTypeSelect = QtWidgets.QComboBox(self.centerWidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.allocLabel.sizePolicy().hasHeightForWidth()) - self.handlerTypeSelect.setSizePolicy(sizePolicy) - self.handlerTypeSelect.setMinimumSize(QtCore.QSize(79, 23)) - self.handlerTypeSelect.setMaximumSize(QtCore.QSize(79, 23)) - self.handlerTypeSelect.setObjectName("handlerTypeSelect") - self.handlerTypeSelect.addItems(["FULL", "MINI"]) - self.optionsLayout.addWidget(self.handlerTypeSelect, 2, 1, 1, 1) - - #hookType label - self.hookTypeLabel = QtWidgets.QLabel(self.centerWidget) - self.hookTypeLabel.setEnabled(False) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.hookTypeLabel.sizePolicy().hasHeightForWidth()) - self.hookTypeLabel.setSizePolicy(sizePolicy) - self.hookTypeLabel.setMinimumSize(QtCore.QSize(79, 23)) - self.hookTypeLabel.setMaximumSize(QtCore.QSize(16777215, 23)) - self.hookTypeLabel.setTextFormat(QtCore.Qt.PlainText) - self.hookTypeLabel.setAlignment(QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter) - self.hookTypeLabel.setObjectName("hookTypeLabel") - self.optionsLayout.addWidget(self.hookTypeLabel, 1, 2, 1, 1) - - #hookType selection - self.hookTypeSelect = QtWidgets.QComboBox(self.centerWidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.allocLabel.sizePolicy().hasHeightForWidth()) - self.hookTypeSelect.setSizePolicy(sizePolicy) - self.hookTypeSelect.setMinimumSize(QtCore.QSize(79, 23)) - self.hookTypeSelect.setMaximumSize(QtCore.QSize(79, 23)) - self.hookTypeSelect.setObjectName("hookTypeSelect") - self.hookTypeSelect.addItems(["VI", "GX", "PAD"]) - self.optionsLayout.addWidget(self.hookTypeSelect, 2, 2, 1, 1) - - #txtCodesInclude label - self.txtCodesIncludeLabel = QtWidgets.QLabel(self.centerWidget) - self.txtCodesIncludeLabel.setEnabled(False) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.txtCodesIncludeLabel.sizePolicy().hasHeightForWidth()) - self.txtCodesIncludeLabel.setSizePolicy(sizePolicy) - self.txtCodesIncludeLabel.setMinimumSize(QtCore.QSize(79, 23)) - self.txtCodesIncludeLabel.setMaximumSize(QtCore.QSize(16777215, 23)) - self.txtCodesIncludeLabel.setTextFormat(QtCore.Qt.PlainText) - self.txtCodesIncludeLabel.setAlignment(QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter) - self.txtCodesIncludeLabel.setObjectName("txtCodesIncludeLabel") - self.optionsLayout.addWidget(self.txtCodesIncludeLabel, 1, 3, 1, 1) - - #txtCodesInclude selection - self.txtCodesIncludeSelect = QtWidgets.QComboBox(self.centerWidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.allocLabel.sizePolicy().hasHeightForWidth()) - self.txtCodesIncludeSelect.setSizePolicy(sizePolicy) - self.txtCodesIncludeSelect.setMinimumSize(QtCore.QSize(79, 23)) - self.txtCodesIncludeSelect.setMaximumSize(QtCore.QSize(79, 23)) - self.txtCodesIncludeSelect.setObjectName("txtCodesIncludeSelect") - self.txtCodesIncludeSelect.addItems(["ACTIVE", "ALL"]) - self.optionsLayout.addWidget(self.txtCodesIncludeSelect, 2, 3, 1, 1) - - #horizontal separater options - self.horiSepOptions = QtWidgets.QFrame(self.centerWidget) - self.horiSepOptions.setMinimumSize(QtCore.QSize(300, 30)) - self.horiSepOptions.setContentsMargins(20, 0, 20, 0) - self.horiSepOptions.setFrameShape(QtWidgets.QFrame.HLine) - self.horiSepOptions.setFrameShadow(QtWidgets.QFrame.Sunken) - self.horiSepOptions.setObjectName("horiSepOptions") - self.optionsLayout.addWidget(self.horiSepOptions, 3, 0, 1, 4) - - #Advanced options button - self.exOptionsButton = QtWidgets.QPushButton(self.centerWidget) - font = QtGui.QFont("Helvetica") - font.setPointSize(13) - self.exOptionsButton.setFont(font) - self.exOptionsButton.setCheckable(False) - self.exOptionsButton.setChecked(False) - self.exOptionsButton.setAutoDefault(True) - self.exOptionsButton.setDefault(False) - self.exOptionsButton.setFlat(False) - self.exOptionsButton.setDisabled(True) - self.exOptionsButton.setObjectName("exOptionsButton") - self.optionsLayout.addWidget(self.exOptionsButton, 4, 0, 1, 4) - - #horizontal separater 1 - self.horiSepA = QtWidgets.QFrame(self.centerWidget) - self.horiSepA.setMinimumSize(QtCore.QSize(470, 30)) - self.horiSepA.setFrameShape(QtWidgets.QFrame.HLine) - self.horiSepA.setFrameShadow(QtWidgets.QFrame.Sunken) - self.horiSepA.setObjectName("horiSepA") - - #horizontal separater 2 - self.horiSepB = QtWidgets.QFrame(self.centerWidget) - self.horiSepB.setMinimumSize(QtCore.QSize(470, 30)) - self.horiSepB.setFrameShape(QtWidgets.QFrame.HLine) - self.horiSepB.setFrameShadow(QtWidgets.QFrame.Sunken) - self.horiSepB.setObjectName("horiSepB") - - #response panel - self.responses = QtWidgets.QPlainTextEdit(self.centerWidget) - self.responses.setEnabled(True) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.responses.sizePolicy().hasHeightForWidth()) - self.responses.setSizePolicy(sizePolicy) - self.responses.setMinimumSize(QtCore.QSize(474, 180)) - self.responses.setMaximumSize(QtCore.QSize(16777215, 180)) - font = QtGui.QFont() - font.setFamily("Consolas") - font.setPointSize(8) - font.setWeight(42) - fontMetrics = QtGui.QFontMetricsF(font) - spaceWidth = fontMetrics.width(' ') - self.responses.setFont(font) - self.responses.setPlainText("") - self.responses.setTabStopDistance(spaceWidth * 4) - self.responses.setReadOnly(True) - self.responses.setObjectName("responses") - - #Compile button - self.compileButton = QtWidgets.QPushButton(self.centerWidget) - font = QtGui.QFont("Helvetica") - font.setPointSize(34) - self.compileButton.setFont(font) - self.compileButton.setCheckable(False) - self.compileButton.setChecked(False) - self.compileButton.setAutoDefault(True) - self.compileButton.setDefault(False) - self.compileButton.setFlat(False) - self.compileButton.setDisabled(True) - self.compileButton.setObjectName("compileButton") - - self.gridLayout.addWidget(self.filesLabel, 0, 0, 1, 1) - self.gridLayout.addLayout(self.filesLayout, 1, 0, 1, 1) - self.gridLayout.addWidget(self.horiSepA, 2, 0, 1, 1) - self.gridLayout.addLayout(self.optionsLayout, 3, 0, 1, 1) - self.gridLayout.addWidget(self.horiSepB, 4, 0, 1, 1) - self.gridLayout.addWidget(self.responses, 5, 0, 1, 1) - self.gridLayout.addWidget(self.compileButton, 6, 0, 1, 1) - - self.setCentralWidget(self.centerWidget) - - #Toolbar - self.menubar = QtWidgets.QMenuBar(self) - self.menubar.setGeometry(QtCore.QRect(0, 0, 470, 22)) - self.menubar.setObjectName("menubar") - - self.menuFile = QtWidgets.QMenu(self.menubar) - font = QtGui.QFont() - font.setFamily("Helvetica") - self.menuFile.setFont(font) - self.menuFile.setObjectName("menuFile") - - self.menuEdit = QtWidgets.QMenu(self.menubar) - font = QtGui.QFont() - font.setFamily("Helvetica") - self.menuEdit.setFont(font) - self.menuEdit.setObjectName("menuEdit") - - self.menuHelp = QtWidgets.QMenu(self.menubar) - font = QtGui.QFont() - font.setFamily("Helvetica") - self.menuHelp.setFont(font) - self.menuHelp.setObjectName("menuHelp") - - self.setMenuBar(self.menubar) - - self.actionOpen = QtWidgets.QAction(self) - font = QtGui.QFont() - font.setFamily("Helvetica") - self.actionOpen.setFont(font) - self.actionOpen.setObjectName("actionOpen") - - self.actionClose = QtWidgets.QAction(self) - font = QtGui.QFont() - font.setFamily("Helvetica") - self.actionClose.setFont(font) - self.actionClose.setObjectName("actionClose") - - self.actionSave = QtWidgets.QAction(self) - self.actionSave.setEnabled(False) - font = QtGui.QFont() - font.setFamily("Helvetica") - self.actionSave.setFont(font) - self.actionSave.setObjectName("actionSave") - - self.actionSave_As = QtWidgets.QAction(self) - self.actionSave_As.setEnabled(False) - font = QtGui.QFont() - font.setFamily("Helvetica") - self.actionSave_As.setFont(font) - self.actionSave_As.setObjectName("actionSave_As") - - self.actionUndo = QtWidgets.QAction(self) - font = QtGui.QFont() - font.setFamily("Helvetica") - self.actionUndo.setFont(font) - self.actionUndo.setMenuRole(QtWidgets.QAction.TextHeuristicRole) - self.actionUndo.setObjectName("actionUndo") - self.actionRedo = QtWidgets.QAction(self) - font = QtGui.QFont() - font.setFamily("Helvetica") - self.actionRedo.setFont(font) - self.actionRedo.setObjectName("actionRedo") - self.actionCut = QtWidgets.QAction(self) - font = QtGui.QFont() - font.setFamily("Helvetica") - self.actionCut.setFont(font) - self.actionCut.setObjectName("actionCut") - self.actionCopy = QtWidgets.QAction(self) - font = QtGui.QFont() - font.setFamily("Helvetica") - self.actionCopy.setFont(font) - self.actionCopy.setObjectName("actionCopy") - self.actionPaste = QtWidgets.QAction(self) - font = QtGui.QFont() - font.setFamily("Helvetica") - self.actionPaste.setFont(font) - self.actionPaste.setObjectName("actionPaste") - self.actionDelete = QtWidgets.QAction(self) - font = QtGui.QFont() - font.setFamily("Helvetica") - self.actionDelete.setFont(font) - self.actionDelete.setObjectName("actionDelete") - self.actionSelect_All = QtWidgets.QAction(self) - font = QtGui.QFont() - font.setFamily("Helvetica") - self.actionSelect_All.setFont(font) - self.actionSelect_All.setObjectName("actionSelect_All") - self.actionPreferences = QtWidgets.QAction(self) - font = QtGui.QFont() - font.setFamily("Helvetica") - self.actionPreferences.setFont(font) - self.actionPreferences.setMenuRole(QtWidgets.QAction.PreferencesRole) - self.actionPreferences.setObjectName("actionPreferences") - - self.actionAbout_GeckoLoader = QtWidgets.QAction(self) - font = QtGui.QFont() - font.setFamily("Helvetica") - self.actionAbout_GeckoLoader.setFont(font) - self.actionAbout_GeckoLoader.setMenuRole(QtWidgets.QAction.AboutRole) - self.actionAbout_GeckoLoader.setObjectName("actionAbout_GeckoLoader") - - self.actionAbout_Qt = QtWidgets.QAction(self) - self.actionAbout_Qt.setStatusTip("") - font = QtGui.QFont() - font.setFamily("Helvetica") - self.actionAbout_Qt.setFont(font) - self.actionAbout_Qt.setMenuRole(QtWidgets.QAction.AboutQtRole) - self.actionAbout_Qt.setObjectName("actionAbout_Qt") - - self.actionCheck_Update = QtWidgets.QAction(self) - self.actionCheck_Update.setStatusTip("") - font = QtGui.QFont() - font.setFamily("Helvetica") - self.actionCheck_Update.setFont(font) - self.actionCheck_Update.setObjectName("actionCheck_Update") - - self.menuFile.addAction(self.actionOpen) - self.menuFile.addAction(self.actionClose) - self.menuFile.addSeparator() - self.menuFile.addAction(self.actionSave) - self.menuFile.addAction(self.actionSave_As) - - self.menuEdit.addAction(self.actionPreferences) - - self.menuHelp.addAction(self.actionAbout_GeckoLoader) - self.menuHelp.addAction(self.actionAbout_Qt) - self.menuHelp.addAction(self.actionCheck_Update) - - self.menubar.addAction(self.menuFile.menuAction()) - self.menubar.addAction(self.menuEdit.menuAction()) - self.menubar.addAction(self.menuHelp.menuAction()) - - #Statusbar - self.statusbar = QtWidgets.QStatusBar(self) - self.statusbar.setObjectName("statusbar") - self.setStatusBar(self.statusbar) - - self.retranslate_ui() - self.set_edit_fields() - - QtCore.QMetaObject.connectSlotsByName(self) - - def _lstrip_textboxes(self): - attributes = [item for item in vars(self) if not item.startswith('__')] - - for item in attributes: - item = getattr(self, item) - if isinstance(item, QtWidgets.QLineEdit): - strlength = len(item.text()) - cursorPos = item.cursorPosition() - item.setText(item.text().lstrip()) - item.setCursorPosition(cursorPos - (strlength - len(item.text()))) - elif isinstance(item, QtWidgets.QPlainTextEdit): - sliderPos = item.verticalScrollBar().sliderPosition() - item.setPlainText(item.toPlainText().lstrip()) - item.verticalScrollBar().setSliderPosition(sliderPos) - - def set_edit_fields(self): - self.filesLabel.setEnabled(True) - self.dolTextBox.setEnabled(True) - self.destTextBox.setEnabled(True) - self.optionsLabel.setEnabled(True) - self.allocLabel.setEnabled(True) - self.allocLineEdit.setEnabled(True) - self.handlerTypeLabel.setEnabled(True) - self.handlerTypeSelect.setEnabled(True) - self.hookTypeLabel.setEnabled(True) - self.hookTypeSelect.setEnabled(True) - self.txtCodesIncludeLabel.setEnabled(True) - self.txtCodesIncludeSelect.setEnabled(True) - self.exOptionsButton.setEnabled(True) - self.actionSave.setEnabled(True) - self.actionSave_As.setEnabled(True) - - self._lstrip_textboxes() - - if self.gctFileTextBox.text() != "": - self.gctFileTextBox.setEnabled(True) - self.gctFolderTextBox.setDisabled(True) - elif self.gctFolderTextBox.text() != "": - self.gctFileTextBox.setDisabled(True) - self.gctFolderTextBox.setEnabled(True) - else: - self.gctFileTextBox.setEnabled(True) - self.gctFolderTextBox.setEnabled(True) - - if self.dolTextBox.text().lower().endswith(".dol") and len(self.dolTextBox.text()) > 4: - self.compileButton.setEnabled(self.gctFileTextBox.text() != "" or self.gctFolderTextBox.text() != "") - else: - self.compileButton.setDisabled(True) - - def retranslate_ui(self): - self.setWindowTitle(QtWidgets.QApplication.translate("MainWindow", f"GeckoLoader {self.apiRevision} - untitled", None)) - self.menuFile.setTitle(QtWidgets.QApplication.translate("MainWindow", "&File", None)) - self.menuEdit.setTitle(QtWidgets.QApplication.translate("MainWindow", "&Edit", None)) - self.menuHelp.setTitle(QtWidgets.QApplication.translate("MainWindow", "&Help", None)) - self.actionOpen.setText(QtWidgets.QApplication.translate("MainWindow", "&Open Session...", None)) - self.actionOpen.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Open a session", None)) - self.actionOpen.setShortcut(QtWidgets.QApplication.translate("MainWindow", "Ctrl+O", None)) - self.actionClose.setText(QtWidgets.QApplication.translate("MainWindow", "&Close Session...", None)) - self.actionClose.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Close the current session", None)) - self.actionClose.setShortcut(QtWidgets.QApplication.translate("MainWindow", "Ctrl+Shift+C", None)) - self.actionSave.setText(QtWidgets.QApplication.translate("MainWindow", "&Save Session", None)) - self.actionSave.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Save the current session", None)) - self.actionSave.setShortcut(QtWidgets.QApplication.translate("MainWindow", "Ctrl+S", None)) - self.actionSave_As.setText(QtWidgets.QApplication.translate("MainWindow", "&Save Session As...", None)) - self.actionSave_As.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Save the current session to the specified location", None)) - self.actionSave_As.setShortcut(QtWidgets.QApplication.translate("MainWindow", "Ctrl+Shift+S", None)) - self.actionUndo.setText(QtWidgets.QApplication.translate("MainWindow", "Undo", None)) - self.actionUndo.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Undo the last action", None)) - self.actionUndo.setShortcut(QtWidgets.QApplication.translate("MainWindow", "Ctrl+Z", None)) - self.actionRedo.setText(QtWidgets.QApplication.translate("MainWindow", "Redo", None)) - self.actionRedo.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Redo the last action", None)) - self.actionRedo.setShortcut(QtWidgets.QApplication.translate("MainWindow", "Ctrl+Shift+Z", None)) - self.actionCut.setText(QtWidgets.QApplication.translate("MainWindow", "Cut", None)) - self.actionCut.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Cuts the selected text and places it on the clipboard", None)) - self.actionCut.setShortcut(QtWidgets.QApplication.translate("MainWindow", "Ctrl+X", None)) - self.actionCopy.setText(QtWidgets.QApplication.translate("MainWindow", "Copy", None)) - self.actionCopy.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Copies the selected text and places it on the clipboard", None)) - self.actionCopy.setShortcut(QtWidgets.QApplication.translate("MainWindow", "Ctrl+C", None)) - self.actionPaste.setText(QtWidgets.QApplication.translate("MainWindow", "Paste", None)) - self.actionPaste.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Paste the contents of the clipboard", None)) - self.actionPaste.setShortcut(QtWidgets.QApplication.translate("MainWindow", "Ctrl+V", None)) - self.actionDelete.setText(QtWidgets.QApplication.translate("MainWindow", "Delete", None)) - self.actionDelete.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Deletes the selected text", None)) - self.actionSelect_All.setText(QtWidgets.QApplication.translate("MainWindow", "Select All", None)) - self.actionSelect_All.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Select all of the text", None)) - self.actionSelect_All.setShortcut(QtWidgets.QApplication.translate("MainWindow", "Ctrl+A", None)) - self.actionPreferences.setText(QtWidgets.QApplication.translate("MainWindow", "&Preferences...", None)) - self.actionPreferences.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Open the application preferences dialog", None)) - self.actionAbout_GeckoLoader.setText(QtWidgets.QApplication.translate("MainWindow", "About &GeckoLoader...", None)) - self.actionAbout_Qt.setText(QtWidgets.QApplication.translate("MainWindow", "About &Qt...", None)) - self.actionCheck_Update.setText(QtWidgets.QApplication.translate("MainWindow", "&Check Update", None)) - - self.filesLabel.setText(QtWidgets.QApplication.translate("MainWindow", "Files", None)) - - self.dolButton.setText(QtWidgets.QApplication.translate("MainWindow", "Open DOL", None)) - self.gctFileButton.setText(QtWidgets.QApplication.translate("MainWindow", "Open Codes", None)) - self.orFolderLabel.setText(QtWidgets.QApplication.translate("MainWindow", "-"*40 + "OR" + "-"*40, None)) - self.gctFolderButton.setText(QtWidgets.QApplication.translate("MainWindow", "Open Folder", None)) - self.destButton.setText(QtWidgets.QApplication.translate("MainWindow", "Destination", None)) - - self.optionsLabel.setText(QtWidgets.QApplication.translate("MainWindow", "Options", None)) - - self.allocLabel.setText(QtWidgets.QApplication.translate("MainWindow", "Allocation", None)) - self.allocLineEdit.setPlaceholderText(QtWidgets.QApplication.translate("MainWindow", "AUTO", None)) - - self.handlerTypeLabel.setText(QtWidgets.QApplication.translate("MainWindow", "Codehandler", None)) - self.handlerTypeSelect.setItemText(0, QtWidgets.QApplication.translate("Dialog", "FULL", None)) - self.handlerTypeSelect.setItemText(1, QtWidgets.QApplication.translate("Dialog", "MINI", None)) - - self.hookTypeLabel.setText(QtWidgets.QApplication.translate("MainWindow", "Code Hook", None)) - self.hookTypeSelect.setItemText(0, QtWidgets.QApplication.translate("Dialog", "VI", None)) - self.hookTypeSelect.setItemText(1, QtWidgets.QApplication.translate("Dialog", "GX", None)) - self.hookTypeSelect.setItemText(2, QtWidgets.QApplication.translate("Dialog", "PAD", None)) - - self.txtCodesIncludeLabel.setText(QtWidgets.QApplication.translate("MainWindow", "Include Codes", None)) - self.txtCodesIncludeSelect.setItemText(0, QtWidgets.QApplication.translate("Dialog", "ACTIVE", None)) - self.txtCodesIncludeSelect.setItemText(1, QtWidgets.QApplication.translate("Dialog", "ALL", None)) - - self.exOptionsButton.setText(QtWidgets.QApplication.translate("MainWindow", "Advanced Settings", None)) - - self.compileButton.setText(QtWidgets.QApplication.translate("MainWindow", "RUN", None)) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f5f9aed --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +bs4 +colorama +dolreader +geckolibs +PySide6 \ No newline at end of file