From 1c08912a59225a0b85e67e3a18df233b7aeb0332 Mon Sep 17 00:00:00 2001
From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com>
Date: Sat, 8 Jul 2023 19:35:59 +0300
Subject: [PATCH 1/6] [IR:USER] Add empty service

---
 CMakeLists.txt                        |  3 ++-
 include/kernel/handles.hpp            |  2 ++
 include/logger.hpp                    |  1 +
 include/services/ir_user.hpp          | 19 +++++++++++++++++++
 include/services/service_manager.hpp  |  2 ++
 src/core/services/ir_user.cpp         | 15 +++++++++++++++
 src/core/services/service_manager.cpp |  7 ++++++-
 7 files changed, 47 insertions(+), 2 deletions(-)
 create mode 100644 include/services/ir_user.hpp
 create mode 100644 src/core/services/ir_user.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8d446ba7..016b2a59 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -104,6 +104,7 @@ set(SERVICE_SOURCE_FILES src/core/services/service_manager.cpp src/core/services
                          src/core/services/frd.cpp src/core/services/nim.cpp src/core/services/shared_font.cpp
                          src/core/services/y2r.cpp src/core/services/cam.cpp src/core/services/ldr_ro.cpp
                          src/core/services/act.cpp src/core/services/nfc.cpp src/core/services/dlp_srvr.cpp
+                         src/core/services/ir_user.cpp
 )
 set(PICA_SOURCE_FILES src/core/PICA/gpu.cpp src/core/PICA/regs.cpp src/core/PICA/shader_unit.cpp
                       src/core/PICA/shader_interpreter.cpp src/core/PICA/dynapica/shader_rec.cpp
@@ -141,7 +142,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/opengl.hpp inc
                  include/result/result_common.hpp include/result/result_fs.hpp include/result/result_fnd.hpp
                  include/result/result_gsp.hpp include/result/result_kernel.hpp include/result/result_os.hpp
                  include/crypto/aes_engine.hpp include/metaprogramming.hpp include/PICA/pica_vertex.hpp include/gl_state.hpp
-                 include/config.hpp
+                 include/config.hpp include/services/ir_user.hpp
 )
 
 set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp
diff --git a/include/kernel/handles.hpp b/include/kernel/handles.hpp
index 589b64f5..3656beeb 100644
--- a/include/kernel/handles.hpp
+++ b/include/kernel/handles.hpp
@@ -21,6 +21,7 @@ namespace KernelHandles {
 		DLP_SRVR, // Download Play: Server. Used for network play.
 		DSP,  // DSP service (Used for audio decoding and output)
 		HID,  // HID service (Handles everything input-related including gyro)
+		IR_USER, // One of 3 infrared communication services
         FRD,  // Friend service (Miiverse friend service)
 		FS,   // Filesystem service
 		GPU,  // GPU service
@@ -68,6 +69,7 @@ namespace KernelHandles {
 			case DSP: return "DSP";
 			case DLP_SRVR: return "DLP::SRVR";
 			case HID: return "HID";
+			case IR_USER: return "IR:USER";
 			case FRD: return "FRD";
 			case FS: return "FS";
 			case GPU: return "GSP::GPU";
diff --git a/include/logger.hpp b/include/logger.hpp
index 62129707..e1b425b9 100644
--- a/include/logger.hpp
+++ b/include/logger.hpp
@@ -42,6 +42,7 @@ namespace Log {
     static Logger<false> frdLogger;
     static Logger<false> fsLogger;
     static Logger<false> hidLogger;
+	static Logger<false> irUserLogger;
     static Logger<false> gspGPULogger;
     static Logger<false> gspLCDLogger;
     static Logger<false> ldrLogger;
diff --git a/include/services/ir_user.hpp b/include/services/ir_user.hpp
new file mode 100644
index 00000000..734bec44
--- /dev/null
+++ b/include/services/ir_user.hpp
@@ -0,0 +1,19 @@
+#pragma once
+#include "helpers.hpp"
+#include "kernel_types.hpp"
+#include "logger.hpp"
+#include "memory.hpp"
+#include "result/result.hpp"
+
+class IRUserService {
+	Handle handle = KernelHandles::IR_USER;
+	Memory& mem;
+	MAKE_LOG_FUNCTION(log, irUserLogger)
+
+	// Service commands
+
+  public:
+	IRUserService(Memory& mem) : mem(mem) {}
+	void reset();
+	void handleSyncRequest(u32 messagePointer);
+};
\ No newline at end of file
diff --git a/include/services/service_manager.hpp b/include/services/service_manager.hpp
index ee2677ad..3136408f 100644
--- a/include/services/service_manager.hpp
+++ b/include/services/service_manager.hpp
@@ -21,6 +21,7 @@
 #include "services/gsp_gpu.hpp"
 #include "services/gsp_lcd.hpp"
 #include "services/hid.hpp"
+#include "services/ir_user.hpp"
 #include "services/ldr_ro.hpp"
 #include "services/mic.hpp"
 #include "services/ndm.hpp"
@@ -52,6 +53,7 @@ class ServiceManager {
 	DlpSrvrService dlp_srvr;
 	DSPService dsp;
 	HIDService hid;
+	IRUserService ir_user;
 	FRDService frd;
 	FSService fs;
 	GPUService gsp_gpu;
diff --git a/src/core/services/ir_user.cpp b/src/core/services/ir_user.cpp
new file mode 100644
index 00000000..6a1a9c11
--- /dev/null
+++ b/src/core/services/ir_user.cpp
@@ -0,0 +1,15 @@
+#include "ipc.hpp"
+#include "services/ir_user.hpp"
+
+namespace IRUserCommands {
+	enum : u32 {};
+}
+
+void IRUserService::reset() {}
+
+void IRUserService::handleSyncRequest(u32 messagePointer) {
+	const u32 command = mem.read32(messagePointer);
+	switch (command) {
+		default: Helpers::panic("ir:USER service requested. Command: %08X\n", command);
+	}
+}
\ No newline at end of file
diff --git a/src/core/services/service_manager.cpp b/src/core/services/service_manager.cpp
index 5f2918de..6ac2478b 100644
--- a/src/core/services/service_manager.cpp
+++ b/src/core/services/service_manager.cpp
@@ -7,7 +7,7 @@
 
 ServiceManager::ServiceManager(std::span<u32, 16> regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel)
 	: regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem),
-	  cecd(mem, kernel), cfg(mem), dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), frd(mem), fs(mem, kernel),
+	  cecd(mem, kernel), cfg(mem), dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), ir_user(mem), frd(mem), fs(mem, kernel),
 	  gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), mic(mem), nfc(mem, kernel), nim(mem), ndm(mem),
 	  ptm(mem), y2r(mem, kernel) {}
 
