13#include "absl/strings/match.h"
14#include "absl/strings/str_format.h"
15#include "absl/strings/str_split.h"
16#include "absl/strings/strip.h"
26 std::ifstream file(path);
27 if (!file.is_open()) {
28 return absl::NotFoundError(
29 absl::StrFormat(
"Failed to open file: %s", path));
31 std::stringstream buffer;
32 buffer << file.rdbuf();
38 std::string clean = str;
40 if (!clean.empty() && clean[0] ==
'$') {
41 clean = clean.substr(1);
44 if (clean.size() >= 2 && clean[0] ==
'0' &&
45 (clean[1] ==
'x' || clean[1] ==
'X')) {
46 clean = clean.substr(2);
49 if (!clean.empty() && clean.back() ==
':') {
53 if (clean.empty() || clean.size() > 6)
58 uint32_t addr = std::stoul(clean, &pos, 16);
59 if (pos != clean.size())
73 if (!std::isalpha(first) && first !=
'_' && first !=
'.')
76 for (
size_t i = 1; i < name.size(); ++i) {
78 if (!std::isalnum(c) && c !=
'_' && c !=
'.')
87 size_t starPos = std::string::npos;
90 while (s < str.size()) {
91 if (p < pattern.size() && (pattern[p] == str[s] || pattern[p] ==
'?')) {
94 }
else if (p < pattern.size() && pattern[p] ==
'*') {
97 }
else if (starPos != std::string::npos) {
105 while (p < pattern.size() && pattern[p] ==
'*')
107 return p == pattern.size();
112 size_t pos = path.find_last_of(
"/\\");
113 if (pos == std::string::npos)
115 return path.substr(pos + 1);
120 size_t pos = filename.find_last_of(
'.');
121 if (pos == std::string::npos)
123 return filename.substr(pos);
129 auto content_or = ReadFileContent(path);
130 if (!content_or.ok()) {
131 return content_or.status();
138 const std::string& directory_path) {
142 (void)directory_path;
143 return absl::UnimplementedError(
144 "Directory loading not supported in browser builds. "
145 "Please load individual symbol files.");
147 std::filesystem::path dir(directory_path);
148 if (!std::filesystem::exists(dir)) {
149 return absl::NotFoundError(
150 absl::StrFormat(
"Directory not found: %s", directory_path));
153 int files_loaded = 0;
154 for (
const auto& entry : std::filesystem::directory_iterator(dir)) {
155 if (entry.is_regular_file()) {
156 auto ext = entry.path().extension().string();
157 if (ext ==
".asm" || ext ==
".s") {
166 if (files_loaded == 0) {
167 return absl::NotFoundError(
"No ASM files found in directory");
170 return absl::OkStatus();
176 auto content_or = ReadFileContent(path);
177 if (!content_or.ok()) {
178 return content_or.status();
181 const std::string& content = *content_or;
182 std::string ext = GetExtension(path);
200 return absl::InvalidArgumentError(
"Unknown symbol format");
210 for (
const auto& sym : symbols) {
223 return it->second.name;
237 uint32_t address)
const {
238 std::vector<Symbol> result;
240 for (
auto it = range.first; it != range.second; ++it) {
241 result.push_back(it->second);
247 const std::string& name)
const {
256 const std::string& pattern)
const {
257 std::vector<Symbol> result;
259 if (WildcardMatch(pattern, name)) {
260 result.push_back(sym);
267 uint32_t end)
const {
268 std::vector<Symbol> result;
271 for (
auto it = it_start; it != it_end; ++it) {
272 result.push_back(it->second);
295 uint32_t max_offset)
const {
305 uint32_t offset = address - nearest->address;
306 if (offset <= max_offset) {
307 return absl::StrFormat(
"%s+$%X", nearest->name, offset);
312 return absl::StrFormat(
"$%06X", address);
316 return [
this](uint32_t address) -> std::string {
322 const std::string& filename) {
323 std::istringstream stream(content);
327 std::string current_label;
328 uint32_t last_address = 0;
332 std::regex label_regex(R
"(^([A-Za-z_][A-Za-z0-9_]*):)");
334 std::regex local_label_regex(R
"(^(\.[A-Za-z_][A-Za-z0-9_]*))");
336 std::regex address_regex(R
"(^#_([0-9A-Fa-f]{6}):)");
338 bool pending_label =
false;
339 std::string pending_label_name;
340 bool pending_is_local =
false;
342 while (std::getline(stream, line)) {
346 std::string trimmed = std::string(absl::StripAsciiWhitespace(line));
347 if (trimmed.empty() || trimmed[0] ==
';')
353 if (std::regex_search(line, match, address_regex)) {
354 auto addr = ParseAddress(match[1].str());
356 last_address = *addr;
361 sym.
name = pending_label_name;
364 sym.
line = line_number;
368 pending_label =
false;
374 if (line[0] !=
' ' && line[0] !=
'\t' && line[0] !=
'#') {
375 if (std::regex_search(line, match, label_regex)) {
376 current_label = match[1].str();
377 pending_label =
true;
378 pending_label_name = current_label;
379 pending_is_local =
false;
384 if (std::regex_search(trimmed, match, local_label_regex)) {
385 std::string local_name = match[1].str();
387 std::string full_name =
388 current_label.empty() ? local_name : current_label + local_name;
389 pending_label =
true;
390 pending_label_name = full_name;
391 pending_is_local =
true;
395 return absl::OkStatus();
404 std::istringstream stream(content);
406 bool in_labels_section =
false;
408 std::regex label_regex(R
"(^([0-9A-Fa-f]{2}):([0-9A-Fa-f]{4})\s+(\S+))");
410 while (std::getline(stream, line)) {
411 std::string trimmed = std::string(absl::StripAsciiWhitespace(line));
413 if (trimmed ==
"[labels]") {
414 in_labels_section =
true;
417 if (trimmed.empty() || trimmed[0] ==
'[') {
418 if (trimmed[0] ==
'[')
419 in_labels_section =
false;
423 if (!in_labels_section)
427 if (std::regex_search(trimmed, match, label_regex)) {
428 uint32_t bank = std::stoul(match[1].str(),
nullptr, 16);
429 uint32_t offset = std::stoul(match[2].str(),
nullptr, 16);
430 uint32_t address = (bank << 16) | offset;
431 std::string name = match[3].str();
433 Symbol sym(name, address);
438 return absl::OkStatus();
447 std::istringstream stream(content);
450 std::regex label_regex(R
"(^(?:PRG:)?([0-9A-Fa-f]+):(\S+))");
452 while (std::getline(stream, line)) {
453 std::string trimmed = std::string(absl::StripAsciiWhitespace(line));
454 if (trimmed.empty() || trimmed[0] ==
';')
458 if (std::regex_search(trimmed, match, label_regex)) {
459 auto addr = ParseAddress(match[1].str());
461 Symbol sym(match[2].str(), *addr);
467 return absl::OkStatus();
475 std::istringstream stream(content);
478 std::regex label_regex(R
"(^([0-9A-Fa-f]{6})\s+(\S+))");
480 while (std::getline(stream, line)) {
481 std::string trimmed = std::string(absl::StripAsciiWhitespace(line));
482 if (trimmed.empty() || trimmed[0] ==
';' || trimmed[0] ==
'#')
486 if (std::regex_search(trimmed, match, label_regex)) {
487 auto addr = ParseAddress(match[1].str());
489 Symbol sym(match[2].str(), *addr);
495 return absl::OkStatus();
499 const std::string& extension)
const {
501 if (extension ==
".asm" || extension ==
".s") {
504 if (extension ==
".mlb") {
509 if (content.find(
"[labels]") != std::string::npos) {
512 if (content.find(
"PRG:") != std::string::npos) {
515 if (content.find(
"#_") != std::string::npos) {
std::function< std::string(uint32_t)> CreateResolver() const
Create a symbol resolver function for the disassembler.
std::vector< Symbol > GetSymbolsInRange(uint32_t start, uint32_t end) const
Get all symbols in an address range.
std::string FormatAddress(uint32_t address, uint32_t max_offset=0x100) const
Format an address with symbol info.
void AddSymbol(const Symbol &symbol)
Add a single symbol manually.
std::map< std::string, Symbol > symbols_by_name_
std::vector< Symbol > GetSymbolsAtAddress(uint32_t address) const
Get all symbols at an address (there may be multiple)
void AddAsarSymbols(const std::vector< Symbol > &symbols)
Add symbols from Asar patch results.
absl::Status ParseBsnesSymFile(const std::string &content)
absl::Status LoadAsarAsmDirectory(const std::string &directory_path)
Load symbols from a directory of ASM files.
absl::Status ParseWlaDxSymFile(const std::string &content)
std::multimap< uint32_t, Symbol > symbols_by_address_
absl::Status LoadAsarAsmFile(const std::string &path)
Load symbols from an Asar-style ASM file (usdasm format)
SymbolFormat DetectFormat(const std::string &content, const std::string &extension) const
absl::Status LoadSymbolFile(const std::string &path, SymbolFormat format=SymbolFormat::kAuto)
Load symbols from a .sym file (various formats)
std::string GetSymbolName(uint32_t address) const
Get symbol name for an address.
void Clear()
Clear all loaded symbols.
std::optional< Symbol > GetNearestSymbol(uint32_t address) const
Get nearest symbol at or before an address.
absl::Status ParseAsarAsmContent(const std::string &content, const std::string &filename)
absl::Status ParseMesenMlbFile(const std::string &content)
std::optional< Symbol > GetSymbol(uint32_t address) const
Get full symbol info for an address.
std::optional< Symbol > FindSymbol(const std::string &name) const
Find symbol by name.
std::vector< Symbol > FindSymbolsMatching(const std::string &pattern) const
Find symbols matching a pattern (supports wildcards)
std::string GetExtension(const std::string &path)
bool WildcardMatch(const std::string &pattern, const std::string &str)
bool IsValidLabelName(const std::string &name)
absl::StatusOr< std::string > ReadFileContent(const std::string &path)
std::string GetFilename(const std::string &path)
std::optional< uint32_t > ParseAddress(const std::string &str)
SymbolFormat
Supported symbol file formats.
Information about a symbol (label, constant, or address)