├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ └── dotnet.yml ├── LICENSE ├── ModAssistant.sln ├── ModAssistant ├── App.config ├── App.xaml ├── App.xaml.cs ├── Classes │ ├── Diagnostics.cs │ ├── External Interfaces │ │ ├── BeatSaver.cs │ │ ├── ModelSaber.cs │ │ ├── Playlists.cs │ │ └── Utils.cs │ ├── Http.cs │ ├── HyperlinkExtensions.cs │ ├── Languages.cs │ ├── Mod.cs │ ├── OneClickInstaller.cs │ ├── Promotions.cs │ ├── Themes.cs │ ├── Updater.cs │ └── Utils.cs ├── Libs │ └── semver │ │ ├── IntExtensions.cs │ │ └── SemVersion.cs ├── Localisation │ ├── cs.xaml │ ├── de.xaml │ ├── en-DEBUG.xaml │ ├── en.xaml │ ├── es.xaml │ ├── fr.xaml │ ├── it.xaml │ ├── ja.xaml │ ├── ko.xaml │ ├── nb.xaml │ ├── nl.xaml │ ├── pl.xaml │ ├── pt.xaml │ ├── ru.xaml │ ├── sv.xaml │ ├── th.xaml │ └── zh.xaml ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── ModAssistant.csproj ├── OneClickStatus.xaml ├── OneClickStatus.xaml.cs ├── Pages │ ├── About.xaml │ ├── About.xaml.cs │ ├── Intro.xaml │ ├── Intro.xaml.cs │ ├── Invalid.xaml │ ├── Invalid.xaml.cs │ ├── Loading.xaml │ ├── Loading.xaml.cs │ ├── Mods.xaml │ ├── Mods.xaml.cs │ ├── Options.xaml │ └── Options.xaml.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── Resources │ ├── Icons.xaml │ └── icon.ico ├── Styles │ ├── Button.xaml │ ├── CheckBox.xaml │ ├── ComboBox.xaml │ ├── ComboBoxItem.xaml │ ├── GridViewColumnHeader.xaml │ ├── Label.xaml │ ├── ListView.xaml │ ├── ListViewItem.xaml │ ├── Menu.xaml │ ├── MenuItem.xaml │ ├── RepeatButton.xaml │ ├── ScrollBar.xaml │ ├── TextBlock.xaml │ ├── Thumb.xaml │ └── ToggleButton.xaml └── Themes │ ├── Anniversary.xaml │ ├── Anniversary │ └── Background.png │ ├── BSMG.xaml │ ├── BSMG │ └── Sidebar.png │ ├── Dark.xaml │ ├── Default Scrollbar.xaml │ ├── Light Pink.xaml │ ├── Light.xaml │ └── Ugly Kulu-Ya-Ku.xaml ├── README.md └── tools ├── README.md └── generate_translation_stubs.py /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = space 7 | indent_size = 4 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.cs] 12 | dotnet_sort_system_directives_first = true 13 | dotnet_separate_import_directive_groups = false 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | 18 | [*.yml] 19 | indent_size = 2 20 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: BeatSaberMods 2 | ko_fi: N4N8JX7B 3 | liberapay: Assistant 4 | custom: ['https://paypal.me/AssistantMoe', 'https://bs.assistant.moe/Donate/'] 5 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: .NET Build 2 | on: 3 | push: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | runs-on: windows-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v3 14 | - name: Setup msbuild 15 | uses: microsoft/setup-msbuild@v1.1.3 16 | - name: Install dependencies 17 | run: msbuild -t:restore 18 | - name: Build project 19 | run: msbuild ModAssistant/ModAssistant.csproj /t:Build /p:Configuration=Release 20 | - name: Cleanup release 21 | shell: bash 22 | run: | 23 | find "ModAssistant/bin/Release" -type f ! -name "ModAssistant.exe" -delete 24 | cp "LICENSE" "ModAssistant/bin/Release/LICENSE.ModAssistant.txt" 25 | - name: Upload Build 26 | if: startsWith(github.ref, 'refs/tags/') == false 27 | uses: actions/upload-artifact@v3 28 | with: 29 | name: ModAssistant-${{ github.sha }} 30 | path: ./ModAssistant/bin/Release/ 31 | - name: Extract Release Version 32 | if: startsWith(github.ref, 'refs/tags/') 33 | id: get_version 34 | shell: bash 35 | run: echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT 36 | - name: Release 37 | if: startsWith(github.ref, 'refs/tags/') 38 | uses: softprops/action-gh-release@v1 39 | env: 40 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 41 | with: 42 | name: Mod Assistant v${{ steps.get_version.outputs.version }} 43 | files: ./ModAssistant/bin/Release/ModAssistant.exe 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2020 Assistant 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ModAssistant.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2027 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModAssistant", "ModAssistant\ModAssistant.csproj", "{6A224B82-40DA-40B3-94DC-EFBEC2BDDA39}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {6A224B82-40DA-40B3-94DC-EFBEC2BDDA39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {6A224B82-40DA-40B3-94DC-EFBEC2BDDA39}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {6A224B82-40DA-40B3-94DC-EFBEC2BDDA39}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {6A224B82-40DA-40B3-94DC-EFBEC2BDDA39}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {ACC0D20B-78C5-40AC-BF48-553C41D9987B} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /ModAssistant/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | False 22 | 23 | 24 | True 25 | 26 | 27 | False 28 | 29 | 30 | False 31 | 32 | 33 | False 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | True 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | False 52 | 53 | 54 | True 55 | 56 | 57 | False 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /ModAssistant/App.xaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /ModAssistant/Classes/Diagnostics.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Windows; 4 | 5 | namespace ModAssistant 6 | { 7 | class Diagnostics 8 | { 9 | public static string[] ReadFolder(string path, int level = 0) 10 | { 11 | List entries = new List(); 12 | 13 | foreach (string file in Directory.GetFileSystemEntries(path)) 14 | { 15 | string line = string.Empty; 16 | 17 | if (File.Exists(file)) 18 | { 19 | line = Utils.CalculateMD5(file) + " " + LevelSeparator(level) + "├─ " + Path.GetFileName(file); 20 | entries.Add(line); 21 | 22 | } 23 | else if (Directory.Exists(file)) 24 | { 25 | line = Utils.Constants.MD5Spacer + LevelSeparator(level) + "├─ " + Path.GetFileName(file); 26 | entries.Add(line); 27 | 28 | foreach (string entry in ReadFolder(file.Replace(@"\", @"\\"), level + 1)) 29 | { 30 | //MessageBox.Show(entry); 31 | entries.Add(entry); 32 | } 33 | 34 | } 35 | else 36 | { 37 | MessageBox.Show("! " + file); 38 | } 39 | } 40 | if (entries.Count > 0) 41 | { 42 | entries[entries.Count - 1] = entries[entries.Count - 1].Replace("├", "└"); 43 | } 44 | 45 | return entries.ToArray(); 46 | } 47 | 48 | private static string LevelSeparator(int level) 49 | { 50 | string separator = string.Empty; 51 | for (int i = 0; i < level; i++) 52 | { 53 | separator += "│ "; 54 | } 55 | return separator; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ModAssistant/Classes/External Interfaces/ModelSaber.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace ModAssistant.API 5 | { 6 | class ModelSaber 7 | { 8 | private const string ModelSaberURLPrefix = "https://modelsaber.com/files/"; 9 | private const string CustomAvatarsFolder = "CustomAvatars"; 10 | private const string CustomSabersFolder = "CustomSabers"; 11 | private const string CustomPlatformsFolder = "CustomPlatforms"; 12 | private const string CustomBloqsFolder = "CustomNotes"; 13 | 14 | public static async Task GetModel(Uri uri) 15 | { 16 | switch (uri.Host) 17 | { 18 | case "avatar": 19 | await Utils.DownloadAsset(ModelSaberURLPrefix + uri.Host + uri.AbsolutePath, CustomAvatarsFolder); 20 | break; 21 | case "saber": 22 | await Utils.DownloadAsset(ModelSaberURLPrefix + uri.Host + uri.AbsolutePath, CustomSabersFolder); 23 | break; 24 | case "platform": 25 | await Utils.DownloadAsset(ModelSaberURLPrefix + uri.Host + uri.AbsolutePath, CustomPlatformsFolder); 26 | break; 27 | case "bloq": 28 | await Utils.DownloadAsset(ModelSaberURLPrefix + uri.Host + uri.AbsolutePath, CustomBloqsFolder); 29 | break; 30 | } 31 | } 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ModAssistant/Classes/External Interfaces/Playlists.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Windows; 6 | using static ModAssistant.Http; 7 | 8 | namespace ModAssistant.API 9 | { 10 | public class Playlists 11 | { 12 | private const string BSaberURLPrefix = "https://bsaber.com/PlaylistAPI/"; 13 | private const string PlaylistsFolder = "Playlists"; 14 | private static readonly string BeatSaberPath = Utils.BeatSaberPath; 15 | 16 | public static void CreatePlaylistsFolder() 17 | { 18 | string playlistsPath = Path.Combine(BeatSaberPath, PlaylistsFolder); 19 | Directory.CreateDirectory(playlistsPath); 20 | } 21 | 22 | public static async Task DownloadAll(Uri uri) 23 | { 24 | switch (uri.Host) 25 | { 26 | case "playlist": 27 | Uri url = new Uri($"{uri.PathAndQuery.Trim('/')}"); 28 | string filename = await Get(url); 29 | await DownloadFrom(filename); 30 | break; 31 | } 32 | } 33 | 34 | public static async Task Get(Uri url) 35 | { 36 | try 37 | { 38 | CreatePlaylistsFolder(); 39 | string filename = await Utils.DownloadAsset(url.ToString(), PlaylistsFolder, preferContentDisposition: true); 40 | return Path.Combine(BeatSaberPath, PlaylistsFolder, filename); 41 | } 42 | catch 43 | { 44 | return null; 45 | } 46 | } 47 | 48 | public static async Task DownloadFrom(string file) 49 | { 50 | CreatePlaylistsFolder(); 51 | 52 | if (Path.Combine(BeatSaberPath, PlaylistsFolder) != Path.GetDirectoryName(file)) 53 | { 54 | string destination = Path.Combine(BeatSaberPath, PlaylistsFolder, Path.GetFileName(file)); 55 | File.Copy(file, destination, true); 56 | } 57 | 58 | int Errors = 0; 59 | int Minimum = 0; 60 | int Value = 0; 61 | 62 | Playlist playlist = JsonSerializer.Deserialize(File.ReadAllText(file)); 63 | int Maximum = playlist.songs.Length; 64 | 65 | foreach (Playlist.Song song in playlist.songs) 66 | { 67 | API.BeatSaver.BeatSaverMap response = new BeatSaver.BeatSaverMap(); 68 | if (!string.IsNullOrEmpty(song.hash)) 69 | { 70 | response = await BeatSaver.GetFromHash(song.hash, false); 71 | } 72 | else if (!string.IsNullOrEmpty(song.key)) 73 | { 74 | response = await BeatSaver.GetFromKey(song.key, false); 75 | } 76 | Value++; 77 | 78 | if (response.Success) 79 | { 80 | Utils.SetMessage($"{string.Format((string)Application.Current.FindResource("Options:InstallingPlaylist"), TextProgress(Minimum, Maximum, Value))} {response.Name}"); 81 | } 82 | else 83 | { 84 | Utils.SetMessage($"{string.Format((string)Application.Current.FindResource("Options:FailedPlaylistSong"), song.songName)}"); 85 | ModAssistant.Utils.Log($"Failed installing BeatSaver map: {song.songName} | {song.key} | {song.hash} | ({response?.response?.ratelimit?.Remaining})"); 86 | App.CloseWindowOnFinish = false; 87 | await Task.Delay(3 * 1000); 88 | Errors++; 89 | } 90 | } 91 | Utils.SetMessage($"{string.Format((string)Application.Current.FindResource("Options:FinishedPlaylist"), Errors, playlist.playlistTitle)}"); 92 | } 93 | 94 | private static string TextProgress(int min, int max, int value) 95 | { 96 | if (max == value) 97 | { 98 | return $" {string.Concat(Enumerable.Repeat("▒", 10))} [{value}/{max}]"; 99 | } 100 | int interval = (int)Math.Floor((double)value / (((double)max - (double)min) / (double)10)); 101 | return $" {string.Concat(Enumerable.Repeat("▒", interval))}{string.Concat(Enumerable.Repeat("░", 10 - interval))} [{value}/{max}]"; 102 | } 103 | 104 | #pragma warning disable IDE1006 // Naming Styles 105 | class Playlist 106 | { 107 | public string playlistTitle { get; set; } 108 | public string playlistAuthor { get; set; } 109 | public string image { get; set; } 110 | public Song[] songs { get; set; } 111 | 112 | public class Song 113 | { 114 | public string key { get; set; } 115 | public string hash { get; set; } 116 | public string songName { get; set; } 117 | public string uploader { get; set; } 118 | } 119 | } 120 | } 121 | } 122 | #pragma warning restore IDE1006 // Naming Styles 123 | -------------------------------------------------------------------------------- /ModAssistant/Classes/External Interfaces/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | 8 | namespace ModAssistant.API 9 | { 10 | public class Utils 11 | { 12 | public static readonly string BeatSaberPath = App.BeatSaberInstallDirectory; 13 | 14 | public static void SetMessage(string message) 15 | { 16 | if (App.OCIWindow != "No" || App.OCIWindow != "Notify") 17 | { 18 | if (App.window == null) 19 | { 20 | if (App.OCIWindow == "No" || App.OCIWindow == "Notify") OneClickStatus.Instance = null; 21 | if (OneClickStatus.Instance == null) return; 22 | 23 | OneClickStatus.Instance.MainText = message; 24 | } 25 | else 26 | { 27 | MainWindow.Instance.MainText = message; 28 | } 29 | } 30 | } 31 | 32 | public static async Task DownloadAsset(string link, string folder, string fileName = null, string displayName = null, bool showNotification = true, bool beatsaver = false, bool preferContentDisposition = false) 33 | { 34 | if (string.IsNullOrEmpty(BeatSaberPath)) 35 | { 36 | ModAssistant.Utils.SendNotify((string)Application.Current.FindResource("OneClick:InstallDirNotFound")); 37 | } 38 | try 39 | { 40 | var parentDir = Path.Combine(BeatSaberPath, folder); 41 | Directory.CreateDirectory(parentDir); 42 | 43 | if (string.IsNullOrEmpty(fileName)) 44 | { 45 | fileName = new Uri(link).Segments.Last(); 46 | } 47 | 48 | if (beatsaver) 49 | { 50 | fileName = WebUtility.UrlDecode(Path.Combine(parentDir, fileName)); 51 | await BeatSaver.Download(link, fileName); 52 | } 53 | else 54 | { 55 | fileName = await ModAssistant.Utils.Download(link, parentDir, fileName, preferContentDisposition); 56 | } 57 | 58 | if (string.IsNullOrEmpty(displayName)) 59 | { 60 | displayName = Path.GetFileNameWithoutExtension(fileName); 61 | } 62 | 63 | if (showNotification) 64 | { 65 | SetMessage(string.Format((string)Application.Current.FindResource("OneClick:InstalledAsset"), displayName)); 66 | } 67 | 68 | return fileName; 69 | } 70 | catch 71 | { 72 | SetMessage((string)Application.Current.FindResource("OneClick:AssetInstallFailed")); 73 | App.CloseWindowOnFinish = false; 74 | return null; 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /ModAssistant/Classes/Http.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Http; 4 | using System.Web.Script.Serialization; 5 | 6 | namespace ModAssistant 7 | { 8 | static class Http 9 | { 10 | private static HttpClient _client = null; 11 | 12 | public static HttpClient HttpClient 13 | { 14 | get 15 | { 16 | if (_client != null) return _client; 17 | 18 | var handler = new HttpClientHandler() 19 | { 20 | AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate, 21 | }; 22 | 23 | _client = new HttpClient(handler) 24 | { 25 | Timeout = TimeSpan.FromSeconds(240), 26 | }; 27 | 28 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; 29 | _client.DefaultRequestHeaders.Add("User-Agent", "ModAssistant/" + App.Version); 30 | 31 | return _client; 32 | } 33 | } 34 | 35 | public static JavaScriptSerializer JsonSerializer = new JavaScriptSerializer() 36 | { 37 | MaxJsonLength = int.MaxValue, 38 | }; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ModAssistant/Classes/HyperlinkExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Windows; 3 | using System.Windows.Documents; 4 | 5 | namespace ModAssistant 6 | { 7 | public static class HyperlinkExtensions 8 | { 9 | public static bool GetIsExternal(DependencyObject obj) 10 | { 11 | return (bool)obj.GetValue(IsExternalProperty); 12 | } 13 | 14 | public static void SetIsExternal(DependencyObject obj, bool value) 15 | { 16 | obj.SetValue(IsExternalProperty, value); 17 | } 18 | 19 | public static readonly DependencyProperty IsExternalProperty = DependencyProperty.RegisterAttached("IsExternal", typeof(bool), typeof(HyperlinkExtensions), new UIPropertyMetadata(false, OnIsExternalChanged)); 20 | 21 | private static void OnIsExternalChanged(object sender, DependencyPropertyChangedEventArgs args) 22 | { 23 | var hyperlink = sender as Hyperlink; 24 | 25 | if ((bool)args.NewValue) 26 | hyperlink.RequestNavigate += Hyperlink_RequestNavigate; 27 | else 28 | hyperlink.RequestNavigate -= Hyperlink_RequestNavigate; 29 | } 30 | 31 | private static void Hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e) 32 | { 33 | Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri)); 34 | e.Handled = true; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ModAssistant/Classes/Languages.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Windows; 7 | using ModAssistant.Pages; 8 | 9 | namespace ModAssistant 10 | { 11 | internal class Languages 12 | { 13 | public static string LoadedLanguage { get; private set; } 14 | public static List LoadedLanguages => availableCultures.ToList(); 15 | public static bool FirstRun = true; 16 | private static readonly string[] availableLanguageCodes = { "cs", "de", "en", "es", "fr", "it", "ja", "ko", "nb", "nl", "pl", "pt", "ru", "sv", "th", "zh" }; 17 | private static IEnumerable availableCultures; 18 | 19 | public static void LoadLanguages() 20 | { 21 | var allCultures = CultureInfo.GetCultures(CultureTypes.AllCultures); 22 | 23 | // Get CultureInfo for any of the available translations 24 | availableCultures = allCultures.Where(cultureInfo => availableLanguageCodes.Any(code => code.Equals(cultureInfo.Name))); 25 | 26 | string savedLanguageCode = Properties.Settings.Default.LanguageCode; 27 | if (!LoadLanguage(savedLanguageCode)) 28 | { 29 | // If no language code was saved, load system language 30 | if (!LoadLanguage(CultureInfo.CurrentUICulture.Name)) 31 | { 32 | _ = LoadLanguage("en"); 33 | } 34 | } 35 | 36 | UpdateUI(LoadedLanguage); 37 | } 38 | 39 | public static void UpdateUI(string languageCode) 40 | { 41 | if (Options.Instance != null && Options.Instance.LanguageSelectComboBox != null) 42 | { 43 | Options.Instance.LanguageSelectComboBox.ItemsSource = availableCultures.Select(cultureInfo => cultureInfo.NativeName).ToList(); 44 | Options.Instance.LanguageSelectComboBox.SelectedIndex = LoadedLanguages.FindIndex(cultureInfo => cultureInfo.Name.Equals(languageCode)); 45 | } 46 | } 47 | 48 | public static ResourceDictionary LanguagesDict 49 | { 50 | get 51 | { 52 | return Application.Current.Resources.MergedDictionaries[1]; 53 | } 54 | } 55 | 56 | public static bool LoadLanguage(string languageCode) 57 | { 58 | if (string.IsNullOrEmpty(languageCode)) 59 | { 60 | return false; 61 | } 62 | 63 | try 64 | { 65 | LanguagesDict.Source = new Uri($"Localisation/{languageCode}.xaml", UriKind.Relative); 66 | LoadedLanguage = languageCode; 67 | return true; 68 | } 69 | catch (IOException) 70 | { 71 | if (languageCode.Contains("-")) 72 | { 73 | return LoadLanguage(languageCode.Split('-').First()); 74 | } 75 | 76 | return false; 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /ModAssistant/Classes/Mod.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using ModAssistant.Pages; 3 | 4 | namespace ModAssistant 5 | { 6 | public class Mod 7 | { 8 | public string name; 9 | public string version; 10 | public string gameVersion; 11 | public string _id; 12 | public string status; 13 | public string authorId; 14 | public string uploadedDate; 15 | public string updatedDate; 16 | public Author author; 17 | public string description; 18 | public string link; 19 | public string category; 20 | public DownloadLink[] downloads; 21 | public bool required; 22 | public Dependency[] dependencies; 23 | public List Dependents = new List(); 24 | public Mods.ModListItem ListItem; 25 | 26 | public class Author 27 | { 28 | public string _id; 29 | public string username; 30 | public string lastLogin; 31 | } 32 | 33 | public class DownloadLink 34 | { 35 | public string type; 36 | public string url; 37 | public FileHashes[] hashMd5; 38 | } 39 | 40 | public class FileHashes 41 | { 42 | public string hash; 43 | public string file; 44 | } 45 | 46 | public class Dependency 47 | { 48 | public string name; 49 | public string _id; 50 | public Mod Mod; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ModAssistant/Classes/OneClickInstaller.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using System.Windows; 5 | using System.Drawing; 6 | using Microsoft.Win32; 7 | using System.Windows.Forms; 8 | using Application = System.Windows.Application; 9 | using MessageBox = System.Windows.MessageBox; 10 | 11 | namespace ModAssistant 12 | { 13 | class OneClickInstaller 14 | { 15 | private static readonly string[] Protocols = new[] { "modelsaber", "beatsaver", "bsplaylist" }; 16 | public static OneClickStatus Status = new OneClickStatus(); 17 | 18 | public static async Task InstallAsset(string link) 19 | { 20 | Uri uri = new Uri(link); 21 | if (!Protocols.Contains(uri.Scheme)) return; 22 | 23 | switch (uri.Scheme) 24 | { 25 | case "modelsaber": 26 | await ModelSaber(uri); 27 | break; 28 | case "beatsaver": 29 | await BeatSaver(uri); 30 | break; 31 | case "bsplaylist": 32 | await Playlist(uri); 33 | break; 34 | } 35 | if (App.OCIWindow != "No") 36 | { 37 | Status.StopRotation(); 38 | API.Utils.SetMessage((string)Application.Current.FindResource("OneClick:Done")); 39 | } 40 | if (App.OCIWindow == "Notify") 41 | { 42 | Utils.SendNotify((string)Application.Current.FindResource("OneClick:DoneNotify")); 43 | Application.Current.Shutdown(); 44 | } 45 | if (App.OCIWindow == "Close") 46 | { 47 | Application.Current.Shutdown(); 48 | } 49 | 50 | 51 | } 52 | 53 | private static async Task BeatSaver(Uri uri) 54 | { 55 | string Key = uri.Host; 56 | await API.BeatSaver.GetFromKey(Key); 57 | } 58 | 59 | private static async Task ModelSaber(Uri uri) 60 | { 61 | if (App.OCIWindow != "No" && App.OCIWindow != "Notify") Status.Show(); 62 | API.Utils.SetMessage($"{string.Format((string)Application.Current.FindResource("OneClick:Installing"), System.Web.HttpUtility.UrlDecode(uri.Segments.Last()))}"); 63 | await API.ModelSaber.GetModel(uri); 64 | } 65 | 66 | private static async Task Playlist(Uri uri) 67 | { 68 | if (App.OCIWindow != "No" && App.OCIWindow != "Notify") Status.Show(); 69 | if (App.OCIWindow == "Notify") Utils.SendNotify((string)Application.Current.FindResource("OneClick:StartDownloadPlaylist")); 70 | await API.Playlists.DownloadAll(uri); 71 | } 72 | 73 | public static void Register(string Protocol, bool Background = false, string Description = null) 74 | { 75 | if (IsRegistered(Protocol) == true) 76 | return; 77 | try 78 | { 79 | if (Utils.IsAdmin) 80 | { 81 | RegistryKey ProtocolKey = Registry.ClassesRoot.OpenSubKey(Protocol, true); 82 | if (ProtocolKey == null) 83 | ProtocolKey = Registry.ClassesRoot.CreateSubKey(Protocol, true); 84 | RegistryKey CommandKey = ProtocolKey.CreateSubKey(@"shell\open\command", true); 85 | if (CommandKey == null) 86 | CommandKey = Registry.ClassesRoot.CreateSubKey(@"shell\open\command", true); 87 | 88 | if (ProtocolKey.GetValue("OneClick-Provider", "").ToString() != "ModAssistant") 89 | { 90 | if (Description != null) 91 | { 92 | ProtocolKey.SetValue("", Description, RegistryValueKind.String); 93 | } 94 | ProtocolKey.SetValue("URL Protocol", "", RegistryValueKind.String); 95 | ProtocolKey.SetValue("OneClick-Provider", "ModAssistant", RegistryValueKind.String); 96 | CommandKey.SetValue("", $"\"{Utils.ExePath}\" \"--install\" \"%1\""); 97 | } 98 | 99 | Utils.SendNotify(string.Format((string)Application.Current.FindResource("OneClick:ProtocolHandler:Registered"), Protocol)); 100 | } 101 | else 102 | { 103 | Utils.StartAsAdmin($"\"--register\" \"{Protocol}\" \"{Description}\""); 104 | } 105 | } 106 | catch (Exception e) 107 | { 108 | MessageBox.Show(e.ToString()); 109 | } 110 | 111 | if (Background) 112 | Application.Current.Shutdown(); 113 | else 114 | Pages.Options.Instance.UpdateHandlerStatus(); 115 | } 116 | 117 | public static void Unregister(string Protocol, bool Background = false) 118 | { 119 | if (IsRegistered(Protocol) == false) 120 | return; 121 | try 122 | { 123 | if (Utils.IsAdmin) 124 | { 125 | using (RegistryKey ProtocolKey = Registry.ClassesRoot.OpenSubKey(Protocol, true)) 126 | { 127 | if (ProtocolKey != null 128 | && ProtocolKey.GetValue("OneClick-Provider", "").ToString() == "ModAssistant") 129 | { 130 | Registry.ClassesRoot.DeleteSubKeyTree(Protocol); 131 | } 132 | } 133 | 134 | Utils.SendNotify(string.Format((string)Application.Current.FindResource("OneClick:ProtocolHandler:Unregistered"), Protocol)); 135 | } 136 | else 137 | { 138 | Utils.StartAsAdmin($"\"--unregister\" \"{Protocol}\""); 139 | } 140 | 141 | } 142 | catch (Exception e) 143 | { 144 | MessageBox.Show(e.ToString()); 145 | } 146 | 147 | if (Background) 148 | Application.Current.Shutdown(); 149 | else 150 | Pages.Options.Instance.UpdateHandlerStatus(); 151 | } 152 | 153 | public static bool IsRegistered(string Protocol) 154 | { 155 | RegistryKey ProtocolKey = Registry.ClassesRoot.OpenSubKey(Protocol); 156 | if (ProtocolKey != null 157 | && ProtocolKey.GetValue("OneClick-Provider", "").ToString() == "ModAssistant") 158 | return true; 159 | else 160 | return false; 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /ModAssistant/Classes/Promotions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ModAssistant 4 | { 5 | class Promotions 6 | { 7 | public static Promotion[] List = 8 | { 9 | new Promotion 10 | { 11 | ModName = "YUR Fit Calorie Tracker", 12 | Links = new List(){ 13 | new PromotionLink{Text = "Join our Discord!", Link = "https://yur.chat", TextAfterLink = " Or find us on "}, 14 | new PromotionLink{Text = "iOS", Link = "https://bit.ly/yuriphone", TextAfterLink = " and " }, 15 | new PromotionLink{Text = "Android", Link = "https://play.google.com/store/apps/details?id=com.yur", TextAfterLink = "!" } 16 | }, 17 | Active = false 18 | } 19 | }; 20 | } 21 | 22 | class Promotion 23 | { 24 | public string ModName; 25 | public List Links; 26 | public bool Active; 27 | } 28 | 29 | class PromotionLink 30 | { 31 | public string Text; 32 | public string Link; 33 | public string TextAfterLink; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ModAssistant/Classes/Updater.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Threading.Tasks; 5 | using System.Windows; 6 | using static ModAssistant.Http; 7 | 8 | namespace ModAssistant 9 | { 10 | class Updater 11 | { 12 | private static readonly string APILatestURL = "https://api.github.com/repos/bsmg/ModAssistant/releases/latest"; 13 | 14 | private static Update LatestUpdate; 15 | private static Version CurrentVersion; 16 | private static Version LatestVersion; 17 | private static bool NeedsUpdate = false; 18 | private static readonly string NewExe = Path.Combine(Path.GetDirectoryName(Utils.ExePath), "ModAssistant.exe"); 19 | private static readonly string Arguments = App.Arguments; 20 | 21 | #pragma warning disable CS0162 // Unreachable code detected 22 | public static async Task CheckForUpdate() 23 | { 24 | #if DEBUG 25 | return false; 26 | #endif 27 | 28 | var resp = await HttpClient.GetAsync(APILatestURL); 29 | var body = await resp.Content.ReadAsStringAsync(); 30 | LatestUpdate = JsonSerializer.Deserialize(body); 31 | 32 | LatestVersion = new Version(LatestUpdate.tag_name.Substring(1)); 33 | CurrentVersion = new Version(App.Version); 34 | 35 | return (LatestVersion > CurrentVersion); 36 | } 37 | #pragma warning restore CS0162 // Unreachable code detected 38 | 39 | public static async Task Run() 40 | { 41 | if (Path.GetFileName(Utils.ExePath).Equals("ModAssistant.old.exe")) RunNew(); 42 | try 43 | { 44 | NeedsUpdate = await CheckForUpdate(); 45 | } 46 | catch 47 | { 48 | Utils.SendNotify((string)Application.Current.FindResource("Updater:CheckFailed")); 49 | } 50 | 51 | if (NeedsUpdate) await StartUpdate(); 52 | } 53 | 54 | public static async Task StartUpdate() 55 | { 56 | string OldExe = Path.Combine(Path.GetDirectoryName(Utils.ExePath), "ModAssistant.old.exe"); 57 | string DownloadLink = null; 58 | 59 | foreach (Update.Asset asset in LatestUpdate.assets) 60 | { 61 | if (asset.name == "ModAssistant.exe") 62 | { 63 | DownloadLink = asset.browser_download_url; 64 | } 65 | } 66 | 67 | if (string.IsNullOrEmpty(DownloadLink)) 68 | { 69 | Utils.SendNotify((string)Application.Current.FindResource("Updater:DownloadFailed")); 70 | } 71 | else 72 | { 73 | if (File.Exists(OldExe)) 74 | { 75 | File.Delete(OldExe); 76 | } 77 | 78 | File.Move(Utils.ExePath, OldExe); 79 | 80 | await Utils.Download(DownloadLink, "", NewExe); 81 | RunNew(); 82 | } 83 | } 84 | 85 | private static void RunNew() 86 | { 87 | Process.Start(NewExe, Arguments); 88 | Application.Current.Dispatcher.Invoke(() => { Application.Current.Shutdown(); }); 89 | } 90 | } 91 | 92 | public class Update 93 | { 94 | public string url; 95 | public string assets_url; 96 | public string upload_url; 97 | public string html_url; 98 | public int id; 99 | public string node_id; 100 | public string tag_name; 101 | public string target_commitish; 102 | public string name; 103 | public bool draft; 104 | public User author; 105 | public bool prerelease; 106 | public string created_at; 107 | public string published_at; 108 | public Asset[] assets; 109 | public string tarball_url; 110 | public string zipball_url; 111 | public string body; 112 | 113 | public class Asset 114 | { 115 | public string url; 116 | public int id; 117 | public string node_id; 118 | public string name; 119 | public string label; 120 | public User uploader; 121 | public string content_type; 122 | public string state; 123 | public int size; 124 | public string created_at; 125 | public string updated_at; 126 | public string browser_download_url; 127 | } 128 | 129 | public class User 130 | { 131 | public string login; 132 | public int id; 133 | public string node_id; 134 | public string avatar_url; 135 | public string gravatar_id; 136 | public string url; 137 | public string html_url; 138 | public string followers_url; 139 | public string following_url; 140 | public string gists_url; 141 | public string starred_url; 142 | public string subscriptions_url; 143 | public string organizations_url; 144 | public string repos_url; 145 | public string events_url; 146 | public string received_events_url; 147 | public string type; 148 | public bool site_admin; 149 | 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /ModAssistant/Libs/semver/IntExtensions.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Max Hauser 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | using System.Text; 24 | 25 | namespace ModAssistant.Libs 26 | { 27 | internal static class IntExtensions 28 | { 29 | /// 30 | /// The number of digits in a non-negative number. Returns 1 for all 31 | /// negative numbers. That is ok because we are using it to calculate 32 | /// string length for a for numbers that 33 | /// aren't supposed to be negative, but when they are it is just a little 34 | /// slower. 35 | /// 36 | /// 37 | /// This approach is based on https://stackoverflow.com/a/51099524/268898 38 | /// where the poster offers performance benchmarks showing this is the 39 | /// fastest way to get a number of digits. 40 | /// 41 | public static int Digits(this int n) 42 | { 43 | if (n < 10) return 1; 44 | if (n < 100) return 2; 45 | if (n < 1_000) return 3; 46 | if (n < 10_000) return 4; 47 | if (n < 100_000) return 5; 48 | if (n < 1_000_000) return 6; 49 | if (n < 10_000_000) return 7; 50 | if (n < 100_000_000) return 8; 51 | if (n < 1_000_000_000) return 9; 52 | return 10; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /ModAssistant/OneClickStatus.xaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 57 | 63 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 86 | 91 | 92 | 97 | 102 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /ModAssistant/OneClickStatus.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Data; 4 | 5 | namespace ModAssistant 6 | { 7 | /// 8 | /// Interaction logic for OneClickStatus.xaml 9 | /// 10 | public partial class OneClickStatus : Window 11 | { 12 | public static OneClickStatus Instance; 13 | 14 | public string HistoryText 15 | { 16 | get 17 | { 18 | return HistoryTextBlock.Text; 19 | } 20 | set 21 | { 22 | Dispatcher.Invoke(new Action(() => { Instance.HistoryTextBlock.Text = value; })); 23 | } 24 | } 25 | public string MainText 26 | { 27 | get 28 | { 29 | return MainTextBlock.Text; 30 | } 31 | set 32 | { 33 | Dispatcher.Invoke(new Action(() => 34 | { 35 | Instance.MainTextBlock.Text = value; 36 | Instance.HistoryTextBlock.Text = string.IsNullOrEmpty(MainText) ? $"{value}" : $"{value}\n{HistoryText}"; 37 | })); 38 | } 39 | } 40 | 41 | public OneClickStatus() 42 | { 43 | InitializeComponent(); 44 | Instance = (App.OCIWindow == "No" && App.OCIWindow == "Notify") ? null : this; 45 | } 46 | 47 | public void StopRotation() 48 | { 49 | Ring.Style = null; 50 | } 51 | } 52 | 53 | [ValueConversion(typeof(double), typeof(double))] 54 | public class DivideDoubleByTwoConverter : IValueConverter 55 | { 56 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 57 | { 58 | if (targetType != typeof(double)) 59 | { 60 | throw new InvalidOperationException("The target must be a double"); 61 | } 62 | double d = (double)value; 63 | return ((double)d) / 2; 64 | } 65 | 66 | public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 67 | { 68 | throw new NotSupportedException(); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /ModAssistant/Pages/About.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Threading.Tasks; 4 | using System.Windows; 5 | using System.Windows.Controls; 6 | using System.Windows.Forms; 7 | using System.Windows.Navigation; 8 | using static ModAssistant.Http; 9 | using MessageBox = System.Windows.Forms.MessageBox; 10 | 11 | namespace ModAssistant.Pages 12 | { 13 | /// 14 | /// Interaction logic for Page1.xaml 15 | /// 16 | public partial class About : Page 17 | { 18 | public static About Instance = new About(); 19 | 20 | public About() 21 | { 22 | InitializeComponent(); 23 | } 24 | 25 | private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e) 26 | { 27 | Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri)); 28 | e.Handled = true; 29 | } 30 | 31 | private async void HeadpatsButton_Click(object sender, RoutedEventArgs e) 32 | { 33 | PatButton.IsEnabled = false; 34 | var success = await Task.Run(async () => await HeadPat()); 35 | 36 | PatUp.IsOpen = success; 37 | if (!success) PatButton.IsEnabled = true; 38 | } 39 | 40 | private async void HugsButton_Click(object sender, RoutedEventArgs e) 41 | { 42 | HugButton.IsEnabled = false; 43 | var success = await Task.Run(async () => await Hug()); 44 | 45 | HugUp.IsOpen = success; 46 | if (!success) HugButton.IsEnabled = true; 47 | } 48 | 49 | private async Task WeebCDN(string type) 50 | { 51 | var resp = await HttpClient.GetAsync(Utils.Constants.WeebCDNAPIURL + type); 52 | var body = await resp.Content.ReadAsStringAsync(); 53 | 54 | var response = JsonSerializer.Deserialize(body); 55 | return response.url; 56 | } 57 | 58 | private async Task HeadPat() 59 | { 60 | try 61 | { 62 | var image = await WeebCDN("pat"); 63 | PatImage.Load(image); 64 | 65 | return true; 66 | } 67 | catch (Exception ex) 68 | { 69 | var buttonType = (string)FindResource("About:HeadpatsButton"); 70 | var title = string.Format((string)FindResource("About:ImageError:Title"), buttonType); 71 | var message = string.Format((string)FindResource("About:ImageError:Message"), buttonType); 72 | 73 | MessageBox.Show($"{message}\n{ex.Message}", title, MessageBoxButtons.OK, MessageBoxIcon.Error); 74 | return false; 75 | } 76 | } 77 | 78 | private async Task Hug() 79 | { 80 | try 81 | { 82 | var image = await WeebCDN("hug"); 83 | HugImage.Load(image); 84 | 85 | return true; 86 | } 87 | catch (Exception ex) 88 | { 89 | var buttonType = (string)FindResource("About:HugsButton"); 90 | var title = string.Format((string)FindResource("About:ImageError:Title"), buttonType); 91 | var message = string.Format((string)FindResource("About:ImageError:Message"), buttonType); 92 | 93 | MessageBox.Show($"{message}\n{ex.Message}", title, MessageBoxButtons.OK, MessageBoxIcon.Error); 94 | return false; 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /ModAssistant/Pages/Intro.xaml: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 36 | 42 | 47 | 48 | 49 | 50 | 55 | 56 | 57 | 58 | 63 | 64 | 65 | 66 | 71 | 72 | 73 | 74 | 79 | 80 | 81 | 82 | 87 | 88 | 89 | 90 | 95 | 96 | 97 | 98 | 103 | 104 | 105 | 106 | 112 | 126 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /ModAssistant/Pages/Intro.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | using System.Windows.Navigation; 5 | 6 | namespace ModAssistant.Pages 7 | { 8 | /// 9 | /// Interaction logic for Intro.xaml 10 | /// 11 | public partial class Intro : Page 12 | { 13 | public static Intro Instance = new Intro(); 14 | 15 | public Intro() 16 | { 17 | InitializeComponent(); 18 | } 19 | 20 | private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e) 21 | { 22 | Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri)); 23 | e.Handled = true; 24 | } 25 | 26 | private void Disagree_Click(object sender, RoutedEventArgs e) 27 | { 28 | MainWindow.Instance.ModsButton.IsEnabled = false; 29 | Properties.Settings.Default.Agreed = false; 30 | Properties.Settings.Default.Save(); 31 | MessageBox.Show((string)FindResource("Intro:ClosingApp")); 32 | Application.Current.Shutdown(); 33 | } 34 | 35 | private void Agree_Click(object sender, RoutedEventArgs e) 36 | { 37 | if (string.IsNullOrEmpty(MainWindow.GameVersion)) 38 | { 39 | string line1 = (string)FindResource("Intro:VersionDownloadFailed"); 40 | string line2 = (string)FindResource("Intro:ModsTabDisabled"); 41 | 42 | MessageBox.Show($"{line1}.\n{line2}"); 43 | } 44 | else 45 | { 46 | MainWindow.Instance.ModsButton.IsEnabled = true; 47 | 48 | string text = (string)FindResource("Intro:ModsTabEnabled"); 49 | Utils.SendNotify(text); 50 | MainWindow.Instance.MainText = text; 51 | } 52 | Properties.Settings.Default.Agreed = true; 53 | Properties.Settings.Default.Save(); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ModAssistant/Pages/Invalid.xaml: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 40 | 47 | 54 | 55 | 61 | 62 | 63 | 64 | 70 | 71 | 72 | 73 | 79 | 80 | 81 | 82 | 88 | 94 | 95 | : 96 | 97 | 102 | 109 | 110 |