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"
5#include "util/i18n/tr.h"
6
7// C++ standard library headers
8#include <algorithm>
9#include <filesystem>
10#include <set>
11
12// Third-party library headers
13#include "absl/status/status.h"
14#include "absl/status/statusor.h"
15#include "absl/strings/str_cat.h"
16#include "absl/strings/str_format.h"
17#include "absl/strings/str_join.h"
18#include "imgui/imgui.h"
19#include "imgui/misc/cpp/imgui_stdlib.h"
20
21// Project headers
27#include "app/gfx/core/bitmap.h"
35#include "app/gui/core/color.h"
36#include "app/gui/core/icons.h"
37#include "app/gui/core/input.h"
38#include "app/gui/core/style.h"
42#include "app/platform/window.h"
45#include "core/project.h"
46#include "core/rom_settings.h"
47#include "rom/rom.h"
48#include "rom/snes.h"
49#include "util/file_util.h"
50#include "util/log.h"
51#include "util/macro.h"
53
54namespace yaze {
55namespace editor {
56
58using ImGui::Button;
59using ImGui::InputInt;
60using ImGui::InputText;
61using ImGui::SameLine;
62
65 return;
66 auto* window_manager = dependencies_.window_manager;
67
68 // Pixel edits go to this session's sheet store (graphics_sheet_sync.h).
70
71 // Initialize panel components
72 sheet_browser_panel_ = std::make_unique<SheetBrowserPanel>(&state_);
73 sheet_browser_panel_->SetDataSources(
75 [this]() -> const project::YazeProject* { return project(); });
76 sheet_browser_panel_->SetUndoManager(&undo_manager_);
77 sheet_browser_panel_->SetSavePlanner([this]() { return PlanGraphicsSave(); });
78 sheet_browser_panel_->SetLabelCallbacks(
79 [this](uint16_t sheet, const std::string& label) {
80 return EditProject([&](project::YazeProject& target) {
81 core::SetGraphicsSheetLabel(target, sheet, label);
82 });
83 },
84 [this](const std::string& csv_text) -> absl::StatusOr<int> {
85 int written = 0;
86 bool counted = false;
88 const int count = core::ImportSpritesetSheetLabels(target, csv_text);
89 if (!counted) {
90 written = count;
91 counted = true;
92 }
93 }));
94 return written;
95 });
97 std::make_unique<PixelEditorPanel>(&state_, rom_, &undo_manager_);
100 };
101 pixel_editor_panel_->SetHistoryCallbacks([this] { return Undo(); },
102 [this] { return Redo(); });
103 pixel_editor_panel_->SetScratchCallback([this] {
105 dependencies_.window_manager->OpenWindow("graphics.scratch");
106 });
107 pixel_editor_panel_->SetPaletteReorderCallback(
108 [this](const std::vector<size_t>& targets,
109 const PalettePermutation& permutation) {
110 return ReorderSourcePalette(targets, permutation);
111 });
112 pixel_editor_panel_->SetSheetBrowserCallbacks(
113 [this]() {
115 "graphics.sheet_browser_v2");
116 },
117 [this](bool visible) {
118 auto* workspace = dependencies_.window_manager;
119 if (visible)
120 workspace->OpenWindow("graphics.sheet_browser_v2");
121 else
122 workspace->CloseWindow("graphics.sheet_browser_v2");
123 });
124 palette_controls_panel_ = std::make_unique<PaletteControlsPanel>(&state_);
126 std::make_unique<ImageImportView>(&state_, &undo_manager_);
127 link_sprite_panel_ = std::make_unique<LinkSpritePanel>(&state_, rom_);
128 gfx_group_panel_ = std::make_unique<GfxGroupEditor>();
130 gfx_group_panel_->SetRom(rom_);
131 gfx_group_panel_->SetGameData(game_data_);
132 gfx_group_panel_->SetUndoManager(&undo_manager_);
133 gfx_group_panel_->SetMainUsageCallback(main_graphics_usage_);
134 gfx_group_panel_->SetRoomsetUsageCallback(roomset_graphics_usage_);
135 gfx_group_panel_->SetHostSurfaceHint(
136 "Gfx Groups: blockset/roomset/spriteset selection syncs with the "
137 "Overworld "
138 "editor for this ROM session (each surface has its own preview "
139 "canvases).");
140 paletteset_panel_ = std::make_unique<PalettesetEditorPanel>();
141 paletteset_panel_->SetRom(rom_);
142 paletteset_panel_->SetGameData(game_data_);
143
144 polyhedral_panel_ = std::make_unique<PolyhedralEditorPanel>(rom_);
145 polyhedral_panel_->SetRom(rom_);
146 usage_preview_view_ = std::make_unique<UsagePreviewView>(&state_);
147 usage_preview_view_->SetRom(rom_);
148 usage_preview_view_->SetGameData(game_data_);
149 usage_preview_view_->SetLiveRoomProvider(room_usage_provider_);
150 usage_preview_view_->SetLiveOverworldProvider(overworld_usage_provider_);
151 pixel_editor_panel_->SetSourcePreviewCallback(
152 [this]() { usage_preview_view_->DrawSourcePreview(); });
153
154 sheet_browser_panel_->Initialize();
155 pixel_editor_panel_->Initialize();
156 link_sprite_panel_->Initialize();
157
158 if (!scratch_view_)
160 std::make_unique<GraphicsScratchView>(&state_, &undo_manager_);
161 scratch_view_->SetApplyValidator([this](uint16_t sheet) -> absl::Status {
162 if (const auto* current = project()) {
163 ASSIGN_OR_RETURN(auto policy, core::BuildGfxSheetWritePolicy(*current));
164 if (policy.reserved_sheets.count(sheet))
165 return absl::FailedPreconditionError(
166 "The project reserves this graphics sheet");
167 }
168 return absl::OkStatus();
169 });
170 scratch_view_->SetExportValidator([this](const std::string& path) {
171 auto* manager = static_cast<EditorManager*>(dependencies_.custom_data);
172 if (manager && manager->session_coordinator())
173 return manager->session_coordinator()->CheckBackingFileAvailable(path);
174 if (rom_ && !rom_->filename().empty() &&
176 return absl::FailedPreconditionError(
177 "BIN export cannot replace an open ROM");
178 return absl::OkStatus();
179 });
180 window_manager->RegisterWindowContent(
181 std::make_unique<GraphicsScratchWindow>([this] {
182 bool open = true;
183 scratch_view_->Draw(&open);
184 }));
185
186 // Register panels using WindowContent system with callbacks
187 window_manager->RegisterWindowContent(
188 std::make_unique<GraphicsSheetBrowserPanel>([this]() {
190 status_ = sheet_browser_panel_->Update();
191 }
192 }));
193
194 window_manager->RegisterWindowContent(
195 std::make_unique<GraphicsPixelEditorPanel>([this]() {
197 status_ = pixel_editor_panel_->Update();
198 }
199 }));
200
201 window_manager->RegisterWindowContent(
202 std::make_unique<GraphicsPaletteControlsPanel>([this]() {
205 }
206 }));
207
208 window_manager->RegisterWindowContent(
209 std::make_unique<GraphicsUsagePreviewPanel>([this]() {
211 const auto* current_project = project();
212 usage_preview_view_->SetHackName(
213 current_project != nullptr
214 ? std::string(current_project->hack_manifest.hack_name())
215 : std::string());
216 usage_preview_view_->Draw();
217 }
218 }));
219
220 window_manager->RegisterWindowContent(
221 std::make_unique<GraphicsLinkSpritePanel>([this]() {
222 if (link_sprite_panel_) {
223 status_ = link_sprite_panel_->Update();
224 }
225 }));
226
227 window_manager->RegisterWindowContent(
228 std::make_unique<GraphicsGfxGroupPanel>([this]() {
229 if (gfx_group_panel_) {
230 status_ = gfx_group_panel_->Update();
231 }
232 }));
233
234 // Paletteset editor panel (separated from GfxGroupEditor for better UX)
235 window_manager->RegisterWindowContent(
236 std::make_unique<GraphicsPalettesetPanel>([this]() {
237 if (paletteset_panel_) {
238 status_ = paletteset_panel_->Update();
239 }
240 }));
241
242 window_manager->RegisterWindowContent(
243 std::make_unique<GraphicsPrototypeViewerPanel>(
244 [this]() { DrawPrototypeViewer(); }));
245
246 window_manager->RegisterWindowContent(
247 std::make_unique<GraphicsPolyhedralPanel>([this]() {
248 if (polyhedral_panel_) {
249 bool open = true;
250 polyhedral_panel_->Draw(&open);
251 }
252 }));
253
254 window_manager->RegisterWindowContent(
255 std::make_unique<GraphicsImageImportPanel>([this]() {
256 if (image_import_view_) {
257 status_ = image_import_view_->Update();
258 }
259 }));
260}
261
262absl::Status GraphicsEditor::Load() {
263 gfx::ScopedTimer timer("GraphicsEditor::Load");
265 usage_preview_view_->Reset();
268
269 // Initialize all graphics sheets with appropriate palettes from ROM
270 // This ensures textures are created for editing
271 if (rom()->is_loaded()) {
272 auto& sheets = gfx::Arena::Get().gfx_sheets();
273
274 // Apply default palettes to all sheets based on common SNES ROM structure
275 // Sheets 0-112: Use overworld/dungeon palettes
276 // Sheets 113-127: Use sprite palettes
277 // Sheets 128-222: Use auxiliary/menu palettes
278
279 LOG_INFO("GraphicsEditor", "Initializing textures for %d graphics sheets",
281
282 int sheets_queued = 0;
283 for (int i = 0; i < zelda3::kNumGfxSheets; i++) {
284 if (!sheets[i].is_active() || !sheets[i].surface()) {
285 continue; // Skip inactive or surface-less sheets
286 }
287
288 // Palettes are now applied during ROM loading in LoadAllGraphicsData()
289 // Just queue texture creation for sheets that don't have textures yet
290 if (!sheets[i].texture()) {
291 // Fix: Ensure default palettes are applied if missing
292 // This handles the case where sheets are loaded but have no palette assigned
293 if (sheets[i].palette().empty()) {
294 // Default palette assignment logic
295 if (i <= 112) {
296 // Overworld/Dungeon sheets - use Dungeon Main palette (Group 0, Index 0)
297 if (game_data() &&
298 game_data()->palette_groups.dungeon_main.size() > 0) {
299 sheets[i].SetPaletteWithTransparent(
300 game_data()->palette_groups.dungeon_main.palette(0), 0);
301 }
302 } else if (i >= 113 && i <= 127) {
303 // Sprite sheets - use Sprites Aux1 palette (Group 4, Index 0)
304 if (game_data() &&
305 game_data()->palette_groups.sprites_aux1.size() > 0) {
306 sheets[i].SetPaletteWithTransparent(
307 game_data()->palette_groups.sprites_aux1.palette(0), 0);
308 }
309 } else {
310 // Menu/Aux sheets - use HUD palette if available, or fallback
311 if (game_data() && game_data()->palette_groups.hud.size() > 0) {
312 sheets[i].SetPaletteWithTransparent(
313 game_data()->palette_groups.hud.palette(0), 0);
314 }
315 }
316 }
317
320 sheets_queued++;
321 }
322 }
323
324 LOG_INFO("GraphicsEditor", "Queued texture creation for %d graphics sheets",
325 sheets_queued);
326 }
327
328 return absl::OkStatus();
329}
330
332 if (!status_bar)
333 return;
334
335 StatusBarSegmentOptions sheet_opts;
336 sheet_opts.tooltip = absl::StrFormat(
337 "Sheet %d (0x%02X) — use Command Palette to jump between sheets",
339 status_bar->SetCustomSegment(
340 "Sheet", absl::StrFormat("0x%02X", state_.current_sheet_id),
341 std::move(sheet_opts));
342
343 if (!state_.selected_sheets.empty()) {
344 status_bar->SetSelection(static_cast<int>(state_.selected_sheets.size()));
345 }
347 StatusBarSegmentOptions modified_opts;
348 modified_opts.tooltip = absl::StrFormat(
349 "%zu modified sheet%s pending save", state_.modified_sheets.size(),
350 state_.modified_sheets.size() == 1 ? "" : "s");
351 status_bar->SetCustomSegment(
352 "Modified", absl::StrFormat("%zu", state_.modified_sheets.size()),
353 std::move(modified_opts));
354 }
355}
356
358 EditorContextSnapshot snapshot;
359 snapshot.category = "Graphics";
360 snapshot.semantic_owner =
361 absl::StrFormat("graphics.sheet.%02X", state_.current_sheet_id);
362 snapshot.title = absl::StrFormat("Sheet %02X", state_.current_sheet_id);
365 snapshot.subtitle = state_.source_context->label;
367 snapshot.metadata.push_back(
368 {"gfx.source_capture", "Source mapping",
369 "Captured when opened; preview uses live pixels"});
370 }
371 if (const auto* target = state_.SourceTarget()) {
372 snapshot.metadata.push_back(
373 {"gfx.selected_tile", "Source tile", target->label});
374 std::string colors;
375 for (size_t i = 0; i < target->palette.size(); ++i) {
376 if (i != 0)
377 colors += " ";
378 colors += absl::StrFormat("%04X", target->palette[i].snes());
379 }
380 snapshot.metadata.push_back(
381 {"gfx.raw_palette", "Raw index colors (SNES)", colors});
382 }
383 if (state_.game_data != nullptr) {
384 snapshot.metadata.push_back(
385 {"gfx.current_sheet_revision", "Current sheet revision",
386 std::to_string(
388 }
389 return snapshot;
390}
391
393 const std::function<void(project::YazeProject&)>& edit) {
394 auto* editor_manager = static_cast<EditorManager*>(dependencies_.custom_data);
395 project::YazeProject* snapshot = project();
396 if (editor_manager == nullptr || snapshot == nullptr ||
397 !editor_manager->IsCurrentProjectContextOwnedBySession(
399 return absl::FailedPreconditionError(
400 "Sheet labels need the project that owns this ROM to be active");
401 }
402 project::YazeProject* active = editor_manager->GetCurrentProject();
403 if (active == nullptr || !active->project_opened()) {
404 return absl::FailedPreconditionError(
405 "Open a .yaze project to store sheet labels");
406 }
407 edit(*active);
408 if (snapshot != active) {
409 edit(*snapshot);
410 }
411 editor_manager->MarkCurrentProjectDirty();
412 return absl::OkStatus();
413}
414
415absl::StatusOr<GraphicsEditor::PreparedGraphicsSave>
417 if (!rom_ || !rom_->is_loaded()) {
418 return absl::FailedPreconditionError("ROM not loaded");
419 }
420 if (game_data() == nullptr) {
421 return absl::FailedPreconditionError("Game data not loaded");
422 }
423 // The edited sheets live in the shared Arena, which holds the last loaded
424 // ROM's sheets. Never write another open ROM's pixels into this one.
425 if (gfx::Arena::Get().gfx_sheets_owner() != game_data()) {
426 return absl::FailedPreconditionError(
427 "Graphics sheets in memory belong to another open ROM. Switch back to "
428 "the ROM they were edited in, or reopen this ROM, before saving "
429 "graphics");
430 }
431
432 PreparedGraphicsSave prepared;
433 if (const auto* project = this->project(); project != nullptr) {
435 }
436
437 auto& sheets = gfx::Arena::Get().gfx_sheets();
438 for (uint16_t sheet_id : state_.modified_sheets) {
439 if (sheet_id >= zelda3::kNumGfxSheets) {
440 prepared.refusals[sheet_id] = "out of range";
441 } else if (prepared.policy.reserved_sheets.count(sheet_id) != 0) {
442 prepared.refusals[sheet_id] = "reserved by the project";
443 } else if (zelda3::GetGfxSheetStorageKind(sheet_id) ==
445 prepared.refusals[sheet_id] = "2bpp, read-only";
446 } else if (!sheets[sheet_id].is_active()) {
447 prepared.refusals[sheet_id] = "not loaded";
448 } else if (std::any_of(sheets[sheet_id].vector().begin(),
449 sheets[sheet_id].vector().end(),
450 [](uint8_t index) { return index > 7; })) {
451 // 3bpp sheets store colors 0-7; packing masks higher indices silently.
452 prepared.refusals[sheet_id] = "uses colors above index 7";
453 } else {
454 prepared.sheet_ids.push_back(sheet_id);
455 }
456 }
457
458 const auto version_constants =
459 zelda3::kVersionConstantsMap.at(game_data()->version);
462 version_constants.kOverworldGfxPtr1);
465 version_constants.kOverworldGfxPtr2);
468 version_constants.kOverworldGfxPtr3);
469 return prepared;
470}
471
473 if (!scratch_view_)
474 return absl::OkStatus();
475 scratch_view_->FinishGesture();
476 return scratch_view_->Flush();
477}
478
479absl::Status GraphicsEditor::Save() {
482 return absl::FailedPreconditionError(
483 "Commit or cancel the floating paste in Graphics before saving "
484 "(Enter to commit, Esc to cancel)");
485 if (pixel_editor_panel_ && pixel_editor_panel_->HasActiveStroke())
486 return absl::FailedPreconditionError(
487 "Finish the pixel stroke before saving");
488 if (!rom_ || !rom_->is_loaded()) {
489 return absl::FailedPreconditionError("ROM not loaded");
490 }
491
492 // Only save sheets that have been modified
493 if (!state_.HasUnsavedChanges()) {
494 LOG_INFO("GraphicsEditor", "No modified sheets to save");
495 return absl::OkStatus();
496 }
498
499 // Refuse the whole batch before writing when any sheet cannot be saved, so
500 // a save never lands only part of the user's edits.
501 if (!prepared.refusals.empty()) {
502 std::vector<std::string> refused;
503 for (const auto& [sheet_id, reason] : prepared.refusals) {
504 refused.push_back(absl::StrFormat("0x%02X (%s)", sheet_id, reason));
505 }
506 return absl::FailedPreconditionError(absl::StrFormat(
507 "Graphics save refused for sheet(s) %s. Discard those edits to save "
508 "the rest.",
509 absl::StrJoin(refused, ", ")));
510 }
511
512 LOG_INFO("GraphicsEditor", "Saving %zu modified graphics sheets",
513 prepared.sheet_ids.size());
514 auto& sheets = gfx::Arena::Get().gfx_sheets();
515
516 // Each WriteGfxSheet call restores its own bytes on failure; this snapshot
517 // also undoes sheets written earlier in the batch.
518 const std::vector<uint8_t> rom_snapshot = rom_->vector();
519 const bool rom_was_dirty = rom_->dirty();
520 auto restore = [&]() {
521 rom_->mutable_vector() = rom_snapshot;
522 rom_->set_dirty(rom_was_dirty);
523 };
524 std::set<uint16_t> saved_sheets;
525 for (uint16_t sheet_id : prepared.sheet_ids) {
526 const auto snes_data =
527 gfx::IndexedToSnesSheet(sheets[sheet_id].vector(), /*bpp=*/3);
528 auto result = zelda3::WriteGfxSheet(*rom_, sheet_id, snes_data,
529 prepared.policy, prepared.tables);
530 if (!result.ok()) {
531 restore();
532 return absl::Status(result.status().code(),
533 absl::StrFormat("Graphics sheet 0x%02X not saved: %s",
534 sheet_id, result.status().message()));
535 }
536 LOG_INFO("GraphicsEditor", "Saved sheet %02X (%zu bytes, %s) at 0x%06X",
537 sheet_id, result->new_stored_size,
538 result->placement == zelda3::GfxSheetPlacement::kRelocated
539 ? "relocated"
540 : "in place",
541 result->new_pc);
542 saved_sheets.insert(sheet_id);
543 }
544
545 // Every written sheet must decode to the edited pixels, with the game's
546 // decoder and with the editor's loader, before the ROM goes to disk (the
547 // caller writes the file only when this returns OK).
548 for (uint16_t sheet_id : prepared.sheet_ids) {
549 const absl::Status verified = verify_written_sheet_(
550 *rom_, sheet_id, sheets[sheet_id].vector(), prepared.tables);
551 if (!verified.ok()) {
552 restore();
553 return absl::Status(
554 verified.code(),
555 absl::StrFormat("Graphics save stopped; nothing was written: %s",
556 verified.message()));
557 }
558 }
559
560 // Clear modified tracking after successful save
561 state_.ClearModifiedSheets(saved_sheets);
562 return absl::OkStatus();
563}
564
565absl::StatusOr<std::vector<GraphicsSavePlanEntry>>
568 std::vector<GraphicsSavePlanEntry> entries;
569 for (const auto& [sheet_id, reason] : prepared.refusals) {
571 entry.sheet_id = sheet_id;
572 entry.refusal = reason;
573 entries.push_back(std::move(entry));
574 }
575 // Run the real writer on a scratch copy; the ROM itself is untouched.
576 Rom scratch;
578 auto& sheets = gfx::Arena::Get().gfx_sheets();
579 for (uint16_t sheet_id : prepared.sheet_ids) {
581 entry.sheet_id = sheet_id;
582 const auto& pixels = sheets[sheet_id].vector();
583 auto result = zelda3::WriteGfxSheet(
584 scratch, sheet_id, gfx::IndexedToSnesSheet(pixels, /*bpp=*/3),
585 prepared.policy, prepared.tables);
586 if (!result.ok()) {
587 entry.refusal = std::string(result.status().message());
588 } else {
589 entry.placement = result->placement;
590 entry.old_pc = result->old_pc;
591 entry.new_pc = result->new_pc;
592 entry.old_stored_size = result->old_stored_size;
593 entry.new_stored_size = result->new_stored_size;
594 const absl::Status verified =
595 verify_written_sheet_(scratch, sheet_id, pixels, prepared.tables);
596 if (!verified.ok()) {
597 entry.refusal = std::string(verified.message());
598 }
599 }
600 entries.push_back(std::move(entry));
601 }
602 std::sort(entries.begin(), entries.end(), [](const auto& a, const auto& b) {
603 return a.sheet_id < b.sheet_id;
604 });
605 return entries;
606}
607
608absl::Status GraphicsEditor::ImportScratchBin(const std::string& path, int bpp,
609 size_t offset,
610 const gfx::SnesPalette& palette) {
612 return absl::FailedPreconditionError(
613 "Initialize Graphics before importing scratch assets");
615 dependencies_.window_manager->OpenWindow("graphics.scratch");
616 return scratch_view_->ImportBin(path, bpp, offset, palette);
617}
618
620 ScratchPreviewCallback callback) {
621 if (scratch_view_)
622 scratch_view_->SetAssociatedPreview(std::move(label), std::move(callback));
623}
624
626 // Panels are now drawn via WorkspaceWindowManager::DrawAllVisiblePanels()
627 // This Update() only handles editor-level state and keyboard shortcuts
628
629 // Show store changes (undo, redo, imports) in the Arena's display copies.
630 if (scratch_view_)
631 scratch_view_->FinishGesture();
634
635 // Keyboard shortcuts (tools, zoom, grid, sheet navigation) are registered
636 // in shortcut_configurator.cc, scoped to EditorType::kGraphics.
637
639 return absl::OkStatus();
640}
641
642absl::Status GraphicsEditor::Undo() {
644 pixel_editor_panel_->FinishReleasedGesture();
645 if (pixel_editor_panel_ && pixel_editor_panel_->HasActiveStroke())
646 return absl::FailedPreconditionError("Finish the pixel stroke before Undo");
647 if (scratch_view_)
648 scratch_view_->FinishGesture();
649 if (scratch_view_ && scratch_view_->HasActiveStroke())
650 return absl::FailedPreconditionError(
651 "Finish the scratch stroke before Undo");
653 return scratch_view_ ? scratch_view_->Flush() : absl::OkStatus();
654}
655
656absl::Status GraphicsEditor::Redo() {
658 pixel_editor_panel_->FinishReleasedGesture();
659 if (pixel_editor_panel_ && pixel_editor_panel_->HasActiveStroke())
660 return absl::FailedPreconditionError("Finish the pixel stroke before Redo");
661 if (scratch_view_)
662 scratch_view_->FinishGesture();
663 if (scratch_view_ && scratch_view_->HasActiveStroke())
664 return absl::FailedPreconditionError(
665 "Finish the scratch stroke before Redo");
667 return scratch_view_ ? scratch_view_->Flush() : absl::OkStatus();
668}
669
671 if (!rom_ || !rom_->is_loaded()) {
672 ImGui::TextWrapped(tr(
673 "No ROM loaded — CGX/SCR/COL/BIN and clipboard tools work without one. "
674 "Load a ROM when you want vanilla palette presets or to save graphics "
675 "back into a cartridge image."));
676 ImGui::Spacing();
677 }
678 if (!prototype_import_feedback_.empty()) {
679 ImGui::TextColored(ImVec4(1.0f, 0.35f, 0.35f, 1.0f), "%s",
681 if (ImGui::SmallButton(tr("Dismiss##prototype_import_feedback"))) {
683 }
684 ImGui::Separator();
685 }
686
688 ImGui::Begin("Memory Editor", &open_memory_editor_);
690 ImGui::End();
691 }
692
693 constexpr ImGuiTableFlags kGfxEditFlags = ImGuiTableFlags_Reorderable |
694 ImGuiTableFlags_Resizable |
695 ImGuiTableFlags_SizingStretchSame;
696
697 BEGIN_TABLE("#gfxEditTable", 4, kGfxEditFlags)
698 SETUP_COLUMN("File Import (BIN, CGX, ROM)")
699 SETUP_COLUMN("Palette (COL)")
700 ImGui::TableSetupColumn("Tilemaps and Objects (SCR, PNL, OBJ)",
701 ImGuiTableColumnFlags_WidthFixed);
702 SETUP_COLUMN("Graphics Preview")
704 NEXT_COLUMN() {
709 }
710
711 NEXT_COLUMN() {
713 }
714
718 scr_loaded_, false, 0);
720
722 if (super_donkey_) {
723 // Super Donkey prototype graphics
724 for (size_t i = 0; i < num_sheets_to_load_ && i < gfx_sheets_.size(); i++) {
725 if (gfx_sheets_[i].is_active() && gfx_sheets_[i].texture()) {
726 ImGui::Image((ImTextureID)(intptr_t)gfx_sheets_[i].texture(),
727 ImVec2(128, 32));
728 if ((i + 1) % 4 != 0) {
729 ImGui::SameLine();
730 }
731 }
732 }
733 } else if (cgx_loaded_ && col_file_) {
734 // Load the CGX graphics
737 cgx_loaded_, true, 5);
738 } else {
739 // Load the BIN/Clipboard Graphics
742 gfx_loaded_, true, 2);
743 }
744 END_TABLE()
745}
746
747// =============================================================================
748// Prototype Viewer Import Methods
749// =============================================================================
750
752 gui::TextWithSeparators("Cgx Import");
753 InputInt(tr("BPP"), &current_bpp_);
754
755 InputText("##CGXFile", &cgx_file_name_);
756 SameLine();
757
758 if (ImGui::Button(tr("Open CGX"))) {
760 cgx_file_name_ = filename;
761 cgx_file_path_ = std::filesystem::absolute(filename).string();
762 is_open_ = true;
763 cgx_loaded_ = true;
764 }
765
766 if (ImGui::Button(tr("Copy CGX Path"))) {
767 ImGui::SetClipboardText(cgx_file_path_.c_str());
768 }
769
770 if (ImGui::Button(tr("Load CGX Data"))) {
773 if (!status_.ok()) {
774 prototype_import_feedback_ = absl::StrCat("[CGX] ", status_.message());
775 return absl::OkStatus();
776 }
778
779 cgx_bitmap_.Create(0x80, 0x200, 8, decoded_cgx_);
780 if (col_file_) {
784 }
785 }
786
787 return absl::OkStatus();
788}
789
791 InputText("##ScrFile", &scr_file_name_);
792
793 if (ImGui::Button(tr("Open SCR"))) {
795 scr_file_name_ = filename;
796 scr_file_path_ = std::filesystem::absolute(filename).string();
797 is_open_ = true;
798 scr_loaded_ = true;
799 }
800
801 InputInt(tr("SCR Mod"), &scr_mod_value_);
802
803 if (ImGui::Button(tr("Load Scr Data"))) {
805 if (!status_.ok()) {
806 prototype_import_feedback_ = absl::StrCat("[SCR] ", status_.message());
807 return absl::OkStatus();
808 }
809
810 decoded_scr_data_.resize(0x100 * 0x100);
813 if (!status_.ok()) {
815 absl::StrCat("[SCR draw] ", status_.message());
816 return absl::OkStatus();
817 }
819
820 scr_bitmap_.Create(0x100, 0x100, 8, decoded_scr_data_);
821 if (scr_loaded_) {
825 }
826 }
827
828 return absl::OkStatus();
829}
830
832 gui::TextWithSeparators("COL Import");
833 InputText("##ColFile", &col_file_name_);
834 SameLine();
835
836 if (ImGui::Button(tr("Open COL"))) {
838 col_file_name_ = filename;
839 col_file_path_ = std::filesystem::absolute(filename).string();
841 auto col_data_ = gfx::GetColFileData(temp_rom_.mutable_data());
842 if (col_file_palette_group_.size() != 0) {
844 }
845 auto col_file_palette_group_status =
847 if (col_file_palette_group_status.ok()) {
848 col_file_palette_group_ = col_file_palette_group_status.value();
849 }
851
852 // gigaleak dev format based code
854 col_file_ = true;
855 is_open_ = true;
856 }
857 HOVER_HINT(".COL, .BAK");
858
859 if (ImGui::Button(tr("Copy Col Path"))) {
860 ImGui::SetClipboardText(col_file_path_.c_str());
861 }
862
863 if (rom()->is_loaded()) {
864 gui::TextWithSeparators("ROM Palette");
865 gui::InputHex("Palette Index", &current_palette_index_);
866 ImGui::Combo(tr("Palette"), &current_palette_, kPaletteGroupAddressesKeys,
867 IM_ARRAYSIZE(kPaletteGroupAddressesKeys));
868 }
869
870 if (col_file_palette_.size() != 0) {
873 }
874
875 return absl::OkStatus();
876}
877
879 gui::TextWithSeparators("OBJ Import");
880
881 InputText("##ObjFile", &obj_file_path_);
882 SameLine();
883
884 if (ImGui::Button(tr("Open OBJ"))) {
886 obj_file_path_ = std::filesystem::absolute(filename).string();
888 is_open_ = true;
889 obj_loaded_ = true;
890 }
891 HOVER_HINT(".OBJ, .BAK");
892
893 return absl::OkStatus();
894}
895
897 gui::TextWithSeparators("Tilemap Import");
898
899 InputText("##TMapFile", &tilemap_file_path_);
900 SameLine();
901
902 if (ImGui::Button(tr("Open Tilemap"))) {
904 tilemap_file_path_ = std::filesystem::absolute(filename).string();
907
908 // Extract the high and low bytes from the file.
909 auto decomp_sheet = gfx::lc_lz2::DecompressV2(tilemap_rom_.data(), 0, 0x800,
912 tilemap_loaded_ = true;
913 is_open_ = true;
914 }
915 HOVER_HINT(".DAT, .BIN, .HEX");
916
917 return absl::OkStatus();
918}
919
921 gui::TextWithSeparators("BIN Import");
922
923 InputText("##ROMFile", &file_path_);
924 SameLine();
925
926 if (ImGui::Button(tr("Open BIN"))) {
928 file_path_ = filename;
930 is_open_ = true;
931 }
932 HOVER_HINT(".BIN, .HEX");
933
934 if (Button(tr("Copy File Path"))) {
935 ImGui::SetClipboardText(file_path_.c_str());
936 }
937
938 gui::InputHex("BIN Offset", &current_offset_);
939 gui::InputHex("BIN Size", &bin_size_);
940
941 if (Button(tr("Decompress BIN"))) {
942 if (file_path_.empty()) {
943 return absl::InvalidArgumentError(
944 "Please select a file before decompressing.");
945 }
947 }
948
949 return absl::OkStatus();
950}
951
953 gui::TextWithSeparators("Clipboard Import");
954 if (Button(tr("Paste From Clipboard"))) {
955 const char* text = ImGui::GetClipboardText();
956 if (text) {
957 const auto clipboard_data =
958 std::vector<uint8_t>(text, text + strlen(text));
959 ImGui::MemFree((void*)text);
960 status_ = temp_rom_.LoadFromData(clipboard_data);
961 is_open_ = true;
962 open_memory_editor_ = true;
963 }
964 }
967 gui::InputHex("Num Sheets", &num_sheets_to_load_);
968
969 if (Button(tr("Decompress Clipboard Data"))) {
970 if (temp_rom_.is_loaded()) {
971 status_ = DecompressImportData(0x40000);
972 } else {
973 status_ = absl::InvalidArgumentError(
974 "Please paste data into the clipboard before "
975 "decompressing.");
976 }
977 }
978
979 return absl::OkStatus();
980}
981
983 gui::TextWithSeparators("Experimental");
984 if (Button(tr("Decompress Super Donkey Full"))) {
985 if (file_path_.empty()) {
986 return absl::InvalidArgumentError(
987 "Please select `super_donkey_1.bin` before "
988 "importing.");
989 }
991 }
992 ImGui::SetItemTooltip(
993 tr("Requires `super_donkey_1.bin` to be imported under the "
994 "BIN import section."));
995 return absl::OkStatus();
996}
997
999 std::string title = "Memory Editor";
1000 if (is_open_) {
1001 static yaze::gui::MemoryEditorWidget mem_edit;
1002 mem_edit.DrawWindow(title.c_str(), temp_rom_.mutable_data(),
1003 temp_rom_.size());
1004 }
1005 return absl::OkStatus();
1006}
1007
1011 size, 1, temp_rom_.size()));
1012
1013 auto converted_sheet = gfx::SnesTo8bppSheet(import_data_, 3);
1015 converted_sheet);
1016
1017 if (rom()->is_loaded() && game_data()) {
1018 auto palette_group = game_data()->palette_groups.overworld_animated;
1019 z3_rom_palette_ = palette_group[current_palette_];
1020 if (col_file_) {
1022 } else {
1024 }
1025 }
1026
1028 &bin_bitmap_);
1029 gfx_loaded_ = true;
1030
1031 return absl::OkStatus();
1032}
1033
1035 int i = 0;
1036 for (const auto& offset : kSuperDonkeyTiles) {
1037 int offset_value =
1038 std::stoi(offset, nullptr, 16); // convert hex string to int
1039 ASSIGN_OR_RETURN(auto decompressed_data,
1041 0x1000, 1, temp_rom_.size()));
1042 auto converted_sheet = gfx::SnesTo8bppSheet(decompressed_data, 3);
1044 gfx::kTilesheetDepth, converted_sheet);
1045 if (col_file_) {
1046 gfx_sheets_[i].SetPalette(
1048 } else {
1049 // ROM palette
1050 if (!game_data()) {
1051 return absl::FailedPreconditionError("GameData not available");
1052 }
1053 auto palette_group = game_data()->palette_groups.get_group(
1054 kPaletteGroupAddressesKeys[current_palette_]);
1055 z3_rom_palette_ = palette_group->palette(current_palette_index_);
1056 gfx_sheets_[i].SetPalette(z3_rom_palette_);
1057 }
1058
1061 i++;
1062 }
1063
1064 for (const auto& offset : kSuperDonkeySprites) {
1065 int offset_value =
1066 std::stoi(offset, nullptr, 16); // convert hex string to int
1067 ASSIGN_OR_RETURN(auto decompressed_data,
1069 0x1000, 1, temp_rom_.size()));
1070 auto converted_sheet = gfx::SnesTo8bppSheet(decompressed_data, 3);
1072 gfx::kTilesheetDepth, converted_sheet);
1073 if (col_file_) {
1074 gfx_sheets_[i].SetPalette(
1076 } else {
1077 // ROM palette
1078 if (game_data()) {
1079 auto palette_group = game_data()->palette_groups.get_group(
1080 kPaletteGroupAddressesKeys[current_palette_]);
1081 z3_rom_palette_ = palette_group->palette(current_palette_index_);
1082 gfx_sheets_[i].SetPalette(z3_rom_palette_);
1083 }
1084 }
1085
1088 i++;
1089 }
1090 super_donkey_ = true;
1092
1093 return absl::OkStatus();
1094}
1095
1101
1107
1111
1113 state_.ZoomIn();
1114}
1115
1119
1123
1124absl::Status GraphicsEditor::Cut() {
1125 if (!pixel_editor_panel_) {
1126 return absl::FailedPreconditionError("Pixel editor is not open");
1127 }
1128 pixel_editor_panel_->CutToSystemClipboard();
1129 return absl::OkStatus();
1130}
1131
1132absl::Status GraphicsEditor::Copy() {
1133 if (!pixel_editor_panel_) {
1134 return absl::FailedPreconditionError("Pixel editor is not open");
1135 }
1136 pixel_editor_panel_->CopyToSystemClipboard();
1137 return absl::OkStatus();
1138}
1139
1141 if (!pixel_editor_panel_) {
1142 return absl::FailedPreconditionError("Pixel editor is not open");
1143 }
1144 pixel_editor_panel_->PasteFromSystemClipboard();
1145 return absl::OkStatus();
1146}
1147
1152
1153void GraphicsEditor::SelectSheet(uint16_t sheet_id) {
1154 if (sheet_id >= zelda3::kNumGfxSheets) {
1155 return;
1156 }
1157 state_.SelectSheet(sheet_id);
1158 state_.requested_sheet_tab = sheet_id;
1159}
1160
1162 if (context.owner == nullptr || context.owner != state_.game_data) {
1163 return absl::FailedPreconditionError(
1164 "Graphics context belongs to a different ROM session");
1165 }
1166 if (context.targets.empty()) {
1167 return absl::InvalidArgumentError("Graphics context has no editable tiles");
1168 }
1169 for (const auto& target : context.targets) {
1170 if (target.sheet_id >= zelda3::kNumGfxSheets || target.tile_index >= 64 ||
1171 target.palette.size() != 8 ||
1172 zelda3::GetGfxSheetStorageKind(target.sheet_id) ==
1174 !state_.game_data->sheet_store.HasSheet(target.sheet_id)) {
1175 return absl::InvalidArgumentError(
1176 "Graphics context needs loaded 3bpp sheets and eight indexed colors");
1177 }
1178 }
1180 return absl::FailedPreconditionError(
1181 "Commit or cancel the floating paste before opening another source");
1182 }
1185 state_.source_context = std::move(context);
1189 pixel_editor_panel_->ShowSourcePreview();
1191 dependencies_.window_manager->OpenWindow("graphics.pixel_editor");
1192 // Source preview is embedded. Deliberately opened comparison windows keep
1193 // their visibility, pins and layout; entering Graphics adds no pop-out.
1194 }
1195 return absl::OkStatus();
1196}
1197
1199 const std::vector<size_t>& targets,
1200 const std::array<uint8_t, 8>& permutation) {
1201 if (pixel_editor_panel_ && pixel_editor_panel_->HasActiveStroke())
1202 return absl::FailedPreconditionError("Finish the pixel stroke first");
1204 permutation));
1206 return absl::OkStatus();
1207}
1208
1209void GraphicsEditor::HighlightTile(uint16_t sheet_id, uint16_t tile_index,
1210 const std::string& label,
1211 double duration_secs) {
1212 if (sheet_id >= zelda3::kNumGfxSheets) {
1213 return;
1214 }
1215 state_.HighlightTile(sheet_id, tile_index, label, duration_secs);
1216}
1217
1218} // namespace editor
1219} // namespace yaze
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:52
absl::Status LoadFromFile(const std::string &filename, const LoadOptions &options=LoadOptions::Defaults())
Definition rom.cc:632
auto filename() const
Definition rom.h:219
void set_dirty(bool dirty)
Definition rom.h:201
auto mutable_data()
Definition rom.h:214
const auto & vector() const
Definition rom.h:217
auto data() const
Definition rom.h:213
auto size() const
Definition rom.h:212
bool dirty() const
Definition rom.h:200
auto & mutable_vector()
Definition rom.h:218
absl::Status LoadFromData(const std::vector< uint8_t > &data, const LoadOptions &options=LoadOptions::Defaults())
Definition rom.cc:781
bool is_loaded() const
Definition rom.h:199
const std::string & hack_name() const
static RomSettings & Get()
uint32_t GetAddressOr(const std::string &key, uint32_t default_value) const
The EditorManager controls the main editor window and manages the various editor classes.
SessionCoordinator * session_coordinator()
EditorContext context() const
Definition editor.h:376
UndoManager undo_manager_
Definition editor.h:383
project::YazeProject * project() const
Definition editor.h:370
zelda3::GameData * game_data() const
Definition editor.h:369
EditorDependencies dependencies_
Definition editor.h:382
std::optional< GraphicsEditContext > source_context
std::optional< uint16_t > requested_sheet_tab
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.
std::function< void()> on_before_sheet_selected
bool HasUnsavedChanges() const
Check if any sheets have unsaved changes.
void SelectSheet(uint16_t sheet_id)
Select a sheet for editing.
const GraphicsEditTarget * SourceTarget() const
void SetTool(PixelTool tool)
Set the current editing tool.
void ClearModifiedSheets()
Clear modification tracking (after save)
usage_preview::LiveRoomProvider room_usage_provider_
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)
absl::Status ReorderSourcePalette(const std::vector< size_t > &targets, const std::array< uint8_t, 8 > &permutation)
GfxGroupEditor::RoomsetUsageCallback roomset_graphics_usage_
void SetPixelTool(PixelTool tool)
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_
EditorContextSnapshot BuildContextSnapshot() const override
absl::Status Copy() override
std::unique_ptr< GraphicsScratchView > scratch_view_
GfxGroupEditor::MainUsageCallback main_graphics_usage_
absl::Status EditProject(const std::function< void(project::YazeProject &)> &edit)
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::function< absl::Status(const Rom &, uint16_t, const std::vector< uint8_t > &, const zelda3::GfxSheetPointerTables &) verify_written_sheet_)
absl::Status Cut() override
std::vector< uint8_t > import_data_
absl::Status Paste() override
absl::StatusOr< std::vector< GraphicsSavePlanEntry > > PlanGraphicsSave()
Graphics save preflight: every dirty sheet written to a scratch copy of the ROM (placement,...
std::vector< SDL_Color > decoded_col_
absl::Status Undo() override
absl::StatusOr< PreparedGraphicsSave > PrepareGraphicsSave()
absl::Status Update() override
absl::Status OpenInContext(GraphicsEditContext context)
absl::Status ImportScratchBin(const std::string &path, int bpp, size_t offset, const gfx::SnesPalette &palette={})
usage_preview::LiveOverworldProvider overworld_usage_provider_
std::vector< uint8_t > extra_cgx_data_
void SetScratchPreview(std::string label, ScratchPreviewCallback callback)
std::array< gfx::Bitmap, zelda3::kNumGfxSheets > gfx_sheets_
std::unique_ptr< ImageImportView > image_import_view_
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< UsagePreviewView > usage_preview_view_
std::unique_ptr< SheetBrowserPanel > sheet_browser_panel_
absl::Status CheckBackingFileAvailable(const std::string &filepath, std::optional< size_t > excluded_session_id=std::nullopt) const
static bool PathsReferToSameBackingFile(const std::string &lhs, const std::string &rhs)
A session-aware status bar displayed at the bottom of the application.
Definition status_bar.h:57
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.
bool IsWindowOpen(size_t session_id, const std::string &base_window_id) const
bool OpenWindow(size_t session_id, const std::string &base_window_id)
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
Definition arena.cc:39
GraphicsSheets & gfx_sheets()
Definition arena.h:178
static Arena & Get()
Definition arena.cc:24
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:70
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:198
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap using SNES palette format.
Definition bitmap.cc:452
RAII timer for automatic timing management.
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
CanvasConfig & GetConfig()
Definition canvas.h:243
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath. Uses global feature flag to...
bool HasSheet(uint16_t sheet) const
True when the buffer holds all 4096 bytes of sheet.
uint64_t Revision(uint16_t sheet) const
Locates graphics sheets in the ROM and writes edited sheets back.
#define LOG_INFO(category, format,...)
Definition log.h:136
#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[]
absl::StatusOr< zelda3::GfxSheetWritePolicy > BuildGfxSheetWritePolicy(const HackManifest &manifest)
void SetGraphicsSheetLabel(project::YazeProject &project, uint16_t sheet, const std::string &label)
int ImportSpritesetSheetLabels(project::YazeProject &project, const std::string &csv_text, bool overwrite)
int SyncArenaFromStore(GraphicsEditorState &state)
std::array< uint8_t, 8 > PalettePermutation
const std::string kSuperDonkeySprites[]
void AppendResolvedGraphicsContext(EditorContextSnapshot &snapshot, const zelda3::ResolvedGraphicsContext &context)
void AttachSheetStore(GraphicsEditorState &state, zelda3::GameData *game_data)
const std::string kSuperDonkeyTiles[]
void RefreshGraphicsPreviewPalettes(GraphicsEditorState &state)
PixelTool
Pixel editing tool types for the graphics editor.
std::function< absl::StatusOr< usage_preview::PreviewImage >( const GraphicsScratchAsset &)> ScratchPreviewCallback
absl::Status ApplyPalettePixelPermutation(GraphicsEditorState &state, UndoManager &history, const std::vector< size_t > &targets, const PalettePermutation &permutation)
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:133
std::vector< uint8_t > IndexedToSnesSheet(std::span< const uint8_t > sheet, int bpp, int num_sheets)
Definition snes_tile.cc:204
absl::StatusOr< PaletteGroup > CreatePaletteGroupFromColFile(std::vector< SnesColor > &palette_rows)
std::vector< SnesColor > GetColFileData(uint8_t *data)
Definition snes_color.cc:74
bool InputHex(const char *label, uint64_t *data)
Definition input.cc:438
void SelectablePalettePipeline(uint64_t &palette_id, bool &refresh_graphics, gfx::SnesPalette &palette)
Definition color.cc:338
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:828
absl::StatusOr< GfxSheetWriteResult > WriteGfxSheet(Rom &rom, uint16_t sheet_id, const std::vector< uint8_t > &snes_3bpp, const GfxSheetWritePolicy &policy, const GfxSheetPointerTables &tables)
Writes one edited 3bpp sheet to the ROM buffer.
constexpr uint32_t kNumGfxSheets
Definition game_data.h:28
GfxSheetStorageKind GetGfxSheetStorageKind(uint16_t sheet_id)
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22
std::vector< EditorContextValue > metadata
WorkspaceWindowManager * window_manager
Definition editor.h:182
GfxGroupWorkspaceState * gfx_group_workspace
Definition editor.h:179
zelda3::GfxSheetPlacement placement
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)
Modern project structure with comprehensive settings consolidation.
Definition project.h:290
core::HackManifest hack_manifest
Definition project.h:376
gfx::PaletteGroupMap palette_groups
Definition game_data.h:95
zelda3_version version
Definition game_data.h:84