forked from Popn_Tools/popnhax
Compare commits
10 Commits
cd4a17329c
...
13cba126f2
Author | SHA1 | Date | |
---|---|---|---|
13cba126f2 | |||
6d3ade4c76 | |||
ef648b0883 | |||
f7bfc0f9d8 | |||
deddedd78e | |||
8806af339f | |||
193c9efe88 | |||
77b06c074e | |||
834ec6e9d1 | |||
89e5c8a562 |
28
dist/popnhax/popnhax.xml
vendored
28
dist/popnhax/popnhax.xml
vendored
@ -18,6 +18,10 @@
|
||||
<!-- 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>
|
||||
|
||||
<!-- Framerate -->
|
||||
<!-- Fix animation speed at higher framerate for a smoother experience (lower visual latency) -->
|
||||
<high_framerate __type="bool">0</high_framerate>
|
||||
|
||||
<!-- Classic patches -->
|
||||
<!-- Prevent crash on boot when using a different default audio source (aka HDMI audio patch) -->
|
||||
<audio_source_fix __type="bool">0</audio_source_fix>
|
||||
@ -120,8 +124,6 @@
|
||||
<keysound_offset __type="s8">0</keysound_offset>
|
||||
<!-- Adjust pop-kun and beam brightness (won't affect long popkuns) -->
|
||||
<beam_brightness __type="s8">0</beam_brightness>
|
||||
<!-- Disable the builtin frame limiter (faster/smoother animations at 120fps+) -->
|
||||
<fps_uncap __type="bool">0</fps_uncap>
|
||||
<!-- 1000Hz polling thread priority (for enhanced_polling only, might cause crashes on some systems if set too high)
|
||||
values THREAD_PRIORITY_LOWEST -2
|
||||
THREAD_PRIORITY_BELOW_NORMAL -1
|
||||
@ -131,7 +133,15 @@
|
||||
THREAD_PRIORITY_TIME_CRITICAL 15 -->
|
||||
<enhanced_polling_priority __type="s8">1</enhanced_polling_priority>
|
||||
|
||||
<!-- Song db patches -->
|
||||
<!-- Framerate -->
|
||||
<!-- Reference fps value for high_framerate animation speed (0: use monitor refreshrate and disable frame limiter) -->
|
||||
<high_framerate_fps __type="u16">0</high_framerate_fps>
|
||||
<!-- Tune the builtin frame limiter the closest to high_framerate_fps -->
|
||||
<high_framerate_limiter __type="bool">1</high_framerate_limiter>
|
||||
<!-- Disable the builtin frame limiter -->
|
||||
<fps_uncap __type="bool">0</fps_uncap>
|
||||
|
||||
<!-- Song db patches (requires patch_db) -->
|
||||
<!-- 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>
|
||||
<!-- Manually set XML file containing patches (requires patch_xml_auto to be disabled) -->
|
||||
@ -143,11 +153,13 @@
|
||||
<!-- Do not perform music limit checks for patch_xml_auto (not recommended) -->
|
||||
<ignore_music_limit __type="bool">0</ignore_music_limit>
|
||||
|
||||
<!-- 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, not recommended since "random" category will use fake song ids in the 65400+ range) -->
|
||||
<custom_categ_max_songid __type="u16">65400</custom_categ_max_songid>
|
||||
<!-- Custom category options (requires patch_db) -->
|
||||
<!-- Also exclude omnimix (song id < 3000) tracks from version/level (requires custom_exclude_from_level or custom_exclude_from_version) -->
|
||||
<exclude_omni __type="bool">0</exclude_omni>
|
||||
<!-- Any new chart added to an existing song moves the song to the customs folder -->
|
||||
<partial_entries __type="bool">0</partial_entries>
|
||||
<!-- Minimum songid for a song to be seen as "custom" (e.g. use 4000 for real customs only) -->
|
||||
<custom_categ_min_songid __type="u16">0</custom_categ_min_songid>
|
||||
<!-- Category title for customs -->
|
||||
<custom_category_title __type="str">Customs</custom_category_title>
|
||||
<!-- Format used for category title (in BM2DXFontScript format, refer to BM2DXFontScript.md in popnhax_tools repo) -->
|
||||
|
@ -17,7 +17,6 @@ struct popnhax_config {
|
||||
bool score_challenge;
|
||||
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;
|
||||
@ -64,6 +63,11 @@ struct popnhax_config {
|
||||
char custom_category_format[64];
|
||||
char custom_track_title_format[64];
|
||||
char custom_track_title_format2[64];
|
||||
bool exclude_omni;
|
||||
bool partial_entries;
|
||||
bool high_framerate;
|
||||
bool high_framerate_limiter;
|
||||
uint16_t high_framerate_fps;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -19,14 +19,10 @@
|
||||
#include "minhook/hde32.h"
|
||||
#include "minhook/include/MinHook.h"
|
||||
|
||||
#include "custom_categs.h"
|
||||
|
||||
#define F_OK 0
|
||||
|
||||
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];
|
||||
@ -34,9 +30,17 @@ typedef struct songlist_s {
|
||||
uint32_t array_end;
|
||||
} songlist_t;
|
||||
|
||||
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;
|
||||
|
||||
bst_t *g_customs_bst = NULL;
|
||||
bool g_exclude_omni = false;
|
||||
|
||||
void (*add_song_in_list)();
|
||||
|
||||
bool g_subcategmode = false;
|
||||
uint32_t g_min_id = 4000;
|
||||
uint32_t g_max_id = 0;
|
||||
|
||||
const char *g_categicon;
|
||||
const char *g_categformat;
|
||||
@ -55,6 +59,16 @@ uint32_t favorites_count = 0;
|
||||
songlist_t favorites_struct;
|
||||
uint32_t favorites_struct_addr = (uint32_t)&favorites_struct;
|
||||
|
||||
bool is_a_custom(uint32_t songid)
|
||||
{
|
||||
return (bst_search(g_customs_bst, songid) != NULL);
|
||||
}
|
||||
|
||||
bool is_excluded_from_level(uint32_t songid)
|
||||
{
|
||||
return ( is_a_custom(songid) && ( g_exclude_omni || songid >= 3000 ) );
|
||||
}
|
||||
|
||||
void add_song_to_favorites()
|
||||
{
|
||||
favorites = (uint32_t *) realloc(favorites, sizeof(uint32_t)*(favorites_count+5));
|
||||
@ -233,12 +247,6 @@ uint32_t songlist_count = 0;
|
||||
songlist_t songlist_struct;
|
||||
uint32_t songlist_struct_addr = (uint32_t)&songlist_struct;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
uint32_t size;
|
||||
uint32_t *songlist;
|
||||
} subcategory_s;
|
||||
|
||||
subcategory_s* subcategories;
|
||||
uint32_t g_subcateg_count = 0;
|
||||
//subcategories[0] is the "ALL SONGS" virtual subcategory,
|
||||
@ -256,10 +264,9 @@ static bool subcateg_has_songid(uint32_t songid, subcategory_s* subcateg)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void add_song_to_subcateg(uint32_t songid, subcategory_s* subcateg)
|
||||
void add_song_to_subcateg(uint32_t songid, subcategory_s* subcateg)
|
||||
{
|
||||
if ( songid >= g_min_id
|
||||
&& (g_max_id == 0 || songid <= g_max_id)
|
||||
if ( is_a_custom(songid)
|
||||
&& !subcateg_has_songid(songid, subcateg) )
|
||||
{
|
||||
subcateg->songlist = (uint32_t *) realloc(subcateg->songlist, sizeof(uint32_t)*(++subcateg->size));
|
||||
@ -275,7 +282,7 @@ static void add_song_to_subcateg(uint32_t songid, subcategory_s* subcateg)
|
||||
}
|
||||
}
|
||||
|
||||
static subcategory_s* get_subcateg(char *title)
|
||||
subcategory_s* get_subcateg(const char *title)
|
||||
{
|
||||
for (uint32_t i = 0; i < g_subcateg_count; i++)
|
||||
{
|
||||
@ -283,7 +290,15 @@ static subcategory_s* get_subcateg(char *title)
|
||||
if (strcmp(title, subcategories[i+1].name) == 0)
|
||||
return &(subcategories[i+1]);
|
||||
}
|
||||
return NULL;
|
||||
|
||||
//not found, allocate a new one
|
||||
subcategories = (subcategory_s*)realloc(subcategories, sizeof(subcategory_s)*((++g_subcateg_count)+1));
|
||||
subcategory_s *subcateg = &(subcategories[g_subcateg_count]);
|
||||
subcateg->name = strdup(title);
|
||||
subcateg->songlist = NULL;
|
||||
subcateg->size = 0;
|
||||
|
||||
return subcateg;
|
||||
}
|
||||
|
||||
void (*categ_inject_songlist)();
|
||||
@ -502,15 +517,19 @@ void hook_categ_reinit_songlist()
|
||||
real_categ_reinit_songlist();
|
||||
}
|
||||
|
||||
// in simple category mode, the game itself builds the full list by checking songids on the fly
|
||||
void (*real_categ_build_songlist)();
|
||||
void hook_categ_build_songlist()
|
||||
{
|
||||
__asm("cmp eax, _g_min_id\n");
|
||||
__asm("jb categ_skip_add\n");
|
||||
__asm("cmp dword ptr _g_max_id, 0\n");
|
||||
__asm("je add_my_song\n");
|
||||
__asm("cmp eax, _g_max_id\n");
|
||||
__asm("ja categ_skip_add\n");
|
||||
__asm("push edx\n");
|
||||
__asm("push ecx\n");
|
||||
__asm("push eax\n");
|
||||
__asm("call %P0" : : "i"(is_a_custom));
|
||||
__asm("test eax, eax\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("push eax\n");
|
||||
@ -570,12 +589,16 @@ 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("push ecx\n");
|
||||
__asm("push edx\n");
|
||||
__asm("push eax\n");
|
||||
__asm("call %P0" : : "i"(is_a_custom));
|
||||
__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");
|
||||
|
||||
@ -595,12 +618,16 @@ 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("push ecx\n");
|
||||
__asm("push edx\n");
|
||||
__asm("push eax\n");
|
||||
__asm("call %P0" : : "i"(is_a_custom));
|
||||
__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");
|
||||
|
||||
@ -745,7 +772,7 @@ static bool patch_favorite_categ(const char *game_dll_fn) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool patch_custom_categ(const char *game_dll_fn) {
|
||||
static bool patch_custom_categ(const char *game_dll_fn, uint16_t min_id) {
|
||||
|
||||
DWORD dllSize = 0;
|
||||
char *data = getDllData(game_dll_fn, &dllSize);
|
||||
@ -890,68 +917,21 @@ static bool patch_custom_categ(const char *game_dll_fn) {
|
||||
|
||||
char formatted_title[128];
|
||||
sprintf(formatted_title, g_categformat, g_categname);
|
||||
LOG("popnhax: custom %s \"%s\" injected (for songids ", g_subcategmode? "subcategories":"category", formatted_title);
|
||||
if (g_max_id)
|
||||
LOG("between %d and %d (incl.))\n", g_min_id, g_max_id);
|
||||
else
|
||||
LOG("%d and up)\n", g_min_id);
|
||||
LOG("popnhax: custom %s \"%s\" injected", g_subcategmode? "subcategories":"category", formatted_title);
|
||||
if (min_id)
|
||||
LOG(" (for songids >= %d)", min_id);
|
||||
LOG("\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//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)*((++g_subcateg_count)+1));
|
||||
subcateg = &(subcategories[g_subcateg_count]);
|
||||
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;
|
||||
void init_subcategories() {
|
||||
g_subcategmode = true;
|
||||
g_subcateg_count = 0;
|
||||
subcategories = (subcategory_s*)realloc(subcategories, sizeof(subcategory_s)*(1));
|
||||
subcategories[0].name = strdup("ALL SONGS");
|
||||
subcategories[0].songlist = NULL;
|
||||
subcategories[0].size = 0;
|
||||
|
||||
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;
|
||||
parse_musicdb(result[i].c_str());
|
||||
}
|
||||
}
|
||||
|
||||
static void print_databases() {
|
||||
@ -968,12 +948,18 @@ void hook_after_getlevel()
|
||||
{
|
||||
__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("push eax\n");
|
||||
__asm("push ecx\n");
|
||||
__asm("push edx\n");
|
||||
__asm("push ebx\n");
|
||||
__asm("call %P0" : : "i"(is_excluded_from_level));
|
||||
__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("mov eax, 0x00\n");
|
||||
@ -1008,8 +994,6 @@ bool patch_exclude(const char *game_dll_fn)
|
||||
|
||||
bool patch_custom_categs(const char *dllFilename, struct popnhax_config *config)
|
||||
{
|
||||
g_min_id = config->custom_categ_min_songid;
|
||||
g_max_id = config->custom_categ_max_songid;
|
||||
uint8_t mode = config->custom_categ;
|
||||
|
||||
char icon_path[64];
|
||||
@ -1024,11 +1008,8 @@ bool patch_custom_categs(const char *dllFilename, struct popnhax_config *config)
|
||||
g_categicon = "cate_13";
|
||||
}
|
||||
|
||||
if (mode == 2)
|
||||
if (mode == 1)
|
||||
{
|
||||
g_subcategmode = true;
|
||||
load_databases();
|
||||
} else {
|
||||
songlist = (uint32_t*)calloc(1,5); //for some reason it crashes without that 4 cells extra margin
|
||||
}
|
||||
|
||||
@ -1041,7 +1022,7 @@ bool patch_custom_categs(const char *dllFilename, struct popnhax_config *config)
|
||||
else
|
||||
g_categformat = "%s";
|
||||
|
||||
if (!patch_custom_categ(dllFilename))
|
||||
if (!patch_custom_categ(dllFilename, config->custom_categ_min_songid))
|
||||
return false;
|
||||
|
||||
if (mode == 2)
|
||||
@ -1059,8 +1040,12 @@ bool patch_custom_categs(const char *dllFilename, struct popnhax_config *config)
|
||||
|
||||
if (config->custom_exclude_from_version)
|
||||
LOG("popnhax: Customs excluded from version folders\n"); //musichax_core_init took care of it
|
||||
|
||||
if (config->custom_exclude_from_level)
|
||||
{
|
||||
g_exclude_omni = config->exclude_omni;
|
||||
patch_exclude(dllFilename);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -3,6 +3,20 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include "popnhax/config.h"
|
||||
#include "util/bst.h"
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
uint32_t size;
|
||||
uint32_t *songlist; //really is a (songlist_t *) pointer
|
||||
} subcategory_s;
|
||||
|
||||
extern uint32_t g_max_id;
|
||||
extern bst_t *g_customs_bst;
|
||||
|
||||
void init_subcategories();
|
||||
void add_song_to_subcateg(uint32_t songid, subcategory_s* subcateg);
|
||||
subcategory_s* get_subcateg(const char *title);
|
||||
|
||||
bool patch_custom_categs(const char *dllFilename, struct popnhax_config *config);
|
||||
bool patch_local_favorites(const char *dllFilename, uint8_t version);
|
||||
|
@ -188,10 +188,12 @@ PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_S8, struct popnhax_config, base_offset,
|
||||
"/popnhax/base_offset")
|
||||
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_U8, struct popnhax_config, custom_categ,
|
||||
"/popnhax/custom_categ")
|
||||
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, exclude_omni,
|
||||
"/popnhax/exclude_omni")
|
||||
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, partial_entries,
|
||||
"/popnhax/partial_entries")
|
||||
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_U16, struct popnhax_config, custom_categ_min_songid,
|
||||
"/popnhax/custom_categ_min_songid")
|
||||
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_U16, struct popnhax_config, custom_categ_max_songid,
|
||||
"/popnhax/custom_categ_max_songid")
|
||||
PSMAP_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,
|
||||
@ -208,6 +210,12 @@ PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, local_favorite
|
||||
"/popnhax/local_favorites")
|
||||
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, ignore_music_limit,
|
||||
"/popnhax/ignore_music_limit")
|
||||
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, high_framerate,
|
||||
"/popnhax/high_framerate")
|
||||
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, high_framerate_limiter,
|
||||
"/popnhax/high_framerate_limiter")
|
||||
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_U16, struct popnhax_config, high_framerate_fps,
|
||||
"/popnhax/high_framerate_fps")
|
||||
PSMAP_END
|
||||
|
||||
enum BufferIndexes {
|
||||
@ -3609,20 +3617,48 @@ int usbPadReadHook(uint32_t *pad_bits)
|
||||
|
||||
uint32_t g_offset_fix[9] = {0};
|
||||
uint8_t g_poll_index = 0;
|
||||
uint32_t g_poll_offset = 0;
|
||||
void (*real_enhanced_poll)();
|
||||
void patch_enhanced_poll() {
|
||||
/* eax contains button being checked [0-8]
|
||||
* esi contains delta about to be evaluated
|
||||
* we need to do esi -= buttonGetMillis([%eax]); to fix the offset accurately */
|
||||
__asm("nop\n");
|
||||
__asm("nop\n");
|
||||
__asm("mov %0, al\n":"=m"(g_poll_index): :);
|
||||
g_poll_offset = buttonGetMillis(g_poll_index);
|
||||
__asm("sub esi, %0\n": :"b"(g_poll_offset));
|
||||
g_offset_fix[g_poll_index] = g_poll_offset;
|
||||
__asm("push edx\n");
|
||||
|
||||
real_enhanced_poll();
|
||||
__asm("mov %0, al\n":"=m"(g_poll_index): :);
|
||||
|
||||
/* reimplem buttonGetMillis(), result in ebx */
|
||||
__asm("xor ebx,ebx\n");
|
||||
__asm("mov edx,dword ptr ds:[eax*4+%0]\n"::"d"(g_button_state));
|
||||
__asm("cmp edx,0xFFFFFFFF\n");
|
||||
__asm("je button_state_empty\n");
|
||||
|
||||
__asm("push ecx\n");
|
||||
__asm("push edx\n");
|
||||
__asm("call %P0" : : "i"(timeGetTime));
|
||||
__asm("pop edx\n");
|
||||
__asm("pop ecx\n");
|
||||
__asm("cmp edx,eax\n");
|
||||
|
||||
__asm("jbe button_has_been_pressed\n");
|
||||
|
||||
__asm("restore_eax:\n");
|
||||
__asm("movzx eax, %0\n": :"d"(g_poll_index));
|
||||
|
||||
/* leave buttonGetMillis */
|
||||
__asm("button_state_empty:\n");
|
||||
__asm("sub esi, ebx\n"); // actual delta correction
|
||||
|
||||
__asm("lea edx,dword ptr [eax*4+%0]\n"::"d"(g_offset_fix));
|
||||
__asm("mov [edx], ebx\n"::); // save correction value in g_offset_fix[g_poll_index];
|
||||
|
||||
__asm("pop edx\n");
|
||||
__asm("mov eax, %0\n" : : "i"(&real_enhanced_poll));
|
||||
__asm("jmp [eax]");
|
||||
|
||||
__asm("button_has_been_pressed:\n");
|
||||
__asm("sub eax,edx\n"); // eax = correction value (currTime - g_button_state[g_poll_index]);
|
||||
__asm("mov ebx, eax\n");// put correction value in ebx
|
||||
__asm("jmp restore_eax\n");
|
||||
}
|
||||
|
||||
static HANDLE enhanced_polling_thread;
|
||||
@ -4395,7 +4431,49 @@ static bool patch_hd_resolution(uint8_t mode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool patch_fps_uncap() {
|
||||
static bool patch_fps_uncap(uint16_t fps) {
|
||||
|
||||
if (fps != 0)
|
||||
{
|
||||
/* TODO: fix in spicetools and remove this */
|
||||
uint8_t count = 0;
|
||||
while (find_and_patch_hex(NULL, "\x55\x31\xC0\x89\xE5\x57\x8B\x4D\x08\x8D\x79\x04\xC7\x01\x00\x00\x00\x00\x83\xE7\xFC\xC7\x41\x24\x00\x00\x00\x00\x29\xF9\x83\xC1\x28\xC1\xE9\x02\xF3\xAB\x8B\x7D\xFC\xC9\xC3", 43, 0, "\x31\xC0\xC3", 3))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count)
|
||||
{
|
||||
LOG("popnhax: frame_limiter: patched %u instance(s) of memset(a1, 0, 40) (bad usbPadReadLast io hook)\n", count);
|
||||
}
|
||||
|
||||
uint8_t ft = (1000 + (fps / 2)) / fps; // rounded 1000/fps
|
||||
int8_t delta = 16-ft;
|
||||
int8_t newval = -1*delta-2;
|
||||
|
||||
/* enforce fps rate */
|
||||
if (!find_and_patch_hex(g_game_dll_fn, "\x7E\x07\xB9\x0C\x00\x00\x00\xEB\x09\x85\xC9", 11, -1, "\xFF", 1))
|
||||
{
|
||||
LOG("popnhax: frame_limiter: cannot patch frame limiter\n");
|
||||
return false;
|
||||
}
|
||||
if (!find_and_patch_hex(g_game_dll_fn, "\x7E\x07\xB9\x0C\x00\x00\x00\xEB\x09\x85\xC9", 11, 2, "\x90\x90\x90\x90\x90", 5))
|
||||
{
|
||||
LOG("popnhax: frame_limiter: cannot patch frame limiter\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* adjust sleep time (original code is "add -2", replace with "add newval") */
|
||||
if (!find_and_patch_hex(g_game_dll_fn, "\x6A\x00\x83\xC1\xFE\x51\xFF", 7, 4, (char *)&newval, 1))
|
||||
{
|
||||
LOG("popnhax: frame_limiter: cannot patch frame limiter\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG("popnhax: fps capped to %u fps (%ums frame time, new val %d)\n", fps, ft, newval);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!find_and_patch_hex(g_game_dll_fn, "\x7E\x07\xB9\x0C\x00\x00\x00\xEB\x09\x85\xC9", 11, 0, "\xEB\x1C", 2))
|
||||
{
|
||||
LOG("popnhax: fps uncap: cannot find frame limiter\n");
|
||||
@ -7407,6 +7485,41 @@ static bool get_music_limit_from_file(const char *filepath, uint32_t *limit){
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool patch_afp_framerate(uint16_t fps)
|
||||
{
|
||||
DWORD framerate = fps;
|
||||
|
||||
if ( framerate == 0 )
|
||||
{
|
||||
DEVMODE lpDevMode;
|
||||
memset(&lpDevMode, 0, sizeof(DEVMODE));
|
||||
lpDevMode.dmSize = sizeof(DEVMODE);
|
||||
lpDevMode.dmDriverExtra = 0;
|
||||
|
||||
if ( EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &lpDevMode) == 0 )
|
||||
{
|
||||
LOG("popnhax: high_framerate: could not retrieve display mode\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
framerate = lpDevMode.dmDisplayFrequency;
|
||||
} else {
|
||||
LOG("popnhax: high_framerate: force %ldHz\n", framerate);
|
||||
}
|
||||
|
||||
float new_value = 1./framerate;
|
||||
char *as_hex = (char*)&new_value;
|
||||
|
||||
if ( !find_and_patch_hex(g_game_dll_fn, "\x82\x9D\x88\x3C", 4, 0, as_hex, 4) )
|
||||
{
|
||||
LOG("popnhax: high_framerate: cannot patch animation speed for %ldHz\n", framerate);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG("popnhax: high_framerate: patched animation speed for %ldHz\n", framerate);
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
|
||||
switch (ul_reason_for_call) {
|
||||
case DLL_PROCESS_ATTACH: {
|
||||
@ -7786,8 +7899,14 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
|
||||
if (config.guidese_off)
|
||||
option_guide_se_off();
|
||||
|
||||
if (config.high_framerate)
|
||||
{
|
||||
patch_afp_framerate(config.high_framerate_fps);
|
||||
config.fps_uncap = true;
|
||||
}
|
||||
|
||||
if (config.fps_uncap)
|
||||
patch_fps_uncap();
|
||||
patch_fps_uncap(config.high_framerate_limiter ? config.high_framerate_fps : 0);
|
||||
|
||||
if (config.enhanced_polling)
|
||||
{
|
||||
|
@ -4,9 +4,11 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include "imports/avs.h"
|
||||
#include "util/bst.h"
|
||||
#include "util/patch.h"
|
||||
#include "util/log.h"
|
||||
#include "xmlhelper.h"
|
||||
#include "custom_categs.h"
|
||||
|
||||
#include "tableinfo.h"
|
||||
#include "loader.h"
|
||||
@ -819,12 +821,54 @@ void parse_charadb(const char *input_filename, const char *target) {
|
||||
free(config_xml);
|
||||
}
|
||||
|
||||
static char *get_subcateg_title(const char* path) {
|
||||
char *categ_name = NULL;
|
||||
char filename[64];
|
||||
|
||||
//try to open "folderpath/_name.txt"
|
||||
size_t len = (size_t)(strchr(path+10, '\\')-(path));
|
||||
strncpy(filename, path, len);
|
||||
sprintf(filename+len, "\\_name.txt");
|
||||
|
||||
FILE *file = fopen(filename, "r");
|
||||
if ( file != NULL ) {
|
||||
//if it has a custom title, use it
|
||||
char line[64];
|
||||
if (fgets(line, sizeof(line), file)) {
|
||||
//handle UTF-8 BOM since that could be common
|
||||
categ_name = (memcmp(line, "\xEF\xBB\xBF", 3) == 0) ? strdup(line+3) : strdup(line);
|
||||
LOG("%s sets subcategory name to %s\n", filename, categ_name);
|
||||
}
|
||||
fclose(file);
|
||||
} else { // or just keep folder name by itself (cut "data_mods")
|
||||
len = (size_t)(strchr(path+10, '\\')-(path+10));
|
||||
categ_name = (char*) malloc(len+1);
|
||||
strncpy(categ_name, path+10, len);
|
||||
categ_name[len] = '\0';
|
||||
}
|
||||
|
||||
return categ_name;
|
||||
}
|
||||
|
||||
static bool is_excluded_folder(const char *input_filename)
|
||||
{
|
||||
return (input_filename[strlen("data_mods/")] == '_');
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
char *subcateg_title = NULL;
|
||||
subcategory_s *subcateg = NULL;
|
||||
if (config->custom_categ == 2)
|
||||
{
|
||||
subcateg_title = get_subcateg_title(input_filename);
|
||||
subcateg = get_subcateg(subcateg_title); //will return a new one if not found
|
||||
}
|
||||
|
||||
property *config_xml = load_prop_file(input_filename);
|
||||
|
||||
if (target && strlen(target) > 0) {
|
||||
@ -860,7 +904,24 @@ void parse_musicdb(const char *input_filename, const char *target, struct popnha
|
||||
// If it doesn't exist, create a new entry in memory
|
||||
// Update the data in-place and make all parameters optional
|
||||
music_entry *m = get_music(idx);
|
||||
bool is_fresh = m == NULL;
|
||||
bool is_fresh = m == NULL; // ie. not part of internal songdb
|
||||
bool is_gone = ( m != NULL && strcmp((const char*) m->title_ptr, "\x81\x5D") == 0); // removed entries all have this title (SJIS "-")
|
||||
|
||||
// Update customs/omni songid list
|
||||
if ( is_fresh || is_gone || config->partial_entries )
|
||||
{
|
||||
if ( idx >= config->custom_categ_min_songid && bst_search(g_customs_bst, idx) == NULL )
|
||||
{
|
||||
g_customs_bst = bst_insert(g_customs_bst, idx);
|
||||
//LOG("%d inserted into customs bst\n", idx);
|
||||
if (config->custom_categ == 2)
|
||||
{
|
||||
add_song_to_subcateg(idx, subcateg);
|
||||
}
|
||||
} else {
|
||||
//LOG("%d already present in customs bst\n", idx);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_fresh) {
|
||||
// Default music entry
|
||||
@ -916,13 +977,11 @@ void parse_musicdb(const char *input_filename, const char *target, struct popnha
|
||||
}
|
||||
}
|
||||
|
||||
//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) )
|
||||
&& !is_excluded_folder(input_filename)
|
||||
&& idx >= config->custom_categ_min_songid
|
||||
&& ( is_fresh || config->exclude_omni ) )
|
||||
{
|
||||
m->cs_version = 0;
|
||||
m->folder = 0;
|
||||
@ -1003,6 +1062,11 @@ void parse_musicdb(const char *input_filename, const char *target, struct popnha
|
||||
}
|
||||
|
||||
free(config_xml);
|
||||
|
||||
if (config->custom_categ == 2)
|
||||
{
|
||||
free(subcateg_title);
|
||||
}
|
||||
}
|
||||
|
||||
void load_databases(const char *target_datecode, struct popnhax_config *config) {
|
||||
@ -1021,6 +1085,9 @@ void load_databases(const char *target_datecode, struct popnhax_config *config)
|
||||
parse_charadb(result[i].c_str(), target_datecode);
|
||||
}
|
||||
|
||||
if (config->custom_categ == 2)
|
||||
init_subcategories();
|
||||
|
||||
for(uint16_t i=0;i<result.size();i++)
|
||||
{
|
||||
if ( strstr(result[i].c_str(), "musicdb") == NULL )
|
||||
|
@ -2,6 +2,7 @@ libs += util
|
||||
|
||||
srcpp_util := \
|
||||
fuzzy_search.cc \
|
||||
bst.cc \
|
||||
search.cc \
|
||||
cmdline.cc \
|
||||
patch.cc \
|
||||
|
34
util/bst.cc
Normal file
34
util/bst.cc
Normal file
@ -0,0 +1,34 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bst.h"
|
||||
|
||||
bst_t* bst_search(bst_t *root, uint32_t val)
|
||||
{
|
||||
if( root == NULL || root->data == val )
|
||||
return root;
|
||||
else if( val > (root->data) )
|
||||
return bst_search(root->right, val);
|
||||
else
|
||||
return bst_search(root->left,val);
|
||||
}
|
||||
|
||||
bst_t* bst_insert(bst_t *root, uint32_t val)
|
||||
{
|
||||
if ( root == NULL )
|
||||
{
|
||||
bst_t *p;
|
||||
p = (bst_t *)malloc(sizeof(bst_t));
|
||||
p->data = val;
|
||||
p->left = NULL;
|
||||
p->right = NULL;
|
||||
return p;
|
||||
}
|
||||
else if ( val > root->data )
|
||||
root->right = bst_insert(root->right, val);
|
||||
else
|
||||
root->left = bst_insert(root->left, val);
|
||||
|
||||
return root;
|
||||
}
|
14
util/bst.h
Normal file
14
util/bst.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef __BST_H__
|
||||
#define __BST_H__
|
||||
|
||||
typedef struct bst_s
|
||||
{
|
||||
uint32_t data;
|
||||
struct bst_s *right;
|
||||
struct bst_s *left;
|
||||
} bst_t;
|
||||
|
||||
bst_t* bst_search(bst_t *root, uint32_t val);
|
||||
bst_t* bst_insert(bst_t *root, uint32_t val);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user