yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
menu_inspector_panel.cc
Go to the documentation of this file.
2#include "util/i18n/tr.h"
3
4#include <filesystem>
5
6#include "absl/strings/ascii.h"
7#include "absl/strings/str_format.h"
11#include "core/project.h"
12
13namespace yaze::editor {
14
15namespace {
16
18 if (auto* project = ContentRegistry::Context::current_project()) {
19 if (!project->code_folder.empty()) {
20 return project->GetAbsolutePath(project->code_folder);
21 }
22 if (!project->filepath.empty()) {
23 return std::filesystem::path(project->filepath).parent_path().string();
24 }
25 }
26 return std::filesystem::current_path().string();
27}
28
29} // namespace
30
32 if (project_path_.empty()) {
33 project_path_ = DetermineInitialProjectPath();
34 }
35
36 if (!attempted_initial_load_ && !project_path_.empty()) {
39 }
40}
41
44 if (!registry_or.ok()) {
45 has_registry_ = false;
46 last_error_ = std::string(registry_or.status().message());
47 status_message_.clear();
50 return;
51 }
52
53 registry_ = std::move(registry_or.value());
54 has_registry_ = true;
55 last_error_.clear();
56 status_message_ = absl::StrFormat(
57 "Loaded %zu asm files, %zu bins, %zu draw routines, %zu components.",
58 registry_.asm_files.size(), registry_.bins.size(),
60
63}
64
65bool OracleMenuInspectorPanel::MatchesFilter(const std::string& value,
66 const std::string& filter) const {
67 if (filter.empty()) {
68 return true;
69 }
70 const std::string value_lower = absl::AsciiStrToLower(value);
71 const std::string filter_lower = absl::AsciiStrToLower(filter);
72 return value_lower.find(filter_lower) != std::string::npos;
73}
74
76 ImGui::InputText(tr("Project Root"), &project_path_);
77 ImGui::SameLine();
78 if (ImGui::Button(tr("Auto Detect"))) {
79 project_path_ = DetermineInitialProjectPath();
80 }
81 ImGui::SameLine();
82 if (ImGui::Button(tr("Refresh"))) {
84 }
85}
86
88 if (!has_registry_) {
89 return;
90 }
91 ImGui::TextDisabled(
92 tr("ASM: %zu Bins: %zu Draw: %zu Components: %zu Warnings: %zu"),
93 registry_.asm_files.size(), registry_.bins.size(),
95 registry_.warnings.size());
96}
97
99 const auto& theme = AgentUI::GetTheme();
100 ImGui::Checkbox(tr("Missing bins only"), &bins_missing_only_);
101
102 if (ImGui::BeginTable("oracle_menu_bins_table", 6,
103 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
104 ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY,
105 ImVec2(0, 320))) {
106 ImGui::TableSetupColumn("Label");
107 ImGui::TableSetupColumn("Bin Path");
108 ImGui::TableSetupColumn("Bytes", ImGuiTableColumnFlags_WidthFixed, 70.0f);
109 ImGui::TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed, 70.0f);
110 ImGui::TableSetupColumn("ASM");
111 ImGui::TableSetupColumn("Line", ImGuiTableColumnFlags_WidthFixed, 55.0f);
112 ImGui::TableHeadersRow();
113
114 for (const auto& entry : registry_.bins) {
115 if (bins_missing_only_ && entry.exists) {
116 continue;
117 }
118
119 ImGui::TableNextRow();
120 ImGui::TableSetColumnIndex(0);
121 ImGui::TextUnformatted(entry.label.empty() ? "(unlabeled)"
122 : entry.label.c_str());
123 ImGui::TableSetColumnIndex(1);
124 ImGui::TextUnformatted(entry.resolved_bin_path.c_str());
125 ImGui::TableSetColumnIndex(2);
126 ImGui::Text("%llu", static_cast<unsigned long long>(entry.size_bytes));
127 ImGui::TableSetColumnIndex(3);
128 ImGui::TextColored(
129 entry.exists ? theme.status_success : theme.status_error, "%s",
130 entry.exists ? "OK" : "MISSING");
131 ImGui::TableSetColumnIndex(4);
132 ImGui::TextUnformatted(entry.asm_path.c_str());
133 ImGui::TableSetColumnIndex(5);
134 ImGui::Text("%d", entry.line);
135 }
136
137 ImGui::EndTable();
138 }
139}
140
142 ImGui::InputText(tr("Filter"), &draw_filter_);
143
144 if (ImGui::BeginTable("oracle_menu_draw_table", 5,
145 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
146 ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY,
147 ImVec2(0, 320))) {
148 ImGui::TableSetupColumn("Routine");
149 ImGui::TableSetupColumn("ASM");
150 ImGui::TableSetupColumn("Line", ImGuiTableColumnFlags_WidthFixed, 55.0f);
151 ImGui::TableSetupColumn("Refs", ImGuiTableColumnFlags_WidthFixed, 50.0f);
152 ImGui::TableSetupColumn("Kind", ImGuiTableColumnFlags_WidthFixed, 60.0f);
153 ImGui::TableHeadersRow();
154
155 for (const auto& routine : registry_.draw_routines) {
156 if (!MatchesFilter(routine.label, draw_filter_)) {
157 continue;
158 }
159
160 ImGui::TableNextRow();
161 ImGui::TableSetColumnIndex(0);
162 ImGui::TextUnformatted(routine.label.c_str());
163 ImGui::TableSetColumnIndex(1);
164 ImGui::TextUnformatted(routine.asm_path.c_str());
165 ImGui::TableSetColumnIndex(2);
166 ImGui::Text("%d", routine.line);
167 ImGui::TableSetColumnIndex(3);
168 ImGui::Text("%d", routine.references);
169 ImGui::TableSetColumnIndex(4);
170 ImGui::TextUnformatted(routine.local ? "local" : "global");
171 }
172
173 ImGui::EndTable();
174 }
175}
176
178 ImGui::InputText(tr("Table Filter"), &component_table_filter_);
179
180 filtered_components_.clear();
182 for (const auto& component : registry_.components) {
183 if (!MatchesFilter(component.table_label, component_table_filter_)) {
184 continue;
185 }
186 filtered_components_.push_back(&component);
187 }
188
190 static_cast<int>(filtered_components_.size())) {
192 }
193
194 const float left_width = ImGui::GetContentRegionAvail().x * 0.58f;
195 ImGui::BeginChild("oracle_menu_components_list", ImVec2(left_width, 340.0f),
196 ImGuiChildFlags_Borders);
197 if (ImGui::BeginTable("oracle_menu_components_table", 5,
198 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
199 ImGuiTableFlags_Resizable |
200 ImGuiTableFlags_ScrollY)) {
201 ImGui::TableSetupColumn("Table");
202 ImGui::TableSetupColumn("Index", ImGuiTableColumnFlags_WidthFixed, 55.0f);
203 ImGui::TableSetupColumn("Row", ImGuiTableColumnFlags_WidthFixed, 50.0f);
204 ImGui::TableSetupColumn("Col", ImGuiTableColumnFlags_WidthFixed, 50.0f);
205 ImGui::TableSetupColumn("Ref");
206 ImGui::TableHeadersRow();
207
208 for (int i = 0; i < static_cast<int>(filtered_components_.size()); ++i) {
209 const auto* component = filtered_components_[i];
210 ImGui::TableNextRow();
211
212 ImGui::TableSetColumnIndex(0);
213 const bool selected = (selected_component_list_index_ == i);
214 std::string row_label =
215 absl::StrFormat("%s##oracle_component_%d", component->table_label, i);
216 if (ImGui::Selectable(row_label.c_str(), selected,
217 ImGuiSelectableFlags_SpanAllColumns)) {
219 edit_row_ = component->row;
220 edit_col_ = component->col;
221 }
222
223 ImGui::TableSetColumnIndex(1);
224 ImGui::Text("%d", component->index);
225 ImGui::TableSetColumnIndex(2);
226 ImGui::Text("%d", component->row);
227 ImGui::TableSetColumnIndex(3);
228 ImGui::Text("%d", component->col);
229 ImGui::TableSetColumnIndex(4);
230 ImGui::Text("%s:%d", component->asm_path.c_str(), component->line);
231 }
232
233 ImGui::EndTable();
234 }
235 ImGui::EndChild();
236
237 ImGui::SameLine();
238 ImGui::BeginChild("oracle_menu_component_editor", ImVec2(0, 340.0f),
239 ImGuiChildFlags_Borders);
242 static_cast<int>(filtered_components_.size())) {
243 ImGui::TextDisabled(tr("Select a component row to preview/apply edits."));
244 ImGui::EndChild();
245 return;
246 }
247
249 ImGui::Text(tr("Table: %s"), component->table_label.c_str());
250 ImGui::Text(tr("Index: %d"), component->index);
251 ImGui::Text(tr("ASM: %s:%d"), component->asm_path.c_str(), component->line);
252 if (!component->note.empty()) {
253 ImGui::TextDisabled(tr("Note: %s"), component->note.c_str());
254 }
255 ImGui::Separator();
256
257 ImGui::InputInt(tr("Row"), &edit_row_);
258 ImGui::InputInt(tr("Col"), &edit_col_);
259 if (edit_row_ < 0) {
260 edit_row_ = 0;
261 }
262 if (edit_col_ < 0) {
263 edit_col_ = 0;
264 }
265
266 if (ImGui::Button(tr("Preview"))) {
268 registry_.project_root, component->asm_path, component->table_label,
269 component->index, edit_row_, edit_col_, false);
270 if (!edit_or.ok()) {
272 absl::StrFormat("Preview failed: %s", edit_or.status().message());
273 } else {
274 const auto& edit = edit_or.value();
275 status_message_ = absl::StrFormat(
276 "Preview %s[%d] (%d,%d) -> (%d,%d)", edit.table_label, edit.index,
277 edit.old_row, edit.old_col, edit.new_row, edit.new_col);
278 }
279 }
280 ImGui::SameLine();
281 if (ImGui::Button(tr("Apply"))) {
283 registry_.project_root, component->asm_path, component->table_label,
284 component->index, edit_row_, edit_col_, true);
285 if (!edit_or.ok()) {
287 absl::StrFormat("Apply failed: %s", edit_or.status().message());
288 } else {
289 const auto& edit = edit_or.value();
290 status_message_ = absl::StrFormat(
291 "Applied %s[%d] (%d,%d) -> (%d,%d)", edit.table_label, edit.index,
292 edit.old_row, edit.old_col, edit.new_row, edit.new_col);
294 }
295 }
296
297 ImGui::EndChild();
298}
299
300void OracleMenuInspectorPanel::Draw(bool* /*p_open*/) {
301 const auto& theme = AgentUI::GetTheme();
303 DrawToolbar();
304
305 if (!last_error_.empty()) {
306 ImGui::TextColored(theme.status_error, "%s", last_error_.c_str());
307 return;
308 }
309
310 if (!status_message_.empty()) {
311 ImGui::TextDisabled("%s", status_message_.c_str());
312 }
313 DrawSummary();
314
315 if (!has_registry_) {
316 ImGui::TextDisabled(tr("No menu registry data loaded."));
317 return;
318 }
319
320 if (gui::BeginThemedTabBar("oracle_menu_tabs")) {
321 if (ImGui::BeginTabItem(tr("Bins"))) {
322 DrawBinsTab();
323 ImGui::EndTabItem();
324 }
325 if (ImGui::BeginTabItem(tr("Draw Routines"))) {
327 ImGui::EndTabItem();
328 }
329 if (ImGui::BeginTabItem(tr("Components"))) {
331 ImGui::EndTabItem();
332 }
333 if (ImGui::BeginTabItem(tr("Warnings"))) {
334 if (registry_.warnings.empty()) {
335 ImGui::TextDisabled(tr("No warnings."));
336 } else {
337 for (const auto& warning : registry_.warnings) {
338 ImGui::BulletText("%s", warning.c_str());
339 }
340 }
341 ImGui::EndTabItem();
342 }
344 }
345}
346
348
349} // namespace yaze::editor
void Draw(bool *p_open) override
Draw the panel content.
std::vector< const core::OracleMenuComponent * > filtered_components_
bool MatchesFilter(const std::string &value, const std::string &filter) const
absl::StatusOr< OracleMenuRegistry > BuildOracleMenuRegistry(const std::filesystem::path &project_root)
absl::StatusOr< OracleMenuComponentEditResult > SetOracleMenuComponentOffset(const std::filesystem::path &project_root, const std::string &asm_relative_path, const std::string &table_label, int index, int row, int col, bool write_changes)
const AgentUITheme & GetTheme()
::yaze::project::YazeProject * current_project()
Get the current project instance.
Editors are the view controllers for the application.
bool BeginThemedTabBar(const char *id, ImGuiTabBarFlags flags)
A stylized tab bar with "Mission Control" branding.
void EndThemedTabBar()
#define REGISTER_PANEL(PanelClass)
Auto-registration macro for panels with default constructors.
std::vector< OracleMenuComponent > components
std::vector< OracleMenuDrawRoutine > draw_routines
std::vector< std::string > warnings
std::vector< std::string > asm_files
std::vector< OracleMenuBinEntry > bins