This commit is contained in:
Amber Brault 2026-03-18 06:40:59 +01:00 committed by GitHub
commit 4c74c7727a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 1438 additions and 7 deletions

View File

@ -12,6 +12,8 @@ Dolphin includes or links code of the following third-party software projects:
[MIT](https://github.com/mutouyun/cpp-ipc/blob/master/LICENSE)
- [cubeb](https://github.com/kinetiknz/cubeb):
[ISC](https://github.com/kinetiknz/cubeb/blob/master/LICENSE)
- [cwdemangle](https://github.com/encounter/cwdemangle)
[CC0-1.0](https://github.com/encounter/cwdemangle/blob/main/LICENSE)
- [Discord-RPC](https://github.com/discordapp/discord-rpc):
[MIT](https://github.com/discordapp/discord-rpc/blob/master/LICENSE)
- [ENet](http://enet.bespin.org/):

View File

@ -47,6 +47,8 @@ add_library(common
Crypto/HMAC.h
Crypto/SHA1.cpp
Crypto/SHA1.h
CWDemangler.cpp
CWDemangler.h
Debug/MemoryPatches.cpp
Debug/MemoryPatches.h
Debug/Threads.h

View File

@ -0,0 +1,778 @@
// Copyright 2026 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// Based on: https://github.com/encounter/cwdemangle
// Copyright 2024 Luke Street <luke@street.dev>
// SPDX-License-Identifier: CC0-1.0
#include "Common/CWDemangler.h"
#include <algorithm>
#include <cctype>
#include <map>
#include <fmt/format.h>
#include "Common/StringUtil.h"
namespace CWDemangler
{
struct ParseQualifiersResult
{
std::string pre;
std::string post;
std::string_view rest;
};
struct ParseDigitsResult
{
std::size_t digits;
std::string_view rest;
};
// Forward declarations
std::optional<std::size_t> find_split(std::string_view s, bool special, DemangleOptions options);
inline static bool IsAscii(std::string_view s)
{
return std::ranges::none_of(s, [](u8 c) { return c > 127; });
}
static const std::map<std::string, std::string> operators = {
{"nw", "operator new"}, {"nwa", "operator new[]"},
{"dl", "operator delete"}, {"dla", "operator delete[]"},
{"pl", "operator+"}, {"mi", "operator-"},
{"ml", "operator*"}, {"dv", "operator/"},
{"md", "operator%"}, {"er", "operator^"},
{"ad", "operator&"}, {"or", "operator|"},
{"co", "operator~"}, {"nt", "operator!"},
{"as", "operator="}, {"lt", "operator<"},
{"gt", "operator>"}, {"apl", "operator+="},
{"ami", "operator-="}, {"amu", "operator*="},
{"adv", "operator/="}, {"amd", "operator%="},
{"aer", "operator^="}, {"aad", "operator&="},
{"aor", "operator|="}, {"ls", "operator<<"},
{"rs", "operator>>"}, {"ars", "operator>>="},
{"als", "operator<<="}, {"eq", "operator=="},
{"ne", "operator!="}, {"le", "operator<="},
{"ge", "operator>="}, {"aa", "operator&&"},
{"oo", "operator||"}, {"pp", "operator++"},
{"mm", "operator--"}, {"cm", "operator,"},
{"rm", "operator->*"}, {"rf", "operator->"},
{"cl", "operator()"}, {"vc", "operator[]"},
{"vt", "__vtable"}};
static const std::map<char, std::string> types = {
{'i', "int"}, {'b', "bool"}, {'c', "char"}, {'s', "short"},
{'l', "long"}, {'x', "long long"}, {'f', "float"}, {'d', "double"},
{'w', "wchar_t"}, {'v', "void"}, {'e', "..."},
};
ParseQualifiersResult parse_qualifiers(std::string_view str)
{
std::string pre;
std::string post;
std::size_t index = 0;
for (char c : str)
{
bool found_non_qualifier = false;
switch (c)
{
case 'P':
if (pre.empty())
{
post.insert(0, "*");
}
else
{
post.insert(0, fmt::format("* {0}", StripTrailingWhitespace(pre)));
pre.clear();
}
break;
case 'R':
if (pre.empty())
{
post.insert(0, "&");
}
else
{
post.insert(0, fmt::format("& {0}", StripTrailingWhitespace(pre)));
pre.clear();
}
break;
case 'C':
pre += "const ";
break;
case 'V':
pre += "volatile ";
break;
case 'U':
pre += "unsigned ";
break;
case 'S':
pre += "signed ";
break;
default:
found_non_qualifier = true;
break;
}
if (found_non_qualifier)
break;
index++;
}
str.remove_prefix(index);
post = StripTrailingWhitespace(post);
return {pre, post, str};
}
std::optional<ParseDigitsResult> parse_digits(std::string_view str)
{
if (str.empty())
return std::nullopt;
const char* str_start = str.data();
const char* str_end = str_start + str.size();
std::size_t val = 0;
std::string_view remainder;
const auto result = std::from_chars(str_start, str_end, val);
if (result.ec != std::errc{})
return std::nullopt;
const char* rest_start = result.ptr;
if (rest_start != str_end)
remainder = std::string_view(rest_start, str_end - rest_start);
return {{val, remainder}};
}
std::optional<DemangleTemplateArgsResult> demangle_template_args(std::string_view str,
DemangleOptions options)
{
const std::size_t start_idx = str.find('<');
if (start_idx == std::string::npos)
return {{str, ""}};
const std::size_t end_idx = str.rfind('>');
if (end_idx == std::string::npos || end_idx < start_idx)
{
return std::nullopt;
}
std::string_view args(&str[start_idx + 1], &str[end_idx]);
const std::string_view template_name = str.substr(0, start_idx);
std::string tmpl_args = "<";
while (!args.empty())
{
const auto result = demangle_arg(args, options);
if (!result)
return std::nullopt;
const auto demangled_arg = result.value();
const std::string_view rest = demangled_arg.rest;
tmpl_args += demangled_arg.arg_pre;
tmpl_args += demangled_arg.arg_post;
if (rest.empty())
break;
tmpl_args += ", ";
args = rest.substr(1);
}
tmpl_args += ">";
return {{template_name, tmpl_args}};
}
std::optional<DemangleNameResult> demangle_name(std::string_view str, DemangleOptions options)
{
const auto result = parse_digits(str);
if (!result)
return std::nullopt;
auto [size, rest] = result.value();
if (rest.length() < size)
{
return std::nullopt;
}
auto result1 = demangle_template_args(rest.substr(0, size), options);
if (!result1)
return std::nullopt;
auto [name, args] = result1.value();
const std::string result_name{name};
return {{result_name, fmt::format("{0}{1}", name, args), rest.substr(size)}};
}
std::optional<DemangleNameResult> demangle_qualified_name(std::string_view str,
DemangleOptions options)
{
if (!str.starts_with('Q'))
return demangle_name(str, options);
if (str.length() < 3)
{
return std::nullopt;
}
// MWCC only preserves up to 9 layers of namespace/class depth in symbol names, so we
// can just check one digit
const int digit = (int)(str[1] - '0');
if (digit < 0 || digit > 9)
return std::nullopt;
const int count = digit;
str = str.substr(2);
std::string last_class;
std::string qualified;
for (int i = 0; i < count; i++)
{
const auto result = demangle_name(str, options);
if (!result)
return std::nullopt;
auto [class_name, full, rest] = result.value();
qualified += full;
last_class = class_name;
str = rest;
if (i < count - 1)
{
qualified += "::";
}
}
return {{last_class, qualified, str}};
}
std::optional<DemangleArgResult> demangle_arg(std::string_view str, DemangleOptions options)
{
// Negative constant
if (str.starts_with('-'))
{
const auto parse_result = parse_digits(str.substr(1));
if (!parse_result)
return std::nullopt;
std::size_t size = parse_result->digits;
const std::string out_val = fmt::format("-{}", size);
return {{out_val, "", parse_result->rest}};
}
std::string result;
const auto parse_qual_result = parse_qualifiers(str);
std::string pre = parse_qual_result.pre;
std::string post = parse_qual_result.post;
std::string_view rest = parse_qual_result.rest;
result += pre;
str = rest;
// Disambiguate arguments starting with a number
if (str.length() > 0 && std::isdigit((u8)str[0]))
{
const auto parse_result = parse_digits(str);
if (!parse_result)
return std::nullopt;
auto& [num, rest_value] = parse_result.value();
rest = rest_value;
// If the number is followed by a comma or the end of the string, it's a template argument
if (rest.empty() || rest.starts_with(','))
{
// ...or a Metrowerks extension type
if (options.mw_extensions)
{
const std::string t = num == 1 ? "__int128" : num == 2 ? "__vec2x32float__" : "";
if (!t.empty())
{
result += t;
return {{result, post, rest}};
}
}
result += fmt::format("{}{}", num, post);
return {{result, "", rest}};
}
// Otherwise, it's (probably) the size of a type
const auto demangle_name_result = demangle_name(str, options);
if (!demangle_name_result)
return std::nullopt;
result += demangle_name_result->full;
result += post;
return {{result, "", demangle_name_result->rest}};
}
// Handle qualified names
if (str.starts_with('Q'))
{
const auto demangle_qual_result = demangle_qualified_name(str, options);
if (!demangle_qual_result)
return std::nullopt;
result += demangle_qual_result->full;
result += post;
return {{result, "", demangle_qual_result->rest}};
}
bool is_member = false;
bool const_member = false;
if (str.starts_with('M'))
{
is_member = true;
const auto demangle_qual_result = demangle_qualified_name(str.substr(1), options);
if (!demangle_qual_result)
return std::nullopt;
rest = demangle_qual_result->rest;
pre = fmt::format("{}::*{}", demangle_qual_result->full, pre);
if (!rest.starts_with('F'))
{
return std::nullopt;
}
str = rest;
}
if (is_member || str.starts_with('F'))
{
str.remove_prefix(1);
if (is_member)
{
// "const void*, const void*" or "const void*, void*"
if (str.starts_with("PCvPCv"))
{
const_member = true;
str.remove_prefix(6);
}
else if (str.starts_with("PCvPv"))
{
str.remove_prefix(5);
}
else
{
return std::nullopt;
}
}
else if (post.starts_with('*'))
{
post = StripLeadingWhitespace(post.substr(1));
pre = fmt::format("*{}", pre);
}
else
{
return std::nullopt;
}
const auto demangle_func_args_result = demangle_function_args(str, options);
if (!demangle_func_args_result)
return std::nullopt;
const auto demangled_func_args = demangle_func_args_result.value();
if (!demangled_func_args.rest.starts_with('_'))
{
return std::nullopt;
}
const auto demangle_arg_result = demangle_arg(demangled_func_args.rest.substr(1), options);
if (!demangle_arg_result)
return std::nullopt;
const std::string const_str = const_member ? " const" : "";
const std::string res_pre = fmt::format("{} ({}{}", demangle_arg_result->arg_pre, pre, post);
const std::string res_post = fmt::format(")({}){}{}", demangled_func_args.args, const_str,
demangle_arg_result->arg_post);
return {{res_pre, res_post, demangle_arg_result->rest}};
}
if (str.starts_with('A'))
{
const auto parse_result = parse_digits(str.substr(1));
if (!parse_result)
return std::nullopt;
auto& [count, rest_value] = parse_result.value();
rest = rest_value;
if (!rest.starts_with('_'))
{
return std::nullopt;
}
const auto demangle_arg_result = demangle_arg(rest.substr(1), options);
if (!demangle_arg_result)
return std::nullopt;
if (!post.empty())
{
post = fmt::format("({})", post);
}
result = fmt::format("{}{}{}", pre, demangle_arg_result->arg_pre, post);
const std::string ret_post = fmt::format("[{}]{}", count, demangle_arg_result->arg_post);
return {{result, ret_post, demangle_arg_result->rest}};
}
if (str.length() == 0)
return std::nullopt;
std::string type;
char c = str[0];
if (types.contains(c))
{
type = types.at(c);
}
else
{
// Handle special cases
switch (c)
{
case '1':
if (options.mw_extensions)
type = "__int128";
break;
case '2':
if (options.mw_extensions)
type = "__vec2x32float__";
break;
case '_':
return {{result, "", rest}};
default:
return std::nullopt;
}
}
result += type;
result += post;
return {{result, "", str.substr(1)}};
}
std::optional<DemangleFunctionArgsResult> demangle_function_args(std::string_view str,
DemangleOptions options)
{
std::string result;
while (!str.empty())
{
if (!result.empty())
{
result += ", ";
}
const auto demangle_arg_result = demangle_arg(str, options);
if (!demangle_arg_result)
return std::nullopt;
result += demangle_arg_result->arg_pre;
result += demangle_arg_result->arg_post;
str = demangle_arg_result->rest;
if (str.starts_with('_') || str.starts_with(','))
{
break;
}
}
return {{result, str}};
}
std::optional<std::string> demangle_special_function(std::string_view str,
std::string_view class_name,
DemangleOptions options)
{
if (str.starts_with("op"))
{
const std::string_view rest = str.substr(2);
const auto demangle_arg_result = demangle_arg(rest, options);
if (!demangle_arg_result)
return std::nullopt;
return fmt::format("operator {}{}", demangle_arg_result->arg_pre,
demangle_arg_result->arg_post);
}
const auto result = demangle_template_args(str, options);
if (!result)
return std::nullopt;
auto& [op, args] = result.value();
std::string func_name;
if (op == "dt")
{
return fmt::format("~{}{}", class_name, args);
}
else if (op == "ct")
{
func_name = class_name;
}
else if (operators.contains(op.data()))
{
func_name = operators.at(op.data());
}
else
{
return fmt::format("__{}{}", op, args);
}
return fmt::format("{0}{1}", func_name, args);
}
// Demangle a symbol name.
//
// Returns `std::nullopt` if the input is not a valid mangled name.
std::optional<std::string> demangle(std::string_view str, DemangleOptions options)
{
if (!IsAscii(str))
{
return std::nullopt;
}
bool special = false;
bool cnst = false;
std::string fn_name;
std::string static_var;
// Handle new static function variables (Wii CW)
bool guard = str.starts_with("@GUARD@");
if (guard || str.starts_with("@LOCAL@"))
{
str = str.substr(7);
std::size_t idx = str.rfind('@');
if (idx == std::string::npos)
return std::nullopt;
const std::string_view rest = str.substr(0, idx);
const std::string_view var = str.substr(idx);
if (guard)
{
static_var = fmt::format("{0} guard", var.substr(1));
}
else
{
static_var = var.substr(1);
}
str = rest;
}
if (str.starts_with("__"))
{
special = true;
str = str.substr(2);
}
{
auto idx_temp = find_split(str, special, options);
if (!idx_temp)
return std::nullopt;
std::size_t idx = idx_temp.value();
// Handle any trailing underscores in the function name
while (str[idx + 2] == '_')
{
idx++;
}
const std::string_view fn_name_out = str.substr(0, idx);
std::string_view rest = str.substr(idx);
if (special)
{
if (fn_name_out == "init")
{
// Special case for double __
std::size_t rest_idx = rest.substr(2).find("__");
if (rest_idx == std::string::npos)
return std::nullopt;
fn_name = str.substr(0, rest_idx + 6);
rest.remove_prefix(rest_idx + 2);
}
else
{
fn_name = fn_name_out;
}
}
else
{
const auto result = demangle_template_args(fn_name_out, options);
if (!result)
return std::nullopt;
const auto template_args = result.value();
fn_name = fmt::format("{}{}", template_args.name, template_args.args);
}
// Handle old static function variables (GC CW)
std::size_t first_idx = fn_name.find('$');
if (first_idx != std::string::npos)
{
std::size_t second_idx = fn_name.substr(first_idx + 1).find('$');
if (second_idx == std::string::npos)
return std::nullopt;
const std::string var = fn_name.substr(0, first_idx);
std::string rest_temp = fn_name.substr(first_idx);
rest_temp = rest_temp.substr(1);
const std::string var_type = rest_temp.substr(0, second_idx);
rest_temp = rest_temp.substr(second_idx);
if (!var_type.starts_with("localstatic"))
{
return std::nullopt;
}
if (var == "init")
{
// Sadly, $localstatic doesn't provide the variable name in guard/init
static_var = fmt::format("{} guard", var_type);
}
else
{
static_var = var;
}
fn_name = rest_temp.substr(1);
}
str = rest.substr(2);
}
std::string class_name;
std::string return_type_pre;
std::string return_type_post;
std::string qualified;
if (!str.starts_with('F'))
{
const auto result = demangle_qualified_name(str, options);
if (!result)
return std::nullopt;
class_name = result->class_name;
qualified = result->full;
str = result->rest;
}
if (special)
{
const auto result = demangle_special_function(fn_name, class_name, options);
if (!result)
return std::nullopt;
fn_name = result.value();
}
if (str.starts_with('C'))
{
str.remove_prefix(1);
cnst = true;
}
if (str.starts_with('F'))
{
str.remove_prefix(1);
const auto result = demangle_function_args(str, options);
if (!result)
return std::nullopt;
if (options.omit_empty_parameters && result->args == "void")
{
fn_name = fmt::format("{}()", fn_name);
}
else
{
fn_name = fmt::format("{}({})", fn_name, result->args);
}
str = result->rest;
}
if (str.starts_with('_'))
{
str.remove_prefix(1);
const auto result = demangle_arg(str, options);
if (!result)
return std::nullopt;
return_type_pre = result->arg_pre;
return_type_post = result->arg_post;
str = result->rest;
}
if (!str.empty())
{
return std::nullopt;
}
if (cnst)
{
fn_name = fmt::format("{} const", fn_name);
}
if (!qualified.empty())
{
fn_name = fmt::format("{}::{}", qualified, fn_name);
}
if (!return_type_pre.empty())
{
fn_name = fmt::format("{} {}{}", return_type_pre, fn_name, return_type_post);
}
if (!static_var.empty())
{
fn_name = fmt::format("{}::{}", fn_name, static_var);
}
return fn_name;
}
// Finds the first double underscore in the string, excluding any that are part of a
// template argument list or operator name.
std::optional<std::size_t> find_split(std::string_view s, bool special, DemangleOptions options)
{
std::size_t start = 0;
if (special && s.starts_with("op"))
{
const auto result = demangle_arg(s.substr(2), options);
if (!result)
return std::nullopt;
const std::string_view rest = result->rest;
start = s.length() - rest.length();
}
int depth = 0;
std::size_t length = s.length();
for (std::size_t i = start; i < length; i++)
{
switch (s[i])
{
case '<':
depth++;
break;
case '>':
depth--;
break;
case '_':
if (i < length - 1 && s[i + 1] == '_' && depth == 0)
{
return i;
}
break;
default:
break;
}
}
return std::nullopt;
}
} // namespace CWDemangler

View File

@ -0,0 +1,67 @@
// Copyright 2026 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// Based on: https://github.com/encounter/cwdemangle
// Copyright 2024 Luke Street <luke@street.dev>
// SPDX-License-Identifier: CC0-1.0
#pragma once
#include <optional>
#include <string>
#include "Common/CommonTypes.h"
namespace CWDemangler
{
// Options for [demangle].
struct DemangleOptions
{
// Replace `(void)` function parameters with `()`
bool omit_empty_parameters = true;
// Enable Metrowerks extension types (`__int128`, `__vec2x32float__`, etc.)
//
// Disabled by default since they conflict with template argument literals
// and can't always be demangled correctly.
bool mw_extensions = false;
};
struct DemangleTemplateArgsResult
{
std::string_view name;
std::string args;
};
struct DemangleNameResult
{
std::string class_name;
std::string full;
std::string_view rest;
};
struct DemangleArgResult
{
std::string arg_pre;
std::string arg_post;
std::string_view rest;
};
struct DemangleFunctionArgsResult
{
std::string args;
std::string_view rest;
};
std::optional<DemangleTemplateArgsResult> demangle_template_args(std::string_view str,
DemangleOptions options);
std::optional<DemangleNameResult> demangle_name(std::string_view str, DemangleOptions options);
std::optional<DemangleNameResult> demangle_qualified_name(std::string_view str,
DemangleOptions options);
std::optional<DemangleArgResult> demangle_arg(std::string_view str, DemangleOptions options);
std::optional<DemangleFunctionArgsResult> demangle_function_args(std::string_view str,
DemangleOptions options);
std::optional<std::string> demangle_special_function(std::string_view str,
std::string_view class_name,
DemangleOptions options);
std::optional<std::string> demangle(std::string_view str, DemangleOptions options);
} // namespace CWDemangler

View File

@ -194,7 +194,7 @@ std::string ArrayToString(const u8* data, u32 size, int line_len, bool spaces)
template <typename T>
static std::string_view StripEnclosingChars(std::string_view str, T chars)
{
const size_t s = str.find_first_not_of(chars);
const std::size_t s = str.find_first_not_of(chars);
if (str.npos != s)
return str.substr(s, str.find_last_not_of(chars) - s + 1);
@ -202,12 +202,41 @@ static std::string_view StripEnclosingChars(std::string_view str, T chars)
return "";
}
template <typename T>
static std::string_view StripLeadingChars(std::string_view str, T chars)
{
const std::size_t s = str.find_first_not_of(chars);
if (str.npos != s)
return str.substr(s);
else
return "";
}
template <typename T>
static std::string_view StripTrailingChars(std::string_view str, T chars)
{
return str.substr(0, str.find_last_not_of(chars) + 1);
}
// Turns "\n\r\t hello " into "hello" (trims at the start and end but not inside).
std::string_view StripWhitespace(std::string_view str)
{
return StripEnclosingChars(str, " \t\r\n");
}
// Turns "\n\r\t hello " into "hello " (trims at the start).
std::string_view StripLeadingWhitespace(std::string_view str)
{
return StripLeadingChars(str, " \t\r\n");
}
// Turns "\n\r\t hello " into "\n\r\t hello" (trims at the end).
std::string_view StripTrailingWhitespace(std::string_view str)
{
return StripTrailingChars(str, " \t\r\n");
}
std::string_view StripSpaces(std::string_view str)
{
return StripEnclosingChars(str, ' ');

View File

@ -49,6 +49,8 @@ inline void CharArrayFromFormat(char (&out)[Count], const char* format, ...)
std::string ArrayToString(const u8* data, u32 size, int line_len = 20, bool spaces = true);
std::string_view StripWhitespace(std::string_view s);
std::string_view StripLeadingWhitespace(std::string_view s);
std::string_view StripTrailingWhitespace(std::string_view s);
std::string_view StripSpaces(std::string_view s);
std::string_view StripQuotes(std::string_view s);

View File

@ -9,6 +9,7 @@
#include <string>
#include <utility>
#include "Common/CWDemangler.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
@ -31,6 +32,10 @@ void Symbol::Rename(const std::string& symbol_name)
{
this->name = symbol_name;
this->function_name = GetStrippedFunctionName(symbol_name);
// Try demangling the symbol name, saving it in the symbol if valid.
auto demangle_result = CWDemangler::demangle(symbol_name, CWDemangler::DemangleOptions());
this->demangled_name = demangle_result.value_or("");
}
void SymbolDB::List()

View File

@ -54,7 +54,22 @@ struct Symbol
void Rename(const std::string& symbol_name);
bool IsDemangled() const { return !demangled_name.empty(); }
const std::string& GetDisplayName(bool use_demangled_name) const
{
if (use_demangled_name && IsDemangled())
{
return demangled_name;
}
else
{
return name;
}
}
std::string name;
std::string demangled_name; // Demangled symbol name
std::string function_name; // stripped function name
std::string object_name; // name of object/source file symbol belongs to
std::vector<SCall> callers; // addresses of functions that call this function

View File

@ -48,6 +48,7 @@
<ClInclude Include="Common\Crypto\ec.h" />
<ClInclude Include="Common\Crypto\HMAC.h" />
<ClInclude Include="Common\Crypto\SHA1.h" />
<ClInclude Include="Common\CWDemangler.h" />
<ClInclude Include="Common\Debug\MemoryPatches.h" />
<ClInclude Include="Common\Debug\Threads.h" />
<ClInclude Include="Common\Debug\Watches.h" />
@ -839,6 +840,7 @@
<ClCompile Include="Common\Crypto\ec.cpp" />
<ClCompile Include="Common\Crypto\HMAC.cpp" />
<ClCompile Include="Common\Crypto\SHA1.cpp" />
<ClCompile Include="Common\CWDemangler.cpp" />
<ClCompile Include="Common\Debug\MemoryPatches.cpp" />
<ClCompile Include="Common\Debug\Watches.cpp" />
<ClCompile Include="Common\DirectIOFile.cpp" />

View File

@ -73,6 +73,9 @@ CodeWidget::CodeWidget(QWidget* parent)
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, &CodeWidget::Update);
connect(&Settings::Instance(), &Settings::ShowDemangledNamesChanged, this,
&CodeWidget::OnShowDemangledNamesChanged);
ConnectWidgets();
m_code_splitter->restoreState(
@ -248,6 +251,16 @@ void CodeWidget::OnSetCodeAddress(u32 address)
SetAddress(address, CodeViewWidget::SetAddressUpdate::WithDetailedUpdate);
}
void CodeWidget::OnShowDemangledNamesChanged()
{
UpdateSymbols();
if (const Common::Symbol* symbol = m_ppc_symbol_db.GetSymbolFromAddr(m_code_view->GetAddress()))
{
UpdateFunctionCalls(symbol);
UpdateFunctionCallers(symbol);
}
}
void CodeWidget::OnPPCSymbolsChanged()
{
UpdateSymbols();
@ -424,7 +437,7 @@ void CodeWidget::UpdateSymbols()
m_symbols_list->clear();
m_ppc_symbol_db.ForEachSymbol([&](const Common::Symbol& symbol) {
QString name = QString::fromStdString(symbol.name);
QString name = QString::fromStdString(GetSymbolDisplayName(&symbol));
// If the symbol has an object name, add it to the entry name.
if (!symbol.object_name.empty())
@ -488,15 +501,16 @@ void CodeWidget::UpdateFunctionCalls(const Common::Symbol* symbol)
if (call_symbol)
{
QString name;
const std::string& symbol_name = GetSymbolDisplayName(call_symbol);
if (!call_symbol->object_name.empty())
{
name = QString::fromStdString(
fmt::format("< {} ({}, {:08x})", call_symbol->name, call_symbol->object_name, addr));
fmt::format("< {} ({}, {:08x})", symbol_name, call_symbol->object_name, addr));
}
else
{
name = QString::fromStdString(fmt::format("< {} ({:08x})", call_symbol->name, addr));
name = QString::fromStdString(fmt::format("< {} ({:08x})", symbol_name, addr));
}
if (!name.contains(filter, Qt::CaseInsensitive))
@ -525,15 +539,16 @@ void CodeWidget::UpdateFunctionCallers(const Common::Symbol* symbol)
if (caller_symbol)
{
QString name;
const std::string& symbol_name = GetSymbolDisplayName(caller_symbol);
if (!caller_symbol->object_name.empty())
{
name = QString::fromStdString(fmt::format("< {} ({}, {:08x})", caller_symbol->name,
caller_symbol->object_name, addr));
name = QString::fromStdString(
fmt::format("< {} ({}, {:08x})", symbol_name, caller_symbol->object_name, addr));
}
else
{
name = QString::fromStdString(fmt::format("< {} ({:08x})", caller_symbol->name, addr));
name = QString::fromStdString(fmt::format("< {} ({:08x})", symbol_name, addr));
}
if (!name.contains(filter, Qt::CaseInsensitive))
@ -546,6 +561,14 @@ void CodeWidget::UpdateFunctionCallers(const Common::Symbol* symbol)
}
}
// Gets the name of this symbol based on the option for whether or not to show
// demangled names.
const std::string& CodeWidget::GetSymbolDisplayName(const Common::Symbol* symbol) const
{
const bool show_demangled_names = Settings::Instance().IsShowDemangledNames();
return symbol->GetDisplayName(show_demangled_names);
}
void CodeWidget::Step()
{
auto& cpu = m_system.GetCPU();

View File

@ -64,6 +64,7 @@ private:
void UpdateFunctionCallers(const Common::Symbol* symbol);
void UpdateNotes();
void OnShowDemangledNamesChanged();
void OnPPCSymbolsChanged();
void OnSearchAddress();
void OnSearchSymbols();
@ -76,6 +77,8 @@ private:
void closeEvent(QCloseEvent*) override;
void showEvent(QShowEvent* event) override;
const std::string& GetSymbolDisplayName(const Common::Symbol* symbol) const;
Core::System& m_system;
PPCSymbolDB& m_ppc_symbol_db;

View File

@ -1059,6 +1059,7 @@ void MenuBar::AddSymbolsMenu()
generate->addAction(tr("Address"), this, &MenuBar::GenerateSymbolsFromAddress);
generate->addAction(tr("Signature Database"), this, &MenuBar::GenerateSymbolsFromSignatureDB);
generate->addAction(tr("RSO Modules"), this, &MenuBar::GenerateSymbolsFromRSO);
m_debugger_show_demangled_names = m_symbols->addAction(tr("Show &Demangled Names"));
m_symbols->addSeparator();
m_symbols->addAction(tr("&Load Symbol Map"), this, &MenuBar::LoadSymbolMap);
@ -1082,6 +1083,13 @@ void MenuBar::AddSymbolsMenu()
m_symbols->addSeparator();
m_symbols->addAction(tr("&Patch HLE Functions"), this, &MenuBar::PatchHLEFunctions);
m_debugger_show_demangled_names->setCheckable(true);
m_debugger_show_demangled_names->setChecked(Settings::Instance().IsShowDemangledNames());
connect(m_debugger_show_demangled_names, &QAction::toggled, &Settings::Instance(),
&Settings::SetShowDemangledNames);
connect(&Settings::Instance(), &Settings::ShowDemangledNamesChanged,
m_debugger_show_demangled_names, &QAction::setChecked);
}
void MenuBar::UpdateToolsMenu(const Core::State state)

View File

@ -309,6 +309,7 @@ private:
QAction* m_jit_systemregisters_off;
QAction* m_jit_branch_off;
QAction* m_jit_register_cache_off;
QAction* m_debugger_show_demangled_names;
bool m_game_selected = false;

View File

@ -835,6 +835,20 @@ QFont Settings::GetDebugFont() const
return GetQSettings().value(QStringLiteral("debugger/font"), default_font).value<QFont>();
}
void Settings::SetShowDemangledNames(bool enabled)
{
if (IsShowDemangledNames() == enabled)
return;
QSettings().setValue(QStringLiteral("debugger/showdemanglednames"), enabled);
emit ShowDemangledNamesChanged(enabled);
}
bool Settings::IsShowDemangledNames() const
{
return QSettings().value(QStringLiteral("debugger/showdemanglednames")).toBool();
}
void Settings::SetAutoUpdateTrack(const QString& mode)
{
if (mode == GetAutoUpdateTrack())

View File

@ -174,6 +174,8 @@ public:
bool IsAssemblerVisible() const;
QFont GetDebugFont() const;
void SetDebugFont(QFont font);
void SetShowDemangledNames(bool enabled);
bool IsShowDemangledNames() const;
// Auto-Update
QString GetAutoUpdateTrack() const;
@ -221,6 +223,7 @@ signals:
void AssemblerVisibilityChanged(bool visible);
void DebugModeToggled(bool enabled);
void DebugFontChanged(const QFont& font);
void ShowDemangledNamesChanged(bool enabled);
void AutoUpdateTrackChanged(const QString& mode);
void FallbackRegionChanged(const DiscIO::Region& region);
void AnalyticsToggled(bool enabled);

View File

@ -7,6 +7,7 @@ add_dolphin_test(BusyLoopTest BusyLoopTest.cpp)
add_dolphin_test(CommonFuncsTest CommonFuncsTest.cpp)
add_dolphin_test(CryptoEcTest Crypto/EcTest.cpp)
add_dolphin_test(CryptoSHA1Test Crypto/SHA1Test.cpp)
add_dolphin_test(CWDemanglerTest CWDemanglerTest.cpp)
add_dolphin_test(EnumFormatterTest EnumFormatterTest.cpp)
add_dolphin_test(EventTest EventTest.cpp)
add_dolphin_test(FileUtilTest FileUtilTest.cpp)

View File

@ -0,0 +1,475 @@
// Copyright 2026 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// Based on: https://github.com/encounter/cwdemangle
// Copyright 2024 Luke Street <luke@street.dev>
// SPDX-License-Identifier: CC0-1.0
#include <gtest/gtest.h>
#include <optional>
#include <string>
#include <tuple>
#include "Common/CWDemangler.h"
using namespace CWDemangler;
void DoDemangleTemplateArgsTest(std::string mangled, std::string name, std::string template_args)
{
DemangleOptions options = DemangleOptions();
auto result = demangle_template_args(mangled, options);
DemangleTemplateArgsResult expected = {name, template_args};
EXPECT_TRUE(result.has_value());
if (result.has_value())
{
EXPECT_EQ(result.value().args, expected.args);
EXPECT_EQ(result.value().name, expected.name);
}
}
void DoDemangleNameTest(std::string mangled, std::string name, std::string full_name)
{
DemangleOptions options = DemangleOptions();
auto result = demangle_name(mangled, options);
DemangleNameResult expected = {name, full_name, ""};
EXPECT_TRUE(result.has_value());
if (result.has_value())
{
EXPECT_EQ(result.value().class_name, expected.class_name);
EXPECT_EQ(result.value().full, expected.full);
EXPECT_EQ(result.value().rest, expected.rest);
}
}
void DoDemangleQualifiedNameTest(std::string mangled, std::string base_name, std::string full_name)
{
DemangleOptions options = DemangleOptions();
auto result = demangle_qualified_name(mangled, options);
DemangleNameResult expected = {base_name, full_name, ""};
EXPECT_TRUE(result.has_value());
if (result.has_value())
{
EXPECT_EQ(result.value().class_name, expected.class_name);
EXPECT_EQ(result.value().full, expected.full);
EXPECT_EQ(result.value().rest, expected.rest);
}
}
void DoDemangleArgTest(std::string mangled, std::string type_pre, std::string type_post,
std::string remainder)
{
DemangleOptions options = DemangleOptions();
auto result = CWDemangler::demangle_arg(mangled, options);
DemangleArgResult expected = {type_pre, type_post, remainder};
EXPECT_TRUE(result.has_value());
if (result.has_value())
{
EXPECT_EQ(result.value().arg_post, expected.arg_post);
EXPECT_EQ(result.value().arg_pre, expected.arg_pre);
EXPECT_EQ(result.value().rest, expected.rest);
}
}
void DoDemangleFunctionArgsTest(std::string mangled, std::string args, std::string remainder)
{
DemangleOptions options = DemangleOptions();
auto result = demangle_function_args(mangled, options);
DemangleFunctionArgsResult expected = {args, remainder};
EXPECT_TRUE(result.has_value());
if (result.has_value())
{
EXPECT_EQ(result.value().args, expected.args);
EXPECT_EQ(result.value().rest, expected.rest);
}
}
void DoDemangleTest(std::string mangled, std::string demangled)
{
DemangleOptions options = DemangleOptions();
auto result = demangle(mangled, options);
std::optional<std::string> expected = {demangled};
if (demangled == "")
expected = std::nullopt;
EXPECT_EQ(result, expected);
}
void DoDemangleOptionsTest(bool omit_empty_params, bool mw_extensions, std::string mangled,
std::string demangled)
{
DemangleOptions options = DemangleOptions(omit_empty_params, mw_extensions);
auto result = demangle(mangled, options);
std::optional<std::string> expected = {demangled};
EXPECT_EQ(result, expected);
}
TEST(CWDemangler, TestDemangleTemplateArgs)
{
DoDemangleTemplateArgsTest("single_ptr<10CModelData>", "single_ptr", "<CModelData>");
DoDemangleTemplateArgsTest("basic_string<w,Q24rstl14char_traits<w>,Q24rstl17rmemory_allocator>",
"basic_string",
"<wchar_t, rstl::char_traits<wchar_t>, rstl::rmemory_allocator>");
}
TEST(CWDemangler, TestDemangleName)
{
DoDemangleNameTest("24single_ptr<10CModelData>", "single_ptr", "single_ptr<CModelData>");
DoDemangleNameTest("66basic_string<w,Q24rstl14char_traits<w>,Q24rstl17rmemory_allocator>",
"basic_string",
"basic_string<wchar_t, rstl::char_traits<wchar_t>, rstl::rmemory_allocator>");
}
TEST(CWDemangler, TestDemangleQualifiedName)
{
DoDemangleQualifiedNameTest("6CActor", "CActor", "CActor");
DoDemangleQualifiedNameTest("Q29CVector3f4EDim", "EDim", "CVector3f::EDim");
DoDemangleQualifiedNameTest(
"Q24rstl66basic_string<w,Q24rstl14char_traits<w>,Q24rstl17rmemory_allocator>", "basic_string",
"rstl::basic_string<wchar_t, rstl::char_traits<wchar_t>, rstl::rmemory_allocator>");
}
TEST(CWDemangler, TestDemangleArg)
{
DoDemangleArgTest("v", "void", "", "");
DoDemangleArgTest("b", "bool", "", "");
DoDemangleArgTest("RC9CVector3fUc", "const CVector3f&", "", "Uc");
DoDemangleArgTest("Q24rstl14char_traits<w>,", "rstl::char_traits<wchar_t>", "", ",");
DoDemangleArgTest("PFPCcPCc_v", "void (*", ")(const char*, const char*)", "");
DoDemangleArgTest("RCPCVPCVUi", "const volatile unsigned int* const volatile* const&", "", "");
}
TEST(CWDemangler, TestDemangleFunctionArgs)
{
DoDemangleFunctionArgsTest("v", "void", "");
DoDemangleFunctionArgsTest("b", "bool", "");
DoDemangleFunctionArgsTest("RC9CVector3fUc_x", "const CVector3f&, unsigned char", "_x");
}
TEST(CWDemangler, TestDemangle)
{
DoDemangleTest("__dt__6CActorFv", "CActor::~CActor()");
DoDemangleTest("GetSfxHandle__6CActorCFv", "CActor::GetSfxHandle() const");
DoDemangleTest(
"mNull__Q24rstl66basic_string<w,Q24rstl14char_traits<w>,Q24rstl17rmemory_allocator>",
"rstl::basic_string<wchar_t, rstl::char_traits<wchar_t>, "
"rstl::rmemory_allocator>::mNull");
DoDemangleTest(
"__ct__Q34rstl495red_black_tree<Ux,Q24rstl194pair<Ux,Q24rstl175auto_ptr<Q24rstl155map<s,"
"Q24rstl96auto_ptr<Q24rstl77list<Q24rstl35auto_ptr<23CGuiFrameMessageMapNode>,"
"Q24rstl17rmemory_allocator>>,Q24rstl7less<s>,Q24rstl17rmemory_allocator>>>,0,"
"Q24rstl215select1st<Q24rstl194pair<Ux,Q24rstl175auto_ptr<Q24rstl155map<s,Q24rstl96auto_"
"ptr<Q24rstl77list<Q24rstl35auto_ptr<23CGuiFrameMessageMapNode>,Q24rstl17rmemory_"
"allocator>>,Q24rstl7less<s>,Q24rstl17rmemory_allocator>>>>,Q24rstl8less<Ux>,"
"Q24rstl17rmemory_allocator>8iteratorFPQ34rstl495red_black_tree<Ux,Q24rstl194pair<Ux,"
"Q24rstl175auto_ptr<Q24rstl155map<s,Q24rstl96auto_ptr<Q24rstl77list<Q24rstl35auto_ptr<"
"23CGuiFrameMessageMapNode>,Q24rstl17rmemory_allocator>>,Q24rstl7less<s>,"
"Q24rstl17rmemory_allocator>>>,0,Q24rstl215select1st<Q24rstl194pair<Ux,Q24rstl175auto_"
"ptr<Q24rstl155map<s,Q24rstl96auto_ptr<Q24rstl77list<Q24rstl35auto_ptr<"
"23CGuiFrameMessageMapNode>,Q24rstl17rmemory_allocator>>,Q24rstl7less<s>,"
"Q24rstl17rmemory_allocator>>>>,Q24rstl8less<Ux>,Q24rstl17rmemory_allocator>"
"4nodePCQ34rstl495red_black_tree<Ux,Q24rstl194pair<Ux,Q24rstl175auto_ptr<Q24rstl155map<s,"
"Q24rstl96auto_ptr<Q24rstl77list<Q24rstl35auto_ptr<23CGuiFrameMessageMapNode>,"
"Q24rstl17rmemory_allocator>>,Q24rstl7less<s>,Q24rstl17rmemory_allocator>>>,0,"
"Q24rstl215select1st<Q24rstl194pair<Ux,Q24rstl175auto_ptr<Q24rstl155map<s,Q24rstl96auto_"
"ptr<Q24rstl77list<Q24rstl35auto_ptr<23CGuiFrameMessageMapNode>,Q24rstl17rmemory_"
"allocator>>,Q24rstl7less<s>,Q24rstl17rmemory_allocator>>>>,Q24rstl8less<Ux>,"
"Q24rstl17rmemory_allocator>6header",
"rstl::red_black_tree<unsigned long long, rstl::pair<unsigned long long, "
"rstl::auto_ptr<rstl::map<short, "
"rstl::auto_ptr<rstl::list<rstl::auto_ptr<CGuiFrameMessageMapNode>, "
"rstl::rmemory_allocator>>, rstl::less<short>, rstl::rmemory_allocator>>>, 0, "
"rstl::select1st<rstl::pair<unsigned long long, rstl::auto_ptr<rstl::map<short, "
"rstl::auto_ptr<rstl::list<rstl::auto_ptr<CGuiFrameMessageMapNode>, "
"rstl::rmemory_allocator>>, rstl::less<short>, rstl::rmemory_allocator>>>>, "
"rstl::less<unsigned long long>, "
"rstl::rmemory_allocator>::iterator::iterator(rstl::red_black_tree<unsigned long long, "
"rstl::pair<unsigned long long, rstl::auto_ptr<rstl::map<short, "
"rstl::auto_ptr<rstl::list<rstl::auto_ptr<CGuiFrameMessageMapNode>, "
"rstl::rmemory_allocator>>, rstl::less<short>, rstl::rmemory_allocator>>>, 0, "
"rstl::select1st<rstl::pair<unsigned long long, rstl::auto_ptr<rstl::map<short, "
"rstl::auto_ptr<rstl::list<rstl::auto_ptr<CGuiFrameMessageMapNode>, "
"rstl::rmemory_allocator>>, rstl::less<short>, rstl::rmemory_allocator>>>>, "
"rstl::less<unsigned long long>, rstl::rmemory_allocator>::node*, const "
"rstl::red_black_tree<unsigned long long, rstl::pair<unsigned long long, "
"rstl::auto_ptr<rstl::map<short, "
"rstl::auto_ptr<rstl::list<rstl::auto_ptr<CGuiFrameMessageMapNode>, "
"rstl::rmemory_allocator>>, rstl::less<short>, rstl::rmemory_allocator>>>, 0, "
"rstl::select1st<rstl::pair<unsigned long long, rstl::auto_ptr<rstl::map<short, "
"rstl::auto_ptr<rstl::list<rstl::auto_ptr<CGuiFrameMessageMapNode>, "
"rstl::rmemory_allocator>>, rstl::less<short>, rstl::rmemory_allocator>>>>, "
"rstl::less<unsigned long long>, rstl::rmemory_allocator>::header*)");
DoDemangleTest(
"for_each<PP12MultiEmitter,Q23std51binder2nd<Q23std30mem_fun1_t<v,12MultiEmitter,l>,"
"l>>__3stdFPP12MultiEmitterPP12MultiEmitterQ23std51binder2nd<Q23std30mem_fun1_t<v,"
"12MultiEmitter,l>,l>_Q23std51binder2nd<Q23std30mem_fun1_t<v,12MultiEmitter,l>,l>",
"std::binder2nd<std::mem_fun1_t<void, MultiEmitter, long>, long> "
"std::for_each<MultiEmitter**, std::binder2nd<std::mem_fun1_t<void, MultiEmitter, "
"long>, long>>(MultiEmitter**, MultiEmitter**, std::binder2nd<std::mem_fun1_t<void, "
"MultiEmitter, long>, long>)");
DoDemangleTest(
"__ct__Q43std3tr16detail383function_imp<PFPCcPCc_v,Q43std3tr16detail334bound_func<v,"
"Q43std3tr16detail59mem_fn_2<v,Q53scn4step7gimmick9shipevent9ShipEvent,PCc,PCc>,"
"Q33std3tr1228tuple<PQ53scn4step7gimmick9shipevent9ShipEvent,"
"Q53std3tr112placeholders6detail5ph<1>,Q53std3tr112placeholders6detail5ph<2>,"
"Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat,"
"Q33std3tr13nat,Q33std3tr13nat>>,0,1>FRCQ43std3tr16detail383function_imp<PFPCcPCc_v,"
"Q43std3tr16detail334bound_func<v,Q43std3tr16detail59mem_fn_2<v,"
"Q53scn4step7gimmick9shipevent9ShipEvent,PCc,PCc>,Q33std3tr1228tuple<"
"PQ53scn4step7gimmick9shipevent9ShipEvent,Q53std3tr112placeholders6detail5ph<1>,"
"Q53std3tr112placeholders6detail5ph<2>,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat,"
"Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat,Q33std3tr13nat>>,0,1>",
"std::tr1::detail::function_imp<void (*)(const char*, const char*), "
"std::tr1::detail::bound_func<void, std::tr1::detail::mem_fn_2<void, "
"scn::step::gimmick::shipevent::ShipEvent, const char*, const char*>, "
"std::tr1::tuple<scn::step::gimmick::shipevent::ShipEvent*, "
"std::tr1::placeholders::detail::ph<1>, std::tr1::placeholders::detail::ph<2>, "
"std::tr1::nat, std::tr1::nat, std::tr1::nat, std::tr1::nat, std::tr1::nat, "
"std::tr1::nat, std::tr1::nat>>, 0, 1>::function_imp(const "
"std::tr1::detail::function_imp<void (*)(const char*, const char*), "
"std::tr1::detail::bound_func<void, std::tr1::detail::mem_fn_2<void, "
"scn::step::gimmick::shipevent::ShipEvent, const char*, const char*>, "
"std::tr1::tuple<scn::step::gimmick::shipevent::ShipEvent*, "
"std::tr1::placeholders::detail::ph<1>, std::tr1::placeholders::detail::ph<2>, "
"std::tr1::nat, std::tr1::nat, std::tr1::nat, std::tr1::nat, std::tr1::nat, "
"std::tr1::nat, std::tr1::nat>>, 0, 1>&)");
DoDemangleTest(
"createJointController<11IKJointCtrl>__"
"2MRFP11IKJointCtrlPC9LiveActorUsM11IKJointCtrlFPCvPvPQ29JGeometry64TPosition3<"
"Q29JGeometry38TMatrix34<Q29JGeometry13SMatrix34C<f>>>RC19JointControllerInfo_"
"bM11IKJointCtrlFPCvPvPQ29JGeometry64TPosition3<Q29JGeometry38TMatrix34<"
"Q29JGeometry13SMatrix34C<f>>>RC19JointControllerInfo_b_P15JointController",
"JointController* MR::createJointController<IKJointCtrl>(IKJointCtrl*, const "
"LiveActor*, unsigned short, bool "
"(IKJointCtrl::*)(JGeometry::TPosition3<JGeometry::TMatrix34<JGeometry::SMatrix34C<"
"float>>>*, const JointControllerInfo&), bool "
"(IKJointCtrl::*)(JGeometry::TPosition3<JGeometry::TMatrix34<JGeometry::SMatrix34C<"
"float>>>*, const JointControllerInfo&))");
DoDemangleTest("execCommand__12JASSeqParserFP8JASTrackM12JASSeqParserFPCvPvP8JASTrackPUl_lUlPUl",
"JASSeqParser::execCommand(JASTrack*, long (JASSeqParser::*)(JASTrack*, unsigned "
"long*), unsigned long, unsigned long*)");
DoDemangleTest("AddWidgetFnMap__"
"10CGuiWidgetFiM10CGuiWidgetFPCvPvP15CGuiFunctionDefP18CGuiControllerInfo_i",
"CGuiWidget::AddWidgetFnMap(int, int (CGuiWidget::*)(CGuiFunctionDef*, "
"CGuiControllerInfo*))");
DoDemangleTest("BareFn__FPFPCcPv_v_v", "void BareFn(void (*)(const char*, void*))");
DoDemangleTest("BareFn__FPFPCcPv_v_PFPCvPv_v",
"void (* BareFn(void (*)(const char*, void*)))(const void*, void*)");
DoDemangleTest(
"SomeFn__FRCPFPFPCvPv_v_RCPFPCvPv_v",
"SomeFn(void (*const& (*const&)(void (*)(const void*, void*)))(const void*, void*))");
DoDemangleTest(
"SomeFn__Q29Namespace5ClassCFRCMQ29Namespace5ClassFPCvPCvMQ29Namespace5ClassFPCvPCvPCvPv_"
"v_RCMQ29Namespace5ClassFPCvPCvPCvPv_v",
"Namespace::Class::SomeFn(void (Namespace::Class::*const & (Namespace::Class::*const "
"&)(void (Namespace::Class::*)(const void*, void*) const) const)(const void*, void*) "
"const) const");
DoDemangleTest("__pl__FRC9CRelAngleRC9CRelAngle",
"operator+(const CRelAngle&, const CRelAngle&)");
DoDemangleTest("destroy<PUi>__4rstlFPUiPUi",
"rstl::destroy<unsigned int*>(unsigned int*, unsigned int*)");
DoDemangleTest("__opb__33TFunctor2<CP15CGuiSliderGroup,Cf>CFv",
"TFunctor2<CGuiSliderGroup* const, const float>::operator bool() const");
DoDemangleTest("__opRC25TToken<15CCharLayoutInfo>__31TLockedToken<15CCharLayoutInfo>CFv",
"TLockedToken<CCharLayoutInfo>::operator const TToken<CCharLayoutInfo>&() const");
DoDemangleTest(
"uninitialized_copy<Q24rstl198pointer_iterator<"
"Q224CSpawnSystemKeyframeData24CSpawnSystemKeyframeInfo,Q24rstl89vector<"
"Q224CSpawnSystemKeyframeData24CSpawnSystemKeyframeInfo,Q24rstl17rmemory_allocator>,"
"Q24rstl17rmemory_allocator>,PQ224CSpawnSystemKeyframeData24CSpawnSystemKeyframeInfo>__"
"4rstlFQ24rstl198pointer_iterator<Q224CSpawnSystemKeyframeData24CSpawnSystemKeyframeInfo,"
"Q24rstl89vector<Q224CSpawnSystemKeyframeData24CSpawnSystemKeyframeInfo,Q24rstl17rmemory_"
"allocator>,Q24rstl17rmemory_allocator>Q24rstl198pointer_iterator<"
"Q224CSpawnSystemKeyframeData24CSpawnSystemKeyframeInfo,Q24rstl89vector<"
"Q224CSpawnSystemKeyframeData24CSpawnSystemKeyframeInfo,Q24rstl17rmemory_allocator>,"
"Q24rstl17rmemory_allocator>PQ224CSpawnSystemKeyframeData24CSpawnSystemKeyframeInfo",
"rstl::uninitialized_copy<rstl::pointer_iterator<CSpawnSystemKeyframeData::"
"CSpawnSystemKeyframeInfo, "
"rstl::vector<CSpawnSystemKeyframeData::CSpawnSystemKeyframeInfo, "
"rstl::rmemory_allocator>, rstl::rmemory_allocator>, "
"CSpawnSystemKeyframeData::CSpawnSystemKeyframeInfo*>(rstl::pointer_iterator<"
"CSpawnSystemKeyframeData::CSpawnSystemKeyframeInfo, "
"rstl::vector<CSpawnSystemKeyframeData::CSpawnSystemKeyframeInfo, "
"rstl::rmemory_allocator>, rstl::rmemory_allocator>, "
"rstl::pointer_iterator<CSpawnSystemKeyframeData::CSpawnSystemKeyframeInfo, "
"rstl::vector<CSpawnSystemKeyframeData::CSpawnSystemKeyframeInfo, "
"rstl::rmemory_allocator>, rstl::rmemory_allocator>, "
"CSpawnSystemKeyframeData::CSpawnSystemKeyframeInfo*)");
DoDemangleTest(
"__rf__Q34rstl120list<Q24rstl78pair<i,PFRC10SObjectTagR12CInputStreamRC15CVParamTransfer_"
"C16CFactoryFnReturn>,Q24rstl17rmemory_allocator>14const_iteratorCFv",
"rstl::list<rstl::pair<int, const CFactoryFnReturn (*)(const SObjectTag&, CInputStream&, "
"const CVParamTransfer&)>, rstl::rmemory_allocator>::const_iterator::operator->() const");
DoDemangleTest(
"ApplyRipples__FRC14CRippleManagerRA43_A43_Q220CFluidPlaneCPURender13SHFieldSampleRA22_"
"A22_UcRA256_CfRQ220CFluidPlaneCPURender10SPatchInfo",
"ApplyRipples(const CRippleManager&, CFluidPlaneCPURender::SHFieldSample(&)[43][43], "
"unsigned char(&)[22][22], const float(&)[256], CFluidPlaneCPURender::SPatchInfo&)");
DoDemangleTest("CalculateFluidTextureOffset__14CFluidUVMotionCFfPA2_f",
"CFluidUVMotion::CalculateFluidTextureOffset(float, float(*)[2]) const");
DoDemangleTest(
"RenderNormals__FRA43_A43_CQ220CFluidPlaneCPURender13SHFieldSampleRA22_A22_"
"CUcRCQ220CFluidPlaneCPURender10SPatchInfo",
"RenderNormals(const CFluidPlaneCPURender::SHFieldSample(&)[43][43], const unsigned "
"char(&)[22][22], const CFluidPlaneCPURender::SPatchInfo&)");
DoDemangleTest("Matrix__FfPA2_A3_f", "Matrix(float, float(*)[2][3])");
DoDemangleTest("__ct<12CStringTable>__31CObjOwnerDerivedFromIObjUntypedFRCQ24rstl24auto_ptr<"
"12CStringTable>",
"CObjOwnerDerivedFromIObjUntyped::CObjOwnerDerivedFromIObjUntyped<CStringTable>("
"const rstl::auto_ptr<CStringTable>&)");
DoDemangleTest("__vt__40TObjOwnerDerivedFromIObj<12CStringTable>",
"TObjOwnerDerivedFromIObj<CStringTable>::__vtable");
DoDemangleTest("__RTTI__40TObjOwnerDerivedFromIObj<12CStringTable>",
"TObjOwnerDerivedFromIObj<CStringTable>::__RTTI");
DoDemangleTest("__init__mNull__Q24rstl66basic_string<c,Q24rstl14char_traits<c>,Q24rstl17rmemory_"
"allocator>",
"rstl::basic_string<char, rstl::char_traits<char>, "
"rstl::rmemory_allocator>::__init__mNull");
DoDemangleTest("__dt__26__partial_array_destructorFv",
"__partial_array_destructor::~__partial_array_destructor()");
DoDemangleTest(
"__distance<Q34rstl195red_black_tree<13TGameScriptId,Q24rstl32pair<13TGameScriptId,"
"9TUniqueId>,1,Q24rstl52select1st<Q24rstl32pair<13TGameScriptId,9TUniqueId>>,"
"Q24rstl21less<13TGameScriptId>,Q24rstl17rmemory_allocator>14const_iterator>__"
"4rstlFQ34rstl195red_black_tree<13TGameScriptId,Q24rstl32pair<13TGameScriptId,9TUniqueId>"
",1,Q24rstl52select1st<Q24rstl32pair<13TGameScriptId,9TUniqueId>>,Q24rstl21less<"
"13TGameScriptId>,Q24rstl17rmemory_allocator>14const_iteratorQ34rstl195red_black_tree<"
"13TGameScriptId,Q24rstl32pair<13TGameScriptId,9TUniqueId>,1,Q24rstl52select1st<"
"Q24rstl32pair<13TGameScriptId,9TUniqueId>>,Q24rstl21less<13TGameScriptId>,"
"Q24rstl17rmemory_allocator>14const_iteratorQ24rstl20forward_iterator_tag",
"rstl::__distance<rstl::red_black_tree<TGameScriptId, rstl::pair<TGameScriptId, "
"TUniqueId>, 1, rstl::select1st<rstl::pair<TGameScriptId, TUniqueId>>, "
"rstl::less<TGameScriptId>, "
"rstl::rmemory_allocator>::const_iterator>(rstl::red_black_tree<TGameScriptId, "
"rstl::pair<TGameScriptId, TUniqueId>, 1, rstl::select1st<rstl::pair<TGameScriptId, "
"TUniqueId>>, rstl::less<TGameScriptId>, rstl::rmemory_allocator>::const_iterator, "
"rstl::red_black_tree<TGameScriptId, rstl::pair<TGameScriptId, TUniqueId>, 1, "
"rstl::select1st<rstl::pair<TGameScriptId, TUniqueId>>, rstl::less<TGameScriptId>, "
"rstl::rmemory_allocator>::const_iterator, rstl::forward_iterator_tag)");
DoDemangleTest(
"__ct__Q210Metrowerks683compressed_pair<RQ23std301allocator<Q33std276__tree_deleter<"
"Q23std34pair<Ci,Q212petfurniture8Instance>,Q33std131__multimap_do_transform<i,"
"Q212petfurniture8Instance,Q23std7less<i>,Q23std53allocator<Q23std34pair<Ci,"
"Q212petfurniture8Instance>>,0>13value_compare,Q23std53allocator<Q23std34pair<Ci,"
"Q212petfurniture8Instance>>>4node>,Q210Metrowerks337compressed_pair<"
"Q210Metrowerks12number<Ul,1>,PQ33std276__tree_deleter<Q23std34pair<Ci,"
"Q212petfurniture8Instance>,Q33std131__multimap_do_transform<i,Q212petfurniture8Instance,"
"Q23std7less<i>,Q23std53allocator<Q23std34pair<Ci,Q212petfurniture8Instance>>,0>13value_"
"compare,Q23std53allocator<Q23std34pair<Ci,Q212petfurniture8Instance>>>4node>>"
"FRQ23std301allocator<Q33std276__tree_deleter<Q23std34pair<Ci,Q212petfurniture8Instance>,"
"Q33std131__multimap_do_transform<i,Q212petfurniture8Instance,Q23std7less<i>,"
"Q23std53allocator<Q23std34pair<Ci,Q212petfurniture8Instance>>,0>13value_compare,"
"Q23std53allocator<Q23std34pair<Ci,Q212petfurniture8Instance>>>4node>"
"Q210Metrowerks337compressed_pair<Q210Metrowerks12number<Ul,1>,PQ33std276__tree_deleter<"
"Q23std34pair<Ci,Q212petfurniture8Instance>,Q33std131__multimap_do_transform<i,"
"Q212petfurniture8Instance,Q23std7less<i>,Q23std53allocator<Q23std34pair<Ci,"
"Q212petfurniture8Instance>>,0>13value_compare,Q23std53allocator<Q23std34pair<Ci,"
"Q212petfurniture8Instance>>>4node>",
"Metrowerks::compressed_pair<std::allocator<std::__tree_deleter<std::pair<const int, "
"petfurniture::Instance>, std::__multimap_do_transform<int, petfurniture::Instance, "
"std::less<int>, std::allocator<std::pair<const int, petfurniture::Instance>>, "
"0>::value_compare, std::allocator<std::pair<const int, "
"petfurniture::Instance>>>::node>&, "
"Metrowerks::compressed_pair<Metrowerks::number<unsigned long, 1>, "
"std::__tree_deleter<std::pair<const int, petfurniture::Instance>, "
"std::__multimap_do_transform<int, petfurniture::Instance, std::less<int>, "
"std::allocator<std::pair<const int, petfurniture::Instance>>, 0>::value_compare, "
"std::allocator<std::pair<const int, "
"petfurniture::Instance>>>::node*>>::compressed_pair(std::allocator<std::__tree_deleter<"
"std::pair<const int, petfurniture::Instance>, std::__multimap_do_transform<int, "
"petfurniture::Instance, std::less<int>, std::allocator<std::pair<const int, "
"petfurniture::Instance>>, 0>::value_compare, std::allocator<std::pair<const int, "
"petfurniture::Instance>>>::node>&, "
"Metrowerks::compressed_pair<Metrowerks::number<unsigned long, 1>, "
"std::__tree_deleter<std::pair<const int, petfurniture::Instance>, "
"std::__multimap_do_transform<int, petfurniture::Instance, std::less<int>, "
"std::allocator<std::pair<const int, petfurniture::Instance>>, 0>::value_compare, "
"std::allocator<std::pair<const int, petfurniture::Instance>>>::node*>)");
DoDemangleTest(
"skBadString$localstatic3$GetNameByToken__31TTokenSet<18EScriptObjectState>"
"CF18EScriptObjectState",
"TTokenSet<EScriptObjectState>::GetNameByToken(EScriptObjectState) const::skBadString");
DoDemangleTest("init$localstatic4$GetNameByToken__31TTokenSet<18EScriptObjectState>"
"CF18EScriptObjectState",
"TTokenSet<EScriptObjectState>::GetNameByToken(EScriptObjectState) "
"const::localstatic4 guard");
DoDemangleTest("@LOCAL@GetAnmPlayPolicy__Q24nw4r3g3dFQ34nw4r3g3d9AnmPolicy@policyTable",
"nw4r::g3d::GetAnmPlayPolicy(nw4r::g3d::AnmPolicy)::policyTable");
DoDemangleTest("@GUARD@GetAnmPlayPolicy__Q24nw4r3g3dFQ34nw4r3g3d9AnmPolicy@policyTable",
"nw4r::g3d::GetAnmPlayPolicy(nw4r::g3d::AnmPolicy)::policyTable guard");
DoDemangleTest(
"lower_bound<Q24rstl180const_pointer_iterator<Q24rstl33pair<Ui,22CAdditiveAnimationInfo>,"
"Q24rstl77vector<Q24rstl33pair<Ui,22CAdditiveAnimationInfo>,Q24rstl17rmemory_allocator>,"
"Q24rstl17rmemory_allocator>,Ui,Q24rstl79pair_sorter_finder<Q24rstl33pair<Ui,"
"22CAdditiveAnimationInfo>,Q24rstl8less<Ui>>>__4rstlFQ24rstl180const_pointer_iterator<"
"Q24rstl33pair<Ui,22CAdditiveAnimationInfo>,Q24rstl77vector<Q24rstl33pair<Ui,"
"22CAdditiveAnimationInfo>,Q24rstl17rmemory_allocator>,Q24rstl17rmemory_allocator>"
"Q24rstl180const_p",
"");
DoDemangleTest("test__FRCPCPCi", "test(const int* const* const&)");
DoDemangleTest("__ct__Q34nw4r2ut14CharStrmReaderFMQ34nw4r2ut14CharStrmReaderFPCvPv_Us",
"nw4r::ut::CharStrmReader::CharStrmReader(unsigned short "
"(nw4r::ut::CharStrmReader::*)())");
DoDemangleTest("QuerySymbolToMapFile___Q24nw4r2dbFPUcPC12OSModuleInfoUlPUcUl",
"nw4r::db::QuerySymbolToMapFile_(unsigned char*, const OSModuleInfo*, unsigned "
"long, unsigned char*, unsigned long)");
DoDemangleTest("__ct__Q37JGadget27TLinkList<10JUTConsole,-24>"
"8iteratorFQ37JGadget13TNodeLinkList8iterator",
"JGadget::TLinkList<JUTConsole, "
"-24>::iterator::iterator(JGadget::TNodeLinkList::iterator)");
DoDemangleTest(
"do_assign<Q23std126__convert_iterator<PP16GAM_eEngineState,Q33std68__cdeque<P16GAM_"
"eEngineState,36ubiSTLAllocator<P16GAM_eEngineState>>8iterator>>__Q23std36__cdeque<PCv,"
"20ubiSTLAllocator<PCv>>FQ23std126__convert_iterator<PP16GAM_eEngineState,Q33std68__"
"cdeque<P16GAM_eEngineState,36ubiSTLAllocator<P16GAM_eEngineState>>8iterator>Q23std126__"
"convert_iterator<PP16GAM_eEngineState,Q33std68__cdeque<P16GAM_eEngineState,"
"36ubiSTLAllocator<P16GAM_eEngineState>>8iterator>Q23std20forward_iterator_tag",
"std::__cdeque<const void*, ubiSTLAllocator<const "
"void*>>::do_assign<std::__convert_iterator<GAM_eEngineState**, "
"std::__cdeque<GAM_eEngineState*, "
"ubiSTLAllocator<GAM_eEngineState*>>::iterator>>(std::__convert_iterator<GAM_"
"eEngineState**, std::__cdeque<GAM_eEngineState*, "
"ubiSTLAllocator<GAM_eEngineState*>>::iterator>, "
"std::__convert_iterator<GAM_eEngineState**, std::__cdeque<GAM_eEngineState*, "
"ubiSTLAllocator<GAM_eEngineState*>>::iterator>, std::forward_iterator_tag)");
DoDemangleTest(
"__opPCQ23std15__locale_imp<1>__Q23std80_RefCountedPtr<Q23std15__locale_imp<1>,"
"Q23std32_Single<Q23std15__locale_imp<1>>>CFv",
"std::_RefCountedPtr<std::__locale_imp<1>, "
"std::_Single<std::__locale_imp<1>>>::operator const std::__locale_imp<1>*() const");
DoDemangleTest("__partition_const_ref<PP12CSpaceObject,Q23std74unary_negate<Q23std52__binder1st_"
"const_ref<Q23std21less<P12CSpaceObject>>>>__"
"3stdFPP12CSpaceObjectPP12CSpaceObjectRCQ23std74unary_negate<Q23std52__binder1st_"
"const_ref<Q23std21less<P12CSpaceObject>>>",
"std::__partition_const_ref<CSpaceObject**, "
"std::unary_negate<std::__binder1st_const_ref<std::less<CSpaceObject*>>>>("
"CSpaceObject**, CSpaceObject**, const "
"std::unary_negate<std::__binder1st_const_ref<std::less<CSpaceObject*>>>&)");
}
TEST(CWDemangler, TestDemangleOptions)
{
DoDemangleOptionsTest(true, false, "__dt__26__partial_array_destructorFv",
"__partial_array_destructor::~__partial_array_destructor()");
DoDemangleOptionsTest(false, false, "__dt__26__partial_array_destructorFv",
"__partial_array_destructor::~__partial_array_destructor(void)");
DoDemangleOptionsTest(true, true,
"__opPCQ23std15__locale_imp<1>__Q23std80_RefCountedPtr<Q23std15__locale_"
"imp<1>,Q23std32_Single<Q23std15__locale_imp<1>>>CFv",
"std::_RefCountedPtr<std::__locale_imp<__int128>, "
"std::_Single<std::__locale_imp<__int128>>>::operator const "
"std::__locale_imp<__int128>*() const");
DoDemangleOptionsTest(true, true, "fn<3,PV2>__FPC2",
"fn<3, volatile __vec2x32float__*>(const __vec2x32float__*)");
}

View File

@ -47,6 +47,7 @@
<ClCompile Include="Common\CommonFuncsTest.cpp" />
<ClCompile Include="Common\Crypto\EcTest.cpp" />
<ClCompile Include="Common\Crypto\SHA1Test.cpp" />
<ClCompile Include="Common\CWDemanglerTest.cpp" />
<ClCompile Include="Common\EnumFormatterTest.cpp" />
<ClCompile Include="Common\EventTest.cpp" />
<ClCompile Include="Common\FileUtilTest.cpp" />