Change IFile and IStorage classes to return Result

This commit is contained in:
Alex Barney 2019-09-03 12:35:34 -05:00
parent d073bdfa54
commit 69e7735666
70 changed files with 868 additions and 506 deletions

View File

@ -30,7 +30,7 @@ namespace LibHac.Fs
SubsectionOffsets = SubsectionEntries.Select(x => x.Offset).ToList();
}
protected override void ReadImpl(Span<byte> destination, long offset)
protected override Result ReadImpl(long offset, Span<byte> destination)
{
AesSubsectionEntry entry = GetSubsectionEntry(offset);
@ -45,7 +45,9 @@ namespace LibHac.Fs
lock (_locker)
{
UpdateCounterSubsection(entry.Counter);
base.ReadImpl(destination.Slice(outPos, bytesToRead), inPos);
Result rc = base.ReadImpl(inPos, destination.Slice(outPos, bytesToRead));
if (rc.IsFailure()) return rc;
}
outPos += bytesToRead;
@ -57,16 +59,18 @@ namespace LibHac.Fs
entry = entry.Next;
}
}
return Result.Success;
}
protected override void WriteImpl(ReadOnlySpan<byte> source, long offset)
protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source)
{
throw new NotImplementedException();
return ResultFs.UnsupportedOperationInAesCtrExStorageWrite.Log();
}
public override void Flush()
public override Result Flush()
{
throw new NotImplementedException();
return Result.Success;
}
private AesSubsectionEntry GetSubsectionEntry(long offset)

View File

@ -58,18 +58,21 @@ namespace LibHac.Fs
Counter = _decryptor.Counter;
}
protected override void ReadImpl(Span<byte> destination, long offset)
protected override Result ReadImpl(long offset, Span<byte> destination)
{
base.ReadImpl(destination, offset);
Result rc = base.ReadImpl(offset, destination);
if (rc.IsFailure()) return rc;
lock (_locker)
{
UpdateCounter(_counterOffset + offset);
_decryptor.TransformBlock(destination);
}
return Result.Success;
}
protected override void WriteImpl(ReadOnlySpan<byte> source, long offset)
protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source)
{
byte[] encrypted = ArrayPool<byte>.Shared.Rent(source.Length);
try
@ -83,12 +86,15 @@ namespace LibHac.Fs
_decryptor.TransformBlock(encryptedSpan);
}
base.WriteImpl(encryptedSpan, offset);
Result rc = base.WriteImpl(offset, encryptedSpan);
if (rc.IsFailure()) return rc;
}
finally
{
ArrayPool<byte>.Shared.Return(encrypted);
}
return Result.Success;
}
private void UpdateCounter(long offset)

View File

@ -37,20 +37,23 @@ namespace LibHac.Fs
_key2 = key2.ToArray();
}
protected override void ReadImpl(Span<byte> destination, long offset)
protected override Result ReadImpl(long offset, Span<byte> destination)
{
int size = destination.Length;
long sectorIndex = offset / SectorSize;
if (_decryptor == null) _decryptor = new Aes128XtsTransform(_key1, _key2, true);
base.ReadImpl(_tempBuffer.AsSpan(0, size), offset);
Result rc = base.ReadImpl(offset, _tempBuffer.AsSpan(0, size));
if (rc.IsFailure()) return rc;
_decryptor.TransformBlock(_tempBuffer, 0, size, (ulong)sectorIndex);
_tempBuffer.AsSpan(0, size).CopyTo(destination);
return Result.Success;
}
protected override void WriteImpl(ReadOnlySpan<byte> source, long offset)
protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source)
{
int size = source.Length;
long sectorIndex = offset / SectorSize;
@ -60,12 +63,12 @@ namespace LibHac.Fs
source.CopyTo(_tempBuffer);
_encryptor.TransformBlock(_tempBuffer, 0, size, (ulong)sectorIndex);
base.WriteImpl(_tempBuffer.AsSpan(0, size), offset);
return base.WriteImpl(offset, _tempBuffer.AsSpan(0, size));
}
public override void Flush()
public override Result Flush()
{
BaseStorage.Flush();
return BaseStorage.Flush();
}
}
}

View File

@ -58,17 +58,20 @@ namespace LibHac.Fs
{
using (IFile file = BaseFileSystem.OpenFile(path, OpenMode.Read))
{
if (file.GetSize() < 0x50)
file.GetSize(out long fileSize).ThrowIfFailure();
if (fileSize < 0x50)
{
return -1;
}
// todo: Use result codes
var buffer = new byte[8];
file.Read(buffer, 0x20);
if (BitConverter.ToUInt32(buffer, 0) != 0x3058414E) return 0;
file.Read(out long _, 0x20, buffer);
if (BitConverter.ToUInt32(buffer, 0) != 0x3058414E) return -1;
file.Read(buffer, 0x48);
file.Read(out long _, 0x48, buffer);
return BitConverter.ToInt64(buffer, 0);
}
}

View File

@ -26,12 +26,14 @@ namespace LibHac.Fs
Header = new AesXtsFileHeader(BaseFile);
baseFile.GetSize(out long fileSize).ThrowIfFailure();
if (!Header.TryDecryptHeader(Path, KekSeed, VerificationKey))
{
ThrowHelper.ThrowResult(ResultFs.AesXtsFileHeaderInvalidKeys, "NAX0 key derivation failed.");
}
if (HeaderLength + Util.AlignUp(Header.Size, 0x10) > baseFile.GetSize())
if (HeaderLength + Util.AlignUp(Header.Size, 0x10) > fileSize)
{
ThrowHelper.ThrowResult(ResultFs.AesXtsFileTooShort, "NAX0 key derivation failed.");
}
@ -49,44 +51,52 @@ namespace LibHac.Fs
return key;
}
public override int Read(Span<byte> destination, long offset, ReadOption options)
public override Result Read(out long bytesRead, long offset, Span<byte> destination, ReadOption options)
{
bytesRead = default;
int toRead = ValidateReadParamsAndGetSize(destination, offset);
BaseStorage.Read(destination.Slice(0, toRead), offset);
Result rc = BaseStorage.Read(offset, destination.Slice(0, toRead));
if (rc.IsFailure()) return rc;
return toRead;
return Result.Success;
}
public override void Write(ReadOnlySpan<byte> source, long offset, WriteOption options)
public override Result Write(long offset, ReadOnlySpan<byte> source, WriteOption options)
{
ValidateWriteParams(source, offset);
BaseStorage.Write(source, offset);
Result rc = BaseStorage.Write(offset, source);
if (rc.IsFailure()) return rc;
if ((options & WriteOption.Flush) != 0)
{
Flush();
return Flush();
}
return Result.Success;
}
public override void Flush()
public override Result Flush()
{
BaseStorage.Flush();
return BaseStorage.Flush();
}
public override long GetSize()
public override Result GetSize(out long size)
{
return Header.Size;
size = Header.Size;
return Result.Success;
}
public override void SetSize(long size)
public override Result SetSize(long size)
{
Header.SetSize(size, VerificationKey);
BaseFile.Write(Header.ToBytes(false), 0);
Result rc = BaseFile.Write(0, Header.ToBytes(false));
if (rc.IsFailure()) return rc;
BaseStorage.SetSize(size);
return BaseStorage.SetSize(size);
}
}
}

View File

@ -21,7 +21,9 @@ namespace LibHac.Fs
public AesXtsFileHeader(IFile aesXtsFile)
{
if (aesXtsFile.GetSize() < 0x80)
aesXtsFile.GetSize(out long fileSize).ThrowIfFailure();
if (fileSize < 0x80)
{
ThrowHelper.ThrowResult(ResultFs.AesXtsFileHeaderTooShort);
}

View File

@ -54,7 +54,7 @@ namespace LibHac.Fs
using (IFile baseFile = BaseFileSystem.OpenFile(path, OpenMode.Write))
{
baseFile.Write(header.ToBytes(false), 0);
baseFile.Write(0, header.ToBytes(false)).ThrowIfFailure();
}
}
@ -235,7 +235,7 @@ namespace LibHac.Fs
using (IFile file = BaseFileSystem.OpenFile(filePath, OpenMode.ReadWrite))
{
file.Write(header.ToBytes(false), 0, WriteOption.Flush);
file.Write(0, header.ToBytes(false), WriteOption.Flush).ThrowIfFailure();
}
}
}

View File

@ -16,7 +16,7 @@ namespace LibHac.Fs
{
BaseStorage = baseStorage;
BlockSize = blockSize;
_length = BaseStorage.GetSize();
BaseStorage.GetSize(out _length).ThrowIfFailure();
if (!leaveOpen) ToDispose.Add(BaseStorage);
@ -30,7 +30,7 @@ namespace LibHac.Fs
public CachedStorage(SectorStorage baseStorage, int cacheSize, bool leaveOpen)
: this(baseStorage, baseStorage.SectorSize, cacheSize, leaveOpen) { }
protected override void ReadImpl(Span<byte> destination, long offset)
protected override Result ReadImpl(long offset, Span<byte> destination)
{
long remaining = destination.Length;
long inOffset = offset;
@ -53,9 +53,11 @@ namespace LibHac.Fs
remaining -= bytesToRead;
}
}
return Result.Success;
}
protected override void WriteImpl(ReadOnlySpan<byte> source, long offset)
protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source)
{
long remaining = source.Length;
long inOffset = offset;
@ -80,9 +82,11 @@ namespace LibHac.Fs
remaining -= bytesToWrite;
}
}
return Result.Success;
}
public override void Flush()
public override Result Flush()
{
lock (Blocks)
{
@ -92,16 +96,26 @@ namespace LibHac.Fs
}
}
BaseStorage.Flush();
return BaseStorage.Flush();
}
public override long GetSize() => _length;
public override void SetSize(long size)
public override Result GetSize(out long size)
{
BaseStorage.SetSize(size);
size = _length;
return Result.Success;
}
_length = BaseStorage.GetSize();
public override Result SetSize(long size)
{
Result rc = BaseStorage.SetSize(size);
if (rc.IsFailure()) return rc;
rc = BaseStorage.GetSize(out long newSize);
if (rc.IsFailure()) return rc;
_length = newSize;
return Result.Success;
}
private CacheBlock GetBlock(long blockIndex)
@ -147,7 +161,7 @@ namespace LibHac.Fs
length = (int)Math.Min(_length - offset, length);
}
BaseStorage.Read(block.Buffer.AsSpan(0, length), offset);
BaseStorage.Read(offset, block.Buffer.AsSpan(0, length)).ThrowIfFailure();
block.Length = length;
block.Index = index;
block.Dirty = false;
@ -158,7 +172,7 @@ namespace LibHac.Fs
if (!block.Dirty) return;
long offset = block.Index * BlockSize;
BaseStorage.Write(block.Buffer.AsSpan(0, block.Length), offset);
BaseStorage.Write(offset, block.Buffer.AsSpan(0, block.Length)).ThrowIfFailure();
block.Dirty = false;
}

View File

