cleanup, expose options

This commit is contained in:
CrazyRedMachine 2024-04-21 18:15:03 +02:00
parent 5f2a9d7bc0
commit 46ec71b83b
5 changed files with 321 additions and 370 deletions

View File

@ -5,6 +5,8 @@
<patch_db __type="bool">0</patch_db> <patch_db __type="bool">0</patch_db>
<!-- Force unlock music, charts, characters, and deco parts when applicable --> <!-- Force unlock music, charts, characters, and deco parts when applicable -->
<force_unlocks __type="bool">0</force_unlocks> <force_unlocks __type="bool">0</force_unlocks>
<!-- Put customs into their own category (0: no, 1: simple category with all customs, 2: subcategories by folder name) -->
<custom_categ __type="u8">0</custom_categ>
<!-- Classic patches --> <!-- Classic patches -->
<!-- Prevent crash on boot when using a different default audio source (aka HDMI audio patch) --> <!-- Prevent crash on boot when using a different default audio source (aka HDMI audio patch) -->
@ -132,6 +134,10 @@
<disable_expansions __type="bool">0</disable_expansions> <disable_expansions __type="bool">0</disable_expansions>
<!-- Copy the new table information over top the old tables (automatically enables disable_expansions) --> <!-- Copy the new table information over top the old tables (automatically enables disable_expansions) -->
<disable_redirection __type="bool">0</disable_redirection> <disable_redirection __type="bool">0</disable_redirection>
<!-- minimum songid for a song to be seen as "custom" (inclusive) -->
<custom_categ_min_songid __type="u16">4000</custom_categ_min_songid>
<!-- maximum songid for a song to be seen as "custom" (0 = no limit) -->
<custom_categ_max_songid __type="u16">0</custom_categ_max_songid>
<!-- Translation --> <!-- Translation -->
<!-- Disable .dict string replacements and .ips patches --> <!-- Disable .dict string replacements and .ips patches -->

View File

