diff --git a/src/GakumasLocalify/Hook.cpp b/src/GakumasLocalify/Hook.cpp index 4d5073f..932e5d1 100644 --- a/src/GakumasLocalify/Hook.cpp +++ b/src/GakumasLocalify/Hook.cpp @@ -514,6 +514,26 @@ namespace GakumasLocal::HookMain { UpdateFont(self); } + DEFINE_HOOK(void, TMP_Text_SetText_2, (void* self, Il2cppString* sourceText, bool syncTextInputBox, void* mtd)) { + if (!sourceText) { + return TMP_Text_SetText_2_Orig(self, sourceText, syncTextInputBox, mtd); + } + const std::string origText = sourceText->ToString(); + std::string transText; + if (Local::GetGenericText(origText, &transText)) { + const auto newText = UnityResolve::UnityType::String::New(transText); + UpdateFont(self); + return TMP_Text_SetText_2_Orig(self, newText, syncTextInputBox, mtd); + } + if (Config::textTest) { + TMP_Text_SetText_2_Orig(self, UnityResolve::UnityType::String::New("[TS]" + sourceText->ToString()), syncTextInputBox, mtd); + } + else { + TMP_Text_SetText_2_Orig(self, sourceText, syncTextInputBox, mtd); + } + UpdateFont(self); + } + DEFINE_HOOK(void, TextMeshProUGUI_Awake, (void* self, void* method)) { // Log::InfoFmt("TextMeshProUGUI_Awake at %p, self at %p", TextMeshProUGUI_Awake_Orig, self); @@ -1240,6 +1260,70 @@ namespace GakumasLocal::HookMain { return ret; } +#ifdef GKMS_WINDOWS + // DMM Only + DEFINE_HOOK(void*, WindowHandle_SetWindowLong, (int32_t nIndex, intptr_t dwNewLong, void* mtd)) { + if (GakumasLocal::Config::dmmUnlockSize) { + // Log::DebugFmt("WindowHandle_SetWindowLong: %d, %p\n", nIndex, dwNewLong); + + if (nIndex == GWLP_WNDPROC) { + return 0; + } + } + + return WindowHandle_SetWindowLong_Orig(nIndex, dwNewLong, mtd); + } + + // DMM Only + void SetResolution(int width, int height, bool fullscreen) { + static auto Screen_SetResolution = reinterpret_cast( + Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Screen::SetResolution_Injected(System.Int32,System.Int32,UnityEngine.FullScreenMode,UnityEngine.RefreshRate&)")); + + int64_t v8[3]; + v8[0] = 0x100000000LL; + Screen_SetResolution(width, height, 2 * !fullscreen + 1, v8); + } + + // DMM Only + DEFINE_HOOK(void, WindowManager_ApplyOrientationSettings, (int orientation, void* method)) { + if (!GakumasLocal::Config::dmmUnlockSize) return WindowManager_ApplyOrientationSettings_Orig(orientation, method); + + static auto get_Height = reinterpret_cast(Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Screen::get_height()")); + static auto get_Width = reinterpret_cast(Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Screen::get_width()")); + + static auto lastWidth = -1; + static auto lastHeight = -1; + + const auto currWidth = get_Width(); + const auto currHeight = get_Height(); + + if (lastWidth == -1) { + lastWidth = currWidth; + lastHeight = currHeight; + return; + } + + const bool lastIsPortrait = lastWidth < lastHeight; + const bool currIsPortrait = currWidth < currHeight; + if (lastIsPortrait == currIsPortrait) { + lastWidth = currWidth; + lastHeight = currHeight; + return; + } + + SetResolution(lastWidth, lastHeight, false); + lastWidth = currWidth; + lastHeight = currHeight; + + Log::DebugFmt("WindowManager_ApplyOrientationSettings: %d (%d, %d)\n", orientation, get_Width(), get_Height()); + } + + // DMM Only + DEFINE_HOOK(void, AspectRatioHandler_NudgeWindow, (void* method)) { + if (!GakumasLocal::Config::dmmUnlockSize) return AspectRatioHandler_NudgeWindow_Orig(method); + // printf("AspectRatioHandler_NudgeWindow\n"); + } +#endif void UpdateSwingBreastBonesData(void* initializeData) { if (!Config::enableBreastParam) return; @@ -1444,6 +1528,9 @@ namespace GakumasLocal::HookMain { ADD_HOOK(TMP_Text_PopulateTextBackingArray, Il2cppUtils::GetMethodPointer("Unity.TextMeshPro.dll", "TMPro", "TMP_Text", "PopulateTextBackingArray", {"System.String", "System.Int32", "System.Int32"})); + ADD_HOOK(TMP_Text_SetText_2, Il2cppUtils::GetMethodPointer("Unity.TextMeshPro.dll", "TMPro", + "TMP_Text", "SetText", + { "System.String", "System.Boolean" })); ADD_HOOK(TextField_set_value, Il2cppUtils::GetMethodPointer("UnityEngine.UIElementsModule.dll", "UnityEngine.UIElements", "TextField", "set_value")); @@ -1634,6 +1721,32 @@ namespace GakumasLocal::HookMain { "RenderPipeline", "EndCameraRendering")); #ifdef GKMS_WINDOWS + ADD_HOOK(WindowHandle_SetWindowLong, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow", + "WindowHandle", "SetWindowLong")); + //ADD_HOOK(WindowHandle_SetWindowLong32, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow", + // "WindowHandle", "SetWindowLong32")); + //ADD_HOOK(WindowHandle_SetWindowLongPtr64, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow", + // "WindowHandle", "SetWindowLongPtr64")); + //ADD_HOOK(WindowSizeUtility_RestoreWindowSize, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow", + // "WindowSizeUtility", "RestoreWindowSize")); + ADD_HOOK(WindowManager_ApplyOrientationSettings, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow", + "WindowManager", "ApplyOrientationSettings")); + ADD_HOOK(AspectRatioHandler_NudgeWindow, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow", + "AspectRatioHandler", "NudgeWindow")); + //ADD_HOOK(AspectRatioHandler_WindowProc, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow", + // "AspectRatioHandler", "WindowProc")); + + if (GakumasLocal::Config::dmmUnlockSize) { + std::thread([]() { + std::this_thread::sleep_for(std::chrono::seconds(3)); + auto hWnd = FindWindowW(L"UnityWndClass", L"gakumas"); + // 添加可调整大小的边框和最大化按钮 + LONG style = GetWindowLong(hWnd, GWL_STYLE); + style |= WS_THICKFRAME | WS_MAXIMIZEBOX; + SetWindowLong(hWnd, GWL_STYLE, style); + }).detach(); + } + g_extra_assetbundle_paths.push_back((gakumasLocalPath / "local-files/gakumasassets").string()); LoadExtraAssetBundle(); GkmsResourceUpdate::CheckUpdateFromAPI(false); diff --git a/src/GakumasLocalify/Local.cpp b/src/GakumasLocalify/Local.cpp index 7a4f0b9..26ab689 100644 --- a/src/GakumasLocalify/Local.cpp +++ b/src/GakumasLocalify/Local.cpp @@ -17,6 +17,8 @@ #include "BaseDefine.h" #include "string_parser/StringParser.hpp" +#include "cpprest/details/http_helpers.h" + namespace GakumasLocal::Local { std::unordered_map i18nData{}; diff --git a/src/GakumasLocalify/config/Config.cpp b/src/GakumasLocalify/config/Config.cpp index f8dd59a..57ea8e3 100644 --- a/src/GakumasLocalify/config/Config.cpp +++ b/src/GakumasLocalify/config/Config.cpp @@ -54,6 +54,8 @@ namespace GakumasLocal::Config { float bLimitZx = 1.0f; float bLimitZy = 1.0f; + bool dmmUnlockSize = false; + void LoadConfig(const std::string& configStr) { try { const auto config = nlohmann::json::parse(configStr); @@ -102,6 +104,7 @@ namespace GakumasLocal::Config { GetConfigItem(bLimitYy); GetConfigItem(bLimitZx); GetConfigItem(bLimitZy); + GetConfigItem(dmmUnlockSize); } catch (std::exception& e) { Log::ErrorFmt("LoadConfig error: %s", e.what()); @@ -157,6 +160,7 @@ namespace GakumasLocal::Config { SetConfigItem(bLimitYy); SetConfigItem(bLimitZx); SetConfigItem(bLimitZy); + SetConfigItem(dmmUnlockSize); std::ofstream out(configPath); if (!out) { diff --git a/src/GakumasLocalify/config/Config.hpp b/src/GakumasLocalify/config/Config.hpp index 49f2a67..1147f53 100644 --- a/src/GakumasLocalify/config/Config.hpp +++ b/src/GakumasLocalify/config/Config.hpp @@ -51,6 +51,8 @@ namespace GakumasLocal::Config { extern float bLimitZx; extern float bLimitZy; + extern bool dmmUnlockSize; + void LoadConfig(const std::string& configStr); void SaveConfig(const std::string& configPath); } diff --git a/src/gkmsGUI/gkmsGUILoop.cpp b/src/gkmsGUI/gkmsGUILoop.cpp index 8507e17..a4fc227 100644 --- a/src/gkmsGUI/gkmsGUILoop.cpp +++ b/src/gkmsGUI/gkmsGUILoop.cpp @@ -222,6 +222,10 @@ namespace GkmsGUILoop { ImGui::RadioButton(ts("orientation_portrait"), &Config::gameOrientation, 1); ImGui::SameLine(); ImGui::RadioButton(ts("orientation_landscape"), &Config::gameOrientation, 2); + ImGui::SameLine(); + ImGui::Checkbox(ts("dmmUnlockSize"), &Config::dmmUnlockSize); + ImGui::SameLine(); + HELP_TOOLTIP("(?)", ts("dmmUnlockSizeHelp")); } if (ImGui::CollapsingHeader((std::string(ts("useCustomeGraphicSettings")) + "##customeGraphicSettings1").c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { diff --git a/src/gkmsGUI/i18nData/strings_en.hpp b/src/gkmsGUI/i18nData/strings_en.hpp index 407d4ce..08153dc 100644 --- a/src/gkmsGUI/i18nData/strings_en.hpp +++ b/src/gkmsGUI/i18nData/strings_en.hpp @@ -6,6 +6,8 @@ namespace I18nData { static const std::unordered_map i18nData_default = { { "local_file_already_latest", "The local file is the latest version. Do you want to update it?" }, + { "dmmUnlockSize", "Unlock Window Size" }, + { "dmmUnlockSizeHelp", "You can freely resize the window. Press F11 to toggle fullscreen." }, { "app_name", "Gakumas Localify" }, { "gakumas_localify", "Gakumas Localify" }, diff --git a/src/gkmsGUI/i18nData/strings_ja.hpp b/src/gkmsGUI/i18nData/strings_ja.hpp index 6661a63..779aedd 100644 --- a/src/gkmsGUI/i18nData/strings_ja.hpp +++ b/src/gkmsGUI/i18nData/strings_ja.hpp @@ -6,6 +6,8 @@ namespace I18nData { static const std::unordered_map i18nData_ja = { { "local_file_already_latest", "ローカルファイルはすでに最新バージョンです。それでも更新を続けますか?" }, + { "dmmUnlockSize", "ウィンドウサイズの制限を解除" }, + { "dmmUnlockSizeHelp", "ウィンドウサイズは自由に変更できます。F11キーで全画面表示を切り替えます。" }, { "about", "情報" }, { "about_about_p1", "このプラグインは完全に無料で提供されます。このプラグインで料金を支払ってしまった場合は、販売者に報告をしてください。" }, diff --git a/src/gkmsGUI/i18nData/strings_zh-rCN.hpp b/src/gkmsGUI/i18nData/strings_zh-rCN.hpp index 619b5ad..4601e9b 100644 --- a/src/gkmsGUI/i18nData/strings_zh-rCN.hpp +++ b/src/gkmsGUI/i18nData/strings_zh-rCN.hpp @@ -6,6 +6,8 @@ namespace I18nData { static const std::unordered_map i18nData_zh_rCN = { { "local_file_already_latest", "本地文件已经是最新版本,是否继续更新?" }, + { "dmmUnlockSize", "解锁窗口大小" }, + { "dmmUnlockSizeHelp", "可随意拖动窗口大小。使用F11切换全屏。" }, { "app_name", "Gakumas Localify" }, { "gakumas_localify", "Gakumas Localify" }, diff --git a/src/platformDefine.hpp b/src/platformDefine.hpp index dc0487b..bc04413 100644 --- a/src/platformDefine.hpp +++ b/src/platformDefine.hpp @@ -11,7 +11,7 @@ #define LogMinVersion ANDROID_LOG_DEBUG -#define PLUGIN_VERSION "3.0.1" +#define PLUGIN_VERSION "3.0.2" #define ADD_HOOK(name, addr) \ name##_Addr = reinterpret_cast(addr); \ diff --git a/src/windowsPlatform.cpp b/src/windowsPlatform.cpp index 1097400..7f765e8 100644 --- a/src/windowsPlatform.cpp +++ b/src/windowsPlatform.cpp @@ -218,6 +218,36 @@ namespace GakumasLocal::WinHooks { return get_assetBundle->Invoke(bundleTask); } + void SwitchFullScreen(HWND hWnd) { + static auto Screen_SetResolution = reinterpret_cast( + Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Screen::SetResolution_Injected(System.Int32,System.Int32,UnityEngine.FullScreenMode,UnityEngine.RefreshRate&)")); + static auto get_Height = reinterpret_cast(Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Screen::get_height()")); + static auto get_Width = reinterpret_cast(Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Screen::get_width()")); + + LONG style = GetWindowLong(hWnd, GWL_STYLE); + bool currFullScreen = style & WS_POPUP; + + static int savedWidth = -1; + static int savedHeight = -1; + + int64_t v8[3]; + v8[0] = 0x100000000LL; + + if (currFullScreen) { + // ȡȫ + if (savedWidth == -1) { + savedWidth = 542; + savedHeight = 990; + } + Screen_SetResolution(savedWidth, savedHeight, 2 * !false + 1, v8); + } + else { + savedWidth = get_Width(); + savedHeight = get_Height(); + Screen_SetResolution(GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), 2 * !true + 1, v8); + } + } + namespace Keyboard { std::function mKeyBoardCallBack = nullptr; @@ -232,6 +262,8 @@ namespace GakumasLocal::WinHooks { DWORD LEFT_key = 0; DWORD RIGHT_key = 0; + // printf("WndProcCallback: 0x%x (%d)\n", uMsg, uMsg); + switch (uMsg) { case WM_INPUT: { RAWINPUT rawInput; @@ -291,7 +323,13 @@ namespace GakumasLocal::WinHooks { if (mKeyBoardCallBack != nullptr) { mKeyBoardCallBack(key, SHIFT_key, CTRL_key, ALT_key, SPACE_key, UP_key, DOWN_key, LEFT_key, RIGHT_key); } - + + if (key == 122) { + // F11 + if (GakumasLocal::Config::dmmUnlockSize) { + SwitchFullScreen(hWnd); + } + } if (key >= 'A' && key <= 'Z') { @@ -327,30 +365,70 @@ namespace GakumasLocal::WinHooks { case WM_CLOSE: { if (g_on_close) g_on_close(); }; break; + case WM_NCHITTEST: { - POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; - ScreenToClient(hWnd, &pt); - RECT rcClient; - GetClientRect(hWnd, &rcClient); - const int borderWidth = 8; // ҪԵ + if (GakumasLocal::Config::dmmUnlockSize) { + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + ScreenToClient(hWnd, &pt); + RECT rcClient; + GetClientRect(hWnd, &rcClient); + const int borderWidth = 8; // ҪԵ - bool left = pt.x < borderWidth; - bool right = pt.x >= rcClient.right - borderWidth; - bool top = pt.y < borderWidth; - bool bottom = pt.y >= rcClient.bottom - borderWidth; + bool left = pt.x < borderWidth; + bool right = pt.x >= rcClient.right - borderWidth; + bool top = pt.y < borderWidth; + bool bottom = pt.y >= rcClient.bottom - borderWidth; - if (top && left) return HTTOPLEFT; - if (top && right) return HTTOPRIGHT; - if (bottom && left) return HTBOTTOMLEFT; - if (bottom && right) return HTBOTTOMRIGHT; - if (left) return HTLEFT; - if (right) return HTRIGHT; - if (top) return HTTOP; - if (bottom) return HTBOTTOM; - return HTCLIENT; + if (top && left) return HTTOPLEFT; + if (top && right) return HTTOPRIGHT; + if (bottom && left) return HTBOTTOMLEFT; + if (bottom && right) return HTBOTTOMRIGHT; + if (left) return HTLEFT; + if (right) return HTRIGHT; + if (top) return HTTOP; + if (bottom) return HTBOTTOM; + return HTCLIENT; + } } break; + case WM_GETMINMAXINFO: + { + if (GakumasLocal::Config::dmmUnlockSize) { + LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam; + // ߴΪĻֱʣͲƴڵߴ + lpMMI->ptMaxTrackSize.x = GetSystemMetrics(SM_CXSCREEN) * 3; + lpMMI->ptMaxTrackSize.y = GetSystemMetrics(SM_CYSCREEN) * 3; + // ѡôСߴ磨200x200 + lpMMI->ptMinTrackSize.x = 200; + lpMMI->ptMinTrackSize.y = 200; + return 1; + } + } break; + + case WM_SYSCOMMAND: { + if (GakumasLocal::Config::dmmUnlockSize) { + if ((wParam & 0xFFF0) == SC_MAXIMIZE) { + SwitchFullScreen(hWnd); + return 1; + } + } + } break; + + case WM_NCPAINT: { + if (GakumasLocal::Config::dmmUnlockSize) { + LONG style = GetWindowLong(hWnd, GWL_STYLE); + // printf("WM_NCPAINT: 0x%x\n", style); + + if (!(style & WS_POPUP)) { + // ӿɵСı߿󻯰ť + style |= WS_THICKFRAME | WS_MAXIMIZEBOX; + SetWindowLong(hWnd, GWL_STYLE, style); + } + } + + } break; + default: break; } @@ -358,17 +436,13 @@ namespace GakumasLocal::WinHooks { } void InstallWndProcHook() { - g_pfnOldWndProc = (WNDPROC)GetWindowLongPtr(FindWindowW(L"UnityWndClass", L"gakumas"), GWLP_WNDPROC); + auto hWnd = FindWindowW(L"UnityWndClass", L"gakumas"); + g_pfnOldWndProc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_WNDPROC); SetWindowLongPtr(FindWindowW(L"UnityWndClass", L"gakumas"), GWLP_WNDPROC, (LONG_PTR)WndProcCallback); - - HWND hwnd = FindWindowW(L"UnityWndClass", L"gakumas"); - LONG_PTR style = GetWindowLongPtr(hwnd, GWL_STYLE); - // ϿɵСϵͳ˵ʽ - style |= WS_OVERLAPPEDWINDOW; - SetWindowLongPtr(hwnd, GWL_STYLE, style); - // ֪ͨϵͳʽѸı - SetWindowPos(hwnd, NULL, 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); + // ӿɵСı߿󻯰ť + LONG style = GetWindowLong(hWnd, GWL_STYLE); + style |= WS_THICKFRAME | WS_MAXIMIZEBOX; + SetWindowLong(hWnd, GWL_STYLE, style); } void UninstallWndProcHook(HWND hWnd)