yaze 0.2.0
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
editor_manager.cc
Go to the documentation of this file.
1#include "editor_manager.h"
2
3#include "absl/status/status.h"
4#include "absl/strings/match.h"
17#include "app/emu/emulator.h"
18#include "app/gui/icons.h"
19#include "app/gui/input.h"
20#include "app/gui/style.h"
21#include "app/rom.h"
22#include "imgui/imgui.h"
23#include "imgui/misc/cpp/imgui_stdlib.h"
24
25namespace yaze {
26namespace app {
27namespace editor {
28
29using namespace ImGui;
30using core::FileDialogWrapper;
31
32namespace {
33
34bool BeginCentered(const char* name) {
35 ImGuiIO const& io = GetIO();
36 ImVec2 pos(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f);
37 SetNextWindowPos(pos, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
38 ImGuiWindowFlags flags =
39 ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration |
40 ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings;
41 return Begin(name, nullptr, flags);
42}
43
44bool IsEditorActive(Editor* editor, std::vector<Editor*>& active_editors) {
45 return std::find(active_editors.begin(), active_editors.end(), editor) !=
46 active_editors.end();
47}
48
49} // namespace
50
51void EditorManager::SetupScreen(std::string filename) {
52 if (!filename.empty()) {
53 PRINT_IF_ERROR(rom()->LoadFromFile(filename));
54 }
56}
57
58absl::Status EditorManager::Update() {
60
65
66 if (rom()->is_loaded() && !rom_assets_loaded_) {
67 // Load all of the graphics data from the game.
68 RETURN_IF_ERROR(rom()->LoadAllGraphicsData())
69 // Initialize overworld graphics, maps, and palettes
71 rom_assets_loaded_ = true;
72 }
73
75
76 return absl::OkStatus();
77}
78
80 // Show popup pane to select an editor to add
81 static bool show_add_editor = false;
82 if (show_add_editor) OpenPopup("AddEditor");
83
84 if (BeginPopup("AddEditor", ImGuiWindowFlags_AlwaysAutoResize)) {
85 if (MenuItem("Overworld", nullptr, false,
86 !IsEditorActive(&overworld_editor_, active_editors_))) {
88 CloseCurrentPopup();
89 }
90 if (MenuItem("Dungeon", nullptr, false,
91 !IsEditorActive(&dungeon_editor_, active_editors_))) {
93 CloseCurrentPopup();
94 }
95 if (MenuItem("Graphics", nullptr, false,
96 !IsEditorActive(&graphics_editor_, active_editors_))) {
98 CloseCurrentPopup();
99 }
100 if (MenuItem("Music", nullptr, false,
101 !IsEditorActive(&music_editor_, active_editors_))) {
102 active_editors_.push_back(&music_editor_);
103 CloseCurrentPopup();
104 }
105 if (MenuItem("Palette", nullptr, false,
106 !IsEditorActive(&palette_editor_, active_editors_))) {
108 CloseCurrentPopup();
109 }
110 if (MenuItem("Screen", nullptr, false,
111 !IsEditorActive(&screen_editor_, active_editors_))) {
113 CloseCurrentPopup();
114 }
115 if (MenuItem("Sprite", nullptr, false,
116 !IsEditorActive(&sprite_editor_, active_editors_))) {
118 CloseCurrentPopup();
119 }
120 if (MenuItem("Code", nullptr, false,
121 !IsEditorActive(&assembly_editor_, active_editors_))) {
123 CloseCurrentPopup();
124 }
125 if (MenuItem("Message", nullptr, false,
126 !IsEditorActive(&message_editor_, active_editors_))) {
128 CloseCurrentPopup();
129 }
130 if (MenuItem("Settings", nullptr, false,
131 !IsEditorActive(&settings_editor_, active_editors_))) {
133 CloseCurrentPopup();
134 }
135 EndPopup();
136 }
137
138 if (!IsPopupOpen("AddEditor")) {
139 show_add_editor = false;
140 }
141
142 if (BeginTabBar("##TabBar", ImGuiTabBarFlags_Reorderable |
143 ImGuiTabBarFlags_AutoSelectNewTabs)) {
144 for (auto editor : active_editors_) {
145 bool open = true;
146 switch (editor->type()) {
148 if (overworld_editor_.jump_to_tab() == -1) {
149 if (BeginTabItem("Overworld", &open)) {
152 EndTabItem();
153 }
154 }
155 break;
157 if (BeginTabItem("Dungeon", &open)) {
160 if (overworld_editor_.jump_to_tab() != -1) {
163 }
164 EndTabItem();
165 }
166 break;
168 if (BeginTabItem("Graphics", &open)) {
171 EndTabItem();
172 }
173 break;
175 if (BeginTabItem("Music", &open)) {
177
179 EndTabItem();
180 }
181 break;
183 if (BeginTabItem("Palette", &open)) {
186 EndTabItem();
187 }
188 break;
190 if (BeginTabItem("Screen", &open)) {
193 EndTabItem();
194 }
195 break;
197 if (BeginTabItem("Sprite", &open)) {
200 EndTabItem();
201 }
202 break;
204 if (BeginTabItem("Code", &open)) {
207 EndTabItem();
208 }
209 break;
211 if (BeginTabItem("Settings", &open)) {
214 EndTabItem();
215 }
216 break;
218 if (BeginTabItem("Message", &open)) {
221 EndTabItem();
222 }
223 break;
224 default:
225 break;
226 }
227 if (!open) {
228 active_editors_.erase(
229 std::remove(active_editors_.begin(), active_editors_.end(), editor),
230 active_editors_.end());
231 }
232 }
233
234 if (TabItemButton(ICON_MD_ADD, ImGuiTabItemFlags_Trailing)) {
235 show_add_editor = true;
236 }
237
238 EndTabBar();
239 }
240}
241
243 bool ctrl_or_super = (GetIO().KeyCtrl || GetIO().KeySuper);
244
245 // If CMD + R is pressed, reload the top result of recent files
246 if (IsKeyDown(ImGuiKey_R) && ctrl_or_super) {
247 static RecentFilesManager manager("recent_files.txt");
248 manager.Load();
249 if (!manager.GetRecentFiles().empty()) {
250 auto front = manager.GetRecentFiles().front();
251 OpenRomOrProject(front);
252 }
253 }
254
255 if (IsKeyDown(ImGuiKey_F1)) {
256 about_ = true;
257 }
258
259 // If CMD + Q is pressed, quit the application
260 if (IsKeyDown(ImGuiKey_Q) && ctrl_or_super) {
261 quit_ = true;
262 }
263
264 // If CMD + O is pressed, open a file dialog
265 if (IsKeyDown(ImGuiKey_O) && ctrl_or_super) {
266 LoadRom();
267 }
268
269 // If CMD + S is pressed, save the current ROM
270 if (IsKeyDown(ImGuiKey_S) && ctrl_or_super) {
271 SaveRom();
272 }
273
274 if (IsKeyDown(ImGuiKey_X) && ctrl_or_super) {
276 }
277
278 if (IsKeyDown(ImGuiKey_C) && ctrl_or_super) {
280 }
281
282 if (IsKeyDown(ImGuiKey_V) && ctrl_or_super) {
284 }
285
286 if (IsKeyDown(ImGuiKey_Z) && ctrl_or_super) {
288 }
289
290 if (IsKeyDown(ImGuiKey_Y) && ctrl_or_super) {
292 }
293
294 if (IsKeyDown(ImGuiKey_F) && ctrl_or_super) {
296 }
297}
298
300 static absl::Status prev_status;
301 if (!status_.ok()) {
302 show_status_ = true;
303 prev_status = status_;
304 }
305
306 if (show_status_ && (BeginCentered("StatusWindow"))) {
307 Text("%s", ICON_MD_ERROR);
308 Text("%s", prev_status.ToString().c_str());
309 Spacing();
310 NextColumn();
311 Columns(1);
312 Separator();
313 NewLine();
314 SameLine(128);
315 if (Button("OK", gui::kDefaultModalSize) ||
316 IsKeyPressed(GetKeyIndex(ImGuiKey_Space))) {
317 show_status_ = false;
318 status_ = absl::OkStatus();
319 }
320 SameLine();
321 if (Button(ICON_MD_CONTENT_COPY, ImVec2(50, 0))) {
322 SetClipboardText(prev_status.ToString().c_str());
323 }
324 End();
325 }
326}
327
329 if (about_) OpenPopup("About");
330 if (BeginPopupModal("About", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
331 Text("Yet Another Zelda3 Editor - v%s", core::kYazeVersion.data());
332 Text("Written by: scawful");
333 Spacing();
334 Text("Special Thanks: Zarby89, JaredBrian");
335 Separator();
336
337 if (Button("Close", gui::kDefaultModalSize)) {
338 about_ = false;
339 CloseCurrentPopup();
340 }
341 EndPopup();
342 }
343}
344
346 if (rom_info_) OpenPopup("ROM Information");
347 if (BeginPopupModal("ROM Information", nullptr,
348 ImGuiWindowFlags_AlwaysAutoResize)) {
349 Text("Title: %s", rom()->title().c_str());
350 Text("ROM Size: %s", core::UppercaseHexLongLong(rom()->size()).c_str());
351
352 if (Button("Close", gui::kDefaultModalSize) ||
353 IsKeyPressed(GetKeyIndex(ImGuiKey_Space))) {
354 rom_info_ = false;
355 CloseCurrentPopup();
356 }
357 EndPopup();
358 }
359}
360
362 static bool show_display_settings = false;
363
364 if (BeginMenuBar()) {
366 SameLine(GetWindowWidth() - GetStyle().ItemSpacing.x -
367 CalcTextSize(ICON_MD_DISPLAY_SETTINGS).x - 110);
368 PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
369 if (Button(ICON_MD_DISPLAY_SETTINGS)) {
370 show_display_settings = !show_display_settings;
371 }
372 PopStyleColor();
373 Text("yaze v%s", core::kYazeVersion.data());
374 EndMenuBar();
375 }
376
377 if (show_display_settings) {
378 Begin("Display Settings", &show_display_settings, ImGuiWindowFlags_None);
380 End();
381 }
382}
383
385 static bool save_as_menu = false;
386 static bool new_project_menu = false;
387
388 if (BeginMenu("File")) {
389 if (MenuItem("Open", "Ctrl+O")) {
390 LoadRom();
391 }
392
393 if (BeginMenu("Open Recent")) {
394 static RecentFilesManager manager("recent_files.txt");
395 manager.Load();
396 if (manager.GetRecentFiles().empty()) {
397 MenuItem("No Recent Files", nullptr, false, false);
398 } else {
399 for (const auto& filePath : manager.GetRecentFiles()) {
400 if (MenuItem(filePath.c_str())) {
401 OpenRomOrProject(filePath);
402 }
403 }
404 }
405 EndMenu();
406 }
407
408 MENU_ITEM2("Save", "Ctrl+S") {
409 if (rom()->is_loaded()) {
410 SaveRom();
411 }
412 }
413 MENU_ITEM("Save As..") { save_as_menu = true; }
414
415 if (rom()->is_loaded()) {
416 MENU_ITEM("Close") {
417 status_ = rom()->Close();
418 rom_assets_loaded_ = false;
419 }
420 }
421
422 Separator();
423
424 if (BeginMenu("Project")) {
425 if (MenuItem("Create New Project")) {
426 // Create a new project
427 new_project_menu = true;
428 }
429 if (MenuItem("Open Project")) {
430 // Open an existing project
433 if (status_.ok()) {
435 }
436 }
437 if (MenuItem("Save Project")) {
438 // Save the current project
440 }
441
442 EndMenu();
443 }
444
445 if (BeginMenu("Options")) {
446 MenuItem("Backup ROM", "", &backup_rom_);
447 MenuItem("Save New Auto", "", &save_new_auto_);
448 Separator();
449 if (BeginMenu("Experiment Flags")) {
450 static FlagsMenu flags_menu;
451 flags_menu.Draw();
452 EndMenu();
453 }
454 EndMenu();
455 }
456
457 Separator();
458
459 if (MenuItem("Quit", "Ctrl+Q")) {
460 quit_ = true;
461 }
462
463 EndMenu();
464 }
465
466 if (save_as_menu) {
467 static std::string save_as_filename = "";
468 Begin("Save As..", &save_as_menu, ImGuiWindowFlags_AlwaysAutoResize);
469 InputText("Filename", &save_as_filename);
470 if (Button("Save", gui::kDefaultModalSize)) {
471 SaveRom();
472 save_as_menu = false;
473 }
474 SameLine();
475 if (Button("Cancel", gui::kDefaultModalSize)) {
476 save_as_menu = false;
477 }
478 End();
479 }
480
481 if (new_project_menu) {
482 Begin("New Project", &new_project_menu, ImGuiWindowFlags_AlwaysAutoResize);
483 static std::string save_as_filename = "";
484 InputText("Project Name", &save_as_filename);
485 if (Button("Destination Filepath", gui::kDefaultModalSize)) {
487 }
488 SameLine();
489 Text("%s", current_project_.filepath.c_str());
490 if (Button("ROM File", gui::kDefaultModalSize)) {
492 }
493 SameLine();
494 Text("%s", current_project_.rom_filename_.c_str());
495 if (Button("Labels File", gui::kDefaultModalSize)) {
498 }
499 SameLine();
500 Text("%s", current_project_.labels_filename_.c_str());
501 if (Button("Code Folder", gui::kDefaultModalSize)) {
503 }
504 SameLine();
505 Text("%s", current_project_.code_folder_.c_str());
506
507 Separator();
508 if (Button("Create", gui::kDefaultModalSize)) {
509 new_project_menu = false;
510 status_ = current_project_.Create(save_as_filename);
511 if (status_.ok()) {
513 }
514 }
515 SameLine();
516 if (Button("Cancel", gui::kDefaultModalSize)) {
517 new_project_menu = false;
518 }
519 End();
520 }
521
522 if (BeginMenu("Edit")) {
523 MENU_ITEM2("Undo", "Ctrl+Z") { status_ = current_editor_->Undo(); }
524 MENU_ITEM2("Redo", "Ctrl+Y") { status_ = current_editor_->Redo(); }
525 Separator();
526 MENU_ITEM2("Cut", "Ctrl+X") { status_ = current_editor_->Cut(); }
527 MENU_ITEM2("Copy", "Ctrl+C") { status_ = current_editor_->Copy(); }
528 MENU_ITEM2("Paste", "Ctrl+V") { status_ = current_editor_->Paste(); }
529 Separator();
530 MENU_ITEM2("Find", "Ctrl+F") { status_ = current_editor_->Find(); }
531 EndMenu();
532 }
533
534 static bool show_imgui_metrics = false;
535 static bool show_memory_editor = false;
536 static bool show_asm_editor = false;
537 static bool show_imgui_demo = false;
538 static bool show_palette_editor = false;
539 static bool show_emulator = false;
540
541 if (show_imgui_demo) ShowDemoWindow();
542 if (show_imgui_metrics) ShowMetricsWindow(&show_imgui_metrics);
543 if (show_memory_editor) memory_editor_.Update(show_memory_editor);
544 if (show_asm_editor) assembly_editor_.Update(show_asm_editor);
545
546 if (show_emulator) {
547 Begin("Emulator", &show_emulator, ImGuiWindowFlags_MenuBar);
548 emulator_.Run();
549 End();
550 }
551
552 if (show_palette_editor) {
553 Begin("Palette Editor", &show_palette_editor);
555 End();
556 }
557
558 if (BeginMenu("View")) {
559 MenuItem("Emulator", nullptr, &show_emulator);
560 Separator();
561 MenuItem("Memory Editor", nullptr, &show_memory_editor);
562 MenuItem("Assembly Editor", nullptr, &show_asm_editor);
563 MenuItem("Palette Editor", nullptr, &show_palette_editor);
564 Separator();
565 MENU_ITEM("ROM Information") rom_info_ = true;
566 Separator();
567 MenuItem("ImGui Demo", nullptr, &show_imgui_demo);
568 MenuItem("ImGui Metrics", nullptr, &show_imgui_metrics);
569 EndMenu();
570 }
571
572 static bool show_resource_label_manager = false;
574 // Project Menu
575 if (BeginMenu("Project")) {
576 Text("Name: %s", current_project_.name.c_str());
577 Text("ROM: %s", current_project_.rom_filename_.c_str());
578 Text("Labels: %s", current_project_.labels_filename_.c_str());
579 Text("Code: %s", current_project_.code_folder_.c_str());
580 Separator();
581 MenuItem("Resource Labels", nullptr, &show_resource_label_manager);
582 EndMenu();
583 }
584 }
585
586 static bool open_rom_help = false;
587 static bool open_supported_features = false;
588 static bool open_manage_project = false;
589 if (BeginMenu("Help")) {
590 if (MenuItem("How to open a ROM")) open_rom_help = true;
591 if (MenuItem("Supported Features")) open_supported_features = true;
592 if (MenuItem("How to manage a project")) open_manage_project = true;
593
594 if (MenuItem("About", "F1")) about_ = true;
595 EndMenu();
596 }
597
598 if (open_supported_features) OpenPopup("Supported Features");
599 if (BeginPopupModal("Supported Features", nullptr,
600 ImGuiWindowFlags_AlwaysAutoResize)) {
601 Text("Overworld");
602 BulletText("LW/DW/SW Tilemap Editing");
603 BulletText("LW/DW/SW Map Properties");
604 BulletText("Create/Delete/Update Entrances");
605 BulletText("Create/Delete/Update Exits");
606 BulletText("Create/Delete/Update Sprites");
607 BulletText("Create/Delete/Update Items");
608
609 Text("Dungeon");
610 BulletText("View Room Header Properties");
611 BulletText("View Entrance Properties");
612
613 Text("Graphics");
614 BulletText("View Decompressed Graphics Sheets");
615 BulletText("View/Update Graphics Groups");
616
617 Text("Palettes");
618 BulletText("View Palette Groups");
619
620 Text("Saveable");
621 BulletText("All Listed Overworld Features");
622 BulletText("Hex Editor Changes");
623
624 if (Button("Close", gui::kDefaultModalSize)) {
625 open_supported_features = false;
626 CloseCurrentPopup();
627 }
628 EndPopup();
629 }
630
631 if (open_rom_help) OpenPopup("Open a ROM");
632 if (BeginPopupModal("Open a ROM", nullptr,
633 ImGuiWindowFlags_AlwaysAutoResize)) {
634 Text("File -> Open");
635 Text("Select a ROM file to open");
636 Text("Supported ROMs (headered or unheadered):");
637 Text("The Legend of Zelda: A Link to the Past");
638 Text("US Version 1.0");
639 Text("JP Version 1.0");
640
641 if (Button("Close", gui::kDefaultModalSize)) {
642 open_rom_help = false;
643 CloseCurrentPopup();
644 }
645 EndPopup();
646 }
647
648 if (open_manage_project) OpenPopup("Manage Project");
649 if (BeginPopupModal("Manage Project", nullptr,
650 ImGuiWindowFlags_AlwaysAutoResize)) {
651 Text("Project Menu");
652 Text("Create a new project or open an existing one.");
653 Text("Save the project to save the current state of the project.");
654 TextWrapped(
655 "To save a project, you need to first open a ROM and initialize your "
656 "code path and labels file. Label resource manager can be found in "
657 "the View menu. Code path is set in the Code editor after opening a "
658 "folder.");
659
660 if (Button("Close", gui::kDefaultModalSize)) {
661 open_manage_project = false;
662 CloseCurrentPopup();
663 }
664 EndPopup();
665 }
666
667 if (show_resource_label_manager) {
668 rom()->resource_label()->DisplayLabels(&show_resource_label_manager);
671 current_project_.labels_filename_ = rom()->resource_label()->filename_;
672 }
673 }
674}
675
677 auto file_name = FileDialogWrapper::ShowOpenFileDialog();
678 auto load_rom = rom()->LoadFromFile(file_name);
679 if (load_rom.ok()) {
680 static RecentFilesManager manager("recent_files.txt");
681 manager.Load();
682 manager.AddFile(file_name);
683 manager.Save();
684 }
685}
686
688 if (flags()->kSaveDungeonMaps) {
691 }
692
695
696 status_ = rom()->SaveToFile(backup_rom_, save_new_auto_);
697}
698
699void EditorManager::OpenRomOrProject(const std::string& filename) {
700 if (absl::StrContains(filename, ".yaze")) {
701 status_ = current_project_.Open(filename);
702 if (status_.ok()) {
704 }
705 } else {
706 status_ = rom()->LoadFromFile(filename);
707 }
708}
709
712
713 if (!rom()->resource_label()->LoadLabels(current_project_.labels_filename_)) {
714 return absl::InternalError(
715 "Could not load labels file, update your project file.");
716 }
717
718 static RecentFilesManager manager("recent_files.txt");
719 manager.Load();
721 ".yaze");
722 manager.Save();
723
725
727
728 return absl::OkStatus();
729}
730
731} // namespace editor
732} // namespace app
733} // namespace yaze
static std::string ShowOpenFileDialog()
static std::string ShowOpenFolderDialog()
void OpenFolder(const std::string &folder_path)
absl::Status Update() override
void SetupScreen(std::string filename="")
void OpenRomOrProject(const std::string &filename)
std::vector< Editor * > active_editors_
MemoryEditorWithDiffChecker memory_editor_
Interface for editor classes.
Definition editor.h:39
virtual absl::Status Cut()=0
virtual absl::Status Undo()=0
virtual absl::Status Find()=0
virtual absl::Status Redo()=0
virtual absl::Status Copy()=0
virtual absl::Status Paste()=0
absl::Status Update() override
absl::Status Update() override
absl::Status LoadGraphics()
Load the Bitmap objects for each OverworldMap.
absl::Status Update() override
const std::vector< std::string > & GetRecentFiles() const
void AddFile(const std::string &filePath)
absl::Status Update() override
absl::Status Update() override
Updates the sprite editor.
#define MENU_ITEM(w)
Definition constants.h:16
#define PRINT_IF_ERROR(expression)
Definition constants.h:43
#define RETURN_IF_ERROR(expression)
Definition constants.h:69
#define MENU_ITEM2(w, v)
Definition constants.h:17
#define RETURN_VOID_IF_ERROR(expression)
Definition constants.h:60
#define ICON_MD_ERROR
Definition icons.h:684
#define ICON_MD_DISPLAY_SETTINGS
Definition icons.h:585
#define ICON_MD_ADD
Definition icons.h:84
#define ICON_MD_CONTENT_COPY
Definition icons.h:463
Definition input.cc:17
std::string UppercaseHexLongLong(uint64_t qword)
Definition labeling.cc:37
constexpr std::string_view kYazeVersion
Definition constants.h:127
bool IsEditorActive(Editor *editor, std::vector< Editor * > &active_editors)
constexpr ImVec2 kDefaultModalSize
Definition input.h:26
void DrawDisplaySettings(ImGuiStyle *ref)
Definition style.cc:60
Definition common.cc:21
std::string name
Definition project.h:106
std::string filepath
Definition project.h:108
std::string code_folder_
Definition project.h:110
absl::Status Open(const std::string &project_path)
Definition project.h:45
std::string labels_filename_
Definition project.h:111
absl::Status Create(const std::string &project_name)
Creates a new project.
Definition project.h:39
absl::Status Save()
Definition project.h:73
std::string rom_filename_
Definition project.h:109
void Update(bool &show_memory_editor)