mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Merge pull request #20196 from hrydgard/module-cleanup-2
Module loading improvements
This commit is contained in:
commit
dba18e8710
19 changed files with 414 additions and 179 deletions
|
@ -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" />
|
||||
|
|
|
@ -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>
|
|
@ -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),
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
116
Core/HLE/HLE.cpp
116
Core/HLE/HLE.cpp
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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")));
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
12
ext/imgui/imgui_extras.h
Normal 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
|
Loading…
Add table
Reference in a new issue