Skeleton or partially implement RomFS-related classes

This commit is contained in:
Alex Barney 2024-07-25 22:13:49 -07:00
parent dc6f4fa489
commit 78d9847b6a
12 changed files with 1169 additions and 8 deletions

View file

@ -0,0 +1,298 @@
// ReSharper disable UnusedMember.Local UnassignedField.Local
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
using System;
using System.Runtime.InteropServices;
// ReSharper disable once CheckNamespace
namespace LibHac.Fs;
using Position = uint;
using RomDirectoryId = uint;
using RomFileId = uint;
public class HierarchicalRomFileTable : IDisposable
{
private const Position InvalidPosition = ~default(Position);
public struct FindPosition
{
public Position NextDirectory;
public Position NextFile;
}
public struct CacheContext
{
public Position ParentFirstFilePosition;
public Position ParentLastFilePosition;
public CacheContext()
{
ParentFirstFilePosition = InvalidPosition;
}
}
private ref struct EntryKey
{
public RomEntryKey Key;
public RomPathTool.RomEntryName Name;
public EntryKey()
{
Name = new RomPathTool.RomEntryName();
}
public readonly uint Hash()
{
uint hash = 123456789 ^ Key.Parent;
foreach (byte c in Name.GetPath())
{
hash = c ^ ((hash << 27) | (hash >> 5));
}
return hash;
}
}
private struct RomEntryKey
{
public Position Parent;
public readonly bool IsEqual(in RomEntryKey rhs, ReadOnlySpan<byte> lhsExtraKey, ReadOnlySpan<byte> rhsExtraKey)
{
if (Parent != rhs.Parent)
return false;
if (lhsExtraKey.Length != rhsExtraKey.Length)
return false;
return RomPathTool.IsEqualPath(lhsExtraKey, rhsExtraKey, lhsExtraKey.Length);
}
}
[StructLayout(LayoutKind.Sequential)]
private struct DirectoryRomEntry
{
public Position Next;
public Position Dir;
public Position File;
}
[StructLayout(LayoutKind.Sequential)]
private struct FileRomEntry
{
public Position Next;
public RomFileInfo Info;
}
// Todo: Add generic types for the key types once generics can use ref structs
private class EntryMapTable<TValue> : KeyValueRomStorageTemplate<RomEntryKey, TValue> where TValue : unmanaged
{
public Result Add(out Position outPosition, in EntryKey key, in TValue value)
{
return AddInternal(out outPosition, in key.Key, key.Hash(), key.Name.GetPath(), in value).Ret();
}
public Result Get(out Position outPosition, out TValue outValue, in EntryKey key)
{
return GetInternal(out outPosition, out outValue, in key.Key, key.Hash(), key.Name.GetPath()).Ret();
}
public new Result GetByPosition(out RomEntryKey outKey, out TValue outValue, Position position)
{
return base.GetByPosition(out outKey, out outValue, position).Ret();
}
public new Result GetByPosition(out RomEntryKey outKey, out TValue outValue, Span<byte> outExtraKey,
out int outExtraKeySize, Position position)
{
return base.GetByPosition(out outKey, out outValue, outExtraKey, out outExtraKeySize, position).Ret();
}
public new Result SetByPosition(Position position, in TValue value)
{
return base.SetByPosition(position, in value).Ret();
}
}
public HierarchicalRomFileTable()
{
throw new NotImplementedException();
}
public void Dispose()
{
throw new NotImplementedException();
}
public static long QueryDirectoryEntryBucketStorageSize(uint bucketCount)
{
return EntryMapTable<DirectoryRomEntry>.QueryBucketCount(bucketCount);
}
public static long QueryDirectoryEntrySize(uint extraKeySize)
{
return EntryMapTable<DirectoryRomEntry>.QueryEntrySize(extraKeySize);
}
public static long QueryFileEntryBucketStorageSize(uint bucketCount)
{
return EntryMapTable<FileRomEntry>.QueryBucketCount(bucketCount);
}
public static long QueryFileEntrySize(uint extraKeySize)
{
return EntryMapTable<FileRomEntry>.QueryEntrySize(extraKeySize);
}
public static Result Format(ref readonly ValueSubStorage directoryBucketStorage,
ref readonly ValueSubStorage fileBucketStorage)
{
throw new NotImplementedException();
}
public Result Initialize(ref readonly ValueSubStorage directoryBucketStorage,
ref readonly ValueSubStorage directoryEntryStorage, ref readonly ValueSubStorage fileBucketStorage,
ref readonly ValueSubStorage fileEntryStorage)
{
throw new NotImplementedException();
}
public void FinalizeObject()
{
throw new NotImplementedException();
}
public Result CreateRootDirectory()
{
throw new NotImplementedException();
}
public Result CreateDirectory(out RomDirectoryId outId, ReadOnlySpan<byte> fullPath)
{
throw new NotImplementedException();
}
public Result CreateFile(out RomFileId outId, ReadOnlySpan<byte> fullPath, in RomFileInfo info)
{
throw new NotImplementedException();
}
public Result CreateFile(out RomFileId outId, ReadOnlySpan<byte> fullPath, in RomFileInfo info,
ref CacheContext cacheContext)
{
throw new NotImplementedException();
}
public Result ConvertPathToDirectoryId(out RomDirectoryId outId, ReadOnlySpan<byte> fullPath)
{
throw new NotImplementedException();
}
public Result ConvertPathToFileId(out RomFileId outId, ReadOnlySpan<byte> fullPath)
{
throw new NotImplementedException();
}
public Result OpenFile(out RomFileInfo outInfo, ReadOnlySpan<byte> fullPath)
{
throw new NotImplementedException();
}
public Result OpenFile(out RomFileInfo outInfo, RomFileId id)
{
throw new NotImplementedException();
}
public Result FindOpen(out FindPosition outPosition, ReadOnlySpan<byte> fullPath)
{
throw new NotImplementedException();
}
public Result FindOpen(out FindPosition outPosition, RomDirectoryId id)
{
throw new NotImplementedException();
}
public Result FindNextDirectory(Span<byte> outName, ref FindPosition findPosition, int length)
{
throw new NotImplementedException();
}
public Result FindNextFile(Span<byte> outName, ref FindPosition findPosition, int length)
{
throw new NotImplementedException();
}
public Result QueryRomFileSystemSize(out long outDirectoryEntrySize, out long outFileEntrySize)
{
throw new NotImplementedException();
}
private Result GetParent(out Position outParentPosition, ref EntryKey outDirectoryKey,
ref DirectoryRomEntry outDirectoryEntry, Position position, ref RomPathTool.RomEntryName name,
ReadOnlySpan<byte> fullPath)
{
throw new NotImplementedException();
}
private Result FindParentDirectoryRecursive(out Position outParentPosition, ref EntryKey outDirectoryKey,
ref DirectoryRomEntry outDirectoryEntry, ref RomPathTool.PathParser parser, ReadOnlySpan<byte> fullPath)
{
throw new NotImplementedException();
}
private Result FindPathRecursive(ref EntryKey outKey, ref DirectoryRomEntry outParentDirectoryEntry,
bool isDirectory, ReadOnlySpan<byte> fullPath)
{
throw new NotImplementedException();
}
private Result FindDirectoryRecursive(ref EntryKey outKey, ref DirectoryRomEntry outParentDirectoryEntry,
ReadOnlySpan<byte> fullPath)
{
throw new NotImplementedException();
}
private Result FindFileRecursive(ref EntryKey outKey, ref DirectoryRomEntry outParentDirectoryEntry,
ReadOnlySpan<byte> fullPath)
{
throw new NotImplementedException();
}
private Result CheckSameEntryExists(in EntryKey key, Result resultIfExists)
{
throw new NotImplementedException();
}
private Result GetDirectoryEntry(out Position outPosition, out DirectoryRomEntry outEntry, in EntryKey key)
{
throw new NotImplementedException();
}
private Result GetDirectoryEntry(out DirectoryRomEntry outEntry, RomDirectoryId id)
{
throw new NotImplementedException();
}
private Result GetFileEntry(out Position outPosition, out FileRomEntry outEntry, in EntryKey key)
{
throw new NotImplementedException();
}
private Result GetFileEntry(out FileRomEntry outEntry, RomFileId id)
{
throw new NotImplementedException();
}
private Result OpenFile(out RomFileInfo outFileInfo, in EntryKey key)
{
throw new NotImplementedException();
}
private Result FindOpen(out FindPosition outPosition, in EntryKey key)
{
throw new NotImplementedException();
}
}

