13#include "absl/strings/str_format.h"
14#include "absl/strings/str_split.h"
15#include "absl/strings/strip.h"
16#include "absl/strings/match.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)
return std::nullopt;
57 uint32_t addr = std::stoul(clean, &pos, 16);
58 if (pos != clean.size())
return std::nullopt;
67 if (name.empty())
return false;
70 if (!std::isalpha(first) && first !=
'_' && first !=
'.')
return false;
72 for (
size_t i = 1; i < name.size(); ++i) {
74 if (!std::isalnum(c) && c !=
'_' && c !=
'.')
return false;
82 size_t starPos = std::string::npos;
85 while (s < str.size()) {
86 if (p < pattern.size() && (pattern[p] == str[s] || pattern[p] ==
'?')) {
89 }
else if (p < pattern.size() && pattern[p] ==
'*') {
92 }
else if (starPos != std::string::npos) {
100 while (p < pattern.size() && pattern[p] ==
'*') ++p;
101 return p == pattern.size();
106 size_t pos = path.find_last_of(
"/\\");
107 if (pos == std::string::npos)
return path;
108 return path.substr(pos + 1);
113 size_t pos = filename.find_last_of(
'.');
114 if (pos == std::string::npos)
return "";
115 return filename.substr(pos);
121 auto content_or = ReadFileContent(path);
122 if (!content_or.ok()) {
123 return content_or.status();
130 const std::string& directory_path) {
134 (void)directory_path;
135 return absl::UnimplementedError(
136 "Directory loading not supported in browser builds. "
137 "Please load individual symbol files.");
139 std::filesystem::path dir(directory_path);
140 if (!std::filesystem::exists(dir)) {
141 return absl::NotFoundError(
142 absl::StrFormat(
"Directory not found: %s", directory_path));
145 int files_loaded = 0;
146 for (
const auto& entry : std::filesystem::directory_iterator(dir)) {
147 if (entry.is_regular_file()) {
148 auto ext = entry.path().extension().string();
149 if (ext ==
".asm" || ext ==
".s") {
158 if (files_loaded == 0) {
159 return absl::NotFoundError(
"No ASM files found in directory");
162 return absl::OkStatus();
168 auto content_or = ReadFileContent(path);
169 if (!content_or.ok()) {
170 return content_or.status();
173 const std::string& content = *content_or;
174 std::string ext = GetExtension(path);
192 return absl::InvalidArgumentError(
"Unknown symbol format");
202 for (
const auto& sym : symbols) {
215 return it->second.name;
229 uint32_t address)
const {
230 std::vector<Symbol> result;
232 for (
auto it = range.first; it != range.second; ++it) {
233 result.push_back(it->second);
239 const std::string& name)
const {
248 const std::string& pattern)
const {
249 std::vector<Symbol> result;
251 if (WildcardMatch(pattern, name)) {
252 result.push_back(sym);
259 uint32_t end)
const {
260 std::vector<Symbol> result;
263 for (
auto it = it_start; it != it_end; ++it) {
264 result.push_back(it->second);
270 uint32_t address)
const {
287 uint32_t max_offset)
const {
297 uint32_t offset = address - nearest->address;
298 if (offset <= max_offset) {
299 return absl::StrFormat(
"%s+$%X", nearest->name, offset);
304 return absl::StrFormat(
"$%06X", address);
308 return [
this](uint32_t address) -> std::string {
314 const std::string& filename) {
315 std::istringstream stream(content);
319 std::string current_label;
320 uint32_t last_address = 0;
324 std::regex label_regex(R
"(^([A-Za-z_][A-Za-z0-9_]*):)");
326 std::regex local_label_regex(R
"(^(\.[A-Za-z_][A-Za-z0-9_]*))");
328 std::regex address_regex(R
"(^#_([0-9A-Fa-f]{6}):)");
330 bool pending_label =
false;
331 std::string pending_label_name;
332 bool pending_is_local =
false;
334 while (std::getline(stream, line)) {
338 std::string trimmed = std::string(absl::StripAsciiWhitespace(line));
339 if (trimmed.empty() || trimmed[0] ==
';')
continue;
344 if (std::regex_search(line, match, address_regex)) {
345 auto addr = ParseAddress(match[1].str());
347 last_address = *addr;
352 sym.
name = pending_label_name;
355 sym.
line = line_number;
359 pending_label =
false;
365 if (line[0] !=
' ' && line[0] !=
'\t' && line[0] !=
'#') {
366 if (std::regex_search(line, match, label_regex)) {
367 current_label = match[1].str();
368 pending_label =
true;
369 pending_label_name = current_label;
370 pending_is_local =
false;
375 if (std::regex_search(trimmed, match, local_label_regex)) {
376 std::string local_name = match[1].str();
378 std::string full_name = current_label.empty()
380 : current_label + local_name;
381 pending_label =
true;
382 pending_label_name = full_name;
383 pending_is_local =
true;
387 return absl::OkStatus();
396 std::istringstream stream(content);
398 bool in_labels_section =
false;
400 std::regex label_regex(R
"(^([0-9A-Fa-f]{2}):([0-9A-Fa-f]{4})\s+(\S+))");
402 while (std::getline(stream, line)) {
403 std::string trimmed = std::string(absl::StripAsciiWhitespace(line));
405 if (trimmed ==
"[labels]") {
406 in_labels_section =
true;
409 if (trimmed.empty() || trimmed[0] ==
'[') {
410 if (trimmed[0] ==
'[') in_labels_section =
false;
414 if (!in_labels_section)
continue;
417 if (std::regex_search(trimmed, match, label_regex)) {
418 uint32_t bank = std::stoul(match[1].str(),
nullptr, 16);
419 uint32_t offset = std::stoul(match[2].str(),
nullptr, 16);
420 uint32_t address = (bank << 16) | offset;
421 std::string name = match[3].str();
423 Symbol sym(name, address);
428 return absl::OkStatus();
437 std::istringstream stream(content);
440 std::regex label_regex(R
"(^(?:PRG:)?([0-9A-Fa-f]+):(\S+))");
442 while (std::getline(stream, line)) {
443 std::string trimmed = std::string(absl::StripAsciiWhitespace(line));
444 if (trimmed.empty() || trimmed[0] ==
';')
continue;
447 if (std::regex_search(trimmed, match, label_regex)) {
448 auto addr = ParseAddress(match[1].str());
450 Symbol sym(match[2].str(), *addr);
456 return absl::OkStatus();
464 std::istringstream stream(content);
467 std::regex label_regex(R
"(^([0-9A-Fa-f]{6})\s+(\S+))");
469 while (std::getline(stream, line)) {
470 std::string trimmed = std::string(absl::StripAsciiWhitespace(line));
471 if (trimmed.empty() || trimmed[0] ==
';' || trimmed[0] ==
'#')
continue;
474 if (std::regex_search(trimmed, match, label_regex)) {
475 auto addr = ParseAddress(match[1].str());
477 Symbol sym(match[2].str(), *addr);
483 return absl::OkStatus();
487 const std::string& extension)
const {
489 if (extension ==
".asm" || extension ==
".s") {
492 if (extension ==
".mlb") {
497 if (content.find(
"[labels]") != std::string::npos) {
500 if (content.find(
"PRG:") != std::string::npos) {
503 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)