diff --git a/codehandler.bin b/codehandler.bin deleted file mode 100644 index 7a2bc3b..0000000 Binary files a/codehandler.bin and /dev/null differ diff --git a/loader.c b/loader.c deleted file mode 100644 index 741e1b2..0000000 --- a/loader.c +++ /dev/null @@ -1,215 +0,0 @@ -/*Credits to riidefi for hook code, cache asm, and teaching me C*/ - -#define dcbst(_val) asm volatile("dcbst 0, %0" \ - : \ - : "r"(_val)) -#define dcbf(_val) asm volatile("dcbf 0, %0" \ - : \ - : "r"(_val)) -#define icbi(_val) asm volatile("icbi 0, %0" \ - : \ - : "r"(_val)) - -#define callFunction(addr) ((void (*)())addr)() - -#define FALSE 0 -#define TRUE 1 -#define NULL 0 - -typedef unsigned int u32; -typedef unsigned short u16; -typedef unsigned char u8; -typedef int s32; -typedef short s16; -typedef char s8; -typedef u32 BOOL; -typedef u32 unk32; - -__attribute__((noreturn)) int main(); - -enum { - MEM1_START = 0x80000000, - MEM1_END = 0x81800000, - CODEHANDLER_ENTRY = 0x800018A8, - GAME_ENTRY = 0xDEADBEEF, - GCT_MAGIC = 0x00D0C0DE -}; - -struct Info { - const u32 allocsize; - const u32 _loaderSize; - const u32 _loaderFullSize; - struct CodeList* _codelistPointer; - const u32 _wiiVIHook[4]; - const u32 _gcnVIHook[8]; - const u32 otherModPointer[4]; -}; - -struct CodeList { - u16 mBaseASM; - u16 mUpperBase; - u16 mOffsetASM; - u16 mLowerOffset; -}; - -struct DiscInfo { - const u8 mDiscID; - const u16 mGameCode; - const u8 mRegionCode; - const u16 mMakerCode; - const u8 mDiscNumber; - const u8 mDiscVersion; - const u8 mAudioStreaming; - const u8 mStreamBufferSize; - const u8 mUnknown[12]; - const u32 mWiiMagic; - const u32 mGCNMagic; - const u32 mUnknown2[2]; - u32 mRAMSize; - const u32 mUnknown3[2]; - u32* mHeapPointer; - u32 mHeapMirror; - u32 mFstSize; - u32 mData[(0x3110 - 0x40) / 4]; - u32 mWiiHeap; -}; - -struct Info gInfo = { - .allocsize = 0, /*This is the code allocation*/ - ._loaderSize = 0, /*This is the size of the GeckoLoader*/ - ._loaderFullSize = 0, /*This is the size of the GeckoLoader + the codelist*/ - ._codelistPointer = (struct CodeList*)0x800018F8, /*This points to where the codelist address is set in the codehandler*/ - ._wiiVIHook = { 0x7CE33B78, 0x38870034, 0x38A70038, 0x38C7004C }, - ._gcnVIHook = { 0x7C030034, 0x38830020, 0x5485083C, 0x7C7F2A14, 0xA0030000, 0x7C7D2A14, 0x20A4003F, 0xB0030000 }, -}; - -//struct Info* infoPointer = &gInfo; - -static inline void flushAddr(void* addr) -{ - dcbf(addr); - icbi(addr); -} - -static inline void directWrite(u32* addr, u32 value) -{ - *addr = value; - flushAddr(addr); -} - -/*This constructs a branch instruction. &TO = ((TO - FROM) & MAX_OFFSET) | BRANCH_TYPE | !!isLink*/ -static inline void directBranchEx(void* addr, void* ptr, BOOL lk) -{ - directWrite((u32*)(addr), ((((u32)(ptr) - (u32)(addr)) & 0x3ffffff) | 0x48000000 | !!lk)); -} - -static inline u32* findArrayInstance(u32* start, const u32 end, const 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; (u32)&start[i] < end; ++i) { - /*If the data matches, increase the index counter and continue search, - else set index to 0 and continue searching*/ - if (start[i] == hookData[index]) - ++index; - else - index = 0; - - /*If the data has matched the whole array, return the address of the match*/ - if (index >= (arrayLength) && ((u32)&start[i] < (u32)&gInfo || (u32)&start[i] > (u32)&gInfo + sizeof(gInfo))) { - return &start[i]; - } - } - return NULL; -} - -static inline u32* findU32Instance(u32* start, u32 end, u32 hookData) -{ - for (u32 i = 0; (u32)&start[i] < end; ++i) { - if (start[i] == hookData) { - return &start[i]; - } - } - return NULL; -} - -/*Find VI hook for Game*/ -static inline u32* findVIHook(struct DiscInfo* discResources, struct Info* infoPointer, u32* start, const u32 end) -{ - volatile const u32* hookData; - volatile u32 arrayLength; - - /*If the game is built for the Wii, set the hookdata to be the Wii variant*/ - if (discResources->mWiiMagic) { - hookData = (const u32*)infoPointer->_wiiVIHook; - arrayLength = sizeof(infoPointer->_wiiVIHook) / sizeof(u32); - } else /*The game is built for the GCN, set the hookdata to be the GCN variant*/ - { - hookData = (const u32*)infoPointer->_gcnVIHook; - arrayLength = sizeof(infoPointer->_gcnVIHook) / sizeof(u32); - } - return findArrayInstance(start, end, (const u32)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(volatile 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(struct DiscInfo* discResources, u32 alloc) -{ - if (discResources->mWiiMagic) { - discResources->mHeapPointer = (u32*)((u32)discResources->mWiiHeap - alloc); - discResources->mWiiHeap = (u32)discResources->mHeapPointer; - } else { - discResources->mHeapPointer = (u32*)((u32)discResources->mHeapPointer - alloc); - } -} - -static inline void memCopy(u32* to, u32* from, s32 size) -{ - for (s32 i = 0; i < size; ++i) { - to[i] = from[i]; - } -} - -static inline BOOL initMods(struct DiscInfo* discResources) -{ - setHeap(discResources, gInfo.allocsize); /*Reallocate the internal heap*/ - s32 sizeDiff = (gInfo._loaderFullSize - gInfo._loaderSize) / 4; /*Calculate size of codelist*/ - - /*Copy codelist to the new allocation*/ - memCopy(discResources->mHeapPointer, findU32Instance((u32*)&gInfo, MEM1_END, GCT_MAGIC), sizeDiff); - - /*Change codelist pointer to the new address in the allocation*/ - gInfo._codelistPointer->mUpperBase = ((u32)discResources->mHeapPointer >> 16) & 0xFFFF; - gInfo._codelistPointer->mLowerOffset = (u32)(discResources->mHeapPointer) & 0xFFFF; - - /*Update the cache, so that the instructions fully update*/ - flushAddr(&gInfo._codelistPointer->mBaseASM); - - u32* functionAddr = findVIHook(discResources, &gInfo, (u32*)MEM1_START, MEM1_END); - if (functionAddr == NULL) return FALSE; - hookFunction(functionAddr, 0x4E800020, CODEHANDLER_ENTRY, FALSE); - return TRUE; -} - -int main() -{ - struct DiscInfo* discResources = (struct DiscInfo*)MEM1_START; - if (discResources->mWiiMagic || discResources->mGCNMagic) { - if (initMods(discResources) == TRUE) { - callFunction(CODEHANDLER_ENTRY); /*Call the codehandler if successful*/ - } - } - callFunction(GAME_ENTRY); /*Call the game start*/ -} diff --git a/main.py b/main.py deleted file mode 100644 index 616097b..0000000 --- a/main.py +++ /dev/null @@ -1,375 +0,0 @@ -#Written by JoshuaMK 2020 - -import sys -import os -import time -import re - -try: - import argparse - import chardet -except ImportError as IE: - print(IE) - sys.exit(1) - -try: - import colorama - from colorama import Fore, Style - colorama.init() - TRESET = Style.RESET_ALL - TGREEN = Fore.GREEN - TGREENLIT = Style.BRIGHT + Fore.GREEN - TYELLOW = Fore.YELLOW - TYELLOWLIT = Style.BRIGHT + Fore.YELLOW - TRED = Fore.RED - TREDLIT = Style.BRIGHT + Fore.RED - -except ImportError: - TRESET = '' - TGREEN = '' - TGREENLIT = '' - TYELLOW = '' - TYELLOWLIT = '' - TRED = '' - TREDLIT = '' - - - -def resource_path(relative_path): - """ Get absolute path to resource, works for dev and for PyInstaller """ - base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__))) - return os.path.join(base_path, relative_path) - -def geckoParser(geckoText, parseAll): - - geckoMagic = '00D0C0DE00D0C0DE' - geckoTerminate = 'F000000000000000' - with open(geckoText, 'rb') as gecko: - result = chardet.detect(gecko.read()) - encodeType = result['encoding'] - - with open(geckoText, 'r', encoding=encodeType) as gecko: - data = gecko.readlines() - geckoCodes = '' - - for line in data: - if parseAll.lower() == 'all': - geckoLine = re.findall(r'[A-F0-9]{8}\s[A-F0-9]{8}', line, re.IGNORECASE) - elif parseAll.lower() == 'active': - geckoLine = re.findall(r'\*\s[A-F0-9]{8}\s[A-F0-9]{8}', line, re.IGNORECASE) - else: - geckoLine = re.findall(r'\*\s[A-F0-9]{8}\s[A-F0-9]{8}', line, re.IGNORECASE) - - geckoLine = ''.join(geckoLine) - geckoLine = re.sub(r'\s+', '', geckoLine) - geckoCodes = geckoCodes + geckoLine.replace('*', '') - - geckoCodes = geckoMagic + geckoCodes + geckoTerminate - geckoSize = '{:08X}'.format(len(bytes.fromhex(geckoCodes))).lstrip('0') - - return [bytes.fromhex(geckoCodes), geckoSize] - -def build(gctFile, dolFile, size, isText): - with open(resource_path('geckoloader.bin'), 'rb') as code, open(r'{}'.format(dolFile), 'rb') as dol, open(r'{}'.format(gctFile), 'rb') as gecko, open(resource_path('codehandler.bin'), 'rb') as handler, open('tmp.bin', 'wb+') as tmp, open(os.path.join('BUILD', os.path.basename(dolFile)), 'wb+') as final: - - if int(get_size(dol).hex(), 16) < int('0x100', 16): - os.remove('tmp.bin') - parser.error('DOL header is corrupted. Please provide a clean file') - - dol.seek(0) - - '''Initialize the new DOL file''' - - final.write(dol.read()) - - '''Initialize the sme-code loader''' - - tmp.write(code.read()) - code.seek(0, 0) - tmp.seek(0, 0) - - '''Search for main entry of loader''' - - entryIndex = 0 - sample = tmp.read(4) - while sample: - if sample == ENTRY: - tmp.seek(-4, 1) - tmp.write(bytes.fromhex('7C0802A6')) - break - entryIndex += 4 - sample = tmp.read(4) - tmp.seek(0) - - '''Get BSS section for insert''' - - final.seek(int('D8', 16)) - BSS = int(final.read(4).hex(), 16) - BSS_length = int(final.read(4).hex(), 16) - dump_address = '{:08X}'.format(int(BSS + (BSS_length / 2)))[:-2] + '00' - _START = bytes.fromhex('{:08X}'.format(int(dump_address, 16) + entryIndex)) - cLoader = bytes.fromhex(dump_address) - - '''Get address split for later''' - - upperAddr, lowerAddr = dump_address[:int(len(dump_address)/2)], dump_address[int(len(dump_address)/2):] - - '''Get code initialize address''' - - final.seek(int('E0', 16)) - _init = [final.read(2), final.read(2)] - - '''Patch the values for the addresses and such''' - - heaped = False - sized = False - fsized = False - - gUpperAddr = bytes.fromhex(upperAddr) - - if isText == True: - geckoCheats = geckoParser(gctFile, args.txtcodes) - - while heaped == False or sized == False or fsized == False: - try: - sample = tmp.read(4) - if sample == HEAP: #Found keyword "HEAP". Goes with the resize of the heap - if not heaped: - tmp.seek(-4, 1) - gInfo = tmp.tell() - if int(lowerAddr, 16) + gInfo > int('7FFF', 16): #Absolute addressing - gUpperAddr = bytes.fromhex('{:04X}'.format(int(upperAddr, 16) + 1)) - if size == '0' or size == '': - if isText == False: - size = get_size(gecko).hex().upper() - else: - size = geckoCheats[1] - tmp.write(bytes.fromhex('{:08X}'.format(int(size, 16)))) - heaped = True - - elif sample == LOADERSIZE: #Found keyword "LSIZ". Goes with the size of the loader - if not sized: - tmp.seek(-4, 1) - tmp.write(get_size(code)) - sized = True - - elif sample == FULLSIZE: #Found keyword "FSIZ". Goes with the size of the loader + codes - if not fsized: - tmp.seek(-4, 1) - code.seek(0, 2) - gecko.seek(0, 2) - if isText == True: - tmp.write(get_size(code, int(geckoCheats[1], 16))) - else: - tmp.write(get_size(code, gecko.tell())) - fsized = True - except Exception as err: - print(err) - sys.exit(1) - - '''Patch all load/store offsets to data''' - - tmp.seek(0) - sample = tmp.read(2) - while sample: - if sample == GH: - tmp.seek(-2, 1) - tmp.write(gUpperAddr) - elif sample == GL: - tmp.seek(-2, 1) - tmp.write(bytes.fromhex('{:04X}'.format(int(lowerAddr, 16) + gInfo))) - elif sample == IH: - tmp.seek(-2, 1) - tmp.write(_init[0]) - elif sample == IL: - tmp.seek(-2, 1) - tmp.write(_init[1]) - sample = tmp.read(2) - - final.seek(0, 2) - tmp.seek(0) - gecko.seek(0) - - dol_handler_offset = get_size(final) - final.write(handler.read()) - time.sleep(0.01) - dol_sme_offset = get_size(final) - - final.write(tmp.read()) - time.sleep(0.01) - - if isText == False: - final.write(gecko.read()) - else: - final.write(geckoCheats[0]) - final.seek(0, 0) - - status = False - i = 0 - - while i < 6: - textOffset = int(final.read(4).hex(), 16) - if textOffset == 0: - status = True - offset = i * 4 - - '''Write offset to each section in DOL file header''' - final.seek(-4, 1) - final.write(dol_handler_offset) - final.write(dol_sme_offset) - - final.seek(int('48', 16) + offset) - - '''Write in game memory addresses for each section in DOL file header''' - final.write(bytes.fromhex('80001800')) - final.write(cLoader) - final.seek(int('E0', 16)) - - '''Write game entry in DOL file header''' - final.write(_START) - - '''Get size of GeckoLoader + gecko codes, and the codehandler''' - handler_size = get_size(handler) - - tmp.seek(0, 2) - gecko.seek(0, 2) - - if isText == True: - sme_code_size = get_size(tmp, int(geckoCheats[1], 16)) - else: - sme_code_size = get_size(tmp, gecko.tell()) - - '''Write size of each section into DOL file header''' - final.seek(int('90', 16) + offset) - final.write(handler_size) - final.write(sme_code_size) - break - else: - i += 1 - - if status == False: - os.remove('tmp.bin') - parser.error(TREDLIT + 'Not enough text sections to patch the DOL file! Potentially due to previous mods?\n' + TRESET) - - if isText == False: - if int(size, 16) < int(get_size(gecko).hex(), 16): - print(TYELLOW + '\n :: WARNING: Allocated codespace was smaller than the given codelist. The game will crash if run' + TRESET) - else: - if int(size, 16) < int(geckoCheats[1], 16): - print(TYELLOW + '\n :: WARNING: Allocated codespace was smaller than the given codelist. The game will crash if run' + TRESET) - - if args.quiet: - return - - if int(size, 16) > int('70000', 16): - print(TYELLOW + '\n :: WARNING: Allocations beyond 70000 will crash certain games. You allocated 0x{}'.format(size) + TRESET) - - elif int(size, 16) > int('40000', 16): - print(TYELLOWLIT + '\n :: HINT: Recommended allocation limit is 0x40000. You allocated 0x{}'.format(size) + TRESET) - - if isText == False: - codelistSize = get_size(gecko).hex().upper().lstrip('0') - else: - codelistSize = geckoCheats[1] - - if args.verbose >= 2: - print('') - info = [TGREENLIT + ' :: GeckoLoader set at address 0x{}, start of game modified to address 0x{}'.format(dump_address.upper(), _START.hex().upper()), - ' :: Game function "_init_registers" located at address 0x{}{}'.format(_init[0].hex(), _init[1].hex().upper()), - ' :: Code allocation is 0x{}; codelist size is 0x{}'.format(size.upper().lstrip('0'), codelistSize), - ' :: Of the 7 text sections in this DOL file, {} were already used'.format(i) + TRESET] - - for bit in info: - print(bit) - - elif args.verbose >= 1: - print('') - info = [TGREENLIT + ' :: GeckoLoader set at address 0x{}'.format(dump_address.upper()), - ' :: Code allocation is 0x{} in hex; codelist size is 0x{}'.format(size.upper().lstrip('0'), codelistSize) + TRESET] - - for bit in info: - print(bit) - return - -def get_size(file, offset=0): - """ Return a file's size in bytes """ - file.seek(0, 2) - return(bytes.fromhex('{:08X}'.format(file.tell() + offset))) - - -if __name__ == "__main__": - - isText = False - - parser = argparse.ArgumentParser(description='Process files and allocations for GeckoLoader') - parser.add_argument('file', help='First file') - parser.add_argument('file2', help='Second file') - parser.add_argument('--alloc', help='Define the size of the code allocation: --alloc hex') - parser.add_argument('-tc', '--txtcodes', help='What codes get parsed when a txt file is used.\n"ALL" makes all codes get parsed,\n"ACTIVE" makes only activated codes get parsed.', default='active') - 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') - - args = parser.parse_args() - - if args.alloc: - size = args.alloc.lstrip('0x') - try: - int(size, 16) - except: - parser.error('The allocation was invalid\n') - else: - size = '0' - - if os.path.splitext(args.file)[1].lower() == '.dol': - dolFile = args.file - elif os.path.splitext(args.file2)[1].lower() == '.dol': - dolFile = args.file2 - else: - parser.error('No dol file was passed\n') - - if os.path.splitext(args.file)[1].lower() == '.gct': - gctFile = args.file - isText = False - elif os.path.splitext(args.file)[1].lower() == '.txt': - gctFile = args.file - isText = True - elif os.path.splitext(args.file2)[1].lower() == '.gct': - gctFile = args.file2 - isText = False - elif os.path.splitext(args.file2)[1].lower() == '.txt': - gctFile = args.file2 - isText = True - else: - parser.error('Neither a gct or gecko text file was passed\n') - - time1 = time.time() - - HEAP = bytes.fromhex('48454150') - LOADERSIZE = bytes.fromhex('4C53495A') - FULLSIZE = bytes.fromhex('4653495A') - ENTRY = bytes.fromhex('454E5452') - GH = bytes.fromhex('4748') - GL = bytes.fromhex('474C') - IH = bytes.fromhex('4948') - IL = bytes.fromhex('494C') - MODFIELD = [bytes.fromhex('BBBBBBBB'), bytes.fromhex('CCCCCCCC'), bytes.fromhex('DDDDDDDD'), bytes.fromhex('EEEEEEEE')] - - try: - if not os.path.isdir('BUILD'): - os.mkdir('BUILD') - - if not os.path.isfile(dolFile): - parser.error(dolFile + ' Does not exist') - - if not os.path.isfile(gctFile): - parser.error(gctFile + ' Does not exist') - - build(gctFile, dolFile, size, isText) - - os.remove('tmp.bin') - if not args.quiet: - print('\n :: Compiled in {:0.4f} seconds!\n'.format(time.time() - time1)) - - except FileNotFoundError as err: - parser.error(err) - sys.exit(1)