private void BuildBSAs() { var bsas = ModList.Directives.OfType <CreateBSA>().ToList(); Info($"Building {bsas.Count} bsa files"); bsas.Do(bsa => { Status($"Building {bsa.To}"); var source_dir = Path.Combine(Outputfolder, Consts.BSACreationDir, bsa.TempID); var source_files = Directory.EnumerateFiles(source_dir, "*", SearchOption.AllDirectories) .Select(e => e.Substring(source_dir.Length + 1)) .ToList(); using (var a = new BSABuilder()) { //a.Create(Path.Combine(Outputfolder, bsa.To), (bsa_archive_type_t)bsa.Type, entries); a.HeaderType = (VersionType)bsa.Type; a.FileFlags = (FileFlags)bsa.FileFlags; a.ArchiveFlags = (ArchiveFlags)bsa.ArchiveFlags; source_files.PMap(f => { Status($"Adding {f} to BSA"); using (var fs = File.OpenRead(Path.Combine(source_dir, f))) a.AddFile(f, fs); }); Info($"Writing {bsa.To}"); a.Build(Path.Combine(Outputfolder, bsa.To)); } }); }
private static async Task <string[]> CookFiles(VFS vfs, AbsolutePath to, bool trial = false) { var files = vfs.AllAppliedFiles.Select(f => f.First()).OfType <ModFile>() .Where(f => Definitions.BatchForExtension.ContainsKey(f.Path.Extension)) .GroupIntoSizes(f => f.Size, Definitions.MaxBatchSize) .Select((itms, idx) => (itms, idx)) .ToArray(); List <string> newEsps = new(); Console.WriteLine($"Found {files.Length} archives to build"); foreach (var(group, idx) in files) { var name = $"_Cooked {idx:0000}.bsa"; var outFile = to.Combine("mods", "Cooked Files", name); if (!trial) { Console.WriteLine($"Building {name}"); var flags = group.Select(f => Definitions.BatchForExtension[f.Path.Extension]).Distinct() .Aggregate((uint)0, (a, b) => a | (uint)b.FileFlags); await using var bsa = await BSABuilder.Create(new BSAStateObject () { ArchiveFlags = ((int)(ArchiveFlags.HasFileNames | ArchiveFlags.HasFolderNames /* | ArchiveFlags.HasFileNameBlobs*/)) | 0x10 | 0x80, FileFlags = flags, Magic = Encoding.ASCII.GetString(new byte[] { 0x42, 0x53, 0x41, 0x00 }), Version = 0x67 }, Definitions.MaxBatchSize); bsa.HeaderType = VersionType.SSE; foreach (var(file, fidx) in group.Select((itm, fidx) => (itm, fidx))) { var state = new BSAFileStateObject { Path = (RelativePath)((string)file.Path).ToLowerInvariant().Replace('/', '\\'), Index = fidx, FlipCompression = false }; await bsa.AddFile(state, await file.AbsolutePath.OpenRead()); } Console.WriteLine($"Writing {name}"); await bsa.Build(outFile); Console.WriteLine($"Done building {name}"); } Console.WriteLine($"Writing Stub"); outFile = outFile.ReplaceExtension(new Extension(".esp")); newEsps.Add(outFile.FileName.ToString()); await "Stub.esp".RelativeTo(AbsolutePath.EntryPoint).CopyToAsync(outFile); } return(newEsps.ToArray()); }
static void Main(string[] args) { foreach (var bsa in Directory.EnumerateFiles(TestDir, "*.bsa", SearchOption.AllDirectories).Skip(0)) { Console.WriteLine($"From {bsa}"); Console.WriteLine("Cleaning Output Dir"); if (Directory.Exists(TempDir)) { Directory.Delete(TempDir, true); } Directory.CreateDirectory(TempDir); Console.WriteLine($"Reading {bsa}"); using (var a = new BSAReader(bsa)) { Parallel.ForEach(a.Files, file => { var abs_name = Path.Combine(TempDir, file.Path); if (!Directory.Exists(Path.GetDirectoryName(abs_name))) { Directory.CreateDirectory(Path.GetDirectoryName(abs_name)); } using (var fs = File.OpenWrite(abs_name)) file.CopyDataTo(fs); Equal((long)file.Size, new FileInfo(abs_name).Length); }); Console.WriteLine($"Building {bsa}"); using (var w = new BSABuilder()) { w.ArchiveFlags = a.ArchiveFlags; w.FileFlags = a.FileFlags; w.HeaderType = a.HeaderType; Parallel.ForEach(a.Files, file => { var abs_path = Path.Combine("c:\\tmp\\out", file.Path); using (var str = File.OpenRead(abs_path)) { var entry = w.AddFile(file.Path, str, file.FlipCompression); } }); w.Build("c:\\tmp\\tmp.bsa"); // Sanity Checks Equal(a.Files.Count(), w.Files.Count()); Equal(a.Files.Select(f => f.Path).ToHashSet(), w.Files.Select(f => f.Path).ToHashSet()); /*foreach (var pair in Enumerable.Zip(a.Files, w.Files, (ai, bi) => (ai, bi))) * { * Console.WriteLine($"{pair.ai.Path}, {pair.ai.Hash}, {pair.bi.Path}, {pair.bi.Hash}"); * }*/ foreach (var pair in Enumerable.Zip(a.Files, w.Files, (ai, bi) => (ai, bi))) { Equal(pair.ai.Path, pair.bi.Path); Equal(pair.ai.Hash, pair.bi.Hash); } } Console.WriteLine($"Verifying {bsa}"); using (var b = new BSAReader("c:\\tmp\\tmp.bsa")) { Console.WriteLine($"Performing A/B tests on {bsa}"); Equal((uint)a.ArchiveFlags, (uint)b.ArchiveFlags); Equal((uint)a.FileFlags, (uint)b.FileFlags); // Check same number of files Equal(a.Files.Count(), b.Files.Count()); int idx = 0; foreach (var pair in Enumerable.Zip(a.Files, b.Files, (ai, bi) => (ai, bi))) { idx++; //Console.WriteLine($" - {pair.ai.Path}"); Equal(pair.ai.Path, pair.bi.Path); Equal(pair.ai.Compressed, pair.bi.Compressed); Equal(pair.ai.Size, pair.bi.Size); Equal(pair.ai.GetData(), pair.bi.GetData()); } } } } }