private static uint HandleNestedPack( string rootFileManifestPath, Stream output, Endian endian) { var opStack = new Stack <PackOperation>(); opStack.Push(new PackOperation() { Type = PackOperationType.File, Path = rootFileManifestPath, IsPack = true, }); var paddingBytes = new byte[16]; var basePosition = output.Position; while (opStack.Count > 0) { var op = opStack.Pop(); if (op.Type == PackOperationType.Pad) { output.WriteBytes(paddingBytes); continue; } if (op.Type == PackOperationType.Header) { var endPosition = output.Position; var nestedPack = op.Parent; var packFile = new PackFile() { Endian = endian, }; var count = nestedPack.Entries.Count; var hasIds = nestedPack.Entries.Any(e => e.RawId != null); for (int i = 0; i < count; i++) { var entry = nestedPack.Entries[i]; var entryOffset = (uint)(entry.Position - nestedPack.HeaderPosition); packFile.Entries.Add(new PackFile.Entry(entry.RawId ?? 0, entryOffset)); if (i + 1 < count) { var nextEntry = nestedPack.Entries[i + 1]; if (entry.Position + entry.Size > nextEntry.Position) { throw new InvalidOperationException(); } } } uint totalSize; if (count > 0) { var lastEntry = nestedPack.Entries[count - 1]; totalSize = (uint)((lastEntry.Position - nestedPack.HeaderPosition) + lastEntry.Size); } else { totalSize = (uint)(nestedPack.DataPosition - nestedPack.HeaderPosition); } packFile.TotalSize = totalSize; output.Position = nestedPack.HeaderPosition; packFile.Serialize(output); if (output.Position > nestedPack.DataPosition) { throw new InvalidOperationException(); } if (nestedPack.Parent != null) { var previousParentEntry = nestedPack.Parent.Entries[nestedPack.ParentIndex]; nestedPack.Parent.Entries[nestedPack.ParentIndex] = new NestedPackEntry( nestedPack.HeaderPosition, packFile.TotalSize + 16 /* padding */, previousParentEntry.RawId); } output.Position = endPosition; continue; } if (op.Type != PackOperationType.File) { throw new NotSupportedException(); } if (op.IsPack == false) { long dataPosition = output.Position; uint dataSize; using (var input = File.OpenRead(op.Path)) { if (input.Length > uint.MaxValue) { throw new InvalidOperationException("file too large"); } dataSize = (uint)input.Length; output.WriteFromStream(input, dataSize); } op.Parent.Entries.Add(new NestedPackEntry(dataPosition, dataSize, op.PackId)); } else { var basePath = Path.GetDirectoryName(op.Path); var fileManifests = ReadManifest <List <FileTableManifest.File> >(op.Path); var headerPosition = output.Position; var hasIds = fileManifests.Any(fm => fm.PackId != null); var headerSize = PackFile.GetHeaderSize(fileManifests.Count, hasIds); output.Position += headerSize; var dataPosition = output.Position; var nestedPack = new NestedPack() { HeaderPosition = headerPosition, DataPosition = dataPosition, }; if (op.Parent != null) { nestedPack.Parent = op.Parent; nestedPack.ParentIndex = op.Parent.Entries.Count; op.Parent.Entries.Add(new NestedPackEntry(-1, 0, op.PackId)); } opStack.Push(new PackOperation() { Type = PackOperationType.Header, Parent = nestedPack, }); if (op.Parent != null) { opStack.Push(new PackOperation() { Type = PackOperationType.Pad, Parent = nestedPack, }); } foreach (var fileManifest in fileManifests.AsEnumerable().Reverse()) { opStack.Push(new PackOperation() { Type = PackOperationType.File, Parent = nestedPack, Path = Path.Combine(basePath, CleanPathForManifest(fileManifest.Path)), IsPack = fileManifest.IsPack, PackId = fileManifest.PackId?.RawId, }); } } } return((uint)(output.Position - basePosition)); }