diff --git a/.gitattributes b/.gitattributes index 11b4a149..7f742e00 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1 @@ resources/schinese/localized_data/umamusumelocalify filter=lfs diff=lfs merge=lfs -text -*.dll filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e14880de..17fc83d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,7 +56,6 @@ jobs: run: | mkdir package cp build/bin/x64/Release/version.dll package/tlg.dll - cp version/version.dll package/version.dll cp -r resources/schinese/localized_data package/localized_data cp resources/config.json package/config.json mkdir package/localized_data/config_schema @@ -69,6 +68,8 @@ jobs: cp resources/text_data_info_i18n_zh_tw.json package/localized_data/config_schema/text_data_info_i18n_zh_tw.json cp resources/text_data_info_i18n_ja.json package/localized_data/config_schema/text_data_info_i18n_ja.json cp resources/legend_g_plugin.exe package/legend_g_plugin.exe.autoupdate + cp resources/loader.dll package/loader.dll + cp resources/tlg_starter.exe package/tlg_starter.exe cp -r resources/eventHelper package/localized_data/eventHelper_examples - uses: actions/upload-artifact@v4 with: diff --git a/resources/legend_g_plugin.exe b/resources/legend_g_plugin.exe index 9413cedd..10862b10 100644 Binary files a/resources/legend_g_plugin.exe and b/resources/legend_g_plugin.exe differ diff --git a/resources/loader.dll b/resources/loader.dll new file mode 100644 index 00000000..b07cb019 Binary files /dev/null and b/resources/loader.dll differ diff --git a/resources/tlg_starter.exe b/resources/tlg_starter.exe new file mode 100644 index 00000000..c3fce7df Binary files /dev/null and b/resources/tlg_starter.exe differ diff --git a/src/eventHelper/eventHelper.cpp b/src/eventHelper/eventHelper.cpp index ca014ea2..dcb5e5ab 100644 --- a/src/eventHelper/eventHelper.cpp +++ b/src/eventHelper/eventHelper.cpp @@ -48,7 +48,7 @@ namespace EventHelper { const auto localLang = GetUserDefaultUILanguage(); - std::filesystem::path dataPath = "localized_data/eventHelper"; + std::filesystem::path dataPath = DLL_DIR / "localized_data/eventHelper"; if (sChineseLangIds.contains(localLang)) { systemLang = L"scn"; dataPath /= "events_scn.json"; diff --git a/src/hook.cpp b/src/hook.cpp index aaefc4b5..7f751ff0 100644 --- a/src/hook.cpp +++ b/src/hook.cpp @@ -15,6 +15,7 @@ bool raceFollowUmaFirstPersonEnableRoll = false; std::function showDialog = nullptr; bool guiStarting = false; void (*testFunction)() = nullptr; +bool gameClosing = false; void _set_u_stat(bool s) { if (autoChangeLineBreakMode) { @@ -69,7 +70,7 @@ namespace { // GameAssembly.dll code must be loaded and decrypted while loading criware library if (path == L"cri_ware_unity.dll"sv) - { + { path_game_assembly(); if (g_on_hook_ready) { @@ -78,7 +79,7 @@ namespace MH_DisableHook(LoadLibraryW); MH_RemoveHook(LoadLibraryW); - + // use original function beacuse we have unhooked that return LoadLibraryW(path); } @@ -422,9 +423,7 @@ namespace std::unordered_map> text_queries; - void* query_setup_orig = nullptr; - void* query_setup_hook(void* _this, void* conn, Il2CppString* sql) - { + void parseQuery(void* queryInstance, Il2CppString* sql) { static const std::wregex statementPattern(LR"(SELECT (.+?) FROM `(.+?)`(?: WHERE (.+))?;)"); static const std::wregex columnPattern(LR"(,?`(\w+)`)"); static const std::wregex whereClausePattern(LR"((?:AND )?`(\w+)=?`)"); @@ -456,7 +455,8 @@ namespace } else { - goto NormalPath; + // goto NormalPath; + return; } auto columnsPtr = columns.c_str(); @@ -483,13 +483,31 @@ namespace } } - text_queries.emplace(_this, std::move(query)); + text_queries.emplace(queryInstance, std::move(query)); } + } - NormalPath: + void* query_setup_orig = nullptr; + void* query_setup_hook(void* _this, void* conn, Il2CppString* sql) + { + parseQuery(_this, sql); return reinterpret_cast(query_setup_orig)(_this, conn, sql); } + void* Connection_Query_orig; + void* Connection_Query_hook(void* _this, Il2CppString* sql) { + auto ret = reinterpret_cast(Connection_Query_orig)(_this, sql); + parseQuery(ret, sql); + return ret; + } + + void* Connection_PreparedQuery_orig; + void* Connection_PreparedQuery_hook(void* _this, Il2CppString* sql) { + auto ret = reinterpret_cast(Connection_PreparedQuery_orig)(_this, sql); + parseQuery(ret, sql); + return ret; + } + void* query_dispose_orig = nullptr; void query_dispose_hook(void* _this) { @@ -509,7 +527,17 @@ namespace return localizedStr; } } + /* + auto ret = reinterpret_cast(query_getstr_orig)(_this, idx); + std::wstring target = L"もうすぐイベントだよ!\r\nワクワク…!ワクワク…!"; + if (ret) { + std::wstring origRet(ret->start_char); + if (origRet == target) { + wprintf(L"query_getstr_hook: %d - %ls\n%ls\n\n", idx, origRet.c_str(), environment_get_stacktrace()->start_char); + } + } + return ret;*/ return reinterpret_cast(query_getstr_orig)(_this, idx); } @@ -637,7 +665,7 @@ namespace void* Get3DAntiAliasingLevel_orig; int Get3DAntiAliasingLevel_hook(void* _this, bool allowMSAA) { - if (g_antialiasing != -1) allowMSAA = true; + // if (g_antialiasing != -1) allowMSAA = true; auto data = reinterpret_cast(Get3DAntiAliasingLevel_orig)(_this, allowMSAA); // printf("Get3DAntiAliasingLevel: %d %d\n", allowMSAA, data); return data; @@ -891,7 +919,6 @@ namespace void* wndproc_orig = nullptr; bool raceStart = false; - bool gameClosing = false; LRESULT wndproc_hook(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == WM_INPUT) @@ -1340,8 +1367,9 @@ namespace } if (g_replace_assets) { - if (std::filesystem::exists(localPath)) { - auto replaceT2D = getLocalT2D(localPath); + const auto localReadPath = DLL_DIR / localPath; + if (std::filesystem::exists(localReadPath)) { + auto replaceT2D = getLocalT2D(localReadPath); if (replaceT2D) { result = replaceT2D; } @@ -1484,7 +1512,7 @@ namespace static auto ImageConversion_EncodeToPNG_mtd = il2cpp_symbols::get_method( "UnityEngine.ImageConversionModule.dll", "UnityEngine", "ImageConversion", "EncodeToPNG", 1); - const std::filesystem::path baseDumpPath = "localized_data/TextureDump"; + const std::filesystem::path baseDumpPath = DLL_DIR / "localized_data/TextureDump"; std::string textureName = utility::conversions::to_utf8string(get_ObjectName(texture)->start_char); replaceAll(textureName, "|", "_"); @@ -1574,7 +1602,7 @@ namespace if (g_replace_assets) { auto object_name = get_ObjectName(texture2D); - static std::filesystem::path baseSearchPath = "localized_data/res/texture2d"; + static std::filesystem::path baseSearchPath = DLL_DIR / "localized_data/res/texture2d"; if (object_name) { const std::wstring objName(object_name->start_char); @@ -4175,7 +4203,7 @@ namespace if (g_read_request_pack && g_save_msgpack) { const auto outPath = std::format("MsgPack/{}Q.msgpack", currentTime()); - writeFile(outPath, src, srcSize); + writeFile((DLL_DIR / outPath).string(), src, srcSize); printf("Save request to %s\n", outPath.c_str()); } @@ -4200,7 +4228,7 @@ namespace if (g_read_request_pack && g_save_msgpack) { const string outPath = std::format("MsgPack/{}R.msgpack", currentTime()); - writeFile(outPath, dst, ret); + writeFile((DLL_DIR / outPath).string(), dst, ret); printf("Save response to %s\n", outPath.c_str()); } @@ -4424,7 +4452,7 @@ namespace /*dump_bytes(_name_##_offset); */ \ \ MH_CreateHook(_name_##_offset, _name_##_hook, &_name_##_orig); \ - MH_EnableHook(_name_##_offset); + MH_EnableHook(_name_##_offset); #pragma endregion #pragma region HOOK_ADDRESSES auto populate_with_errors_addr = il2cpp_symbols::get_method_pointer( @@ -4447,6 +4475,16 @@ namespace "Query", "_Setup", 2 ); + auto Connection_Query_addr = il2cpp_symbols::get_method_pointer( + "LibNative.Runtime.dll", "LibNative.Sqlite3", + "Connection", "Query", 1 + ); + + auto Connection_PreparedQuery_addr = il2cpp_symbols::get_method_pointer( + "LibNative.Runtime.dll", "LibNative.Sqlite3", + "Connection", "PreparedQuery", 1 + ); + auto query_getstr_addr = il2cpp_symbols::get_method_pointer( "LibNative.Runtime.dll", "LibNative.Sqlite3", "Query", "GetText", 1 @@ -5241,6 +5279,8 @@ namespace ADD_HOOK(localize_jp_get, "Gallop.Localize.JP.Get(TextId) at %p\n"); ADD_HOOK(on_exit, "Gallop.GameSystem.onApplicationQuit at %p\n"); ADD_HOOK(query_setup, "Query::_Setup at %p\n"); + ADD_HOOK(Connection_Query, "Connection_Query at %p\n"); + ADD_HOOK(Connection_PreparedQuery, "Connection_PreparedQuery at %p\n"); ADD_HOOK(query_getstr, "Query::GetString at %p\n"); ADD_HOOK(query_dispose, "Query::Dispose at %p\n"); ADD_HOOK(PreparedQuery_BindInt, "PreparedQuery::BindInt at %p\n"); @@ -5534,8 +5574,17 @@ bool init_hook() mh_inited = true; onPluginReload.push_back(reloadAssetBundle); - MH_CreateHook(LoadLibraryW, load_library_w_hook, &load_library_w_orig); - MH_EnableHook(LoadLibraryW); + auto cri_ware_handle = GetModuleHandleW(L"cri_ware_unity.dll"); + if (cri_ware_handle) { + std::thread([]() { + load_library_w_hook(L"cri_ware_unity.dll"); + }).detach(); + } + else { + MH_CreateHook(LoadLibraryW, load_library_w_hook, &load_library_w_orig); + MH_EnableHook(LoadLibraryW); + } + return true; } diff --git a/src/imgui/imgui.cpp b/src/imgui/imgui.cpp index fa8580cd..85547c82 100644 --- a/src/imgui/imgui.cpp +++ b/src/imgui/imgui.cpp @@ -974,6 +974,8 @@ CODE #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead #endif +#include + // Debug options #define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL #define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window @@ -1189,6 +1191,11 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor) MouseCursorScale = ImFloor(MouseCursorScale * scale_factor); } +ImGuiIO::~ImGuiIO() { + free((void*)IniFilename); + free((void*)LogFilename); +} + ImGuiIO::ImGuiIO() { // Most fields are initialized with zero @@ -1201,8 +1208,8 @@ ImGuiIO::ImGuiIO() DisplaySize = ImVec2(-1.0f, -1.0f); DeltaTime = 1.0f / 60.0f; IniSavingRate = 5.0f; - IniFilename = "imgui.ini"; // Important: "imgui.ini" is relative to current working dir, most apps will want to lock this to an absolute path (e.g. same path as executables). - LogFilename = "imgui_log.txt"; + IniFilename = strdup((DLL_DIR / "imgui.ini").string().c_str()); // Important: "imgui.ini" is relative to current working dir, most apps will want to lock this to an absolute path (e.g. same path as executables). + LogFilename = strdup((DLL_DIR / "imgui_log.txt").string().c_str()); MouseDoubleClickTime = 0.30f; MouseDoubleClickMaxDist = 6.0f; #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO diff --git a/src/imgui/imgui.h b/src/imgui/imgui.h index beb4feb1..9f08f610 100644 --- a/src/imgui/imgui.h +++ b/src/imgui/imgui.h @@ -1900,6 +1900,7 @@ struct ImGuiKeyData struct ImGuiIO { + ~ImGuiIO(); //------------------------------------------------------------------ // Configuration // Default value //------------------------------------------------------------------ diff --git a/src/logger/logger.cpp b/src/logger/logger.cpp index 570f500e..cd8eb801 100644 --- a/src/logger/logger.cpp +++ b/src/logger/logger.cpp @@ -47,7 +47,7 @@ namespace logger void open_test_log_file() { test_log_opened = true; - test_log_file.open("legendtest.log", ios::app | ios::out); + test_log_file.open(DLL_DIR / "legendtest.log", ios::app | ios::out); } void write_test_log(wstring text) { @@ -73,7 +73,7 @@ namespace logger if (g_enable_logger) { enabled = true; - log_file.open("dump.txt", ios::app | ios::out); + log_file.open(DLL_DIR / "dump.txt", ios::app | ios::out); thread t([]() { while (!request_exit) diff --git a/src/main.cpp b/src/main.cpp index 4b9acb27..836b4ed5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -108,15 +108,17 @@ bool g_upload_gacha_history = false; std::wstring g_upload_gacha_history_endpoint = L""; bool g_enable_event_helper = false; -constexpr const char LocalizedDataPath[] = "localized_data"; -constexpr const char OldLocalizedDataPath[] = "old_localized_data"; -constexpr const char ConfigJson[] = "config.json"; -constexpr const char VersionDll[] = "version.dll"; -constexpr const char VersionDllTmp[] = "version.dll.tmp"; -constexpr const char StaticDictCache[] = "static_cache.json"; -constexpr const char StaticDictCachePath[] = "localized_data/static_cache.json"; -constexpr const char StaticDictStamp[] = "static_cache.stamp"; -constexpr const char StaticDictStampPath[] = "localized_data/static_cache.stamp"; +std::filesystem::path DLL_DIR = "."; + +char LocalizedDataPath[MAX_PATH] = "localized_data"; +char OldLocalizedDataPath[MAX_PATH] = "old_localized_data"; +char ConfigJson[MAX_PATH] = "config.json"; +char VersionDll[MAX_PATH] = "version.dll"; +char VersionDllTmp[MAX_PATH] = "version.dll.tmp"; +char StaticDictCache[MAX_PATH] = "static_cache.json"; +char StaticDictCachePath[MAX_PATH] = "localized_data/static_cache.json"; +char StaticDictStamp[MAX_PATH] = "static_cache.stamp"; +char StaticDictStampPath[MAX_PATH] = "localized_data/static_cache.stamp"; char open_plugin_hotkey = 'u'; bool openExternalPluginOnLoad = false; @@ -129,6 +131,7 @@ CloseTrans closeTrans{false}; std::unordered_set trans_off_textData{}; std::string dumpGameAssemblyPath; +extern bool gameClosing; // #pragma comment(lib, "cpprest_2_10_18.lib") // #pragma comment(lib, "bcrypt.lib") @@ -343,7 +346,7 @@ namespace void dump_static_dict(const std::filesystem::path& outputPath, const std::map& currentStaticCache) { - std::ofstream output(outputPath); + std::ofstream output(DLL_DIR / outputPath); if (!output.is_open()) { return; @@ -430,7 +433,7 @@ namespace std::vector read_config() { MHotkey::setUmaCommandLine(GetCommandLineA()); - std::ifstream config_stream{ ConfigJson }; + std::ifstream config_stream{ DLL_DIR / ConfigJson }; std::vector dicts{}; if (!config_stream.is_open()) @@ -468,7 +471,7 @@ namespace const auto& extraAssetBundlePath = document["extraAssetBundlePath"]; if (extraAssetBundlePath.IsString()) { - g_extra_assetbundle_paths.push_back(extraAssetBundlePath.GetString()); + g_extra_assetbundle_paths.push_back((DLL_DIR / extraAssetBundlePath.GetString()).string()); } } @@ -477,7 +480,7 @@ namespace if (extraAssetBundlePaths.IsArray()) { for (const auto& i : document["extraAssetBundlePaths"].GetArray()) { - g_extra_assetbundle_paths.push_back(i.GetString()); + g_extra_assetbundle_paths.push_back((DLL_DIR / i.GetString()).string()); } } } @@ -519,6 +522,9 @@ namespace if (document.HasMember("externalPlugin")) { open_plugin_hotkey = document["externalPlugin"]["hotkey"].GetString()[0]; externalPluginPath = document["externalPlugin"]["path"].GetString(); + if (!externalPluginPath.contains(' ')) { + externalPluginPath = (DLL_DIR / document["externalPlugin"]["path"].GetString()).string(); + } MHotkey::setExtPluginPath(externalPluginPath); MHotkey::start_hotkey(open_plugin_hotkey); // 启动热键监听进程 @@ -730,7 +736,7 @@ namespace loadDllList.clear(); for (const auto& fr : document["loadDll"].GetArray()) { if (fr.IsString()) { - loadDllList.push_back(fr.GetString()); + loadDllList.push_back((DLL_DIR / fr.GetString()).string()); } } } @@ -767,18 +773,18 @@ namespace { auto dict = dicts_arr[i].GetString(); - dicts.push_back(dict); + dicts.push_back((DLL_DIR / dict).string()); } - g_static_dict_path = document["static_dict"].GetString(); + g_static_dict_path = (DLL_DIR / document["static_dict"].GetString()).string(); g_no_static_dict_cache = document["no_static_dict_cache"].GetBool(); - g_stories_path = document["stories_path"].GetString(); + g_stories_path = (DLL_DIR / document["stories_path"].GetString()).string(); - g_text_data_dict_path = document["text_data_dict"].GetString(); - g_character_system_text_dict_path = document["character_system_text_dict"].GetString(); - g_race_jikkyo_comment_dict_path = document["race_jikkyo_comment_dict"].GetString(); - g_race_jikkyo_message_dict_path = document["race_jikkyo_message_dict"].GetString(); + g_text_data_dict_path = (DLL_DIR / document["text_data_dict"].GetString()).string(); + g_character_system_text_dict_path = (DLL_DIR / document["character_system_text_dict"].GetString()).string(); + g_race_jikkyo_comment_dict_path = (DLL_DIR / document["race_jikkyo_comment_dict"].GetString()).string(); + g_race_jikkyo_message_dict_path = (DLL_DIR / document["race_jikkyo_message_dict"].GetString()).string(); if (document.HasMember("enableBuiltinAutoUpdate")) { if (document.HasMember("autoUpdate")) @@ -1050,7 +1056,7 @@ std::tuple dicts{}; - const std::string staticDictPath = document["static_dict"].GetString(); + const std::string staticDictPath = (DLL_DIR / document["static_dict"].GetString()).string(); g_static_dict_path = staticDictPath; auto staticDictCache = ensure_latest_static_cache(staticDictPath); @@ -1072,7 +1078,7 @@ namespace { { auto dict = dicts_arr[i].GetString(); - dicts.push_back(dict); + dicts.push_back((DLL_DIR/ dict).string()); } auto&& [storyDict, raceDict] = LoadStories(); @@ -1320,7 +1326,7 @@ namespace { return true; } - + void auto_update() { constexpr const char AutoUpdateTmpPath[] = "UpdateTemp"; @@ -1768,6 +1774,37 @@ namespace HttpServer { } + if (path == L"/fast_reboot") { + gameClosing = true; + auto rebootScript = message.extract_utf8string(true).get(); + + std::ofstream rebootFile("reboot.bat"); + if (rebootFile.is_open()) { + rebootFile.write(rebootScript.c_str(), rebootScript.size()); + rebootFile.close(); + + wchar_t commandLine[] = L"cmd.exe /c reboot.bat"; + STARTUPINFOW startupInfo{ .cb = sizeof(STARTUPINFOW) }; + PROCESS_INFORMATION processInfo{}; + if (CreateProcessW(NULL, commandLine, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo)) { + message.reply(status_codes::OK, "OK(〃'▽'〃)"); + + CloseHandle(processInfo.hThread); + WaitForSingleObject(processInfo.hProcess, INFINITE); + CloseHandle(processInfo.hProcess); + + TerminateProcess(GetCurrentProcess(), 0); + } + else { + message.reply(status_codes::InternalError, "QWQ"); + } + } + else { + message.reply(status_codes::InternalError, "open rebootFile failed."); + } + return; + } + message.reply(status_codes::OK, "OK(〃'▽'〃)"); } catch (std::exception& ex) @@ -1793,7 +1830,10 @@ namespace HttpServer { MHotkey::setTlgPort(port); if (openExternalPlugin && openExternalPluginOnLoad) { // 打开外部插件 - MHotkey::fopenExternalPlugin(http_start_port); + std::thread([]() { + Sleep(5000); + MHotkey::fopenExternalPlugin(http_start_port); + }).detach(); } ucout << utility::string_t(U("Server Start at: ")) << addr << std::endl; @@ -1807,7 +1847,10 @@ namespace HttpServer { printf("HTTP Server start failed.\n"); if (openExternalPlugin && openExternalPluginOnLoad) { // 打开外部插件 - MHotkey::fopenExternalPlugin(http_start_port); + std::thread([]() { + Sleep(5000); + MHotkey::fopenExternalPlugin(http_start_port); + }).detach(); } } else { @@ -1839,6 +1882,32 @@ namespace HttpServer { } +void updatePaths() { + strcpy(LocalizedDataPath, (DLL_DIR / LocalizedDataPath).string().c_str()); + strcpy(OldLocalizedDataPath, (DLL_DIR / OldLocalizedDataPath).string().c_str()); + strcpy(ConfigJson, (DLL_DIR / ConfigJson).string().c_str()); + strcpy(VersionDll, (DLL_DIR / VersionDll).string().c_str()); + strcpy(VersionDllTmp, (DLL_DIR / VersionDllTmp).string().c_str()); + strcpy(StaticDictCache, (DLL_DIR / StaticDictCache).string().c_str()); + strcpy(StaticDictCachePath, (DLL_DIR / StaticDictCachePath).string().c_str()); + strcpy(StaticDictStamp, (DLL_DIR / StaticDictStamp).string().c_str()); + strcpy(StaticDictStampPath, (DLL_DIR / StaticDictStampPath).string().c_str()); +} + +std::string GetDllPath(HMODULE hModule) +{ + char path[MAX_PATH] = { 0 }; + GetModuleFileName(hModule, path, MAX_PATH); + return std::string(path); +} + +std::string GetDllDirectory(HMODULE hModule) +{ + std::string dllPath = GetDllPath(hModule); + std::string::size_type pos = dllPath.find_last_of("\\/"); + return (std::string::npos == pos) ? "" : dllPath.substr(0, pos); +} + int __stdcall DllMain(HINSTANCE dllModule, DWORD reason, LPVOID) { if (reason == DLL_PROCESS_ATTACH) @@ -1854,6 +1923,9 @@ int __stdcall DllMain(HINSTANCE dllModule, DWORD reason, LPVOID) if (module_path.filename() != "umamusume.exe") return 1; + DLL_DIR = GetDllDirectory(dllModule); + updatePaths(); + std::filesystem::current_path( module_path.parent_path() ); diff --git a/src/mhotkey.cpp b/src/mhotkey.cpp index b35280ed..d1df35b4 100644 --- a/src/mhotkey.cpp +++ b/src/mhotkey.cpp @@ -8,8 +8,12 @@ #include #include #include +#include +#include "cpprest/details/basic_types.h" +#include "cpprest/details/http_helpers.h" extern void (*testFunction)(); +extern std::filesystem::path DLL_DIR; namespace MHotkey{ namespace { @@ -41,7 +45,7 @@ namespace MHotkey{ mKeyBoardCallBack = callbackfun; } - void fopenExternalPlugin(int tlgPort) { + void fopenExternalPlugin_Legacy(int tlgPort) { std::thread([tlgPort](){ if (openPluginSuccess) { printf("External plugin is already open.\n"); @@ -67,7 +71,7 @@ namespace MHotkey{ } } - std::string cmdLine = std::format("{} {} --tlgport={}", extPluginPath, MHotkey::umaArgs, tlgPort); + std::string cmdLine = std::format("{} {} --tlgport={} --resource_path=\"{}\"", extPluginPath, MHotkey::umaArgs, tlgPort, DLL_DIR.string()); char* commandLine = new char[255]; strcpy(commandLine, cmdLine.c_str()); @@ -90,10 +94,128 @@ namespace MHotkey{ ext_server_start = false; }).detach(); } + + DWORD GetProcessIdByName(const std::wstring& processName) + { + DWORD pid = 0; + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnapshot == INVALID_HANDLE_VALUE) return 0; + + PROCESSENTRY32W pe = { 0 }; + pe.dwSize = sizeof(pe); + if (Process32FirstW(hSnapshot, &pe)) { + do { + if (_wcsicmp(pe.szExeFile, processName.c_str()) == 0) { + pid = pe.th32ProcessID; + break; + } + } while (Process32NextW(hSnapshot, &pe)); + } + CloseHandle(hSnapshot); + return pid; + } + + + void fopenExternalPlugin(int tlgPort) { + std::thread([tlgPort]() { + if (openPluginSuccess) { + printf("External plugin is already open.\n"); + return; + } + openPluginSuccess = true; + + std::string file_check_name = std::format("{}.autoupdate", extPluginPath); + + if (MHotkey::extPluginPath == "") { + printf("\"externalPlugin\" not found\n"); + return; + } + if (std::filesystem::exists(file_check_name)) { + try { + if (std::filesystem::exists(extPluginPath)) { + std::filesystem::remove(extPluginPath); + } + std::filesystem::rename(file_check_name, extPluginPath); + } + catch (std::exception& e) { + printf("update external plugin failed: %s\n", e.what()); + } + } + + std::string cmdLine = std::format("{} {} --tlgport={} --resource_path=\"{}\"", extPluginPath, MHotkey::umaArgs, tlgPort, DLL_DIR.string()); + + const std::string PIPE_NAME = R"(\\.\pipe\TlgExtPluginPipe)"; + + std::cout << "Connecting to starter pipe..." << std::endl; + + HANDLE hPipe = CreateFile( + PIPE_NAME.c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, // + nullptr, // Ĭϰȫ + OPEN_EXISTING, + 0, + nullptr + ); + + if (hPipe == INVALID_HANDLE_VALUE) { + std::cerr << "Failed to connect to named pipe. Error: " << GetLastError() << std::endl; + printf("Run this command to open manually: %s\n", cmdLine.c_str()); + return; + } + + std::cout << "Connected to starter pipe." << std::endl; + + DWORD bytesWritten; + WriteFile(hPipe, cmdLine.c_str(), cmdLine.size(), &bytesWritten, nullptr); + + while (true) { + char buffer[1024]; + DWORD bytesRead; + if (ReadFile(hPipe, buffer, sizeof(buffer) - 1, &bytesRead, nullptr)) { + buffer[bytesRead] = '\0'; + // std::cout << "Received from server: " << buffer << std::endl; + + try { + int getPid = std::stoi(buffer); + if (getPid > 0) { + pluginPID = getPid; + printf("open external plugin: %s (%lu)\n", cmdLine.c_str(), pluginPID); + } + else { + printf("Open external plugin failed.\n"); + openPluginSuccess = false; + } + } + catch (std::exception& e) { + std::cerr << "Get pid failed" << std::endl; + openPluginSuccess = false; + } + + const std::string requestExitCmd = "exit"; + WriteFile(hPipe, requestExitCmd.c_str(), requestExitCmd.size(), &bytesWritten, nullptr); + break; + } + else { + std::cerr << "Read pipe failed. Error: " << GetLastError() << std::endl; + printf("Run this command to open manually: %s\n", cmdLine.c_str()); + openPluginSuccess = false; + break; + } + } + + CloseHandle(hPipe); + + // openPluginSuccess = false; + // ext_server_start = false; + }).detach(); + + // return fopenExternalPlugin_Legacy(tlgPort); + } void closeExternalPlugin() { - if (std::filesystem::exists("dontcloseext.lock")) { - std::filesystem::remove("dontcloseext.lock"); + if (std::filesystem::exists(DLL_DIR / "dontcloseext.lock")) { + std::filesystem::remove(DLL_DIR / "dontcloseext.lock"); return; } if (openPluginSuccess && pluginPID != -1) { diff --git a/src/stdinclude.hpp b/src/stdinclude.hpp index 5206dfe8..f9ca826c 100644 --- a/src/stdinclude.hpp +++ b/src/stdinclude.hpp @@ -131,6 +131,7 @@ extern int g_custom_font_style; extern float g_custom_font_linespacing; extern bool g_replace_assets; extern bool g_asset_load_log; +extern std::filesystem::path DLL_DIR; extern bool g_auto_fullscreen; extern bool g_fullscreen_block_minimization;