1
0
Fork 0

Create framework and implementation of write types

This commit is contained in:
JoshuaMK 2021-04-20 01:10:30 -05:00
parent a4b1d9ad1a
commit 59aa638191

439
kernel.py
View file

@ -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"(?<![$\*])[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]
except IndexError:
continue
geckoCodes += geckoLine.replace(" ", "").strip()
gct += geckoLine.replace(" ", "").strip()
return geckoCodes
return gct
@staticmethod
def encrypt_key(key: int) -> 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)