diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index c5b7b85e71..8e9bea0b5b 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -596,6 +596,8 @@ KernelObject *KernelObjectPool::CreateByIDType(int type) { return __KernelFileNodeObject(); case PPSSPP_KERNEL_TMID_DirList: return __KernelDirListingObject(); + case SCE_KERNEL_TMID_ThreadEventHandler: + return __KernelThreadEventHandlerObject(); default: ERROR_LOG(COMMON, "Unable to load state: could not find object type %d.", type); @@ -786,10 +788,6 @@ const HLEFunction ThreadManForUser[] = {0X278C0DF5, &WrapI_IU, "sceKernelWaitThreadEnd", 'i', "ix" }, {0XD59EAD2F, &WrapI_I, "sceKernelWakeupThread", 'i', "i" }, //AI Go, audio? - {0X0C106E53, nullptr, "sceKernelRegisterThreadEventHandler", '?', "" }, - {0X72F3C145, nullptr, "sceKernelReleaseThreadEventHandler", '?', "" }, - {0X369EEB6B, nullptr, "sceKernelReferThreadEventHandlerStatus", '?', "" }, - {0x349d6d6c, &sceKernelCheckCallback, "sceKernelCheckCallback", 'i', "" }, {0XE81CAF8F, &WrapI_CUU, "sceKernelCreateCallback", 'i', "sxx" }, {0XEDBA5844, &WrapI_I, "sceKernelDeleteCallback", 'i', "i" }, @@ -864,6 +862,9 @@ const HLEFunction ThreadManForUser[] = {0x0E927AED, &_sceKernelReturnFromTimerHandler, "_sceKernelReturnFromTimerHandler", 'v', "" }, {0X532A522E, &WrapV_I<_sceKernelExitThread>, "_sceKernelExitThread", 'v', "i" }, + {0x0C106E53, &WrapI_CIUUU, "sceKernelRegisterThreadEventHandler", 'i', "sixxx", }, + {0x72F3C145, &WrapI_I, "sceKernelReleaseThreadEventHandler", 'i', "i" }, + {0x369EEB6B, &WrapI_IU, "sceKernelReferThreadEventHandlerStatus", 'i', "ip" }, // Shouldn't hook this up. No games should import this function manually and call it. // {0x6E9EA350, _sceKernelReturnFromCallback,"_sceKernelReturnFromCallback"}, diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 589ac0fbfe..d5414420e4 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -3470,3 +3470,92 @@ int LoadExecForUser_362A956B() Memory::Write_U32(-1, parameterArea + 8); return 0; } + +static const SceUID SCE_TE_THREADID_ALL_USER = 0xFFFFFFF0; +enum { + THREADEVENT_CREATE = 1, + THREADEVENT_START = 2, + THREADEVENT_EXIT = 4, + THREADEVENT_DELETE = 8, + THREADEVENT_KNOWN = THREADEVENT_CREATE | THREADEVENT_START | THREADEVENT_EXIT | THREADEVENT_DELETE, +}; + +struct NativeThreadEventHandler { + u32 size; + char name[KERNELOBJECT_MAX_NAME_LENGTH + 1]; + SceUID threadID; + u32 mask; + u32 handlerPtr; + u32 commonArg; +}; + +struct ThreadEventHandler : public KernelObject { + const char *GetName() { return nteh.name; } + const char *GetTypeName() { return "ThreadEventHandler"; } + static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_TEID; } + static int GetStaticIDType() { return SCE_KERNEL_TMID_ThreadEventHandler; } + int GetIDType() const { return SCE_KERNEL_TMID_ThreadEventHandler; } + + virtual void DoState(PointerWrap &p) { + auto s = p.Section("ThreadEventHandler", 1); + if (!s) + return; + + p.Do(nteh); + } + + NativeThreadEventHandler nteh; +}; + +KernelObject *__KernelThreadEventHandlerObject() { + // Default object to load from state. + return new ThreadEventHandler; +} + +SceUID sceKernelRegisterThreadEventHandler(const char *name, SceUID threadID, u32 mask, u32 handlerPtr, u32 commonArg) { + if (!name) { + return hleReportError(SCEKERNEL, SCE_KERNEL_ERROR_ERROR, "invalid name"); + } + if (threadID == 0) { + return hleReportError(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_ATTR, "invalid thread id"); + } + u32 error; + if (kernelObjects.Get(threadID, error) == NULL && threadID != SCE_TE_THREADID_ALL_USER) { + return hleReportError(SCEKERNEL, error, "bad thread id"); + } + if ((mask & ~THREADEVENT_KNOWN) != 0) { + return hleReportError(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_MASK, "invalid event mask"); + } + + auto teh = new ThreadEventHandler; + teh->nteh.size = sizeof(teh->nteh); + strncpy(teh->nteh.name, name, KERNELOBJECT_MAX_NAME_LENGTH); + teh->nteh.name[KERNELOBJECT_MAX_NAME_LENGTH] = '\0'; + teh->nteh.threadID = threadID; + teh->nteh.mask = mask; + teh->nteh.handlerPtr = handlerPtr; + teh->nteh.commonArg = commonArg; + + SceUID uid = kernelObjects.Create(teh); + + return hleLogSuccessI(SCEKERNEL, uid); +} + +int sceKernelReleaseThreadEventHandler(SceUID uid) { + return hleLogSuccessI(SCEKERNEL, kernelObjects.Destroy(uid)); +} + +int sceKernelReferThreadEventHandlerStatus(SceUID uid, u32 infoPtr) { + u32 error; + auto teh = kernelObjects.Get(uid, error); + if (!teh) { + return hleReportError(SCEKERNEL, error, "bad handler id"); + } + + if (Memory::IsValidAddress(infoPtr) && Memory::Read_U32(infoPtr) != 0) { + Memory::WriteStruct(infoPtr, &teh->nteh); + return hleLogSuccessI(SCEKERNEL, 0); + } else { + return hleLogDebug(SCEKERNEL, 0, "struct size was 0"); + } +} diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 74ec6cd19e..8f02e17719 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -320,3 +320,8 @@ void __KernelChangeThreadState(SceUID threadId, ThreadStatus newStatus); int LoadExecForUser_362A956B(); int sceKernelRegisterExitCallback(SceUID cbId); + +KernelObject *__KernelThreadEventHandlerObject(); +SceUID sceKernelRegisterThreadEventHandler(const char *name, SceUID threadID, u32 mask, u32 handlerPtr, u32 commonArg); +int sceKernelReleaseThreadEventHandler(SceUID uid); +int sceKernelReferThreadEventHandlerStatus(SceUID uid, u32 infoPtr);