save attached games and allow launching from textractor using locale emulator
This commit is contained in:
parent
8e80543e2e
commit
75454b3fa5
@ -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)
|
||||
{
|
||||
|
@ -2,7 +2,6 @@
|
||||
#include "misc.h"
|
||||
#include "host/util.h"
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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")),
|
||||
|
@ -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);
|
||||
|
@ -8,5 +8,6 @@
|
||||
#include <QDialog>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QRegularExpression>
|
||||
#include <QSettings>
|
||||
|
@ -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
|
||||
|
@ -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
BIN
release/LoaderDll.dll
Normal file
Binary file not shown.
BIN
release/LocaleEmulator.dll
Normal file
BIN
release/LocaleEmulator.dll
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user