yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
oracle_validation_panel.cc
Go to the documentation of this file.
2
3#include <chrono>
4#include <string>
5
6#include "absl/strings/str_format.h"
12#include "imgui/imgui.h"
13#include "imgui/misc/cpp/imgui_stdlib.h"
14#include "rom/rom.h"
15
16namespace yaze::editor {
17namespace {
18constexpr ImVec4 kGreen{0.2f, 0.75f, 0.3f, 1.0f};
19constexpr ImVec4 kRed{0.85f, 0.2f, 0.2f, 1.0f};
20constexpr ImVec4 kYellow{0.85f, 0.75f, 0.1f, 1.0f};
21constexpr ImVec4 kGrey{0.55f, 0.55f, 0.55f, 1.0f};
22
23ImVec4 BoolColor(bool flag) { return flag ? kGreen : kRed; }
24
25const char* CheckStr(bool flag) { return flag ? "OK" : "X"; }
26
27void DrawCheckBadge(const std::string& state) {
28 if (state == "ran") {
29 ImGui::TextColored(kGreen, "[ran]");
30 return;
31 }
32 if (state == "skipped") {
33 ImGui::TextColored(kGrey, "[skipped]");
34 return;
35 }
36 ImGui::TextColored(kYellow, "[%s]", state.c_str());
37}
38
39void DrawOptionalBool(const char* label, const std::optional<bool>& flag) {
40 ImGui::Text(" %s", label);
41 ImGui::SameLine();
42 if (flag.has_value()) {
43 ImGui::TextColored(BoolColor(*flag), "%s", CheckStr(*flag));
44 return;
45 }
46 ImGui::TextColored(kGrey, "-");
47}
48} // namespace
49
51 if (pending_.valid()) {
52 pending_.wait();
53 }
54}
55
56std::string OracleValidationPanel::GetId() const { return "oracle.validation"; }
57
59 return "Oracle Validation";
60}
61
62std::string OracleValidationPanel::GetIcon() const {
64}
65
67 return "Oracle";
68}
69
73
74float OracleValidationPanel::GetPreferredWidth() const { return 540.0f; }
75
76void OracleValidationPanel::Draw(bool* p_open) {
77 (void)p_open;
80 ImGui::Separator();
82 ImGui::Separator();
84}
85
87 if (auto* rom = ContentRegistry::Context::rom(); rom && rom->is_loaded()) {
88 return rom->filename();
89 }
90 return "roms/oos168.sfc";
91}
92
94 if (!running_) {
95 return;
96 }
97 if (pending_.wait_for(std::chrono::milliseconds(0)) !=
98 std::future_status::ready) {
99 return;
100 }
101 last_result_ = pending_.get();
102 running_ = false;
103 status_message_ = last_result_->command_ok ? "Completed." : "Failed.";
104}
105
109
111 if (running_) {
112 return;
113 }
114
116 smoke_opts.rom_path = rom_path_;
118 smoke_opts.strict_readiness =
120 if (write_report_ && !report_path_.empty()) {
121 smoke_opts.report_path = report_path_;
122 }
123
125 preflight_opts.rom_path = rom_path_;
127 if (write_report_ && !report_path_.empty()) {
128 preflight_opts.report_path = report_path_;
129 }
130
131 Rom* rom_context = nullptr;
132 if (rom_path_.empty()) {
133 rom_context = GetRom();
134 }
135
136 running_ = true;
137 status_message_ = "Running...";
138
139 pending_ = std::async(std::launch::async,
140 [mode, smoke_opts, preflight_opts, rom_context]() {
142 result.mode = mode;
143 result.timestamp =
145
148 preflight_opts);
150 "dungeon-oracle-preflight", args);
151
153 handler;
154 auto status =
155 handler.Run(args, rom_context, &result.raw_output);
156 result.command_ok =
157 status.ok() ||
158 status.code() ==
159 absl::StatusCode::kFailedPrecondition;
160 result.status_code = status.code();
161 if (!status.ok() &&
162 status.code() !=
163 absl::StatusCode::kFailedPrecondition) {
164 result.error_message =
165 std::string(status.message());
166 return result;
167 }
169 result.raw_output);
170 if (parsed.ok()) {
171 result.preflight = *parsed;
172 } else {
173 result.json_parse_failed = true;
174 }
175 return result;
176 }
177
178 auto args =
181 "oracle-smoke-check", args);
182
184 auto status =
185 handler.Run(args, rom_context, &result.raw_output);
186 result.command_ok =
187 status.ok() ||
188 status.code() ==
189 absl::StatusCode::kFailedPrecondition;
190 result.status_code = status.code();
191 if (!status.ok() &&
192 status.code() !=
193 absl::StatusCode::kFailedPrecondition) {
194 result.error_message = std::string(status.message());
195 return result;
196 }
198 result.raw_output);
199 if (parsed.ok()) {
200 result.smoke = *parsed;
201 } else {
202 result.json_parse_failed = true;
203 }
204 return result;
205 });
206}
207
209 ImGui::SeparatorText("Options");
210
211 ImGui::SetNextItemWidth(320.0f);
212 ImGui::InputText("ROM Path", &rom_path_);
213 ImGui::SameLine();
214 if (ImGui::SmallButton("From ROM")) {
216 }
217
218 ImGui::SetNextItemWidth(80.0f);
219 ImGui::InputInt("Min D6 track rooms", &min_d6_track_rooms_);
220 if (min_d6_track_rooms_ < 0) {
222 }
223
224 ImGui::SeparatorText("Preflight options");
225 ImGui::SetNextItemWidth(200.0f);
226 ImGui::InputText("Required rooms", &required_collision_rooms_);
227 ImGui::SameLine();
228 ImGui::TextDisabled("(e.g. 0x25,0x27)");
229
230 ImGui::Checkbox("Write report file", &write_report_);
231 if (write_report_) {
232 ImGui::SetNextItemWidth(280.0f);
233 ImGui::InputText("Report path", &report_path_);
234 }
235}
236
238 Rom* rom = GetRom();
239 const bool rom_missing =
240 (rom == nullptr || !rom->is_loaded()) && rom_path_.empty();
241 if (rom_missing) {
242 ImGui::BeginDisabled();
243 }
244 if (running_) {
245 ImGui::BeginDisabled();
246 }
247
248 if (ImGui::Button("Run Structural Smoke")) {
250 }
251 ImGui::SameLine();
252 if (ImGui::Button("Run Strict Readiness")) {
254 }
255 ImGui::SameLine();
256 if (ImGui::Button("Run Oracle Preflight")) {
258 }
259
260 if (running_) {
261 ImGui::EndDisabled();
262 ImGui::SameLine();
263 ImGui::TextColored(kYellow, "%s", status_message_.c_str());
264 }
265 if (rom_missing) {
266 ImGui::EndDisabled();
267 ImGui::SameLine();
268 ImGui::TextColored(kRed, "Load a ROM first");
269 }
270}
271
273 if (!last_result_.has_value()) {
274 ImGui::TextDisabled("No results yet. Run a check above.");
275 return;
276 }
277 const auto& result = *last_result_;
278
279 const char* mode_label =
281 ? "Oracle Preflight"
283 ? "Strict Readiness Smoke"
284 : "Structural Smoke");
285 const bool overall_ok =
286 result.smoke.has_value()
287 ? result.smoke->ok
288 : (result.preflight.has_value() && result.preflight->ok);
289
290 ImGui::TextColored(overall_ok ? kGreen : kRed, "%s %s",
291 CheckStr(overall_ok), mode_label);
292 ImGui::SameLine(0.0f, 16.0f);
293 ImGui::TextColored(kGrey, "%s", result.timestamp.c_str());
294
295 ImGui::SetNextItemWidth(380.0f);
296 ImGui::InputText("##cli_cmd",
297 const_cast<char*>(result.cli_command.c_str()),
298 result.cli_command.size() + 1,
299 ImGuiInputTextFlags_ReadOnly);
300 ImGui::SameLine();
301 if (ImGui::SmallButton("Copy")) {
302 ImGui::SetClipboardText(result.cli_command.c_str());
303 }
304
305 if (!result.error_message.empty()) {
306 ImGui::TextColored(kRed, ICON_MD_ERROR " %s",
307 result.error_message.c_str());
308 ImGui::TextDisabled(
309 "Hint: check that the ROM is loaded and the command is available.");
310 DrawRawOutput(result);
311 return;
312 }
313
314 if (result.json_parse_failed) {
315 ImGui::TextColored(kYellow, ICON_MD_WARNING
316 " JSON parse failed - raw output:");
317 DrawRawOutput(result);
318 return;
319 }
320
321 if (result.smoke.has_value()) {
322 DrawSmokeCards(*result.smoke);
323 }
324 if (result.preflight.has_value()) {
325 DrawPreflightCards(*result.preflight);
326 }
327}
328
330 const oracle_validation::SmokeResult& smoke) {
331 if (ImGui::CollapsingHeader("D4 Zora Temple", ImGuiTreeNodeFlags_DefaultOpen)) {
332 ImGui::TextColored(BoolColor(smoke.d4.structural_ok), " Structural %s",
333 CheckStr(smoke.d4.structural_ok));
334 ImGui::Text(" Required rooms check:");
335 ImGui::SameLine();
336 DrawCheckBadge(smoke.d4.required_rooms_check);
337 DrawOptionalBool(" Rooms 0x25/0x27 have collision:",
338 smoke.d4.required_rooms_ok);
339 }
340
341 if (ImGui::CollapsingHeader("D6 Goron Mines", ImGuiTreeNodeFlags_DefaultOpen)) {
342 ImGui::TextColored(BoolColor(smoke.d6.meets_min_track_rooms),
343 " Track rooms %d / %d %s",
345 smoke.d6.meets_min_track_rooms ? "(ok)"
346 : "(below threshold)");
347 ImGui::TextColored(BoolColor(smoke.d6.ok), " Audit command %s",
348 CheckStr(smoke.d6.ok));
349 }
350
351 if (ImGui::CollapsingHeader("D3 Kalyxo Castle", ImGuiTreeNodeFlags_DefaultOpen)) {
352 ImGui::Text(" Readiness check:");
353 ImGui::SameLine();
354 DrawCheckBadge(smoke.d3.readiness_check);
355 DrawOptionalBool(" Room 0x32 has collision:", smoke.d3.ok);
356 }
357}
358
360 const oracle_validation::PreflightResult& preflight) {
361 if (ImGui::CollapsingHeader("Water Fill", ImGuiTreeNodeFlags_DefaultOpen)) {
362 ImGui::TextColored(BoolColor(preflight.water_fill_region_ok),
363 " Region present %s",
364 CheckStr(preflight.water_fill_region_ok));
365 ImGui::TextColored(BoolColor(preflight.water_fill_table_ok),
366 " Table valid %s",
367 CheckStr(preflight.water_fill_table_ok));
368 }
369
370 if (ImGui::CollapsingHeader("Custom Collision",
371 ImGuiTreeNodeFlags_DefaultOpen)) {
372 ImGui::TextColored(BoolColor(preflight.custom_collision_maps_ok),
373 " Maps valid %s",
374 CheckStr(preflight.custom_collision_maps_ok));
375 ImGui::Text(" Required rooms check:");
376 ImGui::SameLine();
377 DrawCheckBadge(preflight.required_rooms_check);
378 DrawOptionalBool(" Required rooms have data:",
379 preflight.required_rooms_ok);
380 }
381
382 if (!preflight.errors.empty()) {
383 const std::string errors_label = absl::StrFormat(
384 "Errors (%d)###preflight_errors", preflight.error_count);
385 if (ImGui::CollapsingHeader(errors_label.c_str())) {
386 for (const auto& err : preflight.errors) {
387 ImGui::TextColored(kRed, " [%s] %s", err.code.c_str(),
388 err.message.c_str());
389 if (err.room_id.has_value()) {
390 ImGui::SameLine();
391 ImGui::TextColored(kGrey, " room %s", err.room_id->c_str());
392 }
393 }
394 }
395 }
396}
397
400 if (ImGui::CollapsingHeader("Raw Output (diagnostics)")) {
401 ImGui::InputTextMultiline(
402 "##raw", const_cast<char*>(result.raw_output.c_str()),
403 result.raw_output.size() + 1, ImVec2(-1.0f, 120.0f),
404 ImGuiInputTextFlags_ReadOnly);
405 }
406}
407
409
410} // namespace yaze::editor
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:28
auto filename() const
Definition rom.h:145
bool is_loaded() const
Definition rom.h:132
absl::Status Run(const std::vector< std::string > &args, Rom *rom_context, std::string *captured_output=nullptr)
Execute the command.
void DrawSmokeCards(const oracle_validation::SmokeResult &smoke)
void DrawRawOutput(const oracle_validation::OracleRunResult &result)
float GetPreferredWidth() const override
Get preferred width for this panel (optional)
std::future< oracle_validation::OracleRunResult > pending_
std::string GetIcon() const override
Material Design icon for this panel.
std::string GetId() const override
Unique identifier for this panel.
void Draw(bool *p_open) override
Draw the panel content.
PanelCategory GetPanelCategory() const override
Get the lifecycle category for this panel.
std::optional< oracle_validation::OracleRunResult > last_result_
std::string GetDisplayName() const override
Human-readable name shown in menus and title bars.
void LaunchRun(oracle_validation::RunMode mode)
std::string GetEditorCategory() const override
Editor category this panel belongs to.
void DrawPreflightCards(const oracle_validation::PreflightResult &preflight)
#define ICON_MD_WARNING
Definition icons.h:2123
#define ICON_MD_ERROR
Definition icons.h:686
#define ICON_MD_VERIFIED_USER
Definition icons.h:2056
Rom * rom()
Get the current ROM instance.
void DrawOptionalBool(const char *label, const std::optional< bool > &flag)
std::vector< std::string > BuildSmokeArgs(const SmokeOptions &opts)
std::vector< std::string > BuildPreflightArgs(const PreflightOptions &opts)
std::string BuildCliCommand(const std::string &command_name, const std::vector< std::string > &args)
absl::StatusOr< SmokeResult > ParseSmokeCheckOutput(const std::string &json_str)
absl::StatusOr< PreflightResult > ParsePreflightOutput(const std::string &json_str)
Editors are the view controllers for the application.
PanelCategory
Defines lifecycle behavior for editor panels.
@ CrossEditor
User can pin to persist across editors.
#define REGISTER_PANEL(PanelClass)
Auto-registration macro for panels with default constructors.