mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Change IFile and IStorage classes to return Result
This commit is contained in:
parent
d073bdfa54
commit
69e7735666
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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)");
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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';
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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++)
|
||||
{
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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; }
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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>();
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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}");
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user