@ -22,7 +22,9 @@ namespace LibHac.Fs
for (int i = 0; i < Sources.Count - 1; i++)
{
if (Sources[i].GetSize() != SubFileSize)
Sources[i].GetSize(out long actualSubFileSize).ThrowIfFailure();
if (actualSubFileSize != SubFileSize)
{
throw new ArgumentException($"Source file must have size {subFileSize}");
}
@ -31,33 +33,41 @@ namespace LibHac.Fs
ToDispose.AddRange(Sources);
}
public override int Read(Span<byte> destination, long offset, ReadOption options)
public override Result Read(out long bytesRead, long offset, Span<byte> destination, ReadOption options)
{
bytesRead = default;
long inPos = offset;
int outPos = 0;
int remaining = ValidateReadParamsAndGetSize(destination, offset);
GetSize(out long fileSize).ThrowIfFailure();
while (remaining > 0)
{
int fileIndex = GetSubFileIndexFromOffset(offset);
IFile file = Sources[fileIndex];
long fileOffset = offset - fileIndex * SubFileSize;
long fileEndOffset = Math.Min((fileIndex + 1) * SubFileSize, GetSize());
long fileEndOffset = Math.Min((fileIndex + 1) * SubFileSize, fileSize);
int bytesToRead = (int)Math.Min(fileEndOffset - inPos, remaining);
int bytesRead = file.Read(destination.Slice(outPos, bytesToRead), fileOffset, options);
outPos += bytesRead;
inPos += bytesRead;
remaining -= bytesRead;
Result rc = file.Read(out long subFileBytesRead, fileOffset, destination.Slice(outPos, bytesToRead), options);
if (rc.IsFailure()) return rc;
outPos += (int)subFileBytesRead;
inPos += subFileBytesRead;
remaining -= (int)subFileBytesRead;
if (bytesRead < bytesToRead) break;
}
return outPos;
bytesRead = outPos;
return Result.Success;
}
public override void Write(ReadOnlySpan<byte> source, long offset, WriteOption options)
public override Result Write(long offset, ReadOnlySpan<byte> source, WriteOption options)
{
ValidateWriteParams(source, offset);
@ -65,15 +75,19 @@ namespace LibHac.Fs
long outPos = offset;
int remaining = source.Length;
GetSize(out long fileSize).ThrowIfFailure();
while (remaining > 0)
{
int fileIndex = GetSubFileIndexFromOffset(outPos);
IFile file = Sources[fileIndex];
long fileOffset = outPos - fileIndex * SubFileSize;
long fileEndOffset = Math.Min((fileIndex + 1) * SubFileSize, GetSize());
long fileEndOffset = Math.Min((fileIndex + 1) * SubFileSize, fileSize);
int bytesToWrite = (int)Math.Min(fileEndOffset - outPos, remaining);
file.Write(source.Slice(inPos, bytesToWrite), fileOffset, options);
Result rc = file.Write(fileOffset, source.Slice(inPos, bytesToWrite), options);
if (rc.IsFailure()) return rc;
outPos += bytesToWrite;
inPos += bytesToWrite;
@ -82,35 +96,43 @@ namespace LibHac.Fs
if ((options & WriteOption.Flush) != 0)
{
Flush();
return Flush();
}
return Result.Success;
}
public override void Flush()
public override Result Flush()
{
foreach (IFile file in Sources)
{
file.Flush();
Result rc = file.Flush();
if (rc.IsFailure()) return rc;
}
return Result.Success;
}
public override long GetSize()
public override Result GetSize(out long size)
{
long size = 0;
size = default;
foreach (IFile file in Sources)
{
size += file.GetSize();
Result rc = file.GetSize(out long subFileSize);
if (rc.IsFailure()) return rc;
size += subFileSize;
}
return size;
return Result.Success;
}
public override void SetSize(long size)
public override Result SetSize(long size)
{
long currentSize = GetSize();
GetSize(out long currentSize).ThrowIfFailure();
if (currentSize == size) return;
if (currentSize == size) return Result.Success;
int currentSubFileCount = QuerySubFileCount(currentSize, SubFileSize);
int newSubFileCount = QuerySubFileCount(size, SubFileSize);
@ -120,7 +142,8 @@ namespace LibHac.Fs
IFile currentLastSubFile = Sources[currentSubFileCount - 1];
long newSubFileSize = QuerySubFileSize(currentSubFileCount - 1, size, SubFileSize);
currentLastSubFile.SetSize(newSubFileSize);
Result rc = currentLastSubFile.SetSize(newSubFileSize);
if (rc.IsFailure()) return rc;
for (int i = currentSubFileCount; i < newSubFileCount; i++)
{
@ -143,8 +166,12 @@ namespace LibHac.Fs
}
long newLastFileSize = QuerySubFileSize(newSubFileCount - 1, size, SubFileSize);
Sources[newSubFileCount - 1].SetSize(newLastFileSize);
Result rc = Sources[newSubFileCount - 1].SetSize(newLastFileSize);
if (rc.IsFailure()) return rc;
}
return Result.Success;
}
private int GetSubFileIndexFromOffset(long offset)

View File

@ -16,15 +16,17 @@ namespace LibHac.Fs
long length = 0;
for (int i = 0; i < sources.Count; i++)
{
if (sources[i].GetSize() < 0) throw new ArgumentException("Sources must have an explicit length.");
Sources[i] = new ConcatSource(sources[i], length, sources[i].GetSize());
length += sources[i].GetSize();
sources[i].GetSize(out long sourceSize).ThrowIfFailure();
if (sourceSize < 0) throw new ArgumentException("Sources must have an explicit length.");
Sources[i] = new ConcatSource(sources[i], length, sourceSize);
length += sourceSize;
}
_length = length;
}
protected override void ReadImpl(Span<byte> destination, long offset)
protected override Result ReadImpl(long offset, Span<byte> destination)
{
long inPos = offset;
int outPos = 0;
@ -38,16 +40,20 @@ namespace LibHac.Fs
long entryRemain = entry.StartOffset + entry.Size - inPos;
int bytesToRead = (int)Math.Min(entryRemain, remaining);
entry.Storage.Read(destination.Slice(outPos, bytesToRead), entryPos);
Result rc = entry.Storage.Read(entryPos, destination.Slice(outPos, bytesToRead));
if (rc.IsFailure()) return rc;
outPos += bytesToRead;
inPos += bytesToRead;
remaining -= bytesToRead;
sourceIndex++;
}
return Result.Success;
}
protected override void WriteImpl(ReadOnlySpan<byte> source, long offset)
protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source)
{
long inPos = offset;
int outPos = 0;
@ -61,24 +67,35 @@ namespace LibHac.Fs
long entryRemain = entry.StartOffset + entry.Size - inPos;
int bytesToWrite = (int)Math.Min(entryRemain, remaining);
entry.Storage.Write(source.Slice(outPos, bytesToWrite), entryPos);
Result rc = entry.Storage.Write(entryPos, source.Slice(outPos, bytesToWrite));
if (rc.IsFailure()) return rc;
outPos += bytesToWrite;
inPos += bytesToWrite;
remaining -= bytesToWrite;
sourceIndex++;
}
return Result.Success;
}
public override void Flush()
public override Result Flush()
{
foreach (ConcatSource source in Sources)
{
source.Storage.Flush();
Result rc = source.Storage.Flush();
if (rc.IsFailure()) return rc;
}
return Result.Success;
}
public override long GetSize() => _length;
public override Result GetSize(out long size)
{
size = _length;
return Result.Success;
}
private int FindSource(long offset)
{

View File

@ -41,8 +41,10 @@ namespace LibHac.Fs
sources.Add(new NullStorage(paddingNeeded));
}
segment.Storage.GetSize(out long segmentSize).ThrowIfFailure();
sources.Add(segment.Storage);
offset = segment.Offset + segment.Storage.GetSize();
offset = segment.Offset + segmentSize;
}
return new ConcatenationStorage(sources, true);

View File

@ -20,15 +20,16 @@ namespace LibHac.Fs
public Delta(IStorage deltaStorage)
{
DeltaStorage = deltaStorage;
deltaStorage.GetSize(out long deltaSize).ThrowIfFailure();
if (DeltaStorage.GetSize() < 0x40) throw new InvalidDataException("Delta file is too small.");
if (deltaSize < 0x40) throw new InvalidDataException("Delta file is too small.");
Header = new DeltaHeader(deltaStorage.AsFile(OpenMode.Read));
if (Header.Magic != Ndv0Magic) throw new InvalidDataException("NDV0 magic value is missing.");
long fragmentSize = Header.HeaderSize + Header.BodySize;
if (DeltaStorage.GetSize() < fragmentSize)
if (deltaSize < fragmentSize)
{
throw new InvalidDataException($"Delta file is smaller than the header indicates. (0x{fragmentSize} bytes)");
}
@ -39,8 +40,9 @@ namespace LibHac.Fs
public void SetBaseStorage(IStorage baseStorage)
{
OriginalStorage = baseStorage;
baseStorage.GetSize(out long storageSize).ThrowIfFailure();
if (OriginalStorage.GetSize() != Header.OriginalSize)
if (storageSize != Header.OriginalSize)
{
throw new InvalidDataException($"Original file size does not match the size in the delta header. (0x{Header.OriginalSize} bytes)");
}

View File

@ -16,29 +16,29 @@ namespace LibHac.Fs
ToDispose.Add(BaseFile);
}
public override int Read(Span<byte> destination, long offset, ReadOption options)
public override Result Read(out long bytesRead, long offset, Span<byte> destination, ReadOption options)
{
return BaseFile.Read(destination, offset, options);
return BaseFile.Read(out bytesRead, offset, destination, options);
}
public override void Write(ReadOnlySpan<byte> source, long offset, WriteOption options)
public override Result Write(long offset, ReadOnlySpan<byte> source, WriteOption options)
{
BaseFile.Write(source, offset, options);
return BaseFile.Write(offset, source, options);
}
public override void Flush()
public override Result Flush()
{
BaseFile.Flush();
return BaseFile.Flush();
}
public override long GetSize()
public override Result GetSize(out long size)
{
return BaseFile.GetSize();
return BaseFile.GetSize(out size);
}
public override void SetSize(long size)
public override Result SetSize(long size)
{
BaseFile.SetSize(size);
return BaseFile.SetSize(size);
}
protected override void Dispose(bool disposing)

View File

@ -31,7 +31,7 @@ namespace LibHac.Fs
{
Debug.Assert(count <= BufferSize);
_file.Read(_buffer.AsSpan(0, count), _start + offset);
_file.Read(out long _, _start + offset, _buffer.AsSpan(0, count)).ThrowIfFailure();
if (updatePosition) Position = offset + count;
}
@ -101,7 +101,7 @@ namespace LibHac.Fs
public long ReadInt64(long offset, bool updatePosition)
{
FillBuffer(offset, sizeof(long), updatePosition);
return MemoryMarshal.Read<long>(_buffer);
}
@ -121,16 +121,16 @@ namespace LibHac.Fs
public byte[] ReadBytes(long offset, int length, bool updatePosition)
{
var result = new byte[length];
_file.Read(result, offset);
var bytes = new byte[length];
_file.Read(out long _, offset, bytes).ThrowIfFailure();
if (updatePosition) Position = offset + length;
return result;
return bytes;
}
public void ReadBytes(Span<byte> destination, long offset, bool updatePosition)
{
_file.Read(destination, offset);
_file.Read(out long _, offset, destination).ThrowIfFailure();
if (updatePosition) Position = offset + destination.Length;
}
@ -138,7 +138,7 @@ namespace LibHac.Fs
public string ReadAscii(long offset, int length, bool updatePosition)
{
var bytes = new byte[length];
_file.Read(bytes, offset);
_file.Read(out long _, offset, bytes).ThrowIfFailure();
if (updatePosition) Position = offset + length;
return Encoding.ASCII.GetString(bytes);

View File

@ -11,26 +11,29 @@ namespace LibHac.Fs
BaseFile = baseFile;
}
protected override void ReadImpl(Span<byte> destination, long offset)
protected override Result ReadImpl(long offset, Span<byte> destination)
{
BaseFile.Read(destination, offset);
return BaseFile.Read(out long _, offset, destination);
}
protected override void WriteImpl(ReadOnlySpan<byte> source, long offset)
protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source)
{
BaseFile.Write(source, offset);
return BaseFile.Write(offset, source);
}
public override void Flush()
public override Result Flush()
{
BaseFile.Flush();
return BaseFile.Flush();
}
public override long GetSize() => BaseFile.GetSize();
public override void SetSize(long size)
public override Result GetSize(out long size)
{
BaseFile.SetSize(size);
return BaseFile.GetSize(out size);
}
public override Result SetSize(long size)
{
return BaseFile.SetSize(size);
}
}
}

View File

@ -103,17 +103,23 @@ namespace LibHac.Fs
public static void CopyTo(this IFile file, IFile dest, IProgressReport logger = null)
{
const int bufferSize = 0x8000;
logger?.SetTotal(file.GetSize());
file.GetSize(out long fileSize).ThrowIfFailure();
logger?.SetTotal(fileSize);
byte[] buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
try
{
long inOffset = 0;
int bytesRead;
while ((bytesRead = file.Read(buffer, inOffset)) != 0)
// todo: use result for loop condition
while (true)
{
dest.Write(buffer.AsSpan(0, bytesRead), inOffset);
file.Read(out long bytesRead, inOffset, buffer).ThrowIfFailure();
if (bytesRead == 0) break;
dest.Write(inOffset, buffer.AsSpan(0, (int)bytesRead)).ThrowIfFailure();
inOffset += bytesRead;
logger?.ReportAdd(bytesRead);
}
@ -190,14 +196,14 @@ namespace LibHac.Fs
}
}
public static int Read(this IFile file, Span<byte> destination, long offset)
public static Result Read(this IFile file, out long bytesRead, long offset, Span<byte> destination)
{
return file.Read(destination, offset, ReadOption.None);
return file.Read(out bytesRead, offset, destination, ReadOption.None);
}
public static void Write(this IFile file, ReadOnlySpan<byte> source, long offset)
public static Result Write(this IFile file, long offset, ReadOnlySpan<byte> source)
{
file.Write(source, offset, WriteOption.None);
return file.Write(offset, source, WriteOption.None);
}
public static bool DirectoryExists(this IFileSystem fs, string path)

View File

