Improved hook related features
This commit is contained in:
parent
d0e7b8c423
commit
348ef19294
5 changed files with 377 additions and 238 deletions
|
@ -90,16 +90,15 @@ if __name__ == "__main__":
|
||||||
default='FULL',
|
default='FULL',
|
||||||
choices=['MINI', 'FULL'],
|
choices=['MINI', 'FULL'],
|
||||||
metavar='TYPE')
|
metavar='TYPE')
|
||||||
parser.add_argument('--codehook',
|
parser.add_argument('--hooktype',
|
||||||
help='''Choose where the codeHandler hooks to, needs to exist at a blr instruction''',
|
help='''The type of hook used for the RAM search. VI or GX are recommended,
|
||||||
|
although PAD can work just as well. VI is the default hook used''',
|
||||||
|
default='VI',
|
||||||
|
choices=['VI', 'GX', 'PAD'],
|
||||||
|
metavar='HOOK')
|
||||||
|
parser.add_argument('--hookaddress',
|
||||||
|
help='Choose where the codeHandler hooks to, overrides auto hooks',
|
||||||
metavar='ADDRESS')
|
metavar='ADDRESS')
|
||||||
parser.add_argument('-q', '--quiet',
|
|
||||||
help='Print nothing to the console',
|
|
||||||
action='store_true')
|
|
||||||
parser.add_argument('-v', '--verbose',
|
|
||||||
help='Print extra info to the console',
|
|
||||||
default=0,
|
|
||||||
action='count')
|
|
||||||
parser.add_argument('-o', '--optimize',
|
parser.add_argument('-o', '--optimize',
|
||||||
help='''Optimizes the codelist by directly patching qualifying
|
help='''Optimizes the codelist by directly patching qualifying
|
||||||
ram writes into the dol file, and removing them from the codelist''',
|
ram writes into the dol file, and removing them from the codelist''',
|
||||||
|
@ -118,6 +117,13 @@ if __name__ == "__main__":
|
||||||
parser.add_argument('--encrypt',
|
parser.add_argument('--encrypt',
|
||||||
help='Encrypts the codelist on compile time, helping to slow the snoopers',
|
help='Encrypts the codelist on compile time, helping to slow the snoopers',
|
||||||
action='store_true')
|
action='store_true')
|
||||||
|
parser.add_argument('-q', '--quiet',
|
||||||
|
help='Print nothing to the console',
|
||||||
|
action='store_true')
|
||||||
|
parser.add_argument('-v', '--verbose',
|
||||||
|
help='Print extra info to the console',
|
||||||
|
default=0,
|
||||||
|
action='count')
|
||||||
|
|
||||||
if len(sys.argv) == 1:
|
if len(sys.argv) == 1:
|
||||||
version = __version__.rjust(9, ' ')
|
version = __version__.rjust(9, ' ')
|
||||||
|
@ -128,7 +134,7 @@ if __name__ == "__main__":
|
||||||
if os.path.splitext(__file__)[1].lower() == ".py":
|
if os.path.splitext(__file__)[1].lower() == ".py":
|
||||||
helpMessage = 'Try the command: python GeckoLoader.py -h'.center(64, ' ')
|
helpMessage = 'Try the command: python GeckoLoader.py -h'.center(64, ' ')
|
||||||
else:
|
else:
|
||||||
helpMessage = 'Try the command: .\GeckLoader.exe -h'.center(64, ' ')
|
helpMessage = 'Try the command: .\GeckoLoader.exe -h'.center(64, ' ')
|
||||||
|
|
||||||
logo = [' ',
|
logo = [' ',
|
||||||
' ╔═══════════════════════════════════════════════════════════╗ ',
|
' ╔═══════════════════════════════════════════════════════════╗ ',
|
||||||
|
@ -190,12 +196,12 @@ if __name__ == "__main__":
|
||||||
else:
|
else:
|
||||||
_allocation = None
|
_allocation = None
|
||||||
|
|
||||||
if args.codehook:
|
if args.hookaddress:
|
||||||
if 0x80000000 > int(args.codehook, 16) >= 0x81800000:
|
if 0x80000000 > int(args.hookaddress, 16) >= 0x81800000:
|
||||||
parser.error(color_text('The codeHandler hook address was beyond bounds\n', defaultColor=TREDLIT))
|
parser.error(color_text('The codeHandler hook address was beyond bounds\n', defaultColor=TREDLIT))
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
_codehook = int(args.codehook, 16)
|
_codehook = int(args.hookaddress, 16)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
parser.error(color_text('The codeHandler hook address was invalid\n', defaultColor=TREDLIT))
|
parser.error(color_text('The codeHandler hook address was invalid\n', defaultColor=TREDLIT))
|
||||||
else:
|
else:
|
||||||
|
@ -222,6 +228,7 @@ if __name__ == "__main__":
|
||||||
codeHandler = CodeHandler(handler)
|
codeHandler = CodeHandler(handler)
|
||||||
codeHandler.allocation = _allocation
|
codeHandler.allocation = _allocation
|
||||||
codeHandler.hookAddress = _codehook
|
codeHandler.hookAddress = _codehook
|
||||||
|
codeHandler.hookType = args.hooktype
|
||||||
codeHandler.includeAll = args.txtcodes
|
codeHandler.includeAll = args.txtcodes
|
||||||
|
|
||||||
with open(resource_path(os.path.join('bin', 'geckoloader.bin')), 'rb') as kernelfile:
|
with open(resource_path(os.path.join('bin', 'geckoloader.bin')), 'rb') as kernelfile:
|
||||||
|
|
28
dolreader.py
28
dolreader.py
|
@ -20,11 +20,8 @@ class DolFile:
|
||||||
self.bssAddress = 0
|
self.bssAddress = 0
|
||||||
self.bssSize = 0
|
self.bssSize = 0
|
||||||
self.entryPoint = 0x80003000
|
self.entryPoint = 0x80003000
|
||||||
|
|
||||||
self._currentEnd = None
|
|
||||||
|
|
||||||
if f is None:
|
if f is None: return
|
||||||
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(self.maxTextSections + self.maxDataSections):
|
||||||
|
@ -106,32 +103,31 @@ otherwise it returns None'''
|
||||||
|
|
||||||
# Unsupported: Reading an entire dol file
|
# Unsupported: Reading an entire dol file
|
||||||
# Assumption: A read should not go beyond the current section
|
# Assumption: A read should not go beyond the current section
|
||||||
def read(self, size):
|
def read(self, _size):
|
||||||
_, address, size, data = self.resolve_address(self._currLogicAddr)
|
_, address, size, data = self.resolve_address(self._currLogicAddr)
|
||||||
if self._currLogicAddr + size > address + size:
|
if self._currLogicAddr + _size > address + size:
|
||||||
raise RuntimeError("Read goes over current section")
|
raise RuntimeError("Read goes over current section")
|
||||||
|
|
||||||
self._currLogicAddr += size
|
self._currLogicAddr += _size
|
||||||
return data.read(size)
|
return data.read(_size)
|
||||||
|
|
||||||
# Assumption: A write should not go beyond the current section
|
# Assumption: A write should not go beyond the current section
|
||||||
def write(self, data):
|
def write(self, _data):
|
||||||
offset, address, size, data = self.resolve_address(self._currLogicAddr)
|
offset, address, size, data = self.resolve_address(self._currLogicAddr)
|
||||||
if self._currLogicAddr + len(data) > address + size:
|
if self._currLogicAddr + len(_data) > address + size:
|
||||||
raise RuntimeError("Write goes over current section")
|
raise RuntimeError("Write goes over current section")
|
||||||
|
|
||||||
data.write(data)
|
data.write(_data)
|
||||||
self._currLogicAddr += len(data)
|
self._currLogicAddr += len(_data)
|
||||||
|
|
||||||
def seek(self, where, whence=0):
|
def seek(self, where, whence=0):
|
||||||
if whence == 0:
|
if whence == 0:
|
||||||
offset, address, size, data = self.resolve_address(where)
|
_, address, _, data = self.resolve_address(where)
|
||||||
data.seek(where - address)
|
data.seek(where - address)
|
||||||
|
|
||||||
self._currLogicAddr = where
|
self._currLogicAddr = where
|
||||||
self._currentEnd = address + size
|
|
||||||
elif whence == 1:
|
elif whence == 1:
|
||||||
offset, address, size, data = self.resolve_address(self._currLogicAddr + where)
|
_, address, _, data = self.resolve_address(self._currLogicAddr + where)
|
||||||
data.seek((self._currLogicAddr + where) - address)
|
data.seek((self._currLogicAddr + where) - address)
|
||||||
|
|
||||||
self._currLogicAddr += where
|
self._currLogicAddr += where
|
||||||
|
@ -256,7 +252,7 @@ otherwise it returns None'''
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def insert_branch(self, to, _from, lk=0):
|
def insert_branch(self, to, _from, lk=0):
|
||||||
self.write(((to - _from) & 0x3FFFFFF | 0x48000000 | lk).to_bytes(4, byteorder='big', signed=False))
|
tools.write_uint32(self, (to - _from) & 0x3FFFFFF | 0x48000000 | lk)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Example usage (reading some enemy info from the Pikmin 2 demo from US demo disc 17)
|
# Example usage (reading some enemy info from the Pikmin 2 demo from US demo disc 17)
|
||||||
|
|
106
kernel.py
106
kernel.py
|
@ -182,10 +182,17 @@ class CodeHandler:
|
||||||
self.handlerLength = tools.get_size(f)
|
self.handlerLength = tools.get_size(f)
|
||||||
self.initAddress = 0x80001800
|
self.initAddress = 0x80001800
|
||||||
self.startAddress = 0x800018A8
|
self.startAddress = 0x800018A8
|
||||||
|
|
||||||
self.wiiVIHook = b'\x7C\xE3\x3B\x78\x38\x87\x00\x34\x38\xA7\x00\x38\x38\xC7\x00\x4C'
|
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.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.allocation = None
|
||||||
self.hookAddress = None
|
self.hookAddress = None
|
||||||
|
self.hookType = None
|
||||||
self.geckoCodes = None
|
self.geckoCodes = None
|
||||||
self.includeAll = False
|
self.includeAll = False
|
||||||
self.optimizeList = False
|
self.optimizeList = False
|
||||||
|
@ -211,7 +218,7 @@ class CodeHandler:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if state is None:
|
if state is None:
|
||||||
if line.startswith('$'):
|
if line.startswith('$') or line.startswith('['):
|
||||||
state = 'Dolphin'
|
state = 'Dolphin'
|
||||||
else:
|
else:
|
||||||
state = 'OcarinaM'
|
state = 'OcarinaM'
|
||||||
|
@ -338,10 +345,7 @@ class KernelLoader:
|
||||||
|
|
||||||
elif sample == b'HOOK': #Found keyword "HOOK". Goes with the codehandler hook
|
elif sample == b'HOOK': #Found keyword "HOOK". Goes with the codehandler hook
|
||||||
self._rawData.seek(-4, 1)
|
self._rawData.seek(-4, 1)
|
||||||
if codeHandler.hookAddress == None:
|
tools.write_uint32(self._rawData, codeHandler.hookAddress)
|
||||||
tools.write_uint32(self._rawData, 0)
|
|
||||||
else:
|
|
||||||
tools.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)
|
self._rawData.seek(-4, 1)
|
||||||
|
@ -438,6 +442,8 @@ class KernelLoader:
|
||||||
if dolFile.get_full_size() < 0x100:
|
if dolFile.get_full_size() < 0x100:
|
||||||
parser.error(tools.color_text('DOL header is corrupted. Please provide a clean file\n', defaultColor=tools.TREDLIT), exit=False)
|
parser.error(tools.color_text('DOL header is corrupted. Please provide a clean file\n', defaultColor=tools.TREDLIT), exit=False)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
oldStart = dolFile.entryPoint
|
||||||
|
|
||||||
'''Initialize our codes'''
|
'''Initialize our codes'''
|
||||||
|
|
||||||
|
@ -510,15 +516,16 @@ class KernelLoader:
|
||||||
|
|
||||||
if self.codeLocation == 'LEGACY':
|
if self.codeLocation == 'LEGACY':
|
||||||
codeHandler.allocation = 0x80003000 - (codeHandler.initAddress + codeHandler.handlerLength)
|
codeHandler.allocation = 0x80003000 - (codeHandler.initAddress + codeHandler.handlerLength)
|
||||||
|
hooked = determine_codehook(dolFile, codeHandler, True)
|
||||||
status = self.patch_legacy(codeHandler, dolFile)
|
status = self.patch_legacy(codeHandler, dolFile)
|
||||||
if status is False:
|
|
||||||
hooked, msg = determine_codehook(dolFile, codeHandler)
|
|
||||||
if not hooked:
|
|
||||||
parser.error(tools.color_text(msg, defaultColor=tools.TREDLIT))
|
|
||||||
legacy = True
|
legacy = True
|
||||||
else:
|
else:
|
||||||
|
hooked = determine_codehook(dolFile, codeHandler, False)
|
||||||
status = self.patch_arena(codeHandler, dolFile)
|
status = self.patch_arena(codeHandler, dolFile)
|
||||||
legacy = False
|
legacy = False
|
||||||
|
|
||||||
|
if not hooked:
|
||||||
|
parser.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:
|
if status is False:
|
||||||
parser.error(tools.color_text('Not enough text sections to patch the DOL file! Potentially due to previous mods?\n', defaultColor=tools.TREDLIT), exit=False)
|
parser.error(tools.color_text('Not enough text sections to patch the DOL file! Potentially due to previous mods?\n', defaultColor=tools.TREDLIT), exit=False)
|
||||||
|
@ -541,34 +548,26 @@ class KernelLoader:
|
||||||
if self.verbosity >= 2:
|
if self.verbosity >= 2:
|
||||||
print('')
|
print('')
|
||||||
if legacy == False:
|
if legacy == False:
|
||||||
info = [f' :: GeckoLoader set at address 0x{self.initAddress:X}, start of game modified to address 0x{self.initAddress:X}',
|
info = [f' :: Start of game modified to address 0x{self.initAddress:X}',
|
||||||
f' :: Game function "__init_registers()" located at address 0x{dolFile.entryPoint:X}',
|
f' :: Game function "__init_registers()" located at address 0x{oldStart:X}',
|
||||||
f' :: Code 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.geckoCodes.size:X}',
|
||||||
|
f' :: Codehandler hooked at 0x{codeHandler.hookAddress:X}',
|
||||||
f' :: Codehandler is of type "{codeHandler.type}"',
|
f' :: Codehandler is of type "{codeHandler.type}"',
|
||||||
f' :: Of the 7 text sections in this DOL file, {len(dolFile.textSections)} were already used']
|
f' :: Of the 7 text sections in this DOL file, {len(dolFile.textSections)} are now being used']
|
||||||
if codeHandler.hookAddress is not None:
|
|
||||||
info.insert(2, f' :: Codehandler hooked at 0x{codeHandler.hookAddress:08X}')
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
info = [f' :: Game function "__init_registers()" located at address 0x{dolFile.entryPoint:X}',
|
info = [f' :: Game function "__init_registers()" located at address 0x{oldStart:X}',
|
||||||
f' :: Code 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.geckoCodes.size:X}',
|
||||||
|
f' :: Codehandler hooked at 0x{codeHandler.hookAddress:X}',
|
||||||
f' :: Codehandler is of type "{codeHandler.type}"',
|
f' :: Codehandler is of type "{codeHandler.type}"',
|
||||||
f' :: Of the 7 text sections in this DOL file, {len(dolFile.textSections)} were already used']
|
f' :: Of the 7 text sections in this DOL file, {len(dolFile.textSections)} are now being used']
|
||||||
if codeHandler.hookAddress is not None:
|
|
||||||
info.insert(1, f' :: Codehandler hooked at 0x{codeHandler.hookAddress:08X}')
|
|
||||||
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:
|
elif self.verbosity >= 1:
|
||||||
print('')
|
print('')
|
||||||
if legacy == False:
|
info = [f' :: GeckoLoader set at address 0x{self.initAddress:X}',
|
||||||
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}"',
|
f' :: Codehandler is of type "{codeHandler.type}"']
|
||||||
f' :: Code allocation is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}']
|
|
||||||
else:
|
|
||||||
info = [f' :: Codehandler is of type "{codeHandler.type}"',
|
|
||||||
f' :: Code allocation is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}']
|
|
||||||
|
|
||||||
for bit in info:
|
for bit in info:
|
||||||
print(tools.color_text(bit, defaultColor=tools.TGREENLIT))
|
print(tools.color_text(bit, defaultColor=tools.TGREENLIT))
|
||||||
|
|
||||||
|
@ -580,30 +579,50 @@ def resource_path(relative_path: str):
|
||||||
base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__)))
|
base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__)))
|
||||||
return os.path.join(base_path, relative_path)
|
return os.path.join(base_path, relative_path)
|
||||||
|
|
||||||
def determine_codehook(dolFile: DolFile, codeHandler: CodeHandler):
|
def determine_codehook(dolFile: DolFile, codeHandler: CodeHandler, hook=False):
|
||||||
if codeHandler.hookAddress == None:
|
if codeHandler.hookAddress == None:
|
||||||
return assert_code_hook(dolFile, codeHandler)
|
if not assert_code_hook(dolFile, codeHandler):
|
||||||
else:
|
return False
|
||||||
return insert_code_hook(dolFile, codeHandler, codeHandler.hookAddress)
|
|
||||||
|
if hook:
|
||||||
|
insert_code_hook(dolFile, codeHandler, codeHandler.hookAddress)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def assert_code_hook(dolFile: DolFile, codeHandler: CodeHandler):
|
def assert_code_hook(dolFile: DolFile, codeHandler: CodeHandler, hook=False):
|
||||||
for _, address, size in dolFile.textSections:
|
for _, address, size, _, in dolFile.textSections:
|
||||||
dolFile.seek(address, 0)
|
dolFile.seek(address, 0)
|
||||||
sample = dolFile.read(size)
|
sample = dolFile.read(size)
|
||||||
|
|
||||||
result = sample.find(codeHandler.gcnVIHook)
|
if codeHandler.hookType == 'VI':
|
||||||
|
result = sample.find(codeHandler.gcnVIHook)
|
||||||
|
elif codeHandler.hookType == 'GX':
|
||||||
|
result = sample.find(codeHandler.gcnGXDrawHook)
|
||||||
|
elif codeHandler.hookType == 'PAD':
|
||||||
|
result = sample.find(codeHandler.gcnPADHook)
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(f'Unsupported hook type specified ({codeHandler.hookType})')
|
||||||
|
|
||||||
if result >= 0:
|
if result >= 0:
|
||||||
dolFile.seek(address, 0)
|
dolFile.seek(address, 0)
|
||||||
dolFile.seek(result, 1)
|
dolFile.seek(result, 1)
|
||||||
else:
|
else:
|
||||||
result = sample.find(codeHandler.wiiVIHook)
|
if codeHandler.hookType == 'VI':
|
||||||
|
result = sample.find(codeHandler.wiiVIHook)
|
||||||
|
elif codeHandler.hookType == 'GX':
|
||||||
|
result = sample.find(codeHandler.wiiGXDrawHook)
|
||||||
|
elif codeHandler.hookType == 'PAD':
|
||||||
|
result = sample.find(codeHandler.wiiPADHook)
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(f'Unsupported hook type specified ({codeHandler.hookType})')
|
||||||
|
|
||||||
if result >= 0:
|
if result >= 0:
|
||||||
dolFile.seek(address, 0)
|
dolFile.seek(address, 0)
|
||||||
dolFile.seek(result, 1)
|
dolFile.seek(result, 1)
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
print(f'{dolFile._currLogicAddr:X}, {address:X}, {result:X}')
|
||||||
sample = tools.read_uint32(dolFile)
|
sample = tools.read_uint32(dolFile)
|
||||||
while sample != 0x4E800020:
|
while sample != 0x4E800020:
|
||||||
sample = tools.read_uint32(dolFile)
|
sample = tools.read_uint32(dolFile)
|
||||||
|
@ -611,15 +630,10 @@ def assert_code_hook(dolFile: DolFile, codeHandler: CodeHandler):
|
||||||
dolFile.seek(-4, 1)
|
dolFile.seek(-4, 1)
|
||||||
codeHandler.hookAddress = dolFile.tell()
|
codeHandler.hookAddress = dolFile.tell()
|
||||||
|
|
||||||
return insert_code_hook(dolFile, codeHandler, codeHandler.hookAddress)
|
if hook: insert_code_hook(dolFile, codeHandler, codeHandler.hookAddress)
|
||||||
return False, 'Failed to find a hook address. Try using option --codehook to use your own address\n'
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def insert_code_hook(dolFile: DolFile, codeHandler: CodeHandler, address: int):
|
def insert_code_hook(dolFile: DolFile, codeHandler: CodeHandler, address: int):
|
||||||
dolFile.seek(address)
|
dolFile.seek(address)
|
||||||
|
dolFile.insert_branch(codeHandler.startAddress, address, lk=0)
|
||||||
if tools.read_uint32(dolFile) != 0x4E800020:
|
|
||||||
return False, 'Codehandler hook given is not a blr\n'
|
|
||||||
|
|
||||||
dolFile.seek(-4, 1)
|
|
||||||
dolFile.insert_branch(codeHandler.startAddress, address, lk=0)
|
|
||||||
return True, ''
|
|
429
loader.cpp
429
loader.cpp
|
@ -1,4 +1,11 @@
|
||||||
/*Credits to riidefi for hook code, cache asm, and teaching me C*/
|
//#include <vector>
|
||||||
|
//#include <string>
|
||||||
|
|
||||||
|
//using std::array;
|
||||||
|
//using std::memcpy;
|
||||||
|
//using std::memcmp;
|
||||||
|
//using std::memset;
|
||||||
|
//using std::string;
|
||||||
|
|
||||||
#define dcbst(_val) asm volatile("dcbst 0, %0" \
|
#define dcbst(_val) asm volatile("dcbst 0, %0" \
|
||||||
: \
|
: \
|
||||||
|
@ -11,38 +18,54 @@
|
||||||
: "r"(_val))
|
: "r"(_val))
|
||||||
|
|
||||||
#define call(addr) ((void (*)(...))addr)
|
#define call(addr) ((void (*)(...))addr)
|
||||||
#define MEM1_START 0x80000000
|
|
||||||
#define MEM1_END 0x81800000
|
|
||||||
#define CODEHANDLER 0x800018A8
|
#define CODEHANDLER 0x800018A8
|
||||||
#define GCT_MAGIC 0x00D0C0DE
|
#define GCT_MAGIC 0x00D0C0DE
|
||||||
|
|
||||||
typedef unsigned int u32;
|
using u32 = unsigned int;
|
||||||
typedef unsigned short u16;
|
using u16 = unsigned short;
|
||||||
typedef unsigned char u8;
|
using u8 = unsigned char;
|
||||||
typedef int s32;
|
using s32 = int;
|
||||||
typedef short s16;
|
using s16 = short;
|
||||||
typedef char s8;
|
using s8 = char;
|
||||||
|
using f32 = float;
|
||||||
|
using f64 = double;
|
||||||
|
|
||||||
__attribute__((noreturn)) int main();
|
__attribute__((noreturn)) int main();
|
||||||
|
|
||||||
struct CodeList {
|
struct CodeList {
|
||||||
u16 mBaseASM;
|
|
||||||
u16 mUpperBase;
|
u16 mBaseASM;
|
||||||
u16 mOffsetASM;
|
u16 mUpperBase;
|
||||||
u16 mLowerOffset;
|
u16 mOffsetASM;
|
||||||
|
u16 mLowerOffset;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Info {
|
struct Info {
|
||||||
const u32 allocsize;
|
|
||||||
const u32 loaderSize;
|
const u32 allocsize;
|
||||||
const u32 handlerSize;
|
const u32 loaderSize;
|
||||||
const u32 codeSize;
|
const u32 handlerSize;
|
||||||
const u32* codehandlerHook;
|
const u32 codeSize;
|
||||||
const u32 wiiVIHook[4];
|
const u32* codehandlerHook;
|
||||||
const u32 gcnVIHook[8];
|
const u32 crypted;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DiscInfo {
|
class DiscHeader {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum class TVMODE {
|
||||||
|
NTSC,
|
||||||
|
PAL,
|
||||||
|
DEBUG,
|
||||||
|
DEBUGPAL,
|
||||||
|
MPAL,
|
||||||
|
PAL60
|
||||||
|
};
|
||||||
|
struct MetaData {
|
||||||
|
|
||||||
const u8 mDiscID; //0x0000
|
const u8 mDiscID; //0x0000
|
||||||
const u16 mGameCode; //0x0001
|
const u16 mGameCode; //0x0001
|
||||||
const u8 mRegionCode; //0x0003
|
const u8 mRegionCode; //0x0003
|
||||||
|
@ -54,180 +77,260 @@ struct DiscInfo {
|
||||||
const u8 _00[12]; //0x000A
|
const u8 _00[12]; //0x000A
|
||||||
const u32 mWiiMagic; //0x0018
|
const u32 mWiiMagic; //0x0018
|
||||||
const u32 mGCNMagic; //0x001C
|
const u32 mGCNMagic; //0x001C
|
||||||
const u32 _01[2]; //0x0020
|
const u32 mNinBootCode; //0x0020
|
||||||
u32 mRAMSize; //0x0028
|
const u32 mAppVersion; //0x0024
|
||||||
const u32 _02[2]; //0x002C
|
const u32 mPhysicalRAMSize; //0x0028
|
||||||
u32* mOSArenaHi; //0x0034
|
const u32 mBoardModel; //0x002C
|
||||||
u32* mFstPointer; //0x0038
|
u8* mOSArenaLo; //0x0030
|
||||||
|
u8* mOSArenaHi; //0x0034
|
||||||
|
u32* mFstStart; //0x0038
|
||||||
u32 mFstSize; //0x003C
|
u32 mFstSize; //0x003C
|
||||||
u32 _03[0xB4 / 4]; //0x0040
|
u32 mDebuggerPresent; //0x0040
|
||||||
u32* mTranslation; //0x00F4
|
const u32 mDebuggerExceptionMask; //0x0044
|
||||||
u32 _04[0x3018 / 4];
|
void* mExceptionHookDest; //0x0048
|
||||||
u32 mWiiHeap;
|
const u32 mExceptionReturn; //0x004C
|
||||||
|
u32 _01[0x10 / 4]; //0x0050
|
||||||
|
u32 mDebuggerHook[0x24 / 4]; //0x0060
|
||||||
|
u32 _02[0x3C / 4]; //0x0084
|
||||||
|
u32 mCurrentOSContext; //0x00C0
|
||||||
|
u32 mPreviousOSMask; //0x00C4
|
||||||
|
u32 mCurrentOSMask; //0x00C8
|
||||||
|
TVMODE mTVMode; //0x00CC
|
||||||
|
u32 mARAMSize; //0x00D0
|
||||||
|
void* mCurOSContextLogical; //0x00D4
|
||||||
|
void* mDefaultOSThreadLogical; //0x00D8
|
||||||
|
u32* mThreadQueueHead; //0x00DC
|
||||||
|
u32* mThreadQueueTail; //0x00E0
|
||||||
|
u32* mCurrentOSThread; //0x00E4
|
||||||
|
u32 mDebuggerSize; //0x00E8
|
||||||
|
u32* mDebuggerMonitorLoc; //0x00EC
|
||||||
|
u32 mSimulatedMemSize; //0x00F0
|
||||||
|
u8* mBi2HeaderLoc; //0x00F4
|
||||||
|
u32 mBusClockSpeed; //0x00F8
|
||||||
|
u32 mCPUClockSpeed; //00x00FC
|
||||||
|
u32 _04[0x3010 / 4];
|
||||||
|
u8* mWiiHeap; //0x3110
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
static MetaData sMetaData;
|
||||||
|
|
||||||
|
enum class CONSOLETYPE { Gamecube, Wii, Unknown };
|
||||||
|
|
||||||
|
inline u32 getGameID() { return ((u32)sMetaData.mDiscID << 24) | ((u32)sMetaData.mGameCode << 8) | ((u32)sMetaData.mRegionCode); }
|
||||||
|
inline u16 getMakerID() { return sMetaData.mMakerCode; }
|
||||||
|
inline u8 getDiscNumber() { return sMetaData.mDiscNumber; }
|
||||||
|
inline u8 getDiscVersion() { return sMetaData.mDiscVersion; }
|
||||||
|
|
||||||
|
CONSOLETYPE detectHomeConsole()
|
||||||
|
{
|
||||||
|
if (sMetaData.mGCNMagic) {
|
||||||
|
return CONSOLETYPE::Gamecube;
|
||||||
|
}
|
||||||
|
else if (sMetaData.mWiiMagic) {
|
||||||
|
return CONSOLETYPE::Wii;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CONSOLETYPE::Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setHeap(u32 alloc)
|
||||||
|
{
|
||||||
|
if (sMetaData.mBi2HeaderLoc < sMetaData.mOSArenaHi) {
|
||||||
|
sMetaData.mOSArenaHi = sMetaData.mBi2HeaderLoc - alloc;
|
||||||
|
if (this->detectHomeConsole() == DiscHeader::CONSOLETYPE::Wii) {
|
||||||
|
sMetaData.mWiiHeap = sMetaData.mBi2HeaderLoc - alloc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sMetaData.mOSArenaHi = sMetaData.mWiiHeap - alloc;
|
||||||
|
if (this->detectHomeConsole() == DiscHeader::CONSOLETYPE::Wii) {
|
||||||
|
sMetaData.mWiiHeap -= alloc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static DiscHeader sDisc;
|
||||||
Info gpModInfo = {
|
Info gpModInfo = {
|
||||||
0, /*This is the code allocation*/
|
0x48454150,
|
||||||
0, /*This is the size of the GeckoLoader*/
|
0x4C53495A,
|
||||||
0, /*This is the size of the codehandler*/
|
0x4853495A,
|
||||||
0, /*This is the size of the GeckoLoader + the codelist*/
|
0x4353495A,
|
||||||
0, /*This is the codehandler hook address*/
|
(const u32*)0x484F4F4B,
|
||||||
{ 0x7CE33B78, 0x38870034, 0x38A70038, 0x38C7004C },
|
0x43525054,
|
||||||
{ 0x7C030034, 0x38830020, 0x5485083C, 0x7C7F2A14, 0xA0030000, 0x7C7D2A14, 0x20A4003F, 0xB0030000 },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
DiscInfo* gpDiscResources = (DiscInfo*)MEM1_START;
|
namespace Memory {
|
||||||
|
|
||||||
static inline void flushAddr(void* addr)
|
static void memcpy(u8* to, u8* from, s32 size)
|
||||||
{
|
{
|
||||||
dcbf(addr);
|
for (s32 i = 0; i < size; ++i) {
|
||||||
icbi(addr);
|
*to++ = *from++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline void directWrite(u32* addr, u32 value)
|
namespace Cache {
|
||||||
{
|
|
||||||
*addr = value;
|
|
||||||
flushAddr(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*This constructs a branch instruction. &TO = ((TO - FROM) & MAX_OFFSET) | BRANCH_TYPE | !!isLink*/
|
static inline void flushAddr(void* addr)
|
||||||
static inline void directBranchEx(void* addr, void* ptr, bool lk)
|
{
|
||||||
{
|
dcbf(addr);
|
||||||
directWrite((u32*)(addr), ((((u32)(ptr) - (u32)(addr)) & 0x3ffffff) | 0x48000000 | !!lk));
|
icbi(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32* findArrayInstance(u32* start, const u32 end, u32 arrayLength, const u32* hookData)
|
static void flushRange(u8* addr, s32 size)
|
||||||
{
|
{
|
||||||
u32 index = 0;
|
size += 31 + (((u32)addr & 31) > 0);
|
||||||
|
|
||||||
/*Loop through the games RAM, make sure we don't find our own hook data by accident*/
|
for (u32 i = 0; i < (size >> 5); ++i) {
|
||||||
for (u32 i = 0; (u32)&start[i] < end; ++i) {
|
flushAddr((void*)(addr + (i << 5)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Direct {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static inline void write(T* addr, T value)
|
||||||
|
{
|
||||||
|
*addr = value;
|
||||||
|
Cache::flushAddr(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*This constructs a branch instruction. &TO = ((TO - FROM) & MAX_OFFSET) | BRANCH_TYPE | !!isLink*/
|
||||||
|
static inline void branch(void* addr, void* to, bool lk)
|
||||||
|
{
|
||||||
|
Direct::write<u32>((u32*)(addr), ((((u32)(to)-(u32)(addr)) & 0x3ffffff) | 0x48000000 | lk));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Search {
|
||||||
|
|
||||||
|
static u32* array(u32* start, u32* end, u32 arrayLength, const u32* hookData)
|
||||||
|
{
|
||||||
|
u32 index = 0;
|
||||||
|
|
||||||
|
/*Loop through the games RAM, make sure we don't find our own hook data by accident*/
|
||||||
|
for (u32 i = 0; &start[i] < end; ++i) {
|
||||||
/*If the data matches, increase the index counter and continue search,
|
/*If the data matches, increase the index counter and continue search,
|
||||||
else set index to 0 and continue searching*/
|
else set index to 0 and continue searching*/
|
||||||
if (start[i] == hookData[index])
|
if (start[i] == hookData[index])
|
||||||
++index;
|
++index;
|
||||||
else
|
else
|
||||||
index = 0;
|
index = 0;
|
||||||
|
|
||||||
/*If the data has matched the whole array, return the address of the match*/
|
/*If the data has matched the whole array, return the address of the match*/
|
||||||
if (index >= (arrayLength) && ((u32)&start[i] < (u32)&gpModInfo || (u32)&start[i] > (u32)&gpModInfo + sizeof(Info)))
|
if (index >= (arrayLength) && (&start[i] < (u32*)&gpModInfo || &start[i] > (u32*) & gpModInfo + sizeof(Info)))
|
||||||
return (u32*)&start[i];
|
return &start[i];
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u32* findU32Instance(u32* start, u32 end, u32 hookData)
|
template <typename T>
|
||||||
{
|
static T* single(T* start, T* end, T hookData)
|
||||||
for (u32 i = 0; (u32)&start[i] < end; ++i) {
|
|
||||||
if (start[i] == hookData) {
|
|
||||||
return (u32*)&start[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Find VI hook for Game*/
|
|
||||||
static inline u32* findVIHook(u32* start, const u32 end)
|
|
||||||
{
|
|
||||||
const u32* hookData;
|
|
||||||
u32 arrayLength;
|
|
||||||
|
|
||||||
/*If the game is built for the Wii, set the hookdata to be the Wii variant*/
|
|
||||||
if (gpDiscResources->mWiiMagic) {
|
|
||||||
hookData = (const u32*)gpModInfo.wiiVIHook;
|
|
||||||
arrayLength = sizeof(gpModInfo.wiiVIHook) / sizeof(u32);
|
|
||||||
} else /*The game is built for the GCN, set the hookdata to be the GCN variant*/
|
|
||||||
{
|
{
|
||||||
hookData = (const u32*)gpModInfo.gcnVIHook;
|
for (u32 i = 0; &start[i] < end; ++i) {
|
||||||
arrayLength = sizeof(gpModInfo.gcnVIHook) / sizeof(u32);
|
if (start[i] == hookData) {
|
||||||
}
|
return &start[i];
|
||||||
return findArrayInstance(start, end, arrayLength, hookData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Call this after findFunction, finds the address of the first instance
|
|
||||||
of value hookInstruction, and hooks it to the pointer hookTo*/
|
|
||||||
static inline void hookFunction(u32* start, u32 hookInstruction, u32 hookTo, bool isLink)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
while (start[i] != hookInstruction) {
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
directBranchEx((u32*)(&start[i]), (void*)(hookTo), isLink);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Reallocate the games internal memory heap based on the console
|
|
||||||
the game is for, to make space for our codes*/
|
|
||||||
static inline void setHeap(u32 alloc)
|
|
||||||
{
|
|
||||||
if (gpDiscResources->mTranslation < gpDiscResources->mOSArenaHi) {
|
|
||||||
if (gpDiscResources->mWiiMagic) {
|
|
||||||
gpDiscResources->mTranslation = (u32*)((u32)gpDiscResources->mTranslation - alloc);
|
|
||||||
gpDiscResources->mWiiHeap = (u32)gpDiscResources->mTranslation;
|
|
||||||
} else {
|
|
||||||
gpDiscResources->mTranslation = (u32*)((u32)gpDiscResources->mTranslation - alloc);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (gpDiscResources->mWiiMagic) {
|
|
||||||
gpDiscResources->mOSArenaHi = (u32*)((u32)gpDiscResources->mWiiHeap - alloc);
|
|
||||||
gpDiscResources->mWiiHeap = (u32)gpDiscResources->mOSArenaHi;
|
|
||||||
} else {
|
|
||||||
gpDiscResources->mOSArenaHi = (u32*)((u32)gpDiscResources->mOSArenaHi - alloc);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*Call this after viHook, finds the address of the first instance
|
||||||
|
of targetVal, and hooks it to the pointer hookTo*/
|
||||||
|
static inline void hookFunction(u32* start, u32 targetVal, void* hookTo, bool lk)
|
||||||
|
{
|
||||||
|
Direct::branch(Search::single<u32>(start, start + 0x500, targetVal), hookTo, lk);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Crypt {
|
||||||
|
|
||||||
|
private:
|
||||||
|
u32 key;
|
||||||
|
|
||||||
|
u32 getKey()
|
||||||
|
{
|
||||||
|
u32 b1 = (this->key >> 24) & 0xFF;
|
||||||
|
u32 b2 = (this->key >> 16) & 0xFF;
|
||||||
|
u32 b3 = (this->key >> 8) & 0xFF;
|
||||||
|
u32 b4 = this->key & 0xFF;
|
||||||
|
b1 ^= b2;
|
||||||
|
b2 ^= b3;
|
||||||
|
b3 ^= b4;
|
||||||
|
return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setKey(u32 key)
|
||||||
|
{
|
||||||
|
u32 b1 = key & 0xFF;
|
||||||
|
u32 b2 = (key >> 8) & 0xFF;
|
||||||
|
u32 b3 = (key >> 16) & 0xFF;
|
||||||
|
u32 b4 = (key >> 24) & 0xFF;
|
||||||
|
b3 ^= b4;
|
||||||
|
b2 ^= b3;
|
||||||
|
b1 ^= b2;
|
||||||
|
this->key = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
Crypt(u32 key)
|
||||||
|
{
|
||||||
|
this->key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void xorCrypt(u32* dest, u32* buffer, u32 size)
|
||||||
|
{
|
||||||
|
u32 key = this->getKey();
|
||||||
|
|
||||||
|
for (u32 i = 0; i < size; ++i) {
|
||||||
|
dest[i] = buffer[i] ^ key;
|
||||||
|
key += i ^ key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Space : u32 { Start = 0x80000000, End = 0x81800000, Size = 0x1800000 };
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void memCopy(u32* to, u32* from, s32 size)
|
Memory::Crypt gpCryptor = { 0x43595054 };
|
||||||
|
|
||||||
|
static bool initMods()
|
||||||
{
|
{
|
||||||
for (s32 i = 0; i < size; ++i) {
|
sDisc.setHeap(gpModInfo.allocsize); /*Reallocate the internal heap*/
|
||||||
*to++ = *from++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void flushCacheRange(u8* addr, s32 size)
|
/*Change codelist pointer to the new address in the allocation*/
|
||||||
{
|
CodeList* codelistPointer = (CodeList*)((u32)&gpModInfo + sizeof(Info) + 0xFC);
|
||||||
if ((u32)addr & 31) size += 32;
|
codelistPointer->mUpperBase = (((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) >> 16) & 0xFFFF;
|
||||||
else size += 31;
|
codelistPointer->mLowerOffset = ((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) & 0xFFFF;
|
||||||
|
|
||||||
for (u32 i = 0; i < (size >> 5); ++i) {
|
/*Update the cache, so that the instructions fully update*/
|
||||||
flushAddr((void*)addr);
|
Memory::Cache::flushAddr(&codelistPointer->mBaseASM);
|
||||||
addr += 32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool initMods(DiscInfo* gpDiscResources)
|
/*Copy codelist to the new allocation*/
|
||||||
{
|
if (gpModInfo.crypted) {
|
||||||
setHeap(gpModInfo.allocsize); /*Reallocate the internal heap*/
|
Memory::memcpy(sDisc.sMetaData.mOSArenaHi, (u8*)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize);
|
||||||
|
gpCryptor.xorCrypt((u32*)(sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize), (u32*)((u8*)&gpModInfo + sizeof(Info) + gpModInfo.handlerSize + 4), gpModInfo.codeSize >> 2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Memory::memcpy(sDisc.sMetaData.mOSArenaHi, (u8*)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize + gpModInfo.codeSize);
|
||||||
|
}
|
||||||
|
|
||||||
/*Change codelist pointer to the new address in the allocation*/
|
Memory::Direct::branch((void*)gpModInfo.codehandlerHook, (void*)((u32)sDisc.sMetaData.mOSArenaHi + 0xA8), false);
|
||||||
CodeList* codelistPointer = (CodeList*)((u32)&gpModInfo + sizeof(gpModInfo) + 0xFC);
|
|
||||||
codelistPointer->mUpperBase = (((u32)gpDiscResources->mOSArenaHi + gpModInfo.handlerSize) >> 16) & 0xFFFF;
|
|
||||||
codelistPointer->mLowerOffset = ((u32)gpDiscResources->mOSArenaHi + gpModInfo.handlerSize) & 0xFFFF;
|
|
||||||
|
|
||||||
/*Copy codelist to the new allocation*/
|
Memory::Cache::flushRange((u8*)sDisc.sMetaData.mOSArenaHi, gpModInfo.handlerSize + gpModInfo.codeSize);
|
||||||
memCopy(gpDiscResources->mOSArenaHi, (u32*)((u32)&gpModInfo + sizeof(gpModInfo) + 4), (gpModInfo.handlerSize + gpModInfo.codeSize) >> 2);
|
return true;
|
||||||
|
|
||||||
/*Update the cache, so that the instructions fully update*/
|
|
||||||
flushAddr(&codelistPointer->mBaseASM);
|
|
||||||
|
|
||||||
if (!gpModInfo.codehandlerHook || *gpModInfo.codehandlerHook != 0x4E800020) {
|
|
||||||
u32* functionAddr = findVIHook((u32*)MEM1_START, MEM1_END);
|
|
||||||
if (functionAddr == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
hookFunction(functionAddr, 0x4E800020, (u32)gpDiscResources->mOSArenaHi + 0xA8, false);
|
|
||||||
} else {
|
|
||||||
directBranchEx((void*)gpModInfo.codehandlerHook, (void*)((u32)gpDiscResources->mOSArenaHi + 0xA8), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
flushCacheRange((u8*)gpDiscResources->mOSArenaHi, gpModInfo.handlerSize + gpModInfo.codeSize);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
if ((gpDiscResources->mWiiMagic || gpDiscResources->mGCNMagic) && initMods(gpDiscResources) == true)
|
if ((sDisc.detectHomeConsole() != DiscHeader::CONSOLETYPE::Unknown) && initMods() == true)
|
||||||
call((void*)((u32)(gpDiscResources->mOSArenaHi) + 0xA8))(); /*Call the codehandler if successful*/
|
call((void*)((u32)(sDisc.sMetaData.mOSArenaHi) + 0xA8))(); /*Call the codehandler if successful*/
|
||||||
|
|
||||||
call(0xDEADBEEF)(); /*Call the game start*/
|
call(0x4948494C)(); /*Call the game start*/
|
||||||
}
|
}
|
19
setup.py
Normal file
19
setup.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import sys
|
||||||
|
from cx_Freeze import setup, Executable
|
||||||
|
|
||||||
|
options = {
|
||||||
|
'build_exe': {
|
||||||
|
'excludes': ['tkinter']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
executables = [
|
||||||
|
Executable('GeckoLoader.py'),
|
||||||
|
]
|
||||||
|
|
||||||
|
setup(name='GeckoLoader',
|
||||||
|
version='v6.0.0',
|
||||||
|
description='DOL Patcher for extending the codespace of Wii/GC games',
|
||||||
|
executables=executables,
|
||||||
|
options=options
|
||||||
|
)
|
Reference in a new issue