130 lines
4.3 KiB
C#
130 lines
4.3 KiB
C#
using System.Diagnostics;
|
|
using Newtonsoft.Json;
|
|
using RobloxLegacy.AppData;
|
|
using RobloxLegacy.Utilities;
|
|
using ICSharpCode.SharpZipLib.Zip;
|
|
|
|
namespace RobloxLegacy;
|
|
|
|
public class VersionData
|
|
{
|
|
[JsonProperty("version")]
|
|
public required string Version { get; set; }
|
|
|
|
[JsonProperty("clientVersionUpload")]
|
|
public required string UploadHash { get; set; }
|
|
}
|
|
|
|
public class VersionManager : IDisposable
|
|
{
|
|
private static readonly HttpClient Client = new();
|
|
private const string CdnUrl = "https://setup.rbxcdn.com";
|
|
private const string DllName = "lptch.dll";
|
|
private string? _currentVersion;
|
|
|
|
private readonly string _tempPath = Path.Combine(Path.GetTempPath(), $"RobloxLegacy.{Guid.NewGuid().ToString()}");
|
|
private readonly IAppData _appData;
|
|
|
|
public VersionManager(IAppData appData)
|
|
{
|
|
Directory.CreateDirectory(_tempPath);
|
|
_currentVersion = Registry.GetVersion(appData.PackageName);
|
|
_appData = appData;
|
|
}
|
|
|
|
private static string GetVersionPath(string version)
|
|
{
|
|
var localAppDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
|
return Path.Combine(localAppDataPath, "Roblox", "Versions", version);
|
|
}
|
|
|
|
private async Task<VersionData?> GetLatestVersion()
|
|
{
|
|
var url = $"https://clientsettings.roblox.com/v2/client-version/{_appData.PackageName}/channel/LIVE";
|
|
var response = await Client.GetAsync(url);
|
|
if (!response.IsSuccessStatusCode) // just to be safe
|
|
return null;
|
|
var version = await response.Content.ReadAsAsync<VersionData>();
|
|
return version;
|
|
}
|
|
|
|
private static void ExtractBundle(string version, string folder, string tempFile)
|
|
{
|
|
var fastZip = new FastZip();
|
|
var extractPath = Path.Combine(GetVersionPath(version), folder);
|
|
fastZip.ExtractZip(tempFile, extractPath, null);
|
|
}
|
|
|
|
private void PatchStudio()
|
|
{
|
|
Logger.Info("Patching application...");
|
|
var exePath = Path.Combine(GetVersionPath(_currentVersion), _appData.ExecutableName);
|
|
|
|
// rename imports first
|
|
Patcher.RenameImports(exePath, ["KERNEL32.dll", "MFPlat.DLL"], DllName);
|
|
|
|
// now we can write the dll to the folder
|
|
var dllContents = new Resource($"RobloxLegacy.{DllName}").GetBytes();
|
|
if (dllContents == null)
|
|
throw new Exception("Failed to get dll resource");
|
|
File.WriteAllBytesAsync(Path.Combine(GetVersionPath(_currentVersion), DllName), dllContents);
|
|
}
|
|
|
|
public async Task InstallPackage()
|
|
{
|
|
var version = await GetLatestVersion();
|
|
|
|
if (version == null)
|
|
throw new Exception("No version data found");
|
|
if (version.UploadHash == _currentVersion)
|
|
return;
|
|
|
|
Logger.Info($"Installing {_appData.PackageName} version {version.Version}...");
|
|
foreach (var file in _appData.PackageFiles)
|
|
{
|
|
try
|
|
{
|
|
var fileName = $"{version.UploadHash}-{file.Key}";
|
|
var fileBytes = await Client.GetByteArrayAsync($"{CdnUrl}/{fileName}");
|
|
|
|
var zipPath = Path.Combine(_tempPath, fileName);
|
|
await File.WriteAllBytesAsync(zipPath, fileBytes);
|
|
|
|
ExtractBundle(version.UploadHash, file.Value, zipPath);
|
|
}
|
|
catch (HttpRequestException)
|
|
{
|
|
Logger.Error($"Failed to download {file.Key}");
|
|
}
|
|
}
|
|
|
|
_currentVersion = version.UploadHash;
|
|
Registry.SaveVersion(_appData.PackageName, version.UploadHash);
|
|
|
|
// need to patch the executable
|
|
if (_appData.PackageName == "WindowsStudio64")
|
|
PatchStudio();
|
|
}
|
|
|
|
public void LaunchApp()
|
|
{
|
|
Logger.Info($"Launching {_appData.PackageName}...");
|
|
if(string.IsNullOrEmpty(_currentVersion))
|
|
throw new Exception("No version data found");
|
|
|
|
var startInfo = new ProcessStartInfo()
|
|
{
|
|
FileName = _appData.ExecutableName,
|
|
WorkingDirectory = GetVersionPath(_currentVersion),
|
|
UseShellExecute = true
|
|
};
|
|
|
|
Process.Start(startInfo);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Directory.Delete(_tempPath, true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
} |