From 5c6f78e75147a7bbe12828ad79af5a2add6ebda8 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Wed, 12 May 2021 01:38:30 -0700 Subject: [PATCH] Implement all defined methods in SaveDataManagement --- src/LibHac/ApplicationId.cs | 3 + src/LibHac/Fs/AccessLog.cs | 24 + src/LibHac/Fs/SaveDataStructs.cs | 2 +- src/LibHac/Fs/Shim/SaveDataManagement.cs | 941 ++++++++++++++++-- .../FsSystem/DirectorySaveDataFileSystem.cs | 2 +- 5 files changed, 914 insertions(+), 58 deletions(-) diff --git a/src/LibHac/ApplicationId.cs b/src/LibHac/ApplicationId.cs index c00d623a..4092c364 100644 --- a/src/LibHac/ApplicationId.cs +++ b/src/LibHac/ApplicationId.cs @@ -13,6 +13,9 @@ namespace LibHac Value = value; } + public static bool operator ==(ApplicationId left, ApplicationId right) => left.Value == right.Value; + public static bool operator !=(ApplicationId left, ApplicationId right) => left.Value != right.Value; + public override bool Equals(object obj) => obj is ApplicationId id && Equals(id); public bool Equals(ApplicationId other) => Value == other.Value; public override int GetHashCode() => HashCode.Combine(Value); diff --git a/src/LibHac/Fs/AccessLog.cs b/src/LibHac/Fs/AccessLog.cs index 42670fdb..41c3d37d 100644 --- a/src/LibHac/Fs/AccessLog.cs +++ b/src/LibHac/Fs/AccessLog.cs @@ -952,6 +952,30 @@ namespace LibHac.Fs.Impl (byte)'d', (byte)':', (byte)' ' }; + public static ReadOnlySpan LogSaveDataTimeStamp => // ", save_data_time_stamp: " + new[] + { + (byte)',', (byte)' ', (byte)'s', (byte)'a', (byte)'v', (byte)'e', (byte)'_', (byte)'d', + (byte)'a', (byte)'t', (byte)'a', (byte)'_', (byte)'t', (byte)'i', (byte)'m', (byte)'e', + (byte)'_', (byte)'s', (byte)'t', (byte)'a', (byte)'m', (byte)'p', (byte)':', (byte)' ' + }; + + public static ReadOnlySpan LogSaveDataCommitId => // ", save_data_commit_id: 0x" + new[] + { + (byte)',', (byte)' ', (byte)'s', (byte)'a', (byte)'v', (byte)'e', (byte)'_', (byte)'d', + (byte)'a', (byte)'t', (byte)'a', (byte)'_', (byte)'c', (byte)'o', (byte)'m', (byte)'m', + (byte)'i', (byte)'t', (byte)'_', (byte)'i', (byte)'d', (byte)':', (byte)' ', (byte)'0', + (byte)'x' + }; + + public static ReadOnlySpan LogRestoreFlag => // ", restore_flag: " + new[] + { + (byte)',', (byte)' ', (byte)'r', (byte)'e', (byte)'s', (byte)'t', (byte)'o', (byte)'r', + (byte)'e', (byte)'_', (byte)'f', (byte)'l', (byte)'a', (byte)'g', (byte)':', (byte)' ' + }; + public static ReadOnlySpan LogSdkVersion => // "sdk_version: " new[] { diff --git a/src/LibHac/Fs/SaveDataStructs.cs b/src/LibHac/Fs/SaveDataStructs.cs index 6c754a23..3ef58da8 100644 --- a/src/LibHac/Fs/SaveDataStructs.cs +++ b/src/LibHac/Fs/SaveDataStructs.cs @@ -283,7 +283,7 @@ namespace LibHac.Fs { [FieldOffset(0x00)] public SaveDataAttribute Attribute; [FieldOffset(0x40)] public ulong OwnerId; - [FieldOffset(0x48)] public ulong TimeStamp; + [FieldOffset(0x48)] public long TimeStamp; [FieldOffset(0x50)] public SaveDataFlags Flags; [FieldOffset(0x58)] public long DataSize; [FieldOffset(0x60)] public long JournalSize; diff --git a/src/LibHac/Fs/Shim/SaveDataManagement.cs b/src/LibHac/Fs/Shim/SaveDataManagement.cs index eefee929..dd67bffc 100644 --- a/src/LibHac/Fs/Shim/SaveDataManagement.cs +++ b/src/LibHac/Fs/Shim/SaveDataManagement.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibHac.Common; using LibHac.Diag; +using LibHac.Fs.Fsa; using LibHac.Fs.Impl; using LibHac.FsSrv.Impl; using LibHac.FsSrv.Sf; @@ -44,7 +45,7 @@ namespace LibHac.Fs.Shim Tick end = fs.Hos.Os.GetSystemTick(); var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogSize).AppendFormat(buffer.Length); + sb.Append(LogSize).AppendFormat(buffer.Length, 'd'); fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); } @@ -331,18 +332,88 @@ namespace LibHac.Fs.Shim public static Result DeleteSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, UserId userId) { - throw new NotImplementedException(); + Result rc; + Span logBuffer = stackalloc byte[0x80]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = Delete(fs, spaceId, saveDataId, userId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X') + .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = Delete(fs, spaceId, saveDataId, userId); + } + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result Delete(FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, UserId userId) + { + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId, + SaveDataType.System, userId, saveDataId); + if (rc.IsFailure()) return rc; + + return fsProxy.Target.DeleteSaveDataFileSystemBySaveDataAttribute(spaceId, in attribute); + } } public static Result DeleteDeviceSaveData(this FileSystemClient fs, ApplicationId applicationId) { - throw new NotImplementedException(); + Result rc; + Span logBuffer = stackalloc byte[0x30]; + + if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = Delete(fs, applicationId); + 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 = Delete(fs, applicationId); + } + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result Delete(FileSystemClient fs, ApplicationId applicationId) + { + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, new ProgramId(applicationId.Value), + SaveDataType.Device, Fs.SaveData.InvalidUserId, 0); + if (rc.IsFailure()) return rc; + + return fsProxy.Target.DeleteSaveDataFileSystemBySaveDataAttribute(SaveDataSpaceId.User, in attribute); + } } public static Result RegisterSaveDataAtomicDeletion(this FileSystemClient fs, ReadOnlySpan saveDataIdList) { - throw new NotImplementedException(); + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + var listBytes = new InBuffer(MemoryMarshal.Cast(saveDataIdList)); + + Result rc = fsProxy.Target.RegisterSaveDataFileSystemAtomicDeletion(listBytes); + fs.Impl.AbortIfNeeded(rc); + return rc; } public static Result OpenSaveDataIterator(this FileSystemClientImpl fs, out SaveDataIterator iterator, @@ -391,10 +462,10 @@ namespace LibHac.Fs.Shim return Result.Success; } - public static Result ReadSaveDataInfoImpl(out long readCount, Span buffer, + public static Result ReadSaveDataIteratorSaveDataInfo(out long readCount, Span buffer, in SaveDataIterator iterator) { - throw new NotImplementedException(); + return iterator.ReadSaveDataInfo(out readCount, buffer); } public static Result OpenSaveDataIterator(this FileSystemClient fs, out SaveDataIterator iterator, @@ -494,8 +565,8 @@ namespace LibHac.Fs.Shim sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16) .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') - .Append(LogSaveDataSize).AppendFormat(size) - .Append(LogSaveDataJournalSize).AppendFormat(journalSize) + .Append(LogSaveDataSize).AppendFormat(size, 'd') + .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd') .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); @@ -525,8 +596,8 @@ namespace LibHac.Fs.Shim sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16) .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') - .Append(LogSaveDataSize).AppendFormat(size) - .Append(LogSaveDataJournalSize).AppendFormat(journalSize) + .Append(LogSaveDataSize).AppendFormat(size, 'd') + .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd') .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); @@ -572,7 +643,7 @@ namespace LibHac.Fs.Shim var sb = new U8StringBuilder(logBuffer, true); sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') - .Append(LogSaveDataSize).AppendFormat(size); + .Append(LogSaveDataSize).AppendFormat(size, 'd'); fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); } @@ -600,8 +671,8 @@ namespace LibHac.Fs.Shim var sb = new U8StringBuilder(logBuffer, true); sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') - .Append(LogSaveDataSize).AppendFormat(size) - .Append(LogSaveDataJournalSize).AppendFormat(journalSize) + .Append(LogSaveDataSize).AppendFormat(size, 'd') + .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd') .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); @@ -652,7 +723,7 @@ namespace LibHac.Fs.Shim var sb = new U8StringBuilder(logBuffer, true); sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') - .Append(LogSaveDataSize).AppendFormat(size) + .Append(LogSaveDataSize).AppendFormat(size, 'd') .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); @@ -683,8 +754,8 @@ namespace LibHac.Fs.Shim sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') .Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') - .Append(LogSaveDataSize).AppendFormat(size) - .Append(LogSaveDataJournalSize).AppendFormat(journalSize) + .Append(LogSaveDataSize).AppendFormat(size, 'd') + .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd') .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); @@ -728,8 +799,8 @@ namespace LibHac.Fs.Shim .Append(LogSaveDataId).AppendFormat(saveDataId, 'X') .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16) .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') - .Append(LogSaveDataSize).AppendFormat(size) - .Append(LogSaveDataJournalSize).AppendFormat(journalSize) + .Append(LogSaveDataSize).AppendFormat(size, 'd') + .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd') .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); @@ -794,19 +865,71 @@ namespace LibHac.Fs.Shim public static Result ExtendSaveData(this FileSystemClientImpl fs, SaveDataSpaceId spaceId, ulong saveDataId, long saveDataSize, long journalSize) { - throw new NotImplementedException(); + using ReferenceCountedDisposable fsProxy = fs.GetFileSystemProxyServiceObject(); + + return fsProxy.Target.ExtendSaveDataFileSystem(spaceId, saveDataId, saveDataSize, journalSize); } public static Result ExtendSaveData(this FileSystemClient fs, ulong saveDataId, long saveDataSize, long journalSize) { - throw new NotImplementedException(); + Result rc; + Span logBuffer = stackalloc byte[0x90]; + + var spaceId = SaveDataSpaceId.System; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.ExtendSaveData(spaceId, saveDataId, saveDataSize, journalSize); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X') + .Append(LogSaveDataSize).AppendFormat(saveDataSize, 'd') + .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.ExtendSaveData(spaceId, saveDataId, saveDataSize, journalSize); + } + fs.Impl.AbortIfNeeded(rc); + return rc; } public static Result ExtendSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, long saveDataSize, long journalSize) { - throw new NotImplementedException(); + Result rc; + Span logBuffer = stackalloc byte[0x90]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.ExtendSaveData(spaceId, saveDataId, saveDataSize, journalSize); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X') + .Append(LogSaveDataSize).AppendFormat(saveDataSize, 'd') + .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.ExtendSaveData(spaceId, saveDataId, saveDataSize, journalSize); + } + fs.Impl.AbortIfNeeded(rc); + return rc; } public static Result QuerySaveDataTotalSize(this FileSystemClientImpl fs, out long totalSize, long size, @@ -836,8 +959,8 @@ namespace LibHac.Fs.Shim Tick end = fs.Hos.Os.GetSystemTick(); var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogSaveDataSize).AppendFormat(size) - .Append(LogSaveDataJournalSize).AppendFormat(journalSize); + sb.Append(LogSaveDataSize).AppendFormat(size, 'd') + .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd'); fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); } @@ -891,7 +1014,7 @@ namespace LibHac.Fs.Shim ulong saveDataId) { Result rc; - Span logBuffer = stackalloc byte[0x40]; + Span logBuffer = stackalloc byte[0x50]; if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) { @@ -929,120 +1052,600 @@ namespace LibHac.Fs.Shim public static Result GetSaveDataFlags(this FileSystemClient fs, out SaveDataFlags flags, ulong saveDataId) { - throw new NotImplementedException(); + Result rc; + Span logBuffer = stackalloc byte[0x40]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = GetFlags(fs, out flags, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = GetFlags(fs, out flags, saveDataId); + } + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result GetFlags(FileSystemClient fs, out SaveDataFlags flags, ulong saveDataId) + { + UnsafeHelpers.SkipParamInit(out flags); + + Result rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, saveDataId); + if (rc.IsFailure()) return rc; + + flags = extraData.Flags; + return Result.Success; + } } public static Result GetSaveDataFlags(this FileSystemClient fs, out SaveDataFlags flags, SaveDataSpaceId spaceId, ulong saveDataId) { - throw new NotImplementedException(); + Result rc; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = GetFlags(fs, out flags, spaceId, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = GetFlags(fs, out flags, spaceId, saveDataId); + } + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result GetFlags(FileSystemClient fs, out SaveDataFlags flags, SaveDataSpaceId spaceId, + ulong saveDataId) + { + UnsafeHelpers.SkipParamInit(out flags); + + Result rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId, + saveDataId); + if (rc.IsFailure()) return rc; + + flags = extraData.Flags; + return Result.Success; + } } public static Result GetSystemSaveDataFlags(this FileSystemClient fs, out SaveDataFlags flags, SaveDataSpaceId spaceId, ulong saveDataId, UserId userId) { - throw new NotImplementedException(); + Result rc; + Span logBuffer = stackalloc byte[0x80]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = GetFlags(fs, out flags, spaceId, saveDataId, userId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X') + .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = GetFlags(fs, out flags, spaceId, saveDataId, userId); + } + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result GetFlags(FileSystemClient fs, out SaveDataFlags flags, SaveDataSpaceId spaceId, + ulong saveDataId, UserId userId) + { + UnsafeHelpers.SkipParamInit(out flags); + + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId, + SaveDataType.System, userId, saveDataId); + if (rc.IsFailure()) return rc; + + rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId, in attribute); + if (rc.IsFailure()) return rc; + + flags = extraData.Flags; + return Result.Success; + } } public static Result SetSaveDataFlags(this FileSystemClient fs, ulong saveDataId, SaveDataFlags flags) { - throw new NotImplementedException(); + return SetSaveDataFlags(fs, saveDataId, SaveDataSpaceId.System, flags); } public static Result SetSaveDataFlags(this FileSystemClient fs, ulong saveDataId, SaveDataSpaceId spaceId, SaveDataFlags flags) { - throw new NotImplementedException(); + Result rc; + Span logBuffer = stackalloc byte[0x70]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = SetFlags(fs, saveDataId, spaceId, flags); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X') + .Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = SetFlags(fs, saveDataId, spaceId, flags); + } + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result SetFlags(FileSystemClient fs, ulong saveDataId, SaveDataSpaceId spaceId, SaveDataFlags flags) + { + Result rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId, + saveDataId); + if (rc.IsFailure()) return rc; + + extraData.Flags = flags; + + return fs.Impl.WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraData); + } } public static Result SetSystemSaveDataFlags(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, UserId userId, SaveDataFlags flags) { - throw new NotImplementedException(); + Result rc; + Span logBuffer = stackalloc byte[0xA0]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = SetFlags(fs, spaceId, saveDataId, userId, flags); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X') + .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16) + .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = SetFlags(fs, spaceId, saveDataId, userId, flags); + } + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result SetFlags(FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, UserId userId, + SaveDataFlags flags) + { + var extraDataMask = new SaveDataExtraData(); + extraDataMask.Flags = unchecked((SaveDataFlags)0xFFFFFFFF); + + var extraData = new SaveDataExtraData(); + extraData.Flags = flags; + + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId, + SaveDataType.System, userId, saveDataId); + if (rc.IsFailure()) return rc; + + return fs.Impl.WriteSaveDataFileSystemExtraData(spaceId, in attribute, in extraData, in extraDataMask); + } + } + + public static Result GetSaveDataTimeStamp(this FileSystemClient fs, out PosixTime timeStamp, ulong saveDataId) + { + Result rc; + Span logBuffer = stackalloc byte[0x30]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = GetTimeStamp(fs, out timeStamp, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = GetTimeStamp(fs, out timeStamp, saveDataId); + } + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result GetTimeStamp(FileSystemClient fs, out PosixTime timeStamp, ulong saveDataId) + { + UnsafeHelpers.SkipParamInit(out timeStamp); + + Result rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, saveDataId); + if (rc.IsFailure()) return rc; + + timeStamp = new PosixTime(extraData.TimeStamp); + return Result.Success; + } } public static Result SetSaveDataTimeStamp(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, PosixTime timeStamp) { - throw new NotImplementedException(); + Result rc; + Span logBuffer = stackalloc byte[0x80]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = SetTimeStamp(fs, spaceId, saveDataId, timeStamp); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X') + .Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataTimeStamp).AppendFormat(timeStamp.Value, 'd'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = SetTimeStamp(fs, spaceId, saveDataId, timeStamp); + } + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result SetTimeStamp(FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, + PosixTime timeStamp) + { + var extraDataMask = new SaveDataExtraData(); + extraDataMask.TimeStamp = unchecked((long)0xFFFFFFFFFFFFFFFF); + + var extraData = new SaveDataExtraData(); + extraData.TimeStamp = timeStamp.Value; + + return fs.Impl.WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraData, in extraDataMask); + } } public static Result GetSaveDataTimeStamp(this FileSystemClient fs, out PosixTime timeStamp, SaveDataSpaceId spaceId, ulong saveDataId) { - throw new NotImplementedException(); + Result rc; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = GetTimeStamp(fs, out timeStamp, spaceId, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = GetTimeStamp(fs, out timeStamp, spaceId, saveDataId); + } + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result GetTimeStamp(FileSystemClient fs, out PosixTime timeStamp, SaveDataSpaceId spaceId, + ulong saveDataId) + { + UnsafeHelpers.SkipParamInit(out timeStamp); + + Result rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId, + saveDataId); + if (rc.IsFailure()) return rc; + + timeStamp = new PosixTime(extraData.TimeStamp); + return Result.Success; + } } public static Result GetSaveDataAvailableSize(this FileSystemClientImpl fs, out long availableSize, ulong saveDataId) { - throw new NotImplementedException(); + UnsafeHelpers.SkipParamInit(out availableSize); + + Result rc = fs.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, saveDataId); + if (rc.IsFailure()) return rc; + + availableSize = extraData.DataSize; + return Result.Success; } public static Result GetSaveDataAvailableSize(this FileSystemClientImpl fs, out long availableSize, SaveDataSpaceId spaceId, ulong saveDataId) { - throw new NotImplementedException(); + UnsafeHelpers.SkipParamInit(out availableSize); + + Result rc = fs.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId, saveDataId); + if (rc.IsFailure()) return rc; + + availableSize = extraData.DataSize; + return Result.Success; } public static Result GetSaveDataAvailableSize(this FileSystemClient fs, out long availableSize, ulong saveDataId) { - throw new NotImplementedException(); + Result rc; + Span logBuffer = stackalloc byte[0x40]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.GetSaveDataAvailableSize(out availableSize, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.GetSaveDataAvailableSize(out availableSize, saveDataId); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; } public static Result GetSaveDataAvailableSize(this FileSystemClient fs, out long availableSize, SaveDataSpaceId spaceId, ulong saveDataId) { - throw new NotImplementedException(); + Result rc; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.GetSaveDataAvailableSize(out availableSize, spaceId, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.GetSaveDataAvailableSize(out availableSize, spaceId, saveDataId); + } + fs.Impl.AbortIfNeeded(rc); + return rc; } public static Result GetSaveDataJournalSize(this FileSystemClientImpl fs, out long journalSize, ulong saveDataId) { - throw new NotImplementedException(); + UnsafeHelpers.SkipParamInit(out journalSize); + + Result rc = fs.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, saveDataId); + if (rc.IsFailure()) return rc; + + journalSize = extraData.JournalSize; + return Result.Success; } public static Result GetSaveDataJournalSize(this FileSystemClientImpl fs, out long journalSize, SaveDataSpaceId spaceId, ulong saveDataId) { - throw new NotImplementedException(); + UnsafeHelpers.SkipParamInit(out journalSize); + + Result rc = fs.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId, saveDataId); + if (rc.IsFailure()) return rc; + + journalSize = extraData.JournalSize; + return Result.Success; } - public static Result GetSaveDataJournalSize(this FileSystemClient fs, out long journalSize, - ulong saveDataId) + public static Result GetSaveDataJournalSize(this FileSystemClient fs, out long journalSize, ulong saveDataId) { - throw new NotImplementedException(); + Result rc; + Span logBuffer = stackalloc byte[0x40]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.GetSaveDataJournalSize(out journalSize, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.GetSaveDataJournalSize(out journalSize, saveDataId); + } + fs.Impl.AbortIfNeeded(rc); + return rc; } public static Result GetSaveDataJournalSize(this FileSystemClient fs, out long journalSize, SaveDataSpaceId spaceId, ulong saveDataId) { - throw new NotImplementedException(); + Result rc; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.GetSaveDataJournalSize(out journalSize, spaceId, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.GetSaveDataJournalSize(out journalSize, spaceId, saveDataId); + } + fs.Impl.AbortIfNeeded(rc); + return rc; } public static Result GetSaveDataCommitId(this FileSystemClient fs, out long commitId, SaveDataSpaceId spaceId, ulong saveDataId) { - throw new NotImplementedException(); + Result rc; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = GetCommitId(fs, out commitId, spaceId, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = GetCommitId(fs, out commitId, spaceId, saveDataId); + } + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result GetCommitId(FileSystemClient fs, out long commitId, SaveDataSpaceId spaceId, ulong saveDataId) + { + UnsafeHelpers.SkipParamInit(out commitId); + + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + return fsProxy.Target.GetSaveDataCommitId(out commitId, spaceId, saveDataId); + } } public static Result SetSaveDataCommitId(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, long commitId) { - throw new NotImplementedException(); + Result rc; + Span logBuffer = stackalloc byte[0x80]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = SetCommitId(fs, spaceId, saveDataId, commitId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X') + .Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataCommitId).AppendFormat(commitId, 'X', 16); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = SetCommitId(fs, spaceId, saveDataId, commitId); + } + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result SetCommitId(FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, long commitId) + { + var extraDataMask = new SaveDataExtraData(); + extraDataMask.CommitId = unchecked((long)0xFFFFFFFFFFFFFFFF); + + var extraData = new SaveDataExtraData(); + extraData.CommitId = commitId; + + return fs.Impl.WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraData, in extraDataMask); + } } public static Result QuerySaveDataInternalStorageTotalSize(this FileSystemClientImpl fs, out long size, SaveDataSpaceId spaceId, ulong saveDataId) { - throw new NotImplementedException(); + UnsafeHelpers.SkipParamInit(out size); + + using ReferenceCountedDisposable fsProxy = fs.GetFileSystemProxyServiceObject(); + + return fsProxy.Target.QuerySaveDataInternalStorageTotalSize(out size, spaceId, saveDataId); } public static Result QuerySaveDataInternalStorageTotalSize(this FileSystemClient fs, out long size, SaveDataSpaceId spaceId, ulong saveDataId) { - throw new NotImplementedException(); + Result rc; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.QuerySaveDataInternalStorageTotalSize(out size, spaceId, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.QuerySaveDataInternalStorageTotalSize(out size, spaceId, saveDataId); + } + fs.Impl.AbortIfNeeded(rc); + return rc; } public static Result VerifySaveData(this FileSystemClient fs, out bool isValid, ulong saveDataId, @@ -1054,7 +1657,28 @@ namespace LibHac.Fs.Shim public static Result VerifySaveData(this FileSystemClient fs, out bool isValid, SaveDataSpaceId spaceId, ulong saveDataId, Span workBuffer) { - throw new NotImplementedException(); + UnsafeHelpers.SkipParamInit(out isValid); + + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = fsProxy.Target.VerifySaveDataFileSystemBySaveDataSpaceId(spaceId, saveDataId, + new OutBuffer(workBuffer)); + + if (ResultFs.DataCorrupted.Includes(rc)) + { + isValid = false; + return Result.Success; + } + + fs.Impl.AbortIfNeeded(rc); + + if (rc.IsSuccess()) + { + isValid = true; + return Result.Success; + } + + return rc; } public static Result CorruptSaveDataForDebug(this FileSystemClient fs, ulong saveDataId) @@ -1065,13 +1689,23 @@ namespace LibHac.Fs.Shim public static Result CorruptSaveDataForDebug(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId) { - throw new NotImplementedException(); + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = fsProxy.Target.CorruptSaveDataFileSystemBySaveDataSpaceId(spaceId, saveDataId); + + fs.Impl.AbortIfNeeded(rc); + return rc; } public static Result CorruptSaveDataForDebug(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, long offset) { - throw new NotImplementedException(); + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = fsProxy.Target.CorruptSaveDataFileSystemByOffset(spaceId, saveDataId, offset); + + fs.Impl.AbortIfNeeded(rc); + return rc; } public static void DisableAutoSaveDataCreation(this FileSystemClient fs) @@ -1086,37 +1720,232 @@ namespace LibHac.Fs.Shim public static Result DeleteCacheStorage(this FileSystemClient fs, int index) { - throw new NotImplementedException(); + Result rc; + Span logBuffer = stackalloc byte[0x20]; + + if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = Delete(fs, index); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogIndex).AppendFormat(index, 'd'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = Delete(fs, index); + } + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result Delete(FileSystemClient fs, int index) + { + if (index < 0) + return ResultFs.InvalidArgument.Log(); + + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + return fsProxy.Target.DeleteCacheStorage((ushort)index); + } } public static Result GetCacheStorageSize(this FileSystemClient fs, out long saveSize, out long journalSize, int index) { - throw new NotImplementedException(); + Result rc; + Span logBuffer = stackalloc byte[0x60]; + + if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = GetSize(fs, out saveSize, out journalSize, index); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogIndex).AppendFormat(index, 'd') + .Append(LogSaveDataSize).AppendFormat(AccessLogImpl.DereferenceOutValue(in saveSize, rc), 'd') + .Append(LogSaveDataJournalSize) + .AppendFormat(AccessLogImpl.DereferenceOutValue(in journalSize, rc), 'd'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = GetSize(fs, out saveSize, out journalSize, index); + } + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result GetSize(FileSystemClient fs, out long saveSize, out long journalSize, int index) + { + UnsafeHelpers.SkipParamInit(out saveSize, out journalSize); + + if (index < 0) + return ResultFs.InvalidArgument.Log(); + + // Note: Nintendo gets the service object in the outer function and captures it for the inner function + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + return fsProxy.Target.GetCacheStorageSize(out saveSize, out journalSize, (ushort)index); + } } public static Result UpdateSaveDataMacForDebug(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId) { - throw new NotImplementedException(); + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + return fsProxy.Target.UpdateSaveDataMacForDebug(spaceId, saveDataId); } - public static Result ListApplicationAccessibleSaveDataOwnerId(this FileSystemClient fs, int readCount, + public static Result ListApplicationAccessibleSaveDataOwnerId(this FileSystemClient fs, out int readCount, Span idBuffer, Ncm.ApplicationId applicationId, int programIndex, int startIndex) { - throw new NotImplementedException(); + if (idBuffer.IsEmpty) + { + readCount = 0; + return Result.Success; + } + + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + var programId = new ProgramId(applicationId.Value + (uint)programIndex); + var idOutBuffer = new OutBuffer(MemoryMarshal.Cast(idBuffer)); + + Result rc = fsProxy.Target.ListAccessibleSaveDataOwnerId(out readCount, idOutBuffer, programId, startIndex, + idBuffer.Length); + fs.Impl.AbortIfNeeded(rc); + return rc; } public static Result GetSaveDataRestoreFlag(this FileSystemClient fs, out bool isRestoreFlagSet, U8Span mountName) { - throw new NotImplementedException(); + UnsafeHelpers.SkipParamInit(out isRestoreFlagSet); + + Result rc; + FileSystemAccessor fileSystem; + Span logBuffer = stackalloc byte[0x40]; + + if (fs.Impl.IsEnabledAccessLog()) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.Find(out fileSystem, mountName); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogName).Append(mountName).Append((byte)'"'); + + fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.Find(out fileSystem, mountName); + } + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog() && fileSystem.IsEnabledAccessLog()) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = GetRestoreFlagValue(fs, out isRestoreFlagSet, fileSystem); + Tick end = fs.Hos.Os.GetSystemTick(); + + ReadOnlySpan isSetString = + AccessLogImpl.ConvertFromBoolToAccessLogBooleanValue( + AccessLogImpl.DereferenceOutValue(in isRestoreFlagSet, rc)); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogName).Append(mountName).Append((byte)'"') + .Append(LogRestoreFlag).Append(isSetString); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = GetRestoreFlagValue(fs, out isRestoreFlagSet, fileSystem); + } + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result GetRestoreFlagValue(FileSystemClient fs, out bool isRestoreFlagSet, + FileSystemAccessor fileSystem) + { + Unsafe.SkipInit(out isRestoreFlagSet); + + if (fileSystem is null) + return ResultFs.NullptrArgument.Log(); + + Result rc = fileSystem.GetSaveDataAttribute(out SaveDataAttribute attribute); + if (rc.IsFailure()) return rc; + + if (attribute.ProgramId == Fs.SaveData.InvalidProgramId) + attribute.ProgramId = Fs.SaveData.AutoResolveCallerProgramId; + + var extraDataMask = new SaveDataExtraData(); + extraDataMask.Flags = SaveDataFlags.Restore; + + rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, SaveDataSpaceId.User, + in attribute, in extraDataMask); + if (rc.IsFailure()) return rc; + + isRestoreFlagSet = extraData.Flags.HasFlag(SaveDataFlags.Restore); + return Result.Success; + } } public static Result GetDeviceSaveDataSize(this FileSystemClientImpl fs, out long saveSize, out long journalSize, ApplicationId applicationId) { - throw new NotImplementedException(); + Result rc; + Span logBuffer = stackalloc byte[0x70]; + + if (fs.IsEnabledAccessLog() && fs.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = GetSize(fs, out saveSize, out journalSize, applicationId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') + .Append(LogSaveDataSize).AppendFormat(AccessLogImpl.DereferenceOutValue(in saveSize, rc), 'd') + .Append(LogSaveDataJournalSize) + .AppendFormat(AccessLogImpl.DereferenceOutValue(in journalSize, rc), 'd'); + + fs.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = GetSize(fs, out saveSize, out journalSize, applicationId); + } + fs.AbortIfNeeded(rc); + return rc; + + static Result GetSize(FileSystemClientImpl fs, out long saveSize, out long journalSize, + ApplicationId applicationId) + { + UnsafeHelpers.SkipParamInit(out saveSize, out journalSize); + + var extraDataMask = new SaveDataExtraData(); + extraDataMask.DataSize = unchecked((long)0xFFFFFFFFFFFFFFFF); + extraDataMask.JournalSize = unchecked((long)0xFFFFFFFFFFFFFFFF); + + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, new ProgramId(applicationId.Value), + SaveDataType.Device, Fs.SaveData.InvalidUserId, 0); + if (rc.IsFailure()) return rc; + + rc = fs.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, SaveDataSpaceId.User, + in attribute, in extraDataMask); + if (rc.IsFailure()) return rc; + + saveSize = extraData.DataSize; + journalSize = extraData.JournalSize; + + return Result.Success; + } } } } diff --git a/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs b/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs index da919272..2c1ece26 100644 --- a/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs +++ b/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs @@ -725,7 +725,7 @@ namespace LibHac.FsSystem if (_timeStampGetter.Get(out long timeStamp).IsSuccess()) { - extraData.TimeStamp = (ulong)timeStamp; + extraData.TimeStamp = timeStamp; } long commitId = 0;