Example #1
0
        private void OpenFile(string fileName, byte[] input = null, TreeViewWithSearchResults.TreeViewPackageTag currentPackage = null)
        {
            Console.WriteLine($"Opening {fileName}");

            var tab = new TabPage(Path.GetFileName(fileName));

            tab.ToolTipText = fileName;
            tab.Controls.Add(new LoadingFile());

            mainTabs.TabPages.Add(tab);
            mainTabs.SelectTab(tab);

            var task = Task.Factory.StartNew(() => ProcessFile(fileName, input, currentPackage));

            task.ContinueWith(
                t =>
            {
                t.Exception?.Flatten().Handle(ex =>
                {
                    var control = new TextBox
                    {
                        Dock      = DockStyle.Fill,
                        Font      = new Font(FontFamily.GenericMonospace, 8),
                        Multiline = true,
                        ReadOnly  = true,
                        Text      = ex.ToString(),
                    };

                    tab.Controls.Clear();
                    tab.Controls.Add(control);

                    return(false);
                });
            },
                CancellationToken.None,
                TaskContinuationOptions.OnlyOnFaulted,
                TaskScheduler.FromCurrentSynchronizationContext());

            task.ContinueWith(
                t =>
            {
                tab.Controls.Clear();

                foreach (Control c in t.Result.Controls)
                {
                    tab.Controls.Add(c);
                }
            },
                CancellationToken.None,
                TaskContinuationOptions.OnlyOnRanToCompletion,
                TaskScheduler.FromCurrentSynchronizationContext());
        }
