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)
|
||||
{
|
||||
var filter = new SaveDataFilter();
|
||||
filter.SetTitleId(applicationId);
|
||||
filter.SetProgramId(applicationId);
|
||||
filter.SetSaveDataType(SaveDataType.Account);
|
||||
filter.SetUserId(new UserId(uid.Id.High, uid.Id.Low));
|
||||
|
||||
@ -82,7 +82,7 @@ namespace LibHac.Fs
|
||||
if (nacp.DeviceSaveDataSize > 0)
|
||||
{
|
||||
var filter = new SaveDataFilter();
|
||||
filter.SetTitleId(applicationId);
|
||||
filter.SetProgramId(applicationId);
|
||||
filter.SetSaveDataType(SaveDataType.Device);
|
||||
|
||||
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
|
||||
// save already exists instead of trying to create a new one.
|
||||
var filter = new SaveDataFilter();
|
||||
filter.SetTitleId(applicationId);
|
||||
filter.SetProgramId(applicationId);
|
||||
filter.SetSaveDataType(SaveDataType.Temporary);
|
||||
|
||||
Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.Temporary, ref filter);
|
||||
@ -234,7 +234,7 @@ namespace LibHac.Fs
|
||||
}
|
||||
|
||||
var filter = new SaveDataFilter();
|
||||
filter.SetTitleId(applicationId);
|
||||
filter.SetProgramId(applicationId);
|
||||
filter.SetSaveDataType(SaveDataType.Bcat);
|
||||
|
||||
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,
|
||||
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 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 InvalidBufferForGameCard => new Result.Base(ModuleFs, 2503);
|
||||
|
@ -56,23 +56,23 @@ namespace LibHac.Fs
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x48)]
|
||||
public struct SaveDataFilter
|
||||
{
|
||||
[FieldOffset(0x00)] public bool FilterByTitleId;
|
||||
[FieldOffset(0x00)] public bool FilterByProgramId;
|
||||
[FieldOffset(0x01)] public bool FilterBySaveDataType;
|
||||
[FieldOffset(0x02)] public bool FilterByUserId;
|
||||
[FieldOffset(0x03)] public bool FilterBySaveDataId;
|
||||
[FieldOffset(0x04)] public bool FilterByIndex;
|
||||
[FieldOffset(0x05)] public SaveDataRank Rank;
|
||||
|
||||
[FieldOffset(0x08)] public TitleId TitleId;
|
||||
[FieldOffset(0x08)] public TitleId ProgramId;
|
||||
[FieldOffset(0x10)] public UserId UserId;
|
||||
[FieldOffset(0x20)] public ulong SaveDataId;
|
||||
[FieldOffset(0x28)] public SaveDataType SaveDataType;
|
||||
[FieldOffset(0x2A)] public short Index;
|
||||
|
||||
public void SetTitleId(TitleId value)
|
||||
public void SetProgramId(TitleId value)
|
||||
{
|
||||
FilterByTitleId = true;
|
||||
TitleId = value;
|
||||
FilterByProgramId = true;
|
||||
ProgramId = 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 EmulatedSdCard SdCard { get; }
|
||||
private IFileSystem RootFileSystem { get; }
|
||||
private string Path { get; }
|
||||
|
||||
private IFileSystem SdCardFileSystem { get; set; }
|
||||
|
||||
public EmulatedSdFileSystemCreator(IFileSystem rootFileSystem)
|
||||
public EmulatedSdFileSystemCreator(EmulatedSdCard sdCard, IFileSystem rootFileSystem)
|
||||
{
|
||||
SdCard = sdCard;
|
||||
RootFileSystem = rootFileSystem;
|
||||
}
|
||||
|
||||
public EmulatedSdFileSystemCreator(IFileSystem rootFileSystem, string path)
|
||||
public EmulatedSdFileSystemCreator(EmulatedSdCard sdCard, IFileSystem rootFileSystem, string path)
|
||||
{
|
||||
SdCard = sdCard;
|
||||
RootFileSystem = rootFileSystem;
|
||||
Path = path;
|
||||
}
|
||||
@ -27,6 +30,11 @@ namespace LibHac.FsService.Creators
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
if (!SdCard.IsSdCardInserted())
|
||||
{
|
||||
return ResultFs.SdCardNotFound.Log();
|
||||
}
|
||||
|
||||
if (SdCardFileSystem != null)
|
||||
{
|
||||
fileSystem = SdCardFileSystem;
|
||||
|
@ -8,11 +8,13 @@ namespace LibHac.FsService
|
||||
public FileSystemCreators FsCreators { get; set; }
|
||||
public IDeviceOperator DeviceOperator { get; set; }
|
||||
public EmulatedGameCard GameCard { get; set; }
|
||||
public EmulatedSdCard SdCard { get; set; }
|
||||
|
||||
public static DefaultFsServerObjects GetDefaultEmulatedCreators(IFileSystem rootFileSystem, Keyset keyset)
|
||||
{
|
||||
var creators = new FileSystemCreators();
|
||||
var gameCard = new EmulatedGameCard(keyset);
|
||||
var sdCard = new EmulatedSdCard();
|
||||
|
||||
var gcStorageCreator = new EmulatedGameCardStorageCreator(gameCard);
|
||||
|
||||
@ -26,15 +28,16 @@ namespace LibHac.FsService
|
||||
creators.GameCardFileSystemCreator = new EmulatedGameCardFsCreator(gcStorageCreator, gameCard);
|
||||
creators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator(keyset);
|
||||
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
|
||||
{
|
||||
FsCreators = creators,
|
||||
DeviceOperator = deviceOperator,
|
||||
GameCard = gameCard
|
||||
GameCard = gameCard,
|
||||
SdCard = sdCard
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,22 @@
|
||||
using System;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs;
|
||||
|
||||
namespace LibHac.FsService
|
||||
{
|
||||
public class EmulatedDeviceOperator : IDeviceOperator
|
||||
{
|
||||
private EmulatedGameCard GameCard { get; set; }
|
||||
private EmulatedSdCard SdCard { get; set; }
|
||||
|
||||
public EmulatedDeviceOperator(EmulatedGameCard gameCard)
|
||||
public EmulatedDeviceOperator(EmulatedGameCard gameCard, EmulatedSdCard sdCard)
|
||||
{
|
||||
GameCard = gameCard;
|
||||
SdCard = sdCard;
|
||||
}
|
||||
|
||||
public Result IsSdCardInserted(out bool isInserted)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
isInserted = SdCard.IsSdCardInserted();
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result IsGameCardInserted(out bool isInserted)
|
||||
|
@ -61,6 +61,7 @@ namespace LibHac.FsService
|
||||
xci = CardImage;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result Read(GameCardHandle handle, long offset, Span<byte> destination)
|
||||
{
|
||||
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)
|
||||
{
|
||||
// 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
|
||||
{
|
||||
@ -854,6 +855,48 @@ namespace LibHac.FsService
|
||||
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)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
@ -973,17 +1016,14 @@ namespace LibHac.FsService
|
||||
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
|
||||
|
||||
Result rc = FsProxyCore.SetSdCardEncryptionSeed(seed);
|
||||
Result rc = FsProxyCore.SetSdCardEncryptionSeed(ref seed);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// todo: Reset save data indexer
|
||||
FsServer.SaveDataIndexerManager.ResetSdCardIndexer();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
@ -1095,12 +1135,16 @@ namespace LibHac.FsService
|
||||
|
||||
public Result SetSdCardAccessibility(bool isAccessible)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
// Missing permission check
|
||||
|
||||
FsProxyCore.IsSdCardAccessible = isAccessible;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result IsSdCardAccessible(out bool isAccessible)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
isAccessible = FsProxyCore.IsSdCardAccessible;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private static bool IsSystemSaveDataId(ulong id)
|
||||
|
@ -21,7 +21,8 @@ namespace LibHac.FsService
|
||||
private const string ContentDirectoryName = "Contents";
|
||||
|
||||
private GlobalAccessLogMode LogMode { get; set; }
|
||||
|
||||
public bool IsSdCardAccessible { get; set; }
|
||||
|
||||
public FileSystemProxyCore(FileSystemCreators fsCreators, ExternalKeySet externalKeys, IDeviceOperator deviceOperator)
|
||||
{
|
||||
FsCreators = fsCreators;
|
||||
@ -176,10 +177,10 @@ namespace LibHac.FsService
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result SetSdCardEncryptionSeed(ReadOnlySpan<byte> seed)
|
||||
public Result SetSdCardEncryptionSeed(ref EncryptionSeed seed)
|
||||
{
|
||||
seed.CopyTo(SdEncryptionSeed);
|
||||
//FsCreators.SaveDataFileSystemCreator.SetSdCardEncryptionSeed(seed);
|
||||
seed.Value.CopyTo(SdEncryptionSeed);
|
||||
// todo: FsCreators.SaveDataFileSystemCreator.SetSdCardEncryptionSeed(seed);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Shim;
|
||||
using LibHac.FsService.Creators;
|
||||
|
||||
namespace LibHac.FsService
|
||||
@ -35,6 +36,10 @@ namespace LibHac.FsService
|
||||
var fsProxy = new FileSystemProxy(FsProxyCore, this);
|
||||
FsClient = new FileSystemClient(this, fsProxy, Timer);
|
||||
|
||||
// NS usually takes care of this
|
||||
if (FsClient.IsSdCardInserted())
|
||||
FsClient.SetSdCardAccessibility(true);
|
||||
|
||||
SaveDataIndexerManager = new SaveDataIndexerManager(FsClient, SaveIndexerId);
|
||||
|
||||
fsProxy.CleanUpTemporaryStorage();
|
||||
|
@ -82,7 +82,7 @@ namespace LibHac.FsService
|
||||
Result QuerySaveDataInternalStorageTotalSize(out long size, SaveDataSpaceId spaceId, ulong saveDataId);
|
||||
Result GetSaveDataCommitId(out long commitId, SaveDataSpaceId spaceId, ulong saveDataId);
|
||||
Result UnregisterExternalKey(ref RightsId rightsId);
|
||||
Result SetSdCardEncryptionSeed(ReadOnlySpan<byte> seed);
|
||||
Result SetSdCardEncryptionSeed(ref EncryptionSeed seed);
|
||||
Result SetSdCardAccessibility(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:
|
||||
Monitor.Enter(_sdCardIndexer.Locker);
|
||||
|
||||
// Missing reinitialize if SD handle is old
|
||||
// todo: Missing reinitialize if SD handle is old
|
||||
|
||||
if (!_sdCardIndexer.IsInitialized)
|
||||
{
|
||||
@ -89,6 +89,14 @@ namespace LibHac.FsService
|
||||
}
|
||||
}
|
||||
|
||||
internal void ResetSdCardIndexer()
|
||||
{
|
||||
lock (_sdCardIndexer.Locker)
|
||||
{
|
||||
_sdCardIndexer.Indexer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private struct IndexerHolder
|
||||
{
|
||||
public object Locker { get; }
|
||||
|
@ -60,8 +60,8 @@ namespace LibHac.FsService
|
||||
[FieldOffset(0x00)] public bool FilterBySaveDataSpaceId;
|
||||
[FieldOffset(0x01)] public SaveDataSpaceId SpaceId;
|
||||
|
||||
[FieldOffset(0x08)] public bool FilterByTitleId;
|
||||
[FieldOffset(0x10)] public TitleId TitleId;
|
||||
[FieldOffset(0x08)] public bool FilterByProgramId;
|
||||
[FieldOffset(0x10)] public TitleId ProgramId;
|
||||
|
||||
[FieldOffset(0x18)] public bool FilterBySaveDataType;
|
||||
[FieldOffset(0x19)] public SaveDataType SaveDataType;
|
||||
@ -86,10 +86,10 @@ namespace LibHac.FsService
|
||||
|
||||
Rank = (int)filter.Rank;
|
||||
|
||||
if (filter.FilterByTitleId)
|
||||
if (filter.FilterByProgramId)
|
||||
{
|
||||
FilterByTitleId = true;
|
||||
TitleId = filter.TitleId;
|
||||
FilterByProgramId = true;
|
||||
ProgramId = filter.ProgramId;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (FilterBySaveDataSpaceId && info.SpaceId != SpaceId)
|
||||
@ -124,7 +160,7 @@ namespace LibHac.FsService
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FilterByTitleId && info.TitleId != TitleId)
|
||||
if (FilterByProgramId && info.TitleId != ProgramId)
|
||||
{
|
||||
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>
|
||||
<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" />
|
||||
</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