diff --git a/src/LibHac/IO/FileBase.cs b/src/LibHac/IO/FileBase.cs
index ab280fd5..b51260c3 100644
--- a/src/LibHac/IO/FileBase.cs
+++ b/src/LibHac/IO/FileBase.cs
@@ -79,6 +79,9 @@ namespace LibHac.IO
}
}
+ ///
+ /// Specifies which operations are available on an .
+ ///
[Flags]
public enum OpenMode
{
diff --git a/src/LibHac/IO/IDirectory.cs b/src/LibHac/IO/IDirectory.cs
index b9a82673..a22c0ea1 100644
--- a/src/LibHac/IO/IDirectory.cs
+++ b/src/LibHac/IO/IDirectory.cs
@@ -2,13 +2,38 @@
namespace LibHac.IO
{
+ ///
+ /// Provides an interface for enumerating the child entries of a directory.
+ ///
public interface IDirectory
{
+ ///
+ /// The that contains the current .
+ ///
IFileSystem ParentFileSystem { get; }
+
+ ///
+ /// The full path of the current in its .
+ ///
string FullPath { get; }
+
+ ///
+ /// Specifies which types of entries will be enumerated when is called.
+ ///
OpenDirectoryMode Mode { get; }
+ ///
+ /// Returns an enumerable collection the file system entries of the types specified by
+ /// that this directory contains. Does not search subdirectories.
+ ///
+ /// An enumerable collection of file system entries in this directory.
IEnumerable Read();
+
+ ///
+ /// Returns the number of file system entries of the types specified by
+ /// that this directory contains. Does not search subdirectories.
+ ///
+ /// The number of child entries the directory contains.
int GetEntryCount();
}
}
\ No newline at end of file
diff --git a/src/LibHac/IO/IFile.cs b/src/LibHac/IO/IFile.cs
index c6e130dd..4367479f 100644
--- a/src/LibHac/IO/IFile.cs
+++ b/src/LibHac/IO/IFile.cs
@@ -2,13 +2,65 @@
namespace LibHac.IO
{
+ ///
+ /// Provides an interface for reading and writing a sequence of bytes.
+ ///
+ /// is similar to , and has a few main differences:
+ ///
+ /// - allows an to be set that controls read, write
+ /// and append permissions for the file.
+ ///
+ /// - If the cannot read or write as many bytes as requested, it will read
+ /// or write as many bytes as it can and return that number of bytes to the caller.
+ ///
+ /// - If is called on an offset past the end of the ,
+ /// the mode is set and the file supports expansion,
+ /// the file will be expanded so that it is large enough to contain the written data.
public interface IFile : IDisposable
{
+ ///
+ /// The permissions mode for the current file.
+ ///
OpenMode Mode { get; }
+
+ ///
+ /// Reads a sequence of bytes from the current .
+ ///
+ /// The buffer where the read bytes will be stored.
+ /// The number of bytes read will be no larger than the length of the buffer.
+ /// The offset in the at which to begin reading.
+ /// The total number of bytes read into the buffer. This can be less than the
+ /// size of the buffer if the IFile is too short to fulfill the request.
+ /// is invalid.
+ /// The file's does not allow reading.
int Read(Span destination, long offset);
+
+ ///
+ /// Writes a sequence of bytes to the current .
+ ///
+ /// The buffer containing the bytes to be written.
+ /// The offset in the at which to begin writing.
+ /// is negative.
+ /// The file's does not allow this request.
void Write(ReadOnlySpan source, long offset);
+
+ ///
+ /// Causes any buffered data to be written to the underlying device.
+ ///
void Flush();
+
+ ///
+ /// Gets the number of bytes in the file.
+ ///
+ /// The length of the file in bytes.
long GetSize();
+
+ ///
+ /// Sets the size of the file in bytes.
+ ///
+ /// The desired size of the file in bytes.
+ /// If increasing the file size, The file's
+ /// does not allow this appending.
void SetSize(long size);
}
}
\ No newline at end of file
diff --git a/src/LibHac/IO/IFileSystem.cs b/src/LibHac/IO/IFileSystem.cs
index bfedc57c..09879fd6 100644
--- a/src/LibHac/IO/IFileSystem.cs
+++ b/src/LibHac/IO/IFileSystem.cs
@@ -1,23 +1,116 @@
using System;
+using System.IO;
namespace LibHac.IO
{
+ ///
+ /// Provides an interface for accessing a file system. / is used as the path delimiter.
+ ///
public interface IFileSystem
{
+ ///
+ /// Creates all directories and subdirectories in the specified path unless they already exist.
+ ///
+ /// The full path of the directory to create.
+ /// An I/O error occurred while creating the directory.
void CreateDirectory(string path);
+
+ ///
+ /// Creates or overwrites a file at the specified path.
+ ///
+ /// The full path of the file to create.
+ /// The initial size of the created file.
+ /// Flags to control how the file is created.
+ /// Should usually be
+ /// An I/O error occurred while creating the file.
void CreateFile(string path, long size, CreateFileOptions options);
+
+ ///
+ /// Deletes the specified directory.
+ ///
+ /// The full path of the directory to delete.
+ /// The specified directory does not exist.
+ /// An I/O error occurred while deleting the directory.
void DeleteDirectory(string path);
+
+ ///
+ /// Deletes the specified file.
+ ///
+ /// The full path of the file to delete.
+ /// The specified file does not exist.
+ /// An I/O error occurred while deleting the file.
void DeleteFile(string path);
+
+ ///
+ /// Creates an instance for enumerating the specified directory.
+ ///
+ /// The directory's full path.
+ /// Specifies which sub-entries should be enumerated.
+ /// An instance for the specified directory.
+ /// The specified directory does not exist.
+ /// An I/O error occurred while opening the directory.
IDirectory OpenDirectory(string path, OpenDirectoryMode mode);
+
+ ///
+ /// Opens an instance for the specified path.
+ ///
+ /// The full path of the file to open.
+ /// Specifies the access permissions of the created .
+ /// An instance for the specified path.
+ /// The specified file does not exist.
+ /// An I/O error occurred while deleting the file.
IFile OpenFile(string path, OpenMode mode);
+
+ ///
+ /// Renames or moves a directory to a new location.
+ ///
+ /// The full path of the directory to rename.
+ /// The new full path of the directory.
+ /// The specified directory does not exist.
+ /// An I/O error occurred while deleting the directory.
void RenameDirectory(string srcPath, string dstPath);
+
+ ///
+ /// Renames or moves a file to a new location.
+ ///
+ /// The full path of the file to rename.
+ /// The new full path of the file.
+ /// The specified file does not exist.
+ /// An I/O error occurred while deleting the file.
void RenameFile(string srcPath, string dstPath);
+
+ ///
+ /// Determines whether the specified directory exists.
+ ///
+ /// The full path of the directory to check.
+ /// if the directory exists, otherwise .
bool DirectoryExists(string path);
+
+ ///
+ /// Determines whether the specified file exists.
+ ///
+ /// The full path of the file to check.
+ /// if the file exists, otherwise .
bool FileExists(string path);
+
+ ///
+ /// Determines whether the specified path is a file or directory.
+ ///
+ /// The full path to check.
+ /// The of the file.
+ /// The specified path does not exist.
DirectoryEntryType GetEntryType(string path);
+
+ ///
+ /// Commits any changes to a transactional file system.
+ /// Does nothing if called on a non-transactional file system.
+ ///
void Commit();
}
+ ///
+ /// Specifies which types of entries are returned when enumerating an .
+ ///
[Flags]
public enum OpenDirectoryMode
{
@@ -26,10 +119,16 @@ namespace LibHac.IO
All = Directories | Files
}
+ ///
+ /// Optional file creation flags.
+ ///
[Flags]
public enum CreateFileOptions
{
None = 0,
+ ///
+ /// On a , creates a concatenation file.
+ ///
CreateConcatenationFile = 1 << 0
}
}
\ No newline at end of file
diff --git a/src/LibHac/IO/IStorage.cs b/src/LibHac/IO/IStorage.cs
index be5c6d2f..7116b6b7 100644
--- a/src/LibHac/IO/IStorage.cs
+++ b/src/LibHac/IO/IStorage.cs
@@ -2,6 +2,9 @@
namespace LibHac.IO
{
+ ///
+ /// Provides an interface for reading and writing a sequence of bytes.
+ ///
public interface IStorage : IDisposable
{
///
@@ -9,14 +12,17 @@ namespace LibHac.IO
///
/// The buffer where the read bytes will be stored.
/// The number of bytes read will be equal to the length of the buffer.
- /// The offset in the to begin reading from.
+ /// The offset in the at which to begin reading.
+ /// Invalid offset or the IStorage contains fewer bytes than requested.
void Read(Span destination, long offset);
///
/// Writes a sequence of bytes to the current .
///
/// The buffer containing the bytes to be written.
- /// The offset in the to begin writing to.
+ /// The offset in the at which to begin writing.
+ /// Invalid offset or
+ /// is too large to be written to the IStorage.
void Write(ReadOnlySpan source, long offset);
///
diff --git a/src/LibHac/IO/LocalFileSystem.cs b/src/LibHac/IO/LocalFileSystem.cs
index 3eae17d0..0eabad86 100644
--- a/src/LibHac/IO/LocalFileSystem.cs
+++ b/src/LibHac/IO/LocalFileSystem.cs
@@ -1,5 +1,4 @@
-using System;
-using System.IO;
+using System.IO;
namespace LibHac.IO
{
@@ -145,9 +144,6 @@ namespace LibHac.IO
throw new FileNotFoundException(path);
}
- public void Commit()
- {
- throw new NotImplementedException();
- }
+ public void Commit() { }
}
}
diff --git a/src/LibHac/IO/PartitionFileSystem.cs b/src/LibHac/IO/PartitionFileSystem.cs
index b8792c56..9c79b141 100644
--- a/src/LibHac/IO/PartitionFileSystem.cs
+++ b/src/LibHac/IO/PartitionFileSystem.cs
@@ -81,7 +81,7 @@ namespace LibHac.IO
public void DeleteFile(string path) => throw new NotSupportedException();
public void RenameDirectory(string srcPath, string dstPath) => throw new NotSupportedException();
public void RenameFile(string srcPath, string dstPath) => throw new NotSupportedException();
- public void Commit() => throw new NotSupportedException();
+ public void Commit() { }
}
public enum PartitionFileSystemType
diff --git a/src/LibHac/IO/RomFs/HierarchicalRomFileTable.cs b/src/LibHac/IO/RomFs/HierarchicalRomFileTable.cs
index 94571480..d27c8e19 100644
--- a/src/LibHac/IO/RomFs/HierarchicalRomFileTable.cs
+++ b/src/LibHac/IO/RomFs/HierarchicalRomFileTable.cs
@@ -5,11 +5,34 @@ using System.Text;
namespace LibHac.IO.RomFs
{
+ ///
+ /// Represents the file table used by the RomFS format.
+ ///
+ ///
+ /// This file table stores the structure of the file tree in a RomFS.
+ /// Each file or directory entry is stored in the table using its full path as a key.
+ /// Once added, a file or directory is assigned an ID that can also be used to retrieve it.
+ /// Each file entry contains the size of the file and its offset in the RomFS.
+ /// Each directory entry contains the IDs for its first child file and first child directory.
+ ///
+ /// The table is represented by four byte arrays. Two of the arrays contain the hash buckets and
+ /// entries for the files, and the other two for the directories.
+ ///
+ /// Once all files have been added to the table, should be called
+ /// to optimize the size of the table.
+ ///
public class HierarchicalRomFileTable
{
private RomFsDictionary FileTable { get; }
private RomFsDictionary DirectoryTable { get; }
+ ///
+ /// Initializes a from an existing table.
+ ///
+ ///
+ ///
+ ///
+ ///
public HierarchicalRomFileTable(IStorage dirHashTable, IStorage dirEntryTable, IStorage fileHashTable,
IStorage fileEntryTable)
{
@@ -17,8 +40,18 @@ namespace LibHac.IO.RomFs
DirectoryTable = new RomFsDictionary(dirHashTable, dirEntryTable);
}
+ ///
+ /// Initializes a new that has the default initial capacity.
+ ///
public HierarchicalRomFileTable() : this(0, 0) { }
+ ///
+ /// Initializes a new that has the specified initial capacity.
+ ///
+ /// The initial number of directories that the
+ /// can contain.
+ /// The initial number of files that the
+ /// can contain.
public HierarchicalRomFileTable(int directoryCapacity, int fileCapacity)
{
FileTable = new RomFsDictionary(fileCapacity);
@@ -73,6 +106,13 @@ namespace LibHac.IO.RomFs
return false;
}
+ ///
+ /// Opens a directory for enumeration.
+ ///
+ /// The full path of the directory to open.
+ /// The initial position of the directory enumerator.
+ /// if the table contains a directory with the specified path;
+ /// otherwise, .
public bool TryOpenDirectory(string path, out FindPosition position)
{
FindDirectoryRecursive(GetUtf8Bytes(path), out RomEntryKey key);
@@ -87,6 +127,13 @@ namespace LibHac.IO.RomFs
return false;
}
+ ///
+ /// Opens a directory for enumeration.
+ ///
+ /// The ID of the directory to open.
+ /// When this method returns, contains the initial position of the directory enumerator.
+ /// if the table contains a directory with the specified path;
+ /// otherwise, .
public bool TryOpenDirectory(int directoryId, out FindPosition position)
{
if (DirectoryTable.TryGetValue(directoryId, out RomKeyValuePair keyValuePair))
@@ -99,6 +146,15 @@ namespace LibHac.IO.RomFs
return false;
}
+ ///
+ /// Returns the next file in a directory and updates the enumerator's position.
+ ///
+ /// The current position of the directory enumerator.
+ /// This position will be updated when the method returns.
+ /// When this method returns, contains the file's metadata.
+ /// When this method returns, contains the file's name (Not the full path).
+ /// if the next file was successfully returned.
+ /// if there are no more files to enumerate.
public bool FindNextFile(ref FindPosition position, out RomFileInfo info, out string name)
{
if (position.NextFile == -1)
@@ -117,6 +173,14 @@ namespace LibHac.IO.RomFs
return true;
}
+ ///
+ /// Returns the next child directory in a directory and updates the enumerator's position.
+ ///
+ /// The current position of the directory enumerator.
+ /// This position will be updated when the method returns.
+ /// When this method returns, contains the directory's name (Not the full path).
+ /// if the next directory was successfully returned.
+ /// if there are no more directories to enumerate.
public bool FindNextDirectory(ref FindPosition position, out string name)
{
if (position.NextDirectory == -1)
@@ -132,7 +196,13 @@ namespace LibHac.IO.RomFs
return true;
}
- public void CreateFile(string path, ref RomFileInfo fileInfo)
+ ///
+ /// Adds a file to the file table. If the file already exists
+ /// its will be updated.
+ ///
+ /// The full path of the file to be added.
+ /// The file information to be stored.
+ public void AddFile(string path, ref RomFileInfo fileInfo)
{
path = PathTools.Normalize(path);
ReadOnlySpan pathBytes = GetUtf8Bytes(path);
@@ -140,13 +210,25 @@ namespace LibHac.IO.RomFs
CreateFileRecursiveInternal(pathBytes, ref fileInfo);
}
- public void CreateDirectory(string path)
+ ///
+ /// Adds a directory to the file table. If the directory already exists,
+ /// no action is performed.
+ ///
+ /// The full path of the directory to be added.
+ public void AddDirectory(string path)
{
path = PathTools.Normalize(path);
CreateDirectoryRecursive(GetUtf8Bytes(path));
}
+ ///
+ /// Sets the capacity of this dictionary to what it would be if
+ /// it had been originally initialized with all its entries.
+ ///
+ /// This method can be used to minimize the memory overhead
+ /// once it is known that no new elements will be added.
+ ///
public void TrimExcess()
{
DirectoryTable.TrimExcess();
@@ -263,9 +345,9 @@ namespace LibHac.IO.RomFs
}
{
- ref FileRomEntry entry = ref FileTable.AddOrGet(ref key, out int offset, out _, out _);
- entry.NextSibling = -1;
+ ref FileRomEntry entry = ref FileTable.AddOrGet(ref key, out int offset, out bool alreadyExists, out _);
entry.Info = fileInfo;
+ if (alreadyExists) entry.NextSibling = -1;
ref DirectoryRomEntry parent = ref DirectoryTable.GetValueReference(prevOffset);
diff --git a/src/LibHac/IO/RomFs/RomFsBuilder.cs b/src/LibHac/IO/RomFs/RomFsBuilder.cs
index 3e8bfbe5..accb502c 100644
--- a/src/LibHac/IO/RomFs/RomFsBuilder.cs
+++ b/src/LibHac/IO/RomFs/RomFsBuilder.cs
@@ -5,6 +5,12 @@ using System.Linq;
namespace LibHac.IO.RomFs
{
+ ///
+ /// Builds a RomFS from a collection of files.
+ ///
+ /// A produces a view of a RomFS archive.
+ /// When doing so, it will create an instance that will
+ /// provide the RomFS data when read. Random seek is supported.
public class RomFsBuilder
{
private const int FileAlignment = 0x10;
@@ -15,8 +21,15 @@ namespace LibHac.IO.RomFs
private HierarchicalRomFileTable FileTable { get; } = new HierarchicalRomFileTable();
private long CurrentOffset { get; set; }
+ ///
+ /// Creates a new, empty
+ ///
public RomFsBuilder() { }
+ ///
+ /// Creates a new and populates it with all
+ /// the files in the specified .
+ ///
public RomFsBuilder(IFileSystem input)
{
foreach (DirectoryEntry file in input.EnumerateEntries().Where(x => x.Type == DirectoryEntryType.File)
@@ -26,6 +39,11 @@ namespace LibHac.IO.RomFs
}
}
+ ///
+ /// Adds a file to the RomFS.
+ ///
+ /// The full path in the RomFS
+ /// An of the file data to add.
public void AddFile(string path, IFile file)
{
var fileInfo = new RomFileInfo();
@@ -43,9 +61,15 @@ namespace LibHac.IO.RomFs
var padding = new NullStorage(CurrentOffset - newOffset);
Sources.Add(padding);
- FileTable.CreateFile(path, ref fileInfo);
+ FileTable.AddFile(path, ref fileInfo);
}
+ ///
+ /// Returns a view of a RomFS containing all the currently added files.
+ /// Additional files may be added and a new view produced without
+ /// invalidating previously built RomFS views.
+ ///
+ ///
public IStorage Build()
{
FileTable.TrimExcess();
diff --git a/src/LibHac/IO/RomFs/RomFsEntries.cs b/src/LibHac/IO/RomFs/RomFsEntries.cs
index f026fad8..9d97e495 100644
--- a/src/LibHac/IO/RomFs/RomFsEntries.cs
+++ b/src/LibHac/IO/RomFs/RomFsEntries.cs
@@ -46,10 +46,15 @@ namespace LibHac.IO.RomFs
public long Length;
}
+ ///
+ /// Represents the current position when enumerating a directory's contents.
+ ///
[StructLayout(LayoutKind.Sequential)]
public struct FindPosition
{
+ /// The ID of the next directory to be enumerated.
public int NextDirectory;
+ /// The ID of the next file to be enumerated.
public int NextFile;
}
}
diff --git a/src/LibHac/IO/RomFs/RomFsFileSystem.cs b/src/LibHac/IO/RomFs/RomFsFileSystem.cs
index 54d5598c..57f76a9e 100644
--- a/src/LibHac/IO/RomFs/RomFsFileSystem.cs
+++ b/src/LibHac/IO/RomFs/RomFsFileSystem.cs
@@ -34,10 +34,7 @@ namespace LibHac.IO.RomFs
throw new FileNotFoundException(path);
}
- public void Commit()
- {
- throw new NotSupportedException();
- }
+ public void Commit() { }
public IDirectory OpenDirectory(string path, OpenDirectoryMode mode)
{