Fixed stacking bug with kernel
This commit is contained in:
parent
ea8e76dae9
commit
8e41d1b8c8
3 changed files with 181 additions and 169 deletions
75
dolreader.py
75
dolreader.py
|
@ -13,17 +13,18 @@ class DolFile(object):
|
||||||
Text = 0
|
Text = 0
|
||||||
Data = 1
|
Data = 1
|
||||||
|
|
||||||
|
maxTextSections = 7
|
||||||
|
maxDataSections = 11
|
||||||
|
offsetInfoLoc = 0
|
||||||
|
addressInfoLoc = 0x48
|
||||||
|
sizeInfoLoc = 0x90
|
||||||
|
bssInfoLoc = 0xD8
|
||||||
|
entryInfoLoc = 0xE0
|
||||||
|
|
||||||
def __init__(self, f=None):
|
def __init__(self, f=None):
|
||||||
self.fileOffsetLoc = 0
|
|
||||||
self.fileAddressLoc = 0x48
|
|
||||||
self.fileSizeLoc = 0x90
|
|
||||||
self.fileBssInfoLoc = 0xD8
|
|
||||||
self.fileEntryLoc = 0xE0
|
|
||||||
|
|
||||||
self.textSections = []
|
self.textSections = []
|
||||||
self.dataSections = []
|
self.dataSections = []
|
||||||
self.maxTextSections = 7
|
|
||||||
self.maxDataSections = 11
|
|
||||||
|
|
||||||
self.bssAddress = 0
|
self.bssAddress = 0
|
||||||
self.bssSize = 0
|
self.bssSize = 0
|
||||||
|
@ -32,27 +33,27 @@ class DolFile(object):
|
||||||
if f is None: return
|
if f is None: return
|
||||||
|
|
||||||
# Read text and data section addresses and sizes
|
# Read text and data section addresses and sizes
|
||||||
for i in range(self.maxTextSections + self.maxDataSections):
|
for i in range(DolFile.maxTextSections + DolFile.maxDataSections):
|
||||||
f.seek(self.fileOffsetLoc + (i << 2))
|
f.seek(DolFile.offsetInfoLoc + (i << 2))
|
||||||
offset = read_uint32(f)
|
offset = read_uint32(f)
|
||||||
f.seek(self.fileAddressLoc + (i << 2))
|
f.seek(DolFile.addressInfoLoc + (i << 2))
|
||||||
address = read_uint32(f)
|
address = read_uint32(f)
|
||||||
f.seek(self.fileSizeLoc + (i << 2))
|
f.seek(DolFile.sizeInfoLoc + (i << 2))
|
||||||
size = read_uint32(f)
|
size = read_uint32(f)
|
||||||
|
|
||||||
if offset >= 0x100:
|
if offset >= 0x100:
|
||||||
f.seek(offset)
|
f.seek(offset)
|
||||||
data = BytesIO(f.read(size))
|
data = BytesIO(f.read(size))
|
||||||
if i < self.maxTextSections:
|
if i < DolFile.maxTextSections:
|
||||||
self.textSections.append([offset, address, size, data, DolFile.SectionType.Text])
|
self.textSections.append([offset, address, size, data, DolFile.SectionType.Text])
|
||||||
else:
|
else:
|
||||||
self.dataSections.append([offset, address, size, data, DolFile.SectionType.Data])
|
self.dataSections.append([offset, address, size, data, DolFile.SectionType.Data])
|
||||||
|
|
||||||
f.seek(self.fileBssInfoLoc)
|
f.seek(DolFile.bssInfoLoc)
|
||||||
self.bssAddress = read_uint32(f)
|
self.bssAddress = read_uint32(f)
|
||||||
self.bssSize = read_uint32(f)
|
self.bssSize = read_uint32(f)
|
||||||
|
|
||||||
f.seek(self.fileEntryLoc)
|
f.seek(DolFile.entryInfoLoc)
|
||||||
self.entryPoint = read_uint32(f)
|
self.entryPoint = read_uint32(f)
|
||||||
|
|
||||||
self._currLogicAddr = self.get_first_section()[1]
|
self._currLogicAddr = self.get_first_section()[1]
|
||||||
|
@ -186,39 +187,42 @@ class DolFile(object):
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
f.write(b"\x00" * self.get_full_size())
|
f.write(b"\x00" * self.get_full_size())
|
||||||
|
|
||||||
for i in range(self.maxTextSections + self.maxDataSections):
|
for i in range(DolFile.maxTextSections + DolFile.maxDataSections):
|
||||||
if i < self.maxTextSections:
|
if i < DolFile.maxTextSections:
|
||||||
if i < len(self.textSections):
|
if i < len(self.textSections):
|
||||||
offset, address, size, data, _ = self.textSections[i]
|
offset, address, size, data, _ = self.textSections[i]
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
if i - self.maxTextSections < len(self.dataSections):
|
if i - DolFile.maxTextSections < len(self.dataSections):
|
||||||
offset, address, size, data, _ = self.dataSections[i - self.maxTextSections]
|
offset, address, size, data, _ = self.dataSections[i - DolFile.maxTextSections]
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
f.seek(self.fileOffsetLoc + (i * 4))
|
f.seek(DolFile.offsetInfoLoc + (i << 2))
|
||||||
write_uint32(f, offset) #offset in file
|
write_uint32(f, offset) #offset in file
|
||||||
f.seek(self.fileAddressLoc + (i * 4))
|
f.seek(DolFile.addressInfoLoc + (i << 2))
|
||||||
write_uint32(f, address) #game address
|
write_uint32(f, address) #game address
|
||||||
f.seek(self.fileSizeLoc + (i * 4))
|
f.seek(DolFile.sizeInfoLoc + (i << 2))
|
||||||
write_uint32(f, size) #size in file
|
write_uint32(f, size) #size in file
|
||||||
|
|
||||||
f.seek(offset)
|
f.seek(offset)
|
||||||
f.write(data.getbuffer())
|
f.write(data.getbuffer())
|
||||||
|
|
||||||
f.seek(self.fileBssInfoLoc)
|
f.seek(DolFile.bssInfoLoc)
|
||||||
write_uint32(f, self.bssAddress)
|
write_uint32(f, self.bssAddress)
|
||||||
write_uint32(f, self.bssSize)
|
write_uint32(f, self.bssSize)
|
||||||
|
|
||||||
f.seek(self.fileEntryLoc)
|
f.seek(DolFile.entryInfoLoc)
|
||||||
write_uint32(f, self.entryPoint)
|
write_uint32(f, self.entryPoint)
|
||||||
align_byte_size(f, 256)
|
align_byte_size(f, 256)
|
||||||
|
|
||||||
def get_full_size(self) -> int:
|
def get_full_size(self) -> int:
|
||||||
offset, _, size, _, _ = self.get_final_section()
|
try:
|
||||||
return (offset + size + 255) & -256
|
offset, _, size, _, _ = self.get_final_section()
|
||||||
|
return (offset + size + 255) & -256
|
||||||
|
except IndexError:
|
||||||
|
return 0x100
|
||||||
|
|
||||||
def get_section_size(self, index: int, section: SectionType) -> int:
|
def get_section_size(self, index: int, section: SectionType) -> int:
|
||||||
""" Return the current size of the specified section\n
|
""" Return the current size of the specified section\n
|
||||||
|
@ -234,8 +238,8 @@ class DolFile(object):
|
||||||
""" Follows the list format: [tuple(<Bytes>Data, <Int>GameAddress or None), tuple(<Bytes>Data... """
|
""" Follows the list format: [tuple(<Bytes>Data, <Int>GameAddress or None), tuple(<Bytes>Data... """
|
||||||
|
|
||||||
for i, dataSet in enumerate(sectionsList):
|
for i, dataSet in enumerate(sectionsList):
|
||||||
if len(self.textSections) >= self.maxTextSections:
|
if len(self.textSections) >= DolFile.maxTextSections:
|
||||||
raise SectionCountFullError(f"Exceeded max text section limit of {self.maxTextSections}")
|
raise SectionCountFullError(f"Exceeded max text section limit of {DolFile.maxTextSections}")
|
||||||
|
|
||||||
fOffset, _, fSize, _, _ = self.get_final_section()
|
fOffset, _, fSize, _, _ = self.get_final_section()
|
||||||
_, pAddress, pSize, _, _ = self.textSections[len(self.textSections) - 1]
|
_, pAddress, pSize, _, _ = self.textSections[len(self.textSections) - 1]
|
||||||
|
@ -267,8 +271,8 @@ class DolFile(object):
|
||||||
""" Follows the list format: [tuple(<Bytes>Data, <Int>GameAddress or None), tuple(<Bytes>Data... """
|
""" Follows the list format: [tuple(<Bytes>Data, <Int>GameAddress or None), tuple(<Bytes>Data... """
|
||||||
|
|
||||||
for i, dataSet in enumerate(sectionsList):
|
for i, dataSet in enumerate(sectionsList):
|
||||||
if len(self.dataSections) >= self.maxDataSections:
|
if len(self.dataSections) >= DolFile.maxDataSections:
|
||||||
raise SectionCountFullError(f"Exceeded max data section limit of {self.maxDataSections}")
|
raise SectionCountFullError(f"Exceeded max data section limit of {DolFile.maxDataSections}")
|
||||||
|
|
||||||
fOffset, _, fSize, _, _ = self.get_final_section()
|
fOffset, _, fSize, _, _ = self.get_final_section()
|
||||||
_, pAddress, pSize, _, _ = self.dataSections[len(self.dataSections) - 1]
|
_, pAddress, pSize, _, _ = self.dataSections[len(self.dataSections) - 1]
|
||||||
|
@ -341,22 +345,25 @@ class DolFile(object):
|
||||||
while (char := self.read(1)) != b"\x00":
|
while (char := self.read(1)) != b"\x00":
|
||||||
try:
|
try:
|
||||||
string += char.decode(encoding)
|
string += char.decode(encoding)
|
||||||
|
length += 1
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
print(f"{char} at pos {length}, (address 0x{addr + length:08X}) is not a valid utf-8 character")
|
print(f"{char} at pos {length}, (address 0x{addr + length:08X}) is not a valid utf-8 character")
|
||||||
return ""
|
return ""
|
||||||
if length > maxlen and maxlen != 0:
|
if length > (maxlen-1) and maxlen != 0:
|
||||||
break
|
return string
|
||||||
|
|
||||||
return string
|
return string
|
||||||
|
|
||||||
def print_info(self):
|
def print_info(self):
|
||||||
print("|---DOL INFO---|".center(20, " "))
|
print("")
|
||||||
|
print("|-- DOL INFO --|".center(20, " "))
|
||||||
|
print("")
|
||||||
|
|
||||||
for i, (offset, addr, size, _) in enumerate(self.textSections):
|
for i, (offset, addr, size, _, _) in enumerate(self.textSections):
|
||||||
header = f"| Text section {i} |"
|
header = f"| Text section {i} |"
|
||||||
print("-"*len(header) + "\n" + header + "\n" + "-"*len(header) + f"\n File offset:\t0x{offset:X}\n Virtual addr:\t0x{addr:X}\n Size:\t\t0x{size:X}\n")
|
print("-"*len(header) + "\n" + header + "\n" + "-"*len(header) + f"\n File offset:\t0x{offset:X}\n Virtual addr:\t0x{addr:X}\n Size:\t\t0x{size:X}\n")
|
||||||
|
|
||||||
for i, (offset, addr, size, _) in enumerate(self.dataSections):
|
for i, (offset, addr, size, _, _) in enumerate(self.dataSections):
|
||||||
header = f"| Data section {i} |"
|
header = f"| Data section {i} |"
|
||||||
print("-"*len(header) + "\n" + header + "\n" + "-"*len(header) + f"\n File offset:\t0x{offset:X}\n Virtual addr:\t0x{addr:X}\n Size:\t\t0x{size:X}\n")
|
print("-"*len(header) + "\n" + header + "\n" + "-"*len(header) + f"\n File offset:\t0x{offset:X}\n Virtual addr:\t0x{addr:X}\n Size:\t\t0x{size:X}\n")
|
||||||
|
|
||||||
|
|
268
kernel.py
268
kernel.py
|
@ -30,7 +30,7 @@ class InvalidGeckoCodeError(Exception): pass
|
||||||
|
|
||||||
class GCT(object):
|
class GCT(object):
|
||||||
|
|
||||||
def __init__(self, f: open):
|
def __init__(self, f):
|
||||||
self.codeList = BytesIO(f.read())
|
self.codeList = BytesIO(f.read())
|
||||||
self.rawLineCount = tools.stream_size(self.codeList) >> 3
|
self.rawLineCount = tools.stream_size(self.codeList) >> 3
|
||||||
self.lineCount = self.rawLineCount - 2
|
self.lineCount = self.rawLineCount - 2
|
||||||
|
@ -38,7 +38,7 @@ class GCT(object):
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def determine_codelength(codetype, info) -> int:
|
def determine_codelength(codetype, info: bytes) -> int:
|
||||||
if codetype.startswith(b'\x06'):
|
if codetype.startswith(b'\x06'):
|
||||||
bytelength = int.from_bytes(info, byteorder='big', signed=False)
|
bytelength = int.from_bytes(info, byteorder='big', signed=False)
|
||||||
padding = get_alignment(bytelength, 8)
|
padding = get_alignment(bytelength, 8)
|
||||||
|
@ -65,12 +65,11 @@ class GCT(object):
|
||||||
return 0x8
|
return 0x8
|
||||||
|
|
||||||
def optimize_codelist(self, dolFile: DolFile):
|
def optimize_codelist(self, dolFile: DolFile):
|
||||||
codelist = b''
|
codelist = b'\x00\xD0\xC0\xDE'*2
|
||||||
codetype = b'temp'
|
|
||||||
skipcodes = 0
|
skipcodes = 0
|
||||||
|
|
||||||
while codetype:
|
self.codeList.seek(8)
|
||||||
codetype = self.codeList.read(4)
|
while codetype := self.codeList.read(4):
|
||||||
info = self.codeList.read(4)
|
info = self.codeList.read(4)
|
||||||
address = 0x80000000 | (int.from_bytes(codetype, byteorder='big', signed=False) & 0x01FFFFFF)
|
address = 0x80000000 | (int.from_bytes(codetype, byteorder='big', signed=False) & 0x01FFFFFF)
|
||||||
try:
|
try:
|
||||||
|
@ -167,7 +166,8 @@ class GCT(object):
|
||||||
codelist += b'\xF0\x00\x00\x00\x00\x00\x00\x00'
|
codelist += b'\xF0\x00\x00\x00\x00\x00\x00\x00'
|
||||||
break
|
break
|
||||||
|
|
||||||
self.codeList.seek(-8, 1)
|
self.codeList.seek(-8, 1)*5
|
||||||
|
|
||||||
length = GCT.determine_codelength(codetype, info)
|
length = GCT.determine_codelength(codetype, info)
|
||||||
while length > 0:
|
while length > 0:
|
||||||
codelist += self.codeList.read(1)
|
codelist += self.codeList.read(1)
|
||||||
|
@ -218,7 +218,7 @@ class CodeHandler(object):
|
||||||
|
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
|
|
||||||
def gecko_self(self, geckoText) -> str:
|
def parse_input(self, geckoText) -> str:
|
||||||
with open(r'{}'.format(geckoText), 'rb') as gecko:
|
with open(r'{}'.format(geckoText), 'rb') as gecko:
|
||||||
result = chardet.detect(gecko.read())
|
result = chardet.detect(gecko.read())
|
||||||
encodeType = result['encoding']
|
encodeType = result['encoding']
|
||||||
|
@ -263,7 +263,7 @@ class CodeHandler(object):
|
||||||
b1 ^= b2
|
b1 ^= b2
|
||||||
return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4
|
return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4
|
||||||
|
|
||||||
def encrypt_data(self, key: int):
|
def encrypt_codes(self, key: int):
|
||||||
self.geckoCodes.codeList.seek(0)
|
self.geckoCodes.codeList.seek(0)
|
||||||
i = 0
|
i = 0
|
||||||
while True:
|
while True:
|
||||||
|
@ -304,7 +304,7 @@ class CodeHandler(object):
|
||||||
def set_variables(self, dolFile: DolFile):
|
def set_variables(self, dolFile: DolFile):
|
||||||
varOffset = self.find_variable_data(b'\x00\xDE\xDE\xDE')
|
varOffset = self.find_variable_data(b'\x00\xDE\xDE\xDE')
|
||||||
if varOffset is None:
|
if varOffset is None:
|
||||||
raise RuntimeError(tools.color_text("Variable codehandler data not found", 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.set_hook_instruction(dolFile, self.hookAddress, varOffset, 0)
|
||||||
|
|
||||||
|
@ -366,8 +366,6 @@ class KernelLoader(object):
|
||||||
_key = random.randrange(0x100000000)
|
_key = random.randrange(0x100000000)
|
||||||
self._rawData.seek(0)
|
self._rawData.seek(0)
|
||||||
|
|
||||||
sample = self._rawData.read(4)
|
|
||||||
|
|
||||||
while sample := self._rawData.read(4):
|
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)
|
self._rawData.seek(-4, 1)
|
||||||
|
@ -420,7 +418,7 @@ class KernelLoader(object):
|
||||||
self.set_variables(initpoint, _lowerAddr)
|
self.set_variables(initpoint, _lowerAddr)
|
||||||
|
|
||||||
if self.encrypt:
|
if self.encrypt:
|
||||||
codeHandler.encrypt_data(_key)
|
codeHandler.encrypt_codes(_key)
|
||||||
|
|
||||||
|
|
||||||
def patch_arena(self, codeHandler: CodeHandler, dolFile: DolFile):
|
def patch_arena(self, codeHandler: CodeHandler, dolFile: DolFile):
|
||||||
|
@ -438,8 +436,8 @@ class KernelLoader(object):
|
||||||
try:
|
try:
|
||||||
dolFile.append_data_sections([(_kernelData, self.initAddress)])
|
dolFile.append_data_sections([(_kernelData, self.initAddress)])
|
||||||
except SectionCountFullError:
|
except SectionCountFullError:
|
||||||
self.error(tools.color_text('There are no unused sections left for GeckoLoader to use!', 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
|
dolFile.entryPoint = self.initAddress
|
||||||
|
|
||||||
def patch_legacy(self, codeHandler: CodeHandler, dolFile: DolFile):
|
def patch_legacy(self, codeHandler: CodeHandler, dolFile: DolFile):
|
||||||
|
@ -454,12 +452,12 @@ class KernelLoader(object):
|
||||||
try:
|
try:
|
||||||
dolFile.append_data_sections([(_handlerData, codeHandler.initAddress)])
|
dolFile.append_data_sections([(_handlerData, codeHandler.initAddress)])
|
||||||
except SectionCountFullError:
|
except SectionCountFullError:
|
||||||
self.error(tools.color_text('There are no unused sections left for GeckoLoader to use!', defaultColor=tools.TREDLIT))
|
self.error(tools.color_text('There are no unused sections left for GeckoLoader to use!\n', defaultColor=tools.TREDLIT))
|
||||||
|
|
||||||
def protect_game(self, codeHandler: CodeHandler):
|
def protect_game(self, codeHandler: CodeHandler):
|
||||||
_oldpos = codeHandler.geckoCodes.codeList.tell()
|
_oldpos = codeHandler.geckoCodes.codeList.tell()
|
||||||
|
|
||||||
protectdata = [b'\xC0\x00\x00\x00\x00\x00\x00\x17',
|
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'\x90\x01\x00\x08\xBC\x61\x00\x0C',
|
||||||
b'\x48\x00\x00\x0D\x00\xD0\xC0\xDE',
|
b'\x48\x00\x00\x0D\x00\xD0\xC0\xDE',
|
||||||
|
@ -482,7 +480,7 @@ class KernelLoader(object):
|
||||||
b'\x9C\xC4\x00\x01\x42\x00\xFF\xF0',
|
b'\x9C\xC4\x00\x01\x42\x00\xFF\xF0',
|
||||||
b'\xB8\x61\x00\x0C\x80\x01\x00\x08',
|
b'\xB8\x61\x00\x0C\x80\x01\x00\x08',
|
||||||
b'\x38\x21\x00\x90\x7C\x08\x03\xA6',
|
b'\x38\x21\x00\x90\x7C\x08\x03\xA6',
|
||||||
b'\x4E\x80\x00\x20\x00\x00\x00\x00']
|
b'\x4E\x80\x00\x20\x00\x00\x00\x00')
|
||||||
|
|
||||||
codeHandler.geckoCodes.codeList.seek(-8, 2)
|
codeHandler.geckoCodes.codeList.seek(-8, 2)
|
||||||
for chunk in protectdata:
|
for chunk in protectdata:
|
||||||
|
@ -494,135 +492,137 @@ class KernelLoader(object):
|
||||||
|
|
||||||
@timer
|
@timer
|
||||||
def build(self, gctFile, dolFile: DolFile, codeHandler: CodeHandler, tmpdir, dump):
|
def build(self, gctFile, dolFile: DolFile, codeHandler: CodeHandler, tmpdir, dump):
|
||||||
with open(dump, 'wb+') as final:
|
oldStart = dolFile.entryPoint
|
||||||
|
|
||||||
if dolFile.get_full_size() < 0x100:
|
'''Initialize our codes'''
|
||||||
self.error(tools.color_text('DOL header is corrupted. Please provide a clean file\n', defaultColor=tools.TREDLIT), exit=False)
|
|
||||||
return
|
|
||||||
|
|
||||||
oldStart = dolFile.entryPoint
|
|
||||||
|
|
||||||
'''Initialize our codes'''
|
if '.' in gctFile:
|
||||||
|
if os.path.splitext(gctFile)[1].lower() == '.txt':
|
||||||
foundData = False
|
|
||||||
|
|
||||||
if '.' in gctFile:
|
|
||||||
if os.path.splitext(gctFile)[1].lower() == '.txt':
|
|
||||||
with open(os.path.join(tmpdir, 'gct.bin'), 'wb+') as temp:
|
|
||||||
temp.write(bytes.fromhex('00D0C0DE'*2 + codeHandler.gecko_self(gctFile) + 'F000000000000000'))
|
|
||||||
temp.seek(0)
|
|
||||||
codeHandler.geckoCodes = GCT(temp)
|
|
||||||
foundData = True
|
|
||||||
elif os.path.splitext(gctFile)[1].lower() == '.gct':
|
|
||||||
with open(gctFile, 'rb') as gct:
|
|
||||||
codeHandler.geckoCodes = GCT(gct)
|
|
||||||
foundData = True
|
|
||||||
|
|
||||||
else:
|
|
||||||
with open(os.path.join(tmpdir, 'gct.bin'), 'wb+') as temp:
|
with open(os.path.join(tmpdir, 'gct.bin'), 'wb+') as temp:
|
||||||
temp.write(b'\x00\xD0\xC0\xDE'*2)
|
temp.write(bytes.fromhex('00D0C0DE'*2 + codeHandler.parse_input(gctFile) + 'F000000000000000'))
|
||||||
|
|
||||||
for file in os.listdir(gctFile):
|
|
||||||
if os.path.isfile(os.path.join(gctFile, file)):
|
|
||||||
if os.path.splitext(file)[1].lower() == '.txt':
|
|
||||||
temp.write(bytes.fromhex(codeHandler.gecko_self(os.path.join(gctFile, file))))
|
|
||||||
foundData = True
|
|
||||||
elif os.path.splitext(file)[1].lower() == '.gct':
|
|
||||||
with open(os.path.join(gctFile, file), 'rb') as gct:
|
|
||||||
temp.write(gct.read()[8:-8])
|
|
||||||
foundData = True
|
|
||||||
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)
|
temp.seek(0)
|
||||||
codeHandler.geckoCodes = GCT(temp)
|
codeHandler.geckoCodes = GCT(temp)
|
||||||
|
elif os.path.splitext(gctFile)[1].lower() == '.gct':
|
||||||
|
with open(gctFile, 'rb') as gct:
|
||||||
|
codeHandler.geckoCodes = GCT(gct)
|
||||||
|
else:
|
||||||
|
with open(os.path.join(tmpdir, 'gct.bin'), 'wb+') as temp:
|
||||||
|
temp.write(b'\x00\xD0\xC0\xDE'*2)
|
||||||
|
|
||||||
if not foundData:
|
for file in os.listdir(gctFile):
|
||||||
self.error(tools.color_text('No valid gecko code file found\n', defaultColor=tools.TREDLIT), exit=False)
|
if os.path.isfile(os.path.join(gctFile, file)):
|
||||||
return
|
if os.path.splitext(file)[1].lower() == '.txt':
|
||||||
|
temp.write(bytes.fromhex(codeHandler.parse_input(os.path.join(gctFile, file))))
|
||||||
if self.protect and self.patchJob == "ARENA":
|
elif os.path.splitext(file)[1].lower() == '.gct':
|
||||||
self.protect_game(codeHandler)
|
with open(os.path.join(gctFile, file), 'rb') as gct:
|
||||||
|
temp.write(gct.read()[8:-8])
|
||||||
if self.patchJob == 'AUTO':
|
else:
|
||||||
if codeHandler.initAddress + codeHandler.handlerLength + codeHandler.geckoCodes.size > 0x80002FFF:
|
print(tools.color_text(f' :: HINT: {file} is not a .txt or .gct file', defaultColor=tools.TYELLOWLIT))
|
||||||
self.patchJob = 'ARENA'
|
|
||||||
else:
|
|
||||||
self.patchJob = 'LEGACY'
|
|
||||||
|
|
||||||
'''Get entrypoint (or BSS midpoint) for insert'''
|
|
||||||
|
|
||||||
if self.initAddress:
|
|
||||||
try:
|
|
||||||
dolFile.resolve_address(self.initAddress)
|
|
||||||
print(tools.color_text(f'\n :: WARNING: Init address specified for GeckoLoader (0x{self.initAddress:X}) clobbers existing dol sections', defaultColor=tools.TYELLOW))
|
|
||||||
except RuntimeError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.initAddress = dolFile.seek_nearest_unmapped(dolFile.bssAddress, len(self._rawData.getbuffer()) + codeHandler.handlerLength + codeHandler.geckoCodes.size)
|
|
||||||
self._rawData.seek(0)
|
|
||||||
|
|
||||||
'''Is insertion legacy?'''
|
|
||||||
|
|
||||||
if codeHandler.geckoCodes.size <= 0x10:
|
|
||||||
dolFile.save(final)
|
|
||||||
if self.verbosity >= 1:
|
|
||||||
print(tools.color_text('\n :: All codes have been successfully pre patched', defaultColor=tools.TGREENLIT))
|
|
||||||
return
|
|
||||||
|
|
||||||
if self.patchJob == 'LEGACY':
|
|
||||||
codeHandler.allocation = 0x80003000 - (codeHandler.initAddress + codeHandler.handlerLength)
|
|
||||||
codeHandler.set_variables(dolFile)
|
|
||||||
hooked = determine_codehook(dolFile, codeHandler, True)
|
|
||||||
self.patch_legacy(codeHandler, dolFile)
|
|
||||||
legacy = True
|
|
||||||
else:
|
|
||||||
hooked = determine_codehook(dolFile, codeHandler, False)
|
|
||||||
self.patch_arena(codeHandler, dolFile)
|
|
||||||
legacy = False
|
|
||||||
|
|
||||||
if not hooked:
|
temp.write(b'\xF0\x00\x00\x00\x00\x00\x00\x00')
|
||||||
self.error(tools.color_text('Failed to find a hook address. Try using option --codehook to use your own address\n', defaultColor=tools.TREDLIT))
|
temp.seek(0)
|
||||||
elif codeHandler.allocation < codeHandler.geckoCodes.size:
|
codeHandler.geckoCodes = GCT(temp)
|
||||||
self.error(tools.color_text('\n :: Error: Allocated codespace was smaller than the given codelist.\n', defaultColor=tools.TYELLOW))
|
|
||||||
|
if codeHandler.geckoCodes is None:
|
||||||
dolFile.save(final)
|
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 and self.patchJob == "ARENA":
|
||||||
|
self.protect_game(codeHandler)
|
||||||
|
|
||||||
|
if self.patchJob == 'AUTO':
|
||||||
|
if codeHandler.initAddress + codeHandler.handlerLength + codeHandler.geckoCodes.size > 0x80002FFF:
|
||||||
|
self.patchJob = 'ARENA'
|
||||||
|
else:
|
||||||
|
self.patchJob = 'LEGACY'
|
||||||
|
|
||||||
|
'''Get entrypoint (or BSS midpoint) for insert'''
|
||||||
|
|
||||||
|
if self.initAddress:
|
||||||
|
try:
|
||||||
|
dolFile.resolve_address(self.initAddress)
|
||||||
|
print(tools.color_text(f'\n :: WARNING: Init address specified for GeckoLoader (0x{self.initAddress:X}) clobbers existing dol sections', defaultColor=tools.TYELLOW))
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.initAddress = dolFile.seek_nearest_unmapped(dolFile.bssAddress, len(self._rawData.getbuffer()) + codeHandler.handlerLength + codeHandler.geckoCodes.size)
|
||||||
|
self._rawData.seek(0)
|
||||||
|
|
||||||
|
if codeHandler.optimizeList:
|
||||||
|
codeHandler.geckoCodes.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':
|
||||||
|
with open(dump, 'wb') as final:
|
||||||
|
dolFile.save(final)
|
||||||
|
|
||||||
if self.quiet:
|
if self.quiet:
|
||||||
return
|
return
|
||||||
|
if self.verbosity >= 3:
|
||||||
|
dolFile.print_info()
|
||||||
|
print('-'*64)
|
||||||
|
if self.verbosity >= 1:
|
||||||
|
print(tools.color_text('\n :: All codes have been successfully pre patched', defaultColor=tools.TGREENLIT))
|
||||||
|
|
||||||
if codeHandler.allocation > 0x70000:
|
return
|
||||||
print(tools.color_text(f'\n :: WARNING: Allocations beyond 0x70000 will crash certain games. You allocated 0x{codeHandler.allocation:X}', defaultColor=tools.TYELLOW))
|
|
||||||
|
if self.patchJob == 'LEGACY':
|
||||||
|
codeHandler.allocation = 0x80003000 - (codeHandler.initAddress + codeHandler.handlerLength)
|
||||||
|
codeHandler.set_variables(dolFile)
|
||||||
|
hooked = determine_codehook(dolFile, codeHandler, True)
|
||||||
|
self.patch_legacy(codeHandler, dolFile)
|
||||||
|
legacy = True
|
||||||
|
else:
|
||||||
|
hooked = determine_codehook(dolFile, codeHandler, False)
|
||||||
|
self.patch_arena(codeHandler, dolFile)
|
||||||
|
legacy = False
|
||||||
|
|
||||||
|
if not hooked:
|
||||||
|
self.error(tools.color_text('Failed to find a hook address. Try using option --codehook to use your own address\n', defaultColor=tools.TREDLIT))
|
||||||
|
elif codeHandler.allocation < codeHandler.geckoCodes.size:
|
||||||
|
self.error(tools.color_text('\n :: Error: Allocated codespace was smaller than the given codelist.\n', defaultColor=tools.TYELLOW))
|
||||||
|
|
||||||
elif codeHandler.allocation > 0x40000:
|
with open(dump, 'wb') as final:
|
||||||
print(tools.color_text(f'\n :: HINT: Recommended allocation limit is 0x40000. You allocated 0x{codeHandler.allocation:X}', defaultColor=tools.TYELLOWLIT))
|
dolFile.save(final)
|
||||||
|
|
||||||
|
if self.quiet:
|
||||||
|
return
|
||||||
|
|
||||||
|
if codeHandler.allocation > 0x70000:
|
||||||
|
print(tools.color_text(f'\n :: WARNING: Allocations beyond 0x70000 will crash certain games. You allocated 0x{codeHandler.allocation:X}', defaultColor=tools.TYELLOW))
|
||||||
|
|
||||||
if self.verbosity >= 2:
|
elif codeHandler.allocation > 0x40000:
|
||||||
print('')
|
print(tools.color_text(f'\n :: HINT: Recommended allocation limit is 0x40000. You allocated 0x{codeHandler.allocation:X}', defaultColor=tools.TYELLOWLIT))
|
||||||
if legacy == False:
|
|
||||||
info = [f' :: Start of game modified to address 0x{self.initAddress:X}',
|
if self.verbosity >= 3:
|
||||||
f' :: Game function "__start()" located at address 0x{oldStart:X}',
|
dolFile.print_info()
|
||||||
f' :: Allocation is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}',
|
print('-'*64)
|
||||||
f' :: Codehandler hooked at 0x{codeHandler.hookAddress:X}',
|
|
||||||
f' :: Codehandler is of type "{codeHandler.type}"',
|
if self.verbosity >= 2:
|
||||||
f' :: Of the 7 text sections in this DOL file, {len(dolFile.textSections)} are now being used']
|
print('')
|
||||||
else:
|
if legacy == False:
|
||||||
info = [f' :: Game function "__start()" located at address 0x{oldStart:X}',
|
info = [f' :: Start of game modified to address 0x{self.initAddress:X}',
|
||||||
f' :: Allocation is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}',
|
f' :: Game function "__start()" located at address 0x{oldStart:X}',
|
||||||
f' :: Codehandler hooked at 0x{codeHandler.hookAddress:X}',
|
f' :: Allocation is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}',
|
||||||
f' :: Codehandler is of type "{codeHandler.type}"',
|
f' :: Codehandler hooked at 0x{codeHandler.hookAddress:X}',
|
||||||
f' :: Of the 7 text sections in this DOL file, {len(dolFile.textSections)} are now being used']
|
f' :: Codehandler is of type "{codeHandler.type}"',
|
||||||
for bit in info:
|
f' :: Of the 7 text sections in this DOL file, {len(dolFile.textSections)} are now being used']
|
||||||
print(tools.color_text(bit, defaultColor=tools.TGREENLIT))
|
else:
|
||||||
|
info = [f' :: Game function "__start()" located at address 0x{oldStart:X}',
|
||||||
elif self.verbosity >= 1:
|
f' :: Allocation is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}',
|
||||||
print('')
|
f' :: Codehandler hooked at 0x{codeHandler.hookAddress:X}',
|
||||||
info = [f' :: GeckoLoader set at address 0x{self.initAddress:X}',
|
f' :: Codehandler is of type "{codeHandler.type}"',
|
||||||
f' :: Legacy size is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}',
|
f' :: Of the 7 text sections in this DOL file, {len(dolFile.textSections)} are now being used']
|
||||||
f' :: Codehandler is of type "{codeHandler.type}"']
|
for bit in info:
|
||||||
for bit in info:
|
print(tools.color_text(bit, defaultColor=tools.TGREENLIT))
|
||||||
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' :: Codehandler is of type "{codeHandler.type}"']
|
||||||
|
for bit in info:
|
||||||
|
print(tools.color_text(bit, defaultColor=tools.TGREENLIT))
|
||||||
|
|
||||||
|
|
||||||
def resource_path(relative_path: str):
|
def resource_path(relative_path: str):
|
||||||
|
|
|
@ -157,11 +157,16 @@ public:
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sMetaData.mOSArenaHi = sMetaData.mWiiHeap - alloc;
|
|
||||||
if (this->detectHomeConsole() == DiscHeader::CONSOLETYPE::Wii)
|
if (this->detectHomeConsole() == DiscHeader::CONSOLETYPE::Wii)
|
||||||
{
|
{
|
||||||
|
sMetaData.mOSArenaHi = sMetaData.mWiiHeap - alloc;
|
||||||
sMetaData.mWiiHeap -= alloc;
|
sMetaData.mWiiHeap -= alloc;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sMetaData.mOSArenaHi -= alloc;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Reference in a new issue