mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Implement or skeleton fsa file system code
This commit is contained in:
parent
61654298d2
commit
a11e84cc81
@ -563,6 +563,7 @@ Module,DescriptionStart,DescriptionEnd,Flags,Namespace,Name,Summary
|
||||
2,6071,,,,InvalidCommitNameCount,Up to 10 file systems can be committed at the same time.
|
||||
2,6072,,,,InvalidOpenMode,
|
||||
2,6074,,,,InvalidDirectoryOpenMode,
|
||||
2,6075,,,,InvalidCommitOption,
|
||||
|
||||
2,6080,6099,,,InvalidEnumValue,
|
||||
2,6081,,,,InvalidSaveDataState,
|
||||
|
|
@ -91,7 +91,7 @@ namespace LibHac.Common
|
||||
/// </summary>
|
||||
/// <returns><see langword="true"/> if the span has no buffer.
|
||||
/// Otherwise, <see langword="false"/>.</returns>
|
||||
public unsafe bool IsNull() => (IntPtr)Unsafe.AsPointer(ref MemoryMarshal.GetReference(_buffer)) == IntPtr.Zero;
|
||||
public bool IsNull() => Unsafe.IsNullRef(ref MemoryMarshal.GetReference(_buffer));
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="U8Span"/> has no buffer or begins with a null terminator.
|
||||
|
@ -87,7 +87,7 @@ namespace LibHac.Common
|
||||
/// </summary>
|
||||
/// <returns><see langword="true"/> if the span has no buffer.
|
||||
/// Otherwise, <see langword="false"/>.</returns>
|
||||
public unsafe bool IsNull() => (IntPtr)Unsafe.AsPointer(ref MemoryMarshal.GetReference(_buffer)) == IntPtr.Zero;
|
||||
public bool IsNull() => Unsafe.IsNullRef(ref MemoryMarshal.GetReference(_buffer));
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="U8StringMutable"/> has no buffer or begins with a null terminator.
|
||||
|
@ -3,6 +3,7 @@ using System.Buffers;
|
||||
using System.Buffers.Text;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace LibHac.Common
|
||||
@ -12,9 +13,12 @@ namespace LibHac.Common
|
||||
{
|
||||
private const int NullTerminatorLength = 1;
|
||||
|
||||
public Span<byte> Buffer { get; }
|
||||
public Span<byte> Buffer { get; private set; }
|
||||
public int Length { get; private set; }
|
||||
public bool Overflowed { get; private set; }
|
||||
public bool AutoExpand { get; }
|
||||
|
||||
private byte[] _rentedArray;
|
||||
|
||||
public readonly int Capacity
|
||||
{
|
||||
@ -22,17 +26,36 @@ namespace LibHac.Common
|
||||
get => Buffer.Length - NullTerminatorLength;
|
||||
}
|
||||
|
||||
public U8StringBuilder(Span<byte> buffer)
|
||||
public U8StringBuilder(Span<byte> buffer, bool autoExpand = false)
|
||||
{
|
||||
Buffer = buffer;
|
||||
Length = 0;
|
||||
Overflowed = false;
|
||||
AutoExpand = autoExpand;
|
||||
_rentedArray = null;
|
||||
|
||||
if (autoExpand)
|
||||
{
|
||||
TryEnsureAdditionalCapacity(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowIfBufferLengthIsZero();
|
||||
}
|
||||
|
||||
AddNullTerminator();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
byte[] toReturn = _rentedArray;
|
||||
this = default;
|
||||
if (toReturn is not null)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(_rentedArray);
|
||||
}
|
||||
}
|
||||
|
||||
// These functions are internal so they can be called by the extension methods
|
||||
// in U8StringBuilderExtensions. It's not an ideal setup, but it allows append
|
||||
// calls to be chained without accidentally creating a copy of the U8StringBuilder.
|
||||
@ -42,7 +65,7 @@ namespace LibHac.Common
|
||||
|
||||
int valueLength = StringUtils.GetLength(value);
|
||||
|
||||
if (!HasAdditionalCapacity(valueLength))
|
||||
if (!TryEnsureAdditionalCapacity(valueLength))
|
||||
{
|
||||
Overflowed = true;
|
||||
return;
|
||||
@ -57,7 +80,7 @@ namespace LibHac.Common
|
||||
{
|
||||
if (Overflowed) return;
|
||||
|
||||
if (!HasAdditionalCapacity(1))
|
||||
if (!TryEnsureAdditionalCapacity(1))
|
||||
{
|
||||
Overflowed = true;
|
||||
return;
|
||||
@ -114,10 +137,18 @@ namespace LibHac.Common
|
||||
return requiredCapacity <= Capacity;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private readonly bool HasAdditionalCapacity(int requiredAdditionalCapacity)
|
||||
private bool TryEnsureAdditionalCapacity(int requiredAdditionalCapacity)
|
||||
{
|
||||
return HasCapacity(Length + requiredAdditionalCapacity);
|
||||
bool hasCapacity = HasCapacity(Length + requiredAdditionalCapacity);
|
||||
|
||||
if (!hasCapacity && AutoExpand)
|
||||
{
|
||||
Grow(requiredAdditionalCapacity);
|
||||
Assert.True(HasCapacity(Length + requiredAdditionalCapacity));
|
||||
hasCapacity = true;
|
||||
}
|
||||
|
||||
return hasCapacity;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@ -126,6 +157,21 @@ namespace LibHac.Common
|
||||
Buffer[Length] = 0;
|
||||
}
|
||||
|
||||
private void Grow(int requiredAdditionalCapacity)
|
||||
{
|
||||
byte[] poolArray =
|
||||
ArrayPool<byte>.Shared.Rent(Math.Max(Length + requiredAdditionalCapacity, Capacity * 2));
|
||||
|
||||
Buffer.Slice(0, Length).CopyTo(poolArray);
|
||||
|
||||
byte[] toReturn = _rentedArray;
|
||||
_rentedArray = poolArray;
|
||||
if (toReturn is not null)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(toReturn);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly void ThrowIfBufferLengthIsZero()
|
||||
{
|
||||
if (Buffer.Length == 0) throw new ArgumentException("Buffer length must be greater than 0.");
|
||||
|
@ -1,5 +1,8 @@
|
||||
using System;
|
||||
using System.Buffers.Text;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.Fs.Shim;
|
||||
@ -115,8 +118,160 @@ namespace LibHac.Fs.Impl
|
||||
All = Application | System
|
||||
}
|
||||
|
||||
internal readonly struct IdentifyAccessLogHandle
|
||||
{
|
||||
public readonly object Handle;
|
||||
|
||||
private IdentifyAccessLogHandle(object handle) => Handle = handle;
|
||||
public static IdentifyAccessLogHandle MakeHandle(object handle) => new IdentifyAccessLogHandle(handle);
|
||||
}
|
||||
|
||||
internal struct IdString
|
||||
{
|
||||
private Buffer32 _buffer;
|
||||
|
||||
private ReadOnlySpan<byte> ToValueString(int value)
|
||||
{
|
||||
bool success = Utf8Formatter.TryFormat(value, _buffer.Bytes, out int length);
|
||||
Assert.True(success && length < _buffer.Bytes.Length);
|
||||
_buffer[length] = 0;
|
||||
|
||||
return _buffer.Bytes;
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> ToString(Priority value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case Priority.Realtime: return new[] { (byte)'R', (byte)'e', (byte)'a', (byte)'l', (byte)'t', (byte)'i', (byte)'m', (byte)'e' };
|
||||
case Priority.Normal: return new[] { (byte)'N', (byte)'o', (byte)'r', (byte)'m', (byte)'a', (byte)'l' };
|
||||
case Priority.Low: return new[] { (byte)'L', (byte)'o', (byte)'w' };
|
||||
default: return ToValueString((int)value);
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> ToString(PriorityRaw value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case PriorityRaw.Realtime: return new[] { (byte)'R', (byte)'e', (byte)'a', (byte)'l', (byte)'t', (byte)'i', (byte)'m', (byte)'e' };
|
||||
case PriorityRaw.Normal: return new[] { (byte)'N', (byte)'o', (byte)'r', (byte)'m', (byte)'a', (byte)'l' };
|
||||
case PriorityRaw.Low: return new[] { (byte)'L', (byte)'o', (byte)'w' };
|
||||
case PriorityRaw.Background: return new[] { (byte)'B', (byte)'a', (byte)'c', (byte)'k', (byte)'g', (byte)'r', (byte)'o', (byte)'u', (byte)'n', (byte)'d' };
|
||||
default: return ToValueString((int)value);
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> ToString(ImageDirectoryId value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case ImageDirectoryId.Nand: return new[] { (byte)'N', (byte)'a', (byte)'n', (byte)'d' };
|
||||
case ImageDirectoryId.SdCard: return new[] { (byte)'S', (byte)'d', (byte)'C', (byte)'a', (byte)'r', (byte)'d' };
|
||||
default: return ToValueString((int)value);
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> ToString(ContentStorageId value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case ContentStorageId.System: return new[] { (byte)'S', (byte)'y', (byte)'s', (byte)'t', (byte)'e', (byte)'m' };
|
||||
case ContentStorageId.User: return new[] { (byte)'U', (byte)'s', (byte)'e', (byte)'r' };
|
||||
case ContentStorageId.SdCard: return new[] { (byte)'S', (byte)'d', (byte)'C', (byte)'a', (byte)'r', (byte)'d' };
|
||||
default: return ToValueString((int)value);
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> ToString(GameCardPartition value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case GameCardPartition.Update: return new[] { (byte)'U', (byte)'p', (byte)'d', (byte)'a', (byte)'t', (byte)'e' };
|
||||
case GameCardPartition.Normal: return new[] { (byte)'N', (byte)'o', (byte)'r', (byte)'m', (byte)'a', (byte)'l' };
|
||||
case GameCardPartition.Secure: return new[] { (byte)'S', (byte)'e', (byte)'c', (byte)'u', (byte)'r', (byte)'e' };
|
||||
case GameCardPartition.Logo: return new[] { (byte)'L', (byte)'o', (byte)'g', (byte)'o' };
|
||||
default: return ToValueString((int)value);
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> ToString(SaveDataSpaceId value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case SaveDataSpaceId.System: return new[] { (byte)'S', (byte)'y', (byte)'s', (byte)'t', (byte)'e', (byte)'m' };
|
||||
case SaveDataSpaceId.User: return new[] { (byte)'U', (byte)'s', (byte)'e', (byte)'r' };
|
||||
case SaveDataSpaceId.SdSystem: return new[] { (byte)'S', (byte)'d', (byte)'S', (byte)'y', (byte)'s', (byte)'t', (byte)'e', (byte)'m' };
|
||||
case SaveDataSpaceId.ProperSystem: return new[] { (byte)'P', (byte)'r', (byte)'o', (byte)'p', (byte)'e', (byte)'r', (byte)'S', (byte)'y', (byte)'s', (byte)'t', (byte)'e', (byte)'m' };
|
||||
default: return ToValueString((int)value);
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> ToString(ContentType value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case ContentType.Meta: return new[] { (byte)'M', (byte)'e', (byte)'t', (byte)'a' };
|
||||
case ContentType.Control: return new[] { (byte)'C', (byte)'o', (byte)'n', (byte)'t', (byte)'r', (byte)'o', (byte)'l' };
|
||||
case ContentType.Manual: return new[] { (byte)'M', (byte)'a', (byte)'n', (byte)'u', (byte)'a', (byte)'l' };
|
||||
case ContentType.Logo: return new[] { (byte)'L', (byte)'o', (byte)'g', (byte)'o' };
|
||||
case ContentType.Data: return new[] { (byte)'D', (byte)'a', (byte)'t', (byte)'a' };
|
||||
default: return ToValueString((int)value);
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> ToString(BisPartitionId value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case BisPartitionId.BootPartition1Root: return new[] { (byte)'B', (byte)'o', (byte)'o', (byte)'t', (byte)'P', (byte)'a', (byte)'r', (byte)'t', (byte)'i', (byte)'t', (byte)'i', (byte)'o', (byte)'n', (byte)'1', (byte)'R', (byte)'o', (byte)'o', (byte)'t' };
|
||||
case BisPartitionId.BootPartition2Root: return new[] { (byte)'B', (byte)'o', (byte)'o', (byte)'t', (byte)'P', (byte)'a', (byte)'r', (byte)'t', (byte)'i', (byte)'t', (byte)'i', (byte)'o', (byte)'n', (byte)'2', (byte)'R', (byte)'o', (byte)'o', (byte)'t' };
|
||||
case BisPartitionId.UserDataRoot: return new[] { (byte)'U', (byte)'s', (byte)'e', (byte)'r', (byte)'D', (byte)'a', (byte)'t', (byte)'a', (byte)'R', (byte)'o', (byte)'o', (byte)'t' };
|
||||
case BisPartitionId.BootConfigAndPackage2Part1: return new[] { (byte)'B', (byte)'o', (byte)'o', (byte)'t', (byte)'C', (byte)'o', (byte)'n', (byte)'f', (byte)'i', (byte)'g', (byte)'A', (byte)'n', (byte)'d', (byte)'P', (byte)'a', (byte)'c', (byte)'k', (byte)'a', (byte)'g', (byte)'e', (byte)'2', (byte)'P', (byte)'a', (byte)'r', (byte)'t', (byte)'1' };
|
||||
case BisPartitionId.BootConfigAndPackage2Part2: return new[] { (byte)'B', (byte)'o', (byte)'o', (byte)'t', (byte)'C', (byte)'o', (byte)'n', (byte)'f', (byte)'i', (byte)'g', (byte)'A', (byte)'n', (byte)'d', (byte)'P', (byte)'a', (byte)'c', (byte)'k', (byte)'a', (byte)'g', (byte)'e', (byte)'2', (byte)'P', (byte)'a', (byte)'r', (byte)'t', (byte)'2' };
|
||||
case BisPartitionId.BootConfigAndPackage2Part3: return new[] { (byte)'B', (byte)'o', (byte)'o', (byte)'t', (byte)'C', (byte)'o', (byte)'n', (byte)'f', (byte)'i', (byte)'g', (byte)'A', (byte)'n', (byte)'d', (byte)'P', (byte)'a', (byte)'c', (byte)'k', (byte)'a', (byte)'g', (byte)'e', (byte)'2', (byte)'P', (byte)'a', (byte)'r', (byte)'t', (byte)'3' };
|
||||
case BisPartitionId.BootConfigAndPackage2Part4: return new[] { (byte)'B', (byte)'o', (byte)'o', (byte)'t', (byte)'C', (byte)'o', (byte)'n', (byte)'f', (byte)'i', (byte)'g', (byte)'A', (byte)'n', (byte)'d', (byte)'P', (byte)'a', (byte)'c', (byte)'k', (byte)'a', (byte)'g', (byte)'e', (byte)'2', (byte)'P', (byte)'a', (byte)'r', (byte)'t', (byte)'4' };
|
||||
case BisPartitionId.BootConfigAndPackage2Part5: return new[] { (byte)'B', (byte)'o', (byte)'o', (byte)'t', (byte)'C', (byte)'o', (byte)'n', (byte)'f', (byte)'i', (byte)'g', (byte)'A', (byte)'n', (byte)'d', (byte)'P', (byte)'a', (byte)'c', (byte)'k', (byte)'a', (byte)'g', (byte)'e', (byte)'2', (byte)'P', (byte)'a', (byte)'r', (byte)'t', (byte)'5' };
|
||||
case BisPartitionId.BootConfigAndPackage2Part6: return new[] { (byte)'B', (byte)'o', (byte)'o', (byte)'t', (byte)'C', (byte)'o', (byte)'n', (byte)'f', (byte)'i', (byte)'g', (byte)'A', (byte)'n', (byte)'d', (byte)'P', (byte)'a', (byte)'c', (byte)'k', (byte)'a', (byte)'g', (byte)'e', (byte)'2', (byte)'P', (byte)'a', (byte)'r', (byte)'t', (byte)'6' };
|
||||
case BisPartitionId.CalibrationBinary: return new[] { (byte)'C', (byte)'a', (byte)'l', (byte)'i', (byte)'b', (byte)'r', (byte)'a', (byte)'t', (byte)'i', (byte)'o', (byte)'n', (byte)'B', (byte)'i', (byte)'n', (byte)'a', (byte)'r', (byte)'y' };
|
||||
case BisPartitionId.CalibrationFile: return new[] { (byte)'C', (byte)'a', (byte)'l', (byte)'i', (byte)'b', (byte)'r', (byte)'a', (byte)'t', (byte)'i', (byte)'o', (byte)'n', (byte)'F', (byte)'i', (byte)'l', (byte)'e' };
|
||||
case BisPartitionId.SafeMode: return new[] { (byte)'S', (byte)'a', (byte)'f', (byte)'e', (byte)'M', (byte)'o', (byte)'d', (byte)'e' };
|
||||
case BisPartitionId.User: return new[] { (byte)'U', (byte)'s', (byte)'e', (byte)'r' };
|
||||
case BisPartitionId.System: return new[] { (byte)'S', (byte)'y', (byte)'s', (byte)'t', (byte)'e', (byte)'m' };
|
||||
case BisPartitionId.SystemProperEncryption: return new[] { (byte)'S', (byte)'y', (byte)'s', (byte)'t', (byte)'e', (byte)'m', (byte)'P', (byte)'r', (byte)'o', (byte)'p', (byte)'e', (byte)'r', (byte)'E', (byte)'n', (byte)'c', (byte)'r', (byte)'y', (byte)'p', (byte)'t', (byte)'i', (byte)'o', (byte)'n' };
|
||||
case BisPartitionId.SystemProperPartition: return new[] { (byte)'S', (byte)'y', (byte)'s', (byte)'t', (byte)'e', (byte)'m', (byte)'P', (byte)'r', (byte)'o', (byte)'p', (byte)'e', (byte)'r', (byte)'P', (byte)'a', (byte)'r', (byte)'t', (byte)'i', (byte)'t', (byte)'i', (byte)'o', (byte)'n' };
|
||||
case (BisPartitionId)35: return new[] { (byte)'I', (byte)'n', (byte)'v', (byte)'a', (byte)'l', (byte)'i', (byte)'d' };
|
||||
default: return ToValueString((int)value);
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> ToString(DirectoryEntryType value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case DirectoryEntryType.Directory: return new[] { (byte)'D', (byte)'i', (byte)'r', (byte)'e', (byte)'c', (byte)'t', (byte)'o', (byte)'r', (byte)'y' };
|
||||
case DirectoryEntryType.File: return new[] { (byte)'F', (byte)'i', (byte)'l', (byte)'e' };
|
||||
default: return ToValueString((int)value);
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> ToString(MountHostOption value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case MountHostOption.PseudoCaseSensitive: return new[] { (byte)'M', (byte)'o', (byte)'u', (byte)'n', (byte)'t', (byte)'H', (byte)'o', (byte)'s', (byte)'t', (byte)'O', (byte)'p', (byte)'t', (byte)'i', (byte)'o', (byte)'n', (byte)'F', (byte)'l', (byte)'a', (byte)'g', (byte)'_', (byte)'P', (byte)'s', (byte)'e', (byte)'u', (byte)'d', (byte)'o', (byte)'C', (byte)'a', (byte)'s', (byte)'e', (byte)'S', (byte)'e', (byte)'n', (byte)'s', (byte)'i', (byte)'t', (byte)'i', (byte)'v', (byte)'e' };
|
||||
default: return ToValueString((int)value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static class AccessLogImpl
|
||||
{
|
||||
internal static T DereferenceOutValue<T>(in T value, Result result) where T : unmanaged
|
||||
{
|
||||
return result.IsSuccess() ? value : default;
|
||||
}
|
||||
|
||||
private static void GetProgramIndexForAccessLog(FileSystemClient fs, out int index, out int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
@ -137,6 +292,53 @@ namespace LibHac.Fs.Impl
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static void OutputAccessLog(this FileSystemClientImpl fs, Result result, Tick start, Tick end,
|
||||
FileHandle2 handle, U8Span message, [CallerMemberName] string functionName = "")
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static void OutputAccessLog(this FileSystemClientImpl fs, Result result, Tick start, Tick end,
|
||||
DirectoryHandle2 handle, U8Span message, [CallerMemberName] string functionName = "")
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static void OutputAccessLog(this FileSystemClientImpl fs, Result result, Tick start, Tick end,
|
||||
IdentifyAccessLogHandle handle, U8Span message, [CallerMemberName] string functionName = "")
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static void OutputAccessLog(this FileSystemClientImpl fs, Result result, Tick start, Tick end,
|
||||
object handle, U8Span message, [CallerMemberName] string functionName = "")
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static void OutputAccessLogToOnlySdCard(this FileSystemClientImpl fs, U8Span message)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static void OutputAccessLogUnlessResultSuccess(this FileSystemClientImpl fs, Result result, Tick start,
|
||||
Tick end, FileHandle2 handle, U8Span message, [CallerMemberName] string functionName = "")
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static void OutputAccessLogUnlessResultSuccess(this FileSystemClientImpl fs, Result result, Tick start,
|
||||
Tick end, DirectoryHandle2 handle, U8Span message, [CallerMemberName] string functionName = "")
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static void OutputAccessLogUnlessResultSuccess(this FileSystemClientImpl fs, Result result, Tick start,
|
||||
Tick end, object handle, U8Span message, [CallerMemberName] string functionName = "")
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal static bool IsEnabledAccessLog(this FileSystemClientImpl fs, AccessLogTarget target)
|
||||
{
|
||||
ref AccessLogGlobals g = ref fs.Globals.AccessLog;
|
||||
@ -180,5 +382,58 @@ namespace LibHac.Fs.Impl
|
||||
{
|
||||
return fs.IsEnabledAccessLog(AccessLogTarget.All);
|
||||
}
|
||||
|
||||
public static bool IsEnabledHandleAccessLog(this FileSystemClientImpl _, FileHandle2 handle)
|
||||
{
|
||||
if (handle.File is null)
|
||||
return true;
|
||||
|
||||
FileSystemAccessor fsAccessor = handle.File.GetParent();
|
||||
return fsAccessor is not null && fsAccessor.IsEnabledAccessLog();
|
||||
}
|
||||
|
||||
public static bool IsEnabledHandleAccessLog(this FileSystemClientImpl _, DirectoryHandle2 handle)
|
||||
{
|
||||
if (handle.Directory is null)
|
||||
return true;
|
||||
|
||||
return handle.Directory.GetParent().IsEnabledAccessLog();
|
||||
}
|
||||
|
||||
public static bool IsEnabledHandleAccessLog(this FileSystemClientImpl _, IdentifyAccessLogHandle handle)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsEnabledHandleAccessLog(this FileSystemClientImpl _, object handle)
|
||||
{
|
||||
if (handle is null)
|
||||
return true;
|
||||
|
||||
// We should never receive non-null here.
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
||||
Assert.True(handle is null);
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsEnabledFileSystemAccessorAccessLog(this FileSystemClientImpl fs, U8Span mountName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static void EnableFileSystemAccessorAccessLog(this FileSystemClientImpl fs, U8Span mountName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static void FlushAccessLog(this FileSystemClientImpl fs)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static void FlushAccessLogOnSdCard(this FileSystemClientImpl fs)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,4 +19,14 @@ namespace LibHac.Fs
|
||||
Directory.Parent.FsClient.CloseDirectory(this);
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct DirectoryHandle2
|
||||
{
|
||||
internal readonly Impl.DirectoryAccessor Directory;
|
||||
|
||||
internal DirectoryHandle2(Impl.DirectoryAccessor directory)
|
||||
{
|
||||
Directory = directory;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,4 +19,14 @@ namespace LibHac.Fs
|
||||
File.Parent.FsClient.CloseFile(this);
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct FileHandle2
|
||||
{
|
||||
internal readonly Impl.FileAccessor File;
|
||||
|
||||
internal FileHandle2(Impl.FileAccessor file)
|
||||
{
|
||||
File = file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,10 @@ namespace LibHac.Fs
|
||||
public HorizonClient Hos;
|
||||
public object InitMutex;
|
||||
public AccessLogGlobals AccessLog;
|
||||
public UserMountTableGlobals UserMountTable;
|
||||
public FileSystemProxyServiceObjectGlobals FileSystemProxyServiceObject;
|
||||
public FsContextHandlerGlobals FsContextHandler;
|
||||
public ResultHandlingUtilityGlobals ResultHandlingUtility;
|
||||
}
|
||||
|
||||
public partial class FileSystemClient
|
||||
@ -51,12 +54,19 @@ namespace LibHac.Fs
|
||||
{
|
||||
Time = horizonClient.Time;
|
||||
|
||||
Globals.Hos = horizonClient;
|
||||
Globals.InitMutex = new object();
|
||||
InitializeGlobals(horizonClient);
|
||||
|
||||
Assert.NotNull(Time);
|
||||
}
|
||||
|
||||
private void InitializeGlobals(HorizonClient horizonClient)
|
||||
{
|
||||
Globals.Hos = horizonClient;
|
||||
Globals.InitMutex = new object();
|
||||
Globals.UserMountTable.Initialize(this);
|
||||
Globals.FsContextHandler.Initialize(this);
|
||||
}
|
||||
|
||||
public bool HasFileSystemServer()
|
||||
{
|
||||
return Hos != null;
|
||||
|
130
src/LibHac/Fs/FsContext.cs
Normal file
130
src/LibHac/Fs/FsContext.cs
Normal file
@ -0,0 +1,130 @@
|
||||
using System;
|
||||
|
||||
namespace LibHac.Fs
|
||||
{
|
||||
public enum AbortSpecifier
|
||||
{
|
||||
Default,
|
||||
Abort,
|
||||
Return
|
||||
}
|
||||
|
||||
internal struct FsContextHandlerGlobals
|
||||
{
|
||||
public bool IsAutoAbortEnabled;
|
||||
public FsContext DefaultContext;
|
||||
public FsContext AllReturnContext;
|
||||
|
||||
public void Initialize(FileSystemClient fsClient)
|
||||
{
|
||||
DefaultContext = new FsContext(fsClient, FsContextHandler.DefaultResultHandler);
|
||||
AllReturnContext = new FsContext(fsClient, FsContextHandler.AllReturnResultHandler);
|
||||
}
|
||||
}
|
||||
|
||||
public delegate AbortSpecifier ResultHandler(FileSystemClient fs, Result result);
|
||||
|
||||
public static class FsContextHandler
|
||||
{
|
||||
[ThreadStatic]
|
||||
private static FsContext _currentThreadContext;
|
||||
|
||||
internal static AbortSpecifier DefaultResultHandler(FileSystemClient fs, Result result)
|
||||
{
|
||||
if (fs.Globals.FsContextHandler.IsAutoAbortEnabled)
|
||||
return AbortSpecifier.Default;
|
||||
else
|
||||
return AbortSpecifier.Return;
|
||||
}
|
||||
|
||||
internal static AbortSpecifier AllReturnResultHandler(FileSystemClient fs, Result result)
|
||||
{
|
||||
return AbortSpecifier.Return;
|
||||
}
|
||||
|
||||
public static void SetEnabledAutoAbort(this FileSystemClient fs, bool isEnabled)
|
||||
{
|
||||
fs.Globals.FsContextHandler.IsAutoAbortEnabled = isEnabled;
|
||||
}
|
||||
|
||||
public static void SetDefaultFsContextResultHandler(this FileSystemClient fs,
|
||||
ResultHandler resultHandler)
|
||||
{
|
||||
fs.Globals.FsContextHandler.DefaultContext.SetHandler(resultHandler ?? DefaultResultHandler);
|
||||
}
|
||||
|
||||
public static ref FsContext GetCurrentThreadFsContext(this FileSystemClient fs)
|
||||
{
|
||||
ref FsContext context = ref _currentThreadContext;
|
||||
if (context.IsValid)
|
||||
{
|
||||
return ref context;
|
||||
}
|
||||
|
||||
return ref fs.Globals.FsContextHandler.DefaultContext;
|
||||
}
|
||||
|
||||
public static void SetCurrentThreadFsContext(this FileSystemClient fs, FsContext context)
|
||||
{
|
||||
_currentThreadContext = context;
|
||||
}
|
||||
|
||||
public static bool IsResolubleAccessFailureResult(Result result)
|
||||
{
|
||||
return ResultFs.GameCardAccessFailed.Includes(result);
|
||||
}
|
||||
|
||||
public static bool IsAutoAbortPolicyCustomized(this FileSystemClient fs)
|
||||
{
|
||||
ref FsContextHandlerGlobals g = ref fs.Globals.FsContextHandler;
|
||||
return !g.IsAutoAbortEnabled || GetCurrentThreadFsContext(fs).GetHandler() != DefaultResultHandler;
|
||||
}
|
||||
}
|
||||
|
||||
public struct FsContext
|
||||
{
|
||||
private readonly FileSystemClient _fsClient;
|
||||
private ResultHandler _resultHandler;
|
||||
|
||||
internal bool IsValid => _fsClient is not null;
|
||||
|
||||
public FsContext(FileSystemClient fsClient, ResultHandler resultHandler)
|
||||
{
|
||||
_fsClient = fsClient;
|
||||
_resultHandler = resultHandler;
|
||||
}
|
||||
|
||||
public AbortSpecifier HandleResult(Result result)
|
||||
{
|
||||
return _resultHandler(_fsClient, result);
|
||||
}
|
||||
|
||||
public ResultHandler GetHandler()
|
||||
{
|
||||
return _resultHandler;
|
||||
}
|
||||
|
||||
public void SetHandler(ResultHandler handler)
|
||||
{
|
||||
_resultHandler = handler;
|
||||
}
|
||||
}
|
||||
|
||||
public struct ScopedAutoAbortDisabler
|
||||
{
|
||||
private FileSystemClient _fsClient;
|
||||
private FsContext _prevContext;
|
||||
|
||||
public ScopedAutoAbortDisabler(FileSystemClient fs)
|
||||
{
|
||||
_fsClient = fs;
|
||||
_prevContext = fs.GetCurrentThreadFsContext();
|
||||
fs.SetCurrentThreadFsContext(fs.Globals.FsContextHandler.AllReturnContext);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_fsClient.SetCurrentThreadFsContext(_prevContext);
|
||||
}
|
||||
}
|
||||
}
|
@ -177,6 +177,12 @@ namespace LibHac.Fs
|
||||
Restore = 1 << 4
|
||||
}
|
||||
|
||||
public enum CommitOptionFlag
|
||||
{
|
||||
None = 1,
|
||||
SetRestoreFlag = 2
|
||||
}
|
||||
|
||||
public enum SdmmcPort
|
||||
{
|
||||
Mmc = 0,
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
|
||||
namespace LibHac.Fs.Fsa
|
||||
{
|
||||
@ -11,5 +12,36 @@ namespace LibHac.Fs.Fsa
|
||||
{
|
||||
Result GetSaveDataAttribute(out SaveDataAttribute attribute);
|
||||
}
|
||||
|
||||
internal static class Registrar
|
||||
{
|
||||
public static Result Register(U8Span name, IFileSystem fileSystem)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Result Register(U8Span name, IFileSystem fileSystem, ICommonMountNameGenerator mountNameGenerator)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Result Register(U8Span name, IMultiCommitTarget multiCommitTarget, IFileSystem fileSystem,
|
||||
ICommonMountNameGenerator mountNameGenerator, bool useDataCache, bool usePathCache)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Result Register(U8Span name, IMultiCommitTarget multiCommitTarget, IFileSystem fileSystem,
|
||||
ICommonMountNameGenerator mountNameGenerator, ISaveDataAttributeGetter saveAttributeGetter,
|
||||
bool useDataCache, bool usePathCache)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static void Unregister(U8Span name)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
100
src/LibHac/Fs/Fsa/UserDirectory.cs
Normal file
100
src/LibHac/Fs/Fsa/UserDirectory.cs
Normal file
@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.Os;
|
||||
|
||||
namespace LibHac.Fs.Fsa
|
||||
{
|
||||
[SkipLocalsInit]
|
||||
public static class UserDirectory
|
||||
{
|
||||
private static DirectoryAccessor Get(DirectoryHandle2 handle)
|
||||
{
|
||||
return handle.Directory;
|
||||
}
|
||||
|
||||
public static Result ReadDirectory(this FileSystemClient fs, out long entriesRead,
|
||||
Span<DirectoryEntry> entryBuffer, DirectoryHandle2 handle)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(handle))
|
||||
{
|
||||
Tick start = fs.Hos.Os.GetSystemTick();
|
||||
rc = Get(handle).Read(out entriesRead, entryBuffer);
|
||||
Tick end = fs.Hos.Os.GetSystemTick();
|
||||
|
||||
Span<byte> buffer = stackalloc byte[0x50];
|
||||
var sb = new U8StringBuilder(buffer, true);
|
||||
|
||||
sb.Append(LogEntryBufferCount).AppendFormat(entryBuffer.Length)
|
||||
.Append(LogEntryCount).AppendFormat(entriesRead);
|
||||
fs.Impl.OutputAccessLog(rc, start, end, handle, new U8Span(sb.Buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = Get(handle).Read(out entriesRead, entryBuffer);
|
||||
}
|
||||
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
public static Result GetDirectoryEntryCount(this FileSystemClient fs, out long count, DirectoryHandle2 handle)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(handle))
|
||||
{
|
||||
Tick start = fs.Hos.Os.GetSystemTick();
|
||||
rc = Get(handle).GetEntryCount(out count);
|
||||
Tick end = fs.Hos.Os.GetSystemTick();
|
||||
|
||||
Span<byte> buffer = stackalloc byte[0x50];
|
||||
var sb = new U8StringBuilder(buffer, true);
|
||||
|
||||
sb.Append(LogEntryCount).AppendFormat(count);
|
||||
fs.Impl.OutputAccessLog(rc, start, end, handle, new U8Span(sb.Buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = Get(handle).GetEntryCount(out count);
|
||||
}
|
||||
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
public static void CloseDirectory(this FileSystemClient fs, DirectoryHandle2 handle)
|
||||
{
|
||||
if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(handle))
|
||||
{
|
||||
Tick start = fs.Hos.Os.GetSystemTick();
|
||||
Get(handle).Dispose();
|
||||
Tick end = fs.Hos.Os.GetSystemTick();
|
||||
|
||||
fs.Impl.OutputAccessLog(Result.Success, start, end, handle, U8Span.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
Get(handle).Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<byte> LogEntryBufferCount => // ", entry_buffer_count: "
|
||||
new[]
|
||||
{
|
||||
(byte)',', (byte)' ', (byte)'e', (byte)'n', (byte)'t', (byte)'r', (byte)'y', (byte)'_',
|
||||
(byte)'b', (byte)'u', (byte)'f', (byte)'f', (byte)'e', (byte)'r', (byte)'_', (byte)'c',
|
||||
(byte)'o', (byte)'u', (byte)'n', (byte)'t', (byte)':', (byte)' '
|
||||
};
|
||||
|
||||
private static ReadOnlySpan<byte> LogEntryCount => // ", entry_count: "
|
||||
new[]
|
||||
{
|
||||
(byte)',', (byte)' ', (byte)'e', (byte)'n', (byte)'t', (byte)'r', (byte)'y', (byte)'_',
|
||||
(byte)'c', (byte)'o', (byte)'u', (byte)'n', (byte)'t', (byte)':', (byte)' '
|
||||
};
|
||||
}
|
||||
}
|
254
src/LibHac/Fs/Fsa/UserFile.cs
Normal file
254
src/LibHac/Fs/Fsa/UserFile.cs
Normal file
@ -0,0 +1,254 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.Os;
|
||||
|
||||
namespace LibHac.Fs.Fsa
|
||||
{
|
||||
[SkipLocalsInit]
|
||||
public static class UserFile
|
||||
{
|
||||
private static FileAccessor Get(FileHandle2 handle)
|
||||
{
|
||||
return handle.File;
|
||||
}
|
||||
|
||||
private static Result ReadFileImpl(FileSystemClient fs, out long bytesRead, FileHandle2 handle, long offset,
|
||||
Span<byte> destination, in ReadOption option)
|
||||
{
|
||||
return Get(handle).Read(out bytesRead, offset, destination, in option);
|
||||
}
|
||||
|
||||
public static Result ReadFile(this FileSystemClient fs, FileHandle2 handle, long offset, Span<byte> destination,
|
||||
in ReadOption option)
|
||||
{
|
||||
Result rc = ReadFileImpl(fs, out long bytesRead, handle, offset, destination, in option);
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (bytesRead == destination.Length)
|
||||
return Result.Success;
|
||||
|
||||
rc = ResultFs.OutOfRange.Log();
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
public static Result ReadFile(this FileSystemClient fs, FileHandle2 handle, long offset, Span<byte> destination)
|
||||
{
|
||||
Result rc = ReadFileImpl(fs, out long bytesRead, handle, offset, destination, ReadOption.None);
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (bytesRead == destination.Length)
|
||||
return Result.Success;
|
||||
|
||||
rc = ResultFs.OutOfRange.Log();
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
public static Result ReadFile(this FileSystemClient fs, out long bytesRead, FileHandle2 handle, long offset,
|
||||
Span<byte> destination, in ReadOption option)
|
||||
{
|
||||
Result rc = ReadFileImpl(fs, out bytesRead, handle, offset, destination, in option);
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
public static Result ReadFile(this FileSystemClient fs, out long bytesRead, FileHandle2 handle, long offset,
|
||||
Span<byte> destination)
|
||||
{
|
||||
Result rc = ReadFileImpl(fs, out bytesRead, handle, offset, destination, ReadOption.None);
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
public static Result WriteFile(this FileSystemClient fs, FileHandle2 handle, long offset,
|
||||
ReadOnlySpan<byte> source, in WriteOption option)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(handle))
|
||||
{
|
||||
Tick start = fs.Hos.Os.GetSystemTick();
|
||||
rc = Get(handle).Write(offset, source, in option);
|
||||
Tick end = fs.Hos.Os.GetSystemTick();
|
||||
|
||||
Span<byte> buffer = stackalloc byte[0x60];
|
||||
var sb = new U8StringBuilder(buffer, true);
|
||||
|
||||
sb.Append(LogOffset).AppendFormat(offset).Append(LogSize).AppendFormat(source.Length);
|
||||
|
||||
if (option.HasFlushFlag())
|
||||
sb.Append(LogWriteOptionFlush);
|
||||
|
||||
fs.Impl.OutputAccessLog(rc, start, end, handle, new U8Span(sb.Buffer));
|
||||
sb.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = Get(handle).Write(offset, source, in option);
|
||||
}
|
||||
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
public static Result FlushFile(this FileSystemClient fs, FileHandle2 handle)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(handle))
|
||||
{
|
||||
Tick start = fs.Hos.Os.GetSystemTick();
|
||||
rc = Get(handle).Flush();
|
||||
Tick end = fs.Hos.Os.GetSystemTick();
|
||||
|
||||
fs.Impl.OutputAccessLog(rc, start, end, handle, U8Span.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = Get(handle).Flush();
|
||||
}
|
||||
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
public static Result SetFileSize(this FileSystemClient fs, FileHandle2 handle, long size)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(handle))
|
||||
{
|
||||
Tick start = fs.Hos.Os.GetSystemTick();
|
||||
rc = Get(handle).SetSize(size);
|
||||
Tick end = fs.Hos.Os.GetSystemTick();
|
||||
|
||||
Span<byte> buffer = stackalloc byte[0x20];
|
||||
var sb = new U8StringBuilder(buffer, true);
|
||||
|
||||
sb.Append(LogSize).AppendFormat(size);
|
||||
fs.Impl.OutputAccessLog(rc, start, end, handle, new U8Span(sb.Buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = Get(handle).SetSize(size);
|
||||
}
|
||||
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
public static Result GetFileSize(this FileSystemClient fs, out long size, FileHandle2 handle)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(handle))
|
||||
{
|
||||
Tick start = fs.Hos.Os.GetSystemTick();
|
||||
rc = Get(handle).GetSize(out size);
|
||||
Tick end = fs.Hos.Os.GetSystemTick();
|
||||
|
||||
Span<byte> buffer = stackalloc byte[0x20];
|
||||
var sb = new U8StringBuilder(buffer, true);
|
||||
|
||||
sb.Append(LogSize).AppendFormat(AccessLogImpl.DereferenceOutValue(in size, rc));
|
||||
fs.Impl.OutputAccessLog(rc, start, end, handle, new U8Span(sb.Buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = Get(handle).GetSize(out size);
|
||||
}
|
||||
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
public static OpenMode GetFileOpenMode(this FileSystemClient fs, FileHandle2 handle)
|
||||
{
|
||||
OpenMode mode = Get(handle).GetOpenMode();
|
||||
|
||||
if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(handle))
|
||||
{
|
||||
Tick start = fs.Hos.Os.GetSystemTick();
|
||||
Tick end = fs.Hos.Os.GetSystemTick();
|
||||
|
||||
Span<byte> buffer = stackalloc byte[0x20];
|
||||
var sb = new U8StringBuilder(buffer, true);
|
||||
|
||||
sb.Append(LogOpenMode).AppendFormat((int)mode, 'X');
|
||||
fs.Impl.OutputAccessLog(Result.Success, start, end, handle, new U8Span(sb.Buffer));
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
public static void CloseFile(this FileSystemClient fs, FileHandle2 handle)
|
||||
{
|
||||
if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(handle))
|
||||
{
|
||||
Tick start = fs.Hos.Os.GetSystemTick();
|
||||
Get(handle).Dispose();
|
||||
Tick end = fs.Hos.Os.GetSystemTick();
|
||||
|
||||
fs.Impl.OutputAccessLog(Result.Success, start, end, handle, U8Span.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
Get(handle).Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static Result QueryRange(this FileSystemClient fs, out QueryRangeInfo rangeInfo, FileHandle2 handle,
|
||||
long offset, long size)
|
||||
{
|
||||
Unsafe.SkipInit(out rangeInfo);
|
||||
|
||||
Result rc = Get(handle).OperateRange(SpanHelpers.AsByteSpan(ref rangeInfo), OperationId.QueryRange, offset,
|
||||
size, ReadOnlySpan<byte>.Empty);
|
||||
|
||||
fs.Impl.IsAbortNeeded(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
public static Result InvalidateCache(this FileSystemClient fs, FileHandle2 handle, long offset, long size)
|
||||
{
|
||||
Result rc = Get(handle).OperateRange(Span<byte>.Empty, OperationId.InvalidateCache, offset, size,
|
||||
ReadOnlySpan<byte>.Empty);
|
||||
|
||||
fs.Impl.IsAbortNeeded(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<byte> LogOffset => // ", offset: "
|
||||
new[]
|
||||
{
|
||||
(byte)',', (byte)' ', (byte)'o', (byte)'f', (byte)'f', (byte)'s', (byte)'e', (byte)'t',
|
||||
(byte)':', (byte)' '
|
||||
};
|
||||
|
||||
private static ReadOnlySpan<byte> LogSize => // ", size: "
|
||||
new[]
|
||||
{
|
||||
(byte)',', (byte)' ', (byte)'s', (byte)'i', (byte)'z', (byte)'e', (byte)':', (byte)' '
|
||||
};
|
||||
|
||||
private static ReadOnlySpan<byte> LogWriteOptionFlush => // ", write_option: Flush"
|
||||
new[]
|
||||
{
|
||||
(byte)',', (byte)' ', (byte)'w', (byte)'r', (byte)'i', (byte)'t', (byte)'e', (byte)'_',
|
||||
(byte)'o', (byte)'p', (byte)'t', (byte)'i', (byte)'o', (byte)'n', (byte)':', (byte)' ',
|
||||
(byte)'F', (byte)'l', (byte)'u', (byte)'s', (byte)'h'
|
||||
};
|
||||
|
||||
private static ReadOnlySpan<byte> LogOpenMode => // ", open_mode: 0x"
|
||||
new[]
|
||||
{
|
||||
(byte)',', (byte)' ', (byte)'o', (byte)'p', (byte)'e', (byte)'n', (byte)'_', (byte)'m',
|
||||
(byte)'o', (byte)'d', (byte)'e', (byte)':', (byte)' ', (byte)'0', (byte)'x'
|
||||
};
|
||||
}
|
||||
}
|
101
src/LibHac/Fs/Fsa/UserFileSystem.cs
Normal file
101
src/LibHac/Fs/Fsa/UserFileSystem.cs
Normal file
@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
|
||||
namespace LibHac.Fs.Fsa
|
||||
{
|
||||
public static class UserFileSystem
|
||||
{
|
||||
public static Result CreateFile(this FileSystemClient fs, U8Span path, long size)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Result DeleteFile(this FileSystemClient fs, U8Span path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Result CreateDirectory(this FileSystemClient fs, U8Span path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Result DeleteDirectory(this FileSystemClient fs, U8Span path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Result DeleteDirectoryRecursively(this FileSystemClient fs, U8Span path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Result CleanDirectoryRecursively(this FileSystemClient fs, U8Span path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Result RenameFile(this FileSystemClient fs, U8Span oldPath, U8Span newPath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Result RenameDirectory(this FileSystemClient fs, U8Span oldPath, U8Span newPath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Result GetEntryType(this FileSystemClient fs, out DirectoryEntryType type, U8Span path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Result GetFreeSpaceSize(this FileSystemClient fs, out long freeSpace, U8Span path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Result OpenFile(this FileSystemClient fs, out FileHandle2 handle, U8Span path, OpenMode mode)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Result OpenFile(this FileSystemClient fs, out FileHandle2 handle, IFile file, OpenMode mode)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Result OpenDirectory(this FileSystemClient fs, out DirectoryHandle2 handle, U8Span path,
|
||||
OpenDirectoryMode mode)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static Result CommitImpl(FileSystemClient fs, U8Span mountName,
|
||||
[CallerMemberName] string functionName = "")
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Result Commit(this FileSystemClient fs, ReadOnlySpan<U8String> mountNames)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Result Commit(this FileSystemClient fs, U8Span mountName, CommitOption option)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Result Commit(this FileSystemClient fs, U8Span mountName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Result CommitSaveData(this FileSystemClient fs, U8Span mountName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
34
src/LibHac/Fs/Fsa/UserMountTable.cs
Normal file
34
src/LibHac/Fs/Fsa/UserMountTable.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Impl;
|
||||
|
||||
namespace LibHac.Fs.Fsa
|
||||
{
|
||||
internal struct UserMountTableGlobals
|
||||
{
|
||||
public MountTable MountTable;
|
||||
|
||||
public void Initialize(FileSystemClient fsClient)
|
||||
{
|
||||
MountTable = new MountTable(fsClient);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class UserMountTable
|
||||
{
|
||||
public static Result Register(this FileSystemClientImpl fs, FileSystemAccessor fileSystem)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Result Find(this FileSystemClientImpl fs, out FileSystemAccessor fileSystem, U8Span name)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static void Unregister(this FileSystemClientImpl fs, U8Span name)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -991,6 +991,8 @@ namespace LibHac.Fs
|
||||
public static Result.Base InvalidOpenMode => new Result.Base(ModuleFs, 6072);
|
||||
/// <summary>Error code: 2002-6074; Inner value: 0x2f7402</summary>
|
||||
public static Result.Base InvalidDirectoryOpenMode => new Result.Base(ModuleFs, 6074);
|
||||
/// <summary>Error code: 2002-6075; Inner value: 0x2f7602</summary>
|
||||
public static Result.Base InvalidCommitOption => new Result.Base(ModuleFs, 6075);
|
||||
|
||||
/// <summary>Error code: 2002-6080; Range: 6080-6099; Inner value: 0x2f8002</summary>
|
||||
public static Result.Base InvalidEnumValue { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6080, 6099); }
|
||||
|
68
src/LibHac/Fs/ResultHandlingUtility.cs
Normal file
68
src/LibHac/Fs/ResultHandlingUtility.cs
Normal file
@ -0,0 +1,68 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Diag;
|
||||
|
||||
namespace LibHac.Fs
|
||||
{
|
||||
internal struct ResultHandlingUtilityGlobals
|
||||
{
|
||||
public bool IsResultHandledByApplication;
|
||||
}
|
||||
|
||||
public static class ResultHandlingUtility
|
||||
{
|
||||
public static void SetResultHandledByApplication(this FileSystemClient fs, bool isHandledByApplication)
|
||||
{
|
||||
fs.Globals.ResultHandlingUtility.IsResultHandledByApplication = isHandledByApplication;
|
||||
}
|
||||
|
||||
public static bool IsAbortNeeded(this FileSystemClientImpl fs, Result result)
|
||||
{
|
||||
if (result.IsSuccess())
|
||||
return false;
|
||||
|
||||
switch (fs.Fs.GetCurrentThreadFsContext().HandleResult(result))
|
||||
{
|
||||
case AbortSpecifier.Default:
|
||||
if (fs.Globals.ResultHandlingUtility.IsResultHandledByApplication)
|
||||
{
|
||||
return ResultFs.HandledByAllProcess.Includes(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
return !(ResultFs.HandledByAllProcess.Includes(result) ||
|
||||
ResultFs.HandledBySystemProcess.Includes(result));
|
||||
}
|
||||
case AbortSpecifier.Abort:
|
||||
return true;
|
||||
case AbortSpecifier.Return:
|
||||
return false;
|
||||
default:
|
||||
Abort.UnexpectedDefault();
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public static void LogErrorMessage(this FileSystemClientImpl fs, Result result,
|
||||
[CallerMemberName] string functionName = "")
|
||||
{
|
||||
// Todo
|
||||
}
|
||||
|
||||
public static void LogResultErrorMessage(this FileSystemClientImpl fs, Result result)
|
||||
{
|
||||
// Todo
|
||||
}
|
||||
|
||||
internal static void AbortIfNeeded(this FileSystemClientImpl fs, Result result,
|
||||
[CallerMemberName] string functionName = "")
|
||||
{
|
||||
if (!IsAbortNeeded(fs, result))
|
||||
return;
|
||||
|
||||
fs.LogErrorMessage(result, functionName);
|
||||
|
||||
if (!result.IsSuccess())
|
||||
Abort.DoAbort(result);
|
||||
}
|
||||
}
|
||||
}
|
@ -223,13 +223,6 @@ namespace LibHac.Fs
|
||||
public ReadOnlySpan<byte> HashRo => SpanHelpers.CreateReadOnlySpan(in _hashStart, HashLength);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct OptionalHashSalt
|
||||
{
|
||||
public bool IsSet;
|
||||
public HashSalt HashSalt;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
|
||||
public struct SaveDataMetaInfo
|
||||
{
|
||||
@ -276,6 +269,11 @@ namespace LibHac.Fs
|
||||
[FieldOffset(0x68)] public long CommitId;
|
||||
}
|
||||
|
||||
public struct CommitOption
|
||||
{
|
||||
public CommitOptionFlag Flags;
|
||||
}
|
||||
|
||||
internal static class SaveDataTypesValidity
|
||||
{
|
||||
public static bool IsValid(in SaveDataAttribute attribute)
|
||||
|
45
src/LibHac/Fs/Shim/Priority.cs
Normal file
45
src/LibHac/Fs/Shim/Priority.cs
Normal file
@ -0,0 +1,45 @@
|
||||
namespace LibHac.Fs
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
Realtime = 0,
|
||||
Normal = 1,
|
||||
Low = 2
|
||||
}
|
||||
|
||||
public enum PriorityRaw
|
||||
{
|
||||
Realtime = 0,
|
||||
Normal = 1,
|
||||
Low = 2,
|
||||
Background = 3
|
||||
}
|
||||
}
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
public static class PriorityShim
|
||||
{
|
||||
public static void SetPriorityOnCurrentThread(this FileSystemClient fs, Priority priority)
|
||||
{
|
||||
// Todo
|
||||
}
|
||||
|
||||
public static Priority GetPriorityOnCurrentThread(this FileSystemClient fs)
|
||||
{
|
||||
// Todo
|
||||
return Priority.Normal;
|
||||
}
|
||||
|
||||
public static void SetPriorityRawOnCurrentThread(this FileSystemClient fs, PriorityRaw priority)
|
||||
{
|
||||
// Todo
|
||||
}
|
||||
|
||||
public static PriorityRaw GetPriorityRawOnCurrentThread(this FileSystemClient fs)
|
||||
{
|
||||
// Todo
|
||||
return PriorityRaw.Normal;
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,6 @@ using System.Text;
|
||||
using LibHac;
|
||||
using LibHac.Common.Keys;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace hactoolnet
|
||||
|
Loading…
x
Reference in New Issue
Block a user