public static async Task <List <PackageManifest> > Get(string branch, string versionGuid) { var result = new List <PackageManifest>(); string pkgManifestUrl = $"https://s3.amazonaws.com/setup.{branch}.com/{versionGuid}-rbxPkgManifest.txt"; string pkgManifestData; using (WebClient http = new WebClient()) pkgManifestData = await http.DownloadStringTaskAsync(pkgManifestUrl); using (StringReader reader = new StringReader(pkgManifestData)) { string version = reader.ReadLine(); if (version != "v0") { string errorMsg = $"Unexpected package manifest version: {version} (expected v0!)\n" + "Please contact CloneTrooper1019 if you see this error."; throw new NotSupportedException(errorMsg); } while (true) { try { string fileName = reader.ReadLine(); string signature = reader.ReadLine(); int packedSize = int.Parse(reader.ReadLine()); int size = int.Parse(reader.ReadLine()); if (fileName.EndsWith(".zip")) { var pkgManifest = new PackageManifest() { Name = fileName, Branch = branch, Signature = signature, PackedSize = packedSize, Size = size }; result.Add(pkgManifest); } } catch { break; } } } return(result); }
public async Task RunInstaller(string branch) { restore(); setStatus("Checking for updates"); echo("Checking build installation..."); string currentBranch = Program.GetString("BuildBranch", "roblox"); string currentVersion = versionRegistry.GetString("VersionGuid"); bool shouldInstall = (forceInstall || currentBranch != branch); string fastVersion = await GetFastVersionGuid(currentBranch); if (branch == "roblox") { buildVersion = fastVersion; } ClientVersionInfo versionInfo = null; if (shouldInstall || fastVersion != currentVersion) { if (currentBranch != "roblox") { echo("Possible update detected, verifying..."); } versionInfo = await GetCurrentVersionInfo(branch, fastVersion); buildVersion = versionInfo.Guid; } else { buildVersion = fastVersion; } if (currentVersion != buildVersion || shouldInstall) { echo("This build needs to be installed!"); bool studioClosed = await shutdownStudioProcesses(); if (studioClosed) { string binaryType = GetStudioBinaryType(); string studioDir = GetStudioDirectory(); string versionId = versionInfo.Version; restore(); setStatus($"Installing Version {versionId} of Roblox Studio..."); var taskQueue = new List <Task>(); echo("Grabbing package manifest..."); List <PackageManifest> pkgManifest = await PackageManifest.Get(branch, buildVersion); echo("Grabbing file manifest..."); fileManifest = await FileManifest.Get(branch, buildVersion); progressBar.Maximum = 1; progressBar.Value = 0; progressBar.Style = ProgressBarStyle.Continuous; progressBar.Refresh(); foreach (PackageManifest package in pkgManifest) { Task installer = Task.Run(() => installPackage(package)); taskQueue.Add(installer); } await Task.WhenAll(taskQueue.ToArray()); echo("Writing AppSettings.xml..."); string appSettings = Path.Combine(studioDir, "AppSettings.xml"); File.WriteAllText(appSettings, appSettingsXml); setStatus("Deleting unused files..."); await deleteUnusedFiles(); progressBar.Style = ProgressBarStyle.Marquee; progressBar.Refresh(); Program.SetValue("BuildBranch", branch); versionRegistry.SetValue("Version", versionId); versionRegistry.SetValue("VersionGuid", buildVersion); } else { progressBar.Style = ProgressBarStyle.Marquee; progressBar.Refresh(); echo("Update cancelled. Launching on current branch and version."); } } else { echo("This version of Roblox Studio has been installed!"); } setStatus("Configuring Roblox Studio..."); echo("Updating registry protocols..."); Program.UpdateStudioRegistryProtocols(); if (exitWhenClosed) { echo("Applying flag configuration..."); FlagEditor.ApplyFlags(); echo("Patching explorer icons..."); await ExplorerIconEditor.PatchExplorerIcons(); } setStatus("Starting Roblox Studio..."); echo("Roblox Studio is up to date!"); await Task.Delay(1000); }
private async Task installPackage(PackageManifest package) { string studioDir = GetStudioDirectory(); string downloads = getDirectory(studioDir, "downloads"); string pkgName = package.Name; string branch = package.Branch; string oldSig = pkgRegistry.GetString(pkgName); string newSig = package.Signature; if (oldSig == newSig && !forceInstall) { echo($"Package '{pkgName}' hasn't changed between builds, skipping."); return; } string zipFileUrl = $"https://s3.amazonaws.com/setup.{branch}.com/{buildVersion}-{pkgName}"; string zipExtractPath = Path.Combine(downloads, package.Name); incrementProgressBarMax(package.Size); echo($"Installing package {zipFileUrl}"); var localHttp = new WebClient(); localHttp.Headers.Set("UserAgent", "Roblox"); // Download the zip file package. byte[] fileContents = await localHttp.DownloadDataTaskAsync(zipFileUrl); // If the size of the file we downloaded does not match the packed size specified // in the manifest, then this file has been tampered with. 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)) { // Compute the MD5 signature of this zip file, and make sure it matches with the // signature specified in the package manifest. string checkSig = computeSignature(fileBuffer); if (checkSig != newSig) { throw new InvalidDataException($"{package.Name} expected signature: {newSig} but got: {checkSig}"); } // Write the zip file. File.WriteAllBytes(zipExtractPath, fileContents); } ZipArchive archive = ZipFile.OpenRead(zipExtractPath); string localRootDir = null; var reprocess = new Dictionary <ZipArchiveEntry, string>(); foreach (ZipArchiveEntry entry in archive.Entries) { if (entry.Length > 0) { string newFileSig = null; // If we have figured out what our root directory is, try to resolve // what the signature of this file is. if (localRootDir != null) { var fileSignatures = fileManifest.FileToSignature; string filePath = entry.FullName.Replace('/', '\\'); bool hasFilePath = fileSignatures.ContainsKey(filePath); // If we can't find this file in the signature lookup table, // try appending the local directory to it. This resolves some // edge cases relating to the fixFilePath function above. if (!hasFilePath) { filePath = localRootDir + filePath; hasFilePath = fileSignatures.ContainsKey(filePath); } // If we can find this file path in the file manifest, then we will // use its pre-computed signature to check if the file has changed. newFileSig = hasFilePath ? fileSignatures[filePath] : null; } // If we couldn't pre-determine the file signature from the manifest, // then we have to compute it manually. This is slower. if (newFileSig == null) { newFileSig = computeSignature(entry); } // Now check what files this signature corresponds with. if (fileManifest.SignatureToFiles.ContainsKey(newFileSig)) { // Write this package to each of the files specified. List <string> files = fileManifest.SignatureToFiles[newFileSig]; foreach (string file in files) { // Write the file from this signature. writePackageFile(studioDir, pkgName, file, newFileSig, entry); if (localRootDir == null) { string filePath = fixFilePath(pkgName, file); string entryPath = entry.FullName.Replace('/', '\\'); if (filePath.EndsWith(entryPath)) { // We can infer what the root extraction // directory is for the files in this package! localRootDir = filePath.Replace(entryPath, ""); } } } } else { string file = entry.FullName; if (localRootDir == null) { // Check back on this file after we extract the regular files, // so we can make sure this is extracted to the correct directory. reprocess.Add(entry, newFileSig); } else { // Append the local root directory. file = localRootDir + file; writePackageFile(studioDir, pkgName, file, newFileSig, entry); } } } } // Process any files that we deferred from writing immediately. foreach (ZipArchiveEntry entry in reprocess.Keys) { string file = entry.FullName; string newFileSig = reprocess[entry]; if (localRootDir != null) { file = localRootDir + file; } writePackageFile(studioDir, pkgName, file, newFileSig, entry); } // Update the signature in the package registry so we can check // if this zip file needs to be updated in future versions. pkgRegistry.SetValue(pkgName, package.Signature); }
public async Task RunInstaller(string branch, bool setStartEvent = false) { restore(); setStatus("检查更新中"); echo("检查该构建版本是否可安装..."); string currentBranch = Program.GetString("BuildBranch", "roblox"); string currentVersion = versionRegistry.GetString("VersionGuid"); bool shouldInstall = (forceInstall || currentBranch != branch); string fastVersion = await GetFastVersionGuid(currentBranch); ClientVersionInfo versionInfo = null; if (shouldInstall || fastVersion != currentVersion) { if (currentBranch != "roblox") { echo("发现已有更新,验证中..."); } versionInfo = await GetCurrentVersionInfo(branch, fastVersion); buildVersion = versionInfo.Guid; } else { buildVersion = fastVersion; } if (currentVersion != buildVersion || shouldInstall) { echo("此构建版本需要安装!"); bool studioClosed = await shutdownStudioProcesses(); if (studioClosed) { string binaryType = GetStudioBinaryType(); string studioDir = GetStudioDirectory(); string versionId = versionInfo.Version; restore(); setStatus($"正在安装版本为 {versionId} 的 Roblox Studio..."); var taskQueue = new List <Task>(); writtenFiles = new HashSet <string>(); echo("正在获取版本元信息..."); var pkgManifest = await PackageManifest.Get(branch, buildVersion); echo("正在获取文件元信息..."); fileManifest = await FileManifest.Get(branch, buildVersion); progressBar.Maximum = 5000; progressBar.Value = 0; progressBar.Style = ProgressBarStyle.Continuous; progressBar.Refresh(); foreach (var package in pkgManifest) { Task installer = Task.Run(() => installPackage(branch, package)); taskQueue.Add(installer); } await Task.WhenAll(taskQueue); echo("正在写入 AppSettings.xml..."); string appSettings = Path.Combine(studioDir, "AppSettings.xml"); File.WriteAllText(appSettings, appSettingsXml); setStatus("正在搜索未使用的文件..."); await deleteUnusedFiles(); progressBar.Style = ProgressBarStyle.Marquee; progressBar.Refresh(); Program.SetValue("BuildBranch", branch); versionRegistry.SetValue("Version", versionId); versionRegistry.SetValue("VersionGuid", buildVersion); } else { progressBar.Style = ProgressBarStyle.Marquee; progressBar.Refresh(); echo("更新已取消,正在启动当前已有分支版本..."); } } else { echo("该版本安装成功!"); } setStatus("正在配置 Roblox Studio..."); echo("正在更新注册表..."); Program.UpdateStudioRegistryProtocols(); if (exitWhenClosed) { echo("应用参数配置中..."); FlagEditor.ApplyFlags(); echo("修改浏览窗口图标中..."); await ClassIconEditor.PatchExplorerIcons(); } setStatus("正在启动 Roblox Studio..."); echo("Roblox Studio 已为最新版!"); if (setStartEvent) { SystemEvent start = new SystemEvent("RobloxStudioModManagerStart"); autoExitTask = Task.Run(async() => { bool started = await start.WaitForEvent(); start.Close(); if (started) { await Task.Delay(500); Application.Exit(); } }); StartEvent = start; } }