using System; using System.Collections.Generic; using System.Linq; namespace Ryujinx.HLE.OsHle.Diagnostics { static class Demangler { private static readonly Dictionary BuiltinTypes = new Dictionary { { "v", "void" }, { "w", "wchar_t" }, { "b", "bool" }, { "c", "char" }, { "a", "signed char" }, { "h", "unsigned char" }, { "s", "short" }, { "t", "unsigned short" }, { "i", "int" }, { "j", "unsigned int" }, { "l", "long" }, { "m", "unsigned long" }, { "x", "long long" }, { "y", "unsigned long long" }, { "n", "__int128" }, { "o", "unsigned __int128" }, { "f", "float" }, { "d", "double" }, { "e", "long double" }, { "g", "__float128" }, { "z", "..." }, { "Dd", "__iec559_double" }, { "De", "__iec559_float128" }, { "Df", "__iec559_float" }, { "Dh", "__iec559_float16" }, { "Di", "char32_t" }, { "Ds", "char16_t" }, { "Da", "decltype(auto)" }, { "Dn", "std::nullptr_t" }, }; private static readonly Dictionary SubstitutionExtra = new Dictionary { {"Sa", "std::allocator"}, {"Sb", "std::basic_string"}, {"Ss", "std::basic_string, ::std::allocator>"}, {"Si", "std::basic_istream>"}, {"So", "std::basic_ostream>"}, {"Sd", "std::basic_iostream>"} }; private static int FromBase36(string encoded) { string base36 = "0123456789abcdefghijklmnopqrstuvwxyz"; char[] reversedEncoded = encoded.ToLower().ToCharArray().Reverse().ToArray(); int result = 0; for (int i = 0; i < reversedEncoded.Length; i++) { char c = reversedEncoded[i]; int value = base36.IndexOf(c); if (value == -1) return -1; result += value * (int)Math.Pow(36, i); } return result; } private static string GetCompressedValue(string compression, List compressionData, out int pos) { string res = null; bool canHaveUnqualifiedName = false; pos = -1; if (compressionData.Count == 0 || !compression.StartsWith("S")) return null; if (compression.Length >= 2 && SubstitutionExtra.TryGetValue(compression.Substring(0, 2), out string substitutionValue)) { pos = 1; res = substitutionValue; compression = compression.Substring(2); } else if (compression.StartsWith("St")) { pos = 1; canHaveUnqualifiedName = true; res = "std"; compression = compression.Substring(2); } else if (compression.StartsWith("S_")) { pos = 1; res = compressionData[0]; canHaveUnqualifiedName = true; compression = compression.Substring(2); } else { int id = -1; int underscorePos = compression.IndexOf('_'); if (underscorePos == -1) return null; string partialId = compression.Substring(1, underscorePos - 1); id = FromBase36(partialId); if (id == -1 || compressionData.Count <= (id + 1)) { return null; } res = compressionData[id + 1]; pos = partialId.Length + 1; canHaveUnqualifiedName= true; compression = compression.Substring(pos); } if (res != null) { if (canHaveUnqualifiedName) { List type = ReadName(compression, compressionData, out int endOfNameType); if (endOfNameType != -1 && type != null) { pos += endOfNameType; res = res + "::" + type[type.Count - 1]; } } } return res; } private static List ReadName(string mangled, List compressionData, out int pos, bool isNested = true) { List res = new List(); string charCountString = null; int charCount = 0; int i; pos = -1; for (i = 0; i < mangled.Length; i++) { char chr = mangled[i]; if (charCountString == null) { if (ReadCVQualifiers(chr) != null) { continue; } if (chr == 'S') { string data = GetCompressedValue(mangled.Substring(i), compressionData, out pos); if (pos == -1) { return null; } if (res.Count == 0) res.Add(data); else res.Add(res[res.Count - 1] + "::" + data); i += pos; if (i < mangled.Length && mangled[i] == 'E') { break; } continue; } else if (chr == 'E') { break; } } if (Char.IsDigit(chr)) { charCountString += chr; } else { if (!int.TryParse(charCountString, out charCount)) { return null; } string demangledPart = mangled.Substring(i, charCount); if (res.Count == 0) res.Add(demangledPart); else res.Add(res[res.Count - 1] + "::" + demangledPart); i = i + charCount - 1; charCount = 0; charCountString = null; if (!isNested) break; } } if (res.Count == 0) { return null; } pos = i; return res; } private static string ReadBuiltinType(string mangledType, out int pos) { string res = null; string possibleBuiltinType; pos = -1; possibleBuiltinType = mangledType[0].ToString(); if (!BuiltinTypes.TryGetValue(possibleBuiltinType, out res)) { if (mangledType.Length >= 2) { // Try to match the first 2 chars if the first call failed possibleBuiltinType = mangledType.Substring(0, 2); BuiltinTypes.TryGetValue(possibleBuiltinType, out res); } } if (res != null) pos = possibleBuiltinType.Length; return res; } private static string ReadCVQualifiers(char qualifier) { if (qualifier == 'r') return "restricted"; else if (qualifier == 'V') return "volatile"; else if (qualifier == 'K') return "const"; return null; } private static string ReadRefQualifiers(char qualifier) { if (qualifier == 'R') return "&"; else if (qualifier == 'O') return "&&"; return null; } private static string ReadSpecialQualifiers(char qualifier) { if (qualifier == 'P') return "*"; else if (qualifier == 'C') return "complex"; else if (qualifier == 'G') return "imaginary"; return null; } private static List ReadParameters(string mangledParams, List compressionData, out int pos) { List res = new List(); List refQualifiers = new List(); string parsedTypePart = null; string currentRefQualifiers = null; string currentBuiltinType = null; string currentSpecialQualifiers = null; string currentCompressedValue = null; int i = 0; pos = -1; for (i = 0; i < mangledParams.Length; i++) { if (currentBuiltinType != null) { string currentCVQualifier = String.Join(" ", refQualifiers); // Try to mimic the compression indexing if (currentRefQualifiers != null) { compressionData.Add(currentBuiltinType + currentRefQualifiers); } if (refQualifiers.Count != 0) { compressionData.Add(currentBuiltinType + " " + currentCVQualifier + currentRefQualifiers); } if (currentSpecialQualifiers != null) { compressionData.Add(currentBuiltinType + " " + currentCVQualifier + currentRefQualifiers + currentSpecialQualifiers); } if (currentRefQualifiers == null && currentCVQualifier == null && currentSpecialQualifiers == null) { compressionData.Add(currentBuiltinType); } currentBuiltinType = null; currentCompressedValue = null; currentCVQualifier = null; currentRefQualifiers = null; refQualifiers.Clear(); currentSpecialQualifiers = null; } char chr = mangledParams[i]; string part = mangledParams.Substring(i); // Try to read qualifiers parsedTypePart = ReadCVQualifiers(chr); if (parsedTypePart != null) { refQualifiers.Add(parsedTypePart); // need more data continue; } parsedTypePart = ReadRefQualifiers(chr); if (parsedTypePart != null) { currentRefQualifiers = parsedTypePart; // need more data continue; } parsedTypePart = ReadSpecialQualifiers(chr); if (parsedTypePart != null) { currentSpecialQualifiers = parsedTypePart; // need more data continue; } // TODO: extended-qualifier? if (part.StartsWith("S")) { parsedTypePart = GetCompressedValue(part, compressionData, out pos); if (pos != -1 && parsedTypePart != null) { currentCompressedValue = parsedTypePart; i += pos; res.Add(currentCompressedValue + " " + String.Join(" ", refQualifiers) + currentRefQualifiers + currentSpecialQualifiers); currentBuiltinType = null; currentCompressedValue = null; currentRefQualifiers = null; refQualifiers.Clear(); currentSpecialQualifiers = null; continue; } pos = -1; return null; } else if (part.StartsWith("N")) { part = part.Substring(1); List name = ReadName(part, compressionData, out pos); if (pos != -1 && name != null) { i += pos + 1; res.Add(name[name.Count - 1] + " " + String.Join(" ", refQualifiers) + currentRefQualifiers + currentSpecialQualifiers); currentBuiltinType = null; currentCompressedValue = null; currentRefQualifiers = null; refQualifiers.Clear(); currentSpecialQualifiers = null; continue; } } // Try builting parsedTypePart = ReadBuiltinType(part, out pos); if (pos == -1) { return null; } currentBuiltinType = parsedTypePart; res.Add(currentBuiltinType + " " + String.Join(" ", refQualifiers) + currentRefQualifiers + currentSpecialQualifiers); i = i + pos -1; } pos = i; return res; } private static string ParseFunctionName(string mangled) { List compressionData = new List(); int pos = 0; string res; bool isNested = mangled.StartsWith("N"); // If it's start with "N" it must be a nested function name if (isNested) mangled = mangled.Substring(1); compressionData = ReadName(mangled, compressionData, out pos, isNested); if (pos == -1) return null; res = compressionData[compressionData.Count - 1]; compressionData.Remove(res); mangled = mangled.Substring(pos + 1); // more data? maybe not a data name so... if (mangled != String.Empty) { List parameters = ReadParameters(mangled, compressionData, out pos); // parameters parsing error, we return the original data to avoid information loss. if (pos == -1) return null; parameters = parameters.Select(outer => outer.Trim()).ToList(); res += "(" + String.Join(", ", parameters) + ")"; } return res; } public static string Parse(string originalMangled) { if (originalMangled.StartsWith("_Z")) { // We assume that we have a name (TOOD: support special names) string res = ParseFunctionName(originalMangled.Substring(2)); if (res == null) return originalMangled; return res; } return originalMangled; } } }