@@ 3,9 3,11 @@
#include <windows.h>
#include "processmoduletable.h"
+#include "types.h"
#include "util.h"
static char* __cdecl __patched_get_minigame_tex(char *, size_t, DWORD);
+static DWORD __cdecl __patched_test_minigame_38(BishiMinigame *);
class GameController
{
@@ 19,7 21,9 @@ private: // types
using BishiGetMinigameTexFunc = char* __cdecl(*)(
char *tex_name_buf,
size_t tex_name_maxlen,
- DWORD minigame_uid);
+ DWORD minigame_uid
+ );
+ using BishiTestMinigame38Func = DWORD __cdecl(*)(BishiMinigame *);
private: // functions
explicit GameController();
@@ 27,17 31,18 @@ private: // functions
char* _patched_get_minigame_tex(char *tex_name_buf,
size_t tex_name_maxlen,
DWORD minigame_uid);
+ DWORD _patched_test_minigame_38(BishiMinigame *minigame);
static void _patch_exe(void *dst, void *src, size_t len);
- static void _patch_exe_ptr(void **ptr, void *new_value);
- static bool _patch_function(const ProcessModuleTable &modtable,
- const char *module_name,
- const char *sym_name,
- void *new_funcptr,
- void **out_orig_funcptr);
+ static void _patch_xrefs(const void *base,
+ const void *replacement,
+ const uint32_t *xrefs,
+ size_t xrefs_count);
friend char* __cdecl __patched_get_minigame_tex(char *, size_t, DWORD);
+ friend DWORD __cdecl __patched_test_minigame_38(BishiMinigame *);
private: // variables
BishiGetMinigameTexFunc _orig_get_minigame_tex_func_ptr;
+ BishiTestMinigame38Func _orig_test_minigame_38_func_ptr;
};
@@ 11,15 11,38 @@
#define DEBUG_LOG(...) printf("[BishiInject] " __VA_ARGS__)
#define ERROR_LOG(...) fprintf(stderr, "[BishiInject] " __VA_ARGS__)
+#define COUNTOF(X) (sizeof(X) / sizeof(X[0]))
namespace
{
+ constexpr const char *BISHI_MODULE_NAME = "bishi09.exe";
+
constexpr uint32_t BISHI_GET_MINIGAME_TEX_FILENAME_OFFSET = 0x67f20;
constexpr uint32_t BISHI_GET_MINIGAME_TEX_FILENAME_XREFS[] = {
0x429ea,
0x466d3
};
- constexpr const char *BISHI_MODULE_NAME = "bishi09.exe";
+
+ constexpr uint32_t BISHI_TEST_MINIGAME_38_OFFSET = 0x680d0;
+ constexpr uint32_t BISHI_TEST_MINIGAME_38_XREFS[] = {
+ 0x44998,
+ 0x51534
+ };
+
+ constexpr uint32_t BISHI_MINIGAME_PIROPIRO_UID = 0x33; // playable, hidden always
+ constexpr uint32_t BISHI_MINIGAME_POPN_UID = 0x3c; // playable, hidden always
+ constexpr uint32_t BISHI_MINIGAME_LOVE_UID = 0x3d; // playable, hidden always
+ constexpr uint32_t BISHI_MINIGAME_MORSE_UID = 0x0e; // playable, hidden by 38
+ constexpr uint32_t BISHI_MINIGAME_USAGI_UID = 0x08; // broken
+ constexpr uint32_t BISHI_MINIGAME_STOP3_UID = 0x0f; // broken
+ constexpr uint32_t BISHI_MINIGAME_CLASS_UID = 0x10; // broken
+
+ constexpr uint32_t BISHI_MINIGAME_REMAP[] = {
+ // FROM -> TO
+ BISHI_MINIGAME_USAGI_UID, BISHI_MINIGAME_PIROPIRO_UID,
+ BISHI_MINIGAME_STOP3_UID, BISHI_MINIGAME_POPN_UID,
+ BISHI_MINIGAME_CLASS_UID, BISHI_MINIGAME_LOVE_UID,
+ };
}
GameController& GameController::instance()
@@ 51,20 74,19 @@ bool GameController::initialize()
return false;
}
- uint8_t *base = (uint8_t *)game_module->DllBase;
- const void *replacement = (const void *)&__patched_get_minigame_tex;
- uint8_t patched_instr_data[5];
-
- _orig_get_minigame_tex_func_ptr = (BishiGetMinigameTexFunc)(base + BISHI_GET_MINIGAME_TEX_FILENAME_OFFSET);
+ _orig_get_minigame_tex_func_ptr = (BishiGetMinigameTexFunc)
+ ((uint8_t *)game_module->DllBase + BISHI_GET_MINIGAME_TEX_FILENAME_OFFSET);
+ _patch_xrefs(game_module->DllBase,
+ (const void *)&__patched_get_minigame_tex,
+ BISHI_GET_MINIGAME_TEX_FILENAME_XREFS,
+ COUNTOF(BISHI_GET_MINIGAME_TEX_FILENAME_XREFS));
- for (const uint32_t xref : BISHI_GET_MINIGAME_TEX_FILENAME_XREFS) {
- void *dst = (void *)(base + xref);
- const uint32_t call_rel = (uintptr_t)replacement - (uintptr_t)dst - sizeof(patched_instr_data);
- X86ISA::call_rel_32(call_rel, patched_instr_data);
-
- DEBUG_LOG("Patching minigame tex getter xref at 0x%p\n", dst);
- _patch_exe(dst, patched_instr_data, sizeof(patched_instr_data));
- }
+ _orig_test_minigame_38_func_ptr = (BishiTestMinigame38Func)
+ ((uint8_t *)game_module->DllBase + BISHI_TEST_MINIGAME_38_OFFSET);
+ _patch_xrefs(game_module->DllBase,
+ (const void *)&__patched_test_minigame_38,
+ BISHI_TEST_MINIGAME_38_XREFS,
+ COUNTOF(BISHI_TEST_MINIGAME_38_XREFS));
DEBUG_LOG("Successfully patched game code.\n");
return true;
@@ 76,7 98,46 @@ char* GameController::_patched_get_minigame_tex(char *tex_name_buf,
size_t tex_name_maxlen,
DWORD minigame_uid)
{
- return _orig_get_minigame_tex_func_ptr(tex_name_buf, tex_name_maxlen, 1);
+ // remap a broken minigame to a known working one
+ constexpr size_t minigame_remap_size = COUNTOF(BISHI_MINIGAME_REMAP);
+ uint32_t remap_result = 0;
+ for (unsigned i = 0; i < minigame_remap_size; i += 2) {
+ if (BISHI_MINIGAME_REMAP[i] == minigame_uid) {
+ remap_result = BISHI_MINIGAME_REMAP[i + 1];
+ break;
+ }
+ }
+
+ DWORD new_minigame_uid = (remap_result ?: minigame_uid);
+ return _orig_get_minigame_tex_func_ptr(tex_name_buf, tex_name_maxlen, new_minigame_uid);
+}
+
+DWORD GameController::_patched_test_minigame_38(BishiMinigame *minigame)
+{
+ DWORD result = 0;
+
+ if (minigame) {
+ const DWORD uid = minigame->uid;
+
+ // we want all visible minigames to pass this test, even the broken ones.
+ switch (uid) {
+ case BISHI_MINIGAME_PIROPIRO_UID:
+ case BISHI_MINIGAME_POPN_UID:
+ case BISHI_MINIGAME_LOVE_UID:
+ case BISHI_MINIGAME_MORSE_UID:
+ case BISHI_MINIGAME_USAGI_UID:
+ case BISHI_MINIGAME_STOP3_UID:
+ case BISHI_MINIGAME_CLASS_UID:
+ result = 1;
+ break;
+
+ default:
+ result = _orig_test_minigame_38_func_ptr(minigame);
+ break;
+ }
+ }
+
+ return result;
}
// static
@@ 90,57 151,30 @@ void GameController::_patch_exe(void *dst, void *src, size_t len)
}
// static
-void GameController::_patch_exe_ptr(void **ptr, void *new_value)
+void GameController::_patch_xrefs(const void *base,
+ const void *replacement,
+ const uint32_t *xrefs,
+ size_t xrefs_count)
{
- _patch_exe(ptr, &new_value, sizeof(void *));
-}
+ uint8_t patched_instr_data[5];
-// static
-bool GameController::_patch_function(const ProcessModuleTable &modtable,
- const char *module_name,
- const char *sym_name,
- void *new_funcptr,
- void **out_orig_funcptr)
-{
- bool patched = false;
-
- for (const LDR_DATA_TABLE_ENTRY &module : modtable) {
- const uint8_t *base = (uint8_t *)module.DllBase;
- const IMAGE_DOS_HEADER *dosh = (IMAGE_DOS_HEADER *)base;
- const IMAGE_NT_HEADERS *hdrs = (IMAGE_NT_HEADERS *)(base + dosh->e_lfanew);
- const IMAGE_DATA_DIRECTORY *data_dir = &hdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
-
- IMAGE_IMPORT_DESCRIPTOR *iid;
- for ( iid = (IMAGE_IMPORT_DESCRIPTOR *)(base + data_dir->VirtualAddress);
- iid->Name != 0 && iid->Name != 0xffff;
- ++iid) {
- const char *iid_name = (char *)(base + iid->Name);
- if (strcmp(iid_name, module_name) != 0) { continue; }
-
- const uintptr_t *import_rvas = (uintptr_t *)(base + iid->OriginalFirstThunk);
- for (size_t i = 0; import_rvas[i] != 0; ++i) {
- const IMAGE_IMPORT_BY_NAME *import = (IMAGE_IMPORT_BY_NAME *)(base + import_rvas[i]);
- const char *import_name = (const char *)import->Name;
- if (strcmp(import_name, sym_name) != 0) { continue; }
-
- void **pointers = (void **)(base + iid->FirstThunk);
- void **ppointer = &pointers[i];
- void *new_value = new_funcptr;
-
- if (out_orig_funcptr) {
- *out_orig_funcptr = *ppointer;
- }
-
- _patch_exe_ptr(ppointer, new_value);
- patched = true;
- }
- }
- }
+ for (unsigned i = 0; i < xrefs_count; ++i) {
+ const uint32_t xref = xrefs[i];
+ void *dst = (void *)((uint8_t *)base + xref);
+ const uint32_t call_rel = (uintptr_t)replacement - (uintptr_t)dst - sizeof(patched_instr_data);
+ X86ISA::call_rel_32(call_rel, patched_instr_data);
- return patched;
+ DEBUG_LOG("Patching xref at 0x%p\n", dst);
+ _patch_exe(dst, patched_instr_data, sizeof(patched_instr_data));
+ }
}
char* __cdecl __patched_get_minigame_tex(char *tex_name_buf, size_t tex_name_maxlen, DWORD minigame_uid)
{
return GameController::instance()._patched_get_minigame_tex(tex_name_buf, tex_name_maxlen, minigame_uid);
}
+
+DWORD __cdecl __patched_test_minigame_38(BishiMinigame *minigame)
+{
+ return GameController::instance()._patched_test_minigame_38(minigame);
+}