allow replacement in regex filter and make names consistent

This commit is contained in:
Akash Mozumdar 2021-01-18 18:58:21 -07:00
parent 4219115a40
commit 084f26a72d
9 changed files with 130 additions and 102 deletions

View File

@ -303,8 +303,8 @@ namespace
QDialog dialog(This, Qt::WindowCloseButtonHint); QDialog dialog(This, Qt::WindowCloseButtonHint);
QFormLayout layout(&dialog); QFormLayout layout(&dialog);
QCheckBox cjkCheckBox(&dialog); QCheckBox CJKCheck(&dialog);
layout.addRow(SEARCH_CJK, &cjkCheckBox); layout.addRow(SEARCH_CJK, &CJKCheck);
QDialogButtonBox confirm(QDialogButtonBox::Ok | QDialogButtonBox::Help | QDialogButtonBox::Retry, &dialog); QDialogButtonBox confirm(QDialogButtonBox::Ok | QDialogButtonBox::Help | QDialogButtonBox::Retry, &dialog);
layout.addRow(&confirm); layout.addRow(&confirm);
confirm.button(QDialogButtonBox::Ok)->setText(START_HOOK_SEARCH); confirm.button(QDialogButtonBox::Ok)->setText(START_HOOK_SEARCH);
@ -323,29 +323,29 @@ namespace
{ {
QDialog dialog(This, Qt::WindowCloseButtonHint); QDialog dialog(This, Qt::WindowCloseButtonHint);
QFormLayout layout(&dialog); QFormLayout layout(&dialog);
QLineEdit textInput(&dialog); QLineEdit textEdit(&dialog);
layout.addRow(TEXT, &textInput); layout.addRow(TEXT, &textEdit);
QSpinBox codepageInput(&dialog); QSpinBox codepageSpin(&dialog);
codepageInput.setMaximum(INT_MAX); codepageSpin.setMaximum(INT_MAX);
codepageInput.setValue(sp.codepage); codepageSpin.setValue(sp.codepage);
layout.addRow(CODEPAGE, &codepageInput); layout.addRow(CODEPAGE, &codepageSpin);
QDialogButtonBox confirm(QDialogButtonBox::Ok); QDialogButtonBox confirm(QDialogButtonBox::Ok);
QObject::connect(&confirm, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); QObject::connect(&confirm, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
layout.addRow(&confirm); layout.addRow(&confirm);
if (!dialog.exec()) return; if (!dialog.exec()) return;
wcsncpy_s(sp.text, S(textInput.text()).c_str(), PATTERN_SIZE - 1); wcsncpy_s(sp.text, S(textEdit.text()).c_str(), PATTERN_SIZE - 1);
try { Host::FindHooks(selectedProcessId, sp); } catch (std::out_of_range) {} try { Host::FindHooks(selectedProcessId, sp); } catch (std::out_of_range) {}
return; return;
} }
filter.setPattern(cjkCheckBox.isChecked() ? "[\\x{3000}-\\x{a000}]{4,}" : "[\\x{0020}-\\x{1000}]{4,}"); filter.setPattern(CJKCheck.isChecked() ? "[\\x{3000}-\\x{a000}]{4,}" : "[\\x{0020}-\\x{1000}]{4,}");
if (customSettings) if (customSettings)
{ {
QDialog dialog(This, Qt::WindowCloseButtonHint); QDialog dialog(This, Qt::WindowCloseButtonHint);
QFormLayout layout(&dialog); QFormLayout layout(&dialog);
QLineEdit patternInput(x64 ? "CC CC 48 89" : "55 8B EC", &dialog); QLineEdit patternEdit(x64 ? "CC CC 48 89" : "55 8B EC", &dialog);
assert(QByteArray::fromHex(patternInput.text().toUtf8()) == QByteArray((const char*)sp.pattern, sp.length)); assert(QByteArray::fromHex(patternEdit.text().toUtf8()) == QByteArray((const char*)sp.pattern, sp.length));
layout.addRow(SEARCH_PATTERN, &patternInput); layout.addRow(SEARCH_PATTERN, &patternEdit);
for (auto [value, label] : Array<int&, const char*>{ for (auto [value, label] : Array<int&, const char*>{
{ sp.searchTime, SEARCH_DURATION }, { sp.searchTime, SEARCH_DURATION },
{ sp.offset, PATTERN_OFFSET }, { sp.offset, PATTERN_OFFSET },
@ -359,36 +359,36 @@ namespace
layout.addRow(label, spinBox); layout.addRow(label, spinBox);
QObject::connect(spinBox, qOverload<int>(&QSpinBox::valueChanged), [&value](int newValue) { value = newValue; }); QObject::connect(spinBox, qOverload<int>(&QSpinBox::valueChanged), [&value](int newValue) { value = newValue; });
} }
QLineEdit boundInput(QFileInfo(S(GetModuleFilename(selectedProcessId).value_or(L""))).fileName(), &dialog); QLineEdit boundEdit(QFileInfo(S(GetModuleFilename(selectedProcessId).value_or(L""))).fileName(), &dialog);
layout.addRow(SEARCH_MODULE, &boundInput); layout.addRow(SEARCH_MODULE, &boundEdit);
for (auto [value, label] : Array<uintptr_t&, const char*>{ for (auto [value, label] : Array<uintptr_t&, const char*>{
{ sp.minAddress, MIN_ADDRESS }, { sp.minAddress, MIN_ADDRESS },
{ sp.maxAddress, MAX_ADDRESS }, { sp.maxAddress, MAX_ADDRESS },
{ sp.padding, STRING_OFFSET }, { sp.padding, STRING_OFFSET },
}) })
{ {
auto input = new QLineEdit(QString::number(value, 16), &dialog); auto edit = new QLineEdit(QString::number(value, 16), &dialog);
layout.addRow(label, input); layout.addRow(label, edit);
QObject::connect(input, &QLineEdit::textEdited, [&value](QString text) { if (uintptr_t newValue = text.toULongLong(&ok, 16); ok) value = newValue; }); QObject::connect(edit, &QLineEdit::textEdited, [&value](QString text) { if (uintptr_t newValue = text.toULongLong(&ok, 16); ok) value = newValue; });
} }
QLineEdit filterInput(filter.pattern(), &dialog); QLineEdit filterEdit(filter.pattern(), &dialog);
layout.addRow(HOOK_SEARCH_FILTER, &filterInput); layout.addRow(HOOK_SEARCH_FILTER, &filterEdit);
QPushButton startButton(START_HOOK_SEARCH, &dialog); QPushButton startButton(START_HOOK_SEARCH, &dialog);
layout.addWidget(&startButton); layout.addWidget(&startButton);
QObject::connect(&startButton, &QPushButton::clicked, &dialog, &QDialog::accept); QObject::connect(&startButton, &QPushButton::clicked, &dialog, &QDialog::accept);
if (!dialog.exec()) return; if (!dialog.exec()) return;
if (patternInput.text().contains('.')) if (patternEdit.text().contains('.'))
{ {
wcsncpy_s(sp.exportModule, S(patternInput.text()).c_str(), MAX_MODULE_SIZE - 1); wcsncpy_s(sp.exportModule, S(patternEdit.text()).c_str(), MAX_MODULE_SIZE - 1);
sp.length = 1; sp.length = 1;
} }
else else
{ {
QByteArray pattern = QByteArray::fromHex(patternInput.text().replace("??", QString::number(XX, 16)).toUtf8()); QByteArray pattern = QByteArray::fromHex(patternEdit.text().replace("??", QString::number(XX, 16)).toUtf8());
memcpy(sp.pattern, pattern.data(), sp.length = min(pattern.size(), PATTERN_SIZE)); memcpy(sp.pattern, pattern.data(), sp.length = min(pattern.size(), PATTERN_SIZE));
} }
wcsncpy_s(sp.boundaryModule, S(boundInput.text()).c_str(), MAX_MODULE_SIZE - 1); wcsncpy_s(sp.boundaryModule, S(boundEdit.text()).c_str(), MAX_MODULE_SIZE - 1);
filter.setPattern(filterInput.text()); filter.setPattern(filterEdit.text());
if (!filter.isValid()) filter.setPattern("."); if (!filter.isValid()) filter.setPattern(".");
} }
else else
@ -467,12 +467,12 @@ namespace
layout.addRow(label, spinBox); layout.addRow(label, spinBox);
QObject::connect(&saveButton, &QPushButton::clicked, [spinBox, label, &settings, &value] { settings.setValue(label, value = spinBox->value()); }); QObject::connect(&saveButton, &QPushButton::clicked, [spinBox, label, &settings, &value] { settings.setValue(label, value = spinBox->value()); });
} }
QComboBox localeComboBox(&dialog); QComboBox localeCombo(&dialog);
assert(PROMPT == 0 && ALWAYS == 1 && NEVER == 2); assert(PROMPT == 0 && ALWAYS == 1 && NEVER == 2);
localeComboBox.addItems({ { "Prompt", "Always", "Never" } }); localeCombo.addItems({ { "Prompt", "Always", "Never" } });
localeComboBox.setCurrentIndex(settings.value(CONFIG_JP_LOCALE, PROMPT).toInt()); localeCombo.setCurrentIndex(settings.value(CONFIG_JP_LOCALE, PROMPT).toInt());
layout.addRow(CONFIG_JP_LOCALE, &localeComboBox); layout.addRow(CONFIG_JP_LOCALE, &localeCombo);
QObject::connect(&localeComboBox, qOverload<int>(&QComboBox::activated), [&settings](int i) { settings.setValue(CONFIG_JP_LOCALE, i); }); QObject::connect(&localeCombo, qOverload<int>(&QComboBox::activated), [&settings](int i) { settings.setValue(CONFIG_JP_LOCALE, i); });
layout.addWidget(&saveButton); layout.addWidget(&saveButton);
QObject::connect(&saveButton, &QPushButton::clicked, &dialog, &QDialog::accept); QObject::connect(&saveButton, &QPushButton::clicked, &dialog, &QDialog::accept);
dialog.setWindowTitle(SETTINGS); dialog.setWindowTitle(SETTINGS);

View File

@ -3,7 +3,7 @@
extern const wchar_t* TRANSLATION_ERROR; extern const wchar_t* TRANSLATION_ERROR;
extern Synchronized<std::wstring> translateTo, apiKey; extern Synchronized<std::wstring> translateTo, authKey;
const char* TRANSLATION_PROVIDER = "Bing Translate"; const char* TRANSLATION_PROVIDER = "Bing Translate";
const char* GET_API_KEY_FROM = "https://www.microsoft.com/en-us/translator/business/trial/#get-started"; const char* GET_API_KEY_FROM = "https://www.microsoft.com/en-us/translator/business/trial/#get-started";
@ -84,14 +84,14 @@ int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 1000;
std::pair<bool, std::wstring> Translate(const std::wstring& text) std::pair<bool, std::wstring> Translate(const std::wstring& text)
{ {
if (!apiKey->empty()) if (!authKey->empty())
if (HttpRequest httpRequest{ if (HttpRequest httpRequest{
L"Mozilla/5.0 Textractor", L"Mozilla/5.0 Textractor",
L"api.cognitive.microsofttranslator.com", L"api.cognitive.microsofttranslator.com",
L"POST", L"POST",
FormatString(L"/translate?api-version=3.0&to=%s", translateTo.Copy()).c_str(), FormatString(L"/translate?api-version=3.0&to=%s", translateTo.Copy()).c_str(),
FormatString(R"([{"text":"%s"}])", JSON::Escape(WideStringToString(text))), FormatString(R"([{"text":"%s"}])", JSON::Escape(WideStringToString(text))),
FormatString(L"Content-Type: application/json; charset=UTF-8\r\nOcp-Apim-Subscription-Key:%s", apiKey.Copy()).c_str() FormatString(L"Content-Type: application/json; charset=UTF-8\r\nOcp-Apim-Subscription-Key:%s", authKey.Copy()).c_str()
}) })
if (auto translation = Copy(JSON::Parse(httpRequest.response)[0][L"translations"][0][L"text"].String())) return { true, translation.value() }; if (auto translation = Copy(JSON::Parse(httpRequest.response)[0][L"translations"][0][L"text"].String())) return { true, translation.value() };
else return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, httpRequest.response) }; else return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, httpRequest.response) };

