Add some RomFS and IFileStorage documentation.

This commit is contained in:
Alex Barney 2019-02-04 19:24:06 -06:00
parent f8e724fe0b
commit 29d1cd110f
11 changed files with 307 additions and 18 deletions

View file

@ -79,6 +79,9 @@ namespace LibHac.IO
} }
} }
/// <summary>
/// Specifies which operations are available on an <see cref="IFile"/>.
/// </summary>
[Flags] [Flags]
public enum OpenMode public enum OpenMode
{ {

View file

@ -2,13 +2,38 @@
namespace LibHac.IO namespace LibHac.IO
{ {
/// <summary>
/// Provides an interface for enumerating the child entries of a directory.
/// </summary>
public interface IDirectory public interface IDirectory
{ {
/// <summary>
/// The <see cref="IFileSystem"/> that contains the current <see cref="IDirectory"/>.
/// </summary>
IFileSystem ParentFileSystem { get; } IFileSystem ParentFileSystem { get; }
/// <summary>
/// The full path of the current <see cref="IDirectory"/> in its <see cref="ParentFileSystem"/>.
/// </summary>
string FullPath { get; } string FullPath { get; }
/// <summary>
/// Specifies which types of entries will be enumerated when <see cref="Read"/> is called.
/// </summary>
OpenDirectoryMode Mode { get; } OpenDirectoryMode Mode { get; }
/// <summary>
/// Returns an enumerable collection the file system entries of the types specified by
/// <see cref="Mode"/> that this directory contains. Does not search subdirectories.
/// </summary>
/// <returns>An enumerable collection of file system entries in this directory.</returns>
IEnumerable<DirectoryEntry> Read(); IEnumerable<DirectoryEntry> Read();
/// <summary>
/// Returns the number of file system entries of the types specified by
/// <see cref="Mode"/> that this directory contains. Does not search subdirectories.
/// </summary>
/// <returns>The number of child entries the directory contains.</returns>
int GetEntryCount(); int GetEntryCount();
} }
} }

View file

@ -2,13 +2,65 @@
namespace LibHac.IO namespace LibHac.IO
{ {
/// <summary>
/// Provides an interface for reading and writing a sequence of bytes.
/// </summary>
/// <remarks><see cref="IFile"/> is similar to <see cref="IStorage"/>, and has a few main differences:
///
/// - <see cref="IFile"/> allows an <see cref="OpenMode"/> to be set that controls read, write
/// and append permissions for the file.
///
/// - If the <see cref="IFile"/> 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 <see cref="Write"/> is called on an offset past the end of the <see cref="IFile"/>,
/// the <see cref="OpenMode.Append"/> mode is set and the file supports expansion,
/// the file will be expanded so that it is large enough to contain the written data.</remarks>
public interface IFile : IDisposable public interface IFile : IDisposable
{ {
/// <summary>
/// The permissions mode for the current file.
/// </summary>
OpenMode Mode { get; } OpenMode Mode { get; }
/// <summary>
/// Reads a sequence of bytes from the current <see cref="IFile"/>.
/// </summary>
/// <param name="destination">The buffer where the read bytes will be stored.
/// The number of bytes read will be no larger than the length of the buffer.</param>
/// <param name="offset">The offset in the <see cref="IFile"/> at which to begin reading.</param>
/// <returns>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.</returns>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> is invalid.</exception>
/// <exception cref="NotSupportedException">The file's <see cref="OpenMode"/> does not allow reading.</exception>
int Read(Span<byte> destination, long offset); int Read(Span<byte> destination, long offset);
/// <summary>
/// Writes a sequence of bytes to the current <see cref="IFile"/>.
/// </summary>
/// <param name="source">The buffer containing the bytes to be written.</param>
/// <param name="offset">The offset in the <see cref="IStorage"/> at which to begin writing.</param>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> is negative.</exception>
/// <exception cref="NotSupportedException">The file's <see cref="OpenMode"/> does not allow this request.</exception>
void Write(ReadOnlySpan<byte> source, long offset); void Write(ReadOnlySpan<byte> source, long offset);
/// <summary>
/// Causes any buffered data to be written to the underlying device.
/// </summary>
void Flush(); void Flush();
/// <summary>
/// Gets the number of bytes in the file.
/// </summary>
/// <returns>The length of the file in bytes.</returns>
long GetSize(); long GetSize();
/// <summary>
/// Sets the size of the file in bytes.
/// </summary>
/// <param name="size">The desired size of the file in bytes.</param>
/// <exception cref="NotSupportedException">If increasing the file size, The file's
/// <see cref="OpenMode"/> does not allow this appending.</exception>
void SetSize(long size); void SetSize(long size);
} }
} }

View file

@ -1,23 +1,116 @@
using System; using System;
using System.IO;
namespace LibHac.IO namespace LibHac.IO
{ {
/// <summary>
/// Provides an interface for accessing a file system. <c>/</c> is used as the path delimiter.
/// </summary>
public interface IFileSystem public interface IFileSystem
{ {
/// <summary>
/// Creates all directories and subdirectories in the specified path unless they already exist.
/// </summary>
/// <param name="path">The full path of the directory to create.</param>
/// <exception cref="IOException">An I/O error occurred while creating the directory.</exception>
void CreateDirectory(string path); void CreateDirectory(string path);
/// <summary>
/// Creates or overwrites a file at the specified path.
/// </summary>
/// <param name="path">The full path of the file to create.</param>
/// <param name="size">The initial size of the created file.</param>
/// <param name="options">Flags to control how the file is created.
/// Should usually be <see cref="CreateFileOptions.None"/></param>
/// <exception cref="IOException">An I/O error occurred while creating the file.</exception>
void CreateFile(string path, long size, CreateFileOptions options); void CreateFile(string path, long size, CreateFileOptions options);
/// <summary>
/// Deletes the specified directory.
/// </summary>
/// <param name="path">The full path of the directory to delete.</param>
/// <exception cref="DirectoryNotFoundException">The specified directory does not exist.</exception>
/// <exception cref="IOException">An I/O error occurred while deleting the directory.</exception>
void DeleteDirectory(string path); void DeleteDirectory(string path);
/// <summary>
/// Deletes the specified file.
/// </summary>
/// <param name="path">The full path of the file to delete.</param>
/// <exception cref="FileNotFoundException">The specified file does not exist.</exception>
/// <exception cref="IOException">An I/O error occurred while deleting the file.</exception>
void DeleteFile(string path); void DeleteFile(string path);
/// <summary>
/// Creates an <see cref="IDirectory"/> instance for enumerating the specified directory.
/// </summary>
/// <param name="path">The directory's full path.</param>
/// <param name="mode">Specifies which sub-entries should be enumerated.</param>
/// <returns>An <see cref="IDirectory"/> instance for the specified directory.</returns>
/// <exception cref="DirectoryNotFoundException">The specified directory does not exist.</exception>
/// <exception cref="IOException">An I/O error occurred while opening the directory.</exception>
IDirectory OpenDirectory(string path, OpenDirectoryMode mode); IDirectory OpenDirectory(string path, OpenDirectoryMode mode);
/// <summary>
/// Opens an <see cref="IFile"/> instance for the specified path.
/// </summary>
/// <param name="path">The full path of the file to open.</param>
/// <param name="mode">Specifies the access permissions of the created <see cref="IFile"/>.</param>
/// <returns>An <see cref="IFile"/> instance for the specified path.</returns>
/// <exception cref="FileNotFoundException">The specified file does not exist.</exception>
/// <exception cref="IOException">An I/O error occurred while deleting the file.</exception>
IFile OpenFile(string path, OpenMode mode); IFile OpenFile(string path, OpenMode mode);
/// <summary>
/// Renames or moves a directory to a new location.
/// </summary>
/// <param name="srcPath">The full path of the directory to rename.</param>
/// <param name="dstPath">The new full path of the directory.</param>
/// <exception cref="DirectoryNotFoundException">The specified directory does not exist.</exception>
/// <exception cref="IOException">An I/O error occurred while deleting the directory.</exception>
void RenameDirectory(string srcPath, string dstPath); void RenameDirectory(string srcPath, string dstPath);
/// <summary>
/// Renames or moves a file to a new location.
/// </summary>
/// <param name="srcPath">The full path of the file to rename.</param>
/// <param name="dstPath">The new full path of the file.</param>
/// <exception cref="FileNotFoundException">The specified file does not exist.</exception>
/// <exception cref="IOException">An I/O error occurred while deleting the file.</exception>
void RenameFile(string srcPath, string dstPath); void RenameFile(string srcPath, string dstPath);
/// <summary>
/// Determines whether the specified directory exists.
/// </summary>
/// <param name="path">The full path of the directory to check.</param>
/// <returns><see langword="true"/> if the directory exists, otherwise <see langword="false"/>.</returns>
bool DirectoryExists(string path); bool DirectoryExists(string path);
/// <summary>
/// Determines whether the specified file exists.
/// </summary>
/// <param name="path">The full path of the file to check.</param>
/// <returns><see langword="true"/> if the file exists, otherwise <see langword="false"/>.</returns>
bool FileExists(string path); bool FileExists(string path);
/// <summary>
/// Determines whether the specified path is a file or directory.
/// </summary>
/// <param name="path">The full path to check.</param>
/// <returns>The <see cref="DirectoryEntryType"/> of the file.</returns>
/// <exception cref="FileNotFoundException">The specified path does not exist.</exception>
DirectoryEntryType GetEntryType(string path); DirectoryEntryType GetEntryType(string path);
/// <summary>
/// Commits any changes to a transactional file system.
/// Does nothing if called on a non-transactional file system.
/// </summary>
void Commit(); void Commit();
} }
/// <summary>
/// Specifies which types of entries are returned when enumerating an <see cref="IDirectory"/>.
/// </summary>
[Flags] [Flags]
public enum OpenDirectoryMode public enum OpenDirectoryMode
{ {
@ -26,10 +119,16 @@ namespace LibHac.IO
All = Directories | Files All = Directories | Files
} }
/// <summary>
/// Optional file creation flags.
/// </summary>
[Flags] [Flags]
public enum CreateFileOptions public enum CreateFileOptions
{ {
None = 0, None = 0,
/// <summary>
/// On a <see cref="ConcatenationFileSystem"/>, creates a concatenation file.
/// </summary>
CreateConcatenationFile = 1 << 0 CreateConcatenationFile = 1 << 0
} }
} }

View file

@ -2,6 +2,9 @@
namespace LibHac.IO namespace LibHac.IO
{ {
/// <summary>
/// Provides an interface for reading and writing a sequence of bytes.
/// </summary>
public interface IStorage : IDisposable public interface IStorage : IDisposable
{ {
/// <summary> /// <summary>
@ -9,14 +12,17 @@ namespace LibHac.IO
/// </summary> /// </summary>
/// <param name="destination">The buffer where the read bytes will be stored. /// <param name="destination">The buffer where the read bytes will be stored.
/// The number of bytes read will be equal to the length of the buffer.</param> /// The number of bytes read will be equal to the length of the buffer.</param>
/// <param name="offset">The offset in the <see cref="IStorage"/> to begin reading from.</param> /// <param name="offset">The offset in the <see cref="IStorage"/> at which to begin reading.</param>
/// <exception cref="ArgumentException">Invalid offset or the IStorage contains fewer bytes than requested. </exception>
void Read(Span<byte> destination, long offset); void Read(Span<byte> destination, long offset);
/// <summary> /// <summary>
/// Writes a sequence of bytes to the current <see cref="IStorage"/>. /// Writes a sequence of bytes to the current <see cref="IStorage"/>.
/// </summary> /// </summary>
/// <param name="source">The buffer containing the bytes to be written.</param> /// <param name="source">The buffer containing the bytes to be written.</param>
/// <param name="offset">The offset in the <see cref="IStorage"/> to begin writing to.</param> /// <param name="offset">The offset in the <see cref="IStorage"/> at which to begin writing.</param>
/// <exception cref="ArgumentException">Invalid offset or <paramref name="source"/>
/// is too large to be written to the IStorage. </exception>
void Write(ReadOnlySpan<byte> source, long offset); void Write(ReadOnlySpan<byte> source, long offset);
/// <summary> /// <summary>

View file

@ -1,5 +1,4 @@
using System; using System.IO;
using System.IO;
namespace LibHac.IO namespace LibHac.IO
{ {
@ -145,9 +144,6 @@ namespace LibHac.IO
throw new FileNotFoundException(path); throw new FileNotFoundException(path);
} }
public void Commit() public void Commit() { }
{
throw new NotImplementedException();
}
} }
} }

View file

@ -81,7 +81,7 @@ namespace LibHac.IO
public void DeleteFile(string path) => throw new NotSupportedException(); public void DeleteFile(string path) => throw new NotSupportedException();
public void RenameDirectory(string srcPath, string dstPath) => 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 RenameFile(string srcPath, string dstPath) => throw new NotSupportedException();
public void Commit() => throw new NotSupportedException(); public void Commit() { }
} }
public enum PartitionFileSystemType public enum PartitionFileSystemType

