diff --git a/src/LibHac/Fs/Common/Path.cs b/src/LibHac/Fs/Common/Path.cs
index b00ba58f..ad12394e 100644
--- a/src/LibHac/Fs/Common/Path.cs
+++ b/src/LibHac/Fs/Common/Path.cs
@@ -26,9 +26,25 @@ namespace LibHac.Fs
public bool IsBackslashAllowed() => (_value & (1 << 4)) != 0;
}
+ ///
+ /// Represents a file path stored as a UTF-8 string.
+ ///
+ ///
+ /// A has three parts to it:
+ /// 1. A that points to the current path string.
+ /// 2. A write buffer that can be allocated if operations need to be done on the path.
+ /// 3. An IsNormalized flag that tracks the path normalization status of the current path.
+ /// There are two different ways to initialize a . The "Initialize*" methods will
+ /// ensure a write buffer is allocated and copy the input path to it. will
+ /// directly use the input buffer without copying. If this method is used, the caller must ensure the path
+ /// is normalized before passing it to .
+ ///
Based on FS 12.0.3 (nnSdk 12.3.1)
[DebuggerDisplay("{" + nameof(ToString) + "(),nq}")]
public ref struct Path
{
+ ///
+ /// Used to store a path in a non-ref struct.
+ ///
[DebuggerDisplay("{" + nameof(ToString) + "(),nq}")]
public struct Stored : IDisposable
{
@@ -44,6 +60,14 @@ namespace LibHac.Fs
}
}
+ ///
+ /// Initializes this path with the data from a standard .
+ /// must be normalized.
+ ///
+ /// The used to initialize this one.
+ /// : The operation was successful.
+ /// : The IsNormalized flag of
+ /// is not .
public Result Initialize(in Path path)
{
if (!path._isNormalized)
@@ -119,27 +143,50 @@ namespace LibHac.Fs
}
}
+ ///
+ /// Gets the current write buffer.
+ ///
+ /// The write buffer.
internal Span GetWriteBuffer()
{
Assert.SdkRequires(_writeBuffer is not null);
return _writeBuffer.AsSpan();
}
+ ///
+ /// Gets the current length of the write buffer.
+ ///
+ /// The write buffer length.
internal readonly long GetWriteBufferLength()
{
return _writeBufferLength;
}
+ ///
+ /// Calculates the length of the current string.
+ ///
+ /// The length of the current string>
public readonly int GetLength()
{
return StringUtils.GetLength(GetString());
}
+ ///
+ /// Returns if
+ ///
+ ///
public readonly bool IsEmpty()
{
return _string.At(0) == 0;
}
+ ///
+ /// Calculates if the first "" characters of the
+ /// current path and are the same.
+ ///
+ /// The string to compare to this .
+ /// The maximum number of characters to compare.
+ /// if the strings are the same; otherwise .
public readonly bool IsMatchHead(ReadOnlySpan value, int length)
{
return StringUtils.Compare(GetString(), value, length) == 0;
@@ -165,6 +212,10 @@ namespace LibHac.Fs
return StringUtils.Compare(left.GetString(), right) == 0;
}
+ ///
+ /// Releases this 's write buffer and returns it to the caller.
+ ///
+ /// The write buffer if the had one; otherwise .
public byte[] ReleaseBuffer()
{
Assert.SdkRequires(_writeBuffer is not null);
@@ -175,6 +226,9 @@ namespace LibHac.Fs
return Shared.Move(ref _writeBuffer);
}
+ ///
+ /// Releases any current write buffer and sets this to an empty string.
+ ///
private void ClearBuffer()
{
byte[] oldBuffer = Shared.Move(ref _writeBuffer);
@@ -186,6 +240,12 @@ namespace LibHac.Fs
_string = EmptyPath;
}
+ ///
+ /// Releases any current write buffer and sets the provided buffer as the new write buffer.
+ ///
+ /// The new write buffer.
+ /// The length of the write buffer.
+ /// Must be a multiple of .
private void SetModifiableBuffer(byte[] buffer, int length)
{
Assert.SdkRequiresNotNull(buffer);
@@ -202,6 +262,10 @@ namespace LibHac.Fs
_string = buffer;
}
+ ///
+ /// Releases any current write buffer and sets as this 's string.
+ ///
+ /// The buffer containing the new path.
private void SetReadOnlyBuffer(ReadOnlySpan buffer)
{
_string = buffer;
@@ -214,6 +278,11 @@ namespace LibHac.Fs
_writeBufferLength = 0;
}
+ ///
+ /// Ensures the write buffer is the specified or larger.
+ ///
+ /// The minimum desired length.
+ /// : The operation was successful.
private Result Preallocate(int length)
{
if (_writeBufferLength > length)
@@ -226,6 +295,14 @@ namespace LibHac.Fs
return Result.Success;
}
+ ///
+ /// Releases any current write buffer and sets as this 's string.
+ /// The path contained by must be normalized.
+ ///
+ /// It is up to the caller to ensure the path contained by is normalized.
+ /// This function will always set the IsNormalized flag to .
+ /// The buffer containing the new path.
+ /// : The operation was successful.
public Result SetShallowBuffer(ReadOnlySpan buffer)
{
Assert.SdkRequires(_writeBufferLength == 0);
@@ -235,6 +312,12 @@ namespace LibHac.Fs
return Result.Success;
}
+ ///
+ /// Gets the buffer containing the current path.
+ ///
+ /// This 's IsNormalized flag should be
+ /// before calling this function.
+ /// The buffer containing the current path.
public readonly ReadOnlySpan GetString()
{
Assert.SdkAssert(_isNormalized);
@@ -242,6 +325,16 @@ namespace LibHac.Fs
return _string;
}
+ ///
+ /// Initializes this with the data from another Path.
+ /// must be normalized.
+ ///
+ /// This 's IsNormalized flag will be set to
+ /// the value of 's flag.
+ /// The used to initialize this one.
+ /// : The operation was successful.
+ /// : The IsNormalized flag of
+ /// is not .
public Result Initialize(in Path other)
{
if (!other._isNormalized)
@@ -261,6 +354,14 @@ namespace LibHac.Fs
return Result.Success;
}
+ ///
+ /// Initializes this with the data from a path.
+ ///
+ /// Ensures we have a large enough write buffer and copies the path to it.
+ /// This function always sets the IsNormalized flag to
+ /// because paths are always normalized upon initialization.
+ /// The path used to initialize this .
+ /// : The operation was successful.
public Result Initialize(in Stored other)
{
int otherLength = other.GetLength();
@@ -277,6 +378,15 @@ namespace LibHac.Fs
return Result.Success;
}
+ ///
+ /// Initializes this using the path in the provided buffer.
+ ///
+ /// Ensures the write buffer is large enough to hold
+ /// and copies to the write buffer.
+ /// This function does not modify the IsNormalized flag.
+ /// The buffer containing the path to use.
+ /// The length of the provided path.
+ /// : The operation was successful.
private Result InitializeImpl(ReadOnlySpan path, int length)
{
if (length == 0 || path.At(0) == NullTerminator)
@@ -296,6 +406,14 @@ namespace LibHac.Fs
return Result.Success;
}
+ ///
+ /// Initializes this using the path in the provided buffer.
+ ///
+ /// Ensures the write buffer is large enough to hold
+ /// and copies to the write buffer.
+ /// This function will always set the IsNormalized flag to .
+ /// The buffer containing the path to use.
+ /// : The operation was successful.
public Result Initialize(ReadOnlySpan path)
{
Result rc = InitializeImpl(path, StringUtils.GetLength(path));
@@ -305,6 +423,19 @@ namespace LibHac.Fs
return Result.Success;
}
+ ///
+ /// Initializes this using the path in the provided buffer and
+ /// normalizes it if the path is a relative path or a Windows path.
+ ///
+ /// This function normalizes relative paths and Windows paths but does not normalize any other paths,
+ /// although all paths are checked for invalid characters and if the path is in a valid format.
+ /// The IsNormalized flag will always be set to even if the incoming path
+ /// is not normalized. This can lead to a situation where the path is not normalized yet the
+ /// IsNormalized flag is still .
+ /// The buffer containing the path to use.
+ /// : The operation was successful.
+ /// : The path contains an invalid character.
+ /// : The path is not in a valid format.
public Result InitializeWithNormalization(ReadOnlySpan path)
{
Result rc = Initialize(path);
@@ -340,6 +471,15 @@ namespace LibHac.Fs
return Result.Success;
}
+ ///
+ /// Initializes this using the path in the provided buffer.
+ ///
+ /// Ensures the write buffer is large enough to hold
+ /// and copies to the write buffer.
+ /// This function will always set the IsNormalized flag to .
+ /// The buffer containing the path to use.
+ /// The length of the provided path.
+ /// : The operation was successful.
public Result Initialize(ReadOnlySpan path, int length)
{
Result rc = InitializeImpl(path, length);
@@ -349,6 +489,20 @@ namespace LibHac.Fs
return Result.Success;
}
+ ///
+ /// Initializes this using the path in the provided buffer and
+ /// normalizes it if the path is a relative path or a Windows path.
+ ///
+ /// This function normalizes relative paths and Windows paths but does not normalize any other paths,
+ /// although all paths are checked for invalid characters and if the path is in a valid format.
+ /// The IsNormalized flag will always be set to even if the incoming path
+ /// is not normalized. This can lead to a situation where the path is not normalized yet the
+ /// IsNormalized flag is still .
+ /// The buffer containing the path to use.
+ /// The length of the provided path.
+ /// : The operation was successful.
+ /// : The path contains an invalid character.
+ /// : The path is not in a valid format.
public Result InitializeWithNormalization(ReadOnlySpan path, int length)
{
Result rc = Initialize(path, length);
@@ -384,6 +538,14 @@ namespace LibHac.Fs
return Result.Success;
}
+
+ ///
+ /// Initializes this using the path in the provided buffer and
+ /// replaces any backslashes in the path with forward slashes.
+ ///
+ /// This function will always set the IsNormalized flag to .
+ /// The buffer containing the path to use.
+ /// : The operation was successful.
public Result InitializeWithReplaceBackslash(ReadOnlySpan path)
{
Result rc = InitializeImpl(path, StringUtils.GetLength(path));
@@ -399,6 +561,13 @@ namespace LibHac.Fs
return Result.Success;
}
+ ///
+ /// Initializes this using the path in the provided buffer. If the path begins with two
+ /// forward slashes (//), those two forward slashes will be replaced with two backslashes (\\).
+ ///
+ /// This function will always set the IsNormalized flag to .
+ /// The buffer containing the path to use.
+ /// : The operation was successful.
public Result InitializeWithReplaceForwardSlashes(ReadOnlySpan path)
{
Result rc = InitializeImpl(path, StringUtils.GetLength(path));
@@ -418,6 +587,18 @@ namespace LibHac.Fs
return Result.Success;
}
+ ///
+ /// Initializes this using the path in the provided buffer
+ /// and makes various UNC path-related replacements.
+ ///
+ /// The following replacements are made:
+ /// :/// located anywhere in the path is replaced with :/\\
+ /// @Host:// located at the beginning of the path is replaced with @Host:\\
+ /// // located at the beginning of the path is replaced with \\
+ /// This function does not modify the IsNormalized flag.
+ ///
+ /// The buffer containing the path to use.
+ /// : The operation was successful.
public Result InitializeWithReplaceUnc(ReadOnlySpan path)
{
Result rc = InitializeImpl(path, StringUtils.GetLength(path));
@@ -455,6 +636,11 @@ namespace LibHac.Fs
return Result.Success;
}
+ ///
+ /// Initializes the as an empty string.
+ ///
+ /// This function will always set the IsNormalized flag to .
+ /// : The operation was successful.
public Result InitializeAsEmpty()
{
ClearBuffer();
@@ -463,6 +649,15 @@ namespace LibHac.Fs
return Result.Success;
}
+ ///
+ /// Updates this by prepending to the current path.
+ ///
+ /// This function does not modify the IsNormalized flag.
+ /// If is not normalized, this can lead to a situation where the resulting
+ /// path is not normalized yet the IsNormalized flag is still .
+ /// The buffer containing the path to insert.
+ /// : The operation was successful.
+ /// : The path provided in is a Windows path.
public Result InsertParent(ReadOnlySpan parent)
{
if (parent.Length == 0 || parent[0] == NullTerminator)
@@ -566,11 +761,28 @@ namespace LibHac.Fs
}
}
+ ///
+ /// Updates this by prepending to the current path.
+ ///
+ /// This function does not modify the IsNormalized flag.
+ /// If is not normalized, this can lead to a situation where the resulting
+ /// path is not normalized yet the IsNormalized flag is still .
+ /// The to insert.
+ /// : The operation was successful.
+ /// : The path provided in is a Windows path.
public Result InsertParent(in Path parent)
{
return InsertParent(parent.GetString());
}
+ ///
+ /// Updates this by appending to the current path.
+ ///
+ /// This function does not modify the IsNormalized flag.
+ /// If is not normalized, this can lead to a situation where the resulting
+ /// path is not normalized yet the IsNormalized flag is still .
+ /// The buffer containing the child path to append to the current path.
+ /// : The operation was successful.
public Result AppendChild(ReadOnlySpan child)
{
ReadOnlySpan trimmedChild = child;
@@ -643,11 +855,30 @@ namespace LibHac.Fs
}
}
+ ///
+ /// Updates this by appending to the current path.
+ ///
+ /// This function does not modify the IsNormalized flag.
+ /// If is not normalized, this can lead to a situation where the resulting
+ /// path is not normalized yet the IsNormalized flag is still .
+ /// The child to append to the current path.
+ /// : The operation was successful.
public Result AppendChild(in Path child)
{
return AppendChild(child.GetString());
}
+ ///
+ /// Combines 2 s into a single path.
+ ///
+ /// If is empty, this 's IsNormalized flag will
+ /// be set to the value of 's flag.
+ /// Otherwise the flag will be set to the value of 's flag.
+ /// The first path to combine.
+ /// The second path to combine.
+ /// : The operation was successful.
+ /// : The IsNormalized flag of either
+ /// or is not .
public Result Combine(in Path path1, in Path path2)
{
int path1Length = path1.GetLength();
@@ -656,23 +887,30 @@ namespace LibHac.Fs
Result rc = Preallocate(path1Length + SeparatorLength + path2Length + NullTerminatorLength);
if (rc.IsFailure()) return rc;
- rc = Initialize(path1);
+ rc = Initialize(in path1);
if (rc.IsFailure()) return rc;
if (IsEmpty())
{
- rc = Initialize(path2);
+ rc = Initialize(in path2);
if (rc.IsFailure()) return rc;
}
else
{
- rc = AppendChild(path2);
+ rc = AppendChild(in path2);
if (rc.IsFailure()) return rc;
}
return Result.Success;
}
+ ///
+ /// Removes the last entry from this .
+ ///
+ /// This function does not modify the IsNormalized flag.
+ /// : The operation was successful.
+ /// : The path before calling this function was
+ /// one of ".", "..", "/" or "\".
public Result RemoveChild()
{
// Make sure the Path has a buffer that we can write to.
@@ -742,6 +980,15 @@ namespace LibHac.Fs
return Result.Success;
}
+ ///
+ /// Normalizes the current path according to the provided .
+ ///
+ /// If this 's IsNormalized flag is set, this function does nothing.
+ /// The IsNormalized flag will be set if this function returns successfully.
+ /// Flags that specify what types of paths are allowed.
+ /// : The operation was successful.
+ /// : The path contains an invalid character.
+ /// : The path is in an invalid format for the specified .
public Result Normalize(PathFlags flags)
{
if (_isNormalized)