Compare commits

...

10 Commits

15 changed files with 333 additions and 72 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*:Zone.Identifier

View File

@ -14,17 +14,15 @@ include popnhax/Module.mk
# #
zipdir := $(BUILDDIR)/zip zipdir := $(BUILDDIR)/zip
popnhax_version := $(shell grep "define PROGRAM_VERSION" popnhax/dllmain.cc | cut -d'"' -f2)
$(zipdir)/: $(BUILDDIR)/popnhax_v$(popnhax_version).zip: \
mkdir -p $@ build/bin/avs2_1508-32/popnhax.dll
@echo ... $@
@mkdir -p $(zipdir)
@cp -a -p build/bin/avs2_1508-32/popnhax.dll $(zipdir)
@cp -r -a -p dist/popnhax/* $(zipdir)
@cd $(zipdir) \
&& zip -r ../popnhax_v$(popnhax_version).zip ./*
$(BUILDDIR)/popnhax.zip: \ all: $(BUILDDIR)/popnhax_v$(popnhax_version).zip
build/bin/avs2_1508-32/popnhax.dll \
dist/popnhax/popnhax.xml \
dist/popnhax/D3d9.dll \
dist/popnhax/ifs_hook.dll \
| $(zipdir)/
echo ... $@
zip -j $@ $^
all: $(BUILDDIR)/popnhax.zip

View File

@ -2,10 +2,32 @@
# popnhax # popnhax
Arcade game patcher. Patcher for pop'n music arcade game.
Featuring pfree, instant retire, visual and audio offset adjust, 1000Hz input polling for true ms-based timing, unlimited favorites, auto hi-speed selection, iidx-like hard gauge and more..
Based on [bemanihax](https://github.com/windyfairy/bemanihax) whose an updated version was included with omnimix v1 Based on [bemanihax](https://github.com/windyfairy/bemanihax) whose an updated version was included with omnimix v1
### Features
Refer to [popnhax.xml](https://github.com/CrazyRedMachine/popnhax/blob/main/dist/popnhax/popnhax.xml) for complete list and explanations
More info could be found in [Release Notes](https://github.com/CrazyRedMachine/popnhax/releases) or in the [popnhax_tools](https://github.com/CrazyRedMachine/popnhax_tools) repository.
### Run Instructions
- Extract all files directly in the `contents` folder of your install.
(**Note**: if you're running your dlls from `modules` subfolder, please rather copy them back into `contents` folder).
- Edit `popnhax.xml` with a text editor and set your desired options.
- Add `popnhax.dll` as an inject dll to your gamestart command or option menu.
eg. modify your gamestart.bat to add `-k popnhax.dll` or `-K popnhax.dll` depending on the launcher you use.
Some launchers also feature an option menu (accessible by pressing F4 ingame), in which case you can locate the "Inject Hook" setting in option tab. Enter `popnhax.dll` there.
### Build Instructions ### Build Instructions
Should be working out of the box with MSYS2/MinGW32. Just run `make`. Using WSL is the recommended method. Just run `make`.

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -15,7 +15,7 @@
<!-- Other categories --> <!-- Other categories -->
<!-- Bring back score challenge in the game for servers supporting it (only for kaimei onwards) --> <!-- Bring back score challenge in the game for servers supporting it (only for kaimei onwards) -->
<score_challenge __type="bool">0</score_challenge> <score_challenge __type="bool">0</score_challenge>
<!-- Handle favorites through data_mods/<friendID>.fav files (Note: allows UNLIMITED favorites) --> <!-- Handle favorites through data_mods/<game>.<friendID>.fav files (Note: allows UNLIMITED favorites as well as favorites without an account/server) -->
<local_favorites __type="bool">0</local_favorites> <local_favorites __type="bool">0</local_favorites>
<!-- Classic patches --> <!-- Classic patches -->
@ -57,7 +57,7 @@
<hispeed_default_bpm __type="u16">0</hispeed_default_bpm> <hispeed_default_bpm __type="u16">0</hispeed_default_bpm>
<!-- IIDX-like hard gauge (start with full gauge, instant fail if gauge drops to 0) --> <!-- IIDX-like hard gauge (start with full gauge, instant fail if gauge drops to 0) -->
<!-- Gauge details: increment: +0.1% for each cool/great/good (like spicy gauge), decrement: -9% for each bad, or -4.5% if gauge <=30% ) --> <!-- Gauge details: increment: +0.1% for each cool/great/good (like spicy gauge), decrement: -9% for each bad, or -4.5% if gauge <=30% (like IIDX) -->
<iidx_hard_gauge __type="bool">0</iidx_hard_gauge> <iidx_hard_gauge __type="bool">0</iidx_hard_gauge>
<!-- Force full options by default (useful when no numpad is available) --> <!-- Force full options by default (useful when no numpad is available) -->
<force_full_opt __type="bool">0</force_full_opt> <force_full_opt __type="bool">0</force_full_opt>
@ -104,11 +104,11 @@
======================================================================================== --> ======================================================================================== -->
<!-- Datecode and Multiboot --> <!-- Datecode and Multiboot -->
<!-- Force a different datecode than the one found in ea3-config (yyyymmdd00) --> <!-- Force a different datecode than the one found in ea3-config (yyyymmdd00), or use "auto" to let music limit decide for you if patch_db is on -->
<force_datecode __type="str"></force_datecode> <force_datecode __type="str">auto</force_datecode>
<!-- Also apply force_datecode to network packets --> <!-- Also apply force_datecode to network packets -->
<network_datecode __type="bool">1</network_datecode> <network_datecode __type="bool">1</network_datecode>
<!-- Disable multiboot auto conf tuning (which takes place when using popn22_yyyymmddrr.dll format and an xml without force_datecode option) --> <!-- Disable multiboot auto conf tuning (which takes place when using popn22_yyyymmddrr.dll format and an xml without another datecode in force_datecode) -->
<disable_multiboot __type="bool">0</disable_multiboot> <disable_multiboot __type="bool">0</disable_multiboot>
<!-- Timing and lanes --> <!-- Timing and lanes -->
@ -132,13 +132,13 @@
<enhanced_polling_priority __type="s8">1</enhanced_polling_priority> <enhanced_polling_priority __type="s8">1</enhanced_polling_priority>
<!-- Song db patches --> <!-- Song db patches -->
<!-- Auto select patch file from data_mods folder (will detect datecode from ea3-config or force_datecode option) --> <!-- Auto select patch file from data_mods folder based on music limit, or datecode otherwise (will detect datecode from ea3-config or force_datecode option) -->
<patch_xml_auto __type="bool">1</patch_xml_auto> <patch_xml_auto __type="bool">1</patch_xml_auto>
<!-- Manually set XML file containing patches (requires patch_xml_auto to be disabled) --> <!-- Manually set XML file containing patches (requires patch_xml_auto to be disabled) -->
<patch_xml_filename __type="str"></patch_xml_filename> <patch_xml_filename __type="str"></patch_xml_filename>
<!-- Force the newly created buffers to be the same size as the original buffers --> <!-- Force the newly created buffers to be the same size as the original buffers (not recommended) -->
<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) (not recommended) -->
<disable_redirection __type="bool">0</disable_redirection> <disable_redirection __type="bool">0</disable_redirection>
<!-- Do not perform music limit checks for patch_xml_auto (not recommended) --> <!-- Do not perform music limit checks for patch_xml_auto (not recommended) -->
<ignore_music_limit __type="bool">0</ignore_music_limit> <ignore_music_limit __type="bool">0</ignore_music_limit>
@ -146,14 +146,16 @@
<!-- Custom category options --> <!-- Custom category options -->
<!-- minimum songid for a song to be seen as "custom" --> <!-- minimum songid for a song to be seen as "custom" -->
<custom_categ_min_songid __type="u16">4000</custom_categ_min_songid> <custom_categ_min_songid __type="u16">4000</custom_categ_min_songid>
<!-- maximum songid for a song to be seen as "custom" (0 = no limit) --> <!-- maximum songid for a song to be seen as "custom" (0 = no limit, not recommended since "random" category will use fake song ids in the 65400+ range) -->
<custom_categ_max_songid __type="u16">0</custom_categ_max_songid> <custom_categ_max_songid __type="u16">65400</custom_categ_max_songid>
<!-- Category title for customs --> <!-- Category title for customs -->
<custom_category_title __type="str">Customs</custom_category_title> <custom_category_title __type="str">Customs</custom_category_title>
<!-- Format used for category title (see --> <!-- Format used for category title (in BM2DXFontScript format, refer to BM2DXFontScript.md in popnhax_tools repo) -->
<custom_category_format __type="str">[ol:4][olc:d92f0d]%s</custom_category_format> <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] ) --> <!-- Format used for custom song titles on song select (Note: colors not working for kaimei and above, but rotation does, e.g. "* [rz:3]%s[/rz]" ) -->
<custom_track_title_format __type="str"></custom_track_title_format> <custom_track_title_format __type="str"></custom_track_title_format>
<!-- Optional secondary format used for older games only (Full colors supported, e.g. "[ol:4][olc:d92f0d]%s") -->
<custom_track_title_format2 __type="str"></custom_track_title_format2>
<!-- Translation --> <!-- Translation -->
<!-- Disable .dict string replacements and .ips patches --> <!-- Disable .dict string replacements and .ips patches -->

View File

@ -63,6 +63,7 @@ struct popnhax_config {
char custom_category_title[16]; char custom_category_title[16];
char custom_category_format[64]; char custom_category_format[64];
char custom_track_title_format[64]; char custom_track_title_format[64];
char custom_track_title_format2[64];
}; };
#endif #endif

View File

@ -55,6 +55,11 @@ uint32_t favorites_count = 0;
songlist_t favorites_struct; songlist_t favorites_struct;
uint32_t favorites_struct_addr = (uint32_t)&favorites_struct; uint32_t favorites_struct_addr = (uint32_t)&favorites_struct;
bool is_a_custom(uint32_t songid)
{
return (songid >= g_min_id && (g_max_id==0 || g_max_id >= songid));
}
void add_song_to_favorites() void add_song_to_favorites()
{ {
favorites = (uint32_t *) realloc(favorites, sizeof(uint32_t)*(favorites_count+5)); favorites = (uint32_t *) realloc(favorites, sizeof(uint32_t)*(favorites_count+5));
@ -250,7 +255,7 @@ 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] & 0x0000FFFF) == songid )
return true; return true;
} }
return false; return false;
@ -258,8 +263,7 @@ static bool subcateg_has_songid(uint32_t songid, subcategory_s* subcateg)
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 >= g_min_id if ( is_a_custom(songid)
&& (g_max_id == 0 || songid <= g_max_id)
&& !subcateg_has_songid(songid, subcateg) ) && !subcateg_has_songid(songid, subcateg) )
{ {
subcateg->songlist = (uint32_t *) realloc(subcateg->songlist, sizeof(uint32_t)*(++subcateg->size)); subcateg->songlist = (uint32_t *) realloc(subcateg->songlist, sizeof(uint32_t)*(++subcateg->size));
@ -267,12 +271,15 @@ static void add_song_to_subcateg(uint32_t songid, subcategory_s* subcateg)
subcateg->songlist[subcateg->size-1] |= 0x00060000; // game wants this otherwise only easy difficulty will appear subcateg->songlist[subcateg->size-1] |= 0x00060000; // game wants this otherwise only easy difficulty will appear
// Also add it to the "ALL SONGS" subcategory // Also add it to the "ALL SONGS" subcategory
if ( !subcateg_has_songid(songid, &subcategories[0]) )
{
subcategories[0].songlist = (uint32_t *) realloc(subcategories[0].songlist, sizeof(uint32_t)*(++subcategories[0].size)); subcategories[0].songlist = (uint32_t *) realloc(subcategories[0].songlist, sizeof(uint32_t)*(++subcategories[0].size));
subcategories[0].songlist[subcategories[0].size-1] = subcateg->songlist[subcateg->size-1]; subcategories[0].songlist[subcategories[0].size-1] = subcateg->songlist[subcateg->size-1];
} }
}
} }
static subcategory_s* get_subcateg(char *title) static subcategory_s* get_subcateg(const char *title)
{ {
for (uint32_t i = 0; i < g_subcateg_count; i++) for (uint32_t i = 0; i < g_subcateg_count; i++)
{ {
@ -502,12 +509,15 @@ void hook_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, _g_min_id\n"); __asm("push edx\n");
__asm("jb categ_skip_add\n"); __asm("push ecx\n");
__asm("cmp dword ptr _g_max_id, 0\n"); __asm("push eax\n");
__asm("je add_my_song\n"); __asm("call %P0" : : "i"(is_a_custom));
__asm("cmp eax, _g_max_id\n"); __asm("test eax, eax\n");
__asm("ja categ_skip_add\n"); __asm("pop eax\n");
__asm("pop ecx\n");
__asm("pop edx\n");
__asm("jz categ_skip_add\n");
__asm("add_my_song:\n"); __asm("add_my_song:\n");
__asm("push eax\n"); __asm("push eax\n");
@ -567,12 +577,16 @@ void hook_song_printf()
__asm("push eax\n"); __asm("push eax\n");
__asm("push ebx\n"); __asm("push ebx\n");
__asm("mov eax, [esp+0x50]\n"); __asm("mov eax, [esp+0x50]\n");
__asm("cmp eax, _g_min_id\n");
__asm("jb print_regular_song\n"); __asm("push ecx\n");
__asm("cmp dword ptr _g_max_id, 0\n"); __asm("push edx\n");
__asm("je print_custom_song\n"); __asm("push eax\n");
__asm("cmp eax, _g_max_id\n"); __asm("call %P0" : : "i"(is_a_custom));
__asm("ja print_regular_song\n"); __asm("test eax, eax\n");
__asm("pop eax\n");
__asm("pop edx\n");
__asm("pop ecx\n");
__asm("jz print_regular_song\n");
__asm("print_custom_song:\n"); __asm("print_custom_song:\n");
@ -592,12 +606,16 @@ void hook_artist_printf()
__asm("push eax\n"); __asm("push eax\n");
__asm("push ebx\n"); __asm("push ebx\n");
__asm("mov eax, [esp+0x50]\n"); __asm("mov eax, [esp+0x50]\n");
__asm("cmp eax, _g_min_id\n");
__asm("jb print_regular_artist\n"); __asm("push ecx\n");
__asm("cmp dword ptr _g_max_id, 0\n"); __asm("push edx\n");
__asm("je print_custom_artist\n"); __asm("push eax\n");
__asm("cmp eax, _g_max_id\n"); __asm("call %P0" : : "i"(is_a_custom));
__asm("ja print_regular_artist\n"); __asm("test eax, eax\n");
__asm("pop eax\n");
__asm("pop edx\n");
__asm("pop ecx\n");
__asm("jz print_regular_artist\n");
__asm("print_custom_artist:\n"); __asm("print_custom_artist:\n");
@ -695,7 +713,7 @@ static bool patch_favorite_categ(const char *game_dll_fn) {
//hook result screen to replace 3 functions //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); int64_t first_loc = search(data, dllSize, "\xBF\x07\x00\x00\x00\xC6\x85", 7, 0);
if (first_loc == -1) { if (first_loc == -1) {
LOG("popnhax: local_favorites: cannot find result screen function\n"); LOG("popnhax: local_favorites: cannot find result screen function\n");
return false; return false;
@ -905,6 +923,12 @@ static char *get_folder_name(const char* path) {
return categ_name; return categ_name;
} }
bool is_excluded_folder(const char *input_filename)
{
return (input_filename[strlen("data_mods/")] == '_');
}
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);
@ -930,6 +954,7 @@ static void parse_musicdb(const char *input_filename) {
add_song_to_subcateg(songid, subcateg); add_song_to_subcateg(songid, subcateg);
} }
} }
free(title);
} }
static void load_databases() { static void load_databases() {
@ -945,8 +970,10 @@ static void load_databases() {
for(uint16_t i=0;i<result.size();i++) for(uint16_t i=0;i<result.size();i++)
{ {
if ( strstr(result[i].c_str(), "musicdb") == NULL ) if ( (strstr(result[i].c_str(), "musicdb") == NULL)
|| is_excluded_folder(result[i].c_str()) )
continue; continue;
parse_musicdb(result[i].c_str()); parse_musicdb(result[i].c_str());
} }
} }
@ -965,12 +992,18 @@ void hook_after_getlevel()
{ {
__asm("push ebx\n"); __asm("push ebx\n");
__asm("mov ebx, dword ptr [esp+0x04]\n"); __asm("mov ebx, dword ptr [esp+0x04]\n");
__asm("cmp ebx, _g_min_id\n");
__asm("jb real_level\n"); __asm("push eax\n");
__asm("cmp dword ptr _g_max_id, 0\n"); __asm("push ecx\n");
__asm("je force_level_0\n"); __asm("push edx\n");
__asm("cmp ebx, _g_max_id\n"); __asm("push ebx\n");
__asm("ja real_level\n"); __asm("call %P0" : : "i"(is_a_custom));
__asm("test eax, eax\n");
__asm("pop ebx\n");
__asm("pop edx\n");
__asm("pop ecx\n");
__asm("pop eax\n");
__asm("jz real_level\n");
__asm("force_level_0:\n"); __asm("force_level_0:\n");
__asm("mov eax, 0x00\n"); __asm("mov eax, 0x00\n");
@ -1006,7 +1039,7 @@ bool patch_exclude(const char *game_dll_fn)
bool patch_custom_categs(const char *dllFilename, struct popnhax_config *config) bool patch_custom_categs(const char *dllFilename, struct popnhax_config *config)
{ {
g_min_id = config->custom_categ_min_songid; g_min_id = config->custom_categ_min_songid;
g_max_id = config->custom_categ_max_songid; //g_max_id = config->custom_categ_max_songid; //handled during injection already
uint8_t mode = config->custom_categ; uint8_t mode = config->custom_categ;
char icon_path[64]; char icon_path[64];
@ -1046,11 +1079,13 @@ bool patch_custom_categs(const char *dllFilename, struct popnhax_config *config)
print_databases(); print_databases();
} }
if ( config->custom_track_title_format[0] != '\0' ) if ( config->game_version < 26 && config->custom_track_title_format2[0] != '\0' )
{ g_customformat = config->custom_track_title_format2;
else if ( config->custom_track_title_format[0] != '\0' )
g_customformat = config->custom_track_title_format; g_customformat = config->custom_track_title_format;
if ( g_customformat != NULL )
patch_custom_track_format(dllFilename); patch_custom_track_format(dllFilename);
}
if (config->custom_exclude_from_version) if (config->custom_exclude_from_version)
LOG("popnhax: Customs excluded from version folders\n"); //musichax_core_init took care of it LOG("popnhax: Customs excluded from version folders\n"); //musichax_core_init took care of it

View File

@ -4,6 +4,9 @@
#include <stdint.h> #include <stdint.h>
#include "popnhax/config.h" #include "popnhax/config.h"
extern uint32_t g_max_id;
bool is_excluded_folder(const char *input_filename);
bool patch_custom_categs(const char *dllFilename, struct popnhax_config *config); bool patch_custom_categs(const char *dllFilename, struct popnhax_config *config);
bool patch_local_favorites(const char *dllFilename, uint8_t version); bool patch_local_favorites(const char *dllFilename, uint8_t version);

View File

@ -32,7 +32,7 @@
#include "SearchFile.h" #include "SearchFile.h"
#define PROGRAM_VERSION "1.11.beta2" #define PROGRAM_VERSION "1.12.dev"
const char *g_game_dll_fn = NULL; const char *g_game_dll_fn = NULL;
const char *g_config_fn = NULL; const char *g_config_fn = NULL;
@ -201,6 +201,8 @@ PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_STR, struct popnhax_config, custom_category
"/popnhax/custom_category_format") "/popnhax/custom_category_format")
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_STR, struct popnhax_config, custom_track_title_format, PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_STR, struct popnhax_config, custom_track_title_format,
"/popnhax/custom_track_title_format") "/popnhax/custom_track_title_format")
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_STR, struct popnhax_config, custom_track_title_format2,
"/popnhax/custom_track_title_format2")
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, local_favorites, PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, local_favorites,
"/popnhax/local_favorites") "/popnhax/local_favorites")
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, ignore_music_limit, PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, ignore_music_limit,
@ -1411,10 +1413,57 @@ static bool patch_purelong()
return true; return true;
} }
static bool get_music_limit(uint32_t* limit) {
void (*real_normal0)();
void hook_normal0()
{
// getChartDifficulty returns 0xFFFFFFFF when there's no chart,
// but the game assumes there's always a normal chart so there's no check in this case
// and it returns 0... let's fix this
__asm("cmp ebx, 1\n"); //chart id
__asm("jne process_normal0\n");
__asm("cmp eax, 0\n"); //difficulty
__asm("jne process_normal0\n");
__asm("or eax, 0xFFFFFFFF\n");
__asm("process_normal0:\n");
real_normal0();
}
static bool patch_normal0()
{
DWORD dllSize = 0; DWORD dllSize = 0;
char *data = getDllData(g_game_dll_fn, &dllSize); char *data = getDllData(g_game_dll_fn, &dllSize);
{
int64_t pattern_offset = search(data, dllSize, "\x83\xC4\x08\x8B\xF8\x89\x7C\x24\x3C", 9, 0);
if (pattern_offset == -1) {
LOG("popnhax: Couldn't find song list display function\n");
return false;
}
uint64_t patch_addr = (int64_t)data + pattern_offset;
MH_CreateHook((LPVOID)patch_addr, (LPVOID)hook_normal0,
(void **)&real_normal0);
}
return true;
}
static bool get_music_limit(uint32_t* limit) {
// avoid doing the search multiple times
static uint32_t music_limit = 0;
if ( music_limit != 0 )
{
*limit = music_limit;
return true;
}
DWORD dllSize = 0;
char *data = getDllData(g_game_dll_fn, &dllSize);
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);
{ {
int64_t string_loc = search(data, dllSize, "Illegal music no %d", 19, 0); int64_t string_loc = search(data, dllSize, "Illegal music no %d", 19, 0);
if (string_loc == -1) { if (string_loc == -1) {
@ -1422,16 +1471,18 @@ static bool get_music_limit(uint32_t* limit) {
return false; return false;
} }
string_loc += reloc_delta; //reloc delta just in case
string_loc += 0x10000000; //entrypoint string_loc += 0x10000000; //entrypoint
char *as_hex = (char *) &string_loc; char *as_hex = (char *) &string_loc;
int64_t pattern_offset = search(data, dllSize, as_hex, 4, 0); int64_t pattern_offset = search(data, dllSize, as_hex, 4, 0);
if (pattern_offset == -1) { if (pattern_offset == -1) {
LOG("popnhax: patch_db: could not retrieve music limit test function\n"); LOG("popnhax: could not retrieve music limit test function\n");
return false; return false;
} }
uint64_t patch_addr = (int64_t)data + pattern_offset - 0x1F; uint64_t patch_addr = (int64_t)data + pattern_offset - 0x1F;
*limit = *(uint32_t*)patch_addr; *limit = *(uint32_t*)patch_addr;
music_limit = *limit;
} }
return true; return true;
} }
@ -1664,6 +1715,8 @@ static bool patch_database() {
patch_purelong(); patch_purelong();
patch_normal0();
{ {
int64_t pattern_offset = search(data, dllSize, "\x8D\x44\x24\x10\x88\x4C\x24\x10\x88\x5C\x24\x11\x8D\x50\x01", 15, 0); int64_t pattern_offset = search(data, dllSize, "\x8D\x44\x24\x10\x88\x4C\x24\x10\x88\x5C\x24\x11\x8D\x50\x01", 15, 0);
if (pattern_offset != -1) { if (pattern_offset != -1) {
@ -3147,7 +3200,7 @@ static unsigned int __stdcall enhanced_polling_stats_proc(void *ctx)
if (config.enhanced_polling_priority) if (config.enhanced_polling_priority)
{ {
SetThreadPriority(GetCurrentThread(), config.enhanced_polling_priority); SetThreadPriority(GetCurrentThread(), config.enhanced_polling_priority);
fprintf(stderr, "[Enhanced polling] Thread priority set to %d\n", GetThreadPriority(GetCurrentThread())); LOG("[Enhanced polling] Thread priority set to %d\n", GetThreadPriority(GetCurrentThread()));
} }
uint32_t count = 0; uint32_t count = 0;
@ -3236,7 +3289,7 @@ static unsigned int __stdcall enhanced_polling_proc(void *ctx)
if (config.enhanced_polling_priority) if (config.enhanced_polling_priority)
{ {
SetThreadPriority(GetCurrentThread(), config.enhanced_polling_priority); SetThreadPriority(GetCurrentThread(), config.enhanced_polling_priority);
fprintf(stderr, "[Enhanced polling] Thread priority set to %d\n", GetThreadPriority(GetCurrentThread())); LOG("[Enhanced polling] Thread priority set to %d\n", GetThreadPriority(GetCurrentThread()));
} }
uint32_t curr_poll_time = 0; uint32_t curr_poll_time = 0;
@ -5430,6 +5483,116 @@ uint8_t get_version()
} }
} }
static bool get_music_limit_from_file(const char *filepath, uint32_t *limit){
HANDLE hFile;
HANDLE hMap;
LPVOID lpBasePtr;
LARGE_INTEGER liFileSize;
hFile = CreateFile(filepath,
GENERIC_READ, // dwDesiredAccess
0, // dwShareMode
NULL, // lpSecurityAttributes
OPEN_EXISTING, // dwCreationDisposition
FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes
0); // hTemplateFile
if (hFile == INVALID_HANDLE_VALUE) {
//file not existing is actually a good thing
return false;
}
if (!GetFileSizeEx(hFile, &liFileSize)) {
LOG("popnhax: auto_diag: GetFileSize failed with error %ld\n", GetLastError());
CloseHandle(hFile);
return false;
}
if (liFileSize.QuadPart == 0) {
LOG("popnhax: auto_diag: popn22.dll file is empty?!\n");
CloseHandle(hFile);
return false;
}
hMap = CreateFileMapping(
hFile,
NULL, // Mapping attributes
PAGE_READONLY, // Protection flags
0, // MaximumSizeHigh
0, // MaximumSizeLow
NULL); // Name
if (hMap == 0) {
LOG("popnhax: auto_diag: CreateFileMapping failed with error %ld\n", GetLastError());
CloseHandle(hFile);
return false;
}
lpBasePtr = MapViewOfFile(
hMap,
FILE_MAP_READ, // dwDesiredAccess
0, // dwFileOffsetHigh
0, // dwFileOffsetLow
0); // dwNumberOfBytesToMap
if (lpBasePtr == NULL) {
LOG("popnhax: auto_diag: MapViewOfFile failed with error %ld\n", GetLastError());
CloseHandle(hMap);
CloseHandle(hFile);
return false;
}
char *data = (char *)lpBasePtr;
int32_t delta = 0;
//first retrieve .rdata virtual and raw addresses to compute delta
{
int64_t string_loc = search(data, liFileSize.QuadPart, ".rdata", 6, 0);
if (string_loc == -1) {
LOG("popnhax: auto_diag: could not retrieve .rdata section header\n");
UnmapViewOfFile(lpBasePtr);
CloseHandle(hMap);
CloseHandle(hFile);
return false;
}
uint32_t virtual_address = *(uint32_t *)(data + string_loc + 0x0C);
uint32_t raw_address = *(uint32_t *)(data + string_loc + 0x14);
delta = virtual_address - raw_address;
}
//now attempt to find music limit from the dll
{
int64_t string_loc = search(data, liFileSize.QuadPart, "Illegal music no %d", 19, 0);
if (string_loc == -1) {
LOG("popnhax: auto_diag: could not retrieve music limit error string\n");
UnmapViewOfFile(lpBasePtr);
CloseHandle(hMap);
CloseHandle(hFile);
return false;
}
string_loc += delta; //convert to virtual address
string_loc += 0x10000000; //entrypoint
char *as_hex = (char *) &string_loc;
int64_t pattern_offset = search(data, liFileSize.QuadPart, as_hex, 4, 0);
if (pattern_offset == -1) {
LOG("popnhax: auto_diag: could not retrieve music limit test function\n");
UnmapViewOfFile(lpBasePtr);
CloseHandle(hMap);
CloseHandle(hFile);
return false;
}
uint64_t patch_addr = (int64_t)data + pattern_offset - 0x1F;
*limit = *(uint32_t*)patch_addr;
}
UnmapViewOfFile(lpBasePtr);
CloseHandle(hMap);
CloseHandle(hFile);
return true;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) { switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH: { case DLL_PROCESS_ATTACH: {
@ -5489,6 +5652,30 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
if (g_game_dll_fn == NULL) if (g_game_dll_fn == NULL)
g_game_dll_fn = strdup("popn22.dll"); g_game_dll_fn = strdup("popn22.dll");
if ( strcmp(g_game_dll_fn, "popn22.dll") == 0 )
{
//ensure you're not running popn22.dll from the modules subfolder
char filename[MAX_PATH];
if ( GetModuleFileName(GetModuleHandle(g_game_dll_fn), filename, MAX_PATH+1) != 0 )
{
if ( strstr(filename, "\\modules\\popn22.dll") != NULL )
{
LOG("WARNING: running popn22.dll from \"modules\" subfolder is not recommended with popnhax. Please copy dlls back to contents folder\n");
}
} else {
LOG("WARNING: auto_diag: Cannot retrieve module path (%ld)\n", GetLastError());
}
//ensure there isn't a more recent version in modules subfolder
uint32_t modules_limit, current_limit;
if ( get_music_limit(&current_limit)
&& get_music_limit_from_file("modules\\popn22.dll", &modules_limit)
&& (modules_limit > current_limit) )
{
LOG("ERROR: a newer version of popn22.dll seems to be present in modules subfolder (%d vs %d songs). Please copy dlls back to contents folder\n", modules_limit, current_limit);
}
}
if (g_config_fn == NULL) if (g_config_fn == NULL)
{ {
/* if there's an xml named like the custom game dll, it takes priority */ /* if there's an xml named like the custom game dll, it takes priority */
@ -5573,10 +5760,10 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
char translation_path[64] = ""; char translation_path[64] = "";
/* parse */ /* parse */
if (config.force_datecode[0] != '\0') if ( g_datecode_override != NULL )
{ {
sprintf(translation_folder, "_%s%s", config.force_datecode, "_tr"); sprintf(translation_folder, "_%s%s", g_datecode_override, "_tr");
sprintf(translation_path, "%s%s", "data_mods\\", translation_folder); sprintf(translation_path, "%s%s", "data_mods\\", translation_folder);
if (access(translation_path, F_OK) != 0) if (access(translation_path, F_OK) != 0)
{ {

View File

@ -7,6 +7,7 @@
#include "util/patch.h" #include "util/patch.h"
#include "util/log.h" #include "util/log.h"
#include "xmlhelper.h" #include "xmlhelper.h"
#include "custom_categs.h"
#include "tableinfo.h" #include "tableinfo.h"
#include "loader.h" #include "loader.h"
@ -855,6 +856,10 @@ void parse_musicdb(const char *input_filename, const char *target, struct popnha
sizeof(idxStr)); sizeof(idxStr));
uint32_t idx = atoi(idxStr); uint32_t idx = atoi(idxStr);
if (idx > g_max_id)
{
g_max_id = idx;
}
// Get an existing music entry in memory // Get an existing music entry in memory
// If it exists, return the existing entry // If it exists, return the existing entry
// If it doesn't exist, create a new entry in memory // If it doesn't exist, create a new entry in memory
@ -917,10 +922,11 @@ void parse_musicdb(const char *input_filename, const char *target, struct popnha
} }
//force loading background for unilab //force loading background for unilab
m->mask |= 0x100; //m->mask |= 0x100;
if ( config->custom_categ if ( config->custom_categ
&& config->custom_exclude_from_version && config->custom_exclude_from_version
&& !is_excluded_folder(input_filename)
&& idx >= config->custom_categ_min_songid && idx >= config->custom_categ_min_songid
&& (config->custom_categ_max_songid == 0 || idx <= config->custom_categ_max_songid) ) && (config->custom_categ_max_songid == 0 || idx <= config->custom_categ_max_songid) )
{ {

View File

@ -73,3 +73,8 @@ int search(char *haystack, size_t haystack_size, const char *needle, size_t need
int res = _search((unsigned char*) haystack, haystack_size, (const unsigned char *)needle, needle_size, orig_offset, 0); int res = _search((unsigned char*) haystack, haystack_size, (const unsigned char *)needle, needle_size, orig_offset, 0);
return res; return res;
} }
int search_debug(char *haystack, size_t haystack_size, const char *needle, size_t needle_size, size_t orig_offset) {
int res = _search((unsigned char*) haystack, haystack_size, (const unsigned char *)needle, needle_size, orig_offset, 2);
return res;
}

View File

@ -2,5 +2,6 @@
#define __SEARCH_H__ #define __SEARCH_H__
int search(char *haystack, size_t haystack_size, const char *needle, size_t needle_size, size_t orig_offset); int search(char *haystack, size_t haystack_size, const char *needle, size_t needle_size, size_t orig_offset);
int search_debug(char *haystack, size_t haystack_size, const char *needle, size_t needle_size, size_t orig_offset);
#endif #endif