From 54a285b53b5bf68175bff9adc73129d75a5ea373 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Mon, 8 Mar 2021 08:37:02 -0700 Subject: [PATCH] improve hook search ux --- GUI/mainwindow.cpp | 34 +++++++++++++++++++--------------- text.cpp | 18 ++++++++++-------- texthook/hookfinder.cc | 11 +++++++---- 3 files changed, 36 insertions(+), 27 deletions(-) diff --git a/GUI/mainwindow.cpp b/GUI/mainwindow.cpp index dd9eaa6..e848db8 100644 --- a/GUI/mainwindow.cpp +++ b/GUI/mainwindow.cpp @@ -35,6 +35,7 @@ extern const char* PROCESSES; extern const char* CODE_INFODUMP; extern const char* FAILED_TO_CREATE_CONFIG_FILE; extern const char* HOOK_SEARCH_UNSTABLE_WARNING; +extern const char* HOOK_SEARCH_STARTING_VIEW_CONSOLE; extern const char* SEARCH_CJK; extern const char* SEARCH_PATTERN; extern const char* SEARCH_DURATION; @@ -99,7 +100,7 @@ namespace ThreadParam ParseTextThreadString(QString ttString) { - QStringList threadParam = ttString.split(":"); + auto threadParam = ttString.splitRef(":"); return { threadParam[1].toUInt(nullptr, 16), threadParam[2].toULongLong(nullptr, 16), threadParam[3].toULongLong(nullptr, 16), threadParam[4].toULongLong(nullptr, 16) }; } @@ -143,6 +144,13 @@ namespace return {}; } + void ViewThread(int index) + { + ui.ttCombo->setCurrentIndex(index); + ui.textOutput->setPlainText(sanitize(S((current = &Host::GetThread(ParseTextThreadString(ui.ttCombo->itemText(index))))->storage->c_str()))); + ui.textOutput->moveCursor(QTextCursor::End); + } + void AttachProcess() { QMultiHash allProcesses; @@ -170,7 +178,7 @@ namespace { if (HMODULE localeEmulator = LoadLibraryW(L"LoaderDll")) { - // see https://github.com/xupefei/Locale-Emulator/blob/aa99dec3b25708e676c90acf5fed9beaac319160/LEProc/LoaderWrapper.cs#L252 + // https://github.com/xupefei/Locale-Emulator/blob/aa99dec3b25708e676c90acf5fed9beaac319160/LEProc/LoaderWrapper.cs#L252 struct { ULONG AnsiCodePage = SHIFT_JIS; @@ -229,14 +237,11 @@ namespace } } - void FindHooks(); - void AddHook(QString hook) { if (QString hookCode = QInputDialog::getText(This, ADD_HOOK, CODE_INFODUMP, QLineEdit::Normal, hook, &ok, Qt::WindowCloseButtonHint); ok) - if (hookCode.startsWith("S") || hookCode.startsWith("/S")) FindHooks(); - else if (auto hp = HookCode::Parse(S(hookCode))) try { Host::InsertHook(selectedProcessId, hp.value()); } catch (std::out_of_range) {} - else Host::AddConsoleOutput(INVALID_CODE); + if (auto hp = HookCode::Parse(S(hookCode))) try { Host::InsertHook(selectedProcessId, hp.value()); } catch (std::out_of_range) {} + else Host::AddConsoleOutput(INVALID_CODE); } void AddHook() @@ -334,7 +339,11 @@ namespace layout.addRow(&confirm); if (!dialog.exec()) return; wcsncpy_s(sp.text, S(textEdit.text()).c_str(), PATTERN_SIZE - 1); - try { Host::FindHooks(selectedProcessId, sp); } catch (std::out_of_range) {} + try + { + Host::FindHooks(selectedProcessId, sp); + ViewThread(0); + } catch (std::out_of_range) {} return; } @@ -404,6 +413,7 @@ namespace [hooks, filter](HookParam hp, std::wstring text) { if (filter.match(S(text)).hasMatch()) *hooks << sanitize(S(HookCode::Generate(hp) + L" => " + text)); }); } catch (std::out_of_range) { return; } + ViewThread(0); std::thread([hooks] { for (int lastSize = 0; hooks->size() == 0 || hooks->size() != lastSize; Sleep(2000)) lastSize = hooks->size(); @@ -434,6 +444,7 @@ namespace } hooks->clear(); }).detach(); + QMessageBox::information(This, SEARCH_FOR_HOOKS, HOOK_SEARCH_STARTING_VIEW_CONSOLE); } void OpenSettings() @@ -485,13 +496,6 @@ namespace extenWindow->showNormal(); } - void ViewThread(int index) - { - ui.ttCombo->setCurrentIndex(index); - ui.textOutput->setPlainText(sanitize(S((current = &Host::GetThread(ParseTextThreadString(ui.ttCombo->itemText(index))))->storage->c_str()))); - ui.textOutput->moveCursor(QTextCursor::End); - } - void SetOutputFont(QString fontString) { QFont font = ui.textOutput->font(); diff --git a/text.cpp b/text.cpp index a051545..2638340 100644 --- a/text.cpp +++ b/text.cpp @@ -69,6 +69,7 @@ const char* EXTENSION_WRITE_ERROR = u8"Failed to save extension"; const char* USE_JP_LOCALE = u8"Emulate japanese locale?"; const char* FAILED_TO_CREATE_CONFIG_FILE = u8"Failed to create config file \"%1\""; const char* HOOK_SEARCH_UNSTABLE_WARNING = u8"Searching for hooks is unstable! Be prepared for your game to crash!"; +const char* HOOK_SEARCH_STARTING_VIEW_CONSOLE = u8"Initializing hook search - please check console for further instructions"; const char* SEARCH_CJK = u8"Search for Chinese/Japanese/Korean"; const char* SEARCH_PATTERN = u8"Search pattern (hex byte array)"; const char* SEARCH_DURATION = u8"Search duration (ms)"; @@ -120,9 +121,10 @@ const char* INSERTING_HOOK = u8"Textractor: inserting hook: %s"; const char* REMOVING_HOOK = u8"Textractor: removing hook: %s"; const char* HOOK_FAILED = u8"Textractor: failed to insert hook"; const char* TOO_MANY_HOOKS = u8"Textractor: too many hooks: can't insert"; -const char* STARTING_SEARCH = u8"Textractor: starting search"; +const char* HOOK_SEARCH_STARTING = u8"Textractor: starting hook search"; +const char* HOOK_SEARCH_INITIALIZING = u8"Textractor: initializing hook search (%f%%)"; const char* NOT_ENOUGH_TEXT = u8"Textractor: not enough text to search accurately"; -const char* HOOK_SEARCH_INITIALIZED = u8"Textractor: search initialized with %zd hooks"; +const char* HOOK_SEARCH_INITIALIZED = u8"Textractor: initialized hook search with %zd hooks"; const char* MAKE_GAME_PROCESS_TEXT = u8"Textractor: please click around in the game to force it to process text during the next %d seconds"; const char* HOOK_SEARCH_FINISHED = u8"Textractor: hook search finished, %d results found"; const char* OUT_OF_RECORDS_RETRY = u8"Textractor: out of search records, please retry if results are poor (default record count increased)"; @@ -145,7 +147,7 @@ const wchar_t* TOO_MANY_TRANS_REQUESTS = L"Rate limit exceeded: refuse to make m const wchar_t* TRANSLATION_ERROR = L"Error while translating"; const char* USE_PREV_SENTENCE_CONTEXT = u8"Use previous sentence as context"; const char* API_KEY = u8"API key"; -const char* CHROME_LOCATION = "Google Chrome location"; +const char* CHROME_LOCATION = "Google Chrome file location"; const char* START_DEVTOOLS = u8"Start DevTools"; const char* STOP_DEVTOOLS = u8"Stop DevTools"; const char* HEADLESS_MODE = u8"Headless mode"; @@ -397,7 +399,7 @@ Clic y arrastra los bordes de la ventana para moverla, o en la esquina inferior REMOVING_HOOK = u8"Textractor: 移除钩子: %s"; HOOK_FAILED = u8"Textractor: 钩子注入失败"; TOO_MANY_HOOKS = u8"Textractor: 钩子数量已达上限: 无法注入"; - STARTING_SEARCH = u8"Textractor: 开始搜索钩子"; + HOOK_SEARCH_STARTING = u8"Textractor: 开始搜索钩子"; NOT_ENOUGH_TEXT = u8"Textractor: 没有足够的文本用来精确搜索"; HOOK_SEARCH_INITIALIZED = u8"Textractor: 搜索初始化完成, 创建了 %zd 个钩子"; MAKE_GAME_PROCESS_TEXT = u8"Textractor: 请点击游戏区域, 在接下来的 %d 秒内使游戏强制处理文本"; @@ -573,7 +575,7 @@ padding: длина добавочных данных перед строкой REMOVING_HOOK = u8"Textractor: удаление хука: %s"; HOOK_FAILED = u8"Textractor: не удалось вставить хук"; TOO_MANY_HOOKS = u8"Textractor: слишком много хуков: невозможно вставить"; - STARTING_SEARCH = u8"Textractor: начало поиска"; + HOOK_SEARCH_STARTING = u8"Textractor: начало поиска"; NOT_ENOUGH_TEXT = u8"Textractor: не достаточно текста для точного поиска"; HOOK_SEARCH_INITIALIZED = u8"Textractor: поиск инициализирован с %zd хуками"; MAKE_GAME_PROCESS_TEXT = u8"Textractor: покликайте в игре, чтобы вызвать смену текста в течение %d секунд"; @@ -831,7 +833,7 @@ esempio: Textractor -p4466 -p"My Game.exe" sta tentando di inniettare i processi REMOVING_HOOK = u8"Textractor: rimuovi gancio: %s"; HOOK_FAILED = u8"Textractor: inserimento gancio non riuscito"; TOO_MANY_HOOKS = u8"Textractor: troppi ganci: impossibile inserirli"; - STARTING_SEARCH = u8"Textractor: avvia la ricerca"; + HOOK_SEARCH_STARTING = u8"Textractor: avvia la ricerca"; NOT_ENOUGH_TEXT = u8"Textractor: testo insufficente per la ricerca accurata"; HOOK_SEARCH_INITIALIZED = u8"Textractor: ricerca inizializzata con %zd ganci"; MAKE_GAME_PROCESS_TEXT = u8"Textractor: clicca intorno al gioco per forzarlo nel testo del processo durante i prossimi %d secondi"; @@ -977,7 +979,7 @@ Se você gostou desse projeto, divulgue a todos :))"; REMOVING_HOOK = u8"Textractor: removendo hook: %s"; HOOK_FAILED = u8"Textractor: falha na inserção do hook"; TOO_MANY_HOOKS = u8"Textractor: há hooks de mais: não é possível inserir mais"; - STARTING_SEARCH = u8"Textractor: iniciando busca "; + HOOK_SEARCH_STARTING = u8"Textractor: iniciando busca "; NOT_ENOUGH_TEXT = u8"Textractor: não há texto suficiente para uma buscar precisa"; HOOK_SEARCH_INITIALIZED = u8"Textractor: busca inicializada com %zd hooks"; HOOK_SEARCH_FINISHED = u8"Textractor: busca por hooks finalizada, %d resultados encontrados"; @@ -1251,7 +1253,7 @@ example: Textractor -p4466 -p"My Game.exe" tries to inject processes with id 446 REMOVING_HOOK = u8"Textractor: enlève le hook: %s"; HOOK_FAILED = u8"Textractor: n'a pas réussi à insérer un hook"; TOO_MANY_HOOKS = u8"Textractor: trop de hooks: impossible d'insérer"; - STARTING_SEARCH = u8"Textractor: démarrage de la recherche"; + HOOK_SEARCH_STARTING = u8"Textractor: démarrage de la recherche"; NOT_ENOUGH_TEXT = u8"Textractor: pas assez de texte pour effectuer une recherche précise"; HOOK_SEARCH_INITIALIZED = u8"Textractor: la recherche a été initialisé avec %zd hooks"; MAKE_GAME_PROCESS_TEXT = u8"Textractor: veuillez cliquer dans le jeu pour le forcer à traiter le texte lors de la prochaine %d seconds"; diff --git a/texthook/hookfinder.cc b/texthook/hookfinder.cc index b96329d..608a73b 100644 --- a/texthook/hookfinder.cc +++ b/texthook/hookfinder.cc @@ -3,7 +3,8 @@ #include "main.h" #include "util.h" -extern const char* STARTING_SEARCH; +extern const char* HOOK_SEARCH_STARTING; +extern const char* HOOK_SEARCH_INITIALIZING; extern const char* HOOK_SEARCH_INITIALIZED; extern const char* MAKE_GAME_PROCESS_TEXT; extern const char* HOOK_SEARCH_FINISHED; @@ -200,12 +201,12 @@ void SearchForHooks(SearchParam spUser) sp = spUser.length == 0 ? spDefault : spUser; + ConsoleOutput(HOOK_SEARCH_INITIALIZING, 0.); do try { records = std::make_unique(recordsAvailable = sp.maxRecords); } catch (std::bad_alloc) { ConsoleOutput("Textractor: SearchForHooks ERROR: out of memory, retrying to allocate %d", sp.maxRecords /= 2); } while (!records && sp.maxRecords); - ConsoleOutput(STARTING_SEARCH); std::vector addresses; if (*sp.boundaryModule) std::tie(sp.minAddress, sp.maxAddress) = Util::QueryModuleLimits(GetModuleHandleW(sp.boundaryModule)); if (*sp.exportModule) addresses = GetFunctions((uintptr_t)GetModuleHandleW(sp.exportModule)); @@ -222,11 +223,13 @@ void SearchForHooks(SearchParam spUser) MH_CreateHook((void*)addresses[i], trampolines[i], &original); MH_QueueEnableHook((void*)addresses[i]); memcpy(trampolines[i], trampoline, sizeof(trampoline)); - *(uintptr_t*)(trampolines[i] + addr_offset) = addresses[i]; + *(uintptr_t*)(trampolines[i] + addr_offset) = addresses[i]; *(void**)(trampolines[i] + original_offset) = original; + if (i % 2500 == 0) ConsoleOutput(HOOK_SEARCH_INITIALIZING, 1 + 98. * i / addresses.size()); } ConsoleOutput(HOOK_SEARCH_INITIALIZED, addresses.size()); MH_ApplyQueued(); + ConsoleOutput(HOOK_SEARCH_STARTING); ConsoleOutput(MAKE_GAME_PROCESS_TEXT, sp.searchTime / 1000); Sleep(sp.searchTime); for (auto addr : addresses) MH_QueueDisableHook((void*)addr); @@ -261,7 +264,7 @@ void SearchForText(wchar_t* text, UINT codepage) char codepageText[PATTERN_SIZE * 4] = {}; WideCharToMultiByte(codepage, 0, text, PATTERN_SIZE, codepageText, PATTERN_SIZE * 4, nullptr, nullptr); if (strlen(utf8Text) < 4 || strlen(codepageText) < 4 || wcslen(text) < 4) return ConsoleOutput(NOT_ENOUGH_TEXT); - ConsoleOutput(STARTING_SEARCH); + ConsoleOutput(HOOK_SEARCH_STARTING); auto GenerateHooks = [&](std::vector addresses, HookParamType type) { for (auto addr : addresses)