1
0
Fork 0

Fixed stacking bug with kernel

This commit is contained in:
JoshuaMKW 2020-10-04 02:30:11 -05:00
parent ea8e76dae9
commit 8e41d1b8c8
3 changed files with 181 additions and 169 deletions

View file

@ -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:
try:
offset, _, size, _, _ = self.get_final_section() offset, _, size, _, _ = self.get_final_section()
return (offset + size + 255) & -256 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")

View file

@ -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,7 +436,7 @@ 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
@ -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,30 +492,19 @@ 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:
if dolFile.get_full_size() < 0x100:
self.error(tools.color_text('DOL header is corrupted. Please provide a clean file\n', defaultColor=tools.TREDLIT), exit=False)
return
oldStart = dolFile.entryPoint oldStart = dolFile.entryPoint
'''Initialize our codes''' '''Initialize our codes'''
foundData = False
if '.' in gctFile: if '.' in gctFile:
if os.path.splitext(gctFile)[1].lower() == '.txt': if os.path.splitext(gctFile)[1].lower() == '.txt':
with open(os.path.join(tmpdir, 'gct.bin'), 'wb+') as temp: with open(os.path.join(tmpdir, 'gct.bin'), 'wb+') as temp:
temp.write(bytes.fromhex('00D0C0DE'*2 + codeHandler.gecko_self(gctFile) + 'F000000000000000')) temp.write(bytes.fromhex('00D0C0DE'*2 + codeHandler.parse_input(gctFile) + 'F000000000000000'))
temp.seek(0) temp.seek(0)
codeHandler.geckoCodes = GCT(temp) codeHandler.geckoCodes = GCT(temp)
foundData = True
elif os.path.splitext(gctFile)[1].lower() == '.gct': elif os.path.splitext(gctFile)[1].lower() == '.gct':
with open(gctFile, 'rb') as gct: with open(gctFile, 'rb') as gct:
codeHandler.geckoCodes = GCT(gct) codeHandler.geckoCodes = GCT(gct)
foundData = True
else: 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(b'\x00\xD0\xC0\xDE'*2)
@ -525,12 +512,10 @@ class KernelLoader(object):
for file in os.listdir(gctFile): for file in os.listdir(gctFile):
if os.path.isfile(os.path.join(gctFile, file)): if os.path.isfile(os.path.join(gctFile, file)):
if os.path.splitext(file)[1].lower() == '.txt': if os.path.splitext(file)[1].lower() == '.txt':
temp.write(bytes.fromhex(codeHandler.gecko_self(os.path.join(gctFile, file)))) temp.write(bytes.fromhex(codeHandler.parse_input(os.path.join(gctFile, file))))
foundData = True
elif os.path.splitext(file)[1].lower() == '.gct': elif os.path.splitext(file)[1].lower() == '.gct':
with open(os.path.join(gctFile, file), 'rb') as gct: with open(os.path.join(gctFile, file), 'rb') as gct:
temp.write(gct.read()[8:-8]) temp.write(gct.read()[8:-8])
foundData = True
else: 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))
@ -538,9 +523,8 @@ class KernelLoader(object):
temp.seek(0) temp.seek(0)
codeHandler.geckoCodes = GCT(temp) codeHandler.geckoCodes = GCT(temp)
if not foundData: if codeHandler.geckoCodes is None:
self.error(tools.color_text('No valid gecko code file found\n', defaultColor=tools.TREDLIT), exit=False) 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))
return
if self.protect and self.patchJob == "ARENA": if self.protect and self.patchJob == "ARENA":
self.protect_game(codeHandler) self.protect_game(codeHandler)
@ -563,12 +547,23 @@ class KernelLoader(object):
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.geckoCodes.size)
self._rawData.seek(0) self._rawData.seek(0)
'''Is insertion legacy?''' if codeHandler.optimizeList:
codeHandler.geckoCodes.optimize_codelist(dolFile)
if codeHandler.geckoCodes.size <= 0x10: '''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) dolFile.save(final)
if self.quiet:
return
if self.verbosity >= 3:
dolFile.print_info()
print('-'*64)
if self.verbosity >= 1: 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 return
if self.patchJob == 'LEGACY': if self.patchJob == 'LEGACY':
@ -587,6 +582,7 @@ class KernelLoader(object):
elif codeHandler.allocation < codeHandler.geckoCodes.size: 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)) self.error(tools.color_text('\n :: Error: Allocated codespace was smaller than the given codelist.\n', defaultColor=tools.TYELLOW))
with open(dump, 'wb') as final:
dolFile.save(final) dolFile.save(final)
if self.quiet: if self.quiet:
@ -598,6 +594,10 @@ class KernelLoader(object):
elif codeHandler.allocation > 0x40000: elif codeHandler.allocation > 0x40000:
print(tools.color_text(f'\n :: HINT: Recommended allocation limit is 0x40000. You allocated 0x{codeHandler.allocation:X}', defaultColor=tools.TYELLOWLIT)) print(tools.color_text(f'\n :: HINT: Recommended allocation limit is 0x40000. You allocated 0x{codeHandler.allocation:X}', defaultColor=tools.TYELLOWLIT))
if self.verbosity >= 3:
dolFile.print_info()
print('-'*64)
if self.verbosity >= 2: if self.verbosity >= 2:
print('') print('')
if legacy == False: if legacy == False:

View file

@ -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;
}
} }
} }
}; };