diff --git a/src/LibHac/IO/AesXtsDirectory.cs b/src/LibHac/IO/AesXtsDirectory.cs index ca45a1dd..1751c6a4 100644 --- a/src/LibHac/IO/AesXtsDirectory.cs +++ b/src/LibHac/IO/AesXtsDirectory.cs @@ -44,14 +44,16 @@ namespace LibHac.IO private long GetAesXtsFileSize(string path) { - IFile file = BaseFileSystem.OpenFile(path, OpenMode.Read); - var buffer = new byte[8]; + using (IFile file = BaseFileSystem.OpenFile(path, OpenMode.Read)) + { + var buffer = new byte[8]; - file.Read(buffer, 0); - if (BitConverter.ToUInt32(buffer, 0) != 0x3058414E) return 0; + file.Read(buffer, 0); + if (BitConverter.ToUInt32(buffer, 0) != 0x3058414E) return 0; - file.Read(buffer, 0x48); - return BitConverter.ToInt32(buffer, 0); + file.Read(buffer, 0x48); + return BitConverter.ToInt32(buffer, 0); + } } } } diff --git a/src/LibHac/IO/AesXtsFileSystem.cs b/src/LibHac/IO/AesXtsFileSystem.cs index ca4fccf5..74da2039 100644 --- a/src/LibHac/IO/AesXtsFileSystem.cs +++ b/src/LibHac/IO/AesXtsFileSystem.cs @@ -28,7 +28,7 @@ namespace LibHac.IO public void CreateDirectory(string path) { - throw new NotImplementedException(); + BaseFileSystem.CreateDirectory(path); } public void CreateFile(string path, long size) @@ -38,12 +38,12 @@ namespace LibHac.IO public void DeleteDirectory(string path) { - throw new NotImplementedException(); + BaseFileSystem.DeleteDirectory(path); } public void DeleteFile(string path) { - throw new NotImplementedException(); + BaseFileSystem.DeleteFile(path); } public IDirectory OpenDirectory(string path, OpenDirectoryMode mode) @@ -77,17 +77,17 @@ namespace LibHac.IO public bool DirectoryExists(string path) { - throw new NotImplementedException(); + return BaseFileSystem.DirectoryExists(path); } public bool FileExists(string path) { - throw new NotImplementedException(); + return BaseFileSystem.FileExists(path); } public void Commit() { - throw new NotImplementedException(); + BaseFileSystem.Commit(); } } } diff --git a/src/LibHac/IO/ConcatenationFile.cs b/src/LibHac/IO/ConcatenationFile.cs index bf9ed4bb..7bf3f925 100644 --- a/src/LibHac/IO/ConcatenationFile.cs +++ b/src/LibHac/IO/ConcatenationFile.cs @@ -9,7 +9,7 @@ namespace LibHac.IO private IFile[] Sources { get; } private long SplitFileSize { get; } - public ConcatenationFile(IList sources, long splitFileSize, OpenMode mode) + internal ConcatenationFile(IList sources, long splitFileSize, OpenMode mode) { Sources = sources.ToArray(); SplitFileSize = splitFileSize; @@ -22,6 +22,8 @@ namespace LibHac.IO throw new ArgumentException($"Source file must have size {splitFileSize}"); } } + + ToDispose.AddRange(Sources); } public override int Read(Span destination, long offset) diff --git a/src/LibHac/IO/FileBase.cs b/src/LibHac/IO/FileBase.cs index 964a3fd5..d975df5d 100644 --- a/src/LibHac/IO/FileBase.cs +++ b/src/LibHac/IO/FileBase.cs @@ -1,10 +1,12 @@ using System; +using System.Collections.Generic; namespace LibHac.IO { public abstract class FileBase : IFile { private bool _isDisposed; + protected List ToDispose { get; } = new List(); public abstract int Read(Span destination, long offset); public abstract void Write(ReadOnlySpan source, long offset); @@ -12,7 +14,7 @@ namespace LibHac.IO public abstract long GetSize(); public abstract void SetSize(long size); - protected OpenMode Mode { get; set; } + public OpenMode Mode { get; protected set; } protected int ValidateReadParamsAndGetSize(ReadOnlySpan span, long offset) { @@ -66,6 +68,11 @@ namespace LibHac.IO if (disposing) { Flush(); + + foreach (IDisposable item in ToDispose) + { + item?.Dispose(); + } } _isDisposed = true; diff --git a/src/LibHac/IO/FileSystemExtensions.cs b/src/LibHac/IO/FileSystemExtensions.cs index 0f35b999..c84b097e 100644 --- a/src/LibHac/IO/FileSystemExtensions.cs +++ b/src/LibHac/IO/FileSystemExtensions.cs @@ -98,8 +98,10 @@ namespace LibHac.IO } } - public static Stream AsStream(this IFile storage) => new NxFileStream(storage, true); - public static Stream AsStream(this IFile storage, bool keepOpen) => new NxFileStream(storage, keepOpen); + public static Stream AsStream(this IFile file) => new NxFileStream(file, true); + public static Stream AsStream(this IFile file, bool keepOpen) => new NxFileStream(file, keepOpen); + + public static IFile AsIFile(this Stream stream, OpenMode mode) => new StreamFile(stream, mode); public static int GetEntryCount(this IFileSystem fs, OpenDirectoryMode mode) { diff --git a/src/LibHac/IO/IFile.cs b/src/LibHac/IO/IFile.cs index bc137701..c6e130dd 100644 --- a/src/LibHac/IO/IFile.cs +++ b/src/LibHac/IO/IFile.cs @@ -4,6 +4,7 @@ namespace LibHac.IO { public interface IFile : IDisposable { + OpenMode Mode { get; } int Read(Span destination, long offset); void Write(ReadOnlySpan source, long offset); void Flush(); diff --git a/src/LibHac/IO/LocalFile.cs b/src/LibHac/IO/LocalFile.cs index 5974bb55..b5906499 100644 --- a/src/LibHac/IO/LocalFile.cs +++ b/src/LibHac/IO/LocalFile.cs @@ -6,20 +6,25 @@ namespace LibHac.IO public class LocalFile : FileBase { private string Path { get; } - private StreamStorage Storage { get; } + private FileStream Stream { get; } + private StreamFile File { get; } public LocalFile(string path, OpenMode mode) { Path = path; Mode = mode; - Storage = new StreamStorage(new FileStream(Path, FileMode.Open), false); + Stream = new FileStream(Path, FileMode.Open, GetFileAccess(mode)); + File = new StreamFile(Stream, mode); + + ToDispose.Add(File); + ToDispose.Add(Stream); } public override int Read(Span destination, long offset) { int toRead = ValidateReadParamsAndGetSize(destination, offset); - Storage.Read(destination.Slice(0, toRead), offset); + File.Read(destination.Slice(0, toRead), offset); return toRead; } @@ -28,22 +33,28 @@ namespace LibHac.IO { ValidateWriteParams(source, offset); - Storage.Write(source, offset); + File.Write(source, offset); } public override void Flush() { - Storage.Flush(); + File.Flush(); } public override long GetSize() { - return Storage.Length; + return File.GetSize(); } public override void SetSize(long size) { - throw new NotImplementedException(); + File.SetSize(size); + } + + private static FileAccess GetFileAccess(OpenMode mode) + { + // FileAccess and OpenMode have the same flags + return (FileAccess)(mode & OpenMode.ReadWrite); } } } diff --git a/src/LibHac/IO/NxFileStream.cs b/src/LibHac/IO/NxFileStream.cs index b361fc18..895996a0 100644 --- a/src/LibHac/IO/NxFileStream.cs +++ b/src/LibHac/IO/NxFileStream.cs @@ -60,9 +60,9 @@ namespace LibHac.IO } // todo access - public override bool CanRead => true; + public override bool CanRead => BaseFile.Mode.HasFlag(OpenMode.Read); public override bool CanSeek => true; - public override bool CanWrite => true; + public override bool CanWrite => BaseFile.Mode.HasFlag(OpenMode.Write); public override long Length { get; } public override long Position { get; set; } diff --git a/src/LibHac/IO/StreamFile.cs b/src/LibHac/IO/StreamFile.cs new file mode 100644 index 00000000..bf241356 --- /dev/null +++ b/src/LibHac/IO/StreamFile.cs @@ -0,0 +1,107 @@ +using System; +using System.IO; + +#if !STREAM_SPAN +using System.Buffers; +#endif + +namespace LibHac.IO +{ + /// + /// Provides an interface for interacting with a + /// + public class StreamFile : FileBase + { + private Stream BaseStream { get; } + private object Locker { get; } = new object(); + + public StreamFile(Stream baseStream, OpenMode mode) + { + BaseStream = baseStream; + Mode = mode; + } + + public override int Read(Span destination, long offset) + { +#if STREAM_SPAN + lock (Locker) + { + if (BaseStream.Position != offset) + { + BaseStream.Position = offset; + } + + return BaseStream.Read(destination); + } +#else + byte[] buffer = ArrayPool.Shared.Rent(destination.Length); + try + { + int bytesRead; + lock (Locker) + { + if (BaseStream.Position != offset) + { + BaseStream.Position = offset; + } + + bytesRead = BaseStream.Read(buffer, 0, destination.Length); + } + + new Span(buffer, 0, destination.Length).CopyTo(destination); + + return bytesRead; + } + finally { ArrayPool.Shared.Return(buffer); } +#endif + } + + public override void Write(ReadOnlySpan source, long offset) + { +#if STREAM_SPAN + lock (Locker) + { + BaseStream.Position = offset; + BaseStream.Write(source); + } +#else + byte[] buffer = ArrayPool.Shared.Rent(source.Length); + try + { + source.CopyTo(buffer); + + lock (Locker) + { + BaseStream.Position = offset; + BaseStream.Write(buffer, 0, source.Length); + } + } + finally { ArrayPool.Shared.Return(buffer); } +#endif + } + + public override void Flush() + { + lock (Locker) + { + BaseStream.Flush(); + } + } + + public override long GetSize() + { + lock (Locker) + { + return BaseStream.Length; + } + } + + public override void SetSize(long size) + { + lock (Locker) + { + BaseStream.SetLength(size); + } + } + } +}