mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Make IStorage interface and classes less complex
This commit is contained in:
parent
30b42eaf34
commit
8e151c4a1c
30 changed files with 165 additions and 262 deletions
|
@ -33,16 +33,14 @@ namespace LibHac.Nand
|
|||
{
|
||||
IStorage encStorage = ProdInfo.Open().AsStorage();
|
||||
var decStorage = new CachedStorage(new Aes128XtsStorage(encStorage, Keyset.BisKeys[0], 0x4000, true), 0x4000, 4, true);
|
||||
decStorage.SetReadOnly();
|
||||
return decStorage.AsStream();
|
||||
return decStorage.AsStream(FileAccess.Read);
|
||||
}
|
||||
|
||||
public FatFileSystemProvider OpenProdInfoF()
|
||||
{
|
||||
IStorage encStorage = ProdInfoF.Open().AsStorage();
|
||||
var decStorage = new CachedStorage(new Aes128XtsStorage(encStorage, Keyset.BisKeys[0], 0x4000, true), 0x4000, 4, true);
|
||||
decStorage.SetReadOnly();
|
||||
var fat = new FatFileSystem(decStorage.AsStream(), Ownership.None);
|
||||
var fat = new FatFileSystem(decStorage.AsStream(FileAccess.Read), Ownership.None);
|
||||
return new FatFileSystemProvider(fat);
|
||||
}
|
||||
|
||||
|
@ -50,8 +48,7 @@ namespace LibHac.Nand
|
|||
{
|
||||
IStorage encStorage = Safe.Open().AsStorage();
|
||||
var decStorage = new CachedStorage(new Aes128XtsStorage(encStorage, Keyset.BisKeys[1], 0x4000, true), 0x4000, 4, true);
|
||||
decStorage.SetReadOnly();
|
||||
var fat = new FatFileSystem(decStorage.AsStream(), Ownership.None);
|
||||
var fat = new FatFileSystem(decStorage.AsStream(FileAccess.Read), Ownership.None);
|
||||
return new FatFileSystemProvider(fat);
|
||||
}
|
||||
|
||||
|
@ -59,8 +56,7 @@ namespace LibHac.Nand
|
|||
{
|
||||
IStorage encStorage = System.Open().AsStorage();
|
||||
var decStorage = new CachedStorage(new Aes128XtsStorage(encStorage, Keyset.BisKeys[2], 0x4000, true), 0x4000, 4, true);
|
||||
decStorage.SetReadOnly();
|
||||
var fat = new FatFileSystem(decStorage.AsStream(), Ownership.None);
|
||||
var fat = new FatFileSystem(decStorage.AsStream(FileAccess.Read), Ownership.None);
|
||||
return new FatFileSystemProvider(fat);
|
||||
}
|
||||
|
||||
|
@ -68,8 +64,7 @@ namespace LibHac.Nand
|
|||
{
|
||||
IStorage encStorage = User.Open().AsStorage();
|
||||
var decStorage = new CachedStorage(new Aes128XtsStorage(encStorage, Keyset.BisKeys[3], 0x4000, true), 0x4000, 4, true);
|
||||
decStorage.SetReadOnly();
|
||||
var fat = new FatFileSystem(decStorage.AsStream(), Ownership.None);
|
||||
var fat = new FatFileSystem(decStorage.AsStream(FileAccess.Read), Ownership.None);
|
||||
return new FatFileSystemProvider(fat);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,8 +60,6 @@ namespace LibHac.IO
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override bool CanWrite => false;
|
||||
|
||||
private AesSubsectionEntry GetSubsectionEntry(long offset)
|
||||
{
|
||||
int index = SubsectionOffsets.BinarySearch(offset);
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace LibHac.IO
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Collections.Generic;
|
|||
|
||||
namespace LibHac.IO
|
||||
{
|
||||
public class CachedStorage : Storage
|
||||
public class CachedStorage : StorageBase
|
||||
{
|
||||
private IStorage BaseStorage { get; }
|
||||
private int BlockSize { get; }
|
||||
|
@ -148,7 +148,7 @@ namespace LibHac.IO
|
|||
if (!block.Dirty) return;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||
|
||||
namespace LibHac.IO
|
||||
{
|
||||
public class ConcatenationStorage : Storage
|
||||
public class ConcatenationStorage : StorageBase
|
||||
{
|
||||
private ConcatSource[] Sources { 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)
|
||||
{
|
||||
foreach (ConcatSource info in Sources)
|
||||
|
|
|
@ -57,7 +57,7 @@ namespace LibHac.IO
|
|||
IStorage source = segment.IsInOriginal ? Original : Delta;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace LibHac.IO
|
||||
{
|
||||
public class FileStorage : Storage
|
||||
public class FileStorage : StorageBase
|
||||
{
|
||||
private IFile BaseFile { get; }
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ using System.Text;
|
|||
|
||||
namespace LibHac.IO
|
||||
{
|
||||
public class HierarchicalIntegrityVerificationStorage : Storage
|
||||
public class HierarchicalIntegrityVerificationStorage : StorageBase
|
||||
{
|
||||
public IStorage[] Levels { get; }
|
||||
public IStorage DataLevel { get; }
|
||||
|
@ -128,7 +128,7 @@ namespace LibHac.IO
|
|||
if (validities[i] == Validity.Unchecked)
|
||||
{
|
||||
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)
|
||||
|
@ -209,6 +209,9 @@ namespace LibHac.IO
|
|||
}
|
||||
|
||||
SaltSource = reader.ReadBytes(0x20);
|
||||
|
||||
if (reader.BaseStream.Position + 0x20 >= reader.BaseStream.Length) return;
|
||||
|
||||
MasterHash = reader.ReadBytes(0x20);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,16 +12,6 @@ namespace LibHac.IO
|
|||
/// <param name="offset">The offset in the <see cref="IStorage"/> to begin reading from.</param>
|
||||
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>
|
||||
/// Writes a sequence of bytes to the current <see cref="IStorage"/>.
|
||||
/// </summary>
|
||||
|
@ -29,16 +19,6 @@ namespace LibHac.IO
|
|||
/// <param name="offset">The offset in the <see cref="IStorage"/> to begin writing to.</param>
|
||||
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>
|
||||
/// Causes any buffered data to be written to the underlying device.
|
||||
/// </summary>
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
|
||||
namespace LibHac.IO
|
||||
{
|
||||
public class IndirectStorage : Storage
|
||||
public class IndirectStorage : StorageBase
|
||||
{
|
||||
private List<RelocationEntry> RelocationEntries { get; }
|
||||
private List<long> RelocationOffsets { get; }
|
||||
|
@ -62,8 +62,6 @@ namespace LibHac.IO
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override long Length { get; }
|
||||
|
||||
private RelocationEntry GetRelocationEntry(long offset)
|
||||
|
|
|
@ -116,16 +116,10 @@ namespace LibHac.IO
|
|||
|
||||
public void Read(Span<byte> destination, long offset, IntegrityCheckLevel integrityCheckLevel)
|
||||
{
|
||||
ValidateSpanParameters(destination, offset);
|
||||
ValidateParameters(destination, offset);
|
||||
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)
|
||||
{
|
||||
long blockIndex = offset / SectorSize;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace LibHac.IO
|
||||
{
|
||||
public class MemoryStorage : Storage
|
||||
public class MemoryStorage : StorageBase
|
||||
{
|
||||
private byte[] Buffer { get; }
|
||||
private int Start { get; }
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace LibHac.IO
|
|||
/// <summary>
|
||||
/// An <see cref="IStorage"/> that returns all zeros when read, and does nothing on write.
|
||||
/// </summary>
|
||||
public class NullStorage : Storage
|
||||
public class NullStorage : StorageBase
|
||||
{
|
||||
public NullStorage() { }
|
||||
public NullStorage(long length) => Length = length;
|
||||
|
|
|
@ -34,8 +34,8 @@ namespace LibHac.IO.Save
|
|||
}
|
||||
}
|
||||
|
||||
public IStorage GetBaseStorage() => BaseStorage.WithAccess(FileAccess.Read);
|
||||
public IStorage GetHeaderStorage() => HeaderStorage.WithAccess(FileAccess.Read);
|
||||
public IStorage GetBaseStorage() => BaseStorage.AsReadOnly();
|
||||
public IStorage GetHeaderStorage() => HeaderStorage.AsReadOnly();
|
||||
}
|
||||
|
||||
public class AllocationTableEntry
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace LibHac.IO.Save
|
||||
{
|
||||
public class AllocationTableStorage : Storage
|
||||
public class AllocationTableStorage : StorageBase
|
||||
{
|
||||
private IStorage BaseStorage { get; }
|
||||
private int BlockSize { get; }
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace LibHac.IO.Save
|
||||
{
|
||||
public class DuplexStorage : Storage
|
||||
public class DuplexStorage : StorageBase
|
||||
{
|
||||
private int BlockSize { get; }
|
||||
private IStorage BitmapStorage { get; }
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace LibHac.IO.Save
|
||||
{
|
||||
public class HierarchicalDuplexStorage : Storage
|
||||
public class HierarchicalDuplexStorage : StorageBase
|
||||
{
|
||||
private DuplexStorage[] Layers { get; }
|
||||
private DuplexStorage DataLayer { get; }
|
||||
|
|
|
@ -50,11 +50,11 @@ namespace LibHac.IO.Save
|
|||
return map;
|
||||
}
|
||||
|
||||
public IStorage GetMapStorage() => MapStorage.WithAccess(FileAccess.Read);
|
||||
public IStorage GetHeaderStorage() => HeaderStorage.WithAccess(FileAccess.Read);
|
||||
public IStorage GetModifiedPhysicalBlocksStorage() => ModifiedPhysicalBlocks.WithAccess(FileAccess.Read);
|
||||
public IStorage GetModifiedVirtualBlocksStorage() => ModifiedVirtualBlocks.WithAccess(FileAccess.Read);
|
||||
public IStorage GetFreeBlocksStorage() => FreeBlocks.WithAccess(FileAccess.Read);
|
||||
public IStorage GetMapStorage() => MapStorage.AsReadOnly();
|
||||
public IStorage GetHeaderStorage() => HeaderStorage.AsReadOnly();
|
||||
public IStorage GetModifiedPhysicalBlocksStorage() => ModifiedPhysicalBlocks.AsReadOnly();
|
||||
public IStorage GetModifiedVirtualBlocksStorage() => ModifiedVirtualBlocks.AsReadOnly();
|
||||
public IStorage GetFreeBlocksStorage() => FreeBlocks.AsReadOnly();
|
||||
}
|
||||
|
||||
public class JournalMapHeader
|
||||
|
|
|
@ -3,7 +3,7 @@ using System.IO;
|
|||
|
||||
namespace LibHac.IO.Save
|
||||
{
|
||||
public class JournalStorage : Storage
|
||||
public class JournalStorage : StorageBase
|
||||
{
|
||||
private IStorage BaseStorage { get; }
|
||||
private IStorage HeaderStorage { get; }
|
||||
|
@ -80,8 +80,8 @@ namespace LibHac.IO.Save
|
|||
BaseStorage.Flush();
|
||||
}
|
||||
|
||||
public IStorage GetBaseStorage() => BaseStorage.WithAccess(FileAccess.Read);
|
||||
public IStorage GetHeaderStorage() => HeaderStorage.WithAccess(FileAccess.Read);
|
||||
public IStorage GetBaseStorage() => BaseStorage.AsReadOnly();
|
||||
public IStorage GetHeaderStorage() => HeaderStorage.AsReadOnly();
|
||||
}
|
||||
|
||||
public class JournalHeader
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.IO;
|
|||
|
||||
namespace LibHac.IO.Save
|
||||
{
|
||||
public class RemapStorage : Storage
|
||||
public class RemapStorage : StorageBase
|
||||
{
|
||||
private IStorage BaseStorage { get; }
|
||||
private IStorage HeaderStorage { get; }
|
||||
|
@ -102,9 +102,9 @@ namespace LibHac.IO.Save
|
|||
BaseStorage.Flush();
|
||||
}
|
||||
|
||||
public IStorage GetBaseStorage() => BaseStorage.WithAccess(FileAccess.Read);
|
||||
public IStorage GetHeaderStorage() => HeaderStorage.WithAccess(FileAccess.Read);
|
||||
public IStorage GetMapEntryStorage() => MapEntryStorage.WithAccess(FileAccess.Read);
|
||||
public IStorage GetBaseStorage() => BaseStorage.AsReadOnly();
|
||||
public IStorage GetHeaderStorage() => HeaderStorage.AsReadOnly();
|
||||
public IStorage GetMapEntryStorage() => MapEntryStorage.AsReadOnly();
|
||||
|
||||
private static RemapSegment[] InitSegments(RemapHeader header, MapEntry[] mapEntries)
|
||||
{
|
||||
|
|
|
@ -152,8 +152,8 @@ namespace LibHac.IO.Save
|
|||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public IStorage GetBaseStorage() => BaseStorage.WithAccess(FileAccess.Read);
|
||||
public IStorage GetHeaderStorage() => HeaderStorage.WithAccess(FileAccess.Read);
|
||||
public IStorage GetBaseStorage() => BaseStorage.AsReadOnly();
|
||||
public IStorage GetHeaderStorage() => HeaderStorage.AsReadOnly();
|
||||
|
||||
private void ReadFileInfo()
|
||||
{
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace LibHac.IO
|
||||
{
|
||||
public class SectorStorage : Storage
|
||||
public class SectorStorage : StorageBase
|
||||
{
|
||||
protected IStorage BaseStorage { get; }
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
62
src/LibHac/IO/StorageBase.cs
Normal file
62
src/LibHac/IO/StorageBase.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ namespace LibHac.IO
|
|||
{
|
||||
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)
|
||||
{
|
||||
|
@ -16,33 +16,29 @@ namespace LibHac.IO
|
|||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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, bool keepOpen) => new StorageStream(storage, keepOpen);
|
||||
public static Stream AsStream(this IStorage storage) => new StorageStream(storage, FileAccess.ReadWrite, true);
|
||||
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)
|
||||
{
|
||||
|
@ -96,7 +92,7 @@ namespace LibHac.IO
|
|||
while (remaining > 0)
|
||||
{
|
||||
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);
|
||||
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 Storage AsStorage(this Stream stream)
|
||||
public static IStorage AsStorage(this Stream stream)
|
||||
{
|
||||
if (stream == null) return null;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
return new StreamStorage(stream, keepOpen).Slice(start, length);
|
||||
|
|
|
@ -8,17 +8,20 @@ namespace LibHac.IO
|
|||
private IStorage BaseStorage { get; }
|
||||
private bool LeaveOpen { get; }
|
||||
|
||||
public StorageStream(IStorage baseStorage, bool leaveOpen)
|
||||
public StorageStream(IStorage baseStorage, FileAccess access, bool leaveOpen)
|
||||
{
|
||||
BaseStorage = baseStorage;
|
||||
LeaveOpen = leaveOpen;
|
||||
Length = baseStorage.Length;
|
||||
|
||||
CanRead = access.HasFlag(FileAccess.Read);
|
||||
CanWrite = access.HasFlag(FileAccess.Write);
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int toRead = (int) Math.Min(count, Length - Position);
|
||||
BaseStorage.Read(buffer, Position, toRead, offset);
|
||||
BaseStorage.Read(buffer.AsSpan(offset, count), Position);
|
||||
|
||||
Position += toRead;
|
||||
return toRead;
|
||||
|
@ -26,7 +29,7 @@ namespace LibHac.IO
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -58,9 +61,9 @@ namespace LibHac.IO
|
|||
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 CanWrite => (BaseStorage as Storage)?.CanWrite ?? true;
|
||||
public override bool CanWrite { get; }
|
||||
public override long Length { get; }
|
||||
public override long Position { get; set; }
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ using System.Buffers;
|
|||
|
||||
namespace LibHac.IO
|
||||
{
|
||||
public class StreamStorage : Storage
|
||||
public class StreamStorage : StorageBase
|
||||
{
|
||||
private Stream BaseStream { get; }
|
||||
private object Locker { get; } = new object();
|
||||
|
@ -20,24 +20,6 @@ namespace LibHac.IO
|
|||
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)
|
||||
{
|
||||
#if STREAM_SPAN
|
||||
|
@ -54,9 +36,17 @@ namespace LibHac.IO
|
|||
byte[] buffer = ArrayPool<byte>.Shared.Rent(destination.Length);
|
||||
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); }
|
||||
#endif
|
||||
|
@ -66,8 +56,12 @@ namespace LibHac.IO
|
|||
{
|
||||
#if STREAM_SPAN
|
||||
lock (Locker)
|
||||
{
|
||||
if (BaseStream.Position != offset)
|
||||
{
|
||||
BaseStream.Position = offset;
|
||||
}
|
||||
|
||||
BaseStream.Write(source);
|
||||
}
|
||||
#else
|
||||
|
@ -75,7 +69,16 @@ namespace LibHac.IO
|
|||
try
|
||||
{
|
||||
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); }
|
||||
#endif
|
||||
|
|
|
@ -3,11 +3,12 @@ using System.IO;
|
|||
|
||||
namespace LibHac.IO
|
||||
{
|
||||
public class SubStorage : Storage
|
||||
public class SubStorage : StorageBase
|
||||
{
|
||||
private IStorage BaseStorage { get; }
|
||||
private long Offset { get; }
|
||||
public override long Length { get; }
|
||||
private FileAccess Access { get; } = FileAccess.ReadWrite;
|
||||
|
||||
public SubStorage(IStorage baseStorage, long offset, long length)
|
||||
{
|
||||
|
@ -16,6 +17,13 @@ namespace LibHac.IO
|
|||
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)
|
||||
: this(baseStorage, offset, length)
|
||||
{
|
||||
|
@ -30,11 +38,13 @@ namespace LibHac.IO
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -42,13 +52,5 @@ namespace LibHac.IO
|
|||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ namespace LibHac
|
|||
/// <returns>The <see cref="IStorage"/> that provides access to the entire raw NCA file.</returns>
|
||||
public IStorage GetStorage()
|
||||
{
|
||||
return BaseStorage.WithAccess(FileAccess.Read);
|
||||
return BaseStorage.AsReadOnly();
|
||||
}
|
||||
|
||||
public bool CanOpenSection(int index)
|
||||
|
|
|
@ -46,8 +46,7 @@ namespace NandReaderGui.ViewModel
|
|||
{
|
||||
DiskInfo disk = SelectedDisk;
|
||||
var storage = new CachedStorage(new DeviceStream(disk.PhysicalName, disk.Length).AsStorage(), disk.SectorSize * 100, 4, true);
|
||||
storage.SetReadOnly();
|
||||
Stream stream = storage.AsStream();
|
||||
Stream stream = storage.AsStream(FileAccess.Read);
|
||||
|
||||
Keyset keyset = OpenKeyset();
|
||||
var nand = new Nand(stream, keyset);
|
||||
|
|
Loading…
Reference in a new issue