From f570c56122fc6eaebb73398e8a11ad7ac1a3a86b Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Tue, 26 Mar 2019 19:41:26 -0500 Subject: [PATCH] Add ConcatenationFile resizing --- src/LibHac/IO/ConcatenationFile.cs | 108 ++++++++++++++++++----- src/LibHac/IO/ConcatenationFileSystem.cs | 34 +++---- src/LibHac/IO/FileSystemExtensions.cs | 2 +- 3 files changed, 105 insertions(+), 39 deletions(-) diff --git a/src/LibHac/IO/ConcatenationFile.cs b/src/LibHac/IO/ConcatenationFile.cs index f6fb44c9..2c4ef3fc 100644 --- a/src/LibHac/IO/ConcatenationFile.cs +++ b/src/LibHac/IO/ConcatenationFile.cs @@ -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 Sources { get; } + private long SubFileSize { get; } - internal ConcatenationFile(IList sources, long splitFileSize, OpenMode mode) + internal ConcatenationFile(IFileSystem baseFileSystem, string path, IEnumerable 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; } } } diff --git a/src/LibHac/IO/ConcatenationFileSystem.cs b/src/LibHac/IO/ConcatenationFileSystem.cs index e85a3edc..b73ad533 100644 --- a/src/LibHac/IO/ConcatenationFileSystem.cs +++ b/src/LibHac/IO/ConcatenationFileSystem.cs @@ -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(); 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; diff --git a/src/LibHac/IO/FileSystemExtensions.cs b/src/LibHac/IO/FileSystemExtensions.cs index a81614b1..2f0fa2f6 100644 --- a/src/LibHac/IO/FileSystemExtensions.cs +++ b/src/LibHac/IO/FileSystemExtensions.cs @@ -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; } }