diff --git a/kernel.py b/kernel.py index 9e0f8e7..be8ab8d 100644 --- a/kernel.py +++ b/kernel.py @@ -3,8 +3,10 @@ import random 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 @@ except ImportError as IE: 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) + 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 size(self): - return len(self.codeList.getbuffer()) + def byteSize(self): + return sum([len(c) for c in self.codelist]) - @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 + @property + def groupSize(self): + return (self.byteSize >> 3) - elif (codetype.startswith(b"\x08") or codetype.startswith(b"\x09") - or codetype.startswith(b"\x18") or codetype.startswith(b"\x18")): - return 0x16 + @property + def virtualSize(self): + return len(self.codelist) - 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) + def add_geckocode(self, code: GeckoCode): + self.codelist.append(code) - 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) + def remove_geckocode(self, code: GeckoCode): + self.codelist.remove(code) - elif codetype.startswith(b"\xF6"): - return 0x8 + (int.from_bytes(info[:4], byteorder="big", signed=False) << 3) + 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) - else: - return 0x8 + 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 - 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) + def apply_to_dol(self, dol: DolFile): + for code in self.codelist: + if code.is_preprocess_allowed(): + code.apply(dol) + self.remove_geckocode(code) - 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 CodeHandler(object): 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 @@ class CodeHandler(object): 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 @@ class CodeHandler(object): 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 @@ class CodeHandler(object): 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 @@ class CodeHandler(object): 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 @@ class CodeHandler(object): encodeType = result["encoding"] with geckoText.open("r", encoding=encodeType) as gecko: - geckoCodes = "" + gct = "" state = None for line in gecko.readlines(): @@ -267,24 +204,27 @@ class CodeHandler(object): 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 @@ class CodeHandler(object): 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 @@ class CodeHandler(object): 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 @@ class CodeHandler(object): 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 @@ class KernelLoader(object): 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 @@ class KernelLoader(object): 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 @@ class KernelLoader(object): 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 @@ class KernelLoader(object): 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 @@ class KernelLoader(object): 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 @@ class KernelLoader(object): 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 @@ class KernelLoader(object): 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 @@ class KernelLoader(object): 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 @@ class KernelLoader(object): 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)