89 auto dungeon_id_opt = parser.
GetString(
"dungeon");
90 bool list_only = parser.
HasFlag(
"list");
92 int dungeon_filter = -1;
93 if (dungeon_id_opt.has_value()) {
94 if (!ParseHexString(dungeon_id_opt.value(), &dungeon_filter)) {
95 return absl::InvalidArgumentError(
96 "Invalid dungeon ID format. Must be hex (e.g., 0x02).");
101 std::map<int, DungeonInfo> dungeons;
104 for (
int i = 0; i < kNumberOfDungeons; ++i) {
107 info.name = GetDungeonName(i);
110 info.start_room = rom->
data()[kDungeonsStartRooms + i];
113 uint16_t boss_room_addr = kDungeonsBossRooms + (i * 2);
115 rom->
data()[boss_room_addr] | (rom->
data()[boss_room_addr + 1] << 8);
118 if (info.boss_room == 0xFFFF) {
126 std::map<int, int> room_to_dungeon;
127 for (
int entrance_id = 0; entrance_id < kNumberOfEntrances; ++entrance_id) {
132 int room_id = entrance.
room_;
135 room_to_dungeon[room_id] = dungeon_id;
138 if (dungeons.find(dungeon_id) == dungeons.end()) {
141 info.id = dungeon_id;
142 info.name = GetDungeonName(dungeon_id);
143 info.start_room = -1;
145 dungeons[dungeon_id] = info;
147 dungeons[dungeon_id].rooms.insert(room_id);
154 if (!spawn_or.ok()) {
155 return spawn_or.status();
157 const auto& spawn = spawn_or.value();
159 int dungeon_id = spawn.dungeon_id;
160 int room_id = spawn.room_id;
162 room_to_dungeon[room_id] = dungeon_id;
164 if (dungeons.find(dungeon_id) != dungeons.end()) {
165 dungeons[dungeon_id].rooms.insert(room_id);
170 std::set<int> unassigned_rooms;
172 if (room_to_dungeon.find(room_id) == room_to_dungeon.end()) {
173 unassigned_rooms.insert(room_id);
183 for (
const auto& [
id, info] : dungeons) {
184 if (dungeon_filter >= 0 &&
id != dungeon_filter) {
188 formatter.
AddField(
"id", absl::StrFormat(
"0x%02X",
id));
189 formatter.
AddField(
"name", info.name);
190 formatter.
AddField(
"room_count",
static_cast<int>(info.rooms.size()));
197 for (
const auto& [
id, info] : dungeons) {
198 if (dungeon_filter >= 0 &&
id != dungeon_filter) {
203 if (info.rooms.empty() && dungeon_filter < 0) {
208 formatter.
AddField(
"id", absl::StrFormat(
"0x%02X",
id));
209 formatter.
AddField(
"name", info.name);
211 if (info.start_room >= 0) {
213 absl::StrFormat(
"0x%02X", info.start_room));
215 formatter.
AddField(
"start_room",
"null");
218 if (info.boss_room >= 0) {
220 absl::StrFormat(
"0x%02X", info.boss_room));
222 formatter.
AddField(
"boss_room",
"null");
227 for (
int room_id : info.rooms) {
229 formatter.
AddField(
"id", absl::StrFormat(
"0x%02X", room_id));
231 if (room_id >= 0 && room_id < 297) {
234 formatter.
AddField(
"name", absl::StrFormat(
"Room 0x%02X", room_id));
245 if (dungeon_filter < 0 && !unassigned_rooms.empty()) {
247 for (
int room_id : unassigned_rooms) {
248 formatter.
AddArrayItem(absl::StrFormat(
"0x%02X", room_id));
256 int total_assigned = 0;
257 for (
const auto& [
id, info] : dungeons) {
258 total_assigned +=
static_cast<int>(info.rooms.size());
260 formatter.
AddField(
"total_dungeons",
static_cast<int>(dungeons.size()));
261 formatter.
AddField(
"assigned_rooms", total_assigned);
262 formatter.
AddField(
"unassigned_rooms",
263 static_cast<int>(unassigned_rooms.size()));
268 return absl::OkStatus();