From 4a15ff950c944053c639e9874d641d545157abf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 31 Mar 2025 12:54:57 +0200 Subject: [PATCH 1/6] ImDebugger: Add new Modules windows, turn the old Modules window into what will be a symbol browser --- Core/HLE/sceKernel.h | 6 ++-- Core/HLE/sceKernelMemory.cpp | 17 +++++------ Core/HLE/sceKernelModule.cpp | 20 ++++++------- UI/ImDebugger/ImDebugger.cpp | 58 +++++++++++++++++++++++++++++++++++- UI/ImDebugger/ImDebugger.h | 1 + 5 files changed, 78 insertions(+), 24 deletions(-) diff --git a/Core/HLE/sceKernel.h b/Core/HLE/sceKernel.h index 1780c1cc0f..be320275cd 100644 --- a/Core/HLE/sceKernel.h +++ b/Core/HLE/sceKernel.h @@ -204,15 +204,15 @@ public: return static_cast(pool[realHandle]); } - template - void Iterate(bool func(T *, ArgT), ArgT arg) { + template + void Iterate(F func) { int type = T::GetStaticIDType(); for (int i = 0; i < maxCount; i++) { if (!occupied[i]) continue; T *t = static_cast(pool[i]); if (t->GetIDType() == type) { - if (!func(t, arg)) + if (!func(i, t)) break; } } diff --git a/Core/HLE/sceKernelMemory.cpp b/Core/HLE/sceKernelMemory.cpp index 423c31c771..2b34498111 100644 --- a/Core/HLE/sceKernelMemory.cpp +++ b/Core/HLE/sceKernelMemory.cpp @@ -1970,14 +1970,6 @@ struct FindTLSByIndexArg { TLSPL *result = nullptr; }; -static bool FindTLSByIndex(TLSPL *possible, FindTLSByIndexArg *state) { - if (possible->ntls.index == state->index) { - state->result = possible; - return false; - } - return true; -} - int sceKernelGetTlsAddr(SceUID uid) { if (!__KernelIsDispatchEnabled() || __IsInInterrupt()) return hleLogWarning(Log::sceKernel, 0, "dispatch disabled"); @@ -1994,7 +1986,14 @@ int sceKernelGetTlsAddr(SceUID uid) { FindTLSByIndexArg state; state.index = (uid >> 3) & 15; - kernelObjects.Iterate(&FindTLSByIndex, &state); + kernelObjects.Iterate([&state](int id, TLSPL *possible) { + if (possible->ntls.index == state.index) { + state.result = possible; + return false; + } + return true; + }); + if (!state.result) return hleLogError(Log::sceKernel, 0, "tlspl not found"); diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index c547342e3b..da5a26fff3 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -1581,7 +1581,6 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load module->nm.entry_addr = module->nm.module_start_func; MIPSAnalyst::PrecompileFunctions(); - } else { module->nm.entry_addr = -1; } @@ -2364,22 +2363,21 @@ struct GetModuleIdByAddressArg SceUID result; }; -static bool __GetModuleIdByAddressIterator(PSPModule *module, GetModuleIdByAddressArg *state) { - const u32 start = module->memoryBlockAddr, size = module->memoryBlockSize; - if (start != 0 && start <= state->addr && start + size > state->addr) { - state->result = module->GetUID(); - return false; - } - return true; -} - static u32 sceKernelGetModuleIdByAddress(u32 moduleAddr) { GetModuleIdByAddressArg state; state.addr = moduleAddr; state.result = SCE_KERNEL_ERROR_UNKNOWN_MODULE; - kernelObjects.Iterate(&__GetModuleIdByAddressIterator, &state); + kernelObjects.Iterate([&state](int id, PSPModule *module) -> bool { + const u32 start = module->memoryBlockAddr, size = module->memoryBlockSize; + if (start != 0 && start <= state.addr && start + size > state.addr) { + state.result = module->GetUID(); + return false; + } + return true; + }); + if (state.result == (SceUID)SCE_KERNEL_ERROR_UNKNOWN_MODULE) { return hleLogError(Log::sceModule, state.result, "module not found at address"); } else { diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index 028b500481..8e61259ec0 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -25,6 +25,7 @@ #include "Core/HLE/HLE.h" #include "Core/HLE/SocketManager.h" #include "Core/HLE/NetInetConstants.h" +#include "Core/HLE/sceKernelModule.h" #include "Core/HLE/sceNp.h" #include "Core/HLE/sceNet.h" #include "Core/HLE/sceNetApctl.h" @@ -1439,13 +1440,60 @@ static void DrawUtilityModules(ImConfig &cfg, ImControl &control) { ImGui::End(); } +// Started as a module browser but really only draws from the symbols database, so let's +// evolve it to that. static void DrawModules(const MIPSDebugInterface *debug, ImConfig &cfg, ImControl &control) { if (!ImGui::Begin("Modules", &cfg.modulesOpen) || !g_symbolMap) { ImGui::End(); return; } - // Hm, this reads from the symbol map. + ImGui::TextUnformatted("This shows modules that have been loaded by the game (not plain HLE)"); + + if (ImGui::BeginTable("modules", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) { + ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("IsFake", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Active", ImGuiTableColumnFlags_WidthFixed); + + ImGui::TableHeadersRow(); + + // TODO: Add context menu and clickability + kernelObjects.Iterate([&cfg, &control](int id, PSPModule *module) -> bool { + ImGui::PushID(id); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (ImGui::Selectable(module->GetName(), cfg.selectedModule == id, ImGuiSelectableFlags_SpanAllColumns)) { + cfg.selectedModule = id; + } + ImGui::TableNextColumn(); + ImClickableValue("addr", module->memoryBlockAddr, control, ImCmd::SHOW_IN_MEMORY_VIEWER); + ImGui::TableNextColumn(); + ImGui::Text("%08x", module->memoryBlockSize); + ImGui::TableNextColumn(); + ImGui::TextUnformatted("n/a"); + ImGui::PopID(); + return true; + }); + + ImGui::EndTable(); + } + + //if (cfg.selectedModule >= 0 && cfg.selectedModule < (int)modules.size()) { + // TODO: Show details + //} + ImGui::End(); +} + +// Started as a module browser but really only draws from the symbols database, so let's +// evolve it to that. +static void DrawSymbols(const MIPSDebugInterface *debug, ImConfig &cfg, ImControl &control) { + if (!ImGui::Begin("Symbols", &cfg.symbolsOpen) || !g_symbolMap) { + ImGui::End(); + return; + } + + // Reads from the symbol map. std::vector modules = g_symbolMap->getAllModules(); if (ImGui::BeginTable("modules", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) { ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed); @@ -1673,6 +1721,9 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu ImGui::EndMenu(); } if (ImGui::BeginMenu("Symbols")) { + ImGui::MenuItem("Symbol browser", nullptr, &cfg_.symbolsOpen); + ImGui::Separator(); + if (ImGui::MenuItem("Load .ppmap...")) { System_BrowseForFile(reqToken_, "Load PPSSPP symbol map", BrowseFileType::SYMBOL_MAP, [&](const char *responseString, int) { Path path(responseString); @@ -1836,6 +1887,10 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu DrawModules(mipsDebug, cfg_, control); } + if (cfg_.symbolsOpen) { + DrawSymbols(mipsDebug, cfg_, control); + } + if (cfg_.utilityModulesOpen) { DrawUtilityModules(cfg_, control); } @@ -2302,6 +2357,7 @@ void ImConfig::SyncConfig(IniFile *ini, bool save) { sync.Sync("threadsOpen", &threadsOpen, false); sync.Sync("callstackOpen", &callstackOpen, false); sync.Sync("breakpointsOpen", &breakpointsOpen, false); + sync.Sync("symbolsOpen", &symbolsOpen, false); sync.Sync("modulesOpen", &modulesOpen, false); sync.Sync("hleModulesOpen", &hleModulesOpen, false); sync.Sync("audioDecodersOpen", &audioDecodersOpen, false); diff --git a/UI/ImDebugger/ImDebugger.h b/UI/ImDebugger/ImDebugger.h index dd16b07932..cbea5d9bb0 100644 --- a/UI/ImDebugger/ImDebugger.h +++ b/UI/ImDebugger/ImDebugger.h @@ -124,6 +124,7 @@ struct ImConfig { bool threadsOpen; bool callstackOpen; bool breakpointsOpen; + bool symbolsOpen; bool modulesOpen; bool hleModulesOpen; bool audioDecodersOpen; From 8cd28dfd8279e2675cf5de1b1ba1c9d1f88f6515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 31 Mar 2025 14:15:24 +0200 Subject: [PATCH 2/6] Start work on real modules window in the debugger --- Core/HLE/sceKernel.h | 11 ++++++++++- UI/ImDebugger/ImDebugger.cpp | 29 ++++++++++++++++++++++------- UI/ImDebugger/ImDebugger.h | 3 ++- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/Core/HLE/sceKernel.h b/Core/HLE/sceKernel.h index be320275cd..1c521df73d 100644 --- a/Core/HLE/sceKernel.h +++ b/Core/HLE/sceKernel.h @@ -172,6 +172,15 @@ public: return occupied[index]; } + template + bool Is(SceUID handle) const { + int index = handle - handleOffset; + if (index < 0 || index >= maxCount) + return false; + else + return occupied[index] && pool[handle - handleOffset]->GetIDType() == T::GetStaticIDType(); + } + template T* Get(SceUID handle, u32 &outError) { if (handle < handleOffset || handle >= handleOffset+maxCount || !occupied[handle-handleOffset]) { @@ -212,7 +221,7 @@ public: continue; T *t = static_cast(pool[i]); if (t->GetIDType() == type) { - if (!func(i, t)) + if (!func(i + handleOffset, t)) break; } } diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index 8e61259ec0..33dffbcf34 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -1420,8 +1420,8 @@ static void DrawUtilityModules(ImConfig &cfg, ImControl &control) { ImGui::PushID(i); ImGui::TableNextRow(); ImGui::TableNextColumn(); - if (ImGui::Selectable(info->name, cfg.selectedModule == i, ImGuiSelectableFlags_SpanAllColumns)) { - cfg.selectedModule = i; + if (ImGui::Selectable(info->name, cfg.selectedUtilityModule == i, ImGuiSelectableFlags_SpanAllColumns)) { + cfg.selectedUtilityModule = i; } ImGui::TableNextColumn(); if (loadedAddr) { @@ -1463,8 +1463,8 @@ static void DrawModules(const MIPSDebugInterface *debug, ImConfig &cfg, ImContro ImGui::PushID(id); ImGui::TableNextRow(); ImGui::TableNextColumn(); - if (ImGui::Selectable(module->GetName(), cfg.selectedModule == id, ImGuiSelectableFlags_SpanAllColumns)) { - cfg.selectedModule = id; + if (ImGui::Selectable(module->GetName(), cfg.selectedModuleId == id, ImGuiSelectableFlags_SpanAllColumns)) { + cfg.selectedModuleId = id; } ImGui::TableNextColumn(); ImClickableValue("addr", module->memoryBlockAddr, control, ImCmd::SHOW_IN_MEMORY_VIEWER); @@ -1479,6 +1479,21 @@ static void DrawModules(const MIPSDebugInterface *debug, ImConfig &cfg, ImContro ImGui::EndTable(); } + if (kernelObjects.Is(cfg.selectedModuleId)) { + PSPModule *sel = kernelObjects.GetFast(cfg.selectedModuleId); + if (sel) { + ImGui::Text("%s %d.%d (%s)\n", sel->GetName(), sel->nm.version[1], sel->nm.version[0], sel->isFake ? "FAKE/HLE" : "normal"); + char buf[512]; + sel->GetLongInfo(buf, sizeof(buf)); + ImGui::TextUnformatted(buf); + if (ImGui::CollapsingHeader("Imports")) { + + } + if (ImGui::CollapsingHeader("Exports")) { + + } + } + } //if (cfg.selectedModule >= 0 && cfg.selectedModule < (int)modules.size()) { // TODO: Show details //} @@ -1509,8 +1524,8 @@ static void DrawSymbols(const MIPSDebugInterface *debug, ImConfig &cfg, ImContro ImGui::PushID(i); ImGui::TableNextRow(); ImGui::TableNextColumn(); - if (ImGui::Selectable(module.name.c_str(), cfg.selectedModule == i, ImGuiSelectableFlags_SpanAllColumns)) { - cfg.selectedModule = i; + if (ImGui::Selectable(module.name.c_str(), cfg.selectedSymbolModule == i, ImGuiSelectableFlags_SpanAllColumns)) { + cfg.selectedSymbolModule = i; } ImGui::TableNextColumn(); ImClickableValue("addr", module.address, control, ImCmd::SHOW_IN_MEMORY_VIEWER); @@ -1524,7 +1539,7 @@ static void DrawSymbols(const MIPSDebugInterface *debug, ImConfig &cfg, ImContro ImGui::EndTable(); } - if (cfg.selectedModule >= 0 && cfg.selectedModule < (int)modules.size()) { + if (cfg.selectedModuleId >= 0 && cfg.selectedModuleId < (int)modules.size()) { // TODO: Show details } ImGui::End(); diff --git a/UI/ImDebugger/ImDebugger.h b/UI/ImDebugger/ImDebugger.h index cbea5d9bb0..dd6f291ab6 100644 --- a/UI/ImDebugger/ImDebugger.h +++ b/UI/ImDebugger/ImDebugger.h @@ -159,7 +159,8 @@ struct ImConfig { // bool filterByUsed = true; // Various selections - int selectedModule = 0; + int selectedModuleId = 0; + int selectedSymbolModule = 0; int selectedUtilityModule = 0; int selectedThread = 0; int selectedKernelObject = 0; From 2fddf50653da3868ca65fdfc5c2cc8448912adf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 31 Mar 2025 16:55:35 +0200 Subject: [PATCH 3/6] Correct PRX dumping (was using the wrong size) --- Core/HLE/sceKernelModule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index da5a26fff3..f3a21aadc2 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -1154,7 +1154,7 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load // Use the name from the header. elfFilename = head->modname; } - DumpFileIfEnabled(ptr, head->psp_size, elfFilename.c_str(), DumpFileType::PRX); + DumpFileIfEnabled(ptr, elfSize, elfFilename.c_str(), DumpFileType::PRX); // This should happen for all "kernel" modules. *error_string = "Missing key"; From 717ea3ec8ea1679c1ff24f126f5eef220477e48f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 31 Mar 2025 17:52:22 +0200 Subject: [PATCH 4/6] ImDebugger: Show imports/exports in Module window --- Common/Common.vcxproj | 1 + Common/Common.vcxproj.filters | 5 +- Core/HLE/sceKernelModule.h | 1 + UI/ImDebugger/ImDebugger.cpp | 135 ++++++++++++++++++++++------------ ext/imgui/imgui_extras.h | 12 +++ 5 files changed, 108 insertions(+), 46 deletions(-) create mode 100644 ext/imgui/imgui_extras.h diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index 13782d7a60..1847fc0fac 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -405,6 +405,7 @@ + diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index 61b63aee76..48c9352654 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -691,6 +691,9 @@ ext\sol + + ext\imgui + @@ -1481,4 +1484,4 @@ ext\lua - + \ No newline at end of file diff --git a/Core/HLE/sceKernelModule.h b/Core/HLE/sceKernelModule.h index eb8480fbe5..c663ab11eb 100644 --- a/Core/HLE/sceKernelModule.h +++ b/Core/HLE/sceKernelModule.h @@ -182,6 +182,7 @@ public: NativeModule nm{}; std::vector waitingThreads; + // TODO: Should we store these grouped by moduleName instead? Seems more reasonable. std::vector exportedFuncs; std::vector importedFuncs; std::vector exportedVars; diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index 33dffbcf34..08c641d2fc 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -2,6 +2,7 @@ #include "ext/imgui/imgui_internal.h" +#include "ext/imgui/imgui_extras.h" #include "Common/StringUtils.h" #include "Common/File/FileUtil.h" @@ -1440,8 +1441,6 @@ static void DrawUtilityModules(ImConfig &cfg, ImControl &control) { ImGui::End(); } -// Started as a module browser but really only draws from the symbols database, so let's -// evolve it to that. static void DrawModules(const MIPSDebugInterface *debug, ImConfig &cfg, ImControl &control) { if (!ImGui::Begin("Modules", &cfg.modulesOpen) || !g_symbolMap) { ImGui::End(); @@ -1450,53 +1449,99 @@ static void DrawModules(const MIPSDebugInterface *debug, ImConfig &cfg, ImContro ImGui::TextUnformatted("This shows modules that have been loaded by the game (not plain HLE)"); - if (ImGui::BeginTable("modules", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) { - ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("IsFake", ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("Active", ImGuiTableColumnFlags_WidthFixed); + if (ImGui::BeginChild("module_list", ImVec2(170.0f, 0.0), ImGuiChildFlags_ResizeX)) { + if (ImGui::BeginTable("modules", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) { + ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableHeadersRow(); - ImGui::TableHeadersRow(); + // TODO: Add context menu and clickability + kernelObjects.Iterate([&cfg, &control](int id, PSPModule *module) -> bool { + ImGui::PushID(id); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (ImGui::Selectable(module->GetName(), cfg.selectedModuleId == id, ImGuiSelectableFlags_SpanAllColumns)) { + cfg.selectedModuleId = id; + } + ImGui::TableNextColumn(); + ImGui::TextUnformatted(module->isFake ? "FAKE/HLE" : "normal"); + ImGui::PopID(); + return true; + }); - // TODO: Add context menu and clickability - kernelObjects.Iterate([&cfg, &control](int id, PSPModule *module) -> bool { - ImGui::PushID(id); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - if (ImGui::Selectable(module->GetName(), cfg.selectedModuleId == id, ImGuiSelectableFlags_SpanAllColumns)) { - cfg.selectedModuleId = id; - } - ImGui::TableNextColumn(); - ImClickableValue("addr", module->memoryBlockAddr, control, ImCmd::SHOW_IN_MEMORY_VIEWER); - ImGui::TableNextColumn(); - ImGui::Text("%08x", module->memoryBlockSize); - ImGui::TableNextColumn(); - ImGui::TextUnformatted("n/a"); - ImGui::PopID(); - return true; - }); - - ImGui::EndTable(); - } - - if (kernelObjects.Is(cfg.selectedModuleId)) { - PSPModule *sel = kernelObjects.GetFast(cfg.selectedModuleId); - if (sel) { - ImGui::Text("%s %d.%d (%s)\n", sel->GetName(), sel->nm.version[1], sel->nm.version[0], sel->isFake ? "FAKE/HLE" : "normal"); - char buf[512]; - sel->GetLongInfo(buf, sizeof(buf)); - ImGui::TextUnformatted(buf); - if (ImGui::CollapsingHeader("Imports")) { - - } - if (ImGui::CollapsingHeader("Exports")) { - - } + ImGui::EndTable(); } + ImGui::EndChild(); + } + ImGui::SameLine(); + + if (ImGui::BeginChild("info")) { + if (kernelObjects.Is(cfg.selectedModuleId)) { + PSPModule *mod = kernelObjects.GetFast(cfg.selectedModuleId); + if (mod) { + if (mod->isFake) { + ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 255, 170)); + } + ImGui::Text("%s %d.%d (%s)\n", mod->GetName(), mod->nm.version[1], mod->nm.version[0], mod->isFake ? "FAKE/HLE" : "normal"); + char buf[512]; + mod->GetLongInfo(buf, sizeof(buf)); + ImGui::TextUnformatted(buf); + if (mod->isFake) { + ImGui::PopStyleColor(); + } + if (ImGui::CollapsingHeader("Import/export modules")) { + for (auto &name : mod->impExpModuleNames) { + ImGui::TextUnformatted(name); + } + } + if (ImGui::CollapsingHeader("Imports")) { + if (!mod->importedVars.empty()) { + if (ImGui::CollapsingHeader("Vars")) { + for (auto &var : mod->importedVars) { + ImGui::TextUnformatted("(some var)"); // TODO + } + } + } + for (auto &import : mod->importedFuncs) { + // Look the name up in our HLE database. + const HLEFunction *func = GetHLEFunc(import.moduleName, import.nid); + ImGui::TextUnformatted(import.moduleName); + if (func) { + ImGui::SameLine(); + ImGui::TextUnformatted(func->name); + } + ImGui::SameLine(); ImClickableValue("addr", import.stubAddr, control, ImCmd::SHOW_IN_CPU_DISASM); + } + } + if (!mod->exportedFuncs.empty() || !mod->exportedVars.empty()) { + if (ImGui::CollapsingHeader("Exports")) { + if (!mod->exportedVars.empty()) { + if (ImGui::CollapsingHeader("Vars")) { + for (auto &var : mod->importedVars) { + ImGui::TextUnformatted("(some var)"); // TODO + } + } + } + for (auto &exportFunc : mod->exportedFuncs) { + // Look the name up in our HLE database. + const HLEFunction *func = GetHLEFunc(exportFunc.moduleName, exportFunc.nid); + ImGui::TextUnformatted(exportFunc.moduleName); + if (func) { + ImGui::SameLine(); + ImGui::TextUnformatted(func->name); + } + ImGui::SameLine(); ImClickableValue("addr", exportFunc.symAddr, control, ImCmd::SHOW_IN_CPU_DISASM); + } + } + } else { + ImGui::TextUnformatted("(no symbols exported)"); + } + } + } else { + ImGui::TextUnformatted("(no module selected)"); + } + ImGui::EndChild(); } - //if (cfg.selectedModule >= 0 && cfg.selectedModule < (int)modules.size()) { - // TODO: Show details - //} ImGui::End(); } diff --git a/ext/imgui/imgui_extras.h b/ext/imgui/imgui_extras.h new file mode 100644 index 0000000000..9d6a8f41db --- /dev/null +++ b/ext/imgui/imgui_extras.h @@ -0,0 +1,12 @@ +// Just some string_view and related wrappers. + +#include +#include "ext/imgui/imgui.h" + +namespace ImGui { + +inline void TextUnformatted(std::string_view str) { + TextUnformatted(str.data(), str.data() + str.size()); +} + +} // namespace ImGui From 5e18576f59a2f33134f94f9a4d211fd1468f5f0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 1 Apr 2025 12:15:46 +0200 Subject: [PATCH 5/6] Separate imports and exports for listing. --- Core/HLE/sceKernelModule.cpp | 12 +++++----- Core/HLE/sceKernelModule.h | 28 +++++++++++++++-------- Core/System.cpp | 6 ++--- Core/System.h | 5 +++-- UI/ImDebugger/ImDebugger.cpp | 43 ++++++++++++++++++------------------ 5 files changed, 53 insertions(+), 41 deletions(-) diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index f3a21aadc2..acac012249 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -295,14 +295,14 @@ void PSPModule::ImportFunc(const FuncSymbolImport &func, bool reimporting) { // Keep track and actually hook it up if possible. importedFuncs.push_back(func); - impExpModuleNames.insert(func.moduleName); + impModuleNames.insert(func.moduleName); ImportFuncSymbol(func, reimporting, GetName()); } void PSPModule::ImportVar(WriteVarSymbolState &state, const VarSymbolImport &var) { // Keep track and actually hook it up if possible. importedVars.push_back(var); - impExpModuleNames.insert(var.moduleName); + impModuleNames.insert(var.moduleName); ImportVarSymbol(state, var); } @@ -311,7 +311,7 @@ void PSPModule::ExportFunc(const FuncSymbolExport &func) { return; } exportedFuncs.push_back(func); - impExpModuleNames.insert(func.moduleName); + expModuleNames.insert(func.moduleName); ExportFuncSymbol(func); } @@ -320,7 +320,7 @@ void PSPModule::ExportVar(const VarSymbolExport &var) { return; } exportedVars.push_back(var); - impExpModuleNames.insert(var.moduleName); + expModuleNames.insert(var.moduleName); ExportVarSymbol(var); } @@ -1150,11 +1150,11 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load // Opportunity to dump the decrypted elf, even if we choose to fake it. // NOTE: filename is not necessarily a good choice! std::string elfFilename(KeepAfterLast(filename, '/')); - if (elfFilename.empty()) { + if (elfFilename.empty() || startsWith(elfFilename, "sce_lbn")) { // Use the name from the header. elfFilename = head->modname; } - DumpFileIfEnabled(ptr, elfSize, elfFilename.c_str(), DumpFileType::PRX); + DumpFileIfEnabled(ptr, (u32)elfSize, elfFilename.c_str(), DumpFileType::PRX); // This should happen for all "kernel" modules. *error_string = "Missing key"; diff --git a/Core/HLE/sceKernelModule.h b/Core/HLE/sceKernelModule.h index c663ab11eb..61ead2a4da 100644 --- a/Core/HLE/sceKernelModule.h +++ b/Core/HLE/sceKernelModule.h @@ -161,22 +161,31 @@ public: void ExportVar(const VarSymbolExport &var); template - void RebuildImpExpList(const std::vector &list) { + void RebuildExpList(const std::vector &list) { for (size_t i = 0; i < list.size(); ++i) { - impExpModuleNames.insert(list[i].moduleName); + expModuleNames.insert(list[i].moduleName); + } + } + + template + void RebuildImpList(const std::vector &list) { + for (size_t i = 0; i < list.size(); ++i) { + impModuleNames.insert(list[i].moduleName); } } void RebuildImpExpModuleNames() { - impExpModuleNames.clear(); - RebuildImpExpList(exportedFuncs); - RebuildImpExpList(importedFuncs); - RebuildImpExpList(exportedVars); - RebuildImpExpList(importedVars); + impModuleNames.clear(); + expModuleNames.clear(); + RebuildExpList(exportedFuncs); + RebuildImpList(importedFuncs); + RebuildExpList(exportedVars); + RebuildImpList(importedVars); } bool ImportsOrExportsModuleName(const std::string &moduleName) { - return impExpModuleNames.find(moduleName) != impExpModuleNames.end(); + return impModuleNames.find(moduleName) != impModuleNames.end() || + expModuleNames.find(moduleName) != expModuleNames.end(); } NativeModule nm{}; @@ -187,7 +196,8 @@ public: std::vector importedFuncs; std::vector exportedVars; std::vector importedVars; - std::set impExpModuleNames; + std::set impModuleNames; + std::set expModuleNames; // Keep track of the code region so we can throw out analysis results // when unloaded. diff --git a/Core/System.cpp b/Core/System.cpp index 5fbe8b9632..ba7d498aee 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -119,7 +119,7 @@ GlobalUIState GetUIState() { return globalUIState; } -void SetGPUBackend(GPUBackend type, const std::string &device) { +void SetGPUBackend(GPUBackend type, std::string_view device) { gpuBackend = type; gpuBackendDevice = device; } @@ -869,7 +869,7 @@ const char *DumpFileTypeToFileExtension(DumpFileType type) { } } -void DumpFileIfEnabled(const u8 *dataPtr, const u32 length, const char *name, DumpFileType type) { +void DumpFileIfEnabled(const u8 *dataPtr, const u32 length, std::string_view name, DumpFileType type) { if (!(g_Config.iDumpFileTypes & (int)type)) { return; } @@ -883,7 +883,7 @@ void DumpFileIfEnabled(const u8 *dataPtr, const u32 length, const char *name, Du } const char *extension = DumpFileTypeToFileExtension(type); - std::string filenameToDumpTo = StringFromFormat("%s_%s", g_paramSFO.GetDiscID().c_str(), name); + std::string filenameToDumpTo = g_paramSFO.GetDiscID() + "_" + std::string(name); if (!endsWithNoCase(filenameToDumpTo, extension)) { filenameToDumpTo += extension; } diff --git a/Core/System.h b/Core/System.h index f867c746aa..9f7bb84bb5 100644 --- a/Core/System.h +++ b/Core/System.h @@ -17,6 +17,7 @@ #pragma once +#include #include "Common/CommonTypes.h" #include "Common/File/Path.h" #include "Core/CoreParameter.h" @@ -68,7 +69,7 @@ void ResetUIState(); void UpdateUIState(GlobalUIState newState); GlobalUIState GetUIState(); -void SetGPUBackend(GPUBackend type, const std::string &device = ""); +void SetGPUBackend(GPUBackend type, std::string_view device = ""); GPUBackend GetGPUBackend(); std::string GetGPUBackendDevice(); @@ -128,4 +129,4 @@ inline CoreParameter &PSP_CoreParameter() { } // Centralized place for dumping useful files, also takes care of checking for dupes and creating a clickable UI popup. -void DumpFileIfEnabled(const u8 *dataPtr, const u32 length, const char *name, DumpFileType type); +void DumpFileIfEnabled(const u8 *dataPtr, const u32 length, std::string_view name, DumpFileType type); diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index 08c641d2fc..851ecba3bc 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -1489,37 +1489,40 @@ static void DrawModules(const MIPSDebugInterface *debug, ImConfig &cfg, ImContro if (mod->isFake) { ImGui::PopStyleColor(); } - if (ImGui::CollapsingHeader("Import/export modules")) { - for (auto &name : mod->impExpModuleNames) { + if (!mod->impModuleNames.empty() && ImGui::CollapsingHeader("Imported modules")) { + for (auto &name : mod->impModuleNames) { ImGui::TextUnformatted(name); } } - if (ImGui::CollapsingHeader("Imports")) { - if (!mod->importedVars.empty()) { - if (ImGui::CollapsingHeader("Vars")) { + if (!mod->expModuleNames.empty() && ImGui::CollapsingHeader("Exported modules")) { + for (auto &name : mod->expModuleNames) { + ImGui::TextUnformatted(name); + } + } + if (!mod->importedFuncs.empty() || !mod->importedVars.empty()) { + if (ImGui::CollapsingHeader("Imports")) { + if (!mod->importedVars.empty() && ImGui::CollapsingHeader("Vars")) { for (auto &var : mod->importedVars) { ImGui::TextUnformatted("(some var)"); // TODO } } - } - for (auto &import : mod->importedFuncs) { - // Look the name up in our HLE database. - const HLEFunction *func = GetHLEFunc(import.moduleName, import.nid); - ImGui::TextUnformatted(import.moduleName); - if (func) { - ImGui::SameLine(); - ImGui::TextUnformatted(func->name); + for (auto &import : mod->importedFuncs) { + // Look the name up in our HLE database. + const HLEFunction *func = GetHLEFunc(import.moduleName, import.nid); + ImGui::TextUnformatted(import.moduleName); + if (func) { + ImGui::SameLine(); + ImGui::TextUnformatted(func->name); + } + ImGui::SameLine(); ImClickableValue("addr", import.stubAddr, control, ImCmd::SHOW_IN_CPU_DISASM); } - ImGui::SameLine(); ImClickableValue("addr", import.stubAddr, control, ImCmd::SHOW_IN_CPU_DISASM); } } if (!mod->exportedFuncs.empty() || !mod->exportedVars.empty()) { if (ImGui::CollapsingHeader("Exports")) { - if (!mod->exportedVars.empty()) { - if (ImGui::CollapsingHeader("Vars")) { - for (auto &var : mod->importedVars) { - ImGui::TextUnformatted("(some var)"); // TODO - } + if (!mod->exportedVars.empty() && ImGui::CollapsingHeader("Vars")) { + for (auto &var : mod->importedVars) { + ImGui::TextUnformatted("(some var)"); // TODO } } for (auto &exportFunc : mod->exportedFuncs) { @@ -1533,8 +1536,6 @@ static void DrawModules(const MIPSDebugInterface *debug, ImConfig &cfg, ImContro ImGui::SameLine(); ImClickableValue("addr", exportFunc.symAddr, control, ImCmd::SHOW_IN_CPU_DISASM); } } - } else { - ImGui::TextUnformatted("(no symbols exported)"); } } } else { From 343ca2600a194c7c243b13b763e3a68685518f07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 1 Apr 2025 20:52:12 +0200 Subject: [PATCH 6/6] Add a developer setting to disable individual HLE modules, allowing them to be loaded properly. Some games survive with a loaded sceAtrac, and start talking to sceAudioCodec instead, the underlying library, though unsuccessfully since it's not properly implemented yet. --- Core/Config.cpp | 1 + Core/Config.h | 1 + Core/ConfigValues.h | 15 +++ Core/Core.vcxproj | 1 - Core/HLE/HLE.cpp | 116 +++++++++++++++++++++-- Core/HLE/HLE.h | 17 +++- Core/HLE/sceKernelModule.cpp | 174 ++++++++++++----------------------- Core/HLE/sceKernelThread.cpp | 4 +- UI/GameSettingsScreen.cpp | 9 ++ 9 files changed, 210 insertions(+), 128 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 01243bc523..45760dd5e2 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -238,6 +238,7 @@ static const ConfigSetting generalSettings[] = { ConfigSetting("GameListScrollPosition", &g_Config.fGameListScrollPosition, 0.0f, CfgFlag::DEFAULT), ConfigSetting("DebugOverlay", &g_Config.iDebugOverlay, 0, CfgFlag::DONT_SAVE), ConfigSetting("DefaultTab", &g_Config.iDefaultTab, 0, CfgFlag::DEFAULT), + ConfigSetting("DisableHLEFlags", &g_Config.iDisableHLE, 0, CfgFlag::PER_GAME), ConfigSetting("ScreenshotMode", &g_Config.iScreenshotMode, 0, CfgFlag::DEFAULT), ConfigSetting("ScreenshotsAsPNG", &g_Config.bScreenshotsAsPNG, false, CfgFlag::PER_GAME), diff --git a/Core/Config.h b/Core/Config.h index 451eaec99b..ca2d958678 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -139,6 +139,7 @@ public: bool bLoadPlugins; int iAskForExitConfirmationAfterSeconds; int iUIScaleFactor; // In 8ths of powers of two. + int iDisableHLE; int iScreenRotation; // The rotation angle of the PPSSPP UI. Only supported on Android and possibly other mobile platforms. int iInternalScreenRotation; // The internal screen rotation angle. Useful for vertical SHMUPs and similar. diff --git a/Core/ConfigValues.h b/Core/ConfigValues.h index 73691206bb..85096ab877 100644 --- a/Core/ConfigValues.h +++ b/Core/ConfigValues.h @@ -116,6 +116,21 @@ enum class RestoreSettingsBits : int { }; ENUM_CLASS_BITOPS(RestoreSettingsBits); +// Modules that are candidates for disabling HLE of. +enum class DisableHLEFlags : int { + sceFont = (1 << 0), + sceAtrac = (1 << 1), + scePsmf = (1 << 2), + scePsmfPlayer = (1 << 3), + sceMpeg = (1 << 4), + sceMp3 = (1 << 5), + sceJpeg = (1 << 6), + sceParseHttp = (1 << 7), + Count = 8, + // TODO: Some of the networking libraries may be interesting candidates, like HTTP. +}; +ENUM_CLASS_BITOPS(DisableHLEFlags); + std::string GPUBackendToString(GPUBackend backend); GPUBackend GPUBackendFromString(std::string_view backend); diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 0ab243b90d..9fb5aab8aa 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -680,7 +680,6 @@ - MaxSpeed MaxSpeed MaxSpeed Default diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp index 71845918da..d63b7cc94d 100644 --- a/Core/HLE/HLE.cpp +++ b/Core/HLE/HLE.cpp @@ -113,6 +113,111 @@ static std::vector enqueuedMipsCalls; // Does need to be saved, referenced by the stack and owned. static std::vector mipsCallActions; +// Modules that games try to load from disk, that we should not load normally. Instead we just HLE hook them. +// TODO: Merge with moduleDB? +static const HLEModuleMeta g_moduleMeta[] = { + {"sceATRAC3plus_Library", "sceAtrac3plus", DisableHLEFlags::sceAtrac}, + {"sceFont_Library", "sceLibFont", DisableHLEFlags::sceFont}, + {"SceFont_Library", "sceLibFont", DisableHLEFlags::sceFont}, + {"SceFont_Library", "sceLibFttt", DisableHLEFlags::sceFont}, + {"SceHttp_Library", "sceHttp"}, + {"sceMpeg_library", "sceMpeg", DisableHLEFlags::sceMpeg}, + {"sceNetAdhocctl_Library"}, + {"sceNetAdhocDownload_Library"}, + {"sceNetAdhocMatching_Library"}, + {"sceNetApDialogDummy_Library"}, + {"sceNetAdhoc_Library"}, + {"sceNetApctl_Library"}, + {"sceNetInet_Library"}, + {"sceNetResolver_Library"}, + {"sceNet_Library"}, + {"sceNetAdhoc_Library"}, + {"sceNetAdhocAuth_Service"}, + {"sceNetAdhocctl_Library"}, + {"sceNetIfhandle_Service"}, + {"sceSsl_Module"}, + {"sceDEFLATE_Library"}, + {"sceMD5_Library"}, + {"sceMemab"}, + {"sceAvcodec_driver"}, + {"sceAudiocodec_Driver"}, + {"sceAudiocodec"}, + {"sceVideocodec_Driver"}, + {"sceVideocodec"}, + {"sceMpegbase_Driver"}, + {"sceMpegbase"}, + {"scePsmf_library", "scePsmf", DisableHLEFlags::scePsmf}, + {"scePsmfP_library", "scePsmfPlayer", DisableHLEFlags::scePsmfPlayer}, + {"scePsmfPlayer", "scePsmfPlayer", DisableHLEFlags::scePsmfPlayer}, + {"sceSAScore", "sceSasCore"}, + {"sceCcc_Library", "sceCcc"}, + {"SceParseHTTPheader_Library", "sceParseHttp", DisableHLEFlags::sceParseHttp}, + {"SceParseURI_Library"}, + // Guessing these names + {"sceJpeg", "sceJpeg", DisableHLEFlags::sceJpeg}, + {"sceJpeg_library", "sceJpeg", DisableHLEFlags::sceJpeg}, + {"sceJpeg_Library", "sceJpeg", DisableHLEFlags::sceJpeg}, +}; + +const HLEModuleMeta *GetHLEModuleMeta(std::string_view modname) { + for (size_t i = 0; i < ARRAY_SIZE(g_moduleMeta); i++) { + if (equalsNoCase(modname, g_moduleMeta[i].modname)) { + return &g_moduleMeta[i]; + } + } + return nullptr; +} + +const HLEModuleMeta *GetHLEModuleMetaByFlag(DisableHLEFlags flag) { + for (size_t i = 0; i < ARRAY_SIZE(g_moduleMeta); i++) { + if (g_moduleMeta[i].disableFlag == flag) { + return &g_moduleMeta[i]; + } + } + return nullptr; +} + +const HLEModuleMeta *GetHLEModuleMetaByImport(std::string_view importModuleName) { + for (size_t i = 0; i < ARRAY_SIZE(g_moduleMeta); i++) { + if (g_moduleMeta[i].importName && equalsNoCase(importModuleName, g_moduleMeta[i].importName)) { + return &g_moduleMeta[i]; + } + } + return nullptr; +} + +// Note: name is the modname from prx, not the export module name! +bool ShouldHLEModule(std::string_view modname, bool *wasDisabled) { + if (wasDisabled) { + *wasDisabled = false; + } + const HLEModuleMeta *meta = GetHLEModuleMeta(modname); + if (!meta) { + return false; + } + + bool disabled = meta->disableFlag & (DisableHLEFlags)g_Config.iDisableHLE; + if (disabled) { + if (wasDisabled) { + *wasDisabled = true; + } + return false; + } + return true; +} + +bool ShouldHLEModuleByImportName(std::string_view name) { + // Check our special metadata lookup. Should probably be merged with the main one. + const HLEModuleMeta *meta = GetHLEModuleMetaByImport(name); + if (meta) { + bool disabled = meta->disableFlag & (DisableHLEFlags)g_Config.iDisableHLE; + return !disabled; + } + + // Otherwise, just fall back to the regular db. If it's in there, we should HLE it. + return GetHLEModuleByName(name) != nullptr; +} + static void hleDelayResultFinish(u64 userdata, int cycleslate) { u32 error; SceUID threadID = (SceUID) userdata; @@ -278,7 +383,7 @@ const char *GetHLEFuncName(int moduleIndex, int func) { u32 GetSyscallOp(std::string_view moduleName, u32 nib) { // Special case to hook up bad imports. if (moduleName.empty()) { - return (0x03FFFFCC); // invalid syscall + return 0x03FFFFCC; // invalid syscall } int modindex = GetHLEModuleIndex(moduleName); @@ -292,15 +397,10 @@ u32 GetSyscallOp(std::string_view moduleName, u32 nib) { } } else { ERROR_LOG(Log::HLE, "Unknown module %.*s!", (int)moduleName.size(), moduleName.data()); - return 0x03FFFFCC; // invalid syscall + return 0x03FFFFCC; // invalid syscall (invalid mod index and func index..) } } -bool FuncImportIsSyscall(std::string_view module, u32 nib) -{ - return GetHLEFunc(module, nib) != nullptr; -} - void WriteFuncStub(u32 stubAddr, u32 symAddr) { // Note that this should be J not JAL, as otherwise control will return to the stub.. @@ -317,7 +417,7 @@ void WriteFuncMissingStub(u32 stubAddr, u32 nid) Memory::Write_U32(GetSyscallOp("", nid), stubAddr + 4); } -bool WriteSyscall(std::string_view moduleName, u32 nib, u32 address) +bool WriteHLESyscall(std::string_view moduleName, u32 nib, u32 address) { if (nib == 0) { diff --git a/Core/HLE/HLE.h b/Core/HLE/HLE.h index 1b2cfd774d..60e892b479 100644 --- a/Core/HLE/HLE.h +++ b/Core/HLE/HLE.h @@ -25,6 +25,7 @@ #include "Common/CommonTypes.h" #include "Common/Log.h" #include "Core/MIPS/MIPS.h" +#include "Core/ConfigValues.h" #ifdef _MSC_VER #pragma warning (error: 4834) // discarding return value of function with 'nodiscard' attribute @@ -97,8 +98,21 @@ struct Syscall { #define RETURN64(n) {u64 RETURN64_tmp = n; currentMIPS->r[MIPS_REG_V0] = RETURN64_tmp & 0xFFFFFFFF; currentMIPS->r[MIPS_REG_V1] = RETURN64_tmp >> 32;} #define RETURNF(fl) currentMIPS->f[0] = fl +struct HLEModuleMeta { + // This is the modname (name from the PRX header). Probably, we should really blacklist on the module names of the exported symbol metadata. + const char *modname; + const char *importName; // Technically a module can export functions with different module names, but doesn't seem to happen. + DisableHLEFlags disableFlag; +}; + +const HLEModuleMeta *GetHLEModuleMetaByFlag(DisableHLEFlags flag); +const HLEModuleMeta *GetHLEModuleMeta(std::string_view modname); +bool ShouldHLEModule(std::string_view modname, bool *wasDisabled = nullptr); +bool ShouldHLEModuleByImportName(std::string_view importModuleName); + const char *GetHLEFuncName(std::string_view module, u32 nib); const char *GetHLEFuncName(int module, int func); +const HLEModule *GetHLEModuleByName(std::string_view name); const HLEFunction *GetHLEFunc(std::string_view module, u32 nib); int GetHLEFuncIndexByNib(int moduleIndex, u32 nib); int GetHLEModuleIndex(std::string_view modulename); @@ -160,8 +174,7 @@ void HLEInit(); void HLEDoState(PointerWrap &p); void HLEShutdown(); u32 GetSyscallOp(std::string_view module, u32 nib); -bool FuncImportIsSyscall(std::string_view module, u32 nib); -bool WriteSyscall(std::string_view module, u32 nib, u32 address); +bool WriteHLESyscall(std::string_view module, u32 nib, u32 address); void CallSyscall(MIPSOpcode op); void WriteFuncStub(u32 stubAddr, u32 symAddr); void WriteFuncMissingStub(u32 stubAddr, u32 nid); diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index acac012249..ecb81fa31c 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -52,12 +52,11 @@ #include "Core/FileSystems/MetaFileSystem.h" #include "Core/Util/BlockAllocator.h" #include "Core/CoreTiming.h" +#include "Core/ConfigValues.h" #include "Core/PSPLoaders.h" #include "Core/System.h" #include "Core/MemMapHelpers.h" #include "Core/Debugger/SymbolMap.h" -#include "Core/MIPS/MIPS.h" - #include "Core/HLE/sceKernel.h" #include "Core/HLE/sceKernelModule.h" #include "Core/HLE/sceKernelThread.h" @@ -103,32 +102,6 @@ static const char * const lieAboutSuccessModules[] = { "flash0:/kd/pspnet_resolver.prx", }; -// Modules to not load. TODO: Look into loosening this a little (say sceFont). -static const char * const blacklistedModules[] = { - "sceATRAC3plus_Library", - "sceFont_Library", - "SceFont_Library", - "SceHttp_Library", - "sceMpeg_library", - "sceNetAdhocctl_Library", - "sceNetAdhocDownload_Library", - "sceNetAdhocMatching_Library", - "sceNetApDialogDummy_Library", - "sceNetAdhoc_Library", - "sceNetApctl_Library", - "sceNetInet_Library", - "sceNetResolver_Library", - "sceNet_Library", - "sceNetAdhoc_Library", - "sceNetAdhocAuth_Service", - "sceNetAdhocctl_Library", - "sceNetIfhandle_Service", - "sceSsl_Module", - "sceDEFLATE_Library", - "sceMD5_Library", - "sceMemab", -}; - const char *NativeModuleStatusToString(NativeModuleStatus status) { switch (status) { case MODULE_STATUS_STARTING: return "STARTING"; @@ -167,6 +140,34 @@ struct ModuleInfo { char name[28]; }; +struct PspLibStubEntry { + u32_le name; + u16_le version; + u16_le flags; + u8 size; + u8 numVars; + u16_le numFuncs; + // each symbol has an associated nid; nidData is a pointer + // (in .rodata.sceNid section) to an array of longs, one + // for each function, which identifies the function whose + // address is to be inserted. + // + // The hash is the first 4 bytes of a SHA-1 hash of the function + // name. (Represented as a little-endian long, so the order + // of the bytes is reversed.) + u32_le nidData; + // the address of the function stubs where the function address jumps + // should be filled in + u32_le firstSymAddr; + // Optional, this is where var relocations are. + // They use the format: u32 addr, u32 nid, ... + // WARNING: May have garbage if size < 6. + u32_le varData; + // Not sure what this is yet, assume garbage for now. + // TODO: Tales of the World: Radiant Mythology 2 has something here? + u32_le extra; +}; + PSPModule::~PSPModule() { if (memoryBlockAddr) { // If it's either below user memory, or using a high kernel bit, it's in kernel. @@ -639,14 +640,26 @@ void UnexportVarSymbol(const VarSymbolExport &var) { } } +static bool FuncImportIsHLE(std::string_view module, u32 nid) { + // TODO: Take into account whether HLE is enabled for the module. + // Also, this needs to be more efficient. + if (!ShouldHLEModuleByImportName(module)) { + return false; + } + + return GetHLEFunc(module, nid) != nullptr; +} + void ImportFuncSymbol(const FuncSymbolImport &func, bool reimporting, const char *importingModule) { - // Prioritize HLE implementations. - // TODO: Or not? - if (FuncImportIsSyscall(func.moduleName, func.nid)) { + bool shouldHLE = ShouldHLEModuleByImportName(func.moduleName); + + // Prioritize HLE implementations, if we should HLE this. + if (shouldHLE && GetHLEFunc(func.moduleName, func.nid)) { if (reimporting && Memory::Read_Instruction(func.stubAddr + 4) != GetSyscallOp(func.moduleName, func.nid)) { WARN_LOG(Log::Loader, "Reimporting updated syscall %s", GetHLEFuncName(func.moduleName, func.nid)); } - WriteSyscall(func.moduleName, func.nid, func.stubAddr); + // TODO: There's some double lookup going on here (we already did the lookup in GetHLEFunc above). + WriteHLESyscall(func.moduleName, func.nid, func.stubAddr); currentMIPS->InvalidateICache(func.stubAddr, 8); if (g_Config.bPreloadFunctions) { MIPSAnalyst::PrecompileFunction(func.stubAddr, 8); @@ -678,21 +691,21 @@ void ImportFuncSymbol(const FuncSymbolImport &func, bool reimporting, const char } // It hasn't been exported yet, but hopefully it will later. Check if we know about it through HLE. - const bool isKnownHLEModule = GetHLEModuleIndex(func.moduleName) != -1; - if (isKnownHLEModule) { + if (shouldHLE) { // We used to report this, but I don't think it's very interesting anymore. WARN_LOG(Log::Loader, "Unknown syscall from known HLE module '%s': 0x%08x (import for '%s')", func.moduleName, func.nid, importingModule); } else { INFO_LOG(Log::Loader, "Function (%s,%08x) unresolved in '%s', storing for later resolving", func.moduleName, func.nid, importingModule); } - if (isKnownHLEModule || !reimporting) { + + if (shouldHLE || !reimporting) { WriteFuncMissingStub(func.stubAddr, func.nid); currentMIPS->InvalidateICache(func.stubAddr, 8); } } void ExportFuncSymbol(const FuncSymbolExport &func) { - if (FuncImportIsSyscall(func.moduleName, func.nid)) { + if (FuncImportIsHLE(func.moduleName, func.nid)) { // HLE covers this already - let's ignore the function. WARN_LOG(Log::Loader, "Ignoring func export %s/%08x, already implemented in HLE.", func.moduleName, func.nid); return; @@ -720,7 +733,7 @@ void ExportFuncSymbol(const FuncSymbolExport &func) { } void UnexportFuncSymbol(const FuncSymbolExport &func) { - if (FuncImportIsSyscall(func.moduleName, func.nid)) { + if (FuncImportIsHLE(func.moduleName, func.nid)) { // Oops, HLE covers this. return; } @@ -769,72 +782,7 @@ void PSPModule::Cleanup() { } } -static bool IsHLEVersionedModule(const char *name) { - // TODO: Only some of these are currently known to be versioned. - // Potentially only sceMpeg_library matters. - // For now, we're just reporting version numbers. - for (size_t i = 0; i < ARRAY_SIZE(blacklistedModules); i++) { - if (!strncmp(name, blacklistedModules[i], 28)) { - return true; - } - } - static const char *otherModules[] = { - "sceAvcodec_driver", - "sceAudiocodec_Driver", - "sceAudiocodec", - "sceVideocodec_Driver", - "sceVideocodec", - "sceMpegbase_Driver", - "sceMpegbase", - "scePsmf_library", - "scePsmfP_library", - "scePsmfPlayer", - "sceSAScore", - "sceCcc_Library", - "SceParseHTTPheader_Library", - "SceParseURI_Library", - // Guessing. - "sceJpeg", - "sceJpeg_library", - "sceJpeg_Library", - }; - for (size_t i = 0; i < ARRAY_SIZE(otherModules); i++) { - if (!strncmp(name, otherModules[i], 28)) { - return true; - } - } - return false; -} - static bool KernelImportModuleFuncs(PSPModule *module, u32 *firstImportStubAddr, bool reimporting) { - struct PspLibStubEntry { - u32_le name; - u16_le version; - u16_le flags; - u8 size; - u8 numVars; - u16_le numFuncs; - // each symbol has an associated nid; nidData is a pointer - // (in .rodata.sceNid section) to an array of longs, one - // for each function, which identifies the function whose - // address is to be inserted. - // - // The hash is the first 4 bytes of a SHA-1 hash of the function - // name. (Represented as a little-endian long, so the order - // of the bytes is reversed.) - u32_le nidData; - // the address of the function stubs where the function address jumps - // should be filled in - u32_le firstSymAddr; - // Optional, this is where var relocations are. - // They use the format: u32 addr, u32 nid, ... - // WARNING: May have garbage if size < 6. - u32_le varData; - // Not sure what this is yet, assume garbage for now. - // TODO: Tales of the World: Radiant Mythology 2 has something here? - u32_le extra; - }; - // Can't run - we didn't keep track of the libstub entry. if (module->libstub == 0) { return false; @@ -1099,7 +1047,8 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load head = (const PSP_Header *)ptr; devkitVersion = head->devkitversion; - if (IsHLEVersionedModule(head->modname)) { + bool wasDisabled; + if (ShouldHLEModule(head->modname, &wasDisabled)) { int ver = (head->module_ver_hi << 8) | head->module_ver_lo; INFO_LOG(Log::sceModule, "Loading module %s with version %04x, devkit %08x, crc %x", head->modname, ver, head->devkitversion, module->crc); @@ -1108,6 +1057,9 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load fakeLoadedModule = true; } + if (wasDisabled) { + g_OSD.Show(OSDType::MESSAGE_WARNING, StringFromFormat("HLE for %s has been manually disabled", head->modname)); + } const u8 *in = ptr; const auto isGzip = head->comp_attribute & 1; // Kind of odd. @@ -1310,16 +1262,7 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load char moduleName[29] = {0}; strncpy(moduleName, modinfo->name, ARRAY_SIZE(module->nm.name)); - // Check for module blacklist - we don't allow games to load these modules from disc - // as we have HLE implementations and the originals won't run in the emu because they - // directly access hardware or for other reasons. - for (u32 i = 0; i < ARRAY_SIZE(blacklistedModules); i++) { - if (strncmp(modinfo->name, blacklistedModules[i], ARRAY_SIZE(modinfo->name)) == 0) { - module->isFake = true; - } - } - - if (!module->isFake && module->memoryBlockAddr != 0) { + if (module->memoryBlockAddr != 0) { g_symbolMap->AddModule(moduleName, module->memoryBlockAddr, module->memoryBlockSize); } @@ -1415,6 +1358,7 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load } // Look at the exports, too. + // TODO: Add them to the symbol map! struct PspLibEntEntry { u32_le name; /* ent's name (module name) address */ @@ -1500,7 +1444,7 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load func.nid = nid; func.symAddr = exportAddr; if (ent->name == 0) { - WARN_LOG_REPORT(Log::HLE, "Exporting func from syslib export: %08x", nid); + WARN_LOG(Log::HLE, "Exporting func from syslib export: %08x", nid); } module->ExportFunc(func); } @@ -1587,7 +1531,7 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load delete [] newptr; - if (!reportedModule && IsHLEVersionedModule(modinfo->name)) { + if (!reportedModule && ShouldHLEModule(modinfo->name)) { INFO_LOG(Log::sceModule, "Loading module %s with version %04x, devkit %08x", modinfo->name, modinfo->moduleVersion, devkitVersion); if (!strcmp(modinfo->name, "sceMpeg_library")) { diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 1c236af5b5..4ad49ab66a 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -746,7 +746,7 @@ static void __KernelWriteFakeSysCall(u32 nid, u32 *ptr, u32 &pos) { *ptr = pos; pos += 8; - WriteSyscall("FakeSysCalls", nid, *ptr); + WriteHLESyscall("FakeSysCalls", nid, *ptr); MIPSAnalyst::PrecompileFunction(*ptr, 8); } @@ -1931,7 +1931,7 @@ int __KernelStartThread(SceUID threadToStartID, int argSize, u32 argBlockPtr, bo // At the bottom of those 64 bytes, the return syscall and ra is written. // Test Drive Unlimited actually depends on it being in the correct place. - WriteSyscall("FakeSysCalls", NID_THREADRETURN, sp); + WriteHLESyscall("FakeSysCalls", NID_THREADRETURN, sp); Memory::Write_U32(MIPS_MAKE_B(-1), sp + 8); Memory::Write_U32(MIPS_MAKE_NOP(), sp + 12); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index b652116246..eca4bce8c8 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -1995,6 +1995,15 @@ void DeveloperToolsScreen::CreateViews() { list->Add(new BitCheckBox(&g_Config.iDumpFileTypes, (int)DumpFileType::PRX, dev->T("PRX"))); list->Add(new BitCheckBox(&g_Config.iDumpFileTypes, (int)DumpFileType::Atrac3, dev->T("Atrac3/3+"))); + list->Add(new ItemHeader("Disable HLE (experimental! Not expected to work yet)")); + for (int i = 0; i < (int)DisableHLEFlags::Count; i++) { + DisableHLEFlags flag = (DisableHLEFlags)(1 << i); + const HLEModuleMeta *meta = GetHLEModuleMetaByFlag(flag); + if (meta) { + list->Add(new BitCheckBox(&g_Config.iDisableHLE, (int)flag, meta->modname)); + } + } + #if !PPSSPP_PLATFORM(ANDROID) && !PPSSPP_PLATFORM(IOS) && !PPSSPP_PLATFORM(SWITCH) list->Add(new ItemHeader(dev->T("MIPSTracer")));