mirror of
https://github.com/NohamR/Reclass.git
synced 2026-05-10 19:59:21 +00:00
fix: C++ generator bitfields, sizeof placement, Ctrl+F search, view sync
- Generator emits proper bitfield members instead of padding stubs - Named bitfield structs (MitigationFlagsValues etc) now converted by parser - sizeof comment moved from top to closing brace (}; // sizeof 0x80) - C/C++ view syncs with workspace double-click and controller navigation - Ctrl+F incremental search in C++ code view (Enter=next, Escape=close) - Workspace dock resizable via 1px drag handle separator - Regenerated Vergilius_25H2.rcx with all fixes (61 named bitfield containers)
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -101,7 +101,9 @@ static void emitStruct(GenContext& ctx, uint64_t structId);
|
|||||||
|
|
||||||
static const QChar kCommentMarker = QChar(0x01);
|
static const QChar kCommentMarker = QChar(0x01);
|
||||||
|
|
||||||
static QString offsetComment(int offset) {
|
static QString offsetComment(int offset, bool isSizeof = false) {
|
||||||
|
if (isSizeof)
|
||||||
|
return QString(kCommentMarker) + QStringLiteral("// sizeof 0x%1").arg(QString::number(offset, 16).toUpper());
|
||||||
return QString(kCommentMarker) + QStringLiteral("// 0x%1").arg(QString::number(offset, 16).toUpper());
|
return QString(kCommentMarker) + QStringLiteral("// 0x%1").arg(QString::number(offset, 16).toUpper());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,6 +228,27 @@ static void emitStructBody(GenContext& ctx, uint64_t structId,
|
|||||||
|
|
||||||
// Emit the field
|
// Emit the field
|
||||||
if (child.kind == NodeKind::Struct) {
|
if (child.kind == NodeKind::Struct) {
|
||||||
|
// Bitfield container — emit inline bitfield members
|
||||||
|
if (child.classKeyword == QStringLiteral("bitfield")
|
||||||
|
&& !child.bitfieldMembers.isEmpty()) {
|
||||||
|
QString bfType = ctx.cType(child.elementKind);
|
||||||
|
if (bfType.isEmpty()) bfType = QStringLiteral("uint32_t");
|
||||||
|
QString fieldName = child.name.isEmpty()
|
||||||
|
? QString() : QStringLiteral(" ") + sanitizeIdent(child.name);
|
||||||
|
ctx.output += ind + QStringLiteral("struct\n");
|
||||||
|
ctx.output += ind + QStringLiteral("{\n");
|
||||||
|
QString bfInd = indent(depth + 1);
|
||||||
|
for (const auto& m : child.bitfieldMembers) {
|
||||||
|
ctx.output += bfInd + bfType + QStringLiteral(" ")
|
||||||
|
+ sanitizeIdent(m.name) + QStringLiteral(" : ")
|
||||||
|
+ QString::number(m.bitWidth) + QStringLiteral(";")
|
||||||
|
+ offsetComment(baseOffset + child.offset)
|
||||||
|
+ QStringLiteral("\n");
|
||||||
|
}
|
||||||
|
ctx.output += ind + QStringLiteral("}") + fieldName + QStringLiteral(";")
|
||||||
|
+ offsetComment(baseOffset + child.offset) + QStringLiteral("\n");
|
||||||
|
} else {
|
||||||
|
|
||||||
bool isAnonymous = child.structTypeName.isEmpty();
|
bool isAnonymous = child.structTypeName.isEmpty();
|
||||||
|
|
||||||
if (isAnonymous) {
|
if (isAnonymous) {
|
||||||
@@ -251,6 +274,7 @@ static void emitStructBody(GenContext& ctx, uint64_t structId,
|
|||||||
+ QStringLiteral(" ") + fieldName + QStringLiteral(";")
|
+ QStringLiteral(" ") + fieldName + QStringLiteral(";")
|
||||||
+ offsetComment(baseOffset + child.offset) + QStringLiteral("\n");
|
+ offsetComment(baseOffset + child.offset) + QStringLiteral("\n");
|
||||||
}
|
}
|
||||||
|
} // end bitfield else
|
||||||
} else if (child.kind == NodeKind::Array) {
|
} else if (child.kind == NodeKind::Array) {
|
||||||
QVector<int> arrayKids = ctx.childMap.value(child.id);
|
QVector<int> arrayKids = ctx.childMap.value(child.id);
|
||||||
bool hasStructChild = false;
|
bool hasStructChild = false;
|
||||||
@@ -338,14 +362,13 @@ static void emitStruct(GenContext& ctx, uint64_t structId) {
|
|||||||
|
|
||||||
if (kw == QStringLiteral("enum")) kw = QStringLiteral("struct");
|
if (kw == QStringLiteral("enum")) kw = QStringLiteral("struct");
|
||||||
|
|
||||||
// Size comment (Vergilius-style)
|
|
||||||
ctx.output += QStringLiteral("//0x%1 bytes (sizeof)\n")
|
|
||||||
.arg(QString::number(structSize, 16).toUpper());
|
|
||||||
ctx.output += kw + QStringLiteral(" ") + typeName + QStringLiteral("\n{\n");
|
ctx.output += kw + QStringLiteral(" ") + typeName + QStringLiteral("\n{\n");
|
||||||
|
|
||||||
emitStructBody(ctx, structId, kw == QStringLiteral("union"), 1, 0);
|
emitStructBody(ctx, structId, kw == QStringLiteral("union"), 1, 0);
|
||||||
|
|
||||||
ctx.output += QStringLiteral("};\n");
|
ctx.output += QStringLiteral("};")
|
||||||
|
+ offsetComment(structSize, true)
|
||||||
|
+ QStringLiteral("\n");
|
||||||
if (ctx.emitAsserts)
|
if (ctx.emitAsserts)
|
||||||
ctx.output += QStringLiteral("static_assert(sizeof(%1) == 0x%2, \"Size mismatch for %1\");\n")
|
ctx.output += QStringLiteral("static_assert(sizeof(%1) == 0x%2, \"Size mismatch for %1\");\n")
|
||||||
.arg(typeName)
|
.arg(typeName)
|
||||||
|
|||||||
99
src/main.cpp
99
src/main.cpp
@@ -251,9 +251,9 @@ public:
|
|||||||
// Kill the 1px frame margin Fusion reserves around QMenu contents
|
// Kill the 1px frame margin Fusion reserves around QMenu contents
|
||||||
if (metric == PM_MenuPanelWidth)
|
if (metric == PM_MenuPanelWidth)
|
||||||
return 0;
|
return 0;
|
||||||
// Kill the separator between dock widgets / central widget
|
// Thin draggable separator between dock widgets / central widget
|
||||||
if (metric == PM_DockWidgetSeparatorExtent)
|
if (metric == PM_DockWidgetSeparatorExtent)
|
||||||
return 0;
|
return 1;
|
||||||
return QProxyStyle::pixelMetric(metric, opt, w);
|
return QProxyStyle::pixelMetric(metric, opt, w);
|
||||||
}
|
}
|
||||||
void drawPrimitive(PrimitiveElement elem, const QStyleOption* opt,
|
void drawPrimitive(PrimitiveElement elem, const QStyleOption* opt,
|
||||||
@@ -1050,10 +1050,64 @@ MainWindow::SplitPane MainWindow::createSplitPane(TabState& tab) {
|
|||||||
QSettings("Reclass", "Reclass").value("relativeOffsets", true).toBool());
|
QSettings("Reclass", "Reclass").value("relativeOffsets", true).toBool());
|
||||||
pane.tabWidget->addTab(pane.editor, "Reclass"); // index 0
|
pane.tabWidget->addTab(pane.editor, "Reclass"); // index 0
|
||||||
|
|
||||||
// Create per-pane rendered C++ view
|
// Create per-pane rendered C++ view with find bar
|
||||||
|
pane.renderedContainer = new QWidget;
|
||||||
|
auto* rvLayout = new QVBoxLayout(pane.renderedContainer);
|
||||||
|
rvLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
rvLayout->setSpacing(0);
|
||||||
pane.rendered = new QsciScintilla;
|
pane.rendered = new QsciScintilla;
|
||||||
setupRenderedSci(pane.rendered);
|
setupRenderedSci(pane.rendered);
|
||||||
pane.tabWidget->addTab(pane.rendered, "C/C++"); // index 1
|
rvLayout->addWidget(pane.rendered);
|
||||||
|
|
||||||
|
// Find bar (hidden by default)
|
||||||
|
pane.findBar = new QLineEdit;
|
||||||
|
pane.findBar->setPlaceholderText("Find...");
|
||||||
|
pane.findBar->setVisible(false);
|
||||||
|
const auto& fbTheme = ThemeManager::instance().current();
|
||||||
|
pane.findBar->setStyleSheet(
|
||||||
|
QStringLiteral("QLineEdit { background: %1; color: %2; border: 1px solid %3;"
|
||||||
|
" padding: 4px 8px; font-size: 13px; }")
|
||||||
|
.arg(fbTheme.backgroundAlt.name())
|
||||||
|
.arg(fbTheme.text.name())
|
||||||
|
.arg(fbTheme.border.name()));
|
||||||
|
rvLayout->addWidget(pane.findBar);
|
||||||
|
|
||||||
|
// Ctrl+F to show find bar
|
||||||
|
QsciScintilla* sci = pane.rendered;
|
||||||
|
QLineEdit* fb = pane.findBar;
|
||||||
|
auto* findAction = new QAction(pane.renderedContainer);
|
||||||
|
findAction->setShortcut(QKeySequence::Find);
|
||||||
|
findAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
||||||
|
pane.renderedContainer->addAction(findAction);
|
||||||
|
connect(findAction, &QAction::triggered, fb, [fb, sci]() {
|
||||||
|
fb->setVisible(true);
|
||||||
|
fb->setFocus();
|
||||||
|
fb->selectAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Escape to hide find bar
|
||||||
|
auto* escAction = new QAction(fb);
|
||||||
|
escAction->setShortcut(QKeySequence(Qt::Key_Escape));
|
||||||
|
escAction->setShortcutContext(Qt::WidgetShortcut);
|
||||||
|
fb->addAction(escAction);
|
||||||
|
connect(escAction, &QAction::triggered, fb, [fb, sci]() {
|
||||||
|
fb->setVisible(false);
|
||||||
|
sci->setFocus();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Search on text change and Enter
|
||||||
|
connect(fb, &QLineEdit::textChanged, sci, [sci](const QString& text) {
|
||||||
|
if (text.isEmpty()) return;
|
||||||
|
sci->findFirst(text, false, false, false, true, true, 0, 0);
|
||||||
|
});
|
||||||
|
connect(fb, &QLineEdit::returnPressed, sci, [sci, fb]() {
|
||||||
|
QString text = fb->text();
|
||||||
|
if (text.isEmpty()) return;
|
||||||
|
if (!sci->findNext())
|
||||||
|
sci->findFirst(text, false, false, false, true, true, 0, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
pane.tabWidget->addTab(pane.renderedContainer, "C/C++"); // index 1
|
||||||
|
|
||||||
pane.tabWidget->setCurrentIndex(0);
|
pane.tabWidget->setCurrentIndex(0);
|
||||||
pane.viewMode = VM_Reclass;
|
pane.viewMode = VM_Reclass;
|
||||||
@@ -1668,7 +1722,7 @@ void MainWindow::toggleMcp() {
|
|||||||
void MainWindow::applyTheme(const Theme& theme) {
|
void MainWindow::applyTheme(const Theme& theme) {
|
||||||
applyGlobalTheme(theme);
|
applyGlobalTheme(theme);
|
||||||
|
|
||||||
// Separator killed via PM_DockWidgetSeparatorExtent in MenuBarStyle
|
// Dock separator is 1px via PM_DockWidgetSeparatorExtent in MenuBarStyle
|
||||||
|
|
||||||
// Custom title bar
|
// Custom title bar
|
||||||
m_titleBar->applyTheme(theme);
|
m_titleBar->applyTheme(theme);
|
||||||
@@ -2034,6 +2088,20 @@ void MainWindow::updateRenderedView(TabState& tab, SplitPane& pane) {
|
|||||||
rootId = findRootStructForNode(tab.doc->tree, selId);
|
rootId = findRootStructForNode(tab.doc->tree, selId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fall back to the controller's current view root (set by double-click / navigation)
|
||||||
|
if (rootId == 0)
|
||||||
|
rootId = findRootStructForNode(tab.doc->tree, tab.ctrl->viewRootId());
|
||||||
|
|
||||||
|
// Last resort: first root-level struct in the project
|
||||||
|
if (rootId == 0) {
|
||||||
|
for (const auto& n : tab.doc->tree.nodes) {
|
||||||
|
if (n.parentId == 0 && n.kind == rcx::NodeKind::Struct) {
|
||||||
|
rootId = n.id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Generate text
|
// Generate text
|
||||||
const QHash<NodeKind, QString>* aliases =
|
const QHash<NodeKind, QString>* aliases =
|
||||||
tab.doc->typeAliases.isEmpty() ? nullptr : &tab.doc->typeAliases;
|
tab.doc->typeAliases.isEmpty() ? nullptr : &tab.doc->typeAliases;
|
||||||
@@ -2717,19 +2785,32 @@ void MainWindow::createWorkspaceDock() {
|
|||||||
int ni = tree.indexOfId(structId);
|
int ni = tree.indexOfId(structId);
|
||||||
if (ni < 0) return;
|
if (ni < 0) return;
|
||||||
|
|
||||||
|
auto& tab = m_tabs[sub];
|
||||||
|
|
||||||
// Child member item: navigate to parent struct, then scroll to this member
|
// Child member item: navigate to parent struct, then scroll to this member
|
||||||
uint64_t parentId = tree.nodes[ni].parentId;
|
uint64_t parentId = tree.nodes[ni].parentId;
|
||||||
if (parentId != 0) {
|
if (parentId != 0) {
|
||||||
int pi = tree.indexOfId(parentId);
|
int pi = tree.indexOfId(parentId);
|
||||||
if (pi >= 0) tree.nodes[pi].collapsed = false;
|
if (pi >= 0) tree.nodes[pi].collapsed = false;
|
||||||
m_tabs[sub].ctrl->setViewRootId(parentId);
|
tab.ctrl->setViewRootId(parentId);
|
||||||
m_tabs[sub].ctrl->scrollToNodeId(structId);
|
tab.ctrl->scrollToNodeId(structId);
|
||||||
} else {
|
} else {
|
||||||
// Root type/enum: navigate directly
|
// Root type/enum: navigate directly
|
||||||
tree.nodes[ni].collapsed = false;
|
tree.nodes[ni].collapsed = false;
|
||||||
m_tabs[sub].ctrl->setViewRootId(structId);
|
tab.ctrl->setViewRootId(structId);
|
||||||
m_tabs[sub].ctrl->scrollToNodeId(structId);
|
tab.ctrl->scrollToNodeId(structId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If active pane is in C/C++ mode, refresh after navigation settles
|
||||||
|
QTimer::singleShot(0, this, [this, sub]() {
|
||||||
|
if (!m_tabs.contains(sub)) return;
|
||||||
|
auto& t = m_tabs[sub];
|
||||||
|
if (t.activePaneIdx >= 0 && t.activePaneIdx < t.panes.size()) {
|
||||||
|
auto& p = t.panes[t.activePaneIdx];
|
||||||
|
if (p.viewMode == VM_Rendered)
|
||||||
|
updateRenderedView(t, p);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,6 +96,8 @@ private:
|
|||||||
QTabWidget* tabWidget = nullptr;
|
QTabWidget* tabWidget = nullptr;
|
||||||
RcxEditor* editor = nullptr;
|
RcxEditor* editor = nullptr;
|
||||||
QsciScintilla* rendered = nullptr;
|
QsciScintilla* rendered = nullptr;
|
||||||
|
QLineEdit* findBar = nullptr;
|
||||||
|
QWidget* renderedContainer = nullptr;
|
||||||
ViewMode viewMode = VM_Reclass;
|
ViewMode viewMode = VM_Reclass;
|
||||||
uint64_t lastRenderedRootId = 0;
|
uint64_t lastRenderedRootId = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ private slots:
|
|||||||
// Header
|
// Header
|
||||||
QVERIFY(result.contains("#pragma once"));
|
QVERIFY(result.contains("#pragma once"));
|
||||||
|
|
||||||
// Size comment (Vergilius-style)
|
// Size comment on closing brace
|
||||||
QVERIFY(result.contains("//0x10 bytes (sizeof)"));
|
QVERIFY(result.contains("// sizeof 0x10"));
|
||||||
|
|
||||||
// Struct definition (brace on new line)
|
// Struct definition (brace on new line)
|
||||||
QVERIFY(result.contains("struct Player\n{"));
|
QVERIFY(result.contains("struct Player\n{"));
|
||||||
|
|||||||
@@ -556,13 +556,11 @@ def postprocess_bitfields(nodes):
|
|||||||
ids_to_remove = set()
|
ids_to_remove = set()
|
||||||
|
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
# Only process anonymous struct nodes (not unions, not named types)
|
# Process struct nodes (not unions, not already bitfields, not named types)
|
||||||
if node.get('kind') != 'Struct':
|
if node.get('kind') != 'Struct':
|
||||||
continue
|
continue
|
||||||
if node.get('classKeyword') in ('union', 'bitfield'):
|
if node.get('classKeyword') in ('union', 'bitfield'):
|
||||||
continue
|
continue
|
||||||
if node.get('name', '') != '':
|
|
||||||
continue
|
|
||||||
if node.get('structTypeName', ''):
|
if node.get('structTypeName', ''):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user