View file

@ -0,0 +1,164 @@
// ReSharper disable UnusedMember.Local NotAccessedField.Local
#pragma warning disable CS0169 // Field is never used
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Diag;
using LibHac.Util;
// ReSharper disable once CheckNamespace
namespace LibHac.Fs;
using Position = uint;
using StorageSizeType = uint;
public class KeyValueRomStorageTemplate<TKey, TValue> : IDisposable where TKey : unmanaged where TValue : unmanaged
{
private long _bucketCount;
private ValueSubStorage _bucketStorage;
private ValueSubStorage _entryStorage;
private long _totalEntrySize;
private uint _entryCount;
private const Position InvalidPosition = ~default(Position);
private struct StorageElement
{
public TKey Key;
public TValue Value;
public Position Next;
public StorageSizeType Size;
}
public KeyValueRomStorageTemplate()
{
throw new NotImplementedException();
}
public virtual void Dispose()
{
throw new NotImplementedException();
}
public static uint QueryBucketCount(long bucketStorageSize)
{
return (uint)(bucketStorageSize / Unsafe.SizeOf<Position>());
}
public static long QueryBucketCount(uint bucketCount)
{
return bucketCount * Unsafe.SizeOf<Position>();
}
public static long QueryEntrySize(uint extraSize)
{
// Todo: Use AlignOf<StorageElement> for the alignment when it's added to the language
return Alignment.AlignUp(Unsafe.SizeOf<StorageElement>() + extraSize, 4);
}
public static Result Format(ref ValueSubStorage bucketStorage, uint bucketCount)
{
Position pos = InvalidPosition;
for (int i = 0; i < bucketCount; i++)
{
Result res = bucketStorage.Write(i * Unsafe.SizeOf<Position>(), SpanHelpers.AsByteSpan(ref pos));
if (res.IsFailure()) return res.Miss();
}
return Result.Success;
}
public Result Initialize(ref readonly ValueSubStorage bucketStorage, long bucketCount,
ref readonly ValueSubStorage entryStorage)
{
throw new NotImplementedException();
}
public void FinalizeObject()
{
throw new NotImplementedException();
}
public long GetTotalEntrySize() => _totalEntrySize;
private long HashToBucket(uint hashKey) => hashKey % _bucketCount;
protected Result AddInternal(out Position outPosition, in TKey key, uint hashKey, ReadOnlySpan<byte> extraKey,
in TValue value)
{
throw new NotImplementedException();
}
protected Result GetInternal(out Position outPosition, out TValue outValue, in TKey key, uint hashKey,
ReadOnlySpan<byte> extraKey)
{
throw new NotImplementedException();
}
protected Result GetByPosition(out TKey outKey, out TValue outValue, Position position)
{
throw new NotImplementedException();
}
protected Result GetByPosition(out TKey outKey, out TValue outValue, Span<byte> outExtraKey,
out int outExtraKeySize, Position position)
{
throw new NotImplementedException();
}
protected Result SetByPosition(Position position, in TValue value)
{
Result res = ReadKeyValue(out StorageElement element, position);
if (res.IsFailure()) return res.Miss();
element.Value = value;
return WriteKeyValue(in element, position, default).Ret();
}
private Result FindInternal(out Position outPosition, out Position outPreviousPosition,
out StorageElement outStorageElement, in TKey key, uint hashKey, ReadOnlySpan<byte> extraKey)
{
throw new NotImplementedException();
}
private Result AllocateEntry(out Position outPosition, uint extraSize)
{
throw new NotImplementedException();
}
private Result LinkEntry(out Position outNextPosition, Position position, uint hashKey)
{
throw new NotImplementedException();
}
private Result ReadBucket(out Position outPosition, long index)
{
throw new NotImplementedException();
}
private Result WriteBucket(Position position, long index)
{
Assert.SdkRequiresLess(index, _bucketCount);
long offset = index * Unsafe.SizeOf<Position>();
return _bucketStorage.Write(offset, SpanHelpers.AsReadOnlyByteSpan(in position)).Ret();
}
private Result ReadKeyValue(out StorageElement outElement, Position position)
{
throw new NotImplementedException();
}
private Result ReadKeyValue(out StorageElement outElement, Span<byte> outExtraKey, out int outExtraKeySize,
Position position)
{
throw new NotImplementedException();
}
private Result WriteKeyValue(in StorageElement element, Position position, ReadOnlySpan<byte> extraKey)
{
throw new NotImplementedException();
}
}

