Compare commits

...

53 Commits
main ... newwip

Author SHA1 Message Date
60f1b172ef wildcard search 2024-06-07 01:42:24 +02:00
f5efc561c0 disable backtosongselect for battle/local modes 2024-06-07 01:42:24 +02:00
d23931f7f7 fix local favorite end of credit crash with guest 2024-06-07 01:42:24 +02:00
03104c4ae9 result screen retire/retry sound ok 2024-06-06 00:33:57 +02:00
65f9c02f65 backtosongselect sound ok 2024-06-06 00:19:59 +02:00
a00cbd6daa indent 2024-06-05 20:44:27 +02:00
3a57387e6a wip backtosongselect fix 2024-06-05 20:43:39 +02:00
d483cd61d7 wip sound when pfree leave 2024-06-05 00:39:18 +02:00
cce17f9483 auto hispeed reset to default bpm between credits 2024-06-04 21:59:52 +02:00
bed5eed9b6 high_framerate_limiter 2024-06-03 21:05:29 +02:00
87de9ed78a high framerate fps 2024-06-03 21:05:29 +02:00
f1a78c8469 high framerate patch 2024-06-03 21:05:29 +02:00
94377ea218 exclude custom folder with _ or _exclude file 2024-06-03 21:05:29 +02:00
3c20bef186 rename subcateg via _name.txt 2024-06-03 21:05:29 +02:00
6a21121e8b remove max_song_id, add partial_entries and exclude_omni 2024-06-03 21:05:29 +02:00
fc4a817824 move subcateg creation to loader.cc 2024-06-03 21:05:29 +02:00
899b311281 add BST for customs 2024-06-03 21:05:29 +02:00
989350e051 wip better custom detection 2024-06-03 21:05:29 +02:00
6f2a340359 wip exclude customs by prepending _ 2024-06-03 21:05:29 +02:00
2b7ba52f1a fix enhanced polling judgement correction 2024-06-03 21:05:29 +02:00
c39bce4e1f fix auto hispeed soflan retry quirks 2024-06-03 21:05:29 +02:00
eb56b39d1b fix auto hispeed random x1.0 bug 2024-06-03 21:05:29 +02:00
aa7505206c enhance custom popkuns handling for record mode 2024-06-03 21:05:29 +02:00
27d6453bd3 r2nk226: popkun change 2024-06-03 21:05:29 +02:00
ae12d608cd r2nk226: record mode, practice mode enhanced 2024-06-03 21:05:04 +02:00
8795e55f02 change ver 2024-05-15 19:12:50 +02:00
f569e9dd4b scrolling bugfix 2024-05-12 11:38:06 +02:00
4c5751a694 fix subcategories scrolling bug 2024-05-04 20:15:25 +02:00
15b1bf20a7 post-release bugfixes 2024-05-04 01:52:36 +02:00
4d618e5d1c assets + build rework 2024-05-03 22:33:01 +02:00
607c2842b6 custom track title format2 2024-05-03 22:30:55 +02:00
dd109549b0 fix N 0 appearing on song select 2024-05-03 22:30:55 +02:00
edea20b403 auto_diag 2024-05-03 22:30:55 +02:00
3fef3f58bf cleanup 2024-05-03 22:30:55 +02:00
6dfed35c5f force_datecode auto 2024-05-03 22:30:55 +02:00
15122be9b7 music limit check 2024-05-03 22:30:55 +02:00
151c90352d wip exclude from level 2024-05-03 22:30:55 +02:00
0e1f365ac2 different favorite files per game version 2024-05-03 22:30:55 +02:00
e9bdbaa9f7 exclude from versions,CS + default fav rework 2024-05-03 22:30:55 +02:00
814edb296f wip favorites 2024-05-03 22:30:43 +02:00
52048c2133 wip custom highlight 2024-05-03 22:23:23 +02:00
3083c62c67 add warning for back to song select without quick retire 2024-05-03 22:23:23 +02:00
11801e6340 wip custom highlight 2024-05-03 22:23:23 +02:00
f2802c5bd7 wip categ 2024-05-03 22:23:23 +02:00
a4a4e53da1 inject custom category 2024-04-17 21:00:57 +02:00
fd529834b5 fix autohispeed for kaimei- 2024-04-17 20:55:22 +02:00
0cb685ef4b fix power point convergence value with customs 2024-01-05 22:32:22 +01:00
dc987c46ac expose base_offset as an option 2024-01-03 14:38:50 +01:00
4706d117a1 auto hispeed soflan retry 2024-01-03 14:11:34 +01:00
2535fba8d6 fix autohispeed 2024-01-03 00:41:21 +01:00
ccf3a4c8f8 rewrite patch pattern for back_to_song_select, render loop (enhanced_polling_stats and practice mode) 2023-12-30 00:49:24 +01:00
c3e8eb5f13 display version in log, rewrite patch pattern for quick retire, auto hispeed 2023-12-30 00:49:09 +01:00
783dafbe97 fix WSL compilation 2023-12-29 23:24:47 +01:00
23 changed files with 5257 additions and 1191 deletions

