using System.Text; namespace PacketIO; public class TreePacketFile { public static async ValueTask WriteTreeFiles(Stream stream, params IPacketFile[] files) { foreach (IPacketFile file in files) { await WriteTreeFile(stream, file).ConfigureAwait(false); } } public static async ValueTask WriteTreeFile(Stream stream, IPacketFile file) { byte fileType = (byte) file.GetFileType(); string fileName = file.GetFileName(); long fileSize = file.GetFileSize(); byte[] fileNameBuffer = Encoding.UTF8.GetBytes(fileName); byte[] buffer = new byte[sizeof(byte) + sizeof(int) + sizeof(long) + fileNameBuffer.Length]; buffer[0] = fileType; Array.Copy(BitConverter.GetBytes(fileNameBuffer.Length), 0, buffer, 1, sizeof(int)); Array.Copy(BitConverter.GetBytes(fileSize), 0, buffer, 5, sizeof(long)); Array.Copy(fileNameBuffer, 0, buffer, 13, fileNameBuffer.Length); await stream.WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false); switch (file.GetFileType()) { case PacketFileType.Directory: { long fileCount = file.EnumerableFiles().LongCount(); buffer = BitConverter.GetBytes(fileCount); await stream.WriteAsync(buffer, 0, sizeof(long)); await WriteTreeFiles(stream, file.EnumerableFiles().ToArray()).ConfigureAwait(false); break; } case PacketFileType.File: { int c; buffer = new byte[8192]; PacketFile packetFile = (PacketFile) file; packetFile.Seek(0, SeekOrigin.Begin); do { c = await packetFile.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); await stream.WriteAsync(buffer, 0, c).ConfigureAwait(false); } while (c > 0); break; } default: throw new NotImplementedException(); } } public static async ValueTask> ReadTreeFiles(Stream stream) { List files = new List(); IPacketFile? file = null; do { file = await ReadTreeFile(stream).ConfigureAwait(false); if (file is object) { files.Add(file); } } while (file is object); return files; } public static async ValueTask ReadTreeFile(Stream stream) { byte[] buffer = new byte[sizeof(byte) + sizeof(int) + sizeof(long)]; int c = await stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); if (c <= 0) { return null; } PacketFileType fileType = (PacketFileType) buffer[0]; int fileNameBufferSize = BitConverter.ToInt32(buffer, 1); long fileSize = BitConverter.ToInt64(buffer, 5); buffer = new byte[fileNameBufferSize]; c = await stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); if (c <= 0) { return null; } string fileName = Encoding.UTF8.GetString(buffer); switch (fileType) { case PacketFileType.Directory: { buffer = new byte[sizeof(long)]; c = await stream.ReadAsync(buffer, 0, buffer.Length); if (c <= 0) { return null; } long fileCount = BitConverter.ToInt64(buffer, 0); PacketDirectory directory = new PacketDirectory(fileName); for (long i = 0 ; i < fileCount ; i++) { IPacketFile? file = await ReadTreeFile(stream).ConfigureAwait(false); if (file is null) { break; } directory.AddFile(file); } return directory; } case PacketFileType.File: { PacketFile file = new PacketFile(fileName); buffer = new byte[8192]; long remainedSize = fileSize; long readed = 0; do { int leftSize = (int) Math.Min(buffer.Length, fileSize - readed); c = await stream.ReadAsync(buffer, 0, leftSize).ConfigureAwait(false); await file.WriteAsync(buffer, 0, c).ConfigureAwait(false); remainedSize -= c; readed += c; } while (remainedSize > 0); file.Seek(0, SeekOrigin.Begin); return file; } default: throw new NotImplementedException(); } } }