Fixed stacking bug with kernel
This commit is contained in:
parent
ea8e76dae9
commit
8e41d1b8c8
3 changed files with 181 additions and 169 deletions
71
dolreader.py
71
dolreader.py
|
@ -13,17 +13,18 @@ class DolFile(object):
|
|||
Text = 0
|
||||
Data = 1
|
||||
|
||||
maxTextSections = 7
|
||||
maxDataSections = 11
|
||||
offsetInfoLoc = 0
|
||||
addressInfoLoc = 0x48
|
||||
sizeInfoLoc = 0x90
|
||||
bssInfoLoc = 0xD8
|
||||
entryInfoLoc = 0xE0
|
||||
|
||||
def __init__(self, f=None):
|
||||
self.fileOffsetLoc = 0
|
||||
self.fileAddressLoc = 0x48
|
||||
self.fileSizeLoc = 0x90
|
||||
self.fileBssInfoLoc = 0xD8
|
||||
self.fileEntryLoc = 0xE0
|
||||
|
||||
self.textSections = []
|
||||
self.dataSections = []
|
||||
self.maxTextSections = 7
|
||||
self.maxDataSections = 11
|
||||
|
||||
self.bssAddress = 0
|
||||
self.bssSize = 0
|
||||
|
@ -32,27 +33,27 @@ class DolFile(object):
|
|||
if f is None: return
|
||||
|
||||
# Read text and data section addresses and sizes
|
||||
for i in range(self.maxTextSections + self.maxDataSections):
|
||||
f.seek(self.fileOffsetLoc + (i << 2))
|
||||
for i in range(DolFile.maxTextSections + DolFile.maxDataSections):
|
||||
f.seek(DolFile.offsetInfoLoc + (i << 2))
|
||||
offset = read_uint32(f)
|
||||
f.seek(self.fileAddressLoc + (i << 2))
|
||||
f.seek(DolFile.addressInfoLoc + (i << 2))
|
||||
address = read_uint32(f)
|
||||
f.seek(self.fileSizeLoc + (i << 2))
|
||||
f.seek(DolFile.sizeInfoLoc + (i << 2))
|
||||
size = read_uint32(f)
|
||||
|
||||
if offset >= 0x100:
|
||||
f.seek(offset)
|
||||
data = BytesIO(f.read(size))
|
||||
if i < self.maxTextSections:
|
||||
if i < DolFile.maxTextSections:
|
||||
self.textSections.append([offset, address, size, data, DolFile.SectionType.Text])
|
||||
else:
|
||||
self.dataSections.append([offset, address, size, data, DolFile.SectionType.Data])
|
||||
|
||||
f.seek(self.fileBssInfoLoc)
|
||||
f.seek(DolFile.bssInfoLoc)
|
||||
self.bssAddress = read_uint32(f)
|
||||
self.bssSize = read_uint32(f)
|
||||
|
||||
f.seek(self.fileEntryLoc)
|
||||
f.seek(DolFile.entryInfoLoc)
|
||||
self.entryPoint = read_uint32(f)
|
||||
|
||||
self._currLogicAddr = self.get_first_section()[1]
|
||||
|
@ -186,39 +187,42 @@ class DolFile(object):
|
|||
f.seek(0)
|
||||
f.write(b"\x00" * self.get_full_size())
|
||||
|
||||
for i in range(self.maxTextSections + self.maxDataSections):
|
||||
if i < self.maxTextSections:
|
||||
for i in range(DolFile.maxTextSections + DolFile.maxDataSections):
|
||||
if i < DolFile.maxTextSections:
|
||||
if i < len(self.textSections):
|
||||
offset, address, size, data, _ = self.textSections[i]
|
||||
else:
|
||||
continue
|
||||
else:
|
||||
if i - self.maxTextSections < len(self.dataSections):
|
||||
offset, address, size, data, _ = self.dataSections[i - self.maxTextSections]
|
||||
if i - DolFile.maxTextSections < len(self.dataSections):
|
||||
offset, address, size, data, _ = self.dataSections[i - DolFile.maxTextSections]
|
||||
else:
|
||||
continue
|
||||
|
||||
f.seek(self.fileOffsetLoc + (i * 4))
|
||||
f.seek(DolFile.offsetInfoLoc + (i << 2))
|
||||
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
|
||||
f.seek(self.fileSizeLoc + (i * 4))
|
||||
f.seek(DolFile.sizeInfoLoc + (i << 2))
|
||||
write_uint32(f, size) #size in file
|
||||
|
||||
f.seek(offset)
|
||||
f.write(data.getbuffer())
|
||||
|
||||
f.seek(self.fileBssInfoLoc)
|
||||
f.seek(DolFile.bssInfoLoc)
|
||||
write_uint32(f, self.bssAddress)
|
||||
write_uint32(f, self.bssSize)
|
||||
|
||||
f.seek(self.fileEntryLoc)
|
||||
f.seek(DolFile.entryInfoLoc)
|
||||
write_uint32(f, self.entryPoint)
|
||||
align_byte_size(f, 256)
|
||||
|
||||
def get_full_size(self) -> int:
|
||||
try:
|
||||
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:
|
||||
""" 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... """
|
||||
|
||||
for i, dataSet in enumerate(sectionsList):
|
||||
if len(self.textSections) >= self.maxTextSections:
|
||||
raise SectionCountFullError(f"Exceeded max text section limit of {self.maxTextSections}")
|
||||
if len(self.textSections) >= DolFile.maxTextSections:
|
||||
raise SectionCountFullError(f"Exceeded max text section limit of {DolFile.maxTextSections}")
|
||||
|
||||
fOffset, _, fSize, _, _ = self.get_final_section()
|
||||
_, 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... """
|
||||
|
||||
for i, dataSet in enumerate(sectionsList):
|
||||
if len(self.dataSections) >= self.maxDataSections:
|
||||
raise SectionCountFullError(f"Exceeded max data section limit of {self.maxDataSections}")
|
||||
if len(self.dataSections) >= DolFile.maxDataSections:
|
||||
raise SectionCountFullError(f"Exceeded max data section limit of {DolFile.maxDataSections}")
|
||||
|
||||
fOffset, _, fSize, _, _ = self.get_final_section()
|
||||
_, pAddress, pSize, _, _ = self.dataSections[len(self.dataSections) - 1]
|
||||
|
@ -341,22 +345,25 @@ class DolFile(object):
|
|||
while (char := self.read(1)) != b"\x00":
|
||||
try:
|
||||
string += char.decode(encoding)
|
||||
length += 1
|
||||
except UnicodeDecodeError:
|
||||
print(f"{char} at pos {length}, (address 0x{addr + length:08X}) is not a valid utf-8 character")
|
||||
return ""
|
||||
if length > maxlen and maxlen != 0:
|
||||
break
|
||||
if length > (maxlen-1) and maxlen != 0:
|
||||
return string
|
||||
|
||||
return string
|
||||
|
||||
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} |"
|
||||
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} |"
|
||||
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")
|
||||
|
||||
|
|
74
kernel.py
74
kernel.py
|
@ -30,7 +30,7 @@ class InvalidGeckoCodeError(Exception): pass
|
|||
|
||||
class GCT(object):
|
||||
|
||||
def __init__(self, f: open):
|
||||
def __init__(self, f):
|
||||
self.codeList = BytesIO(f.read())
|
||||
self.rawLineCount = tools.stream_size(self.codeList) >> 3
|
||||
self.lineCount = self.rawLineCount - 2
|
||||
|
@ -38,7 +38,7 @@ class GCT(object):
|
|||
f.seek(0)
|
||||
|
||||
@staticmethod
|
||||
def determine_codelength(codetype, info) -> int:
|
||||
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)
|
||||
|
@ -65,12 +65,11 @@ class GCT(object):
|
|||
return 0x8
|
||||
|
||||
def optimize_codelist(self, dolFile: DolFile):
|
||||
codelist = b''
|
||||
codetype = b'temp'
|
||||
codelist = b'\x00\xD0\xC0\xDE'*2
|
||||
skipcodes = 0
|
||||
|
||||
while codetype:
|
||||
codetype = self.codeList.read(4)
|
||||
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) & 0x01FFFFFF)
|
||||
try:
|
||||
|
@ -167,7 +166,8 @@ class GCT(object):
|
|||
codelist += b'\xF0\x00\x00\x00\x00\x00\x00\x00'
|
||||
break
|
||||
|
||||
self.codeList.seek(-8, 1)
|
||||
self.codeList.seek(-8, 1)*5
|
||||
|
||||
length = GCT.determine_codelength(codetype, info)
|
||||
while length > 0:
|
||||
codelist += self.codeList.read(1)
|
||||
|
@ -218,7 +218,7 @@ class CodeHandler(object):
|
|||
|
||||
f.seek(0)
|
||||
|
||||
def gecko_self(self, geckoText) -> str:
|
||||
def parse_input(self, geckoText) -> str:
|
||||
with open(r'{}'.format(geckoText), 'rb') as gecko:
|
||||
result = chardet.detect(gecko.read())
|
||||
encodeType = result['encoding']
|
||||
|
@ -263,7 +263,7 @@ class CodeHandler(object):
|
|||
b1 ^= b2
|
||||
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)
|
||||
i = 0
|
||||
while True:
|
||||
|
@ -304,7 +304,7 @@ class CodeHandler(object):
|
|||
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", 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)
|
||||
|
||||
|
@ -366,8 +366,6 @@ class KernelLoader(object):
|
|||
_key = random.randrange(0x100000000)
|
||||
self._rawData.seek(0)
|
||||
|
||||
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
|
||||
self._rawData.seek(-4, 1)
|
||||
|
@ -420,7 +418,7 @@ class KernelLoader(object):
|
|||
self.set_variables(initpoint, _lowerAddr)
|
||||
|
||||
if self.encrypt:
|
||||
codeHandler.encrypt_data(_key)
|
||||
codeHandler.encrypt_codes(_key)
|
||||
|
||||
|
||||
def patch_arena(self, codeHandler: CodeHandler, dolFile: DolFile):
|
||||
|
@ -438,7 +436,7 @@ 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!', 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
|
||||
|
||||
|
@ -454,12 +452,12 @@ class KernelLoader(object):
|
|||
try:
|
||||
dolFile.append_data_sections([(_handlerData, codeHandler.initAddress)])
|
||||
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):
|
||||
_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'\x90\x01\x00\x08\xBC\x61\x00\x0C',
|
||||
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'\xB8\x61\x00\x0C\x80\x01\x00\x08',
|
||||
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)
|
||||
for chunk in protectdata:
|
||||
|
@ -494,30 +492,19 @@ class KernelLoader(object):
|
|||
|
||||
@timer
|
||||
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
|
||||
|
||||
'''Initialize our codes'''
|
||||
|
||||
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.write(bytes.fromhex('00D0C0DE'*2 + codeHandler.parse_input(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:
|
||||
temp.write(b'\x00\xD0\xC0\xDE'*2)
|
||||
|
@ -525,12 +512,10 @@ class KernelLoader(object):
|
|||
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
|
||||
temp.write(bytes.fromhex(codeHandler.parse_input(os.path.join(gctFile, file))))
|
||||
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))
|
||||
|
||||
|
@ -538,9 +523,8 @@ class KernelLoader(object):
|
|||
temp.seek(0)
|
||||
codeHandler.geckoCodes = GCT(temp)
|
||||
|
||||
if not foundData:
|
||||
self.error(tools.color_text('No valid gecko code file found\n', defaultColor=tools.TREDLIT), exit=False)
|
||||
return
|
||||
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 self.protect and self.patchJob == "ARENA":
|
||||
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._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)
|
||||
|
||||
if self.quiet:
|
||||
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))
|
||||
|
||||
return
|
||||
|
||||
if self.patchJob == 'LEGACY':
|
||||
|
@ -587,6 +582,7 @@ class KernelLoader(object):
|
|||
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))
|
||||
|
||||
with open(dump, 'wb') as final:
|
||||
dolFile.save(final)
|
||||
|
||||
if self.quiet:
|
||||
|
@ -598,6 +594,10 @@ class KernelLoader(object):
|
|||
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))
|
||||
|
||||
if self.verbosity >= 3:
|
||||
dolFile.print_info()
|
||||
print('-'*64)
|
||||
|
||||
if self.verbosity >= 2:
|
||||
print('')
|
||||
if legacy == False:
|
||||
|
|
|
@ -157,11 +157,16 @@ public:
|
|||
}
|
||||
else
|
||||
{
|
||||
sMetaData.mOSArenaHi = sMetaData.mWiiHeap - alloc;
|
||||
if (this->detectHomeConsole() == DiscHeader::CONSOLETYPE::Wii)
|
||||
{
|
||||
sMetaData.mOSArenaHi = sMetaData.mWiiHeap - alloc;
|
||||
sMetaData.mWiiHeap -= alloc;
|
||||
}
|
||||
else
|
||||
{
|
||||
sMetaData.mOSArenaHi -= alloc;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
Reference in a new issue