View file

@ -5,11 +5,34 @@ using System.Text;
namespace LibHac.IO.RomFs namespace LibHac.IO.RomFs
{ {
/// <summary>
/// Represents the file table used by the RomFS format.
/// </summary>
/// <remarks>
/// 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, <see cref="TrimExcess"/> should be called
/// to optimize the size of the table.
/// </remarks>
public class HierarchicalRomFileTable public class HierarchicalRomFileTable
{ {
private RomFsDictionary<FileRomEntry> FileTable { get; } private RomFsDictionary<FileRomEntry> FileTable { get; }
private RomFsDictionary<DirectoryRomEntry> DirectoryTable { get; } private RomFsDictionary<DirectoryRomEntry> DirectoryTable { get; }
/// <summary>
/// Initializes a <see cref="HierarchicalRomFileTable"/> from an existing table.
/// </summary>
/// <param name="dirHashTable"></param>
/// <param name="dirEntryTable"></param>
/// <param name="fileHashTable"></param>
/// <param name="fileEntryTable"></param>
public HierarchicalRomFileTable(IStorage dirHashTable, IStorage dirEntryTable, IStorage fileHashTable, public HierarchicalRomFileTable(IStorage dirHashTable, IStorage dirEntryTable, IStorage fileHashTable,
IStorage fileEntryTable) IStorage fileEntryTable)
{ {
@ -17,8 +40,18 @@ namespace LibHac.IO.RomFs
DirectoryTable = new RomFsDictionary<DirectoryRomEntry>(dirHashTable, dirEntryTable); DirectoryTable = new RomFsDictionary<DirectoryRomEntry>(dirHashTable, dirEntryTable);
} }
/// <summary>
/// Initializes a new <see cref="HierarchicalRomFileTable"/> that has the default initial capacity.
/// </summary>
public HierarchicalRomFileTable() : this(0, 0) { } public HierarchicalRomFileTable() : this(0, 0) { }
/// <summary>
/// Initializes a new <see cref="HierarchicalRomFileTable"/> that has the specified initial capacity.
/// </summary>
/// <param name="directoryCapacity">The initial number of directories that the
/// <see cref="HierarchicalRomFileTable"/> can contain.</param>
/// <param name="fileCapacity">The initial number of files that the
/// <see cref="HierarchicalRomFileTable"/> can contain.</param>
public HierarchicalRomFileTable(int directoryCapacity, int fileCapacity) public HierarchicalRomFileTable(int directoryCapacity, int fileCapacity)
{ {
FileTable = new RomFsDictionary<FileRomEntry>(fileCapacity); FileTable = new RomFsDictionary<FileRomEntry>(fileCapacity);
@ -73,6 +106,13 @@ namespace LibHac.IO.RomFs
return false; return false;
} }
/// <summary>
/// Opens a directory for enumeration.
/// </summary>
/// <param name="path">The full path of the directory to open.</param>
/// <param name="position">The initial position of the directory enumerator.</param>
/// <returns><see langword="true"/> if the table contains a directory with the specified path;
/// otherwise, <see langword="false"/>.</returns>
public bool TryOpenDirectory(string path, out FindPosition position) public bool TryOpenDirectory(string path, out FindPosition position)
{ {
FindDirectoryRecursive(GetUtf8Bytes(path), out RomEntryKey key); FindDirectoryRecursive(GetUtf8Bytes(path), out RomEntryKey key);
@ -87,6 +127,13 @@ namespace LibHac.IO.RomFs
return false; return false;
} }
/// <summary>
/// Opens a directory for enumeration.
/// </summary>
/// <param name="directoryId">The ID of the directory to open.</param>
/// <param name="position">When this method returns, contains the initial position of the directory enumerator.</param>
/// <returns><see langword="true"/> if the table contains a directory with the specified path;
/// otherwise, <see langword="false"/>.</returns>
public bool TryOpenDirectory(int directoryId, out FindPosition position) public bool TryOpenDirectory(int directoryId, out FindPosition position)
{ {
if (DirectoryTable.TryGetValue(directoryId, out RomKeyValuePair<DirectoryRomEntry> keyValuePair)) if (DirectoryTable.TryGetValue(directoryId, out RomKeyValuePair<DirectoryRomEntry> keyValuePair))
@ -99,6 +146,15 @@ namespace LibHac.IO.RomFs
return false; return false;
} }
/// <summary>
/// Returns the next file in a directory and updates the enumerator's position.
/// </summary>
/// <param name="position">The current position of the directory enumerator.
/// This position will be updated when the method returns.</param>
/// <param name="info">When this method returns, contains the file's metadata.</param>
/// <param name="name">When this method returns, contains the file's name (Not the full path).</param>
/// <returns><see langword="true"/> if the next file was successfully returned.
/// <see langword="false"/> if there are no more files to enumerate.</returns>
public bool FindNextFile(ref FindPosition position, out RomFileInfo info, out string name) public bool FindNextFile(ref FindPosition position, out RomFileInfo info, out string name)
{ {
if (position.NextFile == -1) if (position.NextFile == -1)
@ -117,6 +173,14 @@ namespace LibHac.IO.RomFs
return true; return true;
} }
/// <summary>
/// Returns the next child directory in a directory and updates the enumerator's position.
/// </summary>
/// <param name="position">The current position of the directory enumerator.
/// This position will be updated when the method returns.</param>
/// <param name="name">When this method returns, contains the directory's name (Not the full path).</param>
/// <returns><see langword="true"/> if the next directory was successfully returned.
/// <see langword="false"/> if there are no more directories to enumerate.</returns>
public bool FindNextDirectory(ref FindPosition position, out string name) public bool FindNextDirectory(ref FindPosition position, out string name)
{ {
if (position.NextDirectory == -1) if (position.NextDirectory == -1)
@ -132,7 +196,13 @@ namespace LibHac.IO.RomFs
return true; return true;
} }
public void CreateFile(string path, ref RomFileInfo fileInfo) /// <summary>
/// Adds a file to the file table. If the file already exists
/// its <see cref="RomFileInfo"/> will be updated.
/// </summary>
/// <param name="path">The full path of the file to be added.</param>
/// <param name="fileInfo">The file information to be stored.</param>
public void AddFile(string path, ref RomFileInfo fileInfo)
{ {
path = PathTools.Normalize(path); path = PathTools.Normalize(path);
ReadOnlySpan<byte> pathBytes = GetUtf8Bytes(path); ReadOnlySpan<byte> pathBytes = GetUtf8Bytes(path);
@ -140,13 +210,25 @@ namespace LibHac.IO.RomFs
CreateFileRecursiveInternal(pathBytes, ref fileInfo); CreateFileRecursiveInternal(pathBytes, ref fileInfo);
} }
public void CreateDirectory(string path) /// <summary>
/// Adds a directory to the file table. If the directory already exists,
/// no action is performed.
/// </summary>
/// <param name="path">The full path of the directory to be added.</param>
public void AddDirectory(string path)
{ {
path = PathTools.Normalize(path); path = PathTools.Normalize(path);
CreateDirectoryRecursive(GetUtf8Bytes(path)); CreateDirectoryRecursive(GetUtf8Bytes(path));
} }
/// <summary>
/// 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.
/// </summary>
public void TrimExcess() public void TrimExcess()
{ {
DirectoryTable.TrimExcess(); DirectoryTable.TrimExcess();
@ -263,9 +345,9 @@ namespace LibHac.IO.RomFs
} }
{ {
ref FileRomEntry entry = ref FileTable.AddOrGet(ref key, out int offset, out _, out _); ref FileRomEntry entry = ref FileTable.AddOrGet(ref key, out int offset, out bool alreadyExists, out _);
entry.NextSibling = -1;
entry.Info = fileInfo; entry.Info = fileInfo;
if (alreadyExists) entry.NextSibling = -1;
ref DirectoryRomEntry parent = ref DirectoryTable.GetValueReference(prevOffset); ref DirectoryRomEntry parent = ref DirectoryTable.GetValueReference(prevOffset);

View file

@ -5,6 +5,12 @@ using System.Linq;
namespace LibHac.IO.RomFs namespace LibHac.IO.RomFs
{ {
/// <summary>
/// Builds a RomFS from a collection of files.
/// </summary>
/// <remarks>A <see cref="RomFsBuilder"/> produces a view of a RomFS archive.
/// When doing so, it will create an <see cref="IStorage"/> instance that will
/// provide the RomFS data when read. Random seek is supported.</remarks>
public class RomFsBuilder public class RomFsBuilder
{ {
private const int FileAlignment = 0x10; private const int FileAlignment = 0x10;
@ -15,8 +21,15 @@ namespace LibHac.IO.RomFs
private HierarchicalRomFileTable FileTable { get; } = new HierarchicalRomFileTable(); private HierarchicalRomFileTable FileTable { get; } = new HierarchicalRomFileTable();
private long CurrentOffset { get; set; } private long CurrentOffset { get; set; }
/// <summary>
/// Creates a new, empty <see cref="RomFsBuilder"/>
/// </summary>
public RomFsBuilder() { } public RomFsBuilder() { }
/// <summary>
/// Creates a new <see cref="RomFsBuilder"/> and populates it with all
/// the files in the specified <see cref="IFileSystem"/>.
/// </summary>
public RomFsBuilder(IFileSystem input) public RomFsBuilder(IFileSystem input)
{ {
foreach (DirectoryEntry file in input.EnumerateEntries().Where(x => x.Type == DirectoryEntryType.File) foreach (DirectoryEntry file in input.EnumerateEntries().Where(x => x.Type == DirectoryEntryType.File)
@ -26,6 +39,11 @@ namespace LibHac.IO.RomFs
} }
} }
/// <summary>
/// Adds a file to the RomFS.
/// </summary>
/// <param name="path">The full path in the RomFS</param>
/// <param name="file">An <see cref="IFile"/> of the file data to add.</param>
public void AddFile(string path, IFile file) public void AddFile(string path, IFile file)
{ {
var fileInfo = new RomFileInfo(); var fileInfo = new RomFileInfo();
@ -43,9 +61,15 @@ namespace LibHac.IO.RomFs
var padding = new NullStorage(CurrentOffset - newOffset); var padding = new NullStorage(CurrentOffset - newOffset);
Sources.Add(padding); Sources.Add(padding);
FileTable.CreateFile(path, ref fileInfo); FileTable.AddFile(path, ref fileInfo);
} }
/// <summary>
/// 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.
/// </summary>
/// <returns></returns>
public IStorage Build() public IStorage Build()
{ {
FileTable.TrimExcess(); FileTable.TrimExcess();

View file

@ -46,10 +46,15 @@ namespace LibHac.IO.RomFs
public long Length; public long Length;
} }
/// <summary>
/// Represents the current position when enumerating a directory's contents.
/// </summary>
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct FindPosition public struct FindPosition
{ {
/// <summary>The ID of the next directory to be enumerated.</summary>
public int NextDirectory; public int NextDirectory;
/// <summary>The ID of the next file to be enumerated.</summary>
public int NextFile; public int NextFile;
} }
} }

View file

@ -34,10 +34,7 @@ namespace LibHac.IO.RomFs
throw new FileNotFoundException(path); throw new FileNotFoundException(path);
} }
public void Commit() public void Commit() { }
{
throw new NotSupportedException();
}
public IDirectory OpenDirectory(string path, OpenDirectoryMode mode) public IDirectory OpenDirectory(string path, OpenDirectoryMode mode)
{ {