View file

@ -0,0 +1,90 @@
using System;
// ReSharper disable once CheckNamespace
namespace LibHac.Fs;
public static class RomPathTool
{
public static bool IsEqualPath(ReadOnlySpan<byte> path1, ReadOnlySpan<byte> path2, int length)
{
throw new NotImplementedException();
}
public ref struct PathParser
{
public PathParser()
{
throw new NotImplementedException();
}
public Result Initialize(ReadOnlySpan<byte> fullPath)
{
throw new NotImplementedException();
}
public void FinalizeObject()
{
throw new NotImplementedException();
}
public readonly bool IsParseFinished()
{
throw new NotImplementedException();
}
public readonly bool IsDirectoryPath()
{
throw new NotImplementedException();
}
public Result GetNextDirectoryName(out RomEntryName outName)
{
throw new NotImplementedException();
}
public readonly Result GetAsDirectoryName(out RomEntryName outName)
{
throw new NotImplementedException();
}
public readonly Result GetAsFileName(out RomEntryName outName)
{
throw new NotImplementedException();
}
}
public ref struct RomEntryName
{
private ReadOnlySpan<byte> _path;
public RomEntryName()
{
_path = default;
}
public void Initialize(ReadOnlySpan<byte> path)
{
_path = path;
}
public readonly bool IsCurrentDirectory()
{
return _path.Length == 1 && _path[0] == (byte)'.';
}
public readonly bool IsParentDirectory()
{
return _path.Length == 2 && _path[0] == (byte)'.' && _path[1] == (byte)'.';
}
public readonly bool IsRootDirectory()
{
return _path.Length == 0;
}
public readonly ReadOnlySpan<byte> GetPath()
{
return _path;
}
}
}

