mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Return Result from IFileSystem methods
This commit is contained in:
parent
69e7735666
commit
9e9fd19f63
@ -56,7 +56,9 @@ namespace LibHac.Fs
|
||||
{
|
||||
try
|
||||
{
|
||||
using (IFile file = BaseFileSystem.OpenFile(path, OpenMode.Read))
|
||||
BaseFileSystem.OpenFile(out IFile file, path, OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
using (file)
|
||||
{
|
||||
file.GetSize(out long fileSize).ThrowIfFailure();
|
||||
|
||||
|
@ -27,14 +27,14 @@ namespace LibHac.Fs
|
||||
BlockSize = blockSize;
|
||||
}
|
||||
|
||||
public void CreateDirectory(string path)
|
||||
public Result CreateDirectory(string path)
|
||||
{
|
||||
BaseFileSystem.CreateDirectory(path);
|
||||
return BaseFileSystem.CreateDirectory(path);
|
||||
}
|
||||
|
||||
public void CreateFile(string path, long size, CreateFileOptions options)
|
||||
public Result CreateFile(string path, long size, CreateFileOptions options)
|
||||
{
|
||||
CreateFile(path, size, options, new byte[0x20]);
|
||||
return CreateFile(path, size, options, new byte[0x20]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -45,83 +45,109 @@ namespace LibHac.Fs
|
||||
/// <param name="options">Flags to control how the file is created.
|
||||
/// Should usually be <see cref="CreateFileOptions.None"/></param>
|
||||
/// <param name="key">The 256-bit key containing a 128-bit data key followed by a 128-bit tweak key.</param>
|
||||
public void CreateFile(string path, long size, CreateFileOptions options, byte[] key)
|
||||
public Result CreateFile(string path, long size, CreateFileOptions options, byte[] key)
|
||||
{
|
||||
long containerSize = AesXtsFile.HeaderLength + Util.AlignUp(size, 0x10);
|
||||
BaseFileSystem.CreateFile(path, containerSize, options);
|
||||
|
||||
var header = new AesXtsFileHeader(key, size, path, KekSource, ValidationKey);
|
||||
|
||||
using (IFile baseFile = BaseFileSystem.OpenFile(path, OpenMode.Write))
|
||||
Result rc = BaseFileSystem.OpenFile(out IFile baseFile, path, OpenMode.Write);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
using (baseFile)
|
||||
{
|
||||
baseFile.Write(0, header.ToBytes(false)).ThrowIfFailure();
|
||||
rc = baseFile.Write(0, header.ToBytes(false));
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void DeleteDirectory(string path)
|
||||
public Result DeleteDirectory(string path)
|
||||
{
|
||||
BaseFileSystem.DeleteDirectory(path);
|
||||
return BaseFileSystem.DeleteDirectory(path);
|
||||
}
|
||||
|
||||
public void DeleteDirectoryRecursively(string path)
|
||||
public Result DeleteDirectoryRecursively(string path)
|
||||
{
|
||||
BaseFileSystem.DeleteDirectoryRecursively(path);
|
||||
return BaseFileSystem.DeleteDirectoryRecursively(path);
|
||||
}
|
||||
|
||||
public void CleanDirectoryRecursively(string path)
|
||||
public Result CleanDirectoryRecursively(string path)
|
||||
{
|
||||
BaseFileSystem.CleanDirectoryRecursively(path);
|
||||
return BaseFileSystem.CleanDirectoryRecursively(path);
|
||||
}
|
||||
|
||||
public void DeleteFile(string path)
|
||||
public Result DeleteFile(string path)
|
||||
{
|
||||
BaseFileSystem.DeleteFile(path);
|
||||
return BaseFileSystem.DeleteFile(path);
|
||||
}
|
||||
|
||||
public IDirectory OpenDirectory(string path, OpenDirectoryMode mode)
|
||||
public Result OpenDirectory(out IDirectory directory, string path, OpenDirectoryMode mode)
|
||||
{
|
||||
directory = default;
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
IDirectory baseDir = BaseFileSystem.OpenDirectory(path, mode);
|
||||
Result rc = BaseFileSystem.OpenDirectory(out IDirectory baseDir, path, mode);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var dir = new AesXtsDirectory(this, baseDir, mode);
|
||||
return dir;
|
||||
directory = new AesXtsDirectory(this, baseDir, mode);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public IFile OpenFile(string path, OpenMode mode)
|
||||
public Result OpenFile(out IFile file, string path, OpenMode mode)
|
||||
{
|
||||
file = default;
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
IFile baseFile = BaseFileSystem.OpenFile(path, mode | OpenMode.Read);
|
||||
var file = new AesXtsFile(mode, baseFile, path, KekSource, ValidationKey, BlockSize);
|
||||
Result rc = BaseFileSystem.OpenFile(out IFile baseFile, path, mode | OpenMode.Read);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
file.ToDispose.Add(baseFile);
|
||||
return file;
|
||||
var xtsFile = new AesXtsFile(mode, baseFile, path, KekSource, ValidationKey, BlockSize);
|
||||
|
||||
xtsFile.ToDispose.Add(baseFile);
|
||||
|
||||
file = xtsFile;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void RenameDirectory(string srcPath, string dstPath)
|
||||
public Result RenameDirectory(string oldPath, string newPath)
|
||||
{
|
||||
srcPath = PathTools.Normalize(srcPath);
|
||||
dstPath = PathTools.Normalize(dstPath);
|
||||
oldPath = PathTools.Normalize(oldPath);
|
||||
newPath = PathTools.Normalize(newPath);
|
||||
|
||||
BaseFileSystem.RenameDirectory(srcPath, dstPath);
|
||||
// todo: Return proper result codes
|
||||
|
||||
// Official code procedure:
|
||||
// Make sure all file headers can be decrypted
|
||||
// Rename directory to the new path
|
||||
// Reencrypt file headers with new path
|
||||
// If no errors, return
|
||||
// Reencrypt any modified file headers with the old path
|
||||
// Rename directory to the old path
|
||||
|
||||
Result rc = BaseFileSystem.RenameDirectory(oldPath, newPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
try
|
||||
{
|
||||
RenameDirectoryImpl(srcPath, dstPath, false);
|
||||
RenameDirectoryImpl(oldPath, newPath, false);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
RenameDirectoryImpl(srcPath, dstPath, true);
|
||||
BaseFileSystem.RenameDirectory(dstPath, srcPath);
|
||||
RenameDirectoryImpl(oldPath, newPath, true);
|
||||
BaseFileSystem.RenameDirectory(oldPath, newPath);
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private void RenameDirectoryImpl(string srcDir, string dstDir, bool doRollback)
|
||||
{
|
||||
IDirectory dir = OpenDirectory(dstDir, OpenDirectoryMode.All);
|
||||
OpenDirectory(out IDirectory dir, dstDir, OpenDirectoryMode.All);
|
||||
|
||||
foreach (DirectoryEntry entry in dir.Read())
|
||||
{
|
||||
@ -151,56 +177,60 @@ namespace LibHac.Fs
|
||||
}
|
||||
}
|
||||
|
||||
public void RenameFile(string srcPath, string dstPath)
|
||||
public Result RenameFile(string oldPath, string newPath)
|
||||
{
|
||||
srcPath = PathTools.Normalize(srcPath);
|
||||
dstPath = PathTools.Normalize(dstPath);
|
||||
oldPath = PathTools.Normalize(oldPath);
|
||||
newPath = PathTools.Normalize(newPath);
|
||||
|
||||
AesXtsFileHeader header = ReadXtsHeader(srcPath, srcPath);
|
||||
// todo: Return proper result codes
|
||||
|
||||
BaseFileSystem.RenameFile(srcPath, dstPath);
|
||||
AesXtsFileHeader header = ReadXtsHeader(oldPath, oldPath);
|
||||
|
||||
BaseFileSystem.RenameFile(oldPath, newPath);
|
||||
|
||||
try
|
||||
{
|
||||
WriteXtsHeader(header, dstPath, dstPath);
|
||||
WriteXtsHeader(header, newPath, newPath);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
BaseFileSystem.RenameFile(dstPath, srcPath);
|
||||
WriteXtsHeader(header, srcPath, srcPath);
|
||||
BaseFileSystem.RenameFile(newPath, oldPath);
|
||||
WriteXtsHeader(header, oldPath, oldPath);
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public DirectoryEntryType GetEntryType(string path)
|
||||
public Result GetEntryType(out DirectoryEntryType entryType, string path)
|
||||
{
|
||||
return BaseFileSystem.GetEntryType(path);
|
||||
return BaseFileSystem.GetEntryType(out entryType, path);
|
||||
}
|
||||
|
||||
public FileTimeStampRaw GetFileTimeStampRaw(string path)
|
||||
public Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, string path)
|
||||
{
|
||||
return BaseFileSystem.GetFileTimeStampRaw(path);
|
||||
return BaseFileSystem.GetFileTimeStampRaw(out timeStamp, path);
|
||||
}
|
||||
|
||||
public long GetFreeSpaceSize(string path)
|
||||
public Result GetFreeSpaceSize(out long freeSpace, string path)
|
||||
{
|
||||
return BaseFileSystem.GetFreeSpaceSize(path);
|
||||
return BaseFileSystem.GetFreeSpaceSize(out freeSpace, path);
|
||||
}
|
||||
|
||||
public long GetTotalSpaceSize(string path)
|
||||
public Result GetTotalSpaceSize(out long totalSpace, string path)
|
||||
{
|
||||
return BaseFileSystem.GetTotalSpaceSize(path);
|
||||
return BaseFileSystem.GetTotalSpaceSize(out totalSpace, path);
|
||||
}
|
||||
|
||||
public void Commit()
|
||||
public Result Commit()
|
||||
{
|
||||
BaseFileSystem.Commit();
|
||||
return BaseFileSystem.Commit();
|
||||
}
|
||||
|
||||
public void QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, string path, QueryId queryId)
|
||||
public Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, string path)
|
||||
{
|
||||
BaseFileSystem.QueryEntry(outBuffer, inBuffer, path, queryId);
|
||||
return BaseFileSystem.QueryEntry(outBuffer, inBuffer, queryId, path);
|
||||
}
|
||||
|
||||
private AesXtsFileHeader ReadXtsHeader(string filePath, string keyPath)
|
||||
@ -218,7 +248,12 @@ namespace LibHac.Fs
|
||||
Debug.Assert(PathTools.IsNormalized(filePath.AsSpan()));
|
||||
Debug.Assert(PathTools.IsNormalized(keyPath.AsSpan()));
|
||||
|
||||
using (IFile file = BaseFileSystem.OpenFile(filePath, OpenMode.Read))
|
||||
header = null;
|
||||
|
||||
Result rc = BaseFileSystem.OpenFile(out IFile file, filePath, OpenMode.Read);
|
||||
if (rc.IsFailure()) return false;
|
||||
|
||||
using (file)
|
||||
{
|
||||
header = new AesXtsFileHeader(file);
|
||||
|
||||
@ -233,7 +268,9 @@ namespace LibHac.Fs
|
||||
|
||||
header.EncryptHeader(keyPath, KekSource, ValidationKey);
|
||||
|
||||
using (IFile file = BaseFileSystem.OpenFile(filePath, OpenMode.ReadWrite))
|
||||
BaseFileSystem.OpenFile(out IFile file, filePath, OpenMode.ReadWrite);
|
||||
|
||||
using (file)
|
||||
{
|
||||
file.Write(0, header.ToBytes(false), WriteOption.Flush).ThrowIfFailure();
|
||||
}
|
||||
|
@ -130,7 +130,8 @@ namespace LibHac.Fs
|
||||
|
||||
public override Result SetSize(long size)
|
||||
{
|
||||
GetSize(out long currentSize).ThrowIfFailure();
|
||||
Result rc = GetSize(out long currentSize);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (currentSize == size) return Result.Success;
|
||||
|
||||
@ -142,7 +143,7 @@ namespace LibHac.Fs
|
||||
IFile currentLastSubFile = Sources[currentSubFileCount - 1];
|
||||
long newSubFileSize = QuerySubFileSize(currentSubFileCount - 1, size, SubFileSize);
|
||||
|
||||
Result rc = currentLastSubFile.SetSize(newSubFileSize);
|
||||
rc = currentLastSubFile.SetSize(newSubFileSize);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
for (int i = currentSubFileCount; i < newSubFileCount; i++)
|
||||
@ -150,8 +151,13 @@ namespace LibHac.Fs
|
||||
string newSubFilePath = ConcatenationFileSystem.GetSubFilePath(FilePath, i);
|
||||
newSubFileSize = QuerySubFileSize(i, size, SubFileSize);
|
||||
|
||||
BaseFileSystem.CreateFile(newSubFilePath, newSubFileSize, CreateFileOptions.None);
|
||||
Sources.Add(BaseFileSystem.OpenFile(newSubFilePath, Mode));
|
||||
rc = BaseFileSystem.CreateFile(newSubFilePath, newSubFileSize, CreateFileOptions.None);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = BaseFileSystem.OpenFile(out IFile newSubFile, newSubFilePath, Mode);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
Sources.Add(newSubFile);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -162,12 +168,14 @@ namespace LibHac.Fs
|
||||
Sources.RemoveAt(i);
|
||||
|
||||
string subFilePath = ConcatenationFileSystem.GetSubFilePath(FilePath, i);
|
||||
BaseFileSystem.DeleteFile(subFilePath);
|
||||
|
||||
rc = BaseFileSystem.DeleteFile(subFilePath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
long newLastFileSize = QuerySubFileSize(newSubFileCount - 1, size, SubFileSize);
|
||||
|
||||
Result rc = Sources[newSubFileCount - 1].SetSize(newLastFileSize);
|
||||
rc = Sources[newSubFileCount - 1].SetSize(newLastFileSize);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
|
@ -68,11 +68,19 @@ namespace LibHac.Fs
|
||||
#if CROSS_PLATFORM
|
||||
private bool IsConcatenationFileHeuristic(string path)
|
||||
{
|
||||
if (BaseFileSystem.GetEntryType(path) != DirectoryEntryType.Directory) return false;
|
||||
// Check if the path is a directory
|
||||
Result getTypeResult = BaseFileSystem.GetEntryType(out DirectoryEntryType pathType, path);
|
||||
if (getTypeResult.IsFailure() || pathType != DirectoryEntryType.Directory) return false;
|
||||
|
||||
if (BaseFileSystem.GetEntryType(PathTools.Combine(path, "00")) != DirectoryEntryType.File) return false;
|
||||
// Check if the directory contains at least one subfile
|
||||
getTypeResult = BaseFileSystem.GetEntryType(out DirectoryEntryType subFileType, PathTools.Combine(path, "00"));
|
||||
if (getTypeResult.IsFailure() || subFileType != DirectoryEntryType.File) return false;
|
||||
|
||||
if (BaseFileSystem.OpenDirectory(path, OpenDirectoryMode.Directory).GetEntryCount() > 0) return false;
|
||||
// Make sure the directory contains no subdirectories
|
||||
Result rc = BaseFileSystem.OpenDirectory(out IDirectory dir, path, OpenDirectoryMode.Directory);
|
||||
if (rc.IsFailure()) return false;
|
||||
|
||||
if (dir.GetEntryCount() > 0) return false;
|
||||
|
||||
// Should be enough checks to avoid most false positives. Maybe
|
||||
return true;
|
||||
@ -91,21 +99,21 @@ namespace LibHac.Fs
|
||||
BaseFileSystem.SetFileAttributes(path, attributes);
|
||||
}
|
||||
|
||||
public void CreateDirectory(string path)
|
||||
public Result CreateDirectory(string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
string parent = PathTools.GetParentDirectory(path);
|
||||
|
||||
if (IsConcatenationFile(parent))
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound,
|
||||
"Cannot create a directory inside of a concatenation file");
|
||||
// Cannot create a directory inside of a concatenation file
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
BaseFileSystem.CreateDirectory(path);
|
||||
return BaseFileSystem.CreateDirectory(path);
|
||||
}
|
||||
|
||||
public void CreateFile(string path, long size, CreateFileOptions options)
|
||||
public Result CreateFile(string path, long size, CreateFileOptions options)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
@ -113,8 +121,7 @@ namespace LibHac.Fs
|
||||
|
||||
if (!options.HasFlag(CreateFileOptions.CreateConcatenationFile))
|
||||
{
|
||||
BaseFileSystem.CreateFile(path, size, newOptions);
|
||||
return;
|
||||
return BaseFileSystem.CreateFile(path, size, newOptions);
|
||||
}
|
||||
|
||||
// A concatenation file directory can't contain normal files
|
||||
@ -122,11 +129,13 @@ namespace LibHac.Fs
|
||||
|
||||
if (IsConcatenationFile(parentDir))
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound,
|
||||
"Cannot create a concatenation file inside of a concatenation file");
|
||||
// Cannot create a file inside of a concatenation file
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
BaseFileSystem.CreateDirectory(path);
|
||||
Result rc = BaseFileSystem.CreateDirectory(path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
SetConcatenationFileAttribute(path);
|
||||
|
||||
long remaining = size;
|
||||
@ -136,82 +145,95 @@ namespace LibHac.Fs
|
||||
long fileSize = Math.Min(SubFileSize, remaining);
|
||||
string fileName = GetSubFilePath(path, i);
|
||||
|
||||
BaseFileSystem.CreateFile(fileName, fileSize, CreateFileOptions.None);
|
||||
Result createSubFileResult = BaseFileSystem.CreateFile(fileName, fileSize, CreateFileOptions.None);
|
||||
|
||||
if (createSubFileResult.IsFailure())
|
||||
{
|
||||
BaseFileSystem.DeleteDirectoryRecursively(path);
|
||||
return createSubFileResult;
|
||||
}
|
||||
|
||||
remaining -= fileSize;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void DeleteDirectory(string path)
|
||||
public Result DeleteDirectory(string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
if (IsConcatenationFile(path))
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
BaseFileSystem.DeleteDirectory(path);
|
||||
return BaseFileSystem.DeleteDirectory(path);
|
||||
}
|
||||
|
||||
public void DeleteDirectoryRecursively(string path)
|
||||
public Result DeleteDirectoryRecursively(string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
if (IsConcatenationFile(path)) ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
if (IsConcatenationFile(path)) return ResultFs.PathNotFound.Log();
|
||||
|
||||
BaseFileSystem.DeleteDirectoryRecursively(path);
|
||||
return BaseFileSystem.DeleteDirectoryRecursively(path);
|
||||
}
|
||||
|
||||
public void CleanDirectoryRecursively(string path)
|
||||
public Result CleanDirectoryRecursively(string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
if (IsConcatenationFile(path)) ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
if (IsConcatenationFile(path)) return ResultFs.PathNotFound.Log();
|
||||
|
||||
BaseFileSystem.CleanDirectoryRecursively(path);
|
||||
return BaseFileSystem.CleanDirectoryRecursively(path);
|
||||
}
|
||||
|
||||
public void DeleteFile(string path)
|
||||
public Result DeleteFile(string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
if (!IsConcatenationFile(path))
|
||||
{
|
||||
BaseFileSystem.DeleteFile(path);
|
||||
return BaseFileSystem.DeleteFile(path);
|
||||
}
|
||||
|
||||
int count = GetSubFileCount(path);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
BaseFileSystem.DeleteFile(GetSubFilePath(path, i));
|
||||
Result rc = BaseFileSystem.DeleteFile(GetSubFilePath(path, i));
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
BaseFileSystem.DeleteDirectory(path);
|
||||
return BaseFileSystem.DeleteDirectory(path);
|
||||
}
|
||||
|
||||
public IDirectory OpenDirectory(string path, OpenDirectoryMode mode)
|
||||
public Result OpenDirectory(out IDirectory directory, string path, OpenDirectoryMode mode)
|
||||
{
|
||||
directory = default;
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
if (IsConcatenationFile(path))
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
IDirectory parentDir = BaseFileSystem.OpenDirectory(path, OpenDirectoryMode.All);
|
||||
var dir = new ConcatenationDirectory(this, parentDir, mode);
|
||||
return dir;
|
||||
Result rc = BaseFileSystem.OpenDirectory(out IDirectory parentDir, path, OpenDirectoryMode.All);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
directory = new ConcatenationDirectory(this, parentDir, mode);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public IFile OpenFile(string path, OpenMode mode)
|
||||
public Result OpenFile(out IFile file, string path, OpenMode mode)
|
||||
{
|
||||
file = default;
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
if (!IsConcatenationFile(path))
|
||||
{
|
||||
return BaseFileSystem.OpenFile(path, mode);
|
||||
return BaseFileSystem.OpenFile(out file, path, mode);
|
||||
}
|
||||
|
||||
int fileCount = GetSubFileCount(path);
|
||||
@ -221,75 +243,85 @@ namespace LibHac.Fs
|
||||
for (int i = 0; i < fileCount; i++)
|
||||
{
|
||||
string filePath = GetSubFilePath(path, i);
|
||||
IFile file = BaseFileSystem.OpenFile(filePath, mode);
|
||||
files.Add(file);
|
||||
|
||||
Result rc = BaseFileSystem.OpenFile(out IFile subFile, filePath, mode);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
files.Add(subFile);
|
||||
}
|
||||
|
||||
return new ConcatenationFile(BaseFileSystem, path, files, SubFileSize, mode);
|
||||
file = new ConcatenationFile(BaseFileSystem, path, files, SubFileSize, mode);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void RenameDirectory(string srcPath, string dstPath)
|
||||
public Result RenameDirectory(string oldPath, string newPath)
|
||||
{
|
||||
srcPath = PathTools.Normalize(srcPath);
|
||||
dstPath = PathTools.Normalize(dstPath);
|
||||
oldPath = PathTools.Normalize(oldPath);
|
||||
newPath = PathTools.Normalize(newPath);
|
||||
|
||||
if (IsConcatenationFile(srcPath))
|
||||
if (IsConcatenationFile(oldPath))
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
BaseFileSystem.RenameDirectory(srcPath, dstPath);
|
||||
return BaseFileSystem.RenameDirectory(oldPath, newPath);
|
||||
}
|
||||
|
||||
public void RenameFile(string srcPath, string dstPath)
|
||||
public Result RenameFile(string oldPath, string newPath)
|
||||
{
|
||||
srcPath = PathTools.Normalize(srcPath);
|
||||
dstPath = PathTools.Normalize(dstPath);
|
||||
oldPath = PathTools.Normalize(oldPath);
|
||||
newPath = PathTools.Normalize(newPath);
|
||||
|
||||
if (IsConcatenationFile(srcPath))
|
||||
if (IsConcatenationFile(oldPath))
|
||||
{
|
||||
BaseFileSystem.RenameDirectory(srcPath, dstPath);
|
||||
return BaseFileSystem.RenameDirectory(oldPath, newPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
BaseFileSystem.RenameFile(srcPath, dstPath);
|
||||
return BaseFileSystem.RenameFile(oldPath, newPath);
|
||||
}
|
||||
}
|
||||
|
||||
public DirectoryEntryType GetEntryType(string path)
|
||||
public Result GetEntryType(out DirectoryEntryType entryType, string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
if (IsConcatenationFile(path)) return DirectoryEntryType.File;
|
||||
if (IsConcatenationFile(path))
|
||||
{
|
||||
entryType = DirectoryEntryType.File;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
return BaseFileSystem.GetEntryType(path);
|
||||
return BaseFileSystem.GetEntryType(out entryType, path);
|
||||
}
|
||||
|
||||
public long GetFreeSpaceSize(string path)
|
||||
public Result GetFreeSpaceSize(out long freeSpace, string path)
|
||||
{
|
||||
return BaseFileSystem.GetFreeSpaceSize(path);
|
||||
return BaseFileSystem.GetFreeSpaceSize(out freeSpace, path);
|
||||
}
|
||||
|
||||
public long GetTotalSpaceSize(string path)
|
||||
public Result GetTotalSpaceSize(out long totalSpace, string path)
|
||||
{
|
||||
return BaseFileSystem.GetTotalSpaceSize(path);
|
||||
return BaseFileSystem.GetTotalSpaceSize(out totalSpace, path);
|
||||
}
|
||||
|
||||
public FileTimeStampRaw GetFileTimeStampRaw(string path)
|
||||
public Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, string path)
|
||||
{
|
||||
return BaseFileSystem.GetFileTimeStampRaw(path);
|
||||
return BaseFileSystem.GetFileTimeStampRaw(out timeStamp, path);
|
||||
}
|
||||
|
||||
public void Commit()
|
||||
public Result Commit()
|
||||
{
|
||||
BaseFileSystem.Commit();
|
||||
return BaseFileSystem.Commit();
|
||||
}
|
||||
|
||||
public void QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, string path, QueryId queryId)
|
||||
public Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, string path)
|
||||
{
|
||||
if (queryId != QueryId.MakeConcatFile) ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationInConcatFsQueryEntry);
|
||||
if (queryId != QueryId.MakeConcatFile) return ResultFs.UnsupportedOperationInConcatFsQueryEntry.Log();
|
||||
|
||||
SetConcatenationFileAttribute(path);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private int GetSubFileCount(string dirPath)
|
||||
|
@ -33,162 +33,170 @@ namespace LibHac.Fs
|
||||
}
|
||||
}
|
||||
|
||||
public void CreateDirectory(string path)
|
||||
public Result CreateDirectory(string path)
|
||||
{
|
||||
string fullPath = GetFullPath(PathTools.Normalize(path));
|
||||
|
||||
lock (Locker)
|
||||
{
|
||||
BaseFs.CreateDirectory(fullPath);
|
||||
return BaseFs.CreateDirectory(fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
public void CreateFile(string path, long size, CreateFileOptions options)
|
||||
public Result CreateFile(string path, long size, CreateFileOptions options)
|
||||
{
|
||||
string fullPath = GetFullPath(PathTools.Normalize(path));
|
||||
|
||||
lock (Locker)
|
||||
{
|
||||
BaseFs.CreateFile(fullPath, size, options);
|
||||
return BaseFs.CreateFile(fullPath, size, options);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteDirectory(string path)
|
||||
public Result DeleteDirectory(string path)
|
||||
{
|
||||
string fullPath = GetFullPath(PathTools.Normalize(path));
|
||||
|
||||
lock (Locker)
|
||||
{
|
||||
BaseFs.DeleteDirectory(fullPath);
|
||||
return BaseFs.DeleteDirectory(fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteDirectoryRecursively(string path)
|
||||
public Result DeleteDirectoryRecursively(string path)
|
||||
{
|
||||
string fullPath = GetFullPath(PathTools.Normalize(path));
|
||||
|
||||
lock (Locker)
|
||||
{
|
||||
BaseFs.DeleteDirectoryRecursively(fullPath);
|
||||
return BaseFs.DeleteDirectoryRecursively(fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
public void CleanDirectoryRecursively(string path)
|
||||
public Result CleanDirectoryRecursively(string path)
|
||||
{
|
||||
string fullPath = GetFullPath(PathTools.Normalize(path));
|
||||
|
||||
lock (Locker)
|
||||
{
|
||||
BaseFs.CleanDirectoryRecursively(fullPath);
|
||||
return BaseFs.CleanDirectoryRecursively(fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteFile(string path)
|
||||
public Result DeleteFile(string path)
|
||||
{
|
||||
string fullPath = GetFullPath(PathTools.Normalize(path));
|
||||
|
||||
lock (Locker)
|
||||
{
|
||||
BaseFs.DeleteFile(fullPath);
|
||||
return BaseFs.DeleteFile(fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
public IDirectory OpenDirectory(string path, OpenDirectoryMode mode)
|
||||
public Result OpenDirectory(out IDirectory directory, string path, OpenDirectoryMode mode)
|
||||
{
|
||||
string fullPath = GetFullPath(PathTools.Normalize(path));
|
||||
|
||||
lock (Locker)
|
||||
{
|
||||
return BaseFs.OpenDirectory(fullPath, mode);
|
||||
return BaseFs.OpenDirectory(out directory, fullPath, mode);
|
||||
}
|
||||
}
|
||||
|
||||
public IFile OpenFile(string path, OpenMode mode)
|
||||
public Result OpenFile(out IFile file, string path, OpenMode mode)
|
||||
{
|
||||
file = default;
|
||||
string fullPath = GetFullPath(PathTools.Normalize(path));
|
||||
|
||||
lock (Locker)
|
||||
{
|
||||
IFile baseFile = BaseFs.OpenFile(fullPath, mode);
|
||||
var file = new DirectorySaveDataFile(this, baseFile);
|
||||
Result rc = BaseFs.OpenFile(out IFile baseFile, fullPath, mode);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
file = new DirectorySaveDataFile(this, baseFile);
|
||||
|
||||
if (mode.HasFlag(OpenMode.Write))
|
||||
{
|
||||
OpenWritableFileCount++;
|
||||
}
|
||||
|
||||
return file;
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
public void RenameDirectory(string srcPath, string dstPath)
|
||||
public Result RenameDirectory(string oldPath, string newPath)
|
||||
{
|
||||
string fullSrcPath = GetFullPath(PathTools.Normalize(srcPath));
|
||||
string fullDstPath = GetFullPath(PathTools.Normalize(dstPath));
|
||||
string fullOldPath = GetFullPath(PathTools.Normalize(oldPath));
|
||||
string fullNewPath = GetFullPath(PathTools.Normalize(newPath));
|
||||
|
||||
lock (Locker)
|
||||
{
|
||||
BaseFs.RenameDirectory(fullSrcPath, fullDstPath);
|
||||
return BaseFs.RenameDirectory(fullOldPath, fullNewPath);
|
||||
}
|
||||
}
|
||||
|
||||
public void RenameFile(string srcPath, string dstPath)
|
||||
public Result RenameFile(string oldPath, string newPath)
|
||||
{
|
||||
string fullSrcPath = GetFullPath(PathTools.Normalize(srcPath));
|
||||
string fullDstPath = GetFullPath(PathTools.Normalize(dstPath));
|
||||
string fullOldPath = GetFullPath(PathTools.Normalize(oldPath));
|
||||
string fullNewPath = GetFullPath(PathTools.Normalize(newPath));
|
||||
|
||||
lock (Locker)
|
||||
{
|
||||
BaseFs.RenameFile(fullSrcPath, fullDstPath);
|
||||
return BaseFs.RenameFile(fullOldPath, fullNewPath);
|
||||
}
|
||||
}
|
||||
|
||||
public DirectoryEntryType GetEntryType(string path)
|
||||
public Result GetEntryType(out DirectoryEntryType entryType, string path)
|
||||
{
|
||||
string fullPath = GetFullPath(PathTools.Normalize(path));
|
||||
|
||||
lock (Locker)
|
||||
{
|
||||
return BaseFs.GetEntryType(fullPath);
|
||||
return BaseFs.GetEntryType(out entryType, fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
public long GetFreeSpaceSize(string path)
|
||||
public Result GetFreeSpaceSize(out long freeSpace, string path)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.NotImplemented);
|
||||
return default;
|
||||
freeSpace = default;
|
||||
return ResultFs.NotImplemented.Log();
|
||||
}
|
||||
|
||||
public long GetTotalSpaceSize(string path)
|
||||
public Result GetTotalSpaceSize(out long totalSpace, string path)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.NotImplemented);
|
||||
return default;
|
||||
totalSpace = default;
|
||||
return ResultFs.NotImplemented.Log();
|
||||
}
|
||||
|
||||
public FileTimeStampRaw GetFileTimeStampRaw(string path)
|
||||
public Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, string path)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.NotImplemented);
|
||||
return default;
|
||||
timeStamp = default;
|
||||
return ResultFs.NotImplemented.Log();
|
||||
}
|
||||
|
||||
public void Commit()
|
||||
public Result Commit()
|
||||
{
|
||||
if (OpenWritableFileCount > 0)
|
||||
lock (Locker)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.WritableFileOpen,
|
||||
"All files must be closed before commiting save data.");
|
||||
if (OpenWritableFileCount > 0)
|
||||
{
|
||||
// All files must be closed before commiting save data.
|
||||
return ResultFs.WritableFileOpen.Log();
|
||||
}
|
||||
|
||||
Result rc = SynchronizeDirectory(SyncDir, WorkingDir);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = BaseFs.DeleteDirectoryRecursively(CommittedDir);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return BaseFs.RenameDirectory(SyncDir, CommittedDir);
|
||||
}
|
||||
|
||||
SynchronizeDirectory(SyncDir, WorkingDir);
|
||||
|
||||
BaseFs.DeleteDirectoryRecursively(CommittedDir);
|
||||
|
||||
BaseFs.RenameDirectory(SyncDir, CommittedDir);
|
||||
}
|
||||
|
||||
public void QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, string path, QueryId queryId)
|
||||
public Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, string path)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.NotImplemented);
|
||||
return ResultFs.NotImplemented.Log();
|
||||
}
|
||||
|
||||
private string GetFullPath(string path)
|
||||
@ -196,19 +204,21 @@ namespace LibHac.Fs
|
||||
return PathTools.Normalize(PathTools.Combine(WorkingDir, path));
|
||||
}
|
||||
|
||||
private void SynchronizeDirectory(string dest, string src)
|
||||
private Result SynchronizeDirectory(string dest, string src)
|
||||
{
|
||||
if (BaseFs.DirectoryExists(dest))
|
||||
{
|
||||
BaseFs.DeleteDirectoryRecursively(dest);
|
||||
}
|
||||
Result rc = BaseFs.DeleteDirectoryRecursively(dest);
|
||||
if (rc.IsFailure() && rc != ResultFs.PathNotFound) return rc;
|
||||
|
||||
BaseFs.CreateDirectory(dest);
|
||||
rc = BaseFs.CreateDirectory(dest);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
IDirectory sourceDir = BaseFs.OpenDirectory(src, OpenDirectoryMode.All);
|
||||
IDirectory destDir = BaseFs.OpenDirectory(dest, OpenDirectoryMode.All);
|
||||
rc = BaseFs.OpenDirectory(out IDirectory sourceDir, src, OpenDirectoryMode.All);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
sourceDir.CopyDirectory(destDir);
|
||||
rc = BaseFs.OpenDirectory(out IDirectory destDir, dest, OpenDirectoryMode.All);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return sourceDir.CopyDirectory(destDir);
|
||||
}
|
||||
|
||||
internal void NotifyCloseWritableFile()
|
||||
|
@ -7,10 +7,11 @@ namespace LibHac.Fs
|
||||
{
|
||||
public static class FileSystemExtensions
|
||||
{
|
||||
public static void CopyDirectory(this IDirectory source, IDirectory dest, IProgressReport logger = null, CreateFileOptions options = CreateFileOptions.None)
|
||||
public static Result CopyDirectory(this IDirectory source, IDirectory dest, IProgressReport logger = null, CreateFileOptions options = CreateFileOptions.None)
|
||||
{
|
||||
IFileSystem sourceFs = source.ParentFileSystem;
|
||||
IFileSystem destFs = dest.ParentFileSystem;
|
||||
Result rc;
|
||||
|
||||
foreach (DirectoryEntry entry in source.Read())
|
||||
{
|
||||
@ -20,32 +21,47 @@ namespace LibHac.Fs
|
||||
if (entry.Type == DirectoryEntryType.Directory)
|
||||
{
|
||||
destFs.EnsureDirectoryExists(subDstPath);
|
||||
IDirectory subSrcDir = sourceFs.OpenDirectory(subSrcPath, OpenDirectoryMode.All);
|
||||
IDirectory subDstDir = destFs.OpenDirectory(subDstPath, OpenDirectoryMode.All);
|
||||
|
||||
subSrcDir.CopyDirectory(subDstDir, logger, options);
|
||||
rc = sourceFs.OpenDirectory(out IDirectory subSrcDir, subSrcPath, OpenDirectoryMode.All);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = destFs.OpenDirectory(out IDirectory subDstDir, subDstPath, OpenDirectoryMode.All);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = subSrcDir.CopyDirectory(subDstDir, logger, options);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
if (entry.Type == DirectoryEntryType.File)
|
||||
{
|
||||
destFs.CreateOrOverwriteFile(subDstPath, entry.Size, options);
|
||||
|
||||
using (IFile srcFile = sourceFs.OpenFile(subSrcPath, OpenMode.Read))
|
||||
using (IFile dstFile = destFs.OpenFile(subDstPath, OpenMode.Write | OpenMode.AllowAppend))
|
||||
rc = sourceFs.OpenFile(out IFile srcFile, subSrcPath, OpenMode.Read);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
using (srcFile)
|
||||
{
|
||||
logger?.LogMessage(subSrcPath);
|
||||
srcFile.CopyTo(dstFile, logger);
|
||||
rc = destFs.OpenFile(out IFile dstFile, subDstPath, OpenMode.Write | OpenMode.AllowAppend);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
using (dstFile)
|
||||
{
|
||||
logger?.LogMessage(subSrcPath);
|
||||
srcFile.CopyTo(dstFile, logger);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static void CopyFileSystem(this IFileSystem source, IFileSystem dest, IProgressReport logger = null, CreateFileOptions options = CreateFileOptions.None)
|
||||
{
|
||||
IDirectory sourceRoot = source.OpenDirectory("/", OpenDirectoryMode.All);
|
||||
IDirectory destRoot = dest.OpenDirectory("/", OpenDirectoryMode.All);
|
||||
source.OpenDirectory(out IDirectory sourceRoot, "/", OpenDirectoryMode.All).ThrowIfFailure();
|
||||
dest.OpenDirectory(out IDirectory destRoot, "/", OpenDirectoryMode.All).ThrowIfFailure();
|
||||
|
||||
sourceRoot.CopyDirectory(destRoot, logger, options);
|
||||
sourceRoot.CopyDirectory(destRoot, logger, options).ThrowIfFailure();
|
||||
}
|
||||
|
||||
public static void Extract(this IFileSystem source, string destinationPath, IProgressReport logger = null)
|
||||
@ -67,7 +83,9 @@ namespace LibHac.Fs
|
||||
|
||||
public static IEnumerable<DirectoryEntry> EnumerateEntries(this IFileSystem fileSystem, string searchPattern, SearchOptions searchOptions)
|
||||
{
|
||||
return fileSystem.OpenDirectory("/", OpenDirectoryMode.All).EnumerateEntries(searchPattern, searchOptions);
|
||||
fileSystem.OpenDirectory(out IDirectory rootDir, "/", OpenDirectoryMode.All).ThrowIfFailure();
|
||||
|
||||
return rootDir.EnumerateEntries(searchPattern, searchOptions);
|
||||
}
|
||||
|
||||
public static IEnumerable<DirectoryEntry> EnumerateEntries(this IDirectory directory)
|
||||
@ -91,7 +109,7 @@ namespace LibHac.Fs
|
||||
|
||||
if (entry.Type != DirectoryEntryType.Directory || !recurse) continue;
|
||||
|
||||
IDirectory subDir = fs.OpenDirectory(PathTools.Combine(directory.FullPath, entry.Name), OpenDirectoryMode.All);
|
||||
fs.OpenDirectory(out IDirectory subDir, PathTools.Combine(directory.FullPath, entry.Name), OpenDirectoryMode.All).ThrowIfFailure();
|
||||
|
||||
foreach (DirectoryEntry subEntry in subDir.EnumerateEntries(searchPattern, searchOptions))
|
||||
{
|
||||
@ -139,7 +157,9 @@ namespace LibHac.Fs
|
||||
|
||||
public static int GetEntryCount(this IFileSystem fs, OpenDirectoryMode mode)
|
||||
{
|
||||
return fs.OpenDirectory("/", OpenDirectoryMode.All).GetEntryCountRecursive(mode);
|
||||
fs.OpenDirectory(out IDirectory rootDir, "/", OpenDirectoryMode.All).ThrowIfFailure();
|
||||
|
||||
return rootDir.GetEntryCountRecursive(mode);
|
||||
}
|
||||
|
||||
public static int GetEntryCountRecursive(this IDirectory directory, OpenDirectoryMode mode)
|
||||
@ -171,7 +191,7 @@ namespace LibHac.Fs
|
||||
|
||||
public static void SetConcatenationFileAttribute(this IFileSystem fs, string path)
|
||||
{
|
||||
fs.QueryEntry(Span<byte>.Empty, Span<byte>.Empty, path, QueryId.MakeConcatFile);
|
||||
fs.QueryEntry(Span<byte>.Empty, Span<byte>.Empty, QueryId.MakeConcatFile, path);
|
||||
}
|
||||
|
||||
public static void CleanDirectoryRecursivelyGeneric(IDirectory directory)
|
||||
@ -184,7 +204,7 @@ namespace LibHac.Fs
|
||||
|
||||
if (entry.Type == DirectoryEntryType.Directory)
|
||||
{
|
||||
IDirectory subDir = fs.OpenDirectory(subPath, OpenDirectoryMode.All);
|
||||
fs.OpenDirectory(out IDirectory subDir, subPath, OpenDirectoryMode.All).ThrowIfFailure();
|
||||
|
||||
CleanDirectoryRecursivelyGeneric(subDir);
|
||||
fs.DeleteDirectory(subPath);
|
||||
@ -208,12 +228,16 @@ namespace LibHac.Fs
|
||||
|
||||
public static bool DirectoryExists(this IFileSystem fs, string path)
|
||||
{
|
||||
return fs.GetEntryType(path) == DirectoryEntryType.Directory;
|
||||
Result rc = fs.GetEntryType(out DirectoryEntryType type, path);
|
||||
|
||||
return (rc.IsSuccess() && type == DirectoryEntryType.Directory);
|
||||
}
|
||||
|
||||
public static bool FileExists(this IFileSystem fs, string path)
|
||||
{
|
||||
return fs.GetEntryType(path) == DirectoryEntryType.File;
|
||||
Result rc = fs.GetEntryType(out DirectoryEntryType type, path);
|
||||
|
||||
return (rc.IsSuccess() && type == DirectoryEntryType.File);
|
||||
}
|
||||
|
||||
public static void EnsureDirectoryExists(this IFileSystem fs, string path)
|
||||
|
@ -11,14 +11,15 @@ namespace LibHac.Fs
|
||||
/// Creates all directories and subdirectories in the specified path unless they already exist.
|
||||
/// </summary>
|
||||
/// <param name="path">The full path of the directory to create.</param>
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
/// <remarks>
|
||||
/// A <see cref="HorizonResultException"/> will be thrown with the given <see cref="Result"/> under the following conditions:
|
||||
/// The following <see cref="Result"/> codes may be returned under certain conditions:
|
||||
///
|
||||
/// The parent directory of the specified path does not exist: <see cref="ResultFs.PathNotFound"/>
|
||||
/// Specified path already exists as either a file or directory: <see cref="ResultFs.PathAlreadyExists"/>
|
||||
/// Insufficient free space to create the directory: <see cref="ResultFs.InsufficientFreeSpace"/>
|
||||
/// </remarks>
|
||||
void CreateDirectory(string path);
|
||||
Result CreateDirectory(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Creates or overwrites a file at the specified path.
|
||||
@ -27,162 +28,177 @@ namespace LibHac.Fs
|
||||
/// <param name="size">The initial size of the created file.</param>
|
||||
/// <param name="options">Flags to control how the file is created.
|
||||
/// Should usually be <see cref="CreateFileOptions.None"/></param>
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
/// <remarks>
|
||||
/// A <see cref="HorizonResultException"/> will be thrown with the given <see cref="Result"/> under the following conditions:
|
||||
/// The following <see cref="Result"/> codes may be returned under certain conditions:
|
||||
///
|
||||
/// The parent directory of the specified path does not exist: <see cref="ResultFs.PathNotFound"/>
|
||||
/// Specified path already exists as either a file or directory: <see cref="ResultFs.PathAlreadyExists"/>
|
||||
/// Insufficient free space to create the file: <see cref="ResultFs.InsufficientFreeSpace"/>
|
||||
/// </remarks>
|
||||
void CreateFile(string path, long size, CreateFileOptions options);
|
||||
Result CreateFile(string path, long size, CreateFileOptions options);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The full path of the directory to delete.</param>
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
/// <remarks>
|
||||
/// A <see cref="HorizonResultException"/> will be thrown with the given <see cref="Result"/> under the following conditions:
|
||||
/// The following <see cref="Result"/> codes may be returned under certain conditions:
|
||||
///
|
||||
/// The specified path does not exist or is a file: <see cref="ResultFs.PathNotFound"/>
|
||||
/// The specified directory is not empty: <see cref="ResultFs.DirectoryNotEmpty"/>
|
||||
/// </remarks>
|
||||
void DeleteDirectory(string path);
|
||||
Result DeleteDirectory(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified directory and any subdirectories and files in the directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The full path of the directory to delete.</param>
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
/// <remarks>
|
||||
/// A <see cref="HorizonResultException"/> will be thrown with the given <see cref="Result"/> under the following conditions:
|
||||
/// The following <see cref="Result"/> codes may be returned under certain conditions:
|
||||
///
|
||||
/// The specified path does not exist or is a file: <see cref="ResultFs.PathNotFound"/>
|
||||
/// </remarks>
|
||||
void DeleteDirectoryRecursively(string path);
|
||||
Result DeleteDirectoryRecursively(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes any subdirectories and files in the specified directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The full path of the directory to clean.</param>
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
/// <remarks>
|
||||
/// A <see cref="HorizonResultException"/> will be thrown with the given <see cref="Result"/> under the following conditions:
|
||||
/// The following <see cref="Result"/> codes may be returned under certain conditions:
|
||||
///
|
||||
/// The specified path does not exist or is a file: <see cref="ResultFs.PathNotFound"/>
|
||||
/// </remarks>
|
||||
void CleanDirectoryRecursively(string path);
|
||||
Result CleanDirectoryRecursively(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified file.
|
||||
/// </summary>
|
||||
/// <param name="path">The full path of the file to delete.</param>
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
/// <remarks>
|
||||
/// A <see cref="HorizonResultException"/> will be thrown with the given <see cref="Result"/> under the following conditions:
|
||||
/// The following <see cref="Result"/> codes may be returned under certain conditions:
|
||||
///
|
||||
/// The specified path does not exist or is a directory: <see cref="ResultFs.PathNotFound"/>
|
||||
/// </remarks>
|
||||
void DeleteFile(string path);
|
||||
Result DeleteFile(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IDirectory"/> instance for enumerating the specified directory.
|
||||
/// </summary>
|
||||
/// <param name="directory">If the operation returns successfully,
|
||||
/// An <see cref="IDirectory"/> instance for the specified directory.</param>
|
||||
/// <param name="path">The directory's full path.</param>
|
||||
/// <param name="mode">Specifies which sub-entries should be enumerated.</param>
|
||||
/// <returns>An <see cref="IDirectory"/> instance for the specified directory.</returns>
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
/// <remarks>
|
||||
/// A <see cref="HorizonResultException"/> will be thrown with the given <see cref="Result"/> under the following conditions:
|
||||
/// The following <see cref="Result"/> codes may be returned under certain conditions:
|
||||
///
|
||||
/// The specified path does not exist or is a file: <see cref="ResultFs.PathNotFound"/>
|
||||
/// </remarks>
|
||||
IDirectory OpenDirectory(string path, OpenDirectoryMode mode);
|
||||
Result OpenDirectory(out IDirectory directory, string path, OpenDirectoryMode mode);
|
||||
|
||||
/// <summary>
|
||||
/// Opens an <see cref="IFile"/> instance for the specified path.
|
||||
/// </summary>
|
||||
/// <param name="file">If the operation returns successfully,
|
||||
/// An <see cref="IFile"/> instance for the specified path.</param>
|
||||
/// <param name="path">The full path of the file to open.</param>
|
||||
/// <param name="mode">Specifies the access permissions of the created <see cref="IFile"/>.</param>
|
||||
/// <returns>An <see cref="IFile"/> instance for the specified path.</returns>
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
/// <remarks>
|
||||
/// A <see cref="HorizonResultException"/> will be thrown with the given <see cref="Result"/> under the following conditions:
|
||||
/// The following <see cref="Result"/> codes may be returned under certain conditions:
|
||||
///
|
||||
/// The specified path does not exist or is a directory: <see cref="ResultFs.PathNotFound"/>
|
||||
/// </remarks>
|
||||
IFile OpenFile(string path, OpenMode mode);
|
||||
Result OpenFile(out IFile file, string path, OpenMode mode);
|
||||
|
||||
/// <summary>
|
||||
/// Renames or moves a directory to a new location.
|
||||
/// </summary>
|
||||
/// <param name="srcPath">The full path of the directory to rename.</param>
|
||||
/// <param name="dstPath">The new full path of the directory.</param>
|
||||
/// <returns>An <see cref="IFile"/> instance for the specified path.</returns>
|
||||
/// <param name="oldPath">The full path of the directory to rename.</param>
|
||||
/// <param name="newPath">The new full path of the directory.</param>
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
/// <remarks>
|
||||
/// If <paramref name="srcPath"/> and <paramref name="dstPath"/> are the same, this function does nothing and returns successfully.
|
||||
/// A <see cref="HorizonResultException"/> will be thrown with the given <see cref="Result"/> under the following conditions:
|
||||
/// If <paramref name="oldPath"/> and <paramref name="newPath"/> are the same, this function does nothing and returns <see cref="Result.Success"/>.
|
||||
/// The following <see cref="Result"/> codes may be returned under certain conditions:
|
||||
///
|
||||
/// <paramref name="srcPath"/> does not exist or is a file: <see cref="ResultFs.PathNotFound"/>
|
||||
/// <paramref name="dstPath"/>'s parent directory does not exist: <see cref="ResultFs.PathNotFound"/>
|
||||
/// <paramref name="dstPath"/> already exists as either a file or directory: <see cref="ResultFs.PathAlreadyExists"/>
|
||||
/// Either <paramref name="srcPath"/> or <paramref name="dstPath"/> is a subpath of the other: <see cref="ResultFs.DestinationIsSubPathOfSource"/>
|
||||
/// <paramref name="oldPath"/> does not exist or is a file: <see cref="ResultFs.PathNotFound"/>
|
||||
/// <paramref name="newPath"/>'s parent directory does not exist: <see cref="ResultFs.PathNotFound"/>
|
||||
/// <paramref name="newPath"/> already exists as either a file or directory: <see cref="ResultFs.PathAlreadyExists"/>
|
||||
/// Either <paramref name="oldPath"/> or <paramref name="newPath"/> is a subpath of the other: <see cref="ResultFs.DestinationIsSubPathOfSource"/>
|
||||
/// </remarks>
|
||||
void RenameDirectory(string srcPath, string dstPath);
|
||||
Result RenameDirectory(string oldPath, string newPath);
|
||||
|
||||
/// <summary>
|
||||
/// Renames or moves a file to a new location.
|
||||
/// </summary>
|
||||
/// <param name="srcPath">The full path of the file to rename.</param>
|
||||
/// <param name="dstPath">The new full path of the file.</param>
|
||||
/// <param name="oldPath">The full path of the file to rename.</param>
|
||||
/// <param name="newPath">The new full path of the file.</param>
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
/// <remarks>
|
||||
/// If <paramref name="srcPath"/> and <paramref name="dstPath"/> are the same, this function does nothing and returns successfully.
|
||||
/// A <see cref="HorizonResultException"/> will be thrown with the given <see cref="Result"/> under the following conditions:
|
||||
/// If <paramref name="oldPath"/> and <paramref name="newPath"/> are the same, this function does nothing and returns successfully.
|
||||
/// The following <see cref="Result"/> codes may be returned under certain conditions:
|
||||
///
|
||||
/// <paramref name="srcPath"/> does not exist or is a directory: <see cref="ResultFs.PathNotFound"/>
|
||||
/// <paramref name="dstPath"/>'s parent directory does not exist: <see cref="ResultFs.PathNotFound"/>
|
||||
/// <paramref name="dstPath"/> already exists as either a file or directory: <see cref="ResultFs.PathAlreadyExists"/>
|
||||
/// <paramref name="oldPath"/> does not exist or is a directory: <see cref="ResultFs.PathNotFound"/>
|
||||
/// <paramref name="newPath"/>'s parent directory does not exist: <see cref="ResultFs.PathNotFound"/>
|
||||
/// <paramref name="newPath"/> already exists as either a file or directory: <see cref="ResultFs.PathAlreadyExists"/>
|
||||
/// </remarks>
|
||||
void RenameFile(string srcPath, string dstPath);
|
||||
Result RenameFile(string oldPath, string newPath);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified path is a file or directory, or does not exist.
|
||||
/// </summary>
|
||||
/// <param name="entryType">If the operation returns successfully, the <see cref="DirectoryEntryType"/> of the file.</param>
|
||||
/// <param name="path">The full path to check.</param>
|
||||
/// <returns>The <see cref="DirectoryEntryType"/> of the file.</returns>
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
/// <remarks>
|
||||
/// This function operates slightly differently than it does in Horizon OS.
|
||||
/// Instead of returning <see cref="ResultFs.PathNotFound"/> when an entry is missing,
|
||||
/// the function will return <see cref="DirectoryEntryType.NotFound"/>.
|
||||
/// </remarks>
|
||||
DirectoryEntryType GetEntryType(string path);
|
||||
Result GetEntryType(out DirectoryEntryType entryType, string path);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of available free space on a drive, in bytes.
|
||||
/// </summary>
|
||||
/// <param name="freeSpace">If the operation returns successfully, the amount of free space available on the drive, in bytes.</param>
|
||||
/// <param name="path">The path of the drive to query. Unused in almost all cases.</param>
|
||||
/// <returns>The amount of free space available on the drive, in bytes.</returns>
|
||||
long GetFreeSpaceSize(string path);
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
Result GetFreeSpaceSize(out long freeSpace, string path);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total size of storage space on a drive, in bytes.
|
||||
/// </summary>
|
||||
/// <param name="totalSpace">If the operation returns successfully, the total size of the drive, in bytes.</param>
|
||||
/// <param name="path">The path of the drive to query. Unused in almost all cases.</param>
|
||||
/// <returns>The total size of the drive, in bytes.</returns>
|
||||
long GetTotalSpaceSize(string path);
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
Result GetTotalSpaceSize(out long totalSpace, string path);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the creation, last accessed, and last modified timestamps of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="timeStamp">If the operation returns successfully, the timestamps for the specified file or directory.
|
||||
/// These value are expressed as Unix timestamps.</param>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <returns>The timestamps for the specified file or directory.
|
||||
/// This value is expressed as a Unix timestamp</returns>
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
/// <remarks>
|
||||
/// A <see cref="HorizonResultException"/> will be thrown with the given <see cref="Result"/> under the following conditions:
|
||||
/// The following <see cref="Result"/> codes may be returned under certain conditions:
|
||||
///
|
||||
/// The specified path does not exist: <see cref="ResultFs.PathNotFound"/>
|
||||
/// </remarks>
|
||||
FileTimeStampRaw GetFileTimeStampRaw(string path);
|
||||
Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, string path);
|
||||
|
||||
/// <summary>
|
||||
/// Commits any changes to a transactional file system.
|
||||
/// Does nothing if called on a non-transactional file system.
|
||||
/// </summary>
|
||||
void Commit();
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
Result Commit();
|
||||
|
||||
/// <summary>
|
||||
/// Performs a query on the specified file.
|
||||
@ -193,9 +209,10 @@ namespace LibHac.Fs
|
||||
/// May be unused depending on the query type.</param>
|
||||
/// <param name="inBuffer">The buffer for sending data to the query operation.
|
||||
/// May be unused depending on the query type.</param>
|
||||
/// <param name="path">The full path of the file to query.</param>
|
||||
/// <param name="queryId">The type of query to perform.</param>
|
||||
void QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, string path, QueryId queryId);
|
||||
/// <param name="path">The full path of the file to query.</param>
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, string path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -12,122 +12,138 @@ namespace LibHac.Fs
|
||||
Sources.AddRange(sourceFileSystems);
|
||||
}
|
||||
|
||||
public IDirectory OpenDirectory(string path, OpenDirectoryMode mode)
|
||||
public Result OpenDirectory(out IDirectory directory, string path, OpenDirectoryMode mode)
|
||||
{
|
||||
directory = default;
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
var dirs = new List<IDirectory>();
|
||||
|
||||
foreach (IFileSystem fs in Sources)
|
||||
{
|
||||
DirectoryEntryType type = fs.GetEntryType(path);
|
||||
Result rc = fs.GetEntryType(out DirectoryEntryType entryType, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (type == DirectoryEntryType.File && dirs.Count == 0)
|
||||
if (entryType == DirectoryEntryType.File && dirs.Count == 0)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
}
|
||||
|
||||
if (fs.GetEntryType(path) == DirectoryEntryType.Directory)
|
||||
if (entryType == DirectoryEntryType.Directory)
|
||||
{
|
||||
dirs.Add(fs.OpenDirectory(path, mode));
|
||||
rc = fs.OpenDirectory(out IDirectory subDirectory, path, mode);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
dirs.Add(subDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
var dir = new LayeredFileSystemDirectory(this, dirs, path, mode);
|
||||
|
||||
return dir;
|
||||
directory = new LayeredFileSystemDirectory(this, dirs, path, mode);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public IFile OpenFile(string path, OpenMode mode)
|
||||
public Result OpenFile(out IFile file, string path, OpenMode mode)
|
||||
{
|
||||
file = default;
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
foreach (IFileSystem fs in Sources)
|
||||
{
|
||||
DirectoryEntryType type = fs.GetEntryType(path);
|
||||
Result rc = fs.GetEntryType(out DirectoryEntryType type, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (type == DirectoryEntryType.File)
|
||||
{
|
||||
return fs.OpenFile(path, mode);
|
||||
return fs.OpenFile(out file, path, mode);
|
||||
}
|
||||
|
||||
if (type == DirectoryEntryType.Directory)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
}
|
||||
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
return default;
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
public DirectoryEntryType GetEntryType(string path)
|
||||
public Result GetEntryType(out DirectoryEntryType entryType, string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
foreach (IFileSystem fs in Sources)
|
||||
{
|
||||
DirectoryEntryType type = fs.GetEntryType(path);
|
||||
Result getEntryResult = fs.GetEntryType(out DirectoryEntryType type, path);
|
||||
|
||||
if (type != DirectoryEntryType.NotFound) return type;
|
||||
}
|
||||
|
||||
return DirectoryEntryType.NotFound;
|
||||
}
|
||||
|
||||
public FileTimeStampRaw GetFileTimeStampRaw(string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
foreach (IFileSystem fs in Sources)
|
||||
{
|
||||
if (fs.GetEntryType(path) != DirectoryEntryType.NotFound)
|
||||
if (getEntryResult.IsSuccess() && type != DirectoryEntryType.NotFound)
|
||||
{
|
||||
return fs.GetFileTimeStampRaw(path);
|
||||
entryType = type;
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
return default;
|
||||
entryType = DirectoryEntryType.NotFound;
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
public void QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, string path, QueryId queryId)
|
||||
public Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
foreach (IFileSystem fs in Sources)
|
||||
{
|
||||
if (fs.GetEntryType(path) != DirectoryEntryType.NotFound)
|
||||
Result getEntryResult = fs.GetEntryType(out DirectoryEntryType type, path);
|
||||
|
||||
if (getEntryResult.IsSuccess() && type != DirectoryEntryType.NotFound)
|
||||
{
|
||||
fs.QueryEntry(outBuffer, inBuffer, path, queryId);
|
||||
return;
|
||||
return fs.GetFileTimeStampRaw(out timeStamp, path);
|
||||
}
|
||||
}
|
||||
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
timeStamp = default;
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
public void Commit() { }
|
||||
|
||||
public void CreateDirectory(string path) => ThrowHelper.ThrowResult(ResultFs.UnsupportedOperation);
|
||||
public void CreateFile(string path, long size, CreateFileOptions options) => ThrowHelper.ThrowResult(ResultFs.UnsupportedOperation);
|
||||
public void DeleteDirectory(string path) => ThrowHelper.ThrowResult(ResultFs.UnsupportedOperation);
|
||||
public void DeleteDirectoryRecursively(string path) => ThrowHelper.ThrowResult(ResultFs.UnsupportedOperation);
|
||||
public void CleanDirectoryRecursively(string path) => ThrowHelper.ThrowResult(ResultFs.UnsupportedOperation);
|
||||
public void DeleteFile(string path) => ThrowHelper.ThrowResult(ResultFs.UnsupportedOperation);
|
||||
public void RenameDirectory(string srcPath, string dstPath) => ThrowHelper.ThrowResult(ResultFs.UnsupportedOperation);
|
||||
public void RenameFile(string srcPath, string dstPath) => ThrowHelper.ThrowResult(ResultFs.UnsupportedOperation);
|
||||
|
||||
public long GetFreeSpaceSize(string path)
|
||||
public Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, string path)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperation);
|
||||
return default;
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
foreach (IFileSystem fs in Sources)
|
||||
{
|
||||
Result getEntryResult = fs.GetEntryType(out DirectoryEntryType type, path);
|
||||
|
||||
if (getEntryResult.IsSuccess() && type != DirectoryEntryType.NotFound)
|
||||
{
|
||||
return fs.QueryEntry(outBuffer, inBuffer, queryId, path);
|
||||
}
|
||||
}
|
||||
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
public long GetTotalSpaceSize(string path)
|
||||
public Result Commit()
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperation);
|
||||
return default;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result CreateDirectory(string path) => ResultFs.UnsupportedOperation.Log();
|
||||
public Result CreateFile(string path, long size, CreateFileOptions options) => ResultFs.UnsupportedOperation.Log();
|
||||
public Result DeleteDirectory(string path) => ResultFs.UnsupportedOperation.Log();
|
||||
public Result DeleteDirectoryRecursively(string path) => ResultFs.UnsupportedOperation.Log();
|
||||
public Result CleanDirectoryRecursively(string path) => ResultFs.UnsupportedOperation.Log();
|
||||
public Result DeleteFile(string path) => ResultFs.UnsupportedOperation.Log();
|
||||
public Result RenameDirectory(string oldPath, string newPath) => ResultFs.UnsupportedOperation.Log();
|
||||
public Result RenameFile(string oldPath, string newPath) => ResultFs.UnsupportedOperation.Log();
|
||||
|
||||
public Result GetFreeSpaceSize(out long freeSpace, string path)
|
||||
{
|
||||
freeSpace = default;
|
||||
return ResultFs.UnsupportedOperation.Log();
|
||||
}
|
||||
|
||||
public Result GetTotalSpaceSize(out long totalSpace, string path)
|
||||
{
|
||||
totalSpace = default;
|
||||
return ResultFs.UnsupportedOperation.Log();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ namespace LibHac.Fs
|
||||
return GetSizeInternal(info);
|
||||
}
|
||||
|
||||
public void CreateDirectory(string path)
|
||||
public Result CreateDirectory(string path)
|
||||
{
|
||||
string localPath = ResolveLocalPath(PathTools.Normalize(path));
|
||||
|
||||
@ -66,18 +66,18 @@ namespace LibHac.Fs
|
||||
|
||||
if (dir.Exists)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathAlreadyExists);
|
||||
return ResultFs.PathAlreadyExists.Log();
|
||||
}
|
||||
|
||||
if (dir.Parent?.Exists != true)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
CreateDirInternal(dir);
|
||||
return CreateDirInternal(dir);
|
||||
}
|
||||
|
||||
public void CreateFile(string path, long size, CreateFileOptions options)
|
||||
public Result CreateFile(string path, long size, CreateFileOptions options)
|
||||
{
|
||||
string localPath = ResolveLocalPath(PathTools.Normalize(path));
|
||||
|
||||
@ -85,121 +85,140 @@ namespace LibHac.Fs
|
||||
|
||||
if (file.Exists)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathAlreadyExists);
|
||||
return ResultFs.PathAlreadyExists.Log();
|
||||
}
|
||||
|
||||
if (file.Directory?.Exists != true)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
using (FileStream stream = CreateFileInternal(file))
|
||||
Result rc = CreateFileInternal(out FileStream stream, file);
|
||||
|
||||
using (stream)
|
||||
{
|
||||
SetStreamLengthInternal(stream, size);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return SetStreamLengthInternal(stream, size);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteDirectory(string path)
|
||||
public Result DeleteDirectory(string path)
|
||||
{
|
||||
string localPath = ResolveLocalPath(PathTools.Normalize(path));
|
||||
|
||||
DirectoryInfo dir = GetDirInfo(localPath);
|
||||
|
||||
DeleteDirectoryInternal(dir, false);
|
||||
return DeleteDirectoryInternal(dir, false);
|
||||
}
|
||||
|
||||
public void DeleteDirectoryRecursively(string path)
|
||||
public Result DeleteDirectoryRecursively(string path)
|
||||
{
|
||||
string localPath = ResolveLocalPath(PathTools.Normalize(path));
|
||||
|
||||
DirectoryInfo dir = GetDirInfo(localPath);
|
||||
|
||||
DeleteDirectoryInternal(dir, true);
|
||||
return DeleteDirectoryInternal(dir, true);
|
||||
}
|
||||
|
||||
public void CleanDirectoryRecursively(string path)
|
||||
public Result CleanDirectoryRecursively(string path)
|
||||
{
|
||||
string localPath = ResolveLocalPath(PathTools.Normalize(path));
|
||||
|
||||
foreach (string file in Directory.EnumerateFiles(localPath))
|
||||
{
|
||||
DeleteFileInternal(GetFileInfo(file));
|
||||
Result rc = DeleteFileInternal(GetFileInfo(file));
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
foreach (string dir in Directory.EnumerateDirectories(localPath))
|
||||
{
|
||||
DeleteDirectoryInternal(GetDirInfo(dir), true);
|
||||
Result rc = DeleteDirectoryInternal(GetDirInfo(dir), true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void DeleteFile(string path)
|
||||
public Result DeleteFile(string path)
|
||||
{
|
||||
string localPath = ResolveLocalPath(PathTools.Normalize(path));
|
||||
|
||||
FileInfo file = GetFileInfo(localPath);
|
||||
|
||||
DeleteFileInternal(file);
|
||||
return DeleteFileInternal(file);
|
||||
}
|
||||
|
||||
public IDirectory OpenDirectory(string path, OpenDirectoryMode mode)
|
||||
public Result OpenDirectory(out IDirectory directory, string path, OpenDirectoryMode mode)
|
||||
{
|
||||
// Getting the local path is done in the LocalDirectory constructor
|
||||
path = PathTools.Normalize(path);
|
||||
directory = default;
|
||||
|
||||
if (GetEntryType(path) == DirectoryEntryType.File)
|
||||
Result rc = GetEntryType(out DirectoryEntryType entryType, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (entryType == DirectoryEntryType.File)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
return new LocalDirectory(this, path, mode);
|
||||
directory = new LocalDirectory(this, path, mode);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public IFile OpenFile(string path, OpenMode mode)
|
||||
public Result OpenFile(out IFile file, string path, OpenMode mode)
|
||||
{
|
||||
file = default;
|
||||
string localPath = ResolveLocalPath(PathTools.Normalize(path));
|
||||
|
||||
if (GetEntryType(path) == DirectoryEntryType.Directory)
|
||||
Result rc = GetEntryType(out DirectoryEntryType entryType, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (entryType == DirectoryEntryType.Directory)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
return new LocalFile(localPath, mode);
|
||||
file = new LocalFile(localPath, mode);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void RenameDirectory(string srcPath, string dstPath)
|
||||
public Result RenameDirectory(string oldPath, string newPath)
|
||||
{
|
||||
srcPath = PathTools.Normalize(srcPath);
|
||||
dstPath = PathTools.Normalize(dstPath);
|
||||
oldPath = PathTools.Normalize(oldPath);
|
||||
newPath = PathTools.Normalize(newPath);
|
||||
|
||||
// Official FS behavior is to do nothing in this case
|
||||
if (srcPath == dstPath) return;
|
||||
if (oldPath == newPath) return Result.Success;
|
||||
|
||||
// FS does the subpath check before verifying the path exists
|
||||
if (PathTools.IsSubPath(srcPath.AsSpan(), dstPath.AsSpan()))
|
||||
if (PathTools.IsSubPath(oldPath.AsSpan(), newPath.AsSpan()))
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.DestinationIsSubPathOfSource);
|
||||
}
|
||||
|
||||
DirectoryInfo srcDir = GetDirInfo(ResolveLocalPath(srcPath));
|
||||
DirectoryInfo dstDir = GetDirInfo(ResolveLocalPath(dstPath));
|
||||
DirectoryInfo srcDir = GetDirInfo(ResolveLocalPath(oldPath));
|
||||
DirectoryInfo dstDir = GetDirInfo(ResolveLocalPath(newPath));
|
||||
|
||||
RenameDirInternal(srcDir, dstDir);
|
||||
return RenameDirInternal(srcDir, dstDir);
|
||||
}
|
||||
|
||||
public void RenameFile(string srcPath, string dstPath)
|
||||
public Result RenameFile(string oldPath, string newPath)
|
||||
{
|
||||
string srcLocalPath = ResolveLocalPath(PathTools.Normalize(srcPath));
|
||||
string dstLocalPath = ResolveLocalPath(PathTools.Normalize(dstPath));
|
||||
string srcLocalPath = ResolveLocalPath(PathTools.Normalize(oldPath));
|
||||
string dstLocalPath = ResolveLocalPath(PathTools.Normalize(newPath));
|
||||
|
||||
// Official FS behavior is to do nothing in this case
|
||||
if (srcLocalPath == dstLocalPath) return;
|
||||
if (srcLocalPath == dstLocalPath) return Result.Success;
|
||||
|
||||
FileInfo srcFile = GetFileInfo(srcLocalPath);
|
||||
FileInfo dstFile = GetFileInfo(dstLocalPath);
|
||||
|
||||
RenameFileInternal(srcFile, dstFile);
|
||||
return RenameFileInternal(srcFile, dstFile);
|
||||
}
|
||||
|
||||
public DirectoryEntryType GetEntryType(string path)
|
||||
public Result GetEntryType(out DirectoryEntryType entryType, string path)
|
||||
{
|
||||
string localPath = ResolveLocalPath(PathTools.Normalize(path));
|
||||
|
||||
@ -207,48 +226,57 @@ namespace LibHac.Fs
|
||||
|
||||
if (dir.Exists)
|
||||
{
|
||||
return DirectoryEntryType.Directory;
|
||||
entryType = DirectoryEntryType.Directory;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
FileInfo file = GetFileInfo(localPath);
|
||||
|
||||
if (file.Exists)
|
||||
{
|
||||
return DirectoryEntryType.File;
|
||||
entryType = DirectoryEntryType.File;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
return DirectoryEntryType.NotFound;
|
||||
entryType = DirectoryEntryType.NotFound;
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
public FileTimeStampRaw GetFileTimeStampRaw(string path)
|
||||
public Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, string path)
|
||||
{
|
||||
timeStamp = default;
|
||||
string localPath = ResolveLocalPath(PathTools.Normalize(path));
|
||||
|
||||
if (!GetFileInfo(localPath).Exists) ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
|
||||
FileTimeStampRaw timeStamp = default;
|
||||
if (!GetFileInfo(localPath).Exists) return ResultFs.PathNotFound.Log();
|
||||
|
||||
timeStamp.Created = new DateTimeOffset(File.GetCreationTime(localPath)).ToUnixTimeSeconds();
|
||||
timeStamp.Accessed = new DateTimeOffset(File.GetLastAccessTime(localPath)).ToUnixTimeSeconds();
|
||||
timeStamp.Modified = new DateTimeOffset(File.GetLastWriteTime(localPath)).ToUnixTimeSeconds();
|
||||
|
||||
return timeStamp;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public long GetFreeSpaceSize(string path)
|
||||
public Result GetFreeSpaceSize(out long freeSpace, string path)
|
||||
{
|
||||
return new DriveInfo(BasePath).AvailableFreeSpace;
|
||||
freeSpace = new DriveInfo(BasePath).AvailableFreeSpace;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public long GetTotalSpaceSize(string path)
|
||||
public Result GetTotalSpaceSize(out long totalSpace, string path)
|
||||
{
|
||||
return new DriveInfo(BasePath).TotalSize;
|
||||
totalSpace = new DriveInfo(BasePath).TotalSize;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void Commit() { }
|
||||
public Result Commit()
|
||||
{
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, string path, QueryId queryId) =>
|
||||
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperation);
|
||||
public Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, string path)
|
||||
{
|
||||
return ResultFs.UnsupportedOperation.Log();
|
||||
}
|
||||
|
||||
private static long GetSizeInternal(FileInfo file)
|
||||
{
|
||||
@ -268,35 +296,36 @@ namespace LibHac.Fs
|
||||
}
|
||||
}
|
||||
|
||||
private static FileStream CreateFileInternal(FileInfo file)
|
||||
private static Result CreateFileInternal(out FileStream file, FileInfo fileInfo)
|
||||
{
|
||||
file = default;
|
||||
|
||||
try
|
||||
{
|
||||
return new FileStream(file.FullName, FileMode.CreateNew, FileAccess.ReadWrite);
|
||||
file = new FileStream(fileInfo.FullName, FileMode.CreateNew, FileAccess.ReadWrite);
|
||||
}
|
||||
catch (DirectoryNotFoundException ex)
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound, ex);
|
||||
throw;
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
catch (IOException ex) when (ex.HResult == ErrorDiskFull || ex.HResult == ErrorHandleDiskFull)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.InsufficientFreeSpace, ex);
|
||||
throw;
|
||||
return ResultFs.InsufficientFreeSpace.Log();
|
||||
}
|
||||
catch (IOException ex) when (ex.HResult == ErrorFileExists)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathAlreadyExists, ex);
|
||||
throw;
|
||||
return ResultFs.PathAlreadyExists.Log();
|
||||
}
|
||||
catch (Exception ex) when (ex is SecurityException || ex is UnauthorizedAccessException)
|
||||
{
|
||||
// todo: Should a HorizonResultException be thrown?
|
||||
// todo: What Result value should be returned?
|
||||
throw;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private static void SetStreamLengthInternal(Stream stream, long size)
|
||||
private static Result SetStreamLengthInternal(Stream stream, long size)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -304,128 +333,131 @@ 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 void DeleteDirectoryInternal(DirectoryInfo dir, bool recursive)
|
||||
private static Result DeleteDirectoryInternal(DirectoryInfo dir, bool recursive)
|
||||
{
|
||||
if (!dir.Exists) ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
if (!dir.Exists) return ResultFs.PathNotFound.Log();
|
||||
|
||||
try
|
||||
{
|
||||
dir.Delete(recursive);
|
||||
}
|
||||
catch (DirectoryNotFoundException ex)
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound, ex);
|
||||
throw;
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
catch (IOException ex) when (ex.HResult == ErrorDirNotEmpty)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.DirectoryNotEmpty, ex);
|
||||
throw;
|
||||
return ResultFs.DirectoryNotEmpty.Log();
|
||||
}
|
||||
catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException)
|
||||
{
|
||||
// todo: Should a HorizonResultException be thrown?
|
||||
// todo: What Result value should be returned?
|
||||
throw;
|
||||
}
|
||||
|
||||
EnsureDeleted(dir);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private static void DeleteFileInternal(FileInfo file)
|
||||
private static Result DeleteFileInternal(FileInfo file)
|
||||
{
|
||||
if (!file.Exists) ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
if (!file.Exists) return ResultFs.PathNotFound.Log();
|
||||
|
||||
try
|
||||
{
|
||||
file.Delete();
|
||||
}
|
||||
catch (DirectoryNotFoundException ex)
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound, ex);
|
||||
throw;
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
catch (IOException ex) when (ex.HResult == ErrorDirNotEmpty)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.DirectoryNotEmpty, ex);
|
||||
throw;
|
||||
return ResultFs.DirectoryNotEmpty.Log();
|
||||
}
|
||||
catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException)
|
||||
{
|
||||
// todo: Should a HorizonResultException be thrown?
|
||||
// todo: What Result value should be returned?
|
||||
throw;
|
||||
}
|
||||
|
||||
EnsureDeleted(file);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private static void CreateDirInternal(DirectoryInfo dir)
|
||||
private static Result CreateDirInternal(DirectoryInfo dir)
|
||||
{
|
||||
try
|
||||
{
|
||||
dir.Create();
|
||||
}
|
||||
catch (DirectoryNotFoundException ex)
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound, ex);
|
||||
throw;
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
catch (IOException ex) when (ex.HResult == ErrorDiskFull || ex.HResult == ErrorHandleDiskFull)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.InsufficientFreeSpace, ex);
|
||||
throw;
|
||||
return ResultFs.InsufficientFreeSpace.Log();
|
||||
}
|
||||
catch (Exception ex) when (ex is SecurityException || ex is UnauthorizedAccessException)
|
||||
{
|
||||
// todo: Should a HorizonResultException be thrown?
|
||||
// todo: What Result value should be returned?
|
||||
throw;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private static void RenameDirInternal(DirectoryInfo source, DirectoryInfo dest)
|
||||
private static Result RenameDirInternal(DirectoryInfo source, DirectoryInfo dest)
|
||||
{
|
||||
if (!source.Exists) ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
if (dest.Exists) ThrowHelper.ThrowResult(ResultFs.PathAlreadyExists);
|
||||
if (!source.Exists) return ResultFs.PathNotFound.Log();
|
||||
if (dest.Exists) return ResultFs.PathAlreadyExists.Log();
|
||||
|
||||
try
|
||||
{
|
||||
source.MoveTo(dest.FullName);
|
||||
}
|
||||
catch (DirectoryNotFoundException ex)
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound, ex);
|
||||
throw;
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException)
|
||||
{
|
||||
// todo: Should a HorizonResultException be thrown?
|
||||
// todo: What Result value should be returned?
|
||||
throw;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private static void RenameFileInternal(FileInfo source, FileInfo dest)
|
||||
private static Result RenameFileInternal(FileInfo source, FileInfo dest)
|
||||
{
|
||||
if (!source.Exists) ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
if (dest.Exists) ThrowHelper.ThrowResult(ResultFs.PathAlreadyExists);
|
||||
if (!source.Exists) return ResultFs.PathNotFound.Log();
|
||||
if (dest.Exists) return ResultFs.PathAlreadyExists.Log();
|
||||
|
||||
try
|
||||
{
|
||||
source.MoveTo(dest.FullName);
|
||||
}
|
||||
catch (DirectoryNotFoundException ex)
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound, ex);
|
||||
throw;
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException)
|
||||
{
|
||||
// todo: Should a HorizonResultException be thrown?
|
||||
// todo: What Result value should be returned?
|
||||
throw;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
|
||||
|
@ -29,12 +29,13 @@ namespace LibHac.Fs
|
||||
BaseStorage = storage;
|
||||
}
|
||||
|
||||
public IDirectory OpenDirectory(string path, OpenDirectoryMode mode)
|
||||
public Result OpenDirectory(out IDirectory directory, string path, OpenDirectoryMode mode)
|
||||
{
|
||||
return new PartitionDirectory(this, path, mode);
|
||||
directory = new PartitionDirectory(this, path, mode);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public IFile OpenFile(string path, OpenMode mode)
|
||||
public Result OpenFile(out IFile file, string path, OpenMode mode)
|
||||
{
|
||||
path = PathTools.Normalize(path).TrimStart('/');
|
||||
|
||||
@ -43,7 +44,8 @@ namespace LibHac.Fs
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
}
|
||||
|
||||
return OpenFile(entry, mode);
|
||||
file = OpenFile(entry, mode);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public IFile OpenFile(PartitionFileEntry entry, OpenMode mode)
|
||||
@ -51,46 +53,59 @@ namespace LibHac.Fs
|
||||
return new PartitionFile(BaseStorage, HeaderSize + entry.Offset, entry.Size, mode);
|
||||
}
|
||||
|
||||
public DirectoryEntryType GetEntryType(string path)
|
||||
public Result GetEntryType(out DirectoryEntryType entryType, string path)
|
||||
{
|
||||
entryType = DirectoryEntryType.NotFound;
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
if (path == "/") return DirectoryEntryType.Directory;
|
||||
if (path == "/")
|
||||
{
|
||||
entryType = DirectoryEntryType.Directory;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
if (FileDict.ContainsKey(path.TrimStart('/'))) return DirectoryEntryType.File;
|
||||
if (FileDict.ContainsKey(path.TrimStart('/')))
|
||||
{
|
||||
entryType = DirectoryEntryType.File;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
return DirectoryEntryType.NotFound;
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
public void CreateDirectory(string path) => ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyPartitionFileSystem);
|
||||
public void CreateFile(string path, long size, CreateFileOptions options) => ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyPartitionFileSystem);
|
||||
public void DeleteDirectory(string path) => ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyPartitionFileSystem);
|
||||
public void DeleteDirectoryRecursively(string path) => ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyPartitionFileSystem);
|
||||
public void CleanDirectoryRecursively(string path) => ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyPartitionFileSystem);
|
||||
public void DeleteFile(string path) => ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyPartitionFileSystem);
|
||||
public void RenameDirectory(string srcPath, string dstPath) => ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyPartitionFileSystem);
|
||||
public void RenameFile(string srcPath, string dstPath) => ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyPartitionFileSystem);
|
||||
public Result CreateDirectory(string path) => ResultFs.UnsupportedOperationModifyPartitionFileSystem.Log();
|
||||
public Result CreateFile(string path, long size, CreateFileOptions options) => ResultFs.UnsupportedOperationModifyPartitionFileSystem.Log();
|
||||
public Result DeleteDirectory(string path) => ResultFs.UnsupportedOperationModifyPartitionFileSystem.Log();
|
||||
public Result DeleteDirectoryRecursively(string path) => ResultFs.UnsupportedOperationModifyPartitionFileSystem.Log();
|
||||
public Result CleanDirectoryRecursively(string path) => ResultFs.UnsupportedOperationModifyPartitionFileSystem.Log();
|
||||
public Result DeleteFile(string path) => ResultFs.UnsupportedOperationModifyPartitionFileSystem.Log();
|
||||
public Result RenameDirectory(string oldPath, string newPath) => ResultFs.UnsupportedOperationModifyPartitionFileSystem.Log();
|
||||
public Result RenameFile(string oldPath, string newPath) => ResultFs.UnsupportedOperationModifyPartitionFileSystem.Log();
|
||||
|
||||
public long GetFreeSpaceSize(string path)
|
||||
public Result GetFreeSpaceSize(out long freeSpace, string path)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.NotImplemented);
|
||||
return default;
|
||||
freeSpace = default;
|
||||
return ResultFs.NotImplemented.Log();
|
||||
}
|
||||
|
||||
public long GetTotalSpaceSize(string path)
|
||||
public Result GetTotalSpaceSize(out long totalSpace, string path)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.NotImplemented);
|
||||
return default;
|
||||
totalSpace = default;
|
||||
return ResultFs.NotImplemented.Log();
|
||||
}
|
||||
|
||||
public FileTimeStampRaw GetFileTimeStampRaw(string path)
|
||||
public Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, string path)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.NotImplemented);
|
||||
return default;
|
||||
timeStamp = default;
|
||||
return ResultFs.NotImplemented.Log();
|
||||
}
|
||||
|
||||
public void Commit() { }
|
||||
public void QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, string path, QueryId queryId) => ThrowHelper.ThrowResult(ResultFs.NotImplemented);
|
||||
public Result Commit()
|
||||
{
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, string path) => ResultFs.NotImplemented.Log();
|
||||
}
|
||||
|
||||
public enum PartitionFileSystemType
|
||||
|
@ -22,11 +22,13 @@ namespace LibHac.Fs
|
||||
/// </summary>
|
||||
public PartitionFileSystemBuilder(IFileSystem input)
|
||||
{
|
||||
IDirectory rootDir = input.OpenDirectory("/", OpenDirectoryMode.File);
|
||||
input.OpenDirectory(out IDirectory rootDir, "/", OpenDirectoryMode.File).ThrowIfFailure();
|
||||
|
||||
foreach (DirectoryEntry file in rootDir.Read().OrderBy(x => x.FullPath, StringComparer.Ordinal))
|
||||
foreach (DirectoryEntry entry in rootDir.Read().OrderBy(x => x.FullPath, StringComparer.Ordinal))
|
||||
{
|
||||
AddFile(file.FullPath.TrimStart('/'), input.OpenFile(file.FullPath, OpenMode.Read));
|
||||
input.OpenFile(out IFile file, entry.FullPath, OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
AddFile(entry.FullPath.TrimStart('/'), file);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,71 +11,82 @@ namespace LibHac.Fs
|
||||
BaseFs = baseFileSystem;
|
||||
}
|
||||
|
||||
public IDirectory OpenDirectory(string path, OpenDirectoryMode mode)
|
||||
public Result OpenDirectory(out IDirectory directory, string path, OpenDirectoryMode mode)
|
||||
{
|
||||
IDirectory baseDir = BaseFs.OpenDirectory(path, mode);
|
||||
return new ReadOnlyDirectory(this, baseDir);
|
||||
directory = default;
|
||||
|
||||
Result rc = BaseFs.OpenDirectory(out IDirectory baseDir, path, mode);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
directory = new ReadOnlyDirectory(this, baseDir);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public IFile OpenFile(string path, OpenMode mode)
|
||||
public Result OpenFile(out IFile file, string path, OpenMode mode)
|
||||
{
|
||||
IFile baseFile = BaseFs.OpenFile(path, mode);
|
||||
return new ReadOnlyFile(baseFile);
|
||||
file = default;
|
||||
|
||||
Result rc = BaseFs.OpenFile(out IFile baseFile, path, mode);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
file = new ReadOnlyFile(baseFile);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public DirectoryEntryType GetEntryType(string path)
|
||||
public Result GetEntryType(out DirectoryEntryType entryType, string path)
|
||||
{
|
||||
return BaseFs.GetEntryType(path);
|
||||
return BaseFs.GetEntryType(out entryType, path);
|
||||
}
|
||||
|
||||
public long GetFreeSpaceSize(string path)
|
||||
public Result GetFreeSpaceSize(out long freeSpace, string path)
|
||||
{
|
||||
return 0;
|
||||
freeSpace = 0;
|
||||
return Result.Success;
|
||||
|
||||
// FS does:
|
||||
// return ResultFs.UnsupportedOperationReadOnlyFileSystemGetSpace.Log();
|
||||
}
|
||||
|
||||
public long GetTotalSpaceSize(string path)
|
||||
public Result GetTotalSpaceSize(out long totalSpace, string path)
|
||||
{
|
||||
return BaseFs.GetTotalSpaceSize(path);
|
||||
return BaseFs.GetTotalSpaceSize(out totalSpace, path);
|
||||
|
||||
// FS does:
|
||||
// return ResultFs.UnsupportedOperationReadOnlyFileSystemGetSpace.Log();
|
||||
}
|
||||
|
||||
public FileTimeStampRaw GetFileTimeStampRaw(string path)
|
||||
public Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, string path)
|
||||
{
|
||||
return BaseFs.GetFileTimeStampRaw(path);
|
||||
return BaseFs.GetFileTimeStampRaw(out timeStamp, path);
|
||||
|
||||
// FS does:
|
||||
// return ResultFs.NotImplemented.Log();
|
||||
}
|
||||
|
||||
public void Commit()
|
||||
public Result Commit()
|
||||
{
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, string path, QueryId queryId)
|
||||
public Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, string path)
|
||||
{
|
||||
BaseFs.QueryEntry(outBuffer, inBuffer, path, queryId);
|
||||
return ResultFs.NotImplemented.Log();
|
||||
}
|
||||
|
||||
public void CreateDirectory(string path) =>
|
||||
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyReadOnlyFileSystem);
|
||||
public Result CreateDirectory(string path) => ResultFs.UnsupportedOperationModifyReadOnlyFileSystem.Log();
|
||||
|
||||
public void CreateFile(string path, long size, CreateFileOptions options) =>
|
||||
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyReadOnlyFileSystem);
|
||||
public Result CreateFile(string path, long size, CreateFileOptions options) => ResultFs.UnsupportedOperationModifyReadOnlyFileSystem.Log();
|
||||
|
||||
public void DeleteDirectory(string path) =>
|
||||
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyReadOnlyFileSystem);
|
||||
public Result DeleteDirectory(string path) => ResultFs.UnsupportedOperationModifyReadOnlyFileSystem.Log();
|
||||
|
||||
public void DeleteDirectoryRecursively(string path) =>
|
||||
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyReadOnlyFileSystem);
|
||||
public Result DeleteDirectoryRecursively(string path) => ResultFs.UnsupportedOperationModifyReadOnlyFileSystem.Log();
|
||||
|
||||
public void CleanDirectoryRecursively(string path) =>
|
||||
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyReadOnlyFileSystem);
|
||||
public Result CleanDirectoryRecursively(string path) => ResultFs.UnsupportedOperationModifyReadOnlyFileSystem.Log();
|
||||
|
||||
public void DeleteFile(string path) =>
|
||||
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyReadOnlyFileSystem);
|
||||
public Result DeleteFile(string path) => ResultFs.UnsupportedOperationModifyReadOnlyFileSystem.Log();
|
||||
|
||||
public void RenameDirectory(string srcPath, string dstPath) =>
|
||||
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyReadOnlyFileSystem);
|
||||
|
||||
public void RenameFile(string srcPath, string dstPath) =>
|
||||
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyReadOnlyFileSystem);
|
||||
public Result RenameDirectory(string oldPath, string newPath) => ResultFs.UnsupportedOperationModifyReadOnlyFileSystem.Log();
|
||||
|
||||
public Result RenameFile(string oldPath, string newPath) => ResultFs.UnsupportedOperationModifyReadOnlyFileSystem.Log();
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@
|
||||
public static Result InsufficientFreeSpace => new Result(ModuleFs, 30);
|
||||
public static Result MountNameAlreadyExists => new Result(ModuleFs, 60);
|
||||
|
||||
public static Result ResultPartitionNotFound => new Result(ModuleFs, 1002);
|
||||
public static Result TargetNotFound => new Result(ModuleFs, 1002);
|
||||
|
||||
public static Result NotImplemented => new Result(ModuleFs, 3001);
|
||||
|
@ -32,10 +32,12 @@ namespace LibHac.Fs.RomFs
|
||||
/// </summary>
|
||||
public RomFsBuilder(IFileSystem input)
|
||||
{
|
||||
foreach (DirectoryEntry file in input.EnumerateEntries().Where(x => x.Type == DirectoryEntryType.File)
|
||||
foreach (DirectoryEntry entry in input.EnumerateEntries().Where(x => x.Type == DirectoryEntryType.File)
|
||||
.OrderBy(x => x.FullPath, StringComparer.Ordinal))
|
||||
{
|
||||
AddFile(file.FullPath, input.OpenFile(file.FullPath, OpenMode.Read));
|
||||
input.OpenFile(out IFile file, entry.FullPath, OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
AddFile(entry.FullPath, file);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,52 +22,63 @@ namespace LibHac.Fs.RomFs
|
||||
FileTable = new HierarchicalRomFileTable<RomFileInfo>(dirHashTable, dirEntryTable, fileHashTable, fileEntryTable);
|
||||
}
|
||||
|
||||
public DirectoryEntryType GetEntryType(string path)
|
||||
public Result GetEntryType(out DirectoryEntryType entryType, string path)
|
||||
{
|
||||
entryType = DirectoryEntryType.NotFound;
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
if (FileTable.TryOpenFile(path, out RomFileInfo _))
|
||||
{
|
||||
return DirectoryEntryType.File;
|
||||
entryType = DirectoryEntryType.File;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
if (FileTable.TryOpenDirectory(path, out FindPosition _))
|
||||
{
|
||||
return DirectoryEntryType.Directory;
|
||||
entryType = DirectoryEntryType.Directory;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
return DirectoryEntryType.NotFound;
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
public void Commit() { }
|
||||
|
||||
public IDirectory OpenDirectory(string path, OpenDirectoryMode mode)
|
||||
public Result Commit()
|
||||
{
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result OpenDirectory(out IDirectory directory, string path, OpenDirectoryMode mode)
|
||||
{
|
||||
directory = default;
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
if (!FileTable.TryOpenDirectory(path, out FindPosition position))
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
return new RomFsDirectory(this, path, position, mode);
|
||||
directory = new RomFsDirectory(this, path, position, mode);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public IFile OpenFile(string path, OpenMode mode)
|
||||
public Result OpenFile(out IFile file, string path, OpenMode mode)
|
||||
{
|
||||
file = default;
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
if (!FileTable.TryOpenFile(path, out RomFileInfo info))
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
if (mode != OpenMode.Read)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.InvalidArgument, "RomFs files must be opened read-only.");
|
||||
// RomFs files must be opened read-only.
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
|
||||
return new RomFsFile(BaseStorage, Header.DataOffset + info.Offset, info.Length);
|
||||
file = new RomFsFile(BaseStorage, Header.DataOffset + info.Offset, info.Length);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public IStorage GetBaseStorage()
|
||||
@ -75,50 +86,37 @@ namespace LibHac.Fs.RomFs
|
||||
return BaseStorage;
|
||||
}
|
||||
|
||||
public void CreateDirectory(string path) =>
|
||||
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyRomFsFileSystem);
|
||||
public Result CreateDirectory(string path) => ResultFs.UnsupportedOperationModifyRomFsFileSystem.Log();
|
||||
public Result CreateFile(string path, long size, CreateFileOptions options) => ResultFs.UnsupportedOperationModifyRomFsFileSystem.Log();
|
||||
public Result DeleteDirectory(string path) => ResultFs.UnsupportedOperationModifyRomFsFileSystem.Log();
|
||||
public Result DeleteDirectoryRecursively(string path) => ResultFs.UnsupportedOperationModifyRomFsFileSystem.Log();
|
||||
public Result CleanDirectoryRecursively(string path) => ResultFs.UnsupportedOperationModifyRomFsFileSystem.Log();
|
||||
public Result DeleteFile(string path) => ResultFs.UnsupportedOperationModifyRomFsFileSystem.Log();
|
||||
public Result RenameDirectory(string oldPath, string newPath) => ResultFs.UnsupportedOperationModifyRomFsFileSystem.Log();
|
||||
public Result RenameFile(string oldPath, string newPath) => ResultFs.UnsupportedOperationModifyRomFsFileSystem.Log();
|
||||
|
||||
public void CreateFile(string path, long size, CreateFileOptions options) =>
|
||||
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyRomFsFileSystem);
|
||||
|
||||
public void DeleteDirectory(string path) =>
|
||||
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyRomFsFileSystem);
|
||||
|
||||
public void DeleteDirectoryRecursively(string path) =>
|
||||
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyRomFsFileSystem);
|
||||
|
||||
public void CleanDirectoryRecursively(string path) =>
|
||||
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyRomFsFileSystem);
|
||||
|
||||
public void DeleteFile(string path) =>
|
||||
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyRomFsFileSystem);
|
||||
|
||||
public void RenameDirectory(string srcPath, string dstPath) =>
|
||||
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyRomFsFileSystem);
|
||||
|
||||
public void RenameFile(string srcPath, string dstPath) =>
|
||||
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationModifyRomFsFileSystem);
|
||||
|
||||
public long GetFreeSpaceSize(string path)
|
||||
public Result GetFreeSpaceSize(out long freeSpace, string path)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationRomFsFileSystemGetSpace);
|
||||
return default;
|
||||
freeSpace = default;
|
||||
return ResultFs.UnsupportedOperationRomFsFileSystemGetSpace.Log();
|
||||
}
|
||||
|
||||
public long GetTotalSpaceSize(string path)
|
||||
public Result GetTotalSpaceSize(out long totalSpace, string path)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.UnsupportedOperationRomFsFileSystemGetSpace);
|
||||
return default;
|
||||
totalSpace = default;
|
||||
return ResultFs.UnsupportedOperationRomFsFileSystemGetSpace.Log();
|
||||
}
|
||||
|
||||
public FileTimeStampRaw GetFileTimeStampRaw(string path)
|
||||
public Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, string path)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.NotImplemented);
|
||||
return default;
|
||||
timeStamp = default;
|
||||
return ResultFs.NotImplemented.Log();
|
||||
}
|
||||
|
||||
public void QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, string path, QueryId queryId) =>
|
||||
ThrowHelper.ThrowResult(ResultFs.NotImplemented);
|
||||
public Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, string path)
|
||||
{
|
||||
return ResultFs.NotImplemented.Log();
|
||||
}
|
||||
}
|
||||
|
||||
public class RomfsHeader
|
||||
|
@ -338,11 +338,11 @@ namespace LibHac.Fs.Save
|
||||
FileTable.ChangeKey(ref oldKey, ref newKey);
|
||||
}
|
||||
|
||||
public void RenameDirectory(string srcPath, string dstPath)
|
||||
public Result RenameDirectory(string srcPath, string dstPath)
|
||||
{
|
||||
if (srcPath == dstPath || TryOpenFile(dstPath, out _) || TryOpenDirectory(dstPath, out _))
|
||||
{
|
||||
throw new IOException(Messages.DestPathAlreadyExists);
|
||||
return ResultFs.PathAlreadyExists.Log();
|
||||
}
|
||||
|
||||
ReadOnlySpan<byte> oldPathBytes = Util.GetUtf8Bytes(srcPath);
|
||||
@ -350,19 +350,19 @@ namespace LibHac.Fs.Save
|
||||
|
||||
if (!FindPathRecursive(oldPathBytes, out SaveEntryKey oldKey))
|
||||
{
|
||||
throw new DirectoryNotFoundException();
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
int dirIndex = DirectoryTable.GetIndexFromKey(ref oldKey).Index;
|
||||
|
||||
if (!FindPathRecursive(newPathBytes, out SaveEntryKey newKey))
|
||||
{
|
||||
throw new IOException(Messages.PartialPathNotFound);
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
if (PathTools.IsSubPath(oldPathBytes, newPathBytes))
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.DestinationIsSubPathOfSource);
|
||||
return ResultFs.DestinationIsSubPathOfSource.Log();
|
||||
}
|
||||
|
||||
if (oldKey.Parent != newKey.Parent)
|
||||
@ -372,6 +372,8 @@ namespace LibHac.Fs.Save
|
||||
}
|
||||
|
||||
DirectoryTable.ChangeKey(ref oldKey, ref newKey);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public bool TryOpenDirectory(string path, out SaveFindPosition position)
|
||||
|
@ -142,198 +142,114 @@ namespace LibHac.Fs.Save
|
||||
IntegrityStorageType.Save, integrityCheckLevel, LeaveOpen);
|
||||
}
|
||||
|
||||
public void CreateDirectory(string path)
|
||||
public Result CreateDirectory(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
SaveDataFileSystemCore.CreateDirectory(path);
|
||||
}
|
||||
catch (HorizonResultException ex)
|
||||
{
|
||||
ConvertResultException(ex);
|
||||
throw;
|
||||
}
|
||||
Result result = SaveDataFileSystemCore.CreateDirectory(path);
|
||||
|
||||
return SaveResults.ConvertToExternalResult(result);
|
||||
}
|
||||
|
||||
public void CreateFile(string path, long size, CreateFileOptions options)
|
||||
public Result CreateFile(string path, long size, CreateFileOptions options)
|
||||
{
|
||||
try
|
||||
{
|
||||
SaveDataFileSystemCore.CreateFile(path, size, options);
|
||||
}
|
||||
catch (HorizonResultException ex)
|
||||
{
|
||||
ConvertResultException(ex);
|
||||
throw;
|
||||
}
|
||||
Result result = SaveDataFileSystemCore.CreateFile(path, size, options);
|
||||
|
||||
return SaveResults.ConvertToExternalResult(result);
|
||||
}
|
||||
|
||||
public void DeleteDirectory(string path)
|
||||
public Result DeleteDirectory(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
SaveDataFileSystemCore.DeleteDirectory(path);
|
||||
}
|
||||
catch (HorizonResultException ex)
|
||||
{
|
||||
ConvertResultException(ex);
|
||||
throw;
|
||||
}
|
||||
Result result = SaveDataFileSystemCore.DeleteDirectory(path);
|
||||
|
||||
return SaveResults.ConvertToExternalResult(result);
|
||||
}
|
||||
|
||||
public void DeleteDirectoryRecursively(string path)
|
||||
public Result DeleteDirectoryRecursively(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
SaveDataFileSystemCore.DeleteDirectoryRecursively(path);
|
||||
}
|
||||
catch (HorizonResultException ex)
|
||||
{
|
||||
ConvertResultException(ex);
|
||||
throw;
|
||||
}
|
||||
Result result = SaveDataFileSystemCore.DeleteDirectoryRecursively(path);
|
||||
|
||||
return SaveResults.ConvertToExternalResult(result);
|
||||
}
|
||||
|
||||
public void CleanDirectoryRecursively(string path)
|
||||
public Result CleanDirectoryRecursively(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
SaveDataFileSystemCore.CleanDirectoryRecursively(path);
|
||||
}
|
||||
catch (HorizonResultException ex)
|
||||
{
|
||||
ConvertResultException(ex);
|
||||
throw;
|
||||
}
|
||||
Result result = SaveDataFileSystemCore.CleanDirectoryRecursively(path);
|
||||
|
||||
return SaveResults.ConvertToExternalResult(result);
|
||||
}
|
||||
|
||||
public void DeleteFile(string path)
|
||||
public Result DeleteFile(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
SaveDataFileSystemCore.DeleteFile(path);
|
||||
}
|
||||
catch (HorizonResultException ex)
|
||||
{
|
||||
ConvertResultException(ex);
|
||||
throw;
|
||||
}
|
||||
Result result = SaveDataFileSystemCore.DeleteFile(path);
|
||||
|
||||
return SaveResults.ConvertToExternalResult(result);
|
||||
}
|
||||
|
||||
public IDirectory OpenDirectory(string path, OpenDirectoryMode mode)
|
||||
public Result OpenDirectory(out IDirectory directory, string path, OpenDirectoryMode mode)
|
||||
{
|
||||
try
|
||||
{
|
||||
return SaveDataFileSystemCore.OpenDirectory(path, mode);
|
||||
}
|
||||
catch (HorizonResultException ex)
|
||||
{
|
||||
ConvertResultException(ex);
|
||||
throw;
|
||||
}
|
||||
Result result = SaveDataFileSystemCore.OpenDirectory(out directory, path, mode);
|
||||
|
||||
return SaveResults.ConvertToExternalResult(result);
|
||||
}
|
||||
|
||||
public IFile OpenFile(string path, OpenMode mode)
|
||||
public Result OpenFile(out IFile file, string path, OpenMode mode)
|
||||
{
|
||||
try
|
||||
{
|
||||
return SaveDataFileSystemCore.OpenFile(path, mode);
|
||||
}
|
||||
catch (HorizonResultException ex)
|
||||
{
|
||||
ConvertResultException(ex);
|
||||
throw;
|
||||
}
|
||||
Result result = SaveDataFileSystemCore.OpenFile(out file, path, mode);
|
||||
|
||||
return SaveResults.ConvertToExternalResult(result);
|
||||
}
|
||||
|
||||
public void RenameDirectory(string srcPath, string dstPath)
|
||||
public Result RenameDirectory(string oldPath, string newPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
SaveDataFileSystemCore.RenameDirectory(srcPath, dstPath);
|
||||
}
|
||||
catch (HorizonResultException ex)
|
||||
{
|
||||
ConvertResultException(ex);
|
||||
throw;
|
||||
}
|
||||
Result result = SaveDataFileSystemCore.RenameDirectory(oldPath, newPath);
|
||||
|
||||
return SaveResults.ConvertToExternalResult(result);
|
||||
}
|
||||
|
||||
public void RenameFile(string srcPath, string dstPath)
|
||||
public Result RenameFile(string oldPath, string newPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
SaveDataFileSystemCore.RenameFile(srcPath, dstPath);
|
||||
}
|
||||
catch (HorizonResultException ex)
|
||||
{
|
||||
ConvertResultException(ex);
|
||||
throw;
|
||||
}
|
||||
Result result = SaveDataFileSystemCore.RenameFile(oldPath, newPath);
|
||||
|
||||
return SaveResults.ConvertToExternalResult(result);
|
||||
}
|
||||
|
||||
public DirectoryEntryType GetEntryType(string path)
|
||||
public Result GetEntryType(out DirectoryEntryType entryType, string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
return SaveDataFileSystemCore.GetEntryType(path);
|
||||
}
|
||||
catch (HorizonResultException ex)
|
||||
{
|
||||
ConvertResultException(ex);
|
||||
throw;
|
||||
}
|
||||
Result result = SaveDataFileSystemCore.GetEntryType(out entryType, path);
|
||||
|
||||
return SaveResults.ConvertToExternalResult(result);
|
||||
}
|
||||
|
||||
public long GetFreeSpaceSize(string path)
|
||||
public Result GetFreeSpaceSize(out long freeSpace, string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
return SaveDataFileSystemCore.GetFreeSpaceSize(path);
|
||||
}
|
||||
catch (HorizonResultException ex)
|
||||
{
|
||||
ConvertResultException(ex);
|
||||
throw;
|
||||
}
|
||||
Result result = SaveDataFileSystemCore.GetFreeSpaceSize(out freeSpace, path);
|
||||
|
||||
return SaveResults.ConvertToExternalResult(result);
|
||||
}
|
||||
|
||||
public long GetTotalSpaceSize(string path)
|
||||
public Result GetTotalSpaceSize(out long totalSpace, string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
return SaveDataFileSystemCore.GetTotalSpaceSize(path);
|
||||
}
|
||||
catch (HorizonResultException ex)
|
||||
{
|
||||
ConvertResultException(ex);
|
||||
throw;
|
||||
}
|
||||
Result result = SaveDataFileSystemCore.GetTotalSpaceSize(out totalSpace, path);
|
||||
|
||||
return SaveResults.ConvertToExternalResult(result);
|
||||
}
|
||||
|
||||
public void Commit()
|
||||
public Result Commit()
|
||||
{
|
||||
try
|
||||
{
|
||||
Commit(Keyset);
|
||||
}
|
||||
catch (HorizonResultException ex)
|
||||
{
|
||||
ConvertResultException(ex);
|
||||
throw;
|
||||
}
|
||||
Result result = Commit(Keyset);
|
||||
|
||||
return SaveResults.ConvertToExternalResult(result);
|
||||
}
|
||||
|
||||
public FileTimeStampRaw GetFileTimeStampRaw(string path)
|
||||
public Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, string path)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.NotImplemented);
|
||||
return default;
|
||||
timeStamp = default;
|
||||
return ResultFs.NotImplemented.Log();
|
||||
}
|
||||
|
||||
public void QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, string path, QueryId queryId) =>
|
||||
ThrowHelper.ThrowResult(ResultFs.NotImplemented);
|
||||
public Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, string path) =>
|
||||
ResultFs.NotImplemented.Log();
|
||||
|
||||
public bool Commit(Keyset keyset)
|
||||
public Result Commit(Keyset keyset)
|
||||
{
|
||||
CoreDataIvfcStorage.Flush();
|
||||
FatIvfcStorage?.Flush();
|
||||
@ -349,7 +265,7 @@ namespace LibHac.Fs.Save
|
||||
headerStream.Position = 0x108;
|
||||
headerStream.Write(hash, 0, hash.Length);
|
||||
|
||||
if (keyset == null || keyset.SaveMacKey.IsEmpty()) return false;
|
||||
if (keyset == null || keyset.SaveMacKey.IsEmpty()) return ResultFs.PreconditionViolation;
|
||||
|
||||
var cmacData = new byte[0x200];
|
||||
var cmac = new byte[0x10];
|
||||
@ -363,7 +279,7 @@ namespace LibHac.Fs.Save
|
||||
headerStream.Write(cmac, 0, 0x10);
|
||||
headerStream.Flush();
|
||||
|
||||
return true;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void FsTrim()
|
||||
@ -395,10 +311,5 @@ namespace LibHac.Fs.Save
|
||||
|
||||
return journalValidity;
|
||||
}
|
||||
|
||||
private void ConvertResultException(HorizonResultException ex)
|
||||
{
|
||||
ex.ResultValue = SaveResults.ConvertToExternalResult(ex.ResultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,14 +27,16 @@ namespace LibHac.Fs.Save
|
||||
FileTable = new HierarchicalSaveFileTable(dirTableStorage, fileTableStorage);
|
||||
}
|
||||
|
||||
public void CreateDirectory(string path)
|
||||
public Result CreateDirectory(string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
FileTable.AddDirectory(path);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void CreateFile(string path, long size, CreateFileOptions options)
|
||||
public Result CreateFile(string path, long size, CreateFileOptions options)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
@ -43,7 +45,7 @@ namespace LibHac.Fs.Save
|
||||
var emptyFileEntry = new SaveFileInfo { StartBlock = int.MinValue, Length = size };
|
||||
FileTable.AddFile(path, ref emptyFileEntry);
|
||||
|
||||
return;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
int blockCount = (int)Util.DivideByRoundUp(size, AllocationTable.Header.BlockSize);
|
||||
@ -51,45 +53,56 @@ namespace LibHac.Fs.Save
|
||||
|
||||
if (startBlock == -1)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.AllocationTableInsufficientFreeBlocks,
|
||||
"Not enough available space to create file.");
|
||||
return ResultFs.AllocationTableInsufficientFreeBlocks.Log();
|
||||
}
|
||||
|
||||
var fileEntry = new SaveFileInfo { StartBlock = startBlock, Length = size };
|
||||
|
||||
FileTable.AddFile(path, ref fileEntry);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void DeleteDirectory(string path)
|
||||
public Result DeleteDirectory(string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
FileTable.DeleteDirectory(path);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void DeleteDirectoryRecursively(string path)
|
||||
public Result DeleteDirectoryRecursively(string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
CleanDirectoryRecursively(path);
|
||||
Result rc = CleanDirectoryRecursively(path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
DeleteDirectory(path);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void CleanDirectoryRecursively(string path)
|
||||
public Result CleanDirectoryRecursively(string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
IDirectory dir = OpenDirectory(path, OpenDirectoryMode.All);
|
||||
Result rc = OpenDirectory(out IDirectory dir, path, OpenDirectoryMode.All);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
FileSystemExtensions.CleanDirectoryRecursivelyGeneric(dir);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void DeleteFile(string path)
|
||||
public Result DeleteFile(string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
if (!FileTable.TryOpenFile(path, out SaveFileInfo fileInfo))
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
if (fileInfo.StartBlock != int.MinValue)
|
||||
@ -98,91 +111,110 @@ namespace LibHac.Fs.Save
|
||||
}
|
||||
|
||||
FileTable.DeleteFile(path);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public IDirectory OpenDirectory(string path, OpenDirectoryMode mode)
|
||||
public Result OpenDirectory(out IDirectory directory, string path, OpenDirectoryMode mode)
|
||||
{
|
||||
directory = default;
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
if (!FileTable.TryOpenDirectory(path, out SaveFindPosition position))
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
return new SaveDataDirectory(this, path, position, mode);
|
||||
directory = new SaveDataDirectory(this, path, position, mode);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public IFile OpenFile(string path, OpenMode mode)
|
||||
public Result OpenFile(out IFile file, string path, OpenMode mode)
|
||||
{
|
||||
file = default;
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
if (!FileTable.TryOpenFile(path, out SaveFileInfo file))
|
||||
if (!FileTable.TryOpenFile(path, out SaveFileInfo fileInfo))
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound);
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
AllocationTableStorage storage = OpenFatStorage(file.StartBlock);
|
||||
AllocationTableStorage storage = OpenFatStorage(fileInfo.StartBlock);
|
||||
|
||||
return new SaveDataFile(storage, path, FileTable, file.Length, mode);
|
||||
file = new SaveDataFile(storage, path, FileTable, fileInfo.Length, mode);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void RenameDirectory(string srcPath, string dstPath)
|
||||
public Result RenameDirectory(string oldPath, string newPath)
|
||||
{
|
||||
srcPath = PathTools.Normalize(srcPath);
|
||||
dstPath = PathTools.Normalize(dstPath);
|
||||
oldPath = PathTools.Normalize(oldPath);
|
||||
newPath = PathTools.Normalize(newPath);
|
||||
|
||||
FileTable.RenameDirectory(srcPath, dstPath);
|
||||
return FileTable.RenameDirectory(oldPath, newPath);
|
||||
}
|
||||
|
||||
public void RenameFile(string srcPath, string dstPath)
|
||||
public Result RenameFile(string oldPath, string newPath)
|
||||
{
|
||||
srcPath = PathTools.Normalize(srcPath);
|
||||
dstPath = PathTools.Normalize(dstPath);
|
||||
oldPath = PathTools.Normalize(oldPath);
|
||||
newPath = PathTools.Normalize(newPath);
|
||||
|
||||
FileTable.RenameFile(srcPath, dstPath);
|
||||
FileTable.RenameFile(oldPath, newPath);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public DirectoryEntryType GetEntryType(string path)
|
||||
public Result GetEntryType(out DirectoryEntryType entryType, string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
if (FileTable.TryOpenFile(path, out SaveFileInfo _))
|
||||
{
|
||||
return DirectoryEntryType.File;
|
||||
entryType = DirectoryEntryType.File;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
if (FileTable.TryOpenDirectory(path, out SaveFindPosition _))
|
||||
{
|
||||
return DirectoryEntryType.Directory;
|
||||
entryType = DirectoryEntryType.Directory;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
return DirectoryEntryType.NotFound;
|
||||
entryType = DirectoryEntryType.NotFound;
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
public long GetFreeSpaceSize(string path)
|
||||
public Result GetFreeSpaceSize(out long freeSpace, string path)
|
||||
{
|
||||
int freeBlockCount = AllocationTable.GetFreeListLength();
|
||||
return Header.BlockSize * freeBlockCount;
|
||||
freeSpace = Header.BlockSize * freeBlockCount;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public long GetTotalSpaceSize(string path)
|
||||
public Result GetTotalSpaceSize(out long totalSpace, string path)
|
||||
{
|
||||
return Header.BlockSize * Header.BlockCount;
|
||||
totalSpace = Header.BlockSize * Header.BlockCount;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void Commit()
|
||||
public Result Commit()
|
||||
{
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public FileTimeStampRaw GetFileTimeStampRaw(string path)
|
||||
public Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, string path)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.NotImplemented);
|
||||
return default;
|
||||
timeStamp = default;
|
||||
return ResultFs.NotImplemented.Log();
|
||||
}
|
||||
|
||||
public void QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, string path, QueryId queryId) =>
|
||||
ThrowHelper.ThrowResult(ResultFs.NotImplemented);
|
||||
public Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, string path)
|
||||
{
|
||||
return ResultFs.NotImplemented.Log();
|
||||
}
|
||||
|
||||
public IStorage GetBaseStorage() => BaseStorage.AsReadOnly();
|
||||
public IStorage GetHeaderStorage() => HeaderStorage.AsReadOnly();
|
||||
|
@ -18,118 +18,119 @@ namespace LibHac.Fs
|
||||
RootPath = PathTools.Normalize(rootPath);
|
||||
}
|
||||
|
||||
public void CreateDirectory(string path)
|
||||
public Result CreateDirectory(string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
ParentFileSystem.CreateDirectory(ResolveFullPath(path));
|
||||
return ParentFileSystem.CreateDirectory(ResolveFullPath(path));
|
||||
}
|
||||
|
||||
public void CreateFile(string path, long size, CreateFileOptions options)
|
||||
public Result CreateFile(string path, long size, CreateFileOptions options)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
ParentFileSystem.CreateFile(ResolveFullPath(path), size, options);
|
||||
return ParentFileSystem.CreateFile(ResolveFullPath(path), size, options);
|
||||
}
|
||||
|
||||
public void DeleteDirectory(string path)
|
||||
public Result DeleteDirectory(string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
ParentFileSystem.DeleteDirectory(ResolveFullPath(path));
|
||||
return ParentFileSystem.DeleteDirectory(ResolveFullPath(path));
|
||||
}
|
||||
|
||||
public void DeleteDirectoryRecursively(string path)
|
||||
public Result DeleteDirectoryRecursively(string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
ParentFileSystem.DeleteDirectoryRecursively(ResolveFullPath(path));
|
||||
return ParentFileSystem.DeleteDirectoryRecursively(ResolveFullPath(path));
|
||||
}
|
||||
|
||||
public void CleanDirectoryRecursively(string path)
|
||||
public Result CleanDirectoryRecursively(string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
ParentFileSystem.CleanDirectoryRecursively(ResolveFullPath(path));
|
||||
return ParentFileSystem.CleanDirectoryRecursively(ResolveFullPath(path));
|
||||
}
|
||||
|
||||
public void DeleteFile(string path)
|
||||
public Result DeleteFile(string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
ParentFileSystem.DeleteFile(ResolveFullPath(path));
|
||||
return ParentFileSystem.DeleteFile(ResolveFullPath(path));
|
||||
}
|
||||
|
||||
public IDirectory OpenDirectory(string path, OpenDirectoryMode mode)
|
||||
public Result OpenDirectory(out IDirectory directory, string path, OpenDirectoryMode mode)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
IDirectory baseDir = ParentFileSystem.OpenDirectory(ResolveFullPath(path), mode);
|
||||
ParentFileSystem.OpenDirectory(out IDirectory baseDir, ResolveFullPath(path), mode);
|
||||
|
||||
return new SubdirectoryFileSystemDirectory(this, baseDir, path, mode);
|
||||
directory = new SubdirectoryFileSystemDirectory(this, baseDir, path, mode);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public IFile OpenFile(string path, OpenMode mode)
|
||||
public Result OpenFile(out IFile file, string path, OpenMode mode)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
return ParentFileSystem.OpenFile(ResolveFullPath(path), mode);
|
||||
return ParentFileSystem.OpenFile(out file, ResolveFullPath(path), mode);
|
||||
}
|
||||
|
||||
public void RenameDirectory(string srcPath, string dstPath)
|
||||
public Result RenameDirectory(string oldPath, string newPath)
|
||||
{
|
||||
srcPath = PathTools.Normalize(srcPath);
|
||||
dstPath = PathTools.Normalize(dstPath);
|
||||
oldPath = PathTools.Normalize(oldPath);
|
||||
newPath = PathTools.Normalize(newPath);
|
||||
|
||||
ParentFileSystem.RenameDirectory(ResolveFullPath(srcPath), ResolveFullPath(dstPath));
|
||||
return ParentFileSystem.RenameDirectory(oldPath, newPath);
|
||||
}
|
||||
|
||||
public void RenameFile(string srcPath, string dstPath)
|
||||
public Result RenameFile(string oldPath, string newPath)
|
||||
{
|
||||
srcPath = PathTools.Normalize(srcPath);
|
||||
dstPath = PathTools.Normalize(dstPath);
|
||||
oldPath = PathTools.Normalize(oldPath);
|
||||
newPath = PathTools.Normalize(newPath);
|
||||
|
||||
ParentFileSystem.RenameFile(ResolveFullPath(srcPath), ResolveFullPath(dstPath));
|
||||
return ParentFileSystem.RenameFile(oldPath, newPath);
|
||||
}
|
||||
|
||||
public DirectoryEntryType GetEntryType(string path)
|
||||
public Result GetEntryType(out DirectoryEntryType entryType, string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
return ParentFileSystem.GetEntryType(ResolveFullPath(path));
|
||||
return ParentFileSystem.GetEntryType(out entryType, ResolveFullPath(path));
|
||||
}
|
||||
|
||||
public void Commit()
|
||||
public Result Commit()
|
||||
{
|
||||
ParentFileSystem.Commit();
|
||||
return ParentFileSystem.Commit();
|
||||
}
|
||||
|
||||
public long GetFreeSpaceSize(string path)
|
||||
public Result GetFreeSpaceSize(out long freeSpace, string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
return ParentFileSystem.GetFreeSpaceSize(ResolveFullPath(path));
|
||||
return ParentFileSystem.GetFreeSpaceSize(out freeSpace, ResolveFullPath(path));
|
||||
}
|
||||
|
||||
public long GetTotalSpaceSize(string path)
|
||||
public Result GetTotalSpaceSize(out long totalSpace, string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
return ParentFileSystem.GetTotalSpaceSize(ResolveFullPath(path));
|
||||
return ParentFileSystem.GetTotalSpaceSize(out totalSpace, ResolveFullPath(path));
|
||||
}
|
||||
|
||||
public FileTimeStampRaw GetFileTimeStampRaw(string path)
|
||||
public Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
return ParentFileSystem.GetFileTimeStampRaw(ResolveFullPath(path));
|
||||
return ParentFileSystem.GetFileTimeStampRaw(out timeStamp, ResolveFullPath(path));
|
||||
}
|
||||
|
||||
public void QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, string path, QueryId queryId)
|
||||
public Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
ParentFileSystem.QueryEntry(outBuffer, inBuffer, ResolveFullPath(path), queryId);
|
||||
return ParentFileSystem.QueryEntry(outBuffer, inBuffer, queryId, ResolveFullPath(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,117 +26,125 @@ namespace LibHac.FsClient.Accessors
|
||||
FsManager = fsManager;
|
||||
}
|
||||
|
||||
public void CreateDirectory(string path)
|
||||
public Result CreateDirectory(string path)
|
||||
{
|
||||
FileSystem.CreateDirectory(path);
|
||||
return FileSystem.CreateDirectory(path);
|
||||
}
|
||||
|
||||
public void CreateFile(string path, long size, CreateFileOptions options)
|
||||
public Result CreateFile(string path, long size, CreateFileOptions options)
|
||||
{
|
||||
FileSystem.CreateFile(path, size, options);
|
||||
return FileSystem.CreateFile(path, size, options);
|
||||
}
|
||||
|
||||
public void DeleteDirectory(string path)
|
||||
public Result DeleteDirectory(string path)
|
||||
{
|
||||
FileSystem.DeleteDirectory(path);
|
||||
return FileSystem.DeleteDirectory(path);
|
||||
}
|
||||
|
||||
public void DeleteDirectoryRecursively(string path)
|
||||
public Result DeleteDirectoryRecursively(string path)
|
||||
{
|
||||
FileSystem.DeleteDirectoryRecursively(path);
|
||||
return FileSystem.DeleteDirectoryRecursively(path);
|
||||
}
|
||||
|
||||
public void CleanDirectoryRecursively(string path)
|
||||
public Result CleanDirectoryRecursively(string path)
|
||||
{
|
||||
FileSystem.CleanDirectoryRecursively(path);
|
||||
return FileSystem.CleanDirectoryRecursively(path);
|
||||
}
|
||||
|
||||
public void DeleteFile(string path)
|
||||
public Result DeleteFile(string path)
|
||||
{
|
||||
FileSystem.DeleteFile(path);
|
||||
return FileSystem.DeleteFile(path);
|
||||
}
|
||||
|
||||
public DirectoryAccessor OpenDirectory(string path, OpenDirectoryMode mode)
|
||||
public Result OpenDirectory(out DirectoryAccessor directory, string path, OpenDirectoryMode mode)
|
||||
{
|
||||
IDirectory dir = FileSystem.OpenDirectory(path, mode);
|
||||
directory = default;
|
||||
|
||||
var accessor = new DirectoryAccessor(dir, this);
|
||||
Result rc = FileSystem.OpenDirectory(out IDirectory rawDirectory, path, mode);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var accessor = new DirectoryAccessor(rawDirectory, this);
|
||||
|
||||
lock (_locker)
|
||||
{
|
||||
OpenDirectories.Add(accessor);
|
||||
}
|
||||
|
||||
return accessor;
|
||||
directory = accessor;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public FileAccessor OpenFile(string path, OpenMode mode)
|
||||
public Result OpenFile(out FileAccessor file, string path, OpenMode mode)
|
||||
{
|
||||
IFile file = FileSystem.OpenFile(path, mode);
|
||||
file = default;
|
||||
|
||||
var accessor = new FileAccessor(file, this, mode);
|
||||
Result rc = FileSystem.OpenFile(out IFile rawFile, path, mode);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var accessor = new FileAccessor(rawFile, this, mode);
|
||||
|
||||
lock (_locker)
|
||||
{
|
||||
OpenFiles.Add(accessor);
|
||||
}
|
||||
|
||||
return accessor;
|
||||
file = accessor;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void RenameDirectory(string srcPath, string dstPath)
|
||||
public Result RenameDirectory(string oldPath, string newPath)
|
||||
{
|
||||
FileSystem.RenameDirectory(srcPath, dstPath);
|
||||
return FileSystem.RenameDirectory(oldPath, newPath);
|
||||
}
|
||||
|
||||
public void RenameFile(string srcPath, string dstPath)
|
||||
public Result RenameFile(string oldPath, string newPath)
|
||||
{
|
||||
FileSystem.RenameFile(srcPath, dstPath);
|
||||
return FileSystem.RenameFile(oldPath, newPath);
|
||||
}
|
||||
|
||||
public void DirectoryExists(string path)
|
||||
public bool DirectoryExists(string path)
|
||||
{
|
||||
FileSystem.DirectoryExists(path);
|
||||
return FileSystem.DirectoryExists(path);
|
||||
}
|
||||
|
||||
public void FileExists(string path)
|
||||
public bool FileExists(string path)
|
||||
{
|
||||
FileSystem.FileExists(path);
|
||||
return FileSystem.FileExists(path);
|
||||
}
|
||||
|
||||
public DirectoryEntryType GetEntryType(string path)
|
||||
public Result GetEntryType(out DirectoryEntryType type, string path)
|
||||
{
|
||||
return FileSystem.GetEntryType(path);
|
||||
return FileSystem.GetEntryType(out type, path);
|
||||
}
|
||||
|
||||
public long GetFreeSpaceSize(string path)
|
||||
public Result GetFreeSpaceSize(out long freeSpace, string path)
|
||||
{
|
||||
return FileSystem.GetFreeSpaceSize(path);
|
||||
return FileSystem.GetFreeSpaceSize(out freeSpace, path);
|
||||
}
|
||||
|
||||
public long GetTotalSpaceSize(string path)
|
||||
public Result GetTotalSpaceSize(out long totalSpace, string path)
|
||||
{
|
||||
return FileSystem.GetTotalSpaceSize(path);
|
||||
return FileSystem.GetTotalSpaceSize(out totalSpace, path);
|
||||
}
|
||||
|
||||
public FileTimeStampRaw GetFileTimeStampRaw(string path)
|
||||
public Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, string path)
|
||||
{
|
||||
return FileSystem.GetFileTimeStampRaw(path);
|
||||
return FileSystem.GetFileTimeStampRaw(out timeStamp, path);
|
||||
}
|
||||
|
||||
public void Commit()
|
||||
public Result Commit()
|
||||
{
|
||||
if (OpenFiles.Any(x => (x.OpenMode & OpenMode.Write) != 0))
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.WritableFileOpen);
|
||||
return ResultFs.WritableFileOpen.Log();
|
||||
}
|
||||
|
||||
FileSystem.Commit();
|
||||
return FileSystem.Commit();
|
||||
}
|
||||
|
||||
public void QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, string path, QueryId queryId)
|
||||
public Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, string path, QueryId queryId)
|
||||
{
|
||||
FileSystem.QueryEntry(outBuffer, inBuffer, path, queryId);
|
||||
return FileSystem.QueryEntry(outBuffer, inBuffer, queryId, path);
|
||||
}
|
||||
|
||||
internal void NotifyCloseFile(FileAccessor file)
|
||||
|
@ -12,7 +12,7 @@ namespace LibHac.FsClient
|
||||
Directory = directory;
|
||||
}
|
||||
|
||||
public int GetId() => Directory.GetHashCode();
|
||||
public int GetId() => Directory?.GetHashCode() ?? 0;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
@ -12,7 +12,7 @@ namespace LibHac.FsClient
|
||||
File = file;
|
||||
}
|
||||
|
||||
public int GetId() => File.GetHashCode();
|
||||
public int GetId() => File?.GetHashCode() ?? 0;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
@ -55,12 +55,12 @@ namespace LibHac.FsClient
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public FileHandle OpenFile(out FileHandle handle, string path, OpenMode mode)
|
||||
public Result OpenFile(out FileHandle handle, string path, OpenMode mode)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public DirectoryHandle OpenDirectory(out DirectoryHandle handle, string path, OpenDirectoryMode mode)
|
||||
public Result OpenDirectory(out DirectoryHandle handle, string path, OpenDirectoryMode mode)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ using LibHac.FsClient.Accessors;
|
||||
|
||||
namespace LibHac.FsClient
|
||||
{
|
||||
// Todo: Access log for FindFileSystem
|
||||
public class FileSystemManager
|
||||
{
|
||||
internal Horizon Os { get; }
|
||||
@ -66,213 +67,229 @@ namespace LibHac.FsClient
|
||||
if (accessLog != null) AccessLog = accessLog;
|
||||
}
|
||||
|
||||
public void CreateDirectory(string path)
|
||||
public Result CreateDirectory(string path)
|
||||
{
|
||||
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||
.ThrowIfFailure();
|
||||
Result rc = FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (IsEnabledAccessLog() && fileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
fileSystem.CreateDirectory(subPath.ToString());
|
||||
rc = fileSystem.CreateDirectory(subPath.ToString());
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, $", path: \"{path}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
fileSystem.CreateDirectory(subPath.ToString());
|
||||
rc = fileSystem.CreateDirectory(subPath.ToString());
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
public void CreateFile(string path, long size)
|
||||
public Result CreateFile(string path, long size)
|
||||
{
|
||||
CreateFile(path, size, CreateFileOptions.None);
|
||||
return CreateFile(path, size, CreateFileOptions.None);
|
||||
}
|
||||
|
||||
public void CreateFile(string path, long size, CreateFileOptions options)
|
||||
public Result CreateFile(string path, long size, CreateFileOptions options)
|
||||
{
|
||||
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||
.ThrowIfFailure();
|
||||
Result rc = FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (IsEnabledAccessLog() && fileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
fileSystem.CreateFile(subPath.ToString(), size, options);
|
||||
rc = fileSystem.CreateFile(subPath.ToString(), size, options);
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, $", path: \"{path}\", size: {size}");
|
||||
}
|
||||
else
|
||||
{
|
||||
fileSystem.CreateFile(subPath.ToString(), size, options);
|
||||
rc = fileSystem.CreateFile(subPath.ToString(), size, options);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
public void DeleteDirectory(string path)
|
||||
public Result DeleteDirectory(string path)
|
||||
{
|
||||
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||
.ThrowIfFailure();
|
||||
Result rc = FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (IsEnabledAccessLog() && fileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
fileSystem.DeleteDirectory(subPath.ToString());
|
||||
rc = fileSystem.DeleteDirectory(subPath.ToString());
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, $", path: \"{path}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
fileSystem.DeleteDirectory(subPath.ToString());
|
||||
rc = fileSystem.DeleteDirectory(subPath.ToString());
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
public void DeleteDirectoryRecursively(string path)
|
||||
public Result DeleteDirectoryRecursively(string path)
|
||||
{
|
||||
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||
.ThrowIfFailure();
|
||||
Result rc = FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (IsEnabledAccessLog() && fileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
fileSystem.DeleteDirectoryRecursively(subPath.ToString());
|
||||
rc = fileSystem.DeleteDirectoryRecursively(subPath.ToString());
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, $", path: \"{path}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
fileSystem.DeleteDirectoryRecursively(subPath.ToString());
|
||||
rc = fileSystem.DeleteDirectoryRecursively(subPath.ToString());
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
public void CleanDirectoryRecursively(string path)
|
||||
public Result CleanDirectoryRecursively(string path)
|
||||
{
|
||||
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||
.ThrowIfFailure();
|
||||
Result rc = FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (IsEnabledAccessLog() && fileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
fileSystem.CleanDirectoryRecursively(subPath.ToString());
|
||||
rc = fileSystem.CleanDirectoryRecursively(subPath.ToString());
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, $", path: \"{path}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
fileSystem.CleanDirectoryRecursively(subPath.ToString());
|
||||
rc = fileSystem.CleanDirectoryRecursively(subPath.ToString());
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
public void DeleteFile(string path)
|
||||
public Result DeleteFile(string path)
|
||||
{
|
||||
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||
.ThrowIfFailure();
|
||||
Result rc = FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (IsEnabledAccessLog() && fileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
fileSystem.DeleteFile(subPath.ToString());
|
||||
rc = fileSystem.DeleteFile(subPath.ToString());
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, $", path: \"{path}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
fileSystem.DeleteFile(subPath.ToString());
|
||||
rc = fileSystem.DeleteFile(subPath.ToString());
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
public void RenameDirectory(string oldPath, string newPath)
|
||||
public Result RenameDirectory(string oldPath, string newPath)
|
||||
{
|
||||
FindFileSystem(oldPath.AsSpan(), out FileSystemAccessor oldFileSystem, out ReadOnlySpan<char> oldSubPath)
|
||||
.ThrowIfFailure();
|
||||
Result rc = FindFileSystem(oldPath.AsSpan(), out FileSystemAccessor oldFileSystem, out ReadOnlySpan<char> oldSubPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
FindFileSystem(newPath.AsSpan(), out FileSystemAccessor newFileSystem, out ReadOnlySpan<char> newSubPath)
|
||||
.ThrowIfFailure();
|
||||
rc = FindFileSystem(newPath.AsSpan(), out FileSystemAccessor newFileSystem, out ReadOnlySpan<char> newSubPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (oldFileSystem != newFileSystem)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.DifferentDestFileSystem);
|
||||
return ResultFs.DifferentDestFileSystem.Log();
|
||||
}
|
||||
|
||||
if (IsEnabledAccessLog() && oldFileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
oldFileSystem.RenameDirectory(oldSubPath.ToString(), newSubPath.ToString());
|
||||
rc = oldFileSystem.RenameDirectory(oldSubPath.ToString(), newSubPath.ToString());
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, $", path: \"{oldPath}\", new_path: \"{newPath}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
oldFileSystem.RenameDirectory(oldSubPath.ToString(), newSubPath.ToString());
|
||||
rc = oldFileSystem.RenameDirectory(oldSubPath.ToString(), newSubPath.ToString());
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
public void RenameFile(string oldPath, string newPath)
|
||||
public Result RenameFile(string oldPath, string newPath)
|
||||
{
|
||||
FindFileSystem(oldPath.AsSpan(), out FileSystemAccessor oldFileSystem, out ReadOnlySpan<char> oldSubPath)
|
||||
.ThrowIfFailure();
|
||||
Result rc = FindFileSystem(oldPath.AsSpan(), out FileSystemAccessor oldFileSystem, out ReadOnlySpan<char> oldSubPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
FindFileSystem(newPath.AsSpan(), out FileSystemAccessor newFileSystem, out ReadOnlySpan<char> newSubPath)
|
||||
.ThrowIfFailure();
|
||||
rc = FindFileSystem(newPath.AsSpan(), out FileSystemAccessor newFileSystem, out ReadOnlySpan<char> newSubPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (oldFileSystem != newFileSystem)
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.DifferentDestFileSystem);
|
||||
return ResultFs.DifferentDestFileSystem.Log();
|
||||
}
|
||||
|
||||
if (IsEnabledAccessLog() && oldFileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
oldFileSystem.RenameFile(oldSubPath.ToString(), newSubPath.ToString());
|
||||
rc = oldFileSystem.RenameFile(oldSubPath.ToString(), newSubPath.ToString());
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, $", path: \"{oldPath}\", new_path: \"{newPath}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
oldFileSystem.RenameFile(oldSubPath.ToString(), newSubPath.ToString());
|
||||
rc = oldFileSystem.RenameFile(oldSubPath.ToString(), newSubPath.ToString());
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
public DirectoryEntryType GetEntryType(string path)
|
||||
public Result GetEntryType(out DirectoryEntryType type, string path)
|
||||
{
|
||||
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||
.ThrowIfFailure();
|
||||
type = default;
|
||||
|
||||
DirectoryEntryType type;
|
||||
Result rc = FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (IsEnabledAccessLog() && fileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
type = fileSystem.GetEntryType(subPath.ToString());
|
||||
rc = fileSystem.GetEntryType(out type, subPath.ToString());
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, $", path: \"{path}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
type = fileSystem.GetEntryType(subPath.ToString());
|
||||
rc = fileSystem.GetEntryType(out type, subPath.ToString());
|
||||
}
|
||||
|
||||
return type;
|
||||
return rc;
|
||||
}
|
||||
|
||||
public FileHandle OpenFile(string path, OpenMode mode)
|
||||
public Result OpenFile(out FileHandle handle, string path, OpenMode mode)
|
||||
{
|
||||
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||
.ThrowIfFailure();
|
||||
handle = default;
|
||||
|
||||
FileHandle handle;
|
||||
Result rc = FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (IsEnabledAccessLog() && fileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
FileAccessor file = fileSystem.OpenFile(subPath.ToString(), mode);
|
||||
rc = fileSystem.OpenFile(out FileAccessor file, subPath.ToString(), mode);
|
||||
handle = new FileHandle(file);
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
@ -280,24 +297,24 @@ namespace LibHac.FsClient
|
||||
}
|
||||
else
|
||||
{
|
||||
FileAccessor file = fileSystem.OpenFile(subPath.ToString(), mode);
|
||||
rc = fileSystem.OpenFile(out FileAccessor file, subPath.ToString(), mode);
|
||||
handle = new FileHandle(file);
|
||||
}
|
||||
|
||||
return handle;
|
||||
return rc;
|
||||
}
|
||||
|
||||
public DirectoryHandle OpenDirectory(string path, OpenDirectoryMode mode)
|
||||
public Result OpenDirectory(out DirectoryHandle handle, string path, OpenDirectoryMode mode)
|
||||
{
|
||||
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||
.ThrowIfFailure();
|
||||
handle = default;
|
||||
|
||||
DirectoryHandle handle;
|
||||
Result rc = FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (IsEnabledAccessLog() && fileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
DirectoryAccessor dir = fileSystem.OpenDirectory(subPath.ToString(), mode);
|
||||
rc = fileSystem.OpenDirectory(out DirectoryAccessor dir, subPath.ToString(), mode);
|
||||
handle = new DirectoryHandle(dir);
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
@ -305,94 +322,105 @@ namespace LibHac.FsClient
|
||||
}
|
||||
else
|
||||
{
|
||||
DirectoryAccessor dir = fileSystem.OpenDirectory(subPath.ToString(), mode);
|
||||
rc = fileSystem.OpenDirectory(out DirectoryAccessor dir, subPath.ToString(), mode);
|
||||
handle = new DirectoryHandle(dir);
|
||||
}
|
||||
|
||||
return handle;
|
||||
return rc;
|
||||
}
|
||||
|
||||
public long GetFreeSpaceSize(string path)
|
||||
public Result GetFreeSpaceSize(out long freeSpace, string path)
|
||||
{
|
||||
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||
.ThrowIfFailure();
|
||||
freeSpace = default;
|
||||
|
||||
return fileSystem.GetFreeSpaceSize(subPath.ToString());
|
||||
Result rc = FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return fileSystem.GetFreeSpaceSize(out freeSpace, subPath.ToString());
|
||||
}
|
||||
|
||||
public long GetTotalSpaceSize(string path)
|
||||
public Result GetTotalSpaceSize(out long totalSpace, string path)
|
||||
{
|
||||
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||
.ThrowIfFailure();
|
||||
totalSpace = default;
|
||||
|
||||
return fileSystem.GetTotalSpaceSize(subPath.ToString());
|
||||
Result rc = FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return fileSystem.GetTotalSpaceSize(out totalSpace, subPath.ToString());
|
||||
}
|
||||
|
||||
public FileTimeStampRaw GetFileTimeStamp(string path)
|
||||
public Result GetFileTimeStamp(out FileTimeStampRaw timeStamp, string path)
|
||||
{
|
||||
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||
.ThrowIfFailure();
|
||||
timeStamp = default;
|
||||
|
||||
return fileSystem.GetFileTimeStampRaw(subPath.ToString());
|
||||
Result rc = FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return fileSystem.GetFileTimeStampRaw(out timeStamp, subPath.ToString());
|
||||
}
|
||||
|
||||
public void Commit(string mountName)
|
||||
public Result Commit(string mountName)
|
||||
{
|
||||
MountTable.Find(mountName, out FileSystemAccessor fileSystem).ThrowIfFailure();
|
||||
Result rc = MountTable.Find(mountName, out FileSystemAccessor fileSystem);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (IsEnabledAccessLog() && fileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
fileSystem.Commit();
|
||||
rc = fileSystem.Commit();
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, $", name: \"{mountName}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
fileSystem.Commit();
|
||||
rc = fileSystem.Commit();
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
// ==========================
|
||||
// Operations on file handles
|
||||
// ==========================
|
||||
public int ReadFile(FileHandle handle, Span<byte> destination, long offset)
|
||||
public Result ReadFile(out long bytesRead, FileHandle handle, Span<byte> destination, long offset)
|
||||
{
|
||||
return ReadFile(handle, destination, offset, ReadOption.None);
|
||||
return ReadFile(out bytesRead, handle, destination, offset, ReadOption.None);
|
||||
}
|
||||
|
||||
public int ReadFile(FileHandle handle, Span<byte> destination, long offset, ReadOption option)
|
||||
public Result ReadFile(out long bytesRead, FileHandle handle, Span<byte> destination, long offset, ReadOption option)
|
||||
{
|
||||
long bytesRead;
|
||||
Result rc;
|
||||
|
||||
if (IsEnabledAccessLog() && IsEnabledHandleAccessLog(handle))
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
handle.File.Read(out bytesRead, offset, destination, option).ThrowIfFailure();
|
||||
rc = handle.File.Read(out bytesRead, offset, destination, option);
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, handle, $", offset: {offset}, size: {destination.Length}");
|
||||
}
|
||||
else
|
||||
{
|
||||
handle.File.Read(out bytesRead, offset, destination, option).ThrowIfFailure();
|
||||
rc = handle.File.Read(out bytesRead, offset, destination, option);
|
||||
}
|
||||
|
||||
return (int)bytesRead;
|
||||
return rc;
|
||||
}
|
||||
|
||||
public void WriteFile(FileHandle handle, ReadOnlySpan<byte> source, long offset)
|
||||
public Result WriteFile(FileHandle handle, ReadOnlySpan<byte> source, long offset)
|
||||
{
|
||||
WriteFile(handle, source, offset, WriteOption.None);
|
||||
return WriteFile(handle, source, offset, WriteOption.None);
|
||||
}
|
||||
|
||||
public void WriteFile(FileHandle handle, ReadOnlySpan<byte> source, long offset, WriteOption option)
|
||||
public Result WriteFile(FileHandle handle, ReadOnlySpan<byte> source, long offset, WriteOption option)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
if (IsEnabledAccessLog() && IsEnabledHandleAccessLog(handle))
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
handle.File.Write(offset, source, option);
|
||||
rc = handle.File.Write(offset, source, option);
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
string optionString = (option & WriteOption.Flush) == 0 ? "" : $", write_option: {option}";
|
||||
@ -401,47 +429,55 @@ namespace LibHac.FsClient
|
||||
}
|
||||
else
|
||||
{
|
||||
handle.File.Write(offset, source, option);
|
||||
rc = handle.File.Write(offset, source, option);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
public void FlushFile(FileHandle handle)
|
||||
public Result FlushFile(FileHandle handle)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
if (IsEnabledAccessLog() && IsEnabledHandleAccessLog(handle))
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
handle.File.Flush();
|
||||
rc = handle.File.Flush();
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, handle, string.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
handle.File.Flush();
|
||||
rc = handle.File.Flush();
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
public long GetFileSize(FileHandle handle)
|
||||
public Result GetFileSize(out long fileSize, FileHandle handle)
|
||||
{
|
||||
handle.File.GetSize(out long fileSize).ThrowIfFailure();
|
||||
|
||||
return fileSize;
|
||||
return handle.File.GetSize(out fileSize);
|
||||
}
|
||||
|
||||
public void SetFileSize(FileHandle handle, long size)
|
||||
public Result SetFileSize(FileHandle handle, long size)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
if (IsEnabledAccessLog() && IsEnabledHandleAccessLog(handle))
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
handle.File.SetSize(size);
|
||||
rc = handle.File.SetSize(size);
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, handle, $", size: {size}");
|
||||
}
|
||||
else
|
||||
{
|
||||
handle.File.SetSize(size);
|
||||
rc = handle.File.SetSize(size);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
public OpenMode GetFileOpenMode(FileHandle handle)
|
||||
@ -468,9 +504,11 @@ namespace LibHac.FsClient
|
||||
// ==========================
|
||||
// Operations on directory handles
|
||||
// ==========================
|
||||
public int GetDirectoryEntryCount(DirectoryHandle handle)
|
||||
public Result GetDirectoryEntryCount(out long count, DirectoryHandle handle)
|
||||
{
|
||||
return handle.Directory.GetEntryCount();
|
||||
count = handle.Directory.GetEntryCount();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public IEnumerable<DirectoryEntry> ReadDirectory(DirectoryHandle handle)
|
||||
|
@ -7,10 +7,13 @@ namespace LibHac.FsClient
|
||||
{
|
||||
public static class FileSystemManagerUtils
|
||||
{
|
||||
public static void CopyDirectory(this FileSystemManager fs, string sourcePath, string destPath,
|
||||
public static Result CopyDirectory(this FileSystemManager fs, string sourcePath, string destPath,
|
||||
CreateFileOptions options = CreateFileOptions.None, IProgressReport logger = null)
|
||||
{
|
||||
using (DirectoryHandle sourceHandle = fs.OpenDirectory(sourcePath, OpenDirectoryMode.All))
|
||||
Result rc = fs.OpenDirectory(out DirectoryHandle sourceHandle, sourcePath, OpenDirectoryMode.All);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
using (sourceHandle)
|
||||
{
|
||||
foreach (DirectoryEntry entry in fs.ReadDirectory(sourceHandle))
|
||||
{
|
||||
@ -21,7 +24,8 @@ namespace LibHac.FsClient
|
||||
{
|
||||
fs.EnsureDirectoryExists(subDstPath);
|
||||
|
||||
fs.CopyDirectory(subSrcPath, subDstPath, options, logger);
|
||||
rc = fs.CopyDirectory(subSrcPath, subDstPath, options, logger);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
if (entry.Type == DirectoryEntryType.File)
|
||||
@ -29,46 +33,65 @@ namespace LibHac.FsClient
|
||||
logger?.LogMessage(subSrcPath);
|
||||
fs.CreateOrOverwriteFile(subDstPath, entry.Size, options);
|
||||
|
||||
fs.CopyFile(subSrcPath, subDstPath, logger);
|
||||
rc = fs.CopyFile(subSrcPath, subDstPath, logger);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static void CopyFile(this FileSystemManager fs, string sourcePath, string destPath, IProgressReport logger = null)
|
||||
public static Result CopyFile(this FileSystemManager fs, string sourcePath, string destPath, IProgressReport logger = null)
|
||||
{
|
||||
using (FileHandle sourceHandle = fs.OpenFile(sourcePath, OpenMode.Read))
|
||||
using (FileHandle destHandle = fs.OpenFile(destPath, OpenMode.Write | OpenMode.AllowAppend))
|
||||
Result rc = fs.OpenFile(out FileHandle sourceHandle, sourcePath, OpenMode.Read);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
using (sourceHandle)
|
||||
{
|
||||
const int maxBufferSize = 0x10000;
|
||||
rc = fs.OpenFile(out FileHandle destHandle, destPath, OpenMode.Write | OpenMode.AllowAppend);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
long fileSize = fs.GetFileSize(sourceHandle);
|
||||
int bufferSize = (int)Math.Min(maxBufferSize, fileSize);
|
||||
|
||||
logger?.SetTotal(fileSize);
|
||||
|
||||
byte[] buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
|
||||
try
|
||||
using (destHandle)
|
||||
{
|
||||
for (long offset = 0; offset < fileSize; offset += bufferSize)
|
||||
const int maxBufferSize = 0x10000;
|
||||
|
||||
rc = fs.GetFileSize(out long fileSize, sourceHandle);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
int bufferSize = (int)Math.Min(maxBufferSize, fileSize);
|
||||
|
||||
logger?.SetTotal(fileSize);
|
||||
|
||||
byte[] buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
|
||||
try
|
||||
{
|
||||
int toRead = (int)Math.Min(fileSize - offset, bufferSize);
|
||||
Span<byte> buf = buffer.AsSpan(0, toRead);
|
||||
for (long offset = 0; offset < fileSize; offset += bufferSize)
|
||||
{
|
||||
int toRead = (int)Math.Min(fileSize - offset, bufferSize);
|
||||
Span<byte> buf = buffer.AsSpan(0, toRead);
|
||||
|
||||
fs.ReadFile(sourceHandle, buf, offset);
|
||||
fs.WriteFile(destHandle, buf, offset);
|
||||
rc = fs.ReadFile(out long _, sourceHandle, buf, offset);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
logger?.ReportAdd(toRead);
|
||||
rc = fs.WriteFile(destHandle, buf, offset);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
logger?.ReportAdd(toRead);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
logger?.SetTotal(0);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
logger?.SetTotal(0);
|
||||
}
|
||||
|
||||
fs.FlushFile(destHandle);
|
||||
rc = fs.FlushFile(destHandle);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static IEnumerable<DirectoryEntry> EnumerateEntries(this FileSystemManager fs, string path)
|
||||
@ -86,7 +109,9 @@ namespace LibHac.FsClient
|
||||
bool ignoreCase = searchOptions.HasFlag(SearchOptions.CaseInsensitive);
|
||||
bool recurse = searchOptions.HasFlag(SearchOptions.RecurseSubdirectories);
|
||||
|
||||
using (DirectoryHandle sourceHandle = fs.OpenDirectory(path, OpenDirectoryMode.All))
|
||||
fs.OpenDirectory(out DirectoryHandle sourceHandle, path, OpenDirectoryMode.All).ThrowIfFailure();
|
||||
|
||||
using (sourceHandle)
|
||||
{
|
||||
foreach (DirectoryEntry entry in fs.ReadDirectory(sourceHandle))
|
||||
{
|
||||
@ -112,12 +137,16 @@ namespace LibHac.FsClient
|
||||
|
||||
public static bool DirectoryExists(this FileSystemManager fs, string path)
|
||||
{
|
||||
return fs.GetEntryType(path) == DirectoryEntryType.Directory;
|
||||
Result rc = fs.GetEntryType(out DirectoryEntryType type, path);
|
||||
|
||||
return (rc.IsSuccess() && type == DirectoryEntryType.Directory);
|
||||
}
|
||||
|
||||
public static bool FileExists(this FileSystemManager fs, string path)
|
||||
{
|
||||
return fs.GetEntryType(path) == DirectoryEntryType.File;
|
||||
Result rc = fs.GetEntryType(out DirectoryEntryType type, path);
|
||||
|
||||
return (rc.IsSuccess() && type == DirectoryEntryType.File);
|
||||
}
|
||||
|
||||
public static void EnsureDirectoryExists(this FileSystemManager fs, string path)
|
||||
|
@ -5,6 +5,6 @@ namespace LibHac.FsService.Creators
|
||||
public interface IHostFileSystemCreator
|
||||
{
|
||||
Result Create(out IFileSystem fileSystem, bool someBool);
|
||||
Result Create(out IFileSystem fileSystem, string path, bool someBool);
|
||||
Result Create(out IFileSystem fileSystem, string path, bool openCaseSensitive);
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ namespace LibHac.FsService.Creators
|
||||
{
|
||||
public interface ITargetManagerFileSystemCreator
|
||||
{
|
||||
Result Create(out IFileSystem fileSystem, bool someBool);
|
||||
Result Create(out IFileSystem fileSystem, bool openCaseSensitive);
|
||||
Result GetCaseSensitivePath(out bool isSuccess, ref string path);
|
||||
}
|
||||
}
|
@ -6,16 +6,10 @@ namespace LibHac.FsService.Creators
|
||||
{
|
||||
public Result Create(out IFileSystem subDirFileSystem, IFileSystem baseFileSystem, string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
baseFileSystem.OpenDirectory(path, OpenDirectoryMode.Directory);
|
||||
}
|
||||
catch (HorizonResultException ex)
|
||||
{
|
||||
subDirFileSystem = default;
|
||||
subDirFileSystem = default;
|
||||
|
||||
return ex.ResultValue;
|
||||
}
|
||||
Result rc = baseFileSystem.OpenDirectory(out IDirectory _, path, OpenDirectoryMode.Directory);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
subDirFileSystem = new SubdirectoryFileSystem(baseFileSystem, path);
|
||||
|
||||
|
@ -11,11 +11,13 @@ namespace LibHac.FsService
|
||||
|
||||
if (!createPathIfMissing)
|
||||
{
|
||||
if (path == null) return ResultFs.NullArgument;
|
||||
if (path == null) return ResultFs.NullArgument.Log();
|
||||
|
||||
if (baseFileSystem.GetEntryType(path) != DirectoryEntryType.Directory)
|
||||
Result rc = baseFileSystem.GetEntryType(out DirectoryEntryType entryType, path);
|
||||
|
||||
if (rc.IsFailure() || entryType != DirectoryEntryType.Directory)
|
||||
{
|
||||
return ResultFs.PathNotFound;
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,16 +30,10 @@ namespace LibHac.FsService
|
||||
{
|
||||
subFileSystem = default;
|
||||
|
||||
if (path == null) return ResultFs.NullArgument;
|
||||
if (path == null) return ResultFs.NullArgument.Log();
|
||||
|
||||
try
|
||||
{
|
||||
baseFileSystem.OpenDirectory(path, OpenDirectoryMode.Directory);
|
||||
}
|
||||
catch (HorizonResultException ex)
|
||||
{
|
||||
return ex.ResultValue;
|
||||
}
|
||||
Result rc = baseFileSystem.OpenDirectory(out IDirectory _, path, OpenDirectoryMode.Directory);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
subFileSystem = new SubdirectoryFileSystem(baseFileSystem, path);
|
||||
|
||||
|
@ -67,9 +67,10 @@ namespace LibHac
|
||||
|
||||
private void OpenAllNcas()
|
||||
{
|
||||
ContentFs.OpenDirectory(out IDirectory rootDir, "/", OpenDirectoryMode.All).ThrowIfFailure();
|
||||
|
||||
// Todo: give warning if directories named "*.nca" are found or manually fix the archive bit
|
||||
IEnumerable<DirectoryEntry> files = ContentFs.OpenDirectory("/", OpenDirectoryMode.All)
|
||||
.EnumerateEntries("*.nca", SearchOptions.RecurseSubdirectories)
|
||||
IEnumerable<DirectoryEntry> files = rootDir.EnumerateEntries("*.nca", SearchOptions.RecurseSubdirectories)
|
||||
.Where(x => x.Type == DirectoryEntryType.File);
|
||||
|
||||
foreach (DirectoryEntry fileEntry in files)
|
||||
@ -77,9 +78,9 @@ namespace LibHac
|
||||
SwitchFsNca nca = null;
|
||||
try
|
||||
{
|
||||
IStorage storage = ContentFs.OpenFile(fileEntry.FullPath, OpenMode.Read).AsStorage();
|
||||
ContentFs.OpenFile(out IFile ncaFile, fileEntry.FullPath, OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
nca = new SwitchFsNca(new Nca(Keyset, storage));
|
||||
nca = new SwitchFsNca(new Nca(Keyset, ncaFile.AsStorage()));
|
||||
|
||||
nca.NcaId = GetNcaFilename(fileEntry.Name, nca);
|
||||
string extension = nca.Nca.Header.ContentType == NcaContentType.Meta ? ".cnmt.nca" : ".nca";
|
||||
@ -115,7 +116,8 @@ namespace LibHac
|
||||
|
||||
try
|
||||
{
|
||||
IFile file = SaveFs.OpenFile(fileEntry.FullPath, OpenMode.Read);
|
||||
SaveFs.OpenFile(out IFile file, fileEntry.FullPath, OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
save = new SaveDataFileSystem(Keyset, file.AsStorage(), IntegrityCheckLevel.None, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -141,7 +143,7 @@ namespace LibHac
|
||||
IFileSystem fs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
||||
string cnmtPath = fs.EnumerateEntries("*.cnmt").Single().FullPath;
|
||||
|
||||
IFile file = fs.OpenFile(cnmtPath, OpenMode.Read);
|
||||
fs.OpenFile(out IFile file, cnmtPath, OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
var metadata = new Cnmt(file.AsStream());
|
||||
title.Id = metadata.TitleId;
|
||||
@ -185,7 +187,7 @@ namespace LibHac
|
||||
foreach (Title title in Titles.Values.Where(x => x.ControlNca != null))
|
||||
{
|
||||
IFileSystem romfs = title.ControlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
||||
IFile control = romfs.OpenFile("control.nacp", OpenMode.Read);
|
||||
romfs.OpenFile(out IFile control, "control.nacp", OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
title.Control = new Nacp(control.AsStream());
|
||||
|
||||
|
@ -28,8 +28,8 @@ namespace LibHac
|
||||
XciPartition root = GetRootPartition();
|
||||
if (type == XciPartitionType.Root) return root;
|
||||
|
||||
IStorage partitionStorage = root.OpenFile(type.GetFileName(), OpenMode.Read).AsStorage();
|
||||
return new XciPartition(partitionStorage);
|
||||
root.OpenFile(out IFile partitionFile, type.GetFileName(), OpenMode.Read).ThrowIfFailure();
|
||||
return new XciPartition(partitionFile.AsStorage());
|
||||
}
|
||||
|
||||
private XciPartition GetRootPartition()
|
||||
|
@ -26,7 +26,9 @@ namespace hactoolnet
|
||||
private static void CopyDirectoryWithProgressInternal(FileSystemManager fs, string sourcePath, string destPath,
|
||||
CreateFileOptions options, IProgressReport logger)
|
||||
{
|
||||
using (DirectoryHandle sourceHandle = fs.OpenDirectory(sourcePath, OpenDirectoryMode.All))
|
||||
fs.OpenDirectory(out DirectoryHandle sourceHandle, sourcePath, OpenDirectoryMode.All).ThrowIfFailure();
|
||||
|
||||
using (sourceHandle)
|
||||
{
|
||||
foreach (DirectoryEntry entry in fs.ReadDirectory(sourceHandle))
|
||||
{
|
||||
@ -63,37 +65,53 @@ namespace hactoolnet
|
||||
return size;
|
||||
}
|
||||
|
||||
public static void CopyFileWithProgress(FileSystemManager fs, string sourcePath, string destPath, IProgressReport logger = null)
|
||||
public static Result CopyFileWithProgress(FileSystemManager fs, string sourcePath, string destPath, IProgressReport logger = null)
|
||||
{
|
||||
using (FileHandle sourceHandle = fs.OpenFile(sourcePath, OpenMode.Read))
|
||||
using (FileHandle destHandle = fs.OpenFile(destPath, OpenMode.Write | OpenMode.AllowAppend))
|
||||
Result rc = fs.OpenFile(out FileHandle sourceHandle, sourcePath, OpenMode.Read);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
using (sourceHandle)
|
||||
{
|
||||
const int maxBufferSize = 1024 * 1024;
|
||||
rc = fs.OpenFile(out FileHandle destHandle, destPath, OpenMode.Write | OpenMode.AllowAppend);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
long fileSize = fs.GetFileSize(sourceHandle);
|
||||
int bufferSize = (int)Math.Min(maxBufferSize, fileSize);
|
||||
|
||||
byte[] buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
|
||||
try
|
||||
using (destHandle)
|
||||
{
|
||||
for (long offset = 0; offset < fileSize; offset += bufferSize)
|
||||
const int maxBufferSize = 1024 * 1024;
|
||||
|
||||
rc = fs.GetFileSize(out long fileSize, sourceHandle);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
int bufferSize = (int)Math.Min(maxBufferSize, fileSize);
|
||||
|
||||
byte[] buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
|
||||
try
|
||||
{
|
||||
int toRead = (int)Math.Min(fileSize - offset, bufferSize);
|
||||
Span<byte> buf = buffer.AsSpan(0, toRead);
|
||||
for (long offset = 0; offset < fileSize; offset += bufferSize)
|
||||
{
|
||||
int toRead = (int)Math.Min(fileSize - offset, bufferSize);
|
||||
Span<byte> buf = buffer.AsSpan(0, toRead);
|
||||
|
||||
fs.ReadFile(sourceHandle, buf, offset);
|
||||
fs.WriteFile(destHandle, buf, offset);
|
||||
rc = fs.ReadFile(out long _, sourceHandle, buf, offset);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
logger?.ReportAdd(toRead);
|
||||
rc = fs.WriteFile(destHandle, buf, offset);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
logger?.ReportAdd(toRead);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
|
||||
fs.FlushFile(destHandle);
|
||||
rc = fs.FlushFile(destHandle);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,9 @@ namespace hactoolnet
|
||||
throw new FileNotFoundException("Specified NCA does not contain a delta fragment");
|
||||
}
|
||||
|
||||
deltaStorage = fs.OpenFile(FragmentFileName, OpenMode.Read).AsStorage();
|
||||
fs.OpenFile(out IFile deltaFragmentFile, FragmentFileName, OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
deltaStorage = deltaFragmentFile.AsStorage();
|
||||
}
|
||||
catch (InvalidDataException) { } // Ignore non-NCA3 files
|
||||
}
|
||||
|
@ -210,8 +210,8 @@ namespace hactoolnet
|
||||
IFileSystem pfs = nca.OpenFileSystem(NcaSectionType.Code, IntegrityCheckLevel.ErrorOnInvalid);
|
||||
if (!pfs.FileExists("main.npdm")) return Validity.Unchecked;
|
||||
|
||||
IFile npdmStorage = pfs.OpenFile("main.npdm", OpenMode.Read);
|
||||
var npdm = new NpdmBinary(npdmStorage.AsStream());
|
||||
pfs.OpenFile(out IFile npdmFile, "main.npdm", OpenMode.Read).ThrowIfFailure();
|
||||
var npdm = new NpdmBinary(npdmFile.AsStream());
|
||||
|
||||
return nca.Header.VerifySignature2(npdm.AciD.Rsa2048Modulus);
|
||||
}
|
||||
|
@ -62,7 +62,9 @@ namespace hactoolnet
|
||||
|
||||
using (IFile inFile = new LocalFile(ctx.Options.ReplaceFileSource, OpenMode.Read))
|
||||
{
|
||||
using (IFile outFile = save.OpenFile(destFilename, OpenMode.ReadWrite))
|
||||
save.OpenFile(out IFile outFile, destFilename, OpenMode.ReadWrite).ThrowIfFailure();
|
||||
|
||||
using (outFile)
|
||||
{
|
||||
inFile.GetSize(out long inFileSize).ThrowIfFailure();
|
||||
outFile.GetSize(out long outFileSize).ThrowIfFailure();
|
||||
@ -100,7 +102,7 @@ namespace hactoolnet
|
||||
{
|
||||
if (signNeeded)
|
||||
{
|
||||
save.Commit(ctx.Keyset);
|
||||
save.Commit(ctx.Keyset).ThrowIfFailure();
|
||||
signNeeded = false;
|
||||
}
|
||||
}
|
||||
@ -114,7 +116,7 @@ namespace hactoolnet
|
||||
|
||||
if (signNeeded)
|
||||
{
|
||||
if (save.Commit(ctx.Keyset))
|
||||
if (save.Commit(ctx.Keyset).IsSuccess())
|
||||
{
|
||||
ctx.Logger.LogMessage("Successfully signed save file");
|
||||
}
|
||||
@ -310,7 +312,7 @@ namespace hactoolnet
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine();
|
||||
|
||||
long freeSpace = save.GetFreeSpaceSize("");
|
||||
save.GetFreeSpaceSize(out long freeSpace, "").ThrowIfFailure();
|
||||
|
||||
sb.AppendLine("Savefile:");
|
||||
PrintItem(sb, colLen, $"CMAC Signature{save.Header.SignatureValidity.GetValidityString()}:", save.Header.Cmac);
|
||||
|
Loading…
x
Reference in New Issue
Block a user