yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dungeon_map.cc
Go to the documentation of this file.
1#include "dungeon_map.h"
2
3#include <fstream>
4#include <vector>
5
6#include "absl/strings/str_format.h"
12#include "app/platform/window.h"
13#include "rom/snes.h"
14#include "util/file_util.h"
15#include "util/hex.h"
16#include "zelda3/game_data.h"
17
18namespace yaze::zelda3 {
19
20absl::StatusOr<std::vector<DungeonMap>> LoadDungeonMaps(
21 Rom& rom, DungeonMapLabels& dungeon_map_labels) {
22 std::vector<DungeonMap> dungeon_maps;
23 std::vector<std::array<uint8_t, kNumRooms>> current_floor_rooms_d;
24 std::vector<std::array<uint8_t, kNumRooms>> current_floor_gfx_d;
25 int total_floors_d;
26 uint8_t nbr_floor_d;
27 uint8_t nbr_basement_d;
28
29 for (int d = 0; d < kNumDungeons; d++) {
30 current_floor_rooms_d.clear();
31 current_floor_gfx_d.clear();
32 ASSIGN_OR_RETURN(int ptr, rom.ReadWord(kDungeonMapRoomsPtr + (d * 2)));
33 ASSIGN_OR_RETURN(int ptr_gfx, rom.ReadWord(kDungeonMapGfxPtr + (d * 2)));
34 ptr |= 0x0A0000; // Add bank to the short ptr
35 ptr_gfx |= 0x0A0000; // Add bank to the short ptr
36 int pc_ptr = SnesToPc(ptr); // Contains data for the next 25 rooms
37 int pc_ptr_gfx = SnesToPc(ptr_gfx); // Contains data for the next 25 rooms
38
39 ASSIGN_OR_RETURN(uint16_t boss_room_d,
40 rom.ReadWord(kDungeonMapBossRooms + (d * 2)));
41
42 ASSIGN_OR_RETURN(nbr_basement_d, rom.ReadByte(kDungeonMapFloors + (d * 2)));
43 nbr_basement_d &= 0x0F;
44
45 ASSIGN_OR_RETURN(nbr_floor_d, rom.ReadByte(kDungeonMapFloors + (d * 2)));
46 nbr_floor_d &= 0xF0;
47 nbr_floor_d = nbr_floor_d >> 4;
48
49 total_floors_d = nbr_basement_d + nbr_floor_d;
50
51 // for each floor in the dungeon
52 for (int i = 0; i < total_floors_d; i++) {
53 dungeon_map_labels[d].emplace_back();
54
55 std::array<uint8_t, kNumRooms> rdata;
56 std::array<uint8_t, kNumRooms> gdata;
57
58 // for each room on the floor
59 for (int j = 0; j < kNumRooms; j++) {
60 gdata[j] = 0xFF;
61 rdata[j] = rom.data()[pc_ptr + j + (i * kNumRooms)]; // Set the rooms
62
63 gdata[j] = rdata[j] == 0x0F ? 0xFF : rom.data()[pc_ptr_gfx++];
64
65 std::string label = util::HexByte(rdata[j]);
66 dungeon_map_labels[d][i][j] = label;
67 }
68
69 current_floor_gfx_d.push_back(gdata); // Add new floor gfx data
70 current_floor_rooms_d.push_back(rdata); // Add new floor data
71 }
72
73 dungeon_maps.emplace_back(boss_room_d, nbr_floor_d, nbr_basement_d,
74 current_floor_rooms_d, current_floor_gfx_d);
75 }
76
77 return dungeon_maps;
78}
79
80absl::Status SaveDungeonMaps(Rom& rom, std::vector<DungeonMap>& dungeon_maps) {
81 int pos = kDungeonMapDataStart;
82
83 for (int d = 0; d < kNumDungeons; d++) {
84 if (d >= static_cast<int>(dungeon_maps.size())) {
85 break;
86 }
87
88 auto& map = dungeon_maps[d];
89 const int total_floors = map.nbr_of_floor + map.nbr_of_basement;
90
91 uint16_t floors = (map.nbr_of_floor << 4) | map.nbr_of_basement;
92 RETURN_IF_ERROR(rom.WriteWord(kDungeonMapFloors + (d * 2), floors));
94 rom.WriteWord(kDungeonMapBossRooms + (d * 2), map.boss_room));
95
96 const bool has_boss = map.boss_room != 0x000F && map.boss_room != 0xFFFF;
97 bool search_boss = has_boss;
98 if (!has_boss) {
99 RETURN_IF_ERROR(rom.WriteWord(kDungeonMapBossFloors + (d * 2), 0xFFFF));
100 }
101
103 rom.WriteWord(kDungeonMapRoomsPtr + (d * 2), PcToSnes(pos)));
104
105 bool restart = false;
106 for (int f = 0; f < total_floors; f++) {
107 for (int r = 0; r < kNumRooms; r++) {
108 if (search_boss && map.floor_rooms[f][r] == map.boss_room) {
110 search_boss = false;
111 }
112
113 if (pos >= kDungeonMapDataReservedStart &&
116 restart = true;
117 break;
118 }
119
120 RETURN_IF_ERROR(rom.WriteByte(pos, map.floor_rooms[f][r]));
121 pos++;
122 }
123 if (restart)
124 break;
125 }
126
127 if (restart) {
128 d--;
129 continue;
130 }
131
133 for (int f = 0; f < total_floors; f++) {
134 for (int r = 0; r < kNumRooms; r++) {
135 if (map.floor_rooms[f][r] != 0x0F) {
136 if (pos >= kDungeonMapDataReservedStart &&
140 rom.WriteWord(kDungeonMapGfxPtr + (d * 2), PcToSnes(pos)));
141 restart = true;
142 break;
143 }
144
145 RETURN_IF_ERROR(rom.WriteByte(pos, map.floor_gfx[f][r]));
146 pos++;
147 }
148 }
149 if (restart)
150 break;
151 }
152
153 if (pos >= kDungeonMapDataLimit) {
154 return absl::OutOfRangeError("Dungeon map data exceeds reserved space");
155 }
156
157 if (restart) {
158 d--;
159 continue;
160 }
161
162 if (search_boss) {
163 return absl::NotFoundError(
164 absl::StrFormat("Boss room not found for dungeon %d", d));
165 }
166 }
167
168 return absl::OkStatus();
169}
170
171absl::Status LoadDungeonMapTile16(gfx::Tilemap& tile16_blockset, Rom& rom,
172 GameData* game_data,
173 const std::vector<uint8_t>& gfx_data,
174 bool bin_mode) {
175 tile16_blockset.tile_size = {16, 16};
176 tile16_blockset.map_size = {186, 186};
177 tile16_blockset.atlas.Create(256, 192, 8,
178 std::vector<uint8_t>(256 * 192, 0x00));
179
180 for (int i = 0; i < kNumDungeonMapTile16; i++) {
181 int addr = kDungeonMapTile16;
182 if (rom.data()[kDungeonMapExpCheck] != 0xB9) {
184 }
185
186 ASSIGN_OR_RETURN(auto tl, rom.ReadWord(addr + (i * 8)));
187 gfx::TileInfo t1 = gfx::WordToTileInfo(tl); // Top left
188
189 ASSIGN_OR_RETURN(auto tr, rom.ReadWord(addr + 2 + (i * 8)));
190 gfx::TileInfo t2 = gfx::WordToTileInfo(tr); // Top right
191
192 ASSIGN_OR_RETURN(auto bl, rom.ReadWord(addr + 4 + (i * 8)));
193 gfx::TileInfo t3 = gfx::WordToTileInfo(bl); // Bottom left
194
195 ASSIGN_OR_RETURN(auto br, rom.ReadWord(addr + 6 + (i * 8)));
196 gfx::TileInfo t4 = gfx::WordToTileInfo(br); // Bottom right
197
198 int sheet_offset = 212;
199 if (bin_mode) {
200 sheet_offset = 0;
201 }
202 ComposeTile16(tile16_blockset, gfx_data, t1, t2, t3, t4, sheet_offset);
203 }
204
205 if (game_data) {
206 tile16_blockset.atlas.SetPalette(
208 }
209
210 // Queue texture creation via Arena's deferred system
212 &tile16_blockset.atlas);
213
214 return absl::OkStatus();
215}
216
217absl::Status SaveDungeonMapTile16(gfx::Tilemap& tile16_blockset, Rom& rom) {
218 for (int i = 0; i < kNumDungeonMapTile16; i++) {
219 int addr = kDungeonMapTile16;
220 if (rom.data()[kDungeonMapExpCheck] != 0xB9) {
222 }
223
224 gfx::TileInfo t1 = tile16_blockset.tile_info[i][0];
225 gfx::TileInfo t2 = tile16_blockset.tile_info[i][1];
226 gfx::TileInfo t3 = tile16_blockset.tile_info[i][2];
227 gfx::TileInfo t4 = tile16_blockset.tile_info[i][3];
228
229 auto tl = gfx::TileInfoToWord(t1);
230 RETURN_IF_ERROR(rom.WriteWord(addr + (i * 8), tl));
231
232 auto tr = gfx::TileInfoToWord(t2);
233 RETURN_IF_ERROR(rom.WriteWord(addr + 2 + (i * 8), tr));
234
235 auto bl = gfx::TileInfoToWord(t3);
236 RETURN_IF_ERROR(rom.WriteWord(addr + 4 + (i * 8), bl));
237
238 auto br = gfx::TileInfoToWord(t4);
239 RETURN_IF_ERROR(rom.WriteWord(addr + 6 + (i * 8), br));
240 }
241 return absl::OkStatus();
242}
243
244absl::Status LoadDungeonMapGfxFromBinary(Rom& rom, GameData* game_data,
245 gfx::Tilemap& tile16_blockset,
246 std::array<gfx::Bitmap, 4>& sheets,
247 std::vector<uint8_t>& gfx_bin_data) {
248 std::string bin_file = util::FileDialogWrapper::ShowOpenFileDialog();
249 if (bin_file.empty()) {
250 return absl::InternalError("No file selected");
251 }
252
253 std::ifstream file(bin_file, std::ios::binary);
254 if (!file.is_open()) {
255 return absl::InternalError("Failed to open file");
256 }
257
258 // Read the gfx data into a buffer
259 std::vector<uint8_t> bin_data((std::istreambuf_iterator<char>(file)),
260 std::istreambuf_iterator<char>());
261 auto converted_bin = gfx::SnesTo8bppSheet(bin_data, 4, 4);
262 gfx_bin_data = converted_bin;
263 if (LoadDungeonMapTile16(tile16_blockset, rom, game_data, converted_bin, true)
264 .ok()) {
265 std::vector<std::vector<uint8_t>> gfx_sheets;
266 for (int i = 0; i < 4; i++) {
267 gfx_sheets.emplace_back(converted_bin.begin() + (i * 0x1000),
268 converted_bin.begin() + ((i + 1) * 0x1000));
269 sheets[i] = gfx::Bitmap(128, 32, 8, gfx_sheets[i]);
270 if (game_data) {
271 sheets[i].SetPalette(
273 }
274
275 // Queue texture creation via Arena's deferred system
278 }
279 }
280 file.close();
281
282 return absl::OkStatus();
283}
284
285} // namespace yaze::zelda3
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:24
absl::Status WriteByte(int addr, uint8_t value)
Definition rom.cc:290
absl::StatusOr< uint16_t > ReadWord(int offset)
Definition rom.cc:230
auto data() const
Definition rom.h:135
absl::StatusOr< uint8_t > ReadByte(int offset)
Definition rom.cc:222
absl::Status WriteWord(int addr, uint16_t value)
Definition rom.cc:303
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
Definition arena.cc:35
static Arena & Get()
Definition arena.cc:20
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:67
void Create(int width, int height, int depth, std::span< uint8_t > data)
Create a bitmap with the given dimensions and data.
Definition bitmap.cc:201
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap using SNES palette format.
Definition bitmap.cc:384
SNES 16-bit tile metadata container.
Definition snes_tile.h:52
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath. Uses global feature flag to...
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition macro.h:62
uint16_t TileInfoToWord(TileInfo tile_info)
Definition snes_tile.cc:361
std::vector< uint8_t > SnesTo8bppSheet(std::span< uint8_t > sheet, int bpp, int num_sheets)
Definition snes_tile.cc:132
TileInfo WordToTileInfo(uint16_t word)
Definition snes_tile.cc:378
std::string HexByte(uint8_t byte, HexStringParams params)
Definition hex.cc:30
Zelda 3 specific classes and functions.
Definition editor.h:35
absl::Status LoadDungeonMapTile16(gfx::Tilemap &tile16_blockset, Rom &rom, GameData *game_data, const std::vector< uint8_t > &gfx_data, bool bin_mode)
Load the dungeon map tile16 from the ROM.
constexpr int kDungeonMapDataStart
Definition dungeon_map.h:22
constexpr int kDungeonMapExpCheck
Definition dungeon_map.h:28
constexpr int kDungeonMapBossFloors
Definition dungeon_map.h:34
constexpr int kDungeonMapDataReservedEnd
Definition dungeon_map.h:24
constexpr int kDungeonMapFloors
Definition dungeon_map.h:17
constexpr int kDungeonMapTile16
Definition dungeon_map.h:29
constexpr int kNumRooms
Definition dungeon_map.h:41
constexpr int kDungeonMapTile16Expanded
Definition dungeon_map.h:30
absl::Status SaveDungeonMapTile16(gfx::Tilemap &tile16_blockset, Rom &rom)
Save the dungeon map tile16 to the ROM.
constexpr int kDungeonMapDataReservedStart
Definition dungeon_map.h:23
absl::Status LoadDungeonMapGfxFromBinary(Rom &rom, GameData *game_data, gfx::Tilemap &tile16_blockset, std::array< gfx::Bitmap, 4 > &sheets, std::vector< uint8_t > &gfx_bin_data)
Load the dungeon map gfx from binary.
constexpr int kDungeonMapDataLimit
Definition dungeon_map.h:25
constexpr int kDungeonMapBossRooms
Definition dungeon_map.h:33
constexpr int kDungeonMapRoomsPtr
Definition dungeon_map.h:16
constexpr int kNumDungeonMapTile16
Definition dungeon_map.h:42
constexpr int kNumDungeons
Definition dungeon_map.h:40
std::array< std::vector< std::array< std::string, kNumRooms > >, kNumDungeons > DungeonMapLabels
Definition dungeon_map.h:65
absl::StatusOr< std::vector< DungeonMap > > LoadDungeonMaps(Rom &rom, DungeonMapLabels &dungeon_map_labels)
Load the dungeon maps from the ROM.
constexpr int kDungeonMapGfxPtr
Definition dungeon_map.h:19
absl::Status SaveDungeonMaps(Rom &rom, std::vector< DungeonMap > &dungeon_maps)
Save the dungeon maps to the ROM.
uint32_t PcToSnes(uint32_t addr)
Definition snes.h:17
uint32_t SnesToPc(uint32_t addr) noexcept
Definition snes.h:8
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22
Tilemap structure for SNES tile-based graphics management.
Definition tilemap.h:118
Pair tile_size
Size of individual tiles (8x8 or 16x16)
Definition tilemap.h:123
Pair map_size
Size of tilemap in tiles.
Definition tilemap.h:124
Bitmap atlas
Master bitmap containing all tiles.
Definition tilemap.h:119
std::vector< std::array< gfx::TileInfo, 4 > > tile_info
Tile metadata (4 tiles per 16x16)
Definition tilemap.h:122
gfx::PaletteGroupMap palette_groups
Definition game_data.h:89