View File

@ -5,7 +5,7 @@
extern const wchar_t* TRANSLATION_ERROR; extern const wchar_t* TRANSLATION_ERROR;
extern const char* USE_PREV_SENTENCE_CONTEXT; extern const char* USE_PREV_SENTENCE_CONTEXT;
extern Synchronized<std::wstring> translateTo, apiKey; extern Synchronized<std::wstring> translateTo, authKey;
const char* TRANSLATION_PROVIDER = "DeepL Translate"; const char* TRANSLATION_PROVIDER = "DeepL Translate";
const char* GET_API_KEY_FROM = "https://www.deepl.com/pro.html"; const char* GET_API_KEY_FROM = "https://www.deepl.com/pro.html";
@ -32,20 +32,20 @@ int keyType = CAT;
std::pair<bool, std::wstring> Translate(const std::wstring& text) std::pair<bool, std::wstring> Translate(const std::wstring& text)
{ {
if (!apiKey->empty()) if (!authKey->empty())
if (HttpRequest httpRequest{ if (HttpRequest httpRequest{
L"Mozilla/5.0 Textractor", L"Mozilla/5.0 Textractor",
L"api.deepl.com", L"api.deepl.com",
L"POST", L"POST",
keyType == CAT ? L"/v1/translate" : L"/v2/translate", keyType == CAT ? L"/v1/translate" : L"/v2/translate",
FormatString("text=%S&auth_key=%S&target_lang=%S", Escape(text), apiKey.Copy(), translateTo.Copy()), FormatString("text=%S&auth_key=%S&target_lang=%S", Escape(text), authKey.Copy(), translateTo.Copy()),
L"Content-Type: application/x-www-form-urlencoded" L"Content-Type: application/x-www-form-urlencoded"
}; httpRequest && (!httpRequest.response.empty() || (httpRequest = HttpRequest{ }; httpRequest && (!httpRequest.response.empty() || (httpRequest = HttpRequest{
L"Mozilla/5.0 Textractor", L"Mozilla/5.0 Textractor",
L"api.deepl.com", L"api.deepl.com",
L"POST", L"POST",
(keyType = !keyType) == CAT ? L"/v1/translate" : L"/v2/translate", (keyType = !keyType) == CAT ? L"/v1/translate" : L"/v2/translate",
FormatString("text=%S&auth_key=%S&target_lang=%S", Escape(text), apiKey.Copy(), translateTo.Copy()), FormatString("text=%S&auth_key=%S&target_lang=%S", Escape(text), authKey.Copy(), translateTo.Copy()),
L"Content-Type: application/x-www-form-urlencoded" L"Content-Type: application/x-www-form-urlencoded"
}))) })))
// Response formatted as JSON: translation starts with text":" and ends with "}] // Response formatted as JSON: translation starts with text":" and ends with "}]

View File

@ -54,10 +54,10 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
display->addRow(CHROME_LOCATION, chromePathEdit); display->addRow(CHROME_LOCATION, chromePathEdit);
auto statusLabel = new QLabel("Stopped"); auto statusLabel = new QLabel("Stopped");
auto startButton = new QPushButton(START_DEVTOOLS), stopButton = new QPushButton(STOP_DEVTOOLS); auto startButton = new QPushButton(START_DEVTOOLS), stopButton = new QPushButton(STOP_DEVTOOLS);
auto headlessCheckBox = new QCheckBox(); auto headlessCheck = new QCheckBox();
headlessCheckBox->setChecked(settings.value(HEADLESS_MODE, true).toBool()); headlessCheck->setChecked(settings.value(HEADLESS_MODE, true).toBool());
QObject::connect(headlessCheckBox, &QCheckBox::clicked, [](bool headless) { settings.setValue(HEADLESS_MODE, headless); }); QObject::connect(headlessCheck, &QCheckBox::clicked, [](bool headless) { settings.setValue(HEADLESS_MODE, headless); });
QObject::connect(startButton, &QPushButton::clicked, [statusLabel, chromePathEdit, headlessCheckBox] { QObject::connect(startButton, &QPushButton::clicked, [statusLabel, chromePathEdit, headlessCheck] {
DevTools::Start( DevTools::Start(
S(chromePathEdit->text()), S(chromePathEdit->text()),
[statusLabel](QString status) [statusLabel](QString status)
@ -82,14 +82,14 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
FormatString(LR"({"userAgent":"%s"})", userAgent->replace(userAgent->find(L"Headless"), 8, L""))); FormatString(LR"({"userAgent":"%s"})", userAgent->replace(userAgent->find(L"Headless"), 8, L"")));
}).detach(); }).detach();
}, },
headlessCheckBox->isChecked() headlessCheck->isChecked()
); );
}); });
QObject::connect(stopButton, &QPushButton::clicked, &DevTools::Close); QObject::connect(stopButton, &QPushButton::clicked, &DevTools::Close);
auto buttons = new QHBoxLayout(); auto buttons = new QHBoxLayout();
buttons->addWidget(startButton); buttons->addWidget(startButton);
buttons->addWidget(stopButton); buttons->addWidget(stopButton);
display->addRow(HEADLESS_MODE, headlessCheckBox); display->addRow(HEADLESS_MODE, headlessCheck);
auto autoStartButton = new QCheckBox(); auto autoStartButton = new QCheckBox();
autoStartButton->setChecked(settings.value(AUTO_START, false).toBool()); autoStartButton->setChecked(settings.value(AUTO_START, false).toBool());
QObject::connect(autoStartButton, &QCheckBox::clicked, [](bool autoStart) { settings.setValue(AUTO_START, autoStart); }); QObject::connect(autoStartButton, &QCheckBox::clicked, [](bool autoStart) { settings.setValue(AUTO_START, autoStart); });

