save attached games and allow launching from textractor using locale emulator

This commit is contained in:
Akash Mozumdar 2019-01-11 17:14:49 -05:00
parent 8e80543e2e
commit 75454b3fa5
11 changed files with 70 additions and 13 deletions

View File

@ -7,7 +7,6 @@
#include <shared_mutex> #include <shared_mutex>
#include <QDragEnterEvent> #include <QDragEnterEvent>
#include <QDropEvent> #include <QDropEvent>
#include <QFileDialog>
#include <QMimeData> #include <QMimeData>
#include <QUrl> #include <QUrl>
#include <QLabel> #include <QLabel>
@ -30,15 +29,13 @@ namespace
{ {
if (extenName == ITH_DLL) return; if (extenName == ITH_DLL) return;
// Extension is dll and exports "OnNewSentence" // Extension is dll and exports "OnNewSentence"
HMODULE module = GetModuleHandleW(S(extenName).c_str()); if (FARPROC callback = GetProcAddress(LoadLibraryOnce(S(extenName)), "OnNewSentence"))
if (!module) module = LoadLibraryW(S(extenName).c_str()); {
if (!module) return;
FARPROC callback = GetProcAddress(module, "OnNewSentence");
if (!callback) return;
std::scoped_lock writeLock(extenMutex); std::scoped_lock writeLock(extenMutex);
extensions[extenName] = (wchar_t*(*)(const wchar_t*, const InfoForExtension*))callback; extensions[extenName] = (wchar_t*(*)(const wchar_t*, const InfoForExtension*))callback;
extenNames.push_back(extenName); extenNames.push_back(extenName);
} }
}
void Unload(QString extenName) void Unload(QString extenName)
{ {

View File

@ -2,7 +2,6 @@
#include "misc.h" #include "misc.h"
#include "host/util.h" #include "host/util.h"
#include <QApplication> #include <QApplication>
#include <QDir>
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {

View File

@ -11,6 +11,7 @@
#include <QPushButton> #include <QPushButton>
#include <QSpinBox> #include <QSpinBox>
#include <QInputDialog> #include <QInputDialog>
#include <QFileDialog>
MainWindow::MainWindow(QWidget *parent) : MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent), QMainWindow(parent),
@ -20,6 +21,7 @@ MainWindow::MainWindow(QWidget *parent) :
ui->setupUi(this); ui->setupUi(this);
for (auto[text, slot] : Array<std::tuple<QString, void(MainWindow::*)()>>{ for (auto[text, slot] : Array<std::tuple<QString, void(MainWindow::*)()>>{
{ ATTACH, &MainWindow::AttachProcess }, { ATTACH, &MainWindow::AttachProcess },
{ LAUNCH, &MainWindow::LaunchProcess },
{ DETACH, &MainWindow::DetachProcess }, { DETACH, &MainWindow::DetachProcess },
{ ADD_HOOK, &MainWindow::AddHook }, { ADD_HOOK, &MainWindow::AddHook },
{ SAVE_HOOKS, &MainWindow::SaveHooks }, { SAVE_HOOKS, &MainWindow::SaveHooks },
@ -90,6 +92,9 @@ void MainWindow::ProcessConnected(DWORD processId)
{ {
QString process = S(Util::GetModuleFilename(processId).value_or(L"???")); QString process = S(Util::GetModuleFilename(processId).value_or(L"???"));
ui->processCombo->addItem(QString::number(processId, 16).toUpper() + ": " + QFileInfo(process).fileName()); ui->processCombo->addItem(QString::number(processId, 16).toUpper() + ": " + QFileInfo(process).fileName());
if (process == "???") return;
QTextFile(GAME_SAVE_FILE, QIODevice::WriteOnly | QIODevice::Append).write((process + "\n").toUtf8());
QStringList allProcesses = QString(QTextFile(HOOK_SAVE_FILE, QIODevice::ReadOnly).readAll()).split("\n", QString::SkipEmptyParts); QStringList allProcesses = QString(QTextFile(HOOK_SAVE_FILE, QIODevice::ReadOnly).readAll()).split("\n", QString::SkipEmptyParts);
// Can't use QFileInfo::absoluteFilePath since hook save file has '\\' as path separator // Can't use QFileInfo::absoluteFilePath since hook save file has '\\' as path separator
@ -198,13 +203,53 @@ void MainWindow::AttachProcess()
QStringList processList(allProcesses.uniqueKeys()); QStringList processList(allProcesses.uniqueKeys());
processList.sort(Qt::CaseInsensitive); processList.sort(Qt::CaseInsensitive);
bool ok;
QString process = QInputDialog::getItem(this, SELECT_PROCESS, ATTACH_INFO, processList, 0, true, &ok, Qt::WindowCloseButtonHint); QString process = QInputDialog::getItem(this, SELECT_PROCESS, ATTACH_INFO, processList, 0, true, &ok, Qt::WindowCloseButtonHint);
if (!ok) return; if (!ok) return;
if (process.toInt(nullptr, 0)) Host::InjectProcess(process.toInt(nullptr, 0)); if (process.toInt(nullptr, 0)) Host::InjectProcess(process.toInt(nullptr, 0));
else for (auto processId : allProcesses.values(process)) Host::InjectProcess(processId); else for (auto processId : allProcesses.values(process)) Host::InjectProcess(processId);
} }
void MainWindow::LaunchProcess()
{
QStringList savedProcesses = QString::fromUtf8(QTextFile(GAME_SAVE_FILE, QIODevice::ReadOnly).readAll()).split("\n", QString::SkipEmptyParts);
savedProcesses.removeDuplicates();
savedProcesses.sort(Qt::CaseInsensitive);
savedProcesses.push_back(SEARCH_GAME);
std::wstring process = S(QInputDialog::getItem(this, SELECT_PROCESS, "", savedProcesses, 0, true, &ok, Qt::WindowCloseButtonHint));
if (!ok) return;
if (S(process) == SEARCH_GAME) process = S(QDir::toNativeSeparators(QFileDialog::getOpenFileName(this, SELECT_PROCESS, "C:\\", PROCESSES)));
if (process.empty()) return;
std::wstring path = std::wstring(process).erase(process.rfind(L'\\'));
PROCESS_INFORMATION info = {};
if (HMODULE localeEmulator = LoadLibraryOnce(L"LoaderDll"))
{
// see https://github.com/xupefei/Locale-Emulator/blob/aa99dec3b25708e676c90acf5fed9beaac319160/LEProc/LoaderWrapper.cs#L252
struct
{
ULONG AnsiCodePage = Host::defaultCodepage;
ULONG OemCodePage = Host::defaultCodepage;
ULONG LocaleID = LANG_JAPANESE;
ULONG DefaultCharset = DEFAULT_CHARSET;
ULONG HookUiLanguageApi = FALSE;
WCHAR DefaultFaceName[LF_FACESIZE] = {};
TIME_ZONE_INFORMATION Timezone;
ULONG64 Unused = 0;
} LEB;
GetTimeZoneInformation(&LEB.Timezone);
((LONG(__stdcall*)(decltype(&LEB), LPCWSTR appName, LPWSTR commandLine, LPCWSTR currentDir, void*, void*, PROCESS_INFORMATION*, void*, void*, void*, void*))
GetProcAddress(localeEmulator, "LeCreateProcess"))(&LEB, process.c_str(), NULL, path.c_str(), NULL, NULL, &info, NULL, NULL, NULL, NULL);
}
if (info.hProcess == NULL)
{
STARTUPINFOW DUMMY = { sizeof(DUMMY) };
CreateProcessW(process.c_str(), NULL, nullptr, nullptr, FALSE, 0, nullptr, path.c_str(), &DUMMY, &info);
}
if (info.hProcess == NULL) return Host::AddConsoleOutput(LAUNCH_FAILED);
Host::InjectProcess(info.dwProcessId);
CloseHandle(info.hProcess);
CloseHandle(info.hThread);
}
void MainWindow::DetachProcess() void MainWindow::DetachProcess()
{ {
Host::DetachProcess(GetSelectedProcessId()); Host::DetachProcess(GetSelectedProcessId());
@ -212,7 +257,6 @@ void MainWindow::DetachProcess()
void MainWindow::AddHook() void MainWindow::AddHook()
{ {
bool ok;
QString hookCode = QInputDialog::getText(this, ADD_HOOK, CODE_INFODUMP, QLineEdit::Normal, "", &ok, Qt::WindowCloseButtonHint); QString hookCode = QInputDialog::getText(this, ADD_HOOK, CODE_INFODUMP, QLineEdit::Normal, "", &ok, Qt::WindowCloseButtonHint);
if (!ok) return; if (!ok) return;
if (auto hp = ParseCode(hookCode)) Host::InsertHook(GetSelectedProcessId(), hp.value()); if (auto hp = ParseCode(hookCode)) Host::InsertHook(GetSelectedProcessId(), hp.value());
@ -233,7 +277,7 @@ void MainWindow::SaveHooks()
if (!(hp.type & HOOK_ENGINE)) hookCodes[tp.addr] = GenerateCode(hp, tp.processId); if (!(hp.type & HOOK_ENGINE)) hookCodes[tp.addr] = GenerateCode(hp, tp.processId);
} }
} }
QTextFile(HOOK_SAVE_FILE, QIODevice::Append).write((S(processName.value()) + " , " + QStringList(hookCodes.values()).join(" , ") + "\n").toUtf8()); QTextFile(HOOK_SAVE_FILE, QIODevice::WriteOnly | QIODevice::Append).write((S(processName.value()) + " , " + QStringList(hookCodes.values()).join(" , ") + "\n").toUtf8());
} }
} }

View File

@ -15,6 +15,8 @@ public:
~MainWindow(); ~MainWindow();
private: private:
inline static thread_local bool ok = false;
void closeEvent(QCloseEvent*) override; void closeEvent(QCloseEvent*) override;
void ProcessConnected(DWORD processId); void ProcessConnected(DWORD processId);
void ProcessDisconnected(DWORD processId); void ProcessDisconnected(DWORD processId);
@ -26,6 +28,7 @@ private:
DWORD GetSelectedProcessId(); DWORD GetSelectedProcessId();
std::unordered_map<const char*, int64_t> GetMiscInfo(TextThread* thread); std::unordered_map<const char*, int64_t> GetMiscInfo(TextThread* thread);
void AttachProcess(); void AttachProcess();
void LaunchProcess();
void DetachProcess(); void DetachProcess();
void AddHook(); void AddHook();
void SaveHooks(); void SaveHooks();

View File

@ -265,6 +265,13 @@ QString GenerateCode(HookParam hp, DWORD processId)
else return GenerateHCode(hp, processId); else return GenerateHCode(hp, processId);
} }
HMODULE LoadLibraryOnce(std::wstring fileName)
{
HMODULE module = GetModuleHandleW(fileName.c_str());
if (!module) module = LoadLibraryW(fileName.c_str());
return module;
}
TEST( TEST(
assert(ParseCode("/HQN936#-c*C:C*1C@4AA:gdi.dll:GetTextOutA")), assert(ParseCode("/HQN936#-c*C:C*1C@4AA:gdi.dll:GetTextOutA")),
assert(ParseCode("HB4@0")), assert(ParseCode("HB4@0")),

View File

@ -13,3 +13,4 @@ inline std::wstring S(const QString& S) { return { S.toStdWString() }; }
inline QString S(const std::wstring& S) { return QString::fromStdWString(S); } inline QString S(const std::wstring& S) { return QString::fromStdWString(S); }
std::optional<HookParam> ParseCode(QString HCode); std::optional<HookParam> ParseCode(QString HCode);
QString GenerateCode(HookParam hp, DWORD processId); QString GenerateCode(HookParam hp, DWORD processId);
HMODULE LoadLibraryOnce(std::wstring fileName);

View File

@ -8,5 +8,6 @@
#include <QDialog> #include <QDialog>
#include <QFile> #include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QDir>
#include <QRegularExpression> #include <QRegularExpression>
#include <QSettings> #include <QSettings>

View File

@ -23,6 +23,7 @@ constexpr auto ITH_HOOKMAN_MUTEX_ = L"VNR_HOOKMAN_"; // ITH_HOOKMAN_%d
constexpr auto ITH_DLL = L"vnrhook"; // .dll but LoadLibrary automatically adds that constexpr auto ITH_DLL = L"vnrhook"; // .dll but LoadLibrary automatically adds that
constexpr auto CONFIG_FILE = u8"Textractor.ini"; constexpr auto CONFIG_FILE = u8"Textractor.ini";
constexpr auto HOOK_SAVE_FILE = u8"SavedHooks.txt"; constexpr auto HOOK_SAVE_FILE = u8"SavedHooks.txt";
constexpr auto GAME_SAVE_FILE = u8"Games.txt";
constexpr auto EXTEN_SAVE_FILE = u8"Extensions.txt"; constexpr auto EXTEN_SAVE_FILE = u8"Extensions.txt";
// Functions // Functions

View File

@ -5,6 +5,7 @@
#ifdef ENGLISH #ifdef ENGLISH
constexpr auto ATTACH = u8"Attach to game"; constexpr auto ATTACH = u8"Attach to game";
constexpr auto LAUNCH = u8"Launch game";
constexpr auto DETACH = u8"Detach from game"; constexpr auto DETACH = u8"Detach from game";
constexpr auto ADD_HOOK = u8"Add hook"; constexpr auto ADD_HOOK = u8"Add hook";
constexpr auto SAVE_HOOKS = u8"Save hook(s)"; constexpr auto SAVE_HOOKS = u8"Save hook(s)";
@ -13,6 +14,8 @@ constexpr auto EXTENSIONS = u8"Extensions";
constexpr auto SELECT_PROCESS = u8"Select Process"; constexpr auto SELECT_PROCESS = u8"Select Process";
constexpr auto ATTACH_INFO = u8R"(If you don't see the process you want to attach, try running with admin rights constexpr auto ATTACH_INFO = u8R"(If you don't see the process you want to attach, try running with admin rights
You can also type in the process id)"; You can also type in the process id)";
constexpr auto SEARCH_GAME = u8"Select from computer";
constexpr auto PROCESSES = u8"Processes (*.exe)";
constexpr auto CODE_INFODUMP = u8R"(Search for text constexpr auto CODE_INFODUMP = u8R"(Search for text
S[codepage#]text S[codepage#]text
OR OR
@ -49,6 +52,7 @@ constexpr auto UPDATE_AVAILABLE = L"Update available: download it from https://g
constexpr auto ALREADY_INJECTED = L"Textractor: already injected"; constexpr auto ALREADY_INJECTED = L"Textractor: already injected";
constexpr auto ARCHITECTURE_MISMATCH = L"Textractor: architecture mismatch: try 32 bit Textractor instead"; constexpr auto ARCHITECTURE_MISMATCH = L"Textractor: architecture mismatch: try 32 bit Textractor instead";
constexpr auto INJECT_FAILED = L"Textractor: couldn't inject"; constexpr auto INJECT_FAILED = L"Textractor: couldn't inject";
constexpr auto LAUNCH_FAILED = L"Textractor: couldn't launch";
constexpr auto INVALID_CODE = L"Textractor: invalid code"; constexpr auto INVALID_CODE = L"Textractor: invalid code";
constexpr auto INVALID_CODEPAGE = L"Textractor: couldn't convert text (invalid codepage?)"; constexpr auto INVALID_CODEPAGE = L"Textractor: couldn't convert text (invalid codepage?)";
constexpr auto PIPE_CONNECTED = u8"Textractor: pipe connected"; constexpr auto PIPE_CONNECTED = u8"Textractor: pipe connected";

BIN
release/LoaderDll.dll Normal file

Binary file not shown.

BIN
release/LocaleEmulator.dll Normal file

Binary file not shown.