Compare commits
57 Commits
383ea11256
...
8ad82f0467
Author | SHA1 | Date | |
---|---|---|---|
8ad82f0467 | |||
287a0365b9 | |||
b03b94da4f | |||
1ad5668c8e | |||
80bf2f0039 | |||
bdf06b518b | |||
4d1dfdac85 | |||
fad9fb3d51 | |||
a7f5dde13b | |||
d53202e553 | |||
|
ff5ea30230 | ||
|
1f7d8d77a8 | ||
|
1e93ceafc3 | ||
|
db399976d5 | ||
|
8447192880 | ||
|
4dbf0742a4 | ||
|
840934678e | ||
|
ed563ebb86 | ||
|
1369937ef5 | ||
|
9dedecf7f4 | ||
|
9d00d56582 | ||
|
7a55c35006 | ||
|
7d2fb06e82 | ||
|
5da847e06a | ||
|
36201b9ff3 | ||
|
54be169714 | ||
|
9f6bb23540 | ||
|
916b2c87b6 | ||
|
c6b52b6886 | ||
|
da56952c61 | ||
|
cb43f0db01 | ||
|
786e0aac05 | ||
|
72c6916c02 | ||
|
fcf1ea2f14 | ||
|
441baaf8ee | ||
|
0ed1e6b378 | ||
|
ac55b984cb | ||
|
9826aa06e2 | ||
|
0e9d3a9085 | ||
|
df53e31119 | ||
|
265af2f79a | ||
|
a9f3ff9644 | ||
|
78e3be549e | ||
|
5777dc3db1 | ||
|
560c1a9a05 | ||
|
3d042c29c8 | ||
|
66b3c9a023 | ||
|
24a199a8c4 | ||
|
85f9d73868 | ||
|
4eeb747694 | ||
|
8977d515c4 | ||
|
92dcaa391c | ||
|
08750b3309 | ||
|
d0d454df24 | ||
|
7cc8d3871e | ||
|
66fbff28ba | ||
|
88de8b1f5c |
@ -688,11 +688,14 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
std::unique_ptr<LPWSTR[], Functor<LocalFree>> argv(CommandLineToArgvW(GetCommandLineW(), &argc));
|
std::unique_ptr<LPWSTR[], Functor<LocalFree>> argv(CommandLineToArgvW(GetCommandLineW(), &argc));
|
||||||
for (int i = 0; i < argc; ++i)
|
for (int i = 0; i < argc; ++i)
|
||||||
if (std::wstring arg = argv[i]; arg[0] == L'/' || arg[0] == L'-')
|
if (std::wstring arg = argv[i]; arg[0] == L'/' || arg[0] == L'-')
|
||||||
|
{
|
||||||
if (arg[1] == L'p' || arg[1] == L'P')
|
if (arg[1] == L'p' || arg[1] == L'P')
|
||||||
if (DWORD processId = wcstoul(arg.substr(2).c_str(), nullptr, 0)) Host::InjectProcess(processId);
|
if (DWORD processId = wcstoul(arg.substr(2).c_str(), nullptr, 0)) Host::InjectProcess(processId);
|
||||||
else for (auto [processId, processName] : processes)
|
else for (auto [processId, processName] : processes)
|
||||||
if (processName.value_or(L"").find(L"\\" + arg.substr(2)) != std::string::npos) Host::InjectProcess(processId);
|
if (processName.value_or(L"").find(L"\\" + arg.substr(2)) != std::string::npos) Host::InjectProcess(processId);
|
||||||
|
if (arg[1] == L'c' || arg[1] == L'C')
|
||||||
|
ViewThread(1);
|
||||||
|
}
|
||||||
std::thread([] { for (; ; Sleep(10000)) AttachSavedProcesses(); }).detach();
|
std::thread([] { for (; ; Sleep(10000)) AttachSavedProcesses(); }).detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,12 +55,15 @@ foreach ($language in @{
|
|||||||
"Regex Replacer",
|
"Regex Replacer",
|
||||||
"Regex Replacer Translated Text",
|
"Regex Replacer Translated Text",
|
||||||
"Remove Repeated Characters",
|
"Remove Repeated Characters",
|
||||||
|
"Remove Repeated Leading Sentence",
|
||||||
"Remove Repeated Phrases",
|
"Remove Repeated Phrases",
|
||||||
"Remove Repeated Phrases 2",
|
"Remove Repeated Phrases 2",
|
||||||
"Remove 30 Repeated Sentences",
|
"Remove 30 Repeated Sentences",
|
||||||
"Replacer",
|
"Replacer",
|
||||||
|
"Replacer Translated Text",
|
||||||
"Styler",
|
"Styler",
|
||||||
"Thread Linker"
|
"Thread Linker",
|
||||||
|
"Yandex Translate"
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
copy -Force -Recurse -Verbose -Destination "$folder/$arch/$extension.xdll" -Path "Release_$arch/$extension.dll";
|
copy -Force -Recurse -Verbose -Destination "$folder/$arch/$extension.xdll" -Path "Release_$arch/$extension.dll";
|
||||||
|
@ -14,12 +14,15 @@ add_library(Regex\ Filter MODULE regexfilter.cpp extensionimpl.cpp)
|
|||||||
add_library(Regex\ Replacer MODULE regexreplacer.cpp extensionimpl.cpp)
|
add_library(Regex\ Replacer MODULE regexreplacer.cpp extensionimpl.cpp)
|
||||||
add_library(Regex\ Replacer\ Translated\ Text MODULE regexreplacertranslatedtext.cpp extensionimpl.cpp)
|
add_library(Regex\ Replacer\ Translated\ Text MODULE regexreplacertranslatedtext.cpp extensionimpl.cpp)
|
||||||
add_library(Remove\ Repeated\ Characters MODULE removerepeatchar.cpp extensionimpl.cpp)
|
add_library(Remove\ Repeated\ Characters MODULE removerepeatchar.cpp extensionimpl.cpp)
|
||||||
|
add_library(Remove\ Repeated\ Leading\ Sentence MODULE removerepeatedleadingsentence.cpp extensionimpl.cpp)
|
||||||
add_library(Remove\ Repeated\ Phrases MODULE removerepeatphrase.cpp extensionimpl.cpp)
|
add_library(Remove\ Repeated\ Phrases MODULE removerepeatphrase.cpp extensionimpl.cpp)
|
||||||
add_library(Remove\ Repeated\ Phrases\ 2 MODULE removerepeatphrase2.cpp extensionimpl.cpp)
|
add_library(Remove\ Repeated\ Phrases\ 2 MODULE removerepeatphrase2.cpp extensionimpl.cpp)
|
||||||
add_library(Remove\ 30\ Repeated\ Sentences MODULE removerepeatsentence.cpp extensionimpl.cpp)
|
add_library(Remove\ 30\ Repeated\ Sentences MODULE removerepeatsentence.cpp extensionimpl.cpp)
|
||||||
add_library(Replacer MODULE replacer.cpp extensionimpl.cpp)
|
add_library(Replacer MODULE replacer.cpp extensionimpl.cpp)
|
||||||
|
add_library(Replacer\ Translated\ Text MODULE replacertranslatedtext.cpp extensionimpl.cpp)
|
||||||
add_library(Styler MODULE styler.cpp extensionimpl.cpp)
|
add_library(Styler MODULE styler.cpp extensionimpl.cpp)
|
||||||
add_library(Thread\ Linker MODULE threadlinker.cpp extensionimpl.cpp)
|
add_library(Thread\ Linker MODULE threadlinker.cpp extensionimpl.cpp)
|
||||||
|
add_library(Yandex\ Translate MODULE yandextranslate.cpp translatewrapper.cpp network.cpp extensionimpl.cpp)
|
||||||
|
|
||||||
target_precompile_headers(Bing\ Translate REUSE_FROM pch)
|
target_precompile_headers(Bing\ Translate REUSE_FROM pch)
|
||||||
target_precompile_headers(Copy\ to\ Clipboard REUSE_FROM pch)
|
target_precompile_headers(Copy\ to\ Clipboard REUSE_FROM pch)
|
||||||
@ -35,12 +38,15 @@ target_precompile_headers(Regex\ Filter REUSE_FROM pch)
|
|||||||
target_precompile_headers(Regex\ Replacer REUSE_FROM pch)
|
target_precompile_headers(Regex\ Replacer REUSE_FROM pch)
|
||||||
target_precompile_headers(Regex\ Replacer\ Translated\ Text REUSE_FROM pch)
|
target_precompile_headers(Regex\ Replacer\ Translated\ Text REUSE_FROM pch)
|
||||||
target_precompile_headers(Remove\ Repeated\ Characters REUSE_FROM pch)
|
target_precompile_headers(Remove\ Repeated\ Characters REUSE_FROM pch)
|
||||||
|
target_precompile_headers(Remove\ Repeated\ Leading\ Sentence REUSE_FROM pch)
|
||||||
target_precompile_headers(Remove\ Repeated\ Phrases REUSE_FROM pch)
|
target_precompile_headers(Remove\ Repeated\ Phrases REUSE_FROM pch)
|
||||||
target_precompile_headers(Remove\ Repeated\ Phrases\ 2 REUSE_FROM pch)
|
target_precompile_headers(Remove\ Repeated\ Phrases\ 2 REUSE_FROM pch)
|
||||||
target_precompile_headers(Remove\ 30\ Repeated\ Sentences REUSE_FROM pch)
|
target_precompile_headers(Remove\ 30\ Repeated\ Sentences REUSE_FROM pch)
|
||||||
target_precompile_headers(Replacer REUSE_FROM pch)
|
target_precompile_headers(Replacer REUSE_FROM pch)
|
||||||
|
target_precompile_headers(Replacer\ Translated\ Text REUSE_FROM pch)
|
||||||
target_precompile_headers(Styler REUSE_FROM pch)
|
target_precompile_headers(Styler REUSE_FROM pch)
|
||||||
target_precompile_headers(Thread\ Linker REUSE_FROM pch)
|
target_precompile_headers(Thread\ Linker REUSE_FROM pch)
|
||||||
|
target_precompile_headers(Yandex\ Translate REUSE_FROM pch)
|
||||||
|
|
||||||
target_link_libraries(Bing\ Translate winhttp Qt5::Widgets)
|
target_link_libraries(Bing\ Translate winhttp Qt5::Widgets)
|
||||||
target_link_libraries(DeepL\ Translate winhttp Qt5::Widgets)
|
target_link_libraries(DeepL\ Translate winhttp Qt5::Widgets)
|
||||||
@ -53,6 +59,7 @@ target_link_libraries(Lua lua53 Qt5::Widgets)
|
|||||||
target_link_libraries(Regex\ Filter Qt5::Widgets)
|
target_link_libraries(Regex\ Filter Qt5::Widgets)
|
||||||
target_link_libraries(Styler Qt5::Widgets)
|
target_link_libraries(Styler Qt5::Widgets)
|
||||||
target_link_libraries(Thread\ Linker Qt5::Widgets)
|
target_link_libraries(Thread\ Linker Qt5::Widgets)
|
||||||
|
target_link_libraries(Yandex\ Translate winhttp Qt5::Widgets)
|
||||||
|
|
||||||
add_custom_target(Cleaner ALL COMMAND del *.xdll WORKING_DIRECTORY ${CMAKE_FINAL_OUTPUT_DIRECTORY})
|
add_custom_target(Cleaner ALL COMMAND del *.xdll WORKING_DIRECTORY ${CMAKE_FINAL_OUTPUT_DIRECTORY})
|
||||||
|
|
||||||
|
@ -194,6 +194,7 @@ extern const std::unordered_map<std::wstring, std::wstring> codes
|
|||||||
|
|
||||||
bool translateSelectedOnly = false, useRateLimiter = true, rateLimitSelected = false, useCache = true, useFilter = true;
|
bool translateSelectedOnly = false, useRateLimiter = true, rateLimitSelected = false, useCache = true, useFilter = true;
|
||||||
int tokenCount = 30, rateLimitTimespan = 60000, maxSentenceSize = 1000;
|
int tokenCount = 30, rateLimitTimespan = 60000, maxSentenceSize = 1000;
|
||||||
|
std::wstring dontTranslateIfMatch = L"";
|
||||||
|
|
||||||
std::pair<bool, std::wstring> Translate(const std::wstring& text, TranslationParam tlp)
|
std::pair<bool, std::wstring> Translate(const std::wstring& text, TranslationParam tlp)
|
||||||
{
|
{
|
||||||
|
@ -105,6 +105,7 @@ extern const std::unordered_map<std::wstring, std::wstring> codes
|
|||||||
|
|
||||||
bool translateSelectedOnly = true, useRateLimiter = true, rateLimitSelected = true, useCache = true, useFilter = true;
|
bool translateSelectedOnly = true, useRateLimiter = true, rateLimitSelected = true, useCache = true, useFilter = true;
|
||||||
int tokenCount = 10, rateLimitTimespan = 60000, maxSentenceSize = 1000;
|
int tokenCount = 10, rateLimitTimespan = 60000, maxSentenceSize = 1000;
|
||||||
|
std::wstring dontTranslateIfMatch = L"";
|
||||||
|
|
||||||
enum KeyType { CAT, REST };
|
enum KeyType { CAT, REST };
|
||||||
int keyType = REST;
|
int keyType = REST;
|
||||||
|
@ -7,6 +7,7 @@ extern const wchar_t* TRANSLATION_ERROR;
|
|||||||
|
|
||||||
const char* TRANSLATION_PROVIDER = "DevTools DeepL Translate";
|
const char* TRANSLATION_PROVIDER = "DevTools DeepL Translate";
|
||||||
const char* GET_API_KEY_FROM = nullptr;
|
const char* GET_API_KEY_FROM = nullptr;
|
||||||
|
std::wstring currTranslateTo;
|
||||||
|
|
||||||
extern const QStringList languagesTo
|
extern const QStringList languagesTo
|
||||||
{
|
{
|
||||||
@ -23,10 +24,13 @@ extern const QStringList languagesTo
|
|||||||
"German",
|
"German",
|
||||||
"Greek",
|
"Greek",
|
||||||
"Hungarian",
|
"Hungarian",
|
||||||
|
"Indonesian",
|
||||||
"Italian",
|
"Italian",
|
||||||
"Japanese",
|
"Japanese",
|
||||||
|
"Korean",
|
||||||
"Latvian",
|
"Latvian",
|
||||||
"Lithuanian",
|
"Lithuanian",
|
||||||
|
"Norwegian",
|
||||||
"Polish",
|
"Polish",
|
||||||
"Portuguese",
|
"Portuguese",
|
||||||
"Portuguese (Brazilian)",
|
"Portuguese (Brazilian)",
|
||||||
@ -35,7 +39,9 @@ extern const QStringList languagesTo
|
|||||||
"Slovak",
|
"Slovak",
|
||||||
"Slovenian",
|
"Slovenian",
|
||||||
"Spanish",
|
"Spanish",
|
||||||
"Swedish"
|
"Swedish",
|
||||||
|
"Turkish",
|
||||||
|
"Ukrainian"
|
||||||
},
|
},
|
||||||
languagesFrom =
|
languagesFrom =
|
||||||
{
|
{
|
||||||
@ -51,10 +57,13 @@ languagesFrom =
|
|||||||
"German",
|
"German",
|
||||||
"Greek",
|
"Greek",
|
||||||
"Hungarian",
|
"Hungarian",
|
||||||
|
"Indonesian",
|
||||||
"Italian",
|
"Italian",
|
||||||
"Japanese",
|
"Japanese",
|
||||||
|
"Korean",
|
||||||
"Latvian",
|
"Latvian",
|
||||||
"Lithuanian",
|
"Lithuanian",
|
||||||
|
"Norwegian",
|
||||||
"Polish",
|
"Polish",
|
||||||
"Portuguese",
|
"Portuguese",
|
||||||
"Romanian",
|
"Romanian",
|
||||||
@ -62,43 +71,51 @@ languagesFrom =
|
|||||||
"Slovak",
|
"Slovak",
|
||||||
"Slovenian",
|
"Slovenian",
|
||||||
"Spanish",
|
"Spanish",
|
||||||
"Swedish"
|
"Swedish",
|
||||||
|
"Turkish",
|
||||||
|
"Ukrainian"
|
||||||
};
|
};
|
||||||
extern const std::unordered_map<std::wstring, std::wstring> codes
|
extern const std::unordered_map<std::wstring, std::wstring> codes
|
||||||
{
|
{
|
||||||
{ { L"Bulgarian" }, { L"Bulgarian" } },
|
{ { L"Bulgarian" }, { L"bg-BG" } },
|
||||||
{ { L"Chinese" }, { L"Chinese" } },
|
{ { L"Chinese" }, { L"zh" } },
|
||||||
{ { L"Chinese (Simplified)" }, { L"Chinese (simplified)" } },
|
{ { L"Chinese (Simplified)" }, { L"zh-ZH" } },
|
||||||
{ { L"Czech" }, { L"Czech" } },
|
{ { L"Czech" }, { L"cs-CS" } },
|
||||||
{ { L"Danish" }, { L"Danish" } },
|
{ { L"Danish" }, { L"da-DA" } },
|
||||||
{ { L"Dutch" }, { L"Dutch" } },
|
{ { L"Dutch" }, { L"nl-NL" } },
|
||||||
{ { L"English" }, { L"English" } },
|
{ { L"English" }, { L"en" } },
|
||||||
{ { L"English (American)" }, { L"English (American)" } },
|
{ { L"English (American)" }, { L"en-US" } },
|
||||||
{ { L"English (British)" }, { L"English (British)" } },
|
{ { L"English (British)" }, { L"en-GB" } },
|
||||||
{ { L"Estonian" }, { L"Estonian" } },
|
{ { L"Estonian" }, { L"et-ET" } },
|
||||||
{ { L"Finnish" }, { L"Finnish" } },
|
{ { L"Finnish" }, { L"fi-FI" } },
|
||||||
{ { L"French" }, { L"French" } },
|
{ { L"French" }, { L"fr-FR" } },
|
||||||
{ { L"German" }, { L"German" } },
|
{ { L"German" }, { L"de-DE" } },
|
||||||
{ { L"Greek" }, { L"Greek" } },
|
{ { L"Greek" }, { L"el-EL" } },
|
||||||
{ { L"Hungarian" }, { L"Hungarian" } },
|
{ { L"Hungarian" }, { L"hu-HU" } },
|
||||||
{ { L"Italian" }, { L"Italian" } },
|
{ { L"Indonesian" }, { L"id-ID" } },
|
||||||
{ { L"Japanese" }, { L"Japanese" } },
|
{ { L"Italian" }, { L"it-IT" } },
|
||||||
{ { L"Latvian" }, { L"Latvian" } },
|
{ { L"Japanese" }, { L"ja-JA" } },
|
||||||
{ { L"Lithuanian" }, { L"Lithuanian" } },
|
{ { L"Korean" }, { L"ko-KO" } },
|
||||||
{ { L"Polish" }, { L"Polish" } },
|
{ { L"Latvian" }, { L"lv-LV" } },
|
||||||
{ { L"Portuguese" }, { L"Portuguese" } },
|
{ { L"Lithuanian" }, { L"lt-LT" } },
|
||||||
{ { L"Portuguese (Brazilian)" }, { L"Portuguese (Brazilian)" } },
|
{ { L"Norwegian" }, { L"nb-NB" } },
|
||||||
{ { L"Romanian" }, { L"Romanian" } },
|
{ { L"Polish" }, { L"pl-PL" } },
|
||||||
{ { L"Russian" }, { L"Russian" } },
|
{ { L"Portuguese" }, { L"pt-PT" } },
|
||||||
{ { L"Slovak" }, { L"Slovak" } },
|
{ { L"Portuguese (Brazilian)" }, { L"pt-BR" } },
|
||||||
{ { L"Slovenian" }, { L"Slovenian" } },
|
{ { L"Romanian" }, { L"ro-RO" } },
|
||||||
{ { L"Spanish" }, { L"Spanish" } },
|
{ { L"Russian" }, { L"ru-RU" } },
|
||||||
{ { L"Swedish" }, { L"Swedish" } },
|
{ { L"Slovak" }, { L"sk-SK" } },
|
||||||
{ { L"?" }, { L"Detect language" } }
|
{ { L"Slovenian" }, { L"sl-SL" } },
|
||||||
|
{ { L"Spanish" }, { L"es-ES" } },
|
||||||
|
{ { L"Swedish" }, { L"sv-SV" } },
|
||||||
|
{ { L"Turkish" }, { L"tr-TR" } },
|
||||||
|
{ { L"Ukrainian" }, { L"uk-UK" } },
|
||||||
|
{ { L"?" }, { L"auto" } }
|
||||||
};
|
};
|
||||||
|
|
||||||
bool translateSelectedOnly = true, useRateLimiter = true, rateLimitSelected = false, useCache = true, useFilter = true;
|
bool translateSelectedOnly = true, useRateLimiter = true, rateLimitSelected = false, useCache = true, useFilter = true;
|
||||||
int tokenCount = 30, rateLimitTimespan = 60000, maxSentenceSize = 2500;
|
int tokenCount = 30, rateLimitTimespan = 60000, maxSentenceSize = 2500;
|
||||||
|
std::wstring dontTranslateIfMatch = L"";
|
||||||
|
|
||||||
BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||||
{
|
{
|
||||||
@ -118,6 +135,21 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::wstring htmlDecode (std::wstring text) {
|
||||||
|
const std::wstring enc[] = { L"&", L"<", L">" };
|
||||||
|
const std::wstring dec[] = { L"&", L"<", L">" };
|
||||||
|
|
||||||
|
size_t pos;
|
||||||
|
for(int j = 0; j < 3; j++) {
|
||||||
|
do {
|
||||||
|
pos = text.find(enc[j]);
|
||||||
|
if (pos != std::wstring::npos)
|
||||||
|
text.replace (pos,enc[j].length(),dec[j]);
|
||||||
|
} while (pos != std::wstring::npos);
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<bool, std::wstring> Translate(const std::wstring& text, TranslationParam tlp)
|
std::pair<bool, std::wstring> Translate(const std::wstring& text, TranslationParam tlp)
|
||||||
{
|
{
|
||||||
if (!DevTools::Connected()) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, ERROR_START_CHROME) };
|
if (!DevTools::Connected()) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, ERROR_START_CHROME) };
|
||||||
@ -126,21 +158,18 @@ std::pair<bool, std::wstring> Translate(const std::wstring& text, TranslationPar
|
|||||||
std::scoped_lock lock(translationMutex);
|
std::scoped_lock lock(translationMutex);
|
||||||
std::wstring escaped; // DeepL breaks with slash in input
|
std::wstring escaped; // DeepL breaks with slash in input
|
||||||
for (auto ch : text) ch == '/' ? escaped += L"\\/" : escaped += ch;
|
for (auto ch : text) ch == '/' ? escaped += L"\\/" : escaped += ch;
|
||||||
DevTools::SendRequest("Page.navigate", FormatString(LR"({"url":"https://www.deepl.com/en/translator#en/en/%s"})", Escape(escaped)));
|
if (currTranslateTo == tlp.translateTo)
|
||||||
for (int retry = 0; ++retry < 20; Sleep(100))
|
DevTools::SendRequest("Page.navigate", FormatString(LR"({"url":"https://www.deepl.com/en/translator#%s/%s/%s"})", (tlp.translateFrom == L"?") ? codes.at(tlp.translateFrom) : codes.at(tlp.translateFrom).substr(0, 2), codes.at(tlp.translateTo).substr(0, 2), Escape(escaped)));
|
||||||
if (Copy(DevTools::SendRequest("Runtime.evaluate", LR"({"expression":"document.readyState"})")[L"result"][L"value"].String()) == L"complete") break;
|
else
|
||||||
|
{
|
||||||
DevTools::SendRequest("Runtime.evaluate", FormatString(LR"({"expression":"
|
currTranslateTo = tlp.translateTo;
|
||||||
document.querySelector('.lmt__language_select--source').querySelector('button').click();
|
DevTools::SendRequest("Page.navigate", FormatString(LR"({"url":"https://www.deepl.com/en/translator#%s/%s/%s"})", (tlp.translateFrom == L"?") ? codes.at(tlp.translateFrom) : codes.at(tlp.translateFrom).substr(0, 2), codes.at(tlp.translateTo), Escape(escaped)));
|
||||||
document.evaluate(`//*[text()='%s']`,document.querySelector('.lmt__language_select__menu'),null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue.click();
|
}
|
||||||
document.querySelector('.lmt__language_select--target').querySelector('button').click();
|
|
||||||
document.evaluate(`//*[text()='%s']`,document.querySelector('.lmt__language_select__menu'),null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue.click();
|
|
||||||
"})", codes.at(tlp.translateFrom), codes.at(tlp.translateTo)));
|
|
||||||
|
|
||||||
for (int retry = 0; ++retry < 100; Sleep(100))
|
for (int retry = 0; ++retry < 100; Sleep(100))
|
||||||
if (auto translation = Copy(DevTools::SendRequest("Runtime.evaluate",
|
if (auto translation = Copy(DevTools::SendRequest("Runtime.evaluate",
|
||||||
LR"({"expression":"document.querySelector('#target-dummydiv').innerHTML.trim() ","returnByValue":true})"
|
LR"({"expression":"document.querySelector('[data-testid=translator-target-input]').textContent.trim() ","returnByValue":true})"
|
||||||
)[L"result"][L"value"].String())) if (!translation->empty()) return { true, translation.value() };
|
)[L"result"][L"value"].String())) if (!translation->empty()) return { true, htmlDecode(translation.value()) };
|
||||||
if (auto errorMessage = Copy(DevTools::SendRequest("Runtime.evaluate",
|
if (auto errorMessage = Copy(DevTools::SendRequest("Runtime.evaluate",
|
||||||
LR"({"expression":"document.querySelector('div.lmt__system_notification').innerHTML","returnByValue":true})"
|
LR"({"expression":"document.querySelector('div.lmt__system_notification').innerHTML","returnByValue":true})"
|
||||||
)[L"result"][L"value"].String())) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, errorMessage.value()) };
|
)[L"result"][L"value"].String())) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, errorMessage.value()) };
|
||||||
|
@ -48,6 +48,7 @@ extern const std::unordered_map<std::wstring, std::wstring> codes
|
|||||||
|
|
||||||
bool translateSelectedOnly = true, useRateLimiter = true, rateLimitSelected = false, useCache = true, useFilter = true;
|
bool translateSelectedOnly = true, useRateLimiter = true, rateLimitSelected = false, useCache = true, useFilter = true;
|
||||||
int tokenCount = 30, rateLimitTimespan = 60000, maxSentenceSize = 2500;
|
int tokenCount = 30, rateLimitTimespan = 60000, maxSentenceSize = 2500;
|
||||||
|
std::wstring dontTranslateIfMatch = L"";
|
||||||
|
|
||||||
BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||||
{
|
{
|
||||||
|
@ -7,6 +7,7 @@ extern const wchar_t* TRANSLATION_ERROR;
|
|||||||
|
|
||||||
const char* TRANSLATION_PROVIDER = "DevTools Systran Translate";
|
const char* TRANSLATION_PROVIDER = "DevTools Systran Translate";
|
||||||
const char* GET_API_KEY_FROM = nullptr;
|
const char* GET_API_KEY_FROM = nullptr;
|
||||||
|
bool firstTranslation = true;
|
||||||
|
|
||||||
extern const QStringList languagesTo
|
extern const QStringList languagesTo
|
||||||
{
|
{
|
||||||
@ -114,6 +115,7 @@ extern const std::unordered_map<std::wstring, std::wstring> codes
|
|||||||
|
|
||||||
bool translateSelectedOnly = true, useRateLimiter = true, rateLimitSelected = false, useCache = true, useFilter = true;
|
bool translateSelectedOnly = true, useRateLimiter = true, rateLimitSelected = false, useCache = true, useFilter = true;
|
||||||
int tokenCount = 30, rateLimitTimespan = 60000, maxSentenceSize = 2500;
|
int tokenCount = 30, rateLimitTimespan = 60000, maxSentenceSize = 2500;
|
||||||
|
std::wstring dontTranslateIfMatch = L"";
|
||||||
|
|
||||||
BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||||
{
|
{
|
||||||
@ -140,11 +142,20 @@ std::pair<bool, std::wstring> Translate(const std::wstring& text, TranslationPar
|
|||||||
static std::mutex translationMutex;
|
static std::mutex translationMutex;
|
||||||
std::scoped_lock lock(translationMutex);
|
std::scoped_lock lock(translationMutex);
|
||||||
|
|
||||||
|
if (firstTranslation) {
|
||||||
|
firstTranslation = false;
|
||||||
|
DevTools::SendRequest("Page.navigate", FormatString(LR"({"url":"https://www.systran.net/en/translate/?source=%s&target=%s&input=%s"})", codes.at(tlp.translateFrom), codes.at(tlp.translateTo), Escape(text)));
|
||||||
|
for (int retry = 0; ++retry < 100; Sleep(100))
|
||||||
|
if (auto translation = Copy(DevTools::SendRequest("Runtime.evaluate",
|
||||||
|
LR"({"expression":"document.querySelector('#yDmH0d button').innerHTML.trim() ","returnByValue":true})"
|
||||||
|
)[L"result"][L"value"].String())) if (!translation->empty()) break;
|
||||||
|
}
|
||||||
|
|
||||||
DevTools::SendRequest(
|
DevTools::SendRequest(
|
||||||
"Page.navigate",
|
"Page.navigate",
|
||||||
FormatString(LR"({"url":"https://translate.systran.net/?source=%s&target=%s&input=%s"})", codes.at(tlp.translateFrom), codes.at(tlp.translateTo), Escape(text))
|
FormatString(LR"({"url":"https://www.systran.net/en/translate/?source=%s&target=%s&input=%s"})", codes.at(tlp.translateFrom), codes.at(tlp.translateTo), Escape(text))
|
||||||
);
|
);
|
||||||
for (int retry = 0; ++retry < 100; Sleep(100))
|
for (int retry = 0; ++retry < 150; Sleep(100))
|
||||||
if (auto translation = Copy(DevTools::SendRequest("Runtime.evaluate",
|
if (auto translation = Copy(DevTools::SendRequest("Runtime.evaluate",
|
||||||
LR"({"expression":"document.querySelector('#outputEditor').textContent.trim() ","returnByValue":true})"
|
LR"({"expression":"document.querySelector('#outputEditor').textContent.trim() ","returnByValue":true})"
|
||||||
)[L"result"][L"value"].String())) if (!translation->empty()) return { true, translation.value() };
|
)[L"result"][L"value"].String())) if (!translation->empty()) return { true, translation.value() };
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <QWheelEvent>
|
#include <QWheelEvent>
|
||||||
#include <QScrollArea>
|
#include <QScrollArea>
|
||||||
#include <QAbstractNativeEventFilter>
|
#include <QAbstractNativeEventFilter>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
extern const char* EXTRA_WINDOW_INFO;
|
extern const char* EXTRA_WINDOW_INFO;
|
||||||
extern const char* TOPMOST;
|
extern const char* TOPMOST;
|
||||||
@ -27,6 +28,7 @@ extern const char* CENTERED_TEXT;
|
|||||||
extern const char* AUTO_RESIZE_WINDOW_HEIGHT;
|
extern const char* AUTO_RESIZE_WINDOW_HEIGHT;
|
||||||
extern const char* CLICK_THROUGH;
|
extern const char* CLICK_THROUGH;
|
||||||
extern const char* HIDE_MOUSEOVER;
|
extern const char* HIDE_MOUSEOVER;
|
||||||
|
extern const char* HIDE_TEXT;
|
||||||
extern const char* DICTIONARY;
|
extern const char* DICTIONARY;
|
||||||
extern const char* DICTIONARY_INSTRUCTIONS;
|
extern const char* DICTIONARY_INSTRUCTIONS;
|
||||||
extern const char* BG_COLOR;
|
extern const char* BG_COLOR;
|
||||||
@ -36,9 +38,16 @@ extern const char* OUTLINE_COLOR;
|
|||||||
extern const char* OUTLINE_SIZE;
|
extern const char* OUTLINE_SIZE;
|
||||||
extern const char* OUTLINE_SIZE_INFO;
|
extern const char* OUTLINE_SIZE_INFO;
|
||||||
extern const char* FONT;
|
extern const char* FONT;
|
||||||
|
extern const char* TIMER_HIDE_TEXT;
|
||||||
|
extern const char* TEXT_TIMEOUT;
|
||||||
|
extern const char* TEXT_TIMEOUT_ADD_PER_CHAR;
|
||||||
|
|
||||||
constexpr auto DICTIONARY_SAVE_FILE = u8"SavedDictionary.txt";
|
constexpr auto DICTIONARY_SAVE_FILE = u8"SavedDictionary.txt";
|
||||||
constexpr int CLICK_THROUGH_HOTKEY = 0xc0d0;
|
constexpr int CLICK_THROUGH_HOTKEY = 0xc0d0;
|
||||||
|
constexpr int HIDE_TEXT_HOTKEY = 0xc0d1;
|
||||||
|
const qreal COLOR_ALFAF_HIDE_WINDOW = 0.05;
|
||||||
|
const int TEXT_TIMEOUT_DEF = 0;
|
||||||
|
const int TEXT_TIMEOUT_ADD_PER_CHAR_DEF = 0;
|
||||||
|
|
||||||
QColor colorPrompt(QWidget* parent, QColor default, const QString& title, bool customOpacity = true)
|
QColor colorPrompt(QWidget* parent, QColor default, const QString& title, bool customOpacity = true)
|
||||||
{
|
{
|
||||||
@ -49,12 +58,17 @@ QColor colorPrompt(QWidget* parent, QColor default, const QString& title, bool c
|
|||||||
|
|
||||||
struct PrettyWindow : QDialog, Localizer
|
struct PrettyWindow : QDialog, Localizer
|
||||||
{
|
{
|
||||||
|
QAction *hideTextAction;
|
||||||
|
QTimer *timerHideText = new QTimer(this);
|
||||||
|
int text_timeout, text_timeout_per_char;
|
||||||
|
|
||||||
PrettyWindow(const char* name)
|
PrettyWindow(const char* name)
|
||||||
{
|
{
|
||||||
ui.setupUi(this);
|
ui.setupUi(this);
|
||||||
ui.display->setGraphicsEffect(outliner = new Outliner);
|
ui.display->setGraphicsEffect(outliner = new Outliner);
|
||||||
setWindowFlags(Qt::FramelessWindowHint);
|
setWindowFlags(Qt::FramelessWindowHint);
|
||||||
setAttribute(Qt::WA_TranslucentBackground);
|
setAttribute(Qt::WA_TranslucentBackground);
|
||||||
|
setWindowTitle("Extra Window");
|
||||||
|
|
||||||
settings.beginGroup(name);
|
settings.beginGroup(name);
|
||||||
QFont font = ui.display->font();
|
QFont font = ui.display->font();
|
||||||
@ -64,18 +78,24 @@ struct PrettyWindow : QDialog, Localizer
|
|||||||
outliner->color = settings.value(OUTLINE_COLOR, outliner->color).value<QColor>();
|
outliner->color = settings.value(OUTLINE_COLOR, outliner->color).value<QColor>();
|
||||||
outliner->size = settings.value(OUTLINE_SIZE, outliner->size).toDouble();
|
outliner->size = settings.value(OUTLINE_SIZE, outliner->size).toDouble();
|
||||||
autoHide = settings.value(HIDE_MOUSEOVER, autoHide).toBool();
|
autoHide = settings.value(HIDE_MOUSEOVER, autoHide).toBool();
|
||||||
|
text_timeout = settings.value(TEXT_TIMEOUT, TEXT_TIMEOUT_DEF).toInt();
|
||||||
|
text_timeout_per_char = settings.value(TEXT_TIMEOUT_ADD_PER_CHAR, TEXT_TIMEOUT_ADD_PER_CHAR_DEF).toInt();
|
||||||
menu.addAction(FONT, this, &PrettyWindow::RequestFont);
|
menu.addAction(FONT, this, &PrettyWindow::RequestFont);
|
||||||
menu.addAction(BG_COLOR, [this] { SetBackgroundColor(colorPrompt(this, backgroundColor, BG_COLOR)); });
|
menu.addAction(BG_COLOR, [this] { if (hideText) ToggleHideText(); SetBackgroundColor(colorPrompt(this, backgroundColor, BG_COLOR)); });
|
||||||
menu.addAction(TEXT_COLOR, [this] { SetTextColor(colorPrompt(this, TextColor(), TEXT_COLOR)); });
|
menu.addAction(TEXT_COLOR, [this] { if (hideText) ToggleHideText(); SetTextColor(colorPrompt(this, TextColor(), TEXT_COLOR)); });
|
||||||
QAction* outlineAction = menu.addAction(TEXT_OUTLINE, this, &PrettyWindow::SetOutline);
|
QAction* outlineAction = menu.addAction(TEXT_OUTLINE, this, &PrettyWindow::SetOutline);
|
||||||
outlineAction->setCheckable(true);
|
outlineAction->setCheckable(true);
|
||||||
outlineAction->setChecked(outliner->size >= 0);
|
outlineAction->setChecked(outliner->size >= 0);
|
||||||
QAction* autoHideAction = menu.addAction(HIDE_MOUSEOVER, this, [this](bool autoHide) { settings.setValue(HIDE_MOUSEOVER, this->autoHide = autoHide); });
|
menu.addAction(TIMER_HIDE_TEXT, this, &PrettyWindow::SetTimerHideText);
|
||||||
|
QAction* autoHideAction = menu.addAction(HIDE_MOUSEOVER, this, &PrettyWindow::SetHideMouseover);
|
||||||
autoHideAction->setCheckable(true);
|
autoHideAction->setCheckable(true);
|
||||||
autoHideAction->setChecked(autoHide);
|
autoHideAction->setChecked(autoHide);
|
||||||
connect(this, &QDialog::customContextMenuRequested, [this](QPoint point) { menu.exec(mapToGlobal(point)); });
|
connect(this, &QDialog::customContextMenuRequested, [this](QPoint point) { menu.exec(mapToGlobal(point)); });
|
||||||
connect(ui.display, &QLabel::customContextMenuRequested, [this](QPoint point) { menu.exec(ui.display->mapToGlobal(point)); });
|
connect(ui.display, &QLabel::customContextMenuRequested, [this](QPoint point) { menu.exec(ui.display->mapToGlobal(point)); });
|
||||||
startTimer(50);
|
startTimer(50);
|
||||||
|
|
||||||
|
timerHideText->setSingleShot(true);
|
||||||
|
connect(timerHideText, &QTimer::timeout, [=]() {on_timeoutHideText();});
|
||||||
}
|
}
|
||||||
|
|
||||||
~PrettyWindow()
|
~PrettyWindow()
|
||||||
@ -85,29 +105,55 @@ struct PrettyWindow : QDialog, Localizer
|
|||||||
|
|
||||||
Ui::ExtraWindow ui;
|
Ui::ExtraWindow ui;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
bool hidden = false, autoHide = false, hideText=false;
|
||||||
|
qreal backgroundColorAlphaF = COLOR_ALFAF_HIDE_WINDOW;
|
||||||
|
|
||||||
|
void SetBackgroundColorHideText(bool hideText)
|
||||||
|
{
|
||||||
|
if (hideText)
|
||||||
|
{
|
||||||
|
if (settings.value(BG_COLOR).value<QColor>().alpha() > backgroundColorAlphaF) backgroundColor.setAlphaF(backgroundColorAlphaF);
|
||||||
|
if (settings.value(OUTLINE_COLOR).value<QColor>().alpha() > backgroundColorAlphaF) outliner->color.setAlphaF(backgroundColorAlphaF);
|
||||||
|
QColor hiddenTextColor = TextColor();
|
||||||
|
if (settings.value(TEXT_COLOR).value<QColor>().alpha() > backgroundColorAlphaF) hiddenTextColor.setAlphaF(backgroundColorAlphaF);
|
||||||
|
ui.display->setPalette(QPalette(hiddenTextColor, {}, {}, {}, {}, {}, {}));
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
backgroundColor.setAlpha(settings.value(BG_COLOR).value<QColor>().alpha());
|
||||||
|
outliner->color.setAlpha(settings.value(OUTLINE_COLOR).value<QColor>().alpha());
|
||||||
|
ui.display->setPalette(QPalette(settings.value(TEXT_COLOR).value<QColor>(), {}, {}, {}, {}, {}, {}));
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ToggleHideText()
|
||||||
|
{
|
||||||
|
if (!autoHide)
|
||||||
|
{
|
||||||
|
hideText = !hideText;
|
||||||
|
SetBackgroundColorHideText(hideText);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
void timerEvent(QTimerEvent*) override
|
void timerEvent(QTimerEvent*) override
|
||||||
{
|
{
|
||||||
if (autoHide && geometry().contains(QCursor::pos()))
|
if (autoHide && geometry().contains(QCursor::pos()))
|
||||||
{
|
{
|
||||||
if (!hidden)
|
if (!hidden)
|
||||||
{
|
{
|
||||||
if (backgroundColor.alphaF() > 0.05) backgroundColor.setAlphaF(0.05);
|
|
||||||
if (outliner->color.alphaF() > 0.05) outliner->color.setAlphaF(0.05);
|
|
||||||
QColor hiddenTextColor = TextColor();
|
|
||||||
if (hiddenTextColor.alphaF() > 0.05) hiddenTextColor.setAlphaF(0.05);
|
|
||||||
ui.display->setPalette(QPalette(hiddenTextColor, {}, {}, {}, {}, {}, {}));
|
|
||||||
hidden = true;
|
hidden = true;
|
||||||
repaint();
|
SetBackgroundColorHideText(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (hidden)
|
else if (hidden)
|
||||||
{
|
{
|
||||||
backgroundColor.setAlpha(settings.value(BG_COLOR).value<QColor>().alpha());
|
|
||||||
outliner->color.setAlpha(settings.value(OUTLINE_COLOR).value<QColor>().alpha());
|
|
||||||
ui.display->setPalette(QPalette(settings.value(TEXT_COLOR).value<QColor>(), {}, {}, {}, {}, {}, {}));
|
|
||||||
hidden = false;
|
hidden = false;
|
||||||
repaint();
|
SetBackgroundColorHideText(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,6 +161,12 @@ protected:
|
|||||||
Settings settings{ this };
|
Settings settings{ this };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void on_timeoutHideText()
|
||||||
|
{
|
||||||
|
if (!hideText)
|
||||||
|
ToggleHideText();
|
||||||
|
}
|
||||||
|
|
||||||
void RequestFont()
|
void RequestFont()
|
||||||
{
|
{
|
||||||
if (QFont font = QFontDialog::getFont(&ok, ui.display->font(), this, FONT); ok)
|
if (QFont font = QFontDialog::getFont(&ok, ui.display->font(), this, FONT); ok)
|
||||||
@ -147,6 +199,8 @@ private:
|
|||||||
|
|
||||||
void SetOutline(bool enable)
|
void SetOutline(bool enable)
|
||||||
{
|
{
|
||||||
|
if (hideText)
|
||||||
|
ToggleHideText();
|
||||||
if (enable)
|
if (enable)
|
||||||
{
|
{
|
||||||
QColor color = colorPrompt(this, outliner->color, OUTLINE_COLOR);
|
QColor color = colorPrompt(this, outliner->color, OUTLINE_COLOR);
|
||||||
@ -158,12 +212,28 @@ private:
|
|||||||
settings.setValue(OUTLINE_SIZE, outliner->size);
|
settings.setValue(OUTLINE_SIZE, outliner->size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetHideMouseover(bool autoHide)
|
||||||
|
{
|
||||||
|
if (hideText)
|
||||||
|
ToggleHideText();
|
||||||
|
settings.setValue(HIDE_MOUSEOVER, this->autoHide = autoHide);
|
||||||
|
hideTextAction->setDisabled(autoHide);
|
||||||
|
};
|
||||||
|
|
||||||
|
void SetTimerHideText()
|
||||||
|
{
|
||||||
|
text_timeout = QInputDialog::getInt(this, TIMER_HIDE_TEXT, TEXT_TIMEOUT, text_timeout, 0, INT_MAX, 2, nullptr, Qt::WindowCloseButtonHint);
|
||||||
|
text_timeout_per_char = QInputDialog::getInt(this, TIMER_HIDE_TEXT, TEXT_TIMEOUT_ADD_PER_CHAR, text_timeout_per_char, 0, INT_MAX, 2, nullptr, Qt::WindowCloseButtonHint);
|
||||||
|
|
||||||
|
settings.setValue(TEXT_TIMEOUT, text_timeout);
|
||||||
|
settings.setValue(TEXT_TIMEOUT_ADD_PER_CHAR, text_timeout_per_char);
|
||||||
|
};
|
||||||
|
|
||||||
void paintEvent(QPaintEvent*) override
|
void paintEvent(QPaintEvent*) override
|
||||||
{
|
{
|
||||||
QPainter(this).fillRect(rect(), backgroundColor);
|
QPainter(this).fillRect(rect(), backgroundColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool autoHide = false, hidden = false;
|
|
||||||
QColor backgroundColor{ palette().window().color() };
|
QColor backgroundColor{ palette().window().color() };
|
||||||
struct Outliner : QGraphicsEffect
|
struct Outliner : QGraphicsEffect
|
||||||
{
|
{
|
||||||
@ -214,6 +284,7 @@ public:
|
|||||||
action->setChecked(default);
|
action->setChecked(default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hideTextAction = menu.addAction(HIDE_TEXT, this, &ExtraWindow::ToggleHideText);
|
||||||
menu.addAction(CLICK_THROUGH, this, &ExtraWindow::ToggleClickThrough);
|
menu.addAction(CLICK_THROUGH, this, &ExtraWindow::ToggleClickThrough);
|
||||||
|
|
||||||
ui.display->installEventFilter(this);
|
ui.display->installEventFilter(this);
|
||||||
@ -221,6 +292,7 @@ public:
|
|||||||
|
|
||||||
QMetaObject::invokeMethod(this, [this]
|
QMetaObject::invokeMethod(this, [this]
|
||||||
{
|
{
|
||||||
|
RegisterHotKey((HWND)winId(), HIDE_TEXT_HOTKEY, MOD_ALT | MOD_NOREPEAT, 0x54);
|
||||||
RegisterHotKey((HWND)winId(), CLICK_THROUGH_HOTKEY, MOD_ALT | MOD_NOREPEAT, 0x58);
|
RegisterHotKey((HWND)winId(), CLICK_THROUGH_HOTKEY, MOD_ALT | MOD_NOREPEAT, 0x58);
|
||||||
show();
|
show();
|
||||||
AddSentence(EXTRA_WINDOW_INFO);
|
AddSentence(EXTRA_WINDOW_INFO);
|
||||||
@ -229,6 +301,7 @@ public:
|
|||||||
|
|
||||||
~ExtraWindow()
|
~ExtraWindow()
|
||||||
{
|
{
|
||||||
|
AddSentence(EXTRA_WINDOW_INFO);
|
||||||
settings.setValue(WINDOW, geometry());
|
settings.setValue(WINDOW, geometry());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,6 +346,10 @@ private:
|
|||||||
resize(width(), height() - ui.display->height() +
|
resize(width(), height() - ui.display->height() +
|
||||||
QFontMetrics(ui.display->font(), ui.display).boundingRect(0, 0, ui.display->width(), INT_MAX, Qt::TextWordWrap, sentence).height()
|
QFontMetrics(ui.display->font(), ui.display).boundingRect(0, 0, ui.display->width(), INT_MAX, Qt::TextWordWrap, sentence).height()
|
||||||
);
|
);
|
||||||
|
if (hideText)
|
||||||
|
ToggleHideText();
|
||||||
|
if (text_timeout > 0 && !autoHide)
|
||||||
|
timerHideText->start(text_timeout+sentence.size()*text_timeout_per_char);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetTopmost(bool topmost)
|
void SetTopmost(bool topmost)
|
||||||
@ -341,6 +418,8 @@ private:
|
|||||||
else exStyle &= ~WS_EX_TRANSPARENT;
|
else exStyle &= ~WS_EX_TRANSPARENT;
|
||||||
SetWindowLongPtrW((HWND)window, GWL_EXSTYLE, exStyle);
|
SetWindowLongPtrW((HWND)window, GWL_EXSTYLE, exStyle);
|
||||||
}
|
}
|
||||||
|
backgroundColorAlphaF = clickThrough ? 0.0 : COLOR_ALFAF_HIDE_WINDOW;
|
||||||
|
SetBackgroundColorHideText(hidden || hideText);
|
||||||
};
|
};
|
||||||
|
|
||||||
void ShowDictionary(QPoint mouse)
|
void ShowDictionary(QPoint mouse)
|
||||||
@ -385,7 +464,10 @@ private:
|
|||||||
{
|
{
|
||||||
auto msg = (MSG*)message;
|
auto msg = (MSG*)message;
|
||||||
if (msg->message == WM_HOTKEY)
|
if (msg->message == WM_HOTKEY)
|
||||||
|
{
|
||||||
|
if (msg->wParam == HIDE_TEXT_HOTKEY) return ToggleHideText(), true;
|
||||||
if (msg->wParam == CLICK_THROUGH_HOTKEY) return ToggleClickThrough(), true;
|
if (msg->wParam == CLICK_THROUGH_HOTKEY) return ToggleClickThrough(), true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,6 +234,7 @@ extern const std::unordered_map<std::wstring, std::wstring> codes
|
|||||||
|
|
||||||
bool translateSelectedOnly = false, useRateLimiter = true, rateLimitSelected = false, useCache = true, useFilter = true;
|
bool translateSelectedOnly = false, useRateLimiter = true, rateLimitSelected = false, useCache = true, useFilter = true;
|
||||||
int tokenCount = 30, rateLimitTimespan = 60000, maxSentenceSize = 1000;
|
int tokenCount = 30, rateLimitTimespan = 60000, maxSentenceSize = 1000;
|
||||||
|
std::wstring dontTranslateIfMatch = L"";
|
||||||
|
|
||||||
std::pair<bool, std::wstring> Translate(const std::wstring& text, TranslationParam tlp)
|
std::pair<bool, std::wstring> Translate(const std::wstring& text, TranslationParam tlp)
|
||||||
{
|
{
|
||||||
|
26
extensions/removerepeatedleadingsentence.cpp
Normal file
26
extensions/removerepeatedleadingsentence.cpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#include "extension.h"
|
||||||
|
|
||||||
|
bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
|
||||||
|
{
|
||||||
|
if (!sentenceInfo["current select"] || sentenceInfo["text number"] == 0) return false;
|
||||||
|
|
||||||
|
static std::wstring prevSentence;
|
||||||
|
|
||||||
|
std::wstring checkSentence = prevSentence;
|
||||||
|
prevSentence = sentence;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (sentence.substr(0, checkSentence.size()) == checkSentence)
|
||||||
|
{
|
||||||
|
auto Ltrim = [](std::wstring& text)
|
||||||
|
{
|
||||||
|
text.erase(text.begin(), std::find_if_not(text.begin(), text.end(), iswspace));
|
||||||
|
};
|
||||||
|
|
||||||
|
sentence = sentence.substr(checkSentence.size());
|
||||||
|
Ltrim(sentence);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
151
extensions/replacertranslatedtext.cpp
Normal file
151
extensions/replacertranslatedtext.cpp
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
#include "extension.h"
|
||||||
|
#include "blockmarkup.h"
|
||||||
|
#include <cwctype>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <process.h>
|
||||||
|
|
||||||
|
extern const wchar_t* REPLACER_INSTRUCTIONS;
|
||||||
|
|
||||||
|
constexpr auto REPLACE_SAVE_FILE = u8"SavedReplacementsTranslatedText.txt";
|
||||||
|
|
||||||
|
std::atomic<std::filesystem::file_time_type> replaceFileLastWrite = {};
|
||||||
|
concurrency::reader_writer_lock m;
|
||||||
|
|
||||||
|
class Trie
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Trie(const std::istream& replacementScript)
|
||||||
|
{
|
||||||
|
BlockMarkupIterator replacementScriptParser(replacementScript, Array<std::wstring_view>{ L"|ORIG|", L"|BECOMES|" });
|
||||||
|
while (auto read = replacementScriptParser.Next())
|
||||||
|
{
|
||||||
|
const auto& [original, replacement] = read.value();
|
||||||
|
Node* current = &root;
|
||||||
|
for (auto ch : original) if (!Ignore(ch)) current = Next(current, ch);
|
||||||
|
if (current != &root)
|
||||||
|
current->value = charStorage.insert(charStorage.end(), replacement.c_str(), replacement.c_str() + replacement.size() + 1) - charStorage.begin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring Replace(const std::wstring& sentence) const
|
||||||
|
{
|
||||||
|
std::wstring result;
|
||||||
|
for (int i = 0; i < sentence.size();)
|
||||||
|
{
|
||||||
|
std::wstring_view replacement(sentence.c_str() + i, 1);
|
||||||
|
int originalLength = 1;
|
||||||
|
|
||||||
|
const Node* current = &root;
|
||||||
|
for (int j = i; current && j <= sentence.size(); ++j)
|
||||||
|
{
|
||||||
|
if (current->value >= 0)
|
||||||
|
{
|
||||||
|
replacement = charStorage.data() + current->value;
|
||||||
|
originalLength = j - i;
|
||||||
|
}
|
||||||
|
if (!Ignore(sentence[j])) current = Next(current, sentence[j]) ? Next(current, sentence[j]) : Next(current, L'^');
|
||||||
|
}
|
||||||
|
|
||||||
|
result += replacement;
|
||||||
|
i += originalLength;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Empty()
|
||||||
|
{
|
||||||
|
return root.charMap.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool Ignore(wchar_t ch)
|
||||||
|
{
|
||||||
|
return ch <= 0x20 || iswspace(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Node>
|
||||||
|
static Node* Next(Node* node, wchar_t ch)
|
||||||
|
{
|
||||||
|
auto it = std::lower_bound(node->charMap.begin(), node->charMap.end(), ch, [](const auto& one, auto two) { return one.first < two; });
|
||||||
|
if (it != node->charMap.end() && it->first == ch) return it->second.get();
|
||||||
|
if constexpr (!std::is_const_v<Node>) return node->charMap.insert(it, { ch, std::make_unique<Node>() })->second.get();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Node
|
||||||
|
{
|
||||||
|
std::vector<std::pair<wchar_t, std::unique_ptr<Node>>> charMap;
|
||||||
|
ptrdiff_t value = -1;
|
||||||
|
} root;
|
||||||
|
|
||||||
|
std::vector<wchar_t> charStorage;
|
||||||
|
} trie = { std::istringstream("") };
|
||||||
|
|
||||||
|
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 lock(m);
|
||||||
|
trie = Trie(std::ifstream(REPLACE_SAVE_FILE, std::ios::binary));
|
||||||
|
}
|
||||||
|
catch (std::filesystem::filesystem_error) { replaceFileLastWrite.store({}); }
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||||
|
{
|
||||||
|
switch (ul_reason_for_call)
|
||||||
|
{
|
||||||
|
case DLL_PROCESS_ATTACH:
|
||||||
|
{
|
||||||
|
UpdateReplacements();
|
||||||
|
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));
|
||||||
|
SpawnThread([] { _spawnlp(_P_DETACH, "notepad", "notepad", REPLACE_SAVE_FILE, NULL); }); // show file to user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DLL_PROCESS_DETACH:
|
||||||
|
{
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
|
||||||
|
{
|
||||||
|
if (!sentenceInfo["current select"] || sentenceInfo["text number"] == 0) return false;
|
||||||
|
|
||||||
|
size_t posTranslation = sentence.find(L"\x200b \n");
|
||||||
|
if (posTranslation == std::wstring::npos) return false;
|
||||||
|
posTranslation +=3;
|
||||||
|
|
||||||
|
UpdateReplacements();
|
||||||
|
|
||||||
|
std::wstring sTranslated = sentence.substr(posTranslation, std::wstring::npos);
|
||||||
|
sentence = sentence.substr(0, posTranslation);
|
||||||
|
|
||||||
|
concurrency::reader_writer_lock::scoped_lock_read readLock(m);
|
||||||
|
sentence += trie.Replace(sTranslated);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(
|
||||||
|
{
|
||||||
|
std::wstring replacementScript = LR"(
|
||||||
|
|ORIG|さよなら|BECOMES|goodbye |END|Ignore this text
|
||||||
|
And this text ツ
|
||||||
|
|ORIG|バカ|BECOMES|idiot|END|
|
||||||
|
|ORIG|こんにちは |BECOMES| hello|END||ORIG|delet^this|BECOMES||END|)";
|
||||||
|
Trie replacements(std::istringstream(std::string{ (const char*)replacementScript.c_str(), replacementScript.size() * sizeof(wchar_t) }));
|
||||||
|
std::wstring original = LR"(Don't replace this
|
||||||
|
さよなら バカ こんにちは delete this)";
|
||||||
|
std::wstring replaced = Trie(std::move(replacements)).Replace(original);
|
||||||
|
assert(replaced == L"Don't replace thisgoodbye idiot hello");
|
||||||
|
}
|
||||||
|
);
|
@ -17,6 +17,7 @@ extern const char* FILTER_GARBAGE;
|
|||||||
extern const char* MAX_TRANSLATIONS_IN_TIMESPAN;
|
extern const char* MAX_TRANSLATIONS_IN_TIMESPAN;
|
||||||
extern const char* TIMESPAN;
|
extern const char* TIMESPAN;
|
||||||
extern const char* MAX_SENTENCE_SIZE;
|
extern const char* MAX_SENTENCE_SIZE;
|
||||||
|
extern const char* DONT_TRANSLATE_IF_MATCH;
|
||||||
extern const char* API_KEY;
|
extern const char* API_KEY;
|
||||||
extern const wchar_t* SENTENCE_TOO_LARGE_TO_TRANS;
|
extern const wchar_t* SENTENCE_TOO_LARGE_TO_TRANS;
|
||||||
extern const wchar_t* TRANSLATION_ERROR;
|
extern const wchar_t* TRANSLATION_ERROR;
|
||||||
@ -27,6 +28,7 @@ extern const char* GET_API_KEY_FROM;
|
|||||||
extern const QStringList languagesTo, languagesFrom;
|
extern const QStringList languagesTo, languagesFrom;
|
||||||
extern bool translateSelectedOnly, useRateLimiter, rateLimitSelected, useCache, useFilter;
|
extern bool translateSelectedOnly, useRateLimiter, rateLimitSelected, useCache, useFilter;
|
||||||
extern int tokenCount, rateLimitTimespan, maxSentenceSize;
|
extern int tokenCount, rateLimitTimespan, maxSentenceSize;
|
||||||
|
extern std::wstring dontTranslateIfMatch;
|
||||||
std::pair<bool, std::wstring> Translate(const std::wstring& text, TranslationParam tlp);
|
std::pair<bool, std::wstring> Translate(const std::wstring& text, TranslationParam tlp);
|
||||||
|
|
||||||
QFormLayout* display;
|
QFormLayout* display;
|
||||||
@ -118,6 +120,14 @@ public:
|
|||||||
display->addRow(label, spinBox);
|
display->addRow(label, spinBox);
|
||||||
connect(spinBox, qOverload<int>(&QSpinBox::valueChanged), [label, &value](int newValue) { settings.setValue(label, value = newValue); });
|
connect(spinBox, qOverload<int>(&QSpinBox::valueChanged), [label, &value](int newValue) { settings.setValue(label, value = newValue); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto matchEdit = new QLineEdit(settings.value(DONT_TRANSLATE_IF_MATCH).toString(), this);
|
||||||
|
dontTranslateIfMatch = S(matchEdit->text());
|
||||||
|
QObject::connect(matchEdit, &QLineEdit::textChanged, [](QString match) { settings.setValue(DONT_TRANSLATE_IF_MATCH, S(dontTranslateIfMatch = S(match))); });
|
||||||
|
auto matchLabel = new QLabel(QString("%1 (<a href=\"https://regexr.com/\">regex</a>)").arg(DONT_TRANSLATE_IF_MATCH), this);
|
||||||
|
matchLabel->setOpenExternalLinks(true);
|
||||||
|
display->addRow(matchLabel, matchEdit);
|
||||||
|
|
||||||
if (GET_API_KEY_FROM)
|
if (GET_API_KEY_FROM)
|
||||||
{
|
{
|
||||||
auto keyEdit = new QLineEdit(settings.value(API_KEY).toString(), this);
|
auto keyEdit = new QLineEdit(settings.value(API_KEY).toString(), this);
|
||||||
@ -182,6 +192,16 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
|
|||||||
sentence.erase(std::remove_if(sentence.begin(), sentence.end(), [](wchar_t ch) { return ch < ' ' && ch != '\n'; }), sentence.end());
|
sentence.erase(std::remove_if(sentence.begin(), sentence.end(), [](wchar_t ch) { return ch < ' ' && ch != '\n'; }), sentence.end());
|
||||||
}
|
}
|
||||||
if (sentence.empty()) return true;
|
if (sentence.empty()) return true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!dontTranslateIfMatch.empty() && std::regex_match(sentence, std::wregex(dontTranslateIfMatch)))
|
||||||
|
{
|
||||||
|
sentence += L"\x200b \n" + sentence;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (...) {}
|
||||||
|
|
||||||
if (sentence.size() > maxSentenceSize) translation = SENTENCE_TOO_LARGE_TO_TRANS;
|
if (sentence.size() > maxSentenceSize) translation = SENTENCE_TOO_LARGE_TO_TRANS;
|
||||||
if (useCache)
|
if (useCache)
|
||||||
{
|
{
|
||||||
|
238
extensions/yandextranslate.cpp
Normal file
238
extensions/yandextranslate.cpp
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
#include "qtcommon.h"
|
||||||
|
#include "translatewrapper.h"
|
||||||
|
#include "network.h"
|
||||||
|
|
||||||
|
extern const wchar_t* TRANSLATION_ERROR;
|
||||||
|
|
||||||
|
const char* TRANSLATION_PROVIDER = "Yandex Translate";
|
||||||
|
const char* GET_API_KEY_FROM = nullptr;
|
||||||
|
extern const QStringList languagesTo
|
||||||
|
{
|
||||||
|
"Afrikaans",
|
||||||
|
"Albanian",
|
||||||
|
"Amharic",
|
||||||
|
"Arabic",
|
||||||
|
"Armenian",
|
||||||
|
"Azerbaijani",
|
||||||
|
"Bashkir",
|
||||||
|
"Basque",
|
||||||
|
"Belarusian",
|
||||||
|
"Bengali",
|
||||||
|
"Bosnian",
|
||||||
|
"Bulgarian",
|
||||||
|
"Burmese",
|
||||||
|
"Catalan",
|
||||||
|
"Cebuano",
|
||||||
|
"Chinese",
|
||||||
|
"Chuvash",
|
||||||
|
"Croatian",
|
||||||
|
"Czech",
|
||||||
|
"Danish",
|
||||||
|
"Dutch",
|
||||||
|
"Elvish (Sindarin)",
|
||||||
|
"Emoji",
|
||||||
|
"English",
|
||||||
|
"Esperanto",
|
||||||
|
"Estonian",
|
||||||
|
"Finnish",
|
||||||
|
"French",
|
||||||
|
"Galician",
|
||||||
|
"Georgian",
|
||||||
|
"German",
|
||||||
|
"Greek",
|
||||||
|
"Gujarati",
|
||||||
|
"Haitian",
|
||||||
|
"Hebrew",
|
||||||
|
"Hill Mari",
|
||||||
|
"Hindi",
|
||||||
|
"Hungarian",
|
||||||
|
"Icelandic",
|
||||||
|
"Indonesian",
|
||||||
|
"Irish",
|
||||||
|
"Italian",
|
||||||
|
"Japanese",
|
||||||
|
"Javanese",
|
||||||
|
"Kannada",
|
||||||
|
"Kazakh",
|
||||||
|
"Kazakh (Latin)",
|
||||||
|
"Khmer",
|
||||||
|
"Korean",
|
||||||
|
"Kyrgyz",
|
||||||
|
"Lao",
|
||||||
|
"Latin",
|
||||||
|
"Latvian",
|
||||||
|
"Lithuanian",
|
||||||
|
"Luxembourgish",
|
||||||
|
"Macedonian",
|
||||||
|
"Malagasy",
|
||||||
|
"Malay",
|
||||||
|
"Malayalam",
|
||||||
|
"Maltese",
|
||||||
|
"Maori",
|
||||||
|
"Marathi",
|
||||||
|
"Mari",
|
||||||
|
"Mongolian",
|
||||||
|
"Nepali",
|
||||||
|
"Norwegian",
|
||||||
|
"Papiamento",
|
||||||
|
"Persian",
|
||||||
|
"Polish",
|
||||||
|
"Portuguese",
|
||||||
|
"Punjabi",
|
||||||
|
"Romanian",
|
||||||
|
"Russian",
|
||||||
|
"Scottish Gaelic",
|
||||||
|
"Serbian",
|
||||||
|
"Sinhalese",
|
||||||
|
"Slovak",
|
||||||
|
"Slovenian",
|
||||||
|
"Spanish",
|
||||||
|
"Sundanese",
|
||||||
|
"Swahili",
|
||||||
|
"Swedish",
|
||||||
|
"Tagalog",
|
||||||
|
"Tajik",
|
||||||
|
"Tamil",
|
||||||
|
"Tatar",
|
||||||
|
"Telugu",
|
||||||
|
"Thai",
|
||||||
|
"Turkish",
|
||||||
|
"Udmurt",
|
||||||
|
"Ukrainian",
|
||||||
|
"Urdu",
|
||||||
|
"Uzbek",
|
||||||
|
"Uzbek (Cyrillic)",
|
||||||
|
"Vietnamese",
|
||||||
|
"Welsh",
|
||||||
|
"Xhosa",
|
||||||
|
"Yakut",
|
||||||
|
"Yiddish",
|
||||||
|
"Zulu"
|
||||||
|
}, languagesFrom = languagesTo;
|
||||||
|
extern const std::unordered_map<std::wstring, std::wstring> codes
|
||||||
|
{
|
||||||
|
{ { L"Afrikaans" }, { L"af" } },
|
||||||
|
{ { L"Albanian" }, { L"sq" } },
|
||||||
|
{ { L"Amharic" }, { L"am" } },
|
||||||
|
{ { L"Arabic" }, { L"ar" } },
|
||||||
|
{ { L"Armenian" }, { L"hy" } },
|
||||||
|
{ { L"Azerbaijani" }, { L"az" } },
|
||||||
|
{ { L"Bashkir" }, { L"ba" } },
|
||||||
|
{ { L"Basque" }, { L"eu" } },
|
||||||
|
{ { L"Belarusian" }, { L"be" } },
|
||||||
|
{ { L"Bengali" }, { L"bn" } },
|
||||||
|
{ { L"Bosnian" }, { L"bs" } },
|
||||||
|
{ { L"Bulgarian" }, { L"bg" } },
|
||||||
|
{ { L"Burmese" }, { L"my" } },
|
||||||
|
{ { L"Catalan" }, { L"ca" } },
|
||||||
|
{ { L"Cebuano" }, { L"ceb" } },
|
||||||
|
{ { L"Chinese" }, { L"zh" } },
|
||||||
|
{ { L"Chuvash" }, { L"cv" } },
|
||||||
|
{ { L"Croatian" }, { L"hr" } },
|
||||||
|
{ { L"Czech" }, { L"cs" } },
|
||||||
|
{ { L"Danish" }, { L"da" } },
|
||||||
|
{ { L"Dutch" }, { L"nl" } },
|
||||||
|
{ { L"Elvish (Sindarin)" }, { L"sjn" } },
|
||||||
|
{ { L"Emoji" }, { L"emj" } },
|
||||||
|
{ { L"English" }, { L"en" } },
|
||||||
|
{ { L"Esperanto" }, { L"eo" } },
|
||||||
|
{ { L"Estonian" }, { L"et" } },
|
||||||
|
{ { L"Finnish" }, { L"fi" } },
|
||||||
|
{ { L"French" }, { L"fr" } },
|
||||||
|
{ { L"Galician" }, { L"gl" } },
|
||||||
|
{ { L"Georgian" }, { L"ka" } },
|
||||||
|
{ { L"German" }, { L"de" } },
|
||||||
|
{ { L"Greek" }, { L"el" } },
|
||||||
|
{ { L"Gujarati" }, { L"gu" } },
|
||||||
|
{ { L"Haitian" }, { L"ht" } },
|
||||||
|
{ { L"Hebrew" }, { L"he" } },
|
||||||
|
{ { L"Hill Mari" }, { L"mrj" } },
|
||||||
|
{ { L"Hindi" }, { L"hi" } },
|
||||||
|
{ { L"Hungarian" }, { L"hu" } },
|
||||||
|
{ { L"Icelandic" }, { L"is" } },
|
||||||
|
{ { L"Indonesian" }, { L"id" } },
|
||||||
|
{ { L"Irish" }, { L"ga" } },
|
||||||
|
{ { L"Italian" }, { L"it" } },
|
||||||
|
{ { L"Japanese" }, { L"ja" } },
|
||||||
|
{ { L"Javanese" }, { L"jv" } },
|
||||||
|
{ { L"Kannada" }, { L"kn" } },
|
||||||
|
{ { L"Kazakh" }, { L"kk" } },
|
||||||
|
{ { L"Kazakh (Latin)" }, { L"kazlat" } },
|
||||||
|
{ { L"Khmer" }, { L"km" } },
|
||||||
|
{ { L"Korean" }, { L"ko" } },
|
||||||
|
{ { L"Kyrgyz" }, { L"ky" } },
|
||||||
|
{ { L"Lao" }, { L"lo" } },
|
||||||
|
{ { L"Latin" }, { L"la" } },
|
||||||
|
{ { L"Latvian" }, { L"lv" } },
|
||||||
|
{ { L"Lithuanian" }, { L"lt" } },
|
||||||
|
{ { L"Luxembourgish" }, { L"lb" } },
|
||||||
|
{ { L"Macedonian" }, { L"mk" } },
|
||||||
|
{ { L"Malagasy" }, { L"mg" } },
|
||||||
|
{ { L"Malay" }, { L"ms" } },
|
||||||
|
{ { L"Malayalam" }, { L"ml" } },
|
||||||
|
{ { L"Maltese" }, { L"mt" } },
|
||||||
|
{ { L"Maori" }, { L"mi" } },
|
||||||
|
{ { L"Marathi" }, { L"mr" } },
|
||||||
|
{ { L"Mari" }, { L"mhr" } },
|
||||||
|
{ { L"Mongolian" }, { L"mn" } },
|
||||||
|
{ { L"Nepali" }, { L"ne" } },
|
||||||
|
{ { L"Norwegian" }, { L"no" } },
|
||||||
|
{ { L"Papiamento" }, { L"pap" } },
|
||||||
|
{ { L"Persian" }, { L"fa" } },
|
||||||
|
{ { L"Polish" }, { L"pl" } },
|
||||||
|
{ { L"Portuguese" }, { L"pt" } },
|
||||||
|
{ { L"Punjabi" }, { L"pa" } },
|
||||||
|
{ { L"Romanian" }, { L"ro" } },
|
||||||
|
{ { L"Russian" }, { L"ru" } },
|
||||||
|
{ { L"Scottish Gaelic" }, { L"gd" } },
|
||||||
|
{ { L"Serbian" }, { L"sr" } },
|
||||||
|
{ { L"Sinhalese" }, { L"si" } },
|
||||||
|
{ { L"Slovak" }, { L"sk" } },
|
||||||
|
{ { L"Slovenian" }, { L"sl" } },
|
||||||
|
{ { L"Spanish" }, { L"es" } },
|
||||||
|
{ { L"Sundanese" }, { L"su" } },
|
||||||
|
{ { L"Swahili" }, { L"sw" } },
|
||||||
|
{ { L"Swedish" }, { L"sv" } },
|
||||||
|
{ { L"Tagalog" }, { L"tl" } },
|
||||||
|
{ { L"Tajik" }, { L"tg" } },
|
||||||
|
{ { L"Tamil" }, { L"ta" } },
|
||||||
|
{ { L"Tatar" }, { L"tt" } },
|
||||||
|
{ { L"Telugu" }, { L"te" } },
|
||||||
|
{ { L"Thai" }, { L"th" } },
|
||||||
|
{ { L"Turkish" }, { L"tr" } },
|
||||||
|
{ { L"Udmurt" }, { L"udm" } },
|
||||||
|
{ { L"Ukrainian" }, { L"uk" } },
|
||||||
|
{ { L"Urdu" }, { L"ur" } },
|
||||||
|
{ { L"Uzbek" }, { L"uz" } },
|
||||||
|
{ { L"Uzbek (Cyrillic)" }, { L"uzbcyr" } },
|
||||||
|
{ { L"Vietnamese" }, { L"vi" } },
|
||||||
|
{ { L"Welsh" }, { L"cy" } },
|
||||||
|
{ { L"Xhosa" }, { L"xh" } },
|
||||||
|
{ { L"Yakut" }, { L"sah" } },
|
||||||
|
{ { L"Yiddish" }, { L"yi" } },
|
||||||
|
{ { L"Zulu" }, { L"zu" } },
|
||||||
|
{ { L"?" }, { L"auto" } }
|
||||||
|
};
|
||||||
|
|
||||||
|
bool translateSelectedOnly = true, useRateLimiter = true, rateLimitSelected = false, useCache = true, useFilter = true;
|
||||||
|
int tokenCount = 30, rateLimitTimespan = 60000, maxSentenceSize = 1000;
|
||||||
|
|
||||||
|
std::pair<bool, std::wstring> Translate(const std::wstring& text, TranslationParam tlp)
|
||||||
|
{
|
||||||
|
//Thnx @Dangetsu
|
||||||
|
if (HttpRequest httpRequest{
|
||||||
|
L"Mozilla/5.0 Textractor",
|
||||||
|
L"api.browser.yandex.ru",
|
||||||
|
L"GET",
|
||||||
|
FormatString(L"/dictionary/translate?brandID=yandex&statLang=%s&targetLang=%s&url=http://vnr.aniclan.com/&locale=ru&text=%s", (tlp.translateFrom == L"?") ? codes.at(tlp.translateTo) : codes.at(tlp.translateFrom), codes.at(tlp.translateTo), Escape(text)).c_str()
|
||||||
|
}) {
|
||||||
|
//In case of sentence translation
|
||||||
|
if (auto translation = Copy(JSON::Parse(httpRequest.response)[L"text"].String())) {
|
||||||
|
if (translation.value() != L"") return { true, translation.value() };}
|
||||||
|
else return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, httpRequest.response) };
|
||||||
|
//In case of single word translation
|
||||||
|
if (auto translation = Copy(JSON::Parse(httpRequest.response)[L"directions"][0][L"translations"][0][L"variants"][0][L"meanings"][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) };
|
||||||
|
}
|
18
text.cpp
18
text.cpp
@ -107,7 +107,7 @@ Please contact Artikash with any problems, feature requests, or questions relati
|
|||||||
You can do so via the project homepage (issues section) or via email
|
You can do so via the project homepage (issues section) or via email
|
||||||
Source code available under GPLv3 at project homepage
|
Source code available under GPLv3 at project homepage
|
||||||
If you like this project, please tell everyone about it! It's time to put AGTH down :))";
|
If you like this project, please tell everyone about it! It's time to put AGTH down :))";
|
||||||
const wchar_t* CL_OPTIONS = LR"(usage: Textractor [-p{process ID|"process name"}]...
|
const wchar_t* CL_OPTIONS = LR"(usage: Textractor [-c] [-p{process ID|"process name"}]...
|
||||||
example: Textractor -p4466 -p"My Game.exe" tries to inject processes with ID 4466 or with name My Game.exe)";
|
example: Textractor -p4466 -p"My Game.exe" tries to inject processes with ID 4466 or with name My Game.exe)";
|
||||||
const wchar_t* UPDATE_AVAILABLE = L"Update available: download it from https://github.com/Artikash/Textractor/releases";
|
const wchar_t* UPDATE_AVAILABLE = L"Update available: download it from https://github.com/Artikash/Textractor/releases";
|
||||||
const wchar_t* ALREADY_INJECTED = L"Textractor: already injected";
|
const wchar_t* ALREADY_INJECTED = L"Textractor: already injected";
|
||||||
@ -160,6 +160,7 @@ 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
|
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)";
|
Click and drag on window edges to move, or the bottom right corner to resize)";
|
||||||
const char* MAX_SENTENCE_SIZE = u8"Max sentence size";
|
const char* MAX_SENTENCE_SIZE = u8"Max sentence size";
|
||||||
|
const char* DONT_TRANSLATE_IF_MATCH = u8"Don't translate if match full";
|
||||||
const char* TOPMOST = u8"Always on top";
|
const char* TOPMOST = u8"Always on top";
|
||||||
const char* DICTIONARY = u8"Dictionary";
|
const char* DICTIONARY = u8"Dictionary";
|
||||||
const char* DICTIONARY_INSTRUCTIONS = u8R"(This file is used only for the "Dictionary" feature of the Extra Window extension.
|
const char* DICTIONARY_INSTRUCTIONS = u8R"(This file is used only for the "Dictionary" feature of the Extra Window extension.
|
||||||
@ -187,6 +188,7 @@ const char* CENTERED_TEXT = u8"Centered text";
|
|||||||
const char* AUTO_RESIZE_WINDOW_HEIGHT = u8"Auto resize window height";
|
const char* AUTO_RESIZE_WINDOW_HEIGHT = u8"Auto resize window height";
|
||||||
const char* CLICK_THROUGH = u8"Click through\tAlt+X";
|
const char* CLICK_THROUGH = u8"Click through\tAlt+X";
|
||||||
const char* HIDE_MOUSEOVER = u8"Hide while mouse on top";
|
const char* HIDE_MOUSEOVER = u8"Hide while mouse on top";
|
||||||
|
const char* HIDE_TEXT = u8"Hide/Show text\tAlt+T";
|
||||||
const char* OPACITY = u8"Opacity";
|
const char* OPACITY = u8"Opacity";
|
||||||
const char* BG_COLOR = u8"Background color";
|
const char* BG_COLOR = u8"Background color";
|
||||||
const char* TEXT_COLOR = u8"Text color";
|
const char* TEXT_COLOR = u8"Text color";
|
||||||
@ -195,6 +197,9 @@ const char* OUTLINE_COLOR = u8"Outline color";
|
|||||||
const char* OUTLINE_SIZE = u8"Outline size";
|
const char* OUTLINE_SIZE = u8"Outline size";
|
||||||
const char* OUTLINE_SIZE_INFO = u8"Size in pixels (recommended to stay below 20% of the font size)";
|
const char* OUTLINE_SIZE_INFO = u8"Size in pixels (recommended to stay below 20% of the font size)";
|
||||||
const char* FONT = u8"Font";
|
const char* FONT = u8"Font";
|
||||||
|
const char* TIMER_HIDE_TEXT = u8"Timer hide text";
|
||||||
|
const char* TEXT_TIMEOUT = u8"Timeout (msec, 0=disabled)";
|
||||||
|
const char* TEXT_TIMEOUT_ADD_PER_CHAR = u8"Additional timeout per char (msec)";
|
||||||
const char* LUA_INTRO = u8R"(--[[
|
const char* LUA_INTRO = u8R"(--[[
|
||||||
ProcessSentence is called each time Textractor receives a sentence of text.
|
ProcessSentence is called each time Textractor receives a sentence of text.
|
||||||
|
|
||||||
@ -616,7 +621,7 @@ padding: длина добавочных данных перед строкой
|
|||||||
Сделать это вы можете на домашней странице (секция issues) или через электронную почту
|
Сделать это вы можете на домашней странице (секция issues) или через электронную почту
|
||||||
Исходный код доступен по лицензии GPLv3 на домашней странице проекта
|
Исходный код доступен по лицензии GPLv3 на домашней странице проекта
|
||||||
Если эта программа вам понравилась, расскажите всем о ней :))";
|
Если эта программа вам понравилась, расскажите всем о ней :))";
|
||||||
CL_OPTIONS = LR"(использование: Textractor [-p{process ID|"process name"}]...
|
CL_OPTIONS = LR"(использование: Textractor [-c] [-p{process ID|"process name"}]...
|
||||||
пример: Textractor -p4466 -p"My Game.exe" попробует присоединиться к процессу с ID 4466 или с именем My Game.exe)";
|
пример: Textractor -p4466 -p"My Game.exe" попробует присоединиться к процессу с ID 4466 или с именем My Game.exe)";
|
||||||
UPDATE_AVAILABLE = L"Доступно обновление: загрузите его на https://github.com/Artikash/Textractor/releases";
|
UPDATE_AVAILABLE = L"Доступно обновление: загрузите его на https://github.com/Artikash/Textractor/releases";
|
||||||
ALREADY_INJECTED = L"Textractor: уже присоединен";
|
ALREADY_INJECTED = L"Textractor: уже присоединен";
|
||||||
@ -875,7 +880,7 @@ Puoi farlo attraverso la pagina principale del progetto (sezione issues) o via e
|
|||||||
Il codice sorgente è disponibile sotto il GPLv3 nella pagina principale
|
Il codice sorgente è disponibile sotto il GPLv3 nella pagina principale
|
||||||
Al momento sono in cerca di un nuovo lavoro: contattatemi per email se conoscete qualcuno che ingaggia periti informatici statunitensi
|
Al momento sono in cerca di un nuovo lavoro: contattatemi per email se conoscete qualcuno che ingaggia periti informatici statunitensi
|
||||||
Se ti piace questo progetto, parlane con tutti per favore :))";
|
Se ti piace questo progetto, parlane con tutti per favore :))";
|
||||||
CL_OPTIONS = LR"(utilizzo: Textractor [-p{process ID|"process name"}]...
|
CL_OPTIONS = LR"(utilizzo: Textractor [-c] [-p{process ID|"process name"}]...
|
||||||
esempio: Textractor -p4466 -p"My Game.exe" sta tentando di inniettare i processi con l'ID 4466 o con il nome My Game.exe)";
|
esempio: Textractor -p4466 -p"My Game.exe" sta tentando di inniettare i processi con l'ID 4466 o con il nome My Game.exe)";
|
||||||
UPDATE_AVAILABLE = L"Aggiornamento disponibile: scaricala da https://github.com/Artikash/Textractor/releases";
|
UPDATE_AVAILABLE = L"Aggiornamento disponibile: scaricala da https://github.com/Artikash/Textractor/releases";
|
||||||
ALREADY_INJECTED = L"Textractor: già inniettato";
|
ALREADY_INJECTED = L"Textractor: già inniettato";
|
||||||
@ -917,6 +922,7 @@ esempio: Textractor -p4466 -p"My Game.exe" sta tentando di inniettare i processi
|
|||||||
EXTRA_WINDOW_INFO = u8R"(Tasto destro per cambiare le impostazioni
|
EXTRA_WINDOW_INFO = u8R"(Tasto destro per cambiare le impostazioni
|
||||||
Clicca e trascina i bordi della finestra per muoverla, oppure nell'angolo in basso a destra per ridimensionare)";
|
Clicca e trascina i bordi della finestra per muoverla, oppure nell'angolo in basso a destra per ridimensionare)";
|
||||||
MAX_SENTENCE_SIZE = u8"Dimensione massima sentenza";
|
MAX_SENTENCE_SIZE = u8"Dimensione massima sentenza";
|
||||||
|
DONT_TRANSLATE_IF_MATCH = u8"Non traduce se corrisponde completamente";
|
||||||
TOPMOST = u8"Sempre in primo piano";
|
TOPMOST = u8"Sempre in primo piano";
|
||||||
DICTIONARY = u8"Dizionario";
|
DICTIONARY = u8"Dizionario";
|
||||||
DICTIONARY_INSTRUCTIONS = u8R"(Questo file è utilizzato solo per la funzione "Dizionario" dell'estenzione Extra Window.
|
DICTIONARY_INSTRUCTIONS = u8R"(Questo file è utilizzato solo per la funzione "Dizionario" dell'estenzione Extra Window.
|
||||||
@ -945,6 +951,7 @@ Funziona solo se questa estenzione è usata direttamente dopo un'estensione di t
|
|||||||
AUTO_RESIZE_WINDOW_HEIGHT = u8"Auto resize altezza finestra";
|
AUTO_RESIZE_WINDOW_HEIGHT = u8"Auto resize altezza finestra";
|
||||||
CLICK_THROUGH = u8"Clicca attraverso\tAlt+X";
|
CLICK_THROUGH = u8"Clicca attraverso\tAlt+X";
|
||||||
HIDE_MOUSEOVER = u8"Nascondi testo mouseover";
|
HIDE_MOUSEOVER = u8"Nascondi testo mouseover";
|
||||||
|
HIDE_TEXT = u8"Nascondi/Mostra testo\tAlt+T";
|
||||||
OPACITY = u8"Opacità";
|
OPACITY = u8"Opacità";
|
||||||
BG_COLOR = u8"Colore dello sfondo";
|
BG_COLOR = u8"Colore dello sfondo";
|
||||||
TEXT_COLOR = u8"Colore del testo";
|
TEXT_COLOR = u8"Colore del testo";
|
||||||
@ -953,6 +960,9 @@ Funziona solo se questa estenzione è usata direttamente dopo un'estensione di t
|
|||||||
OUTLINE_SIZE = u8"Dimensione del contorno";
|
OUTLINE_SIZE = u8"Dimensione del contorno";
|
||||||
OUTLINE_SIZE_INFO = u8"Dimensione in pixel (consigliato di rimanere sotto il 20% della dimensione del font)";
|
OUTLINE_SIZE_INFO = u8"Dimensione in pixel (consigliato di rimanere sotto il 20% della dimensione del font)";
|
||||||
FONT = u8"Font";
|
FONT = u8"Font";
|
||||||
|
TIMER_HIDE_TEXT = u8"Timer nascondi testo";
|
||||||
|
TEXT_TIMEOUT = u8"Timeout (msec, 0=disattivato)";
|
||||||
|
TEXT_TIMEOUT_ADD_PER_CHAR = u8"Timeout aggiuntivo per carattere (msec)";
|
||||||
LUA_INTRO = u8R"(--[[
|
LUA_INTRO = u8R"(--[[
|
||||||
ProcessSentence è chiamato ogni volta che Textractor riceva una sentenza di testo.
|
ProcessSentence è chiamato ogni volta che Textractor riceva una sentenza di testo.
|
||||||
|
|
||||||
@ -1311,7 +1321,7 @@ Veuillez me contacter pour tout problème, demande de fonctionnalité ou questio
|
|||||||
Vous pouvez le faire via la page d'accueil du projet (section problèmes) ou par e-mail
|
Vous pouvez le faire via la page d'accueil du projet (section problèmes) ou par e-mail
|
||||||
Code source disponible sous GPLv3 sur la page d'accueil du projet
|
Code source disponible sous GPLv3 sur la page d'accueil du projet
|
||||||
Si vous aimez ce projet, parlez-en à tout le monde :))";
|
Si vous aimez ce projet, parlez-en à tout le monde :))";
|
||||||
CL_OPTIONS = LR"(usage: Textractor [-p{process ID|"process name"}]...
|
CL_OPTIONS = LR"(usage: Textractor [-c] [-p{process ID|"process name"}]...
|
||||||
example: Textractor -p4466 -p"My Game.exe" tries to inject processes with ID 4466 or with name My Game.exe)";
|
example: Textractor -p4466 -p"My Game.exe" tries to inject processes with ID 4466 or with name My Game.exe)";
|
||||||
UPDATE_AVAILABLE = L"Mise à jour disponible: téléchargez-la depuis https://github.com/Artikash/Textractor/releases";
|
UPDATE_AVAILABLE = L"Mise à jour disponible: téléchargez-la depuis https://github.com/Artikash/Textractor/releases";
|
||||||
ALREADY_INJECTED = L"Textractor: déjà injecté";
|
ALREADY_INJECTED = L"Textractor: déjà injecté";
|
||||||
|
@ -7983,6 +7983,7 @@ bool InsertCotophaHook2()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool InsertCotophaHook4()
|
bool InsertCotophaHook4()
|
||||||
{
|
{
|
||||||
//by Blu3train
|
//by Blu3train
|
||||||
@ -8022,10 +8023,29 @@ bool InsertCotophaHook4()
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
bool InsertCotophaHook3() {
|
||||||
|
const BYTE bytes[] = { 0x8B,0x75,0xB8,0x8B,0xCE,0x50,0xC6,0x45,0xFC,0x01,0xE8 };
|
||||||
|
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
|
||||||
|
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
|
||||||
|
if (!addr) {
|
||||||
|
ConsoleOutput("vnreng:Cotopha3: Cotopha3 not found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HookParam myhp = {};
|
||||||
|
myhp.address = addr;
|
||||||
|
|
||||||
|
myhp.type = USING_UNICODE | USING_STRING | NO_CONTEXT;
|
||||||
|
myhp.offset = pusha_eax_off - 4;
|
||||||
|
|
||||||
|
char nameForUser[HOOK_NAME_SIZE] = "Cotopha3_EWideString";
|
||||||
|
NewHook(myhp, nameForUser);
|
||||||
|
ConsoleOutput("Insert: Cotopha3_EWideString Hook BY:IOV");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
bool InsertCotophaHook()
|
bool InsertCotophaHook()
|
||||||
{
|
{
|
||||||
return InsertCotophaHook4() || InsertCotophaHook1() || InsertCotophaHook2();
|
return InsertCotophaHook4() || InsertCotophaHook1() || InsertCotophaHook3() || InsertCotophaHook2();
|
||||||
}
|
}
|
||||||
|
|
||||||
// jichi 5/10/2014
|
// jichi 5/10/2014
|
||||||
@ -10980,12 +11000,63 @@ bool InsertWolf2Hook()
|
|||||||
NewHook(hp, "WolfRPG2");
|
NewHook(hp, "WolfRPG2");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
//example-game:妹!せいかつ~ファンタジー~ by:iov
|
||||||
|
bool InsertWolf3Hook()
|
||||||
|
{
|
||||||
|
const BYTE bytes[] = { 0xC7,0x45,0xFC,0x00,0x00,0x00,0x00,0x8B,0x45,0x94,0x83,0xE0,0x01 };
|
||||||
|
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
|
||||||
|
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
|
||||||
|
if (!addr) {
|
||||||
|
ConsoleOutput("vnreng:WolfRPG: pattern3 not found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HookParam myhp = {};
|
||||||
|
myhp.address = addr+41;
|
||||||
|
|
||||||
|
myhp.type = USING_STRING | NO_CONTEXT;
|
||||||
|
myhp.offset = pusha_eax_off - 4;
|
||||||
|
myhp.type |= DATA_INDIRECT;
|
||||||
|
|
||||||
|
myhp.index = 4;
|
||||||
|
|
||||||
|
char nameForUser[HOOK_NAME_SIZE] = "WolfRPG_String_Copy";
|
||||||
|
NewHook(myhp, nameForUser);
|
||||||
|
ConsoleOutput("Insert: WolfRPG_String_Copy Hook");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InsertWolf4Hook() {
|
||||||
|
const BYTE bytes[] = {0xC6,0x45,0xFC,0x29,0x8B,0x8D,0xE0,0xEF,0xFF,0xFF,0xE8,XX4,0x50,0x8B,0x4D,0xE8,0x2B,0x4D,0xEC };
|
||||||
|
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
|
||||||
|
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
|
||||||
|
if (!addr) {
|
||||||
|
ConsoleOutput("vnreng:WolfRPG: pattern4 not found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HookParam myhp = {};
|
||||||
|
myhp.address = addr + 16;
|
||||||
|
|
||||||
|
myhp.type = USING_STRING | NO_CONTEXT;
|
||||||
|
myhp.offset = pusha_eax_off - 4;
|
||||||
|
// myhp.type |= DATA_INDIRECT;
|
||||||
|
|
||||||
|
// myhp.index = 4;
|
||||||
|
|
||||||
|
char nameForUser[HOOK_NAME_SIZE] = "WolfRPG4";
|
||||||
|
NewHook(myhp, nameForUser);
|
||||||
|
ConsoleOutput("Insert: WolfRPG4 Hook");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // WolfRPG namespace
|
} // WolfRPG namespace
|
||||||
|
|
||||||
bool InsertWolfHook()
|
bool InsertWolfHook()
|
||||||
{
|
{
|
||||||
return InsertOldWolfHook(), InsertWolf2Hook();
|
return InsertOldWolfHook(), InsertWolf2Hook(), InsertWolf3Hook(), InsertWolf4Hook();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InsertIGSDynamicHook(LPVOID addr, DWORD frame, DWORD stack)
|
bool InsertIGSDynamicHook(LPVOID addr, DWORD frame, DWORD stack)
|
||||||
@ -11420,6 +11491,24 @@ void SpecialHookWillPlusA(DWORD esp_base, HookParam *, BYTE index, DWORD *data,
|
|||||||
|
|
||||||
bool InsertWillPlusAHook()
|
bool InsertWillPlusAHook()
|
||||||
{
|
{
|
||||||
|
//by iov
|
||||||
|
const BYTE bytes2[] = { 0x8B,0x00,0xFF,0x76,0xFC,0x8B,0xCF,0x50 };
|
||||||
|
ULONG range2 = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
|
||||||
|
ULONG addr2 = MemDbg::findBytes(bytes2, sizeof(bytes2), processStartAddress, processStartAddress + range2);
|
||||||
|
if (addr2) {
|
||||||
|
HookParam myhp = {};
|
||||||
|
myhp.address = addr2 + 2;
|
||||||
|
|
||||||
|
myhp.type = USING_UNICODE | NO_CONTEXT | USING_STRING;
|
||||||
|
|
||||||
|
myhp.offset = pusha_eax_off - 4;//esp+4
|
||||||
|
|
||||||
|
char nameForUser[HOOK_NAME_SIZE] = "WillPlus3_memcpy";
|
||||||
|
NewHook(myhp, nameForUser);
|
||||||
|
ConsoleOutput("Insert: WillPlus3_memcpy Hook");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const BYTE bytes[] = {
|
const BYTE bytes[] = {
|
||||||
0x81,0xec, 0x14,0x08,0x00,0x00 // 0042B5E0 81EC 14080000 SUB ESP,0x814 ; jichi: text in eax, name in eax - 1024, able to copy
|
0x81,0xec, 0x14,0x08,0x00,0x00 // 0042B5E0 81EC 14080000 SUB ESP,0x814 ; jichi: text in eax, name in eax - 1024, able to copy
|
||||||
};
|
};
|
||||||
@ -11552,7 +11641,7 @@ static bool InsertNewWillPlusHook()
|
|||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
hook cmp esi,0x3000
|
hook cmp esi or ebx,0x3000
|
||||||
Sample games:
|
Sample games:
|
||||||
https://vndb.org/r54549
|
https://vndb.org/r54549
|
||||||
https://vndb.org/v22705
|
https://vndb.org/v22705
|
||||||
@ -11560,17 +11649,25 @@ static bool InsertNewWillPlusHook()
|
|||||||
https://vndb.org/v25719
|
https://vndb.org/v25719
|
||||||
https://vndb.org/v27227
|
https://vndb.org/v27227
|
||||||
https://vndb.org/v27385
|
https://vndb.org/v27385
|
||||||
|
https://vndb.org/v34544
|
||||||
|
https://vndb.org/v35279
|
||||||
|
https://vndb.org/r94284
|
||||||
*/
|
*/
|
||||||
const BYTE pattern[] =
|
const BYTE pattern[] =
|
||||||
{
|
{
|
||||||
0x81,0xfe,0x00,0x30,0x00,0x00 //81FE 00300000 cmp esi,0x3000
|
0x81,XX, 0x00,0x30,0x00,0x00 // 81FE or FB 00300000 cmp esi or ebx,0x3000
|
||||||
|
// je xx
|
||||||
|
// hook here
|
||||||
};
|
};
|
||||||
for (auto addr : Util::SearchMemory(pattern, sizeof(pattern), PAGE_EXECUTE, processStartAddress, processStopAddress))
|
for (auto addr : Util::SearchMemory(pattern, sizeof(pattern), PAGE_EXECUTE, processStartAddress, processStopAddress))
|
||||||
{
|
{
|
||||||
|
BYTE byte = *(BYTE*)(addr + 1);
|
||||||
|
if (byte != 0xfe && byte != 0xfb)
|
||||||
|
continue;
|
||||||
HookParam hp = {};
|
HookParam hp = {};
|
||||||
hp.address = addr;
|
hp.address = addr + 8;
|
||||||
hp.type = USING_UNICODE;
|
hp.type = USING_UNICODE;
|
||||||
hp.offset = pusha_esi_off - 4;
|
hp.offset = byte == 0xfe ? pusha_esi_off - 4 : pusha_ebx_off - 4;
|
||||||
hp.length_offset = 1;
|
hp.length_offset = 1;
|
||||||
NewHook(hp, "WillPlus3");
|
NewHook(hp, "WillPlus3");
|
||||||
found = true;
|
found = true;
|
||||||
@ -19886,9 +19983,115 @@ bool InsertRenpyHook()
|
|||||||
ConsoleOutput("Textractor: Ren'py failed: failed to find python2X.dll");
|
ConsoleOutput("Textractor: Ren'py failed: failed to find python2X.dll");
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uintptr_t(*mono_assembly_get_image)(uintptr_t) = NULL;
|
||||||
|
static char* (*mono_image_get_name)(uintptr_t) = NULL;
|
||||||
|
static uintptr_t(*mono_class_from_name)(uintptr_t, char*, char*) = NULL;
|
||||||
|
static uintptr_t(*mono_class_vtable)(uintptr_t, uintptr_t) = NULL;
|
||||||
|
static void* (*mono_vtable_get_static_field_data)(uintptr_t) = NULL;
|
||||||
|
static uintptr_t(*mono_class_get_method_from_name)(uintptr_t, char*, int) = NULL;
|
||||||
|
static uintptr_t(*mono_class_get_property_from_name)(uintptr_t, char*) = NULL;
|
||||||
|
static uintptr_t(*mono_property_get_set_method)(uintptr_t) = NULL;
|
||||||
|
static uint64_t* (*mono_compile_method)(uintptr_t) = NULL;
|
||||||
|
static MonoDomain* (*mono_get_root_domain)() = NULL;
|
||||||
|
static void (*mono_thread_attach)(MonoDomain*) = NULL;
|
||||||
|
int getV8StringLength(uintptr_t stack, uintptr_t data) {
|
||||||
|
int len = *(int*)(data - 4);
|
||||||
|
int checkLength = len > 0 && len < PIPE_BUFFER_SIZE ? len : 0;
|
||||||
|
for (size_t i = 0; i < checkLength; i++)
|
||||||
|
{
|
||||||
|
if (*(WORD*)(data + i * 2) == 0x0)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return checkLength * 2;
|
||||||
|
|
||||||
|
}
|
||||||
|
void MonoCallBack(uintptr_t assembly, void* userData) {
|
||||||
|
uintptr_t mono_property = NULL;
|
||||||
|
uintptr_t image = mono_assembly_get_image(assembly);
|
||||||
|
// TMP_Text TextMeshProUGUI
|
||||||
|
auto mono_tmp_class = mono_class_from_name(image, "TMPro", "TMP_Text");
|
||||||
|
auto mono_ugui_class = mono_class_from_name(image, "UnityEngine.UI", "Text");
|
||||||
|
auto mono_ngui_class = mono_class_from_name(image, "", "UILabel");
|
||||||
|
if (!mono_tmp_class && !mono_ugui_class && !mono_ngui_class)
|
||||||
|
return;
|
||||||
|
if (mono_tmp_class) {
|
||||||
|
mono_property = mono_class_get_property_from_name(mono_tmp_class, "text");
|
||||||
|
}
|
||||||
|
else if(mono_ugui_class)
|
||||||
|
{
|
||||||
|
mono_property = mono_class_get_property_from_name(mono_ugui_class, "text");
|
||||||
|
}
|
||||||
|
else if (mono_ngui_class) {
|
||||||
|
mono_property = mono_class_get_property_from_name(mono_ngui_class, "text");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mono_property == NULL)
|
||||||
|
return;
|
||||||
|
auto mono_set_method = mono_property_get_set_method(mono_property);
|
||||||
|
//注意必须调用mono_thread_attach 附加到主domain 才能调用 mono_method_get_unmanaged_thunk mono_compile_method 或mono_runtime_invoke
|
||||||
|
mono_thread_attach(mono_get_root_domain());
|
||||||
|
uint64_t* method_pointer = mono_compile_method(mono_set_method);
|
||||||
|
if (method_pointer) {
|
||||||
|
HookParam hp = {};
|
||||||
|
hp.type = USING_STRING | USING_UNICODE |DATA_INDIRECT;
|
||||||
|
hp.address = (uint64_t)method_pointer;
|
||||||
|
hp.offset = pusha_esp_off-4; // esp+8
|
||||||
|
hp.index = 12;
|
||||||
|
hp.padding = 12;
|
||||||
|
|
||||||
|
if (mono_tmp_class) {
|
||||||
|
ConsoleOutput("Mono_X86,Insert: TextMeshProUGUI_set_text Hook BY:IOV");
|
||||||
|
hp.length_fun = getV8StringLength;
|
||||||
|
NewHook(hp, "TextMeshProUGUI_set_text");
|
||||||
|
}
|
||||||
|
else if(mono_ugui_class)
|
||||||
|
{
|
||||||
|
ConsoleOutput("Mono_X86,Insert: UGUI_set_text Hook BY:IOV");
|
||||||
|
hp.length_fun = getV8StringLength;
|
||||||
|
NewHook(hp, "UGUI_set_text");
|
||||||
|
}
|
||||||
|
else if(mono_ngui_class)
|
||||||
|
{
|
||||||
|
ConsoleOutput("Mono_X86,Insert: NGUI_set_text Hook BY:IOV");
|
||||||
|
hp.length_fun = getV8StringLength;
|
||||||
|
NewHook(hp, "NGUI_set_text");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
bool InsertMonoHooksByAssembly(HMODULE module) {
|
||||||
|
//void mono_assembly_foreach (GFunc func, gpointer user_data)
|
||||||
|
//遍历程序集。用于获取目标程序集的指针。其中的func 是一个回调函数,要自己写。它有两个参数,前者就是MonoAssembly*,而后者则是user_data
|
||||||
|
static auto mono_assembly_foreach = (void (*)(void (*)(uintptr_t, void*), uintptr_t))GetProcAddress(module, "mono_assembly_foreach");
|
||||||
|
mono_assembly_get_image = (uintptr_t(*)(uintptr_t))GetProcAddress(module, "mono_assembly_get_image");
|
||||||
|
mono_image_get_name = (char* (*)(uintptr_t))GetProcAddress(module, "mono_image_get_name");
|
||||||
|
mono_class_from_name = (uintptr_t(*)(uintptr_t, char*, char*))GetProcAddress(module, "mono_class_from_name");
|
||||||
|
mono_class_get_property_from_name = (uintptr_t(*)(uintptr_t, char*))GetProcAddress(module, "mono_class_get_property_from_name");
|
||||||
|
mono_property_get_set_method = (uintptr_t(*)(uintptr_t))GetProcAddress(module, "mono_property_get_set_method");
|
||||||
|
mono_compile_method = (uint64_t * (*)(uintptr_t))GetProcAddress(module, "mono_compile_method");
|
||||||
|
mono_get_root_domain = (MonoDomain * (*)())GetProcAddress(module, "mono_get_root_domain");
|
||||||
|
|
||||||
|
mono_thread_attach = (void (*)(MonoDomain*))GetProcAddress(module, "mono_thread_attach");
|
||||||
|
if (mono_assembly_foreach && mono_assembly_get_image && mono_image_get_name && mono_class_from_name &&
|
||||||
|
mono_class_get_property_from_name && mono_property_get_set_method && mono_compile_method &&
|
||||||
|
mono_get_root_domain && mono_thread_attach) {
|
||||||
|
mono_assembly_foreach(MonoCallBack, NULL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void InsertMonoHook(HMODULE h)
|
void InsertMonoHook(HMODULE h)
|
||||||
{
|
{
|
||||||
static HMODULE mono = h;
|
static HMODULE mono = h;
|
||||||
|
if (InsertMonoHooksByAssembly(mono))
|
||||||
|
return ;
|
||||||
/* Artikash 2/13/2019:
|
/* Artikash 2/13/2019:
|
||||||
How to hook Mono/Unity3D:
|
How to hook Mono/Unity3D:
|
||||||
Find all standard function prologs in memory with write/execute permission: these represent possible JIT compiled functions
|
Find all standard function prologs in memory with write/execute permission: these represent possible JIT compiled functions
|
||||||
@ -19975,7 +20178,49 @@ bool NoAsciiFilter(LPVOID data, DWORD *size, HookParam *, BYTE)
|
|||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
bool InsertAnimHook() {
|
||||||
|
const BYTE bytes[] = { 0xC7,0x45,0xFC,0x01,0x00,0x00,0x00,0x8B,0x4D,0x10,0x51,0x8D,0x8D,0x40,0x7E,0xFF,0xFF };
|
||||||
|
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
|
||||||
|
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
|
||||||
|
if (!addr) {
|
||||||
|
ConsoleOutput("vnreng:Anim: pattern not found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
HookParam myhp = {};
|
||||||
|
myhp.address = addr+10;
|
||||||
|
|
||||||
|
myhp.type = USING_STRING| NO_CONTEXT; // /HQ 不使用上下文区分 把所有线程的文本都提取
|
||||||
|
|
||||||
|
// data_offset
|
||||||
|
myhp.offset = pusha_ecx_off - 4;//esp+4
|
||||||
|
|
||||||
|
char nameForUser[HOOK_NAME_SIZE] = "Anim";
|
||||||
|
NewHook(myhp, nameForUser);
|
||||||
|
ConsoleOutput("Insert: Anim Hook by:IOV");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InsertAnim2Hook() {
|
||||||
|
const BYTE bytes[] = { 0xC7,0x45,0xFC,0x01,0x00,0x00,0x00,0x8B,0x45,0x10,0x50,0x8D,0x8D,0xAC,0x7E,0xFF,0xFF };
|
||||||
|
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
|
||||||
|
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
|
||||||
|
if (!addr) {
|
||||||
|
ConsoleOutput("vnreng:Anim2: pattern not found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
HookParam myhp = {};
|
||||||
|
myhp.address = addr + 10;
|
||||||
|
|
||||||
|
myhp.type = USING_STRING | NO_CONTEXT;
|
||||||
|
|
||||||
|
// data_offset
|
||||||
|
myhp.offset = pusha_eax_off - 4;//esp+4
|
||||||
|
|
||||||
|
char nameForUser[HOOK_NAME_SIZE] = "Anim2";
|
||||||
|
NewHook(myhp, nameForUser);
|
||||||
|
ConsoleOutput("Insert: Anim2 Hook by:IOV");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
bool Anim3Filter(LPVOID data, DWORD *size, HookParam *, BYTE)
|
bool Anim3Filter(LPVOID data, DWORD *size, HookParam *, BYTE)
|
||||||
{
|
{
|
||||||
auto text = reinterpret_cast<LPSTR>(data);
|
auto text = reinterpret_cast<LPSTR>(data);
|
||||||
|
@ -157,6 +157,9 @@ bool InsertWillPlusHook(); // WillPlus: Rio.arc
|
|||||||
bool InsertWolfHook(); // Wolf: Data.wolf
|
bool InsertWolfHook(); // Wolf: Data.wolf
|
||||||
bool InsertYukaSystemHooks(); // YukaSystem2: *.ykc
|
bool InsertYukaSystemHooks(); // YukaSystem2: *.ykc
|
||||||
bool InsertYurisHook(); // YU-RIS: *.ypf
|
bool InsertYurisHook(); // YU-RIS: *.ypf
|
||||||
|
bool InsertAnimHook(); //
|
||||||
|
bool InsertAnim2Hook(); //
|
||||||
|
bool InsertCotophaHook3();
|
||||||
bool InsertONScripterruHooks(); // ONScripter-RU: resource string
|
bool InsertONScripterruHooks(); // ONScripter-RU: resource string
|
||||||
bool InsertSakanaGLHook(); // SakanaGL: sakanagl.dll
|
bool InsertSakanaGLHook(); // SakanaGL: sakanagl.dll
|
||||||
bool InsertDebonosuWorksHook(); // DebonosuWorks: resource string
|
bool InsertDebonosuWorksHook(); // DebonosuWorks: resource string
|
||||||
|
@ -556,9 +556,10 @@ bool DetermineEngineByFile4()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Util::CheckFile(L"voice\\*.pck")) {
|
if (Util::CheckFile(L"voice\\*.pck")) {
|
||||||
return /*InsertAnimHook() || InsertAnim2Hook() ||*/ InsertAnim3Hook();
|
return InsertAnimHook() || InsertAnim2Hook() || InsertAnim3Hook();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// jichi 11/22/2015: 凍京NECRO 体験版
|
// jichi 11/22/2015: 凍京NECRO 体験版
|
||||||
// Jazzinghen 23/05/2020: Add check for 凍京NECRO
|
// Jazzinghen 23/05/2020: Add check for 凍京NECRO
|
||||||
// ResEdit shows multiple potential strings:
|
// ResEdit shows multiple potential strings:
|
||||||
|
@ -383,9 +383,132 @@ namespace Engine
|
|||||||
}
|
}
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
//MonoImage* mono_assembly_get_image(MonoAssembly* assembly):获取程序集的镜像。后面几乎所有的操作都会以MonoImage* 为第一个参数。
|
||||||
|
static uintptr_t (*mono_assembly_get_image)(uintptr_t) = NULL;
|
||||||
|
// const char* mono_image_get_name(MonoImage * image) :获取程序集名。我们用它判断哪个程序集是我们的目标
|
||||||
|
static char* (*mono_image_get_name)(uintptr_t) = NULL;
|
||||||
|
//MonoClass* mono_class_from_name (MonoImage *image, const char* name_space, const char *name):通过类名获取类(非实例)。
|
||||||
|
static uintptr_t(*mono_class_from_name)(uintptr_t, char*, char*) = NULL;
|
||||||
|
//MonoVTable* mono_class_vtable (MonoDomain *domain, MonoClass *klass):获取vtable,我们通过它可以找到静态字段的起始地址。
|
||||||
|
static uintptr_t(*mono_class_vtable)(uintptr_t, uintptr_t) = NULL;
|
||||||
|
//void* mono_vtable_get_static_field_data (MonoVTable *vt):获取静态字段的起始地址。
|
||||||
|
static void* (*mono_vtable_get_static_field_data)(uintptr_t) = NULL;
|
||||||
|
//MonoMethod* mono_class_get_method_from_name (MonoClass *klass, const char *name, int param_count):获取方法(非native code地址)。
|
||||||
|
//其中param_count是参数数量,可以输入-1来省略。此函数无法获取重载的方法,但对于我们来说足够了。
|
||||||
|
static uintptr_t(*mono_class_get_method_from_name)(uintptr_t, char*,int) = NULL;
|
||||||
|
//获取属性。用它可以进一步获得属性的getter和setter。
|
||||||
|
//MonoProperty* mono_class_get_property_from_name(MonoClass* klass, const char* name):
|
||||||
|
static uintptr_t(*mono_class_get_property_from_name)(uintptr_t, char*) = NULL;
|
||||||
|
//获取属性的getter和setter。
|
||||||
|
//MonoMethod* mono_property_get_get_method(MonoProperty* prop) 与 MonoMethod* mono_property_get_set_method(MonoProperty* prop):
|
||||||
|
static uintptr_t(*mono_property_get_set_method)(uintptr_t) = NULL;
|
||||||
|
// (不安全)返回方法的地址,如果方法尚未编译,则JIT开始编译。这个是解决问题的核心方法。 gpointer mono_compile_method (MonoMethod *method):
|
||||||
|
static uint64_t* (*mono_compile_method)(uintptr_t) = NULL;
|
||||||
|
|
||||||
|
//获取函数的非托管块指针 (native) gpointer mono_method_get_unmanaged_thunk (MonoMethod *method)
|
||||||
|
//使用这个来获取native代码 方法尚未编译,会执行编译 并提取 x86版本可能使用的是__stdcall
|
||||||
|
static uint64_t* (*mono_method_get_unmanaged_thunk)(uintptr_t) = NULL;
|
||||||
|
//MonoDomain* mono_get_root_domain (void) :获取主作用域。用于附加线程以及获取静态字段的地址。
|
||||||
|
static MonoDomain* (*mono_get_root_domain)() = NULL;
|
||||||
|
//void mono_thread_attach (MonoDomain*):附加到进程的主线程。这个操作是必须的。
|
||||||
|
static void (*mono_thread_attach)(MonoDomain*) = NULL;
|
||||||
|
|
||||||
|
//MonoAssembly* assembly,而后者则是void* user_data
|
||||||
|
int getV8StringLength(uintptr_t stack, uintptr_t data) {
|
||||||
|
int len = *(int*)(data - 4);
|
||||||
|
int checkLength = len > 0 && len < PIPE_BUFFER_SIZE ? len : 0;
|
||||||
|
//检查是否为错误的unicode字符
|
||||||
|
for (size_t i = 0; i < checkLength; i++)
|
||||||
|
{
|
||||||
|
if (*(WORD*)(data + i * 2) == 0x0)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return checkLength * 2;
|
||||||
|
|
||||||
|
}
|
||||||
|
void MonoCallBack(uintptr_t assembly, void* userData) {
|
||||||
|
uintptr_t mono_property = NULL;
|
||||||
|
uintptr_t image=mono_assembly_get_image(assembly);
|
||||||
|
// TMP_Text TextMeshProUGUI
|
||||||
|
auto mono_tmp_class=mono_class_from_name(image, "TMPro", "TMP_Text");
|
||||||
|
auto mono_ugui_class = mono_class_from_name(image, "UnityEngine.UI", "Text");
|
||||||
|
auto mono_ngui_class = mono_class_from_name(image, "", "UILabel");
|
||||||
|
if (!mono_tmp_class && !mono_ugui_class && !mono_ngui_class)
|
||||||
|
return;
|
||||||
|
if (mono_tmp_class) {
|
||||||
|
mono_property = mono_class_get_property_from_name(mono_tmp_class, "text");
|
||||||
|
}
|
||||||
|
else if (mono_ugui_class)
|
||||||
|
{
|
||||||
|
mono_property = mono_class_get_property_from_name(mono_ugui_class, "text");
|
||||||
|
}
|
||||||
|
else if (mono_ngui_class) {
|
||||||
|
mono_property = mono_class_get_property_from_name(mono_ngui_class, "text");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mono_property == NULL)
|
||||||
|
return;
|
||||||
|
auto mono_set_method= mono_property_get_set_method(mono_property);
|
||||||
|
//注意必须调用mono_thread_attach 附加到主domain 才能调用 mono_method_get_unmanaged_thunk mono_compile_method 或mono_runtime_invoke
|
||||||
|
mono_thread_attach(mono_get_root_domain());
|
||||||
|
uint64_t* method_pointer= mono_compile_method(mono_set_method);
|
||||||
|
if (method_pointer) {
|
||||||
|
HookParam hp = {};
|
||||||
|
hp.type = USING_STRING | USING_UNICODE;
|
||||||
|
hp.address = (uint64_t)method_pointer;
|
||||||
|
hp.offset = -0x28; // rdx
|
||||||
|
//hp.index = 0;
|
||||||
|
hp.padding = 0x14;
|
||||||
|
if (mono_tmp_class) {
|
||||||
|
ConsoleOutput("Mono_X64,Insert: TextMeshProUGUI_set_text Hook BY:IOV");
|
||||||
|
hp.length_fun = getV8StringLength;
|
||||||
|
NewHook(hp, "TextMeshProUGUI_set_text");
|
||||||
|
}
|
||||||
|
else if (mono_ugui_class)
|
||||||
|
{
|
||||||
|
ConsoleOutput("Mono_X64,Insert: UGUI_set_text Hook BY:IOV");
|
||||||
|
hp.length_fun = getV8StringLength;
|
||||||
|
NewHook(hp, "UGUI_set_text");
|
||||||
|
}
|
||||||
|
else if (mono_ngui_class)
|
||||||
|
{
|
||||||
|
ConsoleOutput("Mono_X64,Insert: NGUI_set_text Hook BY:IOV");
|
||||||
|
hp.length_fun = getV8StringLength;
|
||||||
|
NewHook(hp, "NGUI_set_text");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InsertMonoHooksByAssembly(HMODULE module) {
|
||||||
|
//void mono_assembly_foreach (GFunc func, gpointer user_data)
|
||||||
|
//遍历程序集。用于获取目标程序集的指针。其中的func 是一个回调函数,要自己写。它有两个参数,前者就是MonoAssembly*,而后者则是user_data
|
||||||
|
static auto mono_assembly_foreach = (void (*)(void (*)(uintptr_t, void*), uintptr_t))GetProcAddress(module, "mono_assembly_foreach");
|
||||||
|
mono_assembly_get_image= (uintptr_t(*)(uintptr_t))GetProcAddress(module, "mono_assembly_get_image");
|
||||||
|
mono_image_get_name = (char* (*)(uintptr_t))GetProcAddress(module, "mono_image_get_name");
|
||||||
|
mono_class_from_name = (uintptr_t(*)(uintptr_t, char*, char*))GetProcAddress(module, "mono_class_from_name");
|
||||||
|
mono_class_get_property_from_name = (uintptr_t(*)(uintptr_t, char*))GetProcAddress(module, "mono_class_get_property_from_name");
|
||||||
|
mono_property_get_set_method = (uintptr_t(*)(uintptr_t))GetProcAddress(module, "mono_property_get_set_method");
|
||||||
|
mono_compile_method = (uint64_t * (*)(uintptr_t))GetProcAddress(module, "mono_compile_method");
|
||||||
|
//mono_method_get_unmanaged_thunk= (uint64_t * (*)(uintptr_t))GetProcAddress(module, "mono_method_get_unmanaged_thunk");
|
||||||
|
mono_get_root_domain = (MonoDomain * (*)())GetProcAddress(module, "mono_get_root_domain");
|
||||||
|
|
||||||
|
mono_thread_attach = (void (*)(MonoDomain*))GetProcAddress(module, "mono_thread_attach");
|
||||||
|
if (mono_assembly_foreach && mono_assembly_get_image && mono_image_get_name && mono_class_from_name &&
|
||||||
|
mono_class_get_property_from_name && mono_property_get_set_method && mono_compile_method &&
|
||||||
|
mono_get_root_domain && mono_thread_attach) {
|
||||||
|
mono_assembly_foreach(MonoCallBack, NULL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
bool InsertMonoHooks(HMODULE module)
|
bool InsertMonoHooks(HMODULE module)
|
||||||
{
|
{
|
||||||
|
return InsertMonoHooksByAssembly(module);
|
||||||
auto SpecialHookMonoString = nullptr;
|
auto SpecialHookMonoString = nullptr;
|
||||||
static HMODULE mono = module;
|
static HMODULE mono = module;
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
@ -567,7 +690,42 @@ namespace Engine
|
|||||||
ConsoleOutput("Textractor: Ren'py failed: failed to find python2X.dll");
|
ConsoleOutput("Textractor: Ren'py failed: failed to find python2X.dll");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool InsertGodotHook2_X64() {
|
int getGodoStringLength(uintptr_t stack, uintptr_t data) {
|
||||||
|
int len = *(int*)(data - 4);
|
||||||
|
len--;
|
||||||
|
int checkLength = len > 0 && len < PIPE_BUFFER_SIZE ? len : 0;
|
||||||
|
//检查是否为错误的unicode字符
|
||||||
|
for (size_t i = 0; i < checkLength; i++)
|
||||||
|
{
|
||||||
|
if (*(WORD*)(data + i * 2) == 0x0)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return checkLength * 2;
|
||||||
|
|
||||||
|
}
|
||||||
|
//BY:IOV
|
||||||
|
bool InsertGodotHook_X64() {
|
||||||
|
const BYTE bytes[] = { 0x8B,0x40,0xFC,0x83,0xF8,0x01,0x83,0xD0,0xFF,0x41,0x39,0xC6 };
|
||||||
|
|
||||||
|
ULONG64 range = min(processStopAddress - processStartAddress, X64_MAX_REL_ADDR);
|
||||||
|
for (auto addr : Util::SearchMemory(bytes, sizeof(bytes), PAGE_EXECUTE, processStartAddress, processStartAddress + range)) {
|
||||||
|
HookParam myhp = {};
|
||||||
|
myhp.address = addr;
|
||||||
|
|
||||||
|
myhp.type = USING_STRING | USING_UNICODE | NO_CONTEXT; // /HQ 不使用上下文区分 把所有线程的文本都提取
|
||||||
|
//myhp.padding = 0xc;//[esp+4]+padding
|
||||||
|
// data_offset
|
||||||
|
myhp.offset = -0xC-4;//RCX
|
||||||
|
myhp.length_fun = getGodoStringLength;
|
||||||
|
char nameForUser[HOOK_NAME_SIZE] = "RichTextLabel_add_text";
|
||||||
|
NewHook(myhp, nameForUser);
|
||||||
|
ConsoleOutput("Insert: Godot_add_text_X64 Hook ");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleOutput("vnreng:Godot_x64: pattern not found");
|
||||||
|
return false;
|
||||||
|
} bool InsertGodotHook2_X64() {
|
||||||
//by Blu3train
|
//by Blu3train
|
||||||
/*
|
/*
|
||||||
* Sample games:
|
* Sample games:
|
||||||
|
Loading…
Reference in New Issue
Block a user