14#include "absl/status/status.h"
15#include "absl/strings/str_format.h"
16#include "absl/strings/string_view.h"
17#include "absl/types/span.h"
62#include "imgui/imgui.h"
83 return absl::FailedPreconditionError(
"ROM not loaded");
86 bool any_dirty =
false;
93 return absl::OkStatus();
96 std::vector<zelda3::WaterFillZoneEntry> zones;
98 for (
int room_id = 0; room_id < static_cast<int>(rooms.
size()); ++room_id) {
100 if (room ==
nullptr) {
108 if (tile_count <= 0) {
111 if (tile_count > 255) {
112 return absl::InvalidArgumentError(absl::StrFormat(
113 "Water fill zone in room 0x%02X has %d tiles (max 255)", room_id,
120 z.
fill_offsets.reserve(
static_cast<size_t>(tile_count));
123 for (
size_t i = 0; i < map.size(); ++i) {
128 zones.push_back(std::move(z));
131 if (zones.size() > 8) {
132 return absl::InvalidArgumentError(absl::StrFormat(
133 "Too many water fill zones: %zu (max 8 fits in $7EF411 bitfield)",
140 for (
const auto& z : zones) {
150 return absl::OkStatus();
156 std::vector<std::pair<uint32_t, uint32_t>> ranges;
157 if (rom ==
nullptr || !rom->
is_loaded()) {
161 bool any_dirty =
false;
174 if (layout !=
nullptr &&
176 for (
const auto& range : layout->allocation_regions) {
180 const uint32_t pointer_width =
184 const uint32_t pointer_table_pc =
yaze::SnesToPc(layout->pointer_table);
187 pointer_table_pc + layout->pointer_count * pointer_width);
192 const auto& rom_data = rom->
vector();
198 if (pointer_slot < 0 ||
199 pointer_slot + 1 >=
static_cast<int>(rom_data.size())) {
202 const uint16_t pointer =
203 (
static_cast<uint16_t
>(rom_data[pointer_slot + 1]) << 8) |
204 rom_data[pointer_slot];
205 if (pointer < 0x8000) {
209 const uint32_t encoded_size =
210 static_cast<uint32_t
>(room.
GetPotItems().size() * 3 + 2);
211 ranges.emplace_back(stream_pc, stream_pc + encoded_size);
216absl::StatusOr<std::vector<std::pair<uint32_t, uint32_t>>>
218 bool any_dirty =
false;
225 return std::vector<std::pair<uint32_t, uint32_t>>{};
232 std::optional<zelda3::DungeonStreamLayout> repack_layout;
233 bool any_dirty =
false;
242 if (manifest_layout !=
nullptr &&
248 repack_layout = std::move(allocator_layout);
253 rom,
static_cast<int>(rooms.
size()),
254 [&rooms](
int room_id) { return rooms.GetIfMaterialized(room_id); },
255 repack_layout.has_value() ? &*repack_layout :
nullptr);
263 return absl::OkStatus();
266 if (ranges.empty()) {
267 return absl::OkStatus();
271 save_scope,
"DungeonEditorV2", toast_manager);
276 const std::array<zelda3::RoomEntrance, zelda3::kNumDungeonEntranceSlots>&
278 const std::array<zelda3::DungeonSpawnPoint, zelda3::kNumDungeonSpawnPoints>&
280 absl::string_view save_scope,
ToastManager* toast_manager) {
281 if (rom ==
nullptr || !rom->
is_loaded()) {
282 return absl::FailedPreconditionError(
"ROM not loaded");
294 return absl::OkStatus();
300 ranges.insert(ranges.end(), spawn_ranges.begin(), spawn_ranges.end());
301 if (ranges.empty()) {
302 return absl::OkStatus();
306 save_scope,
"DungeonEditorV2", toast_manager);
312 absl::string_view save_scope,
315 if (ranges.empty() || project ==
nullptr ||
317 return absl::OkStatus();
321 save_scope,
"DungeonEditorV2", toast_manager);
328 return absl::FailedPreconditionError(
329 "Dungeon save transaction is already active");
335 palette_manager.HasUnsavedChanges(
game_data_)) {
336 if (!palette_manager.IsManaging(
game_data_)) {
337 return absl::FailedPreconditionError(
338 "Cannot save dungeon palettes from a different ROM session");
347 for (
size_t i = 0; i <
entrances_.size(); ++i) {
358 return absl::OkStatus();
368 room->RestoreSaveDirtySnapshot(state);
371 for (
size_t i = 0; i <
entrances_.size(); ++i) {
408 const std::function<absl::Status()>& operation) {
410 return absl::FailedPreconditionError(
"ROM not loaded");
422 const absl::Status status = operation();
430 return absl::OkStatus();
435 return absl::FailedPreconditionError(
"ROM not loaded");
440 if (flags.kSaveEntrances) {
446 if (flags.kSaveChests) {
451 if (flags.kSavePotItems) {
458 if (flags.kSavePalettes && palette_manager.HasUnsavedChanges(
game_data_)) {
459 if (!palette_manager.IsManaging(
game_data_)) {
460 return absl::FailedPreconditionError(
461 "Cannot save dungeon palettes from a different ROM session");
463 const size_t modified_color_count =
464 palette_manager.GetModifiedColorCount(
game_data_);
465 auto status = palette_manager.SaveAllToRom();
467 LOG_ERROR(
"DungeonEditorV2",
"Failed to save palette changes: %s",
468 status.message().data());
471 LOG_INFO(
"DungeonEditorV2",
"Saved %zu modified colors to ROM",
472 modified_color_count);
475 if (flags.kSaveObjects || flags.kSaveSprites || flags.kSaveRoomHeaders) {
476 absl::Status save_status = absl::OkStatus();
478 if (!save_status.ok()) {
483 save_status = status;
486 if (!save_status.ok()) {
491 if (flags.kSaveTorches) {
494 [
this](
int room_id) { return rooms_.GetIfMaterialized(room_id); });
496 LOG_ERROR(
"DungeonEditorV2",
"Failed to save torches: %s",
497 status.message().data());
502 if (flags.kSavePits) {
506 LOG_ERROR(
"DungeonEditorV2",
"Failed to save pits: %s",
507 status.message().data());
512 if (flags.kSaveBlocks) {
515 [
this](
int room_id) { return rooms_.GetIfMaterialized(room_id); });
517 LOG_ERROR(
"DungeonEditorV2",
"Failed to save blocks: %s",
518 status.message().data());
523 if (flags.kSaveCollision) {
526 [
this](
int room_id) { return rooms_.GetIfMaterialized(room_id); });
528 LOG_ERROR(
"DungeonEditorV2",
"Failed to save collision: %s",
529 status.message().data());
534 if (flags.kSaveWaterFillZones) {
535 auto status = SaveWaterFillZones(
rom_,
rooms_);
537 LOG_ERROR(
"DungeonEditorV2",
"Failed to save water fill zones: %s",
538 status.message().data());
543 if (flags.kSaveChests) {
546 [
this](
int room_id) { return rooms_.GetIfMaterialized(room_id); });
548 LOG_ERROR(
"DungeonEditorV2",
"Failed to save chests: %s",
549 status.message().data());
554 if (flags.kSavePotItems) {
558 LOG_ERROR(
"DungeonEditorV2",
"Failed to save pot items: %s",
559 status.message().data());
564 if (flags.kSaveEntrances) {
567 LOG_ERROR(
"DungeonEditorV2",
"Failed to save dungeon entrances: %s",
568 status.message().data());
573 LOG_ERROR(
"DungeonEditorV2",
"Failed to save dungeon spawn points: %s",
574 status.message().data());
579 return absl::OkStatus();
584 std::vector<std::pair<uint32_t, uint32_t>> ranges;
594 if (flags.kSavePalettes) {
595 auto palette_ranges =
596 palette_manager.GetModifiedColorWriteRanges(
game_data_);
597 ranges.insert(ranges.end(), palette_ranges.begin(), palette_ranges.end());
600 if (flags.kSaveEntrances) {
601 auto entrance_ranges =
603 ranges.insert(ranges.end(), entrance_ranges.begin(), entrance_ranges.end());
606 ranges.insert(ranges.end(), spawn_ranges.begin(), spawn_ranges.end());
610 bool stream_dirty,
int room_id) {
618 if (layout ==
nullptr ||
621 static_cast<uint32_t
>(room_id) >= layout->pointer_count) {
625 for (
const auto& range : layout->allocation_regions) {
629 const uint32_t pointer_width =
632 const uint32_t pointer_slot =
634 static_cast<uint32_t
>(room_id) * pointer_width;
635 ranges.emplace_back(pointer_slot, pointer_slot + pointer_width);
637 const uint32_t door_slot =
639 ranges.emplace_back(door_slot, door_slot + 3u);
647 if (flags.kSaveWaterFillZones &&
649 bool any_dirty =
false;
664 if (flags.kSaveCollision) {
666 const bool has_ptr_table =
668 static_cast<int>(rom_data.size()));
670 static_cast<int>(rom_data.size()));
671 if (has_ptr_table && has_data_region) {
672 bool any_dirty =
false;
690 if (flags.kSaveChests) {
691 auto chest_ranges = CollectDirtyChestWriteRanges(
rom_,
rooms_);
692 if (chest_ranges.ok()) {
693 ranges.insert(ranges.end(), chest_ranges->begin(), chest_ranges->end());
695 LOG_WARN(
"DungeonEditorV2",
"Could not predict chest write ranges: %s",
696 chest_ranges.status().message().data());
705 if (zelda3::kRoomHeaderPointer + 2 < static_cast<int>(rom_data.size())) {
706 int header_ptr_table =
707 (rom_data[zelda3::kRoomHeaderPointer + 2] << 16) |
708 (rom_data[zelda3::kRoomHeaderPointer + 1] << 8) |
709 rom_data[zelda3::kRoomHeaderPointer];
710 header_ptr_table = yaze::SnesToPc(header_ptr_table);
711 int table_offset = header_ptr_table + (room_id * 2);
713 if (table_offset + 1 < static_cast<int>(rom_data.size())) {
714 int address = (rom_data[zelda3::kRoomHeaderPointerBank] << 16) |
715 (rom_data[table_offset + 1] << 8) |
716 rom_data[table_offset];
717 int header_location = yaze::SnesToPc(address);
718 ranges.emplace_back(header_location, header_location + 14);
722 if (message_address >= 0 &&
723 message_address + 1 <
static_cast<int>(rom_data.size())) {
724 ranges.emplace_back(message_address, message_address + 2);
730 const uint32_t door_slot =
732 ranges.emplace_back(door_slot, door_slot + 3u);
738 int entry_offset = obj_ptr_table + (room_id * 3);
740 if (entry_offset + 2 <
static_cast<int>(rom_data.size())) {
741 int tile_addr = (rom_data[entry_offset + 2] << 16) |
742 (rom_data[entry_offset + 1] << 8) |
743 rom_data[entry_offset];
747 ranges.emplace_back(objects_location,
748 objects_location + encoded.size() + 2);
755 const uint32_t table_snes =
761 if ((table_snes & 0xFFFFu) >= 0x8000u) {
763 const int pointer_slot = table_pc + room_id * 3;
764 if (pointer_slot >= 0 &&
765 pointer_slot + 2 <
static_cast<int>(rom_data.size())) {
766 const uint32_t stream_snes =
767 (
static_cast<uint32_t
>(rom_data[pointer_slot + 2]) << 16) |
768 (
static_cast<uint32_t
>(rom_data[pointer_slot + 1]) << 8) |
769 rom_data[pointer_slot];
770 if ((stream_snes & 0xFFFFu) >= 0x8000u) {
772 if (
static_cast<uint64_t
>(stream_pc) + 2u <= rom_data.size()) {
773 ranges.emplace_back(stream_pc, stream_pc + 2u);
783 const uint32_t table_snes =
789 const int pointer_slot = table_pc + room_id * 2;
790 if (pointer_slot >= 0 &&
791 pointer_slot + 1 <
static_cast<int>(rom_data.size())) {
792 const uint16_t pointer =
793 (
static_cast<uint16_t
>(rom_data[pointer_slot + 1]) << 8) |
794 rom_data[pointer_slot];
795 if (pointer >= 0x8000) {
797 ranges.emplace_back(stream_pc,
803 append_declared_cow_ranges(
805 flags.kSaveObjects &&
815 if (flags.kSavePotItems) {
817 CollectDirtyPotItemWriteRanges(dependencies_.project, rom_, rooms_);
818 ranges.insert(ranges.end(), pot_ranges.begin(), pot_ranges.end());
824absl::Status DungeonEditorV2::SaveRoom(
int room_id) {
825 return RunWithSaveTransaction([
this, room_id]() -> absl::Status {
826 if (room_id < 0 || room_id >=
static_cast<int>(rooms_.size())) {
827 return absl::InvalidArgumentError(
"Invalid room ID");
831 if (flags.kSaveEntrances) {
833 dependencies_.project, rom_, entrances_, spawn_points_,
835 "dungeon entrances and spawn points (Apply Room 0x%03X)",
837 dependencies_.toast_manager));
839 if (flags.kSaveChests) {
841 dependencies_.project, rom_, rooms_,
842 absl::StrFormat(
"chest table for room 0x%03X", room_id),
843 dependencies_.toast_manager));
845 if (flags.kSavePotItems) {
847 dependencies_.project, rom_, rooms_,
848 absl::StrFormat(
"pot items for room 0x%03X", room_id),
849 dependencies_.toast_manager));
852 if (flags.kSavePalettes && palette_manager.HasUnsavedChanges(game_data_)) {
853 if (!palette_manager.IsManaging(game_data_)) {
854 return absl::FailedPreconditionError(
855 "Cannot save dungeon palettes from a different ROM session");
857 auto status = palette_manager.SaveAllToRom();
859 LOG_ERROR(
"DungeonEditorV2",
"Failed to save palette changes: %s",
860 status.message().data());
864 if (flags.kSaveObjects || flags.kSaveSprites || flags.kSaveRoomHeaders) {
868 if (flags.kSaveTorches) {
870 rom_,
static_cast<int>(rooms_.size()),
871 [
this](
int id) { return rooms_.GetIfMaterialized(id); }));
873 if (flags.kSavePits) {
875 rom_, game_data_ ? &game_data_->pit_damage_table :
nullptr));
877 if (flags.kSaveBlocks) {
879 rom_,
static_cast<int>(rooms_.size()),
880 [
this](
int id) { return rooms_.GetIfMaterialized(id); }));
882 if (flags.kSaveCollision) {
884 rom_,
static_cast<int>(rooms_.size()),
885 [
this](
int id) { return rooms_.GetIfMaterialized(id); }));
887 if (flags.kSaveWaterFillZones) {
890 if (flags.kSaveChests) {
892 rom_,
static_cast<int>(rooms_.size()),
893 [
this](
int id) { return rooms_.GetIfMaterialized(id); }));
895 if (flags.kSavePotItems) {
897 SaveAllPotItemsForProject(dependencies_.project, rom_, rooms_));
899 if (flags.kSaveEntrances) {
904 return absl::OkStatus();
908absl::Status DungeonEditorV2::SaveRoomData(
int room_id) {
909 if (!rom_ || !rom_->is_loaded()) {
910 return absl::FailedPreconditionError(
"ROM not loaded");
912 if (room_id < 0 || room_id >=
static_cast<int>(rooms_.size())) {
913 return absl::InvalidArgumentError(
"Invalid room ID");
916 auto& room = rooms_[room_id];
917 if (!room.IsLoaded()) {
918 return absl::OkStatus();
925 for (
const auto& w : result.warnings) {
926 LOG_WARN(
"DungeonEditorV2",
"Room 0x%03X validation warning: %s", room_id,
929 if (!result.is_valid) {
930 for (
const auto& e : result.errors) {
931 LOG_ERROR(
"DungeonEditorV2",
"Room 0x%03X validation error: %s",
934 if (dependencies_.toast_manager) {
935 dependencies_.toast_manager->Show(
937 "Save blocked: room 0x%03X failed validation (%zu error(s))",
938 room_id, result.errors.size()),
941 return absl::FailedPreconditionError(
942 absl::StrFormat(
"Room 0x%03X failed validation", room_id));
951 std::optional<zelda3::DungeonStreamLayout> object_cow_layout;
952 std::optional<zelda3::DungeonStreamLayout> sprite_cow_layout;
953 if (dependencies_.project && dependencies_.project->hack_manifest.loaded()) {
954 const auto& manifest = dependencies_.project->hack_manifest;
955 auto load_cow_layout =
957 std::optional<zelda3::DungeonStreamLayout>* out) -> absl::Status {
959 return absl::OkStatus();
961 const auto* manifest_layout =
962 manifest.GetDungeonStreamLayout(stream_type);
963 if (manifest_layout ==
nullptr ||
964 manifest_layout->strategy !=
966 return absl::OkStatus();
971 *out = std::move(allocator_layout);
972 return absl::OkStatus();
977 flags.kSaveObjects &&
978 (room.object_stream_dirty() || room.object_stream_header_dirty()),
979 &object_cow_layout));
981 flags.kSaveSprites && room.sprites_dirty(),
982 &sprite_cow_layout));
986 if (dependencies_.project && dependencies_.project->hack_manifest.loaded()) {
987 std::vector<std::pair<uint32_t, uint32_t>> ranges;
988 const auto& manifest = dependencies_.project->hack_manifest;
989 const auto& rom_data = rom_->vector();
992 if (flags.kSaveRoomHeaders && room.header_dirty()) {
994 int header_ptr_table =
999 int table_offset = header_ptr_table + (room_id * 2);
1001 if (table_offset + 1 <
static_cast<int>(rom_data.size())) {
1003 (rom_data[table_offset + 1] << 8) |
1004 rom_data[table_offset];
1006 ranges.emplace_back(header_location, header_location + 14);
1010 if (message_address >= 0 &&
1011 message_address + 1 <
static_cast<int>(rom_data.size())) {
1012 ranges.emplace_back(message_address, message_address + 2);
1017 if (flags.kSaveObjects && room.object_stream_dirty()) {
1018 const uint32_t door_slot =
1020 ranges.emplace_back(door_slot, door_slot + 3u);
1026 int entry_offset = obj_ptr_table + (room_id * 3);
1028 if (entry_offset + 2 <
static_cast<int>(rom_data.size())) {
1029 int tile_addr = (rom_data[entry_offset + 2] << 16) |
1030 (rom_data[entry_offset + 1] << 8) |
1031 rom_data[entry_offset];
1038 auto encoded = room.EncodeObjects();
1039 ranges.emplace_back(objects_location,
1040 objects_location + encoded.size() + 2);
1045 if (flags.kSaveObjects && room.object_stream_header_dirty() &&
1047 const uint32_t table_snes =
1053 if ((table_snes & 0xFFFFu) >= 0x8000u) {
1055 const int pointer_slot = table_pc + room_id * 3;
1056 if (pointer_slot >= 0 &&
1057 pointer_slot + 2 <
static_cast<int>(rom_data.size())) {
1058 const uint32_t stream_snes =
1059 (
static_cast<uint32_t
>(rom_data[pointer_slot + 2]) << 16) |
1060 (
static_cast<uint32_t
>(rom_data[pointer_slot + 1]) << 8) |
1061 rom_data[pointer_slot];
1062 if ((stream_snes & 0xFFFFu) >= 0x8000u) {
1064 if (
static_cast<uint64_t
>(stream_pc) + 2u <= rom_data.size()) {
1065 ranges.emplace_back(stream_pc, stream_pc + 2u);
1073 if (flags.kSaveSprites && room.sprites_dirty() &&
1075 const uint32_t table_snes =
1081 const int pointer_slot = table_pc + room_id * 2;
1082 if (pointer_slot >= 0 &&
1083 pointer_slot + 1 <
static_cast<int>(rom_data.size())) {
1084 const uint16_t pointer =
1085 (
static_cast<uint16_t
>(rom_data[pointer_slot + 1]) << 8) |
1086 rom_data[pointer_slot];
1087 if (pointer >= 0x8000) {
1089 ranges.emplace_back(stream_pc,
1090 stream_pc + room.EncodeSprites().size() + 1);
1100 for (
const auto& range : layout.allocation_ranges) {
1101 ranges.emplace_back(range.begin, range.end);
1103 const uint32_t pointer_width =
1107 const uint32_t pointer_slot =
1108 layout.pointer_table_pc +
1109 static_cast<uint32_t
>(room_id) * pointer_width;
1110 ranges.emplace_back(pointer_slot, pointer_slot + pointer_width);
1112 const uint32_t door_slot =
1114 ranges.emplace_back(door_slot, door_slot + 3u);
1117 if (object_cow_layout.has_value()) {
1118 append_cow_ranges(*object_cow_layout);
1120 if (sprite_cow_layout.has_value()) {
1121 append_cow_ranges(*sprite_cow_layout);
1126 const auto write_policy = dependencies_.project->rom_metadata.write_policy;
1128 manifest, write_policy, ranges, absl::StrFormat(
"room 0x%03X", room_id),
1129 "DungeonEditorV2", dependencies_.toast_manager));
1132 if (flags.kSaveObjects && room.object_stream_dirty()) {
1133 auto status = room.SaveObjects(
1134 object_cow_layout.has_value() ? &*object_cow_layout :
nullptr);
1136 LOG_ERROR(
"DungeonEditorV2",
"Failed to save room objects: %s",
1137 status.message().data());
1142 if (flags.kSaveObjects && room.object_stream_header_dirty()) {
1143 auto status = room.SaveObjectStreamHeader(
1144 object_cow_layout.has_value() ? &*object_cow_layout :
nullptr);
1147 "Failed to save room object-stream header: %s",
1148 status.message().data());
1153 if (flags.kSaveSprites && room.sprites_dirty()) {
1154 auto status = room.SaveSprites(
1155 sprite_cow_layout.has_value() ? &*sprite_cow_layout :
nullptr);
1157 LOG_ERROR(
"DungeonEditorV2",
"Failed to save room sprites: %s",
1158 status.message().data());
1163 if (flags.kSaveRoomHeaders && room.header_dirty()) {
1164 auto status = room.SaveRoomHeader();
1166 LOG_ERROR(
"DungeonEditorV2",
"Failed to save room header: %s",
1167 status.message().data());
1172 return absl::OkStatus();
1175void DungeonEditorV2::SaveAllRooms() {
1176 auto status = RunWithSaveTransaction([
this]() {
return Save(); });
1178 if (dependencies_.toast_manager) {
1179 dependencies_.toast_manager->Show(
"Applied loaded rooms to ROM buffer",
1180 ToastType::kSuccess);
1183 LOG_ERROR(
"DungeonEditorV2",
"SaveAllRooms failed: %s",
1184 status.message().data());
1185 if (dependencies_.toast_manager) {
1186 dependencies_.toast_manager->Show(
1187 absl::StrFormat(
"Save all failed: %s", status.message()),
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
const auto & vector() const
const DungeonStreamLayout * GetDungeonStreamLayout(DungeonStreamType stream) const
Get an explicitly declared dungeon stream layout.
bool loaded() const
Check if the manifest has been loaded.
void CommitSaveTransaction() override
absl::Status SaveRoomData(int room_id)
absl::Status Save() override
std::array< zelda3::RoomEntrance, zelda3::kNumDungeonEntranceSlots > entrances_
void RollbackSaveTransaction() override
absl::Status BeginSaveTransaction() override
absl::Status RunWithSaveTransaction(const std::function< absl::Status()> &operation)
std::array< zelda3::DungeonSpawnPoint, zelda3::kNumDungeonSpawnPoints > spawn_points_
std::optional< SaveTransactionSnapshot > save_transaction_snapshot_
zelda3::GameData * game_data_
std::vector< std::pair< uint32_t, uint32_t > > CollectWriteRanges() const
zelda3::Room * GetIfMaterialized(int room_id)
void ForEachMaterialized(Fn &&fn)
void ForEachLoaded(Fn &&fn)
EditorDependencies dependencies_
void RollbackSaveTransaction()
void CommitSaveTransaction()
static PaletteManager & Get()
Get the singleton instance.
ValidationResult ValidateRoom(const Room &room)
void set_water_fill_sram_bit_mask(uint8_t mask)
bool custom_collision_dirty() const
const WaterFillZoneMap & water_fill_zone() const
int WaterFillTileCount() const
bool pot_items_dirty() const
bool object_stream_dirty() const
bool sprites_dirty() const
bool header_dirty() const
bool object_stream_header_dirty() const
bool has_water_fill_zone() const
bool chests_dirty() const
std::vector< uint8_t > EncodeObjects() const
void ClearWaterFillDirty()
std::vector< uint8_t > EncodeSprites() const
SaveDirtySnapshot CaptureSaveDirtySnapshot() const
bool water_fill_dirty() const
const std::vector< PotItem > & GetPotItems() const
uint8_t water_fill_sram_bit_mask() const
#define LOG_ERROR(category, format,...)
#define LOG_WARN(category, format,...)
#define LOG_INFO(category, format,...)
#define ASSIGN_OR_RETURN(type_variable_name, expression)
DungeonStreamType
Dungeon stream kinds with allocator layouts in the hack manifest.
absl::StatusOr< zelda3::DungeonStreamLayout > ToDungeonStreamAllocatorLayout(DungeonStreamType stream_type, const DungeonStreamLayout &manifest_layout)
absl::Status ValidatePotItemManifestConflicts(const project::YazeProject *project, Rom *rom, const DungeonRoomStore &rooms, absl::string_view save_scope, ToastManager *toast_manager)
absl::StatusOr< std::vector< std::pair< uint32_t, uint32_t > > > CollectDirtyChestWriteRanges(const Rom *rom, const DungeonRoomStore &rooms)
std::vector< std::pair< uint32_t, uint32_t > > CollectDirtyPotItemWriteRanges(const project::YazeProject *project, const Rom *rom, const DungeonRoomStore &rooms)
absl::Status SaveAllPotItemsForProject(const project::YazeProject *project, Rom *rom, DungeonRoomStore &rooms)
absl::Status ValidateChestManifestConflicts(const project::YazeProject *project, Rom *rom, const DungeonRoomStore &rooms, absl::string_view save_scope, ToastManager *toast_manager)
absl::Status ValidateDungeonEntranceSavePreflight(const project::YazeProject *project, Rom *rom, const std::array< zelda3::RoomEntrance, zelda3::kNumDungeonEntranceSlots > &entrances, const std::array< zelda3::DungeonSpawnPoint, zelda3::kNumDungeonSpawnPoints > &spawn_points, absl::string_view save_scope, ToastManager *toast_manager)
absl::Status SaveWaterFillZones(Rom *rom, DungeonRoomStore &rooms)
Editors are the view controllers for the application.
absl::Status ValidateHackManifestSaveConflicts(const core::HackManifest &manifest, project::RomWritePolicy write_policy, const std::vector< std::pair< uint32_t, uint32_t > > &ranges, absl::string_view save_scope, const char *log_tag, ToastManager *toast_manager)
absl::Status SaveAllChests(Rom *rom, absl::Span< const Room > rooms)
constexpr int kDoorPointers
constexpr int kWaterFillTableEnd
constexpr int kCustomCollisionDataSoftEnd
constexpr int kWaterFillTableStart
constexpr int kMessagesIdDungeon
absl::Status NormalizeWaterFillZoneMasks(std::vector< WaterFillZoneEntry > *zones)
absl::Status SaveAllPotItems(Rom *rom, absl::Span< const Room > rooms)
std::vector< DungeonEntranceWriteRange > CollectDirtyRegularDungeonEntranceWriteRanges(const std::array< RoomEntrance, kNumDungeonEntranceSlots > &entrances)
absl::StatusOr< std::vector< std::pair< uint32_t, uint32_t > > > GetChestTableWriteRanges(const Rom *rom)
absl::Status SaveAllDungeonSpawnPoints(Rom *rom, std::array< DungeonSpawnPoint, kNumDungeonSpawnPoints > &spawn_points, bool dirty_only=true)
absl::Status SaveAllTorches(Rom *rom, absl::Span< const Room > rooms)
constexpr int kRoomsSpritePointer
absl::Status SaveAllPits(Rom *rom)
absl::Status SaveAllBlocks(Rom *rom)
constexpr int kRoomItemsPointers
constexpr int kCustomCollisionDataPosition
absl::Status RejectUnsupportedDungeonSpawnPointSaves(const std::array< RoomEntrance, kNumDungeonEntranceSlots > &entrances, bool dirty_only=true)
absl::Status SaveAllDungeonEntrances(Rom *rom, std::array< RoomEntrance, kNumDungeonEntranceSlots > &entrances, bool dirty_only=true)
absl::Status ValidateRegularDungeonEntrancesForSave(const Rom &rom, const std::array< RoomEntrance, kNumDungeonEntranceSlots > &entrances, bool dirty_only=true)
constexpr int kNumberOfRooms
constexpr int kRoomHeaderPointer
constexpr int kRoomHeaderPointerBank
constexpr int kCustomCollisionRoomPointers
absl::Status SaveAllCollision(Rom *rom, absl::Span< Room > rooms)
absl::Status ValidateDungeonSpawnPointsForSave(const Rom &rom, const std::array< DungeonSpawnPoint, kNumDungeonSpawnPoints > &spawn_points, bool dirty_only=true)
constexpr int kRoomObjectPointer
std::vector< DungeonSpawnPointWriteRange > CollectDirtyDungeonSpawnPointWriteRanges(const std::array< DungeonSpawnPoint, kNumDungeonSpawnPoints > &spawn_points)
absl::Status WriteWaterFillTable(Rom *rom, const std::vector< WaterFillZoneEntry > &zones)
uint32_t SnesToPc(uint32_t addr) noexcept
#define RETURN_IF_ERROR(expr)
DungeonPointerEncoding pointer_encoding
struct yaze::core::FeatureFlags::Flags::Dungeon dungeon
bool has_palette_transaction
std::vector< std::pair< int, zelda3::Room::SaveDirtySnapshot > > room_states
std::array< bool, zelda3::kNumDungeonSpawnPoints > spawn_dirty_states
bool has_pit_damage_table
std::array< bool, zelda3::kNumDungeonEntranceSlots > entrance_dirty_states
project::YazeProject * project
ToastManager * toast_manager
Modern project structure with comprehensive settings consolidation.
core::HackManifest hack_manifest
PitDamageTable pit_damage_table
std::vector< uint16_t > fill_offsets
std::array< uint8_t, 64 *64 > tiles