View file

@ -0,0 +1,26 @@
using System.Runtime.InteropServices;
// ReSharper disable once CheckNamespace
namespace LibHac.Fs;
[StructLayout(LayoutKind.Sequential)]
public struct RomFileSystemInformation
{
public long HeaderSize;
public long DirectoryBucketOffset;
public long DirectoryBucketSize;
public long DirectoryEntryOffset;
public long DirectoryEntrySize;
public long FileBucketOffset;
public long FileBucketSize;
public long FileEntryOffset;
public long FileEntrySize;
public long DataOffset;
}
[StructLayout(LayoutKind.Sequential)]
public struct RomFileInfo
{
public Int64 Offset;
public Int64 Size;
}

View file

@ -0,0 +1,260 @@
// ReSharper disable UnusedMember.Local NotAccessedField.Local UnusedType.Local
#pragma warning disable CS0169 // Field is never used
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
using System;
using LibHac.Common;
using LibHac.Fs.Fsa;
namespace LibHac.Fs.Impl
{
public class RomFsFile : IFile
{
private RomFsFileSystem _parent;
private long _startOffset;
private long _emdOffset;
public RomFsFile(RomFsFileSystem parent, long startOffset, long emdOffset)
{
throw new NotImplementedException();
}
public long GetOffset()
{
throw new NotImplementedException();
}
public long GetSize()
{
throw new NotImplementedException();
}
public IStorage GetStorage()
{
throw new NotImplementedException();
}
protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination, in ReadOption option)
{
throw new NotImplementedException();
}
protected override Result DoGetSize(out long size)
{
throw new NotImplementedException();
}
protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size,
ReadOnlySpan<byte> inBuffer)
{
throw new NotImplementedException();
}
protected override Result DoWrite(long offset, ReadOnlySpan<byte> source, in WriteOption option)
{
throw new NotImplementedException();
}
protected override Result DoSetSize(long size)
{
throw new NotImplementedException();
}
protected override Result DoFlush()
{
throw new NotImplementedException();
}
}
}
namespace LibHac.Fs
{
file static class Anonymous
{
public static long CalculateRequiredWorkingMemorySize(in RomFileSystemInformation fsInfo)
{
return fsInfo.DirectoryBucketSize + fsInfo.DirectoryEntrySize + fsInfo.FileBucketSize + fsInfo.FileEntrySize;
}
public static Result ReadFile(IStorage storage, long offset, Span<byte> buffer)
{
throw new NotImplementedException();
}
public static Result ReadFileHeader(IStorage storage, out RomFileSystemInformation outHeader)
{
throw new NotImplementedException();
}
}
file class RomFsDirectory : IDirectory
{
private RomFsFileSystem _parent;
private HierarchicalRomFileTable.FindPosition _currentPosition;
private HierarchicalRomFileTable.FindPosition _initialPosition;
private OpenDirectoryMode _mode;
public RomFsDirectory(RomFsFileSystem parent, in HierarchicalRomFileTable.FindPosition initialFindPosition,
OpenDirectoryMode mode)
{
throw new NotImplementedException();
}
public override void Dispose()
{
throw new NotImplementedException();
}
protected override Result DoRead(out long entriesRead, Span<DirectoryEntry> entryBuffer)
{
throw new NotImplementedException();
}
protected override Result DoGetEntryCount(out long entryCount)
{
throw new NotImplementedException();
}
private Result ReadInternal(out long outReadCount, ref HierarchicalRomFileTable.FindPosition findPosition,
Span<DirectoryEntry> entryBuffer)
{
throw new NotImplementedException();
}
}
public class RomFsFileSystem : IFileSystem
{
private HierarchicalRomFileTable _romFileTable;
private IStorage _baseStorage;
private UniqueRef<IStorage> _uniqueBaseStorage;
private UniqueRef<IStorage> _directoryBucketStorage;
private UniqueRef<IStorage> _directoryEntryStorage;
private UniqueRef<IStorage> _fileBucketStorage;
private UniqueRef<IStorage> _fileEntryStorage;
private long _entrySize;
public RomFsFileSystem()
{
throw new NotImplementedException();
}
public override void Dispose()
{
throw new NotImplementedException();
}
public IStorage GetBaseStorage() => _baseStorage;
public HierarchicalRomFileTable GetRomFileTable() => _romFileTable;
public static Result GetRequiredWorkingMemorySize(out long outValue, IStorage storage)
{
throw new NotImplementedException();
}
public Result Initialize(ref UniqueRef<IStorage> baseStorage, Memory<byte> workingMemory,
bool isFileSystemCacheUsed)
{
throw new NotImplementedException();
}
public Result Initialize(IStorage baseStorage, Memory<byte> workingMemory, bool isFileSystemCacheUsed)
{
throw new NotImplementedException();
}
protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, ref readonly Path path, OpenMode mode)
{
throw new NotImplementedException();
}
protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, ref readonly Path path, OpenDirectoryMode mode)
{
throw new NotImplementedException();
}
protected override Result DoGetEntryType(out DirectoryEntryType entryType, ref readonly Path path)
{
throw new NotImplementedException();
}
protected override Result DoCreateFile(ref readonly Path path, long size, CreateFileOptions option)
{
throw new NotImplementedException();
}
protected override Result DoDeleteFile(ref readonly Path path)
{
throw new NotImplementedException();
}
protected override Result DoCreateDirectory(ref readonly Path path)
{
throw new NotImplementedException();
}
protected override Result DoDeleteDirectory(ref readonly Path path)
{
throw new NotImplementedException();
}
protected override Result DoDeleteDirectoryRecursively(ref readonly Path path)
{
throw new NotImplementedException();
}
protected override Result DoCleanDirectoryRecursively(ref readonly Path path)
{
throw new NotImplementedException();
}
protected override Result DoRenameFile(ref readonly Path currentPath, ref readonly Path newPath)
{
throw new NotImplementedException();
}
protected override Result DoRenameDirectory(ref readonly Path currentPath, ref readonly Path newPath)
{
throw new NotImplementedException();
}
protected override Result DoCommit()
{
throw new NotImplementedException();
}
protected override Result DoCommitProvisionally(long counter)
{
throw new NotImplementedException();
}
protected override Result DoRollback()
{
throw new NotImplementedException();
}
protected override Result DoGetFreeSpaceSize(out long freeSpace, ref readonly Path path)
{
throw new NotImplementedException();
}
protected override Result DoGetTotalSpaceSize(out long totalSpace, ref readonly Path path)
{
throw new NotImplementedException();
}
protected override Result DoGetFileSystemAttribute(out FileSystemAttribute outAttribute)
{
throw new NotImplementedException();
}
public Result GetFileBaseOffset(out long outOffset, ReadOnlySpan<byte> path)
{
throw new NotImplementedException();
}
private Result GetFileInfo(out RomFileInfo outInfo, ReadOnlySpan<byte> path)
{
throw new NotImplementedException();
}
}
}

