Add DirectorySaveDataFileSystem

This commit is contained in:
Alex Barney 2019-05-20 17:47:52 -05:00
parent 5b764e487e
commit 0fa9fccc5f
5 changed files with 313 additions and 9 deletions

View 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();
}
}
}
}
}

View 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--;
}
}
}
}

View file

@ -5,7 +5,7 @@ namespace LibHac.Fs
{
public abstract class FileBase : IFile
{
private bool _isDisposed;
protected bool IsDisposed { get; private set; }
internal List<IDisposable> ToDispose { get; } = new List<IDisposable>();
public abstract int Read(Span<byte> destination, long offset);
@ -18,7 +18,7 @@ namespace LibHac.Fs
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 (span == null) throw new ArgumentNullException(nameof(span));
@ -34,7 +34,7 @@ namespace LibHac.Fs
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.");
@ -63,7 +63,7 @@ namespace LibHac.Fs
protected virtual void Dispose(bool disposing)
{
if (_isDisposed) return;
if (IsDisposed) return;
if (disposing)
{
@ -75,7 +75,7 @@ namespace LibHac.Fs
}
}
_isDisposed = true;
IsDisposed = true;
}
}

View file

@ -13,7 +13,7 @@ namespace LibHac.Fs
{
Path = path;
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);
ToDispose.Add(File);
@ -56,5 +56,10 @@ namespace LibHac.Fs
// FileAccess and OpenMode have the same flags
return (FileAccess)(mode & OpenMode.ReadWrite);
}
private static FileShare GetFileShare(OpenMode mode)
{
return mode.HasFlag(OpenMode.Write) ? FileShare.Read : FileShare.ReadWrite;
}
}
}

View file

@ -61,7 +61,7 @@ namespace LibHac.Fs
{
path = PathTools.Normalize(path);
string localPath = ResolveLocalPath(path);
string localDir = Path.GetDirectoryName(localPath);
string localDir = ResolveLocalPath(PathTools.GetParentDirectory(path));
if (localDir != null) Directory.CreateDirectory(localDir);
@ -132,7 +132,7 @@ namespace LibHac.Fs
string srcLocalPath = ResolveLocalPath(srcPath);
string dstLocalPath = ResolveLocalPath(dstPath);
string directoryName = Path.GetDirectoryName(dstLocalPath);
string directoryName = ResolveLocalPath(PathTools.GetParentDirectory(dstPath));
if (directoryName != null) Directory.CreateDirectory(directoryName);
Directory.Move(srcLocalPath, dstLocalPath);
}
@ -144,7 +144,7 @@ namespace LibHac.Fs
string srcLocalPath = ResolveLocalPath(srcPath);
string dstLocalPath = ResolveLocalPath(dstPath);
string dstLocalDir = Path.GetDirectoryName(dstLocalPath);
string dstLocalDir = ResolveLocalPath(PathTools.GetParentDirectory(dstPath));
if (dstLocalDir != null) Directory.CreateDirectory(dstLocalDir);
File.Move(srcLocalPath, dstLocalPath);