mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-14 15:00:30 +02:00
Merge pull request #22432 from sum2012/patch4
Add some kernel NIDs and sceReg write overlay by Zcode
This commit is contained in:
7 files changed
+235
-17
No files matched your search
@@ -351,6 +351,7 @@ void RegisterAllModules() {
|
||||
|
||||
// add new modules here.
|
||||
Register_SystemCtrlForKernel();
|
||||
Register_sceGe_driver();
|
||||
|
||||
// Not ready to enable this due to apparent softlocks in Patapon 3.
|
||||
// Register_sceNpMatching2();
|
||||
|
||||
@@ -656,6 +656,9 @@ static const HLEFunction sceCtrl[] =
|
||||
// sceCtrl_driver's NID for sceCtrlPeekBufferPositive in 3.71. Imported by NJEMU's SystemButtons.prx
|
||||
// kernel plugin, which picks it when sceKernelDevkitVersion() is 3.71 or newer (#8925).
|
||||
{0XC4AAD55F, &WrapI_UU<sceCtrlPeekBufferPositive>, "sceCtrlPeekBufferPositive", 'i', "xx"},
|
||||
// sceCtrl_driver NID for sceCtrlReadBufferPositive (firmware 3.71+, per JPCSP).
|
||||
// Imported by translation-patch loaders like CLANNAD's chfont.prx.
|
||||
{0X454455AC, &WrapI_UU<sceCtrlReadBufferPositive>, "sceCtrlReadBufferPositive", 'i', "xx"},
|
||||
};
|
||||
|
||||
void Register_sceCtrl()
|
||||
|
||||
@@ -680,3 +680,9 @@ const HLEFunction sceGe_user[] = {
|
||||
void Register_sceGe_user() {
|
||||
RegisterHLEModule("sceGe_user", ARRAY_SIZE(sceGe_user), sceGe_user);
|
||||
}
|
||||
|
||||
void Register_sceGe_driver() {
|
||||
// Same function set as sceGe_user; imported by kernel modules like chfont.prx
|
||||
// using the driver NIDs (e.g. 0xE47E40E4 = sceGeEdramGetAddr).
|
||||
RegisterHLEModule("sceGe_driver", ARRAY_SIZE(sceGe_user), sceGe_user);
|
||||
}
|
||||
@@ -46,6 +46,7 @@ struct PspGeListArgs
|
||||
};
|
||||
|
||||
void Register_sceGe_user();
|
||||
void Register_sceGe_driver();
|
||||
|
||||
void __GeInit();
|
||||
void __GeDoState(PointerWrap &p);
|
||||
|
||||
@@ -976,6 +976,16 @@ const HLEFunction ThreadManForKernel[] =
|
||||
{0xC11BA8C4, &WrapI_II<sceKernelNotifyCallback>, "sceKernelNotifyCallback", 'i', "ii", HLE_KERNEL_SYSCALL },
|
||||
{0xF6427665, &WrapI_V<sceKernelGetUserLevel>, "sceKernelGetUserLevel", 'i', "", HLE_KERNEL_SYSCALL },
|
||||
{0x85A2A5BF, &WrapI_V<sceKernelIsUserModeThread>, "sceKernelIsUserModeThread", 'i', "", HLE_KERNEL_SYSCALL },
|
||||
// Refer*Status functions that ThreadManForUser also exports, imported from kernel context by
|
||||
// translation-patch loaders like CLANNAD's HookIO.prx. NIDs per JPCSP's ThreadManForUser.
|
||||
{0x17C1684E, &WrapU_UU<sceKernelReferThreadStatus>, "sceKernelReferThreadStatus", 'i', "xp", HLE_KERNEL_SYSCALL },
|
||||
{0x369EEB6B, &WrapI_IU<sceKernelReferThreadEventHandlerStatus>, "sceKernelReferThreadEventHandlerStatus", 'i', "ip", HLE_KERNEL_SYSCALL },
|
||||
{0x730ED8BC, &WrapI_IU<sceKernelReferCallbackStatus>, "sceKernelReferCallbackStatus", 'i', "ip", HLE_KERNEL_SYSCALL },
|
||||
{0xBC6FEBC5, &WrapI_IU<sceKernelReferSemaStatus>, "sceKernelReferSemaStatus", 'i', "ip", HLE_KERNEL_SYSCALL },
|
||||
{0xA66B0120, &WrapU_IU<sceKernelReferEventFlagStatus>, "sceKernelReferEventFlagStatus", 'x', "ix", HLE_KERNEL_SYSCALL },
|
||||
{0x33BE4024, &WrapI_IU<sceKernelReferMsgPipeStatus>, "sceKernelReferMsgPipeStatus", 'i', "ip", HLE_KERNEL_SYSCALL },
|
||||
{0xD8199E4C, &WrapI_IU<sceKernelReferFplStatus>, "sceKernelReferFplStatus", 'i', "ip", HLE_KERNEL_SYSCALL },
|
||||
{0x5F32BEAA, &WrapU_IU<sceKernelReferVTimerStatus>, "sceKernelReferVTimerStatus", 'x', "ix", HLE_KERNEL_SYSCALL },
|
||||
// NOT added on purpose, even though we implement all four for user mode already:
|
||||
// sceKernelCreateMutex (0xB7D098C6), sceKernelLockMutex (0xB011B11F), sceKernelUnlockMutex
|
||||
// (0x6B30100F) and sceKernelAllocateFpl (0xD979E9BF). They are what the real flash0 NAND and
|
||||
|
||||
@@ -2659,6 +2659,12 @@ static u32 sctrlHENSetStartModuleHandler(u32 handlerAddr) {
|
||||
return hleLogInfo(Log::sceModule, previous, "handler %08x", handlerAddr);
|
||||
}
|
||||
|
||||
// NID 0x78E46415, per JPCSP's SystemCtrlForKernel. Imported by CLANNAD's chfont.prx to look up
|
||||
// a driver by name; since no real driver patching is done, always report it as found.
|
||||
static u32 sctrlHENFindDriver(const char *drvname) {
|
||||
return hleLogInfo(Log::sceModule, 0, "driver %s", drvname ? drvname : "(null)");
|
||||
}
|
||||
|
||||
int __KernelStartModule(SceUID moduleId, u32 argsize, u32 argAddr, u32 returnValueAddr, SceKernelSMOption *smoption, bool *needsWait) {
|
||||
if (needsWait) {
|
||||
*needsWait = false;
|
||||
@@ -3393,6 +3399,7 @@ void Register_ModuleMgrForKernel() {
|
||||
|
||||
static const HLEFunction SystemCtrlForKernel[] = {
|
||||
{0x1C90BECB, &WrapU_U<sctrlHENSetStartModuleHandler>, "sctrlHENSetStartModuleHandler", 'x', "x", HLE_KERNEL_SYSCALL},
|
||||
{0x78E46415, &WrapU_C<sctrlHENFindDriver>, "sctrlHENFindDriver", 'x', "s", HLE_KERNEL_SYSCALL},
|
||||
};
|
||||
|
||||
void Register_SystemCtrlForKernel() {
|
||||
|
||||
+207
-17
@@ -1,4 +1,5 @@
|
||||
#include <map>
|
||||
#include <deque>
|
||||
#include <algorithm>
|
||||
#include "Core/HLE/sceReg.h"
|
||||
#include "Core/HLE/HLE.h"
|
||||
@@ -59,6 +60,31 @@ struct KeyValue {
|
||||
const KeyValue *dirContents; // intValue is the count.
|
||||
};
|
||||
|
||||
// Guest-written registry values (sceRegCreateKey / sceRegSetKeyValue), keyed by category path
|
||||
// (no leading slash, e.g. "DATA/FONT") then key name. The static dump above is read-only, but
|
||||
// plugins like CLANNAD's chfont.prx register their font by creating /DATA/FONT/PROPERTY/INFO18
|
||||
// and rewriting /DATA/FONT's path_name and num_fonts at game boot - the game's font library only
|
||||
// sees the font if these writes are visible to later reads.
|
||||
struct OverlayValue {
|
||||
ValueType type = ValueType::INT;
|
||||
std::string str; // STR/BIN payload
|
||||
int intValue = 0;
|
||||
bool isDir = false;
|
||||
};
|
||||
static std::map<std::string, std::map<std::string, OverlayValue>> g_overlay;
|
||||
|
||||
// Merged view of a category: static dump entries overridden by g_overlay entries. Cached per
|
||||
// path; invalidated whenever the overlay changes. strValue pointers point into strStorage.
|
||||
struct MergedCategory {
|
||||
std::vector<KeyValue> keyvals;
|
||||
std::deque<std::string> strStorage;
|
||||
};
|
||||
static std::map<std::string, MergedCategory> g_mergedCache;
|
||||
|
||||
static void InvalidateMergedCache() {
|
||||
g_mergedCache.clear();
|
||||
}
|
||||
|
||||
// TODO: /DATA/FONT/PROPERTY could just be generated from our fontRegistry in sceFont.cpp.
|
||||
|
||||
// Partial dump of the PSP registry using tests/misc/reg.prx in pspautotests
|
||||
@@ -967,11 +993,94 @@ void __RegInit() {
|
||||
g_openRegistryCount = 0;
|
||||
g_handleGen = 1337;
|
||||
g_openCategories.clear();
|
||||
g_overlay.clear();
|
||||
InvalidateMergedCache();
|
||||
}
|
||||
|
||||
void __RegShutdown() {
|
||||
g_openCategories.clear();
|
||||
g_openRegistryCount = 0;
|
||||
g_overlay.clear();
|
||||
InvalidateMergedCache();
|
||||
}
|
||||
|
||||
// Walks the static registry dump only (no guest writes applied).
|
||||
static const KeyValue *LookupStaticCategory(const std::string &path, int *count) {
|
||||
const KeyValue *curDir = ROOT;
|
||||
int curCount = ARRAY_SIZE(ROOT);
|
||||
|
||||
if (!path.empty()) {
|
||||
std::vector<std::string_view> parts;
|
||||
SplitString(path, '/', parts);
|
||||
for (const auto part : parts) {
|
||||
bool found = false;
|
||||
for (int i = 0; i < curCount; i++) {
|
||||
if (equals(curDir[i].name, part)) {
|
||||
if (curDir[i].type == ValueType::DIR) {
|
||||
curCount = curDir[i].intValue;
|
||||
curDir = curDir[i].dirContents;
|
||||
found = true;
|
||||
break;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*count = curCount;
|
||||
return curDir;
|
||||
}
|
||||
|
||||
// Merges the static dump entries for path with the overlay entries written by the guest.
|
||||
static const MergedCategory &GetMergedCategory(const std::string &path) {
|
||||
auto cached = g_mergedCache.find(path);
|
||||
if (cached != g_mergedCache.end()) {
|
||||
return cached->second;
|
||||
}
|
||||
|
||||
MergedCategory &merged = g_mergedCache[path];
|
||||
int staticCount = 0;
|
||||
const KeyValue *staticKeyvals = LookupStaticCategory(path, &staticCount);
|
||||
if (staticKeyvals) {
|
||||
merged.keyvals.assign(staticKeyvals, staticKeyvals + staticCount);
|
||||
}
|
||||
|
||||
auto overlayIter = g_overlay.find(path);
|
||||
if (overlayIter != g_overlay.end()) {
|
||||
for (const auto &entry : overlayIter->second) {
|
||||
const std::string &keyName = entry.first;
|
||||
const OverlayValue &value = entry.second;
|
||||
|
||||
KeyValue kv;
|
||||
kv.name = keyName;
|
||||
kv.dirContents = nullptr;
|
||||
bool replaced = false;
|
||||
for (auto &existing : merged.keyvals) {
|
||||
if (equals(existing.name, keyName)) {
|
||||
merged.strStorage.push_back(value.str);
|
||||
existing.strValue = merged.strStorage.back().c_str();
|
||||
existing.type = value.type;
|
||||
existing.intValue = value.intValue;
|
||||
replaced = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!replaced) {
|
||||
merged.strStorage.push_back(value.str);
|
||||
kv.strValue = merged.strStorage.back().c_str();
|
||||
kv.type = value.type;
|
||||
kv.intValue = value.intValue;
|
||||
merged.keyvals.push_back(kv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
static const KeyValue *LookupCategory(std::string_view path, int *count) {
|
||||
@@ -979,34 +1088,33 @@ static const KeyValue *LookupCategory(std::string_view path, int *count) {
|
||||
std::vector<std::string_view> parts;
|
||||
SplitString(path, '/', parts);
|
||||
|
||||
const KeyValue *curDir = ROOT;
|
||||
int curCount = ARRAY_SIZE(ROOT);
|
||||
|
||||
const MergedCategory *cur = &GetMergedCategory("");
|
||||
std::string curPath;
|
||||
for (const auto part : parts) {
|
||||
bool found = false;
|
||||
for (int i = 0; i < curCount; i++) {
|
||||
if (equals(curDir[i].name, part)) {
|
||||
// Found the subdir.
|
||||
if (curDir[i].type == ValueType::DIR) {
|
||||
// Must update curCount before curDir, of course (since that line accesses it).
|
||||
curCount = curDir[i].intValue;
|
||||
curDir = curDir[i].dirContents;
|
||||
found = true;
|
||||
break;
|
||||
} else {
|
||||
for (const auto &kv : cur->keyvals) {
|
||||
if (equals(kv.name, part)) {
|
||||
if (kv.type != ValueType::DIR) {
|
||||
ERROR_LOG(Log::sceReg, "Not a dir");
|
||||
return nullptr;
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
WARN_LOG(Log::sceReg, "LookupCategory: Path not found: %.*s", (int)path.size(), path.data());
|
||||
return nullptr;
|
||||
}
|
||||
if (!curPath.empty()) {
|
||||
curPath += '/';
|
||||
}
|
||||
curPath += std::string(part);
|
||||
cur = &GetMergedCategory(curPath);
|
||||
}
|
||||
|
||||
*count = curCount;
|
||||
return curDir;
|
||||
*count = (int)cur->keyvals.size();
|
||||
return cur->keyvals.data();
|
||||
}
|
||||
|
||||
void __RegDoState(PointerWrap &p) {
|
||||
@@ -1354,11 +1462,93 @@ int sceRegGetKeyValueByName(int catHandle, const char *name, u32 bufAddr, u32 si
|
||||
}
|
||||
|
||||
int sceRegSetKeyValue(int catHandle, const char *name, u32 bufAddr, u32 size) {
|
||||
return hleLogError(Log::sceReg, 0);
|
||||
if (!name) {
|
||||
return hleLogError(Log::sceReg, -1, "Invalid name pointer");
|
||||
}
|
||||
|
||||
auto iter = g_openCategories.find(catHandle);
|
||||
if (iter == g_openCategories.end()) {
|
||||
return hleLogError(Log::sceReg, 0, "Not an open category");
|
||||
}
|
||||
|
||||
// chfont.prx (and other callers) pass their whole 32-byte staging buffer as size even for
|
||||
// INT keys, so the size alone doesn't tell the value type. These are all the INT keys of
|
||||
// the font registry entries it writes; anything else falls back to the size heuristic.
|
||||
static const char *const knownIntKeys[] = {
|
||||
"h_size", "v_size", "h_resolution", "v_resolution", "extra_attributes", "weight",
|
||||
"family_code", "style", "sub_style", "language_code", "region_code", "country_code",
|
||||
"expire_date", "shadow_option", "num_fonts",
|
||||
};
|
||||
bool isIntKey = size == sizeof(u32);
|
||||
for (const char *key : knownIntKeys) {
|
||||
if (equals(name, key)) {
|
||||
isIntKey = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
OverlayValue value;
|
||||
if (isIntKey && Memory::IsValid4AlignedAddress(bufAddr)) {
|
||||
value.type = ValueType::INT;
|
||||
value.intValue = (int)Memory::ReadUnchecked_U32(bufAddr);
|
||||
} else if (Memory::IsValidRange(bufAddr, size)) {
|
||||
value.type = ValueType::STR;
|
||||
const char *str = (const char *)Memory::GetPointerUnchecked(bufAddr);
|
||||
value.str.assign(str, size);
|
||||
// Match the static dump convention: STR lengths include the null terminator.
|
||||
if (!value.str.empty() && value.str.back() != '\0') {
|
||||
value.str.push_back('\0');
|
||||
}
|
||||
value.intValue = (int)value.str.size();
|
||||
} else {
|
||||
return hleLogError(Log::sceReg, -1, "bad input addr");
|
||||
}
|
||||
|
||||
std::string path = iter->second.path;
|
||||
if (!path.empty() && path.front() == '/') {
|
||||
path.erase(path.begin());
|
||||
}
|
||||
g_overlay[path][name] = value;
|
||||
InvalidateMergedCache();
|
||||
return hleLogDebug(Log::sceReg, 0, "set %s/%s", path.c_str(), name);
|
||||
}
|
||||
|
||||
int sceRegCreateKey(int catHandle, const char *name, int type, u32 size) {
|
||||
return hleLogError(Log::sceReg, 0);
|
||||
if (!name) {
|
||||
return hleLogError(Log::sceReg, -1, "Invalid name pointer");
|
||||
}
|
||||
|
||||
auto iter = g_openCategories.find(catHandle);
|
||||
if (iter == g_openCategories.end()) {
|
||||
return hleLogError(Log::sceReg, 0, "Not an open category");
|
||||
}
|
||||
|
||||
OverlayValue value;
|
||||
switch (type) {
|
||||
case REG_TYPE_DIR:
|
||||
value.isDir = true;
|
||||
value.type = ValueType::DIR;
|
||||
break;
|
||||
case REG_TYPE_INT:
|
||||
value.type = ValueType::INT;
|
||||
break;
|
||||
case REG_TYPE_STR:
|
||||
value.type = ValueType::STR;
|
||||
break;
|
||||
case REG_TYPE_BIN:
|
||||
value.type = ValueType::BIN;
|
||||
break;
|
||||
default:
|
||||
return hleLogError(Log::sceReg, -1, "bad type %d", type);
|
||||
}
|
||||
|
||||
std::string path = iter->second.path;
|
||||
if (!path.empty() && path.front() == '/') {
|
||||
path.erase(path.begin());
|
||||
}
|
||||
g_overlay[path][name] = value;
|
||||
InvalidateMergedCache();
|
||||
return hleLogDebug(Log::sceReg, 0, "create %s/%s (type %d)", path.c_str(), name, type);
|
||||
}
|
||||
// Speculated signature
|
||||
int sceRegRemoveKey(int catHandle, int key) {
|
||||
|
||||
Reference in new issue
Block a user