diff --git a/src/LibHac/Fs/Shim/SdCard.cs b/src/LibHac/Fs/Shim/SdCard.cs index 6c461f37..91af5574 100644 --- a/src/LibHac/Fs/Shim/SdCard.cs +++ b/src/LibHac/Fs/Shim/SdCard.cs @@ -6,6 +6,7 @@ using LibHac.Fs.Fsa; using LibHac.Fs.Impl; using LibHac.FsSrv.Sf; using LibHac.Os; +using LibHac.Sf; using static LibHac.Fs.Impl.AccessLogStrings; using IFileSystem = LibHac.Fs.Fsa.IFileSystem; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; @@ -23,9 +24,9 @@ public static class SdCard { using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); - return OpenFileSystem(fs, ref fileSystemProxy.Ref(), ref outFileSystem); + return OpenFileSystem(fs, in fileSystemProxy, ref outFileSystem); - static Result OpenFileSystem(FileSystemClient fs, ref SharedRef fileSystemProxy, + static Result OpenFileSystem(FileSystemClient fs, in SharedRef fileSystemProxy, ref SharedRef outFileSystem) { // Retry a few times if the storage device isn't ready yet @@ -88,7 +89,7 @@ public static class SdCard } fs.Impl.AbortIfNeeded(rc); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); // Open the SD card file system using var fileSystem = new SharedRef(); @@ -106,8 +107,7 @@ public static class SdCard rc = OpenSdCardFileSystem(fs, ref fileSystem.Ref()); } - fs.Impl.AbortIfNeeded(rc); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); // Mount the file system if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) @@ -124,7 +124,7 @@ public static class SdCard } fs.Impl.AbortIfNeeded(rc); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) fs.Impl.EnableFileSystemAccessorAccessLog(mountName); @@ -132,6 +132,73 @@ public static class SdCard return Result.Success; } + public static Result MountSdCardForDebug(this FileSystemClient fs, U8Span mountName) + { + Result rc; + Span logBuffer = stackalloc byte[0x30]; + + // Check if the mount name is valid + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.CheckMountName(mountName); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogName).Append(mountName); + logBuffer = sb.Buffer; + + fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer)); + } + else + { + rc = fs.Impl.CheckMountName(mountName); + } + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + // Open the SD card file system + using var fileSystem = new SharedRef(); + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = OpenSdCardFileSystem(fs, ref fileSystem.Ref()); + Tick end = fs.Hos.Os.GetSystemTick(); + + fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer)); + } + else + { + rc = OpenSdCardFileSystem(fs, ref fileSystem.Ref()); + } + + if (rc.IsFailure()) return rc.Miss(); + + // Mount the file system + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = RegisterFileSystem(fs, mountName, ref fileSystem.Ref()); + Tick end = fs.Hos.Os.GetSystemTick(); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer)); + } + else + { + rc = RegisterFileSystem(fs, mountName, ref fileSystem.Ref()); + } + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) + fs.Impl.EnableFileSystemAccessorAccessLog(mountName); + + return Result.Success; + } + public static bool IsSdCardInserted(this FileSystemClient fs) { using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); @@ -176,6 +243,293 @@ public static class SdCard } } + public static Result GetSdCardSpeedMode(this FileSystemClient fs, out SdCardSpeedMode outMode) + { + UnsafeHelpers.SkipParamInit(out outMode); + + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using var deviceOperator = new SharedRef(); + + Result rc = fileSystemProxy.Get.OpenDeviceOperator(ref deviceOperator.Ref()); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + rc = GetSpeedMode(fs, in deviceOperator, out long speedMode); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + outMode = (SdCardSpeedMode)speedMode; + return Result.Success; + + static Result GetSpeedMode(FileSystemClient fs, in SharedRef deviceOperator, + out long outSpeedMode) + { + outSpeedMode = 0; + + // Retry a few times if the storage device isn't ready yet + const int maxRetries = 10; + const int retryInterval = 1000; + + for (int i = 0; i < maxRetries; i++) + { + Result rc = deviceOperator.Get.GetSdCardSpeedMode(out outSpeedMode); + + if (rc.IsSuccess()) + break; + + if (!ResultFs.StorageDeviceNotReady.Includes(rc)) + return rc; + + if (i == maxRetries - 1) + return rc; + + fs.Hos.Os.SleepThread(TimeSpan.FromMilliSeconds(retryInterval)); + } + + return Result.Success; + } + } + + public static Result GetSdCardCid(this FileSystemClient fs, Span outCidBuffer) + { + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using var deviceOperator = new SharedRef(); + + Result rc = fileSystemProxy.Get.OpenDeviceOperator(ref deviceOperator.Ref()); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + rc = GetCid(fs, in deviceOperator, outCidBuffer); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + + static Result GetCid(FileSystemClient fs, in SharedRef deviceOperator, Span outCidBuffer) + { + // Retry a few times if the storage device isn't ready yet + const int maxRetries = 10; + const int retryInterval = 1000; + + for (int i = 0; i < maxRetries; i++) + { + Result rc = deviceOperator.Get.GetSdCardCid(new OutBuffer(outCidBuffer), outCidBuffer.Length); + + if (rc.IsSuccess()) + break; + + if (!ResultFs.StorageDeviceNotReady.Includes(rc)) + return rc; + + if (i == maxRetries - 1) + return rc; + + fs.Hos.Os.SleepThread(TimeSpan.FromMilliSeconds(retryInterval)); + } + + return Result.Success; + } + } + + public static Result GetSdCardUserAreaSize(this FileSystemClient fs, out long outSize) + { + UnsafeHelpers.SkipParamInit(out outSize); + + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using var deviceOperator = new SharedRef(); + + Result rc = fileSystemProxy.Get.OpenDeviceOperator(ref deviceOperator.Ref()); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + rc = GetUserAreaSize(fs, in deviceOperator, out outSize); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + + static Result GetUserAreaSize(FileSystemClient fs, in SharedRef deviceOperator, + out long outSize) + { + outSize = 0; + + // Retry a few times if the storage device isn't ready yet + const int maxRetries = 10; + const int retryInterval = 1000; + + for (int i = 0; i < maxRetries; i++) + { + Result rc = deviceOperator.Get.GetSdCardUserAreaSize(out outSize); + + if (rc.IsSuccess()) + break; + + if (!ResultFs.StorageDeviceNotReady.Includes(rc)) + return rc; + + if (i == maxRetries - 1) + return rc; + + fs.Hos.Os.SleepThread(TimeSpan.FromMilliSeconds(retryInterval)); + } + + return Result.Success; + } + } + + public static Result GetSdCardProtectedAreaSize(this FileSystemClient fs, out long outSize) + { + UnsafeHelpers.SkipParamInit(out outSize); + + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using var deviceOperator = new SharedRef(); + + Result rc = fileSystemProxy.Get.OpenDeviceOperator(ref deviceOperator.Ref()); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + rc = GetProtectedAreaSize(fs, in deviceOperator, out outSize); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + + static Result GetProtectedAreaSize(FileSystemClient fs, in SharedRef deviceOperator, + out long outSize) + { + outSize = 0; + + // Retry a few times if the storage device isn't ready yet + const int maxRetries = 10; + const int retryInterval = 1000; + + for (int i = 0; i < maxRetries; i++) + { + Result rc = deviceOperator.Get.GetSdCardProtectedAreaSize(out outSize); + + if (rc.IsSuccess()) + break; + + if (!ResultFs.StorageDeviceNotReady.Includes(rc)) + return rc; + + if (i == maxRetries - 1) + return rc; + + fs.Hos.Os.SleepThread(TimeSpan.FromMilliSeconds(retryInterval)); + } + + return Result.Success; + } + } + + public static Result GetAndClearMmcErrorInfo(this FileSystemClient fs, out StorageErrorInfo outErrorInfo, + out long outLogSize, Span logBuffer) + { + UnsafeHelpers.SkipParamInit(out outErrorInfo, out outLogSize); + + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using var deviceOperator = new SharedRef(); + + Result rc = fileSystemProxy.Get.OpenDeviceOperator(ref deviceOperator.Ref()); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + rc = GetErrorInfo(fs, in deviceOperator, out outErrorInfo, out long logSize, logBuffer); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + outLogSize = logSize; + return Result.Success; + + static Result GetErrorInfo(FileSystemClient fs, in SharedRef deviceOperator, + out StorageErrorInfo outErrorInfo, out long outLogSize, Span logBuffer) + { + UnsafeHelpers.SkipParamInit(out outErrorInfo, out outLogSize); + + // Retry a few times if the storage device isn't ready yet + const int maxRetries = 10; + const int retryInterval = 1000; + + for (int i = 0; i < maxRetries; i++) + { + Result rc = deviceOperator.Get.GetAndClearSdCardErrorInfo(out outErrorInfo, out outLogSize, + new OutBuffer(logBuffer), logBuffer.Length); + + if (rc.IsSuccess()) + break; + + if (!ResultFs.StorageDeviceNotReady.Includes(rc)) + return rc; + + if (i == maxRetries - 1) + return rc; + + fs.Hos.Os.SleepThread(TimeSpan.FromMilliSeconds(retryInterval)); + } + + return Result.Success; + } + } + + public static Result FormatSdCard(this FileSystemClient fs) + { + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = Format(fs, in fileSystemProxy); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + + static Result Format(FileSystemClient fs, in SharedRef fileSystemProxy) + { + // Retry a few times if the storage device isn't ready yet + const int maxRetries = 10; + const int retryInterval = 1000; + + for (int i = 0; i < maxRetries; i++) + { + Result rc = fileSystemProxy.Get.FormatSdCardFileSystem(); + + if (rc.IsSuccess()) + break; + + if (!ResultFs.StorageDeviceNotReady.Includes(rc)) + return rc; + + if (i == maxRetries - 1) + return rc; + + fs.Hos.Os.SleepThread(TimeSpan.FromMilliSeconds(retryInterval)); + } + + return Result.Success; + } + } + + public static Result FormatSdCardDryRun(this FileSystemClient fs) + { + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = fileSystemProxy.Get.FormatSdCardDryRun(); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static bool IsExFatSupported(this FileSystemClient fs) + { + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = fileSystemProxy.Get.IsExFatSupported(out bool isSupported); + fs.Impl.LogResultErrorMessage(rc); + Abort.DoAbortUnless(rc.IsSuccess()); + + return isSupported; + } + public static Result SetSdCardEncryptionSeed(this FileSystemClient fs, in EncryptionSeed seed) { using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); @@ -203,6 +557,76 @@ public static class SdCard return isAccessible; } + private static Result SetSdCardSimulationEvent(FileSystemClient fs, + SimulatingDeviceTargetOperation simulatedOperationType, + SimulatingDeviceAccessFailureEventType simulatedFailureType, Result failureResult, bool autoClearEvent) + { + throw new NotImplementedException(); + } + + public static Result SimulateSdCardDetectionEvent(this FileSystemClient fs, SimulatingDeviceDetectionMode mode, + bool signalEvent) + { + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = fileSystemProxy.Get.SimulateDeviceDetectionEvent(SdmmcPort.SdCard, mode, signalEvent); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result SetSdCardSimulationEvent(this FileSystemClient fs, + SimulatingDeviceTargetOperation simulatedOperationType, + SimulatingDeviceAccessFailureEventType simulatedFailureType) + { + Result rc = SetSdCardSimulationEvent(fs, simulatedOperationType, simulatedFailureType, Result.Success, + autoClearEvent: false); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result SetSdCardSimulationEvent(this FileSystemClient fs, + SimulatingDeviceTargetOperation simulatedOperationType, + SimulatingDeviceAccessFailureEventType simulatedFailureType, bool autoClearEvent) + { + Result rc = SetSdCardSimulationEvent(fs, simulatedOperationType, simulatedFailureType, Result.Success, + autoClearEvent); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result SetSdCardSimulationEvent(this FileSystemClient fs, + SimulatingDeviceTargetOperation simulatedOperationType, Result failureResult, bool autoClearEvent) + { + Result rc = SetSdCardSimulationEvent(fs, simulatedOperationType, + SimulatingDeviceAccessFailureEventType.AccessFailure, failureResult, autoClearEvent); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result ClearSdCardSimulationEvent(this FileSystemClient fs) + { + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using var deviceOperator = new SharedRef(); + + Result rc = fileSystemProxy.Get.OpenDeviceOperator(ref deviceOperator.Ref()); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + rc = deviceOperator.Get.ClearDeviceSimulationEvent((uint)SdmmcPort.SdCard); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + public static Result SetSdCardAccessibility(this FileSystemClientImpl fs, bool isAccessible) { using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); diff --git a/src/LibHac/Fs/Shim/SdmmcControl.cs b/src/LibHac/Fs/Shim/SdmmcControl.cs index c7c7ab7b..e92a4b3e 100644 --- a/src/LibHac/Fs/Shim/SdmmcControl.cs +++ b/src/LibHac/Fs/Shim/SdmmcControl.cs @@ -1,4 +1,5 @@ -using System; +using LibHac.Common; +using LibHac.FsSrv.Sf; namespace LibHac.Fs.Shim; @@ -8,19 +9,56 @@ namespace LibHac.Fs.Shim; /// Based on nnSdk 14.3.0 public static class SdmmcControl { - public static Result GetSdmmcConnectionStatus(this FileSystemClient fs, out SdmmcSpeedMode speedMode, - out SdmmcBusWidth busWidth, SdmmcPort port) + public static Result GetSdmmcConnectionStatus(this FileSystemClient fs, out SdmmcSpeedMode outSpeedMode, + out SdmmcBusWidth outBusWidth, SdmmcPort port) { - throw new NotImplementedException(); + UnsafeHelpers.SkipParamInit(out outSpeedMode, out outBusWidth); + + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using var deviceOperator = new SharedRef(); + + Result rc = fileSystemProxy.Get.OpenDeviceOperator(ref deviceOperator.Ref()); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + rc = deviceOperator.Get.GetSdmmcConnectionStatus(out int speedMode, out int busWidth, (int)port); + if (rc.IsFailure()) return rc.Miss(); + + outSpeedMode = (SdmmcSpeedMode)speedMode; + outBusWidth = (SdmmcBusWidth)busWidth; + + return Result.Success; } - public static Result SuspendSdmmcControl() + public static Result SuspendSdmmcControl(this FileSystemClient fs) { - throw new NotImplementedException(); + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using var deviceOperator = new SharedRef(); + + Result rc = fileSystemProxy.Get.OpenDeviceOperator(ref deviceOperator.Ref()); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + rc = deviceOperator.Get.SuspendSdmmcControl(); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; } - public static Result ResumeSdmmcControl() + public static Result ResumeSdmmcControl(this FileSystemClient fs) { - throw new NotImplementedException(); + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using var deviceOperator = new SharedRef(); + + Result rc = fileSystemProxy.Get.OpenDeviceOperator(ref deviceOperator.Ref()); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + rc = deviceOperator.Get.ResumeSdmmcControl(); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; } } \ No newline at end of file diff --git a/src/LibHac/Fs/Shim/SpeedEmulation.cs b/src/LibHac/Fs/Shim/SpeedEmulation.cs index a95fb0ae..f0908414 100644 --- a/src/LibHac/Fs/Shim/SpeedEmulation.cs +++ b/src/LibHac/Fs/Shim/SpeedEmulation.cs @@ -1,4 +1,5 @@ -using System; +using LibHac.Common; +using LibHac.FsSrv.Sf; namespace LibHac.Fs { @@ -17,12 +18,38 @@ namespace LibHac.Fs.Shim { public static Result SetSpeedEmulationMode(this FileSystemClient fs, SpeedEmulationMode mode) { - throw new NotImplementedException(); + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using var deviceOperator = new SharedRef(); + + Result rc = fileSystemProxy.Get.OpenDeviceOperator(ref deviceOperator.Ref()); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + rc = deviceOperator.Get.SetSpeedEmulationMode((int)mode); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; } public static Result GetSpeedEmulationMode(this FileSystemClient fs, out SpeedEmulationMode outMode) { - throw new NotImplementedException(); + UnsafeHelpers.SkipParamInit(out outMode); + + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using var deviceOperator = new SharedRef(); + + Result rc = fileSystemProxy.Get.OpenDeviceOperator(ref deviceOperator.Ref()); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + rc = deviceOperator.Get.GetSpeedEmulationMode(out int mode); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + outMode = (SpeedEmulationMode)mode; + + return Result.Success; } } } \ No newline at end of file