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

View File

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

View File

@ -11,6 +11,7 @@
#include <QPushButton>
#include <QSpinBox>
#include <QInputDialog>
#include <QFileDialog>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
@ -20,6 +21,7 @@ MainWindow::MainWindow(QWidget *parent) :
ui->setupUi(this);
for (auto[text, slot] : Array<std::tuple<QString, void(MainWindow::*)()>>{
{ ATTACH, &MainWindow::AttachProcess },
{ LAUNCH, &MainWindow::LaunchProcess },
{ DETACH, &MainWindow::DetachProcess },
{ ADD_HOOK, &MainWindow::AddHook },
{ SAVE_HOOKS, &MainWindow::SaveHooks },
@ -90,6 +92,9 @@ void MainWindow::ProcessConnected(DWORD processId)
{
QString process = S(Util::GetModuleFilename(processId).value_or(L"???"));
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);
// Can't use QFileInfo::absoluteFilePath since hook save file has '\\' as path separator
@ -198,13 +203,53 @@ void MainWindow::AttachProcess()
QStringList processList(allProcesses.uniqueKeys());
processList.sort(Qt::CaseInsensitive);
bool ok;
QString process = QInputDialog::getItem(this, SELECT_PROCESS, ATTACH_INFO, processList, 0, true, &ok, Qt::WindowCloseButtonHint);
if (!ok) return;
if (process.toInt(nullptr, 0)) Host::InjectProcess(process.toInt(nullptr, 0));
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()
{
Host::DetachProcess(GetSelectedProcessId());
@ -212,7 +257,6 @@ void MainWindow::DetachProcess()
void MainWindow::AddHook()
{
bool ok;
QString hookCode = QInputDialog::getText(this, ADD_HOOK, CODE_INFODUMP, QLineEdit::Normal, "", &ok, Qt::WindowCloseButtonHint);
if (!ok) return;
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);
}
}
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();
private:
inline static thread_local bool ok = false;
void closeEvent(QCloseEvent*) override;
void ProcessConnected(DWORD processId);
void ProcessDisconnected(DWORD processId);
@ -26,6 +28,7 @@ private:
DWORD GetSelectedProcessId();
std::unordered_map<const char*, int64_t> GetMiscInfo(TextThread* thread);
void AttachProcess();
void LaunchProcess();
void DetachProcess();
void AddHook();
void SaveHooks();

View File

@ -265,6 +265,13 @@ QString GenerateCode(HookParam hp, DWORD 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(
assert(ParseCode("/HQN936#-c*C:C*1C@4AA:gdi.dll:GetTextOutA")),
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); }
std::optional<HookParam> ParseCode(QString HCode);
QString GenerateCode(HookParam hp, DWORD processId);
HMODULE LoadLibraryOnce(std::wstring fileName);

View File

@ -8,5 +8,6 @@
#include <QDialog>
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QRegularExpression>
#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 CONFIG_FILE = u8"Textractor.ini";
constexpr auto HOOK_SAVE_FILE = u8"SavedHooks.txt";
constexpr auto GAME_SAVE_FILE = u8"Games.txt";
constexpr auto EXTEN_SAVE_FILE = u8"Extensions.txt";
// Functions

View File

@ -5,6 +5,7 @@
#ifdef ENGLISH
constexpr auto ATTACH = u8"Attach to game";
constexpr auto LAUNCH = u8"Launch game";
constexpr auto DETACH = u8"Detach from game";
constexpr auto ADD_HOOK = u8"Add hook";
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 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)";
constexpr auto SEARCH_GAME = u8"Select from computer";
constexpr auto PROCESSES = u8"Processes (*.exe)";
constexpr auto CODE_INFODUMP = u8R"(Search for text
S[codepage#]text
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 ARCHITECTURE_MISMATCH = L"Textractor: architecture mismatch: try 32 bit Textractor instead";
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_CODEPAGE = L"Textractor: couldn't convert text (invalid codepage?)";
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.