diff --git a/main.cpp b/main.cpp index f9fab901b..87a722d11 100644 --- a/main.cpp +++ b/main.cpp @@ -1,93 +1,131 @@ +#include +#include +#include #include +#include +#include +#include + +// 引入 Dobby Hook 库 +#include "dobby.h" + +// 引入 ImGui #include "imgui.h" +#include "imgui_impl_android.h" +#include "imgui_impl_opengl3.h" +#include "imgui_impl_vulkan.h" -// 定义日志宏,方便在 PC 端通过 adb logcat -s IMG_DEBUG 查看 -#define LOG_TAG "IMG_DEBUG" -#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +#define TAG "JKMenu_Core" +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__) -// 模拟全局状态 -bool g_ShowDebugWindow = true; +// --- 全局变量与状态 --- +static bool g_Initialized = false; +static bool g_VulkanMode = false; -/** - * 核心功能:处理 Android 原生输入事件并传递给 ImGui - * 你需要确保你的 AInputQueue 或者 dispatchTouchEvent 正在调用此逻辑 - */ -void HandleAndroidInput(int action, float x, float y) { - ImGuiIO& io = ImGui::GetIO(); +// Vulkan 偷取到的上下文 +struct { + VkInstance Instance = VK_NULL_HANDLE; + VkPhysicalDevice PhysicalDevice = VK_NULL_HANDLE; + VkDevice Device = VK_NULL_HANDLE; + VkQueue Queue = VK_NULL_HANDLE; + uint32_t QueueFamily = 0; +} g_VulkanCtx; + +// --- 原函数指针备份 --- +typedef EGLBoolean (*eglSwapBuffers_t)(EGLDisplay, EGLSurface); +static eglSwapBuffers_t old_eglSwapBuffers = nullptr; + +typedef VkResult (*vkQueueSubmit_t)(VkQueue, uint32_t, const VkSubmitInfo*, VkFence); +static vkQueueSubmit_t old_vkQueueSubmit = nullptr; + +// --- 统一的菜单绘制逻辑 --- +void DrawJKMenu() { + if (!ImGui::GetCurrentContext()) return; + + // 这里处理你的功能逻辑,比如读取 com.tencent.jkchess 的内存偏移 + ImGui::Begin("JKMenu v1.0", nullptr, ImGuiWindowFlags_AlwaysAutoResize); + ImGui::Text("Render: %s", g_VulkanMode ? "Vulkan (Adreno)" : "OpenGL ES 3.0"); + ImGui::Separator(); - // 更新坐标 - io.MousePos = ImVec2(x, y); - - // 更新点击状态 - if (action == 0) { // AMOTION_EVENT_ACTION_DOWN - io.MouseDown[0] = true; - LOGI("Touch DOWN at: x=%.1f, y=%.1f", x, y); - } - else if (action == 1) { // AMOTION_EVENT_ACTION_UP - io.MouseDown[0] = false; - LOGI("Touch UP at: x=%.1f, y=%.1f", x, y); - } - else if (action == 2) { // AMOTION_EVENT_ACTION_MOVE - // Move 事件通常不需要设置 MouseDown,只需更新坐标 - } -} - -/** - * 核心功能:每帧调用的 UI 渲染函数 - * 请将此函数放入你的渲染循环中 - */ -void RenderImGuiFrame() { - ImGuiIO& io = ImGui::GetIO(); - - // 1. 设置窗口样式 (可选,为了更好看) - ImGui::SetNextWindowSize(ImVec2(400, 300), ImGuiCond_FirstUseEver); + static bool test_check = false; + ImGui::Checkbox("示例功能开关", &test_check); - // 2. 开始渲染调试窗口 - // 注意:这里使用纯英文,避免截图中的 ?? 乱码问题 - if (ImGui::Begin("INPUT DEBUGGER v1.0", &g_ShowDebugWindow)) { - - ImGui::Text("Device: OnePlus Pad (Testing)"); - ImGui::Separator(); - - // --- 坐标实时显示 --- - // 如果你手指在屏幕上移动,这两个数字必须跳动 - ImGui::TextColored(ImVec4(0, 1, 0, 1), "Real-time Mouse Position:"); - ImGui::BulletText("X: %.1f", io.MousePos.x); - ImGui::BulletText("Y: %.1f", io.MousePos.y); - - ImGui::Spacing(); - - // --- 点击状态显示 --- - // 按下时显示红色 "PRESSED",松开时显示灰色 "IDLE" - ImGui::Text("Touch Status: "); - ImGui::SameLine(); - if (io.MouseDown[0]) { - ImGui::TextColored(ImVec4(1, 0, 0, 1), "PRESSED"); - } else { - ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1), "IDLE"); - } - - ImGui::Separator(); - - // --- 功能测试按钮 --- - // 这是一个物理检测:如果坐标对,但点不动这个按钮,说明 io.MouseDown 没传对 - if (ImGui::Button("TEST CLICK FEEDBACK", ImVec2(-1, 80))) { - LOGI("Button Clicked via ImGui!"); - } - - if (ImGui::Button("RESET COORDINATES", ImVec2(-1, 40))) { - io.MousePos = ImVec2(-1, -1); - } - - ImGui::Spacing(); - ImGui::TextDisabled("Check Logcat with: adb logcat -s %s", LOG_TAG); + if (ImGui::Button("清理日志")) { + LOGI("用户点击了清理按钮"); } + ImGui::End(); } -/** - * 简易说明: - * 1. 如果窗口里的 X/Y 始终是 0 或者固定值,说明 HandleAndroidInput 没被触发。 - * 2. 如果 X/Y 在动,但按钮点不下去,说明 io.MouseDown[0] 的赋值逻辑有问题。 - * 3. 请确保 RenderImGuiFrame 在你的绘图指令(DrawData)之前执行。 - */ +// --- OpenGL Hook 实现 (兼容模式用) --- +EGLBoolean hook_eglSwapBuffers(EGLDisplay dpy, EGLSurface surf) { + if (!g_Initialized) { + LOGI("正在初始化 OpenGL ImGui..."); + ImGui::CreateContext(); + ImGui_ImplAndroid_Init(nullptr); // 实际项目中需传入 NativeWindow + ImGui_ImplOpenGL3_Init("#version 300 es"); + g_Initialized = true; + } + + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplAndroid_NewFrame(); + ImGui::NewFrame(); + + DrawJKMenu(); + + ImGui::Render(); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + return old_eglSwapBuffers(dpy, surf); +} + +// --- Vulkan Hook 实现 (普通模式用) --- +VkResult hook_vkQueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence) { + // Vulkan 模式下的逻辑更复杂,通常建议在兼容模式稳定后再深入 + // 这里仅做环境记录,防止崩溃 + if (!g_Initialized) { + LOGI("检测到 Vulkan 提交,当前为普通模式运行。"); + // 注意:Vulkan 的 ImGui 需要大量的 DescriptorPool 初始化,建议先引导用户用兼容模式 + g_Initialized = true; + } + return old_vkQueueSubmit(queue, submitCount, pSubmits, fence); +} + +// --- 核心探测与 Hook 线程 --- +void* init_thread(void*) { + LOGI("JKMenu 线程已启动,等待游戏渲染模块加载..."); + sleep(5); // 等待游戏初始化完成 + + // 1. 优先检查是否为 Vulkan (普通模式) + void* vulkan_handle = dlopen("vulkan.adreno.so", RTLD_NOLOAD); + if (vulkan_handle) { + g_VulkanMode = true; + LOGI(">>> 发现 Vulkan 驱动,尝试挂载普通模式 Hook..."); + + // 使用你之前日志里的高通特有符号 + void* vk_submit = dlsym(vulkan_handle, "_ZN11qglinternal13vkQueueSubmitEP9VkQueue_TjPK12VkSubmitInfoP9VkFence_T"); + if (vk_submit) { + DobbyHook(vk_submit, (void*)hook_vkQueueSubmit, (void**)&old_vkQueueSubmit); + LOGI("Vulkan Hook 成功!"); + } + } + + // 2. 无论是否发现 Vulkan,都 Hook EGL (兼容模式) + // 因为用户可能随时在游戏设置里切换 + void* egl_addr = DobbySymbolResolver("libEGL.so", "eglSwapBuffers"); + if (egl_addr) { + DobbyHook(egl_addr, (void*)hook_eglSwapBuffers, (void**)&old_eglSwapBuffers); + LOGI("OpenGL (兼容模式) Hook 成功!"); + } + + return nullptr; +} + +// --- JNI 入口 --- +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { + LOGI("JKMenu .so 已成功注入进程: com.tencent.jkchess"); + pthread_t tid; + pthread_create(&tid, nullptr, init_thread, nullptr); + return JNI_VERSION_1_6; +}