diff --git a/blcrack/cracker/ui/hook.cpp b/blcrack/cracker/ui/hook.cpp index 49750a634..b87571676 100644 --- a/blcrack/cracker/ui/hook.cpp +++ b/blcrack/cracker/ui/hook.cpp @@ -13,6 +13,23 @@ #include "il2cpp.hpp" #include "dobby.h" +extern ImVec2 g_window_pos; +extern ImVec2 g_window_size; + +#define IMGUI_WND_EDGE 50 + +bool ImGui_IsPosInWnd(float x, float y) { + auto start_x = g_window_pos.x - IMGUI_WND_EDGE; + auto start_y = g_window_pos.y - IMGUI_WND_EDGE; + auto end_x = g_window_pos.x + g_window_size.x + IMGUI_WND_EDGE; + auto end_y = g_window_pos.y + g_window_size.y + IMGUI_WND_EDGE; + + if (x >= start_x && x <= end_x && y >= start_y && y <= end_y) { + return true; + } + return false; +} + using eglSwapBuffers_fnT = EGLBoolean(EGLDisplay dpy, EGLSurface surface); eglSwapBuffers_fnT* old_eglSwapBuffers = nullptr; @@ -20,6 +37,7 @@ EGLint g_width = 0; EGLint g_height = 0; std::atomic g_imgui_started = false; +bool g_need_ui_penetration = false; EGLBoolean my_eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) { ui_eglSwapBuffers(dpy, surface); @@ -58,7 +76,7 @@ int64_t my_Input(void* thiz, void* ex_ab, void* ex_ac) g_use_native_input.store(true); } - if (g_imgui_started.load() && CrackerUI::get_instance().is_ui_showed()) { + if (g_imgui_started.load() && CrackerUI::get_instance().is_ui_showed() && g_need_ui_penetration) { ImGui_ImplAndroid_HandleInputEvent((AInputEvent *) thiz); } @@ -88,7 +106,7 @@ int64_t my_Consume(void* thiz, int64_t arg1, char arg2, int64_t arg3, uint32_t* g_use_native_input.store(true); } - if (g_imgui_started.load() && CrackerUI::get_instance().is_ui_showed()) { + if (g_imgui_started.load() && CrackerUI::get_instance().is_ui_showed() && g_need_ui_penetration) { ImGui_ImplAndroid_HandleInputEvent(*input_event); } @@ -106,7 +124,13 @@ bool has_hooked = false; #define TOUCH_PHASE_ENDED 3 #define TOUCH_PHASE_CANCELED 4 -void My_ImGui_ImplAndroid_HandleInputEvent(const UnityEngine_Touch_o& touch) { +int My_ImGui_ImplAndroid_HandleInputEvent(const UnityEngine_Touch_o& touch) { + auto x = touch.fields.m_Position.fields.x; + auto y = g_height - touch.fields.m_Position.fields.y; + if (!ImGui_IsPosInWnd(x, y)) { + return 1; + } + auto& io = ImGui::GetIO(); // SPDLOG_INFO("Touch (RawX = {}, RawY = {}), (X = {}, Y = {}), (DeltaX = {}, DeltaY = {})", @@ -119,18 +143,18 @@ void My_ImGui_ImplAndroid_HandleInputEvent(const UnityEngine_Touch_o& touch) { io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen); switch(phase) { case TOUCH_PHASE_BEGAN: - io.AddMousePosEvent(touch.fields.m_Position.fields.x, g_height - touch.fields.m_Position.fields.y); + io.AddMousePosEvent(x, y); io.AddMouseButtonEvent(0, true); io.AddMouseButtonEvent(1, true); io.AddMouseButtonEvent(2, true); break; case TOUCH_PHASE_MOVED: case TOUCH_PHASE_STATIONARY: - io.AddMousePosEvent(touch.fields.m_Position.fields.x, g_height - touch.fields.m_Position.fields.y); + io.AddMousePosEvent(x, y); break; case TOUCH_PHASE_ENDED: case TOUCH_PHASE_CANCELED: - io.AddMousePosEvent(touch.fields.m_Position.fields.x, g_height - touch.fields.m_Position.fields.y); + io.AddMousePosEvent(x, y); io.AddMouseButtonEvent(0, false); io.AddMouseButtonEvent(1, false); io.AddMouseButtonEvent(2, false); @@ -138,12 +162,13 @@ void My_ImGui_ImplAndroid_HandleInputEvent(const UnityEngine_Touch_o& touch) { default: break; } + return 0; } using GlobalClickEventMgr_HandlePinchOnTouch_fnT = void(void* thiz, UnityEngine_Touch_array* touches); GlobalClickEventMgr_HandlePinchOnTouch_fnT* old_GlobalClickEventMgr_HandlePinchOnTouch = nullptr; void my_GlobalClickEventMgr_HandlePinchOnTouch(void* thiz, UnityEngine_Touch_array* touches) { - if (touches != nullptr && !g_use_native_input.load() && g_imgui_started.load() && CrackerUI::get_instance().is_ui_showed()) { + if (touches != nullptr && !g_use_native_input.load() && g_imgui_started.load() && CrackerUI::get_instance().is_ui_showed() && g_need_ui_penetration) { for(int i = 0; i < touches->max_length; i++) { My_ImGui_ImplAndroid_HandleInputEvent(touches->m_Items[i]); } @@ -181,6 +206,56 @@ static GlobalClickEventMgr_HandlePinchOnTouch_fnT* get_GlobalClickEventMgr_Handl return fn; } +using UnityEngine_Input_GetTouch_fnT = UnityEngine_Touch_o*(UnityEngine_Touch_o* ret, int32_t index); +static UnityEngine_Input_GetTouch_fnT* get_UnityEngine_Input_GetTouch() { + static UnityEngine_Input_GetTouch_fnT* fn = []() { + Il2CppDomain* domain = il2cpp_domain_get(); + size_t assembly_count = 0; + Il2CppAssembly** assemblies = il2cpp_domain_get_assemblies(domain, &assembly_count); + + Il2CppClass* cls = nullptr; + for (size_t i = 0; i < assembly_count; ++i) { + Il2CppImage* image = il2cpp_assembly_get_image(assemblies[i]); + Il2CppClass* klass = il2cpp_class_from_name(image, "UnityEngine", "Input"); + if (klass != nullptr) { + cls = klass; + break; + } + } + if (cls != nullptr) { + MethodInfo* method_info = il2cpp_class_get_method_from_name(cls, "GetTouch", 1); + if (method_info != nullptr) { + auto method = reinterpret_cast(method_info->methodPointer); + + SPDLOG_INFO("Found UnityEngine.Input.GetTouch at {}", (void*)method_info->methodPointer); + return method; + } + } + SPDLOG_ERROR("Failed to get UnityEngine.Input.GetTouch"); + throw std::runtime_error("Failed to get UnityEngine.Input.GetTouch"); + }(); + return fn; +} + +UnityEngine_Input_GetTouch_fnT* old_UnityEngine_Input_GetTouch = nullptr; +UnityEngine_Touch_o* my_UnityEngine_Input_GetTouch(UnityEngine_Touch_o* ret, int32_t index) { + auto touch = old_UnityEngine_Input_GetTouch(ret, index); + + if (g_imgui_started.load() && !g_need_ui_penetration && touch != nullptr) { + if (My_ImGui_ImplAndroid_HandleInputEvent(*touch) == 0) { + touch->fields.m_RawPosition.fields.x = 0; + touch->fields.m_RawPosition.fields.y = 0; + touch->fields.m_Position.fields.x = 0; + touch->fields.m_Position.fields.y = 0; + touch->fields.m_PositionDelta.fields.x = 0; + touch->fields.m_PositionDelta.fields.y = 0; + touch->fields.m_Phase = TOUCH_PHASE_CANCELED; + } + } + + return touch; +} + void do_ui_hook() { if (has_hooked) { return; @@ -197,6 +272,15 @@ void do_ui_hook() { SPDLOG_INFO("Hook GlobalClickEventMgr.HandlePinchOnTouch failed: {}", dobby_hook_status); } + dobby_hook_status = DobbyHook( + reinterpret_cast(get_UnityEngine_Input_GetTouch()), + reinterpret_cast(my_UnityEngine_Input_GetTouch), + reinterpret_cast(&old_UnityEngine_Input_GetTouch) + ); + if (dobby_hook_status != 0) { + SPDLOG_INFO("Hook UnityEngine.Input.GetTouch failed: {}", dobby_hook_status); + } + has_hooked = true; SPDLOG_INFO("Hook ingame ui done!"); } diff --git a/blcrack/cracker/ui/ui.cpp b/blcrack/cracker/ui/ui.cpp index 4ca3e57fa..18cc3e6f7 100644 --- a/blcrack/cracker/ui/ui.cpp +++ b/blcrack/cracker/ui/ui.cpp @@ -11,6 +11,9 @@ #include "font.hpp" #include "skincache.hpp" +ImVec2 g_window_pos = ImVec2(0, 0); +ImVec2 g_window_size = ImVec2(0, 0); + CrackerUI::CrackerUI() {} void CrackerUI::init(EGLint width, EGLint height) { @@ -42,12 +45,17 @@ void CrackerUI::init(EGLint width, EGLint height) { #define SHIP_PROPERTY(n) m_cracker_config.globle_ship_properties.n #define FILL_SHIP_PROPERTY(n) m_cracker_config.globle_ship_properties.n = m_all_ship_properties +extern bool g_need_ui_penetration; + void CrackerUI::draw_menu() { ImGui::Begin("AzurLane Crack"); ImVec2 window_pos = ImGui::GetWindowPos(); ImVec2 window_size = ImGui::GetWindowSize(); + g_window_pos = window_pos; + g_window_size = window_size; + ImVec2 new_pos = window_pos; new_pos.x = ImClamp(new_pos.x, m_constrain_min.x, m_constrain_max.x - window_size.x); new_pos.y = ImClamp(new_pos.y, m_constrain_min.y, m_constrain_max.y - window_size.y); @@ -190,6 +198,7 @@ void CrackerUI::draw_menu() { if (ImGui::Button("清空全皮肤设置")) { SkinCache::get_instance().clear(); } + ImGui::Checkbox("UI穿透", &g_need_ui_penetration); ImGui::EndTabItem(); } ImGui::EndTabBar();