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"
3
4#include <filesystem>
5
6#include "absl/status/status.h"
7#include "absl/status/statusor.h"
8#include "absl/strings/str_cat.h"
10#include "util/file_util.h"
11#include "app/platform/window.h"
13#include "app/gfx/core/bitmap.h"
19#include "app/gui/core/color.h"
20#include "app/gui/core/icons.h"
21#include "app/gui/core/input.h"
23#include "app/gui/core/style.h"
24#include "app/rom.h"
26#include "imgui/imgui.h"
27#include "imgui/misc/cpp/imgui_stdlib.h"
28#include "imgui_memory_editor.h"
29#include "util/log.h"
30
31namespace yaze {
32namespace editor {
33
35using ImGui::Button;
36using ImGui::InputInt;
37using ImGui::InputText;
38using ImGui::SameLine;
39using ImGui::TableNextColumn;
40
41constexpr ImGuiTableFlags kGfxEditTableFlags =
42 ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
43 ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable |
44 ImGuiTableFlags_SizingFixedFit;
45
47 if (!dependencies_.card_registry) return;
48 auto* card_registry = dependencies_.card_registry;
49
50 card_registry->RegisterCard({.card_id = "graphics.sheet_editor", .display_name = "Sheet Editor",
51 .icon = ICON_MD_EDIT, .category = "Graphics",
52 .shortcut_hint = "Ctrl+Shift+1", .priority = 10});
53 card_registry->RegisterCard({.card_id = "graphics.sheet_browser", .display_name = "Sheet Browser",
54 .icon = ICON_MD_VIEW_LIST, .category = "Graphics",
55 .shortcut_hint = "Ctrl+Shift+2", .priority = 20});
56 card_registry->RegisterCard({.card_id = "graphics.player_animations", .display_name = "Player Animations",
57 .icon = ICON_MD_PERSON, .category = "Graphics",
58 .shortcut_hint = "Ctrl+Shift+3", .priority = 30});
59 card_registry->RegisterCard({.card_id = "graphics.prototype_viewer", .display_name = "Prototype Viewer",
60 .icon = ICON_MD_CONSTRUCTION, .category = "Graphics",
61 .shortcut_hint = "Ctrl+Shift+4", .priority = 40});
62
63 // Show sheet editor by default when Graphics Editor is activated
64 card_registry->ShowCard("graphics.sheet_editor");
65}
66
67absl::Status GraphicsEditor::Load() {
68 gfx::ScopedTimer timer("GraphicsEditor::Load");
69
70 // Initialize all graphics sheets with appropriate palettes from ROM
71 // This ensures textures are created for editing
72 if (rom()->is_loaded()) {
73 auto& sheets = gfx::Arena::Get().gfx_sheets();
74
75 // Apply default palettes to all sheets based on common SNES ROM structure
76 // Sheets 0-112: Use overworld/dungeon palettes
77 // Sheets 113-127: Use sprite palettes
78 // Sheets 128-222: Use auxiliary/menu palettes
79
80 LOG_INFO("GraphicsEditor", "Initializing textures for %d graphics sheets", kNumGfxSheets);
81
82 int sheets_queued = 0;
83 for (int i = 0; i < kNumGfxSheets; i++) {
84 if (!sheets[i].is_active() || !sheets[i].surface()) {
85 continue; // Skip inactive or surface-less sheets
86 }
87
88 // Palettes are now applied during ROM loading in LoadAllGraphicsData()
89 // Just queue texture creation for sheets that don't have textures yet
90 if (!sheets[i].texture()) {
93 sheets_queued++;
94 }
95 }
96
97 LOG_INFO("GraphicsEditor", "Queued texture creation for %d graphics sheets", sheets_queued);
98 }
99
100 return absl::OkStatus();
101}
102
104 if (!dependencies_.card_registry) return absl::OkStatus();
105 auto* card_registry = dependencies_.card_registry;
106
107 static gui::EditorCard sheet_editor_card("Sheet Editor", ICON_MD_EDIT);
108 static gui::EditorCard sheet_browser_card("Sheet Browser", ICON_MD_VIEW_LIST);
109 static gui::EditorCard player_anims_card("Player Animations", ICON_MD_PERSON);
110 static gui::EditorCard prototype_card("Prototype Viewer", ICON_MD_CONSTRUCTION);
111
112 sheet_editor_card.SetDefaultSize(900, 700);
113 sheet_browser_card.SetDefaultSize(400, 600);
114 player_anims_card.SetDefaultSize(500, 600);
115 prototype_card.SetDefaultSize(600, 500);
116
117 // Sheet Editor Card - Check visibility flag exists and is true before rendering
118 bool* sheet_editor_visible = card_registry->GetVisibilityFlag("graphics.sheet_editor");
119 if (sheet_editor_visible && *sheet_editor_visible) {
120 if (sheet_editor_card.Begin(sheet_editor_visible)) {
122 }
123 sheet_editor_card.End();
124 }
125
126 // Sheet Browser Card - Check visibility flag exists and is true before rendering
127 bool* sheet_browser_visible = card_registry->GetVisibilityFlag("graphics.sheet_browser");
128 if (sheet_browser_visible && *sheet_browser_visible) {
129 if (sheet_browser_card.Begin(sheet_browser_visible)) {
130 if (asset_browser_.Initialized == false) {
132 }
133 asset_browser_.Draw(gfx::Arena::Get().gfx_sheets());
134 }
135 sheet_browser_card.End();
136 }
137
138 // Player Animations Card - Check visibility flag exists and is true before rendering
139 bool* player_anims_visible = card_registry->GetVisibilityFlag("graphics.player_animations");
140 if (player_anims_visible && *player_anims_visible) {
141 if (player_anims_card.Begin(player_anims_visible)) {
143 }
144 player_anims_card.End();
145 }
146
147 // Prototype Viewer Card - Check visibility flag exists and is true before rendering
148 bool* prototype_visible = card_registry->GetVisibilityFlag("graphics.prototype_viewer");
149 if (prototype_visible && *prototype_visible) {
150 if (prototype_card.Begin(prototype_visible)) {
152 }
153 prototype_card.End();
154 }
155
157 return absl::OkStatus();
158}
159
161 if (ImGui::BeginTable("##GfxEditTable", 3, kGfxEditTableFlags,
162 ImVec2(0, 0))) {
163 for (const auto& name :
164 {"Tilesheets", "Current Graphics", "Palette Controls"})
165 ImGui::TableSetupColumn(name);
166
167 ImGui::TableHeadersRow();
168 ImGui::TableNextColumn();
170
171 ImGui::TableNextColumn();
172 if (rom()->is_loaded()) {
175 }
176
177 ImGui::TableNextColumn();
178 if (rom()->is_loaded()) {
180 }
181 }
182 ImGui::EndTable();
183
184 return absl::OkStatus();
185}
186
203 if (ImGui::BeginTable("##GfxEditToolset", 9, ImGuiTableFlags_SizingFixedFit,
204 ImVec2(0, 0))) {
205 for (const auto& name :
206 {"Select", "Pencil", "Fill", "Copy Sheet", "Paste Sheet", "Zoom Out",
207 "Zoom In", "Current Color", "Tile Size"})
208 ImGui::TableSetupColumn(name);
209
210 TableNextColumn();
211 if (Button(ICON_MD_SELECT_ALL)) {
213 }
214
215 TableNextColumn();
216 if (Button(ICON_MD_DRAW)) {
218 }
219 HOVER_HINT("Draw with current color");
220
221 TableNextColumn();
222 if (Button(ICON_MD_FORMAT_COLOR_FILL)) {
224 }
225 HOVER_HINT("Fill with current color");
226
227 TableNextColumn();
228 if (Button(ICON_MD_CONTENT_COPY)) {
229 status_ = absl::UnimplementedError("PNG export functionality removed");
230 }
231 HOVER_HINT("Copy to Clipboard");
232
233 TableNextColumn();
234 if (Button(ICON_MD_CONTENT_PASTE)) {
235 status_ = absl::UnimplementedError("PNG import functionality removed");
236 }
237 HOVER_HINT("Paste from Clipboard");
238
239 TableNextColumn();
240 if (Button(ICON_MD_ZOOM_OUT)) {
241 if (current_scale_ >= 0.0f) {
242 current_scale_ -= 1.0f;
243 }
244 }
245
246 TableNextColumn();
247 if (Button(ICON_MD_ZOOM_IN)) {
248 if (current_scale_ <= 16.0f) {
249 current_scale_ += 1.0f;
250 }
251 }
252
253 TableNextColumn();
254 // Enhanced palette color picker with SNES-specific features
255 auto bitmap = gfx::Arena::Get().gfx_sheets()[current_sheet_];
256 auto palette = bitmap.palette();
257
258 // Display palette colors in a grid layout for better ROM hacking workflow
259 for (int i = 0; i < palette.size(); i++) {
260 if (i > 0 && i % 8 == 0) {
261 ImGui::NewLine(); // New row every 8 colors (SNES palette standard)
262 }
263 ImGui::SameLine();
264
265 // Convert SNES color to ImGui format with proper scaling
266 auto color = ImVec4(palette[i].rgb().x / 255.0f, palette[i].rgb().y / 255.0f,
267 palette[i].rgb().z / 255.0f, 1.0f);
268
269 // Enhanced color button with tooltip showing SNES color value
270 if (ImGui::ColorButton(absl::StrFormat("Palette Color %d", i).c_str(),
271 color, ImGuiColorEditFlags_NoTooltip)) {
272 current_color_ = color;
273 }
274
275 // Add tooltip with SNES color information
276 if (ImGui::IsItemHovered()) {
277 ImGui::SetTooltip("SNES Color: $%04X\nRGB: (%d, %d, %d)",
278 palette[i].snes(),
279 static_cast<int>(palette[i].rgb().x),
280 static_cast<int>(palette[i].rgb().y),
281 static_cast<int>(palette[i].rgb().z));
282 }
283 }
284
285 TableNextColumn();
286 gui::InputHexByte("Tile Size", &tile_size_);
287
288 ImGui::EndTable();
289 }
290}
291
293 ImGui::BeginChild(
294 "##GfxEditChild", ImVec2(0, 0), true,
295 ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysVerticalScrollbar);
296 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
297 // TODO: Update the interaction for multi select on sheets
298 static ImGuiSelectionBasicStorage selection;
299 ImGuiMultiSelectFlags flags =
300 ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
301 ImGuiMultiSelectIO* ms_io =
302 ImGui::BeginMultiSelect(flags, selection.Size, kNumGfxSheets);
303 selection.ApplyRequests(ms_io);
304 ImGuiListClipper clipper;
305 clipper.Begin(kNumGfxSheets);
306 if (ms_io->RangeSrcItem != -1)
307 clipper.IncludeItemByIndex(
308 (int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
309
310 int key = 0;
311 for (auto& value : gfx::Arena::Get().gfx_sheets()) {
312 ImGui::BeginChild(absl::StrFormat("##GfxSheet%02X", key).c_str(),
313 ImVec2(0x100 + 1, 0x40 + 1), true,
314 ImGuiWindowFlags_NoDecoration);
315 ImGui::PopStyleVar();
316
317 graphics_bin_canvas_.DrawBackground(ImVec2(0x100 + 1, 0x40 + 1));
319 if (value.is_active()) {
320 // Ensure texture exists for active sheets
321 if (!value.texture() && value.surface()) {
324 }
325
326 auto texture = value.texture();
327 if (texture) {
329 (ImTextureID)(intptr_t)texture,
330 ImVec2(graphics_bin_canvas_.zero_point().x + 2,
333 value.width() * sheet_scale_,
335 value.height() * sheet_scale_));
336
337 if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
338 current_sheet_ = key;
339 open_sheets_.insert(key);
340 }
341
342 // Add a slightly transparent rectangle behind the text
343 ImVec2 text_pos(graphics_bin_canvas_.zero_point().x + 2,
345 ImVec2 text_size =
346 ImGui::CalcTextSize(absl::StrFormat("%02X", key).c_str());
347 ImVec2 rect_min(text_pos.x, text_pos.y);
348 ImVec2 rect_max(text_pos.x + text_size.x, text_pos.y + text_size.y);
349
350 graphics_bin_canvas_.draw_list()->AddRectFilled(rect_min, rect_max,
351 IM_COL32(0, 125, 0, 128));
352
354 text_pos, IM_COL32(125, 255, 125, 255),
355 absl::StrFormat("%02X", key).c_str());
356 }
357 key++;
358 }
361
362 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
363 ImGui::EndChild();
364 }
365 ImGui::PopStyleVar();
366 ms_io = ImGui::EndMultiSelect();
367 selection.ApplyRequests(ms_io);
368 ImGui::EndChild();
369 return absl::OkStatus();
370}
371
373 gfx::ScopedTimer timer("graphics_editor_update_gfx_tab_view");
374
375 static int next_tab_id = 0;
376 constexpr ImGuiTabBarFlags kGfxEditTabBarFlags =
377 ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable |
378 ImGuiTabBarFlags_FittingPolicyResizeDown |
379 ImGuiTabBarFlags_TabListPopupButton;
380
381 if (ImGui::BeginTabBar("##GfxEditTabBar", kGfxEditTabBarFlags)) {
382 if (ImGui::TabItemButton(ICON_MD_ADD, ImGuiTabItemFlags_Trailing |
383 ImGuiTabItemFlags_NoTooltip)) {
384 open_sheets_.insert(next_tab_id++);
385 }
386
387 for (auto& sheet_id : open_sheets_) {
388 bool open = true;
389 if (ImGui::BeginTabItem(absl::StrFormat("%d", sheet_id).c_str(), &open,
390 ImGuiTabItemFlags_None)) {
391 current_sheet_ = sheet_id;
392 if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
393 release_queue_.push(sheet_id);
394 }
395 if (ImGui::IsItemHovered()) {
396 if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
397 release_queue_.push(sheet_id);
398 child_window_sheets_.insert(sheet_id);
399 }
400 }
401
402 const auto child_id =
403 absl::StrFormat("##GfxEditPaletteChildWindow%d", sheet_id);
404 ImGui::BeginChild(child_id.c_str(), ImVec2(0, 0), true,
405 ImGuiWindowFlags_NoDecoration |
406 ImGuiWindowFlags_AlwaysVerticalScrollbar |
407 ImGuiWindowFlags_AlwaysHorizontalScrollbar);
408
409 gfx::Bitmap& current_bitmap =
410 gfx::Arena::Get().mutable_gfx_sheets()->at(sheet_id);
411
412 auto draw_tile_event = [&]() {
415 // Notify Arena that this sheet has been modified for cross-editor synchronization
417 };
418
420 nullptr, gfx::Arena::Get().mutable_gfx_sheets()->at(sheet_id),
421 current_color_, draw_tile_event, tile_size_, current_scale_);
422
423 // Notify Arena that this sheet has been modified for cross-editor synchronization
425
426 ImGui::EndChild();
427 ImGui::EndTabItem();
428 }
429
430 if (!open) release_queue_.push(sheet_id);
431 }
432
433 ImGui::EndTabBar();
434 }
435
436 // Release any tabs that were closed
437 while (!release_queue_.empty()) {
438 auto sheet_id = release_queue_.top();
439 open_sheets_.erase(sheet_id);
440 release_queue_.pop();
441 }
442
443 // Draw any child windows that were created
444 if (!child_window_sheets_.empty()) {
445 int id_to_release = -1;
446 for (const auto& id : child_window_sheets_) {
447 bool active = true;
448 ImGui::SetNextWindowPos(ImGui::GetIO().MousePos, ImGuiCond_Once);
449 ImGui::SetNextWindowSize(ImVec2(0x100 + 1 * 16, 0x40 + 1 * 16),
450 ImGuiCond_Once);
451 ImGui::Begin(absl::StrFormat("##GfxEditPaletteChildWindow%d", id).c_str(),
452 &active, ImGuiWindowFlags_AlwaysUseWindowPadding);
453 current_sheet_ = id;
454 // ImVec2(0x100, 0x40),
456 nullptr, gfx::Arena::Get().mutable_gfx_sheets()->at(id), current_color_,
457 [&]() {
458
459 },
461 ImGui::End();
462
463 if (active == false) {
464 id_to_release = id;
465 }
466 }
467 if (id_to_release != -1) {
468 child_window_sheets_.erase(id_to_release);
469 }
470 }
471
472 return absl::OkStatus();
473}
474
476 if (rom()->is_loaded()) {
477 auto palette_group = *rom()->palette_group().get_group(
479 auto palette = palette_group.palette(edit_palette_index_);
480 gui::TextWithSeparators("ROM Palette Management");
481
482 // Quick palette presets for common SNES graphics types
483 ImGui::Text("Quick Presets:");
484 if (ImGui::Button("Overworld")) {
485 edit_palette_group_name_index_ = 0; // Dungeon Main
487 refresh_graphics_ = true;
488 }
489 ImGui::SameLine();
490 if (ImGui::Button("Dungeon")) {
491 edit_palette_group_name_index_ = 0; // Dungeon Main
493 refresh_graphics_ = true;
494 }
495 ImGui::SameLine();
496 if (ImGui::Button("Sprites")) {
497 edit_palette_group_name_index_ = 4; // Sprites Aux1
499 refresh_graphics_ = true;
500 }
501 ImGui::Separator();
502
503 // Apply current palette to current sheet
504 if (ImGui::Button("Apply to Current Sheet") && !open_sheets_.empty()) {
505 refresh_graphics_ = true;
506 }
507 ImGui::SameLine();
508 if (ImGui::Button("Apply to All Sheets")) {
509 // Apply current palette to all active sheets
510 for (int i = 0; i < kNumGfxSheets; i++) {
511 auto& sheet = gfx::Arena::Get().mutable_gfx_sheets()->data()[i];
512 if (sheet.is_active() && sheet.surface()) {
513 sheet.SetPaletteWithTransparent(palette, edit_palette_sub_index_);
514 // Notify Arena that this sheet has been modified
516 }
517 }
518 }
519 ImGui::Separator();
520
521 ImGui::SetNextItemWidth(150.f);
522 ImGui::Combo("Palette Group", (int*)&edit_palette_group_name_index_,
524 IM_ARRAYSIZE(kPaletteGroupAddressesKeys));
525 ImGui::SetNextItemWidth(100.f);
526 gui::InputHex("Palette Index", &edit_palette_index_);
527 ImGui::SetNextItemWidth(100.f);
528 gui::InputHex("Sub-Palette", &edit_palette_sub_index_);
529
531 palette);
532
533 if (refresh_graphics_ && !open_sheets_.empty()) {
534 auto& current = gfx::Arena::Get().mutable_gfx_sheets()->data()[current_sheet_];
535 if (current.is_active() && current.surface()) {
536 current.SetPaletteWithTransparent(palette, edit_palette_sub_index_);
537 // Notify Arena that this sheet has been modified
539 }
540 refresh_graphics_ = false;
541 }
542 }
543 return absl::OkStatus();
544}
545
547 if (ImGui::BeginTable("##PlayerAnimationTable", 3, kGfxEditTableFlags,
548 ImVec2(0, 0))) {
549 for (const auto& name : {"Canvas", "Animation Steps", "Properties"})
550 ImGui::TableSetupColumn(name);
551
552 ImGui::TableHeadersRow();
553
554 ImGui::TableNextColumn();
556 link_canvas_.DrawGrid(16.0f);
557
558 int i = 0;
559 for (auto& link_sheet : link_sheets_) {
560 int x_offset = 0;
561 int y_offset = gfx::kTilesheetHeight * i * 4;
563 link_canvas_.DrawBitmap(link_sheet, x_offset, y_offset, 4);
564 i++;
565 }
568
569 ImGui::TableNextColumn();
570 ImGui::Text("Placeholder");
571
572 ImGui::TableNextColumn();
573 if (ImGui::Button("Load Link Graphics (Experimental)")) {
574 if (rom()->is_loaded()) {
575 // Load Links graphics from the ROM
577
578 // Split it into the pose data frames
579 // Create an animation step display for the poses
580 // Allow the user to modify the frames used in an anim step
581 // LinkOAM_AnimationSteps:
582 // #_0D85FB
583 }
584 }
585 }
586 ImGui::EndTable();
587
588 return absl::OkStatus();
589}
590
593 ImGui::Begin("Memory Editor", &open_memory_editor_);
595 ImGui::End();
596 }
597
598 constexpr ImGuiTableFlags kGfxEditFlags = ImGuiTableFlags_Reorderable |
599 ImGuiTableFlags_Resizable |
600 ImGuiTableFlags_SizingStretchSame;
601
602 BEGIN_TABLE("#gfxEditTable", 4, kGfxEditFlags)
603 SETUP_COLUMN("File Import (BIN, CGX, ROM)")
604 SETUP_COLUMN("Palette (COL)")
605 ImGui::TableSetupColumn("Tilemaps and Objects (SCR, PNL, OBJ)",
606 ImGuiTableColumnFlags_WidthFixed);
607 SETUP_COLUMN("Graphics Preview")
609 NEXT_COLUMN() {
614 }
615
617
620 scr_loaded_, false, 0);
622
624 if (super_donkey_) {
625 // TODO: Implement the Super Donkey 1 graphics decompression
626 // if (refresh_graphics_) {
627 // for (int i = 0; i < kNumGfxSheets; i++) {
628 // status_ = graphics_bin_[i].SetPalette(
629 // col_file_palette_group_[current_palette_index_]);
630 // Renderer::Get().UpdateBitmap(&graphics_bin_[i]);
631 // }
632 // refresh_graphics_ = false;
633 // }
634 // Load the full graphics space from `super_donkey_1.bin`
635 // gui::GraphicsBinCanvasPipeline(0x100, 0x40, 0x20, num_sheets_to_load_, 3,
636 // super_donkey_, graphics_bin_);
637 } else if (cgx_loaded_ && col_file_) {
638 // Load the CGX graphics
640 cgx_loaded_, true, 5);
641 } else {
642 // Load the BIN/Clipboard Graphics
644 gfx_loaded_, true, 2);
645 }
646 END_TABLE()
647
648 return absl::OkStatus();
649}
650
652 gui::TextWithSeparators("Cgx Import");
653 InputInt("BPP", &current_bpp_);
654
655 InputText("##CGXFile", &cgx_file_name_);
656 SameLine();
657
658 if (ImGui::Button("Open CGX")) {
660 cgx_file_name_ = filename;
661 cgx_file_path_ = std::filesystem::absolute(filename).string();
662 is_open_ = true;
663 cgx_loaded_ = true;
664 }
665
666 if (ImGui::Button("Copy CGX Path")) {
667 ImGui::SetClipboardText(cgx_file_path_.c_str());
668 }
669
670 if (ImGui::Button("Load CGX Data")) {
673
674 cgx_bitmap_.Create(0x80, 0x200, 8, decoded_cgx_);
675 if (col_file_) {
679 }
680 }
681
682 return absl::OkStatus();
683}
684
686 InputText("##ScrFile", &scr_file_name_);
687
688 if (ImGui::Button("Open SCR")) {
690 scr_file_name_ = filename;
691 scr_file_path_ = std::filesystem::absolute(filename).string();
692 is_open_ = true;
693 scr_loaded_ = true;
694 }
695
696 InputInt("SCR Mod", &scr_mod_value_);
697
698 if (ImGui::Button("Load Scr Data")) {
700
701 decoded_scr_data_.resize(0x100 * 0x100);
704
705 scr_bitmap_.Create(0x100, 0x100, 8, decoded_scr_data_);
706 if (scr_loaded_) {
710 }
711 }
712
713 return absl::OkStatus();
714}
715
717 gui::TextWithSeparators("COL Import");
718 InputText("##ColFile", &col_file_name_);
719 SameLine();
720
721 if (ImGui::Button("Open COL")) {
723 col_file_name_ = filename;
724 col_file_path_ = std::filesystem::absolute(filename).string();
726 /*z3_load=*/false);
727 auto col_data_ = gfx::GetColFileData(temp_rom_.mutable_data());
728 if (col_file_palette_group_.size() != 0) {
730 }
731 auto col_file_palette_group_status =
733 if (col_file_palette_group_status.ok()) {
734 col_file_palette_group_ = col_file_palette_group_status.value();
735 }
737
738 // gigaleak dev format based code
740 col_file_ = true;
741 is_open_ = true;
742 }
743 HOVER_HINT(".COL, .BAK");
744
745 if (ImGui::Button("Copy Col Path")) {
746 ImGui::SetClipboardText(col_file_path_.c_str());
747 }
748
749 if (rom()->is_loaded()) {
750 gui::TextWithSeparators("ROM Palette");
751 gui::InputHex("Palette Index", &current_palette_index_);
752 ImGui::Combo("Palette", &current_palette_, kPaletteGroupAddressesKeys,
753 IM_ARRAYSIZE(kPaletteGroupAddressesKeys));
754 }
755
756 if (col_file_palette_.size() != 0) {
759 }
760
761 return absl::OkStatus();
762}
763
765 gui::TextWithSeparators("OBJ Import");
766
767 InputText("##ObjFile", &obj_file_path_);
768 SameLine();
769
770 if (ImGui::Button("Open OBJ")) {
772 obj_file_path_ = std::filesystem::absolute(filename).string();
774 is_open_ = true;
775 obj_loaded_ = true;
776 }
777 HOVER_HINT(".OBJ, .BAK");
778
779 return absl::OkStatus();
780}
781
783 gui::TextWithSeparators("Tilemap Import");
784
785 InputText("##TMapFile", &tilemap_file_path_);
786 SameLine();
787
788 if (ImGui::Button("Open Tilemap")) {
790 tilemap_file_path_ = std::filesystem::absolute(filename).string();
793
794 // Extract the high and low bytes from the file.
795 auto decomp_sheet = gfx::lc_lz2::DecompressV2(tilemap_rom_.data(),
797 tilemap_loaded_ = true;
798 is_open_ = true;
799 }
800 HOVER_HINT(".DAT, .BIN, .HEX");
801
802 return absl::OkStatus();
803}
804
806 gui::TextWithSeparators("BIN Import");
807
808 InputText("##ROMFile", &file_path_);
809 SameLine();
810
811 if (ImGui::Button("Open BIN")) {
813 file_path_ = filename;
815 is_open_ = true;
816 }
817 HOVER_HINT(".BIN, .HEX");
818
819 if (Button("Copy File Path")) {
820 ImGui::SetClipboardText(file_path_.c_str());
821 }
822
823 gui::InputHex("BIN Offset", &current_offset_);
824 gui::InputHex("BIN Size", &bin_size_);
825
826 if (Button("Decompress BIN")) {
827 if (file_path_.empty()) {
828 return absl::InvalidArgumentError(
829 "Please select a file before decompressing.");
830 }
832 }
833
834 return absl::OkStatus();
835}
836
838 gui::TextWithSeparators("Clipboard Import");
839 if (Button("Paste From Clipboard")) {
840 const char* text = ImGui::GetClipboardText();
841 if (text) {
842 const auto clipboard_data =
843 std::vector<uint8_t>(text, text + strlen(text));
844 ImGui::MemFree((void*)text);
845 status_ = temp_rom_.LoadFromData(clipboard_data);
846 is_open_ = true;
847 open_memory_editor_ = true;
848 }
849 }
852 gui::InputHex("Num Sheets", &num_sheets_to_load_);
853
854 if (Button("Decompress Clipboard Data")) {
855 if (temp_rom_.is_loaded()) {
856 status_ = DecompressImportData(0x40000);
857 } else {
858 status_ = absl::InvalidArgumentError(
859 "Please paste data into the clipboard before "
860 "decompressing.");
861 }
862 }
863
864 return absl::OkStatus();
865}
866
868 gui::TextWithSeparators("Experimental");
869 if (Button("Decompress Super Donkey Full")) {
870 if (file_path_.empty()) {
871 return absl::InvalidArgumentError(
872 "Please select `super_donkey_1.bin` before "
873 "importing.");
874 }
876 }
877 ImGui::SetItemTooltip(
878 "Requires `super_donkey_1.bin` to be imported under the "
879 "BIN import section.");
880 return absl::OkStatus();
881}
882
884 std::string title = "Memory Editor";
885 if (is_open_) {
886 static MemoryEditor mem_edit;
887 mem_edit.DrawWindow(title.c_str(), temp_rom_.mutable_data(),
888 temp_rom_.size());
889 }
890 return absl::OkStatus();
891}
892
895 temp_rom_.data(), current_offset_, size));
896
897 auto converted_sheet = gfx::SnesTo8bppSheet(import_data_, 3);
899 converted_sheet);
900
901 if (rom()->is_loaded()) {
902 auto palette_group = rom()->palette_group().overworld_animated;
903 z3_rom_palette_ = palette_group[current_palette_];
904 if (col_file_) {
906 } else {
908 }
909 }
910
913 gfx_loaded_ = true;
914
915 return absl::OkStatus();
916}
917
919 int i = 0;
920 for (const auto& offset : kSuperDonkeyTiles) {
921 int offset_value =
922 std::stoi(offset, nullptr, 16); // convert hex string to int
924 auto decompressed_data,
925 gfx::lc_lz2::DecompressV2(temp_rom_.data(), offset_value, 0x1000));
926 auto converted_sheet = gfx::SnesTo8bppSheet(decompressed_data, 3);
928 gfx::kTilesheetDepth, converted_sheet);
929 if (col_file_) {
930 gfx_sheets_[i].SetPalette(
932 } else {
933 // ROM palette
934
935 auto palette_group = rom()->palette_group().get_group(
937 z3_rom_palette_ = *palette_group->mutable_palette(current_palette_index_);
938 gfx_sheets_[i].SetPalette(z3_rom_palette_);
939 }
940
943 i++;
944 }
945
946 for (const auto& offset : kSuperDonkeySprites) {
947 int offset_value =
948 std::stoi(offset, nullptr, 16); // convert hex string to int
950 auto decompressed_data,
951 gfx::lc_lz2::DecompressV2(temp_rom_.data(), offset_value, 0x1000));
952 auto converted_sheet = gfx::SnesTo8bppSheet(decompressed_data, 3);
954 gfx::kTilesheetDepth, converted_sheet);
955 if (col_file_) {
956 gfx_sheets_[i].SetPalette(
958 } else {
959 // ROM palette
960 auto palette_group = rom()->palette_group().get_group(
962 z3_rom_palette_ = *palette_group->mutable_palette(current_palette_index_);
963 gfx_sheets_[i].SetPalette(z3_rom_palette_);
964 }
965
968 i++;
969 }
970 super_donkey_ = true;
972
973 return absl::OkStatus();
974}
975
976} // namespace editor
977} // namespace yaze
absl::Status LoadFromFile(const std::string &filename, bool z3_load=true)
Definition rom.cc:292
auto palette_group() const
Definition rom.h:216
absl::Status LoadFromData(const std::vector< uint8_t > &data, bool z3_load=true)
Definition rom.cc:384
auto mutable_data()
Definition rom.h:207
auto data() const
Definition rom.h:206
auto size() const
Definition rom.h:205
bool is_loaded() const
Definition rom.h:200
void RegisterCard(size_t session_id, const CardInfo &base_info)
Register a card for a specific session.
EditorDependencies dependencies_
Definition editor.h:165
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:334
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:1004
void DrawContextMenu()
Definition canvas.cc:428
auto draw_list() const
Definition canvas.h:293
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:348
void DrawTileOnBitmap(int tile_size, gfx::Bitmap *bitmap, ImVec4 color)
Definition canvas.cc:841
auto zero_point() const
Definition canvas.h:294
void DrawBackground(ImVec2 canvas_size=ImVec2(0, 0))
Definition canvas.cc:372
void DrawGrid(float grid_step=64.0f, int tile_id_offset=8)
Definition canvas.cc:1304
Draggable, dockable card for editor sub-windows.
bool Begin(bool *p_open=nullptr)
void SetDefaultSize(float width, float height)
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 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:1451
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:305
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.
Definition controller.cc:20
absl::StatusOr< std::array< gfx::Bitmap, kNumLinkSheets > > LoadLinkGraphics(const Rom &rom)
Loads the players 4bpp graphics sheet from Rom data.
Definition rom.cc:97
constexpr uint32_t kNumGfxSheets
Definition rom.h:31
EditorCardRegistry * card_registry
Definition editor.h:80
void Draw(const std::array< gfx::Bitmap, kNumGfxSheets > &bmp_manager)
void Initialize(const std::array< gfx::Bitmap, kNumGfxSheets > &bmp_manager)