bloxstrap/Bloxstrap/Bootstrapper/Bootstrapper.RobloxInstall.cs
pizzaboxer bacb650ddc Features and bugfixes for v1.1.0
- Features
    - Add Discord Rich Presence support (the nuget package is like a year and a half out of date so submodule it is lol)
    - Add update checker
    - Add start menu folder creation

- Bugfixes
   - Fix "Directory is not empty" error when updating Roblox
   - Fix uninstalling sometimes not working properly

- Quality of Life
   - Split Bootstrapper class into partial files
   - Renamed TaskDialogStyle to VistaDialog for name simplification
2022-08-11 08:26:28 +01:00

209 lines
7.6 KiB
C#

using System.Diagnostics;
using System.IO.Compression;
using Bloxstrap.Helpers;
using Bloxstrap.Helpers.RSMM;
namespace Bloxstrap
{
partial class Bootstrapper
{
private async Task CheckLatestVersion()
{
if (Program.BaseDirectory is null)
return;
Message = "Connecting to Roblox...";
VersionGuid = await Client.GetStringAsync($"{Program.BaseUrlSetup}/version");
VersionFolder = Path.Combine(Program.BaseDirectory, "Versions", VersionGuid);
VersionPackageManifest = await PackageManifest.Get(VersionGuid);
VersionFileManifest = await FileManifest.Get(VersionGuid);
}
private async Task InstallLatestVersion()
{
if (Program.BaseDirectory is null)
return;
CheckIfRunning();
if (FreshInstall)
Message = "Installing Roblox...";
else
Message = "Upgrading Roblox...";
Directory.CreateDirectory(Program.BaseDirectory);
CancelEnabled = true;
// i believe the original bootstrapper bases the progress bar off zip
// extraction progress, but here i'm doing package download progress
ProgressStyle = ProgressBarStyle.Continuous;
ProgressIncrement = (int)Math.Floor((decimal)1 / VersionPackageManifest.Count * 100);
Directory.CreateDirectory(Path.Combine(Program.BaseDirectory, "Downloads"));
foreach (Package package in VersionPackageManifest)
{
// no await, download all the packages at once
DownloadPackage(package);
}
do
{
// wait for download to finish (and also round off the progress bar if needed)
if (Progress == ProgressIncrement * VersionPackageManifest.Count)
Progress = 100;
await Task.Delay(1000);
}
while (Progress != 100);
ProgressStyle = ProgressBarStyle.Marquee;
Debug.WriteLine("Finished downloading");
Directory.CreateDirectory(Path.Combine(Program.BaseDirectory, "Versions"));
foreach (Package package in VersionPackageManifest)
{
// extract all the packages at once (shouldn't be too heavy on cpu?)
ExtractPackage(package);
}
Debug.WriteLine("Finished extracting packages");
Message = "Configuring Roblox...";
string appSettingsLocation = Path.Combine(VersionFolder, "AppSettings.xml");
await File.WriteAllTextAsync(appSettingsLocation, AppSettings);
if (!FreshInstall)
{
// let's take this opportunity to delete any packages we don't need anymore
foreach (string filename in Directory.GetFiles(DownloadsFolder))
{
if (!VersionPackageManifest.Exists(package => filename.Contains(package.Signature)))
File.Delete(filename);
}
// and also to delete our old version folder
Directory.Delete(Path.Combine(Program.BaseDirectory, "Versions", Program.Settings.VersionGuid), true);
}
CancelEnabled = false;
Program.Settings.VersionGuid = VersionGuid;
}
private async void ApplyModifications()
{
// i guess we can just assume that if the hash does not match the manifest, then it's a mod
// probably not the best way to do this? don't think file corruption is that much of a worry here
// TODO - i'm thinking i could have a manifest on my website like rbxManifest.txt
// for integrity checking and to quickly fix/alter stuff (like ouch.ogg being renamed)
// but that probably wouldn't be great to check on every run in case my webserver ever goes down
// interesting idea nonetheless, might add it sometime
// TODO - i'm hoping i can take this idea of content mods much further
// for stuff like easily installing (community-created?) texture/shader/audio mods
// but for now, let's just keep it at this
await ModifyDeathSound();
}
private async void DownloadPackage(Package package)
{
string packageUrl = $"{Program.BaseUrlSetup}/{VersionGuid}-{package.Name}";
string packageLocation = Path.Combine(DownloadsFolder, package.Signature);
string robloxPackageLocation = Path.Combine(Program.LocalAppData, "Roblox", "Downloads", package.Signature);
if (File.Exists(packageLocation))
{
FileInfo file = new(packageLocation);
string calculatedMD5 = Utilities.CalculateMD5(packageLocation);
if (calculatedMD5 != package.Signature)
{
Debug.WriteLine($"{package.Name} is corrupted ({calculatedMD5} != {package.Signature})! Deleting and re-downloading...");
file.Delete();
}
else
{
Debug.WriteLine($"{package.Name} is already downloaded, skipping...");
Progress += ProgressIncrement;
return;
}
}
else if (File.Exists(robloxPackageLocation))
{
// let's cheat! if the stock bootstrapper already previously downloaded the file,
// then we can just copy the one from there
Debug.WriteLine($"Found existing version of {package.Name} ({robloxPackageLocation})! Copying to Downloads folder...");
File.Copy(robloxPackageLocation, packageLocation);
Progress += ProgressIncrement;
return;
}
if (!File.Exists(packageLocation))
{
Debug.WriteLine($"Downloading {package.Name}...");
var response = await Client.GetAsync(packageUrl);
if (CancelFired)
return;
using (var fileStream = new FileStream(packageLocation, FileMode.CreateNew))
{
await response.Content.CopyToAsync(fileStream);
}
Debug.WriteLine($"Finished downloading {package.Name}!");
Progress += ProgressIncrement;
}
}
private void ExtractPackage(Package package)
{
if (CancelFired)
return;
string packageLocation = Path.Combine(DownloadsFolder, package.Signature);
string packageFolder = Path.Combine(VersionFolder, PackageDirectories[package.Name]);
string extractPath;
Debug.WriteLine($"Extracting {package.Name} to {packageFolder}...");
using (ZipArchive archive = ZipFile.OpenRead(packageLocation))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (CancelFired)
return;
if (entry.FullName.EndsWith(@"\"))
continue;
extractPath = Path.Combine(packageFolder, entry.FullName);
Debug.WriteLine($"[{package.Name}] Writing {extractPath}...");
Directory.CreateDirectory(Path.GetDirectoryName(extractPath));
if (File.Exists(extractPath))
File.Delete(extractPath);
entry.ExtractToFile(extractPath);
}
}
}
}
}