forked from Public-Mirror/Textractor
sanitize surrogate characters which cause qt to crash if unpaired
This commit is contained in:
parent
81d5b745aa
commit
0ace753199
@ -60,7 +60,7 @@ namespace
|
||||
std::scoped_lock writeLock(extenMutex);
|
||||
std::vector<Extension> extensions;
|
||||
for (auto extenName : extenNames)
|
||||
extensions.push_back(*std::find_if(::extensions.begin(), ::extensions.end(), [&](auto extension) { return extension.name == S(extenName); }));
|
||||
extensions.push_back(*std::find_if(::extensions.begin(), ::extensions.end(), [&](Extension extension) { return extension.name == S(extenName); }));
|
||||
::extensions = extensions;
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ void TextThread::Push(BYTE* data, int length)
|
||||
|
||||
if (filterRepetition)
|
||||
{
|
||||
if (std::all_of(buffer.begin(), buffer.end(), [&](auto ch) { return repeatingChars.find(ch) != repeatingChars.end(); })) buffer.clear();
|
||||
if (std::all_of(buffer.begin(), buffer.end(), [&](wchar_t ch) { return repeatingChars.find(ch) != repeatingChars.end(); })) buffer.clear();
|
||||
if (RemoveRepetition(buffer)) // sentence repetition detected, which means the entire sentence has already been received
|
||||
{
|
||||
if (hp.type & BLOCK_FLOOD) Host::RemoveHook(tp.processId, tp.addr);
|
||||
|
@ -66,7 +66,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
extenWindow(new ExtenWindow(this))
|
||||
{
|
||||
ui->setupUi(this);
|
||||
for (auto [text, slot] : Array<std::tuple<const char*, void(MainWindow::*)()>>{
|
||||
for (auto [text, slot] : Array<const char*, void(MainWindow::*)()>{
|
||||
{ ATTACH, &MainWindow::AttachProcess },
|
||||
{ LAUNCH, &MainWindow::LaunchProcess },
|
||||
{ DETACH, &MainWindow::DetachProcess },
|
||||
@ -211,8 +211,9 @@ bool MainWindow::SentenceReceived(TextThread& thread, std::wstring& sentence)
|
||||
{
|
||||
if (!DispatchSentenceToExtensions(sentence, GetSentenceInfo(thread).data())) return false;
|
||||
sentence += L'\n';
|
||||
if (current == &thread) QMetaObject::invokeMethod(this, [this, sentence = S(sentence)]
|
||||
if (current == &thread) QMetaObject::invokeMethod(this, [this, sentence = S(sentence)]() mutable
|
||||
{
|
||||
sanitize(sentence);
|
||||
auto scrollbar = ui->textOutput->verticalScrollBar();
|
||||
bool atBottom = scrollbar->value() + 3 > scrollbar->maximum() || (double)scrollbar->value() / scrollbar->maximum() > 0.975; // arbitrary
|
||||
QTextCursor cursor(ui->textOutput->document());
|
||||
@ -354,7 +355,7 @@ void MainWindow::ForgetProcess()
|
||||
for (auto file : { GAME_SAVE_FILE, HOOK_SAVE_FILE })
|
||||
{
|
||||
QStringList lines = QString::fromUtf8(QTextFile(file, QIODevice::ReadOnly).readAll()).split("\n", QString::SkipEmptyParts);
|
||||
lines.erase(std::remove_if(lines.begin(), lines.end(), [&](auto line) { return line.contains(S(processName.value())); }), lines.end());
|
||||
lines.erase(std::remove_if(lines.begin(), lines.end(), [&](const QString& line) { return line.contains(S(processName.value())); }), lines.end());
|
||||
QTextFile(file, QIODevice::WriteOnly | QIODevice::Truncate).write(lines.join("\n").append("\n").toUtf8());
|
||||
}
|
||||
}
|
||||
@ -475,7 +476,7 @@ void MainWindow::FindHooks()
|
||||
QLineEdit patternInput(x64 ? "CC CC 48 89" : "55 8B EC", &dialog);
|
||||
assert(QByteArray::fromHex(patternInput.text().toUtf8()) == QByteArray((const char*)sp.pattern, sp.length));
|
||||
layout.addRow(SEARCH_PATTERN, &patternInput);
|
||||
for (auto [value, label] : Array<std::tuple<int&, const char*>>{
|
||||
for (auto [value, label] : Array<int&, const char*>{
|
||||
{ sp.searchTime, SEARCH_DURATION },
|
||||
{ sp.offset, PATTERN_OFFSET },
|
||||
{ sp.maxRecords, MAX_HOOK_SEARCH_RECORDS },
|
||||
@ -490,7 +491,7 @@ void MainWindow::FindHooks()
|
||||
}
|
||||
QLineEdit boundInput(QFileInfo(S(Util::GetModuleFilename(GetSelectedProcessId()).value_or(L""))).fileName(), &dialog);
|
||||
layout.addRow(SEARCH_MODULE, &boundInput);
|
||||
for (auto [value, label] : Array<std::tuple<uintptr_t&, const char*>>{
|
||||
for (auto [value, label] : Array<uintptr_t&, const char*>{
|
||||
{ sp.minAddress, MIN_ADDRESS },
|
||||
{ sp.maxAddress, MAX_ADDRESS },
|
||||
{ sp.padding, STRING_OFFSET },
|
||||
@ -528,7 +529,8 @@ void MainWindow::FindHooks()
|
||||
auto hooks = std::make_shared<QStringList>();
|
||||
try
|
||||
{
|
||||
Host::FindHooks(processId, sp, [=](HookParam hp, std::wstring text) { if (std::regex_search(text, filter)) *hooks << S(Util::GenerateCode(hp) + L" => " + text); });
|
||||
Host::FindHooks(processId, sp,
|
||||
[=](HookParam hp, std::wstring text) { if (std::regex_search(text, filter)) *hooks << sanitize(S(Util::GenerateCode(hp) + L" => " + text)); });
|
||||
}
|
||||
catch (std::out_of_range) { return; }
|
||||
std::thread([this, hooks]
|
||||
@ -572,7 +574,7 @@ void MainWindow::Settings()
|
||||
QSettings settings(CONFIG_FILE, QSettings::IniFormat, &dialog);
|
||||
QFormLayout layout(&dialog);
|
||||
QPushButton saveButton(SAVE_SETTINGS, &dialog);
|
||||
for (auto [value, label] : Array<std::tuple<bool&, const char*>>{
|
||||
for (auto [value, label] : Array<bool&, const char*>{
|
||||
{ TextThread::filterRepetition, FILTER_REPETITION },
|
||||
{ autoAttach, AUTO_ATTACH },
|
||||
{ autoAttachSavedOnly, ATTACH_SAVED_ONLY },
|
||||
@ -584,7 +586,7 @@ void MainWindow::Settings()
|
||||
layout.addRow(label, checkBox);
|
||||
connect(&saveButton, &QPushButton::clicked, [checkBox, label, &settings, &value] { settings.setValue(label, value = checkBox->isChecked()); });
|
||||
}
|
||||
for (auto [value, label] : Array<std::tuple<int&, const char*>>{
|
||||
for (auto [value, label] : Array<int&, const char*>{
|
||||
{ TextThread::maxBufferSize, MAX_BUFFER_SIZE },
|
||||
{ TextThread::flushDelay, FLUSH_DELAY },
|
||||
{ TextThread::maxHistorySize, MAX_HISTORY_SIZE },
|
||||
@ -612,7 +614,7 @@ void MainWindow::Extensions()
|
||||
void MainWindow::ViewThread(int index)
|
||||
{
|
||||
ui->ttCombo->setCurrentIndex(index);
|
||||
ui->textOutput->setPlainText(S((current = &Host::GetThread(ParseTextThreadString(ui->ttCombo->itemText(index))))->storage->c_str()));
|
||||
ui->textOutput->setPlainText(sanitize(S((current = &Host::GetThread(ParseTextThreadString(ui->ttCombo->itemText(index))))->storage->c_str())));
|
||||
ui->textOutput->moveCursor(QTextCursor::End);
|
||||
}
|
||||
|
||||
|
@ -157,7 +157,7 @@ public:
|
||||
ui.display->setTextFormat(Qt::PlainText);
|
||||
setGeometry(settings.value(WINDOW, geometry()).toRect());
|
||||
|
||||
for (auto [name, default, slot] : Array<std::tuple<const char*, bool, void(ExtraWindow::*)(bool)>>{
|
||||
for (auto [name, default, slot] : Array<const char*, bool, void(ExtraWindow::*)(bool)>{
|
||||
{ TOPMOST, false, &ExtraWindow::setTopmost },
|
||||
{ SIZE_LOCK, false, &ExtraWindow::setLock },
|
||||
{ SHOW_ORIGINAL, true, &ExtraWindow::setShowOriginal },
|
||||
@ -187,6 +187,7 @@ public:
|
||||
void AddSentence(QString sentence)
|
||||
{
|
||||
if (!showOriginal) sentence = sentence.section('\n', sentence.count('\n') / 2 + 1);
|
||||
sanitize(sentence);
|
||||
sentenceHistory.push_back(sentence);
|
||||
historyIndex = sentenceHistory.size() - 1;
|
||||
ui.display->setText(sentence);
|
||||
|
@ -101,7 +101,7 @@ std::wstring GetTranslationUri(const std::wstring& text)
|
||||
|
||||
bool IsHash(const std::wstring& result)
|
||||
{
|
||||
return result.size() == 32 && std::all_of(result.begin(), result.end(), [](auto ch) { return (ch >= L'0' && ch <= L'9') || (ch >= L'a' && ch <= L'z'); });
|
||||
return result.size() == 32 && std::all_of(result.begin(), result.end(), [](char ch) { return (ch >= L'0' && ch <= L'9') || (ch >= L'a' && ch <= L'z'); });
|
||||
}
|
||||
|
||||
std::pair<bool, std::wstring> Translate(const std::wstring& text)
|
||||
|
@ -26,7 +26,12 @@ constexpr bool x64 = true;
|
||||
constexpr bool x64 = false;
|
||||
#endif
|
||||
|
||||
template <typename T, typename... X> using Array = std::conditional_t<sizeof...(X), std::tuple<T, X...>[], T[]>;
|
||||
template <typename T, typename... Xs>
|
||||
struct ArrayImpl { using type = std::tuple<T, Xs...>[]; };
|
||||
template <typename T>
|
||||
struct ArrayImpl<T> { using type = T[]; };
|
||||
template <typename... Ts>
|
||||
using Array = typename ArrayImpl<Ts...>::type;
|
||||
|
||||
template <auto F> using Functor = std::integral_constant<std::decay_t<decltype(F)>, F>;
|
||||
|
||||
|
@ -23,6 +23,9 @@
|
||||
static thread_local bool ok;
|
||||
|
||||
struct QTextFile : QFile { QTextFile(QString name, QIODevice::OpenMode mode) : QFile(name) { open(mode | QIODevice::Text); } };
|
||||
inline std::wstring S(const QString& S) { return { S.toStdWString() }; }
|
||||
inline QString S(const std::string& S) { return QString::fromStdString(S); }
|
||||
inline QString S(const std::wstring& S) { return QString::fromStdWString(S); }
|
||||
inline std::wstring S(const QString& s) { return { s.toStdWString() }; }
|
||||
inline QString S(const std::string& s) { return QString::fromStdString(s); }
|
||||
inline QString S(const std::wstring& s) { return QString::fromStdWString(s); }
|
||||
// TODO: allow paired surrogates
|
||||
inline void sanitize(QString& s) { s.chop(std::distance(std::remove_if(s.begin(), s.end(), [](QChar ch) { return ch.isSurrogate(); }), s.end())); }
|
||||
inline QString sanitize(QString&& s) { QString result; for (auto ch : s) if (!ch.isSurrogate()) result.push_back(ch); return result; }
|
||||
|
Loading…
x
Reference in New Issue
Block a user