yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
graphics_editor.cc
Go to the documentation of this file.
1#include "graphics_editor.h"
2
3#include <filesystem>
4
5#include "absl/status/status.h"
6#include "absl/status/statusor.h"
7#include "absl/strings/str_cat.h"
9#include "util/file_util.h"
10#include "app/core/window.h"
11#include "app/gfx/arena.h"
12#include "app/gfx/bitmap.h"
13#include "app/gfx/compression.h"
14#include "app/gfx/scad_format.h"
16#include "app/gfx/snes_tile.h"
17#include "app/gui/canvas.h"
18#include "app/gui/color.h"
19#include "app/gui/icons.h"
20#include "app/gui/input.h"
22#include "app/gui/style.h"
23#include "app/rom.h"
25#include "imgui/imgui.h"
26#include "imgui/misc/cpp/imgui_stdlib.h"
27#include "imgui_memory_editor.h"
28#include "util/log.h"
29
30namespace yaze {
31namespace editor {
32
34using ImGui::Button;
35using ImGui::InputInt;
36using ImGui::InputText;
37using ImGui::SameLine;
38using ImGui::TableNextColumn;
39
40constexpr ImGuiTableFlags kGfxEditTableFlags =
41 ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
42 ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable |
43 ImGuiTableFlags_SizingFixedFit;
44
46 // Register cards with EditorCardManager during initialization (once)
47 auto& card_manager = gui::EditorCardManager::Get();
48
49 card_manager.RegisterCard({
50 .card_id = "graphics.sheet_editor",
51 .display_name = "Sheet Editor",
52 .icon = ICON_MD_EDIT,
53 .category = "Graphics",
54 .shortcut_hint = "Ctrl+Shift+1",
55 .visibility_flag = &show_sheet_editor_,
56 .priority = 10
57 });
58
59 card_manager.RegisterCard({
60 .card_id = "graphics.sheet_browser",
61 .display_name = "Sheet Browser",
62 .icon = ICON_MD_VIEW_LIST,
63 .category = "Graphics",
64 .shortcut_hint = "Ctrl+Shift+2",
65 .visibility_flag = &show_sheet_browser_,
66 .priority = 20
67 });
68
69 card_manager.RegisterCard({
70 .card_id = "graphics.player_animations",
71 .display_name = "Player Animations",
72 .icon = ICON_MD_PERSON,
73 .category = "Graphics",
74 .shortcut_hint = "Ctrl+Shift+3",
75 .visibility_flag = &show_player_animations_,
76 .priority = 30
77 });
78
79 card_manager.RegisterCard({
80 .card_id = "graphics.prototype_viewer",
81 .display_name = "Prototype Viewer",
83 .category = "Graphics",
84 .shortcut_hint = "Ctrl+Shift+4",
85 .visibility_flag = &show_prototype_viewer_,
86 .priority = 40
87 });
88}
89
90absl::Status GraphicsEditor::Load() {
91 gfx::ScopedTimer timer("GraphicsEditor::Load");
92
93 // Initialize all graphics sheets with appropriate palettes from ROM
94 // This ensures textures are created for editing
95 if (rom()->is_loaded()) {
96 auto& sheets = gfx::Arena::Get().gfx_sheets();
97
98 // Apply default palettes to all sheets based on common SNES ROM structure
99 // Sheets 0-112: Use overworld/dungeon palettes
100 // Sheets 113-127: Use sprite palettes
101 // Sheets 128-222: Use auxiliary/menu palettes
102
103 LOG_INFO("GraphicsEditor", "Initializing textures for %d graphics sheets", kNumGfxSheets);
104
105 int sheets_queued = 0;
106 for (int i = 0; i < kNumGfxSheets; i++) {
107 if (!sheets[i].is_active() || !sheets[i].surface()) {
108 continue; // Skip inactive or surface-less sheets
109 }
110
111 // Palettes are now applied during ROM loading in LoadAllGraphicsData()
112 // Just queue texture creation for sheets that don't have textures yet
113 if (!sheets[i].texture()) {
116 sheets_queued++;
117 }
118 }
119
120 LOG_INFO("GraphicsEditor", "Queued texture creation for %d graphics sheets", sheets_queued);
121 }
122
123 return absl::OkStatus();
124}
125
127 DrawToolset();
129
130 // Create session-aware cards (non-static for multi-session support)
131 gui::EditorCard sheet_editor_card(MakeCardTitle("Sheet Editor").c_str(), ICON_MD_EDIT);
132 gui::EditorCard sheet_browser_card(MakeCardTitle("Sheet Browser").c_str(), ICON_MD_VIEW_LIST);
133 gui::EditorCard player_anims_card(MakeCardTitle("Player Animations").c_str(), ICON_MD_PERSON);
134 gui::EditorCard prototype_card(MakeCardTitle("Prototype Viewer").c_str(), ICON_MD_CONSTRUCTION);
135
136 if (show_sheet_editor_) {
137 if (sheet_editor_card.Begin(&show_sheet_editor_)) {
139 }
140 sheet_editor_card.End(); // ALWAYS call End after Begin
141 }
142
144 if (sheet_browser_card.Begin(&show_sheet_browser_)) {
145 if (asset_browser_.Initialized == false) {
147 }
148 asset_browser_.Draw(gfx::Arena::Get().gfx_sheets());
149 }
150 sheet_browser_card.End(); // ALWAYS call End after Begin
151 }
152
154 if (player_anims_card.Begin(&show_player_animations_)) {
156 }
157 player_anims_card.End(); // ALWAYS call End after Begin
158 }
159
161 if (prototype_card.Begin(&show_prototype_viewer_)) {
163 }
164 prototype_card.End(); // ALWAYS call End after Begin
165 }
166
168 return absl::OkStatus();
169}
170
172 static gui::Toolset toolbar;
173 toolbar.Begin();
174
175 if (toolbar.AddAction(ICON_MD_EDIT, "Sheet Editor")) {
177 }
178 if (toolbar.AddAction(ICON_MD_VIEW_LIST, "Sheet Browser")) {
180 }
181 if (toolbar.AddAction(ICON_MD_PERSON, "Player Animations")) {
183 }
184 if (toolbar.AddAction(ICON_MD_CONSTRUCTION, "Prototype Viewer")) {
186 }
187
188 toolbar.End();
189}
190
191
193 if (ImGui::BeginTable("##GfxEditTable", 3, kGfxEditTableFlags,
194 ImVec2(0, 0))) {
195 for (const auto& name :
196 {"Tilesheets", "Current Graphics", "Palette Controls"})
197 ImGui::TableSetupColumn(name);
198
199 ImGui::TableHeadersRow();
200 ImGui::TableNextColumn();
202
203 ImGui::TableNextColumn();
204 if (rom()->is_loaded()) {
207 }
208
209 ImGui::TableNextColumn();
210 if (rom()->is_loaded()) {
212 }
213 }
214 ImGui::EndTable();
215
216 return absl::OkStatus();
217}
218
235 if (ImGui::BeginTable("##GfxEditToolset", 9, ImGuiTableFlags_SizingFixedFit,
236 ImVec2(0, 0))) {
237 for (const auto& name :
238 {"Select", "Pencil", "Fill", "Copy Sheet", "Paste Sheet", "Zoom Out",
239 "Zoom In", "Current Color", "Tile Size"})
240 ImGui::TableSetupColumn(name);
241
242 TableNextColumn();
243 if (Button(ICON_MD_SELECT_ALL)) {
245 }
246
247 TableNextColumn();
248 if (Button(ICON_MD_DRAW)) {
250 }
251 HOVER_HINT("Draw with current color");
252
253 TableNextColumn();
254 if (Button(ICON_MD_FORMAT_COLOR_FILL)) {
256 }
257 HOVER_HINT("Fill with current color");
258
259 TableNextColumn();
260 if (Button(ICON_MD_CONTENT_COPY)) {
261 status_ = absl::UnimplementedError("PNG export functionality removed");
262 }
263 HOVER_HINT("Copy to Clipboard");
264
265 TableNextColumn();
266 if (Button(ICON_MD_CONTENT_PASTE)) {
267 status_ = absl::UnimplementedError("PNG import functionality removed");
268 }
269 HOVER_HINT("Paste from Clipboard");
270
271 TableNextColumn();
272 if (Button(ICON_MD_ZOOM_OUT)) {
273 if (current_scale_ >= 0.0f) {
274 current_scale_ -= 1.0f;
275 }
276 }
277
278 TableNextColumn();
279 if (Button(ICON_MD_ZOOM_IN)) {
280 if (current_scale_ <= 16.0f) {
281 current_scale_ += 1.0f;
282 }
283 }
284
285 TableNextColumn();
286 // Enhanced palette color picker with SNES-specific features
287 auto bitmap = gfx::Arena::Get().gfx_sheets()[current_sheet_];
288 auto palette = bitmap.palette();
289
290 // Display palette colors in a grid layout for better ROM hacking workflow
291 for (int i = 0; i < palette.size(); i++) {
292 if (i > 0 && i % 8 == 0) {
293 ImGui::NewLine(); // New row every 8 colors (SNES palette standard)
294 }
295 ImGui::SameLine();
296
297 // Convert SNES color to ImGui format with proper scaling
298 auto color = ImVec4(palette[i].rgb().x / 255.0f, palette[i].rgb().y / 255.0f,
299 palette[i].rgb().z / 255.0f, 1.0f);
300
301 // Enhanced color button with tooltip showing SNES color value
302 if (ImGui::ColorButton(absl::StrFormat("Palette Color %d", i).c_str(),
303 color, ImGuiColorEditFlags_NoTooltip)) {
304 current_color_ = color;
305 }
306
307 // Add tooltip with SNES color information
308 if (ImGui::IsItemHovered()) {
309 ImGui::SetTooltip("SNES Color: $%04X\nRGB: (%d, %d, %d)",
310 palette[i].snes(),
311 static_cast<int>(palette[i].rgb().x),
312 static_cast<int>(palette[i].rgb().y),
313 static_cast<int>(palette[i].rgb().z));
314 }
315 }
316
317 TableNextColumn();
318 gui::InputHexByte("Tile Size", &tile_size_);
319
320 ImGui::EndTable();
321 }
322}
323
325 ImGui::BeginChild(
326 "##GfxEditChild", ImVec2(0, 0), true,
327 ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysVerticalScrollbar);
328 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
329 // TODO: Update the interaction for multi select on sheets
330 static ImGuiSelectionBasicStorage selection;
331 ImGuiMultiSelectFlags flags =
332 ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
333 ImGuiMultiSelectIO* ms_io =
334 ImGui::BeginMultiSelect(flags, selection.Size, kNumGfxSheets);
335 selection.ApplyRequests(ms_io);
336 ImGuiListClipper clipper;
337 clipper.Begin(kNumGfxSheets);
338 if (ms_io->RangeSrcItem != -1)
339 clipper.IncludeItemByIndex(
340 (int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
341
342 int key = 0;
343 for (auto& value : gfx::Arena::Get().gfx_sheets()) {
344 ImGui::BeginChild(absl::StrFormat("##GfxSheet%02X", key).c_str(),
345 ImVec2(0x100 + 1, 0x40 + 1), true,
346 ImGuiWindowFlags_NoDecoration);
347 ImGui::PopStyleVar();
348
349 graphics_bin_canvas_.DrawBackground(ImVec2(0x100 + 1, 0x40 + 1));
351 if (value.is_active()) {
352 // Ensure texture exists for active sheets
353 if (!value.texture() && value.surface()) {
356 }
357
358 auto texture = value.texture();
359 if (texture) {
361 (ImTextureID)(intptr_t)texture,
362 ImVec2(graphics_bin_canvas_.zero_point().x + 2,
365 value.width() * sheet_scale_,
367 value.height() * sheet_scale_));
368
369 if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
370 current_sheet_ = key;
371 open_sheets_.insert(key);
372 }
373
374 // Add a slightly transparent rectangle behind the text
375 ImVec2 text_pos(graphics_bin_canvas_.zero_point().x + 2,
377 ImVec2 text_size =
378 ImGui::CalcTextSize(absl::StrFormat("%02X", key).c_str());
379 ImVec2 rect_min(text_pos.x, text_pos.y);
380 ImVec2 rect_max(text_pos.x + text_size.x, text_pos.y + text_size.y);
381
382 graphics_bin_canvas_.draw_list()->AddRectFilled(rect_min, rect_max,
383 IM_COL32(0, 125, 0, 128));
384
386 text_pos, IM_COL32(125, 255, 125, 255),
387 absl::StrFormat("%02X", key).c_str());
388 }
389 key++;
390 }
393
394 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
395 ImGui::EndChild();
396 }
397 ImGui::PopStyleVar();
398 ms_io = ImGui::EndMultiSelect();
399 selection.ApplyRequests(ms_io);
400 ImGui::EndChild();
401 return absl::OkStatus();
402}
403
405 gfx::ScopedTimer timer("graphics_editor_update_gfx_tab_view");
406
407 static int next_tab_id = 0;
408 constexpr ImGuiTabBarFlags kGfxEditTabBarFlags =
409 ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable |
410 ImGuiTabBarFlags_FittingPolicyResizeDown |
411 ImGuiTabBarFlags_TabListPopupButton;
412
413 if (ImGui::BeginTabBar("##GfxEditTabBar", kGfxEditTabBarFlags)) {
414 if (ImGui::TabItemButton(ICON_MD_ADD, ImGuiTabItemFlags_Trailing |
415 ImGuiTabItemFlags_NoTooltip)) {
416 open_sheets_.insert(next_tab_id++);
417 }
418
419 for (auto& sheet_id : open_sheets_) {
420 bool open = true;
421 if (ImGui::BeginTabItem(absl::StrFormat("%d", sheet_id).c_str(), &open,
422 ImGuiTabItemFlags_None)) {
423 current_sheet_ = sheet_id;
424 if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
425 release_queue_.push(sheet_id);
426 }
427 if (ImGui::IsItemHovered()) {
428 if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
429 release_queue_.push(sheet_id);
430 child_window_sheets_.insert(sheet_id);
431 }
432 }
433
434 const auto child_id =
435 absl::StrFormat("##GfxEditPaletteChildWindow%d", sheet_id);
436 ImGui::BeginChild(child_id.c_str(), ImVec2(0, 0), true,
437 ImGuiWindowFlags_NoDecoration |
438 ImGuiWindowFlags_AlwaysVerticalScrollbar |
439 ImGuiWindowFlags_AlwaysHorizontalScrollbar);
440
441 gfx::Bitmap& current_bitmap =
442 gfx::Arena::Get().mutable_gfx_sheets()->at(sheet_id);
443
444 auto draw_tile_event = [&]() {
447 // Notify Arena that this sheet has been modified for cross-editor synchronization
449 };
450
452 nullptr, gfx::Arena::Get().mutable_gfx_sheets()->at(sheet_id),
453 current_color_, draw_tile_event, tile_size_, current_scale_);
454
455 // Notify Arena that this sheet has been modified for cross-editor synchronization
457
458 ImGui::EndChild();
459 ImGui::EndTabItem();
460 }
461
462 if (!open) release_queue_.push(sheet_id);
463 }
464
465 ImGui::EndTabBar();
466 }
467
468 // Release any tabs that were closed
469 while (!release_queue_.empty()) {
470 auto sheet_id = release_queue_.top();
471 open_sheets_.erase(sheet_id);
472 release_queue_.pop();
473 }
474
475 // Draw any child windows that were created
476 if (!child_window_sheets_.empty()) {
477 int id_to_release = -1;
478 for (const auto& id : child_window_sheets_) {
479 bool active = true;
480 ImGui::SetNextWindowPos(ImGui::GetIO().MousePos, ImGuiCond_Once);
481 ImGui::SetNextWindowSize(ImVec2(0x100 + 1 * 16, 0x40 + 1 * 16),
482 ImGuiCond_Once);
483 ImGui::Begin(absl::StrFormat("##GfxEditPaletteChildWindow%d", id).c_str(),
484 &active, ImGuiWindowFlags_AlwaysUseWindowPadding);
485 current_sheet_ = id;
486 // ImVec2(0x100, 0x40),
488 nullptr, gfx::Arena::Get().mutable_gfx_sheets()->at(id), current_color_,
489 [&]() {
490
491 },
493 ImGui::End();
494
495 if (active == false) {
496 id_to_release = id;
497 }
498 }
499 if (id_to_release != -1) {
500 child_window_sheets_.erase(id_to_release);
501 }
502 }
503
504 return absl::OkStatus();
505}
506
508 if (rom()->is_loaded()) {
509 auto palette_group = *rom()->palette_group().get_group(
511 auto palette = palette_group.palette(edit_palette_index_);
512 gui::TextWithSeparators("ROM Palette Management");
513
514 // Quick palette presets for common SNES graphics types
515 ImGui::Text("Quick Presets:");
516 if (ImGui::Button("Overworld")) {
517 edit_palette_group_name_index_ = 0; // Dungeon Main
519 refresh_graphics_ = true;
520 }
521 ImGui::SameLine();
522 if (ImGui::Button("Dungeon")) {
523 edit_palette_group_name_index_ = 0; // Dungeon Main
525 refresh_graphics_ = true;
526 }
527 ImGui::SameLine();
528 if (ImGui::Button("Sprites")) {
529 edit_palette_group_name_index_ = 4; // Sprites Aux1
531 refresh_graphics_ = true;
532 }
533 ImGui::Separator();
534
535 // Apply current palette to current sheet
536 if (ImGui::Button("Apply to Current Sheet") && !open_sheets_.empty()) {
537 refresh_graphics_ = true;
538 }
539 ImGui::SameLine();
540 if (ImGui::Button("Apply to All Sheets")) {
541 // Apply current palette to all active sheets
542 for (int i = 0; i < kNumGfxSheets; i++) {
543 auto& sheet = gfx::Arena::Get().mutable_gfx_sheets()->data()[i];
544 if (sheet.is_active() && sheet.surface()) {
545 sheet.SetPaletteWithTransparent(palette, edit_palette_sub_index_);
546 // Notify Arena that this sheet has been modified
548 }
549 }
550 }
551 ImGui::Separator();
552
553 ImGui::SetNextItemWidth(150.f);
554 ImGui::Combo("Palette Group", (int*)&edit_palette_group_name_index_,
556 IM_ARRAYSIZE(kPaletteGroupAddressesKeys));
557 ImGui::SetNextItemWidth(100.f);
558 gui::InputHex("Palette Index", &edit_palette_index_);
559 ImGui::SetNextItemWidth(100.f);
560 gui::InputHex("Sub-Palette", &edit_palette_sub_index_);
561
563 palette);
564
565 if (refresh_graphics_ && !open_sheets_.empty()) {
566 auto& current = gfx::Arena::Get().mutable_gfx_sheets()->data()[current_sheet_];
567 if (current.is_active() && current.surface()) {
568 current.SetPaletteWithTransparent(palette, edit_palette_sub_index_);
569 // Notify Arena that this sheet has been modified
571 }
572 refresh_graphics_ = false;
573 }
574 }
575 return absl::OkStatus();
576}
577
579 if (ImGui::BeginTable("##PlayerAnimationTable", 3, kGfxEditTableFlags,
580 ImVec2(0, 0))) {
581 for (const auto& name : {"Canvas", "Animation Steps", "Properties"})
582 ImGui::TableSetupColumn(name);
583
584 ImGui::TableHeadersRow();
585
586 ImGui::TableNextColumn();
588 link_canvas_.DrawGrid(16.0f);
589
590 int i = 0;
591 for (auto& link_sheet : link_sheets_) {
592 int x_offset = 0;
593 int y_offset = gfx::kTilesheetHeight * i * 4;
595 link_canvas_.DrawBitmap(link_sheet, x_offset, y_offset, 4);
596 i++;
597 }
600
601 ImGui::TableNextColumn();
602 ImGui::Text("Placeholder");
603
604 ImGui::TableNextColumn();
605 if (ImGui::Button("Load Link Graphics (Experimental)")) {
606 if (rom()->is_loaded()) {
607 // Load Links graphics from the ROM
609
610 // Split it into the pose data frames
611 // Create an animation step display for the poses
612 // Allow the user to modify the frames used in an anim step
613 // LinkOAM_AnimationSteps:
614 // #_0D85FB
615 }
616 }
617 }
618 ImGui::EndTable();
619
620 return absl::OkStatus();
621}
622
624 DrawToolset();
625
627 ImGui::Begin("Memory Editor", &open_memory_editor_);
629 ImGui::End();
630 }
631
632 constexpr ImGuiTableFlags kGfxEditFlags = ImGuiTableFlags_Reorderable |
633 ImGuiTableFlags_Resizable |
634 ImGuiTableFlags_SizingStretchSame;
635
636 BEGIN_TABLE("#gfxEditTable", 4, kGfxEditFlags)
637 SETUP_COLUMN("File Import (BIN, CGX, ROM)")
638 SETUP_COLUMN("Palette (COL)")
639 ImGui::TableSetupColumn("Tilemaps and Objects (SCR, PNL, OBJ)",
640 ImGuiTableColumnFlags_WidthFixed);
641 SETUP_COLUMN("Graphics Preview")
643 NEXT_COLUMN() {
648 }
649
651
654 scr_loaded_, false, 0);
656
658 if (super_donkey_) {
659 // TODO: Implement the Super Donkey 1 graphics decompression
660 // if (refresh_graphics_) {
661 // for (int i = 0; i < kNumGfxSheets; i++) {
662 // status_ = graphics_bin_[i].SetPalette(
663 // col_file_palette_group_[current_palette_index_]);
664 // Renderer::Get().UpdateBitmap(&graphics_bin_[i]);
665 // }
666 // refresh_graphics_ = false;
667 // }
668 // Load the full graphics space from `super_donkey_1.bin`
669 // gui::GraphicsBinCanvasPipeline(0x100, 0x40, 0x20, num_sheets_to_load_, 3,
670 // super_donkey_, graphics_bin_);
671 } else if (cgx_loaded_ && col_file_) {
672 // Load the CGX graphics
674 cgx_loaded_, true, 5);
675 } else {
676 // Load the BIN/Clipboard Graphics
678 gfx_loaded_, true, 2);
679 }
680 END_TABLE()
681
682 return absl::OkStatus();
683}
684
686 gui::TextWithSeparators("Cgx Import");
687 InputInt("BPP", &current_bpp_);
688
689 InputText("##CGXFile", &cgx_file_name_);
690 SameLine();
691
692 if (ImGui::Button("Open CGX")) {
694 cgx_file_name_ = filename;
695 cgx_file_path_ = std::filesystem::absolute(filename).string();
696 is_open_ = true;
697 cgx_loaded_ = true;
698 }
699
700 if (ImGui::Button("Copy CGX Path")) {
701 ImGui::SetClipboardText(cgx_file_path_.c_str());
702 }
703
704 if (ImGui::Button("Load CGX Data")) {
707
708 cgx_bitmap_.Create(0x80, 0x200, 8, decoded_cgx_);
709 if (col_file_) {
713 }
714 }
715
716 return absl::OkStatus();
717}
718
720 InputText("##ScrFile", &scr_file_name_);
721
722 if (ImGui::Button("Open SCR")) {
724 scr_file_name_ = filename;
725 scr_file_path_ = std::filesystem::absolute(filename).string();
726 is_open_ = true;
727 scr_loaded_ = true;
728 }
729
730 InputInt("SCR Mod", &scr_mod_value_);
731
732 if (ImGui::Button("Load Scr Data")) {
734
735 decoded_scr_data_.resize(0x100 * 0x100);
738
739 scr_bitmap_.Create(0x100, 0x100, 8, decoded_scr_data_);
740 if (scr_loaded_) {
744 }
745 }
746
747 return absl::OkStatus();
748}
749
751 gui::TextWithSeparators("COL Import");
752 InputText("##ColFile", &col_file_name_);
753 SameLine();
754
755 if (ImGui::Button("Open COL")) {
757 col_file_name_ = filename;
758 col_file_path_ = std::filesystem::absolute(filename).string();
760 /*z3_load=*/false);
761 auto col_data_ = gfx::GetColFileData(temp_rom_.mutable_data());
762 if (col_file_palette_group_.size() != 0) {
764 }
765 auto col_file_palette_group_status =
767 if (col_file_palette_group_status.ok()) {
768 col_file_palette_group_ = col_file_palette_group_status.value();
769 }
771
772 // gigaleak dev format based code
774 col_file_ = true;
775 is_open_ = true;
776 }
777 HOVER_HINT(".COL, .BAK");
778
779 if (ImGui::Button("Copy Col Path")) {
780 ImGui::SetClipboardText(col_file_path_.c_str());
781 }
782
783 if (rom()->is_loaded()) {
784 gui::TextWithSeparators("ROM Palette");
785 gui::InputHex("Palette Index", &current_palette_index_);
786 ImGui::Combo("Palette", &current_palette_, kPaletteGroupAddressesKeys,
787 IM_ARRAYSIZE(kPaletteGroupAddressesKeys));
788 }
789
790 if (col_file_palette_.size() != 0) {
793 }
794
795 return absl::OkStatus();
796}
797
799 gui::TextWithSeparators("OBJ Import");
800
801 InputText("##ObjFile", &obj_file_path_);
802 SameLine();
803
804 if (ImGui::Button("Open OBJ")) {
806 obj_file_path_ = std::filesystem::absolute(filename).string();
808 is_open_ = true;
809 obj_loaded_ = true;
810 }
811 HOVER_HINT(".OBJ, .BAK");
812
813 return absl::OkStatus();
814}
815
817 gui::TextWithSeparators("Tilemap Import");
818
819 InputText("##TMapFile", &tilemap_file_path_);
820 SameLine();
821
822 if (ImGui::Button("Open Tilemap")) {
824 tilemap_file_path_ = std::filesystem::absolute(filename).string();
827
828 // Extract the high and low bytes from the file.
829 auto decomp_sheet = gfx::lc_lz2::DecompressV2(tilemap_rom_.data(),
831 tilemap_loaded_ = true;
832 is_open_ = true;
833 }
834 HOVER_HINT(".DAT, .BIN, .HEX");
835
836 return absl::OkStatus();
837}
838
840 gui::TextWithSeparators("BIN Import");
841
842 InputText("##ROMFile", &file_path_);
843 SameLine();
844
845 if (ImGui::Button("Open BIN")) {
847 file_path_ = filename;
849 is_open_ = true;
850 }
851 HOVER_HINT(".BIN, .HEX");
852
853 if (Button("Copy File Path")) {
854 ImGui::SetClipboardText(file_path_.c_str());
855 }
856
857 gui::InputHex("BIN Offset", &current_offset_);
858 gui::InputHex("BIN Size", &bin_size_);
859
860 if (Button("Decompress BIN")) {
861 if (file_path_.empty()) {
862 return absl::InvalidArgumentError(
863 "Please select a file before decompressing.");
864 }
866 }
867
868 return absl::OkStatus();
869}
870
872 gui::TextWithSeparators("Clipboard Import");
873 if (Button("Paste From Clipboard")) {
874 const char* text = ImGui::GetClipboardText();
875 if (text) {
876 const auto clipboard_data =
877 std::vector<uint8_t>(text, text + strlen(text));
878 ImGui::MemFree((void*)text);
879 status_ = temp_rom_.LoadFromData(clipboard_data);
880 is_open_ = true;
881 open_memory_editor_ = true;
882 }
883 }
886 gui::InputHex("Num Sheets", &num_sheets_to_load_);
887
888 if (Button("Decompress Clipboard Data")) {
889 if (temp_rom_.is_loaded()) {
890 status_ = DecompressImportData(0x40000);
891 } else {
892 status_ = absl::InvalidArgumentError(
893 "Please paste data into the clipboard before "
894 "decompressing.");
895 }
896 }
897
898 return absl::OkStatus();
899}
900
902 gui::TextWithSeparators("Experimental");
903 if (Button("Decompress Super Donkey Full")) {
904 if (file_path_.empty()) {
905 return absl::InvalidArgumentError(
906 "Please select `super_donkey_1.bin` before "
907 "importing.");
908 }
910 }
911 ImGui::SetItemTooltip(
912 "Requires `super_donkey_1.bin` to be imported under the "
913 "BIN import section.");
914 return absl::OkStatus();
915}
916
918 std::string title = "Memory Editor";
919 if (is_open_) {
920 static MemoryEditor mem_edit;
921 mem_edit.DrawWindow(title.c_str(), temp_rom_.mutable_data(),
922 temp_rom_.size());
923 }
924 return absl::OkStatus();
925}
926
929 temp_rom_.data(), current_offset_, size));
930
931 auto converted_sheet = gfx::SnesTo8bppSheet(import_data_, 3);
933 converted_sheet);
934
935 if (rom()->is_loaded()) {
936 auto palette_group = rom()->palette_group().overworld_animated;
937 z3_rom_palette_ = palette_group[current_palette_];
938 if (col_file_) {
940 } else {
942 }
943 }
944
947 gfx_loaded_ = true;
948
949 return absl::OkStatus();
950}
951
953 int i = 0;
954 for (const auto& offset : kSuperDonkeyTiles) {
955 int offset_value =
956 std::stoi(offset, nullptr, 16); // convert hex string to int
958 auto decompressed_data,
959 gfx::lc_lz2::DecompressV2(temp_rom_.data(), offset_value, 0x1000));
960 auto converted_sheet = gfx::SnesTo8bppSheet(decompressed_data, 3);
962 gfx::kTilesheetDepth, converted_sheet);
963 if (col_file_) {
964 gfx_sheets_[i].SetPalette(
966 } else {
967 // ROM palette
968
969 auto palette_group = rom()->palette_group().get_group(
971 z3_rom_palette_ = *palette_group->mutable_palette(current_palette_index_);
972 gfx_sheets_[i].SetPalette(z3_rom_palette_);
973 }
974
977 i++;
978 }
979
980 for (const auto& offset : kSuperDonkeySprites) {
981 int offset_value =
982 std::stoi(offset, nullptr, 16); // convert hex string to int
984 auto decompressed_data,
985 gfx::lc_lz2::DecompressV2(temp_rom_.data(), offset_value, 0x1000));
986 auto converted_sheet = gfx::SnesTo8bppSheet(decompressed_data, 3);
988 gfx::kTilesheetDepth, converted_sheet);
989 if (col_file_) {
990 gfx_sheets_[i].SetPalette(
992 } else {
993 // ROM palette
994 auto palette_group = rom()->palette_group().get_group(
996 z3_rom_palette_ = *palette_group->mutable_palette(current_palette_index_);
997 gfx_sheets_[i].SetPalette(z3_rom_palette_);
998 }
999
1002 i++;
1003 }
1004 super_donkey_ = true;
1006
1007 return absl::OkStatus();
1008}
1009
1010} // namespace editor
1011} // namespace yaze
absl::Status LoadFromFile(const std::string &filename, bool z3_load=true)
Definition rom.cc:289
auto palette_group() const
Definition rom.h:213
absl::Status LoadFromData(const std::vector< uint8_t > &data, bool z3_load=true)
Definition rom.cc:381
auto mutable_data()
Definition rom.h:204
auto data() const
Definition rom.h:203
auto size() const
Definition rom.h:202
bool is_loaded() const
Definition rom.h:197
std::string MakeCardTitle(const std::string &base_title) const
Definition editor.h:127
std::vector< uint8_t > scr_data_
std::array< gfx::Bitmap, kNumLinkSheets > link_sheets_
gfx::PaletteGroup col_file_palette_group_
absl::Status Load() override
std::vector< uint8_t > decoded_cgx_
std::set< uint16_t > child_window_sheets_
std::array< gfx::Bitmap, kNumGfxSheets > gfx_sheets_
std::vector< uint8_t > cgx_data_
void DrawGfxEditToolset()
Draw the graphics editing toolset with enhanced ROM hacking features.
std::vector< uint8_t > import_data_
std::vector< SDL_Color > decoded_col_
std::set< uint16_t > open_sheets_
absl::Status Update() override
std::vector< uint8_t > extra_cgx_data_
std::vector< uint8_t > decoded_scr_data_
std::stack< uint16_t > release_queue_
gui::GfxSheetAssetBrowser asset_browser_
absl::Status DecompressImportData(int size)
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
Definition arena.cc:32
auto mutable_gfx_sheets()
Get mutable reference to all graphics sheets.
Definition arena.h:98
std::array< gfx::Bitmap, 223 > & gfx_sheets()
Get reference to all graphics sheets.
Definition arena.h:78
void NotifySheetModified(int sheet_index)
Notify Arena that a graphics sheet has been modified.
Definition arena.cc:183
static Arena & Get()
Definition arena.cc:15
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:66
void Create(int width, int height, int depth, std::span< uint8_t > data)
Create a bitmap with the given dimensions and data.
Definition bitmap.cc:162
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap.
Definition bitmap.cc:292
RAII timer for automatic timing management.
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
void DrawBitmap(Bitmap &bitmap, int border_offset, float scale)
Definition canvas.cc:1062
void DrawContextMenu()
Definition canvas.cc:441
auto draw_list() const
Definition canvas.h:309
void UpdateColorPainter(gfx::IRenderer *renderer, gfx::Bitmap &bitmap, const ImVec4 &color, const std::function< void()> &event, int tile_size, float scale=1.0f)
Definition canvas.cc:357
void DrawTileOnBitmap(int tile_size, gfx::Bitmap *bitmap, ImVec4 color)
Definition canvas.cc:899
auto zero_point() const
Definition canvas.h:310
void DrawBackground(ImVec2 canvas_size=ImVec2(0, 0))
Definition canvas.cc:381
void DrawGrid(float grid_step=64.0f, int tile_id_offset=8)
Definition canvas.cc:1386
static EditorCardManager & Get()
Draggable, dockable card for editor sub-windows.
bool Begin(bool *p_open=nullptr)
Ultra-compact toolbar that merges mode buttons with settings.
bool AddAction(const char *icon, const char *tooltip)
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath. Uses global feature flag to...
#define ICON_MD_VIEW_LIST
Definition icons.h:2090
#define ICON_MD_CONSTRUCTION
Definition icons.h:456
#define ICON_MD_DRAW
Definition icons.h:623
#define ICON_MD_ZOOM_OUT
Definition icons.h:2194
#define ICON_MD_FORMAT_COLOR_FILL
Definition icons.h:828
#define ICON_MD_EDIT
Definition icons.h:643
#define ICON_MD_CONTENT_PASTE
Definition icons.h:465
#define ICON_MD_ADD
Definition icons.h:84
#define ICON_MD_PERSON
Definition icons.h:1413
#define ICON_MD_ZOOM_IN
Definition icons.h:2192
#define ICON_MD_SELECT_ALL
Definition icons.h:1678
#define ICON_MD_CONTENT_COPY
Definition icons.h:463
#define LOG_INFO(category, format,...)
Definition log.h:106
#define SETUP_COLUMN(l)
Definition macro.h:12
#define END_TABLE()
Definition macro.h:20
#define TABLE_HEADERS()
Definition macro.h:14
#define BEGIN_TABLE(l, n, f)
Definition macro.h:11
#define RETURN_IF_ERROR(expression)
Definition macro.h:53
#define NEXT_COLUMN()
Definition macro.h:18
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition macro.h:61
#define CLEAR_AND_RETURN_STATUS(status)
Definition macro.h:96
#define HOVER_HINT(string)
Definition macro.h:24
const std::string kSuperDonkeySprites[]
constexpr ImGuiTableFlags kGfxEditTableFlags
const std::string kSuperDonkeyTiles[]
constexpr int kNintendoMode1
Definition compression.h:53
absl::StatusOr< std::vector< uint8_t > > DecompressV2(const uint8_t *data, int offset, int size, int mode)
Decompresses a buffer of data using the LC_LZ2 algorithm.
absl::Status LoadScr(std::string_view filename, uint8_t input_value, std::vector< uint8_t > &map_data)
Load Scr file (screen data)
std::vector< SDL_Color > DecodeColFile(const std::string_view filename)
Decode color file.
constexpr int kTilesheetHeight
Definition snes_tile.h:17
constexpr int kTilesheetWidth
Definition snes_tile.h:16
absl::Status LoadCgx(uint8_t bpp, std::string_view filename, std::vector< uint8_t > &cgx_data, std::vector< uint8_t > &cgx_loaded, std::vector< uint8_t > &cgx_header)
Load Cgx file (graphical content)
constexpr const char * kPaletteGroupAddressesKeys[]
absl::Status DrawScrWithCgx(uint8_t bpp, std::vector< uint8_t > &map_bitmap_data, std::vector< uint8_t > &map_data, std::vector< uint8_t > &cgx_loaded)
Draw screen tilemap with graphical data.
constexpr int kTilesheetDepth
Definition snes_tile.h:18
std::vector< uint8_t > SnesTo8bppSheet(std::span< uint8_t > sheet, int bpp, int num_sheets)
Definition snes_tile.cc:129
absl::StatusOr< PaletteGroup > CreatePaletteGroupFromColFile(std::vector< SnesColor > &palette_rows)
std::vector< SnesColor > GetColFileData(uint8_t *data)
Definition snes_color.cc:89
void VerticalSpacing(float pixels)
void BitmapCanvasPipeline(gui::Canvas &canvas, gfx::Bitmap &bitmap, int width, int height, int tile_size, bool is_loaded, bool scrollbar, int canvas_id)
Definition canvas.cc:1531
bool InputHex(const char *label, uint64_t *data)
Definition input.cc:156
void SelectablePalettePipeline(uint64_t &palette_id, bool &refresh_graphics, gfx::SnesPalette &palette)
Definition color.cc:123
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
Definition input.cc:189
void TextWithSeparators(const absl::string_view &text)
Definition style.cc:1280
Main namespace for the application.
absl::StatusOr< std::array< gfx::Bitmap, kNumLinkSheets > > LoadLinkGraphics(const Rom &rom)
Loads the players 4bpp graphics sheet from Rom data.
Definition rom.cc:94
constexpr uint32_t kNumGfxSheets
Definition rom.h:28
void Draw(const std::array< gfx::Bitmap, kNumGfxSheets > &bmp_manager)
void Initialize(const std::array< gfx::Bitmap, kNumGfxSheets > &bmp_manager)