diff --git a/UI/Debugger/Integration/PceasSymbolFile.cs b/UI/Debugger/Integration/LegacyPceasSymbolFile.cs similarity index 99% rename from UI/Debugger/Integration/PceasSymbolFile.cs rename to UI/Debugger/Integration/LegacyPceasSymbolFile.cs index 372b0d4ae..58148bd92 100644 --- a/UI/Debugger/Integration/PceasSymbolFile.cs +++ b/UI/Debugger/Integration/LegacyPceasSymbolFile.cs @@ -12,7 +12,7 @@ namespace Mesen.Debugger.Integration; -public class PceasSymbolFile +public class LegacyPceasSymbolFile { public static bool IsValidFile(string content) { diff --git a/UI/Debugger/Integration/PceasSymbolImporter.cs b/UI/Debugger/Integration/PceasSymbolImporter.cs new file mode 100644 index 000000000..8d9de18ae --- /dev/null +++ b/UI/Debugger/Integration/PceasSymbolImporter.cs @@ -0,0 +1,372 @@ +using Mesen.Config; +using Mesen.Debugger.Labels; +using Mesen.Interop; +using Mesen.Utilities; +using Mesen.Windows; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Mesen.Debugger.Integration; + +public class PceasSymbolImporter : ISymbolProvider +{ + private static Regex _definitionRegex = new Regex(@"^([0-9a-fA-F]{8}) ([^\s]*)", RegexOptions.Compiled); + private static Regex _symbolRegex = new Regex(@"^([0-9a-fA-F]{2,4})[:]{0,1}([0-9a-fA-F]{4}) ([0-9a-fA-F]{8}) ([0-9a-fA-F]{4}):([0-9a-fA-F]{8}) ([^\s]*)", RegexOptions.Compiled); + private static Regex _fileRegex = new Regex(@"^([0-9a-fA-F]{4}):([0-9a-fA-F]{4}) ([0-9a-fA-F]{8}) (.*)", RegexOptions.Compiled); + private static Regex _filePathRegex = new Regex(@"^(""([^;""]*)""\s*;{0,1}\s*(.*))|(.*)", RegexOptions.Compiled); + private static Regex _srcMappingRegex = new Regex(@"^([0-9a-fA-F]{2}):([0-9a-fA-F]{4}) ([0-9a-fA-F]{8}) ([0-9a-fA-F]{4}):([0-9a-fA-F]{8})", RegexOptions.Compiled); + + private Dictionary _sourceFiles = new(); + private Dictionary _addressByLine = new(); + private Dictionary _linesByAddress = new(); + private List _symbols = new(); + private RomFormat _format; + + public DateTime SymbolFileStamp { get; private set; } + public string SymbolPath { get; private set; } = ""; + + public List SourceFiles { get { return _sourceFiles.Values.ToList(); } } + + public PceasSymbolImporter() + { + _format = EmuApi.GetRomInfo().Format; + } + + public static bool IsValidFile(string content) + { + return content.Contains("; Generated by PCEAS"); + } + + public AddressInfo? GetLineAddress(SourceFileInfo file, int lineIndex) + { + AddressInfo address; + if(_addressByLine.TryGetValue(file.Name.ToString() + "_" + lineIndex.ToString(), out address)) { + return address; + } + return null; + } + + public AddressInfo? GetLineEndAddress(SourceFileInfo file, int lineIndex) + { + return null; + } + + public string GetSourceCodeLine(int prgRomAddress) + { + throw new NotImplementedException(); + } + + public SourceCodeLocation? GetSourceCodeLineInfo(AddressInfo address) + { + string key = address.Type.ToString() + address.Address.ToString(); + SourceCodeLocation location; + if(_linesByAddress.TryGetValue(key, out location)) { + return location; + } + return null; + } + + public SourceSymbol? GetSymbol(string word, int scopeStart, int scopeEnd) + { + foreach(SymbolInfo symbol in _symbols) { + if(symbol.Name == word) { + //TODOv2 SCOPE + return symbol.SourceSymbol; + } + } + return null; + } + + public AddressInfo? GetSymbolAddressInfo(SourceSymbol symbol) + { + if(symbol.InternalSymbol is SymbolInfo dbgSymbol) { + return dbgSymbol.Address; + } + return null; + } + + public SourceCodeLocation? GetSymbolDefinition(SourceSymbol symbol) + { + return null; + } + + public List GetSymbols() + { + return _symbols.Select(s => s.SourceSymbol).ToList(); + } + + public int GetSymbolSize(SourceSymbol srcSymbol) + { + return 1; + } + + private string[] ProcessSourceFile(string filename, string comment, string[] data) + { + int ignoreColumnCount = 0; + Regex regex = new Regex("IgnoreColumns=(\\d*)"); + Match m = regex.Match(comment); + if(m.Success) { + int.TryParse(m.Groups[1].Value, out ignoreColumnCount); + } + + if(ignoreColumnCount > 0) { + return data.Select(line => { + if(line.StartsWith('#')) { + //Columns that start with # in listing are converted to comments + return ";" + line; + } else if(line.Length >= ignoreColumnCount) { + //Ignore first X columns (contains address, byte code, etc.) + return line.Substring(ignoreColumnCount); + } + return line; + }).ToArray(); + } else { + return data; + } + } + + private AddressInfo GetLabelAddress(int bank, int addr) + { + if(bank == 0xF8 && bank <= 0xFB) { + return new AddressInfo() { + Address = (bank - 0xF8) * 0x2000 + (addr & 0x1FFF), + Type = MemoryType.PceWorkRam + }; + } else if(bank == 0xF7) { + return new AddressInfo() { + Address = (addr & 0x1FFF), + Type = MemoryType.PceSaveRam + }; + } else if(bank == 0xFF) { + return new AddressInfo() { + Address = (addr & 0x1FFF), + Type = MemoryType.PceMemory + }; + } else if(_format == RomFormat.PceCdRom && (bank >= 0x68 && bank <= 0x7F)) { + return new AddressInfo() { + Address = (bank - 0x68) * 0x2000 + (addr & 0x1FFF), + Type = MemoryType.PceCardRam + }; + } else if(bank > 0xFF) { + return new AddressInfo() { + Address = (bank - 0x80) * 0x2000 + (addr & 0x1FFF), + Type = MemoryType.PcePrgRom + }; + } else if(bank < 0x80) { + return new AddressInfo() { + Address = bank * 0x2000 + (addr & 0x1FFF), + Type = MemoryType.PcePrgRom + }; + } + return default; + } + + public void Import(string path, bool showResult) + { + SymbolFileStamp = File.GetLastWriteTime(path); + + string basePath = Path.GetDirectoryName(path) ?? ""; + SymbolPath = basePath; + + string[] lines = File.ReadAllLines(path); + + Dictionary labels = new Dictionary(); + byte[] cdlData = new byte[DebugApi.GetMemorySize(MemoryType.PcePrgRom)]; + int errorCount = 0; + + for(int i = 0; i < lines.Length; i++) { + string str = lines[i].Trim(); + if(str == "[symbols]") { + for(; i < lines.Length; i++) { + str = lines[i].Trim(); + int commentStart = str.IndexOf(';'); + if(commentStart >= 0) { + str = str.Substring(0, commentStart); + } + + if(str.Length > 0) { + Match m = _symbolRegex.Match(str); + if(m.Success) { + int bank = Int32.Parse(m.Groups[1].Value, System.Globalization.NumberStyles.HexNumber); + int addr = Int32.Parse(m.Groups[2].Value, System.Globalization.NumberStyles.HexNumber); + long lengthFlags = long.Parse(m.Groups[3].Value, System.Globalization.NumberStyles.HexNumber); + uint length = (uint)(lengthFlags & ~0xC0000000); + if((lengthFlags & 0x80000000) != 0) { + //Code, always use length = 1 + length = 1; + } + + string originalLabel = m.Groups[6].Value; + string label = LabelManager.InvalidLabelRegex.Replace(originalLabel, "_"); + + if(!LabelManager.LabelRegex.IsMatch(label)) { + //ignore labels that don't respect the label naming restrictions + errorCount++; + continue; + } + + AddressInfo absAddr = GetLabelAddress(bank, addr); + + if(absAddr.Address < 0) { + errorCount++; + continue; + } + + SymbolInfo symbol = new(originalLabel, absAddr); + _symbols.Add(symbol); + + string orgLabel = label; + int j = 1; + while(labels.ContainsKey(label)) { + label = orgLabel + j.ToString(); + j++; + } + + if(ConfigManager.Config.Debug.Integration.IsMemoryTypeImportEnabled(absAddr.Type)) { + labels[label] = new CodeLabel() { + Label = label, + Address = (UInt32)absAddr.Address, + MemoryType = absAddr.Type, + Comment = "", + Flags = CodeLabelFlags.None, + Length = length + }; + } + } + } else { + break; + } + } + } else if(str == "[definitions]") { + for(; i < lines.Length; i++) { + str = lines[i].Trim(); + if(str.Length > 0) { + Match m = _definitionRegex.Match(str); + if(m.Success) { + int value = Int32.Parse(m.Groups[1].Value, System.Globalization.NumberStyles.HexNumber); + string label = m.Groups[2].Value; + + SymbolInfo symbol = new(label, value); + _symbols.Add(symbol); + } + } else { + break; + } + } + } else if(str == "[source files v2]") { + for(; i < lines.Length; i++) { + if(lines[i].Length > 0) { + Match m = _fileRegex.Match(lines[i]); + if(m.Success) { + int fileId = Int32.Parse(m.Groups[2].Value, System.Globalization.NumberStyles.HexNumber); + //int fileCrc = Int32.Parse(m.Groups[2].Value, System.Globalization.NumberStyles.HexNumber); + + Match fileMatch = _filePathRegex.Match(m.Groups[4].Value); + if(fileMatch.Success) { + string filePath = fileMatch.Groups[2].Success ? fileMatch.Groups[2].Value : fileMatch.Groups[4].Value; + string comment = fileMatch.Groups[3].Value; + string fullPath; + if(Path.IsPathFullyQualified(filePath)) { + fullPath = filePath; + } else { + string? srcBasePath = basePath; + fullPath = Path.Combine(srcBasePath, filePath); + while(!File.Exists(fullPath)) { + //Go back up folder structure to attempt to find the file + string oldPath = srcBasePath; + srcBasePath = Path.GetDirectoryName(srcBasePath); + if(srcBasePath == null || srcBasePath == oldPath) { + break; + } + fullPath = Path.Combine(srcBasePath, filePath); + } + } + string[] fileData = this.ProcessSourceFile(filePath, comment, File.Exists(fullPath) ? File.ReadAllLines(fullPath) : Array.Empty()); + _sourceFiles[fileId] = new SourceFileInfo(filePath, true, new PceasSourceFile() { Data = fileData }); + } + } + } else { + break; + } + } + } else if(str == "[bank-to-source]") { + for(; i < lines.Length; i++) { + if(lines[i].Length > 0) { + Match m = _srcMappingRegex.Match(lines[i]); + if(m.Success) { + int bank = Int32.Parse(m.Groups[1].Value, System.Globalization.NumberStyles.HexNumber); + int addr = Int32.Parse(m.Groups[2].Value, System.Globalization.NumberStyles.HexNumber); + + int fileId = Int32.Parse(m.Groups[4].Value, System.Globalization.NumberStyles.HexNumber); + int lineNumber = Int32.Parse(m.Groups[5].Value, System.Globalization.NumberStyles.HexNumber); + + AddressInfo absAddr = GetLabelAddress(bank, addr); + if(absAddr.Address >= 0) { + if(absAddr.Type == MemoryType.PcePrgRom) { + //Build CDL data based on the extra flags present in the mappings + long lengthFlags = long.Parse(m.Groups[3].Value, System.Globalization.NumberStyles.HexNumber); + if((lengthFlags & 0x40000000) != 0) { + cdlData[absAddr.Address] |= (byte)CdlFlags.SubEntryPoint; + } + + byte cdlFlags = (lengthFlags & 0x80000000) != 0 ? (byte)CdlFlags.Code : (byte)CdlFlags.Data; + for(long j = 0, len = (lengthFlags & ~0xC0000000); j < len; j++) { + if(absAddr.Address + j < cdlData.Length) { + cdlData[absAddr.Address + j] |= cdlFlags; + } + } + } + + _addressByLine[_sourceFiles[fileId].Name + "_" + lineNumber.ToString()] = absAddr; + _linesByAddress[absAddr.Type.ToString() + absAddr.Address.ToString()] = new SourceCodeLocation(_sourceFiles[fileId], lineNumber); + } + } + } else { + break; + } + } + } + } + + DebugApi.SetCdlData(MemoryType.PcePrgRom, cdlData, cdlData.Length); + LabelManager.SetLabels(labels.Values, true); + + if(showResult) { + if(errorCount > 0) { + MesenMsgBox.Show(null, "ImportLabelsWithErrors", MessageBoxButtons.OK, MessageBoxIcon.Warning, labels.Count.ToString(), errorCount.ToString()); + } else { + MesenMsgBox.Show(null, "ImportLabels", MessageBoxButtons.OK, MessageBoxIcon.Info, labels.Count.ToString()); + } + } + } + + class PceasSourceFile : IFileDataProvider + { + public string[] Data { get; init; } = Array.Empty(); + } + + private readonly struct SymbolInfo + { + public string Name { get; } + public int? Value { get; } + public AddressInfo? Address { get; } + public SourceSymbol SourceSymbol { get => new SourceSymbol(Name, Address?.Address ?? Value, this); } + + public SymbolInfo(string name, AddressInfo address) + { + Name = name; + Address = address; + } + + public SymbolInfo(string name, int value) + { + Name = name; + Value = value; + } + } +} diff --git a/UI/Debugger/Integration/WlaDxImporter.cs b/UI/Debugger/Integration/WlaDxImporter.cs index 184e584a1..7d7c8a5e6 100644 --- a/UI/Debugger/Integration/WlaDxImporter.cs +++ b/UI/Debugger/Integration/WlaDxImporter.cs @@ -96,11 +96,6 @@ public int GetSymbolSize(SourceSymbol srcSymbol) return 1; } - protected virtual string[] ProcessSourceFile(string filename, string comment, string[] data) - { - return data; - } - public void Import(string path, bool showResult) { SymbolFileStamp = File.GetLastWriteTime(path); @@ -116,7 +111,6 @@ public void Import(string path, bool showResult) int errorCount = 0; bool isAsar = false; - bool isPceas = false; //When [labels] tag isn't found, the symbols were output using the "nocash" symbol format (-s vs -S command line option) bool labelsOnly = !lines.Contains("[labels]"); @@ -216,7 +210,7 @@ public void Import(string path, bool showResult) fullPath = Path.Combine(srcBasePath, filePath); } } - string[] fileData = this.ProcessSourceFile(filePath, comment, File.Exists(fullPath) ? File.ReadAllLines(fullPath) : Array.Empty()); + string[] fileData = File.Exists(fullPath) ? File.ReadAllLines(fullPath) : Array.Empty(); _sourceFiles[fileId] = new SourceFileInfo(filePath, true, new WlaDxFile() { Data = fileData }); } } @@ -275,53 +269,11 @@ public void Import(string path, bool showResult) break; } } - } else if(isPceas && str == "[bank-to-source]") { - for(; i < lines.Length; i++) { - if(lines[i].Length > 0) { - Match m = _pceasSourceRegex.Match(lines[i]); - if(m.Success) { - int bank = Int32.Parse(m.Groups[1].Value, System.Globalization.NumberStyles.HexNumber); - int addr = Int32.Parse(m.Groups[2].Value, System.Globalization.NumberStyles.HexNumber); - - int fileId = Int32.Parse(m.Groups[4].Value, System.Globalization.NumberStyles.HexNumber); - int lineNumber = Int32.Parse(m.Groups[5].Value, System.Globalization.NumberStyles.HexNumber); - - AddressInfo absAddr = GetLabelAddress(bank, addr); - if(absAddr.Address >= 0) { - if(absAddr.Type == MemoryType.PcePrgRom) { - //Build CDL data based on the extra flags present in the mappings - long lengthFlags = long.Parse(m.Groups[3].Value, System.Globalization.NumberStyles.HexNumber); - if((lengthFlags & 0x40000000) != 0) { - cdlData[absAddr.Address] |= (byte)CdlFlags.SubEntryPoint; - } - - byte cdlFlags = (lengthFlags & 0x80000000) != 0 ? (byte)CdlFlags.Code : (byte)CdlFlags.Data; - for(long j = 0, len = (lengthFlags & ~0xC0000000); j < len; j++) { - if(absAddr.Address + j < cdlData.Length) { - cdlData[absAddr.Address + j] |= cdlFlags; - } - } - } - - _addressByLine[_sourceFiles[fileId].Name + "_" + lineNumber.ToString()] = absAddr; - _linesByAddress[absAddr.Type.ToString() + absAddr.Address.ToString()] = new SourceCodeLocation(_sourceFiles[fileId], lineNumber); - } - } - } else { - break; - } - } } else if(str == "; generated by asar") { isAsar = true; - } else if(str == "; Generated by PCEAS") { - isPceas = true; } } - if(isPceas) { - DebugApi.SetCdlData(MemoryType.PcePrgRom, cdlData, cdlData.Length); - } - LabelManager.SetLabels(labels.Values, true); if(showResult) { @@ -385,31 +337,6 @@ public PceWlaDxImporter() _format = EmuApi.GetRomInfo().Format; } - protected override string[] ProcessSourceFile(string filename, string comment, string[] data) - { - int ignoreColumnCount = 0; - Regex regex = new Regex("IgnoreColumns=(\\d*)"); - Match m = regex.Match(comment); - if(m.Success) { - int.TryParse(m.Groups[1].Value, out ignoreColumnCount); - } - - if(ignoreColumnCount > 0) { - return data.Select(line => { - if(line.StartsWith('#')) { - //Columns that start with # in listing are converted to comments - return ";" + line; - } else if(line.Length >= ignoreColumnCount) { - //Ignore first X columns (contains address, byte code, etc.) - return line.Substring(ignoreColumnCount); - } - return line; - }).ToArray(); - } else { - return data; - } - } - protected override AddressInfo GetLabelAddress(int bank, int addr) { if(bank == 0xF8 && bank <= 0xFB) { diff --git a/UI/Debugger/Utilities/DebugWorkspaceManager.cs b/UI/Debugger/Utilities/DebugWorkspaceManager.cs index 332874274..daed7571d 100644 --- a/UI/Debugger/Utilities/DebugWorkspaceManager.cs +++ b/UI/Debugger/Utilities/DebugWorkspaceManager.cs @@ -166,8 +166,12 @@ public static void LoadSymFile(string path, bool showResult) BassLabelFile.Import(path, showResult, CpuType.Gameboy); } } else { - if(_romInfo.ConsoleType == ConsoleType.PcEngine && PceasSymbolFile.IsValidFile(symContent)) { - PceasSymbolFile.Import(path, showResult); + if(_romInfo.ConsoleType == ConsoleType.PcEngine && PceasSymbolImporter.IsValidFile(symContent)) { + PceasSymbolImporter importer = new PceasSymbolImporter(); + importer.Import(path, showResult); + SymbolProvider = importer; + } else if(_romInfo.ConsoleType == ConsoleType.PcEngine && LegacyPceasSymbolFile.IsValidFile(symContent)) { + LegacyPceasSymbolFile.Import(path, showResult); } else { BassLabelFile.Import(path, showResult, _romInfo.ConsoleType.GetMainCpuType()); }