private async Task <RobloxFileManifest> getFileManifest()
        {
            string fileManifestUrl  = constructDownloadUrl("rbxManifest.txt");
            string fileManifestData = await http.DownloadStringTaskAsync(fileManifestUrl);

            RobloxFileManifest result = new RobloxFileManifest();

            result.FileToSignature  = new Dictionary <string, string>();
            result.SignatureToFiles = new Dictionary <string, List <string> >();

            using (StringReader reader = new StringReader(fileManifestData))
            {
                string path      = "";
                string signature = "";

                while (path != null && signature != null)
                {
                    try
                    {
                        path      = reader.ReadLine();
                        signature = reader.ReadLine();

                        if (path == null || signature == null)
                        {
                            break;
                        }

                        if (!result.SignatureToFiles.ContainsKey(signature))
                        {
                            result.SignatureToFiles.Add(signature, new List <string>());
                        }

                        result.SignatureToFiles[signature].Add(path);
                        result.FileToSignature.Add(path, signature);
                    }
                    catch
                    {
                        break;
                    }
                }
            }

            return(result);
        }
        public async Task <string> RunInstaller(string database, bool forceInstall = false)
        {
            string localAppData = Environment.GetEnvironmentVariable("LocalAppData");
            string rootDir      = getDirectory(localAppData, "Roblox Studio");
            string downloads    = getDirectory(rootDir, "downloads");

            Show();
            BringToFront();

            if (!exitWhenClosed)
            {
                TopMost         = true;
                FormBorderStyle = FormBorderStyle.None;
            }

            await setStatus("Checking for updates");

            setupDir  = "setup." + database + ".com/";
            buildName = await getCurrentVersion(database);

            robloxStudioBetaPath = Path.Combine(rootDir, "RobloxStudioBeta.exe");

            echo("Checking build installation...");

            string currentBuildDatabase = Program.ModManagerRegistry.GetValue("BuildDatabase", "") as string;
            string currentBuildVersion  = Program.ModManagerRegistry.GetValue("BuildVersion", "") as string;

            if (currentBuildDatabase != database || currentBuildVersion != buildName || forceInstall)
            {
                echo("This build needs to be installed!");

                await setStatus("Installing the latest '" + (database == "roblox" ? "production" : database) + "' branch of Roblox Studio...");

                bool safeToContinue = false;
                bool cancelled      = false;

                while (!safeToContinue)
                {
                    Process[] running = Process.GetProcessesByName("RobloxStudioBeta");

                    if (running.Length > 0)
                    {
                        foreach (Process p in running)
                        {
                            p.CloseMainWindow();
                            await Task.Delay(50);
                        }

                        await Task.Delay(1000);

                        Process[] runningNow = Process.GetProcessesByName("RobloxStudioBeta");
                        BringToFront();

                        if (runningNow.Length > 0)
                        {
                            echo("Running apps detected, action on the user's part is needed.");
                            Hide();

                            DialogResult result = MessageBox.Show("All Roblox Studio instances needs to be closed in order to install the new version.\nPress Ok once you've saved your work and the windows are closed, or\nPress Cancel to skip the update for this launch.", "Notice", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning);
                            Show();

                            if (result == DialogResult.Cancel)
                            {
                                safeToContinue = true;
                                cancelled      = true;
                            }
                        }
                    }
                    else
                    {
                        safeToContinue = true;
                    }
                }

                if (!cancelled)
                {
                    List <Task> taskQueue = new List <Task>();

                    echo("Grabbing package manifest...");
                    List <RobloxPackageManifest> pkgManifest = await getPackageManifest();

                    echo("Grabbing file manifest...");
                    RobloxFileManifest fileManifest = await getFileManifest();

                    progressBar.Maximum = 1;
                    progressBar.Value   = 0;
                    progressBar.Style   = ProgressBarStyle.Continuous;

                    foreach (RobloxPackageManifest package in pkgManifest)
                    {
                        int size = package.Size;

                        string pkgName = package.Name;
                        string oldSig  = pkgRegistry.GetValue(pkgName, "") as string;
                        string newSig  = package.Signature;

                        if (oldSig == newSig && !forceInstall)
                        {
                            echo("Package '" + pkgName + "' hasn't changed between builds, skipping.");
                            continue;
                        }

                        progressBar.Maximum += size;

                        Task installer = Task.Run(async() =>
                        {
                            string zipFileUrl     = amazonAws + setupDir + buildName + '-' + package.Name;
                            string zipExtractPath = Path.Combine(downloads, package.Name);

                            echo("Installing package " + zipFileUrl);

                            try
                            {
                                WebClient localHttp = new WebClient();
                                localHttp.Headers.Set("UserAgent", "Roblox");

                                byte[] fileContents = await localHttp.DownloadDataTaskAsync(zipFileUrl);
                                if (fileContents.Length != package.PackedSize)
                                {
                                    throw new InvalidDataException(package.Name + " expected packed size: " + package.PackedSize + " but got: " + fileContents.Length);
                                }

                                using (MemoryStream fileBuffer = new MemoryStream(fileContents))
                                {
                                    string checkSig = computeSignature(fileBuffer);
                                    if (checkSig != newSig)
                                    {
                                        throw new InvalidDataException(package.Name + " expected signature: " + newSig + " but got: " + checkSig);
                                    }
                                }

                                File.WriteAllBytes(zipExtractPath, fileContents);
                                ZipArchive archive = ZipFile.OpenRead(zipExtractPath);

                                string localRootDir = null;

                                foreach (ZipArchiveEntry entry in archive.Entries)
                                {
                                    if (entry.Length > 0)
                                    {
                                        string newFileSig = null;

                                        if (localRootDir != null)
                                        {
                                            string filePath = entry.FullName.Replace('/', '\\');
                                            if (!fileManifest.FileToSignature.ContainsKey(filePath))
                                            {
                                                filePath = localRootDir + filePath;
                                            }

                                            if (fileManifest.FileToSignature.ContainsKey(filePath))
                                            {
                                                newFileSig = fileManifest.FileToSignature[filePath];
                                            }
                                        }

                                        if (newFileSig == null)
                                        {
                                            using (Stream data = entry.Open())
                                                newFileSig = computeSignature(data);
                                        }

                                        if (fileManifest.SignatureToFiles.ContainsKey(newFileSig))
                                        {
                                            List <string> files = fileManifest.SignatureToFiles[newFileSig];
                                            foreach (string file in files)
                                            {
                                                writePackageFile(rootDir, pkgName, file, newFileSig, entry, forceInstall);

                                                if (localRootDir == null)
                                                {
                                                    string filePath  = fixFilePath(pkgName, file);
                                                    string entryPath = entry.FullName.Replace('/', '\\');

                                                    if (filePath.EndsWith(entryPath))
                                                    {
                                                        localRootDir = filePath.Replace(entryPath, "");
                                                    }
                                                }
                                            }
                                        }
                                        else
                                        {
                                            string file = entry.FullName;
                                            writePackageFile(rootDir, pkgName, file, newFileSig, entry, forceInstall);
                                        }
                                    }
                                }

                                pkgRegistry.SetValue(pkgName, package.Signature);
                            }
                            catch (Exception e)
                            {
                                throw e;
                            }
                        });

                        taskQueue.Add(installer);
                    }

                    await Task.WhenAll(taskQueue.ToArray());

                    await setStatus("Writing AppSettings.xml");

                    progressBar.Style = ProgressBarStyle.Marquee;

                    Program.ModManagerRegistry.SetValue("BuildDatabase", database);
                    Program.ModManagerRegistry.SetValue("BuildVersion", buildName);

                    echo("Writing AppSettings.xml...");

                    string appSettings = Path.Combine(rootDir, "AppSettings.xml");
                    File.WriteAllText(appSettings, "<Settings>\r\n\t<ContentFolder>content</ContentFolder>\r\n\t<BaseUrl>http://www.roblox.com</BaseUrl>\r\n</Settings>");
                }
                else
                {
                    echo("Update cancelled. Proceeding with launch on current database and version.");
                }
            }
            else
            {
                echo("This version of Roblox Studio has been installed!");
            }

            await setStatus("Configuring Roblox Studio...");

            Program.UpdateStudioRegistryProtocols(setupDir, buildName, robloxStudioBetaPath);

            RegistryKey fvarRegistry      = Program.GetSubKey(Program.ModManagerRegistry, "FVariables");
            string      clientSettings    = getDirectory(rootDir, "ClientSettings");
            string      clientAppSettings = Path.Combine(clientSettings, "ClientAppSettings.json");

            applyFVariableConfiguration(fvarRegistry, clientAppSettings);

            await setStatus("Roblox Studio is up to date!");

            return(robloxStudioBetaPath);
        }