@ -33,8 +33,9 @@ namespace LibHac.Fs
for (int i = 1; i < Levels.Length; i++)
{
var levelData = new IntegrityVerificationStorage(levelInfo[i], Levels[i - 1], integrityCheckLevel, leaveOpen);
levelData.GetSize(out long levelSize).ThrowIfFailure();
int cacheCount = Math.Min((int)Util.DivideByRoundUp(levelData.GetSize(), levelInfo[i].BlockSize), 4);
int cacheCount = Math.Min((int)Util.DivideByRoundUp(levelSize, levelInfo[i].BlockSize), 4);
Levels[i] = new CachedStorage(levelData, cacheCount, leaveOpen);
LevelValidities[i - 1] = levelData.BlockValidities;
@ -42,7 +43,7 @@ namespace LibHac.Fs
}
DataLevel = Levels[Levels.Length - 1];
_length = DataLevel.GetSize();
DataLevel.GetSize(out _length).ThrowIfFailure();
if (!leaveOpen) ToDispose.Add(DataLevel);
}
@ -92,22 +93,26 @@ namespace LibHac.Fs
return initInfo;
}
protected override void ReadImpl(Span<byte> destination, long offset)
protected override Result ReadImpl(long offset, Span<byte> destination)
{
DataLevel.Read(destination, offset);
return DataLevel.Read(offset, destination);
}
protected override void WriteImpl(ReadOnlySpan<byte> source, long offset)
protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source)
{
DataLevel.Write(source, offset);
return DataLevel.Write(offset, source);
}
public override void Flush()
public override Result Flush()
{
DataLevel.Flush();
return DataLevel.Flush();
}
public override long GetSize() => _length;
public override Result GetSize(out long size)
{
size = _length;
return Result.Success;
}
/// <summary>
/// Checks the hashes of any unchecked blocks and returns the <see cref="Validity"/> of the data.
@ -132,8 +137,10 @@ namespace LibHac.Fs
{
if (validities[i] == Validity.Unchecked)
{
int toRead = (int)Math.Min(storage.GetSize() - blockSize * i, buffer.Length);
storage.Read(buffer.AsSpan(0, toRead), blockSize * i, IntegrityCheckLevel.IgnoreOnInvalid);
storage.GetSize(out long storageSize).ThrowIfFailure();
int toRead = (int)Math.Min(storageSize - blockSize * i, buffer.Length);
storage.Read(blockSize * i, buffer.AsSpan(0, toRead), IntegrityCheckLevel.IgnoreOnInvalid);
}
if (validities[i] == Validity.Invalid)

View File

@ -26,24 +26,22 @@ namespace LibHac.Fs
/// <summary>
/// Reads a sequence of bytes from the current <see cref="IFile"/>.
/// </summary>
/// <param name="bytesRead">If the operation returns successfully, The total number of bytes read into
/// the buffer. This can be less than the size of the buffer if the IFile is too short to fulfill the request.</param>
/// <param name="offset">The offset in the <see cref="IFile"/> at which to begin reading.</param>
/// <param name="destination">The buffer where the read bytes will be stored.
/// The number of bytes read will be no larger than the length of the buffer.</param>
/// <param name="offset">The offset in the <see cref="IFile"/> at which to begin reading.</param>
/// <param name="options">Options for reading from the <see cref="IFile"/>.</param>
/// <returns>The total number of bytes read into the buffer. This can be less than the
/// size of the buffer if the IFile is too short to fulfill the request.</returns>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> is invalid.</exception>
/// <exception cref="NotSupportedException">The file's <see cref="OpenMode"/> does not allow reading.</exception>
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
Result Read(out long bytesRead, long offset, Span<byte> destination, ReadOption options);
/// <summary>
/// Writes a sequence of bytes to the current <see cref="IFile"/>.
/// </summary>
/// <param name="source">The buffer containing the bytes to be written.</param>
/// <param name="offset">The offset in the <see cref="IStorage"/> at which to begin writing.</param>
/// <param name="source">The buffer containing the bytes to be written.</param>
/// <param name="options">Options for writing to the <see cref="IFile"/>.</param>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> is negative.</exception>
/// <exception cref="NotSupportedException">The file's <see cref="OpenMode"/> does not allow this request.</exception>
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
Result Write(long offset, ReadOnlySpan<byte> source, WriteOption options);
/// <summary>
@ -54,15 +52,15 @@ namespace LibHac.Fs
/// <summary>
/// Gets the number of bytes in the file.
/// </summary>
/// <returns>The length of the file in bytes.</returns>
/// <param name="size">If the operation returns successfully, the length of the file in bytes.</param>
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
Result GetSize(out long size);
/// <summary>
/// Sets the size of the file in bytes.
/// </summary>
/// <param name="size">The desired size of the file in bytes.</param>
/// <exception cref="NotSupportedException">If increasing the file size, The file's
/// <see cref="OpenMode"/> does not allow this appending.</exception>
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
Result SetSize(long size);
}
}

View File

@ -27,13 +27,13 @@ namespace LibHac.Fs
_length = BucketTree.BucketOffsets.OffsetEnd;
}
protected override void ReadImpl(Span<byte> destination, long offset)
protected override Result ReadImpl(long offset, Span<byte> destination)
{
RelocationEntry entry = GetRelocationEntry(offset);
if (entry.SourceIndex > Sources.Count)
{
ThrowHelper.ThrowResult(ResultFs.InvalidIndirectStorageSource);
return ResultFs.InvalidIndirectStorageSource.Log();
}
long inPos = offset;
@ -45,7 +45,9 @@ namespace LibHac.Fs
long entryPos = inPos - entry.Offset;
int bytesToRead = (int)Math.Min(entry.OffsetEnd - inPos, remaining);
Sources[entry.SourceIndex].Read(destination.Slice(outPos, bytesToRead), entry.SourceOffset + entryPos);
Result rc = Sources[entry.SourceIndex].Read(entry.SourceOffset + entryPos, destination.Slice(outPos, bytesToRead));
if (rc.IsFailure()) return rc;
outPos += bytesToRead;
inPos += bytesToRead;
@ -56,20 +58,29 @@ namespace LibHac.Fs
entry = entry.Next;
}
}
return Result.Success;
}
protected override void WriteImpl(ReadOnlySpan<byte> source, long offset)
protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source)
{
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationInIndirectStorageWrite);
return ResultFs.UnsupportedOperationInIndirectStorageSetSize.Log();
}
public override void Flush() { }
public override long GetSize() => _length;
public override void SetSize(long size)
public override Result Flush()
{
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationInIndirectStorageSetSize);
return Result.Success;
}
public override Result GetSize(out long size)
{
size = _length;
return Result.Success;
}
public override Result SetSize(long size)
{
return ResultFs.UnsupportedOperationInIndirectStorageSetSize.Log();
}
private RelocationEntry GetRelocationEntry(long offset)

View File

@ -32,7 +32,7 @@ namespace LibHac.Fs
BlockValidities = new Validity[SectorCount];
}
private void ReadImpl(Span<byte> destination, long offset, IntegrityCheckLevel integrityCheckLevel)
private Result ReadImpl(long offset, Span<byte> destination, IntegrityCheckLevel integrityCheckLevel)
{
int count = destination.Length;
@ -52,13 +52,13 @@ namespace LibHac.Fs
if (Type != IntegrityStorageType.Save && !needsHashCheck)
{
BaseStorage.Read(destination, offset);
return;
BaseStorage.Read(offset, destination);
return Result.Success;
}
Span<byte> hashBuffer = stackalloc byte[DigestSize];
long hashPos = blockIndex * DigestSize;
HashStorage.Read(hashBuffer, hashPos);
HashStorage.Read(hashPos, hashBuffer);
if (Type == IntegrityStorageType.Save)
{
@ -66,23 +66,23 @@ namespace LibHac.Fs
{
destination.Clear();
BlockValidities[blockIndex] = Validity.Valid;
return;
return Result.Success;
}
if (!needsHashCheck)
{
BaseStorage.Read(destination, offset);
return;
BaseStorage.Read(offset, destination);
return Result.Success;
}
}
byte[] dataBuffer = ArrayPool<byte>.Shared.Rent(SectorSize);
try
{
BaseStorage.Read(destination, offset);
BaseStorage.Read(offset, destination);
destination.CopyTo(dataBuffer);
if (BlockValidities[blockIndex] != Validity.Unchecked) return;
if (BlockValidities[blockIndex] != Validity.Unchecked) return Result.Success;
int bytesToHash = SectorSize;
@ -112,25 +112,30 @@ namespace LibHac.Fs
{
ArrayPool<byte>.Shared.Return(dataBuffer);
}
return Result.Success;
}
protected override void ReadImpl(Span<byte> destination, long offset)
protected override Result ReadImpl(long offset, Span<byte> destination)
{
ReadImpl(destination, offset, IntegrityCheckLevel);
return ReadImpl(offset, destination, IntegrityCheckLevel);
}
public void Read(Span<byte> destination, long offset, IntegrityCheckLevel integrityCheckLevel)
public Result Read(long offset, Span<byte> destination, IntegrityCheckLevel integrityCheckLevel)
{
ValidateParameters(destination, offset);
ReadImpl(destination, offset, integrityCheckLevel);
return ReadImpl(offset, destination, integrityCheckLevel);
}
protected override void WriteImpl(ReadOnlySpan<byte> source, long offset)
protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source)
{
long blockIndex = offset / SectorSize;
long hashPos = blockIndex * DigestSize;
int toWrite = (int)Math.Min(source.Length, GetSize() - offset);
Result rc = GetSize(out long storageSize);
if (rc.IsFailure()) return rc;
int toWrite = (int)Math.Min(source.Length, storageSize - offset);
byte[] dataBuffer = ArrayPool<byte>.Shared.Rent(SectorSize);
try
@ -143,15 +148,17 @@ namespace LibHac.Fs
Array.Clear(hash, 0, DigestSize);
}
BaseStorage.Write(source, offset);
BaseStorage.Write(offset, source);
HashStorage.Write(hash, hashPos);
HashStorage.Write(hashPos, hash);
BlockValidities[blockIndex] = Validity.Unchecked;
}
finally
{
ArrayPool<byte>.Shared.Return(dataBuffer);
}
return Result.Success;
}
private byte[] DoHash(byte[] buffer, int offset, int count)
@ -180,10 +187,12 @@ namespace LibHac.Fs
}
}
public override void Flush()
public override Result Flush()
{
HashStorage.Flush();
base.Flush();
Result rc = HashStorage.Flush();
if (rc.IsFailure()) return rc;
return base.Flush();
}
public void FsTrim()
@ -195,7 +204,7 @@ namespace LibHac.Fs
for (int i = 0; i < SectorCount; i++)
{
long hashPos = i * DigestSize;
HashStorage.Read(digest, hashPos);
HashStorage.Read(hashPos, digest).ThrowIfFailure();
if (!Util.IsEmpty(digest)) continue;

View File

@ -21,33 +21,31 @@ namespace LibHac.Fs
ToDispose.Add(Stream);
}
public override int Read(Span<byte> destination, long offset, ReadOption options)
public override Result Read(out long bytesRead, long offset, Span<byte> destination, ReadOption options)
{
int toRead = ValidateReadParamsAndGetSize(destination, offset);
File.Read(destination.Slice(0, toRead), offset, options);
return toRead;
return File.Read(out bytesRead, offset, destination.Slice(0, toRead), options);
}
public override void Write(ReadOnlySpan<byte> source, long offset, WriteOption options)
public override Result Write(long offset, ReadOnlySpan<byte> source, WriteOption options)
{
ValidateWriteParams(source, offset);
File.Write(source, offset, options);
return File.Write(offset, source, options);
}
public override void Flush()
public override Result Flush()
{
File.Flush();
return File.Flush();
}
public override long GetSize()
public override Result GetSize(out long size)
{
return File.GetSize();
return File.GetSize(out size);
}
public override void SetSize(long size)
public override Result SetSize(long size)
{
try
{
@ -55,9 +53,10 @@ namespace LibHac.Fs
}
catch (IOException ex) when (ex.HResult == ErrorDiskFull || ex.HResult == ErrorHandleDiskFull)
{
ThrowHelper.ThrowResult(ResultFs.InsufficientFreeSpace, ex);
throw;
return ResultFs.InsufficientFreeSpace.Log();
}
return Result.Success;
}
private static FileAccess GetFileAccess(OpenMode mode)

View File

@ -21,21 +21,24 @@ namespace LibHac.Fs
ToDispose.Add(Stream);
}
protected override void ReadImpl(Span<byte> destination, long offset)
protected override Result ReadImpl(long offset, Span<byte> destination)
{
Storage.Read(destination, offset);
return Storage.Read(offset, destination);
}
protected override void WriteImpl(ReadOnlySpan<byte> source, long offset)
protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source)
{
Storage.Write(source, offset);
return Storage.Write(offset, source);
}
public override void Flush()
public override Result Flush()
{
Storage.Flush();
return Storage.Flush();
}
public override long GetSize() => Storage.GetSize();
public override Result GetSize(out long size)
{
return Storage.GetSize(out size);
}
}
}

View File

@ -38,12 +38,14 @@ namespace LibHac.Fs
_isExpandable = false;
}
protected override void ReadImpl(Span<byte> destination, long offset)
protected override Result ReadImpl(long offset, Span<byte> destination)
{
_buffer.AsSpan((int)(_start + offset), destination.Length).CopyTo(destination);
return Result.Success;
}
protected override void WriteImpl(ReadOnlySpan<byte> source, long offset)
protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source)
{
long requiredCapacity = _start + offset + source.Length;
@ -54,6 +56,8 @@ namespace LibHac.Fs
}
source.CopyTo(_buffer.AsSpan((int)(_start + offset), source.Length));
return Result.Success;
}
public byte[] ToArray()
@ -92,13 +96,17 @@ namespace LibHac.Fs
}
}
public override void Flush() { }
public override Result Flush() => Result.Success;
public override long GetSize() => _length;
public override void SetSize(long size)
public override Result GetSize(out long size)
{
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationInMemoryStorageSetSize);
size = _length;
return Result.Success;
}
public override Result SetSize(long size)
{
return ResultFs.UnsupportedOperationInMemoryStorageSetSize.Log();
}
}
}

View File

