From 9e488e8be43189f5af28f40c0fb1678dc61547d2 Mon Sep 17 00:00:00 2001
From: "Unknown W. Brackets" <checkins@unknownbrackets.org>
Date: Fri, 5 Aug 2016 20:42:58 -0700
Subject: [PATCH 1/3] 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   |  17 ++--
 3 files changed, 138 insertions(+), 54 deletions(-)

diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp
index ebeaeaaf9365..23b4f8c9ebb1 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<SceUID> memStickCallbacks;
-static std::set<SceUID> memStickFatCallbacks;
+static std::vector<SceUID> memStickCallbacks;
+static std::vector<SceUID> 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<SceUID> 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 f23c84369e4c..3fbedb14581d 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 db5800efb722..6ea4c2365b76 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_DRIVER_READY    = 1,
-	PSP_MEMORYSTICK_STATE_DRIVER_BUSY     = 2,
-	PSP_MEMORYSTICK_STATE_DEVICE_INSERTED = 4,
-	PSP_MEMORYSTICK_STATE_DEVICE_REMOVED  = 8,
+	PSP_MEMORYSTICK_STATE_INSERTED        = 1,
+	PSP_MEMORYSTICK_STATE_NOT_INSERTED    = 2,
 };
 
-// memstick FAT states.
+// 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,
+};
+
 void MemoryStick_Init();
 void MemoryStick_DoState(PointerWrap &p);
 MemStickState MemoryStick_State();

From bf7a020c6a1cf08da8695c59768ebe84fd8fa377 Mon Sep 17 00:00:00 2001
From: "Unknown W. Brackets" <checkins@unknownbrackets.org>
Date: Fri, 5 Aug 2016 21:05:48 -0700
Subject: [PATCH 2/3] Io: Notify callbacks on memstick insert/remove.

---
 Core/HLE/sceIo.cpp      | 60 ++++++++++++++++++++++++++++++++++++++++-
 Core/HW/MemoryStick.cpp | 49 ++++++++++++++++++++++-----------
 Core/HW/MemoryStick.h   |  1 +
 3 files changed, 94 insertions(+), 16 deletions(-)

diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp
index 23b4f8c9ebb1..5cc6f4598b53 100644
--- a/Core/HLE/sceIo.cpp
+++ b/Core/HLE/sceIo.cpp
@@ -33,6 +33,7 @@
 #include "Core/SaveState.h"
 #include "Core/HLE/HLE.h"
 #include "Core/HLE/FunctionWrappers.h"
+#include "Core/HLE/sceDisplay.h"
 #include "Core/HLE/sceKernel.h"
 #include "Core/HLE/sceUmd.h"
 #include "Core/MIPS/MIPS.h"
@@ -118,8 +119,12 @@ const int PSP_STDIN = 3;
 static int asyncNotifyEvent = -1;
 static int syncNotifyEvent = -1;
 static SceUID fds[PSP_COUNT_FDS];
+
 static std::vector<SceUID> memStickCallbacks;
 static std::vector<SceUID> memStickFatCallbacks;
+static MemStickState lastMemStickState;
+static MemStickFatState lastMemStickFatState;
+
 static AsyncIOManager ioManager;
 static bool ioManagerThreadEnabled = false;
 static std::thread *ioManagerThread;
@@ -497,6 +502,50 @@ static void __IoWakeManager() {
 	ioManager.FinishEventLoop();
 }
 
