1
0
Fork 0

Improved hook related features

This commit is contained in:
JoshuaMKW 2020-08-24 05:10:23 -05:00
parent d0e7b8c423
commit 348ef19294
5 changed files with 377 additions and 238 deletions

View file

@ -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:

View file

@ -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
View file

@ -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, ''

View file

@ -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
View 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
)