diff --git a/src/LibHac.Nand/Nand.cs b/src/LibHac.Nand/Nand.cs index f83483ea..ae02b956 100644 --- a/src/LibHac.Nand/Nand.cs +++ b/src/LibHac.Nand/Nand.cs @@ -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); } } diff --git a/src/LibHac/IO/Aes128CtrExStorage.cs b/src/LibHac/IO/Aes128CtrExStorage.cs index 70931a72..6cefd4e8 100644 --- a/src/LibHac/IO/Aes128CtrExStorage.cs +++ b/src/LibHac/IO/Aes128CtrExStorage.cs @@ -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); diff --git a/src/LibHac/IO/AesXtsFile.cs b/src/LibHac/IO/AesXtsFile.cs index 6fb98bc4..f2aec314 100644 --- a/src/LibHac/IO/AesXtsFile.cs +++ b/src/LibHac/IO/AesXtsFile.cs @@ -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); } diff --git a/src/LibHac/IO/CachedStorage.cs b/src/LibHac/IO/CachedStorage.cs index 1ca66a73..6e849e8e 100644 --- a/src/LibHac/IO/CachedStorage.cs +++ b/src/LibHac/IO/CachedStorage.cs @@ -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; } diff --git a/src/LibHac/IO/ConcatenationStorage.cs b/src/LibHac/IO/ConcatenationStorage.cs index 48d7b4ae..cc6e53ce 100644 --- a/src/LibHac/IO/ConcatenationStorage.cs +++ b/src/LibHac/IO/ConcatenationStorage.cs @@ -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) diff --git a/src/LibHac/IO/DeltaFragment.cs b/src/LibHac/IO/DeltaFragment.cs index 4d279383..2c202eac 100644 --- a/src/LibHac/IO/DeltaFragment.cs +++ b/src/LibHac/IO/DeltaFragment.cs @@ -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); } diff --git a/src/LibHac/IO/FileStorage.cs b/src/LibHac/IO/FileStorage.cs index 7de4bdb8..60cfbb93 100644 --- a/src/LibHac/IO/FileStorage.cs +++ b/src/LibHac/IO/FileStorage.cs @@ -2,7 +2,7 @@ namespace LibHac.IO { - public class FileStorage : Storage + public class FileStorage : StorageBase { private IFile BaseFile { get; } diff --git a/src/LibHac/IO/HierarchicalIntegrityVerificationStorage.cs b/src/LibHac/IO/HierarchicalIntegrityVerificationStorage.cs index 913810c5..c91c2932 100644 --- a/src/LibHac/IO/HierarchicalIntegrityVerificationStorage.cs +++ b/src/LibHac/IO/HierarchicalIntegrityVerificationStorage.cs @@ -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); } diff --git a/src/LibHac/IO/IStorage.cs b/src/LibHac/IO/IStorage.cs index cda45dfb..be5c6d2f 100644 --- a/src/LibHac/IO/IStorage.cs +++ b/src/LibHac/IO/IStorage.cs @@ -12,16 +12,6 @@ namespace LibHac.IO /// The offset in the to begin reading from. void Read(Span destination, long offset); - /// - /// Reads a sequence of bytes from the current . - /// - /// The buffer where the read bytes will be stored. - /// The zero-based byte offset in - /// at which to begin storing the data read from the current . - /// The number of bytes to be read from the . - /// The offset in the to begin reading from. - void Read(byte[] buffer, long offset, int count, int bufferOffset); - /// /// Writes a sequence of bytes to the current . /// @@ -29,16 +19,6 @@ namespace LibHac.IO /// The offset in the to begin writing to. void Write(ReadOnlySpan source, long offset); - /// - /// Writes a sequence of bytes to the current . - /// - /// - /// The zero-based byte offset in - /// at which to begin begin copying bytes to the current . - /// The number of bytes to be written to the . - /// The offset in the to begin writing to. - void Write(byte[] buffer, long offset, int count, int bufferOffset); - /// /// Causes any buffered data to be written to the underlying device. /// diff --git a/src/LibHac/IO/IndirectStorage.cs b/src/LibHac/IO/IndirectStorage.cs index 7fbfcc19..3a470840 100644 --- a/src/LibHac/IO/IndirectStorage.cs +++ b/src/LibHac/IO/IndirectStorage.cs @@ -4,7 +4,7 @@ using System.Linq; namespace LibHac.IO { - public class IndirectStorage : Storage + public class IndirectStorage : StorageBase { private List RelocationEntries { get; } private List 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) diff --git a/src/LibHac/IO/IntegrityVerificationStorage.cs b/src/LibHac/IO/IntegrityVerificationStorage.cs index 050173d7..23aefa45 100644 --- a/src/LibHac/IO/IntegrityVerificationStorage.cs +++ b/src/LibHac/IO/IntegrityVerificationStorage.cs @@ -116,16 +116,10 @@ namespace LibHac.IO public void Read(Span 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 source, long offset) { long blockIndex = offset / SectorSize; diff --git a/src/LibHac/IO/MemoryStorage.cs b/src/LibHac/IO/MemoryStorage.cs index 82d966a1..3a3ca272 100644 --- a/src/LibHac/IO/MemoryStorage.cs +++ b/src/LibHac/IO/MemoryStorage.cs @@ -2,7 +2,7 @@ namespace LibHac.IO { - public class MemoryStorage : Storage + public class MemoryStorage : StorageBase { private byte[] Buffer { get; } private int Start { get; } diff --git a/src/LibHac/IO/NullStorage.cs b/src/LibHac/IO/NullStorage.cs index 38cd4e13..37831ce5 100644 --- a/src/LibHac/IO/NullStorage.cs +++ b/src/LibHac/IO/NullStorage.cs @@ -5,7 +5,7 @@ namespace LibHac.IO /// /// An that returns all zeros when read, and does nothing on write. /// - public class NullStorage : Storage + public class NullStorage : StorageBase { public NullStorage() { } public NullStorage(long length) => Length = length; diff --git a/src/LibHac/IO/Save/AllocationTable.cs b/src/LibHac/IO/Save/AllocationTable.cs index 6269912a..654f7dc3 100644 --- a/src/LibHac/IO/Save/AllocationTable.cs +++ b/src/LibHac/IO/Save/AllocationTable.cs @@ -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 diff --git a/src/LibHac/IO/Save/AllocationTableStorage.cs b/src/LibHac/IO/Save/AllocationTableStorage.cs index afd22f0d..0eea011a 100644 --- a/src/LibHac/IO/Save/AllocationTableStorage.cs +++ b/src/LibHac/IO/Save/AllocationTableStorage.cs @@ -2,7 +2,7 @@ namespace LibHac.IO.Save { - public class AllocationTableStorage : Storage + public class AllocationTableStorage : StorageBase { private IStorage BaseStorage { get; } private int BlockSize { get; } diff --git a/src/LibHac/IO/Save/DuplexStorage.cs b/src/LibHac/IO/Save/DuplexStorage.cs index 51c2d439..b8f5a9e3 100644 --- a/src/LibHac/IO/Save/DuplexStorage.cs +++ b/src/LibHac/IO/Save/DuplexStorage.cs @@ -2,7 +2,7 @@ namespace LibHac.IO.Save { - public class DuplexStorage : Storage + public class DuplexStorage : StorageBase { private int BlockSize { get; } private IStorage BitmapStorage { get; } diff --git a/src/LibHac/IO/Save/HierarchicalDuplexStorage.cs b/src/LibHac/IO/Save/HierarchicalDuplexStorage.cs index 15f22402..e569471b 100644 --- a/src/LibHac/IO/Save/HierarchicalDuplexStorage.cs +++ b/src/LibHac/IO/Save/HierarchicalDuplexStorage.cs @@ -2,7 +2,7 @@ namespace LibHac.IO.Save { - public class HierarchicalDuplexStorage : Storage + public class HierarchicalDuplexStorage : StorageBase { private DuplexStorage[] Layers { get; } private DuplexStorage DataLayer { get; } diff --git a/src/LibHac/IO/Save/JournalMap.cs b/src/LibHac/IO/Save/JournalMap.cs index 44ddfbf5..8acf3500 100644 --- a/src/LibHac/IO/Save/JournalMap.cs +++ b/src/LibHac/IO/Save/JournalMap.cs @@ -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 diff --git a/src/LibHac/IO/Save/JournalStorage.cs b/src/LibHac/IO/Save/JournalStorage.cs index 336f718a..6617c755 100644 --- a/src/LibHac/IO/Save/JournalStorage.cs +++ b/src/LibHac/IO/Save/JournalStorage.cs @@ -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 diff --git a/src/LibHac/IO/Save/RemapStorage.cs b/src/LibHac/IO/Save/RemapStorage.cs index 7c260e37..30b27195 100644 --- a/src/LibHac/IO/Save/RemapStorage.cs +++ b/src/LibHac/IO/Save/RemapStorage.cs @@ -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) { diff --git a/src/LibHac/IO/Save/SaveDataFileSystemCore.cs b/src/LibHac/IO/Save/SaveDataFileSystemCore.cs index 787692ee..4ddc9b60 100644 --- a/src/LibHac/IO/Save/SaveDataFileSystemCore.cs +++ b/src/LibHac/IO/Save/SaveDataFileSystemCore.cs @@ -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() { diff --git a/src/LibHac/IO/SectorStorage.cs b/src/LibHac/IO/SectorStorage.cs index a8b7ff5b..96b080ee 100644 --- a/src/LibHac/IO/SectorStorage.cs +++ b/src/LibHac/IO/SectorStorage.cs @@ -2,7 +2,7 @@ namespace LibHac.IO { - public class SectorStorage : Storage + public class SectorStorage : StorageBase { protected IStorage BaseStorage { get; } diff --git a/src/LibHac/IO/Storage.cs b/src/LibHac/IO/Storage.cs deleted file mode 100644 index 0a7cfc7e..00000000 --- a/src/LibHac/IO/Storage.cs +++ /dev/null @@ -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 ToDispose { get; } = new List(); - - protected abstract void ReadImpl(Span destination, long offset); - protected abstract void WriteImpl(ReadOnlySpan source, long offset); - public abstract void Flush(); - public abstract long Length { get; } - - protected FileAccess Access { get; set; } = FileAccess.ReadWrite; - - public void Read(Span 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 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 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"); - } - } - } -} diff --git a/src/LibHac/IO/StorageBase.cs b/src/LibHac/IO/StorageBase.cs new file mode 100644 index 00000000..41ba3c88 --- /dev/null +++ b/src/LibHac/IO/StorageBase.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; + +namespace LibHac.IO +{ + public abstract class StorageBase : IStorage + { + private bool _isDisposed; + protected internal List ToDispose { get; } = new List(); + + protected abstract void ReadImpl(Span destination, long offset); + protected abstract void WriteImpl(ReadOnlySpan source, long offset); + public abstract void Flush(); + public abstract long Length { get; } + + public void Read(Span destination, long offset) + { + ValidateParameters(destination, offset); + ReadImpl(destination, offset); + } + + public void Write(ReadOnlySpan 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 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"); + } + } + } +} diff --git a/src/LibHac/IO/StorageExtensions.cs b/src/LibHac/IO/StorageExtensions.cs index f4d5bcd7..f91b4d07 100644 --- a/src/LibHac/IO/StorageExtensions.cs +++ b/src/LibHac/IO/StorageExtensions.cs @@ -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); diff --git a/src/LibHac/IO/StorageStream.cs b/src/LibHac/IO/StorageStream.cs index c25cd809..7cd63045 100644 --- a/src/LibHac/IO/StorageStream.cs +++ b/src/LibHac/IO/StorageStream.cs @@ -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; } diff --git a/src/LibHac/IO/StreamStorage.cs b/src/LibHac/IO/StreamStorage.cs index 47171143..81efa251 100644 --- a/src/LibHac/IO/StreamStorage.cs +++ b/src/LibHac/IO/StreamStorage.cs @@ -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 destination, long offset) { #if STREAM_SPAN @@ -54,9 +36,17 @@ namespace LibHac.IO byte[] buffer = ArrayPool.Shared.Rent(destination.Length); try { - Read(buffer, offset, destination.Length, 0); + lock (Locker) + { + if (BaseStream.Position != offset) + { + BaseStream.Position = offset; + } - new Span(buffer, 0, destination.Length).CopyTo(destination); + BaseStream.Read(buffer, 0, destination.Length); + } + + buffer.AsSpan(0, destination.Length).CopyTo(destination); } finally { ArrayPool.Shared.Return(buffer); } #endif @@ -67,7 +57,11 @@ namespace LibHac.IO #if STREAM_SPAN lock (Locker) { - BaseStream.Position = offset; + 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.Shared.Return(buffer); } #endif diff --git a/src/LibHac/IO/SubStorage.cs b/src/LibHac/IO/SubStorage.cs index b54f6d76..ebcdd803 100644 --- a/src/LibHac/IO/SubStorage.cs +++ b/src/LibHac/IO/SubStorage.cs @@ -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 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 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; - } } } diff --git a/src/LibHac/Nca.cs b/src/LibHac/Nca.cs index 1299807d..82d8e4b0 100644 --- a/src/LibHac/Nca.cs +++ b/src/LibHac/Nca.cs @@ -75,7 +75,7 @@ namespace LibHac /// The that provides access to the entire raw NCA file. public IStorage GetStorage() { - return BaseStorage.WithAccess(FileAccess.Read); + return BaseStorage.AsReadOnly(); } public bool CanOpenSection(int index) diff --git a/src/NandReaderGui/ViewModel/NandViewModel.cs b/src/NandReaderGui/ViewModel/NandViewModel.cs index 422a0bc1..275b2671 100644 --- a/src/NandReaderGui/ViewModel/NandViewModel.cs +++ b/src/NandReaderGui/ViewModel/NandViewModel.cs @@ -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);