From 1c9a3ac3d3d9414a7e6b270132fe7aacb786a651 Mon Sep 17 00:00:00 2001
From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com>
Date: Tue, 2 Jul 2024 15:30:38 +0300
Subject: [PATCH 1/5] Add Y2R event delay

---
 include/kernel/kernel.hpp            |  2 ++
 include/scheduler.hpp                |  3 ++-
 include/services/service_manager.hpp |  1 +
 include/services/y2r.hpp             |  6 +++++-
 src/core/kernel/kernel.cpp           |  2 ++
 src/core/services/y2r.cpp            | 32 ++++++++++++++++++++++------
 src/emulator.cpp                     |  2 ++
 7 files changed, 40 insertions(+), 8 deletions(-)

diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp
index fc7fe3f3..e0c0651b 100644
--- a/include/kernel/kernel.hpp
+++ b/include/kernel/kernel.hpp
@@ -15,6 +15,7 @@
 #include "services/service_manager.hpp"
 
 class CPU;
+struct Scheduler;
 
 class Kernel {
 	std::span<u32, 16> regs;
@@ -243,6 +244,7 @@ public:
 	}
 
 	ServiceManager& getServiceManager() { return serviceManager; }
+	Scheduler& getScheduler();
 
 	void sendGPUInterrupt(GPUInterrupt type) { serviceManager.sendGPUInterrupt(type); }
 	void clearInstructionCache();
diff --git a/include/scheduler.hpp b/include/scheduler.hpp
index 97c50afc..cfc4d5e8 100644
--- a/include/scheduler.hpp
+++ b/include/scheduler.hpp
@@ -11,7 +11,8 @@ struct Scheduler {
 		VBlank = 0,          // End of frame event
 		UpdateTimers = 1,    // Update kernel timer objects
 		RunDSP = 2,          // Make the emulated DSP run for one audio frame
-		Panic = 3,           // Dummy event that is always pending and should never be triggered (Timestamp = UINT64_MAX)
+		SignalY2R = 3,       // Signal that a Y2R conversion has finished
+		Panic = 4,           // Dummy event that is always pending and should never be triggered (Timestamp = UINT64_MAX)
 		TotalNumberOfEvents  // How many event types do we have in total?
 	};
 	static constexpr usize totalNumberOfEvents = static_cast<usize>(EventType::TotalNumberOfEvents);
diff --git a/include/services/service_manager.hpp b/include/services/service_manager.hpp
index 8d1cf381..6679f98d 100644
--- a/include/services/service_manager.hpp
+++ b/include/services/service_manager.hpp
@@ -109,4 +109,5 @@ class ServiceManager {
 	HIDService& getHID() { return hid; }
 	NFCService& getNFC() { return nfc; }
 	DSPService& getDSP() { return dsp; }
+	Y2RService& getY2R() { return y2r; }
 };
diff --git a/include/services/y2r.hpp b/include/services/y2r.hpp
index 0cc1d587..4aa96d7b 100644
--- a/include/services/y2r.hpp
+++ b/include/services/y2r.hpp
@@ -113,8 +113,12 @@ class Y2RService {
 	void startConversion(u32 messagePointer);
 	void stopConversion(u32 messagePointer);
 
-public:
+	bool isBusy;
+
+  public:
 	Y2RService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
 	void reset();
 	void handleSyncRequest(u32 messagePointer);
+
+	void signalConversionDone();
 };
\ No newline at end of file
diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp
index 392b87fd..0d1efc15 100644
--- a/src/core/kernel/kernel.cpp
+++ b/src/core/kernel/kernel.cpp
@@ -399,3 +399,5 @@ std::string Kernel::getProcessName(u32 pid) {
 		Helpers::panic("Attempted to name non-current process");
 	}
 }