@ -117,10 +117,12 @@ namespace LibHac.Fs.NcaUtils
long offset = Header.GetSectionStartOffset(index);
long size = Header.GetSectionSize(index);
if (!Util.IsSubRange(offset, size, BaseStorage.GetSize()))
BaseStorage.GetSize(out long baseSize).ThrowIfFailure();
if (!Util.IsSubRange(offset, size, baseSize))
{
throw new InvalidDataException(
$"Section offset (0x{offset:x}) and length (0x{size:x}) fall outside the total NCA length (0x{BaseStorage.GetSize():x}).");
$"Section offset (0x{offset:x}) and length (0x{size:x}) fall outside the total NCA length (0x{baseSize:x}).");
}
return BaseStorage.Slice(offset, size);
@ -492,7 +494,7 @@ namespace LibHac.Fs.NcaUtils
private int ReadHeaderVersion(IStorage header)
{
Span<byte> buf = stackalloc byte[1];
header.Read(buf, 0x203);
header.Read(0x203, buf).Log();
return buf[0] - '0';
}

View File

@ -95,7 +95,7 @@ namespace LibHac.Fs.NcaUtils
IStorage storage = nca.OpenRawStorage(index);
var data = new byte[size];
storage.Read(data, offset);
storage.Read(offset, data).ThrowIfFailure();
byte[] actualHash = Crypto.ComputeSha256(data, 0, data.Length);
@ -116,7 +116,7 @@ namespace LibHac.Fs.NcaUtils
IStorage decryptedStorage = nca.OpenRawStorage(index);
Span<byte> buffer = stackalloc byte[sizeof(long)];
decryptedStorage.Read(buffer, header.EncryptionTreeOffset + 8);
decryptedStorage.Read(header.EncryptionTreeOffset + 8, buffer).ThrowIfFailure();
long readDataSize = BinaryPrimitives.ReadInt64LittleEndian(buffer);
if (header.EncryptionTreeOffset != readDataSize) return Validity.Invalid;

View File

@ -17,7 +17,7 @@ namespace LibHac.Fs.NcaUtils
public NcaHeader(IStorage headerStorage)
{
_header = new byte[HeaderSize];
headerStorage.Read(_header.Span, 0);
headerStorage.Read(0, _header.Span);
}
public NcaHeader(Keyset keyset, IStorage headerStorage)
@ -188,7 +188,7 @@ namespace LibHac.Fs.NcaUtils
public static byte[] DecryptHeader(Keyset keyset, IStorage storage)
{
var buf = new byte[HeaderSize];
storage.Read(buf, 0);
storage.Read(0, buf);
byte[] key1 = keyset.HeaderKey.AsSpan(0, 0x10).ToArray();
byte[] key2 = keyset.HeaderKey.AsSpan(0x10, 0x10).ToArray();

View File

@ -13,26 +13,34 @@ namespace LibHac.Fs
private long Length { get; }
public override int Read(Span<byte> destination, long offset, ReadOption options)
public override Result Read(out long bytesRead, long offset, Span<byte> destination, ReadOption options)
{
int toRead = ValidateReadParamsAndGetSize(destination, offset);
destination.Slice(0, toRead).Clear();
return toRead;
bytesRead = toRead;
return Result.Success;
}
public override void Write(ReadOnlySpan<byte> source, long offset, WriteOption options)
public override Result Write(long offset, ReadOnlySpan<byte> source, WriteOption options)
{
return Result.Success;
}
public override void Flush()
public override Result Flush()
{
return Result.Success;
}
public override long GetSize() => Length;
public override void SetSize(long size)
public override Result GetSize(out long size)
{
throw new NotSupportedException();
size = Length;
return Result.Success;
}
public override Result SetSize(long size)
{
return ResultFs.UnsupportedOperation.Log();
}
}
}

View File

@ -12,19 +12,26 @@ namespace LibHac.Fs
private long _length;
protected override void ReadImpl(Span<byte> destination, long offset)
protected override Result ReadImpl(long offset, Span<byte> destination)
{
destination.Clear();
return Result.Success;
}
protected override void WriteImpl(ReadOnlySpan<byte> source, long offset)
protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source)
{
return Result.Success;
}
public override void Flush()
public override Result Flush()
{
return Result.Success;
}
public override long GetSize() => _length;
public override Result GetSize(out long size)
{
size = _length;
return Result.Success;
}
}
}

View File

@ -13,21 +13,21 @@ namespace LibHac.Fs
{
BaseFile = baseFile;
LeaveOpen = leaveOpen;
_length = baseFile.GetSize();
baseFile.GetSize(out _length).ThrowIfFailure();
}
public override int Read(byte[] buffer, int offset, int count)
{
int toRead = (int)Math.Min(count, Length - Position);
BaseFile.Read(buffer.AsSpan(offset, toRead), Position);
BaseFile.Read(out long bytesRead, Position, buffer.AsSpan(offset, count));
Position += toRead;
return toRead;
Position += bytesRead;
return (int)bytesRead;
}
public override void Write(byte[] buffer, int offset, int count)
{
BaseFile.Write(buffer.AsSpan(offset, count), Position);
BaseFile.Write(Position, buffer.AsSpan(offset, count));
Position += count;
}
@ -57,9 +57,9 @@ namespace LibHac.Fs
public override void SetLength(long value)
{
BaseFile.SetSize(value);
BaseFile.SetSize(value).ThrowIfFailure();
_length = BaseFile.GetSize();
BaseFile.GetSize(out _length).ThrowIfFailure();
}
public override bool CanRead => BaseFile.Mode.HasFlag(OpenMode.Read);

View File

@ -16,44 +16,53 @@ namespace LibHac.Fs
Size = size;
}
public override int Read(Span<byte> destination, long offset, ReadOption options)
public override Result Read(out long bytesRead, long offset, Span<byte> destination, ReadOption options)
{
bytesRead = default;
int toRead = ValidateReadParamsAndGetSize(destination, offset);
long storageOffset = Offset + offset;
BaseStorage.Read(destination.Slice(0, toRead), storageOffset);
BaseStorage.Read(storageOffset, destination.Slice(0, toRead));
return toRead;
bytesRead = toRead;
return Result.Success;
}
public override void Write(ReadOnlySpan<byte> source, long offset, WriteOption options)
public override Result Write(long offset, ReadOnlySpan<byte> source, WriteOption options)
{
ValidateWriteParams(source, offset);
BaseStorage.Write(source, offset);
Result rc = BaseStorage.Write(offset, source);
if (rc.IsFailure()) return rc;
if ((options & WriteOption.Flush) != 0)
{
BaseStorage.Flush();
return BaseStorage.Flush();
}
return Result.Success;
}
public override void Flush()
public override Result Flush()
{
if ((Mode & OpenMode.Write) != 0)
{
BaseStorage.Flush();
return BaseStorage.Flush();
}
return Result.Success;
}
public override long GetSize()
public override Result GetSize(out long size)
{
return Size;
size = Size;
return Result.Success;
}
public override void SetSize(long size)
public override Result SetSize(long size)
{
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationInPartitionFileSetSize);
return ResultFs.UnsupportedOperationInPartitionFileSetSize.Log();
}
}
}

View File

@ -32,11 +32,13 @@ namespace LibHac.Fs
public void AddFile(string filename, IFile file)
{
file.GetSize(out long fileSize).ThrowIfFailure();
var entry = new Entry
{
Name = filename,
File = file,
Length = file.GetSize(),
Length = fileSize,
Offset = CurrentOffset,
NameLength = Encoding.UTF8.GetByteCount(filename),
HashOffset = 0,
@ -148,7 +150,12 @@ namespace LibHac.Fs
if (entry.HashLength == 0) entry.HashLength = 0x200;
var data = new byte[entry.HashLength];
entry.File.Read(data, entry.HashOffset);
entry.File.Read(out long bytesRead, entry.HashOffset, data);
if (bytesRead != entry.HashLength)
{
throw new ArgumentOutOfRangeException();
}
entry.Hash = sha.ComputeHash(data);
}

View File

@ -11,22 +11,29 @@ namespace LibHac.Fs
BaseFile = baseFile;
}
public override int Read(Span<byte> destination, long offset, ReadOption options)
public override Result Read(out long bytesRead, long offset, Span<byte> destination, ReadOption options)
{
return BaseFile.Read(destination, offset, options);
return BaseFile.Read(out bytesRead, offset, destination, options);
}
public override long GetSize()
public override Result GetSize(out long size)
{
return BaseFile.GetSize();
return BaseFile.GetSize(out size);
}
public override void Flush() { }
public override Result Flush()
{
return Result.Success;
}
public override void Write(ReadOnlySpan<byte> source, long offset, WriteOption options) =>
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyReadOnlyFile);
public override Result Write(long offset, ReadOnlySpan<byte> source, WriteOption options)
{
return ResultFs.UnsupportedOperationModifyReadOnlyFile.Log();
}
public override void SetSize(long size) =>
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyReadOnlyFile);
public override Result SetSize(long size)
{
return ResultFs.UnsupportedOperationModifyReadOnlyFile.Log();
}
}
}

View File

@ -87,8 +87,11 @@
public static Result InvalidOpenModeForWrite => new Result(ModuleFs, 6203);
public static Result UnsupportedOperation => new Result(ModuleFs, 6300);
public static Result SubStorageNotResizable => new Result(ModuleFs, 6302);
public static Result SubStorageNotResizableMiddleOfFile => new Result(ModuleFs, 6302);
public static Result UnsupportedOperationInMemoryStorageSetSize => new Result(ModuleFs, 6316);
public static Result UnsupportedOperationInHierarchicalIvfcStorageSetSize => new Result(ModuleFs, 6304);
public static Result UnsupportedOperationInAesCtrExStorageWrite => new Result(ModuleFs, 6310);
public static Result UnsupportedOperationInIndirectStorageWrite => new Result(ModuleFs, 6324);
public static Result UnsupportedOperationInIndirectStorageSetSize => new Result(ModuleFs, 6325);
public static Result UnsupportedOperationInConcatFsQueryEntry => new Result(ModuleFs, 6359);
@ -108,6 +111,7 @@
public static Result AllocationTableInsufficientFreeBlocks => new Result(ModuleFs, 6707);
public static Result Result6902 => new Result(ModuleFs, 6902);
public static Result MountNameNotFound => new Result(ModuleFs, 6905);
}
}

View File

@ -47,7 +47,7 @@ namespace LibHac.Fs.RomFs
public void AddFile(string path, IFile file)
{
var fileInfo = new RomFileInfo();
long fileSize = file.GetSize();
file.GetSize(out long fileSize).ThrowIfFailure();
fileInfo.Offset = CurrentOffset;
fileInfo.Length = fileSize;
@ -81,7 +81,11 @@ namespace LibHac.Fs.RomFs
sources.Add(new MemoryStorage(header));
sources.AddRange(Sources);
long fileLength = sources.Sum(x => x.GetSize());
long fileLength = sources.Sum(x =>
{
x.GetSize(out long fileSize).ThrowIfFailure();
return fileSize;
});
headerWriter.Write((long)HeaderSize);

View File

@ -16,33 +16,41 @@ namespace LibHac.Fs.RomFs
Size = size;
}
public override int Read(Span<byte> destination, long offset, ReadOption options)
public override Result Read(out long bytesRead, long offset, Span<byte> destination, ReadOption options)
{
bytesRead = default;
int toRead = ValidateReadParamsAndGetSize(destination, offset);
long storageOffset = Offset + offset;
BaseStorage.Read(destination.Slice(0, toRead), storageOffset);
return toRead;
Result rc = BaseStorage.Read(storageOffset, destination.Slice(0, toRead));
if (rc.IsFailure()) return rc;
bytesRead = toRead;
return Result.Success;
}
public override void Write(ReadOnlySpan<byte> source, long offset, WriteOption options)
public override Result Write(long offset, ReadOnlySpan<byte> source, WriteOption options)
{
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyRomFsFile);
return ResultFs.UnsupportedOperationModifyRomFsFile.Log();
}
public override void Flush()
public override Result Flush()
{
return Result.Success;
}
public override long GetSize()
public override Result GetSize(out long size)
{
return Size;
size = Size;
return Result.Success;
}
public override void SetSize(long size)
public override Result SetSize(long size)
{
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyRomFsFile);
return ResultFs.UnsupportedOperationModifyRomFsFile.Log();
}
}
}

View File

@ -356,7 +356,7 @@ namespace LibHac.Fs.Save
Span<byte> buffer = MemoryMarshal.Cast<AllocationTableEntry, byte>(entries.Slice(0, entriesToRead));
BaseStorage.Read(buffer, offset);
BaseStorage.Read(offset, buffer).ThrowIfFailure();
}
private AllocationTableEntry ReadEntry(int entryIndex)
@ -364,7 +364,7 @@ namespace LibHac.Fs.Save
Span<byte> bytes = stackalloc byte[EntrySize];
int offset = entryIndex * EntrySize;
BaseStorage.Read(bytes, offset);
BaseStorage.Read(offset, bytes).ThrowIfFailure();
return GetEntryFromBytes(bytes);
}
@ -377,7 +377,7 @@ namespace LibHac.Fs.Save
ref AllocationTableEntry newEntry = ref GetEntryFromBytes(bytes);
newEntry = entry;
BaseStorage.Write(bytes, offset);
BaseStorage.Write(offset, bytes).ThrowIfFailure();
}
// ReSharper disable once UnusedMember.Local

View File

