From 72c6916c02d66b98ef5760c9883333189cde80ae Mon Sep 17 00:00:00 2001 From: Blu3train Date: Mon, 10 Oct 2022 00:05:13 +0200 Subject: [PATCH] Replacer Translated Text Extension --- deploy.ps1 | 1 + extensions/CMakeLists.txt | 2 + extensions/replacertranslatedtext.cpp | 151 ++++++++++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 extensions/replacertranslatedtext.cpp diff --git a/deploy.ps1 b/deploy.ps1 index ac13101..b5e2146 100644 --- a/deploy.ps1 +++ b/deploy.ps1 @@ -58,6 +58,7 @@ foreach ($language in @{ "Remove Repeated Phrases 2", "Remove 30 Repeated Sentences", "Replacer", + "Replacer Translated Text", "Styler", "Thread Linker" )) diff --git a/extensions/CMakeLists.txt b/extensions/CMakeLists.txt index 3e3d0a6..d4e2706 100644 --- a/extensions/CMakeLists.txt +++ b/extensions/CMakeLists.txt @@ -20,6 +20,7 @@ add_library(Remove\ Repeated\ Phrases MODULE removerepeatphrase.cpp extensionimp add_library(Remove\ Repeated\ Phrases\ 2 MODULE removerepeatphrase2.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\ Translated\ Text MODULE replacertranslatedtext.cpp extensionimpl.cpp) add_library(Styler MODULE styler.cpp extensionimpl.cpp) add_library(Thread\ Linker MODULE threadlinker.cpp extensionimpl.cpp) @@ -40,6 +41,7 @@ target_precompile_headers(Remove\ Repeated\ Phrases 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(Replacer REUSE_FROM pch) +target_precompile_headers(Replacer\ Translated\ Text REUSE_FROM pch) target_precompile_headers(Styler REUSE_FROM pch) target_precompile_headers(Thread\ Linker REUSE_FROM pch) diff --git a/extensions/replacertranslatedtext.cpp b/extensions/replacertranslatedtext.cpp new file mode 100644 index 0000000..c4b7ce0 --- /dev/null +++ b/extensions/replacertranslatedtext.cpp @@ -0,0 +1,151 @@ +#include "extension.h" +#include "blockmarkup.h" +#include +#include +#include +#include + +extern const wchar_t* REPLACER_INSTRUCTIONS; + +constexpr auto REPLACE_SAVE_FILE = u8"SavedReplacementsTranslatedText.txt"; + +std::atomic replaceFileLastWrite = {}; +concurrency::reader_writer_lock m; + +class Trie +{ +public: + Trie(const std::istream& replacementScript) + { + BlockMarkupIterator replacementScriptParser(replacementScript, Array{ 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 + 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) return node->charMap.insert(it, { ch, std::make_unique() })->second.get(); + return nullptr; + } + + struct Node + { + std::vector>> charMap; + ptrdiff_t value = -1; + } root; + + std::vector 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"); + } +);