From f78acb8049edc03cead982ec6482894c9c00802d Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Tue, 23 Jan 2024 15:32:21 +0200 Subject: [PATCH 1/3] Add more CAM definitions --- include/services/cam.hpp | 6 ++++++ src/core/services/cam.cpp | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/include/services/cam.hpp b/include/services/cam.hpp index 611a3b6d7..9c2170820 100644 --- a/include/services/cam.hpp +++ b/include/services/cam.hpp @@ -23,11 +23,17 @@ class CAMService { // Service commands void driverInitialize(u32 messagePointer); + void driverFinalize(u32 messagePointer); void getMaxLines(u32 messagePointer); void getBufferErrorInterruptEvent(u32 messagePointer); + void getSuitableY2RCoefficients(u32 messagePointer); + void getTransferBytes(u32 messagePointer); void setContrast(u32 messagePointer); void setFrameRate(u32 messagePointer); + void setSize(u32 messagePointer); void setTransferLines(u32 messagePointer); + void setTrimming(u32 messagePointer); + void setTrimminsParamsCenter(u32 messagePointer); public: CAMService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {} diff --git a/src/core/services/cam.cpp b/src/core/services/cam.cpp index a0206077d..56a2c3804 100644 --- a/src/core/services/cam.cpp +++ b/src/core/services/cam.cpp @@ -6,10 +6,16 @@ namespace CAMCommands { enum : u32 { GetBufferErrorInterruptEvent = 0x00060040, DriverInitialize = 0x00390000, + DriverFinalize = 0x003A0000, SetTransferLines = 0x00090100, GetMaxLines = 0x000A0080, + GetTransferBytes = 0x000C0040, + SetTrimming = 0x000E0080, + SetTrimmingParamsCenter = 0x00120140, + SetSize = 0x001F00C0, // Set size has different headers between cam:u and New3DS QTM module SetFrameRate = 0x00200080, SetContrast = 0x00230080, + GetSuitableY2rStandardCoefficient = 0x00360000, }; } From 84b8bc8c48a9922cae3c13ff504e12ee8dbe2ca1 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Tue, 23 Jan 2024 16:29:05 +0200 Subject: [PATCH 2/3] Properly implement camera ports, add more cam commands --- include/services/cam.hpp | 19 ++++- src/core/services/cam.cpp | 150 +++++++++++++++++++++++++++++++++++--- 2 files changed, 153 insertions(+), 16 deletions(-) diff --git a/include/services/cam.hpp b/include/services/cam.hpp index 9c2170820..57cb7e13a 100644 --- a/include/services/cam.hpp +++ b/include/services/cam.hpp @@ -12,14 +12,25 @@ class Kernel; class CAMService { + using Event = std::optional; + + struct Port { + Event bufferErrorInterruptevent = std::nullopt; + u16 transferBytes; + + void reset() { + bufferErrorInterruptevent = std::nullopt; + transferBytes = 256; + } + }; + Handle handle = KernelHandles::CAM; Memory& mem; Kernel& kernel; MAKE_LOG_FUNCTION(log, camLogger) - using Event = std::optional; - static constexpr size_t portCount = 4; // PORT_NONE, PORT_CAM1, PORT_CAM2, PORT_BOTH - std::array bufferErrorInterruptEvents; + static constexpr size_t portCount = 2; + std::array ports; // Service commands void driverInitialize(u32 messagePointer); @@ -33,7 +44,7 @@ class CAMService { void setSize(u32 messagePointer); void setTransferLines(u32 messagePointer); void setTrimming(u32 messagePointer); - void setTrimminsParamsCenter(u32 messagePointer); + void setTrimmingParamsCenter(u32 messagePointer); public: CAMService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {} diff --git a/src/core/services/cam.cpp b/src/core/services/cam.cpp index 56a2c3804..38f3d6d7c 100644 --- a/src/core/services/cam.cpp +++ b/src/core/services/cam.cpp @@ -19,17 +19,65 @@ namespace CAMCommands { }; } -void CAMService::reset() { bufferErrorInterruptEvents.fill(std::nullopt); } +// Helper struct for working with camera ports +class PortSelect { + u32 value; + + public: + PortSelect(u32 val) : value(val) {} + bool isValid() const { return value < 4; } + + bool isSinglePort() const { + // 1 corresponds to the first camera port and 2 corresponds to the second port + return value == 1 || value == 2; + } + + bool isBothPorts() const { + // 3 corresponds to both ports + return value == 3; + } + + // Returns the index of the camera port, assuming that it's only a single port + int getSingleIndex() const { + if (!isSinglePort()) [[unlikely]] { + Helpers::panic("Camera: getSingleIndex called for port with invalid value"); + } + + return value - 1; + } + + std::vector getPortIndices() const { + switch (value) { + case 1: return {0}; // Only port 1 + case 2: return {1}; // Only port 2 + case 3: return {0, 1}; // Both port 1 and port 2 + default: return {}; // No ports or invalid ports + } + } +}; + +void CAMService::reset() { + for (auto& port : ports) { + port.reset(); + } +} void CAMService::handleSyncRequest(u32 messagePointer) { const u32 command = mem.read32(messagePointer); switch (command) { case CAMCommands::DriverInitialize: driverInitialize(messagePointer); break; + case CAMCommands::DriverFinalize: driverFinalize(messagePointer); break; case CAMCommands::GetBufferErrorInterruptEvent: getBufferErrorInterruptEvent(messagePointer); break; case CAMCommands::GetMaxLines: getMaxLines(messagePointer); break; + case CAMCommands::GetSuitableY2rStandardCoefficient: getSuitableY2RCoefficients(messagePointer); break; + case CAMCommands::GetTransferBytes: getTransferBytes(messagePointer); break; case CAMCommands::SetContrast: setContrast(messagePointer); break; case CAMCommands::SetFrameRate: setFrameRate(messagePointer); break; case CAMCommands::SetTransferLines: setTransferLines(messagePointer); break; + case CAMCommands::SetTrimming: setTrimming(messagePointer); break; + case CAMCommands::SetTrimmingParamsCenter: setTrimmingParamsCenter(messagePointer); break; + case CAMCommands::SetSize: setSize(messagePointer); break; + default: Helpers::panic("Unimplemented CAM service requested. Command: %08X\n", command); break; @@ -42,6 +90,12 @@ void CAMService::driverInitialize(u32 messagePointer) { mem.write32(messagePointer + 4, Result::Success); } +void CAMService::driverFinalize(u32 messagePointer) { + log("CAM::DriverFinalize\n"); + mem.write32(messagePointer, IPC::responseHeader(0x3A, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); +} + void CAMService::setContrast(u32 messagePointer) { const u32 cameraSelect = mem.read32(messagePointer + 4); const u32 contrast = mem.read32(messagePointer + 8); @@ -53,12 +107,23 @@ void CAMService::setContrast(u32 messagePointer) { } void CAMService::setTransferLines(u32 messagePointer) { - const u32 port = mem.read32(messagePointer + 4); - const s16 lines = mem.read16(messagePointer + 8); - const s16 width = mem.read16(messagePointer + 12); - const s16 height = mem.read16(messagePointer + 16); + const u32 portIndex = mem.read32(messagePointer + 4); + const u16 lines = mem.read16(messagePointer + 8); + const u16 width = mem.read16(messagePointer + 12); + const u16 height = mem.read16(messagePointer + 16); + const PortSelect port(portIndex); + + if (port.isValid()) { + const u32 transferBytes = lines * width * 2; + + for (int i : port.getPortIndices()) { + ports[i].transferBytes = transferBytes; + } + } else { + Helpers::warn("CAM::SetTransferLines: Invalid port\n"); + } - log("CAM::SetTransferLines (port = %d, lines = %d, width = %d, height = %d)\n", port, lines, width, height); + log("CAM::SetTransferLines (port = %d, lines = %d, width = %d, height = %d)\n", portIndex, lines, width, height); mem.write32(messagePointer, IPC::responseHeader(0x9, 1, 0)); mem.write32(messagePointer + 4, Result::Success); @@ -74,6 +139,41 @@ void CAMService::setFrameRate(u32 messagePointer) { mem.write32(messagePointer + 4, Result::Success); } +void CAMService::setSize(u32 messagePointer) { + const u32 cameraSelect = mem.read32(messagePointer + 4); + const u32 size = mem.read32(messagePointer + 8); + const u32 context = mem.read32(messagePointer + 12); + + log("CAM::SetSize (camera select = %d, size = %d, context = %d)\n", cameraSelect, size, context); + + mem.write32(messagePointer, IPC::responseHeader(0x1F, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); +} + +void CAMService::setTrimming(u32 messagePointer) { + const u32 port = mem.read32(messagePointer + 4); + const bool trim = mem.read8(messagePointer + 8) != 0; + + log("CAM::SetTrimming (port = %d, trimming = %s)\n", port, trim ? "enabled" : "disabled"); + + mem.write32(messagePointer, IPC::responseHeader(0x0E, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); +} + +void CAMService::setTrimmingParamsCenter(u32 messagePointer) { + const u32 port = mem.read32(messagePointer + 4); + const s16 trimWidth = s16(mem.read16(messagePointer + 8)); + const s16 trimHeight = s16(mem.read16(messagePointer + 12)); + const s16 cameraWidth = s16(mem.read16(messagePointer + 16)); + const s16 cameraHeight = s16(mem.read16(messagePointer + 20)); + + log("CAM::SetTrimmingParamsCenter (port = %d), trim size = (%d, %d), camera size = (%d, %d)\n", port, trimWidth, trimHeight, cameraWidth, + cameraHeight); + + mem.write32(messagePointer, IPC::responseHeader(0x12, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); +} + // Algorithm taken from Citra // https://github.com/citra-emu/citra/blob/master/src/core/hle/service/cam/cam.cpp#L465 void CAMService::getMaxLines(u32 messagePointer) { @@ -106,16 +206,40 @@ void CAMService::getMaxLines(u32 messagePointer) { } } +void CAMService::getSuitableY2RCoefficients(u32 messagePointer) { + log("CAM::GetSuitableY2RCoefficients\n"); + mem.write32(messagePointer, IPC::responseHeader(0x36, 2, 0)); + mem.write32(messagePointer + 4, Result::Success); + // Y2R standard coefficient value + mem.write32(messagePointer + 8, 0); +} + +void CAMService::getTransferBytes(u32 messagePointer) { + const u32 portIndex = mem.read32(messagePointer + 4); + const PortSelect port(portIndex); + log("CAM::GetTransferBytes (port = %d)\n", portIndex); + + mem.write32(messagePointer, IPC::responseHeader(0x0C, 2, 0)); + mem.write32(messagePointer + 4, Result::Success); + + if (port.isSinglePort()) { + mem.write32(messagePointer + 8, ports[port.getSingleIndex()].transferBytes); + } else { + // TODO: This should return the proper error code + Helpers::warn("CAM::GetTransferBytes: Invalid port index"); + mem.write32(messagePointer + 8, 0); + } +} + void CAMService::getBufferErrorInterruptEvent(u32 messagePointer) { - const u32 port = mem.read32(messagePointer + 4); - log("CAM::GetBufferErrorInterruptEvent (port = %d)\n", port); + const u32 portIndex = mem.read32(messagePointer + 4); + const PortSelect port(portIndex); + log("CAM::GetBufferErrorInterruptEvent (port = %d)\n", portIndex); mem.write32(messagePointer, IPC::responseHeader(0x6, 1, 2)); - if (port >= portCount) { - Helpers::panic("CAM::GetBufferErrorInterruptEvent: Invalid port"); - } else { - auto& event = bufferErrorInterruptEvents[port]; + if (port.isSinglePort()) { + auto& event = ports[port.getSingleIndex()].bufferErrorInterruptevent; if (!event.has_value()) { event = kernel.makeEvent(ResetType::OneShot); } @@ -123,5 +247,7 @@ void CAMService::getBufferErrorInterruptEvent(u32 messagePointer) { mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 8, 0); mem.write32(messagePointer + 12, event.value()); + } else { + Helpers::panic("CAM::GetBufferErrorInterruptEvent: Invalid port"); } } \ No newline at end of file From a3fda72f88e61ff14330842e697013bd186040db Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Tue, 23 Jan 2024 16:38:18 +0200 Subject: [PATCH 3/3] Add FS::SdmcArchiveResource --- include/services/fs.hpp | 1 + src/core/services/fs.cpp | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/include/services/fs.hpp b/include/services/fs.hpp index 2fe3ba5d3..4d879e7f1 100644 --- a/include/services/fs.hpp +++ b/include/services/fs.hpp @@ -61,6 +61,7 @@ class FSService { void getFreeBytes(u32 messagePointer); void getFormatInfo(u32 messagePointer); void getPriority(u32 messagePointer); + void getSdmcArchiveResource(u32 messagePointer); void getThisSaveDataSecureValue(u32 messagePointer); void theGameboyVCFunction(u32 messagePointer); void initialize(u32 messagePointer); diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 1f3317fbf..264abcabb 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -27,6 +27,7 @@ namespace FSCommands { CloseArchive = 0x080E0080, FormatThisUserSaveData = 0x080F0180, GetFreeBytes = 0x08120080, + GetSdmcArchiveResource = 0x08140000, IsSdmcDetected = 0x08170000, IsSdmcWritable = 0x08180000, CardSlotIsInserted = 0x08210000, @@ -179,6 +180,7 @@ void FSService::handleSyncRequest(u32 messagePointer) { case FSCommands::GetFreeBytes: getFreeBytes(messagePointer); break; case FSCommands::GetFormatInfo: getFormatInfo(messagePointer); break; case FSCommands::GetPriority: getPriority(messagePointer); break; + case FSCommands::GetSdmcArchiveResource: getSdmcArchiveResource(messagePointer); break; case FSCommands::GetThisSaveDataSecureValue: getThisSaveDataSecureValue(messagePointer); break; case FSCommands::Initialize: initialize(messagePointer); break; case FSCommands::InitializeWithSdkVersion: initializeWithSdkVersion(messagePointer); break; @@ -764,3 +766,22 @@ void FSService::renameFile(u32 messagePointer) { const HorizonResult res = sourceArchive->archive->renameFile(sourcePath, destPath); mem.write32(messagePointer + 4, static_cast(res)); } + +void FSService::getSdmcArchiveResource(u32 messagePointer) { + log("FS::GetSdmcArchiveResource"); // For the time being, return the same stubbed archive resource for every media type + + static constexpr ArchiveResource resource = { + .sectorSize = 512, + .clusterSize = 16_KB, + .partitionCapacityInClusters = 0x80000, // 0x80000 * 16 KB = 8GB + .freeSpaceInClusters = 0x80000, // Same here + }; + + mem.write32(messagePointer, IPC::responseHeader(0x814, 5, 0)); + mem.write32(messagePointer + 4, Result::Success); + + mem.write32(messagePointer + 8, resource.sectorSize); + mem.write32(messagePointer + 12, resource.clusterSize); + mem.write32(messagePointer + 16, resource.partitionCapacityInClusters); + mem.write32(messagePointer + 20, resource.freeSpaceInClusters); +} \ No newline at end of file