View file

@ -36,7 +36,7 @@ public class DefaultFsServerObjects
IBufferManager bufferManager = null; IBufferManager bufferManager = null;
IHash256GeneratorFactorySelector ncaHashGeneratorFactorySelector = null; IHash256GeneratorFactorySelector ncaHashGeneratorFactorySelector = null;
creators.RomFileSystemCreator = new RomFileSystemCreator(); creators.RomFileSystemCreator = new RomFileSystemCreator(memoryResource);
creators.PartitionFileSystemCreator = new PartitionFileSystemCreator(); creators.PartitionFileSystemCreator = new PartitionFileSystemCreator();
creators.StorageOnNcaCreator = new StorageOnNcaCreator(memoryResource, bufferManager, InitializeNcaReader, new NcaCompressionConfiguration(), ncaHashGeneratorFactorySelector); creators.StorageOnNcaCreator = new StorageOnNcaCreator(memoryResource, bufferManager, InitializeNcaReader, new NcaCompressionConfiguration(), ncaHashGeneratorFactorySelector);
creators.TargetManagerFileSystemCreator = new TargetManagerFileSystemCreator(); creators.TargetManagerFileSystemCreator = new TargetManagerFileSystemCreator();

View file

@ -1,10 +1,11 @@
using LibHac.Common; using System;
using LibHac.Common;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
namespace LibHac.FsSrv.FsCreator; namespace LibHac.FsSrv.FsCreator;
public interface IRomFileSystemCreator public interface IRomFileSystemCreator : IDisposable
{ {
Result Create(ref SharedRef<IFileSystem> outFileSystem, ref readonly SharedRef<IStorage> romFsStorage); Result Create(ref SharedRef<IFileSystem> outFileSystem, ref readonly SharedRef<IStorage> romFsStorage);
} }

