yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
song_browser_view.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cstring>
5
6#include "absl/strings/str_format.h"
8#include "imgui/imgui.h"
9
10namespace yaze {
11namespace editor {
12namespace music {
13
15
17 // Search filter
18 ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
19 ImGui::InputTextWithHint("##SongFilter", ICON_MD_SEARCH " Search songs...",
21
22 // Bank Space Management Section
23 if (ImGui::CollapsingHeader(ICON_MD_STORAGE " Bank Space")) {
24 ImGui::Indent(8.0f);
25
26 // Check for expanded music patch
27 if (bank.HasExpandedMusicPatch()) {
28 ImGui::TextColored(ImVec4(0.4f, 0.8f, 0.4f, 1.0f), ICON_MD_CHECK_CIRCLE
29 " Oracle of Secrets expanded music detected");
30 const auto& info = bank.GetExpandedBankInfo();
31 ImGui::TextDisabled("Expanded bank at $%06X, Aux at $%06X",
32 info.main_rom_offset, info.aux_rom_offset);
33 ImGui::Spacing();
34 }
35
36 // Display space for each bank
37 static const char* bank_names[] = {"Overworld", "Dungeon", "Credits",
38 "Expanded", "Auxiliary"};
39 static const MusicBank::Bank banks[] = {
43
44 int num_banks = bank.HasExpandedMusicPatch() ? 5 : 3;
45
46 for (int i = 0; i < num_banks; ++i) {
47 auto space = bank.CalculateSpaceUsage(banks[i]);
48 if (space.total_bytes == 0)
49 continue; // Skip empty/invalid banks
50
51 // Progress bar color based on usage
52 ImVec4 bar_color;
53 if (space.is_critical) {
54 bar_color = ImVec4(0.9f, 0.2f, 0.2f, 1.0f); // Red
55 } else if (space.is_warning) {
56 bar_color = ImVec4(0.9f, 0.7f, 0.2f, 1.0f); // Yellow
57 } else {
58 bar_color = ImVec4(0.3f, 0.7f, 0.3f, 1.0f); // Green
59 }
60
61 ImGui::Text("%s:", bank_names[i]);
62 ImGui::SameLine(100);
63
64 // Progress bar
65 ImGui::PushStyleColor(ImGuiCol_PlotHistogram, bar_color);
66 float fraction = space.usage_percent / 100.0f;
67 std::string overlay =
68 absl::StrFormat("%d / %d bytes (%.1f%%)", space.used_bytes,
69 space.total_bytes, space.usage_percent);
70 ImGui::ProgressBar(fraction, ImVec2(-1, 0), overlay.c_str());
71 ImGui::PopStyleColor();
72
73 // Warning/critical messages
74 if (space.is_critical) {
75 ImGui::TextColored(ImVec4(0.9f, 0.3f, 0.3f, 1.0f), ICON_MD_ERROR " %s",
76 space.recommendation.c_str());
77 } else if (space.is_warning) {
78 ImGui::TextColored(ImVec4(0.9f, 0.7f, 0.2f, 1.0f),
79 ICON_MD_WARNING " %s", space.recommendation.c_str());
80 }
81 }
82
83 // Overall status
84 ImGui::Spacing();
85 if (!bank.AllSongsFit()) {
86 ImGui::TextColored(ImVec4(0.9f, 0.3f, 0.3f, 1.0f),
87 ICON_MD_ERROR " Some banks are overflowing!");
88 ImGui::TextDisabled("Songs won't fit in ROM. Remove or shorten songs.");
89 } else {
90 ImGui::TextColored(ImVec4(0.4f, 0.8f, 0.4f, 1.0f),
91 ICON_MD_CHECK " All songs fit in ROM");
92 }
93
94 ImGui::Unindent(8.0f);
95 }
96
97 ImGui::Separator();
98
99 // Toolbar
100 if (ImGui::Button(ICON_MD_ADD " New Song")) {
101 int new_idx = bank.CreateNewSong("New Song", MusicBank::Bank::Dungeon);
102 if (new_idx >= 0) {
103 selected_song_index_ = new_idx;
105 on_song_selected_(new_idx);
106 if (on_edit_)
107 on_edit_();
108 }
109 }
110 ImGui::SameLine();
111 if (ImGui::Button(ICON_MD_FILE_UPLOAD " Import")) {
112 // TODO: Implement SPC/MML import
113 }
114
115 ImGui::Separator();
116
117 ImGui::BeginChild("SongList", ImVec2(0, 0), true);
118
119 // Vanilla Songs Section
120 if (ImGui::CollapsingHeader(ICON_MD_LIBRARY_MUSIC " Vanilla Songs",
121 ImGuiTreeNodeFlags_DefaultOpen)) {
122 for (size_t i = 0; i < bank.GetSongCount(); ++i) {
123 const auto* song = bank.GetSong(static_cast<int>(i));
124 if (!song || !bank.IsVanilla(static_cast<int>(i)))
125 continue;
126
127 // Filter check
128 std::string display_name = absl::StrFormat("%02X: %s", i + 1, song->name);
129 if (!MatchesSearch(display_name))
130 continue;
131
132 // Icon + label
133 std::string label = absl::StrFormat(ICON_MD_MUSIC_NOTE " %s##vanilla%zu",
134 display_name, i);
135 bool is_selected = (selected_song_index_ == static_cast<int>(i));
136 if (ImGui::Selectable(label.c_str(), is_selected)) {
137 selected_song_index_ = static_cast<int>(i);
138 if (on_song_selected_) {
140 }
141 }
142
143 // Double-click opens tracker
144 if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) {
145 if (on_open_tracker_) {
146 on_open_tracker_(static_cast<int>(i));
147 }
148 }
149
150 // Context menu for vanilla songs
151 if (ImGui::BeginPopupContextItem()) {
152 if (ImGui::MenuItem(ICON_MD_MUSIC_NOTE " Open Tracker")) {
154 on_open_tracker_(static_cast<int>(i));
155 }
156 if (ImGui::MenuItem(ICON_MD_PIANO " Open Piano Roll")) {
158 on_open_piano_roll_(static_cast<int>(i));
159 }
160 ImGui::Separator();
161 if (ImGui::MenuItem(ICON_MD_CONTENT_COPY " Duplicate as Custom")) {
162 bank.DuplicateSong(static_cast<int>(i));
163 if (on_edit_)
164 on_edit_();
165 }
166 ImGui::Separator();
167 if (ImGui::MenuItem(ICON_MD_FILE_DOWNLOAD " Export to ASM...")) {
168 if (on_export_asm_)
169 on_export_asm_(static_cast<int>(i));
170 }
171 ImGui::EndPopup();
172 }
173 }
174 }
175
176 // Custom Songs Section
177 if (ImGui::CollapsingHeader(ICON_MD_EDIT " Custom Songs",
178 ImGuiTreeNodeFlags_DefaultOpen)) {
179 bool has_custom = false;
180 for (size_t i = 0; i < bank.GetSongCount(); ++i) {
181 const auto* song = bank.GetSong(static_cast<int>(i));
182 if (!song || bank.IsVanilla(static_cast<int>(i)))
183 continue;
184
185 has_custom = true;
186
187 // Filter check
188 std::string display_name = absl::StrFormat("%02X: %s", i + 1, song->name);
189 if (!MatchesSearch(display_name))
190 continue;
191
192 // Custom song icon + label (different color)
193 std::string label =
194 absl::StrFormat(ICON_MD_AUDIOTRACK " %s##custom%zu", display_name, i);
195 bool is_selected = (selected_song_index_ == static_cast<int>(i));
196
197 // Highlight custom songs with a subtle green color
198 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.6f, 0.9f, 0.6f, 1.0f));
199 if (ImGui::Selectable(label.c_str(), is_selected)) {
200 selected_song_index_ = static_cast<int>(i);
201 if (on_song_selected_) {
203 }
204 }
205 ImGui::PopStyleColor();
206
207 // Double-click opens tracker
208 if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) {
209 if (on_open_tracker_) {
210 on_open_tracker_(static_cast<int>(i));
211 }
212 }
213
214 // Context menu for custom songs (includes delete/rename)
215 if (ImGui::BeginPopupContextItem()) {
216 if (ImGui::MenuItem(ICON_MD_MUSIC_NOTE " Open Tracker")) {
218 on_open_tracker_(static_cast<int>(i));
219 }
220 if (ImGui::MenuItem(ICON_MD_PIANO " Open Piano Roll")) {
222 on_open_piano_roll_(static_cast<int>(i));
223 }
224 ImGui::Separator();
225 if (ImGui::MenuItem(ICON_MD_CONTENT_COPY " Duplicate")) {
226 bank.DuplicateSong(static_cast<int>(i));
227 if (on_edit_)
228 on_edit_();
229 }
230 if (ImGui::MenuItem(ICON_MD_DRIVE_FILE_RENAME_OUTLINE " Rename")) {
231 rename_target_index_ = static_cast<int>(i);
232 // TODO: Open rename popup
233 }
234 ImGui::Separator();
235 if (ImGui::MenuItem(ICON_MD_FILE_DOWNLOAD " Export to ASM...")) {
236 if (on_export_asm_)
237 on_export_asm_(static_cast<int>(i));
238 }
239 if (ImGui::MenuItem(ICON_MD_FILE_UPLOAD " Import from ASM...")) {
240 if (on_import_asm_)
241 on_import_asm_(static_cast<int>(i));
242 }
243 ImGui::Separator();
244 if (ImGui::MenuItem(ICON_MD_DELETE " Delete")) {
245 (void)bank.DeleteSong(static_cast<int>(i));
246 if (selected_song_index_ == static_cast<int>(i)) {
248 }
249 if (on_edit_)
250 on_edit_();
251 }
252 ImGui::EndPopup();
253 }
254 }
255
256 if (!has_custom) {
257 ImGui::TextDisabled("No custom songs yet");
258 ImGui::TextDisabled("Click 'New Song' or duplicate a vanilla song");
259 }
260 }
261
262 ImGui::EndChild();
263}
264
265bool SongBrowserView::MatchesSearch(const std::string& name) const {
266 if (search_buffer_[0] == '\0')
267 return true;
268
269 // Case-insensitive search
270 std::string lower_name = name;
271 std::string lower_search(search_buffer_);
272 std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(),
273 ::tolower);
274 std::transform(lower_search.begin(), lower_search.end(), lower_search.begin(),
275 ::tolower);
276
277 return lower_name.find(lower_search) != std::string::npos;
278}
279
280} // namespace music
281} // namespace editor
282} // namespace yaze
std::function< void(int)> on_open_piano_roll_
std::function< void(int)> on_song_selected_
std::function< void(int)> on_export_asm_
bool MatchesSearch(const std::string &name) const
std::function< void(int)> on_open_tracker_
void Draw(MusicBank &bank)
Draw the song browser.
std::function< void(int)> on_import_asm_
Manages the collection of songs, instruments, and samples from a ROM.
Definition music_bank.h:27
bool IsVanilla(int index) const
Check if a song is a vanilla (original) song.
MusicSong * GetSong(int index)
Get a song by index.
size_t GetSongCount() const
Get the number of songs loaded.
Definition music_bank.h:97
const ExpandedBankInfo & GetExpandedBankInfo() const
Get information about the expanded bank configuration.
Definition music_bank.h:160
bool AllSongsFit() const
Check if all songs fit in their banks.
absl::Status DeleteSong(int index)
Delete a song by index.
SpaceInfo CalculateSpaceUsage(Bank bank) const
Calculate space usage for a bank.
int CreateNewSong(const std::string &name, Bank bank)
Create a new empty song.
bool HasExpandedMusicPatch() const
Check if the ROM has the Oracle of Secrets expanded music patch.
Definition music_bank.h:147
int DuplicateSong(int index)
Duplicate a song.
#define ICON_MD_PIANO
Definition icons.h:1462
#define ICON_MD_LIBRARY_MUSIC
Definition icons.h:1080
#define ICON_MD_STORAGE
Definition icons.h:1865
#define ICON_MD_WARNING
Definition icons.h:2123
#define ICON_MD_SEARCH
Definition icons.h:1673
#define ICON_MD_CHECK
Definition icons.h:397
#define ICON_MD_FILE_DOWNLOAD
Definition icons.h:744
#define ICON_MD_EDIT
Definition icons.h:645
#define ICON_MD_AUDIOTRACK
Definition icons.h:213
#define ICON_MD_FILE_UPLOAD
Definition icons.h:749
#define ICON_MD_ERROR
Definition icons.h:686
#define ICON_MD_MUSIC_NOTE
Definition icons.h:1264
#define ICON_MD_ADD
Definition icons.h:86
#define ICON_MD_CHECK_CIRCLE
Definition icons.h:400
#define ICON_MD_DELETE
Definition icons.h:530
#define ICON_MD_CONTENT_COPY
Definition icons.h:465
#define ICON_MD_DRIVE_FILE_RENAME_OUTLINE
Definition icons.h:630