Example #2
0
        private TabPage ProcessFile(string fileName, byte[] input, TreeViewWithSearchResults.TreeViewPackageTag currentPackage)
        {
            var tab           = new TabPage();
            var vrfGuiContext = new VrfGuiContext(fileName, currentPackage);

            uint   magic = 0;
            ushort magicResourceVersion = 0;

            if (input != null)
            {
                if (input.Length >= 6)
                {
                    magic = BitConverter.ToUInt32(input, 0);
                    magicResourceVersion = BitConverter.ToUInt16(input, 4);
                }
            }
            else
            {
                var magicData = new byte[6];

                using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                {
                    fs.Read(magicData, 0, 6);
                }

                magic = BitConverter.ToUInt32(magicData, 0);
                magicResourceVersion = BitConverter.ToUInt16(magicData, 4);
            }

            if (magic != Package.MAGIC && input == null && Regex.IsMatch(fileName, @"_[0-9]{3}\.vpk$"))
            {
                // TODO: Update tab name
                fileName = $"{fileName.Substring(0, fileName.Length - 8)}_dir.vpk";
                magic    = Package.MAGIC;
            }

            if (magic == Package.MAGIC)
            {
                var package = new Package();

                if (input != null)
                {
                    package.SetFileName(fileName);
                    package.Read(new MemoryStream(input));
                }
                else
                {
                    package.Read(fileName);
                }

                // create a TreeView with search capabilities, register its events, and add it to the tab
                var treeViewWithSearch = new TreeViewWithSearchResults(ImageList);
                treeViewWithSearch.InitializeTreeViewFromPackage(fileName, new TreeViewWithSearchResults.TreeViewPackageTag
                {
                    Package       = package,
                    ParentPackage = currentPackage?.Package,
                });
                treeViewWithSearch.TreeNodeMouseDoubleClick += VPK_OpenFile;
                treeViewWithSearch.TreeNodeMouseClick       += VPK_OnClick;
                treeViewWithSearch.ListViewItemDoubleClick  += VPK_OpenFile;
                treeViewWithSearch.ListViewItemRightClick   += VPK_OnClick;
                treeViewWithSearch.Disposed += VPK_Disposed;
                tab.Controls.Add(treeViewWithSearch);

                // since we're in a separate thread, invoke to update the UI
                Invoke((MethodInvoker)(() => findToolStripButton.Enabled = true));
            }
            else if (magic == CompiledShader.MAGIC)
            {
                var shader = new CompiledShader();

                var buffer = new StringWriter();
                var oldOut = Console.Out;
                Console.SetOut(buffer);

                if (input != null)
                {
                    shader.Read(fileName, new MemoryStream(input));
                }
                else
                {
                    shader.Read(fileName);
                }

                Console.SetOut(oldOut);

                var control = new TextBox();
                control.Font       = new Font(FontFamily.GenericMonospace, control.Font.Size);
                control.Text       = NormalizeLineEndings(buffer.ToString());
                control.Dock       = DockStyle.Fill;
                control.Multiline  = true;
                control.ReadOnly   = true;
                control.ScrollBars = ScrollBars.Both;
                tab.Controls.Add(control);
            }
            else if (magic == ClosedCaptions.MAGIC)
            {
                var captions = new ClosedCaptions();
                if (input != null)
                {
                    captions.Read(fileName, new MemoryStream(input));
                }
                else
                {
                    captions.Read(fileName);
                }

                var control = new DataGridView
                {
                    Dock                = DockStyle.Fill,
                    AutoSize            = true,
                    ReadOnly            = true,
                    AllowUserToAddRows  = false,
                    AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill,
                    DataSource          = new BindingSource(new BindingList <ClosedCaption>(captions.Captions), null),
                    ScrollBars          = ScrollBars.Both,
                };
                tab.Controls.Add(control);
            }
            else if (magic == ToolsAssetInfo.MAGIC)
            {
                var toolsAssetInfo = new ToolsAssetInfo();
                if (input != null)
                {
                    toolsAssetInfo.Read(new MemoryStream(input));
                }
                else
                {
                    toolsAssetInfo.Read(fileName);
                }

                var text = new TextBox
                {
                    Dock       = DockStyle.Fill,
                    ScrollBars = ScrollBars.Vertical,
                    Multiline  = true,
                    ReadOnly   = true,
                    Text       = NormalizeLineEndings(toolsAssetInfo.ToString()),
                };
                tab.Controls.Add(text);
            }
            else if (magic == BinaryKV3.MAGIC || magic == BinaryKV3.MAGIC2)
            {
                var    kv3 = new BinaryKV3();
                Stream kv3stream;

                if (input != null)
                {
                    kv3stream = new MemoryStream(input);
                }
                else
                {
                    kv3stream = File.OpenRead(fileName);
                }

                using (var binaryReader = new BinaryReader(kv3stream))
                {
                    kv3.Size = (uint)kv3stream.Length;
                    kv3.Read(binaryReader, null);
                }

                kv3stream.Close();

                var control = new TextBox();
                control.Font       = new Font(FontFamily.GenericMonospace, control.Font.Size);
                control.Text       = kv3.ToString();
                control.Dock       = DockStyle.Fill;
                control.Multiline  = true;
                control.ReadOnly   = true;
                control.ScrollBars = ScrollBars.Both;
                tab.Controls.Add(control);
            }
            else if (magicResourceVersion == Resource.KnownHeaderVersion)
            {
                var resource = new Resource();
                if (input != null)
                {
                    resource.Read(new MemoryStream(input));
                }
                else
                {
                    resource.Read(fileName);
                }

                var resTabs = new TabControl
                {
                    Dock = DockStyle.Fill,
                };

                switch (resource.ResourceType)
                {
                case ResourceType.Texture:
                    var tab2 = new TabPage("TEXTURE")
                    {
                        AutoScroll = true,
                    };

                    try
                    {
                        var tex = (Texture)resource.DataBlock;

                        var control = new Forms.Texture
                        {
                            BackColor = Color.Black,
                        };
                        control.SetImage(tex.GenerateBitmap().ToBitmap(), Path.GetFileNameWithoutExtension(fileName), tex.Width, tex.Height);

                        tab2.Controls.Add(control);
                        Invoke(new ExportDel(AddToExport), $"Export {Path.GetFileName(fileName)} as an image", fileName, new ExportData {
                            Resource = resource
                        });
                    }
                    catch (Exception e)
                    {
                        var control = new TextBox
                        {
                            Dock      = DockStyle.Fill,
                            Font      = new Font(FontFamily.GenericMonospace, 8),
                            Multiline = true,
                            ReadOnly  = true,
                            Text      = e.ToString(),
                        };

                        tab2.Controls.Add(control);
                    }

                    resTabs.TabPages.Add(tab2);
                    break;

                case ResourceType.Panorama:
                    if (((Panorama)resource.DataBlock).Names.Count > 0)
                    {
                        var nameTab     = new TabPage("PANORAMA NAMES");
                        var nameControl = new DataGridView
                        {
                            Dock                = DockStyle.Fill,
                            AutoSize            = true,
                            ReadOnly            = true,
                            AllowUserToAddRows  = false,
                            AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill,
                            DataSource          = new BindingSource(new BindingList <Panorama.NameEntry>(((Panorama)resource.DataBlock).Names), null),
                        };
                        nameTab.Controls.Add(nameControl);
                        resTabs.TabPages.Add(nameTab);
                    }

                    break;

                case ResourceType.PanoramaLayout:
                    Invoke(new ExportDel(AddToExport), $"Export {Path.GetFileName(fileName)} as XML", fileName, new ExportData {
                        Resource = resource
                    });
                    break;

                case ResourceType.PanoramaScript:
                    Invoke(new ExportDel(AddToExport), $"Export {Path.GetFileName(fileName)} as JS", fileName, new ExportData {
                        Resource = resource
                    });
                    break;

                case ResourceType.PanoramaStyle:
                    Invoke(new ExportDel(AddToExport), $"Export {Path.GetFileName(fileName)} as CSS", fileName, new ExportData {
                        Resource = resource
                    });
                    break;

                case ResourceType.Particle:
                    var viewerControl = new GLParticleViewer(vrfGuiContext);
                    viewerControl.Load += (_, __) =>
                    {
                        var particleSystem   = (ParticleSystem)resource.DataBlock;
                        var particleRenderer = new ParticleRenderer(particleSystem, vrfGuiContext);

                        viewerControl.AddRenderer(particleRenderer);
                    };

                    var particleRendererTab = new TabPage("PARTICLE");
                    particleRendererTab.Controls.Add(viewerControl.Control);
                    resTabs.TabPages.Add(particleRendererTab);
                    break;

                case ResourceType.Sound:
                    var soundTab = new TabPage("SOUND");
                    var ap       = new AudioPlayer(resource, soundTab);
                    resTabs.TabPages.Add(soundTab);

                    Invoke(new ExportDel(AddToExport), $"Export {Path.GetFileName(fileName)} as {((Sound)resource.DataBlock).SoundType}", fileName, new ExportData {
                        Resource = resource
                    });

                    break;

                case ResourceType.World:
                    var worldmeshTab = new TabPage("MAP");
                    worldmeshTab.Controls.Add(new GLWorldViewer(vrfGuiContext, (World)resource.DataBlock).ViewerControl);
                    resTabs.TabPages.Add(worldmeshTab);
                    break;

                case ResourceType.WorldNode:
                    var nodemeshTab = new TabPage("WORLD NODE");
                    nodemeshTab.Controls.Add(new GLWorldViewer(vrfGuiContext, (WorldNode)resource.DataBlock).ViewerControl);
                    resTabs.TabPages.Add(nodemeshTab);
                    break;

                case ResourceType.Model:
                    var modelRendererTab = new TabPage("MODEL");
                    modelRendererTab.Controls.Add(new GLModelViewer(vrfGuiContext, (Model)resource.DataBlock).ViewerControl);
                    resTabs.TabPages.Add(modelRendererTab);
                    break;

                case ResourceType.Mesh:
                    if (!resource.ContainsBlockType(BlockType.VBIB))
                    {
                        Console.WriteLine("Old style model, no VBIB!");
                        break;
                    }

                    Invoke(new ExportDel(AddToExport), $"Export {Path.GetFileName(fileName)} as OBJ", fileName, new ExportData {
                        Resource = resource, VrfGuiContext = vrfGuiContext
                    });

                    var meshRendererTab = new TabPage("MESH");
                    meshRendererTab.Controls.Add(new GLModelViewer(vrfGuiContext, new Mesh(resource)).ViewerControl);
                    resTabs.TabPages.Add(meshRendererTab);
                    break;

                case ResourceType.Material:
                    var materialViewerControl = new GLMaterialViewer();
                    materialViewerControl.Load += (_, __) =>
                    {
                        var material         = vrfGuiContext.MaterialLoader.LoadMaterial(resource);
                        var materialRenderer = new MaterialRenderer(material, vrfGuiContext);

                        materialViewerControl.AddRenderer(materialRenderer);
                    };

                    var materialRendererTab = new TabPage("MATERIAL");
                    materialRendererTab.Controls.Add(materialViewerControl.Control);
                    resTabs.TabPages.Add(materialRendererTab);
                    break;
                }

                foreach (var block in resource.Blocks)
                {
                    if (block.Type == BlockType.RERL)
                    {
                        var externalRefsTab = new TabPage("External Refs");

                        var externalRefs = new DataGridView
                        {
                            Dock = DockStyle.Fill,
                            AutoGenerateColumns = true,
                            AutoSize            = true,
                            ReadOnly            = true,
                            AllowUserToAddRows  = false,
                            AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill,
                            DataSource          = new BindingSource(new BindingList <ResourceExtRefList.ResourceReferenceInfo>(resource.ExternalReferences.ResourceRefInfoList), null),
                        };

                        externalRefsTab.Controls.Add(externalRefs);

                        resTabs.TabPages.Add(externalRefsTab);

                        continue;
                    }

                    if (block.Type == BlockType.NTRO)
                    {
                        if (((ResourceIntrospectionManifest)block).ReferencedStructs.Count > 0)
                        {
                            var externalRefsTab = new TabPage("Introspection Manifest: Structs");

                            var externalRefs = new DataGridView
                            {
                                Dock = DockStyle.Fill,
                                AutoGenerateColumns = true,
                                AutoSize            = true,
                                ReadOnly            = true,
                                AllowUserToAddRows  = false,
                                AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill,
                                DataSource          = new BindingSource(new BindingList <ResourceIntrospectionManifest.ResourceDiskStruct>(((ResourceIntrospectionManifest)block).ReferencedStructs), null),
                            };

                            externalRefsTab.Controls.Add(externalRefs);
                            resTabs.TabPages.Add(externalRefsTab);
                        }

                        if (((ResourceIntrospectionManifest)block).ReferencedEnums.Count > 0)
                        {
                            var externalRefsTab = new TabPage("Introspection Manifest: Enums");
                            var externalRefs2   = new DataGridView
                            {
                                Dock = DockStyle.Fill,
                                AutoGenerateColumns = true,
                                AutoSize            = true,
                                ReadOnly            = true,
                                AllowUserToAddRows  = false,
                                AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill,
                                DataSource          = new BindingSource(new BindingList <ResourceIntrospectionManifest.ResourceDiskEnum>(((ResourceIntrospectionManifest)block).ReferencedEnums), null),
                            };

                            externalRefsTab.Controls.Add(externalRefs2);
                            resTabs.TabPages.Add(externalRefsTab);
                        }

                        //continue;
                    }

                    var tab2 = new TabPage(block.Type.ToString());
                    try
                    {
                        var control = new TextBox();
                        control.Font = new Font(FontFamily.GenericMonospace, control.Font.Size);

                        if (block.Type == BlockType.DATA)
                        {
                            switch (resource.ResourceType)
                            {
                            case ResourceType.Sound:
                                control.Text = NormalizeLineEndings(((Sound)block).ToString());
                                break;

                            case ResourceType.Particle:
                            case ResourceType.Mesh:
                                if (block is BinaryKV3 blockKeyvalues)
                                {
                                    //Wrap it around a KV3File object to get the header.
                                    control.Text = NormalizeLineEndings(blockKeyvalues.GetKV3File().ToString());
                                }
                                else if (block is NTRO blockNTRO)
                                {
                                    control.Text = NormalizeLineEndings(blockNTRO.ToString());
                                }

                                break;

                            default:
                                control.Text = NormalizeLineEndings(block.ToString());
                                break;
                            }
                        }
                        else
                        {
                            control.Text = NormalizeLineEndings(block.ToString());
                        }

                        control.Dock       = DockStyle.Fill;
                        control.Multiline  = true;
                        control.ReadOnly   = true;
                        control.ScrollBars = ScrollBars.Both;
                        tab2.Controls.Add(control);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e);

                        var bv = new ByteViewer();
                        bv.Dock = DockStyle.Fill;
                        tab2.Controls.Add(bv);

                        Invoke((MethodInvoker)(() =>
                        {
                            resource.Reader.BaseStream.Position = block.Offset;
                            bv.SetBytes(resource.Reader.ReadBytes((int)block.Size));
                        }));
                    }

                    resTabs.TabPages.Add(tab2);
                }

                if (resource.ResourceType == ResourceType.PanoramaLayout ||
                    resource.ResourceType == ResourceType.PanoramaScript ||
                    resource.ResourceType == ResourceType.PanoramaStyle ||
                    resource.ResourceType == ResourceType.SoundEventScript ||
                    resource.ResourceType == ResourceType.SoundStackScript ||
                    resource.ResourceType == ResourceType.EntityLump)
                {
                    resTabs.SelectTab(resTabs.TabCount - 1);
                }

                tab.Controls.Add(resTabs);
            }
            else
            {
                var resTabs = new TabControl
                {
                    Dock = DockStyle.Fill,
                };
                tab.Controls.Add(resTabs);

                var bvTab = new TabPage("Hex");
                var bv    = new ByteViewer
                {
                    Dock = DockStyle.Fill,
                };
                bvTab.Controls.Add(bv);
                resTabs.TabPages.Add(bvTab);

                if (input == null)
                {
                    input = File.ReadAllBytes(fileName);
                }

                if (!input.Contains <byte>(0x00))
                {
                    var textTab = new TabPage("Text");
                    var text    = new TextBox
                    {
                        Dock       = DockStyle.Fill,
                        ScrollBars = ScrollBars.Vertical,
                        Multiline  = true,
                        ReadOnly   = true,
                        Text       = System.Text.Encoding.UTF8.GetString(input),
                    };
                    textTab.Controls.Add(text);
                    resTabs.TabPages.Add(textTab);
                    resTabs.SelectedTab = textTab;
                }

                Invoke((MethodInvoker)(() =>
                {
                    bv.SetBytes(input);
                }));
            }

            return(tab);
        }
