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#include "util/i18n/tr.h"
4
5// C++ standard library headers
6#include <algorithm>
7#include <filesystem>
8#include <set>
9
10// Third-party library headers
11#include "absl/status/status.h"
12#include "absl/status/statusor.h"
13#include "absl/strings/str_cat.h"
14#include "absl/strings/str_format.h"
15#include "imgui/imgui.h"
16#include "imgui/misc/cpp/imgui_stdlib.h"
17
18// Project headers
22#include "app/gfx/core/bitmap.h"
30#include "app/gui/core/color.h"
31#include "app/gui/core/icons.h"
32#include "app/gui/core/input.h"
33#include "app/gui/core/style.h"
37#include "app/platform/window.h"
38#include "core/rom_settings.h"
39#include "rom/rom.h"
40#include "rom/snes.h"
41#include "util/file_util.h"
42#include "util/log.h"
43
44namespace yaze {
45namespace editor {
46
48using ImGui::Button;
49using ImGui::InputInt;
50using ImGui::InputText;
51using ImGui::SameLine;
52
55 return;
56 auto* window_manager = dependencies_.window_manager;
57
58 // Initialize panel components
59 sheet_browser_panel_ = std::make_unique<SheetBrowserPanel>(&state_);
61 std::make_unique<PixelEditorPanel>(&state_, rom_, &undo_manager_);
63 std::make_unique<PaletteControlsPanel>(&state_, rom_);
64 link_sprite_panel_ = std::make_unique<LinkSpritePanel>(&state_, rom_);
65 gfx_group_panel_ = std::make_unique<GfxGroupEditor>();
67 gfx_group_panel_->SetRom(rom_);
68 gfx_group_panel_->SetGameData(game_data_);
69 gfx_group_panel_->SetHostSurfaceHint(
70 "Gfx Groups: blockset/roomset/spriteset selection syncs with the "
71 "Overworld "
72 "editor for this ROM session (each surface has its own preview "
73 "canvases).");
74 paletteset_panel_ = std::make_unique<PalettesetEditorPanel>();
75 paletteset_panel_->SetRom(rom_);
76 paletteset_panel_->SetGameData(game_data_);
77
78 polyhedral_panel_ = std::make_unique<PolyhedralEditorPanel>(rom_);
79 polyhedral_panel_->SetRom(rom_);
80
81 sheet_browser_panel_->Initialize();
82 pixel_editor_panel_->Initialize();
83 palette_controls_panel_->Initialize();
84 link_sprite_panel_->Initialize();
85
86 // Register panels using WindowContent system with callbacks
87 window_manager->RegisterWindowContent(
88 std::make_unique<GraphicsSheetBrowserPanel>([this]() {
90 status_ = sheet_browser_panel_->Update();
91 }
92 }));
93
94 window_manager->RegisterWindowContent(
95 std::make_unique<GraphicsPixelEditorPanel>([this]() {
97 status_ = pixel_editor_panel_->Update();
98 }
99 }));
100
101 window_manager->RegisterWindowContent(
102 std::make_unique<GraphicsPaletteControlsPanel>([this]() {
105 }
106 }));
107
108 window_manager->RegisterWindowContent(
109 std::make_unique<GraphicsLinkSpritePanel>([this]() {
110 if (link_sprite_panel_) {
111 status_ = link_sprite_panel_->Update();
112 }
113 }));
114
115 window_manager->RegisterWindowContent(
116 std::make_unique<GraphicsGfxGroupPanel>([this]() {
117 if (gfx_group_panel_) {
118 status_ = gfx_group_panel_->Update();
119 }
120 }));
121
122 // Paletteset editor panel (separated from GfxGroupEditor for better UX)
123 window_manager->RegisterWindowContent(
124 std::make_unique<GraphicsPalettesetPanel>([this]() {
125 if (paletteset_panel_) {
126 status_ = paletteset_panel_->Update();
127 }
128 }));
129
130 window_manager->RegisterWindowContent(
131 std::make_unique<GraphicsPrototypeViewerPanel>(
132 [this]() { DrawPrototypeViewer(); }));
133
134 window_manager->RegisterWindowContent(
135 std::make_unique<GraphicsPolyhedralPanel>([this]() {
136 if (polyhedral_panel_) {
137 bool open = true;
138 polyhedral_panel_->Draw(&open);
139 }
140 }));
141}
142
143absl::Status GraphicsEditor::Load() {
144 gfx::ScopedTimer timer("GraphicsEditor::Load");
145
146 // Initialize all graphics sheets with appropriate palettes from ROM
147 // This ensures textures are created for editing
148 if (rom()->is_loaded()) {
149 auto& sheets = gfx::Arena::Get().gfx_sheets();
150
151 // Apply default palettes to all sheets based on common SNES ROM structure
152 // Sheets 0-112: Use overworld/dungeon palettes
153 // Sheets 113-127: Use sprite palettes
154 // Sheets 128-222: Use auxiliary/menu palettes
155
156 LOG_INFO("GraphicsEditor", "Initializing textures for %d graphics sheets",
158
159 int sheets_queued = 0;
160 for (int i = 0; i < zelda3::kNumGfxSheets; i++) {
161 if (!sheets[i].is_active() || !sheets[i].surface()) {
162 continue; // Skip inactive or surface-less sheets
163 }
164
165 // Palettes are now applied during ROM loading in LoadAllGraphicsData()
166 // Just queue texture creation for sheets that don't have textures yet
167 if (!sheets[i].texture()) {
168 // Fix: Ensure default palettes are applied if missing
169 // This handles the case where sheets are loaded but have no palette assigned
170 if (sheets[i].palette().empty()) {
171 // Default palette assignment logic
172 if (i <= 112) {
173 // Overworld/Dungeon sheets - use Dungeon Main palette (Group 0, Index 0)
174 if (game_data() &&
175 game_data()->palette_groups.dungeon_main.size() > 0) {
176 sheets[i].SetPaletteWithTransparent(
177 game_data()->palette_groups.dungeon_main.palette(0), 0);
178 }
179 } else if (i >= 113 && i <= 127) {
180 // Sprite sheets - use Sprites Aux1 palette (Group 4, Index 0)
181 if (game_data() &&
182 game_data()->palette_groups.sprites_aux1.size() > 0) {
183 sheets[i].SetPaletteWithTransparent(
184 game_data()->palette_groups.sprites_aux1.palette(0), 0);
185 }
186 } else {
187 // Menu/Aux sheets - use HUD palette if available, or fallback
188 if (game_data() && game_data()->palette_groups.hud.size() > 0) {
189 sheets[i].SetPaletteWithTransparent(
190 game_data()->palette_groups.hud.palette(0), 0);
191 }
192 }
193 }
194
197 sheets_queued++;
198 }
199 }
200
201 LOG_INFO("GraphicsEditor", "Queued texture creation for %d graphics sheets",
202 sheets_queued);
203 }
204
205 return absl::OkStatus();
206}
207
209 if (!status_bar)
210 return;
211
212 StatusBarSegmentOptions sheet_opts;
213 sheet_opts.tooltip = absl::StrFormat(
214 "Sheet %d (0x%02X) — use Command Palette to jump between sheets",
216 status_bar->SetCustomSegment(
217 "Sheet", absl::StrFormat("0x%02X", state_.current_sheet_id),
218 std::move(sheet_opts));
219
220 if (!state_.selected_sheets.empty()) {
221 status_bar->SetSelection(static_cast<int>(state_.selected_sheets.size()));
222 }
224 StatusBarSegmentOptions modified_opts;
225 modified_opts.tooltip = absl::StrFormat(
226 "%zu modified sheet%s pending save", state_.modified_sheets.size(),
227 state_.modified_sheets.size() == 1 ? "" : "s");
228 status_bar->SetCustomSegment(
229 "Modified", absl::StrFormat("%zu", state_.modified_sheets.size()),
230 std::move(modified_opts));
231 }
232}
233
234absl::Status GraphicsEditor::Save() {
235 if (!rom_ || !rom_->is_loaded()) {
236 return absl::FailedPreconditionError("ROM not loaded");
237 }
238
239 // Only save sheets that have been modified
240 if (!state_.HasUnsavedChanges()) {
241 LOG_INFO("GraphicsEditor", "No modified sheets to save");
242 return absl::OkStatus();
243 }
244
245 LOG_INFO("GraphicsEditor", "Saving %zu modified graphics sheets",
246 state_.modified_sheets.size());
247
248 auto& sheets = gfx::Arena::Get().gfx_sheets();
249 std::set<uint16_t> saved_sheets;
250 std::vector<uint16_t> skipped_sheets;
251
252 for (uint16_t sheet_id : state_.modified_sheets) {
253 if (sheet_id >= zelda3::kNumGfxSheets)
254 continue;
255
256 auto& sheet = sheets[sheet_id];
257 if (!sheet.is_active())
258 continue;
259
260 // Determine BPP and compression based on sheet range
261 int bpp = 3; // Default 3BPP
262 bool compressed = true;
263
264 // Sheets 113-114, 218+ are 2BPP
265 if (sheet_id == 113 || sheet_id == 114 || sheet_id >= 218) {
266 bpp = 2;
267 }
268
269 // Sheets 115-126 are uncompressed
270 if (sheet_id >= 115 && sheet_id <= 126) {
271 compressed = false;
272 }
273
274 if (bpp == 2) {
275 const size_t expected_size =
277 const size_t actual_size = sheet.vector().size();
278 if (actual_size < expected_size) {
279 LOG_WARN("GraphicsEditor",
280 "Skipping 2BPP sheet %02X save (expected %zu bytes, got %zu)",
281 sheet_id, expected_size, actual_size);
282 skipped_sheets.push_back(sheet_id);
283 continue;
284 }
285 }
286
287 // Calculate ROM offset for this sheet
288 // Get version constants from game_data
289 auto version_constants =
290 zelda3::kVersionConstantsMap.at(game_data()->version);
291 const uint32_t gfx_ptr1 = core::RomSettings::Get().GetAddressOr(
293 version_constants.kOverworldGfxPtr1);
294 const uint32_t gfx_ptr2 = core::RomSettings::Get().GetAddressOr(
296 version_constants.kOverworldGfxPtr2);
297 const uint32_t gfx_ptr3 = core::RomSettings::Get().GetAddressOr(
299 version_constants.kOverworldGfxPtr3);
300 uint32_t offset =
301 zelda3::GetGraphicsAddress(rom_->data(), static_cast<uint8_t>(sheet_id),
302 gfx_ptr1, gfx_ptr2, gfx_ptr3, rom_->size());
303
304 // Convert 8BPP bitmap data to SNES planar format
305 auto snes_tile_data = gfx::IndexedToSnesSheet(sheet.vector(), bpp);
306
307 constexpr size_t kDecompressedSheetSize = 0x800;
308 std::vector<uint8_t> base_data;
309 if (compressed) {
310 auto decomp_result = gfx::lc_lz2::DecompressV2(
311 rom_->data(), offset, static_cast<int>(kDecompressedSheetSize), 1,
312 rom_->size());
313 if (!decomp_result.ok()) {
314 return decomp_result.status();
315 }
316 base_data = std::move(*decomp_result);
317 } else {
318 auto read_result = rom_->ReadByteVector(offset, kDecompressedSheetSize);
319 if (!read_result.ok()) {
320 return read_result.status();
321 }
322 base_data = std::move(*read_result);
323 }
324
325 if (base_data.size() < snes_tile_data.size()) {
326 base_data.resize(snes_tile_data.size(), 0);
327 }
328 std::copy(snes_tile_data.begin(), snes_tile_data.end(), base_data.begin());
329
330 std::vector<uint8_t> final_data;
331 if (compressed) {
332 // Compress using Hyrule Magic LC-LZ2
333 int compressed_size = 0;
334 auto compressed_data = gfx::HyruleMagicCompress(
335 base_data.data(), static_cast<int>(base_data.size()),
336 &compressed_size, 1);
337 final_data.assign(compressed_data.begin(),
338 compressed_data.begin() + compressed_size);
339 } else {
340 final_data = std::move(base_data);
341 }
342
343 // Write data to ROM buffer
344 for (size_t i = 0; i < final_data.size(); i++) {
345 rom_->WriteByte(offset + i, final_data[i]);
346 }
347
348 LOG_INFO("GraphicsEditor",
349 "Saved sheet %02X (%zu bytes, %s) at offset %06X", sheet_id,
350 final_data.size(), compressed ? "compressed" : "raw", offset);
351 saved_sheets.insert(sheet_id);
352 }
353
354 // Clear modified tracking after successful save
355 state_.ClearModifiedSheets(saved_sheets);
356 if (!skipped_sheets.empty()) {
357 return absl::FailedPreconditionError(
358 absl::StrCat("Skipped ", skipped_sheets.size(),
359 " 2BPP sheet(s); full data unavailable."));
360 }
361
362 return absl::OkStatus();
363}
364
366 // Panels are now drawn via WorkspaceWindowManager::DrawAllVisiblePanels()
367 // This Update() only handles editor-level state and keyboard shortcuts
368
369 // Handle editor-level keyboard shortcuts
371
373 return absl::OkStatus();
374}
375
376absl::Status GraphicsEditor::Undo() {
377 return undo_manager_.Undo();
378}
379
380absl::Status GraphicsEditor::Redo() {
381 return undo_manager_.Redo();
382}
383
385 // Skip if ImGui wants keyboard input
386 if (ImGui::GetIO().WantTextInput) {
387 return;
388 }
389
390 // Tool shortcuts (only when graphics editor is active)
391 if (ImGui::IsKeyPressed(ImGuiKey_V, false)) {
393 }
394 if (ImGui::IsKeyPressed(ImGuiKey_B, false)) {
396 }
397 if (ImGui::IsKeyPressed(ImGuiKey_E, false)) {
399 }
400 if (ImGui::IsKeyPressed(ImGuiKey_G, false) && !ImGui::GetIO().KeyCtrl) {
402 }
403 if (ImGui::IsKeyPressed(ImGuiKey_I, false)) {
405 }
406 if (ImGui::IsKeyPressed(ImGuiKey_L, false) && !ImGui::GetIO().KeyCtrl) {
408 }
409 if (ImGui::IsKeyPressed(ImGuiKey_R, false) && !ImGui::GetIO().KeyCtrl) {
411 }
412
413 // Zoom shortcuts
414 if (ImGui::IsKeyPressed(ImGuiKey_Equal, false) ||
415 ImGui::IsKeyPressed(ImGuiKey_KeypadAdd, false)) {
416 state_.ZoomIn();
417 }
418 if (ImGui::IsKeyPressed(ImGuiKey_Minus, false) ||
419 ImGui::IsKeyPressed(ImGuiKey_KeypadSubtract, false)) {
420 state_.ZoomOut();
421 }
422
423 // Grid toggle (Ctrl+G)
424 if (ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_G, false)) {
426 }
427
428 // Sheet navigation
429 if (ImGui::IsKeyPressed(ImGuiKey_PageDown, false)) {
430 NextSheet();
431 }
432 if (ImGui::IsKeyPressed(ImGuiKey_PageUp, false)) {
433 PrevSheet();
434 }
435}
436
438 if (!rom_ || !rom_->is_loaded()) {
439 ImGui::TextWrapped(tr(
440 "No ROM loaded — CGX/SCR/COL/BIN and clipboard tools work without one. "
441 "Load a ROM when you want vanilla palette presets or to save graphics "
442 "back into a cartridge image."));
443 ImGui::Spacing();
444 }
445 if (!prototype_import_feedback_.empty()) {
446 ImGui::TextColored(ImVec4(1.0f, 0.35f, 0.35f, 1.0f), "%s",
448 if (ImGui::SmallButton(tr("Dismiss##prototype_import_feedback"))) {
450 }
451 ImGui::Separator();
452 }
453
455 ImGui::Begin("Memory Editor", &open_memory_editor_);
457 ImGui::End();
458 }
459
460 constexpr ImGuiTableFlags kGfxEditFlags = ImGuiTableFlags_Reorderable |
461 ImGuiTableFlags_Resizable |
462 ImGuiTableFlags_SizingStretchSame;
463
464 BEGIN_TABLE("#gfxEditTable", 4, kGfxEditFlags)
465 SETUP_COLUMN("File Import (BIN, CGX, ROM)")
466 SETUP_COLUMN("Palette (COL)")
467 ImGui::TableSetupColumn("Tilemaps and Objects (SCR, PNL, OBJ)",
468 ImGuiTableColumnFlags_WidthFixed);
469 SETUP_COLUMN("Graphics Preview")
471 NEXT_COLUMN() {
476 }
477
478 NEXT_COLUMN() {
480 }
481
485 scr_loaded_, false, 0);
487
489 if (super_donkey_) {
490 // Super Donkey prototype graphics
491 for (size_t i = 0; i < num_sheets_to_load_ && i < gfx_sheets_.size(); i++) {
492 if (gfx_sheets_[i].is_active() && gfx_sheets_[i].texture()) {
493 ImGui::Image((ImTextureID)(intptr_t)gfx_sheets_[i].texture(),
494 ImVec2(128, 32));
495 if ((i + 1) % 4 != 0) {
496 ImGui::SameLine();
497 }
498 }
499 }
500 } else if (cgx_loaded_ && col_file_) {
501 // Load the CGX graphics
504 cgx_loaded_, true, 5);
505 } else {
506 // Load the BIN/Clipboard Graphics
509 gfx_loaded_, true, 2);
510 }
511 END_TABLE()
512}
513
514// =============================================================================
515// Prototype Viewer Import Methods
516// =============================================================================
517
519 gui::TextWithSeparators("Cgx Import");
520 InputInt(tr("BPP"), &current_bpp_);
521
522 InputText("##CGXFile", &cgx_file_name_);
523 SameLine();
524
525 if (ImGui::Button(tr("Open CGX"))) {
527 cgx_file_name_ = filename;
528 cgx_file_path_ = std::filesystem::absolute(filename).string();
529 is_open_ = true;
530 cgx_loaded_ = true;
531 }
532
533 if (ImGui::Button(tr("Copy CGX Path"))) {
534 ImGui::SetClipboardText(cgx_file_path_.c_str());
535 }
536
537 if (ImGui::Button(tr("Load CGX Data"))) {
540 if (!status_.ok()) {
541 prototype_import_feedback_ = absl::StrCat("[CGX] ", status_.message());
542 return absl::OkStatus();
543 }
545
546 cgx_bitmap_.Create(0x80, 0x200, 8, decoded_cgx_);
547 if (col_file_) {
551 }
552 }
553
554 return absl::OkStatus();
555}
556
558 InputText("##ScrFile", &scr_file_name_);
559
560 if (ImGui::Button(tr("Open SCR"))) {
562 scr_file_name_ = filename;
563 scr_file_path_ = std::filesystem::absolute(filename).string();
564 is_open_ = true;
565 scr_loaded_ = true;
566 }
567
568 InputInt(tr("SCR Mod"), &scr_mod_value_);
569
570 if (ImGui::Button(tr("Load Scr Data"))) {
572 if (!status_.ok()) {
573 prototype_import_feedback_ = absl::StrCat("[SCR] ", status_.message());
574 return absl::OkStatus();
575 }
576
577 decoded_scr_data_.resize(0x100 * 0x100);
580 if (!status_.ok()) {
582 absl::StrCat("[SCR draw] ", status_.message());
583 return absl::OkStatus();
584 }
586
587 scr_bitmap_.Create(0x100, 0x100, 8, decoded_scr_data_);
588 if (scr_loaded_) {
592 }
593 }
594
595 return absl::OkStatus();
596}
597
599 gui::TextWithSeparators("COL Import");
600 InputText("##ColFile", &col_file_name_);
601 SameLine();
602
603 if (ImGui::Button(tr("Open COL"))) {
605 col_file_name_ = filename;
606 col_file_path_ = std::filesystem::absolute(filename).string();
608 auto col_data_ = gfx::GetColFileData(temp_rom_.mutable_data());
609 if (col_file_palette_group_.size() != 0) {
611 }
612 auto col_file_palette_group_status =
614 if (col_file_palette_group_status.ok()) {
615 col_file_palette_group_ = col_file_palette_group_status.value();
616 }
618
619 // gigaleak dev format based code
621 col_file_ = true;
622 is_open_ = true;
623 }
624 HOVER_HINT(".COL, .BAK");
625
626 if (ImGui::Button(tr("Copy Col Path"))) {
627 ImGui::SetClipboardText(col_file_path_.c_str());
628 }
629
630 if (rom()->is_loaded()) {
631 gui::TextWithSeparators("ROM Palette");
632 gui::InputHex("Palette Index", &current_palette_index_);
633 ImGui::Combo(tr("Palette"), &current_palette_, kPaletteGroupAddressesKeys,
634 IM_ARRAYSIZE(kPaletteGroupAddressesKeys));
635 }
636
637 if (col_file_palette_.size() != 0) {
640 }
641
642 return absl::OkStatus();
643}
644
646 gui::TextWithSeparators("OBJ Import");
647
648 InputText("##ObjFile", &obj_file_path_);
649 SameLine();
650
651 if (ImGui::Button(tr("Open OBJ"))) {
653 obj_file_path_ = std::filesystem::absolute(filename).string();
655 is_open_ = true;
656 obj_loaded_ = true;
657 }
658 HOVER_HINT(".OBJ, .BAK");
659
660 return absl::OkStatus();
661}
662
664 gui::TextWithSeparators("Tilemap Import");
665
666 InputText("##TMapFile", &tilemap_file_path_);
667 SameLine();
668
669 if (ImGui::Button(tr("Open Tilemap"))) {
671 tilemap_file_path_ = std::filesystem::absolute(filename).string();
674
675 // Extract the high and low bytes from the file.
676 auto decomp_sheet = gfx::lc_lz2::DecompressV2(tilemap_rom_.data(), 0, 0x800,
679 tilemap_loaded_ = true;
680 is_open_ = true;
681 }
682 HOVER_HINT(".DAT, .BIN, .HEX");
683
684 return absl::OkStatus();
685}
686
688 gui::TextWithSeparators("BIN Import");
689
690 InputText("##ROMFile", &file_path_);
691 SameLine();
692
693 if (ImGui::Button(tr("Open BIN"))) {
695 file_path_ = filename;
697 is_open_ = true;
698 }
699 HOVER_HINT(".BIN, .HEX");
700
701 if (Button(tr("Copy File Path"))) {
702 ImGui::SetClipboardText(file_path_.c_str());
703 }
704
705 gui::InputHex("BIN Offset", &current_offset_);
706 gui::InputHex("BIN Size", &bin_size_);
707
708 if (Button(tr("Decompress BIN"))) {
709 if (file_path_.empty()) {
710 return absl::InvalidArgumentError(
711 "Please select a file before decompressing.");
712 }
714 }
715
716 return absl::OkStatus();
717}
718
720 gui::TextWithSeparators("Clipboard Import");
721 if (Button(tr("Paste From Clipboard"))) {
722 const char* text = ImGui::GetClipboardText();
723 if (text) {
724 const auto clipboard_data =
725 std::vector<uint8_t>(text, text + strlen(text));
726 ImGui::MemFree((void*)text);
727 status_ = temp_rom_.LoadFromData(clipboard_data);
728 is_open_ = true;
729 open_memory_editor_ = true;
730 }
731 }
734 gui::InputHex("Num Sheets", &num_sheets_to_load_);
735
736 if (Button(tr("Decompress Clipboard Data"))) {
737 if (temp_rom_.is_loaded()) {
738 status_ = DecompressImportData(0x40000);
739 } else {
740 status_ = absl::InvalidArgumentError(
741 "Please paste data into the clipboard before "
742 "decompressing.");
743 }
744 }
745
746 return absl::OkStatus();
747}
748
750 gui::TextWithSeparators("Experimental");
751 if (Button(tr("Decompress Super Donkey Full"))) {
752 if (file_path_.empty()) {
753 return absl::InvalidArgumentError(
754 "Please select `super_donkey_1.bin` before "
755 "importing.");
756 }
758 }
759 ImGui::SetItemTooltip(
760 tr("Requires `super_donkey_1.bin` to be imported under the "
761 "BIN import section."));
762 return absl::OkStatus();
763}
764
766 std::string title = "Memory Editor";
767 if (is_open_) {
768 static yaze::gui::MemoryEditorWidget mem_edit;
769 mem_edit.DrawWindow(title.c_str(), temp_rom_.mutable_data(),
770 temp_rom_.size());
771 }
772 return absl::OkStatus();
773}
774
778 size, 1, temp_rom_.size()));
779
780 auto converted_sheet = gfx::SnesTo8bppSheet(import_data_, 3);
782 converted_sheet);
783
784 if (rom()->is_loaded() && game_data()) {
785 auto palette_group = game_data()->palette_groups.overworld_animated;
786 z3_rom_palette_ = palette_group[current_palette_];
787 if (col_file_) {
789 } else {
791 }
792 }
793
795 &bin_bitmap_);
796 gfx_loaded_ = true;
797
798 return absl::OkStatus();
799}
800
802 int i = 0;
803 for (const auto& offset : kSuperDonkeyTiles) {
804 int offset_value =
805 std::stoi(offset, nullptr, 16); // convert hex string to int
806 ASSIGN_OR_RETURN(auto decompressed_data,
808 0x1000, 1, temp_rom_.size()));
809 auto converted_sheet = gfx::SnesTo8bppSheet(decompressed_data, 3);
811 gfx::kTilesheetDepth, converted_sheet);
812 if (col_file_) {
813 gfx_sheets_[i].SetPalette(
815 } else {
816 // ROM palette
817 if (!game_data()) {
818 return absl::FailedPreconditionError("GameData not available");
819 }
820 auto palette_group = game_data()->palette_groups.get_group(
821 kPaletteGroupAddressesKeys[current_palette_]);
822 z3_rom_palette_ = palette_group->palette(current_palette_index_);
823 gfx_sheets_[i].SetPalette(z3_rom_palette_);
824 }
825
828 i++;
829 }
830
831 for (const auto& offset : kSuperDonkeySprites) {
832 int offset_value =
833 std::stoi(offset, nullptr, 16); // convert hex string to int
834 ASSIGN_OR_RETURN(auto decompressed_data,
836 0x1000, 1, temp_rom_.size()));
837 auto converted_sheet = gfx::SnesTo8bppSheet(decompressed_data, 3);
839 gfx::kTilesheetDepth, converted_sheet);
840 if (col_file_) {
841 gfx_sheets_[i].SetPalette(
843 } else {
844 // ROM palette
845 if (game_data()) {
846 auto palette_group = game_data()->palette_groups.get_group(
847 kPaletteGroupAddressesKeys[current_palette_]);
848 z3_rom_palette_ = palette_group->palette(current_palette_index_);
849 gfx_sheets_[i].SetPalette(z3_rom_palette_);
850 }
851 }
852
855 i++;
856 }
857 super_donkey_ = true;
859
860 return absl::OkStatus();
861}
862
868
874
875void GraphicsEditor::SelectSheet(uint16_t sheet_id) {
876 if (sheet_id >= zelda3::kNumGfxSheets) {
877 return;
878 }
879 state_.SelectSheet(sheet_id);
880}
881
882void GraphicsEditor::HighlightTile(uint16_t sheet_id, uint16_t tile_index,
883 const std::string& label,
884 double duration_secs) {
885 if (sheet_id >= zelda3::kNumGfxSheets) {
886 return;
887 }
888 state_.HighlightTile(sheet_id, tile_index, label, duration_secs);
889}
890
891} // namespace editor
892} // namespace yaze
absl::StatusOr< std::vector< uint8_t > > ReadByteVector(uint32_t offset, uint32_t length) const
Definition rom.cc:541
absl::Status LoadFromFile(const std::string &filename, const LoadOptions &options=LoadOptions::Defaults())
Definition rom.cc:227
absl::Status WriteByte(int addr, uint8_t value)
Definition rom.cc:586
auto mutable_data()
Definition rom.h:152
auto data() const
Definition rom.h:151
auto size() const
Definition rom.h:150
absl::Status LoadFromData(const std::vector< uint8_t > &data, const LoadOptions &options=LoadOptions::Defaults())
Definition rom.cc:335
bool is_loaded() const
Definition rom.h:144
static RomSettings & Get()
uint32_t GetAddressOr(const std::string &key, uint32_t default_value) const
UndoManager undo_manager_
Definition editor.h:334
zelda3::GameData * game_data() const
Definition editor.h:320
EditorDependencies dependencies_
Definition editor.h:333
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).
CanvasConfig & GetConfig()
Definition canvas.h:229
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
bool InputHex(const char *label, uint64_t *data)
Definition input.cc:336
void SelectablePalettePipeline(uint64_t &palette_id, bool &refresh_graphics, gfx::SnesPalette &palette)
Definition color.cc:314
void BitmapCanvasPipeline(Canvas &canvas, gfx::Bitmap &bitmap, int width, int height, int tile_size, bool is_loaded, bool scrollbar, int canvas_id)
void TextWithSeparators(const absl::string_view &text)
Definition style.cc:1325
constexpr uint32_t kNumGfxSheets
Definition game_data.h:26
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:181
GfxGroupWorkspaceState * gfx_group_workspace
Definition editor.h:178
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:92