mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-02-04 03:40:11 +00:00
HLE linkage rewritten
This commit is contained in:
parent
14f02b23c2
commit
7428bb3025
@ -1435,5 +1435,5 @@ DECLARE(ppu_module_manager::cellGcmSys)("cellGcmSys", []()
|
||||
REG_FUNC(cellGcmSys, cellGcmGpadCaptureSnapshot);
|
||||
|
||||
// Special
|
||||
REG_FNID(cellGcmSys, 0x00000000, cellGcmCallback);
|
||||
REG_FUNC(cellGcmSys, cellGcmCallback).flags = MFF_HIDDEN;
|
||||
});
|
||||
|
@ -2149,6 +2149,8 @@ void PPUDisAsm::FCFID(ppu_opcode_t op)
|
||||
DisAsm_F2_RC("fcfid", op.frd, op.frb, op.rc);
|
||||
}
|
||||
|
||||
extern std::vector<std::string> g_ppu_function_names;
|
||||
|
||||
void PPUDisAsm::UNK(ppu_opcode_t op)
|
||||
{
|
||||
if (op.opcode == dump_pc && ppu_function_manager::addr)
|
||||
@ -2158,7 +2160,7 @@ void PPUDisAsm::UNK(ppu_opcode_t op)
|
||||
|
||||
if (index < ppu_function_manager::get().size())
|
||||
{
|
||||
Write(fmt::format("Function : (index %u)", index));
|
||||
Write(fmt::format("Function : %s (index %u)", index < g_ppu_function_names.size() ? g_ppu_function_names[index].c_str() : "?", index));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -125,6 +125,8 @@ cfg::bool_entry g_cfg_load_libreq(cfg::root.core, "Load required libraries", tru
|
||||
|
||||
cfg::set_entry g_cfg_load_libs(cfg::root.core, "Load libraries");
|
||||
|
||||
extern cfg::map_entry<ppu_decoder_type> g_cfg_ppu_decoder;
|
||||
|
||||
extern std::string ppu_get_function_name(const std::string& module, u32 fnid);
|
||||
extern std::string ppu_get_variable_name(const std::string& module, u32 vnid);
|
||||
extern void ppu_register_range(u32 addr, u32 size);
|
||||
@ -136,6 +138,9 @@ extern void sys_initialize_tls(ppu_thread&, u64, u32, u32, u32);
|
||||
|
||||
extern u32 g_ps3_sdk_version;
|
||||
|
||||
// HLE function name cache
|
||||
std::vector<std::string> g_ppu_function_names;
|
||||
|
||||
extern u32 ppu_generate_id(const char* name)
|
||||
{
|
||||
// Symbol name suffix
|
||||
@ -195,13 +200,16 @@ struct ppu_linkage_info
|
||||
{
|
||||
struct info
|
||||
{
|
||||
bool hle = false;
|
||||
u32 export_addr = 0;
|
||||
std::set<u32> imports;
|
||||
};
|
||||
|
||||
// FNID -> (export; [imports...])
|
||||
std::map<u32, info> functions;
|
||||
std::map<u32, info> variables;
|
||||
std::unordered_map<u32, info, value_hash<u32>> functions;
|
||||
std::unordered_map<u32, info, value_hash<u32>> variables;
|
||||
|
||||
bool imported = false;
|
||||
};
|
||||
|
||||
// Module map
|
||||
@ -209,7 +217,7 @@ struct ppu_linkage_info
|
||||
};
|
||||
|
||||
// Initialize static modules.
|
||||
static void ppu_initialize_modules()
|
||||
static void ppu_initialize_modules(const std::shared_ptr<ppu_linkage_info>& link)
|
||||
{
|
||||
const std::initializer_list<const ppu_static_module*> registered
|
||||
{
|
||||
@ -312,23 +320,6 @@ static void ppu_initialize_modules()
|
||||
&ppu_module_manager::sys_lv2dbg,
|
||||
};
|
||||
|
||||
// "Use" all the modules for correct linkage
|
||||
for (auto& module : registered)
|
||||
{
|
||||
LOG_TRACE(LOADER, "Registered static module: %s", module->name);
|
||||
|
||||
for (auto& function : module->functions)
|
||||
{
|
||||
LOG_TRACE(LOADER, "** 0x%08X: %s", function.first, function.second.name);
|
||||
}
|
||||
|
||||
for (auto& variable : module->variables)
|
||||
{
|
||||
LOG_TRACE(LOADER, "** &0x%08X: %s (size=0x%x, align=0x%x)", variable.first, variable.second.name, variable.second.size, variable.second.align);
|
||||
variable.second.var->set(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize double-purpose fake OPD array for HLE functions
|
||||
const auto& hle_funcs = ppu_function_manager::get();
|
||||
|
||||
@ -352,6 +343,86 @@ static void ppu_initialize_modules()
|
||||
|
||||
// Set memory protection to read-only
|
||||
vm::page_protect(ppu_function_manager::addr, ::align(::size32(hle_funcs) * 8, 0x1000), 0, 0, vm::page_writable);
|
||||
|
||||
// Initialize function names
|
||||
const bool is_first = g_ppu_function_names.empty();
|
||||
|
||||
if (is_first)
|
||||
{
|
||||
g_ppu_function_names.resize(hle_funcs.size());
|
||||
g_ppu_function_names[0] = "INVALID";
|
||||
g_ppu_function_names[1] = "HLE RETURN";
|
||||
}
|
||||
|
||||
// For HLE variable allocation
|
||||
u32 alloc_addr = 0;
|
||||
|
||||
// "Use" all the modules for correct linkage
|
||||
for (auto& module : registered)
|
||||
{
|
||||
LOG_TRACE(LOADER, "Registered static module: %s", module->name);
|
||||
|
||||
auto& linkage = link->modules[module->name];
|
||||
|
||||
for (auto& function : module->functions)
|
||||
{
|
||||
LOG_TRACE(LOADER, "** 0x%08X: %s", function.first, function.second.name);
|
||||
|
||||
if (is_first)
|
||||
{
|
||||
g_ppu_function_names[function.second.index] = fmt::format("%s.%s", module->name, function.second.name);
|
||||
}
|
||||
|
||||
if ((function.second.flags & MFF_HIDDEN) == 0)
|
||||
{
|
||||
auto& flink = linkage.functions[function.first];
|
||||
|
||||
flink.hle = true;
|
||||
flink.export_addr = ppu_function_manager::addr + 8 * function.second.index;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& variable : module->variables)
|
||||
{
|
||||
LOG_TRACE(LOADER, "** &0x%08X: %s (size=0x%x, align=0x%x)", variable.first, variable.second.name, variable.second.size, variable.second.align);
|
||||
|
||||
// Allocate HLE variable
|
||||
if (variable.second.size >= 4096 || variable.second.align >= 4096)
|
||||
{
|
||||
variable.second.var->set(vm::alloc(variable.second.size, vm::main, std::max<u32>(variable.second.align, 4096)));
|
||||
}
|
||||
else
|
||||
{
|
||||
const u32 next = ::align(alloc_addr, variable.second.align);
|
||||
const u32 end = next + variable.second.size;
|
||||
|
||||
if (!next || (end >> 12 != alloc_addr >> 12))
|
||||
{
|
||||
alloc_addr = vm::alloc(4096, vm::main);
|
||||
}
|
||||
else
|
||||
{
|
||||
alloc_addr = next;
|
||||
}
|
||||
|
||||
variable.second.var->set(alloc_addr);
|
||||
alloc_addr += variable.second.size;
|
||||
}
|
||||
|
||||
LOG_TRACE(LOADER, "Allocated HLE variable %s.%s at 0x%x", module->name, variable.second.name, variable.second.var->addr());
|
||||
|
||||
// Initialize HLE variable
|
||||
if (variable.second.init)
|
||||
{
|
||||
variable.second.init();
|
||||
}
|
||||
|
||||
auto& vlink = linkage.variables[variable.first];
|
||||
|
||||
vlink.hle = true;
|
||||
vlink.export_addr = variable.second.var->addr();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Link variable
|
||||
@ -452,6 +523,9 @@ static auto ppu_load_exports(const std::shared_ptr<ppu_linkage_info>& link, u32
|
||||
// Static module
|
||||
const auto _sm = ppu_module_manager::get_module(module_name);
|
||||
|
||||
// Module linkage
|
||||
auto& mlink = link->modules[module_name];
|
||||
|
||||
const auto fnids = +lib.nids;
|
||||
const auto faddrs = +lib.addrs;
|
||||
|
||||
@ -463,13 +537,13 @@ static auto ppu_load_exports(const std::shared_ptr<ppu_linkage_info>& link, u32
|
||||
LOG_NOTICE(LOADER, "**** %s export: [%s] at 0x%x", module_name, ppu_get_function_name(module_name, fnid), faddr);
|
||||
|
||||
// Function linkage info
|
||||
auto& flink = link->modules[module_name].functions[fnid];
|
||||
auto& flink = mlink.functions[fnid];
|
||||
|
||||
if (flink.export_addr)
|
||||
if (flink.export_addr && !flink.hle)
|
||||
{
|
||||
LOG_FATAL(LOADER, "Already linked function '%s' in module '%s'", ppu_get_function_name(module_name, fnid), module_name);
|
||||
LOG_ERROR(LOADER, "Already linked function '%s' in module '%s'", ppu_get_function_name(module_name, fnid), module_name);
|
||||
}
|
||||
else
|
||||
//else
|
||||
{
|
||||
// Static function
|
||||
const auto _sf = _sm && _sm->functions.count(fnid) ? &_sm->functions.at(fnid) : nullptr;
|
||||
@ -499,6 +573,7 @@ static auto ppu_load_exports(const std::shared_ptr<ppu_linkage_info>& link, u32
|
||||
{
|
||||
// Set exported function
|
||||
flink.export_addr = faddr;
|
||||
flink.hle = false;
|
||||
|
||||
// Fix imports
|
||||
for (const u32 addr : flink.imports)
|
||||
@ -521,16 +596,17 @@ static auto ppu_load_exports(const std::shared_ptr<ppu_linkage_info>& link, u32
|
||||
LOG_NOTICE(LOADER, "**** %s export: &[%s] at 0x%x", module_name, ppu_get_variable_name(module_name, vnid), vaddr);
|
||||
|
||||
// Variable linkage info
|
||||
auto& vlink = link->modules[module_name].variables[vnid];
|
||||
auto& vlink = mlink.variables[vnid];
|
||||
|
||||
if (vlink.export_addr)
|
||||
if (vlink.export_addr && !vlink.hle)
|
||||
{
|
||||
LOG_FATAL(LOADER, "Already linked variable '%s' in module '%s'", ppu_get_variable_name(module_name, vnid), module_name);
|
||||
LOG_ERROR(LOADER, "Already linked variable '%s' in module '%s'", ppu_get_variable_name(module_name, vnid), module_name);
|
||||
}
|
||||
else
|
||||
//else
|
||||
{
|
||||
// Set exported variable
|
||||
vlink.export_addr = vaddr;
|
||||
vlink.hle = false;
|
||||
|
||||
// Fix imports
|
||||
for (const auto vref : vlink.imports)
|
||||
@ -565,6 +641,9 @@ static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32
|
||||
// Static module
|
||||
const auto _sm = ppu_module_manager::get_module(module_name);
|
||||
|
||||
// Module linkage
|
||||
auto& mlink = link->modules[module_name];
|
||||
|
||||
const auto fnids = +lib.nids;
|
||||
const auto faddrs = +lib.addrs;
|
||||
|
||||
@ -580,12 +659,17 @@ static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32
|
||||
|
||||
// Add new import
|
||||
flink.imports.emplace(faddr);
|
||||
mlink.imported = true;
|
||||
|
||||
// Link if available
|
||||
if (flink.export_addr)
|
||||
{
|
||||
vm::write32(faddr, flink.export_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
vm::write32(faddr, ppu_function_manager::addr);
|
||||
}
|
||||
|
||||
//LOG_WARNING(LOADER, "Imported function '%s' in module '%s' (0x%x)", ppu_get_function_name(module_name, fnid), module_name, faddr);
|
||||
}
|
||||
@ -604,6 +688,7 @@ static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32
|
||||
|
||||
// Add new import
|
||||
vlink.imports.emplace(vref);
|
||||
mlink.imported = true;
|
||||
|
||||
// Link if available
|
||||
if (vlink.export_addr)
|
||||
@ -620,6 +705,12 @@ static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32
|
||||
|
||||
std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::string& name)
|
||||
{
|
||||
if (g_cfg_ppu_decoder.get() == ppu_decoder_type::llvm && name == "libfiber.sprx")
|
||||
{
|
||||
LOG_FATAL(PPU, "libfiber.sprx is not compatible with PPU LLVM Recompiler. Use PPU Interpreter.");
|
||||
Emu.Pause();
|
||||
}
|
||||
|
||||
std::vector<std::pair<u32, u32>> segments;
|
||||
std::vector<std::pair<u32, u32>> sections;
|
||||
|
||||
@ -889,7 +980,7 @@ void ppu_load_exec(const ppu_exec_object& elf)
|
||||
}
|
||||
|
||||
// Initialize HLE modules
|
||||
ppu_initialize_modules();
|
||||
ppu_initialize_modules(link);
|
||||
|
||||
// Load other programs
|
||||
for (auto& prog : elf.progs)
|
||||
@ -1120,8 +1211,14 @@ void ppu_load_exec(const ppu_exec_object& elf)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: recursively scan all SPRX files in /app_home/ for imports
|
||||
for (const auto& pair : link->modules)
|
||||
{
|
||||
if (!pair.second.imported)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto range = sprx_map.equal_range(pair.first); range.first != range.second; ++range.first)
|
||||
{
|
||||
load_libs.emplace(range.first->second);
|
||||
@ -1169,106 +1266,6 @@ void ppu_load_exec(const ppu_exec_object& elf)
|
||||
}
|
||||
}
|
||||
|
||||
// Check unlinked functions and variables
|
||||
for (auto& module : link->modules)
|
||||
{
|
||||
const auto _sm = ppu_module_manager::get_module(module.first);
|
||||
|
||||
if (!_sm)
|
||||
{
|
||||
LOG_ERROR(LOADER, "Unknown module '%s'", module.first);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Allocate HLE variables (TODO)
|
||||
for (auto& var : _sm->variables)
|
||||
{
|
||||
var.second.var->set(vm::alloc(var.second.size, vm::main, std::max<u32>(var.second.align, 4096)));
|
||||
LOG_WARNING(LOADER, "Allocated variable '%s' in module '%s' at *0x%x", var.second.name, module.first, var.second.var->addr());
|
||||
}
|
||||
|
||||
// Initialize HLE variables (TODO)
|
||||
for (auto& var : _sm->variables)
|
||||
{
|
||||
var.second.init();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& entry : module.second.functions)
|
||||
{
|
||||
const u32 fnid = entry.first;
|
||||
const u32 faddr = entry.second.export_addr;
|
||||
|
||||
if (faddr == 0)
|
||||
{
|
||||
const std::string fname = ppu_get_function_name(module.first, fnid);
|
||||
|
||||
// Link HLE implementation if available
|
||||
if (const auto _sf = _sm && _sm->functions.count(fnid) ? &_sm->functions.at(fnid) : nullptr)
|
||||
{
|
||||
LOG_NOTICE(LOADER, "Linking HLE function '%s' in module '%s' (index %u)", fname, module.first, _sf->index);
|
||||
|
||||
for (const u32 import : entry.second.imports)
|
||||
{
|
||||
LOG_TRACE(LOADER, "** Linked at *0x%x (0x%x)", import, vm::read32(import));
|
||||
vm::write32(import, ppu_function_manager::addr + 8 * _sf->index);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR(LOADER, "Unknown function '%s' in module '%s'", fname, module.first);
|
||||
|
||||
for (const u32 import : entry.second.imports)
|
||||
{
|
||||
LOG_WARNING(LOADER, "** Not linked at *0x%x (0x%x)", import, vm::read32(import));
|
||||
vm::write32(import, ppu_function_manager::addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& entry : module.second.variables)
|
||||
{
|
||||
const u32 vnid = entry.first;
|
||||
const u32 vaddr = entry.second.export_addr;
|
||||
|
||||
if (vaddr == 0)
|
||||
{
|
||||
const std::string vname = ppu_get_variable_name(module.first, vnid);
|
||||
|
||||
// Link HLE variable if available
|
||||
if (const auto _sv = _sm && _sm->variables.count(vnid) ? &_sm->variables.at(vnid) : nullptr)
|
||||
{
|
||||
LOG_NOTICE(LOADER, "Linking HLE variable '%s' in module '%s' (*0x%x):", vname, module.first, _sv->var->addr());
|
||||
|
||||
for (const u32 ref : entry.second.imports)
|
||||
{
|
||||
ppu_patch_variable_refs(ref, _sv->var->addr());
|
||||
LOG_NOTICE(LOADER, "** Linked at ref=*0x%x", ref);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR(LOADER, "Unknown variable '%s' in module '%s'", vname, module.first);
|
||||
|
||||
for (const u32 ref : entry.second.imports)
|
||||
{
|
||||
LOG_WARNING(LOADER, "** Not linked at ref=*0x%x", ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Retro-link LLE variable (TODO: HLE must not be allocated/initialized in this case)
|
||||
if (const auto _sv = _sm && _sm->variables.count(vnid) ? &_sm->variables.at(vnid) : nullptr)
|
||||
{
|
||||
_sv->var->set(vaddr);
|
||||
LOG_NOTICE(LOADER, "Linked LLE variable '%s' in module '%s' -> 0x%x", ppu_get_variable_name(module.first, vnid), module.first, vaddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Analyse executable
|
||||
std::vector<ppu_function> main_funcs = ppu_analyse(segments, sections, 0, elf.header.e_entry);
|
||||
@ -1307,7 +1304,9 @@ void ppu_load_exec(const ppu_exec_object& elf)
|
||||
ppu->cmd_push({ppu_cmd::initialize, 0});
|
||||
|
||||
// TODO: adjust for liblv2 loading option
|
||||
if (!g_cfg_load_liblv2)
|
||||
u32 entry = static_cast<u32>(elf.header.e_entry);
|
||||
|
||||
if (!g_cfg_load_liblv2 || g_cfg_load_libreq)
|
||||
{
|
||||
// Set TLS args, call sys_initialize_tls
|
||||
ppu->cmd_list
|
||||
@ -1316,6 +1315,13 @@ void ppu_load_exec(const ppu_exec_object& elf)
|
||||
{ ppu_cmd::hle_call, FIND_FUNC(sys_initialize_tls) },
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Run liblv2.sprx entry point (TODO)
|
||||
entry = loaded_modules[0]->start.addr();
|
||||
|
||||
loaded_modules.clear();
|
||||
}
|
||||
|
||||
// Run start functions
|
||||
for (const auto& prx : loaded_modules)
|
||||
@ -1337,9 +1343,9 @@ void ppu_load_exec(const ppu_exec_object& elf)
|
||||
ppu->cmd_list
|
||||
({
|
||||
{ ppu_cmd::set_args, 8 }, u64{args.size()}, u64{argv.addr()}, u64{envp.addr()}, u64{0}, u64{ppu->id}, u64{tls_vaddr}, u64{tls_fsize}, u64{tls_vsize},
|
||||
{ ppu_cmd::set_gpr, 11 }, u64{0xabadcafe},
|
||||
{ ppu_cmd::set_gpr, 11 }, u64{elf.header.e_entry},
|
||||
{ ppu_cmd::set_gpr, 12 }, u64{malloc_pagesize},
|
||||
{ ppu_cmd::lle_call, static_cast<u32>(elf.header.e_entry) },
|
||||
{ ppu_cmd::lle_call, entry },
|
||||
});
|
||||
|
||||
// Set actual memory protection (experimental)
|
||||
|
@ -14,6 +14,7 @@ enum ppu_static_function_flags : u32
|
||||
{
|
||||
MFF_FORCED_HLE = (1 << 0), // Always call HLE function
|
||||
MFF_PERFECT = (1 << 1), // Indicates complete implementation and LLE interchangeability
|
||||
MFF_HIDDEN = (1 << 2), // Invisible function for internal use (TODO)
|
||||
};
|
||||
|
||||
// HLE function information
|
||||
@ -43,8 +44,8 @@ public:
|
||||
task_stack on_load;
|
||||
task_stack on_unload;
|
||||
|
||||
std::map<u32, ppu_static_function> functions;
|
||||
std::map<u32, ppu_static_variable> variables;
|
||||
std::unordered_map<u32, ppu_static_function, value_hash<u32>> functions;
|
||||
std::unordered_map<u32, ppu_static_variable, value_hash<u32>> variables;
|
||||
|
||||
public:
|
||||
ppu_static_module(const char* name);
|
||||
|
@ -82,13 +82,6 @@ void fmt_class_string<join_status>::format(std::string& out, u64 arg)
|
||||
});
|
||||
}
|
||||
|
||||
enum class ppu_decoder_type
|
||||
{
|
||||
precise,
|
||||
fast,
|
||||
llvm,
|
||||
};
|
||||
|
||||
cfg::map_entry<ppu_decoder_type> g_cfg_ppu_decoder(cfg::root.core, "PPU Decoder", 1,
|
||||
{
|
||||
{ "Interpreter (precise)", ppu_decoder_type::precise },
|
||||
@ -868,17 +861,6 @@ extern void ppu_initialize()
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_cfg_ppu_decoder.get() == ppu_decoder_type::llvm)
|
||||
{
|
||||
idm::select<lv2_obj, lv2_prx>([](u32, lv2_prx& prx)
|
||||
{
|
||||
if (prx.name == "libfiber.sprx")
|
||||
{
|
||||
fmt::raw_error("libfiber.sprx is not compatible with PPU LLVM Recompiler.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::size_t fpos = 0;
|
||||
|
||||
while (fpos < _funcs->size())
|
||||
|
@ -18,6 +18,13 @@ enum class ppu_cmd : u32
|
||||
sleep,
|
||||
};
|
||||
|
||||
enum class ppu_decoder_type
|
||||
{
|
||||
precise,
|
||||
fast,
|
||||
llvm,
|
||||
};
|
||||
|
||||
class ppu_thread : public cpu_thread
|
||||
{
|
||||
public:
|
||||
|
@ -363,7 +363,15 @@ void Emulator::Load()
|
||||
}
|
||||
|
||||
debug::autopause::reload();
|
||||
if (g_cfg_autostart) Run();
|
||||
|
||||
if (g_cfg_autostart && IsReady())
|
||||
{
|
||||
Run();
|
||||
}
|
||||
else if (IsPaused())
|
||||
{
|
||||
m_status = Ready;
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
@ -412,7 +420,7 @@ bool Emulator::Pause()
|
||||
// Try to pause
|
||||
if (!m_status.compare_and_swap_test(Running, Paused))
|
||||
{
|
||||
return false;
|
||||
return m_status.compare_and_swap_test(Ready, Paused);
|
||||
}
|
||||
|
||||
rpcs3::on_pause()();
|
||||
|
@ -193,7 +193,7 @@ void KernelExplorer::Update()
|
||||
case SYS_PRX_OBJECT:
|
||||
{
|
||||
auto& prx = static_cast<lv2_prx&>(obj);
|
||||
m_tree->AppendItem(node, fmt::format("PRX: ID = 0x%08x", id));
|
||||
m_tree->AppendItem(node, fmt::format("PRX: ID = 0x%08x '%s'", id, prx.name));
|
||||
break;
|
||||
}
|
||||
case SYS_SPUPORT_OBJECT:
|
||||
|
Loading…
x
Reference in New Issue
Block a user