forked from Popn_Tools/popnhax
Compare commits
13 Commits
de69af2d66
...
d1c1d8a13f
Author | SHA1 | Date | |
---|---|---|---|
d1c1d8a13f | |||
0b88b1253d | |||
f0daa9e761 | |||
d6ca83d076 | |||
5e57926b58 | |||
e838d88269 | |||
ed6a3a23f0 | |||
badd6535b3 | |||
2dcc503eca | |||
94b7eb014d | |||
a7bf74962b | |||
f2320cdf07 | |||
30ba232511 |
22
dist/popnhax/popnhax.xml
vendored
22
dist/popnhax/popnhax.xml
vendored
@ -7,6 +7,16 @@
|
||||
<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>
|
||||
<!-- Prevent customs from showing up in version folders -->
|
||||
<custom_exclude_from_version __type="bool">0</custom_exclude_from_version>
|
||||
<!-- Prevent customs from showing up in level folders -->
|
||||
<custom_exclude_from_level __type="bool">0</custom_exclude_from_level>
|
||||
|
||||
<!-- Other categories -->
|
||||
<!-- Bring back score challenge in the game for servers supporting it (only for kaimei onwards) -->
|
||||
<score_challenge __type="bool">0</score_challenge>
|
||||
<!-- Handle favorites through data_mods/<friendID>.fav files (Note: allows UNLIMITED favorites) -->
|
||||
<local_favorites __type="bool">0</local_favorites>
|
||||
|
||||
<!-- Classic patches -->
|
||||
<!-- Prevent crash on boot when using a different default audio source (aka HDMI audio patch) -->
|
||||
@ -29,10 +39,6 @@
|
||||
<!-- quick_retire with pfree also enables quick retry: press numpad 8 during song or on result screen to retry (keep holding to skip option select) -->
|
||||
<quick_retire __type="bool">0</quick_retire>
|
||||
|
||||
<!-- Network features -->
|
||||
<!-- Bring back score challenge in the game for servers supporting it (only for kaimei onwards) -->
|
||||
<score_challenge __type="bool">0</score_challenge>
|
||||
|
||||
<!-- Audio offset -->
|
||||
<!-- Offset the audio by x ms (negative plays audio earlier). This will disable keysounds -->
|
||||
<audio_offset __type="s8">0</audio_offset>
|
||||
@ -134,10 +140,18 @@
|
||||
<disable_expansions __type="bool">0</disable_expansions>
|
||||
<!-- Copy the new table information over top the old tables (automatically enables disable_expansions) -->
|
||||
<disable_redirection __type="bool">0</disable_redirection>
|
||||
|
||||
<!-- Custom category options -->
|
||||
<!-- minimum songid for a song to be seen as "custom" -->
|
||||
<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>
|
||||
<!-- Category title for customs -->
|
||||
<custom_category_title __type="str">Customs</custom_category_title>
|
||||
<!-- Format used for category title (see -->
|
||||
<custom_category_format __type="str">[ol:4][olc:d92f0d]%s</custom_category_format>
|
||||
<!-- Format used for custom song titles on song select (Note: colors not working for kaimei and unilab, but rotation does, e.g.: * [rz:3]%s[/rz] ) -->
|
||||
<custom_track_title_format __type="str"></custom_track_title_format>
|
||||
|
||||
<!-- Translation -->
|
||||
<!-- Disable .dict string replacements and .ips patches -->
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
struct popnhax_config {
|
||||
uint8_t game_version;
|
||||
bool practice_mode;
|
||||
bool hidden_is_offset;
|
||||
bool iidx_hard_gauge;
|
||||
@ -17,6 +18,8 @@ struct popnhax_config {
|
||||
uint8_t custom_categ;
|
||||
uint16_t custom_categ_min_songid;
|
||||
uint16_t custom_categ_max_songid;
|
||||
bool custom_exclude_from_version;
|
||||
bool custom_exclude_from_level;
|
||||
bool force_hd_timing;
|
||||
uint8_t force_hd_resolution;
|
||||
bool force_unlocks;
|
||||
@ -29,6 +32,7 @@ struct popnhax_config {
|
||||
bool force_full_opt;
|
||||
bool netvs_off;
|
||||
bool guidese_off;
|
||||
bool local_favorites;
|
||||
|
||||
bool patch_db;
|
||||
bool disable_expansions;
|
||||
@ -55,6 +59,9 @@ struct popnhax_config {
|
||||
bool survival_iidx;
|
||||
bool survival_spicy;
|
||||
int8_t base_offset;
|
||||
char custom_category_title[16];
|
||||
char custom_category_format[64];
|
||||
char custom_track_title_format[64];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -19,13 +19,27 @@
|
||||
#include "minhook/hde32.h"
|
||||
#include "minhook/include/MinHook.h"
|
||||
|
||||
uint8_t g_game_version;
|
||||
uint32_t g_playerdata_ptr_addr; //pointer to the playerdata memory zone (offset 0x08 is popn friend ID as ascii (12 char long), offset 0x1A5 is "is logged in" flag)
|
||||
char *g_current_friendid;
|
||||
uint32_t g_current_songid;
|
||||
|
||||
void (*add_song_in_list)();
|
||||
//game code takes array start address from offset 0xC and the address after the list end from offset 0x10
|
||||
typedef struct songlist_s {
|
||||
uint32_t dummy[3];
|
||||
uint32_t array_start;
|
||||
uint32_t array_end;
|
||||
} songlist_t;
|
||||
|
||||
bool g_subcategmode = false;
|
||||
uint32_t g_min_id = 4000;
|
||||
uint32_t g_max_id = 0;
|
||||
|
||||
const char *g_categname = "Custom Tracks";
|
||||
const char *g_categicon = "cate_13";
|
||||
const char *g_categformat = "[ol:4][olc:d92f0d]%s";
|
||||
char *g_categname;
|
||||
const char *g_categicon = "cate_cc";
|
||||
char *g_categformat;
|
||||
char *g_customformat;
|
||||
|
||||
char *g_string_addr;
|
||||
uint8_t idx = 0;
|
||||
@ -33,6 +47,190 @@ uint32_t tmp_size = 0;
|
||||
uint32_t tmp_categ_ptr = 0;
|
||||
uint32_t tmp_songlist_ptr = 0;
|
||||
|
||||
uint32_t* favorites;
|
||||
uint32_t favorites_addr = (uint32_t)&favorites;
|
||||
uint32_t favorites_count = 0;
|
||||
songlist_t favorites_struct;
|
||||
uint32_t favorites_struct_addr = (uint32_t)&favorites_struct;
|
||||
|
||||
void add_song_to_favorites()
|
||||
{
|
||||
favorites = (uint32_t *) realloc(favorites, sizeof(uint32_t)*(favorites_count+5));
|
||||
favorites[favorites_count++] = g_current_songid | 0x00060000; // game wants this otherwise only easy difficulty will appear
|
||||
return;
|
||||
}
|
||||
void remove_song_from_favorites()
|
||||
{
|
||||
for (uint32_t i = 0; i < favorites_count; i++)
|
||||
{
|
||||
if ( g_current_songid == (favorites[i] & 0x0000FFFF) )
|
||||
{
|
||||
for (uint32_t j = i+1; j < favorites_count; j++)
|
||||
{
|
||||
favorites[j-1] = favorites[j];
|
||||
}
|
||||
favorites_count--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void prepare_favorite_list(){
|
||||
|
||||
char fav_filepath[64];
|
||||
sprintf(fav_filepath, "data_mods\\%d.%s.fav", g_game_version, g_current_friendid);
|
||||
FILE *file = fopen(fav_filepath, "rb");
|
||||
|
||||
favorites_count = 0;
|
||||
|
||||
if ( file == NULL )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char line[32];
|
||||
|
||||
while (fgets(line, sizeof(line), file)) {
|
||||
/* note that fgets don't strip the terminating \n, checking its
|
||||
presence would allow to handle lines longer that sizeof(line) */
|
||||
int songid = strtol(line, NULL, 10);
|
||||
if ( songid != 0 )
|
||||
{
|
||||
g_current_songid = songid;
|
||||
add_song_to_favorites();
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
//printf("added %d songs from %s to favorites\n",favorites_count,fav_filepath);
|
||||
return;
|
||||
}
|
||||
|
||||
void commit_favorites()
|
||||
{
|
||||
if ( favorites_count == 0 )
|
||||
return;
|
||||
|
||||
char fav_filepath[64];
|
||||
sprintf(fav_filepath, "data_mods\\%d.%s.fav", g_game_version, g_current_friendid);
|
||||
FILE *file = fopen(fav_filepath, "w");
|
||||
|
||||
if ( file == NULL )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < favorites_count; i++)
|
||||
{
|
||||
fprintf(file, "%d\n", (favorites[i] & 0x0000FFFF));
|
||||
}
|
||||
fclose(file);
|
||||
return;
|
||||
}
|
||||
|
||||
void (*real_song_is_in_favorite)();
|
||||
void hook_song_is_in_favorite()
|
||||
{
|
||||
__asm("push ecx\n");
|
||||
__asm("push edx\n");
|
||||
//dx contains songid
|
||||
__asm("mov _g_current_songid, dx\n");
|
||||
|
||||
for (uint32_t i = 0; i < favorites_count; i++)
|
||||
{
|
||||
if ( g_current_songid == (favorites[i] & 0x0000FFFF) )
|
||||
{
|
||||
__asm("pop edx\n");
|
||||
__asm("pop ecx\n");
|
||||
__asm("pop ebx\n");
|
||||
__asm("mov eax, 0x01\n");
|
||||
__asm("ret\n");
|
||||
}
|
||||
}
|
||||
__asm("pop edx\n");
|
||||
__asm("pop ecx\n");
|
||||
__asm("pop ebx\n");
|
||||
__asm("mov eax, 0x00\n");
|
||||
__asm("ret\n");
|
||||
}
|
||||
|
||||
void (*real_add_to_favorite)();
|
||||
void hook_add_to_favorite()
|
||||
{
|
||||
__asm("push ecx\n");
|
||||
//dx contains songid
|
||||
__asm("mov _g_current_songid, dx\n");
|
||||
|
||||
add_song_to_favorites();
|
||||
commit_favorites();
|
||||
|
||||
__asm("mov eax, [_favorites_count]\n");
|
||||
__asm("mov edx, _g_current_songid\n");
|
||||
__asm("pop ecx\n");
|
||||
real_add_to_favorite();
|
||||
}
|
||||
|
||||
|
||||
void (*real_remove_from_favorite)();
|
||||
void hook_remove_from_favorite()
|
||||
{
|
||||
//code pushes edi, esi and ebx as well
|
||||
__asm("push ecx\n");
|
||||
__asm("push edx\n");
|
||||
//dx contains songid
|
||||
__asm("mov _g_current_songid, cx\n");
|
||||
|
||||
remove_song_from_favorites();
|
||||
commit_favorites();
|
||||
|
||||
__asm("pop edx\n");
|
||||
__asm("pop ecx\n");
|
||||
real_remove_from_favorite();
|
||||
}
|
||||
|
||||
//this replaces the category handling function ( add_song_in_list is a subroutine called by the game )
|
||||
void (*real_categ_favorite)();
|
||||
void categ_inject_favorites()
|
||||
{
|
||||
__asm("add esp, 0xC"); // cancel a sub esp 0xC that is added by this code for no reason
|
||||
__asm("push ecx\n");
|
||||
__asm("push edx\n");
|
||||
|
||||
//fake login if necessary
|
||||
__asm("mov ecx, dword ptr [_g_playerdata_ptr_addr]\n");
|
||||
__asm("mov edx, [ecx]\n");
|
||||
__asm("add edx, 0x1A5\n"); //offset where result screen is checking to decide if the favorite option should be displayed/handled
|
||||
__asm("mov ecx, [edx]\n");
|
||||
__asm("cmp ecx, 0\n");
|
||||
__asm("jne skip_fake_login\n");
|
||||
__asm("mov dword ptr [edx], 0xFF000001\n");
|
||||
__asm("sub edx, 0x19D\n"); //back to popn friendid offset
|
||||
__asm("mov dword ptr [edx], 0x61666564\n"); // "defa"
|
||||
__asm("add edx, 0x04\n");
|
||||
__asm("mov dword ptr [edx], 0x00746C75\n"); // "ult"
|
||||
__asm("skip_fake_login:\n");
|
||||
|
||||
//retrieve songlist according to friend id
|
||||
__asm("mov ecx, dword ptr [_g_playerdata_ptr_addr]\n");
|
||||
__asm("mov edx, [ecx]\n");
|
||||
__asm("add edx, 0x08\n");
|
||||
__asm("mov _g_current_friendid, edx\n");
|
||||
|
||||
prepare_favorite_list();
|
||||
|
||||
//finally prepare songlist struct and inject it in the category
|
||||
favorites_struct.array_start = (uint32_t)favorites;
|
||||
favorites_struct.array_end = (uint32_t)&(favorites[favorites_count]);
|
||||
__asm("pop edx\n");
|
||||
__asm("pop ecx\n");
|
||||
__asm("push ecx\n");
|
||||
__asm("push _favorites_struct_addr\n");
|
||||
__asm("lea eax, dword ptr [ecx+0x24]\n");
|
||||
__asm("call [_add_song_in_list]\n");
|
||||
__asm("pop ecx\n");
|
||||
__asm("ret\n"); //because we patch inside the function
|
||||
}
|
||||
|
||||
#define SIMPLE_CATEG_ALLOC 1
|
||||
|
||||
#if SIMPLE_CATEG_ALLOC == 1
|
||||
@ -43,11 +241,7 @@ 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;
|
||||
songlist_t songlist_struct;
|
||||
uint32_t songlist_struct_addr = (uint32_t)&songlist_struct;
|
||||
|
||||
typedef struct {
|
||||
@ -100,17 +294,16 @@ static subcategory_s* get_subcateg(char *title)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void (*add_song_in_list)();
|
||||
void (*categ_inject_songlist)();
|
||||
|
||||
struct songlist_struct_s *new_song_list = NULL;
|
||||
songlist_t *new_song_list = NULL;
|
||||
void get_subcateg_size_impl()
|
||||
{
|
||||
__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 = (songlist_t*) 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]);
|
||||
|
||||
@ -137,7 +330,7 @@ void hook_event_categ_generation()
|
||||
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 = (songlist_t*) 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;
|
||||
@ -379,6 +572,187 @@ void hook_categ_listing()
|
||||
real_categ_listing();
|
||||
}
|
||||
|
||||
void (*real_song_printf)();
|
||||
void hook_song_printf()
|
||||
{
|
||||
__asm("push eax\n");
|
||||
__asm("push ebx\n");
|
||||
__asm("mov eax, [esp+0x50]\n");
|
||||
__asm("cmp eax, _g_min_id\n");
|
||||
__asm("jb print_regular_song\n");
|
||||
__asm("cmp dword ptr _g_max_id, 0\n");
|
||||
__asm("je print_custom_song\n");
|
||||
__asm("cmp eax, _g_max_id\n");
|
||||
__asm("ja print_regular_song\n");
|
||||
|
||||
__asm("print_custom_song:\n");
|
||||
|
||||
__asm("lea eax, [esp+0x08]\n");
|
||||
__asm("mov ebx, _g_customformat\n");
|
||||
__asm("mov [eax], ebx\n");
|
||||
|
||||
__asm("print_regular_song:\n");
|
||||
__asm("pop ebx\n");
|
||||
__asm("pop eax\n");
|
||||
real_song_printf();
|
||||
}
|
||||
|
||||
void (*real_artist_printf)();
|
||||
void hook_artist_printf()
|
||||
{
|
||||
__asm("push eax\n");
|
||||
__asm("push ebx\n");
|
||||
__asm("mov eax, [esp+0x50]\n");
|
||||
__asm("cmp eax, _g_min_id\n");
|
||||
__asm("jb print_regular_artist\n");
|
||||
__asm("cmp dword ptr _g_max_id, 0\n");
|
||||
__asm("je print_custom_artist\n");
|
||||
__asm("cmp eax, _g_max_id\n");
|
||||
__asm("ja print_regular_artist\n");
|
||||
|
||||
__asm("print_custom_artist:\n");
|
||||
|
||||
__asm("lea eax, [esp+0x08]\n");
|
||||
__asm("mov ebx, _g_customformat\n");
|
||||
__asm("mov [eax], ebx\n");
|
||||
|
||||
__asm("print_regular_artist:\n");
|
||||
__asm("pop ebx\n");
|
||||
__asm("pop eax\n");
|
||||
real_artist_printf();
|
||||
}
|
||||
|
||||
static bool patch_custom_track_format(const char *game_dll_fn) {
|
||||
DWORD dllSize = 0;
|
||||
char *data = getDllData(game_dll_fn, &dllSize);
|
||||
|
||||
//hook format string for song/genre name
|
||||
{
|
||||
int64_t pattern_offset = search(data, dllSize, "\x83\xC4\x08\x8B\x44\x24\x50\x50\x68", 9, 0);
|
||||
if (pattern_offset == -1) {
|
||||
pattern_offset = search(data, dllSize, "\x83\xC4\x08\x8B\x44\x24\x4C\x50\x68", 9, 0); //usaneko
|
||||
if (pattern_offset == -1) {
|
||||
LOG("popnhax: custom_track_format: cannot find song/genre print function\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t patch_addr = (int64_t)data + pattern_offset - 0x07;
|
||||
|
||||
MH_CreateHook((LPVOID)patch_addr, (LPVOID)hook_song_printf,
|
||||
(void **)&real_song_printf);
|
||||
}
|
||||
|
||||
//hook format string for artist
|
||||
{
|
||||
int64_t pattern_offset = search(data, dllSize, "\x83\xC4\x08\x33\xFF\x8B\x43\x0C\x8B\x70\x04\x83\xC0\x04", 14, 0);
|
||||
if (pattern_offset == -1) {
|
||||
LOG("popnhax: custom_track_format: cannot find artist print function\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t patch_addr = (int64_t)data + pattern_offset - 0x07;
|
||||
|
||||
MH_CreateHook((LPVOID)patch_addr, (LPVOID)hook_artist_printf,
|
||||
(void **)&real_artist_printf);
|
||||
}
|
||||
|
||||
LOG("popnhax: custom_track_format: custom format injected\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool patch_favorite_categ(const char *game_dll_fn) {
|
||||
|
||||
DWORD dllSize = 0;
|
||||
char *data = getDllData(game_dll_fn, &dllSize);
|
||||
|
||||
if (add_song_in_list == NULL) {
|
||||
int64_t pattern_offset = search(data, dllSize, "\x8B\x4D\x10\x8B\x5D\x0C\x8B\xF1", 8, 0);
|
||||
if (pattern_offset == -1) {
|
||||
LOG("popnhax: local_favorites: cannot find add_song_in_list function\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
//I need to call this subfunction from my hook
|
||||
add_song_in_list = (void (*)())(data + pattern_offset - 0x12);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
LOG("popnhax: local_favorites: cannot find category jump table\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t function_call_addr = (int64_t)(data + pattern_offset + 0x05 + 0x5A);
|
||||
uint32_t function_offset = *((uint32_t*)(function_call_addr +0x01));
|
||||
uint64_t function_addr = function_call_addr+5+function_offset;
|
||||
|
||||
MH_CreateHook((LPVOID)function_addr, (LPVOID)categ_inject_favorites,
|
||||
(void **)&real_categ_favorite);
|
||||
}
|
||||
//categ_inject_favorites will need to force "logged in" status (for result screen)
|
||||
{
|
||||
//this is the same function used in score challenge patch, checking if we're logged in... but now we just directly retrieve the address
|
||||
int64_t pattern_offset = search(data, dllSize, "\x8B\x01\x8B\x50\x14\xFF\xE2\xC3\xCC\xCC\xCC\xCC", 12, 0);
|
||||
if (pattern_offset == -1) {
|
||||
LOG("popnhax: local_favorites: cannot find check if logged function\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
g_playerdata_ptr_addr = (*(uint32_t *)(data + pattern_offset + 0x25));
|
||||
}
|
||||
|
||||
//hook result screen to replace 3 functions
|
||||
{
|
||||
int64_t first_loc = search(data, dllSize, "\xBF\x07\x00\x00\x00\xC6\x85\x61\xD3", 9, 0);
|
||||
if (first_loc == -1) {
|
||||
LOG("popnhax: local_favorites: cannot find result screen function\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
//song is in favorite
|
||||
int64_t second_loc = search(data, 1000, "\x8B\xC8\xE8", 3, first_loc);
|
||||
if (second_loc == -1) {
|
||||
LOG("popnhax: local_favorites: cannot retrieve is song in favorites call\n");
|
||||
return false;
|
||||
}
|
||||
uint64_t function_call_addr = (int64_t)(data + second_loc + 0x02);
|
||||
uint32_t function_offset = *((uint32_t*)(function_call_addr +0x01));
|
||||
uint64_t function_addr = function_call_addr+5+function_offset;
|
||||
MH_CreateHook((LPVOID)function_addr, (LPVOID)hook_song_is_in_favorite,
|
||||
(void **)&real_song_is_in_favorite);
|
||||
|
||||
//remove from favorites
|
||||
int64_t third_loc = search(data, 1000, "\x6A\x01\x6A\x00\x68", 5, second_loc);
|
||||
if (third_loc == -1) {
|
||||
LOG("popnhax: local_favorites: cannot retrieve remove from favorites call\n");
|
||||
return false;
|
||||
}
|
||||
uint64_t function2_call_addr = (int64_t)(data + third_loc - 0x05);
|
||||
uint32_t function2_offset = *((uint32_t*)(function2_call_addr +0x01));
|
||||
uint64_t function2_addr = function2_call_addr+5+function2_offset;
|
||||
MH_CreateHook((LPVOID)function2_addr, (LPVOID)hook_remove_from_favorite,
|
||||
(void **)&real_remove_from_favorite);
|
||||
|
||||
//add to favorites
|
||||
int64_t fourth_loc = search(data, 1000, "\x6A\x01\x6A\x00\x68", 5, third_loc+2);
|
||||
if (fourth_loc == -1) {
|
||||
LOG("popnhax: local_favorites: cannot retrieve add to favorites call\n");
|
||||
return false;
|
||||
}
|
||||
uint64_t function3_call_addr = (int64_t)(data + fourth_loc - 0x05);
|
||||
uint32_t function3_offset = *((uint32_t*)(function3_call_addr +0x01));
|
||||
uint64_t function3_addr = function3_call_addr+5+function3_offset;
|
||||
MH_CreateHook((LPVOID)function3_addr, (LPVOID)hook_add_to_favorite,
|
||||
(void **)&real_add_to_favorite);
|
||||
|
||||
}
|
||||
LOG("popnhax: local_favorites: favorite category handling replaced\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool patch_custom_categ(const char *game_dll_fn) {
|
||||
|
||||
DWORD dllSize = 0;
|
||||
@ -595,10 +969,63 @@ static void load_databases() {
|
||||
LOG("\n");
|
||||
}
|
||||
|
||||
bool patch_custom_categs(const char *dllFilename, uint8_t mode, uint16_t min, uint16_t max)
|
||||
void (*real_after_getlevel)();
|
||||
void hook_after_getlevel()
|
||||
{
|
||||
g_min_id = min;
|
||||
g_max_id = max;
|
||||
__asm("push ebx\n");
|
||||
__asm("mov ebx, dword ptr [esp+0x04]\n");
|
||||
__asm("cmp ebx, _g_min_id\n");
|
||||
__asm("jb real_level\n");
|
||||
__asm("cmp dword ptr _g_max_id, 0\n");
|
||||
__asm("je force_level_0\n");
|
||||
__asm("cmp ebx, _g_max_id\n");
|
||||
__asm("ja real_level\n");
|
||||
|
||||
__asm("force_level_0:\n");
|
||||
__asm("mov eax, 0x00\n");
|
||||
|
||||
__asm("real_level:\n");
|
||||
__asm("pop ebx\n");
|
||||
real_after_getlevel();
|
||||
}
|
||||
|
||||
bool patch_exclude(const char *game_dll_fn)
|
||||
{
|
||||
|
||||
DWORD dllSize = 0;
|
||||
char *data = getDllData(game_dll_fn, &dllSize);
|
||||
|
||||
{
|
||||
int64_t pattern_offset = search(data, dllSize, "\x8B\xF8\x83\xC4\x08\x85\xFF\x7E\x42", 9, 0);
|
||||
if (pattern_offset == -1) {
|
||||
LOG("popnhax: custom_exclude_from_level: cannot find songlist processing table\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t patch_addr = (int64_t)(data + pattern_offset);
|
||||
|
||||
MH_CreateHook((LPVOID)patch_addr, (LPVOID)hook_after_getlevel,
|
||||
(void **)&real_after_getlevel);
|
||||
}
|
||||
|
||||
LOG("popnhax: custom_exclude_from_level: Custom songs excluded from level listings\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool patch_custom_categs(const char *dllFilename, struct popnhax_config *config)
|
||||
{
|
||||
LOG("config is %p\n", (void*)config);
|
||||
g_min_id = config->custom_categ_min_songid;
|
||||
g_max_id = config->custom_categ_max_songid;
|
||||
uint8_t mode = config->custom_categ;
|
||||
|
||||
g_categname = config->custom_category_title;
|
||||
g_categformat = config->custom_category_format;
|
||||
|
||||
LOG("custom songid range is [%d;%d]\n", g_min_id, g_max_id);
|
||||
LOG("category name is %s\n", g_categname);
|
||||
LOG("category format is %s\n", g_categformat);
|
||||
|
||||
#if SIMPLE_CATEG_ALLOC == 1
|
||||
songlist = (uint32_t*)calloc(1,5);
|
||||
#endif
|
||||
@ -608,5 +1035,21 @@ bool patch_custom_categs(const char *dllFilename, uint8_t mode, uint16_t min, ui
|
||||
load_databases();
|
||||
}
|
||||
|
||||
if ( config->custom_track_title_format[0] != '\0' )
|
||||
{
|
||||
g_customformat = config->custom_track_title_format;
|
||||
LOG("custom title format is %s\n", g_customformat);
|
||||
patch_custom_track_format(dllFilename);
|
||||
}
|
||||
|
||||
if (config->custom_exclude_from_level)
|
||||
patch_exclude(dllFilename);
|
||||
|
||||
return patch_custom_categ(dllFilename);
|
||||
}
|
||||
|
||||
bool patch_local_favorites(const char *dllFilename, uint8_t version)
|
||||
{
|
||||
g_game_version = version;
|
||||
return patch_favorite_categ(dllFilename);
|
||||
}
|
@ -2,7 +2,9 @@
|
||||
#define __CUSTOM_CATEGS_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "popnhax/config.h"
|
||||
|
||||
bool patch_custom_categs(const char *dllFilename, uint8_t mode, uint16_t min, uint16_t max);
|
||||
bool patch_custom_categs(const char *dllFilename, struct popnhax_config *config);
|
||||
bool patch_local_favorites(const char *dllFilename, uint8_t version);
|
||||
|
||||
#endif
|
||||
|
@ -32,7 +32,7 @@
|
||||
|
||||
#include "SearchFile.h"
|
||||
|
||||
#define PROGRAM_VERSION "1.11.beta1"
|
||||
#define PROGRAM_VERSION "1.11.beta2"
|
||||
|
||||
const char *g_game_dll_fn = NULL;
|
||||
const char *g_config_fn = NULL;
|
||||
@ -191,6 +191,18 @@ PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_U16, struct popnhax_config, custom_categ_mi
|
||||
"/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_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, custom_exclude_from_version,
|
||||
"/popnhax/custom_exclude_from_version")
|
||||
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, custom_exclude_from_level,
|
||||
"/popnhax/custom_exclude_from_level")
|
||||
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_STR, struct popnhax_config, custom_category_title,
|
||||
"/popnhax/custom_category_title")
|
||||
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_STR, struct popnhax_config, custom_category_format,
|
||||
"/popnhax/custom_category_format")
|
||||
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_STR, struct popnhax_config, custom_track_title_format,
|
||||
"/popnhax/custom_track_title_format")
|
||||
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, local_favorites,
|
||||
"/popnhax/local_favorites")
|
||||
PSMAP_END
|
||||
|
||||
enum BufferIndexes {
|
||||
@ -1441,7 +1453,7 @@ static bool patch_datecode(char *datecode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool patch_database(uint8_t force_unlocks) {
|
||||
static bool patch_database() {
|
||||
DWORD dllSize = 0;
|
||||
char *data = getDllData(g_game_dll_fn, &dllSize);
|
||||
|
||||
@ -1519,10 +1531,7 @@ static bool patch_database(uint8_t force_unlocks) {
|
||||
config.disable_expansions = true;
|
||||
}
|
||||
|
||||
musichax_core_init(
|
||||
force_unlocks,
|
||||
!config.disable_expansions,
|
||||
!config.disable_redirection,
|
||||
musichax_core_init(&config,
|
||||
target,
|
||||
|
||||
data,
|
||||
@ -1554,6 +1563,9 @@ static bool patch_database(uint8_t force_unlocks) {
|
||||
);
|
||||
limit_table[STYLE_TABLE_IDX] = new_limit_table[STYLE_TABLE_IDX];
|
||||
|
||||
if (config.custom_exclude_from_version)
|
||||
LOG("popnhax: custom_exclude_from_version: customs excluded from version folders\n"); //musichax_core_init took care of it
|
||||
|
||||
if (config.disable_redirection) {
|
||||
LOG("Redirection-related code is disabled, buffer address, buffer size and related patches will not be applied");
|
||||
printf("Redirection-related code is disabled, buffer address, buffer size and related patches will not be applied");
|
||||
@ -3869,7 +3881,7 @@ static bool patch_score_challenge()
|
||||
{
|
||||
int64_t pattern_offset = search(data, dllSize, "\xF7\xD8\x1B\xC0\x40\xC3\xE8", 7, 0);
|
||||
if (pattern_offset == -1) {
|
||||
LOG("popnhax: score challenge: cannot find check if logged function\n");
|
||||
LOG("popnhax: score challenge: cannot find check if normal mode function\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -5291,6 +5303,22 @@ static bool option_net_ojama_off(){
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t get_version()
|
||||
{
|
||||
DWORD dllSize = 0;
|
||||
char *data = getDllData(g_game_dll_fn, &dllSize);
|
||||
{
|
||||
int64_t pattern_offset = search(data, dllSize, "\x00\x8B\x56\x04\x0F\xB7\x02\xE8", 8, 0);
|
||||
if (pattern_offset == -1) {
|
||||
LOG("popnhax: get_version: cannot retrieve game version (eclale or less?)\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t version = *(uint8_t*)(data + pattern_offset + 14);
|
||||
return version;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
|
||||
switch (ul_reason_for_call) {
|
||||
case DLL_PROCESS_ATTACH: {
|
||||
@ -5362,7 +5390,9 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
|
||||
free(tmp_name);
|
||||
}
|
||||
|
||||
LOG("popnhax: game dll: %s\n",g_game_dll_fn);
|
||||
uint8_t game_version = get_version();
|
||||
|
||||
LOG("popnhax: game dll: %s (popn%d)\n",g_game_dll_fn, game_version);
|
||||
LOG("popnhax: config file: %s\n",g_config_fn);
|
||||
|
||||
if (!_load_config(g_config_fn, &config, config_psmap))
|
||||
@ -5371,12 +5401,15 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
config.game_version = game_version;
|
||||
|
||||
if (force_trans_debug)
|
||||
config.translation_debug = true;
|
||||
|
||||
if (force_no_omni)
|
||||
config.patch_db = false;
|
||||
|
||||
|
||||
if (!config.disable_multiboot)
|
||||
{
|
||||
/* automatically force datecode based on dll name when applicable (e.g. popn22_2022061300.dll and no force_datecode) */
|
||||
@ -5386,21 +5419,26 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
|
||||
LOG("popnhax: multiboot autotune activated (custom game dll, force_datecode off)\n");
|
||||
memcpy(config.force_datecode, g_game_dll_fn+7, 10);
|
||||
LOG("popnhax: multiboot: auto set datecode to %s\n", config.force_datecode);
|
||||
if (config.score_challenge && ( strcmp(config.force_datecode,"2020092800") <= 0 ) )
|
||||
if (config.score_challenge && ( config.game_version < 26 || strcmp(config.force_datecode,"2020092800") <= 0 ) )
|
||||
{
|
||||
LOG("popnhax: multiboot: auto disable score challenge patch (already ingame)\n");
|
||||
config.score_challenge = false;
|
||||
}
|
||||
if (config.patch_db && ( strcmp(config.force_datecode,"2016121400") < 0 ) )
|
||||
if (config.patch_db && ( config.game_version == 0 || strcmp(config.force_datecode,"2016121400") < 0 ) )
|
||||
{
|
||||
LOG("popnhax: multiboot: auto disable omnimix patch (not compatible)\n");
|
||||
config.patch_db = false;
|
||||
}
|
||||
if (config.guidese_off && ( strcmp(config.force_datecode,"2016121400") < 0 ) )
|
||||
if (config.guidese_off && ( config.game_version == 0 || strcmp(config.force_datecode,"2016121400") < 0 ) )
|
||||
{
|
||||
LOG("popnhax: multiboot: auto disable Guide SE patch (not compatible)\n");
|
||||
config.guidese_off = false;
|
||||
}
|
||||
if (config.local_favorites && ( config.game_version == 0 || strcmp(config.force_datecode,"2016121400") < 0 ) )
|
||||
{
|
||||
LOG("popnhax: multiboot: auto disable local favorites patch (not compatible)\n");
|
||||
config.local_favorites = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5475,6 +5513,8 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
|
||||
|
||||
if (config.quick_retire) {
|
||||
patch_quick_retire(config.pfree);
|
||||
} else if (config.back_to_song_select) {
|
||||
LOG("WARNING: back to song select cannot be enabled without quick retire.\n");
|
||||
}
|
||||
|
||||
if (config.score_challenge) {
|
||||
@ -5587,8 +5627,8 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
|
||||
patch_db_power_points();
|
||||
patch_db_fix_cursor();
|
||||
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_custom_categs(g_game_dll_fn, &config);
|
||||
patch_database();
|
||||
}
|
||||
|
||||
if (config.force_unlocks) {
|
||||
@ -5601,6 +5641,16 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
|
||||
force_unlock_deco_parts();
|
||||
}
|
||||
|
||||
if (config.local_favorites)
|
||||
{
|
||||
if ( config.game_version == 0 )
|
||||
{
|
||||
LOG("popnhax: local_favorites: patch is not compatible with your game version.\n");
|
||||
} else {
|
||||
patch_local_favorites(g_game_dll_fn, config.game_version);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.force_full_opt)
|
||||
option_full();
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include "imports/avs.h"
|
||||
#include "util/patch.h"
|
||||
#include "util/log.h"
|
||||
#include "xmlhelper.h"
|
||||
|
||||
#include "tableinfo.h"
|
||||
@ -66,7 +67,7 @@ uint32_t add_chart(uint32_t cur_idx, uint8_t *folder, uint8_t *filename, int32_t
|
||||
uint32_t file_type, uint16_t used_keys, bool override_idx);
|
||||
|
||||
void parse_charadb(const char *input_filename, const char *target);
|
||||
void parse_musicdb(const char *input_filename, const char *target);
|
||||
void parse_musicdb(const char *input_filename, const char *target, struct popnhax_config *config);
|
||||
|
||||
std::map<uint32_t, int8_t> chart_type_overrides;
|
||||
|
||||
@ -818,7 +819,7 @@ void parse_charadb(const char *input_filename, const char *target) {
|
||||
free(config_xml);
|
||||
}
|
||||
|
||||
void parse_musicdb(const char *input_filename, const char *target) {
|
||||
void parse_musicdb(const char *input_filename, const char *target, struct popnhax_config *config) {
|
||||
if (!file_exists(input_filename)) {
|
||||
printf("Couldn't find %s, skipping...\n", input_filename);
|
||||
return;
|
||||
@ -915,6 +916,18 @@ void parse_musicdb(const char *input_filename, const char *target) {
|
||||
}
|
||||
}
|
||||
|
||||
//force loading background for unilab
|
||||
m->mask |= 0x100;
|
||||
|
||||
if ( config->custom_categ
|
||||
&& config->custom_exclude_from_version
|
||||
&& idx >= config->custom_categ_min_songid
|
||||
&& (config->custom_categ_max_songid == 0 || idx <= config->custom_categ_max_songid) )
|
||||
{
|
||||
m->cs_version = 0;
|
||||
m->folder = 0;
|
||||
}
|
||||
|
||||
if ((prop_chart = property_search(config_xml, prop, "charts/chart"))) {
|
||||
for (; prop_chart != NULL; prop_chart = property_node_traversal(
|
||||
prop_chart, TRAVERSE_NEXT_SEARCH_RESULT)) {
|
||||
@ -992,7 +1005,7 @@ void parse_musicdb(const char *input_filename, const char *target) {
|
||||
free(config_xml);
|
||||
}
|
||||
|
||||
void load_databases(const char *target_datecode) {
|
||||
void load_databases(const char *target_datecode, struct popnhax_config *config) {
|
||||
|
||||
SearchFile s;
|
||||
printf("XML db files search...\n");
|
||||
@ -1013,11 +1026,11 @@ void load_databases(const char *target_datecode) {
|
||||
if ( strstr(result[i].c_str(), "musicdb") == NULL )
|
||||
continue;
|
||||
printf("(musicdb) Loading %s...\n", result[i].c_str());
|
||||
parse_musicdb(result[i].c_str(), target_datecode);
|
||||
parse_musicdb(result[i].c_str(), target_datecode, config);
|
||||
}
|
||||
}
|
||||
|
||||
void musichax_core_init(bool force_unlocks, bool is_expansion_allowed, bool is_redirection_allowed,
|
||||
void musichax_core_init(struct popnhax_config *config,
|
||||
char *target_datecode,
|
||||
|
||||
char *base_data,
|
||||
@ -1036,6 +1049,11 @@ void musichax_core_init(bool force_unlocks, bool is_expansion_allowed, bool is_r
|
||||
|
||||
uint64_t chara_size, uint64_t *new_chara_size, char *orig_chara_data,
|
||||
uint8_t **new_chara_table) {
|
||||
|
||||
bool force_unlocks = config->force_unlocks;
|
||||
bool is_expansion_allowed = !config->disable_expansions;
|
||||
bool is_redirection_allowed = !config->disable_redirection;
|
||||
|
||||
if (style_size > fontstyle_table_size) {
|
||||
fontstyle_table_size = style_size;
|
||||
}
|
||||
@ -1181,7 +1199,7 @@ void musichax_core_init(bool force_unlocks, bool is_expansion_allowed, bool is_r
|
||||
cur->chara_x, cur->chara_y, cur->unk1, cur->display_bpm, cur->hold_flags, true);
|
||||
}
|
||||
|
||||
load_databases((const char *)target_datecode);
|
||||
load_databases((const char *)target_datecode, config);
|
||||
|
||||
// Add some filler charts to fix some bugs (hack)
|
||||
for (int i = 0; i < 10; i++) {
|
||||
|
@ -2,10 +2,11 @@
|
||||
#define __LOADER_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "popnhax/config.h"
|
||||
|
||||
int8_t get_chart_type_override(uint8_t *, uint32_t, uint32_t);
|
||||
|
||||
void musichax_core_init(bool force_unlocks, bool is_expansion_allowed, bool is_redirection_allowed,
|
||||
void musichax_core_init(struct popnhax_config *config,
|
||||
char *target_datecode, char *base_data, uint64_t music_size,
|
||||
uint64_t *new_music_size, char *orig_music_data, uint8_t **new_music_table,
|
||||
uint64_t chart_size, uint64_t *new_chart_size, char *orig_chart_data,
|
||||
|
@ -56,10 +56,10 @@ bool patch_sjis(const char *dllFilename, const char *find, uint8_t find_size, in
|
||||
} while (valid_sjis);
|
||||
|
||||
if (dump_dict)
|
||||
{
|
||||
{
|
||||
//fprintf(g_dict_applied_fp,"0x%x;%s;%s\n",rva_to_offset(dllFilename, (uint32_t)*offset),(char*)find,(char*)replace);
|
||||
fprintf(g_dict_applied_fp,";%s;%s\n",(char*)find,(char*)replace);
|
||||
}
|
||||
}
|
||||
/* safety check replace is not too big */
|
||||
uint8_t free_size = find_size-1;
|
||||
do
|
||||
@ -83,41 +83,41 @@ bool patch_sjis(const char *dllFilename, const char *find, uint8_t find_size, in
|
||||
|
||||
#define RELOC_HIGHLOW 0x3
|
||||
static void perform_reloc(char *data, int32_t delta, uint32_t ext_base, uint32_t ext_delta)
|
||||
{
|
||||
PIMAGE_NT_HEADERS headers = (PIMAGE_NT_HEADERS)((int64_t)data + ((PIMAGE_DOS_HEADER)data)->e_lfanew);
|
||||
PIMAGE_DATA_DIRECTORY datadir = &headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
||||
PIMAGE_BASE_RELOCATION reloc = (PIMAGE_BASE_RELOCATION)(data + datadir->VirtualAddress);
|
||||
{
|
||||
PIMAGE_NT_HEADERS headers = (PIMAGE_NT_HEADERS)((int64_t)data + ((PIMAGE_DOS_HEADER)data)->e_lfanew);
|
||||
PIMAGE_DATA_DIRECTORY datadir = &headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
||||
PIMAGE_BASE_RELOCATION reloc = (PIMAGE_BASE_RELOCATION)(data + datadir->VirtualAddress);
|
||||
|
||||
while(reloc->VirtualAddress != 0)
|
||||
{
|
||||
if (reloc->SizeOfBlock >= sizeof(IMAGE_BASE_RELOCATION))
|
||||
{
|
||||
DWORD relocDescNb = (reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
|
||||
LPWORD relocDescList = (LPWORD)((LPBYTE)reloc + sizeof(IMAGE_BASE_RELOCATION));
|
||||
while(reloc->VirtualAddress != 0)
|
||||
{
|
||||
if (reloc->SizeOfBlock >= sizeof(IMAGE_BASE_RELOCATION))
|
||||
{
|
||||
DWORD relocDescNb = (reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
|
||||
LPWORD relocDescList = (LPWORD)((LPBYTE)reloc + sizeof(IMAGE_BASE_RELOCATION));
|
||||
|
||||
for (DWORD i = 0; i < relocDescNb; i++)
|
||||
{
|
||||
if ( ((relocDescList[i])>>12) == RELOC_HIGHLOW )
|
||||
{
|
||||
DWORD_PTR *p = (DWORD_PTR *)( data + (reloc->VirtualAddress + ((relocDescList[i])&0x0FFF)) );
|
||||
/* Change the offset to adapt to injected module base address */
|
||||
|
||||
DWORD old_prot;
|
||||
VirtualProtect((LPVOID)p, 4, PAGE_EXECUTE_READWRITE, &old_prot);
|
||||
*p += delta;
|
||||
if ( ext_base > 0 && *p >= ((int64_t)data+ext_base) )
|
||||
{
|
||||
//fprintf(stderr,"reloc rva %lx to ext ", *p);
|
||||
*p += ext_delta;
|
||||
//fprintf(stderr," %lx\n", *p);
|
||||
}
|
||||
VirtualProtect((LPVOID)p, 4, old_prot, &old_prot);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Set reloc pointer to the next relocation block */
|
||||
reloc = (PIMAGE_BASE_RELOCATION)((LPBYTE)reloc + reloc->SizeOfBlock);
|
||||
}
|
||||
for (DWORD i = 0; i < relocDescNb; i++)
|
||||
{
|
||||
if ( ((relocDescList[i])>>12) == RELOC_HIGHLOW )
|
||||
{
|
||||
DWORD_PTR *p = (DWORD_PTR *)( data + (reloc->VirtualAddress + ((relocDescList[i])&0x0FFF)) );
|
||||
/* Change the offset to adapt to injected module base address */
|
||||
|
||||
DWORD old_prot;
|
||||
VirtualProtect((LPVOID)p, 4, PAGE_EXECUTE_READWRITE, &old_prot);
|
||||
*p += delta;
|
||||
if ( ext_base > 0 && *p >= ((int64_t)data+ext_base) )
|
||||
{
|
||||
//fprintf(stderr,"reloc rva %lx to ext ", *p);
|
||||
*p += ext_delta;
|
||||
//fprintf(stderr," %lx\n", *p);
|
||||
}
|
||||
VirtualProtect((LPVOID)p, 4, old_prot, &old_prot);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Set reloc pointer to the next relocation block */
|
||||
reloc = (PIMAGE_BASE_RELOCATION)((LPBYTE)reloc + reloc->SizeOfBlock);
|
||||
}
|
||||
}
|
||||
|
||||
#define BYTE3_TO_UINT(bp) \
|
||||
@ -134,191 +134,191 @@ static void perform_reloc(char *data, int32_t delta, uint32_t ext_base, uint32_t
|
||||
static bool patch_translation_ips(const char *dllFilename, const char *foldername, bool dump_dll)
|
||||
{
|
||||
#define IPS_READ(_ips_read_dest, _ips_read_size, _ips_read_name) do {\
|
||||
if ( fread(_ips_read_dest, 1, _ips_read_size, ips_fp) != _ips_read_size )\
|
||||
{\
|
||||
LOG("CANNOT READ %s\n", _ips_read_name);\
|
||||
return false;\
|
||||
}\
|
||||
if ( fread(_ips_read_dest, 1, _ips_read_size, ips_fp) != _ips_read_size )\
|
||||
{\
|
||||
LOG("CANNOT READ %s\n", _ips_read_name);\
|
||||
return false;\
|
||||
}\
|
||||
} while (0)
|
||||
DWORD dllSize = 0;
|
||||
DWORD dllSize = 0;
|
||||
char *data = getDllData(dllFilename, &dllSize);
|
||||
|
||||
char dict_filepath[64];
|
||||
char dict_filepath[64];
|
||||
sprintf(dict_filepath, "%s%s%s", "data_mods\\", foldername, "\\popn22.ips");
|
||||
FILE *ips_fp = fopen(dict_filepath, "rb");
|
||||
FILE *ips_fp = fopen(dict_filepath, "rb");
|
||||
|
||||
if (ips_fp == NULL)
|
||||
{
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
LOG("popnhax: translation: popn22.ips found\n");
|
||||
LOG("popnhax: translation: popn22.ips found\n");
|
||||
|
||||
/* check .ips header */
|
||||
uint8_t buffer[8];
|
||||
if (fread(&buffer, 1, 5, ips_fp) != 5)
|
||||
return false;
|
||||
|
||||
if (memcmp(buffer, "PATCH", 5) != 0)
|
||||
{
|
||||
LOG("popnhax: translation: invalid .ips header\n");
|
||||
return false;
|
||||
}
|
||||
/* check .ips header */
|
||||
uint8_t buffer[8];
|
||||
if (fread(&buffer, 1, 5, ips_fp) != 5)
|
||||
return false;
|
||||
|
||||
if (dump_dll)
|
||||
{
|
||||
LOG("popnhax: translation debug: dump dll before patch\n");
|
||||
FILE* dllrtp = fopen("dllruntime.dll", "wb");
|
||||
fwrite(data, 1, dllSize, dllrtp);
|
||||
fclose(dllrtp);
|
||||
LOG("popnhax: translation debug: dllruntime.dll generated\n");
|
||||
}
|
||||
if (memcmp(buffer, "PATCH", 5) != 0)
|
||||
{
|
||||
LOG("popnhax: translation: invalid .ips header\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* undo all relocation so you can apply ips patch with correct values, we'll reapply them later */
|
||||
PIMAGE_NT_HEADERS headers = (PIMAGE_NT_HEADERS)((int64_t)data + ((PIMAGE_DOS_HEADER)data)->e_lfanew);
|
||||
DWORD_PTR reloc_delta = (DWORD_PTR)((int64_t)data - headers->OptionalHeader.ImageBase);
|
||||
perform_reloc(data, -1*reloc_delta, 0, 0);
|
||||
if (dump_dll)
|
||||
{
|
||||
LOG("popnhax: translation debug: dump dll before patch\n");
|
||||
FILE* dllrtp = fopen("dllruntime.dll", "wb");
|
||||
fwrite(data, 1, dllSize, dllrtp);
|
||||
fclose(dllrtp);
|
||||
LOG("popnhax: translation debug: dllruntime.dll generated\n");
|
||||
}
|
||||
|
||||
uint32_t trans_base = 0; /* eclale patch adds new section header which I'm relocating */
|
||||
uint32_t trans_base_offset = 0;
|
||||
uint32_t trans_rebase = 0;
|
||||
/* undo all relocation so you can apply ips patch with correct values, we'll reapply them later */
|
||||
PIMAGE_NT_HEADERS headers = (PIMAGE_NT_HEADERS)((int64_t)data + ((PIMAGE_DOS_HEADER)data)->e_lfanew);
|
||||
DWORD_PTR reloc_delta = (DWORD_PTR)((int64_t)data - headers->OptionalHeader.ImageBase);
|
||||
perform_reloc(data, -1*reloc_delta, 0, 0);
|
||||
|
||||
uint32_t offset = 0;
|
||||
uint16_t size = 0;
|
||||
uint16_t count = 0;
|
||||
uint8_t replace[MAX_REPLACE_SIZE] = {0};
|
||||
|
||||
while ( fread(&offset, 1, 3, ips_fp) == 3 && offset != IPS_EOF )
|
||||
{
|
||||
bool skip = false;
|
||||
|
||||
/* need to convert offset to rva before applying patch since the dll is already in memory */
|
||||
uint8_t *bp = (uint8_t *)&offset;
|
||||
offset = BYTE3_TO_UINT(bp);
|
||||
uint32_t rva;
|
||||
uint32_t trans_base = 0; /* eclale patch adds new section header which I'm relocating */
|
||||
uint32_t trans_base_offset = 0;
|
||||
uint32_t trans_rebase = 0;
|
||||
|
||||
if (offset < 0x400)
|
||||
{
|
||||
rva = offset;
|
||||
}
|
||||
else if ( !offset_to_rva(dllFilename, offset, &rva) )
|
||||
{
|
||||
LOG("Invalid offset %x conversion. Skip this patch\n", offset);
|
||||
skip = true;
|
||||
/* still need to go through the loop to increase read pointer accordingly */
|
||||
}
|
||||
|
||||
IPS_READ(&size, 2, "SIZE");
|
||||
bp = (uint8_t *)&size;
|
||||
size = BYTE2_TO_UINT(bp);
|
||||
++count;
|
||||
|
||||
//LOG("%03d: offset %x converted to %x\nsize %d\n", count, offset, rva,size);
|
||||
if ( size == 0 )
|
||||
{
|
||||
uint8_t value;
|
||||
|
||||
IPS_READ(&size, 2, "RLE SIZE");
|
||||
bp = (uint8_t *)&size;
|
||||
size = BYTE2_TO_UINT(bp);
|
||||
uint32_t offset = 0;
|
||||
uint16_t size = 0;
|
||||
uint16_t count = 0;
|
||||
uint8_t replace[MAX_REPLACE_SIZE] = {0};
|
||||
|
||||
IPS_READ(&value, 1, "RLE VALUE");
|
||||
|
||||
//LOG("RLE PATCH! size %d value %d\n", size, value);
|
||||
//fprintf(stderr, "rle value %d (%d bytes)\n",value, size);
|
||||
if ( size > MAX_REPLACE_SIZE )
|
||||
{
|
||||
LOG("RLE replacement too big.\n");
|
||||
return false;
|
||||
}
|
||||
memset(replace, value, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( size > MAX_REPLACE_SIZE )
|
||||
{
|
||||
|
||||
uint16_t remaining = size;
|
||||
uint32_t chunk_rva = rva;
|
||||
|
||||
do {
|
||||
/* patch in multiple chunks */
|
||||
LOG("multipart patch: rva %x, %d remaining\n", chunk_rva, remaining);
|
||||
IPS_READ(&replace, MAX_REPLACE_SIZE, "DATA");
|
||||
|
||||
if ( !skip )
|
||||
patch_memory((int64_t)data+chunk_rva, (char *)replace, MAX_REPLACE_SIZE);
|
||||
|
||||
remaining -= MAX_REPLACE_SIZE;
|
||||
chunk_rva += MAX_REPLACE_SIZE;
|
||||
|
||||
} while (remaining > MAX_REPLACE_SIZE);
|
||||
|
||||
size = remaining;
|
||||
rva = chunk_rva;
|
||||
|
||||
}
|
||||
|
||||
IPS_READ(&replace, size, "DATA");
|
||||
}
|
||||
|
||||
/* eclale woes */
|
||||
if ( trans_base == 0 && rva < 0x400 )
|
||||
{
|
||||
if (memcmp(replace, ".trans", 6) == 0)
|
||||
{
|
||||
trans_base = *(uint32_t*)(replace+0x0C);
|
||||
trans_base_offset = *(uint32_t*)(replace+0x14);
|
||||
//LOG("found .trans section at offset %x rva %x\n", trans_base_offset, trans_base);
|
||||
}
|
||||
}
|
||||
while ( fread(&offset, 1, 3, ips_fp) == 3 && offset != IPS_EOF )
|
||||
{
|
||||
bool skip = false;
|
||||
|
||||
if ( trans_base_offset != 0 && offset >= trans_base_offset )
|
||||
{
|
||||
/* patching into new section */
|
||||
if ( trans_rebase == 0 )
|
||||
{
|
||||
HANDLE hProc = GetCurrentProcess();
|
||||
LPVOID myAlloc = VirtualAllocEx(hProc, NULL, 16384, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||
if (myAlloc == NULL)
|
||||
{
|
||||
LOG("Failed to allocate memory in target process. Error: 0x%lX\n", GetLastError());
|
||||
exit(0);
|
||||
}
|
||||
trans_rebase = (uint32_t)myAlloc - (uint32_t)data;
|
||||
//LOG( "virtualalloc worked; address %x (%p)\n", trans_rebase, myAlloc);
|
||||
/* need to convert offset to rva before applying patch since the dll is already in memory */
|
||||
uint8_t *bp = (uint8_t *)&offset;
|
||||
offset = BYTE3_TO_UINT(bp);
|
||||
uint32_t rva;
|
||||
|
||||
/* seems useless */
|
||||
//memcpy(replace, &trans_rebase, 4);
|
||||
//patch_memory((int64_t)data+0x02d4, (char *)replace, 4);
|
||||
//LOG( "patched .trans section address to %02x %02x %02x %02x\n", replace[0], replace[1], replace[2], replace[3]);
|
||||
}
|
||||
rva = (offset - trans_base_offset) + trans_rebase;
|
||||
//LOG( "off %x - base %x + rebase %x = %x\n\n", offset, trans_base_offset, trans_rebase, rva);
|
||||
//LOG( "offset %x relocated to rva %x.", offset, rva);
|
||||
}
|
||||
|
||||
if ( !skip )
|
||||
{
|
||||
patch_memory((int64_t)data+rva, (char *)replace, size);
|
||||
}
|
||||
}
|
||||
if (offset < 0x400)
|
||||
{
|
||||
rva = offset;
|
||||
}
|
||||
else if ( !offset_to_rva(dllFilename, offset, &rva) )
|
||||
{
|
||||
LOG("Invalid offset %x conversion. Skip this patch\n", offset);
|
||||
skip = true;
|
||||
/* still need to go through the loop to increase read pointer accordingly */
|
||||
}
|
||||
|
||||
/* redo all relocation now the dll is patched */
|
||||
perform_reloc(data, reloc_delta, trans_base, trans_rebase-trans_base);
|
||||
IPS_READ(&size, 2, "SIZE");
|
||||
bp = (uint8_t *)&size;
|
||||
size = BYTE2_TO_UINT(bp);
|
||||
++count;
|
||||
|
||||
LOG("popnhax: translation: IPS patch applied.\n");
|
||||
|
||||
if (dump_dll)
|
||||
{
|
||||
LOG("popnhax: translation debug: dump dll after patch\n");
|
||||
FILE* dllrtp = fopen("dllruntime_patched.dll", "wb");
|
||||
fwrite(data, 1, dllSize, dllrtp);
|
||||
fclose(dllrtp);
|
||||
}
|
||||
//LOG("%03d: offset %x converted to %x\nsize %d\n", count, offset, rva,size);
|
||||
if ( size == 0 )
|
||||
{
|
||||
uint8_t value;
|
||||
|
||||
fclose(ips_fp);
|
||||
return true;
|
||||
IPS_READ(&size, 2, "RLE SIZE");
|
||||
bp = (uint8_t *)&size;
|
||||
size = BYTE2_TO_UINT(bp);
|
||||
|
||||
IPS_READ(&value, 1, "RLE VALUE");
|
||||
|
||||
//LOG("RLE PATCH! size %d value %d\n", size, value);
|
||||
//fprintf(stderr, "rle value %d (%d bytes)\n",value, size);
|
||||
if ( size > MAX_REPLACE_SIZE )
|
||||
{
|
||||
LOG("RLE replacement too big.\n");
|
||||
return false;
|
||||
}
|
||||
memset(replace, value, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( size > MAX_REPLACE_SIZE )
|
||||
{
|
||||
|
||||
uint16_t remaining = size;
|
||||
uint32_t chunk_rva = rva;
|
||||
|
||||
do {
|
||||
/* patch in multiple chunks */
|
||||
LOG("multipart patch: rva %x, %d remaining\n", chunk_rva, remaining);
|
||||
IPS_READ(&replace, MAX_REPLACE_SIZE, "DATA");
|
||||
|
||||
if ( !skip )
|
||||
patch_memory((int64_t)data+chunk_rva, (char *)replace, MAX_REPLACE_SIZE);
|
||||
|
||||
remaining -= MAX_REPLACE_SIZE;
|
||||
chunk_rva += MAX_REPLACE_SIZE;
|
||||
|
||||
} while (remaining > MAX_REPLACE_SIZE);
|
||||
|
||||
size = remaining;
|
||||
rva = chunk_rva;
|
||||
|
||||
}
|
||||
|
||||
IPS_READ(&replace, size, "DATA");
|
||||
}
|
||||
|
||||
/* eclale woes */
|
||||
if ( trans_base == 0 && rva < 0x400 )
|
||||
{
|
||||
if (memcmp(replace, ".trans", 6) == 0)
|
||||
{
|
||||
trans_base = *(uint32_t*)(replace+0x0C);
|
||||
trans_base_offset = *(uint32_t*)(replace+0x14);
|
||||
//LOG("found .trans section at offset %x rva %x\n", trans_base_offset, trans_base);
|
||||
}
|
||||
}
|
||||
|
||||
if ( trans_base_offset != 0 && offset >= trans_base_offset )
|
||||
{
|
||||
/* patching into new section */
|
||||
if ( trans_rebase == 0 )
|
||||
{
|
||||
HANDLE hProc = GetCurrentProcess();
|
||||
LPVOID myAlloc = VirtualAllocEx(hProc, NULL, 16384, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||
if (myAlloc == NULL)
|
||||
{
|
||||
LOG("Failed to allocate memory in target process. Error: 0x%lX\n", GetLastError());
|
||||
exit(0);
|
||||
}
|
||||
trans_rebase = (uint32_t)myAlloc - (uint32_t)data;
|
||||
//LOG( "virtualalloc worked; address %x (%p)\n", trans_rebase, myAlloc);
|
||||
|
||||
/* seems useless */
|
||||
//memcpy(replace, &trans_rebase, 4);
|
||||
//patch_memory((int64_t)data+0x02d4, (char *)replace, 4);
|
||||
//LOG( "patched .trans section address to %02x %02x %02x %02x\n", replace[0], replace[1], replace[2], replace[3]);
|
||||
}
|
||||
rva = (offset - trans_base_offset) + trans_rebase;
|
||||
//LOG( "off %x - base %x + rebase %x = %x\n\n", offset, trans_base_offset, trans_rebase, rva);
|
||||
//LOG( "offset %x relocated to rva %x.", offset, rva);
|
||||
}
|
||||
|
||||
if ( !skip )
|
||||
{
|
||||
patch_memory((int64_t)data+rva, (char *)replace, size);
|
||||
}
|
||||
}
|
||||
|
||||
/* redo all relocation now the dll is patched */
|
||||
perform_reloc(data, reloc_delta, trans_base, trans_rebase-trans_base);
|
||||
|
||||
LOG("popnhax: translation: IPS patch applied.\n");
|
||||
|
||||
if (dump_dll)
|
||||
{
|
||||
LOG("popnhax: translation debug: dump dll after patch\n");
|
||||
FILE* dllrtp = fopen("dllruntime_patched.dll", "wb");
|
||||
fwrite(data, 1, dllSize, dllrtp);
|
||||
fclose(dllrtp);
|
||||
}
|
||||
|
||||
fclose(ips_fp);
|
||||
return true;
|
||||
#undef IPS_READ
|
||||
}
|
||||
|
||||
@ -331,16 +331,16 @@ static bool patch_translation_dict(const char *dllFilename, const char *folderna
|
||||
uint8_t word_count = 0;
|
||||
uint8_t orig_size = 0;
|
||||
|
||||
char dict_filepath[64];
|
||||
char dict_filepath[64];
|
||||
sprintf(dict_filepath, "%s%s%s", "data_mods\\", foldername, "\\popn22.dict");
|
||||
FILE *dict_fp = fopen(dict_filepath, "rb");
|
||||
|
||||
FILE *dict_fp = fopen(dict_filepath, "rb");
|
||||
|
||||
if (dict_fp == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG("popnhax: translation: popn22.dict file found\n");
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG("popnhax: translation: popn22.dict file found\n");
|
||||
|
||||
#define STATE_WAITING 0
|
||||
#define STATE_ORIGINAL 1
|
||||
@ -435,11 +435,11 @@ static bool patch_translation_dict(const char *dllFilename, const char *folderna
|
||||
|
||||
bool patch_translate(const char *dllFilename, const char *folder, bool debug)
|
||||
{
|
||||
bool ips_done = false;
|
||||
bool dict_done = false;
|
||||
bool ips_done = false;
|
||||
bool dict_done = false;
|
||||
|
||||
ips_done = patch_translation_ips(dllFilename, folder, debug);
|
||||
dict_done = patch_translation_dict(dllFilename, folder, debug);
|
||||
|
||||
return ips_done || dict_done;
|
||||
ips_done = patch_translation_ips(dllFilename, folder, debug);
|
||||
dict_done = patch_translation_dict(dllFilename, folder, debug);
|
||||
|
||||
return ips_done || dict_done;
|
||||
}
|
Loading…
Reference in New Issue
Block a user