View file

@ -1,16 +1,77 @@
using LibHac.Common; using LibHac.Common;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
using LibHac.Tools.FsSystem.RomFs; using Buffer = LibHac.Mem.Buffer;
using RomFsFileSystem = LibHac.FsSystem.RomFsFileSystem;
namespace LibHac.FsSrv.FsCreator; namespace LibHac.FsSrv.FsCreator;
/// <summary>
/// Extends <see cref="RomFsFileSystem"/> by allocating a cache buffer that is then deallocated upon disposal.
/// </summary>
/// <remarks>Based on nnSdk 18.3.0 (FS 18.0.0)</remarks>
file class RomFileSystemWithBuffer : RomFsFileSystem
{
private const int MaxBufferSize = 1024 * 128;
private Buffer _metaCacheBuffer;
private MemoryResource _allocator;
public RomFileSystemWithBuffer(MemoryResource allocator)
{
_allocator = allocator;
}
public override void Dispose()
{
if (!_metaCacheBuffer.IsNull)
_allocator.Deallocate(ref _metaCacheBuffer);
base.Dispose();
}
public Result Initialize(ref readonly SharedRef<IStorage> baseStorage)
{
Result res = GetRequiredWorkingMemorySize(out long bufferSize, baseStorage.Get);
if (res.IsFailure() || bufferSize == 0 || bufferSize >= MaxBufferSize)
{
return Initialize(in baseStorage, Buffer.Empty, useCache: false).Ret();
}
_metaCacheBuffer = _allocator.Allocate(bufferSize);
if (_metaCacheBuffer.IsNull)
{
return Initialize(in baseStorage, Buffer.Empty, useCache: false).Ret();
}
return Initialize(in baseStorage, _metaCacheBuffer, useCache: true).Ret();
}
}
/// <summary>
/// Takes a <see cref="IStorage"/> containing a RomFs and opens it as an <see cref="IFileSystem"/>
/// </summary>
/// <remarks>Based on nnSdk 18.3.0 (FS 18.0.0)</remarks>
public class RomFileSystemCreator : IRomFileSystemCreator public class RomFileSystemCreator : IRomFileSystemCreator
{ {
// todo: Implement properly private MemoryResource _allocator;
public RomFileSystemCreator(MemoryResource allocator)
{
_allocator = allocator;
}
public void Dispose() { }
public Result Create(ref SharedRef<IFileSystem> outFileSystem, ref readonly SharedRef<IStorage> romFsStorage) public Result Create(ref SharedRef<IFileSystem> outFileSystem, ref readonly SharedRef<IStorage> romFsStorage)
{ {
outFileSystem.Reset(new RomFsFileSystem(in romFsStorage)); using var fs = new SharedRef<RomFileSystemWithBuffer>(new RomFileSystemWithBuffer(_allocator));
if (!fs.HasValue) return ResultFs.AllocationMemoryFailedInRomFileSystemCreatorA.Log();
Result res = fs.Get.Initialize(in romFsStorage);
if (res.IsFailure()) return res.Miss();
outFileSystem.SetByMove(ref fs.Ref);
return Result.Success; return Result.Success;
} }
} }

