From 6a30e0a4024d72305669c34dfbc4655f37717240 Mon Sep 17 00:00:00 2001 From: IChooseYou Date: Sat, 14 Mar 2026 12:11:08 -0600 Subject: [PATCH] fix: replace remaining QList::append({}) in plugins and tests Missed plugin and test directories in the previous Qt 6.8 compat fix. --- plugins/KernelMemory/KernelMemoryPlugin.cpp | 4 +- plugins/ProcessMemory/ProcessMemoryPlugin.cpp | 9 +- .../RemoteProcessMemoryPlugin.cpp | 2 +- tests/bench_project.cpp | 4 +- tests/grab_tabs.cpp | 222 ++++++++++ tests/test_addressparser.cpp | 24 ++ tests/test_disasm.cpp | 2 +- tests/test_kernel_provider.cpp | 397 ++++++++++++++++++ tests/test_mcp.cpp | 2 +- tests/test_project_dock.cpp | 143 +++++++ tests/test_roundtrip_winsdk.cpp | 307 ++++++++++++++ tests/test_scanner.cpp | 70 +-- 12 files changed, 1140 insertions(+), 46 deletions(-) create mode 100644 tests/grab_tabs.cpp create mode 100644 tests/test_kernel_provider.cpp create mode 100644 tests/test_project_dock.cpp create mode 100644 tests/test_roundtrip_winsdk.cpp diff --git a/plugins/KernelMemory/KernelMemoryPlugin.cpp b/plugins/KernelMemory/KernelMemoryPlugin.cpp index c6737dd..7434998 100644 --- a/plugins/KernelMemory/KernelMemoryPlugin.cpp +++ b/plugins/KernelMemory/KernelMemoryPlugin.cpp @@ -222,7 +222,7 @@ QVector KernelProcessProvider::tebs() const auto* entries = reinterpret_cast(outBuf.constData()); for (int i = 0; i < count; ++i) - result.append({entries[i].tebAddress, entries[i].threadId}); + result.push_back(ThreadInfo{entries[i].tebAddress, entries[i].threadId}); #endif return result; } @@ -253,7 +253,7 @@ void KernelProcessProvider::cacheModules() if (i == 0) m_base = entries[i].base; - m_modules.append({modName, entries[i].base, entries[i].size}); + m_modules.push_back(ModuleInfo{modName, entries[i].base, entries[i].size}); } #endif } diff --git a/plugins/ProcessMemory/ProcessMemoryPlugin.cpp b/plugins/ProcessMemory/ProcessMemoryPlugin.cpp index f551f2a..722168a 100644 --- a/plugins/ProcessMemory/ProcessMemoryPlugin.cpp +++ b/plugins/ProcessMemory/ProcessMemoryPlugin.cpp @@ -190,7 +190,7 @@ void ProcessMemoryProvider::cacheModules() if (GetModuleFileNameExW(m_handle, mods[i], modPath, MAX_PATH)) fullPath = QString::fromWCharArray(modPath); - m_modules.append({ + m_modules.push_back(ModuleInfo{ QString::fromWCharArray(modName), fullPath, (uint64_t)mi.lpBaseOfDll, @@ -205,7 +205,7 @@ QVector ProcessMemoryProvider::enumerateModules() co QVector result; result.reserve(m_modules.size()); for (const auto& m : m_modules) - result.append({m.name, m.fullPath, m.base, m.size}); + result.push_back(ModuleEntry{m.name, m.fullPath, m.base, m.size}); return result; } @@ -415,8 +415,9 @@ void ProcessMemoryProvider::cacheModules() for (auto it = moduleRanges.begin(); it != moduleRanges.end(); ++it) { QFileInfo fi(it.key()); - m_modules.append({ + m_modules.push_back(ModuleInfo{ fi.fileName(), + it.key(), it->base, it->end - it->base }); @@ -545,7 +546,7 @@ QVector ProcessMemoryProvider::tebs() const ULONG tbiLen = 0; NTSTATUS qitSt = pNtQIT(hThread, 0, &tbi, sizeof(tbi), &tbiLen); if (qitSt >= 0 && tbi.TebBaseAddress) - result.append({(uint64_t)(uintptr_t)tbi.TebBaseAddress, tid}); + result.push_back(ThreadInfo{(uint64_t)(uintptr_t)tbi.TebBaseAddress, tid}); CloseHandle(hThread); } break; diff --git a/plugins/RemoteProcessMemory/RemoteProcessMemoryPlugin.cpp b/plugins/RemoteProcessMemory/RemoteProcessMemoryPlugin.cpp index ed69ead..2c53bcb 100644 --- a/plugins/RemoteProcessMemory/RemoteProcessMemoryPlugin.cpp +++ b/plugins/RemoteProcessMemory/RemoteProcessMemoryPlugin.cpp @@ -244,7 +244,7 @@ struct IpcClient { reinterpret_cast(data + entry->nameOffset), (int)entry->nameLength); #endif - result.append({modName, entry->base, entry->size}); + result.push_back(RemoteProcessProvider::ModuleInfo{modName, entry->base, entry->size}); } return result; } diff --git a/tests/bench_project.cpp b/tests/bench_project.cpp index 01b452d..9cffffd 100644 --- a/tests/bench_project.cpp +++ b/tests/bench_project.cpp @@ -202,7 +202,7 @@ void BenchProject::benchBuildWorkspaceModel() // Build TabInfo array QVector tabs; for (const auto& t : trees) - tabs.append({ &t, QStringLiteral("test"), nullptr }); + tabs.push_back(TabInfo{ &t, QStringLiteral("test"), nullptr }); QStandardItemModel model; const int ITERS = 20; @@ -244,7 +244,7 @@ void BenchProject::benchWorkspaceSearch() QVector tabs; for (const auto& t : trees) - tabs.append({ &t, QStringLiteral("test"), nullptr }); + tabs.push_back(TabInfo{ &t, QStringLiteral("test"), nullptr }); QStandardItemModel model; buildProjectExplorer(&model, tabs); diff --git a/tests/grab_tabs.cpp b/tests/grab_tabs.cpp new file mode 100644 index 0000000..71d416e --- /dev/null +++ b/tests/grab_tabs.cpp @@ -0,0 +1,222 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../src/themes/thememanager.h" + +// Minimal replica of the real app's MenuBarStyle for dock tab painting +class TestTabStyle : public QProxyStyle { +public: + using QProxyStyle::QProxyStyle; + + QSize sizeFromContents(ContentsType type, const QStyleOption* opt, + const QSize& sz, const QWidget* w) const override { + QSize s = QProxyStyle::sizeFromContents(type, opt, sz, w); + if (type == CT_TabBarTab) { + if (auto* tabBar = qobject_cast(w)) { + if (tabBar->parent() && qobject_cast(tabBar->parent())) + s.setHeight(28); + } + } + return s; + } + + void drawControl(ControlElement element, const QStyleOption* opt, + QPainter* p, const QWidget* w) const override { + // Tab shape — background, accent line, borders + if (element == CE_TabBarTabShape) { + if (auto* tab = qstyleoption_cast(opt)) { + auto* tabBar = qobject_cast(w); + if (tabBar && tabBar->parent() && qobject_cast(tabBar->parent())) { + bool selected = tab->state & State_Selected; + bool hovered = tab->state & State_MouseOver; + QColor bg = tab->palette.color(QPalette::Window); + if (hovered && !selected) + bg = tab->palette.color(QPalette::Mid); + p->fillRect(tab->rect, bg); + if (selected) + p->fillRect(QRect(tab->rect.left(), tab->rect.top(), + tab->rect.width(), 2), + tab->palette.color(QPalette::Link)); + p->setPen(tab->palette.color(QPalette::Dark)); + p->drawLine(tab->rect.bottomLeft(), tab->rect.bottomRight()); + return; + } + } + } + // Tab label — middle-elide long names, editor font + if (element == CE_TabBarTabLabel) { + if (auto* tab = qstyleoption_cast(opt)) { + auto* tabBar = qobject_cast(w); + if (tabBar && tabBar->parent() && qobject_cast(tabBar->parent())) { + int tabIdx = -1; + for (int i = 0; i < tabBar->count(); ++i) { + if (tabBar->tabRect(i).contains(tab->rect.center())) { tabIdx = i; break; } + } + int btnWidth = 0; + if (tabIdx >= 0) { + auto* btn = tabBar->tabButton(tabIdx, QTabBar::RightSide); + if (btn) btnWidth = btn->sizeHint().width() + 4; + } + QRect textRect = tab->rect.adjusted(8, 0, -(8 + btnWidth), 0); + QFont f("JetBrains Mono", 10); + f.setFixedPitch(true); + p->setFont(f); + QFontMetrics fm(f); + QString text = (tabIdx >= 0) ? tabBar->tabText(tabIdx) : tab->text; + int maxW = textRect.width(); + if (fm.horizontalAdvance(text) > maxW) { + int ellW = fm.horizontalAdvance(QStringLiteral("\u2026")); + int avail = maxW - ellW; + if (avail > 0) { + int half = avail / 2; + QString left, right; + for (int i = 0; i < text.size(); ++i) + if (fm.horizontalAdvance(text.left(i+1)) > half) { left = text.left(i); break; } + if (left.isEmpty()) left = text.left(1); + for (int i = text.size()-1; i >= 0; --i) + if (fm.horizontalAdvance(text.mid(i)) > half) { right = text.mid(i+1); break; } + if (right.isEmpty()) right = text.right(1); + text = left + QStringLiteral("\u2026") + right; + } else { + text = QStringLiteral("\u2026"); + } + } + bool selected = tab->state & QStyle::State_Selected; + p->setPen(selected ? tab->palette.color(QPalette::Text) + : tab->palette.color(QPalette::WindowText)); + p->drawText(textRect, Qt::AlignVCenter | Qt::AlignLeft, text); + return; + } + } + } + QProxyStyle::drawControl(element, opt, p, w); + } +}; + +class TabBtns : public QWidget { +public: + explicit TabBtns(const QColor& hover, QWidget* parent = nullptr) : QWidget(parent) { + auto* hl = new QHBoxLayout(this); + hl->setContentsMargins(2, 0, 0, 0); + hl->setSpacing(0); + QString style = QStringLiteral( + "QToolButton { border: none; padding: 1px; border-radius: 0px; }" + "QToolButton:hover { background: %1; }").arg(hover.name()); + auto* pin = new QToolButton(this); + pin->setFixedSize(16, 16); + pin->setAutoRaise(true); + pin->setIcon(QIcon(":/vsicons/pin.svg")); + pin->setIconSize(QSize(12, 12)); + pin->setStyleSheet(style); + hl->addWidget(pin); + auto* close = new QToolButton(this); + close->setFixedSize(16, 16); + close->setAutoRaise(true); + close->setIcon(QIcon(":/vsicons/close.svg")); + close->setIconSize(QSize(12, 12)); + close->setStyleSheet(style); + hl->addWidget(close); + } +}; + +class GrabTabs : public QObject { + Q_OBJECT +private slots: + void grab() { + const auto& t = rcx::ThemeManager::instance().current(); + + // Install custom style (no stylesheet — all painting via style) + QApplication::setStyle(new TestTabStyle("Fusion")); + + // Apply dark palette globally + QPalette pal; + pal.setColor(QPalette::Window, t.background); + pal.setColor(QPalette::WindowText, t.textDim); + pal.setColor(QPalette::Base, t.background); + pal.setColor(QPalette::Text, t.text); + pal.setColor(QPalette::Mid, t.hover); + pal.setColor(QPalette::Dark, t.border); + pal.setColor(QPalette::Link, t.indHoverSpan); + QApplication::setPalette(pal); + + auto* win = new QMainWindow; + win->resize(700, 500); + win->setDockNestingEnabled(true); + win->setTabPosition(Qt::TopDockWidgetArea, QTabWidget::North); + + auto* central = new QWidget(win); + central->setMaximumSize(0, 0); + central->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); + win->setCentralWidget(central); + win->setStyleSheet(QStringLiteral( + "QMainWindow::separator { width: 0px; height: 0px; background: transparent; }")); + + QStringList names = { + "shader_color_helper.hpp", + "shader_crypt.cpp", + "EPROCESS (class)", + "very_long_struct_name_that_should_elide.h" + }; + + QVector docks; + for (const auto& name : names) { + auto* dock = new QDockWidget(name, win); + dock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable); + auto* emptyTitle = new QWidget(dock); + emptyTitle->setFixedHeight(0); + dock->setTitleBarWidget(emptyTitle); + dock->setWidget(new QTextEdit(dock)); + if (!docks.isEmpty()) + win->tabifyDockWidget(docks.last(), dock); + else + win->addDockWidget(Qt::TopDockWidgetArea, dock); + docks.append(dock); + } + // Select first tab + docks.first()->raise(); + + win->show(); + QVERIFY(QTest::qWaitForWindowExposed(win)); + QApplication::processEvents(); + + // No stylesheet on dock tab bars — painting handled by TestTabStyle + for (auto* tabBar : win->findChildren()) { + if (tabBar->parent() != win) continue; + tabBar->setStyleSheet(QString()); + tabBar->setElideMode(Qt::ElideNone); + tabBar->setExpanding(false); + + QPalette tp = tabBar->palette(); + tp.setColor(QPalette::WindowText, t.textDim); + tp.setColor(QPalette::Text, t.text); + tp.setColor(QPalette::Window, t.background); + tp.setColor(QPalette::Mid, t.hover); + tp.setColor(QPalette::Dark, t.border); + tp.setColor(QPalette::Link, t.indHoverSpan); + tabBar->setPalette(tp); + + for (int i = 0; i < tabBar->count(); ++i) + tabBar->setTabButton(i, QTabBar::RightSide, new TabBtns(t.hover, tabBar)); + } + QApplication::processEvents(); + QApplication::processEvents(); + + QPixmap shot = win->grab(QRect(0, 0, win->width(), 50)); + shot.save(QStringLiteral("tab_screenshot.png")); + qDebug() << "Saved" << shot.size(); + delete win; + } +}; + +QTEST_MAIN(GrabTabs) +#include "grab_tabs.moc" diff --git a/tests/test_addressparser.cpp b/tests/test_addressparser.cpp index f159e26..034a19b 100644 --- a/tests/test_addressparser.cpp +++ b/tests/test_addressparser.cpp @@ -382,6 +382,30 @@ private slots: QCOMPARE(r.value, 0x140000000ULL); } + // -- Bare module.dll identifier -- + + void bareModuleDll() { + AddressParserCallbacks cbs; + cbs.resolveIdentifier = [](const QString& name, bool* ok) -> uint64_t { + *ok = (name == "client.dll"); + return *ok ? 0x7FF600000000ULL : 0; + }; + auto r = AddressParser::evaluate("client.dll + 0xFF", 8, &cbs); + QVERIFY(r.ok); + QCOMPARE(r.value, 0x7FF6000000FFULL); + } + + void bareModuleExe() { + AddressParserCallbacks cbs; + cbs.resolveIdentifier = [](const QString& name, bool* ok) -> uint64_t { + *ok = (name == "cs2.exe"); + return *ok ? 0x140000000ULL : 0; + }; + auto r = AddressParser::evaluate("cs2.exe + 0xDE", 8, &cbs); + QVERIFY(r.ok); + QCOMPARE(r.value, 0x1400000DEULL); + } + // -- Validate with new syntax -- void validateIdentifier() { diff --git a/tests/test_disasm.cpp b/tests/test_disasm.cpp index f2ec1ba..ac4a86c 100644 --- a/tests/test_disasm.cpp +++ b/tests/test_disasm.cpp @@ -230,7 +230,7 @@ private slots: // Only include the pointer-expanded ones (near vtable at 0x100) if (lm.offsetAddr >= 0x100 && lm.offsetAddr < 0x200) { int nodeIdx = lm.nodeIdx; - funcPtrs.append({i, lm.offsetAddr, lm.nodeKind, + funcPtrs.push_back(FuncInfo{i, lm.offsetAddr, lm.nodeKind, nodeIdx >= 0 ? tree.nodes[nodeIdx].name : QString()}); } } diff --git a/tests/test_kernel_provider.cpp b/tests/test_kernel_provider.cpp new file mode 100644 index 0000000..ae72bf1 --- /dev/null +++ b/tests/test_kernel_provider.cpp @@ -0,0 +1,397 @@ +#include +#include +#include +#include + +#include "providers/provider.h" +#include "scanner.h" +#include "../plugins/KernelMemory/KernelMemoryPlugin.h" + +#ifdef _WIN32 +#include +#include +#endif + +using namespace rcx; + +class TestKernelProvider : public QObject { + Q_OBJECT + +private: + bool m_driverAvailable = false; + KernelMemoryPlugin* m_plugin = nullptr; + std::unique_ptr m_provider; + uint32_t m_selfPid = 0; + +private slots: + + // ── Setup: try to load driver, skip tests if unavailable ── + + void initTestCase() + { + m_plugin = new KernelMemoryPlugin(); + +#ifdef _WIN32 + m_selfPid = GetCurrentProcessId(); + + // Try to open driver directly to see if it's available + HANDLE h = CreateFileA(RCX_DRV_USERMODE_PATH, + GENERIC_READ | GENERIC_WRITE, + 0, nullptr, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, nullptr); + if (h != INVALID_HANDLE_VALUE) { + CloseHandle(h); + m_driverAvailable = true; + } else { + // Try loading via plugin + QString errorMsg; + QString target = QStringLiteral("km:%1:self").arg(m_selfPid); + m_provider = m_plugin->createProvider(target, &errorMsg); + if (m_provider && m_provider->isValid()) { + m_driverAvailable = true; + } else { + qWarning("Kernel driver not available: %s", qPrintable(errorMsg)); + qWarning("Tests requiring the driver will be skipped."); + } + } + + if (m_driverAvailable && !m_provider) { + QString target = QStringLiteral("km:%1:self").arg(m_selfPid); + m_provider = m_plugin->createProvider(target, nullptr); + } +#endif + } + + void cleanupTestCase() + { + m_provider.reset(); + delete m_plugin; + m_plugin = nullptr; + } + + // ── 1. Plugin metadata (no driver needed) ── + + void plugin_name() + { + QCOMPARE(QString::fromStdString(m_plugin->Name()), QStringLiteral("Kernel Memory")); + } + + void plugin_loadType() + { + QCOMPARE(m_plugin->LoadType(), IPlugin::k_ELoadTypeManual); + } + + void plugin_canHandle() + { + QVERIFY(m_plugin->canHandle(QStringLiteral("km:1234:test.exe"))); + QVERIFY(m_plugin->canHandle(QStringLiteral("phys:0"))); + QVERIFY(m_plugin->canHandle(QStringLiteral("msr:"))); + QVERIFY(!m_plugin->canHandle(QStringLiteral("1234:test.exe"))); + QVERIFY(!m_plugin->canHandle(QStringLiteral("file:test.bin"))); + } + + void provider_noDriver_invalid() + { + // Creating provider with invalid target should fail gracefully + QString err; + auto prov = m_plugin->createProvider(QStringLiteral("km:0:invalid"), &err); + // Either nullptr or invalid -- both are acceptable + if (prov) QVERIFY(!prov->isValid() || prov->size() == 0); + } + + // ── 2. KUSER_SHARED_DATA validation (at 0x7FFE0000) ── + + void kusd_ntMajorVersion() + { + if (!m_driverAvailable) QSKIP("Driver not loaded"); + QVERIFY(m_provider); + + // KUSER_SHARED_DATA.NtMajorVersion at offset 0x26C + uint32_t major = m_provider->readU32(0x7FFE0000 + 0x26C); + QCOMPARE(major, (uint32_t)10); // Windows 10/11 + } + + void kusd_ntMinorVersion() + { + if (!m_driverAvailable) QSKIP("Driver not loaded"); + + uint32_t minor = m_provider->readU32(0x7FFE0000 + 0x270); + QCOMPARE(minor, (uint32_t)0); // Windows 10+ has minor = 0 + } + + void kusd_ntBuildNumber() + { + if (!m_driverAvailable) QSKIP("Driver not loaded"); + +#ifdef _WIN32 + // Cross-validate with RtlGetVersion + typedef NTSTATUS(NTAPI* RtlGetVersion_t)(PRTL_OSVERSIONINFOW); + auto pRtlGetVersion = (RtlGetVersion_t)GetProcAddress( + GetModuleHandleA("ntdll.dll"), "RtlGetVersion"); + QVERIFY(pRtlGetVersion); + + RTL_OSVERSIONINFOW osvi{}; + osvi.dwOSVersionInfoSize = sizeof(osvi); + QCOMPARE(pRtlGetVersion(&osvi), (NTSTATUS)0); + + uint32_t buildFromDriver = m_provider->readU32(0x7FFE0000 + 0x260); + QCOMPARE(buildFromDriver, (uint32_t)osvi.dwBuildNumber); +#endif + } + + void kusd_systemTime_nonZero() + { + if (!m_driverAvailable) QSKIP("Driver not loaded"); + + uint64_t sysTime = m_provider->readU64(0x7FFE0000 + 0x14); + QVERIFY(sysTime != 0); + } + + void kusd_tickCount_increasing() + { + if (!m_driverAvailable) QSKIP("Driver not loaded"); + + // TickCountMultiplier at 0x4, TickCount at 0x320 + uint64_t tick1 = m_provider->readU64(0x7FFE0000 + 0x320); + QTest::qWait(120); + uint64_t tick2 = m_provider->readU64(0x7FFE0000 + 0x320); + QVERIFY2(tick2 > tick1, + qPrintable(QStringLiteral("tick1=%1 tick2=%2").arg(tick1).arg(tick2))); + } + + void kusd_crossValidate_readProcessMemory() + { + if (!m_driverAvailable) QSKIP("Driver not loaded"); + +#ifdef _WIN32 + // Read same KUSD page through driver and ReadProcessMemory + QByteArray driverBuf(256, 0); + m_provider->read(0x7FFE0000, driverBuf.data(), 256); + + QByteArray rpmBuf(256, 0); + SIZE_T bytesRead = 0; + HANDLE self = GetCurrentProcess(); + ReadProcessMemory(self, (LPCVOID)0x7FFE0000, rpmBuf.data(), 256, &bytesRead); + + // NtMajorVersion (offset 0x26C relative = not in first 256 bytes, so compare what we have) + // Compare first 256 bytes -- should be identical + QCOMPARE(driverBuf, rpmBuf); +#endif + } + + // ── 3. Self-read integration ── + + void selfRead_mzHeader() + { + if (!m_driverAvailable) QSKIP("Driver not loaded"); + +#ifdef _WIN32 + uint64_t selfBase = (uint64_t)GetModuleHandleA(nullptr); + QVERIFY(selfBase != 0); + + uint8_t mz[2] = {}; + m_provider->read(selfBase, mz, 2); + QCOMPARE(mz[0], (uint8_t)'M'); + QCOMPARE(mz[1], (uint8_t)'Z'); +#endif + } + + void selfRead_peSignature() + { + if (!m_driverAvailable) QSKIP("Driver not loaded"); + +#ifdef _WIN32 + uint64_t selfBase = (uint64_t)GetModuleHandleA(nullptr); + + // PE offset at +0x3C + uint32_t peOffset = m_provider->readU32(selfBase + 0x3C); + QVERIFY(peOffset > 0 && peOffset < 0x1000); + + // PE signature = "PE\0\0" = 0x00004550 + uint32_t peSig = m_provider->readU32(selfBase + peOffset); + QCOMPARE(peSig, (uint32_t)0x00004550); +#endif + } + + // ── 4. Scanner integration ── + + void scanner_mzSigScan() + { + if (!m_driverAvailable) QSKIP("Driver not loaded"); + +#ifdef _WIN32 + auto shared = std::shared_ptr(m_provider.get(), [](Provider*){}); + + ScanRequest req; + req.pattern = QByteArray("\x4D\x5A", 2); + req.mask = QByteArray("\xFF\xFF", 2); + req.alignment = 1; + req.maxResults = 10; + + // Constrain to our own module for speed + uint64_t selfBase = (uint64_t)GetModuleHandleA(nullptr); + req.startAddress = selfBase; + req.endAddress = selfBase + 0x1000; + + ScanEngine engine; + QSignalSpy spy(&engine, &ScanEngine::finished); + engine.start(shared, req); + QVERIFY(spy.wait(5000)); + + auto results = spy.at(0).at(0).value>(); + QVERIFY(results.size() >= 1); + QCOMPARE(results[0].address, selfBase); +#endif + } + + // ── 5. Region enumeration ── + + void regions_selfProcess() + { + if (!m_driverAvailable) QSKIP("Driver not loaded"); + + auto regions = m_provider->enumerateRegions(); + QVERIFY(regions.size() > 0); + + // Should have at least one executable region (our code) + bool hasExec = false; + for (const auto& r : regions) { + if (r.executable) { hasExec = true; break; } + } + QVERIFY(hasExec); + } + + // ── 6. PEB / modules ── + + void peb_nonZero() + { + if (!m_driverAvailable) QSKIP("Driver not loaded"); + + QVERIFY(m_provider->peb() != 0); + } + + void symbol_selfModule() + { + if (!m_driverAvailable) QSKIP("Driver not loaded"); + +#ifdef _WIN32 + uint64_t selfBase = (uint64_t)GetModuleHandleA(nullptr); + QString sym = m_provider->getSymbol(selfBase + 0x100); + QVERIFY(!sym.isEmpty()); + QVERIFY(sym.contains(QStringLiteral("+0x"))); +#endif + } + + // ── 7. CR3 / address translation ── + + void cr3_nonZero() + { + if (!m_driverAvailable) QSKIP("Driver not loaded"); + + auto* kprov = dynamic_cast(m_provider.get()); + QVERIFY(kprov); + + uint64_t cr3 = kprov->getCr3(); + QVERIFY2(cr3 != 0, "CR3 should be non-zero for a running process"); + // CR3 should be page-aligned (low 12 bits cleared) + QCOMPARE(cr3 & 0xFFF, (uint64_t)0); + } + + void vtop_kusd() + { + if (!m_driverAvailable) QSKIP("Driver not loaded"); + + auto* kprov = dynamic_cast(m_provider.get()); + QVERIFY(kprov); + + // KUSER_SHARED_DATA is at VA 0x7FFE0000 in every process + auto result = kprov->translateAddress(0x7FFE0000); + QVERIFY2(result.valid, "KUSER_SHARED_DATA should be mapped"); + QVERIFY(result.physical != 0); + // PML4E and PDPTE should be present + QVERIFY(result.pml4e & 1); // Present bit + QVERIFY(result.pdpte & 1); // Present bit + } + + void vtop_selfModule() + { + if (!m_driverAvailable) QSKIP("Driver not loaded"); + +#ifdef _WIN32 + auto* kprov = dynamic_cast(m_provider.get()); + QVERIFY(kprov); + + uint64_t selfBase = (uint64_t)GetModuleHandleA(nullptr); + auto result = kprov->translateAddress(selfBase); + QVERIFY2(result.valid, "Own module base should be mapped"); + QVERIFY(result.physical != 0); + + // Cross-validate: read MZ header via physical address + // Read the first 2 bytes at the physical address using physical provider + auto physEntries = kprov->readPageTable(kprov->getCr3(), 0, 16); + QVERIFY(physEntries.size() > 0); // Should get at least some PML4 entries +#endif + } + + void vtop_unmapped() + { + if (!m_driverAvailable) QSKIP("Driver not loaded"); + + auto* kprov = dynamic_cast(m_provider.get()); + QVERIFY(kprov); + + // Address 0 should not be mapped in user mode + auto result = kprov->translateAddress(0); + QVERIFY2(!result.valid, "Address 0 should not be mapped"); + } + + void readPageTable_cr3() + { + if (!m_driverAvailable) QSKIP("Driver not loaded"); + + auto* kprov = dynamic_cast(m_provider.get()); + QVERIFY(kprov); + + uint64_t cr3 = kprov->getCr3(); + QVERIFY(cr3 != 0); + + // Read the full PML4 table (512 entries) + auto entries = kprov->readPageTable(cr3, 0, 512); + QCOMPARE(entries.size(), 512); + + // At least some entries should be present (kernel maps upper half) + int presentCount = 0; + for (const auto& e : entries) { + if (e & 1) presentCount++; + } + QVERIFY2(presentCount > 0, + qPrintable(QStringLiteral("Expected present PML4 entries, got 0"))); + } + + // ── 8. Ping ── + + void ping_version() + { + if (!m_driverAvailable) QSKIP("Driver not loaded"); + +#ifdef _WIN32 + HANDLE h = CreateFileA(RCX_DRV_USERMODE_PATH, + GENERIC_READ | GENERIC_WRITE, + 0, nullptr, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, nullptr); + if (h == INVALID_HANDLE_VALUE) QSKIP("Cannot open driver handle"); + + RcxDrvPingResponse ping{}; + DWORD br = 0; + BOOL ok = DeviceIoControl(h, IOCTL_RCX_PING, nullptr, 0, + &ping, sizeof(ping), &br, nullptr); + CloseHandle(h); + + QVERIFY(ok); + QCOMPARE(ping.version, (uint32_t)RCX_DRV_VERSION); +#endif + } +}; + +QTEST_MAIN(TestKernelProvider) +#include "test_kernel_provider.moc" diff --git a/tests/test_mcp.cpp b/tests/test_mcp.cpp index f3133b7..c2ed6c7 100644 --- a/tests/test_mcp.cpp +++ b/tests/test_mcp.cpp @@ -26,7 +26,7 @@ public: if (!m_server->listen(name)) return false; connect(m_server, &QLocalServer::newConnection, this, [this]() { while (auto* s = m_server->nextPendingConnection()) { - m_clients.append({s, {}, false}); + m_clients.push_back(Client{s, {}, false}); connect(s, &QLocalSocket::readyRead, this, [this, s]() { processSocket(s); }); connect(s, &QLocalSocket::disconnected, this, [this, s]() { for (int i = 0; i < m_clients.size(); i++) diff --git a/tests/test_project_dock.cpp b/tests/test_project_dock.cpp new file mode 100644 index 0000000..3247355 --- /dev/null +++ b/tests/test_project_dock.cpp @@ -0,0 +1,143 @@ +#include +#include +#include +#include +#include +#include + +// Replicates the real app layout: QTabWidget central widget, project dock in LeftDockWidgetArea. + +class TestProjectDock : public QObject { + Q_OBJECT +private: + struct AppLayout { + QMainWindow* win; + QTabWidget* tabs; + QDockWidget* project; + }; + + AppLayout buildApp() { + auto* win = new QMainWindow; + win->resize(1280, 800); + + // QTabWidget as central widget — same as real app + auto* tabs = new QTabWidget(win); + tabs->setTabsClosable(true); + tabs->setMovable(true); + tabs->setDocumentMode(true); + tabs->addTab(new QTextEdit(tabs), "Untitled"); + win->setCentralWidget(tabs); + + // Project dock — same as real app + auto* project = new QDockWidget("Project", win); + project->setObjectName("WorkspaceDock"); + project->setAllowedAreas(Qt::AllDockWidgetAreas); + project->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable); + project->setWidget(new QTextEdit(project)); + win->addDockWidget(Qt::LeftDockWidgetArea, project); + project->hide(); + + return {win, tabs, project}; + } + + void showProject(AppLayout& a) { + if (a.project->isHidden() && !a.project->isFloating()) { + a.win->addDockWidget(Qt::LeftDockWidgetArea, a.project); + a.project->show(); + a.win->resizeDocks({a.project}, {qMax(200, a.win->width() / 5)}, Qt::Horizontal); + } else { + a.project->show(); + } + } + +private slots: + void dockStartsLeft(); + void dockWidthIsReasonable(); + void dockStaysLeftAfterHideShow(); + void dockRespectsDragAfterShow(); +}; + +void TestProjectDock::dockStartsLeft() +{ + auto app = buildApp(); + app.win->show(); + QTest::qWaitForWindowExposed(app.win); + + showProject(app); + QApplication::processEvents(); + + // Project should be to the left of the central tab widget + QVERIFY2(app.project->x() < app.tabs->x(), + qPrintable(QString("Project x=%1, Tabs x=%2") + .arg(app.project->x()).arg(app.tabs->x()))); + delete app.win; +} + +void TestProjectDock::dockWidthIsReasonable() +{ + auto app = buildApp(); + app.win->show(); + QTest::qWaitForWindowExposed(app.win); + + showProject(app); + QApplication::processEvents(); + + int dockWidth = app.project->width(); + int winWidth = app.win->width(); + double ratio = (double)dockWidth / winWidth; + + qDebug() << "Dock width:" << dockWidth << "Window width:" << winWidth + << "Ratio:" << QString::number(ratio * 100, 'f', 1) + "%"; + + QVERIFY2(ratio < 0.40, + qPrintable(QString("Dock too wide: %1% of window").arg(ratio * 100, 0, 'f', 1))); + QVERIFY2(ratio > 0.10, + qPrintable(QString("Dock too narrow: %1% of window").arg(ratio * 100, 0, 'f', 1))); + delete app.win; +} + +void TestProjectDock::dockStaysLeftAfterHideShow() +{ + auto app = buildApp(); + app.win->show(); + QTest::qWaitForWindowExposed(app.win); + + showProject(app); + QApplication::processEvents(); + QVERIFY(app.project->x() < app.tabs->x()); + + app.project->hide(); + QApplication::processEvents(); + + showProject(app); + QApplication::processEvents(); + QVERIFY2(app.project->x() < app.tabs->x(), + qPrintable(QString("After re-show: Project x=%1, Tabs x=%2") + .arg(app.project->x()).arg(app.tabs->x()))); + delete app.win; +} + +void TestProjectDock::dockRespectsDragAfterShow() +{ + auto app = buildApp(); + app.win->show(); + QTest::qWaitForWindowExposed(app.win); + + showProject(app); + QApplication::processEvents(); + QVERIFY(app.project->x() < app.tabs->x()); + + // Simulate user dragging to right + app.win->addDockWidget(Qt::RightDockWidgetArea, app.project); + QApplication::processEvents(); + QCOMPARE(app.win->dockWidgetArea(app.project), Qt::RightDockWidgetArea); + + // Dock is visible — showProject should NOT force it back to left + showProject(app); + QApplication::processEvents(); + QCOMPARE(app.win->dockWidgetArea(app.project), Qt::RightDockWidgetArea); + delete app.win; +} + +QTEST_MAIN(TestProjectDock) +#include "test_project_dock.moc" diff --git a/tests/test_roundtrip_winsdk.cpp b/tests/test_roundtrip_winsdk.cpp new file mode 100644 index 0000000..9d217cf --- /dev/null +++ b/tests/test_roundtrip_winsdk.cpp @@ -0,0 +1,307 @@ +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "imports/import_source.h" +#include "generator.h" + +using namespace rcx; + +class TestRoundtripWinSdk : public QObject { + Q_OBJECT +private: + NodeTree fullTree; + QVector rootIndices; + +private slots: + void initTestCase(); + void importCount(); + void pebOffsets(); + void roundTrip30(); + void generateRcx(); +}; + +void TestRoundtripWinSdk::initTestCase() +{ + QString path = QStringLiteral(WINSDK_HEADER_PATH); + QFile file(path); + QVERIFY2(file.open(QIODevice::ReadOnly | QIODevice::Text), + qPrintable("Cannot open " + path)); + QString source = QString::fromUtf8(file.readAll()); + QVERIFY(!source.isEmpty()); + + QString err; + fullTree = importFromSource(source, &err, 8); + + for (int i = 0; i < fullTree.nodes.size(); i++) { + const auto& n = fullTree.nodes[i]; + if (n.parentId == 0 && n.kind == NodeKind::Struct) + rootIndices.append(i); + } + qDebug() << "Imported" << fullTree.nodes.size() << "total nodes," + << rootIndices.size() << "root structs"; +} + +void TestRoundtripWinSdk::importCount() +{ + QVERIFY2(rootIndices.size() >= 3000, + qPrintable(QString("Expected >= 3000 roots, got %1").arg(rootIndices.size()))); +} + +void TestRoundtripWinSdk::pebOffsets() +{ + // Verify _PEB field offsets match WinDbg dt ntdll!_PEB + int pebIdx = -1; + for (int i = 0; i < fullTree.nodes.size(); i++) { + if (fullTree.nodes[i].parentId == 0 && + fullTree.nodes[i].structTypeName == QStringLiteral("_PEB")) { + pebIdx = i; + break; + } + } + QVERIFY2(pebIdx >= 0, "Could not find _PEB root struct"); + + uint64_t pebId = fullTree.nodes[pebIdx].id; + + // Collect direct children with offsets and sizes + struct ChildInfo { QString name; int offset; int size; NodeKind kind; }; + QVector children; + for (int i = 0; i < fullTree.nodes.size(); i++) { + if (fullTree.nodes[i].parentId == pebId) { + int sz = sizeForKind(fullTree.nodes[i].kind); + if (sz == 0) sz = fullTree.structSpan(fullTree.nodes[i].id); + if (sz == 0) sz = 1; + children.push_back(ChildInfo{fullTree.nodes[i].name, fullTree.nodes[i].offset, sz, fullTree.nodes[i].kind}); + } + } + + // Sort by offset + std::sort(children.begin(), children.end(), + [](const ChildInfo& a, const ChildInfo& b) { return a.offset < b.offset; }); + + // Dump all children for diagnostics + for (const auto& c : children) { + qDebug() << " " << Qt::hex << c.offset << c.name + << "kind=" << kindToString(c.kind) << "size=" << c.size; + } + + // Check for overlaps + int overlapCount = 0; + for (int i = 1; i < children.size(); i++) { + int prevEnd = children[i-1].offset + children[i-1].size; + if (children[i].offset < prevEnd && children[i-1].kind != NodeKind::Struct) { + // Only flag overlaps where previous field has a known size (not struct references) + overlapCount++; + if (overlapCount <= 10) + qDebug() << " OVERLAP:" << children[i].name << "at" << Qt::hex << children[i].offset + << "overlaps" << children[i-1].name << "(ends at" << Qt::hex << prevEnd << ")"; + } + } + + // Build name→offset map for field checks + QHash offsets; + QHash kinds; + for (const auto& c : children) { + offsets[c.name] = c.offset; + kinds[c.name] = c.kind; + } + + int failCount = 0; + auto checkField = [&](const QString& name, int expected, bool mustBePointer = false) { + if (!offsets.contains(name)) { + qDebug() << " MISSING:" << name; + failCount++; + return; + } + if (offsets[name] != expected) { + qDebug() << " OFFSET MISMATCH:" << name << "got" << Qt::hex << offsets[name] + << "expected" << Qt::hex << expected; + failCount++; + return; + } + if (mustBePointer) { + NodeKind k = kinds[name]; + if (k != NodeKind::Pointer64 && k != NodeKind::Pointer32) { + qDebug() << " NOT POINTER:" << name << "kind=" << kindToString(k); + failCount++; + } + } + }; + + // Expected offsets computed from the source header layout (Vergilius-style) + // Note: This header has union ALIGN(8) { KernelCallbackTable; UserSharedInfoPtr; } + // after CrossProcessFlags, which shifts fields +0xC compared to some WinDbg versions. + checkField(QStringLiteral("InheritedAddressSpace"), 0x000); + checkField(QStringLiteral("ReadImageFileExecOptions"), 0x001); + checkField(QStringLiteral("BeingDebugged"), 0x002); + checkField(QStringLiteral("Mutant"), 0x008, true); + checkField(QStringLiteral("ImageBaseAddress"), 0x010, true); + checkField(QStringLiteral("Ldr"), 0x018, true); + checkField(QStringLiteral("ProcessParameters"), 0x020, true); + checkField(QStringLiteral("SubSystemData"), 0x028, true); + checkField(QStringLiteral("ProcessHeap"), 0x030, true); + checkField(QStringLiteral("FastPebLock"), 0x038, true); + checkField(QStringLiteral("AtlThunkSListPtr"), 0x040, true); + checkField(QStringLiteral("IFEOKey"), 0x048, true); + checkField(QStringLiteral("SystemReserved"), 0x060); + checkField(QStringLiteral("AtlThunkSListPtr32"), 0x064); + checkField(QStringLiteral("ApiSetMap"), 0x068, true); + checkField(QStringLiteral("TlsExpansionCounter"), 0x070); + checkField(QStringLiteral("TlsBitmap"), 0x078, true); + checkField(QStringLiteral("TlsBitmapBits"), 0x080); + checkField(QStringLiteral("ReadOnlySharedMemoryBase"), 0x088, true); + checkField(QStringLiteral("SharedData"), 0x090, true); + checkField(QStringLiteral("ReadOnlyStaticServerData"), 0x098, true); + checkField(QStringLiteral("AnsiCodePageData"), 0x0A0, true); + checkField(QStringLiteral("OemCodePageData"), 0x0A8, true); + checkField(QStringLiteral("UnicodeCaseTableData"), 0x0B0, true); + checkField(QStringLiteral("NumberOfProcessors"), 0x0B8); + checkField(QStringLiteral("NtGlobalFlag"), 0x0BC); + checkField(QStringLiteral("HeapSegmentReserve"), 0x0C8); + checkField(QStringLiteral("NumberOfHeaps"), 0x0E8); + checkField(QStringLiteral("MaximumNumberOfHeaps"), 0x0EC); + checkField(QStringLiteral("ProcessHeaps"), 0x0F0, true); + checkField(QStringLiteral("OSMajorVersion"), 0x118); + checkField(QStringLiteral("OSMinorVersion"), 0x11C); + checkField(QStringLiteral("OSBuildNumber"), 0x120); + checkField(QStringLiteral("SessionId"), 0x2C0); + checkField(QStringLiteral("CsrServerReadOnlySharedMemoryBase"), 0x380); + checkField(QStringLiteral("TppWorkerpListLock"), 0x388, true); + checkField(QStringLiteral("WaitOnAddressHashTable"), 0x3A0); + checkField(QStringLiteral("TelemetryCoverageHeader"), 0x7A0, true); + checkField(QStringLiteral("CloudFileFlags"), 0x7A8); + checkField(QStringLiteral("CloudFileDiagFlags"), 0x7AC); + checkField(QStringLiteral("PlaceholderCompatibilityMode"), 0x7B0); + checkField(QStringLiteral("LeapSecondData"), 0x7B8, true); + checkField(QStringLiteral("NtGlobalFlag2"), 0x7C4); + + QVERIFY2(failCount == 0, + qPrintable(QString("%1 PEB field(s) have wrong offsets or are missing").arg(failCount))); +} + +void TestRoundtripWinSdk::roundTrip30() +{ + const int kRequired = 30; + + // Deterministic shuffle + QVector shuffled = rootIndices; + std::mt19937 rng(42); + std::shuffle(shuffled.begin(), shuffled.end(), rng); + + int passCount = 0; + int failCount = 0; + int skipCount = 0; + + for (int ri : shuffled) { + uint64_t rootId = fullTree.nodes[ri].id; + QString structName = fullTree.nodes[ri].structTypeName; + + // Pass 1: export from full tree + QString cpp1 = renderCpp(fullTree, rootId, nullptr, true); + if (cpp1.isEmpty()) { + skipCount++; + continue; + } + + // Pass 2: re-import + QString err; + NodeTree tree2 = importFromSource(cpp1, &err); + if (tree2.nodes.isEmpty()) { + skipCount++; + continue; + } + + // Find the root in re-imported tree + int rootIdx2 = -1; + for (int i = 0; i < tree2.nodes.size(); i++) { + if (tree2.nodes[i].parentId == 0 && tree2.nodes[i].kind == NodeKind::Struct) { + if (tree2.nodes[i].structTypeName == structName) { + rootIdx2 = i; + break; + } + } + } + if (rootIdx2 < 0) { + // Take first root + for (int i = 0; i < tree2.nodes.size(); i++) { + if (tree2.nodes[i].parentId == 0 && tree2.nodes[i].kind == NodeKind::Struct) { + rootIdx2 = i; + break; + } + } + } + if (rootIdx2 < 0) { + skipCount++; + continue; + } + + // Pass 3: re-export + QString cpp2 = renderCpp(tree2, tree2.nodes[rootIdx2].id, nullptr, true); + + if (cpp1 == cpp2) { + passCount++; + if (passCount <= kRequired) + qDebug() << " PASS" << passCount << structName; + } else { + failCount++; + if (failCount <= 5) { + // Log first few failures for diagnostics + QStringList lines1 = cpp1.split('\n'); + QStringList lines2 = cpp2.split('\n'); + int diffLine = -1; + for (int i = 0; i < qMin(lines1.size(), lines2.size()); i++) { + if (lines1[i] != lines2[i]) { diffLine = i; break; } + } + if (diffLine >= 0) { + qDebug() << " FAIL" << structName << "first diff at line" << diffLine; + qDebug() << " cpp1:" << lines1[diffLine].left(120); + qDebug() << " cpp2:" << lines2[diffLine].left(120); + } else { + qDebug() << " FAIL" << structName << "line count differs:" + << lines1.size() << "vs" << lines2.size(); + } + } + } + + if (passCount >= kRequired && failCount > 5) + break; // found enough passes and logged enough failures + } + + qDebug() << "Round-trip results: pass=" << passCount + << "fail=" << failCount << "skip=" << skipCount; + QVERIFY2(passCount >= kRequired, + qPrintable(QString("Need %1 stable round-trips, got %2") + .arg(kRequired).arg(passCount))); +} + +void TestRoundtripWinSdk::generateRcx() +{ + // Set all root structs collapsed + for (int ri : rootIndices) + fullTree.nodes[ri].collapsed = true; + + fullTree.baseAddress = 0xFFFFF80000000000ULL; + + QJsonObject json = fullTree.toJson(); + QJsonDocument jdoc(json); + QByteArray data = jdoc.toJson(QJsonDocument::Indented); + + QVERIFY2(data.size() > 1000000, + qPrintable(QString("RCX too small: %1 bytes").arg(data.size()))); + + QString outPath = QStringLiteral(WINSDK_RCX_OUTPUT); + QFile file(outPath); + QVERIFY2(file.open(QIODevice::WriteOnly | QIODevice::Truncate), + qPrintable("Cannot write " + outPath)); + file.write(data); + file.close(); + + qDebug() << "Wrote" << data.size() << "bytes to" << outPath; +} + +QTEST_MAIN(TestRoundtripWinSdk) +#include "test_roundtrip_winsdk.moc" diff --git a/tests/test_scanner.cpp b/tests/test_scanner.cpp index 7d42181..afdb38d 100644 --- a/tests/test_scanner.cpp +++ b/tests/test_scanner.cpp @@ -644,8 +644,8 @@ private slots: data[16] = 0xAA; // in region 1 (executable) QVector regions; - regions.append({0, 16, true, true, false, "heap"}); - regions.append({16, 16, true, false, true, "code"}); + regions.push_back(MemoryRegion{0, 16, true, true, false, "heap"}); + regions.push_back(MemoryRegion{16, 16, true, false, true, "code"}); auto prov = std::make_shared(data, regions); ScanEngine engine; @@ -671,8 +671,8 @@ private slots: data[16] = 0xBB; // region 1 (not writable) QVector regions; - regions.append({0, 16, true, true, false, "data"}); - regions.append({16, 16, true, false, true, "code"}); + regions.push_back(MemoryRegion{0, 16, true, true, false, "data"}); + regions.push_back(MemoryRegion{16, 16, true, false, true, "code"}); auto prov = std::make_shared(data, regions); ScanEngine engine; @@ -698,9 +698,9 @@ private slots: data[32] = 0xCC; // region 2: +w +x QVector regions; - regions.append({0, 16, true, true, false, "data"}); - regions.append({16, 16, true, false, true, "code"}); - regions.append({32, 16, true, true, true, "rwx"}); + regions.push_back(MemoryRegion{0, 16, true, true, false, "data"}); + regions.push_back(MemoryRegion{16, 16, true, false, true, "code"}); + regions.push_back(MemoryRegion{32, 16, true, true, true, "rwx"}); auto prov = std::make_shared(data, regions); ScanEngine engine; @@ -726,7 +726,7 @@ private slots: data[0] = 0xDD; QVector regions; - regions.append({0, 16, true, true, true, "Game.exe"}); + regions.push_back(MemoryRegion{0, 16, true, true, true, "Game.exe"}); auto prov = std::make_shared(data, regions); ScanEngine engine; @@ -943,8 +943,8 @@ private slots: void provider_customRegions() { QVector regs; - regs.append({0x1000, 0x2000, true, true, false, "heap"}); - regs.append({0x3000, 0x1000, true, false, true, "code"}); + regs.push_back(MemoryRegion{0x1000, 0x2000, true, true, false, "heap"}); + regs.push_back(MemoryRegion{0x3000, 0x1000, true, false, true, "code"}); RegionProvider p(QByteArray(0x4000, '\0'), regs); auto result = p.enumerateRegions(); @@ -982,9 +982,9 @@ private slots: data[36] = 0xEE; // region 2 QVector regions; - regions.append({0, 16, true, true, false, "region0"}); - regions.append({16, 16, true, true, false, "region1"}); - regions.append({32, 16, true, true, false, "region2"}); + regions.push_back(MemoryRegion{0, 16, true, true, false, "region0"}); + regions.push_back(MemoryRegion{16, 16, true, true, false, "region1"}); + regions.push_back(MemoryRegion{32, 16, true, true, false, "region2"}); auto prov = std::make_shared(data, regions); ScanEngine engine; @@ -1215,7 +1215,7 @@ private slots: data[160] = char(0xCC); data[210] = char(0xCC); QVector regions; - regions.append({100, 100, true, false, false, {}}); + regions.push_back(MemoryRegion{100, 100, true, false, false, {}}); auto prov = std::make_shared(data, regions); ScanEngine engine; QSignalSpy finSpy(&engine, &ScanEngine::finished); @@ -1233,7 +1233,7 @@ private slots: void scan_constrainRegions_noOverlap() { QByteArray data(32, char(0xEE)); QVector regions; - regions.append({0, 16, true, false, false, {}}); + regions.push_back(MemoryRegion{0, 16, true, false, false, {}}); auto prov = std::make_shared(data, regions); ScanEngine engine; QSignalSpy finSpy(&engine, &ScanEngine::finished); @@ -1256,8 +1256,8 @@ private slots: data[10] = char(0xDD); data[35] = char(0xDD); QVector regions; - regions.append({0, 16, true, true, false, {}}); - regions.append({32, 16, true, true, false, {}}); + regions.push_back(MemoryRegion{0, 16, true, true, false, {}}); + regions.push_back(MemoryRegion{32, 16, true, true, false, {}}); auto prov = std::make_shared(data, regions); ScanEngine engine; QSignalSpy finSpy(&engine, &ScanEngine::finished); @@ -1279,7 +1279,7 @@ private slots: data[120] = char(0xAB); data[160] = char(0xAB); QVector regions; - regions.append({100, 100, true, true, false, {}}); + regions.push_back(MemoryRegion{100, 100, true, true, false, {}}); auto prov = std::make_shared(data, regions); ScanEngine engine; QSignalSpy finSpy(&engine, &ScanEngine::finished); @@ -1300,8 +1300,8 @@ private slots: data[0x1500] = char(0xCC); data[0x5500] = char(0xCC); QVector regions; - regions.append({0x1000, 0x1000, true, false, true, QString("game.exe")}); - regions.append({0x5000, 0x1000, true, true, false, {}}); + regions.push_back(MemoryRegion{0x1000, 0x1000, true, false, true, QString("game.exe")}); + regions.push_back(MemoryRegion{0x5000, 0x1000, true, true, false, {}}); auto prov = std::make_shared(data, regions); ScanEngine engine; QSignalSpy finSpy(&engine, &ScanEngine::finished); @@ -1345,8 +1345,8 @@ private slots: data[12] = char(0xEF); data[20] = char(0xEF); QVector regions; - regions.append({0, 16, true, true, false, {}}); - regions.append({16, 16, true, true, false, {}}); + regions.push_back(MemoryRegion{0, 16, true, true, false, {}}); + regions.push_back(MemoryRegion{16, 16, true, true, false, {}}); auto prov = std::make_shared(data, regions); ScanEngine engine; QSignalSpy finSpy(&engine, &ScanEngine::finished); @@ -1368,8 +1368,8 @@ private slots: data[0x1100] = char(0xBB); data[0x2100] = char(0xBB); QVector regions; - regions.append({0x1000, 0x1000, true, false, true, {}}); - regions.append({0x2000, 0x1000, true, true, false, {}}); + regions.push_back(MemoryRegion{0x1000, 0x1000, true, false, true, {}}); + regions.push_back(MemoryRegion{0x2000, 0x1000, true, true, false, {}}); auto prov = std::make_shared(data, regions); ScanEngine engine; QSignalSpy finSpy(&engine, &ScanEngine::finished); @@ -1394,7 +1394,7 @@ private slots: data[15] = char(0xAA); // inside region, should be found data[25] = char(0xAA); // outside region, should NOT be found QVector regions; - regions.append({10, 10, true, true, false, {}}); + regions.push_back(MemoryRegion{10, 10, true, true, false, {}}); auto prov = std::make_shared(data, regions); ScanEngine engine; QSignalSpy finSpy(&engine, &ScanEngine::finished); @@ -1415,7 +1415,7 @@ private slots: data[5] = char(0xBB); data[15] = char(0xBB); QVector regions; - regions.append({0, 32, true, true, false, {}}); + regions.push_back(MemoryRegion{0, 32, true, true, false, {}}); auto prov = std::make_shared(data, regions); ScanEngine engine; QSignalSpy finSpy(&engine, &ScanEngine::finished); @@ -1506,7 +1506,7 @@ private slots: QByteArray data(0x10000, 0); data[0x8100] = char(0xFF); QVector regions; - regions.append({0x8000, 0x1000, true, true, false, {}}); + regions.push_back(MemoryRegion{0x8000, 0x1000, true, true, false, {}}); auto prov = std::make_shared(data, regions); ScanEngine engine; QSignalSpy finSpy(&engine, &ScanEngine::finished); @@ -1581,7 +1581,7 @@ private slots: QByteArray data(64, 0); data[20] = char(0xFE); QVector regions; - regions.append({0, 64, true, true, false, {}}); + regions.push_back(MemoryRegion{0, 64, true, true, false, {}}); auto prov = std::make_shared(data, regions); ScanEngine engine; QSignalSpy finSpy(&engine, &ScanEngine::finished); @@ -1602,7 +1602,7 @@ private slots: QByteArray data(64, 0); data[36] = char(0xDE); data[37] = char(0xAD); data[38] = char(0xBE); data[39] = char(0xEF); QVector regions; - regions.append({0, 64, true, true, false, {}}); + regions.push_back(MemoryRegion{0, 64, true, true, false, {}}); auto prov = std::make_shared(data, regions); ScanEngine engine; QSignalSpy finSpy(&engine, &ScanEngine::finished); @@ -1624,7 +1624,7 @@ private slots: QByteArray data(64, 0); data[36] = char(0xDE); data[37] = char(0xAD); data[38] = char(0xBE); data[39] = char(0xEF); QVector regions; - regions.append({0, 64, true, true, false, {}}); + regions.push_back(MemoryRegion{0, 64, true, true, false, {}}); auto prov = std::make_shared(data, regions); ScanEngine engine; QSignalSpy finSpy(&engine, &ScanEngine::finished); @@ -1643,7 +1643,7 @@ private slots: // Region [0, 64). Constraint [30, 32). 4-byte pattern can't fit in 2 bytes. QByteArray data(64, char(0xAA)); QVector regions; - regions.append({0, 64, true, true, false, {}}); + regions.push_back(MemoryRegion{0, 64, true, true, false, {}}); auto prov = std::make_shared(data, regions); ScanEngine engine; QSignalSpy finSpy(&engine, &ScanEngine::finished); @@ -1663,7 +1663,7 @@ private slots: QByteArray data(64, 0); data[30] = char(0x11); data[31] = char(0x22); data[32] = char(0x33); data[33] = char(0x44); QVector regions; - regions.append({0, 64, true, true, false, {}}); + regions.push_back(MemoryRegion{0, 64, true, true, false, {}}); auto prov = std::make_shared(data, regions); ScanEngine engine; QSignalSpy finSpy(&engine, &ScanEngine::finished); @@ -1686,8 +1686,8 @@ private slots: data[15] = char(0x77); // last byte of first region data[16] = char(0x77); // first byte of second region QVector regions; - regions.append({0, 16, true, true, false, {}}); - regions.append({16, 16, true, true, false, {}}); + regions.push_back(MemoryRegion{0, 16, true, true, false, {}}); + regions.push_back(MemoryRegion{16, 16, true, true, false, {}}); auto prov = std::make_shared(data, regions); ScanEngine engine; QSignalSpy finSpy(&engine, &ScanEngine::finished); @@ -1711,7 +1711,7 @@ private slots: QByteArray data(64, 0); data[10] = char(0xAA); data[11] = char(0xBB); data[12] = char(0xCC); data[13] = char(0xDD); QVector regions; - regions.append({0, 64, true, true, false, {}}); + regions.push_back(MemoryRegion{0, 64, true, true, false, {}}); auto prov = std::make_shared(data, regions); ScanEngine engine; QSignalSpy finSpy(&engine, &ScanEngine::finished);