Make IStorage interface and classes less complex

This commit is contained in:
Alex Barney 2019-01-21 16:57:41 -06:00
parent 30b42eaf34
commit 8e151c4a1c
30 changed files with 165 additions and 262 deletions

View file

@ -33,16 +33,14 @@ namespace LibHac.Nand
{ {
IStorage encStorage = ProdInfo.Open().AsStorage(); IStorage encStorage = ProdInfo.Open().AsStorage();
var decStorage = new CachedStorage(new Aes128XtsStorage(encStorage, Keyset.BisKeys[0], 0x4000, true), 0x4000, 4, true); var decStorage = new CachedStorage(new Aes128XtsStorage(encStorage, Keyset.BisKeys[0], 0x4000, true), 0x4000, 4, true);
decStorage.SetReadOnly(); return decStorage.AsStream(FileAccess.Read);
return decStorage.AsStream();
} }
public FatFileSystemProvider OpenProdInfoF() public FatFileSystemProvider OpenProdInfoF()
{ {
IStorage encStorage = ProdInfoF.Open().AsStorage(); IStorage encStorage = ProdInfoF.Open().AsStorage();
var decStorage = new CachedStorage(new Aes128XtsStorage(encStorage, Keyset.BisKeys[0], 0x4000, true), 0x4000, 4, true); var decStorage = new CachedStorage(new Aes128XtsStorage(encStorage, Keyset.BisKeys[0], 0x4000, true), 0x4000, 4, true);
decStorage.SetReadOnly(); var fat = new FatFileSystem(decStorage.AsStream(FileAccess.Read), Ownership.None);
var fat = new FatFileSystem(decStorage.AsStream(), Ownership.None);
return new FatFileSystemProvider(fat); return new FatFileSystemProvider(fat);
} }
@ -50,8 +48,7 @@ namespace LibHac.Nand
{ {
IStorage encStorage = Safe.Open().AsStorage(); IStorage encStorage = Safe.Open().AsStorage();
var decStorage = new CachedStorage(new Aes128XtsStorage(encStorage, Keyset.BisKeys[1], 0x4000, true), 0x4000, 4, true); var decStorage = new CachedStorage(new Aes128XtsStorage(encStorage, Keyset.BisKeys[1], 0x4000, true), 0x4000, 4, true);
decStorage.SetReadOnly(); var fat = new FatFileSystem(decStorage.AsStream(FileAccess.Read), Ownership.None);
var fat = new FatFileSystem(decStorage.AsStream(), Ownership.None);
return new FatFileSystemProvider(fat); return new FatFileSystemProvider(fat);
} }
@ -59,8 +56,7 @@ namespace LibHac.Nand
{ {
IStorage encStorage = System.Open().AsStorage(); IStorage encStorage = System.Open().AsStorage();
var decStorage = new CachedStorage(new Aes128XtsStorage(encStorage, Keyset.BisKeys[2], 0x4000, true), 0x4000, 4, true); var decStorage = new CachedStorage(new Aes128XtsStorage(encStorage, Keyset.BisKeys[2], 0x4000, true), 0x4000, 4, true);
decStorage.SetReadOnly(); var fat = new FatFileSystem(decStorage.AsStream(FileAccess.Read), Ownership.None);
var fat = new FatFileSystem(decStorage.AsStream(), Ownership.None);
return new FatFileSystemProvider(fat); return new FatFileSystemProvider(fat);
} }
@ -68,8 +64,7 @@ namespace LibHac.Nand
{ {
IStorage encStorage = User.Open().AsStorage(); IStorage encStorage = User.Open().AsStorage();
var decStorage = new CachedStorage(new Aes128XtsStorage(encStorage, Keyset.BisKeys[3], 0x4000, true), 0x4000, 4, true); var decStorage = new CachedStorage(new Aes128XtsStorage(encStorage, Keyset.BisKeys[3], 0x4000, true), 0x4000, 4, true);
decStorage.SetReadOnly(); var fat = new FatFileSystem(decStorage.AsStream(FileAccess.Read), Ownership.None);
var fat = new FatFileSystem(decStorage.AsStream(), Ownership.None);
return new FatFileSystemProvider(fat); return new FatFileSystemProvider(fat);
} }
} }

View file

