mirror of
https://github.com/NohamR/Reclass.git
synced 2026-05-10 19:59:21 +00:00
fix: replace QList::append({}) with push_back/emplaceBack for Qt 6.8
Qt 6.8's stricter QList rejects brace-enclosed initializer lists in append(). Fixed 43 call sites across 13 files.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#include "controller.h"
|
||||
#include "addressparser.h"
|
||||
#include "symbolstore.h"
|
||||
#include "typeselectorpopup.h"
|
||||
#include "providerregistry.h"
|
||||
#include "themes/thememanager.h"
|
||||
@@ -74,8 +75,10 @@ RcxDocument::RcxDocument(QObject* parent)
|
||||
}
|
||||
|
||||
ComposeResult RcxDocument::compose(uint64_t viewRootId, bool compactColumns,
|
||||
bool treeLines, bool braceWrap, bool typeHints) const {
|
||||
return rcx::compose(tree, *provider, viewRootId, compactColumns, treeLines, braceWrap, typeHints);
|
||||
bool treeLines, bool braceWrap, bool typeHints,
|
||||
SymbolLookupFn symbolLookup) const {
|
||||
return rcx::compose(tree, *provider, viewRootId, compactColumns, treeLines, braceWrap, typeHints,
|
||||
std::move(symbolLookup));
|
||||
}
|
||||
|
||||
bool RcxDocument::save(const QString& path) {
|
||||
@@ -269,6 +272,10 @@ void RcxController::connectEditor(RcxEditor* editor) {
|
||||
// Footer "Trim" button — remove trailing hex nodes from end of struct
|
||||
connect(editor, &RcxEditor::trimHexRequested,
|
||||
this, [this](uint64_t structId) {
|
||||
// Unions don't have trailing padding — all members overlap at offset 0
|
||||
int si = m_doc->tree.indexOfId(structId);
|
||||
if (si >= 0 && m_doc->tree.nodes[si].classKeyword == QStringLiteral("union"))
|
||||
return;
|
||||
QVector<int> children = m_doc->tree.childrenOf(structId);
|
||||
if (children.isEmpty()) return;
|
||||
|
||||
@@ -304,7 +311,7 @@ void RcxController::connectEditor(RcxEditor* editor) {
|
||||
int64_t nextVal = members.isEmpty() ? 0 : members.last().second + 1;
|
||||
auto oldMembers = members;
|
||||
for (int i = 0; i < count; i++)
|
||||
members.append({QStringLiteral("Member%1").arg(nextVal + i), nextVal + i});
|
||||
members.emplaceBack(QStringLiteral("Member%1").arg(nextVal + i), nextVal + i);
|
||||
m_doc->undoStack.push(new RcxCommand(this,
|
||||
cmd::ChangeEnumMembers{enumId, oldMembers, members}));
|
||||
});
|
||||
@@ -442,6 +449,9 @@ void RcxController::connectEditor(RcxEditor* editor) {
|
||||
*ok = prov->read(addr, &val, ptrSz);
|
||||
return val;
|
||||
};
|
||||
cbs.resolveIdentifier = [prov](const QString& name, bool* ok) -> uint64_t {
|
||||
return SymbolStore::instance().resolve(name, prov, ok);
|
||||
};
|
||||
// Wire kernel paging callbacks if provider supports it
|
||||
if (prov->hasKernelPaging()) {
|
||||
cbs.vtop = [prov](uint32_t pid, uint64_t va, bool* ok) -> uint64_t {
|
||||
@@ -467,9 +477,9 @@ void RcxController::connectEditor(RcxEditor* editor) {
|
||||
if (result.ok && result.value != m_doc->tree.baseAddress) {
|
||||
uint64_t oldBase = m_doc->tree.baseAddress;
|
||||
QString oldFormula = m_doc->tree.baseAddressFormula;
|
||||
// Store formula if input uses module/deref/kernel-function syntax
|
||||
// Store formula if input uses module/deref/kernel-function/symbol syntax
|
||||
static const QRegularExpression formulaRx(
|
||||
QStringLiteral("[<\\[]|\\b(?:vtop|cr3|phys)\\s*\\("));
|
||||
QStringLiteral("[<\\[]|\\b(?:vtop|cr3|phys)\\s*\\(|\\w+!\\w+"));
|
||||
QString newFormula = formulaRx.match(s).hasMatch() ? s : QString();
|
||||
m_doc->undoStack.push(new RcxCommand(this,
|
||||
cmd::ChangeBase{oldBase, result.value, oldFormula, newFormula}));
|
||||
@@ -631,11 +641,20 @@ void RcxController::refresh() {
|
||||
// Bracket compose with thread-local doc pointer for type name resolution
|
||||
s_composeDoc = m_doc;
|
||||
|
||||
// Build symbol lookup callback if PDB symbols are loaded
|
||||
SymbolLookupFn symLookup;
|
||||
if (SymbolStore::instance().hasSymbols() && m_doc->provider) {
|
||||
auto* prov = m_doc->provider.get();
|
||||
symLookup = [prov](uint64_t addr) -> QString {
|
||||
return SymbolStore::instance().getSymbolForAddress(addr, prov);
|
||||
};
|
||||
}
|
||||
|
||||
// Compose against snapshot provider if active, otherwise real provider
|
||||
if (m_snapshotProv)
|
||||
m_lastResult = rcx::compose(m_doc->tree, *m_snapshotProv, m_viewRootId, m_compactColumns, m_treeLines, m_braceWrap, m_typeHints);
|
||||
m_lastResult = rcx::compose(m_doc->tree, *m_snapshotProv, m_viewRootId, m_compactColumns, m_treeLines, m_braceWrap, m_typeHints, symLookup);
|
||||
else
|
||||
m_lastResult = m_doc->compose(m_viewRootId, m_compactColumns, m_treeLines, m_braceWrap, m_typeHints);
|
||||
m_lastResult = m_doc->compose(m_viewRootId, m_compactColumns, m_treeLines, m_braceWrap, m_typeHints, symLookup);
|
||||
|
||||
s_composeDoc = nullptr;
|
||||
|
||||
@@ -836,7 +855,7 @@ void RcxController::changeNodeKind(int nodeIdx, NodeKind newKind) {
|
||||
if (si == nodeIdx) continue;
|
||||
auto& sib = m_doc->tree.nodes[si];
|
||||
if (sib.offset >= oldEnd)
|
||||
adjs.append({sib.id, sib.offset, sib.offset + delta});
|
||||
adjs.push_back(cmd::OffsetAdj{sib.id, sib.offset, sib.offset + delta});
|
||||
}
|
||||
}
|
||||
bool needsRename = isHexNode(node.kind) && !isHexNode(newKind);
|
||||
@@ -910,7 +929,7 @@ void RcxController::insertNodeAbove(int beforeIdx, NodeKind kind, const QString&
|
||||
for (int si : siblings) {
|
||||
auto& sib = m_doc->tree.nodes[si];
|
||||
if (sib.offset >= before.offset)
|
||||
adjs.append({sib.id, sib.offset, sib.offset + insertSize});
|
||||
adjs.push_back(cmd::OffsetAdj{sib.id, sib.offset, sib.offset + insertSize});
|
||||
}
|
||||
|
||||
m_doc->undoStack.push(new RcxCommand(this, cmd::Insert{n, adjs}));
|
||||
@@ -935,7 +954,7 @@ void RcxController::removeNode(int nodeIdx) {
|
||||
if (si == nodeIdx) continue;
|
||||
auto& sib = m_doc->tree.nodes[si];
|
||||
if (sib.offset >= deletedEnd) {
|
||||
adjs.append({sib.id, sib.offset, sib.offset - deletedSize});
|
||||
adjs.push_back(cmd::OffsetAdj{sib.id, sib.offset, sib.offset - deletedSize});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1442,7 +1461,7 @@ void RcxController::duplicateNode(int nodeIdx) {
|
||||
if (si == nodeIdx) continue;
|
||||
auto& sib = m_doc->tree.nodes[si];
|
||||
if (sib.offset >= copyOffset)
|
||||
adjs.append({sib.id, sib.offset, sib.offset + copySize});
|
||||
adjs.push_back(cmd::OffsetAdj{sib.id, sib.offset, sib.offset + copySize});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1762,14 +1781,24 @@ void RcxController::showContextMenu(RcxEditor* editor, int line, int nodeIdx,
|
||||
|
||||
// ── Insert ► submenu ──
|
||||
{
|
||||
// Find earliest selected node (lowest offset) for insert-above
|
||||
int firstIdx = -1;
|
||||
int lowestOff = INT_MAX;
|
||||
for (uint64_t id : ids) {
|
||||
int idx = m_doc->tree.indexOfId(id);
|
||||
if (idx >= 0 && m_doc->tree.nodes[idx].offset < lowestOff) {
|
||||
lowestOff = m_doc->tree.nodes[idx].offset;
|
||||
firstIdx = idx;
|
||||
}
|
||||
}
|
||||
auto* insertMenu = menu.addMenu(icon("diff-added.svg"), "Insert");
|
||||
insertMenu->addAction("Insert 4", [this]() {
|
||||
uint64_t target = m_viewRootId ? m_viewRootId : 0;
|
||||
insertNode(target, -1, NodeKind::Hex32, QStringLiteral("field"));
|
||||
insertMenu->addAction("Insert 4 Above", [this, firstIdx]() {
|
||||
if (firstIdx >= 0)
|
||||
insertNodeAbove(firstIdx, NodeKind::Hex32, QStringLiteral("field"));
|
||||
});
|
||||
insertMenu->addAction("Insert 8", [this]() {
|
||||
uint64_t target = m_viewRootId ? m_viewRootId : 0;
|
||||
insertNode(target, -1, NodeKind::Hex64, QStringLiteral("field"));
|
||||
insertMenu->addAction("Insert 8 Above", [this, firstIdx]() {
|
||||
if (firstIdx >= 0)
|
||||
insertNodeAbove(firstIdx, NodeKind::Hex64, QStringLiteral("field"));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1919,7 +1948,7 @@ void RcxController::showContextMenu(RcxEditor* editor, int line, int nodeIdx,
|
||||
auto members = m_doc->tree.nodes[ni].enumMembers;
|
||||
int64_t nextVal = members.isEmpty() ? 0 : members.last().second + 1;
|
||||
auto oldMembers = members;
|
||||
members.append({QStringLiteral("NewMember"), nextVal});
|
||||
members.emplaceBack(QStringLiteral("NewMember"), nextVal);
|
||||
m_doc->undoStack.push(new RcxCommand(this,
|
||||
cmd::ChangeEnumMembers{nodeId, oldMembers, members}));
|
||||
});
|
||||
@@ -3328,6 +3357,9 @@ void RcxController::attachViaPlugin(const QString& providerIdentifier, const QSt
|
||||
*ok = prov->read(addr, &val, ptrSz);
|
||||
return val;
|
||||
};
|
||||
cbs.resolveIdentifier = [prov](const QString& name, bool* ok) -> uint64_t {
|
||||
return SymbolStore::instance().resolve(name, prov, ok);
|
||||
};
|
||||
// Wire kernel paging callbacks if provider supports it
|
||||
if (prov->hasKernelPaging()) {
|
||||
cbs.vtop = [prov](uint32_t pid, uint64_t va, bool* ok) -> uint64_t {
|
||||
@@ -3470,6 +3502,9 @@ void RcxController::selectSource(const QString& text) {
|
||||
*ok = prov->read(addr, &val, ptrSz);
|
||||
return val;
|
||||
};
|
||||
cbs.resolveIdentifier = [prov](const QString& name, bool* ok) -> uint64_t {
|
||||
return SymbolStore::instance().resolve(name, prov, ok);
|
||||
};
|
||||
// Wire kernel paging callbacks if provider supports it
|
||||
if (prov->hasKernelPaging()) {
|
||||
cbs.vtop = [prov](uint32_t pid, uint64_t va, bool* ok) -> uint64_t {
|
||||
@@ -3616,7 +3651,7 @@ void RcxController::collectPointerRanges(
|
||||
|
||||
int span = m_doc->tree.structSpan(structId);
|
||||
if (span <= 0) return;
|
||||
ranges.append({memBase, span});
|
||||
ranges.emplaceBack(memBase, span);
|
||||
|
||||
if (!m_snapshotProv) return;
|
||||
|
||||
@@ -3664,7 +3699,7 @@ void RcxController::onRefreshTick() {
|
||||
|
||||
// Collect all needed ranges: main struct + pointer targets (absolute addresses)
|
||||
QVector<QPair<uint64_t,int>> ranges;
|
||||
ranges.append({m_doc->tree.baseAddress, extent});
|
||||
ranges.emplaceBack(m_doc->tree.baseAddress, extent);
|
||||
|
||||
if (m_snapshotProv) {
|
||||
QSet<QPair<uint64_t,uint64_t>> visited;
|
||||
|
||||
11
src/core.h
11
src/core.h
@@ -297,8 +297,8 @@ struct Node {
|
||||
QJsonArray arr = o["enumMembers"].toArray();
|
||||
for (const auto& v : arr) {
|
||||
QJsonObject em = v.toObject();
|
||||
n.enumMembers.append({em["name"].toString(),
|
||||
em["value"].toString("0").toLongLong()});
|
||||
n.enumMembers.emplaceBack(em["name"].toString(),
|
||||
em["value"].toString("0").toLongLong());
|
||||
}
|
||||
}
|
||||
if (o.contains("bitfieldMembers")) {
|
||||
@@ -1043,8 +1043,13 @@ namespace fmt {
|
||||
|
||||
// ── Compose function forward declaration ──
|
||||
|
||||
// Optional callback: given an absolute address, return a symbol name (e.g. "nt!PsActiveProcessHead")
|
||||
// or empty string if no symbol matches. Used for PDB symbol annotations on rows.
|
||||
using SymbolLookupFn = std::function<QString(uint64_t addr)>;
|
||||
|
||||
ComposeResult compose(const NodeTree& tree, const Provider& prov, uint64_t viewRootId = 0,
|
||||
bool compactColumns = false, bool treeLines = false,
|
||||
bool braceWrap = false, bool typeHints = false);
|
||||
bool braceWrap = false, bool typeHints = false,
|
||||
SymbolLookupFn symbolLookup = {});
|
||||
|
||||
} // namespace rcx
|
||||
|
||||
@@ -396,7 +396,7 @@ uint64_t PdbCtx::importEnum(uint32_t typeIndex) {
|
||||
field->data.LF_ENUMERATE.value,
|
||||
field->data.LF_ENUMERATE.lfEasy.kind);
|
||||
if (eName)
|
||||
s.enumMembers.append({QString::fromUtf8(eName), val});
|
||||
s.enumMembers.emplaceBack(QString::fromUtf8(eName), val);
|
||||
|
||||
i += static_cast<size_t>(eName - reinterpret_cast<const char*>(field));
|
||||
i += strnlen(eName, maxSize - i - 1) + 1;
|
||||
@@ -880,7 +880,7 @@ void PdbCtx::importMemberType(uint32_t typeIndex, int offset, const QString& nam
|
||||
n.name = name;
|
||||
n.parentId = parentId;
|
||||
n.offset = offset;
|
||||
n.bitfieldMembers.append({name, bitPos, bitLen});
|
||||
n.bitfieldMembers.push_back(BitfieldMember{name, bitPos, bitLen});
|
||||
tree.addNode(n);
|
||||
break;
|
||||
}
|
||||
@@ -943,6 +943,118 @@ struct PdbFile {
|
||||
}
|
||||
};
|
||||
|
||||
// ── Public API: extractPdbSymbols ──
|
||||
|
||||
PdbSymbolResult extractPdbSymbols(const QString& pdbPath, QString* errorMsg) {
|
||||
auto setErr = [&](const QString& msg) { if (errorMsg) *errorMsg = msg; };
|
||||
|
||||
MappedFile mapped;
|
||||
if (!QFile::exists(pdbPath)) {
|
||||
setErr(QStringLiteral("PDB file not found: ") + pdbPath);
|
||||
return {};
|
||||
}
|
||||
if (!mapped.open(pdbPath)) {
|
||||
setErr(QStringLiteral("Failed to memory-map PDB file: ") + pdbPath);
|
||||
return {};
|
||||
}
|
||||
if (PDB::ValidateFile(mapped.base, mapped.size) != PDB::ErrorCode::Success) {
|
||||
setErr(QStringLiteral("Invalid PDB file: ") + pdbPath);
|
||||
return {};
|
||||
}
|
||||
|
||||
PDB::RawFile rawFile = PDB::CreateRawFile(mapped.base);
|
||||
if (PDB::HasValidDBIStream(rawFile) != PDB::ErrorCode::Success) {
|
||||
setErr(QStringLiteral("PDB has no valid DBI stream: ") + pdbPath);
|
||||
return {};
|
||||
}
|
||||
|
||||
const PDB::DBIStream dbiStream = PDB::CreateDBIStream(rawFile);
|
||||
|
||||
// Validate required sub-streams
|
||||
if (dbiStream.HasValidSymbolRecordStream(rawFile) != PDB::ErrorCode::Success ||
|
||||
dbiStream.HasValidPublicSymbolStream(rawFile) != PDB::ErrorCode::Success ||
|
||||
dbiStream.HasValidImageSectionStream(rawFile) != PDB::ErrorCode::Success) {
|
||||
setErr(QStringLiteral("PDB DBI stream missing required sub-streams"));
|
||||
return {};
|
||||
}
|
||||
|
||||
const PDB::ImageSectionStream imageSectionStream = dbiStream.CreateImageSectionStream(rawFile);
|
||||
const PDB::CoalescedMSFStream symbolRecordStream = dbiStream.CreateSymbolRecordStream(rawFile);
|
||||
|
||||
PdbSymbolResult result;
|
||||
|
||||
// Derive module name from PDB filename (e.g. "ntoskrnl.pdb" → "ntoskrnl")
|
||||
QFileInfo fi(pdbPath);
|
||||
result.moduleName = fi.completeBaseName();
|
||||
|
||||
// Read public symbols (S_PUB32)
|
||||
const PDB::PublicSymbolStream publicSymbolStream = dbiStream.CreatePublicSymbolStream(rawFile);
|
||||
{
|
||||
const PDB::ArrayView<PDB::HashRecord> hashRecords = publicSymbolStream.GetRecords();
|
||||
const size_t count = hashRecords.GetLength();
|
||||
result.symbols.reserve(static_cast<int>(count));
|
||||
|
||||
for (const PDB::HashRecord& hashRecord : hashRecords) {
|
||||
const PDB::CodeView::DBI::Record* record =
|
||||
publicSymbolStream.GetRecord(symbolRecordStream, hashRecord);
|
||||
if (record->header.kind != PDB::CodeView::DBI::SymbolRecordKind::S_PUB32)
|
||||
continue;
|
||||
|
||||
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(
|
||||
record->data.S_PUB32.section, record->data.S_PUB32.offset);
|
||||
if (rva == 0u)
|
||||
continue;
|
||||
|
||||
result.symbols.push_back(PdbSymbol{QString::fromUtf8(record->data.S_PUB32.name), rva});
|
||||
}
|
||||
}
|
||||
|
||||
// Read global symbols (S_GDATA32, S_GTHREAD32, S_LDATA32, S_LTHREAD32, S_GPROC32, S_LPROC32)
|
||||
if (dbiStream.HasValidGlobalSymbolStream(rawFile) == PDB::ErrorCode::Success) {
|
||||
const PDB::GlobalSymbolStream globalSymbolStream = dbiStream.CreateGlobalSymbolStream(rawFile);
|
||||
const PDB::ArrayView<PDB::HashRecord> hashRecords = globalSymbolStream.GetRecords();
|
||||
|
||||
result.symbols.reserve(result.symbols.size() + static_cast<int>(hashRecords.GetLength()));
|
||||
|
||||
for (const PDB::HashRecord& hashRecord : hashRecords) {
|
||||
const PDB::CodeView::DBI::Record* record =
|
||||
globalSymbolStream.GetRecord(symbolRecordStream, hashRecord);
|
||||
|
||||
const char* name = nullptr;
|
||||
uint32_t rva = 0u;
|
||||
|
||||
if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_GDATA32) {
|
||||
name = record->data.S_GDATA32.name;
|
||||
rva = imageSectionStream.ConvertSectionOffsetToRVA(
|
||||
record->data.S_GDATA32.section, record->data.S_GDATA32.offset);
|
||||
} else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_GTHREAD32) {
|
||||
name = record->data.S_GTHREAD32.name;
|
||||
rva = imageSectionStream.ConvertSectionOffsetToRVA(
|
||||
record->data.S_GTHREAD32.section, record->data.S_GTHREAD32.offset);
|
||||
} else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LDATA32) {
|
||||
name = record->data.S_LDATA32.name;
|
||||
rva = imageSectionStream.ConvertSectionOffsetToRVA(
|
||||
record->data.S_LDATA32.section, record->data.S_LDATA32.offset);
|
||||
} else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LTHREAD32) {
|
||||
name = record->data.S_LTHREAD32.name;
|
||||
rva = imageSectionStream.ConvertSectionOffsetToRVA(
|
||||
record->data.S_LTHREAD32.section, record->data.S_LTHREAD32.offset);
|
||||
}
|
||||
|
||||
if (rva == 0u)
|
||||
continue;
|
||||
if (!name || name[0] == '\0')
|
||||
continue;
|
||||
|
||||
result.symbols.push_back(PdbSymbol{QString::fromUtf8(name), rva});
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "[PDB] extractPdbSymbols:" << result.symbols.size() << "symbols from"
|
||||
<< result.moduleName;
|
||||
return result;
|
||||
}
|
||||
|
||||
// ── Public API: enumeratePdbTypes ──
|
||||
|
||||
QVector<PdbTypeInfo> enumeratePdbTypes(const QString& pdbPath, QString* errorMsg) {
|
||||
@@ -1126,6 +1238,11 @@ NodeTree importPdb(const QString& pdbPath, const QString& structFilter, QString*
|
||||
|
||||
namespace rcx {
|
||||
|
||||
PdbSymbolResult extractPdbSymbols(const QString&, QString* errorMsg) {
|
||||
if (errorMsg) *errorMsg = QStringLiteral("PDB import requires Windows");
|
||||
return {};
|
||||
}
|
||||
|
||||
QVector<PdbTypeInfo> enumeratePdbTypes(const QString&, QString* errorMsg) {
|
||||
if (errorMsg) *errorMsg = QStringLiteral("PDB import requires Windows");
|
||||
return {};
|
||||
|
||||
@@ -294,7 +294,7 @@ NodeTree importReclassXml(const QString& filePath, QString* errorMsg, int pointe
|
||||
|
||||
// Defer ref resolution if array references a class
|
||||
if (!arrayClassName.isEmpty()) {
|
||||
pendingRefs.append({arrId, arrayClassName});
|
||||
pendingRefs.push_back(PendingRef{arrId, arrayClassName});
|
||||
}
|
||||
|
||||
childOffset += nodeSize > 0 ? nodeSize : 0;
|
||||
@@ -321,7 +321,7 @@ NodeTree importReclassXml(const QString& filePath, QString* errorMsg, int pointe
|
||||
n.collapsed = true; // Start collapsed to avoid recursive expansion freeze
|
||||
int nodeIdx = tree.addNode(n);
|
||||
uint64_t nodeId = tree.nodes[nodeIdx].id;
|
||||
pendingRefs.append({nodeId, ptrClass});
|
||||
pendingRefs.push_back(PendingRef{nodeId, ptrClass});
|
||||
childOffset += nodeSize > 0 ? nodeSize : sizeForKind(kind);
|
||||
continue;
|
||||
}
|
||||
@@ -335,7 +335,7 @@ NodeTree importReclassXml(const QString& filePath, QString* errorMsg, int pointe
|
||||
if (!n.structTypeName.isEmpty()) {
|
||||
int nodeIdx = tree.addNode(n);
|
||||
uint64_t nodeId = tree.nodes[nodeIdx].id;
|
||||
pendingRefs.append({nodeId, n.structTypeName});
|
||||
pendingRefs.push_back(PendingRef{nodeId, n.structTypeName});
|
||||
} else {
|
||||
tree.addNode(n);
|
||||
}
|
||||
|
||||
@@ -200,10 +200,10 @@ struct Tokenizer {
|
||||
case '=': tk = TokKind::Equals; break;
|
||||
default: tk = TokKind::Other; break;
|
||||
}
|
||||
tokens.append({tk, QString(c), line});
|
||||
tokens.push_back(Token{tk, QString(c), line});
|
||||
pos++;
|
||||
}
|
||||
tokens.append({TokKind::Eof, {}, line});
|
||||
tokens.push_back(Token{TokKind::Eof, {}, line});
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -241,7 +241,7 @@ private:
|
||||
bool ok;
|
||||
int val = m.captured(1).toInt(&ok, 16);
|
||||
if (ok) {
|
||||
offsets.append({commentLine, val});
|
||||
offsets.push_back(LineOffset{commentLine, val});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -259,7 +259,7 @@ private:
|
||||
void parseIdent() {
|
||||
int start = pos;
|
||||
while (pos < src.size() && (src[pos].isLetterOrNumber() || src[pos] == '_')) pos++;
|
||||
tokens.append({TokKind::Ident, src.mid(start, pos - start), line});
|
||||
tokens.push_back(Token{TokKind::Ident, src.mid(start, pos - start), line});
|
||||
}
|
||||
|
||||
void parseNumber() {
|
||||
@@ -276,7 +276,7 @@ private:
|
||||
// Skip integer suffixes (U, L, LL, ULL, etc.)
|
||||
while (pos < src.size() && (src[pos] == 'u' || src[pos] == 'U' ||
|
||||
src[pos] == 'l' || src[pos] == 'L')) pos++;
|
||||
tokens.append({TokKind::Number, src.mid(start, pos - start), line});
|
||||
tokens.push_back(Token{TokKind::Number, src.mid(start, pos - start), line});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1034,7 +1034,7 @@ struct Parser {
|
||||
}
|
||||
}
|
||||
|
||||
ps.enumValues.append({memberName, memberValue});
|
||||
ps.enumValues.emplaceBack(memberName, memberValue);
|
||||
nextValue = memberValue + 1;
|
||||
|
||||
// Skip comma between members
|
||||
@@ -1312,7 +1312,7 @@ static void buildFields(BuildContext& ctx, uint64_t parentId, int baseOffset,
|
||||
|
||||
if (!field.pointerTarget.isEmpty() &&
|
||||
field.pointerTarget != QStringLiteral("void")) {
|
||||
ctx.pendingRefs.append({nodeId, field.pointerTarget});
|
||||
ctx.pendingRefs.push_back(PendingRef{nodeId, field.pointerTarget});
|
||||
}
|
||||
|
||||
computedOffset = fieldOffset + ctx.ptrSize;
|
||||
@@ -1342,7 +1342,7 @@ static void buildFields(BuildContext& ctx, uint64_t parentId, int baseOffset,
|
||||
n.offset = fieldOffset;
|
||||
int nodeIdx = ctx.tree.addNode(n);
|
||||
uint64_t nodeId = ctx.tree.nodes[nodeIdx].id;
|
||||
ctx.pendingRefs.append({nodeId, field.typeName});
|
||||
ctx.pendingRefs.push_back(PendingRef{nodeId, field.typeName});
|
||||
computedOffset = fieldOffset + elemSize;
|
||||
}
|
||||
continue;
|
||||
@@ -1461,7 +1461,7 @@ static void buildFields(BuildContext& ctx, uint64_t parentId, int baseOffset,
|
||||
|
||||
int nodeIdx = ctx.tree.addNode(n);
|
||||
uint64_t nodeId = ctx.tree.nodes[nodeIdx].id;
|
||||
ctx.pendingRefs.append({nodeId, field.typeName});
|
||||
ctx.pendingRefs.push_back(PendingRef{nodeId, field.typeName});
|
||||
if (elemSize > 0)
|
||||
computedOffset = fieldOffset + totalElements * elemSize;
|
||||
continue;
|
||||
@@ -1477,7 +1477,7 @@ static void buildFields(BuildContext& ctx, uint64_t parentId, int baseOffset,
|
||||
|
||||
int nodeIdx = ctx.tree.addNode(n);
|
||||
uint64_t nodeId = ctx.tree.nodes[nodeIdx].id;
|
||||
ctx.pendingRefs.append({nodeId, field.typeName});
|
||||
ctx.pendingRefs.push_back(PendingRef{nodeId, field.typeName});
|
||||
if (elemSize > 0)
|
||||
computedOffset = fieldOffset + elemSize;
|
||||
continue;
|
||||
|
||||
83
src/main.cpp
83
src/main.cpp
@@ -570,6 +570,9 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) {
|
||||
m_titleBar->applyTheme(ThemeManager::instance().current());
|
||||
setMenuWidget(m_titleBar);
|
||||
m_menuBar = m_titleBar->menuBar();
|
||||
#ifdef __linux__
|
||||
m_menuBar->setNativeMenuBar(false);
|
||||
#endif
|
||||
#else
|
||||
setWindowTitle(QStringLiteral("Reclass"));
|
||||
setUnifiedTitleAndToolBarOnMac(true);
|
||||
@@ -3640,7 +3643,7 @@ void MainWindow::importPdb() {
|
||||
QVector<QPair<QString, uint32_t>> symPairs;
|
||||
symPairs.reserve(symResult.symbols.size());
|
||||
for (const auto& s : symResult.symbols)
|
||||
symPairs.append({s.name, s.rva});
|
||||
symPairs.emplaceBack(s.name, s.rva);
|
||||
int symCount = rcx::SymbolStore::instance().addModule(
|
||||
symResult.moduleName, pdbPath, symPairs);
|
||||
if (symCount > 0)
|
||||
@@ -4207,7 +4210,7 @@ void MainWindow::createWorkspaceDock() {
|
||||
const auto& nd = m_tabs[dk].doc->tree.nodes[ni];
|
||||
QString tn = nd.structTypeName.isEmpty() ? nd.name : nd.structTypeName;
|
||||
if (tn.isEmpty()) tn = QStringLiteral("(unnamed)");
|
||||
items.append({sid, dk, ni, nd.resolvedClassKeyword(), tn});
|
||||
items.push_back(SelItem{sid, dk, ni, nd.resolvedClassKeyword(), tn});
|
||||
}
|
||||
if (items.isEmpty()) return;
|
||||
|
||||
@@ -4888,7 +4891,7 @@ void MainWindow::createSymbolsDock() {
|
||||
QVector<QPair<QString, uint32_t>> pairs;
|
||||
pairs.reserve(result.symbols.size());
|
||||
for (const auto& s : result.symbols)
|
||||
pairs.append({s.name, s.rva});
|
||||
pairs.emplaceBack(s.name, s.rva);
|
||||
int count = rcx::SymbolStore::instance().addModule(
|
||||
result.moduleName, pdbPath, pairs);
|
||||
setAppStatus(QStringLiteral("Loaded %1 symbols for %2").arg(count).arg(name));
|
||||
@@ -4981,7 +4984,7 @@ void MainWindow::createSymbolsDock() {
|
||||
QVector<QPair<QString, uint32_t>> pairs;
|
||||
pairs.reserve(result.symbols.size());
|
||||
for (const auto& s : result.symbols)
|
||||
pairs.append({s.name, s.rva});
|
||||
pairs.emplaceBack(s.name, s.rva);
|
||||
int count = rcx::SymbolStore::instance().addModule(
|
||||
result.moduleName, localPdb, pairs);
|
||||
setAppStatus(QStringLiteral("Loaded %1 symbols for %2 (local)")
|
||||
@@ -5017,7 +5020,7 @@ void MainWindow::createSymbolsDock() {
|
||||
QVector<QPair<QString, uint32_t>> pairs;
|
||||
pairs.reserve(result.symbols.size());
|
||||
for (const auto& s : result.symbols)
|
||||
pairs.append({s.name, s.rva});
|
||||
pairs.emplaceBack(s.name, s.rva);
|
||||
int count = rcx::SymbolStore::instance().addModule(
|
||||
result.moduleName, localPath, pairs);
|
||||
setAppStatus(QStringLiteral("Loaded %1 symbols for %2")
|
||||
@@ -5042,7 +5045,7 @@ void MainWindow::createSymbolsDock() {
|
||||
QVector<QPair<QString, uint32_t>> pairs;
|
||||
pairs.reserve(result.symbols.size());
|
||||
for (const auto& s : result.symbols)
|
||||
pairs.append({s.name, s.rva});
|
||||
pairs.emplaceBack(s.name, s.rva);
|
||||
int count = rcx::SymbolStore::instance().addModule(
|
||||
result.moduleName, cached, pairs);
|
||||
setAppStatus(QStringLiteral("Loaded %1 symbols for %2 (cached)")
|
||||
@@ -5073,7 +5076,7 @@ void MainWindow::createSymbolsDock() {
|
||||
QVector<QPair<QString, uint32_t>> pairs;
|
||||
pairs.reserve(result.symbols.size());
|
||||
for (const auto& s : result.symbols)
|
||||
pairs.append({s.name, s.rva});
|
||||
pairs.emplaceBack(s.name, s.rva);
|
||||
int count = rcx::SymbolStore::instance().addModule(
|
||||
result.moduleName, path, pairs);
|
||||
setAppStatus(QStringLiteral("Loaded %1 symbols for %2")
|
||||
@@ -5159,7 +5162,7 @@ void MainWindow::createSymbolsDock() {
|
||||
m_symbolsProxy->setRecursiveFilteringEnabled(true);
|
||||
|
||||
m_symbolsTree->setModel(m_symbolsProxy);
|
||||
m_symbolsTree->setExpandsOnDoubleClick(true);
|
||||
m_symbolsTree->setExpandsOnDoubleClick(false);
|
||||
styleTree(m_symbolsTree);
|
||||
|
||||
// Debounced search
|
||||
@@ -5199,12 +5202,57 @@ void MainWindow::createSymbolsDock() {
|
||||
child->setData(moduleName, Qt::UserRole);
|
||||
child->setData(sym.first, Qt::UserRole + 1);
|
||||
child->setData(sym.second, Qt::UserRole + 2);
|
||||
static const QIcon symIcon(":/vsicons/symbol-method.svg");
|
||||
child->setIcon(symIcon);
|
||||
item->appendRow(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Double-click symbol → navigate to moduleBase + RVA
|
||||
connect(m_symbolsTree, &QTreeView::doubleClicked, this, [this](const QModelIndex& proxyIdx) {
|
||||
QModelIndex srcIdx = m_symbolsProxy->mapToSource(proxyIdx);
|
||||
auto* item = m_symbolsModel->itemFromIndex(srcIdx);
|
||||
if (!item) return;
|
||||
|
||||
// Module-level: toggle expand
|
||||
if (!item->parent()) {
|
||||
if (m_symbolsTree->isExpanded(proxyIdx))
|
||||
m_symbolsTree->collapse(proxyIdx);
|
||||
else
|
||||
m_symbolsTree->expand(proxyIdx);
|
||||
return;
|
||||
}
|
||||
|
||||
// Symbol-level: navigate to moduleBase + RVA
|
||||
QString moduleName = item->data(Qt::UserRole).toString();
|
||||
uint32_t rva = item->data(Qt::UserRole + 1).toUInt();
|
||||
|
||||
auto* ctrl = activeController();
|
||||
if (!ctrl || !ctrl->document()->provider) return;
|
||||
|
||||
uint64_t moduleBase = ctrl->document()->provider->symbolToAddress(moduleName);
|
||||
if (moduleBase == 0)
|
||||
moduleBase = ctrl->document()->provider->symbolToAddress(moduleName + QStringLiteral(".dll"));
|
||||
if (moduleBase == 0)
|
||||
moduleBase = ctrl->document()->provider->symbolToAddress(moduleName + QStringLiteral(".exe"));
|
||||
if (moduleBase == 0)
|
||||
moduleBase = ctrl->document()->provider->symbolToAddress(moduleName + QStringLiteral(".sys"));
|
||||
if (moduleBase == 0) return;
|
||||
|
||||
uint64_t addr = moduleBase + rva;
|
||||
ctrl->document()->tree.baseAddress = addr;
|
||||
ctrl->document()->tree.baseAddressFormula.clear();
|
||||
ctrl->resetChangeTracking();
|
||||
ctrl->refresh();
|
||||
|
||||
QString symName = item->data(Qt::UserRole + 2).toString();
|
||||
setAppStatus(QStringLiteral("Navigated to %1!%2 (0x%3)")
|
||||
.arg(moduleName, symName)
|
||||
.arg(addr, 0, 16));
|
||||
});
|
||||
|
||||
// Context menu for symbols
|
||||
m_symbolsTree->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(m_symbolsTree, &QWidget::customContextMenuRequested, this, [this](const QPoint& pos) {
|
||||
@@ -5288,7 +5336,8 @@ void MainWindow::rebuildSymbolsModel() {
|
||||
if (!set) continue;
|
||||
|
||||
int count = set->nameToRva.size();
|
||||
auto* moduleItem = new QStandardItem(
|
||||
static const QIcon modIcon(":/vsicons/symbol-structure.svg");
|
||||
auto* moduleItem = new QStandardItem(modIcon,
|
||||
QStringLiteral("%1 (%2 symbols)").arg(moduleName).arg(count));
|
||||
moduleItem->setData(moduleName, Qt::UserRole);
|
||||
moduleItem->setToolTip(set->pdbPath);
|
||||
@@ -5310,8 +5359,9 @@ void MainWindow::rebuildModulesModel() {
|
||||
auto modules = ctrl->document()->provider->enumerateModules();
|
||||
if (modules.isEmpty()) return;
|
||||
|
||||
static const QIcon modIcon(":/vsicons/symbol-structure.svg");
|
||||
for (const auto& mod : modules) {
|
||||
auto* item = new QStandardItem(
|
||||
auto* item = new QStandardItem(modIcon,
|
||||
QStringLiteral("%1 [0x%2] (%3 KB)")
|
||||
.arg(mod.name)
|
||||
.arg(mod.base, 0, 16)
|
||||
@@ -5363,7 +5413,7 @@ void MainWindow::downloadSymbolsForProcess() {
|
||||
QVector<QPair<QString, uint32_t>> pairs;
|
||||
pairs.reserve(result.symbols.size());
|
||||
for (const auto& s : result.symbols)
|
||||
pairs.append({s.name, s.rva});
|
||||
pairs.emplaceBack(s.name, s.rva);
|
||||
int count = rcx::SymbolStore::instance().addModule(
|
||||
result.moduleName, localPath, pairs);
|
||||
setAppStatus(QStringLiteral("Loaded %1 symbols for %2")
|
||||
@@ -5409,7 +5459,7 @@ void MainWindow::downloadSymbolsForProcess() {
|
||||
QVector<QPair<QString, uint32_t>> pairs;
|
||||
pairs.reserve(result.symbols.size());
|
||||
for (const auto& s : result.symbols)
|
||||
pairs.append({s.name, s.rva});
|
||||
pairs.emplaceBack(s.name, s.rva);
|
||||
int count = store.addModule(result.moduleName, localPdb, pairs);
|
||||
setAppStatus(QStringLiteral("Loaded %1 symbols for %2 (local)")
|
||||
.arg(count).arg(mod.name));
|
||||
@@ -5433,7 +5483,7 @@ void MainWindow::downloadSymbolsForProcess() {
|
||||
QVector<QPair<QString, uint32_t>> pairs;
|
||||
pairs.reserve(result.symbols.size());
|
||||
for (const auto& s : result.symbols)
|
||||
pairs.append({s.name, s.rva});
|
||||
pairs.emplaceBack(s.name, s.rva);
|
||||
int count = store.addModule(result.moduleName, cached, pairs);
|
||||
setAppStatus(QStringLiteral("Loaded %1 symbols for %2 (cached)")
|
||||
.arg(count).arg(mod.name));
|
||||
@@ -5442,7 +5492,7 @@ void MainWindow::downloadSymbolsForProcess() {
|
||||
continue;
|
||||
}
|
||||
|
||||
pending.append({mod.name, mod.fullPath, mod.base, info});
|
||||
pending.push_back(PendingModule{mod.name, mod.fullPath, mod.base, info});
|
||||
}
|
||||
|
||||
rebuildSymbolsModel();
|
||||
@@ -5515,7 +5565,7 @@ void MainWindow::rebuildWorkspaceModelNow() {
|
||||
if (seenDocs.contains(tab.doc)) continue;
|
||||
seenDocs.insert(tab.doc);
|
||||
QString name = rootName(tab.doc->tree, tab.ctrl->viewRootId());
|
||||
tabs.append({ &tab.doc->tree, name, static_cast<void*>(it.key()) });
|
||||
tabs.push_back(rcx::TabInfo{ &tab.doc->tree, name, static_cast<void*>(it.key()) });
|
||||
}
|
||||
rcx::syncProjectExplorer(m_workspaceModel, tabs, m_pinnedIds);
|
||||
|
||||
@@ -5843,6 +5893,9 @@ int main(int argc, char* argv[]) {
|
||||
window.setWindowIcon(QIcon(":/icons/class.png"));
|
||||
|
||||
window.show();
|
||||
#ifdef __linux__
|
||||
window.showMaximized();
|
||||
#endif
|
||||
|
||||
// --screenshot <path>: open default project, grab window, save, exit
|
||||
{
|
||||
|
||||
@@ -121,7 +121,7 @@ void McpBridge::onNewConnection() {
|
||||
auto* pending = m_server->nextPendingConnection();
|
||||
if (!pending) return;
|
||||
|
||||
m_clients.append({pending, {}, false});
|
||||
m_clients.push_back(ClientState{pending, {}, false});
|
||||
|
||||
connect(pending, &QLocalSocket::readyRead,
|
||||
this, &McpBridge::onReadyRead);
|
||||
@@ -156,7 +156,7 @@ void McpBridge::onReadyRead() {
|
||||
if (line.isEmpty()) continue;
|
||||
|
||||
if (m_processing) {
|
||||
m_pendingRequests.append({sock, line});
|
||||
m_pendingRequests.push_back(PendingRequest{sock, line});
|
||||
continue;
|
||||
}
|
||||
m_processing = true;
|
||||
@@ -819,7 +819,7 @@ QJsonObject McpBridge::toolProjectState(const QJsonObject& args) {
|
||||
QJsonArray nodeArr;
|
||||
struct QueueEntry { uint64_t parentId; int depth; };
|
||||
QVector<QueueEntry> queue;
|
||||
queue.append({filterParentId, 0});
|
||||
queue.push_back(QueueEntry{filterParentId, 0});
|
||||
|
||||
int totalCount = 0; // total nodes that match depth filter
|
||||
int emitted = 0;
|
||||
@@ -839,13 +839,13 @@ QJsonObject McpBridge::toolProjectState(const QJsonObject& args) {
|
||||
if (totalCount <= offset) {
|
||||
// Still skipping — but enqueue children for counting
|
||||
if (entry.depth + 1 <= maxDepth)
|
||||
queue.append({n.id, entry.depth + 1});
|
||||
queue.push_back(QueueEntry{n.id, entry.depth + 1});
|
||||
continue;
|
||||
}
|
||||
if (emitted >= limit) {
|
||||
// Past limit — just keep counting total
|
||||
if (entry.depth + 1 <= maxDepth)
|
||||
queue.append({n.id, entry.depth + 1});
|
||||
queue.push_back(QueueEntry{n.id, entry.depth + 1});
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -875,7 +875,7 @@ QJsonObject McpBridge::toolProjectState(const QJsonObject& args) {
|
||||
|
||||
// Enqueue children if we haven't hit depth limit
|
||||
if (entry.depth + 1 <= maxDepth)
|
||||
queue.append({n.id, entry.depth + 1});
|
||||
queue.push_back(QueueEntry{n.id, entry.depth + 1});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1665,7 +1665,7 @@ static QVector<AddressRange> parseRegionsArg(const QJsonObject& args, QString* e
|
||||
if (errOut) *errOut = QStringLiteral("regions[%1]: end must be > start").arg(i);
|
||||
return {};
|
||||
}
|
||||
out.append({start, end});
|
||||
out.push_back(AddressRange{start, end});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,11 @@ void PluginManager::LoadPlugins()
|
||||
|
||||
for (const QFileInfo& fileInfo : files)
|
||||
{
|
||||
// Skip the remote-inject payload binary — it's not a plugin and
|
||||
// loading it (especially on Linux) spawns a rogue thread.
|
||||
if (fileInfo.baseName().startsWith("rcx_payload"))
|
||||
continue;
|
||||
|
||||
LoadPlugin(fileInfo.absoluteFilePath());
|
||||
}
|
||||
|
||||
@@ -83,7 +88,7 @@ bool PluginManager::LoadPlugin(const QString& path)
|
||||
qDebug() << "PluginManager: Loaded plugin:" << plugin->Name().c_str() << plugin->Version().c_str() << "by" << plugin->Author().c_str();
|
||||
|
||||
// Store plugin entry
|
||||
m_entries.append({library, plugin});
|
||||
m_entries.push_back(PluginEntry{library, plugin});
|
||||
m_plugins.append(plugin);
|
||||
|
||||
// Auto-register providers in global registry
|
||||
|
||||
@@ -171,8 +171,8 @@ private:
|
||||
for (const auto& path : s.value("recentFiles").toStringList()) {
|
||||
QFileInfo fi(path);
|
||||
if (!fi.exists()) continue;
|
||||
m_all.append({fi.absoluteFilePath(), fi.fileName(), fi.absolutePath(),
|
||||
fi.lastModified(), false});
|
||||
m_all.push_back(Entry{fi.absoluteFilePath(), fi.fileName(), fi.absolutePath(),
|
||||
fi.lastModified(), false});
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
QDir exDir(QDir::cleanPath(QCoreApplication::applicationDirPath() + "/../Resources/examples"));
|
||||
@@ -180,8 +180,8 @@ private:
|
||||
QDir exDir(QCoreApplication::applicationDirPath() + "/examples");
|
||||
#endif
|
||||
for (const auto& fn : exDir.entryList({"*.rcx"}, QDir::Files, QDir::Name))
|
||||
m_all.append({exDir.absoluteFilePath(fn), fn, exDir.absolutePath(),
|
||||
QFileInfo(exDir.filePath(fn)).lastModified(), true});
|
||||
m_all.push_back(Entry{exDir.absoluteFilePath(fn), fn, exDir.absolutePath(),
|
||||
QFileInfo(exDir.filePath(fn)).lastModified(), true});
|
||||
}
|
||||
|
||||
void buildGroups() {
|
||||
@@ -207,7 +207,7 @@ private:
|
||||
static const char* names[] = {"Today","Yesterday","This week","This month","Older","Examples"};
|
||||
m_groups.clear();
|
||||
for (int i = 0; i < 6; i++)
|
||||
if (!bk[i].isEmpty()) m_groups.append({names[i], true, bk[i]});
|
||||
if (!bk[i].isEmpty()) m_groups.push_back(Group{names[i], true, bk[i]});
|
||||
m_scrollY = 0;
|
||||
}
|
||||
|
||||
@@ -223,13 +223,11 @@ private:
|
||||
{":/vsicons/debug.svg", "Import PDB", "Import types from a .pdb symbol file"}
|
||||
};
|
||||
|
||||
const int N = 5, CH = 84, R = 6, panelH = N * CH;
|
||||
const int N = 5, CH = 84, panelH = N * CH;
|
||||
|
||||
// Rounded panel background
|
||||
QPainterPath clip;
|
||||
clip.addRoundedRect(QRectF(x, y, w, panelH), R, R);
|
||||
// Sharp-cornered panel background
|
||||
p.save();
|
||||
p.setClipPath(clip);
|
||||
p.setClipRect(QRectF(x, y, w, panelH));
|
||||
p.fillRect(x, y, w, panelH, m_t.background);
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
@@ -289,7 +287,7 @@ private:
|
||||
if (gi > 0) fy += 15;
|
||||
|
||||
// Group header
|
||||
m_grpRects.append({gi, QRectF(x, fy, w, 28)});
|
||||
m_grpRects.emplaceBack(gi, QRectF(x, fy, w, 28));
|
||||
p.setPen(Qt::NoPen); p.setBrush(m_t.text);
|
||||
int triX = x + 8, triY = fy + 11;
|
||||
QPolygonF tri;
|
||||
@@ -307,7 +305,7 @@ private:
|
||||
for (int ei : g.entries) {
|
||||
auto& e = m_filtered[ei];
|
||||
QRectF er(x, fy, w, 52);
|
||||
m_entRects.append({ei, er});
|
||||
m_entRects.emplaceBack(ei, er);
|
||||
if (m_hz == HZ_Entry && m_hi == ei) p.fillRect(er, m_t.hover);
|
||||
|
||||
drawIcon(p, e.isExample ? ":/vsicons/book.svg" : ":/vsicons/symbol-structure.svg",
|
||||
|
||||
@@ -31,7 +31,7 @@ int SymbolStore::addModule(const QString& moduleName, const QString& pdbPath,
|
||||
if (set.nameToRva.contains(sym.first))
|
||||
continue;
|
||||
set.nameToRva.insert(sym.first, sym.second);
|
||||
set.rvaToName.append({sym.second, sym.first});
|
||||
set.rvaToName.emplaceBack(sym.second, sym.first);
|
||||
}
|
||||
|
||||
set.sortRvaIndex();
|
||||
|
||||
@@ -191,23 +191,26 @@ inline FeatureResult countFlagFeatures(uint32_t val,
|
||||
// ── Pointer feature checker ──
|
||||
|
||||
inline FeatureResult countPtrFeatures64(uint64_t val) {
|
||||
// Hard reject: common sentinel values are never pointers
|
||||
// Hard reject: common sentinel values
|
||||
if (val == 0 || val == 0xFFFFFFFFFFFFFFFFULL || val == 0x00000000FFFFFFFFULL)
|
||||
return {0, 6};
|
||||
return {0, 5};
|
||||
|
||||
int passed = 0, checked = 6;
|
||||
// Feature 1: canonical 48-bit address (sign-extended from bit 47)
|
||||
passed += (val <= 0x00007FFFFFFFFFFFULL
|
||||
|| val >= 0xFFFF800000000000ULL) ? 1 : 0;
|
||||
// Feature 2: aligned to 8 (heap/vtable allocations)
|
||||
// Hard reject: non-canonical address — impossible to dereference on x64
|
||||
// User-mode: 0x0000000000000000 – 0x00007FFFFFFFFFFF
|
||||
// Kernel: 0xFFFF800000000000 – 0xFFFFFFFFFFFFFFFF
|
||||
if (val > 0x00007FFFFFFFFFFFULL && val < 0xFFFF800000000000ULL)
|
||||
return {0, 5};
|
||||
|
||||
int passed = 0, checked = 5;
|
||||
// Feature 1: aligned to 8 (heap/vtable allocations)
|
||||
passed += ((val & 7) == 0) ? 1 : 0;
|
||||
// Feature 3: above null guard pages (real addresses >= 64KB)
|
||||
// Feature 2: above null guard pages (real addresses >= 64KB)
|
||||
passed += (val >= 0x10000) ? 1 : 0;
|
||||
// Feature 4: has upper 32 bits (real 64-bit address, not a small constant)
|
||||
// Feature 3: has upper 32 bits (real 64-bit address, not a small constant)
|
||||
passed += ((val >> 32) != 0) ? 1 : 0;
|
||||
// Feature 5: above 4GB (in real 64-bit address space, not a 32-bit value)
|
||||
// Feature 4: above 4GB (in real 64-bit address space)
|
||||
passed += (val > 0x100000000ULL) ? 1 : 0;
|
||||
// Feature 6: user-mode address range (not kernel 0xFFFF800000000000+)
|
||||
// Feature 5: user-mode address range (not kernel)
|
||||
passed += (val < 0xFFFF800000000000ULL) ? 1 : 0;
|
||||
return {passed, checked};
|
||||
}
|
||||
@@ -289,13 +292,13 @@ struct Candidate {
|
||||
};
|
||||
|
||||
inline void addCandidate(QVector<Candidate>& out, NodeKind k, int score) {
|
||||
if (score >= 25) out.append({{k}, score});
|
||||
if (score >= 25) out.push_back(Candidate{{k}, score});
|
||||
}
|
||||
|
||||
inline void addSplitCandidate(QVector<Candidate>& out, NodeKind k, int count, int score) {
|
||||
if (score >= 25) {
|
||||
QVector<NodeKind> kinds(count, k);
|
||||
out.append({std::move(kinds), score});
|
||||
out.push_back(Candidate{std::move(kinds), score});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,32 +311,43 @@ inline void tryWhole8(const uint8_t* data, const InferHints& h, QVector<Candidat
|
||||
if (h.ptrSize == 8)
|
||||
addCandidate(out, NodeKind::Pointer64, featureScore(countPtrFeatures64(u64)));
|
||||
|
||||
// Double
|
||||
// Double — rare in RE work; require strong evidence
|
||||
{
|
||||
double d; std::memcpy(&d, data, 8);
|
||||
uint64_t exp = (u64 >> 52) & 0x7FF;
|
||||
int passed = 0, checked = 3;
|
||||
passed += std::isfinite(d) ? 1 : 0;
|
||||
passed += (exp > 0 || (u64 & 0x7FFFFFFFFFFFFFFFull) == 0) ? 1 : 0;
|
||||
double ad = std::fabs(d);
|
||||
passed += (d == 0.0 || (ad >= 1e-6 && ad <= 1e12)) ? 1 : 0;
|
||||
addCandidate(out, NodeKind::Double, featureScore({passed, checked}));
|
||||
uint64_t mantissa = u64 & 0x000FFFFFFFFFFFFFull;
|
||||
// Hard reject: outside plausible range [1e-6, 1e7] (matches float checker)
|
||||
bool inRange = (d == 0.0 || (ad >= 1e-6 && ad <= 1e7));
|
||||
// Hard reject: lower 32 zero with non-zero mantissa (two 32-bit fields)
|
||||
bool splitField = ((u64 & 0xFFFFFFFF) == 0 && mantissa != 0);
|
||||
if (inRange && !splitField) {
|
||||
uint64_t exp = (u64 >> 52) & 0x7FF;
|
||||
int passed = 0, checked = 4;
|
||||
// Feature 1: finite
|
||||
passed += std::isfinite(d) ? 1 : 0;
|
||||
// Feature 2: non-denormal
|
||||
passed += (exp > 0 || (u64 & 0x7FFFFFFFFFFFFFFFull) == 0) ? 1 : 0;
|
||||
// Feature 3: has fractional part or is a small special value
|
||||
double ip; double frac = std::fabs(std::modf(d, &ip));
|
||||
passed += (frac > 0.001 || ad <= 1.0) ? 1 : 0;
|
||||
// Feature 4: not a large exact integer (likely reinterpreted binary data)
|
||||
passed += !(ad > 1000.0 && frac < 0.001) ? 1 : 0;
|
||||
addCandidate(out, NodeKind::Double, featureScore({passed, checked}));
|
||||
}
|
||||
}
|
||||
|
||||
// UTF8
|
||||
addCandidate(out, NodeKind::UTF8, featureScore(countStringFeatures(data, 8)));
|
||||
|
||||
// UInt64 / Int64
|
||||
{
|
||||
int passed = 0, checked = 4;
|
||||
// Feature 1: fits in 32 bits (small constant, not an address)
|
||||
passed += (u64 <= 0xFFFFFFFFull) ? 1 : 0;
|
||||
// Feature 2: upper 32 bits are zero (confirms it's a small value, not a pointer)
|
||||
passed += ((u64 >> 32) == 0) ? 1 : 0;
|
||||
// Feature 3: non-zero
|
||||
passed += (u64 != 0) ? 1 : 0;
|
||||
// Feature 4: monotonic or very small (< 0x10000)
|
||||
passed += (h.monotonic || u64 < 0x10000) ? 1 : 0;
|
||||
// UInt64 / Int64 — only meaningful when value exceeds 32-bit range
|
||||
if ((u64 >> 32) != 0) {
|
||||
int passed = 0, checked = 3;
|
||||
// Feature 1: non-zero (always true after guard)
|
||||
passed += 1;
|
||||
// Feature 2: reasonable magnitude (below kernel range)
|
||||
passed += (u64 < 0x0000FFFFFFFFFFFFULL) ? 1 : 0;
|
||||
// Feature 3: monotonic or page-aligned
|
||||
passed += (h.monotonic || (u64 & 0xFFF) == 0) ? 1 : 0;
|
||||
addCandidate(out, NodeKind::UInt64, featureScore({passed, checked}));
|
||||
}
|
||||
}
|
||||
@@ -467,7 +481,7 @@ inline QVector<TypeSuggestion> pruneAndRank(QVector<Candidate>& cands, int maxRe
|
||||
for (const auto& c : deduped) {
|
||||
int str = strengthFromScore(c.score);
|
||||
if (str > 0)
|
||||
result.append({c.kinds, c.score, str});
|
||||
result.push_back(TypeSuggestion{c.kinds, c.score, str});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1030,7 +1030,7 @@ void TypeSelectorPopup::applyFilter(const QString& text) {
|
||||
else if (t.category == TypeEntry::CatEnum) enumCount++;
|
||||
else typeCount++;
|
||||
if (catAllowed(t))
|
||||
scored.append({i, sc, std::move(pos)});
|
||||
scored.push_back(Scored{i, sc, std::move(pos)});
|
||||
}
|
||||
std::sort(scored.begin(), scored.end(),
|
||||
[](const Scored& a, const Scored& b) { return a.score > b.score; });
|
||||
|
||||
@@ -108,9 +108,9 @@ inline void buildProjectExplorer(QStandardItemModel* model,
|
||||
const Node& n = tab.tree->nodes[idx];
|
||||
if (n.kind != NodeKind::Struct) continue;
|
||||
if (n.resolvedClassKeyword() == QStringLiteral("enum"))
|
||||
enums.append({&n, tab.subPtr, tab.tree});
|
||||
enums.push_back(Entry{&n, tab.subPtr, tab.tree});
|
||||
else
|
||||
types.append({&n, tab.subPtr, tab.tree});
|
||||
types.push_back(Entry{&n, tab.subPtr, tab.tree});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ inline void syncProjectExplorer(QStandardItemModel* model,
|
||||
const Node& n = tab.tree->nodes[idx];
|
||||
if (n.kind != NodeKind::Struct) continue;
|
||||
bool ie = n.resolvedClassKeyword() == QStringLiteral("enum");
|
||||
desired.append({n.id, &n, tab.subPtr, tab.tree, ie});
|
||||
desired.push_back(Entry{n.id, &n, tab.subPtr, tab.tree, ie});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user