From 9e488e8be43189f5af28f40c0fb1678dc61547d2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 5 Aug 2016 20:42:58 -0700 Subject: [PATCH] Io: Correct some memstick devctl accuracy issues. This makes it pass the right values in a few more cases, and unregister callbacks properly. Also handles duplicate callbacks. --- Core/HLE/sceIo.cpp | 173 +++++++++++++++++++++++++++++----------- Core/HW/MemoryStick.cpp | 2 +- Core/HW/MemoryStick.h | 19 +++-- 3 files changed, 139 insertions(+), 55 deletions(-) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index ebeaeaaf93..23b4f8c9eb 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -34,6 +34,7 @@ #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" #include "Core/HLE/sceKernel.h" +#include "Core/HLE/sceUmd.h" #include "Core/MIPS/MIPS.h" #include "Core/HW/MemoryStick.h" #include "Core/HW/AsyncIOManager.h" @@ -117,8 +118,8 @@ const int PSP_STDIN = 3; static int asyncNotifyEvent = -1; static int syncNotifyEvent = -1; static SceUID fds[PSP_COUNT_FDS]; -static std::set memStickCallbacks; -static std::set memStickFatCallbacks; +static std::vector memStickCallbacks; +static std::vector memStickFatCallbacks; static AsyncIOManager ioManager; static bool ioManagerThreadEnabled = false; static std::thread *ioManagerThread; @@ -547,7 +548,7 @@ void __IoInit() { } void __IoDoState(PointerWrap &p) { - auto s = p.Section("sceIo", 1); + auto s = p.Section("sceIo", 1, 2); if (!s) return; @@ -557,8 +558,24 @@ void __IoDoState(PointerWrap &p) { CoreTiming::RestoreRegisterEvent(asyncNotifyEvent, "IoAsyncNotify", __IoAsyncNotify); p.Do(syncNotifyEvent); CoreTiming::RestoreRegisterEvent(syncNotifyEvent, "IoSyncNotify", __IoSyncNotify); - p.Do(memStickCallbacks); - p.Do(memStickFatCallbacks); + if (s < 2) { + std::set legacy; + memStickCallbacks.clear(); + memStickFatCallbacks.clear(); + + // Convert from set to vector. + p.Do(legacy); + for (SceUID id : legacy) { + memStickCallbacks.push_back(id); + } + p.Do(legacy); + for (SceUID id : legacy) { + memStickFatCallbacks.push_back(id); + } + } else { + p.Do(memStickCallbacks); + p.Do(memStickFatCallbacks); + } } void __IoShutdown() { @@ -1471,45 +1488,69 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o if (!strcmp(name, "mscmhc0:") || !strcmp(name, "ms0:") || !strcmp(name, "memstick:")) { - // MemorySticks Checks + // MemoryStick checks switch (cmd) { case 0x02025801: - // Check the MemoryStick's driver status (mscmhc0). - if (Memory::IsValidAddress(outPtr)) { - Memory::Write_U32(4, outPtr); // JPSCP: The right return value is 4 for some reason + // Check the MemoryStick's driver status (mscmhc0: only.) + if (Memory::IsValidAddress(outPtr) && outLen >= 4) { + if (MemoryStick_State() == PSP_MEMORYSTICK_STATE_INSERTED) { + // 1 = not inserted (ready), 4 = inserted + Memory::Write_U32(PSP_MEMORYSTICK_STATE_DEVICE_INSERTED, outPtr); + } else { + Memory::Write_U32(PSP_MEMORYSTICK_STATE_DRIVER_READY, outPtr); + } return 0; } else { return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS; } break; - case 0x02015804: + case 0x02015804: // Register MemoryStick's insert/eject callback (mscmhc0) - if (Memory::IsValidAddress(argAddr) && argLen == 4) { - // TODO: Verify how duplicates work / how many are allowed. + if (Memory::IsValidAddress(argAddr) && outPtr == 0 && argLen >= 4) { u32 cbId = Memory::Read_U32(argAddr); - if (memStickCallbacks.find(cbId) == memStickCallbacks.end()) { - memStickCallbacks.insert(cbId); - DEBUG_LOG(SCEIO, "sceIoDevCtl: Memstick callback %i registered, notifying immediately.", cbId); - __KernelNotifyCallback(cbId, MemoryStick_State()); + int type = -1; + kernelObjects.GetIDType(cbId, &type); + + if (memStickCallbacks.size() < 32 && type == SCE_KERNEL_TMID_Callback) { + memStickCallbacks.push_back(cbId); + if (MemoryStick_State() == PSP_MEMORYSTICK_STATE_INSERTED) { + // Only fired immediately if the card is currently inserted. + // Values observed: + // * 1 = Memory stick inserted + // * 2 = Memory stick removed + // * 4 = Memory stick mounting? (followed by a 1 about 500ms later) + DEBUG_LOG(SCEIO, "sceIoDevctl: Memstick callback %i registered, notifying immediately", cbId); + __KernelNotifyCallback(cbId, MemoryStick_State()); + } else { + DEBUG_LOG(SCEIO, "sceIoDevctl: Memstick callback %i registered", cbId); + } return 0; } else { - return ERROR_MEMSTICK_DEVCTL_TOO_MANY_CALLBACKS; + return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT; } } else { return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS; } break; - case 0x02025805: + case 0x02015805: // Unregister MemoryStick's insert/eject callback (mscmhc0) - if (Memory::IsValidAddress(argAddr) && argLen == 4) { - // TODO: Verify how duplicates work / how many are allowed. + if (Memory::IsValidAddress(argAddr) && argLen >= 4) { u32 cbId = Memory::Read_U32(argAddr); - if (memStickCallbacks.find(cbId) != memStickCallbacks.end()) { - memStickCallbacks.erase(cbId); - DEBUG_LOG(SCEIO, "sceIoDevCtl: Unregistered memstick callback %i", cbId); + size_t slot = (size_t)-1; + // We want to only remove one at a time. + for (size_t i = 0; i < memStickCallbacks.size(); ++i) { + if (memStickCallbacks[i] == cbId) { + slot = i; + break; + } + } + + if (slot != (size_t)-1) { + memStickCallbacks.erase(memStickCallbacks.begin() + slot); + DEBUG_LOG(SCEIO, "sceIoDevctl: Unregistered memstick callback %i", cbId); return 0; } else { - return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS; + return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT; } } else { return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS; @@ -1517,18 +1558,21 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o break; case 0x02025806: // Check if the device is inserted (mscmhc0) - if (Memory::IsValidAddress(outPtr)) { - // 0 = Not inserted. + if (Memory::IsValidAddress(outPtr) && outLen >= 4) { // 1 = Inserted. - Memory::Write_U32(1, outPtr); + // 2 = Not inserted. + Memory::Write_U32(MemoryStick_State(), outPtr); return 0; } else { return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS; } break; case 0x02425818: - // // Get MS capacity (fatms0). - // Pretend we have a 2GB memory stick. + // Get MS capacity (fatms0). + if (MemoryStick_State() != PSP_MEMORYSTICK_STATE_INSERTED) { + return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND; + } + // TODO: Pretend we have a 2GB memory stick? Should we check MemoryStick_FreeSpace? if (Memory::IsValidAddress(argAddr) && argLen >= 4) { // NOTE: not outPtr u32 pointer = Memory::Read_U32(argAddr); u32 sectorSize = 0x200; @@ -1547,7 +1591,11 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS; } break; - case 0x02425824: // Check if write protected + case 0x02425824: + // Check if write protected + if (MemoryStick_State() != PSP_MEMORYSTICK_STATE_INSERTED) { + return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND; + } if (Memory::IsValidAddress(outPtr) && outLen == 4) { Memory::Write_U32(0, outPtr); return 0; @@ -1562,31 +1610,55 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o if (!strcmp(name, "fatms0:")) { switch (cmd) { - case 0x02415821: // MScmRegisterMSInsertEjectCallback - { - // TODO: Verify how duplicates work / how many are allowed. + case 0x0240d81e: + // TODO: Invalidate MS driver file table cache (nop) + break; + case 0x02415821: + // MScmRegisterMSInsertEjectCallback + if (Memory::IsValidAddress(argAddr) && argLen >= 4) { u32 cbId = Memory::Read_U32(argAddr); - if (memStickFatCallbacks.find(cbId) == memStickFatCallbacks.end()) { - memStickFatCallbacks.insert(cbId); - DEBUG_LOG(SCEIO, "sceIoDevCtl: Memstick FAT callback %i registered, notifying immediately.", cbId); - __KernelNotifyCallback(cbId, MemoryStick_FatState()); + int type = -1; + kernelObjects.GetIDType(cbId, &type); + + if (memStickFatCallbacks.size() < 32 && type == SCE_KERNEL_TMID_Callback) { + memStickFatCallbacks.push_back(cbId); + if (MemoryStick_State() == PSP_MEMORYSTICK_STATE_INSERTED) { + // Only fired immediately if the card is currently inserted. + // Values observed: + // * 1 = Memory stick inserted + // * 2 = Memory stick removed + DEBUG_LOG(SCEIO, "sceIoDevCtl: Memstick FAT callback %i registered, notifying immediately", cbId); + __KernelNotifyCallback(cbId, MemoryStick_FatState()); + } else { + DEBUG_LOG(SCEIO, "sceIoDevCtl: Memstick FAT callback %i registered", cbId); + } return 0; } else { - return -1; + return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT; } + } else { + return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT; } break; - case 0x02415822: - { - // MScmUnregisterMSInsertEjectCallback - // TODO: Verify how duplicates work / how many are allowed. + case 0x02415822: + // MScmUnregisterMSInsertEjectCallback + if (Memory::IsValidAddress(argAddr) && argLen >= 4) { u32 cbId = Memory::Read_U32(argAddr); - if (memStickFatCallbacks.find(cbId) != memStickFatCallbacks.end()) { - memStickFatCallbacks.erase(cbId); + size_t slot = (size_t)-1; + // We want to only remove one at a time. + for (size_t i = 0; i < memStickFatCallbacks.size(); ++i) { + if (memStickFatCallbacks[i] == cbId) { + slot = i; + break; + } + } + + if (slot != (size_t)-1) { + memStickFatCallbacks.erase(memStickFatCallbacks.begin() + slot); DEBUG_LOG(SCEIO, "sceIoDevCtl: Unregistered memstick FAT callback %i", cbId); return 0; } else { - return -1; + return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT; } } break; @@ -1612,12 +1684,16 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT; } else { // Does not care about outLen, even if it's 0. + // Note: writes 1 when inserted, 0 when not inserted. Memory::Write_U32(MemoryStick_FatState(), outPtr); return hleDelayResult(0, "check fat state", cyclesToUs(23500)); } break; case 0x02425824: // Check if write protected + if (MemoryStick_State() != PSP_MEMORYSTICK_STATE_INSERTED) { + return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND; + } if (Memory::IsValidAddress(outPtr) && outLen == 4) { Memory::Write_U32(0, outPtr); return 0; @@ -1627,8 +1703,11 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o } break; case 0x02425818: - // // Get MS capacity (fatms0). - // Pretend we have a 2GB memory stick. + // Get MS capacity (fatms0). + if (MemoryStick_State() != PSP_MEMORYSTICK_STATE_INSERTED) { + return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND; + } + // TODO: Pretend we have a 2GB memory stick? Should we check MemoryStick_FreeSpace? if (Memory::IsValidAddress(argAddr) && argLen >= 4) { // NOTE: not outPtr u32 pointer = Memory::Read_U32(argAddr); u32 sectorSize = 0x200; diff --git a/Core/HW/MemoryStick.cpp b/Core/HW/MemoryStick.cpp index f23c84369e..3fbedb1458 100644 --- a/Core/HW/MemoryStick.cpp +++ b/Core/HW/MemoryStick.cpp @@ -53,7 +53,7 @@ void MemoryStick_SetFatState(MemStickFatState state) void MemoryStick_Init() { - memStickState = PSP_MEMORYSTICK_STATE_DRIVER_READY; + memStickState = PSP_MEMORYSTICK_STATE_INSERTED; memStickFatState = PSP_FAT_MEMORYSTICK_STATE_ASSIGNED; // Harry Potter and the Goblet of Fire has a bug where it can't handle certain amounts // of free space due to incorrect 32-bit math. diff --git a/Core/HW/MemoryStick.h b/Core/HW/MemoryStick.h index db5800efb7..6ea4c2365b 100644 --- a/Core/HW/MemoryStick.h +++ b/Core/HW/MemoryStick.h @@ -4,20 +4,25 @@ class PointerWrap; -// mscmhc0 states +// mscmhc0 states (status of the card.) enum MemStickState { + PSP_MEMORYSTICK_STATE_INSERTED = 1, + PSP_MEMORYSTICK_STATE_NOT_INSERTED = 2, +}; + +// memstick FAT states (status of mounting.) +enum MemStickFatState { + PSP_FAT_MEMORYSTICK_STATE_UNASSIGNED = 0, + PSP_FAT_MEMORYSTICK_STATE_ASSIGNED = 1, +}; + +enum MemStickDriverState { PSP_MEMORYSTICK_STATE_DRIVER_READY = 1, PSP_MEMORYSTICK_STATE_DRIVER_BUSY = 2, PSP_MEMORYSTICK_STATE_DEVICE_INSERTED = 4, PSP_MEMORYSTICK_STATE_DEVICE_REMOVED = 8, }; -// memstick FAT states. -enum MemStickFatState { - PSP_FAT_MEMORYSTICK_STATE_UNASSIGNED = 0, - PSP_FAT_MEMORYSTICK_STATE_ASSIGNED = 1, -}; - void MemoryStick_Init(); void MemoryStick_DoState(PointerWrap &p); MemStickState MemoryStick_State();