Exemplo n.º 1
0
        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));
        }