From 326594f3e6ad4b25b815254773e7655b3ac5b690 Mon Sep 17 00:00:00 2001 From: scawful Date: Mon, 22 Dec 2025 14:56:17 -0500 Subject: [PATCH] fix(dungeon-map): rewrite save layout --- src/zelda3/screen/dungeon_map.cc | 98 +++++++++++++++++++++++++++----- src/zelda3/screen/dungeon_map.h | 4 ++ 2 files changed, 88 insertions(+), 14 deletions(-) diff --git a/src/zelda3/screen/dungeon_map.cc b/src/zelda3/screen/dungeon_map.cc index 006abff4..10923ca7 100644 --- a/src/zelda3/screen/dungeon_map.cc +++ b/src/zelda3/screen/dungeon_map.cc @@ -3,6 +3,7 @@ #include #include +#include "absl/strings/str_format.h" #include "app/gfx/backend/irenderer.h" #include "app/gfx/core/bitmap.h" #include "app/gfx/render/tilemap.h" @@ -77,22 +78,91 @@ absl::StatusOr> LoadDungeonMaps( } absl::Status SaveDungeonMaps(Rom& rom, std::vector& dungeon_maps) { - for (int d = 0; d < kNumDungeons; d++) { - int ptr = kDungeonMapRoomsPtr + (d * 2); - int ptr_gfx = kDungeonMapGfxPtr + (d * 2); - int pc_ptr = SnesToPc(ptr); - int pc_ptr_gfx = SnesToPc(ptr_gfx); + int pos = kDungeonMapDataStart; - const int nbr_floors = dungeon_maps[d].nbr_of_floor; - const int nbr_basements = dungeon_maps[d].nbr_of_basement; - for (int i = 0; i < nbr_floors + nbr_basements; i++) { - for (int j = 0; j < kNumRooms; j++) { - RETURN_IF_ERROR(rom.WriteByte(pc_ptr + j + (i * kNumRooms), - dungeon_maps[d].floor_rooms[i][j])); - RETURN_IF_ERROR(rom.WriteByte(pc_ptr_gfx + j + (i * kNumRooms), - dungeon_maps[d].floor_gfx[i][j])); - pc_ptr_gfx++; + for (int d = 0; d < kNumDungeons; d++) { + if (d >= static_cast(dungeon_maps.size())) { + break; + } + + auto& map = dungeon_maps[d]; + const int total_floors = map.nbr_of_floor + map.nbr_of_basement; + + uint16_t floors = (map.nbr_of_floor << 4) | map.nbr_of_basement; + RETURN_IF_ERROR(rom.WriteWord(kDungeonMapFloors + (d * 2), floors)); + RETURN_IF_ERROR(rom.WriteWord(kDungeonMapBossRooms + (d * 2), + map.boss_room)); + + bool search_boss = map.boss_room != 0x000F; + if (!search_boss) { + RETURN_IF_ERROR( + rom.WriteWord(kDungeonMapBossFloors + (d * 2), 0xFFFF)); + } + + RETURN_IF_ERROR( + rom.WriteWord(kDungeonMapRoomsPtr + (d * 2), PcToSnes(pos))); + + bool restart = false; + for (int f = 0; f < total_floors; f++) { + for (int r = 0; r < kNumRooms; r++) { + if (search_boss && map.floor_rooms[f][r] == map.boss_room) { + RETURN_IF_ERROR( + rom.WriteWord(kDungeonMapBossFloors + (d * 2), f)); + search_boss = false; + } + + RETURN_IF_ERROR(rom.WriteByte(pos, map.floor_rooms[f][r])); + pos++; + + if (pos >= kDungeonMapDataReservedStart && + pos <= kDungeonMapDataReservedEnd) { + pos = kDungeonMapDataReservedEnd + 1; + restart = true; + break; + } } + if (restart) break; + } + + if (restart) { + d--; + continue; + } + + RETURN_IF_ERROR( + rom.WriteWord(kDungeonMapGfxPtr + (d * 2), PcToSnes(pos))); + for (int f = 0; f < total_floors; f++) { + for (int r = 0; r < kNumRooms; r++) { + if (map.floor_rooms[f][r] != 0x0F) { + RETURN_IF_ERROR(rom.WriteByte(pos, map.floor_gfx[f][r])); + pos++; + + if (pos >= kDungeonMapDataReservedStart && + pos <= kDungeonMapDataReservedEnd) { + pos = kDungeonMapDataReservedEnd + 1; + RETURN_IF_ERROR(rom.WriteWord(kDungeonMapGfxPtr + (d * 2), + PcToSnes(pos))); + restart = true; + break; + } + } + } + if (restart) break; + } + + if (pos >= kDungeonMapDataLimit) { + return absl::OutOfRangeError( + "Dungeon map data exceeds reserved space"); + } + + if (restart) { + d--; + continue; + } + + if (search_boss) { + return absl::NotFoundError( + absl::StrFormat("Boss room not found for dungeon %d", d)); } } diff --git a/src/zelda3/screen/dungeon_map.h b/src/zelda3/screen/dungeon_map.h index 12263a21..55ec3ae8 100644 --- a/src/zelda3/screen/dungeon_map.h +++ b/src/zelda3/screen/dungeon_map.h @@ -20,6 +20,9 @@ constexpr int kDungeonMapGfxPtr = 0x57BE4; // 14 pointers of gfx data // data start for floors/gfx MUST skip 575D9 to 57621 (pointers) constexpr int kDungeonMapDataStart = 0x57039; +constexpr int kDungeonMapDataReservedStart = 0x575D9; +constexpr int kDungeonMapDataReservedEnd = 0x57620; +constexpr int kDungeonMapDataLimit = 0x57CE0; // IF Byte = 0xB9 dungeon maps are not expanded constexpr int kDungeonMapExpCheck = 0x56652; // $0A:E652 @@ -28,6 +31,7 @@ constexpr int kDungeonMapTile16Expanded = 0x109010; // $21:9010 // 14 words values 0x000F = no boss constexpr int kDungeonMapBossRooms = 0x56807; +constexpr int kDungeonMapBossFloors = 0x56E79; constexpr int kTriforceVertices = 0x04FFD2; // group of 3, X, Y ,Z constexpr int kTriforceFaces = 0x04FFE4; // group of 5