Merge pull request #20196 from hrydgard/module-cleanup-2

Module loading improvements
This commit is contained in:
Henrik Rydgård 2025-04-02 10:55:52 +02:00 committed by GitHub
commit dba18e8710
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 414 additions and 179 deletions

View file

@ -405,6 +405,7 @@
<ClInclude Include="..\ext\basis_universal\basisu_transcoder_uastc.h" />
<ClInclude Include="..\ext\imgui\imconfig.h" />
<ClInclude Include="..\ext\imgui\imgui.h" />
<ClInclude Include="..\ext\imgui\imgui_extras.h" />
<ClInclude Include="..\ext\imgui\imgui_impl_platform.h" />
<ClInclude Include="..\ext\imgui\imgui_impl_thin3d.h" />
<ClInclude Include="..\ext\imgui\imgui_internal.h" />

View file

@ -691,6 +691,9 @@
<ClInclude Include="..\ext\sol\sol.hpp">
<Filter>ext\sol</Filter>
</ClInclude>
<ClInclude Include="..\ext\imgui\imgui_extras.h">
<Filter>ext\imgui</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="ABI.cpp" />
@ -1481,4 +1484,4 @@
<Filter>ext\lua</Filter>
</None>
</ItemGroup>
</Project>
</Project>

View file

@ -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),

View file

@ -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.

View file

@ -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);

View file

@ -680,7 +680,6 @@
<ClCompile Include="Font\PGF.cpp" />
<ClCompile Include="HDRemaster.cpp" />
<ClCompile Include="HLE\HLE.cpp">
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">MaxSpeed</Optimization>
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">MaxSpeed</Optimization>
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">MaxSpeed</Optimization>
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Default</BasicRuntimeChecks>

View file

@ -113,6 +113,111 @@ static std::vector<HLEMipsCallInfo> enqueuedMipsCalls;
// Does need to be saved, referenced by the stack and owned.
static std::vector<PSPAction *> 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)
{

View file

@ -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);

View file

@ -172,6 +172,15 @@ public:
return occupied[index];
}
template<class T>
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 <class T>
T* Get(SceUID handle, u32 &outError) {
if (handle < handleOffset || handle >= handleOffset+maxCount || !occupied[handle-handleOffset]) {
@ -204,15 +213,15 @@ public:
return static_cast<T *>(pool[realHandle]);
}
template <class T, typename ArgT>
void Iterate(bool func(T *, ArgT), ArgT arg) {
template <typename T, typename F>
void Iterate(F func) {
int type = T::GetStaticIDType();
for (int i = 0; i < maxCount; i++) {
if (!occupied[i])
continue;
T *t = static_cast<T *>(pool[i]);
if (t->GetIDType() == type) {
if (!func(t, arg))
if (!func(i + handleOffset, t))
break;
}
}

View file

@ -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<TLSPL>(&FindTLSByIndex, &state);
kernelObjects.Iterate<TLSPL>([&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");

View file

@ -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.
@ -295,14 +296,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 +312,7 @@ void PSPModule::ExportFunc(const FuncSymbolExport &func) {
return;
}
exportedFuncs.push_back(func);
impExpModuleNames.insert(func.moduleName);
expModuleNames.insert(func.moduleName);
ExportFuncSymbol(func);
}
@ -320,7 +321,7 @@ void PSPModule::ExportVar(const VarSymbolExport &var) {
return;
}
exportedVars.push_back(var);
impExpModuleNames.insert(var.moduleName);
expModuleNames.insert(var.moduleName);
ExportVarSymbol(var);
}
@ -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.
@ -1150,11 +1102,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, head->psp_size, 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";
@ -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);
}
@ -1581,14 +1525,13 @@ 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;
}
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")) {
@ -2364,22 +2307,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<PSPModule>([&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 {

View file

@ -161,32 +161,43 @@ public:
void ExportVar(const VarSymbolExport &var);
template <typename T>
void RebuildImpExpList(const std::vector<T> &list) {
void RebuildExpList(const std::vector<T> &list) {
for (size_t i = 0; i < list.size(); ++i) {
impExpModuleNames.insert(list[i].moduleName);
expModuleNames.insert(list[i].moduleName);
}
}
template <typename T>
void RebuildImpList(const std::vector<T> &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{};
std::vector<ModuleWaitingThread> waitingThreads;
// TODO: Should we store these grouped by moduleName instead? Seems more reasonable.
std::vector<FuncSymbolExport> exportedFuncs;
std::vector<FuncSymbolImport> importedFuncs;
std::vector<VarSymbolExport> exportedVars;
std::vector<VarSymbolImport> importedVars;
std::set<std::string> impExpModuleNames;
std::set<std::string> impModuleNames;
std::set<std::string> expModuleNames;
// Keep track of the code region so we can throw out analysis results
// when unloaded.

View file

@ -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);

View file

@ -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;
}

View file

@ -17,6 +17,7 @@
#pragma once
#include <string_view>
#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);

View file

@ -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")));

View file

@ -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"
@ -25,6 +26,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"
@ -1419,8 +1421,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) {
@ -1445,7 +1447,114 @@ static void DrawModules(const MIPSDebugInterface *debug, ImConfig &cfg, ImContro
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::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();
// TODO: Add context menu and clickability
kernelObjects.Iterate<PSPModule>([&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;
});
ImGui::EndTable();
}
ImGui::EndChild();
}
ImGui::SameLine();
if (ImGui::BeginChild("info")) {
if (kernelObjects.Is<PSPModule>(cfg.selectedModuleId)) {
PSPModule *mod = kernelObjects.GetFast<PSPModule>(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 (!mod->impModuleNames.empty() && ImGui::CollapsingHeader("Imported modules")) {
for (auto &name : mod->impModuleNames) {
ImGui::TextUnformatted(name);
}
}
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);
}
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() && 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 module selected)");
}
ImGui::EndChild();
}
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<LoadedModuleInfo> modules = g_symbolMap->getAllModules();
if (ImGui::BeginTable("modules", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed);
@ -1461,8 +1570,8 @@ static void DrawModules(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);
@ -1476,7 +1585,7 @@ static void DrawModules(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();
@ -1673,6 +1782,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 +1948,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 +2418,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);

View file

@ -124,6 +124,7 @@ struct ImConfig {
bool threadsOpen;
bool callstackOpen;
bool breakpointsOpen;
bool symbolsOpen;
bool modulesOpen;
bool hleModulesOpen;
bool audioDecodersOpen;
@ -158,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;

12
ext/imgui/imgui_extras.h Normal file
View file

@ -0,0 +1,12 @@
// Just some string_view and related wrappers.
#include <string_view>
#include "ext/imgui/imgui.h"
namespace ImGui {
inline void TextUnformatted(std::string_view str) {
TextUnformatted(str.data(), str.data() + str.size());
}
} // namespace ImGui