├── .gitignore ├── .vs ├── ModsUpdateUI │ └── v15 │ │ ├── .suo │ │ └── Server │ │ └── sqlite3 │ │ ├── db.lock │ │ ├── storage.ide │ │ ├── storage.ide-shm │ │ └── storage.ide-wal ├── ProjectSettings.json ├── VSWorkspaceState.json └── slnx.sqlite ├── AppUpdate ├── App.config ├── AppUpdate.csproj ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── packages.config ├── LICENSE ├── ModsUpdateUI.sln ├── ModsUpdateUI ├── App.config ├── App.xaml ├── App.xaml.cs ├── Configurations │ ├── ConfigurationManager.cs │ ├── DownloadModsConfig.cs │ ├── SoftwareInfo.cs │ └── UpdateModsConfig.cs ├── Constants.cs ├── Converters │ ├── ArrayToStringConverter.cs │ ├── IntToBooleanConverter.cs │ └── TwoNumsToPercentage.cs ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── Models │ ├── LocalModInfo.cs │ └── RemoteModInfo.cs ├── ModsUpdateUI.csproj ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── Services │ ├── ApplicationService.cs │ ├── GithubService.cs │ ├── LocalService.cs │ └── ServiceManager.cs ├── Utils │ └── Utilities.cs ├── ViewModels │ ├── ModsDownloadViewModel.cs │ └── ModsUpdateViewModel.cs ├── Views │ ├── AboutView.xaml │ ├── AboutView.xaml.cs │ ├── ModsDownloadConfigView.xaml │ ├── ModsDownloadConfigView.xaml.cs │ ├── ModsDownloadView.xaml │ ├── ModsDownloadView.xaml.cs │ ├── ModsUpdateConfigView.xaml │ ├── ModsUpdateConfigView.xaml.cs │ ├── ModsUpdateView.xaml │ └── ModsUpdateView.xaml.cs └── packages.config └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | ModsUpdateUI/obj/ 2 | ModsUpdateUI/bin/ 3 | packages/ 4 | AppUpdate/bin/ 5 | AppUpdate/obj/ 6 | .vs/ -------------------------------------------------------------------------------- /.vs/ModsUpdateUI/v15/.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevinSusen/ModsUpdateUI/fee3b5701775d3b922ddb48acfdfa4253fcad7a0/.vs/ModsUpdateUI/v15/.suo -------------------------------------------------------------------------------- /.vs/ModsUpdateUI/v15/Server/sqlite3/db.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevinSusen/ModsUpdateUI/fee3b5701775d3b922ddb48acfdfa4253fcad7a0/.vs/ModsUpdateUI/v15/Server/sqlite3/db.lock -------------------------------------------------------------------------------- /.vs/ModsUpdateUI/v15/Server/sqlite3/storage.ide: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevinSusen/ModsUpdateUI/fee3b5701775d3b922ddb48acfdfa4253fcad7a0/.vs/ModsUpdateUI/v15/Server/sqlite3/storage.ide -------------------------------------------------------------------------------- /.vs/ModsUpdateUI/v15/Server/sqlite3/storage.ide-shm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevinSusen/ModsUpdateUI/fee3b5701775d3b922ddb48acfdfa4253fcad7a0/.vs/ModsUpdateUI/v15/Server/sqlite3/storage.ide-shm -------------------------------------------------------------------------------- /.vs/ModsUpdateUI/v15/Server/sqlite3/storage.ide-wal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevinSusen/ModsUpdateUI/fee3b5701775d3b922ddb48acfdfa4253fcad7a0/.vs/ModsUpdateUI/v15/Server/sqlite3/storage.ide-wal -------------------------------------------------------------------------------- /.vs/ProjectSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "CurrentProjectSetting": null 3 | } -------------------------------------------------------------------------------- /.vs/VSWorkspaceState.json: -------------------------------------------------------------------------------- 1 | { 2 | "ExpandedNodes": [ 3 | "", 4 | "\\AppUpdate\\Properties", 5 | "\\ModsUpdateUI\\ViewModels" 6 | ], 7 | "SelectedNode": "\\.gitignore", 8 | "PreviewInSolutionExplorer": false 9 | } -------------------------------------------------------------------------------- /.vs/slnx.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevinSusen/ModsUpdateUI/fee3b5701775d3b922ddb48acfdfa4253fcad7a0/.vs/slnx.sqlite -------------------------------------------------------------------------------- /AppUpdate/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /AppUpdate/AppUpdate.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {549DF3AB-AE6D-46F0-BA51-A1D6917ED330} 8 | Exe 9 | AppUpdate 10 | AppUpdate 11 | v4.6.1 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 7.1 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 7.1 36 | 37 | 38 | 39 | ..\packages\Octokit.0.32.0\lib\net45\Octokit.dll 40 | 41 | 42 | ..\packages\ShellProgressBar.4.2.0\lib\netstandard2.0\ShellProgressBar.dll 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /AppUpdate/Program.cs: -------------------------------------------------------------------------------- 1 | using Octokit; 2 | using ShellProgressBar; 3 | using System; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Net; 7 | using System.Threading.Tasks; 8 | 9 | namespace AppUpdate 10 | { 11 | class Program 12 | { 13 | private static ProgressBar _bar; 14 | 15 | static async Task Main(string[] args) 16 | { 17 | var (uri, size) = await GetAppUriAsync(); 18 | 19 | var options = new ProgressBarOptions 20 | { 21 | ProgressBarOnBottom = true, 22 | CollapseWhenFinished = true 23 | }; 24 | using (_bar = new ProgressBar(size, "Downloading...", options)) 25 | { 26 | using (WebClient client = new WebClient()) 27 | { 28 | client.DownloadProgressChanged += Client_DownloadProgressChanged; 29 | client.DownloadFileAsync(uri, Path.GetTempPath() + Path.GetFileName(uri.AbsolutePath)); 30 | while (client.IsBusy) ; 31 | 32 | Console.WriteLine("下载完成"); 33 | Process.Start("cmd.exe", "/k ..\\update.bat ModsUpdateUI.7z"); 34 | Process.GetCurrentProcess().Kill(); 35 | } 36 | } 37 | } 38 | 39 | private static async Task<(Uri, int)> GetAppUriAsync() 40 | { 41 | GitHubClient _client = new GitHubClient(new ProductHeaderValue("Update")); 42 | Release result = null; 43 | try 44 | { 45 | result = await _client.Repository.Release.GetLatest("DevinSusen", "ModsUpdateUI"); 46 | } 47 | catch (Exception) 48 | { 49 | Console.WriteLine("网络错误"); 50 | Environment.Exit(1); 51 | } 52 | 53 | Uri u = new Uri(result.Assets[0].BrowserDownloadUrl); 54 | int size = result.Assets[0].Size; 55 | return (u, size); 56 | } 57 | 58 | private static void Client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) 59 | { 60 | for (int i = 0; i < e.BytesReceived; i++) 61 | _bar.Tick(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /AppUpdate/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("AppUpdate")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("AppUpdate")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("549df3ab-ae6d-46f0-ba51-a1d6917ed330")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 33 | // 方法是按如下所示使用“*”: : 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /AppUpdate/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Devin 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 | -------------------------------------------------------------------------------- /ModsUpdateUI.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.329 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModsUpdateUI", "ModsUpdateUI\ModsUpdateUI.csproj", "{F14E4732-17E8-4E65-BD5E-D210A298E771}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppUpdate", "AppUpdate\AppUpdate.csproj", "{549DF3AB-AE6D-46F0-BA51-A1D6917ED330}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {F14E4732-17E8-4E65-BD5E-D210A298E771}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {F14E4732-17E8-4E65-BD5E-D210A298E771}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {F14E4732-17E8-4E65-BD5E-D210A298E771}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {F14E4732-17E8-4E65-BD5E-D210A298E771}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {549DF3AB-AE6D-46F0-BA51-A1D6917ED330}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {549DF3AB-AE6D-46F0-BA51-A1D6917ED330}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {549DF3AB-AE6D-46F0-BA51-A1D6917ED330}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {549DF3AB-AE6D-46F0-BA51-A1D6917ED330}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {B9E08F78-BAC9-485D-9CC6-F5FE20A67DE6} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /ModsUpdateUI/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ModsUpdateUI/App.xaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ModsUpdateUI/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace ModsUpdateUI 4 | { 5 | /// 6 | /// App.xaml 的交互逻辑 7 | /// 8 | public partial class App : Application 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ModsUpdateUI/Configurations/ConfigurationManager.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.IO; 3 | 4 | namespace ModsUpdateUI.Configurations 5 | { 6 | public static class ConfigurationManager 7 | { 8 | #region Download Configuration 9 | private static DownloadModsConfig _downloadModsConfig = null; 10 | 11 | public static DownloadModsConfig DownloadModsConfiguration 12 | { 13 | get 14 | { 15 | if (_downloadModsConfig == null) 16 | _downloadModsConfig = LoadConfig(Constants.DownloadModsConfigPath); 17 | return _downloadModsConfig; 18 | } 19 | } 20 | #endregion 21 | 22 | #region Update Mods Configuration 23 | private static UpdateModsConfig _updateModsConfig = null; 24 | 25 | public static UpdateModsConfig UpdateModsConfiguration 26 | { 27 | get 28 | { 29 | if (_updateModsConfig == null) 30 | _updateModsConfig = LoadConfig(Constants.UpdateModsConfigPath); 31 | return _updateModsConfig; 32 | } 33 | } 34 | #endregion 35 | 36 | #region Load Configuration 37 | 38 | private static T LoadConfig(string fileName) where T : new() 39 | { 40 | if (!File.Exists(fileName)) 41 | return InitConfiguration(fileName); 42 | string s = File.ReadAllText(fileName); 43 | return JsonConvert.DeserializeObject(s); 44 | } 45 | 46 | private static T InitConfiguration(string fileName) where T : new() 47 | { 48 | T t = new T(); 49 | SaveConfiguration(t, fileName); 50 | return t; 51 | } 52 | #endregion 53 | 54 | #region Save Configuration 55 | 56 | public static void SaveConfiguration(T t, string fileName) where T : new() 57 | { 58 | string s = JsonConvert.SerializeObject(t, Formatting.Indented); 59 | File.WriteAllText(fileName, s); 60 | } 61 | 62 | #endregion 63 | 64 | #region Software Mata Data 65 | 66 | private static SoftwareInfo _sinfo = null; 67 | 68 | public static SoftwareInfo SoftwareInformation 69 | { 70 | get 71 | { 72 | if (_sinfo == null) 73 | _sinfo = LoadConfig(Constants.SoftwareMataDataPath); 74 | return _sinfo; 75 | } 76 | } 77 | 78 | #endregion 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /ModsUpdateUI/Configurations/DownloadModsConfig.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.IO; 4 | 5 | namespace ModsUpdateUI.Configurations 6 | { 7 | public class DownloadModsConfig 8 | { 9 | /// 10 | /// 下载目录 11 | /// 12 | [JsonProperty("DownloadDirectory")] 13 | public string DownloadDirectory { get; set; } = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); 14 | 15 | /// 16 | /// 是否自动解压 17 | /// 18 | [JsonProperty("IsAutoDecompress")] 19 | public bool IsAutoDecompress { get; set; } = false; 20 | 21 | /// 22 | /// 解压后是否删除原文件 23 | /// 24 | [JsonProperty("IsDeleteFileWhenDecompress")] 25 | public bool IsDeleteFileWhenDecompress { get; set; } = true; 26 | 27 | /// 28 | /// Github账户名 29 | /// 30 | [JsonProperty("Owner")] 31 | public string Owner { get; set; } = "phorcys"; 32 | 33 | /// 34 | /// 代码库 35 | /// 36 | [JsonProperty("Repository")] 37 | public string Repository { get; set; } = "Taiwu_mods"; 38 | 39 | /// 40 | /// 该配置是否已经合法 41 | /// 42 | /// 如果true,则配置生效 43 | [JsonIgnore] 44 | public bool IsOK { get => !string.IsNullOrWhiteSpace(Owner) && !string.IsNullOrWhiteSpace(Repository); } 45 | 46 | /// 47 | /// 是否可以下载 48 | /// 49 | /// 如果true,则可以开始下载 50 | [JsonIgnore] 51 | public bool CanDownload { get => Directory.Exists(DownloadDirectory); } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ModsUpdateUI/Configurations/SoftwareInfo.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace ModsUpdateUI.Configurations 5 | { 6 | public class SoftwareInfo 7 | { 8 | [JsonProperty("Author")] 9 | public string Author { get; set; } = "Devin Tung"; 10 | 11 | [JsonProperty("HomePage")] 12 | public Uri HomePage { get; set; } = new Uri(@"https://github.com/DevinSusen/ModsUpdateUI"); 13 | 14 | [JsonProperty("Version")] 15 | public string Version { get; set; } = "1.2"; 16 | 17 | [JsonProperty("ReleaseTime")] 18 | public DateTime ReleaseTime { get; set; } = DateTime.Now; 19 | 20 | [JsonProperty("Dependencies")] 21 | public Dependency[] Dependencies { get; set; } 22 | } 23 | 24 | public class Dependency 25 | { 26 | [JsonProperty("Name")] 27 | public string Name { get; set; } 28 | 29 | [JsonProperty("Author")] 30 | public string Author { get; set; } 31 | 32 | [JsonProperty("License")] 33 | public string License { get; set; } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ModsUpdateUI/Configurations/UpdateModsConfig.cs: -------------------------------------------------------------------------------- 1 | using ModsUpdateUI.Utils; 2 | using Newtonsoft.Json; 3 | using System.IO; 4 | 5 | namespace ModsUpdateUI.Configurations 6 | { 7 | public class UpdateModsConfig 8 | { 9 | /// 10 | /// Mod的存放目录 11 | /// 12 | [JsonProperty("ModsDirectory")] 13 | public string ModsDirectory { get; set; } = Utilities.GetTheScrollOfTaiwuModsFolder(); 14 | 15 | /// 16 | /// 所有者 17 | /// 18 | [JsonProperty("Owner")] 19 | public string Owner { get; set; } = "phorcys"; 20 | 21 | /// 22 | /// 仓库名 23 | /// 24 | [JsonProperty("Repository")] 25 | public string Repository { get; set; } = "Taiwu_mods"; 26 | 27 | /// 28 | /// 要查看的文件类型 29 | /// 30 | [JsonProperty("FileType")] 31 | public string FileType { get; set; } = "application/zip"; 32 | 33 | /// 34 | /// 存储信息文件 35 | /// 36 | [JsonProperty("InfoFile")] 37 | public string InfoFile { get; set; } = "Info.json"; 38 | 39 | /// 40 | /// 该配置是否已经合法 41 | /// 42 | /// 如果true,则配置生效 43 | [JsonIgnore] 44 | public bool IsOK { get => Directory.Exists(ModsDirectory); } 45 | 46 | /// 47 | /// 是否可以可以更新 48 | /// 49 | /// 如果true,则可以开始更新 50 | [JsonIgnore] 51 | public bool CanUpdate { get => !string.IsNullOrWhiteSpace(Owner) && !string.IsNullOrWhiteSpace(Repository); } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ModsUpdateUI/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace ModsUpdateUI 2 | { 3 | public static class Constants 4 | { 5 | #region Steam Key 6 | public const string SteamRegistryKey64 = @"HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Valve\Steam"; 7 | public const string SteamRegistryKey32 = @"HKEY_LOCAL_MACHINE\SOFTWARE\Valve\Steam"; 8 | #endregion 9 | 10 | #region TaiWu 11 | public const string TaiWuModsFolder = @"\steamapps\common\The Scroll Of Taiwu\Mods"; 12 | 13 | public const string TaiWuSaveFilesFolder = @"\steamapps\common\The Scroll Of Taiwu\The Scroll Of Taiwu Alpha V1.0_Data\SaveFiles"; 14 | #endregion 15 | 16 | #region Configuration 17 | public const string DownloadModsConfigPath = @"DownloadModsConfig.json"; 18 | public const string UpdateModsConfigPath = @"UpdateModsConfig.json"; 19 | 20 | public const string SoftwareMataDataPath = @"MataData.json"; 21 | #endregion 22 | 23 | #region File Type 24 | 25 | public const string ZipType = @"application/zip"; 26 | public const string JsonType = @"application/json"; 27 | 28 | #endregion 29 | 30 | #region CheckUpdate 31 | 32 | public const string UpdateApplication = @"AppUpdate.exe"; 33 | 34 | #endregion 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ModsUpdateUI/Converters/ArrayToStringConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace ModsUpdateUI.Converters 6 | { 7 | public class ArrayToStringConverter : IValueConverter 8 | { 9 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 10 | { 11 | string splitCode = parameter as string; 12 | if (!(value is string[] vs)) 13 | return ""; 14 | if (!string.IsNullOrEmpty(splitCode)) 15 | return string.Join(splitCode, vs); 16 | else return string.Join(",", vs); 17 | } 18 | 19 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 20 | { 21 | throw new NotImplementedException(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ModsUpdateUI/Converters/IntToBooleanConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace ModsUpdateUI.Converters 6 | { 7 | public class IntToBooleanConverter : IValueConverter 8 | { 9 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 10 | { 11 | int v = System.Convert.ToInt32(value); 12 | if (v > 0) 13 | return true; 14 | return false; 15 | } 16 | 17 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 18 | { 19 | throw new NotImplementedException(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ModsUpdateUI/Converters/TwoNumsToPercentage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace ModsUpdateUI.Converters 6 | { 7 | class TwoNumsToPercentage : IMultiValueConverter 8 | { 9 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 10 | { 11 | int v = System.Convert.ToInt32(values[0]); 12 | bool isSuc = int.TryParse(values[1].ToString(), out int all); 13 | if (all == 0) 14 | return 0; 15 | return v * 100 / all; 16 | } 17 | 18 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 19 | { 20 | throw new NotImplementedException(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ModsUpdateUI/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  18 | 19 | 20 | 21 | 101 | 102 | 103 | 108 | 124 | 125 | 126 | 186 | 187 | 188 | 189 | 190 | -------------------------------------------------------------------------------- /ModsUpdateUI/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using MahApps.Metro.Controls; 2 | using ModsUpdateUI.Configurations; 3 | using ModsUpdateUI.Services; 4 | using ModsUpdateUI.Views; 5 | using System.Diagnostics; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace ModsUpdateUI 10 | { 11 | /// 12 | /// MainWindow.xaml 的交互逻辑 13 | /// 14 | public partial class MainWindow : MetroWindow 15 | { 16 | public MainWindow() 17 | { 18 | InitializeComponent(); 19 | 20 | CheckUpdateAsync(); 21 | } 22 | 23 | private async void CheckUpdateAsync() 24 | { 25 | if (await CanUpdate()) 26 | { 27 | var result = MessageBox.Show("有更新,是否下载?", "检查更新", MessageBoxButton.YesNo); 28 | if (result == MessageBoxResult.Yes) 29 | { 30 | Process.Start(@"https://github.com/DevinSusen/ModsUpdateUI/releases"); 31 | } 32 | } 33 | } 34 | 35 | private async Task CanUpdate() 36 | { 37 | ApplicationService service = new ApplicationService(); 38 | return await service.CheckUpdateAsync(); 39 | } 40 | 41 | private async void CheckUpdateButton_Click(object sender, RoutedEventArgs e) 42 | { 43 | if(await CanUpdate()) 44 | { 45 | var result = MessageBox.Show("有更新,是否下载?", "检查更新", MessageBoxButton.YesNo); 46 | if (result == MessageBoxResult.Yes) 47 | { 48 | Process.Start(@"https://github.com/DevinSusen/ModsUpdateUI/releases"); 49 | } 50 | } 51 | else 52 | { 53 | MessageBox.Show("当前已是最新版本", "检查更新"); 54 | } 55 | } 56 | 57 | private void WikiButton_Click(object sender, RoutedEventArgs e) 58 | { 59 | Process.Start(@"https://github.com/DevinSusen/ModsUpdateUI"); 60 | } 61 | 62 | private void AboutButton_Click(object sender, RoutedEventArgs e) 63 | { 64 | AboutView view = new AboutView(); 65 | view.Show(); 66 | } 67 | 68 | private void ModsDownloadConfigButton_Click(object sender, RoutedEventArgs e) 69 | { 70 | ModsDownloadConfigView view = new ModsDownloadConfigView(); 71 | view.Show(); 72 | } 73 | 74 | private void ModsUpdateConfigButton_Click(object sender, RoutedEventArgs e) 75 | { 76 | ModsUpdateConfigView view = new ModsUpdateConfigView(); 77 | view.Show(); 78 | } 79 | 80 | private void DownloadModsButton_Click(object sender, RoutedEventArgs e) 81 | { 82 | if (!ConfigurationManager.DownloadModsConfiguration.IsOK) 83 | return; 84 | ModsDownloadView view = new ModsDownloadView(); 85 | view.Show(); 86 | } 87 | 88 | private void UpdateModsButton_Click(object sender, RoutedEventArgs e) 89 | { 90 | if (!ConfigurationManager.UpdateModsConfiguration.IsOK) 91 | return; 92 | ModsUpdateView view = new ModsUpdateView(); 93 | view.Show(); 94 | } 95 | 96 | //private void PackButton_Click(object sender, RoutedEventArgs e) 97 | //{ 98 | // string email = EmailTextBox.Text.Trim(); 99 | // string password = EmailPasswordBox.Password; 100 | // if (!IsValidEmail(email)) 101 | // { 102 | // //TODO: Message 103 | // return; 104 | // } 105 | // string path = CompressSaveFiles(Utils.Utilities.GetTheScrollOfTaiwuSaveFilesFolder()); 106 | // MailMessage mail = new MailMessage(email, email); 107 | // mail.Subject = "存档"; 108 | // mail.SubjectEncoding = Encoding.UTF8; 109 | // mail.Attachments.Add(new Attachment(path)); 110 | 111 | // SmtpClient client = new SmtpClient(); 112 | //} 113 | 114 | //private bool IsValidEmail(string email) 115 | //{ 116 | // try 117 | // { 118 | // var addr = new MailAddress(email); 119 | // return addr.Address == email; 120 | // } 121 | // catch 122 | // { 123 | // return false; 124 | // } 125 | //} 126 | 127 | //private string CompressSaveFiles(string filePath) 128 | //{ 129 | // Process proc = new Process(); 130 | // string basePath = new DirectoryInfo("..").FullName + Path.DirectorySeparatorChar + @"tools"; 131 | // string exePath = basePath + @"\7za.exe"; 132 | // proc.StartInfo.FileName = exePath; 133 | // string compFile = basePath + @"\Save.7z"; 134 | // proc.StartInfo.Arguments = "a " + compFile + " " + filePath; 135 | // proc.StartInfo.CreateNoWindow = false; 136 | // proc.Start(); 137 | // proc.WaitForExit(); 138 | // proc.Close(); 139 | // return compFile; 140 | //} 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /ModsUpdateUI/Models/LocalModInfo.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | 6 | namespace ModsUpdateUI.Models 7 | { 8 | public class LocalModInfo 9 | { 10 | /// 11 | /// 标识 12 | /// 13 | [JsonProperty("Id")] 14 | public string Id { get; set; } 15 | 16 | /// 17 | /// 显示名 18 | /// 19 | [JsonProperty("DisplayName")] 20 | public string DisplayName { get; set; } 21 | 22 | /// 23 | /// 作者 24 | /// 25 | [JsonProperty("Author")] 26 | public string Author { get; set; } 27 | 28 | /// 29 | /// 版本号 30 | /// 31 | [JsonProperty("Version")] 32 | public string Version { get; set; } 33 | 34 | /// 35 | /// 程序集名称 36 | /// 37 | [JsonProperty("AssemblyName")] 38 | public string AssemblyName { get; set; } 39 | 40 | /// 41 | /// 加载方法 42 | /// 43 | [JsonProperty("EntryMethod")] 44 | public string EntryMethod { get; set; } 45 | 46 | /// 47 | /// 该Mod的前置需求 48 | /// 49 | [JsonProperty("Requirements")] 50 | public string[] Requirements { get; set; } 51 | 52 | /// 53 | /// 主页 54 | /// 55 | [JsonProperty("HomePage")] 56 | public Uri HomePage { get; set; } 57 | 58 | /// 59 | /// Mod地址 60 | /// 61 | [JsonProperty("Repository")] 62 | public string Repository { get; set; } 63 | 64 | /// 65 | /// 简介 66 | /// 67 | [JsonProperty("Description")] 68 | public string Description { get; set; } 69 | 70 | /// 71 | /// 管理器版本 72 | /// 73 | [JsonProperty("ManagerVersion")] 74 | public string ManagerVersion { get; set; } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /ModsUpdateUI/Models/RemoteModInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace ModsUpdateUI.Models 5 | { 6 | public class RemoteModInfo 7 | { 8 | /// 9 | /// Mod的唯一标识符 10 | /// 11 | public int Id { get; set; } // "id": 10918193 12 | 13 | /// 14 | /// Mod名称 15 | /// 16 | public string Name { get; set; } // "name": "AllWin-1.0.1.zip" 17 | 18 | /// 19 | /// 文件类型 20 | /// 21 | public string ContentType { get; set; } // "content_type": "application/zip", 22 | 23 | /// 24 | /// 版本 25 | /// 26 | public string Version // "name": "AllWin-1.0.1.zip" -> "1.0.1" 27 | { 28 | get 29 | { 30 | string fileName = Path.GetFileNameWithoutExtension(Name); 31 | int idx = fileName.IndexOf('-'); 32 | if (idx == -1) 33 | return "?"; 34 | return fileName.Substring(idx+1); 35 | } 36 | } 37 | 38 | /// 39 | /// 大小 以Byte为单位 40 | /// 41 | public int Size { get; set; } // "size": 2723, 42 | 43 | /// 44 | /// 最后更新时间 45 | /// 46 | public DateTime Updated { get; set; }// "updated_at": "2019-02-04T08:56:54Z", 47 | 48 | /// 49 | /// 下载地址 50 | /// 51 | public string BrowserDownloadUrl { get; set; } // "browser_download_url": "https://github.com/phorcys/Taiwu_mods/releases/download/release190204.1/AllWin-1.0.1.zip" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ModsUpdateUI/ModsUpdateUI.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {F14E4732-17E8-4E65-BD5E-D210A298E771} 8 | WinExe 9 | ModsUpdateUI 10 | ModsUpdateUI 11 | v4.6.1 12 | 512 13 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 4 15 | true 16 | true 17 | 18 | 19 | AnyCPU 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | 28 | 29 | AnyCPU 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 7.1 37 | 38 | 39 | 40 | ..\packages\ControlzEx.3.0.2.4\lib\net45\ControlzEx.dll 41 | 42 | 43 | ..\packages\MahApps.Metro.1.6.5\lib\net46\MahApps.Metro.dll 44 | 45 | 46 | ..\packages\MaterialDesignColors.1.1.3\lib\net45\MaterialDesignColors.dll 47 | 48 | 49 | ..\packages\MaterialDesignThemes.2.5.0.1205\lib\net45\MaterialDesignThemes.Wpf.dll 50 | 51 | 52 | ..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll 53 | 54 | 55 | ..\packages\Octokit.0.32.0\lib\net45\Octokit.dll 56 | 57 | 58 | 59 | 60 | 61 | 62 | 4.0 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | MSBuild:Compile 71 | Designer 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | AboutView.xaml 90 | 91 | 92 | ModsDownloadConfigView.xaml 93 | 94 | 95 | ModsDownloadView.xaml 96 | 97 | 98 | ModsUpdateView.xaml 99 | 100 | 101 | ModsUpdateConfigView.xaml 102 | 103 | 104 | MSBuild:Compile 105 | Designer 106 | 107 | 108 | App.xaml 109 | Code 110 | 111 | 112 | MainWindow.xaml 113 | Code 114 | 115 | 116 | Designer 117 | MSBuild:Compile 118 | 119 | 120 | Designer 121 | MSBuild:Compile 122 | 123 | 124 | Designer 125 | MSBuild:Compile 126 | 127 | 128 | Designer 129 | MSBuild:Compile 130 | 131 | 132 | Designer 133 | MSBuild:Compile 134 | 135 | 136 | 137 | 138 | 139 | 140 | Code 141 | 142 | 143 | True 144 | True 145 | Resources.resx 146 | 147 | 148 | True 149 | Settings.settings 150 | True 151 | 152 | 153 | ResXFileCodeGenerator 154 | Resources.Designer.cs 155 | 156 | 157 | 158 | SettingsSingleFileGenerator 159 | Settings.Designer.cs 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /ModsUpdateUI/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // 有关程序集的一般信息由以下 8 | // 控制。更改这些特性值可修改 9 | // 与程序集关联的信息。 10 | [assembly: AssemblyTitle("ModsUpdateUI")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("ModsUpdateUI")] 15 | [assembly: AssemblyCopyright("Copyright © 2019")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // 将 ComVisible 设置为 false 会使此程序集中的类型 20 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 21 | //请将此类型的 ComVisible 特性设置为 true。 22 | [assembly: ComVisible(false)] 23 | 24 | //若要开始生成可本地化的应用程序,请设置 25 | //.csproj 文件中的 CultureYouAreCodingWith 26 | //例如,如果您在源文件中使用的是美国英语, 27 | //使用的是美国英语,请将 设置为 en-US。 然后取消 28 | //对以下 NeutralResourceLanguage 特性的注释。 更新 29 | //以下行中的“en-US”以匹配项目文件中的 UICulture 设置。 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //主题特定资源词典所处位置 36 | //(未在页面中找到资源时使用, 37 | //或应用程序资源字典中找到时使用) 38 | ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置 39 | //(未在页面中找到资源时使用, 40 | //、应用程序或任何主题专用资源字典中找到时使用) 41 | )] 42 | 43 | 44 | // 程序集的版本信息由下列四个值组成: 45 | // 46 | // 主版本 47 | // 次版本 48 | // 生成号 49 | // 修订号 50 | // 51 | // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 52 | // 方法是按如下所示使用“*”: : 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /ModsUpdateUI/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本: 4.0.30319.42000 5 | // 6 | // 对此文件的更改可能导致不正确的行为,如果 7 | // 重新生成代码,则所做更改将丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ModsUpdateUI.Properties 12 | { 13 | 14 | 15 | /// 16 | /// 强类型资源类,用于查找本地化字符串等。 17 | /// 18 | // 此类是由 StronglyTypedResourceBuilder 19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 20 | // 若要添加或删除成员,请编辑 .ResX 文件,然后重新运行 ResGen 21 | // (以 /str 作为命令选项),或重新生成 VS 项目。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// 返回此类使用的缓存 ResourceManager 实例。 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ModsUpdateUI.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// 覆盖当前线程的 CurrentUICulture 属性 56 | /// 使用此强类型的资源类的资源查找。 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /ModsUpdateUI/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /ModsUpdateUI/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ModsUpdateUI.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ModsUpdateUI/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /ModsUpdateUI/Services/ApplicationService.cs: -------------------------------------------------------------------------------- 1 | using Octokit; 2 | using System.Threading.Tasks; 3 | 4 | namespace ModsUpdateUI.Services 5 | { 6 | internal class ApplicationService 7 | { 8 | public async Task CheckUpdateAsync() 9 | { 10 | GithubService service = new GithubService("DevinSusen", "ModsUpdateUI"); 11 | var result = await service.GetLastestReleaseAsync(); 12 | double newVersion = double.Parse(result.TagName.Substring(1)); 13 | double oldVersion = double.Parse(Configurations.ConfigurationManager.SoftwareInformation.Version); 14 | return newVersion > oldVersion; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ModsUpdateUI/Services/GithubService.cs: -------------------------------------------------------------------------------- 1 | using ModsUpdateUI.Models; 2 | using Octokit; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace ModsUpdateUI.Services 7 | { 8 | public class GithubService 9 | { 10 | private GitHubClient _client = new GitHubClient(new ProductHeaderValue("user")); 11 | 12 | public string Owner { get; } 13 | 14 | public string Repos { get; } 15 | 16 | public GithubService(string owner, string repos) 17 | { 18 | Owner = owner; 19 | Repos = repos; 20 | } 21 | 22 | public async Task GetLastestReleaseAsync() => await _client.Repository.Release.GetLatest(Owner, Repos); 23 | 24 | public async Task> GetLastestModsAsync() 25 | { 26 | Release result; 27 | List mods = new List(); 28 | try 29 | { 30 | result = await _client.Repository.Release.GetLatest(Owner, Repos); 31 | } 32 | catch (System.Exception) 33 | { 34 | return mods; 35 | } 36 | 37 | foreach (var i in result.Assets) 38 | { 39 | RemoteModInfo info = new RemoteModInfo 40 | { 41 | BrowserDownloadUrl = i.BrowserDownloadUrl, 42 | ContentType = i.ContentType, 43 | Id = i.Id, 44 | Name = i.Name, 45 | Size = i.Size, 46 | Updated = i.UpdatedAt.DateTime 47 | }; 48 | mods.Add(info); 49 | } 50 | return mods; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ModsUpdateUI/Services/LocalService.cs: -------------------------------------------------------------------------------- 1 | using ModsUpdateUI.Models; 2 | using Newtonsoft.Json; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | 6 | namespace ModsUpdateUI.Services 7 | { 8 | public class LocalService 9 | { 10 | /// 11 | /// 本机存放目录 12 | /// 13 | public string LocalPath { get; } 14 | 15 | /// 16 | /// 元信息文件 17 | /// 18 | public string InfoFileName { get; } 19 | 20 | /// 21 | /// 构造LocalService 22 | /// 23 | /// 本机存放目录 24 | /// 元信息文件 25 | public LocalService(string path, string infoFileName) 26 | { 27 | LocalPath = path; 28 | InfoFileName = infoFileName; 29 | } 30 | 31 | /// 32 | /// 获得本机已下载的Mod 33 | /// 34 | /// Mods文件夹路径 35 | /// Mod元信息文件 36 | /// 所有Mod信息的集合 37 | public List FromDirectory() 38 | { 39 | List infos = new List(); 40 | 41 | foreach (var i in Directory.EnumerateDirectories(LocalPath)) 42 | { 43 | DirectoryInfo info = new DirectoryInfo(i); 44 | string infoFilePath = info.FullName + @"\" + InfoFileName; 45 | if (!File.Exists(infoFilePath)) 46 | continue; 47 | string content = File.ReadAllText(infoFilePath); 48 | infos.Add(JsonConvert.DeserializeObject(content)); 49 | } 50 | return infos; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ModsUpdateUI/Services/ServiceManager.cs: -------------------------------------------------------------------------------- 1 | using ModsUpdateUI.Configurations; 2 | 3 | namespace ModsUpdateUI.Services 4 | { 5 | public static class ServiceManager 6 | { 7 | private static LocalService _localService = null; 8 | 9 | public static LocalService LocalService 10 | { 11 | get 12 | { 13 | if (_localService == null) 14 | _localService = new LocalService(ConfigurationManager.UpdateModsConfiguration.ModsDirectory, ConfigurationManager.UpdateModsConfiguration.InfoFile); 15 | return _localService; 16 | } 17 | } 18 | 19 | private static GithubService _githubService = null; 20 | 21 | public static GithubService GithubService 22 | { 23 | get 24 | { 25 | if (_githubService == null) 26 | _githubService = new GithubService(ConfigurationManager.DownloadModsConfiguration.Owner, ConfigurationManager.DownloadModsConfiguration.Repository); 27 | return _githubService; 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ModsUpdateUI/Utils/Utilities.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32; 2 | 3 | namespace ModsUpdateUI.Utils 4 | { 5 | public static class Utilities 6 | { 7 | /// 8 | /// Get Steam's installation path 9 | /// 10 | /// null if can't find it. The path if it exists. 11 | public static string GetSteamFolder() 12 | { 13 | object value = Registry.GetValue(Constants.SteamRegistryKey64, @"InstallPath", null); 14 | if(value == null) 15 | value = Registry.GetValue(Constants.SteamRegistryKey32, @"InstallPath", null); 16 | if (value == null) 17 | return null; 18 | return value as string; 19 | } 20 | 21 | /// 22 | /// Get the mods folder of The Scroll Of Taiwu. 23 | /// 24 | /// empty string if can't find it 25 | public static string GetTheScrollOfTaiwuModsFolder() 26 | { 27 | string steamPath = GetSteamFolder(); 28 | if (steamPath == null) 29 | return ""; 30 | return steamPath + Constants.TaiWuModsFolder; 31 | } 32 | 33 | public static string GetTheScrollOfTaiwuSaveFilesFolder() 34 | { 35 | string steamPath = GetSteamFolder(); 36 | if (steamPath == null) 37 | return ""; 38 | return steamPath + Constants.TaiWuSaveFilesFolder; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ModsUpdateUI/ViewModels/ModsDownloadViewModel.cs: -------------------------------------------------------------------------------- 1 | using ModsUpdateUI.Models; 2 | using ModsUpdateUI.Services; 3 | using System.Collections.Generic; 4 | using System.Collections.ObjectModel; 5 | using System.ComponentModel; 6 | using System.Runtime.CompilerServices; 7 | using System.Linq; 8 | using System.IO; 9 | using System; 10 | using ModsUpdateUI.Configurations; 11 | using MaterialDesignThemes.Wpf; 12 | using System.IO.Compression; 13 | using System.Threading.Tasks; 14 | 15 | namespace ModsUpdateUI.ViewModels 16 | { 17 | public class ModsDownloadViewModel : INotifyPropertyChanged 18 | { 19 | private List _allItems; 20 | private ObservableCollection _modItems; 21 | public ObservableCollection ModItems 22 | { 23 | get => _modItems; 24 | private set 25 | { 26 | _modItems = value; 27 | OnPropertyChanged("ModItems"); 28 | } 29 | } 30 | 31 | private readonly ISnackbarMessageQueue _snackbarMessQueue; 32 | 33 | private string _fileTypeFilter; 34 | public string FileTypeFilter 35 | { 36 | get => _fileTypeFilter; 37 | set 38 | { 39 | _fileTypeFilter = value; 40 | OnPropertyChanged("FileTypeFilter"); 41 | GetListByFileType(value); 42 | } 43 | } 44 | 45 | private List _fileTypes; 46 | public List FileTypes 47 | { 48 | get => _fileTypes; 49 | set 50 | { 51 | _fileTypes = value; 52 | OnPropertyChanged("FileTypes"); 53 | } 54 | } 55 | 56 | private List _downloadItems; 57 | public List DownloadItems 58 | { 59 | get => _downloadItems; 60 | set 61 | { 62 | _downloadItems = value; 63 | OnPropertyChanged("DownloadItems"); 64 | } 65 | } 66 | 67 | private int _leavingCount; 68 | public int LeavingCount 69 | { 70 | get => _leavingCount; 71 | set 72 | { 73 | _leavingCount = value; 74 | OnPropertyChanged("LeavingCount"); 75 | } 76 | } 77 | 78 | private int _downloadedCount; 79 | public int DownloadedCount 80 | { 81 | get => _downloadedCount; 82 | set 83 | { 84 | _downloadedCount = value; 85 | OnPropertyChanged("DownloadedCount"); 86 | } 87 | } 88 | 89 | private string _message; 90 | public string Message 91 | { 92 | get => _message; 93 | set 94 | { 95 | _message = value; 96 | OnPropertyChanged("Message"); 97 | _snackbarMessQueue.Enqueue(value); 98 | } 99 | } 100 | 101 | #region Download 102 | private System.Net.WebClient _client; 103 | private System.Net.WebClient Client 104 | { 105 | get 106 | { 107 | if (_client == null) 108 | _client = new System.Net.WebClient(); 109 | return _client; 110 | } 111 | } 112 | 113 | public async Task DownloadAsync() 114 | { 115 | await Task.Run(() => DownloadMod()); 116 | } 117 | 118 | private void DownloadMod() 119 | { 120 | DownloadedCount = 0; 121 | LeavingCount = DownloadItems.Count; 122 | if (LeavingCount > 0) 123 | { 124 | Client.DownloadFileCompleted += Client_DownloadFileCompleted; 125 | Client.DownloadFileAsync(new Uri(DownloadItems[0].ModInfo.BrowserDownloadUrl), 126 | ConfigurationManager.DownloadModsConfiguration.DownloadDirectory + Path.DirectorySeparatorChar + DownloadItems[0].ModInfo.Name); 127 | while(Client.IsBusy); 128 | } 129 | else Message = "未选择待下载项目"; 130 | } 131 | 132 | private void Client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) 133 | { 134 | if (ConfigurationManager.DownloadModsConfiguration.IsAutoDecompress) 135 | { 136 | if (DownloadItems[0].ModInfo.ContentType == Constants.ZipType) 137 | { 138 | string path = ConfigurationManager.DownloadModsConfiguration.DownloadDirectory + Path.DirectorySeparatorChar + DownloadItems[0].ModInfo.Name; 139 | DecompressFile(path, ConfigurationManager.DownloadModsConfiguration.IsDeleteFileWhenDecompress); 140 | } 141 | } 142 | DownloadItems.RemoveAt(0); 143 | DownloadedCount += 1; 144 | LeavingCount -= 1; 145 | if (LeavingCount > 0) 146 | { 147 | Client.DownloadFileAsync(new Uri(DownloadItems[0].ModInfo.BrowserDownloadUrl), 148 | ConfigurationManager.DownloadModsConfiguration.DownloadDirectory + Path.DirectorySeparatorChar + DownloadItems[0].ModInfo.Name); 149 | } 150 | else 151 | { 152 | Message = "下载完毕"; 153 | Client.DownloadFileCompleted -= Client_DownloadFileCompleted; 154 | } 155 | } 156 | 157 | private void DecompressFile(string filePath, bool canDelete) 158 | { 159 | try 160 | { 161 | ZipFile.ExtractToDirectory(filePath, ConfigurationManager.DownloadModsConfiguration.DownloadDirectory); 162 | } 163 | catch (Exception) 164 | { 165 | Message = "出现错误,请检查您的网络,并重新尝试。"; 166 | if(File.Exists(filePath)) 167 | File.Delete(filePath); 168 | return; 169 | } 170 | 171 | if (canDelete) 172 | File.Delete(filePath); 173 | } 174 | 175 | #endregion 176 | 177 | #region Show 178 | 179 | public void GetAll() 180 | { 181 | ModItems = new ObservableCollection(_allItems); 182 | } 183 | 184 | public void GetUndownloaded() 185 | { 186 | var result = from mod in _allItems 187 | where !mod.IsExists 188 | select mod; 189 | ModItems = new ObservableCollection(result); 190 | } 191 | 192 | public void GetDownloaded() 193 | { 194 | var result = from mod in _allItems 195 | where mod.IsExists 196 | select mod; 197 | ModItems = new ObservableCollection(result); 198 | } 199 | 200 | public void GetListByFileType(string fileType) 201 | { 202 | if (fileType == "All") 203 | GetAll(); 204 | else 205 | { 206 | var result = from mod in _allItems 207 | where mod.ModInfo.ContentType == fileType 208 | select mod; 209 | ModItems = new ObservableCollection(result); 210 | } 211 | } 212 | 213 | #endregion 214 | 215 | #region Constructor 216 | 217 | public ModsDownloadViewModel(ISnackbarMessageQueue messageQueue) 218 | { 219 | _snackbarMessQueue = messageQueue ?? throw new ArgumentNullException(nameof(messageQueue)); 220 | InitAsync(); 221 | } 222 | 223 | private async void InitAsync() 224 | { 225 | var result = await ServiceManager.GithubService.GetLastestModsAsync(); 226 | _allItems = DownloadModItem.FromRemoteList(result); 227 | ModItems = new ObservableCollection(_allItems); 228 | 229 | FileTypes = new List(result.Select(m => m.ContentType).Distinct()) 230 | { 231 | "All" 232 | }; 233 | FileTypeFilter = "All"; 234 | Message = "数据加载完成"; 235 | } 236 | 237 | #endregion 238 | 239 | public event PropertyChangedEventHandler PropertyChanged; 240 | 241 | public void OnPropertyChanged([CallerMemberName]string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 242 | } 243 | 244 | public partial class DownloadModItem : INotifyPropertyChanged 245 | { 246 | private RemoteModInfo _modInfo; 247 | public RemoteModInfo ModInfo 248 | { 249 | get => _modInfo; 250 | set 251 | { 252 | _modInfo = value; 253 | OnPropertyChanged("ModInfo"); 254 | } 255 | } 256 | 257 | private bool _isChecked; 258 | public bool IsChecked 259 | { 260 | get => _isChecked; 261 | set 262 | { 263 | _isChecked = value; 264 | OnPropertyChanged("IsChecked"); 265 | } 266 | } 267 | 268 | private bool _isExisting; 269 | public bool IsExists 270 | { 271 | get => _isExisting; 272 | set 273 | { 274 | _isExisting = value; 275 | OnPropertyChanged("IsExists"); 276 | } 277 | } 278 | 279 | //private bool _visibility; 280 | //public bool Visibility 281 | //{ 282 | // get => _visibility; 283 | // set 284 | // { 285 | // _visibility = value; 286 | // OnPropertyChanged("Visibility"); 287 | // } 288 | //} 289 | 290 | public event PropertyChangedEventHandler PropertyChanged; 291 | 292 | public void OnPropertyChanged([CallerMemberName]string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 293 | } 294 | 295 | public partial class DownloadModItem 296 | { 297 | public static List FromRemoteList(List items) 298 | { 299 | List downloadMods = new List(); 300 | LocalService ls = new LocalService(ConfigurationManager.UpdateModsConfiguration.ModsDirectory, ConfigurationManager.UpdateModsConfiguration.InfoFile); 301 | var lss = ls.FromDirectory(); 302 | foreach(var i in items) 303 | { 304 | DownloadModItem item = new DownloadModItem 305 | { 306 | ModInfo = i, 307 | IsChecked = false 308 | }; 309 | downloadMods.Add(item); 310 | string fileName = Path.GetFileNameWithoutExtension(i.Name); 311 | int idx = fileName.IndexOf('-'); 312 | if (idx == -1) 313 | continue; 314 | var name = fileName.Substring(0, idx); 315 | if (lss.Exists(v => v.Id == name)) 316 | item.IsExists = true; 317 | else item.IsExists = false; 318 | } 319 | return downloadMods; 320 | } 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /ModsUpdateUI/ViewModels/ModsUpdateViewModel.cs: -------------------------------------------------------------------------------- 1 | using ModsUpdateUI.Models; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.ComponentModel; 5 | using System.Runtime.CompilerServices; 6 | using ModsUpdateUI.Services; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | using System; 10 | using ModsUpdateUI.Configurations; 11 | using System.IO; 12 | using System.IO.Compression; 13 | using MaterialDesignThemes.Wpf; 14 | 15 | namespace ModsUpdateUI.ViewModels 16 | { 17 | public class ModsUpdateViewModel : INotifyPropertyChanged 18 | { 19 | private readonly ISnackbarMessageQueue _snackbarMessQueue; 20 | 21 | public void GetUpdatableMods() 22 | { 23 | var result = from mod in ModItems 24 | where mod.CanUpdate 25 | select mod; 26 | ModItems = new ObservableCollection(result); 27 | } 28 | 29 | public void GetAllMods() => ModItems = new ObservableCollection(_allItems); 30 | 31 | private bool _isChecking = true; 32 | public bool IsChecking 33 | { 34 | get => _isChecking; 35 | set 36 | { 37 | _isChecking = value; 38 | OnPropertyChanged("IsChecking"); 39 | } 40 | } 41 | 42 | public async void CheckUpdateAsync() 43 | { 44 | var result = await _remoteMods; 45 | var lists = result.Where(v => v.ContentType == Constants.ZipType); 46 | ChangeToRemoteItems(lists); 47 | foreach (var i in ModItems) 48 | i.CanUpdate = i.CheckUpdate(lists); 49 | IsChecking = false; 50 | } 51 | 52 | private string _message; 53 | public string Message 54 | { 55 | get => _message; 56 | set 57 | { 58 | _message = value; 59 | OnPropertyChanged("Message"); 60 | _snackbarMessQueue.Enqueue(value); 61 | } 62 | } 63 | 64 | private System.Net.WebClient _client; 65 | private System.Net.WebClient Client 66 | { 67 | get 68 | { 69 | if (_client == null) 70 | _client = new System.Net.WebClient(); 71 | return _client; 72 | } 73 | } 74 | 75 | private int _updateCount; 76 | public int UpdateCount 77 | { 78 | get => _updateCount; 79 | set 80 | { 81 | _updateCount = value; 82 | OnPropertyChanged("UpdateCount"); 83 | } 84 | } 85 | 86 | private int _updatedCount; 87 | public int UpdatedCount 88 | { 89 | get => _updatedCount; 90 | set 91 | { 92 | _updatedCount = value; 93 | OnPropertyChanged("UpdatedCount"); 94 | } 95 | } 96 | 97 | private List _map; 98 | private List _updateItems; 99 | public List UpdateItems 100 | { 101 | get => _updateItems; 102 | set 103 | { 104 | _updateItems = value; 105 | OnPropertyChanged("UpdateItems"); 106 | _map = new List(); 107 | foreach (var i in value) 108 | { 109 | var it = _mappingRemote.First(v => v.Name.StartsWith(i.ModInfo.Id)); 110 | _map.Add(it); 111 | } 112 | } 113 | } 114 | 115 | private List _mappingRemote = new List(); 116 | 117 | private void ChangeToRemoteItems(IEnumerable lists) 118 | { 119 | foreach(var i in ModItems) 120 | { 121 | var it = lists.FirstOrDefault(v => v.Name.StartsWith(i.ModInfo.Id)); 122 | if (it == default(RemoteModInfo)) 123 | continue; 124 | _mappingRemote.Add(it); 125 | } 126 | } 127 | 128 | public async Task UpdateAsync() 129 | { 130 | await Task.Run(() => UpdateMod()); 131 | } 132 | 133 | private void UpdateMod() 134 | { 135 | UpdatedCount = 0; 136 | UpdateCount = UpdateItems.Count; 137 | if (UpdateCount > 0) 138 | { 139 | Client.DownloadFileCompleted += Client_DownloadFileCompleted; 140 | Client.DownloadFileAsync(new Uri(_map[0].BrowserDownloadUrl), 141 | ConfigurationManager.UpdateModsConfiguration.ModsDirectory + Path.DirectorySeparatorChar + _map[0].Name); 142 | while (Client.IsBusy) ; 143 | } 144 | else Message = "未选择待更新项目"; 145 | } 146 | 147 | private void Client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) 148 | { 149 | string prefix = ConfigurationManager.UpdateModsConfiguration.ModsDirectory + Path.DirectorySeparatorChar; 150 | string name = _map[0].Name.Substring(0, _map[0].Name.LastIndexOf('-')); 151 | Directory.Delete(prefix + name, true); 152 | DecompressFile(prefix + _map[0].Name); 153 | _map.RemoveAt(0); 154 | UpdatedCount += 1; 155 | UpdateCount -= 1; 156 | if (UpdateCount > 0) 157 | { 158 | Client.DownloadFileAsync(new Uri(_map[0].BrowserDownloadUrl), 159 | ConfigurationManager.UpdateModsConfiguration.ModsDirectory + Path.DirectorySeparatorChar + _map[0].Name); 160 | } 161 | else 162 | { 163 | Message = "更新完毕"; 164 | Client.DownloadFileCompleted -= Client_DownloadFileCompleted; 165 | } 166 | } 167 | 168 | private void DecompressFile(string filePath) 169 | { 170 | try 171 | { 172 | ZipFile.ExtractToDirectory(filePath, ConfigurationManager.UpdateModsConfiguration.ModsDirectory); 173 | } 174 | catch (Exception) 175 | { 176 | Message = "出现错误,请检查您的网络,并重新尝试。"; 177 | if (File.Exists(filePath)) 178 | File.Delete(filePath); 179 | return; 180 | } 181 | File.Delete(filePath); 182 | } 183 | 184 | private Task> _remoteMods; 185 | 186 | public ModsUpdateViewModel(ISnackbarMessageQueue messageQueue) 187 | { 188 | _snackbarMessQueue = messageQueue ?? throw new ArgumentNullException(nameof(messageQueue)); 189 | 190 | _allItems = UpdateModItem.FromLocalModInfo(ServiceManager.LocalService.FromDirectory()); 191 | ModItems = new ObservableCollection(_allItems); 192 | 193 | InitAsync(); 194 | } 195 | 196 | public void InitAsync() 197 | { 198 | GithubService service = new GithubService(ConfigurationManager.UpdateModsConfiguration.Owner, ConfigurationManager.UpdateModsConfiguration.Repository); 199 | _remoteMods = service.GetLastestModsAsync(); 200 | Message = "数据加载完成"; 201 | } 202 | 203 | public event PropertyChangedEventHandler PropertyChanged; 204 | 205 | private readonly List _allItems; 206 | 207 | private ObservableCollection _modItems; 208 | public ObservableCollection ModItems 209 | { 210 | get => _modItems; 211 | private set 212 | { 213 | _modItems = value; 214 | OnPropertyChanged("ModItems"); 215 | } 216 | } 217 | 218 | public void OnPropertyChanged([CallerMemberName]string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 219 | } 220 | 221 | 222 | public partial class UpdateModItem : INotifyPropertyChanged 223 | { 224 | private LocalModInfo _modInfo; 225 | public LocalModInfo ModInfo 226 | { 227 | get => _modInfo; 228 | set 229 | { 230 | _modInfo = value; 231 | OnPropertyChanged("ModInfo"); 232 | } 233 | } 234 | 235 | private bool _isChecked; 236 | public bool IsChecked 237 | { 238 | get => _isChecked; 239 | set 240 | { 241 | _isChecked = value; 242 | OnPropertyChanged("IsChecked"); 243 | } 244 | } 245 | 246 | private bool _canUpdate; 247 | public bool CanUpdate 248 | { 249 | get => _canUpdate; 250 | set 251 | { 252 | _canUpdate = value; 253 | OnPropertyChanged("CanUpdate"); 254 | } 255 | } 256 | 257 | //private bool _visibility; 258 | //public bool Visibility 259 | //{ 260 | // get => _visibility; 261 | // set 262 | // { 263 | // _visibility = value; 264 | // OnPropertyChanged("Visibility"); 265 | // } 266 | //} 267 | 268 | public event PropertyChangedEventHandler PropertyChanged; 269 | 270 | public void OnPropertyChanged([CallerMemberName]string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 271 | } 272 | 273 | public partial class UpdateModItem 274 | { 275 | public static List FromLocalModInfo(List infos) 276 | { 277 | List items = new List(); 278 | foreach (var i in infos) 279 | { 280 | items.Add(new UpdateModItem 281 | { 282 | ModInfo = i, 283 | IsChecked = false, 284 | CanUpdate = false 285 | }); 286 | } 287 | return items; 288 | } 289 | 290 | public bool CheckUpdate(IEnumerable infos) 291 | { 292 | var it = infos.FirstOrDefault(v => v.Name.StartsWith(ModInfo.Id)); 293 | if (it == default(RemoteModInfo)) 294 | return false; 295 | if (it.Version != ModInfo.Version) 296 | return true; 297 | return false; 298 | } 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /ModsUpdateUI/Views/AboutView.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 24 | 28 | 32 | 36 | 39 | 42 |