diff --git a/dist/popnhax/popnhax.xml b/dist/popnhax/popnhax.xml index ea8830a..0e646ca 100644 --- a/dist/popnhax/popnhax.xml +++ b/dist/popnhax/popnhax.xml @@ -5,6 +5,8 @@ 0 0 + + 0 @@ -132,6 +134,10 @@ 0 0 + + 4000 + + 0 diff --git a/popnhax/config.h b/popnhax/config.h index df8c72b..5397ef7 100644 --- a/popnhax/config.h +++ b/popnhax/config.h @@ -14,6 +14,9 @@ struct popnhax_config { bool quick_retire; bool back_to_song_select; bool score_challenge; + uint8_t custom_categ; + uint16_t custom_categ_min_songid; + uint16_t custom_categ_max_songid; bool force_hd_timing; uint8_t force_hd_resolution; bool force_unlocks; diff --git a/popnhax/custom_categs.cc b/popnhax/custom_categs.cc index f175fef..0a50da1 100644 --- a/popnhax/custom_categs.cc +++ b/popnhax/custom_categs.cc @@ -19,49 +19,50 @@ #include "minhook/hde32.h" #include "minhook/include/MinHook.h" -uint8_t g_subcateg_count = 3; -uint32_t **subcategs; -char **subcateg_names; +uint32_t g_min_id = 4000; +uint32_t g_max_id = 0; typedef struct { - char *name; - uint32_t size; - uint32_t *songlist; + char *name; + uint32_t size; + uint32_t *songlist; } subcategory_s; subcategory_s* subcategories; -uint32_t subcateg_count = 0; +uint32_t g_subcateg_count = 0; struct property *load_prop_file(const char *filename); static bool subcateg_has_songid(uint32_t songid, subcategory_s* subcateg) { - for ( uint32_t i = 0; i < subcateg->size; i++ ) - { - if ( subcateg->songlist[i] == songid ) - return true; - } - return false; + for ( uint32_t i = 0; i < subcateg->size; i++ ) + { + if ( subcateg->songlist[i] == songid ) + return true; + } + return false; } static void add_song_to_subcateg(uint32_t songid, subcategory_s* subcateg) { - if ( songid >= 3000 && !subcateg_has_songid(songid, subcateg) ) - { - subcateg->songlist = (uint32_t *) realloc(subcateg->songlist, sizeof(uint32_t)*(++subcateg->size)); - subcateg->songlist[subcateg->size-1] = songid; - LOG("%s : %d (subcateg size %d)\n", subcateg->name, songid, subcateg->size); - } + if ( songid >= g_min_id + && (g_max_id == 0 || songid <= g_max_id) + && !subcateg_has_songid(songid, subcateg) ) + { + subcateg->songlist = (uint32_t *) realloc(subcateg->songlist, sizeof(uint32_t)*(++subcateg->size)); + subcateg->songlist[subcateg->size-1] = songid; + subcateg->songlist[subcateg->size-1] |= 0x00060000; // game wants this otherwise only easy difficulty will appear + } } static subcategory_s* get_subcateg(char *title) { - for (uint32_t i = 0; i < subcateg_count; i++) - { - if (strcmp(title, subcategories[i].name) == 0) - return &(subcategories[i]); - } - return NULL; + for (uint32_t i = 0; i < g_subcateg_count; i++) + { + if (strcmp(title, subcategories[i].name) == 0) + return &(subcategories[i]); + } + return NULL; } bool g_subcategmode = false; @@ -92,58 +93,58 @@ void (*categ_inject_songlist)(); struct songlist_struct_s *new_song_list = NULL; void get_subcateg_size_impl() { - __asm("push edx\n"); - __asm("mov _idx, eax\n"); + __asm("push edx\n"); + __asm("mov _idx, eax\n"); - tmp_size = subcategories[idx].size; - new_song_list = (struct songlist_struct_s*) songlist_struct_addr; - new_song_list->array_start = (uint32_t)&(subcategories[idx].songlist[0]); - new_song_list->array_end = (uint32_t)&(subcategories[idx].songlist[tmp_size]); + tmp_size = subcategories[idx].size; + new_song_list = (struct songlist_struct_s*) songlist_struct_addr; + new_song_list->array_start = (uint32_t)&(subcategories[idx].songlist[0]); + new_song_list->array_end = (uint32_t)&(subcategories[idx].songlist[tmp_size]); - __asm("mov eax, [_tmp_size]"); - __asm("mov ecx, _new_song_list"); - __asm("pop edx\n"); + __asm("mov eax, [_tmp_size]"); + __asm("mov ecx, _new_song_list"); + __asm("pop edx\n"); } uint32_t tmp_str_addr; void (*real_event_categ_generation)(); void hook_event_categ_generation() { - //chaine dans [esp+0x1C], on check si l'adresse correspond à l'une de nos subcateg - //si c'est pas le cas on s'en va sans rien faire - //si c'est le cas alors on met les coord de début et fin dans la struct_songlist et on envoie l'adresse de struct sur eax - __asm("mov _new_song_list, eax"); //save original intended value - __asm("push ecx"); - __asm("push edx"); - __asm("mov ebx, [esp+0x2C]\n"); - __asm("mov _tmp_str_addr, ebx\n"); - - for (uint32_t i = 0; i < subcateg_count; i++) - { - if ( (uint32_t)subcategories[i].name == tmp_str_addr ) - { - tmp_size = subcategories[i].size; - new_song_list = (struct songlist_struct_s*) songlist_struct_addr; - new_song_list->array_start = (uint32_t)&(subcategories[i].songlist[0]); - new_song_list->array_end = (uint32_t)&(subcategories[i].songlist[tmp_size]); - break; - } - } - __asm("pop edx"); - __asm("pop ecx"); - __asm("mov eax, _new_song_list"); - real_event_categ_generation(); + //chaine dans [esp+0x1C], on check si l'adresse correspond à l'une de nos subcateg + //si c'est pas le cas on s'en va sans rien faire + //si c'est le cas alors on met les coord de début et fin dans la struct_songlist et on envoie l'adresse de struct sur eax + __asm("mov _new_song_list, eax"); //save original intended value + __asm("push ecx"); + __asm("push edx"); + __asm("mov ebx, [esp+0x2C]\n"); + __asm("mov _tmp_str_addr, ebx\n"); + + for (uint32_t i = 0; i < g_subcateg_count; i++) + { + if ( (uint32_t)subcategories[i].name == tmp_str_addr ) + { + tmp_size = subcategories[i].size; + new_song_list = (struct songlist_struct_s*) songlist_struct_addr; + new_song_list->array_start = (uint32_t)&(subcategories[i].songlist[0]); + new_song_list->array_end = (uint32_t)&(subcategories[i].songlist[tmp_size]); + break; + } + } + __asm("pop edx"); + __asm("pop ecx"); + __asm("mov eax, _new_song_list"); + real_event_categ_generation(); } void get_subcateg_name_impl() { - __asm("push ecx\n"); - __asm("push edx\n"); - __asm("mov _idx, eax\n"); - g_string_addr = subcategories[idx].name; - __asm("mov eax, _g_string_addr"); - __asm("pop edx\n"); - __asm("pop ecx\n"); + __asm("push ecx\n"); + __asm("push edx\n"); + __asm("mov _idx, eax\n"); + g_string_addr = subcategories[idx].name; + __asm("mov eax, _g_string_addr"); + __asm("pop edx\n"); + __asm("pop ecx\n"); } uint32_t reimpl_value_1; @@ -156,127 +157,127 @@ 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() +void categ_inject_songlist_subcategs() { - __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("push ebx\n"); - __asm("mov ebx, dword ptr ds:[_reimpl_value_2]\n"); - __asm("mov eax, [ebx]\n"); - __asm("pop ebx\n"); - - __asm("xor eax,esp\n"); - __asm("push eax\n"); - __asm("lea eax, dword ptr [esp+0x24]\n"); - __asm("mov dword ptr fs:[0], eax\n"); - __asm("mov ebp, dword ptr [esp+0x34]\n"); - __asm("xor ebx, ebx\n"); + __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 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_generate_event_category(); - - __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 [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 [esp+0x1C]\n"); - __asm("push eax\n"); - __asm("lea ecx, dword ptr [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("push ebx\n"); + __asm("mov ebx, dword ptr ds:[_reimpl_value_2]\n"); + __asm("mov eax, [ebx]\n"); + __asm("pop ebx\n"); - __asm("cmp ebx, [_subcateg_count]\n"); - __asm("jb subcateg_loop\n"); + __asm("xor eax,esp\n"); + __asm("push eax\n"); + __asm("lea eax, dword ptr [esp+0x24]\n"); + __asm("mov dword ptr fs:[0], eax\n"); + __asm("mov ebp, dword ptr [esp+0x34]\n"); + __asm("xor ebx, ebx\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"); + __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_generate_event_category(); + + __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 [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 [esp+0x1C]\n"); + __asm("push eax\n"); + __asm("lea ecx, dword ptr [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() +void categ_inject_songlist_simple() { __asm("push edx\n"); songlist_struct.array_start = (uint32_t)&songlist; @@ -289,21 +290,23 @@ void categ_inject_songlist_impl() __asm("pop ecx\n"); } -bool mask_applied = false; - void (*real_categ_reinit_songlist)(); void hook_categ_reinit_songlist() { songlist_count = 0; - mask_applied = false; real_categ_reinit_songlist(); } void (*real_categ_build_songlist)(); void hook_categ_build_songlist() { - __asm("cmp eax, 0xFA0\n"); + __asm("cmp eax, _g_min_id\n"); __asm("jb categ_skip_add\n"); + __asm("cmp dword ptr _g_max_id, 0\n"); + __asm("je add_my_song\n"); + __asm("cmp eax, _g_max_id\n"); + __asm("ja categ_skip_add\n"); + __asm("add_my_song:\n"); __asm("push eax\n"); __asm("push ebx\n"); __asm("mov eax, [_songlist_count]\n"); @@ -326,69 +329,15 @@ void apply_mask_to_all() __asm("mov _songid_mask, eax"); __asm("pop eax"); songid_mask &= 0xFFFF0000; - for (uint32_t i = 0; i < subcateg_count; i++) + for (uint32_t i = 0; i < g_subcateg_count; i++) { - for (uint32_t j = 0; j < subcategories[i].size; j++) - { - subcategories[i].songlist[j] |= songid_mask; - } + for (uint32_t j = 0; j < subcategories[i].size; j++) + { + subcategories[i].songlist[j] |= songid_mask; + } } } - -//cherche dans toutes mes categ où est la song que je veux -//et renvoie un pointeur dessus dans eax -uint32_t *return_value; -void put_song_addr_in_eax() -{ - for (uint32_t i = 0; i < subcateg_count; i++) - { - for (uint32_t j = 0; j < subcategories[i].size; j++) - { - if (subcategories[i].songlist[j] == (songid_mask&0xFFFF)) - { - return_value = &(subcategories[i].songlist[j]); - __asm("mov eax, [_return_value]"); - return; - } - } - } -} - -void hook_subcateg_adjust_songlist() -{ -#if 1 - __asm("cmp eax, 0xFA0\n"); - __asm("jb skip_mask\n"); - - __asm("push eax"); - __asm("push ebx"); - __asm("push ecx"); - __asm("push edx"); - - __asm("push eax"); - __asm("mov eax, [ebx]"); - __asm("mov _songid_mask, eax"); - __asm("pop eax"); -//songid_mask contient le uint32_t complet - put_song_addr_in_eax(); -__asm("mov ebx, _songid_mask"); -__asm("mov [eax], ebx"); - - __asm("pop edx"); - __asm("pop ecx"); - __asm("pop ebx"); - __asm("pop eax"); -#else - __asm("cmp byte ptr [_mask_applied], 0\n"); - __asm("jne skip_mask\n"); - apply_mask_to_all(); - mask_applied = true; -#endif - __asm("skip_mask:\n"); - real_categ_build_songlist(); -} - void (*real_categ_printf_call)(); void (*real_categ_title_printf)(); void hook_categ_title_printf() @@ -402,7 +351,6 @@ void hook_categ_title_printf() real_categ_title_printf(); } -void (*categ_listing_event)(); void (*real_categ_listing)(); void hook_categ_listing() { @@ -417,7 +365,7 @@ void hook_categ_listing() real_categ_listing(); } -static bool patch_custom_categ_simple(const char *game_dll_fn) { +static bool patch_custom_categ(const char *game_dll_fn) { DWORD dllSize = 0; char *data = getDllData(game_dll_fn, &dllSize); @@ -437,6 +385,7 @@ static bool patch_custom_categ_simple(const char *game_dll_fn) { (void **)&real_categ_title_printf); } + // patch category handling jumptable to add our processing { int64_t pattern_offset = search(data, dllSize, "\x83\xF8\x10\x77\x75\xFF\x24\x85", 8, 0); if (pattern_offset == -1) { @@ -445,32 +394,37 @@ static bool patch_custom_categ_simple(const char *game_dll_fn) { } 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_generate_event_category = (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) ); - - //get_subcateg_size = (void (*)())( *((uint32_t*)(function_addr +0x37)) + (uint32_t)(function_addr +0x04 +0x37) ); - //get_subcateg_name = (void (*)())( *((uint32_t*)(function_addr +0x63)) + (uint32_t)(function_addr +0x04 +0x63) ); MH_CreateHook((LPVOID)patch_addr, (LPVOID)hook_categ_listing, (void **)&real_categ_listing); - uint64_t patch_addr_2 = (int64_t)reimpl_func_2_generate_event_category + 80; - //need to inject correct memory zone after generation as well - MH_CreateHook((LPVOID)patch_addr_2, (LPVOID)hook_event_categ_generation, - (void **)&real_event_categ_generation); + if (g_subcategmode) + { + 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; + + /* retrieve values from this function, needed for my reimplementation */ + 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_generate_event_category = (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) ); + + uint64_t patch_addr_2 = (int64_t)reimpl_func_2_generate_event_category + 80; + //need to inject correct memory zone after generation as well + MH_CreateHook((LPVOID)patch_addr_2, (LPVOID)hook_event_categ_generation, + (void **)&real_event_categ_generation); + } } //add new category processing in jump table + if (g_subcategmode) + { + categ_inject_songlist = &categ_inject_songlist_subcategs; + } + else { int64_t pattern_offset = search(data, dllSize, "\x8B\x4D\x10\x8B\x5D\x0C\x8B\xF1", 8, 0); if (pattern_offset == -1) { @@ -478,101 +432,87 @@ static bool patch_custom_categ_simple(const char *game_dll_fn) { return false; } + //I need to call this subfunction from my hook add_song_in_list = (void (*)())(data + pattern_offset - 0x12); - - 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; + + categ_inject_songlist = &categ_inject_songlist_simple; } - //create new song list + //generate songlist with all songs whose ID is above the threshold + if (!g_subcategmode) { - int64_t pattern_offset = search(data, dllSize, "\x00\x8B\x56\x04\x0F\xB7\x02\xE8", 8, 0); - if (pattern_offset == -1) { - LOG("popnhax: custom_categ: cannot find songlist processing table\n"); - return false; - } - - uint64_t patch_addr = (int64_t)data + pattern_offset + 0x07; - - if (g_subcategmode) - { - MH_CreateHook((LPVOID)patch_addr, (LPVOID)hook_subcateg_adjust_songlist, - (void **)&real_categ_build_songlist); - } - else { - MH_CreateHook((LPVOID)patch_addr, (LPVOID)hook_categ_build_songlist, - (void **)&real_categ_build_songlist); - } - } + int64_t pattern_offset = search(data, dllSize, "\x00\x8B\x56\x04\x0F\xB7\x02\xE8", 8, 0); + if (pattern_offset == -1) { + LOG("popnhax: custom_categ: cannot find songlist processing table\n"); + return false; + } - //create new song list - { - int64_t pattern_offset = search(data, dllSize, "\x33\xC9\xB8\x12\x00\x00\x00\xBA", 8, 0); - if (pattern_offset == -1) { - LOG("popnhax: custom_categ: cannot find category generation function\n"); - return false; + uint64_t patch_addr = (int64_t)data + pattern_offset + 0x07; + + MH_CreateHook((LPVOID)patch_addr, (LPVOID)hook_categ_build_songlist, + (void **)&real_categ_build_songlist); } - uint64_t patch_addr = (int64_t)data + pattern_offset; + //force rearm songlist creation so that it keeps working + { + int64_t pattern_offset = search(data, dllSize, "\x33\xC9\xB8\x12\x00\x00\x00\xBA", 8, 0); + if (pattern_offset == -1) { + LOG("popnhax: custom_categ: cannot find category generation function\n"); + return false; + } - MH_CreateHook((LPVOID)patch_addr, (LPVOID)hook_categ_reinit_songlist, - (void **)&real_categ_reinit_songlist); + uint64_t patch_addr = (int64_t)data + pattern_offset; + + MH_CreateHook((LPVOID)patch_addr, (LPVOID)hook_categ_reinit_songlist, + (void **)&real_categ_reinit_songlist); + } } - - //bump category number from 16 to 17 + //bump category count from 16 to 17 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(game_dll_fn, "\x83\xEC\x44\x98\xC7\x04\x24", 7, 2, "\x48", 1)) { - return false; - } - - 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(game_dll_fn, "\x8B\x04\x84\x83\xC4\x44\xC3\xCC", 8, 0, categname_patch_string, 15)) - { - return false; + //make array one cell larger + if (!find_and_patch_hex(game_dll_fn, "\x83\xEC\x44\x98\xC7\x04\x24", 7, 2, "\x48", 1)) + { + return false; + } + //add the new name + 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(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(game_dll_fn, "\x83\xEC\x44\x98\xC7\x04\x24", 7, 2, "\x48", 1)) //2nd occurrence since first one just got patched { - return false; + 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; + } + //add the new icon name + 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(game_dll_fn, "\x8B\x04\x84\x83\xC4\x44\xC3\xCC", 8, 0, categicon_patch_string, 15)) + { + return false; + } } - 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(game_dll_fn, "\x8B\x04\x84\x83\xC4\x44\xC3\xCC", 8, 0, categicon_patch_string, 15)) - { - return false; - } - - LOG("popnhax: custom category injected\n"); + LOG("popnhax: custom %s injected (for songids ", g_subcategmode? "subcategories":"category"); + if (g_max_id) + LOG("between %d and %d (inclusive))\n", g_min_id, g_max_id); + else + LOG("above %d (inclusive))\n", g_min_id); return true; } @@ -588,17 +528,17 @@ static char *get_folder_name(const char* path) { static void parse_musicdb(const char *input_filename) { char *title = get_folder_name(input_filename); - - subcategory_s *subcateg = get_subcateg(title); - if ( subcateg == NULL ) - { - subcategories = (subcategory_s*)realloc(subcategories, sizeof(subcategory_s)*(++subcateg_count)); - subcateg = &(subcategories[subcateg_count-1]); - subcateg->name = strdup(title); - subcateg->songlist = NULL; - subcateg->size = 0; - } - + + subcategory_s *subcateg = get_subcateg(title); + if ( subcateg == NULL ) + { + subcategories = (subcategory_s*)realloc(subcategories, sizeof(subcategory_s)*(++g_subcateg_count)); + subcateg = &(subcategories[g_subcateg_count-1]); + subcateg->name = strdup(title); + subcateg->songlist = NULL; + subcateg->size = 0; + } + property* musicdb = load_prop_file(input_filename); //iterate over all music id property_node *prop = NULL; @@ -615,6 +555,7 @@ static void parse_musicdb(const char *input_filename) { static void load_databases() { SearchFile s; + g_subcateg_count = 0; printf("musicdb search...\n"); s.search("data_mods", "xml", true); auto result = s.getResult(); @@ -628,29 +569,23 @@ static void load_databases() { } } -bool patch_custom_categs(const char *dllFilename, uint8_t mode) +bool patch_custom_categs(const char *dllFilename, uint8_t mode, uint16_t min, uint16_t max) { - if (mode == 1) - { - g_subcategmode = true; - subcateg_count = 0; - load_databases(); - return patch_custom_categ_simple(dllFilename); - } + g_min_id = min; + g_max_id = max; + if (mode == 2) + { + g_subcategmode = true; + load_databases(); + } - if (mode == 2) - { - g_subcategmode = true; - subcateg_count = 0; - load_databases(); - } - LOG("Subcateg list :\n"); - for (uint32_t i = 0; i < subcateg_count; i++) - { - if (subcategories[i].size != 0) - LOG(" %s (%d songs)", subcategories[i].name, subcategories[i].size); - } - - LOG("HELLO OK :\n"); - return true; + return patch_custom_categ(dllFilename); +/* + LOG("Subcateg list :\n"); + for (uint32_t i = 0; i < g_subcateg_count; i++) + { + if (subcategories[i].size != 0) + LOG(" %s (%d songs)", subcategories[i].name, subcategories[i].size); + } + */ } \ No newline at end of file diff --git a/popnhax/custom_categs.h b/popnhax/custom_categs.h index 4971087..15d48d3 100644 --- a/popnhax/custom_categs.h +++ b/popnhax/custom_categs.h @@ -3,6 +3,6 @@ #include -bool patch_custom_categs(const char *dllFilename, uint8_t mode); +bool patch_custom_categs(const char *dllFilename, uint8_t mode, uint16_t min, uint16_t max); #endif diff --git a/popnhax/dllmain.cc b/popnhax/dllmain.cc index 0f51806..553ca50 100644 --- a/popnhax/dllmain.cc +++ b/popnhax/dllmain.cc @@ -185,6 +185,12 @@ PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_U16, struct popnhax_config, hispeed_default "/popnhax/hispeed_default_bpm") PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_S8, struct popnhax_config, base_offset, "/popnhax/base_offset") +PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_U8, struct popnhax_config, custom_categ, + "/popnhax/custom_categ") +PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_U16, struct popnhax_config, custom_categ_min_songid, + "/popnhax/custom_categ_min_songid") +PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_U16, struct popnhax_config, custom_categ_max_songid, + "/popnhax/custom_categ_max_songid") PSMAP_END enum BufferIndexes { @@ -5580,7 +5586,8 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv /* must be called after force_datecode */ patch_db_power_points(); patch_db_fix_cursor(); - patch_custom_categs(g_game_dll_fn, 1); + if (config.custom_categ) + patch_custom_categs(g_game_dll_fn, config.custom_categ, config.custom_categ_min_songid, config.custom_categ_max_songid); patch_database(config.force_unlocks); }