using PacketIO; namespace Archiver.Lib; public static class ArchiveEncoder { public static async ValueTask Encode(string inputPath, string outputPath) { if (!Directory.Exists(inputPath)) { throw new FileNotFoundException($"{inputPath} does not exist", inputPath); } using (FileStream fs = new FileStream(outputPath, FileMode.OpenOrCreate)) { byte[] buffer = CreateFileHeader(); await fs.WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false); FileAttributes attributes = File.GetAttributes(inputPath); if (attributes.HasFlag(FileAttributes.Directory)) { DirectoryInfo dirInfo = new DirectoryInfo(inputPath); PacketDirectoryModel model = await CreatePacketDirectory(dirInfo).ConfigureAwait(false); try { await TreePacketFile.WriteTreeFiles(fs, model.PacketDirectory).ConfigureAwait(false); } finally { DisposeFiles(model); } } else { FileInfo fileInfo = new FileInfo(inputPath); PacketFileModel model = await CreatePacketFile(fileInfo).ConfigureAwait(false); try { await TreePacketFile.WriteTreeFiles(fs, model.PacketFile).ConfigureAwait(false); } finally { DisposeFiles(model); } } } } public static async ValueTask Encode(IEnumerable inputFiles, string outputPath) { using (FileStream fs = new FileStream(outputPath, FileMode.OpenOrCreate)) { byte[] buffer = CreateFileHeader(); await fs.WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false); foreach (string inputPath in inputFiles) { FileAttributes attributes = File.GetAttributes(inputPath); if (attributes.HasFlag(FileAttributes.Directory)) { DirectoryInfo dirInfo = new DirectoryInfo(inputPath); PacketDirectoryModel model = await CreatePacketDirectory(dirInfo).ConfigureAwait(false); try { await TreePacketFile.WriteTreeFiles(fs, model.PacketDirectory).ConfigureAwait(false); } finally { DisposeFiles(model); } } else { FileInfo fileInfo = new FileInfo(inputPath); PacketFileModel model = await CreatePacketFile(fileInfo).ConfigureAwait(false); try { await TreePacketFile.WriteTreeFiles(fs, model.PacketFile).ConfigureAwait(false); } finally { DisposeFiles(model); } } } } } public static bool IsArchiveFile(string inputPath) { FileAttributes attributes = File.GetAttributes(inputPath); if (attributes.HasFlag(FileAttributes.Directory)) { return false; } using (FileStream fs = File.OpenRead(inputPath)) { byte[] buffer = new byte[CreateFileHeader().Length]; int c = fs.Read(buffer, 0, buffer.Length); if (c <= 0) { return false; } return buffer.SequenceEqual(CreateFileHeader()); } } internal static byte[] CreateFileHeader() { byte[] header = new byte[] { 0xCC, 0xDE, 0xFF, 0x00, }; return header; } private static void DisposeFiles(PacketDirectoryModel dirModel) { dirModel.PacketDirectory.Dispose(); foreach (PacketDirectoryModel subDirModel in dirModel.Directories) { DisposeFiles(subDirModel); } foreach (PacketFileModel fileModel in dirModel.Files) { DisposeFiles(fileModel); } } private static void DisposeFiles(PacketFileModel fileModel) { fileModel.PacketFile.Dispose(); fileModel.TempFile.Dispose(); } private static async ValueTask CreatePacketDirectory(DirectoryInfo dirInfo) { PacketDirectory packetDirectory = new PacketDirectory(dirInfo.Name); PacketDirectoryModel dirModel = new PacketDirectoryModel() { PacketDirectory = packetDirectory, Files = new List(), }; foreach (FileInfo fileInfo in dirInfo.EnumerateFiles()) { PacketFileModel model = await CreatePacketFile(fileInfo).ConfigureAwait(false); dirModel.Files.Add(model); packetDirectory.AddFile(model.PacketFile); } foreach (DirectoryInfo subInfo in dirInfo.EnumerateDirectories()) { PacketDirectoryModel subDirModel = await CreatePacketDirectory(subInfo).ConfigureAwait(false); dirModel.Directories.Add(subDirModel); packetDirectory.AddFile(subDirModel.PacketDirectory); } return dirModel; } private static async ValueTask CreatePacketFile(FileInfo fileInfo) { TempFile tempFile = new TempFile(); Stream outputStream = tempFile.Open(FileMode.Open); using (FileStream sourceStream = fileInfo.OpenRead()) { await sourceStream.CopyToAsync(outputStream).ConfigureAwait(false); } outputStream.Seek(0, SeekOrigin.Begin); PacketFile file = new PacketFile(fileInfo.Name, outputStream); PacketFileModel model = new PacketFileModel() { TempFile = tempFile, PacketFile = file, }; return model; } }