public TabPage Create(VrfGuiContext vrfGuiContext, byte[] input)
        {
            var tab     = new TabPage();
            var package = new SteamDatabase.ValvePak.Package();

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

            // create a TreeView with search capabilities, register its events, and add it to the tab
            var treeViewWithSearch = new TreeViewWithSearchResults(ImageList);

            treeViewWithSearch.InitializeTreeViewFromPackage(vrfGuiContext.FileName, new TreeViewWithSearchResults.TreeViewPackageTag
            {
                Package          = package,
                ParentFileLoader = vrfGuiContext.FileLoader,
            });
            treeViewWithSearch.TreeNodeMouseDoubleClick += VPK_OpenFile;
            treeViewWithSearch.TreeNodeRightClick       += VPK_OnClick;
            treeViewWithSearch.ListViewItemDoubleClick  += VPK_OpenFile;
            treeViewWithSearch.ListViewItemRightClick   += VPK_OnClick;
            treeViewWithSearch.Disposed += VPK_Disposed;
            tab.Controls.Add(treeViewWithSearch);

            return(tab);
        }
        internal static List <PackageEntry> RecoverDeletedFiles(SteamDatabase.ValvePak.Package package)
        {
            var allEntries = package.Entries
                             .SelectMany(file => file.Value)
                             .OrderBy(file => file.Offset)
                             .GroupBy(file => file.ArchiveIndex)
                             .OrderBy(x => x.Key)
                             .ToDictionary(x => x.Key, x => x.ToList());

            var hiddenIndex    = 0;
            var totalSlackSize = 0u;
            var hiddenFiles    = new List <PackageEntry>();

            // TODO: Skip non-chunked vpks?
            foreach (var(archiveIndex, entries) in allEntries)
            {
                var nextOffset = 0u;

                foreach (var entry in entries)
                {
                    if (entry.Length == 0)
                    {
                        continue;
                    }

                    var offset = nextOffset;
                    nextOffset = entry.Offset + entry.Length;

                    totalSlackSize += entry.Offset - offset;

                    var scan = true;

                    while (scan)
                    {
                        scan = false;

                        if (offset == entry.Offset)
                        {
                            break;
                        }

                        offset = (offset + 16 - 1) & ~(16u - 1); // TODO: Validate this gap

                        var length = entry.Offset - offset;

                        if (length <= 16)
                        {
                            // TODO: Verify what this gap is, seems to be null bytes
                            break;
                        }

                        hiddenIndex++;
                        var newEntry = new PackageEntry
                        {
                            FileName      = $"Archive {archiveIndex} File {hiddenIndex}",
                            DirectoryName = DELETED_FILES_FOLDER,
                            TypeName      = " ",
                            CRC32         = 0,
                            SmallData     = Array.Empty <byte>(),
                            ArchiveIndex  = archiveIndex,
                            Offset        = offset,
                            Length        = length,
                        };

                        package.ReadEntry(newEntry, out var bytes, validateCrc: false);
                        var stream = new MemoryStream(bytes);

                        try
                        {
                            var resource = new ValveResourceFormat.Resource();
                            resource.Read(stream, verifyFileSize: false);

                            var fileSize = resource.FullFileSize;

                            if (fileSize != length)
                            {
                                if (fileSize > length)
                                {
                                    throw new Exception("Resource filesize is bigger than the gap length we found");
                                }

                                newEntry.Length = fileSize;
                                offset         += fileSize;
                                scan            = true;
                            }

                            if (resource.ResourceType != ResourceType.Unknown)
                            {
                                var type = typeof(ResourceType).GetMember(resource.ResourceType.ToString())[0];
                                newEntry.TypeName  = ((ExtensionAttribute)type.GetCustomAttributes(typeof(ExtensionAttribute), false)[0]).Extension;
                                newEntry.TypeName += "_c";
                            }

                            newEntry.DirectoryName += "/" + resource.ResourceType;
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"File {hiddenIndex} - {ex.Message}");

                            newEntry.FileName += $" ({length} bytes)";
                        }

                        if (!package.Entries.TryGetValue(newEntry.TypeName, out var typeEntries))
                        {
                            typeEntries = new List <PackageEntry>();
                            package.Entries.Add(newEntry.TypeName, typeEntries);
                        }

                        typeEntries.Add(newEntry);
                        hiddenFiles.Add(newEntry);
                    }
                }

                // TODO: Check nextOffset against archive file size
            }

            Console.WriteLine($"Found {hiddenIndex} deleted files totaling {totalSlackSize.ToFileSizeString()}");

            // TODO: Check for completely unused vpk chunk files

            return(hiddenFiles);
        }