@ -14,6 +14,9 @@ struct popnhax_config {
bool quick_retire; bool quick_retire;
bool back_to_song_select; bool back_to_song_select;
bool score_challenge; bool score_challenge;
uint8_t custom_categ;
uint16_t custom_categ_min_songid;
uint16_t custom_categ_max_songid;
bool force_hd_timing; bool force_hd_timing;
uint8_t force_hd_resolution; uint8_t force_hd_resolution;
bool force_unlocks; bool force_unlocks;

View File

@ -19,49 +19,50 @@
#include "minhook/hde32.h" #include "minhook/hde32.h"
#include "minhook/include/MinHook.h" #include "minhook/include/MinHook.h"
uint8_t g_subcateg_count = 3; uint32_t g_min_id = 4000;
uint32_t **subcategs; uint32_t g_max_id = 0;
char **subcateg_names;
typedef struct { typedef struct {
char *name; char *name;
uint32_t size; uint32_t size;
uint32_t *songlist; uint32_t *songlist;
} subcategory_s; } subcategory_s;
subcategory_s* subcategories; subcategory_s* subcategories;
uint32_t subcateg_count = 0; uint32_t g_subcateg_count = 0;
struct property *load_prop_file(const char *filename); struct property *load_prop_file(const char *filename);
static bool subcateg_has_songid(uint32_t songid, subcategory_s* subcateg) static bool subcateg_has_songid(uint32_t songid, subcategory_s* subcateg)
{ {
for ( uint32_t i = 0; i < subcateg->size; i++ ) for ( uint32_t i = 0; i < subcateg->size; i++ )
{ {
if ( subcateg->songlist[i] == songid ) if ( subcateg->songlist[i] == songid )
return true; return true;
} }
return false; return false;
} }
static void add_song_to_subcateg(uint32_t songid, subcategory_s* subcateg) static void add_song_to_subcateg(uint32_t songid, subcategory_s* subcateg)
{ {
if ( songid >= 3000 && !subcateg_has_songid(songid, subcateg) ) if ( songid >= g_min_id
{ && (g_max_id == 0 || songid <= g_max_id)
subcateg->songlist = (uint32_t *) realloc(subcateg->songlist, sizeof(uint32_t)*(++subcateg->size)); && !subcateg_has_songid(songid, subcateg) )
subcateg->songlist[subcateg->size-1] = songid; {
LOG("%s : %d (subcateg size %d)\n", subcateg->name, songid, subcateg->size); 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) static subcategory_s* get_subcateg(char *title)
{ {
for (uint32_t i = 0; i < subcateg_count; i++) for (uint32_t i = 0; i < g_subcateg_count; i++)
{ {
if (strcmp(title, subcategories[i].name) == 0) if (strcmp(title, subcategories[i].name) == 0)
return &(subcategories[i]); return &(subcategories[i]);
} }
return NULL; return NULL;
} }
bool g_subcategmode = false; bool g_subcategmode = false;
@ -92,58 +93,58 @@ void (*categ_inject_songlist)();
struct songlist_struct_s *new_song_list = NULL; struct songlist_struct_s *new_song_list = NULL;
void get_subcateg_size_impl() void get_subcateg_size_impl()
{ {
__asm("push edx\n"); __asm("push edx\n");
__asm("mov _idx, eax\n"); __asm("mov _idx, eax\n");
tmp_size = subcategories[idx].size; tmp_size = subcategories[idx].size;
new_song_list = (struct songlist_struct_s*) songlist_struct_addr; 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_start = (uint32_t)&(subcategories[idx].songlist[0]);
new_song_list->array_end = (uint32_t)&(subcategories[idx].songlist[tmp_size]); new_song_list->array_end = (uint32_t)&(subcategories[idx].songlist[tmp_size]);
__asm("mov eax, [_tmp_size]"); __asm("mov eax, [_tmp_size]");
__asm("mov ecx, _new_song_list"); __asm("mov ecx, _new_song_list");
__asm("pop edx\n"); __asm("pop edx\n");
} }
uint32_t tmp_str_addr; uint32_t tmp_str_addr;
void (*real_event_categ_generation)(); void (*real_event_categ_generation)();
void hook_event_categ_generation() void hook_event_categ_generation()
{ {
//chaine dans [esp+0x1C], on check si l'adresse correspond à l'une de nos subcateg //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 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 //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("mov _new_song_list, eax"); //save original intended value
__asm("push ecx"); __asm("push ecx");
__asm("push edx"); __asm("push edx");
__asm("mov ebx, [esp+0x2C]\n"); __asm("mov ebx, [esp+0x2C]\n");
__asm("mov _tmp_str_addr, ebx\n"); __asm("mov _tmp_str_addr, ebx\n");
for (uint32_t i = 0; i < subcateg_count; i++) for (uint32_t i = 0; i < g_subcateg_count; i++)
{ {
if ( (uint32_t)subcategories[i].name == tmp_str_addr ) if ( (uint32_t)subcategories[i].name == tmp_str_addr )
{ {
tmp_size = subcategories[i].size; tmp_size = subcategories[i].size;
new_song_list = (struct songlist_struct_s*) songlist_struct_addr; 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_start = (uint32_t)&(subcategories[i].songlist[0]);
new_song_list->array_end = (uint32_t)&(subcategories[i].songlist[tmp_size]); new_song_list->array_end = (uint32_t)&(subcategories[i].songlist[tmp_size]);
break; break;
} }
} }
__asm("pop edx"); __asm("pop edx");
__asm("pop ecx"); __asm("pop ecx");
__asm("mov eax, _new_song_list"); __asm("mov eax, _new_song_list");
real_event_categ_generation(); real_event_categ_generation();
} }
void get_subcateg_name_impl() void get_subcateg_name_impl()
{ {
__asm("push ecx\n"); __asm("push ecx\n");
__asm("push edx\n"); __asm("push edx\n");
__asm("mov _idx, eax\n"); __asm("mov _idx, eax\n");
g_string_addr = subcategories[idx].name; g_string_addr = subcategories[idx].name;
__asm("mov eax, _g_string_addr"); __asm("mov eax, _g_string_addr");
__asm("pop edx\n"); __asm("pop edx\n");
__asm("pop ecx\n"); __asm("pop ecx\n");
} }
uint32_t reimpl_value_1; uint32_t reimpl_value_1;
@ -156,127 +157,127 @@ void (*reimpl_func_3)();
void (*reimpl_func_4)(); void (*reimpl_func_4)();
//this is a reimplementation of the event category generation function, modded to use popnhax internal subcategories //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("add esp, 0xC"); // cancel a sub esp 0xC that is added by this code for no reason
__asm("push 0xFFFFFFFF\n"); __asm("push 0xFFFFFFFF\n");
__asm("push [_reimpl_value_1]\n"); __asm("push [_reimpl_value_1]\n");
__asm("mov eax, dword ptr fs:[0]\n"); __asm("mov eax, dword ptr fs:[0]\n");
__asm("push eax\n"); __asm("push eax\n");
__asm("sub esp, 0x10\n"); __asm("sub esp, 0x10\n");
__asm("push ebx\n"); __asm("push ebx\n");
__asm("push ebp\n"); __asm("push ebp\n");
__asm("push esi\n"); __asm("push esi\n");
__asm("push edi\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("mov dword ptr ss:[esp+0x34], ebx\n"); __asm("push ebx\n");
__asm("subcateg_loop:\n"); __asm("mov ebx, dword ptr ds:[_reimpl_value_2]\n");
__asm("mov ecx, dword ptr ss:[ebp+0x08]\n"); __asm("mov eax, [ebx]\n");
__asm("mov eax, ebx\n"); __asm("pop 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, [_subcateg_count]\n"); __asm("xor eax,esp\n");
__asm("jb subcateg_loop\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 ss:[esp+0x34], ebx\n");
__asm("mov dword ptr fs:[0], ecx\n"); __asm("subcateg_loop:\n");
__asm("pop ecx\n"); __asm("mov ecx, dword ptr ss:[ebp+0x08]\n");
__asm("pop edi\n"); __asm("mov eax, ebx\n");
__asm("pop esi\n");
__asm("pop ebp\n"); get_subcateg_size();
__asm("pop ebx\n");
__asm("add esp, 0x1C\n"); __asm("test eax, eax\n");
__asm("ret 4\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 ) //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"); __asm("push edx\n");
songlist_struct.array_start = (uint32_t)&songlist; songlist_struct.array_start = (uint32_t)&songlist;
@ -289,21 +290,23 @@ void categ_inject_songlist_impl()
__asm("pop ecx\n"); __asm("pop ecx\n");
} }
bool mask_applied = false;
void (*real_categ_reinit_songlist)(); void (*real_categ_reinit_songlist)();
void hook_categ_reinit_songlist() void hook_categ_reinit_songlist()
{ {
songlist_count = 0; songlist_count = 0;
mask_applied = false;
real_categ_reinit_songlist(); real_categ_reinit_songlist();
} }
void (*real_categ_build_songlist)(); void (*real_categ_build_songlist)();
void hook_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("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 eax\n");
__asm("push ebx\n"); __asm("push ebx\n");
__asm("mov eax, [_songlist_count]\n"); __asm("mov eax, [_songlist_count]\n");
@ -326,69 +329,15 @@ void apply_mask_to_all()
__asm("mov _songid_mask, eax"); __asm("mov _songid_mask, eax");
__asm("pop eax"); __asm("pop eax");
songid_mask &= 0xFFFF0000; 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++) for (uint32_t j = 0; j < subcategories[i].size; j++)
{ {
subcategories[i].songlist[j] |= songid_mask; 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_printf_call)();
void (*real_categ_title_printf)(); void (*real_categ_title_printf)();
void hook_categ_title_printf() void hook_categ_title_printf()
@ -402,7 +351,6 @@ void hook_categ_title_printf()
real_categ_title_printf(); real_categ_title_printf();
} }
void (*categ_listing_event)();
void (*real_categ_listing)(); void (*real_categ_listing)();
void hook_categ_listing() void hook_categ_listing()
{ {
@ -417,7 +365,7 @@ void hook_categ_listing()
real_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; DWORD dllSize = 0;
char *data = getDllData(game_dll_fn, &dllSize); 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); (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); int64_t pattern_offset = search(data, dllSize, "\x83\xF8\x10\x77\x75\xFF\x24\x85", 8, 0);
if (pattern_offset == -1) { 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 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, MH_CreateHook((LPVOID)patch_addr, (LPVOID)hook_categ_listing,
(void **)&real_categ_listing); (void **)&real_categ_listing);
uint64_t patch_addr_2 = (int64_t)reimpl_func_2_generate_event_category + 80; if (g_subcategmode)
//need to inject correct memory zone after generation as well {
MH_CreateHook((LPVOID)patch_addr_2, (LPVOID)hook_event_categ_generation, uint64_t function_call_addr = (int64_t)(data + pattern_offset + 0x05 + 69);
(void **)&real_event_categ_generation); 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 //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); int64_t pattern_offset = search(data, dllSize, "\x8B\x4D\x10\x8B\x5D\x0C\x8B\xF1", 8, 0);
if (pattern_offset == -1) { if (pattern_offset == -1) {
@ -478,101 +432,87 @@ static bool patch_custom_categ_simple(const char *game_dll_fn) {
return false; return false;
} }
//I need to call this subfunction from my hook
add_song_in_list = (void (*)())(data + pattern_offset - 0x12); add_song_in_list = (void (*)())(data + pattern_offset - 0x12);
if (g_subcategmode) categ_inject_songlist = &categ_inject_songlist_simple;
{
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 //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, int64_t pattern_offset = search(data, dllSize, "\x00\x8B\x56\x04\x0F\xB7\x02\xE8", 8, 0);
(void **)&real_categ_build_songlist); if (pattern_offset == -1) {
} LOG("popnhax: custom_categ: cannot find songlist processing table\n");
} return false;
}
//create new song list uint64_t patch_addr = (int64_t)data + pattern_offset + 0x07;
{
int64_t pattern_offset = search(data, dllSize, "\x33\xC9\xB8\x12\x00\x00\x00\xBA", 8, 0); MH_CreateHook((LPVOID)patch_addr, (LPVOID)hook_categ_build_songlist,
if (pattern_offset == -1) { (void **)&real_categ_build_songlist);
LOG("popnhax: custom_categ: cannot find category generation function\n");
return false;
} }
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, uint64_t patch_addr = (int64_t)data + pattern_offset;
(void **)&real_categ_reinit_songlist);
MH_CreateHook((LPVOID)patch_addr, (LPVOID)hook_categ_reinit_songlist,
(void **)&real_categ_reinit_songlist);
}
} }
//bump category count from 16 to 17
//bump category number 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)) if (!find_and_patch_hex(game_dll_fn, "\x83\xFE\x11\x0F\x82\x59\xFE\xFF\xFF", 9, 2, "\x12", 1))
{ {
return false; return false;
} }
//patch getCategName for a 17th entry //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; //make array one cell larger
} if (!find_and_patch_hex(game_dll_fn, "\x83\xEC\x44\x98\xC7\x04\x24", 7, 2, "\x48", 1))
{
uint32_t categname_str_addr = (uint32_t)g_categname; return false;
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); //add the new name
if (!find_and_patch_hex(game_dll_fn, "\x8B\x04\x84\x83\xC4\x44\xC3\xCC", 8, 0, categname_patch_string, 15)) 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";
return false; 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 //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; LOG("popnhax: custom %s injected (for songids ", g_subcategmode? "subcategories":"category");
char categicon_patch_string[] = "\xC7\x44\x24\x44\xDC\x00\x28\x10\x8B\x04\x84\x83\xC4\x48\xC3"; if (g_max_id)
memcpy(categicon_patch_string+4, &categicon_str_addr, 4); LOG("between %d and %d (inclusive))\n", g_min_id, g_max_id);
if (!find_and_patch_hex(game_dll_fn, "\x8B\x04\x84\x83\xC4\x44\xC3\xCC", 8, 0, categicon_patch_string, 15)) else
{ LOG("above %d (inclusive))\n", g_min_id);
return false;
}
LOG("popnhax: custom category injected\n");
return true; return true;
} }
@ -588,17 +528,17 @@ static char *get_folder_name(const char* path) {
static void parse_musicdb(const char *input_filename) { static void parse_musicdb(const char *input_filename) {
char *title = get_folder_name(input_filename); char *title = get_folder_name(input_filename);
subcategory_s *subcateg = get_subcateg(title); subcategory_s *subcateg = get_subcateg(title);
if ( subcateg == NULL ) if ( subcateg == NULL )
{ {
subcategories = (subcategory_s*)realloc(subcategories, sizeof(subcategory_s)*(++subcateg_count)); subcategories = (subcategory_s*)realloc(subcategories, sizeof(subcategory_s)*(++g_subcateg_count));
subcateg = &(subcategories[subcateg_count-1]); subcateg = &(subcategories[g_subcateg_count-1]);
subcateg->name = strdup(title); subcateg->name = strdup(title);
subcateg->songlist = NULL; subcateg->songlist = NULL;
subcateg->size = 0; subcateg->size = 0;
} }
property* musicdb = load_prop_file(input_filename); property* musicdb = load_prop_file(input_filename);
//iterate over all music id //iterate over all music id
property_node *prop = NULL; property_node *prop = NULL;
@ -615,6 +555,7 @@ static void parse_musicdb(const char *input_filename) {
static void load_databases() { static void load_databases() {
SearchFile s; SearchFile s;
g_subcateg_count = 0;
printf("musicdb search...\n"); printf("musicdb search...\n");
s.search("data_mods", "xml", true); s.search("data_mods", "xml", true);
auto result = s.getResult(); 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_min_id = min;
{ g_max_id = max;
g_subcategmode = true; if (mode == 2)
subcateg_count = 0; {
load_databases(); g_subcategmode = true;
return patch_custom_categ_simple(dllFilename); load_databases();
} }
if (mode == 2) return patch_custom_categ(dllFilename);
{ /*
g_subcategmode = true; LOG("Subcateg list :\n");
subcateg_count = 0; for (uint32_t i = 0; i < g_subcateg_count; i++)
load_databases(); {
} if (subcategories[i].size != 0)
LOG("Subcateg list :\n"); LOG(" %s (%d songs)", subcategories[i].name, subcategories[i].size);
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;
} }

View File

@ -3,6 +3,6 @@
#include <stdint.h> #include <stdint.h>
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 #endif

View File

@ -185,6 +185,12 @@ PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_U16, struct popnhax_config, hispeed_default
"/popnhax/hispeed_default_bpm") "/popnhax/hispeed_default_bpm")
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_S8, struct popnhax_config, base_offset, PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_S8, struct popnhax_config, base_offset,
"/popnhax/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 PSMAP_END
enum BufferIndexes { enum BufferIndexes {
@ -5580,7 +5586,8 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
/* must be called after force_datecode */ /* must be called after force_datecode */
patch_db_power_points(); patch_db_power_points();
patch_db_fix_cursor(); 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); patch_database(config.force_unlocks);
} }