5#include "absl/strings/str_format.h"
12#include "imgui/imgui.h"
25 const std::string& display_name,
Rom* rom,
27 : group_name_(group_name), display_name_(display_name), rom_(rom),
28 game_data_(game_data) {
49 if (ImGui::BeginTable(
50 "##PalettePanelLayout", 2,
51 ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV)) {
52 ImGui::TableSetupColumn(
"Grid", ImGuiTableColumnFlags_WidthStretch, 0.6f);
53 ImGui::TableSetupColumn(
"Editor", ImGuiTableColumnFlags_WidthStretch,
56 ImGui::TableNextRow();
57 ImGui::TableNextColumn();
64 ImGui::TableNextColumn();
74 ImGui::TextDisabled(
"Select a color to edit");
94 ImGui::BeginDisabled(!has_changes);
101 ImGui::EndDisabled();
106 ImGui::BeginDisabled(!has_changes);
110 ImGui::EndDisabled();
116 size_t modified_count = 0;
119 for (
int p = 0; p < group->size(); p++) {
125 ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.0f, 1.0f),
"%s %zu modified",
130 ImGui::Dummy(ImVec2(20, 0));
138 ImGui::EndDisabled();
145 ImGui::EndDisabled();
148 ImGui::Dummy(ImVec2(20, 0));
163 ImGui::OpenPopup(
"BatchOperations");
175 int num_palettes = palette_group->size();
177 ImGui::Text(
"Palette:");
181 if (ImGui::BeginCombo(
184 for (
int i = 0; i < num_palettes; i++) {
188 std::string label = absl::StrFormat(
"Palette %d", i);
193 if (ImGui::Selectable(label.c_str(), is_selected)) {
198 ImGui::SetItemDefaultFocus();
210 ImGui::EndDisabled();
228 if (ImGui::ColorPicker4(
"##picker", &col.x,
229 ImGuiColorEditFlags_NoAlpha |
230 ImGuiColorEditFlags_PickerHueWheel |
231 ImGuiColorEditFlags_DisplayRGB |
232 ImGuiColorEditFlags_DisplayHSV)) {
239 ImGui::Text(
"Current vs Original");
241 ImGui::ColorButton(
"##current", col,
242 ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker,
250 if (ImGui::ColorButton(
251 "##original", orig_col,
252 ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker,
259 if (ImGui::IsItemHovered()) {
260 ImGui::SetTooltip(
"Click to restore original color");
269 ImGui::EndDisabled();
279 int r =
static_cast<int>(col.x);
280 int g =
static_cast<int>(col.y);
281 int b =
static_cast<int>(col.z);
284 ImGui::Text(
"RGB (0-255): (%d, %d, %d)", r, g, b);
285 if (ImGui::IsItemClicked()) {
286 ImGui::SetClipboardText(absl::StrFormat(
"(%d, %d, %d)", r, g, b).c_str());
292 if (ImGui::IsItemClicked()) {
293 ImGui::SetClipboardText(
300 ImGui::Text(
"Hex: #%02X%02X%02X", r, g, b);
301 if (ImGui::IsItemClicked()) {
302 ImGui::SetClipboardText(
303 absl::StrFormat(
"#%02X%02X%02X", r, g, b).c_str());
307 ImGui::TextDisabled(
"Click any value to copy");
320 ImGui::Text(
"Palette ID: %d", pal_meta.palette_id);
323 if (!pal_meta.name.empty()) {
324 ImGui::Text(
"Name: %s", pal_meta.name.c_str());
328 if (!pal_meta.description.empty()) {
329 ImGui::TextWrapped(
"%s", pal_meta.description.c_str());
335 ImGui::Text(
"Dimensions: %d colors (%dx%d)", metadata.colors_per_palette,
336 metadata.colors_per_row,
337 (metadata.colors_per_palette + metadata.colors_per_row - 1) /
338 metadata.colors_per_row);
340 ImGui::Text(
"Color Depth: %d BPP (4-bit SNES)", 4);
341 ImGui::TextDisabled(
"(16 colors per palette possible)");
346 ImGui::Text(
"ROM Address: $%06X", pal_meta.rom_address);
347 if (ImGui::IsItemClicked()) {
348 ImGui::SetClipboardText(
349 absl::StrFormat(
"$%06X", pal_meta.rom_address).c_str());
351 if (ImGui::IsItemHovered()) {
352 ImGui::SetTooltip(
"Click to copy address");
356 if (pal_meta.vram_address > 0) {
357 ImGui::Text(
"VRAM Address: $%04X", pal_meta.vram_address);
358 if (ImGui::IsItemClicked()) {
359 ImGui::SetClipboardText(
360 absl::StrFormat(
"$%04X", pal_meta.vram_address).c_str());
362 if (ImGui::IsItemHovered()) {
363 ImGui::SetTooltip(
"Click to copy VRAM address");
368 if (!pal_meta.usage_notes.empty()) {
370 ImGui::TextDisabled(
"Usage Notes:");
371 ImGui::TextWrapped(
"%s", pal_meta.usage_notes.c_str());
376 if (ImGui::BeginPopup(
"BatchOperations")) {
379 if (
ThemedButton(
"Copy Current Palette", ImVec2(-1, 0))) {
381 ImGui::CloseCurrentPopup();
384 if (
ThemedButton(
"Paste to Current Palette", ImVec2(-1, 0))) {
386 ImGui::CloseCurrentPopup();
391 if (
ThemedButton(
"Reset All Palettes", ImVec2(-1, 0))) {
393 ImGui::CloseCurrentPopup();
406 color_index, new_color);
468 int color_index)
const {
493 if (!palette_group || index < 0 || index >= palette_group->size()) {
496 return palette_group->mutable_palette(index);
500 int color_index)
const {
525 return absl::UnimplementedError(
"Import from JSON not yet implemented");
537 for (
size_t i = 0; i < palette.size(); i++) {
538 result += absl::StrFormat(
"$%04X", palette[i].snes());
539 if (i < palette.size() - 1) {
544 ImGui::SetClipboardText(result.c_str());
550 return absl::UnimplementedError(
"Import from clipboard not yet implemented");
573 for (
int i = 0; i < 20; i++) {
576 pal.
name = absl::StrFormat(
"Light World %d", i);
577 pal.
description =
"Used for Light World overworld graphics";
580 pal.
usage_notes =
"Modifying these colors affects Light World appearance";
585 for (
int i = 20; i < 40; i++) {
588 pal.
name = absl::StrFormat(
"Dark World %d", i - 20);
589 pal.
description =
"Used for Dark World overworld graphics";
592 pal.
usage_notes =
"Modifying these colors affects Dark World appearance";
597 for (
int i = 40; i < 60; i++) {
600 pal.
name = absl::StrFormat(
"Special %d", i - 40);
601 pal.
description =
"Used for Special World and triforce room";
604 pal.
usage_notes =
"Modifying these colors affects Special World areas";
626 const float button_size = 32.0f;
629 for (
int i = 0; i < palette->size(); i++) {
636 (*palette)[i], is_selected, is_modified,
637 ImVec2(button_size, button_size))) {
645 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
657 :
PaletteGroupPanel(
"ow_animated",
"Overworld Animated Palettes", rom, game_data) {}
667 const char* anim_names[] = {
"Water",
"Lava",
"Poison Water",
"Ice"};
668 for (
int i = 0; i < 4; i++) {
671 pal.
name = anim_names[i];
673 absl::StrFormat(
"%s animated palette cycle", anim_names[i]);
677 "These palettes cycle through multiple frames for animation";
700 const float button_size = 32.0f;
703 for (
int i = 0; i < palette->size(); i++) {
710 (*palette)[i], is_selected, is_modified,
711 ImVec2(button_size, button_size))) {
718 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
740 const char* dungeon_names[] = {
741 "Sewers",
"Hyrule Castle",
"Eastern Palace",
"Desert Palace",
742 "Agahnim's Tower",
"Swamp Palace",
"Palace of Darkness",
"Misery Mire",
743 "Skull Woods",
"Ice Palace",
"Tower of Hera",
"Thieves' Town",
744 "Turtle Rock",
"Ganon's Tower",
"Generic 1",
"Generic 2",
745 "Generic 3",
"Generic 4",
"Generic 5",
"Generic 6"};
747 for (
int i = 0; i < 20; i++) {
750 pal.
name = dungeon_names[i];
751 pal.
description = absl::StrFormat(
"Dungeon palette %d", i);
777 const float button_size = 28.0f;
780 for (
int i = 0; i < palette->size(); i++) {
787 (*palette)[i], is_selected, is_modified,
788 ImVec2(button_size, button_size))) {
795 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
818 const char* sprite_names[] = {
"Global Sprites (Light World)",
819 "Global Sprites (Dark World)"};
821 for (
int i = 0; i < 2; i++) {
824 pal.
name = sprite_names[i];
826 "60 colors = 4 sprite sub-palettes (rows) with transparent at 0, 16, "
831 "4 sprite sub-palettes of 15 colors + transparent each. "
832 "Row 0: colors 0-15, Row 1: 16-31, Row 2: 32-47, Row 3: 48-59";
855 const float button_size = 28.0f;
858 for (
int i = 0; i < palette->size(); i++) {
866 bool is_transparent_slot = (i % 16 == 0);
867 if (is_transparent_slot) {
870 (*palette)[i], is_selected, is_modified,
871 ImVec2(button_size, button_size))) {
876 ImVec2 pos = ImGui::GetItemRectMin();
877 ImGui::GetWindowDrawList()->AddText(
878 ImVec2(pos.x + button_size / 2 - 4, pos.y + button_size / 2 - 8),
879 IM_COL32(255, 255, 255, 200),
"T");
882 if (ImGui::IsItemHovered()) {
883 ImGui::SetTooltip(
"Transparent color slot for sprite sub-palette %d",
888 (*palette)[i], is_selected, is_modified,
889 ImVec2(button_size, button_size))) {
897 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
911 ImGui::TextWrapped(
"This sprite palette is loaded to VRAM address $%04X",
912 pal_meta.vram_address);
914 "VRAM palettes are used by the SNES PPU for sprite rendering");
933 const char* armor_names[] = {
"Green Mail",
"Blue Mail",
"Red Mail"};
935 for (
int i = 0; i < 3; i++) {
938 pal.
name = armor_names[i];
939 pal.
description = absl::StrFormat(
"Link's %s colors", armor_names[i]);
942 pal.
usage_notes =
"Changes Link's tunic appearance";
964 const float button_size = 32.0f;
967 for (
int i = 0; i < palette->size(); i++) {
974 (*palette)[i], is_selected, is_modified,
975 ImVec2(button_size, button_size))) {
982 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1004 for (
int i = 0; i < 12; i++) {
1007 pal.
name = absl::StrFormat(
"Sprites Aux1 %02d", i);
1008 pal.
description =
"Auxiliary sprite palette (7 colors + transparent)";
1011 pal.
usage_notes =
"Used by specific sprites. Color 0 is transparent.";
1034 const float button_size = 32.0f;
1037 for (
int i = 0; i < palette->size(); i++) {
1044 (*palette)[i], is_selected, is_modified,
1045 ImVec2(button_size, button_size))) {
1052 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1074 for (
int i = 0; i < 11; i++) {
1077 pal.
name = absl::StrFormat(
"Sprites Aux2 %02d", i);
1078 pal.
description =
"Auxiliary sprite palette (7 colors + transparent)";
1081 pal.
usage_notes =
"Used by specific sprites. Color 0 is transparent.";
1104 const float button_size = 32.0f;
1107 for (
int i = 0; i < palette->size(); i++) {
1115 ImGui::BeginGroup();
1117 (*palette)[i], is_selected, is_modified,
1118 ImVec2(button_size, button_size))) {
1123 ImVec2 pos = ImGui::GetItemRectMin();
1124 ImGui::GetWindowDrawList()->AddText(
1125 ImVec2(pos.x + button_size / 2 - 4, pos.y + button_size / 2 - 8),
1126 IM_COL32(255, 255, 255, 200),
"T");
1130 (*palette)[i], is_selected, is_modified,
1131 ImVec2(button_size, button_size))) {
1139 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1161 for (
int i = 0; i < 24; i++) {
1164 pal.
name = absl::StrFormat(
"Sprites Aux3 %02d", i);
1165 pal.
description =
"Auxiliary sprite palette (7 colors + transparent)";
1168 pal.
usage_notes =
"Used by specific sprites. Color 0 is transparent.";
1191 const float button_size = 32.0f;
1194 for (
int i = 0; i < palette->size(); i++) {
1202 ImGui::BeginGroup();
1204 (*palette)[i], is_selected, is_modified,
1205 ImVec2(button_size, button_size))) {
1210 ImVec2 pos = ImGui::GetItemRectMin();
1211 ImGui::GetWindowDrawList()->AddText(
1212 ImVec2(pos.x + button_size / 2 - 4, pos.y + button_size / 2 - 8),
1213 IM_COL32(255, 255, 255, 200),
"T");
1217 (*palette)[i], is_selected, is_modified,
1218 ImVec2(button_size, button_size))) {
1226 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
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.
virtual void DrawCustomPanels()
Draw additional panels (called after main content)
void DrawColorPicker()
Draw color picker for selected color.
bool IsPaletteModified(int palette_index) const
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
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.
const PaletteGroupMetadata & GetMetadata() const override
Get metadata for this palette group.
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.
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 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
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 ThemedButton(const char *label, const ImVec2 &size)
Draw a standard text button with theme colors.
bool DangerButton(const char *label, const ImVec2 &size)
Draw a danger action button (error color).
void SectionHeader(const char *icon, const char *label, const ImVec4 &color)
bool ThemedIconButton(const char *icon, const char *tooltip, const ImVec2 &size, bool is_active, bool is_disabled)
Draw a standard icon button with theme-aware colors.
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.
bool PrimaryButton(const char *label, const ImVec2 &size)
Draw a primary action button (accented color).
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