diff --git a/ModAssistant/Classes/External Interfaces/BeatSaver.cs b/ModAssistant/Classes/External Interfaces/BeatSaver.cs index 4842a77..d501ceb 100644 --- a/ModAssistant/Classes/External Interfaces/BeatSaver.cs +++ b/ModAssistant/Classes/External Interfaces/BeatSaver.cs @@ -1,6 +1,8 @@ using System; using System.IO; using System.IO.Compression; +using System.Net; +using System.Net.Http.Headers; using System.Threading.Tasks; using System.Windows; using static ModAssistant.Http; @@ -13,49 +15,124 @@ namespace ModAssistant.API private static readonly string CustomSongsFolder = Path.Combine("Beat Saber_Data", "CustomLevels"); private const bool BypassDownloadCounter = false; - public static async Task GetFromKey(string Key) + public static async Task GetFromKey(string Key, bool showNotification = true) { - BeatSaverApiResponse Map = await GetResponse(BeatSaverURLPrefix + "/api/maps/detail/" + Key); - await InstallMap(Map); + return await GetMap(Key, "key", showNotification); } - public static async Task GetFromHash(string Hash) + public static async Task GetFromHash(string Hash, bool showNotification = true) { - BeatSaverApiResponse Map = await GetResponse(BeatSaverURLPrefix + "/api/maps/by-hash/" + Hash); - await InstallMap(Map); + return await GetMap(Hash, "hash", showNotification); } - private static async Task GetResponse(string url) + private static async Task GetMap(string id, string type, bool showNotification) { + string urlSegment; + switch (type) + { + case "hash": + urlSegment = "/api/maps/by-hash/"; + break; + case "key": + urlSegment = "/api/maps/detail/"; + break; + default: + return null; + } + + BeatSaverMap map = new BeatSaverMap(); + map.Success = false; try { - var resp = await HttpClient.GetAsync(url); - var body = await resp.Content.ReadAsStringAsync(); - - return JsonSerializer.Deserialize(body); + BeatSaverApiResponse beatsaver = await GetResponse(BeatSaverURLPrefix + urlSegment + id); + map.Name = await InstallMap(beatsaver.map, showNotification); + map.Success = true; } catch (Exception e) { - MessageBox.Show($"{Application.Current.FindResource("OneClick:MapDownloadFailed")}\n\n" + e); + ModAssistant.Utils.Log($"Failed downloading BeatSaver map: {id} | Error: {e}", "ERROR"); + } + return map; + } + + private static async Task GetResponse(string url, bool showNotification = true) + { + BeatSaverApiResponse response = new BeatSaverApiResponse(); + try + { + var resp = await HttpClient.GetAsync(url); + response.statusCode = resp.StatusCode; + response.ratelimit = GetRatelimit(resp.Headers); + if (response.statusCode == HttpStatusCode.OK) + { + if (response.ratelimit.IsSafe) + { + string body = await resp.Content.ReadAsStringAsync(); + response.map = JsonSerializer.Deserialize(body); + return response; + } + else + { + return response; + } + } + else + { + return response; + } + } + catch (Exception e) + { + if (showNotification) + { + MessageBox.Show($"{Application.Current.FindResource("OneClick:MapDownloadFailed")}\n\n" + e); + } return null; } } - public static async Task InstallMap(BeatSaverApiResponse Map) + private static BeatSaverRatelimit GetRatelimit(HttpResponseHeaders headers) + { + BeatSaverRatelimit ratelimit = new BeatSaverRatelimit(); + + var _rateLimitRemaining = headers.GetValues("Rate-Limit-Remaining").GetEnumerator(); + var _rateLimitReset = headers.GetValues("Rate-Limit-Reset").GetEnumerator(); + var _rateLimitTotal = headers.GetValues("Rate-Limit-Total").GetEnumerator(); + _rateLimitRemaining.MoveNext(); + _rateLimitReset.MoveNext(); + _rateLimitTotal.MoveNext(); + ratelimit.Remaining = Int32.Parse(_rateLimitRemaining.Current); + ratelimit.Reset = Int32.Parse(_rateLimitReset.Current); + ratelimit.Total = Int32.Parse(_rateLimitTotal.Current); + ratelimit.ResetTime = UnixTimestampToDateTime((long)ratelimit.Reset); + + return ratelimit; + } + + public static DateTime UnixTimestampToDateTime(double unixTime) + { + DateTime unixStart = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc); + long unixTimeStampInTicks = (long)(unixTime * TimeSpan.TicksPerSecond); + return new DateTime(unixStart.Ticks + unixTimeStampInTicks, System.DateTimeKind.Utc); + } + + public static async Task InstallMap(BeatSaverApiResponseMap Map, bool showNotification = true) { string zip = Path.Combine(Utils.BeatSaberPath, CustomSongsFolder, Map.hash) + ".zip"; string mapName = string.Concat(($"{Map.key} ({Map.metadata.songName} - {Map.metadata.levelAuthorName})") .Split(ModAssistant.Utils.Constants.IllegalCharacters)); string directory = Path.Combine(Utils.BeatSaberPath, CustomSongsFolder, mapName); +#pragma warning disable CS0162 // Unreachable code detected if (BypassDownloadCounter) { - await Utils.DownloadAsset(BeatSaverURLPrefix + Map.directDownload, CustomSongsFolder, Map.hash + ".zip", mapName); + await Utils.DownloadAsset(BeatSaverURLPrefix + Map.directDownload, CustomSongsFolder, Map.hash + ".zip", mapName, showNotification); } else { - await Utils.DownloadAsset(BeatSaverURLPrefix + Map.downloadURL, CustomSongsFolder, Map.hash + ".zip", mapName); + await Utils.DownloadAsset(BeatSaverURLPrefix + Map.downloadURL, CustomSongsFolder, Map.hash + ".zip", mapName, showNotification); } +#pragma warning restore CS0162 // Unreachable code detected if (File.Exists(zip)) { @@ -81,14 +158,54 @@ namespace ModAssistant.API } else { - string line1 = (string)Application.Current.FindResource("OneClick:SongDownload:Failed"); - string line2 = (string)Application.Current.FindResource("OneClick:SongDownload:NetworkIssues"); - string title = (string)Application.Current.FindResource("OneClick:SongDownload:FailedTitle"); - MessageBox.Show($"{line1}\n{line2}", title); + if (showNotification) + { + string line1 = (string)Application.Current.FindResource("OneClick:SongDownload:Failed"); + string line2 = (string)Application.Current.FindResource("OneClick:SongDownload:NetworkIssues"); + string title = (string)Application.Current.FindResource("OneClick:SongDownload:FailedTitle"); + MessageBox.Show($"{line1}\n{line2}", title); + } + return null; } + return mapName; + } + + public class BeatSaverMap + { + public BeatSaverApiResponse response { get; set; } + public bool Success { get; set; } + public string Name { get; set; } } public class BeatSaverApiResponse + { + public HttpStatusCode statusCode { get; set; } + public BeatSaverRatelimit ratelimit { get; set;} + public BeatSaverApiResponseMap map { get; set; } + } + + public class BeatSaverRatelimit + { + public int Remaining { get; set; } + public int Total { get; set; } + public int Reset { get; set; } + public DateTime ResetTime { get; set; } + public bool IsSafe + { + get + { + if (Remaining > 3) return true; + else return false; + } + } + + public async Task Wait() + { + await Task.Delay(new TimeSpan(ResetTime.Ticks - DateTime.Now.Ticks)); + } + } + + public class BeatSaverApiResponseMap { public Metadata metadata { get; set; } public Stats stats { get; set; } diff --git a/ModAssistant/Classes/External Interfaces/Playlists.cs b/ModAssistant/Classes/External Interfaces/Playlists.cs new file mode 100644 index 0000000..17f74a7 --- /dev/null +++ b/ModAssistant/Classes/External Interfaces/Playlists.cs @@ -0,0 +1,126 @@ +using System; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using static ModAssistant.Http; +using System.Windows; + +namespace ModAssistant.API +{ + public class Playlists + { + private const string BSaberURLPrefix = "https://bsaber.com/PlaylistAPI/"; + private const string PlaylistsFolder = "Playlists"; + private static readonly string BeatSaberPath = Utils.BeatSaberPath; + + public static async Task DownloadAll(Uri uri) + { + switch (uri.Host) + { + case "playlist": + Uri url = new Uri($"{uri.LocalPath.Trim('/')}"); + string filename = await Get(url); + await DownloadFrom(filename); + break; + + } + } + + public static async Task Get(Uri url) + { + string filename = url.Segments.Last(); + string absolutePath = Path.Combine(BeatSaberPath, PlaylistsFolder, filename); + try + { + await Utils.DownloadAsset(url.ToString(), PlaylistsFolder, filename); + return absolutePath; + } + catch + { + return null; + } + } + + public static async Task DownloadFrom(string file, bool gui = false, System.Windows.Controls.ProgressBar progress = null) + { + if (Path.Combine(BeatSaberPath, PlaylistsFolder) != Path.GetDirectoryName(file)) + { + string destination = Path.Combine(BeatSaberPath, PlaylistsFolder, Path.GetFileName(file)); + File.Copy(file, destination, true); + } + + int Errors = 0; + int Minimum = 0; + int Value = 0; + if (progress != null) + { + progress.Minimum = 0; + progress.Maximum = 1; + progress.Value = 0; + } + Playlist playlist = JsonSerializer.Deserialize(File.ReadAllText(file)); + int Maximum = playlist.songs.Length; + if (progress != null) progress.Maximum = playlist.songs.Length; + + foreach (Playlist.Song song in playlist.songs) + { + API.BeatSaver.BeatSaverMap response = new BeatSaver.BeatSaverMap(); + if (!string.IsNullOrEmpty(song.hash)) + { + response = await BeatSaver.GetFromHash(song.hash, false); + } + else if (!string.IsNullOrEmpty(song.key)) + { + response = await BeatSaver.GetFromKey(song.key, false); + } + Value++; + if (progress != null) progress.Value++; + + if (gui) + { + if (response.Success) + { + MainWindow.Instance.MainText = $"{string.Format((string)Application.Current.FindResource("Options:InstallingPlaylist"), TextProgress(Minimum, Maximum, Value))}"; + } + else + { + MainWindow.Instance.MainText = $"{string.Format((string)Application.Current.FindResource("Options:FailedPlaylistSong"), song.songName)}"; + ModAssistant.Utils.Log($"Failed installing BeatSaver map: {song.songName} | {song.key} | {song.hash}"); + await Task.Delay(3 * 1000); + Errors++; + } + } + } + if (gui) + { + MainWindow.Instance.MainText = $"{string.Format((string)Application.Current.FindResource("Options:FinishedPlaylist"), Errors, playlist.playlistTitle)}"; + } + } + + private static string TextProgress(int min, int max, int value) + { + if (max == value) + { + return $" {string.Concat(Enumerable.Repeat("▒", 10))} [{value}/{max}]"; + } + int interval = (int)Math.Floor((double)value / ( ((double)max - (double)min ) / (double)10)); + return $" {string.Concat(Enumerable.Repeat("▒", interval))}{string.Concat(Enumerable.Repeat("░", 10 - interval))} [{value}/{max}]"; + } + + class Playlist + { + public string playlistTitle { get; set; } + public string playlistAuthor { get; set; } + public string image { get; set; } + public Song[] songs { get; set; } + + public class Song + { + public string key { get; set; } + public string hash { get; set; } + public string songName { get; set; } + public string uploader { get; set; } + } + } + } +} diff --git a/ModAssistant/Classes/External Interfaces/Utils.cs b/ModAssistant/Classes/External Interfaces/Utils.cs index 282481b..4588a47 100644 --- a/ModAssistant/Classes/External Interfaces/Utils.cs +++ b/ModAssistant/Classes/External Interfaces/Utils.cs @@ -11,12 +11,18 @@ namespace ModAssistant.API { public static readonly string BeatSaberPath = App.BeatSaberInstallDirectory; + public static async Task DownloadAsset(string link, string folder, bool showNotifcation, string fileName = null) + { + await DownloadAsset(link, folder, fileName, null, showNotifcation); + } + public static async Task DownloadAsset(string link, string folder, string fileName = null, string displayName = null) { - if (string.IsNullOrEmpty(displayName)) - { - displayName = Path.GetFileNameWithoutExtension(fileName); - } + await DownloadAsset(link, folder, fileName, displayName, true); + } + + public static async Task DownloadAsset(string link, string folder, string fileName, string displayName, bool showNotification) + { if (string.IsNullOrEmpty(BeatSaberPath)) { ModAssistant.Utils.SendNotify((string)Application.Current.FindResource("OneClick:InstallDirNotFound")); @@ -32,9 +38,16 @@ namespace ModAssistant.API { fileName = WebUtility.UrlDecode(Path.Combine(BeatSaberPath, folder, fileName)); } + if (string.IsNullOrEmpty(displayName)) + { + displayName = Path.GetFileNameWithoutExtension(fileName); + } await ModAssistant.Utils.Download(link, fileName); - ModAssistant.Utils.SendNotify(string.Format((string)Application.Current.FindResource("OneClick:InstalledAsset"), displayName)); + if (showNotification) + { + ModAssistant.Utils.SendNotify(string.Format((string)Application.Current.FindResource("OneClick:InstalledAsset"), displayName)); + } } catch { diff --git a/ModAssistant/Classes/OneClickInstaller.cs b/ModAssistant/Classes/OneClickInstaller.cs index 9c34a6b..9d0206d 100644 --- a/ModAssistant/Classes/OneClickInstaller.cs +++ b/ModAssistant/Classes/OneClickInstaller.cs @@ -8,7 +8,7 @@ namespace ModAssistant { class OneClickInstaller { - private static readonly string[] Protocols = new[] { "modelsaber", "beatsaver" }; + private static readonly string[] Protocols = new[] { "modelsaber", "beatsaver", "bsplaylist" }; public static async Task InstallAsset(string link) { @@ -23,6 +23,9 @@ namespace ModAssistant case "beatsaver": await BeatSaver(uri); break; + case "bsplaylist": + await Playlist(uri); + break; } } @@ -37,6 +40,11 @@ namespace ModAssistant await API.ModelSaber.GetModel(uri); } + private static async Task Playlist(Uri uri) + { + await API.Playlists.DownloadAll(uri); + } + public static void Register(string Protocol, bool Background = false) { if (IsRegistered(Protocol) == true) diff --git a/ModAssistant/Classes/Utils.cs b/ModAssistant/Classes/Utils.cs index 0c40a9d..4f1c425 100644 --- a/ModAssistant/Classes/Utils.cs +++ b/ModAssistant/Classes/Utils.cs @@ -1,6 +1,7 @@ using Microsoft.Win32; using System; using System.Collections.Generic; +using System.Configuration; using System.Diagnostics; using System.IO; using System.Linq; @@ -309,7 +310,7 @@ namespace ModAssistant public static string GetManualDir() { - var dialog = new Microsoft.Win32.SaveFileDialog() + var dialog = new SaveFileDialog() { Title = (string)Application.Current.FindResource("Utils:InstallDir:DialogTitle"), Filter = "Directory|*.this.directory", @@ -339,6 +340,22 @@ namespace ModAssistant return null; } + public static string GetManualFile(string filter = "", string title = "Open File") + { + var dialog = new OpenFileDialog() + { + Title = title, + Filter = filter, + Multiselect = false, + }; + + if (dialog.ShowDialog() == true) + { + return dialog.FileName; + } + return null; + } + public static bool IsVoid() { string directory = App.BeatSaberInstallDirectory; @@ -387,6 +404,13 @@ namespace ModAssistant MessageBox.Show($"{string.Format((string)Application.Current.FindResource("Utils:CannotOpenFolder"), location)}."); } + public static void Log(string message, string severity = "LOG") + { + string path = Path.GetDirectoryName(ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal).FilePath); + string logFile = $"{path}{Path.DirectorySeparatorChar}log.log"; + File.AppendAllText(logFile, $"[{DateTime.UtcNow.ToString("yyyy-mm-dd HH:mm:ss.ffffff")}][{severity.ToUpper()}] {message}\n"); + } + public static async Task Download(string link, string output) { var resp = await HttpClient.GetAsync(link); diff --git a/ModAssistant/Localisation/de.xaml b/ModAssistant/Localisation/de.xaml new file mode 100644 index 0000000..7eec5ac --- /dev/null +++ b/ModAssistant/Localisation/de.xaml @@ -0,0 +1,238 @@ + + i18n:en-US + + + Der Beat Saber Installationsordner konnte nicht gefunden werden! + Drücke OK um es erneut zu versuchen, oder Abbrechen um das Programm zu beenden. + Ungültiges Argument! '{0}' benötigt eine Option. + Unbekanntes Argument. Beende Mod Assistant. + Eine nicht behandelte Ausnahme ist aufgetreten + Ausnahme + + + ModAssistant + Intro + Mods + Über + Optionen + Spiel Version + Version + Mod Info + Installieren/ + Aktualisieren + Spielversion konnte nicht geladen werden, der Mods Tab wird nicht verfügbar sein. + Neue Spielversion gefunden! + Es scheint ein Spiel Update gegeben zu haben. + Bitte prüfe ob unten links die richtige Version ausgewählt ist! + Kein Mod ausgewählt! + {0} hat keine Informationsseite. + + + Intro + Willkommen bei Mod Assistant + Bitte lies diese Seite vollständig und aufmerksam! + + Durch Nutzung des Programms wird bestätigt, dass folgende Bedinungen gelesen und akzeptiert wurden: + + + Beat Saber + unterstützt normalerweise keine Mods. Das heißt: + + + Mods + werden nach jedem Update nicht mehr funktionieren. Dies ist normal, und + die Schuld liegt nicht bei Beat Games. + + + Mods + werden Fehler und Leistungsprobleme verursachen. Die Schuld + liegt nicht bei Beat Games. + + + Mods werden + kostenlos von Leuten in deren + Freizeit erstellt. Bitte sei geduldig und verständnissvoll. + + + Bitte gib KEINE schlechten Bewertungen weil die Mods nicht funktionieren. Die Schuld + liegt nicht bei Beat Games. + Sie versuchen nicht die Mods zu unterbinden. + + + Wenn ich weiterhin schlecht Bewertungen + wegen nicht funktionierenden Mods sehe, + + Werde ich persönlich die Mods mit einem rostigen Löffel töten + + + Bitte lies den Einsteiger Leitfaden im + + Wiki + . + + Annehmen + Ablehnen + Programm wird beendet: Du hast den Bedingunen nicht zugestimmt. + Versionsliste konnte nicht geladen werden + Mods Tab deaktiviert. Bitte Programm neu starten um es nochmal zu versuchen. + Du kannst jetzt den Mods Tab benutzen! + + + Mods + Name + Installiert + Neuste + Beschreibung + Entfernen + Entfernen + Modliste konnte nicht geladen werden + Prüfe installierte Mods + Lade Mods + Laden der Mods abgeschlossen + Installiere {0} + {0} installiert + Mod Installation abgeschlossen + Downloadlink für {0} konnte nicht gefunden werden + {0} entfernen? + Bist du dir sicher das du {0} entfernen möchtest? + Dies kann die anderen Mods unbrauchbar machen + Fehler beim Extrahieren von {0}, neuer Versuch in {1} Sekunden. ({2}/{3}) + Fehler beim Extrahieren von {0} nach {1} Versuchen, wird übersprungen. Dieser Mod funktioniert möglicherweise nicht richtig, also gehe auf eigenes Risiko vor + Search... + + + Über + Über Mod Assistant + Ich bin Assistant und ich habe Mod Assistant als Assistent für Mods mit ein paar Prinzipen im Auge gemacht: + Einfachheit + Portabilität + Nur eine Datei + Verantwortungsbewusster Umgang + + Wenn dir das Programm gefällt und du mich unterstützen möchtest, dann besuche meine + + Spendenseite + + oder mein + + Patreon + + + Besonder Dank ♥ + Spenden + Kopftätscheln + Umarmungen + + + Optionen + Einstellungen + Installations Ordner + Ordner wählen + Order öffnen + Ausgewählte Mods speichern + Installierte Mods prüfen + Installierte Mods auswählen + Installierte Mods neu installieren + OneClick™ Installation aktivieren + BeatSaver + ModelSaber + Spiel Typ + Steam + Oculus + Tools + Install Playlist + Installing Playlist: {0} + Failed song: {0} + [{0} fails] Finished Installing Playlist: {1} + Diagnose + Log öffnen + AppData öffnen + BSIPA entfernen + Mods entfernen + Design + Exportieren + Log wird hochgeladen + Log URL in die Zwischenablage kopiert! + Log Hochladen fehlgeschlagen! + Log Hochladen fehlgeschlagen! + Log Datei konnte nicht zu Teknik hochgeladen werden, bitte nochmal versuchen oder die Datei manuell senden. + Lade Liste der Mods + Suche BSIPA Version + BSIPA entfernt + Alle Mods entfernen? + Bist du dir sicher das du ALLE Mods entfernen möchtest? + Dies kann nicht rückgängig gemacht werden. + Alle Mods entfernt + Aktuelles Design wurde entfernt, gehe zurück zum Standart... + Designs Ordner nicht gefunden! Versuche die Vorlage zu exportieren... + AppData Ordner nicht gefunden! Versuche dein Spiel zu starten. + + + Lade Mods + + + Ungültig + Ungültige Installation erkannt + Die SPielinstallation ist beschädigt oder anderwärtig ungültig + Dies kann passieren wenn dein Spiel eine Raubkopie ist oder eine Raubkopie über eine legitime Version kopiert wurde + + Falls dein Spiel eine Raubkopie ist, + bitte kaufe das Spiel + + HIER + + . + + + Wenn dein Spiel + keine Raubkopie ist, bitte + + mach eine saubere Neuinstallation + . + + + Falls das nicht hilft, frage im + #support Kanal in der + + BSMG + . + + Falls du eine Raubkopie hattest aber das Spiel jetzt gekauft hast + Select Folder + Muss Mod Assisstant neu gestartet werden wenn eine legitime Version installiert wurde + + + Map Details konnten nicht geladen werden. + Titel konnte nicht geladen werden. + Titel konnte nicht geladen werden. + Möglicherweise gibt es Probleme mit BeatSaver oder deiner Internetverbindung. + Herunterladen der Titel ZIP fehlgeschlagen + Beat Saber Installations Pfad nicht gefunden. + Installiert: {0} + Installation fehlgeschlagen. + {0} OneClick™ Installion Handler registriert! + {0} OneClick™ Installion Handler entfernt! + + + Design nicht gefunden, gehe zurück zum Standart Design... + Design gesetzt auf: {0}. + {0} existiert nicht. + Designvorlage "{0}" in Design Ordner gespeichert. + Designvorlage existiert bereits! + Fehler beim Laden der .xaml Datei von Design {0}: {1} + + + Konnte nicht auf Aktualisierungen prüfen. + Konnte Aktualisierung nicht herunterladen. + + + Mod Assistant + Beat Saber Installationsordner konnte nicht erkannt werden. Bitte manuell auswählen. + Mod Assistant muss diese Aufgabe mit Administrator Rechten ausführen. Bitte nochmal versuchen. + Wähle den Beat Saber Installationsordner aus + Ordner konnte nicht geöffnet werden: {0} + diff --git a/ModAssistant/Localisation/en-DEBUG.xaml b/ModAssistant/Localisation/en-DEBUG.xaml index d9dc848..1cc0be3 100644 --- a/ModAssistant/Localisation/en-DEBUG.xaml +++ b/ModAssistant/Localisation/en-DEBUG.xaml @@ -71,6 +71,7 @@ Mods:UninstallBox:Body2 {0} {1} {2} {3} Mods:FailedExtract {0} {1} Mods:FailedExtractMaxReached + Mods:SearchLabel About:Title @@ -102,6 +103,11 @@ Options:GameType Options:GameType:Steam Options:GameType:Oculus + Options:Tools + Options:InstallPlaylist + {0} Options:InstallingPlaylist + {0} Options:FailedPlaylistSong + {0} {1} Options:FinishedPlaylist Options:Diagnostics Options:OpenLogsButton Options:OpenAppDataButton diff --git a/ModAssistant/Localisation/en.xaml b/ModAssistant/Localisation/en.xaml index ef57045..4838fec 100644 --- a/ModAssistant/Localisation/en.xaml +++ b/ModAssistant/Localisation/en.xaml @@ -102,6 +102,7 @@ This could break your other mods Failed to extract {0}, trying again in {1} seconds. ({2}/{3}) Failed to extract {0} after max attempts ({1}), skipping. This mod might not work properly so proceed at your own risk + Search... About @@ -142,6 +143,11 @@ Game Type Steam Oculus + Tools + Install Playlist + Installing Playlist: {0} + Failed song: {0} + [{0} fails] Finished Installing Playlist: {1} Diagnostics Open Logs Open AppData diff --git a/ModAssistant/Localisation/fr.xaml b/ModAssistant/Localisation/fr.xaml new file mode 100644 index 0000000..9d98e43 --- /dev/null +++ b/ModAssistant/Localisation/fr.xaml @@ -0,0 +1,245 @@ + + i18n:fr-FR + + + Impossible de trouver le dossier d'installation de Beat Saber ! + Appuyez sur OK pour réessayer, ou Annuler pour fermer l'application. + Argument invalide ! '{0}' nécessite une option. + Argument non reconnu. Fermeture de Mod Assistant. + Une exception non gérée est survenue + Exception + + + ModAssistant + Intro + Mods + À propos + Options + Version du jeu + Version + Info sur le mod + Installer + ou Mettre à jour + Impossible de charger les versions du jeu, l'onglet Mods sera indisponible. + Nouvelle version du jeu détectée ! + Il semble que le jeu a été mis à jour. + Veuillez vous assurer que la bonne version soit sélectionnée en bas à gauche ! + Aucun mod sélectionné ! + {0} n'a pas de page informative. + + + Intro + Bienvenue sur Mod Assistant + Veuillez lire cette page entièrement et avec attention + + En utilisant ce programme, vous attestez que vous avez lu et accepté les modalités suivantes : + + + Beat Saber + ne supporte + pas nativement les mods. Cela signifie que : + + + Les mods + dysfonctionneront à chaque mise à jour. C'est normal, et ce + n'est + pas la faute de Beat Games. + + + Les mods + causeront des bugs et des problèmes de performance. Ce + n'est + pas la faute de Beat Games. + + + Les mods sont créés + gratuitement par des gens sur leur + temps libre. Veuillez être patient et compréhensif. + + + NE laissez + PAS de commentaires négatifs parce que les mods ne fonctionnent plus. Ce + n'est + pas la faute de Beat Games. + Ils n'essaient pas de faire disparaître les mods. + + + Si je continue de voir des gens laisser des commentaires négatifs + parce que les mods ne fonctionnent plus, + + J'irai personnellement liquider les mods avec une cuillère rouillée + + + Veuillez lire le Guide du Débutant sur le + + Wiki + . + + J'accepte + Je refuse + Fermeture de l'application : vous n'avez pas accepté les modalités et conditions. + Impossible de télécharger la liste des versions + Onglet Mods désactivé. Veuillez relancer pour réessayer. + Vous pouvez désormais utiliser l'onglet Mods ! + + + Mods + Nom + Installé + Récent + Description + Désinstaller + Désinstaller + Impossible de charger la liste des mods + Vérification des mods installés + Chargement des mods + Fin : mods chargés + Installation de {0} + {0} installé + Fin : mods installés + Impossible de trouver le lien de téléchargement de {0} + Désinstaller {0} ? + Êtes-vous sûr de vouloir supprimer {0} ? + Cela pourrait faire dysfonctionner d'autres mods installés + Échec de l'extraction de {0}, nouvelle tentative dans {1} secondes. ({2}/{3}) + Échec de l'extraction de {0} après le maximum de tentatives ({1}), abandon. Ce mod pourrait ne pas fonctionner correctement, continuez à vos propres risques + Recherche... + + + + À propos + À propos de Mod Assistant + Je suis Assistant, et je réalise Mod Assistant pour l'assistance aux mods, avec quelques principes en tête : + Simplicité + Portabilité + Exécutable unique + Utilisation responsable + + Si vous aimez ce programme et souhaitez me soutenir, veuillez visiter ma + + page de don + + ou mon + + Patreon + + + Remerciements particuliers ♥ + Faire un don + Caresses-tête + Câlins + + + Options + Paramètres + Dossier d'installation + Sélectionner un dossier + Ouvrir le dossier + Sauvegarder les mods sélectionnés + Détecter les mods installés + Sélectionner les mods installés + Réinstaller les mods installés + Activer les installations OneClick™ + BeatSaver + ModelSaber + Type du jeu + Steam + Oculus + Outils + Installer une playlist + Installation de la playlist : {0} + Échec de la musique : {0} + [{0} échecs] Installation de la playlist terminée : {1} + Diagnostic + Ouvrir les logs + Ouvrir AppData + Désinstaller BSIPA + Supprimer tous les mods + Thème de l'application + Exporter le modèle + Envoi des logs + URL des logs copiée dans le presse-papier ! + L'envoi des logs a échoué + L'envoi des logs a échoué ! + Impossible d'envoyer le fichier de logs à Teknik, veuillez réessayer ou envoyer le fichier manuellement. + Récupération de la liste des mods + Découverte de la version de BSIPA + BSIPA désinstallé + Désinstaller les mods ? + Êtes-vous sûr de vouloir supprimer TOUS les mods ? + Cela ne peut pas être annulé. + Tous les mods ont été désinstallés + Le thème actuel a été supprimé, passage au thème par défaut... + Dossier Themes non trouvé ! Essayez d'exporter le modèle... + Dossier AppData non trouvé ! Essayez de lancer votre jeu. + + + Chargement des mods + + + Invalide + Installation invalide détectée + Votre installation du jeu est corrompue sinon invalide + Cela peut survenir si votre copie du jeu est piratée, ou si vous copiez une copie piratée sur votre installation légitime + + Si votre copie du jeu est piratée, + veuillez acheter le jeu + + ICI + + . + + + Si votre copie du jeu + n'est + pas piratée, veuillez + + faire une installation propre + . + + + Si cela n'a pas fonctionné, demandez de l'aide au support dans le canal textuel + #support dans + + BSMG + . + + Si vous utilisiez une version piratée mais avez acheté le jeu depuis + Sélectionner un dossier + Vous devez relancer Mod Assistant après avoir choisi l'installation légitime + + + Impossible de récupérer les détails de la map. + Impossible de télécharger la musique. + Impossible de télécharger la musique. + Il pourrait y avoir des problèmes avec BeatSaver ou votre connexion Internet. + Échec du téléchargement du ZIP de la musique + Chemin de l'installation de Beat Saber non trouvé. + Installé : {0} + Échec de l'installation. + {0} : gestionnaires d'installation OneClick™ inscrits ! + {0} : gestionnaires d'installation OneClick™ désinscrits ! + + + Thème non trouvé, passage au thème par défaut... + Thème défini sur {0}. + {0} n'existe pas. + Modèle du thème "{0}" sauvegardé dans le dossier Themes. + Le modèle du thème existe déjà ! + Échec du chargement du fichier .xaml pour le thème {0} : {1} + + + Impossible de vérifier les mises à jour. + Impossible de télécharger la mise à jour. + + + Mod Assistant + Impossible de détecter le dossier d'installation de Beat Saber. Veuillez le sélectionner manuellement. + Mod Assistant a besoin de lancer cette tâche en administrateur. Veuillez réessayer. + Sélectionnez le dossier d'installation de Beat Saber + Impossible d'ouvrir le dossier : {0} + diff --git a/ModAssistant/Localisation/it.xaml b/ModAssistant/Localisation/it.xaml index 4e86207..bbf7ac0 100644 --- a/ModAssistant/Localisation/it.xaml +++ b/ModAssistant/Localisation/it.xaml @@ -102,7 +102,7 @@ Continuando, altre mod potrebbero smettere di funzionare Impossibile estrarre {0}, prossimo tentativo in {1} secondi. ({2}/{3}) Non sono riuscito ad estrarre {0} dopo aver raggiunto il numero massimo di tentativi ({1}), salto questa mod. Questa protrebbe anche non funzionare correttamente, quindi procedi a tuo rischio e pericolo - Cerca... + Cerca... Info @@ -143,11 +143,11 @@ Tipo di Installazione Steam Oculus - Strumenti - Installa Playlist - Installazione Playlist: {0} - Installazione canzone fallita: {0} - [{0} fails] Installazione playlist terminata: {1} + Strumenti + Installa Playlist + Installazione Playlist: {0} + Installazione canzone fallita: {0} + [{0} fails] Installazione playlist terminata: {1} Diagnostica Apri il Log Apri AppData diff --git a/ModAssistant/Localisation/ko.xaml b/ModAssistant/Localisation/ko.xaml new file mode 100644 index 0000000..57721fe --- /dev/null +++ b/ModAssistant/Localisation/ko.xaml @@ -0,0 +1,237 @@ + + i18n:ko-KR + + + 비트세이버 설치 폴더를 찾을 수 없습니다! + OK를 눌러 재시도하거나, Cancel을 눌러 프로그램을 종료할 수 있습니다. + 유효하지 않은 인수입니다! '{0}'은 옵션을 필요로 합니다. + 인식할 수 없는 인수입니다. 모드 어시스턴트를 종료합니다. + 처리되지 않은 예외가 발생했습니다. + 예외 + + + 모드 어시스턴트 + 인트로 + 모드 + 정보 + 옵션 + 게임 버전 + 버전 + 모드 설명 + 설치 + 또는 업데이트 + 게임 버전을 로드할 수 없었기 때문에 모드 탭이 비활성화됩니다. + 새로운 게임 버전이 감지되었습니다! + 게임 업데이트가 있었던 것 같습니다. + 왼쪽 아래에 선택된 버전이 맞는 버전인지 다시 한번 확인해주세요! + 아무런 모드도 선택되지 않았습니다! + {0}는 설명 페이지를 가지고 있지 않습니다. + + + 인트로 + 모드 어시스턴트에 어서오세요 + 이 페이지를 정독해주세요 + + 이 프로그램을 사용하려면 다음 약관을 읽고 동의해야 합니다: + + + 비트세이버는 공식적으로 모드를 + 지원하지 않습니다. + + + 이것은 모드들이 매 업데이트마다 + 망가진다는 것을 의미합니다. 이것은 일반적이며, Beat Games'의 탓이 + 아닙니다. + + + 모드들은 버그와 성능 문제를 + 발생시킵니다. 이것은 Beat Games'의 탓이 + 아닙니다. + + + 모드들은 + 무료로 만들어졌으며 모더들의 + 소중한 시간의 결과물입니다.기다림을 갖고 이해해주세요. + + + 모드가 망가진 것 때문에 게임에 대한 부정적인 의견을남기지 마세요. 이것은 Beat Games'의 탓이 + 아닙니다. + Beat Games'는 모드를 죽이려 하지 않습니다. + + + 만일 사람들이 모드가 + 망가진 것을 이유로 부정적인 의견을 남기는 것을 계속 보게 된다면, + + 제가 개인적으로 모드를 터트릴지도요..? + + + + 위키 + 에 있는 초보자 가이드를 읽어주세요. + + 동의합니다 + 거부합니다 + 어플리케이션을 종료합니다: 당신은 약관에 동의하지 않았습니다. + 버전 리스트를 다운로드할 수 없었습니다 + 모드 탭이 비활성화되었습니다. 어시스턴트를 재시작해주세요. + 이제 모드 탭을 사용할 수 있습니다! + + + 모드 + 이름 + 설치 버전 + 최신 버전 + 설명 + 제거 + 제거 + 모드 목록을 불러올 수 없었습니다 + 설치된 모드들을 확인하고 있습니다 + 모드들을 불러오고 있습니다 + 모드들을 불러왔습니다 + Installing {0} + Installed {0} + 모드 설치를 마쳤습니다 + {0}를 위한 다운로드 링크를 찾을 수 없었습니다 + {0}를 제거하시겠습니까? + 정말로 {0}를 제거하시겠습니까? + 다른 모드를 사용 못하게 만들 수도 있습니다. + {0}를 추출하는데 실패했습니다. {1}초 안에 재시도합니다. ({2}/{3}) + ({1})회동안 {0}를 추출하는데 실패했습니다. 이 모드가 제대로 작동하지 않을지도 모릅니다 + Search... + + + 정보 + 모드 어시스턴트에 대하여 + I'm Assistant, and I made Mod Assistant for mod assistance, with a few principles in mind: + Simplicity + Portability + Single Executable + Responsible use + + If you enjoy this program and would like to support me, please visit my + + donation page + + or my + + Patreon + + + Special Thanks ♥ + Donate + Headpats + Hugs + + + 옵션 + 설정 + 설치 폴더 + 폴더 선택 + 폴더 열기 + 선택된 모드 저장 + 설치된 모드 감지 + 설치된 모드 선택 + 설치된 모드 재설치 + OneClick™ 설치 활성화 + BeatSaver + ModelSaber + 게임 유형 + Steam + Oculus + Tools + Install Playlist + Installing Playlist: {0} + Failed song: {0} + [{0} fails] Finished Installing Playlist: {1} + 진단 + 로그 열기 + 앱데이터 열기 + BSIPA 삭제 + 모든 모드 삭제 + 어플리케이션 테마 + 템플릿 추출 + 로그 제출 + 로그 URL이 클립보드에 복사되었습니다! + 로그 제출에 실패하였습니다 + 로그 제출에 실패하였습니다! + Teknik에게 로그 파일을 제출하는데에 실패하였습니다. 재시도하거나 수동으로 파일을 보내주세요. + 모드 목록을 얻는중입니다 + BSIPA 버전을 찾는중입니다 + BSIPA가 제거되었습니다 + 모든 모드를 제거할까요? + 정말로 모든 모드를 제거할까요? + 이것은 취소할 수 없습니다. + 모든 모드가 제거되었습니다 + 현재 테마를 제거중입니다. 원래 테마로 돌아갑니다... + 테마 폴더를 찾을 수 없습니다! 템플릿으로 내보냅니다... + AppData 폴더를 찾을 수 없습니다! 게임을 실행해보세요. + + + 모드 로딩중 + + + 잘못된 프로그램 + 잘못된 프로그램 설치가 감지되었습니다 + 게임이 손상되었거나 다른 이유로 잘못된 것 같습니다 + 이 오류는 게임이 불법적인 경로로 받아졌거나, 불법적인 경로로 받아진 게임을 당신의 정상적인 게임에 덮어씌워졌을 때에 발생합니다 + + 만일 당신이 게임을 불법적인 경로로 받았다면, + + + 여기서 + + 게임을 구매해주세요. + + + 만일 당신의 게임이 불법적인 경로로 받아진게 + 아니라면, + + 클린재설치 + 를 해주세요. + + + 만일 그것들이 도움되지 않았다면, + + BSMG + 의 + #support 채널에서 도움을 구하세요. + + 만일 불법적인 경로로 받아진 게임을 가지고 있었다가 게임을 구매했다면, + 폴더를 선택해주세요 + 정상적인 설치 이후 모드 어시스턴트를 재시작할 필요가 있습니다 + + + 맵의 세부정보를 얻어올 수 없었습니다. + 곡을 받아올 수 없었습니다. + 곡을 받아올 수 없었습니다. + BeatSaver 또는 당신의 인터넷 연결에 문제가 있는 것 같습니다. + 노래 ZIP 파일을 받는 데에 실패했습니다. + 비트세이버 설치 폴더를 찾을 수 없었습니다. + 설치됨: {0} + 설치에 실패하였습니다. + {0} OneClick™ 설치 관리자가 등록되었습니다! + {0} OneClick™ 설치 관리자가 등록 취소되었습니다! + + + 테마를 찾을 수 없어, 기본 테마로 돌아갑니다... + {0} 테마로 설정합니다. + {0}는 존재하지 않습니다. + 템플릿 테마 "{0}"가 템플릿 폴더에 저장되었습니다. + 템플릿 테마가 이미 존재합니다! + 테마를 위한 .xaml 파일을 불러오지 못했습니다 {0}: {1} + + + 업데이트를 확인할 수 없습니다. + 업데이트를 다운로드할 수 없습니다. + + + 모드 어시스턴트 + 비트세이버 설치 폴더를 찾을 수 없습니다. 수동으로 선택해주세요. + 모드 어시스턴트는 이 작업을 관리자 권한으로 실행하는 것을 필요로 합니다. 다시 시도해주세요. + 비트세이버 설치 폴더를 선택해주세요 + 폴더를 열 수 없습니다: {0} + diff --git a/ModAssistant/Localisation/nl.xaml b/ModAssistant/Localisation/nl.xaml index 8b0ecea..4837c0f 100644 --- a/ModAssistant/Localisation/nl.xaml +++ b/ModAssistant/Localisation/nl.xaml @@ -36,7 +36,7 @@ Welkom bij Mod Assistant Lees deze pagina alstublieft volledig en aandachtig - Door het gebruiken van dit programma verlkaar ik de volgende voorwaarden te hebben gelezen en hier mee akkoord te gaan: + Door het gebruiken van dit programma verklaar ik de volgende voorwaarden te hebben gelezen en hier mee akkoord te gaan: Beat Saber @@ -67,7 +67,7 @@ zal ik persoonlijk met een roestige lepel mods niet meer laten werken - Lees alstublieft de 'Beginners Guide' op de wiki + Lees alstublieft de 'Beginners Guide' op de Wiki . (engels) @@ -77,7 +77,7 @@ Sluit applicatie af: U accepteerde de voorwaarden niet. Kon de versie lijst niet downloaden Mods tabblad uitgeschakeld. Herstart het programma om opnieuw te proberen. - U kan nu het mods tabblad gebruiken! + U kunt nu het mods tabblad gebruiken! Mods @@ -100,6 +100,7 @@ Dit zou uw andere mods niet meer kunnen laten werken Kon {0} niet extracten, probeer opniew over {1} seconden. ({2}/{3}) Kon {0} niet extracten na maximaal aantal pogingen ({1}), Overslaan. Deze mod werkt msischien niet goed dus ga verder op eigen risico + Search... Over @@ -140,6 +141,11 @@ Game Type Steam Oculus + Tools + Install Playlist + Installing Playlist: {0} + Failed song: {0} + [{0} fails] Finished Installing Playlist: {1} Diagnostiek Open Logs Open AppData diff --git a/ModAssistant/Localisation/zh.xaml b/ModAssistant/Localisation/zh.xaml index 201cd79..a40b94f 100644 --- a/ModAssistant/Localisation/zh.xaml +++ b/ModAssistant/Localisation/zh.xaml @@ -94,6 +94,7 @@ 这可能会导致其他Mod不可用。 {0}解压失败,将在{1}秒后重试。({2}/{3}) {0}在重试{1}次后仍然无法解压,将被跳过。注意,这个Mod可能无法使用。 + 搜索... 关于 @@ -134,6 +135,11 @@ 游戏类型 Steam Oculus + 工具 + 添加歌单(Playlist) + 正在添加歌单:{0} + 失败歌曲:{0} + [{0}失败]添加{1}完成 诊断工具 打开日志 打开游戏存档 diff --git a/ModAssistant/ModAssistant.csproj b/ModAssistant/ModAssistant.csproj index fa6418a..2edaf87 100644 --- a/ModAssistant/ModAssistant.csproj +++ b/ModAssistant/ModAssistant.csproj @@ -38,6 +38,7 @@ + @@ -67,6 +68,7 @@ + @@ -80,6 +82,10 @@ Intro.xaml + + MSBuild:Compile + Designer + Designer MSBuild:Compile @@ -101,6 +107,18 @@ MSBuild:Compile Designer + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile Designer diff --git a/ModAssistant/Pages/Mods.xaml b/ModAssistant/Pages/Mods.xaml index ddc8caf..bda6daf 100644 --- a/ModAssistant/Pages/Mods.xaml +++ b/ModAssistant/Pages/Mods.xaml @@ -12,15 +12,51 @@ mc:Ignorable="d"> + + + + + + + +