yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
palette_manager.h
Go to the documentation of this file.
1#ifndef YAZE_APP_GFX_PALETTE_MANAGER_H
2#define YAZE_APP_GFX_PALETTE_MANAGER_H
3
4#include <cstdint>
5#include <deque>
6#include <functional>
7#include <memory>
8#include <optional>
9#include <string>
10#include <unordered_map>
11#include <unordered_set>
12#include <utility>
13#include <vector>
14
15#include "absl/status/status.h"
16#include "absl/status/statusor.h"
19
20namespace yaze {
21
22// Forward declarations
23class Rom;
24namespace zelda3 {
25struct GameData;
26}
27
28namespace gfx {
29
41
54
56 std::string group_name;
57 int palette_index = -1;
58 int color_index = -1;
59};
60
77 public:
78 using ChangeCallback = std::function<void(const PaletteChangeEvent&)>;
79
81 static PaletteManager& Get() {
82 static PaletteManager instance;
83 return instance;
84 }
85
86 // Delete copy/move constructors and assignment operators
91
92 // ========== Initialization ==========
93
98 void Initialize(zelda3::GameData* game_data);
99
104 void Initialize(Rom* rom);
105
109 bool IsInitialized() const;
110
117 void ActivateSession(zelda3::GameData* game_data);
118
119 // In-memory color changes, independent of sheet pixels and file-save state.
120 uint64_t ColorRevision(const zelda3::GameData* game_data) const;
121
128 bool IsSessionActive(const zelda3::GameData* game_data) const;
129
133 bool IsManaging(const zelda3::GameData* game_data) const;
134
138 void ReleaseSession(const zelda3::GameData* game_data);
139
143 void ResetForTesting();
144
145 // ========== Color Operations ==========
146
154 SnesColor GetColor(const std::string& group_name, int palette_index,
155 int color_index) const;
156
165 absl::Status SetColor(const std::string& group_name, int palette_index,
166 int color_index, const SnesColor& new_color);
167
168 // A command in another editor owns history for these changes. Preflights
169 // expected colors and defers notifications until the participant succeeds.
170 // On failure restores palette values and tracking; the participant must
171 // restore its own state before returning an error. Does not push/clear history.
172 absl::Status ApplyExternalChanges(
173 const zelda3::GameData* owner,
174 const std::vector<PaletteColorChange>& changes,
175 const std::function<absl::Status()>& participant);
176
180 absl::Status ResetColor(const std::string& group_name, int palette_index,
181 int color_index);
182
186 absl::Status ResetPalette(const std::string& group_name, int palette_index);
187
188 // Live single-color interaction. SetColor still publishes every preview;
189 // commit records only its first/final colors. Cancel preserves Undo/Redo.
190 // Explicit navigation or another operation commits the interaction. A nonzero
191 // caller ID prevents another visible picker from ending this gesture.
192 absl::Status BeginColorEdit(const std::string& group_name, int palette_index,
193 int color_index, uint64_t caller_id = 0);
194 void CommitColorEdit(uint64_t caller_id = 0);
195 void CancelColorEdit(uint64_t caller_id = 0);
196 bool HasColorEdit(uint64_t caller_id = 0) const;
197
198 // ========== Dirty Tracking ==========
199
203 bool HasUnsavedChanges() const;
204
208 bool HasUnsavedChanges(const zelda3::GameData* game_data) const;
209
213 std::vector<std::string> GetModifiedGroups() const;
214
218 bool IsGroupModified(const std::string& group_name) const;
219
223 bool IsPaletteModified(const std::string& group_name,
224 int palette_index) const;
225
229 bool IsColorModified(const std::string& group_name, int palette_index,
230 int color_index) const;
231
235 size_t GetModifiedColorCount() const;
236
240 size_t GetModifiedColorCount(const zelda3::GameData* game_data) const;
241
245 std::vector<std::pair<uint32_t, uint32_t>> GetModifiedColorWriteRanges()
246 const;
247
251 std::vector<std::pair<uint32_t, uint32_t>> GetModifiedColorWriteRanges(
252 const zelda3::GameData* game_data) const;
253
260 std::vector<std::pair<uint32_t, uint32_t>> GetModifiedGroupColorWriteRanges(
261 const std::string& group_name) const;
262
263 // ========== Persistence ==========
264
268 absl::Status SaveGroup(const std::string& group_name);
269
273 absl::Status SaveAllToRom();
274
275 // Checkpoint persistence metadata that SaveGroup/SaveAllToRom clear. This is
276 // used by the coordinated ROM-save pipeline so a later serializer,
277 // validation gate, backup, or disk failure leaves palette edits retryable.
278 absl::Status BeginSaveTransaction();
281
285 void DiscardGroup(const std::string& group_name);
286
290 void DiscardAllChanges();
291
292 // ========== Preview Mode ==========
293
301 absl::Status ApplyPreviewChanges();
302
303 // ========== Undo/Redo ==========
304
308 void Undo();
309
313 void Redo();
314
319 bool CanUndo() const;
320
325 bool CanRedo() const;
326
330 size_t GetUndoStackSize() const;
331
335 size_t GetRedoStackSize() const;
336
340 void ClearHistory();
341
342 // ========== Change Notifications ==========
343
349
353 void UnregisterChangeListener(int callback_id);
354
355 // ========== Batch Operations ==========
356
363 void BeginBatch();
364
368 void EndBatch();
369
373 bool InBatch() const;
374
375 // Original value used by ResetColor and the before/after comparison UI.
376 SnesColor GetOriginalColor(const std::string& group_name, int palette_index,
377 int color_index) const;
378
379 private:
380 PaletteManager() = default;
381 ~PaletteManager() = default;
382
384 PaletteGroup* GetMutableGroup(const std::string& group_name);
385
387 const PaletteGroup* GetGroup(const std::string& group_name) const;
388
390 void RecordChanges(std::vector<PaletteColorChange> changes);
391
392 // Simulate the complete entry in replay order before changing any color or
393 // history. Repeated-slot changes compare against staged intermediate values.
394 bool CanReplayHistory(const std::vector<PaletteColorChange>& changes,
395 bool redo) const;
396
398 void RecordChange(const PaletteColorChange& change);
399
401 void NotifyListeners(const PaletteChangeEvent& event);
402
404 void MarkModified(const std::string& group_name, int palette_index,
405 int color_index);
406
408 void ClearModifiedFlags(const std::string& group_name);
409
411 std::unordered_map<std::string, std::vector<SnesPalette>> original_palettes;
412 std::unordered_map<std::string, std::unordered_set<int>> modified_palettes;
413 std::unordered_map<std::string,
414 std::unordered_map<int, std::unordered_set<int>>>
416 };
417
419 bool initialized = false;
420 uint64_t color_revision = 0;
421 std::unordered_map<std::string, std::vector<SnesPalette>> original_palettes;
422 std::unordered_map<std::string, std::unordered_set<int>> modified_palettes;
423 std::unordered_map<std::string,
424 std::unordered_map<int, std::unordered_set<int>>>
426 std::optional<SaveTransactionSnapshot> save_transaction_snapshot;
427 std::deque<std::vector<PaletteColorChange>> undo_stack;
428 std::deque<std::vector<PaletteColorChange>> redo_stack;
429 std::optional<PaletteColorChange> color_edit;
430 uint64_t color_edit_caller = 0;
431 int batch_depth = 0;
432 std::vector<PaletteColorChange> batch_changes;
433 };
434
436 const SessionState* CurrentState() const;
437 const SessionState* FindState(const zelda3::GameData* game_data) const;
438
439 // ========== Member Variables ==========
440
443
445 Rom* rom_ = nullptr;
446
448 std::unordered_map<const zelda3::GameData*, SessionState> session_states_;
449
452
453 static constexpr size_t kMaxUndoHistory = 500;
454
456 std::unordered_map<int, ChangeCallback> change_listeners_;
458};
459
460} // namespace gfx
461} // namespace yaze
462
463#endif // YAZE_APP_GFX_PALETTE_MANAGER_H
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:49
Centralized palette management system.
void RecordChange(const PaletteColorChange &change)
Helper: Record a single-color undo step.
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 CanReplayHistory(const std::vector< PaletteColorChange > &changes, bool redo) const
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.
uint64_t ColorRevision(const zelda3::GameData *game_data) const
absl::Status SaveGroup(const std::string &group_name)
Save a specific palette group to ROM.
std::vector< std::pair< uint32_t, uint32_t > > GetModifiedColorWriteRanges() const
Get exact, coalesced half-open ROM ranges for modified colors.
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 ActivateSession(zelda3::GameData *game_data)
Select the palette state for a ROM session without resnapshotting it.
size_t GetUndoStackSize() const
Get size of undo stack.
absl::Status BeginColorEdit(const std::string &group_name, int palette_index, int color_index, uint64_t caller_id=0)
void Undo()
Undo the most recent change.
size_t GetRedoStackSize() const
Get size of redo stack.
void ClearHistory()
Clear undo/redo history.
std::unordered_map< int, ChangeCallback > change_listeners_
Change listeners.
std::vector< std::pair< uint32_t, uint32_t > > GetModifiedGroupColorWriteRanges(const std::string &group_name) const
Get exact, coalesced half-open ROM ranges for one modified group.
void UnregisterChangeListener(int callback_id)
Unregister a change listener.
Rom * rom_
ROM instance (not owned) - legacy, used when game_data_ is null.
bool IsManaging(const zelda3::GameData *game_data) const
Check whether the manager is bound to this exact GameData and ROM.
void CancelColorEdit(uint64_t caller_id=0)
bool CanRedo() const
Check if the complete redo entry matches current colors. Conflicting history is retained,...
bool InBatch() const
Check if currently in a batch operation.
bool CanUndo() const
Check if the complete undo entry matches current colors. Conflicting history is retained,...
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.
const SessionState * FindState(const zelda3::GameData *game_data) const
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.
absl::Status ApplyExternalChanges(const zelda3::GameData *owner, const std::vector< PaletteColorChange > &changes, const std::function< absl::Status()> &participant)
void EndBatch()
End a batch operation.
int RegisterChangeListener(ChangeCallback callback)
Register a callback for palette change events.
void RecordChanges(std::vector< PaletteColorChange > changes)
Helper: Record one undo step (a gesture or an entire batch).
std::unordered_map< const zelda3::GameData *, SessionState > session_states_
Per-GameData state. RomSession owns the keys and releases them on teardown.
SessionState unbound_state_
State used only before a GameData session is selected (legacy/tests).
PaletteManager(PaletteManager &&)=delete
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
PaletteManager & operator=(const PaletteManager &)=delete
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.
void CommitColorEdit(uint64_t caller_id=0)
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.
bool HasColorEdit(uint64_t caller_id=0) const
PaletteManager & operator=(PaletteManager &&)=delete
static PaletteManager & Get()
Get the singleton instance.
PaletteManager(const PaletteManager &)=delete
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
SessionState * CurrentState()
absl::Status SaveAllToRom()
Save ALL modified palettes to ROM.
absl::Status BeginSaveTransaction()
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.
bool IsSessionActive(const zelda3::GameData *game_data) const
Check whether this exact GameData is the active palette session.
void ReleaseSession(const zelda3::GameData *game_data)
Forget all palette tracking for a closing or reloaded ROM session.
SNES Color container.
Definition snes_color.h:110
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.
SnesColor new_color
New color after change.
int color_index
Index of color within palette.
std::string group_name
Palette group name (e.g., "ow_main")
int palette_index
Index of palette within group.
uint64_t timestamp_ms
Timestamp in milliseconds.
SnesColor original_color
Original color before change.
Represents a group of palettes.
std::unordered_map< std::string, std::unordered_map< int, std::unordered_set< int > > > modified_colors
std::unordered_map< std::string, std::vector< SnesPalette > > original_palettes
std::unordered_map< std::string, std::unordered_set< int > > modified_palettes
std::vector< PaletteColorChange > batch_changes
std::deque< std::vector< PaletteColorChange > > redo_stack
std::unordered_map< std::string, std::unordered_map< int, std::unordered_set< int > > > modified_colors
std::unordered_map< std::string, std::vector< SnesPalette > > original_palettes
std::optional< SaveTransactionSnapshot > save_transaction_snapshot
std::optional< PaletteColorChange > color_edit
std::deque< std::vector< PaletteColorChange > > undo_stack
std::unordered_map< std::string, std::unordered_set< int > > modified_palettes