@ -22,7 +22,7 @@ namespace LibHac.Fs.Save
_length = initialBlock == -1 ? 0 : table.GetListLength(initialBlock) * blockSize;
}
protected override void ReadImpl(Span<byte> destination, long offset)
protected override Result ReadImpl(long offset, Span<byte> destination)
{
var iterator = new AllocationTableIterator(Fat, InitialBlock);
@ -41,15 +41,18 @@ namespace LibHac.Fs.Save
int remainingInSegment = iterator.CurrentSegmentSize * BlockSize - segmentPos;
int bytesToRead = Math.Min(remaining, remainingInSegment);
BaseStorage.Read(destination.Slice(outPos, bytesToRead), physicalOffset);
Result rc = BaseStorage.Read(physicalOffset, destination.Slice(outPos, bytesToRead));
if (rc.IsFailure()) return rc;
outPos += bytesToRead;
inPos += bytesToRead;
remaining -= bytesToRead;
}
return Result.Success;
}
protected override void WriteImpl(ReadOnlySpan<byte> source, long offset)
protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source)
{
var iterator = new AllocationTableIterator(Fat, InitialBlock);
@ -68,27 +71,34 @@ namespace LibHac.Fs.Save
int remainingInSegment = iterator.CurrentSegmentSize * BlockSize - segmentPos;
int bytesToWrite = Math.Min(remaining, remainingInSegment);
BaseStorage.Write(source.Slice(outPos, bytesToWrite), physicalOffset);
Result rc = BaseStorage.Write(physicalOffset, source.Slice(outPos, bytesToWrite));
if (rc.IsFailure()) return rc;
outPos += bytesToWrite;
inPos += bytesToWrite;
remaining -= bytesToWrite;
}
return Result.Success;
}
public override void Flush()
public override Result Flush()
{
BaseStorage.Flush();
return BaseStorage.Flush();
}
public override long GetSize() => _length;
public override Result GetSize(out long size)
{
size = _length;
return Result.Success;
}
public override void SetSize(long size)
public override Result SetSize(long size)
{
int oldBlockCount = (int)Util.DivideByRoundUp(_length, BlockSize);
int newBlockCount = (int)Util.DivideByRoundUp(size, BlockSize);
if (oldBlockCount == newBlockCount) return;
if (oldBlockCount == newBlockCount) return Result.Success;
if (oldBlockCount == 0)
{
@ -97,7 +107,7 @@ namespace LibHac.Fs.Save
_length = newBlockCount * BlockSize;
return;
return Result.Success;
}
if (newBlockCount == 0)
@ -107,7 +117,7 @@ namespace LibHac.Fs.Save
InitialBlock = int.MinValue;
_length = 0;
return;
return Result.Success;
}
if (newBlockCount > oldBlockCount)
@ -124,6 +134,8 @@ namespace LibHac.Fs.Save
}
_length = newBlockCount * BlockSize;
return Result.Success;
}
}
}

View File

@ -19,11 +19,13 @@ namespace LibHac.Fs.Save
BitmapStorage = bitmap;
BlockSize = blockSize;
Bitmap = new DuplexBitmap(BitmapStorage, (int)(bitmap.GetSize() * 8));
_length = DataA.GetSize();
bitmap.GetSize(out long bitmapSize).ThrowIfFailure();
Bitmap = new DuplexBitmap(BitmapStorage, (int)(bitmapSize * 8));
DataA.GetSize(out _length).ThrowIfFailure();
}
protected override void ReadImpl(Span<byte> destination, long offset)
protected override Result ReadImpl(long offset, Span<byte> destination)
{
long inPos = offset;
int outPos = 0;
@ -38,15 +40,18 @@ namespace LibHac.Fs.Save
IStorage data = Bitmap.Bitmap[blockNum] ? DataB : DataA;
data.Read(destination.Slice(outPos, bytesToRead), inPos);
Result rc = data.Read(inPos, destination.Slice(outPos, bytesToRead));
if (rc.IsFailure()) return rc;
outPos += bytesToRead;
inPos += bytesToRead;
remaining -= bytesToRead;
}
return Result.Success;
}
protected override void WriteImpl(ReadOnlySpan<byte> source, long offset)
protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source)
{
long inPos = offset;
int outPos = 0;
@ -61,26 +66,42 @@ namespace LibHac.Fs.Save
IStorage data = Bitmap.Bitmap[blockNum] ? DataB : DataA;
data.Write(source.Slice(outPos, bytesToWrite), inPos);
Result rc = data.Write(inPos, source.Slice(outPos, bytesToWrite));
if (rc.IsFailure()) return rc;
outPos += bytesToWrite;
inPos += bytesToWrite;
remaining -= bytesToWrite;
}
return Result.Success;
}
public override void Flush()
public override Result Flush()
{
BitmapStorage?.Flush();
DataA?.Flush();
DataB?.Flush();
Result rc = BitmapStorage.Flush();
if (rc.IsFailure()) return rc;
rc = DataA.Flush();
if (rc.IsFailure()) return rc;
rc = DataB.Flush();
if (rc.IsFailure()) return rc;
return Result.Success;
}
public override long GetSize() => _length;
public override Result GetSize(out long size)
{
size = _length;
return Result.Success;
}
public void FsTrim()
{
int blockCount = (int)(DataA.GetSize() / BlockSize);
DataA.GetSize(out long dataSize).ThrowIfFailure();
int blockCount = (int)(dataSize / BlockSize);
for (int i = 0; i < blockCount; i++)
{

View File

@ -29,25 +29,29 @@ namespace LibHac.Fs.Save
}
DataLayer = Layers[Layers.Length - 1];
_length = DataLayer.GetSize();
DataLayer.GetSize(out _length).ThrowIfFailure();
}
protected override void ReadImpl(Span<byte> destination, long offset)
protected override Result ReadImpl(long offset, Span<byte> destination)
{
DataLayer.Read(destination, offset);
return DataLayer.Read(offset, destination);
}
protected override void WriteImpl(ReadOnlySpan<byte> source, long offset)
protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source)
{
DataLayer.Write(source, offset);
return DataLayer.Write(offset, source);
}
public override void Flush()
public override Result Flush()
{
DataLayer.Flush();
return DataLayer.Flush();
}
public override long GetSize() => _length;
public override Result GetSize(out long size)
{
size = _length;
return Result.Success;
}
public void FsTrim()
{

View File

@ -31,7 +31,7 @@ namespace LibHac.Fs.Save
if (!leaveOpen) ToDispose.Add(baseStorage);
}
protected override void ReadImpl(Span<byte> destination, long offset)
protected override Result ReadImpl(long offset, Span<byte> destination)
{
long inPos = offset;
int outPos = 0;
@ -46,15 +46,18 @@ namespace LibHac.Fs.Save
int bytesToRead = Math.Min(remaining, BlockSize - blockPos);
BaseStorage.Read(destination.Slice(outPos, bytesToRead), physicalOffset);
Result rc = BaseStorage.Read(physicalOffset, destination.Slice(outPos, bytesToRead));
if (rc.IsFailure()) return rc;
outPos += bytesToRead;
inPos += bytesToRead;
remaining -= bytesToRead;
}
return Result.Success;
}
protected override void WriteImpl(ReadOnlySpan<byte> source, long offset)
protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source)
{
long inPos = offset;
int outPos = 0;
@ -69,20 +72,27 @@ namespace LibHac.Fs.Save
int bytesToWrite = Math.Min(remaining, BlockSize - blockPos);
BaseStorage.Write(source.Slice(outPos, bytesToWrite), physicalOffset);
Result rc = BaseStorage.Write(physicalOffset, source.Slice(outPos, bytesToWrite));
if (rc.IsFailure()) return rc;
outPos += bytesToWrite;
inPos += bytesToWrite;
remaining -= bytesToWrite;
}
return Result.Success;
}
public override void Flush()
public override Result Flush()
{
BaseStorage.Flush();
return BaseStorage.Flush();
}
public override long GetSize() => _length;
public override Result GetSize(out long size)
{
size = _length;
return Result.Success;
}
public IStorage GetBaseStorage() => BaseStorage.AsReadOnly();
public IStorage GetHeaderStorage() => HeaderStorage.AsReadOnly();

View File

@ -46,9 +46,9 @@ namespace LibHac.Fs.Save
Segments = InitSegments(Header, MapEntries);
}
protected override void ReadImpl(Span<byte> destination, long offset)
protected override Result ReadImpl(long offset, Span<byte> destination)
{
if (destination.Length == 0) return;
if (destination.Length == 0) return Result.Success;
MapEntry entry = GetMapEntry(offset);
@ -61,7 +61,7 @@ namespace LibHac.Fs.Save
long entryPos = inPos - entry.VirtualOffset;
int bytesToRead = (int)Math.Min(entry.VirtualOffsetEnd - inPos, remaining);
BaseStorage.Read(destination.Slice(outPos, bytesToRead), entry.PhysicalOffset + entryPos);
BaseStorage.Read(entry.PhysicalOffset + entryPos, destination.Slice(outPos, bytesToRead));
outPos += bytesToRead;
inPos += bytesToRead;
@ -72,11 +72,13 @@ namespace LibHac.Fs.Save
entry = entry.Next;
}
}
return Result.Success;
}
protected override void WriteImpl(ReadOnlySpan<byte> source, long offset)
protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source)
{
if (source.Length == 0) return;
if (source.Length == 0) return Result.Success;
MapEntry entry = GetMapEntry(offset);
@ -89,7 +91,9 @@ namespace LibHac.Fs.Save
long entryPos = inPos - entry.VirtualOffset;
int bytesToWrite = (int)Math.Min(entry.VirtualOffsetEnd - inPos, remaining);
BaseStorage.Write(source.Slice(outPos, bytesToWrite), entry.PhysicalOffset + entryPos);
Result rc = BaseStorage.Write(entry.PhysicalOffset + entryPos, source.Slice(outPos, bytesToWrite));
if (rc.IsFailure()) return rc;
outPos += bytesToWrite;
inPos += bytesToWrite;
@ -100,14 +104,21 @@ namespace LibHac.Fs.Save
entry = entry.Next;
}
}
return Result.Success;
}
public override void Flush()
public override Result Flush()
{
BaseStorage.Flush();
return BaseStorage.Flush();
}
public override long GetSize() => -1;
public override Result GetSize(out long size)
{
// todo: Different result code
size = -1;
return Result.Success;
}
public IStorage GetBaseStorage() => BaseStorage.AsReadOnly();
public IStorage GetHeaderStorage() => HeaderStorage.AsReadOnly();
@ -199,7 +210,7 @@ namespace LibHac.Fs.Save
public class RemapHeader
{
public string Magic { get; }
public uint Verison { get; }
public uint Version { get; }
public int MapEntryCount { get; }
public int MapSegmentCount { get; }
public int SegmentBits { get; }
@ -209,7 +220,7 @@ namespace LibHac.Fs.Save
var reader = new BinaryReader(storage.AsStream());
Magic = reader.ReadAscii(4);
Verison = reader.ReadUInt32();
Version = reader.ReadUInt32();
MapEntryCount = reader.ReadInt32();
MapSegmentCount = reader.ReadInt32();
SegmentBits = reader.ReadInt32();

View File

@ -19,43 +19,51 @@ namespace LibHac.Fs.Save
Size = size;
}
public override int Read(Span<byte> destination, long offset, ReadOption options)
public override Result Read(out long bytesRead, long offset, Span<byte> destination, ReadOption options)
{
bytesRead = default;
int toRead = ValidateReadParamsAndGetSize(destination, offset);
BaseStorage.Read(destination.Slice(0, toRead), offset);
Result rc = BaseStorage.Read(offset, destination.Slice(0, toRead));
if (rc.IsFailure()) return rc;
return toRead;
bytesRead = toRead;
return Result.Success;
}
public override void Write(ReadOnlySpan<byte> source, long offset, WriteOption options)
public override Result Write(long offset, ReadOnlySpan<byte> source, WriteOption options)
{
ValidateWriteParams(source, offset);
BaseStorage.Write(source, offset);
BaseStorage.Write(offset, source);
if ((options & WriteOption.Flush) != 0)
{
Flush();
return Flush();
}
return Result.Success;
}
public override void Flush()
public override Result Flush()
{
BaseStorage.Flush();
return BaseStorage.Flush();
}
public override long GetSize()
public override Result GetSize(out long size)
{
return Size;
size = Size;
return Result.Success;
}
public override void SetSize(long size)
public override Result SetSize(long size)
{
if (size < 0) throw new ArgumentOutOfRangeException(nameof(size));
if (Size == size) return;
if (Size == size) return Result.Success;
BaseStorage.SetSize(size);
Result rc = BaseStorage.SetSize(size);
if (rc.IsFailure()) return rc;
if (!FileTable.TryOpenFile(Path, out SaveFileInfo fileInfo))
{
@ -68,6 +76,8 @@ namespace LibHac.Fs.Save
FileTable.AddFile(Path, ref fileInfo);
Size = size;
return Result.Success;
}
}
}

View File

