Fixup ApplicationSaveDataManagement and update to 13.1.0

This commit is contained in:
Alex Barney 2022-01-24 15:02:32 -07:00
parent dea3b3a8b0
commit 0ba0a9ad9c
15 changed files with 958 additions and 531 deletions

View file

@ -9,7 +9,7 @@ namespace LibHac.Account;
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct Uid : IEquatable<Uid>, IComparable<Uid>, IComparable
{
public static Uid Zero => default;
public static Uid InvalidUid => default;
public readonly Id128 Id;
@ -55,4 +55,4 @@ public struct Uid : IEquatable<Uid>, IComparable<Uid>, IComparable
public static bool operator >(Uid left, Uid right) => left.CompareTo(right) > 0;
public static bool operator <=(Uid left, Uid right) => left.CompareTo(right) <= 0;
public static bool operator >=(Uid left, Uid right) => left.CompareTo(right) >= 0;
}
}

View file

@ -1,5 +1,6 @@
using System;
using System.Runtime.CompilerServices;
using InlineIL;
using static InlineIL.IL.Emit;
namespace LibHac.Common;
@ -11,7 +12,7 @@ public static class UniqueRefExtensions
{
Ldarg(nameof(value));
Ret();
throw InlineIL.IL.Unreachable();
throw IL.Unreachable();
}
}
@ -20,7 +21,17 @@ public struct UniqueRef<T> : IDisposable where T : class, IDisposable
{
private T _value;
public readonly T Get => _value;
public readonly ref T Get
{
get
{
Ldarg_0();
Ldflda(new FieldRef(typeof(UniqueRef<T>), nameof(_value)));
Ret();
throw IL.Unreachable();
}
}
public readonly bool HasValue => Get is not null;
public UniqueRef(T value)
@ -87,4 +98,4 @@ public struct UniqueRef<T> : IDisposable where T : class, IDisposable
return oldValue;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -5,8 +5,8 @@ namespace LibHac.Fs;
public static class SaveData
{
public static readonly ulong SaveIndexerId = 0x8000000000000000;
public static ProgramId InvalidProgramId => ProgramId.InvalidId;
public static ProgramId InvalidProgramId => default;
public static ProgramId AutoResolveCallerProgramId => new ProgramId(ulong.MaxValue - 1);
public static UserId InvalidUserId => UserId.InvalidId;
public static UserId InvalidUserId => default;
public static ulong InvalidSystemSaveDataId => 0;
}
}

View file

@ -17,30 +17,10 @@ public struct SaveDataAttribute : IEquatable<SaveDataAttribute>, IComparable<Sav
public ushort Index;
public Array24<byte> Reserved;
public SaveDataAttribute(ProgramId programId, SaveDataType type, UserId userId, ulong saveDataId) : this(
programId, type, userId, saveDataId, 0, SaveDataRank.Primary)
{ }
public SaveDataAttribute(ProgramId programId, SaveDataType type, UserId userId, ulong saveDataId,
ushort index) : this(programId, type, userId, saveDataId, index, SaveDataRank.Primary)
{ }
public SaveDataAttribute(ProgramId programId, SaveDataType type, UserId userId, ulong saveDataId, ushort index,
SaveDataRank rank)
{
ProgramId = programId;
Type = type;
UserId = userId;
StaticSaveDataId = saveDataId;
Index = index;
Rank = rank;
Reserved = new Array24<byte>();
}
public static Result Make(out SaveDataAttribute attribute, ProgramId programId, SaveDataType type,
UserId userId, ulong staticSaveDataId)
{
return Make(out attribute, programId, type, userId, staticSaveDataId, 0, SaveDataRank.Primary);
return Make(out attribute, programId, type, userId, staticSaveDataId, index: 0, SaveDataRank.Primary);
}
public static Result Make(out SaveDataAttribute attribute, ProgramId programId, SaveDataType type,
@ -154,36 +134,6 @@ public struct SaveDataFilter
public SaveDataAttribute Attribute;
public void SetProgramId(ProgramId value)
{
FilterByProgramId = true;
Attribute.ProgramId = value;
}
public void SetSaveDataType(SaveDataType value)
{
FilterBySaveDataType = true;
Attribute.Type = value;
}
public void SetUserId(UserId value)
{
FilterByUserId = true;
Attribute.UserId = value;
}
public void SetSaveDataId(ulong value)
{
FilterBySaveDataId = true;
Attribute.StaticSaveDataId = value;
}
public void SetIndex(ushort value)
{
FilterByIndex = true;
Attribute.Index = value;
}
public static Result Make(out SaveDataFilter filter, Optional<ulong> programId, Optional<SaveDataType> saveType,
Optional<UserId> userId, Optional<ulong> saveDataId, Optional<ushort> index)
{

View file

@ -5,7 +5,9 @@ using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv.Sf;
using LibHac.Os;
using static LibHac.Fs.Impl.AccessLogStrings;
using static LibHac.Fs.SaveData;
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
@ -58,7 +60,7 @@ public static class BcatSaveData
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Bcat,
Fs.SaveData.InvalidUserId, 0);
InvalidUserId, InvalidSystemSaveDataId);
if (rc.IsFailure()) return rc;
using var fileSystem = new SharedRef<IFileSystemSf>();

