mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add GameCardService
This commit is contained in:
parent
2fb3d88261
commit
49759d3d10
15 changed files with 984 additions and 36 deletions
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using LibHac.Common.FixedArrays;
|
||||
|
||||
namespace LibHac.Fs;
|
||||
|
||||
|
@ -41,3 +42,51 @@ public enum GameCardAttribute : byte
|
|||
DifferentRegionCupToTerraDeviceFlag = 1 << 3,
|
||||
DifferentRegionCupToGlobalDeviceFlag = 1 << 4
|
||||
}
|
||||
|
||||
public struct GameCardErrorInfo
|
||||
{
|
||||
public ushort GameCardCrcErrorCount;
|
||||
public ushort Reserved2;
|
||||
public ushort AsicCrcErrorCount;
|
||||
public ushort Reserved6;
|
||||
public ushort RefreshCount;
|
||||
public ushort ReservedA;
|
||||
public ushort ReadRetryCount;
|
||||
public ushort TimeoutRetryErrorCount;
|
||||
}
|
||||
|
||||
public struct GameCardErrorReportInfo
|
||||
{
|
||||
public GameCardErrorInfo ErrorInfo;
|
||||
public ushort AsicReinitializeFailureDetail;
|
||||
public ushort InsertionCount;
|
||||
public ushort RemovalCount;
|
||||
public ushort AsicReinitializeCount;
|
||||
public uint AsicInitializeCount;
|
||||
public ushort AsicReinitializeFailureCount;
|
||||
public ushort AwakenFailureCount;
|
||||
public ushort Reserved20;
|
||||
public ushort RefreshCount;
|
||||
public uint LastReadErrorPageAddress;
|
||||
public uint LastReadErrorPageCount;
|
||||
public uint AwakenCount;
|
||||
public uint ReadCountFromInsert;
|
||||
public uint ReadCountFromAwaken;
|
||||
public Array8<byte> Reserved38;
|
||||
}
|
||||
|
||||
public readonly struct GameCardHandle : IEquatable<GameCardHandle>
|
||||
{
|
||||
public readonly int Value;
|
||||
|
||||
public GameCardHandle(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) => obj is GameCardHandle handle && Equals(handle);
|
||||
public bool Equals(GameCardHandle other) => Value == other.Value;
|
||||
public override int GetHashCode() => Value.GetHashCode();
|
||||
public static bool operator ==(GameCardHandle left, GameCardHandle right) => left.Equals(right);
|
||||
public static bool operator !=(GameCardHandle left, GameCardHandle right) => !(left == right);
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.FsSrv.Storage.Sf;
|
||||
using LibHac.Sf;
|
||||
using IStorageSf = LibHac.FsSrv.Sf.IStorage;
|
||||
|
||||
|
@ -20,6 +21,11 @@ internal class StorageServiceObjectAdapter : IStorage
|
|||
_baseStorage = SharedRef<IStorageSf>.CreateMove(ref baseStorage);
|
||||
}
|
||||
|
||||
public StorageServiceObjectAdapter(ref SharedRef<IStorageDevice> baseStorage)
|
||||
{
|
||||
_baseStorage = SharedRef<IStorageSf>.CreateMove(ref baseStorage);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
_baseStorage.Destroy();
|
||||
|
|
|
@ -4,7 +4,6 @@ using LibHac.Common;
|
|||
using LibHac.Diag;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.FsSrv;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Os;
|
||||
using LibHac.Util;
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
using LibHac.FsSrv.Impl;
|
||||
using System;
|
||||
using LibHac.FsSrv.Impl;
|
||||
using LibHac.FsSrv.Storage;
|
||||
using LibHac.FsSystem;
|
||||
|
||||
namespace LibHac.FsSrv;
|
||||
|
||||
public class FileSystemServer
|
||||
public class FileSystemServer : IDisposable
|
||||
{
|
||||
internal FileSystemServerGlobals Globals;
|
||||
|
||||
|
@ -20,9 +21,15 @@ public class FileSystemServer
|
|||
{
|
||||
Globals.Initialize(horizonClient, this);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Globals.Dispose();
|
||||
Globals = default;
|
||||
}
|
||||
}
|
||||
|
||||
internal struct FileSystemServerGlobals
|
||||
internal struct FileSystemServerGlobals : IDisposable
|
||||
{
|
||||
public HorizonClient Hos;
|
||||
public object InitMutex;
|
||||
|
@ -36,6 +43,7 @@ internal struct FileSystemServerGlobals
|
|||
public MultiCommitManagerGlobals MultiCommitManager;
|
||||
public LocationResolverSetGlobals LocationResolverSet;
|
||||
public PooledBufferGlobals PooledBuffer;
|
||||
public GameCardServiceGlobals GameCardService;
|
||||
|
||||
public void Initialize(HorizonClient horizonClient, FileSystemServer fsServer)
|
||||
{
|
||||
|
@ -46,6 +54,12 @@ internal struct FileSystemServerGlobals
|
|||
MultiCommitManager.Initialize();
|
||||
LocationResolverSet.Initialize();
|
||||
PooledBuffer.Initialize();
|
||||
GameCardService.Initialize();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GameCardService.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace LibHac.FsSrv;
|
||||
|
||||
public readonly struct GameCardHandle : IEquatable<GameCardHandle>
|
||||
{
|
||||
public readonly int Value;
|
||||
|
||||
public GameCardHandle(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) => obj is GameCardHandle handle && Equals(handle);
|
||||
public bool Equals(GameCardHandle other) => Value == other.Value;
|
||||
public override int GetHashCode() => Value.GetHashCode();
|
||||
public static bool operator ==(GameCardHandle left, GameCardHandle right) => left.Equals(right);
|
||||
public static bool operator !=(GameCardHandle left, GameCardHandle right) => !(left == right);
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using LibHac.Fs;
|
||||
|
||||
namespace LibHac.FsSrv.Sf;
|
||||
|
||||
|
@ -7,4 +8,4 @@ public interface IDeviceOperator : IDisposable
|
|||
Result IsSdCardInserted(out bool isInserted);
|
||||
Result IsGameCardInserted(out bool isInserted);
|
||||
Result GetGameCardHandle(out GameCardHandle handle);
|
||||
}
|
||||
}
|
616
src/LibHac/FsSrv/Storage/GameCardService.cs
Normal file
616
src/LibHac/FsSrv/Storage/GameCardService.cs
Normal file
|
@ -0,0 +1,616 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.FsSrv.Impl;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.FsSrv.Storage.Sf;
|
||||
using LibHac.Gc;
|
||||
using LibHac.GcSrv;
|
||||
using LibHac.Os;
|
||||
using LibHac.Sf;
|
||||
using static LibHac.Gc.Values;
|
||||
using IStorage = LibHac.Fs.IStorage;
|
||||
|
||||
namespace LibHac.FsSrv.Storage;
|
||||
|
||||
internal struct GameCardServiceGlobals : IDisposable
|
||||
{
|
||||
public SdkMutexType StorageDeviceMutex;
|
||||
public SharedRef<IStorageDevice> CachedStorageDevice;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
StorageDeviceMutex = new SdkMutexType();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
CachedStorageDevice.Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains functions for interacting with the game card storage device.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
internal static class GameCardService
|
||||
{
|
||||
private static ulong MakeAttributeId(OpenGameCardAttribute attribute) => (ulong)attribute;
|
||||
private static int MakeOperationId(GameCardManagerOperationIdValue operation) => (int)operation;
|
||||
private static int MakeOperationId(GameCardOperationIdValue operation) => (int)operation;
|
||||
|
||||
private static Result GetGameCardManager(this StorageService service,
|
||||
ref SharedRef<IStorageDeviceManager> outManager)
|
||||
{
|
||||
return service.CreateStorageDeviceManager(ref outManager, StorageDevicePortId.GameCard);
|
||||
}
|
||||
|
||||
private static Result OpenAndCacheGameCardDevice(this StorageService service,
|
||||
ref SharedRef<IStorageDevice> outStorageDevice, OpenGameCardAttribute attribute)
|
||||
{
|
||||
using var storageDeviceManager = new SharedRef<IStorageDeviceManager>();
|
||||
Result rc = service.GetGameCardManager(ref storageDeviceManager.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
ref GameCardServiceGlobals g = ref service.Globals.GameCardService;
|
||||
|
||||
using var gameCardStorageDevice = new SharedRef<IStorageDevice>();
|
||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref g.StorageDeviceMutex);
|
||||
|
||||
rc = storageDeviceManager.Get.OpenDevice(ref gameCardStorageDevice.Ref(), MakeAttributeId(attribute));
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
g.CachedStorageDevice.SetByCopy(in gameCardStorageDevice);
|
||||
outStorageDevice.SetByCopy(in gameCardStorageDevice);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private static Result GetGameCardManagerOperator(this StorageService service,
|
||||
ref SharedRef<IStorageDeviceOperator> outDeviceOperator)
|
||||
{
|
||||
using var storageDeviceManager = new SharedRef<IStorageDeviceManager>();
|
||||
Result rc = service.GetGameCardManager(ref storageDeviceManager.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
return storageDeviceManager.Get.OpenOperator(ref outDeviceOperator);
|
||||
}
|
||||
|
||||
private static Result GetGameCardOperator(this StorageService service,
|
||||
ref SharedRef<IStorageDeviceOperator> outDeviceOperator, OpenGameCardAttribute attribute)
|
||||
{
|
||||
using var storageDevice = new SharedRef<IStorageDevice>();
|
||||
Result rc = service.OpenAndCacheGameCardDevice(ref storageDevice.Ref(), attribute);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
return storageDevice.Get.OpenOperator(ref outDeviceOperator.Ref());
|
||||
}
|
||||
|
||||
private static Result GetGameCardOperator(this StorageService service,
|
||||
ref SharedRef<IStorageDeviceOperator> outDeviceOperator)
|
||||
{
|
||||
ref GameCardServiceGlobals g = ref service.Globals.GameCardService;
|
||||
|
||||
using (ScopedLock.Lock(ref g.StorageDeviceMutex))
|
||||
{
|
||||
if (g.CachedStorageDevice.HasValue)
|
||||
{
|
||||
return g.CachedStorageDevice.Get.OpenOperator(ref outDeviceOperator);
|
||||
}
|
||||
}
|
||||
|
||||
return service.GetGameCardOperator(ref outDeviceOperator, OpenGameCardAttribute.ReadOnly);
|
||||
}
|
||||
|
||||
public static Result OpenGameCardStorage(this StorageService service, ref SharedRef<IStorage> outStorage,
|
||||
OpenGameCardAttribute attribute, uint handle)
|
||||
{
|
||||
using var gameCardStorageDevice = new SharedRef<IStorageDevice>();
|
||||
|
||||
Result rc = service.OpenAndCacheGameCardDevice(ref gameCardStorageDevice.Ref(), attribute);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
// Verify that the game card handle hasn't changed.
|
||||
rc = service.GetCurrentGameCardHandle(out StorageDeviceHandle newHandle);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
if (newHandle.Value != handle)
|
||||
{
|
||||
switch (attribute)
|
||||
{
|
||||
case OpenGameCardAttribute.ReadOnly:
|
||||
return ResultFs.GameCardFsCheckHandleInCreateReadOnlyFailure.Log();
|
||||
case OpenGameCardAttribute.SecureReadOnly:
|
||||
return ResultFs.GameCardFsCheckHandleInCreateSecureReadOnlyFailure.Log();
|
||||
case OpenGameCardAttribute.WriteOnly:
|
||||
break;
|
||||
default:
|
||||
return ResultFs.GameCardFsFailure.Log();
|
||||
}
|
||||
}
|
||||
|
||||
// Open the storage and add IPC and event simulation wrappers.
|
||||
using var storage = new SharedRef<IStorage>(new StorageServiceObjectAdapter(ref gameCardStorageDevice.Ref()));
|
||||
|
||||
using var deviceEventSimulationStorage = new SharedRef<DeviceEventSimulationStorage>(
|
||||
new DeviceEventSimulationStorage(ref storage.Ref(), service.FsSrv.Impl.GetGameCardEventSimulator()));
|
||||
|
||||
outStorage.SetByMove(ref deviceEventSimulationStorage.Ref());
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result GetCurrentGameCardHandle(this StorageService service, out StorageDeviceHandle outHandle)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out outHandle);
|
||||
|
||||
ref GameCardServiceGlobals g = ref service.Globals.GameCardService;
|
||||
|
||||
using (ScopedLock.Lock(ref g.StorageDeviceMutex))
|
||||
{
|
||||
if (g.CachedStorageDevice.HasValue)
|
||||
{
|
||||
Result rc = g.CachedStorageDevice.Get.GetHandle(out uint handleValue);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
outHandle = new StorageDeviceHandle(handleValue, StorageDevicePortId.GameCard);
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
using var gameCardStorageDevice = new SharedRef<IStorageDevice>();
|
||||
Result rc = service.OpenAndCacheGameCardDevice(ref gameCardStorageDevice.Ref(),
|
||||
OpenGameCardAttribute.ReadOnly);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
rc = gameCardStorageDevice.Get.GetHandle(out uint handleValue);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
outHandle = new StorageDeviceHandle(handleValue, StorageDevicePortId.GameCard);
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
public static Result IsGameCardHandleValid(this StorageService service, out bool isValid,
|
||||
in StorageDeviceHandle handle)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out isValid);
|
||||
|
||||
using var storageDeviceManager = new SharedRef<IStorageDeviceManager>();
|
||||
Result rc = service.GetGameCardManager(ref storageDeviceManager.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
return storageDeviceManager.Get.IsHandleValid(out isValid, handle.Value);
|
||||
}
|
||||
|
||||
public static Result IsGameCardInserted(this StorageService service, out bool outIsInserted)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out outIsInserted);
|
||||
|
||||
using var storageDeviceManager = new SharedRef<IStorageDeviceManager>();
|
||||
Result rc = service.GetGameCardManager(ref storageDeviceManager.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
// Get the actual state of the game card.
|
||||
rc = storageDeviceManager.Get.IsInserted(out bool isInserted);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
// Get the simulated state of the game card based on the actual state.
|
||||
outIsInserted = service.FsSrv.Impl.GetGameCardEventSimulator().FilterDetectionState(isInserted);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result EraseGameCard(this StorageService service, uint gameCardSize, ulong romAreaStartPageAddress)
|
||||
{
|
||||
using var gcOperator = new SharedRef<IStorageDeviceOperator>();
|
||||
Result rc = service.GetGameCardOperator(ref gcOperator.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
InBuffer inBuffer = InBuffer.FromStruct(in romAreaStartPageAddress);
|
||||
int operationId = MakeOperationId(GameCardOperationIdValue.EraseGameCard);
|
||||
|
||||
return gcOperator.Get.OperateIn(inBuffer, offset: 0, gameCardSize, operationId);
|
||||
}
|
||||
|
||||
public static Result GetInitializationResult(this StorageService service)
|
||||
{
|
||||
using var gcOperator = new SharedRef<IStorageDeviceOperator>();
|
||||
Result rc = service.GetGameCardManagerOperator(ref gcOperator.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
int operationId = MakeOperationId(GameCardManagerOperationIdValue.GetInitializationResult);
|
||||
|
||||
return gcOperator.Get.Operate(operationId);
|
||||
}
|
||||
|
||||
public static Result GetGameCardStatus(this StorageService service, out GameCardStatus outGameCardStatus,
|
||||
uint handle)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out outGameCardStatus);
|
||||
|
||||
using var gcOperator = new SharedRef<IStorageDeviceOperator>();
|
||||
Result rc = service.GetGameCardOperator(ref gcOperator.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
// Verify that the game card handle hasn't changed.
|
||||
var deviceHandle = new StorageDeviceHandle(handle, StorageDevicePortId.GameCard);
|
||||
rc = service.IsGameCardHandleValid(out bool isValidHandle, in deviceHandle);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
if (!isValidHandle)
|
||||
return ResultFs.GameCardFsCheckHandleInGetStatusFailure.Log();
|
||||
|
||||
// Get the GameCardStatus.
|
||||
OutBuffer outCardStatusBuffer = OutBuffer.FromStruct(ref outGameCardStatus);
|
||||
int operationId = MakeOperationId(GameCardOperationIdValue.GetGameCardStatus);
|
||||
|
||||
rc = gcOperator.Get.OperateOut(out long bytesWritten, outCardStatusBuffer, operationId);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
Assert.SdkEqual(Unsafe.SizeOf<GameCardStatus>(), bytesWritten);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result FinalizeGameCardLibrary(this StorageService service)
|
||||
{
|
||||
using var gcOperator = new SharedRef<IStorageDeviceOperator>();
|
||||
Result rc = service.GetGameCardManagerOperator(ref gcOperator.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
int operationId = MakeOperationId(GameCardManagerOperationIdValue.Finalize);
|
||||
|
||||
return gcOperator.Get.Operate(operationId);
|
||||
}
|
||||
|
||||
public static Result GetGameCardDeviceCertificate(this StorageService service, Span<byte> outBuffer, uint handle)
|
||||
{
|
||||
using var gcOperator = new SharedRef<IStorageDeviceOperator>();
|
||||
Result rc = service.GetGameCardOperator(ref gcOperator.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
// Verify that the game card handle hasn't changed.
|
||||
var deviceHandle = new StorageDeviceHandle(handle, StorageDevicePortId.GameCard);
|
||||
rc = service.IsGameCardHandleValid(out bool isValidHandle, in deviceHandle);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
if (!isValidHandle)
|
||||
return ResultFs.GameCardFsCheckHandleInGetDeviceCertFailure.Log();
|
||||
|
||||
// Get the device certificate.
|
||||
var outCertBuffer = new OutBuffer(outBuffer);
|
||||
int operationId = MakeOperationId(GameCardOperationIdValue.GetGameCardDeviceCertificate);
|
||||
|
||||
rc = gcOperator.Get.OperateOut(out long bytesWritten, outCertBuffer, operationId);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
Assert.SdkEqual(GcDeviceCertificateSize, bytesWritten);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result ChallengeCardExistence(this StorageService service, Span<byte> outResponseBuffer,
|
||||
ReadOnlySpan<byte> challengeSeed, ReadOnlySpan<byte> challengeValue, uint handle)
|
||||
{
|
||||
using var gcOperator = new SharedRef<IStorageDeviceOperator>();
|
||||
Result rc = service.GetGameCardOperator(ref gcOperator.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
// Verify that the game card handle hasn't changed.
|
||||
var deviceHandle = new StorageDeviceHandle(handle, StorageDevicePortId.GameCard);
|
||||
rc = service.IsGameCardHandleValid(out bool isValidHandle, in deviceHandle);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
if (!isValidHandle)
|
||||
return ResultFs.GameCardFsCheckHandleInChallengeCardExistence.Log();
|
||||
|
||||
// Get the challenge response.
|
||||
var valueBuffer = new InBuffer(challengeValue);
|
||||
var seedBuffer = new InBuffer(challengeSeed);
|
||||
var responseBuffer = new OutBuffer(outResponseBuffer);
|
||||
int operationId = MakeOperationId(GameCardOperationIdValue.ChallengeCardExistence);
|
||||
|
||||
rc = gcOperator.Get.OperateIn2Out(out long bytesWritten, responseBuffer, valueBuffer, seedBuffer, offset: 0,
|
||||
size: 0, operationId);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
Assert.SdkEqual(GcCardExistenceResponseDataSize, bytesWritten);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result GetGameCardHandle(this StorageService service, out uint handle)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out handle);
|
||||
|
||||
using var gcOperator = new SharedRef<IStorageDeviceOperator>();
|
||||
Result rc = service.GetGameCardManagerOperator(ref gcOperator.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
// Get the current handle.
|
||||
OutBuffer handleOutBuffer = OutBuffer.FromStruct(ref handle);
|
||||
int operationId = MakeOperationId(GameCardManagerOperationIdValue.GetHandle);
|
||||
|
||||
rc = gcOperator.Get.OperateOut(out long bytesWritten, handleOutBuffer, operationId);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
Assert.SdkEqual(Unsafe.SizeOf<GameCardHandle>(), bytesWritten);
|
||||
|
||||
// Clear the cached storage device if it has an old handle.
|
||||
ref GameCardServiceGlobals g = ref service.Globals.GameCardService;
|
||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref g.StorageDeviceMutex);
|
||||
|
||||
if (g.CachedStorageDevice.HasValue)
|
||||
{
|
||||
g.CachedStorageDevice.Get.GetHandle(out uint handleValue);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
var currentHandle = new StorageDeviceHandle(handleValue, StorageDevicePortId.GameCard);
|
||||
rc = service.IsGameCardHandleValid(out bool isHandleValid, in currentHandle);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
if (!isHandleValid)
|
||||
g.CachedStorageDevice.Reset();
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result GetGameCardAsicInfo(this StorageService service, out RmaInformation rmaInfo,
|
||||
ReadOnlySpan<byte> firmwareBuffer)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out rmaInfo);
|
||||
|
||||
using var gcOperator = new SharedRef<IStorageDeviceOperator>();
|
||||
Result rc = service.GetGameCardManagerOperator(ref gcOperator.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
var inFirmwareBuffer = new InBuffer(firmwareBuffer);
|
||||
OutBuffer outRmaInfoBuffer = OutBuffer.FromStruct(ref rmaInfo);
|
||||
int operationId = MakeOperationId(GameCardManagerOperationIdValue.GetGameCardAsicInfo);
|
||||
|
||||
rc = gcOperator.Get.OperateInOut(out long bytesWritten, outRmaInfoBuffer, inFirmwareBuffer, offset: 0,
|
||||
size: firmwareBuffer.Length, operationId);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
Assert.SdkEqual(Unsafe.SizeOf<RmaInformation>(), bytesWritten);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result GetGameCardIdSet(this StorageService service, out GameCardIdSet outIdSet)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out outIdSet);
|
||||
|
||||
using var gcOperator = new SharedRef<IStorageDeviceOperator>();
|
||||
Result rc = service.GetGameCardOperator(ref gcOperator.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
OutBuffer outIdSetBuffer = OutBuffer.FromStruct(ref outIdSet);
|
||||
int operationId = MakeOperationId(GameCardOperationIdValue.GetGameCardIdSet);
|
||||
|
||||
rc = gcOperator.Get.OperateOut(out long bytesWritten, outIdSetBuffer, operationId);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
Assert.SdkEqual(Unsafe.SizeOf<GameCardIdSet>(), bytesWritten);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result WriteToGameCardDirectly(this StorageService service, long offset, Span<byte> buffer)
|
||||
{
|
||||
using var gcOperator = new SharedRef<IStorageDeviceOperator>();
|
||||
Result rc = service.GetGameCardManagerOperator(ref gcOperator.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
var outBuffer = new OutBuffer(buffer);
|
||||
var inUnusedBuffer = new InBuffer();
|
||||
int operationId = MakeOperationId(GameCardManagerOperationIdValue.WriteToGameCardDirectly);
|
||||
|
||||
// Missing: Register device buffer
|
||||
|
||||
rc = gcOperator.Get.OperateInOut(out _, outBuffer, inUnusedBuffer, offset, buffer.Length, operationId);
|
||||
|
||||
// Missing: Unregister device buffer
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
public static Result SetVerifyWriteEnableFlag(this StorageService service, bool isEnabled)
|
||||
{
|
||||
using var gcOperator = new SharedRef<IStorageDeviceOperator>();
|
||||
Result rc = service.GetGameCardManagerOperator(ref gcOperator.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
InBuffer inIsEnabledBuffer = InBuffer.FromStruct(in isEnabled);
|
||||
int operationId = MakeOperationId(GameCardManagerOperationIdValue.SetVerifyEnableFlag);
|
||||
|
||||
return gcOperator.Get.OperateIn(inIsEnabledBuffer, offset: 0, size: 0, operationId);
|
||||
}
|
||||
|
||||
public static Result GetGameCardImageHash(this StorageService service, Span<byte> outBuffer, uint handle)
|
||||
{
|
||||
using var gcOperator = new SharedRef<IStorageDeviceOperator>();
|
||||
Result rc = service.GetGameCardOperator(ref gcOperator.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
// Verify that the game card handle hasn't changed.
|
||||
var deviceHandle = new StorageDeviceHandle(handle, StorageDevicePortId.GameCard);
|
||||
rc = service.IsGameCardHandleValid(out bool isValidHandle, in deviceHandle);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
if (!isValidHandle)
|
||||
return ResultFs.GameCardFsCheckHandleInGetCardImageHashFailure.Log();
|
||||
|
||||
// Get the card image hash.
|
||||
var outImageHashBuffer = new OutBuffer(outBuffer);
|
||||
int operationId = MakeOperationId(GameCardOperationIdValue.GetGameCardImageHash);
|
||||
|
||||
rc = gcOperator.Get.OperateOut(out long bytesWritten, outImageHashBuffer, operationId);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
Assert.SdkEqual(GcCardImageHashSize, bytesWritten);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result GetGameCardDeviceIdForProdCard(this StorageService service, Span<byte> outBuffer,
|
||||
ReadOnlySpan<byte> devHeaderBuffer)
|
||||
{
|
||||
using var gcOperator = new SharedRef<IStorageDeviceOperator>();
|
||||
Result rc = service.GetGameCardManagerOperator(ref gcOperator.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
var inDevHeaderBuffer = new InBuffer(devHeaderBuffer);
|
||||
var outDeviceIdBuffer = new OutBuffer(outBuffer);
|
||||
int operationId = MakeOperationId(GameCardManagerOperationIdValue.GetGameCardDeviceIdForProdCard);
|
||||
|
||||
rc = gcOperator.Get.OperateInOut(out long bytesWritten, outDeviceIdBuffer, inDevHeaderBuffer, offset: 0,
|
||||
size: 0, operationId);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
Assert.SdkEqual(GcPageSize, bytesWritten);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result EraseAndWriteParamDirectly(this StorageService service, ReadOnlySpan<byte> devParamBuffer)
|
||||
{
|
||||
using var gcOperator = new SharedRef<IStorageDeviceOperator>();
|
||||
Result rc = service.GetGameCardManagerOperator(ref gcOperator.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
var inDevParamBuffer = new InBuffer(devParamBuffer);
|
||||
int operationId = MakeOperationId(GameCardManagerOperationIdValue.EraseAndWriteParamDirectly);
|
||||
|
||||
return gcOperator.Get.OperateIn(inDevParamBuffer, offset: 0, size: devParamBuffer.Length, operationId);
|
||||
}
|
||||
|
||||
public static Result ReadParamDirectly(this StorageService service, Span<byte> outDevParamBuffer)
|
||||
{
|
||||
using var gcOperator = new SharedRef<IStorageDeviceOperator>();
|
||||
Result rc = service.GetGameCardManagerOperator(ref gcOperator.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
int operationId = MakeOperationId(GameCardManagerOperationIdValue.ReadParamDirectly);
|
||||
|
||||
rc = gcOperator.Get.OperateOut(out long bytesWritten, new OutBuffer(outDevParamBuffer), operationId);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
Assert.SdkEqual(GcPageSize, bytesWritten);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result ForceEraseGameCard(this StorageService service)
|
||||
{
|
||||
using var gcOperator = new SharedRef<IStorageDeviceOperator>();
|
||||
Result rc = service.GetGameCardManagerOperator(ref gcOperator.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
int operationId = MakeOperationId(GameCardManagerOperationIdValue.ForceErase);
|
||||
|
||||
return gcOperator.Get.Operate(operationId);
|
||||
}
|
||||
|
||||
public static Result GetGameCardErrorInfo(this StorageService service, out GameCardErrorInfo errorInfo)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out errorInfo);
|
||||
|
||||
using var gcOperator = new SharedRef<IStorageDeviceOperator>();
|
||||
Result rc = service.GetGameCardManagerOperator(ref gcOperator.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
OutBuffer outErrorInfoBuffer = OutBuffer.FromStruct(ref errorInfo);
|
||||
int operationId = MakeOperationId(GameCardManagerOperationIdValue.GetGameCardErrorInfo);
|
||||
|
||||
rc = gcOperator.Get.OperateOut(out long bytesWritten, outErrorInfoBuffer, operationId);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
Assert.SdkEqual(Unsafe.SizeOf<GameCardErrorInfo>(), bytesWritten);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result GetGameCardErrorReportInfo(this StorageService service,
|
||||
out GameCardErrorReportInfo errorReportInfo)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out errorReportInfo);
|
||||
|
||||
using var gcOperator = new SharedRef<IStorageDeviceOperator>();
|
||||
Result rc = service.GetGameCardManagerOperator(ref gcOperator.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
OutBuffer outErrorReportInfoBuffer = OutBuffer.FromStruct(ref errorReportInfo);
|
||||
int operationId = MakeOperationId(GameCardManagerOperationIdValue.GetGameCardErrorReportInfo);
|
||||
|
||||
rc = gcOperator.Get.OperateOut(out long bytesWritten, outErrorReportInfoBuffer, operationId);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
Assert.SdkEqual(Unsafe.SizeOf<GameCardErrorReportInfo>(), bytesWritten);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result GetGameCardDeviceId(this StorageService service, Span<byte> outBuffer)
|
||||
{
|
||||
using var gcOperator = new SharedRef<IStorageDeviceOperator>();
|
||||
Result rc = service.GetGameCardOperator(ref gcOperator.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
var outDeviceIdBuffer = new OutBuffer(outBuffer);
|
||||
int operationId = MakeOperationId(GameCardOperationIdValue.GetGameCardDeviceId);
|
||||
|
||||
rc = gcOperator.Get.OperateOut(out long bytesWritten, outDeviceIdBuffer, operationId);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
Assert.SdkEqual(GcCardDeviceIdSize, bytesWritten);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static bool IsGameCardActivationValid(this StorageService service, uint handle)
|
||||
{
|
||||
using var gcOperator = new SharedRef<IStorageDeviceOperator>();
|
||||
Result rc = service.GetGameCardManagerOperator(ref gcOperator.Ref());
|
||||
if (rc.IsFailure()) return Result.ConvertResultToReturnType<bool>(rc);
|
||||
|
||||
bool isValid = false;
|
||||
InBuffer inHandleBuffer = InBuffer.FromStruct(in handle);
|
||||
OutBuffer outIsValidBuffer = OutBuffer.FromStruct(ref isValid);
|
||||
int operationId = MakeOperationId(GameCardManagerOperationIdValue.IsGameCardActivationValid);
|
||||
|
||||
rc = gcOperator.Get.OperateInOut(out long bytesWritten, outIsValidBuffer, inHandleBuffer, offset: 0, size: 0,
|
||||
operationId);
|
||||
if (rc.IsFailure()) return Result.ConvertResultToReturnType<bool>(rc);
|
||||
|
||||
Assert.SdkEqual(Unsafe.SizeOf<bool>(), bytesWritten);
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
public static Result OpenGameCardDetectionEvent(this StorageService service,
|
||||
ref SharedRef<IEventNotifier> outEventNotifier)
|
||||
{
|
||||
using var storageDeviceManager = new SharedRef<IStorageDeviceManager>();
|
||||
Result rc = service.GetGameCardManager(ref storageDeviceManager.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
return storageDeviceManager.Get.OpenDetectionEvent(ref outEventNotifier);
|
||||
}
|
||||
|
||||
public static Result SimulateGameCardDetectionEventSignaled(this StorageService service)
|
||||
{
|
||||
using var gcOperator = new SharedRef<IStorageDeviceOperator>();
|
||||
Result rc = service.GetGameCardManagerOperator(ref gcOperator.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
int operationId = MakeOperationId(GameCardManagerOperationIdValue.SimulateDetectionEventSignaled);
|
||||
|
||||
return gcOperator.Get.Operate(operationId);
|
||||
}
|
||||
}
|
|
@ -1,18 +1,21 @@
|
|||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Common;
|
||||
using IStorage = LibHac.FsSrv.Sf.IStorage;
|
||||
|
||||
namespace LibHac.FsSrv.Storage.Sf;
|
||||
|
||||
public interface IStorageDevice : IDisposable
|
||||
// Note: This interface doesn't actually implement IStorage. We're giving it IStorage as a base because
|
||||
// StorageServiceObjectAdapter is a template that is used with either IStorage or IStorageDevice
|
||||
public interface IStorageDevice : IStorage
|
||||
{
|
||||
Result GetHandle(out uint handle);
|
||||
Result IsHandleValid(out bool isValid);
|
||||
Result OpenOperator(ref SharedRef<IStorageDeviceOperator> outDeviceOperator);
|
||||
Result Read(long offset, Span<byte> destination);
|
||||
Result Write(long offset, ReadOnlySpan<byte> source);
|
||||
Result Flush();
|
||||
Result SetSize(long size);
|
||||
Result GetSize(out long size);
|
||||
Result OperateRange(out QueryRangeInfo rangeInfo, int operationId, long offset, long size);
|
||||
}
|
||||
|
||||
// These methods come from inheriting IStorage
|
||||
//Result Read(long offset, OutBuffer destination, long size);
|
||||
//Result Write(long offset, InBuffer source, long size);
|
||||
//Result Flush();
|
||||
//Result SetSize(long size);
|
||||
//Result GetSize(out long size);
|
||||
//Result OperateRange(out QueryRangeInfo rangeInfo, int operationId, long offset, long size);
|
||||
}
|
33
src/LibHac/Gc/GameCardTypes.cs
Normal file
33
src/LibHac/Gc/GameCardTypes.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using LibHac.Common.FixedArrays;
|
||||
using LibHac.Gc.Impl;
|
||||
|
||||
namespace LibHac.Gc;
|
||||
|
||||
public struct GameCardStatus
|
||||
{
|
||||
public Array32<byte> PartitionFsHeaderHash;
|
||||
public ulong PackageId;
|
||||
public long Size;
|
||||
public long PartitionFsHeaderOffset;
|
||||
public long PartitionFsHeaderSize;
|
||||
public long SecureAreaOffset;
|
||||
public long SecureAreaSize;
|
||||
public uint UpdatePartitionVersion;
|
||||
public ulong UpdatePartitionId;
|
||||
public byte CompatibilityType;
|
||||
public Array3<byte> Reserved61;
|
||||
public byte GameCardAttribute;
|
||||
public Array11<byte> Reserved65;
|
||||
}
|
||||
|
||||
public struct RmaInformation
|
||||
{
|
||||
public Array512<byte> Data;
|
||||
}
|
||||
|
||||
public struct GameCardIdSet
|
||||
{
|
||||
public CardId1 Id1;
|
||||
public CardId2 Id2;
|
||||
public CardId3 Id3;
|
||||
}
|
11
src/LibHac/Gc/GameCardValues.cs
Normal file
11
src/LibHac/Gc/GameCardValues.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace LibHac.Gc;
|
||||
|
||||
public static class Values
|
||||
{
|
||||
public static int GcPageSize => 0x200;
|
||||
public static int GcAsicFirmwareSize => 0x7800;
|
||||
public static int GcCardDeviceIdSize => 0x10;
|
||||
public static int GcCardExistenceResponseDataSize => 0x58;
|
||||
public static int GcCardImageHashSize => 0x20;
|
||||
public static int GcDeviceCertificateSize => 0x200;
|
||||
}
|
35
src/LibHac/Gc/Impl/GameCardImplTypes.cs
Normal file
35
src/LibHac/Gc/Impl/GameCardImplTypes.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using LibHac.Common.FixedArrays;
|
||||
|
||||
namespace LibHac.Gc.Impl;
|
||||
|
||||
public struct CardId1
|
||||
{
|
||||
public byte MakerCode;
|
||||
public MemoryCapacity MemoryCapacity;
|
||||
public byte Reserved;
|
||||
public byte MemoryType;
|
||||
}
|
||||
|
||||
public struct CardId2
|
||||
{
|
||||
public byte CardSecurityNumber;
|
||||
public byte CardType;
|
||||
public Array2<byte> Reserved;
|
||||
}
|
||||
|
||||
public struct CardId3
|
||||
{
|
||||
public Array4<byte> Reserved;
|
||||
}
|
||||
|
||||
public enum MemoryCapacity : byte
|
||||
{
|
||||
// ReSharper disable InconsistentNaming
|
||||
Capacity1GB = 0xFA,
|
||||
Capacity2GB = 0xF8,
|
||||
Capacity4GB = 0xF0,
|
||||
Capacity8GB = 0xE0,
|
||||
Capacity16GB = 0xE1,
|
||||
Capacity32GB = 0xE2
|
||||
// ReSharper restore InconsistentNaming
|
||||
}
|
37
src/LibHac/GcSrv/GcSrvEnums.cs
Normal file
37
src/LibHac/GcSrv/GcSrvEnums.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
namespace LibHac.GcSrv;
|
||||
|
||||
public enum GameCardManagerOperationIdValue
|
||||
{
|
||||
Finalize = 1,
|
||||
GetHandle = 2,
|
||||
IsGameCardActivationValid = 3,
|
||||
GetInitializationResult = 4,
|
||||
GetGameCardErrorInfo = 5,
|
||||
GetGameCardErrorReportInfo = 6,
|
||||
SetVerifyEnableFlag = 7,
|
||||
GetGameCardAsicInfo = 8,
|
||||
GetGameCardDeviceIdForProdCard = 9,
|
||||
EraseAndWriteParamDirectly = 10,
|
||||
ReadParamDirectly = 11,
|
||||
WriteToGameCardDirectly = 12,
|
||||
ForceErase = 13,
|
||||
SimulateDetectionEventSignaled = 14
|
||||
}
|
||||
|
||||
public enum GameCardOperationIdValue
|
||||
{
|
||||
EraseGameCard = 1,
|
||||
GetGameCardIdSet = 2,
|
||||
GetGameCardDeviceId = 3,
|
||||
GetGameCardImageHash = 4,
|
||||
GetGameCardDeviceCertificate = 5,
|
||||
ChallengeCardExistence = 6,
|
||||
GetGameCardStatus = 7
|
||||
}
|
||||
|
||||
public enum OpenGameCardAttribute : long
|
||||
{
|
||||
ReadOnly = 0,
|
||||
SecureReadOnly = 1,
|
||||
WriteOnly = 2
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using BaseType = System.UInt32;
|
||||
|
||||
namespace LibHac;
|
||||
|
@ -209,6 +210,27 @@ public readonly struct Result : IEquatable<Result>
|
|||
|
||||
}
|
||||
|
||||
internal static T ConvertResultToReturnType<T>(Result result)
|
||||
{
|
||||
result.Miss();
|
||||
ConvertInvalidImpl(result);
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
internal static void ConvertInvalidImpl(Result result)
|
||||
{
|
||||
OnUnhandledResult(result);
|
||||
}
|
||||
|
||||
internal static void OnUnhandledResult(Result result)
|
||||
{
|
||||
Abort.DoAbortUnless(result.IsSuccess());
|
||||
|
||||
// The original does an infinite loop here
|
||||
throw new HorizonResultException(result, "Unhandled result.");
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static BaseType GetBitsValue(BaseType value, int bitsOffset, int bitsCount)
|
||||
{
|
||||
|
@ -232,7 +254,7 @@ public readonly struct Result : IEquatable<Result>
|
|||
/// <br/>A Result definition should look like this: <c>public static Result.Base PathNotFound => new Result.Base(ModuleFs, 1);</c>
|
||||
/// <br/>Being a computed property like this will allow the compiler to do constant propagation to optimize comparisons.
|
||||
/// <br/><br/>This is an example of how a Result should be returned from a function: <c>return PathNotFound.Log();</c>
|
||||
/// <br/>The <see cref="Log()"/> method will return the <see cref="Result"/> for the specified <see cref="Base"/>, and
|
||||
/// <br/>The <see cref="Result.Log"/> method will return the <see cref="Result"/> for the specified <see cref="Base"/>, and
|
||||
/// will optionally log the returned Result when running in debug mode for easier debugging. All Result logging functionality
|
||||
/// is removed from release builds.
|
||||
/// If the <see cref="Result"/> is not being used as a return value, <see cref="Value"/> will get the Result without logging anything.
|
||||
|
|
|
@ -419,4 +419,56 @@ public class TypeLayoutTests
|
|||
|
||||
Assert.Equal(0, GetOffset(in s, in s.Value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void GameCardErrorInfo_Layout()
|
||||
{
|
||||
var s = new GameCardErrorInfo();
|
||||
|
||||
Assert.Equal(0x10, Unsafe.SizeOf<GameCardErrorInfo>());
|
||||
|
||||
Assert.Equal(0x0, GetOffset(in s, in s.GameCardCrcErrorCount));
|
||||
Assert.Equal(0x2, GetOffset(in s, in s.Reserved2));
|
||||
Assert.Equal(0x4, GetOffset(in s, in s.AsicCrcErrorCount));
|
||||
Assert.Equal(0x6, GetOffset(in s, in s.Reserved6));
|
||||
Assert.Equal(0x8, GetOffset(in s, in s.RefreshCount));
|
||||
Assert.Equal(0xA, GetOffset(in s, in s.ReservedA));
|
||||
Assert.Equal(0xC, GetOffset(in s, in s.ReadRetryCount));
|
||||
Assert.Equal(0xE, GetOffset(in s, in s.TimeoutRetryErrorCount));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void GameCardErrorReportInfo_Layout()
|
||||
{
|
||||
var s = new GameCardErrorReportInfo();
|
||||
|
||||
Assert.Equal(0x40, Unsafe.SizeOf<GameCardErrorReportInfo>());
|
||||
|
||||
Assert.Equal(0x00, GetOffset(in s, in s.ErrorInfo));
|
||||
Assert.Equal(0x10, GetOffset(in s, in s.AsicReinitializeFailureDetail));
|
||||
Assert.Equal(0x12, GetOffset(in s, in s.InsertionCount));
|
||||
Assert.Equal(0x14, GetOffset(in s, in s.RemovalCount));
|
||||
Assert.Equal(0x16, GetOffset(in s, in s.AsicReinitializeCount));
|
||||
Assert.Equal(0x18, GetOffset(in s, in s.AsicInitializeCount));
|
||||
Assert.Equal(0x1C, GetOffset(in s, in s.AsicReinitializeFailureCount));
|
||||
Assert.Equal(0x1E, GetOffset(in s, in s.AwakenFailureCount));
|
||||
Assert.Equal(0x20, GetOffset(in s, in s.Reserved20));
|
||||
Assert.Equal(0x22, GetOffset(in s, in s.RefreshCount));
|
||||
Assert.Equal(0x24, GetOffset(in s, in s.LastReadErrorPageAddress));
|
||||
Assert.Equal(0x28, GetOffset(in s, in s.LastReadErrorPageCount));
|
||||
Assert.Equal(0x2C, GetOffset(in s, in s.AwakenCount));
|
||||
Assert.Equal(0x30, GetOffset(in s, in s.ReadCountFromInsert));
|
||||
Assert.Equal(0x34, GetOffset(in s, in s.ReadCountFromAwaken));
|
||||
Assert.Equal(0x38, GetOffset(in s, in s.Reserved38));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void GameCardHandle_Layout()
|
||||
{
|
||||
var s = new GameCardHandle();
|
||||
|
||||
Assert.Equal(4, Unsafe.SizeOf<GameCardHandle>());
|
||||
|
||||
Assert.Equal(0, GetOffset(in s, in s.Value));
|
||||
}
|
||||
}
|
89
tests/LibHac.Tests/Gc/TypeLayoutTests.cs
Normal file
89
tests/LibHac.Tests/Gc/TypeLayoutTests.cs
Normal file
|
@ -0,0 +1,89 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Gc;
|
||||
using LibHac.Gc.Impl;
|
||||
using Xunit;
|
||||
using static LibHac.Tests.Common.Layout;
|
||||
|
||||
namespace LibHac.Tests.Gc;
|
||||
|
||||
public class TypeLayoutTests
|
||||
{
|
||||
[Fact]
|
||||
public static void GameCardStatus_Layout()
|
||||
{
|
||||
var s = new GameCardStatus();
|
||||
|
||||
Assert.Equal(0x70, Unsafe.SizeOf<GameCardStatus>());
|
||||
|
||||
Assert.Equal(0x00, GetOffset(in s, in s.PartitionFsHeaderHash));
|
||||
Assert.Equal(0x20, GetOffset(in s, in s.PackageId));
|
||||
Assert.Equal(0x28, GetOffset(in s, in s.Size));
|
||||
Assert.Equal(0x30, GetOffset(in s, in s.PartitionFsHeaderOffset));
|
||||
Assert.Equal(0x38, GetOffset(in s, in s.PartitionFsHeaderSize));
|
||||
Assert.Equal(0x40, GetOffset(in s, in s.SecureAreaOffset));
|
||||
Assert.Equal(0x48, GetOffset(in s, in s.SecureAreaSize));
|
||||
Assert.Equal(0x50, GetOffset(in s, in s.UpdatePartitionVersion));
|
||||
Assert.Equal(0x58, GetOffset(in s, in s.UpdatePartitionId));
|
||||
Assert.Equal(0x60, GetOffset(in s, in s.CompatibilityType));
|
||||
Assert.Equal(0x61, GetOffset(in s, in s.Reserved61));
|
||||
Assert.Equal(0x64, GetOffset(in s, in s.GameCardAttribute));
|
||||
Assert.Equal(0x65, GetOffset(in s, in s.Reserved65));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RmaInformation_Layout()
|
||||
{
|
||||
var s = new RmaInformation();
|
||||
|
||||
Assert.Equal(0x200, Unsafe.SizeOf<RmaInformation>());
|
||||
|
||||
Assert.Equal(0x00, GetOffset(in s, in s.Data));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void GameCardIdSet_Layout()
|
||||
{
|
||||
var s = new GameCardIdSet();
|
||||
|
||||
Assert.Equal(12, Unsafe.SizeOf<GameCardIdSet>());
|
||||
|
||||
Assert.Equal(0, GetOffset(in s, in s.Id1));
|
||||
Assert.Equal(4, GetOffset(in s, in s.Id2));
|
||||
Assert.Equal(8, GetOffset(in s, in s.Id3));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void CardId1_Layout()
|
||||
{
|
||||
var s = new CardId1();
|
||||
|
||||
Assert.Equal(4, Unsafe.SizeOf<CardId1>());
|
||||
|
||||
Assert.Equal(0, GetOffset(in s, in s.MakerCode));
|
||||
Assert.Equal(1, GetOffset(in s, in s.MemoryCapacity));
|
||||
Assert.Equal(2, GetOffset(in s, in s.Reserved));
|
||||
Assert.Equal(3, GetOffset(in s, in s.MemoryType));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void CardId2_Layout()
|
||||
{
|
||||
var s = new CardId2();
|
||||
|
||||
Assert.Equal(4, Unsafe.SizeOf<CardId2>());
|
||||
|
||||
Assert.Equal(0, GetOffset(in s, in s.CardSecurityNumber));
|
||||
Assert.Equal(1, GetOffset(in s, in s.CardType));
|
||||
Assert.Equal(2, GetOffset(in s, in s.Reserved));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void CardId3_Layout()
|
||||
{
|
||||
var s = new CardId3();
|
||||
|
||||
Assert.Equal(4, Unsafe.SizeOf<CardId3>());
|
||||
|
||||
Assert.Equal(0, GetOffset(in s, in s.Reserved));
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue