From 8e41d1b8c8cce16d2157f6e872d82e47b1140660 Mon Sep 17 00:00:00 2001 From: JoshuaMKW Date: Sun, 4 Oct 2020 02:30:11 -0500 Subject: [PATCH] Fixed stacking bug with kernel --- dolreader.py | 75 +++++++------- kernel.py | 268 +++++++++++++++++++++++++-------------------------- loader.cpp | 7 +- 3 files changed, 181 insertions(+), 169 deletions(-) diff --git a/dolreader.py b/dolreader.py index d053f1b..2840482 100644 --- a/dolreader.py +++ b/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: - offset, _, size, _, _ = self.get_final_section() - return (offset + size + 255) & -256 + 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(Data, GameAddress or None), tuple(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(Data, GameAddress or None), tuple(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") diff --git a/kernel.py b/kernel.py index 650be6a..6d93ece 100644 --- a/kernel.py +++ b/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,8 +436,8 @@ 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 def patch_legacy(self, codeHandler: CodeHandler, dolFile: DolFile): @@ -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,135 +492,137 @@ class KernelLoader(object): @timer 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: - 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''' - '''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.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: + if '.' in gctFile: + if os.path.splitext(gctFile)[1].lower() == '.txt': with open(os.path.join(tmpdir, 'gct.bin'), 'wb+') as temp: - temp.write(b'\x00\xD0\xC0\xDE'*2) - - 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.write(bytes.fromhex('00D0C0DE'*2 + codeHandler.parse_input(gctFile) + 'F000000000000000')) temp.seek(0) 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: - self.error(tools.color_text('No valid gecko code file found\n', defaultColor=tools.TREDLIT), exit=False) - return - - 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) - - '''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 + 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.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]) + else: + print(tools.color_text(f' :: HINT: {file} is not a .txt or .gct file', defaultColor=tools.TYELLOWLIT)) - 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)) - - dolFile.save(final) + temp.write(b'\xF0\x00\x00\x00\x00\x00\x00\x00') + temp.seek(0) + codeHandler.geckoCodes = GCT(temp) + + 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) + + 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: 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: - print(tools.color_text(f'\n :: WARNING: Allocations beyond 0x70000 will crash certain games. You allocated 0x{codeHandler.allocation:X}', defaultColor=tools.TYELLOW)) + 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: + 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: - print(tools.color_text(f'\n :: HINT: Recommended allocation limit is 0x40000. You allocated 0x{codeHandler.allocation:X}', defaultColor=tools.TYELLOWLIT)) + with open(dump, 'wb') as final: + 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: - print('') - if legacy == False: - 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' :: Codehandler hooked at 0x{codeHandler.hookAddress:X}', - f' :: Codehandler is of type "{codeHandler.type}"', - f' :: Of the 7 text sections in this DOL file, {len(dolFile.textSections)} are now being used'] - else: - info = [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' :: Codehandler hooked at 0x{codeHandler.hookAddress:X}', - f' :: Codehandler is of type "{codeHandler.type}"', - f' :: Of the 7 text sections in this DOL file, {len(dolFile.textSections)} are now being used'] - 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' :: Codehandler is of type "{codeHandler.type}"'] - for bit in info: - print(tools.color_text(bit, defaultColor=tools.TGREENLIT)) + 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: + 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' :: Codehandler hooked at 0x{codeHandler.hookAddress:X}', + f' :: Codehandler is of type "{codeHandler.type}"', + f' :: Of the 7 text sections in this DOL file, {len(dolFile.textSections)} are now being used'] + else: + info = [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' :: Codehandler hooked at 0x{codeHandler.hookAddress:X}', + f' :: Codehandler is of type "{codeHandler.type}"', + f' :: Of the 7 text sections in this DOL file, {len(dolFile.textSections)} are now being used'] + 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' :: Codehandler is of type "{codeHandler.type}"'] + for bit in info: + print(tools.color_text(bit, defaultColor=tools.TGREENLIT)) def resource_path(relative_path: str): diff --git a/loader.cpp b/loader.cpp index 1f8a6cf..7885817 100644 --- a/loader.cpp +++ b/loader.cpp @@ -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; + } + } } };