1
.gitignore vendored Normal file
View File

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

View File

@ -232,8 +232,8 @@ $$(dll_$1_$2_$3) $$(implib_$1_$2_$3): $$(obj_$1_$2_$3) $$(abslib_$1_$2_$3) \
$(ccache) $$(toolchain_$1)gcc -shared $$(srcdir_$3)/$3.def \
-o $$(dll_$1_$2_$3) -Wl,--out-implib,$$(implib_$1_$2_$3) \
$$^ $$(ldflags_$3) $(optflags_$1)
strip -s $$(dll_$1_$2_$3)
ranlib $$(implib_$1_$2_$3)
$$(toolchain_$1)strip -s $$(dll_$1_$2_$3)
$$(toolchain_$1)ranlib $$(implib_$1_$2_$3)
endef
@ -248,7 +248,7 @@ exe_$1_$2_$3 := $$(bindir_$1_$2)/$3.exe
$$(exe_$1_$2_$3): $$(obj_$1_$2_$3) $$(abslib_$1_$2_$3) $$(absdpl_$1_$2_$3) \
| $$(bindir_$1_$2)
$(ccache) $$(toolchain_$1)gcc -o $$@ $$^ $$(ldflags_$3) $(optflags_$1)
strip -s $$@
$$(toolchain_$1)strip -s $$@
endef
@ -259,7 +259,7 @@ define t_import
impdef_$1_$2_$3 ?= imports/import_$1_$2_$3.def
$$(bindir_$1_$2)/lib$3.a: $$(impdef_$1_$2_$3) | $$(bindir_$1_$2)
dlltool -l $$@ -d $$<
$$(toolchain_$1)dlltool -l $$@ -d $$<
endef

View File

@ -14,17 +14,15 @@ include popnhax/Module.mk
#
zipdir := $(BUILDDIR)/zip
popnhax_version := $(shell grep "define PROGRAM_VERSION" popnhax/dllmain.cc | cut -d'"' -f2)
$(zipdir)/:
mkdir -p $@
$(BUILDDIR)/popnhax_v$(popnhax_version).zip: \
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: \
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
all: $(BUILDDIR)/popnhax_v$(popnhax_version).zip

View File

@ -2,10 +2,32 @@
# 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
### 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
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.

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

