diff --git a/src/LibHac/Common/FixedArrays/Array20.cs b/src/LibHac/Common/FixedArrays/Array20.cs new file mode 100644 index 00000000..5edeba12 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array20.cs @@ -0,0 +1,31 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +[StructLayout(LayoutKind.Sequential)] +public struct Array20 +{ + public const int Length = 20; + + private Array16 _0; + private Array4 _16; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array20 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array60.cs b/src/LibHac/Common/FixedArrays/Array60.cs new file mode 100644 index 00000000..f7a04c95 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array60.cs @@ -0,0 +1,32 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +[StructLayout(LayoutKind.Sequential)] +public struct Array60 +{ + public const int Length = 60; + + private Array32 _0; + private Array16 _32; + private Array12 _48; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array60 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/UserFile.cs b/src/LibHac/Fs/Fsa/UserFile.cs index 60b6598e..89a6a630 100644 --- a/src/LibHac/Fs/Fsa/UserFile.cs +++ b/src/LibHac/Fs/Fsa/UserFile.cs @@ -7,6 +7,10 @@ using static LibHac.Fs.Impl.AccessLogStrings; namespace LibHac.Fs.Fsa; +/// +/// Contains functions for interacting with opened files. +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) [SkipLocalsInit] public static class UserFile { @@ -215,12 +219,75 @@ public static class UserFile return rc; } - public static Result InvalidateCache(this FileSystemClient fs, FileHandle handle, long offset, long size) + public static Result InvalidateCache(this FileSystemClient fs, FileHandle handle) { - Result rc = Get(handle).OperateRange(Span.Empty, OperationId.InvalidateCache, offset, size, + Result rc = Get(handle).OperateRange(Span.Empty, OperationId.InvalidateCache, 0, long.MaxValue, ReadOnlySpan.Empty); fs.Impl.AbortIfNeeded(rc); return rc; } -} + + public static Result QueryUnpreparedRange(this FileSystemClient fs, out Range unpreparedRange, FileHandle handle) + { + UnsafeHelpers.SkipParamInit(out unpreparedRange); + Unsafe.SkipInit(out UnpreparedRangeInfo info); + + Result rc = Get(handle).OperateRange(SpanHelpers.AsByteSpan(ref info), OperationId.QueryUnpreparedRange, 0, 0, + ReadOnlySpan.Empty); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + unpreparedRange = info.Range; + return Result.Success; + } + + public static Result QueryUnpreparedRangeDetail(this FileSystemClient fs, + out UnpreparedRangeInfo unpreparedRangeInfo, FileHandle handle) + { + UnsafeHelpers.SkipParamInit(out unpreparedRangeInfo); + Unsafe.SkipInit(out UnpreparedRangeInfo info); + + Result rc = Get(handle).OperateRange(SpanHelpers.AsByteSpan(ref info), OperationId.QueryUnpreparedRange, 0, 0, + ReadOnlySpan.Empty); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + unpreparedRangeInfo = info; + return Result.Success; + } + + public static Result QueryLazyLoadCompletionRate(this FileSystemClient fs, out int completionRate, + FileHandle handle, int guideIndex) + { + UnsafeHelpers.SkipParamInit(out completionRate); + + Unsafe.SkipInit(out UnpreparedRangeInfo info); + var args = new LazyLoadArguments { GuideIndex = guideIndex }; + + Result rc = Get(handle).OperateRange(SpanHelpers.AsByteSpan(ref info), OperationId.QueryLazyLoadCompletionRate, + 0, 0, SpanHelpers.AsReadOnlyByteSpan(in args)); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + completionRate = info.CompletionRate; + return Result.Success; + } + + public static Result ReadyLazyLoadFileForciblyForDebug(this FileSystemClient fs, FileHandle handle, long offset, + long size) + { + Unsafe.SkipInit(out UnpreparedRangeInfo info); + + Result rc = Get(handle).OperateRange(SpanHelpers.AsByteSpan(ref info), OperationId.ReadyLazyLoadFile, + offset, size, ReadOnlySpan.Empty); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } +} \ No newline at end of file diff --git a/src/LibHac/Fs/LazyLoadTypes.cs b/src/LibHac/Fs/LazyLoadTypes.cs new file mode 100644 index 00000000..f5b2f025 --- /dev/null +++ b/src/LibHac/Fs/LazyLoadTypes.cs @@ -0,0 +1,19 @@ +using LibHac.Common.FixedArrays; + +namespace LibHac.Fs; + +public struct UnpreparedRangeInfo +{ + public Range Range; + public long FileSize; + public long PreparedRangeSize; + public long TotalReadSize; + public int CompletionRate; + public Array20 Reserved; +} + +public struct LazyLoadArguments +{ + public int GuideIndex; + public Array60 Reserved; +} \ No newline at end of file diff --git a/tests/LibHac.Tests/Fs/LazyLoadTypeTests.cs b/tests/LibHac.Tests/Fs/LazyLoadTypeTests.cs new file mode 100644 index 00000000..4cbf9562 --- /dev/null +++ b/tests/LibHac.Tests/Fs/LazyLoadTypeTests.cs @@ -0,0 +1,22 @@ +// ReSharper disable InconsistentNaming + +using System.Runtime.CompilerServices; +using LibHac.Fs; +using Xunit; + +namespace LibHac.Tests.Fs; + +public class LazyLoadTypeTests +{ + [Fact] + public static void UnpreparedRangeInfoSizeIs0x40() + { + Assert.Equal(0x40, Unsafe.SizeOf()); + } + + [Fact] + public static void LazyLoadArgumentsSizeIs0x40() + { + Assert.Equal(0x40, Unsafe.SizeOf()); + } +} \ No newline at end of file