+
+Scheduler& Kernel::getScheduler() { return cpu.getScheduler(); }
diff --git a/src/core/services/y2r.cpp b/src/core/services/y2r.cpp
index a796631c..ae0961cf 100644
--- a/src/core/services/y2r.cpp
+++ b/src/core/services/y2r.cpp
@@ -61,6 +61,7 @@ void Y2RService::reset() {
 	inputLineWidth = 420;
 
 	conversionCoefficients.fill(0);
+	isBusy = false;
 }
 
 void Y2RService::handleSyncRequest(u32 messagePointer) {
@@ -156,6 +157,11 @@ void Y2RService::setTransferEndInterrupt(u32 messagePointer) {
 void Y2RService::stopConversion(u32 messagePointer) {
 	log("Y2R::StopConversion\n");
 
+	if (isBusy) {
+		isBusy = false;
+		kernel.getScheduler().removeEvent(Scheduler::EventType::SignalY2R);
+	}
+
 	mem.write32(messagePointer, IPC::responseHeader(0x27, 1, 0));
 	mem.write32(messagePointer + 4, Result::Success);
 }
@@ -167,7 +173,7 @@ void Y2RService::isBusyConversion(u32 messagePointer) {
 
 	mem.write32(messagePointer, IPC::responseHeader(0x28, 2, 0));
 	mem.write32(messagePointer + 4, Result::Success);
-	mem.write32(messagePointer + 8, static_cast<u32>(BusyStatus::NotBusy));
+	mem.write32(messagePointer + 8, static_cast<u32>(isBusy ? BusyStatus::Busy : BusyStatus::NotBusy));
 }
 
 void Y2RService::setBlockAlignment(u32 messagePointer) {
@@ -434,11 +440,14 @@ void Y2RService::startConversion(u32 messagePointer) {
 	mem.write32(messagePointer, IPC::responseHeader(0x26, 1, 0));
 	mem.write32(messagePointer + 4, Result::Success);
 
-	// Make Y2R conversion end instantly.
-	// Signal the transfer end event if it's been created. TODO: Is this affected by SetTransferEndInterrupt?
-	if (transferEndEvent.has_value()) {
-		kernel.signalEvent(transferEndEvent.value());
-	}
+	// Schedule Y2R conversion end event.
+	static constexpr u64 delayTicks = 60'000;
+	isBusy = true;
+
+	// Remove any potential pending Y2R event and schedule a new one
+	Scheduler& scheduler = kernel.getScheduler();
+	scheduler.removeEvent(Scheduler::EventType::SignalY2R);
+	scheduler.addEvent(Scheduler::EventType::SignalY2R, scheduler.currentTimestamp + delayTicks);
 }
 
 void Y2RService::isFinishedSendingYUV(u32 messagePointer) {
@@ -484,4 +493,15 @@ void Y2RService::isFinishedReceiving(u32 messagePointer) {
 	mem.write32(messagePointer, IPC::responseHeader(0x17, 2, 0));
 	mem.write32(messagePointer + 4, Result::Success);
 	mem.write32(messagePointer + 8, finished ? 1 : 0);
+}
+
+void Y2RService::signalConversionDone() {
+	if (isBusy) {
+		isBusy = false;
+
+		// Signal the transfer end event if it's been created. TODO: Is this affected by SetTransferEndInterrupt?
+		if (transferEndEvent.has_value()) {
+			kernel.signalEvent(transferEndEvent.value());
+		}
+	}
 }
\ No newline at end of file
diff --git a/src/emulator.cpp b/src/emulator.cpp
index 16c3bffd..af156eeb 100644
--- a/src/emulator.cpp
+++ b/src/emulator.cpp
@@ -169,6 +169,8 @@ void Emulator::pollScheduler() {
 				break;
 			}
 
+			case Scheduler::EventType::SignalY2R: kernel.getServiceManager().getY2R().signalConversionDone(); break;
+
 			default: {
 				Helpers::panic("Scheduler: Unimplemented event type received: %d\n", static_cast<int>(eventType));
 				break;

From d4cf54d56cafaf1ae06d26c48e9a4f0ca1596401 Mon Sep 17 00:00:00 2001
From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com>
Date: Tue, 2 Jul 2024 15:57:39 +0300
Subject: [PATCH 2/5] Tweak Y2R timings

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

diff --git a/src/core/services/y2r.cpp b/src/core/services/y2r.cpp
index ae0961cf..1c7b33cd 100644
--- a/src/core/services/y2r.cpp
+++ b/src/core/services/y2r.cpp
@@ -441,7 +441,8 @@ void Y2RService::startConversion(u32 messagePointer) {
 	mem.write32(messagePointer + 4, Result::Success);
 
 	// Schedule Y2R conversion end event.
-	static constexpr u64 delayTicks = 60'000;
+	// The tick value is tweaked based on the minimum delay needed to get FIFA 15 to not hang due to a race condition on its title screen
+	static constexpr u64 delayTicks = 1'350'000;
 	isBusy = true;
 
 	// Remove any potential pending Y2R event and schedule a new one

From 800c11ff62a4893dc07e6c0b3eb760394befa9b4 Mon Sep 17 00:00:00 2001
From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com>
Date: Tue, 2 Jul 2024 17:19:20 +0300
Subject: [PATCH 3/5] HLE DSP: Add PCM8 audio decoding

---
 include/audio/hle_core.hpp  |  1 +
 src/core/audio/hle_core.cpp | 22 +++++++++++++++++++++-
 2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/include/audio/hle_core.hpp b/include/audio/hle_core.hpp
index c57f221e..b59dc811 100644
--- a/include/audio/hle_core.hpp
+++ b/include/audio/hle_core.hpp
@@ -176,6 +176,7 @@ namespace Audio {
 		// Decode an entire buffer worth of audio
 		void decodeBuffer(DSPSource& source);
 
+		SampleBuffer decodePCM8(const u8* data, usize sampleCount, Source& source);
 		SampleBuffer decodePCM16(const u8* data, usize sampleCount, Source& source);
 		SampleBuffer decodeADPCM(const u8* data, usize sampleCount, Source& source);
 
diff --git a/src/core/audio/hle_core.cpp b/src/core/audio/hle_core.cpp
index 146c7bdf..12c8f4c8 100644
--- a/src/core/audio/hle_core.cpp
+++ b/src/core/audio/hle_core.cpp
@@ -355,7 +355,7 @@ namespace Audio {
 		}
 
 		switch (buffer.format) {
-			case SampleFormat::PCM8: Helpers::warn("Unimplemented sample format!"); break;
+			case SampleFormat::PCM8: source.currentSamples = decodePCM8(data, buffer.sampleCount, source); break;
 			case SampleFormat::PCM16: source.currentSamples = decodePCM16(data, buffer.sampleCount, source); break;
 			case SampleFormat::ADPCM: source.currentSamples = decodeADPCM(data, buffer.sampleCount, source); break;
 
@@ -406,6 +406,26 @@ namespace Audio {
 		}
 	}
 
+	HLE_DSP::SampleBuffer HLE_DSP::decodePCM8(const u8* data, usize sampleCount, Source& source) {
+		SampleBuffer decodedSamples(sampleCount);
+
+		if (source.sourceType == SourceType::Stereo) {
+			for (usize i = 0; i < sampleCount; i++) {
+				const s16 left = s16(u16(*data++) << 8);
+				const s16 right = s16(u16(*data++) << 8);
+				decodedSamples[i] = {left, right};
+			}
+		} else {
+			// Mono
+			for (usize i = 0; i < sampleCount; i++) {
+				const s16 sample = s16(u16(*data++) << 8);
+				decodedSamples[i] = {sample, sample};
+			}
+		}
+
+		return decodedSamples;
+	}
+
 	HLE_DSP::SampleBuffer HLE_DSP::decodePCM16(const u8* data, usize sampleCount, Source& source) {
 		SampleBuffer decodedSamples(sampleCount);
 		const s16* data16 = reinterpret_cast<const s16*>(data);

From de9375122b012ab357a0bf54064422f6e2025c0a Mon Sep 17 00:00:00 2001
From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com>
Date: Tue, 2 Jul 2024 17:30:51 +0300
Subject: [PATCH 4/5] Add SDMC::DeleteFile

---
 src/core/fs/archive_sdmc.cpp | 30 +++++++++++++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/src/core/fs/archive_sdmc.cpp b/src/core/fs/archive_sdmc.cpp
index 6c34de7a..8fda1320 100644
--- a/src/core/fs/archive_sdmc.cpp
+++ b/src/core/fs/archive_sdmc.cpp
@@ -39,7 +39,35 @@ HorizonResult SDMCArchive::createFile(const FSPath& path, u64 size) {
 }
 
 HorizonResult SDMCArchive::deleteFile(const FSPath& path) {
-	Helpers::panic("[SDMC] Unimplemented DeleteFile");
+	if (path.type == PathType::UTF16) {
+		if (!isPathSafe<PathType::UTF16>(path)) {
+			Helpers::panic("Unsafe path in SDMC::DeleteFile");
+		}
+
+		fs::path p = IOFile::getAppData() / "SDMC";
+		p += fs::path(path.utf16_string).make_preferred();
+
+		if (fs::is_directory(p)) {
+			Helpers::panic("SDMC::DeleteFile: Tried to delete directory");
+		}
+
+		if (!fs::is_regular_file(p)) {
+			return Result::FS::FileNotFoundAlt;
+		}
+
+		std::error_code ec;
+		bool success = fs::remove(p, ec);
+
+		// It might still be possible for fs::remove to fail, if there's eg an open handle to a file being deleted
+		// In this case, print a warning, but still return success for now
+		if (!success) {
+			Helpers::warn("SDMC::DeleteFile: fs::remove failed\n");
+		}
+
+		return Result::Success;
+	}
+
+	Helpers::panic("SaveDataArchive::DeleteFile: Unknown path type");
 	return Result::Success;
 }
 

From 0fe62f9b46153e0d6f72571650454814ae0e7cf1 Mon Sep 17 00:00:00 2001
From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com>
Date: Tue, 2 Jul 2024 17:32:17 +0300
Subject: [PATCH 5/5] Correct archive names

---
 src/core/fs/archive_sdmc.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/core/fs/archive_sdmc.cpp b/src/core/fs/archive_sdmc.cpp
index 8fda1320..97b02b9e 100644
--- a/src/core/fs/archive_sdmc.cpp
+++ b/src/core/fs/archive_sdmc.cpp
@@ -67,7 +67,7 @@ HorizonResult SDMCArchive::deleteFile(const FSPath& path) {
 		return Result::Success;
 	}
 
-	Helpers::panic("SaveDataArchive::DeleteFile: Unknown path type");
+	Helpers::panic("SDMCArchive::DeleteFile: Unknown path type");
 	return Result::Success;
 }
 
@@ -173,7 +173,7 @@ Rust::Result<DirectorySession, HorizonResult> SDMCArchive::openDirectory(const F
 
 	if (path.type == PathType::UTF16) {
 		if (!isPathSafe<PathType::UTF16>(path)) {
-			Helpers::panic("Unsafe path in SaveData::OpenDirectory");
+			Helpers::panic("Unsafe path in SDMC::OpenDirectory");
 		}
 
 		fs::path p = IOFile::getAppData() / "SDMC";