@ -5,6 +5,22 @@
<patch_db __type="bool">0</patch_db>
<!-- Force unlock music, charts, characters, and deco parts when applicable -->
<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">2</custom_categ>
<!-- Prevent customs from showing up in version folders -->
<custom_exclude_from_version __type="bool">1</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/<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) -->
@ -27,10 +43,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>
@ -49,7 +61,7 @@
<hispeed_default_bpm __type="u16">0</hispeed_default_bpm>
<!-- 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>
<!-- Force full options by default (useful when no numpad is available) -->
<force_full_opt __type="bool">0</force_full_opt>
@ -87,7 +99,6 @@
<!-- PLEASE USE OFFLINE ONLY -->
<practice_mode __type="bool">0</practice_mode>
<!-- Press 9 on option select screen to go back to song selection (requires quick_retire) -->
<!-- Note: causes issues with sounds on song select -->
<back_to_song_select __type="bool">0</back_to_song_select>
@ -96,22 +107,22 @@
======================================================================================== -->
<!-- Datecode and Multiboot -->
<!-- Force a different datecode than the one found in ea3-config (yyyymmdd00) -->
<force_datecode __type="str"></force_datecode>
<!-- 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">auto</force_datecode>
<!-- Also apply force_datecode to network packets -->
<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>
<!-- Timing and lanes -->
<!-- Base visual offset (value will be added to the base SD (-60) and base HD (-76) values) -->
<base_offset __type="s8">0</base_offset>
<!-- Automatically play keysounds during songs -->
<disable_keysounds __type="bool">0</disable_keysounds>
<!-- Offset the keysounds by x ms (negative is earlier). With disable_keysounds, becomes an audio offset -->
<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
@ -121,15 +132,41 @@
THREAD_PRIORITY_TIME_CRITICAL 15 -->
<enhanced_polling_priority __type="s8">1</enhanced_polling_priority>
<!-- Song db patches -->
<!-- Auto select patch file from data_mods folder (will detect datecode from ea3-config or force_datecode option) -->
<!-- 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) -->
<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>
<!-- 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>
<!-- Do not perform music limit checks for patch_xml_auto (not recommended) -->
<ignore_music_limit __type="bool">0</ignore_music_limit>
<!-- 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) -->
<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 above, but rotation does, e.g. "* [rz:3]%s[/rz]" ) -->
<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 -->
<!-- Disable .dict string replacements and .ips patches -->

View File

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

View File

