5#include "absl/strings/str_format.h"
27 const char* group_names[] = {
"ow_main",
"ow_aux",
"ow_animated",
28 "hud",
"global_sprites",
"armors",
29 "swords",
"shields",
"sprites_aux1",
30 "sprites_aux2",
"sprites_aux3",
"dungeon_main",
31 "grass",
"3d_object",
"ow_mini_map"};
33 for (
const auto& group_name : group_names) {
35 auto* group = palette_groups->
get_group(group_name);
37 std::vector<SnesPalette> originals;
38 for (
size_t i = 0; i < group->size(); i++) {
39 originals.push_back(group->palette(i));
43 }
catch (
const std::exception& e) {
73 int palette_index,
int color_index)
const {
74 const auto* group =
GetGroup(group_name);
75 if (!group || palette_index < 0 || palette_index >= group->size()) {
79 const auto& palette = group->palette_ref(palette_index);
80 if (color_index < 0 || color_index >= palette.size()) {
84 return palette[color_index];
88 int palette_index,
int color_index,
91 return absl::FailedPreconditionError(
"PaletteManager not initialized");
96 return absl::NotFoundError(
97 absl::StrFormat(
"Palette group '%s' not found", group_name));
100 if (palette_index < 0 || palette_index >= group->size()) {
101 return absl::InvalidArgumentError(absl::StrFormat(
102 "Palette index %d out of range [0, %d)", palette_index, group->size()));
105 auto* palette = group->mutable_palette(palette_index);
106 if (color_index < 0 || color_index >= palette->size()) {
107 return absl::InvalidArgumentError(absl::StrFormat(
108 "Color index %d out of range [0, %d)", color_index, palette->size()));
112 SnesColor original_color = (*palette)[color_index];
115 (*palette)[color_index] = new_color;
122 auto now = std::chrono::system_clock::now();
123 auto timestamp_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
124 now.time_since_epoch())
128 color_index, original_color,
129 new_color,
static_cast<uint64_t
>(timestamp_ms)};
134 group_name, palette_index, color_index};
138 auto now = std::chrono::system_clock::now();
139 auto timestamp_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
140 now.time_since_epoch())
143 original_color, new_color,
144 static_cast<uint64_t
>(timestamp_ms)});
147 return absl::OkStatus();
151 int palette_index,
int color_index) {
153 return SetColor(group_name, palette_index, color_index, original);
159 return absl::FailedPreconditionError(
"PaletteManager not initialized");
165 return absl::NotFoundError(
"Original palette not found");
169 if (!group || palette_index >= group->size()) {
170 return absl::NotFoundError(
"Palette group or index not found");
174 *group->mutable_palette(palette_index) = it->second[palette_index];
185 return absl::OkStatus();
195 std::vector<std::string> groups;
197 groups.push_back(group_name);
208 int palette_index)
const {
213 return it->second.contains(palette_index);
217 int palette_index,
int color_index)
const {
223 auto pal_it = group_it->second.find(palette_index);
224 if (pal_it == group_it->second.end()) {
228 return pal_it->second.contains(color_index);
234 for (
const auto& [__, color_set] : palette_map) {
235 count += color_set.size();
245 return absl::FailedPreconditionError(
"PaletteManager not initialized");
250 return absl::NotFoundError(
251 absl::StrFormat(
"Palette group '%s' not found", group_name));
258 return absl::OkStatus();
262 for (
int palette_idx : pal_it->second) {
263 auto* palette = group->mutable_palette(palette_idx);
268 for (
int color_idx : color_it->second) {
281 for (
size_t i = 0; i < group->size() && i < originals.size(); i++) {
282 originals[i] = group->palette(i);
299 return absl::OkStatus();
304 return absl::FailedPreconditionError(
"PaletteManager not initialized");
316 return absl::OkStatus();
321 return absl::FailedPreconditionError(
"PaletteManager not initialized");
328 if (modified_groups.empty()) {
329 return absl::OkStatus();
332 for (
const auto& group_name : modified_groups) {
340 return absl::OkStatus();
362 for (
int palette_idx : pal_it->second) {
363 if (palette_idx < orig_it->second.size()) {
364 *group->mutable_palette(palette_idx) = orig_it->second[palette_idx];
408 if (group && change.palette_index < group->size()) {
409 auto* palette = group->mutable_palette(change.palette_index);
410 if (change.color_index < palette->size()) {
411 (*palette)[change.color_index] = change.original_color;
435 if (group && change.palette_index < group->size()) {
436 auto* palette = group->mutable_palette(change.palette_index);
437 if (change.color_index < palette->size()) {
438 (*palette)[change.color_index] = change.new_color;
512 }
catch (
const std::exception&) {
518 const std::string& group_name)
const {
525 ->get_group(group_name);
528 }
catch (
const std::exception&) {
535 int color_index)
const {
541 const auto& palette = it->second[palette_index];
542 if (color_index >= palette.size()) {
546 return palette[color_index];
568 int palette_index,
int color_index) {
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
void set_dirty(bool dirty)
absl::Status WriteShort(int addr, uint16_t value)
void NotifyPaletteModified(const std::string &group_name, int palette_index=-1)
Notify all listeners that a palette has been modified.
void RecordChange(const PaletteColorChange &change)
Helper: Record a change for undo.
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)
std::vector< std::string > GetModifiedGroups() const
Get list of modified palette group names.
static constexpr size_t kMaxUndoHistory
bool HasUnsavedChanges() const
Check if there are ANY unsaved changes.
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 BeginBatch()
Begin a batch operation (groups multiple changes into one undo step)
PaletteGroup * GetMutableGroup(const std::string &group_name)
Helper: Get mutable palette group.
void Undo()
Undo the most recent change.
void ClearHistory()
Clear undo/redo history.
std::unordered_map< int, ChangeCallback > change_listeners_
Change listeners.
int batch_depth_
Batch operation support.
std::deque< PaletteColorChange > redo_stack_
std::unordered_map< std::string, std::unordered_set< int > > modified_palettes_
void UnregisterChangeListener(int callback_id)
Unregister a change listener.
Rom * rom_
ROM instance (not owned) - legacy, used when game_data_ is null.
std::unordered_map< std::string, std::unordered_map< int, std::unordered_set< int > > > modified_colors_
bool CanRedo() const
Check if redo is available.
bool InBatch() const
Check if currently in a batch operation.
bool CanUndo() const
Check if undo is available.
void Initialize(zelda3::GameData *game_data)
Initialize the palette manager with GameData.
absl::Status ResetColor(const std::string &group_name, int palette_index, int color_index)
Reset a single color to its original ROM value.
void NotifyListeners(const PaletteChangeEvent &event)
Helper: Notify all listeners of an event.
void ClearModifiedFlags(const std::string &group_name)
Helper: Clear modified flags for a group.
void EndBatch()
End a batch operation.
int RegisterChangeListener(ChangeCallback callback)
Register a callback for palette change events.
std::deque< PaletteColorChange > undo_stack_
Undo/redo stacks.
absl::Status ResetPalette(const std::string &group_name, int palette_index)
Reset an entire palette to original ROM values.
SnesColor GetOriginalColor(const std::string &group_name, int palette_index, int color_index) const
Helper: Get original color from snapshot.
bool IsColorModified(const std::string &group_name, int palette_index, int color_index) const
Check if a specific color is modified.
void MarkModified(const std::string &group_name, int palette_index, int color_index)
Helper: Mark a color as modified.
void DiscardGroup(const std::string &group_name)
Discard changes for a specific group.
bool IsInitialized() const
Check if manager is initialized.
zelda3::GameData * game_data_
GameData instance (not owned) - preferred.
void DiscardAllChanges()
Discard ALL unsaved changes.
size_t GetModifiedColorCount() const
Get count of modified colors across all groups.
std::unordered_map< std::string, std::vector< SnesPalette > > original_palettes_
std::vector< PaletteColorChange > batch_changes_
const PaletteGroup * GetGroup(const std::string &group_name) const
Helper: Get const palette group.
SnesColor GetColor(const std::string &group_name, int palette_index, int color_index) const
Get a color from a palette.
std::function< void(const PaletteChangeEvent &)> ChangeCallback
absl::Status SaveAllToRom()
Save ALL modified palettes to ROM.
bool IsPaletteModified(const std::string &group_name, int palette_index) const
Check if a specific palette is modified.
absl::Status ApplyPreviewChanges()
Apply preview changes to other editors without saving to ROM.
void Redo()
Redo the most recently undone change.
uint32_t GetPaletteAddress(const std::string &group_name, size_t palette_index, size_t color_index)
#define RETURN_IF_ERROR(expr)
Event notification for palette changes.
@ kColorChanged
Single color was modified.
@ kPaletteReset
Entire palette was reset.
@ kAllDiscarded
All changes discarded.
@ kGroupDiscarded
Palette group changes were discarded.
@ kAllSaved
All changes saved to ROM.
@ kGroupSaved
Palette group was saved to ROM.
Represents a single color change operation.
Represents a mapping of palette groups.
PaletteGroup * get_group(const std::string &group_name)
Represents a group of palettes.
gfx::PaletteGroupMap palette_groups