yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
resource_labels.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cctype>
5#include <sstream>
6#include <string>
7#include <vector>
8
9#include "absl/strings/str_format.h"
10#include "absl/strings/str_split.h"
11#include "absl/strings/strip.h"
14
15// External declarations for Hyrule Magic sprite names (defined in sprite.cc)
16namespace yaze::zelda3 {
17extern const char* const kSpriteNames[];
18extern const size_t kSpriteNameCount;
19} // namespace yaze::zelda3
20
21namespace yaze {
22namespace zelda3 {
23
24// ============================================================================
25// Resource Type String Conversion
26// ============================================================================
27
29 switch (type) {
31 return "sprite";
33 return "room";
35 return "entrance";
37 return "item";
39 return "overlord";
41 return "overworld_map";
43 return "music";
45 return "graphics";
47 return "room_effect";
49 return "room_tag";
51 return "tile_type";
52 }
53 return "unknown";
54}
55
56ResourceType StringToResourceType(const std::string& type_str) {
57 if (type_str == "sprite")
59 if (type_str == "room")
61 if (type_str == "entrance")
63 if (type_str == "item")
65 if (type_str == "overlord")
67 if (type_str == "overworld_map")
69 if (type_str == "music")
71 if (type_str == "graphics")
73 if (type_str == "room_effect")
75 if (type_str == "room_tag")
77 if (type_str == "tile_type")
79 // Default fallback
81}
82
83// ============================================================================
84// Global Provider Instance
85// ============================================================================
86
88 static ResourceLabelProvider instance;
89 return instance;
90}
91
92// ============================================================================
93// ResourceLabelProvider Implementation
94// ============================================================================
95
96std::string ResourceLabelProvider::GetLabel(ResourceType type, int id) const {
97 std::string type_str = ResourceTypeToString(type);
98
99 // 1. Check project-specific labels first
100 if (project_labels_) {
101 auto type_it = project_labels_->find(type_str);
102 if (type_it != project_labels_->end()) {
103 auto label_it = type_it->second.find(std::to_string(id));
104 if (label_it != type_it->second.end() && !label_it->second.empty()) {
105 return label_it->second;
106 }
107 }
108 }
109
110 // 2. For sprites, check Hyrule Magic names if preferred
111 if (type == ResourceType::kSprite && prefer_hmagic_) {
112 std::string hmagic = GetHMagicLabel(type, id);
113 if (!hmagic.empty()) {
114 return hmagic;
115 }
116 }
117
118 // 3. Fall back to vanilla labels
119 return GetVanillaLabel(type, id);
120}
121
122std::string ResourceLabelProvider::GetLabel(const std::string& type_str,
123 int id) const {
124 ResourceType type = StringToResourceType(type_str);
125 return GetLabel(type, id);
126}
127
129 const std::string& label) {
130 if (!project_labels_) {
131 return;
132 }
133 std::string type_str = ResourceTypeToString(type);
134 (*project_labels_)[type_str][std::to_string(id)] = label;
135}
136
138 if (!project_labels_) {
139 return;
140 }
141 std::string type_str = ResourceTypeToString(type);
142 auto type_it = project_labels_->find(type_str);
143 if (type_it != project_labels_->end()) {
144 type_it->second.erase(std::to_string(id));
145 }
146}
147
149 if (!project_labels_) {
150 return false;
151 }
152 std::string type_str = ResourceTypeToString(type);
153 auto type_it = project_labels_->find(type_str);
154 if (type_it == project_labels_->end()) {
155 return false;
156 }
157 return type_it->second.find(std::to_string(id)) != type_it->second.end();
158}
159
161 int id) const {
162 switch (type) {
164 const auto& names = Zelda3Labels::GetSpriteNames();
165 if (id >= 0 && static_cast<size_t>(id) < names.size()) {
166 return names[id];
167 }
168 return absl::StrFormat("Sprite %02X", id);
169 }
170 case ResourceType::kRoom: {
171 const auto& names = Zelda3Labels::GetRoomNames();
172 if (id >= 0 && static_cast<size_t>(id) < names.size()) {
173 return names[id];
174 }
175 return absl::StrFormat("Room %03X", id);
176 }
178 const auto& names = Zelda3Labels::GetEntranceNames();
179 if (id >= 0 && static_cast<size_t>(id) < names.size()) {
180 return names[id];
181 }
182 return absl::StrFormat("Entrance %02X", id);
183 }
184 case ResourceType::kItem: {
185 const auto& names = Zelda3Labels::GetItemNames();
186 if (id >= 0 && static_cast<size_t>(id) < names.size()) {
187 return names[id];
188 }
189 return absl::StrFormat("Item %02X", id);
190 }
192 const auto& names = Zelda3Labels::GetOverlordNames();
193 if (id >= 0 && static_cast<size_t>(id) < names.size()) {
194 return names[id];
195 }
196 return absl::StrFormat("Overlord %02X", id);
197 }
199 const auto& names = Zelda3Labels::GetOverworldMapNames();
200 if (id >= 0 && static_cast<size_t>(id) < names.size()) {
201 return names[id];
202 }
203 return absl::StrFormat("Map %02X", id);
204 }
206 const auto& names = Zelda3Labels::GetMusicTrackNames();
207 if (id >= 0 && static_cast<size_t>(id) < names.size()) {
208 return names[id];
209 }
210 return absl::StrFormat("Music %02X", id);
211 }
213 const auto& names = Zelda3Labels::GetGraphicsSheetNames();
214 if (id >= 0 && static_cast<size_t>(id) < names.size()) {
215 return names[id];
216 }
217 return absl::StrFormat("GFX %02X", id);
218 }
220 const auto& names = Zelda3Labels::GetRoomEffectNames();
221 if (id >= 0 && static_cast<size_t>(id) < names.size()) {
222 return names[id];
223 }
224 return absl::StrFormat("Effect %02X", id);
225 }
227 const auto& names = Zelda3Labels::GetRoomTagNames();
228 if (id >= 0 && static_cast<size_t>(id) < names.size()) {
229 return names[id];
230 }
231 return absl::StrFormat("Tag %02X", id);
232 }
234 const auto& names = Zelda3Labels::GetTileTypeNames();
235 if (id >= 0 && static_cast<size_t>(id) < names.size()) {
236 return names[id];
237 }
238 return absl::StrFormat("Tile %02X", id);
239 }
240 }
241 return absl::StrFormat("Unknown %02X", id);
242}
243
245 int id) const {
246 // Hyrule Magic names are only available for sprites
247 if (type != ResourceType::kSprite) {
248 return "";
249 }
250
251 // Use the kSpriteNames array from sprite_names.h
252 if (id >= 0 && static_cast<size_t>(id) < kSpriteNameCount) {
253 return kSpriteNames[id];
254 }
255 return "";
256}
257
259 switch (type) {
261 return 256;
263 return 297;
265 return 133;
267 return static_cast<int>(Zelda3Labels::GetItemNames().size());
269 return static_cast<int>(Zelda3Labels::GetOverlordNames().size());
271 return 160;
273 return static_cast<int>(Zelda3Labels::GetMusicTrackNames().size());
275 return static_cast<int>(Zelda3Labels::GetGraphicsSheetNames().size());
277 return static_cast<int>(Zelda3Labels::GetRoomEffectNames().size());
279 return static_cast<int>(Zelda3Labels::GetRoomTagNames().size());
281 return static_cast<int>(Zelda3Labels::GetTileTypeNames().size());
282 }
283 return 0;
284}
285
288 if (!project_labels_) {
289 return nullptr;
290 }
291 std::string type_str = ResourceTypeToString(type);
292 auto it = project_labels_->find(type_str);
293 if (it == project_labels_->end()) {
294 return nullptr;
295 }
296 return &it->second;
297}
298
304
305// ============================================================================
306// ZScream DefaultNames.txt Import/Export
307// ============================================================================
308
310 const std::string& content) {
311 if (!project_labels_) {
312 return absl::FailedPreconditionError(
313 "Project labels not initialized. Open a project first.");
314 }
315
316 std::istringstream stream(content);
317 std::string line;
318 std::string current_section;
319 int line_index = 0;
320 int line_number = 0;
321
322 while (std::getline(stream, line)) {
323 line_number++;
324
325 // Trim whitespace
326 std::string trimmed = std::string(absl::StripAsciiWhitespace(line));
327
328 // Skip empty lines and comments
329 if (trimmed.empty() || trimmed.substr(0, 2) == "//") {
330 continue;
331 }
332
333 // Check for section headers
334 if (trimmed.front() == '[' && trimmed.back() == ']') {
335 current_section = trimmed.substr(1, trimmed.length() - 2);
336 line_index = 0; // Reset line index for new section
337 continue;
338 }
339
340 // Parse the line based on current section
341 if (!current_section.empty()) {
342 ParseZScreamLine(trimmed, current_section, line_index);
343 }
344 }
345
346 return absl::OkStatus();
347}
348
349bool ResourceLabelProvider::ParseZScreamLine(const std::string& line,
350 const std::string& section,
351 int& line_index) {
352 if (!project_labels_) {
353 return false;
354 }
355
356 // Determine resource type and format based on section
357 std::string resource_type;
358 bool has_hex_prefix = false;
359
360 if (section == "Sprites Names") {
361 resource_type = "sprite";
362 has_hex_prefix = true; // Format: "00 Raven"
363 } else if (section == "Rooms Names") {
364 resource_type = "room";
365 has_hex_prefix = false; // Format: just "Room Name" by line order
366 } else if (section == "Chests Items") {
367 resource_type = "item";
368 has_hex_prefix = true; // Format: "00 Fighter sword and shield"
369 } else if (section == "Tags Names") {
370 resource_type = "room_tag";
371 has_hex_prefix = false; // Format: just "Tag Name" by line order
372 } else {
373 // Unknown section, skip
374 return false;
375 }
376
377 std::string id_str;
378 std::string label;
379
380 if (has_hex_prefix) {
381 // Format: "XX Label Name" where XX is hex
382 size_t space_pos = line.find(' ');
383 if (space_pos == std::string::npos || space_pos < 2) {
384 // No space found or too short for hex prefix
385 return false;
386 }
387
388 std::string hex_str = line.substr(0, space_pos);
389 label = line.substr(space_pos + 1);
390
391 // Parse hex to int
392 try {
393 int id = std::stoi(hex_str, nullptr, 16);
394 id_str = std::to_string(id);
395 } catch (...) {
396 return false;
397 }
398 } else {
399 // Line index determines the ID
400 id_str = std::to_string(line_index);
401 label = line;
402 line_index++;
403 }
404
405 // Trim the label
406 label = std::string(absl::StripAsciiWhitespace(label));
407
408 // Store the label
409 if (!label.empty()) {
410 (*project_labels_)[resource_type][id_str] = label;
411 }
412
413 return true;
414}
415
417 std::ostringstream output;
418
419 output << "//Do not use brackets [] in naming\n";
420
421 // Export sprites
422 output << "[Sprites Names]\n";
423 for (int i = 0; i < 256; ++i) {
424 std::string label = GetLabel(ResourceType::kSprite, i);
425 output << absl::StrFormat("%02X %s\n", i, label);
426 }
427
428 // Export rooms
429 output << "\n[Rooms Names]\n";
430 for (int i = 0; i < 297; ++i) {
431 std::string label = GetLabel(ResourceType::kRoom, i);
432 output << label << "\n";
433 }
434
435 // Export items
436 output << "\n[Chests Items]\n";
437 int item_count = GetResourceCount(ResourceType::kItem);
438 for (int i = 0; i < item_count; ++i) {
439 std::string label = GetLabel(ResourceType::kItem, i);
440 output << absl::StrFormat("%02X %s\n", i, label);
441 }
442
443 // Export room tags
444 output << "\n[Tags Names]\n";
446 for (int i = 0; i < tag_count; ++i) {
447 std::string label = GetLabel(ResourceType::kRoomTag, i);
448 output << label << "\n";
449 }
450
451 return output.str();
452}
453
454} // namespace zelda3
455} // namespace yaze
456
Unified interface for accessing resource labels with project overrides.
std::string ExportToZScreamFormat() const
Export project labels to ZScream DefaultNames.txt format.
void SetProjectLabel(ResourceType type, int id, const std::string &label)
Set a project-specific label override.
bool HasProjectLabel(ResourceType type, int id) const
Check if a project-specific label exists.
void ClearAllProjectLabels()
Clear all project labels.
int GetResourceCount(ResourceType type) const
Get the count of resources for a given type.
std::unordered_map< std::string, std::string > LabelMap
std::string GetLabel(ResourceType type, int id) const
Get a label for a resource by type and ID.
bool ParseZScreamLine(const std::string &line, const std::string &section, int &line_index)
absl::Status ImportFromZScreamFormat(const std::string &content)
Import labels from ZScream DefaultNames.txt format.
std::string GetVanillaLabel(ResourceType type, int id) const
Get the vanilla (default) label for a resource.
const LabelMap * GetProjectLabelsForType(ResourceType type) const
Get all project labels for a given type.
std::string GetHMagicLabel(ResourceType type, int id) const
Get the Hyrule Magic label for a resource (sprites only)
void ClearProjectLabel(ResourceType type, int id)
Clear a project-specific label (revert to default)
Zelda 3 specific classes and functions.
Definition editor.h:35
ResourceType
Enumeration of all supported resource types for labeling.
const size_t kSpriteNameCount
Definition sprite.cc:292
const char *const kSpriteNames[]
Definition sprite.cc:291
std::string ResourceTypeToString(ResourceType type)
Convert ResourceType enum to string key for storage.
ResourceType StringToResourceType(const std::string &type_str)
Convert string key to ResourceType enum.
ResourceLabelProvider & GetResourceLabels()
Get the global ResourceLabelProvider instance.
static const std::vector< std::string > & GetRoomNames()
static const std::vector< std::string > & GetItemNames()
static const std::vector< std::string > & GetEntranceNames()
static const std::vector< std::string > & GetGraphicsSheetNames()
static const std::vector< std::string > & GetMusicTrackNames()
static const std::vector< std::string > & GetTileTypeNames()
static const std::vector< std::string > & GetOverlordNames()
static const std::vector< std::string > & GetSpriteNames()
static const std::vector< std::string > & GetOverworldMapNames()
static const std::vector< std::string > & GetRoomEffectNames()
static const std::vector< std::string > & GetRoomTagNames()