yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
session_types.cc
Go to the documentation of this file.
2
3#include "zelda3/game_data.h"
4
5#include <algorithm>
6#include <filesystem>
7
11#include "app/editor/editor.h"
28
29namespace yaze::editor {
30
32 UserSettings* user_settings, size_t session_id,
33 EditorRegistry* editor_registry)
34 : session_id_(session_id),
35 rom_(rom),
36 game_data_(game_data),
37 user_settings_(user_settings),
38 editor_registry_(editor_registry),
39 gfx_group_workspace_(std::make_unique<GfxGroupWorkspaceState>()) {
40 auto register_factory = [&](EditorType type, auto&& fallback) {
41 editor_factories_[type] =
42 [this, type,
43 fallback = std::forward<decltype(fallback)>(
44 fallback)]() mutable -> std::unique_ptr<Editor> {
45 if (editor_registry_) {
46 if (auto created = editor_registry_->CreateEditor(type, rom_)) {
47 return created;
48 }
49 }
50 return fallback();
51 };
52 };
53
54 register_factory(EditorType::kAssembly,
55 [this]() { return std::make_unique<AssemblyEditor>(rom_); });
56 register_factory(EditorType::kDungeon, [this]() {
57 return std::make_unique<DungeonEditorV2>(rom_);
58 });
59 register_factory(EditorType::kGraphics,
60 [this]() { return std::make_unique<GraphicsEditor>(rom_); });
61 register_factory(EditorType::kMusic,
62 [this]() { return std::make_unique<MusicEditor>(rom_); });
63 register_factory(EditorType::kOverworld, [this]() {
64 return std::make_unique<OverworldEditor>(rom_);
65 });
66 register_factory(EditorType::kPalette,
67 [this]() { return std::make_unique<PaletteEditor>(rom_); });
68 register_factory(EditorType::kScreen,
69 [this]() { return std::make_unique<ScreenEditor>(rom_); });
70 register_factory(EditorType::kSprite,
71 [this]() { return std::make_unique<SpriteEditor>(rom_); });
72 register_factory(EditorType::kMessage,
73 [this]() { return std::make_unique<MessageEditor>(rom_); });
74 register_factory(EditorType::kHex,
75 [this]() { return std::make_unique<MemoryEditor>(rom_); });
76 register_factory(EditorType::kSettings,
77 []() { return std::make_unique<SettingsPanel>(); });
78 register_factory(EditorType::kPlanning,
79 []() { return std::make_unique<PlanningEditor>(); });
80}
81
82EditorSet::~EditorSet() = default;
83
85 user_settings_ = settings;
86 if (auto* panel = GetSettingsPanel()) {
87 panel->SetUserSettings(settings);
88 }
89}
90
92 game_data_ = game_data;
93 for (auto& [type, editor] : editors_) {
94 (void)type;
95 editor->SetGameData(game_data_);
96 }
97}
98
100 dependencies_ = dependencies;
101 for (auto& [type, editor] : editors_) {
102 editor->SetDependencies(dependencies);
103 }
104}
105
108 return;
109 }
111 for (auto& [type, editor] : editors_) {
112 (void)type;
113 editor->PrepareForSessionTeardown();
114 }
115}
116
118 return EnsureEditorCreated(type);
119}
120
122 if (type == EditorType::kUnknown) {
123 return nullptr;
124 }
125
126 if (auto it = editors_.find(type); it != editors_.end()) {
127 return it->second.get();
128 }
129 return nullptr;
130}
131
133 if (type == EditorType::kUnknown) {
134 return nullptr;
135 }
136
137 if (auto* editor = FindEditor(type)) {
138 return editor;
139 }
140
141 auto factory_it = editor_factories_.find(type);
142 if (factory_it == editor_factories_.end()) {
143 return nullptr;
144 }
145
146 auto editor = factory_it->second();
147 if (!editor) {
148 return nullptr;
149 }
150
151 if (dependencies_.has_value()) {
153 }
154 if (game_data_ != nullptr) {
155 editor->SetGameData(game_data_);
156 }
157 if (auto* dungeon = dynamic_cast<DungeonEditorV2*>(editor.get())) {
158 dungeon->SetRoomGraphicsOverworldProvider(
159 [this]() { return GetOverworldData(); });
160 }
161 auto main_usage =
162 [this](int group,
163 int slot) -> absl::StatusOr<zelda3::MainGroupUsageReport> {
165 return absl::FailedPreconditionError("No loaded graphics session");
166 }
167 auto rooms = zelda3::CollectRoomGfx(*rom_);
168 std::map<int, uint8_t> explicit_main;
169 if (const auto* dungeon = dynamic_cast<const DungeonEditorV2*>(
171 dungeon->rooms().ForEachLoaded([&](int id, const zelda3::Room& room) {
172 rooms[id] = {room.blockset(), room.spriteset()};
173 explicit_main[id] = room.render_entrance_blockset();
174 });
175 }
176 return zelda3::DescribeMainGroupUsage(*rom_, *game_data_, group, slot,
177 rooms, explicit_main,
179 };
180 auto roomset_usage =
181 [this](int group) -> absl::StatusOr<zelda3::RoomsetUsageReport> {
183 return absl::FailedPreconditionError("No loaded graphics session");
184 if (const auto* dungeon = dynamic_cast<const DungeonEditorV2*>(
186 return dungeon->GetRoomGraphicsUsage(group);
190 };
191 if (auto* graphics = dynamic_cast<GraphicsEditor*>(editor.get())) {
192 graphics->SetRoomUsageProvider([this]() {
195 return contexts;
196 if (auto* dungeon = static_cast<DungeonEditorV2*>(
198 dungeon->VisitGraphicsUsageRooms([&](int id, zelda3::Room& room,
199 const std::string& provenance,
200 bool current, int entrance_slot) {
201 contexts.emplace(id, usage_preview::LiveRoomContext{
202 &room, provenance, current, entrance_slot});
203 });
204 }
205 return contexts;
206 });
207 graphics->SetOverworldUsageProvider([this]() -> zelda3::Overworld* {
209 return nullptr;
210 auto* overworld = GetOverworldData();
211 return overworld && overworld->is_loaded() && overworld->rom() == rom_
212 ? overworld
213 : nullptr;
214 });
215 graphics->SetMainGraphicsUsageCallback(main_usage);
216 graphics->SetRoomsetGraphicsUsageCallback(roomset_usage);
217 }
218 if (auto* overworld = dynamic_cast<OverworldEditor*>(editor.get())) {
219 overworld->SetMainGraphicsUsageCallback(main_usage);
220 overworld->SetRoomsetGraphicsUsageCallback(roomset_usage);
221 }
222 if (type == EditorType::kSettings && user_settings_ != nullptr) {
223 if (auto* settings_panel = static_cast<SettingsPanel*>(editor.get())) {
224 settings_panel->SetUserSettings(user_settings_);
225 }
226 }
227
228 Editor* editor_ptr = editor.get();
229 editors_[type] = std::move(editor);
230
231 if (ShouldTrackAsActiveEditor(type)) {
232 const_cast<EditorSet*>(this)->TrackActiveEditor(editor_ptr);
233 }
234
235 return editor_ptr;
236}
237
239 switch (type) {
250 return true;
251 default:
252 return false;
253 }
254}
255
257 if (editor == nullptr) {
258 return;
259 }
260 if (std::find(active_editors_.begin(), active_editors_.end(), editor) ==
261 active_editors_.end()) {
262 active_editors_.push_back(editor);
263 }
264}
265
266void EditorSet::OpenAssemblyFolder(const std::string& folder_path) const {
267 if (auto* editor = GetAssemblyEditor()) {
268 editor->OpenFolder(folder_path);
269 }
270}
271
272void EditorSet::ChangeActiveAssemblyFile(std::string_view path) const {
273 if (auto* editor = GetAssemblyEditor()) {
274 editor->ChangeActiveFile(path);
275 }
276}
277
279 if (auto* editor =
281 return editor->asar_wrapper();
282 }
283 return nullptr;
284}
285
286const std::map<std::string, core::AsarSymbol>& EditorSet::GetAssemblySymbols()
287 const {
288 static const std::map<std::string, core::AsarSymbol> kEmpty;
289 if (auto* editor =
291 return editor->symbols();
292 }
293 return kEmpty;
294}
295
297 if (auto* editor =
299 return editor->LoadedRoomCount();
300 }
301 return 0;
302}
303
305 if (auto* editor =
307 return editor->TotalRoomCount();
308 }
309 return 0;
310}
311
312std::vector<std::pair<uint32_t, uint32_t>>
314 if (auto* editor =
316 return editor->CollectWriteRanges();
317 }
318 return {};
319}
320
322 if (!game_data_loaded || !rom.is_loaded()) {
323 return false;
324 }
325 auto diff = zelda3::DiffGfxGroups(rom, game_data);
326 return diff.ok() && diff->any();
327}
328
330 if (auto* editor =
332 return editor->HasPendingGraphicsChanges();
333 }
334 return false;
335}
336
338 if (auto* editor =
340 return editor->HasPendingSpriteChanges();
341 return false;
342}
343
345 if (auto* editor =
347 return editor->HasPendingScreenChanges();
348 }
349 return false;
350}
351
353 if (auto* editor =
355 editor->InvalidateRomBackedState();
356 }
357}
358
363
366 return true;
367 if (auto* editor =
369 if (editor->HasPendingProjectDraftChanges())
370 return true;
371 }
372 if (auto* editor =
374 return editor->HasPendingProjectDraftChanges();
375 }
376 return false;
377}
378
380 auto* editor =
382 return editor && editor->HasPendingProjectDraftChanges();
383}
384
386 auto* editor =
388 return editor ? editor->Save() : absl::OkStatus();
389}
390
391absl::Status EditorSet::PrepareProjectSave(bool publish_lyra) {
392 if (auto* editor =
394 RETURN_IF_ERROR(editor->PrepareProjectSave());
395 if (auto* editor =
397 RETURN_IF_ERROR(editor->PrepareProjectSave());
398 }
399 if (publish_lyra) {
400 if (auto* editor =
402 RETURN_IF_ERROR(editor->PrepareProjectSave());
403 }
404 }
405 if (auto* editor =
407 RETURN_IF_ERROR(editor->Save());
408 }
409 return absl::OkStatus();
410}
411
413 if (auto* editor =
415 editor->ProjectSaveCommitted();
416 if (auto* editor =
418 editor->ProjectSaveCommitted();
419 }
420}
421
423 if (auto* editor =
425 editor->DiscardProjectDraftChanges();
426 }
427}
428
430 if (auto* editor =
432 return &editor->overworld();
433 }
434 return nullptr;
435}
436
437// Deprecated named accessors
439 return GetEditorAs<AssemblyEditor>(EditorType::kAssembly);
440}
442 return GetEditorAs<DungeonEditorV2>(EditorType::kDungeon);
443}
445 return GetEditorAs<GraphicsEditor>(EditorType::kGraphics);
446}
448 return GetEditorAs<MusicEditor>(EditorType::kMusic);
449}
451 return GetEditorAs<OverworldEditor>(EditorType::kOverworld);
452}
454 return GetEditorAs<PaletteEditor>(EditorType::kPalette);
455}
457 return GetEditorAs<ScreenEditor>(EditorType::kScreen);
458}
460 return GetEditorAs<SpriteEditor>(EditorType::kSprite);
461}
463 return GetEditorAs<SettingsPanel>(EditorType::kSettings);
464}
466 return GetEditorAs<MessageEditor>(EditorType::kMessage);
467}
469 return GetEditorAs<MemoryEditor>(EditorType::kHex);
470}
471
472RomSession::RomSession(UserSettings* user_settings, size_t session_id,
473 EditorRegistry* editor_registry)
474 : game_data(&rom),
475 editors(&rom, &game_data, user_settings, session_id, editor_registry) {}
476
477RomSession::RomSession(Rom&& r, UserSettings* user_settings, size_t session_id,
478 EditorRegistry* editor_registry)
479 : rom(std::move(r)),
480 game_data(&rom),
481 editors(&rom, &game_data, user_settings, session_id, editor_registry) {
485}
486
487std::string ComputeRomFileBytesSha256(const Rom& rom) {
488 std::string bytes(reinterpret_cast<const char*>(rom.copier_header().data()),
489 rom.copier_header().size());
490 bytes.append(reinterpret_cast<const char*>(rom.data()), rom.size());
492}
493
494void RomSession::CaptureRomIdentity(bool preserve_editing_source) {
495 if (!rom.is_loaded()) {
496 rom_baseline.reset();
497 return;
498 }
499 auto path = std::filesystem::path(rom.filename()).lexically_normal();
500 path.make_preferred();
501 const std::string editing_source = preserve_editing_source && rom_baseline
502 ? rom_baseline->editing_source_path
503 : path.string();
505 util::ComputeRomIdentityHashes(rom.data(), rom.size()), path.string(),
506 editing_source, rom.dirty() ? "" : ComputeRomFileBytesSha256(rom)};
507}
508
512 std::make_unique<std::array<gfx::Bitmap, zelda3::kNumGfxSheets>>(
514 // Rehydration always reads the authoring store, including unsaved pixels.
515 for (uint16_t i = 0; i < graphics_display_->size(); ++i) {
516 auto& bitmap = graphics_display_->at(i);
517 if (!bitmap.is_active() || !game_data.sheet_store.HasSheet(i))
518 continue;
519 const auto pixels = game_data.sheet_store.Sheet(i);
520 bitmap.set_data(std::vector<uint8_t>(pixels.begin(), pixels.end()));
521 }
523}
524
528
530 auto& arena = gfx::Arena::Get();
531 if (arena.gfx_sheets_owner() == &game_data) {
532 arena.BindGraphicsSheets(nullptr, nullptr);
533 }
534 if (graphics_display_) {
535 for (auto& bitmap : *graphics_display_)
536 arena.RetireBitmap(bitmap);
537 graphics_display_.reset();
538 }
539}
540
545
546std::string RomSession::GetDisplayName() const {
547 if (!custom_name.empty()) {
548 return custom_name;
549 }
550 return rom.title().empty() ? "Untitled Session" : rom.title();
551}
552
553} // namespace yaze::editor
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
auto filename() const
Definition rom.h:219
const std::vector< uint8_t > & copier_header() const
Definition rom.h:226
auto data() const
Definition rom.h:213
auto size() const
Definition rom.h:212
bool dirty() const
Definition rom.h:200
bool is_loaded() const
Definition rom.h:199
auto title() const
Definition rom.h:211
Modern C++ wrapper for Asar 65816 assembler integration.
Text editor for modifying assembly code.
DungeonEditorV2 - Simplified dungeon editor using component delegation.
Manages editor types, categories, and lifecycle.
std::unique_ptr< Editor > CreateEditor(EditorType type, Rom *rom) const
Contains a complete set of editors for a single ROM instance.
std::optional< EditorDependencies > dependencies_
MemoryEditor * GetMemoryEditor() const
std::unordered_map< EditorType, std::unique_ptr< Editor > > editors_
bool HasPendingSpriteChanges() const
DungeonEditorV2 * GetDungeonEditor() const
void OpenAssemblyFolder(const std::string &folder_path) const
MusicEditor * GetMusicEditor() const
ScreenEditor * GetScreenEditor() const
void SetGameData(zelda3::GameData *game_data)
bool HasPendingProjectDraftChanges() const
Editor * GetExistingEditor(EditorType type) const
bool HasPendingPlanningProjectDraftChanges() const
bool ShouldTrackAsActiveEditor(EditorType type) const
core::AsarWrapper * GetAsarWrapper() const
bool HasPendingBuildProjectDraftChanges() const
void TrackActiveEditor(Editor *editor)
absl::Status PrepareProjectSave(bool publish_lyra=true)
void ChangeActiveAssemblyFile(std::string_view path) const
void ApplyDependencies(const EditorDependencies &dependencies)
const std::map< std::string, core::AsarSymbol > & GetAssemblySymbols() const
EditorSet(Rom *rom=nullptr, zelda3::GameData *game_data=nullptr, UserSettings *user_settings=nullptr, size_t session_id=0, EditorRegistry *editor_registry=nullptr)
Editor * EnsureEditorCreated(EditorType type) const
SpriteEditor * GetSpriteEditor() const
UserSettings * user_settings_
Editor * GetEditor(EditorType type) const
GraphicsEditor * GetGraphicsEditor() const
int LoadedDungeonRoomCount() const
Editor * FindEditor(EditorType type) const
PaletteEditor * GetPaletteEditor() const
bool HasPendingGraphicsChanges() const
bool HasPendingScreenChanges() const
std::unordered_map< EditorType, EditorFactory > editor_factories_
EditorRegistry * editor_registry_
void set_user_settings(UserSettings *settings)
zelda3::Overworld * GetOverworldData() const
MessageEditor * GetMessageEditor() const
int TotalDungeonRoomCount() const
SettingsPanel * GetSettingsPanel() const
absl::Status SavePlanningProjectDraft()
std::vector< Editor * > active_editors_
zelda3::GameData * game_data_
OverworldEditor * GetOverworldEditor() const
AssemblyEditor * GetAssemblyEditor() const
std::vector< std::pair< uint32_t, uint32_t > > CollectDungeonWriteRanges() const
Interface for editor classes.
Definition editor.h:252
virtual void SetDependencies(const EditorDependencies &deps)
Definition editor.h:257
Allows the user to edit graphics sheets from the game or view prototype graphics.
A class for editing music data in a Rom.
Main UI class for editing overworld maps in A Link to the Past.
Allows the user to view and edit in game palettes.
absl::Status Save() override
bool HasPendingProjectDraftChanges() const
The ScreenEditor class allows the user to edit a variety of screens in the game or create a custom me...
Manages the settings UI displayed in the right sidebar.
Allows the user to edit sprites.
Manages user preferences and settings persistence.
void BindGraphicsSheets(GraphicsSheets *sheets, const void *owner)
Definition arena.cc:522
static Arena & Get()
Definition arena.cc:24
static PaletteManager & Get()
Get the singleton instance.
void ReleaseSession(const zelda3::GameData *game_data)
Forget all palette tracking for a closing or reloaded ROM session.
bool HasSheet(uint16_t sheet) const
True when the buffer holds all 4096 bytes of sheet.
absl::Span< const uint8_t > Sheet(uint16_t sheet) const
The sheet's 4096 pixels, or an empty span when it is not held.
uint8_t blockset() const
Definition room.h:1015
uint8_t spriteset() const
Definition room.h:1018
uint8_t render_entrance_blockset() const
Definition room.h:1016
std::string ComputeSourceArtifactSha256(std::string_view content)
std::map< int, LiveRoomContext > LiveRoomContexts
Editors are the view controllers for the application.
std::string ComputeRomFileBytesSha256(const Rom &rom)
RomIdentityHashes ComputeRomIdentityHashes(const uint8_t *data, size_t size)
Definition rom_hash.cc:85
absl::StatusOr< RoomsetUsageReport > DescribeRoomsetUsage(Rom &rom, GameData &game_data, int roomset, const std::map< int, RoomGfxInfo > &rooms, const Overworld *live_overworld)
absl::StatusOr< MainGroupUsageReport > DescribeMainGroupUsage(Rom &rom, GameData &game_data, int group, int slot, const std::map< int, RoomGfxInfo > &rooms, const std::map< int, uint8_t > &explicit_room_main, const Overworld *live_overworld)
absl::StatusOr< GfxGroupDiff > DiffGfxGroups(const Rom &rom, const GameData &data)
Definition game_data.cc:334
std::map< int, RoomGfxInfo > CollectRoomGfx(const Rom &rom)
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22
Unified dependency container for all editor types.
Definition editor.h:170
Per-ROM-session UI state shared by all GfxGroupEditor surfaces.
std::unique_ptr< std::array< gfx::Bitmap, zelda3::kNumGfxSheets > > graphics_display_
core::FeatureFlags::Flags feature_flags
void CaptureRomIdentity(bool preserve_editing_source=false)
bool HasPendingGfxGroupChanges() const
std::optional< RomBaselineIdentity > rom_baseline
zelda3::GameData game_data
std::string GetDisplayName() const
std::array< gfx::Bitmap, kNumGfxSheets > gfx_bitmaps
Definition game_data.h:90