yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dungeon_room_loader.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <map>
5#include <future>
6#include <thread>
7#include <mutex>
8
12#include "util/log.h"
13
14namespace yaze::editor {
15
16absl::Status DungeonRoomLoader::LoadRoom(int room_id, zelda3::Room& room) {
17 if (!rom_ || !rom_->is_loaded()) {
18 return absl::FailedPreconditionError("ROM not loaded");
19 }
20 if (room_id < 0 || room_id >= 0x128) {
21 return absl::InvalidArgumentError("Invalid room ID");
22 }
23
24 room = zelda3::LoadRoomFromRom(rom_, room_id);
25 room.LoadObjects();
26
27 return absl::OkStatus();
28}
29
30absl::Status DungeonRoomLoader::LoadAllRooms(std::array<zelda3::Room, 0x128>& rooms) {
31 if (!rom_ || !rom_->is_loaded()) {
32 return absl::FailedPreconditionError("ROM not loaded");
33 }
34
35 constexpr int kTotalRooms = 0x100 + 40; // 296 rooms
36 constexpr int kMaxConcurrency = 8; // Reasonable thread limit for room loading
37
38 // Determine optimal number of threads
39 const int max_concurrency = std::min(kMaxConcurrency,
40 static_cast<int>(std::thread::hardware_concurrency()));
41 const int rooms_per_thread = (kTotalRooms + max_concurrency - 1) / max_concurrency;
42
43 LOG_DEBUG("Dungeon", "Loading %d dungeon rooms using %d threads (%d rooms per thread)",
44 kTotalRooms, max_concurrency, rooms_per_thread);
45
46 // Thread-safe data structures for collecting results
47 std::mutex results_mutex;
48 std::vector<std::pair<int, zelda3::RoomSize>> room_size_results;
49 std::vector<std::pair<int, ImVec4>> room_palette_results;
50
51 // Process rooms in parallel batches
52 std::vector<std::future<absl::Status>> futures;
53
54 for (int thread_id = 0; thread_id < max_concurrency; ++thread_id) {
55 auto task = [this, &rooms, thread_id, rooms_per_thread, &results_mutex,
56 &room_size_results, &room_palette_results, kTotalRooms]() -> absl::Status {
57 const int start_room = thread_id * rooms_per_thread;
58 const int end_room = std::min(start_room + rooms_per_thread, kTotalRooms);
59
60 auto dungeon_man_pal_group = rom_->palette_group().dungeon_main;
61
62 for (int i = start_room; i < end_room; ++i) {
63 // Load room data (this is the expensive operation)
64 rooms[i] = zelda3::LoadRoomFromRom(rom_, i);
65
66 // Calculate room size
67 auto room_size = zelda3::CalculateRoomSize(rom_, i);
68
69 // Load room objects
70 rooms[i].LoadObjects();
71
72 // Process palette
73 auto dungeon_palette_ptr = rom_->paletteset_ids[rooms[i].palette][0];
74 auto palette_id = rom_->ReadWord(0xDEC4B + dungeon_palette_ptr);
75 if (palette_id.status() == absl::OkStatus()) {
76 int p_id = palette_id.value() / 180;
77 auto color = dungeon_man_pal_group[p_id][3];
78
79 // Thread-safe collection of results
80 {
81 std::lock_guard<std::mutex> lock(results_mutex);
82 room_size_results.emplace_back(i, room_size);
83 room_palette_results.emplace_back(rooms[i].palette, color.rgb());
84 }
85 }
86 }
87
88 return absl::OkStatus();
89 };
90
91 futures.emplace_back(std::async(std::launch::async, task));
92 }
93
94 // Wait for all threads to complete
95 for (auto& future : futures) {
96 RETURN_IF_ERROR(future.get());
97 }
98
99 // Process collected results on main thread
100 {
101 gfx::ScopedTimer postprocess_timer("DungeonRoomLoader::PostProcessResults");
102
103 // Sort results by room ID for consistent ordering
104 std::sort(room_size_results.begin(), room_size_results.end(),
105 [](const auto& a, const auto& b) { return a.first < b.first; });
106 std::sort(room_palette_results.begin(), room_palette_results.end(),
107 [](const auto& a, const auto& b) { return a.first < b.first; });
108
109 // Process room size results
110 for (const auto& [room_id, room_size] : room_size_results) {
111 room_size_pointers_.push_back(room_size.room_size_pointer);
112 room_sizes_.push_back(room_size.room_size);
113 if (room_size.room_size_pointer != 0x0A8000) {
114 room_size_addresses_[room_id] = room_size.room_size_pointer;
115 }
116 }
117
118 // Process palette results
119 for (const auto& [palette_id, color] : room_palette_results) {
120 room_palette_[palette_id] = color;
121 }
122 }
123
125 return absl::OkStatus();
126}
127
128absl::Status DungeonRoomLoader::LoadRoomEntrances(std::array<zelda3::RoomEntrance, 0x8C>& entrances) {
129 if (!rom_ || !rom_->is_loaded()) {
130 return absl::FailedPreconditionError("ROM not loaded");
131 }
132
133 // Load entrances
134 for (int i = 0; i < 0x07; ++i) {
135 entrances[i] = zelda3::RoomEntrance(rom_, i, true);
136 }
137
138 for (int i = 0; i < 0x85; ++i) {
139 entrances[i + 0x07] = zelda3::RoomEntrance(rom_, i, false);
140 }
141
142 return absl::OkStatus();
143}
144
146 std::map<int, std::vector<int>> rooms_by_bank;
147 for (const auto& room : room_size_addresses_) {
148 int bank = room.second >> 16;
149 rooms_by_bank[bank].push_back(room.second);
150 }
151
152 // Process and calculate room sizes within each bank
153 for (auto& bank_rooms : rooms_by_bank) {
154 std::ranges::sort(bank_rooms.second);
155
156 for (size_t i = 0; i < bank_rooms.second.size(); ++i) {
157 int room_ptr = bank_rooms.second[i];
158
159 // Identify the room ID for the current room pointer
160 int room_id =
161 std::ranges::find_if(room_size_addresses_, [room_ptr](
162 const auto& entry) {
163 return entry.second == room_ptr;
164 })->first;
165
166 if (room_ptr != 0x0A8000) {
167 if (i < bank_rooms.second.size() - 1) {
168 room_sizes_[room_id] = bank_rooms.second[i + 1] - room_ptr;
169 } else {
170 int bank_end_address = (bank_rooms.first << 16) | 0xFFFF;
171 room_sizes_[room_id] = bank_end_address - room_ptr + 1;
172 }
173 total_room_size_ += room_sizes_[room_id];
174 } else {
175 room_sizes_[room_id] = 0x00;
176 }
177 }
178 }
179}
180
182 if (!rom_ || !rom_->is_loaded()) {
183 return absl::FailedPreconditionError("ROM not loaded");
184 }
185
186 // Load room graphics with proper blockset
187 room.LoadRoomGraphics(room.blockset);
188
189 // Render the room graphics to the graphics arena
190 room.RenderRoomGraphics();
191
192 return absl::OkStatus();
193}
194
195absl::Status DungeonRoomLoader::ReloadAllRoomGraphics(std::array<zelda3::Room, 0x128>& rooms) {
196 if (!rom_ || !rom_->is_loaded()) {
197 return absl::FailedPreconditionError("ROM not loaded");
198 }
199
200 // Reload graphics for all rooms
201 for (auto& room : rooms) {
202 auto status = LoadAndRenderRoomGraphics(room);
203 if (!status.ok()) {
204 continue; // Log error but continue with other rooms
205 }
206 }
207
208 return absl::OkStatus();
209}
210
211} // namespace yaze::editor
auto palette_group() const
Definition rom.h:213
std::array< std::array< uint8_t, 4 >, kNumPalettesets > paletteset_ids
Definition rom.h:228
absl::StatusOr< uint16_t > ReadWord(int offset)
Definition rom.cc:665
bool is_loaded() const
Definition rom.h:197
absl::Status LoadRoomEntrances(std::array< zelda3::RoomEntrance, 0x8C > &entrances)
absl::Status LoadAllRooms(std::array< zelda3::Room, 0x128 > &rooms)
absl::Status LoadRoom(int room_id, zelda3::Room &room)
std::unordered_map< int, int > room_size_addresses_
std::vector< int64_t > room_size_pointers_
absl::Status ReloadAllRoomGraphics(std::array< zelda3::Room, 0x128 > &rooms)
std::unordered_map< int, ImVec4 > room_palette_
absl::Status LoadAndRenderRoomGraphics(zelda3::Room &room)
RAII timer for automatic timing management.
Dungeon Room Entrance or Spawn Point.
void LoadRoomGraphics(uint8_t entrance_blockset=0xFF)
Definition room.cc:195
uint8_t blockset
Definition room.h:349
void RenderRoomGraphics()
Definition room.cc:289
void LoadObjects()
Definition room.cc:606
#define LOG_DEBUG(category, format,...)
Definition log.h:104
#define RETURN_IF_ERROR(expression)
Definition macro.h:53
Editors are the view controllers for the application.
RoomSize CalculateRoomSize(Rom *rom, int room_id)
Definition room.cc:21
Room LoadRoomFromRom(Rom *rom, int room_id)
Definition room.cc:75