From 60e6fa514ad3f15f1a0cd7ddda91ac45dc4477de Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2013 00:02:36 -0800 Subject: [PATCH 01/17] Start with sceIoOpenAsync() cleaning up async io. Now it actually fires the callback as necessary. Some games care. --- Core/HLE/sceIo.cpp | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 2a909a105077..f40002c44c88 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -25,6 +25,7 @@ #include "HLE.h" #include "../MIPS/MIPS.h" #include "../HW/MemoryStick.h" +#include "Core/CoreTiming.h" #include "../FileSystems/FileSystem.h" #include "../FileSystems/MetaFileSystem.h" @@ -90,6 +91,7 @@ typedef u64 SceIores; typedef u32 (*DeferredAction)(SceUID id, int param); DeferredAction defAction = 0; u32 defParam = 0; +int asyncNotifyEvent = -1; #define SCE_STM_FDIR 0x1000 #define SCE_STM_FREG 0x2000 @@ -187,15 +189,35 @@ class FileNode : public KernelObject { /******************************************************************************/ +void __IoCompleteAsyncIO(SceUID id); + static void TellFsThreadEnded (SceUID threadID) { pspFileSystem.ThreadEnded(threadID); } +// Async IO seems to work roughly like this: +// 1. Game calls SceIo*Async() to start the process. +// 2. This runs a thread with a customizable priority. +// 3. The operation runs, which takes an inconsistent amount of time from UMD. +// 4. Once done (regardless of other syscalls), the fd-registered callback is notified. +// 5. The game can find out via *CB() or sceKernelCheckCallback(). +// 6. At this point, the fd is STILL not usable. +// 7. One must call sceIoWaitAsync / sceIoWaitAsyncCB / sceIoPollAsync / possibly sceIoGetAsyncStat. +// 8. Finally, the fd is usable (or closed via sceIoCloseAsync.) Presumably the io thread has joined now. + +// TODO: We don't do any of that yet. +// For now, let's at least delay the callback mnotification. +void __IoAsyncNotify(u64 userdata, int cyclesLate) { + __IoCompleteAsyncIO((SceUID) userdata); +} + void __IoInit() { INFO_LOG(HLE, "Starting up I/O..."); MemoryStick_SetFatState(PSP_FAT_MEMORYSTICK_STATE_ASSIGNED); + asyncNotifyEvent = CoreTiming::RegisterEvent("IoAsyncNotify", __IoAsyncNotify); + #ifdef _WIN32 char path_buffer[_MAX_PATH], drive[_MAX_DRIVE] ,dir[_MAX_DIR], file[_MAX_FNAME], ext[_MAX_EXT]; @@ -242,6 +264,9 @@ void __IoDoState(PointerWrap &p) { if (defAction != NULL) { WARN_LOG(HLE, "FIXME: Savestate failure: deferred IO not saved yet."); } + + p.Do(asyncNotifyEvent); + CoreTiming::RestoreRegisterEvent(asyncNotifyEvent, "IoAsyncNotify", __IoAsyncNotify); } void __IoShutdown() { @@ -987,8 +1012,9 @@ u32 sceIoOpenAsync(const char *filename, int flags, int mode) DEBUG_LOG(HLE, "sceIoOpenAsync() sorta implemented"); u32 fd = sceIoOpen(filename, flags, mode); - // TODO: This can't actually have a callback yet, but if it's set before waiting, it should be called. - __IoCompleteAsyncIO(fd); + // TODO: Timing is very inconsistent. From ms0, 10ms - 20ms depending on filesize/dir depth? From umd, can take > 1s. + // For now let's aim low. + CoreTiming::ScheduleEvent(usToCycles(100), asyncNotifyEvent, fd); // We have to return an fd here, which may have been destroyed when we reach Wait if it failed. if (fd == ERROR_ERRNO_FILE_NOT_FOUND) From 56d369b4d732c1d2f0cc456ef8703293b6bc4c41 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2013 00:14:50 -0800 Subject: [PATCH 02/17] Tests show async result is a sign-extended s64. Errors become fffffff80010002, etc. --- Core/HLE/sceIo.cpp | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index f40002c44c88..28545b99d4e4 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -170,7 +170,7 @@ class FileNode : public KernelObject { u32 callbackID; u32 callbackArg; - u32 asyncResult; + s64 asyncResult; bool pendingAsyncResult; bool sectorBlockMode; @@ -438,12 +438,12 @@ u32 sceIoRead(int id, u32 data_addr, int size) { else if (Memory::IsValidAddress(data_addr)) { u8 *data = (u8*) Memory::GetPointer(data_addr); if(f->npdrm){ - f->asyncResult = (u32) npdrmRead(f, data, size); + f->asyncResult = npdrmRead(f, data, size); }else{ - f->asyncResult = (u32) pspFileSystem.ReadFile(f->handle, data, size); + f->asyncResult = pspFileSystem.ReadFile(f->handle, data, size); } DEBUG_LOG(HLE, "%i=sceIoRead(%d, %08x , %i)", f->asyncResult, id, data_addr, size); - return f->asyncResult; + return (u32) f->asyncResult; } else { ERROR_LOG(HLE, "sceIoRead Reading into bad pointer %08x", data_addr); return -1; @@ -480,8 +480,8 @@ u32 sceIoWrite(int id, void *data_ptr, int size) else { u8 *data = (u8*) data_ptr; - f->asyncResult = (u32) pspFileSystem.WriteFile(f->handle, data, size); - return f->asyncResult; + f->asyncResult = pspFileSystem.WriteFile(f->handle, data, size); + return (u32) f->asyncResult; } } else { ERROR_LOG(HLE, "sceIoWrite ERROR: no file open"); @@ -574,10 +574,10 @@ s64 sceIoLseek(int id, s64 offset, int whence) { if(f->npdrm){ f->asyncResult = npdrmLseek(f, (s32)offset, seek); }else{ - f->asyncResult = (u32) pspFileSystem.SeekFile(f->handle, (s32) offset, seek); + f->asyncResult = pspFileSystem.SeekFile(f->handle, (s32) offset, seek); } - DEBUG_LOG(HLE, "%i = sceIoLseek(%d,%i,%i)", f->asyncResult, id, (int) offset, whence); - return f->asyncResult; + DEBUG_LOG(HLE, "%i = sceIoLseek(%d,%i,%i)", (u32) f->asyncResult, id, (int) offset, whence); + return (u32) f->asyncResult; } else { ERROR_LOG(HLE, "sceIoLseek(%d, %i, %i) - ERROR: invalid file", id, (int) offset, whence); return error; @@ -610,10 +610,10 @@ u32 sceIoLseek32(int id, int offset, int whence) { if(f->npdrm){ f->asyncResult = npdrmLseek(f, (s32)offset, seek); }else{ - f->asyncResult = (u32) pspFileSystem.SeekFile(f->handle, (s32) offset, seek); + f->asyncResult = pspFileSystem.SeekFile(f->handle, (s32) offset, seek); } - DEBUG_LOG(HLE, "%i = sceIoLseek32(%d,%i,%i)", f->asyncResult, id, (int) offset, whence); - return f->asyncResult; + DEBUG_LOG(HLE, "%i = sceIoLseek32(%d,%i,%i)", (u32) f->asyncResult, id, (int) offset, whence); + return (u32) f->asyncResult; } else { ERROR_LOG(HLE, "sceIoLseek32(%d, %i, %i) - ERROR: invalid file", id, (int) offset, whence); return error; @@ -1012,20 +1012,21 @@ u32 sceIoOpenAsync(const char *filename, int flags, int mode) DEBUG_LOG(HLE, "sceIoOpenAsync() sorta implemented"); u32 fd = sceIoOpen(filename, flags, mode); - // TODO: Timing is very inconsistent. From ms0, 10ms - 20ms depending on filesize/dir depth? From umd, can take > 1s. - // For now let's aim low. - CoreTiming::ScheduleEvent(usToCycles(100), asyncNotifyEvent, fd); - // We have to return an fd here, which may have been destroyed when we reach Wait if it failed. if (fd == ERROR_ERRNO_FILE_NOT_FOUND) { + // TODO: Should close/delete after waitasync/etc. FileNode *f = new FileNode(); fd = kernelObjects.Create(f); - f->handle = 0; + f->handle = fd; f->fullpath = filename; - f->asyncResult = ERROR_ERRNO_FILE_NOT_FOUND; + f->asyncResult = (s32) ERROR_ERRNO_FILE_NOT_FOUND; } + // TODO: Timing is very inconsistent. From ms0, 10ms - 20ms depending on filesize/dir depth? From umd, can take > 1s. + // For now let's aim low. + CoreTiming::ScheduleEvent(usToCycles(100), asyncNotifyEvent, fd); + return fd; } @@ -1043,8 +1044,8 @@ u32 sceIoGetAsyncStat(int id, u32 poll, u32 address) FileNode *f = kernelObjects.Get < FileNode > (id, error); if (f) { - Memory::Write_U64(f->asyncResult, address); - DEBUG_LOG(HLE, "%i = sceIoGetAsyncStat(%i, %i, %08x) (HACK)", (u32) f->asyncResult, id, poll, address); + Memory::Write_U64((u64) f->asyncResult, address); + DEBUG_LOG(HLE, "%lli = sceIoGetAsyncStat(%i, %i, %08x) (HACK)", f->asyncResult, id, poll, address); if (!poll) hleReSchedule("io waited"); return 0; //completed From da7dfb07c301a0af45ee949fd88fbff3e966945c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2013 01:12:03 -0800 Subject: [PATCH 03/17] Correctly notify callbacks on sceIoCloseAsync(). And remove defAction, it was the wrong way. --- Core/HLE/sceIo.cpp | 54 +++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 28545b99d4e4..d42f1100ce67 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -45,7 +45,7 @@ extern "C" { // For headless screenshots. #include "sceDisplay.h" -#define ERROR_ERRNO_FILE_NOT_FOUND 0x80010002 +const int ERROR_ERRNO_FILE_NOT_FOUND = 0x80010002; #define ERROR_MEMSTICK_DEVCTL_BAD_PARAMS 0x80220081 #define ERROR_MEMSTICK_DEVCTL_TOO_MANY_CALLBACKS 0x80220082 @@ -88,9 +88,6 @@ typedef s32 SceMode; typedef s64 SceOff; typedef u64 SceIores; -typedef u32 (*DeferredAction)(SceUID id, int param); -DeferredAction defAction = 0; -u32 defParam = 0; int asyncNotifyEvent = -1; #define SCE_STM_FDIR 0x1000 @@ -171,9 +168,10 @@ class FileNode : public KernelObject { u32 callbackArg; s64 asyncResult; - bool pendingAsyncResult; + bool sectorBlockMode; + // TODO: Use an enum instead? bool closePending; PSPFileInfo info; @@ -205,6 +203,9 @@ static void TellFsThreadEnded (SceUID threadID) { // 7. One must call sceIoWaitAsync / sceIoWaitAsyncCB / sceIoPollAsync / possibly sceIoGetAsyncStat. // 8. Finally, the fd is usable (or closed via sceIoCloseAsync.) Presumably the io thread has joined now. +// TODO: Closed files are a bit special: until the fd is reused (?), the async result is still available. +// Clearly a buffer is used, it doesn't seem like they are actually kernel objects. + // TODO: We don't do any of that yet. // For now, let's at least delay the callback mnotification. void __IoAsyncNotify(u64 userdata, int cyclesLate) { @@ -259,19 +260,11 @@ void __IoInit() { } void __IoDoState(PointerWrap &p) { - // TODO: defAction is hard to save, and not the right way anyway. - // Should probbly be an enum and on the FileNode anyway. - if (defAction != NULL) { - WARN_LOG(HLE, "FIXME: Savestate failure: deferred IO not saved yet."); - } - p.Do(asyncNotifyEvent); CoreTiming::RestoreRegisterEvent(asyncNotifyEvent, "IoAsyncNotify", __IoAsyncNotify); } void __IoShutdown() { - defAction = 0; - defParam = 0; } u32 __IoGetFileHandleFromId(u32 id, u32 &outError) @@ -519,7 +512,7 @@ u32 sceIoCancel(int id) u32 error; FileNode *f = kernelObjects.Get < FileNode > (id, error); if (f) { - f->closePending = true; + // TODO: Cancel the async operation if possible? } else { ERROR_LOG(HLE, "sceIoCancel: unknown id %d", id); error = ERROR_KERNEL_BAD_FILE_DESCRIPTOR; @@ -965,11 +958,16 @@ u32 __IoClose(SceUID actedFd, int closedFd) int sceIoCloseAsync(int id) { DEBUG_LOG(HLE, "sceIoCloseAsync(%d)", id); - //sceIoClose(); - // TODO: Not sure this is a good solution. Seems like you can defer one per fd. - defAction = &__IoClose; - defParam = id; - return 0; + u32 error; + FileNode *f = kernelObjects.Get(id, error); + if (f) + { + f->closePending = true; + CoreTiming::ScheduleEvent(usToCycles(100), asyncNotifyEvent, id); + return 0; + } + else + return error; } u32 sceIoLseekAsync(int id, s64 offset, int whence) @@ -1020,7 +1018,7 @@ u32 sceIoOpenAsync(const char *filename, int flags, int mode) fd = kernelObjects.Create(f); f->handle = fd; f->fullpath = filename; - f->asyncResult = (s32) ERROR_ERRNO_FILE_NOT_FOUND; + f->asyncResult = ERROR_ERRNO_FILE_NOT_FOUND; } // TODO: Timing is very inconsistent. From ms0, 10ms - 20ms depending on filesize/dir depth? From umd, can take > 1s. @@ -1062,9 +1060,8 @@ int sceIoWaitAsync(int id, u32 address) { FileNode *f = kernelObjects.Get < FileNode > (id, error); if (f) { u64 res = f->asyncResult; - if (defAction) { - res = defAction(id, defParam); - defAction = 0; + if (f->closePending) { + kernelObjects.Destroy(id); } Memory::Write_U64(res, address); DEBUG_LOG(HLE, "%i = sceIoWaitAsync(%i, %08x) (HACK)", (u32) res, id, address); @@ -1082,9 +1079,9 @@ int sceIoWaitAsyncCB(int id, u32 address) { FileNode *f = kernelObjects.Get < FileNode > (id, error); if (f) { u64 res = f->asyncResult; - if (defAction) { - res = defAction(id, defParam); - defAction = 0; + // TODO: Is the close before or after executing the callback? + if (f->closePending) { + kernelObjects.Destroy(id); } Memory::Write_U64(res, address); DEBUG_LOG(HLE, "%i = sceIoWaitAsyncCB(%i, %08x) (HACK)", (u32) res, id, address); @@ -1102,9 +1099,8 @@ u32 sceIoPollAsync(int id, u32 address) { FileNode *f = kernelObjects.Get < FileNode > (id, error); if (f) { u64 res = f->asyncResult; - if (defAction) { - res = defAction(id, defParam); - defAction = 0; + if (f->closePending) { + kernelObjects.Destroy(id); } Memory::Write_U64(res, address); DEBUG_LOG(HLE, "%i = sceIoPollAsync(%i, %08x) (HACK)", (u32) res, id, address); From 99240914eaf19840b98bb3b7dbc9b513d48076d4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2013 01:13:45 -0800 Subject: [PATCH 04/17] sceIoLseek() should sign extend error codes. < 0 means error, and 0x0000000080000000 is not < 0. --- Core/HLE/sceIo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index d42f1100ce67..1869caace043 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -573,7 +573,7 @@ s64 sceIoLseek(int id, s64 offset, int whence) { return (u32) f->asyncResult; } else { ERROR_LOG(HLE, "sceIoLseek(%d, %i, %i) - ERROR: invalid file", id, (int) offset, whence); - return error; + return (s32) error; } } From 2b8cb0011c2a3752cc30dd6077e0fd00125570ac Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2013 03:06:43 -0700 Subject: [PATCH 05/17] sceIoRead() always reschedules, and handle async. Tried to estimate some rough timing. Fixes Unchained Blades. --- Core/HLE/sceIo.cpp | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 1869caace043..8ff14a6d4355 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -415,7 +415,7 @@ u32 npdrmRead(FileNode *f, u8 *data, int size) { } //Not sure about wrapping it or not, since the log seems to take the address of the data var -u32 sceIoRead(int id, u32 data_addr, int size) { +int __IoRead(int id, u32 data_addr, int size) { if (id == 3) { DEBUG_LOG(HLE, "sceIoRead STDIN"); return 0; //stdin @@ -431,12 +431,10 @@ u32 sceIoRead(int id, u32 data_addr, int size) { else if (Memory::IsValidAddress(data_addr)) { u8 *data = (u8*) Memory::GetPointer(data_addr); if(f->npdrm){ - f->asyncResult = npdrmRead(f, data, size); + return npdrmRead(f, data, size); }else{ - f->asyncResult = pspFileSystem.ReadFile(f->handle, data, size); + return pspFileSystem.ReadFile(f->handle, data, size); } - DEBUG_LOG(HLE, "%i=sceIoRead(%d, %08x , %i)", f->asyncResult, id, data_addr, size); - return (u32) f->asyncResult; } else { ERROR_LOG(HLE, "sceIoRead Reading into bad pointer %08x", data_addr); return -1; @@ -447,6 +445,34 @@ u32 sceIoRead(int id, u32 data_addr, int size) { } } +u32 sceIoRead(int id, u32 data_addr, int size) { + int result = __IoRead(id, data_addr, size); + if (result >= 0) + { + DEBUG_LOG(HLE, "%x=sceIoRead(%d, %08x, %x)", result, id, data_addr, size); + // TODO: Timing is probably not very accurate, low estimate. + return hleDelayResult(result, "io read", result / 100); + } + else + return result; +} + +u32 sceIoReadAsync(int id, u32 data_addr, int size) +{ + u32 error; + FileNode *f = kernelObjects.Get < FileNode > (id, error); + if (f) { + f->asyncResult = __IoRead(id, data_addr, size); + // TODO: Not sure what the correct delay is (and technically we shouldn't read into the buffer yet...) + CoreTiming::ScheduleEvent(usToCycles(size / 100), asyncNotifyEvent, id); + DEBUG_LOG(HLE, "%llx=sceIoReadAsync(%d, %08x, %x)", f->asyncResult, id, data_addr, size); + return 0; + } else { + ERROR_LOG(HLE, "sceIoReadAsync: bad file %d", id); + return error; + } +} + u32 sceIoWrite(int id, void *data_ptr, int size) { if (id == 2) { @@ -1028,14 +1054,6 @@ u32 sceIoOpenAsync(const char *filename, int flags, int mode) return fd; } -u32 sceIoReadAsync(int id, u32 data_addr, int size) -{ - DEBUG_LOG(HLE, "sceIoReadAsync(%d)", id); - sceIoRead(id, data_addr, size); - __IoCompleteAsyncIO(id); - return 0; -} - u32 sceIoGetAsyncStat(int id, u32 poll, u32 address) { u32 error; From 0b097fb67b51db24e40ea295214b33752190dc0e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2013 03:43:50 -0700 Subject: [PATCH 06/17] Make async IO actually wait for completion. --- Core/HLE/sceIo.cpp | 152 ++++++++++++++++++++++++++----------- Core/HLE/sceKernelThread.h | 1 + 2 files changed, 109 insertions(+), 44 deletions(-) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 8ff14a6d4355..41391eabac33 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -209,7 +209,21 @@ static void TellFsThreadEnded (SceUID threadID) { // TODO: We don't do any of that yet. // For now, let's at least delay the callback mnotification. void __IoAsyncNotify(u64 userdata, int cyclesLate) { - __IoCompleteAsyncIO((SceUID) userdata); + SceUID threadID = userdata >> 32; + SceUID fd = (SceUID) (userdata & 0xFFFFFFFF); + __IoCompleteAsyncIO(fd); + + u32 error; + SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_IO, error); + u32 address = __KernelGetWaitValue(threadID, error); + if (waitID == fd && error == 0) { + __KernelResumeThreadFromWait(threadID, 0); + + FileNode *f = kernelObjects.Get(fd, error); + if (Memory::IsValidAddress(address) && f) { + Memory::Write_U64((u64) f->asyncResult, address); + } + } } void __IoInit() { @@ -326,6 +340,7 @@ void __IoCompleteAsyncIO(SceUID id) { if (f->callbackID) { __KernelNotifyCallback(THREAD_CALLBACK_IO, f->callbackID, f->callbackArg); } + f->pendingAsyncResult = false; } } @@ -359,6 +374,11 @@ void __IoGetStat(SceIoStat *stat, PSPFileInfo &info) { stat->st_private[0] = info.startSector; } +void __IoSchedAsync(SceUID fd, int usec) { + u64 param = ((u64)__KernelGetCurThread()) << 32 | fd; + CoreTiming::ScheduleEvent(usToCycles(usec), asyncNotifyEvent, param); +} + u32 sceIoGetstat(const char *filename, u32 addr) { SceIoStat stat; PSPFileInfo info = pspFileSystem.GetFileInfo(filename); @@ -437,7 +457,9 @@ int __IoRead(int id, u32 data_addr, int size) { } } else { ERROR_LOG(HLE, "sceIoRead Reading into bad pointer %08x", data_addr); - return -1; + // TODO: Returning 0 because it wasn't being sign-extended in async result before. + // What should this do? + return 0; } } else { ERROR_LOG(HLE, "sceIoRead ERROR: no file open"); @@ -447,8 +469,7 @@ int __IoRead(int id, u32 data_addr, int size) { u32 sceIoRead(int id, u32 data_addr, int size) { int result = __IoRead(id, data_addr, size); - if (result >= 0) - { + if (result >= 0) { DEBUG_LOG(HLE, "%x=sceIoRead(%d, %08x, %x)", result, id, data_addr, size); // TODO: Timing is probably not very accurate, low estimate. return hleDelayResult(result, "io read", result / 100); @@ -457,14 +478,14 @@ u32 sceIoRead(int id, u32 data_addr, int size) { return result; } -u32 sceIoReadAsync(int id, u32 data_addr, int size) -{ +u32 sceIoReadAsync(int id, u32 data_addr, int size) { u32 error; FileNode *f = kernelObjects.Get < FileNode > (id, error); if (f) { f->asyncResult = __IoRead(id, data_addr, size); + f->pendingAsyncResult = true; // TODO: Not sure what the correct delay is (and technically we shouldn't read into the buffer yet...) - CoreTiming::ScheduleEvent(usToCycles(size / 100), asyncNotifyEvent, id); + __IoSchedAsync(id, size / 100); DEBUG_LOG(HLE, "%llx=sceIoReadAsync(%d, %08x, %x)", f->asyncResult, id, data_addr, size); return 0; } else { @@ -639,7 +660,7 @@ u32 sceIoLseek32(int id, int offset, int whence) { } } -u32 sceIoOpen(const char* filename, int flags, int mode) { +FileNode *__IoOpen(const char* filename, int flags, int mode) { //memory stick filename int access = FILEACCESS_NONE; if (flags & O_RDONLY) @@ -653,11 +674,8 @@ u32 sceIoOpen(const char* filename, int flags, int mode) { PSPFileInfo info = pspFileSystem.GetFileInfo(filename); u32 h = pspFileSystem.OpenFile(filename, (FileAccess) access); - if (h == 0) - { - ERROR_LOG(HLE, - "ERROR_ERRNO_FILE_NOT_FOUND=sceIoOpen(%s, %08x, %08x) - file not found", filename, flags, mode); - return ERROR_ERRNO_FILE_NOT_FOUND; + if (h == 0) { + return NULL; } FileNode *f = new FileNode(); @@ -670,6 +688,17 @@ u32 sceIoOpen(const char* filename, int flags, int mode) { f->npdrm = (flags & O_NPDRM)? true: false; + return f; +} + +u32 sceIoOpen(const char* filename, int flags, int mode) { + FileNode *f = __IoOpen(filename, flags, mode); + if (f == NULL) { + ERROR_LOG(HLE, "ERROR_ERRNO_FILE_NOT_FOUND=sceIoOpen(%s, %08x, %08x) - file not found", filename, flags, mode); + return ERROR_ERRNO_FILE_NOT_FOUND; + } + + SceUID id = f->GetUID(); DEBUG_LOG(HLE, "%i=sceIoOpen(%s, %08x, %08x)", id, filename, flags, mode); return id; } @@ -989,7 +1018,10 @@ int sceIoCloseAsync(int id) if (f) { f->closePending = true; - CoreTiming::ScheduleEvent(usToCycles(100), asyncNotifyEvent, id); + f->asyncResult = 0; + f->pendingAsyncResult = true; + // TODO: Rough estimate. + __IoSchedAsync(id, 100); return 0; } else @@ -1033,23 +1065,31 @@ u32 sceIoLseek32Async(int id, int offset, int whence) u32 sceIoOpenAsync(const char *filename, int flags, int mode) { - DEBUG_LOG(HLE, "sceIoOpenAsync() sorta implemented"); - u32 fd = sceIoOpen(filename, flags, mode); + FileNode *f = __IoOpen(filename, flags, mode); + SceUID fd; // We have to return an fd here, which may have been destroyed when we reach Wait if it failed. - if (fd == ERROR_ERRNO_FILE_NOT_FOUND) + if (f == NULL) { - // TODO: Should close/delete after waitasync/etc. - FileNode *f = new FileNode(); + ERROR_LOG(HLE, "ERROR_ERRNO_FILE_NOT_FOUND=sceIoOpenAsync(%s, %08x, %08x) - file not found", filename, flags, mode); + + f = new FileNode(); fd = kernelObjects.Create(f); f->handle = fd; f->fullpath = filename; f->asyncResult = ERROR_ERRNO_FILE_NOT_FOUND; + f->closePending = true; + } + else + { + fd = f->GetUID(); + DEBUG_LOG(HLE, "%x=sceIoOpenAsync(%s, %08x, %08x)", fd, filename, flags, mode); } + f->pendingAsyncResult = true; // TODO: Timing is very inconsistent. From ms0, 10ms - 20ms depending on filesize/dir depth? From umd, can take > 1s. // For now let's aim low. - CoreTiming::ScheduleEvent(usToCycles(100), asyncNotifyEvent, fd); + __IoSchedAsync(fd, 100); return fd; } @@ -1060,10 +1100,22 @@ u32 sceIoGetAsyncStat(int id, u32 poll, u32 address) FileNode *f = kernelObjects.Get < FileNode > (id, error); if (f) { - Memory::Write_U64((u64) f->asyncResult, address); - DEBUG_LOG(HLE, "%lli = sceIoGetAsyncStat(%i, %i, %08x) (HACK)", f->asyncResult, id, poll, address); - if (!poll) - hleReSchedule("io waited"); + if (f->pendingAsyncResult) { + if (poll) { + DEBUG_LOG(HLE, "%lli = sceIoGetAsyncStat(%i, %i, %08x): not ready", f->asyncResult, id, poll, address); + return 1; + } else { + DEBUG_LOG(HLE, "%lli = sceIoGetAsyncStat(%i, %i, %08x): waiting", f->asyncResult, id, poll, address); + __KernelWaitCurThread(WAITTYPE_IO, id, address, 0, false, "io waited"); + } + } else { + DEBUG_LOG(HLE, "%lli = sceIoGetAsyncStat(%i, %i, %08x)", f->asyncResult, id, poll, address); + Memory::Write_U64((u64) f->asyncResult, address); + + if (f->closePending) { + kernelObjects.Destroy(id); + } + } return 0; //completed } else @@ -1077,13 +1129,17 @@ int sceIoWaitAsync(int id, u32 address) { u32 error; FileNode *f = kernelObjects.Get < FileNode > (id, error); if (f) { - u64 res = f->asyncResult; - if (f->closePending) { - kernelObjects.Destroy(id); + if (f->pendingAsyncResult) { + DEBUG_LOG(HLE, "%lli = sceIoWaitAsync(%i, %08x): waiting", f->asyncResult, id, address); + __KernelWaitCurThread(WAITTYPE_IO, id, address, 0, false, "io waited"); + } else { + DEBUG_LOG(HLE, "%lli = sceIoWaitAsync(%i, %08x)", f->asyncResult, id, address); + Memory::Write_U64((u64) f->asyncResult, address); + + if (f->closePending) { + kernelObjects.Destroy(id); + } } - Memory::Write_U64(res, address); - DEBUG_LOG(HLE, "%i = sceIoWaitAsync(%i, %08x) (HACK)", (u32) res, id, address); - hleReSchedule("io waited"); return 0; //completed } else { ERROR_LOG(HLE, "ERROR - sceIoWaitAsync waiting for invalid id %i", id); @@ -1096,15 +1152,18 @@ int sceIoWaitAsyncCB(int id, u32 address) { u32 error; FileNode *f = kernelObjects.Get < FileNode > (id, error); if (f) { - u64 res = f->asyncResult; - // TODO: Is the close before or after executing the callback? - if (f->closePending) { - kernelObjects.Destroy(id); - } - Memory::Write_U64(res, address); - DEBUG_LOG(HLE, "%i = sceIoWaitAsyncCB(%i, %08x) (HACK)", (u32) res, id, address); hleCheckCurrentCallbacks(); - hleReSchedule(true, "io waited"); + if (f->pendingAsyncResult) { + DEBUG_LOG(HLE, "%lli = sceIoWaitAsyncCB(%i, %08x): waiting", f->asyncResult, id, address); + __KernelWaitCurThread(WAITTYPE_IO, id, address, 0, false, "io waited"); + } else { + DEBUG_LOG(HLE, "%lli = sceIoWaitAsyncCB(%i, %08x)", f->asyncResult, id, address); + Memory::Write_U64((u64) f->asyncResult, address); + + if (f->closePending) { + kernelObjects.Destroy(id); + } + } return 0; //completed } else { ERROR_LOG(HLE, "ERROR - sceIoWaitAsyncCB waiting for invalid id %i", id); @@ -1116,13 +1175,18 @@ u32 sceIoPollAsync(int id, u32 address) { u32 error; FileNode *f = kernelObjects.Get < FileNode > (id, error); if (f) { - u64 res = f->asyncResult; - if (f->closePending) { - kernelObjects.Destroy(id); + if (f->pendingAsyncResult) { + DEBUG_LOG(HLE, "%lli = sceIoPollAsync(%i, %08x): not ready", f->asyncResult, id, address); + return 1; + } else { + DEBUG_LOG(HLE, "%lli = sceIoPollAsync(%i, %08x)", f->asyncResult, id, address); + Memory::Write_U64((u64) f->asyncResult, address); + + if (f->closePending) { + kernelObjects.Destroy(id); + } + return 0; //completed } - Memory::Write_U64(res, address); - DEBUG_LOG(HLE, "%i = sceIoPollAsync(%i, %08x) (HACK)", (u32) res, id, address); - return 0; //completed } else { ERROR_LOG(HLE, "ERROR - sceIoPollAsync waiting for invalid id %i", id); return -1; // TODO: correct error code diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index aec38bb2b896..88ed9c44d968 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -83,6 +83,7 @@ enum WaitType //probably not the real values WAITTYPE_MUTEX = 13, WAITTYPE_LWMUTEX = 14, WAITTYPE_CTRL = 15, + WAITTYPE_IO = 16, }; From feba4215669fc212d2fbf6ee4118fd9216972ffc Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2013 10:16:21 -0700 Subject: [PATCH 07/17] Make sceIoLseekAsync() etc. work better + cleanup. No need to have this logic duplicated. --- Core/HLE/sceIo.cpp | 117 ++++++++++++++++++++++----------------------- 1 file changed, 58 insertions(+), 59 deletions(-) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 41391eabac33..79799ca9cc19 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -374,9 +374,11 @@ void __IoGetStat(SceIoStat *stat, PSPFileInfo &info) { stat->st_private[0] = info.startSector; } -void __IoSchedAsync(SceUID fd, int usec) { +void __IoSchedAsync(FileNode *f, SceUID fd, int usec) { u64 param = ((u64)__KernelGetCurThread()) << 32 | fd; CoreTiming::ScheduleEvent(usToCycles(usec), asyncNotifyEvent, param); + + f->pendingAsyncResult = true; } u32 sceIoGetstat(const char *filename, u32 addr) { @@ -483,9 +485,8 @@ u32 sceIoReadAsync(int id, u32 data_addr, int size) { FileNode *f = kernelObjects.Get < FileNode > (id, error); if (f) { f->asyncResult = __IoRead(id, data_addr, size); - f->pendingAsyncResult = true; // TODO: Not sure what the correct delay is (and technically we shouldn't read into the buffer yet...) - __IoSchedAsync(id, size / 100); + __IoSchedAsync(f, id, size / 100); DEBUG_LOG(HLE, "%llx=sceIoReadAsync(%d, %08x, %x)", f->asyncResult, id, data_addr, size); return 0; } else { @@ -588,12 +589,12 @@ u32 npdrmLseek(FileNode *f, s32 where, FileMove whence) return newPos; } -s64 sceIoLseek(int id, s64 offset, int whence) { +s64 __IoLseek(SceUID id, s64 offset, int whence) { u32 error; - FileNode *f = kernelObjects.Get < FileNode > (id, error); + FileNode *f = kernelObjects.Get(id, error); if (f) { FileMove seek = FILEMOVE_BEGIN; - bool outOfBound = false; + s64 newPos = 0; switch (whence) { case 0: @@ -608,56 +609,72 @@ s64 sceIoLseek(int id, s64 offset, int whence) { seek = FILEMOVE_END; break; } - if(newPos < 0) + // Yes, -1 is the correct return code for this case. + if (newPos < 0) return -1; if(f->npdrm){ - f->asyncResult = npdrmLseek(f, (s32)offset, seek); + return npdrmLseek(f, (s32)offset, seek); }else{ - f->asyncResult = pspFileSystem.SeekFile(f->handle, (s32) offset, seek); + return pspFileSystem.SeekFile(f->handle, (s32) offset, seek); } - DEBUG_LOG(HLE, "%i = sceIoLseek(%d,%i,%i)", (u32) f->asyncResult, id, (int) offset, whence); - return (u32) f->asyncResult; } else { - ERROR_LOG(HLE, "sceIoLseek(%d, %i, %i) - ERROR: invalid file", id, (int) offset, whence); return (s32) error; } } +s64 sceIoLseek(int id, s64 offset, int whence) { + s64 result = __IoLseek(id, offset, whence); + if (result >= 0) { + DEBUG_LOG(HLE, "%lli = sceIoLseek(%d, %llx, %i)", result, id, offset, whence); + return result; + } else { + ERROR_LOG(HLE, "sceIoLseek(%d, %llx, %i) - ERROR: invalid file", id, offset, whence); + return result; + } +} + u32 sceIoLseek32(int id, int offset, int whence) { + s32 result = (s32) __IoLseek(id, offset, whence); + if (result >= 0) { + DEBUG_LOG(HLE, "%lli = sceIoLseek(%d, %x, %i)", result, id, offset, whence); + return result; + } else { + ERROR_LOG(HLE, "sceIoLseek(%d, %x, %i) - ERROR: invalid file", id, offset, whence); + return result; + } +} + +u32 sceIoLseekAsync(int id, s64 offset, int whence) { u32 error; - FileNode *f = kernelObjects.Get < FileNode > (id, error); + FileNode *f = kernelObjects.Get(id, error); if (f) { - FileMove seek = FILEMOVE_BEGIN; - bool outOfBound = false; - s64 newPos = 0; - switch (whence) { - case 0: - newPos = offset; - break; - case 1: - newPos = pspFileSystem.GetSeekPos(f->handle) + offset; - seek = FILEMOVE_CURRENT; - break; - case 2: - newPos = f->info.size + offset; - seek = FILEMOVE_END; - break; - } - if(newPos < 0) - return -1; + f->asyncResult = __IoLseek(id, offset, whence); + // Educated guess at timing. + __IoSchedAsync(f, id, 100); + DEBUG_LOG(HLE, "%lli = sceIoLseekAsync(%d, %llx, %i)", f->asyncResult, id, offset, whence); + return 0; + } else { + ERROR_LOG(HLE, "sceIoLseekAsync(%d, %llx, %i) - ERROR: invalid file", id, offset, whence); + return error; + } + return 0; +} - if(f->npdrm){ - f->asyncResult = npdrmLseek(f, (s32)offset, seek); - }else{ - f->asyncResult = pspFileSystem.SeekFile(f->handle, (s32) offset, seek); - } - DEBUG_LOG(HLE, "%i = sceIoLseek32(%d,%i,%i)", (u32) f->asyncResult, id, (int) offset, whence); - return (u32) f->asyncResult; +u32 sceIoLseek32Async(int id, int offset, int whence) { + u32 error; + FileNode *f = kernelObjects.Get(id, error); + if (f) { + f->asyncResult = __IoLseek(id, offset, whence); + // Educated guess at timing. + __IoSchedAsync(f, id, 100); + DEBUG_LOG(HLE, "%lli = sceIoLseek32Async(%d, %x, %i)", f->asyncResult, id, offset, whence); + return 0; } else { - ERROR_LOG(HLE, "sceIoLseek32(%d, %i, %i) - ERROR: invalid file", id, (int) offset, whence); + ERROR_LOG(HLE, "sceIoLseek32Async(%d, %x, %i) - ERROR: invalid file", id, offset, whence); return error; } + return 0; } FileNode *__IoOpen(const char* filename, int flags, int mode) { @@ -1019,23 +1036,14 @@ int sceIoCloseAsync(int id) { f->closePending = true; f->asyncResult = 0; - f->pendingAsyncResult = true; // TODO: Rough estimate. - __IoSchedAsync(id, 100); + __IoSchedAsync(f, id, 100); return 0; } else return error; } -u32 sceIoLseekAsync(int id, s64 offset, int whence) -{ - DEBUG_LOG(HLE, "sceIoLseekAsync(%d) sorta implemented", id); - sceIoLseek(id, offset, whence); - __IoCompleteAsyncIO(id); - return 0; -} - u32 sceIoSetAsyncCallback(int id, u32 clbckId, u32 clbckArg) { DEBUG_LOG(HLE, "sceIoSetAsyncCallback(%d, %i, %08x)", id, clbckId, clbckArg); @@ -1055,14 +1063,6 @@ u32 sceIoSetAsyncCallback(int id, u32 clbckId, u32 clbckArg) } } -u32 sceIoLseek32Async(int id, int offset, int whence) -{ - DEBUG_LOG(HLE, "sceIoLseek32Async(%d) sorta implemented", id); - sceIoLseek32(id, offset, whence); - __IoCompleteAsyncIO(id); - return 0; -} - u32 sceIoOpenAsync(const char *filename, int flags, int mode) { FileNode *f = __IoOpen(filename, flags, mode); @@ -1086,10 +1086,9 @@ u32 sceIoOpenAsync(const char *filename, int flags, int mode) DEBUG_LOG(HLE, "%x=sceIoOpenAsync(%s, %08x, %08x)", fd, filename, flags, mode); } - f->pendingAsyncResult = true; // TODO: Timing is very inconsistent. From ms0, 10ms - 20ms depending on filesize/dir depth? From umd, can take > 1s. // For now let's aim low. - __IoSchedAsync(fd, 100); + __IoSchedAsync(f, fd, 100); return fd; } From 0b9c2488563bbe364fce82edb2065cbbf759709e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2013 10:59:59 -0700 Subject: [PATCH 08/17] Reschedule in sceIoLseek() etc. Also make it so we can return u64s easily in places... --- Core/HLE/HLE.cpp | 10 +++++++- Core/HLE/HLE.h | 11 ++++++++ Core/HLE/sceIo.cpp | 6 +++-- Core/HLE/sceKernelThread.cpp | 49 ++++++++++++++++++++++++++++++++++-- Core/HLE/sceKernelThread.h | 22 +++++++++++++++- 5 files changed, 92 insertions(+), 6 deletions(-) diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp index 4930f9272398..4dd0d92e717b 100644 --- a/Core/HLE/HLE.cpp +++ b/Core/HLE/HLE.cpp @@ -65,7 +65,7 @@ void hleDelayResultFinish(u64 userdata, int cycleslate) u32 error; SceUID threadID = (SceUID) userdata; SceUID verify = __KernelGetWaitID(threadID, WAITTYPE_DELAY, error); - SceUID result = __KernelGetWaitValue(threadID, error); + u64 result = (userdata ^ threadID) | __KernelGetWaitValue(threadID, error); if (error == 0 && verify == 1) __KernelResumeThreadFromWait(threadID, result); @@ -335,6 +335,14 @@ u32 hleDelayResult(u32 result, const char *reason, int usec) return result; } +u64 hleDelayResult(u64 result, const char *reason, int usec) +{ + u64 param = (result & 0xFFFFFFFF00000000) | __KernelGetCurThread(); + CoreTiming::ScheduleEvent(usToCycles(usec), delayedResultEvent, param); + __KernelWaitCurThread(WAITTYPE_DELAY, 1, (u32) result, 0, false, reason); + return result; +} + void hleEatMicro(int usec) { // Maybe this should Idle, at least for larger delays? Could that cause issues? diff --git a/Core/HLE/HLE.h b/Core/HLE/HLE.h index 2618eefcfb68..2f51492e1b84 100644 --- a/Core/HLE/HLE.h +++ b/Core/HLE/HLE.h @@ -89,8 +89,19 @@ void hleDebugBreak(); // Delays the result for usec microseconds, allowing other threads to run during this time. u32 hleDelayResult(u32 result, const char *reason, int usec); +u64 hleDelayResult(u64 result, const char *reason, int usec); void hleEatMicro(int usec); +inline int hleDelayResult(int result, const char *reason, int usec) +{ + return hleDelayResult((u32) result, reason, usec); +} + +inline s64 hleDelayResult(s64 result, const char *reason, int usec) +{ + return hleDelayResult((u64) result, reason, usec); +} + void HLEInit(); void HLEDoState(PointerWrap &p); void HLEShutdown(); diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 79799ca9cc19..5ea70936f3fc 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -627,7 +627,8 @@ s64 sceIoLseek(int id, s64 offset, int whence) { s64 result = __IoLseek(id, offset, whence); if (result >= 0) { DEBUG_LOG(HLE, "%lli = sceIoLseek(%d, %llx, %i)", result, id, offset, whence); - return result; + // Educated guess at timing. + return hleDelayResult(result, "io seek", 100); } else { ERROR_LOG(HLE, "sceIoLseek(%d, %llx, %i) - ERROR: invalid file", id, offset, whence); return result; @@ -638,7 +639,8 @@ u32 sceIoLseek32(int id, int offset, int whence) { s32 result = (s32) __IoLseek(id, offset, whence); if (result >= 0) { DEBUG_LOG(HLE, "%lli = sceIoLseek(%d, %x, %i)", result, id, offset, whence); - return result; + // Educated guess at timing. + return hleDelayResult(result, "io seek", 100); } else { ERROR_LOG(HLE, "sceIoLseek(%d, %x, %i) - ERROR: invalid file", id, offset, whence); return result; diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index b92f792eef5d..23e2bb5d95ec 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -372,6 +372,7 @@ class Thread : public KernelObject ActionAfterMipsCall *getRunningCallbackAction(); void setReturnValue(u32 retval); + void setReturnValue(u64 retval); void resumeFromWait(); bool isWaitingFor(WaitType type, int id); int getWaitID(WaitType type); @@ -588,6 +589,12 @@ void MipsCall::setReturnValue(u32 value) savedV0 = value; } +void MipsCall::setReturnValue(u64 value) +{ + savedV0 = value & 0xFFFFFFFF; + savedV1 = (value >> 32) & 0xFFFFFFFF; +} + // TODO: Should move to this wrapper so we can keep the current thread as a SceUID instead // of a dangerous raw pointer. Thread *__GetCurrentThread() { @@ -1058,7 +1065,24 @@ u32 __KernelResumeThreadFromWait(SceUID threadID) } } -u32 __KernelResumeThreadFromWait(SceUID threadID, int retval) +u32 __KernelResumeThreadFromWait(SceUID threadID, u32 retval) +{ + u32 error; + Thread *t = kernelObjects.Get(threadID, error); + if (t) + { + t->resumeFromWait(); + t->setReturnValue(retval); + return 0; + } + else + { + ERROR_LOG(HLE, "__KernelResumeThreadFromWait(%d): bad thread: %08x", threadID, error); + return error; + } +} + +u32 __KernelResumeThreadFromWait(SceUID threadID, u64 retval) { u32 error; Thread *t = kernelObjects.Get(threadID, error); @@ -1091,7 +1115,7 @@ bool __KernelTriggerWait(WaitType type, int id, bool useRetVal, int retVal, cons // This thread was waiting for the triggered object. t->resumeFromWait(); if (useRetVal) - t->setReturnValue(retVal); + t->setReturnValue((u32)retVal); doneAnything = true; } } @@ -2279,6 +2303,27 @@ void Thread::setReturnValue(u32 retval) } } +void Thread::setReturnValue(u64 retval) +{ + if (this->GetUID() == currentThread) { + if (g_inCbCount) { + u32 callId = this->currentCallbackId; + MipsCall *call = mipsCalls.get(callId); + if (call) { + call->setReturnValue(retval); + } else { + ERROR_LOG(HLE, "Failed to inject return value %08llx in thread", retval); + } + } else { + currentMIPS->r[2] = retval & 0xFFFFFFFF; + currentMIPS->r[3] = (retval >> 32) & 0xFFFFFFFF; + } + } else { + context.r[2] = retval & 0xFFFFFFFF; + context.r[3] = (retval >> 32) & 0xFFFFFFFF; + } +} + void Thread::resumeFromWait() { // Do we need to "inject" it? diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 88ed9c44d968..07883a757076 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -127,7 +127,18 @@ void __KernelLoadContext(ThreadContext *ctx); bool __KernelTriggerWait(WaitType type, int id, const char *reason, bool dontSwitch = false); bool __KernelTriggerWait(WaitType type, int id, int retVal, const char *reason, bool dontSwitch); u32 __KernelResumeThreadFromWait(SceUID threadID); // can return an error value -u32 __KernelResumeThreadFromWait(SceUID threadID, int retval); +u32 __KernelResumeThreadFromWait(SceUID threadID, u32 retval); +u32 __KernelResumeThreadFromWait(SceUID threadID, u64 retval); + +inline u32 __KernelResumeThreadFromWait(SceUID threadID, int retval) +{ + return __KernelResumeThreadFromWait(threadID, (u32)retval); +} + +inline u32 __KernelResumeThreadFromWait(SceUID threadID, s64 retval) +{ + return __KernelResumeThreadFromWait(threadID, (u64)retval); +} u32 __KernelGetWaitValue(SceUID threadID, u32 &error); u32 __KernelGetWaitTimeoutPtr(SceUID threadID, u32 &error); @@ -231,6 +242,15 @@ struct MipsCall { void DoState(PointerWrap &p); void setReturnValue(u32 value); + void setReturnValue(u64 value); + inline void setReturnValue(int value) + { + setReturnValue((u32)value); + } + inline void setReturnValue(s64 value) + { + setReturnValue((u64)value); + } }; class Action From 70cbe30e45f8621cd63806b1442dfa703119a262 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2013 15:03:24 -0700 Subject: [PATCH 09/17] Clean up sceIoIoctl() async and add delay. A low estimate based on tests. --- Core/HLE/sceIo.cpp | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 5ea70936f3fc..c37ca771295b 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -26,6 +26,7 @@ #include "../MIPS/MIPS.h" #include "../HW/MemoryStick.h" #include "Core/CoreTiming.h" +#include "Core/Reporting.h" #include "../FileSystems/FileSystem.h" #include "../FileSystems/MetaFileSystem.h" @@ -1272,11 +1273,12 @@ u32 sceIoDclose(int id) { return kernelObjects.Destroy(id); } -u32 sceIoIoctl(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen) +int __IoIoctl(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen) { u32 error; FileNode *f = kernelObjects.Get(id, error); if (error) { + ERROR_LOG(HLE, "UNIMPL %08x=sceIoIoctl id: %08x, cmd %08x, bad file", error, id, cmd); return error; } @@ -1302,7 +1304,6 @@ u32 sceIoIoctl(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 ou f->npdrm = false; pspFileSystem.SeekFile(f->handle, (s32)0, FILEMOVE_BEGIN); } - f->asyncResult = 0; } break; @@ -1339,19 +1340,34 @@ u32 sceIoIoctl(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 ou break; default: + Reporting::ReportMessage("sceIoIoctl(%s, %08x, %x, %08x, %x)", f->fullpath.c_str(), indataPtr, inlen, outdataPtr, outlen); ERROR_LOG(HLE, "UNIMPL 0=sceIoIoctl id: %08x, cmd %08x, indataPtr %08x, inlen %08x, outdataPtr %08x, outLen %08x", id,cmd,indataPtr,inlen,outdataPtr,outlen); break; } - return 0; + return 0; +} + +u32 sceIoIoctl(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen) +{ + int result = __IoIoctl(id, cmd, indataPtr, inlen, outdataPtr, outlen); + // Just a low estimate on timing. + return hleDelayResult(0, "io ctrl command", 100); } u32 sceIoIoctlAsync(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen) { - DEBUG_LOG(HLE, "sceIoIoctlAsync(%08x, %08x, %08x, %08x, %08x, %08x)", id, cmd, indataPtr, inlen, outdataPtr, outlen); - sceIoIoctl(id, cmd, indataPtr, inlen, outdataPtr, outlen); - __IoCompleteAsyncIO(id); - return 0; + u32 error; + FileNode *f = kernelObjects.Get < FileNode > (id, error); + if (f) { + DEBUG_LOG(HLE, "sceIoIoctlAsync(%08x, %08x, %08x, %08x, %08x, %08x)", id, cmd, indataPtr, inlen, outdataPtr, outlen); + f->asyncResult = __IoIoctl(id, cmd, indataPtr, inlen, outdataPtr, outlen); + __IoSchedAsync(f, id, 100); + return 0; + } else { + ERROR_LOG(HLE, "UNIMPL %08x=sceIoIoctl id: %08x, cmd %08x, bad file", error, id, cmd); + return error; + } } KernelObject *__KernelFileNodeObject() { From 4a18ace1aac2dc267bb15b42ce5165912a615d92 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2013 16:44:47 -0700 Subject: [PATCH 10/17] Cleanup some sceIoDevctl() umd commands. Based on tests. --- Core/HLE/sceIo.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index c37ca771295b..32fc0782ab52 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -780,22 +780,23 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr, // UMD checks switch (cmd) { case 0x01F20001: // Get Disc Type. - if (Memory::IsValidAddress(outPtr)) { - Memory::Write_U32(0x10, outPtr); // Game disc + if (Memory::IsValidAddress(outPtr + 4)) { + Memory::Write_U32(0x10, outPtr + 4); // Game disc return 0; - } else { - return -1; } - break; - case 0x01F20002: // Get current LBA. + return -1; + case 0x01F20002: // Get last sector number. + case 0x01F20003: // Seems identical? if (Memory::IsValidAddress(outPtr)) { - Memory::Write_U32(0, outPtr); // Game disc + PSPFileInfo info = pspFileSystem.GetFileInfo("umd1:"); + Memory::Write_U32((u32) (info.size / 2048) - 1, outPtr); return 0; - } else { - return -1; } - break; + return -1; case 0x01F100A3: // Seek + // Timing is just a rough guess, probably takes longer. + return hleDelayResult(0, "dev seek", 100); + case 0x01F100A4: // Cache return 0; } From f8d9846179854153a468acdfa219a7c01f74b1e7 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2013 17:08:12 -0700 Subject: [PATCH 11/17] Add a few more umd devctl commands. With help from jpcsp and testing. --- Core/HLE/sceIo.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 32fc0782ab52..c49c75fb7673 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -798,6 +798,20 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr, return hleDelayResult(0, "dev seek", 100); case 0x01F100A4: // Cache return 0; + case 0x01F300A5: // Cache + status + if (Memory::IsValidAddress(outPtr)) { + Memory::Write_U32(1, outPtr); + return 0; + } + return -1; + // TODO: What does these do? Seem to require a u32 in, no output. + case 0x01F100A6: + case 0x01F100A8: + case 0x01F100A9: + case 0x01F300A7: + Reporting::ReportMessage("sceIoDevctl(\"%s\", %08x, %08x, %i, %08x, %i)", name, cmd, argAddr, argLen, outPtr, outLen); + ERROR_LOG(HLE, "UNIMPL sceIoDevctl(\"%s\", %08x, %08x, %i, %08x, %i)", name, cmd, argAddr, argLen, outPtr, outLen); + return 0; } // This should really send it on to a FileSystem implementation instead. From fff8d0c7c410135d6e1618ef87a78c55fdfdde58 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2013 18:00:15 -0700 Subject: [PATCH 12/17] Add a memory stick devctl to check writable. Final Fantasy 4 seemed to want this. --- Core/HLE/sceIo.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index c49c75fb7673..b7fe7bc96206 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -883,6 +883,16 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr, ERROR_LOG(HLE, "memstick size query: bad params"); return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS; } + + case 0x02425824: // Check if write protected + if (Memory::IsValidAddress(outPtr) && outLen == 4) { + Memory::Write_U32(0, outPtr); + hleDebugBreak(); + return 0; + } else { + ERROR_LOG(HLE, "Failed 0x02425824 fat"); + return -1; + } } } @@ -932,6 +942,16 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr, } break; + case 0x02425824: // Check if write protected + if (Memory::IsValidAddress(outPtr) && outLen == 4) { + Memory::Write_U32(0, outPtr); + return 0; + } else { + ERROR_LOG(HLE, "Failed 0x02425824 fat"); + return -1; + } + break; + case 0x02425818: // Get memstick size etc // Pretend we have a 2GB memory stick. { From 0fe7f3e8d3cc89942d4f4bb6d86f7f8d9091fb9d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2013 18:57:17 -0700 Subject: [PATCH 13/17] Add delays for open, close, and getstat. Actual delays for these seem much higher, but not sure. --- Core/HLE/sceIo.cpp | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index b7fe7bc96206..84767f5a020e 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -383,6 +383,9 @@ void __IoSchedAsync(FileNode *f, SceUID fd, int usec) { } u32 sceIoGetstat(const char *filename, u32 addr) { + // TODO: Improve timing (although this seems normally slow..) + int usec = 1000; + SceIoStat stat; PSPFileInfo info = pspFileSystem.GetFileInfo(filename); if (info.exists) { @@ -390,14 +393,14 @@ u32 sceIoGetstat(const char *filename, u32 addr) { if (Memory::IsValidAddress(addr)) { Memory::WriteStruct(addr, &stat); DEBUG_LOG(HLE, "sceIoGetstat(%s, %08x) : sector = %08x", filename, addr, info.startSector); - return 0; + return hleDelayResult(0, "io getstat", usec); } else { ERROR_LOG(HLE, "sceIoGetstat(%s, %08x) : bad address", filename, addr); - return -1; + return hleDelayResult(-1, "io getstat", usec); } } else { DEBUG_LOG(HLE, "sceIoGetstat(%s, %08x) : FILE NOT FOUND", filename, addr); - return ERROR_ERRNO_FILE_NOT_FOUND; + return hleDelayResult(ERROR_ERRNO_FILE_NOT_FOUND, "io getstat", usec); } } @@ -715,22 +718,25 @@ u32 sceIoOpen(const char* filename, int flags, int mode) { FileNode *f = __IoOpen(filename, flags, mode); if (f == NULL) { ERROR_LOG(HLE, "ERROR_ERRNO_FILE_NOT_FOUND=sceIoOpen(%s, %08x, %08x) - file not found", filename, flags, mode); - return ERROR_ERRNO_FILE_NOT_FOUND; + // Timing is not accurate, aiming low for now. + return hleDelayResult(ERROR_ERRNO_FILE_NOT_FOUND, "file opened", 100); } SceUID id = f->GetUID(); DEBUG_LOG(HLE, "%i=sceIoOpen(%s, %08x, %08x)", id, filename, flags, mode); - return id; + // Timing is not accurate, aiming low for now. + return hleDelayResult(id, "file opened", 100); } u32 sceIoClose(int id) { u32 error; DEBUG_LOG(HLE, "sceIoClose(%d)", id); - FileNode *f = kernelObjects.Get < FileNode > (id, error); + FileNode *f = kernelObjects.Get(id, error); if(f && f->npdrm){ pgd_close(f->pgdInfo); } - return kernelObjects.Destroy < FileNode > (id); + // Timing is not accurate, aiming low for now. + return hleDelayResult(kernelObjects.Destroy(id), "file closed", 100); } u32 sceIoRemove(const char *filename) { @@ -1058,13 +1064,6 @@ int sceIoChangeAsyncPriority(int id, int priority) return 0; } -u32 __IoClose(SceUID actedFd, int closedFd) -{ - DEBUG_LOG(HLE, "Deferred IoClose(%d, %d)", actedFd, closedFd); - __IoCompleteAsyncIO(closedFd); - return kernelObjects.Destroy < FileNode > (closedFd); -} - int sceIoCloseAsync(int id) { DEBUG_LOG(HLE, "sceIoCloseAsync(%d)", id); From 7726b95037fb8a44b61f206ff9146b5a1364a233 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2013 19:14:46 -0700 Subject: [PATCH 14/17] Reschedule / async in sceIoWrite as well. --- Core/HLE/sceIo.cpp | 61 ++++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 84767f5a020e..05a983fd6839 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -46,11 +46,10 @@ extern "C" { // For headless screenshots. #include "sceDisplay.h" -const int ERROR_ERRNO_FILE_NOT_FOUND = 0x80010002; - -#define ERROR_MEMSTICK_DEVCTL_BAD_PARAMS 0x80220081 -#define ERROR_MEMSTICK_DEVCTL_TOO_MANY_CALLBACKS 0x80220082 -#define ERROR_KERNEL_BAD_FILE_DESCRIPTOR 0x80020323 +const int ERROR_ERRNO_FILE_NOT_FOUND = 0x80010002; +const int ERROR_MEMSTICK_DEVCTL_BAD_PARAMS = 0x80220081; +const int ERROR_MEMSTICK_DEVCTL_TOO_MANY_CALLBACKS = 0x80220082; +const int ERROR_KERNEL_BAD_FILE_DESCRIPTOR = 0x80020323; #define PSP_DEV_TYPE_ALIAS 0x20 @@ -440,7 +439,6 @@ u32 npdrmRead(FileNode *f, u8 *data, int size) { return size; } -//Not sure about wrapping it or not, since the log seems to take the address of the data var int __IoRead(int id, u32 data_addr, int size) { if (id == 3) { DEBUG_LOG(HLE, "sceIoRead STDIN"); @@ -459,7 +457,7 @@ int __IoRead(int id, u32 data_addr, int size) { if(f->npdrm){ return npdrmRead(f, data, size); }else{ - return pspFileSystem.ReadFile(f->handle, data, size); + return (int) pspFileSystem.ReadFile(f->handle, data, size); } } else { ERROR_LOG(HLE, "sceIoRead Reading into bad pointer %08x", data_addr); @@ -499,8 +497,7 @@ u32 sceIoReadAsync(int id, u32 data_addr, int size) { } } -u32 sceIoWrite(int id, void *data_ptr, int size) -{ +int __IoWrite(int id, void *data_ptr, int size) { if (id == 2) { //stderr! const char *str = (const char*) data_ptr; @@ -518,28 +515,40 @@ u32 sceIoWrite(int id, void *data_ptr, int size) u32 error; FileNode *f = kernelObjects.Get < FileNode > (id, error); if (f) { - if(!(f->openMode & FILEACCESS_WRITE)) - { + if(!(f->openMode & FILEACCESS_WRITE)) { return ERROR_KERNEL_BAD_FILE_DESCRIPTOR; } - else - { - u8 *data = (u8*) data_ptr; - f->asyncResult = pspFileSystem.WriteFile(f->handle, data, size); - return (u32) f->asyncResult; - } + return (int) pspFileSystem.WriteFile(f->handle, (u8*) data_ptr, size); } else { ERROR_LOG(HLE, "sceIoWrite ERROR: no file open"); - return error; + return (s32) error; } } -u32 sceIoWriteAsync(int id, void *data_ptr, int size) -{ - DEBUG_LOG(HLE, "sceIoWriteAsync(%d)", id); - sceIoWrite(id, data_ptr, size); - __IoCompleteAsyncIO(id); - return 0; +u32 sceIoWrite(int id, u32 data_addr, int size) { + int result = __IoWrite(id, Memory::GetPointer(data_addr), size); + if (result >= 0) { + DEBUG_LOG(HLE, "%x=sceIoWrite(%d, %08x, %x)", result, id, data_addr, size); + // TODO: Timing is probably not very accurate, low estimate. + return hleDelayResult(result, "io write", result / 100); + } + else + return result; +} + +u32 sceIoWriteAsync(int id, u32 data_addr, int size) { + u32 error; + FileNode *f = kernelObjects.Get < FileNode > (id, error); + if (f) { + f->asyncResult = __IoWrite(id, Memory::GetPointer(data_addr), size); + // TODO: Not sure what the correct delay is (and technically we shouldn't read into the buffer yet...) + __IoSchedAsync(f, id, size / 100); + DEBUG_LOG(HLE, "%llx=sceIoWriteAsync(%d, %08x, %x)", f->asyncResult, id, data_addr, size); + return 0; + } else { + ERROR_LOG(HLE, "sceIoWriteAsync: bad file %d", id); + return error; + } } u32 sceIoGetDevType(int id) @@ -1445,8 +1454,8 @@ const HLEFunction IoFileMgrForUser[] = { { 0xA12A0514, &WrapU_IUU, "sceIoSetAsyncCallback" }, { 0xab96437f, &WrapU_CI, "sceIoSync" }, { 0x6d08a871, &WrapU_C, "sceIoUnassign" }, - { 0x42EC03AC, &WrapU_IVI, "sceIoWrite" }, //(int fd, void *data, int size); - { 0x0facab19, &WrapU_IVI, "sceIoWriteAsync" }, + { 0x42EC03AC, &WrapU_IUI, "sceIoWrite" }, //(int fd, void *data, int size); + { 0x0facab19, &WrapU_IUI, "sceIoWriteAsync" }, { 0x35dbd746, &WrapI_IU, "sceIoWaitAsyncCB" }, { 0xe23eec33, &WrapI_IU, "sceIoWaitAsync" }, }; From 2b62bf51812cd5cb46b688e06c8955455149e338 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2013 19:26:49 -0700 Subject: [PATCH 15/17] Add io delays to some misc io funcs. --- Core/HLE/sceIo.cpp | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 05a983fd6839..45ac7d617c71 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -47,6 +47,7 @@ extern "C" { #include "sceDisplay.h" const int ERROR_ERRNO_FILE_NOT_FOUND = 0x80010002; +const int ERROR_ERRNO_FILE_ALREADY_EXISTS = 0x80010011; const int ERROR_MEMSTICK_DEVCTL_BAD_PARAMS = 0x80220081; const int ERROR_MEMSTICK_DEVCTL_TOO_MANY_CALLBACKS = 0x80220082; const int ERROR_KERNEL_BAD_FILE_DESCRIPTOR = 0x80020323; @@ -751,27 +752,30 @@ u32 sceIoClose(int id) { u32 sceIoRemove(const char *filename) { DEBUG_LOG(HLE, "sceIoRemove(%s)", filename); + // TODO: This timing isn't necessarily accurate, low end for now. if(!pspFileSystem.GetFileInfo(filename).exists) - return ERROR_ERRNO_FILE_NOT_FOUND; + return hleDelayResult(ERROR_ERRNO_FILE_NOT_FOUND, "file removed", 100); pspFileSystem.RemoveFile(filename); - return 0; + return hleDelayResult(0, "file removed", 100); } u32 sceIoMkdir(const char *dirname, int mode) { DEBUG_LOG(HLE, "sceIoMkdir(%s, %i)", dirname, mode); + // TODO: Improve timing. if (pspFileSystem.MkDir(dirname)) - return 0; + return hleDelayResult(0, "mkdir", 1000); else - return -1; + return hleDelayResult(ERROR_ERRNO_FILE_ALREADY_EXISTS, "mkdir", 1000); } u32 sceIoRmdir(const char *dirname) { DEBUG_LOG(HLE, "sceIoRmdir(%s)", dirname); + // TODO: Improve timing. if (pspFileSystem.RmDir(dirname)) - return 0; + return hleDelayResult(0, "rmdir", 1000); else - return -1; + return hleDelayResult(ERROR_ERRNO_FILE_NOT_FOUND, "rmdir", 1000); } u32 sceIoSync(const char *devicename, int flag) { @@ -1053,12 +1057,13 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr, u32 sceIoRename(const char *from, const char *to) { DEBUG_LOG(HLE, "sceIoRename(%s, %s)", from, to); - if(!pspFileSystem.GetFileInfo(from).exists) - return ERROR_ERRNO_FILE_NOT_FOUND; + // TODO: Timing isn't terribly accurate. + if (!pspFileSystem.GetFileInfo(from).exists) + return hleDelayResult(ERROR_ERRNO_FILE_NOT_FOUND, "file renamed", 1000); - if(!pspFileSystem.RenameFile(from, to)) - WARN_LOG(HLE, "Could not move %s to %s\n",from, to); - return 0; + if (!pspFileSystem.RenameFile(from, to)) + WARN_LOG(HLE, "Could not move %s to %s", from, to); + return hleDelayResult(0, "file renamed", 1000); } u32 sceIoChdir(const char *dirname) { @@ -1098,7 +1103,6 @@ u32 sceIoSetAsyncCallback(int id, u32 clbckId, u32 clbckArg) FileNode *f = kernelObjects.Get < FileNode > (id, error); if (f) { - // TODO: Check replacing / updating? f->callbackID = clbckId; f->callbackArg = clbckArg; return 0; From f1e324da47305f6f22bdd319e7a1884c32632025 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2013 19:59:26 -0700 Subject: [PATCH 16/17] Add delays to sceIoDread(), which needs them. Only the first time though. --- Core/HLE/sceIo.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 45ac7d617c71..da60faa30713 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -1271,7 +1271,6 @@ class DirListing : public KernelObject { u32 sceIoDopen(const char *path) { DEBUG_LOG(HLE, "sceIoDopen(\"%s\")", path); - if(!pspFileSystem.GetFileInfo(path).exists) { return ERROR_ERRNO_FILE_NOT_FOUND; @@ -1284,6 +1283,7 @@ u32 sceIoDopen(const char *path) { dir->index = 0; dir->name = std::string(path); + // TODO: The result is delayed only from the memstick, it seems. return id; } @@ -1307,7 +1307,10 @@ u32 sceIoDread(int id, u32 dirent_addr) { entry->d_private = 0xC0DEBABE; DEBUG_LOG(HLE, "sceIoDread( %d %08x ) = %s", id, dirent_addr, entry->d_name); - dir->index++; + // TODO: Improve timing. Only happens on the *first* entry read, ms and umd. + if (dir->index++ == 0) { + return hleDelayResult(1, "readdir", 1000); + } return 1; } else { DEBUG_LOG(HLE, "sceIoDread - invalid listing %i, error %08x", id, error); From 26306342fd93c3b6edf2a4ea9445648e396d0ea4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 11 Mar 2013 02:52:15 -0700 Subject: [PATCH 17/17] Simplify hleDelayResult()'s handler a bit. --- Core/HLE/HLE.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp index 4dd0d92e717b..35196005560e 100644 --- a/Core/HLE/HLE.cpp +++ b/Core/HLE/HLE.cpp @@ -65,7 +65,9 @@ void hleDelayResultFinish(u64 userdata, int cycleslate) u32 error; SceUID threadID = (SceUID) userdata; SceUID verify = __KernelGetWaitID(threadID, WAITTYPE_DELAY, error); - u64 result = (userdata ^ threadID) | __KernelGetWaitValue(threadID, error); + // The top 32 bits of userdata are the top 32 bits of the 64 bit result. + // We can't just put it all in userdata because we need to know the threadID... + u64 result = (userdata & 0xFFFFFFFF00000000ULL) | __KernelGetWaitValue(threadID, error); if (error == 0 && verify == 1) __KernelResumeThreadFromWait(threadID, result);