It's hitting this:
Code:
T *GetFast(SceUID handle)
{
const SceUID realHandle = handle - handleOffset;
_dbg_assert_(SCEKERNEL, realHandle >= 0 && realHandle < maxCount && occupied[realHandle]); <-- BOOM
return static_cast<T *>(pool[realHandle]);
}
This means one of:
* The game is deleting a thread in a way that doesn't remove it from the thread queues.
* The game is writing somewhere that is corrupting PPSSPP's memory.
* A buffer overflow in PPSSPP is overwriting PPSSPP's memory.
But, your crash seems to indicate a crazy value of priority, which suggests one of the latter...
In sceKernelThread.cpp, find:
Code:
void __KernelChangeReadyState(Thread *thread, SceUID threadID, bool ready)
{
// Passing the id as a parameter is just an optimization, if it's wrong it will cause havoc.
_dbg_assert_msg_(SCEKERNEL, thread->GetUID() == threadID, "Incorrect threadID");
int prio = thread->nt.currentPriority;
if (thread->isReady())
{
if (!ready)
threadReadyQueue.remove(prio, threadID);
}
else if (ready)
{
if (thread->isRunning())
threadReadyQueue.push_front(prio, threadID);
else
threadReadyQueue.push_back(prio, threadID);
thread->nt.status = THREADSTATUS_READY;
}
}
Change to:
Code:
void __KernelChangeReadyState(Thread *thread, SceUID threadID, bool ready)
{
// Passing the id as a parameter is just an optimization, if it's wrong it will cause havoc.
_dbg_assert_msg_(SCEKERNEL, thread->GetUID() == threadID, "Incorrect threadID");
int prio = thread->nt.currentPriority;
if (prio < 0 || prio > 127) {
NOTICE_LOG(HLE, "Wackjob crazy priority %d for thread %d", prio, threadID);
DebugBreak();
thread->nt.currentPriority = 127;
return;
}
if (thread->isReady())
{
if (!ready)
threadReadyQueue.remove(prio, threadID);
}
else if (ready)
{
if (thread->isRunning())
threadReadyQueue.push_front(prio, threadID);
else
threadReadyQueue.push_back(prio, threadID);
thread->nt.status = THREADSTATUS_READY;
}
}
Also:
Code:
Thread *__KernelNextThread() {
SceUID bestThread;
// If the current thread is running, it's a valid candidate.
Thread *cur = __GetCurrentThread();
if (cur && cur->isRunning())
{
bestThread = threadReadyQueue.pop_first_better(cur->nt.currentPriority);
if (bestThread != 0)
__KernelChangeReadyState(cur, currentThread, true);
}
else
bestThread = threadReadyQueue.pop_first();
// Assume threadReadyQueue has not become corrupt.
if (bestThread != 0)
return kernelObjects.GetFast<Thread>(bestThread);
else
return 0;
}
Change to:
Code:
Thread *__KernelNextThread() {
SceUID bestThread;
// If the current thread is running, it's a valid candidate.
Thread *cur = __GetCurrentThread();
if (cur && cur->isRunning())
{
bestThread = threadReadyQueue.pop_first_better(cur->nt.currentPriority);
if (bestThread != 0)
__KernelChangeReadyState(cur, currentThread, true);
}
else
bestThread = threadReadyQueue.pop_first();
// Assume threadReadyQueue has not become corrupt.
if (bestThread != 0) {
u32 errorIgnored;
Thread *t = kernelObjects.Get<Thread>(bestThread, errorIgnored);
if (t == NULL && bestThread != 0) {
NOTICE_LOG(HLE, "ACK ACK ACK ACK Invalid thread id %d on thread queue", bestThread);
DebugBreak();
// Try, try again?
t = __KernelNextThread();
}
if (t == NULL) {
NOTICE_LOG(HLE, "ACK ACK ACK ACK Thread queue empty");
DebugBreak();
// Just brute force it, hopefully...
return kernelObjects.Get<Thread>(threadIdleID[0], errorIgnored);
}
return t;
} else {
return 0;
}
}
(and run in the debugger.)
-[Unknown]