@ -60,8 +60,6 @@ namespace LibHac.IO
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override bool CanWrite => false;
private AesSubsectionEntry GetSubsectionEntry(long offset) private AesSubsectionEntry GetSubsectionEntry(long offset)
{ {
int index = SubsectionOffsets.BinarySearch(offset); int index = SubsectionOffsets.BinarySearch(offset);

View file

@ -31,7 +31,7 @@ namespace LibHac.IO
throw new ArgumentException("NAX0 key derivation failed."); throw new ArgumentException("NAX0 key derivation failed.");
} }
Storage encStorage = new FileStorage(BaseFile).Slice(HeaderLength, Header.Size); IStorage encStorage = new FileStorage(BaseFile).Slice(HeaderLength, Header.Size);
BaseStorage = new CachedStorage(new Aes128XtsStorage(encStorage, Header.DecryptedKey1, Header.DecryptedKey2, BlockSize, true), 4, true); BaseStorage = new CachedStorage(new Aes128XtsStorage(encStorage, Header.DecryptedKey1, Header.DecryptedKey2, BlockSize, true), 4, true);
} }

View file

@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace LibHac.IO namespace LibHac.IO
{ {
public class CachedStorage : Storage public class CachedStorage : StorageBase
{ {
private IStorage BaseStorage { get; } private IStorage BaseStorage { get; }
private int BlockSize { get; } private int BlockSize { get; }
@ -148,7 +148,7 @@ namespace LibHac.IO
if (!block.Dirty) return; if (!block.Dirty) return;
long offset = block.Index * BlockSize; long offset = block.Index * BlockSize;
BaseStorage.Write(block.Buffer, offset, block.Length, 0); BaseStorage.Write(block.Buffer.AsSpan(0, block.Length), offset);
block.Dirty = false; block.Dirty = false;
} }

View file

@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace LibHac.IO namespace LibHac.IO
{ {
public class ConcatenationStorage : Storage public class ConcatenationStorage : StorageBase
{ {
private ConcatSource[] Sources { get; } private ConcatSource[] Sources { get; }
public override long Length { get; } public override long Length { get; }
@ -72,22 +72,6 @@ namespace LibHac.IO
} }
} }
public override Storage Slice(long start, long length, bool leaveOpen)
{
ConcatSource startSource = FindSource(start);
ConcatSource endSource = FindSource(start + length - 1);
if (startSource != endSource)
{
return base.Slice(start, length, leaveOpen);
}
Storage storage = startSource.Storage.Slice(start - startSource.StartOffset, length, true);
if (!leaveOpen) storage.ToDispose.Add(this);
return storage;
}
private ConcatSource FindSource(long offset) private ConcatSource FindSource(long offset)
{ {
foreach (ConcatSource info in Sources) foreach (ConcatSource info in Sources)

View file

@ -57,7 +57,7 @@ namespace LibHac.IO
IStorage source = segment.IsInOriginal ? Original : Delta; IStorage source = segment.IsInOriginal ? Original : Delta;
// todo Do this without tons of SubStorages // todo Do this without tons of SubStorages
Storage sub = source.Slice(segment.SourceOffset, segment.Size); IStorage sub = source.Slice(segment.SourceOffset, segment.Size);
storages.Add(sub); storages.Add(sub);
} }

View file

@ -2,7 +2,7 @@
namespace LibHac.IO namespace LibHac.IO
{ {
public class FileStorage : Storage public class FileStorage : StorageBase
{ {
private IFile BaseFile { get; } private IFile BaseFile { get; }

View file

@ -6,7 +6,7 @@ using System.Text;
namespace LibHac.IO namespace LibHac.IO
{ {
public class HierarchicalIntegrityVerificationStorage : Storage public class HierarchicalIntegrityVerificationStorage : StorageBase
{ {
public IStorage[] Levels { get; } public IStorage[] Levels { get; }
public IStorage DataLevel { get; } public IStorage DataLevel { get; }
@ -128,7 +128,7 @@ namespace LibHac.IO
if (validities[i] == Validity.Unchecked) if (validities[i] == Validity.Unchecked)
{ {
int toRead = (int)Math.Min(storage.Length - blockSize * i, buffer.Length); int toRead = (int)Math.Min(storage.Length - blockSize * i, buffer.Length);
storage.Read(buffer, blockSize * i, toRead, 0, IntegrityCheckLevel.IgnoreOnInvalid); storage.Read(buffer.AsSpan(0, toRead), blockSize * i, IntegrityCheckLevel.IgnoreOnInvalid);
} }
if (validities[i] == Validity.Invalid) if (validities[i] == Validity.Invalid)
@ -209,6 +209,9 @@ namespace LibHac.IO
} }
SaltSource = reader.ReadBytes(0x20); SaltSource = reader.ReadBytes(0x20);
if (reader.BaseStream.Position + 0x20 >= reader.BaseStream.Length) return;
MasterHash = reader.ReadBytes(0x20); MasterHash = reader.ReadBytes(0x20);
} }

View file

@ -12,16 +12,6 @@ namespace LibHac.IO
/// <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"/> to begin reading from.</param>
void Read(Span<byte> destination, long offset); void Read(Span<byte> destination, long offset);
/// <summary>
/// Reads a sequence of bytes from the current <see cref="IStorage"/>.
/// </summary>
/// <param name="buffer">The buffer where the read bytes will be stored.</param>
/// <param name="offset">The zero-based byte offset in <paramref name="buffer"/>
/// at which to begin storing the data read from the current <see cref="IStorage"/>.</param>
/// <param name="count">The number of bytes to be read from the <see cref="IStorage"/>.</param>
/// <param name="bufferOffset">The offset in the <see cref="IStorage"/> to begin reading from.</param>
void Read(byte[] buffer, long offset, int count, int bufferOffset);
/// <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>
@ -29,16 +19,6 @@ namespace LibHac.IO
/// <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"/> to begin writing to.</param>
void Write(ReadOnlySpan<byte> source, long offset); void Write(ReadOnlySpan<byte> source, long offset);
/// <summary>
/// Writes a sequence of bytes to the current <see cref="IStorage"/>.
/// </summary>
/// <param name="buffer"></param>
/// <param name="offset">The zero-based byte offset in <paramref name="buffer"/>
/// at which to begin begin copying bytes to the current <see cref="IStorage"/>.</param>
/// <param name="count">The number of bytes to be written to the <see cref="IStorage"/>.</param>
/// <param name="bufferOffset">The offset in the <see cref="IStorage"/> to begin writing to.</param>
void Write(byte[] buffer, long offset, int count, int bufferOffset);
/// <summary> /// <summary>
/// Causes any buffered data to be written to the underlying device. /// Causes any buffered data to be written to the underlying device.
/// </summary> /// </summary>

View file

@ -4,7 +4,7 @@ using System.Linq;
namespace LibHac.IO namespace LibHac.IO
{ {
public class IndirectStorage : Storage public class IndirectStorage : StorageBase
{ {
private List<RelocationEntry> RelocationEntries { get; } private List<RelocationEntry> RelocationEntries { get; }
private List<long> RelocationOffsets { get; } private List<long> RelocationOffsets { get; }
@ -62,8 +62,6 @@ namespace LibHac.IO
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override bool CanWrite => false;
public override long Length { get; } public override long Length { get; }
private RelocationEntry GetRelocationEntry(long offset) private RelocationEntry GetRelocationEntry(long offset)

View file

@ -116,16 +116,10 @@ namespace LibHac.IO
public void Read(Span<byte> destination, long offset, IntegrityCheckLevel integrityCheckLevel) public void Read(Span<byte> destination, long offset, IntegrityCheckLevel integrityCheckLevel)
{ {
ValidateSpanParameters(destination, offset); ValidateParameters(destination, offset);
ReadImpl(destination, offset, integrityCheckLevel); ReadImpl(destination, offset, integrityCheckLevel);
} }
public void Read(byte[] buffer, long offset, int count, int bufferOffset, IntegrityCheckLevel integrityCheckLevel)
{
ValidateArrayParameters(buffer, offset, count, bufferOffset);
ReadImpl(buffer.AsSpan(bufferOffset, count), offset, integrityCheckLevel);
}
protected override void WriteImpl(ReadOnlySpan<byte> source, long offset) protected override void WriteImpl(ReadOnlySpan<byte> source, long offset)
{ {
long blockIndex = offset / SectorSize; long blockIndex = offset / SectorSize;

View file

@ -2,7 +2,7 @@
namespace LibHac.IO namespace LibHac.IO
{ {
public class MemoryStorage : Storage public class MemoryStorage : StorageBase
{ {
private byte[] Buffer { get; } private byte[] Buffer { get; }
private int Start { get; } private int Start { get; }

View file

@ -5,7 +5,7 @@ namespace LibHac.IO
/// <summary> /// <summary>
/// An <see cref="IStorage"/> that returns all zeros when read, and does nothing on write. /// An <see cref="IStorage"/> that returns all zeros when read, and does nothing on write.
/// </summary> /// </summary>
public class NullStorage : Storage public class NullStorage : StorageBase
{ {
public NullStorage() { } public NullStorage() { }
public NullStorage(long length) => Length = length; public NullStorage(long length) => Length = length;

View file

@ -34,8 +34,8 @@ namespace LibHac.IO.Save
} }
} }
public IStorage GetBaseStorage() => BaseStorage.WithAccess(FileAccess.Read); public IStorage GetBaseStorage() => BaseStorage.AsReadOnly();
public IStorage GetHeaderStorage() => HeaderStorage.WithAccess(FileAccess.Read); public IStorage GetHeaderStorage() => HeaderStorage.AsReadOnly();
} }
public class AllocationTableEntry public class AllocationTableEntry

View file

@ -2,7 +2,7 @@
namespace LibHac.IO.Save namespace LibHac.IO.Save
{ {
public class AllocationTableStorage : Storage public class AllocationTableStorage : StorageBase
{ {
private IStorage BaseStorage { get; } private IStorage BaseStorage { get; }
private int BlockSize { get; } private int BlockSize { get; }

View file

@ -2,7 +2,7 @@
namespace LibHac.IO.Save namespace LibHac.IO.Save
{ {
public class DuplexStorage : Storage public class DuplexStorage : StorageBase
{ {
private int BlockSize { get; } private int BlockSize { get; }
private IStorage BitmapStorage { get; } private IStorage BitmapStorage { get; }

View file

@ -2,7 +2,7 @@
namespace LibHac.IO.Save namespace LibHac.IO.Save
{ {
public class HierarchicalDuplexStorage : Storage public class HierarchicalDuplexStorage : StorageBase
{ {
private DuplexStorage[] Layers { get; } private DuplexStorage[] Layers { get; }
private DuplexStorage DataLayer { get; } private DuplexStorage DataLayer { get; }

View file

@ -50,11 +50,11 @@ namespace LibHac.IO.Save
return map; return map;
} }
public IStorage GetMapStorage() => MapStorage.WithAccess(FileAccess.Read); public IStorage GetMapStorage() => MapStorage.AsReadOnly();
public IStorage GetHeaderStorage() => HeaderStorage.WithAccess(FileAccess.Read); public IStorage GetHeaderStorage() => HeaderStorage.AsReadOnly();
public IStorage GetModifiedPhysicalBlocksStorage() => ModifiedPhysicalBlocks.WithAccess(FileAccess.Read); public IStorage GetModifiedPhysicalBlocksStorage() => ModifiedPhysicalBlocks.AsReadOnly();
public IStorage GetModifiedVirtualBlocksStorage() => ModifiedVirtualBlocks.WithAccess(FileAccess.Read); public IStorage GetModifiedVirtualBlocksStorage() => ModifiedVirtualBlocks.AsReadOnly();
public IStorage GetFreeBlocksStorage() => FreeBlocks.WithAccess(FileAccess.Read); public IStorage GetFreeBlocksStorage() => FreeBlocks.AsReadOnly();
} }
public class JournalMapHeader public class JournalMapHeader

View file

@ -3,7 +3,7 @@ using System.IO;
namespace LibHac.IO.Save namespace LibHac.IO.Save
{ {
public class JournalStorage : Storage public class JournalStorage : StorageBase
{ {
private IStorage BaseStorage { get; } private IStorage BaseStorage { get; }
private IStorage HeaderStorage { get; } private IStorage HeaderStorage { get; }
@ -80,8 +80,8 @@ namespace LibHac.IO.Save
BaseStorage.Flush(); BaseStorage.Flush();
} }
public IStorage GetBaseStorage() => BaseStorage.WithAccess(FileAccess.Read); public IStorage GetBaseStorage() => BaseStorage.AsReadOnly();
public IStorage GetHeaderStorage() => HeaderStorage.WithAccess(FileAccess.Read); public IStorage GetHeaderStorage() => HeaderStorage.AsReadOnly();
} }
public class JournalHeader public class JournalHeader

View file

@ -4,7 +4,7 @@ using System.IO;
namespace LibHac.IO.Save namespace LibHac.IO.Save
{ {
public class RemapStorage : Storage public class RemapStorage : StorageBase
{ {
private IStorage BaseStorage { get; } private IStorage BaseStorage { get; }
private IStorage HeaderStorage { get; } private IStorage HeaderStorage { get; }
@ -102,9 +102,9 @@ namespace LibHac.IO.Save
BaseStorage.Flush(); BaseStorage.Flush();
} }
public IStorage GetBaseStorage() => BaseStorage.WithAccess(FileAccess.Read); public IStorage GetBaseStorage() => BaseStorage.AsReadOnly();
public IStorage GetHeaderStorage() => HeaderStorage.WithAccess(FileAccess.Read); public IStorage GetHeaderStorage() => HeaderStorage.AsReadOnly();
public IStorage GetMapEntryStorage() => MapEntryStorage.WithAccess(FileAccess.Read); public IStorage GetMapEntryStorage() => MapEntryStorage.AsReadOnly();
private static RemapSegment[] InitSegments(RemapHeader header, MapEntry[] mapEntries) private static RemapSegment[] InitSegments(RemapHeader header, MapEntry[] mapEntries)
{ {

View file

@ -152,8 +152,8 @@ namespace LibHac.IO.Save
throw new System.NotImplementedException(); throw new System.NotImplementedException();
} }
public IStorage GetBaseStorage() => BaseStorage.WithAccess(FileAccess.Read); public IStorage GetBaseStorage() => BaseStorage.AsReadOnly();
public IStorage GetHeaderStorage() => HeaderStorage.WithAccess(FileAccess.Read); public IStorage GetHeaderStorage() => HeaderStorage.AsReadOnly();
private void ReadFileInfo() private void ReadFileInfo()
{ {

View file

@ -2,7 +2,7 @@
namespace LibHac.IO namespace LibHac.IO
{ {
public class SectorStorage : Storage public class SectorStorage : StorageBase
{ {
protected IStorage BaseStorage { get; } protected IStorage BaseStorage { get; }

View file

@ -1,114 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace LibHac.IO
{
public abstract class Storage : IStorage
{
private bool _isDisposed;
protected internal List<IDisposable> ToDispose { get; } = new List<IDisposable>();
protected abstract void ReadImpl(Span<byte> destination, long offset);
protected abstract void WriteImpl(ReadOnlySpan<byte> source, long offset);
public abstract void Flush();
public abstract long Length { get; }
protected FileAccess Access { get; set; } = FileAccess.ReadWrite;
public void Read(Span<byte> destination, long offset)
{
EnsureCanRead();
ValidateSpanParameters(destination, offset);
ReadImpl(destination, offset);
}
public virtual void Read(byte[] buffer, long offset, int count, int bufferOffset)
{
ValidateArrayParameters(buffer, offset, count, bufferOffset);
Read(buffer.AsSpan(bufferOffset, count), offset);
}
public void Write(ReadOnlySpan<byte> source, long offset)
{
EnsureCanWrite();
ValidateSpanParameters(source, offset);
WriteImpl(source, offset);
}
public virtual void Write(byte[] buffer, long offset, int count, int bufferOffset)
{
ValidateArrayParameters(buffer, offset, count, bufferOffset);
Write(buffer.AsSpan(bufferOffset, count), offset);
}
public virtual Storage Slice(long start, long length, bool leaveOpen)
{
return new SubStorage(this, start, length, leaveOpen);
}
protected virtual void Dispose(bool disposing)
{
if (_isDisposed) return;
if (disposing)
{
Flush();
foreach (IDisposable item in ToDispose)
{
item?.Dispose();
}
}
_isDisposed = true;
}
public void SetReadOnly() => Access = FileAccess.Read;
public virtual bool CanRead => (Access & FileAccess.Read) != 0;
public virtual bool CanWrite => (Access & FileAccess.Write) != 0;
private void EnsureCanRead()
{
if (!CanRead) throw new InvalidOperationException("Storage is not readable");
}
private void EnsureCanWrite()
{
if (!CanWrite) throw new InvalidOperationException("Storage is not writable");
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void ValidateArrayParameters(byte[] buffer, long offset, int count, int bufferOffset)
{
if (_isDisposed) throw new ObjectDisposedException(null);
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset), "Argument must be non-negative.");
if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), "Argument must be non-negative.");
if (bufferOffset < 0) throw new ArgumentOutOfRangeException(nameof(bufferOffset), "Argument must be non-negative.");
if (buffer.Length - bufferOffset < count) throw new ArgumentException("bufferOffset, length, and count were out of bounds for the array.");
if (Length != -1)
{
if (offset + count > Length) throw new ArgumentException("The given offset and count exceed the length of the Storage");
}
}
protected void ValidateSpanParameters(ReadOnlySpan<byte> destination, long offset)
{
if (_isDisposed) throw new ObjectDisposedException(null);
if (destination == null) throw new ArgumentNullException(nameof(destination));
if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset), "Argument must be non-negative.");
if (Length != -1)
{
if (offset + destination.Length > Length) throw new ArgumentException("The given offset and count exceed the length of the Storage");
}
}
}
}

View file

@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
namespace LibHac.IO
{
public abstract class StorageBase : IStorage
{
private bool _isDisposed;
protected internal List<IDisposable> ToDispose { get; } = new List<IDisposable>();
protected abstract void ReadImpl(Span<byte> destination, long offset);
protected abstract void WriteImpl(ReadOnlySpan<byte> source, long offset);
public abstract void Flush();
public abstract long Length { get; }
public void Read(Span<byte> destination, long offset)
{
ValidateParameters(destination, offset);
ReadImpl(destination, offset);
}
public void Write(ReadOnlySpan<byte> source, long offset)
{
ValidateParameters(source, offset);
WriteImpl(source, offset);
}
protected virtual void Dispose(bool disposing)
{
if (_isDisposed) return;
if (disposing)
{
Flush();
foreach (IDisposable item in ToDispose)
{
item?.Dispose();
}
}
_isDisposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void ValidateParameters(ReadOnlySpan<byte> span, long offset)
{
if (_isDisposed) throw new ObjectDisposedException(null);
if (span == null) throw new ArgumentNullException(nameof(span));
if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset), "Argument must be non-negative.");
if (Length != -1)
{
if (offset + span.Length > Length) throw new ArgumentException("The given offset and count exceed the length of the Storage");
}
}
}
}

View file

@ -6,7 +6,7 @@ namespace LibHac.IO
{ {
public static class StorageExtensions public static class StorageExtensions
{ {
public static Storage Slice(this IStorage storage, long start) public static IStorage Slice(this IStorage storage, long start)
{ {
if (storage.Length == -1) if (storage.Length == -1)
{ {
@ -16,33 +16,29 @@ namespace LibHac.IO
return storage.Slice(start, storage.Length - start); return storage.Slice(start, storage.Length - start);
} }
public static Storage Slice(this IStorage storage, long start, long length) public static IStorage Slice(this IStorage storage, long start, long length)
{ {
return storage.Slice(start, length, true); return storage.Slice(start, length, true);
} }
public static Storage Slice(this IStorage storage, long start, long length, bool leaveOpen) public static IStorage Slice(this IStorage storage, long start, long length, bool leaveOpen)
{ {
if (storage is Storage s)
{
return s.Slice(start, length, leaveOpen);
}
return new SubStorage(storage, start, length, leaveOpen); return new SubStorage(storage, start, length, leaveOpen);
} }
public static Storage WithAccess(this IStorage storage, FileAccess access) public static IStorage AsReadOnly(this IStorage storage)
{ {
return storage.WithAccess(access, true); return storage.AsReadOnly(true);
} }
public static Storage WithAccess(this IStorage storage, FileAccess access, bool leaveOpen) public static IStorage AsReadOnly(this IStorage storage, bool leaveOpen)
{ {
return new SubStorage(storage, 0, storage.Length, leaveOpen, access); return new SubStorage(storage, 0, storage.Length, leaveOpen, FileAccess.Read);
} }
public static Stream AsStream(this IStorage storage) => new StorageStream(storage, true); public static Stream AsStream(this IStorage storage) => new StorageStream(storage, FileAccess.ReadWrite, true);
public static Stream AsStream(this IStorage storage, bool keepOpen) => new StorageStream(storage, keepOpen); public static Stream AsStream(this IStorage storage, FileAccess access) => new StorageStream(storage, access, true);
public static Stream AsStream(this IStorage storage, FileAccess access, bool keepOpen) => new StorageStream(storage, access, keepOpen);
public static void CopyTo(this IStorage input, IStorage output, IProgressReport progress = null) public static void CopyTo(this IStorage input, IStorage output, IProgressReport progress = null)
{ {
@ -96,7 +92,7 @@ namespace LibHac.IO
while (remaining > 0) while (remaining > 0)
{ {
int toWrite = (int) Math.Min(buffer.Length, remaining); int toWrite = (int) Math.Min(buffer.Length, remaining);
input.Read(buffer, inOffset, toWrite, 0); input.Read(buffer.AsSpan(0, toWrite), inOffset);
output.Write(buffer, 0, toWrite); output.Write(buffer, 0, toWrite);
remaining -= toWrite; remaining -= toWrite;
@ -107,31 +103,31 @@ namespace LibHac.IO
public static void CopyToStream(this IStorage input, Stream output) => CopyToStream(input, output, input.Length); public static void CopyToStream(this IStorage input, Stream output) => CopyToStream(input, output, input.Length);
public static Storage AsStorage(this Stream stream) public static IStorage AsStorage(this Stream stream)
{ {
if (stream == null) return null; if (stream == null) return null;
return new StreamStorage(stream, true); return new StreamStorage(stream, true);
} }
public static Storage AsStorage(this Stream stream, bool keepOpen) public static IStorage AsStorage(this Stream stream, bool keepOpen)
{ {
if (stream == null) return null; if (stream == null) return null;
return new StreamStorage(stream, keepOpen); return new StreamStorage(stream, keepOpen);
} }
public static Storage AsStorage(this Stream stream, long start) public static IStorage AsStorage(this Stream stream, long start)
{ {
if (stream == null) return null; if (stream == null) return null;
return new StreamStorage(stream, true).Slice(start); return new StreamStorage(stream, true).Slice(start);
} }
public static Storage AsStorage(this Stream stream, long start, int length) public static IStorage AsStorage(this Stream stream, long start, int length)
{ {
if (stream == null) return null; if (stream == null) return null;
return new StreamStorage(stream, true).Slice(start, length); return new StreamStorage(stream, true).Slice(start, length);
} }
public static Storage AsStorage(this Stream stream, long start, int length, bool keepOpen) public static IStorage AsStorage(this Stream stream, long start, int length, bool keepOpen)
{ {
if (stream == null) return null; if (stream == null) return null;
return new StreamStorage(stream, keepOpen).Slice(start, length); return new StreamStorage(stream, keepOpen).Slice(start, length);

View file

@ -8,17 +8,20 @@ namespace LibHac.IO
private IStorage BaseStorage { get; } private IStorage BaseStorage { get; }
private bool LeaveOpen { get; } private bool LeaveOpen { get; }
public StorageStream(IStorage baseStorage, bool leaveOpen) public StorageStream(IStorage baseStorage, FileAccess access, bool leaveOpen)
{ {
BaseStorage = baseStorage; BaseStorage = baseStorage;
LeaveOpen = leaveOpen; LeaveOpen = leaveOpen;
Length = baseStorage.Length; Length = baseStorage.Length;
CanRead = access.HasFlag(FileAccess.Read);
CanWrite = access.HasFlag(FileAccess.Write);
} }
public override int Read(byte[] buffer, int offset, int count) public override int Read(byte[] buffer, int offset, int count)
{ {
int toRead = (int) Math.Min(count, Length - Position); int toRead = (int) Math.Min(count, Length - Position);
BaseStorage.Read(buffer, Position, toRead, offset); BaseStorage.Read(buffer.AsSpan(offset, count), Position);
Position += toRead; Position += toRead;
return toRead; return toRead;
@ -26,7 +29,7 @@ namespace LibHac.IO
public override void Write(byte[] buffer, int offset, int count) public override void Write(byte[] buffer, int offset, int count)
{ {
BaseStorage.Write(buffer, Position, count, offset); BaseStorage.Write(buffer.AsSpan(offset, count), Position);
Position += count; Position += count;
} }
@ -58,9 +61,9 @@ namespace LibHac.IO
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override bool CanRead => (BaseStorage as Storage)?.CanRead ?? true; public override bool CanRead { get; }
public override bool CanSeek => true; public override bool CanSeek => true;
public override bool CanWrite => (BaseStorage as Storage)?.CanWrite ?? true; public override bool CanWrite { get; }
public override long Length { get; } public override long Length { get; }
public override long Position { get; set; } public override long Position { get; set; }

View file

@ -7,7 +7,7 @@ using System.Buffers;
namespace LibHac.IO namespace LibHac.IO
{ {
public class StreamStorage : Storage public class StreamStorage : StorageBase
{ {
private Stream BaseStream { get; } private Stream BaseStream { get; }
private object Locker { get; } = new object(); private object Locker { get; } = new object();
@ -20,24 +20,6 @@ namespace LibHac.IO
if (!leaveOpen) ToDispose.Add(BaseStream); if (!leaveOpen) ToDispose.Add(BaseStream);
} }
public override void Read(byte[] buffer, long offset, int count, int bufferOffset)
{
lock (Locker)
{
BaseStream.Position = offset;
BaseStream.Read(buffer, bufferOffset, count);
}
}
public override void Write(byte[] buffer, long offset, int count, int bufferOffset)
{
lock (Locker)
{
BaseStream.Position = offset;
BaseStream.Write(buffer, bufferOffset, count);
}
}
protected override void ReadImpl(Span<byte> destination, long offset) protected override void ReadImpl(Span<byte> destination, long offset)
{ {
#if STREAM_SPAN #if STREAM_SPAN
@ -54,9 +36,17 @@ namespace LibHac.IO
byte[] buffer = ArrayPool<byte>.Shared.Rent(destination.Length); byte[] buffer = ArrayPool<byte>.Shared.Rent(destination.Length);
try try
{ {
Read(buffer, offset, destination.Length, 0); lock (Locker)
{
if (BaseStream.Position != offset)
{
BaseStream.Position = offset;
}
new Span<byte>(buffer, 0, destination.Length).CopyTo(destination); BaseStream.Read(buffer, 0, destination.Length);
}
buffer.AsSpan(0, destination.Length).CopyTo(destination);
} }
finally { ArrayPool<byte>.Shared.Return(buffer); } finally { ArrayPool<byte>.Shared.Return(buffer); }
#endif #endif
@ -67,7 +57,11 @@ namespace LibHac.IO
#if STREAM_SPAN #if STREAM_SPAN
lock (Locker) lock (Locker)
{ {
BaseStream.Position = offset; if (BaseStream.Position != offset)
{
BaseStream.Position = offset;
}
BaseStream.Write(source); BaseStream.Write(source);
} }
#else #else
@ -75,7 +69,16 @@ namespace LibHac.IO
try try
{ {
source.CopyTo(buffer); source.CopyTo(buffer);
Write(buffer, offset, source.Length, 0);
lock (Locker)
{
if (BaseStream.Position != offset)
{
BaseStream.Position = offset;
}
BaseStream.Write(buffer, 0, source.Length);
}
} }
finally { ArrayPool<byte>.Shared.Return(buffer); } finally { ArrayPool<byte>.Shared.Return(buffer); }
#endif #endif

View file

@ -3,11 +3,12 @@ using System.IO;
namespace LibHac.IO namespace LibHac.IO
{ {
public class SubStorage : Storage public class SubStorage : StorageBase
{ {
private IStorage BaseStorage { get; } private IStorage BaseStorage { get; }
private long Offset { get; } private long Offset { get; }
public override long Length { get; } public override long Length { get; }
private FileAccess Access { get; } = FileAccess.ReadWrite;
public SubStorage(IStorage baseStorage, long offset, long length) public SubStorage(IStorage baseStorage, long offset, long length)
{ {
@ -16,6 +17,13 @@ namespace LibHac.IO
Length = length; Length = length;
} }
public SubStorage(SubStorage baseStorage, long offset, long length)
{
BaseStorage = baseStorage.BaseStorage;
Offset = baseStorage.Offset + offset;
Length = length;
}
public SubStorage(IStorage baseStorage, long offset, long length, bool leaveOpen) public SubStorage(IStorage baseStorage, long offset, long length, bool leaveOpen)
: this(baseStorage, offset, length) : this(baseStorage, offset, length)
{ {
@ -30,11 +38,13 @@ namespace LibHac.IO
protected override void ReadImpl(Span<byte> destination, long offset) protected override void ReadImpl(Span<byte> destination, long offset)
{ {
if((Access & FileAccess.Read) == 0) throw new InvalidOperationException("Storage is not readable");
BaseStorage.Read(destination, offset + Offset); BaseStorage.Read(destination, offset + Offset);
} }
protected override void WriteImpl(ReadOnlySpan<byte> source, long offset) protected override void WriteImpl(ReadOnlySpan<byte> source, long offset)
{ {
if((Access & FileAccess.Write) == 0) throw new InvalidOperationException("Storage is not writable");
BaseStorage.Write(source, offset + Offset); BaseStorage.Write(source, offset + Offset);
} }
@ -42,13 +52,5 @@ namespace LibHac.IO
{ {
BaseStorage.Flush(); BaseStorage.Flush();
} }
public override Storage Slice(long start, long length, bool leaveOpen)
{
Storage storage = BaseStorage.Slice(Offset + start, length, true);
if (!leaveOpen) storage.ToDispose.Add(this);
return storage;
}
} }
} }

View file

@ -75,7 +75,7 @@ namespace LibHac
/// <returns>The <see cref="IStorage"/> that provides access to the entire raw NCA file.</returns> /// <returns>The <see cref="IStorage"/> that provides access to the entire raw NCA file.</returns>
public IStorage GetStorage() public IStorage GetStorage()
{ {
return BaseStorage.WithAccess(FileAccess.Read); return BaseStorage.AsReadOnly();
} }
public bool CanOpenSection(int index) public bool CanOpenSection(int index)

View file

@ -46,8 +46,7 @@ namespace NandReaderGui.ViewModel
{ {
DiskInfo disk = SelectedDisk; DiskInfo disk = SelectedDisk;
var storage = new CachedStorage(new DeviceStream(disk.PhysicalName, disk.Length).AsStorage(), disk.SectorSize * 100, 4, true); var storage = new CachedStorage(new DeviceStream(disk.PhysicalName, disk.Length).AsStorage(), disk.SectorSize * 100, 4, true);
storage.SetReadOnly(); Stream stream = storage.AsStream(FileAccess.Read);
Stream stream = storage.AsStream();
Keyset keyset = OpenKeyset(); Keyset keyset = OpenKeyset();
var nand = new Nand(stream, keyset); var nand = new Nand(stream, keyset);