diff --git a/GUI/CMakeLists.txt b/GUI/CMakeLists.txt index 86ea2bf..be99474 100644 --- a/GUI/CMakeLists.txt +++ b/GUI/CMakeLists.txt @@ -14,10 +14,13 @@ add_executable(Textractor WIN32 Textractor.ico ) target_precompile_headers(Textractor REUSE_FROM pch) -target_link_libraries(${PROJECT_NAME} Qt5::Widgets shell32 winhttp) +target_link_libraries(Textractor Qt5::Widgets shell32 winhttp) -if (NOT EXISTS ${CMAKE_FINAL_OUTPUT_DIRECTORY}/Qt5Core.dll) - if (NOT EXISTS ${CMAKE_FINAL_OUTPUT_DIRECTORY}/Qt5Cored.dll) - install_qt5_libs(${PROJECT_NAME}) - endif() +if (NOT EXISTS ${CMAKE_FINAL_OUTPUT_DIRECTORY}/Qt5Core.dll AND NOT EXISTS ${CMAKE_FINAL_OUTPUT_DIRECTORY}/Qt5Cored.dll) + add_custom_command(TARGET Textractor + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/windeployqt" + COMMAND set PATH=%PATH%$${qt5_install_prefix}/bin + COMMAND Qt5::windeployqt --dir ${CMAKE_FINAL_OUTPUT_DIRECTORY} "${CMAKE_FINAL_OUTPUT_DIRECTORY}/Textractor.exe" + ) endif() diff --git a/GUI/extenwindow.cpp b/GUI/extenwindow.cpp index 5925af1..019830c 100644 --- a/GUI/extenwindow.cpp +++ b/GUI/extenwindow.cpp @@ -138,8 +138,6 @@ ExtenWindow::ExtenWindow(QWidget* parent) : Sync(); } -ExtenWindow::~ExtenWindow() = default; - bool ExtenWindow::eventFilter(QObject* target, QEvent* event) { // See https://stackoverflow.com/questions/1224432/how-do-i-respond-to-an-internal-drag-and-drop-operation-using-a-qlistwidget/1528215 diff --git a/GUI/extenwindow.h b/GUI/extenwindow.h index eb52395..7561092 100644 --- a/GUI/extenwindow.h +++ b/GUI/extenwindow.h @@ -20,7 +20,6 @@ class ExtenWindow : public QMainWindow { public: explicit ExtenWindow(QWidget* parent = nullptr); - ~ExtenWindow(); private: bool eventFilter(QObject* target, QEvent* event) override; diff --git a/GUI/host/hookcode.cpp b/GUI/host/hookcode.cpp index 35fb50a..c7a176d 100644 --- a/GUI/host/hookcode.cpp +++ b/GUI/host/hookcode.cpp @@ -129,8 +129,7 @@ namespace { size_t size = 0; int value = 0; - try { value = std::stoi(HCode, &size, 16); } - catch (std::invalid_argument) {} + try { value = std::stoi(HCode, &size, 16); } catch (std::invalid_argument) {} HCode.erase(0, size); return value; }; diff --git a/GUI/host/host.cpp b/GUI/host/host.cpp index 83e25a8..54046bf 100644 --- a/GUI/host/host.cpp +++ b/GUI/host/host.cpp @@ -32,8 +32,7 @@ namespace { if (!view) return {}; std::scoped_lock lock(viewMutex); - for (auto hook : view) - if (hook.address == addr) return hook; + for (auto hook : view) if (hook.address == addr) return hook; return {}; } @@ -69,8 +68,7 @@ namespace void RemoveThreads(std::function removeIf) { std::vector threadsToRemove; - for (auto& [tp, thread] : textThreadsByParams.Acquire().contents) - if (removeIf(tp)) threadsToRemove.push_back(&thread); + for (auto& [tp, thread] : textThreadsByParams.Acquire().contents) if (removeIf(tp)) threadsToRemove.push_back(&thread); for (auto thread : threadsToRemove) { OnDestroy(*thread); @@ -250,8 +248,7 @@ namespace Host TextThread* GetThread(int64_t handle) { - for (auto& [tp, thread] : textThreadsByParams.Acquire().contents) - if (thread.handle == handle) return &thread; + for (auto& [tp, thread] : textThreadsByParams.Acquire().contents) if (thread.handle == handle) return &thread; return nullptr; } diff --git a/GUI/host/textthread.cpp b/GUI/host/textthread.cpp index 7a70c4a..c3adc2b 100644 --- a/GUI/host/textthread.cpp +++ b/GUI/host/textthread.cpp @@ -22,7 +22,7 @@ TextThread::TextThread(ThreadParam tp, HookParam hp, std::optional void TextThread::Start() { - CreateTimerQueueTimer(&timer, NULL, [](void* This, BOOLEAN) { ((TextThread*)This)->Flush(); }, this, 10, 10, WT_EXECUTELONGFUNCTION); + CreateTimerQueueTimer(&timer, NULL, [](void* This, auto) { ((TextThread*)This)->Flush(); }, this, 10, 10, WT_EXECUTELONGFUNCTION); } void TextThread::Stop() @@ -42,8 +42,21 @@ void TextThread::Push(BYTE* data, int length) BYTE doubleByteChar[2]; if (length == 1) // doublebyte characters must be processed as pairs - if (leadByte) std::tie(doubleByteChar[0], doubleByteChar[1], data, length, leadByte) = std::tuple(leadByte, data[0], doubleByteChar, 2, 0); - else if (IsDBCSLeadByteEx(hp.codepage ? hp.codepage : Host::defaultCodepage, data[0])) std::tie(leadByte, length) = std::tuple(data[0], 0); + { + if (leadByte) + { + doubleByteChar[0] = leadByte; + doubleByteChar[1] = data[0]; + data = doubleByteChar; + length = 2; + leadByte = 0; + } + else if (IsDBCSLeadByteEx(hp.codepage ? hp.codepage : Host::defaultCodepage, data[0])) + { + leadByte = data[0]; + length = 0; + } + } if (hp.type & HEX_DUMP) for (int i = 0; i < length; i += sizeof(short)) buffer.append(FormatString(L"%04hX ", *(short*)(data + i))); else if (hp.type & USING_UNICODE) buffer.append((wchar_t*)data, length / sizeof(wchar_t)); diff --git a/GUI/mainwindow.cpp b/GUI/mainwindow.cpp index 5cfb22c..abb577f 100644 --- a/GUI/mainwindow.cpp +++ b/GUI/mainwindow.cpp @@ -17,7 +17,7 @@ extern const char* ATTACH; extern const char* LAUNCH; -extern const char* GAME_CONFIG; +extern const char* CONFIG; extern const char* DETACH; extern const char* FORGET; extern const char* ADD_HOOK; @@ -198,14 +198,14 @@ namespace CloseHandle(info.hThread); } - void OpenProcessConfig() + void ConfigureProcess() { if (auto processName = GetModuleFilename(selectedProcessId)) if (int last = processName->rfind(L'\\') + 1) { std::wstring configFile = std::wstring(processName.value()).replace(last, std::string::npos, GAME_CONFIG_FILE); if (!std::filesystem::exists(configFile)) QTextFile(S(configFile), QFile::WriteOnly).write("see https://github.com/Artikash/Textractor/wiki/Game-configuration-file"); if (std::filesystem::exists(configFile)) _wspawnlp(_P_DETACH, L"notepad", L"notepad", configFile.c_str(), NULL); - else QMessageBox::critical(This, GAME_CONFIG, QString(FAILED_TO_CREATE_CONFIG_FILE).arg(S(configFile))); + else QMessageBox::critical(This, CONFIG, QString(FAILED_TO_CREATE_CONFIG_FILE).arg(S(configFile))); } } @@ -258,8 +258,7 @@ namespace hookList->setAttribute(Qt::WA_DeleteOnClose); hookList->setMinimumSize({ 300, 50 }); hookList->setWindowTitle(DOUBLE_CLICK_TO_REMOVE_HOOK); - for (auto [address, hp] : hooks) - new QListWidgetItem(QString(hp.name) + "@" + QString::number(address, 16), hookList); + for (auto [address, hp] : hooks) new QListWidgetItem(QString(hp.name) + "@" + QString::number(address, 16), hookList); QObject::connect(hookList, &QListWidget::itemDoubleClicked, [processId, hookList](QListWidgetItem* item) { try @@ -335,8 +334,7 @@ namespace layout.addRow(&confirm); if (!dialog.exec()) return; wcsncpy_s(sp.text, S(textInput.text()).c_str(), PATTERN_SIZE - 1); - try { Host::FindHooks(selectedProcessId, sp); } - catch (std::out_of_range) {} + try { Host::FindHooks(selectedProcessId, sp); } catch (std::out_of_range) {} return; } @@ -408,8 +406,7 @@ namespace catch (std::out_of_range) { return; } std::thread([hooks] { - for (int lastSize = 0; hooks->size() == 0 || hooks->size() != lastSize; Sleep(2000)) - lastSize = hooks->size(); + for (int lastSize = 0; hooks->size() == 0 || hooks->size() != lastSize; Sleep(2000)) lastSize = hooks->size(); QString saveFileName; QMetaObject::invokeMethod(This, [&] @@ -562,6 +559,7 @@ namespace bool SentenceReceived(TextThread& thread, std::wstring& sentence) { + for (int i = 0; i < sentence.size(); ++i) if (sentence[i] == '\r' && sentence[i + 1] == '\n') sentence[i] = 0x200b; // for some reason \r appears as newline - no need to double if (!DispatchSentenceToExtensions(sentence, GetSentenceInfo(thread).data())) return false; sentence += L'\n'; if (&thread == current) QMetaObject::invokeMethod(This, [sentence = S(sentence)]() mutable @@ -598,7 +596,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) for (auto [text, slot] : Array{ { ATTACH, AttachProcess }, { LAUNCH, LaunchProcess }, - { GAME_CONFIG, OpenProcessConfig }, + { CONFIG, ConfigureProcess }, { DETACH, DetachProcess }, { FORGET, ForgetProcess }, { ADD_HOOK, AddHook }, @@ -647,7 +645,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) for (int i = 0; i < argc; ++i) if (std::wstring arg = argv[i]; arg[0] == L'/' || arg[0] == L'-') if (arg[1] == L'p' || arg[1] == L'P') - if (DWORD processId = _wtoi(arg.substr(2).c_str())) Host::InjectProcess(processId); + if (DWORD processId = wcstoul(arg.substr(2).c_str(), nullptr, 0)) Host::InjectProcess(processId); else for (auto [processId, processName] : processes) if (processName.value_or(L"").find(L"\\" + arg.substr(2)) != std::string::npos) Host::InjectProcess(processId); diff --git a/cmake/QtUtils.cmake b/cmake/QtUtils.cmake index d2523d0..2c5fd3d 100644 --- a/cmake/QtUtils.cmake +++ b/cmake/QtUtils.cmake @@ -87,23 +87,3 @@ macro(find_qt5) message(FATAL_ERROR "Cannot find QT5!") endif() endmacro(find_qt5) - -# Copies required DLLs to directory with target -# Optionally can provide QML directory as second argument -function(install_qt5_libs target) - if(TARGET Qt5::windeployqt) - set(EXTRA "") - if(EXISTS ${ARGV1}) - message("QML directory to be scanned=${ARGV1}") - list(APPEND EXTRA --qmldir ${ARGV1}) - endif() - - # execute windeployqt in a tmp directory after build - add_custom_command(TARGET ${target} - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/windeployqt" - COMMAND set PATH=%PATH%$${qt5_install_prefix}/bin - COMMAND Qt5::windeployqt --dir $ "$/$" ${EXTRA} - ) - endif() -endfunction(install_qt5_libs) diff --git a/extensions/CMakeLists.txt b/extensions/CMakeLists.txt index 751ec15..b6ebbbc 100644 --- a/extensions/CMakeLists.txt +++ b/extensions/CMakeLists.txt @@ -1,12 +1,12 @@ include(QtUtils) msvc_registry_search() -find_qt5(Core Widgets) - +find_qt5(Core Widgets WebSockets) cmake_policy(SET CMP0037 OLD) add_library(Bing\ Translate MODULE bingtranslate.cpp translatewrapper.cpp network.cpp extensionimpl.cpp) add_library(Copy\ to\ Clipboard MODULE copyclipboard.cpp extensionimpl.cpp) add_library(DeepL\ Translate MODULE deepltranslate.cpp translatewrapper.cpp network.cpp extensionimpl.cpp) +add_library(DevTools\ DeepL\ Translate MODULE devtoolsdeepltranslate.cpp devtools.cpp translatewrapper.cpp network.cpp extensionimpl.cpp) add_library(Extra\ Newlines MODULE extranewlines.cpp extensionimpl.cpp) add_library(Extra\ Window MODULE extrawindow.cpp extensionimpl.cpp) add_library(Google\ Translate MODULE googletranslate.cpp translatewrapper.cpp network.cpp extensionimpl.cpp) @@ -22,6 +22,7 @@ add_library(Thread\ Linker MODULE threadlinker.cpp extensionimpl.cpp) target_precompile_headers(Bing\ Translate REUSE_FROM pch) target_precompile_headers(Copy\ to\ Clipboard REUSE_FROM pch) target_precompile_headers(DeepL\ Translate REUSE_FROM pch) +target_precompile_headers(DevTools\ DeepL\ Translate REUSE_FROM pch) target_precompile_headers(Extra\ Newlines REUSE_FROM pch) target_precompile_headers(Extra\ Window REUSE_FROM pch) target_precompile_headers(Google\ Translate REUSE_FROM pch) @@ -36,8 +37,18 @@ target_precompile_headers(Thread\ Linker REUSE_FROM pch) target_link_libraries(Bing\ Translate winhttp Qt5::Widgets) target_link_libraries(DeepL\ Translate winhttp Qt5::Widgets) +target_link_libraries(DevTools\ DeepL\ Translate shell32 winhttp Qt5::Widgets Qt5::WebSockets) target_link_libraries(Extra\ Window Qt5::Widgets) target_link_libraries(Google\ Translate winhttp Qt5::Widgets) target_link_libraries(Lua lua53 Qt5::Widgets) target_link_libraries(Regex\ Filter Qt5::Widgets) target_link_libraries(Thread\ Linker Qt5::Widgets) + +if (NOT EXISTS ${CMAKE_FINAL_OUTPUT_DIRECTORY}/Qt5WebSockets.dll AND NOT EXISTS ${CMAKE_FINAL_OUTPUT_DIRECTORY}/Qt5WebSocketsd.dll) + add_custom_command(TARGET DevTools\ DeepL\ Translate + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/windeployqt" + COMMAND set PATH=%PATH%$${qt5_install_prefix}/bin + COMMAND Qt5::windeployqt --dir ${CMAKE_FINAL_OUTPUT_DIRECTORY} "${CMAKE_FINAL_OUTPUT_DIRECTORY}/DevTools\ DeepL\ Translate.dll" + ) +endif() diff --git a/extensions/bingtranslate.cpp b/extensions/bingtranslate.cpp index cb0983f..6571ad1 100644 --- a/extensions/bingtranslate.cpp +++ b/extensions/bingtranslate.cpp @@ -1,5 +1,4 @@ -#include "extension.h" -#include "network.h" +#include "network.h" #include extern const wchar_t* TRANSLATION_ERROR; @@ -81,7 +80,7 @@ QStringList languages }; bool translateSelectedOnly = false, rateLimitAll = true, rateLimitSelected = false, useCache = true; -int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 500; +int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 1000; std::pair Translate(const std::wstring& text) { @@ -94,10 +93,8 @@ std::pair Translate(const std::wstring& text) FormatString(R"([{"text":"%s"}])", JSON::Escape(WideStringToString(text))), FormatString(L"Content-Type: application/json; charset=UTF-8\r\nOcp-Apim-Subscription-Key:%s", apiKey.Copy()).c_str() }) - { if (auto translation = Copy(JSON::Parse(httpRequest.response)[0][L"translations"][0][L"text"].String())) return { true, translation.value() }; else return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, httpRequest.response) }; - } else return { false, FormatString(L"%s (code=%u)", TRANSLATION_ERROR, httpRequest.errorCode) }; if (HttpRequest httpRequest{ diff --git a/extensions/deepltranslate.cpp b/extensions/deepltranslate.cpp index 831d9ad..9a91786 100644 --- a/extensions/deepltranslate.cpp +++ b/extensions/deepltranslate.cpp @@ -1,5 +1,4 @@ #include "qtcommon.h" -#include "extension.h" #include "network.h" #include @@ -26,7 +25,7 @@ QStringList languages }; bool translateSelectedOnly = true, rateLimitAll = true, rateLimitSelected = true, useCache = true; -int tokenCount = 10, tokenRestoreDelay = 60000, maxSentenceSize = 500; +int tokenCount = 10, tokenRestoreDelay = 60000, maxSentenceSize = 1000; enum KeyType { CAT, REST }; int keyType = CAT; @@ -90,6 +89,7 @@ std::pair Translate(const std::wstring& text) L"/jsonrpc", body, L"Host: www2.deepl.com\r\nAccept-Language: en-US,en;q=0.5\r\nContent-type: application/json; charset=utf-8\r\nOrigin: https://www.deepl.com\r\nTE: Trailers", + INTERNET_DEFAULT_PORT, L"https://www.deepl.com/translator", WINHTTP_FLAG_SECURE }) diff --git a/extensions/devtools.cpp b/extensions/devtools.cpp new file mode 100644 index 0000000..a2bc204 --- /dev/null +++ b/extensions/devtools.cpp @@ -0,0 +1,116 @@ +#include "devtools.h" +#include +#include +#include + +namespace +{ + std::function OnStatusChanged = Swallow; + PROCESS_INFORMATION processInfo = {}; + std::atomic idCounter = 0; + std::mutex devToolsMutex; + QWebSocket webSocket; + std::unordered_map>> mapQueue; + auto _ = ([] + { + QObject::connect(&webSocket, &QWebSocket::stateChanged, + [](QAbstractSocket::SocketState state) { OnStatusChanged(QMetaEnum::fromType().valueToKey(state)); } + ); + QObject::connect(&webSocket, &QWebSocket::textMessageReceived, [](QString message) + { + auto result = JSON::Parse(S(message)); + std::scoped_lock lock(devToolsMutex); + if (auto id = result[L"id"].Number()) if (auto request = mapQueue.find((int)*id); request != mapQueue.end()) + { + request->second.set(result); + mapQueue.erase(request); + } + }); + }(), 0); +} + +namespace DevTools +{ + void Start(const std::wstring& path, std::function statusChanged, bool headless) + { + OnStatusChanged = statusChanged; + DWORD exitCode = 0; + auto args = FormatString( + L"%s --proxy-server=direct:// --disable-extensions --disable-gpu --user-data-dir=%s\\devtoolscache --remote-debugging-port=9222", + path, + std::filesystem::current_path().wstring() + ); + if (headless) args += L" --headless"; + STARTUPINFOW DUMMY = { sizeof(DUMMY) }; + if ((GetExitCodeProcess(processInfo.hProcess, &exitCode) && exitCode == STILL_ACTIVE) || + CreateProcessW(NULL, args.data(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &DUMMY, &processInfo) + ) + { + if (HttpRequest httpRequest{ + L"Mozilla/5.0 Textractor", + L"127.0.0.1", + L"POST", + L"/json/list", + "", + NULL, + 9222, + NULL, + WINHTTP_FLAG_ESCAPE_DISABLE + }) + { + if (auto list = Copy(JSON::Parse(httpRequest.response).Array())) if (auto it = std::find_if( + list->begin(), + list->end(), + [](const JSON::Value& object) { return object[L"type"].String() && *object[L"type"].String() == L"page" && object[L"webSocketDebuggerUrl"].String(); } + ); it != list->end()) + { + std::scoped_lock lock(devToolsMutex); + webSocket.open(S(*(*it)[L"webSocketDebuggerUrl"].String())); + return; + } + } + OnStatusChanged("Failed Connection"); + } + else OnStatusChanged("Failed Startup"); + } + + void Close() + { + std::scoped_lock lock(devToolsMutex); + for (const auto& [_, task] : mapQueue) task.set_exception(std::runtime_error("closed")); + webSocket.close(); + mapQueue.clear(); + DWORD exitCode = 0; + if (GetExitCodeProcess(processInfo.hProcess, &exitCode) && exitCode == STILL_ACTIVE) + { + TerminateProcess(processInfo.hProcess, 0); + WaitForSingleObject(processInfo.hProcess, 100); + CloseHandle(processInfo.hProcess); + CloseHandle(processInfo.hThread); + } + try { std::filesystem::remove_all(L"devtoolscache"); } catch (std::filesystem::filesystem_error) {} + OnStatusChanged("Stopped"); + } + + bool Connected() + { + std::scoped_lock lock(devToolsMutex); + return webSocket.state() == QAbstractSocket::ConnectedState; + } + + JSON::Value SendRequest(const std::wstring& method, const std::wstring& params) + { + concurrency::task_completion_event> response; + int id = idCounter += 1; + auto message = FormatString(LR"({"id":%d,"method":"%s","params":%s})", id, method, params); + { + std::scoped_lock lock(devToolsMutex); + if (webSocket.state() != QAbstractSocket::ConnectedState) return {}; + mapQueue.try_emplace(id, response); + webSocket.sendTextMessage(S(message)); + webSocket.flush(); + } + try { if (auto result = create_task(response).get()[L"result"]) return result; } catch (...) {} + return {}; + } +} diff --git a/extensions/devtools.h b/extensions/devtools.h new file mode 100644 index 0000000..81c996f --- /dev/null +++ b/extensions/devtools.h @@ -0,0 +1,10 @@ +#include "qtcommon.h" +#include "network.h" + +namespace DevTools +{ + void Start(const std::wstring& path, std::function statusChanged, bool headless); + void Close(); + bool Connected(); + JSON::Value SendRequest(const std::wstring& method, const std::wstring& params = L"{}"); +} diff --git a/extensions/devtoolsdeepltranslate.cpp b/extensions/devtoolsdeepltranslate.cpp new file mode 100644 index 0000000..1247d71 --- /dev/null +++ b/extensions/devtoolsdeepltranslate.cpp @@ -0,0 +1,124 @@ +#include "qtcommon.h" +#include "devtools.h" +#include + +extern const wchar_t* TRANSLATION_ERROR; +extern Synchronized translateTo; +extern QFormLayout* display; +extern Settings settings; + +const char* TRANSLATION_PROVIDER = "DevTools DeepL Translate"; +const char* GET_API_KEY_FROM = nullptr; +bool translateSelectedOnly = true, rateLimitAll = false, rateLimitSelected = false, useCache = true; +int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 2500; + +extern const char* CHROME_LOCATION; +extern const char* START_DEVTOOLS; +extern const char* STOP_DEVTOOLS; +extern const char* HEADLESS_MODE; +extern const char* DEVTOOLS_STATUS; +extern const char* AUTO_START; +extern const wchar_t* ERROR_START_CHROME; + +QStringList languages +{ + "Chinese (simplified): zh", + "Dutch: nl", + "English: en", + "French: fr", + "German: de", + "Italian: it", + "Japanese: ja", + "Polish: pl", + "Portuguese: pt", + "Russian: ru", + "Spanish: es", +}; + +BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + { + QString chromePath = settings.value(CHROME_LOCATION).toString(); + wchar_t programFiles[MAX_PATH + 100] = {}; + if (chromePath.isEmpty()) for (auto folder : { CSIDL_PROGRAM_FILESX86, CSIDL_PROGRAM_FILES, CSIDL_LOCAL_APPDATA }) + { + SHGetFolderPathW(NULL, folder, NULL, SHGFP_TYPE_CURRENT, programFiles); + wcscat_s(programFiles, L"/Google/Chrome/Application/chrome.exe"); + if (std::filesystem::exists(programFiles)) chromePath = S(programFiles); + } + auto chromePathEdit = new QLineEdit(chromePath); + QObject::connect(chromePathEdit, &QLineEdit::textChanged, [chromePathEdit](QString path) { settings.setValue(CHROME_LOCATION, path); }); + display->addRow(CHROME_LOCATION, chromePathEdit); + auto statusLabel = new QLabel("Stopped"); + auto startButton = new QPushButton(START_DEVTOOLS), stopButton = new QPushButton(STOP_DEVTOOLS); + auto headlessCheckBox = new QCheckBox(); + headlessCheckBox->setChecked(settings.value(HEADLESS_MODE, true).toBool()); + QObject::connect(headlessCheckBox, &QCheckBox::clicked, [](bool headless) { settings.setValue(HEADLESS_MODE, headless); }); + QObject::connect(startButton, &QPushButton::clicked, [statusLabel, chromePathEdit, headlessCheckBox] { + DevTools::Start( + S(chromePathEdit->text()), + [statusLabel](QString status) + { + QMetaObject::invokeMethod(statusLabel, std::bind(&QLabel::setText, statusLabel, status)); + if (status == "ConnectedState") std::thread([] + { + if (HttpRequest httpRequest{ + L"Mozilla/5.0 Textractor", + L"127.0.0.1", + L"POST", + L"/json/version", + "", + NULL, + 9222, + NULL, + WINHTTP_FLAG_ESCAPE_DISABLE + }) + if (auto userAgent = Copy(JSON::Parse(httpRequest.response)[L"User-Agent"].String())) + if (userAgent->find(L"Headless") != std::string::npos) + DevTools::SendRequest(L"Network.setUserAgentOverride", + FormatString(LR"({"userAgent":"%s"})", userAgent->replace(userAgent->find(L"Headless"), 8, L""))); + }).detach(); + }, + headlessCheckBox->isChecked() + ); + }); + QObject::connect(stopButton, &QPushButton::clicked, &DevTools::Close); + auto buttons = new QHBoxLayout(); + buttons->addWidget(startButton); + buttons->addWidget(stopButton); + display->addRow(HEADLESS_MODE, headlessCheckBox); + auto autoStartButton = new QCheckBox(); + autoStartButton->setChecked(settings.value(AUTO_START, false).toBool()); + QObject::connect(autoStartButton, &QCheckBox::clicked, [](bool autoStart) { settings.setValue(AUTO_START, autoStart); }); + display->addRow(AUTO_START, autoStartButton); + display->addRow(buttons); + statusLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken); + display->addRow(DEVTOOLS_STATUS, statusLabel); + if (autoStartButton->isChecked()) QMetaObject::invokeMethod(startButton, &QPushButton::click, Qt::QueuedConnection); + } + break; + case DLL_PROCESS_DETACH: + { + DevTools::Close(); + } + break; + } + return TRUE; +} + +std::pair Translate(const std::wstring& text) +{ + if (!DevTools::Connected()) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, ERROR_START_CHROME) }; + // DevTools can't handle concurrent translations yet + static std::mutex translationMutex; + std::scoped_lock lock(translationMutex); + DevTools::SendRequest(L"Page.navigate", FormatString(LR"({"url":"https://www.deepl.com/translator#any/%s/%s"})", translateTo.Copy(), Escape(text))); + for (int retry = 0; ++retry < 100; Sleep(100)) + if (auto translation = Copy(DevTools::SendRequest( + L"Runtime.evaluate", LR"({"expression":"document.querySelector('#target-dummydiv').innerHTML","returnByValue":true})")[L"result"][L"value"].String()) + ) if (!translation->empty()) return { true, translation.value() }; + return { false, TRANSLATION_ERROR }; +} \ No newline at end of file diff --git a/extensions/extrawindow.cpp b/extensions/extrawindow.cpp index a602595..01b2776 100644 --- a/extensions/extrawindow.cpp +++ b/extensions/extrawindow.cpp @@ -46,7 +46,7 @@ struct PrettyWindow : QDialog { PrettyWindow(const char* name) { - localize(); + Localize(); ui.setupUi(this); ui.display->setGraphicsEffect(&outliner); setWindowFlags(Qt::FramelessWindowHint); @@ -439,8 +439,7 @@ private: { std::vector results; for (auto [it, end] = std::equal_range(dictionary.begin(), dictionary.end(), DictionaryEntry{ term.toUtf8() }); it != end; ++it) - if (foundDefinitions.emplace(it->definition).second) - results.push_back({ term, it->definition, inflectionsUsed }); + if (foundDefinitions.emplace(it->definition).second) results.push_back({ term, it->definition, inflectionsUsed }); for (const auto& inflection : inflections) if (auto match = inflection.inflectsTo.match(term); match.hasMatch()) { QStringList currentInflectionsUsed = inflectionsUsed; diff --git a/extensions/googletranslate.cpp b/extensions/googletranslate.cpp index 95abfd2..9998f36 100644 --- a/extensions/googletranslate.cpp +++ b/extensions/googletranslate.cpp @@ -1,5 +1,4 @@ #include "qtcommon.h" -#include "extension.h" #include "network.h" #include @@ -123,7 +122,7 @@ QStringList languages }; bool translateSelectedOnly = false, rateLimitAll = true, rateLimitSelected = false, useCache = true; -int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 500; +int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 1000; std::pair Translate(const std::wstring& text) { @@ -153,9 +152,11 @@ std::pair Translate(const std::wstring& text) if (auto blob = Copy(JSON::Parse(httpRequest.response.substr(start))[0][2].String())) if (auto translations = Copy(JSON::Parse(blob.value())[1][0].Array())) { std::wstring translation; - if (translations->size() == 1 && (translations = Copy(translations.value()[0][5].Array()))) + if (translations->size() == 1) { - for (const auto& sentence : translations.value()) if (sentence[0].String()) (translation += *sentence[0].String()) += L" "; + if (translations = Copy(translations.value()[0][5].Array())) + for (const auto& sentence : translations.value()) + if (sentence[0].String()) (translation += *sentence[0].String()) += L" "; } else { diff --git a/extensions/lua.cpp b/extensions/lua.cpp index 0999774..9d5c272 100644 --- a/extensions/lua.cpp +++ b/extensions/lua.cpp @@ -47,7 +47,7 @@ public: Window() : QDialog(nullptr, Qt::WindowMinMaxButtonsHint) { - localize(); + Localize(); connect(&loadButton, &QPushButton::clicked, this, &Window::LoadScript); if (scriptEditor.toPlainText().isEmpty()) scriptEditor.setPlainText(LUA_INTRO); @@ -57,6 +57,8 @@ public: resize(800, 600); setWindowTitle("Lua"); QMetaObject::invokeMethod(this, &QWidget::show, Qt::QueuedConnection); + + LoadScript(); } ~Window() diff --git a/extensions/network.cpp b/extensions/network.cpp index 454fe2c..e14d9cc 100644 --- a/extensions/network.cpp +++ b/extensions/network.cpp @@ -7,6 +7,7 @@ HttpRequest::HttpRequest( const wchar_t* objectName, std::string body, const wchar_t* headers, + DWORD port, const wchar_t* referrer, DWORD requestFlags, const wchar_t* httpVersion, @@ -16,7 +17,7 @@ HttpRequest::HttpRequest( static std::atomic internet = NULL; if (!internet) internet = WinHttpOpen(agentName, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, NULL, NULL, 0); if (internet) - if (InternetHandle connection = WinHttpConnect(internet, serverName, INTERNET_DEFAULT_PORT, 0)) + if (InternetHandle connection = WinHttpConnect(internet, serverName, port, 0)) if (InternetHandle request = WinHttpOpenRequest(connection, action, objectName, httpVersion, referrer, acceptTypes, requestFlags)) if (WinHttpSendRequest(request, headers, -1UL, body.empty() ? NULL : body.data(), body.size(), body.size(), NULL)) { diff --git a/extensions/network.h b/extensions/network.h index bc9dfcc..5a0db8c 100644 --- a/extensions/network.h +++ b/extensions/network.h @@ -14,6 +14,7 @@ struct HttpRequest const wchar_t* objectName, std::string body = "", const wchar_t* headers = NULL, + DWORD port = INTERNET_DEFAULT_PORT, const wchar_t* referrer = NULL, DWORD requestFlags = WINHTTP_FLAG_SECURE | WINHTTP_FLAG_ESCAPE_DISABLE, const wchar_t* httpVersion = NULL, @@ -37,7 +38,7 @@ namespace JSON std::basic_string Escape(std::basic_string text) { int oldSize = text.size(); - text.resize(text.size() + std::count_if(text.begin(), text.end(), [](auto ch) { return ch == '\n' || ch == '\r' || ch == '\t' || ch == '\\' || ch == '"'; })); + text.resize(text.size() + std::count_if(text.begin(), text.end(), [](C ch) { return ch == '\n' || ch == '\r' || ch == '\t' || ch == '\\' || ch == '"'; })); auto out = text.rbegin(); for (int i = oldSize - 1; i >= 0; --i) { @@ -59,13 +60,13 @@ namespace JSON template struct UTF {}; template <> struct UTF { - inline static std::wstring FromCodepoint(int codepoint) { return { (wchar_t)codepoint }; } // TODO: surrogate pairs + inline static std::wstring FromCodepoint(unsigned codepoint) { return { (wchar_t)codepoint }; } // TODO: surrogate pairs }; template - struct Value : private std::variant, std::vector>, std::unordered_map, Value>> + struct Value : private std::variant, std::vector>, std::unordered_map, Value>> { - using std::variant, std::vector>, std::unordered_map, Value>>::variant; + using std::variant, std::vector>, std::unordered_map, Value>>::variant; explicit operator bool() const { return index(); } bool IsNull() const { return index() == 1; } @@ -77,17 +78,18 @@ namespace JSON const Value& operator[](std::basic_string key) const { - static const Value failure; if (auto object = Object()) if (auto it = object->find(key); it != object->end()) return it->second; return failure; } const Value& operator[](int i) const { - static const Value failure; if (auto array = Array()) if (i < array->size()) return array->at(i); return failure; } + + static const Value failure; }; + template const Value Value::failure; template Value Parse(const std::basic_string& text, int64_t& i, int depth) @@ -115,7 +117,7 @@ namespace JSON if (ch == 'u' && isxdigit(text[i + 2]) && isxdigit(text[i + 3]) && isxdigit(text[i + 4]) && isxdigit(text[i + 5])) { char charCode[] = { text[i + 2], text[i + 3], text[i + 4], text[i + 5], 0 }; - unescaped += UTF::FromCodepoint(strtol(charCode, nullptr, 16)); + unescaped += UTF::FromCodepoint(strtoul(charCode, nullptr, 16)); i += 5; continue; } @@ -136,7 +138,7 @@ namespace JSON static C nullStr[] = { 'n', 'u', 'l', 'l' }, trueStr[] = { 't', 'r', 'u', 'e' }, falseStr[] = { 'f', 'a', 'l', 's', 'e' }; if (ch == nullStr[0]) - if (std::char_traits::compare(text.data() + i, nullStr, std::size(nullStr)) == 0) return i += std::size(nullStr), std::nullopt; + if (std::char_traits::compare(text.data() + i, nullStr, std::size(nullStr)) == 0) return i += std::size(nullStr), nullptr; else return {}; if (ch == trueStr[0]) if (std::char_traits::compare(text.data() + i, trueStr, std::size(trueStr)) == 0) return i += std::size(trueStr), true; diff --git a/extensions/regexfilter.cpp b/extensions/regexfilter.cpp index e4178ba..ac472f2 100644 --- a/extensions/regexfilter.cpp +++ b/extensions/regexfilter.cpp @@ -21,7 +21,7 @@ public: Window() : QDialog(nullptr, Qt::WindowMinMaxButtonsHint) { - localize(); + Localize(); ui.setupUi(this); connect(ui.input, &QLineEdit::textEdited, this, &Window::setRegex); @@ -34,7 +34,7 @@ public: void setRegex(QString regex) { ui.input->setText(regex); - std::lock_guard l(m); + std::scoped_lock lock(m); if (!regex.isEmpty()) try { ::regex = S(regex); } catch (std::regex_error) { return ui.output->setText(INVALID_REGEX); } else ::regex = std::nullopt; diff --git a/extensions/removerepeatphrase.cpp b/extensions/removerepeatphrase.cpp index f8dc5ee..e24a98b 100644 --- a/extensions/removerepeatphrase.cpp +++ b/extensions/removerepeatphrase.cpp @@ -59,14 +59,12 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo) { std::wstring substring(sentence, suffixArray[i], commonPrefixLength); bool substringCharMap[0x10000] = {}; - for (auto ch : substring) - substringCharMap[ch] = true; + for (auto ch : substring) substringCharMap[ch] = true; for (int regionSize = 0, j = 0; j <= sentence.size(); ++j) if (substringCharMap[sentence[j]]) regionSize += 1; else if (regionSize >= commonPrefixLength * 2) - while (regionSize > 0) - sentence[j - regionSize--] = ERASED; + while (regionSize > 0) sentence[j - regionSize--] = ERASED; else regionSize = 0; if (!wcsstr(sentence.c_str(), substring.c_str())) std::copy(substring.begin(), substring.end(), sentence.begin() + max(suffixArray[i], suffixArray[i + 1])); diff --git a/extensions/replacer.cpp b/extensions/replacer.cpp index 3488e9d..d317316 100644 --- a/extensions/replacer.cpp +++ b/extensions/replacer.cpp @@ -87,7 +87,7 @@ void UpdateReplacements() try { if (replaceFileLastWrite.exchange(std::filesystem::last_write_time(REPLACE_SAVE_FILE)) == std::filesystem::last_write_time(REPLACE_SAVE_FILE)) return; - std::scoped_lock l(m); + std::scoped_lock lock(m); trie = Trie(std::ifstream(REPLACE_SAVE_FILE, std::ios::binary)); } catch (std::filesystem::filesystem_error) { replaceFileLastWrite.store({}); } @@ -103,7 +103,8 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved if (trie.Empty()) { auto file = std::ofstream(REPLACE_SAVE_FILE, std::ios::binary) << "\xff\xfe"; - for (auto ch : std::wstring_view(REPLACER_INSTRUCTIONS)) file << (ch == L'\n' ? std::string_view("\r\0\n", 4) : std::string_view((char*)&ch, 2)); + for (auto ch : std::wstring_view(REPLACER_INSTRUCTIONS)) + file << (ch == L'\n' ? std::string_view("\r\0\n", 4) : std::string_view((char*)&ch, 2)); _spawnlp(_P_DETACH, "notepad", "notepad", REPLACE_SAVE_FILE, NULL); // show file to user } } diff --git a/extensions/threadlinker.cpp b/extensions/threadlinker.cpp index ed7751a..aa7b93b 100644 --- a/extensions/threadlinker.cpp +++ b/extensions/threadlinker.cpp @@ -17,7 +17,7 @@ public: Window() : QDialog(nullptr, Qt::WindowMinMaxButtonsHint) { - localize(); + Localize(); connect(&linkButton, &QPushButton::clicked, this, &Window::Link); layout.addWidget(&linkList); @@ -35,7 +35,7 @@ private: int to = QInputDialog::getText(this, THREAD_LINK_TO, HEXADECIMAL, QLineEdit::Normal, "", &ok3, Qt::WindowCloseButtonHint).toInt(&ok4, 16); if (ok1 && ok2 && ok3 && ok4) { - std::lock_guard l(m); + std::scoped_lock lock(m); linkedTextHandles[from].insert(to); linkList.addItem(QString::number(from, 16) + "->" + QString::number(to, 16)); } @@ -47,7 +47,7 @@ private: { QStringList link = linkList.currentItem()->text().split("->"); linkList.takeItem(linkList.currentRow()); - std::lock_guard l(m); + std::scoped_lock lock(m); linkedTextHandles[link[0].toInt(nullptr, 16)].erase(link[1].toInt(nullptr, 16)); } } diff --git a/extensions/translatewrapper.cpp b/extensions/translatewrapper.cpp index dd4613d..f1f984c 100644 --- a/extensions/translatewrapper.cpp +++ b/extensions/translatewrapper.cpp @@ -32,16 +32,18 @@ QFormLayout* display; Settings settings; Synchronized translateTo = L"en", apiKey; -Synchronized> translationCache; -int savedSize; - -void SaveCache() +namespace { - std::wstring allTranslations(L"\xfeff"); - for (const auto& [sentence, translation] : translationCache.Acquire().contents) - allTranslations.append(L"|SENTENCE|").append(sentence).append(L"|TRANSLATION|").append(translation).append(L"|END|\r\n"); - std::ofstream(TRANSLATION_CACHE_FILE, std::ios::binary | std::ios::trunc).write((const char*)allTranslations.c_str(), allTranslations.size() * sizeof(wchar_t)); - savedSize = translationCache->size(); + Synchronized> translationCache; + int savedSize; + void SaveCache() + { + std::wstring allTranslations(L"\xfeff"); + for (const auto& [sentence, translation] : translationCache.Acquire().contents) + allTranslations.append(L"|SENTENCE|").append(sentence).append(L"|TRANSLATION|").append(translation).append(L"|END|\r\n"); + std::ofstream(TRANSLATION_CACHE_FILE, std::ios::binary | std::ios::trunc).write((const char*)allTranslations.c_str(), allTranslations.size() * sizeof(wchar_t)); + savedSize = translationCache->size(); + } } class Window : public QDialog @@ -50,7 +52,7 @@ public: Window() : QDialog(nullptr, Qt::WindowMinMaxButtonsHint) { - localize(); + Localize(); display = new QFormLayout(this); settings.beginGroup(TRANSLATION_PROVIDER); @@ -93,7 +95,7 @@ public: } if (GET_API_KEY_FROM) { - auto keyInput = new QLineEdit(settings.value(API_KEY).toString()); + auto keyInput = new QLineEdit(settings.value(API_KEY).toString(), this); apiKey->assign(S(keyInput->text())); QObject::connect(keyInput, &QLineEdit::textChanged, [](QString key) { settings.setValue(API_KEY, S(apiKey->assign(S(key)))); }); auto keyLabel = new QLabel(QString("%2").arg(GET_API_KEY_FROM, API_KEY), this); @@ -147,8 +149,15 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo) Synchronized> tokens; } rateLimiter; + auto StripWhitespace = [](std::wstring& text) + { + text.erase(text.begin(), std::find_if_not(text.begin(), text.end(), iswspace)); + text.erase(std::find_if_not(text.rbegin(), text.rend(), iswspace).base(), text.end()); + }; + bool cache = false; std::wstring translation; + StripWhitespace(sentence); if (useCache) { auto translationCache = ::translationCache.Acquire(); @@ -157,6 +166,7 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo) if (translation.empty() && (!translateSelectedOnly || sentenceInfo["current select"])) if (rateLimiter.Request() || !rateLimitAll || (!rateLimitSelected && sentenceInfo["current select"])) std::tie(cache, translation) = Translate(sentence); else translation = TOO_MANY_TRANS_REQUESTS; + StripWhitespace(translation); if (cache) translationCache->try_emplace(sentence, translation); if (cache && translationCache->size() > savedSize + 50) SaveCache(); diff --git a/include/common.h b/include/common.h index e316111..fe0ced0 100644 --- a/include/common.h +++ b/include/common.h @@ -82,6 +82,8 @@ static struct // should be inline but MSVC (linker) is bugged template operator T*() { static_assert(sizeof(T) < sizeof(DUMMY)); return (T*)DUMMY; } } DUMMY; +inline auto Swallow = [](auto&&...) {}; + template std::optional> Copy(T* ptr) { if (ptr) return *ptr; return {}; } template inline auto FormatArg(T arg) { return arg; } @@ -134,7 +136,7 @@ inline void TEXTRACTOR_MESSAGE(const wchar_t* format, const Args&... args) { Mes template inline void TEXTRACTOR_DEBUG(const wchar_t* format, const Args&... args) { std::thread([=] { TEXTRACTOR_MESSAGE(format, args...); }).detach(); } -void localize(); +void Localize(); #ifdef _DEBUG #define TEST(...) static auto _ = CreateThread(nullptr, 0, [](auto) { __VA_ARGS__; return 0UL; }, NULL, 0, nullptr); diff --git a/text.cpp b/text.cpp index fe65c5a..34d39b8 100644 --- a/text.cpp +++ b/text.cpp @@ -22,7 +22,7 @@ const char* NATIVE_LANGUAGE = "English"; const char* ATTACH = u8"Attach to game"; const char* LAUNCH = u8"Launch game"; -const char* GAME_CONFIG = u8"Configure game"; +const char* CONFIG = u8"Configure game"; const char* DETACH = u8"Detach from game"; const char* FORGET = u8"Forget game"; const char* ADD_HOOK = u8"Add hook"; @@ -144,6 +144,13 @@ 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* START_DEVTOOLS = u8"Start DevTools"; +const char* STOP_DEVTOOLS = u8"Stop DevTools"; +const char* HEADLESS_MODE = u8"Headless mode"; +const char* DEVTOOLS_STATUS = u8"DevTools status"; +const char* AUTO_START = u8"Start automatically"; +const wchar_t* ERROR_START_CHROME = L"failed to start Chrome or to connect to it"; const char* EXTRA_WINDOW_INFO = u8R"(Right click to change settings Click and drag on window edges to move, or the bottom right corner to resize)"; const char* SENTENCE_TOO_BIG = u8"Sentence too large to display"; @@ -215,7 +222,7 @@ const char* THREAD_LINK_FROM = u8"Thread number to link from"; const char* THREAD_LINK_TO = u8"Thread number to link to"; const char* HEXADECIMAL = u8"Hexadecimal"; -void localize() +void Localize() { #ifdef TURKISH NATIVE_LANGUAGE = "Turkish"; @@ -316,7 +323,7 @@ Clic y arrastra los bordes de la ventana para moverla, o en la esquina inferior NATIVE_LANGUAGE = "Chinese (simplified)"; ATTACH = u8"附加到游戏"; LAUNCH = u8"启动游戏"; - GAME_CONFIG = u8"配置游戏"; + CONFIG = u8"配置游戏"; DETACH = u8"从游戏分离"; FORGET = u8"移除游戏"; ADD_HOOK = u8"添加钩子"; @@ -469,7 +476,7 @@ end)"; NATIVE_LANGUAGE = "Russian"; ATTACH = u8"Присоединить к игре"; LAUNCH = u8"Запустить игру"; - GAME_CONFIG = u8"Настройки игры"; + CONFIG = u8"Настройки игры"; DETACH = u8"Отсоединить от игры"; FORGET = u8"Забыть игру"; ADD_HOOK = u8"Добавить хук"; @@ -725,7 +732,7 @@ Klik dan tarik pinggiran jendela untuk memindahkan, atau sudut kanan bawah untuk NATIVE_LANGUAGE = "Italian"; ATTACH = u8"Collega al gioco"; LAUNCH = u8"Avvia gioco"; - GAME_CONFIG = u8"Configura gioco"; + CONFIG = u8"Configura gioco"; DETACH = u8"Scollega dal gioco"; FORGET = u8"Dimentica gioco"; ADD_HOOK = u8"Aggiungi gancio"; @@ -1145,7 +1152,7 @@ original_text의 빈공간은 무시되지만, replacement_text는 공백과 엔 NATIVE_LANGUAGE = "French"; ATTACH = u8"Attacher le jeu"; LAUNCH = u8"Lancer le jeu"; - GAME_CONFIG = u8"Configure le jeu"; + CONFIG = u8"Configure le jeu"; DETACH = u8"Detacher du jeu"; FORGET = u8"Oublier le jeu"; ADD_HOOK = u8"Ajouter un hook"; @@ -1336,4 +1343,4 @@ Ce fichier doit être encodé en Unicode (UTF-16 Little Endian).)"; #endif // FRENCH }; -static auto _ = (localize(), 0); +static auto _ = (Localize(), 0); diff --git a/texthook/engine/match.cc b/texthook/engine/match.cc index e7d49ad..817595c 100644 --- a/texthook/engine/match.cc +++ b/texthook/engine/match.cc @@ -35,7 +35,7 @@ namespace Engine void Hijack() { - static auto _ = [] + static auto _ = ([] { GetModuleFileNameW(nullptr, processPath, MAX_PATH); processName = wcsrchr(processPath, L'\\') + 1; @@ -68,8 +68,7 @@ namespace Engine ConsoleOutput("Textractor: hijacking process located from 0x%p to 0x%p", processStartAddress, processStopAddress); DetermineEngineType(); - return NULL; - }(); + }(), 0); } bool ShouldMonoHook(const char* name) diff --git a/texthook/texthook.cc b/texthook/texthook.cc index d2dceb7..66f63d3 100644 --- a/texthook/texthook.cc +++ b/texthook/texthook.cc @@ -30,7 +30,7 @@ namespace { // unnamed 0xff, 0xd3, // call ebx 0x9d, // popfd 0x61, // popad - 0x9d, // popfd + 0x9d, // popfd 0x68, 0,0,0,0, // push @original 0xc3 // ret ; basically absolute jmp to @original }; @@ -86,7 +86,7 @@ namespace { // unnamed 0x5b, // pop rbx 0x58, // pop rax 0x9d, // pop rflags - 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [0] ; relative to next instruction (i.e. jmp @original) + 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [rip] ; relative to next instruction (i.e. jmp @original) 0,0,0,0,0,0,0,0 // @original }; int this_offset = 50, send_offset = 60, original_offset = 126;