diff --git a/GUI/CMakeLists.txt b/GUI/CMakeLists.txt index 95d0397..25048a7 100644 --- a/GUI/CMakeLists.txt +++ b/GUI/CMakeLists.txt @@ -9,7 +9,7 @@ set(gui_SRCS main.cpp mainwindow.cpp misc.cpp - extensions.cpp + extenwindow.cpp tests.cpp host/host.cc host/textthread.cc diff --git a/GUI/extensions.cpp b/GUI/extensions.cpp deleted file mode 100644 index 082f3d0..0000000 --- a/GUI/extensions.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "extensions.h" -#include "types.h" - -static std::optional LoadExtension(QString extenName) -{ - // Extension is dll and exports "OnNewSentence" - HMODULE module = GetModuleHandleW(extenName.toStdWString().c_str()); - if (!module) module = LoadLibraryW(extenName.toStdWString().c_str()); - if (!module) return {}; - FARPROC callback = GetProcAddress(module, "OnNewSentence"); - if (!callback) return {}; - return Extension{ extenName, (wchar_t*(*)(const wchar_t*, const InfoForExtension*))callback }; -} - -void Extension::Load(QString extenName) -{ - LOCK(extenMutex); - if (auto extension = LoadExtension(extenName)) extensions.push_back(extension.value()); -} - -void Extension::SendToBack(QString extenName) -{ - LOCK(extenMutex); - Extension* extenIter = std::find_if(extensions.begin(), extensions.end(), [&](Extension extension) { return extension.name == extenName; }); - Extension extension = *extenIter; - extensions.erase(extenIter); - extensions.push_back(extension); -} - -void Extension::Unload(QString extenName) -{ - LOCK(extenMutex); - extensions.erase(std::find_if(extensions.begin(), extensions.end(), [&](Extension extension) { return extension.name == extenName; })); - FreeLibrary(GetModuleHandleW(extenName.toStdWString().c_str())); -} - -QVector Extension::GetNames() -{ - std::shared_lock sharedLock(extenMutex); - QVector ret; - for (auto extension : extensions) ret.push_back(extension.name); - return ret; -} - -bool Extension::DispatchSentence(std::wstring& sentence, std::unordered_map miscInfo) -{ - bool success = true; - wchar_t* sentenceBuffer = (wchar_t*)HeapAlloc(GetProcessHeap(), 0, (sentence.size() + 1) * sizeof(wchar_t)); - wcscpy_s(sentenceBuffer, sentence.size() + 1, sentence.c_str()); - - InfoForExtension miscInfoLinkedList{ "", 0, nullptr }; - InfoForExtension* miscInfoTraverser = &miscInfoLinkedList; - for (auto& i : miscInfo) miscInfoTraverser = miscInfoTraverser->next = new InfoForExtension{ i.first.c_str(), i.second, nullptr }; - - std::shared_lock sharedLock(extenMutex); - for (auto extension : extensions) - { - wchar_t* nextBuffer = extension.callback(sentenceBuffer, &miscInfoLinkedList); - if (nextBuffer == nullptr) { success = false; break; } - if (nextBuffer != sentenceBuffer) HeapFree(GetProcessHeap(), 0, sentenceBuffer); - sentenceBuffer = nextBuffer; - } - sentence = std::wstring(sentenceBuffer); - - HeapFree(GetProcessHeap(), 0, sentenceBuffer); - return success; -} diff --git a/GUI/extensions.h b/GUI/extensions.h deleted file mode 100644 index fd414f9..0000000 --- a/GUI/extensions.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef EXTENSIONS_H -#define EXTENSIONS_H - -#include "qtcommon.h" -#include - -struct InfoForExtension -{ - const char* name; - int64_t value; - InfoForExtension* next; - ~InfoForExtension() { if (next) delete next; }; -}; - -class Extension -{ -public: - static bool DispatchSentence(std::wstring& sentence, std::unordered_map miscInfo); - static void Load(QString extenName); - static void SendToBack(QString extenName); - static void Unload(QString extenName); - static QVector GetNames(); - - QString name; - wchar_t* (*callback)(const wchar_t*, const InfoForExtension*); - -private: - inline static std::shared_mutex extenMutex; - inline static QVector extensions; -}; - -#endif // EXTENSIONS_H diff --git a/GUI/extenwindow.cpp b/GUI/extenwindow.cpp new file mode 100644 index 0000000..9ab3b1b --- /dev/null +++ b/GUI/extenwindow.cpp @@ -0,0 +1,152 @@ +#include "extenwindow.h" +#include "ui_extenwindow.h" +#include "defs.h" +#include "types.h" +#include +#include +#include + +namespace +{ + struct InfoForExtension + { + const char* name; + int64_t value; + InfoForExtension* next; + ~InfoForExtension() { if (next) delete next; }; + }; + + QHash extensions; + QStringList extenNames; + std::shared_mutex extenMutex; + + void Load(QString extenName) + { + if (extenName == ITH_DLL) return; + // Extension is dll and exports "OnNewSentence" + HMODULE module = GetModuleHandleW(extenName.toStdWString().c_str()); + if (!module) module = LoadLibraryW(extenName.toStdWString().c_str()); + if (!module) return; + FARPROC callback = GetProcAddress(module, "OnNewSentence"); + if (!callback) return; + LOCK(extenMutex); + extensions[extenName] = (wchar_t*(*)(const wchar_t*, const InfoForExtension*))callback; + extenNames.push_back(extenName); + } + + void Unload(QString extenName) + { + LOCK(extenMutex); + extenNames.erase(std::remove(extenNames.begin(), extenNames.end(), extenName), extenNames.end()); + FreeLibrary(GetModuleHandleW(extenName.toStdWString().c_str())); + } + + void Reorder(QStringList extenNames) + { + LOCK(extenMutex); + ::extenNames = extenNames; + } +} + +bool DispatchSentenceToExtensions(std::wstring& sentence, std::unordered_map miscInfo) +{ + bool success = true; + wchar_t* sentenceBuffer = (wchar_t*)HeapAlloc(GetProcessHeap(), 0, (sentence.size() + 1) * sizeof(wchar_t)); + wcscpy_s(sentenceBuffer, sentence.size() + 1, sentence.c_str()); + + InfoForExtension miscInfoLinkedList{ "", 0, nullptr }; + InfoForExtension* miscInfoTraverser = &miscInfoLinkedList; + for (auto& i : miscInfo) miscInfoTraverser = miscInfoTraverser->next = new InfoForExtension{ i.first.c_str(), i.second, nullptr }; + + std::shared_lock sharedLock(extenMutex); + for (auto extenName : extenNames) + { + wchar_t* nextBuffer = extensions[extenName](sentenceBuffer, &miscInfoLinkedList); + if (nextBuffer == nullptr) { success = false; break; } + if (nextBuffer != sentenceBuffer) HeapFree(GetProcessHeap(), 0, sentenceBuffer); + sentenceBuffer = nextBuffer; + } + sentence = std::wstring(sentenceBuffer); + + HeapFree(GetProcessHeap(), 0, sentenceBuffer); + return success; +} + +ExtenWindow::ExtenWindow(QWidget* parent) : + QMainWindow(parent), + ui(new Ui::ExtenWindow) +{ + ui->setupUi(this); + + extenList = findChild("extenList"); + extenList->installEventFilter(this); + + if (extensions.empty()) + { + extenSaveFile.open(QIODevice::ReadOnly); + for (auto extenName : QString(extenSaveFile.readAll()).split(">")) Load(extenName); + extenSaveFile.close(); + } + Sync(); +} + +ExtenWindow::~ExtenWindow() +{ + delete ui; +} + +void ExtenWindow::Sync() +{ + extenList->clear(); + extenSaveFile.open(QIODevice::WriteOnly | QIODevice::Truncate); + std::shared_lock sharedLock(extenMutex); + for (auto extenName : extenNames) + { + extenList->addItem(extenName); + extenSaveFile.write((extenName + ">").toUtf8()); + } + extenSaveFile.close(); +} + +void ExtenWindow::Add(QString fileName) +{ + if (!fileName.endsWith(".dll")) return; + QString extenName = fileName.mid(fileName.lastIndexOf("/") + 1); + QFile::copy(fileName, extenName); + Load(extenName.split(".dll")[0]); + Sync(); +} + +bool ExtenWindow::eventFilter(QObject* target, QEvent* event) +{ + // See https://stackoverflow.com/questions/1224432/how-do-i-respond-to-an-internal-drag-and-drop-operation-using-a-qlistwidget/1528215 + if (event->type() == QEvent::ChildRemoved) + { + QStringList extenNames; + for (int i = 0; i < extenList->count(); ++i) extenNames.push_back(extenList->item(i)->text()); + Reorder(extenNames); + Sync(); + } + return false; +} + +void ExtenWindow::dragEnterEvent(QDragEnterEvent* event) +{ + event->acceptProposedAction(); +} + +void ExtenWindow::dropEvent(QDropEvent* event) +{ + for (auto file : event->mimeData()->urls()) Add(file.toLocalFile()); +} + +void ExtenWindow::on_addButton_clicked() +{ + Add(QFileDialog::getOpenFileName(this, "Select Extension", "C:\\", "Extensions (*.dll)")); +} + +void ExtenWindow::on_rmvButton_clicked() +{ + if (auto extenName = extenList->currentItem()) Unload(extenName->text()); + Sync(); +} diff --git a/GUI/extenwindow.h b/GUI/extenwindow.h new file mode 100644 index 0000000..9923674 --- /dev/null +++ b/GUI/extenwindow.h @@ -0,0 +1,41 @@ +#ifndef EXTENSIONS_H +#define EXTENSIONS_H + +#include "qtcommon.h" +#include +#include +#include +#include + +namespace Ui +{ + class ExtenWindow; +} + +bool DispatchSentenceToExtensions(std::wstring& sentence, std::unordered_map miscInfo); + +class ExtenWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit ExtenWindow(QWidget* parent = nullptr); + ~ExtenWindow(); + +private slots: + void on_addButton_clicked(); + void on_rmvButton_clicked(); + +private: + void Add(QString fileName); + void Sync(); + bool eventFilter(QObject* target, QEvent* event); + void dragEnterEvent(QDragEnterEvent* event); + void dropEvent(QDropEvent* event); + + Ui::ExtenWindow* ui; + QFile extenSaveFile = QFile("Extensions.txt"); + QListWidget* extenList; +}; + +#endif // EXTENSIONS_H diff --git a/GUI/extenwindow.ui b/GUI/extenwindow.ui new file mode 100644 index 0000000..b6fbe41 --- /dev/null +++ b/GUI/extenwindow.ui @@ -0,0 +1,87 @@ + + + ExtenWindow + + + + 0 + 0 + 400 + 300 + + + + true + + + Extensions + + + QObject +{ + font: 10pt "MS Shell Dlg 2"; +} +#textOutput +{ + font: 13pt "MS Shell Dlg 2";; +} +QPushButton, QComboBox +{ + padding-top: 3px; + padding-bottom: 3px; + padding-right: 5px; + padding-left: 5px; + text-align: left; +} + + + + + + + QAbstractItemView::InternalMove + + + Qt::MoveAction + + + + + + + + + Qt::Vertical + + + + + + + Add + + + + + + + Remove + + + + + + + Qt::Vertical + + + + + + + + + + + + diff --git a/GUI/mainwindow.cpp b/GUI/mainwindow.cpp index e094c76..c523829 100644 --- a/GUI/mainwindow.cpp +++ b/GUI/mainwindow.cpp @@ -1,36 +1,21 @@ #include "mainwindow.h" #include "ui_mainwindow.h" #include "defs.h" -#include "extensions.h" +#include "extenwindow.h" #include "misc.h" -#include #include -#include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), - ui(new Ui::MainWindow) + ui(new Ui::MainWindow), + extenWindow(new ExtenWindow) { ui->setupUi(this); processCombo = findChild("processCombo"); ttCombo = findChild("ttCombo"); - extenCombo = findChild("extenCombo"); textOutput = findChild("textOutput"); - QFile extenSaveFile("Extensions.txt"); - if (extenSaveFile.exists()) - { - extenSaveFile.open(QIODevice::ReadOnly); - for (auto extenName : QString(extenSaveFile.readAll()).split(">")) Extension::Load(extenName); - } - else - { - for (auto file : QDir().entryList()) - if (file.endsWith(".dll") && file != ITH_DLL) Extension::Load(file.left(file.lastIndexOf(".dll"))); - } - ReloadExtensions(); - if (settings.contains("Window")) this->setGeometry(settings.value("Window").toRect()); // TODO: add GUI for changing these if (settings.contains("Flush_Delay")) TextThread::flushDelay = settings.value("Flush_Delay").toInt(); @@ -65,6 +50,11 @@ MainWindow::~MainWindow() Host::Close(); } +void MainWindow::closeEvent(QCloseEvent*) +{ + QCoreApplication::quit(); // Need to do this to kill any windows that might've been made by extensions +} + void MainWindow::AddProcess(unsigned processId) { processCombo->addItem(QString::number(processId, 16).toUpper() + ": " + GetModuleName(processId)); @@ -120,7 +110,7 @@ void MainWindow::ThreadOutput(QString threadString, QString output) bool MainWindow::ProcessThreadOutput(TextThread* thread, std::wstring& output) { - if (Extension::DispatchSentence(output, GetInfoForExtensions(thread))) + if (DispatchSentenceToExtensions(output, GetMiscInfo(thread))) { output += L"\r\n"; emit SigThreadOutput(TextThreadString(thread), QString::fromStdWString(output)); @@ -152,19 +142,7 @@ DWORD MainWindow::GetSelectedProcessId() return processCombo->currentText().split(":")[0].toULong(nullptr, 16); } -void MainWindow::ReloadExtensions() -{ - extenCombo->clear(); - QFile extenSaveFile("Extensions.txt"); - extenSaveFile.open(QIODevice::WriteOnly | QIODevice::Truncate); - for (auto extenName : Extension::GetNames()) - { - extenSaveFile.write((extenName + ">").toUtf8()); - extenCombo->addItem(extenName); - } -} - -std::unordered_map MainWindow::GetInfoForExtensions(TextThread* thread) +std::unordered_map MainWindow::GetMiscInfo(TextThread* thread) { return { @@ -251,33 +229,14 @@ void MainWindow::on_saveButton_clicked() file.write((hookList + "\r\n").toUtf8()); } +void MainWindow::on_extenButton_clicked() +{ + extenWindow->activateWindow(); + extenWindow->showNormal(); +} + void MainWindow::on_ttCombo_activated(int index) { textOutput->setPlainText(QString::fromStdWString(Host::GetThread(ParseTextThreadString(ttCombo->itemText(index)))->GetStorage())); textOutput->moveCursor(QTextCursor::End); } - -void MainWindow::on_addExtenButton_clicked() -{ - QString extenFileName = QFileDialog::getOpenFileName(this, "Select Extension", "C:\\", "Extensions (*.dll)"); - if (!extenFileName.size()) return; - QString extenName = extenFileName.mid(extenFileName.lastIndexOf("/") + 1); - QFile::copy(extenFileName, extenName); - Extension::Load(extenName.left(extenName.lastIndexOf(".dll"))); - ReloadExtensions(); -} - -void MainWindow::on_moveExtenButton_clicked() -{ - if (extenCombo->currentText() == "") return; - Extension::SendToBack(extenCombo->currentText()); - ReloadExtensions(); - Host::AddConsoleOutput(L"extension sent to back"); -} - -void MainWindow::on_rmvExtenButton_clicked() -{ - if (extenCombo->currentText() == "") return; - Extension::Unload(extenCombo->currentText()); - ReloadExtensions(); -} diff --git a/GUI/mainwindow.h b/GUI/mainwindow.h index 91a3840..a9bd8c4 100644 --- a/GUI/mainwindow.h +++ b/GUI/mainwindow.h @@ -3,7 +3,6 @@ #include "qtcommon.h" #include "host/host.h" -#include #include #include #include @@ -38,29 +37,27 @@ private slots: void ThreadOutput(QString threadString, QString output); // this function doesn't take TextThread* because it might be destroyed on pipe thread void on_attachButton_clicked(); void on_detachButton_clicked(); - void on_ttCombo_activated(int index); void on_unhookButton_clicked(); void on_hookButton_clicked(); void on_saveButton_clicked(); - void on_addExtenButton_clicked(); - void on_moveExtenButton_clicked(); - void on_rmvExtenButton_clicked(); + void on_extenButton_clicked(); + void on_ttCombo_activated(int index); private: bool ProcessThreadOutput(TextThread* thread, std::wstring& output); QString TextThreadString(TextThread* thread); ThreadParam ParseTextThreadString(QString textThreadString); DWORD GetSelectedProcessId(); - void ReloadExtensions(); - std::unordered_map GetInfoForExtensions(TextThread* thread); + std::unordered_map GetMiscInfo(TextThread* thread); QVector GetAllHooks(DWORD processId); + void closeEvent(QCloseEvent*); Ui::MainWindow* ui; QSettings settings = QSettings("Textractor.ini", QSettings::IniFormat); QComboBox* processCombo; QComboBox* ttCombo; - QComboBox* extenCombo; QPlainTextEdit* textOutput; + QWidget* extenWindow; }; #endif // MAINWINDOW_H diff --git a/GUI/mainwindow.ui b/GUI/mainwindow.ui index b3d9271..692749d 100644 --- a/GUI/mainwindow.ui +++ b/GUI/mainwindow.ui @@ -6,16 +6,10 @@ 0 0 - 949 + 900 600 - - - 2 - 0 - - Textractor @@ -38,271 +32,115 @@ QPushButton, QComboBox } - - - 0 - - - 0 - - - 0 - - - 0 - + - - - - 0 - 0 - + + + true - - QFrame::StyledPanel - - - QFrame::Raised - - + - 4 + 0 - 4 + 0 - 4 + 0 - 4 + 0 - - - true - - - - 2 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - false - - - QComboBox::InsertAtBottom - - - QComboBox::AdjustToContents - - - - - - - Attach to game - - - - - - - Detach from game - - - - - - - Add hook - - - - - - - Remove hook - - - - - - - Save hook(s) - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - false - - - QComboBox::AdjustToContents - - - - - - - Add extension - - - - - - - Move extension - - - - - - - Remove extension - - - - - - - - - - - - - - 6 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 4 - - - 4 - - - 4 - - - 4 - - - + false - - 50 + + QComboBox::InsertAtBottom + + + QComboBox::AdjustToContents - - - - 5 - 0 - - - - true + + + Attach to game + + + + Detach from game + + + + + + + Add hook + + + + + + + Remove hook + + + + + + + Save hook(s) + + + + + + + Extensions + + + + + + + Qt::Vertical + + + + + + + + + false + + + 50 + + + + + + + true + + + + + - - - - 0 - 0 - 949 - 22 - - - - + diff --git a/GUI/misc.h b/GUI/misc.h index 7cab830..dd93884 100644 --- a/GUI/misc.h +++ b/GUI/misc.h @@ -3,7 +3,6 @@ #include "qtcommon.h" #include "types.h" -#include QString GetFullModuleName(DWORD processId, HMODULE module = NULL); QString GetModuleName(DWORD processId, HMODULE module = NULL); diff --git a/GUI/qtcommon.h b/GUI/qtcommon.h index 07fc0db..cdc2efe 100644 --- a/GUI/qtcommon.h +++ b/GUI/qtcommon.h @@ -3,4 +3,7 @@ #include "common.h" #include #include +#include +#include +#include #include diff --git a/GUI/tests.cpp b/GUI/tests.cpp index a831b8a..0ae4dc0 100644 --- a/GUI/tests.cpp +++ b/GUI/tests.cpp @@ -1,4 +1,3 @@ -#include "extensions.h" #include "misc.h" static int TESTS = [] diff --git a/include/defs.h b/include/defs.h index da11226..de94067 100644 --- a/include/defs.h +++ b/include/defs.h @@ -3,7 +3,7 @@ // vnrhook/defs.h // 8/23/2013 jichi -#define ITH_DLL L"vnrhook.dll" +#define ITH_DLL L"vnrhook" // Pipes