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