View file

@ -6,7 +6,9 @@ using LibHac.Fs.Impl;
using LibHac.FsSrv.Sf;
using LibHac.Ncm;
using LibHac.Os;
using static LibHac.Fs.Impl.AccessLogStrings;
using static LibHac.Fs.SaveData;
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
@ -48,7 +50,8 @@ public static class SaveData
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, programId, type, userId, 0, index);
rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, programId, type, userId, InvalidSystemSaveDataId,
index);
if (rc.IsFailure()) return rc;
using var fileSystem = new SharedRef<IFileSystemSf>();
@ -86,7 +89,7 @@ public static class SaveData
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, ProgramId.InvalidId, SaveDataType.Account,
UserId.InvalidId, 0);
InvalidUserId, InvalidSystemSaveDataId);
if (rc.IsFailure()) return rc;
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, SaveDataSizeForDebug,
@ -127,7 +130,7 @@ public static class SaveData
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = MountSaveData(fs.Impl, mountName, UserId.InvalidId);
rc = MountSaveData(fs.Impl, mountName, InvalidUserId);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
@ -138,7 +141,7 @@ public static class SaveData
}
else
{
rc = MountSaveData(fs.Impl, mountName, UserId.InvalidId);
rc = MountSaveData(fs.Impl, mountName, InvalidUserId);
}
fs.Impl.AbortIfNeeded(rc);
@ -238,7 +241,8 @@ public static class SaveData
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, type, UserId.InvalidId, 0);
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, type, userId,
InvalidSystemSaveDataId);
if (rc.IsFailure()) return rc.Miss();
using var fileSystem = new SharedRef<IFileSystemSf>();
@ -267,8 +271,8 @@ public static class SaveData
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.Temporary, Fs.SaveData.InvalidProgramId,
Fs.SaveData.InvalidUserId, SaveDataType.Temporary, openReadOnly: false, index: 0);
rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.Temporary, InvalidProgramId, InvalidUserId,
SaveDataType.Temporary, openReadOnly: false, index: 0);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
@ -278,8 +282,8 @@ public static class SaveData
}
else
{
rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.Temporary, Fs.SaveData.InvalidProgramId,
Fs.SaveData.InvalidUserId, SaveDataType.Temporary, openReadOnly: false, index: 0);
rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.Temporary, InvalidProgramId, InvalidUserId,
SaveDataType.Temporary, openReadOnly: false, index: 0);
}
fs.Impl.AbortIfNeeded(rc);
@ -299,8 +303,8 @@ public static class SaveData
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, Fs.SaveData.InvalidProgramId,
Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, index: 0);
rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, InvalidProgramId,
InvalidUserId, SaveDataType.Cache, openReadOnly: false, index: 0);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
@ -310,8 +314,8 @@ public static class SaveData
}
else
{
rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, Fs.SaveData.InvalidProgramId,
Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, index: 0);
rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, InvalidProgramId,
InvalidUserId, SaveDataType.Cache, openReadOnly: false, index: 0);
}
fs.Impl.AbortIfNeeded(rc);
@ -332,8 +336,8 @@ public static class SaveData
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, Fs.SaveData.InvalidProgramId,
Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, (ushort)index);
rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, InvalidProgramId,
InvalidUserId, SaveDataType.Cache, openReadOnly: false, (ushort)index);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
@ -344,8 +348,8 @@ public static class SaveData
}
else
{
rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, Fs.SaveData.InvalidProgramId,
Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, (ushort)index);
rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, InvalidProgramId,
InvalidUserId, SaveDataType.Cache, openReadOnly: false, (ushort)index);
}
fs.Impl.AbortIfNeeded(rc);
@ -366,7 +370,7 @@ public static class SaveData
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, applicationId,
Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, index: 0);
InvalidUserId, SaveDataType.Cache, openReadOnly: false, index: 0);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
@ -378,7 +382,7 @@ public static class SaveData
else
{
rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, applicationId,
Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, index: 0);
InvalidUserId, SaveDataType.Cache, openReadOnly: false, index: 0);
}
fs.Impl.AbortIfNeeded(rc);
@ -400,7 +404,7 @@ public static class SaveData
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, applicationId,
Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, (ushort)index);
InvalidUserId, SaveDataType.Cache, openReadOnly: false, (ushort)index);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
@ -413,7 +417,7 @@ public static class SaveData
else
{
rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, applicationId,
Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, (ushort)index);
InvalidUserId, SaveDataType.Cache, openReadOnly: false, (ushort)index);
}
fs.Impl.AbortIfNeeded(rc);

View file