@@ -26,6 +26,7 @@ void ServiceManager::reset() {
 	dlp_srvr.reset();
 	dsp.reset();
 	hid.reset();
+	ir_user.reset();
 	frd.reset();
 	fs.reset();
 	gsp_gpu.reset();
@@ -83,6 +84,7 @@ void ServiceManager::registerClient(u32 messagePointer) {
 	mem.write32(messagePointer + 4, Result::Success);
 }
 
+// clang-format off
 static std::map<std::string, Handle> serviceMap = {
 	{ "ac:u", KernelHandles::AC },
 	{ "act:u", KernelHandles::ACT },
@@ -97,6 +99,7 @@ static std::map<std::string, Handle> serviceMap = {
 	{ "dlp:SRVR", KernelHandles::DLP_SRVR },
 	{ "dsp::DSP", KernelHandles::DSP },
 	{ "hid:USER", KernelHandles::HID },
+	{ "ir:USER", KernelHandles::IR_USER },
 	{ "frd:u", KernelHandles::FRD },
 	{ "fs:USER", KernelHandles::FS },
 	{ "gsp::Gpu", KernelHandles::GPU },
@@ -110,6 +113,7 @@ static std::map<std::string, Handle> serviceMap = {
 	{ "ptm:sysm", KernelHandles::PTM },
 	{ "y2r:u", KernelHandles::Y2R }
 };
+// clang-format on
 
 // https://www.3dbrew.org/wiki/SRV:GetServiceHandle
 void ServiceManager::getServiceHandle(u32 messagePointer) {
@@ -179,6 +183,7 @@ void ServiceManager::sendCommandToService(u32 messagePointer, Handle handle) {
 		case KernelHandles::CFG: cfg.handleSyncRequest(messagePointer); break;
 		case KernelHandles::DLP_SRVR: dlp_srvr.handleSyncRequest(messagePointer); break;
 		case KernelHandles::HID: hid.handleSyncRequest(messagePointer); break;
+		case KernelHandles::IR_USER: ir_user.handleSyncRequest(messagePointer); break;
         case KernelHandles::FRD: frd.handleSyncRequest(messagePointer); break;
 		case KernelHandles::LCD: gsp_lcd.handleSyncRequest(messagePointer); break;
         case KernelHandles::LDR_RO: ldr.handleSyncRequest(messagePointer); break;

From b83526378edca4cf22ea853d592b7f185acb092c Mon Sep 17 00:00:00 2001
From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com>
Date: Sat, 8 Jul 2023 20:13:20 +0300
Subject: [PATCH 2/6] [IR:USER] Some more stubbing

---
 include/services/ir_user.hpp          | 15 +++++-
 src/core/services/ir_user.cpp         | 69 +++++++++++++++++++++++++--
 src/core/services/service_manager.cpp |  7 ++-
 3 files changed, 83 insertions(+), 8 deletions(-)

diff --git a/include/services/ir_user.hpp b/include/services/ir_user.hpp
index 734bec44..2c7b1559 100644
--- a/include/services/ir_user.hpp
+++ b/include/services/ir_user.hpp
@@ -1,19 +1,32 @@
 #pragma once
+#include <optional>
+
 #include "helpers.hpp"
 #include "kernel_types.hpp"
 #include "logger.hpp"
 #include "memory.hpp"
 #include "result/result.hpp"
 
+// Circular dependencies in this project? Never
+class Kernel;
+
 class IRUserService {
 	Handle handle = KernelHandles::IR_USER;
 	Memory& mem;
+	Kernel& kernel;
 	MAKE_LOG_FUNCTION(log, irUserLogger)
 
 	// Service commands
+	void disconnect(u32 messagePointer);
+	void finalizeIrnop(u32 messagePointer);
+	void getConnectionStatusEvent(u32 messagePointer);
+	void initializeIrnopShared(u32 messagePointer);
+	void requireConnection(u32 messagePointer);
+
+	std::optional<Handle> connectionStatusEvent = std::nullopt;
 
   public:
-	IRUserService(Memory& mem) : mem(mem) {}
+	IRUserService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
 	void reset();
 	void handleSyncRequest(u32 messagePointer);
 };
\ No newline at end of file
diff --git a/src/core/services/ir_user.cpp b/src/core/services/ir_user.cpp
index 6a1a9c11..c8573182 100644
--- a/src/core/services/ir_user.cpp
+++ b/src/core/services/ir_user.cpp
@@ -1,15 +1,78 @@
-#include "ipc.hpp"
 #include "services/ir_user.hpp"
 
+#include "ipc.hpp"
+#include "kernel.hpp"
+
 namespace IRUserCommands {
-	enum : u32 {};
+	enum : u32 {
+		FinalizeIrnop = 0x00020000,
+		RequireConnection = 0x00060040,
+		Disconnect = 0x00090000,
+		GetConnectionStatusEvent = 0x000C0000,
+		InitializeIrnopShared = 0x00180182
+	};
 }
 
-void IRUserService::reset() {}
+void IRUserService::reset() { connectionStatusEvent = std::nullopt; }
 
 void IRUserService::handleSyncRequest(u32 messagePointer) {
 	const u32 command = mem.read32(messagePointer);
 	switch (command) {
+		case IRUserCommands::Disconnect: disconnect(messagePointer); break;
+		case IRUserCommands::FinalizeIrnop: finalizeIrnop(messagePointer); break;
+		case IRUserCommands::GetConnectionStatusEvent: getConnectionStatusEvent(messagePointer); break;
+		case IRUserCommands::InitializeIrnopShared: initializeIrnopShared(messagePointer); break;
+		case IRUserCommands::RequireConnection: requireConnection(messagePointer); break;
 		default: Helpers::panic("ir:USER service requested. Command: %08X\n", command);
 	}
+}
+
+void IRUserService::initializeIrnopShared(u32 messagePointer) {
+	const u32 sharedMemSize = mem.read32(messagePointer + 4);
+	const u32 receiveBufferSize = mem.read32(messagePointer + 8);
+	const u32 receiveBufferPackageCount = mem.read32(messagePointer + 12);
+	const u32 sendBufferSize = mem.read32(messagePointer + 16);
+	const u32 sendBufferPackageCount = mem.read32(messagePointer + 20);
+	const u32 bitrate = mem.read32(messagePointer + 24);
+	const u32 descriptor = mem.read32(messagePointer + 28);
+	const u32 sharedMemHandle = mem.read32(messagePointer + 32);
+
+	log("IR:USER: InitializeIrnopShared (shared mem size = %08X, sharedMemHandle = %X) (stubbed)\n", sharedMemSize, sharedMemHandle);
+	mem.write32(messagePointer, IPC::responseHeader(0x18, 1, 0));
+	mem.write32(messagePointer + 4, Result::Success);
+}
+
+void IRUserService::finalizeIrnop(u32 messagePointer) {
+	log("IR:USER: FinalizeIrnop\n");
+
+	// This should disconnect any connected device de-initialize the shared memory
+	mem.write32(messagePointer, IPC::responseHeader(0x2, 1, 0));
+	mem.write32(messagePointer + 4, Result::Success);
+}
+
+void IRUserService::getConnectionStatusEvent(u32 messagePointer) {
+	log("IR:USER: GetConnectionStatusEvent\n");
+
+	if (!connectionStatusEvent.has_value()) {
+		connectionStatusEvent = kernel.makeEvent(ResetType::OneShot);
+	}
+
+	mem.write32(messagePointer, IPC::responseHeader(0xC, 1, 2));
+	mem.write32(messagePointer + 4, Result::Success);
+	// TOOD: Descriptor here
+	mem.write32(messagePointer + 12, connectionStatusEvent.value());
+}
+
+void IRUserService::requireConnection(u32 messagePointer) {
+	const u8 deviceID = mem.read8(messagePointer + 4);
+	log("IR:USER: RequireConnection (device: %d)\n", deviceID);
+
+	mem.write32(messagePointer, IPC::responseHeader(0x6, 1, 0));
+	mem.write32(messagePointer + 4, Result::Success);
+}
+
+void IRUserService::disconnect(u32 messagePointer) {
+	log("IR:USER: Disconnect\n");
+	mem.write32(messagePointer, IPC::responseHeader(0x9, 1, 0));
+	mem.write32(messagePointer + 4, Result::Success);
 }
\ No newline at end of file
diff --git a/src/core/services/service_manager.cpp b/src/core/services/service_manager.cpp
index 6ac2478b..17fdf1da 100644
--- a/src/core/services/service_manager.cpp
+++ b/src/core/services/service_manager.cpp
@@ -6,10 +6,9 @@
 #include "kernel.hpp"
 
 ServiceManager::ServiceManager(std::span<u32, 16> regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel)
-	: regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem),
-	  cecd(mem, kernel), cfg(mem), dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), ir_user(mem), frd(mem), fs(mem, kernel),
-	  gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), mic(mem), nfc(mem, kernel), nim(mem), ndm(mem),
-	  ptm(mem), y2r(mem, kernel) {}
+	: regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem), cecd(mem, kernel), cfg(mem),
+	  dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), ir_user(mem, kernel), frd(mem), fs(mem, kernel), gsp_gpu(mem, gpu, kernel, currentPID),
+	  gsp_lcd(mem), ldr(mem), mic(mem), nfc(mem, kernel), nim(mem), ndm(mem), ptm(mem), y2r(mem, kernel) {}
 
 static constexpr int MAX_NOTIFICATION_COUNT = 16;
 

From 817b3de945162737d1e216a9cd64157f57d520b6 Mon Sep 17 00:00:00 2001
From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com>
Date: Sat, 8 Jul 2023 20:23:43 +0300
Subject: [PATCH 3/6] [GPU] Implement vertex padding

---
 src/core/PICA/gpu.cpp | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/core/PICA/gpu.cpp b/src/core/PICA/gpu.cpp
index ed67067c..a2a1f761 100644
--- a/src/core/PICA/gpu.cpp
+++ b/src/core/PICA/gpu.cpp
@@ -168,7 +168,15 @@ void GPU::drawArrays() {
 
 				for (int j = 0; j < attr.componentCount; j++) {
 					uint index = (attrCfg >> (j * 4)) & 0xf; // Get index of attribute in vertexCfg
-					if (index >= 12) Helpers::panic("[PICA] Vertex attribute used as padding");
+
+					// Vertex attributes used as padding
+					// 12, 13, 14 and 15 are equivalent to 4, 8, 12 and 16 bytes of padding respectively
+					if (index >= 12) [[unlikely]] {
+						// Align attriubte address up to a 4 byte boundary
+						attrAddress = (attrAddress + 3) & -4;
+						attrAddress += (index - 11) << 2;
+						continue;
+					}
 
 					u32 attribInfo = (vertexCfg >> (index * 4)) & 0xf;
 					u32 attribType = attribInfo & 0x3; //  Type of attribute(sbyte/ubyte/short/float)

From 91bf249cbaaab71a45d08c5166db2f47c0d26f94 Mon Sep 17 00:00:00 2001
From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com>
Date: Sat, 8 Jul 2023 20:32:21 +0300
Subject: [PATCH 4/6] [BOSS] Stub GetTaskInfo

---
 include/services/boss.hpp  |  1 +
 src/core/services/boss.cpp | 11 +++++++++++
 2 files changed, 12 insertions(+)

diff --git a/include/services/boss.hpp b/include/services/boss.hpp
index e31018e6..9e6ae27a 100644
--- a/include/services/boss.hpp
+++ b/include/services/boss.hpp
@@ -16,6 +16,7 @@ class BOSSService {
 	void getOptoutFlag(u32 messagePointer);
 	void getStorageEntryInfo(u32 messagePointer); // Unknown what this is, name taken from Citra
 	void getTaskIdList(u32 messagePointer);
+	void getTaskInfo(u32 messagePOinter);
 	void getTaskStorageInfo(u32 messagePointer);
 	void receiveProperty(u32 messagePointer);
 	void registerStorageEntry(u32 messagePointer);
diff --git a/src/core/services/boss.cpp b/src/core/services/boss.cpp
index b7bb0a6b..c535dda2 100644
--- a/src/core/services/boss.cpp
+++ b/src/core/services/boss.cpp
@@ -11,6 +11,7 @@ namespace BOSSCommands {
 		GetTaskIdList = 0x000E0000,
 		GetNsDataIdList = 0x00100102,
 		ReceiveProperty = 0x00160082,
+		GetTaskInfo = 0x00250082,
 		RegisterStorageEntry = 0x002F0140,
 		GetStorageEntryInfo = 0x00300000
 	};
@@ -27,6 +28,7 @@ void BOSSService::handleSyncRequest(u32 messagePointer) {
 		case BOSSCommands::GetOptoutFlag: getOptoutFlag(messagePointer); break;
 		case BOSSCommands::GetStorageEntryInfo: getStorageEntryInfo(messagePointer); break;
 		case BOSSCommands::GetTaskIdList: getTaskIdList(messagePointer); break;
+		case BOSSCommands::GetTaskInfo: getTaskInfo(messagePointer); break;
 		case BOSSCommands::GetTaskStorageInfo: getTaskStorageInfo(messagePointer); break;
 		case BOSSCommands::InitializeSession: initializeSession(messagePointer); break;
 		case BOSSCommands::ReceiveProperty: receiveProperty(messagePointer); break;
@@ -63,6 +65,15 @@ void BOSSService::getTaskIdList(u32 messagePointer) {
 	mem.write32(messagePointer + 4, Result::Success);
 }
 
+// This function is completely undocumented, including on 3DBrew
+// The name GetTaskInfo is taken from Citra source and nobody seems to know what exactly it does
+// Kid Icarus: Uprising uses it on startup
+void BOSSService::getTaskInfo(u32 messagePointer) {
+	log("BOSS::GetTaskInfo (stubbed and undocumented)\n");
+	mem.write32(messagePointer, IPC::responseHeader(0x25, 1, 2));
+	mem.write32(messagePointer + 4, Result::Success);
+}
+
 void BOSSService::getStorageEntryInfo(u32 messagePointer) {
 	log("BOSS::GetStorageEntryInfo (undocumented)\n");
 	mem.write32(messagePointer, IPC::responseHeader(0x30, 3, 0));

From 5f2f82d074502c0fbbf88f580be67c3ae1c5b1c5 Mon Sep 17 00:00:00 2001
From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com>
Date: Sat, 8 Jul 2023 20:43:48 +0300
Subject: [PATCH 5/6] [FS] Implement SdmcIsWritable

---
 include/services/fs.hpp  |  1 +
 src/core/services/fs.cpp | 17 ++++++++++++++++-
 2 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/include/services/fs.hpp b/include/services/fs.hpp
index 9220a869..52ac97ad 100644
--- a/include/services/fs.hpp
+++ b/include/services/fs.hpp
@@ -49,6 +49,7 @@ class FSService {
 	void initialize(u32 messagePointer);
 	void initializeWithSdkVersion(u32 messagePointer);
 	void isSdmcDetected(u32 messagePointer);
+	void isSdmcWritable(u32 messagePOinter);
 	void openArchive(u32 messagePointer);
 	void openDirectory(u32 messagePointer);
 	void openFile(u32 messagePointer);
diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp
index 06c970f0..2fd3b8e6 100644
--- a/src/core/services/fs.cpp
+++ b/src/core/services/fs.cpp
@@ -24,6 +24,7 @@ namespace FSCommands {
 		FormatThisUserSaveData = 0x080F0180,
 		GetFreeBytes = 0x08120080,
 		IsSdmcDetected = 0x08170000,
+		IsSdmcWritable = 0x08180000,
 		GetFormatInfo = 0x084500C2,
 		FormatSaveData = 0x084C0242,
 		InitializeWithSdkVersion = 0x08610042,
@@ -155,6 +156,7 @@ void FSService::handleSyncRequest(u32 messagePointer) {
 		case FSCommands::Initialize: initialize(messagePointer); break;
 		case FSCommands::InitializeWithSdkVersion: initializeWithSdkVersion(messagePointer); break;
 		case FSCommands::IsSdmcDetected: isSdmcDetected(messagePointer); break;
+		case FSCommands::IsSdmcWritable: isSdmcWritable(messagePointer); break;
 		case FSCommands::OpenArchive: openArchive(messagePointer); break;
 		case FSCommands::OpenDirectory: openDirectory(messagePointer); break;
 		case FSCommands::OpenFile: [[likely]] openFile(messagePointer); break;
@@ -536,9 +538,22 @@ void FSService::setPriority(u32 messagePointer) {
 	priority = value;
 }
 
+
+// Shows whether an SD card is inserted. At the moment stubbed to no
+constexpr bool sdInserted = false;
+
 void FSService::isSdmcDetected(u32 messagePointer) {
 	log("FS::IsSdmcDetected\n");
 	mem.write32(messagePointer, IPC::responseHeader(0x817, 2, 0));
 	mem.write32(messagePointer + 4, Result::Success);
-	mem.write32(messagePointer + 8, 0); // Whether SD is detected. For now we emulate a 3DS without an SD.
+	mem.write8(messagePointer + 8, sdInserted ? 1 : 0);
+}
+
+// We consider our SD card to always be writable if oen is inserted for now
+// So isSdmcWritable returns 1 if an SD card is inserted (because it's always writable) and 0 if not.
+void FSService::isSdmcWritable(u32 messagePointer) {
+	log("FS::isSdmcWritable\n");
+	mem.write32(messagePointer, IPC::responseHeader(0x818, 2, 0));
+	mem.write32(messagePointer + 4, Result::Success);
+	mem.write8(messagePointer + 8, sdInserted ? 1 : 0);
 }
\ No newline at end of file

From 7e93d082017b665b1cca016d3bc6d26e8caac0e3 Mon Sep 17 00:00:00 2001
From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com>
Date: Sat, 8 Jul 2023 20:59:30 +0300
Subject: [PATCH 6/6] Add warning when initializing IR:USER

---
 src/core/services/ir_user.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/core/services/ir_user.cpp b/src/core/services/ir_user.cpp
index c8573182..a4c98a82 100644
--- a/src/core/services/ir_user.cpp
+++ b/src/core/services/ir_user.cpp
@@ -38,6 +38,8 @@ void IRUserService::initializeIrnopShared(u32 messagePointer) {
 	const u32 sharedMemHandle = mem.read32(messagePointer + 32);
 
 	log("IR:USER: InitializeIrnopShared (shared mem size = %08X, sharedMemHandle = %X) (stubbed)\n", sharedMemSize, sharedMemHandle);
+	Helpers::warn("Game is initializing IR:USER. If it explodes, this is probably why");
+
 	mem.write32(messagePointer, IPC::responseHeader(0x18, 1, 0));
 	mem.write32(messagePointer + 4, Result::Success);
 }