Compare commits

...

30 Commits

Author SHA1 Message Date
c0098cc222 popkun change 2024-05-24 00:00:56 +02:00
6eb22666ac record mode 2024-05-15 19:13:00 +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
20 changed files with 4667 additions and 1172 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 \ $(ccache) $$(toolchain_$1)gcc -shared $$(srcdir_$3)/$3.def \
-o $$(dll_$1_$2_$3) -Wl,--out-implib,$$(implib_$1_$2_$3) \ -o $$(dll_$1_$2_$3) -Wl,--out-implib,$$(implib_$1_$2_$3) \
$$^ $$(ldflags_$3) $(optflags_$1) $$^ $$(ldflags_$3) $(optflags_$1)
strip -s $$(dll_$1_$2_$3) $$(toolchain_$1)strip -s $$(dll_$1_$2_$3)
ranlib $$(implib_$1_$2_$3) $$(toolchain_$1)ranlib $$(implib_$1_$2_$3)
endef 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) \ $$(exe_$1_$2_$3): $$(obj_$1_$2_$3) $$(abslib_$1_$2_$3) $$(absdpl_$1_$2_$3) \
| $$(bindir_$1_$2) | $$(bindir_$1_$2)
$(ccache) $$(toolchain_$1)gcc -o $$@ $$^ $$(ldflags_$3) $(optflags_$1) $(ccache) $$(toolchain_$1)gcc -o $$@ $$^ $$(ldflags_$3) $(optflags_$1)
strip -s $$@ $$(toolchain_$1)strip -s $$@
endef endef
@ -259,7 +259,7 @@ define t_import
impdef_$1_$2_$3 ?= imports/import_$1_$2_$3.def impdef_$1_$2_$3 ?= imports/import_$1_$2_$3.def
$$(bindir_$1_$2)/lib$3.a: $$(impdef_$1_$2_$3) | $$(bindir_$1_$2) $$(bindir_$1_$2)/lib$3.a: $$(impdef_$1_$2_$3) | $$(bindir_$1_$2)
dlltool -l $$@ -d $$< $$(toolchain_$1)dlltool -l $$@ -d $$<
endef endef

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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -5,6 +5,18 @@
<patch_db __type="bool">0</patch_db> <patch_db __type="bool">0</patch_db>
<!-- Force unlock music, charts, characters, and deco parts when applicable --> <!-- Force unlock music, charts, characters, and deco parts when applicable -->
<force_unlocks __type="bool">0</force_unlocks> <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>
<!-- Classic patches --> <!-- Classic patches -->
<!-- Prevent crash on boot when using a different default audio source (aka HDMI audio patch) --> <!-- Prevent crash on boot when using a different default audio source (aka HDMI audio patch) -->
@ -27,10 +39,6 @@
<!-- quick_retire with pfree also enables quick retry: press numpad 8 during song or on result screen to retry (keep holding to skip option select) --> <!-- quick_retire 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> <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 --> <!-- Audio offset -->
<!-- Offset the audio by x ms (negative plays audio earlier). This will disable keysounds --> <!-- Offset the audio by x ms (negative plays audio earlier). This will disable keysounds -->
<audio_offset __type="s8">0</audio_offset> <audio_offset __type="s8">0</audio_offset>
@ -49,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>
@ -96,14 +104,16 @@
======================================================================================== --> ======================================================================================== -->
<!-- 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 -->
<!-- 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 --> <!-- Automatically play keysounds during songs -->
<disable_keysounds __type="bool">0</disable_keysounds> <disable_keysounds __type="bool">0</disable_keysounds>
<!-- Offset the keysounds by x ms (negative is earlier). With disable_keysounds, becomes an audio offset --> <!-- Offset the keysounds by x ms (negative is earlier). With disable_keysounds, becomes an audio offset -->
@ -122,14 +132,30 @@
<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) -->
<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>
<!-- 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 --> <!-- Translation -->
<!-- Disable .dict string replacements and .ips patches --> <!-- Disable .dict string replacements and .ips patches -->

View File

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

View File

@ -4,6 +4,7 @@
#include <stdbool.h> #include <stdbool.h>
struct popnhax_config { struct popnhax_config {
uint8_t game_version;
bool practice_mode; bool practice_mode;
bool hidden_is_offset; bool hidden_is_offset;
bool iidx_hard_gauge; bool iidx_hard_gauge;
@ -14,6 +15,11 @@ struct popnhax_config {
bool quick_retire; bool quick_retire;
bool back_to_song_select; bool back_to_song_select;
bool score_challenge; 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; bool force_hd_timing;
uint8_t force_hd_resolution; uint8_t force_hd_resolution;
bool force_unlocks; bool force_unlocks;
@ -26,12 +32,14 @@ struct popnhax_config {
bool force_full_opt; bool force_full_opt;
bool netvs_off; bool netvs_off;
bool guidese_off; bool guidese_off;
bool local_favorites;
bool patch_db; bool patch_db;
bool disable_expansions; bool disable_expansions;
bool disable_redirection; bool disable_redirection;
bool disable_multiboot; bool disable_multiboot;
bool patch_xml_auto; bool patch_xml_auto;
bool ignore_music_limit;
char patch_xml_filename[MAX_PATH]; char patch_xml_filename[MAX_PATH];
char force_datecode[11]; char force_datecode[11];
bool network_datecode; bool network_datecode;
@ -51,6 +59,11 @@ struct popnhax_config {
uint8_t survival_gauge; uint8_t survival_gauge;
bool survival_iidx; bool survival_iidx;
bool survival_spicy; 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];
}; };
#endif #endif

1072
popnhax/custom_categs.cc Normal file

File diff suppressed because it is too large Load Diff

10
popnhax/custom_categs.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef __CUSTOM_CATEGS_H__
#define __CUSTOM_CATEGS_H__
#include <stdint.h>
#include "popnhax/config.h"
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

@ -5,6 +5,7 @@
#include "imports/avs.h" #include "imports/avs.h"
#include "util/patch.h" #include "util/patch.h"
#include "util/log.h"
#include "xmlhelper.h" #include "xmlhelper.h"
#include "tableinfo.h" #include "tableinfo.h"
@ -66,7 +67,7 @@ uint32_t add_chart(uint32_t cur_idx, uint8_t *folder, uint8_t *filename, int32_t
uint32_t file_type, uint16_t used_keys, bool override_idx); uint32_t file_type, uint16_t used_keys, bool override_idx);
void parse_charadb(const char *input_filename, const char *target); 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; std::map<uint32_t, int8_t> chart_type_overrides;
@ -818,7 +819,7 @@ void parse_charadb(const char *input_filename, const char *target) {
free(config_xml); free(config_xml);
} }
void parse_musicdb(const char *input_filename, const char *target) { void parse_musicdb(const char *input_filename, const char *target, struct popnhax_config *config) {
if (!file_exists(input_filename)) { if (!file_exists(input_filename)) {
printf("Couldn't find %s, skipping...\n", input_filename); printf("Couldn't find %s, skipping...\n", input_filename);
return; return;
@ -915,6 +916,18 @@ void parse_musicdb(const char *input_filename, const char *target) {
} }
} }
//force loading background for unilab
//m->mask |= 0x100;
if ( config->custom_categ
&& config->custom_exclude_from_version
&& idx >= config->custom_categ_min_songid
&& (config->custom_categ_max_songid == 0 || idx <= config->custom_categ_max_songid) )
{
m->cs_version = 0;
m->folder = 0;
}
if ((prop_chart = property_search(config_xml, prop, "charts/chart"))) { if ((prop_chart = property_search(config_xml, prop, "charts/chart"))) {
for (; prop_chart != NULL; prop_chart = property_node_traversal( for (; prop_chart != NULL; prop_chart = property_node_traversal(
prop_chart, TRAVERSE_NEXT_SEARCH_RESULT)) { prop_chart, TRAVERSE_NEXT_SEARCH_RESULT)) {
@ -992,7 +1005,7 @@ void parse_musicdb(const char *input_filename, const char *target) {
free(config_xml); free(config_xml);
} }
void load_databases(const char *target_datecode) { void load_databases(const char *target_datecode, struct popnhax_config *config) {
SearchFile s; SearchFile s;
printf("XML db files search...\n"); printf("XML db files search...\n");
@ -1013,11 +1026,11 @@ void load_databases(const char *target_datecode) {
if ( strstr(result[i].c_str(), "musicdb") == NULL ) if ( strstr(result[i].c_str(), "musicdb") == NULL )
continue; continue;
printf("(musicdb) Loading %s...\n", result[i].c_str()); 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 *target_datecode,
char *base_data, char *base_data,
@ -1036,6 +1049,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, uint64_t chara_size, uint64_t *new_chara_size, char *orig_chara_data,
uint8_t **new_chara_table) { 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) { if (style_size > fontstyle_table_size) {
fontstyle_table_size = style_size; fontstyle_table_size = style_size;
} }
@ -1181,7 +1198,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); 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) // Add some filler charts to fix some bugs (hack)
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {

View File

@ -2,10 +2,11 @@
#define __LOADER_H__ #define __LOADER_H__
#include <stdint.h> #include <stdint.h>
#include "popnhax/config.h"
int8_t get_chart_type_override(uint8_t *, uint32_t, uint32_t); 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, 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 *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, 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); } while (valid_sjis);
if (dump_dict) 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,"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); fprintf(g_dict_applied_fp,";%s;%s\n",(char*)find,(char*)replace);
} }
/* safety check replace is not too big */ /* safety check replace is not too big */
uint8_t free_size = find_size-1; uint8_t free_size = find_size-1;
do do
@ -83,41 +83,41 @@ bool patch_sjis(const char *dllFilename, const char *find, uint8_t find_size, in
#define RELOC_HIGHLOW 0x3 #define RELOC_HIGHLOW 0x3
static void perform_reloc(char *data, int32_t delta, uint32_t ext_base, uint32_t ext_delta) 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_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_DATA_DIRECTORY datadir = &headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
PIMAGE_BASE_RELOCATION reloc = (PIMAGE_BASE_RELOCATION)(data + datadir->VirtualAddress); PIMAGE_BASE_RELOCATION reloc = (PIMAGE_BASE_RELOCATION)(data + datadir->VirtualAddress);
while(reloc->VirtualAddress != 0) while(reloc->VirtualAddress != 0)
{ {
if (reloc->SizeOfBlock >= sizeof(IMAGE_BASE_RELOCATION)) if (reloc->SizeOfBlock >= sizeof(IMAGE_BASE_RELOCATION))
{ {
DWORD relocDescNb = (reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD); DWORD relocDescNb = (reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
LPWORD relocDescList = (LPWORD)((LPBYTE)reloc + sizeof(IMAGE_BASE_RELOCATION)); LPWORD relocDescList = (LPWORD)((LPBYTE)reloc + sizeof(IMAGE_BASE_RELOCATION));
for (DWORD i = 0; i < relocDescNb; i++) for (DWORD i = 0; i < relocDescNb; i++)
{ {
if ( ((relocDescList[i])>>12) == RELOC_HIGHLOW ) if ( ((relocDescList[i])>>12) == RELOC_HIGHLOW )
{ {
DWORD_PTR *p = (DWORD_PTR *)( data + (reloc->VirtualAddress + ((relocDescList[i])&0x0FFF)) ); DWORD_PTR *p = (DWORD_PTR *)( data + (reloc->VirtualAddress + ((relocDescList[i])&0x0FFF)) );
/* Change the offset to adapt to injected module base address */ /* Change the offset to adapt to injected module base address */
DWORD old_prot; DWORD old_prot;
VirtualProtect((LPVOID)p, 4, PAGE_EXECUTE_READWRITE, &old_prot); VirtualProtect((LPVOID)p, 4, PAGE_EXECUTE_READWRITE, &old_prot);
*p += delta; *p += delta;
if ( ext_base > 0 && *p >= ((int64_t)data+ext_base) ) if ( ext_base > 0 && *p >= ((int64_t)data+ext_base) )
{ {
//fprintf(stderr,"reloc rva %lx to ext ", *p); //fprintf(stderr,"reloc rva %lx to ext ", *p);
*p += ext_delta; *p += ext_delta;
//fprintf(stderr," %lx\n", *p); //fprintf(stderr," %lx\n", *p);
} }
VirtualProtect((LPVOID)p, 4, old_prot, &old_prot); VirtualProtect((LPVOID)p, 4, old_prot, &old_prot);
} }
} }
} }
/* Set reloc pointer to the next relocation block */ /* Set reloc pointer to the next relocation block */
reloc = (PIMAGE_BASE_RELOCATION)((LPBYTE)reloc + reloc->SizeOfBlock); reloc = (PIMAGE_BASE_RELOCATION)((LPBYTE)reloc + reloc->SizeOfBlock);
} }
} }
#define BYTE3_TO_UINT(bp) \ #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) 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 {\ #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 )\ if ( fread(_ips_read_dest, 1, _ips_read_size, ips_fp) != _ips_read_size )\
{\ {\
LOG("CANNOT READ %s\n", _ips_read_name);\ LOG("CANNOT READ %s\n", _ips_read_name);\
return false;\ return false;\
}\ }\
} while (0) } while (0)
DWORD dllSize = 0; DWORD dllSize = 0;
char *data = getDllData(dllFilename, &dllSize); char *data = getDllData(dllFilename, &dllSize);
char dict_filepath[64]; char dict_filepath[64];
sprintf(dict_filepath, "%s%s%s", "data_mods\\", foldername, "\\popn22.ips"); 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) if (ips_fp == NULL)
{ {
return false; return false;
} }
LOG("popnhax: translation: popn22.ips found\n"); LOG("popnhax: translation: popn22.ips found\n");
/* check .ips header */ /* check .ips header */
uint8_t buffer[8]; uint8_t buffer[8];
if (fread(&buffer, 1, 5, ips_fp) != 5) if (fread(&buffer, 1, 5, ips_fp) != 5)
return false; return false;
if (memcmp(buffer, "PATCH", 5) != 0)
{
LOG("popnhax: translation: invalid .ips header\n");
return false;
}
if (dump_dll) if (memcmp(buffer, "PATCH", 5) != 0)
{ {
LOG("popnhax: translation debug: dump dll before patch\n"); LOG("popnhax: translation: invalid .ips header\n");
FILE* dllrtp = fopen("dllruntime.dll", "wb"); return false;
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 */ if (dump_dll)
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); LOG("popnhax: translation debug: dump dll before patch\n");
perform_reloc(data, -1*reloc_delta, 0, 0); FILE* dllrtp = fopen("dllruntime.dll", "wb");
fwrite(data, 1, dllSize, dllrtp);
fclose(dllrtp);
LOG("popnhax: translation debug: dllruntime.dll generated\n");
}
uint32_t trans_base = 0; /* eclale patch adds new section header which I'm relocating */ /* undo all relocation so you can apply ips patch with correct values, we'll reapply them later */
uint32_t trans_base_offset = 0; PIMAGE_NT_HEADERS headers = (PIMAGE_NT_HEADERS)((int64_t)data + ((PIMAGE_DOS_HEADER)data)->e_lfanew);
uint32_t trans_rebase = 0; DWORD_PTR reloc_delta = (DWORD_PTR)((int64_t)data - headers->OptionalHeader.ImageBase);
perform_reloc(data, -1*reloc_delta, 0, 0);
uint32_t offset = 0; uint32_t trans_base = 0; /* eclale patch adds new section header which I'm relocating */
uint16_t size = 0; uint32_t trans_base_offset = 0;
uint16_t count = 0; uint32_t trans_rebase = 0;
uint8_t replace[MAX_REPLACE_SIZE] = {0};
while ( fread(&offset, 1, 3, ips_fp) == 3 && offset != IPS_EOF )
{
bool skip = false;
/* need to convert offset to rva before applying patch since the dll is already in memory */
uint8_t *bp = (uint8_t *)&offset;
offset = BYTE3_TO_UINT(bp);
uint32_t rva;
if (offset < 0x400) uint32_t offset = 0;
{ uint16_t size = 0;
rva = offset; uint16_t count = 0;
} uint8_t replace[MAX_REPLACE_SIZE] = {0};
else if ( !offset_to_rva(dllFilename, offset, &rva) )
{
LOG("Invalid offset %x conversion. Skip this patch\n", offset);
skip = true;
/* still need to go through the loop to increase read pointer accordingly */
}
IPS_READ(&size, 2, "SIZE");
bp = (uint8_t *)&size;
size = BYTE2_TO_UINT(bp);
++count;
//LOG("%03d: offset %x converted to %x\nsize %d\n", count, offset, rva,size);
if ( size == 0 )
{
uint8_t value;
IPS_READ(&size, 2, "RLE SIZE");
bp = (uint8_t *)&size;
size = BYTE2_TO_UINT(bp);
IPS_READ(&value, 1, "RLE VALUE"); while ( fread(&offset, 1, 3, ips_fp) == 3 && offset != IPS_EOF )
{
//LOG("RLE PATCH! size %d value %d\n", size, value); bool skip = false;
//fprintf(stderr, "rle value %d (%d bytes)\n",value, size);
if ( size > MAX_REPLACE_SIZE )
{
LOG("RLE replacement too big.\n");
return false;
}
memset(replace, value, size);
}
else
{
if ( size > MAX_REPLACE_SIZE )
{
uint16_t remaining = size;
uint32_t chunk_rva = rva;
do {
/* patch in multiple chunks */
LOG("multipart patch: rva %x, %d remaining\n", chunk_rva, remaining);
IPS_READ(&replace, MAX_REPLACE_SIZE, "DATA");
if ( !skip )
patch_memory((int64_t)data+chunk_rva, (char *)replace, MAX_REPLACE_SIZE);
remaining -= MAX_REPLACE_SIZE;
chunk_rva += MAX_REPLACE_SIZE;
} while (remaining > MAX_REPLACE_SIZE);
size = remaining;
rva = chunk_rva;
}
IPS_READ(&replace, size, "DATA");
}
/* eclale woes */
if ( trans_base == 0 && rva < 0x400 )
{
if (memcmp(replace, ".trans", 6) == 0)
{
trans_base = *(uint32_t*)(replace+0x0C);
trans_base_offset = *(uint32_t*)(replace+0x14);
//LOG("found .trans section at offset %x rva %x\n", trans_base_offset, trans_base);
}
}
if ( trans_base_offset != 0 && offset >= trans_base_offset ) /* need to convert offset to rva before applying patch since the dll is already in memory */
{ uint8_t *bp = (uint8_t *)&offset;
/* patching into new section */ offset = BYTE3_TO_UINT(bp);
if ( trans_rebase == 0 ) uint32_t rva;
{
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 */ if (offset < 0x400)
//memcpy(replace, &trans_rebase, 4); {
//patch_memory((int64_t)data+0x02d4, (char *)replace, 4); rva = offset;
//LOG( "patched .trans section address to %02x %02x %02x %02x\n", replace[0], replace[1], replace[2], replace[3]); }
} else if ( !offset_to_rva(dllFilename, offset, &rva) )
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("Invalid offset %x conversion. Skip this patch\n", offset);
//LOG( "offset %x relocated to rva %x.", offset, rva); skip = true;
} /* still need to go through the loop to increase read pointer accordingly */
}
if ( !skip )
{
patch_memory((int64_t)data+rva, (char *)replace, size);
}
}
/* redo all relocation now the dll is patched */ IPS_READ(&size, 2, "SIZE");
perform_reloc(data, reloc_delta, trans_base, trans_rebase-trans_base); bp = (uint8_t *)&size;
size = BYTE2_TO_UINT(bp);
++count;
LOG("popnhax: translation: IPS patch applied.\n"); //LOG("%03d: offset %x converted to %x\nsize %d\n", count, offset, rva,size);
if ( size == 0 )
if (dump_dll) {
{ uint8_t value;
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); IPS_READ(&size, 2, "RLE SIZE");
return true; bp = (uint8_t *)&size;
size = BYTE2_TO_UINT(bp);
IPS_READ(&value, 1, "RLE VALUE");
//LOG("RLE PATCH! size %d value %d\n", size, value);
//fprintf(stderr, "rle value %d (%d bytes)\n",value, size);
if ( size > MAX_REPLACE_SIZE )
{
LOG("RLE replacement too big.\n");
return false;
}
memset(replace, value, size);
}
else
{
if ( size > MAX_REPLACE_SIZE )
{
uint16_t remaining = size;
uint32_t chunk_rva = rva;
do {
/* patch in multiple chunks */
LOG("multipart patch: rva %x, %d remaining\n", chunk_rva, remaining);
IPS_READ(&replace, MAX_REPLACE_SIZE, "DATA");
if ( !skip )
patch_memory((int64_t)data+chunk_rva, (char *)replace, MAX_REPLACE_SIZE);
remaining -= MAX_REPLACE_SIZE;
chunk_rva += MAX_REPLACE_SIZE;
} while (remaining > MAX_REPLACE_SIZE);
size = remaining;
rva = chunk_rva;
}
IPS_READ(&replace, size, "DATA");
}
/* eclale woes */
if ( trans_base == 0 && rva < 0x400 )
{
if (memcmp(replace, ".trans", 6) == 0)
{
trans_base = *(uint32_t*)(replace+0x0C);
trans_base_offset = *(uint32_t*)(replace+0x14);
//LOG("found .trans section at offset %x rva %x\n", trans_base_offset, trans_base);
}
}
if ( trans_base_offset != 0 && offset >= trans_base_offset )
{
/* patching into new section */
if ( trans_rebase == 0 )
{
HANDLE hProc = GetCurrentProcess();
LPVOID myAlloc = VirtualAllocEx(hProc, NULL, 16384, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (myAlloc == NULL)
{
LOG("Failed to allocate memory in target process. Error: 0x%lX\n", GetLastError());
exit(0);
}
trans_rebase = (uint32_t)myAlloc - (uint32_t)data;
//LOG( "virtualalloc worked; address %x (%p)\n", trans_rebase, myAlloc);
/* seems useless */
//memcpy(replace, &trans_rebase, 4);
//patch_memory((int64_t)data+0x02d4, (char *)replace, 4);
//LOG( "patched .trans section address to %02x %02x %02x %02x\n", replace[0], replace[1], replace[2], replace[3]);
}
rva = (offset - trans_base_offset) + trans_rebase;
//LOG( "off %x - base %x + rebase %x = %x\n\n", offset, trans_base_offset, trans_rebase, rva);
//LOG( "offset %x relocated to rva %x.", offset, rva);
}
if ( !skip )
{
patch_memory((int64_t)data+rva, (char *)replace, size);
}
}
/* redo all relocation now the dll is patched */
perform_reloc(data, reloc_delta, trans_base, trans_rebase-trans_base);
LOG("popnhax: translation: IPS patch applied.\n");
if (dump_dll)
{
LOG("popnhax: translation debug: dump dll after patch\n");
FILE* dllrtp = fopen("dllruntime_patched.dll", "wb");
fwrite(data, 1, dllSize, dllrtp);
fclose(dllrtp);
}
fclose(ips_fp);
return true;
#undef IPS_READ #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 word_count = 0;
uint8_t orig_size = 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"); 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) if (dict_fp == NULL)
{ {
return false; return false;
} }
LOG("popnhax: translation: popn22.dict file found\n"); LOG("popnhax: translation: popn22.dict file found\n");
#define STATE_WAITING 0 #define STATE_WAITING 0
#define STATE_ORIGINAL 1 #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 patch_translate(const char *dllFilename, const char *folder, bool debug)
{ {
bool ips_done = false; bool ips_done = false;
bool dict_done = false; bool dict_done = false;
ips_done = patch_translation_ips(dllFilename, folder, debug); ips_done = patch_translation_ips(dllFilename, folder, debug);
dict_done = patch_translation_dict(dllFilename, folder, debug); dict_done = patch_translation_dict(dllFilename, folder, debug);
return ips_done || dict_done; return ips_done || dict_done;
} }

View File

@ -72,4 +72,9 @@ 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 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, 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