public static long CalculateRomFsSize(IList <IArchiveFileInfo> files, UPath rootDirectory) { var rootNode = RomFsDirectoryNode.Parse(files, rootDirectory); long totalSize = BlockSize_; // Calculate meta size totalSize += GetHashTableEntryCount(rootNode.DirectoryCount) * 4; totalSize += GetHashTableEntryCount(files.Count) * 4; totalSize += rootNode.SizeInBytes; totalSize += files.Sum(x => MetaData.FileEntry.Size + ((Encoding.Unicode.GetByteCount(x.FilePath.GetName()) + 3) & ~3)); // Calculate file size totalSize = (totalSize + 0xF) & ~0xF; totalSize += files.Sum(x => (x.FileSize + 0xF) & ~0xF); totalSize = (totalSize + BlockSize_ - 1) & ~(BlockSize_ - 1); // Calculate hash level sizes var levelSize = totalSize - BlockSize_; for (var i = 0; i < 2; i++) { var currentLevelSize = levelSize / BlockSize_ * 0x20; currentLevelSize = (currentLevelSize + BlockSize_ - 1) & ~(BlockSize_ - 1); totalSize += currentLevelSize; levelSize = currentLevelSize; } return(totalSize); }
public static RomFsDirectoryNode Parse(IList <IArchiveFileInfo> files, UPath rootDirectory) { var root = new RomFsDirectoryNode { Name = "/", Directories = new List <RomFsDirectoryNode>(), Files = new List <IArchiveFileInfo>() }; var directories = files.Select(x => x.FilePath.GetSubDirectory(rootDirectory.ToAbsolute()).GetDirectory()).Distinct().OrderBy(x => x).ToArray(); foreach (var directory in directories) { var currentNode = root; foreach (var part in directory.Split()) { var newNode = currentNode.Directories.FirstOrDefault(x => x.Name == part); if (newNode != null) { currentNode = newNode; continue; } newNode = new RomFsDirectoryNode { _parent = currentNode, Name = part, Directories = new List <RomFsDirectoryNode>(), Files = new List <IArchiveFileInfo>() }; currentNode.Directories.Add(newNode); currentNode = newNode; } foreach (var file in files.Where(x => x.FilePath.GetDirectory().ToRelative() == rootDirectory / directory.ToRelative())) { currentNode.Files.Add(file); } } return(root); }
public static (long, long) Build(Stream input, IList <IArchiveFileInfo> files, UPath rootDirectory) { // Parse files into file tree var rootNode = RomFsDirectoryNode.Parse(files, rootDirectory); // Create MetaData Tree var metaData = new MetaData { DirMetaOffset = rootNode.Directories.Count <= 0 ? UnusedEntry_ : 0x18 }; metaData.Dirs.Add(new MetaData.DirEntry { MetaOffset = 0, ParentOffset = 0, NextSiblingOffset = UnusedEntry_, FirstChildOffset = rootNode.Directories.Count <= 0 ? UnusedEntry_ : 0x18, FirstFileOffset = 0, NextDirInSameBucket = UnusedEntry_, Hash = CalculatePathHash(0, Encoding.Unicode.GetBytes(string.Empty)), Name = string.Empty }); PopulateMetaData(metaData, rootNode, metaData.Dirs[0]); // Creating directory hash buckets metaData.DirHashTable = Enumerable.Repeat(0xFFFFFFFF, GetHashTableEntryCount(metaData.Dirs.Count)).ToArray(); PopulateDirHashTable(metaData.Dirs, metaData.DirHashTable); // Creating file hash buckets metaData.FileHashTable = Enumerable.Repeat(0xFFFFFFFF, GetHashTableEntryCount(metaData.Files.Count)).ToArray(); PopulateFileHashTable(metaData.Files, metaData.FileHashTable); // Write RomFs var romFsSizes = WriteRomFs(input, metaData); return(romFsSizes); }
/// <summary> /// Populates the meta data tree. /// </summary> /// <param name="metaData">The meta data to populate.</param> /// <param name="dir">The directory to populate the meta data with.</param> /// <param name="parentDir">The parent directory meta data.</param> private static void PopulateMetaData(MetaData metaData, RomFsDirectoryNode dir, MetaData.DirEntry parentDir) { // Adding files var files = dir.Files; for (var i = 0; i < files.Count; i++) { var newFileEntry = new MetaData.FileEntry { MetaOffset = metaData.FileMetaOffset, Hash = CalculatePathHash((uint)parentDir.MetaOffset, Encoding.Unicode.GetBytes(files[i].FilePath.GetName())), FileData = files[i].GetFileData().Result, ParentDirOffset = parentDir.MetaOffset, DataOffset = metaData.FileOffset, DataSize = files[i].FileSize, Name = files[i].FilePath.GetName() }; metaData.FileOffset = (metaData.FileOffset + files[i].FileSize + 0xF) & ~0xF; metaData.FileMetaOffset += MetaData.FileEntry.Size + files[i].FilePath.GetName().Length * 2; if (metaData.FileMetaOffset % 4 != 0) { metaData.FileMetaOffset += 2; } newFileEntry.NextSiblingOffset = i + 1 == files.Count ? UnusedEntry_ : metaData.FileMetaOffset; metaData.Files.Add(newFileEntry); } // Adding sub directories var dirs = dir.Directories; var metaDirIndices = new List <int>(); for (var i = 0; i < dirs.Count; i++) { var newDirEntry = new MetaData.DirEntry { //Parent = parentDir, MetaOffset = metaData.DirMetaOffset, Hash = CalculatePathHash((uint)parentDir.MetaOffset, Encoding.Unicode.GetBytes(dirs[i].Name)), ParentOffset = parentDir.MetaOffset, Name = dirs[i].Name }; metaData.DirMetaOffset += MetaData.DirEntry.Size + dirs[i].Name.Length * 2; if (metaData.DirMetaOffset % 4 != 0) { metaData.DirMetaOffset += 2; } newDirEntry.NextSiblingOffset = i + 1 < dirs.Count ? metaData.DirMetaOffset : UnusedEntry_; metaData.Dirs.Add(newDirEntry); metaDirIndices.Add(metaData.Dirs.Count - 1); } // Adding children of sub directories for (var i = 0; i < dirs.Count; i++) { metaData.Dirs[metaDirIndices[i]].FirstChildOffset = dirs[i].Directories.Count > 0 ? metaData.DirMetaOffset : UnusedEntry_; metaData.Dirs[metaDirIndices[i]].FirstFileOffset = dirs[i].Files.Count > 0 ? metaData.FileMetaOffset : UnusedEntry_; PopulateMetaData(metaData, dirs[i], metaData.Dirs[metaDirIndices[i]]); } }