feat: 32-bit process support, scanner rescan filtering, suppress flash on navigate

- Add pointerSize() to Provider base; WoW64/ELF detection in ProcessMemory,
  WinDbg, and RemoteProcessMemory plugins
- Wire pointer size through NodeTree, source/XML imports, C++ generator,
  controller, compose, address parser, and RPC protocol header
- Add is32Bit to PluginProcessInfo and ProcessInfo; show (32-bit) in picker
- Scanner rescan now filters results against the current input value
- Go-to-address from scanner resets change tracking to prevent false flashing
This commit is contained in:
IChooseYou
2026-03-01 07:42:40 -07:00
committed by IChooseYou
parent ecfac3decf
commit ed8a44917b
28 changed files with 761 additions and 98 deletions

View File

@@ -80,15 +80,19 @@ static const struct { int xmlType; NodeKind kind; } kTypeMap2013[] = {
{ 30, NodeKind::Array }, // ClassPointerArray
};
static NodeKind lookupKind(int xmlType, XmlVersion ver) {
static NodeKind lookupKind(int xmlType, XmlVersion ver, int ptrSize = 8) {
NodeKind k = NodeKind::Hex8;
if (ver == XmlVersion::V2016) {
for (const auto& e : kTypeMap2016)
if (e.xmlType == xmlType) return e.kind;
if (e.xmlType == xmlType) { k = e.kind; break; }
} else {
for (const auto& e : kTypeMap2013)
if (e.xmlType == xmlType) return e.kind;
if (e.xmlType == xmlType) { k = e.kind; break; }
}
return NodeKind::Hex8; // fallback
// Remap pointer types for 32-bit targets
if (ptrSize < 8 && k == NodeKind::Pointer64)
k = NodeKind::Pointer32;
return k;
}
// Is this XML type a pointer-like type that uses the "Pointer" attribute?
@@ -135,7 +139,7 @@ struct PendingRef {
QString className;
};
NodeTree importReclassXml(const QString& filePath, QString* errorMsg) {
NodeTree importReclassXml(const QString& filePath, QString* errorMsg, int pointerSize) {
qDebug() << "[ImportXML] Opening file:" << filePath;
QFile file(filePath);
@@ -152,6 +156,7 @@ NodeTree importReclassXml(const QString& filePath, QString* errorMsg) {
NodeTree tree;
tree.baseAddress = 0x00400000;
tree.pointerSize = pointerSize;
// Class name → struct node ID (for pointer resolution)
QHash<QString, uint64_t> classIds;
@@ -249,7 +254,7 @@ NodeTree importReclassXml(const QString& filePath, QString* errorMsg) {
continue;
}
NodeKind kind = lookupKind(xmlType, version);
NodeKind kind = lookupKind(xmlType, version, pointerSize);
// Handle ClassInstanceArray: read child <Array> element
if (isClassInstanceArrayType(xmlType, version)) {

View File

@@ -5,7 +5,9 @@ namespace rcx {
// Import a ReClass XML file (.reclass, .MemeCls, etc.) into a NodeTree.
// Supports ReClassEx, MemeClsEx, ReClass 2011/2013/2016 XML formats.
// pointerSize: 4 for 32-bit targets, 8 for 64-bit (default).
// Returns an empty NodeTree on failure; populates errorMsg if non-null.
NodeTree importReclassXml(const QString& filePath, QString* errorMsg = nullptr);
NodeTree importReclassXml(const QString& filePath, QString* errorMsg = nullptr,
int pointerSize = 8);
} // namespace rcx

View File

@@ -14,8 +14,12 @@ struct TypeInfo {
int size; // bytes (0 = dynamic/pointer)
};
static QHash<QString, TypeInfo> buildTypeTable() {
static QHash<QString, TypeInfo> buildTypeTable(int ptrSize = 8) {
QHash<QString, TypeInfo> t;
// Pointer/size_t kinds depend on target architecture
NodeKind ptrKind = (ptrSize >= 8) ? NodeKind::Pointer64 : NodeKind::Pointer32;
NodeKind uintpKind = (ptrSize >= 8) ? NodeKind::UInt64 : NodeKind::UInt32;
NodeKind intpKind = (ptrSize >= 8) ? NodeKind::Int64 : NodeKind::Int32;
// stdint.h
t[QStringLiteral("uint8_t")] = {NodeKind::UInt8, 1};
@@ -85,35 +89,35 @@ static QHash<QString, TypeInfo> buildTypeTable() {
t[QStringLiteral("LONG64")] = {NodeKind::Int64, 8};
t[QStringLiteral("INT64")] = {NodeKind::Int64, 8};
// Platform pointer-size types
t[QStringLiteral("PVOID")] = {NodeKind::Pointer64, 8};
t[QStringLiteral("LPVOID")] = {NodeKind::Pointer64, 8};
t[QStringLiteral("HANDLE")] = {NodeKind::Pointer64, 8};
t[QStringLiteral("HMODULE")] = {NodeKind::Pointer64, 8};
t[QStringLiteral("HWND")] = {NodeKind::Pointer64, 8};
t[QStringLiteral("HINSTANCE")] = {NodeKind::Pointer64, 8};
t[QStringLiteral("SIZE_T")] = {NodeKind::UInt64, 8};
t[QStringLiteral("ULONG_PTR")] = {NodeKind::UInt64, 8};
t[QStringLiteral("UINT_PTR")] = {NodeKind::UInt64, 8};
t[QStringLiteral("DWORD_PTR")] = {NodeKind::UInt64, 8};
t[QStringLiteral("LONG_PTR")] = {NodeKind::Int64, 8};
t[QStringLiteral("INT_PTR")] = {NodeKind::Int64, 8};
t[QStringLiteral("SSIZE_T")] = {NodeKind::Int64, 8};
t[QStringLiteral("uintptr_t")] = {NodeKind::UInt64, 8};
t[QStringLiteral("intptr_t")] = {NodeKind::Int64, 8};
t[QStringLiteral("size_t")] = {NodeKind::UInt64, 8};
t[QStringLiteral("ptrdiff_t")] = {NodeKind::Int64, 8};
t[QStringLiteral("ssize_t")] = {NodeKind::Int64, 8};
// Platform pointer-size types (depend on target architecture)
t[QStringLiteral("PVOID")] = {ptrKind, ptrSize};
t[QStringLiteral("LPVOID")] = {ptrKind, ptrSize};
t[QStringLiteral("HANDLE")] = {ptrKind, ptrSize};
t[QStringLiteral("HMODULE")] = {ptrKind, ptrSize};
t[QStringLiteral("HWND")] = {ptrKind, ptrSize};
t[QStringLiteral("HINSTANCE")] = {ptrKind, ptrSize};
t[QStringLiteral("SIZE_T")] = {uintpKind, ptrSize};
t[QStringLiteral("ULONG_PTR")] = {uintpKind, ptrSize};
t[QStringLiteral("UINT_PTR")] = {uintpKind, ptrSize};
t[QStringLiteral("DWORD_PTR")] = {uintpKind, ptrSize};
t[QStringLiteral("LONG_PTR")] = {intpKind, ptrSize};
t[QStringLiteral("INT_PTR")] = {intpKind, ptrSize};
t[QStringLiteral("SSIZE_T")] = {intpKind, ptrSize};
t[QStringLiteral("uintptr_t")] = {uintpKind, ptrSize};
t[QStringLiteral("intptr_t")] = {intpKind, ptrSize};
t[QStringLiteral("size_t")] = {uintpKind, ptrSize};
t[QStringLiteral("ptrdiff_t")] = {intpKind, ptrSize};
t[QStringLiteral("ssize_t")] = {intpKind, ptrSize};
// Pointer type aliases
t[QStringLiteral("PCHAR")] = {NodeKind::Pointer64, 8};
t[QStringLiteral("LPSTR")] = {NodeKind::Pointer64, 8};
t[QStringLiteral("LPCSTR")] = {NodeKind::Pointer64, 8};
t[QStringLiteral("PCSTR")] = {NodeKind::Pointer64, 8};
t[QStringLiteral("PWSTR")] = {NodeKind::Pointer64, 8};
t[QStringLiteral("LPWSTR")] = {NodeKind::Pointer64, 8};
t[QStringLiteral("LPCWSTR")]= {NodeKind::Pointer64, 8};
t[QStringLiteral("PCWSTR")] = {NodeKind::Pointer64, 8};
t[QStringLiteral("PCHAR")] = {ptrKind, ptrSize};
t[QStringLiteral("LPSTR")] = {ptrKind, ptrSize};
t[QStringLiteral("LPCSTR")] = {ptrKind, ptrSize};
t[QStringLiteral("PCSTR")] = {ptrKind, ptrSize};
t[QStringLiteral("PWSTR")] = {ptrKind, ptrSize};
t[QStringLiteral("LPWSTR")] = {ptrKind, ptrSize};
t[QStringLiteral("LPCWSTR")]= {ptrKind, ptrSize};
t[QStringLiteral("PCWSTR")] = {ptrKind, ptrSize};
return t;
}
@@ -940,6 +944,7 @@ struct BuildContext {
QVector<PendingRef>& pendingRefs;
bool useCommentOffsets;
QSet<QString> enumNames; // enum type names (emit as UInt32 + refId)
int ptrSize = 8; // target pointer size (4 or 8)
};
static void buildFields(BuildContext& ctx, uint64_t parentId, int baseOffset,
@@ -1018,7 +1023,7 @@ static void buildFields(BuildContext& ctx, uint64_t parentId, int baseOffset,
// Pointer field
if (field.isPointer) {
Node n;
n.kind = NodeKind::Pointer64;
n.kind = (ctx.ptrSize >= 8) ? NodeKind::Pointer64 : NodeKind::Pointer32;
n.name = field.name;
n.parentId = parentId;
n.offset = fieldOffset;
@@ -1032,7 +1037,7 @@ static void buildFields(BuildContext& ctx, uint64_t parentId, int baseOffset,
ctx.pendingRefs.append({nodeId, field.pointerTarget});
}
computedOffset = fieldOffset + 8;
computedOffset = fieldOffset + ctx.ptrSize;
continue;
}
@@ -1217,7 +1222,7 @@ static bool hasAnyCommentOffset(const QVector<ParsedField>& fields) {
// ── NodeTree builder ──
NodeTree importFromSource(const QString& sourceCode, QString* errorMsg) {
NodeTree importFromSource(const QString& sourceCode, QString* errorMsg, int pointerSize) {
if (sourceCode.trimmed().isEmpty()) {
if (errorMsg) *errorMsg = QStringLiteral("Empty source code");
return {};
@@ -1236,8 +1241,8 @@ NodeTree importFromSource(const QString& sourceCode, QString* errorMsg) {
return {};
}
// Build type table
QHash<QString, TypeInfo> typeTable = buildTypeTable();
// Build type table (pointer-size types depend on target architecture)
QHash<QString, TypeInfo> typeTable = buildTypeTable(pointerSize);
// Register typedefs into type table
for (auto it = parser.typedefs.begin(); it != parser.typedefs.end(); ++it) {
@@ -1248,6 +1253,7 @@ NodeTree importFromSource(const QString& sourceCode, QString* errorMsg) {
NodeTree tree;
tree.baseAddress = 0x00400000;
tree.pointerSize = pointerSize;
QHash<QString, uint64_t> classIds;
QVector<PendingRef> pendingRefs;
@@ -1265,7 +1271,7 @@ NodeTree importFromSource(const QString& sourceCode, QString* errorMsg) {
enumNames.insert(ps.name);
}
BuildContext ctx{tree, typeTable, classIds, pendingRefs, useCommentOffsets, enumNames};
BuildContext ctx{tree, typeTable, classIds, pendingRefs, useCommentOffsets, enumNames, pointerSize};
// Build nodes for each struct/enum
for (const auto& ps : parser.structs) {

View File

@@ -7,7 +7,9 @@ namespace rcx {
// Supports two modes (auto-detected):
// 1. With comment offsets (// 0xNN) - trusts the offset values
// 2. Without comment offsets - computes offsets from type sizes
// pointerSize: 4 for 32-bit targets, 8 for 64-bit (default).
// Returns an empty NodeTree on failure; populates errorMsg if non-null.
NodeTree importFromSource(const QString& sourceCode, QString* errorMsg = nullptr);
NodeTree importFromSource(const QString& sourceCode, QString* errorMsg = nullptr,
int pointerSize = 8);
} // namespace rcx