mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add DirectorySaveDataFileSystem
This commit is contained in:
parent
5b764e487e
commit
0fa9fccc5f
5 changed files with 313 additions and 9 deletions
61
src/LibHac/Fs/DirectorySaveDataFile.cs
Normal file
61
src/LibHac/Fs/DirectorySaveDataFile.cs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace LibHac.Fs
|
||||||
|
{
|
||||||
|
public class DirectorySaveDataFile : FileBase
|
||||||
|
{
|
||||||
|
private IFile BaseFile { get; }
|
||||||
|
private DirectorySaveDataFileSystem ParentFs { get; }
|
||||||
|
private object DisposeLocker { get; } = new object();
|
||||||
|
|
||||||
|
public DirectorySaveDataFile(DirectorySaveDataFileSystem parentFs, IFile baseFile)
|
||||||
|
{
|
||||||
|
ParentFs = parentFs;
|
||||||
|
BaseFile = baseFile;
|
||||||
|
Mode = BaseFile.Mode;
|
||||||
|
ToDispose.Add(BaseFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int Read(Span<byte> destination, long offset)
|
||||||
|
{
|
||||||
|
return BaseFile.Read(destination, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(ReadOnlySpan<byte> source, long offset)
|
||||||
|
{
|
||||||
|
BaseFile.Write(source, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Flush()
|
||||||
|
{
|
||||||
|
BaseFile.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override long GetSize()
|
||||||
|
{
|
||||||
|
return BaseFile.GetSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetSize(long size)
|
||||||
|
{
|
||||||
|
BaseFile.SetSize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
lock (DisposeLocker)
|
||||||
|
{
|
||||||
|
if (IsDisposed) return;
|
||||||
|
|
||||||
|
base.Dispose(disposing);
|
||||||
|
|
||||||
|
if (Mode.HasFlag(OpenMode.Write))
|
||||||
|
{
|
||||||
|
ParentFs.NotifyCloseWritableFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
238
src/LibHac/Fs/DirectorySaveDataFileSystem.cs
Normal file
238
src/LibHac/Fs/DirectorySaveDataFileSystem.cs
Normal file
|
@ -0,0 +1,238 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace LibHac.Fs
|
||||||
|
{
|
||||||
|
public class DirectorySaveDataFileSystem : IFileSystem
|
||||||
|
{
|
||||||
|
private const string CommittedDir = "/0/";
|
||||||
|
private const string WorkingDir = "/1/";
|
||||||
|
private const string SyncDir = "/_/";
|
||||||
|
|
||||||
|
private IFileSystem BaseFs { get; }
|
||||||
|
private object Locker { get; } = new object();
|
||||||
|
private int OpenWritableFileCount { get; set; }
|
||||||
|
|
||||||
|
public DirectorySaveDataFileSystem(IFileSystem baseFileSystem)
|
||||||
|
{
|
||||||
|
BaseFs = baseFileSystem;
|
||||||
|
|
||||||
|
if (!BaseFs.DirectoryExists(WorkingDir))
|
||||||
|
{
|
||||||
|
BaseFs.CreateDirectory(WorkingDir);
|
||||||
|
BaseFs.CreateDirectory(CommittedDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BaseFs.DirectoryExists(CommittedDir))
|
||||||
|
{
|
||||||
|
SynchronizeDirectory(WorkingDir, CommittedDir);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SynchronizeDirectory(SyncDir, WorkingDir);
|
||||||
|
BaseFs.RenameDirectory(SyncDir, CommittedDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateDirectory(string path)
|
||||||
|
{
|
||||||
|
string fullPath = GetFullPath(PathTools.Normalize(path));
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
BaseFs.CreateDirectory(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateFile(string path, long size, CreateFileOptions options)
|
||||||
|
{
|
||||||
|
string fullPath = GetFullPath(PathTools.Normalize(path));
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
BaseFs.CreateFile(fullPath, size, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteDirectory(string path)
|
||||||
|
{
|
||||||
|
string fullPath = GetFullPath(PathTools.Normalize(path));
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
BaseFs.DeleteDirectory(fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteDirectoryRecursively(string path)
|
||||||
|
{
|
||||||
|
string fullPath = GetFullPath(PathTools.Normalize(path));
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
BaseFs.DeleteDirectoryRecursively(fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CleanDirectoryRecursively(string path)
|
||||||
|
{
|
||||||
|
string fullPath = GetFullPath(PathTools.Normalize(path));
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
BaseFs.CleanDirectoryRecursively(fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteFile(string path)
|
||||||
|
{
|
||||||
|
string fullPath = GetFullPath(PathTools.Normalize(path));
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
BaseFs.DeleteFile(fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDirectory OpenDirectory(string path, OpenDirectoryMode mode)
|
||||||
|
{
|
||||||
|
string fullPath = GetFullPath(PathTools.Normalize(path));
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
return BaseFs.OpenDirectory(fullPath, mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IFile OpenFile(string path, OpenMode mode)
|
||||||
|
{
|
||||||
|
string fullPath = GetFullPath(PathTools.Normalize(path));
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
IFile baseFile = BaseFs.OpenFile(fullPath, mode);
|
||||||
|
var file = new DirectorySaveDataFile(this, baseFile);
|
||||||
|
|
||||||
|
if (mode.HasFlag(OpenMode.Write))
|
||||||
|
{
|
||||||
|
OpenWritableFileCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RenameDirectory(string srcPath, string dstPath)
|
||||||
|
{
|
||||||
|
string fullSrcPath = GetFullPath(PathTools.Normalize(srcPath));
|
||||||
|
string fullDstPath = GetFullPath(PathTools.Normalize(dstPath));
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
BaseFs.RenameDirectory(fullSrcPath, fullDstPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RenameFile(string srcPath, string dstPath)
|
||||||
|
{
|
||||||
|
string fullSrcPath = GetFullPath(PathTools.Normalize(srcPath));
|
||||||
|
string fullDstPath = GetFullPath(PathTools.Normalize(dstPath));
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
BaseFs.RenameFile(fullSrcPath, fullDstPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DirectoryExists(string path)
|
||||||
|
{
|
||||||
|
string fullPath = GetFullPath(PathTools.Normalize(path));
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
return BaseFs.DirectoryExists(fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool FileExists(string path)
|
||||||
|
{
|
||||||
|
string fullPath = GetFullPath(PathTools.Normalize(path));
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
return BaseFs.FileExists(fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectoryEntryType GetEntryType(string path)
|
||||||
|
{
|
||||||
|
string fullPath = GetFullPath(PathTools.Normalize(path));
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
return BaseFs.GetEntryType(fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long GetFreeSpaceSize(string path)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long GetTotalSpaceSize(string path)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileTimeStampRaw GetFileTimeStampRaw(string path)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Commit()
|
||||||
|
{
|
||||||
|
if (OpenWritableFileCount > 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("All files must be closed before commiting save data.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SynchronizeDirectory(SyncDir, WorkingDir);
|
||||||
|
|
||||||
|
BaseFs.DeleteDirectoryRecursively(CommittedDir);
|
||||||
|
|
||||||
|
BaseFs.RenameDirectory(SyncDir, CommittedDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, string path, QueryId queryId)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetFullPath(string path)
|
||||||
|
{
|
||||||
|
return PathTools.Normalize(PathTools.Combine(WorkingDir, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SynchronizeDirectory(string dest, string src)
|
||||||
|
{
|
||||||
|
if (BaseFs.DirectoryExists(dest))
|
||||||
|
{
|
||||||
|
BaseFs.DeleteDirectoryRecursively(dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseFs.CreateDirectory(dest);
|
||||||
|
|
||||||
|
IDirectory sourceDir = BaseFs.OpenDirectory(src, OpenDirectoryMode.All);
|
||||||
|
IDirectory destDir = BaseFs.OpenDirectory(dest, OpenDirectoryMode.All);
|
||||||
|
|
||||||
|
sourceDir.CopyDirectory(destDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void NotifyCloseWritableFile()
|
||||||
|
{
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
OpenWritableFileCount--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ namespace LibHac.Fs
|
||||||
{
|
{
|
||||||
public abstract class FileBase : IFile
|
public abstract class FileBase : IFile
|
||||||
{
|
{
|
||||||
private bool _isDisposed;
|
protected bool IsDisposed { get; private set; }
|
||||||
internal List<IDisposable> ToDispose { get; } = new List<IDisposable>();
|
internal List<IDisposable> ToDispose { get; } = new List<IDisposable>();
|
||||||
|
|
||||||
public abstract int Read(Span<byte> destination, long offset);
|
public abstract int Read(Span<byte> destination, long offset);
|
||||||
|
@ -18,7 +18,7 @@ namespace LibHac.Fs
|
||||||
|
|
||||||
protected int ValidateReadParamsAndGetSize(ReadOnlySpan<byte> span, long offset)
|
protected int ValidateReadParamsAndGetSize(ReadOnlySpan<byte> span, long offset)
|
||||||
{
|
{
|
||||||
if (_isDisposed) throw new ObjectDisposedException(null);
|
if (IsDisposed) throw new ObjectDisposedException(null);
|
||||||
|
|
||||||
if ((Mode & OpenMode.Read) == 0) throw new NotSupportedException("File does not allow reading.");
|
if ((Mode & OpenMode.Read) == 0) throw new NotSupportedException("File does not allow reading.");
|
||||||
if (span == null) throw new ArgumentNullException(nameof(span));
|
if (span == null) throw new ArgumentNullException(nameof(span));
|
||||||
|
@ -34,7 +34,7 @@ namespace LibHac.Fs
|
||||||
|
|
||||||
protected void ValidateWriteParams(ReadOnlySpan<byte> span, long offset)
|
protected void ValidateWriteParams(ReadOnlySpan<byte> span, long offset)
|
||||||
{
|
{
|
||||||
if (_isDisposed) throw new ObjectDisposedException(null);
|
if (IsDisposed) throw new ObjectDisposedException(null);
|
||||||
|
|
||||||
if ((Mode & OpenMode.Write) == 0) throw new NotSupportedException("File does not allow writing.");
|
if ((Mode & OpenMode.Write) == 0) throw new NotSupportedException("File does not allow writing.");
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ namespace LibHac.Fs
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (_isDisposed) return;
|
if (IsDisposed) return;
|
||||||
|
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
|
@ -75,7 +75,7 @@ namespace LibHac.Fs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_isDisposed = true;
|
IsDisposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace LibHac.Fs
|
||||||
{
|
{
|
||||||
Path = path;
|
Path = path;
|
||||||
Mode = mode;
|
Mode = mode;
|
||||||
Stream = new FileStream(Path, FileMode.Open, GetFileAccess(mode));
|
Stream = new FileStream(Path, FileMode.Open, GetFileAccess(mode), GetFileShare(mode));
|
||||||
File = new StreamFile(Stream, mode);
|
File = new StreamFile(Stream, mode);
|
||||||
|
|
||||||
ToDispose.Add(File);
|
ToDispose.Add(File);
|
||||||
|
@ -56,5 +56,10 @@ namespace LibHac.Fs
|
||||||
// FileAccess and OpenMode have the same flags
|
// FileAccess and OpenMode have the same flags
|
||||||
return (FileAccess)(mode & OpenMode.ReadWrite);
|
return (FileAccess)(mode & OpenMode.ReadWrite);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static FileShare GetFileShare(OpenMode mode)
|
||||||
|
{
|
||||||
|
return mode.HasFlag(OpenMode.Write) ? FileShare.Read : FileShare.ReadWrite;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ namespace LibHac.Fs
|
||||||
{
|
{
|
||||||
path = PathTools.Normalize(path);
|
path = PathTools.Normalize(path);
|
||||||
string localPath = ResolveLocalPath(path);
|
string localPath = ResolveLocalPath(path);
|
||||||
string localDir = Path.GetDirectoryName(localPath);
|
string localDir = ResolveLocalPath(PathTools.GetParentDirectory(path));
|
||||||
|
|
||||||
if (localDir != null) Directory.CreateDirectory(localDir);
|
if (localDir != null) Directory.CreateDirectory(localDir);
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ namespace LibHac.Fs
|
||||||
string srcLocalPath = ResolveLocalPath(srcPath);
|
string srcLocalPath = ResolveLocalPath(srcPath);
|
||||||
string dstLocalPath = ResolveLocalPath(dstPath);
|
string dstLocalPath = ResolveLocalPath(dstPath);
|
||||||
|
|
||||||
string directoryName = Path.GetDirectoryName(dstLocalPath);
|
string directoryName = ResolveLocalPath(PathTools.GetParentDirectory(dstPath));
|
||||||
if (directoryName != null) Directory.CreateDirectory(directoryName);
|
if (directoryName != null) Directory.CreateDirectory(directoryName);
|
||||||
Directory.Move(srcLocalPath, dstLocalPath);
|
Directory.Move(srcLocalPath, dstLocalPath);
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,7 @@ namespace LibHac.Fs
|
||||||
|
|
||||||
string srcLocalPath = ResolveLocalPath(srcPath);
|
string srcLocalPath = ResolveLocalPath(srcPath);
|
||||||
string dstLocalPath = ResolveLocalPath(dstPath);
|
string dstLocalPath = ResolveLocalPath(dstPath);
|
||||||
string dstLocalDir = Path.GetDirectoryName(dstLocalPath);
|
string dstLocalDir = ResolveLocalPath(PathTools.GetParentDirectory(dstPath));
|
||||||
|
|
||||||
if (dstLocalDir != null) Directory.CreateDirectory(dstLocalDir);
|
if (dstLocalDir != null) Directory.CreateDirectory(dstLocalDir);
|
||||||
File.Move(srcLocalPath, dstLocalPath);
|
File.Move(srcLocalPath, dstLocalPath);
|
||||||
|
|
Loading…
Reference in a new issue