Add ConcatenationFile resizing

This commit is contained in:
Alex Barney 2019-03-26 19:41:26 -05:00
parent 1ee3841125
commit f570c56122
3 changed files with 105 additions and 39 deletions

View file

@ -1,25 +1,30 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace LibHac.IO
{
public class ConcatenationFile : FileBase
{
private IFile[] Sources { get; }
private long SplitFileSize { get; }
private IFileSystem BaseFileSystem { get; }
private string FilePath { get; }
private List<IFile> Sources { get; }
private long SubFileSize { get; }
internal ConcatenationFile(IList<IFile> sources, long splitFileSize, OpenMode mode)
internal ConcatenationFile(IFileSystem baseFileSystem, string path, IEnumerable<IFile> sources, long subFileSize, OpenMode mode)
{
Sources = sources.ToArray();
SplitFileSize = splitFileSize;
BaseFileSystem = baseFileSystem;
FilePath = path;
Sources = sources.ToList();
SubFileSize = subFileSize;
Mode = mode;
for (int i = 0; i < Sources.Length - 1; i++)
for (int i = 0; i < Sources.Count - 1; i++)
{
if (Sources[i].GetSize() != SplitFileSize)
if (Sources[i].GetSize() != SubFileSize)
{
throw new ArgumentException($"Source file must have size {splitFileSize}");
throw new ArgumentException($"Source file must have size {subFileSize}");
}
}
@ -34,11 +39,11 @@ namespace LibHac.IO
while (remaining > 0)
{
int fileIndex = GetFileIndexFromOffset(offset);
int fileIndex = GetSubFileIndexFromOffset(offset);
IFile file = Sources[fileIndex];
long fileOffset = offset - fileIndex * SplitFileSize;
long fileOffset = offset - fileIndex * SubFileSize;
long fileEndOffset = Math.Min((fileIndex + 1) * SplitFileSize, GetSize());
long fileEndOffset = Math.Min((fileIndex + 1) * SubFileSize, GetSize());
int bytesToRead = (int)Math.Min(fileEndOffset - inPos, remaining);
int bytesRead = file.Read(destination.Slice(outPos, bytesToRead), fileOffset);
@ -56,19 +61,19 @@ namespace LibHac.IO
{
ValidateWriteParams(source, offset);
long inPos = offset;
int outPos = 0;
int inPos = 0;
long outPos = offset;
int remaining = source.Length;
while (remaining > 0)
{
int fileIndex = GetFileIndexFromOffset(offset);
int fileIndex = GetSubFileIndexFromOffset(outPos);
IFile file = Sources[fileIndex];
long fileOffset = offset - fileIndex * SplitFileSize;
long fileOffset = outPos - fileIndex * SubFileSize;
long fileEndOffset = Math.Min((fileIndex + 1) * SplitFileSize, GetSize());
int bytesToWrite = (int)Math.Min(fileEndOffset - inPos, remaining);
file.Write(source.Slice(outPos, bytesToWrite), fileOffset);
long fileEndOffset = Math.Min((fileIndex + 1) * SubFileSize, GetSize());
int bytesToWrite = (int)Math.Min(fileEndOffset - outPos, remaining);
file.Write(source.Slice(inPos, bytesToWrite), fileOffset);
outPos += bytesToWrite;
inPos += bytesToWrite;
@ -98,12 +103,73 @@ namespace LibHac.IO
public override void SetSize(long size)
{
throw new NotImplementedException();
long currentSize = GetSize();
if (currentSize == size) return;
int currentSubFileCount = QuerySubFileCount(currentSize, SubFileSize);
int newSubFileCount = QuerySubFileCount(size, SubFileSize);
if (size > currentSize)
{
IFile currentLastSubFile = Sources[currentSubFileCount - 1];
long newSubFileSize = QuerySubFileSize(currentSubFileCount - 1, size, SubFileSize);
currentLastSubFile.SetSize(newSubFileSize);
for (int i = currentSubFileCount; i < newSubFileCount; i++)
{
string newSubFilePath = ConcatenationFileSystem.GetSubFilePath(FilePath, i);
newSubFileSize = QuerySubFileSize(i, size, SubFileSize);
BaseFileSystem.CreateFile(newSubFilePath, newSubFileSize, CreateFileOptions.None);
Sources.Add(BaseFileSystem.OpenFile(newSubFilePath, Mode));
}
}
else
{
for (int i = currentSubFileCount - 1; i > newSubFileCount - 1; i--)
{
Sources[i].Dispose();
Sources.RemoveAt(i);
string subFilePath = ConcatenationFileSystem.GetSubFilePath(FilePath, i);
BaseFileSystem.DeleteFile(subFilePath);
}
long newLastFileSize = QuerySubFileSize(newSubFileCount - 1, size, SubFileSize);
Sources[newSubFileCount - 1].SetSize(newLastFileSize);
}
}
private int GetFileIndexFromOffset(long offset)
private int GetSubFileIndexFromOffset(long offset)
{
return (int)(offset / SplitFileSize);
return (int)(offset / SubFileSize);
}
private static int QuerySubFileCount(long size, long subFileSize)
{
Debug.Assert(size >= 0);
Debug.Assert(subFileSize > 0);
if (size == 0) return 1;
return (int)Util.DivideByRoundUp(size, subFileSize);
}
private static long QuerySubFileSize(int subFileIndex, long totalSize, long subFileSize)
{
int subFileCount = QuerySubFileCount(totalSize, subFileSize);
Debug.Assert(subFileIndex < subFileCount);
if (subFileIndex + 1 == subFileCount)
{
long remainder = totalSize % subFileSize;
return remainder == 0 ? subFileSize : remainder;
}
return subFileSize;
}
}
}

View file

@ -6,16 +6,16 @@ namespace LibHac.IO
{
public class ConcatenationFileSystem : IFileSystem
{
private const long DefaultSplitFileSize = 0xFFFF0000; // Hard-coded value used by FS
private const long DefaultSubFileSize = 0xFFFF0000; // Hard-coded value used by FS
private IAttributeFileSystem BaseFileSystem { get; }
private long SplitFileSize { get; }
private long SubFileSize { get; }
public ConcatenationFileSystem(IAttributeFileSystem baseFileSystem) : this(baseFileSystem, DefaultSplitFileSize) { }
public ConcatenationFileSystem(IAttributeFileSystem baseFileSystem) : this(baseFileSystem, DefaultSubFileSize) { }
public ConcatenationFileSystem(IAttributeFileSystem baseFileSystem, long splitFileSize)
public ConcatenationFileSystem(IAttributeFileSystem baseFileSystem, long subFileSize)
{
BaseFileSystem = baseFileSystem;
SplitFileSize = splitFileSize;
SubFileSize = subFileSize;
}
internal bool IsConcatenationFile(string path)
@ -64,8 +64,8 @@ namespace LibHac.IO
for (int i = 0; remaining > 0; i++)
{
long fileSize = Math.Min(SplitFileSize, remaining);
string fileName = GetSplitFilePath(path, i);
long fileSize = Math.Min(SubFileSize, remaining);
string fileName = GetSubFilePath(path, i);
BaseFileSystem.CreateFile(fileName, fileSize, CreateFileOptions.None);
@ -94,11 +94,11 @@ namespace LibHac.IO
BaseFileSystem.DeleteFile(path);
}
int count = GetSplitFileCount(path);
int count = GetSubFileCount(path);
for (int i = 0; i < count; i++)
{
BaseFileSystem.DeleteFile(GetSplitFilePath(path, i));
BaseFileSystem.DeleteFile(GetSubFilePath(path, i));
}
BaseFileSystem.DeleteDirectory(path);
@ -127,18 +127,18 @@ namespace LibHac.IO
return BaseFileSystem.OpenFile(path, mode);
}
int fileCount = GetSplitFileCount(path);
int fileCount = GetSubFileCount(path);
var files = new List<IFile>();
for (int i = 0; i < fileCount; i++)
{
string filePath = GetSplitFilePath(path, i);
string filePath = GetSubFilePath(path, i);
IFile file = BaseFileSystem.OpenFile(filePath, mode);
files.Add(file);
}
return new ConcatenationFile(files, SplitFileSize, mode);
return new ConcatenationFile(BaseFileSystem, path, files, SubFileSize, mode);
}
public void RenameDirectory(string srcPath, string dstPath)
@ -197,11 +197,11 @@ namespace LibHac.IO
BaseFileSystem.Commit();
}
private int GetSplitFileCount(string dirPath)
private int GetSubFileCount(string dirPath)
{
int count = 0;
while (BaseFileSystem.FileExists(GetSplitFilePath(dirPath, count)))
while (BaseFileSystem.FileExists(GetSubFilePath(dirPath, count)))
{
count++;
}
@ -209,19 +209,19 @@ namespace LibHac.IO
return count;
}
private static string GetSplitFilePath(string dirPath, int index)
internal static string GetSubFilePath(string dirPath, int index)
{
return $"{dirPath}/{index:D2}";
}
internal long GetConcatenationFileSize(string path)
{
int fileCount = GetSplitFileCount(path);
int fileCount = GetSubFileCount(path);
long size = 0;
for (int i = 0; i < fileCount; i++)
{
size += BaseFileSystem.GetFileSize(GetSplitFilePath(path, i));
size += BaseFileSystem.GetFileSize(GetSubFilePath(path, i));
}
return size;

View file

@ -174,7 +174,7 @@ namespace LibHac.IO
public static FileAttributes ApplyNxAttributes(this FileAttributes attributes, NxFileAttributes nxAttributes)
{
var nxAttributeBits = (FileAttributes)(((int)attributes & 3) << 4);
var nxAttributeBits = (FileAttributes)(((int)nxAttributes & 3) << 4);
return attributes | nxAttributeBits;
}
}