@ -101,10 +101,10 @@ namespace LibHac.Fs.Save
if (capacity == 0 || length >= capacity)
{
long currentSize = Storage.GetSize();
Storage.GetSize(out long currentSize).ThrowIfFailure();
Storage.SetSize(currentSize + CapacityIncrement);
long newSize = Storage.GetSize();
Storage.GetSize(out long newSize).ThrowIfFailure();
SetListCapacity((int)(newSize / _sizeOfEntry));
}
@ -282,7 +282,7 @@ namespace LibHac.Fs.Save
private int GetListCapacity()
{
Span<byte> buf = stackalloc byte[sizeof(int)];
Storage.Read(buf, 4);
Storage.Read(4, buf).ThrowIfFailure();
return MemoryMarshal.Read<int>(buf);
}
@ -290,7 +290,7 @@ namespace LibHac.Fs.Save
private int GetListLength()
{
Span<byte> buf = stackalloc byte[sizeof(int)];
Storage.Read(buf, 0);
Storage.Read(0, buf).ThrowIfFailure();
return MemoryMarshal.Read<int>(buf);
}
@ -300,7 +300,7 @@ namespace LibHac.Fs.Save
Span<byte> buf = stackalloc byte[sizeof(int)];
MemoryMarshal.Write(buf, ref capacity);
Storage.Write(buf, 4);
Storage.Write(4, buf).ThrowIfFailure();
}
private void SetListLength(int length)
@ -308,7 +308,7 @@ namespace LibHac.Fs.Save
Span<byte> buf = stackalloc byte[sizeof(int)];
MemoryMarshal.Write(buf, ref length);
Storage.Write(buf, 0);
Storage.Write(0, buf).ThrowIfFailure();
}
private void ReadEntry(int index, out SaveFsEntry entry)
@ -352,7 +352,7 @@ namespace LibHac.Fs.Save
Debug.Assert(entry.Length == _sizeOfEntry);
int offset = index * _sizeOfEntry;
Storage.Read(entry, offset);
Storage.Read(offset, entry);
}
private void WriteEntry(int index, Span<byte> entry)
@ -360,7 +360,7 @@ namespace LibHac.Fs.Save
Debug.Assert(entry.Length == _sizeOfEntry);
int offset = index * _sizeOfEntry;
Storage.Write(entry, offset);
Storage.Write(offset, entry);
}
private ref SaveFsEntry GetEntryFromBytes(Span<byte> entry)

View File

@ -15,37 +15,50 @@ namespace LibHac.Fs
{
BaseStorage = baseStorage;
SectorSize = sectorSize;
SectorCount = (int)Util.DivideByRoundUp(BaseStorage.GetSize(), SectorSize);
_length = BaseStorage.GetSize();
baseStorage.GetSize(out long baseSize).ThrowIfFailure();
SectorCount = (int)Util.DivideByRoundUp(baseSize, SectorSize);
_length = baseSize;
if (!leaveOpen) ToDispose.Add(BaseStorage);
}
protected override void ReadImpl(Span<byte> destination, long offset)
protected override Result ReadImpl(long offset, Span<byte> destination)
{
ValidateSize(destination.Length, offset);
BaseStorage.Read(destination, offset);
return BaseStorage.Read(offset, destination);
}
protected override void WriteImpl(ReadOnlySpan<byte> source, long offset)
protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source)
{
ValidateSize(source.Length, offset);
BaseStorage.Write(source, offset);
return BaseStorage.Write(offset, source);
}
public override void Flush()
public override Result Flush()
{
BaseStorage.Flush();
return BaseStorage.Flush();
}
public override long GetSize() => _length;
public override void SetSize(long size)
public override Result GetSize(out long size)
{
BaseStorage.SetSize(size);
size = _length;
return Result.Success;
}
SectorCount = (int)Util.DivideByRoundUp(BaseStorage.GetSize(), SectorSize);
_length = BaseStorage.GetSize();
public override Result SetSize(long size)
{
Result rc = BaseStorage.SetSize(size);
if (rc.IsFailure()) return rc;
rc = BaseStorage.GetSize(out long newSize);
if (rc.IsFailure()) return rc;
SectorCount = (int)Util.DivideByRoundUp(newSize, SectorSize);
_length = newSize;
return Result.Success;
}
/// <summary>

View File

@ -7,15 +7,16 @@ namespace LibHac.Fs
{
public static class StorageExtensions
{
public static void Read(this IStorage storage, byte[] buffer, long offset, int count, int bufferOffset)
public static Result Read(this IStorage storage, long offset, byte[] buffer, int count, int bufferOffset)
{
ValidateStorageParameters(buffer, offset, count, bufferOffset);
storage.Read(buffer.AsSpan(bufferOffset, count), offset);
return storage.Read(offset, buffer.AsSpan(bufferOffset, count));
}
public static void Write(this IStorage storage, byte[] buffer, long offset, int count, int bufferOffset)
public static Result Write(this IStorage storage, long offset, byte[] buffer, int count, int bufferOffset)
{
ValidateStorageParameters(buffer, offset, count, bufferOffset);
storage.Write(buffer.AsSpan(bufferOffset, count), offset);
return storage.Write(offset, buffer.AsSpan(bufferOffset, count));
}
private static void ValidateStorageParameters(byte[] buffer, long offset, int count, int bufferOffset)
@ -28,7 +29,7 @@ namespace LibHac.Fs
public static IStorage Slice(this IStorage storage, long start)
{
long length = storage.GetSize();
storage.GetSize(out long length).ThrowIfFailure();
if (length == -1)
{
@ -53,10 +54,10 @@ namespace LibHac.Fs
return storage.AsReadOnly(true);
}
// Todo: Move out of SubStorage
public static IStorage AsReadOnly(this IStorage storage, bool leaveOpen)
{
return new SubStorage(storage, 0, storage.GetSize(), leaveOpen, FileAccess.Read);
storage.GetSize(out long storageSize).ThrowIfFailure();
return new SubStorage(storage, 0, storageSize, leaveOpen, FileAccess.Read);
}
public static Stream AsStream(this IStorage storage) => new StorageStream(storage, FileAccess.ReadWrite, true);
@ -68,7 +69,11 @@ namespace LibHac.Fs
public static void CopyTo(this IStorage input, IStorage output, IProgressReport progress = null)
{
const int bufferSize = 81920;
long remaining = Math.Min(input.GetSize(), output.GetSize());
input.GetSize(out long inputSize).ThrowIfFailure();
output.GetSize(out long outputSize).ThrowIfFailure();
long remaining = Math.Min(inputSize, outputSize);
if (remaining < 0) throw new ArgumentException("Storage must have an explicit length");
progress?.SetTotal(remaining);
@ -81,8 +86,8 @@ namespace LibHac.Fs
{
int toCopy = (int)Math.Min(bufferSize, remaining);
Span<byte> buf = buffer.AsSpan(0, toCopy);
input.Read(buf, pos);
output.Write(buf, pos);
input.Read(pos, buf);
output.Write(pos, buf);
remaining -= toCopy;
pos += toCopy;
@ -100,7 +105,8 @@ namespace LibHac.Fs
public static void Fill(this IStorage input, byte value, IProgressReport progress = null)
{
input.Fill(value, 0, input.GetSize(), progress);
input.GetSize(out long inputSize).ThrowIfFailure();
input.Fill(value, 0, inputSize, progress);
}
public static void Fill(this IStorage input, byte value, long offset, long count, IProgressReport progress = null)
@ -116,7 +122,7 @@ namespace LibHac.Fs
Span<byte> buf = stackalloc byte[(int)count];
buf.Fill(value);
input.Write(buf, offset);
input.Write(offset, buf);
}
private static void FillLarge(this IStorage input, byte value, long offset, long count, IProgressReport progress = null)
@ -139,7 +145,7 @@ namespace LibHac.Fs
int toFill = (int)Math.Min(bufferSize, remaining);
Span<byte> buf = buffer.AsSpan(0, toFill);
input.Write(buf, pos);
input.Write(pos, buf);
remaining -= toFill;
pos += toFill;
@ -157,9 +163,11 @@ namespace LibHac.Fs
public static void WriteAllBytes(this IStorage input, string filename, IProgressReport progress = null)
{
input.GetSize(out long inputSize).ThrowIfFailure();
using (var outFile = new FileStream(filename, FileMode.Create, FileAccess.Write))
{
input.CopyToStream(outFile, input.GetSize(), progress);
input.CopyToStream(outFile, inputSize, progress);
}
}
@ -167,7 +175,9 @@ namespace LibHac.Fs
{
if (storage == null) return new byte[0];
var arr = new byte[storage.GetSize()];
storage.GetSize(out long storageSize).ThrowIfFailure();
var arr = new byte[storageSize];
storage.CopyTo(new MemoryStorage(arr));
return arr;
}
@ -176,10 +186,12 @@ namespace LibHac.Fs
{
if (storage == null) return new T[0];
var arr = new T[storage.GetSize() / Marshal.SizeOf<T>()];
storage.GetSize(out long storageSize).ThrowIfFailure();
var arr = new T[storageSize / Marshal.SizeOf<T>()];
Span<byte> dest = MemoryMarshal.Cast<T, byte>(arr.AsSpan());
storage.Read(dest, 0);
storage.Read(0, dest);
return arr;
}
@ -194,7 +206,7 @@ namespace LibHac.Fs
while (remaining > 0)
{
int toWrite = (int)Math.Min(buffer.Length, remaining);
input.Read(buffer.AsSpan(0, toWrite), inOffset);
input.Read(inOffset, buffer.AsSpan(0, toWrite));
output.Write(buffer, 0, toWrite);
remaining -= toWrite;
@ -203,7 +215,11 @@ namespace LibHac.Fs
}
}
public static void CopyToStream(this IStorage input, Stream output) => CopyToStream(input, output, input.GetSize());
public static void CopyToStream(this IStorage input, Stream output)
{
input.GetSize(out long inputSize).ThrowIfFailure();
CopyToStream(input, output, inputSize);
}
public static IStorage AsStorage(this Stream stream)
{

View File

@ -12,40 +12,46 @@ namespace LibHac.Fs
Mode = mode;
}
public override int Read(Span<byte> destination, long offset, ReadOption options)
public override Result Read(out long bytesRead, long offset, Span<byte> destination, ReadOption options)
{
bytesRead = default;
int toRead = ValidateReadParamsAndGetSize(destination, offset);
BaseStorage.Read(destination.Slice(0, toRead), offset);
Result rc = BaseStorage.Read(offset, destination.Slice(0, toRead));
if (rc.IsFailure()) return rc;
return toRead;
bytesRead = toRead;
return Result.Success;
}
public override void Write(ReadOnlySpan<byte> source, long offset, WriteOption options)
public override Result Write(long offset, ReadOnlySpan<byte> source, WriteOption options)
{
ValidateWriteParams(source, offset);
BaseStorage.Write(source, offset);
Result rc = BaseStorage.Write(offset, source);
if (rc.IsFailure()) return rc;
if ((options & WriteOption.Flush) != 0)
{
Flush();
return Flush();
}
return Result.Success;
}
public override void Flush()
public override Result Flush()
{
BaseStorage.Flush();
return BaseStorage.Flush();
}
public override long GetSize()
public override Result GetSize(out long size)
{
return BaseStorage.GetSize();
return BaseStorage.GetSize(out size);
}
public override void SetSize(long size)
public override Result SetSize(long size)
{
BaseStorage.SetSize(size);
return BaseStorage.SetSize(size);
}
}
}

View File

@ -13,7 +13,8 @@ namespace LibHac.Fs
{
BaseStorage = baseStorage;
LeaveOpen = leaveOpen;
_length = baseStorage.GetSize();
baseStorage.GetSize(out _length).ThrowIfFailure();
CanRead = access.HasFlag(FileAccess.Read);
CanWrite = access.HasFlag(FileAccess.Write);
@ -22,7 +23,7 @@ namespace LibHac.Fs
public override int Read(byte[] buffer, int offset, int count)
{
int toRead = (int)Math.Min(count, Length - Position);
BaseStorage.Read(buffer.AsSpan(offset, toRead), Position);
BaseStorage.Read(Position, buffer.AsSpan(offset, toRead)).ThrowIfFailure();
Position += toRead;
return toRead;
@ -30,7 +31,7 @@ namespace LibHac.Fs
public override void Write(byte[] buffer, int offset, int count)
{
BaseStorage.Write(buffer.AsSpan(offset, count), Position);
BaseStorage.Write(Position, buffer.AsSpan(offset, count)).ThrowIfFailure();
Position += count;
}
@ -59,9 +60,9 @@ namespace LibHac.Fs
public override void SetLength(long value)
{
BaseStorage.SetSize(value);
BaseStorage.SetSize(value).ThrowIfFailure();
_length = BaseStorage.GetSize();
BaseStorage.GetSize(out _length).ThrowIfFailure();
}
public override bool CanRead { get; }

View File

@ -12,6 +12,8 @@ namespace LibHac.Fs
/// </summary>
public class StreamFile : FileBase
{
// todo: handle Stream exceptions
private Stream BaseStream { get; }
private object Locker { get; } = new object();
@ -21,7 +23,7 @@ namespace LibHac.Fs
Mode = mode;
}
public override int Read(Span<byte> destination, long offset, ReadOption options)
public override Result Read(out long bytesRead, long offset, Span<byte> destination, ReadOption options)
{
#if STREAM_SPAN
lock (Locker)
@ -31,13 +33,13 @@ namespace LibHac.Fs
BaseStream.Position = offset;
}
return BaseStream.Read(destination);
bytesRead = BaseStream.Read(destination);
return Result.Success;
}
#else
byte[] buffer = ArrayPool<byte>.Shared.Rent(destination.Length);
try
{
int bytesRead;
lock (Locker)
{
if (BaseStream.Position != offset)
@ -50,13 +52,13 @@ namespace LibHac.Fs
new Span<byte>(buffer, 0, destination.Length).CopyTo(destination);
return bytesRead;
return Result.Success;
}
finally { ArrayPool<byte>.Shared.Return(buffer); }
#endif
}
public override void Write(ReadOnlySpan<byte> source, long offset, WriteOption options)
public override Result Write(long offset, ReadOnlySpan<byte> source, WriteOption options)
{
#if STREAM_SPAN
lock (Locker)
@ -81,31 +83,36 @@ namespace LibHac.Fs
if ((options & WriteOption.Flush) != 0)
{
Flush();
return Flush();
}
return Result.Success;
}
public override void Flush()
public override Result Flush()
{
lock (Locker)
{
BaseStream.Flush();
return Result.Success;
}
}
public override long GetSize()
public override Result GetSize(out long size)
{
lock (Locker)
{
return BaseStream.Length;
size = BaseStream.Length;
return Result.Success;
}
}
public override void SetSize(long size)
public override Result SetSize(long size)
{
lock (Locker)
{
BaseStream.SetLength(size);
return Result.Success;
}
}
}

View File

@ -9,6 +9,8 @@ namespace LibHac.Fs
{
public class StreamStorage : StorageBase
{
// todo: handle Stream exceptions
private Stream BaseStream { get; }
private object Locker { get; } = new object();
private long _length;
@ -20,7 +22,7 @@ namespace LibHac.Fs
if (!leaveOpen) ToDispose.Add(BaseStream);
}
protected override void ReadImpl(Span<byte> destination, long offset)
protected override Result ReadImpl(long offset, Span<byte> destination)
{
#if STREAM_SPAN
lock (Locker)
@ -50,9 +52,11 @@ namespace LibHac.Fs
}
finally { ArrayPool<byte>.Shared.Return(buffer); }
#endif
return Result.Success;
}
protected override void WriteImpl(ReadOnlySpan<byte> source, long offset)
protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source)
{
#if STREAM_SPAN
lock (Locker)
@ -82,16 +86,24 @@ namespace LibHac.Fs
}
finally { ArrayPool<byte>.Shared.Return(buffer); }
#endif
return Result.Success;
}
public override void Flush()
public override Result Flush()
{
lock (Locker)
{
BaseStream.Flush();
return Result.Success;
}
}
public override long GetSize() => _length;
public override Result GetSize(out long size)
{
size = _length;
return Result.Success;
}
}
}

