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