Add StreamFile. Make sure LocalFile closes the base file

This commit is contained in:
Alex Barney 2019-01-09 19:47:06 -06:00
parent 01a3bef903
commit 3db2c81b77
9 changed files with 157 additions and 25 deletions

View file

@ -44,14 +44,16 @@ namespace LibHac.IO
private long GetAesXtsFileSize(string path) private long GetAesXtsFileSize(string path)
{ {
IFile file = BaseFileSystem.OpenFile(path, OpenMode.Read); using (IFile file = BaseFileSystem.OpenFile(path, OpenMode.Read))
var buffer = new byte[8]; {
var buffer = new byte[8];
file.Read(buffer, 0); file.Read(buffer, 0);
if (BitConverter.ToUInt32(buffer, 0) != 0x3058414E) return 0; if (BitConverter.ToUInt32(buffer, 0) != 0x3058414E) return 0;
file.Read(buffer, 0x48); file.Read(buffer, 0x48);
return BitConverter.ToInt32(buffer, 0); return BitConverter.ToInt32(buffer, 0);
}
} }
} }
} }

View file

@ -28,7 +28,7 @@ namespace LibHac.IO
public void CreateDirectory(string path) public void CreateDirectory(string path)
{ {
throw new NotImplementedException(); BaseFileSystem.CreateDirectory(path);
} }
public void CreateFile(string path, long size) public void CreateFile(string path, long size)
@ -38,12 +38,12 @@ namespace LibHac.IO
public void DeleteDirectory(string path) public void DeleteDirectory(string path)
{ {
throw new NotImplementedException(); BaseFileSystem.DeleteDirectory(path);
} }
public void DeleteFile(string path) public void DeleteFile(string path)
{ {
throw new NotImplementedException(); BaseFileSystem.DeleteFile(path);
} }
public IDirectory OpenDirectory(string path, OpenDirectoryMode mode) public IDirectory OpenDirectory(string path, OpenDirectoryMode mode)
@ -77,17 +77,17 @@ namespace LibHac.IO
public bool DirectoryExists(string path) public bool DirectoryExists(string path)
{ {
throw new NotImplementedException(); return BaseFileSystem.DirectoryExists(path);
} }
public bool FileExists(string path) public bool FileExists(string path)
{ {
throw new NotImplementedException(); return BaseFileSystem.FileExists(path);
} }
public void Commit() public void Commit()
{ {
throw new NotImplementedException(); BaseFileSystem.Commit();
} }
} }
} }

View file

@ -9,7 +9,7 @@ namespace LibHac.IO
private IFile[] Sources { get; } private IFile[] Sources { get; }
private long SplitFileSize { get; } private long SplitFileSize { get; }
public ConcatenationFile(IList<IFile> sources, long splitFileSize, OpenMode mode) internal ConcatenationFile(IList<IFile> sources, long splitFileSize, OpenMode mode)
{ {
Sources = sources.ToArray(); Sources = sources.ToArray();
SplitFileSize = splitFileSize; SplitFileSize = splitFileSize;
@ -22,6 +22,8 @@ namespace LibHac.IO
throw new ArgumentException($"Source file must have size {splitFileSize}"); throw new ArgumentException($"Source file must have size {splitFileSize}");
} }
} }
ToDispose.AddRange(Sources);
} }
public override int Read(Span<byte> destination, long offset) public override int Read(Span<byte> destination, long offset)

View file