View File

@ -36,41 +36,53 @@ namespace LibHac.Fs
Access = access;
}
protected override void ReadImpl(Span<byte> destination, long offset)
protected override Result ReadImpl(long offset, Span<byte> destination)
{
if ((Access & FileAccess.Read) == 0) throw new InvalidOperationException("Storage is not readable");
BaseStorage.Read(destination, offset + Offset);
return BaseStorage.Read(offset + Offset, destination);
}
protected override void WriteImpl(ReadOnlySpan<byte> source, long offset)
protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source)
{
if ((Access & FileAccess.Write) == 0) throw new InvalidOperationException("Storage is not writable");
BaseStorage.Write(source, offset + Offset);
return BaseStorage.Write(offset + Offset, source);
}
public override void Flush()
public override Result Flush()
{
BaseStorage.Flush();
return BaseStorage.Flush();
}
public override long GetSize() => _length;
public override void SetSize(long size)
public override Result GetSize(out long size)
{
//if (!IsResizable)
// return 0x313802;
size = _length;
return Result.Success;
}
//if (Offset < 0 || size < 0)
// return 0x2F5C02;
public override Result SetSize(long size)
{
if (BaseStorage == null) return ResultFs.Result6902.Log();
if (BaseStorage.GetSize() != Offset + _length)
// todo: Add IsResizable member
// if (!IsResizable) return ResultFs.SubStorageNotResizable.Log();
if (Offset < 0 || size < 0) return ResultFs.InvalidSize.Log();
Result rc = BaseStorage.GetSize(out long baseSize);
if (rc.IsFailure()) return rc;
if (baseSize != Offset + _length)
{
throw new NotSupportedException("SubStorage cannot be resized unless it is located at the end of the base storage.");
// SubStorage cannot be resized unless it is located at the end of the base storage.
return ResultFs.SubStorageNotResizableMiddleOfFile.Log();
}
BaseStorage.SetSize(Offset + size);
rc = BaseStorage.SetSize(Offset + size);
if (rc.IsFailure()) return rc;
_length = size;
return Result.Success;
}
}
}

View File

@ -12,8 +12,9 @@ namespace LibHac.FsClient.Accessors
public OpenMode OpenMode { get; }
// Todo: Consider removing Mode from interface because OpenMode is in FileAccessor
// Todo: Set WriteState to Error based on returned results
OpenMode IFile.Mode => OpenMode;
public FileAccessor(IFile baseFile, FileSystemAccessor parent, OpenMode mode)
{
File = baseFile;
@ -21,14 +22,14 @@ namespace LibHac.FsClient.Accessors
OpenMode = mode;
}
public int Read(Span<byte> destination, long offset, ReadOption options)
public Result Read(out long bytesRead, long offset, Span<byte> destination, ReadOption options)
{
CheckIfDisposed();
return File.Read(destination, offset, options);
return File.Read(out bytesRead, offset, destination, options);
}
public void Write(ReadOnlySpan<byte> source, long offset, WriteOption options)
public Result Write(long offset, ReadOnlySpan<byte> source, WriteOption options)
{
CheckIfDisposed();
@ -36,35 +37,46 @@ namespace LibHac.FsClient.Accessors
{
WriteState = (WriteState)(~options & WriteOption.Flush);
return;
return Result.Success;
}
File.Write(source, offset, options);
//
Result rc = File.Write(offset, source, options);
WriteState = (WriteState)(~options & WriteOption.Flush);
if (rc.IsSuccess())
{
WriteState = (WriteState)(~options & WriteOption.Flush);
}
return rc;
}
public void Flush()
public Result Flush()
{
CheckIfDisposed();
File.Flush();
Result rc = File.Flush();
WriteState = WriteState.None;
if (rc.IsSuccess())
{
WriteState = WriteState.None;
}
return rc;
}
public long GetSize()
public Result GetSize(out long size)
{
CheckIfDisposed();
return File.GetSize();
return File.GetSize(out size);
}
public void SetSize(long size)
public Result SetSize(long size)
{
CheckIfDisposed();
File.SetSize(size);
return File.SetSize(size);
}
public void Dispose()
@ -75,7 +87,7 @@ namespace LibHac.FsClient.Accessors
{
// Original FS code would return an error:
// ThrowHelper.ThrowResult(ResultsFs.ResultFsWriteStateUnflushed);
Flush();
}

View File

