diff --git a/popnhax/custom_categs.cc b/popnhax/custom_categs.cc index 4dbb764..7451fc3 100644 --- a/popnhax/custom_categs.cc +++ b/popnhax/custom_categs.cc @@ -19,6 +19,9 @@ #include "minhook/hde32.h" #include "minhook/include/MinHook.h" +uint8_t g_subcateg_count = 5; + +bool g_subcategmode = false; const char *g_categname = "Custom Tracks"; const char *g_categicon = "cate_13"; const char *g_categformat = "[ol:4][olc:d92f0d]%s"; @@ -38,6 +41,140 @@ uint32_t songlist_struct_addr = (uint32_t)&songlist_struct; void (*add_song_in_list)(); void (*categ_inject_songlist)(); + +void get_subcateg_size_impl() +{ + __asm("mov eax, 0"); +} + +char g_test_string[9] = "TOTOMCDO"; +void get_subcateg_name_impl() +{ + __asm("mov eax, _g_test_string"); +} + +uint32_t reimpl_value_1; +uint32_t reimpl_value_2; +void (*get_subcateg_size)() = &get_subcateg_size_impl; +void (*get_subcateg_name)() = &get_subcateg_name_impl; +void (*reimpl_func_1)(); +void (*reimpl_func_2)(); +void (*reimpl_func_3)(); +void (*reimpl_func_4)(); + +//this is a reimplementation of the event category generation function, modded to use popnhax internal subcategories +void categ_inject_songlist_reimpl() +{ + __asm("add esp, 0xC"); // cancel a sub esp 0xC that is added by this code for no reason + __asm("push 0xFFFFFFFF\n"); + __asm("push [_reimpl_value_1]\n"); + __asm("mov eax, dword ptr fs:[0]\n"); + __asm("push eax\n"); + __asm("sub esp, 0x10\n"); + __asm("push ebx\n"); + __asm("push ebp\n"); + __asm("push esi\n"); + __asm("push edi\n"); + __asm("mov eax, dword ptr ds:[_reimpl_value_2]\n"); + __asm("xor eax,esp\n"); + __asm("push eax\n"); + __asm("lea eax, dword ptr ss:[esp+0x24]\n"); + __asm("mov dword ptr fs:[0], eax\n"); + __asm("lea ebp, dword ptr ss:[esp+0x34]\n"); + __asm("xor ebx, ebx\n"); + __asm("mov dword ptr ss:[esp+0x34], ebx\n"); + __asm("subcateg_loop:\n"); + __asm("mov ecx, dword ptr ss:[ebp+0x08]\n"); + __asm("mov eax, ebx\n"); + + get_subcateg_size(); + + __asm("test eax, eax\n"); + __asm("je next_iter\n"); + __asm("push 0xD8\n"); + + reimpl_func_1(); + + __asm("mov ecx, eax\n"); + __asm("add esp, 0x04\n"); + __asm("mov dword ptr ss:[esp+0x14], ecx\n"); + __asm("xor eax, eax\n"); + __asm("mov dword ptr ss:[esp+0x2C], eax\n"); + __asm("cmp ecx, eax\n"); + __asm("je jump_point_1\n"); + __asm("mov eax, ebx\n"); + + get_subcateg_name(); + + __asm("mov edi, dword ptr ss:[ebp+0x04]\n"); + __asm("mov edx, dword ptr ss:[ebp+0x0C]\n"); + __asm("push eax\n"); + __asm("push ecx\n"); + __asm("mov ecx, dword ptr ss:[ebp+0x44]\n"); + + reimpl_func_2(); + + __asm("jump_point_1:\n"); + __asm("mov dword ptr ss:[esp+0x2C], 0xFFFFFFFF\n"); + __asm("mov ecx, dword ptr ss:[ebp+0xB4]\n"); + __asm("lea esi, dword ptr ss:[ebp+0xA8]\n"); + __asm("mov dword ptr ss:[esp+0x14], eax\n"); + __asm("test ecx, ecx\n"); + __asm("jne jump_point_2\n"); + __asm("xor edx, edx\n"); + __asm("jmp jump_point_3\n"); + __asm("jump_point_2:\n"); + __asm("mov edx, dword ptr ds:[esi+0x14]\n"); + __asm("sub edx, ecx\n"); + __asm("sar edx, 0x02\n"); + __asm("jump_point_3:\n"); + __asm("mov edi, dword ptr ds:[esi+0x10]\n"); + __asm("mov ebx, edi\n"); + __asm("sub ebx, ecx\n"); + __asm("sar ebx, 0x02\n"); + __asm("cmp ebx, edx\n"); + __asm("jae jump_point_4\n"); + __asm("mov dword ptr ds:[edi], eax\n"); + __asm("add edi, 0x04\n"); + __asm("mov dword ptr ds:[esi+0x10], edi\n"); + __asm("jmp jump_point_5\n"); + __asm("jump_point_4:\n"); + __asm("cmp ecx, edi\n"); + __asm("jbe jump_point_6\n"); + + reimpl_func_3(); + + __asm("jump_point_6:\n"); + __asm("mov eax, dword ptr ds:[esi]\n"); + __asm("push edi\n"); + __asm("push eax\n"); + __asm("lea eax, dword ptr ss:[esp+0x1C]\n"); + __asm("push eax\n"); + __asm("lea ecx, dword ptr ss:[esp+0x24]\n"); + __asm("push ecx\n"); + __asm("mov eax, esi\n"); + + reimpl_func_4(); + + __asm("jump_point_5:\n"); + __asm("mov ebx, dword ptr ss:[esp+0x34]\n"); + __asm("next_iter:\n"); + __asm("inc ebx\n"); + __asm("mov dword ptr ss:[esp+0x34], ebx\n"); + __asm("cmp ebx, [_g_subcateg_count]\n"); + __asm("jb subcateg_loop\n"); + __asm("mov ecx, dword ptr ss:[esp+0x24]\n"); + __asm("mov dword ptr fs:[0], ecx\n"); + __asm("pop ecx\n"); + __asm("pop edi\n"); + __asm("pop esi\n"); + __asm("pop ebp\n"); + __asm("pop ebx\n"); + __asm("add esp, 0x1C\n"); + __asm("ret 4\n"); +} + +//this replaces the category handling function ( add_song_in_list is a subroutine called by the game ) void categ_inject_songlist_impl() { __asm("push edx\n"); @@ -91,21 +228,25 @@ void hook_categ_title_printf() real_categ_title_printf(); } -void (*categ_listing_newsongs)(); +void (*categ_listing_event)(); void (*real_categ_listing)(); void hook_categ_listing() { __asm("cmp eax, 0x11\n"); __asm("jne categ_listing_ok\n"); - __asm("call [_categ_inject_songlist]\n"); + __asm("cmp byte ptr ds:[_g_subcategmode], 1\n"); + __asm("jne skip_push\n"); + __asm("push esi\n"); //push esi to prepare for subcategory mode (categ_inject_songlist points to the correct function) + __asm("skip_push:\n"); + __asm("call [_categ_inject_songlist]\n"); // __asm("categ_listing_ok:\n"); real_categ_listing(); } -static bool patch_custom_categ_simple(const char *g_game_dll_fn) { +static bool patch_custom_categ_simple(const char *game_dll_fn) { DWORD dllSize = 0; - char *data = getDllData(g_game_dll_fn, &dllSize); + char *data = getDllData(game_dll_fn, &dllSize); //patch format string for any category above 16 (prevent crash) { @@ -122,6 +263,31 @@ static bool patch_custom_categ_simple(const char *g_game_dll_fn) { (void **)&real_categ_title_printf); } + { + int64_t pattern_offset = search(data, dllSize, "\x83\xF8\x10\x77\x75\xFF\x24\x85", 8, 0); + if (pattern_offset == -1) { + LOG("popnhax: custom_categ: cannot find category jump table\n"); + return false; + } + + uint64_t patch_addr = (int64_t)data + pattern_offset + 0x05 + 0x75; + uint64_t function_call_addr = (int64_t)(data + pattern_offset + 0x05 + 69); + uint32_t function_offset = *((uint32_t*)(function_call_addr +0x01)); + uint64_t function_addr = function_call_addr+5+function_offset; + categ_listing_event = (void (*)())(function_addr); + +/* retrieve useful values from this function */ + reimpl_value_1 = *((uint32_t*)(function_addr +0x03)); + reimpl_value_2 = *((uint32_t*)(function_addr +0x16)); + reimpl_func_1 = (void (*)())( *((uint32_t*)(function_addr +0x49)) + (uint32_t)(function_addr +0x04 +0x49) ); + reimpl_func_2 = (void (*)())( *((uint32_t*)(function_addr +0x73)) + (uint32_t)(function_addr +0x04 +0x73) ); + reimpl_func_3 = (void (*)())( *((uint32_t*)(function_addr +0xBC)) + (uint32_t)(function_addr +0x04 +0xBC) ); + reimpl_func_4 = (void (*)())( *((uint32_t*)(function_addr +0xD1)) + (uint32_t)(function_addr +0x04 +0xD1) ); + + MH_CreateHook((LPVOID)patch_addr, (LPVOID)hook_categ_listing, + (void **)&real_categ_listing); + } + //add new category processing in jump table { int64_t pattern_offset = search(data, dllSize, "\x8B\x4D\x10\x8B\x5D\x0C\x8B\xF1", 8, 0); @@ -131,21 +297,25 @@ static bool patch_custom_categ_simple(const char *g_game_dll_fn) { } add_song_in_list = (void (*)())(data + pattern_offset - 0x12); - categ_inject_songlist = &categ_inject_songlist_impl; - } - - { - int64_t pattern_offset = search(data, dllSize, "\x83\xF8\x10\x77\x75\xFF\x24\x85", 8, 0); - if (pattern_offset == -1) { - LOG("popnhax: custom_categ: cannot find category jump table\n"); - return false; - } - - uint64_t patch_addr = (int64_t)data + pattern_offset + 0x05 + 0x75; - //categ_listing_newsongs = (void (*)())(data + pattern_offset + 0x05 + 0x0E); //TODO: replace this with my own function - - MH_CreateHook((LPVOID)patch_addr, (LPVOID)hook_categ_listing, - (void **)&real_categ_listing); + + if (g_subcategmode) + { + categ_inject_songlist = &categ_inject_songlist_reimpl; + //categ_inject_songlist = categ_listing_event; + /* + //create a copy of event categ handling function then mod it + categ_inject_songlist = (void(*)())malloc(256); + DWORD old_prot; + VirtualProtect((LPVOID)categ_inject_songlist, 256, PAGE_EXECUTE_READWRITE, &old_prot); + memcpy((LPVOID)categ_inject_songlist, (uint64_t*)categ_listing_event, 256); + VirtualProtect((LPVOID)categ_inject_songlist, 256, old_prot, &old_prot); + + uint32_t call_offset = (uint32_t*)categ_inject_songlist - (uint32_t*)categ_listing_event; + LOG("popnhax: call offset is %x\n",call_offset); + */ + } + else + categ_inject_songlist = &categ_inject_songlist_impl; } //create new song list @@ -178,14 +348,14 @@ static bool patch_custom_categ_simple(const char *g_game_dll_fn) { //bump category number from 16 to 17 - if (!find_and_patch_hex(g_game_dll_fn, "\x83\xFE\x11\x0F\x82\x59\xFE\xFF\xFF", 9, 2, "\x12", 1)) + if (!find_and_patch_hex(game_dll_fn, "\x83\xFE\x11\x0F\x82\x59\xFE\xFF\xFF", 9, 2, "\x12", 1)) { return false; } //patch getCategName for a 17th entry //make array one cell larger - if (!find_and_patch_hex(g_game_dll_fn, "\x83\xEC\x44\x98\xC7\x04\x24", 7, 2, "\x48", 1)) + if (!find_and_patch_hex(game_dll_fn, "\x83\xEC\x44\x98\xC7\x04\x24", 7, 2, "\x48", 1)) { return false; } @@ -193,13 +363,13 @@ static bool patch_custom_categ_simple(const char *g_game_dll_fn) { uint32_t categname_str_addr = (uint32_t)g_categname; char categname_patch_string[] = "\xC7\x44\x24\x44\x20\x00\x28\x10\x8B\x04\x84\x83\xC4\x48\xC3"; memcpy(categname_patch_string+4, &categname_str_addr, 4); - if (!find_and_patch_hex(g_game_dll_fn, "\x8B\x04\x84\x83\xC4\x44\xC3\xCC", 8, 0, categname_patch_string, 15)) + if (!find_and_patch_hex(game_dll_fn, "\x8B\x04\x84\x83\xC4\x44\xC3\xCC", 8, 0, categname_patch_string, 15)) { return false; } //patch getIconName for a 17th entry - if (!find_and_patch_hex(g_game_dll_fn, "\x83\xEC\x44\x98\xC7\x04\x24", 7, 2, "\x48", 1)) //2nd occurrence since first one just got patched + if (!find_and_patch_hex(game_dll_fn, "\x83\xEC\x44\x98\xC7\x04\x24", 7, 2, "\x48", 1)) //2nd occurrence since first one just got patched { return false; } @@ -207,7 +377,7 @@ static bool patch_custom_categ_simple(const char *g_game_dll_fn) { uint32_t categicon_str_addr = (uint32_t)g_categicon; char categicon_patch_string[] = "\xC7\x44\x24\x44\xDC\x00\x28\x10\x8B\x04\x84\x83\xC4\x48\xC3"; memcpy(categicon_patch_string+4, &categicon_str_addr, 4); - if (!find_and_patch_hex(g_game_dll_fn, "\x8B\x04\x84\x83\xC4\x44\xC3\xCC", 8, 0, categicon_patch_string, 15)) + if (!find_and_patch_hex(game_dll_fn, "\x8B\x04\x84\x83\xC4\x44\xC3\xCC", 8, 0, categicon_patch_string, 15)) { return false; } @@ -316,11 +486,13 @@ bool patch_custom_categs(const char *dllFilename, uint8_t mode) { if (mode == 1) { + g_subcategmode = true; return patch_custom_categ_simple(dllFilename); } if (mode == 2) { + g_subcategmode = true; subcateg_count = 0; load_databases(); }