8#include <initializer_list>
10#include "absl/status/status.h"
11#include "absl/strings/ascii.h"
12#include "absl/strings/match.h"
13#include "absl/strings/str_format.h"
24#include "imgui/misc/cpp/imgui_stdlib.h"
36 : editor_manager_(editor_manager), status_(absl::OkStatus()) {}
167 for (
auto& [name, params] :
popups_) {
168 if (params.is_visible &&
173 if (top ==
nullptr) {
176 OpenPopup(top->
name.c_str());
180 ? ImGuiWindowFlags_None
181 : ImGuiWindowFlags_AlwaysAutoResize;
183 if (BeginPopupModal(top->
name.c_str(),
nullptr, popup_flags)) {
194 std::string name_str(name);
195 auto it =
popups_.find(name_str);
197 if (!it->second.is_visible) {
203 it->second.is_visible =
true;
207 "[PopupManager] Warning: Popup '%s' not registered. Available popups: ",
209 for (
const auto& [key, _] :
popups_) {
210 printf(
"'%s' ", key.c_str());
221 std::string name_str(name);
222 auto it =
popups_.find(name_str);
224 it->second.is_visible =
false;
233 std::function<
void()> draw_function,
240 popups_[name] = std::move(params);
248 std::string name_str(name);
249 auto it =
popups_.find(name_str);
251 return it->second.is_visible;
265 ImGuiIO
const& io = GetIO();
266 ImVec2 pos(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f);
267 SetNextWindowPos(pos, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
268 ImGuiWindowFlags flags =
269 ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration |
270 ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings;
271 return Begin(name,
nullptr, flags);
285 IsKeyPressed(ImGuiKey_Space)) {
298 Text(tr(
"Yet Another Zelda3 Editor - v%s"),
300 Text(tr(
"Written by: scawful"));
302 Text(tr(
"Special Thanks: Zarby89, JaredBrian"));
315 Text(tr(
"Title: %s"), current_rom->title().c_str());
322 const auto& layout = profile.lyra_layout;
325 TextUnformatted(
"Overworld Format: Lyra descriptor");
326 Text(
"ROM Format Revision: %u", layout.revision);
327 Text(
"Area Capacity: %u", layout.capacity);
328 Text(
"Active Areas: %u", layout.active_count);
330 ?
"Validation: valid"
331 :
"Validation: invalid");
332 TextUnformatted(
"Write Support: inspection only");
333 if (!layout.error.empty()) {
334 TextWrapped(
"Reason: %s", layout.error.c_str());
336 }
else if (profile.layout_kind ==
339 TextUnformatted(
"Overworld Format: older $EA tail-map expansion");
340 }
else if (profile.layout_kind ==
343 Text(
"Overworld Format: %s",
345 TextUnformatted(
"Collision metadata: per-sheet, read-only");
346 TextUnformatted(
"Preview: static full-load sheets");
347 TextUnformatted(
"Tail-map expansion: unavailable");
348 }
else if (profile.layout_kind ==
351 Text(
"Overworld Format: %s (raw marker $%02X)",
353 static_cast<unsigned>(profile.asm_version));
354 TextUnformatted(
"Write Support: unqualified for this custom layout");
355 TextUnformatted(
"Compatibility: unqualified");
359 if (project && project->project_opened()) {
363 Text(tr(
"Write Policy: %s"),
366 Text(tr(
"Expected Hash: %s"),
367 project->rom_metadata.expected_hash.empty()
369 : project->rom_metadata.expected_hash.c_str());
373 tr(
"ROM hash mismatch detected"));
378 IsKeyPressed(ImGuiKey_Escape)) {
379 Hide(
"ROM Information");
388 using namespace ImGui;
389 namespace fs = std::filesystem;
391 auto register_widget = [](
const char* name,
const char* type) {
393 std::string(
"SaveAs/") + type +
":" + name, type, GetItemID());
397 TextUnformatted(tr(
"No ROM loaded."));
398 if (Button(tr(
"Close"))) {
412 auto source = fs::absolute(fs::path(rom->
filename()), ec);
413 if (ec || rom->
filename().empty()) {
415 tr(
"The current ROM has no usable folder. Choose Browse.");
419 auto extension = source.extension().string();
420 const auto lower_extension = absl::AsciiStrToLower(extension);
421 if (lower_extension !=
".sfc" && lower_extension !=
".smc") {
425 source.stem().empty() ? std::string(
"rom") : source.stem().string();
437 TextUnformatted(tr(
"Folder:"));
438 PushTextWrapPos(GetCursorPosX() + 480.0f);
441 SetNextItemWidth(360.0f);
443 register_widget(
"Filename",
"input");
449 const bool browse = Button(
452 register_widget(
"Browse",
"button");
455 if (!selected.empty()) {
457 const auto destination = fs::absolute(fs::path(selected), ec);
466 const bool save = Button(absl::StrFormat(
"%s Save",
ICON_MD_SAVE).c_str(),
468 register_widget(
"Save",
"button");
471 if (filename.empty() || filename ==
"." || filename ==
".." ||
474 tr(
"Enter a filename only. Use Browse to choose a different folder.");
476 const auto extension =
477 absl::AsciiStrToLower(filename.extension().string());
478 if (extension !=
".sfc" && extension !=
".smc") {
488 if (!absl::IsCancelled(status)) {
498 register_widget(
"Cancel",
"button");
499 if (cancel || IsKeyPressed(ImGuiKey_Escape)) {
505 using namespace ImGui;
510 tr(
"Controls which data is written during File > Save ROM. Dungeon "
511 "choices also control Apply Room. "
512 "Changes apply immediately."));
515 if (CollapsingHeader(tr(
"Overworld"), ImGuiTreeNodeFlags_DefaultOpen)) {
516 Checkbox(tr(
"Save Overworld Maps"),
518 Checkbox(tr(
"Save Overworld Entrances"),
520 Checkbox(tr(
"Save Overworld Exits"),
522 Checkbox(tr(
"Save Overworld Items"),
524 Checkbox(tr(
"Save Overworld Sprites"),
526 Checkbox(tr(
"Save Overworld Properties"),
530 if (CollapsingHeader(tr(
"Dungeon"), ImGuiTreeNodeFlags_DefaultOpen)) {
531 Checkbox(tr(
"Save Dungeon Maps"),
533 Checkbox(tr(
"Save Objects"),
535 Checkbox(tr(
"Save Sprites"),
537 Checkbox(tr(
"Save Room Headers"),
539 Checkbox(tr(
"Save Torches"),
543 Checkbox(tr(
"Save Collision"),
546 Checkbox(tr(
"Save Pot Items"),
548 if (IsItemHovered()) {
550 tr(
"Enable to save pending pot item edits, then retry Save "
551 "or Apply Room. No project reopen is needed."));
553 Checkbox(tr(
"Save Entrances"),
555 Checkbox(tr(
"Save Palettes"),
559 if (CollapsingHeader(tr(
"Graphics"), ImGuiTreeNodeFlags_DefaultOpen)) {
560 Checkbox(tr(
"Save Graphics Sheets"),
562 Checkbox(tr(
"Save All Palettes"),
567 if (CollapsingHeader(tr(
"Messages"), ImGuiTreeNodeFlags_DefaultOpen)) {
578 using namespace ImGui;
582 Text(tr(
"No ROM loaded."));
590 std::string backup_dir;
591 if (project && project->project_opened() &&
592 !project->rom_backup_folder.empty()) {
595 backup_dir = std::filesystem::path(rom->
filename()).parent_path().string();
600 TextWrapped(tr(
"Backup folder: %s"), backup_dir.c_str());
605 tr(
"A restored backup is staged in this session. Save ROM to commit "
606 "it. Discard reloads the backing ROM and abandons staged ROM-buffer "
607 "edits; resolve pending graphics, dungeon, or palette edits "
613 toast->Show(absl::StrFormat(
"Discard failed: %s", status.message()),
617 toast->Show(
"Restored backup discarded; reloaded ROM from disk",
628 toast->Show(absl::StrFormat(
"Prune failed: %s", status.message()),
638 if (backups.empty()) {
639 TextDisabled(tr(
"No backups found."));
640 }
else if (BeginTable(
"RomBackupTable", 4,
641 ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders |
642 ImGuiTableFlags_Resizable)) {
643 TableSetupColumn(
"Timestamp");
644 TableSetupColumn(
"Size");
645 TableSetupColumn(
"Filename");
646 TableSetupColumn(
"Actions");
649 auto format_size = [](uintmax_t bytes) {
650 if (bytes > (1024 * 1024)) {
651 return absl::StrFormat(
"%.2f MB",
652 static_cast<double>(bytes) / (1024 * 1024));
655 return absl::StrFormat(
"%.1f KB",
static_cast<double>(bytes) / 1024.0);
657 return absl::StrFormat(
"%llu B",
static_cast<unsigned long long>(bytes));
660 for (
size_t i = 0; i < backups.size(); ++i) {
661 const auto& backup = backups[i];
664 char time_buffer[32] =
"unknown";
665 if (backup.timestamp != 0) {
668 localtime_s(&local_tm, &backup.timestamp);
670 localtime_r(&backup.timestamp, &local_tm);
672 std::strftime(time_buffer,
sizeof(time_buffer),
"%Y-%m-%d %H:%M:%S",
675 TextUnformatted(time_buffer);
678 TextUnformatted(format_size(backup.size_bytes).c_str());
681 TextUnformatted(backup.filename.c_str());
684 PushID(
static_cast<int>(i));
689 toast->Show(absl::StrFormat(
"Restore failed: %s", status.message()),
693 toast->Show(
"Backup loaded; inspect it, then save ROM to commit",
702 toast->Show(absl::StrFormat(
"Open failed: %s", status.message()),
709 SetClipboardText(backup.path.c_str());
723 using namespace ImGui;
725 static std::string project_name =
"";
726 static std::string project_filepath =
"";
727 static std::string rom_filename =
"";
728 static std::string labels_filename =
"";
729 static std::string code_folder =
"";
731 InputText(tr(
"Project Name"), &project_name);
733 if (Button(absl::StrFormat(
"%s Destination Folder",
ICON_MD_FOLDER).c_str(),
738 Text(
"%s", project_filepath.empty() ?
"(Not set)" : project_filepath.c_str());
746 Text(
"%s", rom_filename.empty() ?
"(Not set)" : rom_filename.c_str());
748 if (Button(absl::StrFormat(
"%s Labels File",
ICON_MD_LABEL).c_str(),
753 Text(
"%s", labels_filename.empty() ?
"(Not set)" : labels_filename.c_str());
755 if (Button(absl::StrFormat(
"%s Code Folder",
ICON_MD_CODE).c_str(),
760 Text(
"%s", code_folder.empty() ?
"(Not set)" : code_folder.c_str());
764 if (Button(absl::StrFormat(
"%s Choose Project File Location",
ICON_MD_SAVE)
767 auto project_file_path =
769 if (!project_file_path.empty()) {
770 if (!(absl::EndsWith(project_file_path,
".yaze") ||
771 absl::EndsWith(project_file_path,
".yazeproj"))) {
772 project_file_path +=
".yaze";
774 project_filepath = project_file_path;
778 if (Button(absl::StrFormat(
"%s Create Project",
ICON_MD_ADD).c_str(),
780 if (!project_filepath.empty() && !project_name.empty() &&
781 !rom_filename.empty()) {
783 "Basic ROM Hack", rom_filename, project_name, project_filepath);
787 if (!labels_filename.empty()) {
790 if (!code_folder.empty()) {
791 project->code_folder = code_folder;
793 if (!labels_filename.empty() || !code_folder.empty()) {
802 project_filepath =
"";
804 labels_filename =
"";
817 project_filepath =
"";
819 labels_filename =
"";
832 auto status_color = [&](
const char* status) -> ImVec4 {
833 if (strcmp(status,
"Stable") == 0 || strcmp(status,
"Working") == 0) {
836 if (strcmp(status,
"Beta") == 0 || strcmp(status,
"Experimental") == 0) {
839 if (strcmp(status,
"Preview") == 0) {
842 if (strcmp(status,
"Not available") == 0) {
851 const char* persistence;
855 auto draw_table = [&](
const char* table_id,
856 std::initializer_list<FeatureRow> rows) {
857 ImGuiTableFlags flags = ImGuiTableFlags_BordersInnerH |
858 ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable;
859 if (!BeginTable(table_id, 4, flags)) {
862 TableSetupColumn(
"Feature", ImGuiTableColumnFlags_WidthStretch);
863 TableSetupColumn(
"Status", ImGuiTableColumnFlags_WidthFixed, 120.0f);
864 TableSetupColumn(
"Save/Load", ImGuiTableColumnFlags_WidthFixed, 180.0f);
865 TableSetupColumn(
"Notes", ImGuiTableColumnFlags_WidthStretch);
868 for (
const auto& row : rows) {
870 TableSetColumnIndex(0);
871 TextUnformatted(row.feature);
872 TableSetColumnIndex(1);
873 TextColored(status_color(row.status),
"%s", row.status);
874 TableSetColumnIndex(2);
875 TextUnformatted(row.persistence);
876 TableSetColumnIndex(3);
877 TextWrapped(
"%s", row.notes);
884 tr(
"Status: Stable = production ready, Beta = usable with gaps, "
885 "Experimental = WIP, Preview = web parity in progress."));
886 TextDisabled(tr(
"See Settings > Feature Flags for ROM-specific toggles."));
889 if (CollapsingHeader(tr(
"Desktop App (yaze)"),
890 ImGuiTreeNodeFlags_DefaultOpen)) {
891 draw_table(
"desktop_features",
893 {
"ROM load/save",
"Stable",
"ROM + backups",
894 "Backups on save when enabled."},
895 {
"Overworld Editor",
"Stable",
"ROM",
896 "Maps/entrances/exits/items; version-gated."},
897 {
"Dungeon Editor",
"Stable",
"ROM",
898 "Room objects/tiles/palettes persist."},
899 {
"Palette Editor",
"Stable",
"ROM",
900 "Palette edits persist; JSON IO pending."},
901 {
"Graphics Editor",
"Beta",
"ROM",
902 "Sheet edits persist; tooling still expanding."},
903 {
"Sprite Editor",
"Stable",
"ROM",
"Sprite edits persist."},
904 {
"Message Editor",
"Stable",
"ROM",
"Text edits persist."},
905 {
"Screen Editor",
"Experimental",
"ROM (partial)",
906 "Save coverage incomplete."},
907 {
"Hex Editor",
"Beta",
"ROM",
"Search UX incomplete."},
908 {
"Assembly/Asar",
"Beta",
"ROM + project",
909 "Patch apply + symbol export."},
910 {
"Emulator",
"Beta",
"Runtime only",
911 "Save-state UI partially wired."},
912 {
"Music Editor",
"Experimental",
"ROM (partial)",
913 "Serialization in progress."},
914 {
"Agent UI",
"Experimental",
".yaze/agent",
915 "Requires AI provider configuration."},
916 {
"Settings/Layouts",
"Beta",
".yaze config",
917 "Layout serialization improving."},
921 if (CollapsingHeader(tr(
"z3ed CLI"))) {
922 draw_table(
"cli_features",
924 {
"ROM read/write/validate",
"Stable",
"ROM file",
925 "Direct command execution."},
926 {
"Agent workflows",
"Stable",
".yaze/proposals + sandboxes",
927 "Commit writes ROM; revert reloads."},
928 {
"Snapshots/restore",
"Stable",
"Sandbox copies",
929 "Supports YAZE_SANDBOX_ROOT override."},
930 {
"Doctor/test suites",
"Stable",
"Reports",
931 "Structured output for automation."},
932 {
"TUI/REPL",
"Stable",
"Session history",
933 "Interactive command palette + logs."},
937 if (CollapsingHeader(tr(
"Web/WASM Preview"))) {
941 {
"ROM load/save",
"Preview",
"IndexedDB + download",
942 "Drag/drop or picker; download for backups."},
943 {
"Editors (OW/Dungeon/Palette/etc.)",
"Preview",
944 "IndexedDB + download",
"Parity work in progress."},
945 {
"Hex Editor",
"Working",
"IndexedDB + download",
946 "Direct ROM editing available."},
947 {
"Asar patching",
"Preview",
"ROM",
"Basic patch apply support."},
948 {
"Emulator",
"Not available",
"N/A",
"Desktop only."},
949 {
"Collaboration",
"Experimental",
"Server",
950 "Requires yaze-server."},
951 {
"AI features",
"Preview",
"Server",
"Requires AI-enabled server."},
961 Text(tr(
"File -> Open"));
962 Text(tr(
"Select a ROM file to open"));
963 Text(tr(
"Supported ROMs (headered or unheadered):"));
964 Text(tr(
"The Legend of Zelda: A Link to the Past"));
965 Text(tr(
"US Version 1.0"));
966 Text(tr(
"JP Version 1.0"));
969 tr(
"ROM files are not bundled. Use a clean, legally obtained copy."));
977 Text(tr(
"Project Menu"));
978 Text(tr(
"Create a new project or open an existing one."));
979 Text(tr(
"Save the project to save the current state of the project."));
981 tr(
"To save a project, you need to first open a ROM and initialize your "
982 "code path and labels file. Label resource manager can be found in "
983 "the View menu. Code path is set in the Code editor after opening a "
987 Hide(
"Manage Project");
994 "YAZE lets you modify 'The Legend of Zelda: A Link to the Past' (US or "
995 "JP) ROMs with modern tooling."));
997 TextWrapped(tr(
"Release Highlights:"));
999 tr(
"AI-assisted workflows via z3ed agent and in-app panels "
1000 "(Ollama/Gemini/OpenAI/Anthropic)"));
1001 BulletText(tr(
"Clear feature status panels and improved help/tooltips"));
1002 BulletText(tr(
"Unified .yaze storage across desktop/CLI/web"));
1004 TextWrapped(tr(
"General Tips:"));
1005 BulletText(tr(
"Open a clean ROM and save a backup before editing"));
1006 BulletText(tr(
"Use Help (F1) for context-aware guidance and shortcuts"));
1007 BulletText(tr(
"Coming from ZScream? Open Help → Coming from ZScream"));
1009 "Configure AI providers (Ollama/Gemini/OpenAI/Anthropic) in Settings > "
1013 Hide(
"Getting Started");
1018 TextWrapped(tr(
"Asar 65816 Assembly Integration"));
1020 tr(
"YAZE includes full Asar assembler support for ROM patching."));
1022 TextWrapped(tr(
"Features:"));
1023 BulletText(tr(
"Cross-platform ROM patching with assembly code"));
1024 BulletText(tr(
"Symbol export with addresses and opcodes"));
1025 BulletText(tr(
"Assembly validation with detailed error reporting"));
1026 BulletText(tr(
"Memory-safe patch application with size checks"));
1029 Hide(
"Asar Integration");
1034 TextWrapped(tr(
"Build Instructions"));
1035 TextWrapped(tr(
"YAZE uses modern CMake for cross-platform builds."));
1037 TextWrapped(tr(
"Quick Start (examples):"));
1038 BulletText(tr(
"cmake --preset mac-dbg | lin-dbg | win-dbg"));
1039 BulletText(tr(
"cmake --build --preset <preset> --target yaze"));
1041 TextWrapped(tr(
"AI Builds:"));
1042 BulletText(tr(
"cmake --preset mac-ai | lin-ai | win-ai"));
1043 BulletText(tr(
"cmake --build --preset <preset> --target yaze z3ed"));
1045 TextWrapped(tr(
"Docs: docs/public/build/quick-reference.md"));
1048 Hide(
"Build Instructions");
1053 TextWrapped(tr(
"Command Line Interface (z3ed)"));
1054 TextWrapped(tr(
"Scriptable ROM editing and AI agent workflows."));
1056 TextWrapped(tr(
"Commands:"));
1057 BulletText(tr(
"z3ed rom-info --rom=zelda3.sfc"));
1058 BulletText(tr(
"z3ed agent simple-chat --rom=zelda3.sfc --ai_provider=auto"));
1059 BulletText(tr(
"z3ed agent plan --rom=zelda3.sfc"));
1060 BulletText(tr(
"z3ed test-list --format json"));
1061 BulletText(tr(
"z3ed patch apply-asar patch.asm --rom=zelda3.sfc"));
1062 BulletText(tr(
"z3ed help dungeon-place-sprite"));
1064 TextWrapped(tr(
"Storage:"));
1066 tr(
"Agent plans/proposals live under ~/.yaze (see docs for details)"));
1074 TextWrapped(tr(
"Troubleshooting"));
1075 TextWrapped(tr(
"Common issues and solutions:"));
1077 BulletText(tr(
"ROM won't load: Check file format (SFC/SMC supported)"));
1079 tr(
"AI agent missing: Start Ollama or set GEMINI_API_KEY/OPENAI_API_KEY/"
1080 "ANTHROPIC_API_KEY (web uses AI_AGENT_ENDPOINT)"));
1081 BulletText(tr(
"Graphics issues: Disable experimental flags in Settings"));
1083 tr(
"Performance: Enable hardware acceleration in display settings"));
1084 BulletText(tr(
"Crashes: Check ROM file integrity and available memory"));
1085 BulletText(tr(
"Layout issues: Reset workspace layouts from View > Layouts"));
1088 Hide(
"Troubleshooting");
1093 TextWrapped(tr(
"Contributing to YAZE"));
1094 TextWrapped(tr(
"YAZE is open source and welcomes contributions!"));
1096 TextWrapped(tr(
"How to contribute:"));
1097 BulletText(tr(
"Fork the repository on GitHub"));
1098 BulletText(tr(
"Create feature branches for new work"));
1099 BulletText(tr(
"Follow C++ coding standards"));
1100 BulletText(tr(
"Include tests for new features"));
1101 BulletText(tr(
"Submit pull requests for review"));
1104 Hide(
"Contributing");
1112 if (CollapsingHeader(
1115 ImGuiTreeNodeFlags_DefaultOpen)) {
1117 tr(
"Feature status/persistence summaries across desktop/CLI/web"));
1118 BulletText(tr(
"Shortcut/help panels now match configured keybindings"));
1119 BulletText(tr(
"Refined onboarding tips and error messaging"));
1120 BulletText(tr(
"Help text refreshed across desktop, CLI, and web"));
1123 if (CollapsingHeader(
1124 absl::StrFormat(
"%s Development & Build System",
ICON_MD_BUILD)
1126 ImGuiTreeNodeFlags_DefaultOpen)) {
1127 BulletText(tr(
"Asar 65816 assembler integration for ROM patching"));
1128 BulletText(tr(
"z3ed CLI + TUI for scripting, test/doctor, and automation"));
1129 BulletText(tr(
"Modern CMake presets for desktop, AI, and web builds"));
1130 BulletText(tr(
"Unified version + storage references for 0.5.1"));
1133 if (CollapsingHeader(
1135 BulletText(tr(
"Improved project metadata + .yaze storage alignment"));
1136 BulletText(tr(
"Stronger error reporting and status feedback"));
1137 BulletText(tr(
"Performance and stability improvements across editors"));
1138 BulletText(tr(
"Expanded logging and diagnostics tooling"));
1141 if (CollapsingHeader(
1142 absl::StrFormat(
"%s Editor Features",
ICON_MD_EDIT).c_str())) {
1143 BulletText(tr(
"Music editor updates with SPC parsing/playback"));
1145 tr(
"AI agent-assisted editing workflows (multi-provider + vision)"));
1146 BulletText(tr(
"Expanded overworld/dungeon tooling and palette accuracy"));
1147 BulletText(tr(
"Web/WASM preview with collaboration hooks"));
1165 TextWrapped(tr(
"Coming from ZScream?"));
1167 "yaze covers the same Zelda 3 editing surfaces with a modern workspace, "
1168 "projects, and CLI. Use this map to find familiar tools."));
1171 if (CollapsingHeader(absl::StrFormat(
"%s Editors",
ICON_MD_APPS).c_str(),
1172 ImGuiTreeNodeFlags_DefaultOpen)) {
1173 BulletText(tr(
"Overworld Editor ≈ ZScream overworld map canvas + tile16"));
1175 tr(
"Dungeon Editor ≈ underworld rooms (objects, sprites, properties)"));
1176 BulletText(tr(
"Graphics / Sprite Editors ≈ GFX + sprite drawing"));
1177 BulletText(tr(
"Palette Editor ≈ palette groups and edits"));
1178 BulletText(tr(
"Message Editor ≈ dialogue / text"));
1180 tr(
"Screen Editor → Overworld Map ≈ pause-menu map art "
1181 "(not the playable overworld)"));
1184 if (CollapsingHeader(
1186 ImGuiTreeNodeFlags_DefaultOpen)) {
1188 tr(
"Left activity bar opens editor categories (like ZScream "
1191 tr(
"Dungeon workbench defaults to ZScream-style: selectors "
1192 "left, inspector right"));
1193 BulletText(tr(
"Ctrl+Shift+P opens the command palette for any action"));
1195 tr(
"F1 / Help drawer shows context shortcuts for the active "
1199 if (CollapsingHeader(
1200 absl::StrFormat(
"%s Projects & ROM files",
ICON_MD_FOLDER).c_str(),
1201 ImGuiTreeNodeFlags_DefaultOpen)) {
1203 tr(
".yaze / .yazeproj track ROM path, flags, and workspace "
1206 tr(
".zsproj can be opened when present — same Open ROM / "
1209 tr(
"Always keep a clean backup; Backup Before Save is on by "
1212 tr(
"ZSO / ZSCustomOverworld versions are detected from the ROM "
1213 "(vanilla, v1, v2, v3)"));
1216 if (CollapsingHeader(
1218 ImGuiTreeNodeFlags_DefaultOpen)) {
1219 BulletText(tr(
"Tile16 selector / workbench ≈ ZScream tile picker + paint"));
1221 tr(
"Map Properties gates ZSO fields (area size, BG colors, "
1222 "animated GFX) by detected version"));
1223 BulletText(tr(
"Screen Editor → Overworld Map is pause-menu art only"));
1226 if (CollapsingHeader(absl::StrFormat(
"%s Saving",
ICON_MD_SAVE).c_str())) {
1227 BulletText(tr(
"File → Save Scope chooses ROM, project, or both"));
1228 BulletText(tr(
"Autosave can be tuned under Settings → Editor Behavior"));
1229 BulletText(tr(
"ROM Backups lists timestamped copies for restore"));
1234 "Tip: first-run toasts point at this guide once per tip. Reset tips in "
1235 "Settings → Editor Behavior if you want them again."));
1243 TextWrapped(tr(
"Workspace Management"));
1245 "YAZE supports multiple ROM sessions and flexible workspace layouts."));
1248 TextWrapped(tr(
"Session Management:"));
1249 BulletText(tr(
"Ctrl+Shift+N: Create new session"));
1250 BulletText(tr(
"Ctrl+Shift+W: Close current session"));
1251 BulletText(tr(
"Ctrl+Tab: Quick session switcher"));
1252 BulletText(tr(
"Each session maintains its own ROM and editor state"));
1255 TextWrapped(tr(
"Layout Management:"));
1256 BulletText(tr(
"Drag window tabs to dock/undock"));
1257 BulletText(tr(
"Ctrl+Shift+S: Save current layout"));
1258 BulletText(tr(
"Ctrl+Shift+O: Load saved layout"));
1259 BulletText(tr(
"F11: Maximize current window"));
1262 TextWrapped(tr(
"Preset Layouts:"));
1263 BulletText(tr(
"Developer: Code, memory, testing tools"));
1264 BulletText(tr(
"Designer: Graphics, palettes, sprites"));
1265 BulletText(tr(
"Modder: All gameplay editing tools"));
1268 Hide(
"Workspace Help");
1274 TextWrapped(tr(
"You have reached the recommended session limit."));
1275 TextWrapped(tr(
"Having too many sessions open may impact performance."));
1277 TextWrapped(tr(
"Consider closing unused sessions or saving your work."));
1280 Hide(
"Session Limit Warning");
1284 Hide(
"Session Limit Warning");
1291 TextWrapped(tr(
"This will reset your current workspace layout to default."));
1292 TextWrapped(tr(
"Any custom window arrangements will be lost."));
1294 TextWrapped(tr(
"Do you want to continue?"));
1297 Hide(
"Layout Reset Confirm");
1302 Hide(
"Layout Reset Confirm");
1312 tr(
"Choose a workspace preset to quickly configure your layout:"));
1319 const char* description;
1323 PresetInfo presets[] = {
1325 "Essential cards only - maximum editing space",
1328 "Debug and development focused - CPU/Memory/Breakpoints",
1331 "Visual and artistic focused - Graphics/Palettes/Sprites",
1334 "Full-featured - All tools available for comprehensive editing",
1337 "Complete overworld editing toolkit with all map tools",
1340 "Complete dungeon editing toolkit with room tools",
1342 {
"Testing",
ICON_MD_SCIENCE,
"Quality assurance and ROM testing layout",
1348 constexpr int kPresetCount = 8;
1351 float button_width = 200.0f;
1352 float button_height = 50.0f;
1354 for (
int i = 0; i < kPresetCount; i++) {
1360 ImVec2(0.0f, 0.5f));
1361 if (Button(absl::StrFormat(
"%s %s", presets[i].icon, presets[i].name)
1363 ImVec2(button_width, button_height))) {
1365 auto preset = presets[i].getter();
1370 for (
const auto& panel_id : preset.default_visible_panels) {
1371 window_manager.OpenWindow(panel_id);
1377 if (IsItemHovered()) {
1379 TextUnformatted(presets[i].description);
1394 if (current_editor) {
1395 auto current_type = current_editor->
type();
1396 window_manager.ResetToDefaults(0, current_type);
1402 if (Button(tr(
"Close"), ImVec2(-1, 0))) {
1415 Text(tr(
"Active Sessions: %zu"), session_count);
1419 if (BeginTable(
"SessionTable", 4,
1420 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
1421 TableSetupColumn(
"#", ImGuiTableColumnFlags_WidthFixed, 30.0f);
1422 TableSetupColumn(
"ROM", ImGuiTableColumnFlags_WidthStretch);
1423 TableSetupColumn(
"Status", ImGuiTableColumnFlags_WidthFixed, 80.0f);
1424 TableSetupColumn(
"Actions", ImGuiTableColumnFlags_WidthFixed, 120.0f);
1427 for (
size_t i = 0; i < session_count; i++) {
1431 TableSetColumnIndex(0);
1435 TableSetColumnIndex(1);
1436 if (i == active_session) {
1439 TextUnformatted(rom->
filename().c_str());
1441 TextDisabled(tr(
"(No ROM loaded)"));
1444 TextDisabled(tr(
"Session %zu"), i + 1);
1448 TableSetColumnIndex(2);
1449 if (i == active_session) {
1453 TextDisabled(tr(
"Inactive"));
1457 TableSetColumnIndex(3);
1458 PushID(
static_cast<int>(i));
1460 if (i != active_session) {
1461 if (SmallButton(tr(
"Switch"))) {
1467 BeginDisabled(session_count <= 1);
1468 if (SmallButton(tr(
"Close"))) {
1484 if (Button(absl::StrFormat(
"%s New Session",
ICON_MD_ADD).c_str(),
1490 if (Button(tr(
"Close"), ImVec2(-1, 0))) {
1497 SetNextWindowSize(ImVec2(900, 700), ImGuiCond_FirstUseEver);
1498 SetNextWindowSizeConstraints(ImVec2(600, 400), ImVec2(FLT_MAX, FLT_MAX));
1501 TextWrapped(tr(
"Customize your YAZE experience - accessible anytime!"));
1506 float available_height =
1507 GetContentRegionAvail().y - 60;
1508 if (BeginChild(
"DisplaySettingsContent", ImVec2(0, available_height),
true,
1509 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
1518 ImGuiIO& io = GetIO();
1520 Text(tr(
"Global Font Scale"));
1521 float font_global_scale = io.FontGlobalScale;
1522 if (SliderFloat(
"##global_scale", &font_global_scale, 0.5f, 2.0f,
"%.2f")) {
1526 io.FontGlobalScale = font_global_scale;
1534 Hide(
"Display Settings");
1539 using namespace ImGui;
1542 Text(tr(
"Feature Flags Configuration"));
1545 BeginChild(
"##FlagsContent", ImVec2(0, -30),
true);
1550 if (BeginTabBar(
"FlagCategories")) {
1551 if (BeginTabItem(tr(
"Overworld"))) {
1555 if (BeginTabItem(tr(
"Dungeon"))) {
1559 if (BeginTabItem(tr(
"Resources"))) {
1563 if (BeginTabItem(tr(
"System"))) {
1579 using namespace ImGui;
1581 Text(tr(
"Data Integrity Check Results"));
1584 BeginChild(
"##IntegrityContent", ImVec2(0, -30),
true);
1588 Text(tr(
"ROM Data Integrity:"));
1596 Text(tr(
"No issues detected."));
1607 using namespace ImGui;
1610 Text(tr(
"Editor manager unavailable."));
1620 Text(tr(
"Pot Item Save Confirmation"));
1622 TextWrapped(tr(
"Dungeon pot item saving is enabled, but %d of %d rooms are "
1627 tr(
"Saving now can overwrite pot items in unloaded rooms. Choose how to "
1631 if (Button(tr(
"Save without pot items"), ImVec2(0, 0))) {
1638 const bool save_anyway = Button(tr(
"Save anyway"), ImVec2(0, 0));
1639 if (ImGui::GetItemID() != 0) {
1641 "dungeon.pot_item_save_confirm/button:Save anyway",
"button",
1643 "Continue the pending dungeon save with pot item writes enabled");
1652 if (Button(tr(
"Cancel"), ImVec2(0, 0))) {
1660 using namespace ImGui;
1663 Text(tr(
"Editor manager unavailable."));
1679 const auto editable_path =
1680 project && project->hack_manifest.loaded() &&
1681 !project->hack_manifest.build_pipeline().dev_rom.empty()
1682 ? project->GetAbsolutePath(
1683 project->hack_manifest.build_pipeline().dev_rom)
1684 : (project ? project->rom_filename : std::string());
1686 Text(tr(
"ROM Write Confirmation"));
1689 tr(
"The loaded ROM hash does not match the project's expected hash."));
1691 Text(tr(
"Role: %s"), role.c_str());
1692 Text(tr(
"Write policy: %s"), policy.c_str());
1693 Text(tr(
"Loaded ROM: %s"),
1694 actual_path.empty() ?
"(unknown)" : actual_path.c_str());
1695 Text(tr(
"Editable target: %s"),
1696 editable_path.empty() ?
"(unset)" : editable_path.c_str());
1697 Text(tr(
"Expected: %s"), expected.empty() ?
"(unset)" : expected.c_str());
1698 Text(tr(
"Actual: %s"), actual.empty() ?
"(unknown)" : actual.c_str());
1701 "Proceeding will write to the current ROM file. This may corrupt a base "
1702 "or release ROM if it is not the intended editable project ROM."));
1705 const bool save_anyway = Button(tr(
"Save anyway"), ImVec2(0, 0));
1707 "RomWriteConfirm/button:Save anyway",
"button", GetItemID());
1712 if (!status.ok() && !absl::IsCancelled(status)) {
1714 toast->Show(absl::StrFormat(
"Save failed: %s", status.message()),
1721 if (Button(tr(
"Cancel"), ImVec2(0, 0)) || IsKeyPressed(ImGuiKey_Escape)) {
1728 using namespace ImGui;
1731 Text(tr(
"Editor manager unavailable."));
1744 tr(
"The following ROM addresses are owned by ASM hooks and will be "
1745 "overwritten on next build. Saving now will write data that asar "
1749 if (!conflicts.empty()) {
1750 if (BeginTable(
"WriteConflictTable", 3,
1751 ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders |
1752 ImGuiTableFlags_Resizable)) {
1753 TableSetupColumn(
"Address", ImGuiTableColumnFlags_WidthFixed, 120.0f);
1754 TableSetupColumn(
"Ownership", ImGuiTableColumnFlags_WidthFixed, 140.0f);
1755 TableSetupColumn(
"Module", ImGuiTableColumnFlags_WidthStretch);
1758 for (
const auto& conflict : conflicts) {
1761 Text(
"$%06X", conflict.address);
1766 if (!conflict.module.empty()) {
1767 TextUnformatted(conflict.module.c_str());
1769 TextDisabled(tr(
"(unknown)"));
1777 Text(tr(
"%zu conflict(s) detected."), conflicts.size());
1780 if (Button(tr(
"Save Anyway"), ImVec2(0, 0))) {
1784 if (!status.ok() && !absl::IsCancelled(status)) {
1786 toast->Show(absl::StrFormat(
"Save failed: %s", status.message()),
1793 if (Button(tr(
"Cancel"), ImVec2(0, 0)) || IsKeyPressed(ImGuiKey_Escape)) {
1800 using namespace ImGui;
1820 const std::string save_label =
1822 if (!save_label.empty()) {
1829 const std::string continue_label =
1838 IsKeyPressed(ImGuiKey_Escape)) {
The EditorManager controls the main editor window and manages the various editor classes.
void ConfirmPendingUnsavedSessionActionSaveAndContinue()
absl::Status SaveRomAs(const std::string &filename)
void CancelPendingUnsavedSessionAction()
void SwitchToSession(size_t index)
absl::Status RestoreRomBackup(const std::string &backup_path)
size_t GetActiveSessionCount() const
Rom * GetCurrentRom() const override
std::vector< editor::RomFileManager::BackupEntry > GetRomBackups() const
void MarkCurrentProjectDirty()
project::RomWritePolicy GetProjectRomWritePolicy() const
absl::Status CreateNewProjectFromRom(const std::string &template_name, const std::string &rom_path, const std::string &project_name, const std::string &project_path=std::string())
void ConfirmPendingUnsavedSessionActionDiscardAndContinue()
std::string GetCurrentRomHash() const
bool HasPendingUnsavedSessionAction() const
void CancelRomWriteConfirm()
bool IsRomBackupRestorePending() const
absl::Status PruneRomBackups()
void SetFontGlobalScale(float scale)
void ResolvePotItemSaveConfirmation(PotItemSaveDecision decision)
auto GetCurrentEditor() const -> Editor *override
absl::Status OpenRomOrProject(const std::string &filename, LoadOptionsPrompt prompt=LoadOptionsPrompt::kAsk)
WorkspaceWindowManager & window_manager()
std::string GetProjectExpectedRomHash() const
absl::Status SaveProject()
absl::Status DiscardPendingRomBackupRestore()
const std::vector< core::WriteConflict > & pending_write_conflicts() const
std::string GetPendingUnsavedSessionActionContinueLabel() const
std::string GetPendingUnsavedSessionActionPrompt() const
project::RomRole GetProjectRomRole() const
void BypassWriteConflictOnce()
void ClearPendingWriteConflicts()
size_t GetCurrentSessionIndex() const
size_t GetCurrentSessionId() const
Stable workspace identity; unlike the UI index, it does not compact.
project::YazeProject * GetCurrentProject()
bool IsRomHashMismatch() const
absl::Status ResumePendingRomSave()
int pending_pot_item_unloaded_rooms() const
int pending_pot_item_total_rooms() const
void RemoveSession(size_t index)
std::string GetPendingUnsavedSessionActionSaveLabel() const
ToastManager * toast_manager()
static PanelLayoutPreset GetLogicDebuggerPreset()
Get the "logic debugger" workspace preset (QA and debug focused)
static PanelLayoutPreset GetDungeonMasterPreset()
Get the "dungeon master" workspace preset.
static PanelLayoutPreset GetAudioEngineerPreset()
Get the "audio engineer" workspace preset (music focused)
static PanelLayoutPreset GetDesignerPreset()
Get the "designer" workspace preset (visual-focused)
static PanelLayoutPreset GetOverworldArtistPreset()
Get the "overworld artist" workspace preset.
static PanelLayoutPreset GetModderPreset()
Get the "modder" workspace preset (full-featured)
static PanelLayoutPreset GetMinimalPreset()
Get the "minimal" workspace preset (minimal cards)
static PanelLayoutPreset GetDeveloperPreset()
Get the "developer" workspace preset (debug-focused)
void HideAll(size_t session_id)
RAII guard for ImGui style vars.
const Theme & GetCurrentTheme() const
static ThemeManager & Get()
static std::string ShowSaveFileDialog(const std::string &default_name="", const std::string &default_extension="")
ShowSaveFileDialog opens a save file dialog and returns the selected filepath. Uses global feature fl...
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath. Uses global feature flag to...
static std::string ShowOpenFolderDialog()
ShowOpenFolderDialog opens a file dialog and returns the selected folder path. Uses global feature fl...
#define YAZE_VERSION_STRING
#define ICON_MD_FOLDER_OPEN
#define ICON_MD_LANDSCAPE
#define ICON_MD_DOOR_SLIDING
#define ICON_MD_VIDEOGAME_ASSET
#define ICON_MD_BUG_REPORT
#define ICON_MD_MUSIC_NOTE
#define ICON_MD_DISPLAY_SETTINGS
#define ICON_MD_CHECK_CIRCLE
#define ICON_MD_DESCRIPTION
#define ICON_MD_DASHBOARD
#define ICON_MD_OPEN_IN_NEW
#define ICON_MD_CONTENT_COPY
#define ICON_MD_CROP_FREE
#define ICON_MD_DELETE_SWEEP
std::string AddressOwnershipToString(AddressOwnership ownership)
PopupType
Type classification for popups to enable future filtering and organization.
ImVec4 ConvertColorToImVec4(const Color &color)
void DrawDisplaySettingsForPopup(ImGuiStyle *ref)
constexpr ImVec2 kDefaultModalSize
void TextWithSeparators(const absl::string_view &text)
std::string RomRoleToString(RomRole role)
std::string RomWritePolicyToString(RomWritePolicy policy)
std::string HexLongLong(uint64_t qword, HexStringParams params)
FileDialogOptions MakeRomFileDialogOptions(bool include_all_files)
const char * LayoutName(LayoutDialect dialect)
OverworldRomProfile DetectOverworldRomProfile(const Rom &rom)
Room transition destination.
Defines default panel visibility for an editor type.
std::string labels_filename
std::string GetAbsolutePath(const std::string &relative_path) const
Public YAZE API umbrella header.