performance improvements and autoselect native language for translations

This commit is contained in:
Akash Mozumdar 2020-02-25 04:39:27 -07:00
parent 2e23d4016d
commit dc48f2a3c8
6 changed files with 89 additions and 62 deletions

View File

@ -68,10 +68,8 @@ namespace
void RemoveThreads(std::function<bool(ThreadParam)> removeIf) void RemoveThreads(std::function<bool(ThreadParam)> removeIf)
{ {
std::vector<TextThread*> threadsToRemove; std::vector<TextThread*> threadsToRemove;
{ for (auto& [tp, thread] : textThreadsByParams.Acquire().contents)
auto textThreadsByParams = ::textThreadsByParams.Acquire(); if (removeIf(tp)) threadsToRemove.push_back(&thread);
std::for_each(textThreadsByParams->begin(), textThreadsByParams->end(), [&](auto& it) { if (removeIf(it.first)) threadsToRemove.push_back(&it.second); });
}
for (auto thread : threadsToRemove) for (auto thread : threadsToRemove)
{ {
OnDestroy(*thread); OnDestroy(*thread);
@ -131,14 +129,14 @@ namespace
{ {
auto tp = *(ThreadParam*)buffer; auto tp = *(ThreadParam*)buffer;
auto textThreadsByParams = ::textThreadsByParams.Acquire(); auto textThreadsByParams = ::textThreadsByParams.Acquire();
auto textThread = textThreadsByParams->find(tp); auto thread = textThreadsByParams->find(tp);
if (textThread == textThreadsByParams->end()) if (thread == textThreadsByParams->end())
{ {
try { textThread = textThreadsByParams->try_emplace(tp, tp, processRecordsByIds->at(tp.processId).GetHook(tp.addr).hp).first; } try { thread = textThreadsByParams->try_emplace(tp, tp, processRecordsByIds->at(tp.processId).GetHook(tp.addr).hp).first; }
catch (std::out_of_range) { continue; } // probably garbage data in pipe, try again catch (std::out_of_range) { continue; } // probably garbage data in pipe, try again
OnCreate(textThread->second); OnCreate(thread->second);
} }
textThread->second.Push(buffer + sizeof(tp), bytesRead - sizeof(tp)); thread->second.Push(buffer + sizeof(tp), bytesRead - sizeof(tp));
} }
break; break;
} }
@ -247,9 +245,9 @@ namespace Host
TextThread* GetThread(int64_t handle) TextThread* GetThread(int64_t handle)
{ {
auto textThreadsByParams = ::textThreadsByParams.Acquire(); for (auto& [tp, thread] : textThreadsByParams.Acquire().contents)
auto thread = std::find_if(textThreadsByParams->begin(), textThreadsByParams->end(), [&](const auto& thread) { return thread.second.handle == handle; }); if (thread.handle == handle) return &thread;
return thread != textThreadsByParams->end() ? &thread->second : nullptr; return nullptr;
} }
void AddConsoleOutput(std::wstring text) void AddConsoleOutput(std::wstring text)

View File

@ -9,7 +9,6 @@ extern Synchronized<std::wstring> translateTo;
const char* TRANSLATION_PROVIDER = "Bing"; const char* TRANSLATION_PROVIDER = "Bing";
QStringList languages QStringList languages
{ {
"English: en",
"Arabic: ar", "Arabic: ar",
"Bosnian: bs-Latn", "Bosnian: bs-Latn",
"Bulgarian: bg", "Bulgarian: bg",
@ -20,6 +19,7 @@ QStringList languages
"Czech: cs", "Czech: cs",
"Danish: da", "Danish: da",
"Dutch: nl", "Dutch: nl",
"English: en",
"Estonian: et", "Estonian: et",
"Finnish: fi", "Finnish: fi",
"French: fr", "French: fr",

View File

@ -3,53 +3,57 @@
#include "common.h" #include "common.h"
#include <istream> #include <istream>
template <typename C, int DelimiterCount, int bufferStartSize = 200> template <typename C, int delimiterCount, int blockSize = 0x1000 / sizeof(C)> // windows file block size
class BlockMarkupIterator class BlockMarkupIterator
{ {
public: public:
BlockMarkupIterator(std::istreambuf_iterator<char> it, const std::basic_string_view<C> (&delimiters)[DelimiterCount]) : BlockMarkupIterator(const std::istream& it, const std::basic_string_view<C>(&delimiters)[delimiterCount]) :
it(it) streambuf(*it.rdbuf())
{ {
std::copy_n(delimiters, DelimiterCount, this->delimiters.begin()); std::copy_n(delimiters, delimiterCount, this->delimiters.begin());
} }
std::optional<std::array<std::basic_string<C>, DelimiterCount>> Next()
std::optional<std::array<std::basic_string<C>, delimiterCount>> Next()
{ {
std::array<std::basic_string<C>, DelimiterCount> results; std::array<std::basic_string<C>, delimiterCount> results;
std::basic_string<C> buffer; Find(delimiters[0], true);
buffer.reserve(bufferStartSize); for (int i = 0; i < delimiterCount; ++i)
Find(buffer, delimiters[0]);
buffer.clear();
for (int i = 0; i < DelimiterCount; ++i)
{ {
const auto delimiter = i + 1 < DelimiterCount ? delimiters[i + 1] : end; const auto delimiter = i + 1 < delimiterCount ? delimiters[i + 1] : end;
if (!Find(buffer, delimiter)) return {}; if (auto found = Find(delimiter, false)) results[i] = std::move(found.value());
buffer.erase(buffer.size() - delimiter.size()); else return {};
results[i] = std::move(buffer);
(buffer = {}).reserve(bufferStartSize);
} }
return results; return results;
} }
private: private:
bool Find(std::basic_string<C>& result, std::basic_string_view<C> delimiter) std::optional<std::basic_string<C>> Find(std::basic_string_view<C> delimiter, bool discard)
{ {
while (Read((result += C{}).back())) if (result.back() == '|' && result.find(delimiter, result.size() - delimiter.size()) != std::string::npos) return true; for (int i = 0; ;)
return false; {
int pos = buffer.find(delimiter, i);
if (pos != std::string::npos)
{
auto result = !discard ? std::optional(std::basic_string(buffer.begin(), buffer.begin() + pos)) : std::nullopt;
buffer.erase(buffer.begin(), buffer.begin() + pos + delimiter.size());
return result;
} }
int oldSize = buffer.size();
bool Read(C& out) buffer.resize(oldSize + blockSize);
if (!streambuf.sgetn((char*)buffer.data() + oldSize, blockSize * sizeof(C))) return {};
i = max(0, oldSize - (int)delimiter.size());
if (discard)
{ {
BYTE buffer[sizeof(C)]; buffer.erase(0, i);
for (int i = 0; i < sizeof(C); ++i, ++it) i = 0;
if (it.equal({})) return false; }
else buffer[i] = *it; }
out = reinterpret_cast<C&>(buffer);
return true;
} }
static constexpr C endImpl[5] = { '|', 'E', 'N', 'D', '|' }; static constexpr C endImpl[5] = { '|', 'E', 'N', 'D', '|' };
static constexpr std::basic_string_view end{ endImpl, 5 }; static constexpr std::basic_string_view end{ endImpl, 5 };
std::istreambuf_iterator<char> it; std::basic_streambuf<char>& streambuf;
std::array<std::basic_string_view<C>, DelimiterCount> delimiters; std::basic_string<C> buffer;
std::array<std::basic_string_view<C>, delimiterCount> delimiters;
}; };

View File

@ -11,7 +11,6 @@ extern Synchronized<std::wstring> translateTo;
const char* TRANSLATION_PROVIDER = "Google"; const char* TRANSLATION_PROVIDER = "Google";
QStringList languages QStringList languages
{ {
"English: en",
"Afrikaans: af", "Afrikaans: af",
"Arabic: ar", "Arabic: ar",
"Albanian: sq", "Albanian: sq",
@ -26,6 +25,7 @@ QStringList languages
"Czech: cs", "Czech: cs",
"Danish: da", "Danish: da",
"Dutch: nl", "Dutch: nl",
"English: en",
"Esperanto: eo", "Esperanto: eo",
"Estonian: et", "Estonian: et",
"Filipino: tl", "Filipino: tl",

View File

@ -1,10 +1,13 @@
#include "qtcommon.h" #include "qtcommon.h"
#include "extension.h" #include "extension.h"
#include "defs.h" #include "defs.h"
#include "util.h"
#include "blockmarkup.h"
#include "network.h" #include "network.h"
#include <map> #include <fstream>
#include <QTimer> #include <QTimer>
extern const char* NATIVE_LANGUAGE;
extern const char* SELECT_LANGUAGE; extern const char* SELECT_LANGUAGE;
extern const char* SELECT_LANGUAGE_MESSAGE; extern const char* SELECT_LANGUAGE_MESSAGE;
extern const char* LANGUAGE_SAVED; extern const char* LANGUAGE_SAVED;
@ -15,19 +18,25 @@ extern QStringList languages;
std::pair<bool, std::wstring> Translate(const std::wstring& text); std::pair<bool, std::wstring> Translate(const std::wstring& text);
const char* LANGUAGE = u8"Language"; const char* LANGUAGE = u8"Language";
const QString CACHE_FILE = QString("%1 Cache.txt").arg(TRANSLATION_PROVIDER); const std::string TRANSLATION_CACHE_FILE = FormatString("%sCache.txt", TRANSLATION_PROVIDER);
Synchronized<std::wstring> translateTo = L"en"; Synchronized<std::wstring> translateTo = L"en";
QSettings settings(CONFIG_FILE, QSettings::IniFormat); QSettings settings(CONFIG_FILE, QSettings::IniFormat);
struct Translation
{
std::wstring original, translation;
bool operator<(const Translation& other) const { return original < other.original; }
};
Synchronized<std::vector<Translation>> translationCache;
int savedSize; int savedSize;
Synchronized<std::map<std::wstring, std::wstring>> translationCache;
void SaveCache() void SaveCache()
{ {
QTextFile file(CACHE_FILE, QIODevice::WriteOnly | QIODevice::Truncate); std::wstring allTranslations(L"\xfeff");
auto translationCache = ::translationCache.Acquire(); for (const auto& [original, translation] : translationCache.Acquire().contents)
for (const auto& [original, translation] : translationCache.contents) allTranslations.append(L"|SENTENCE|").append(original).append(L"|TRANSLATION|").append(translation).append(L"|END|\r\n");
file.write(S(FormatString(L"%s|T|\n%s|T|\n", original, translation)).toUtf8()); std::ofstream(TRANSLATION_CACHE_FILE, std::ios::binary | std::ios::trunc).write((const char*)allTranslations.c_str(), allTranslations.size() * sizeof(wchar_t));
savedSize = translationCache->size(); savedSize = translationCache->size();
} }
@ -46,7 +55,7 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
SELECT_LANGUAGE, SELECT_LANGUAGE,
QString(SELECT_LANGUAGE_MESSAGE).arg(TRANSLATION_PROVIDER), QString(SELECT_LANGUAGE_MESSAGE).arg(TRANSLATION_PROVIDER),
languages, languages,
0, std::find_if(languages.begin(), languages.end(), [](QString language) { return language.startsWith(NATIVE_LANGUAGE); }) - languages.begin(),
false, false,
nullptr, nullptr,
Qt::WindowCloseButtonHint Qt::WindowCloseButtonHint
@ -56,10 +65,16 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
QMessageBox::information(nullptr, SELECT_LANGUAGE, QString(LANGUAGE_SAVED).arg(CONFIG_FILE)); QMessageBox::information(nullptr, SELECT_LANGUAGE, QString(LANGUAGE_SAVED).arg(CONFIG_FILE));
}); });
QStringList savedCache = QString(QTextFile(CACHE_FILE, QIODevice::ReadOnly).readAll()).split("|T|\n", QString::SkipEmptyParts); std::ifstream stream(TRANSLATION_CACHE_FILE, std::ios::binary);
for (int i = 0; i < savedCache.size() - 1; i += 2) BlockMarkupIterator savedTranslations(stream, Array<std::wstring_view>{ L"|SENTENCE|", L"|TRANSLATION|" });
translationCache->insert({ S(savedCache[i]), S(savedCache[i + 1]) }); auto translationCache = ::translationCache.Acquire();
while (auto read = savedTranslations.Next())
{
const auto& [original, translation] = read.value();
translationCache->push_back({ original, translation });
}
savedSize = translationCache->size(); savedSize = translationCache->size();
std::sort(translationCache->begin(), translationCache->end());
} }
break; break;
case DLL_PROCESS_DETACH: case DLL_PROCESS_DETACH:
@ -94,14 +109,15 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
bool cache = false; bool cache = false;
std::wstring translation; std::wstring translation;
if (translationCache->count(sentence) != 0) translation = translationCache->at(sentence); {
auto translationCache = ::translationCache.Acquire();
auto translationLocation = std::lower_bound(translationCache->begin(), translationCache->end(), Translation{ sentence });
if (translationLocation != translationCache->end() && translationLocation->original == sentence) translation = translationLocation->translation;
else if (!(rateLimiter.Request() || sentenceInfo["current select"])) translation = TOO_MANY_TRANS_REQUESTS; else if (!(rateLimiter.Request() || sentenceInfo["current select"])) translation = TOO_MANY_TRANS_REQUESTS;
else std::tie(cache, translation) = Translate(sentence); else std::tie(cache, translation) = Translate(sentence);
if (cache && sentenceInfo["current select"]) if (cache && sentenceInfo["current select"]) translationCache->insert(translationLocation, { sentence, translation });
{
translationCache->insert({ sentence, translation });
if (translationCache->size() > savedSize + 50) SaveCache();
} }
if (cache && translationCache->size() > savedSize + 50) SaveCache();
Unescape(translation); Unescape(translation);
sentence += L"\n" + translation; sentence += L"\n" + translation;

