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
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue