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) {
86 int palette_index,
int color_index)
const {
87 const auto* group =
GetGroup(group_name);
88 if (!group || palette_index < 0 || palette_index >= group->size()) {
92 const auto& palette = group->palette_ref(palette_index);
93 if (color_index < 0 || color_index >= palette.size()) {
97 return palette[color_index];
101 int palette_index,
int color_index,
104 return absl::FailedPreconditionError(
"PaletteManager not initialized");
109 return absl::NotFoundError(
110 absl::StrFormat(
"Palette group '%s' not found", group_name));
113 if (palette_index < 0 || palette_index >= group->size()) {
114 return absl::InvalidArgumentError(absl::StrFormat(
115 "Palette index %d out of range [0, %d)", palette_index, group->size()));
118 auto* palette = group->mutable_palette(palette_index);
119 if (color_index < 0 || color_index >= palette->size()) {
120 return absl::InvalidArgumentError(absl::StrFormat(
121 "Color index %d out of range [0, %d)", color_index, palette->size()));
125 SnesColor original_color = (*palette)[color_index];
128 (*palette)[color_index] = new_color;
135 auto now = std::chrono::system_clock::now();
136 auto timestamp_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
137 now.time_since_epoch())
141 color_index, original_color,
142 new_color,
static_cast<uint64_t
>(timestamp_ms)};
147 group_name, palette_index, color_index};
151 auto now = std::chrono::system_clock::now();
152 auto timestamp_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
153 now.time_since_epoch())
156 original_color, new_color,
157 static_cast<uint64_t
>(timestamp_ms)});
160 return absl::OkStatus();
164 int palette_index,
int color_index) {
166 return SetColor(group_name, palette_index, color_index, original);
172 return absl::FailedPreconditionError(
"PaletteManager not initialized");
178 return absl::NotFoundError(
"Original palette not found");
182 if (!group || palette_index >= group->size()) {
183 return absl::NotFoundError(
"Palette group or index not found");
187 *group->mutable_palette(palette_index) = it->second[palette_index];
198 return absl::OkStatus();
208 std::vector<std::string> groups;
210 groups.push_back(group_name);
221 int palette_index)
const {
226 return it->second.contains(palette_index);
230 int palette_index,
int color_index)
const {
236 auto pal_it = group_it->second.find(palette_index);
237 if (pal_it == group_it->second.end()) {
241 return pal_it->second.contains(color_index);
247 for (
const auto& [__, color_set] : palette_map) {
248 count += color_set.size();
258 return absl::FailedPreconditionError(
"PaletteManager not initialized");
266 return absl::FailedPreconditionError(
"No ROM available for palette save");
271 return absl::NotFoundError(
272 absl::StrFormat(
"Palette group '%s' not found", group_name));
279 return absl::OkStatus();
283 for (
int palette_idx : pal_it->second) {
284 auto* palette = group->mutable_palette(palette_idx);
289 for (
int color_idx : color_it->second) {
302 for (
size_t i = 0; i < group->size() && i < originals.size(); i++) {
303 originals[i] = group->palette(i);
320 return absl::OkStatus();
325 return absl::FailedPreconditionError(
"PaletteManager not initialized");
337 return absl::OkStatus();
342 return absl::FailedPreconditionError(
"PaletteManager not initialized");
349 if (modified_groups.empty()) {
350 return absl::OkStatus();
353 for (
const auto& group_name : modified_groups) {
361 return absl::OkStatus();
383 for (
int palette_idx : pal_it->second) {
384 if (palette_idx < orig_it->second.size()) {
385 *group->mutable_palette(palette_idx) = orig_it->second[palette_idx];
429 if (group && change.palette_index < group->size()) {
430 auto* palette = group->mutable_palette(change.palette_index);
431 if (change.color_index < palette->size()) {
432 (*palette)[change.color_index] = change.original_color;
456 if (group && change.palette_index < group->size()) {
457 auto* palette = group->mutable_palette(change.palette_index);
458 if (change.color_index < palette->size()) {
459 (*palette)[change.color_index] = change.new_color;
533 }
catch (
const std::exception&) {
539 const std::string& group_name)
const {
546 ->get_group(group_name);
549 }
catch (
const std::exception&) {
556 int color_index)
const {
562 const auto& palette = it->second[palette_index];
563 if (color_index >= palette.size()) {
567 return palette[color_index];
589 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.
void ResetForTesting()
Reset all state for test isolation.
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