+static void __IoVblank() {
+	// We update memstick status here just to avoid possible thread safety issues.
+	// It doesn't actually need to be on a vblank.
+
+	MemStickState newState = MemoryStick_State();
+	MemStickFatState newFatState = MemoryStick_FatState();
+
+	// First, the fat callbacks, these are easy.
+	if (lastMemStickFatState != newFatState) {
+		int notifyMsg = 0;
+		if (newFatState == PSP_FAT_MEMORYSTICK_STATE_ASSIGNED) {
+			notifyMsg = 1;
+		} else if (newFatState == PSP_FAT_MEMORYSTICK_STATE_UNASSIGNED) {
+			notifyMsg = 2;
+		}
+		if (notifyMsg != 0) {
+			for (SceUID cbId : memStickFatCallbacks) {
+				__KernelNotifyCallback(cbId, notifyMsg);
+			}
+		}
+	}
+
+	// Next, the controller notifies mounting (fat) too.
+	if (lastMemStickState != newState || lastMemStickFatState != newFatState) {
+		int notifyMsg = 0;
+		if (newState == PSP_MEMORYSTICK_STATE_INSERTED && newFatState == PSP_FAT_MEMORYSTICK_STATE_ASSIGNED) {
+			notifyMsg = 1;
+		} else if (newState == PSP_MEMORYSTICK_STATE_INSERTED && newFatState == PSP_FAT_MEMORYSTICK_STATE_UNASSIGNED) {
+			// Still mounting (1 will come later.)
+			notifyMsg = 4;
+		} else if (newState == PSP_MEMORYSTICK_STATE_NOT_INSERTED) {
+			notifyMsg = 2;
+		}
+		if (notifyMsg != 0) {
+			for (SceUID cbId : memStickCallbacks) {
+				__KernelNotifyCallback(cbId, notifyMsg);
+			}
+		}
+	}
+
+	lastMemStickState = newState;
+	lastMemStickFatState = newFatState;
+}
+
 void __IoInit() {
 	MemoryStick_Init();
 
@@ -545,10 +594,14 @@ void __IoInit() {
 	}
 
 	__KernelRegisterWaitTypeFuncs(WAITTYPE_ASYNCIO, __IoAsyncBeginCallback, __IoAsyncEndCallback);
+
+	lastMemStickState = MemoryStick_State();
+	lastMemStickFatState = MemoryStick_FatState();
+	__DisplayListenVblank(__IoVblank);
 }
 
 void __IoDoState(PointerWrap &p) {
-	auto s = p.Section("sceIo", 1, 2);
+	auto s = p.Section("sceIo", 1, 3);
 	if (!s)
 		return;
 
@@ -576,6 +629,11 @@ void __IoDoState(PointerWrap &p) {
 		p.Do(memStickCallbacks);
 		p.Do(memStickFatCallbacks);
 	}
+
+	if (s >= 3) {
+		p.Do(lastMemStickState);
+		p.Do(lastMemStickFatState);
+	}
 }
 
 void __IoShutdown() {
diff --git a/Core/HW/MemoryStick.cpp b/Core/HW/MemoryStick.cpp
index 3fbedb14581d..5c1ab7a82f6d 100644
--- a/Core/HW/MemoryStick.cpp
+++ b/Core/HW/MemoryStick.cpp
@@ -1,4 +1,5 @@
 #include "Common/ChunkFile.h"
+#include "Core/CoreTiming.h"
 #include "Core/FileSystems/MetaFileSystem.h"
 #include "Core/HW/MemoryStick.h"
 #include "Core/System.h"
@@ -7,10 +8,11 @@
 static MemStickState memStickState;
 static MemStickFatState memStickFatState;
 static u64 memStickSize;
+static bool memStickNeedsAssign = false;
+static s64 memStickInsertedAt = 0;
 
-void MemoryStick_DoState(PointerWrap &p)
-{
-	auto s = p.Section("MemoryStick", 1, 2);
+void MemoryStick_DoState(PointerWrap &p) {
+	auto s = p.Section("MemoryStick", 1, 3);
 	if (!s)
 		return;
 
@@ -20,43 +22,60 @@ void MemoryStick_DoState(PointerWrap &p)
 		p.Do(memStickSize);
 	else
 		memStickSize = 1ULL * 1024 * 1024 * 1024;
+	if (s >= 3) {
+		p.Do(memStickNeedsAssign);
+		p.Do(memStickInsertedAt);
+	}
 
 }
 
-MemStickState MemoryStick_State()
-{
+MemStickState MemoryStick_State() {
 	return memStickState;
 }
 
-MemStickFatState MemoryStick_FatState()
-{
+MemStickFatState MemoryStick_FatState() {
+	if (memStickNeedsAssign && CoreTiming::GetTicks() > memStickInsertedAt + msToCycles(500)) {
+		// It's been long enough for us to be done mounting the memory stick.
+		memStickFatState = PSP_FAT_MEMORYSTICK_STATE_ASSIGNED;
+		memStickNeedsAssign = false;
+	}
 	return memStickFatState;
 }
 
-u64 MemoryStick_SectorSize()
-{
+u64 MemoryStick_SectorSize() {
 	return 32 * 1024; // 32KB
 }
 
-u64 MemoryStick_FreeSpace()
-{
+u64 MemoryStick_FreeSpace() {
 	u64 freeSpace = pspFileSystem.FreeSpace("ms0:/");
 	if (freeSpace < memStickSize)
 		return freeSpace;
 	return memStickSize;
 }
 
-void MemoryStick_SetFatState(MemStickFatState state)
-{
+void MemoryStick_SetFatState(MemStickFatState state) {
 	memStickFatState = state;
+	memStickNeedsAssign = false;
 }
 
-void MemoryStick_Init()
-{
+void MemoryStick_SetState(MemStickState state) {
+	memStickState = state;
+
+	// If removed, we unmount.  Otherwise, mounting is delayed.
+	if (state == PSP_MEMORYSTICK_STATE_NOT_INSERTED) {
+		MemoryStick_SetFatState(PSP_FAT_MEMORYSTICK_STATE_UNASSIGNED);
+	} else {
+		memStickInsertedAt = CoreTiming::GetTicks();
+		memStickNeedsAssign = true;
+	}
+}
+
+void MemoryStick_Init() {
 	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.
 	// We use 9GB here, which does not trigger the bug, as a cap for the max free space.
 	memStickSize = 9ULL * 1024 * 1024 * 1024; // 9GB
+	memStickNeedsAssign = false;
 }
diff --git a/Core/HW/MemoryStick.h b/Core/HW/MemoryStick.h
index 6ea4c2365b76..01b526fa984b 100644
--- a/Core/HW/MemoryStick.h
+++ b/Core/HW/MemoryStick.h
@@ -28,6 +28,7 @@ void MemoryStick_DoState(PointerWrap &p);
 MemStickState MemoryStick_State();
 MemStickFatState MemoryStick_FatState();
 
+void MemoryStick_SetState(MemStickState state);
 void MemoryStick_SetFatState(MemStickFatState state);
 
 u64 MemoryStick_SectorSize();

From 980d4194c0741e3055699adffd055e10fe9114e2 Mon Sep 17 00:00:00 2001
From: "Unknown W. Brackets" <checkins@unknownbrackets.org>
Date: Fri, 5 Aug 2016 21:27:53 -0700
Subject: [PATCH 3/3] Io: Add a config setting for memstick status.

---
 Core/Config.cpp           |  2 ++
 Core/Config.h             |  1 +
 Core/HLE/sceIo.cpp        |  3 +++
 Core/HW/MemoryStick.cpp   | 14 ++++++++++++--
 UI/GameSettingsScreen.cpp |  2 ++
 5 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/Core/Config.cpp b/Core/Config.cpp
index 3ee86667dbfe..e38a04c2d9a6 100644
--- a/Core/Config.cpp
+++ b/Core/Config.cpp
@@ -358,6 +358,8 @@ static ConfigSetting generalSettings[] = {
 	ConfigSetting("DumpDecryptedEboots", &g_Config.bDumpDecryptedEboot, false, true, true),
 	ConfigSetting("FullscreenOnDoubleclick", &g_Config.bFullscreenOnDoubleclick, true, false, false),
 
+	ReportedConfigSetting("MemStickInserted", &g_Config.bMemStickInserted, true, true, true),
+
 	ConfigSetting(false),
 };
 
diff --git a/Core/Config.h b/Core/Config.h
index 1a3afc10cc89..119acebea97a 100644
--- a/Core/Config.h
+++ b/Core/Config.h
@@ -139,6 +139,7 @@ struct Config {
 	bool bAutoSaveSymbolMap;
 	bool bCacheFullIsoInRam;
 	int iRemoteISOPort;
+	bool bMemStickInserted;
 
 	int iScreenRotation;  // The rotation angle of the PPSSPP UI. Only supported on Android and possibly other mobile platforms.
 	int iInternalScreenRotation;  // The internal screen rotation angle. Useful for vertical SHMUPs and similar.
diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp
index 5cc6f4598b53..f02b2fa5b565 100644
--- a/Core/HLE/sceIo.cpp
+++ b/Core/HLE/sceIo.cpp
@@ -506,6 +506,9 @@ static void __IoVblank() {
 	// We update memstick status here just to avoid possible thread safety issues.
 	// It doesn't actually need to be on a vblank.
 
+	// This will only change status if g_Config was changed.
+	MemoryStick_SetState(g_Config.bMemStickInserted ? PSP_MEMORYSTICK_STATE_INSERTED : PSP_MEMORYSTICK_STATE_NOT_INSERTED);
+
 	MemStickState newState = MemoryStick_State();
 	MemStickFatState newFatState = MemoryStick_FatState();
 
diff --git a/Core/HW/MemoryStick.cpp b/Core/HW/MemoryStick.cpp
index 5c1ab7a82f6d..2995f1005503 100644
--- a/Core/HW/MemoryStick.cpp
+++ b/Core/HW/MemoryStick.cpp
@@ -59,6 +59,10 @@ void MemoryStick_SetFatState(MemStickFatState state) {
 }
 
 void MemoryStick_SetState(MemStickState state) {
+	if (memStickState == state) {
+		return;
+	}
+
 	memStickState = state;
 
 	// If removed, we unmount.  Otherwise, mounting is delayed.
@@ -71,8 +75,14 @@ void MemoryStick_SetState(MemStickState state) {
 }
 
 void MemoryStick_Init() {
-	memStickState = PSP_MEMORYSTICK_STATE_INSERTED;
-	memStickFatState = PSP_FAT_MEMORYSTICK_STATE_ASSIGNED;
+	if (g_Config.bMemStickInserted) {
+		memStickState = PSP_MEMORYSTICK_STATE_INSERTED;
+		memStickFatState = PSP_FAT_MEMORYSTICK_STATE_ASSIGNED;
+	} else {
+		memStickState = PSP_MEMORYSTICK_STATE_NOT_INSERTED;
+		memStickFatState = PSP_FAT_MEMORYSTICK_STATE_UNASSIGNED;
+	}
+
 	// 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.
 	// We use 9GB here, which does not trigger the bug, as a cap for the max free space.
diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp
index 79c76fd42b38..d25a46df4cf6 100644
--- a/UI/GameSettingsScreen.cpp
+++ b/UI/GameSettingsScreen.cpp
@@ -534,6 +534,8 @@ void GameSettingsScreen::CreateViews() {
 	PopupSliderChoice *rewindFreq = systemSettings->Add(new PopupSliderChoice(&g_Config.iRewindFlipFrequency, 0, 1800, sy->T("Rewind Snapshot Frequency", "Rewind Snapshot Frequency (mem hog)"), screenManager(), sy->T("frames, 0:off")));
 	rewindFreq->SetZeroLabel(sy->T("Off"));
 
+	systemSettings->Add(new CheckBox(&g_Config.bMemStickInserted, sy->T("Memory Stick inserted")));
+
 	systemSettings->Add(new ItemHeader(sy->T("General")));
 
 #ifdef ANDROID