From 934e9af9a41ae14fe70a4de88534b42276d05827 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Thu, 5 May 2022 00:33:18 -0700 Subject: [PATCH] Implement most of the remaining game card FS shim --- src/LibHac/Fs/GameCard.cs | 18 + src/LibHac/Fs/Shim/GameCard.cs | 503 ++++++++++++++++++++++- tests/LibHac.Tests/Fs/TypeLayoutTests.cs | 11 + 3 files changed, 523 insertions(+), 9 deletions(-) diff --git a/src/LibHac/Fs/GameCard.cs b/src/LibHac/Fs/GameCard.cs index a10d301e..6568c2c4 100644 --- a/src/LibHac/Fs/GameCard.cs +++ b/src/LibHac/Fs/GameCard.cs @@ -24,6 +24,18 @@ public static class GameCard } } +public enum GameCardSize +{ + // ReSharper disable InconsistentNaming + Size1GB = 1, + Size2GB = 2, + Size4GB = 4, + Size8GB = 8, + Size16GB = 16, + Size32GB = 32 + // ReSharper restore InconsistentNaming +} + public enum GameCardSizeInternal : byte { Size1Gb = 0xFA, @@ -83,4 +95,10 @@ public struct GameCardErrorReportInfo public uint ReadCountFromInsert; public uint ReadCountFromAwaken; public Array8 Reserved38; +} + +public struct GameCardUpdatePartitionInfo +{ + public uint CupVersion; + public ulong CupId; } \ No newline at end of file diff --git a/src/LibHac/Fs/Shim/GameCard.cs b/src/LibHac/Fs/Shim/GameCard.cs index 0a38a02f..db78c8b7 100644 --- a/src/LibHac/Fs/Shim/GameCard.cs +++ b/src/LibHac/Fs/Shim/GameCard.cs @@ -5,9 +5,14 @@ using LibHac.Diag; using LibHac.Fs.Fsa; using LibHac.Fs.Impl; using LibHac.FsSrv.Sf; +using LibHac.Gc; using LibHac.Os; +using LibHac.Sf; using LibHac.Util; + using static LibHac.Fs.Impl.AccessLogStrings; +using static LibHac.Gc.Values; + using IFileSystem = LibHac.Fs.Fsa.IFileSystem; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; using IStorageSf = LibHac.FsSrv.Sf.IStorage; @@ -81,7 +86,7 @@ public static class GameCard Result rc = fileSystemProxy.Get.OpenDeviceOperator(ref deviceOperator.Ref()); fs.Impl.AbortIfNeeded(rc); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); rc = deviceOperator.Get.GetGameCardHandle(out GameCardHandle handle); fs.Impl.AbortIfNeeded(rc); @@ -107,7 +112,7 @@ public static class GameCard var sb = new U8StringBuilder(logBuffer, true); sb.Append(LogName).Append(mountName).Append(LogQuote) - .Append(LogGameCardHandle).AppendFormat(handle) + .Append(LogGameCardHandle).AppendFormat(handle, 'X') .Append(LogGameCardPartition).Append(idString.ToString(partitionId)); fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); @@ -118,24 +123,23 @@ public static class GameCard } 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); return Result.Success; - static Result Mount(FileSystemClient fs, U8Span mountName, GameCardHandle handle, - GameCardPartition partitionId) + static Result Mount(FileSystemClient fs, U8Span mountName, GameCardHandle handle, GameCardPartition partitionId) { Result rc = fs.Impl.CheckMountNameAcceptingReservedMountName(mountName); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); using var fileSystem = new SharedRef(); rc = fileSystemProxy.Get.OpenGameCardFileSystem(ref fileSystem.Ref(), handle, partitionId); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); using var fileSystemAdapter = new UniqueRef(new FileSystemServiceObjectAdapter(ref fileSystem.Ref())); @@ -149,7 +153,7 @@ public static class GameCard if (!mountNameGenerator.HasValue) return ResultFs.AllocationMemoryFailedInGameCardD.Log(); - return fs.Register(mountName, ref fileSystemAdapter.Ref(), ref mountNameGenerator.Ref()); + return fs.Register(mountName, ref fileSystemAdapter.Ref(), ref mountNameGenerator.Ref()).Ret(); } } @@ -177,7 +181,7 @@ public static class GameCard Result rc = fileSystemProxy.Get.OpenGameCardStorage(ref storage.Ref(), handle, partitionType); fs.Impl.AbortIfNeeded(rc); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); using var storageAdapter = new UniqueRef(new StorageServiceObjectAdapter(ref storage.Ref())); @@ -187,4 +191,485 @@ public static class GameCard outStorage.Set(ref storageAdapter.Ref()); return Result.Success; } + + public static Result EraseGameCard(this FileSystemClient fs, GameCardSize cardSize, ulong romAreaStartPageAddress) + { + 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.EraseGameCard((uint)cardSize, romAreaStartPageAddress); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result GetGameCardUpdatePartitionInfo(this FileSystemClient fs, + out GameCardUpdatePartitionInfo outPartitionInfo, GameCardHandle handle) + { + outPartitionInfo = default; + + 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.GetGameCardUpdatePartitionInfo(out uint cupVersion, out ulong cupId, handle); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + outPartitionInfo.CupVersion = cupVersion; + outPartitionInfo.CupId = cupId; + + return Result.Success; + } + + public static void FinalizeGameCardDriver(this FileSystemClient fs) + { + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using var deviceOperator = new SharedRef(); + + Result rc = fileSystemProxy.Get.OpenDeviceOperator(ref deviceOperator.Ref()); + fs.Impl.LogResultErrorMessage(rc); + Abort.DoAbortUnless(rc.IsSuccess()); + + rc = deviceOperator.Get.FinalizeGameCardDriver(); + fs.Impl.LogResultErrorMessage(rc); + Abort.DoAbortUnless(rc.IsSuccess()); + } + + public static Result GetGameCardAttribute(this FileSystemClient fs, out GameCardAttribute outAttribute, + GameCardHandle handle) + { + UnsafeHelpers.SkipParamInit(out outAttribute); + + 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.GetGameCardAttribute(out byte gameCardAttribute, handle); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + outAttribute = (GameCardAttribute)gameCardAttribute; + + return Result.Success; + } + + public static Result GetGameCardCompatibilityType(this FileSystemClient fs, + out GameCardCompatibilityType outCompatibilityType, GameCardHandle handle) + { + UnsafeHelpers.SkipParamInit(out outCompatibilityType); + + 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.GetGameCardCompatibilityType(out byte gameCardCompatibilityType, handle); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + outCompatibilityType = (GameCardCompatibilityType)gameCardCompatibilityType; + + return Result.Success; + } + + public static Result GetGameCardDeviceCertificate(this FileSystemClient fs, Span outBuffer, + GameCardHandle handle) + { + 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.GetGameCardDeviceCertificate(new OutBuffer(outBuffer), outBuffer.Length, handle); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result ChallengeCardExistence(this FileSystemClient fs, Span responseBuffer, + ReadOnlySpan challengeSeedBuffer, ReadOnlySpan challengeValueBuffer, GameCardHandle handle) + { + 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.ChallengeCardExistence(new OutBuffer(responseBuffer), new InBuffer(challengeSeedBuffer), + new InBuffer(challengeValueBuffer), handle); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result GetGameCardAsicInfo(this FileSystemClient fs, out RmaInformation outRmaInfo, + ReadOnlySpan asicFirmwareBuffer) + { + UnsafeHelpers.SkipParamInit(out outRmaInfo); + + 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(); + + Unsafe.SkipInit(out RmaInformation rmaInformation); + + rc = deviceOperator.Get.GetGameCardAsicInfo(OutBuffer.FromStruct(ref rmaInformation), + Unsafe.SizeOf(), new InBuffer(asicFirmwareBuffer), asicFirmwareBuffer.Length); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + outRmaInfo = rmaInformation; + + return Result.Success; + } + + public static Result GetGameCardIdSet(this FileSystemClient fs, out GameCardIdSet outGcIdSet) + { + UnsafeHelpers.SkipParamInit(out outGcIdSet); + + 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(); + + Unsafe.SkipInit(out GameCardIdSet gcIdSet); + + rc = deviceOperator.Get.GetGameCardIdSet(OutBuffer.FromStruct(ref gcIdSet), Unsafe.SizeOf()); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + outGcIdSet = gcIdSet; + + return Result.Success; + } + + public static Result GetGameCardCid(this FileSystemClient fs, Span outCidBuffer) + { + Result rc; + + if (outCidBuffer.Length < Unsafe.SizeOf()) + { + rc = ResultFs.InvalidSize.Value; + fs.Impl.AbortIfNeeded(rc); + return rc.Log(); + } + + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using var deviceOperator = new SharedRef(); + + rc = fileSystemProxy.Get.OpenDeviceOperator(ref deviceOperator.Ref()); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + Unsafe.SkipInit(out GameCardIdSet gcIdSet); + + rc = deviceOperator.Get.GetGameCardIdSet(OutBuffer.FromStruct(ref gcIdSet), Unsafe.SizeOf()); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + SpanHelpers.AsByteSpan(ref gcIdSet).CopyTo(outCidBuffer); + + return Result.Success; + } + + public static Result WriteToGameCard(this FileSystemClient fs, long offset, Span buffer) + { + 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.WriteToGameCardDirectly(offset, new OutBuffer(buffer), buffer.Length); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result SetVerifyWriteEnableFlag(this FileSystemClient fs, bool isEnabled) + { + 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.SetVerifyWriteEnableFlag(isEnabled); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result GetGameCardImageHash(this FileSystemClient fs, Span outBuffer, GameCardHandle handle) + { + 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.GetGameCardImageHash(new OutBuffer(outBuffer), outBuffer.Length, handle); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result GetGameCardDeviceIdForProdCard(this FileSystemClient fs, Span outIdBuffer, + ReadOnlySpan devHeaderBuffer) + { + 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.GetGameCardDeviceIdForProdCard(new OutBuffer(outIdBuffer), outIdBuffer.Length, + new InBuffer(devHeaderBuffer), devHeaderBuffer.Length); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result EraseAndWriteParamDirectly(this FileSystemClient fs, ReadOnlySpan devParamBuffer) + { + 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.EraseAndWriteParamDirectly(new InBuffer(devParamBuffer), devParamBuffer.Length); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result ReadParamDirectly(this FileSystemClient fs, Span outBuffer) + { + 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.ReadParamDirectly(new OutBuffer(outBuffer), outBuffer.Length); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result ForceEraseGameCard(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.ForceEraseGameCard(); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result GetGameCardErrorInfo(this FileSystemClient fs, out GameCardErrorInfo outErrorInfo) + { + UnsafeHelpers.SkipParamInit(out outErrorInfo); + + 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.GetGameCardErrorInfo(out GameCardErrorInfo gameCardErrorInfo); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + outErrorInfo = gameCardErrorInfo; + + return Result.Success; + } + + public static Result GetGameCardErrorReportInfo(this FileSystemClient fs, out GameCardErrorReportInfo outErrorInfo) + { + UnsafeHelpers.SkipParamInit(out outErrorInfo); + + 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.GetGameCardErrorReportInfo(out GameCardErrorReportInfo gameCardErrorReportInfo); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + outErrorInfo = gameCardErrorReportInfo; + + return Result.Success; + } + + public static Result CheckGameCardPartitionAvailability(this FileSystemClient fs, GameCardHandle handle, + GameCardPartition partitionId) + { + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using var fileSystem = new SharedRef(); + + Result rc = fileSystemProxy.Get.OpenGameCardFileSystem(ref fileSystem.Ref(), handle, partitionId); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result GetGameCardDeviceId(this FileSystemClient fs, Span outBuffer) + { + Result rc; + + // Note: Nintendo checks for length 8 here rather than GcCardDeviceIdSize (0x10) + if (outBuffer.Length < GcCardDeviceIdSize) + { + rc = ResultFs.InvalidSize.Value; + fs.Impl.AbortIfNeeded(rc); + return rc.Log(); + } + + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using var deviceOperator = new SharedRef(); + + rc = fileSystemProxy.Get.OpenDeviceOperator(ref deviceOperator.Ref()); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + Span buffer = stackalloc byte[GcCardDeviceIdSize]; + + rc = deviceOperator.Get.GetGameCardDeviceId(new OutBuffer(buffer), GcCardDeviceIdSize); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + buffer.CopyTo(outBuffer); + + return Result.Success; + } + + private static Result SetGameCardSimulationEventImpl(FileSystemClient fs, + SimulatingDeviceTargetOperation simulatedOperationType, + SimulatingDeviceAccessFailureEventType simulatedFailureType, Result failureResult, bool autoClearEvent) + { + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using var deviceOperator = new SharedRef(); + + Result rc = fileSystemProxy.Get.OpenDeviceOperator(ref deviceOperator.Ref()); + if (rc.IsFailure()) return rc.Miss(); + + rc = deviceOperator.Get.SetDeviceSimulationEvent((uint)SdmmcPort.GcAsic, (uint)simulatedOperationType, + (uint)simulatedFailureType, failureResult.Value, autoClearEvent); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result SimulateGameCardDetectionEvent(this FileSystemClient fs, SimulatingDeviceDetectionMode mode, + bool signalEvent) + { + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = fileSystemProxy.Get.SimulateDeviceDetectionEvent(SdmmcPort.GcAsic, mode, signalEvent); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result SetGameCardSimulationEvent(this FileSystemClient fs, + SimulatingDeviceTargetOperation simulatedOperationType, + SimulatingDeviceAccessFailureEventType simulatedFailureType) + { + Result rc = SetGameCardSimulationEventImpl(fs, simulatedOperationType, simulatedFailureType, Result.Success, + autoClearEvent: false); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result SetGameCardSimulationEvent(this FileSystemClient fs, + SimulatingDeviceTargetOperation simulatedOperationType, + SimulatingDeviceAccessFailureEventType simulatedFailureType, bool autoClearEvent) + { + Result rc = SetGameCardSimulationEventImpl(fs, simulatedOperationType, simulatedFailureType, Result.Success, + autoClearEvent); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result SetGameCardSimulationEvent(this FileSystemClient fs, + SimulatingDeviceTargetOperation simulatedOperationType, Result failureResult, bool autoClearEvent) + { + Result rc = SetGameCardSimulationEventImpl(fs, simulatedOperationType, + SimulatingDeviceAccessFailureEventType.AccessFailure, failureResult, autoClearEvent); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result ClearGameCardSimulationEvent(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.GcAsic); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } } \ No newline at end of file diff --git a/tests/LibHac.Tests/Fs/TypeLayoutTests.cs b/tests/LibHac.Tests/Fs/TypeLayoutTests.cs index 0c20f71d..5747b841 100644 --- a/tests/LibHac.Tests/Fs/TypeLayoutTests.cs +++ b/tests/LibHac.Tests/Fs/TypeLayoutTests.cs @@ -492,6 +492,17 @@ public class TypeLayoutTests Assert.Equal(0x38, GetOffset(in s, in s.Reserved38)); } + [Fact] + public static void GameCardUpdatePartitionInfo_Layout() + { + var s = new GameCardUpdatePartitionInfo(); + + Assert.Equal(0x10, Unsafe.SizeOf()); + + Assert.Equal(0, GetOffset(in s, in s.CupVersion)); + Assert.Equal(8, GetOffset(in s, in s.CupId)); + } + [Fact] public static void Int64_Layout() {