yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
tile16_edit_session.h
Go to the documentation of this file.
1#ifndef YAZE_APP_EDITOR_OVERWORLD_TILE16_TILE16_EDIT_SESSION_H_
2#define YAZE_APP_EDITOR_OVERWORLD_TILE16_TILE16_EDIT_SESSION_H_
3
4#include <algorithm>
5#include <array>
6#include <chrono>
7#include <cstdint>
8#include <functional>
9#include <map>
10#include <memory>
11#include <optional>
12#include <string>
13#include <utility>
14#include <vector>
15
16#include "absl/status/status.h"
17#include "absl/status/statusor.h"
20#include "app/gfx/core/bitmap.h"
24#include "rom/rom.h"
28
29namespace yaze {
30namespace zelda3 {
31struct GameData;
32} // namespace zelda3
33
34namespace editor {
35
44 public:
47
50 std::array<uint8_t, 0x200>& all_tiles_types);
51
52 // Bind to the owning document before editing. Standalone sessions use their
53 // own history and read unchanged definitions from ROM.
54 void BindDocument(std::vector<gfx::Tile16>* definitions, UndoManager* history,
55 std::function<void()> before_edit) {
56 document_definitions_ = definitions;
57 history_ = history ? history : &undo_manager_;
58 before_edit_ = std::move(before_edit);
59 }
60 bool has_shared_history() const { return history_ != &undo_manager_; }
61 // Edits passing the same non-zero group merge into one Undo step while that
62 // step is still the newest history entry. Allocate one group per user
63 // interaction (for example, one property dialog session).
64 uint64_t NewEditGroup() { return ++last_edit_group_; }
65 absl::Status ReplaceCurrentTile(const gfx::Tile16& data,
66 uint64_t edit_group = 0);
67
68 // Prepare an unapplied action for an owning command. Validates the complete
69 // batch without changing definitions, pending edits, dirty state or history.
70 // Empty/no-op imports return nullptr. The session must outlive the action.
71 absl::StatusOr<std::unique_ptr<UndoAction>> PrepareDefinitionImport(
72 const std::vector<Tile16Commit>& definitions,
73 std::string description = "Import Tile16 definitions");
74 // Standalone import: apply the prepared batch and push one shared Undo step.
75 absl::Status ImportDefinitions(
76 const std::vector<Tile16Commit>& definitions,
77 std::string description = "Import Tile16 definitions");
78
79 absl::Status LoadTile8();
80 absl::Status SetCurrentTile(int id);
81 void RequestTileSwitch(int target_tile_id);
82
83 absl::Status CopyTile16ToClipboard(int tile_id);
84 absl::Status PasteTile16FromClipboard();
85 absl::Status SaveTile16ToScratchSpace(int slot);
86 absl::Status LoadTile16FromScratchSpace(int slot);
87 absl::Status ClearScratchSpace(int slot);
88
89 absl::Status FlipTile16Horizontal();
90 absl::Status FlipTile16Vertical();
91 absl::Status RotateTile16();
92 absl::Status FillTile16WithTile8(int tile8_id);
93 absl::Status ClearTile16();
94
95 absl::Status CyclePalette(bool forward = true);
96 absl::Status ApplyPaletteToAll(uint8_t palette_id);
97 absl::Status ApplyPaletteToQuadrant(int quadrant, uint8_t palette_id);
98 absl::Status PreviewPaletteChange(uint8_t palette_id);
99
100 absl::Status Undo();
101 absl::Status Redo();
102 bool CanUndo() const { return history_->CanUndo(); }
103 bool CanRedo() const { return history_->CanRedo(); }
104
105 void EnableLivePreview(bool enable) { live_preview_enabled_ = enable; }
107 absl::Status UpdateLivePreview();
108
109 absl::Status ValidateTile16Data();
110 bool IsTile16Valid(int tile_id) const;
111
112 // Legacy standalone serialization adapters. Bound editor sessions use the
113 // owning document Save and shared Undo instead. Never exposed by the UI.
114 absl::Status SaveTile16ToROM();
115 absl::Status UpdateOverworldTilemap();
116 absl::Status CommitChangesToBlockset();
117 absl::Status CommitChangesToOverworld();
118 absl::Status DiscardChanges();
119
120 bool has_pending_changes() const { return !document_edits_.empty(); }
122 return static_cast<int>(document_edits_.size());
123 }
124 bool is_tile_modified(int tile_id) const {
125 return document_edits_.find(tile_id) != document_edits_.end();
126 }
127 const gfx::Bitmap* GetPendingTileBitmap(int tile_id) const {
128 auto it = edited_tile_bitmaps_.find(tile_id);
129 return it != edited_tile_bitmaps_.end() ? &it->second : nullptr;
130 }
131 const std::map<int, gfx::Tile16>& pending_tile16_changes() const {
132 return document_edits_;
133 }
134 const std::map<int, gfx::Bitmap>& pending_tile16_bitmaps() const {
136 }
137
138 absl::Status CommitAllChanges();
139 void DiscardAllChanges();
142
143 absl::Status UpdateTile8Palette(int tile8_id);
144 absl::Status RefreshAllPalettes();
145
146 int GetActualPaletteSlot(int palette_button, int sheet_index) const;
147 int GetSheetIndexForTile8(int tile8_id) const;
150 const gfx::SnesPalette& source, int target_row) const;
151 int GetEncodedPaletteRow(uint8_t pixel_value) const;
152
153 absl::Status UpdateROMTile16Data();
154 absl::Status RefreshTile16Blockset();
156 absl::Status RegenerateTile16BitmapFromROM();
157 absl::Status UpdateBlocksetBitmap();
158 absl::Status PickTile8FromTile16(Tile16LocalPos position);
159 absl::Status DrawToCurrentTile16(Tile16LocalPos pos,
160 const gfx::Bitmap* source_tile = nullptr);
161 // Render the exact proposed stamp without changing the document/history.
162 absl::Status BuildStampPreview(Tile16LocalPos pos, gfx::Bitmap* output) const;
163 absl::Status HandleTile16CanvasClick(Tile16LocalPos tile_position,
164 bool left_click, bool right_click);
165
167 Tile16LocalPos display_position);
168
169 absl::Status SaveLayoutToScratch(int slot);
170 absl::Status LoadLayoutFromScratch(int slot);
171
172 void SetRom(Rom* rom) { rom_ = rom; }
173 Rom* rom() const { return rom_; }
176
178
180 std::function<void(const std::vector<Tile16Commit>&)> callback) {
181 on_document_changed_ = std::move(callback);
182 }
184 std::function<absl::Status(const std::vector<Tile16Commit>&)> callback) {
185 on_changes_committed_ = std::move(callback);
186 }
187 void set_on_current_tile_changed(std::function<void(int)> callback) {
188 on_current_tile_changed_ = std::move(callback);
189 }
191 void set_on_tile_selection_changed(std::function<void(int)> callback) {
192 on_tile_selection_changed_ = std::move(callback);
193 }
196 std::function<void(float width, float height)> callback) {
197 on_tile8_sheet_resized_ = std::move(callback);
198 }
199
200 int current_palette() const { return current_palette_; }
202 current_palette_ = static_cast<uint8_t>(std::clamp(palette, 0, 7));
203 }
206 int current_tile16() const { return current_tile16_; }
207 int current_tile8() const { return current_tile8_; }
208 void set_current_tile8(int id) { current_tile8_ = id; }
209 int active_quadrant() const { return active_quadrant_; }
210 void set_active_quadrant(int quadrant) {
211 active_quadrant_ = std::clamp(quadrant, 0, 3);
212 }
215
216 bool x_flip() const { return x_flip_; }
217 bool y_flip() const { return y_flip_; }
218 bool priority_tile() const { return priority_tile_; }
219 bool* mutable_x_flip() { return &x_flip_; }
220 bool* mutable_y_flip() { return &y_flip_; }
222
224 void set_map_blockset_loaded(bool loaded) { map_blockset_loaded_ = loaded; }
225
228 std::chrono::steady_clock::time_point last_rom_write_time() const {
230 }
231
232 int jump_to_tile_id() const { return jump_to_tile_id_; }
234 bool scroll_to_current() const { return scroll_to_current_; }
235 void set_scroll_to_current(bool scroll) { scroll_to_current_ = scroll; }
236 int current_page() const { return current_page_; }
237 void set_current_page(int page) { current_page_ = page; }
238 static constexpr int kTilesPerPage = 64;
239 static constexpr int kTilesPerRow = 8;
240
246 }
247
251 const std::vector<zelda3::Tile8PixelData>& current_gfx_individual() const {
253 }
254 std::vector<zelda3::Tile8PixelData>& mutable_current_gfx_individual() {
256 }
257
260 const Tile16ScratchData& scratch_slot(int i) const {
261 return scratch_space_[i];
262 }
264
265 bool auto_tile_mode() const { return auto_tile_mode_; }
267 bool grid_snap_enabled() const { return grid_snap_enabled_; }
269 bool show_tile_info() const { return show_tile_info_; }
273 bool show_tile_grid() const { return show_tile_grid_; }
277 int tile8_stamp_size() const { return tile8_stamp_size_; }
278 void set_tile8_stamp_size(int size) { tile8_stamp_size_ = size; }
288
295 }
301
302 const gfx::SnesPalette& palette() const { return palette_; }
304 return overworld_palette_;
305 }
306 const std::array<uint8_t, 0x200>& all_tiles_types() const {
307 return all_tiles_types_;
308 }
309 void SetCollisionTypes(const std::array<uint8_t, 0x200>& types) {
310 all_tiles_types_ = types;
312 }
314 all_tiles_types_.fill(0);
316 }
318
319 absl::Status RebuildTile8UsageCache();
326 }
327
329 void AnalyzeTile8SourceData() const;
330
332
333 bool HasCurrentGfxBitmap() const {
334 return current_gfx_bmp_ != nullptr && current_gfx_bmp_->is_active();
335 }
338 }
339
340 absl::Status BuildTile16BitmapFromData(const gfx::Tile16& tile_data,
341 gfx::Bitmap* output_bitmap) const;
342 void CopyTileBitmapToBlockset(int tile_id, const gfx::Bitmap& tile_bitmap);
343 void CopyTile16ToAtlas(int tile_id);
344
345 private:
346 absl::Status RunEdit(const std::function<absl::Status()>& operation,
347 uint64_t edit_group = 0);
348 absl::Status ApplyDefinitions(const std::vector<Tile16Commit>& definitions);
349 absl::Status ValidateDefinitionImport(
350 const std::vector<Tile16Commit>& definitions) const;
351 absl::StatusOr<gfx::Tile16> ReadDocumentTile(int id) const;
352 absl::Status PasteTile16FromClipboardImpl();
353 absl::Status LoadTile16FromScratchSpaceImpl(int slot);
354 absl::Status FlipTile16HorizontalImpl();
355 absl::Status FlipTile16VerticalImpl();
356 absl::Status RotateTile16Impl();
357 absl::Status FillTile16WithTile8Impl(int tile8_id);
358 absl::Status ClearTile16Impl();
359 absl::Status ApplyPaletteToAllImpl(uint8_t palette_id);
360 absl::Status ApplyPaletteToQuadrantImpl(int quadrant, uint8_t palette_id);
361 absl::StatusOr<std::vector<zelda3::Tile16StampMutation>> BuildStampMutations(
362 Tile16LocalPos pos) const;
364 const gfx::Bitmap* source_tile);
366 int target_row,
367 int tile8_id) const;
368 bool BitmapHasEncodedPaletteRows(const gfx::Bitmap& bitmap) const;
369
370 // Keep naturally aligned document/history data before small UI flags.
371 std::vector<gfx::Tile16>* document_definitions_ = nullptr;
373 uint64_t last_edit_group_ = 0;
374 Rom* rom_ = nullptr;
376 std::chrono::steady_clock::time_point last_rom_write_time_{};
380 std::map<int, gfx::Tile16> document_edits_;
381 std::map<int, gfx::Bitmap> edited_tile_bitmaps_;
382 std::vector<zelda3::Tile8PixelData> current_gfx_individual_;
383 std::function<void()> before_edit_;
384 std::function<void(const std::vector<Tile16Commit>&)> on_document_changed_;
385 std::function<absl::Status(const std::vector<Tile16Commit>&)>
387 std::function<void(int)> on_current_tile_changed_;
388 std::function<void(int)> on_tile_selection_changed_;
389 std::function<void(float, float)> on_tile8_sheet_resized_;
391 std::array<Tile16LayoutScratch, 4> layout_scratch_;
399 std::array<Tile16ScratchData, 4> scratch_space_;
412 bool x_flip_ = false;
413 bool y_flip_ = false;
414 bool priority_tile_ = false;
415 uint8_t current_palette_ = 0;
417 bool preview_dirty_ = false;
418 bool auto_tile_mode_ = false;
420 bool show_tile_info_ = true;
422 bool show_tile_grid_ = true;
430 bool scroll_to_current_ = false;
431 std::array<uint8_t, 0x200> all_tiles_types_{};
433};
434
435} // namespace editor
436} // namespace yaze
437
438#endif // YAZE_APP_EDITOR_OVERWORLD_TILE16_TILE16_EDIT_SESSION_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:52
Domain session for Tile16 editing (no ImGui).
const std::map< int, gfx::Tile16 > & pending_tile16_changes() const
absl::Status ApplyDefinitions(const std::vector< Tile16Commit > &definitions)
gfx::SnesPalette CreateRemappedPaletteForTile8(const gfx::SnesPalette &source, int target_row, int tile8_id) const
absl::Status ApplyPaletteToAllImpl(uint8_t palette_id)
int GetActualPaletteSlot(int palette_button, int sheet_index) const
gfx::SnesPalette CreateRemappedPaletteForViewing(const gfx::SnesPalette &source, int target_row) const
void set_on_current_tile_changed(std::function< void(int)> callback)
const Tile16ClipboardData & clipboard() const
std::array< Tile16ScratchData, 4 > scratch_space_
bool IsTile16Valid(int tile_id) const
absl::Status HandleTile16CanvasClick(Tile16LocalPos tile_position, bool left_click, bool right_click)
const std::vector< zelda3::Tile8PixelData > & current_gfx_individual() const
absl::Status RunEdit(const std::function< absl::Status()> &operation, uint64_t edit_group=0)
absl::Status ApplyPaletteToQuadrantImpl(int quadrant, uint8_t palette_id)
zelda3::GameData * game_data() const
absl::StatusOr< gfx::Tile16 > ReadDocumentTile(int id) const
absl::Status FillTile16WithTile8Impl(int tile8_id)
void CopyTileBitmapToBlockset(int tile_id, const gfx::Bitmap &tile_bitmap)
void set_palette(const gfx::SnesPalette &palette)
void SetCollisionTypes(const std::array< uint8_t, 0x200 > &types)
const std::array< uint8_t, 0x200 > & all_tiles_types() const
void set_on_document_changed(std::function< void(const std::vector< Tile16Commit > &)> callback)
std::map< int, gfx::Bitmap > edited_tile_bitmaps_
absl::Status CyclePalette(bool forward=true)
absl::Status ValidateDefinitionImport(const std::vector< Tile16Commit > &definitions) const
absl::Status InitializeBitmaps(gfx::Bitmap &tile16_blockset_bmp, gfx::Bitmap &current_gfx_bmp, std::array< uint8_t, 0x200 > &all_tiles_types)
const gfx::SnesPalette & overworld_palette() const
absl::Status PickTile8FromTile16(Tile16LocalPos position)
absl::Status ApplyPaletteToAll(uint8_t palette_id)
std::array< uint8_t, 0x200 > all_tiles_types_
Tile16ScratchData & scratch_slot(int i)
absl::Status BuildStampPreview(Tile16LocalPos pos, gfx::Bitmap *output) const
const gfx::Bitmap & current_tile16_bmp() const
void SetGameData(zelda3::GameData *game_data)
absl::Status DrawToCurrentTile16Impl(Tile16LocalPos pos, const gfx::Bitmap *source_tile)
std::chrono::steady_clock::time_point last_rom_write_time() const
absl::Status DrawToCurrentTile16(Tile16LocalPos pos, const gfx::Bitmap *source_tile=nullptr)
std::function< void(int)> on_tile_selection_changed_
void set_on_tile_selection_changed(std::function< void(int)> callback)
Optional: UI can sync blockset selector when current tile changes.
absl::Status LoadLayoutFromScratch(int slot)
std::function< void(int)> on_current_tile_changed_
absl::Status CopyTile16ToClipboard(int tile_id)
std::chrono::steady_clock::time_point last_rom_write_time_
absl::Status ReplaceCurrentTile(const gfx::Tile16 &data, uint64_t edit_group=0)
std::map< int, gfx::Tile16 > document_edits_
absl::Status PreviewPaletteChange(uint8_t palette_id)
absl::Status ImportDefinitions(const std::vector< Tile16Commit > &definitions, std::string description="Import Tile16 definitions")
absl::Status BuildTile16BitmapFromData(const gfx::Tile16 &tile_data, gfx::Bitmap *output_bitmap) const
Tile16LayoutScratch & layout_scratch(int i)
const std::map< int, gfx::Bitmap > & pending_tile16_bitmaps() const
absl::Status SaveLayoutToScratch(int slot)
Tile16EditSession(Rom *rom, gfx::Tilemap *tile16_blockset)
const gfx::Tile16 & current_tile16_data() const
absl::Status FillTile16WithTile8(int tile8_id)
absl::Status ApplyPaletteToQuadrant(int quadrant, uint8_t palette_id)
std::function< void(float, float)> on_tile8_sheet_resized_
absl::Status LoadTile16FromScratchSpace(int slot)
std::array< Tile16LayoutScratch, 4 > layout_scratch_
zelda3::Tile8UsageIndex tile8_usage_cache_
void RequestTileSwitch(int target_tile_id)
std::vector< zelda3::Tile8PixelData > & mutable_current_gfx_individual()
absl::Status SaveTile16ToScratchSpace(int slot)
std::function< void(const std::vector< Tile16Commit > &) on_document_changed_)
const Tile16ScratchData & scratch_slot(int i) const
void set_on_changes_committed(std::function< absl::Status(const std::vector< Tile16Commit > &)> callback)
std::function< absl::Status(const std::vector< Tile16Commit > &) on_changes_committed_)
absl::Status UpdateTile8Palette(int tile8_id)
absl::StatusOr< std::unique_ptr< UndoAction > > PrepareDefinitionImport(const std::vector< Tile16Commit > &definitions, std::string description="Import Tile16 definitions")
int GetEncodedPaletteRow(uint8_t pixel_value) const
const gfx::SnesPalette & palette() const
const gfx::SnesPalette * ResolveDisplayPalette() const
int GetSheetIndexForTile8(int tile8_id) const
static Tile16LocalPos Tile16PreviewDisplayPixelToTilePosition(Tile16LocalPos display_position)
bool BitmapHasEncodedPaletteRows(const gfx::Bitmap &bitmap) const
const gfx::Bitmap & Tile8PreviewBitmap() const
bool is_tile_modified(int tile_id) const
const zelda3::Tile8UsageIndex & tile8_usage_cache() const
void set_on_tile8_sheet_resized(std::function< void(float width, float height)> callback)
Optional: UI updates Tile8 source canvas size after LoadTile8.
absl::Status LoadTile16FromScratchSpaceImpl(int slot)
const gfx::Bitmap * GetPendingTileBitmap(int tile_id) const
std::vector< gfx::Tile16 > * document_definitions_
absl::StatusOr< std::vector< zelda3::Tile16StampMutation > > BuildStampMutations(Tile16LocalPos pos) const
std::vector< zelda3::Tile8PixelData > current_gfx_individual_
void BindDocument(std::vector< gfx::Tile16 > *definitions, UndoManager *history, std::function< void()> before_edit)
void set_edit_mode(Tile16EditMode mode)
absl::Status ClearScratchSpace(int slot)
Manages undo/redo stacks for a single editor.
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:70
bool is_active() const
Definition bitmap.h:414
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
Tile composition of four 8x8 tiles.
Definition snes_tile.h:142
std::array< std::vector< Tile8UsageHit >, kMaxTile8UsageId > Tile8UsageIndex
Tilemap structure for SNES tile-based graphics management.
Definition tilemap.h:118