@ -364,22 +364,22 @@ namespace LibHac.FsClient
public int ReadFile(FileHandle handle, Span<byte> destination, long offset, ReadOption option)
{
int bytesRead;
long bytesRead;
if (IsEnabledAccessLog() && IsEnabledHandleAccessLog(handle))
{
TimeSpan startTime = Time.GetCurrent();
bytesRead = handle.File.Read(destination, offset, option);
handle.File.Read(out bytesRead, offset, destination, option).ThrowIfFailure();
TimeSpan endTime = Time.GetCurrent();
OutputAccessLog(startTime, endTime, handle, $", offset: {offset}, size: {destination.Length}");
}
else
{
bytesRead = handle.File.Read(destination, offset, option);
handle.File.Read(out bytesRead, offset, destination, option).ThrowIfFailure();
}
return bytesRead;
return (int)bytesRead;
}
public void WriteFile(FileHandle handle, ReadOnlySpan<byte> source, long offset)
@ -392,7 +392,7 @@ namespace LibHac.FsClient
if (IsEnabledAccessLog() && IsEnabledHandleAccessLog(handle))
{
TimeSpan startTime = Time.GetCurrent();
handle.File.Write(source, offset, option);
handle.File.Write(offset, source, option);
TimeSpan endTime = Time.GetCurrent();
string optionString = (option & WriteOption.Flush) == 0 ? "" : $", write_option: {option}";
@ -401,7 +401,7 @@ namespace LibHac.FsClient
}
else
{
handle.File.Write(source, offset, option);
handle.File.Write(offset, source, option);
}
}
@ -423,7 +423,9 @@ namespace LibHac.FsClient
public long GetFileSize(FileHandle handle)
{
return handle.File.GetSize();
handle.File.GetSize(out long fileSize).ThrowIfFailure();
return fileSize;
}
public void SetFileSize(FileHandle handle, long size)
@ -501,16 +503,16 @@ namespace LibHac.FsClient
handle.Directory.Dispose();
}
}
internal Result FindFileSystem(ReadOnlySpan<char> path, out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
{
fileSystem = default;
Result result = GetMountName(path, out ReadOnlySpan<char> mountName, out subPath);
if (result.IsFailure()) return result;
Result rc = GetMountName(path, out ReadOnlySpan<char> mountName, out subPath);
if (rc.IsFailure()) return rc;
result = MountTable.Find(mountName.ToString(), out fileSystem);
if (result.IsFailure()) return result;
rc = MountTable.Find(mountName.ToString(), out fileSystem);
if (rc.IsFailure()) return rc;
return Result.Success;
}

View File

@ -37,10 +37,10 @@ namespace LibHac.FsService.Creators
string partitionPath = GetPartitionPath(partitionId);
Result subFsResult =
Result rc =
Util.CreateSubFileSystem(out IFileSystem subFileSystem, Config.RootFileSystem, partitionPath, true);
if (subFsResult.IsFailure()) return subFsResult;
if (rc.IsFailure()) return rc;
if (rootPath == string.Empty)
{

View File

@ -103,9 +103,9 @@ namespace LibHac.FsService
if (!IsSystemSaveDataId(attribute.SaveId)) return ResultFs.InvalidArgument.Log();
Result saveFsResult = OpenSaveDataFileSystemImpl(out IFileSystem saveFs, out ulong saveDataId, spaceId,
Result rc = OpenSaveDataFileSystemImpl(out IFileSystem saveFs, out ulong saveDataId, spaceId,
attribute, false, true);
if (saveFsResult.IsFailure()) return saveFsResult.Log();
if (rc.IsFailure()) return rc;
// Missing check if the current title owns the save data or can open it
@ -121,8 +121,8 @@ namespace LibHac.FsService
// Missing permission check
Result res = FsProxyCore.SetSdCardEncryptionSeed(seed);
if (res.IsFailure()) return res;
Result rc = FsProxyCore.SetSdCardEncryptionSeed(seed);
if (rc.IsFailure()) return rc;
// todo: Reset save data indexer

View File

@ -35,35 +35,35 @@ namespace LibHac.FsService
string contentDirPath = default;
IFileSystem baseFileSystem = default;
bool isEncrypted = false;
Result baseFsResult;
Result rc;
switch (storageId)
{
case ContentStorageId.System:
baseFsResult = OpenBisFileSystem(out baseFileSystem, string.Empty, BisPartitionId.System);
rc = OpenBisFileSystem(out baseFileSystem, string.Empty, BisPartitionId.System);
contentDirPath = $"/{ContentDirectoryName}";
break;
case ContentStorageId.User:
baseFsResult = OpenBisFileSystem(out baseFileSystem, string.Empty, BisPartitionId.User);
rc = OpenBisFileSystem(out baseFileSystem, string.Empty, BisPartitionId.User);
contentDirPath = $"/{ContentDirectoryName}";
break;
case ContentStorageId.SdCard:
baseFsResult = OpenSdCardFileSystem(out baseFileSystem);
rc = OpenSdCardFileSystem(out baseFileSystem);
contentDirPath = $"/{NintendoDirectoryName}/{ContentDirectoryName}";
isEncrypted = true;
break;
default:
baseFsResult = ResultFs.InvalidArgument;
rc = ResultFs.InvalidArgument;
break;
}
if (baseFsResult.IsFailure()) return baseFsResult;
if (rc.IsFailure()) return rc;
baseFileSystem.EnsureDirectoryExists(contentDirPath);
Result subFsResult = FsCreators.SubDirectoryFileSystemCreator.Create(out IFileSystem subDirFileSystem,
rc = FsCreators.SubDirectoryFileSystemCreator.Create(out IFileSystem subDirFileSystem,
baseFileSystem, contentDirPath);
if (subFsResult.IsFailure()) return subFsResult;
if (rc.IsFailure()) return rc;
if (!isEncrypted)
{
@ -93,8 +93,8 @@ namespace LibHac.FsService
{
fileSystem = default;
Result openSaveDirResult = OpenSaveDataDirectory(out IFileSystem saveDirFs, spaceId, saveDataRootPath, true);
if (openSaveDirResult.IsFailure()) return openSaveDirResult.Log();
Result rc = OpenSaveDataDirectory(out IFileSystem saveDirFs, spaceId, saveDataRootPath, true);
if (rc.IsFailure()) return rc;
bool allowDirectorySaveData = AllowDirectorySaveData(spaceId, saveDataRootPath);
bool useDeviceUniqueMac = Util.UseDeviceUniqueSaveMac(spaceId);
@ -113,11 +113,11 @@ namespace LibHac.FsService
// Missing save FS cache lookup
Result saveFsResult = FsCreators.SaveDataFileSystemCreator.Create(out IFileSystem saveFs,
rc = FsCreators.SaveDataFileSystemCreator.Create(out IFileSystem saveFs,
out ISaveDataExtraDataAccessor extraDataAccessor, saveDirFs, saveDataId, allowDirectorySaveData,
useDeviceUniqueMac, type, null);
if (saveFsResult.IsFailure()) return saveFsResult.Log();
if (rc.IsFailure()) return rc;
if (cacheExtraData)
{
@ -133,12 +133,12 @@ namespace LibHac.FsService
{
if (openOnHostFs && AllowDirectorySaveData(spaceId, saveDataRootPath))
{
Result hostFsResult = FsCreators.TargetManagerFileSystemCreator.Create(out IFileSystem hostFs, false);
Result rc = FsCreators.TargetManagerFileSystemCreator.Create(out IFileSystem hostFs, false);
if (hostFsResult.IsFailure())
if (rc.IsFailure())
{
fileSystem = default;
return hostFsResult.Log();
return rc;
}
return Util.CreateSubFileSystem(out fileSystem, hostFs, saveDataRootPath, true);
@ -152,44 +152,45 @@ namespace LibHac.FsService
public Result OpenSaveDataDirectoryImpl(out IFileSystem fileSystem, SaveDataSpaceId spaceId, string saveDirName, bool createIfMissing)
{
fileSystem = default;
Result rc;
switch (spaceId)
{
case SaveDataSpaceId.System:
Result sysFsResult = OpenBisFileSystem(out IFileSystem sysFs, string.Empty, BisPartitionId.System);
if (sysFsResult.IsFailure()) return sysFsResult.Log();
rc = OpenBisFileSystem(out IFileSystem sysFs, string.Empty, BisPartitionId.System);
if (rc.IsFailure()) return rc;
return Util.CreateSubFileSystem(out fileSystem, sysFs, saveDirName, createIfMissing);
case SaveDataSpaceId.User:
case SaveDataSpaceId.TemporaryStorage:
Result userFsResult = OpenBisFileSystem(out IFileSystem userFs, string.Empty, BisPartitionId.System);
if (userFsResult.IsFailure()) return userFsResult.Log();
rc = OpenBisFileSystem(out IFileSystem userFs, string.Empty, BisPartitionId.User);
if (rc.IsFailure()) return rc;
return Util.CreateSubFileSystem(out fileSystem, userFs, saveDirName, createIfMissing);
case SaveDataSpaceId.SdSystem:
case SaveDataSpaceId.SdCache:
Result sdFsResult = OpenSdCardFileSystem(out IFileSystem sdFs);
if (sdFsResult.IsFailure()) return sdFsResult.Log();
rc = OpenSdCardFileSystem(out IFileSystem sdFs);
if (rc.IsFailure()) return rc;
string sdSaveDirPath = $"/{NintendoDirectoryName}{saveDirName}";
Result sdSubResult = Util.CreateSubFileSystem(out IFileSystem sdSubFs, sdFs, sdSaveDirPath, createIfMissing);
if (sdSubResult.IsFailure()) return sdSubResult.Log();
rc = Util.CreateSubFileSystem(out IFileSystem sdSubFs, sdFs, sdSaveDirPath, createIfMissing);
if (rc.IsFailure()) return rc;
return FsCreators.EncryptedFileSystemCreator.Create(out fileSystem, sdSubFs,
EncryptedFsKeyId.Save, SdEncryptionSeed);
case SaveDataSpaceId.ProperSystem:
Result sysProperFsResult = OpenBisFileSystem(out IFileSystem sysProperFs, string.Empty, BisPartitionId.SystemProperPartition);
if (sysProperFsResult.IsFailure()) return sysProperFsResult.Log();
rc = OpenBisFileSystem(out IFileSystem sysProperFs, string.Empty, BisPartitionId.SystemProperPartition);
if (rc.IsFailure()) return rc;
return Util.CreateSubFileSystem(out fileSystem, sysProperFs, saveDirName, createIfMissing);
case SaveDataSpaceId.Safe:
Result safeFsResult = OpenBisFileSystem(out IFileSystem safeFs, string.Empty, BisPartitionId.SafeMode);
if (safeFsResult.IsFailure()) return safeFsResult.Log();
rc = OpenBisFileSystem(out IFileSystem safeFs, string.Empty, BisPartitionId.SafeMode);
if (rc.IsFailure()) return rc;
return Util.CreateSubFileSystem(out fileSystem, safeFs, saveDirName, createIfMissing);

View File

@ -198,7 +198,7 @@ namespace LibHac
using (var keyblobDec = new Aes128CtrStorage(
new MemoryStorage(EncryptedKeyblobs[i], 0x20, Keyblobs[i].Length), KeyblobKeys[i], counter, false))
{
keyblobDec.Read(Keyblobs[i], 0);
keyblobDec.Read(0, Keyblobs[i]).ThrowIfFailure();
}
}
}

View File

@ -43,9 +43,11 @@ namespace LibHac
public byte[] DecompressSection(int index)
{
IStorage compStream = OpenSection(index);
var compressed = new byte[compStream.GetSize()];
compStream.Read(compressed, 0);
IStorage compStorage = OpenSection(index);
compStorage.GetSize(out long compressedSize).ThrowIfFailure();
var compressed = new byte[compressedSize];
compStorage.Read(0, compressed).ThrowIfFailure();
return DecompressBlz(compressed);
}

View File

@ -58,8 +58,8 @@ namespace LibHac.Kvdb
key = default;
value = default;
Result sizeResult = GetEntrySize(out int keySize, out int valueSize);
if (sizeResult.IsFailure()) return sizeResult;
Result rc = GetEntrySize(out int keySize, out int valueSize);
if (rc.IsFailure()) return rc;
_position += Unsafe.SizeOf<ImkvdbEntryHeader>();

View File

@ -37,13 +37,13 @@ namespace LibHac.Kvdb
{
var reader = new ImkvdbReader(data);
Result headerResult = reader.ReadHeader(out int entryCount);
if (headerResult.IsFailure()) return headerResult;
Result rc = reader.ReadHeader(out int entryCount);
if (rc.IsFailure()) return rc;
for (int i = 0; i < entryCount; i++)
{
Result entryResult = reader.ReadEntry(out ReadOnlySpan<byte> keyBytes, out ReadOnlySpan<byte> valueBytes);
if (entryResult.IsFailure()) return entryResult;
rc = reader.ReadEntry(out ReadOnlySpan<byte> keyBytes, out ReadOnlySpan<byte> valueBytes);
if (rc.IsFailure()) return rc;
var key = new TKey();
var value = new TValue();

View File

@ -21,7 +21,9 @@ namespace LibHac
if (Header.Magic != "NRO0")
throw new InvalidDataException("NRO0 magic is incorrect!");
if (Header.Size < Storage.GetSize())
Storage.GetSize(out long storageSize).ThrowIfFailure();
if (Header.Size < storageSize)
{
AssetStorage = Storage.Slice(Header.Size);
var assetReader = new BinaryReader(AssetStorage.AsStream());

View File

@ -82,7 +82,7 @@ namespace LibHac
public byte[] DecompressSection()
{
var compressed = new byte[CompressedSize];
OpenSection().Read(compressed, 0);
OpenSection().Read(0, compressed).ThrowIfFailure();
if (IsCompressed)
return Lz4.Decompress(compressed, (int)DecompressedSize);

View File

@ -40,7 +40,7 @@ namespace LibHac
for (int i = 0; i < 0x20; i++)
{
var dec = new Aes128CtrStorage(encStorage, keyset.Package1Keys[i], Counter, true);
dec.Read(decBuffer, 0);
dec.Read(0, decBuffer).ThrowIfFailure();
if (BitConverter.ToUInt32(decBuffer, 0) == Pk11Magic)
{

View File

@ -109,12 +109,12 @@ namespace LibHac
var counter = new byte[0x10];
var decBuffer = new byte[0x10];
storage.Read(counter, 0x100);
storage.Read(0x100, counter).ThrowIfFailure();
for (int i = 0; i < 0x20; i++)
{
var dec = new Aes128CtrStorage(storage.Slice(0x100), keyset.Package2Keys[i], counter, false);
dec.Read(decBuffer, 0x50);
dec.Read(0x50, decBuffer).ThrowIfFailure();
if (BitConverter.ToUInt32(decBuffer, 0) == Pk21Magic)
{

View File

@ -25,7 +25,9 @@ namespace hactoolnet
encryptWatch.Stop();
logger.SetTotal(0);
string rate = Util.GetBytesReadable((long)(src.GetSize() * iterations / encryptWatch.Elapsed.TotalSeconds));
src.GetSize(out long srcSize).ThrowIfFailure();
string rate = Util.GetBytesReadable((long)(srcSize * iterations / encryptWatch.Elapsed.TotalSeconds));
logger.LogMessage($"{label}{rate}/s");
}

View File

@ -19,7 +19,7 @@ namespace hactoolnet
{
IStorage deltaStorage = deltaFile;
Span<byte> magic = stackalloc byte[4];
deltaFile.Read(magic, 0);
deltaFile.Read(0, magic).ThrowIfFailure();
if (MemoryMarshal.Read<uint>(magic) != Ndv0Magic)
{
@ -51,7 +51,9 @@ namespace hactoolnet
using (var outFile = new FileStream(ctx.Options.OutFile, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
IStorage patchedStorage = delta.GetPatchedStorage();
patchedStorage.CopyToStream(outFile, patchedStorage.GetSize(), ctx.Logger);
patchedStorage.GetSize(out long patchedStorageSize).ThrowIfFailure();
patchedStorage.CopyToStream(outFile, patchedStorageSize, ctx.Logger);
}
}
}

View File

@ -17,13 +17,15 @@ namespace hactoolnet
var localFs = new LocalFileSystem(ctx.Options.InFile);
var builder = new RomFsBuilder(localFs);
IStorage romfs = builder.Build();
IStorage romFs = builder.Build();
ctx.Logger.LogMessage($"Building RomFS as {ctx.Options.OutFile}");
romFs.GetSize(out long romFsSize).ThrowIfFailure();
using (var outFile = new FileStream(ctx.Options.OutFile, FileMode.Create, FileAccess.ReadWrite))
{
romfs.CopyToStream(outFile, romfs.GetSize(), ctx.Logger);
romFs.CopyToStream(outFile, romFsSize, ctx.Logger);
}
ctx.Logger.LogMessage($"Finished writing {ctx.Options.OutFile}");
@ -48,9 +50,11 @@ namespace hactoolnet
ctx.Logger.LogMessage($"Building Partition FS as {ctx.Options.OutFile}");
partitionFs.GetSize(out long partitionFsSize).ThrowIfFailure();
using (var outFile = new FileStream(ctx.Options.OutFile, FileMode.Create, FileAccess.ReadWrite))
{
partitionFs.CopyToStream(outFile, partitionFs.GetSize(), ctx.Logger);
partitionFs.CopyToStream(outFile, partitionFsSize, ctx.Logger);
}
ctx.Logger.LogMessage($"Finished writing {ctx.Options.OutFile}");

View File

@ -109,9 +109,12 @@ namespace hactoolnet
{
long bytesToRead = 1024L * 1024 * 1024 * 5;
IStorage storage = OpenStorageByType(NcaSectionType.Data);
var dest = new NullStorage(storage.GetSize());
int iterations = (int)(bytesToRead / storage.GetSize()) + 1;
storage.GetSize(out long sectionSize).ThrowIfFailure();
var dest = new NullStorage(sectionSize);
int iterations = (int)(bytesToRead / sectionSize) + 1;
ctx.Logger.LogMessage(iterations.ToString());
ctx.Logger.StartNewStopWatch();

View File

@ -89,11 +89,13 @@ namespace hactoolnet
Assembly thisAssembly = Assembly.GetExecutingAssembly();
Stream cert = thisAssembly.GetManifestResourceStream("hactoolnet.CA00000003_XS00000020");
builder.AddFile($"{ticket.RightsId.ToHexString()}.cert", cert.AsIFile(OpenMode.Read));
using (var outStream = new FileStream(ctx.Options.NspOut, FileMode.Create, FileAccess.ReadWrite))
{
IStorage builtPfs = builder.Build(PartitionFileSystemType.Standard);
builtPfs.CopyToStream(outStream, builtPfs.GetSize(), ctx.Logger);
builtPfs.GetSize(out long pfsSize).ThrowIfFailure();
builtPfs.CopyToStream(outStream, pfsSize, ctx.Logger);
}
}
}

View File

@ -28,9 +28,11 @@ namespace hactoolnet
if (ctx.Options.RomfsOut != null)
{
romfsStorage.GetSize(out long romFsSize).ThrowIfFailure();
using (var outFile = new FileStream(ctx.Options.RomfsOut, FileMode.Create, FileAccess.ReadWrite))
{
romfsStorage.CopyToStream(outFile, romfsStorage.GetSize(), ctx.Logger);
romfsStorage.CopyToStream(outFile, romFsSize, ctx.Logger);
}
}

View File

@ -64,9 +64,12 @@ namespace hactoolnet
{
using (IFile outFile = save.OpenFile(destFilename, OpenMode.ReadWrite))
{
if (inFile.GetSize() != outFile.GetSize())
inFile.GetSize(out long inFileSize).ThrowIfFailure();
outFile.GetSize(out long outFileSize).ThrowIfFailure();
if (inFileSize != outFileSize)
{
outFile.SetSize(inFile.GetSize());
outFile.SetSize(inFileSize).ThrowIfFailure();
}
inFile.CopyTo(outFile, ctx.Logger);