Array element offset display, fold arrow UX, type picker popup, and provider cleanup

- Show relative hex offset on array element separators ([N] +0x...)
- Dim fold arrows and add hover highlight for better visibility
- Extend fold/chevron click areas for easier interaction
- Add type picker popup for array element type and pointer target editing
- Remove process_provider.h in favor of plugin-based provider system
- Expand compose/format to handle struct-of-array type names and widths

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
IChooseYou
2026-02-11 09:13:17 -07:00
committed by sysadmin
parent 3db051f4ba
commit df07b61144
16 changed files with 373 additions and 184 deletions

View File

@@ -1,109 +0,0 @@
#pragma once
#include "provider.h"
#ifdef _WIN32
#include <windows.h>
#include <psapi.h>
namespace rcx {
class ProcessProvider : public Provider {
HANDLE m_handle = nullptr;
uint64_t m_base = 0;
int m_size = 0;
QString m_name;
struct ModuleInfo {
QString name;
uint64_t base;
uint64_t size;
};
QVector<ModuleInfo> m_modules;
public:
ProcessProvider(HANDLE proc, uint64_t base, int regionSize, const QString& name)
: m_handle(proc), m_base(base), m_size(regionSize), m_name(name)
{
cacheModules();
}
~ProcessProvider() override {
if (m_handle) CloseHandle(m_handle);
}
ProcessProvider(const ProcessProvider&) = delete;
ProcessProvider& operator=(const ProcessProvider&) = delete;
int size() const override { return m_size; }
bool isReadable(uint64_t, int len) const override { return len >= 0; }
bool read(uint64_t addr, void* buf, int len) const override {
if (!m_handle || len <= 0) return false;
SIZE_T got = 0;
ReadProcessMemory(m_handle,
(LPCVOID)(m_base + addr), buf, len, &got);
if ((int)got < len)
memset((char*)buf + got, 0, len - got);
return got > 0;
}
bool isWritable() const override { return true; }
bool write(uint64_t addr, const void* buf, int len) override {
SIZE_T got = 0;
BOOL ok = WriteProcessMemory(m_handle,
(LPVOID)(m_base + addr), buf, len, &got);
return ok && (int)got == len;
}
QString name() const override { return m_name; }
QString kind() const override { return QStringLiteral("Process"); }
bool isLive() const override { return true; }
// getSymbol takes an absolute virtual address and resolves it to
// "module.dll+0xOFFSET" using the cached module list.
QString getSymbol(uint64_t absAddr) const override {
for (const auto& mod : m_modules) {
if (absAddr >= mod.base && absAddr < mod.base + mod.size) {
uint64_t offset = absAddr - mod.base;
return QStringLiteral("%1+0x%2")
.arg(mod.name)
.arg(offset, 0, 16, QChar('0'));
}
}
return {};
}
HANDLE handle() const { return m_handle; }
uint64_t baseAddress() const { return m_base; }
uint64_t base() const override { return m_base; }
void setBase(uint64_t b) override { m_base = b; }
void refreshModules() { m_modules.clear(); cacheModules(); }
private:
void cacheModules() {
HMODULE mods[1024];
DWORD needed = 0;
if (!EnumProcessModulesEx(m_handle, mods, sizeof(mods),
&needed, LIST_MODULES_ALL))
return;
int count = qMin((int)(needed / sizeof(HMODULE)), 1024);
m_modules.reserve(count);
for (int i = 0; i < count; ++i) {
MODULEINFO mi{};
WCHAR modName[MAX_PATH];
if (GetModuleInformation(m_handle, mods[i], &mi, sizeof(mi))
&& GetModuleBaseNameW(m_handle, mods[i], modName, MAX_PATH))
{
m_modules.append({
QString::fromWCharArray(modName),
(uint64_t)mi.lpBaseOfDll,
(uint64_t)mi.SizeOfImage
});
}
}
}
};
} // namespace rcx
#endif // _WIN32

View File

@@ -40,7 +40,7 @@ public:
// Resolve an absolute address to a symbol name.
// Returns empty string if no symbol is known.
// ProcessProvider: "ntdll.dll+0x1A30"
// Example: "ntdll.dll+0x1A30"
// BufferProvider: "" (no symbols in flat files)
virtual QString getSymbol(uint64_t addr) const {
Q_UNUSED(addr);