Example #3
0
        private void ExtractFiles(object sender, bool decompile)
        {
            TreeViewWithSearchResults.TreeViewPackageTag package = null;
            TreeNode selectedNode = null;

            // the context menu can come from a TreeView or a ListView depending on where the user clicked to extract
            // each option has a difference in where we can get the values to extract
            if (((ContextMenuStrip)((ToolStripMenuItem)sender).Owner).SourceControl is TreeView)
            {
                var tree = ((ContextMenuStrip)((ToolStripMenuItem)sender).Owner).SourceControl as TreeView;
                selectedNode = tree.SelectedNode;
                package      = tree.Tag as TreeViewWithSearchResults.TreeViewPackageTag;
            }
            else if (((ContextMenuStrip)((ToolStripMenuItem)sender).Owner).SourceControl is ListView)
            {
                var listView = ((ContextMenuStrip)((ToolStripMenuItem)sender).Owner).SourceControl as ListView;
                selectedNode = listView.SelectedItems[0].Tag as TreeNode;
                package      = listView.Tag as TreeViewWithSearchResults.TreeViewPackageTag;
            }

            if (selectedNode.Tag.GetType() == typeof(PackageEntry))
            {
                // We are a file
                var file     = selectedNode.Tag as PackageEntry;
                var fileName = file.GetFileName();

                package.Package.ReadEntry(file, out var output);

                if (decompile && fileName.EndsWith("_c", StringComparison.Ordinal))
                {
                    using (var resource = new Resource())
                        using (var memory = new MemoryStream(output))
                        {
                            resource.Read(memory);

                            var extension = FileExtract.GetExtension(resource);

                            if (extension == null)
                            {
                                fileName = fileName.Substring(0, fileName.Length - 2);
                            }
                            else
                            {
                                fileName = Path.ChangeExtension(fileName, extension);
                            }

                            output = FileExtract.Extract(resource).ToArray();
                        }
                }

                var dialog = new SaveFileDialog
                {
                    InitialDirectory = Settings.Config.SaveDirectory,
                    Filter           = "All files (*.*)|*.*",
                    FileName         = fileName,
                };
                var userOK = dialog.ShowDialog();

                if (userOK == DialogResult.OK)
                {
                    Settings.Config.SaveDirectory = Path.GetDirectoryName(dialog.FileName);
                    Settings.Save();

                    using (var stream = dialog.OpenFile())
                    {
                        stream.Write(output, 0, output.Length);
                    }
                }
            }
            else
            {
                //We are a folder
                var dialog = new FolderBrowserDialog();
                if (dialog.ShowDialog() == DialogResult.OK)
                {
                    var extractDialog = new ExtractProgressForm(package.Package, selectedNode, dialog.SelectedPath, decompile);
                    extractDialog.ShowDialog();
                }
            }
        }
        private TabPage ProcessFile(string fileName, byte[] input, TreeViewWithSearchResults.TreeViewPackageTag currentPackage)
        {
            uint   magic = 0;
            ushort magicResourceVersion = 0;

            if (input != null)
            {
                if (input.Length >= 6)
                {
                    magic = BitConverter.ToUInt32(input, 0);
                    magicResourceVersion = BitConverter.ToUInt16(input, 4);
                }
            }
            else
            {
                var magicData = new byte[6];

                using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                {
                    fs.Read(magicData, 0, 6);
                }

                magic = BitConverter.ToUInt32(magicData, 0);
                magicResourceVersion = BitConverter.ToUInt16(magicData, 4);
            }

            var vrfGuiContext = new VrfGuiContext(fileName, currentPackage);

            if (Types.Viewers.Package.IsAccepted(magic))
            {
                var tab = new Types.Viewers.Package
                {
                    ImageList = ImageList, // TODO: Move this directly into Package
                }.Create(vrfGuiContext, input);

                // since we're in a separate thread, invoke to update the UI
                Invoke((MethodInvoker)(() => findToolStripButton.Enabled = true));

                return(tab);
            }
            else if (Types.Viewers.CompiledShader.IsAccepted(magic))
            {
                return(new Types.Viewers.CompiledShader().Create(vrfGuiContext, input));
            }
            else if (Types.Viewers.ClosedCaptions.IsAccepted(magic))
            {
                return(new Types.Viewers.ClosedCaptions().Create(vrfGuiContext, input));
            }
            else if (Types.Viewers.ToolsAssetInfo.IsAccepted(magic))
            {
                return(new Types.Viewers.ToolsAssetInfo().Create(vrfGuiContext, input));
            }
            else if (Types.Viewers.BinaryKeyValues.IsAccepted(magic))
            {
                return(new Types.Viewers.BinaryKeyValues().Create(vrfGuiContext, input));
            }
            else if (Types.Viewers.Resource.IsAccepted(magicResourceVersion))
            {
                return(new Types.Viewers.Resource().Create(vrfGuiContext, input));
            }
            else if (Types.Viewers.Image.IsAccepted(magic))
            {
                return(new Types.Viewers.Image().Create(vrfGuiContext, input));
            }
            else if (Types.Viewers.Audio.IsAccepted(magic, fileName))
            {
                return(new Types.Viewers.Audio().Create(vrfGuiContext, input));
            }

            return(new Types.Viewers.ByteViewer().Create(vrfGuiContext, input));
        }
        public void OpenFile(string fileName, byte[] input = null, TreeViewWithSearchResults.TreeViewPackageTag currentPackage = null)
        {
            Console.WriteLine($"Opening {fileName}");

            if (input == null && Regex.IsMatch(fileName, @"_[0-9]{3}\.vpk$"))
            {
                var fixedPackage = $"{fileName.Substring(0, fileName.Length - 8)}_dir.vpk";

                if (File.Exists(fixedPackage))
                {
                    Console.WriteLine($"You opened \"{Path.GetFileName(fileName)}\" but there is \"{Path.GetFileName(fixedPackage)}\"");
                    fileName = fixedPackage;
                }
            }

            var tab = new TabPage(Path.GetFileName(fileName));

            tab.ToolTipText = fileName;
            tab.Controls.Add(new LoadingFile());

            mainTabs.TabPages.Add(tab);
            mainTabs.SelectTab(tab);

            var task = Task.Factory.StartNew(() => ProcessFile(fileName, input, currentPackage));

            task.ContinueWith(
                t =>
            {
                t.Exception?.Flatten().Handle(ex =>
                {
                    var control = new TextBox
                    {
                        Dock      = DockStyle.Fill,
                        Font      = new Font(FontFamily.GenericMonospace, 8),
                        Multiline = true,
                        ReadOnly  = true,
                        Text      = ex.ToString(),
                    };

                    tab.Controls.Clear();
                    tab.Controls.Add(control);

                    return(false);
                });
            },
                CancellationToken.None,
                TaskContinuationOptions.OnlyOnFaulted,
                TaskScheduler.FromCurrentSynchronizationContext());

            task.ContinueWith(
                t =>
            {
                tab.Controls.Clear();

                foreach (Control c in t.Result.Controls)
                {
                    tab.Controls.Add(c);
                }
            },
                CancellationToken.None,
                TaskContinuationOptions.OnlyOnRanToCompletion,
                TaskScheduler.FromCurrentSynchronizationContext());
        }