wip categ

This commit is contained in:
CrazyRedMachine 2024-04-20 10:32:49 +02:00
parent a4a4e53da1
commit 3c459addea
4 changed files with 348 additions and 200 deletions

View File

@ -14,4 +14,5 @@ srcpp_popnhax := \
dllmain.cc \
loader.cc \
SearchFile.cc \
translation.cc
translation.cc \
custom_categs.cc

336
popnhax/custom_categs.cc Normal file
View File

@ -0,0 +1,336 @@
#include <cstdio>
#include <cstring>
#include <io.h>
#include <windows.h>
#include "SearchFile.h"
#include "util/search.h"
#include "util/log.h"
#include "util/patch.h"
#include "tableinfo.h"
#include "loader.h"
#include "imports/avs.h"
#include "xmlhelper.h"
#include "minhook/hde32.h"
#include "minhook/include/MinHook.h"
const char *g_categname = "Custom Tracks";
const char *g_categicon = "cate_13";
const char *g_categformat = "[ol:4][olc:d92f0d]%s";
uint32_t songlist[4096] = {0};
uint32_t songlist_addr = (uint32_t)&songlist;
uint32_t songlist_count = 0;
struct songlist_struct_s {
uint32_t dummy[3];
uint32_t array_start;
uint32_t array_end;
} songlist_struct;
uint32_t songlist_struct_addr = (uint32_t)&songlist_struct;
void (*add_song_in_list)();
void (*categ_inject_songlist)();
void categ_inject_songlist_impl()
{
__asm("push edx\n");
songlist_struct.array_start = (uint32_t)&songlist;
songlist_struct.array_end = (uint32_t)&(songlist[songlist_count]);
__asm("pop edx\n");
__asm("push ecx\n");
__asm("push _songlist_struct_addr\n");
__asm("lea eax, dword ptr [ecx+0x24]\n");
__asm("call [_add_song_in_list]\n");
__asm("pop ecx\n");
}
void (*real_categ_reinit_songlist)();
void hook_categ_reinit_songlist()
{
songlist_count = 0;
real_categ_reinit_songlist();
}
void (*real_categ_build_songlist)();
void hook_categ_build_songlist()
{
__asm("cmp eax, 0xFA0\n");
__asm("jb categ_skip_add\n");
__asm("push eax\n");
__asm("push ebx\n");
__asm("mov eax, [_songlist_count]\n");
__asm("sal eax, 2\n");
__asm("add eax, _songlist_addr\n");
__asm("mov ebx, [edx]\n");
__asm("mov dword ptr [eax], ebx\n");
songlist_count++;
__asm("pop ebx\n");
__asm("pop eax\n");
__asm("categ_skip_add:\n");
real_categ_build_songlist();
}
void (*real_categ_printf_call)();
void (*real_categ_title_printf)();
void hook_categ_title_printf()
{
__asm("cmp edi, 0x10\n");
__asm("jle categ_title_printf_ok\n");
__asm("mov eax, _g_categformat\n");
__asm("push eax\n");
__asm("jmp [_real_categ_printf_call]\n");
__asm("categ_title_printf_ok:\n");
real_categ_title_printf();
}
void (*categ_listing_newsongs)();
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("categ_listing_ok:\n");
real_categ_listing();
}
static bool patch_custom_categ_simple(const char *g_game_dll_fn) {
DWORD dllSize = 0;
char *data = getDllData(g_game_dll_fn, &dllSize);
//patch format string for any category above 16 (prevent crash)
{
int64_t pattern_offset = search(data, dllSize, "\x6A\xFF\x8B\xCB\xFF\xD2\x50", 7, 0);
if (pattern_offset == -1) {
LOG("popnhax: custom_categ: cannot find category title format string function\n");
return false;
}
uint64_t patch_addr = (int64_t)data + pattern_offset + 0x07;
real_categ_printf_call = (void (*)())(patch_addr + 0x08);
MH_CreateHook((LPVOID)patch_addr, (LPVOID)hook_categ_title_printf,
(void **)&real_categ_title_printf);
}
//add new category processing in jump table
{
int64_t pattern_offset = search(data, dllSize, "\x8B\x4D\x10\x8B\x5D\x0C\x8B\xF1", 8, 0);
if (pattern_offset == -1) {
LOG("popnhax: custom_categ: cannot find add_song_in_list function\n");
return false;
}
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);
}
//create new song list
{
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;
MH_CreateHook((LPVOID)patch_addr, (LPVOID)hook_categ_build_songlist,
(void **)&real_categ_build_songlist);
}
//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;
MH_CreateHook((LPVOID)patch_addr, (LPVOID)hook_categ_reinit_songlist,
(void **)&real_categ_reinit_songlist);
}
//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))
{
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))
{
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(g_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
{
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(g_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");
return true;
}
uint32_t **subcategs;
char **subcateg_names;
typedef struct {
char *name;
uint32_t size;
uint32_t *songlist;
} subcategory_s;
subcategory_s* subcategories;
uint32_t 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;
}
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);
}
}
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;
}
//extract folder name (cut "data_mods")
static char *get_folder_name(const char* path) {
size_t len = (size_t)(strchr(path+10, '\\')-(path+10));
char *categ_name = (char*) malloc(len+1);
strncpy(categ_name, path+10, len);
categ_name[len] = '\0';
return categ_name;
}
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;
}
property* musicdb = load_prop_file(input_filename);
//iterate over all music id
property_node *prop = NULL;
if ((prop = property_search(musicdb, NULL, "/database/music"))) {
for (; prop != NULL; prop = property_node_traversal(prop, TRAVERSE_NEXT_SEARCH_RESULT)) {
char idxStr[256] = {};
property_node_refer(musicdb, prop, "id@", PROPERTY_TYPE_ATTR, idxStr,
sizeof(idxStr));
uint32_t songid = atoi(idxStr);
add_song_to_subcateg(songid, subcateg);
}
}
}
static void load_databases() {
SearchFile s;
printf("musicdb search...\n");
s.search("data_mods", "xml", true);
auto result = s.getResult();
for(uint16_t i=0;i<result.size();i++)
{
if ( strstr(result[i].c_str(), "musicdb") == NULL )
continue;
printf("(musicdb) Loading %s...\n", result[i].c_str());
parse_musicdb(result[i].c_str());
}
}
bool patch_custom_categs(const char *dllFilename, uint8_t mode)
{
if (mode == 1)
{
return patch_custom_categ_simple(dllFilename);
}
if (mode == 2)
{
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;
}

8
popnhax/custom_categs.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef __CUSTOM_CATEGS_H__
#define __CUSTOM_CATEGS_H__
#include <stdint.h>
bool patch_custom_categs(const char *dllFilename, uint8_t mode);
#endif

View File

@ -24,6 +24,7 @@
#include "util/xmlprop.hpp"
#include "xmlhelper.h"
#include "translation.h"
#include "custom_categs.h"
#include "tableinfo.h"
#include "loader.h"
@ -5284,204 +5285,6 @@ static bool option_net_ojama_off(){
return true;
}
const char *g_categname = "Custom Tracks";
const char *g_categicon = "cate_13";
const char *g_categformat = "[ol:4][olc:d92f0d]%s";
uint32_t songlist[4096] = {0};
uint32_t songlist_addr = (uint32_t)&songlist;
uint32_t songlist_count = 0;
struct songlist_struct_s {
uint32_t dummy[3];
uint32_t array_start;
uint32_t array_end;
} songlist_struct;
uint32_t songlist_struct_addr = (uint32_t)&songlist_struct;
void (*add_song_in_list)();
void (*categ_inject_songlist)();
void categ_inject_songlist_impl()
{
__asm("push edx\n");
songlist_struct.array_start = (uint32_t)&songlist;
songlist_struct.array_end = (uint32_t)&(songlist[songlist_count]);
__asm("pop edx\n");
__asm("push ecx\n");
__asm("push _songlist_struct_addr\n");
__asm("lea eax, dword ptr [ecx+0x24]\n");
__asm("call [_add_song_in_list]\n");
__asm("pop ecx\n");
}
void (*real_categ_reinit_songlist)();
void hook_categ_reinit_songlist()
{
songlist_count = 0;
real_categ_reinit_songlist();
}
void (*real_categ_build_songlist)();
void hook_categ_build_songlist()
{
__asm("cmp eax, 0xFA0\n");
__asm("jb categ_skip_add\n");
__asm("push eax\n");
__asm("push ebx\n");
__asm("mov eax, [_songlist_count]\n");
__asm("sal eax, 2\n");
__asm("add eax, _songlist_addr\n");
__asm("mov ebx, [edx]\n");
__asm("mov dword ptr [eax], ebx\n");
songlist_count++;
__asm("pop ebx\n");
__asm("pop eax\n");
__asm("categ_skip_add:\n");
real_categ_build_songlist();
}
void (*real_categ_printf_call)();
void (*real_categ_title_printf)();
void hook_categ_title_printf()
{
__asm("cmp edi, 0x10\n");
__asm("jle categ_title_printf_ok\n");
__asm("mov eax, _g_categformat\n");
__asm("push eax\n");
__asm("jmp [_real_categ_printf_call]\n");
__asm("categ_title_printf_ok:\n");
real_categ_title_printf();
}
void (*categ_listing_newsongs)();
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("categ_listing_ok:\n");
real_categ_listing();
}
static bool patch_custom_categ() {
DWORD dllSize = 0;
char *data = getDllData(g_game_dll_fn, &dllSize);
//patch format string for any category above 16 (prevent crash)
{
int64_t pattern_offset = search(data, dllSize, "\x6A\xFF\x8B\xCB\xFF\xD2\x50", 7, 0);
if (pattern_offset == -1) {
LOG("popnhax: custom_categ: cannot find category title format string function\n");
return false;
}
uint64_t patch_addr = (int64_t)data + pattern_offset + 0x07;
real_categ_printf_call = (void (*)())(patch_addr + 0x08);
MH_CreateHook((LPVOID)patch_addr, (LPVOID)hook_categ_title_printf,
(void **)&real_categ_title_printf);
}
//add new category processing in jump table
{
int64_t pattern_offset = search(data, dllSize, "\x8B\x4D\x10\x8B\x5D\x0C\x8B\xF1", 8, 0);
if (pattern_offset == -1) {
LOG("popnhax: custom_categ: cannot find add_song_in_list function\n");
return false;
}
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);
}
//create new song list
{
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;
MH_CreateHook((LPVOID)patch_addr, (LPVOID)hook_categ_build_songlist,
(void **)&real_categ_build_songlist);
}
//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;
MH_CreateHook((LPVOID)patch_addr, (LPVOID)hook_categ_reinit_songlist,
(void **)&real_categ_reinit_songlist);
}
//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))
{
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))
{
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(g_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
{
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(g_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");
return true;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH: {
@ -5777,7 +5580,7 @@ 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_categ();
patch_custom_categs(g_game_dll_fn, 1);
patch_database(config.force_unlocks);
}