88 auto dungeon_id_opt = parser.
GetString(
"dungeon");
89 bool list_only = parser.
HasFlag(
"list");
91 int dungeon_filter = -1;
92 if (dungeon_id_opt.has_value()) {
93 if (!ParseHexString(dungeon_id_opt.value(), &dungeon_filter)) {
94 return absl::InvalidArgumentError(
95 "Invalid dungeon ID format. Must be hex (e.g., 0x02).");
100 std::map<int, DungeonInfo> dungeons;
103 for (
int i = 0; i < kNumberOfDungeons; ++i) {
106 info.name = GetDungeonName(i);
109 info.start_room = rom->
data()[kDungeonsStartRooms + i];
112 uint16_t boss_room_addr = kDungeonsBossRooms + (i * 2);
113 info.boss_room = rom->
data()[boss_room_addr] |
114 (rom->
data()[boss_room_addr + 1] << 8);
117 if (info.boss_room == 0xFFFF) {
125 std::map<int, int> room_to_dungeon;
126 for (
int entrance_id = 0; entrance_id < kNumberOfEntrances; ++entrance_id) {
130 int room_id = entrance.
room_;
133 room_to_dungeon[room_id] = dungeon_id;
136 if (dungeons.find(dungeon_id) == dungeons.end()) {
139 info.id = dungeon_id;
140 info.name = GetDungeonName(dungeon_id);
141 info.start_room = -1;
143 dungeons[dungeon_id] = info;
145 dungeons[dungeon_id].rooms.insert(room_id);
149 for (
int spawn_id = 0; spawn_id < 0x14; ++spawn_id) {
153 int room_id = spawn.
room_;
155 room_to_dungeon[room_id] = dungeon_id;
157 if (dungeons.find(dungeon_id) != dungeons.end()) {
158 dungeons[dungeon_id].rooms.insert(room_id);
163 std::set<int> unassigned_rooms;
165 if (room_to_dungeon.find(room_id) == room_to_dungeon.end()) {
166 unassigned_rooms.insert(room_id);
176 for (
const auto& [
id, info] : dungeons) {
177 if (dungeon_filter >= 0 &&
id != dungeon_filter) {
181 formatter.
AddField(
"id", absl::StrFormat(
"0x%02X",
id));
182 formatter.
AddField(
"name", info.name);
183 formatter.
AddField(
"room_count",
static_cast<int>(info.rooms.size()));
190 for (
const auto& [
id, info] : dungeons) {
191 if (dungeon_filter >= 0 &&
id != dungeon_filter) {
196 if (info.rooms.empty() && dungeon_filter < 0) {
201 formatter.
AddField(
"id", absl::StrFormat(
"0x%02X",
id));
202 formatter.
AddField(
"name", info.name);
204 if (info.start_room >= 0) {
206 absl::StrFormat(
"0x%02X", info.start_room));
208 formatter.
AddField(
"start_room",
"null");
211 if (info.boss_room >= 0) {
213 absl::StrFormat(
"0x%02X", info.boss_room));
215 formatter.
AddField(
"boss_room",
"null");
220 for (
int room_id : info.rooms) {
222 formatter.
AddField(
"id", absl::StrFormat(
"0x%02X", room_id));
224 if (room_id >= 0 && room_id < 297) {
227 formatter.
AddField(
"name", absl::StrFormat(
"Room 0x%02X", room_id));
238 if (dungeon_filter < 0 && !unassigned_rooms.empty()) {
240 for (
int room_id : unassigned_rooms) {
241 formatter.
AddArrayItem(absl::StrFormat(
"0x%02X", room_id));
249 int total_assigned = 0;
250 for (
const auto& [
id, info] : dungeons) {
251 total_assigned +=
static_cast<int>(info.rooms.size());
253 formatter.
AddField(
"total_dungeons",
static_cast<int>(dungeons.size()));
254 formatter.
AddField(
"assigned_rooms", total_assigned);
255 formatter.
AddField(
"unassigned_rooms",
256 static_cast<int>(unassigned_rooms.size()));
261 return absl::OkStatus();