diff --git a/include/audio/hle_core.hpp b/include/audio/hle_core.hpp index 44c0ba20..26b78cd2 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/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 2a10d8e1..199d978a 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 regs; @@ -241,6 +242,7 @@ class Kernel { } 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(EventType::TotalNumberOfEvents); diff --git a/include/services/service_manager.hpp b/include/services/service_manager.hpp index c85f9950..335dc5d5 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 0e070242..9b0f4f1e 100644 --- a/include/services/y2r.hpp +++ b/include/services/y2r.hpp @@ -101,8 +101,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(); }; 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(data); diff --git a/src/core/fs/archive_sdmc.cpp b/src/core/fs/archive_sdmc.cpp index 6c34de7a..97b02b9e 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(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("SDMCArchive::DeleteFile: Unknown path type"); return Result::Success; } @@ -145,7 +173,7 @@ Rust::Result SDMCArchive::openDirectory(const F if (path.type == PathType::UTF16) { if (!isPathSafe(path)) { - Helpers::panic("Unsafe path in SaveData::OpenDirectory"); + Helpers::panic("Unsafe path in SDMC::OpenDirectory"); } fs::path p = IOFile::getAppData() / "SDMC"; diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index 55eb9a81..978b8194 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -398,3 +398,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..1c7b33cd 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(BusyStatus::NotBusy)); + mem.write32(messagePointer + 8, static_cast(isBusy ? BusyStatus::Busy : BusyStatus::NotBusy)); } void Y2RService::setBlockAlignment(u32 messagePointer) { @@ -434,11 +440,15 @@ 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. + // 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 + Scheduler& scheduler = kernel.getScheduler(); + scheduler.removeEvent(Scheduler::EventType::SignalY2R); + scheduler.addEvent(Scheduler::EventType::SignalY2R, scheduler.currentTimestamp + delayTicks); } void Y2RService::isFinishedSendingYUV(u32 messagePointer) { @@ -484,4 +494,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(eventType)); break;