9#include "absl/strings/str_cat.h"
10#include "absl/strings/str_format.h"
11#include "absl/strings/str_split.h"
12#include "absl/strings/strip.h"
20#include "imgui/imgui.h"
36 token = std::string(absl::StripAsciiWhitespace(token));
38 return absl::InvalidArgumentError(
"Empty color token");
41 if (token[0] ==
'$') {
43 }
else if (token.size() > 2 &&
44 (token.rfind(
"0x", 0) == 0 || token.rfind(
"0X", 0) == 0)) {
49 return absl::InvalidArgumentError(
"Color token is missing hex digits");
52 for (
char ch : token) {
53 if (!std::isxdigit(
static_cast<unsigned char>(ch))) {
54 return absl::InvalidArgumentError(
55 absl::StrCat(
"Invalid hex digit in color token: ", token));
59 if (token.size() > 4) {
60 return absl::InvalidArgumentError(
61 absl::StrCat(
"Color token is too long for SNES color: ", token));
66 value =
static_cast<uint32_t
>(std::stoul(token,
nullptr, 16));
67 }
catch (
const std::exception&) {
68 return absl::InvalidArgumentError(
69 absl::StrCat(
"Failed to parse color token: ", token));
73 return absl::InvalidArgumentError(
74 absl::StrCat(
"SNES color out of range (0x0000-0x7FFF): ", token));
77 return static_cast<uint16_t
>(value);
81 const std::string& clipboard) {
82 std::vector<uint16_t> colors;
83 for (
const auto& raw_token : absl::StrSplit(
84 clipboard, absl::ByAnyChar(
", \n\r\t"), absl::SkipEmpty())) {
85 const std::string token =
86 std::string(absl::StripAsciiWhitespace(raw_token));
92 return color_or.status();
94 colors.push_back(*color_or);
98 return absl::InvalidArgumentError(
"No colors found in clipboard data");
104#if defined(YAZE_WITH_JSON)
105absl::StatusOr<uint16_t> ParseSnesColorJson(
const yaze::Json& value) {
107 return ParseSnesHexToken(value.
get<std::string>());
109 if (value.is_number_integer()) {
110 int parsed = value.
get<
int>();
111 if (parsed < 0 || parsed > 0x7FFF) {
112 return absl::InvalidArgumentError(
113 absl::StrFormat(
"SNES color out of range: %d", parsed));
115 return static_cast<uint16_t
>(parsed);
117 if (value.is_number_unsigned()) {
118 uint32_t parsed = value.
get<uint32_t>();
119 if (parsed > 0x7FFF) {
120 return absl::InvalidArgumentError(
121 absl::StrFormat(
"SNES color out of range: %u", parsed));
123 return static_cast<uint16_t
>(parsed);
125 return absl::InvalidArgumentError(
126 "Invalid color value type (expected string or number)");
133 const std::string& display_name,
Rom* rom,
135 : group_name_(group_name),
136 display_name_(display_name),
138 game_data_(game_data) {
147 tr(
"Palette controls are unavailable for an inactive ROM session."));
164 if (ImGui::BeginTable(
165 "##PalettePanelLayout", 2,
166 ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV)) {
167 ImGui::TableSetupColumn(
"Grid", ImGuiTableColumnFlags_WidthStretch, 0.6f);
168 ImGui::TableSetupColumn(
"Editor", ImGuiTableColumnFlags_WidthStretch, 0.4f);
170 ImGui::TableNextRow();
171 ImGui::TableNextColumn();
178 ImGui::TableNextColumn();
188 ImGui::TextDisabled(tr(
"Select a color to edit"));
208 ImGui::BeginDisabled(!has_changes);
219 ImGui::EndDisabled();
221 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
222 ImGui::SetTooltip(tr(
"No palette changes to save"));
228 ImGui::BeginDisabled(!has_changes);
232 ImGui::EndDisabled();
234 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
235 ImGui::SetTooltip(tr(
"No palette changes to discard"));
242 size_t modified_count = 0;
245 for (
int p = 0; p < group->size(); p++) {
251 ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.0f, 1.0f), tr(
"%s %zu modified"),
256 ImGui::Dummy(ImVec2(20, 0));
261 ImGui::BeginDisabled(!can_undo);
265 ImGui::EndDisabled();
266 if (!can_undo && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
267 ImGui::SetTooltip(tr(
"Nothing to undo"));
272 ImGui::BeginDisabled(!can_redo);
276 ImGui::EndDisabled();
277 if (!can_redo && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
278 ImGui::SetTooltip(tr(
"Nothing to redo"));
282 ImGui::Dummy(ImVec2(20, 0));
297 ImGui::OpenPopup(
"BatchOperations");
309 int num_palettes = palette_group->size();
311 ImGui::Text(tr(
"Palette:"));
315 if (ImGui::BeginCombo(
318 const float item_height = ImGui::GetTextLineHeightWithSpacing();
319 ImGuiListClipper clipper;
320 clipper.Begin(num_palettes, item_height);
325 while (clipper.Step()) {
326 for (
int i = clipper.DisplayStart; i < clipper.DisplayEnd; ++i) {
330 std::string label = absl::StrFormat(
"Palette %d", i);
335 if (ImGui::Selectable(label.c_str(), is_selected)) {
340 ImGui::SetItemDefaultFocus();
353 ImGui::EndDisabled();
371 if (ImGui::ColorPicker4(
"##picker", &col.x,
372 ImGuiColorEditFlags_NoAlpha |
373 ImGuiColorEditFlags_PickerHueWheel |
374 ImGuiColorEditFlags_DisplayRGB |
375 ImGuiColorEditFlags_DisplayHSV)) {
382 ImGui::Text(tr(
"Current vs Original"));
384 ImGui::ColorButton(
"##current", col,
385 ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker,
393 if (ImGui::ColorButton(
394 "##original", orig_col,
395 ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker,
402 if (ImGui::IsItemHovered()) {
403 ImGui::SetTooltip(tr(
"Click to restore original color"));
412 ImGui::EndDisabled();
422 int r =
static_cast<int>(col.x);
423 int g =
static_cast<int>(col.y);
424 int b =
static_cast<int>(col.z);
427 ImGui::Text(tr(
"RGB (0-255): (%d, %d, %d)"), r, g, b);
428 if (ImGui::IsItemClicked()) {
429 ImGui::SetClipboardText(absl::StrFormat(
"(%d, %d, %d)", r, g, b).c_str());
435 if (ImGui::IsItemClicked()) {
436 ImGui::SetClipboardText(
443 ImGui::Text(tr(
"Hex: #%02X%02X%02X"), r, g, b);
444 if (ImGui::IsItemClicked()) {
445 ImGui::SetClipboardText(
446 absl::StrFormat(
"#%02X%02X%02X", r, g, b).c_str());
450 ImGui::TextDisabled(tr(
"Click any value to copy"));
463 ImGui::Text(tr(
"Palette ID: %d"), pal_meta.palette_id);
466 if (!pal_meta.name.empty()) {
467 ImGui::Text(tr(
"Name: %s"), pal_meta.name.c_str());
471 if (!pal_meta.description.empty()) {
472 ImGui::TextWrapped(
"%s", pal_meta.description.c_str());
478 ImGui::Text(tr(
"Dimensions: %d colors (%dx%d)"), metadata.colors_per_palette,
479 metadata.colors_per_row,
480 (metadata.colors_per_palette + metadata.colors_per_row - 1) /
481 metadata.colors_per_row);
483 ImGui::Text(tr(
"Color Depth: %d BPP (4-bit SNES)"), 4);
484 ImGui::TextDisabled(tr(
"(16 colors per palette possible)"));
489 ImGui::Text(tr(
"ROM Address: $%06X"), pal_meta.rom_address);
490 if (ImGui::IsItemClicked()) {
491 ImGui::SetClipboardText(
492 absl::StrFormat(
"$%06X", pal_meta.rom_address).c_str());
494 if (ImGui::IsItemHovered()) {
495 ImGui::SetTooltip(tr(
"Click to copy address"));
499 if (pal_meta.vram_address > 0) {
500 ImGui::Text(tr(
"VRAM Address: $%04X"), pal_meta.vram_address);
501 if (ImGui::IsItemClicked()) {
502 ImGui::SetClipboardText(
503 absl::StrFormat(
"$%04X", pal_meta.vram_address).c_str());
505 if (ImGui::IsItemHovered()) {
506 ImGui::SetTooltip(tr(
"Click to copy VRAM address"));
511 if (!pal_meta.usage_notes.empty()) {
513 ImGui::TextDisabled(tr(
"Usage Notes:"));
514 ImGui::TextWrapped(
"%s", pal_meta.usage_notes.c_str());
519 if (ImGui::BeginPopup(
"BatchOperations")) {
522 if (
ThemedButton(
"Copy Current Palette", ImVec2(-1, 0))) {
524 ImGui::CloseCurrentPopup();
527 if (
ThemedButton(
"Paste to Current Palette", ImVec2(-1, 0))) {
529 ImGui::CloseCurrentPopup();
534 if (
ThemedButton(
"Reset All Palettes", ImVec2(-1, 0))) {
536 ImGui::CloseCurrentPopup();
557 color_index, new_color);
561 absl::StrFormat(
"Failed to set color: %s", status.message()),
575 return absl::FailedPreconditionError(
576 "Cannot save palettes from an inactive ROM session");
648 int color_index)
const {
685 if (!palette_group || index < 0 || index >= palette_group->size()) {
688 return palette_group->mutable_palette(index);
692 int color_index)
const {
716#if defined(YAZE_WITH_JSON)
718 if (!palette_group) {
728 for (
size_t palette_index = 0; palette_index < palette_group->size();
730 const auto& palette =
731 palette_group->palette_ref(
static_cast<int>(palette_index));
733 palette_json[
"index"] =
static_cast<int>(palette_index);
736 for (
size_t color_index = 0; color_index < palette.size(); color_index++) {
737 palette_json[
"colors"].push_back(
738 absl::StrFormat(
"$%04X", palette[color_index].snes()));
741 root[
"palettes"].push_back(palette_json);
752 return absl::FailedPreconditionError(
753 "Cannot import palettes into an inactive ROM session");
755#if !defined(YAZE_WITH_JSON)
756 return absl::UnimplementedError(
"JSON support is disabled");
759 if (!palette_group) {
760 return absl::FailedPreconditionError(
"Palette group is unavailable");
766 }
catch (
const std::exception& e) {
767 return absl::InvalidArgumentError(
768 absl::StrCat(
"Failed to parse palette JSON: ", e.what()));
772 return absl::InvalidArgumentError(
"Palette JSON must be an object");
776 const auto& version_value = root[
"version"];
777 if (!version_value.is_number_integer() &&
778 !version_value.is_number_unsigned()) {
779 return absl::InvalidArgumentError(
780 "Palette JSON 'version' must be an integer");
782 int version = version_value.get<
int>();
784 return absl::InvalidArgumentError(
785 absl::StrFormat(
"Unsupported palette JSON version: %d", version));
790 const auto& group_value = root[
"group"];
791 if (!group_value.is_string()) {
792 return absl::InvalidArgumentError(
793 "Palette JSON 'group' must be a string");
795 const std::string group = group_value.get<std::string>();
797 return absl::InvalidArgumentError(absl::StrFormat(
798 "Palette JSON group '%s' does not match '%s'", group,
group_name_));
803 return absl::InvalidArgumentError(
804 "Palette JSON must contain a 'palettes' array");
807 struct PaletteImport {
809 std::vector<uint16_t> colors;
812 std::vector<PaletteImport> imports;
813 for (
const auto& palette_json : root[
"palettes"]) {
814 if (!palette_json.is_object()) {
815 return absl::InvalidArgumentError(
"Palette entry must be a JSON object");
818 if (!palette_json.contains(
"index") ||
819 !palette_json[
"index"].is_number_integer()) {
820 return absl::InvalidArgumentError(
821 "Palette entry is missing integer 'index'");
824 int palette_index = palette_json[
"index"].get<
int>();
825 if (palette_index < 0 || palette_index >= palette_group->size()) {
826 return absl::InvalidArgumentError(absl::StrFormat(
827 "Palette index %d out of range [0, %d)", palette_index,
828 static_cast<int>(palette_group->size())));
831 if (!palette_json.contains(
"colors") ||
832 !palette_json[
"colors"].is_array()) {
833 return absl::InvalidArgumentError(
834 "Palette entry is missing 'colors' array");
837 std::vector<uint16_t> colors;
838 colors.reserve(palette_json[
"colors"].size());
839 for (
const auto& color_json : palette_json[
"colors"]) {
840 auto color_or = ParseSnesColorJson(color_json);
841 if (!color_or.ok()) {
842 return color_or.status();
844 colors.push_back(*color_or);
847 const auto& palette = palette_group->palette_ref(palette_index);
848 if (colors.size() != palette.size()) {
849 return absl::InvalidArgumentError(absl::StrFormat(
850 "Palette %d expects %d colors but received %d", palette_index,
851 static_cast<int>(palette.size()),
static_cast<int>(colors.size())));
854 imports.push_back({palette_index, std::move(colors)});
858 manager.BeginBatch();
859 for (
const auto&
import : imports) {
860 for (
size_t color_index = 0; color_index <
import.colors.size();
862 auto status = manager.SetColor(
863 group_name_,
import.index,
static_cast<int>(color_index),
880 return absl::OkStatus();
893 for (
size_t i = 0; i < palette.size(); i++) {
894 result += absl::StrFormat(
"$%04X", palette[i].snes());
895 if (i < palette.size() - 1) {
900 ImGui::SetClipboardText(result.c_str());
906 return absl::FailedPreconditionError(
907 "Cannot import palettes into an inactive ROM session");
911 return absl::FailedPreconditionError(
"No palette selected");
914 const char* clipboard = ImGui::GetClipboardText();
915 if (!clipboard || clipboard[0] ==
'\0') {
916 return absl::InvalidArgumentError(
"Clipboard is empty");
919 auto colors_or = ParseClipboardColors(clipboard);
920 if (!colors_or.ok()) {
921 return colors_or.status();
924 const auto& colors = *colors_or;
925 if (colors.size() != palette->size()) {
926 return absl::InvalidArgumentError(absl::StrFormat(
927 "Clipboard contains %d colors but palette expects %d",
928 static_cast<int>(colors.size()),
static_cast<int>(palette->size())));
932 manager.BeginBatch();
933 for (
size_t color_index = 0; color_index < colors.size(); color_index++) {
935 static_cast<int>(color_index),
948 return absl::OkStatus();
974 for (
int i = 0; i < 6; i++) {
977 pal.
name = absl::StrFormat(
"Overworld Main %02d", i);
979 "BG main palette set (35 colors = 5x7, transparent slots are implicit)";
983 "Loaded by PaletteLoad_OWBGMain to CGRAM $0042 (rows 2-6, cols 1-7).";
1000 ->palette_groups.get_group(
"ow_main");
1008 const float button_size = 32.0f;
1011 for (
int i = 0; i < palette->size(); i++) {
1018 (*palette)[i], is_selected, is_modified,
1019 ImVec2(button_size, button_size))) {
1027 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1052 for (
int i = 0; i < 14; i++) {
1055 pal.
name = absl::StrFormat(
"OW Anim %02d", i);
1057 "Animated overlay palette (7 colors, transparent slot is implicit)";
1061 "Loaded by PaletteLoad_OWBG3 to CGRAM $00E2 (row 7, cols 1-7).";
1079 ->palette_groups.get_group(
"ow_animated");
1087 const float button_size = 32.0f;
1090 for (
int i = 0; i < palette->size(); i++) {
1097 (*palette)[i], is_selected, is_modified,
1098 ImVec2(button_size, button_size))) {
1105 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1129 const char* dungeon_names[] = {
1130 "Sewers",
"Hyrule Castle",
"Eastern Palace",
"Desert Palace",
1131 "Agahnim's Tower",
"Swamp Palace",
"Palace of Darkness",
"Misery Mire",
1132 "Skull Woods",
"Ice Palace",
"Tower of Hera",
"Thieves' Town",
1133 "Turtle Rock",
"Ganon's Tower",
"Generic 1",
"Generic 2",
1134 "Generic 3",
"Generic 4",
"Generic 5",
"Generic 6"};
1136 for (
int i = 0; i < 20; i++) {
1139 pal.
name = dungeon_names[i];
1140 pal.
description = absl::StrFormat(
"Dungeon palette %d", i);
1144 "90 colors = 6 CGRAM banks x 15 colors (transparent slot per bank is "
1162 ->palette_groups.get_group(
"dungeon_main");
1170 const float button_size = 28.0f;
1173 for (
int i = 0; i < palette->size(); i++) {
1180 (*palette)[i], is_selected, is_modified,
1181 ImVec2(button_size, button_size))) {
1188 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1211 const char* sprite_names[] = {
"Global Sprites (Light World)",
1212 "Global Sprites (Dark World)"};
1214 for (
int i = 0; i < 2; i++) {
1217 pal.
name = sprite_names[i];
1219 "60 colors = 4 sprite banks x 15 colors (transparent slots are "
1225 "Loaded into CGRAM rows 9-12, cols 1-15 (row col0 is transparent).";
1242 ->palette_groups.get_group(
"global_sprites");
1250 const float button_size = 28.0f;
1253 for (
int i = 0; i < palette->size(); i++) {
1260 (*palette)[i], is_selected, is_modified,
1261 ImVec2(button_size, button_size))) {
1268 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1277 tr(
"Global sprite palettes are stored in ROM as 4 banks of 15 colors "
1278 "(transparent is implicit) and loaded to PPU CGRAM rows 9-12, cols "
1280 ImGui::TextDisabled(tr(
"Note: Palettes live in CGRAM, not VRAM."));
1299 const char* armor_names[] = {
"Green Mail",
"Blue Mail",
"Red Mail",
"Bunny",
1302 for (
int i = 0; i < 5; i++) {
1305 pal.
name = armor_names[i];
1306 pal.
description = absl::StrFormat(
"Link appearance: %s", armor_names[i]);
1310 "15 colors per set (transparent slot is implicit when loaded into "
1328 ->palette_groups.get_group(
"armors");
1336 const float button_size = 32.0f;
1339 for (
int i = 0; i < palette->size(); i++) {
1346 (*palette)[i], is_selected, is_modified,
1347 ImVec2(button_size, button_size))) {
1354 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1376 for (
int i = 0; i < 12; i++) {
1379 pal.
name = absl::StrFormat(
"Sprites Aux1 %02d", i);
1381 "Auxiliary sprite palette (7 colors, transparent is implicit)";
1385 "Loaded into CGRAM with an implicit transparent slot at index 0 of the "
1403 ->palette_groups.get_group(
"sprites_aux1");
1411 const float button_size = 32.0f;
1414 for (
int i = 0; i < palette->size(); i++) {
1421 (*palette)[i], is_selected, is_modified,
1422 ImVec2(button_size, button_size))) {
1429 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1451 for (
int i = 0; i < 11; i++) {
1454 pal.
name = absl::StrFormat(
"Sprites Aux2 %02d", i);
1456 "Auxiliary sprite palette (7 colors, transparent is implicit)";
1460 "Loaded into CGRAM with an implicit transparent slot at index 0 of the "
1478 ->palette_groups.get_group(
"sprites_aux2");
1486 const float button_size = 32.0f;
1489 for (
int i = 0; i < palette->size(); i++) {
1496 (*palette)[i], is_selected, is_modified,
1497 ImVec2(button_size, button_size))) {
1504 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1526 for (
int i = 0; i < 24; i++) {
1529 pal.
name = absl::StrFormat(
"Sprites Aux3 %02d", i);
1531 "Auxiliary sprite palette (7 colors, transparent is implicit)";
1535 "Loaded into CGRAM with an implicit transparent slot at index 0 of the "
1553 ->palette_groups.get_group(
"sprites_aux3");
1561 const float button_size = 32.0f;
1564 for (
int i = 0; i < palette->size(); i++) {
1572 ImGui::BeginGroup();
1574 (*palette)[i], is_selected, is_modified,
1575 ImVec2(button_size, button_size))) {
1580 ImVec2 pos = ImGui::GetItemRectMin();
1581 ImGui::GetWindowDrawList()->AddText(
1582 ImVec2(pos.x + button_size / 2 - 4, pos.y + button_size / 2 - 8),
1583 IM_COL32(255, 255, 255, 200),
"T");
1587 (*palette)[i], is_selected, is_modified,
1588 ImVec2(button_size, button_size))) {
1596 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
static Json parse(const std::string &)
std::string dump(int=-1, char=' ', bool=false, int=0) const
bool contains(const std::string &) const
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
absl::Status WriteColor(uint32_t address, const gfx::SnesColor &color)
void DrawPaletteGrid() override
Draw the palette grid specific to this palette type.
int GetColorsPerRow() const override
Get the number of colors per row for grid layout.
static const PaletteGroupMetadata metadata_
DungeonMainPalettePanel(Rom *rom, zelda3::GameData *game_data=nullptr)
gfx::PaletteGroup * GetPaletteGroup() override
Get the palette group for this card.
static PaletteGroupMetadata InitializeMetadata()
gfx::PaletteGroup * GetPaletteGroup() override
Get the palette group for this card.
int GetColorsPerRow() const override
Get the number of colors per row for grid layout.
EquipmentPalettePanel(Rom *rom, zelda3::GameData *game_data=nullptr)
static PaletteGroupMetadata InitializeMetadata()
static const PaletteGroupMetadata metadata_
void DrawPaletteGrid() override
Draw the palette grid specific to this palette type.
void DrawPaletteGrid() override
Draw the palette grid specific to this palette type.
int GetColorsPerRow() const override
Get the number of colors per row for grid layout.
OverworldAnimatedPalettePanel(Rom *rom, zelda3::GameData *game_data=nullptr)
static PaletteGroupMetadata InitializeMetadata()
static const PaletteGroupMetadata metadata_
gfx::PaletteGroup * GetPaletteGroup() override
Get the palette group for this card.
static PaletteGroupMetadata InitializeMetadata()
OverworldMainPalettePanel(Rom *rom, zelda3::GameData *game_data=nullptr)
void DrawPaletteGrid() override
Draw the palette grid specific to this palette type.
static const PaletteGroupMetadata metadata_
gfx::PaletteGroup * GetPaletteGroup() override
Get the palette group for this card.
int GetColorsPerRow() const override
Get the number of colors per row for grid layout.
Base class for palette group editing cards.
gfx::SnesColor editing_color_
virtual gfx::PaletteGroup * GetPaletteGroup()=0
Get the palette group for this card.
void ResetPalette(int palette_index)
Reset a specific palette to original ROM values.
virtual void DrawCustomToolbarButtons()
Draw additional toolbar buttons (called after standard buttons)
void DrawPaletteSelector()
Draw palette selector dropdown.
void ResetColor(int palette_index, int color_index)
Reset a specific color to original ROM value.
gfx::SnesColor GetOriginalColor(int palette_index, int color_index) const
Get original color from ROM (for reset/comparison)
gfx::SnesPalette * GetMutablePalette(int index)
Get mutable palette by index.
absl::Status ImportFromClipboard()
void DiscardChanges()
Discard all unsaved changes.
std::string ExportToJson() const
void DrawToolbar()
Draw standard toolbar with save/discard/undo/redo.
bool HasUnsavedChanges() const
void DrawBatchOperationsPopup()
Draw batch operations popup.
void DrawMetadataInfo()
Draw palette metadata info panel.
void SetColor(int palette_index, int color_index, const gfx::SnesColor &new_color)
Set a color value (records change for undo)
virtual const PaletteGroupMetadata & GetMetadata() const =0
Get metadata for this palette group.
absl::Status SaveToRom()
Save all modified palettes to ROM.
absl::Status WriteColorToRom(int palette_index, int color_index, const gfx::SnesColor &color)
Write a single color to ROM.
ToastManager * toast_manager_
virtual void DrawCustomPanels()
Draw additional panels (called after main content)
void DrawColorPicker()
Draw color picker for selected color.
bool IsPaletteModified(int palette_index) const
std::string display_name_
PaletteGroupPanel(const std::string &group_name, const std::string &display_name, Rom *rom, zelda3::GameData *game_data=nullptr)
Construct a new Palette Group Panel.
zelda3::GameData * game_data_
bool IsColorModified(int palette_index, int color_index) const
bool IsManagedSession() const
Return true only while PaletteManager is bound to this panel's session.
void Draw(bool *p_open) override
Draw the card's ImGui UI.
void DrawColorInfo()
Draw color info panel with RGB/SNES/Hex values.
absl::Status ImportFromJson(const std::string &json)
std::string ExportToClipboard() const
virtual void DrawPaletteGrid()=0
Draw the palette grid specific to this palette type.
static PaletteGroupMetadata InitializeMetadata()
static const PaletteGroupMetadata metadata_
SpritePalettePanel(Rom *rom, zelda3::GameData *game_data=nullptr)
int GetColorsPerRow() const override
Get the number of colors per row for grid layout.
gfx::PaletteGroup * GetPaletteGroup() override
Get the palette group for this card.
void DrawCustomPanels() override
Draw additional panels (called after main content)
void DrawPaletteGrid() override
Draw the palette grid specific to this palette type.
gfx::PaletteGroup * GetPaletteGroup() override
Get the palette group for this card.
static PaletteGroupMetadata InitializeMetadata()
void DrawPaletteGrid() override
Draw the palette grid specific to this palette type.
int GetColorsPerRow() const override
Get the number of colors per row for grid layout.
SpritesAux1PalettePanel(Rom *rom, zelda3::GameData *game_data=nullptr)
static const PaletteGroupMetadata metadata_
int GetColorsPerRow() const override
Get the number of colors per row for grid layout.
gfx::PaletteGroup * GetPaletteGroup() override
Get the palette group for this card.
void DrawPaletteGrid() override
Draw the palette grid specific to this palette type.
static const PaletteGroupMetadata metadata_
static PaletteGroupMetadata InitializeMetadata()
SpritesAux2PalettePanel(Rom *rom, zelda3::GameData *game_data=nullptr)
static PaletteGroupMetadata InitializeMetadata()
int GetColorsPerRow() const override
Get the number of colors per row for grid layout.
SpritesAux3PalettePanel(Rom *rom, zelda3::GameData *game_data=nullptr)
void DrawPaletteGrid() override
Draw the palette grid specific to this palette type.
static const PaletteGroupMetadata metadata_
gfx::PaletteGroup * GetPaletteGroup() override
Get the palette group for this card.
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f)
absl::Status SetColor(const std::string &group_name, int palette_index, int color_index, const SnesColor &new_color)
Set a color in a palette (records change for undo)
bool IsGroupModified(const std::string &group_name) const
Check if a specific palette group has modifications.
absl::Status SaveGroup(const std::string &group_name)
Save a specific palette group to ROM.
void Undo()
Undo the most recent change.
void ClearHistory()
Clear undo/redo history.
bool IsManaging(const zelda3::GameData *game_data) const
Check whether the manager is bound to this exact GameData and ROM.
bool CanRedo() const
Check if redo is available.
bool CanUndo() const
Check if undo is available.
absl::Status ResetColor(const std::string &group_name, int palette_index, int color_index)
Reset a single color to its original ROM value.
absl::Status ResetPalette(const std::string &group_name, int palette_index)
Reset an entire palette to original ROM values.
bool IsColorModified(const std::string &group_name, int palette_index, int color_index) const
Check if a specific color is modified.
void DiscardGroup(const std::string &group_name)
Discard changes for a specific group.
static PaletteManager & Get()
Get the singleton instance.
SnesColor GetColor(const std::string &group_name, int palette_index, int color_index) const
Get a color from a palette.
bool IsPaletteModified(const std::string &group_name, int palette_index) const
Check if a specific palette is modified.
void Redo()
Redo the most recently undone change.
constexpr ImVec4 rgb() const
Get RGB values (WARNING: stored as 0-255 in ImVec4)
constexpr uint16_t snes() const
Get SNES 15-bit color.
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
static void HelpMarker(const char *desc)
static float GetStandardInputWidth()
#define ICON_MD_MORE_VERT
#define ICON_MD_FILE_DOWNLOAD
#define ICON_MD_FILE_UPLOAD
absl::StatusOr< uint16_t > ParseSnesHexToken(std::string token)
absl::StatusOr< std::vector< uint16_t > > ParseClipboardColors(const std::string &clipboard)
constexpr int kArmorPalettes
constexpr int kOverworldPaletteAnimated
constexpr int kOverworldPaletteMain
constexpr int kGlobalSpritesLW
constexpr int kGlobalSpritePalettesDW
constexpr int kDungeonMainPalettes
uint32_t GetPaletteAddress(const std::string &group_name, size_t palette_index, size_t color_index)
Graphical User Interface (GUI) components for the application.
bool ThemedIconButton(const char *icon, const char *tooltip, const ImVec2 &size, bool is_active, bool is_disabled, const char *panel_id, const char *anim_id)
Draw a standard icon button with theme-aware colors.
bool PrimaryButton(const char *label, const ImVec2 &size, const char *panel_id, const char *anim_id)
Draw a primary action button (accented color).
bool ThemedButton(const char *label, const ImVec2 &size, const char *panel_id, const char *anim_id)
Draw a standard text button with theme colors.
bool DangerButton(const char *label, const ImVec2 &size, const char *panel_id, const char *anim_id)
Draw a danger action button (error color).
void SectionHeader(const char *icon, const char *label, const ImVec4 &color)
IMGUI_API bool PaletteColorButton(const char *id, const gfx::SnesColor &color, bool is_selected, bool is_modified, const ImVec2 &size, ImGuiColorEditFlags flags)
ImVec4 ConvertSnesColorToImVec4(const gfx::SnesColor &color)
Convert SnesColor to standard ImVec4 for display.
gfx::SnesColor ConvertImVec4ToSnesColor(const ImVec4 &color)
Convert standard ImVec4 to SnesColor.
PaletteGroup * get_group(const std::string &group_name)
Represents a group of palettes.
gfx::PaletteGroupMap palette_groups