mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add ConcatenationFileSystem writing
This commit is contained in:
parent
a33f829b55
commit
c3026f04b6
16 changed files with 142 additions and 27 deletions
|
@ -75,6 +75,13 @@ namespace LibHac.Nand
|
|||
return Fs.GetAttributes(path);
|
||||
}
|
||||
|
||||
public void SetFileAttributes(string path, FileAttributes attributes)
|
||||
{
|
||||
path = ToDiscUtilsPath(PathTools.Normalize(path));
|
||||
|
||||
Fs.SetAttributes(path, attributes);
|
||||
}
|
||||
|
||||
public long GetFileSize(string path)
|
||||
{
|
||||
path = ToDiscUtilsPath(PathTools.Normalize(path));
|
||||
|
@ -83,9 +90,9 @@ namespace LibHac.Nand
|
|||
}
|
||||
|
||||
public void Commit() { }
|
||||
|
||||
|
||||
public void CreateDirectory(string path) => throw new NotSupportedException();
|
||||
public void CreateFile(string path, long size) => throw new NotSupportedException();
|
||||
public void CreateFile(string path, long size, CreateFileOptions options) => throw new NotSupportedException();
|
||||
public void RenameDirectory(string srcPath, string dstPath) => throw new NotSupportedException();
|
||||
public void RenameFile(string srcPath, string dstPath) => throw new NotSupportedException();
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace LibHac.IO
|
|||
BaseFileSystem.CreateDirectory(path);
|
||||
}
|
||||
|
||||
public void CreateFile(string path, long size)
|
||||
public void CreateFile(string path, long size, CreateFileOptions options)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace LibHac.IO
|
|||
}
|
||||
else
|
||||
{
|
||||
long size = ParentFileSystem.GetSplitFileSize(entry.FullPath);
|
||||
long size = ParentFileSystem.GetConcatenationFileSize(entry.FullPath);
|
||||
yield return new DirectoryEntry(entry.Name, entry.FullPath, DirectoryEntryType.File, size);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,7 +54,26 @@ namespace LibHac.IO
|
|||
|
||||
public override void Write(ReadOnlySpan<byte> source, long offset)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
ValidateWriteParams(source, offset);
|
||||
|
||||
long inPos = offset;
|
||||
int outPos = 0;
|
||||
int remaining = source.Length;
|
||||
|
||||
while (remaining > 0)
|
||||
{
|
||||
int fileIndex = GetFileIndexFromOffset(offset);
|
||||
IFile file = Sources[fileIndex];
|
||||
long fileOffset = offset - fileIndex * SplitFileSize;
|
||||
|
||||
long fileEndOffset = Math.Min((fileIndex + 1) * SplitFileSize, GetSize());
|
||||
int bytesToWrite = (int)Math.Min(fileEndOffset - inPos, remaining);
|
||||
file.Write(source.Slice(outPos, bytesToWrite), fileOffset);
|
||||
|
||||
outPos += bytesToWrite;
|
||||
inPos += bytesToWrite;
|
||||
remaining -= bytesToWrite;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
|
|
|
@ -6,12 +6,16 @@ namespace LibHac.IO
|
|||
{
|
||||
public class ConcatenationFileSystem : IFileSystem
|
||||
{
|
||||
private const long DefaultSplitFileSize = 0xFFFF0000; // Hard-coded value used by FS
|
||||
private IAttributeFileSystem BaseFileSystem { get; }
|
||||
private long SplitFileSize { get; } = 0xFFFF0000; // Hard-coded value used by FS
|
||||
private long SplitFileSize { get; }
|
||||
|
||||
public ConcatenationFileSystem(IAttributeFileSystem baseFileSystem)
|
||||
public ConcatenationFileSystem(IAttributeFileSystem baseFileSystem) : this(baseFileSystem, DefaultSplitFileSize) { }
|
||||
|
||||
public ConcatenationFileSystem(IAttributeFileSystem baseFileSystem, long splitFileSize)
|
||||
{
|
||||
BaseFileSystem = baseFileSystem;
|
||||
SplitFileSize = splitFileSize;
|
||||
}
|
||||
|
||||
internal bool IsSplitFile(string path)
|
||||
|
@ -23,22 +27,78 @@ namespace LibHac.IO
|
|||
|
||||
public void CreateDirectory(string path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
if (FileExists(path))
|
||||
{
|
||||
throw new IOException("Cannot create directory because a file with this name already exists.");
|
||||
}
|
||||
|
||||
BaseFileSystem.CreateDirectory(path);
|
||||
}
|
||||
|
||||
public void CreateFile(string path, long size)
|
||||
public void CreateFile(string path, long size, CreateFileOptions options)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
CreateFileOptions newOptions = options & ~CreateFileOptions.CreateConcatenationFile;
|
||||
|
||||
if (!options.HasFlag(CreateFileOptions.CreateConcatenationFile))
|
||||
{
|
||||
BaseFileSystem.CreateFile(path, size, newOptions);
|
||||
return;
|
||||
}
|
||||
|
||||
// A concatenation file directory can't contain normal files
|
||||
string parentDir = PathTools.GetParentDirectory(path);
|
||||
if (IsSplitFile(parentDir)) throw new IOException("Cannot create files inside of a concatenation file");
|
||||
|
||||
BaseFileSystem.CreateDirectory(path);
|
||||
FileAttributes attributes = BaseFileSystem.GetFileAttributes(path) | FileAttributes.Archive;
|
||||
BaseFileSystem.SetFileAttributes(path, attributes);
|
||||
|
||||
long remaining = size;
|
||||
|
||||
for (int i = 0; remaining > 0; i++)
|
||||
{
|
||||
long fileSize = Math.Min(SplitFileSize, remaining);
|
||||
string fileName = GetSplitFilePath(path, i);
|
||||
|
||||
BaseFileSystem.CreateFile(fileName, fileSize, CreateFileOptions.None);
|
||||
|
||||
remaining -= fileSize;
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteDirectory(string path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
if (IsSplitFile(path))
|
||||
{
|
||||
throw new DirectoryNotFoundException(path);
|
||||
}
|
||||
|
||||
BaseFileSystem.DeleteDirectory(path);
|
||||
}
|
||||
|
||||
public void DeleteFile(string path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
if (!IsSplitFile(path))
|
||||
{
|
||||
BaseFileSystem.DeleteFile(path);
|
||||
}
|
||||
|
||||
int count = GetSplitFileCount(path);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
BaseFileSystem.DeleteFile(GetSplitFilePath(path, i));
|
||||
}
|
||||
|
||||
BaseFileSystem.DeleteDirectory(path);
|
||||
}
|
||||
|
||||
public IDirectory OpenDirectory(string path, OpenDirectoryMode mode)
|
||||
|
@ -151,7 +211,7 @@ namespace LibHac.IO
|
|||
return $"{dirPath}/{index:D2}";
|
||||
}
|
||||
|
||||
internal long GetSplitFileSize(string path)
|
||||
internal long GetConcatenationFileSize(string path)
|
||||
{
|
||||
int fileCount = GetSplitFileCount(path);
|
||||
long size = 0;
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace LibHac.IO
|
|||
{
|
||||
public static class FileSystemExtensions
|
||||
{
|
||||
public static void CopyDirectory(this IDirectory source, IDirectory dest, IProgressReport logger = null)
|
||||
public static void CopyDirectory(this IDirectory source, IDirectory dest, IProgressReport logger = null, CreateFileOptions options = CreateFileOptions.None)
|
||||
{
|
||||
IFileSystem sourceFs = source.ParentFileSystem;
|
||||
IFileSystem destFs = dest.ParentFileSystem;
|
||||
|
@ -27,12 +27,12 @@ namespace LibHac.IO
|
|||
IDirectory subSrcDir = sourceFs.OpenDirectory(subSrcPath, OpenDirectoryMode.All);
|
||||
IDirectory subDstDir = destFs.OpenDirectory(subDstPath, OpenDirectoryMode.All);
|
||||
|
||||
subSrcDir.CopyDirectory(subDstDir, logger);
|
||||
subSrcDir.CopyDirectory(subDstDir, logger, options);
|
||||
}
|
||||
|
||||
if (entry.Type == DirectoryEntryType.File)
|
||||
{
|
||||
destFs.CreateFile(subDstPath, entry.Size);
|
||||
destFs.CreateFile(subDstPath, entry.Size, options);
|
||||
|
||||
using (IFile srcFile = sourceFs.OpenFile(subSrcPath, OpenMode.Read))
|
||||
using (IFile dstFile = destFs.OpenFile(subDstPath, OpenMode.Write))
|
||||
|
@ -44,12 +44,12 @@ namespace LibHac.IO
|
|||
}
|
||||
}
|
||||
|
||||
public static void CopyFileSystem(this IFileSystem source, IFileSystem dest, IProgressReport logger = null)
|
||||
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);
|
||||
|
||||
sourceRoot.CopyDirectory(destRoot, logger);
|
||||
sourceRoot.CopyDirectory(destRoot, logger, options);
|
||||
}
|
||||
|
||||
public static void Extract(this IFileSystem source, string destinationPath, IProgressReport logger = null)
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace LibHac.IO
|
|||
public interface IAttributeFileSystem : IFileSystem
|
||||
{
|
||||
FileAttributes GetFileAttributes(string path);
|
||||
void SetFileAttributes(string path, FileAttributes attributes);
|
||||
long GetFileSize(string path);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace LibHac.IO
|
|||
public interface IFileSystem
|
||||
{
|
||||
void CreateDirectory(string path);
|
||||
void CreateFile(string path, long size);
|
||||
void CreateFile(string path, long size, CreateFileOptions options);
|
||||
void DeleteDirectory(string path);
|
||||
void DeleteFile(string path);
|
||||
IDirectory OpenDirectory(string path, OpenDirectoryMode mode);
|
||||
|
@ -25,4 +25,11 @@ namespace LibHac.IO
|
|||
Files = 2,
|
||||
All = Directories | Files
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum CreateFileOptions
|
||||
{
|
||||
None = 0,
|
||||
CreateConcatenationFile = 1 << 0
|
||||
}
|
||||
}
|
|
@ -100,7 +100,7 @@ namespace LibHac.IO
|
|||
public void Commit() { }
|
||||
|
||||
public void CreateDirectory(string path) => throw new NotSupportedException();
|
||||
public void CreateFile(string path, long size) => throw new NotSupportedException();
|
||||
public void CreateFile(string path, long size, CreateFileOptions options) => throw new NotSupportedException();
|
||||
public void DeleteDirectory(string path) => throw new NotSupportedException();
|
||||
public void DeleteFile(string path) => throw new NotSupportedException();
|
||||
public void RenameDirectory(string srcPath, string dstPath) => throw new NotSupportedException();
|
||||
|
|
|
@ -23,6 +23,12 @@ namespace LibHac.IO
|
|||
return File.GetAttributes(ResolveLocalPath(path));
|
||||
}
|
||||
|
||||
public void SetFileAttributes(string path, FileAttributes attributes)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
File.SetAttributes(ResolveLocalPath(path), attributes);
|
||||
}
|
||||
|
||||
public long GetFileSize(string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
@ -36,7 +42,7 @@ namespace LibHac.IO
|
|||
Directory.CreateDirectory(ResolveLocalPath(path));
|
||||
}
|
||||
|
||||
public void CreateFile(string path, long size)
|
||||
public void CreateFile(string path, long size, CreateFileOptions options)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
string localPath = ResolveLocalPath(path);
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace LibHac.IO
|
|||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void CreateFile(string path, long size)
|
||||
public void CreateFile(string path, long size, CreateFileOptions options)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
|
|
@ -76,6 +76,21 @@ namespace LibHac.IO
|
|||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string GetParentDirectory(string path)
|
||||
{
|
||||
if (path.Length == 0) return "/";
|
||||
|
||||
int i = path.Length - 1;
|
||||
|
||||
// A trailing separator should be ignored
|
||||
if (path[i] == '/') i--;
|
||||
|
||||
while (i >= 0 && path[i] != '/') i--;
|
||||
|
||||
if (i < 1) return "/";
|
||||
return path.Substring(0, i);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static bool IsDirectorySeparator(char c)
|
||||
{
|
||||
|
|
|
@ -148,7 +148,7 @@ namespace LibHac.IO
|
|||
}
|
||||
|
||||
public void CreateDirectory(string path) => throw new NotSupportedException();
|
||||
public void CreateFile(string path, long size) => throw new NotSupportedException();
|
||||
public void CreateFile(string path, long size, CreateFileOptions options) => throw new NotSupportedException();
|
||||
public void DeleteDirectory(string path) => throw new NotSupportedException();
|
||||
public void DeleteFile(string path) => throw new NotSupportedException();
|
||||
public void RenameDirectory(string srcPath, string dstPath) => throw new NotSupportedException();
|
||||
|
|
|
@ -122,7 +122,7 @@ namespace LibHac.IO.Save
|
|||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public void CreateFile(string path, long size)
|
||||
public void CreateFile(string path, long size, CreateFileOptions options)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ namespace LibHac.IO.Save
|
|||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public void CreateFile(string path, long size)
|
||||
public void CreateFile(string path, long size, CreateFileOptions options)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
|
|
@ -24,11 +24,11 @@
|
|||
ParentFileSystem.CreateDirectory(ResolveFullPath(path));
|
||||
}
|
||||
|
||||
public void CreateFile(string path, long size)
|
||||
public void CreateFile(string path, long size, CreateFileOptions options)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
ParentFileSystem.CreateFile(ResolveFullPath(path), size);
|
||||
ParentFileSystem.CreateFile(ResolveFullPath(path), size, options);
|
||||
}
|
||||
|
||||
public void DeleteDirectory(string path)
|
||||
|
|
Loading…
Reference in a new issue