@ -1,10 +1,12 @@
using System; using System;
using System.Collections.Generic;
namespace LibHac.IO namespace LibHac.IO
{ {
public abstract class FileBase : IFile public abstract class FileBase : IFile
{ {
private bool _isDisposed; private bool _isDisposed;
protected List<IDisposable> ToDispose { get; } = new List<IDisposable>();
public abstract int Read(Span<byte> destination, long offset); public abstract int Read(Span<byte> destination, long offset);
public abstract void Write(ReadOnlySpan<byte> source, long offset); public abstract void Write(ReadOnlySpan<byte> source, long offset);
@ -12,7 +14,7 @@ namespace LibHac.IO
public abstract long GetSize(); public abstract long GetSize();
public abstract void SetSize(long size); public abstract void SetSize(long size);
protected OpenMode Mode { get; set; } public OpenMode Mode { get; protected set; }
protected int ValidateReadParamsAndGetSize(ReadOnlySpan<byte> span, long offset) protected int ValidateReadParamsAndGetSize(ReadOnlySpan<byte> span, long offset)
{ {
@ -66,6 +68,11 @@ namespace LibHac.IO
if (disposing) if (disposing)
{ {
Flush(); Flush();
foreach (IDisposable item in ToDispose)
{
item?.Dispose();
}
} }
_isDisposed = true; _isDisposed = true;

View file

@ -98,8 +98,10 @@ namespace LibHac.IO
} }
} }
public static Stream AsStream(this IFile storage) => new NxFileStream(storage, true); public static Stream AsStream(this IFile file) => new NxFileStream(file, true);
public static Stream AsStream(this IFile storage, bool keepOpen) => new NxFileStream(storage, keepOpen); 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) public static int GetEntryCount(this IFileSystem fs, OpenDirectoryMode mode)
{ {

View file

@ -4,6 +4,7 @@ namespace LibHac.IO
{ {
public interface IFile : IDisposable public interface IFile : IDisposable
{ {
OpenMode Mode { get; }
int Read(Span<byte> destination, long offset); int Read(Span<byte> destination, long offset);
void Write(ReadOnlySpan<byte> source, long offset); void Write(ReadOnlySpan<byte> source, long offset);
void Flush(); void Flush();

View file

@ -6,20 +6,25 @@ namespace LibHac.IO
public class LocalFile : FileBase public class LocalFile : FileBase
{ {
private string Path { get; } private string Path { get; }
private StreamStorage Storage { get; } private FileStream Stream { get; }
private StreamFile File { get; }
public LocalFile(string path, OpenMode mode) public LocalFile(string path, OpenMode mode)
{ {
Path = path; Path = path;
Mode = mode; 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<byte> destination, long offset) public override int Read(Span<byte> destination, long offset)
{ {
int toRead = ValidateReadParamsAndGetSize(destination, offset); int toRead = ValidateReadParamsAndGetSize(destination, offset);
Storage.Read(destination.Slice(0, toRead), offset); File.Read(destination.Slice(0, toRead), offset);
return toRead; return toRead;
} }
@ -28,22 +33,28 @@ namespace LibHac.IO
{ {
ValidateWriteParams(source, offset); ValidateWriteParams(source, offset);
Storage.Write(source, offset); File.Write(source, offset);
} }
public override void Flush() public override void Flush()
{ {
Storage.Flush(); File.Flush();
} }
public override long GetSize() public override long GetSize()
{ {
return Storage.Length; return File.GetSize();
} }
public override void SetSize(long size) 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);
} }
} }
} }

View file

@ -60,9 +60,9 @@ namespace LibHac.IO
} }
// todo access // todo access
public override bool CanRead => true; public override bool CanRead => BaseFile.Mode.HasFlag(OpenMode.Read);
public override bool CanSeek => true; 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 Length { get; }
public override long Position { get; set; } public override long Position { get; set; }

107
src/LibHac/IO/StreamFile.cs Normal file
View file

@ -0,0 +1,107 @@
using System;
using System.IO;
#if !STREAM_SPAN
using System.Buffers;
#endif
namespace LibHac.IO
{
/// <summary>
/// Provides an <see cref="IFile"/> interface for interacting with a <see cref="Stream"/>
/// </summary>
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<byte> destination, long offset)
{
#if STREAM_SPAN
lock (Locker)
{
if (BaseStream.Position != offset)
{
BaseStream.Position = offset;
}
return BaseStream.Read(destination);
}
#else
byte[] buffer = ArrayPool<byte>.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<byte>(buffer, 0, destination.Length).CopyTo(destination);
return bytesRead;
}
finally { ArrayPool<byte>.Shared.Return(buffer); }
#endif
}
public override void Write(ReadOnlySpan<byte> source, long offset)
{
#if STREAM_SPAN
lock (Locker)
{
BaseStream.Position = offset;
BaseStream.Write(source);
}
#else
byte[] buffer = ArrayPool<byte>.Shared.Rent(source.Length);
try
{
source.CopyTo(buffer);
lock (Locker)
{
BaseStream.Position = offset;
BaseStream.Write(buffer, 0, source.Length);
}
}
finally { ArrayPool<byte>.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);
}
}
}
}