View File

@ -55,14 +55,14 @@ struct PrettyWindow : QDialog
settings.beginGroup(name); settings.beginGroup(name);
QFont font = ui.display->font(); QFont font = ui.display->font();
if (font.fromString(settings.value(FONT, font.toString()).toString())) ui.display->setFont(font); if (font.fromString(settings.value(FONT, font.toString()).toString())) ui.display->setFont(font);
setBgColor(settings.value(BG_COLOR, bgColor).value<QColor>()); SetBackgroundColor(settings.value(BG_COLOR, backgroundColor).value<QColor>());
setTextColor(settings.value(TEXT_COLOR, textColor()).value<QColor>()); SetTextColor(settings.value(TEXT_COLOR, TextColor()).value<QColor>());
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();
menu.addAction(FONT, this, &PrettyWindow::RequestFont); menu.addAction(FONT, this, &PrettyWindow::RequestFont);
menu.addAction(BG_COLOR, [this] { setBgColor(colorPrompt(this, bgColor, BG_COLOR)); }); menu.addAction(BG_COLOR, [this] { SetBackgroundColor(colorPrompt(this, backgroundColor, BG_COLOR)); });
menu.addAction(TEXT_COLOR, [this] { setTextColor(colorPrompt(this, textColor(), TEXT_COLOR)); }); menu.addAction(TEXT_COLOR, [this] { 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);
connect(ui.display, &QLabel::customContextMenuRequested, [this](QPoint point) { menu.exec(mapToGlobal(point)); }); connect(ui.display, &QLabel::customContextMenuRequested, [this](QPoint point) { menu.exec(mapToGlobal(point)); });
@ -89,28 +89,28 @@ private:
} }
}; };
void setBgColor(QColor color) void SetBackgroundColor(QColor color)
{ {
if (!color.isValid()) return; if (!color.isValid()) return;
if (color.alpha() == 0) color.setAlpha(1); if (color.alpha() == 0) color.setAlpha(1);
bgColor = color; backgroundColor = color;
repaint(); repaint();
settings.setValue(BG_COLOR, color.name(QColor::HexArgb)); settings.setValue(BG_COLOR, color.name(QColor::HexArgb));
}; };
QColor textColor() QColor TextColor()
{ {
return ui.display->palette().color(QPalette::WindowText); return ui.display->palette().color(QPalette::WindowText);
} }
void setTextColor(QColor color) void SetTextColor(QColor color)
{ {
if (!color.isValid()) return; if (!color.isValid()) return;
ui.display->setPalette(QPalette(color, {}, {}, {}, {}, {}, {})); ui.display->setPalette(QPalette(color, {}, {}, {}, {}, {}, {}));
settings.setValue(TEXT_COLOR, color.name(QColor::HexArgb)); settings.setValue(TEXT_COLOR, color.name(QColor::HexArgb));
}; };
void setOutline(bool enable) void SetOutline(bool enable)
{ {
if (enable) if (enable)
{ {
@ -125,10 +125,10 @@ private:
void paintEvent(QPaintEvent*) override void paintEvent(QPaintEvent*) override
{ {
QPainter(this).fillRect(rect(), bgColor); QPainter(this).fillRect(rect(), backgroundColor);
} }
QColor bgColor{ palette().window().color() }; QColor backgroundColor{ palette().window().color() };
struct : QGraphicsEffect struct : QGraphicsEffect
{ {
void draw(QPainter* painter) override void draw(QPainter* painter) override
@ -163,10 +163,10 @@ public:
maxSentenceSize = settings.value(MAX_SENTENCE_SIZE, maxSentenceSize).toInt(); maxSentenceSize = settings.value(MAX_SENTENCE_SIZE, maxSentenceSize).toInt();
for (auto [name, default, slot] : Array<const char*, bool, void(ExtraWindow::*)(bool)>{ for (auto [name, default, slot] : Array<const char*, bool, void(ExtraWindow::*)(bool)>{
{ TOPMOST, false, &ExtraWindow::setTopmost }, { TOPMOST, false, &ExtraWindow::SetTopmost },
{ SIZE_LOCK, false, &ExtraWindow::setLock }, { SIZE_LOCK, false, &ExtraWindow::SetLock },
{ SHOW_ORIGINAL, true, &ExtraWindow::setShowOriginal }, { SHOW_ORIGINAL, true, &ExtraWindow::SetShowOriginal },
{ DICTIONARY, false, &ExtraWindow::setUseDictionary }, { DICTIONARY, false, &ExtraWindow::SetUseDictionary },
}) })
{ {
// delay processing anything until Textractor has finished initializing // delay processing anything until Textractor has finished initializing
@ -206,26 +206,26 @@ public:
} }
private: private:
void setTopmost(bool topmost) void SetTopmost(bool topmost)
{ {
for (auto window : { winId(), dictionaryWindow.winId() }) for (auto window : { winId(), dictionaryWindow.winId() })
SetWindowPos((HWND)window, topmost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); SetWindowPos((HWND)window, topmost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
settings.setValue(TOPMOST, topmost); settings.setValue(TOPMOST, topmost);
}; };
void setLock(bool locked) void SetLock(bool locked)
{ {
setSizeGripEnabled(!locked); setSizeGripEnabled(!locked);
settings.setValue(SIZE_LOCK, this->locked = locked); settings.setValue(SIZE_LOCK, this->locked = locked);
}; };
void setShowOriginal(bool showOriginal) void SetShowOriginal(bool showOriginal)
{ {
if (!showOriginal && settings.value(SHOW_ORIGINAL, false).toBool()) QMessageBox::information(this, SHOW_ORIGINAL, SHOW_ORIGINAL_INFO); if (!showOriginal && settings.value(SHOW_ORIGINAL, false).toBool()) QMessageBox::information(this, SHOW_ORIGINAL, SHOW_ORIGINAL_INFO);
settings.setValue(SHOW_ORIGINAL, this->showOriginal = showOriginal); settings.setValue(SHOW_ORIGINAL, this->showOriginal = showOriginal);
}; };
void setUseDictionary(bool useDictionary) void SetUseDictionary(bool useDictionary)
{ {
if (useDictionary) if (useDictionary)
{ {
@ -239,11 +239,11 @@ private:
settings.setValue(DICTIONARY, this->useDictionary = useDictionary); settings.setValue(DICTIONARY, this->useDictionary = useDictionary);
} }
void computeDictionaryPosition(QPoint mouse) void ComputeDictionaryPosition(QPoint mouse)
{ {
QString sentence = ui.display->text(); QString sentence = ui.display->text();
const QFont& font = ui.display->font(); const QFont& font = ui.display->font();
if (cachedDisplayInfo.compareExchange(ui.display)) if (cachedDisplayInfo.CompareExchange(ui.display))
{ {
QFontMetrics fontMetrics(font, ui.display); QFontMetrics fontMetrics(font, ui.display);
textPositionMap.clear(); textPositionMap.clear();
@ -269,7 +269,7 @@ private:
if (i == textPositionMap.size() || (mouse - textPositionMap[i]).manhattanLength() > font.pointSize() * 3) return dictionaryWindow.hide(); if (i == textPositionMap.size() || (mouse - textPositionMap[i]).manhattanLength() > font.pointSize() * 3) return dictionaryWindow.hide();
if (sentence.mid(i) == dictionaryWindow.term) return dictionaryWindow.ShowDefinition(); if (sentence.mid(i) == dictionaryWindow.term) return dictionaryWindow.ShowDefinition();
dictionaryWindow.ui.display->setFixedWidth(ui.display->width() * 3 / 4); dictionaryWindow.ui.display->setFixedWidth(ui.display->width() * 3 / 4);
dictionaryWindow.setTerm(sentence.mid(i)); dictionaryWindow.SetTerm(sentence.mid(i));
int left = i == 0 ? 0 : textPositionMap[i - 1].x(), right = textPositionMap[i].x(), int left = i == 0 ? 0 : textPositionMap[i - 1].x(), right = textPositionMap[i].x(),
x = textPositionMap[i].x() > ui.display->width() / 2 ? -dictionaryWindow.width() + (right * 3 + left) / 4 : (left * 3 + right) / 4, y = 0; x = textPositionMap[i].x() > ui.display->width() / 2 ? -dictionaryWindow.width() + (right * 3 + left) / 4 : (left * 3 + right) / 4, y = 0;
for (auto point : textPositionMap) if (point.y() > y && point.y() < textPositionMap[i].y()) y = point.y(); for (auto point : textPositionMap) if (point.y() > y && point.y() < textPositionMap[i].y()) y = point.y();
@ -278,7 +278,7 @@ private:
bool eventFilter(QObject*, QEvent* event) override bool eventFilter(QObject*, QEvent* event) override
{ {
if (useDictionary && event->type() == QEvent::MouseMove) computeDictionaryPosition(((QMouseEvent*)event)->localPos().toPoint()); if (useDictionary && event->type() == QEvent::MouseMove) ComputeDictionaryPosition(((QMouseEvent*)event)->localPos().toPoint());
if (event->type() == QEvent::MouseButtonPress) dictionaryWindow.hide(); if (event->type() == QEvent::MouseButtonPress) dictionaryWindow.hide();
return false; return false;
} }
@ -309,7 +309,7 @@ private:
class class
{ {
public: public:
bool compareExchange(QLabel* display) bool CompareExchange(QLabel* display)
{ {
if (display->text() == text && display->font() == font && display->width() == width) return false; if (display->text() == text && display->font() == font && display->width() == width) return false;
text = display->text(); text = display->text();
@ -389,7 +389,7 @@ private:
} }
} }
void setTerm(QString term) void SetTerm(QString term)
{ {
this->term = term; this->term = term;
UpdateDictionary(); UpdateDictionary();

View File

@ -4,7 +4,7 @@
extern const wchar_t* TRANSLATION_ERROR; extern const wchar_t* TRANSLATION_ERROR;
extern Synchronized<std::wstring> translateTo, apiKey; extern Synchronized<std::wstring> translateTo, authKey;
const char* TRANSLATION_PROVIDER = "Google Translate"; const char* TRANSLATION_PROVIDER = "Google Translate";
const char* GET_API_KEY_FROM = "https://codelabs.developers.google.com/codelabs/cloud-translation-intro"; const char* GET_API_KEY_FROM = "https://codelabs.developers.google.com/codelabs/cloud-translation-intro";
@ -126,12 +126,12 @@ int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 1000;
std::pair<bool, std::wstring> Translate(const std::wstring& text) std::pair<bool, std::wstring> Translate(const std::wstring& text)
{ {
if (!apiKey->empty()) if (!authKey->empty())
if (HttpRequest httpRequest{ if (HttpRequest httpRequest{
L"Mozilla/5.0 Textractor", L"Mozilla/5.0 Textractor",
L"translation.googleapis.com", L"translation.googleapis.com",
L"POST", L"POST",
FormatString(L"/language/translate/v2?format=text&target=%s&key=%s", translateTo.Copy(), apiKey.Copy()).c_str(), FormatString(L"/language/translate/v2?format=text&target=%s&key=%s", translateTo.Copy(), authKey.Copy()).c_str(),
FormatString(R"({"q":["%s"]})", JSON::Escape(WideStringToString(text))) FormatString(R"({"q":["%s"]})", JSON::Escape(WideStringToString(text)))
}) })
if (auto translation = Copy(JSON::Parse(httpRequest.response)[L"data"][L"translations"][0][L"translatedText"].String())) return { true, translation.value() }; if (auto translation = Copy(JSON::Parse(httpRequest.response)[L"data"][L"translations"][0][L"translatedText"].String())) return { true, translation.value() };

View File

@ -12,6 +12,7 @@ extern const char* CURRENT_FILTER;
const char* REGEX_SAVE_FILE = "SavedRegexFilters.txt"; const char* REGEX_SAVE_FILE = "SavedRegexFilters.txt";
std::optional<std::wregex> regex; std::optional<std::wregex> regex;
std::wstring replace;
std::shared_mutex m; std::shared_mutex m;
DWORD (*GetSelectedProcessId)() = nullptr; DWORD (*GetSelectedProcessId)() = nullptr;
@ -24,16 +25,17 @@ public:
Localize(); Localize();
ui.setupUi(this); ui.setupUi(this);
connect(ui.input, &QLineEdit::textEdited, this, &Window::setRegex); connect(ui.regexEdit, &QLineEdit::textEdited, this, &Window::SetRegex);
connect(ui.save, &QPushButton::clicked, this, &Window::saveRegex); connect(ui.replaceEdit, &QLineEdit::textEdited, this, &Window::SetReplace);
connect(ui.saveButton, &QPushButton::clicked, this, &Window::Save);
setWindowTitle(REGEX_FILTER); setWindowTitle(REGEX_FILTER);
QMetaObject::invokeMethod(this, &QWidget::show, Qt::QueuedConnection); QMetaObject::invokeMethod(this, &QWidget::show, Qt::QueuedConnection);
} }
void setRegex(QString regex) void SetRegex(QString regex)
{ {
ui.input->setText(regex); ui.regexEdit->setText(regex);
std::scoped_lock lock(m); std::scoped_lock lock(m);
if (!regex.isEmpty()) try { ::regex = S(regex); } if (!regex.isEmpty()) try { ::regex = S(regex); }
catch (std::regex_error) { return ui.output->setText(INVALID_REGEX); } catch (std::regex_error) { return ui.output->setText(INVALID_REGEX); }
@ -41,13 +43,21 @@ public:
ui.output->setText(QString(CURRENT_FILTER).arg(regex)); ui.output->setText(QString(CURRENT_FILTER).arg(regex));
} }
void SetReplace(QString replace)
{
ui.replaceEdit->setText(replace);
std::scoped_lock lock(m);
::replace = S(replace);
}
private: private:
void saveRegex() void Save()
{ {
auto formatted = FormatString( auto formatted = FormatString(
L"\xfeff|PROCESS|%s|FILTER|%s|END|\r\n", L"\xfeff|PROCESS|%s|FILTER|%s|REPLACE|%s|END|\r\n",
GetModuleFilename(GetSelectedProcessId()).value_or(FormatString(L"Error getting name of process 0x%X", GetSelectedProcessId())), GetModuleFilename(GetSelectedProcessId()).value_or(FormatString(L"Error getting name of process 0x%X", GetSelectedProcessId())),
S(ui.input->text()) S(ui.regexEdit->text()),
S(ui.replaceEdit->text())
); );
std::ofstream(REGEX_SAVE_FILE, std::ios::binary | std::ios::app).write((const char*)formatted.c_str(), formatted.size() * sizeof(wchar_t)); std::ofstream(REGEX_SAVE_FILE, std::ios::binary | std::ios::app).write((const char*)formatted.c_str(), formatted.size() * sizeof(wchar_t));
} }
@ -62,12 +72,16 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
if (sentenceInfo["current select"] && !regex) if (auto processName = GetModuleFilename(sentenceInfo["process id"])) if (sentenceInfo["current select"] && !regex) if (auto processName = GetModuleFilename(sentenceInfo["process id"]))
{ {
std::ifstream stream(REGEX_SAVE_FILE, std::ios::binary); std::ifstream stream(REGEX_SAVE_FILE, std::ios::binary);
BlockMarkupIterator savedFilters(stream, Array<std::wstring_view>{ L"|PROCESS|", L"|FILTER|" }); BlockMarkupIterator savedFilters(stream, Array<std::wstring_view>{ L"|PROCESS|", L"|FILTER|", L"|REPLACE|" });
std::vector<std::wstring> regexes; std::vector<std::array<std::wstring, 3>> regexes;
while (auto read = savedFilters.Next()) if (read->at(0) == processName) regexes.push_back(std::move(read->at(1))); while (auto read = savedFilters.Next()) if (read->at(0) == processName) regexes.push_back(std::move(read.value()));
if (!regexes.empty()) QMetaObject::invokeMethod(&window, [regex = S(regexes.back())] { window.setRegex(regex); }, Qt::BlockingQueuedConnection); if (!regexes.empty()) QMetaObject::invokeMethod(&window, [regex = S(regexes.back()[1]), replace = S(regexes.back()[2])]
{
window.SetRegex(regex);
window.SetReplace(replace);
}, Qt::BlockingQueuedConnection);
} }
std::shared_lock l(m); std::shared_lock l(m);
if (regex) sentence = std::regex_replace(sentence, regex.value(), L""); if (regex) sentence = std::regex_replace(sentence, regex.value(), replace);
return true; return true;
} }

View File

@ -6,13 +6,27 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>350</width> <width>500</width>
<height>105</height> <height>107</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout"> <layout class="QVBoxLayout">
<item> <item>
<widget class="QLineEdit" name="input"/> <layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="regexEdit"/>
</item>
<item>
<widget class="QLabel">
<property name="text">
<string>=></string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="replaceEdit"/>
</item>
</layout>
</item> </item>
<item> <item>
<widget class="QLabel" name="output"> <widget class="QLabel" name="output">
@ -25,7 +39,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="save"> <widget class="QPushButton" name="saveButton">
<property name="text"> <property name="text">
<string>Save</string> <string>Save</string>
</property> </property>

View File

@ -30,7 +30,7 @@ const std::string TRANSLATION_CACHE_FILE = FormatString("%s Translation Cache.tx
QFormLayout* display; QFormLayout* display;
Settings settings; Settings settings;
Synchronized<std::wstring> translateTo = L"en", apiKey; Synchronized<std::wstring> translateTo = L"en", authKey;
namespace namespace
{ {
@ -57,16 +57,16 @@ public:
settings.beginGroup(TRANSLATION_PROVIDER); settings.beginGroup(TRANSLATION_PROVIDER);
auto languageBox = new QComboBox(this); auto languageCombo = new QComboBox(this);
languageBox->addItems(languages); languageCombo->addItems(languages);
int language = -1; int language = -1;
if (settings.contains(LANGUAGE)) language = languageBox->findText(settings.value(LANGUAGE).toString(), Qt::MatchEndsWith); if (settings.contains(LANGUAGE)) language = languageCombo->findText(settings.value(LANGUAGE).toString(), Qt::MatchEndsWith);
if (language < 0) language = languageBox->findText(NATIVE_LANGUAGE, Qt::MatchStartsWith); if (language < 0) language = languageCombo->findText(NATIVE_LANGUAGE, Qt::MatchStartsWith);
if (language < 0) language = languageBox->findText("English", Qt::MatchStartsWith); if (language < 0) language = languageCombo->findText("English", Qt::MatchStartsWith);
languageBox->setCurrentIndex(language); languageCombo->setCurrentIndex(language);
saveLanguage(languageBox->currentText()); saveLanguage(languageCombo->currentText());
display->addRow(TRANSLATE_TO, languageBox); display->addRow(TRANSLATE_TO, languageCombo);
connect(languageBox, &QComboBox::currentTextChanged, this, &Window::saveLanguage); connect(languageCombo, &QComboBox::currentTextChanged, this, &Window::saveLanguage);
for (auto [value, label] : Array<bool&, const char*>{ for (auto [value, label] : Array<bool&, const char*>{
{ translateSelectedOnly, TRANSLATE_SELECTED_THREAD_ONLY }, { translateSelectedOnly, TRANSLATE_SELECTED_THREAD_ONLY },
{ rateLimitAll, RATE_LIMIT_ALL_THREADS }, { rateLimitAll, RATE_LIMIT_ALL_THREADS },
@ -95,12 +95,12 @@ public:
} }
if (GET_API_KEY_FROM) if (GET_API_KEY_FROM)
{ {
auto keyInput = new QLineEdit(settings.value(API_KEY).toString(), this); auto keyEdit = new QLineEdit(settings.value(API_KEY).toString(), this);
apiKey->assign(S(keyInput->text())); authKey->assign(S(keyEdit->text()));
QObject::connect(keyInput, &QLineEdit::textChanged, [](QString key) { settings.setValue(API_KEY, S(apiKey->assign(S(key)))); }); QObject::connect(keyEdit, &QLineEdit::textChanged, [](QString key) { settings.setValue(API_KEY, S(authKey->assign(S(key)))); });
auto keyLabel = new QLabel(QString("<a href=\"%1\">%2</a>").arg(GET_API_KEY_FROM, API_KEY), this); auto keyLabel = new QLabel(QString("<a href=\"%1\">%2</a>").arg(GET_API_KEY_FROM, API_KEY), this);
keyLabel->setOpenExternalLinks(true); keyLabel->setOpenExternalLinks(true);
display->addRow(keyLabel, keyInput); display->addRow(keyLabel, keyEdit);
} }
setWindowTitle(TRANSLATION_PROVIDER); setWindowTitle(TRANSLATION_PROVIDER);