VRCMelonAssistant/ModAssistant/Classes/OneClickInstaller.cs

318 lines
11 KiB
C#
Raw Normal View History

2020-02-02 21:11:06 +13:00
using System;
2019-04-30 04:34:41 +12:00
using System.IO;
2019-04-22 18:41:43 +12:00
using System.Linq;
2019-04-30 04:34:41 +12:00
using System.Net;
using System.Windows;
using Microsoft.Win32;
using System.IO.Compression;
using System.Threading.Tasks;
using static ModAssistant.Http;
2019-04-22 18:41:43 +12:00
namespace ModAssistant
{
class OneClickInstaller
{
2019-04-30 04:34:41 +12:00
private const string ModelSaberURLPrefix = "https://modelsaber.com/files/";
private const string BeatSaverURLPrefix = "https://beatsaver.com";
2020-02-02 21:13:23 +13:00
private static readonly string BeatSaberPath = App.BeatSaberInstallDirectory;
2019-04-22 18:41:43 +12:00
private const string CustomAvatarsFolder = "CustomAvatars";
private const string CustomSabersFolder = "CustomSabers";
private const string CustomPlatformsFolder = "CustomPlatforms";
private static readonly string CustomSongsFolder = Path.Combine("Beat Saber_Data", "CustomLevels");
2019-04-22 18:41:43 +12:00
private static readonly string[] Protocols = new[] { "modelsaber", "beatsaver" };
2019-04-22 18:41:43 +12:00
2020-01-24 13:48:01 +13:00
private const bool BypassDownloadCounter = false;
public static async void InstallAsset(string link)
2019-04-22 18:41:43 +12:00
{
Uri uri = new Uri(link);
if (!Protocols.Contains(uri.Scheme)) return;
2019-04-30 04:34:41 +12:00
switch (uri.Scheme)
{
case "modelsaber":
await ModelSaber(uri);
2019-04-30 04:34:41 +12:00
break;
case "beatsaver":
await BeatSaver(uri);
break;
}
}
private static async Task BeatSaver(Uri uri)
{
string Key = uri.Host;
BeatSaverApiResponse Response;
try
{
var resp = await HttpClient.GetAsync(BeatSaverURLPrefix + "/api/maps/detail/" + Key);
var body = await resp.Content.ReadAsStringAsync();
Response = JsonSerializer.Deserialize<BeatSaverApiResponse>(body);
}
catch (Exception e)
{
MessageBox.Show("Could not get map details.\n\n" + e);
return;
}
string zip = Path.Combine(BeatSaberPath, CustomSongsFolder, Response.hash) + ".zip";
string directory = Path.Combine(
BeatSaberPath,
CustomSongsFolder,
2020-02-02 21:42:15 +13:00
string.Concat(
(Response.key + " (" + Response.metadata.songName + " - " + Response.metadata.levelAuthorName + ")")
.Split(Utils.Constants.IllegalCharacters)
)
);
if (BypassDownloadCounter)
{
await DownloadAsset(BeatSaverURLPrefix + Response.directDownload, CustomSongsFolder, Response.hash + ".zip");
}
else
{
await DownloadAsset(BeatSaverURLPrefix + Response.downloadURL, CustomSongsFolder, Response.hash + ".zip");
}
if (File.Exists(zip))
{
using (FileStream stream = new FileStream(zip, FileMode.Open))
using (ZipArchive archive = new ZipArchive(stream))
{
foreach (ZipArchiveEntry file in archive.Entries)
{
string fileDirectory = Path.GetDirectoryName(Path.Combine(directory, file.FullName));
if (!Directory.Exists(fileDirectory))
{
Directory.CreateDirectory(fileDirectory);
2020-02-03 00:04:30 +13:00
}
if (!string.IsNullOrEmpty(file.Name))
{
file.ExtractToFile(Path.Combine(directory, file.FullName), true);
}
}
}
File.Delete(zip);
}
else
{
MessageBox.Show("Could not download the song.\nThere might be issues with BeatSaver or your internet connection.", "Failed to download song ZIP");
}
}
private static async Task ModelSaber(Uri uri)
2019-04-30 04:34:41 +12:00
{
switch (uri.Host)
{
case "avatar":
await DownloadAsset(ModelSaberURLPrefix + uri.Host + uri.AbsolutePath, CustomAvatarsFolder);
2019-04-30 04:34:41 +12:00
break;
case "saber":
await DownloadAsset(ModelSaberURLPrefix + uri.Host + uri.AbsolutePath, CustomSabersFolder);
2019-04-30 04:34:41 +12:00
break;
case "platform":
await DownloadAsset(ModelSaberURLPrefix + uri.Host + uri.AbsolutePath, CustomPlatformsFolder);
2019-04-30 04:34:41 +12:00
break;
}
2019-04-22 18:41:43 +12:00
}
private static async Task DownloadAsset(string link, string folder, string fileName = null)
2019-04-22 18:41:43 +12:00
{
if (string.IsNullOrEmpty(BeatSaberPath))
{
Utils.SendNotify("Beat Saber installation path not found.");
}
2019-04-30 04:34:41 +12:00
try
{
Directory.CreateDirectory(Path.Combine(BeatSaberPath, folder));
2020-02-02 21:42:15 +13:00
if (string.IsNullOrEmpty(fileName))
{
fileName = WebUtility.UrlDecode(Path.Combine(BeatSaberPath, folder, new Uri(link).Segments.Last()));
}
else
{
fileName = WebUtility.UrlDecode(Path.Combine(BeatSaberPath, folder, fileName));
}
await Utils.Download(link, fileName);
2019-04-30 04:34:41 +12:00
Utils.SendNotify("Installed: " + Path.GetFileNameWithoutExtension(fileName));
}
catch
{
Utils.SendNotify("Failed to install.");
}
}
2019-04-22 18:41:43 +12:00
public static void Register(string Protocol, bool Background = false)
{
if (IsRegistered(Protocol) == true)
return;
try
{
if (Utils.IsAdmin)
{
RegistryKey ProtocolKey = Registry.ClassesRoot.OpenSubKey(Protocol, true);
if (ProtocolKey == null)
ProtocolKey = Registry.ClassesRoot.CreateSubKey(Protocol, true);
RegistryKey CommandKey = ProtocolKey.CreateSubKey(@"shell\open\command", true);
if (CommandKey == null)
CommandKey = Registry.ClassesRoot.CreateSubKey(@"shell\open\command", true);
if (ProtocolKey.GetValue("OneClick-Provider", "").ToString() != "ModAssistant")
{
ProtocolKey.SetValue("URL Protocol", "", RegistryValueKind.String);
ProtocolKey.SetValue("OneClick-Provider", "ModAssistant", RegistryValueKind.String);
CommandKey.SetValue("", $"\"{Utils.ExePath}\" \"--install\" \"%1\"");
}
Utils.SendNotify($"{Protocol} One Click Install handlers registered!");
}
else
{
Utils.StartAsAdmin($"\"--register\" \"{Protocol}\"");
}
2020-02-03 00:04:30 +13:00
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
if (Background)
Application.Current.Shutdown();
else
Pages.Options.Instance.UpdateHandlerStatus();
}
public static void Unregister(string Protocol, bool Background = false)
2019-05-05 04:08:54 +12:00
{
if (IsRegistered(Protocol) == false)
return;
try
2019-05-05 04:08:54 +12:00
{
if (Utils.IsAdmin)
{
using (RegistryKey ProtocolKey = Registry.ClassesRoot.OpenSubKey(Protocol, true))
{
if (ProtocolKey != null
&& ProtocolKey.GetValue("OneClick-Provider", "").ToString() == "ModAssistant")
{
Registry.ClassesRoot.DeleteSubKeyTree(Protocol);
}
}
Utils.SendNotify($"{Protocol} One Click Install handlers unregistered!");
}
else
{
Utils.StartAsAdmin($"\"--unregister\" \"{Protocol}\"");
}
2019-05-05 04:08:54 +12:00
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
if (Background)
Application.Current.Shutdown();
else
Pages.Options.Instance.UpdateHandlerStatus();
2019-05-05 04:08:54 +12:00
}
public static bool IsRegistered(string Protocol)
2019-05-05 04:08:54 +12:00
{
RegistryKey ProtocolKey = Registry.ClassesRoot.OpenSubKey(Protocol);
if (ProtocolKey != null
&& ProtocolKey.GetValue("OneClick-Provider", "").ToString() == "ModAssistant")
return true;
else
return false;
2019-05-05 04:08:54 +12:00
}
2019-04-22 18:41:43 +12:00
}
2020-02-02 21:11:06 +13:00
#pragma warning disable IDE1006 // Naming Styles
class BeatSaverApiResponse
{
public Metadata metadata { get; set; }
public Stats stats { get; set; }
public string description { get; set; }
public DateTime? deletedAt { get; set; }
public string _id { get; set; }
public string key { get; set; }
public string name { get; set; }
public Uploader uploader { get; set; }
public DateTime uploaded { get; set; }
public string hash { get; set; }
public string directDownload { get; set; }
public string downloadURL { get; set; }
public string coverURL { get; set; }
public class Difficulties
{
public bool easy { get; set; }
public bool normal { get; set; }
public bool hard { get; set; }
public bool expert { get; set; }
public bool expertPlus { get; set; }
}
public class Metadata
{
public Difficulties difficulties { get; set; }
public Characteristic[] characteristics { get; set; }
public string songName { get; set; }
public string songSubName { get; set; }
public string songAuthorName { get; set; }
public string levelAuthorName { get; set; }
2019-06-28 01:48:03 +12:00
public double bpm { get; set; }
}
2020-02-03 00:04:30 +13:00
public class Characteristic
{
public string name { get; set; }
public CharacteristicDifficulties difficulties { get; set; }
}
2020-02-03 00:04:30 +13:00
public class CharacteristicDifficulties
{
public Difficulty easy { get; set; }
public Difficulty normal { get; set; }
public Difficulty hard { get; set; }
public Difficulty expert { get; set; }
public Difficulty expertPlus { get; set; }
}
2020-02-03 00:04:30 +13:00
public class Difficulty
{
2019-07-15 13:46:52 +12:00
public double? duration { get; set; }
public double? length { get; set; }
public double bombs { get; set; }
public double notes { get; set; }
public double obstacles { get; set; }
public double njs { get; set; }
}
public class Stats
{
public int downloads { get; set; }
public int plays { get; set; }
public int downVotes { get; set; }
public int upVotes { get; set; }
public double heat { get; set; }
public double rating { get; set; }
}
public class Uploader
{
public string _id { get; set; }
public string username { get; set; }
}
}
2019-04-22 18:41:43 +12:00
}
2020-02-02 21:11:06 +13:00
#pragma warning restore IDE1006 // Naming Styles