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// Related header
2#include "graphics_editor.h"
3
4// C++ standard library headers
5#include <algorithm>
6#include <filesystem>
7#include <set>
8
9// Third-party library headers
10#include "absl/status/status.h"
11#include "absl/status/statusor.h"
12#include "absl/strings/str_cat.h"
13#include "absl/strings/str_format.h"
14#include "imgui/imgui.h"
15#include "imgui/misc/cpp/imgui_stdlib.h"
16
17// Project headers
21#include "app/gfx/core/bitmap.h"
29#include "app/gui/core/color.h"
30#include "app/gui/core/icons.h"
31#include "app/gui/core/input.h"
32#include "app/gui/core/style.h"
36#include "app/platform/window.h"
37#include "core/rom_settings.h"
38#include "rom/rom.h"
39#include "rom/snes.h"
40#include "util/file_util.h"
41#include "util/log.h"
42
43namespace yaze {
44namespace editor {
45
47using ImGui::Button;
48using ImGui::InputInt;
49using ImGui::InputText;
50using ImGui::SameLine;
51
54 return;
55 auto* window_manager = dependencies_.window_manager;
56
57 // Initialize panel components
58 sheet_browser_panel_ = std::make_unique<SheetBrowserPanel>(&state_);
60 std::make_unique<PixelEditorPanel>(&state_, rom_, &undo_manager_);
62 std::make_unique<PaletteControlsPanel>(&state_, rom_);
63 link_sprite_panel_ = std::make_unique<LinkSpritePanel>(&state_, rom_);
64 gfx_group_panel_ = std::make_unique<GfxGroupEditor>();
66 gfx_group_panel_->SetRom(rom_);
67 gfx_group_panel_->SetGameData(game_data_);
68 gfx_group_panel_->SetHostSurfaceHint(
69 "Gfx Groups: blockset/roomset/spriteset selection syncs with the "
70 "Overworld "
71 "editor for this ROM session (each surface has its own preview "
72 "canvases).");
73 paletteset_panel_ = std::make_unique<PalettesetEditorPanel>();
74 paletteset_panel_->SetRom(rom_);
75 paletteset_panel_->SetGameData(game_data_);
76
77 polyhedral_panel_ = std::make_unique<PolyhedralEditorPanel>(rom_);
78 polyhedral_panel_->SetRom(rom_);
79
80 sheet_browser_panel_->Initialize();
81 pixel_editor_panel_->Initialize();
82 palette_controls_panel_->Initialize();
83 link_sprite_panel_->Initialize();
84
85 // Register panels using WindowContent system with callbacks
86 window_manager->RegisterWindowContent(
87 std::make_unique<GraphicsSheetBrowserPanel>([this]() {
89 status_ = sheet_browser_panel_->Update();
90 }
91 }));
92
93 window_manager->RegisterWindowContent(
94 std::make_unique<GraphicsPixelEditorPanel>([this]() {
96 status_ = pixel_editor_panel_->Update();
97 }
98 }));
99
100 window_manager->RegisterWindowContent(
101 std::make_unique<GraphicsPaletteControlsPanel>([this]() {
104 }
105 }));
106
107 window_manager->RegisterWindowContent(
108 std::make_unique<GraphicsLinkSpritePanel>([this]() {
109 if (link_sprite_panel_) {
110 status_ = link_sprite_panel_->Update();
111 }
112 }));
113
114 window_manager->RegisterWindowContent(
115 std::make_unique<GraphicsGfxGroupPanel>([this]() {
116 if (gfx_group_panel_) {
117 status_ = gfx_group_panel_->Update();
118 }
119 }));
120
121 // Paletteset editor panel (separated from GfxGroupEditor for better UX)
122 window_manager->RegisterWindowContent(
123 std::make_unique<GraphicsPalettesetPanel>([this]() {
124 if (paletteset_panel_) {
125 status_ = paletteset_panel_->Update();
126 }
127 }));
128
129 window_manager->RegisterWindowContent(
130 std::make_unique<GraphicsPrototypeViewerPanel>(
131 [this]() { DrawPrototypeViewer(); }));
132
133 window_manager->RegisterWindowContent(
134 std::make_unique<GraphicsPolyhedralPanel>([this]() {
135 if (polyhedral_panel_) {
136 bool open = true;
137 polyhedral_panel_->Draw(&open);
138 }
139 }));
140}
141
142absl::Status GraphicsEditor::Load() {
143 gfx::ScopedTimer timer("GraphicsEditor::Load");
144
145 // Initialize all graphics sheets with appropriate palettes from ROM
146 // This ensures textures are created for editing
147 if (rom()->is_loaded()) {
148 auto& sheets = gfx::Arena::Get().gfx_sheets();
149
150 // Apply default palettes to all sheets based on common SNES ROM structure
151 // Sheets 0-112: Use overworld/dungeon palettes
152 // Sheets 113-127: Use sprite palettes
153 // Sheets 128-222: Use auxiliary/menu palettes
154
155 LOG_INFO("GraphicsEditor", "Initializing textures for %d graphics sheets",
157
158 int sheets_queued = 0;
159 for (int i = 0; i < zelda3::kNumGfxSheets; i++) {
160 if (!sheets[i].is_active() || !sheets[i].surface()) {
161 continue; // Skip inactive or surface-less sheets
162 }
163
164 // Palettes are now applied during ROM loading in LoadAllGraphicsData()
165 // Just queue texture creation for sheets that don't have textures yet
166 if (!sheets[i].texture()) {
167 // Fix: Ensure default palettes are applied if missing
168 // This handles the case where sheets are loaded but have no palette assigned
169 if (sheets[i].palette().empty()) {
170 // Default palette assignment logic
171 if (i <= 112) {
172 // Overworld/Dungeon sheets - use Dungeon Main palette (Group 0, Index 0)
173 if (game_data() &&
174 game_data()->palette_groups.dungeon_main.size() > 0) {
175 sheets[i].SetPaletteWithTransparent(
176 game_data()->palette_groups.dungeon_main.palette(0), 0);
177 }
178 } else if (i >= 113 && i <= 127) {
179 // Sprite sheets - use Sprites Aux1 palette (Group 4, Index 0)
180 if (game_data() &&
181 game_data()->palette_groups.sprites_aux1.size() > 0) {
182 sheets[i].SetPaletteWithTransparent(
183 game_data()->palette_groups.sprites_aux1.palette(0), 0);
184 }
185 } else {
186 // Menu/Aux sheets - use HUD palette if available, or fallback
187 if (game_data() && game_data()->palette_groups.hud.size() > 0) {
188 sheets[i].SetPaletteWithTransparent(
189 game_data()->palette_groups.hud.palette(0), 0);
190 }
191 }
192 }
193
196 sheets_queued++;
197 }
198 }
199
200 LOG_INFO("GraphicsEditor", "Queued texture creation for %d graphics sheets",
201 sheets_queued);
202 }
203
204 return absl::OkStatus();
205}
206
208 if (!status_bar)
209 return;
210
211 StatusBarSegmentOptions sheet_opts;
212 sheet_opts.tooltip = absl::StrFormat(
213 "Sheet %d (0x%02X) — use Command Palette to jump between sheets",
215 status_bar->SetCustomSegment(
216 "Sheet", absl::StrFormat("0x%02X", state_.current_sheet_id),
217 std::move(sheet_opts));
218
219 if (!state_.selected_sheets.empty()) {
220 status_bar->SetSelection(static_cast<int>(state_.selected_sheets.size()));
221 }
223 StatusBarSegmentOptions modified_opts;
224 modified_opts.tooltip = absl::StrFormat(
225 "%zu modified sheet%s pending save", state_.modified_sheets.size(),
226 state_.modified_sheets.size() == 1 ? "" : "s");
227 status_bar->SetCustomSegment(
228 "Modified", absl::StrFormat("%zu", state_.modified_sheets.size()),
229 std::move(modified_opts));
230 }
231}
232
233absl::Status GraphicsEditor::Save() {
234 if (!rom_ || !rom_->is_loaded()) {
235 return absl::FailedPreconditionError("ROM not loaded");
236 }
237
238 // Only save sheets that have been modified
239 if (!state_.HasUnsavedChanges()) {
240 LOG_INFO("GraphicsEditor", "No modified sheets to save");
241 return absl::OkStatus();
242 }
243
244 LOG_INFO("GraphicsEditor", "Saving %zu modified graphics sheets",
245 state_.modified_sheets.size());
246
247 auto& sheets = gfx::Arena::Get().gfx_sheets();
248 std::set<uint16_t> saved_sheets;
249 std::vector<uint16_t> skipped_sheets;
250
251 for (uint16_t sheet_id : state_.modified_sheets) {
252 if (sheet_id >= zelda3::kNumGfxSheets)
253 continue;
254
255 auto& sheet = sheets[sheet_id];
256 if (!sheet.is_active())
257 continue;
258
259 // Determine BPP and compression based on sheet range
260 int bpp = 3; // Default 3BPP
261 bool compressed = true;
262
263 // Sheets 113-114, 218+ are 2BPP
264 if (sheet_id == 113 || sheet_id == 114 || sheet_id >= 218) {
265 bpp = 2;
266 }
267
268 // Sheets 115-126 are uncompressed
269 if (sheet_id >= 115 && sheet_id <= 126) {
270 compressed = false;
271 }
272
273 if (bpp == 2) {
274 const size_t expected_size =
276 const size_t actual_size = sheet.vector().size();
277 if (actual_size < expected_size) {
278 LOG_WARN("GraphicsEditor",
279 "Skipping 2BPP sheet %02X save (expected %zu bytes, got %zu)",
280 sheet_id, expected_size, actual_size);
281 skipped_sheets.push_back(sheet_id);
282 continue;
283 }
284 }
285
286 // Calculate ROM offset for this sheet
287 // Get version constants from game_data
288 auto version_constants =
289 zelda3::kVersionConstantsMap.at(game_data()->version);
290 const uint32_t gfx_ptr1 = core::RomSettings::Get().GetAddressOr(
292 version_constants.kOverworldGfxPtr1);
293 const uint32_t gfx_ptr2 = core::RomSettings::Get().GetAddressOr(
295 version_constants.kOverworldGfxPtr2);
296 const uint32_t gfx_ptr3 = core::RomSettings::Get().GetAddressOr(
298 version_constants.kOverworldGfxPtr3);
299 uint32_t offset =
300 zelda3::GetGraphicsAddress(rom_->data(), static_cast<uint8_t>(sheet_id),
301 gfx_ptr1, gfx_ptr2, gfx_ptr3, rom_->size());
302
303 // Convert 8BPP bitmap data to SNES planar format
304 auto snes_tile_data = gfx::IndexedToSnesSheet(sheet.vector(), bpp);
305
306 constexpr size_t kDecompressedSheetSize = 0x800;
307 std::vector<uint8_t> base_data;
308 if (compressed) {
309 auto decomp_result = gfx::lc_lz2::DecompressV2(
310 rom_->data(), offset, static_cast<int>(kDecompressedSheetSize), 1,
311 rom_->size());
312 if (!decomp_result.ok()) {
313 return decomp_result.status();
314 }
315 base_data = std::move(*decomp_result);
316 } else {
317 auto read_result = rom_->ReadByteVector(offset, kDecompressedSheetSize);
318 if (!read_result.ok()) {
319 return read_result.status();
320 }
321 base_data = std::move(*read_result);
322 }
323
324 if (base_data.size() < snes_tile_data.size()) {
325 base_data.resize(snes_tile_data.size(), 0);
326 }
327 std::copy(snes_tile_data.begin(), snes_tile_data.end(), base_data.begin());
328
329 std::vector<uint8_t> final_data;
330 if (compressed) {
331 // Compress using Hyrule Magic LC-LZ2
332 int compressed_size = 0;
333 auto compressed_data = gfx::HyruleMagicCompress(
334 base_data.data(), static_cast<int>(base_data.size()),
335 &compressed_size, 1);
336 final_data.assign(compressed_data.begin(),
337 compressed_data.begin() + compressed_size);
338 } else {
339 final_data = std::move(base_data);
340 }
341
342 // Write data to ROM buffer
343 for (size_t i = 0; i < final_data.size(); i++) {
344 rom_->WriteByte(offset + i, final_data[i]);
345 }
346
347 LOG_INFO("GraphicsEditor",
348 "Saved sheet %02X (%zu bytes, %s) at offset %06X", sheet_id,
349 final_data.size(), compressed ? "compressed" : "raw", offset);
350 saved_sheets.insert(sheet_id);
351 }
352
353 // Clear modified tracking after successful save
354 state_.ClearModifiedSheets(saved_sheets);
355 if (!skipped_sheets.empty()) {
356 return absl::FailedPreconditionError(
357 absl::StrCat("Skipped ", skipped_sheets.size(),
358 " 2BPP sheet(s); full data unavailable."));
359 }
360
361 return absl::OkStatus();
362}
363
365 // Panels are now drawn via WorkspaceWindowManager::DrawAllVisiblePanels()
366 // This Update() only handles editor-level state and keyboard shortcuts
367
368 // Handle editor-level keyboard shortcuts
370
372 return absl::OkStatus();
373}
374
375absl::Status GraphicsEditor::Undo() {
376 return undo_manager_.Undo();
377}
378
379absl::Status GraphicsEditor::Redo() {
380 return undo_manager_.Redo();
381}
382
384 // Skip if ImGui wants keyboard input
385 if (ImGui::GetIO().WantTextInput) {
386 return;
387 }
388
389 // Tool shortcuts (only when graphics editor is active)
390 if (ImGui::IsKeyPressed(ImGuiKey_V, false)) {
392 }
393 if (ImGui::IsKeyPressed(ImGuiKey_B, false)) {
395 }
396 if (ImGui::IsKeyPressed(ImGuiKey_E, false)) {
398 }
399 if (ImGui::IsKeyPressed(ImGuiKey_G, false) && !ImGui::GetIO().KeyCtrl) {
401 }
402 if (ImGui::IsKeyPressed(ImGuiKey_I, false)) {
404 }
405 if (ImGui::IsKeyPressed(ImGuiKey_L, false) && !ImGui::GetIO().KeyCtrl) {
407 }
408 if (ImGui::IsKeyPressed(ImGuiKey_R, false) && !ImGui::GetIO().KeyCtrl) {
410 }
411
412 // Zoom shortcuts
413 if (ImGui::IsKeyPressed(ImGuiKey_Equal, false) ||
414 ImGui::IsKeyPressed(ImGuiKey_KeypadAdd, false)) {
415 state_.ZoomIn();
416 }
417 if (ImGui::IsKeyPressed(ImGuiKey_Minus, false) ||
418 ImGui::IsKeyPressed(ImGuiKey_KeypadSubtract, false)) {
419 state_.ZoomOut();
420 }
421
422 // Grid toggle (Ctrl+G)
423 if (ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_G, false)) {
425 }
426
427 // Sheet navigation
428 if (ImGui::IsKeyPressed(ImGuiKey_PageDown, false)) {
429 NextSheet();
430 }
431 if (ImGui::IsKeyPressed(ImGuiKey_PageUp, false)) {
432 PrevSheet();
433 }
434}
435
437 if (!rom_ || !rom_->is_loaded()) {
438 ImGui::TextWrapped(
439 "No ROM loaded — CGX/SCR/COL/BIN and clipboard tools work without one. "
440 "Load a ROM when you want vanilla palette presets or to save graphics "
441 "back into a cartridge image.");
442 ImGui::Spacing();
443 }
444 if (!prototype_import_feedback_.empty()) {
445 ImGui::TextColored(ImVec4(1.0f, 0.35f, 0.35f, 1.0f), "%s",
447 if (ImGui::SmallButton("Dismiss##prototype_import_feedback")) {
449 }
450 ImGui::Separator();
451 }
452
454 ImGui::Begin("Memory Editor", &open_memory_editor_);
456 ImGui::End();
457 }
458
459 constexpr ImGuiTableFlags kGfxEditFlags = ImGuiTableFlags_Reorderable |
460 ImGuiTableFlags_Resizable |
461 ImGuiTableFlags_SizingStretchSame;
462
463 BEGIN_TABLE("#gfxEditTable", 4, kGfxEditFlags)
464 SETUP_COLUMN("File Import (BIN, CGX, ROM)")
465 SETUP_COLUMN("Palette (COL)")
466 ImGui::TableSetupColumn("Tilemaps and Objects (SCR, PNL, OBJ)",
467 ImGuiTableColumnFlags_WidthFixed);
468 SETUP_COLUMN("Graphics Preview")
470 NEXT_COLUMN() {
475 }
476
477 NEXT_COLUMN() {
479 }
480
483 scr_loaded_, false, 0);
485
487 if (super_donkey_) {
488 // Super Donkey prototype graphics
489 for (size_t i = 0; i < num_sheets_to_load_ && i < gfx_sheets_.size(); i++) {
490 if (gfx_sheets_[i].is_active() && gfx_sheets_[i].texture()) {
491 ImGui::Image((ImTextureID)(intptr_t)gfx_sheets_[i].texture(),
492 ImVec2(128, 32));
493 if ((i + 1) % 4 != 0) {
494 ImGui::SameLine();
495 }
496 }
497 }
498 } else if (cgx_loaded_ && col_file_) {
499 // Load the CGX graphics
501 cgx_loaded_, true, 5);
502 } else {
503 // Load the BIN/Clipboard Graphics
505 gfx_loaded_, true, 2);
506 }
507 END_TABLE()
508}
509
510// =============================================================================
511// Prototype Viewer Import Methods
512// =============================================================================
513
515 gui::TextWithSeparators("Cgx Import");
516 InputInt("BPP", &current_bpp_);
517
518 InputText("##CGXFile", &cgx_file_name_);
519 SameLine();
520
521 if (ImGui::Button("Open CGX")) {
523 cgx_file_name_ = filename;
524 cgx_file_path_ = std::filesystem::absolute(filename).string();
525 is_open_ = true;
526 cgx_loaded_ = true;
527 }
528
529 if (ImGui::Button("Copy CGX Path")) {
530 ImGui::SetClipboardText(cgx_file_path_.c_str());
531 }
532
533 if (ImGui::Button("Load CGX Data")) {
536 if (!status_.ok()) {
537 prototype_import_feedback_ = absl::StrCat("[CGX] ", status_.message());
538 return absl::OkStatus();
539 }
541
542 cgx_bitmap_.Create(0x80, 0x200, 8, decoded_cgx_);
543 if (col_file_) {
547 }
548 }
549
550 return absl::OkStatus();
551}
552
554 InputText("##ScrFile", &scr_file_name_);
555
556 if (ImGui::Button("Open SCR")) {
558 scr_file_name_ = filename;
559 scr_file_path_ = std::filesystem::absolute(filename).string();
560 is_open_ = true;
561 scr_loaded_ = true;
562 }
563
564 InputInt("SCR Mod", &scr_mod_value_);
565
566 if (ImGui::Button("Load Scr Data")) {
568 if (!status_.ok()) {
569 prototype_import_feedback_ = absl::StrCat("[SCR] ", status_.message());
570 return absl::OkStatus();
571 }
572
573 decoded_scr_data_.resize(0x100 * 0x100);
576 if (!status_.ok()) {
578 absl::StrCat("[SCR draw] ", status_.message());
579 return absl::OkStatus();
580 }
582
583 scr_bitmap_.Create(0x100, 0x100, 8, decoded_scr_data_);
584 if (scr_loaded_) {
588 }
589 }
590
591 return absl::OkStatus();
592}
593
595 gui::TextWithSeparators("COL Import");
596 InputText("##ColFile", &col_file_name_);
597 SameLine();
598
599 if (ImGui::Button("Open COL")) {
601 col_file_name_ = filename;
602 col_file_path_ = std::filesystem::absolute(filename).string();
604 auto col_data_ = gfx::GetColFileData(temp_rom_.mutable_data());
605 if (col_file_palette_group_.size() != 0) {
607 }
608 auto col_file_palette_group_status =
610 if (col_file_palette_group_status.ok()) {
611 col_file_palette_group_ = col_file_palette_group_status.value();
612 }
614
615 // gigaleak dev format based code
617 col_file_ = true;
618 is_open_ = true;
619 }
620 HOVER_HINT(".COL, .BAK");
621
622 if (ImGui::Button("Copy Col Path")) {
623 ImGui::SetClipboardText(col_file_path_.c_str());
624 }
625
626 if (rom()->is_loaded()) {
627 gui::TextWithSeparators("ROM Palette");
628 gui::InputHex("Palette Index", &current_palette_index_);
629 ImGui::Combo("Palette", &current_palette_, kPaletteGroupAddressesKeys,
630 IM_ARRAYSIZE(kPaletteGroupAddressesKeys));
631 }
632
633 if (col_file_palette_.size() != 0) {
636 }
637
638 return absl::OkStatus();
639}
640
642 gui::TextWithSeparators("OBJ Import");
643
644 InputText("##ObjFile", &obj_file_path_);
645 SameLine();
646
647 if (ImGui::Button("Open OBJ")) {
649 obj_file_path_ = std::filesystem::absolute(filename).string();
651 is_open_ = true;
652 obj_loaded_ = true;
653 }
654 HOVER_HINT(".OBJ, .BAK");
655
656 return absl::OkStatus();
657}
658
660 gui::TextWithSeparators("Tilemap Import");
661
662 InputText("##TMapFile", &tilemap_file_path_);
663 SameLine();
664
665 if (ImGui::Button("Open Tilemap")) {
667 tilemap_file_path_ = std::filesystem::absolute(filename).string();
670
671 // Extract the high and low bytes from the file.
672 auto decomp_sheet = gfx::lc_lz2::DecompressV2(tilemap_rom_.data(), 0, 0x800,
675 tilemap_loaded_ = true;
676 is_open_ = true;
677 }
678 HOVER_HINT(".DAT, .BIN, .HEX");
679
680 return absl::OkStatus();
681}
682
684 gui::TextWithSeparators("BIN Import");
685
686 InputText("##ROMFile", &file_path_);
687 SameLine();
688
689 if (ImGui::Button("Open BIN")) {
691 file_path_ = filename;
693 is_open_ = true;
694 }
695 HOVER_HINT(".BIN, .HEX");
696
697 if (Button("Copy File Path")) {
698 ImGui::SetClipboardText(file_path_.c_str());
699 }
700
701 gui::InputHex("BIN Offset", &current_offset_);
702 gui::InputHex("BIN Size", &bin_size_);
703
704 if (Button("Decompress BIN")) {
705 if (file_path_.empty()) {
706 return absl::InvalidArgumentError(
707 "Please select a file before decompressing.");
708 }
710 }
711
712 return absl::OkStatus();
713}
714
716 gui::TextWithSeparators("Clipboard Import");
717 if (Button("Paste From Clipboard")) {
718 const char* text = ImGui::GetClipboardText();
719 if (text) {
720 const auto clipboard_data =
721 std::vector<uint8_t>(text, text + strlen(text));
722 ImGui::MemFree((void*)text);
723 status_ = temp_rom_.LoadFromData(clipboard_data);
724 is_open_ = true;
725 open_memory_editor_ = true;
726 }
727 }
730 gui::InputHex("Num Sheets", &num_sheets_to_load_);
731
732 if (Button("Decompress Clipboard Data")) {
733 if (temp_rom_.is_loaded()) {
734 status_ = DecompressImportData(0x40000);
735 } else {
736 status_ = absl::InvalidArgumentError(
737 "Please paste data into the clipboard before "
738 "decompressing.");
739 }
740 }
741
742 return absl::OkStatus();
743}
744
746 gui::TextWithSeparators("Experimental");
747 if (Button("Decompress Super Donkey Full")) {
748 if (file_path_.empty()) {
749 return absl::InvalidArgumentError(
750 "Please select `super_donkey_1.bin` before "
751 "importing.");
752 }
754 }
755 ImGui::SetItemTooltip(
756 "Requires `super_donkey_1.bin` to be imported under the "
757 "BIN import section.");
758 return absl::OkStatus();
759}
760
762 std::string title = "Memory Editor";
763 if (is_open_) {
764 static yaze::gui::MemoryEditorWidget mem_edit;
765 mem_edit.DrawWindow(title.c_str(), temp_rom_.mutable_data(),
766 temp_rom_.size());
767 }
768 return absl::OkStatus();
769}
770
774 size, 1, temp_rom_.size()));
775
776 auto converted_sheet = gfx::SnesTo8bppSheet(import_data_, 3);
778 converted_sheet);
779
780 if (rom()->is_loaded() && game_data()) {
781 auto palette_group = game_data()->palette_groups.overworld_animated;
782 z3_rom_palette_ = palette_group[current_palette_];
783 if (col_file_) {
785 } else {
787 }
788 }
789
791 &bin_bitmap_);
792 gfx_loaded_ = true;
793
794 return absl::OkStatus();
795}
796
798 int i = 0;
799 for (const auto& offset : kSuperDonkeyTiles) {
800 int offset_value =
801 std::stoi(offset, nullptr, 16); // convert hex string to int
802 ASSIGN_OR_RETURN(auto decompressed_data,
804 0x1000, 1, temp_rom_.size()));
805 auto converted_sheet = gfx::SnesTo8bppSheet(decompressed_data, 3);
807 gfx::kTilesheetDepth, converted_sheet);
808 if (col_file_) {
809 gfx_sheets_[i].SetPalette(
811 } else {
812 // ROM palette
813 if (!game_data()) {
814 return absl::FailedPreconditionError("GameData not available");
815 }
816 auto palette_group = game_data()->palette_groups.get_group(
817 kPaletteGroupAddressesKeys[current_palette_]);
818 z3_rom_palette_ = palette_group->palette(current_palette_index_);
819 gfx_sheets_[i].SetPalette(z3_rom_palette_);
820 }
821
824 i++;
825 }
826
827 for (const auto& offset : kSuperDonkeySprites) {
828 int offset_value =
829 std::stoi(offset, nullptr, 16); // convert hex string to int
830 ASSIGN_OR_RETURN(auto decompressed_data,
832 0x1000, 1, temp_rom_.size()));
833 auto converted_sheet = gfx::SnesTo8bppSheet(decompressed_data, 3);
835 gfx::kTilesheetDepth, converted_sheet);
836 if (col_file_) {
837 gfx_sheets_[i].SetPalette(
839 } else {
840 // ROM palette
841 if (game_data()) {
842 auto palette_group = game_data()->palette_groups.get_group(
843 kPaletteGroupAddressesKeys[current_palette_]);
844 z3_rom_palette_ = palette_group->palette(current_palette_index_);
845 gfx_sheets_[i].SetPalette(z3_rom_palette_);
846 }
847 }
848
851 i++;
852 }
853 super_donkey_ = true;
855
856 return absl::OkStatus();
857}
858
864
870
871void GraphicsEditor::SelectSheet(uint16_t sheet_id) {
872 if (sheet_id >= zelda3::kNumGfxSheets) {
873 return;
874 }
875 state_.SelectSheet(sheet_id);
876}
877
878void GraphicsEditor::HighlightTile(uint16_t sheet_id, uint16_t tile_index,
879 const std::string& label,
880 double duration_secs) {
881 if (sheet_id >= zelda3::kNumGfxSheets) {
882 return;
883 }
884 state_.HighlightTile(sheet_id, tile_index, label, duration_secs);
885}
886
887} // namespace editor
888} // namespace yaze
absl::StatusOr< std::vector< uint8_t > > ReadByteVector(uint32_t offset, uint32_t length) const
Definition rom.cc:431
absl::Status LoadFromFile(const std::string &filename, const LoadOptions &options=LoadOptions::Defaults())
Definition rom.cc:155
absl::Status WriteByte(int addr, uint8_t value)
Definition rom.cc:476
auto mutable_data()
Definition rom.h:140
auto data() const
Definition rom.h:139
auto size() const
Definition rom.h:138
absl::Status LoadFromData(const std::vector< uint8_t > &data, const LoadOptions &options=LoadOptions::Defaults())
Definition rom.cc:255
bool is_loaded() const
Definition rom.h:132
static RomSettings & Get()
uint32_t GetAddressOr(const std::string &key, uint32_t default_value) const
UndoManager undo_manager_
Definition editor.h:317
zelda3::GameData * game_data() const
Definition editor.h:307
EditorDependencies dependencies_
Definition editor.h:316
void HighlightTile(uint16_t sheet_id, uint16_t tile_index, const std::string &label="", double duration_secs=3.0)
Highlight a tile in the current sheet for quick visual focus.
bool HasUnsavedChanges() const
Check if any sheets have unsaved changes.
void SelectSheet(uint16_t sheet_id)
Select a sheet for editing.
void SetTool(PixelTool tool)
Set the current editing tool.
void ClearModifiedSheets()
Clear modification tracking (after save)
std::vector< uint8_t > scr_data_
absl::Status Save() override
void HighlightTile(uint16_t sheet_id, uint16_t tile_index, const std::string &label="", double duration_secs=3.0)
gfx::PaletteGroup col_file_palette_group_
void SelectSheet(uint16_t sheet_id)
absl::Status Load() override
std::unique_ptr< GfxGroupEditor > gfx_group_panel_
std::unique_ptr< PaletteControlsPanel > palette_controls_panel_
std::vector< uint8_t > decoded_cgx_
std::vector< uint8_t > cgx_data_
std::unique_ptr< PixelEditorPanel > pixel_editor_panel_
absl::Status Redo() override
std::unique_ptr< PolyhedralEditorPanel > polyhedral_panel_
std::unique_ptr< PalettesetEditorPanel > paletteset_panel_
std::vector< uint8_t > import_data_
std::vector< SDL_Color > decoded_col_
absl::Status Undo() override
absl::Status Update() override
std::vector< uint8_t > extra_cgx_data_
std::array< gfx::Bitmap, zelda3::kNumGfxSheets > gfx_sheets_
std::vector< uint8_t > decoded_scr_data_
std::unique_ptr< LinkSpritePanel > link_sprite_panel_
absl::Status DecompressImportData(int size)
void ContributeStatus(StatusBar *status_bar) override
std::unique_ptr< SheetBrowserPanel > sheet_browser_panel_
A session-aware status bar displayed at the bottom of the application.
Definition status_bar.h:54
void SetSelection(int count, int width=0, int height=0)
Set selection information.
void SetCustomSegment(const std::string &key, const std::string &value)
Set a custom segment with key-value pair.
absl::Status Redo()
Redo the top action. Returns error if stack is empty.
absl::Status Undo()
Undo the top action. Returns error if stack is empty.
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
Definition arena.cc:36
std::array< gfx::Bitmap, 223 > & gfx_sheets()
Get reference to all graphics sheets.
Definition arena.h:152
static Arena & Get()
Definition arena.cc:21
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:67
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:201
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap using SNES palette format.
Definition bitmap.cc:384
RAII timer for automatic timing management.
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath. Uses global feature flag to...
#define LOG_WARN(category, format,...)
Definition log.h:107
#define LOG_INFO(category, format,...)
Definition log.h:105
#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 NEXT_COLUMN()
Definition macro.h:18
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition macro.h:62
#define CLEAR_AND_RETURN_STATUS(status)
Definition macro.h:97
#define HOVER_HINT(string)
Definition macro.h:24
constexpr char kOverworldGfxPtr3[]
constexpr char kOverworldGfxPtr1[]
constexpr char kOverworldGfxPtr2[]
const std::string kSuperDonkeySprites[]
const std::string kSuperDonkeyTiles[]
absl::StatusOr< std::vector< uint8_t > > DecompressV2(const uint8_t *data, int offset, int size, int mode, size_t rom_size)
Decompresses a buffer of data using the LC_LZ2 algorithm.
constexpr int kNintendoMode1
Definition compression.h:54
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< const uint8_t > sheet, int bpp, int num_sheets)
Definition snes_tile.cc:132
std::vector< uint8_t > IndexedToSnesSheet(std::span< const uint8_t > sheet, int bpp, int num_sheets)
Definition snes_tile.cc:203
std::vector< uint8_t > HyruleMagicCompress(uint8_t const *const src, int const oldsize, int *const size, int const flag)
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:1751
bool InputHex(const char *label, uint64_t *data)
Definition input.cc:335
void SelectablePalettePipeline(uint64_t &palette_id, bool &refresh_graphics, gfx::SnesPalette &palette)
Definition color.cc:312
void TextWithSeparators(const absl::string_view &text)
Definition style.cc:1320
constexpr uint32_t kNumGfxSheets
Definition game_data.h:25
uint32_t GetGraphicsAddress(const uint8_t *data, uint8_t addr, uint32_t ptr1, uint32_t ptr2, uint32_t ptr3, size_t rom_size)
Gets the graphics address for a sheet index.
Definition game_data.cc:112
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22
WorkspaceWindowManager * window_manager
Definition editor.h:176
GfxGroupWorkspaceState * gfx_group_workspace
Definition editor.h:173
Optional behavior for an interactive status bar segment.
Definition status_bar.h:27
PaletteGroup * get_group(const std::string &group_name)
void DrawWindow(const char *title, void *mem_data, size_t mem_size, size_t base_display_addr=0x0000)
gfx::PaletteGroupMap palette_groups
Definition game_data.h:91