From 31563ad10834be4f672926c9da08a5937d4b44d9 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sun, 3 Nov 2019 17:03:33 -0700 Subject: [PATCH] Add FileStorageBasedFileSystem --- src/LibHac/Common/StringUtils.cs | 24 ++++ src/LibHac/Fs/FileStorage2.cs | 130 ++++++++++++++++++++ src/LibHac/Fs/FileStorageBasedFileSystem.cs | 49 ++++++++ src/LibHac/Fs/FileSystemBase.cs | 24 ++++ src/LibHac/Fs/FsEnums.cs | 3 +- src/LibHac/Fs/QueryRangeInfo.cs | 11 ++ src/LibHac/Fs/ResultFs.cs | 1 + 7 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 src/LibHac/Fs/FileStorage2.cs create mode 100644 src/LibHac/Fs/FileStorageBasedFileSystem.cs create mode 100644 src/LibHac/Fs/QueryRangeInfo.cs diff --git a/src/LibHac/Common/StringUtils.cs b/src/LibHac/Common/StringUtils.cs index 7bdeca9f..9579b5e6 100644 --- a/src/LibHac/Common/StringUtils.cs +++ b/src/LibHac/Common/StringUtils.cs @@ -45,6 +45,30 @@ namespace LibHac.Common return i; } + public static int Compare(ReadOnlySpan s1, ReadOnlySpan s2) + { + int maxLen = Math.Min(s1.Length, s2.Length); + + return Compare(s1, s2, maxLen); + } + + public static int Compare(ReadOnlySpan s1, ReadOnlySpan s2, int maxLen) + { + for (int i = 0; i < maxLen; i++) + { + byte c1 = s1[i]; + byte c2 = s2[i]; + + if (c1 != c2) + return c1 - c2; + + if (c1 == 0) + return 0; + } + + return 0; + } + /// /// Concatenates 2 byte strings. /// diff --git a/src/LibHac/Fs/FileStorage2.cs b/src/LibHac/Fs/FileStorage2.cs new file mode 100644 index 00000000..0cd93c0f --- /dev/null +++ b/src/LibHac/Fs/FileStorage2.cs @@ -0,0 +1,130 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace LibHac.Fs +{ + public class FileStorage2 : StorageBase + { + private const long InvalidSize = -1; + + private IFile BaseFile { get; set; } + private long FileSize { get; set; } + + public FileStorage2(IFile baseFile) + { + BaseFile = baseFile; + FileSize = InvalidSize; + } + + protected FileStorage2() { } + + protected void SetFile(IFile file) + { + Debug.Assert(file != null); + Debug.Assert(BaseFile == null); + + BaseFile = file; + } + + private Result UpdateSize() + { + if (FileSize != InvalidSize) + return Result.Success; + + Result rc = BaseFile.GetSize(out long fileSize); + if (rc.IsFailure()) return rc; + + FileSize = fileSize; + return Result.Success; + } + + protected override Result ReadImpl(long offset, Span destination) + { + if (destination.Length == 0) + return Result.Success; + + Result rc = UpdateSize(); + if (rc.IsFailure()) return rc; + + if (!IsRangeValid(offset, destination.Length, FileSize)) + return ResultFs.ValueOutOfRange.Log(); + + return BaseFile.Read(out _, offset, destination, ReadOption.None); + } + + protected override Result WriteImpl(long offset, ReadOnlySpan source) + { + if (source.Length == 0) + return Result.Success; + + Result rc = UpdateSize(); + if (rc.IsFailure()) return rc; + + if (!IsRangeValid(offset, source.Length, FileSize)) + return ResultFs.ValueOutOfRange.Log(); + + return BaseFile.Write(offset, source, WriteOption.None); + } + + protected override Result FlushImpl() + { + return BaseFile.Flush(); + } + + protected override Result GetSizeImpl(out long size) + { + Result rc = UpdateSize(); + if (rc.IsFailure()) + { + size = default; + return rc; + } + + size = FileSize; + return Result.Success; + } + + protected override Result SetSizeImpl(long size) + { + FileSize = InvalidSize; + return BaseFile.SetSize(size); + } + + protected override Result OperateRangeImpl(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) + { + switch (operationId) + { + case OperationId.InvalidateCache: + case OperationId.QueryRange: + if (size == 0) + { + if (operationId == OperationId.QueryRange) + { + if (outBuffer.Length != Unsafe.SizeOf()) + { + return ResultFs.InvalidSize.Log(); + } + + Unsafe.As(ref outBuffer[0]) = new QueryRangeInfo(); + } + + return Result.Success; + } + + Result rc = UpdateSize(); + if (rc.IsFailure()) return rc; + + if (size < 0 || offset < 0) + { + return ResultFs.ValueOutOfRange.Log(); + } + + return BaseFile.OperateRange(outBuffer, operationId, offset, size, inBuffer); + + default: + return ResultFs.UnsupportedOperationInFileStorageOperateRange.Log(); + } + } + } +} diff --git a/src/LibHac/Fs/FileStorageBasedFileSystem.cs b/src/LibHac/Fs/FileStorageBasedFileSystem.cs new file mode 100644 index 00000000..2ee32426 --- /dev/null +++ b/src/LibHac/Fs/FileStorageBasedFileSystem.cs @@ -0,0 +1,49 @@ +namespace LibHac.Fs +{ + public class FileStorageBasedFileSystem : FileStorage2 + { + private IFileSystem BaseFileSystem { get; set; } + private IFile BaseFile { get; set; } + + private FileStorageBasedFileSystem() : base() { } + + public static Result CreateNew(out FileStorageBasedFileSystem created, IFileSystem baseFileSystem, string path, + OpenMode mode) + { + var obj = new FileStorageBasedFileSystem(); + Result rc = obj.Initialize(baseFileSystem, path, mode); + + if (rc.IsSuccess()) + { + created = obj; + return Result.Success; + } + + obj.Dispose(); + created = default; + return rc; + } + + private Result Initialize(IFileSystem baseFileSystem, string path, OpenMode mode) + { + Result rc = baseFileSystem.OpenFile(out IFile file, path, mode); + if (rc.IsFailure()) return rc; + + SetFile(file); + BaseFile = file; + BaseFileSystem = baseFileSystem; + + return Result.Success; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + BaseFile?.Dispose(); + } + + base.Dispose(disposing); + } + } +} diff --git a/src/LibHac/Fs/FileSystemBase.cs b/src/LibHac/Fs/FileSystemBase.cs index f87cd1d9..4c6ab0ff 100644 --- a/src/LibHac/Fs/FileSystemBase.cs +++ b/src/LibHac/Fs/FileSystemBase.cs @@ -95,6 +95,18 @@ namespace LibHac.Fs return ResultFs.PreconditionViolation.Log(); } + if (path == null) + { + directory = default; + return ResultFs.NullArgument.Log(); + } + + if ((mode & ~OpenDirectoryMode.All) != 0 || (mode & OpenDirectoryMode.All) == 0) + { + directory = default; + return ResultFs.InvalidArgument.Log(); + } + return OpenDirectoryImpl(out directory, path, mode); } @@ -106,6 +118,18 @@ namespace LibHac.Fs return ResultFs.PreconditionViolation.Log(); } + if (path == null) + { + file = default; + return ResultFs.NullArgument.Log(); + } + + if ((mode & ~OpenMode.All) != 0 || (mode & OpenMode.ReadWrite) == 0) + { + file = default; + return ResultFs.InvalidArgument.Log(); + } + return OpenFileImpl(out file, path, mode); } diff --git a/src/LibHac/Fs/FsEnums.cs b/src/LibHac/Fs/FsEnums.cs index 02ef7768..861f3829 100644 --- a/src/LibHac/Fs/FsEnums.cs +++ b/src/LibHac/Fs/FsEnums.cs @@ -122,7 +122,8 @@ namespace LibHac.Fs Read = 1, Write = 2, AllowAppend = 4, - ReadWrite = Read | Write + ReadWrite = Read | Write, + All = Read | Write | AllowAppend } [Flags] diff --git a/src/LibHac/Fs/QueryRangeInfo.cs b/src/LibHac/Fs/QueryRangeInfo.cs new file mode 100644 index 00000000..58473e6a --- /dev/null +++ b/src/LibHac/Fs/QueryRangeInfo.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace LibHac.Fs +{ + [StructLayout(LayoutKind.Sequential, Size = 0x40)] + public struct QueryRangeInfo + { + public uint AesCtrKeyType; + public uint SpeedEmulationType; + } +} diff --git a/src/LibHac/Fs/ResultFs.cs b/src/LibHac/Fs/ResultFs.cs index e98f7151..c751a9c9 100644 --- a/src/LibHac/Fs/ResultFs.cs +++ b/src/LibHac/Fs/ResultFs.cs @@ -109,6 +109,7 @@ public static Result SubStorageNotResizable => new Result(ModuleFs, 6302); public static Result SubStorageNotResizableMiddleOfFile => new Result(ModuleFs, 6303); public static Result UnsupportedOperationInMemoryStorageSetSize => new Result(ModuleFs, 6304); + public static Result UnsupportedOperationInFileStorageOperateRange => new Result(ModuleFs, 6306); public static Result UnsupportedOperationInAesCtrExStorageWrite => new Result(ModuleFs, 6310); public static Result UnsupportedOperationInHierarchicalIvfcStorageSetSize => new Result(ModuleFs, 6316); public static Result UnsupportedOperationInIndirectStorageWrite => new Result(ModuleFs, 6324);