View File

@ -4,6 +4,7 @@
#define ARCH "x86" #define ARCH "x86"
#endif #endif
const char* NATIVE_LANGUAGE = "English";
const char* ATTACH = u8"Attach to game"; const char* ATTACH = u8"Attach to game";
const char* LAUNCH = u8"Launch game"; const char* LAUNCH = u8"Launch game";
const char* DETACH = u8"Detach from game"; const char* DETACH = u8"Detach from game";
@ -185,6 +186,7 @@ const char* HEXADECIMAL = u8"Hexadecimal";
static auto _ = [] static auto _ = []
{ {
#ifdef TURKISH #ifdef TURKISH
NATIVE_LANGUAGE = "Turkish";
ATTACH = u8"Oyuna bağla"; ATTACH = u8"Oyuna bağla";
DETACH = u8"Oyundan kopar"; DETACH = u8"Oyundan kopar";
ADD_HOOK = u8"Kanca ekle"; ADD_HOOK = u8"Kanca ekle";
@ -218,6 +220,7 @@ Kaynak kodu GKLv3 koruması altında proje ana sayfasında mevcut)";
#endif // TURKISH #endif // TURKISH
#ifdef SPANISH #ifdef SPANISH
NATIVE_LANGUAGE = "Spanish";
ATTACH = u8"Adjuntar juego"; ATTACH = u8"Adjuntar juego";
LAUNCH = u8"Iniciar juego"; LAUNCH = u8"Iniciar juego";
DETACH = u8"Desconectar juego"; DETACH = u8"Desconectar juego";
@ -280,6 +283,7 @@ Clic y arrastra los bordes de la ventana para moverla, o en la esquina inferior
#endif // SPANISH #endif // SPANISH
#ifdef SIMPLIFIED_CHINESE #ifdef SIMPLIFIED_CHINESE
NATIVE_LANGUAGE = "Chinese(Simplified)"
ATTACH = u8"附加到游戏"; ATTACH = u8"附加到游戏";
LAUNCH = u8"启动游戏"; LAUNCH = u8"启动游戏";
DETACH = u8"从游戏分离"; DETACH = u8"从游戏分离";
@ -344,6 +348,7 @@ Clic y arrastra los bordes de la ventana para moverla, o en la esquina inferior
#endif // SIMPLIFIED_CHINESE #endif // SIMPLIFIED_CHINESE
#ifdef RUSSIAN #ifdef RUSSIAN
NATIVE_LANGUAGE = "Russian";
ATTACH = u8"Присоединить к игре"; ATTACH = u8"Присоединить к игре";
LAUNCH = u8"Запустить игру"; LAUNCH = u8"Запустить игру";
DETACH = u8"Отсоединить от игры"; DETACH = u8"Отсоединить от игры";
@ -475,6 +480,7 @@ end)";
#endif // RUSSIAN #endif // RUSSIAN
#ifdef INDONESIAN #ifdef INDONESIAN
NATIVE_LANGUAGE = "Indonesian";
ATTACH = u8"Tempelkan kedalam game"; ATTACH = u8"Tempelkan kedalam game";
LAUNCH = u8"Mulai game"; LAUNCH = u8"Mulai game";
DETACH = u8"Lepaskan dari game"; DETACH = u8"Lepaskan dari game";
@ -539,6 +545,7 @@ Klik dan tarik pinggiran jendela untuk memindahkan, atau sudut kanan bawah untuk
#endif // INDONESIAN #endif // INDONESIAN
#ifdef PORTUGUESE #ifdef PORTUGUESE
NATIVE_LANGUAGE = "Portuguese";
ATTACH = u8"Anexar ao Jogo"; ATTACH = u8"Anexar ao Jogo";
LAUNCH = u8"Iniciar Jogo"; LAUNCH = u8"Iniciar Jogo";
DETACH = u8"Desconectar do Jogo"; DETACH = u8"Desconectar do Jogo";
@ -626,6 +633,7 @@ Esse arquívo deve ser codifícado em (UTF-16 little endian).)";
#endif // PORTUGUESE #endif // PORTUGUESE
#ifdef THAI #ifdef THAI
NATIVE_LANGUAGE = "Thai";
ATTACH = u8"เชื่อมเกม"; ATTACH = u8"เชื่อมเกม";
LAUNCH = u8"เริ่มเกม"; LAUNCH = u8"เริ่มเกม";
DETACH = u8"ยกเลิกการเชื่อม"; DETACH = u8"ยกเลิกการเชื่อม";
@ -687,6 +695,7 @@ Source code สามารถหาได้จากส่วนของ GPLv
#endif // THAI #endif // THAI
#ifdef KOREAN #ifdef KOREAN
NATIVE_LANGUAGE = "Korean";
ATTACH = u8"게임에 부착"; ATTACH = u8"게임에 부착";
LAUNCH = u8"게임 실행"; LAUNCH = u8"게임 실행";
DETACH = u8"게임에서 탈착"; DETACH = u8"게임에서 탈착";