@ -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;
@ -14,6 +15,10 @@ struct popnhax_config {
bool quick_retire;
bool back_to_song_select;
bool score_challenge;
uint8_t custom_categ;
uint16_t custom_categ_min_songid;
bool custom_exclude_from_version;
bool custom_exclude_from_level;
bool force_hd_timing;
uint8_t force_hd_resolution;
bool force_unlocks;
@ -26,12 +31,14 @@ struct popnhax_config {
bool force_full_opt;
bool netvs_off;
bool guidese_off;
bool local_favorites;
bool patch_db;
bool disable_expansions;
bool disable_redirection;
bool disable_multiboot;
bool patch_xml_auto;
bool ignore_music_limit;
char patch_xml_filename[MAX_PATH];
char force_datecode[11];
bool network_datecode;
@ -51,6 +58,16 @@ struct popnhax_config {
uint8_t survival_gauge;
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];
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

1115
popnhax/custom_categs.cc Normal file

File diff suppressed because it is too large Load Diff

24
popnhax/custom_categs.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef __CUSTOM_CATEGS_H__
#define __CUSTOM_CATEGS_H__
#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);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -4,8 +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"
@ -66,7 +69,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,12 +821,76 @@ void parse_charadb(const char *input_filename, const char *target) {
free(config_xml);
}
void parse_musicdb(const char *input_filename, const char *target) {
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;
}
#define F_OK 0
static bool is_excluded_folder(const char *path)
{
char filename[64];
//try to open "folderpath/_exclude"
size_t len = (size_t)(strchr(path+10, '\\')-(path));
strncpy(filename, path, len);
sprintf(filename+len, "\\_exclude");
if (access(filename, F_OK) == 0)
{
LOG("%s causes folder to be excluded from customs (contents will be treated as regular songs)\n", filename);
return true;
}
if ( path[strlen("data_mods/")] == '_' )
{
filename[strchr(path+10, '\\')-path] = '\0';
LOG("%s starting with _ causes folder to be excluded from customs (contents will be treated as regular songs)\n", filename);
return true;
}
return false;
}
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;
}
bool excluded = (config->custom_categ && is_excluded_folder(input_filename));
char *subcateg_title = NULL;
subcategory_s *subcateg = NULL;
if (config->custom_categ == 2 && !excluded)
{
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) {
@ -859,7 +926,22 @@ void parse_musicdb(const char *input_filename, const char *target) {
// 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 && !excluded && 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);
}
}
}
if (is_fresh) {
// Default music entry
@ -915,6 +997,16 @@ void parse_musicdb(const char *input_filename, const char *target) {
}
}
if ( config->custom_categ
&& config->custom_exclude_from_version
&& !excluded
&& idx >= config->custom_categ_min_songid
&& ( is_fresh || config->exclude_omni ) )
{
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)) {
@ -990,9 +1082,14 @@ void parse_musicdb(const char *input_filename, const char *target) {
}
free(config_xml);
if (config->custom_categ == 2 && !excluded)
{
free(subcateg_title);
}
}
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");
@ -1008,16 +1105,19 @@ void load_databases(const char *target_datecode) {
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 )
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 +1136,10 @@ 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 +1285,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++) {

View File

@ -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,

View File

@ -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
@ -84,40 +84,40 @@ 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 */
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);
}
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;
/* 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;
}
if (memcmp(buffer, "PATCH", 5) != 0)
{
LOG("popnhax: translation: invalid .ips header\n");
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 (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");
}
/* 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);
/* 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 trans_base = 0; /* eclale patch adds new section header which I'm relocating */
uint32_t trans_base_offset = 0;
uint32_t trans_rebase = 0;
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;
uint32_t offset = 0;
uint16_t size = 0;
uint16_t count = 0;
uint8_t replace[MAX_REPLACE_SIZE] = {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;
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;
/* 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;
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 */
}
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;
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;
//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);
IPS_READ(&size, 2, "RLE SIZE");
bp = (uint8_t *)&size;
size = BYTE2_TO_UINT(bp);
IPS_READ(&value, 1, "RLE VALUE");
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 )
{
//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;
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");
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);
if ( !skip )
patch_memory((int64_t)data+chunk_rva, (char *)replace, MAX_REPLACE_SIZE);
remaining -= MAX_REPLACE_SIZE;
chunk_rva += MAX_REPLACE_SIZE;
remaining -= MAX_REPLACE_SIZE;
chunk_rva += MAX_REPLACE_SIZE;
} while (remaining > MAX_REPLACE_SIZE);
} while (remaining > MAX_REPLACE_SIZE);
size = remaining;
rva = chunk_rva;
size = remaining;
rva = chunk_rva;
}
}
IPS_READ(&replace, size, "DATA");
}
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);
}
}
/* 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);
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);
}
/* 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 ( !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);
/* 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");
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);
}
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;
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");
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);
ips_done = patch_translation_ips(dllFilename, folder, debug);
dict_done = patch_translation_dict(dllFilename, folder, debug);
return ips_done || dict_done;
return ips_done || dict_done;
}

View File

@ -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
View 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
View 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

View File

@ -26,7 +26,7 @@ static void badCharHeuristic(const unsigned char *str, int size, int* badchar) {
#define DEBUG_SEARCH 0
int _search(unsigned char *haystack, size_t haystack_size, const unsigned char *needle, size_t needle_size, int orig_offset, int debug) {
int _search(unsigned char *haystack, size_t haystack_size, const unsigned char *needle, size_t needle_size, int orig_offset, bool wildcards, int debug) {
int badchar[NO_OF_CHARS];
badCharHeuristic(needle, needle_size, badchar);
@ -46,12 +46,21 @@ int _search(unsigned char *haystack, size_t haystack_size, const unsigned char *
LOG("pat...");
for (size_t i = 0; i < needle_size; i++)
{
LOG("%02x ", needle[i]);
if (wildcards && needle[i] == '?')
LOG("** ");
else
LOG("%02x ", needle[i]);
}
LOG("\n");
}
while (j >= 0 && needle[j] == haystack[orig_offset + s + j])
j--;
if ( wildcards )
{
while (j >= 0 && ( needle[j] == '?' || needle[j] == haystack[orig_offset + s + j]) )
j--;
} else {
while (j >= 0 && ( needle[j] == haystack[orig_offset + s + j]) )
j--;
}
if (j < 0) {
if (debug)
@ -70,6 +79,21 @@ int _search(unsigned char *haystack, size_t haystack_size, const unsigned char *
}
int search(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, 0);
int res = _search((unsigned char*) haystack, haystack_size, (const unsigned char *)needle, needle_size, orig_offset, false, 0);
return res;
}
int wildcard_search(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, true, 0);
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, false, 2);
return res;
}
int wildcard_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, true, 2);
return res;
}

View File

@ -2,5 +2,8 @@
#define __SEARCH_H__
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);
int wildcard_search(char *haystack, size_t haystack_size, const char *needle, size_t needle_size, size_t orig_offset);
int wildcard_search_debug(char *haystack, size_t haystack_size, const char *needle, size_t needle_size, size_t orig_offset);
#endif