mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add DeviceSaveData mounting functions
This commit is contained in:
parent
c2e4b80bac
commit
e70e23492f
4 changed files with 308 additions and 30 deletions
179
src/LibHac/Fs/Shim/DeviceSaveData.cs
Normal file
179
src/LibHac/Fs/Shim/DeviceSaveData.cs
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
using System;
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Diag;
|
||||||
|
using LibHac.Fs.Fsa;
|
||||||
|
using LibHac.Fs.Impl;
|
||||||
|
using LibHac.FsSrv.Sf;
|
||||||
|
using LibHac.Ncm;
|
||||||
|
using LibHac.Os;
|
||||||
|
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
||||||
|
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||||
|
|
||||||
|
using static LibHac.Fs.Impl.AccessLogStrings;
|
||||||
|
using static LibHac.Fs.SaveData;
|
||||||
|
|
||||||
|
namespace LibHac.Fs.Shim;
|
||||||
|
|
||||||
|
public static class DeviceSaveData
|
||||||
|
{
|
||||||
|
private class DeviceSaveDataAttributeGetter : ISaveDataAttributeGetter
|
||||||
|
{
|
||||||
|
private ProgramId _programId;
|
||||||
|
|
||||||
|
public DeviceSaveDataAttributeGetter(ProgramId programId)
|
||||||
|
{
|
||||||
|
_programId = programId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() { }
|
||||||
|
|
||||||
|
public Result GetSaveDataAttribute(out SaveDataAttribute attribute)
|
||||||
|
{
|
||||||
|
Result rc = SaveDataAttribute.Make(out attribute, _programId, SaveDataType.Device, InvalidUserId,
|
||||||
|
InvalidSystemSaveDataId);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const SaveDataSpaceId DeviceSaveDataSpaceId = SaveDataSpaceId.User;
|
||||||
|
|
||||||
|
private static Result MountDeviceSaveDataImpl(this FileSystemClientImpl fs, U8Span mountName,
|
||||||
|
in SaveDataAttribute attribute)
|
||||||
|
{
|
||||||
|
Result rc = fs.CheckMountName(mountName);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
|
||||||
|
using var fileSystem = new SharedRef<IFileSystemSf>();
|
||||||
|
|
||||||
|
rc = fileSystemProxy.Get.OpenSaveDataFileSystem(ref fileSystem.Ref(), DeviceSaveDataSpaceId, in attribute);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
var fileSystemAdapterRaw = new FileSystemServiceObjectAdapter(ref fileSystem.Ref());
|
||||||
|
using var fileSystemAdapter = new UniqueRef<IFileSystem>(fileSystemAdapterRaw);
|
||||||
|
|
||||||
|
if (!fileSystemAdapter.HasValue)
|
||||||
|
return ResultFs.AllocationMemoryFailedInDeviceSaveDataA.Log();
|
||||||
|
|
||||||
|
using var saveDataAttributeGetter =
|
||||||
|
new UniqueRef<ISaveDataAttributeGetter>(new DeviceSaveDataAttributeGetter(attribute.ProgramId));
|
||||||
|
|
||||||
|
using var mountNameGenerator = new UniqueRef<ICommonMountNameGenerator>();
|
||||||
|
|
||||||
|
rc = fs.Fs.Register(mountName, fileSystemAdapterRaw, ref fileSystemAdapter.Ref(), ref mountNameGenerator.Ref(),
|
||||||
|
ref saveDataAttributeGetter.Ref(), useDataCache: false, storageForPurgeFileDataCache: null,
|
||||||
|
usePathCache: true);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result MountDeviceSaveData(this FileSystemClient fs, U8Span mountName)
|
||||||
|
{
|
||||||
|
Span<byte> logBuffer = stackalloc byte[0x30];
|
||||||
|
|
||||||
|
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, InvalidProgramId, SaveDataType.Device,
|
||||||
|
InvalidUserId, InvalidSystemSaveDataId);
|
||||||
|
|
||||||
|
fs.Impl.AbortIfNeeded(rc);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||||
|
{
|
||||||
|
Tick start = fs.Hos.Os.GetSystemTick();
|
||||||
|
rc = MountDeviceSaveDataImpl(fs.Impl, mountName, in attribute);
|
||||||
|
Tick end = fs.Hos.Os.GetSystemTick();
|
||||||
|
|
||||||
|
var sb = new U8StringBuilder(logBuffer, true);
|
||||||
|
sb.Append(LogName).Append(mountName).Append(LogQuote);
|
||||||
|
|
||||||
|
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rc = MountDeviceSaveDataImpl(fs.Impl, mountName, in attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.Impl.AbortIfNeeded(rc);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||||
|
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result MountDeviceSaveData(this FileSystemClient fs, U8Span mountName,
|
||||||
|
Ncm.ApplicationId applicationId)
|
||||||
|
{
|
||||||
|
Span<byte> logBuffer = stackalloc byte[0x50];
|
||||||
|
|
||||||
|
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Device,
|
||||||
|
InvalidUserId, InvalidSystemSaveDataId);
|
||||||
|
|
||||||
|
fs.Impl.AbortIfNeeded(rc);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||||
|
{
|
||||||
|
Tick start = fs.Hos.Os.GetSystemTick();
|
||||||
|
rc = MountDeviceSaveDataImpl(fs.Impl, mountName, in attribute);
|
||||||
|
Tick end = fs.Hos.Os.GetSystemTick();
|
||||||
|
|
||||||
|
var sb = new U8StringBuilder(logBuffer, true);
|
||||||
|
sb.Append(LogName).Append(mountName).Append(LogQuote)
|
||||||
|
.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X');
|
||||||
|
|
||||||
|
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rc = MountDeviceSaveDataImpl(fs.Impl, mountName, in attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.Impl.AbortIfNeeded(rc);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||||
|
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result MountDeviceSaveData(this FileSystemClient fs, U8Span mountName, ApplicationId applicationId)
|
||||||
|
{
|
||||||
|
return MountDeviceSaveData(fs, mountName, new Ncm.ApplicationId(applicationId.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsDeviceSaveDataExisting(this FileSystemClient fs, ApplicationId applicationId)
|
||||||
|
{
|
||||||
|
Result rc;
|
||||||
|
Span<byte> logBuffer = stackalloc byte[0x30];
|
||||||
|
|
||||||
|
bool exists;
|
||||||
|
var appId = new Ncm.ApplicationId(applicationId.Value);
|
||||||
|
|
||||||
|
if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(null))
|
||||||
|
{
|
||||||
|
Tick start = fs.Hos.Os.GetSystemTick();
|
||||||
|
rc = fs.Impl.IsSaveDataExisting(out exists, appId, SaveDataType.Device, InvalidUserId);
|
||||||
|
Tick end = fs.Hos.Os.GetSystemTick();
|
||||||
|
|
||||||
|
var sb = new U8StringBuilder(logBuffer, true);
|
||||||
|
sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X');
|
||||||
|
|
||||||
|
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rc = fs.Impl.IsSaveDataExisting(out exists, appId, SaveDataType.Device, InvalidUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.Impl.LogResultErrorMessage(rc);
|
||||||
|
Abort.DoAbortUnless(rc.IsSuccess());
|
||||||
|
|
||||||
|
return exists;
|
||||||
|
}
|
||||||
|
}
|
|
@ -147,6 +147,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
|
||||||
}
|
}
|
||||||
else if (attribute.Type == SaveDataType.Account && attribute.UserId == InvalidUserId)
|
else if (attribute.Type == SaveDataType.Account && attribute.UserId == InvalidUserId)
|
||||||
{
|
{
|
||||||
|
// Trying to create a program's debug save.
|
||||||
bool canAccess =
|
bool canAccess =
|
||||||
accessControl.CanCall(OperationType.CreateSaveData) ||
|
accessControl.CanCall(OperationType.CreateSaveData) ||
|
||||||
accessControl.CanCall(OperationType.DebugSaveData);
|
accessControl.CanCall(OperationType.DebugSaveData);
|
||||||
|
@ -193,6 +194,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
|
||||||
}
|
}
|
||||||
else if (attribute.Type == SaveDataType.Account)
|
else if (attribute.Type == SaveDataType.Account)
|
||||||
{
|
{
|
||||||
|
// We need debug save data permissions to open a debug save.
|
||||||
bool canAccess = attribute.UserId != InvalidUserId ||
|
bool canAccess = attribute.UserId != InvalidUserId ||
|
||||||
accessControl.CanCall(OperationType.DebugSaveData);
|
accessControl.CanCall(OperationType.DebugSaveData);
|
||||||
|
|
||||||
|
@ -303,11 +305,11 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
|
||||||
{
|
{
|
||||||
canAccess |= accessControl.CanCall(OperationType.ExtendOwnSaveData);
|
canAccess |= accessControl.CanCall(OperationType.ExtendOwnSaveData);
|
||||||
|
|
||||||
bool hasDebugAccess = accessControl.CanCall(OperationType.DebugSaveData)
|
bool canAccessDebugSave = accessControl.CanCall(OperationType.DebugSaveData)
|
||||||
&& attribute.Type == SaveDataType.Account
|
&& attribute.Type == SaveDataType.Account
|
||||||
&& attribute.UserId == UserId.InvalidId;
|
&& attribute.UserId == UserId.InvalidId;
|
||||||
|
|
||||||
canAccess |= hasDebugAccess;
|
canAccess |= canAccessDebugSave;
|
||||||
|
|
||||||
if (!canAccess)
|
if (!canAccess)
|
||||||
return ResultFs.PermissionDenied.Log();
|
return ResultFs.PermissionDenied.Log();
|
||||||
|
@ -351,11 +353,11 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
|
||||||
{
|
{
|
||||||
canAccess |= accessControl.CanCall(OperationType.ReadOwnSaveDataFileSystemExtraData);
|
canAccess |= accessControl.CanCall(OperationType.ReadOwnSaveDataFileSystemExtraData);
|
||||||
|
|
||||||
bool hasDebugAccess = accessControl.CanCall(OperationType.DebugSaveData)
|
bool canAccessDebugSave = accessControl.CanCall(OperationType.DebugSaveData)
|
||||||
&& attribute.Type == SaveDataType.Account
|
&& attribute.Type == SaveDataType.Account
|
||||||
&& attribute.UserId == UserId.InvalidId;
|
&& attribute.UserId == UserId.InvalidId;
|
||||||
|
|
||||||
canAccess |= hasDebugAccess;
|
canAccess |= canAccessDebugSave;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!canAccess)
|
if (!canAccess)
|
||||||
|
@ -442,11 +444,11 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
|
||||||
AccessControl accessControl = programInfo.AccessControl;
|
AccessControl accessControl = programInfo.AccessControl;
|
||||||
canAccess = accessControl.CanCall(OperationType.FindOwnSaveDataWithFilter);
|
canAccess = accessControl.CanCall(OperationType.FindOwnSaveDataWithFilter);
|
||||||
|
|
||||||
bool hasDebugAccess = accessControl.CanCall(OperationType.DebugSaveData)
|
bool canAccessDebugSave = accessControl.CanCall(OperationType.DebugSaveData)
|
||||||
&& filter.Attribute.Type == SaveDataType.Account
|
&& filter.Attribute.Type == SaveDataType.Account
|
||||||
&& filter.Attribute.UserId == UserId.InvalidId;
|
&& filter.Attribute.UserId == UserId.InvalidId;
|
||||||
|
|
||||||
canAccess |= hasDebugAccess;
|
canAccess |= canAccessDebugSave;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,27 +2,45 @@
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.FsSrv;
|
using LibHac.FsSrv;
|
||||||
|
using LibHac.FsSrv.Impl;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
|
using LibHac.Ncm;
|
||||||
using LibHac.Tools.Fs;
|
using LibHac.Tools.Fs;
|
||||||
|
|
||||||
namespace LibHac.Tests.Fs.FileSystemClientTests;
|
namespace LibHac.Tests.Fs.FileSystemClientTests;
|
||||||
|
|
||||||
|
public class HorizonServerSet
|
||||||
|
{
|
||||||
|
public Horizon Server { get; set; }
|
||||||
|
public HorizonClient FsProcessClient { get; set; }
|
||||||
|
public HorizonClient InitialProcessClient { get; set; }
|
||||||
|
public HorizonClient Client { get; set; }
|
||||||
|
public FileSystemServer FsServer { get; set; }
|
||||||
|
public IFileSystem RootFileSystem { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public static class FileSystemServerFactory
|
public static class FileSystemServerFactory
|
||||||
{
|
{
|
||||||
private static Horizon CreateHorizonImpl(bool sdCardInserted, out IFileSystem rootFs)
|
private static HorizonServerSet CreateHorizonImpl(bool sdCardInserted = true,
|
||||||
|
AccessControlBits.Bits fsAcBits = AccessControlBits.Bits.Debug, ProgramLocation programLocation = default)
|
||||||
{
|
{
|
||||||
rootFs = new InMemoryFileSystem();
|
var hos = new HorizonServerSet();
|
||||||
|
hos.RootFileSystem = new InMemoryFileSystem();
|
||||||
var keySet = new KeySet();
|
var keySet = new KeySet();
|
||||||
|
|
||||||
var horizon = new Horizon(new HorizonConfiguration());
|
hos.Server = new Horizon(new HorizonConfiguration());
|
||||||
|
|
||||||
HorizonClient fsServerClient = horizon.CreatePrivilegedHorizonClient();
|
hos.FsProcessClient = hos.Server.CreatePrivilegedHorizonClient();
|
||||||
var fsServer = new FileSystemServer(fsServerClient);
|
hos.FsServer = new FileSystemServer(hos.FsProcessClient);
|
||||||
|
|
||||||
|
hos.InitialProcessClient = hos.Server.CreatePrivilegedHorizonClient();
|
||||||
|
|
||||||
var random = new Random(12345);
|
var random = new Random(12345);
|
||||||
RandomDataGenerator randomGenerator = buffer => random.NextBytes(buffer);
|
RandomDataGenerator randomGenerator = buffer => random.NextBytes(buffer);
|
||||||
|
|
||||||
var defaultObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(rootFs, keySet, fsServer, randomGenerator);
|
var defaultObjects =
|
||||||
|
DefaultFsServerObjects.GetDefaultEmulatedCreators(hos.RootFileSystem, keySet, hos.FsServer,
|
||||||
|
randomGenerator);
|
||||||
|
|
||||||
defaultObjects.SdCard.SetSdCardInsertionStatus(sdCardInserted);
|
defaultObjects.SdCard.SetSdCardInsertionStatus(sdCardInserted);
|
||||||
|
|
||||||
|
@ -32,31 +50,45 @@ public static class FileSystemServerFactory
|
||||||
config.ExternalKeySet = new ExternalKeySet();
|
config.ExternalKeySet = new ExternalKeySet();
|
||||||
config.RandomGenerator = randomGenerator;
|
config.RandomGenerator = randomGenerator;
|
||||||
|
|
||||||
FileSystemServerInitializer.InitializeWithConfig(fsServerClient, fsServer, config);
|
FileSystemServerInitializer.InitializeWithConfig(hos.FsProcessClient, hos.FsServer, config);
|
||||||
return horizon;
|
hos.FsServer.SetDebugFlagEnabled(true);
|
||||||
}
|
|
||||||
|
|
||||||
private static FileSystemClient CreateClientImpl(bool sdCardInserted, out IFileSystem rootFs)
|
if (programLocation.ProgramId == ProgramId.InvalidId)
|
||||||
{
|
{
|
||||||
Horizon horizon = CreateHorizonImpl(sdCardInserted, out rootFs);
|
hos.Client = hos.Server.CreateHorizonClient();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hos.Client = hos.Server.CreateHorizonClient(programLocation, fsAcBits);
|
||||||
|
}
|
||||||
|
|
||||||
HorizonClient horizonClient = horizon.CreatePrivilegedHorizonClient();
|
return hos;
|
||||||
|
|
||||||
return horizonClient.Fs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FileSystemClient CreateClient(bool sdCardInserted)
|
public static FileSystemClient CreateClient(bool sdCardInserted)
|
||||||
{
|
{
|
||||||
return CreateClientImpl(sdCardInserted, out _);
|
HorizonServerSet hos = CreateHorizonImpl(sdCardInserted: sdCardInserted);
|
||||||
|
|
||||||
|
return hos.InitialProcessClient.Fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FileSystemClient CreateClient(out IFileSystem rootFs)
|
public static FileSystemClient CreateClient(out IFileSystem rootFs)
|
||||||
{
|
{
|
||||||
return CreateClientImpl(false, out rootFs);
|
HorizonServerSet hos = CreateHorizonImpl(sdCardInserted: true);
|
||||||
|
rootFs = hos.RootFileSystem;
|
||||||
|
|
||||||
|
return hos.InitialProcessClient.Fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Horizon CreateHorizonServer()
|
public static Horizon CreateHorizonServer()
|
||||||
{
|
{
|
||||||
return CreateHorizonImpl(true, out _);
|
return CreateHorizonImpl(sdCardInserted: true).Server;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HorizonServerSet CreateHorizon(ProgramId programId = default, bool sdCardInserted = true,
|
||||||
|
AccessControlBits.Bits fsAcBits = AccessControlBits.Bits.Debug)
|
||||||
|
{
|
||||||
|
var programLocation = new ProgramLocation(programId, StorageId.BuiltInUser);
|
||||||
|
return CreateHorizonImpl(sdCardInserted, fsAcBits, programLocation);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.Shim;
|
||||||
|
using LibHac.FsSrv.Impl;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace LibHac.Tests.Fs.FileSystemClientTests.ShimTests;
|
||||||
|
|
||||||
|
public class DeviceSaveData
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public static void MountDeviceSaveData_SaveDoesNotExist_ReturnsTargetNotFound()
|
||||||
|
{
|
||||||
|
var applicationId = new Ncm.ApplicationId(1234);
|
||||||
|
HorizonServerSet hos = FileSystemServerFactory.CreateHorizon(applicationId, fsAcBits: AccessControlBits.Bits.FullPermission);
|
||||||
|
|
||||||
|
Assert.Result(ResultFs.TargetNotFound, hos.Client.Fs.MountDeviceSaveData("device".ToU8Span(), applicationId));
|
||||||
|
Assert.Result(ResultFs.TargetNotFound, hos.Client.Fs.MountDeviceSaveData("device2".ToU8Span()));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void MountDeviceSaveData_OwnDeviceSaveExists_ReturnsSuccess()
|
||||||
|
{
|
||||||
|
var applicationId = new Ncm.ApplicationId(1234);
|
||||||
|
HorizonServerSet hos = FileSystemServerFactory.CreateHorizon(applicationId, fsAcBits: AccessControlBits.Bits.FullPermission);
|
||||||
|
|
||||||
|
Assert.Success(hos.Client.Fs.CreateDeviceSaveData(applicationId, applicationId.Value, 0, 0, SaveDataFlags.None));
|
||||||
|
Assert.Success(hos.Client.Fs.MountDeviceSaveData("device".ToU8Span()));
|
||||||
|
Assert.Success(hos.Client.Fs.MountDeviceSaveData("device2".ToU8Span(), applicationId));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void MountDeviceSaveData_OtherDeviceSaveExists_ReturnsSuccess()
|
||||||
|
{
|
||||||
|
var ownApplicationId = new Ncm.ApplicationId(1234);
|
||||||
|
var otherApplicationId = new Ncm.ApplicationId(12345);
|
||||||
|
HorizonServerSet hos = FileSystemServerFactory.CreateHorizon(ownApplicationId, fsAcBits: AccessControlBits.Bits.FullPermission);
|
||||||
|
|
||||||
|
Assert.Success(hos.Client.Fs.CreateDeviceSaveData(otherApplicationId, otherApplicationId.Value, 0, 0, SaveDataFlags.None));
|
||||||
|
Assert.Success(hos.Client.Fs.MountDeviceSaveData("device".ToU8Span(), otherApplicationId));
|
||||||
|
|
||||||
|
// Try to open missing own device save data
|
||||||
|
Assert.Result(ResultFs.TargetNotFound, hos.Client.Fs.MountDeviceSaveData("device2".ToU8Span()));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void IsDeviceSaveDataExisting_ReturnsCorrectState()
|
||||||
|
{
|
||||||
|
var applicationId = new ApplicationId(1234);
|
||||||
|
var ncmApplicationId = new Ncm.ApplicationId(applicationId.Value);
|
||||||
|
HorizonServerSet hos = FileSystemServerFactory.CreateHorizon(ncmApplicationId);
|
||||||
|
FileSystemClient fs = hos.InitialProcessClient.Fs;
|
||||||
|
|
||||||
|
// Should return false when there aren't any saves.
|
||||||
|
Assert.False(fs.IsDeviceSaveDataExisting(applicationId));
|
||||||
|
|
||||||
|
// Should return true after creating the save.
|
||||||
|
Assert.Success(fs.CreateDeviceSaveData(ncmApplicationId, applicationId.Value, 0, 0, SaveDataFlags.None));
|
||||||
|
Assert.True(fs.IsDeviceSaveDataExisting(applicationId));
|
||||||
|
|
||||||
|
// Should return false after deleting the save.
|
||||||
|
Assert.Success(fs.DeleteDeviceSaveData(applicationId));
|
||||||
|
Assert.False(fs.IsDeviceSaveDataExisting(applicationId));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue