mirror of
https://github.com/CrazyRedMachine/popnhax.git
synced 2024-11-27 15:30:50 +01:00
translation support (.ips)
This commit is contained in:
parent
3593ccc80c
commit
273bb4b69a
@ -12,5 +12,6 @@ libs_popnhax := \
|
||||
|
||||
srcpp_popnhax := \
|
||||
dllmain.cc \
|
||||
loader.cc \
|
||||
SearchFile.cc
|
||||
loader.cc \
|
||||
SearchFile.cc \
|
||||
translation.cc
|
||||
|
@ -35,7 +35,7 @@ struct popnhax_config {
|
||||
int8_t beam_brightness;
|
||||
bool fps_uncap;
|
||||
bool disable_translation;
|
||||
bool dump_dict;
|
||||
bool translation_debug;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -18,23 +18,22 @@
|
||||
#include "minhook/include/MinHook.h"
|
||||
|
||||
#include "popnhax/config.h"
|
||||
#include "util/log.h"
|
||||
#include "util/patch.h"
|
||||
#include "util/xmlprop.hpp"
|
||||
#include "xmlhelper.h"
|
||||
#include "translation.h"
|
||||
|
||||
#include "tableinfo.h"
|
||||
#include "loader.h"
|
||||
|
||||
|
||||
#include "SearchFile.h"
|
||||
|
||||
const char *g_game_dll_fn = NULL;
|
||||
const char *g_config_fn = NULL;
|
||||
FILE *g_log_fp = NULL;
|
||||
FILE *g_log_fp = NULL;
|
||||
|
||||
#define LOG(...) do { \
|
||||
fprintf(g_log_fp, __VA_ARGS__); \
|
||||
fflush(g_log_fp);\
|
||||
} while (0)
|
||||
|
||||
#define DEBUG 0
|
||||
|
||||
@ -147,8 +146,8 @@ PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, fps_uncap,
|
||||
"/popnhax/fps_uncap")
|
||||
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, disable_translation,
|
||||
"/popnhax/disable_translation")
|
||||
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, dump_dict,
|
||||
"/popnhax/dump_dict")
|
||||
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, translation_debug,
|
||||
"/popnhax/translation_debug")
|
||||
PSMAP_END
|
||||
|
||||
enum BufferIndexes {
|
||||
@ -208,7 +207,6 @@ void asm_patch_datecode() {
|
||||
/* retrieve destination address when it checks for "soft/ext", then wait next iteration to overwrite */
|
||||
uint8_t g_libavs_datecode_patch_state = 0;
|
||||
char *datecode_property_ptr;
|
||||
uint32_t eaxsave;
|
||||
void (*real_asm_patch_datecode_libavs)();
|
||||
void asm_patch_datecode_libavs() {
|
||||
if (g_libavs_datecode_patch_state == 2)
|
||||
@ -574,261 +572,6 @@ asm(
|
||||
" jmp [_real_check_music_idx_usaneko]\n"
|
||||
);
|
||||
|
||||
void patch_string(const char *input_string, const char *new_string) {
|
||||
DWORD dllSize = 0;
|
||||
char *data = getDllData(g_game_dll_fn, &dllSize);
|
||||
|
||||
while (1) {
|
||||
fuzzy_search_task task;
|
||||
|
||||
FUZZY_START(task, 1)
|
||||
FUZZY_CODE(task, 0, input_string, strlen(input_string))
|
||||
|
||||
int64_t pattern_offset = find_block(data, dllSize, &task, 0);
|
||||
if (pattern_offset == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint64_t patch_addr = (int64_t)data + pattern_offset;
|
||||
char *new_string_buff = (char*)calloc(strlen(new_string) + 1, sizeof(char));
|
||||
memcpy(new_string_buff, new_string, strlen(new_string));
|
||||
patch_memory(patch_addr, new_string_buff, strlen(new_string) + 1);
|
||||
free(new_string_buff);
|
||||
}
|
||||
}
|
||||
|
||||
bool patch_hex(const char *find, uint8_t find_size, int64_t shift, const char *replace, uint8_t replace_size) {
|
||||
DWORD dllSize = 0;
|
||||
char *data = getDllData(g_game_dll_fn, &dllSize);
|
||||
|
||||
fuzzy_search_task task;
|
||||
|
||||
FUZZY_START(task, 1)
|
||||
FUZZY_CODE(task, 0, find, find_size)
|
||||
|
||||
int64_t pattern_offset = find_block(data, dllSize, &task, 0);
|
||||
if (pattern_offset == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if DEBUG == 1
|
||||
LOG("BEFORE PATCH :\n");
|
||||
uint8_t *offset = (uint8_t *) ((int64_t)data + pattern_offset + shift - 5);
|
||||
for (int i=0; i<32; i++)
|
||||
{
|
||||
LOG("%02x ", *offset);
|
||||
offset++;
|
||||
if (i == 15)
|
||||
LOG("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
uint64_t patch_addr = (int64_t)data + pattern_offset + shift;
|
||||
patch_memory(patch_addr, (char *)replace, replace_size);
|
||||
|
||||
#if DEBUG == 1
|
||||
LOG("\nAFTER PATCH :\n");
|
||||
offset = (uint8_t *) ((int64_t)data + pattern_offset + shift - 5);
|
||||
for (int i=0; i<32; i++)
|
||||
{
|
||||
LOG("%02x ", *offset);
|
||||
offset++;
|
||||
if (i == 15)
|
||||
LOG("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
FILE *dictfile;
|
||||
|
||||
bool patch_sjis(const uint8_t *find, uint8_t find_size, int64_t *offset, uint8_t *replace, uint8_t replace_size) {
|
||||
static DWORD dllSize = 0;
|
||||
static char *data = getDllData(g_game_dll_fn, &dllSize);
|
||||
static uint8_t first_offset = 0;
|
||||
|
||||
int64_t offset_orig = *offset;
|
||||
uint64_t patch_addr;
|
||||
bool valid_sjis = false;
|
||||
do {
|
||||
fuzzy_search_task task;
|
||||
|
||||
FUZZY_START(task, 1)
|
||||
FUZZY_CODE(task, 0, find, find_size)
|
||||
|
||||
*offset = find_block(data, dllSize-*offset, &task, *offset);
|
||||
if (*offset == -1) {
|
||||
*offset = offset_orig;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !first_offset )
|
||||
{
|
||||
if (config.dump_dict)
|
||||
{
|
||||
LOG("popnhax: dump_dict: dump applied patches in dict_applied.txt\n");
|
||||
dictfile = fopen("dict_applied.txt","wb");
|
||||
}
|
||||
/* limit search to a 0x100000-wide zone starting from first string found to speedup the process
|
||||
* make sure to put the first string first (usually 種類順)
|
||||
*/
|
||||
dllSize = *offset + 0x100000;
|
||||
first_offset = 1;
|
||||
}
|
||||
|
||||
patch_addr = (int64_t)data + *offset;
|
||||
|
||||
/* filter out partial matches (check that there isn't a valid sjis char before our match) */
|
||||
uint8_t byte1 = *(uint8_t*)(patch_addr-2);
|
||||
uint8_t byte2 = *(uint8_t*)(patch_addr-1);
|
||||
bool valid_first = ((0x81 <= byte1) && (byte1 <= 0x9F)) || ((0xE0 <= byte1) && (byte1 <= 0xFC));
|
||||
bool valid_secnd = ((0x40 <= byte2) && (byte2 <= 0x9E)) || ((0x9F <= byte2) && (byte2 <= 0xFC));
|
||||
valid_sjis = valid_first && valid_secnd;
|
||||
|
||||
if (valid_sjis)
|
||||
{
|
||||
printf("Partial match at offset 0x%x, retry...\n",(uint32_t)*offset);
|
||||
*offset += find_size;
|
||||
}
|
||||
} while (valid_sjis);
|
||||
|
||||
if (config.dump_dict)
|
||||
fprintf(dictfile,"0x%x;%s;%s\n",(uint32_t)*offset,(char*)find,(char*)replace);
|
||||
|
||||
/* safety check replace is not too big */
|
||||
uint8_t free_size = find_size-1;
|
||||
do
|
||||
{
|
||||
free_size++;
|
||||
}
|
||||
while ( *(uint8_t *)(patch_addr+free_size) == 0 );
|
||||
|
||||
if ((free_size-1) < replace_size)
|
||||
{
|
||||
LOG("WARNING: translation %s is too big, truncating to ",(char *)replace);
|
||||
replace_size = free_size-1;
|
||||
replace[replace_size-1] = '\0';
|
||||
LOG("%s\n",(char *)replace);
|
||||
}
|
||||
|
||||
patch_memory(patch_addr, (char *)replace, replace_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static FILE* _translation_open_dict(char *foldername)
|
||||
{
|
||||
char dict_filepath[64];
|
||||
sprintf(dict_filepath, "%s%s%s", "data_mods\\", foldername, "\\popn22.dict");
|
||||
FILE *file = fopen(dict_filepath, "rb");
|
||||
return file;
|
||||
}
|
||||
|
||||
bool patch_translation(FILE* dict_fp)
|
||||
{
|
||||
uint8_t original[128];
|
||||
uint8_t translation[128];
|
||||
uint8_t buffer;
|
||||
int64_t curr_offset = 0;
|
||||
uint8_t word_count = 0;
|
||||
uint8_t orig_size = 0;
|
||||
|
||||
if (dict_fp == NULL)
|
||||
return false;
|
||||
|
||||
#define STATE_WAITING 0
|
||||
#define STATE_ORIGINAL 1
|
||||
#define STATE_TRANSLATION 2
|
||||
uint16_t err_count = 0;
|
||||
uint8_t state = STATE_WAITING;
|
||||
uint8_t arr_idx = 0;
|
||||
while (fread(&buffer, 1, 1, dict_fp) == 1)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case STATE_WAITING:
|
||||
if (buffer == ';')
|
||||
{
|
||||
state = STATE_ORIGINAL;
|
||||
arr_idx = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("Unexpected char %c\n", buffer);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case STATE_ORIGINAL:
|
||||
if (buffer == ';')
|
||||
{
|
||||
original[arr_idx++] = '\0';
|
||||
state = STATE_TRANSLATION;
|
||||
orig_size = arr_idx;
|
||||
arr_idx = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
original[arr_idx++] = buffer;
|
||||
}
|
||||
break;
|
||||
case STATE_TRANSLATION:
|
||||
if (buffer == ';')
|
||||
{
|
||||
/* end of word, let's patch! */
|
||||
translation[arr_idx-1] = '\0'; /* strip last \n */
|
||||
while ( arr_idx < orig_size )
|
||||
{
|
||||
translation[arr_idx++] = '\0'; /* fill with null when translation is shorter */
|
||||
}
|
||||
printf("%d: %s -> %s\n",++word_count,(char *)original,(char *)translation);
|
||||
|
||||
/* patch all occurrences */
|
||||
/*curr_offset = 0;
|
||||
uint8_t count = 0;
|
||||
while (patch_sjis(original, orig_size, &curr_offset, translation, arr_idx-1))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
LOG("%d occurrences found\n",count);
|
||||
*/
|
||||
if (!patch_sjis(original, orig_size, &curr_offset, translation, arr_idx-1))
|
||||
{
|
||||
printf("Warning: string %s (%s) not found in order, trying again.\n", (char *)original, (char *)translation);
|
||||
curr_offset = 0;
|
||||
if (!patch_sjis(original, orig_size, &curr_offset, translation, arr_idx-1))
|
||||
{
|
||||
LOG("Warning: string %s not found, skipping.\n", (char *)original);
|
||||
err_count++;
|
||||
}
|
||||
}
|
||||
|
||||
state = STATE_ORIGINAL;
|
||||
arr_idx = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
translation[arr_idx++] = buffer;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
LOG("popnhax: translation: patched %u strings", word_count - err_count);
|
||||
if (err_count)
|
||||
LOG(" (%u skipped strings)", err_count);
|
||||
LOG("\n");
|
||||
|
||||
if (config.dump_dict)
|
||||
fclose(dictfile);
|
||||
return true;
|
||||
#undef STATE_WAITING
|
||||
#undef STATE_ORIGINAL
|
||||
#undef STATE_TRANSLATION
|
||||
}
|
||||
|
||||
char *parse_patchdb(const char *input_filename, char *base_data) {
|
||||
const char *folder = "data_mods\\";
|
||||
char *input_filepath = (char*)calloc(strlen(input_filename) + strlen(folder) + 1, sizeof(char));
|
||||
@ -1368,7 +1111,7 @@ static bool patch_database(uint8_t force_unlocks) {
|
||||
}
|
||||
|
||||
static bool patch_audio_source_fix() {
|
||||
if (!patch_hex("\x85\xC0\x75\x96\x8D\x70\x7F\xE8\xF8\x2B\x00\x00", 12, 0, "\x90\x90\x90\x90", 4))
|
||||
if (!find_and_patch_hex(g_game_dll_fn, "\x85\xC0\x75\x96\x8D\x70\x7F\xE8\xF8\x2B\x00\x00", 12, 0, "\x90\x90\x90\x90", 4))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -1417,7 +1160,7 @@ static bool patch_unset_volume() {
|
||||
}
|
||||
|
||||
static bool patch_event_mode() {
|
||||
if (!patch_hex("\x8B\x00\xC3\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC"
|
||||
if (!find_and_patch_hex(g_game_dll_fn, "\x8B\x00\xC3\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC"
|
||||
"\xCC\xCC\xC7", 17, 0, "\x31\xC0\x40\xC3", 4))
|
||||
{
|
||||
return false;
|
||||
@ -1467,7 +1210,7 @@ static bool patch_remove_timer() {
|
||||
}
|
||||
|
||||
static bool patch_freeze_timer() {
|
||||
if (!patch_hex("\xC7\x45\x38\x09\x00\x00\x00", 7, 0, "\x90\x90\x90\x90\x90\x90\x90", 7))
|
||||
if (!find_and_patch_hex(g_game_dll_fn, "\xC7\x45\x38\x09\x00\x00\x00", 7, 0, "\x90\x90\x90\x90\x90\x90\x90", 7))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -1546,7 +1289,7 @@ static bool patch_skip_tutorials() {
|
||||
}
|
||||
|
||||
bool force_unlock_deco_parts() {
|
||||
if (!patch_hex("\x83\xC4\x04\x83\x38\x00\x75\x22", 8, 6, "\x90\x90", 2))
|
||||
if (!find_and_patch_hex(g_game_dll_fn, "\x83\xC4\x04\x83\x38\x00\x75\x22", 8, 6, "\x90\x90", 2))
|
||||
{
|
||||
LOG("popnhax: couldn't unlock deco parts\n");
|
||||
return false;
|
||||
@ -1686,7 +1429,7 @@ static bool patch_unlocks_offline() {
|
||||
}
|
||||
|
||||
{
|
||||
if (!patch_hex("\xA9\x06\x00\x00\x68\x74", 6, 5, "\xEB", 1))
|
||||
if (!find_and_patch_hex(g_game_dll_fn, "\xA9\x06\x00\x00\x68\x74", 6, 5, "\xEB", 1))
|
||||
{
|
||||
LOG("Couldn't find second song unlock\n");
|
||||
return false;
|
||||
@ -1696,8 +1439,8 @@ static bool patch_unlocks_offline() {
|
||||
LOG("popnhax: songs unlocked for offline\n");
|
||||
|
||||
{
|
||||
if (!patch_hex("\xA9\x10\x01\x00\x00\x74", 6, 5, "\xEB", 1) /* unilab */
|
||||
&& !patch_hex("\xA9\x50\x01\x00\x00\x74", 6, 5, "\xEB", 1))
|
||||
if (!find_and_patch_hex(g_game_dll_fn, "\xA9\x10\x01\x00\x00\x74", 6, 5, "\xEB", 1) /* unilab */
|
||||
&& !find_and_patch_hex(g_game_dll_fn, "\xA9\x50\x01\x00\x00\x74", 6, 5, "\xEB", 1))
|
||||
{
|
||||
LOG("Couldn't find character unlock\n");
|
||||
return false;
|
||||
@ -2477,13 +2220,13 @@ static bool patch_beam_brightness(uint8_t value) {
|
||||
uint32_t beam_brightness_addr;
|
||||
get_addr_beam_brightness(&beam_brightness_addr);
|
||||
|
||||
if (!patch_hex("\xB8\x64\x00\x00\x00\xD9", 6, 0x3A, as_hex, 4))
|
||||
if (!find_and_patch_hex(g_game_dll_fn, "\xB8\x64\x00\x00\x00\xD9", 6, 0x3A, as_hex, 4))
|
||||
{
|
||||
LOG("popnhax: base offset: cannot patch HD beam brightness\n");
|
||||
res = false;
|
||||
}
|
||||
|
||||
if (!patch_hex("\xB8\x64\x00\x00\x00\xD9", 6, 1, as_hex, 4))
|
||||
if (!find_and_patch_hex(g_game_dll_fn, "\xB8\x64\x00\x00\x00\xD9", 6, 1, as_hex, 4))
|
||||
{
|
||||
LOG("popnhax: base offset: cannot patch SD beam brightness\n");
|
||||
res = false;
|
||||
@ -2684,13 +2427,13 @@ static bool patch_base_offset(int32_t value) {
|
||||
uint32_t hd_timing_addr;
|
||||
get_addr_hd_timing(&hd_timing_addr);
|
||||
|
||||
if (!patch_hex("\xB8\xC4\xFF\xFF\xFF", 5, 1, as_hex, 4))
|
||||
if (!find_and_patch_hex(g_game_dll_fn, "\xB8\xC4\xFF\xFF\xFF", 5, 1, as_hex, 4))
|
||||
{
|
||||
LOG("popnhax: base offset: cannot patch base SD timing\n");
|
||||
res = false;
|
||||
}
|
||||
|
||||
if (!patch_hex("\xB8\xB4\xFF\xFF\xFF", 5, 1, as_hex, 4))
|
||||
if (!find_and_patch_hex(g_game_dll_fn, "\xB8\xB4\xFF\xFF\xFF", 5, 1, as_hex, 4))
|
||||
{
|
||||
LOG("popnhax: base offset: cannot patch base HD timing\n");
|
||||
res = false;
|
||||
@ -2725,27 +2468,27 @@ static bool patch_hd_resolution(uint8_t mode) {
|
||||
}
|
||||
|
||||
/* set window to 1360*768 */
|
||||
if (!patch_hex("\x0F\xB6\xC0\xF7\xD8\x1B\xC0\x25\xD0\x02", 10, -5, "\xB8\x50\x05\x00\x00\xC3\xCC\xCC\xCC", 9)
|
||||
&& !patch_hex("\x84\xc0\x74\x14\x0f\xb6\x05", 7, -5, "\xB8\x50\x05\x00\x00\xC3\xCC\xCC\xCC", 9))
|
||||
if (!find_and_patch_hex(g_game_dll_fn, "\x0F\xB6\xC0\xF7\xD8\x1B\xC0\x25\xD0\x02", 10, -5, "\xB8\x50\x05\x00\x00\xC3\xCC\xCC\xCC", 9)
|
||||
&& !find_and_patch_hex(g_game_dll_fn, "\x84\xc0\x74\x14\x0f\xb6\x05", 7, -5, "\xB8\x50\x05\x00\x00\xC3\xCC\xCC\xCC", 9))
|
||||
{
|
||||
LOG("popnhax: HD resolution: cannot find screen width function\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!patch_hex("\x0f\xb6\xc0\xf7\xd8\x1b\xc0\x25\x20\x01", 10, -5, "\xB8\x00\x03\x00\x00\xC3\xCC\xCC\xCC", 9))
|
||||
if (!find_and_patch_hex(g_game_dll_fn, "\x0f\xb6\xc0\xf7\xd8\x1b\xc0\x25\x20\x01", 10, -5, "\xB8\x00\x03\x00\x00\xC3\xCC\xCC\xCC", 9))
|
||||
LOG("popnhax: HD resolution: cannot find screen height function\n");
|
||||
|
||||
if (!patch_hex("\x8B\x54\x24\x20\x53\x51\x52\xEB\x0C", 9, -6, "\x90\x90", 2))
|
||||
if (!find_and_patch_hex(g_game_dll_fn, "\x8B\x54\x24\x20\x53\x51\x52\xEB\x0C", 9, -6, "\x90\x90", 2))
|
||||
LOG("popnhax: HD resolution: cannot find screen aspect ratio function\n");
|
||||
|
||||
|
||||
if ( mode == 1 )
|
||||
{
|
||||
/* move texts (by forcing HD behavior) */
|
||||
if (!patch_hex("\x1B\xC9\x83\xE1\x95\x81\xC1\x86", 8, -5, "\xB9\xFF\xFF\xFF\xFF\x90\x90", 7))
|
||||
if (!find_and_patch_hex(g_game_dll_fn, "\x1B\xC9\x83\xE1\x95\x81\xC1\x86", 8, -5, "\xB9\xFF\xFF\xFF\xFF\x90\x90", 7))
|
||||
LOG("popnhax: HD resolution: cannot move gamecode position\n");
|
||||
|
||||
if (!patch_hex("\x6a\x01\x6a\x00\x50\x8b\x06\x33\xff", 9, -7, "\xEB", 1))
|
||||
if (!find_and_patch_hex(g_game_dll_fn, "\x6a\x01\x6a\x00\x50\x8b\x06\x33\xff", 9, -7, "\xEB", 1))
|
||||
LOG("popnhax: HD resolution: cannot move credit/network position\n");
|
||||
}
|
||||
|
||||
@ -2755,7 +2498,7 @@ static bool patch_hd_resolution(uint8_t mode) {
|
||||
}
|
||||
|
||||
static bool patch_fps_uncap() {
|
||||
if (!patch_hex("\x7E\x07\xB9\x0C\x00\x00\x00\xEB\x09\x85\xC9", 11, 0, "\xEB\x1C", 2))
|
||||
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");
|
||||
return false;
|
||||
@ -3520,6 +3263,8 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool force_trans_debug = false;
|
||||
|
||||
LPWSTR *szArglist;
|
||||
int nArgs;
|
||||
|
||||
@ -3544,6 +3289,11 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
|
||||
wsprintfA ( resultStr, "%S", szArglist[i+1]);
|
||||
g_config_fn = strdup(resultStr);
|
||||
}
|
||||
else if ( wcscmp(szArglist[i], L"--translation-debug") == 0 )
|
||||
{
|
||||
LOG("--translation-debug: turning on translation-related dumps\n");
|
||||
force_trans_debug = true;
|
||||
}
|
||||
}
|
||||
LocalFree(szArglist);
|
||||
|
||||
@ -3567,6 +3317,9 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
|
||||
|
||||
_load_config(g_config_fn, &config, config_psmap);
|
||||
|
||||
if (force_trans_debug)
|
||||
config.translation_debug = true;
|
||||
|
||||
if (!config.disable_multiboot)
|
||||
{
|
||||
/* automatically force datecode based on dll name when applicable (e.g. popn22_2022061300.dll and no force_datecode) */
|
||||
@ -3605,27 +3358,45 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
|
||||
/* look for possible translation patch folder ("_yyyymmddrr_tr" for multiboot, or simply "_translation") */
|
||||
if (!config.disable_translation)
|
||||
{
|
||||
FILE *fp = NULL;
|
||||
char translation_folder[16] = "";
|
||||
char translation_path[64] = "";
|
||||
|
||||
/* parse */
|
||||
if (config.force_datecode[0] != '\0')
|
||||
{
|
||||
|
||||
sprintf(translation_folder, "_%s%s", config.force_datecode, "_tr");
|
||||
fp = _translation_open_dict(translation_folder);
|
||||
sprintf(translation_path, "%s%s", "data_mods\\", translation_folder);
|
||||
if (access(translation_path, F_OK) != 0)
|
||||
{
|
||||
translation_folder[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
if (!fp)
|
||||
if (translation_folder[0] == '\0')
|
||||
{
|
||||
sprintf(translation_folder, "%s", "_translation");
|
||||
fp = _translation_open_dict(translation_folder);
|
||||
sprintf(translation_path, "%s%s", "data_mods\\", translation_folder);
|
||||
if (access(translation_path, F_OK) != 0)
|
||||
{
|
||||
translation_folder[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
if (fp != NULL)
|
||||
if (translation_folder[0] != '\0')
|
||||
{
|
||||
LOG("popnhax: translation: using folder \"%s\"\n", translation_folder);
|
||||
patch_translation(fp);
|
||||
fclose(fp);
|
||||
patch_translate(g_game_dll_fn, translation_folder, config.translation_debug);
|
||||
}
|
||||
else if ( config.translation_debug )
|
||||
{
|
||||
DWORD dllSize = 0;
|
||||
char *data = getDllData(g_game_dll_fn, &dllSize);
|
||||
LOG("popnhax: translation debug: no translation applied, dump prepatched dll\n");
|
||||
FILE* dllrtp = fopen("dllruntime_prepatched.dll", "wb");
|
||||
fwrite(data, 1, dllSize, dllrtp);
|
||||
fclose(dllrtp);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.practice_mode) {
|
||||
|
478
popnhax/translation.cc
Normal file
478
popnhax/translation.cc
Normal file
@ -0,0 +1,478 @@
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <io.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "util/fuzzy_search.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/patch.h"
|
||||
|
||||
#include "tableinfo.h"
|
||||
#include "loader.h"
|
||||
|
||||
#define MAX_REPLACE_SIZE 16384
|
||||
|
||||
FILE *g_dict_applied_fp;
|
||||
bool patch_sjis(const char *dllFilename, const uint8_t *find, uint8_t find_size, int64_t *offset, uint8_t *replace, uint8_t replace_size, bool dump_dict) {
|
||||
static DWORD dllSize = 0;
|
||||
static char *data = getDllData(dllFilename, &dllSize);
|
||||
static uint8_t first_offset = 0;
|
||||
|
||||
int64_t offset_orig = *offset;
|
||||
uint64_t patch_addr;
|
||||
bool valid_sjis = false;
|
||||
do {
|
||||
fuzzy_search_task task;
|
||||
|
||||
FUZZY_START(task, 1)
|
||||
FUZZY_CODE(task, 0, find, find_size)
|
||||
|
||||
*offset = find_block(data, dllSize-*offset-0x100000, &task, *offset);
|
||||
if (*offset == -1) {
|
||||
*offset = offset_orig;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !first_offset )
|
||||
{
|
||||
if (dump_dict)
|
||||
{
|
||||
LOG("popnhax: dump_dict: dump applied patches in dict_applied.txt\n");
|
||||
g_dict_applied_fp = fopen("dict_applied.txt","wb");
|
||||
}
|
||||
/* limit search to a 0x100000-wide zone starting from first string found to speedup the process
|
||||
* make sure to put the first string first (usually 種類順)
|
||||
*/
|
||||
dllSize = *offset + 0x100000;
|
||||
first_offset = 1;
|
||||
}
|
||||
|
||||
patch_addr = (int64_t)data + *offset;
|
||||
|
||||
/* filter out partial matches (check that there isn't a valid sjis char before our match) */
|
||||
uint8_t byte1 = *(uint8_t*)(patch_addr-2);
|
||||
uint8_t byte2 = *(uint8_t*)(patch_addr-1);
|
||||
bool valid_first = ((0x81 <= byte1) && (byte1 <= 0x9F)) || ((0xE0 <= byte1) && (byte1 <= 0xFC));
|
||||
bool valid_secnd = ((0x40 <= byte2) && (byte2 <= 0x9E)) || ((0x9F <= byte2) && (byte2 <= 0xFC));
|
||||
valid_sjis = valid_first && valid_secnd;
|
||||
|
||||
if (valid_sjis)
|
||||
{
|
||||
printf("Partial match at offset 0x%x, retry...\n",(uint32_t)*offset);
|
||||
*offset += find_size;
|
||||
}
|
||||
} 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
|
||||
{
|
||||
free_size++;
|
||||
}
|
||||
while ( *(uint8_t *)(patch_addr+free_size) == 0 );
|
||||
|
||||
if ((free_size-1) < replace_size)
|
||||
{
|
||||
LOG("WARNING: translation %s is too big, truncating to ",(char *)replace);
|
||||
replace_size = free_size-1;
|
||||
replace[replace_size-1] = '\0';
|
||||
LOG("%s\n",(char *)replace);
|
||||
}
|
||||
|
||||
patch_memory(patch_addr, (char *)replace, replace_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FILE* _translation_open_dict(char *foldername, bool *ips)
|
||||
{
|
||||
*ips = true;
|
||||
char dict_filepath[64];
|
||||
sprintf(dict_filepath, "%s%s%s", "data_mods\\", foldername, "\\popn22.ips");
|
||||
FILE *file = fopen(dict_filepath, "rb");
|
||||
if (file != NULL)
|
||||
{
|
||||
return file;
|
||||
}
|
||||
|
||||
*ips = false;
|
||||
sprintf(dict_filepath, "%s%s%s", "data_mods\\", foldername, "\\popn22.dict");
|
||||
file = fopen(dict_filepath, "rb");
|
||||
return file;
|
||||
}
|
||||
|
||||
#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);
|
||||
|
||||
while(reloc->VirtualAddress != 0)
|
||||
{
|
||||
if (reloc->SizeOfBlock >= sizeof(IMAGE_BASE_RELOCATION))
|
||||
{
|
||||
DWORD relocDescNb = (reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
|
||||
LPWORD relocDescList = (LPWORD)((LPBYTE)reloc + sizeof(IMAGE_BASE_RELOCATION));
|
||||
|
||||
for (DWORD i = 0; i < relocDescNb; i++)
|
||||
{
|
||||
if ( ((relocDescList[i])>>12) == RELOC_HIGHLOW )
|
||||
{
|
||||
DWORD_PTR *p = (DWORD_PTR *)( data + (reloc->VirtualAddress + ((relocDescList[i])&0x0FFF)) );
|
||||
/* Change the offset to adapt to injected module base address */
|
||||
|
||||
DWORD old_prot;
|
||||
VirtualProtect((LPVOID)p, 4, PAGE_EXECUTE_READWRITE, &old_prot);
|
||||
*p += delta;
|
||||
if ( ext_base > 0 && *p >= ((int64_t)data+ext_base) )
|
||||
{
|
||||
fprintf(stderr,"reloc rva %lx to ext ", *p);
|
||||
*p += ext_delta;
|
||||
fprintf(stderr," %lx\n", *p);
|
||||
}
|
||||
VirtualProtect((LPVOID)p, 4, old_prot, &old_prot);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Set reloc pointer to the next relocation block */
|
||||
reloc = (PIMAGE_BASE_RELOCATION)((LPBYTE)reloc + reloc->SizeOfBlock);
|
||||
}
|
||||
}
|
||||
|
||||
#define BYTE3_TO_UINT(bp) \
|
||||
(((unsigned int)(bp)[0] << 16) & 0x00FF0000) | \
|
||||
(((unsigned int)(bp)[1] << 8) & 0x0000FF00) | \
|
||||
((unsigned int)(bp)[2] & 0x000000FF)
|
||||
|
||||
#define BYTE2_TO_UINT(bp) \
|
||||
(((unsigned int)(bp)[0] << 8) & 0xFF00) | \
|
||||
((unsigned int) (bp)[1] & 0x00FF)
|
||||
|
||||
#define IPS_EOF 0x464F45
|
||||
|
||||
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;\
|
||||
}\
|
||||
} while (0)
|
||||
DWORD dllSize = 0;
|
||||
char *data = getDllData(dllFilename, &dllSize);
|
||||
|
||||
/* some of the patches might require fixups */
|
||||
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);
|
||||
|
||||
char dict_filepath[64];
|
||||
sprintf(dict_filepath, "%s%s%s", "data_mods\\", foldername, "\\popn22.ips");
|
||||
FILE *ips_fp = fopen(dict_filepath, "rb");
|
||||
|
||||
if (ips_fp == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG("popnhax: translation: popn22.ips found\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");
|
||||
}
|
||||
|
||||
perform_reloc(data, -1*reloc_delta, 0, 0);
|
||||
|
||||
//check 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;
|
||||
}
|
||||
|
||||
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};
|
||||
|
||||
while ( fread(&offset, 1, 3, ips_fp) == 3 && offset != IPS_EOF )
|
||||
{
|
||||
bool skip = false;
|
||||
|
||||
/* need to convert offset to rva before applying patch */
|
||||
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 */
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
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 ( rva+size > dllSize
|
||||
//LOG( "%02x %02x %02x %02x...\n", replace[0], replace[1], replace[2], replace[3]);
|
||||
if ( trans_base_offset != 0 && offset >= trans_base_offset )
|
||||
{
|
||||
//LOG( "WARNING: patching into new section.\n");
|
||||
|
||||
if ( trans_rebase == 0 )
|
||||
{
|
||||
//LOG( "ALLOCATING NEW SECTION FOR TRANS\n");
|
||||
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);
|
||||
//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);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
static bool patch_translation_dict(const char *dllFilename, const char *foldername, bool dump_dict)
|
||||
{
|
||||
uint8_t original[128];
|
||||
uint8_t translation[128];
|
||||
uint8_t buffer;
|
||||
int64_t curr_offset = 0;
|
||||
uint8_t word_count = 0;
|
||||
uint8_t orig_size = 0;
|
||||
|
||||
char dict_filepath[64];
|
||||
sprintf(dict_filepath, "%s%s%s", "data_mods\\", foldername, "\\popn22.dict");
|
||||
FILE *dict_fp = fopen(dict_filepath, "rb");
|
||||
|
||||
if (dict_fp == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG("popnhax: translation: popn22.dict file found\n");
|
||||
|
||||
#define STATE_WAITING 0
|
||||
#define STATE_ORIGINAL 1
|
||||
#define STATE_TRANSLATION 2
|
||||
uint16_t err_count = 0;
|
||||
uint8_t state = STATE_WAITING;
|
||||
uint8_t arr_idx = 0;
|
||||
while (fread(&buffer, 1, 1, dict_fp) == 1)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case STATE_WAITING:
|
||||
if (buffer == ';')
|
||||
{
|
||||
state = STATE_ORIGINAL;
|
||||
arr_idx = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("Unexpected char %c\n", buffer);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case STATE_ORIGINAL:
|
||||
if (buffer == ';')
|
||||
{
|
||||
original[arr_idx++] = '\0';
|
||||
state = STATE_TRANSLATION;
|
||||
orig_size = arr_idx;
|
||||
arr_idx = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
original[arr_idx++] = buffer;
|
||||
}
|
||||
break;
|
||||
case STATE_TRANSLATION:
|
||||
if (buffer == ';')
|
||||
{
|
||||
/* end of word, let's patch! */
|
||||
translation[arr_idx-1] = '\0'; /* strip last \n */
|
||||
while ( arr_idx < orig_size )
|
||||
{
|
||||
translation[arr_idx++] = '\0'; /* fill with null when translation is shorter */
|
||||
}
|
||||
printf("%d: %s -> %s\n",++word_count,(char *)original,(char *)translation);
|
||||
|
||||
/* patch all occurrences */
|
||||
/*curr_offset = 0;
|
||||
uint8_t count = 0;
|
||||
while (patch_sjis(dllFilename, original, orig_size, &curr_offset, translation, arr_idx-1, dump_dict))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
LOG("%d occurrences found\n",count);
|
||||
*/
|
||||
if (!patch_sjis(dllFilename, original, orig_size, &curr_offset, translation, arr_idx-1, dump_dict))
|
||||
{
|
||||
printf("Warning: string %s (%s) not found in order, trying again.\n", (char *)original, (char *)translation);
|
||||
curr_offset = 0;
|
||||
if (!patch_sjis(dllFilename, original, orig_size, &curr_offset, translation, arr_idx-1, dump_dict))
|
||||
{
|
||||
LOG("Warning: string %s not found, skipping.\n", (char *)original);
|
||||
err_count++;
|
||||
}
|
||||
}
|
||||
|
||||
state = STATE_ORIGINAL;
|
||||
arr_idx = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
translation[arr_idx++] = buffer;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
LOG("popnhax: translation: patched %u strings", word_count - err_count);
|
||||
if (err_count)
|
||||
LOG(" (%u skipped strings)", err_count);
|
||||
LOG("\n");
|
||||
|
||||
if (dump_dict)
|
||||
fclose(g_dict_applied_fp);
|
||||
return true;
|
||||
#undef STATE_WAITING
|
||||
#undef STATE_ORIGINAL
|
||||
#undef STATE_TRANSLATION
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
|
||||
bool patch_translate(const char *dllFilename, const char *folder, bool debug)
|
||||
{
|
||||
bool ips_done = false;
|
||||
bool dict_done = false;
|
||||
|
||||
ips_done = patch_translation_ips(dllFilename, folder, debug);
|
||||
|
||||
dict_done = patch_translation_dict(dllFilename, folder, debug);
|
||||
|
||||
return ips_done || dict_done;
|
||||
}
|
10
popnhax/translation.h
Normal file
10
popnhax/translation.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef __TRANSLATION_H__
|
||||
#define __TRANSLATION_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
FILE* _translation_open_dict(char *foldername, bool *ips);
|
||||
|
||||
bool patch_translate(const char *dllFilename, const char *folder, bool debug);
|
||||
|
||||
#endif
|
15
util/log.h
Normal file
15
util/log.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef __LOG_H__
|
||||
#define __LOG_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern FILE *g_log_fp;
|
||||
|
||||
#define LOG(...) do { \
|
||||
fprintf(g_log_fp, __VA_ARGS__); \
|
||||
fflush(g_log_fp);\
|
||||
} while (0)
|
||||
|
||||
#endif
|
125
util/patch.cc
125
util/patch.cc
@ -3,6 +3,7 @@
|
||||
#include <psapi.h>
|
||||
// clang-format on
|
||||
|
||||
#include "util/fuzzy_search.h"
|
||||
#include "patch.h"
|
||||
|
||||
void patch_memory(uint64_t patch_addr, char *data, size_t len) {
|
||||
@ -25,3 +26,127 @@ char *getDllData(const char *dllFilename, DWORD *dllSize) {
|
||||
*dllSize = module_info.SizeOfImage;
|
||||
return (char *)module_info.lpBaseOfDll;
|
||||
}
|
||||
|
||||
|
||||
bool rva_to_offset(const char *dllFilename, uint32_t rva, uint32_t *offset)
|
||||
{
|
||||
uintptr_t baseAddr = (uintptr_t)GetModuleHandle(dllFilename);
|
||||
IMAGE_DOS_HEADER * pDosHdr = (IMAGE_DOS_HEADER *) baseAddr;
|
||||
IMAGE_NT_HEADERS * pNtHdr = (IMAGE_NT_HEADERS *) (baseAddr + pDosHdr->e_lfanew);
|
||||
|
||||
int i;
|
||||
WORD wSections;
|
||||
PIMAGE_SECTION_HEADER pSectionHdr;
|
||||
pSectionHdr = IMAGE_FIRST_SECTION(pNtHdr);
|
||||
wSections = pNtHdr -> FileHeader.NumberOfSections;
|
||||
for (i = 0; i < wSections; i++)
|
||||
{
|
||||
if (pSectionHdr -> VirtualAddress <= rva)
|
||||
if ((pSectionHdr -> VirtualAddress + pSectionHdr -> Misc.VirtualSize) > rva)
|
||||
{
|
||||
rva -= pSectionHdr -> VirtualAddress;
|
||||
rva += pSectionHdr -> PointerToRawData;
|
||||
*offset = rva;
|
||||
return true;
|
||||
}
|
||||
pSectionHdr++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool offset_to_rva(const char *dllFilename, uint32_t offset, uint32_t *rva)
|
||||
{
|
||||
uintptr_t baseAddr = (uintptr_t)GetModuleHandle(dllFilename);
|
||||
IMAGE_DOS_HEADER * pDosHdr = (IMAGE_DOS_HEADER *) baseAddr;
|
||||
IMAGE_NT_HEADERS * pNtHdr = (IMAGE_NT_HEADERS *) (baseAddr + pDosHdr->e_lfanew);
|
||||
|
||||
int i;
|
||||
WORD wSections;
|
||||
PIMAGE_SECTION_HEADER pSectionHdr;
|
||||
|
||||
pSectionHdr = IMAGE_FIRST_SECTION(pNtHdr);
|
||||
wSections = pNtHdr -> FileHeader.NumberOfSections;
|
||||
|
||||
for (i = 0; i < wSections; i++) {
|
||||
if (pSectionHdr -> PointerToRawData <= offset)
|
||||
if ((pSectionHdr -> PointerToRawData + pSectionHdr -> SizeOfRawData) > offset) {
|
||||
offset -= pSectionHdr -> PointerToRawData;
|
||||
offset += pSectionHdr -> VirtualAddress;
|
||||
|
||||
*rva = offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
pSectionHdr++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void find_and_patch_string(const char *dllFilename, const char *input_string, const char *new_string) {
|
||||
DWORD dllSize = 0;
|
||||
char *data = getDllData(dllFilename, &dllSize);
|
||||
|
||||
while (1) {
|
||||
fuzzy_search_task task;
|
||||
|
||||
FUZZY_START(task, 1)
|
||||
FUZZY_CODE(task, 0, input_string, strlen(input_string))
|
||||
|
||||
int64_t pattern_offset = find_block(data, dllSize, &task, 0);
|
||||
if (pattern_offset == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint64_t patch_addr = (int64_t)data + pattern_offset;
|
||||
char *new_string_buff = (char*)calloc(strlen(new_string) + 1, sizeof(char));
|
||||
memcpy(new_string_buff, new_string, strlen(new_string));
|
||||
patch_memory(patch_addr, new_string_buff, strlen(new_string) + 1);
|
||||
free(new_string_buff);
|
||||
}
|
||||
}
|
||||
|
||||
bool find_and_patch_hex(const char *dllFilename, const char *find, uint8_t find_size, int64_t shift, const char *replace, uint8_t replace_size) {
|
||||
DWORD dllSize = 0;
|
||||
char *data = getDllData(dllFilename, &dllSize);
|
||||
|
||||
fuzzy_search_task task;
|
||||
|
||||
FUZZY_START(task, 1)
|
||||
FUZZY_CODE(task, 0, find, find_size)
|
||||
|
||||
int64_t pattern_offset = find_block(data, dllSize, &task, 0);
|
||||
if (pattern_offset == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if DEBUG == 1
|
||||
LOG("BEFORE PATCH :\n");
|
||||
uint8_t *offset = (uint8_t *) ((int64_t)data + pattern_offset + shift - 5);
|
||||
for (int i=0; i<32; i++)
|
||||
{
|
||||
LOG("%02x ", *offset);
|
||||
offset++;
|
||||
if (i == 15)
|
||||
LOG("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
uint64_t patch_addr = (int64_t)data + pattern_offset + shift;
|
||||
patch_memory(patch_addr, (char *)replace, replace_size);
|
||||
|
||||
#if DEBUG == 1
|
||||
LOG("\nAFTER PATCH :\n");
|
||||
offset = (uint8_t *) ((int64_t)data + pattern_offset + shift - 5);
|
||||
for (int i=0; i<32; i++)
|
||||
{
|
||||
LOG("%02x ", *offset);
|
||||
offset++;
|
||||
if (i == 15)
|
||||
LOG("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
|
||||
}
|
@ -7,7 +7,14 @@
|
||||
|
||||
typedef unsigned long DWORD;
|
||||
|
||||
void patch_memory(uint64_t patch_addr, char *data, size_t len);
|
||||
char *getDllData(const char *dllFilename, DWORD *dllSize);
|
||||
|
||||
bool rva_to_offset(const char *dllFilename, uint32_t rva, uint32_t *offset);
|
||||
bool offset_to_rva(const char *dllFilename, uint32_t offset, uint32_t *rva);
|
||||
|
||||
void patch_memory(uint64_t patch_addr, char *data, size_t len);
|
||||
|
||||
bool find_and_patch_hex(const char *dllFilename, const char *find, uint8_t find_size, int64_t shift, const char *replace, uint8_t replace_size);
|
||||
void find_and_patch_string(const char *dllFilename, const char *input_string, const char *new_string);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user