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 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());
|
||||
}
|
||||
|
||||
@@ -226,6 +228,27 @@ static void emitStructBody(GenContext& ctx, uint64_t structId,
|
||||
|
||||
// Emit the field
|
||||
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();
|
||||
|
||||
if (isAnonymous) {
|
||||
@@ -251,6 +274,7 @@ static void emitStructBody(GenContext& ctx, uint64_t structId,
|
||||
+ QStringLiteral(" ") + fieldName + QStringLiteral(";")
|
||||
+ offsetComment(baseOffset + child.offset) + QStringLiteral("\n");
|
||||
}
|
||||
} // end bitfield else
|
||||
} else if (child.kind == NodeKind::Array) {
|
||||
QVector<int> arrayKids = ctx.childMap.value(child.id);
|
||||
bool hasStructChild = false;
|
||||
@@ -338,14 +362,13 @@ static void emitStruct(GenContext& ctx, uint64_t structId) {
|
||||
|
||||
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");
|
||||
|
||||
emitStructBody(ctx, structId, kw == QStringLiteral("union"), 1, 0);
|
||||
|
||||
ctx.output += QStringLiteral("};\n");
|
||||
ctx.output += QStringLiteral("};")
|
||||
+ offsetComment(structSize, true)
|
||||
+ QStringLiteral("\n");
|
||||
if (ctx.emitAsserts)
|
||||
ctx.output += QStringLiteral("static_assert(sizeof(%1) == 0x%2, \"Size mismatch for %1\");\n")
|
||||
.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
|
||||
if (metric == PM_MenuPanelWidth)
|
||||
return 0;
|
||||
// Kill the separator between dock widgets / central widget
|
||||
// Thin draggable separator between dock widgets / central widget
|
||||
if (metric == PM_DockWidgetSeparatorExtent)
|
||||
return 0;
|
||||
return 1;
|
||||
return QProxyStyle::pixelMetric(metric, opt, w);
|
||||
}
|
||||
void drawPrimitive(PrimitiveElement elem, const QStyleOption* opt,
|
||||
@@ -1050,10 +1050,64 @@ MainWindow::SplitPane MainWindow::createSplitPane(TabState& tab) {
|
||||
QSettings("Reclass", "Reclass").value("relativeOffsets", true).toBool());
|
||||
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;
|
||||
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.viewMode = VM_Reclass;
|
||||
@@ -1668,7 +1722,7 @@ void MainWindow::toggleMcp() {
|
||||
void MainWindow::applyTheme(const Theme& theme) {
|
||||
applyGlobalTheme(theme);
|
||||
|
||||
// Separator killed via PM_DockWidgetSeparatorExtent in MenuBarStyle
|
||||
// Dock separator is 1px via PM_DockWidgetSeparatorExtent in MenuBarStyle
|
||||
|
||||
// Custom title bar
|
||||
m_titleBar->applyTheme(theme);
|
||||
@@ -2034,6 +2088,20 @@ void MainWindow::updateRenderedView(TabState& tab, SplitPane& pane) {
|
||||
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
|
||||
const QHash<NodeKind, QString>* aliases =
|
||||
tab.doc->typeAliases.isEmpty() ? nullptr : &tab.doc->typeAliases;
|
||||
@@ -2717,19 +2785,32 @@ void MainWindow::createWorkspaceDock() {
|
||||
int ni = tree.indexOfId(structId);
|
||||
if (ni < 0) return;
|
||||
|
||||
auto& tab = m_tabs[sub];
|
||||
|
||||
// Child member item: navigate to parent struct, then scroll to this member
|
||||
uint64_t parentId = tree.nodes[ni].parentId;
|
||||
if (parentId != 0) {
|
||||
int pi = tree.indexOfId(parentId);
|
||||
if (pi >= 0) tree.nodes[pi].collapsed = false;
|
||||
m_tabs[sub].ctrl->setViewRootId(parentId);
|
||||
m_tabs[sub].ctrl->scrollToNodeId(structId);
|
||||
tab.ctrl->setViewRootId(parentId);
|
||||
tab.ctrl->scrollToNodeId(structId);
|
||||
} else {
|
||||
// Root type/enum: navigate directly
|
||||
tree.nodes[ni].collapsed = false;
|
||||
m_tabs[sub].ctrl->setViewRootId(structId);
|
||||
m_tabs[sub].ctrl->scrollToNodeId(structId);
|
||||
tab.ctrl->setViewRootId(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;
|
||||
RcxEditor* editor = nullptr;
|
||||
QsciScintilla* rendered = nullptr;
|
||||
QLineEdit* findBar = nullptr;
|
||||
QWidget* renderedContainer = nullptr;
|
||||
ViewMode viewMode = VM_Reclass;
|
||||
uint64_t lastRenderedRootId = 0;
|
||||
};
|
||||
|
||||
@@ -56,8 +56,8 @@ private slots:
|
||||
// Header
|
||||
QVERIFY(result.contains("#pragma once"));
|
||||
|
||||
// Size comment (Vergilius-style)
|
||||
QVERIFY(result.contains("//0x10 bytes (sizeof)"));
|
||||
// Size comment on closing brace
|
||||
QVERIFY(result.contains("// sizeof 0x10"));
|
||||
|
||||
// Struct definition (brace on new line)
|
||||
QVERIFY(result.contains("struct Player\n{"));
|
||||
|
||||
@@ -556,13 +556,11 @@ def postprocess_bitfields(nodes):
|
||||
ids_to_remove = set()
|
||||
|
||||
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':
|
||||
continue
|
||||
if node.get('classKeyword') in ('union', 'bitfield'):
|
||||
continue
|
||||
if node.get('name', '') != '':
|
||||
continue
|
||||
if node.get('structTypeName', ''):
|
||||
continue
|
||||
|
||||
|
||||
Reference in New Issue
Block a user