View file

@ -0,0 +1,229 @@
// ReSharper disable UnusedMember.Local NotAccessedField.Local UnusedType.Local
#pragma warning disable CS0169 // Field is never used
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
using System;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using Buffer = LibHac.Mem.Buffer;
namespace LibHac.FsSystem;
file static class Anonymous
{
public static long CalculateRequiredWorkingMemorySize(in RomFileSystemInformation fsInfo)
{
return fsInfo.DirectoryBucketSize + fsInfo.DirectoryEntrySize + fsInfo.FileBucketSize + fsInfo.FileEntrySize;
}
}
file class RomFsFile : IFile
{
private RomFsFileSystem _parent;
private long _startOffset;
private long _emdOffset;
public RomFsFile(RomFsFileSystem parent, long startOffset, long emdOffset)
{
throw new NotImplementedException();
}
private long GetSize() => _emdOffset - _startOffset;
protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination, in ReadOption option)
{
throw new NotImplementedException();
}
protected override Result DoGetSize(out long size)
{
throw new NotImplementedException();
}
protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size,
ReadOnlySpan<byte> inBuffer)
{
throw new NotImplementedException();
}
protected override Result DoWrite(long offset, ReadOnlySpan<byte> source, in WriteOption option)
{
throw new NotImplementedException();
}
protected override Result DoSetSize(long size)
{
throw new NotImplementedException();
}
protected override Result DoFlush()
{
throw new NotImplementedException();
}
}
file class RomFsDirectory : IDirectory
{
private RomFsFileSystem _parent;
private HierarchicalRomFileTable.FindPosition _currentPosition;
private HierarchicalRomFileTable.FindPosition _initialPosition;
private OpenDirectoryMode _mode;
public RomFsDirectory(RomFsFileSystem parent, in HierarchicalRomFileTable.FindPosition initialFindPosition,
OpenDirectoryMode mode)
{
throw new NotImplementedException();
}
public override void Dispose()
{
throw new NotImplementedException();
}
protected override Result DoRead(out long entriesRead, Span<DirectoryEntry> entryBuffer)
{
throw new NotImplementedException();
}
protected override Result DoGetEntryCount(out long entryCount)
{
throw new NotImplementedException();
}
private Result ReadInternal(out long outReadCount, ref HierarchicalRomFileTable.FindPosition findPosition,
Span<DirectoryEntry> entryBuffer)
{
throw new NotImplementedException();
}
}
public class RomFsFileSystem : IFileSystem
{
private HierarchicalRomFileTable _romFileTable;
private IStorage _baseStorage;
private SharedRef<IStorage> _sharedBaseStorage;
private UniqueRef<IStorage> _directoryBucketStorage;
private UniqueRef<IStorage> _directoryEntryStorage;
private UniqueRef<IStorage> _fileBucketStorage;
private UniqueRef<IStorage> _fileEntryStorage;
private long _entrySize;
public RomFsFileSystem()
{
throw new NotImplementedException();
}
public override void Dispose()
{
throw new NotImplementedException();
}
public IStorage GetBaseFile() => _baseStorage;
public HierarchicalRomFileTable GetRomFileTable() => _romFileTable;
public static Result GetRequiredWorkingMemorySize(out long outValue, IStorage storage)
{
throw new NotImplementedException();
}
public Result Initialize(ref readonly SharedRef<IStorage> baseStorage, Buffer workingMemory, bool useCache)
{
throw new NotImplementedException();
}
public Result Initialize(IStorage baseStorage, Buffer workingMemory, bool useCache)
{
throw new NotImplementedException();
}
private Result CheckPathFormat(ref readonly Path path)
{
throw new NotImplementedException();
}
public Result GetFileBaseOffset(out long outOffset, ref readonly Path path)
{
throw new NotImplementedException();
}
protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, ref readonly Path path, OpenMode mode)
{
throw new NotImplementedException();
}
protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, ref readonly Path path,
OpenDirectoryMode mode)
{
throw new NotImplementedException();
}
protected override Result DoGetEntryType(out DirectoryEntryType entryType, ref readonly Path path)
{
throw new NotImplementedException();
}
protected override Result DoCreateFile(ref readonly Path path, long size, CreateFileOptions option)
{
throw new NotImplementedException();
}
protected override Result DoDeleteFile(ref readonly Path path)
{
throw new NotImplementedException();
}
protected override Result DoCreateDirectory(ref readonly Path path)
{
throw new NotImplementedException();
}
protected override Result DoDeleteDirectory(ref readonly Path path)
{
throw new NotImplementedException();
}
protected override Result DoDeleteDirectoryRecursively(ref readonly Path path)
{
throw new NotImplementedException();
}
protected override Result DoCleanDirectoryRecursively(ref readonly Path path)
{
throw new NotImplementedException();
}
protected override Result DoRenameFile(ref readonly Path currentPath, ref readonly Path newPath)
{
throw new NotImplementedException();
}
protected override Result DoRenameDirectory(ref readonly Path currentPath, ref readonly Path newPath)
{
throw new NotImplementedException();
}
protected override Result DoCommit()
{
throw new NotImplementedException();
}
protected override Result DoCommitProvisionally(long counter)
{
throw new NotImplementedException();
}
protected override Result DoGetFreeSpaceSize(out long freeSpace, ref readonly Path path)
{
throw new NotImplementedException();
}
protected override Result DoGetFileSystemAttribute(out FileSystemAttribute outAttribute)
{
throw new NotImplementedException();
}
private Result GetFileInfo(out RomFileInfo outInfo, ReadOnlySpan<byte> path)
{
throw new NotImplementedException();
}
}

