mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add ConcatenationFileSystem
This commit is contained in:
parent
404e05da53
commit
cf48b5b21e
3 changed files with 308 additions and 0 deletions
62
src/LibHac/IO/ConcatenationDirectory.cs
Normal file
62
src/LibHac/IO/ConcatenationDirectory.cs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LibHac.IO
|
||||||
|
{
|
||||||
|
public class ConcatenationDirectory : IDirectory
|
||||||
|
{
|
||||||
|
IFileSystem IDirectory.ParentFileSystem => ParentFileSystem;
|
||||||
|
public string FullPath { get; }
|
||||||
|
public OpenDirectoryMode Mode { get; }
|
||||||
|
|
||||||
|
private ConcatenationFileSystem ParentFileSystem { get; }
|
||||||
|
private IDirectory ParentDirectory { get; }
|
||||||
|
|
||||||
|
public ConcatenationDirectory(ConcatenationFileSystem fs, IDirectory parentDirectory, OpenDirectoryMode mode)
|
||||||
|
{
|
||||||
|
ParentFileSystem = fs;
|
||||||
|
ParentDirectory = parentDirectory;
|
||||||
|
Mode = mode;
|
||||||
|
FullPath = parentDirectory.FullPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<DirectoryEntry> Read()
|
||||||
|
{
|
||||||
|
foreach (DirectoryEntry entry in ParentDirectory.Read())
|
||||||
|
{
|
||||||
|
bool isSplit = ParentFileSystem.IsSplitFile(entry.FullPath);
|
||||||
|
|
||||||
|
if (!CanReturnEntry(entry, isSplit)) continue;
|
||||||
|
|
||||||
|
if (!isSplit)
|
||||||
|
{
|
||||||
|
yield return entry;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
long size = ParentFileSystem.GetSplitFileSize(entry.FullPath);
|
||||||
|
yield return new DirectoryEntry(entry.Name, entry.FullPath, DirectoryEntryType.File, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetEntryCount()
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
foreach (DirectoryEntry entry in ParentDirectory.Read())
|
||||||
|
{
|
||||||
|
bool isSplit = ParentFileSystem.IsSplitFile(entry.FullPath);
|
||||||
|
|
||||||
|
if (CanReturnEntry(entry, isSplit)) count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CanReturnEntry(DirectoryEntry entry, bool isSplit)
|
||||||
|
{
|
||||||
|
return Mode.HasFlag(OpenDirectoryMode.Files) && (entry.Type == DirectoryEntryType.File || isSplit) ||
|
||||||
|
Mode.HasFlag(OpenDirectoryMode.Directories) && entry.Type == DirectoryEntryType.Directory && !isSplit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
88
src/LibHac/IO/ConcatenationFile.cs
Normal file
88
src/LibHac/IO/ConcatenationFile.cs
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace LibHac.IO
|
||||||
|
{
|
||||||
|
public class ConcatenationFile : FileBase
|
||||||
|
{
|
||||||
|
private IFile[] Sources { get; }
|
||||||
|
private long SplitFileSize { get; }
|
||||||
|
|
||||||
|
public ConcatenationFile(IList<IFile> sources, long splitFileSize, OpenMode mode)
|
||||||
|
{
|
||||||
|
Sources = sources.ToArray();
|
||||||
|
SplitFileSize = splitFileSize;
|
||||||
|
Mode = mode;
|
||||||
|
|
||||||
|
for (int i = 0; i < Sources.Length - 1; i++)
|
||||||
|
{
|
||||||
|
if (Sources[i].GetSize() != SplitFileSize)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Source file must have size {splitFileSize}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int Read(Span<byte> destination, long offset)
|
||||||
|
{
|
||||||
|
long inPos = offset;
|
||||||
|
int outPos = 0;
|
||||||
|
int remaining = ValidateReadParamsAndGetSize(destination, offset);
|
||||||
|
|
||||||
|
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 bytesToRead = (int)Math.Min(fileEndOffset - inPos, remaining);
|
||||||
|
int bytesRead = file.Read(destination.Slice(outPos, bytesToRead), fileOffset);
|
||||||
|
|
||||||
|
outPos += bytesRead;
|
||||||
|
inPos += bytesRead;
|
||||||
|
remaining -= bytesRead;
|
||||||
|
|
||||||
|
if (bytesRead < bytesToRead) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return outPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(ReadOnlySpan<byte> source, long offset)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Flush()
|
||||||
|
{
|
||||||
|
foreach (IFile file in Sources)
|
||||||
|
{
|
||||||
|
file.Flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override long GetSize()
|
||||||
|
{
|
||||||
|
long size = 0;
|
||||||
|
|
||||||
|
foreach (IFile file in Sources)
|
||||||
|
{
|
||||||
|
size += file.GetSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetSize(long size)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetFileIndexFromOffset(long offset)
|
||||||
|
{
|
||||||
|
return (int)(offset / SplitFileSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
158
src/LibHac/IO/ConcatenationFileSystem.cs
Normal file
158
src/LibHac/IO/ConcatenationFileSystem.cs
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace LibHac.IO
|
||||||
|
{
|
||||||
|
public class ConcatenationFileSystem : IFileSystem
|
||||||
|
{
|
||||||
|
private IAttributeFileSystem BaseFileSystem { get; }
|
||||||
|
private long SplitFileSize { get; } = 0xFFFF0000; // Hard-coded value used by FS
|
||||||
|
|
||||||
|
public ConcatenationFileSystem(IAttributeFileSystem baseFileSystem)
|
||||||
|
{
|
||||||
|
BaseFileSystem = baseFileSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool IsSplitFile(string path)
|
||||||
|
{
|
||||||
|
FileAttributes attributes = BaseFileSystem.GetFileAttributes(path);
|
||||||
|
|
||||||
|
return (attributes & FileAttributes.Directory) != 0 && (attributes & FileAttributes.Archive) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateDirectory(string path)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateFile(string path, long size)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteDirectory(string path)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteFile(string path)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDirectory OpenDirectory(string path, OpenDirectoryMode mode)
|
||||||
|
{
|
||||||
|
path = PathTools.Normalize(path);
|
||||||
|
|
||||||
|
if (IsSplitFile(path))
|
||||||
|
{
|
||||||
|
throw new DirectoryNotFoundException(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
IDirectory parentDir = BaseFileSystem.OpenDirectory(path, OpenDirectoryMode.All);
|
||||||
|
var dir = new ConcatenationDirectory(this, parentDir, mode);
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IFile OpenFile(string path, OpenMode mode)
|
||||||
|
{
|
||||||
|
path = PathTools.Normalize(path);
|
||||||
|
|
||||||
|
if (!IsSplitFile(path))
|
||||||
|
{
|
||||||
|
return BaseFileSystem.OpenFile(path, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fileCount = GetSplitFileCount(path);
|
||||||
|
|
||||||
|
var files = new List<IFile>();
|
||||||
|
|
||||||
|
for (int i = 0; i < fileCount; i++)
|
||||||
|
{
|
||||||
|
string filePath = GetSplitFilePath(path, i);
|
||||||
|
IFile file = BaseFileSystem.OpenFile(filePath, mode);
|
||||||
|
files.Add(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ConcatenationFile(files, SplitFileSize, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RenameDirectory(string srcPath, string dstPath)
|
||||||
|
{
|
||||||
|
srcPath = PathTools.Normalize(srcPath);
|
||||||
|
dstPath = PathTools.Normalize(dstPath);
|
||||||
|
|
||||||
|
if (IsSplitFile(srcPath))
|
||||||
|
{
|
||||||
|
throw new DirectoryNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseFileSystem.RenameDirectory(srcPath, dstPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RenameFile(string srcPath, string dstPath)
|
||||||
|
{
|
||||||
|
srcPath = PathTools.Normalize(srcPath);
|
||||||
|
dstPath = PathTools.Normalize(dstPath);
|
||||||
|
|
||||||
|
if (IsSplitFile(srcPath))
|
||||||
|
{
|
||||||
|
BaseFileSystem.RenameDirectory(srcPath, dstPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BaseFileSystem.RenameFile(srcPath, dstPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DirectoryExists(string path)
|
||||||
|
{
|
||||||
|
path = PathTools.Normalize(path);
|
||||||
|
|
||||||
|
return BaseFileSystem.DirectoryExists(path) && !IsSplitFile(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool FileExists(string path)
|
||||||
|
{
|
||||||
|
path = PathTools.Normalize(path);
|
||||||
|
|
||||||
|
return BaseFileSystem.FileExists(path) || BaseFileSystem.DirectoryExists(path) && IsSplitFile(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Commit()
|
||||||
|
{
|
||||||
|
BaseFileSystem.Commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetSplitFileCount(string dirPath)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
while (BaseFileSystem.FileExists(GetSplitFilePath(dirPath, count)))
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetSplitFilePath(string dirPath, int index)
|
||||||
|
{
|
||||||
|
return $"{dirPath}/{index:D2}";
|
||||||
|
}
|
||||||
|
|
||||||
|
internal long GetSplitFileSize(string path)
|
||||||
|
{
|
||||||
|
int fileCount = GetSplitFileCount(path);
|
||||||
|
long size = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < fileCount; i++)
|
||||||
|
{
|
||||||
|
size += BaseFileSystem.GetFileSize(GetSplitFilePath(path, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue