mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Add cache storage and an emulated SD card
This commit is contained in:
parent
4ff12ec21b
commit
44ff25ee9b
@ -19,7 +19,7 @@ namespace LibHac.Fs
|
|||||||
if (uid != Uid.Zero && nacp.UserAccountSaveDataSize > 0)
|
if (uid != Uid.Zero && nacp.UserAccountSaveDataSize > 0)
|
||||||
{
|
{
|
||||||
var filter = new SaveDataFilter();
|
var filter = new SaveDataFilter();
|
||||||
filter.SetTitleId(applicationId);
|
filter.SetProgramId(applicationId);
|
||||||
filter.SetSaveDataType(SaveDataType.Account);
|
filter.SetSaveDataType(SaveDataType.Account);
|
||||||
filter.SetUserId(new UserId(uid.Id.High, uid.Id.Low));
|
filter.SetUserId(new UserId(uid.Id.High, uid.Id.Low));
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ namespace LibHac.Fs
|
|||||||
if (nacp.DeviceSaveDataSize > 0)
|
if (nacp.DeviceSaveDataSize > 0)
|
||||||
{
|
{
|
||||||
var filter = new SaveDataFilter();
|
var filter = new SaveDataFilter();
|
||||||
filter.SetTitleId(applicationId);
|
filter.SetProgramId(applicationId);
|
||||||
filter.SetSaveDataType(SaveDataType.Device);
|
filter.SetSaveDataType(SaveDataType.Device);
|
||||||
|
|
||||||
Result rc = fs.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, ref filter);
|
Result rc = fs.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, ref filter);
|
||||||
@ -155,7 +155,7 @@ namespace LibHac.Fs
|
|||||||
// If there was already insufficient space to create the previous saves, check if the temp
|
// If there was already insufficient space to create the previous saves, check if the temp
|
||||||
// save already exists instead of trying to create a new one.
|
// save already exists instead of trying to create a new one.
|
||||||
var filter = new SaveDataFilter();
|
var filter = new SaveDataFilter();
|
||||||
filter.SetTitleId(applicationId);
|
filter.SetProgramId(applicationId);
|
||||||
filter.SetSaveDataType(SaveDataType.Temporary);
|
filter.SetSaveDataType(SaveDataType.Temporary);
|
||||||
|
|
||||||
Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.Temporary, ref filter);
|
Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.Temporary, ref filter);
|
||||||
@ -234,7 +234,7 @@ namespace LibHac.Fs
|
|||||||
}
|
}
|
||||||
|
|
||||||
var filter = new SaveDataFilter();
|
var filter = new SaveDataFilter();
|
||||||
filter.SetTitleId(applicationId);
|
filter.SetProgramId(applicationId);
|
||||||
filter.SetSaveDataType(SaveDataType.Bcat);
|
filter.SetSaveDataType(SaveDataType.Bcat);
|
||||||
|
|
||||||
Result rc = fs.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, ref filter);
|
Result rc = fs.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, ref filter);
|
||||||
|
29
src/LibHac/Fs/EncryptionSeed.cs
Normal file
29
src/LibHac/Fs/EncryptionSeed.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using LibHac.Common;
|
||||||
|
|
||||||
|
namespace LibHac.Fs
|
||||||
|
{
|
||||||
|
[DebuggerDisplay("{ToString()}")]
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
||||||
|
public struct EncryptionSeed : IEquatable<EncryptionSeed>
|
||||||
|
{
|
||||||
|
private readonly Key128 Key;
|
||||||
|
|
||||||
|
public ReadOnlySpan<byte> Value => SpanHelpers.AsByteSpan(ref this);
|
||||||
|
|
||||||
|
public EncryptionSeed(ReadOnlySpan<byte> bytes)
|
||||||
|
{
|
||||||
|
Key = new Key128(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => Key.ToString();
|
||||||
|
|
||||||
|
public override bool Equals(object obj) => obj is EncryptionSeed key && Equals(key);
|
||||||
|
public bool Equals(EncryptionSeed other) => Key.Equals(other.Key);
|
||||||
|
public override int GetHashCode() => Key.GetHashCode();
|
||||||
|
public static bool operator ==(EncryptionSeed left, EncryptionSeed right) => left.Equals(right);
|
||||||
|
public static bool operator !=(EncryptionSeed left, EncryptionSeed right) => !(left == right);
|
||||||
|
}
|
||||||
|
}
|
@ -173,4 +173,11 @@ namespace LibHac.Fs
|
|||||||
KeepAfterResettingSystemSaveDataWithoutUserSaveData = 1 << 2,
|
KeepAfterResettingSystemSaveDataWithoutUserSaveData = 1 << 2,
|
||||||
NeedsSecureDelete = 1 << 3
|
NeedsSecureDelete = 1 << 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum SdmmcPort
|
||||||
|
{
|
||||||
|
Mmc = 0,
|
||||||
|
SdCard = 1,
|
||||||
|
GcAsic = 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,8 @@ namespace LibHac.Fs
|
|||||||
public static Result.Base ExternalKeyNotFound => new Result.Base(ModuleFs, 1004);
|
public static Result.Base ExternalKeyNotFound => new Result.Base(ModuleFs, 1004);
|
||||||
|
|
||||||
public static Result.Base SdCardAccessFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 2000, 2499); }
|
public static Result.Base SdCardAccessFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 2000, 2499); }
|
||||||
public static Result.Base SdCardNotPresent => new Result.Base(ModuleFs, 2001);
|
public static Result.Base SdCardNotFound => new Result.Base(ModuleFs, 2001);
|
||||||
|
public static Result.Base SdCardAsleep => new Result.Base(ModuleFs, 2004);
|
||||||
|
|
||||||
public static Result.Base GameCardAccessFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 2500, 2999); }
|
public static Result.Base GameCardAccessFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 2500, 2999); }
|
||||||
public static Result.Base InvalidBufferForGameCard => new Result.Base(ModuleFs, 2503);
|
public static Result.Base InvalidBufferForGameCard => new Result.Base(ModuleFs, 2503);
|
||||||
|
@ -56,23 +56,23 @@ namespace LibHac.Fs
|
|||||||
[StructLayout(LayoutKind.Explicit, Size = 0x48)]
|
[StructLayout(LayoutKind.Explicit, Size = 0x48)]
|
||||||
public struct SaveDataFilter
|
public struct SaveDataFilter
|
||||||
{
|
{
|
||||||
[FieldOffset(0x00)] public bool FilterByTitleId;
|
[FieldOffset(0x00)] public bool FilterByProgramId;
|
||||||
[FieldOffset(0x01)] public bool FilterBySaveDataType;
|
[FieldOffset(0x01)] public bool FilterBySaveDataType;
|
||||||
[FieldOffset(0x02)] public bool FilterByUserId;
|
[FieldOffset(0x02)] public bool FilterByUserId;
|
||||||
[FieldOffset(0x03)] public bool FilterBySaveDataId;
|
[FieldOffset(0x03)] public bool FilterBySaveDataId;
|
||||||
[FieldOffset(0x04)] public bool FilterByIndex;
|
[FieldOffset(0x04)] public bool FilterByIndex;
|
||||||
[FieldOffset(0x05)] public SaveDataRank Rank;
|
[FieldOffset(0x05)] public SaveDataRank Rank;
|
||||||
|
|
||||||
[FieldOffset(0x08)] public TitleId TitleId;
|
[FieldOffset(0x08)] public TitleId ProgramId;
|
||||||
[FieldOffset(0x10)] public UserId UserId;
|
[FieldOffset(0x10)] public UserId UserId;
|
||||||
[FieldOffset(0x20)] public ulong SaveDataId;
|
[FieldOffset(0x20)] public ulong SaveDataId;
|
||||||
[FieldOffset(0x28)] public SaveDataType SaveDataType;
|
[FieldOffset(0x28)] public SaveDataType SaveDataType;
|
||||||
[FieldOffset(0x2A)] public short Index;
|
[FieldOffset(0x2A)] public short Index;
|
||||||
|
|
||||||
public void SetTitleId(TitleId value)
|
public void SetProgramId(TitleId value)
|
||||||
{
|
{
|
||||||
FilterByTitleId = true;
|
FilterByProgramId = true;
|
||||||
TitleId = value;
|
ProgramId = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetSaveDataType(SaveDataType value)
|
public void SetSaveDataType(SaveDataType value)
|
||||||
|
91
src/LibHac/Fs/Shim/SdCard.cs
Normal file
91
src/LibHac/Fs/Shim/SdCard.cs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
using System;
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.FsService;
|
||||||
|
|
||||||
|
namespace LibHac.Fs.Shim
|
||||||
|
{
|
||||||
|
public static class SdCard
|
||||||
|
{
|
||||||
|
public static Result MountSdCard(this FileSystemClient fs, U8Span mountName)
|
||||||
|
{
|
||||||
|
Result rc;
|
||||||
|
|
||||||
|
if (fs.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||||
|
{
|
||||||
|
TimeSpan startTime = fs.Time.GetCurrent();
|
||||||
|
rc = Run(fs, mountName);
|
||||||
|
TimeSpan endTime = fs.Time.GetCurrent();
|
||||||
|
|
||||||
|
fs.OutputAccessLog(rc, startTime, endTime, "");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rc = Run(fs, mountName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
if (fs.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||||
|
{
|
||||||
|
fs.EnableFileSystemAccessorAccessLog(mountName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
|
||||||
|
static Result Run(FileSystemClient fs, U8Span mountName)
|
||||||
|
{
|
||||||
|
// ReSharper disable once VariableHidesOuterVariable
|
||||||
|
Result rc = MountHelpers.CheckMountName(mountName);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||||
|
|
||||||
|
rc = fsProxy.OpenSdCardFileSystem(out IFileSystem fileSystem);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
return fs.Register(mountName, fileSystem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsSdCardInserted(this FileSystemClient fs)
|
||||||
|
{
|
||||||
|
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||||
|
|
||||||
|
Result rc = fsProxy.OpenDeviceOperator(out IDeviceOperator deviceOperator);
|
||||||
|
if (rc.IsFailure()) throw new HorizonResultException(rc, "Abort");
|
||||||
|
|
||||||
|
rc = deviceOperator.IsSdCardInserted(out bool isInserted);
|
||||||
|
if (rc.IsFailure()) throw new HorizonResultException(rc, "Abort");
|
||||||
|
|
||||||
|
return isInserted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result SetSdCardEncryptionSeed(this FileSystemClient fs, ref EncryptionSeed seed)
|
||||||
|
{
|
||||||
|
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||||
|
|
||||||
|
Result rc = fsProxy.SetSdCardEncryptionSeed(ref seed);
|
||||||
|
if (rc.IsFailure()) throw new HorizonResultException(rc, "Abort");
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetSdCardAccessibility(this FileSystemClient fs, bool isAccessible)
|
||||||
|
{
|
||||||
|
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||||
|
|
||||||
|
Result rc = fsProxy.SetSdCardAccessibility(isAccessible);
|
||||||
|
if (rc.IsFailure()) throw new HorizonResultException(rc, "Abort");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsSdCardAccessible(this FileSystemClient fs)
|
||||||
|
{
|
||||||
|
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||||
|
|
||||||
|
Result rc = fsProxy.IsSdCardAccessible(out bool isAccessible);
|
||||||
|
if (rc.IsFailure()) throw new HorizonResultException(rc, "Abort");
|
||||||
|
|
||||||
|
return isAccessible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,18 +7,21 @@ namespace LibHac.FsService.Creators
|
|||||||
{
|
{
|
||||||
private const string DefaultPath = "/sdcard";
|
private const string DefaultPath = "/sdcard";
|
||||||
|
|
||||||
|
private EmulatedSdCard SdCard { get; }
|
||||||
private IFileSystem RootFileSystem { get; }
|
private IFileSystem RootFileSystem { get; }
|
||||||
private string Path { get; }
|
private string Path { get; }
|
||||||
|
|
||||||
private IFileSystem SdCardFileSystem { get; set; }
|
private IFileSystem SdCardFileSystem { get; set; }
|
||||||
|
|
||||||
public EmulatedSdFileSystemCreator(IFileSystem rootFileSystem)
|
public EmulatedSdFileSystemCreator(EmulatedSdCard sdCard, IFileSystem rootFileSystem)
|
||||||
{
|
{
|
||||||
|
SdCard = sdCard;
|
||||||
RootFileSystem = rootFileSystem;
|
RootFileSystem = rootFileSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EmulatedSdFileSystemCreator(IFileSystem rootFileSystem, string path)
|
public EmulatedSdFileSystemCreator(EmulatedSdCard sdCard, IFileSystem rootFileSystem, string path)
|
||||||
{
|
{
|
||||||
|
SdCard = sdCard;
|
||||||
RootFileSystem = rootFileSystem;
|
RootFileSystem = rootFileSystem;
|
||||||
Path = path;
|
Path = path;
|
||||||
}
|
}
|
||||||
@ -27,6 +30,11 @@ namespace LibHac.FsService.Creators
|
|||||||
{
|
{
|
||||||
fileSystem = default;
|
fileSystem = default;
|
||||||
|
|
||||||
|
if (!SdCard.IsSdCardInserted())
|
||||||
|
{
|
||||||
|
return ResultFs.SdCardNotFound.Log();
|
||||||
|
}
|
||||||
|
|
||||||
if (SdCardFileSystem != null)
|
if (SdCardFileSystem != null)
|
||||||
{
|
{
|
||||||
fileSystem = SdCardFileSystem;
|
fileSystem = SdCardFileSystem;
|
||||||
|
@ -8,11 +8,13 @@ namespace LibHac.FsService
|
|||||||
public FileSystemCreators FsCreators { get; set; }
|
public FileSystemCreators FsCreators { get; set; }
|
||||||
public IDeviceOperator DeviceOperator { get; set; }
|
public IDeviceOperator DeviceOperator { get; set; }
|
||||||
public EmulatedGameCard GameCard { get; set; }
|
public EmulatedGameCard GameCard { get; set; }
|
||||||
|
public EmulatedSdCard SdCard { get; set; }
|
||||||
|
|
||||||
public static DefaultFsServerObjects GetDefaultEmulatedCreators(IFileSystem rootFileSystem, Keyset keyset)
|
public static DefaultFsServerObjects GetDefaultEmulatedCreators(IFileSystem rootFileSystem, Keyset keyset)
|
||||||
{
|
{
|
||||||
var creators = new FileSystemCreators();
|
var creators = new FileSystemCreators();
|
||||||
var gameCard = new EmulatedGameCard(keyset);
|
var gameCard = new EmulatedGameCard(keyset);
|
||||||
|
var sdCard = new EmulatedSdCard();
|
||||||
|
|
||||||
var gcStorageCreator = new EmulatedGameCardStorageCreator(gameCard);
|
var gcStorageCreator = new EmulatedGameCardStorageCreator(gameCard);
|
||||||
|
|
||||||
@ -26,15 +28,16 @@ namespace LibHac.FsService
|
|||||||
creators.GameCardFileSystemCreator = new EmulatedGameCardFsCreator(gcStorageCreator, gameCard);
|
creators.GameCardFileSystemCreator = new EmulatedGameCardFsCreator(gcStorageCreator, gameCard);
|
||||||
creators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator(keyset);
|
creators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator(keyset);
|
||||||
creators.BuiltInStorageFileSystemCreator = new EmulatedBisFileSystemCreator(rootFileSystem);
|
creators.BuiltInStorageFileSystemCreator = new EmulatedBisFileSystemCreator(rootFileSystem);
|
||||||
creators.SdFileSystemCreator = new EmulatedSdFileSystemCreator(rootFileSystem);
|
creators.SdFileSystemCreator = new EmulatedSdFileSystemCreator(sdCard, rootFileSystem);
|
||||||
|
|
||||||
var deviceOperator = new EmulatedDeviceOperator(gameCard);
|
var deviceOperator = new EmulatedDeviceOperator(gameCard, sdCard);
|
||||||
|
|
||||||
return new DefaultFsServerObjects
|
return new DefaultFsServerObjects
|
||||||
{
|
{
|
||||||
FsCreators = creators,
|
FsCreators = creators,
|
||||||
DeviceOperator = deviceOperator,
|
DeviceOperator = deviceOperator,
|
||||||
GameCard = gameCard
|
GameCard = gameCard,
|
||||||
|
SdCard = sdCard
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,22 @@
|
|||||||
using System;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs;
|
|
||||||
|
|
||||||
namespace LibHac.FsService
|
namespace LibHac.FsService
|
||||||
{
|
{
|
||||||
public class EmulatedDeviceOperator : IDeviceOperator
|
public class EmulatedDeviceOperator : IDeviceOperator
|
||||||
{
|
{
|
||||||
private EmulatedGameCard GameCard { get; set; }
|
private EmulatedGameCard GameCard { get; set; }
|
||||||
|
private EmulatedSdCard SdCard { get; set; }
|
||||||
|
|
||||||
public EmulatedDeviceOperator(EmulatedGameCard gameCard)
|
public EmulatedDeviceOperator(EmulatedGameCard gameCard, EmulatedSdCard sdCard)
|
||||||
{
|
{
|
||||||
GameCard = gameCard;
|
GameCard = gameCard;
|
||||||
|
SdCard = sdCard;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result IsSdCardInserted(out bool isInserted)
|
public Result IsSdCardInserted(out bool isInserted)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
isInserted = SdCard.IsSdCardInserted();
|
||||||
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result IsGameCardInserted(out bool isInserted)
|
public Result IsGameCardInserted(out bool isInserted)
|
||||||
|
@ -61,6 +61,7 @@ namespace LibHac.FsService
|
|||||||
xci = CardImage;
|
xci = CardImage;
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result Read(GameCardHandle handle, long offset, Span<byte> destination)
|
public Result Read(GameCardHandle handle, long offset, Span<byte> destination)
|
||||||
{
|
{
|
||||||
if (IsGameCardHandleInvalid(handle)) return ResultFs.InvalidGameCardHandleOnRead.Log();
|
if (IsGameCardHandleInvalid(handle)) return ResultFs.InvalidGameCardHandleOnRead.Log();
|
||||||
|
17
src/LibHac/FsService/EmulatedSdCard.cs
Normal file
17
src/LibHac/FsService/EmulatedSdCard.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
namespace LibHac.FsService
|
||||||
|
{
|
||||||
|
public class EmulatedSdCard
|
||||||
|
{
|
||||||
|
private bool IsInserted { get; set; }
|
||||||
|
|
||||||
|
public bool IsSdCardInserted()
|
||||||
|
{
|
||||||
|
return IsInserted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSdCardInsertionStatus(bool isInserted)
|
||||||
|
{
|
||||||
|
IsInserted = isInserted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -554,7 +554,8 @@ namespace LibHac.FsService
|
|||||||
if (attributeCopy.Type == SaveDataType.Cache)
|
if (attributeCopy.Type == SaveDataType.Cache)
|
||||||
{
|
{
|
||||||
// Check whether the save is on the SD card or the BIS
|
// Check whether the save is on the SD card or the BIS
|
||||||
throw new NotImplementedException();
|
Result rc = GetSpaceIdForCacheStorage(out actualSpaceId, attributeCopy.TitleId);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -854,6 +855,48 @@ namespace LibHac.FsService
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Result GetSpaceIdForCacheStorage(out SaveDataSpaceId spaceId, TitleId programId)
|
||||||
|
{
|
||||||
|
spaceId = default;
|
||||||
|
|
||||||
|
if (FsProxyCore.IsSdCardAccessible)
|
||||||
|
{
|
||||||
|
var filter = new SaveDataFilterInternal();
|
||||||
|
|
||||||
|
filter.SetSaveDataSpaceId(SaveDataSpaceId.SdCache);
|
||||||
|
filter.SetProgramId(programId);
|
||||||
|
filter.SetSaveDataType(SaveDataType.Cache);
|
||||||
|
|
||||||
|
Result rc = FindSaveDataWithFilterImpl(out long count, out _, SaveDataSpaceId.SdCache, ref filter);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
spaceId = SaveDataSpaceId.SdCache;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var filter = new SaveDataFilterInternal();
|
||||||
|
|
||||||
|
filter.SetSaveDataSpaceId(SaveDataSpaceId.User);
|
||||||
|
filter.SetProgramId(programId);
|
||||||
|
filter.SetSaveDataType(SaveDataType.Cache);
|
||||||
|
|
||||||
|
Result rc = FindSaveDataWithFilterImpl(out long count, out _, SaveDataSpaceId.User, ref filter);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
spaceId = SaveDataSpaceId.User;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultFs.TargetNotFound.Log();
|
||||||
|
}
|
||||||
|
|
||||||
public Result DeleteCacheStorage(short index)
|
public Result DeleteCacheStorage(short index)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
@ -973,17 +1016,14 @@ namespace LibHac.FsService
|
|||||||
return FsProxyCore.UnregisterAllExternalKey();
|
return FsProxyCore.UnregisterAllExternalKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result SetSdCardEncryptionSeed(ReadOnlySpan<byte> seed)
|
public Result SetSdCardEncryptionSeed(ref EncryptionSeed seed)
|
||||||
{
|
{
|
||||||
// todo: use struct instead of byte span
|
|
||||||
if (seed.Length != 0x10) return ResultFs.InvalidSize.Log();
|
|
||||||
|
|
||||||
// Missing permission check
|
// Missing permission check
|
||||||
|
|
||||||
Result rc = FsProxyCore.SetSdCardEncryptionSeed(seed);
|
Result rc = FsProxyCore.SetSdCardEncryptionSeed(ref seed);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
// todo: Reset save data indexer
|
FsServer.SaveDataIndexerManager.ResetSdCardIndexer();
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
@ -1095,12 +1135,16 @@ namespace LibHac.FsService
|
|||||||
|
|
||||||
public Result SetSdCardAccessibility(bool isAccessible)
|
public Result SetSdCardAccessibility(bool isAccessible)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
// Missing permission check
|
||||||
|
|
||||||
|
FsProxyCore.IsSdCardAccessible = isAccessible;
|
||||||
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result IsSdCardAccessible(out bool isAccessible)
|
public Result IsSdCardAccessible(out bool isAccessible)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
isAccessible = FsProxyCore.IsSdCardAccessible;
|
||||||
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsSystemSaveDataId(ulong id)
|
private static bool IsSystemSaveDataId(ulong id)
|
||||||
|
@ -21,7 +21,8 @@ namespace LibHac.FsService
|
|||||||
private const string ContentDirectoryName = "Contents";
|
private const string ContentDirectoryName = "Contents";
|
||||||
|
|
||||||
private GlobalAccessLogMode LogMode { get; set; }
|
private GlobalAccessLogMode LogMode { get; set; }
|
||||||
|
public bool IsSdCardAccessible { get; set; }
|
||||||
|
|
||||||
public FileSystemProxyCore(FileSystemCreators fsCreators, ExternalKeySet externalKeys, IDeviceOperator deviceOperator)
|
public FileSystemProxyCore(FileSystemCreators fsCreators, ExternalKeySet externalKeys, IDeviceOperator deviceOperator)
|
||||||
{
|
{
|
||||||
FsCreators = fsCreators;
|
FsCreators = fsCreators;
|
||||||
@ -176,10 +177,10 @@ namespace LibHac.FsService
|
|||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result SetSdCardEncryptionSeed(ReadOnlySpan<byte> seed)
|
public Result SetSdCardEncryptionSeed(ref EncryptionSeed seed)
|
||||||
{
|
{
|
||||||
seed.CopyTo(SdEncryptionSeed);
|
seed.Value.CopyTo(SdEncryptionSeed);
|
||||||
//FsCreators.SaveDataFileSystemCreator.SetSdCardEncryptionSeed(seed);
|
// todo: FsCreators.SaveDataFileSystemCreator.SetSdCardEncryptionSeed(seed);
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.Shim;
|
||||||
using LibHac.FsService.Creators;
|
using LibHac.FsService.Creators;
|
||||||
|
|
||||||
namespace LibHac.FsService
|
namespace LibHac.FsService
|
||||||
@ -35,6 +36,10 @@ namespace LibHac.FsService
|
|||||||
var fsProxy = new FileSystemProxy(FsProxyCore, this);
|
var fsProxy = new FileSystemProxy(FsProxyCore, this);
|
||||||
FsClient = new FileSystemClient(this, fsProxy, Timer);
|
FsClient = new FileSystemClient(this, fsProxy, Timer);
|
||||||
|
|
||||||
|
// NS usually takes care of this
|
||||||
|
if (FsClient.IsSdCardInserted())
|
||||||
|
FsClient.SetSdCardAccessibility(true);
|
||||||
|
|
||||||
SaveDataIndexerManager = new SaveDataIndexerManager(FsClient, SaveIndexerId);
|
SaveDataIndexerManager = new SaveDataIndexerManager(FsClient, SaveIndexerId);
|
||||||
|
|
||||||
fsProxy.CleanUpTemporaryStorage();
|
fsProxy.CleanUpTemporaryStorage();
|
||||||
|
@ -82,7 +82,7 @@ namespace LibHac.FsService
|
|||||||
Result QuerySaveDataInternalStorageTotalSize(out long size, SaveDataSpaceId spaceId, ulong saveDataId);
|
Result QuerySaveDataInternalStorageTotalSize(out long size, SaveDataSpaceId spaceId, ulong saveDataId);
|
||||||
Result GetSaveDataCommitId(out long commitId, SaveDataSpaceId spaceId, ulong saveDataId);
|
Result GetSaveDataCommitId(out long commitId, SaveDataSpaceId spaceId, ulong saveDataId);
|
||||||
Result UnregisterExternalKey(ref RightsId rightsId);
|
Result UnregisterExternalKey(ref RightsId rightsId);
|
||||||
Result SetSdCardEncryptionSeed(ReadOnlySpan<byte> seed);
|
Result SetSdCardEncryptionSeed(ref EncryptionSeed seed);
|
||||||
Result SetSdCardAccessibility(bool isAccessible);
|
Result SetSdCardAccessibility(bool isAccessible);
|
||||||
Result IsSdCardAccessible(out bool isAccessible);
|
Result IsSdCardAccessible(out bool isAccessible);
|
||||||
|
|
||||||
|
10
src/LibHac/FsService/ResultSdmmc.cs
Normal file
10
src/LibHac/FsService/ResultSdmmc.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
namespace LibHac.FsService
|
||||||
|
{
|
||||||
|
public static class ResultSdmmc
|
||||||
|
{
|
||||||
|
public const int ModuleSdmmc = 24;
|
||||||
|
|
||||||
|
public static Result.Base DeviceNotFound => new Result.Base(ModuleSdmmc, 1);
|
||||||
|
public static Result.Base DeviceAsleep => new Result.Base(ModuleSdmmc, 4);
|
||||||
|
}
|
||||||
|
}
|
@ -40,7 +40,7 @@ namespace LibHac.FsService
|
|||||||
case SaveDataSpaceId.SdCache:
|
case SaveDataSpaceId.SdCache:
|
||||||
Monitor.Enter(_sdCardIndexer.Locker);
|
Monitor.Enter(_sdCardIndexer.Locker);
|
||||||
|
|
||||||
// Missing reinitialize if SD handle is old
|
// todo: Missing reinitialize if SD handle is old
|
||||||
|
|
||||||
if (!_sdCardIndexer.IsInitialized)
|
if (!_sdCardIndexer.IsInitialized)
|
||||||
{
|
{
|
||||||
@ -89,6 +89,14 @@ namespace LibHac.FsService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void ResetSdCardIndexer()
|
||||||
|
{
|
||||||
|
lock (_sdCardIndexer.Locker)
|
||||||
|
{
|
||||||
|
_sdCardIndexer.Indexer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private struct IndexerHolder
|
private struct IndexerHolder
|
||||||
{
|
{
|
||||||
public object Locker { get; }
|
public object Locker { get; }
|
||||||
|
@ -60,8 +60,8 @@ namespace LibHac.FsService
|
|||||||
[FieldOffset(0x00)] public bool FilterBySaveDataSpaceId;
|
[FieldOffset(0x00)] public bool FilterBySaveDataSpaceId;
|
||||||
[FieldOffset(0x01)] public SaveDataSpaceId SpaceId;
|
[FieldOffset(0x01)] public SaveDataSpaceId SpaceId;
|
||||||
|
|
||||||
[FieldOffset(0x08)] public bool FilterByTitleId;
|
[FieldOffset(0x08)] public bool FilterByProgramId;
|
||||||
[FieldOffset(0x10)] public TitleId TitleId;
|
[FieldOffset(0x10)] public TitleId ProgramId;
|
||||||
|
|
||||||
[FieldOffset(0x18)] public bool FilterBySaveDataType;
|
[FieldOffset(0x18)] public bool FilterBySaveDataType;
|
||||||
[FieldOffset(0x19)] public SaveDataType SaveDataType;
|
[FieldOffset(0x19)] public SaveDataType SaveDataType;
|
||||||
@ -86,10 +86,10 @@ namespace LibHac.FsService
|
|||||||
|
|
||||||
Rank = (int)filter.Rank;
|
Rank = (int)filter.Rank;
|
||||||
|
|
||||||
if (filter.FilterByTitleId)
|
if (filter.FilterByProgramId)
|
||||||
{
|
{
|
||||||
FilterByTitleId = true;
|
FilterByProgramId = true;
|
||||||
TitleId = filter.TitleId;
|
ProgramId = filter.ProgramId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.FilterBySaveDataType)
|
if (filter.FilterBySaveDataType)
|
||||||
@ -117,6 +117,42 @@ namespace LibHac.FsService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetSaveDataSpaceId(SaveDataSpaceId spaceId)
|
||||||
|
{
|
||||||
|
FilterBySaveDataSpaceId = true;
|
||||||
|
SpaceId = spaceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetProgramId(TitleId value)
|
||||||
|
{
|
||||||
|
FilterByProgramId = true;
|
||||||
|
ProgramId = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSaveDataType(SaveDataType value)
|
||||||
|
{
|
||||||
|
FilterBySaveDataType = true;
|
||||||
|
SaveDataType = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetUserId(UserId value)
|
||||||
|
{
|
||||||
|
FilterByUserId = true;
|
||||||
|
UserId = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSaveDataId(ulong value)
|
||||||
|
{
|
||||||
|
FilterBySaveDataId = true;
|
||||||
|
SaveDataId = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetIndex(short value)
|
||||||
|
{
|
||||||
|
FilterByIndex = true;
|
||||||
|
Index = value;
|
||||||
|
}
|
||||||
|
|
||||||
public bool Matches(ref SaveDataInfo info)
|
public bool Matches(ref SaveDataInfo info)
|
||||||
{
|
{
|
||||||
if (FilterBySaveDataSpaceId && info.SpaceId != SpaceId)
|
if (FilterBySaveDataSpaceId && info.SpaceId != SpaceId)
|
||||||
@ -124,7 +160,7 @@ namespace LibHac.FsService
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FilterByTitleId && info.TitleId != TitleId)
|
if (FilterByProgramId && info.TitleId != ProgramId)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.FsService;
|
||||||
|
|
||||||
|
namespace LibHac.Tests.Fs.FileSystemClientTests
|
||||||
|
{
|
||||||
|
public static class FileSystemServerFactory
|
||||||
|
{
|
||||||
|
public static FileSystemServer CreateServer(bool sdCardInserted)
|
||||||
|
{
|
||||||
|
var rootFs = new InMemoryFileSystem();
|
||||||
|
|
||||||
|
var defaultObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(rootFs, new Keyset());
|
||||||
|
|
||||||
|
defaultObjects.SdCard.SetSdCardInsertionStatus(sdCardInserted);
|
||||||
|
|
||||||
|
var config = new FileSystemServerConfig();
|
||||||
|
config.FsCreators = defaultObjects.FsCreators;
|
||||||
|
config.DeviceOperator = defaultObjects.DeviceOperator;
|
||||||
|
config.ExternalKeySet = new ExternalKeySet();
|
||||||
|
|
||||||
|
var fsServer = new FileSystemServer(config);
|
||||||
|
|
||||||
|
return fsServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FileSystemClient CreateClient(bool sdCardInserted)
|
||||||
|
{
|
||||||
|
FileSystemServer fsServer = CreateServer(sdCardInserted);
|
||||||
|
|
||||||
|
return fsServer.CreateFileSystemClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.Shim;
|
||||||
|
using LibHac.Ncm;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace LibHac.Tests.Fs.FileSystemClientTests.ShimTests
|
||||||
|
{
|
||||||
|
public class SaveData
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void MountCacheStorage_CanMountCreatedCacheStorage()
|
||||||
|
{
|
||||||
|
var applicationId = new TitleId(1);
|
||||||
|
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||||
|
|
||||||
|
fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId, 0, 0, SaveDataFlags.None);
|
||||||
|
|
||||||
|
Assert.Success(fs.MountCacheStorage("cache".ToU8String(), applicationId));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MountCacheStorage_WrittenDataPersists()
|
||||||
|
{
|
||||||
|
var applicationId = new TitleId(1);
|
||||||
|
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||||
|
|
||||||
|
fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdCache, applicationId, 0, 0, SaveDataFlags.None);
|
||||||
|
fs.MountCacheStorage("cache".ToU8String(), applicationId);
|
||||||
|
|
||||||
|
fs.CreateFile("cache:/file", 0);
|
||||||
|
fs.Commit("cache");
|
||||||
|
fs.Unmount("cache");
|
||||||
|
|
||||||
|
Assert.Success(fs.MountCacheStorage("cache".ToU8String(), applicationId));
|
||||||
|
Assert.Success(fs.GetEntryType(out DirectoryEntryType type, "cache:/file"));
|
||||||
|
Assert.Equal(DirectoryEntryType.File, type);
|
||||||
|
}
|
||||||
|
[Fact]
|
||||||
|
public void MountCacheStorage_SdCardIsPreferredOverBis()
|
||||||
|
{
|
||||||
|
var applicationId = new TitleId(1);
|
||||||
|
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||||
|
|
||||||
|
fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdCache, applicationId, 0, 0, SaveDataFlags.None);
|
||||||
|
fs.MountCacheStorage("cache".ToU8String(), applicationId);
|
||||||
|
fs.CreateFile("cache:/sd", 0);
|
||||||
|
fs.Commit("cache");
|
||||||
|
fs.Unmount("cache");
|
||||||
|
|
||||||
|
// Turn off the SD card so the User save is mounted
|
||||||
|
fs.SetSdCardAccessibility(false);
|
||||||
|
|
||||||
|
fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId, 0, 0, SaveDataFlags.None);
|
||||||
|
fs.MountCacheStorage("cache".ToU8String(), applicationId);
|
||||||
|
fs.CreateFile("cache:/bis", 0);
|
||||||
|
fs.Commit("cache");
|
||||||
|
fs.Unmount("cache");
|
||||||
|
|
||||||
|
fs.SetSdCardAccessibility(true);
|
||||||
|
|
||||||
|
Assert.Success(fs.MountCacheStorage("cache".ToU8String(), applicationId));
|
||||||
|
Assert.Success(fs.GetEntryType(out _, "cache:/sd"));
|
||||||
|
Assert.Failure(fs.GetEntryType(out _, "cache:/bis"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.Shim;
|
||||||
|
using LibHac.Ncm;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace LibHac.Tests.Fs.FileSystemClientTests.ShimTests
|
||||||
|
{
|
||||||
|
public class SaveDataManagement
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void CreateCacheStorage_InUserSaveSpace_StorageIsCreated()
|
||||||
|
{
|
||||||
|
var applicationId = new TitleId(1);
|
||||||
|
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||||
|
|
||||||
|
Assert.Success(fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId, 0, 0, SaveDataFlags.None));
|
||||||
|
|
||||||
|
fs.OpenSaveDataIterator(out SaveDataIterator iterator, SaveDataSpaceId.User);
|
||||||
|
|
||||||
|
var info = new SaveDataInfo[2];
|
||||||
|
iterator.ReadSaveDataInfo(out long entriesRead, info);
|
||||||
|
|
||||||
|
Assert.Equal(1, entriesRead);
|
||||||
|
Assert.Equal(applicationId, info[0].TitleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateCacheStorage_InSdCacheSaveSpace_StorageIsCreated()
|
||||||
|
{
|
||||||
|
var applicationId = new TitleId(1);
|
||||||
|
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||||
|
|
||||||
|
Assert.Success(fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdCache, applicationId, 0, 0, SaveDataFlags.None));
|
||||||
|
|
||||||
|
fs.OpenSaveDataIterator(out SaveDataIterator iterator, SaveDataSpaceId.SdCache);
|
||||||
|
|
||||||
|
var info = new SaveDataInfo[2];
|
||||||
|
iterator.ReadSaveDataInfo(out long entriesRead, info);
|
||||||
|
|
||||||
|
Assert.Equal(1, entriesRead);
|
||||||
|
Assert.Equal(applicationId, info[0].TitleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateCacheStorage_InSdCacheSaveSpaceWhenNoSdCard_ReturnsSdCardNotFound()
|
||||||
|
{
|
||||||
|
var applicationId = new TitleId(1);
|
||||||
|
FileSystemClient fs = FileSystemServerFactory.CreateClient(false);
|
||||||
|
|
||||||
|
Assert.Result(ResultFs.SdCardNotFound, fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdCache, applicationId, 0, 0, SaveDataFlags.None));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateCacheStorage_AlreadyExists_ReturnsPathAlreadyExists()
|
||||||
|
{
|
||||||
|
var applicationId = new TitleId(1);
|
||||||
|
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||||
|
|
||||||
|
Assert.Success(fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId, 0, 0, SaveDataFlags.None));
|
||||||
|
Assert.Result(ResultFs.PathAlreadyExists, fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId, 0, 0, SaveDataFlags.None));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateCacheStorage_WithIndex_CreatesMultiple()
|
||||||
|
{
|
||||||
|
var applicationId = new TitleId(1);
|
||||||
|
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||||
|
|
||||||
|
Assert.Success(fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId, 0, 0, 0, SaveDataFlags.None));
|
||||||
|
Assert.Success(fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId, 1, 0, 0, SaveDataFlags.None));
|
||||||
|
|
||||||
|
fs.OpenSaveDataIterator(out SaveDataIterator iterator, SaveDataSpaceId.User);
|
||||||
|
|
||||||
|
var info = new SaveDataInfo[3];
|
||||||
|
iterator.ReadSaveDataInfo(out long entriesRead, info);
|
||||||
|
|
||||||
|
Assert.Equal(2, entriesRead);
|
||||||
|
Assert.Equal(applicationId, info[0].TitleId);
|
||||||
|
Assert.Equal(applicationId, info[1].TitleId);
|
||||||
|
|
||||||
|
var expectedIndexes = new short[] { 0, 1 };
|
||||||
|
short[] actualIndexes = info.Take(2).Select(x => x.Index).OrderBy(x => x).ToArray();
|
||||||
|
|
||||||
|
Assert.Equal(expectedIndexes, actualIndexes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.Shim;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace LibHac.Tests.Fs.FileSystemClientTests.ShimTests
|
||||||
|
{
|
||||||
|
public class SdCard
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void MountSdCard_CardIsInserted_Succeeds()
|
||||||
|
{
|
||||||
|
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||||
|
|
||||||
|
Assert.Success(fs.MountSdCard("sdcard".ToU8String()));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MountSdCard_CardIsNotInserted_Fails()
|
||||||
|
{
|
||||||
|
FileSystemClient fs = FileSystemServerFactory.CreateClient(false);
|
||||||
|
|
||||||
|
Assert.Result(ResultFs.SdCardNotFound, fs.MountSdCard("sdcard".ToU8String()));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MountSdCard_CanWriteToFsAfterMounted()
|
||||||
|
{
|
||||||
|
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||||
|
|
||||||
|
fs.MountSdCard("sdcard".ToU8String());
|
||||||
|
|
||||||
|
Assert.Success(fs.CreateFile("sdcard:/file", 100, CreateFileOptions.None));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IsSdCardInserted_CardIsInserted_ReturnsTrue()
|
||||||
|
{
|
||||||
|
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||||
|
|
||||||
|
Assert.True(fs.IsSdCardInserted());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IsSdCardInserted_CardIsNotInserted_ReturnsFalse()
|
||||||
|
{
|
||||||
|
FileSystemClient fs = FileSystemServerFactory.CreateClient(false);
|
||||||
|
|
||||||
|
Assert.False(fs.IsSdCardInserted());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IsSdCardAccessible_CardIsInserted_ReturnsTrue()
|
||||||
|
{
|
||||||
|
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||||
|
|
||||||
|
Assert.True(fs.IsSdCardAccessible());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IsSdCardAccessible_CardIsNotInserted_ReturnsFalse()
|
||||||
|
{
|
||||||
|
FileSystemClient fs = FileSystemServerFactory.CreateClient(false);
|
||||||
|
|
||||||
|
Assert.False(fs.IsSdCardAccessible());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetSdCardAccessibility_SetAccessibilityPersists()
|
||||||
|
{
|
||||||
|
FileSystemClient fs = FileSystemServerFactory.CreateClient(false);
|
||||||
|
|
||||||
|
fs.SetSdCardAccessibility(true);
|
||||||
|
Assert.True(fs.IsSdCardAccessible());
|
||||||
|
|
||||||
|
fs.SetSdCardAccessibility(false);
|
||||||
|
Assert.False(fs.IsSdCardAccessible());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
tests/LibHac.Tests/GlobalSuppressions.cs
Normal file
7
tests/LibHac.Tests/GlobalSuppressions.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// This file is used by Code Analysis to maintain SuppressMessage
|
||||||
|
// attributes that are applied to this project.
|
||||||
|
// Project-level suppressions either have no target or are given
|
||||||
|
// a specific target and scoped to a namespace, type, member, etc.
|
||||||
|
|
||||||
|
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Assertions", "xUnit2007:Do not use typeof expression to check the type", Justification = "Analyzer doesn't apply to Xunit internals.", Scope = "namespaceanddescendants", Target = "Xunit")]
|
||||||
|
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Assertions", "xUnit2015:Do not use typeof expression to check the exception type", Justification = "Analyzer doesn't apply to Xunit internals.", Scope = "namespaceanddescendants", Target = "Xunit")]
|
@ -8,7 +8,9 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||||
<PackageReference Include="xunit" Version="2.4.1" />
|
<PackageReference Include="xunit.core" Version="2.4.1" />
|
||||||
|
<PackageReference Include="xunit.analyzers" Version="0.10.0" />
|
||||||
|
<PackageReference Include="xunit.assert.source" Version="2.4.1" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
25
tests/LibHac.Tests/ResultAsserts.cs
Normal file
25
tests/LibHac.Tests/ResultAsserts.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using LibHac;
|
||||||
|
using Xunit.Sdk;
|
||||||
|
|
||||||
|
// ReSharper disable once CheckNamespace
|
||||||
|
namespace Xunit
|
||||||
|
{
|
||||||
|
public partial class Assert
|
||||||
|
{
|
||||||
|
public static void Success(Result result)
|
||||||
|
{
|
||||||
|
Equal(LibHac.Result.Success, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Failure(Result result)
|
||||||
|
{
|
||||||
|
NotEqual(LibHac.Result.Success, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Result(Result.Base expected, Result actual)
|
||||||
|
{
|
||||||
|
if (!expected.Includes(actual))
|
||||||
|
throw new EqualException(expected.Value, actual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user