View file

@ -12,8 +12,8 @@ using LibHac.Fs.Fsa;
using LibHac.FsSystem; using LibHac.FsSystem;
using LibHac.Spl; using LibHac.Spl;
using LibHac.Tools.Crypto; using LibHac.Tools.Crypto;
using LibHac.Tools.FsSystem.RomFs;
using KeyType = LibHac.Common.Keys.KeyType; using KeyType = LibHac.Common.Keys.KeyType;
using RomFsFileSystem = LibHac.Tools.FsSystem.RomFs.RomFsFileSystem;
namespace LibHac.Tools.FsSystem.NcaUtils; namespace LibHac.Tools.FsSystem.NcaUtils;

View file

@ -3,7 +3,7 @@ using LibHac.Fs;
using LibHac.FsSystem; using LibHac.FsSystem;
using LibHac.Tools.Fs; using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.RomFs; using RomFsFileSystem = LibHac.Tools.FsSystem.RomFs.RomFsFileSystem;
namespace hactoolnet; namespace hactoolnet;

View file

@ -563,4 +563,36 @@ public class TypeLayoutTests
Assert.Equal(8, Unsafe.SizeOf<Int64>()); Assert.Equal(8, Unsafe.SizeOf<Int64>());
Assert.Equal(4, AlignOf<Int64>()); Assert.Equal(4, AlignOf<Int64>());
} }
[Fact]
public static void RomFileSystemInformation_Layout()
{
var s = new RomFileSystemInformation();
Assert.Equal(0x50, Unsafe.SizeOf<RomFileSystemInformation>());
Assert.Equal(0x00, GetOffset(in s, in s.HeaderSize));
Assert.Equal(0x08, GetOffset(in s, in s.DirectoryBucketOffset));
Assert.Equal(0x10, GetOffset(in s, in s.DirectoryBucketSize));
Assert.Equal(0x18, GetOffset(in s, in s.DirectoryEntryOffset));
Assert.Equal(0x20, GetOffset(in s, in s.DirectoryEntrySize));
Assert.Equal(0x28, GetOffset(in s, in s.FileBucketOffset));
Assert.Equal(0x30, GetOffset(in s, in s.FileBucketSize));
Assert.Equal(0x38, GetOffset(in s, in s.FileEntryOffset));
Assert.Equal(0x40, GetOffset(in s, in s.FileEntrySize));
Assert.Equal(0x48, GetOffset(in s, in s.DataOffset));
}
[Fact]
public static void RomFileInfo_Layout()
{
var s = new RomFileInfo();
Assert.Equal(0x10, Unsafe.SizeOf<RomFileInfo>());
Assert.Equal(0, GetOffset(in s, in s.Offset));
Assert.Equal(8, GetOffset(in s, in s.Size));
Assert.Equal(4, AlignOf<RomFileInfo>());
}
} }