@ -12,7 +12,9 @@ using LibHac.Ncm;
using LibHac.Os;
using LibHac.Sf;
using LibHac.Time;
using static LibHac.Fs.Impl.AccessLogStrings;
using static LibHac.Fs.SaveData;
namespace LibHac.Fs
{
@ -297,7 +299,7 @@ namespace LibHac.Fs.Shim
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Account,
userId, staticSaveDataId: 0);
userId, InvalidSystemSaveDataId);
if (rc.IsFailure()) return rc;
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags,
@ -319,7 +321,7 @@ namespace LibHac.Fs.Shim
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Bcat,
Fs.SaveData.InvalidUserId, staticSaveDataId: 0);
InvalidUserId, InvalidSystemSaveDataId);
if (rc.IsFailure()) return rc;
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size,
@ -345,7 +347,7 @@ namespace LibHac.Fs.Shim
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Device,
Fs.SaveData.InvalidUserId, staticSaveDataId: 0);
InvalidUserId, InvalidSystemSaveDataId);
if (rc.IsFailure()) return rc;
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags,
@ -367,7 +369,7 @@ namespace LibHac.Fs.Shim
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Cache,
Fs.SaveData.InvalidUserId, staticSaveDataId: 0, index);
InvalidUserId, InvalidSystemSaveDataId, index);
if (rc.IsFailure()) return rc;
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags,
@ -492,7 +494,7 @@ namespace LibHac.Fs.Shim
{
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId,
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, InvalidProgramId,
SaveDataType.System, userId, saveDataId);
if (rc.IsFailure()) return rc;
@ -529,7 +531,7 @@ namespace LibHac.Fs.Shim
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, new ProgramId(applicationId.Value),
SaveDataType.Device, Fs.SaveData.InvalidUserId, staticSaveDataId: 0);
SaveDataType.Device, InvalidUserId, InvalidSystemSaveDataId);
if (rc.IsFailure()) return rc;
return fileSystemProxy.Get.DeleteSaveDataFileSystemBySaveDataAttribute(SaveDataSpaceId.User, in attribute);
@ -587,7 +589,7 @@ namespace LibHac.Fs.Shim
}
public static Result ReadSaveDataIteratorSaveDataInfo(this FileSystemClientImpl fs, out long readCount,
Span<SaveDataInfo> buffer, in SaveDataIterator iterator)
Span<SaveDataInfo> buffer, ref SaveDataIterator iterator)
{
Result rc = iterator.ReadSaveDataInfo(out readCount, buffer);
if (rc.IsFailure()) return rc.Miss();
@ -743,7 +745,7 @@ namespace LibHac.Fs.Shim
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Account,
userId, staticSaveDataId: 0);
userId, InvalidSystemSaveDataId);
if (rc.IsFailure()) return rc;
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags,
@ -820,7 +822,7 @@ namespace LibHac.Fs.Shim
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Temporary,
Fs.SaveData.InvalidUserId, staticSaveDataId: 0);
InvalidUserId, InvalidSystemSaveDataId);
if (rc.IsFailure()) return rc;
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize: 0, ownerId, flags,
@ -946,7 +948,7 @@ namespace LibHac.Fs.Shim
{
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId,
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, InvalidProgramId,
SaveDataType.System, userId, saveDataId);
if (rc.IsFailure()) return rc;
@ -961,8 +963,7 @@ namespace LibHac.Fs.Shim
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, UserId userId,
ulong ownerId, long size, long journalSize, SaveDataFlags flags)
{
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, userId, ownerId, size, journalSize,
flags);
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, userId, ownerId, size, journalSize, flags);
}
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, UserId userId, long size,
@ -974,20 +975,19 @@ namespace LibHac.Fs.Shim
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, ulong ownerId, long size,
long journalSize, SaveDataFlags flags)
{
return CreateSystemSaveData(fs, saveDataId, Fs.SaveData.InvalidUserId, ownerId, size, journalSize, flags);
return CreateSystemSaveData(fs, saveDataId, InvalidUserId, ownerId, size, journalSize, flags);
}
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, long size,
long journalSize, SaveDataFlags flags)
{
return CreateSystemSaveData(fs, saveDataId, Fs.SaveData.InvalidUserId, ownerId: 0, size, journalSize, flags);
return CreateSystemSaveData(fs, saveDataId, InvalidUserId, ownerId: 0, size, journalSize, flags);
}
public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId,
ulong ownerId, long size, long journalSize, SaveDataFlags flags)
{
return CreateSystemSaveData(fs, spaceId, saveDataId, Fs.SaveData.InvalidUserId, ownerId, size, journalSize,
flags);
return CreateSystemSaveData(fs, spaceId, saveDataId, InvalidUserId, ownerId, size, journalSize, flags);
}
public static Result ExtendSaveData(this FileSystemClientImpl fs, SaveDataSpaceId spaceId, ulong saveDataId,
@ -1295,7 +1295,7 @@ namespace LibHac.Fs.Shim
{
UnsafeHelpers.SkipParamInit(out flags);
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId,
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, InvalidProgramId,
SaveDataType.System, userId, saveDataId);
if (rc.IsFailure()) return rc;
@ -1392,7 +1392,7 @@ namespace LibHac.Fs.Shim
SaveDataExtraData extraData = default;
extraData.Flags = flags;
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId,
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, InvalidProgramId,
SaveDataType.System, userId, saveDataId);
if (rc.IsFailure()) return rc;
@ -2186,8 +2186,8 @@ namespace LibHac.Fs.Shim
Result rc = fileSystem.GetSaveDataAttribute(out SaveDataAttribute attribute);
if (rc.IsFailure()) return rc;
if (attribute.ProgramId == Fs.SaveData.InvalidProgramId)
attribute.ProgramId = Fs.SaveData.AutoResolveCallerProgramId;
if (attribute.ProgramId == InvalidProgramId)
attribute.ProgramId = AutoResolveCallerProgramId;
SaveDataExtraData extraDataMask = default;
extraDataMask.Flags = SaveDataFlags.Restore;
@ -2239,7 +2239,7 @@ namespace LibHac.Fs.Shim
extraDataMask.JournalSize = unchecked((long)0xFFFFFFFFFFFFFFFF);
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, new ProgramId(applicationId.Value),
SaveDataType.Device, Fs.SaveData.InvalidUserId, staticSaveDataId: 0);
SaveDataType.Device, InvalidUserId, InvalidSystemSaveDataId);
if (rc.IsFailure()) return rc;
rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, SaveDataSpaceId.User,

View file

@ -178,7 +178,7 @@ namespace LibHac.Fs
public Result OpenSaveDataImporter(ref UniqueRef<ISaveDataDivisionImporter> outImporter,
in InitialDataVersion2 initialData, SaveDataSpaceId spaceId, bool useSwap)
{
return OpenSaveDataImporterImpl(ref outImporter, in initialData, UserId.InvalidId, spaceId, useSwap);
return OpenSaveDataImporterImpl(ref outImporter, in initialData, InvalidUserId, spaceId, useSwap);
}
public Result OpenSaveDataImporterByContext(ref UniqueRef<ISaveDataDivisionImporter> outImporter,
@ -354,7 +354,7 @@ namespace LibHac.Fs.Shim
Unsafe.SkipInit(out SaveDataInfo info);
rc = fs.Impl.ReadSaveDataIteratorSaveDataInfo(out long count, SpanHelpers.AsSpan(ref info),
iterator.Get);
ref iterator.Get);
fs.Impl.LogResultErrorMessage(rc);
if (rc.IsFailure()) return rc.Miss();

View file

@ -5,7 +5,9 @@ using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv.Sf;
using LibHac.Os;
using static LibHac.Fs.Impl.AccessLogStrings;
using static LibHac.Fs.SaveData;
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
@ -20,7 +22,7 @@ public static class SystemSaveData
{
public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, ulong saveDataId)
{
return fs.MountSystemSaveData(mountName, saveDataId, Fs.SaveData.InvalidUserId);
return fs.MountSystemSaveData(mountName, saveDataId, InvalidUserId);
}
public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, ulong saveDataId,
@ -32,7 +34,7 @@ public static class SystemSaveData
public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId,
ulong saveDataId)
{
return fs.MountSystemSaveData(mountName, spaceId, saveDataId, Fs.SaveData.InvalidUserId);
return fs.MountSystemSaveData(mountName, spaceId, saveDataId, InvalidUserId);
}
public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName,
@ -77,7 +79,7 @@ public static class SystemSaveData
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId,
rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, InvalidProgramId,
SaveDataType.System, userId, saveDataId);
if (rc.IsFailure()) return rc;

View file

@ -10,13 +10,15 @@ using LibHac.Kvdb;
using LibHac.Ncm;
using LibHac.Sf;
using LibHac.Util;
using static LibHac.Fs.SaveData;
using static LibHac.Fs.StringTraits;
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
using IFile = LibHac.Fs.Fsa.IFile;
using IFileSf = LibHac.FsSrv.Sf.IFile;
using Path = LibHac.Fs.Path;
using SaveData = LibHac.Fs.SaveData;
using static LibHac.Fs.StringTraits;
using Utility = LibHac.FsSystem.Utility;
namespace LibHac.FsSrv;
@ -146,7 +148,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
return ResultFs.PermissionDenied.Log();
}
}
else if (attribute.Type == SaveDataType.Account && attribute.UserId == UserId.InvalidId)
else if (attribute.Type == SaveDataType.Account && attribute.UserId == InvalidUserId)
{
bool canAccess =
accessControl.CanCall(OperationType.CreateSaveData) ||
@ -195,7 +197,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
}
else if (attribute.Type == SaveDataType.Account)
{
bool canAccess = attribute.UserId != UserId.InvalidId ||
bool canAccess = attribute.UserId != InvalidUserId ||
accessControl.CanCall(OperationType.DebugSaveData);
if (!canAccess)
@ -478,7 +480,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
private Result DeleteSaveDataFileSystemBySaveDataSpaceIdCore(SaveDataSpaceId spaceId, ulong saveDataId)
{
if (saveDataId != SaveData.SaveIndexerId)
if (saveDataId != SaveIndexerId)
{
using var accessor = new UniqueRef<SaveDataIndexerAccessor>();
Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
@ -515,7 +517,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
SaveDataSpaceId actualSpaceId;
// Only the FS process may delete the save indexer's save data.
if (saveDataId == SaveData.SaveIndexerId)
if (saveDataId == SaveIndexerId)
{
if (!_serviceImpl.FsServer.IsCurrentProcess(_processId))
return ResultFs.PermissionDenied.Log();
@ -565,7 +567,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
// Remove the save data from the indexer.
// The indexer doesn't track itself, so skip if deleting its save data.
if (saveDataId != SaveData.SaveIndexerId)
if (saveDataId != SaveIndexerId)
{
rc = accessor.Get.GetInterface().Delete(saveDataId);
if (rc.IsFailure()) return rc;
@ -656,7 +658,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
// ReSharper disable once UnusedParameter.Global
public Result UpdateSaveDataMacForDebug(SaveDataSpaceId spaceId, ulong saveDataId)
{
if (saveDataId == SaveData.SaveIndexerId)
if (saveDataId == SaveIndexerId)
return ResultFs.InvalidArgument.Log();
return ResultFs.NotImplemented.Log();
@ -697,10 +699,10 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
try
{
// Add the new save data to the save indexer
if (attribute.StaticSaveDataId == SaveData.SaveIndexerId)
if (attribute.StaticSaveDataId == SaveIndexerId)
{
// The save indexer doesn't index itself
saveDataId = SaveData.SaveIndexerId;
saveDataId = SaveIndexerId;
rc = _serviceImpl.DoesSaveDataEntityExist(out bool saveExists, creationInfo.SpaceId, saveDataId);
if (rc.IsSuccess() && saveExists)
@ -720,7 +722,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
SaveDataAttribute indexerKey = attribute;
// Add the new value to the indexer
if (attribute.StaticSaveDataId != 0 && attribute.UserId == UserId.InvalidId)
if (attribute.StaticSaveDataId != 0 && attribute.UserId == InvalidUserId)
{
// If a static save data ID is specified that ID is always used
saveDataId = attribute.StaticSaveDataId;
@ -823,7 +825,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
}
// The indexer's save data isn't tracked, so we don't need to update its state.
if (attribute.StaticSaveDataId != SaveData.SaveIndexerId)
if (attribute.StaticSaveDataId != SaveIndexerId)
{
// Mark the save data as being successfully created
rc = accessor.Get.GetInterface().SetState(saveDataId, SaveDataState.Normal);
@ -843,7 +845,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
{
DeleteSaveDataFileSystemCore(creationInfo.SpaceId, saveDataId, false).IgnoreResult();
if (accessorInitialized && saveDataId != SaveData.SaveIndexerId)
if (accessorInitialized && saveDataId != SaveIndexerId)
{
rc = accessor.Get.GetInterface().GetValue(out SaveDataIndexerValue value, saveDataId);
@ -922,7 +924,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
rc = SaveDataAccessibilityChecker.CheckCreate(in attribute, in creationInfo, programInfo, programId);
if (rc.IsFailure()) return rc;
if (tempAttribute.Type == SaveDataType.Account && tempAttribute.UserId == UserId.InvalidId)
if (tempAttribute.Type == SaveDataType.Account && tempAttribute.UserId == InvalidUserId)
{
if (tempAttribute.ProgramId == ProgramId.InvalidId)
{
@ -995,7 +997,8 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var accessor = new UniqueRef<SaveDataIndexerAccessor>();
ulong tempSaveDataId;
bool isStaticSaveDataId = attribute.StaticSaveDataId != 0 && attribute.UserId == UserId.InvalidId;
bool isStaticSaveDataId =
attribute.StaticSaveDataId != InvalidSystemSaveDataId && attribute.UserId == InvalidUserId;
// Get the ID of the save data
if (isStaticSaveDataId)
@ -1052,7 +1055,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
Result RemoveSaveIndexerEntry()
{
if (tempSaveDataId == SaveData.SaveIndexerId)
if (tempSaveDataId == SaveIndexerId)
return Result.Success;
if (isStaticSaveDataId)
@ -1379,7 +1382,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
SaveDataAttribute tempAttribute = attribute;
if (tempAttribute.ProgramId == SaveData.AutoResolveCallerProgramId)
if (tempAttribute.ProgramId == AutoResolveCallerProgramId)
{
tempAttribute.ProgramId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId);
}
@ -1428,7 +1431,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
SaveDataAttribute tempAttribute = attribute;
if (tempAttribute.ProgramId == SaveData.AutoResolveCallerProgramId)
if (tempAttribute.ProgramId == AutoResolveCallerProgramId)
{
tempAttribute.ProgramId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId);
}
@ -1505,7 +1508,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
SaveDataAttribute tempAttribute = attribute;
if (tempAttribute.ProgramId == SaveData.AutoResolveCallerProgramId)
if (tempAttribute.ProgramId == AutoResolveCallerProgramId)
{
tempAttribute.ProgramId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId);
}
@ -2042,7 +2045,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
{
Index = 0,
Type = SaveDataType.System,
UserId = UserId.InvalidId,
UserId = InvalidUserId,
StaticSaveDataId = MultiCommitManager.SaveDataId,
ProgramId = new ProgramId(MultiCommitManager.ProgramId)
};
@ -2067,7 +2070,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
{
Index = saveInfo.Index,
Type = saveInfo.Type,
UserId = UserId.InvalidId,
UserId = InvalidUserId,
StaticSaveDataId = saveInfo.StaticSaveDataId,
ProgramId = saveInfo.ProgramId
};

View file

@ -12,7 +12,8 @@ using LibHac.Kvdb;
using LibHac.Os;
using LibHac.Sf;
using LibHac.Util;
using SaveData = LibHac.Fs.SaveData;
using static LibHac.Fs.SaveData;
namespace LibHac.FsSrv;
@ -451,8 +452,8 @@ public class SaveDataIndexer : ISaveDataIndexer
if (rc.IsFailure()) return rc;
Assert.SdkRequires(_isLoaded);
Assert.SdkRequires(key.StaticSaveDataId != SaveData.InvalidSystemSaveDataId);
Assert.SdkRequires(key.UserId == SaveData.InvalidUserId);
Assert.SdkRequires(key.StaticSaveDataId != InvalidSystemSaveDataId);
Assert.SdkRequires(key.UserId == InvalidUserId);
// Iterate through all existing values to check if the save ID is already in use.
FlatMapKeyValueStore<SaveDataAttribute>.Iterator iterator = _kvDatabase.GetBeginIterator();

View file

@ -5,7 +5,7 @@ using LibHac.Fs.Shim;
using LibHac.Ns;
using Xunit;
using static LibHac.Fs.ApplicationSaveDataManagement;
using static LibHac.Fs.SaveData;
namespace LibHac.Tests.Fs.FileSystemClientTests;
@ -19,13 +19,13 @@ public class ApplicationSaveDataManagementTests
var applicationId = new Ncm.ApplicationId(11);
var userId = new Uid(2, 3);
var nacp = new ApplicationControlProperty
var controlProperty = new ApplicationControlProperty
{
UserAccountSaveDataSize = 0x1000,
UserAccountSaveDataJournalSize = 0x1000
};
Assert.Success(EnsureApplicationSaveData(fs, out _, applicationId, ref nacp, ref userId));
Assert.Success(fs.EnsureApplicationSaveData(out _, applicationId, in controlProperty, in userId));
using var iterator = new UniqueRef<SaveDataIterator>();
fs.OpenSaveDataIterator(ref iterator.Ref(), SaveDataSpaceId.User);
@ -47,13 +47,13 @@ public class ApplicationSaveDataManagementTests
var applicationId = new Ncm.ApplicationId(11);
var userId = new Uid(2, 3);
var nacp = new ApplicationControlProperty
var controlProperty = new ApplicationControlProperty
{
DeviceSaveDataSize = 0x1000,
DeviceSaveDataJournalSize = 0x1000
};
Assert.Success(EnsureApplicationSaveData(fs, out _, applicationId, ref nacp, ref userId));
Assert.Success(fs.EnsureApplicationSaveData(out _, applicationId, in controlProperty, in userId));
using var iterator = new UniqueRef<SaveDataIterator>();
fs.OpenSaveDataIterator(ref iterator.Ref(), SaveDataSpaceId.User);
@ -63,7 +63,7 @@ public class ApplicationSaveDataManagementTests
Assert.Equal(1, entriesRead);
Assert.Equal(applicationId, info[0].ProgramId);
Assert.Equal(UserId.InvalidId, info[0].UserId);
Assert.Equal(InvalidUserId, info[0].UserId);
Assert.Equal(SaveDataType.Device, info[0].Type);
}
@ -75,12 +75,12 @@ public class ApplicationSaveDataManagementTests
var applicationId = new Ncm.ApplicationId(11);
var userId = new Uid(2, 3);
var nacp = new ApplicationControlProperty
var controlProperty = new ApplicationControlProperty
{
BcatDeliveryCacheStorageSize = 0x1000
};
Assert.Success(EnsureApplicationSaveData(fs, out _, applicationId, ref nacp, ref userId));
Assert.Success(fs.EnsureApplicationSaveData(out _, applicationId, in controlProperty, in userId));
using var iterator = new UniqueRef<SaveDataIterator>();
fs.OpenSaveDataIterator(ref iterator.Ref(), SaveDataSpaceId.User);
@ -90,7 +90,7 @@ public class ApplicationSaveDataManagementTests
Assert.Equal(1, entriesRead);
Assert.Equal(applicationId, info[0].ProgramId);
Assert.Equal(UserId.InvalidId, info[0].UserId);
Assert.Equal(InvalidUserId, info[0].UserId);
Assert.Equal(SaveDataType.Bcat, info[0].Type);
}
@ -102,12 +102,12 @@ public class ApplicationSaveDataManagementTests
var applicationId = new Ncm.ApplicationId(11);
var userId = new Uid(2, 3);
var nacp = new ApplicationControlProperty
var controlProperty = new ApplicationControlProperty
{
TemporaryStorageSize = 0x1000
};
Assert.Success(EnsureApplicationSaveData(fs, out _, applicationId, ref nacp, ref userId));
Assert.Success(fs.EnsureApplicationSaveData(out _, applicationId, in controlProperty, in userId));
using var iterator = new UniqueRef<SaveDataIterator>();
fs.OpenSaveDataIterator(ref iterator.Ref(), SaveDataSpaceId.Temporary);
@ -117,9 +117,53 @@ public class ApplicationSaveDataManagementTests
Assert.Equal(1, entriesRead);
Assert.Equal(applicationId, info[0].ProgramId);
Assert.Equal(UserId.InvalidId, info[0].UserId);
Assert.Equal(InvalidUserId, info[0].UserId);
Assert.Equal(SaveDataType.Temporary, info[0].Type);
}
[Fact]
public static void EnsureApplicationSaveData_NeedsExtension_IsExtended()
{
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
var applicationId = new Ncm.ApplicationId(11);
var userId = new Uid(2, 3);
var controlProperty = new ApplicationControlProperty
{
UserAccountSaveDataSize = 0x1000,
UserAccountSaveDataJournalSize = 0x1000
};
Assert.Success(fs.EnsureApplicationSaveData(out _, applicationId, in controlProperty, in userId));
const int newAvailableSize = 1024 * 1024 * 2;
const int newJournalSize = 1024 * 1024;
controlProperty.UserAccountSaveDataSize = newAvailableSize;
controlProperty.UserAccountSaveDataJournalSize = newJournalSize;
Assert.Success(fs.EnsureApplicationSaveData(out _, applicationId, in controlProperty, in userId));
using var iterator = new UniqueRef<SaveDataIterator>();
fs.OpenSaveDataIterator(ref iterator.Ref(), SaveDataSpaceId.User);
var info = new SaveDataInfo[2];
Assert.Success(iterator.Get.ReadSaveDataInfo(out long entriesRead, info));
Assert.Equal(1, entriesRead);
Assert.Equal(applicationId, info[0].ProgramId);
Assert.Equal(Utility.ConvertAccountUidToFsUserId(userId), info[0].UserId);
Assert.Equal(SaveDataType.Account, info[0].Type);
// ReSharper disable UnusedVariable
Assert.Success(fs.GetSaveDataAvailableSize(out long availableSize, SaveDataSpaceId.User, info[0].SaveDataId));
Assert.Success(fs.GetSaveDataJournalSize(out long journalSize, SaveDataSpaceId.User, info[0].SaveDataId));
// ReSharper restore UnusedVariable
// Todo: Remove once save data extension is implemented
// Assert.Equal(newAvailableSize, availableSize);
// Assert.Equal(newJournalSize, journalSize);
}
[Fact]
public static void EnsureApplicationCacheStorage_SdCardAvailable_CreatesCacheStorageOnSd()
@ -128,14 +172,14 @@ public class ApplicationSaveDataManagementTests
var applicationId = new Ncm.ApplicationId(11);
var nacp = new ApplicationControlProperty
var controlProperty = new ApplicationControlProperty
{
CacheStorageSize = 0x1000,
CacheStorageJournalSize = 0x1000
};
Assert.Success(fs.EnsureApplicationCacheStorage(out _, out CacheStorageTargetMedia target, applicationId,
ref nacp));
in controlProperty));
Assert.Equal(CacheStorageTargetMedia.SdCard, target);
@ -157,14 +201,14 @@ public class ApplicationSaveDataManagementTests
var applicationId = new Ncm.ApplicationId(11);
var nacp = new ApplicationControlProperty
var controlProperty = new ApplicationControlProperty
{
CacheStorageSize = 0x1000,
CacheStorageJournalSize = 0x1000
};
Assert.Success(fs.EnsureApplicationCacheStorage(out _, out CacheStorageTargetMedia target, applicationId,
ref nacp));
in controlProperty));
Assert.Equal(CacheStorageTargetMedia.Nand, target);
@ -188,13 +232,14 @@ public class ApplicationSaveDataManagementTests
var applicationId = new Ncm.ApplicationId(11);
var nacp = new ApplicationControlProperty
var controlProperty = new ApplicationControlProperty
{
CacheStorageSize = 0x1000,
CacheStorageJournalSize = 0x1000
};
fs.EnsureApplicationCacheStorage(out _, out CacheStorageTargetMedia targetFromCreation, applicationId, ref nacp);
fs.EnsureApplicationCacheStorage(out _, out CacheStorageTargetMedia targetFromCreation, applicationId,
in controlProperty);
Assert.Success(fs.GetCacheStorageTargetMedia(out CacheStorageTargetMedia target, applicationId));
Assert.Equal(targetFromCreation, target);

View file

@ -8,6 +8,8 @@ using LibHac.Ncm;
using LibHac.Time;
using Xunit;
using static LibHac.Fs.SaveData;
namespace LibHac.Tests.Fs.FileSystemClientTests.ShimTests;
public class SaveDataManagement
@ -322,7 +324,7 @@ public class SaveDataManagement
Assert.Success(client.Fs.RegisterProgramIndexMapInfo(mapInfo));
Assert.Success(subProgramClient.Fs.CreateSaveData(Ncm.ApplicationId.InvalidId, UserId.InvalidId, 0, 0x4000,
Assert.Success(subProgramClient.Fs.CreateSaveData(Ncm.ApplicationId.InvalidId, InvalidUserId, 0, 0x4000,
0x4000, SaveDataFlags.None));
// Get the created save data's ID
@ -497,7 +499,8 @@ public class SaveDataManagement
Assert.Success(fs.CreateSaveData(applicationId, user2Id, 0, 0x4000, 0x4000, SaveDataFlags.None));
}
Assert.Success(SaveDataFilter.Make(out SaveDataFilter filter, default, default, user2Id, default, default));
Assert.Success(SaveDataFilter.Make(out SaveDataFilter filter, programId: default, saveType: default, user2Id,
saveDataId: default, index: default));
using var iterator = new UniqueRef<SaveDataIterator>();
Assert.Success(fs.OpenSaveDataIterator(ref iterator.Ref(), SaveDataSpaceId.User, in filter));
@ -651,7 +654,7 @@ public class SaveDataManagement
for (int i = 1; i <= count; i++)
{
var applicationId = new Ncm.ApplicationId((uint)i);
Result rc = fs.CreateSaveData(applicationId, UserId.InvalidId, 0, 0x4000, 0x4000, SaveDataFlags.None);
Result rc = fs.CreateSaveData(applicationId, InvalidUserId, 0, 0x4000, 0x4000, SaveDataFlags.None);
if (rc.IsFailure()) return rc;
}
}
@ -662,7 +665,7 @@ public class SaveDataManagement
for (int i = 1; i <= count; i++)
{
var applicationId = new Ncm.ApplicationId((uint)rng.Next());
Result rc = fs.CreateSaveData(applicationId, UserId.InvalidId, 0, 0x4000, 0x4000, SaveDataFlags.None);
Result rc = fs.CreateSaveData(applicationId, InvalidUserId, 0, 0x4000, 0x4000, SaveDataFlags.None);
if (rc.IsFailure()) return rc;
}
}

View file

@ -6,6 +6,8 @@ using LibHac.Fs.Shim;
using LibHac.Tests.Fs.FileSystemClientTests;
using Xunit;
using static LibHac.Fs.SaveData;
namespace LibHac.Tests.Fs.FsaTests;
public class MultiCommitTests
@ -25,8 +27,8 @@ public class MultiCommitTests
foreach ((int id, string name) info in saveInfo)
{
var applicationId = new Ncm.ApplicationId((uint)info.id);
fs.CreateSaveData(applicationId, UserId.InvalidId, 0, 0x4000, 0x4000, SaveDataFlags.None);
fs.MountSaveData(info.name.ToU8Span(), applicationId, UserId.InvalidId);
fs.CreateSaveData(applicationId, InvalidUserId, 0, 0x4000, 0x4000, SaveDataFlags.None);
fs.MountSaveData(info.name.ToU8Span(), applicationId, InvalidUserId);
}
foreach ((int id, string name) info in saveInfo)
@ -51,7 +53,7 @@ public class MultiCommitTests
foreach ((int id, string name) info in saveInfo)
{
var applicationId = new Ncm.ApplicationId((uint)info.id);
fs.MountSaveData(info.name.ToU8Span(), applicationId, UserId.InvalidId);
fs.MountSaveData(info.name.ToU8Span(), applicationId, InvalidUserId);
}
foreach ((int id, string name) info in saveInfo)
@ -59,4 +61,4 @@ public class MultiCommitTests
Assert.Success(fs.GetEntryType(out _, $"{info.name}:/file{info.id}".ToU8Span()));
}
}
}
}