Create framework and implementation of write types
This commit is contained in:
parent
a4b1d9ad1a
commit
59aa638191
1 changed files with 205 additions and 234 deletions
439
kernel.py
439
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"(?<![$\*])[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)
|
||||
|
|
Reference in a new issue