diff --git a/dist/popnhax/popnhax.xml b/dist/popnhax/popnhax.xml
index e573831..1ef33b6 100644
--- a/dist/popnhax/popnhax.xml
+++ b/dist/popnhax/popnhax.xml
@@ -56,20 +56,32 @@
+
1
+
+ 0
+
+
0
0
0
+
+
0
0
-
- 0
+
+
+
+ 0
+
+ 0
+
diff --git a/popnhax/config.h b/popnhax/config.h
index 49f5693..16d7d08 100644
--- a/popnhax/config.h
+++ b/popnhax/config.h
@@ -34,6 +34,8 @@ struct popnhax_config {
int8_t keysound_offset;
int8_t beam_brightness;
bool fps_uncap;
+ bool disable_translation;
+ bool dump_dict;
};
#endif
diff --git a/popnhax/dllmain.cc b/popnhax/dllmain.cc
index b73c695..476b6c1 100644
--- a/popnhax/dllmain.cc
+++ b/popnhax/dllmain.cc
@@ -139,6 +139,10 @@ PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_S8, struct popnhax_config, beam_brightness,
"/popnhax/beam_brightness")
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_END
enum BufferIndexes {
@@ -632,6 +636,194 @@ bool patch_hex(const char *find, uint8_t find_size, int64_t shift, const char *r
}
+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) {
+ fprintf(stderr,"NOT FOUND\n");
+ *offset = offset_orig;
+ return false;
+ }
+
+ if ( !first_offset )
+ {
+ if (config.dump_dict)
+ {
+ printf("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)
+ {
+ fprintf(stderr, "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)
+ {
+ printf("WARNING: translation %s is too big, truncating to ",(char *)replace);
+ replace_size = free_size-1;
+ replace[replace_size-1] = '\0';
+ printf("%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
+ {
+ printf("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++;
+ }
+ printf("%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))
+ {
+ printf("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;
+ }
+ }
+ printf("popnhax: translation: patched all strings");
+ if (err_count)
+ printf(" (%u skipped strings)", err_count);
+ printf("\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));
@@ -3395,6 +3587,32 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
patch_datecode(config.force_datecode);
}
+ /* look for possible translation patch folder ("_yyyymmddrr_tr" for multiboot, or simply "_translation") */
+ if (!config.disable_translation)
+ {
+ FILE *fp = NULL;
+ char translation_folder[16] = "";
+ /* parse */
+ if (config.force_datecode[0] != '\0')
+ {
+ sprintf(translation_folder, "_%s%s", config.force_datecode, "_tr");
+ fp = _translation_open_dict(translation_folder);
+ }
+
+ if (!fp)
+ {
+ sprintf(translation_folder, "%s", "_translation");
+ fp = _translation_open_dict(translation_folder);
+ }
+
+ if (fp != NULL)
+ {
+ printf("popnhax: translation: using folder \"%s\"\n", translation_folder);
+ patch_translation(fp);
+ fclose(fp);
+ }
+ }
+
if (config.practice_mode) {
patch_practice_mode();
}